[mutter/wip/wayland-kms: 2/15] Track the X Shape input region and use it for picking



commit 5055f709712a3d8343cf2afc3b9f0a3b80645e11
Author: Robert Bragg <robert linux intel com>
Date:   Thu Jan 19 01:20:02 2012 +0000

    Track the X Shape input region and use it for picking
    
    We now track whether a window has an input shape specified via the X
    Shape extension. Intersecting that with the bounding shape (as required
    by the X Shape extension) we use the resulting rectangles to paint
    window silhouettes when picking. As well as improving the correctness of
    picking this should also be much more efficient because typically when
    only picking solid rectangles then the need to actually render and issue
    a read_pixels request can be optimized away and instead the picking is
    done on the cpu.

 src/compositor/meta-shaped-texture.c |   85 +++++++++---
 src/compositor/meta-window-actor.c   |  237 ++++++++++++++++++++++++++--------
 src/core/display.c                   |   27 ++++
 src/core/window-private.h            |    4 +-
 src/core/window.c                    |   26 ++++
 src/meta/meta-shaped-texture.h       |    2 +
 6 files changed, 304 insertions(+), 77 deletions(-)
---
diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
index 627a4e9..c12b513 100644
--- a/src/compositor/meta-shaped-texture.c
+++ b/src/compositor/meta-shaped-texture.c
@@ -95,6 +95,7 @@ struct _MetaShapedTexturePrivate
 
   cairo_region_t *clip_region;
   cairo_region_t *shape_region;
+  cairo_region_t *input_shape_region;
 
   cairo_region_t *overlay_region;
   cairo_path_t *overlay_path;
@@ -571,40 +572,54 @@ meta_shaped_texture_pick (ClutterActor       *actor,
   MetaShapedTexture *stex = (MetaShapedTexture *) actor;
   MetaShapedTexturePrivate *priv = stex->priv;
 
+  if (!clutter_actor_should_pick_paint (actor) ||
+      (priv->clip_region && cairo_region_is_empty (priv->clip_region)))
+    return;
+
   /* If there is no region then use the regular pick */
-  if (priv->shape_region == NULL)
+  if (priv->input_shape_region == NULL)
     CLUTTER_ACTOR_CLASS (meta_shaped_texture_parent_class)
       ->pick (actor, color);
-  else if (clutter_actor_should_pick_paint (actor))
+  else
     {
-      CoglHandle paint_tex;
-      ClutterActorBox alloc;
-      guint tex_width, tex_height;
+      int n_rects;
+      float *rectangles;
+      int i;
 
-      paint_tex = priv->texture;
+      /* Note: We don't bother trying to intersect the pick and clip regions
+       * since needing to copy the region, do the intersection, and probably
+       * increase the number of rectangles seems more likely to have a negative
+       * effect.
+       *
+       * NB: Most of the time when just using rectangles for picking then
+       * picking shouldn't involve any rendering, and minimizing the number of
+       * rectangles has more benefit than reducing the area of the pick
+       * region.
+       */
 
-      if (paint_tex == COGL_INVALID_HANDLE)
-        return;
+      n_rects = cairo_region_num_rectangles (priv->input_shape_region);
 
-      tex_width = cogl_texture_get_width (paint_tex);
-      tex_height = cogl_texture_get_height (paint_tex);
+      rectangles = g_alloca (sizeof (float) * 4 * n_rects);
 
-      if (tex_width == 0 || tex_height == 0) /* no contents yet */
-        return;
+      for (i = 0; i < n_rects; i++)
+        {
+          cairo_rectangle_int_t rect;
+          int pos = i * 4;
 
-      meta_shaped_texture_ensure_mask (stex);
+          cairo_region_get_rectangle (priv->input_shape_region, i, &rect);
 
-      cogl_set_source_color4ub (color->red, color->green, color->blue,
-                                 color->alpha);
+          rectangles[pos] = rect.x;
+          rectangles[pos + 1] = rect.y;
+          rectangles[pos + 2] = rect.x + rect.width;
+          rectangles[pos + 3] = rect.y + rect.height;
+        }
 
-      clutter_actor_get_allocation_box (actor, &alloc);
+      cogl_set_source_color4ub (color->red,
+                                color->green,
+                                color->blue,
+                                color->alpha);
 
-      /* Paint the mask rectangle in the given color */
-      cogl_set_source_texture (priv->mask_texture);
-      cogl_rectangle_with_texture_coords (0, 0,
-                                          alloc.x2 - alloc.x1,
-                                          alloc.y2 - alloc.y1,
-                                          0, 0, 1, 1);
+      cogl_rectangles (rectangles, n_rects);
     }
 }
 
@@ -787,6 +802,32 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
   clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip);
 }
 
+void
+meta_shaped_texture_set_input_shape_region (MetaShapedTexture *stex,
+                                            cairo_region_t    *region)
+{
+  MetaShapedTexturePrivate *priv;
+
+  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
+
+  priv = stex->priv;
+
+  if (priv->input_shape_region != NULL)
+    {
+      cairo_region_destroy (priv->input_shape_region);
+      priv->input_shape_region = NULL;
+    }
+
+  if (region != NULL)
+    {
+      cairo_region_reference (region);
+      priv->input_shape_region = region;
+    }
+
+  meta_shaped_texture_dirty_mask (stex);
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
+}
+
 /**
  * meta_shaped_texture_set_pixmap:
  * @stex: The #MetaShapedTexture
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
index bf67867..2a36edb 100644
--- a/src/compositor/meta-window-actor.c
+++ b/src/compositor/meta-window-actor.c
@@ -73,6 +73,8 @@ struct _MetaWindowActorPrivate
 
   /* If the window is shaped, a region that matches the shape */
   cairo_region_t   *shape_region;
+  /* If the window has an input shape, a region that matches the shape */
+  cairo_region_t   *input_shape_region;
   /* A rectangular region with the visible extents of the window */
   cairo_region_t   *bounding_region;
   /* The region we should clip to when painting the shadow */
@@ -163,9 +165,10 @@ static void     meta_window_actor_detach     (MetaWindowActor *self);
 #endif
 static gboolean meta_window_actor_has_shadow (MetaWindowActor *self);
 
-static void meta_window_actor_clear_shape_region    (MetaWindowActor *self);
-static void meta_window_actor_clear_bounding_region (MetaWindowActor *self);
-static void meta_window_actor_clear_shadow_clip     (MetaWindowActor *self);
+static void meta_window_actor_clear_shape_region        (MetaWindowActor *self);
+static void meta_window_actor_clear_input_shape_region  (MetaWindowActor *self);
+static void meta_window_actor_clear_bounding_region     (MetaWindowActor *self);
+static void meta_window_actor_clear_shadow_clip         (MetaWindowActor *self);
 
 static void meta_window_actor_update_bounding_region_and_borders (MetaWindowActor *self,
                                                                   int              width,
@@ -435,6 +438,7 @@ meta_window_actor_dispose (GObject *object)
 #endif
 
   meta_window_actor_clear_shape_region (self);
+  meta_window_actor_clear_input_shape_region (self);
   meta_window_actor_clear_bounding_region (self);
   meta_window_actor_clear_shadow_clip (self);
 
@@ -1641,6 +1645,18 @@ meta_window_actor_clear_shape_region (MetaWindowActor *self)
 }
 
 static void
+meta_window_actor_clear_input_shape_region (MetaWindowActor *self)
+{
+  MetaWindowActorPrivate *priv = self->priv;
+
+  if (priv->input_shape_region)
+    {
+      cairo_region_destroy (priv->input_shape_region);
+      priv->input_shape_region = NULL;
+    }
+}
+
+static void
 meta_window_actor_clear_bounding_region (MetaWindowActor *self)
 {
   MetaWindowActorPrivate *priv = self->priv;
@@ -1713,33 +1729,6 @@ meta_window_actor_update_bounding_region_and_borders (MetaWindowActor *self,
   g_signal_emit (self, signals[SIZE_CHANGED], 0);
 }
 
-static void
-meta_window_actor_update_shape_region (MetaWindowActor *self,
-                                       cairo_region_t  *region)
-{
-  MetaWindowActorPrivate *priv = self->priv;
-
-  meta_window_actor_clear_shape_region (self);
-
-  /* region must be non-null */
-  priv->shape_region = region;
-  cairo_region_reference (region);
-
-  /* Our "shape_region" is called the "bounding region" in the X Shape
-   * Extension Documentation.
-   *
-   * Our "bounding_region" is called the "bounding rectangle", which defines
-   * the shape of the window as if it the window was unshaped.
-   *
-   * The X Shape extension requires that the "bounding region" can never
-   * extend outside the "bounding rectangle", and says it must be implicitly
-   * clipped before rendering. The region we get back hasn't been clipped.
-   * We explicitly clip the region here.
-   */
-  if (priv->bounding_region != NULL)
-    cairo_region_intersect (priv->shape_region, priv->bounding_region);
-}
-
 /**
  * meta_window_actor_get_obscured_region:
  * @self: a #MetaWindowActor
@@ -2207,22 +2196,34 @@ update_corners (MetaWindowActor   *self,
 }
 
 static void
-check_needs_reshape (MetaWindowActor *self)
+union_shape_rectangles_with_region (cairo_region_t *region,
+                                    const XRectangle *rects,
+                                    int n_rects,
+                                    int dx,
+                                    int dy)
+{
+  int i;
+  for (i = 0; i < n_rects; i ++)
+    {
+      cairo_rectangle_int_t rect = { rects[i].x + dx,
+                                     rects[i].y + dy,
+                                     rects[i].width,
+                                     rects[i].height };
+      cairo_region_union_rectangle (region, &rect);
+    }
+}
+
+static void
+meta_window_actor_update_shape_region (MetaWindowActor *self,
+                                       MetaFrameBorders *borders)
 {
   MetaWindowActorPrivate *priv = self->priv;
   MetaScreen *screen = priv->screen;
   MetaDisplay *display = meta_screen_get_display (screen);
-  MetaFrameBorders borders;
   cairo_region_t *region;
 
-  if (!priv->needs_reshape)
-    return;
-
-  meta_shaped_texture_set_shape_region (META_SHAPED_TEXTURE (priv->actor), NULL);
   meta_window_actor_clear_shape_region (self);
 
-  meta_frame_calc_borders (priv->window->frame, &borders);
-
   region = meta_window_get_frame_bounds (priv->window);
   if (region != NULL)
     {
@@ -2241,17 +2242,17 @@ check_needs_reshape (MetaWindowActor *self)
   if (priv->window->has_shape)
     {
       Display *xdisplay = meta_display_get_xdisplay (display);
+      cairo_rectangle_int_t client_area;
       XRectangle *rects;
       int n_rects, ordering;
-      cairo_rectangle_int_t client_area;
 
       client_area.width = priv->window->rect.width;
       client_area.height = priv->window->rect.height;
 
       if (priv->window->frame)
         {
-          client_area.x = borders.total.left;
-          client_area.y = borders.total.top;
+          client_area.x = borders->total.left;
+          client_area.y = borders->total.top;
         }
       else
         {
@@ -2272,26 +2273,154 @@ check_needs_reshape (MetaWindowActor *self)
 
       if (rects)
         {
-          int i;
-          for (i = 0; i < n_rects; i ++)
-            {
-              cairo_rectangle_int_t rect = { rects[i].x + client_area.x,
-                                             rects[i].y + client_area.y,
-                                             rects[i].width,
-                                             rects[i].height };
-              cairo_region_union_rectangle (region, &rect);
-            }
+          union_shape_rectangles_with_region (region, rects, n_rects,
+                                              client_area.x,
+                                              client_area.y);
           XFree (rects);
         }
+
+      /* Our "shape_region" is called the "bounding region" in the X Shape
+       * Extension Documentation.
+       *
+       * Our "bounding_region" is called the "bounding rectangle", which
+       * defines the shape of the window as if it the window was unshaped.
+       *
+       * The X Shape extension requires that the "bounding region" can never
+       * extend outside the "bounding rectangle", and says it must be
+       * implicitly clipped before rendering. The region we get back hasn't
+       * been clipped.  We explicitly clip the region here.
+       */
+      if (priv->bounding_region != NULL)
+        cairo_region_intersect (region, priv->bounding_region);
     }
 #endif
 
-  meta_shaped_texture_set_shape_region (META_SHAPED_TEXTURE (priv->actor),
-                                        region);
+  priv->shape_region = region;
+}
 
-  meta_window_actor_update_shape_region (self, region);
+static void
+meta_window_actor_update_input_shape_region (MetaWindowActor *self)
+{
+  MetaWindowActorPrivate *priv = self->priv;
+  MetaScreen *screen = priv->screen;
+  MetaDisplay *display = meta_screen_get_display (screen);
+  cairo_region_t *region;
+  cairo_rectangle_int_t default_input_rect;
+  MetaRectangle *default_input_meta_rect;
+#ifdef HAVE_SHAPE
+  Display *xdisplay = meta_display_get_xdisplay (display);
+  XRectangle *rects;
+  int n_rects, ordering;
+  cairo_region_t *bounding_region;
+#endif
+
+  meta_window_actor_clear_input_shape_region (self);
+
+  /* According to the X Shape extension spec; to determine the "effective input
+   * region" used by the x server we need to intersect our region with the
+   * default input region for the window... */
+  if (priv->window->frame)
+    default_input_meta_rect = &priv->window->frame->rect;
+  else
+    default_input_meta_rect = &priv->window->rect;
+
+  default_input_rect.x  = 0;
+  default_input_rect.y = 0;
+  default_input_rect.width = default_input_meta_rect->width;
+  default_input_rect.height = default_input_meta_rect->height;
 
-  cairo_region_destroy (region);
+#ifdef HAVE_SHAPE
+  /* Note: we assume that the effective input area of frames matches the
+   * default input rectangle.
+   */
+  if (!priv->window->frame &&
+      (priv->window->has_shape || priv->window->has_input_shape))
+    {
+      bounding_region = cairo_region_create ();
+
+      /* Strictly speaking the shape_region we compute in
+       * meta_window_actor_update_shape_region() isn't exactly the "bounding
+       * region" according to the X Shape extension and so we need to query the
+       * true bounding region so we can correctly determine the "effective
+       * input region". */
+      meta_error_trap_push (display);
+      rects = XShapeGetRectangles (xdisplay,
+                                   priv->window->xwindow,
+                                   ShapeBounding,
+                                   &n_rects,
+                                   &ordering);
+      meta_error_trap_pop (display);
+      if (rects)
+        {
+          union_shape_rectangles_with_region (bounding_region, rects, n_rects,
+                                              0, 0);
+          XFree (rects);
+        }
+
+      if (!priv->window->frame)
+        {
+          region = cairo_region_create ();
+
+          /* Note we only actually query the ShapeInput shape of a window when we
+           * don't have a frame because we assume currently that mutter never sets
+           * an ShapeInput shape on a frame. */
+          meta_error_trap_push (display);
+          rects = XShapeGetRectangles (xdisplay,
+                                       priv->window->xwindow,
+                                       ShapeInput,
+                                       &n_rects,
+                                       &ordering);
+          meta_error_trap_pop (display);
+          if (rects)
+            {
+              union_shape_rectangles_with_region (region, rects, n_rects, 0, 0);
+              XFree (rects);
+            }
+          cairo_region_intersect (region, bounding_region);
+          cairo_region_destroy (bounding_region);
+          bounding_region = NULL;
+        }
+      else
+        {
+          /* We assume that we never set a ShapeInput shape on frame windows so we
+           * can assume the input shape matches the bounding region. */
+          region = bounding_region;
+          bounding_region = NULL;
+        }
+
+      /* As well as intersecting our input region with the bounding shape the X
+       * Shape extension also requires us to intersect with the default input
+       * bounds to determine the "effective input region"... */
+      cairo_region_intersect_rectangle (region, &default_input_rect);
+    }
+  else
+#endif /* HAVE_SHAPE */
+    {
+      region = cairo_region_create();
+      cairo_region_union_rectangle (region, &default_input_rect);
+    }
+
+  priv->input_shape_region = region;
+}
+
+static void
+check_needs_reshape (MetaWindowActor *self)
+{
+  MetaWindowActorPrivate *priv = self->priv;
+  MetaFrameBorders borders;
+
+  if (!priv->needs_reshape)
+    return;
+
+  meta_frame_calc_borders (priv->window->frame, &borders);
+
+  meta_window_actor_update_shape_region (self, &borders);
+  meta_window_actor_update_input_shape_region (self);
+
+  meta_shaped_texture_set_shape_region (META_SHAPED_TEXTURE (priv->actor),
+                                        priv->shape_region);
+  meta_shaped_texture_set_input_shape_region (META_SHAPED_TEXTURE (priv->actor),
+                                              priv->input_shape_region);
 
   update_corners (self, &borders);
 
diff --git a/src/core/display.c b/src/core/display.c
index 4f3a67f..d84bde9 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -1768,6 +1768,33 @@ event_callback (XEvent   *event,
                 meta_compositor_window_shape_changed (display->compositor,
                                                       window);
             }
+          else if (sev->kind == ShapeInput)
+            {
+              if (sev->shaped && !window->has_input_shape)
+                {
+                  window->has_input_shape = TRUE;                  
+                  meta_topic (META_DEBUG_SHAPES,
+                              "Window %s now has an input shape\n",
+                              window->desc);
+                }
+              else if (!sev->shaped && window->has_input_shape)
+                {
+                  window->has_input_shape = FALSE;
+                  meta_topic (META_DEBUG_SHAPES,
+                              "Window %s no longer has an input shape\n",
+                              window->desc);
+                }
+              else
+                {
+                  meta_topic (META_DEBUG_SHAPES,
+                              "Window %s input shape changed\n",
+                              window->desc);
+                }
+
+              if (display->compositor)
+                meta_compositor_window_shape_changed (display->compositor,
+                                                      window);
+            }
         }
       else
         {
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 76370d9..634ba08 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -335,8 +335,10 @@ struct _MetaWindow
   guint using_net_wm_icon_name         : 1; /* vs. plain wm_icon_name */
   guint using_net_wm_visible_icon_name : 1; /* tracked so we can clear it */
 
-  /* has a shape mask */
+  /* has a bounding shape mask */
   guint has_shape : 1;
+  /* has an input shape mask */
+  guint has_input_shape : 1;
 
   /* icon props have changed */
   guint need_reread_icon : 1;
diff --git a/src/core/window.c b/src/core/window.c
index 3ddf98f..ac7ae40 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -809,6 +809,7 @@ meta_window_new_full (MetaDisplay         *display,
   gulong event_mask;
   MetaMoveResizeFlags flags;
   gboolean has_shape;
+  gboolean has_input_shape;
   MetaScreen *screen;
 
   g_assert (attrs != NULL);
@@ -937,6 +938,7 @@ meta_window_new_full (MetaDisplay         *display,
     }
 
   has_shape = FALSE;
+  has_input_shape = FALSE;
 #ifdef HAVE_SHAPE
   if (META_DISPLAY_HAS_SHAPE (display) &&
       client_type == META_WINDOW_CLIENT_TYPE_X11)
@@ -944,6 +946,8 @@ meta_window_new_full (MetaDisplay         *display,
       int x_bounding, y_bounding, x_clip, y_clip;
       unsigned w_bounding, h_bounding, w_clip, h_clip;
       int bounding_shaped, clip_shaped;
+      XRectangle *input_rectangles;
+      int n_rects, ordering;
 
       XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
 
@@ -955,6 +959,27 @@ meta_window_new_full (MetaDisplay         *display,
 
       has_shape = bounding_shaped != FALSE;
 
+      /* XXX: The x shape extension doesn't provide a way to only test if an
+       * input shape has been specified, so we have to query and throw away the
+       * rectangles. */
+      meta_error_trap_push (display);
+      input_rectangles = XShapeGetRectangles (display->xdisplay, xwindow,
+                                              ShapeInput, &n_rects, &ordering);
+      meta_error_trap_pop (display);
+      if (input_rectangles)
+        {
+          if (n_rects > 1 ||
+              (n_rects == 1 &&
+               (input_rectangles[0].x != x_bounding ||
+                input_rectangles[1].y != y_bounding ||
+                input_rectangles[2].width != w_bounding ||
+                input_rectangles[3].height != h_bounding)))
+            {
+              has_input_shape = TRUE;
+            }
+          XFree (input_rectangles);
+        }
+
       meta_topic (META_DEBUG_SHAPES,
                   "Window has_shape = %d extents %d,%d %u x %u\n",
                   has_shape, x_bounding, y_bounding,
@@ -1024,6 +1049,7 @@ meta_window_new_full (MetaDisplay         *display,
   meta_stack_freeze (window->screen->stack);
 
   window->has_shape = has_shape;
+  window->has_input_shape = has_input_shape;
 
   window->rect.x = attrs->x;
   window->rect.y = attrs->y;
diff --git a/src/meta/meta-shaped-texture.h b/src/meta/meta-shaped-texture.h
index 7849aa4..5a30a39 100644
--- a/src/meta/meta-shaped-texture.h
+++ b/src/meta/meta-shaped-texture.h
@@ -91,6 +91,8 @@ CoglHandle meta_shaped_texture_get_texture (MetaShapedTexture *stex);
 
 void meta_shaped_texture_set_shape_region (MetaShapedTexture *stex,
                                            cairo_region_t    *region);
+void meta_shaped_texture_set_input_shape_region (MetaShapedTexture *stex,
+                                                 cairo_region_t    *region);
 
 void meta_shaped_texture_set_overlay_path (MetaShapedTexture *stex,
                                            cairo_region_t    *overlay_region,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]