diff --git a/js/ui/main.js b/js/ui/main.js index c52a2460b..b2217906d 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -103,13 +103,14 @@ function start() { // Make sure not more than one run dialog is shown. if (!run_dialog) { run_dialog = new RunDialog.RunDialog(); - let handler = function() { + let end_handler = function() { run_dialog.destroy(); run_dialog = null; }; - run_dialog.connect('run', handler); - run_dialog.connect('cancel', handler); - run_dialog.show(); + run_dialog.connect('run', end_handler); + run_dialog.connect('cancel', end_handler); + if (!run_dialog.show()) + end_handler(); } }); @@ -120,17 +121,33 @@ function start() { wm = new WindowManager.WindowManager(); } -function show_overlay() { +// 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 +function startModal() { let global = Shell.global_get(); - overlay.show(); + if (!global.grab_keyboard()) + return false; + global.set_stage_input_area(0, 0, global.screen_width, global.screen_height); + + return true; +} + +function endModal() { + let global = Shell.global_get(); + + global.ungrab_keyboard(); + global.set_stage_input_area(0, 0, global.screen_width, Panel.PANEL_HEIGHT); +} + +function show_overlay() { + if (startModal()) + overlay.show(); } function hide_overlay() { - let global = Shell.global_get(); - overlay.hide(); - panel.overlayHidden(); - global.set_stage_input_area(0, 0, global.screen_width, Panel.PANEL_HEIGHT); + endModal(); } diff --git a/js/ui/overlay.js b/js/ui/overlay.js index e15ed991f..827d36660 100644 --- a/js/ui/overlay.js +++ b/js/ui/overlay.js @@ -178,8 +178,6 @@ Overlay.prototype = { let global = Shell.global_get(); - global.focus_stage(); - let windows = global.get_windows(); let desktopWindow = null; diff --git a/js/ui/run_dialog.js b/js/ui/run_dialog.js index 9c9fa2078..aa8beaa26 100644 --- a/js/ui/run_dialog.js +++ b/js/ui/run_dialog.js @@ -26,8 +26,9 @@ RunDialog.prototype = { _init : function() { let global = Shell.global_get(); - // All actors are inside _group. - this._group = new Clutter.Group(); + // All actors are inside _group. We create it initially + // hidden then show it in show() + this._group = new Clutter.Group({ visible: false }); global.stage.add_actor(this._group); this._overlay = new Clutter.Rectangle({ color: OVERLAY_COLOR, @@ -74,19 +75,6 @@ RunDialog.prototype = { return false; }); - // TODO: Detect escape key and make it cancel the operation. - // Use me.on_cancel() if it exists. Something like this: - // this._entry.connect('key-press-event', function(o, e) { - // if (the pressed key is the escape key) { - // me.hide(); - // me.emit('cancel'); - // return false; - // } else - // return true; - // }); - - global.focus_stage(); - global.stage.set_key_focus(this._entry); }, _run : function(command) { @@ -104,14 +92,41 @@ RunDialog.prototype = { }, show : function() { + if (this._group.visible) // Already shown + return false; + + if (!Main.startModal()) + return false; + this._group.show_all(); + + // TODO: Detect escape key and make it cancel the operation. + // Use me.on_cancel() if it exists. Something like this: + // this._entry.connect('key-press-event', function(o, e) { + // if (the pressed key is the escape key) { + // me.hide(); + // me.emit('cancel'); + // return false; + // } else + // return true; + // }); + + let global = Shell.global_get(); + global.stage.set_key_focus(this._entry); + + return true; }, hide : function() { - this._group.hide(); + if (!this._group.visible) + return; + + this._group.hide(); + Main.endModal(); }, destroy : function(){ + this.hide(); this._group.destroy(); } }; diff --git a/src/shell-global.c b/src/shell-global.c index d469c9875..55b837859 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -11,6 +11,7 @@ struct _ShellGlobal { MutterPlugin *plugin; ShellWM *wm; + gboolean keyboard_grabbed; }; enum { @@ -273,19 +274,64 @@ _shell_global_set_plugin (ShellGlobal *global, } /** - * shell_global_focus_stage: + * shell_global_grab_keyboard: + * @global: a #ShellGlobal * - * Set the keyboard focus to the Clutter stage window. This function - * is best used in combination with some sort of visual notification - * that the shell has taken over input. + * Grab the keyboard to the stage window. The stage will receive + * all keyboard events until shell_global_ungrab_keyboard() is called. + * This is appropriate to do when the desktop goes into a special + * mode where no normal global key shortcuts or application keyboard + * processing should happen. */ -void -shell_global_focus_stage (ShellGlobal *global) +gboolean +shell_global_grab_keyboard (ShellGlobal *global) { MetaScreen *screen = mutter_plugin_get_screen (global->plugin); MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); ClutterStage *stage = CLUTTER_STAGE (mutter_plugin_get_stage (global->plugin)); Window stagewin = clutter_x11_get_stage_window (stage); - XSetInputFocus (xdisplay, stagewin, RevertToParent, CurrentTime); + + /* FIXME: we need to coordinate with the rest of Metacity or we + * may grab the keyboard away from other portions of Metacity + * and leave Metacity in a confused state. An X client is allowed + * to overgrab itself, though not allowed to grab they keyboard + * away from another applications. + */ + if (global->keyboard_grabbed) + return FALSE; + + if (XGrabKeyboard (xdisplay, stagewin, + False, /* owner_events - steal events from the rest of metacity */ + GrabModeAsync, GrabModeAsync, + CurrentTime) != Success) + return FALSE; /* probably AlreadyGrabbed, some other app has a keyboard grab */ + + global->keyboard_grabbed = TRUE; + + return TRUE; +} + +/** + * shell_global_ungrab_keyboard: + * @global: a #ShellGlobal + * + * Undoes the effect of shell_global_grab_keyboard + */ +void +shell_global_ungrab_keyboard (ShellGlobal *global) +{ + MetaScreen *screen; + MetaDisplay *display; + Display *xdisplay; + + g_return_if_fail (global->keyboard_grabbed); + + screen = mutter_plugin_get_screen (global->plugin); + display = meta_screen_get_display (screen); + xdisplay = meta_display_get_xdisplay (display); + + XUngrabKeyboard (xdisplay, CurrentTime); + + global->keyboard_grabbed = FALSE; } diff --git a/src/shell-global.h b/src/shell-global.h index d85796234..1970b0ac4 100644 --- a/src/shell-global.h +++ b/src/shell-global.h @@ -49,7 +49,8 @@ void _shell_global_set_plugin (ShellGlobal *global, MetaScreen * shell_global_get_screen (ShellGlobal *global); -void shell_global_focus_stage (ShellGlobal *global); +gboolean shell_global_grab_keyboard (ShellGlobal *global); +void shell_global_ungrab_keyboard (ShellGlobal *global); G_END_DECLS