[gnome-shell] Create a more extensible/organized strut/input_area management system.



commit 4a5873dd224f0e574b2021763bd8ce5098654a8e
Author: Dan Winship <danw gnome org>
Date:   Wed Apr 29 14:01:09 2009 -0400

    Create a more extensible/organized strut/input_area management system.
    
    Now code can call Main.addShellActor(actor) to declare that that actor
    is part of the shell, and so it should (a) be protected by wm struts, and
    (b) be part of the stage input area, and then that code automatically
    deals with updating if the actor changes size or visibility.
---
 js/ui/main.js      |  106 ++++++++++++++++++++++++++++++++++++++-------
 js/ui/panel.js     |   34 +-------------
 src/shell-global.c |  121 ++++++++++++++++++++++++++++++++++++---------------
 src/shell-global.h |   15 ++++--
 4 files changed, 188 insertions(+), 88 deletions(-)

diff --git a/js/ui/main.js b/js/ui/main.js
index d4b3f9c..2db7981 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -3,6 +3,7 @@
 const Clutter = imports.gi.Clutter;
 const Gio = imports.gi.Gio;
 const Mainloop = imports.mainloop;
+const Meta = imports.gi.Meta;
 const Shell = imports.gi.Shell;
 const Signals = imports.signals;
 
@@ -60,8 +61,6 @@ function start() {
     });
 
     panel = new Panel.Panel();
-    panel.actor.connect('notify::visible', _panelVisibilityChanged);
-    _panelVisibilityChanged();
 
     overlay = new Overlay.Overlay();
     wm = new WindowManager.WindowManager();
@@ -95,6 +94,9 @@ function start() {
     display.connect('overlay-key', toggleOverlay);
     global.connect('panel-main-menu', toggleOverlay);
     
+    // Need to update struts on new workspaces when they are added
+    global.screen.connect('notify::n-workspaces', _setStageArea);
+
     Mainloop.idle_add(_removeUnusedWorkspaces);
 }
 
@@ -128,18 +130,6 @@ function _removeUnusedWorkspaces() {
     return false;
 }
 
-function _panelVisibilityChanged() {
-    if (!inModal) {
-        let global = Shell.Global.get();
-
-        if (panel.actor.visible) {
-            global.set_stage_input_area(0, 0,
-                                        global.screen_width, Panel.PANEL_HEIGHT);
-        } else
-            global.set_stage_input_area(0, 0, 0, 0);
-    }
-}
-
 // Used to go into a mode where all keyboard and mouse input goes to
 // the stage. Returns true if we successfully grabbed the keyboard and
 // went modal, false otherwise
@@ -148,9 +138,9 @@ function startModal() {
 
     if (!global.grab_keyboard())
         return false;
+    global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
 
     inModal = true;
-    global.set_stage_input_area(0, 0, global.screen_width, global.screen_height);
 
     return true;
 }
@@ -159,8 +149,8 @@ function endModal() {
     let global = Shell.Global.get();
 
     global.ungrab_keyboard();
+    global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
     inModal = false;
-    _panelVisibilityChanged();
 }
 
 function show_overlay() {
@@ -175,3 +165,87 @@ function hide_overlay() {
     overlayActive = false;
     endModal();
 }
+
+let _shellActors = [];
+
+// For adding an actor that is part of the shell in the normal desktop view
+function addShellActor(actor) {
+    let global = Shell.Global.get();
+
+    _shellActors.push(actor);
+
+    actor.connect('notify::visible', _setStageArea);
+    actor.connect('destroy', function(actor) {
+                      let i = _shellActors.indexOf(actor);
+                      if (i != -1)
+                          _shellActors.splice(i, 1);
+                      _setStageArea();
+                  });
+
+    while (actor != global.stage) {
+        actor.connect('notify::allocation', _setStageArea);
+        actor = actor.get_parent();
+    }
+
+    _setStageArea();
+}
+
+function _setStageArea() {
+    let global = Shell.Global.get();
+    let rects = [], struts = [];
+
+    for (let i = 0; i < _shellActors.length; i++) {
+        if (!_shellActors[i].visible)
+            continue;
+
+        let [x, y] = _shellActors[i].get_transformed_position();
+        let [w, h] = _shellActors[i].get_transformed_size();
+
+        let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
+        rects.push(rect);
+
+        // Metacity wants to know what side of the screen the strut is
+        // considered to be attached to. If the actor is only touching
+        // one edge, or is touching the entire width/height of one
+        // edge, then it's obvious which side to call it. If it's in a
+        // corner, we pick a side arbitrarily. If it doesn't touch any
+        // edges, or it spans the width/height across the middle of
+        // the screen, then we don't create a strut for it at all.
+        let side;
+        if (w >= global.screen_width) {
+            if (y <= 0)
+                side = Meta.Side.TOP;
+            else if (y + h >= global.screen_height)
+                side = Meta.Side.BOTTOM;
+            else
+                continue;
+        } else if (h >= global.screen_height) {
+            if (x <= 0)
+                side = Meta.Side.LEFT;
+            else if (x + w >= global.screen_width)
+                side = Meta.Side.RIGHT;
+            else
+                continue;
+        } else if (x <= 0)
+            side = Meta.Side.LEFT;
+        else if (y <= 0)
+            side = Meta.Side.TOP;
+        else if (x + w >= global.screen_width)
+            side = Meta.Side.RIGHT;
+        else if (y + h >= global.screen_height)
+            side = Meta.Side.BOTTOM;
+        else
+            continue;
+
+        let strut = new Meta.Strut({ rect: rect, side: side });
+        struts.push(strut);
+    }
+
+    let screen = global.screen;
+    for (let w = 0; w < screen.n_workspaces; w++) {
+        let workspace = screen.get_workspace_by_index(w);
+        workspace.set_builtin_struts(struts);
+    }
+
+    global.set_stage_input_region(rects);
+}
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 90fd713..1114444 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -69,7 +69,7 @@ Panel.prototype = {
                                                         PANEL_BOTTOM_COLOR);
         let shadow = global.create_vertical_gradient(SHADOW_COLOR,
                                                      TRANSPARENT_COLOR);
-        shadow.set_height(SHADOW_HEIGHT);
+        shadow.set_height(SHADOW_HEIGHT + 1);
         backBox.append(backUpper, Big.BoxPackFlags.EXPAND);
         backBox.append(backLower, Big.BoxPackFlags.EXPAND);
         backBox.append(shadow, Big.BoxPackFlags.NONE);
@@ -159,15 +159,11 @@ Panel.prototype = {
                 return true;
             });
 
-        this._setStruts();
-        global.screen.connect('notify::n-workspaces',
-            function() {
-                me._setStruts();
-            });
-
         this.actor.add_actor(box);
 
         global.stage.add_actor(this.actor);
+        // Declare just "box" (ie, not the drop shadow) as a shell actor
+        Main.addShellActor(box);
 
         global.screen.connect('restacked',
             function() {
@@ -179,30 +175,6 @@ Panel.prototype = {
         this._updateClock();
     },
 
-    // Struts determine the area along each side of the screen that is reserved
-    // and not available to applications
-    _setStruts: function() {
-        let global = Shell.Global.get();
-
-        let struts = [
-            new Meta.Strut({
-                rect: {
-                    x: 0,
-                    y: 0,
-                    width: global.screen_width,
-                    height: PANEL_HEIGHT
-                },
-                side: Meta.Side.TOP
-            })
-        ];
-
-        let screen = global.screen;
-        for (let i = 0; i < screen.n_workspaces; i++) {
-            let workspace = screen.get_workspace_by_index(i);
-            workspace.set_builtin_struts(struts);
-        }
-    },
-
     _restacked: function() {
         let global = Shell.Global.get();
         let windows = global.get_windows();
diff --git a/src/shell-global.c b/src/shell-global.c
index ba6bdf2..0857e98 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -16,6 +16,7 @@
 #include <dbus/dbus-glib.h>
 #include <libgnomeui/gnome-thumbnail.h>
 #include <math.h>
+#include <X11/extensions/Xfixes.h>
 
 #define SHELL_DBUS_SERVICE "org.gnome.Shell"
 
@@ -31,12 +32,10 @@ struct _ShellGlobal {
    * This window is never mapped or shown.
    */
   GtkWindow *grab_notifier;
-  gboolean grab_active;
-  /* See shell_global_set_stage_input_area */
-  int input_x;
-  int input_y;
-  int input_width;
-  int input_height;
+  gboolean gtk_grab_active;
+
+  ShellStageInputMode input_mode;
+  XserverRegion input_region;
   
   MutterPlugin *plugin;
   ShellWM *wm;
@@ -172,9 +171,11 @@ shell_global_init (ShellGlobal *global)
   
   global->grab_notifier = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
   g_signal_connect (global->grab_notifier, "grab-notify", G_CALLBACK (grab_notify), global);
-  global->grab_active = FALSE;
+  global->gtk_grab_active = FALSE;
 
   global->root_pixmap = NULL;
+
+  global->input_mode = SHELL_STAGE_INPUT_MODE_NORMAL;
 }
 
 static void
@@ -682,32 +683,85 @@ shell_global_get (void)
 }
 
 /**
- * shell_global_set_stage_input_area:
- * x: X coordinate of rectangle
- * y: Y coordinate of rectangle
- * width: width of rectangle
- * height: height of rectangle
+ * shell_global_set_stage_input_mode:
+ * @global: the #ShellGlobal
+ * @mode: the stage input mode
  *
- * Sets the area of the stage that is responsive to mouse clicks as
- * a rectangle.
+ * Sets the input mode of the stage; when @mode is
+ * %SHELL_STAGE_INPUT_MODE_NONREACTIVE, then the stage does not absorb
+ * any clicks, but just passes them through to underlying windows.
+ * When it is %SHELL_STAGE_INPUT_MODE_NORMAL, then the stage accepts
+ * clicks in the region defined by
+ * shell_global_set_stage_input_region() but passes through clicks
+ * outside that region. When it is %SHELL_STAGE_INPUT_MODE_FULLSCREEN,
+ * the stage absorbs all input.
+ *
+ * Note that whenever a mutter-internal Gtk widget has a pointer grab,
+ * the shell behaves as though it was in
+ * %SHELL_STAGE_INPUT_MODE_NONREACTIVE, to ensure that the widget gets
+ * any clicks it is expecting.
  */
 void
-shell_global_set_stage_input_area (ShellGlobal *global,
-                                   int          x,
-                                   int          y,
-                                   int          width,
-                                   int          height)
+shell_global_set_stage_input_mode (ShellGlobal         *global,
+                                   ShellStageInputMode  mode)
 {
   g_return_if_fail (SHELL_IS_GLOBAL (global));
 
-  /* Cache these so we can save/restore across grabs */ 
-  global->input_x = x;
-  global->input_y = y;
-  global->input_width = width;
-  global->input_height = height;
-  /* If we have a grab active, we'll set the input area when we ungrab. */
-  if (!global->grab_active)
-    mutter_plugin_set_stage_input_area (global->plugin, x, y, width, height);
+  if (mode == SHELL_STAGE_INPUT_MODE_NONREACTIVE || global->gtk_grab_active)
+    mutter_plugin_set_stage_reactive (global->plugin, FALSE);
+  else if (mode == SHELL_STAGE_INPUT_MODE_FULLSCREEN || !global->input_region)
+    mutter_plugin_set_stage_reactive (global->plugin, TRUE);
+  else
+    mutter_plugin_set_stage_input_region (global->plugin, global->input_region);
+
+  global->input_mode = mode;
+}
+
+/**
+ * shell_global_set_stage_input_region:
+ * @global: the #ShellGlobal
+ * @rectangles: (element-type Meta.Rectangle): a list of #MetaRectangle
+ * describing the input region.
+ *
+ * Sets the area of the stage that is responsive to mouse clicks when
+ * the stage mode is %SHELL_STAGE_INPUT_MODE_NORMAL (but does not change the
+ * current stage mode).
+ */
+void
+shell_global_set_stage_input_region (ShellGlobal *global,
+                                     GSList      *rectangles)
+{
+  MetaScreen *screen = mutter_plugin_get_screen (global->plugin);
+  MetaDisplay *display = meta_screen_get_display (screen);
+  Display *xdpy = meta_display_get_xdisplay (display);
+  MetaRectangle *rect;
+  XRectangle *rects;
+  int nrects, i;
+  GSList *r;
+
+  g_return_if_fail (SHELL_IS_GLOBAL (global));
+
+  nrects = g_slist_length (rectangles);
+  rects = g_new (XRectangle, nrects);
+  for (r = rectangles, i = 0; r; r = r->next, i++)
+    {
+      rect = (MetaRectangle *)r->data;
+      rects[i].x = rect->x;
+      rects[i].y = rect->y;
+      rects[i].width = rect->width;
+      rects[i].height = rect->height;
+    }
+
+  if (global->input_region)
+    XFixesDestroyRegion (xdpy, global->input_region);
+
+  global->input_region = XFixesCreateRegion (xdpy, rects, nrects);
+  g_free (rects);
+
+  /* set_stage_input_mode() will figure out whether or not we
+   * should actually change the input region right now.
+   */
+  shell_global_set_stage_input_mode (global, global->input_mode);
 }
 
 /**
@@ -982,15 +1036,10 @@ grab_notify (GtkWidget *widget, gboolean was_grabbed, gpointer user_data)
 {
   ShellGlobal *global = SHELL_GLOBAL (user_data);
   
-  if (!was_grabbed)
-    {
-      mutter_plugin_set_stage_input_area (global->plugin, 0, 0, 0, 0);
-    }
-  else
-    {
-      mutter_plugin_set_stage_input_area (global->plugin, global->input_x, global->input_y,
-                                          global->input_width, global->input_height);      
-    }
+  global->gtk_grab_active = !was_grabbed;
+
+  /* Update for the new setting of gtk_grab_active */
+  shell_global_set_stage_input_mode (global, global->input_mode);
 }
 
 /**
diff --git a/src/shell-global.h b/src/shell-global.h
index 54ec987..f263890 100644
--- a/src/shell-global.h
+++ b/src/shell-global.h
@@ -54,11 +54,16 @@ void shell_global_grab_dbus_service (ShellGlobal *global);
 
 void shell_global_start_task_panel (ShellGlobal *global);
 
-void shell_global_set_stage_input_area (ShellGlobal *global,
-					int          x,
-					int          y,
-					int          width,
-					int          height);
+typedef enum {
+  SHELL_STAGE_INPUT_MODE_NONREACTIVE,
+  SHELL_STAGE_INPUT_MODE_NORMAL,
+  SHELL_STAGE_INPUT_MODE_FULLSCREEN
+} ShellStageInputMode;
+
+void shell_global_set_stage_input_mode   (ShellGlobal         *global,
+					  ShellStageInputMode  mode);
+void shell_global_set_stage_input_region (ShellGlobal         *global,
+					  GSList              *rectangles);
 
 GList *shell_global_get_windows (ShellGlobal *global);
 



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