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 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);
}

View File

@ -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();

View File

@ -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);
}
/**

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_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);