OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2016 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 "ui/events/keycodes/key_map_win.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #include "base/logging.h" | |
10 #include "base/macros.h" | |
11 #include "base/threading/thread_local_storage.h" | |
12 | |
13 #include "ui/events/event_constants.h" | |
14 #include "ui/events/keycodes/dom/dom_code.h" | |
15 | |
16 namespace ui { | |
17 | |
18 namespace { | |
19 | |
20 struct DomCodeEntry { | |
21 DomCode dom_code; | |
22 int scan_code; | |
23 }; | |
24 #define USB_KEYMAP_DECLARATION const DomCodeEntry supported_dom_code_list[] = | |
25 #define USB_KEYMAP(usb, evdev, xkb, win, mac, code, id) {DomCode::id, win} | |
26 #include "ui/events/keycodes/dom/keycode_converter_data.inc" | |
27 #undef USB_KEYMAP | |
28 #undef USB_KEYMAP_DECLARATION | |
29 | |
30 // List of modifiers mentioned in https://w3c.github.io/uievents/#keys-modifiers | |
31 // Some modifiers are commented out because they usually don't change keys. | |
32 const EventFlags modifier_flags[] = { | |
33 EF_SHIFT_DOWN, | |
34 EF_CONTROL_DOWN, | |
35 EF_ALT_DOWN, | |
36 // EF_COMMAND_DOWN, | |
37 EF_ALTGR_DOWN, | |
38 // EF_NUM_LOCK_ON, | |
39 EF_CAPS_LOCK_ON, | |
40 // EF_SCROLL_LOCK_ON | |
41 }; | |
42 const int kModifierFlagsCombinations = (1 << arraysize(modifier_flags)) - 1; | |
43 | |
44 int GetModifierFlags(int combination) { | |
45 int flags = EF_NONE; | |
46 for (int i = 0; i < arraysize(modifier_flags); ++i) { | |
47 if (combination & (1 << i)) | |
48 flags |= modifier_flags[i]; | |
49 } | |
50 return flags; | |
51 } | |
52 | |
53 void SetModifierState(BYTE* keyboard_state, int flags) { | |
54 if (flags & EF_SHIFT_DOWN) | |
55 keyboard_state[VK_SHIFT] |= 0x80; | |
56 | |
57 if (flags & EF_CONTROL_DOWN) | |
58 keyboard_state[VK_CONTROL] |= 0x80; | |
59 | |
60 if (flags & EF_ALT_DOWN) | |
61 keyboard_state[VK_MENU] |= 0x80; | |
62 | |
63 if (flags & EF_ALTGR_DOWN) { | |
64 // AltGr should be RightAlt+LeftControl within Windows, but actually only | |
65 // the non-located keys will work here. | |
66 keyboard_state[VK_MENU] |= 0x80; | |
67 keyboard_state[VK_CONTROL] |= 0x80; | |
68 } | |
69 | |
70 if (flags & EF_COMMAND_DOWN) | |
71 keyboard_state[VK_LWIN] |= 0x80; | |
72 | |
73 if (flags & EF_NUM_LOCK_ON) | |
74 keyboard_state[VK_NUMLOCK] |= 0x01; | |
75 | |
76 if (flags & EF_CAPS_LOCK_ON) | |
77 keyboard_state[VK_CAPITAL] |= 0x01; | |
78 | |
79 if (flags & EF_SCROLL_LOCK_ON) | |
80 keyboard_state[VK_SCROLL] |= 0x01; | |
81 } | |
82 | |
83 // Use TLS because KeyboardLayout is per thread. | |
84 // However usually WindowsKeyMap will be used by the host application, which is | |
85 // just one process and one thread. | |
86 static base::ThreadLocalStorage::StaticSlot s_tls_key_map = TLS_INITIALIZER; | |
chongz
2016/02/10 01:25:10
I'm not sure I'm using it in the right way?
dtapuska
2016/02/10 21:56:34
From the other patterns this seems fine; although
| |
87 | |
88 void ThreadCleanupFunction(void* data) { | |
89 WindowsKeyMap* key_map = static_cast<WindowsKeyMap*>(data); | |
90 if (key_map) | |
91 delete key_map; | |
92 } | |
93 | |
94 } // anonymous namespace | |
95 | |
96 DomKey WindowsKeyMap::DomCodeAndFlagsToKey(DomCode code, int flags) const { | |
97 const int flags_to_try[] = { | |
98 // Trying to match Firefox's behavior and UIEvents DomKey guidelines. | |
99 // If the combination doesn't produce a printable character, the key value | |
100 // should be the key with no modifiers except for Shift and AltGr. | |
101 // See https://w3c.github.io/uievents/#keys-guidelines | |
102 flags, | |
103 flags & (EF_SHIFT_DOWN | EF_ALTGR_DOWN | EF_CAPS_LOCK_ON), | |
104 flags & (EF_SHIFT_DOWN | EF_CAPS_LOCK_ON), | |
105 EF_NONE, | |
106 }; | |
107 | |
108 DomKey key = DomKey::NONE; | |
109 for (auto try_flags : flags_to_try) { | |
110 const auto& it = | |
111 key_map_.find(std::make_pair(static_cast<int>(code), try_flags)); | |
112 if (it != key_map_.end()) { | |
113 key = it->second; | |
114 if (key != DomKey::NONE) | |
115 break; | |
116 } | |
117 } | |
118 return key; | |
119 } | |
120 | |
121 // static | |
122 DomKey WindowsKeyMap::CodeAndFlagsToDomKey(DomCode code, int flags) { | |
123 if (!s_tls_key_map.initialized()) { | |
124 s_tls_key_map.Initialize(ThreadCleanupFunction); | |
chongz
2016/02/10 01:25:10
Should I add a lock here? Currently it's only used
| |
125 } | |
126 WindowsKeyMap* tls_key_map = static_cast<WindowsKeyMap*>(s_tls_key_map.Get()); | |
127 if (!tls_key_map) { | |
128 tls_key_map = new WindowsKeyMap(); | |
129 s_tls_key_map.Set(tls_key_map); | |
130 } | |
131 | |
132 HKL current_layout = ::GetKeyboardLayout(0); | |
133 tls_key_map->UpdateLayout(current_layout); | |
134 return tls_key_map->DomCodeAndFlagsToKey(code, flags); | |
135 } | |
136 | |
137 void WindowsKeyMap::UpdateLayout(HKL layout) { | |
138 if (layout == keyboard_layout_) | |
139 return; | |
140 | |
141 keyboard_layout_ = layout; | |
142 key_map_.clear(); | |
143 // Usually the map has ~1000 entries. | |
144 key_map_.reserve(1000); | |
chongz
2016/02/10 01:25:10
Could be less if we can use some fallback for the
| |
145 | |
146 BYTE keyboard_state_to_restore[256]; | |
147 ::GetKeyboardState(keyboard_state_to_restore); | |
148 | |
149 for (int eindex = 0; eindex <= kModifierFlagsCombinations; ++eindex) { | |
150 BYTE keyboard_state[256]; | |
151 memset(keyboard_state, 0, sizeof(keyboard_state)); | |
152 int flags = GetModifierFlags(eindex); | |
153 SetModifierState(keyboard_state, flags); | |
154 for (const auto& dom_code_entry : supported_dom_code_list) { | |
155 wchar_t translated_chars[5]; | |
156 int key_code = ::MapVirtualKeyEx(dom_code_entry.scan_code, | |
157 MAPVK_VSC_TO_VK, keyboard_layout_); | |
158 int rv = ::ToUnicodeEx(key_code, 0, keyboard_state, translated_chars, | |
159 arraysize(translated_chars), 0, keyboard_layout_); | |
160 | |
161 if (rv < 0) { | |
162 // Dead key, injecting VK_SPACE to get character representation. | |
163 BYTE empty_state[256]; | |
164 memset(empty_state, 0, sizeof(empty_state)); | |
165 rv = ::ToUnicodeEx(VK_SPACE, 0, empty_state, translated_chars, | |
166 arraysize(translated_chars), 0, keyboard_layout_); | |
167 // Must be a repeat of dead key character. | |
168 DCHECK(rv == 2); | |
169 key_map_[std::make_pair(static_cast<int>(dom_code_entry.dom_code), | |
170 flags)] = | |
171 DomKey::DeadKeyFromCombiningCharacter(translated_chars[0]); | |
172 } else if (rv == 1) { | |
173 if (translated_chars[0] >= 0x20) { | |
174 key_map_[std::make_pair(static_cast<int>(dom_code_entry.dom_code), | |
175 flags)] = | |
176 DomKey::FromCharacter(translated_chars[0]); | |
177 } else { | |
178 // Ignores legacy non-printable control characters. | |
179 } | |
180 } else { | |
181 // TODO(chongz): Handle multiple characters case. | |
182 } | |
183 } | |
184 } | |
185 ::SetKeyboardState(keyboard_state_to_restore); | |
186 } | |
187 | |
188 } // namespace ui | |
OLD | NEW |