14e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 24e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 34e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// found in the LICENSE file. 44e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 54e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chrome/browser/extensions/global_shortcut_listener_x11.h" 64e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 74e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "content/public/browser/browser_thread.h" 84e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ui/base/accelerators/accelerator.h" 94e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ui/events/keycodes/keyboard_code_conversion_x.h" 10a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch#include "ui/events/platform/x11/x11_event_source.h" 1123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#include "ui/gfx/x/x11_error_tracker.h" 124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ui/gfx/x/x11_types.h" 134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)using content::BrowserThread; 154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace { 174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// The modifiers masks used for grabing keys. Due to XGrabKey only working on 194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// exact modifiers, we need to grab all key combination including zero or more 204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// of the following: Num lock, Caps lock and Scroll lock. So that we can make 214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// sure the behavior of global shortcuts is consistent on all platforms. 225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const unsigned int kModifiersMasks[] = { 234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 0, // No additional modifier. 244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) Mod2Mask, // Num lock 254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) LockMask, // Caps lock 264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) Mod5Mask, // Scroll lock 274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) Mod2Mask | LockMask, 284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) Mod2Mask | Mod5Mask, 294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) LockMask | Mod5Mask, 304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) Mod2Mask | LockMask | Mod5Mask 314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}; 324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)int GetNativeModifiers(const ui::Accelerator& accelerator) { 344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) int modifiers = 0; 354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) modifiers |= accelerator.IsShiftDown() ? ShiftMask : 0; 364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) modifiers |= accelerator.IsCtrlDown() ? ControlMask : 0; 374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) modifiers |= accelerator.IsAltDown() ? Mod1Mask : 0; 384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return modifiers; 404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} // namespace 434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace extensions { 454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// static 474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)GlobalShortcutListener* GlobalShortcutListener::GetInstance() { 484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) static GlobalShortcutListenerX11* instance = 505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) new GlobalShortcutListenerX11(); 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return instance; 524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)GlobalShortcutListenerX11::GlobalShortcutListenerX11() 554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) : is_listening_(false), 564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) x_display_(gfx::GetXDisplay()), 574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) x_root_window_(DefaultRootWindow(x_display_)) { 584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)GlobalShortcutListenerX11::~GlobalShortcutListenerX11() { 624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (is_listening_) 634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) StopListening(); 644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void GlobalShortcutListenerX11::StartListening() { 674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) DCHECK(!is_listening_); // Don't start twice. 684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) DCHECK(!registered_hot_keys_.empty()); // Also don't start if no hotkey is 694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // registered. 70a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch 71c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch ui::X11EventSource::GetInstance()->AddPlatformEventDispatcher(this); 724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) is_listening_ = true; 744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void GlobalShortcutListenerX11::StopListening() { 774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) DCHECK(is_listening_); // No point if we are not already listening. 784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) DCHECK(registered_hot_keys_.empty()); // Make sure the set is clean before 794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // ending. 804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 81c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch ui::X11EventSource::GetInstance()->RemovePlatformEventDispatcher(this); 824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) is_listening_ = false; 844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 86c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochbool GlobalShortcutListenerX11::CanDispatchEvent( 87c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const ui::PlatformEvent& event) { 88c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch return event->type == KeyPress; 89c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch} 90c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 91c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochuint32_t GlobalShortcutListenerX11::DispatchEvent( 92c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch const ui::PlatformEvent& event) { 93c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch CHECK_EQ(KeyPress, event->type); 94c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch OnXKeyPressEvent(event); 954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 96c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch return ui::POST_DISPATCH_NONE; 974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool GlobalShortcutListenerX11::RegisterAcceleratorImpl( 1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const ui::Accelerator& accelerator) { 1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(registered_hot_keys_.find(accelerator) == registered_hot_keys_.end()); 102f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) int modifiers = GetNativeModifiers(accelerator); 1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) KeyCode keycode = XKeysymToKeycode(x_display_, 1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) XKeysymForWindowsKeyCode(accelerator.key_code(), false)); 10623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) gfx::X11ErrorTracker err_tracker; 1074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // Because XGrabKey only works on the exact modifiers mask, we should register 1094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // our hot keys with modifiers that we want to ignore, including Num lock, 1104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // Caps lock, Scroll lock. See comment about |kModifiersMasks|. 1114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { 1124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) XGrabKey(x_display_, keycode, modifiers | kModifiersMasks[i], 1134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) x_root_window_, False, GrabModeAsync, GrabModeAsync); 1144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 1154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (err_tracker.FoundNewError()) { 1174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // We may have part of the hotkeys registered, clean up. 1184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { 1194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) XUngrabKey(x_display_, keycode, modifiers | kModifiersMasks[i], 1204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) x_root_window_); 1214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return false; 1244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) registered_hot_keys_.insert(accelerator); 1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return true; 1284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 1294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void GlobalShortcutListenerX11::UnregisterAcceleratorImpl( 1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const ui::Accelerator& accelerator) { 1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(registered_hot_keys_.find(accelerator) != registered_hot_keys_.end()); 1334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) int modifiers = GetNativeModifiers(accelerator); 1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) KeyCode keycode = XKeysymToKeycode(x_display_, 1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) XKeysymForWindowsKeyCode(accelerator.key_code(), false)); 1374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { 1394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) XUngrabKey(x_display_, keycode, modifiers | kModifiersMasks[i], 1404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) x_root_window_); 1414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 1424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) registered_hot_keys_.erase(accelerator); 1434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 1444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void GlobalShortcutListenerX11::OnXKeyPressEvent(::XEvent* x_event) { 1464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) DCHECK(x_event->type == KeyPress); 1474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) int modifiers = 0; 1484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) modifiers |= (x_event->xkey.state & ShiftMask) ? ui::EF_SHIFT_DOWN : 0; 1494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) modifiers |= (x_event->xkey.state & ControlMask) ? ui::EF_CONTROL_DOWN : 0; 1504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) modifiers |= (x_event->xkey.state & Mod1Mask) ? ui::EF_ALT_DOWN : 0; 1514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) ui::Accelerator accelerator( 1534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) ui::KeyboardCodeFromXKeyEvent(x_event), modifiers); 1544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (registered_hot_keys_.find(accelerator) != registered_hot_keys_.end()) 1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NotifyKeyPressed(accelerator); 1564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 1574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} // namespace extensions 159