Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/extensions/global_shortcut_listener_gtk.h" | |
| 6 | |
| 7 #include <gdk/gdkx.h> | |
| 8 #include <X11/Xlib.h> | |
| 9 | |
| 10 #include "base/x11/x11_error_tracker.h" | |
| 11 #include "content/public/browser/browser_thread.h" | |
| 12 #include "ui/base/accelerators/accelerator.h" | |
| 13 #include "ui/events/keycodes/keyboard_code_conversion_x.h" | |
| 14 | |
| 15 using content::BrowserThread; | |
| 16 | |
| 17 namespace { | |
| 18 | |
| 19 static base::LazyInstance<extensions::GlobalShortcutListenerGtk> instance = | |
| 20 LAZY_INSTANCE_INITIALIZER; | |
| 21 | |
| 22 static const unsigned int kModifiersMasks[] = { | |
|
Finnur
2013/09/27 10:35:02
I would comment this with why we're keeping track
zhchbin
2013/09/27 13:47:20
Done.
| |
| 23 0, // only specified modifiers. | |
|
Finnur
2013/09/27 10:35:02
I would rephrase this: No additional modifier.
zhchbin
2013/09/27 13:47:20
Done.
| |
| 24 Mod2Mask, // NUM_LOCK | |
| 25 LockMask, // CAPS_LOCK | |
| 26 Mod5Mask, // SCROLL_LOCK | |
| 27 Mod2Mask | LockMask, | |
| 28 Mod2Mask | Mod5Mask, | |
| 29 LockMask | Mod5Mask, | |
| 30 Mod2Mask | LockMask | Mod5Mask | |
| 31 }; | |
| 32 | |
| 33 int GetNativeModifiers(const ui::Accelerator& accelerator) { | |
| 34 int modifiers = 0; | |
| 35 modifiers |= accelerator.IsShiftDown() ? ShiftMask : 0; | |
| 36 modifiers |= accelerator.IsCtrlDown() ? ControlMask : 0; | |
| 37 modifiers |= accelerator.IsAltDown() ? Mod1Mask : 0; | |
| 38 | |
| 39 return modifiers; | |
| 40 } | |
| 41 | |
| 42 } // namespace | |
| 43 | |
| 44 namespace extensions { | |
| 45 | |
| 46 // static | |
| 47 GlobalShortcutListener* GlobalShortcutListener::GetInstance() { | |
| 48 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 49 return instance.Pointer(); | |
| 50 } | |
| 51 | |
| 52 GlobalShortcutListenerGtk::GlobalShortcutListenerGtk() | |
| 53 : is_listening_(false) { | |
| 54 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 55 } | |
| 56 | |
| 57 GlobalShortcutListenerGtk::~GlobalShortcutListenerGtk() { | |
| 58 if (is_listening_) | |
| 59 StopListening(); | |
| 60 } | |
| 61 | |
| 62 void GlobalShortcutListenerGtk::StartListening() { | |
| 63 DCHECK(!is_listening_); // Don't start twice. | |
| 64 if (!is_listening_) { | |
|
Finnur
2013/09/27 10:35:02
In general, we prefer:
if (foo)
return;
bar();
zhchbin
2013/09/27 13:47:20
Done.
| |
| 65 gdk_window_add_filter(gdk_get_default_root_window(), | |
| 66 &GlobalShortcutListenerGtk::OnXEventThunk, | |
| 67 this); | |
| 68 is_listening_ = true; | |
| 69 } | |
| 70 } | |
| 71 | |
| 72 void GlobalShortcutListenerGtk::StopListening() { | |
| 73 DCHECK(is_listening_); // No point if we are not already listening. | |
| 74 | |
| 75 if (is_listening_) { | |
| 76 gdk_window_remove_filter(NULL, | |
| 77 &GlobalShortcutListenerGtk::OnXEventThunk, | |
| 78 this); | |
| 79 is_listening_ = false; | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 void GlobalShortcutListenerGtk::RegisterAccelerator( | |
| 84 const ui::Accelerator& accelerator, | |
| 85 GlobalShortcutListener::Observer* observer) { | |
| 86 Display* display = GDK_WINDOW_XDISPLAY(gdk_get_default_root_window()); | |
| 87 int modifiers = GetNativeModifiers(accelerator); | |
| 88 KeyCode keycode = XKeysymToKeycode(display, accelerator.key_code()); | |
| 89 base::X11ErrorTracker err_tracker; | |
| 90 | |
| 91 // Because XGrabKey only works on the exact modifiers mask, we should register | |
|
Finnur
2013/09/27 10:35:02
Yikes. :) That's unfortunate. :)
What about mod3 a
zhchbin
2013/09/27 13:47:20
Yeah, this problem has obsessed me since I working
| |
| 92 // our hot keys with modifiers that we want to ignore, including NUM_LOCK, | |
| 93 // CAPS_LOCK, SCROLL_LOCK. | |
| 94 for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { | |
| 95 XGrabKey(display, keycode, modifiers | kModifiersMasks[i], | |
| 96 gdk_x11_get_default_root_xwindow(), False, | |
| 97 GrabModeAsync, GrabModeAsync); | |
| 98 } | |
| 99 | |
| 100 if (err_tracker.FoundNewError()) { | |
|
Finnur
2013/09/27 10:35:02
So, what are the possible error scenarios here?
I
zhchbin
2013/09/27 13:47:20
Yes, we will get BadAccess if there is a conflict.
| |
| 101 LOG(ERROR) << "X failed to grab global hotkey: " | |
| 102 << accelerator.GetShortcutText(); | |
|
Finnur
2013/09/27 10:35:02
Add the error number?
zhchbin
2013/09/27 13:47:20
It seems that we couldn't get the error number if
Finnur
2013/09/27 14:01:27
I thought we had the error, but I guess not. That'
| |
| 103 | |
| 104 // We may have part of the hotkeys registered, clean up. | |
| 105 for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { | |
| 106 XUngrabKey(display, keycode, modifiers | kModifiersMasks[i], | |
| 107 gdk_x11_get_default_root_xwindow()); | |
| 108 } | |
| 109 } else { | |
| 110 registered_hot_keys_.insert(accelerator); | |
| 111 GlobalShortcutListener::RegisterAccelerator(accelerator, observer); | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 void GlobalShortcutListenerGtk::UnregisterAccelerator( | |
| 116 const ui::Accelerator& accelerator, | |
| 117 GlobalShortcutListener::Observer* observer) { | |
| 118 if (registered_hot_keys_.find(accelerator) == registered_hot_keys_.end()) | |
| 119 return; | |
| 120 | |
| 121 Display* display = GDK_WINDOW_XDISPLAY(gdk_get_default_root_window()); | |
| 122 int modifiers = GetNativeModifiers(accelerator); | |
| 123 KeyCode keycode = XKeysymToKeycode(display, accelerator.key_code()); | |
| 124 | |
| 125 for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { | |
| 126 XUngrabKey(display, keycode, modifiers | kModifiersMasks[i], | |
| 127 gdk_x11_get_default_root_xwindow()); | |
| 128 } | |
| 129 registered_hot_keys_.erase(accelerator); | |
| 130 GlobalShortcutListener::UnregisterAccelerator(accelerator, observer); | |
| 131 } | |
| 132 | |
| 133 GdkFilterReturn GlobalShortcutListenerGtk::OnXEvent(GdkXEvent* gdk_x_event, | |
| 134 GdkEvent* gdk_event) { | |
| 135 XEvent* x_event = static_cast<XEvent*>(gdk_x_event); | |
| 136 if (x_event->type == KeyPress) { | |
| 137 int modifiers = 0; | |
| 138 modifiers |= (x_event->xkey.state & ShiftMask) ? ui::EF_SHIFT_DOWN : 0; | |
| 139 modifiers |= (x_event->xkey.state & ControlMask) ? ui::EF_CONTROL_DOWN : 0; | |
| 140 modifiers |= (x_event->xkey.state & Mod1Mask) ? ui::EF_ALT_DOWN : 0; | |
| 141 | |
| 142 ui::Accelerator accelerator( | |
| 143 ui::KeyboardCodeFromXKeyEvent(x_event), modifiers); | |
| 144 instance.Get().NotifyKeyPressed(accelerator); | |
| 145 } | |
| 146 | |
| 147 return GDK_FILTER_CONTINUE; | |
| 148 } | |
| 149 | |
| 150 } // namespace extensions | |
| OLD | NEW |