1 // Copyright 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.
5 #include "chrome/browser/extensions/global_shortcut_listener_x11.h"
7 #include "content/public/browser/browser_thread.h"
8 #include "ui/base/accelerators/accelerator.h"
9 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
10 #include "ui/events/platform/x11/x11_event_source.h"
11 #include "ui/gfx/x/x11_error_tracker.h"
12 #include "ui/gfx/x/x11_types.h"
14 using content::BrowserThread;
18 // The modifiers masks used for grabing keys. Due to XGrabKey only working on
19 // exact modifiers, we need to grab all key combination including zero or more
20 // of the following: Num lock, Caps lock and Scroll lock. So that we can make
21 // sure the behavior of global shortcuts is consistent on all platforms.
22 const unsigned int kModifiersMasks[] = {
23 0, // No additional modifier.
25 LockMask, // Caps lock
26 Mod5Mask, // Scroll lock
30 Mod2Mask | LockMask | Mod5Mask
33 int GetNativeModifiers(const ui::Accelerator& accelerator) {
35 modifiers |= accelerator.IsShiftDown() ? ShiftMask : 0;
36 modifiers |= accelerator.IsCtrlDown() ? ControlMask : 0;
37 modifiers |= accelerator.IsAltDown() ? Mod1Mask : 0;
44 namespace extensions {
47 GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
48 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
49 static GlobalShortcutListenerX11* instance =
50 new GlobalShortcutListenerX11();
54 GlobalShortcutListenerX11::GlobalShortcutListenerX11()
55 : is_listening_(false),
56 x_display_(gfx::GetXDisplay()),
57 x_root_window_(DefaultRootWindow(x_display_)) {
58 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
61 GlobalShortcutListenerX11::~GlobalShortcutListenerX11() {
66 void GlobalShortcutListenerX11::StartListening() {
67 DCHECK(!is_listening_); // Don't start twice.
68 DCHECK(!registered_hot_keys_.empty()); // Also don't start if no hotkey is
71 ui::X11EventSource::GetInstance()->AddPlatformEventDispatcher(this);
76 void GlobalShortcutListenerX11::StopListening() {
77 DCHECK(is_listening_); // No point if we are not already listening.
78 DCHECK(registered_hot_keys_.empty()); // Make sure the set is clean before
81 ui::X11EventSource::GetInstance()->RemovePlatformEventDispatcher(this);
83 is_listening_ = false;
86 bool GlobalShortcutListenerX11::CanDispatchEvent(
87 const ui::PlatformEvent& event) {
88 return event->type == KeyPress;
91 uint32_t GlobalShortcutListenerX11::DispatchEvent(
92 const ui::PlatformEvent& event) {
93 CHECK_EQ(KeyPress, event->type);
94 OnXKeyPressEvent(event);
96 return ui::POST_DISPATCH_NONE;
99 bool GlobalShortcutListenerX11::RegisterAcceleratorImpl(
100 const ui::Accelerator& accelerator) {
101 DCHECK(registered_hot_keys_.find(accelerator) == registered_hot_keys_.end());
103 int modifiers = GetNativeModifiers(accelerator);
104 KeyCode keycode = XKeysymToKeycode(x_display_,
105 XKeysymForWindowsKeyCode(accelerator.key_code(), false));
106 gfx::X11ErrorTracker err_tracker;
108 // Because XGrabKey only works on the exact modifiers mask, we should register
109 // our hot keys with modifiers that we want to ignore, including Num lock,
110 // Caps lock, Scroll lock. See comment about |kModifiersMasks|.
111 for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) {
112 XGrabKey(x_display_, keycode, modifiers | kModifiersMasks[i],
113 x_root_window_, False, GrabModeAsync, GrabModeAsync);
116 if (err_tracker.FoundNewError()) {
117 // We may have part of the hotkeys registered, clean up.
118 for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) {
119 XUngrabKey(x_display_, keycode, modifiers | kModifiersMasks[i],
126 registered_hot_keys_.insert(accelerator);
130 void GlobalShortcutListenerX11::UnregisterAcceleratorImpl(
131 const ui::Accelerator& accelerator) {
132 DCHECK(registered_hot_keys_.find(accelerator) != registered_hot_keys_.end());
134 int modifiers = GetNativeModifiers(accelerator);
135 KeyCode keycode = XKeysymToKeycode(x_display_,
136 XKeysymForWindowsKeyCode(accelerator.key_code(), false));
138 for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) {
139 XUngrabKey(x_display_, keycode, modifiers | kModifiersMasks[i],
142 registered_hot_keys_.erase(accelerator);
145 void GlobalShortcutListenerX11::OnXKeyPressEvent(::XEvent* x_event) {
146 DCHECK(x_event->type == KeyPress);
148 modifiers |= (x_event->xkey.state & ShiftMask) ? ui::EF_SHIFT_DOWN : 0;
149 modifiers |= (x_event->xkey.state & ControlMask) ? ui::EF_CONTROL_DOWN : 0;
150 modifiers |= (x_event->xkey.state & Mod1Mask) ? ui::EF_ALT_DOWN : 0;
152 ui::Accelerator accelerator(
153 ui::KeyboardCodeFromXKeyEvent(x_event), modifiers);
154 if (registered_hot_keys_.find(accelerator) != registered_hot_keys_.end())
155 NotifyKeyPressed(accelerator);
158 } // namespace extensions