1 // Copyright 2018 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 <windows.h>
6
7 #include <cstdint>
8 #include <string>
9 #include <utility>
10 #include <vector>
11
12 #include "base/containers/flat_map.h"
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/stl_util.h"
16 #include "ui/events/keycodes/dom/dom_code.h"
17 #include "ui/events/keycodes/dom/dom_key.h"
18 #include "ui/events/keycodes/dom/dom_keyboard_layout_map_base.h"
19 #include "ui/events/keycodes/dom/keycode_converter.h"
20
21 namespace ui {
22
23 namespace {
24
25 class DomKeyboardLayoutMapWin : public DomKeyboardLayoutMapBase {
26 public:
27 DomKeyboardLayoutMapWin();
28 ~DomKeyboardLayoutMapWin() override;
29
30 private:
31 // ui::DomKeyboardLayoutMapBase implementation.
32 uint32_t GetKeyboardLayoutCount() override;
33 ui::DomKey GetDomKeyFromDomCodeForLayout(
34 ui::DomCode dom_code,
35 uint32_t keyboard_layout_index) override;
36
37 // Set of keyboard layout handles provided by the operating system.
38 // The handles stored do not need to be released when the vector is destroyed.
39 std::vector<HKL> keyboard_layout_handles_;
40
41 DISALLOW_COPY_AND_ASSIGN(DomKeyboardLayoutMapWin);
42 };
43
44 DomKeyboardLayoutMapWin::DomKeyboardLayoutMapWin() = default;
45
46 DomKeyboardLayoutMapWin::~DomKeyboardLayoutMapWin() = default;
47
GetKeyboardLayoutCount()48 uint32_t DomKeyboardLayoutMapWin::GetKeyboardLayoutCount() {
49 keyboard_layout_handles_.clear();
50 const size_t keyboard_layout_count = ::GetKeyboardLayoutList(0, nullptr);
51 if (!keyboard_layout_count) {
52 DPLOG(ERROR) << "GetKeyboardLayoutList failed: ";
53 return false;
54 }
55
56 keyboard_layout_handles_.resize(keyboard_layout_count);
57 const size_t copy_count = ::GetKeyboardLayoutList(
58 keyboard_layout_handles_.size(), keyboard_layout_handles_.data());
59 if (!copy_count) {
60 DPLOG(ERROR) << "GetKeyboardLayoutList failed: ";
61 return false;
62 }
63 DCHECK_EQ(keyboard_layout_count, copy_count);
64
65 // The set of layouts returned from GetKeyboardLayoutList does not follow the
66 // the order of the layouts in the control panel so we use GetKeyboardLayout
67 // to retrieve the current layout and swap (if needed) to ensure it is always
68 // evaluated first.
69 auto iter = std::find(keyboard_layout_handles_.begin(),
70 keyboard_layout_handles_.end(), GetKeyboardLayout(0));
71 if (iter != keyboard_layout_handles_.begin() &&
72 iter != keyboard_layout_handles_.end())
73 std::iter_swap(keyboard_layout_handles_.begin(), iter);
74
75 return keyboard_layout_handles_.size();
76 }
77
GetDomKeyFromDomCodeForLayout(ui::DomCode dom_code,uint32_t keyboard_layout_index)78 ui::DomKey DomKeyboardLayoutMapWin::GetDomKeyFromDomCodeForLayout(
79 ui::DomCode dom_code,
80 uint32_t keyboard_layout_index) {
81 DCHECK_NE(dom_code, ui::DomCode::NONE);
82 DCHECK_LT(keyboard_layout_index, keyboard_layout_handles_.size());
83
84 HKL keyboard_layout = keyboard_layout_handles_[keyboard_layout_index];
85 int32_t scan_code = ui::KeycodeConverter::DomCodeToNativeKeycode(dom_code);
86 uint32_t virtual_key_code =
87 MapVirtualKeyEx(scan_code, MAPVK_VSC_TO_VK_EX, keyboard_layout);
88 if (!virtual_key_code) {
89 if (GetLastError() != 0)
90 DPLOG(ERROR) << "MapVirtualKeyEx failed: ";
91 return ui::DomKey::NONE;
92 }
93
94 // Represents a keyboard state with all keys up (i.e. no keys pressed).
95 BYTE keyboard_state[256] = {0};
96
97 // ToUnicodeEx() return value indicates the category for the scan code
98 // passed in for the keyboard layout provided.
99 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms646322(v=vs.85).aspx
100 wchar_t char_buffer[1] = {0};
101 int key_type =
102 ::ToUnicodeEx(virtual_key_code, scan_code, keyboard_state, char_buffer,
103 base::size(char_buffer), /*wFlags=*/0, keyboard_layout);
104
105 // Handle special cases for Japanese keyboard layout.
106 if (0x04110411 == reinterpret_cast<uintptr_t>(keyboard_layout)) {
107 // Fix value for Japanese yen currency symbol.
108 // Windows returns '\' for both IntlRo and IntlYen, even though IntlYen
109 // should be the yen symbol.
110 if (dom_code == ui::DomCode::INTL_YEN)
111 return ui::DomKey::FromCharacter(0x00a5); // Japanese yen symbol.
112
113 // Special case for Backquote.
114 // Technically, this layout is not completely ASCII-capable because the
115 // Backquote key is used as an IME function key (hankaku/zenkaku) and is
116 // thus not a printable key. However, other than this key, it is a perfectly
117 // usable ASCII-capable layout and it matches the values printed on the
118 // keyboard, so we have special handling to allow this key.
119 if (dom_code == ui::DomCode::BACKQUOTE)
120 return ui::DomKey::ZENKAKU_HANKAKU;
121 }
122
123 // Handle special cases for Korean keyboard layout.
124 if (0x04120412 == reinterpret_cast<uintptr_t>(keyboard_layout)) {
125 // Fix value for Korean won currency symbol.
126 // Windows returns '\' for both Backslash and IntlBackslash, even though
127 // IntlBackslash should be the won symbol.
128 if (dom_code == ui::DomCode::INTL_BACKSLASH)
129 return ui::DomKey::FromCharacter(0x20a9); // Korean won symbol.
130 }
131
132 ui::DomKey key = ui::DomKey::NONE;
133 if (key_type == 1)
134 key = ui::DomKey::FromCharacter(char_buffer[0]);
135 else if (key_type == -1) {
136 key = ui::DomKey::DeadKeyFromCombiningCharacter(char_buffer[0]);
137
138 // When we query info about dead keys, the system is left in a state
139 // such that the next key queried is in the context of that dead key.
140 // This causes ToUnicodeEx to return an incorrect result for the second
141 // key. To fix this we query a Space key after any dead key to clear out
142 // the dead key state. See crbug/977609 for details on how this problem
143 // exhibits itself to users.
144 ::ToUnicodeEx(0x0020, 0x0039, keyboard_state, char_buffer,
145 base::size(char_buffer), /*wFlags=*/0, keyboard_layout);
146 }
147 return key;
148 }
149
150 } // namespace
151
152 // static
GenerateDomKeyboardLayoutMap()153 base::flat_map<std::string, std::string> GenerateDomKeyboardLayoutMap() {
154 return DomKeyboardLayoutMapWin().Generate();
155 }
156
157 } // namespace ui
158