| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/sparky/keyboard_util.h" |
| |
| #include "base/containers/fixed_flat_map.h" |
| #include "base/strings/string_util.h" |
| #include "ui/events/keycodes/keyboard_code_conversion.h" |
| #include "ui/events/keycodes/keyboard_codes_posix.h" |
| |
| namespace ash { |
| namespace { |
| |
| // A mapping from the typeable characters on a US keyboard, to a pair describing |
| // how to type that character. The pair contains a) the key code and b) any |
| // modifiers. Modifiers are encoded as a bitset and 0 means 'no modifiers'. |
| constexpr auto kKeyboardCodeForCharacter = |
| base::MakeFixedFlatMap<char, std::pair<ui::KeyboardCode, int>>({ |
| {' ', {ui::VKEY_SPACE, 0}}, |
| {'\t', {ui::VKEY_TAB, 0}}, |
| {'\n', {ui::VKEY_RETURN, 0}}, |
| |
| {'a', {ui::VKEY_A, 0}}, |
| {'b', {ui::VKEY_B, 0}}, |
| {'c', {ui::VKEY_C, 0}}, |
| {'d', {ui::VKEY_D, 0}}, |
| {'e', {ui::VKEY_E, 0}}, |
| {'f', {ui::VKEY_F, 0}}, |
| {'g', {ui::VKEY_G, 0}}, |
| {'h', {ui::VKEY_H, 0}}, |
| {'i', {ui::VKEY_I, 0}}, |
| {'j', {ui::VKEY_J, 0}}, |
| {'k', {ui::VKEY_K, 0}}, |
| {'l', {ui::VKEY_L, 0}}, |
| {'m', {ui::VKEY_M, 0}}, |
| {'n', {ui::VKEY_N, 0}}, |
| {'o', {ui::VKEY_O, 0}}, |
| {'p', {ui::VKEY_P, 0}}, |
| {'q', {ui::VKEY_Q, 0}}, |
| {'r', {ui::VKEY_R, 0}}, |
| {'s', {ui::VKEY_S, 0}}, |
| {'t', {ui::VKEY_T, 0}}, |
| {'u', {ui::VKEY_U, 0}}, |
| {'v', {ui::VKEY_V, 0}}, |
| {'w', {ui::VKEY_W, 0}}, |
| {'x', {ui::VKEY_X, 0}}, |
| {'y', {ui::VKEY_Y, 0}}, |
| {'z', {ui::VKEY_Z, 0}}, |
| |
| {'A', {ui::VKEY_A, ui::EF_SHIFT_DOWN}}, |
| {'B', {ui::VKEY_B, ui::EF_SHIFT_DOWN}}, |
| {'C', {ui::VKEY_C, ui::EF_SHIFT_DOWN}}, |
| {'D', {ui::VKEY_D, ui::EF_SHIFT_DOWN}}, |
| {'E', {ui::VKEY_E, ui::EF_SHIFT_DOWN}}, |
| {'F', {ui::VKEY_F, ui::EF_SHIFT_DOWN}}, |
| {'G', {ui::VKEY_G, ui::EF_SHIFT_DOWN}}, |
| {'H', {ui::VKEY_H, ui::EF_SHIFT_DOWN}}, |
| {'I', {ui::VKEY_I, ui::EF_SHIFT_DOWN}}, |
| {'J', {ui::VKEY_J, ui::EF_SHIFT_DOWN}}, |
| {'K', {ui::VKEY_K, ui::EF_SHIFT_DOWN}}, |
| {'L', {ui::VKEY_L, ui::EF_SHIFT_DOWN}}, |
| {'M', {ui::VKEY_M, ui::EF_SHIFT_DOWN}}, |
| {'N', {ui::VKEY_N, ui::EF_SHIFT_DOWN}}, |
| {'O', {ui::VKEY_O, ui::EF_SHIFT_DOWN}}, |
| {'P', {ui::VKEY_P, ui::EF_SHIFT_DOWN}}, |
| {'Q', {ui::VKEY_Q, ui::EF_SHIFT_DOWN}}, |
| {'R', {ui::VKEY_R, ui::EF_SHIFT_DOWN}}, |
| {'S', {ui::VKEY_S, ui::EF_SHIFT_DOWN}}, |
| {'T', {ui::VKEY_T, ui::EF_SHIFT_DOWN}}, |
| {'U', {ui::VKEY_U, ui::EF_SHIFT_DOWN}}, |
| {'V', {ui::VKEY_V, ui::EF_SHIFT_DOWN}}, |
| {'W', {ui::VKEY_W, ui::EF_SHIFT_DOWN}}, |
| {'X', {ui::VKEY_X, ui::EF_SHIFT_DOWN}}, |
| {'Y', {ui::VKEY_Y, ui::EF_SHIFT_DOWN}}, |
| {'Z', {ui::VKEY_Z, ui::EF_SHIFT_DOWN}}, |
| |
| {'1', {ui::VKEY_1, 0}}, |
| {'2', {ui::VKEY_2, 0}}, |
| {'3', {ui::VKEY_3, 0}}, |
| {'4', {ui::VKEY_4, 0}}, |
| {'5', {ui::VKEY_5, 0}}, |
| {'6', {ui::VKEY_6, 0}}, |
| {'7', {ui::VKEY_7, 0}}, |
| {'8', {ui::VKEY_8, 0}}, |
| {'9', {ui::VKEY_9, 0}}, |
| {'0', {ui::VKEY_0, 0}}, |
| {'-', {ui::VKEY_OEM_MINUS, 0}}, |
| {'=', {ui::VKEY_OEM_PLUS, 0}}, |
| |
| {'!', {ui::VKEY_1, ui::EF_SHIFT_DOWN}}, |
| {'@', {ui::VKEY_2, ui::EF_SHIFT_DOWN}}, |
| {'#', {ui::VKEY_3, ui::EF_SHIFT_DOWN}}, |
| {'$', {ui::VKEY_4, ui::EF_SHIFT_DOWN}}, |
| {'%', {ui::VKEY_5, ui::EF_SHIFT_DOWN}}, |
| {'^', {ui::VKEY_6, ui::EF_SHIFT_DOWN}}, |
| {'&', {ui::VKEY_7, ui::EF_SHIFT_DOWN}}, |
| {'*', {ui::VKEY_8, ui::EF_SHIFT_DOWN}}, |
| {'(', {ui::VKEY_9, ui::EF_SHIFT_DOWN}}, |
| {')', {ui::VKEY_0, ui::EF_SHIFT_DOWN}}, |
| {'_', {ui::VKEY_OEM_MINUS, ui::EF_SHIFT_DOWN}}, |
| {'+', {ui::VKEY_OEM_PLUS, ui::EF_SHIFT_DOWN}}, |
| |
| {'`', {ui::VKEY_OEM_3, 0}}, |
| {',', {ui::VKEY_OEM_COMMA, 0}}, |
| {'.', {ui::VKEY_OEM_PERIOD, 0}}, |
| {'/', {ui::VKEY_OEM_2, 0}}, |
| {';', {ui::VKEY_OEM_1, 0}}, |
| {'\'', {ui::VKEY_OEM_7, 0}}, |
| {'[', {ui::VKEY_OEM_4, 0}}, |
| {']', {ui::VKEY_OEM_6, 0}}, |
| {'\\', {ui::VKEY_OEM_5, 0}}, |
| |
| {'~', {ui::VKEY_OEM_3, ui::EF_SHIFT_DOWN}}, |
| {'<', {ui::VKEY_OEM_COMMA, ui::EF_SHIFT_DOWN}}, |
| {'>', {ui::VKEY_OEM_PERIOD, ui::EF_SHIFT_DOWN}}, |
| {'?', {ui::VKEY_OEM_2, ui::EF_SHIFT_DOWN}}, |
| {':', {ui::VKEY_OEM_1, ui::EF_SHIFT_DOWN}}, |
| {'"', {ui::VKEY_OEM_7, ui::EF_SHIFT_DOWN}}, |
| {'{', {ui::VKEY_OEM_4, ui::EF_SHIFT_DOWN}}, |
| {'}', {ui::VKEY_OEM_6, ui::EF_SHIFT_DOWN}}, |
| {'|', {ui::VKEY_OEM_5, ui::EF_SHIFT_DOWN}}, |
| }); |
| |
| // A mapping from the lowercased versions of a subset of strings defined in: |
| // |
| // https://www.w3.org/TR/uievents-key/ |
| // |
| // to their ui::KeyboardCodes. |
| constexpr auto kKeyboardCodeForDOMString = |
| base::MakeFixedFlatMap<std::string, ui::KeyboardCode>({ |
| {"tab", ui::VKEY_TAB}, |
| {"enter", ui::VKEY_RETURN}, |
| {"space", ui::VKEY_SPACE}, |
| |
| {"arrowleft", ui::VKEY_LEFT}, |
| {"arrowright", ui::VKEY_RIGHT}, |
| {"arrowdown", ui::VKEY_DOWN}, |
| {"arrowup", ui::VKEY_UP}, |
| }); |
| |
| } // namespace |
| |
| std::pair<ui::KeyEvent, ui::KeyEvent> MakeKeyEventPair( |
| ui::KeyboardCode key_code, |
| bool control, |
| bool alt, |
| bool shift) { |
| const auto dom_code = ui::UsLayoutKeyboardCodeToDomCode(key_code); |
| |
| int modifiers = 0; |
| if (control) { |
| modifiers |= ui::EF_CONTROL_DOWN; |
| } |
| if (alt) { |
| modifiers |= ui::EF_ALT_DOWN; |
| } |
| if (shift) { |
| modifiers |= ui::EF_SHIFT_DOWN; |
| } |
| |
| return { |
| ui::KeyEvent(ui::EventType::kKeyPressed, key_code, dom_code, modifiers), |
| ui::KeyEvent(ui::EventType::kKeyReleased, key_code, dom_code, modifiers)}; |
| } |
| |
| std::optional<std::vector<ui::KeyEvent>> KeyEventsForText( |
| const std::string& text) { |
| std::vector<ui::KeyEvent> events; |
| for (const char& character : text) { |
| const auto it = kKeyboardCodeForCharacter.find(character); |
| if (it == kKeyboardCodeForCharacter.end()) { |
| return std::nullopt; |
| } |
| |
| const auto key_code = it->second.first; |
| const auto modifier = it->second.second; |
| const auto pressed_released = |
| MakeKeyEventPair(key_code, false, false, modifier); |
| |
| events.push_back(pressed_released.first); |
| events.push_back(pressed_released.second); |
| } |
| |
| return events; |
| } |
| |
| std::optional<ui::KeyboardCode> KeyboardCodeForDOMString( |
| const std::string& key) { |
| if (!base::IsStringASCII(key)) { |
| return std::nullopt; |
| } |
| |
| const auto it = kKeyboardCodeForDOMString.find(base::ToLowerASCII(key)); |
| if (it != kKeyboardCodeForDOMString.end()) { |
| return it->second; |
| } |
| return std::nullopt; |
| } |
| |
| } // namespace ash |