Home | History | Annotate | Download | only in extensions
      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.
      4 
      5 #include "chrome/browser/extensions/global_shortcut_listener_x11.h"
      6 
      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"
     13 
     14 using content::BrowserThread;
     15 
     16 namespace {
     17 
     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.
     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   static GlobalShortcutListenerX11* instance =
     50       new GlobalShortcutListenerX11();
     51   return instance;
     52 }
     53 
     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));
     59 }
     60 
     61 GlobalShortcutListenerX11::~GlobalShortcutListenerX11() {
     62   if (is_listening_)
     63     StopListening();
     64 }
     65 
     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
     69                                           // registered.
     70 
     71   ui::X11EventSource::GetInstance()->AddPlatformEventDispatcher(this);
     72 
     73   is_listening_ = true;
     74 }
     75 
     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
     79                                          // ending.
     80 
     81   ui::X11EventSource::GetInstance()->RemovePlatformEventDispatcher(this);
     82 
     83   is_listening_ = false;
     84 }
     85 
     86 bool GlobalShortcutListenerX11::CanDispatchEvent(
     87     const ui::PlatformEvent& event) {
     88   return event->type == KeyPress;
     89 }
     90 
     91 uint32_t GlobalShortcutListenerX11::DispatchEvent(
     92     const ui::PlatformEvent& event) {
     93   CHECK_EQ(KeyPress, event->type);
     94   OnXKeyPressEvent(event);
     95 
     96   return ui::POST_DISPATCH_NONE;
     97 }
     98 
     99 bool GlobalShortcutListenerX11::RegisterAcceleratorImpl(
    100     const ui::Accelerator& accelerator) {
    101   DCHECK(registered_hot_keys_.find(accelerator) == registered_hot_keys_.end());
    102 
    103   int modifiers = GetNativeModifiers(accelerator);
    104   KeyCode keycode = XKeysymToKeycode(x_display_,
    105       XKeysymForWindowsKeyCode(accelerator.key_code(), false));
    106   gfx::X11ErrorTracker err_tracker;
    107 
    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);
    114   }
    115 
    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],
    120                  x_root_window_);
    121     }
    122 
    123     return false;
    124   }
    125 
    126   registered_hot_keys_.insert(accelerator);
    127   return true;
    128 }
    129 
    130 void GlobalShortcutListenerX11::UnregisterAcceleratorImpl(
    131     const ui::Accelerator& accelerator) {
    132   DCHECK(registered_hot_keys_.find(accelerator) != registered_hot_keys_.end());
    133 
    134   int modifiers = GetNativeModifiers(accelerator);
    135   KeyCode keycode = XKeysymToKeycode(x_display_,
    136       XKeysymForWindowsKeyCode(accelerator.key_code(), false));
    137 
    138   for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) {
    139     XUngrabKey(x_display_, keycode, modifiers | kModifiersMasks[i],
    140                x_root_window_);
    141   }
    142   registered_hot_keys_.erase(accelerator);
    143 }
    144 
    145 void GlobalShortcutListenerX11::OnXKeyPressEvent(::XEvent* x_event) {
    146   DCHECK(x_event->type == KeyPress);
    147   int modifiers = 0;
    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;
    151 
    152   ui::Accelerator accelerator(
    153       ui::KeyboardCodeFromXKeyEvent(x_event), modifiers);
    154   if (registered_hot_keys_.find(accelerator) != registered_hot_keys_.end())
    155     NotifyKeyPressed(accelerator);
    156 }
    157 
    158 }  // namespace extensions
    159