| Index: ui/views/widget/desktop_aura/desktop_keyboard_capture_win.cc
|
| diff --git a/ui/views/widget/desktop_aura/desktop_keyboard_capture_win.cc b/ui/views/widget/desktop_aura/desktop_keyboard_capture_win.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3ee80479b07cb7d3f87222b03604ebf04f63a141
|
| --- /dev/null
|
| +++ b/ui/views/widget/desktop_aura/desktop_keyboard_capture_win.cc
|
| @@ -0,0 +1,238 @@
|
| +// Copyright (c) 2014 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 "ui/views/widget/desktop_aura/desktop_keyboard_capture_win.h"
|
| +
|
| +#include <map>
|
| +
|
| +#include "base/containers/scoped_ptr_hash_map.h"
|
| +#include "base/logging.h"
|
| +#include "base/macros.h"
|
| +#include "base/memory/singleton.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +
|
| +namespace {
|
| +// Some helper routines used to construct keyboard event.
|
| +
|
| +// Return true of WPARAM corresponds to a UP keyboard event.
|
| +bool IsKeyUp(WPARAM w_param) {
|
| + return (w_param == WM_KEYUP) || (w_param == WM_SYSKEYUP);
|
| +}
|
| +
|
| +// Check if the given bit is set.
|
| +bool IsBitSet(ULONG value, ULONG mask) {
|
| + return ((value & mask) != 0);
|
| +}
|
| +
|
| +// Construct LPARAM corresponding to the given low level hook callback
|
| +// structure.
|
| +LPARAM GetLParamFromHookStruct(WPARAM w_param, KBDLLHOOKSTRUCT hook_struct) {
|
| + ULONG key_state = 0;
|
| + // There is no way to get repeat count so always set it to 1.
|
| + key_state = 1;
|
| +
|
| + // Scan code.
|
| + key_state |= (hook_struct.scanCode & 0xFF) << 16;
|
| +
|
| + // Extended key when the event is received as part window event and so skip
|
| + // it.
|
| +
|
| + // Context code.
|
| + key_state |= IsBitSet(hook_struct.flags, LLKHF_ALTDOWN) << 29;
|
| +
|
| + // Previous key state - set to 1 for KEYUP events.
|
| + key_state |= IsKeyUp(w_param) << 30;
|
| +
|
| + // Transition state.
|
| + key_state |= IsBitSet(hook_struct.flags, LLKHF_UP) << 31;
|
| + return static_cast<LPARAM>(key_state);
|
| +}
|
| +
|
| +// Return the location independent keycode corresponding to given keycode (e.g.
|
| +// return shift when left/right shift is pressed). This is needed as low level
|
| +// hooks get location information which is not returned as part of normal window
|
| +// keyboard events.
|
| +DWORD RemoveLocationOnKeycode(DWORD vk_code) {
|
| + // Virtual keycode from low level hook include location while window messages
|
| + // does not. So convert them to be without location.
|
| + switch (vk_code) {
|
| + case VK_LSHIFT:
|
| + case VK_RSHIFT:
|
| + return VK_SHIFT;
|
| + case VK_LCONTROL:
|
| + case VK_RCONTROL:
|
| + return VK_CONTROL;
|
| + case VK_LMENU:
|
| + case VK_RMENU:
|
| + return VK_MENU;
|
| + }
|
| + return vk_code;
|
| +}
|
| +
|
| +// Update thread keyboard state so GetKeyboardStatus() works correctly. Thread
|
| +// keyboard state is not updated when low level hook is used to intercept
|
| +// keyboard events.
|
| +bool UpdateThreadKeyboardState() {
|
| + const int kKeyboardStateLength = 256;
|
| + BYTE keyboard_state[kKeyboardStateLength];
|
| + if (!GetKeyboardState(keyboard_state)) {
|
| + DVLOG(ERROR) << "Error getting keyboard state";
|
| + return false;
|
| + }
|
| +
|
| + int keys_to_update[] = {VK_SHIFT, VK_CONTROL, VK_MENU};
|
| + for (int index = 0; index < arraysize(keys_to_update); index++) {
|
| + int key = keys_to_update[index];
|
| + SHORT key_state = GetAsyncKeyState(key);
|
| + keyboard_state[key] = (IsBitSet(key_state, 0x8000) ? 0x80 : 0) |
|
| + (IsBitSet(key_state, 0x1) ? 1 : 0);
|
| + }
|
| +
|
| + if (!SetKeyboardState(keyboard_state)) {
|
| + DVLOG(ERROR) << "Error setting keyboard state";
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +}
|
| +
|
| +namespace views {
|
| +
|
| +// Maintains low level registration for a window.
|
| +class KeyboardInterceptRegistration {
|
| + public:
|
| + KeyboardInterceptRegistration() : hook_handle_(NULL) {}
|
| +
|
| + ~KeyboardInterceptRegistration() {
|
| + if (hook_handle_ != NULL)
|
| + Unhook();
|
| + }
|
| +
|
| + // Register for low level hook.
|
| + bool Hook(HOOKPROC callback_function) {
|
| + // Make sure that hook is set from main thread as it has to be valid for
|
| + // the lifetime of the registration.
|
| + DCHECK(base::MessageLoopForUI::IsCurrent());
|
| + DCHECK(hook_handle_ == NULL) << "Keyboard hook already registered";
|
| + hook_handle_ = SetWindowsHookEx(WH_KEYBOARD_LL, callback_function, NULL, 0);
|
| + if (hook_handle_ == NULL) {
|
| + DVLOG(ERROR) << "Error calling SetWindowsHookEx() - GLE = "
|
| + << GetLastError();
|
| + return false;
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + // Unhook registered hook.
|
| + bool Unhook() {
|
| + DCHECK(hook_handle_ != NULL) << "Unhook called without registring hooks";
|
| + BOOL result = UnhookWindowsHookEx(hook_handle_);
|
| + if (!result) {
|
| + DVLOG(ERROR) << "Error calling UnhookWindowsHookEx() - GLE = "
|
| + << GetLastError();
|
| + return false;
|
| + }
|
| + hook_handle_ = NULL;
|
| + return true;
|
| + }
|
| +
|
| + private:
|
| + // Hook returned when it was installed.
|
| + HHOOK hook_handle_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(KeyboardInterceptRegistration);
|
| +};
|
| +
|
| +// Implements low level hook and manages registration for all the windows.
|
| +class LowLevelHookHandler {
|
| + public:
|
| + // Request all keyboard events to be routed to the given window.
|
| + bool Register(HWND window_handle);
|
| +
|
| + // Release the request for all keyboard events.
|
| + void Deregister(HWND window_handle);
|
| +
|
| + // Get singleton instance.
|
| + static LowLevelHookHandler* GetInstance();
|
| +
|
| + private:
|
| + // Private constructor/destructor so it is accessible only
|
| + // DefaultSingletonTraits.
|
| + friend struct DefaultSingletonTraits<LowLevelHookHandler>;
|
| + LowLevelHookHandler() {}
|
| + ~LowLevelHookHandler() {}
|
| +
|
| + // Check if window handle is registered to intercept keyboard events.
|
| + bool IsRegistered(HWND handle);
|
| +
|
| + // Low level keyboard hook processing related functions.
|
| + // Hook callback called from the OS.
|
| + static LRESULT CALLBACK
|
| + KeyboardHook(int code, WPARAM w_param, LPARAM l_param);
|
| +
|
| + // There is no lock protecting this list as the low level hook callbacks are
|
| + // executed on same thread that registered the hook and there is only one
|
| + // thread
|
| + // that execute all view code in browser.
|
| + base::ScopedPtrHashMap<HWND, KeyboardInterceptRegistration> registrations_;
|
| +};
|
| +
|
| +// static
|
| +LowLevelHookHandler* LowLevelHookHandler::GetInstance() {
|
| + return Singleton<LowLevelHookHandler,
|
| + DefaultSingletonTraits<LowLevelHookHandler> >::get();
|
| +}
|
| +
|
| +bool LowLevelHookHandler::Register(HWND window_handle) {
|
| + if (registrations_.contains(window_handle))
|
| + return false;
|
| +
|
| + scoped_ptr<KeyboardInterceptRegistration> registration(
|
| + new KeyboardInterceptRegistration());
|
| + if (registration->Hook(KeyboardHook)) {
|
| + if (registrations_.add(window_handle, registration.Pass()).second)
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +void LowLevelHookHandler::Deregister(HWND window_handle) {
|
| + registrations_.erase(window_handle);
|
| + DVLOG(1) << "Keyboard hook unregistered for handle = " << window_handle;
|
| +}
|
| +
|
| +bool LowLevelHookHandler::IsRegistered(HWND handle) {
|
| + return registrations_.contains(handle);
|
| +}
|
| +
|
| +// static
|
| +LRESULT CALLBACK
|
| +LowLevelHookHandler::KeyboardHook(int code, WPARAM w_param, LPARAM l_param) {
|
| + HWND current_active_window = GetActiveWindow();
|
| + if ((code >= 0) && GetInstance()->IsRegistered(current_active_window)) {
|
| + UpdateThreadKeyboardState();
|
| +
|
| + KBDLLHOOKSTRUCT hook_struct = *reinterpret_cast<KBDLLHOOKSTRUCT*>(l_param);
|
| + PostMessage(current_active_window,
|
| + w_param,
|
| + RemoveLocationOnKeycode(hook_struct.vkCode),
|
| + GetLParamFromHookStruct(w_param, hook_struct));
|
| +
|
| + return 1;
|
| + }
|
| +
|
| + return CallNextHookEx(NULL, code, w_param, l_param);
|
| +}
|
| +
|
| +DesktopKeyboardCaptureWin::DesktopKeyboardCaptureWin(HWND window_handle)
|
| + : window_handle_(window_handle) {
|
| + LowLevelHookHandler::GetInstance()->Register(window_handle_);
|
| +}
|
| +
|
| +DesktopKeyboardCaptureWin::~DesktopKeyboardCaptureWin() {
|
| + LowLevelHookHandler::GetInstance()->Deregister(window_handle_);
|
| +}
|
| +
|
| +} // namespace views
|
|
|