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 14using content::BrowserThread; 15 16namespace { 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. 22const 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 33int 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 44namespace extensions { 45 46// static 47GlobalShortcutListener* GlobalShortcutListener::GetInstance() { 48 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 49 static GlobalShortcutListenerX11* instance = 50 new GlobalShortcutListenerX11(); 51 return instance; 52} 53 54GlobalShortcutListenerX11::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 61GlobalShortcutListenerX11::~GlobalShortcutListenerX11() { 62 if (is_listening_) 63 StopListening(); 64} 65 66void 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 76void 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 86bool GlobalShortcutListenerX11::CanDispatchEvent( 87 const ui::PlatformEvent& event) { 88 return event->type == KeyPress; 89} 90 91uint32_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 99bool 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 130void 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 145void 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