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_win.h" 6 7#include "base/win/win_util.h" 8#include "content/public/browser/browser_thread.h" 9#include "ui/base/accelerators/accelerator.h" 10#include "ui/events/event_constants.h" 11#include "ui/events/keycodes/keyboard_code_conversion_win.h" 12 13using content::BrowserThread; 14 15namespace { 16 17static base::LazyInstance<extensions::GlobalShortcutListenerWin> instance = 18 LAZY_INSTANCE_INITIALIZER; 19 20} // namespace 21 22namespace extensions { 23 24// static 25GlobalShortcutListener* GlobalShortcutListener::GetInstance() { 26 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 27 return instance.Pointer(); 28} 29 30GlobalShortcutListenerWin::GlobalShortcutListenerWin() 31 : is_listening_(false) { 32 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 33} 34 35GlobalShortcutListenerWin::~GlobalShortcutListenerWin() { 36 if (is_listening_) 37 StopListening(); 38} 39 40void GlobalShortcutListenerWin::StartListening() { 41 DCHECK(!is_listening_); // Don't start twice. 42 DCHECK(!hotkey_ids_.empty()); // Also don't start if no hotkey is registered. 43 gfx::SingletonHwnd::GetInstance()->AddObserver(this); 44 is_listening_ = true; 45} 46 47void GlobalShortcutListenerWin::StopListening() { 48 DCHECK(is_listening_); // No point if we are not already listening. 49 DCHECK(hotkey_ids_.empty()); // Make sure the map is clean before ending. 50 gfx::SingletonHwnd::GetInstance()->RemoveObserver(this); 51 is_listening_ = false; 52} 53 54void GlobalShortcutListenerWin::OnWndProc(HWND hwnd, 55 UINT message, 56 WPARAM wparam, 57 LPARAM lparam) { 58 if (message != WM_HOTKEY) 59 return; 60 61 int key_code = HIWORD(lparam); 62 int modifiers = 0; 63 modifiers |= (LOWORD(lparam) & MOD_SHIFT) ? ui::EF_SHIFT_DOWN : 0; 64 modifiers |= (LOWORD(lparam) & MOD_ALT) ? ui::EF_ALT_DOWN : 0; 65 modifiers |= (LOWORD(lparam) & MOD_CONTROL) ? ui::EF_CONTROL_DOWN : 0; 66 ui::Accelerator accelerator( 67 ui::KeyboardCodeForWindowsKeyCode(key_code), modifiers); 68 69 instance.Get().NotifyKeyPressed(accelerator); 70} 71 72void GlobalShortcutListenerWin::RegisterAccelerator( 73 const ui::Accelerator& accelerator, 74 GlobalShortcutListener::Observer* observer) { 75 if (hotkey_ids_.find(accelerator) != hotkey_ids_.end()) { 76 // The shortcut has already been registered. Some shortcuts, such as 77 // MediaKeys can have multiple targets, all keyed off of the same 78 // accelerator. 79 return; 80 } 81 82 int modifiers = 0; 83 modifiers |= accelerator.IsShiftDown() ? MOD_SHIFT : 0; 84 modifiers |= accelerator.IsCtrlDown() ? MOD_CONTROL : 0; 85 modifiers |= accelerator.IsAltDown() ? MOD_ALT : 0; 86 static int hotkey_id = 0; 87 bool success = !!RegisterHotKey( 88 gfx::SingletonHwnd::GetInstance()->hwnd(), 89 hotkey_id, 90 modifiers, 91 accelerator.key_code()); 92 93 if (!success) { 94 // Most likely error: 1409 (Hotkey already registered). 95 LOG(ERROR) << "RegisterHotKey failed, error: " << GetLastError(); 96 return; 97 } 98 99 hotkey_ids_[accelerator] = hotkey_id++; 100 GlobalShortcutListener::RegisterAccelerator(accelerator, observer); 101} 102 103void GlobalShortcutListenerWin::UnregisterAccelerator( 104 const ui::Accelerator& accelerator, 105 GlobalShortcutListener::Observer* observer) { 106 // We may get asked to unregister something that we couldn't register (for 107 // example if the shortcut was already taken by another app), so we 108 // need to handle that gracefully. 109 HotkeyIdMap::iterator it = hotkey_ids_.find(accelerator); 110 if (it == hotkey_ids_.end()) 111 return; 112 113 bool success = !!UnregisterHotKey( 114 gfx::SingletonHwnd::GetInstance()->hwnd(), it->second); 115 // This call should always succeed, as long as we pass in the right HWND and 116 // an id we've used to register before. 117 DCHECK(success); 118 119 hotkey_ids_.erase(it); 120 GlobalShortcutListener::UnregisterAccelerator(accelerator, observer); 121} 122 123} // namespace extensions 124