blob: 65b244d55441b07f55e61c7b634d6cec57f2b1e7 [file] [log] [blame]
// 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/bind.h"
#include "base/bind_helpers.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"
#include "ui/gfx/win/singleton_hwnd.h"
using content::BrowserThread;
namespace extensions {
// static
GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
static GlobalShortcutListenerWin* instance =
new GlobalShortcutListenerWin();
return instance;
}
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.
singleton_hwnd_observer_.reset(new gfx::SingletonHwndObserver(
base::Bind(
&GlobalShortcutListenerWin::OnWndProc, base::Unretained(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.
singleton_hwnd_observer_.reset(nullptr);
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);
NotifyKeyPressed(accelerator);
}
bool GlobalShortcutListenerWin::RegisterAcceleratorImpl(
const ui::Accelerator& accelerator) {
DCHECK(hotkey_ids_.find(accelerator) == hotkey_ids_.end());
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).
return false;
}
hotkey_ids_[accelerator] = hotkey_id++;
return true;
}
void GlobalShortcutListenerWin::UnregisterAcceleratorImpl(
const ui::Accelerator& accelerator) {
HotkeyIdMap::iterator it = hotkey_ids_.find(accelerator);
DCHECK(it != hotkey_ids_.end());
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);
}
} // namespace extensions