diff --git a/js/ui/altTab.js b/js/ui/altTab.js index fbc1268a7..e4560ef42 100644 --- a/js/ui/altTab.js +++ b/js/ui/altTab.js @@ -37,7 +37,9 @@ AltTabPopup.prototype = { corner_radius: POPUP_GRID_SPACING, padding: POPUP_GRID_SPACING, spacing: POPUP_GRID_SPACING, - orientation: Big.BoxOrientation.VERTICAL }); + orientation: Big.BoxOrientation.VERTICAL, + reactive: true }); + this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); // Icon grid. TODO: Investigate Nbtk.Grid once that lands. Currently // just implemented using a chain of Big.Box. @@ -70,11 +72,12 @@ AltTabPopup.prototype = { this.actor.append(this._indicator, Big.BoxPackFlags.FIXED); this._items = []; + this._haveModal = false; global.stage.add_actor(this.actor); }, - addWindow : function(win) { + _addWindow : function(win) { let item = { window: win, metaWindow: win.get_meta_window() }; @@ -100,6 +103,31 @@ AltTabPopup.prototype = { }, show : function(initialSelection) { + let appMonitor = Shell.AppMonitor.get_default(); + let apps = appMonitor.get_running_apps (""); + + if (!apps.length) + return false; + + if (!Main.pushModal(this.actor)) + return false; + this._haveModal = true; + + this._keyPressEventId = global.stage.connect('key-press-event', Lang.bind(this, this._keyPressEvent)); + this._keyReleaseEventId = global.stage.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent)); + + // Fill in the windows + let windows = []; + for (let i = 0; i < apps.length; i++) { + let appWindows = appMonitor.get_windows_for_app(apps[i].get_id()); + windows = windows.concat(appWindows); + } + + windows.sort(function(w1, w2) { return w2.get_user_time() - w1.get_user_time(); }); + + for (let i = 0; i < windows.length; i++) + this._addWindow(windows[i].get_compositor_private()); + // Need to specify explicit width and height because the // window_group may not actually cover the whole screen this._lightbox = new Lightbox.Lightbox(global.window_group, @@ -110,15 +138,56 @@ AltTabPopup.prototype = { this.actor.x = Math.floor((global.screen_width - this.actor.width) / 2); this.actor.y = Math.floor((global.screen_height - this.actor.height) / 2); - this.select(initialSelection); + this._updateSelection(initialSelection); + return true; + }, + + _keyPressEvent : function(actor, event) { + let keysym = event.get_key_symbol(); + let backwards = (event.get_state() & Clutter.ModifierType.SHIFT_MASK); + + if (keysym == Clutter.Tab) + this._updateSelection(backwards ? -1 : 1); + else if (keysym == Clutter.Escape) + this.destroy(); + + return true; + }, + + _keyReleaseEvent : function(actor, event) { + let keysym = event.get_key_symbol(); + + if (keysym == Clutter.Alt_L || keysym == Clutter.Alt_R) { + if (this._selected) { + Main.activateWindow(this._selected.metaWindow, + event.get_time()); + } + this.destroy(); + } + + return true; }, destroy : function() { this.actor.destroy(); - this._lightbox.destroy(); }, - select : function(n) { + _onDestroy : function() { + if (this._haveModal) + Main.popModal(this.actor); + + if (this._lightbox) + this._lightbox.destroy(); + + if (this._keyPressEventId) + global.stage.disconnect(this._keyPressEventId); + if (this._keyReleaseEventId) + global.stage.disconnect(this._keyReleaseEventId); + }, + + _updateSelection : function(delta) { + let n = ((this._selected ? this._selected.n : 0) + this._items.length + delta) % this._items.length; + if (this._selected) { // Unselect previous @@ -177,7 +246,6 @@ AltTabPopup.prototype = { }, _allocationChanged : function() { - if (this._selected) - this.select(this._selected.n); + this._updateSelection(0); } }; diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js index 2ebe5fcbc..4251e098d 100644 --- a/js/ui/windowManager.js +++ b/js/ui/windowManager.js @@ -4,6 +4,7 @@ const Clutter = imports.gi.Clutter; const Lang = imports.lang; const Mainloop = imports.mainloop; const Meta = imports.gi.Meta; +const Shell = imports.gi.Shell; const AltTab = imports.ui.altTab; const Main = imports.ui.main; @@ -39,7 +40,8 @@ WindowManager.prototype = { shellwm.connect('destroy', Lang.bind(this, this._destroyWindow)); shellwm.connect('kill-destroy', Lang.bind(this, this._destroyWindowDone)); - shellwm.connect('begin-alt-tab', Lang.bind(this, this._beginAltTab)); + shellwm.takeover_keybinding('switch_windows'); + shellwm.connect('keybinding::switch_windows', Lang.bind(this, this._startAppSwitcher)); }, _shouldAnimate : function(actor) { @@ -300,12 +302,10 @@ WindowManager.prototype = { shellwm.completed_switch_workspace(); }, - _beginAltTab : function(shellwm, handler) { - let popup = new AltTab.AltTabPopup(); + _startAppSwitcher : function(shellwm, binding, window, backwards) { + let tabPopup = new AltTab.AltTabPopup(); - handler.connect('window-added', function(handler, window) { popup.addWindow(window); }); - handler.connect('show', function(handler, initialSelection) { popup.show(initialSelection); }); - handler.connect('destroy', function() { popup.destroy(); }); - handler.connect('notify::selected', function() { popup.select(handler.selected); }); - } + if (!tabPopup.show(backwards ? -1 : 1)) + tabPopup.destroy(); + } }; diff --git a/src/Makefile.am b/src/Makefile.am index da7de3812..fe8157946 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -52,8 +52,6 @@ CLEANFILES += $(SHELL_STAMP_FILES) libgnome_shell_la_SOURCES = \ $(shell_built_sources) \ gnome-shell-plugin.c \ - shell-alttab.c \ - shell-alttab.h \ shell-app-monitor.c \ shell-app-monitor.h \ shell-app-system.c \ diff --git a/src/shell-alttab.c b/src/shell-alttab.c deleted file mode 100644 index d36967312..000000000 --- a/src/shell-alttab.c +++ /dev/null @@ -1,236 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -#include "shell-alttab.h" -#include "shell-global.h" -#include "shell-wm.h" -#include - -/* Our MetaAltTabHandler implementation; ideally we would implement - * this directly from JavaScript, but for now we can't. So we register - * this glue class as our MetaAltTabHandler and then when mutter - * creates one, we pass it on to ShellWM, which emits a signal to hand - * it off to javascript code, which then connects to the signals on - * this object. - */ - -static void shell_alt_tab_handler_interface_init (MetaAltTabHandlerInterface *handler_iface); - -G_DEFINE_TYPE_WITH_CODE (ShellAltTabHandler, shell_alt_tab_handler, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (META_TYPE_ALT_TAB_HANDLER, - shell_alt_tab_handler_interface_init)) - -/* Signals */ -enum -{ - WINDOW_ADDED, - SHOW, - DESTROY, - - LAST_SIGNAL -}; -static guint signals [LAST_SIGNAL] = { 0 }; - -enum -{ - PROP_SELECTED = 1, - PROP_SCREEN, - PROP_IMMEDIATE -}; - -static void -shell_alt_tab_handler_init (ShellAltTabHandler *sth) -{ - sth->windows = g_ptr_array_new (); - sth->selected = -1; -} - -static void -shell_alt_tab_handler_constructed (GObject *object) -{ - ShellGlobal *global = shell_global_get (); - ShellWM *wm; - - g_object_get (G_OBJECT (global), "window-manager", &wm, NULL); - _shell_wm_begin_alt_tab (wm, SHELL_ALT_TAB_HANDLER (object)); - g_object_unref (wm); -} - -static void -shell_alt_tab_handler_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - ShellAltTabHandler *sth = SHELL_ALT_TAB_HANDLER (object); - - switch (prop_id) - { - case PROP_SCREEN: - /* We don't care */ - break; - case PROP_IMMEDIATE: - sth->immediate_mode = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -shell_alt_tab_handler_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - ShellAltTabHandler *sth = SHELL_ALT_TAB_HANDLER (object); - - switch (prop_id) - { - case PROP_SELECTED: - g_value_set_int (value, sth->selected); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -shell_alt_tab_handler_finalize (GObject *object) -{ - ShellAltTabHandler *sth = SHELL_ALT_TAB_HANDLER (object); - - g_ptr_array_free (sth->windows, FALSE); - - G_OBJECT_CLASS (shell_alt_tab_handler_parent_class)->finalize (object); -} - -static void -shell_alt_tab_handler_add_window (MetaAltTabHandler *handler, - MetaWindow *window) -{ - ShellAltTabHandler *sth = SHELL_ALT_TAB_HANDLER (handler); - - g_ptr_array_add (sth->windows, window); - g_signal_emit (handler, signals[WINDOW_ADDED], 0, - meta_window_get_compositor_private (window)); -} - -static void -shell_alt_tab_handler_show (MetaAltTabHandler *handler, - MetaWindow *initial_selection) -{ - ShellAltTabHandler *sth = SHELL_ALT_TAB_HANDLER (handler); - int i; - - sth->selected = -1; - for (i = 0; i < sth->windows->len; i++) - { - if (sth->windows->pdata[i] == (gpointer)initial_selection) - { - sth->selected = i; - break; - } - } - - g_signal_emit (handler, signals[SHOW], 0, sth->selected); -} - -static void -shell_alt_tab_handler_destroy (MetaAltTabHandler *handler) -{ - g_signal_emit (handler, signals[DESTROY], 0); -} - -static void -shell_alt_tab_handler_forward (MetaAltTabHandler *handler) -{ - ShellAltTabHandler *sth = SHELL_ALT_TAB_HANDLER (handler); - - if (sth->selected == sth->windows->len - 1) - sth->selected = 0; - else - sth->selected++; - g_object_notify (G_OBJECT (handler), "selected"); -} - -static void -shell_alt_tab_handler_backward (MetaAltTabHandler *handler) -{ - ShellAltTabHandler *sth = SHELL_ALT_TAB_HANDLER (handler); - - if (sth->selected == 0) - sth->selected = sth->windows->len - 1; - else - sth->selected--; - g_object_notify (G_OBJECT (handler), "selected"); -} - -static MetaWindow * -shell_alt_tab_handler_get_selected (MetaAltTabHandler *handler) -{ - ShellAltTabHandler *sth = SHELL_ALT_TAB_HANDLER (handler); - - if (sth->selected > -1) - return sth->windows->pdata[sth->selected]; - else - return NULL; -} - -static void -shell_alt_tab_handler_class_init (ShellAltTabHandlerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->constructed = shell_alt_tab_handler_constructed; - object_class->set_property = shell_alt_tab_handler_set_property; - object_class->get_property = shell_alt_tab_handler_get_property; - object_class->finalize = shell_alt_tab_handler_finalize; - - g_object_class_override_property (object_class, PROP_SCREEN, "screen"); - g_object_class_override_property (object_class, PROP_IMMEDIATE, "immediate"); - g_object_class_install_property (object_class, - PROP_SELECTED, - g_param_spec_int ("selected", - "Selected", - "Selected window", - -1, G_MAXINT, -1, - G_PARAM_READABLE)); - - - signals[WINDOW_ADDED] = g_signal_new ("window-added", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, - MUTTER_TYPE_COMP_WINDOW); - signals[SHOW] = g_signal_new ("show", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, 1, - G_TYPE_INT); - signals[DESTROY] = g_signal_new ("destroy", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - -static void -shell_alt_tab_handler_interface_init (MetaAltTabHandlerInterface *handler_iface) -{ - handler_iface->add_window = shell_alt_tab_handler_add_window; - handler_iface->show = shell_alt_tab_handler_show; - handler_iface->destroy = shell_alt_tab_handler_destroy; - handler_iface->forward = shell_alt_tab_handler_forward; - handler_iface->backward = shell_alt_tab_handler_backward; - handler_iface->get_selected = shell_alt_tab_handler_get_selected; -} diff --git a/src/shell-alttab.h b/src/shell-alttab.h deleted file mode 100644 index 4e04b3b58..000000000 --- a/src/shell-alttab.h +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -#ifndef SHELL_ALT_TAB_HANDLER_H -#define SHELL_ALT_TAB_HANDLER_H - -#include - -#define SHELL_TYPE_ALT_TAB_HANDLER (shell_alt_tab_handler_get_type ()) -#define SHELL_ALT_TAB_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_ALT_TAB_HANDLER, ShellAltTabHandler)) -#define SHELL_ALT_TAB_HANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_ALT_TAB_HANDLER, ShellAltTabHandlerClass)) -#define SHELL_IS_ALT_TAB_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_ALT_TAB_HANDLER_TYPE)) -#define SHELL_IS_ALT_TAB_HANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_ALT_TAB_HANDLER)) -#define SHELL_ALT_TAB_HANDLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_ALT_TAB_HANDLER, ShellAltTabHandlerClass)) - -typedef struct _ShellAltTabHandler ShellAltTabHandler; -typedef struct _ShellAltTabHandlerClass ShellAltTabHandlerClass; - -struct _ShellAltTabHandler { - GObject parent_instance; - - GPtrArray *windows; - int selected; - gboolean immediate_mode; -}; - -struct _ShellAltTabHandlerClass { - GObjectClass parent_class; - -}; - -GType shell_alt_tab_handler_get_type (void); - -#endif - diff --git a/src/shell-marshal.list b/src/shell-marshal.list index 329f8b319..6a69de7e0 100644 --- a/src/shell-marshal.list +++ b/src/shell-marshal.list @@ -2,3 +2,4 @@ VOID:INT,INT,INT VOID:OBJECT,INT,INT,INT,INT VOID:BOXED VOID:BOXED,OBJECT +VOID:STRING,OBJECT,BOOLEAN diff --git a/src/shell-wm.c b/src/shell-wm.c index 42ec233c5..3b757cc0c 100644 --- a/src/shell-wm.c +++ b/src/shell-wm.c @@ -1,9 +1,13 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +#include + #include "shell-wm.h" #include "shell-global.h" #include "shell-marshal.h" +#include + struct _ShellWM { GObject parent; @@ -27,7 +31,7 @@ enum SWITCH_WORKSPACE, KILL_SWITCH_WORKSPACE, - BEGIN_ALT_TAB, + KEYBINDING, LAST_SIGNAL }; @@ -42,7 +46,6 @@ static guint shell_wm_signals [LAST_SIGNAL] = { 0 }; static void shell_wm_init (ShellWM *wm) { - meta_alt_tab_handler_register (SHELL_TYPE_ALT_TAB_HANDLER); } static void @@ -169,15 +172,32 @@ shell_wm_class_init (ShellWMClass *klass) NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - shell_wm_signals[BEGIN_ALT_TAB] = - g_signal_new ("begin-alt-tab", + + /** + * ShellWM::keybinding: + * @shellwm: the #ShellWM + * @binding: the keybinding name + * @window: for window keybindings, the #MetaWindow + * @backwards: for "reversible" keybindings, whether or not + * the backwards (Shifted) variant was invoked + * + * Emitted when a keybinding captured via + * shell_wm_takeover_keybinding() is invoked. The keybinding name + * (which has underscores, not hyphens) is also included as the + * detail of the signal name, so you can connect just specific + * keybindings. + */ + shell_wm_signals[KEYBINDING] = + g_signal_new ("keybinding", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, - META_TYPE_ALT_TAB_HANDLER); + _shell_marshal_VOID__STRING_OBJECT_BOOLEAN, + G_TYPE_NONE, 3, + G_TYPE_STRING, + META_TYPE_WINDOW, + G_TYPE_BOOLEAN); } void @@ -391,14 +411,6 @@ _shell_wm_destroy (ShellWM *wm, g_signal_emit (wm, shell_wm_signals[DESTROY], 0, actor); } -/* Called from shell-alttab.c */ -void -_shell_wm_begin_alt_tab (ShellWM *wm, - ShellAltTabHandler *handler) -{ - g_signal_emit (wm, shell_wm_signals[BEGIN_ALT_TAB], 0, handler); -} - /** * shell_wm_new: * @plugin: the #MutterPlugin @@ -417,3 +429,37 @@ shell_wm_new (MutterPlugin *plugin) return wm; } + +static void +shell_wm_key_handler (MetaDisplay *display, + MetaScreen *screen, + MetaWindow *window, + XEvent *event, + MetaKeyBinding *binding, + gpointer data) +{ + ShellWM *wm = data; + gboolean backwards = (event->xkey.state & ShiftMask); + + g_signal_emit (wm, shell_wm_signals[KEYBINDING], + g_quark_from_string (binding->name), + binding->name, window, backwards); +} + +/** + * shell_wm_takeover_keybinding: + * @wm: the #ShellWM + * @binding_name: a mutter keybinding name + * + * Tells mutter to forward keypresses for @binding_name to the shell + * rather than processing them internally. This will cause a + * #ShellWM::keybinding signal to be emitted when that key is pressed. + */ +void +shell_wm_takeover_keybinding (ShellWM *wm, + const char *binding_name) +{ + meta_keybindings_set_custom_handler (binding_name, + shell_wm_key_handler, + wm, NULL); +} diff --git a/src/shell-wm.h b/src/shell-wm.h index 59387cff8..aa8e13195 100644 --- a/src/shell-wm.h +++ b/src/shell-wm.h @@ -4,8 +4,6 @@ #include #include -#include "shell-alttab.h" - G_BEGIN_DECLS typedef struct _ShellWM ShellWM; @@ -73,10 +71,9 @@ void _shell_wm_kill_effect (ShellWM *wm, MutterWindow *actor, gulong events); -/* Called by ShellAltTabHandler */ - -void _shell_wm_begin_alt_tab (ShellWM *wm, - ShellAltTabHandler *handler); +/* Keybinding stuff */ +void shell_wm_takeover_keybinding (ShellWM *wm, + const char *binding_name); G_END_DECLS