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 |