// 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