From d2368d2d3b3de9eec4cc32b6aaf035cc89921bf1 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Tue, 9 Sep 2014 19:11:54 +0800 Subject: [PATCH] Add Volume keys support in global-shortcut API, fix #630. --- .../extensions/global_shortcut_listener_mac.h | 20 ++--- .../global_shortcut_listener_mac.mm | 79 +++++++++++-------- 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.h b/chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.h index 8ee91a10da6e..5295a7195654 100644 --- a/chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.h +++ b/chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.h @@ -21,8 +21,8 @@ namespace extensions { // 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). +// 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 { @@ -38,7 +38,7 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener { // Keyboard event callbacks. void OnHotKeyEvent(EventHotKeyID hot_key_id); - bool OnMediaKeyEvent(int key_code); + bool OnMediaOrVolumeKeyEvent(int key_code); // GlobalShortcutListener implementation. virtual void StartListening() OVERRIDE; @@ -52,16 +52,16 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener { 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 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 keys are currently registered. - bool IsAnyMediaKeyRegistered(); + // Whether or not any media/volume keys are currently registered. + bool IsAnyMediaOrVolumeKeyRegistered(); // Whether or not any hot keys are currently registered. bool IsAnyHotKeyRegistered(); @@ -80,7 +80,7 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener { // 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 + // 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_; @@ -91,7 +91,7 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener { // Keyboard shortcut IDs to hotkeys map for unregistration. IdHotKeyRefMap id_hot_key_refs_; - // Event tap for intercepting mac media keys. + // Event tap for intercepting mac media/volume keys. CFMachPortRef event_tap_; CFRunLoopSourceRef event_tap_source_; diff --git a/chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.mm b/chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.mm index 2fce520b7670..80daf8712ab6 100644 --- a/chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.mm +++ b/chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.mm @@ -19,11 +19,11 @@ using extensions::GlobalShortcutListenerMac; 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 -const int kSystemDefinedEventMediaKeysSubtype = 8; +const int kSystemDefinedEventMediaAndVolumeKeysSubtype = 8; -ui::KeyboardCode MediaKeyCodeToKeyboardCode(int key_code) { +ui::KeyboardCode MediaOrVolumeKeyCodeToKeyboardCode(int key_code) { switch (key_code) { case NX_KEYTYPE_PLAY: return ui::VKEY_MEDIA_PLAY_PAUSE; @@ -33,17 +33,26 @@ 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; } -bool IsMediaKey(const ui::Accelerator& accelerator) { +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_MEDIA_STOP || + accelerator.key_code() == ui::VKEY_VOLUME_UP || + accelerator.key_code() == ui::VKEY_VOLUME_DOWN || + accelerator.key_code() == ui::VKEY_VOLUME_MUTE); } } // namespace @@ -77,8 +86,8 @@ GlobalShortcutListenerMac::~GlobalShortcutListenerMac() { // If keys are still registered, make sure we stop the tap. Again, this // should never happen. - if (IsAnyMediaKeyRegistered()) - StopWatchingMediaKeys(); + if (IsAnyMediaOrVolumeKeyRegistered()) + StopWatchingMediaOrVolumeKeys(); if (IsAnyHotKeyRegistered()) StopWatchingHotKeys(); @@ -114,9 +123,11 @@ void GlobalShortcutListenerMac::OnHotKeyEvent(EventHotKeyID hot_key_id) { NotifyKeyPressed(accelerator); } -bool GlobalShortcutListenerMac::OnMediaKeyEvent(int media_key_code) { +bool GlobalShortcutListenerMac::OnMediaOrVolumeKeyEvent( + int media_or_volume_key_code) { 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. ui::Accelerator accelerator(key_code, 0); // Look for a match with a bound hot_key. @@ -133,10 +144,10 @@ bool GlobalShortcutListenerMac::RegisterAcceleratorImpl( 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(); + 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. @@ -161,7 +172,7 @@ void GlobalShortcutListenerMac::UnregisterAcceleratorImpl( DCHECK(accelerator_ids_.find(accelerator) != accelerator_ids_.end()); // Unregister the hot_key if it's a keyboard shortcut. - if (!IsMediaKey(accelerator)) + if (!IsMediaOrVolumeKey(accelerator)) UnregisterHotKey(accelerator); // Remove hot_key from the mappings. @@ -169,11 +180,11 @@ void GlobalShortcutListenerMac::UnregisterAcceleratorImpl( 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(); + 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. @@ -226,12 +237,12 @@ void GlobalShortcutListenerMac::UnregisterHotKey( id_hot_key_refs_.erase(key_id); } -void GlobalShortcutListenerMac::StartWatchingMediaKeys() { +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 key events. + // Add an event tap to intercept the system defined media/volume key events. event_tap_ = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, @@ -254,7 +265,7 @@ void GlobalShortcutListenerMac::StartWatchingMediaKeys() { kCFRunLoopCommonModes); } -void GlobalShortcutListenerMac::StopWatchingMediaKeys() { +void GlobalShortcutListenerMac::StopWatchingMediaOrVolumeKeys() { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), event_tap_source_, kCFRunLoopCommonModes); // Ensure both event tap and source are initialized. @@ -287,11 +298,11 @@ void GlobalShortcutListenerMac::StopWatchingHotKeys() { event_handler_ = NULL; } -bool GlobalShortcutListenerMac::IsAnyMediaKeyRegistered() { - // Iterate through registered accelerators, looking for media keys. +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 (IsMediaKey(it->first)) + if (IsMediaOrVolumeKey(it->first)) return true; } return false; @@ -300,7 +311,7 @@ bool GlobalShortcutListenerMac::IsAnyMediaKeyRegistered() { bool GlobalShortcutListenerMac::IsAnyHotKeyRegistered() { AcceleratorIdMap::iterator it; for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) { - if (!IsMediaKey(it->first)) + if (!IsMediaOrVolumeKey(it->first)) return true; } return false; @@ -329,20 +340,24 @@ CGEventRef GlobalShortcutListenerMac::EventTapCallback( 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 || [ns_event type] != NSSystemDefined || - [ns_event subtype] != kSystemDefinedEventMediaKeysSubtype) { + [ns_event subtype] != kSystemDefinedEventMediaAndVolumeKeysSubtype) { return event; } 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/ 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; } @@ -353,8 +368,8 @@ CGEventRef GlobalShortcutListenerMac::EventTapCallback( 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); + // 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)