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" |
| 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 | |
| 32 | namespace remoting { |
| 33 | |
| 34 | namespace { |
| 35 | |
| 36 | constexpr base::TimeDelta POLL_INTERVAL = |
| 37 | base::TimeDelta::FromMilliseconds(1000); |
| 38 | |
| 39 | class 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 | |
| 67 | KeyboardLayoutMonitorWin::KeyboardLayoutMonitorWin( |
| 68 | base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback) |
| 69 | : callback_(std::move(callback)) {} |
| 70 | |
| 71 | KeyboardLayoutMonitorWin::~KeyboardLayoutMonitorWin() = default; |
| 72 | |
| 73 | void KeyboardLayoutMonitorWin::Start() { |
| 74 | timer_.Start(FROM_HERE, POLL_INTERVAL, this, |
| 75 | &KeyboardLayoutMonitorWin::QueryLayout); |
| 76 | } |
| 77 | |
| 78 | void 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 | |
| 206 | void 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 | |
| 218 | bool 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 | |
| 248 | UINT 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 | |
| 284 | protocol::LayoutKeyFunction |
| 285 | KeyboardLayoutMonitorWin::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 | |
| 423 | std::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 |