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 // The modifiers masks used for grabing keys. Due to XGrabKey only working on |
| 23 // exact modifiers, we need to grab all key combination including zero or more |
| 24 // of the following: Num lock, Caps lock and Scroll lock. So that we can make |
| 25 // sure the behavior of global shortcuts is consistent on all platforms. |
| 26 static const unsigned int kModifiersMasks[] = { |
| 27 0, // No additional modifier. |
| 28 Mod2Mask, // Num lock |
| 29 LockMask, // Caps lock |
| 30 Mod5Mask, // Scroll lock |
| 31 Mod2Mask | LockMask, |
| 32 Mod2Mask | Mod5Mask, |
| 33 LockMask | Mod5Mask, |
| 34 Mod2Mask | LockMask | Mod5Mask |
| 35 }; |
| 36 |
| 37 int GetNativeModifiers(const ui::Accelerator& accelerator) { |
| 38 int modifiers = 0; |
| 39 modifiers |= accelerator.IsShiftDown() ? ShiftMask : 0; |
| 40 modifiers |= accelerator.IsCtrlDown() ? ControlMask : 0; |
| 41 modifiers |= accelerator.IsAltDown() ? Mod1Mask : 0; |
| 42 |
| 43 return modifiers; |
| 44 } |
| 45 |
| 46 } // namespace |
| 47 |
| 48 namespace extensions { |
| 49 |
| 50 // static |
| 51 GlobalShortcutListener* GlobalShortcutListener::GetInstance() { |
| 52 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 53 return instance.Pointer(); |
| 54 } |
| 55 |
| 56 GlobalShortcutListenerGtk::GlobalShortcutListenerGtk() |
| 57 : is_listening_(false) { |
| 58 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 59 } |
| 60 |
| 61 GlobalShortcutListenerGtk::~GlobalShortcutListenerGtk() { |
| 62 if (is_listening_) |
| 63 StopListening(); |
| 64 } |
| 65 |
| 66 void GlobalShortcutListenerGtk::StartListening() { |
| 67 DCHECK(!is_listening_); // Don't start twice. |
| 68 DCHECK(!registered_hot_keys_.empty()); // Also don't start if no hotkey is |
| 69 // registered. |
| 70 |
| 71 gdk_window_add_filter(gdk_get_default_root_window(), |
| 72 &GlobalShortcutListenerGtk::OnXEventThunk, |
| 73 this); |
| 74 is_listening_ = true; |
| 75 } |
| 76 |
| 77 void GlobalShortcutListenerGtk::StopListening() { |
| 78 DCHECK(is_listening_); // No point if we are not already listening. |
| 79 DCHECK(registered_hot_keys_.empty()); // Make sure the set is clean before |
| 80 // ending. |
| 81 |
| 82 gdk_window_remove_filter(NULL, |
| 83 &GlobalShortcutListenerGtk::OnXEventThunk, |
| 84 this); |
| 85 is_listening_ = false; |
| 86 } |
| 87 |
| 88 void GlobalShortcutListenerGtk::RegisterAccelerator( |
| 89 const ui::Accelerator& accelerator, |
| 90 GlobalShortcutListener::Observer* observer) { |
| 91 Display* display = GDK_WINDOW_XDISPLAY(gdk_get_default_root_window()); |
| 92 int modifiers = GetNativeModifiers(accelerator); |
| 93 KeyCode keycode = XKeysymToKeycode(display, accelerator.key_code()); |
| 94 base::X11ErrorTracker err_tracker; |
| 95 |
| 96 // Because XGrabKey only works on the exact modifiers mask, we should register |
| 97 // our hot keys with modifiers that we want to ignore, including Num lock, |
| 98 // Caps lock, Scroll lock. See comment about |kModifiersMasks|. |
| 99 for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { |
| 100 XGrabKey(display, keycode, modifiers | kModifiersMasks[i], |
| 101 gdk_x11_get_default_root_xwindow(), False, |
| 102 GrabModeAsync, GrabModeAsync); |
| 103 } |
| 104 |
| 105 if (err_tracker.FoundNewError()) { |
| 106 LOG(ERROR) << "X failed to grab global hotkey: " |
| 107 << accelerator.GetShortcutText(); |
| 108 |
| 109 // We may have part of the hotkeys registered, clean up. |
| 110 for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { |
| 111 XUngrabKey(display, keycode, modifiers | kModifiersMasks[i], |
| 112 gdk_x11_get_default_root_xwindow()); |
| 113 } |
| 114 } else { |
| 115 registered_hot_keys_.insert(accelerator); |
| 116 GlobalShortcutListener::RegisterAccelerator(accelerator, observer); |
| 117 } |
| 118 } |
| 119 |
| 120 void GlobalShortcutListenerGtk::UnregisterAccelerator( |
| 121 const ui::Accelerator& accelerator, |
| 122 GlobalShortcutListener::Observer* observer) { |
| 123 if (registered_hot_keys_.find(accelerator) == registered_hot_keys_.end()) |
| 124 return; |
| 125 |
| 126 Display* display = GDK_WINDOW_XDISPLAY(gdk_get_default_root_window()); |
| 127 int modifiers = GetNativeModifiers(accelerator); |
| 128 KeyCode keycode = XKeysymToKeycode(display, accelerator.key_code()); |
| 129 |
| 130 for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { |
| 131 XUngrabKey(display, keycode, modifiers | kModifiersMasks[i], |
| 132 gdk_x11_get_default_root_xwindow()); |
| 133 } |
| 134 registered_hot_keys_.erase(accelerator); |
| 135 GlobalShortcutListener::UnregisterAccelerator(accelerator, observer); |
| 136 } |
| 137 |
| 138 GdkFilterReturn GlobalShortcutListenerGtk::OnXEvent(GdkXEvent* gdk_x_event, |
| 139 GdkEvent* gdk_event) { |
| 140 XEvent* x_event = static_cast<XEvent*>(gdk_x_event); |
| 141 if (x_event->type == KeyPress) { |
| 142 int modifiers = 0; |
| 143 modifiers |= (x_event->xkey.state & ShiftMask) ? ui::EF_SHIFT_DOWN : 0; |
| 144 modifiers |= (x_event->xkey.state & ControlMask) ? ui::EF_CONTROL_DOWN : 0; |
| 145 modifiers |= (x_event->xkey.state & Mod1Mask) ? ui::EF_ALT_DOWN : 0; |
| 146 |
| 147 ui::Accelerator accelerator( |
| 148 ui::KeyboardCodeFromXKeyEvent(x_event), modifiers); |
| 149 instance.Get().NotifyKeyPressed(accelerator); |
| 150 } |
| 151 |
| 152 return GDK_FILTER_CONTINUE; |
| 153 } |
| 154 |
| 155 } // namespace extensions |
OLD | NEW |