Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(65)

Unified Diff: ui/views/widget/desktop_aura/desktop_keyboard_capture_win.cc

Issue 297123002: API proposal for chrome.app.window to intercept all keys. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Updated based on sky's comments Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..b1a6ed1c026ed8e31d0304eedb4f29e4dab62a68
--- /dev/null
+++ b/ui/views/widget/desktop_aura/desktop_keyboard_capture_win.cc
@@ -0,0 +1,246 @@
+// 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"
+
+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) {
+ 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) {
sky 2014/07/31 21:36:55 Why do we need to support more than one hwnd captu
Sriram 2014/07/31 22:54:17 I wanted to protect against register/unregistered
+ 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;
+ }
+
sky 2014/07/31 21:36:55 Seems like we should unhook and/or DCHECK if you d
Sriram 2014/07/31 22:54:17 We could get called after we call unhook (Windows
+ return CallNextHookEx(NULL, code, w_param, l_param);
+}
+
+DesktopKeyboardCaptureWin::DesktopKeyboardCaptureWin(HWND window_handle)
+ : window_handle_(window_handle), registered_(false) {
+}
+
+DesktopKeyboardCaptureWin::~DesktopKeyboardCaptureWin() {
+ if (registered_)
+ Release();
+}
+
+bool DesktopKeyboardCaptureWin::Capture() {
+ DCHECK(!registered_) << "Capture called multiple times";
+ registered_ = LowLevelHookHandler::GetInstance()->Register(window_handle_);
+ return registered_;
+}
+
+void DesktopKeyboardCaptureWin::Release() {
+ DCHECK(registered_) << "Release called without registring for capture";
+ LowLevelHookHandler::GetInstance()->Deregister(window_handle_);
+ registered_ = false;
+}
+
+} // namespace views

Powered by Google App Engine
This is Rietveld 408576698