blob: 318e06e11a3647f6d13c76995f0f104c3406a340 [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/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