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 works on | |
Finnur
2013/09/27 14:01:27
s/works/working/
| |
23 // exact modifiers, we need to grab all key combination including zero or more | |
Finnur
2013/09/27 14:01:27
s/more/more of the/
| |
24 // following lock keys: Num lock, Caps lock and Scroll lock. So that we can make | |
Finnur
2013/09/27 14:01:27
Remove 'lock keys'.
| |
25 // sure the behavior of global command keep consistency with other platforms. | |
Finnur
2013/09/27 14:01:27
End this with: "behavior of global shortcuts is co
| |
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 |