refactor: remove global_shortcut_listener from chromium_src/ (#15070)
This commit is contained in:
parent
f69e91975f
commit
ce26687577
12 changed files with 144 additions and 1114 deletions
|
@ -9,6 +9,12 @@ import("//electron/buildflags/buildflags.gni")
|
||||||
source_set("chrome") {
|
source_set("chrome") {
|
||||||
visibility = [ "//electron:electron_lib" ]
|
visibility = [ "//electron:electron_lib" ]
|
||||||
sources = [
|
sources = [
|
||||||
|
"//chrome/browser/extensions/global_shortcut_listener.cc",
|
||||||
|
"//chrome/browser/extensions/global_shortcut_listener.h",
|
||||||
|
"//chrome/browser/extensions/global_shortcut_listener_mac.h",
|
||||||
|
"//chrome/browser/extensions/global_shortcut_listener_mac.mm",
|
||||||
|
"//chrome/browser/extensions/global_shortcut_listener_win.cc",
|
||||||
|
"//chrome/browser/extensions/global_shortcut_listener_win.h",
|
||||||
"//chrome/browser/icon_loader.cc",
|
"//chrome/browser/icon_loader.cc",
|
||||||
"//chrome/browser/icon_loader.h",
|
"//chrome/browser/icon_loader.h",
|
||||||
"//chrome/browser/icon_loader_mac.mm",
|
"//chrome/browser/icon_loader_mac.mm",
|
||||||
|
@ -43,6 +49,10 @@ source_set("chrome") {
|
||||||
|
|
||||||
if (is_linux) {
|
if (is_linux) {
|
||||||
sources += [ "//chrome/browser/icon_loader_auralinux.cc" ]
|
sources += [ "//chrome/browser/icon_loader_auralinux.cc" ]
|
||||||
|
sources += [
|
||||||
|
"//chrome/browser/extensions/global_shortcut_listener_x11.cc",
|
||||||
|
"//chrome/browser/extensions/global_shortcut_listener_x11.h",
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enable_desktop_capturer) {
|
if (enable_desktop_capturer) {
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
// 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 "content/public/browser/browser_thread.h"
|
|
||||||
#include "ui/base/accelerators/accelerator.h"
|
|
||||||
|
|
||||||
using content::BrowserThread;
|
|
||||||
|
|
||||||
namespace extensions {
|
|
||||||
|
|
||||||
GlobalShortcutListener::GlobalShortcutListener()
|
|
||||||
: shortcut_handling_suspended_(false) {
|
|
||||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalShortcutListener::~GlobalShortcutListener() {
|
|
||||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
|
||||||
DCHECK(accelerator_map_.empty()); // Make sure we've cleaned up.
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
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 extensions
|
|
|
@ -1,99 +0,0 @@
|
||||||
// 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_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_
|
|
||||||
#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "base/macros.h"
|
|
||||||
#include "ui/events/keycodes/keyboard_codes.h"
|
|
||||||
|
|
||||||
namespace ui {
|
|
||||||
class Accelerator;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
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<ui::Accelerator, Observer*> AcceleratorMap;
|
|
||||||
AcceleratorMap accelerator_map_;
|
|
||||||
|
|
||||||
// Keeps track of whether shortcut handling is currently suspended.
|
|
||||||
bool shortcut_handling_suspended_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListener);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace extensions
|
|
||||||
|
|
||||||
#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_
|
|
|
@ -1,108 +0,0 @@
|
||||||
// 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_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_
|
|
||||||
#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_
|
|
||||||
|
|
||||||
#include "chrome/browser/extensions/global_shortcut_listener.h"
|
|
||||||
|
|
||||||
#include <Carbon/Carbon.h>
|
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "base/mac/scoped_nsobject.h"
|
|
||||||
|
|
||||||
namespace extensions {
|
|
||||||
|
|
||||||
// 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/volume keys. Uses an event tap for intercepting media
|
|
||||||
// keys (PlayPause, NextTrack, PreviousTrack) and volume keys(VolumeUp,
|
|
||||||
// VolumeDown, VolumeMute).
|
|
||||||
// 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();
|
|
||||||
~GlobalShortcutListenerMac() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
typedef int KeyId;
|
|
||||||
typedef std::map<ui::Accelerator, KeyId> AcceleratorIdMap;
|
|
||||||
typedef std::map<KeyId, ui::Accelerator> IdAcceleratorMap;
|
|
||||||
typedef std::map<KeyId, EventHotKeyRef> IdHotKeyRefMap;
|
|
||||||
|
|
||||||
// Keyboard event callbacks.
|
|
||||||
void OnHotKeyEvent(EventHotKeyID hot_key_id);
|
|
||||||
bool OnMediaOrVolumeKeyEvent(int key_code);
|
|
||||||
|
|
||||||
// GlobalShortcutListener implementation.
|
|
||||||
void StartListening() override;
|
|
||||||
void StopListening() override;
|
|
||||||
bool RegisterAcceleratorImpl(const ui::Accelerator& accelerator) override;
|
|
||||||
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/volume key event tap.
|
|
||||||
void StartWatchingMediaOrVolumeKeys();
|
|
||||||
void StopWatchingMediaOrVolumeKeys();
|
|
||||||
|
|
||||||
// Enable and disable the hot key event handler.
|
|
||||||
void StartWatchingHotKeys();
|
|
||||||
void StopWatchingHotKeys();
|
|
||||||
|
|
||||||
// Whether or not any media/volume keys are currently registered.
|
|
||||||
bool IsAnyMediaOrVolumeKeyRegistered();
|
|
||||||
|
|
||||||
// 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/volume 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/volume keys.
|
|
||||||
CFMachPortRef event_tap_;
|
|
||||||
CFRunLoopSourceRef event_tap_source_;
|
|
||||||
|
|
||||||
// Event handler for keyboard shortcut hot keys.
|
|
||||||
EventHandlerRef event_handler_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerMac);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace extensions
|
|
||||||
|
|
||||||
#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_
|
|
|
@ -1,398 +0,0 @@
|
||||||
// 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_mac.h"
|
|
||||||
|
|
||||||
#include <ApplicationServices/ApplicationServices.h>
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#include <IOKit/hidsystem/ev_keymap.h>
|
|
||||||
|
|
||||||
#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 extensions::GlobalShortcutListenerMac;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// The media/volume keys subtype. No official docs found, but widely known.
|
|
||||||
// http://lists.apple.com/archives/cocoa-dev/2007/Aug/msg00499.html
|
|
||||||
const int kSystemDefinedEventMediaAndVolumeKeysSubtype = 8;
|
|
||||||
|
|
||||||
ui::KeyboardCode MediaOrVolumeKeyCodeToKeyboardCode(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;
|
|
||||||
case NX_KEYTYPE_SOUND_UP:
|
|
||||||
return ui::VKEY_VOLUME_UP;
|
|
||||||
case NX_KEYTYPE_SOUND_DOWN:
|
|
||||||
return ui::VKEY_VOLUME_DOWN;
|
|
||||||
case NX_KEYTYPE_MUTE:
|
|
||||||
return ui::VKEY_VOLUME_MUTE;
|
|
||||||
}
|
|
||||||
return ui::VKEY_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsMediaOrVolumeKey(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 ||
|
|
||||||
accelerator.key_code() == ui::VKEY_VOLUME_UP ||
|
|
||||||
accelerator.key_code() == ui::VKEY_VOLUME_DOWN ||
|
|
||||||
accelerator.key_code() == ui::VKEY_VOLUME_MUTE);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace extensions {
|
|
||||||
|
|
||||||
// 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 (IsAnyMediaOrVolumeKeyRegistered())
|
|
||||||
StopWatchingMediaOrVolumeKeys();
|
|
||||||
|
|
||||||
if (IsAnyHotKeyRegistered())
|
|
||||||
StopWatchingHotKeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
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::OnMediaOrVolumeKeyEvent(
|
|
||||||
int media_or_volume_key_code) {
|
|
||||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
|
||||||
ui::KeyboardCode key_code =
|
|
||||||
MediaOrVolumeKeyCodeToKeyboardCode(media_or_volume_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 (IsMediaOrVolumeKey(accelerator)) {
|
|
||||||
if (!IsAnyMediaOrVolumeKeyRegistered()) {
|
|
||||||
// If this is the first media/volume key registered, start the event tap.
|
|
||||||
StartWatchingMediaOrVolumeKeys();
|
|
||||||
}
|
|
||||||
} 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 (!IsMediaOrVolumeKey(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 (IsMediaOrVolumeKey(accelerator)) {
|
|
||||||
// If we unregistered a media/volume key, and now no media/volume keys are
|
|
||||||
// registered, stop the media/volume key tap.
|
|
||||||
if (!IsAnyMediaOrVolumeKeyRegistered())
|
|
||||||
StopWatchingMediaOrVolumeKeys();
|
|
||||||
} 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::StartWatchingMediaOrVolumeKeys() {
|
|
||||||
// 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/volume 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::StopWatchingMediaOrVolumeKeys() {
|
|
||||||
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::IsAnyMediaOrVolumeKeyRegistered() {
|
|
||||||
// Iterate through registered accelerators, looking for media/volume keys.
|
|
||||||
AcceleratorIdMap::iterator it;
|
|
||||||
for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) {
|
|
||||||
if (IsMediaOrVolumeKey(it->first))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GlobalShortcutListenerMac::IsAnyHotKeyRegistered() {
|
|
||||||
AcceleratorIdMap::iterator it;
|
|
||||||
for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) {
|
|
||||||
if (!IsMediaOrVolumeKey(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<GlobalShortcutListenerMac*>(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/volume keys.
|
|
||||||
if (type != NX_SYSDEFINED || [ns_event type] != NSSystemDefined ||
|
|
||||||
[ns_event subtype] != kSystemDefinedEventMediaAndVolumeKeysSubtype) {
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSInteger data1 = [ns_event data1];
|
|
||||||
// Ignore media keys that aren't previous, next and play/pause and
|
|
||||||
// volume keys that aren't up, down and mute.
|
|
||||||
// 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 && key_code != NX_KEYTYPE_SOUND_UP &&
|
|
||||||
key_code != NX_KEYTYPE_SOUND_DOWN && key_code != NX_KEYTYPE_MUTE) {
|
|
||||||
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/volume key that we care about. Send it to the caller.
|
|
||||||
bool was_handled = shortcut_listener->OnMediaOrVolumeKeyEvent(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<GlobalShortcutListenerMac*>(user_data);
|
|
||||||
shortcut_listener->OnHotKeyEvent(hot_key_id);
|
|
||||||
return noErr;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extensions
|
|
|
@ -1,109 +0,0 @@
|
||||||
// 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_win.h"
|
|
||||||
|
|
||||||
#include "base/bind.h"
|
|
||||||
#include "base/bind_helpers.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"
|
|
||||||
#include "ui/gfx/win/singleton_hwnd.h"
|
|
||||||
|
|
||||||
using content::BrowserThread;
|
|
||||||
|
|
||||||
namespace extensions {
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GlobalShortcutListenerWin::StartListening() {
|
|
||||||
DCHECK(!is_listening_); // Don't start twice.
|
|
||||||
DCHECK(!hotkey_ids_.empty()); // Also don't start if no hotkey is registered.
|
|
||||||
singleton_hwnd_observer_.reset(new gfx::SingletonHwndObserver(base::Bind(
|
|
||||||
&GlobalShortcutListenerWin::OnWndProc, base::Unretained(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.
|
|
||||||
singleton_hwnd_observer_.reset(nullptr);
|
|
||||||
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;
|
|
||||||
modifiers |= (LOWORD(lparam) & MOD_WIN) ? ui::EF_COMMAND_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;
|
|
||||||
modifiers |= accelerator.IsCmdDown() ? MOD_WIN : 0;
|
|
||||||
|
|
||||||
static int hotkey_id = 0;
|
|
||||||
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 =
|
|
||||||
!!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 extensions
|
|
|
@ -1,50 +0,0 @@
|
||||||
// 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_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
|
|
||||||
#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#include "chrome/browser/extensions/global_shortcut_listener.h"
|
|
||||||
#include "ui/gfx/win/singleton_hwnd.h"
|
|
||||||
#include "ui/gfx/win/singleton_hwnd_observer.h"
|
|
||||||
|
|
||||||
namespace extensions {
|
|
||||||
|
|
||||||
// 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:
|
|
||||||
GlobalShortcutListenerWin();
|
|
||||||
~GlobalShortcutListenerWin() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// The implementation of our Window Proc, called by SingletonHwndObserver.
|
|
||||||
void OnWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
|
|
||||||
|
|
||||||
// GlobalShortcutListener implementation.
|
|
||||||
void StartListening() override;
|
|
||||||
void StopListening() override;
|
|
||||||
bool RegisterAcceleratorImpl(const ui::Accelerator& accelerator) override;
|
|
||||||
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<ui::Accelerator, int> HotkeyIdMap;
|
|
||||||
HotkeyIdMap hotkey_ids_;
|
|
||||||
|
|
||||||
std::unique_ptr<gfx::SingletonHwndObserver> singleton_hwnd_observer_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerWin);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace extensions
|
|
||||||
|
|
||||||
#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
|
|
|
@ -1,161 +0,0 @@
|
||||||
// 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/extensions/global_shortcut_listener_x11.h"
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#include "base/macros.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/events/platform/platform_event_source.h"
|
|
||||||
#include "ui/gfx/x/x11.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;
|
|
||||||
modifiers |= accelerator.IsCmdDown() ? Mod4Mask : 0;
|
|
||||||
|
|
||||||
return modifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace extensions {
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GlobalShortcutListenerX11::StartListening() {
|
|
||||||
DCHECK(!is_listening_); // Don't start twice.
|
|
||||||
DCHECK(!registered_hot_keys_.empty()); // Also don't start if no hotkey is
|
|
||||||
// registered.
|
|
||||||
|
|
||||||
ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(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.
|
|
||||||
|
|
||||||
ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
|
|
||||||
|
|
||||||
is_listening_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GlobalShortcutListenerX11::CanDispatchEvent(
|
|
||||||
const ui::PlatformEvent& event) {
|
|
||||||
return event->type == KeyPress;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t GlobalShortcutListenerX11::DispatchEvent(
|
|
||||||
const ui::PlatformEvent& event) {
|
|
||||||
CHECK_EQ(KeyPress, event->type);
|
|
||||||
OnXKeyPressEvent(event);
|
|
||||||
|
|
||||||
return ui::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_, x11::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;
|
|
||||||
modifiers |= (x_event->xkey.state & Mod4Mask) ? ui::EF_COMMAND_DOWN : 0;
|
|
||||||
|
|
||||||
ui::Accelerator accelerator(ui::KeyboardCodeFromXKeyEvent(x_event),
|
|
||||||
modifiers);
|
|
||||||
if (registered_hot_keys_.find(accelerator) != registered_hot_keys_.end())
|
|
||||||
NotifyKeyPressed(accelerator);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extensions
|
|
|
@ -1,58 +0,0 @@
|
||||||
// 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_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_X11_H_
|
|
||||||
#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_X11_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "base/macros.h"
|
|
||||||
#include "chrome/browser/extensions/global_shortcut_listener.h"
|
|
||||||
#include "ui/events/platform/platform_event_dispatcher.h"
|
|
||||||
#include "ui/gfx/x/x11.h"
|
|
||||||
|
|
||||||
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 GlobalShortcutListener,
|
|
||||||
public ui::PlatformEventDispatcher {
|
|
||||||
public:
|
|
||||||
GlobalShortcutListenerX11();
|
|
||||||
~GlobalShortcutListenerX11() override;
|
|
||||||
|
|
||||||
// ui::PlatformEventDispatcher implementation.
|
|
||||||
bool CanDispatchEvent(const ui::PlatformEvent& event) override;
|
|
||||||
uint32_t DispatchEvent(const ui::PlatformEvent& event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// GlobalShortcutListener implementation.
|
|
||||||
void StartListening() override;
|
|
||||||
void StopListening() override;
|
|
||||||
bool RegisterAcceleratorImpl(const ui::Accelerator& accelerator) override;
|
|
||||||
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<ui::Accelerator> RegisteredHotKeys;
|
|
||||||
RegisteredHotKeys registered_hot_keys_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerX11);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace extensions
|
|
||||||
|
|
||||||
#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_X11_H_
|
|
|
@ -582,14 +582,6 @@ filenames = {
|
||||||
"chromium_src/chrome/browser/chrome_process_finder_win.cc",
|
"chromium_src/chrome/browser/chrome_process_finder_win.cc",
|
||||||
"chromium_src/chrome/browser/chrome_process_finder_win.h",
|
"chromium_src/chrome/browser/chrome_process_finder_win.h",
|
||||||
"chromium_src/chrome/browser/chrome_notification_types.h",
|
"chromium_src/chrome/browser/chrome_notification_types.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/printing/print_job.cc",
|
"chromium_src/chrome/browser/printing/print_job.cc",
|
||||||
"chromium_src/chrome/browser/printing/print_job.h",
|
"chromium_src/chrome/browser/printing/print_job.h",
|
||||||
"chromium_src/chrome/browser/printing/print_job_manager.cc",
|
"chromium_src/chrome/browser/printing/print_job_manager.cc",
|
||||||
|
|
|
@ -491,3 +491,15 @@ patches:
|
||||||
dylib currently fails to resolve Squirrel.framework on OSX, we need to fix
|
dylib currently fails to resolve Squirrel.framework on OSX, we need to fix
|
||||||
this but it is not a blocker for releasing Electron. This patch removes
|
this but it is not a blocker for releasing Electron. This patch removes
|
||||||
the hard fail on dylib resolve failure from dump_syms
|
the hard fail on dylib resolve failure from dump_syms
|
||||||
|
-
|
||||||
|
author: Jeremy Apthorp <jeremya@chromium.org>
|
||||||
|
file: command-ismediakey.patch
|
||||||
|
description: |
|
||||||
|
define Command::IsMediaKey on mac
|
||||||
|
|
||||||
|
the definition is copied from //chrome/common/extensions/command.cc,
|
||||||
|
which also defines a bunch of other stuff that depends on extensions.
|
||||||
|
since we only need IsMediaKey, and we don't want the extensions stuff
|
||||||
|
(and aren't compiling command.cc), it's safe to duplicate the
|
||||||
|
definition. A candidate for upstreaming would be to move the IsMediaKey
|
||||||
|
function into //ui.
|
||||||
|
|
122
patches/common/chromium/command-ismediakey.patch
Normal file
122
patches/common/chromium/command-ismediakey.patch
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
From 2d028f727b68a0aa7f83cc069e1df4d13a9e173e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Jeremy Apthorp <jeremya@chromium.org>
|
||||||
|
Date: Wed, 10 Oct 2018 15:07:34 -0700
|
||||||
|
Subject: define Command::IsMediaKey on mac
|
||||||
|
|
||||||
|
the definition is copied from //chrome/common/extensions/command.cc,
|
||||||
|
which also defines a bunch of other stuff that depends on extensions.
|
||||||
|
since we only need IsMediaKey, and we don't want the extensions stuff
|
||||||
|
(and aren't compiling command.cc), it's safe to duplicate the
|
||||||
|
definition. A candidate for upstreaming would be to move the IsMediaKey
|
||||||
|
function into //ui.
|
||||||
|
|
||||||
|
Also apply electron/electron@0f67b1866a9f00b852370e721affa4efda623f3a
|
||||||
|
and electron/electron@d2368d2d3b3de9eec4cc32b6aaf035cc89921bf1 as
|
||||||
|
patches.
|
||||||
|
|
||||||
|
diff --git a/chrome/browser/extensions/global_shortcut_listener_mac.mm b/chrome/browser/extensions/global_shortcut_listener_mac.mm
|
||||||
|
index f612ba2fb795..77c2c68e6f9b 100644
|
||||||
|
--- a/chrome/browser/extensions/global_shortcut_listener_mac.mm
|
||||||
|
+++ b/chrome/browser/extensions/global_shortcut_listener_mac.mm
|
||||||
|
@@ -20,6 +20,26 @@ using extensions::GlobalShortcutListenerMac;
|
||||||
|
|
||||||
|
namespace extensions {
|
||||||
|
|
||||||
|
+// NOTE: this is defined in command.cc, but command.cc is full of
|
||||||
|
+// chrome-extensions-specific logic that we don't want to depend on.
|
||||||
|
+// Since we don't build command.cc in Electron, it's safe to re-define this
|
||||||
|
+// function here. Ideally, though, `IsMediaKey` would be the responsibility of
|
||||||
|
+// `ui::Accelerator`, rather than `extensions::Command`.
|
||||||
|
+
|
||||||
|
+// static
|
||||||
|
+bool Command::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 ||
|
||||||
|
+ accelerator.key_code() == ui::VKEY_VOLUME_UP ||
|
||||||
|
+ accelerator.key_code() == ui::VKEY_VOLUME_DOWN ||
|
||||||
|
+ accelerator.key_code() == ui::VKEY_VOLUME_MUTE);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
// static
|
||||||
|
GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
|
||||||
|
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||||
|
diff --git a/chrome/browser/extensions/global_shortcut_listener_win.cc b/chrome/browser/extensions/global_shortcut_listener_win.cc
|
||||||
|
index 65b244d55441..a217f0a1ad96 100644
|
||||||
|
--- a/chrome/browser/extensions/global_shortcut_listener_win.cc
|
||||||
|
+++ b/chrome/browser/extensions/global_shortcut_listener_win.cc
|
||||||
|
@@ -63,6 +63,8 @@ void GlobalShortcutListenerWin::OnWndProc(HWND hwnd,
|
||||||
|
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;
|
||||||
|
+ modifiers |= (LOWORD(lparam) & MOD_WIN) ? ui::EF_COMMAND_DOWN : 0;
|
||||||
|
+
|
||||||
|
ui::Accelerator accelerator(
|
||||||
|
ui::KeyboardCodeForWindowsKeyCode(key_code), modifiers);
|
||||||
|
|
||||||
|
@@ -77,6 +79,8 @@ bool GlobalShortcutListenerWin::RegisterAcceleratorImpl(
|
||||||
|
modifiers |= accelerator.IsShiftDown() ? MOD_SHIFT : 0;
|
||||||
|
modifiers |= accelerator.IsCtrlDown() ? MOD_CONTROL : 0;
|
||||||
|
modifiers |= accelerator.IsAltDown() ? MOD_ALT : 0;
|
||||||
|
+ modifiers |= accelerator.IsCmdDown() ? MOD_WIN : 0;
|
||||||
|
+
|
||||||
|
static int hotkey_id = 0;
|
||||||
|
bool success = !!RegisterHotKey(
|
||||||
|
gfx::SingletonHwnd::GetInstance()->hwnd(),
|
||||||
|
diff --git a/chrome/browser/extensions/global_shortcut_listener_x11.cc b/chrome/browser/extensions/global_shortcut_listener_x11.cc
|
||||||
|
index 362811063a42..fc407da7047b 100644
|
||||||
|
--- a/chrome/browser/extensions/global_shortcut_listener_x11.cc
|
||||||
|
+++ b/chrome/browser/extensions/global_shortcut_listener_x11.cc
|
||||||
|
@@ -38,6 +38,7 @@ int GetNativeModifiers(const ui::Accelerator& accelerator) {
|
||||||
|
modifiers |= accelerator.IsShiftDown() ? ShiftMask : 0;
|
||||||
|
modifiers |= accelerator.IsCtrlDown() ? ControlMask : 0;
|
||||||
|
modifiers |= accelerator.IsAltDown() ? Mod1Mask : 0;
|
||||||
|
+ modifiers |= accelerator.IsCmdDown() ? Mod4Mask : 0;
|
||||||
|
|
||||||
|
return modifiers;
|
||||||
|
}
|
||||||
|
@@ -151,6 +152,8 @@ void GlobalShortcutListenerX11::OnXKeyPressEvent(::XEvent* x_event) {
|
||||||
|
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;
|
||||||
|
+ // For Windows key
|
||||||
|
+ modifiers |= (x_event->xkey.state & Mod4Mask) ? ui::EF_COMMAND_DOWN: 0;
|
||||||
|
|
||||||
|
ui::Accelerator accelerator(
|
||||||
|
ui::KeyboardCodeFromXKeyEvent(x_event), modifiers);
|
||||||
|
diff --git a/ui/base/accelerators/media_keys_listener_mac.mm b/ui/base/accelerators/media_keys_listener_mac.mm
|
||||||
|
index cd595b0c017d..941c1a76a1c3 100644
|
||||||
|
--- a/ui/base/accelerators/media_keys_listener_mac.mm
|
||||||
|
+++ b/ui/base/accelerators/media_keys_listener_mac.mm
|
||||||
|
@@ -30,6 +30,12 @@ ui::KeyboardCode MediaKeyCodeToKeyboardCode(int key_code) {
|
||||||
|
case NX_KEYTYPE_NEXT:
|
||||||
|
case NX_KEYTYPE_FAST:
|
||||||
|
return ui::VKEY_MEDIA_NEXT_TRACK;
|
||||||
|
+ case NX_KEYTYPE_SOUND_UP:
|
||||||
|
+ return ui::VKEY_VOLUME_UP;
|
||||||
|
+ case NX_KEYTYPE_SOUND_DOWN:
|
||||||
|
+ return ui::VKEY_VOLUME_DOWN;
|
||||||
|
+ case NX_KEYTYPE_MUTE:
|
||||||
|
+ return ui::VKEY_VOLUME_MUTE;
|
||||||
|
}
|
||||||
|
return ui::VKEY_UNKNOWN;
|
||||||
|
}
|
||||||
|
@@ -180,7 +186,10 @@ CGEventRef MediaKeysListenerImpl::EventTapCallback(CGEventTapProxy proxy,
|
||||||
|
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) {
|
||||||
|
+ key_code != NX_KEYTYPE_REWIND &&
|
||||||
|
+ key_code != NX_KEYTYPE_SOUND_UP &&
|
||||||
|
+ key_code != NX_KEYTYPE_SOUND_DOWN &&
|
||||||
|
+ key_code != NX_KEYTYPE_MUTE) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.17.0
|
||||||
|
|
Loading…
Reference in a new issue