blob: a48fb5b46cdef6e1d585e4520e7a493f514d5428 [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"
Lei Zhang71d3a022021-05-27 17:08:4217#include "base/cxx17_backports.h"
Erik Jensenb8876672019-12-03 04:30:0518#include "base/memory/ptr_util.h"
19#include "base/memory/weak_ptr.h"
Erik Jensenb8876672019-12-03 04:30:0520#include "base/strings/utf_string_conversions.h"
21#include "base/task/post_task.h"
Patrick Monette643cdf62021-10-15 19:13:4222#include "base/task/single_thread_task_runner.h"
Erik Jensenb8876672019-12-03 04:30:0523#include "base/threading/sequenced_task_runner_handle.h"
24#include "base/threading/thread_local.h"
25#include "base/timer/timer.h"
26#include "remoting/proto/control.pb.h"
Erik Jensenc08ecf112019-12-19 00:29:4927#include "third_party/webrtc/modules/desktop_capture/win/desktop.h"
28#include "third_party/webrtc/modules/desktop_capture/win/scoped_thread_desktop.h"
Erik Jensenb8876672019-12-03 04:30:0529#include "ui/events/keycodes/dom/dom_code.h"
30#include "ui/events/keycodes/dom/keycode_converter.h"
31
32namespace remoting {
33
34namespace {
35
Peter Kastinge5a38ed2021-10-02 03:06:3536constexpr base::TimeDelta POLL_INTERVAL = base::Milliseconds(1000);
Erik Jensen34caf362020-01-09 20:17:0737// If second is equivalent to first (generates the same functions/characters at
38// all shift levels), second will be removed from the map.
39constexpr std::pair<ui::DomCode, ui::DomCode> POSSIBLE_EQUIVALENTS[] = {
40 // These are equivalent on the US QWERTY layout, among others.
41 {ui::DomCode::BACKSLASH, ui::DomCode::INTL_BACKSLASH}};
Erik Jensenb8876672019-12-03 04:30:0542
43class KeyboardLayoutMonitorWin : public KeyboardLayoutMonitor {
44 public:
45 KeyboardLayoutMonitorWin(
Erik Jensenc08ecf112019-12-19 00:29:4946 base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback,
47 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner);
Erik Jensenb8876672019-12-03 04:30:0548 ~KeyboardLayoutMonitorWin() override;
49 void Start() override;
50
51 private:
52 // Check the current layout, and invoke the callback if it has changed.
53 void QueryLayout();
Erik Jensenc08ecf112019-12-19 00:29:4954 void ResetTimer();
55 static void QueryLayoutOnInputThread(
56 scoped_refptr<base::SequencedTaskRunner> reply_sequence,
57 base::WeakPtr<KeyboardLayoutMonitorWin> monitor,
58 HKL previous_layout);
59 void OnLayoutChanged(HKL new_layout, protocol::KeyboardLayout layout_details);
Erik Jensenb8876672019-12-03 04:30:0560
61 HKL previous_layout_ = nullptr;
62 base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback_;
Erik Jensenc08ecf112019-12-19 00:29:4963 base::RetainingOneShotTimer timer_;
64 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner_;
65 base::WeakPtrFactory<KeyboardLayoutMonitorWin> weak_ptr_factory_;
Erik Jensenb8876672019-12-03 04:30:0566};
67
68KeyboardLayoutMonitorWin::KeyboardLayoutMonitorWin(
Erik Jensenc08ecf112019-12-19 00:29:4969 base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback,
70 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner)
71 : callback_(std::move(callback)),
72 input_task_runner_(std::move(input_task_runner)),
73 weak_ptr_factory_(this) {}
Erik Jensenb8876672019-12-03 04:30:0574
75KeyboardLayoutMonitorWin::~KeyboardLayoutMonitorWin() = default;
76
77void KeyboardLayoutMonitorWin::Start() {
78 timer_.Start(FROM_HERE, POLL_INTERVAL, this,
79 &KeyboardLayoutMonitorWin::QueryLayout);
80}
81
Erik Jensenc08ecf112019-12-19 00:29:4982void ClearDeadKeys(HKL layout);
83bool IsNumpadKey(ui::DomCode code);
84UINT TranslateVirtualKey(bool numlock_state,
85 bool shift_state,
86 UINT virtual_key,
87 ui::DomCode code);
88protocol::LayoutKeyFunction VirtualKeyToLayoutKeyFunction(UINT virtual_key,
89 LANGID lang);
90
Erik Jensenb8876672019-12-03 04:30:0591void KeyboardLayoutMonitorWin::QueryLayout() {
Erik Jensenc08ecf112019-12-19 00:29:4992 // Only reset the timer once the task has completed. This ensures that a delay
93 // on the input thread doesn't result in us queuing up a bunch of redundant
94 // tasks.
95 input_task_runner_->PostTaskAndReply(
96 FROM_HERE,
97 base::BindOnce(&QueryLayoutOnInputThread,
98 base::SequencedTaskRunnerHandle::Get(),
99 weak_ptr_factory_.GetWeakPtr(), previous_layout_),
100 base::BindOnce(&KeyboardLayoutMonitorWin::ResetTimer,
101 weak_ptr_factory_.GetWeakPtr()));
102}
103
104void KeyboardLayoutMonitorWin::ResetTimer() {
105 timer_.Reset();
106}
107
108// static
109void KeyboardLayoutMonitorWin::QueryLayoutOnInputThread(
110 scoped_refptr<base::SequencedTaskRunner> reply_sequence,
111 base::WeakPtr<KeyboardLayoutMonitorWin> monitor,
112 HKL previous_layout) {
113 // Switch to the active desktop.
114 webrtc::ScopedThreadDesktop desktop;
115 std::unique_ptr<webrtc::Desktop> input_desktop(
116 webrtc::Desktop::GetInputDesktop());
117 if (input_desktop && !desktop.IsSame(*input_desktop)) {
118 desktop.SetThreadDesktop(input_desktop.release());
119 }
120
Erik Jensenb8876672019-12-03 04:30:05121 // Get the keyboard layout for the active window.
122 DWORD thread_id = 0;
123 HWND foreground_window = GetForegroundWindow();
124 if (foreground_window) {
125 thread_id = GetWindowThreadProcessId(foreground_window, nullptr);
Erik Jensenc08ecf112019-12-19 00:29:49126 } else if (previous_layout != 0) {
Erik Jensenb8876672019-12-03 04:30:05127 // There's no currently active window, so keep using the previous layout.
128 return;
129 }
130 // If there is no previous layout and there's no active window
131 // (thread_id == 0), this will return the layout associated with this
132 // thread, which is better than nothing.
133 HKL layout = GetKeyboardLayout(thread_id);
Erik Jensenc08ecf112019-12-19 00:29:49134 if (layout == previous_layout) {
Erik Jensenb8876672019-12-03 04:30:05135 return;
136 }
Erik Jensenb8876672019-12-03 04:30:05137
138 protocol::KeyboardLayout layout_message;
139 // TODO(rkjnsn): Windows doesn't provide an API to read the keyboard layout
140 // directly. We can use the various key translation functions to mostly get
141 // the information we need, but it requires some hacks, could miss some edge
142 // cases, and modifies the system keyboard state. Windows keyboard layouts
143 // consist of DLLs providing data tables in a reasonable straight-forward
144 // format, so it may make sense to look at reading them directly.
145
146 // It would be nice if we could use MapVirtualKeyEx to translate virtual
147 // keys to characters, as it neither is affected by nor modifies the system
148 // keyboard state. Unfortunately, it provides no way to specify shift
149 // states, so it can only be used to retrieve the unshifted character for a
150 // given key. Instead, we use ToUnicodeEx, which is affected by (and
151 // affects) the system keyboard state, so we start by clearing any queued-up
152 // dead keys.
153 ClearDeadKeys(layout);
154
155 // Keyboard layouts have a KLLF_ALTGR flag that indicated whether the right
156 // alt key is AltGr (and thus generates Ctrl+Alt). Unfortunately, there
157 // doesn't seem to be any way to determine whether the current layout sets
158 // this flag using the Windows API. As a hack/workaround, we assume that
159 // right Alt == AltGr if and only if there are keys that generate characters
160 // when both Ctrl and Alt modifiers are set. This is by no means guaranteed,
161 // but is expected to be true for common layouts.
162 bool has_altgr = false;
163
164 // Keys/shift levels that function as right Alt. These will be updated to
165 // AltGr if the keyboard appears to use it. (Most keyboards will have at
166 // most one of these.)
167 std::vector<std::pair<std::uint32_t, int>> right_alts;
168
169 for (ui::DomCode key : KeyboardLayoutMonitor::kSupportedKeys) {
Erik Jensen34caf362020-01-09 20:17:07170 // These keys cannot be injected properly on Windows with the APIs we use.
171 //
172 // The USB keyboard driver translates NumLock to the scancode 0x45, but it
173 // is delivered to applications as 0xE045. Meanwhile, Pause is translated
174 // to the scancode sequence 0xE1 0x1D 0x45 0xE1 0x9D 0xC5, but is delivered
175 // to applications as a plain 0x45.
176 //
177 // Injecting 0x45 using SendInput does get interpreted by Windows as
178 // VK_NUMLOCK, but is not translated to 0xE045 before being delivered to
179 // applications as it is with a physical keyboard. As a result, Chrome (for
180 // example) reports a key value of "NumLock" but a code value of "Pause".
181 //
182 // Injecting 0xE045, on the otherhand, does not get interpreted as
183 // VK_NUMLOCK, so Chrome sees reports a code value of "NumLock", but a key
184 // value of "Unidentified".
185 //
186 // I have not been able to determine any way to use SendInput to inject an
187 // event that Windows interprets as VK_PAUSE.
188 //
189 // NumpadEqual also behaves inconsistently when injected, but in a
190 // different way: while when input using a physical keyboard, the
191 // corresponding scancode (0x59) is always interpreted as VK_CLEAR
192 // regardless of the num lock state. When injected, however, Windows
193 // translates the key to VK_NUMPAD5 when numlock is enabled. Since the
194 // virtual keyboard considers num lock always to be enabled, this
195 // effectively results in an extra 5 key in the NumpadEqual position, which
196 // is both redundant and confusing. Given that most keyboards lack this key,
197 // and those that do have it label it '=', it seems easiest just to exclude
198 // it for now. In the future, we could consider adding support to it for
199 // keyboard layouts that treat is as something other than VK_CLEAR, if such
200 // layouts turn out to exist. (Why Windows maps the USB 'Keypad =' key to
201 // scancode 0x59 in the first place, even though 0x59 does not generate an
202 // '=' character, is unclear.)
203 if (key == ui::DomCode::NUM_LOCK || key == ui::DomCode::PAUSE ||
204 key == ui::DomCode::NUMPAD_EQUAL) {
205 continue;
206 }
207
Erik Jensenb8876672019-12-03 04:30:05208 std::uint32_t usb_code = ui::KeycodeConverter::DomCodeToUsbKeycode(key);
209 int scancode = ui::KeycodeConverter::DomCodeToNativeKeycode(key);
210 UINT virtual_key = MapVirtualKeyEx(scancode, MAPVK_VSC_TO_VK_EX, layout);
211 if (virtual_key == 0) {
212 // This key is not mapped in the current layout.
213 continue;
214 }
215
Erik Jensenc08ecf112019-12-19 00:29:49216 if (virtual_key == VK_CAPITAL || virtual_key == VK_NUMLOCK) {
217 // Don't send caps or numlock keys until we decide how to handle them.
Erik Jensen34caf362020-01-09 20:17:07218 // (We currently skip the key in the NumLock position above due to
219 // difficulties injecting it, but the user still may have mapped a
220 // different key to that function.)
Erik Jensenc08ecf112019-12-19 00:29:49221 continue;
222 }
223
Erik Jensenb8876672019-12-03 04:30:05224 google::protobuf::Map<google::protobuf::uint32,
225 protocol::KeyboardLayout_KeyAction>& key_actions =
226 *(*layout_message.mutable_keys())[usb_code].mutable_actions();
227
228 for (int shift_level = 0; shift_level < 4; ++shift_level) {
229 // Mimic Windows's handling of number pad key.
230 UINT translated_key = TranslateVirtualKey(
231 /* numlock_state */ true, shift_level & 1, virtual_key, key);
232
233 // First check if the key generates a character.
234 BYTE key_state[256] = {0};
235 // Modifiers set the high-order bit when pressed.
236 key_state[VK_SHIFT] = (shift_level & 1) << 7;
237 key_state[VK_CONTROL] = key_state[VK_MENU] = (shift_level & 2) << 6;
238 // Locks set the low-order bit when toggled on.
239 // For now, generate a layout with numlock always on and caps lock
240 // always off.
241 // TODO(rkjnsn): Update this when we decide how we want to handle locks
242 // for the on-screen keyboard.
243 key_state[VK_NUMLOCK] = 1;
244 key_state[VK_CAPITAL] = 0;
245 WCHAR char_buffer[16];
Erik Jensen34caf362020-01-09 20:17:07246 // According to the documentation, ToUnicodeEx usually does the
247 // translation solely based on the virtual key, but can use bit 15 of the
248 // scancode to distinguish between a keypress and a key release. This
249 // suggests that the expected format for scancode is the upper word of
250 // lParam from a WM_CHAR, where the top bit similarly distinguished press
251 // versus release. In any event, passing |scancode| as the second
252 // parameter here would thus cause extended scancodes (which have the 15th
253 // bit set) to erroneously be interpreted as key-up events and not
254 // generate the appropriate character. Rather than attempting to munge the
255 // scancode into whatever format ToUnicodeEx expects, passing 0 seems to
256 // work just fine.
257 int size = ToUnicodeEx(translated_key, 0, key_state, char_buffer,
Erik Jensenb8876672019-12-03 04:30:05258 base::size(char_buffer), 0, layout);
259 if (size < 0) {
260 // We don't handle dead keys specially for the layout, but we do
261 // need to clear them from the system keyboard state.
262 ClearDeadKeys(layout);
263 size = -size;
264 }
265 if (size > 0) {
Erik Jensen34caf362020-01-09 20:17:07266 if (size == 1 && char_buffer[0] < 0x20) {
267 // Handle known control characters.
268 protocol::LayoutKeyFunction function =
269 protocol::LayoutKeyFunction::UNKNOWN;
270 switch (char_buffer[0]) {
271 case 0x08:
272 function = protocol::LayoutKeyFunction::BACKSPACE;
273 break;
274 case 0x09:
275 function = protocol::LayoutKeyFunction::TAB;
276 break;
277 case 0x0D:
278 function = protocol::LayoutKeyFunction::ENTER;
279 break;
280 case 0x1B:
281 function = protocol::LayoutKeyFunction::ESCAPE;
282 break;
283 }
284 if (function != protocol::LayoutKeyFunction::UNKNOWN) {
285 key_actions[shift_level].set_function(function);
286 continue;
287 }
288 }
Erik Jensenb8876672019-12-03 04:30:05289 // The key generated at least one character.
290 key_actions[shift_level].set_character(
Jan Wilken Dörrie06451e62021-02-22 18:07:29291 base::WideToUTF8(base::WStringPiece(char_buffer, size)));
Erik Jensenb8876672019-12-03 04:30:05292 if (shift_level > 2) {
293 has_altgr = true;
294 }
295 continue;
296 }
297
298 // If the key didn't generate a character, translate it based on the
299 // virtual key value.
300 key_actions[shift_level].set_function(VirtualKeyToLayoutKeyFunction(
301 translated_key, reinterpret_cast<std::uintptr_t>(layout) & 0xFFFF));
302 if (translated_key == VK_RMENU) {
303 right_alts.emplace_back(usb_code, shift_level);
304 }
305 }
306 }
307
308 // If any ctrl+alt+key sequence generated a character, assume right Alt is
309 // AltGr.
310 if (has_altgr) {
311 for (std::pair<std::uint32_t, int> right_alt : right_alts) {
312 (*(*layout_message.mutable_keys())[right_alt.first]
313 .mutable_actions())[right_alt.second]
314 .set_function(protocol::LayoutKeyFunction::ALT_GR);
315 }
316 } else {
317 // Remove higher shift levels since there's no way to generate them.
318 for (auto& key : *layout_message.mutable_keys()) {
319 key.second.mutable_actions()->erase(2);
320 key.second.mutable_actions()->erase(3);
321 }
322 }
323
Erik Jensen34caf362020-01-09 20:17:07324 // Some layouts have equivalent keys. Remove the redundant keys to make the
325 // layout cleaner.
326 auto* keys = layout_message.mutable_keys();
327 for (const std::pair<ui::DomCode, ui::DomCode>& possible_equivalent :
328 POSSIBLE_EQUIVALENTS) {
329 std::uint32_t code1 =
330 ui::KeycodeConverter::DomCodeToUsbKeycode(possible_equivalent.first);
331 std::uint32_t code2 =
332 ui::KeycodeConverter::DomCodeToUsbKeycode(possible_equivalent.second);
333 auto key_behavior1 = keys->find(code1);
334 auto key_behavior2 = keys->find(code2);
335 if (key_behavior1 != keys->end() && key_behavior2 != keys->end() &&
336 key_behavior1->second.SerializeAsString() ==
337 key_behavior2->second.SerializeAsString()) {
338 keys->erase(key_behavior2);
339 }
340 }
341
342 // There seem to be a number of keys that are mapped to a virtual key in the
343 // layout but don't do anything useful. E.g., the US QWERTY layout maps
344 // NonConvert, which isn't on a standard US keyboard, to VK_OEM_PA1, which
345 // doesn't appear to be useful. To avoid cluttering the on-screen keyboard
346 // with blank, useless keys, just omit unknown keys for now. We can revisit
347 // this if folks send feedback about useful keys being missing.
348 for (auto it = keys->begin(); it != keys->end();) {
349 bool has_action = false;
350 for (const auto& action : it->second.actions()) {
351 if (action.second.has_character() ||
352 (action.second.has_function() &&
353 action.second.function() != protocol::LayoutKeyFunction::UNKNOWN)) {
354 has_action = true;
355 }
356 }
357 if (!has_action) {
358 it = keys->erase(it);
359 } else {
360 ++it;
361 }
362 }
363
Erik Jensenc08ecf112019-12-19 00:29:49364 reply_sequence->PostTask(
365 FROM_HERE,
366 base::BindOnce(&KeyboardLayoutMonitorWin::OnLayoutChanged,
367 std::move(monitor), layout, std::move(layout_message)));
Erik Jensenb8876672019-12-03 04:30:05368}
369
Erik Jensenc08ecf112019-12-19 00:29:49370void KeyboardLayoutMonitorWin::OnLayoutChanged(
371 HKL new_layout,
372 protocol::KeyboardLayout layout_details) {
373 previous_layout_ = new_layout;
374 callback_.Run(std::move(layout_details));
375}
376
377void ClearDeadKeys(HKL layout) {
Erik Jensenb8876672019-12-03 04:30:05378 // ToUnicodeEx both is affected by and modifies the current keyboard state,
379 // which includes the list of currently stored dead keys. Pressing space
380 // translates previously pressed dead keys to characters, clearing the dead-
381 // key buffer.
382 BYTE key_state[256] = {0};
383 WCHAR char_buffer[16];
384 ToUnicodeEx(VK_SPACE,
385 ui::KeycodeConverter::DomCodeToNativeKeycode(ui::DomCode::SPACE),
386 key_state, char_buffer, base::size(char_buffer), 0, layout);
387}
388
Erik Jensenc08ecf112019-12-19 00:29:49389bool IsNumpadKey(ui::DomCode code) {
Erik Jensenb8876672019-12-03 04:30:05390 // Windows keyboard layouts map number pad keys to virtual keys based on
391 // their function when num lock is off. E.g., 4 on the number pad generates
392 // VK_LEFT, the same as the left arrow key. To distinguish them, the layout
393 // sets the KBDNUMPAD flag on the numlock versions, allowing Windows to
394 // translate VK_LEFT to VK_NUMPAD4 when numlock is enabled only for the keys
395 // on the number pad. Unfortunately, the state of the KBDNUMPAD flag for a
396 // given scan code does not appear to be accessible via the Windows API, so
397 // for now just assume that all layouts use the same scan codes for the
398 // number pad.
399 // TODO(rkjnsn): Figure out if there's a better way to determine this.
400
401 switch (code) {
402 case ui::DomCode::NUMPAD0:
403 case ui::DomCode::NUMPAD1:
404 case ui::DomCode::NUMPAD2:
405 case ui::DomCode::NUMPAD3:
406 case ui::DomCode::NUMPAD4:
407 case ui::DomCode::NUMPAD5:
408 case ui::DomCode::NUMPAD6:
409 case ui::DomCode::NUMPAD7:
410 case ui::DomCode::NUMPAD8:
411 case ui::DomCode::NUMPAD9:
412 case ui::DomCode::NUMPAD_DECIMAL:
413 return true;
414 default:
415 return false;
416 }
417}
418
Erik Jensenc08ecf112019-12-19 00:29:49419UINT TranslateVirtualKey(bool numlock_state,
420 bool shift_state,
421 UINT virtual_key,
422 ui::DomCode code) {
Erik Jensenb8876672019-12-03 04:30:05423 // Windows only translates numpad keys when num lock is on and shift is not
424 // pressed. (Pressing shift when num lock is on will get you navigation, but
425 // pressing shift when num lock is off will not get you numbers.)
426 if (!numlock_state || shift_state || !IsNumpadKey(code)) {
427 return virtual_key;
428 }
429 switch (virtual_key) {
Erik Jensen34caf362020-01-09 20:17:07430 case VK_DELETE:
431 return VK_DECIMAL;
Erik Jensenb8876672019-12-03 04:30:05432 case VK_INSERT:
433 return VK_NUMPAD0;
434 case VK_END:
435 return VK_NUMPAD1;
436 case VK_DOWN:
437 return VK_NUMPAD2;
438 case VK_NEXT:
439 return VK_NUMPAD3;
440 case VK_LEFT:
441 return VK_NUMPAD4;
442 case VK_CLEAR:
443 return VK_NUMPAD5;
444 case VK_RIGHT:
445 return VK_NUMPAD6;
446 case VK_HOME:
447 return VK_NUMPAD7;
448 case VK_UP:
449 return VK_NUMPAD8;
450 case VK_PRIOR:
451 return VK_NUMPAD9;
452 default:
453 return virtual_key;
454 }
455}
456
Erik Jensenc08ecf112019-12-19 00:29:49457protocol::LayoutKeyFunction VirtualKeyToLayoutKeyFunction(UINT virtual_key,
458 LANGID lang) {
Erik Jensenb8876672019-12-03 04:30:05459 switch (virtual_key) {
460 case VK_LCONTROL:
461 case VK_RCONTROL:
462 return protocol::LayoutKeyFunction::CONTROL;
463 case VK_LMENU:
464 case VK_RMENU:
465 return protocol::LayoutKeyFunction::ALT;
466 case VK_LSHIFT:
467 case VK_RSHIFT:
468 return protocol::LayoutKeyFunction::SHIFT;
469 case VK_LWIN:
470 case VK_RWIN:
471 return protocol::LayoutKeyFunction::META;
472 case VK_NUMLOCK:
473 return protocol::LayoutKeyFunction::NUM_LOCK;
474 case VK_CAPITAL:
475 return protocol::LayoutKeyFunction::CAPS_LOCK;
476 case VK_SCROLL:
477 return protocol::LayoutKeyFunction::SCROLL_LOCK;
478 case VK_BACK:
479 return protocol::LayoutKeyFunction::BACKSPACE;
480 case VK_RETURN:
481 return protocol::LayoutKeyFunction::ENTER;
482 case VK_TAB:
483 return protocol::LayoutKeyFunction::TAB;
484 case VK_INSERT:
485 return protocol::LayoutKeyFunction::INSERT;
486 case VK_DELETE:
487 return protocol::LayoutKeyFunction::DELETE_;
488 case VK_HOME:
489 return protocol::LayoutKeyFunction::HOME;
490 case VK_END:
491 return protocol::LayoutKeyFunction::END;
492 case VK_PRIOR:
493 return protocol::LayoutKeyFunction::PAGE_UP;
494 case VK_NEXT:
495 return protocol::LayoutKeyFunction::PAGE_DOWN;
496 case VK_CLEAR:
497 return protocol::LayoutKeyFunction::CLEAR;
498 case VK_UP:
499 return protocol::LayoutKeyFunction::ARROW_UP;
500 case VK_DOWN:
501 return protocol::LayoutKeyFunction::ARROW_DOWN;
502 case VK_LEFT:
503 return protocol::LayoutKeyFunction::ARROW_LEFT;
504 case VK_RIGHT:
505 return protocol::LayoutKeyFunction::ARROW_RIGHT;
506 case VK_F1:
507 return protocol::LayoutKeyFunction::F1;
508 case VK_F2:
509 return protocol::LayoutKeyFunction::F2;
510 case VK_F3:
511 return protocol::LayoutKeyFunction::F3;
512 case VK_F4:
513 return protocol::LayoutKeyFunction::F4;
514 case VK_F5:
515 return protocol::LayoutKeyFunction::F5;
516 case VK_F6:
517 return protocol::LayoutKeyFunction::F6;
518 case VK_F7:
519 return protocol::LayoutKeyFunction::F7;
520 case VK_F8:
521 return protocol::LayoutKeyFunction::F8;
522 case VK_F9:
523 return protocol::LayoutKeyFunction::F9;
524 case VK_F10:
525 return protocol::LayoutKeyFunction::F10;
526 case VK_F11:
527 return protocol::LayoutKeyFunction::F11;
528 case VK_F12:
529 return protocol::LayoutKeyFunction::F12;
530 case VK_F13:
531 return protocol::LayoutKeyFunction::F13;
532 case VK_F14:
533 return protocol::LayoutKeyFunction::F14;
534 case VK_F15:
535 return protocol::LayoutKeyFunction::F15;
536 case VK_F16:
537 return protocol::LayoutKeyFunction::F16;
538 case VK_F17:
539 return protocol::LayoutKeyFunction::F17;
540 case VK_F18:
541 return protocol::LayoutKeyFunction::F18;
542 case VK_F19:
543 return protocol::LayoutKeyFunction::F19;
544 case VK_F20:
545 return protocol::LayoutKeyFunction::F20;
546 case VK_F21:
547 return protocol::LayoutKeyFunction::F21;
548 case VK_F22:
549 return protocol::LayoutKeyFunction::F22;
550 case VK_F23:
551 return protocol::LayoutKeyFunction::F23;
552 case VK_F24:
553 return protocol::LayoutKeyFunction::F24;
554 case VK_ESCAPE:
555 return protocol::LayoutKeyFunction::ESCAPE;
556 case VK_APPS:
557 return protocol::LayoutKeyFunction::CONTEXT_MENU;
558 case VK_PAUSE:
559 return protocol::LayoutKeyFunction::PAUSE;
Erik Jensen34caf362020-01-09 20:17:07560 case VK_SNAPSHOT:
Erik Jensenb8876672019-12-03 04:30:05561 return protocol::LayoutKeyFunction::PRINT_SCREEN;
562 }
563
564 // Handle language-specific keys.
565 if (PRIMARYLANGID(lang) == 0x11) { // Japanese
566 switch (virtual_key) {
567 case VK_DBE_SBCSCHAR:
568 return protocol::LayoutKeyFunction::HANKAKU_ZENKAKU_KANJI;
569 case VK_CONVERT:
570 return protocol::LayoutKeyFunction::HENKAN;
571 case VK_NONCONVERT:
572 return protocol::LayoutKeyFunction::MUHENKAN;
573 case VK_DBE_KATAKANA:
574 case VK_DBE_HIRAGANA:
575 // TODO(rkjnsn): Make sure it makes sense to use the same key cap for
576 // both of these.
577 return protocol::LayoutKeyFunction::KATAKANA_HIRAGANA_ROMAJI;
578 case VK_DBE_ALPHANUMERIC:
579 return protocol::LayoutKeyFunction::EISU;
580 }
581 } else if (PRIMARYLANGID(lang) == 0x12) { // Korean
582 switch (virtual_key) {
583 case VK_HANJA:
584 return protocol::LayoutKeyFunction::HANJA;
585 case VK_HANGUL:
586 return protocol::LayoutKeyFunction::HAN_YEONG;
587 }
588 }
589
590 return protocol::LayoutKeyFunction::UNKNOWN;
591}
592
593} // namespace
594
595std::unique_ptr<KeyboardLayoutMonitor> KeyboardLayoutMonitor::Create(
Erik Jensenc08ecf112019-12-19 00:29:49596 base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback,
597 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner) {
598 return std::make_unique<KeyboardLayoutMonitorWin>(
599 std::move(callback), std::move(input_task_runner));
Erik Jensenb8876672019-12-03 04:30:05600}
601
602} // namespace remoting