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.
This commit is contained in:
Dan Winship 2009-04-29 14:01:09 -04:00
parent 63821f1ae7
commit 4a5873dd22
4 changed files with 188 additions and 88 deletions

View File

@ -3,6 +3,7 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
@ -60,8 +61,6 @@ function start() {
}); });
panel = new Panel.Panel(); panel = new Panel.Panel();
panel.actor.connect('notify::visible', _panelVisibilityChanged);
_panelVisibilityChanged();
overlay = new Overlay.Overlay(); overlay = new Overlay.Overlay();
wm = new WindowManager.WindowManager(); wm = new WindowManager.WindowManager();
@ -95,6 +94,9 @@ function start() {
display.connect('overlay-key', toggleOverlay); display.connect('overlay-key', toggleOverlay);
global.connect('panel-main-menu', 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); Mainloop.idle_add(_removeUnusedWorkspaces);
} }
@ -128,18 +130,6 @@ function _removeUnusedWorkspaces() {
return false; 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 // 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 // the stage. Returns true if we successfully grabbed the keyboard and
// went modal, false otherwise // went modal, false otherwise
@ -148,9 +138,9 @@ function startModal() {
if (!global.grab_keyboard()) if (!global.grab_keyboard())
return false; return false;
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
inModal = true; inModal = true;
global.set_stage_input_area(0, 0, global.screen_width, global.screen_height);
return true; return true;
} }
@ -159,8 +149,8 @@ function endModal() {
let global = Shell.Global.get(); let global = Shell.Global.get();
global.ungrab_keyboard(); global.ungrab_keyboard();
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
inModal = false; inModal = false;
_panelVisibilityChanged();
} }
function show_overlay() { function show_overlay() {
@ -175,3 +165,87 @@ function hide_overlay() {
overlayActive = false; overlayActive = false;
endModal(); 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);
}

View File

@ -69,7 +69,7 @@ Panel.prototype = {
PANEL_BOTTOM_COLOR); PANEL_BOTTOM_COLOR);
let shadow = global.create_vertical_gradient(SHADOW_COLOR, let shadow = global.create_vertical_gradient(SHADOW_COLOR,
TRANSPARENT_COLOR); TRANSPARENT_COLOR);
shadow.set_height(SHADOW_HEIGHT); shadow.set_height(SHADOW_HEIGHT + 1);
backBox.append(backUpper, Big.BoxPackFlags.EXPAND); backBox.append(backUpper, Big.BoxPackFlags.EXPAND);
backBox.append(backLower, Big.BoxPackFlags.EXPAND); backBox.append(backLower, Big.BoxPackFlags.EXPAND);
backBox.append(shadow, Big.BoxPackFlags.NONE); backBox.append(shadow, Big.BoxPackFlags.NONE);
@ -159,15 +159,11 @@ Panel.prototype = {
return true; return true;
}); });
this._setStruts();
global.screen.connect('notify::n-workspaces',
function() {
me._setStruts();
});
this.actor.add_actor(box); this.actor.add_actor(box);
global.stage.add_actor(this.actor); 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', global.screen.connect('restacked',
function() { function() {
@ -179,30 +175,6 @@ Panel.prototype = {
this._updateClock(); 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() { _restacked: function() {
let global = Shell.Global.get(); let global = Shell.Global.get();
let windows = global.get_windows(); let windows = global.get_windows();

View File

@ -16,6 +16,7 @@
#include <dbus/dbus-glib.h> #include <dbus/dbus-glib.h>
#include <libgnomeui/gnome-thumbnail.h> #include <libgnomeui/gnome-thumbnail.h>
#include <math.h> #include <math.h>
#include <X11/extensions/Xfixes.h>
#define SHELL_DBUS_SERVICE "org.gnome.Shell" #define SHELL_DBUS_SERVICE "org.gnome.Shell"
@ -31,12 +32,10 @@ struct _ShellGlobal {
* This window is never mapped or shown. * This window is never mapped or shown.
*/ */
GtkWindow *grab_notifier; GtkWindow *grab_notifier;
gboolean grab_active; gboolean gtk_grab_active;
/* See shell_global_set_stage_input_area */
int input_x; ShellStageInputMode input_mode;
int input_y; XserverRegion input_region;
int input_width;
int input_height;
MutterPlugin *plugin; MutterPlugin *plugin;
ShellWM *wm; ShellWM *wm;
@ -172,9 +171,11 @@ shell_global_init (ShellGlobal *global)
global->grab_notifier = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL)); global->grab_notifier = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
g_signal_connect (global->grab_notifier, "grab-notify", G_CALLBACK (grab_notify), global); 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->root_pixmap = NULL;
global->input_mode = SHELL_STAGE_INPUT_MODE_NORMAL;
} }
static void static void
@ -682,32 +683,85 @@ shell_global_get (void)
} }
/** /**
* shell_global_set_stage_input_area: * shell_global_set_stage_input_mode:
* x: X coordinate of rectangle * @global: the #ShellGlobal
* y: Y coordinate of rectangle * @mode: the stage input mode
* width: width of rectangle
* height: height of rectangle
* *
* Sets the area of the stage that is responsive to mouse clicks as * Sets the input mode of the stage; when @mode is
* a rectangle. * %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 void
shell_global_set_stage_input_area (ShellGlobal *global, shell_global_set_stage_input_mode (ShellGlobal *global,
int x, ShellStageInputMode mode)
int y,
int width,
int height)
{ {
g_return_if_fail (SHELL_IS_GLOBAL (global)); g_return_if_fail (SHELL_IS_GLOBAL (global));
/* Cache these so we can save/restore across grabs */ if (mode == SHELL_STAGE_INPUT_MODE_NONREACTIVE || global->gtk_grab_active)
global->input_x = x; mutter_plugin_set_stage_reactive (global->plugin, FALSE);
global->input_y = y; else if (mode == SHELL_STAGE_INPUT_MODE_FULLSCREEN || !global->input_region)
global->input_width = width; mutter_plugin_set_stage_reactive (global->plugin, TRUE);
global->input_height = height; else
/* If we have a grab active, we'll set the input area when we ungrab. */ mutter_plugin_set_stage_input_region (global->plugin, global->input_region);
if (!global->grab_active)
mutter_plugin_set_stage_input_area (global->plugin, x, y, width, height); 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); ShellGlobal *global = SHELL_GLOBAL (user_data);
if (!was_grabbed) global->gtk_grab_active = !was_grabbed;
{
mutter_plugin_set_stage_input_area (global->plugin, 0, 0, 0, 0); /* Update for the new setting of gtk_grab_active */
} shell_global_set_stage_input_mode (global, global->input_mode);
else
{
mutter_plugin_set_stage_input_area (global->plugin, global->input_x, global->input_y,
global->input_width, global->input_height);
}
} }
/** /**

View File

@ -54,11 +54,16 @@ void shell_global_grab_dbus_service (ShellGlobal *global);
void shell_global_start_task_panel (ShellGlobal *global); void shell_global_start_task_panel (ShellGlobal *global);
void shell_global_set_stage_input_area (ShellGlobal *global, typedef enum {
int x, SHELL_STAGE_INPUT_MODE_NONREACTIVE,
int y, SHELL_STAGE_INPUT_MODE_NORMAL,
int width, SHELL_STAGE_INPUT_MODE_FULLSCREEN
int height); } 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); GList *shell_global_get_windows (ShellGlobal *global);