Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/extensions/global_shortcut_listener_mac.h" | |
| 6 | |
| 7 #import <Cocoa/Cocoa.h> | |
| 8 | |
| 9 #include "content/public/browser/browser_thread.h" | |
| 10 #include "ui/events/event.h" | |
| 11 #include "ui/base/accelerators/accelerator.h" | |
| 12 #import "ui/events/keycodes/keyboard_code_conversion_mac.h" | |
| 13 | |
| 14 #define SystemDefinedEventMediaKeys 8 | |
|
Robert Sesek
2013/11/18 15:25:03
Use type-safe C++ constants. Do not use #define fo
smus
2013/11/18 23:13:41
Done.
| |
| 15 #define EVENT_KEY @"event" | |
| 16 #define HANDLED_KEY @"handled" | |
| 17 | |
| 18 typedef extensions::GlobalShortcutListenerMac GSL; | |
|
Robert Sesek
2013/11/18 15:25:03
This is not an appropriate typedef. Use |using ext
smus
2013/11/18 23:13:41
Done.
| |
| 19 | |
| 20 @interface GlobalShortcutListenerTap : NSObject { | |
| 21 @public | |
|
Robert Sesek
2013/11/18 15:25:03
nit: one space before @public. But, I'm not sure y
smus
2013/11/18 23:13:41
Removed @public member.
| |
| 22 CFMachPortRef eventTap_; | |
| 23 | |
| 24 @private | |
| 25 CFRunLoopSourceRef eventTapSource_; | |
| 26 CFRunLoopRef tapThreadRunLoop_; | |
| 27 GSL* gsl_; | |
|
Robert Sesek
2013/11/18 15:25:03
Similarly, this is not an appropriate variable nam
smus
2013/11/18 23:13:41
Done.
| |
| 28 } | |
| 29 | |
| 30 - (id)initWithGSL:(GSL*)gsl; | |
| 31 - (void)startWatchingMediaKeys; | |
| 32 - (void)stopWatchingMediaKeys; | |
| 33 - (void)handleMediaKeyEvent:(NSEvent*)event; | |
| 34 - (BOOL)performEventHandlerOnMainThread:(SEL)selector withEvent:(NSEvent*)event; | |
| 35 @end | |
| 36 | |
| 37 // Processed events should propagate if they aren't handled by any listeners. | |
| 38 // Returning event causes the event to propagate to other applications. | |
| 39 // Returning NULL prevents the event from propagating. | |
| 40 static CGEventRef tapEventCallback( | |
|
Robert Sesek
2013/11/18 15:25:03
naming: TapEventCallback
You mix and match anonym
smus
2013/11/18 23:13:41
Done.
| |
| 41 CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon) { | |
| 42 NSAutoreleasePool* pool = [NSAutoreleasePool new]; | |
| 43 CGEventRef ret = event; | |
|
Robert Sesek
2013/11/18 15:25:03
Naming: |ret|?
smus
2013/11/18 23:13:41
Done.
| |
| 44 | |
| 45 GlobalShortcutListenerTap* self = (GlobalShortcutListenerTap*) refcon; | |
|
Robert Sesek
2013/11/18 15:25:03
C-style casts are banned.
smus
2013/11/18 23:13:41
Changed to static_cast.
| |
| 46 | |
| 47 // Handle the timeout case by re-enabling the tap. | |
| 48 if (type == kCGEventTapDisabledByTimeout) { | |
| 49 LOG(ERROR) << "Event tap was disabled by a timeout."; | |
| 50 CGEventTapEnable(self->eventTap_, TRUE); | |
| 51 // Release the event as soon as possible. | |
| 52 return ret; | |
| 53 } | |
| 54 | |
| 55 // TODO(smus): do some error handling since eventWithCGEvent can fail. | |
| 56 NSEvent* nsEvent = [NSEvent eventWithCGEvent:event]; | |
|
Robert Sesek
2013/11/18 15:25:03
Why do you need to convert this to a NSEvent? Can'
Robert Sesek
2013/11/18 15:25:03
Naming: in C/C++, use under_scores for variables.
smus
2013/11/18 23:13:41
I looked for a way of doing this from the CGEvent,
smus
2013/11/18 23:13:41
Done.
Robert Sesek
2013/11/19 18:34:58
int64_t subtype = CGEventGetIntegerValueField(even
smus
2013/11/20 04:28:51
Not sure I follow. Are you suggesting that CGEvent
Robert Sesek
2013/11/20 16:23:57
What about kCGEventSourceUserData?
smus
2013/11/20 17:50:53
No, unfortunately that field is always zero.
in
| |
| 57 | |
| 58 // Handle media keys (PlayPause, NextTrack, PreviousTrack). | |
| 59 if (type != NX_SYSDEFINED || | |
| 60 [nsEvent subtype] != SystemDefinedEventMediaKeys) { | |
| 61 int keyCode = (([nsEvent data1] & 0xFFFF0000) >> 16); | |
| 62 if (keyCode != NX_KEYTYPE_PLAY && keyCode != NX_KEYTYPE_NEXT && | |
| 63 keyCode != NX_KEYTYPE_PREVIOUS && keyCode != NX_KEYTYPE_FAST && | |
| 64 keyCode != NX_KEYTYPE_REWIND) { | |
| 65 // Release the event as soon as possible. | |
| 66 return ret; | |
| 67 } | |
| 68 } | |
| 69 | |
| 70 // If we got here, we are dealing with a real media key event. | |
| 71 BOOL wasHandled = [self | |
| 72 performEventHandlerOnMainThread:@selector(handleMediaKeyEvent:) | |
| 73 withEvent:nsEvent]; | |
|
Finnur
2013/11/18 11:55:57
As I recall, from my brief exposure to Mac, the co
smus
2013/11/18 23:13:41
Couldn't confirm that from http://google-styleguid
| |
| 74 // Prevent the event from proagating to other mac applications if it was | |
| 75 // handled by Chrome. | |
| 76 if (wasHandled) { | |
| 77 ret = NULL; | |
| 78 } | |
|
Finnur
2013/11/18 11:55:57
Yup, you guessed it (single-line if, no braces) :)
smus
2013/11/18 23:13:41
Done.
| |
| 79 | |
| 80 [pool drain]; | |
| 81 // By default, pass the event through. | |
| 82 return ret; | |
| 83 } | |
| 84 | |
| 85 @implementation GlobalShortcutListenerTap | |
| 86 | |
| 87 - (id)initWithGSL:(GSL*)gsl { | |
| 88 gsl_ = gsl; | |
|
Robert Sesek
2013/11/18 15:25:03
if ((self = [super init])) {
shortcutListener_ =
smus
2013/11/18 23:13:41
Done.
| |
| 89 return self; | |
| 90 } | |
| 91 | |
| 92 - (void)eventTapThread { | |
|
Robert Sesek
2013/11/18 15:25:03
Yikes – an entire thread just to listen for global
smus
2013/11/18 23:13:41
I think this is the best practice. Doing this on t
Robert Sesek
2013/11/19 18:34:58
What are you worried about blocking? You already h
smus
2013/11/20 04:28:51
Good point. Initially the thread was not synchroni
Robert Sesek
2013/11/20 16:23:57
Rebroadcasting the event would post it out-of-orde
smus
2013/11/20 17:50:53
Okay, I've gone this route. I had a (big!) bug bef
| |
| 93 tapThreadRunLoop_ = CFRunLoopGetCurrent(); | |
| 94 CFRunLoopAddSource(tapThreadRunLoop_, eventTapSource_, | |
| 95 kCFRunLoopCommonModes); | |
| 96 CFRunLoopRun(); | |
| 97 } | |
| 98 | |
| 99 - (BOOL)performEventHandlerOnMainThread:(SEL)selector | |
| 100 withEvent:(NSEvent*)event { | |
| 101 NSMutableDictionary* dict = [[NSMutableDictionary alloc] init]; | |
| 102 [dict setObject:event forKey:EVENT_KEY]; | |
| 103 [self performSelectorOnMainThread:selector | |
| 104 withObject:dict waitUntilDone:YES]; | |
| 105 // Keep track of the result from the main thread to know if the event has | |
| 106 // been handled. | |
| 107 BOOL wasHandled = [[dict objectForKey:HANDLED_KEY] boolValue]; | |
| 108 [dict release]; | |
| 109 return wasHandled; | |
| 110 } | |
| 111 | |
| 112 - (ui::KeyboardCode)mediaKeyCodeToKeyboardCode:(int)keyCode { | |
| 113 switch (keyCode) { | |
| 114 case NX_KEYTYPE_PLAY: | |
| 115 return ui::VKEY_MEDIA_PLAY_PAUSE; | |
| 116 case NX_KEYTYPE_PREVIOUS: | |
| 117 case NX_KEYTYPE_REWIND: | |
| 118 return ui::VKEY_MEDIA_PREV_TRACK; | |
| 119 case NX_KEYTYPE_NEXT: | |
| 120 case NX_KEYTYPE_FAST: | |
| 121 return ui::VKEY_MEDIA_NEXT_TRACK; | |
|
Finnur
2013/11/18 11:55:57
Is there no 'Stop' event?
For example, see Chaobi
smus
2013/11/18 23:13:41
Mac keyboards don't have this button.
| |
| 122 } | |
| 123 return ui::VKEY_UNKNOWN; | |
| 124 } | |
| 125 | |
| 126 - (void)startWatchingMediaKeys { | |
| 127 // Make sure there's no existing event tap. | |
| 128 assert(eventTap_ == NULL); | |
|
Robert Sesek
2013/11/18 15:25:03
No assert(). Use Chromium's logging facilities for
smus
2013/11/18 23:13:41
Done.
| |
| 129 | |
| 130 // Add an event tap to intercept the system defined media key events. | |
| 131 eventTap_ = CGEventTapCreate(kCGSessionEventTap, | |
|
Robert Sesek
2013/11/18 15:25:03
Per https://developer.apple.com/library/mac/docume
Robert Sesek
2013/11/18 18:36:20
Sorry, this isn't accurate. This code isn't tappin
smus
2013/11/18 23:13:41
Self-addressed.
smus
2013/11/18 23:13:41
Done.
| |
| 132 kCGHeadInsertEventTap, | |
| 133 kCGEventTapOptionDefault, | |
| 134 CGEventMaskBit(NX_SYSDEFINED), | |
| 135 tapEventCallback, | |
| 136 self); | |
| 137 assert(eventTap_ != NULL); | |
| 138 | |
| 139 eventTapSource_ = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, | |
| 140 eventTap_, 0); | |
| 141 assert(eventTapSource_ != NULL); | |
| 142 | |
| 143 // Run the event tap in separate thread to prevent blocking UI. | |
| 144 [NSThread detachNewThreadSelector:@selector(eventTapThread) | |
| 145 toTarget:self withObject:nil]; | |
| 146 } | |
| 147 | |
| 148 - (void)stopWatchingMediaKeys { | |
| 149 if (tapThreadRunLoop_) { | |
| 150 CFRunLoopStop(tapThreadRunLoop_); | |
| 151 tapThreadRunLoop_ = nil; | |
| 152 } | |
| 153 | |
| 154 if (eventTap_) { | |
| 155 CFMachPortInvalidate(eventTap_); | |
| 156 CFRelease(eventTap_); | |
| 157 eventTap_ = nil; | |
| 158 } | |
| 159 | |
| 160 if (eventTapSource_) { | |
| 161 CFRelease(eventTapSource_); | |
| 162 eventTapSource_ = nil; | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 // Event will have been retained in the other thread. | |
| 167 - (void)handleMediaKeyEvent:(NSMutableDictionary*)dict { | |
| 168 NSEvent* event = [dict objectForKey:EVENT_KEY]; | |
| 169 | |
| 170 int keyCode = (([event data1] & 0xFFFF0000) >> 16); | |
| 171 int keyFlags = ([event data1] & 0x0000FFFF); | |
| 172 BOOL keyIsPressed = (((keyFlags & 0xFF00) >> 8)) == 0xA; | |
| 173 | |
| 174 bool result = false; | |
| 175 if (keyIsPressed) | |
| 176 result = gsl_->OnMediaKeyEvent([self mediaKeyCodeToKeyboardCode:keyCode]); | |
| 177 | |
| 178 [dict setObject:[NSNumber numberWithBool:result] forKey:HANDLED_KEY]; | |
| 179 } | |
| 180 | |
| 181 @end | |
| 182 | |
| 183 using content::BrowserThread; | |
|
Robert Sesek
2013/11/18 15:25:03
This belongs after the #includes, not here.
smus
2013/11/18 23:13:41
Done.
| |
| 184 | |
| 185 namespace { | |
| 186 | |
| 187 static base::LazyInstance<extensions::GlobalShortcutListenerMac> instance = | |
|
Robert Sesek
2013/11/18 15:25:03
Naming: g_instance
smus
2013/11/18 23:13:41
Done.
| |
| 188 LAZY_INSTANCE_INITIALIZER; | |
| 189 | |
| 190 } // namespace | |
| 191 | |
| 192 namespace extensions { | |
| 193 | |
| 194 // static | |
| 195 GlobalShortcutListener* GlobalShortcutListener::GetInstance() { | |
| 196 LOG(ERROR) << "GlobalShortcutListener GetInstance"; | |
| 197 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 198 return instance.Pointer(); | |
| 199 } | |
| 200 | |
| 201 GlobalShortcutListenerMac::GlobalShortcutListenerMac() | |
| 202 : is_listening_(false) { | |
| 203 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 204 | |
| 205 // TODO(implementor): Remove this. | |
| 206 LOG(ERROR) << "GlobalShortcutListenerMac object created"; | |
|
Finnur
2013/11/18 11:55:57
You can remove this and other LOG(ERROR) statement
smus
2013/11/18 23:13:41
Done.
| |
| 207 | |
| 208 tap_.reset([[GlobalShortcutListenerTap alloc] initWithGSL:this]); | |
| 209 } | |
| 210 | |
| 211 GlobalShortcutListenerMac::~GlobalShortcutListenerMac() { | |
|
Robert Sesek
2013/11/18 15:25:03
What about all the registered hotkeys? Won't those
smus
2013/11/18 23:13:41
Following other implementations (X11 and Win). I t
Finnur
2013/11/19 13:10:05
Yes, we'll get calls to Unregister before the obje
| |
| 212 if (is_listening_) | |
| 213 StopListening(); | |
| 214 } | |
| 215 | |
| 216 void GlobalShortcutListenerMac::StartListening() { | |
|
Robert Sesek
2013/11/18 15:25:03
The Start/StopListeneing pair of methods only affe
smus
2013/11/18 23:13:41
Added clarifying comment.
Finnur
2013/11/19 13:10:05
So, this Start/StopListening pair is meant as a co
smus
2013/11/20 04:28:51
Ok, made this change.
| |
| 217 DCHECK(!is_listening_); // Don't start twice. | |
| 218 DCHECK(!hotkey_ids_.empty()); // Don't start if no hotkey registered. | |
| 219 DCHECK(!id_hotkeys_.empty()); | |
| 220 DCHECK(!id_hotkey_refs_.empty()); | |
| 221 LOG(ERROR) << "GlobalShortcutListenerMac StartListening"; | |
| 222 is_listening_ = true; | |
| 223 | |
| 224 [tap_ startWatchingMediaKeys]; | |
| 225 } | |
| 226 | |
| 227 void GlobalShortcutListenerMac::StopListening() { | |
| 228 DCHECK(is_listening_); // No point if we are not already listening. | |
| 229 DCHECK(hotkey_ids_.empty()); // Make sure the set is clean. | |
| 230 DCHECK(id_hotkeys_.empty()); | |
| 231 DCHECK(id_hotkey_refs_.empty()); | |
| 232 LOG(ERROR) << "GlobalShortcutListenerMac StopListening"; | |
| 233 is_listening_ = false; | |
| 234 | |
| 235 [tap_ stopWatchingMediaKeys]; | |
| 236 } | |
| 237 | |
| 238 void GlobalShortcutListenerMac::RegisterAccelerator( | |
| 239 const ui::Accelerator& accelerator, | |
| 240 GlobalShortcutListener::Observer* observer) { | |
| 241 LOG(ERROR) << "GlobalShortcutListenerMac RegisterAccelerator"; | |
| 242 VLOG(0) << "Registered keyCode: " << accelerator.key_code() | |
| 243 << ", modifiers: " << accelerator.modifiers(); | |
| 244 // To implement: | |
| 245 // 1) Convert modifiers to platform specific modifiers. | |
| 246 bool isMediaKey = (accelerator.modifiers() == 0); | |
| 247 VLOG(0) << "isMediaKey: " << isMediaKey; | |
| 248 if (!isMediaKey) { | |
| 249 RegisterHotKey(accelerator); | |
| 250 } | |
|
Finnur
2013/11/18 11:55:57
nit: Single line. :)
smus
2013/11/18 23:13:41
Done.
| |
| 251 // 2) Register for the hotkey. | |
| 252 // 3) If not successful, log why. | |
| 253 // 4) Else, call base class RegisterAccelerator. | |
|
Finnur
2013/11/18 11:55:57
You can remove these comments, they were only mean
smus
2013/11/18 23:13:41
Done.
| |
| 254 id_hotkeys_[hotkey_id] = accelerator; | |
| 255 hotkey_ids_[accelerator] = hotkey_id; | |
| 256 hotkey_id += 1; | |
| 257 GlobalShortcutListener::RegisterAccelerator(accelerator, observer); | |
| 258 } | |
| 259 | |
| 260 void GlobalShortcutListenerMac::UnregisterAccelerator( | |
| 261 const ui::Accelerator& accelerator, | |
| 262 GlobalShortcutListener::Observer* observer) { | |
| 263 LOG(ERROR) << "GlobalShortcutListenerMac UnregisterAccelerator"; | |
| 264 // To implement: | |
| 265 // 1) Unregister for the hotkey. | |
| 266 // 2) Call base class UnregisterAccelerator. | |
|
Finnur
2013/11/18 11:55:57
Same here.
smus
2013/11/18 23:13:41
Done.
| |
| 267 bool isMediaKey = (accelerator.modifiers() == 0); | |
|
Finnur
2013/11/18 11:55:57
Chaobin is adding a static IsMediaKey function to
smus
2013/11/18 23:13:41
Oh excellent, I added my own for now.
| |
| 268 VLOG(0) << "isMediaKey: " << isMediaKey; | |
| 269 if (!isMediaKey) { | |
| 270 UnregisterHotKey(accelerator); | |
| 271 } | |
|
Finnur
2013/11/18 11:55:57
nit: Single line, your favorite. :)
smus
2013/11/18 23:13:41
Done.
| |
| 272 | |
| 273 int id = hotkey_ids_[accelerator]; | |
| 274 id_hotkeys_.erase(id); | |
| 275 hotkey_ids_.erase(accelerator); | |
| 276 GlobalShortcutListener::UnregisterAccelerator(accelerator, observer); | |
| 277 } | |
| 278 | |
| 279 bool GlobalShortcutListenerMac::OnKeyEvent(EventHotKeyID hotKeyID) { | |
| 280 // Look up the accelerator based on this hot key ID. | |
| 281 VLOG(0) << "OnKeyEvent! hotKeyID: " << hotKeyID.id; | |
| 282 ui::Accelerator accelerator = id_hotkeys_[hotKeyID.id]; | |
| 283 VLOG(0) << "Key code: " << accelerator.key_code() << | |
| 284 " modifiers: " << accelerator.modifiers(); | |
| 285 instance.Get().NotifyKeyPressed(accelerator); | |
|
Robert Sesek
2013/11/18 15:25:03
Why are you not using the |this| pointer? This met
smus
2013/11/18 23:13:41
Fair point (I think). If so, same should apply to
Finnur
2013/11/19 13:10:05
Yeah, looks like.
On 2013/11/18 23:13:41, smus wr
| |
| 286 return true; | |
| 287 } | |
| 288 | |
| 289 // Returns true iff event was handled. | |
| 290 bool GlobalShortcutListenerMac::OnMediaKeyEvent(ui::KeyboardCode keyCode) { | |
| 291 VLOG(0) << "OnMediaKeyEvent! keyCode: " << keyCode; | |
| 292 // Create an accelerator corresponding to the keyCode. | |
| 293 ui::Accelerator accelerator(keyCode, 0); | |
| 294 // Look for a match with a bound hotkey. | |
| 295 if (hotkey_ids_.find(accelerator) != hotkey_ids_.end()) { | |
| 296 // If matched, callback to the event handling system. | |
| 297 instance.Get().NotifyKeyPressed(accelerator); | |
| 298 return true; | |
| 299 } | |
| 300 return false; | |
| 301 } | |
| 302 | |
| 303 OSStatus HotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, | |
|
Robert Sesek
2013/11/18 15:25:03
This doesn't belong here. This belongs with your o
Robert Sesek
2013/11/18 15:25:03
naming: You use HotKey here but Hotkey elsewhere.
smus
2013/11/18 23:13:41
Done.
smus
2013/11/18 23:13:41
Done.
| |
| 304 void *userData) { | |
| 305 VLOG(0) << "HotKeyHandler fired with event: " << theEvent; | |
| 306 // Extract the hotkey from the event. | |
| 307 EventHotKeyID hotKeyID; | |
| 308 int result = GetEventParameter(theEvent, kEventParamDirectObject, | |
| 309 typeEventHotKeyID, NULL, sizeof(hotKeyID), NULL, &hotKeyID); | |
| 310 assert(result == noErr); | |
| 311 | |
| 312 // Callback to the parent class. | |
| 313 GlobalShortcutListenerMac* gsl = (GlobalShortcutListenerMac*) userData; | |
| 314 gsl->OnKeyEvent(hotKeyID); | |
| 315 return noErr; | |
| 316 } | |
| 317 | |
| 318 void GlobalShortcutListenerMac::RegisterHotKey(ui::Accelerator accelerator) { | |
| 319 VLOG(0) << "Registering hotkey. Windows keycode: " << accelerator.key_code(); | |
| 320 EventHotKeyRef hotKeyRef; | |
| 321 EventHotKeyID hotKeyID; | |
| 322 EventHandlerUPP hotKeyFunction = NewEventHandlerUPP(HotKeyHandler); | |
| 323 | |
| 324 EventTypeSpec eventType; | |
| 325 eventType.eventClass = kEventClassKeyboard; | |
| 326 eventType.eventKind = kEventHotKeyPressed; | |
| 327 InstallApplicationEventHandler(hotKeyFunction, 1, &eventType, this, NULL); | |
| 328 | |
| 329 // TODO: Understand what signature means. | |
|
Robert Sesek
2013/11/18 15:25:03
I think this is important to understand before com
smus
2013/11/18 23:13:41
Agreed. Added a comment explaining what it is.
| |
| 330 hotKeyID.signature = 31337; | |
| 331 hotKeyID.id = hotkey_id; | |
| 332 | |
| 333 // Translate ui::Accelerator modifiers to cmdKey, altKey, etc. | |
| 334 int modifiers = 0; | |
| 335 modifiers += (accelerator.IsShiftDown() ? shiftKey : 0); | |
| 336 modifiers += (accelerator.IsCtrlDown() ? controlKey : 0); | |
| 337 modifiers += (accelerator.IsAltDown() ? optionKey : 0); | |
| 338 modifiers += (accelerator.IsCmdDown() ? cmdKey : 0); | |
| 339 | |
| 340 unichar character; | |
| 341 unichar characterIgnoringModifiers; | |
| 342 int keyCode = ui::MacKeyCodeForWindowsKeyCode(accelerator.key_code(), 0, | |
| 343 &character, &characterIgnoringModifiers); | |
| 344 VLOG(0) << "RegisterHotKey. Code: " << keyCode << " modifier: " << modifiers; | |
| 345 | |
| 346 // TODO: Move away from hardcoded CMD + Enter event. | |
|
Finnur
2013/11/18 11:55:57
What does this mean?
smus
2013/11/18 23:13:41
Stale comment. Removed.
| |
| 347 RegisterEventHotKey(keyCode, modifiers, hotKeyID, | |
| 348 GetApplicationEventTarget(), 0, &hotKeyRef); | |
| 349 | |
| 350 id_hotkey_refs_[hotkey_id] = hotKeyRef; | |
|
Finnur
2013/11/18 11:55:57
Don't you need to advance hotkey_id now?
smus
2013/11/18 23:13:41
Added a comment explaining (it happens in the call
| |
| 351 } | |
| 352 | |
| 353 void GlobalShortcutListenerMac::UnregisterHotKey(ui::Accelerator accelerator) { | |
| 354 // Get the ref corresponding to this accelerator. | |
| 355 int id = hotkey_ids_[accelerator]; | |
| 356 EventHotKeyRef ref = id_hotkey_refs_[id]; | |
| 357 // Unregister the event hot key. | |
| 358 UnregisterEventHotKey(ref); | |
| 359 | |
| 360 // Remove the event from the mapping. | |
| 361 id_hotkey_refs_.erase(id); | |
| 362 } | |
| 363 | |
| 364 | |
|
Finnur
2013/11/18 11:55:57
nit: Extra line break.
smus
2013/11/18 23:13:41
Done.
| |
| 365 } // namespace extensions | |
| OLD | NEW |