diff --git a/atom.gyp b/atom.gyp index 3fb2cbd53ab9..c94063ebfe44 100644 --- a/atom.gyp +++ b/atom.gyp @@ -238,6 +238,8 @@ 'chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.h', 'chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.mm', 'chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.h', + 'chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.cc', + 'chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.h', 'chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.cc', 'chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.h', '<@(native_mate_files)', diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.cc b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.cc new file mode 100755 index 000000000000..adfb365cad26 --- /dev/null +++ b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.cc @@ -0,0 +1,160 @@ +// Copyright 2014 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/ui/shortcut/global_shortcut_listener_x11.h" + +#include "base/message_loop/message_pump_x11.h" +#include "content/public/browser/browser_thread.h" +#include "ui/base/accelerators/accelerator.h" +#include "ui/events/keycodes/keyboard_code_conversion_x.h" +#include "ui/gfx/x/x11_error_tracker.h" +#include "ui/gfx/x/x11_types.h" + +using content::BrowserThread; + +namespace { + +// 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. +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 atom { + +namespace api { + +// static +GlobalShortcutListener* GlobalShortcutListener::GetInstance() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + static GlobalShortcutListenerX11* instance = + new GlobalShortcutListenerX11(); + return instance; +} + +GlobalShortcutListenerX11::GlobalShortcutListenerX11() + : is_listening_(false), + x_display_(gfx::GetXDisplay()), + x_root_window_(DefaultRootWindow(x_display_)) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +GlobalShortcutListenerX11::~GlobalShortcutListenerX11() { + if (is_listening_) + StopListening(); +} + +bool GlobalShortcutListenerX11::IsAcceleratorRegistered( + const ui::Accelerator& accelerator) { + return registered_hot_keys_.find(accelerator) != registered_hot_keys_.end(); +} + +void GlobalShortcutListenerX11::StartListening() { + DCHECK(!is_listening_); // Don't start twice. + DCHECK(!registered_hot_keys_.empty()); // Also don't start if no hotkey is + // registered. + base::MessagePumpX11::Current()->AddDispatcherForRootWindow(this); + + is_listening_ = true; +} + +void GlobalShortcutListenerX11::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. + + base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this); + + is_listening_ = false; +} + +uint32_t GlobalShortcutListenerX11::Dispatch(const base::NativeEvent& event) { + if (event->type == KeyPress) + OnXKeyPressEvent(event); + + return POST_DISPATCH_NONE; +} + +bool GlobalShortcutListenerX11::RegisterAcceleratorImpl( + const ui::Accelerator& accelerator) { + DCHECK(registered_hot_keys_.find(accelerator) == registered_hot_keys_.end()); + + int modifiers = GetNativeModifiers(accelerator); + KeyCode keycode = XKeysymToKeycode(x_display_, + XKeysymForWindowsKeyCode(accelerator.key_code(), false)); + gfx::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(x_display_, keycode, modifiers | kModifiersMasks[i], + x_root_window_, False, GrabModeAsync, GrabModeAsync); + } + + if (err_tracker.FoundNewError()) { + // We may have part of the hotkeys registered, clean up. + for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { + XUngrabKey(x_display_, keycode, modifiers | kModifiersMasks[i], + x_root_window_); + } + + return false; + } + + registered_hot_keys_.insert(accelerator); + return true; +} + +void GlobalShortcutListenerX11::UnregisterAcceleratorImpl( + const ui::Accelerator& accelerator) { + DCHECK(registered_hot_keys_.find(accelerator) != registered_hot_keys_.end()); + + int modifiers = GetNativeModifiers(accelerator); + KeyCode keycode = XKeysymToKeycode(x_display_, + XKeysymForWindowsKeyCode(accelerator.key_code(), false)); + + for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { + XUngrabKey(x_display_, keycode, modifiers | kModifiersMasks[i], + x_root_window_); + } + registered_hot_keys_.erase(accelerator); +} + +void GlobalShortcutListenerX11::OnXKeyPressEvent(::XEvent* x_event) { + DCHECK(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); + if (registered_hot_keys_.find(accelerator) != registered_hot_keys_.end()) + NotifyKeyPressed(accelerator); +} + +} // namespace api + +} // namespace atom diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.h b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.h new file mode 100755 index 000000000000..409d248554f0 --- /dev/null +++ b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.h @@ -0,0 +1,64 @@ +// Copyright 2014 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. + +#ifndef CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_X11_H_ +#define CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_X11_H_ + +#include +#include + +#include "base/message_loop/message_pump_dispatcher.h" +#include "chrome/browser/ui/shortcut/global_shortcut_listener.h" + +namespace atom { + +namespace api { + +// X11-specific implementation of the GlobalShortcutListener class that +// listens for global shortcuts. Handles basic keyboard intercepting and +// forwards its output to the base class for processing. +class GlobalShortcutListenerX11 + : public base::MessagePumpDispatcher, + public GlobalShortcutListener { + public: + GlobalShortcutListenerX11(); + virtual ~GlobalShortcutListenerX11(); + + // base::MessagePumpDispatcher implementation. + virtual uint32_t Dispatch(const base::NativeEvent& event) OVERRIDE; + + virtual bool IsAcceleratorRegistered( + const ui::Accelerator& accelerator) OVERRIDE; + + private: + // GlobalShortcutListener implementation. + virtual void StartListening() OVERRIDE; + virtual void StopListening() OVERRIDE; + virtual bool RegisterAcceleratorImpl( + const ui::Accelerator& accelerator) OVERRIDE; + virtual void UnregisterAcceleratorImpl( + const ui::Accelerator& accelerator) OVERRIDE; + + // Invoked when a global shortcut is pressed. + void OnXKeyPressEvent(::XEvent* x_event); + + // Whether this object is listening for global shortcuts. + bool is_listening_; + + // The x11 default display and the native root window. + ::Display* x_display_; + ::Window x_root_window_; + + // A set of registered accelerators. + typedef std::set RegisteredHotKeys; + RegisteredHotKeys registered_hot_keys_; + + DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerX11); +}; + +} // namespace api + +} // namespace atom + +#endif // CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_X11_H_