Chromium Code Reviews| Index: chrome/browser/extensions/global_shortcut_listener_gtk.cc |
| diff --git a/chrome/browser/extensions/global_shortcut_listener_gtk.cc b/chrome/browser/extensions/global_shortcut_listener_gtk.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..fd24e5fabdcbe50bc9b44f1dbe2cdc25f796f363 |
| --- /dev/null |
| +++ b/chrome/browser/extensions/global_shortcut_listener_gtk.cc |
| @@ -0,0 +1,150 @@ |
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/extensions/global_shortcut_listener_gtk.h" |
| + |
| +#include <gdk/gdkx.h> |
| +#include <X11/Xlib.h> |
| + |
| +#include "base/x11/x11_error_tracker.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "ui/base/accelerators/accelerator.h" |
| +#include "ui/events/keycodes/keyboard_code_conversion_x.h" |
| + |
| +using content::BrowserThread; |
| + |
| +namespace { |
| + |
| +static base::LazyInstance<extensions::GlobalShortcutListenerGtk> instance = |
| + LAZY_INSTANCE_INITIALIZER; |
| + |
| +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.
|
| + 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.
|
| + Mod2Mask, // NUM_LOCK |
| + LockMask, // CAPS_LOCK |
| + Mod5Mask, // SCROLL_LOCK |
| + Mod2Mask | LockMask, |
| + Mod2Mask | Mod5Mask, |
| + LockMask | Mod5Mask, |
| + Mod2Mask | LockMask | Mod5Mask |
| +}; |
| + |
| +int GetNativeModifiers(const ui::Accelerator& accelerator) { |
| + int modifiers = 0; |
| + modifiers |= accelerator.IsShiftDown() ? ShiftMask : 0; |
| + modifiers |= accelerator.IsCtrlDown() ? ControlMask : 0; |
| + modifiers |= accelerator.IsAltDown() ? Mod1Mask : 0; |
| + |
| + return modifiers; |
| +} |
| + |
| +} // namespace |
| + |
| +namespace extensions { |
| + |
| +// static |
| +GlobalShortcutListener* GlobalShortcutListener::GetInstance() { |
| + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + return instance.Pointer(); |
| +} |
| + |
| +GlobalShortcutListenerGtk::GlobalShortcutListenerGtk() |
| + : is_listening_(false) { |
| + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| +} |
| + |
| +GlobalShortcutListenerGtk::~GlobalShortcutListenerGtk() { |
| + if (is_listening_) |
| + StopListening(); |
| +} |
| + |
| +void GlobalShortcutListenerGtk::StartListening() { |
| + DCHECK(!is_listening_); // Don't start twice. |
| + 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.
|
| + gdk_window_add_filter(gdk_get_default_root_window(), |
| + &GlobalShortcutListenerGtk::OnXEventThunk, |
| + this); |
| + is_listening_ = true; |
| + } |
| +} |
| + |
| +void GlobalShortcutListenerGtk::StopListening() { |
| + DCHECK(is_listening_); // No point if we are not already listening. |
| + |
| + if (is_listening_) { |
| + gdk_window_remove_filter(NULL, |
| + &GlobalShortcutListenerGtk::OnXEventThunk, |
| + this); |
| + is_listening_ = false; |
| + } |
| +} |
| + |
| +void GlobalShortcutListenerGtk::RegisterAccelerator( |
| + const ui::Accelerator& accelerator, |
| + GlobalShortcutListener::Observer* observer) { |
| + Display* display = GDK_WINDOW_XDISPLAY(gdk_get_default_root_window()); |
| + int modifiers = GetNativeModifiers(accelerator); |
| + KeyCode keycode = XKeysymToKeycode(display, accelerator.key_code()); |
| + base::X11ErrorTracker err_tracker; |
| + |
| + // 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
|
| + // our hot keys with modifiers that we want to ignore, including NUM_LOCK, |
| + // CAPS_LOCK, SCROLL_LOCK. |
| + for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { |
| + XGrabKey(display, keycode, modifiers | kModifiersMasks[i], |
| + gdk_x11_get_default_root_xwindow(), False, |
| + GrabModeAsync, GrabModeAsync); |
| + } |
| + |
| + 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.
|
| + LOG(ERROR) << "X failed to grab global hotkey: " |
| + << 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'
|
| + |
| + // We may have part of the hotkeys registered, clean up. |
| + for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { |
| + XUngrabKey(display, keycode, modifiers | kModifiersMasks[i], |
| + gdk_x11_get_default_root_xwindow()); |
| + } |
| + } else { |
| + registered_hot_keys_.insert(accelerator); |
| + GlobalShortcutListener::RegisterAccelerator(accelerator, observer); |
| + } |
| +} |
| + |
| +void GlobalShortcutListenerGtk::UnregisterAccelerator( |
| + const ui::Accelerator& accelerator, |
| + GlobalShortcutListener::Observer* observer) { |
| + if (registered_hot_keys_.find(accelerator) == registered_hot_keys_.end()) |
| + return; |
| + |
| + Display* display = GDK_WINDOW_XDISPLAY(gdk_get_default_root_window()); |
| + int modifiers = GetNativeModifiers(accelerator); |
| + KeyCode keycode = XKeysymToKeycode(display, accelerator.key_code()); |
| + |
| + for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { |
| + XUngrabKey(display, keycode, modifiers | kModifiersMasks[i], |
| + gdk_x11_get_default_root_xwindow()); |
| + } |
| + registered_hot_keys_.erase(accelerator); |
| + GlobalShortcutListener::UnregisterAccelerator(accelerator, observer); |
| +} |
| + |
| +GdkFilterReturn GlobalShortcutListenerGtk::OnXEvent(GdkXEvent* gdk_x_event, |
| + GdkEvent* gdk_event) { |
| + XEvent* x_event = static_cast<XEvent*>(gdk_x_event); |
| + if (x_event->type == KeyPress) { |
| + int modifiers = 0; |
| + modifiers |= (x_event->xkey.state & ShiftMask) ? ui::EF_SHIFT_DOWN : 0; |
| + modifiers |= (x_event->xkey.state & ControlMask) ? ui::EF_CONTROL_DOWN : 0; |
| + modifiers |= (x_event->xkey.state & Mod1Mask) ? ui::EF_ALT_DOWN : 0; |
| + |
| + ui::Accelerator accelerator( |
| + ui::KeyboardCodeFromXKeyEvent(x_event), modifiers); |
| + instance.Get().NotifyKeyPressed(accelerator); |
| + } |
| + |
| + return GDK_FILTER_CONTINUE; |
| +} |
| + |
| +} // namespace extensions |