Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 1 | // 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 Zhang | 71d3a02 | 2021-05-27 17:08:42 | [diff] [blame] | 17 | #include "base/cxx17_backports.h" |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 18 | #include "base/memory/ptr_util.h" |
| 19 | #include "base/memory/weak_ptr.h" |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 20 | #include "base/strings/utf_string_conversions.h" |
| 21 | #include "base/task/post_task.h" |
Patrick Monette | 643cdf6 | 2021-10-15 19:13:42 | [diff] [blame] | 22 | #include "base/task/single_thread_task_runner.h" |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 23 | #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 Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 27 | #include "third_party/webrtc/modules/desktop_capture/win/desktop.h" |
| 28 | #include "third_party/webrtc/modules/desktop_capture/win/scoped_thread_desktop.h" |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 29 | #include "ui/events/keycodes/dom/dom_code.h" |
| 30 | #include "ui/events/keycodes/dom/keycode_converter.h" |
| 31 | |
| 32 | namespace remoting { |
| 33 | |
| 34 | namespace { |
| 35 | |
Peter Kasting | e5a38ed | 2021-10-02 03:06:35 | [diff] [blame] | 36 | constexpr base::TimeDelta POLL_INTERVAL = base::Milliseconds(1000); |
Erik Jensen | 34caf36 | 2020-01-09 20:17:07 | [diff] [blame] | 37 | // If second is equivalent to first (generates the same functions/characters at |
| 38 | // all shift levels), second will be removed from the map. |
| 39 | constexpr 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 Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 42 | |
| 43 | class KeyboardLayoutMonitorWin : public KeyboardLayoutMonitor { |
| 44 | public: |
| 45 | KeyboardLayoutMonitorWin( |
Erik Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 46 | base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback, |
| 47 | scoped_refptr<base::SingleThreadTaskRunner> input_task_runner); |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 48 | ~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 Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 54 | 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 Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 60 | |
| 61 | HKL previous_layout_ = nullptr; |
| 62 | base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback_; |
Erik Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 63 | base::RetainingOneShotTimer timer_; |
| 64 | scoped_refptr<base::SingleThreadTaskRunner> input_task_runner_; |
| 65 | base::WeakPtrFactory<KeyboardLayoutMonitorWin> weak_ptr_factory_; |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 66 | }; |
| 67 | |
| 68 | KeyboardLayoutMonitorWin::KeyboardLayoutMonitorWin( |
Erik Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 69 | 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 Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 74 | |
| 75 | KeyboardLayoutMonitorWin::~KeyboardLayoutMonitorWin() = default; |
| 76 | |
| 77 | void KeyboardLayoutMonitorWin::Start() { |
| 78 | timer_.Start(FROM_HERE, POLL_INTERVAL, this, |
| 79 | &KeyboardLayoutMonitorWin::QueryLayout); |
| 80 | } |
| 81 | |
Erik Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 82 | void ClearDeadKeys(HKL layout); |
| 83 | bool IsNumpadKey(ui::DomCode code); |
| 84 | UINT TranslateVirtualKey(bool numlock_state, |
| 85 | bool shift_state, |
| 86 | UINT virtual_key, |
| 87 | ui::DomCode code); |
| 88 | protocol::LayoutKeyFunction VirtualKeyToLayoutKeyFunction(UINT virtual_key, |
| 89 | LANGID lang); |
| 90 | |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 91 | void KeyboardLayoutMonitorWin::QueryLayout() { |
Erik Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 92 | // 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 | |
| 104 | void KeyboardLayoutMonitorWin::ResetTimer() { |
| 105 | timer_.Reset(); |
| 106 | } |
| 107 | |
| 108 | // static |
| 109 | void 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 Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 121 | // 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 Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 126 | } else if (previous_layout != 0) { |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 127 | // 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 Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 134 | if (layout == previous_layout) { |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 135 | return; |
| 136 | } |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 137 | |
| 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 Jensen | 34caf36 | 2020-01-09 20:17:07 | [diff] [blame] | 170 | // 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 Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 208 | 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 Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 216 | 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 Jensen | 34caf36 | 2020-01-09 20:17:07 | [diff] [blame] | 218 | // (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 Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 221 | continue; |
| 222 | } |
| 223 | |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 224 | 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 Jensen | 34caf36 | 2020-01-09 20:17:07 | [diff] [blame] | 246 | // 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 Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 258 | 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 Jensen | 34caf36 | 2020-01-09 20:17:07 | [diff] [blame] | 266 | 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 Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 289 | // The key generated at least one character. |
| 290 | key_actions[shift_level].set_character( |
Jan Wilken Dörrie | 06451e6 | 2021-02-22 18:07:29 | [diff] [blame] | 291 | base::WideToUTF8(base::WStringPiece(char_buffer, size))); |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 292 | 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 Jensen | 34caf36 | 2020-01-09 20:17:07 | [diff] [blame] | 324 | // 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 Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 364 | reply_sequence->PostTask( |
| 365 | FROM_HERE, |
| 366 | base::BindOnce(&KeyboardLayoutMonitorWin::OnLayoutChanged, |
| 367 | std::move(monitor), layout, std::move(layout_message))); |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 368 | } |
| 369 | |
Erik Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 370 | void 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 | |
| 377 | void ClearDeadKeys(HKL layout) { |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 378 | // 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 Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 389 | bool IsNumpadKey(ui::DomCode code) { |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 390 | // 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 Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 419 | UINT TranslateVirtualKey(bool numlock_state, |
| 420 | bool shift_state, |
| 421 | UINT virtual_key, |
| 422 | ui::DomCode code) { |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 423 | // 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 Jensen | 34caf36 | 2020-01-09 20:17:07 | [diff] [blame] | 430 | case VK_DELETE: |
| 431 | return VK_DECIMAL; |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 432 | 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 Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 457 | protocol::LayoutKeyFunction VirtualKeyToLayoutKeyFunction(UINT virtual_key, |
| 458 | LANGID lang) { |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 459 | 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 Jensen | 34caf36 | 2020-01-09 20:17:07 | [diff] [blame] | 560 | case VK_SNAPSHOT: |
Erik Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 561 | 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 | |
| 595 | std::unique_ptr<KeyboardLayoutMonitor> KeyboardLayoutMonitor::Create( |
Erik Jensen | c08ecf11 | 2019-12-19 00:29:49 | [diff] [blame] | 596 | 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 Jensen | b887667 | 2019-12-03 04:30:05 | [diff] [blame] | 600 | } |
| 601 | |
| 602 | } // namespace remoting |