Merge pull request #642 from hokein/master
Add Volume keys support in global-shortcut API, fix #630.
This commit is contained in:
commit
92b5dab3f9
2 changed files with 57 additions and 42 deletions
|
@ -21,8 +21,8 @@ namespace extensions {
|
||||||
// forwards its output to the base class for processing.
|
// forwards its output to the base class for processing.
|
||||||
//
|
//
|
||||||
// This class does two things:
|
// This class does two things:
|
||||||
// 1. Intercepts media keys. Uses an event tap for intercepting media keys
|
// 1. Intercepts media/volume keys. Uses an event tap for intercepting media keys
|
||||||
// (PlayPause, NextTrack, PreviousTrack).
|
// (PlayPause, NextTrack, PreviousTrack) and volume keys(VolumeUp, VolumeDown, VolumeMute).
|
||||||
// 2. Binds keyboard shortcuts (hot keys). Carbon RegisterEventHotKey API for
|
// 2. Binds keyboard shortcuts (hot keys). Carbon RegisterEventHotKey API for
|
||||||
// binding to non-media key global hot keys (eg. Command-Shift-1).
|
// binding to non-media key global hot keys (eg. Command-Shift-1).
|
||||||
class GlobalShortcutListenerMac : public GlobalShortcutListener {
|
class GlobalShortcutListenerMac : public GlobalShortcutListener {
|
||||||
|
@ -38,7 +38,7 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener {
|
||||||
|
|
||||||
// Keyboard event callbacks.
|
// Keyboard event callbacks.
|
||||||
void OnHotKeyEvent(EventHotKeyID hot_key_id);
|
void OnHotKeyEvent(EventHotKeyID hot_key_id);
|
||||||
bool OnMediaKeyEvent(int key_code);
|
bool OnMediaOrVolumeKeyEvent(int key_code);
|
||||||
|
|
||||||
// GlobalShortcutListener implementation.
|
// GlobalShortcutListener implementation.
|
||||||
virtual void StartListening() OVERRIDE;
|
virtual void StartListening() OVERRIDE;
|
||||||
|
@ -52,16 +52,16 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener {
|
||||||
bool RegisterHotKey(const ui::Accelerator& accelerator, KeyId hot_key_id);
|
bool RegisterHotKey(const ui::Accelerator& accelerator, KeyId hot_key_id);
|
||||||
void UnregisterHotKey(const ui::Accelerator& accelerator);
|
void UnregisterHotKey(const ui::Accelerator& accelerator);
|
||||||
|
|
||||||
// Enable and disable the media key event tap.
|
// Enable and disable the media/volume key event tap.
|
||||||
void StartWatchingMediaKeys();
|
void StartWatchingMediaOrVolumeKeys();
|
||||||
void StopWatchingMediaKeys();
|
void StopWatchingMediaOrVolumeKeys();
|
||||||
|
|
||||||
// Enable and disable the hot key event handler.
|
// Enable and disable the hot key event handler.
|
||||||
void StartWatchingHotKeys();
|
void StartWatchingHotKeys();
|
||||||
void StopWatchingHotKeys();
|
void StopWatchingHotKeys();
|
||||||
|
|
||||||
// Whether or not any media keys are currently registered.
|
// Whether or not any media/volume keys are currently registered.
|
||||||
bool IsAnyMediaKeyRegistered();
|
bool IsAnyMediaOrVolumeKeyRegistered();
|
||||||
|
|
||||||
// Whether or not any hot keys are currently registered.
|
// Whether or not any hot keys are currently registered.
|
||||||
bool IsAnyHotKeyRegistered();
|
bool IsAnyHotKeyRegistered();
|
||||||
|
@ -80,7 +80,7 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener {
|
||||||
// The hotkey identifier for the next global shortcut that is added.
|
// The hotkey identifier for the next global shortcut that is added.
|
||||||
KeyId hot_key_id_;
|
KeyId hot_key_id_;
|
||||||
|
|
||||||
// A map of all hotkeys (media keys and shortcuts) mapping to their
|
// A map of all hotkeys (media/volume keys and shortcuts) mapping to their
|
||||||
// corresponding hotkey IDs. For quickly finding if an accelerator is
|
// corresponding hotkey IDs. For quickly finding if an accelerator is
|
||||||
// registered.
|
// registered.
|
||||||
AcceleratorIdMap accelerator_ids_;
|
AcceleratorIdMap accelerator_ids_;
|
||||||
|
@ -91,7 +91,7 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener {
|
||||||
// Keyboard shortcut IDs to hotkeys map for unregistration.
|
// Keyboard shortcut IDs to hotkeys map for unregistration.
|
||||||
IdHotKeyRefMap id_hot_key_refs_;
|
IdHotKeyRefMap id_hot_key_refs_;
|
||||||
|
|
||||||
// Event tap for intercepting mac media keys.
|
// Event tap for intercepting mac media/volume keys.
|
||||||
CFMachPortRef event_tap_;
|
CFMachPortRef event_tap_;
|
||||||
CFRunLoopSourceRef event_tap_source_;
|
CFRunLoopSourceRef event_tap_source_;
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,11 @@ using extensions::GlobalShortcutListenerMac;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// The media keys subtype. No official docs found, but widely known.
|
// The media/volume keys subtype. No official docs found, but widely known.
|
||||||
// http://lists.apple.com/archives/cocoa-dev/2007/Aug/msg00499.html
|
// http://lists.apple.com/archives/cocoa-dev/2007/Aug/msg00499.html
|
||||||
const int kSystemDefinedEventMediaKeysSubtype = 8;
|
const int kSystemDefinedEventMediaAndVolumeKeysSubtype = 8;
|
||||||
|
|
||||||
ui::KeyboardCode MediaKeyCodeToKeyboardCode(int key_code) {
|
ui::KeyboardCode MediaOrVolumeKeyCodeToKeyboardCode(int key_code) {
|
||||||
switch (key_code) {
|
switch (key_code) {
|
||||||
case NX_KEYTYPE_PLAY:
|
case NX_KEYTYPE_PLAY:
|
||||||
return ui::VKEY_MEDIA_PLAY_PAUSE;
|
return ui::VKEY_MEDIA_PLAY_PAUSE;
|
||||||
|
@ -33,17 +33,26 @@ ui::KeyboardCode MediaKeyCodeToKeyboardCode(int key_code) {
|
||||||
case NX_KEYTYPE_NEXT:
|
case NX_KEYTYPE_NEXT:
|
||||||
case NX_KEYTYPE_FAST:
|
case NX_KEYTYPE_FAST:
|
||||||
return ui::VKEY_MEDIA_NEXT_TRACK;
|
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;
|
return ui::VKEY_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsMediaKey(const ui::Accelerator& accelerator) {
|
bool IsMediaOrVolumeKey(const ui::Accelerator& accelerator) {
|
||||||
if (accelerator.modifiers() != 0)
|
if (accelerator.modifiers() != 0)
|
||||||
return false;
|
return false;
|
||||||
return (accelerator.key_code() == ui::VKEY_MEDIA_NEXT_TRACK ||
|
return (accelerator.key_code() == ui::VKEY_MEDIA_NEXT_TRACK ||
|
||||||
accelerator.key_code() == ui::VKEY_MEDIA_PREV_TRACK ||
|
accelerator.key_code() == ui::VKEY_MEDIA_PREV_TRACK ||
|
||||||
accelerator.key_code() == ui::VKEY_MEDIA_PLAY_PAUSE ||
|
accelerator.key_code() == ui::VKEY_MEDIA_PLAY_PAUSE ||
|
||||||
accelerator.key_code() == ui::VKEY_MEDIA_STOP);
|
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
|
||||||
|
@ -77,8 +86,8 @@ GlobalShortcutListenerMac::~GlobalShortcutListenerMac() {
|
||||||
|
|
||||||
// If keys are still registered, make sure we stop the tap. Again, this
|
// If keys are still registered, make sure we stop the tap. Again, this
|
||||||
// should never happen.
|
// should never happen.
|
||||||
if (IsAnyMediaKeyRegistered())
|
if (IsAnyMediaOrVolumeKeyRegistered())
|
||||||
StopWatchingMediaKeys();
|
StopWatchingMediaOrVolumeKeys();
|
||||||
|
|
||||||
if (IsAnyHotKeyRegistered())
|
if (IsAnyHotKeyRegistered())
|
||||||
StopWatchingHotKeys();
|
StopWatchingHotKeys();
|
||||||
|
@ -114,9 +123,11 @@ void GlobalShortcutListenerMac::OnHotKeyEvent(EventHotKeyID hot_key_id) {
|
||||||
NotifyKeyPressed(accelerator);
|
NotifyKeyPressed(accelerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GlobalShortcutListenerMac::OnMediaKeyEvent(int media_key_code) {
|
bool GlobalShortcutListenerMac::OnMediaOrVolumeKeyEvent(
|
||||||
|
int media_or_volume_key_code) {
|
||||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||||
ui::KeyboardCode key_code = MediaKeyCodeToKeyboardCode(media_key_code);
|
ui::KeyboardCode key_code = MediaOrVolumeKeyCodeToKeyboardCode(
|
||||||
|
media_or_volume_key_code);
|
||||||
// Create an accelerator corresponding to the keyCode.
|
// Create an accelerator corresponding to the keyCode.
|
||||||
ui::Accelerator accelerator(key_code, 0);
|
ui::Accelerator accelerator(key_code, 0);
|
||||||
// Look for a match with a bound hot_key.
|
// Look for a match with a bound hot_key.
|
||||||
|
@ -133,10 +144,10 @@ bool GlobalShortcutListenerMac::RegisterAcceleratorImpl(
|
||||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||||
DCHECK(accelerator_ids_.find(accelerator) == accelerator_ids_.end());
|
DCHECK(accelerator_ids_.find(accelerator) == accelerator_ids_.end());
|
||||||
|
|
||||||
if (IsMediaKey(accelerator)) {
|
if (IsMediaOrVolumeKey(accelerator)) {
|
||||||
if (!IsAnyMediaKeyRegistered()) {
|
if (!IsAnyMediaOrVolumeKeyRegistered()) {
|
||||||
// If this is the first media key registered, start the event tap.
|
// If this is the first media/volume key registered, start the event tap.
|
||||||
StartWatchingMediaKeys();
|
StartWatchingMediaOrVolumeKeys();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Register hot_key if they are non-media keyboard shortcuts.
|
// Register hot_key if they are non-media keyboard shortcuts.
|
||||||
|
@ -161,7 +172,7 @@ void GlobalShortcutListenerMac::UnregisterAcceleratorImpl(
|
||||||
DCHECK(accelerator_ids_.find(accelerator) != accelerator_ids_.end());
|
DCHECK(accelerator_ids_.find(accelerator) != accelerator_ids_.end());
|
||||||
|
|
||||||
// Unregister the hot_key if it's a keyboard shortcut.
|
// Unregister the hot_key if it's a keyboard shortcut.
|
||||||
if (!IsMediaKey(accelerator))
|
if (!IsMediaOrVolumeKey(accelerator))
|
||||||
UnregisterHotKey(accelerator);
|
UnregisterHotKey(accelerator);
|
||||||
|
|
||||||
// Remove hot_key from the mappings.
|
// Remove hot_key from the mappings.
|
||||||
|
@ -169,11 +180,11 @@ void GlobalShortcutListenerMac::UnregisterAcceleratorImpl(
|
||||||
id_accelerators_.erase(key_id);
|
id_accelerators_.erase(key_id);
|
||||||
accelerator_ids_.erase(accelerator);
|
accelerator_ids_.erase(accelerator);
|
||||||
|
|
||||||
if (IsMediaKey(accelerator)) {
|
if (IsMediaOrVolumeKey(accelerator)) {
|
||||||
// If we unregistered a media key, and now no media keys are registered,
|
// If we unregistered a media/volume key, and now no media/volume keys are registered,
|
||||||
// stop the media key tap.
|
// stop the media/volume key tap.
|
||||||
if (!IsAnyMediaKeyRegistered())
|
if (!IsAnyMediaOrVolumeKeyRegistered())
|
||||||
StopWatchingMediaKeys();
|
StopWatchingMediaOrVolumeKeys();
|
||||||
} else {
|
} else {
|
||||||
// If we unregistered a hot key, and no more hot keys are registered, remove
|
// If we unregistered a hot key, and no more hot keys are registered, remove
|
||||||
// the hot key handler.
|
// the hot key handler.
|
||||||
|
@ -226,12 +237,12 @@ void GlobalShortcutListenerMac::UnregisterHotKey(
|
||||||
id_hot_key_refs_.erase(key_id);
|
id_hot_key_refs_.erase(key_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalShortcutListenerMac::StartWatchingMediaKeys() {
|
void GlobalShortcutListenerMac::StartWatchingMediaOrVolumeKeys() {
|
||||||
// Make sure there's no existing event tap.
|
// Make sure there's no existing event tap.
|
||||||
DCHECK(event_tap_ == NULL);
|
DCHECK(event_tap_ == NULL);
|
||||||
DCHECK(event_tap_source_ == NULL);
|
DCHECK(event_tap_source_ == NULL);
|
||||||
|
|
||||||
// Add an event tap to intercept the system defined media key events.
|
// Add an event tap to intercept the system defined media/volume key events.
|
||||||
event_tap_ = CGEventTapCreate(kCGSessionEventTap,
|
event_tap_ = CGEventTapCreate(kCGSessionEventTap,
|
||||||
kCGHeadInsertEventTap,
|
kCGHeadInsertEventTap,
|
||||||
kCGEventTapOptionDefault,
|
kCGEventTapOptionDefault,
|
||||||
|
@ -254,7 +265,7 @@ void GlobalShortcutListenerMac::StartWatchingMediaKeys() {
|
||||||
kCFRunLoopCommonModes);
|
kCFRunLoopCommonModes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalShortcutListenerMac::StopWatchingMediaKeys() {
|
void GlobalShortcutListenerMac::StopWatchingMediaOrVolumeKeys() {
|
||||||
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), event_tap_source_,
|
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), event_tap_source_,
|
||||||
kCFRunLoopCommonModes);
|
kCFRunLoopCommonModes);
|
||||||
// Ensure both event tap and source are initialized.
|
// Ensure both event tap and source are initialized.
|
||||||
|
@ -287,11 +298,11 @@ void GlobalShortcutListenerMac::StopWatchingHotKeys() {
|
||||||
event_handler_ = NULL;
|
event_handler_ = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GlobalShortcutListenerMac::IsAnyMediaKeyRegistered() {
|
bool GlobalShortcutListenerMac::IsAnyMediaOrVolumeKeyRegistered() {
|
||||||
// Iterate through registered accelerators, looking for media keys.
|
// Iterate through registered accelerators, looking for media/volume keys.
|
||||||
AcceleratorIdMap::iterator it;
|
AcceleratorIdMap::iterator it;
|
||||||
for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) {
|
for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) {
|
||||||
if (IsMediaKey(it->first))
|
if (IsMediaOrVolumeKey(it->first))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -300,7 +311,7 @@ bool GlobalShortcutListenerMac::IsAnyMediaKeyRegistered() {
|
||||||
bool GlobalShortcutListenerMac::IsAnyHotKeyRegistered() {
|
bool GlobalShortcutListenerMac::IsAnyHotKeyRegistered() {
|
||||||
AcceleratorIdMap::iterator it;
|
AcceleratorIdMap::iterator it;
|
||||||
for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) {
|
for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) {
|
||||||
if (!IsMediaKey(it->first))
|
if (!IsMediaOrVolumeKey(it->first))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -329,20 +340,24 @@ CGEventRef GlobalShortcutListenerMac::EventTapCallback(
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore events that are not system defined media keys.
|
// Ignore events that are not system defined media/volume keys.
|
||||||
if (type != NX_SYSDEFINED ||
|
if (type != NX_SYSDEFINED ||
|
||||||
[ns_event type] != NSSystemDefined ||
|
[ns_event type] != NSSystemDefined ||
|
||||||
[ns_event subtype] != kSystemDefinedEventMediaKeysSubtype) {
|
[ns_event subtype] != kSystemDefinedEventMediaAndVolumeKeysSubtype) {
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSInteger data1 = [ns_event data1];
|
NSInteger data1 = [ns_event data1];
|
||||||
// Ignore media keys that aren't previous, next and play/pause.
|
// 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/
|
// Magical constants are from http://weblog.rogueamoeba.com/2007/09/29/
|
||||||
int key_code = (data1 & 0xFFFF0000) >> 16;
|
int key_code = (data1 & 0xFFFF0000) >> 16;
|
||||||
if (key_code != NX_KEYTYPE_PLAY && key_code != NX_KEYTYPE_NEXT &&
|
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_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;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,8 +368,8 @@ CGEventRef GlobalShortcutListenerMac::EventTapCallback(
|
||||||
if (!is_key_pressed)
|
if (!is_key_pressed)
|
||||||
return event;
|
return event;
|
||||||
|
|
||||||
// Now we have a media key that we care about. Send it to the caller.
|
// Now we have a media/volume key that we care about. Send it to the caller.
|
||||||
bool was_handled = shortcut_listener->OnMediaKeyEvent(key_code);
|
bool was_handled = shortcut_listener->OnMediaOrVolumeKeyEvent(key_code);
|
||||||
|
|
||||||
// Prevent event from proagating to other apps if handled by Chrome.
|
// Prevent event from proagating to other apps if handled by Chrome.
|
||||||
if (was_handled)
|
if (was_handled)
|
||||||
|
|
Loading…
Reference in a new issue