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

Side by Side 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, 4 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/views/widget/desktop_aura/desktop_keyboard_capture_win.h"
6
7 #include <map>
8
9 #include "base/containers/scoped_ptr_hash_map.h"
10 #include "base/logging.h"
11 #include "base/macros.h"
12 #include "base/memory/singleton.h"
13
14 namespace {
15 // Some helper routines used to construct keyboard event.
16
17 // Return true of WPARAM corresponds to a UP keyboard event.
18 bool IsKeyUp(WPARAM w_param) {
19 return (w_param == WM_KEYUP) || (w_param == WM_SYSKEYUP);
20 }
21
22 // Check if the given bit is set.
23 bool IsBitSet(ULONG value, ULONG mask) {
24 return ((value & mask) != 0);
25 }
26
27 // Construct LPARAM corresponding to the given low level hook callback
28 // structure.
29 LPARAM GetLParamFromHookStruct(WPARAM w_param, KBDLLHOOKSTRUCT hook_struct) {
30 ULONG key_state = 0;
31 // There is no way to get repeat count so always set it to 1.
32 key_state = 1;
33
34 // Scan code.
35 key_state |= (hook_struct.scanCode & 0xFF) << 16;
36
37 // Extended key when the event is received as part window event and so skip
38 // it.
39
40 // Context code.
41 key_state |= IsBitSet(hook_struct.flags, LLKHF_ALTDOWN) << 29;
42
43 // Previous key state - set to 1 for KEYUP events.
44 key_state |= IsKeyUp(w_param) << 30;
45
46 // Transition state.
47 key_state |= IsBitSet(hook_struct.flags, LLKHF_UP) << 31;
48 return static_cast<LPARAM>(key_state);
49 }
50
51 // Return the location independent keycode corresponding to given keycode (e.g.
52 // return shift when left/right shift is pressed). This is needed as low level
53 // hooks get location information which is not returned as part of normal window
54 // keyboard events.
55 DWORD RemoveLocationOnKeycode(DWORD vk_code) {
56 // Virtual keycode from low level hook include location while window messages
57 // does not. So convert them to be without location.
58 switch (vk_code) {
59 case VK_LSHIFT:
60 case VK_RSHIFT:
61 return VK_SHIFT;
62 case VK_LCONTROL:
63 case VK_RCONTROL:
64 return VK_CONTROL;
65 case VK_LMENU:
66 case VK_RMENU:
67 return VK_MENU;
68 }
69 return vk_code;
70 }
71
72 // Update thread keyboard state so GetKeyboardStatus() works correctly. Thread
73 // keyboard state is not updated when low level hook is used to intercept
74 // keyboard events.
75 bool UpdateThreadKeyboardState() {
76 const int kKeyboardStateLength = 256;
77 BYTE keyboard_state[kKeyboardStateLength];
78 if (!GetKeyboardState(keyboard_state)) {
79 DVLOG(ERROR) << "Error getting keyboard state";
80 return false;
81 }
82
83 int keys_to_update[] = {VK_SHIFT, VK_CONTROL, VK_MENU};
84 for (int index = 0; index < arraysize(keys_to_update); index++) {
85 int key = keys_to_update[index];
86 SHORT key_state = GetAsyncKeyState(key);
87 keyboard_state[key] = (IsBitSet(key_state, 0x8000) ? 0x80 : 0) |
88 (IsBitSet(key_state, 0x1) ? 1 : 0);
89 }
90
91 if (!SetKeyboardState(keyboard_state)) {
92 DVLOG(ERROR) << "Error setting keyboard state";
93 return false;
94 }
95
96 return true;
97 }
98 }
99
100 namespace views {
101
102 // Maintains low level registration for a window.
103 class KeyboardInterceptRegistration {
104 public:
105 KeyboardInterceptRegistration() : hook_handle_(NULL) {}
106
107 ~KeyboardInterceptRegistration() {
108 if (hook_handle_ != NULL)
109 Unhook();
110 }
111
112 // Register for low level hook.
113 bool Hook(HOOKPROC callback_function) {
114 DCHECK(hook_handle_ == NULL) << "Keyboard hook already registered";
115 hook_handle_ = SetWindowsHookEx(WH_KEYBOARD_LL, callback_function, NULL, 0);
116 if (hook_handle_ == NULL) {
117 DVLOG(ERROR) << "Error calling SetWindowsHookEx() - GLE = "
118 << GetLastError();
119 return false;
120 }
121 return true;
122 }
123
124 // Unhook registered hook.
125 bool Unhook() {
126 DCHECK(hook_handle_ != NULL) << "Unhook called without registring hooks";
127 BOOL result = UnhookWindowsHookEx(hook_handle_);
128 if (!result) {
129 DVLOG(ERROR) << "Error calling UnhookWindowsHookEx() - GLE = "
130 << GetLastError();
131 return false;
132 }
133 hook_handle_ = NULL;
134 return true;
135 }
136
137 private:
138 // Hook returned when it was installed.
139 HHOOK hook_handle_;
140
141 DISALLOW_COPY_AND_ASSIGN(KeyboardInterceptRegistration);
142 };
143
144 // Implements low level hook and manages registration for all the windows.
145 class LowLevelHookHandler {
146 public:
147 // Request all keyboard events to be routed to the given window.
148 bool Register(HWND window_handle);
149
150 // Release the request for all keyboard events.
151 void Deregister(HWND window_handle);
152
153 // Get singleton instance.
154 static LowLevelHookHandler* GetInstance();
155
156 private:
157 // Private constructor/destructor so it is accessible only
158 // DefaultSingletonTraits.
159 friend struct DefaultSingletonTraits<LowLevelHookHandler>;
160 LowLevelHookHandler() {}
161 ~LowLevelHookHandler() {}
162
163 // Check if window handle is registered to intercept keyboard events.
164 bool IsRegistered(HWND handle);
165
166 // Low level keyboard hook processing related functions.
167 // Hook callback called from the OS.
168 static LRESULT CALLBACK
169 KeyboardHook(int code, WPARAM w_param, LPARAM l_param);
170
171 // There is no lock protecting this list as the low level hook callbacks are
172 // executed on same thread that registered the hook and there is only one
173 // thread
174 // that execute all view code in browser.
175 base::ScopedPtrHashMap<HWND, KeyboardInterceptRegistration> registrations_;
176 };
177
178 // static
179 LowLevelHookHandler* LowLevelHookHandler::GetInstance() {
180 return Singleton<LowLevelHookHandler,
181 DefaultSingletonTraits<LowLevelHookHandler> >::get();
182 }
183
184 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
185 if (registrations_.contains(window_handle))
186 return false;
187
188 scoped_ptr<KeyboardInterceptRegistration> registration(
189 new KeyboardInterceptRegistration());
190 if (registration->Hook(KeyboardHook)) {
191 if (registrations_.add(window_handle, registration.Pass()).second)
192 return true;
193 }
194 return false;
195 }
196
197 void LowLevelHookHandler::Deregister(HWND window_handle) {
198 registrations_.erase(window_handle);
199 DVLOG(1) << "Keyboard hook unregistered for handle = " << window_handle;
200 }
201
202 bool LowLevelHookHandler::IsRegistered(HWND handle) {
203 return registrations_.contains(handle);
204 }
205
206 // static
207 LRESULT CALLBACK
208 LowLevelHookHandler::KeyboardHook(int code, WPARAM w_param, LPARAM l_param) {
209 HWND current_active_window = GetActiveWindow();
210 if ((code >= 0) && GetInstance()->IsRegistered(current_active_window)) {
211 UpdateThreadKeyboardState();
212
213 KBDLLHOOKSTRUCT hook_struct = *reinterpret_cast<KBDLLHOOKSTRUCT*>(l_param);
214 PostMessage(current_active_window,
215 w_param,
216 RemoveLocationOnKeycode(hook_struct.vkCode),
217 GetLParamFromHookStruct(w_param, hook_struct));
218
219 return 1;
220 }
221
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
222 return CallNextHookEx(NULL, code, w_param, l_param);
223 }
224
225 DesktopKeyboardCaptureWin::DesktopKeyboardCaptureWin(HWND window_handle)
226 : window_handle_(window_handle), registered_(false) {
227 }
228
229 DesktopKeyboardCaptureWin::~DesktopKeyboardCaptureWin() {
230 if (registered_)
231 Release();
232 }
233
234 bool DesktopKeyboardCaptureWin::Capture() {
235 DCHECK(!registered_) << "Capture called multiple times";
236 registered_ = LowLevelHookHandler::GetInstance()->Register(window_handle_);
237 return registered_;
238 }
239
240 void DesktopKeyboardCaptureWin::Release() {
241 DCHECK(registered_) << "Release called without registring for capture";
242 LowLevelHookHandler::GetInstance()->Deregister(window_handle_);
243 registered_ = false;
244 }
245
246 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698