Chromium Code Reviews| 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..8e3f6913cc32e0b2af382319a896587a89297886 |
| --- /dev/null |
| +++ b/ui/views/widget/desktop_aura/desktop_keyboard_capture_win.cc |
| @@ -0,0 +1,207 @@ |
| +// Copyright (c) 2012 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 "base/bind.h" |
| +#include "base/location.h" |
| +#include "base/logging.h" |
| +#include "base/synchronization/lock.h" |
| +#include "base/task_runner.h" |
| + |
| +namespace views { |
| + |
| +base::Lock DesktopKeyboardCaptureWin::registration_lock_; |
| + |
| +base::ScopedPtrHashMap<HWND, |
| + DesktopKeyboardCaptureWin::KeyboardInterceptRegistration> |
| + DesktopKeyboardCaptureWin::registrations_; |
|
sadrul
2014/07/29 22:22:41
I don't think you can have non-POD globals like th
Sriram
2014/07/31 00:21:13
Done.
|
| + |
| +class VIEWS_EXPORT DesktopKeyboardCaptureWin::KeyboardInterceptRegistration { |
| + public: |
| + KeyboardInterceptRegistration() : hook_handle_(NULL) {} |
| + |
| + ~KeyboardInterceptRegistration() { |
| + if (hook_handle_ != NULL) { |
| + Unhook(); |
| + } |
| + } |
| + |
| + bool Hook(HOOKPROC callback_function) { |
| + DCHECK(hook_handle_ == NULL) << "Keyboard hook already registered"; |
| + hook_handle_ = SetWindowsHookEx(WH_KEYBOARD_LL, callback_function, NULL, 0); |
| + if (hook_handle_ == NULL) { |
| + DLOG(ERROR) << "Error calling SetWindowsHookEx() - GLE = " |
| + << GetLastError(); |
| + return false; |
| + } |
| + return true; |
| + } |
| + |
| + bool Unhook() { |
| + DCHECK(hook_handle_ != NULL) << "Unhook called without registring hooks"; |
| + BOOL result = UnhookWindowsHookEx(hook_handle_); |
| + if (!result) { |
| + DLOG(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); |
| +}; |
| + |
| +// static |
| +void DesktopKeyboardCaptureWin::Capture(HWND window_handle, |
| + base::TaskRunner* task_runner) { |
| + if (AddToRegistrationList(window_handle)) { |
| + // Task to register from thread which should be active for the duration |
| + // of the registration. |
| + task_runner->PostTask( |
| + FROM_HERE, |
| + base::Bind(&DesktopKeyboardCaptureWin::RegisterExistingHandle, |
| + window_handle)); |
| + } |
| +} |
| + |
| +// static |
| +void DesktopKeyboardCaptureWin::RegisterExistingHandle(HWND window_handle) { |
| + base::AutoLock lock(registration_lock_); |
| + KeyboardInterceptRegistration* registration = |
| + registrations_.get(window_handle); |
| + if (registration != NULL) { |
| + VLOG(1) << "Registered keyboard hook for handle = " << window_handle; |
| + registration->Hook(KeyboardHook); |
| + } |
| +} |
| + |
| +// static |
| +void DesktopKeyboardCaptureWin::Release(HWND window_handle) { |
| + base::AutoLock lock(registration_lock_); |
| + registrations_.erase(window_handle); |
| + VLOG(1) << "Keyboard hook unregistered for handle = " << window_handle; |
| +} |
| + |
| +// static |
| +bool DesktopKeyboardCaptureWin::AddToRegistrationList(HWND handle) { |
| + base::AutoLock lock(registration_lock_); |
| + if (registrations_.contains(handle)) { |
| + return false; |
| + } |
| + scoped_ptr<KeyboardInterceptRegistration> registration( |
| + new KeyboardInterceptRegistration()); |
| + |
| + return registrations_.add(handle, registration.Pass()).second; |
| +} |
| + |
| +// static |
| +bool DesktopKeyboardCaptureWin::IsRegistered(HWND handle) { |
| + base::AutoLock lock(registration_lock_); |
| + return registrations_.contains(handle); |
| +} |
| + |
| +// static |
| +bool DesktopKeyboardCaptureWin::IsKeyUp(WPARAM w_param) { |
| + return (w_param == WM_KEYUP) || (w_param == WM_SYSKEYUP); |
| +} |
| + |
| +// static |
| +bool DesktopKeyboardCaptureWin::IsBitSet(ULONG value, ULONG mask) { |
| + return ((value & mask) != 0); |
| +} |
| + |
| +// static |
| +LPARAM DesktopKeyboardCaptureWin::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); |
| +} |
| + |
| +// static |
| +DWORD DesktopKeyboardCaptureWin::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; |
| +} |
| + |
| +// static |
| +bool DesktopKeyboardCaptureWin::UpdateThreadKeyboardState() { |
| + const int kKeyboardStateLength = 256; |
| + BYTE keyboard_state[kKeyboardStateLength]; |
| + if (!GetKeyboardState(keyboard_state)) { |
| + PLOG(ERROR) << "Error getting keyboard state"; |
| + return false; |
| + } |
| + |
| + int keys_to_update[] = {VK_SHIFT, VK_CONTROL, VK_MENU}; |
| + for (int index = 0; index < _countof(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)) { |
| + PLOG(ERROR) << "Error setting keyboard state"; |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +// static |
| +LRESULT CALLBACK DesktopKeyboardCaptureWin::KeyboardHook(int code, |
| + WPARAM w_param, |
| + LPARAM l_param) { |
| + HWND current_active_window = GetActiveWindow(); |
| + if ((code >= 0) && 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); |
| +} |
| + |
| +} // namespace views |