blob: d48ebf7970d1018a8bbd1289c79ff1e269453137 [file] [log] [blame]
Erik Jensenb8876672019-12-03 04:30:051// Copyright 2019 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 "remoting/host/keyboard_layout_monitor.h"
6
7#include <windows.h>
8#include <ime.h>
9
10#include <memory>
11#include <utility>
12#include <vector>
13
14#include "base/bind.h"
15#include "base/callback.h"
16#include "base/compiler_specific.h"
17#include "base/logging.h"
18#include "base/memory/ptr_util.h"
19#include "base/memory/weak_ptr.h"
20#include "base/no_destructor.h"
21#include "base/single_thread_task_runner.h"
22#include "base/stl_util.h"
23#include "base/strings/utf_string_conversions.h"
24#include "base/task/post_task.h"
25#include "base/threading/sequenced_task_runner_handle.h"
26#include "base/threading/thread_local.h"
27#include "base/timer/timer.h"
28#include "remoting/proto/control.pb.h"
29#include "ui/events/keycodes/dom/dom_code.h"
30#include "ui/events/keycodes/dom/keycode_converter.h"
31
32namespace remoting {
33
34namespace {
35
36constexpr base::TimeDelta POLL_INTERVAL =
37 base::TimeDelta::FromMilliseconds(1000);
38
39class KeyboardLayoutMonitorWin : public KeyboardLayoutMonitor {
40 public:
41 KeyboardLayoutMonitorWin(
42 base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback);
43 ~KeyboardLayoutMonitorWin() override;
44 void Start() override;
45
46 private:
47 // Check the current layout, and invoke the callback if it has changed.
48 void QueryLayout();
49
50 void ClearDeadKeys(HKL layout);
51
52 bool IsNumpadKey(ui::DomCode code);
53
54 UINT TranslateVirtualKey(bool numlock_state,
55 bool shift_state,
56 UINT virtual_key,
57 ui::DomCode code);
58
59 protocol::LayoutKeyFunction VirtualKeyToLayoutKeyFunction(UINT virtual_key,
60 LANGID lang);
61
62 HKL previous_layout_ = nullptr;
63 base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback_;
64 base::RepeatingTimer timer_;
65};
66
67KeyboardLayoutMonitorWin::KeyboardLayoutMonitorWin(
68 base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback)
69 : callback_(std::move(callback)) {}
70
71KeyboardLayoutMonitorWin::~KeyboardLayoutMonitorWin() = default;
72
73void KeyboardLayoutMonitorWin::Start() {
74 timer_.Start(FROM_HERE, POLL_INTERVAL, this,
75 &KeyboardLayoutMonitorWin::QueryLayout);
76}
77
78void KeyboardLayoutMonitorWin::QueryLayout() {
79 // Get the keyboard layout for the active window.
80 DWORD thread_id = 0;
81 HWND foreground_window = GetForegroundWindow();
82 if (foreground_window) {
83 thread_id = GetWindowThreadProcessId(foreground_window, nullptr);
84 } else if (previous_layout_ != 0) {
85 // There's no currently active window, so keep using the previous layout.
86 return;
87 }
88 // If there is no previous layout and there's no active window
89 // (thread_id == 0), this will return the layout associated with this
90 // thread, which is better than nothing.
91 HKL layout = GetKeyboardLayout(thread_id);
92 if (layout == previous_layout_) {
93 return;
94 }
95 previous_layout_ = layout;
96
97 protocol::KeyboardLayout layout_message;
98 // TODO(rkjnsn): Windows doesn't provide an API to read the keyboard layout
99 // directly. We can use the various key translation functions to mostly get
100 // the information we need, but it requires some hacks, could miss some edge
101 // cases, and modifies the system keyboard state. Windows keyboard layouts
102 // consist of DLLs providing data tables in a reasonable straight-forward
103 // format, so it may make sense to look at reading them directly.
104
105 // It would be nice if we could use MapVirtualKeyEx to translate virtual
106 // keys to characters, as it neither is affected by nor modifies the system
107 // keyboard state. Unfortunately, it provides no way to specify shift
108 // states, so it can only be used to retrieve the unshifted character for a
109 // given key. Instead, we use ToUnicodeEx, which is affected by (and
110 // affects) the system keyboard state, so we start by clearing any queued-up
111 // dead keys.
112 ClearDeadKeys(layout);
113
114 // Keyboard layouts have a KLLF_ALTGR flag that indicated whether the right
115 // alt key is AltGr (and thus generates Ctrl+Alt). Unfortunately, there
116 // doesn't seem to be any way to determine whether the current layout sets
117 // this flag using the Windows API. As a hack/workaround, we assume that
118 // right Alt == AltGr if and only if there are keys that generate characters
119 // when both Ctrl and Alt modifiers are set. This is by no means guaranteed,
120 // but is expected to be true for common layouts.
121 bool has_altgr = false;
122
123 // Keys/shift levels that function as right Alt. These will be updated to
124 // AltGr if the keyboard appears to use it. (Most keyboards will have at
125 // most one of these.)
126 std::vector<std::pair<std::uint32_t, int>> right_alts;
127
128 for (ui::DomCode key : KeyboardLayoutMonitor::kSupportedKeys) {
129 std::uint32_t usb_code = ui::KeycodeConverter::DomCodeToUsbKeycode(key);
130 int scancode = ui::KeycodeConverter::DomCodeToNativeKeycode(key);
131 UINT virtual_key = MapVirtualKeyEx(scancode, MAPVK_VSC_TO_VK_EX, layout);
132 if (virtual_key == 0) {
133 // This key is not mapped in the current layout.
134 continue;
135 }
136
137 google::protobuf::Map<google::protobuf::uint32,
138 protocol::KeyboardLayout_KeyAction>& key_actions =
139 *(*layout_message.mutable_keys())[usb_code].mutable_actions();
140
141 for (int shift_level = 0; shift_level < 4; ++shift_level) {
142 // Mimic Windows's handling of number pad key.
143 UINT translated_key = TranslateVirtualKey(
144 /* numlock_state */ true, shift_level & 1, virtual_key, key);
145
146 // First check if the key generates a character.
147 BYTE key_state[256] = {0};
148 // Modifiers set the high-order bit when pressed.
149 key_state[VK_SHIFT] = (shift_level & 1) << 7;
150 key_state[VK_CONTROL] = key_state[VK_MENU] = (shift_level & 2) << 6;
151 // Locks set the low-order bit when toggled on.
152 // For now, generate a layout with numlock always on and caps lock
153 // always off.
154 // TODO(rkjnsn): Update this when we decide how we want to handle locks
155 // for the on-screen keyboard.
156 key_state[VK_NUMLOCK] = 1;
157 key_state[VK_CAPITAL] = 0;
158 WCHAR char_buffer[16];
159 int size = ToUnicodeEx(translated_key, scancode, key_state, char_buffer,
160 base::size(char_buffer), 0, layout);
161 if (size < 0) {
162 // We don't handle dead keys specially for the layout, but we do
163 // need to clear them from the system keyboard state.
164 ClearDeadKeys(layout);
165 size = -size;
166 }
167 if (size > 0) {
168 // The key generated at least one character.
169 key_actions[shift_level].set_character(
170 base::UTF16ToUTF8(base::StringPiece16(char_buffer, size)));
171 if (shift_level > 2) {
172 has_altgr = true;
173 }
174 continue;
175 }
176
177 // If the key didn't generate a character, translate it based on the
178 // virtual key value.
179 key_actions[shift_level].set_function(VirtualKeyToLayoutKeyFunction(
180 translated_key, reinterpret_cast<std::uintptr_t>(layout) & 0xFFFF));
181 if (translated_key == VK_RMENU) {
182 right_alts.emplace_back(usb_code, shift_level);
183 }
184 }
185 }
186
187 // If any ctrl+alt+key sequence generated a character, assume right Alt is
188 // AltGr.
189 if (has_altgr) {
190 for (std::pair<std::uint32_t, int> right_alt : right_alts) {
191 (*(*layout_message.mutable_keys())[right_alt.first]
192 .mutable_actions())[right_alt.second]
193 .set_function(protocol::LayoutKeyFunction::ALT_GR);
194 }
195 } else {
196 // Remove higher shift levels since there's no way to generate them.
197 for (auto& key : *layout_message.mutable_keys()) {
198 key.second.mutable_actions()->erase(2);
199 key.second.mutable_actions()->erase(3);
200 }
201 }
202
203 callback_.Run(std::move(layout_message));
204}
205
206void KeyboardLayoutMonitorWin::ClearDeadKeys(HKL layout) {
207 // ToUnicodeEx both is affected by and modifies the current keyboard state,
208 // which includes the list of currently stored dead keys. Pressing space
209 // translates previously pressed dead keys to characters, clearing the dead-
210 // key buffer.
211 BYTE key_state[256] = {0};
212 WCHAR char_buffer[16];
213 ToUnicodeEx(VK_SPACE,
214 ui::KeycodeConverter::DomCodeToNativeKeycode(ui::DomCode::SPACE),
215 key_state, char_buffer, base::size(char_buffer), 0, layout);
216}
217
218bool KeyboardLayoutMonitorWin::IsNumpadKey(ui::DomCode code) {
219 // Windows keyboard layouts map number pad keys to virtual keys based on
220 // their function when num lock is off. E.g., 4 on the number pad generates
221 // VK_LEFT, the same as the left arrow key. To distinguish them, the layout
222 // sets the KBDNUMPAD flag on the numlock versions, allowing Windows to
223 // translate VK_LEFT to VK_NUMPAD4 when numlock is enabled only for the keys
224 // on the number pad. Unfortunately, the state of the KBDNUMPAD flag for a
225 // given scan code does not appear to be accessible via the Windows API, so
226 // for now just assume that all layouts use the same scan codes for the
227 // number pad.
228 // TODO(rkjnsn): Figure out if there's a better way to determine this.
229
230 switch (code) {
231 case ui::DomCode::NUMPAD0:
232 case ui::DomCode::NUMPAD1:
233 case ui::DomCode::NUMPAD2:
234 case ui::DomCode::NUMPAD3:
235 case ui::DomCode::NUMPAD4:
236 case ui::DomCode::NUMPAD5:
237 case ui::DomCode::NUMPAD6:
238 case ui::DomCode::NUMPAD7:
239 case ui::DomCode::NUMPAD8:
240 case ui::DomCode::NUMPAD9:
241 case ui::DomCode::NUMPAD_DECIMAL:
242 return true;
243 default:
244 return false;
245 }
246}
247
248UINT KeyboardLayoutMonitorWin::TranslateVirtualKey(bool numlock_state,
249 bool shift_state,
250 UINT virtual_key,
251 ui::DomCode code) {
252 // Windows only translates numpad keys when num lock is on and shift is not
253 // pressed. (Pressing shift when num lock is on will get you navigation, but
254 // pressing shift when num lock is off will not get you numbers.)
255 if (!numlock_state || shift_state || !IsNumpadKey(code)) {
256 return virtual_key;
257 }
258 switch (virtual_key) {
259 case VK_INSERT:
260 return VK_NUMPAD0;
261 case VK_END:
262 return VK_NUMPAD1;
263 case VK_DOWN:
264 return VK_NUMPAD2;
265 case VK_NEXT:
266 return VK_NUMPAD3;
267 case VK_LEFT:
268 return VK_NUMPAD4;
269 case VK_CLEAR:
270 return VK_NUMPAD5;
271 case VK_RIGHT:
272 return VK_NUMPAD6;
273 case VK_HOME:
274 return VK_NUMPAD7;
275 case VK_UP:
276 return VK_NUMPAD8;
277 case VK_PRIOR:
278 return VK_NUMPAD9;
279 default:
280 return virtual_key;
281 }
282}
283
284protocol::LayoutKeyFunction
285KeyboardLayoutMonitorWin::VirtualKeyToLayoutKeyFunction(UINT virtual_key,
286 LANGID lang) {
287 switch (virtual_key) {
288 case VK_LCONTROL:
289 case VK_RCONTROL:
290 return protocol::LayoutKeyFunction::CONTROL;
291 case VK_LMENU:
292 case VK_RMENU:
293 return protocol::LayoutKeyFunction::ALT;
294 case VK_LSHIFT:
295 case VK_RSHIFT:
296 return protocol::LayoutKeyFunction::SHIFT;
297 case VK_LWIN:
298 case VK_RWIN:
299 return protocol::LayoutKeyFunction::META;
300 case VK_NUMLOCK:
301 return protocol::LayoutKeyFunction::NUM_LOCK;
302 case VK_CAPITAL:
303 return protocol::LayoutKeyFunction::CAPS_LOCK;
304 case VK_SCROLL:
305 return protocol::LayoutKeyFunction::SCROLL_LOCK;
306 case VK_BACK:
307 return protocol::LayoutKeyFunction::BACKSPACE;
308 case VK_RETURN:
309 return protocol::LayoutKeyFunction::ENTER;
310 case VK_TAB:
311 return protocol::LayoutKeyFunction::TAB;
312 case VK_INSERT:
313 return protocol::LayoutKeyFunction::INSERT;
314 case VK_DELETE:
315 return protocol::LayoutKeyFunction::DELETE_;
316 case VK_HOME:
317 return protocol::LayoutKeyFunction::HOME;
318 case VK_END:
319 return protocol::LayoutKeyFunction::END;
320 case VK_PRIOR:
321 return protocol::LayoutKeyFunction::PAGE_UP;
322 case VK_NEXT:
323 return protocol::LayoutKeyFunction::PAGE_DOWN;
324 case VK_CLEAR:
325 return protocol::LayoutKeyFunction::CLEAR;
326 case VK_UP:
327 return protocol::LayoutKeyFunction::ARROW_UP;
328 case VK_DOWN:
329 return protocol::LayoutKeyFunction::ARROW_DOWN;
330 case VK_LEFT:
331 return protocol::LayoutKeyFunction::ARROW_LEFT;
332 case VK_RIGHT:
333 return protocol::LayoutKeyFunction::ARROW_RIGHT;
334 case VK_F1:
335 return protocol::LayoutKeyFunction::F1;
336 case VK_F2:
337 return protocol::LayoutKeyFunction::F2;
338 case VK_F3:
339 return protocol::LayoutKeyFunction::F3;
340 case VK_F4:
341 return protocol::LayoutKeyFunction::F4;
342 case VK_F5:
343 return protocol::LayoutKeyFunction::F5;
344 case VK_F6:
345 return protocol::LayoutKeyFunction::F6;
346 case VK_F7:
347 return protocol::LayoutKeyFunction::F7;
348 case VK_F8:
349 return protocol::LayoutKeyFunction::F8;
350 case VK_F9:
351 return protocol::LayoutKeyFunction::F9;
352 case VK_F10:
353 return protocol::LayoutKeyFunction::F10;
354 case VK_F11:
355 return protocol::LayoutKeyFunction::F11;
356 case VK_F12:
357 return protocol::LayoutKeyFunction::F12;
358 case VK_F13:
359 return protocol::LayoutKeyFunction::F13;
360 case VK_F14:
361 return protocol::LayoutKeyFunction::F14;
362 case VK_F15:
363 return protocol::LayoutKeyFunction::F15;
364 case VK_F16:
365 return protocol::LayoutKeyFunction::F16;
366 case VK_F17:
367 return protocol::LayoutKeyFunction::F17;
368 case VK_F18:
369 return protocol::LayoutKeyFunction::F18;
370 case VK_F19:
371 return protocol::LayoutKeyFunction::F19;
372 case VK_F20:
373 return protocol::LayoutKeyFunction::F20;
374 case VK_F21:
375 return protocol::LayoutKeyFunction::F21;
376 case VK_F22:
377 return protocol::LayoutKeyFunction::F22;
378 case VK_F23:
379 return protocol::LayoutKeyFunction::F23;
380 case VK_F24:
381 return protocol::LayoutKeyFunction::F24;
382 case VK_ESCAPE:
383 return protocol::LayoutKeyFunction::ESCAPE;
384 case VK_APPS:
385 return protocol::LayoutKeyFunction::CONTEXT_MENU;
386 case VK_PAUSE:
387 return protocol::LayoutKeyFunction::PAUSE;
388 case VK_PRINT:
389 return protocol::LayoutKeyFunction::PRINT_SCREEN;
390 }
391
392 // Handle language-specific keys.
393 if (PRIMARYLANGID(lang) == 0x11) { // Japanese
394 switch (virtual_key) {
395 case VK_DBE_SBCSCHAR:
396 return protocol::LayoutKeyFunction::HANKAKU_ZENKAKU_KANJI;
397 case VK_CONVERT:
398 return protocol::LayoutKeyFunction::HENKAN;
399 case VK_NONCONVERT:
400 return protocol::LayoutKeyFunction::MUHENKAN;
401 case VK_DBE_KATAKANA:
402 case VK_DBE_HIRAGANA:
403 // TODO(rkjnsn): Make sure it makes sense to use the same key cap for
404 // both of these.
405 return protocol::LayoutKeyFunction::KATAKANA_HIRAGANA_ROMAJI;
406 case VK_DBE_ALPHANUMERIC:
407 return protocol::LayoutKeyFunction::EISU;
408 }
409 } else if (PRIMARYLANGID(lang) == 0x12) { // Korean
410 switch (virtual_key) {
411 case VK_HANJA:
412 return protocol::LayoutKeyFunction::HANJA;
413 case VK_HANGUL:
414 return protocol::LayoutKeyFunction::HAN_YEONG;
415 }
416 }
417
418 return protocol::LayoutKeyFunction::UNKNOWN;
419}
420
421} // namespace
422
423std::unique_ptr<KeyboardLayoutMonitor> KeyboardLayoutMonitor::Create(
424 base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback) {
425 return std::make_unique<KeyboardLayoutMonitorWin>(std::move(callback));
426}
427
428} // namespace remoting