Merge lp:~3v1n0/unity/ups-menu-close-on-keybindings into lp:unity
- ups-menu-close-on-keybindings
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Christopher Townsend |
Approved revision: | no longer in the source branch. |
Merged at revision: | 3551 |
Proposed branch: | lp:~3v1n0/unity/ups-menu-close-on-keybindings |
Merge into: | lp:unity |
Diff against target: |
942 lines (+452/-128) 11 files modified
launcher/Launcher.cpp (+2/-1) launcher/SwitcherController.cpp (+4/-4) launcher/SwitcherView.h (+7/-7) services/panel-service-private.h (+41/-0) services/panel-service.c (+178/-102) tests/autopilot/unity/emulators/window_manager.py (+8/-0) tests/autopilot/unity/tests/test_panel.py (+17/-5) tests/autopilot/unity/tests/test_spread.py (+3/-3) tests/autopilot/unity/tests/test_wm_keybindings.py (+55/-2) tests/data/external.gschema.xml (+6/-4) tests/test_panel_service.cpp (+131/-0) |
To merge this branch: | bzr merge lp:~3v1n0/unity/ups-menu-close-on-keybindings |
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Christopher Townsend | Approve | ||
Brandon Schaefer (community) | Needs Fixing | ||
Review via email: mp+188971@code.launchpad.net |
Commit message
PanelService: close a menu and re-send the keyevent when handling a combination
Or when we try to open HUD/Dash. Also, close the active menu if a new application is opened
and focused.
Description of the change
Make the PanelService to ignore all the <Meta>+<key> keybindings and to reinject them to the X server root window so that the WindowManager can process them. Also monitor HUD and Dash keybindings and blacklist them.
We also now listen to the indicators signals about opening invalid entries as an hint to close our menus if the focused window has just been changed.
Added new AP tests.
Brandon Schaefer (brandontschaefer) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:3562
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Brandon Schaefer (brandontschaefer) wrote : | # |
It working now! Forgot to start my compiled version of u-p-s :).
Everything looks good...but im also tired haha... would like a second set of eyes, or ill re-review it tomorrow.
Christopher Townsend (townsend) wrote : | # |
This looks all good. Very nice!
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:3563
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'launcher/Launcher.cpp' |
2 | --- launcher/Launcher.cpp 2013-10-01 23:51:59 +0000 |
3 | +++ launcher/Launcher.cpp 2013-10-03 15:16:29 +0000 |
4 | @@ -1188,8 +1188,9 @@ |
5 | |
6 | if (icon_under_mouse_) |
7 | icon_under_mouse_->HideTooltip(); |
8 | + |
9 | + QueueDraw(); |
10 | } |
11 | - QueueDraw(); |
12 | } |
13 | |
14 | void Launcher::OnOverlayHidden(GVariant* data) |
15 | |
16 | === modified file 'launcher/SwitcherController.cpp' |
17 | --- launcher/SwitcherController.cpp 2013-09-30 16:21:14 +0000 |
18 | +++ launcher/SwitcherController.cpp 2013-10-03 15:16:29 +0000 |
19 | @@ -471,10 +471,10 @@ |
20 | ResetDetailTimer(obj_->detail_timeout_length); |
21 | }); |
22 | |
23 | - view_->switcher_next.connect(sigc::mem_fun(this, &Controller::Impl::Next)); |
24 | - view_->switcher_prev.connect(sigc::mem_fun(this, &Controller::Impl::Prev)); |
25 | - view_->switcher_start_detail.connect(sigc::mem_fun(this, &Controller::Impl::StartDetailMode)); |
26 | - view_->switcher_stop_detail.connect(sigc::mem_fun(this, &Controller::Impl::StopDetailMode)); |
27 | + view_->switcher_next.connect(sigc::mem_fun(this, &Impl::Next)); |
28 | + view_->switcher_prev.connect(sigc::mem_fun(this, &Impl::Prev)); |
29 | + view_->switcher_start_detail.connect(sigc::mem_fun(this, &Impl::StartDetailMode)); |
30 | + view_->switcher_stop_detail.connect(sigc::mem_fun(this, &Impl::StopDetailMode)); |
31 | |
32 | ConstructWindow(); |
33 | main_layout_->AddView(view_.GetPointer(), 1); |
34 | |
35 | === modified file 'launcher/SwitcherView.h' |
36 | --- launcher/SwitcherView.h 2013-09-30 16:21:14 +0000 |
37 | +++ launcher/SwitcherView.h 2013-10-03 15:16:29 +0000 |
38 | @@ -76,17 +76,17 @@ |
39 | int DetailIconIdexAt(int x, int y) const; |
40 | |
41 | /* void; int icon_index, int button*/ |
42 | - sigc::signal<void, int, int> switcher_mouse_down; |
43 | - sigc::signal<void, int, int> switcher_mouse_up; |
44 | + sigc::signal<void, int, int> switcher_mouse_down; |
45 | + sigc::signal<void, int, int> switcher_mouse_up; |
46 | |
47 | /* void; int icon_index */ |
48 | - sigc::signal<void, int> switcher_mouse_move; |
49 | + sigc::signal<void, int> switcher_mouse_move; |
50 | |
51 | /* void; */ |
52 | - sigc::signal<void> switcher_next; |
53 | - sigc::signal<void> switcher_prev; |
54 | - sigc::signal<void> switcher_start_detail; |
55 | - sigc::signal<void> switcher_stop_detail; |
56 | + sigc::signal<void> switcher_next; |
57 | + sigc::signal<void> switcher_prev; |
58 | + sigc::signal<void> switcher_start_detail; |
59 | + sigc::signal<void> switcher_stop_detail; |
60 | |
61 | /* void; bool visible */ |
62 | sigc::signal<void, bool> hide_request; |
63 | |
64 | === added file 'services/panel-service-private.h' |
65 | --- services/panel-service-private.h 1970-01-01 00:00:00 +0000 |
66 | +++ services/panel-service-private.h 2013-10-03 15:16:29 +0000 |
67 | @@ -0,0 +1,41 @@ |
68 | +/* |
69 | + * Copyright (C) 2013 Canonical Ltd |
70 | + * |
71 | + * This program is free software: you can redistribute it and/or modify |
72 | + * it under the terms of the GNU General Public License version 3 as |
73 | + * published by the Free Software Foundation. |
74 | + * |
75 | + * This program is distributed in the hope that it will be useful, |
76 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
77 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
78 | + * GNU General Public License for more details. |
79 | + * |
80 | + * You should have received a copy of the GNU General Public License |
81 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
82 | + * |
83 | + * Authored by: Marco Trevisan <marco.trevisan@canonical.com> |
84 | + */ |
85 | + |
86 | +#ifndef _PANEL_SERVICE_PRIVATE_H_ |
87 | +#define _PANEL_SERVICE_PRIVATE_H_ |
88 | + |
89 | +#include <X11/Xlib.h> |
90 | +#include <X11/keysym.h> |
91 | + |
92 | +G_BEGIN_DECLS |
93 | + |
94 | +typedef struct _KeyBinding |
95 | +{ |
96 | + KeySym key; |
97 | + KeySym fallback; |
98 | + guint32 modifiers; |
99 | +} KeyBinding; |
100 | + |
101 | +#define AltMask Mod1Mask |
102 | +#define SuperMask Mod4Mask |
103 | + |
104 | +void parse_string_keybinding (const char *, KeyBinding *); |
105 | + |
106 | +G_END_DECLS |
107 | + |
108 | +#endif /* _PANEL_SERVICE_PRIVATE_H_ */ |
109 | |
110 | === modified file 'services/panel-service.c' |
111 | --- services/panel-service.c 2013-09-24 00:27:02 +0000 |
112 | +++ services/panel-service.c 2013-10-03 15:16:29 +0000 |
113 | @@ -21,6 +21,7 @@ |
114 | |
115 | #include "config.h" |
116 | #include "panel-service.h" |
117 | +#include "panel-service-private.h" |
118 | |
119 | #include <stdlib.h> |
120 | #include <string.h> |
121 | @@ -29,8 +30,8 @@ |
122 | #include <glib/gi18n-lib.h> |
123 | #include <libindicator/indicator-ng.h> |
124 | |
125 | +#include <X11/XKBlib.h> |
126 | #include <X11/extensions/XInput2.h> |
127 | -#include <X11/XKBlib.h> |
128 | |
129 | #include <upstart.h> |
130 | #include <nih/alloc.h> |
131 | @@ -47,6 +48,8 @@ |
132 | #define COMPIZ_OPTION_SCHEMA "org.compiz.unityshell" |
133 | #define COMPIZ_OPTION_PATH "/org/compiz/profiles/unity/plugins/" |
134 | #define MENU_TOGGLE_KEYBINDING_KEY "panel-first-menu" |
135 | +#define SHOW_DASH_KEY "show-launcher" |
136 | +#define SHOW_HUD_KEY "show-hud" |
137 | |
138 | static PanelService *static_service = NULL; |
139 | |
140 | @@ -62,8 +65,6 @@ |
141 | GtkWidget *menubar; |
142 | GtkWidget *offscreen_window; |
143 | GtkMenu *last_menu; |
144 | - guint32 last_menu_id; |
145 | - guint32 last_menu_move_id; |
146 | gint32 last_x; |
147 | gint32 last_y; |
148 | gint last_left; |
149 | @@ -72,9 +73,10 @@ |
150 | gint last_bottom; |
151 | guint32 last_menu_button; |
152 | |
153 | - KeyCode toggle_key; |
154 | - guint32 toggle_modifiers; |
155 | GSettings *gsettings; |
156 | + KeyBinding menu_toggle; |
157 | + KeyBinding show_dash; |
158 | + KeyBinding show_hud; |
159 | |
160 | IndicatorObjectEntry *pressed_entry; |
161 | gboolean use_event; |
162 | @@ -125,22 +127,13 @@ |
163 | }; |
164 | |
165 | /* Forwards */ |
166 | -static void load_indicator (PanelService *self, |
167 | - IndicatorObject *object, |
168 | - const gchar *_name); |
169 | -static void load_indicators (PanelService *self); |
170 | -static void load_indicators_from_indicator_files (PanelService *self); |
171 | -static void sort_indicators (PanelService *self); |
172 | - |
173 | +static void load_indicator (PanelService *, IndicatorObject *, const gchar *); |
174 | +static void load_indicators (PanelService *); |
175 | +static void load_indicators_from_indicator_files (PanelService *); |
176 | +static void sort_indicators (PanelService *); |
177 | static void notify_object (IndicatorObject *object); |
178 | - |
179 | -static GdkFilterReturn event_filter (GdkXEvent *ev, |
180 | - GdkEvent *gev, |
181 | - PanelService *self); |
182 | - |
183 | -static void on_keybinding_changed (GSettings *settings, |
184 | - gchar *key, |
185 | - gpointer data); |
186 | +static void update_keybinding (GSettings *, const gchar *, gpointer); |
187 | +static GdkFilterReturn event_filter (GdkXEvent *, GdkEvent *, PanelService *); |
188 | |
189 | /* |
190 | * GObject stuff |
191 | @@ -184,7 +177,8 @@ |
192 | if (GTK_IS_WIDGET (priv->last_menu) && |
193 | gtk_widget_get_realized (GTK_WIDGET (priv->last_menu))) |
194 | { |
195 | - g_object_unref (priv->last_menu); |
196 | + gtk_menu_popdown (GTK_MENU (priv->last_menu)); |
197 | + g_signal_handlers_disconnect_by_data (priv->last_menu, self); |
198 | priv->last_menu = NULL; |
199 | } |
200 | |
201 | @@ -199,7 +193,8 @@ |
202 | |
203 | if (G_IS_OBJECT (priv->gsettings)) |
204 | { |
205 | - g_signal_handlers_disconnect_by_func (priv->gsettings, on_keybinding_changed, self); |
206 | + g_signal_handlers_disconnect_matched (priv->gsettings, G_SIGNAL_MATCH_FUNC, |
207 | + 0, 0, NULL, update_keybinding, NULL); |
208 | g_object_unref (priv->gsettings); |
209 | priv->gsettings = NULL; |
210 | } |
211 | @@ -216,6 +211,8 @@ |
212 | g_hash_table_destroy (priv->panel2entries_hash); |
213 | |
214 | static_service = NULL; |
215 | + |
216 | + G_OBJECT_CLASS (panel_service_parent_class)->finalize (object); |
217 | } |
218 | |
219 | static void |
220 | @@ -357,6 +354,42 @@ |
221 | return entry; |
222 | } |
223 | |
224 | +static void |
225 | +reinject_key_event_to_root_window (XIDeviceEvent *ev) |
226 | +{ |
227 | + XKeyEvent kev; |
228 | + kev.display = ev->display; |
229 | + kev.window = ev->root; |
230 | + kev.root = ev->root; |
231 | + kev.subwindow = None; |
232 | + kev.time = ev->time; |
233 | + kev.x = ev->event_x; |
234 | + kev.y = ev->event_x; |
235 | + kev.x_root = ev->root_x; |
236 | + kev.y_root = ev->root_y; |
237 | + kev.same_screen = True; |
238 | + kev.keycode = ev->detail; |
239 | + kev.state = ev->mods.base; |
240 | + kev.type = ev->evtype; |
241 | + |
242 | + XSendEvent (kev.display, kev.root, True, KeyPressMask, (XEvent*) &kev); |
243 | + XFlush (kev.display); |
244 | +} |
245 | + |
246 | +static gboolean |
247 | +event_matches_keybinding (guint32 modifiers, KeySym key, KeyBinding *kb) |
248 | +{ |
249 | + if (modifiers == kb->modifiers && key != NoSymbol) |
250 | + { |
251 | + if (key == kb->key || key == kb->fallback) |
252 | + { |
253 | + return TRUE; |
254 | + } |
255 | + } |
256 | + |
257 | + return FALSE; |
258 | +} |
259 | + |
260 | static GdkFilterReturn |
261 | event_filter (GdkXEvent *ev, GdkEvent *gev, PanelService *self) |
262 | { |
263 | @@ -365,41 +398,65 @@ |
264 | GdkFilterReturn ret = GDK_FILTER_CONTINUE; |
265 | |
266 | if (!PANEL_IS_SERVICE (self)) |
267 | - { |
268 | - g_warning ("%s: Invalid PanelService instance", G_STRLOC); |
269 | - return ret; |
270 | - } |
271 | + { |
272 | + g_warning ("%s: Invalid PanelService instance", G_STRLOC); |
273 | + return ret; |
274 | + } |
275 | |
276 | if (!GTK_IS_WIDGET (self->priv->last_menu)) |
277 | return ret; |
278 | |
279 | /* Use XI2 to read the event data */ |
280 | XGenericEventCookie *cookie = &e->xcookie; |
281 | - if (cookie->type == GenericEvent) |
282 | + if (cookie->type != GenericEvent) |
283 | + return ret; |
284 | + |
285 | + XIDeviceEvent *event = cookie->data; |
286 | + if (!event) |
287 | + return ret; |
288 | + |
289 | + switch (event->evtype) |
290 | { |
291 | - XIDeviceEvent *event = cookie->data; |
292 | - if (!event) |
293 | - return ret; |
294 | - |
295 | - if (event->evtype == XI_KeyPress) |
296 | + case XI_KeyPress: |
297 | { |
298 | - if (event->mods.base == priv->toggle_modifiers && event->detail == priv->toggle_key) |
299 | - { |
300 | - if (GTK_IS_MENU (priv->last_menu)) |
301 | - gtk_menu_popdown (GTK_MENU (priv->last_menu)); |
302 | - } |
303 | + KeySym keysym = XkbKeycodeToKeysym (event->display, event->detail, 0, 0); |
304 | + |
305 | + if (event_matches_keybinding (event->mods.base, keysym, &priv->menu_toggle) || |
306 | + event_matches_keybinding (event->mods.base, keysym, &priv->show_dash) || |
307 | + event_matches_keybinding (event->mods.base, keysym, &priv->show_hud)) |
308 | + { |
309 | + if (GTK_IS_MENU (priv->last_menu)) |
310 | + gtk_menu_popdown (GTK_MENU (priv->last_menu)); |
311 | + |
312 | + ret = GDK_FILTER_REMOVE; |
313 | + } |
314 | + else if (event->mods.base != GDK_CONTROL_MASK) |
315 | + { |
316 | + if (!IsModifierKey (keysym) && (event->mods.base != 0 || keysym == XK_Print)) |
317 | + { |
318 | + if (GTK_IS_MENU (priv->last_menu)) |
319 | + gtk_menu_popdown (GTK_MENU (priv->last_menu)); |
320 | + |
321 | + reinject_key_event_to_root_window (event); |
322 | + ret = GDK_FILTER_REMOVE; |
323 | + } |
324 | + } |
325 | + |
326 | + break; |
327 | } |
328 | |
329 | - if (event->evtype == XI_ButtonPress) |
330 | + case XI_ButtonPress: |
331 | { |
332 | priv->pressed_entry = get_entry_at (self, event->root_x, event->root_y); |
333 | priv->use_event = (priv->pressed_entry == NULL); |
334 | |
335 | if (priv->pressed_entry) |
336 | ret = GDK_FILTER_REMOVE; |
337 | + |
338 | + break; |
339 | } |
340 | |
341 | - if (event->evtype == XI_ButtonRelease) |
342 | + case XI_ButtonRelease: |
343 | { |
344 | IndicatorObjectEntry *entry; |
345 | gboolean event_is_a_click = FALSE; |
346 | @@ -469,6 +526,8 @@ |
347 | ret = GDK_FILTER_REMOVE; |
348 | g_free (entry_id); |
349 | } |
350 | + |
351 | + break; |
352 | } |
353 | } |
354 | |
355 | @@ -497,20 +556,28 @@ |
356 | } |
357 | |
358 | static void |
359 | -panel_service_update_menu_keybinding (PanelService *self) |
360 | -{ |
361 | - gchar *binding = g_settings_get_string (self->priv->gsettings, MENU_TOGGLE_KEYBINDING_KEY); |
362 | - |
363 | - KeyCode keycode = 0; |
364 | - KeySym keysym = NoSymbol; |
365 | - guint32 modifiers = 0; |
366 | - |
367 | +update_keybinding (GSettings *settings, const gchar *key, gpointer data) |
368 | +{ |
369 | + KeyBinding *kb = data; |
370 | + gchar *binding = g_settings_get_string (settings, key); |
371 | + parse_string_keybinding (binding, kb); |
372 | + g_free (binding); |
373 | +} |
374 | + |
375 | +void |
376 | +parse_string_keybinding (const char *str, KeyBinding *kb) |
377 | +{ |
378 | + kb->key = NoSymbol; |
379 | + kb->fallback = NoSymbol; |
380 | + kb->modifiers = 0; |
381 | + |
382 | + gchar *binding = g_strdup (str); |
383 | gchar *keystart = (binding) ? strrchr (binding, '>') : NULL; |
384 | |
385 | if (!keystart) |
386 | keystart = binding; |
387 | |
388 | - while (keystart && !g_ascii_isalnum (*keystart)) |
389 | + while (keystart && *keystart != '\0' && !g_ascii_isalnum (*keystart)) |
390 | keystart++; |
391 | |
392 | gchar *keyend = keystart; |
393 | @@ -519,51 +586,63 @@ |
394 | keyend++; |
395 | |
396 | if (keystart != keyend) |
397 | - { |
398 | - gchar *keystr = g_strndup (keystart, keyend-keystart); |
399 | - keysym = XStringToKeysym (keystr); |
400 | - g_free (keystr); |
401 | - } |
402 | - |
403 | - if (keysym != NoSymbol) |
404 | - { |
405 | - Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); |
406 | - keycode = XKeysymToKeycode(dpy, keysym); |
407 | - |
408 | + { |
409 | + gchar *keystr = g_strndup (keystart, keyend-keystart); |
410 | + kb->key = XStringToKeysym (keystr); |
411 | + g_free (keystr); |
412 | + } |
413 | + else |
414 | + { |
415 | + /* Parsing the case where we only have meta-keys */ |
416 | + keyend = (binding) ? strrchr (binding, '>') : NULL; |
417 | + keyend = keyend ? keyend - 1 : NULL; |
418 | + keystart = keyend; |
419 | + |
420 | + while (keystart && keystart > binding && g_ascii_isalnum (*keystart)) |
421 | + keystart--; |
422 | + |
423 | + if (keystart != keyend) |
424 | + { |
425 | + gchar *keystr = g_strndup (keystart+1, keyend-keystart); |
426 | + gchar *left = g_strconcat (keystr, "_L", NULL); |
427 | + kb->key = XStringToKeysym (left); |
428 | + |
429 | + gchar *right = g_strconcat (keystr, "_R", NULL); |
430 | + kb->fallback = XStringToKeysym (right); |
431 | + g_free (left); |
432 | + g_free (right); |
433 | + g_free (keystr); |
434 | + |
435 | + keystr = g_strndup (binding, keystart-binding); |
436 | + g_free (binding); |
437 | + binding = keystr; |
438 | + } |
439 | + } |
440 | + |
441 | + if (kb->key != NoSymbol) |
442 | + { |
443 | if (g_strrstr (binding, "<Shift>")) |
444 | { |
445 | - modifiers |= GDK_SHIFT_MASK; |
446 | + kb->modifiers |= ShiftMask; |
447 | } |
448 | if (g_strrstr (binding, "<Control>") || g_strrstr (binding, "<Primary>")) |
449 | { |
450 | - modifiers |= GDK_CONTROL_MASK; |
451 | + kb->modifiers |= ControlMask; |
452 | } |
453 | if (g_strrstr (binding, "<Alt>") || g_strrstr (binding, "<Mod1>")) |
454 | { |
455 | - modifiers |= GDK_MOD1_MASK; |
456 | + kb->modifiers |= AltMask; |
457 | } |
458 | if (g_strrstr (binding, "<Super>")) |
459 | { |
460 | - modifiers |= GDK_SUPER_MASK; |
461 | + kb->modifiers |= SuperMask; |
462 | } |
463 | } |
464 | |
465 | - self->priv->toggle_key = keycode; |
466 | - self->priv->toggle_modifiers = modifiers; |
467 | - |
468 | g_free (binding); |
469 | } |
470 | |
471 | static void |
472 | -on_keybinding_changed (GSettings *settings, gchar *key, gpointer data) |
473 | -{ |
474 | - g_return_if_fail (PANEL_IS_SERVICE (data)); |
475 | - PanelService *self = data; |
476 | - |
477 | - panel_service_update_menu_keybinding (self); |
478 | -} |
479 | - |
480 | -static void |
481 | panel_service_init (PanelService *self) |
482 | { |
483 | PanelServicePrivate *priv; |
484 | @@ -582,15 +661,20 @@ |
485 | |
486 | priv->gsettings = g_settings_new_with_path (COMPIZ_OPTION_SCHEMA, COMPIZ_OPTION_PATH); |
487 | g_signal_connect (priv->gsettings, "changed::"MENU_TOGGLE_KEYBINDING_KEY, |
488 | - G_CALLBACK (on_keybinding_changed), self); |
489 | - |
490 | - panel_service_update_menu_keybinding (self); |
491 | - |
492 | - const gchar * upstartsession = g_getenv ("UPSTART_SESSION"); |
493 | + G_CALLBACK (update_keybinding), &priv->menu_toggle); |
494 | + g_signal_connect (priv->gsettings, "changed::"SHOW_DASH_KEY, |
495 | + G_CALLBACK (update_keybinding), &priv->show_dash); |
496 | + g_signal_connect (priv->gsettings, "changed::"SHOW_HUD_KEY, |
497 | + G_CALLBACK (update_keybinding), &priv->show_hud); |
498 | + |
499 | + update_keybinding (priv->gsettings, MENU_TOGGLE_KEYBINDING_KEY, &priv->menu_toggle); |
500 | + update_keybinding (priv->gsettings, SHOW_DASH_KEY, &priv->show_dash); |
501 | + update_keybinding (priv->gsettings, SHOW_HUD_KEY, &priv->show_hud); |
502 | + |
503 | + const gchar *upstartsession = g_getenv ("UPSTART_SESSION"); |
504 | if (upstartsession != NULL) |
505 | { |
506 | - DBusConnection * conn = NULL; |
507 | - conn = dbus_connection_open (upstartsession, NULL); |
508 | + DBusConnection *conn = dbus_connection_open (upstartsession, NULL); |
509 | if (conn != NULL) |
510 | { |
511 | priv->upstart = nih_dbus_proxy_new (NULL, conn, |
512 | @@ -603,7 +687,6 @@ |
513 | |
514 | if (priv->upstart != NULL) |
515 | priv->upstart->auto_start = FALSE; |
516 | - |
517 | } |
518 | |
519 | static gboolean |
520 | @@ -1040,11 +1123,13 @@ |
521 | PanelService *self) |
522 | { |
523 | gchar *entry_id; |
524 | - |
525 | g_return_if_fail (PANEL_IS_SERVICE (self)); |
526 | - if (entry == NULL) |
527 | + |
528 | + if (!entry) |
529 | { |
530 | - g_warning ("on_indicator_menu_show() called with a NULL entry"); |
531 | + if (GTK_IS_MENU (self->priv->last_menu)) |
532 | + gtk_menu_popdown (GTK_MENU (self->priv->last_menu)); |
533 | + |
534 | return; |
535 | } |
536 | |
537 | @@ -1060,11 +1145,11 @@ |
538 | PanelService *self) |
539 | { |
540 | gchar *entry_id; |
541 | - |
542 | g_return_if_fail (PANEL_IS_SERVICE (self)); |
543 | - if (entry == NULL) |
544 | + |
545 | + if (!entry) |
546 | { |
547 | - g_warning ("on_indicator_menu_show_now_changed() called with a NULL entry"); |
548 | + g_warning ("%s called with a NULL entry", G_STRFUNC); |
549 | return; |
550 | } |
551 | |
552 | @@ -1443,12 +1528,7 @@ |
553 | priv->last_y = 0; |
554 | priv->last_menu_button = 0; |
555 | |
556 | - g_signal_handler_disconnect (priv->last_menu, priv->last_menu_id); |
557 | - g_signal_handler_disconnect (priv->last_menu, priv->last_menu_move_id); |
558 | - |
559 | priv->last_menu = NULL; |
560 | - priv->last_menu_id = 0; |
561 | - priv->last_menu_move_id = 0; |
562 | priv->last_entry = NULL; |
563 | priv->last_left = 0; |
564 | priv->last_right = 0; |
565 | @@ -1857,13 +1937,10 @@ |
566 | priv->last_x = 0; |
567 | priv->last_y = 0; |
568 | |
569 | - g_signal_handler_disconnect (priv->last_menu, priv->last_menu_id); |
570 | - g_signal_handler_disconnect (priv->last_menu, priv->last_menu_move_id); |
571 | + g_signal_handlers_disconnect_by_data (priv->last_menu, self); |
572 | |
573 | priv->last_entry = NULL; |
574 | priv->last_menu = NULL; |
575 | - priv->last_menu_id = 0; |
576 | - priv->last_menu_move_id = 0; |
577 | priv->last_menu_button = 0; |
578 | } |
579 | |
580 | @@ -1905,10 +1982,9 @@ |
581 | priv->last_x = x; |
582 | priv->last_y = y; |
583 | priv->last_menu_button = button; |
584 | - priv->last_menu_id = g_signal_connect (priv->last_menu, "hide", |
585 | - G_CALLBACK (on_active_menu_hidden), self); |
586 | - priv->last_menu_move_id = g_signal_connect_after (priv->last_menu, "move-current", |
587 | - G_CALLBACK (on_active_menu_move_current), self); |
588 | + g_signal_connect (priv->last_menu, "hide", G_CALLBACK (on_active_menu_hidden), self); |
589 | + g_signal_connect_after (priv->last_menu, "move-current", |
590 | + G_CALLBACK (on_active_menu_move_current), self); |
591 | |
592 | gtk_menu_popup (priv->last_menu, NULL, NULL, positon_menu, self, 0, CurrentTime); |
593 | gtk_menu_reposition (priv->last_menu); |
594 | |
595 | === modified file 'tests/autopilot/unity/emulators/window_manager.py' |
596 | --- tests/autopilot/unity/emulators/window_manager.py 2012-07-04 02:37:23 +0000 |
597 | +++ tests/autopilot/unity/emulators/window_manager.py 2013-10-03 15:16:29 +0000 |
598 | @@ -25,6 +25,14 @@ |
599 | """Returns a tuple of (x,y,w,h) for the screen.""" |
600 | return (self.x, self.y, self.width, self.height) |
601 | |
602 | + def initiate_spread(self): |
603 | + self.keybinding("spread/start") |
604 | + self.scale_active.wait_for(True) |
605 | + |
606 | + def terminate_spread(self): |
607 | + self.keybinding("spread/cancel") |
608 | + self.scale_active.wait_for(False) |
609 | + |
610 | def enter_show_desktop(self): |
611 | if not self.showdesktop_active: |
612 | logger.info("Entering show desktop mode.") |
613 | |
614 | === modified file 'tests/autopilot/unity/tests/test_panel.py' |
615 | --- tests/autopilot/unity/tests/test_panel.py 2013-05-10 05:16:07 +0000 |
616 | +++ tests/autopilot/unity/tests/test_panel.py 2013-10-03 15:16:29 +0000 |
617 | @@ -919,7 +919,6 @@ |
618 | |
619 | self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) |
620 | |
621 | - |
622 | def test_menus_dont_show_with_hud(self): |
623 | """Tests that menus are not showing when opening the HUD.""" |
624 | self.open_new_application_window("Text Editor", maximized=True) |
625 | @@ -982,6 +981,17 @@ |
626 | self.assertThat(menu_entry.menu_x, Eventually(Equals(0))) |
627 | self.assertThat(menu_entry.menu_y, Eventually(Equals(0))) |
628 | |
629 | + def test_menu_closes_on_new_focused_application(self): |
630 | + """Clicking outside an open menu must close it.""" |
631 | + menu_entry = self.open_app_and_get_menu_entry() |
632 | + self.mouse_open_indicator(menu_entry) |
633 | + |
634 | + # This assert is for timing purposes only: |
635 | + self.assertThat(menu_entry.active, Eventually(Equals(True))) |
636 | + |
637 | + self.open_new_application_window("Text Editor") |
638 | + self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) |
639 | + |
640 | def test_indicator_opens_when_dash_is_open(self): |
641 | """When the dash is open and a click is on an indicator the dash |
642 | must close and the indicator must open. |
643 | @@ -1011,9 +1021,10 @@ |
644 | def test_panel_first_menu_show_works(self): |
645 | """Pressing the open-menus keybinding must open the first indicator.""" |
646 | self.open_new_application_window("Calculator") |
647 | - sleep(1) |
648 | + refresh_fn = lambda: len(self.panel.menus.get_entries()) |
649 | + self.assertThat(refresh_fn, Eventually(GreaterThan(0))) |
650 | + self.addCleanup(self.keyboard.press_and_release, "Escape") |
651 | self.keybinding("panel/open_first_menu") |
652 | - self.addCleanup(self.keyboard.press_and_release, "Escape") |
653 | |
654 | open_indicator = self.get_active_indicator() |
655 | expected_indicator = self.panel.get_indicator_entries(include_hidden_menus=True)[0] |
656 | @@ -1025,9 +1036,10 @@ |
657 | def test_panel_menu_accelerators_work(self): |
658 | """Pressing a valid menu accelerator must open the correct menu item.""" |
659 | self.open_new_application_window("Text Editor") |
660 | - sleep(1) |
661 | + refresh_fn = lambda: len(self.panel.menus.get_entries()) |
662 | + self.assertThat(refresh_fn, Eventually(GreaterThan(0))) |
663 | + self.addCleanup(self.keyboard.press_and_release, "Escape") |
664 | self.keyboard.press_and_release("Alt+f") |
665 | - self.addCleanup(self.keyboard.press_and_release, "Escape") |
666 | |
667 | open_indicator = self.get_active_indicator() |
668 | self.assertThat(open_indicator.label, Eventually(Equals("_File"))) |
669 | |
670 | === modified file 'tests/autopilot/unity/tests/test_spread.py' |
671 | --- tests/autopilot/unity/tests/test_spread.py 2013-05-01 00:18:11 +0000 |
672 | +++ tests/autopilot/unity/tests/test_spread.py 2013-10-03 15:16:29 +0000 |
673 | @@ -36,8 +36,8 @@ |
674 | |
675 | def initiate_spread_for_screen(self): |
676 | """Initiate the Spread for all windows""" |
677 | - self.addCleanup(self.keybinding, "spread/cancel") |
678 | - self.keybinding("spread/start") |
679 | + self.addCleanup(self.unity.window_manager.terminate_spread) |
680 | + self.unity.window_manager.initiate_spread() |
681 | self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) |
682 | |
683 | def initiate_spread_for_application(self, desktop_id): |
684 | @@ -46,7 +46,7 @@ |
685 | self.assertThat(icon, NotEquals(None)) |
686 | launcher = self.unity.launcher.get_launcher_for_monitor(self.display.get_primary_screen()) |
687 | |
688 | - self.addCleanup(self.keybinding, "spread/cancel") |
689 | + self.addCleanup(self.unity.window_manager.terminate_spread) |
690 | launcher.click_launcher_icon(icon) |
691 | self.assertThat(self.unity.window_manager.scale_active_for_group, Eventually(Equals(True))) |
692 | |
693 | |
694 | === modified file 'tests/autopilot/unity/tests/test_wm_keybindings.py' |
695 | --- tests/autopilot/unity/tests/test_wm_keybindings.py 2013-09-16 13:59:22 +0000 |
696 | +++ tests/autopilot/unity/tests/test_wm_keybindings.py 2013-10-03 15:16:29 +0000 |
697 | @@ -9,17 +9,70 @@ |
698 | from __future__ import absolute_import |
699 | |
700 | from autopilot.matchers import Eventually |
701 | -from testtools.matchers import Equals, NotEquals |
702 | +from testtools.matchers import Equals, NotEquals, GreaterThan |
703 | from unity.tests import UnityTestCase |
704 | +from unity.emulators import switcher |
705 | |
706 | class WindowManagerKeybindings(UnityTestCase): |
707 | """Window Manager keybindings tests""" |
708 | |
709 | + def setUp(self): |
710 | + super(WindowManagerKeybindings, self).setUp() |
711 | + |
712 | + def open_panel_menu(self): |
713 | + panel = self.unity.panels.get_panel_for_monitor(0) |
714 | + self.assertThat(lambda: len(panel.menus.get_entries()), Eventually(GreaterThan(0))) |
715 | + self.addCleanup(self.keyboard.press_and_release, "Escape") |
716 | + self.keybinding("panel/open_first_menu") |
717 | + self.assertThat(self.unity.panels.get_active_indicator, Eventually(NotEquals(None))) |
718 | + |
719 | + def test_dash_shows_on_menus_opened(self): |
720 | + self.process_manager.start_app_window("Calculator") |
721 | + self.open_panel_menu() |
722 | + self.addCleanup(self.unity.dash.ensure_hidden) |
723 | + self.unity.dash.ensure_visible() |
724 | + self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) |
725 | + |
726 | + def test_hud_shows_on_menus_opened(self): |
727 | + self.process_manager.start_app_window("Calculator") |
728 | + self.open_panel_menu() |
729 | + self.addCleanup(self.unity.hud.ensure_hidden) |
730 | + self.unity.hud.ensure_visible() |
731 | + self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) |
732 | + |
733 | + def test_switcher_shows_on_menus_opened(self): |
734 | + self.process_manager.start_app_window("Calculator") |
735 | + self.open_panel_menu() |
736 | + self.addCleanup(self.unity.switcher.terminate) |
737 | + self.unity.switcher.initiate() |
738 | + self.assertProperty(self.unity.switcher, mode=switcher.SwitcherMode.NORMAL) |
739 | + self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) |
740 | + |
741 | + def test_shortcut_hints_shows_on_menus_opened(self): |
742 | + self.process_manager.start_app_window("Calculator") |
743 | + self.open_panel_menu() |
744 | + self.addCleanup(self.unity.shortcut_hint.ensure_hidden) |
745 | + self.unity.shortcut_hint.show() |
746 | + self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(True))) |
747 | + self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) |
748 | + |
749 | + def test_spread_shows_on_menus_opened(self): |
750 | + self.process_manager.start_app_window("Calculator") |
751 | + self.open_panel_menu() |
752 | + self.addCleanup(self.unity.window_manager.terminate_spread) |
753 | + self.unity.window_manager.initiate_spread() |
754 | + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) |
755 | + self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) |
756 | + |
757 | + |
758 | +class WindowManagerKeybindingsForWindowHandling(WindowManagerKeybindings): |
759 | + """Window Manager keybindings tests for handling a window""" |
760 | + |
761 | scenarios = [('Restored Window', {'start_restored': True}), |
762 | ('Maximized Window', {'start_restored': False})] |
763 | |
764 | def setUp(self): |
765 | - super(WindowManagerKeybindings, self).setUp() |
766 | + super(WindowManagerKeybindingsForWindowHandling, self).setUp() |
767 | self.start_test_window() |
768 | |
769 | def keybinding_if_not_minimized(self, keybinding): |
770 | |
771 | === modified file 'tests/data/external.gschema.xml' |
772 | --- tests/data/external.gschema.xml 2013-07-12 23:24:53 +0000 |
773 | +++ tests/data/external.gschema.xml 2013-10-03 15:16:29 +0000 |
774 | @@ -5,15 +5,17 @@ |
775 | <schema path="/apps/indicator-session/" id="com.canonical.indicator.session"> |
776 | <key type="b" name="suppress-logout-restart-shutdown"> |
777 | <default>false</default> |
778 | - <summary>Suppress the dialog to confirm logout, restart and shutdown action</summary> |
779 | - <description>Whether or not to show confirmation dialogs for logout, restart and shutdown actions.</description> |
780 | </key> |
781 | </schema> |
782 | <schema id="org.compiz.unityshell" gettext-domain="compiz"> |
783 | <key type="s" name="panel-first-menu"> |
784 | <default>'<Alt>F10'</default> |
785 | - <summary>Key to open the first panel menu</summary> |
786 | - <description>Opens the first indicator menu of the Panel, allowing keyboard navigation thereafter.</description> |
787 | + </key> |
788 | + <key type="s" name="show-launcher"> |
789 | + <default>'<Super>'</default> |
790 | + </key> |
791 | + <key type="s" name="show-hud"> |
792 | + <default>'<Alt>'</default> |
793 | </key> |
794 | </schema> |
795 | </schemalist> |
796 | |
797 | === modified file 'tests/test_panel_service.cpp' |
798 | --- tests/test_panel_service.cpp 2013-07-25 11:07:06 +0000 |
799 | +++ tests/test_panel_service.cpp 2013-10-03 15:16:29 +0000 |
800 | @@ -22,6 +22,7 @@ |
801 | #include <UnityCore/GLibWrapper.h> |
802 | #include <UnityCore/Variant.h> |
803 | #include "panel-service.h" |
804 | +#include "panel-service-private.h" |
805 | #include "mock_indicator_object.h" |
806 | |
807 | using namespace testing; |
808 | @@ -418,4 +419,134 @@ |
809 | EXPECT_TRUE(called); |
810 | } |
811 | |
812 | +TEST(TestPanelServiceCompizShortcutParsing, Null) |
813 | +{ |
814 | + KeyBinding kb; |
815 | + parse_string_keybinding(NULL, &kb); |
816 | + |
817 | + EXPECT_EQ(NoSymbol, kb.key); |
818 | + EXPECT_EQ(NoSymbol, kb.fallback); |
819 | + EXPECT_EQ(0, kb.modifiers); |
820 | +} |
821 | + |
822 | +TEST(TestPanelServiceCompizShortcutParsing, Empty) |
823 | +{ |
824 | + KeyBinding kb; |
825 | + parse_string_keybinding("", &kb); |
826 | + |
827 | + EXPECT_EQ(NoSymbol, kb.key); |
828 | + EXPECT_EQ(NoSymbol, kb.fallback); |
829 | + EXPECT_EQ(0, kb.modifiers); |
830 | +} |
831 | + |
832 | +TEST(TestPanelServiceCompizShortcutParsing, SimpleKey) |
833 | +{ |
834 | + KeyBinding kb; |
835 | + parse_string_keybinding("U", &kb); |
836 | + |
837 | + EXPECT_EQ(XK_U, kb.key); |
838 | + EXPECT_EQ(NoSymbol, kb.fallback); |
839 | + EXPECT_EQ(0, kb.modifiers); |
840 | +} |
841 | + |
842 | +TEST(TestPanelServiceCompizShortcutParsing, ControlCombo) |
843 | +{ |
844 | + KeyBinding kb; |
845 | + parse_string_keybinding("<Control>F1", &kb); |
846 | + |
847 | + EXPECT_EQ(XK_F1, kb.key); |
848 | + EXPECT_EQ(NoSymbol, kb.fallback); |
849 | + EXPECT_EQ(ControlMask, kb.modifiers); |
850 | +} |
851 | + |
852 | +TEST(TestPanelServiceCompizShortcutParsing, AltCombo) |
853 | +{ |
854 | + KeyBinding kb; |
855 | + parse_string_keybinding("<Alt>F2", &kb); |
856 | + |
857 | + EXPECT_EQ(XK_F2, kb.key); |
858 | + EXPECT_EQ(NoSymbol, kb.fallback); |
859 | + EXPECT_EQ(AltMask, kb.modifiers); |
860 | +} |
861 | + |
862 | +TEST(TestPanelServiceCompizShortcutParsing, ShiftCombo) |
863 | +{ |
864 | + KeyBinding kb; |
865 | + parse_string_keybinding("<Shift>F3", &kb); |
866 | + |
867 | + EXPECT_EQ(XK_F3, kb.key); |
868 | + EXPECT_EQ(NoSymbol, kb.fallback); |
869 | + EXPECT_EQ(ShiftMask, kb.modifiers); |
870 | +} |
871 | + |
872 | +TEST(TestPanelServiceCompizShortcutParsing, SuperCombo) |
873 | +{ |
874 | + KeyBinding kb; |
875 | + parse_string_keybinding("<Super>F4", &kb); |
876 | + |
877 | + EXPECT_EQ(XK_F4, kb.key); |
878 | + EXPECT_EQ(NoSymbol, kb.fallback); |
879 | + EXPECT_EQ(SuperMask, kb.modifiers); |
880 | +} |
881 | + |
882 | +TEST(TestPanelServiceCompizShortcutParsing, FullCombo) |
883 | +{ |
884 | + KeyBinding kb; |
885 | + parse_string_keybinding("<Control><Alt><Shift><Super>Escape", &kb); |
886 | + |
887 | + EXPECT_EQ(XK_Escape, kb.key); |
888 | + EXPECT_EQ(NoSymbol, kb.fallback); |
889 | + EXPECT_EQ(ControlMask|AltMask|ShiftMask|SuperMask, kb.modifiers); |
890 | +} |
891 | + |
892 | +TEST(TestPanelServiceCompizShortcutParsing, MetaKeyControl) |
893 | +{ |
894 | + KeyBinding kb; |
895 | + parse_string_keybinding("<Control>", &kb); |
896 | + |
897 | + EXPECT_EQ(XK_Control_L, kb.key); |
898 | + EXPECT_EQ(XK_Control_R, kb.fallback); |
899 | + EXPECT_EQ(0, kb.modifiers); |
900 | +} |
901 | + |
902 | +TEST(TestPanelServiceCompizShortcutParsing, MetaKeyAlt) |
903 | +{ |
904 | + KeyBinding kb; |
905 | + parse_string_keybinding("<Alt>", &kb); |
906 | + |
907 | + EXPECT_EQ(XK_Alt_L, kb.key); |
908 | + EXPECT_EQ(XK_Alt_R, kb.fallback); |
909 | + EXPECT_EQ(0, kb.modifiers); |
910 | +} |
911 | + |
912 | +TEST(TestPanelServiceCompizShortcutParsing, MetaKeyShift) |
913 | +{ |
914 | + KeyBinding kb; |
915 | + parse_string_keybinding("<Shift>", &kb); |
916 | + |
917 | + EXPECT_EQ(XK_Shift_L, kb.key); |
918 | + EXPECT_EQ(XK_Shift_R, kb.fallback); |
919 | + EXPECT_EQ(0, kb.modifiers); |
920 | +} |
921 | + |
922 | +TEST(TestPanelServiceCompizShortcutParsing, MetaKeySuper) |
923 | +{ |
924 | + KeyBinding kb; |
925 | + parse_string_keybinding("<Super>", &kb); |
926 | + |
927 | + EXPECT_EQ(XK_Super_L, kb.key); |
928 | + EXPECT_EQ(XK_Super_R, kb.fallback); |
929 | + EXPECT_EQ(0, kb.modifiers); |
930 | +} |
931 | + |
932 | +TEST(TestPanelServiceCompizShortcutParsing, MetaKeyMix) |
933 | +{ |
934 | + KeyBinding kb; |
935 | + parse_string_keybinding("<Control><Alt><Super>", &kb); |
936 | + |
937 | + EXPECT_EQ(XK_Super_L, kb.key); |
938 | + EXPECT_EQ(XK_Super_R, kb.fallback); |
939 | + EXPECT_EQ(ControlMask|AltMask, kb.modifiers); |
940 | +} |
941 | + |
942 | } // anonymous namespace |
Hmm my menus don't seem to want to close, but it is getting the super event. If I open the menu press super, the menu stays open then I click outside to close then the dash appears....