Index: ui/events/keycodes/key_map_win.cc |
diff --git a/ui/events/keycodes/key_map_win.cc b/ui/events/keycodes/key_map_win.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..57ecee0cfd48599717cae26c2d57f5e0c2bcbe82 |
--- /dev/null |
+++ b/ui/events/keycodes/key_map_win.cc |
@@ -0,0 +1,188 @@ |
+// Copyright (c) 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ui/events/keycodes/key_map_win.h" |
+ |
+#include <utility> |
+ |
+#include "base/logging.h" |
+#include "base/macros.h" |
+#include "base/threading/thread_local_storage.h" |
+ |
+#include "ui/events/event_constants.h" |
+#include "ui/events/keycodes/dom/dom_code.h" |
+ |
+namespace ui { |
+ |
+namespace { |
+ |
+struct DomCodeEntry { |
+ DomCode dom_code; |
+ int scan_code; |
+}; |
+#define USB_KEYMAP_DECLARATION const DomCodeEntry supported_dom_code_list[] = |
+#define USB_KEYMAP(usb, evdev, xkb, win, mac, code, id) {DomCode::id, win} |
+#include "ui/events/keycodes/dom/keycode_converter_data.inc" |
+#undef USB_KEYMAP |
+#undef USB_KEYMAP_DECLARATION |
+ |
+// List of modifiers mentioned in https://w3c.github.io/uievents/#keys-modifiers |
+// Some modifiers are commented out because they usually don't change keys. |
+const EventFlags modifier_flags[] = { |
+ EF_SHIFT_DOWN, |
+ EF_CONTROL_DOWN, |
+ EF_ALT_DOWN, |
+ // EF_COMMAND_DOWN, |
+ EF_ALTGR_DOWN, |
+ // EF_NUM_LOCK_ON, |
+ EF_CAPS_LOCK_ON, |
+ // EF_SCROLL_LOCK_ON |
+}; |
+const int kModifierFlagsCombinations = (1 << arraysize(modifier_flags)) - 1; |
+ |
+int GetModifierFlags(int combination) { |
+ int flags = EF_NONE; |
+ for (int i = 0; i < arraysize(modifier_flags); ++i) { |
+ if (combination & (1 << i)) |
+ flags |= modifier_flags[i]; |
+ } |
+ return flags; |
+} |
+ |
+void SetModifierState(BYTE* keyboard_state, int flags) { |
+ if (flags & EF_SHIFT_DOWN) |
+ keyboard_state[VK_SHIFT] |= 0x80; |
+ |
+ if (flags & EF_CONTROL_DOWN) |
+ keyboard_state[VK_CONTROL] |= 0x80; |
+ |
+ if (flags & EF_ALT_DOWN) |
+ keyboard_state[VK_MENU] |= 0x80; |
+ |
+ if (flags & EF_ALTGR_DOWN) { |
+ // AltGr should be RightAlt+LeftControl within Windows, but actually only |
+ // the non-located keys will work here. |
+ keyboard_state[VK_MENU] |= 0x80; |
+ keyboard_state[VK_CONTROL] |= 0x80; |
+ } |
+ |
+ if (flags & EF_COMMAND_DOWN) |
+ keyboard_state[VK_LWIN] |= 0x80; |
+ |
+ if (flags & EF_NUM_LOCK_ON) |
+ keyboard_state[VK_NUMLOCK] |= 0x01; |
+ |
+ if (flags & EF_CAPS_LOCK_ON) |
+ keyboard_state[VK_CAPITAL] |= 0x01; |
+ |
+ if (flags & EF_SCROLL_LOCK_ON) |
+ keyboard_state[VK_SCROLL] |= 0x01; |
+} |
+ |
+// Use TLS because KeyboardLayout is per thread. |
+// However usually WindowsKeyMap will be used by the host application, which is |
+// just one process and one thread. |
+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
|
+ |
+void ThreadCleanupFunction(void* data) { |
+ WindowsKeyMap* key_map = static_cast<WindowsKeyMap*>(data); |
+ if (key_map) |
+ delete key_map; |
+} |
+ |
+} // anonymous namespace |
+ |
+DomKey WindowsKeyMap::DomCodeAndFlagsToKey(DomCode code, int flags) const { |
+ const int flags_to_try[] = { |
+ // Trying to match Firefox's behavior and UIEvents DomKey guidelines. |
+ // If the combination doesn't produce a printable character, the key value |
+ // should be the key with no modifiers except for Shift and AltGr. |
+ // See https://w3c.github.io/uievents/#keys-guidelines |
+ flags, |
+ flags & (EF_SHIFT_DOWN | EF_ALTGR_DOWN | EF_CAPS_LOCK_ON), |
+ flags & (EF_SHIFT_DOWN | EF_CAPS_LOCK_ON), |
+ EF_NONE, |
+ }; |
+ |
+ DomKey key = DomKey::NONE; |
+ for (auto try_flags : flags_to_try) { |
+ const auto& it = |
+ key_map_.find(std::make_pair(static_cast<int>(code), try_flags)); |
+ if (it != key_map_.end()) { |
+ key = it->second; |
+ if (key != DomKey::NONE) |
+ break; |
+ } |
+ } |
+ return key; |
+} |
+ |
+// static |
+DomKey WindowsKeyMap::CodeAndFlagsToDomKey(DomCode code, int flags) { |
+ if (!s_tls_key_map.initialized()) { |
+ s_tls_key_map.Initialize(ThreadCleanupFunction); |
chongz
2016/02/10 01:25:10
Should I add a lock here? Currently it's only used
|
+ } |
+ WindowsKeyMap* tls_key_map = static_cast<WindowsKeyMap*>(s_tls_key_map.Get()); |
+ if (!tls_key_map) { |
+ tls_key_map = new WindowsKeyMap(); |
+ s_tls_key_map.Set(tls_key_map); |
+ } |
+ |
+ HKL current_layout = ::GetKeyboardLayout(0); |
+ tls_key_map->UpdateLayout(current_layout); |
+ return tls_key_map->DomCodeAndFlagsToKey(code, flags); |
+} |
+ |
+void WindowsKeyMap::UpdateLayout(HKL layout) { |
+ if (layout == keyboard_layout_) |
+ return; |
+ |
+ keyboard_layout_ = layout; |
+ key_map_.clear(); |
+ // Usually the map has ~1000 entries. |
+ key_map_.reserve(1000); |
chongz
2016/02/10 01:25:10
Could be less if we can use some fallback for the
|
+ |
+ BYTE keyboard_state_to_restore[256]; |
+ ::GetKeyboardState(keyboard_state_to_restore); |
+ |
+ for (int eindex = 0; eindex <= kModifierFlagsCombinations; ++eindex) { |
+ BYTE keyboard_state[256]; |
+ memset(keyboard_state, 0, sizeof(keyboard_state)); |
+ int flags = GetModifierFlags(eindex); |
+ SetModifierState(keyboard_state, flags); |
+ for (const auto& dom_code_entry : supported_dom_code_list) { |
+ wchar_t translated_chars[5]; |
+ int key_code = ::MapVirtualKeyEx(dom_code_entry.scan_code, |
+ MAPVK_VSC_TO_VK, keyboard_layout_); |
+ int rv = ::ToUnicodeEx(key_code, 0, keyboard_state, translated_chars, |
+ arraysize(translated_chars), 0, keyboard_layout_); |
+ |
+ if (rv < 0) { |
+ // Dead key, injecting VK_SPACE to get character representation. |
+ BYTE empty_state[256]; |
+ memset(empty_state, 0, sizeof(empty_state)); |
+ rv = ::ToUnicodeEx(VK_SPACE, 0, empty_state, translated_chars, |
+ arraysize(translated_chars), 0, keyboard_layout_); |
+ // Must be a repeat of dead key character. |
+ DCHECK(rv == 2); |
+ key_map_[std::make_pair(static_cast<int>(dom_code_entry.dom_code), |
+ flags)] = |
+ DomKey::DeadKeyFromCombiningCharacter(translated_chars[0]); |
+ } else if (rv == 1) { |
+ if (translated_chars[0] >= 0x20) { |
+ key_map_[std::make_pair(static_cast<int>(dom_code_entry.dom_code), |
+ flags)] = |
+ DomKey::FromCharacter(translated_chars[0]); |
+ } else { |
+ // Ignores legacy non-printable control characters. |
+ } |
+ } else { |
+ // TODO(chongz): Handle multiple characters case. |
+ } |
+ } |
+ } |
+ ::SetKeyboardState(keyboard_state_to_restore); |
+} |
+ |
+} // namespace ui |