From 4b3bd9c3cc8e5566032c62f377c4fdf1dcb584c5 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Sun, 20 Jul 2014 23:09:26 +0800 Subject: [PATCH 1/7] mac: Implement global keyboard shortcut API. --- atom.gyp | 7 + atom/browser/api/atom_api_shortcut.cc | 107 +++++ atom/browser/api/atom_api_shortcut.h | 67 +++ atom/browser/api/lib/shortcut.coffee | 7 + atom/common/node_bindings.cc | 1 + .../ui/shortcut/global_shortcut_listener.cc | 125 ++++++ .../ui/shortcut/global_shortcut_listener.h | 105 +++++ .../shortcut/global_shortcut_listener_mac.h | 111 +++++ .../shortcut/global_shortcut_listener_mac.mm | 392 ++++++++++++++++++ 9 files changed, 922 insertions(+) create mode 100644 atom/browser/api/atom_api_shortcut.cc create mode 100644 atom/browser/api/atom_api_shortcut.h create mode 100644 atom/browser/api/lib/shortcut.coffee create mode 100644 chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.cc create mode 100644 chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.h create mode 100644 chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.h create mode 100644 chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.mm diff --git a/atom.gyp b/atom.gyp index 6bd28b25d90d1..230fdb6469a67 100644 --- a/atom.gyp +++ b/atom.gyp @@ -24,6 +24,7 @@ 'atom/browser/api/lib/menu-item.coffee', 'atom/browser/api/lib/power-monitor.coffee', 'atom/browser/api/lib/protocol.coffee', + 'atom/browser/api/lib/shortcut.coffee', 'atom/browser/api/lib/tray.coffee', 'atom/browser/api/lib/web-contents.coffee', 'atom/browser/lib/init.coffee', @@ -62,6 +63,8 @@ 'atom/browser/api/atom_api_power_monitor.h', 'atom/browser/api/atom_api_protocol.cc', 'atom/browser/api/atom_api_protocol.h', + 'atom/browser/api/atom_api_shortcut.cc', + 'atom/browser/api/atom_api_shortcut.h', 'atom/browser/api/atom_api_tray.cc', 'atom/browser/api/atom_api_tray.h', 'atom/browser/api/atom_api_web_contents.cc', @@ -231,6 +234,10 @@ 'chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h', 'chromium_src/chrome/browser/ui/views/status_icons/status_tray_state_changer_win.cc', 'chromium_src/chrome/browser/ui/views/status_icons/status_tray_state_changer_win.h', + 'chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.cc', + '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', '<@(native_mate_files)', ], 'framework_sources': [ diff --git a/atom/browser/api/atom_api_shortcut.cc b/atom/browser/api/atom_api_shortcut.cc new file mode 100644 index 0000000000000..d752b453ddf69 --- /dev/null +++ b/atom/browser/api/atom_api_shortcut.cc @@ -0,0 +1,107 @@ +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_shortcut.h" + +#include +#include + +#include "atom/browser/ui/accelerator_util.h" +#include "base/values.h" +#include "native_mate/constructor.h" +#include "native_mate/dictionary.h" + +#include "atom/common/node_includes.h" + +namespace atom { + +namespace api { + +Shortcut::Shortcut(const std::string& key) { + is_key_valid_ = accelerator_util::StringToAccelerator(key, &accelerator_); +} + +Shortcut::~Shortcut() { + Unregister(); +} + +// static +mate::Wrappable* Shortcut::Create(const std::string& key) { + return new Shortcut(key); +} + +void Shortcut::OnActive() { + Emit("active"); +} + +void Shortcut::OnFailed(const std::string& error_msg) { + base::ListValue args; + args.AppendString(error_msg); + Emit("failed", args); +} + +void Shortcut::SetKey(const std::string& key) { + // We need to unregister the previous key before set new key eachtime. + Unregister(); + is_key_valid_ = accelerator_util::StringToAccelerator(key, &accelerator_); +} + +void Shortcut::OnKeyPressed(const ui::Accelerator& accelerator) { + if (accelerator != accelerator_) { + // This should never occur, because if it does, GlobalShortcutListener + // notifes us with wrong accelerator. + NOTREACHED(); + return; + } + + OnActive(); +} + +void Shortcut::Register() { + if (!is_key_valid_) { + OnFailed("Shortcut is invalid."); + return; + } + GlobalShortcutListener::GetInstance()->RegisterAccelerator( + accelerator_, this); +} + +void Shortcut::Unregister() { + GlobalShortcutListener::GetInstance()->UnregisterAccelerator(accelerator_, this); +} + +bool Shortcut::IsRegistered() { + return GlobalShortcutListener::GetInstance()->IsAcceleratorRegistered(accelerator_); +} + +// static +void Shortcut::BuildPrototype(v8::Isolate* isolate, + v8::Handle prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .SetMethod("setKey", &Shortcut::SetKey) + .SetMethod("register", &Shortcut::Register) + .SetMethod("unregister", &Shortcut::Unregister) + .SetMethod("isRegistered", &Shortcut::IsRegistered); +} + +} // namespace api + +} // namespace atom + + +namespace { + +void Initialize(v8::Handle exports, v8::Handle unused, + v8::Handle context, void* priv) { + using atom::api::Shortcut; + v8::Isolate* isolate = context->GetIsolate(); + v8::Handle constructor = mate::CreateConstructor( + isolate, "Shortcut", base::Bind(&Shortcut::Create)); + mate::Dictionary dict(isolate, exports); + dict.Set("Shortcut", static_cast>(constructor)); +} + +} // namespace + +NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_shortcut, Initialize) diff --git a/atom/browser/api/atom_api_shortcut.h b/atom/browser/api/atom_api_shortcut.h new file mode 100644 index 0000000000000..b71dad380a9d1 --- /dev/null +++ b/atom/browser/api/atom_api_shortcut.h @@ -0,0 +1,67 @@ +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_API_ATOM_API_SHORTCUT_H_ +#define ATOM_BROWSER_API_ATOM_API_SHORTCUT_H_ + +#include +#include + +#include "atom/browser/api/event_emitter.h" +#include "chrome/browser/ui/shortcut/global_shortcut_listener.h" +#include "ui/base/accelerators/accelerator.h" + +namespace mate { + +class Dictionary; + +} + +namespace atom { + +class Shortcut; + +namespace api { + +class Menu; + +class Shortcut : public mate::EventEmitter, + public GlobalShortcutListener::Observer { + public: + static mate::Wrappable* Create(const std::string& key); + + static void BuildPrototype(v8::Isolate* isolate, + v8::Handle prototype); + + protected: + explicit Shortcut(const std::string& key); + virtual ~Shortcut(); + + const ui::Accelerator& GetAccelerator() const { + return accelerator_; + } + + void OnActive() ; + void OnFailed(const std::string& error_msg) ; + + // GlobalShortcutListener::Observer implementation. + virtual void OnKeyPressed(const ui::Accelerator& accelerator) OVERRIDE; + + void SetKey(const std::string& key); + void Register(); + void Unregister(); + bool IsRegistered(); + + private: + bool is_key_valid_; + ui::Accelerator accelerator_; + + DISALLOW_COPY_AND_ASSIGN(Shortcut); +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_BROWSER_API_ATOM_API_SHORTCUT_H_ diff --git a/atom/browser/api/lib/shortcut.coffee b/atom/browser/api/lib/shortcut.coffee new file mode 100644 index 0000000000000..04cc252ee7f96 --- /dev/null +++ b/atom/browser/api/lib/shortcut.coffee @@ -0,0 +1,7 @@ +EventEmitter = require('events').EventEmitter +bindings = process.atomBinding 'shortcut' + +Shortcut = bindings.Shortcut +Shortcut::__proto__ = EventEmitter.prototype + +module.exports = Shortcut diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index 2d5d8364bc08e..ffc1967b43c29 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -65,6 +65,7 @@ REFERENCE_MODULE(atom_browser_dialog); REFERENCE_MODULE(atom_browser_menu); REFERENCE_MODULE(atom_browser_power_monitor); REFERENCE_MODULE(atom_browser_protocol); +REFERENCE_MODULE(atom_browser_shortcut); REFERENCE_MODULE(atom_browser_tray); REFERENCE_MODULE(atom_browser_window); REFERENCE_MODULE(atom_common_clipboard); diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.cc b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.cc new file mode 100644 index 0000000000000..f16ca705d1cfa --- /dev/null +++ b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.cc @@ -0,0 +1,125 @@ +// Copyright (c) 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 "base/logging.h" +#include "chrome/browser/ui/shortcut/global_shortcut_listener.h" +#include "content/public/browser/browser_thread.h" +#include "ui/base/accelerators/accelerator.h" + +using content::BrowserThread; + +namespace atom { + +namespace api { + +GlobalShortcutListener::GlobalShortcutListener() + : shortcut_handling_suspended_(false) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +GlobalShortcutListener::~GlobalShortcutListener() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +bool GlobalShortcutListener::RegisterAccelerator( + const ui::Accelerator& accelerator, Observer* observer) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (IsShortcutHandlingSuspended()) + return false; + + AcceleratorMap::const_iterator it = accelerator_map_.find(accelerator); + if (it != accelerator_map_.end()) { + // The accelerator has been registered. + return false; + } + + if (!RegisterAcceleratorImpl(accelerator)) { + // If the platform-specific registration fails, mostly likely the shortcut + // has been registered by other native applications. + return false; + } + + if (accelerator_map_.empty()) + StartListening(); + + accelerator_map_[accelerator] = observer; + return true; +} + +void GlobalShortcutListener::UnregisterAccelerator( + const ui::Accelerator& accelerator, Observer* observer) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (IsShortcutHandlingSuspended()) + return; + + AcceleratorMap::iterator it = accelerator_map_.find(accelerator); + if (it == accelerator_map_.end()) + return; + // The caller should call this function with the right observer. + DCHECK(it->second == observer); + + UnregisterAcceleratorImpl(accelerator); + accelerator_map_.erase(it); + if (accelerator_map_.empty()) + StopListening(); +} + +void GlobalShortcutListener::UnregisterAccelerators(Observer* observer) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (IsShortcutHandlingSuspended()) + return; + + AcceleratorMap::iterator it = accelerator_map_.begin(); + while (it != accelerator_map_.end()) { + if (it->second == observer) { + AcceleratorMap::iterator to_remove = it++; + UnregisterAccelerator(to_remove->first, observer); + } else { + ++it; + } + } +} + +void GlobalShortcutListener::SetShortcutHandlingSuspended(bool suspended) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (shortcut_handling_suspended_ == suspended) + return; + + shortcut_handling_suspended_ = suspended; + for (AcceleratorMap::iterator it = accelerator_map_.begin(); + it != accelerator_map_.end(); + ++it) { + // On Linux, when shortcut handling is suspended we cannot simply early + // return in NotifyKeyPressed (similar to what we do for non-global + // shortcuts) because we'd eat the keyboard event thereby preventing the + // user from setting the shortcut. Therefore we must unregister while + // handling is suspended and register when handling resumes. + if (shortcut_handling_suspended_) + UnregisterAcceleratorImpl(it->first); + else + RegisterAcceleratorImpl(it->first); + } +} + +bool GlobalShortcutListener::IsShortcutHandlingSuspended() const { + return shortcut_handling_suspended_; +} + +void GlobalShortcutListener::NotifyKeyPressed( + const ui::Accelerator& accelerator) { + AcceleratorMap::iterator iter = accelerator_map_.find(accelerator); + if (iter == accelerator_map_.end()) { + // This should never occur, because if it does, we have failed to unregister + // or failed to clean up the map after unregistering the shortcut. + NOTREACHED(); + return; // No-one is listening to this key. + } + + iter->second->OnKeyPressed(accelerator); +} + +} // namespace api + +} // namespace atom diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.h b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.h new file mode 100644 index 0000000000000..8985edb9c9457 --- /dev/null +++ b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.h @@ -0,0 +1,105 @@ +// Copyright (c) 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_H_ +#define CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_H_ + +#include + +#include "base/basictypes.h" +#include "ui/events/keycodes/keyboard_codes.h" + +namespace ui { +class Accelerator; +} + +namespace atom { + +namespace api { + +// Platform-neutral implementation of a class that keeps track of observers and +// monitors keystrokes. It relays messages to the appropriate observer when a +// global shortcut has been struck by the user. +class GlobalShortcutListener { + public: + class Observer { + public: + // Called when your global shortcut (|accelerator|) is struck. + virtual void OnKeyPressed(const ui::Accelerator& accelerator) = 0; + }; + + virtual ~GlobalShortcutListener(); + + static GlobalShortcutListener* GetInstance(); + + // Register an observer for when a certain |accelerator| is struck. Returns + // true if register successfully, or false if 1) the specificied |accelerator| + // has been registered by another caller or other native applications, or + // 2) shortcut handling is suspended. + // + // Note that we do not support recognizing that an accelerator has been + // registered by another application on all platforms. This is a per-platform + // consideration. + bool RegisterAccelerator(const ui::Accelerator& accelerator, + Observer* observer); + + // Stop listening for the given |accelerator|, does nothing if shortcut + // handling is suspended. + void UnregisterAccelerator(const ui::Accelerator& accelerator, + Observer* observer); + + // Stop listening for all accelerators of the given |observer|, does nothing + // if shortcut handling is suspended. + void UnregisterAccelerators(Observer* observer); + + // Suspend/Resume global shortcut handling. Note that when suspending, + // RegisterAccelerator/UnregisterAccelerator/UnregisterAccelerators are not + // allowed to be called until shortcut handling has been resumed. + void SetShortcutHandlingSuspended(bool suspended); + + // Returns whether shortcut handling is currently suspended. + bool IsShortcutHandlingSuspended() const; + + // Returen whether accelerator is registered. + virtual bool IsAcceleratorRegistered(const ui::Accelerator& accelerator) = 0; + + protected: + GlobalShortcutListener(); + + // Called by platform specific implementations of this class whenever a key + // is struck. Only called for keys that have an observer registered. + void NotifyKeyPressed(const ui::Accelerator& accelerator); + + private: + // The following methods are implemented by platform-specific implementations + // of this class. + // + // Start/StopListening are called when transitioning between zero and nonzero + // registered accelerators. StartListening will be called after + // RegisterAcceleratorImpl and StopListening will be called after + // UnregisterAcceleratorImpl. + // + // For RegisterAcceleratorImpl, implementations return false if registration + // did not complete successfully. + virtual void StartListening() = 0; + virtual void StopListening() = 0; + virtual bool RegisterAcceleratorImpl(const ui::Accelerator& accelerator) = 0; + virtual void UnregisterAcceleratorImpl( + const ui::Accelerator& accelerator) = 0; + + // The map of accelerators that have been successfully registered as global + // shortcuts and their observer. + typedef std::map AcceleratorMap; + AcceleratorMap accelerator_map_; + + // Keeps track of whether shortcut handling is currently suspended. + bool shortcut_handling_suspended_; + + DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListener); +}; + +} // namespace api + +} // namespace atom +#endif // CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_H_ diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.h b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.h new file mode 100644 index 0000000000000..27cbadcb163c5 --- /dev/null +++ b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.h @@ -0,0 +1,111 @@ +// Copyright (c) 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_MAC_H_ +#define CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_MAC_H_ + +#include "chrome/browser/ui/shortcut/global_shortcut_listener.h" + +#include +#include + +#include + +#include "base/mac/scoped_nsobject.h" + +namespace atom { + +namespace api { + +// Mac-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. +// +// This class does two things: +// 1. Intercepts media keys. Uses an event tap for intercepting media keys +// (PlayPause, NextTrack, PreviousTrack). +// 2. Binds keyboard shortcuts (hot keys). Carbon RegisterEventHotKey API for +// binding to non-media key global hot keys (eg. Command-Shift-1). +class GlobalShortcutListenerMac : public GlobalShortcutListener { + public: + GlobalShortcutListenerMac(); + virtual ~GlobalShortcutListenerMac(); + + virtual bool IsAcceleratorRegistered(const ui::Accelerator& accelerator) OVERRIDE; + private: + typedef int KeyId; + typedef std::map AcceleratorIdMap; + typedef std::map IdAcceleratorMap; + typedef std::map IdHotKeyRefMap; + + // Keyboard event callbacks. + void OnHotKeyEvent(EventHotKeyID hot_key_id); + bool OnMediaKeyEvent(int key_code); + + // 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; + + // Mac-specific functions for registering hot keys with modifiers. + bool RegisterHotKey(const ui::Accelerator& accelerator, KeyId hot_key_id); + void UnregisterHotKey(const ui::Accelerator& accelerator); + + // Enable and disable the media key event tap. + void StartWatchingMediaKeys(); + void StopWatchingMediaKeys(); + + // Enable and disable the hot key event handler. + void StartWatchingHotKeys(); + void StopWatchingHotKeys(); + + // Whether or not any media keys are currently registered. + bool IsAnyMediaKeyRegistered(); + + // Whether or not any hot keys are currently registered. + bool IsAnyHotKeyRegistered(); + + // The callback for when an event tap happens. + static CGEventRef EventTapCallback( + CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon); + + // The callback for when a hot key event happens. + static OSStatus HotKeyHandler( + EventHandlerCallRef next_handler, EventRef event, void* user_data); + + // Whether this object is listening for global shortcuts. + bool is_listening_; + + // The hotkey identifier for the next global shortcut that is added. + KeyId hot_key_id_; + + // A map of all hotkeys (media keys and shortcuts) mapping to their + // corresponding hotkey IDs. For quickly finding if an accelerator is + // registered. + AcceleratorIdMap accelerator_ids_; + + // The inverse map for quickly looking up accelerators by hotkey id. + IdAcceleratorMap id_accelerators_; + + // Keyboard shortcut IDs to hotkeys map for unregistration. + IdHotKeyRefMap id_hot_key_refs_; + + // Event tap for intercepting mac media keys. + CFMachPortRef event_tap_; + CFRunLoopSourceRef event_tap_source_; + + // Event handler for keyboard shortcut hot keys. + EventHandlerRef event_handler_; + + DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerMac); +}; + +} // namespace api + +} // namespace atom + +#endif // CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_MAC_H_ diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.mm b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.mm new file mode 100644 index 0000000000000..47b390e60f3ba --- /dev/null +++ b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.mm @@ -0,0 +1,392 @@ +// Copyright (c) 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_mac.h" + +#include +#import +#include + +#import "base/mac/foundation_util.h" +#include "content/public/browser/browser_thread.h" +#include "ui/base/accelerators/accelerator.h" +#include "ui/events/event.h" +#import "ui/events/keycodes/keyboard_code_conversion_mac.h" + +using content::BrowserThread; +using atom::api::GlobalShortcutListenerMac; + +namespace { + +// The media keys subtype. No official docs found, but widely known. +// http://lists.apple.com/archives/cocoa-dev/2007/Aug/msg00499.html +const int kSystemDefinedEventMediaKeysSubtype = 8; + +ui::KeyboardCode MediaKeyCodeToKeyboardCode(int key_code) { + switch (key_code) { + case NX_KEYTYPE_PLAY: + return ui::VKEY_MEDIA_PLAY_PAUSE; + case NX_KEYTYPE_PREVIOUS: + case NX_KEYTYPE_REWIND: + return ui::VKEY_MEDIA_PREV_TRACK; + case NX_KEYTYPE_NEXT: + case NX_KEYTYPE_FAST: + return ui::VKEY_MEDIA_NEXT_TRACK; + } + return ui::VKEY_UNKNOWN; +} + +bool IsMediaKey(const ui::Accelerator& accelerator) { + if (accelerator.modifiers() != 0) + return false; + return (accelerator.key_code() == ui::VKEY_MEDIA_NEXT_TRACK || + accelerator.key_code() == ui::VKEY_MEDIA_PREV_TRACK || + accelerator.key_code() == ui::VKEY_MEDIA_PLAY_PAUSE || + accelerator.key_code() == ui::VKEY_MEDIA_STOP); +} + +} // namespace + +namespace atom { + +namespace api { + +// static +GlobalShortcutListener* GlobalShortcutListener::GetInstance() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + static GlobalShortcutListenerMac* instance = + new GlobalShortcutListenerMac(); + return instance; +} + +GlobalShortcutListenerMac::GlobalShortcutListenerMac() + : is_listening_(false), + hot_key_id_(0), + event_tap_(NULL), + event_tap_source_(NULL), + event_handler_(NULL) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +GlobalShortcutListenerMac::~GlobalShortcutListenerMac() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // By this point, UnregisterAccelerator should have been called for all + // keyboard shortcuts. Still we should clean up. + if (is_listening_) + StopListening(); + + // If keys are still registered, make sure we stop the tap. Again, this + // should never happen. + if (IsAnyMediaKeyRegistered()) + StopWatchingMediaKeys(); + + if (IsAnyHotKeyRegistered()) + StopWatchingHotKeys(); +} + +bool GlobalShortcutListenerMac::IsAcceleratorRegistered( + const ui::Accelerator& accelerator) { + return accelerator_ids_.find(accelerator) != accelerator_ids_.end(); +} + +void GlobalShortcutListenerMac::StartListening() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + DCHECK(!accelerator_ids_.empty()); + DCHECK(!id_accelerators_.empty()); + DCHECK(!is_listening_); + + is_listening_ = true; +} + +void GlobalShortcutListenerMac::StopListening() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + DCHECK(accelerator_ids_.empty()); // Make sure the set is clean. + DCHECK(id_accelerators_.empty()); + DCHECK(is_listening_); + + is_listening_ = false; +} + +void GlobalShortcutListenerMac::OnHotKeyEvent(EventHotKeyID hot_key_id) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // This hot key should be registered. + DCHECK(id_accelerators_.find(hot_key_id.id) != id_accelerators_.end()); + // Look up the accelerator based on this hot key ID. + const ui::Accelerator& accelerator = id_accelerators_[hot_key_id.id]; + NotifyKeyPressed(accelerator); +} + +bool GlobalShortcutListenerMac::OnMediaKeyEvent(int media_key_code) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + ui::KeyboardCode key_code = MediaKeyCodeToKeyboardCode(media_key_code); + // Create an accelerator corresponding to the keyCode. + ui::Accelerator accelerator(key_code, 0); + // Look for a match with a bound hot_key. + if (accelerator_ids_.find(accelerator) != accelerator_ids_.end()) { + // If matched, callback to the event handling system. + NotifyKeyPressed(accelerator); + return true; + } + return false; +} + +bool GlobalShortcutListenerMac::RegisterAcceleratorImpl( + const ui::Accelerator& accelerator) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(accelerator_ids_.find(accelerator) == accelerator_ids_.end()); + + if (IsMediaKey(accelerator)) { + if (!IsAnyMediaKeyRegistered()) { + // If this is the first media key registered, start the event tap. + StartWatchingMediaKeys(); + } + } else { + // Register hot_key if they are non-media keyboard shortcuts. + if (!RegisterHotKey(accelerator, hot_key_id_)) + return false; + + if (!IsAnyHotKeyRegistered()) { + StartWatchingHotKeys(); + } + } + + // Store the hotkey-ID mappings we will need for lookup later. + id_accelerators_[hot_key_id_] = accelerator; + accelerator_ids_[accelerator] = hot_key_id_; + ++hot_key_id_; + return true; +} + +void GlobalShortcutListenerMac::UnregisterAcceleratorImpl( + const ui::Accelerator& accelerator) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(accelerator_ids_.find(accelerator) != accelerator_ids_.end()); + + // Unregister the hot_key if it's a keyboard shortcut. + if (!IsMediaKey(accelerator)) + UnregisterHotKey(accelerator); + + // Remove hot_key from the mappings. + KeyId key_id = accelerator_ids_[accelerator]; + id_accelerators_.erase(key_id); + accelerator_ids_.erase(accelerator); + + if (IsMediaKey(accelerator)) { + // If we unregistered a media key, and now no media keys are registered, + // stop the media key tap. + if (!IsAnyMediaKeyRegistered()) + StopWatchingMediaKeys(); + } else { + // If we unregistered a hot key, and no more hot keys are registered, remove + // the hot key handler. + if (!IsAnyHotKeyRegistered()) { + StopWatchingHotKeys(); + } + } +} + +bool GlobalShortcutListenerMac::RegisterHotKey( + const ui::Accelerator& accelerator, KeyId hot_key_id) { + EventHotKeyID event_hot_key_id; + + // Signature uniquely identifies the application that owns this hot_key. + event_hot_key_id.signature = base::mac::CreatorCodeForApplication(); + event_hot_key_id.id = hot_key_id; + + // Translate ui::Accelerator modifiers to cmdKey, altKey, etc. + int modifiers = 0; + modifiers |= (accelerator.IsShiftDown() ? shiftKey : 0); + modifiers |= (accelerator.IsCtrlDown() ? controlKey : 0); + modifiers |= (accelerator.IsAltDown() ? optionKey : 0); + modifiers |= (accelerator.IsCmdDown() ? cmdKey : 0); + + int key_code = ui::MacKeyCodeForWindowsKeyCode(accelerator.key_code(), 0, + NULL, NULL); + + // Register the event hot key. + EventHotKeyRef hot_key_ref; + OSStatus status = RegisterEventHotKey(key_code, modifiers, event_hot_key_id, + GetApplicationEventTarget(), 0, &hot_key_ref); + if (status != noErr) + return false; + + id_hot_key_refs_[hot_key_id] = hot_key_ref; + return true; +} + +void GlobalShortcutListenerMac::UnregisterHotKey( + const ui::Accelerator& accelerator) { + // Ensure this accelerator is already registered. + DCHECK(accelerator_ids_.find(accelerator) != accelerator_ids_.end()); + // Get the ref corresponding to this accelerator. + KeyId key_id = accelerator_ids_[accelerator]; + EventHotKeyRef ref = id_hot_key_refs_[key_id]; + // Unregister the event hot key. + UnregisterEventHotKey(ref); + + // Remove the event from the mapping. + id_hot_key_refs_.erase(key_id); +} + +void GlobalShortcutListenerMac::StartWatchingMediaKeys() { + // Make sure there's no existing event tap. + DCHECK(event_tap_ == NULL); + DCHECK(event_tap_source_ == NULL); + + // Add an event tap to intercept the system defined media key events. + event_tap_ = CGEventTapCreate(kCGSessionEventTap, + kCGHeadInsertEventTap, + kCGEventTapOptionDefault, + CGEventMaskBit(NX_SYSDEFINED), + EventTapCallback, + this); + if (event_tap_ == NULL) { + LOG(ERROR) << "Error: failed to create event tap."; + return; + } + + event_tap_source_ = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, + event_tap_, 0); + if (event_tap_source_ == NULL) { + LOG(ERROR) << "Error: failed to create new run loop source."; + return; + } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), event_tap_source_, + kCFRunLoopCommonModes); +} + +void GlobalShortcutListenerMac::StopWatchingMediaKeys() { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), event_tap_source_, + kCFRunLoopCommonModes); + // Ensure both event tap and source are initialized. + DCHECK(event_tap_ != NULL); + DCHECK(event_tap_source_ != NULL); + + // Invalidate the event tap. + CFMachPortInvalidate(event_tap_); + CFRelease(event_tap_); + event_tap_ = NULL; + + // Release the event tap source. + CFRelease(event_tap_source_); + event_tap_source_ = NULL; +} + +void GlobalShortcutListenerMac::StartWatchingHotKeys() { + DCHECK(!event_handler_); + EventHandlerUPP hot_key_function = NewEventHandlerUPP(HotKeyHandler); + EventTypeSpec event_type; + event_type.eventClass = kEventClassKeyboard; + event_type.eventKind = kEventHotKeyPressed; + InstallApplicationEventHandler( + hot_key_function, 1, &event_type, this, &event_handler_); +} + +void GlobalShortcutListenerMac::StopWatchingHotKeys() { + DCHECK(event_handler_); + RemoveEventHandler(event_handler_); + event_handler_ = NULL; +} + +bool GlobalShortcutListenerMac::IsAnyMediaKeyRegistered() { + // Iterate through registered accelerators, looking for media keys. + AcceleratorIdMap::iterator it; + for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) { + if (IsMediaKey(it->first)) + return true; + } + return false; +} + +bool GlobalShortcutListenerMac::IsAnyHotKeyRegistered() { + AcceleratorIdMap::iterator it; + for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) { + if (!IsMediaKey(it->first)) + return true; + } + return false; +} + +// Processed events should propagate if they aren't handled by any listeners. +// For events that don't matter, this handler should return as quickly as +// possible. +// Returning event causes the event to propagate to other applications. +// Returning NULL prevents the event from propagating. +// static +CGEventRef GlobalShortcutListenerMac::EventTapCallback( + CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon) { + GlobalShortcutListenerMac* shortcut_listener = + static_cast(refcon); + + // Handle the timeout case by re-enabling the tap. + if (type == kCGEventTapDisabledByTimeout) { + CGEventTapEnable(shortcut_listener->event_tap_, TRUE); + return event; + } + + // Convert the CGEvent to an NSEvent for access to the data1 field. + NSEvent* ns_event = [NSEvent eventWithCGEvent:event]; + if (ns_event == nil) { + return event; + } + + // Ignore events that are not system defined media keys. + if (type != NX_SYSDEFINED || + [ns_event type] != NSSystemDefined || + [ns_event subtype] != kSystemDefinedEventMediaKeysSubtype) { + return event; + } + + NSInteger data1 = [ns_event data1]; + // Ignore media keys that aren't previous, next and play/pause. + // Magical constants are from http://weblog.rogueamoeba.com/2007/09/29/ + int key_code = (data1 & 0xFFFF0000) >> 16; + if (key_code != NX_KEYTYPE_PLAY && key_code != NX_KEYTYPE_NEXT && + key_code != NX_KEYTYPE_PREVIOUS && key_code != NX_KEYTYPE_FAST && + key_code != NX_KEYTYPE_REWIND) { + return event; + } + + int key_flags = data1 & 0x0000FFFF; + bool is_key_pressed = ((key_flags & 0xFF00) >> 8) == 0xA; + + // If the key wasn't pressed (eg. was released), ignore this event. + if (!is_key_pressed) + return event; + + // Now we have a media key that we care about. Send it to the caller. + bool was_handled = shortcut_listener->OnMediaKeyEvent(key_code); + + // Prevent event from proagating to other apps if handled by Chrome. + if (was_handled) + return NULL; + + // By default, pass the event through. + return event; +} + +// static +OSStatus GlobalShortcutListenerMac::HotKeyHandler( + EventHandlerCallRef next_handler, EventRef event, void* user_data) { + // Extract the hotkey from the event. + EventHotKeyID hot_key_id; + OSStatus result = GetEventParameter(event, kEventParamDirectObject, + typeEventHotKeyID, NULL, sizeof(hot_key_id), NULL, &hot_key_id); + if (result != noErr) + return result; + + GlobalShortcutListenerMac* shortcut_listener = + static_cast(user_data); + shortcut_listener->OnHotKeyEvent(hot_key_id); + return noErr; +} + +} // namespace api + +} // namespace atom From c2c5111d75a9f6a4d37c28e25355b0805b116185 Mon Sep 17 00:00:00 2001 From: Yeechan Lu Date: Sun, 27 Jul 2014 09:28:14 +0800 Subject: [PATCH 2/7] win: Implement global keyboard shortcut API. --- atom.gyp | 2 + .../shortcut/global_shortcut_listener_win.cc | 117 ++++++++++++++++++ .../shortcut/global_shortcut_listener_win.h | 58 +++++++++ 3 files changed, 177 insertions(+) create mode 100644 chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.cc create mode 100644 chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.h diff --git a/atom.gyp b/atom.gyp index 230fdb6469a67..3fb2cbd53ab9d 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_win.cc', + 'chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.h', '<@(native_mate_files)', ], 'framework_sources': [ diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.cc b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.cc new file mode 100644 index 0000000000000..b31fb362a83bf --- /dev/null +++ b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.cc @@ -0,0 +1,117 @@ +// Copyright (c) 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_win.h" + +#include "base/win/win_util.h" +#include "content/public/browser/browser_thread.h" +#include "ui/base/accelerators/accelerator.h" +#include "ui/events/event_constants.h" +#include "ui/events/keycodes/keyboard_code_conversion_win.h" + +using content::BrowserThread; + +namespace atom { + +namespace api { + +// static +GlobalShortcutListener* GlobalShortcutListener::GetInstance() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + static GlobalShortcutListenerWin* instance = + new GlobalShortcutListenerWin(); + return instance; +} + +GlobalShortcutListenerWin::GlobalShortcutListenerWin() + : is_listening_(false) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +GlobalShortcutListenerWin::~GlobalShortcutListenerWin() { + if (is_listening_) + StopListening(); +} + +bool GlobalShortcutListenerWin::IsAcceleratorRegistered( + const ui::Accelerator& accelerator) { + return hotkey_ids_.find(accelerator) != hotkey_ids_.end(); +} + +void GlobalShortcutListenerWin::StartListening() { + DCHECK(!is_listening_); // Don't start twice. + DCHECK(!hotkey_ids_.empty()); // Also don't start if no hotkey is registered. + gfx::SingletonHwnd::GetInstance()->AddObserver(this); + is_listening_ = true; +} + +void GlobalShortcutListenerWin::StopListening() { + DCHECK(is_listening_); // No point if we are not already listening. + DCHECK(hotkey_ids_.empty()); // Make sure the map is clean before ending. + gfx::SingletonHwnd::GetInstance()->RemoveObserver(this); + is_listening_ = false; +} + +void GlobalShortcutListenerWin::OnWndProc(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + if (message != WM_HOTKEY) + return; + + int key_code = HIWORD(lparam); + int modifiers = 0; + modifiers |= (LOWORD(lparam) & MOD_SHIFT) ? ui::EF_SHIFT_DOWN : 0; + modifiers |= (LOWORD(lparam) & MOD_ALT) ? ui::EF_ALT_DOWN : 0; + modifiers |= (LOWORD(lparam) & MOD_CONTROL) ? ui::EF_CONTROL_DOWN : 0; + ui::Accelerator accelerator( + ui::KeyboardCodeForWindowsKeyCode(key_code), modifiers); + + NotifyKeyPressed(accelerator); +} + +bool GlobalShortcutListenerWin::RegisterAcceleratorImpl( + const ui::Accelerator& accelerator) { + DCHECK(hotkey_ids_.find(accelerator) == hotkey_ids_.end()); + + int modifiers = 0; + modifiers |= accelerator.IsShiftDown() ? MOD_SHIFT : 0; + modifiers |= accelerator.IsCtrlDown() ? MOD_CONTROL : 0; + modifiers |= accelerator.IsAltDown() ? MOD_ALT : 0; + static int hotkey_id = 0; + //bool success = false; + bool success = !!RegisterHotKey( + gfx::SingletonHwnd::GetInstance()->hwnd(), + hotkey_id, + modifiers, + accelerator.key_code()); + + if (!success) { + // Most likely error: 1409 (Hotkey already registered). + return false; + } + + hotkey_ids_[accelerator] = hotkey_id++; + return true; +} + +void GlobalShortcutListenerWin::UnregisterAcceleratorImpl( + const ui::Accelerator& accelerator) { + HotkeyIdMap::iterator it = hotkey_ids_.find(accelerator); + DCHECK(it != hotkey_ids_.end()); + + //bool success = false; + gfx::SingletonHwnd::GetInstance(); + bool success = !!UnregisterHotKey( + gfx::SingletonHwnd::GetInstance()->hwnd(), it->second); + // This call should always succeed, as long as we pass in the right HWND and + // an id we've used to register before. + DCHECK(success); + + hotkey_ids_.erase(it); +} + +} // namespace api + +} // namespace atom diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.h b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.h new file mode 100644 index 0000000000000..c85ee108376d9 --- /dev/null +++ b/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.h @@ -0,0 +1,58 @@ +// Copyright (c) 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_WIN_H_ +#define CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_WIN_H_ + +#include + +#include "chrome/browser/ui/shortcut/global_shortcut_listener.h" +#include "ui/gfx/win/singleton_hwnd.h" + +namespace atom { + +namespace api { + +// Windows-specific implementation of the GlobalShortcutListener class that +// listens for global shortcuts. Handles setting up a keyboard hook and +// forwarding its output to the base class for processing. +class GlobalShortcutListenerWin : public GlobalShortcutListener, + public gfx::SingletonHwnd::Observer { + public: + GlobalShortcutListenerWin(); + virtual ~GlobalShortcutListenerWin(); + + virtual bool IsAcceleratorRegistered( + const ui::Accelerator& accelerator) OVERRIDE; + + private: + // The implementation of our Window Proc, called by SingletonHwnd. + virtual void OnWndProc(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) OVERRIDE; + + // 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; + + // Whether this object is listening for global shortcuts. + bool is_listening_; + + // A map of registered accelerators and their registration ids. + typedef std::map HotkeyIdMap; + HotkeyIdMap hotkey_ids_; + + DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerWin); +}; + +} // namespace api + +} // namespace atom + +#endif // CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_WIN_H_ From a8034364ffbf200318927c7ae56de506cd8f4fcd Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Mon, 28 Jul 2014 23:00:44 +0800 Subject: [PATCH 3/7] linux: Implement global keyboard shortcut API. --- atom.gyp | 2 + .../shortcut/global_shortcut_listener_x11.cc | 160 ++++++++++++++++++ .../shortcut/global_shortcut_listener_x11.h | 64 +++++++ 3 files changed, 226 insertions(+) create mode 100755 chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.cc create mode 100755 chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.h diff --git a/atom.gyp b/atom.gyp index 3fb2cbd53ab9d..c94063ebfe447 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 0000000000000..adfb365cad26e --- /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 0000000000000..409d248554f00 --- /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_ From ad827eee908cd36d22c5783e058fd139cb71f81f Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Wed, 30 Jul 2014 14:56:29 +0800 Subject: [PATCH 4/7] Emit failed message when fail to register shortcut. --- atom/browser/api/atom_api_shortcut.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/atom/browser/api/atom_api_shortcut.cc b/atom/browser/api/atom_api_shortcut.cc index d752b453ddf69..0613215e48b19 100644 --- a/atom/browser/api/atom_api_shortcut.cc +++ b/atom/browser/api/atom_api_shortcut.cc @@ -63,8 +63,9 @@ void Shortcut::Register() { OnFailed("Shortcut is invalid."); return; } - GlobalShortcutListener::GetInstance()->RegisterAccelerator( - accelerator_, this); + if (!GlobalShortcutListener::GetInstance()->RegisterAccelerator( + accelerator_, this)) + OnFailed("Fail to register the shortcut."); } void Shortcut::Unregister() { From 9342d59a7c9b4afa126acd0756d0755b1a92f071 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Wed, 30 Jul 2014 14:58:22 +0800 Subject: [PATCH 5/7] :memo: Shortcut API document. --- docs/README.md | 1 + docs/api/shortcut.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 docs/api/shortcut.md diff --git a/docs/README.md b/docs/README.md index 527a7a5bde9a5..79b8ca410efa4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,6 +21,7 @@ Modules for browser side: * [power-monitor](api/power-monitor.md) * [protocol](api/protocol.md) * [tray](api/tray.md) +* [shortcut](api/shortcut.md) Modules for web page: diff --git a/docs/api/shortcut.md b/docs/api/shortcut.md new file mode 100644 index 0000000000000..5f85079037a4e --- /dev/null +++ b/docs/api/shortcut.md @@ -0,0 +1,67 @@ +# shortcut + +A `Shortcut` presents a global keyboard shortcut in operating system. If a +`Shortcut` is registered in app, the app will receive an `active` event when +user presses the shortcut. Note that it is global, even your app does not get +focused, it still works. + + +```javascript +var Shortcut = require('shortcut'); + +shortcut = new Shortcut('ctrl+a'); +shortcut.setKey('ctrl+s'); +shortcut.on('active', function() { console.log('ctrl+s pressed'); }); +shortcut.on('failed', function() { console.log("failed"); }); +shortcut.register(); +``` + +## Class: Shortcut + +`Shortcut` is an [EventEmitter](event-emitter). + +### new Shortcut(keycode) + +* `keycode` String + +Creates a new `Shortcut` associated with the `keycode`. + +`keycode` is a string to specify shortcut key, such as "ctrl+shift+a". + +A `keycode` consists of modifier and key two parts: + +__Modifiers__: control(ctrl), command(cmd), alt, shift, commandorcontrol(cmdorctrl). + +__Supported keys__: 0-9, a-z, up, down, left, right, home, end, pagedown, pageup, +insert, delete, esc, space, backspace, tab, f1-f12, volumeup, volumedown, media +keys(medianextrack, mediaprevioustrack, mediastop, mediaplaypause). + +### Event: active + +Emitted when a registered `shortcut` is pressed by user. + +### Event: failed + +Emitted when the keycode of `shortcut` is invalid. + +### Shortcut.setKey(keycode) + +* `keycode` String + +Set new `keycode` to a `Shortcut`. Note that this operation will override previous +keycode and will unregister the `Shortcut`, developer should register the +`Shortcut` again after `setKey`. + +### Shortcut.register + +Register a `Shortcut` to operating system. + +### Shortcut.unregister + +Unregister a `Shortcut` to operating system. + +### Shortcut.isRegistered + +Return whether the shortcut is registered. + +[event-emitter]: http://nodejs.org/api/events.html#events_class_events_eventemitter From b2217474c1ed4490e3916f5acc4049958d6a61eb Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Wed, 30 Jul 2014 21:18:26 +0800 Subject: [PATCH 6/7] Nits: Fix code style. --- atom/browser/api/atom_api_shortcut.cc | 6 ++++-- atom/browser/api/atom_api_shortcut.h | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/atom/browser/api/atom_api_shortcut.cc b/atom/browser/api/atom_api_shortcut.cc index 0613215e48b19..4fc8847b1671d 100644 --- a/atom/browser/api/atom_api_shortcut.cc +++ b/atom/browser/api/atom_api_shortcut.cc @@ -69,11 +69,13 @@ void Shortcut::Register() { } void Shortcut::Unregister() { - GlobalShortcutListener::GetInstance()->UnregisterAccelerator(accelerator_, this); + GlobalShortcutListener::GetInstance()->UnregisterAccelerator( + accelerator_, this); } bool Shortcut::IsRegistered() { - return GlobalShortcutListener::GetInstance()->IsAcceleratorRegistered(accelerator_); + return GlobalShortcutListener::GetInstance()->IsAcceleratorRegistered( + accelerator_); } // static diff --git a/atom/browser/api/atom_api_shortcut.h b/atom/browser/api/atom_api_shortcut.h index b71dad380a9d1..a8dc9b51b6612 100644 --- a/atom/browser/api/atom_api_shortcut.h +++ b/atom/browser/api/atom_api_shortcut.h @@ -42,8 +42,8 @@ class Shortcut : public mate::EventEmitter, return accelerator_; } - void OnActive() ; - void OnFailed(const std::string& error_msg) ; + void OnActive(); + void OnFailed(const std::string& error_msg); // GlobalShortcutListener::Observer implementation. virtual void OnKeyPressed(const ui::Accelerator& accelerator) OVERRIDE; From 035679057e56073ea5f5cff962314887759214bc Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Thu, 31 Jul 2014 12:22:29 +0800 Subject: [PATCH 7/7] Update GlobalShortcut API design. * Rename Shortcut API to GlobalShortcut for better suite. * Implement the new design interfaces. * Put the chromium related source code to the same directory as in chrome. --- atom.gyp | 22 ++-- atom/browser/api/atom_api_global_shortcut.cc | 123 ++++++++++++++++++ atom/browser/api/atom_api_global_shortcut.h | 54 ++++++++ atom/browser/api/atom_api_shortcut.cc | 110 ---------------- atom/browser/api/atom_api_shortcut.h | 67 ---------- atom/browser/api/lib/global-shortcut.coffee | 5 + atom/browser/api/lib/shortcut.coffee | 7 - atom/common/node_bindings.cc | 2 +- .../global_shortcut_listener.cc | 17 +-- .../global_shortcut_listener.h | 18 +-- .../global_shortcut_listener_mac.h | 19 +-- .../global_shortcut_listener_mac.mm | 19 +-- .../global_shortcut_listener_win.cc | 20 +-- .../global_shortcut_listener_win.h | 21 +-- .../global_shortcut_listener_x11.cc | 49 +++++-- .../global_shortcut_listener_x11.h | 37 ++++-- 16 files changed, 288 insertions(+), 302 deletions(-) create mode 100644 atom/browser/api/atom_api_global_shortcut.cc create mode 100644 atom/browser/api/atom_api_global_shortcut.h delete mode 100644 atom/browser/api/atom_api_shortcut.cc delete mode 100644 atom/browser/api/atom_api_shortcut.h create mode 100644 atom/browser/api/lib/global-shortcut.coffee delete mode 100644 atom/browser/api/lib/shortcut.coffee rename chromium_src/chrome/browser/{ui/shortcut => extensions}/global_shortcut_listener.cc (90%) rename chromium_src/chrome/browser/{ui/shortcut => extensions}/global_shortcut_listener.h (88%) rename chromium_src/chrome/browser/{ui/shortcut => extensions}/global_shortcut_listener_mac.h (87%) rename chromium_src/chrome/browser/{ui/shortcut => extensions}/global_shortcut_listener_mac.mm (96%) rename chromium_src/chrome/browser/{ui/shortcut => extensions}/global_shortcut_listener_win.cc (87%) rename chromium_src/chrome/browser/{ui/shortcut => extensions}/global_shortcut_listener_win.h (74%) rename chromium_src/chrome/browser/{ui/shortcut => extensions}/global_shortcut_listener_x11.cc (84%) mode change 100755 => 100644 rename chromium_src/chrome/browser/{ui/shortcut => extensions}/global_shortcut_listener_x11.h (64%) mode change 100755 => 100644 diff --git a/atom.gyp b/atom.gyp index c94063ebfe447..97a2d08c30fb8 100644 --- a/atom.gyp +++ b/atom.gyp @@ -19,12 +19,12 @@ 'atom/browser/api/lib/auto-updater.coffee', 'atom/browser/api/lib/browser-window.coffee', 'atom/browser/api/lib/dialog.coffee', + 'atom/browser/api/lib/global-shortcut.coffee', 'atom/browser/api/lib/ipc.coffee', 'atom/browser/api/lib/menu.coffee', 'atom/browser/api/lib/menu-item.coffee', 'atom/browser/api/lib/power-monitor.coffee', 'atom/browser/api/lib/protocol.coffee', - 'atom/browser/api/lib/shortcut.coffee', 'atom/browser/api/lib/tray.coffee', 'atom/browser/api/lib/web-contents.coffee', 'atom/browser/lib/init.coffee', @@ -53,6 +53,8 @@ 'atom/browser/api/atom_api_auto_updater.cc', 'atom/browser/api/atom_api_auto_updater.h', 'atom/browser/api/atom_api_dialog.cc', + 'atom/browser/api/atom_api_global_shortcut.cc', + 'atom/browser/api/atom_api_global_shortcut.h', 'atom/browser/api/atom_api_menu.cc', 'atom/browser/api/atom_api_menu.h', 'atom/browser/api/atom_api_menu_views.cc', @@ -63,8 +65,6 @@ 'atom/browser/api/atom_api_power_monitor.h', 'atom/browser/api/atom_api_protocol.cc', 'atom/browser/api/atom_api_protocol.h', - 'atom/browser/api/atom_api_shortcut.cc', - 'atom/browser/api/atom_api_shortcut.h', 'atom/browser/api/atom_api_tray.cc', 'atom/browser/api/atom_api_tray.h', 'atom/browser/api/atom_api_web_contents.cc', @@ -226,6 +226,14 @@ 'atom/renderer/atom_render_view_observer.h', 'atom/renderer/atom_renderer_client.cc', 'atom/renderer/atom_renderer_client.h', + 'chromium_src/chrome/browser/extensions/global_shortcut_listener.cc', + 'chromium_src/chrome/browser/extensions/global_shortcut_listener.h', + 'chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.mm', + 'chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.h', + 'chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.cc', + 'chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.h', + 'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.cc', + 'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h', 'chromium_src/chrome/browser/ui/libgtk2ui/app_indicator_icon_menu.cc', 'chromium_src/chrome/browser/ui/libgtk2ui/app_indicator_icon_menu.h', 'chromium_src/chrome/browser/ui/libgtk2ui/gtk2_status_icon.cc', @@ -234,14 +242,6 @@ 'chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h', 'chromium_src/chrome/browser/ui/views/status_icons/status_tray_state_changer_win.cc', 'chromium_src/chrome/browser/ui/views/status_icons/status_tray_state_changer_win.h', - 'chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.cc', - '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)', ], 'framework_sources': [ diff --git a/atom/browser/api/atom_api_global_shortcut.cc b/atom/browser/api/atom_api_global_shortcut.cc new file mode 100644 index 0000000000000..bb4de6cf16686 --- /dev/null +++ b/atom/browser/api/atom_api_global_shortcut.cc @@ -0,0 +1,123 @@ +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_global_shortcut.h" + +#include + +#include "atom/browser/ui/accelerator_util.h" +#include "atom/common/native_mate_converters/function_converter.h" +#include "native_mate/dictionary.h" + +#include "atom/common/node_includes.h" + +using extensions::GlobalShortcutListener; + +namespace atom { + +namespace api { + +GlobalShortcut::GlobalShortcut() { +} + +GlobalShortcut::~GlobalShortcut() { + UnregisterAll(); +} + +void GlobalShortcut::OnKeyPressed(const ui::Accelerator& accelerator) { + if (accelerator_callback_map_.find(accelerator) == + accelerator_callback_map_.end()) { + // This should never occur, because if it does, GlobalGlobalShortcutListener + // notifes us with wrong accelerator. + NOTREACHED(); + return; + } + accelerator_callback_map_[accelerator].Run(); +} + +bool GlobalShortcut::Register(const std::string& keycode, + const base::Closure& callback) { + ui::Accelerator accelerator; + if (!accelerator_util::StringToAccelerator(keycode, &accelerator)) { + LOG(ERROR) << keycode << " is invalid."; + return false; + } + if (!GlobalShortcutListener::GetInstance()->RegisterAccelerator( + accelerator, this)) { + return false; + } + accelerator_callback_map_[accelerator] = callback; + return true; +} + +void GlobalShortcut::Unregister(const std::string& keycode) { + ui::Accelerator accelerator; + if (!accelerator_util::StringToAccelerator(keycode, &accelerator)) { + LOG(ERROR) << "The keycode: " << keycode << " is invalid."; + return; + } + if (accelerator_callback_map_.find(accelerator) == + accelerator_callback_map_.end()) { + LOG(ERROR) << "The keycode: " << keycode << " isn't registered yet!"; + return; + } + accelerator_callback_map_.erase(accelerator); + GlobalShortcutListener::GetInstance()->UnregisterAccelerator( + accelerator, this); +} + +void GlobalShortcut::UnregisterAll() { + accelerator_callback_map_.clear(); + GlobalShortcutListener::GetInstance()->UnregisterAccelerators(this); +} + +bool GlobalShortcut::IsRegistered(const std::string& keycode) { + ui::Accelerator accelerator; + if (!accelerator_util::StringToAccelerator(keycode, &accelerator)) { + LOG(ERROR) << "The keycode: " << keycode << " is invalid."; + return false; + } + return accelerator_callback_map_.find(accelerator) != + accelerator_callback_map_.end(); +} + +// static +mate::ObjectTemplateBuilder GlobalShortcut::GetObjectTemplateBuilder( + v8::Isolate* isolate) { + return mate::ObjectTemplateBuilder(isolate) + .SetMethod("register", + base::Bind(&GlobalShortcut::Register, + base::Unretained(this))) + .SetMethod("isRegistered", + base::Bind(&GlobalShortcut::IsRegistered, + base::Unretained(this))) + .SetMethod("unregister", + base::Bind(&GlobalShortcut::Unregister, + base::Unretained(this))) + .SetMethod("unregisterAll", + base::Bind(&GlobalShortcut::UnregisterAll, + base::Unretained(this))); +} + +// static +mate::Handle GlobalShortcut::Create(v8::Isolate* isolate) { + return CreateHandle(isolate, new GlobalShortcut); +} + +} // namespace api + +} // namespace atom + +namespace { + +void Initialize(v8::Handle exports, v8::Handle unused, + v8::Handle context, void* priv) { + v8::Isolate* isolate = context->GetIsolate(); + mate::Dictionary dict(isolate, exports); + dict.Set("globalShortcut", atom::api::GlobalShortcut::Create(isolate)); +} + +} // namespace + +NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_global_shortcut, Initialize) diff --git a/atom/browser/api/atom_api_global_shortcut.h b/atom/browser/api/atom_api_global_shortcut.h new file mode 100644 index 0000000000000..cb731c665d3d6 --- /dev/null +++ b/atom/browser/api/atom_api_global_shortcut.h @@ -0,0 +1,54 @@ +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_API_ATOM_API_GLOBAL_SHORTCUT_H_ +#define ATOM_BROWSER_API_ATOM_API_GLOBAL_SHORTCUT_H_ + +#include +#include + +#include "base/callback.h" +#include "chrome/browser/extensions/global_shortcut_listener.h" +#include "native_mate/wrappable.h" +#include "native_mate/handle.h" +#include "ui/base/accelerators/accelerator.h" + +namespace atom { + +namespace api { + +class GlobalShortcut : public extensions::GlobalShortcutListener::Observer, + public mate::Wrappable { + public: + static mate::Handle Create(v8::Isolate* isolate); + + protected: + GlobalShortcut(); + virtual ~GlobalShortcut(); + + // mate::Wrappable implementations: + virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) OVERRIDE; + + private: + typedef std::map AcceleratorCallbackMap; + + bool Register(const std::string& keycode, const base::Closure& callback); + bool IsRegistered(const std::string& keycode); + void Unregister(const std::string& keycode); + void UnregisterAll(); + + // GlobalShortcutListener::Observer implementation. + virtual void OnKeyPressed(const ui::Accelerator& accelerator) OVERRIDE; + + AcceleratorCallbackMap accelerator_callback_map_; + + DISALLOW_COPY_AND_ASSIGN(GlobalShortcut); +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_BROWSER_API_ATOM_API_GLOBAL_SHORTCUT_H_ diff --git a/atom/browser/api/atom_api_shortcut.cc b/atom/browser/api/atom_api_shortcut.cc deleted file mode 100644 index 4fc8847b1671d..0000000000000 --- a/atom/browser/api/atom_api_shortcut.cc +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. All rights reserved. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/api/atom_api_shortcut.h" - -#include -#include - -#include "atom/browser/ui/accelerator_util.h" -#include "base/values.h" -#include "native_mate/constructor.h" -#include "native_mate/dictionary.h" - -#include "atom/common/node_includes.h" - -namespace atom { - -namespace api { - -Shortcut::Shortcut(const std::string& key) { - is_key_valid_ = accelerator_util::StringToAccelerator(key, &accelerator_); -} - -Shortcut::~Shortcut() { - Unregister(); -} - -// static -mate::Wrappable* Shortcut::Create(const std::string& key) { - return new Shortcut(key); -} - -void Shortcut::OnActive() { - Emit("active"); -} - -void Shortcut::OnFailed(const std::string& error_msg) { - base::ListValue args; - args.AppendString(error_msg); - Emit("failed", args); -} - -void Shortcut::SetKey(const std::string& key) { - // We need to unregister the previous key before set new key eachtime. - Unregister(); - is_key_valid_ = accelerator_util::StringToAccelerator(key, &accelerator_); -} - -void Shortcut::OnKeyPressed(const ui::Accelerator& accelerator) { - if (accelerator != accelerator_) { - // This should never occur, because if it does, GlobalShortcutListener - // notifes us with wrong accelerator. - NOTREACHED(); - return; - } - - OnActive(); -} - -void Shortcut::Register() { - if (!is_key_valid_) { - OnFailed("Shortcut is invalid."); - return; - } - if (!GlobalShortcutListener::GetInstance()->RegisterAccelerator( - accelerator_, this)) - OnFailed("Fail to register the shortcut."); -} - -void Shortcut::Unregister() { - GlobalShortcutListener::GetInstance()->UnregisterAccelerator( - accelerator_, this); -} - -bool Shortcut::IsRegistered() { - return GlobalShortcutListener::GetInstance()->IsAcceleratorRegistered( - accelerator_); -} - -// static -void Shortcut::BuildPrototype(v8::Isolate* isolate, - v8::Handle prototype) { - mate::ObjectTemplateBuilder(isolate, prototype) - .SetMethod("setKey", &Shortcut::SetKey) - .SetMethod("register", &Shortcut::Register) - .SetMethod("unregister", &Shortcut::Unregister) - .SetMethod("isRegistered", &Shortcut::IsRegistered); -} - -} // namespace api - -} // namespace atom - - -namespace { - -void Initialize(v8::Handle exports, v8::Handle unused, - v8::Handle context, void* priv) { - using atom::api::Shortcut; - v8::Isolate* isolate = context->GetIsolate(); - v8::Handle constructor = mate::CreateConstructor( - isolate, "Shortcut", base::Bind(&Shortcut::Create)); - mate::Dictionary dict(isolate, exports); - dict.Set("Shortcut", static_cast>(constructor)); -} - -} // namespace - -NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_shortcut, Initialize) diff --git a/atom/browser/api/atom_api_shortcut.h b/atom/browser/api/atom_api_shortcut.h deleted file mode 100644 index a8dc9b51b6612..0000000000000 --- a/atom/browser/api/atom_api_shortcut.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. All rights reserved. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_BROWSER_API_ATOM_API_SHORTCUT_H_ -#define ATOM_BROWSER_API_ATOM_API_SHORTCUT_H_ - -#include -#include - -#include "atom/browser/api/event_emitter.h" -#include "chrome/browser/ui/shortcut/global_shortcut_listener.h" -#include "ui/base/accelerators/accelerator.h" - -namespace mate { - -class Dictionary; - -} - -namespace atom { - -class Shortcut; - -namespace api { - -class Menu; - -class Shortcut : public mate::EventEmitter, - public GlobalShortcutListener::Observer { - public: - static mate::Wrappable* Create(const std::string& key); - - static void BuildPrototype(v8::Isolate* isolate, - v8::Handle prototype); - - protected: - explicit Shortcut(const std::string& key); - virtual ~Shortcut(); - - const ui::Accelerator& GetAccelerator() const { - return accelerator_; - } - - void OnActive(); - void OnFailed(const std::string& error_msg); - - // GlobalShortcutListener::Observer implementation. - virtual void OnKeyPressed(const ui::Accelerator& accelerator) OVERRIDE; - - void SetKey(const std::string& key); - void Register(); - void Unregister(); - bool IsRegistered(); - - private: - bool is_key_valid_; - ui::Accelerator accelerator_; - - DISALLOW_COPY_AND_ASSIGN(Shortcut); -}; - -} // namespace api - -} // namespace atom - -#endif // ATOM_BROWSER_API_ATOM_API_SHORTCUT_H_ diff --git a/atom/browser/api/lib/global-shortcut.coffee b/atom/browser/api/lib/global-shortcut.coffee new file mode 100644 index 0000000000000..8b24d27253664 --- /dev/null +++ b/atom/browser/api/lib/global-shortcut.coffee @@ -0,0 +1,5 @@ +bindings = process.atomBinding 'global_shortcut' + +globalShortcut = bindings.globalShortcut + +module.exports = globalShortcut diff --git a/atom/browser/api/lib/shortcut.coffee b/atom/browser/api/lib/shortcut.coffee deleted file mode 100644 index 04cc252ee7f96..0000000000000 --- a/atom/browser/api/lib/shortcut.coffee +++ /dev/null @@ -1,7 +0,0 @@ -EventEmitter = require('events').EventEmitter -bindings = process.atomBinding 'shortcut' - -Shortcut = bindings.Shortcut -Shortcut::__proto__ = EventEmitter.prototype - -module.exports = Shortcut diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index ffc1967b43c29..f421e303a91b4 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -65,7 +65,7 @@ REFERENCE_MODULE(atom_browser_dialog); REFERENCE_MODULE(atom_browser_menu); REFERENCE_MODULE(atom_browser_power_monitor); REFERENCE_MODULE(atom_browser_protocol); -REFERENCE_MODULE(atom_browser_shortcut); +REFERENCE_MODULE(atom_browser_global_shortcut); REFERENCE_MODULE(atom_browser_tray); REFERENCE_MODULE(atom_browser_window); REFERENCE_MODULE(atom_common_clipboard); diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.cc b/chromium_src/chrome/browser/extensions/global_shortcut_listener.cc similarity index 90% rename from chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.cc rename to chromium_src/chrome/browser/extensions/global_shortcut_listener.cc index f16ca705d1cfa..925cb40d0f968 100644 --- a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.cc +++ b/chromium_src/chrome/browser/extensions/global_shortcut_listener.cc @@ -1,18 +1,16 @@ -// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// 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.h" #include "base/logging.h" -#include "chrome/browser/ui/shortcut/global_shortcut_listener.h" #include "content/public/browser/browser_thread.h" #include "ui/base/accelerators/accelerator.h" using content::BrowserThread; -namespace atom { - -namespace api { +namespace extensions { GlobalShortcutListener::GlobalShortcutListener() : shortcut_handling_suspended_(false) { @@ -21,6 +19,7 @@ GlobalShortcutListener::GlobalShortcutListener() GlobalShortcutListener::~GlobalShortcutListener() { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(accelerator_map_.empty()); // Make sure we've cleaned up. } bool GlobalShortcutListener::RegisterAccelerator( @@ -55,8 +54,8 @@ void GlobalShortcutListener::UnregisterAccelerator( return; AcceleratorMap::iterator it = accelerator_map_.find(accelerator); - if (it == accelerator_map_.end()) - return; + // We should never get asked to unregister something that we didn't register. + DCHECK(it != accelerator_map_.end()); // The caller should call this function with the right observer. DCHECK(it->second == observer); @@ -120,6 +119,4 @@ void GlobalShortcutListener::NotifyKeyPressed( iter->second->OnKeyPressed(accelerator); } -} // namespace api - -} // namespace atom +} // namespace extensions diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.h b/chromium_src/chrome/browser/extensions/global_shortcut_listener.h similarity index 88% rename from chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.h rename to chromium_src/chrome/browser/extensions/global_shortcut_listener.h index 8985edb9c9457..1f07df2b6e102 100644 --- a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener.h +++ b/chromium_src/chrome/browser/extensions/global_shortcut_listener.h @@ -1,9 +1,9 @@ -// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// 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. -#ifndef CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_H_ -#define CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_H_ +#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_ +#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_ #include @@ -14,9 +14,7 @@ namespace ui { class Accelerator; } -namespace atom { - -namespace api { +namespace extensions { // Platform-neutral implementation of a class that keeps track of observers and // monitors keystrokes. It relays messages to the appropriate observer when a @@ -61,9 +59,6 @@ class GlobalShortcutListener { // Returns whether shortcut handling is currently suspended. bool IsShortcutHandlingSuspended() const; - // Returen whether accelerator is registered. - virtual bool IsAcceleratorRegistered(const ui::Accelerator& accelerator) = 0; - protected: GlobalShortcutListener(); @@ -99,7 +94,6 @@ class GlobalShortcutListener { DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListener); }; -} // namespace api +} // namespace extensions -} // namespace atom -#endif // CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_H_ +#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_ diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.h b/chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.h similarity index 87% rename from chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.h rename to chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.h index 27cbadcb163c5..8ee91a10da6e4 100644 --- a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.h +++ b/chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.h @@ -1,11 +1,11 @@ -// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// 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. -#ifndef CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_MAC_H_ -#define CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_MAC_H_ +#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_ +#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_ -#include "chrome/browser/ui/shortcut/global_shortcut_listener.h" +#include "chrome/browser/extensions/global_shortcut_listener.h" #include #include @@ -14,9 +14,7 @@ #include "base/mac/scoped_nsobject.h" -namespace atom { - -namespace api { +namespace extensions { // Mac-specific implementation of the GlobalShortcutListener class that // listens for global shortcuts. Handles basic keyboard intercepting and @@ -32,7 +30,6 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener { GlobalShortcutListenerMac(); virtual ~GlobalShortcutListenerMac(); - virtual bool IsAcceleratorRegistered(const ui::Accelerator& accelerator) OVERRIDE; private: typedef int KeyId; typedef std::map AcceleratorIdMap; @@ -104,8 +101,6 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener { DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerMac); }; -} // namespace api - -} // namespace atom +} // namespace extensions -#endif // CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_MAC_H_ +#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_ diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.mm b/chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.mm similarity index 96% rename from chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.mm rename to chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.mm index 47b390e60f3ba..2fce520b76700 100644 --- a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_mac.mm +++ b/chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.mm @@ -1,8 +1,8 @@ -// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// 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/ui/shortcut/global_shortcut_listener_mac.h" +#include "chrome/browser/extensions/global_shortcut_listener_mac.h" #include #import @@ -15,7 +15,7 @@ #import "ui/events/keycodes/keyboard_code_conversion_mac.h" using content::BrowserThread; -using atom::api::GlobalShortcutListenerMac; +using extensions::GlobalShortcutListenerMac; namespace { @@ -48,9 +48,7 @@ bool IsMediaKey(const ui::Accelerator& accelerator) { } // namespace -namespace atom { - -namespace api { +namespace extensions { // static GlobalShortcutListener* GlobalShortcutListener::GetInstance() { @@ -86,11 +84,6 @@ bool IsMediaKey(const ui::Accelerator& accelerator) { StopWatchingHotKeys(); } -bool GlobalShortcutListenerMac::IsAcceleratorRegistered( - const ui::Accelerator& accelerator) { - return accelerator_ids_.find(accelerator) != accelerator_ids_.end(); -} - void GlobalShortcutListenerMac::StartListening() { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -387,6 +380,4 @@ bool IsMediaKey(const ui::Accelerator& accelerator) { return noErr; } -} // namespace api - -} // namespace atom +} // namespace extensions diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.cc b/chromium_src/chrome/browser/extensions/global_shortcut_listener_win.cc similarity index 87% rename from chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.cc rename to chromium_src/chrome/browser/extensions/global_shortcut_listener_win.cc index b31fb362a83bf..8f6f1912e36fc 100644 --- a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.cc +++ b/chromium_src/chrome/browser/extensions/global_shortcut_listener_win.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// 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/ui/shortcut/global_shortcut_listener_win.h" +#include "chrome/browser/extensions/global_shortcut_listener_win.h" #include "base/win/win_util.h" #include "content/public/browser/browser_thread.h" @@ -12,9 +12,7 @@ using content::BrowserThread; -namespace atom { - -namespace api { +namespace extensions { // static GlobalShortcutListener* GlobalShortcutListener::GetInstance() { @@ -34,11 +32,6 @@ GlobalShortcutListenerWin::~GlobalShortcutListenerWin() { StopListening(); } -bool GlobalShortcutListenerWin::IsAcceleratorRegistered( - const ui::Accelerator& accelerator) { - return hotkey_ids_.find(accelerator) != hotkey_ids_.end(); -} - void GlobalShortcutListenerWin::StartListening() { DCHECK(!is_listening_); // Don't start twice. DCHECK(!hotkey_ids_.empty()); // Also don't start if no hotkey is registered. @@ -80,7 +73,6 @@ bool GlobalShortcutListenerWin::RegisterAcceleratorImpl( modifiers |= accelerator.IsCtrlDown() ? MOD_CONTROL : 0; modifiers |= accelerator.IsAltDown() ? MOD_ALT : 0; static int hotkey_id = 0; - //bool success = false; bool success = !!RegisterHotKey( gfx::SingletonHwnd::GetInstance()->hwnd(), hotkey_id, @@ -101,8 +93,6 @@ void GlobalShortcutListenerWin::UnregisterAcceleratorImpl( HotkeyIdMap::iterator it = hotkey_ids_.find(accelerator); DCHECK(it != hotkey_ids_.end()); - //bool success = false; - gfx::SingletonHwnd::GetInstance(); bool success = !!UnregisterHotKey( gfx::SingletonHwnd::GetInstance()->hwnd(), it->second); // This call should always succeed, as long as we pass in the right HWND and @@ -112,6 +102,4 @@ void GlobalShortcutListenerWin::UnregisterAcceleratorImpl( hotkey_ids_.erase(it); } -} // namespace api - -} // namespace atom +} // namespace extensions diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.h b/chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h similarity index 74% rename from chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.h rename to chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h index c85ee108376d9..a19207340e951 100644 --- a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_win.h +++ b/chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h @@ -1,18 +1,16 @@ -// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// 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. -#ifndef CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_WIN_H_ -#define CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_WIN_H_ +#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_ +#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_ #include -#include "chrome/browser/ui/shortcut/global_shortcut_listener.h" +#include "chrome/browser/extensions/global_shortcut_listener.h" #include "ui/gfx/win/singleton_hwnd.h" -namespace atom { - -namespace api { +namespace extensions { // Windows-specific implementation of the GlobalShortcutListener class that // listens for global shortcuts. Handles setting up a keyboard hook and @@ -23,9 +21,6 @@ class GlobalShortcutListenerWin : public GlobalShortcutListener, GlobalShortcutListenerWin(); virtual ~GlobalShortcutListenerWin(); - virtual bool IsAcceleratorRegistered( - const ui::Accelerator& accelerator) OVERRIDE; - private: // The implementation of our Window Proc, called by SingletonHwnd. virtual void OnWndProc(HWND hwnd, @@ -51,8 +46,6 @@ class GlobalShortcutListenerWin : public GlobalShortcutListener, DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerWin); }; -} // namespace api - -} // namespace atom +} // namespace extensions -#endif // CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_WIN_H_ +#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_ diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.cc b/chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.cc old mode 100755 new mode 100644 similarity index 84% rename from chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.cc rename to chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.cc index adfb365cad26e..556bd707a54aa --- a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.cc +++ b/chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.cc @@ -1,16 +1,21 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 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/ui/shortcut/global_shortcut_listener_x11.h" +#include "chrome/browser/extensions/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" +#if defined(TOOLKIT_GTK) +#include +#else +#include "base/message_loop/message_pump_x11.h" +#endif + using content::BrowserThread; namespace { @@ -41,9 +46,7 @@ int GetNativeModifiers(const ui::Accelerator& accelerator) { } // namespace -namespace atom { - -namespace api { +namespace extensions { // static GlobalShortcutListener* GlobalShortcutListener::GetInstance() { @@ -65,16 +68,17 @@ GlobalShortcutListenerX11::~GlobalShortcutListenerX11() { 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. +#if defined(TOOLKIT_GTK) + gdk_window_add_filter(gdk_get_default_root_window(), + &GlobalShortcutListenerX11::OnXEventThunk, + this); +#else base::MessagePumpX11::Current()->AddDispatcherForRootWindow(this); +#endif is_listening_ = true; } @@ -84,17 +88,25 @@ void GlobalShortcutListenerX11::StopListening() { DCHECK(registered_hot_keys_.empty()); // Make sure the set is clean before // ending. +#if defined(TOOLKIT_GTK) + gdk_window_remove_filter(NULL, + &GlobalShortcutListenerX11::OnXEventThunk, + this); +#else base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this); +#endif is_listening_ = false; } +#if !defined(TOOLKIT_GTK) uint32_t GlobalShortcutListenerX11::Dispatch(const base::NativeEvent& event) { if (event->type == KeyPress) OnXKeyPressEvent(event); return POST_DISPATCH_NONE; } +#endif bool GlobalShortcutListenerX11::RegisterAcceleratorImpl( const ui::Accelerator& accelerator) { @@ -142,6 +154,17 @@ void GlobalShortcutListenerX11::UnregisterAcceleratorImpl( registered_hot_keys_.erase(accelerator); } +#if defined(TOOLKIT_GTK) +GdkFilterReturn GlobalShortcutListenerX11::OnXEvent(GdkXEvent* gdk_x_event, + GdkEvent* gdk_event) { + XEvent* x_event = static_cast(gdk_x_event); + if (x_event->type == KeyPress) + OnXKeyPressEvent(x_event); + + return GDK_FILTER_CONTINUE; +} +#endif + void GlobalShortcutListenerX11::OnXKeyPressEvent(::XEvent* x_event) { DCHECK(x_event->type == KeyPress); int modifiers = 0; @@ -155,6 +178,4 @@ void GlobalShortcutListenerX11::OnXKeyPressEvent(::XEvent* x_event) { NotifyKeyPressed(accelerator); } -} // namespace api - -} // namespace atom +} // namespace extensions diff --git a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.h b/chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.h old mode 100755 new mode 100644 similarity index 64% rename from chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.h rename to chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.h index 409d248554f00..69ebca1474395 --- a/chromium_src/chrome/browser/ui/shortcut/global_shortcut_listener_x11.h +++ b/chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.h @@ -1,35 +1,40 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 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. -#ifndef CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_X11_H_ -#define CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_X11_H_ +#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_X11_H_ +#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_X11_H_ #include #include #include "base/message_loop/message_pump_dispatcher.h" -#include "chrome/browser/ui/shortcut/global_shortcut_listener.h" +#include "chrome/browser/extensions/global_shortcut_listener.h" -namespace atom { +#if defined(TOOLKIT_GTK) +#include +#include "ui/base/gtk/gtk_signal.h" +#endif // defined(TOOLKIT_GTK) -namespace api { +namespace extensions { // 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, + : +#if !defined(TOOLKIT_GTK) + public base::MessagePumpDispatcher, +#endif public GlobalShortcutListener { public: GlobalShortcutListenerX11(); virtual ~GlobalShortcutListenerX11(); +#if !defined(TOOLKIT_GTK) // base::MessagePumpDispatcher implementation. virtual uint32_t Dispatch(const base::NativeEvent& event) OVERRIDE; - - virtual bool IsAcceleratorRegistered( - const ui::Accelerator& accelerator) OVERRIDE; +#endif private: // GlobalShortcutListener implementation. @@ -40,6 +45,12 @@ class GlobalShortcutListenerX11 virtual void UnregisterAcceleratorImpl( const ui::Accelerator& accelerator) OVERRIDE; +#if defined(TOOLKIT_GTK) + // Callback for XEvents of the default root window. + CHROMEG_CALLBACK_1(GlobalShortcutListenerX11, GdkFilterReturn, + OnXEvent, GdkXEvent*, GdkEvent*); +#endif + // Invoked when a global shortcut is pressed. void OnXKeyPressEvent(::XEvent* x_event); @@ -57,8 +68,6 @@ class GlobalShortcutListenerX11 DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerX11); }; -} // namespace api - -} // namespace atom +} // namespace extensions -#endif // CHROME_BROWSER_UI_SHORTCUT_GLOBAL_SHORTCUT_LISTENER_X11_H_ +#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_X11_H_