| // 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/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" |
| |
| using content::BrowserThread; |
| |
| namespace { |
| |
| static base::LazyInstance<extensions::GlobalShortcutListenerWin> instance = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| } // namespace |
| |
| namespace extensions { |
| |
| // static |
| GlobalShortcutListener* GlobalShortcutListener::GetInstance() { |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| return instance.Pointer(); |
| } |
| |
| 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. |
| gfx::SingletonHwnd::GetInstance()->AddObserver(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. |
| gfx::SingletonHwnd::GetInstance()->RemoveObserver(this); |
| 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; |
| ui::Accelerator accelerator( |
| ui::KeyboardCodeForWindowsKeyCode(key_code), modifiers); |
| |
| instance.Get().NotifyKeyPressed(accelerator); |
| } |
| |
| void GlobalShortcutListenerWin::RegisterAccelerator( |
| const ui::Accelerator& accelerator, |
| GlobalShortcutListener::Observer* observer) { |
| if (hotkey_ids_.find(accelerator) != hotkey_ids_.end()) { |
| // The shortcut has already been registered. Some shortcuts, such as |
| // MediaKeys can have multiple targets, all keyed off of the same |
| // accelerator. |
| return; |
| } |
| |
| int modifiers = 0; |
| modifiers |= accelerator.IsShiftDown() ? MOD_SHIFT : 0; |
| modifiers |= accelerator.IsCtrlDown() ? MOD_CONTROL : 0; |
| modifiers |= accelerator.IsAltDown() ? MOD_ALT : 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). |
| LOG(ERROR) << "RegisterHotKey failed, error: " << GetLastError(); |
| return; |
| } |
| |
| hotkey_ids_[accelerator] = hotkey_id++; |
| GlobalShortcutListener::RegisterAccelerator(accelerator, observer); |
| } |
| |
| void GlobalShortcutListenerWin::UnregisterAccelerator( |
| const ui::Accelerator& accelerator, |
| GlobalShortcutListener::Observer* observer) { |
| // We may get asked to unregister something that we couldn't register (for |
| // example if the shortcut was already taken by another app), so we |
| // need to handle that gracefully. |
| HotkeyIdMap::iterator it = hotkey_ids_.find(accelerator); |
| if (it == hotkey_ids_.end()) |
| return; |
| |
| 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); |
| GlobalShortcutListener::UnregisterAccelerator(accelerator, observer); |
| } |
| |
| } // namespace extensions |