| 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..3000219375dd9a83730a6bf8d6b5720e2013334f
|
| --- /dev/null
|
| +++ b/chrome/browser/extensions/global_shortcut_listener_gtk.cc
|
| @@ -0,0 +1,155 @@
|
| +// 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;
|
| +
|
| +// The modifiers masks used for grabing keys. Due to XGrabKey only working on
|
| +// exact modifiers, we need to grab all key combination including zero or more
|
| +// of the following: Num lock, Caps lock and Scroll lock. So that we can make
|
| +// sure the behavior of global shortcuts is consistent on all platforms.
|
| +static const unsigned int kModifiersMasks[] = {
|
| + 0, // No additional modifier.
|
| + 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.
|
| + DCHECK(!registered_hot_keys_.empty()); // Also don't start if no hotkey is
|
| + // registered.
|
| +
|
| + 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.
|
| + DCHECK(registered_hot_keys_.empty()); // Make sure the set is clean before
|
| + // ending.
|
| +
|
| + 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
|
| + // our hot keys with modifiers that we want to ignore, including Num lock,
|
| + // Caps lock, Scroll lock. See comment about |kModifiersMasks|.
|
| + 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()) {
|
| + LOG(ERROR) << "X failed to grab global hotkey: "
|
| + << accelerator.GetShortcutText();
|
| +
|
| + // 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
|
|
|