| // Copyright 2014 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/ozone/layout/xkb/xkb_keyboard_layout_engine.h" |
| |
| #include <stddef.h> |
| #include <xkbcommon/xkbcommon-names.h> |
| |
| #include <algorithm> |
| |
| #include "base/bind.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/memory/free_deleter.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/task/post_task.h" |
| #include "base/task_runner.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "build/build_config.h" |
| #include "ui/events/event_constants.h" |
| #include "ui/events/keycodes/dom/dom_code.h" |
| #include "ui/events/keycodes/dom/dom_key.h" |
| #include "ui/events/keycodes/dom/keycode_converter.h" |
| #include "ui/events/keycodes/keyboard_code_conversion.h" |
| #include "ui/events/keycodes/keyboard_code_conversion_xkb.h" |
| |
| namespace ui { |
| |
| namespace { |
| |
| typedef base::OnceCallback<void(const std::string&, |
| std::unique_ptr<char, base::FreeDeleter>)> |
| LoadKeymapCallback; |
| |
| KeyboardCode AlphanumericKeyboardCode(xkb_keysym_t xkb_keysym, |
| base::char16 character) { |
| // Plain ASCII letters and digits map directly to VKEY values. |
| if ((character >= '0') && (character <= '9')) { |
| int zero = ((xkb_keysym >= XKB_KEY_KP_0) && (xkb_keysym <= XKB_KEY_KP_9)) |
| ? VKEY_NUMPAD0 |
| : VKEY_0; |
| return static_cast<KeyboardCode>(zero + character - '0'); |
| } |
| if ((character >= 'a') && (character <= 'z')) |
| return static_cast<KeyboardCode>(VKEY_A + character - 'a'); |
| if ((character >= 'A') && (character <= 'Z')) |
| return static_cast<KeyboardCode>(VKEY_A + character - 'A'); |
| return VKEY_UNKNOWN; |
| } |
| |
| // These tables map layout-dependent printable characters/codes |
| // to legacy Windows-based VKEY values. |
| // |
| // VKEYs are determined by the character produced from a DomCode without |
| // any modifiers, plus zero or more of the DomCode itself, the character |
| // produced with the Shift modifier, and the character produced with the |
| // AltGr modifier. |
| |
| // A table of one or more PrintableSubEntry cases applies when the VKEY is |
| // not determined by the unmodified character value alone. Each such table |
| // corresponds to one unmodified character value. For an entry to match, |
| // the dom_code must match, and, if test_X is set, then the character for |
| // the key plus modifier X must also match. |
| struct PrintableSubEntry { |
| DomCode dom_code; |
| bool test_shift : 1; |
| bool test_altgr : 1; |
| base::char16 shift_character; |
| base::char16 altgr_character; |
| KeyboardCode key_code; |
| }; |
| |
| // The two designated Unicode "not-a-character" values are used as sentinels. |
| const base::char16 kNone = 0xFFFE; |
| const base::char16 kAny = 0xFFFF; |
| |
| // U+0021 exclamation mark |
| const PrintableSubEntry kU0021[] = { |
| {DomCode::DIGIT1, 0, 0, kAny, kAny, VKEY_1}, |
| {DomCode::DIGIT8, 0, 0, kAny, kAny, VKEY_8}, |
| {DomCode::SLASH, 0, 0, kAny, kAny, VKEY_OEM_8}}; |
| |
| // U+0022 quote |
| const PrintableSubEntry kU0022[] = { |
| {DomCode::DIGIT2, 0, 0, kAny, kAny, VKEY_2}, |
| {DomCode::DIGIT3, 0, 0, kAny, kAny, VKEY_3}}; |
| |
| // U+0023 number sign |
| const PrintableSubEntry kU0023[] = { |
| {DomCode::BACKQUOTE, 0, 0, kAny, kAny, VKEY_OEM_7}, |
| {DomCode::BACKSLASH, 1, 0, 0x0027, kAny, VKEY_OEM_2}, // apostrophe |
| {DomCode::BACKSLASH, 1, 0, 0x007E, kAny, VKEY_OEM_7}}; // ~, NoSymbol |
| |
| // U+0024 dollar sign |
| const PrintableSubEntry kU0024[] = { |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_1}, |
| {DomCode::BACKSLASH, 0, 0, kAny, kAny, VKEY_OEM_8}}; |
| |
| // U+0027 apostrophe |
| const PrintableSubEntry kU0027[] = { |
| {DomCode::DIGIT4, 0, 0, kAny, kAny, VKEY_4}, |
| {DomCode::US_Q, 0, 0, kAny, kAny, VKEY_OEM_7}, |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_1}, |
| {DomCode::SLASH, 0, 0, kAny, kAny, VKEY_OEM_7}, |
| {DomCode::QUOTE, 1, 0, 0x0022, kAny, VKEY_OEM_7}, // quote |
| {DomCode::BACKQUOTE, 1, 0, 0x0022, kAny, VKEY_OEM_3}, // quote |
| {DomCode::BACKQUOTE, 1, 0, 0x00B7, kAny, VKEY_OEM_5}, // middle dot |
| {DomCode::BACKSLASH, 1, 0, kNone, kAny, VKEY_OEM_5}, // NoSymbol |
| {DomCode::MINUS, 1, 0, 0x003F, kAny, VKEY_OEM_4}, // ? |
| {DomCode::EQUAL, 1, 0, 0x002A, kAny, VKEY_OEM_PLUS}, // * |
| {DomCode::QUOTE, 1, 0, 0x0040, kAny, VKEY_OEM_3}, // @ |
| {DomCode::BACKSLASH, 1, 1, 0x002A, 0x00BD, VKEY_OEM_5}, // *, one half |
| {DomCode::BACKSLASH, 1, 0, 0x002A, kAny, VKEY_OEM_2}, // *, NoSymbol |
| {DomCode::US_Z, 1, 1, 0x0022, 0x0158, VKEY_OEM_7}, // quote, R caron |
| {DomCode::US_Z, 1, 0, 0x0022, kAny, VKEY_Z}}; // quote |
| |
| // U+0028 left parenthesis |
| const PrintableSubEntry kU0028[] = { |
| {DomCode::DIGIT5, 0, 0, kAny, kAny, VKEY_5}, |
| {DomCode::DIGIT9, 0, 0, kAny, kAny, VKEY_9}, |
| {DomCode::BACKSLASH, 0, 0, kAny, kAny, VKEY_OEM_5}}; |
| |
| // U+0029 right parenthesis |
| const PrintableSubEntry kU0029[] = { |
| {DomCode::DIGIT0, 0, 0, kAny, kAny, VKEY_0}, |
| {DomCode::MINUS, 0, 0, kAny, kAny, VKEY_OEM_4}, |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_6}}; |
| |
| // U+002A * |
| const PrintableSubEntry kU002A[] = { |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_1}, |
| {DomCode::BACKSLASH, 0, 0, kAny, kAny, VKEY_OEM_5}}; |
| |
| // U+002B plus sign |
| const PrintableSubEntry kU002B[] = { |
| {DomCode::DIGIT1, 0, 0, kAny, kAny, VKEY_1}, |
| {DomCode::EQUAL, 0, 0, kAny, kAny, VKEY_OEM_PLUS}, |
| {DomCode::BRACKET_LEFT, 0, 0, kAny, kAny, VKEY_OEM_PLUS}, |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_PLUS}, |
| {DomCode::SEMICOLON, 0, 0, kAny, kAny, VKEY_OEM_PLUS}, |
| {DomCode::BACKSLASH, 0, 0, kAny, kAny, VKEY_OEM_2}, |
| {DomCode::MINUS, 1, 0, 0x003F, kAny, VKEY_OEM_PLUS}}; // ? |
| |
| // U+002C comma |
| const PrintableSubEntry kU002C[] = { |
| {DomCode::DIGIT3, 0, 0, kAny, kAny, VKEY_3}, |
| {DomCode::DIGIT5, 0, 0, kAny, kAny, VKEY_5}, |
| {DomCode::DIGIT6, 0, 0, kAny, kAny, VKEY_6}, |
| {DomCode::DIGIT9, 0, 0, kAny, kAny, VKEY_9}, |
| {DomCode::US_W, 0, 0, kAny, kAny, VKEY_OEM_COMMA}, |
| {DomCode::US_V, 0, 0, kAny, kAny, VKEY_OEM_COMMA}, |
| {DomCode::US_M, 0, 0, kAny, kAny, VKEY_OEM_COMMA}, |
| {DomCode::COMMA, 0, 0, kAny, kAny, VKEY_OEM_COMMA}}; |
| |
| // U+002D hyphen-minus |
| const PrintableSubEntry kU002D[] = { |
| {DomCode::DIGIT2, 0, 0, kAny, kAny, VKEY_2}, |
| {DomCode::DIGIT6, 0, 0, kAny, kAny, VKEY_6}, |
| {DomCode::MINUS, 0, 0, kAny, kAny, VKEY_OEM_MINUS}, |
| {DomCode::US_A, 0, 0, kAny, kAny, VKEY_OEM_MINUS}, |
| {DomCode::QUOTE, 0, 0, kAny, kAny, VKEY_OEM_MINUS}, |
| {DomCode::SLASH, 1, 0, 0x003D, kAny, VKEY_OEM_MINUS}, // = |
| {DomCode::EQUAL, 1, 1, 0x005F, 0x0157, VKEY_OEM_4}, // _, r cedilla |
| {DomCode::EQUAL, 1, 0, 0x005F, kAny, VKEY_OEM_MINUS}, // _ |
| {DomCode::SLASH, 1, 1, 0x005F, 0x002F, VKEY_OEM_2}, // _, / |
| {DomCode::SLASH, 1, 0, 0x005F, kAny, VKEY_OEM_MINUS}}; // _ |
| |
| // U+002E full stop |
| const PrintableSubEntry kU002E[] = { |
| {DomCode::DIGIT7, 0, 0, kAny, kAny, VKEY_7}, |
| {DomCode::DIGIT8, 0, 0, kAny, kAny, VKEY_8}, |
| {DomCode::US_E, 0, 0, kAny, kAny, VKEY_OEM_PERIOD}, |
| {DomCode::US_R, 0, 0, kAny, kAny, VKEY_OEM_PERIOD}, |
| {DomCode::US_O, 0, 0, kAny, kAny, VKEY_OEM_PERIOD}, |
| {DomCode::QUOTE, 0, 0, kAny, kAny, VKEY_OEM_7}, |
| {DomCode::PERIOD, 0, 0, kAny, kAny, VKEY_OEM_PERIOD}, |
| {DomCode::SLASH, 0, 0, kAny, kAny, VKEY_OEM_2}}; |
| |
| // U+002F / |
| const PrintableSubEntry kU002F[] = { |
| {DomCode::MINUS, 0, 0, kAny, kAny, VKEY_OEM_4}, |
| {DomCode::BRACKET_LEFT, 0, 0, kAny, kAny, VKEY_OEM_2}, |
| {DomCode::BACKQUOTE, 0, 0, kAny, kAny, VKEY_OEM_7}, |
| {DomCode::BACKSLASH, 0, 0, kAny, kAny, VKEY_OEM_5}, |
| {DomCode::SLASH, 0, 0, kAny, kAny, VKEY_OEM_2}, |
| {DomCode::DIGIT3, 1, 0, 0x0033, kAny, VKEY_3}, // 3 |
| {DomCode::DIGIT3, 1, 0, 0x003F, kAny, VKEY_OEM_2}, // ? |
| {DomCode::DIGIT0, 1, 0, 0x0030, kAny, VKEY_0}, // 0 |
| {DomCode::DIGIT0, 1, 0, 0x003F, kAny, VKEY_OEM_2}}; // ? |
| |
| // U+003A colon |
| const PrintableSubEntry kU003A[] = { |
| {DomCode::DIGIT1, 0, 0, kAny, kAny, VKEY_1}, |
| {DomCode::DIGIT5, 0, 0, kAny, kAny, VKEY_5}, |
| {DomCode::DIGIT6, 0, 0, kAny, kAny, VKEY_6}, |
| {DomCode::PERIOD, 0, 0, kAny, kAny, VKEY_OEM_2}}; |
| |
| // U+003B semicolon |
| const PrintableSubEntry kU003B[] = { |
| {DomCode::DIGIT4, 0, 0, kAny, kAny, VKEY_4}, |
| {DomCode::DIGIT8, 0, 0, kAny, kAny, VKEY_8}, |
| {DomCode::US_Q, 0, 0, kAny, kAny, VKEY_OEM_1}, |
| {DomCode::BRACKET_LEFT, 0, 0, kAny, kAny, VKEY_OEM_1}, |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_6}, |
| {DomCode::SEMICOLON, 0, 0, kAny, kAny, VKEY_OEM_1}, |
| {DomCode::BACKQUOTE, 0, 0, kAny, kAny, VKEY_OEM_3}, |
| {DomCode::US_Z, 0, 0, kAny, kAny, VKEY_OEM_1}, |
| {DomCode::COMMA, 0, 0, kAny, kAny, VKEY_OEM_PERIOD}, |
| {DomCode::SLASH, 0, 0, kAny, kAny, VKEY_OEM_2}}; |
| // U+003D = |
| const PrintableSubEntry kU003D[] = { |
| {DomCode::DIGIT8, 0, 0, kAny, kAny, VKEY_8}, |
| {DomCode::EQUAL, 0, 0, kAny, kAny, VKEY_OEM_PLUS}, |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_PLUS}, |
| {DomCode::SLASH, 1, 0, 0x0025, kAny, VKEY_OEM_8}, // % |
| {DomCode::SLASH, 1, 0, 0x002B, kAny, VKEY_OEM_PLUS}, // + |
| {DomCode::MINUS, 1, 1, 0x0025, 0x002D, VKEY_OEM_MINUS}, // %, - |
| {DomCode::MINUS, 1, 0, 0x0025, kAny, VKEY_OEM_PLUS}}; // %, NoSymbol |
| |
| // U+003F ? |
| const PrintableSubEntry kU003F[] = { |
| {DomCode::DIGIT2, 0, 0, kAny, kAny, VKEY_2}, |
| {DomCode::DIGIT7, 0, 0, kAny, kAny, VKEY_7}, |
| {DomCode::DIGIT8, 0, 0, kAny, kAny, VKEY_8}, |
| {DomCode::MINUS, 0, 0, kAny, kAny, VKEY_OEM_PLUS}}; |
| |
| // U+0040 @ |
| const PrintableSubEntry kU0040[] = { |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_6}, |
| {DomCode::BACKQUOTE, 0, 0, kAny, kAny, VKEY_OEM_7}}; |
| |
| // U+005B left square bracket |
| const PrintableSubEntry kU005B[] = { |
| {DomCode::DIGIT1, 0, 0, kAny, kAny, VKEY_OEM_4}, |
| {DomCode::MINUS, 0, 0, kAny, kAny, VKEY_OEM_4}, |
| {DomCode::BRACKET_LEFT, 0, 0, kAny, kAny, VKEY_OEM_4}, |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_6}, |
| {DomCode::QUOTE, 0, 0, kAny, kAny, VKEY_OEM_7}}; |
| |
| // U+005C backslash |
| const PrintableSubEntry kU005C[] = { |
| {DomCode::EQUAL, 0, 0, kAny, kAny, VKEY_OEM_4}, |
| {DomCode::BACKSLASH, 1, 0, 0x002F, kAny, VKEY_OEM_7}, // / |
| {DomCode::BACKSLASH, 1, 0, 0x007C, kAny, VKEY_OEM_5}, // | |
| {DomCode::BACKQUOTE, 1, 1, 0x007C, 0x0031, VKEY_OEM_5}, // |, 1 |
| {DomCode::BACKQUOTE, 1, 1, 0x007C, 0x0145, VKEY_OEM_3}}; // |, N cedilla |
| |
| // U+005D right square bracket |
| const PrintableSubEntry kU005D[] = { |
| {DomCode::DIGIT2, 0, 0, kAny, kAny, VKEY_OEM_6}, |
| {DomCode::EQUAL, 0, 0, kAny, kAny, VKEY_OEM_6}, |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_6}, |
| {DomCode::BACKQUOTE, 0, 0, kAny, kAny, VKEY_OEM_3}, |
| {DomCode::BACKSLASH, 0, 0, kAny, kAny, VKEY_OEM_5}}; |
| |
| // U+005F _ |
| const PrintableSubEntry kU005F[] = { |
| {DomCode::DIGIT8, 0, 0, kAny, kAny, VKEY_8}, |
| {DomCode::MINUS, 0, 0, kAny, kAny, VKEY_OEM_MINUS}}; |
| |
| // U+0060 grave accent |
| const PrintableSubEntry kU0060[] = { |
| {DomCode::BACKQUOTE, 1, 0, kNone, kAny, VKEY_OEM_3}, // NoSymbol |
| {DomCode::BACKQUOTE, 1, 0, 0x00AC, kAny, VKEY_OEM_8}, // not |
| {DomCode::BACKQUOTE, 1, 0, 0x007E, kAny, VKEY_OEM_3}}; // ~ |
| |
| // U+00A7 section |
| const PrintableSubEntry kU00A7[] = { |
| {DomCode::DIGIT4, 0, 0, kAny, kAny, VKEY_4}, |
| {DomCode::DIGIT6, 0, 0, kAny, kAny, VKEY_6}, |
| {DomCode::QUOTE, 0, 0, kAny, kAny, VKEY_OEM_7}, |
| {DomCode::BACKQUOTE, 1, 0, 0x00B0, kAny, VKEY_OEM_2}, // degree |
| {DomCode::BACKQUOTE, 1, 0, 0x00BD, kAny, VKEY_OEM_5}}; // one half |
| |
| // U+00AB left-pointing double angle quote |
| const PrintableSubEntry kU00AB[] = { |
| {DomCode::DIGIT8, 0, 0, kAny, kAny, VKEY_8}, |
| {DomCode::EQUAL, 0, 0, kAny, kAny, VKEY_OEM_6}}; |
| |
| // U+00B0 degree |
| const PrintableSubEntry kU00B0[] = { |
| {DomCode::EQUAL, 0, 0, kAny, kAny, VKEY_OEM_2}, |
| {DomCode::BACKQUOTE, 0, 0, kAny, kAny, VKEY_OEM_7}}; |
| |
| // U+00BA masculine ordinal indicator |
| const PrintableSubEntry kU00BA[] = { |
| {DomCode::QUOTE, 0, 0, kAny, kAny, VKEY_OEM_7}, |
| {DomCode::BACKQUOTE, 0, 0, kAny, kAny, VKEY_OEM_5}}; |
| |
| // U+00E0 a grave |
| const PrintableSubEntry kU00E0[] = { |
| {DomCode::DIGIT0, 0, 0, kAny, kAny, VKEY_0}, |
| {DomCode::BACKSLASH, 0, 0, kAny, kAny, VKEY_OEM_5}, |
| {DomCode::QUOTE, 1, 0, 0x00B0, kAny, VKEY_OEM_7}, // degree |
| {DomCode::QUOTE, 1, 0, 0x00E4, kAny, VKEY_OEM_5}}; // a diaeresis |
| |
| // U+00E1 a acute |
| const PrintableSubEntry kU00E1[] = { |
| {DomCode::DIGIT8, 0, 0, kAny, kAny, VKEY_8}, |
| {DomCode::QUOTE, 0, 0, kAny, kAny, VKEY_OEM_7}}; |
| |
| // U+00E2 a circumflex |
| const PrintableSubEntry kU00E2[] = { |
| {DomCode::DIGIT2, 0, 0, kAny, kAny, VKEY_2}, |
| {DomCode::BACKSLASH, 0, 0, kAny, kAny, VKEY_OEM_5}}; |
| |
| // U+00E4 a diaeresis |
| const PrintableSubEntry kU00E4[] = { |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_6}, |
| {DomCode::QUOTE, 1, 0, 0x00E0, kAny, VKEY_OEM_5}, // a grave |
| {DomCode::QUOTE, 1, 0, 0x00C4, kAny, VKEY_OEM_7}}; // A dia. |
| |
| // U+00E6 ae |
| const PrintableSubEntry kU00E6[] = { |
| {DomCode::SEMICOLON, 0, 0, kAny, kAny, VKEY_OEM_3}, |
| {DomCode::QUOTE, 0, 0, kAny, kAny, VKEY_OEM_7}}; |
| |
| // U+00E7 c cedilla |
| const PrintableSubEntry kU00E7[] = { |
| {DomCode::DIGIT9, 0, 0, kAny, kAny, VKEY_9}, |
| {DomCode::BRACKET_LEFT, 0, 0, kAny, kAny, VKEY_OEM_4}, |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_6}, |
| {DomCode::QUOTE, 0, 0, kAny, kAny, VKEY_OEM_7}, |
| {DomCode::BACKSLASH, 0, 0, kAny, kAny, VKEY_OEM_2}, |
| {DomCode::COMMA, 0, 0, kAny, kAny, VKEY_OEM_COMMA}, |
| {DomCode::SEMICOLON, 1, 1, 0x00C7, 0x00DE, VKEY_OEM_3}, // C ced., Thorn |
| {DomCode::SEMICOLON, 1, 0, 0x00C7, kAny, VKEY_OEM_1}}; // C ced., NoSy |
| |
| // U+00E8 e grave |
| const PrintableSubEntry kU00E8[] = { |
| {DomCode::DIGIT7, 0, 0, kAny, kAny, VKEY_7}, |
| {DomCode::BRACKET_LEFT, 0, 0, kAny, kAny, VKEY_OEM_1}, |
| {DomCode::QUOTE, 0, 0, kAny, kAny, VKEY_OEM_3}}; |
| |
| // U+00E9 e acute |
| const PrintableSubEntry kU00E9[] = { |
| {DomCode::DIGIT2, 0, 0, kAny, kAny, VKEY_2}, |
| {DomCode::DIGIT0, 0, 0, kAny, kAny, VKEY_0}, |
| {DomCode::SLASH, 0, 0, kAny, kAny, VKEY_OEM_2}, |
| {DomCode::SEMICOLON, 1, 0, 0x00C9, kAny, VKEY_OEM_1}, // E acute |
| {DomCode::SEMICOLON, 1, 0, 0x00F6, kAny, VKEY_OEM_7}}; // o diaeresis |
| |
| // U+00ED i acute |
| const PrintableSubEntry kU00ED[] = { |
| {DomCode::DIGIT9, 0, 0, kAny, kAny, VKEY_9}, |
| {DomCode::BACKQUOTE, 0, 0, kAny, kAny, VKEY_0}}; |
| |
| // U+00F0 eth |
| const PrintableSubEntry kU00F0[] = { |
| {DomCode::BRACKET_LEFT, 0, 0, kAny, kAny, VKEY_OEM_6}, |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_1}}; |
| |
| // U+00F3 o acute |
| const PrintableSubEntry kU00F3[] = { |
| {DomCode::EQUAL, 0, 0, kAny, kAny, VKEY_OEM_PLUS}, |
| {DomCode::BACKSLASH, 0, 0, kAny, kAny, VKEY_OEM_5}}; |
| |
| // U+00F4 o circumflex |
| const PrintableSubEntry kU00F4[] = { |
| {DomCode::DIGIT4, 0, 0, kAny, kAny, VKEY_4}, |
| {DomCode::SEMICOLON, 0, 0, kAny, kAny, VKEY_OEM_1}}; |
| |
| // U+00F6 o diaeresis |
| const PrintableSubEntry kU00F6[] = { |
| {DomCode::DIGIT0, 0, 0, kAny, kAny, VKEY_OEM_3}, |
| {DomCode::MINUS, 0, 0, kAny, kAny, VKEY_OEM_PLUS}, |
| {DomCode::BRACKET_LEFT, 0, 0, kAny, kAny, VKEY_OEM_4}, |
| {DomCode::SEMICOLON, 1, 0, 0x00E9, kAny, VKEY_OEM_7}, // e acute |
| {DomCode::SEMICOLON, 1, 1, 0x00D6, 0x0162, VKEY_OEM_3}, // O dia., T ced. |
| {DomCode::SEMICOLON, 1, 0, 0x00D6, kAny, VKEY_OEM_3}}; // O diaresis |
| |
| // U+00F8 o stroke |
| const PrintableSubEntry kU00F8[] = { |
| {DomCode::SEMICOLON, 0, 0, kAny, kAny, VKEY_OEM_3}, |
| {DomCode::QUOTE, 0, 0, kAny, kAny, VKEY_OEM_7}}; |
| |
| // U+00F9 u grave |
| const PrintableSubEntry kU00F9[] = { |
| {DomCode::QUOTE, 0, 0, kAny, kAny, VKEY_OEM_3}, |
| {DomCode::BACKSLASH, 0, 0, kAny, kAny, VKEY_OEM_2}}; |
| |
| // U+00FA u acute |
| const PrintableSubEntry kU00FA[] = { |
| {DomCode::BRACKET_LEFT, 0, 0, kAny, kAny, VKEY_OEM_4}, |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_6}}; |
| |
| // U+00FC u diaeresis |
| const PrintableSubEntry kU00FC[] = { |
| {DomCode::US_W, 0, 0, kAny, kAny, VKEY_W}, |
| {DomCode::BRACKET_LEFT, 1, 0, 0x00E8, kAny, VKEY_OEM_1}, // e grave |
| {DomCode::MINUS, 1, 0, 0x00DC, kAny, VKEY_OEM_2}, // U diaresis |
| {DomCode::BRACKET_LEFT, 1, 1, 0x00DC, 0x0141, VKEY_OEM_3}, // U dia., L- |
| {DomCode::BRACKET_LEFT, 1, 0, 0x00DC, kAny, VKEY_OEM_1}}; // U diaresis |
| |
| // U+0103 a breve |
| const PrintableSubEntry kU0103[] = { |
| {DomCode::DIGIT1, 0, 0, kAny, kAny, VKEY_1}, |
| {DomCode::BRACKET_LEFT, 0, 0, kAny, kAny, VKEY_OEM_4}}; |
| |
| // U+0105 a ogonek |
| const PrintableSubEntry kU0105[] = { |
| {DomCode::DIGIT1, 0, 0, kAny, kAny, VKEY_1}, |
| {DomCode::US_Q, 0, 0, kAny, kAny, VKEY_Q}, |
| {DomCode::QUOTE, 0, 0, kAny, kAny, VKEY_OEM_7}}; |
| |
| // U+010D c caron |
| const PrintableSubEntry kU010D[] = { |
| {DomCode::DIGIT2, 0, 0, kAny, kAny, VKEY_2}, |
| {DomCode::DIGIT4, 0, 0, kAny, kAny, VKEY_4}, |
| {DomCode::US_P, 0, 0, kAny, kAny, VKEY_X}, |
| {DomCode::SEMICOLON, 0, 0, kAny, kAny, VKEY_OEM_1}, |
| {DomCode::COMMA, 0, 0, kAny, kAny, VKEY_OEM_COMMA}}; |
| |
| // U+0111 d stroke |
| const PrintableSubEntry kU0111[] = { |
| {DomCode::DIGIT0, 0, 0, kAny, kAny, VKEY_0}, |
| {DomCode::BRACKET_RIGHT, 0, 0, kAny, kAny, VKEY_OEM_6}}; |
| |
| // U+0117 e dot above |
| const PrintableSubEntry kU0117[] = { |
| {DomCode::DIGIT4, 0, 0, kAny, kAny, VKEY_4}, |
| {DomCode::QUOTE, 0, 0, kAny, kAny, VKEY_OEM_7}}; |
| |
| // U+0119 e ogonek |
| const PrintableSubEntry kU0119[] = { |
| {DomCode::DIGIT3, 0, 0, kAny, kAny, VKEY_3}, |
| {DomCode::SLASH, 1, 1, 0x0118, 0x006E, VKEY_OEM_2}, // E ogonek, n |
| {DomCode::SLASH, 1, 0, 0x0118, kAny, VKEY_OEM_MINUS}}; // E ogonek |
| |
| // U+012F i ogonek |
| const PrintableSubEntry kU012F[] = { |
| {DomCode::DIGIT5, 0, 0, kAny, kAny, VKEY_5}, |
| {DomCode::BRACKET_LEFT, 1, 0, 0x012E, kAny, VKEY_OEM_4}}; // Iogonek |
| |
| // U+0142 l stroke |
| const PrintableSubEntry kU0142[] = { |
| {DomCode::SEMICOLON, 0, 0, kAny, kAny, VKEY_OEM_1}, |
| {DomCode::BACKSLASH, 0, 0, kAny, kAny, VKEY_OEM_2}}; |
| |
| // U+015F s cedilla |
| const PrintableSubEntry kU015F[] = { |
| {DomCode::SEMICOLON, 0, 0, kAny, kAny, VKEY_OEM_1}, |
| {DomCode::PERIOD, 0, 0, kAny, kAny, VKEY_OEM_PERIOD}}; |
| |
| // U+0161 s caron |
| const PrintableSubEntry kU0161[] = { |
| {DomCode::DIGIT3, 0, 0, kAny, kAny, VKEY_3}, |
| {DomCode::DIGIT6, 0, 0, kAny, kAny, VKEY_6}, |
| {DomCode::BRACKET_LEFT, 0, 0, kAny, kAny, VKEY_OEM_4}, |
| {DomCode::US_A, 0, 0, kAny, kAny, VKEY_OEM_1}, |
| {DomCode::US_F, 0, 0, kAny, kAny, VKEY_F}, |
| {DomCode::PERIOD, 0, 0, kAny, kAny, VKEY_OEM_PERIOD}}; |
| |
| // U+016B u macron |
| const PrintableSubEntry kU016B[] = { |
| {DomCode::DIGIT8, 0, 0, kAny, kAny, VKEY_8}, |
| {DomCode::US_Q, 0, 0, kAny, kAny, VKEY_Q}, |
| {DomCode::US_X, 0, 0, kAny, kAny, VKEY_X}}; |
| |
| // U+0173 u ogonek |
| const PrintableSubEntry kU0173[] = { |
| {DomCode::DIGIT7, 0, 0, kAny, kAny, VKEY_7}, |
| {DomCode::SEMICOLON, 1, 1, 0x0172, 0x0162, VKEY_OEM_1}, // U ogo., T ced. |
| {DomCode::SEMICOLON, 1, 0, 0x0172, kAny, VKEY_OEM_3}}; // U ogonek |
| |
| // U+017C z dot above |
| const PrintableSubEntry kU017C[] = { |
| {DomCode::BRACKET_LEFT, 0, 0, kAny, kAny, VKEY_OEM_4}, |
| {DomCode::BACKSLASH, 0, 0, kAny, kAny, VKEY_OEM_5}}; |
| |
| // U+017E z caron |
| const PrintableSubEntry kU017E[] = { |
| {DomCode::DIGIT6, 0, 0, kAny, kAny, VKEY_6}, |
| {DomCode::EQUAL, 0, 0, kAny, kAny, VKEY_OEM_PLUS}, |
| {DomCode::US_W, 0, 0, kAny, kAny, VKEY_W}, |
| {DomCode::BRACKET_LEFT, 0, 0, kAny, kAny, VKEY_Y}, |
| {DomCode::BACKSLASH, 0, 0, kAny, kAny, VKEY_OEM_5}}; |
| |
| // Table mapping unshifted characters to PrintableSubEntry tables. |
| struct PrintableMultiEntry { |
| base::char16 plain_character; |
| const PrintableSubEntry* subtable; |
| size_t subtable_size; |
| }; |
| |
| // Entries are ordered by character value. |
| const PrintableMultiEntry kMultiMap[] = { |
| {0x0021, kU0021, base::size(kU0021)}, // exclamation mark |
| {0x0022, kU0022, base::size(kU0022)}, // quotation mark |
| {0x0023, kU0023, base::size(kU0023)}, // number sign |
| {0x0024, kU0024, base::size(kU0024)}, // dollar sign |
| {0x0027, kU0027, base::size(kU0027)}, // apostrophe |
| {0x0028, kU0028, base::size(kU0028)}, // left parenthesis |
| {0x0029, kU0029, base::size(kU0029)}, // right parenthesis |
| {0x002A, kU002A, base::size(kU002A)}, // asterisk |
| {0x002B, kU002B, base::size(kU002B)}, // plus sign |
| {0x002C, kU002C, base::size(kU002C)}, // comma |
| {0x002D, kU002D, base::size(kU002D)}, // hyphen-minus |
| {0x002E, kU002E, base::size(kU002E)}, // full stop |
| {0x002F, kU002F, base::size(kU002F)}, // solidus |
| {0x003A, kU003A, base::size(kU003A)}, // colon |
| {0x003B, kU003B, base::size(kU003B)}, // semicolon |
| {0x003D, kU003D, base::size(kU003D)}, // equals sign |
| {0x003F, kU003F, base::size(kU003F)}, // question mark |
| {0x0040, kU0040, base::size(kU0040)}, // commercial at |
| {0x005B, kU005B, base::size(kU005B)}, // left square bracket |
| {0x005C, kU005C, base::size(kU005C)}, // reverse solidus |
| {0x005D, kU005D, base::size(kU005D)}, // right square bracket |
| {0x005F, kU005F, base::size(kU005F)}, // low line |
| {0x0060, kU0060, base::size(kU0060)}, // grave accent |
| {0x00A7, kU00A7, base::size(kU00A7)}, // section sign |
| {0x00AB, kU00AB, base::size(kU00AB)}, // left double angle quotation mark |
| {0x00B0, kU00B0, base::size(kU00B0)}, // degree sign |
| {0x00BA, kU00BA, base::size(kU00BA)}, // masculine ordinal indicator |
| {0x00E0, kU00E0, base::size(kU00E0)}, // a grave |
| {0x00E1, kU00E1, base::size(kU00E1)}, // a acute |
| {0x00E2, kU00E2, base::size(kU00E2)}, // a circumflex |
| {0x00E4, kU00E4, base::size(kU00E4)}, // a diaeresis |
| {0x00E6, kU00E6, base::size(kU00E6)}, // ae |
| {0x00E7, kU00E7, base::size(kU00E7)}, // c cedilla |
| {0x00E8, kU00E8, base::size(kU00E8)}, // e grave |
| {0x00E9, kU00E9, base::size(kU00E9)}, // e acute |
| {0x00ED, kU00ED, base::size(kU00ED)}, // i acute |
| {0x00F0, kU00F0, base::size(kU00F0)}, // eth |
| {0x00F3, kU00F3, base::size(kU00F3)}, // o acute |
| {0x00F4, kU00F4, base::size(kU00F4)}, // o circumflex |
| {0x00F6, kU00F6, base::size(kU00F6)}, // o diaeresis |
| {0x00F8, kU00F8, base::size(kU00F8)}, // o stroke |
| {0x00F9, kU00F9, base::size(kU00F9)}, // u grave |
| {0x00FA, kU00FA, base::size(kU00FA)}, // u acute |
| {0x00FC, kU00FC, base::size(kU00FC)}, // u diaeresis |
| {0x0103, kU0103, base::size(kU0103)}, // a breve |
| {0x0105, kU0105, base::size(kU0105)}, // a ogonek |
| {0x010D, kU010D, base::size(kU010D)}, // c caron |
| {0x0111, kU0111, base::size(kU0111)}, // d stroke |
| {0x0117, kU0117, base::size(kU0117)}, // e dot above |
| {0x0119, kU0119, base::size(kU0119)}, // e ogonek |
| {0x012F, kU012F, base::size(kU012F)}, // i ogonek |
| {0x0142, kU0142, base::size(kU0142)}, // l stroke |
| {0x015F, kU015F, base::size(kU015F)}, // s cedilla |
| {0x0161, kU0161, base::size(kU0161)}, // s caron |
| {0x016B, kU016B, base::size(kU016B)}, // u macron |
| {0x0173, kU0173, base::size(kU0173)}, // u ogonek |
| {0x017C, kU017C, base::size(kU017C)}, // z dot above |
| {0x017E, kU017E, base::size(kU017E)}, // z caron |
| }; |
| |
| // Table mapping unshifted characters to VKEY values. |
| struct PrintableSimpleEntry { |
| base::char16 plain_character; |
| KeyboardCode key_code; |
| }; |
| |
| // Entries are ordered by character value. |
| const PrintableSimpleEntry kSimpleMap[] = { |
| {0x0025, VKEY_5}, // percent sign |
| {0x0026, VKEY_1}, // ampersand |
| {0x003C, VKEY_OEM_5}, // less-than sign |
| {0x007B, VKEY_OEM_7}, // left curly bracket |
| {0x007C, VKEY_OEM_5}, // vertical line |
| {0x007D, VKEY_OEM_2}, // right curly bracket |
| {0x007E, VKEY_OEM_5}, // tilde |
| {0x00A1, VKEY_OEM_6}, // inverted exclamation mark |
| {0x00AD, VKEY_OEM_3}, // soft hyphen |
| {0x00B2, VKEY_OEM_7}, // superscript two |
| {0x00B5, VKEY_OEM_5}, // micro sign |
| {0x00BB, VKEY_9}, // right-pointing double angle quotation mark |
| {0x00BD, VKEY_OEM_5}, // vulgar fraction one half |
| {0x00BF, VKEY_OEM_6}, // inverted question mark |
| {0x00DF, VKEY_OEM_4}, // sharp s |
| {0x00E5, VKEY_OEM_6}, // a ring above |
| {0x00EA, VKEY_3}, // e circumflex |
| {0x00EB, VKEY_OEM_1}, // e diaeresis |
| {0x00EC, VKEY_OEM_6}, // i grave |
| {0x00EE, VKEY_OEM_6}, // i circumflex |
| {0x00F1, VKEY_OEM_3}, // n tilde |
| {0x00F2, VKEY_OEM_3}, // o grave |
| {0x00F5, VKEY_OEM_4}, // o tilde |
| {0x00F7, VKEY_OEM_6}, // division sign |
| {0x00FD, VKEY_7}, // y acute |
| {0x00FE, VKEY_OEM_MINUS}, // thorn |
| {0x0101, VKEY_OEM_8}, // a macron |
| {0x0107, VKEY_OEM_7}, // c acute |
| {0x010B, VKEY_OEM_3}, // c dot above |
| {0x0113, VKEY_W}, // e macron |
| {0x011B, VKEY_2}, // e caron |
| {0x011F, VKEY_OEM_6}, // g breve |
| {0x0121, VKEY_OEM_4}, // g dot above |
| {0x0127, VKEY_OEM_6}, // h stroke |
| {0x012B, VKEY_OEM_6}, // i macron |
| {0x0131, VKEY_OEM_1}, // dotless i |
| {0x0137, VKEY_OEM_5}, // k cedilla |
| {0x013C, VKEY_OEM_2}, // l cedilla |
| {0x013E, VKEY_2}, // l caron |
| {0x0146, VKEY_OEM_4}, // n cedilla |
| {0x0148, VKEY_OEM_5}, // n caron |
| {0x0151, VKEY_OEM_4}, // o double acute |
| {0x0159, VKEY_5}, // r caron |
| {0x0163, VKEY_OEM_7}, // t cedilla |
| {0x0165, VKEY_5}, // t caron |
| {0x016F, VKEY_OEM_1}, // u ring above |
| {0x0171, VKEY_OEM_5}, // u double acute |
| {0x01A1, VKEY_OEM_6}, // o horn |
| {0x01B0, VKEY_OEM_4}, // u horn |
| {0x01B6, VKEY_OEM_6}, // z stroke |
| {0x0259, VKEY_OEM_3}, // schwa |
| }; |
| |
| #if defined(OS_CHROMEOS) |
| void LoadKeymap(const std::string& layout_name, |
| scoped_refptr<base::SingleThreadTaskRunner> reply_runner, |
| LoadKeymapCallback reply_callback) { |
| std::string layout_id; |
| std::string layout_variant; |
| XkbKeyboardLayoutEngine::ParseLayoutName(layout_name, &layout_id, |
| &layout_variant); |
| xkb_rule_names names = {.rules = NULL, |
| .model = "pc101", |
| .layout = layout_id.c_str(), |
| .variant = layout_variant.c_str(), |
| .options = ""}; |
| std::unique_ptr<xkb_context, XkbContextDeleter> context; |
| context.reset(xkb_context_new(XKB_CONTEXT_NO_DEFAULT_INCLUDES)); |
| xkb_context_include_path_append(context.get(), "/usr/share/X11/xkb"); |
| std::unique_ptr<xkb_keymap, XkbKeymapDeleter> keymap; |
| keymap.reset(xkb_keymap_new_from_names(context.get(), &names, |
| XKB_KEYMAP_COMPILE_NO_FLAGS)); |
| if (keymap) { |
| std::unique_ptr<char, base::FreeDeleter> keymap_str( |
| xkb_keymap_get_as_string(keymap.get(), XKB_KEYMAP_FORMAT_TEXT_V1)); |
| reply_runner->PostTask( |
| FROM_HERE, base::BindOnce(std::move(reply_callback), layout_name, |
| base::Passed(&keymap_str))); |
| } else { |
| LOG(FATAL) << "Keymap file failed to load: " << layout_name; |
| } |
| } |
| #endif |
| |
| bool IsControlCharacter(uint32_t character) { |
| return (character < 0x20) || (character > 0x7E && character < 0xA0); |
| } |
| |
| } // anonymous namespace |
| |
| XkbKeyCodeConverter::XkbKeyCodeConverter() { |
| } |
| |
| XkbKeyCodeConverter::~XkbKeyCodeConverter() { |
| } |
| |
| XkbKeyboardLayoutEngine::XkbKeyboardLayoutEngine( |
| const XkbKeyCodeConverter& converter) |
| : key_code_converter_(converter), weak_ptr_factory_(this) { |
| // TODO: add XKB_CONTEXT_NO_ENVIRONMENT_NAMES |
| xkb_context_.reset(xkb_context_new(XKB_CONTEXT_NO_DEFAULT_INCLUDES)); |
| xkb_context_include_path_append(xkb_context_.get(), |
| "/usr/share/X11/xkb"); |
| } |
| |
| XkbKeyboardLayoutEngine::~XkbKeyboardLayoutEngine() { |
| for (const auto& entry : xkb_keymaps_) { |
| xkb_keymap_unref(entry.keymap); |
| } |
| } |
| |
| bool XkbKeyboardLayoutEngine::CanSetCurrentLayout() const { |
| #if defined(OS_CHROMEOS) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| bool XkbKeyboardLayoutEngine::SetCurrentLayoutByName( |
| const std::string& layout_name) { |
| #if defined(OS_CHROMEOS) |
| current_layout_name_ = layout_name; |
| for (const auto& entry : xkb_keymaps_) { |
| if (entry.layout_name == layout_name) { |
| SetKeymap(entry.keymap); |
| return true; |
| } |
| } |
| LoadKeymapCallback reply_callback = base::BindOnce( |
| &XkbKeyboardLayoutEngine::OnKeymapLoaded, weak_ptr_factory_.GetWeakPtr()); |
| base::PostTaskWithTraits( |
| FROM_HERE, |
| {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, |
| base::BindOnce(&LoadKeymap, layout_name, |
| base::ThreadTaskRunnerHandle::Get(), |
| std::move(reply_callback))); |
| #else |
| NOTIMPLEMENTED(); |
| #endif // defined(OS_CHROMEOS) |
| return true; |
| } |
| |
| void XkbKeyboardLayoutEngine::OnKeymapLoaded( |
| const std::string& layout_name, |
| std::unique_ptr<char, base::FreeDeleter> keymap_str) { |
| if (keymap_str) { |
| xkb_keymap* keymap = xkb_keymap_new_from_string( |
| xkb_context_.get(), keymap_str.get(), XKB_KEYMAP_FORMAT_TEXT_V1, |
| XKB_KEYMAP_COMPILE_NO_FLAGS); |
| XkbKeymapEntry entry = {layout_name, keymap}; |
| xkb_keymaps_.push_back(entry); |
| if (layout_name == current_layout_name_) |
| SetKeymap(keymap); |
| } else { |
| LOG(FATAL) << "Keymap file failed to load: " << layout_name; |
| } |
| } |
| |
| bool XkbKeyboardLayoutEngine::UsesISOLevel5Shift() const { |
| // NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| bool XkbKeyboardLayoutEngine::UsesAltGr() const { |
| // NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| bool XkbKeyboardLayoutEngine::Lookup(DomCode dom_code, |
| int flags, |
| DomKey* dom_key, |
| KeyboardCode* key_code) const { |
| if (dom_code == DomCode::NONE) |
| return false; |
| // Convert DOM physical key to XKB representation. |
| xkb_keycode_t xkb_keycode = key_code_converter_.DomCodeToXkbKeyCode(dom_code); |
| if (xkb_keycode == key_code_converter_.InvalidXkbKeyCode()) { |
| LOG(ERROR) << "No XKB keycode for DomCode 0x" << std::hex |
| << static_cast<int>(dom_code) << " '" |
| << KeycodeConverter::DomCodeToCodeString(dom_code) << "'"; |
| return false; |
| } |
| xkb_mod_mask_t xkb_flags = EventFlagsToXkbFlags(flags); |
| // Obtain keysym and character. |
| xkb_keysym_t xkb_keysym; |
| uint32_t character = 0; |
| if (!XkbLookup(xkb_keycode, xkb_flags, &xkb_keysym, &character)) { |
| // If we do not have matching legacy Xkb keycode for the Dom code, |
| // we could be dealing with a newer application launcher or similar |
| // key. Let's see if we have a basic mapping for it. |
| return DomCodeToNonPrintableDomKey(dom_code, dom_key, key_code); |
| } |
| |
| // Classify the keysym and convert to DOM and VKEY representations. |
| if (xkb_keysym != XKB_KEY_at || (flags & EF_CONTROL_DOWN) == 0) { |
| // Non-character key. (We only support NUL as ^@.) |
| *dom_key = NonPrintableXKeySymToDomKey(xkb_keysym); |
| if (*dom_key != DomKey::NONE) { |
| *key_code = NonPrintableDomKeyToKeyboardCode(*dom_key); |
| if (*key_code == VKEY_UNKNOWN) |
| *key_code = DomCodeToUsLayoutNonLocatedKeyboardCode(dom_code); |
| return true; |
| } |
| if (character == 0) { |
| *dom_key = DomKey::UNIDENTIFIED; |
| *key_code = DomCodeToUsLayoutNonLocatedKeyboardCode(dom_code); |
| return true; |
| } |
| } |
| |
| // Per UI Events rules for determining |key|, if the character is |
| // non-printable and a non-shiftlike modifier is down, we preferentially |
| // return a printable key as if the modifier were not down. |
| // https://w3c.github.io/uievents/#keys-guidelines |
| const int kNonShiftlikeModifiers = |
| EF_CONTROL_DOWN | EF_ALT_DOWN | EF_COMMAND_DOWN; |
| if ((flags & kNonShiftlikeModifiers) && IsControlCharacter(character)) { |
| int normal_ui_flags = flags & ~kNonShiftlikeModifiers; |
| xkb_mod_mask_t normal_xkb_flags = EventFlagsToXkbFlags(normal_ui_flags); |
| xkb_keysym_t normal_keysym; |
| uint32_t normal_character = 0; |
| if (XkbLookup(xkb_keycode, normal_xkb_flags, &normal_keysym, |
| &normal_character) && |
| !IsControlCharacter(normal_character)) { |
| flags = normal_ui_flags; |
| xkb_flags = normal_xkb_flags; |
| character = normal_character; |
| xkb_keysym = normal_keysym; |
| } |
| } |
| |
| *dom_key = DomKey::FromCharacter(character); |
| *key_code = AlphanumericKeyboardCode(xkb_keysym, character); |
| if (*key_code == VKEY_UNKNOWN) { |
| *key_code = DifficultKeyboardCode(dom_code, flags, xkb_keycode, xkb_flags, |
| xkb_keysym, character); |
| if (*key_code == VKEY_UNKNOWN) |
| *key_code = DomCodeToUsLayoutNonLocatedKeyboardCode(dom_code); |
| } |
| return true; |
| } |
| |
| bool XkbKeyboardLayoutEngine::SetCurrentLayoutFromBuffer( |
| const char* keymap_string, |
| size_t size) { |
| xkb_keymap* keymap = xkb_keymap_new_from_buffer( |
| xkb_context_.get(), keymap_string, size, XKB_KEYMAP_FORMAT_TEXT_V1, |
| XKB_KEYMAP_COMPILE_NO_FLAGS); |
| if (!keymap) |
| return false; |
| |
| SetKeymap(keymap); |
| return true; |
| } |
| |
| void XkbKeyboardLayoutEngine::SetKeymap(xkb_keymap* keymap) { |
| xkb_state_.reset(xkb_state_new(keymap)); |
| // Update flag map. |
| static const struct { |
| int ui_flag; |
| const char* xkb_name; |
| } flags[] = {{ui::EF_SHIFT_DOWN, XKB_MOD_NAME_SHIFT}, |
| {ui::EF_CONTROL_DOWN, XKB_MOD_NAME_CTRL}, |
| {ui::EF_ALT_DOWN, XKB_MOD_NAME_ALT}, |
| {ui::EF_COMMAND_DOWN, XKB_MOD_NAME_LOGO}, |
| {ui::EF_ALTGR_DOWN, "Mod5"}, |
| {ui::EF_MOD3_DOWN, "Mod3"}, |
| {ui::EF_CAPS_LOCK_ON, XKB_MOD_NAME_CAPS}}; |
| xkb_flag_map_.clear(); |
| xkb_flag_map_.reserve(base::size(flags)); |
| for (size_t i = 0; i < base::size(flags); ++i) { |
| xkb_mod_index_t index = xkb_keymap_mod_get_index(keymap, flags[i].xkb_name); |
| if (index == XKB_MOD_INVALID) { |
| DVLOG(3) << "XKB keyboard layout does not contain " << flags[i].xkb_name; |
| } else { |
| xkb_mod_mask_t flag = static_cast<xkb_mod_mask_t>(1) << index; |
| XkbFlagMapEntry e = {flags[i].ui_flag, flag}; |
| xkb_flag_map_.push_back(e); |
| } |
| } |
| |
| // Update num lock mask. |
| num_lock_mod_mask_ = 0; |
| xkb_mod_index_t num_mod_index = |
| xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_NUM); |
| if (num_mod_index != XKB_MOD_INVALID) |
| num_lock_mod_mask_ = static_cast<xkb_mod_mask_t>(1) << num_mod_index; |
| } |
| |
| xkb_mod_mask_t XkbKeyboardLayoutEngine::EventFlagsToXkbFlags( |
| int ui_flags) const { |
| xkb_mod_mask_t xkb_flags = 0; |
| for (const auto& entry : xkb_flag_map_) { |
| if (ui_flags & entry.ui_flag) |
| xkb_flags |= entry.xkb_flag; |
| } |
| // NumLock is always on. |
| xkb_flags |= num_lock_mod_mask_; |
| return xkb_flags; |
| } |
| |
| bool XkbKeyboardLayoutEngine::XkbLookup(xkb_keycode_t xkb_keycode, |
| xkb_mod_mask_t xkb_flags, |
| xkb_keysym_t* xkb_keysym, |
| uint32_t* character) const { |
| if (!xkb_state_) { |
| LOG(ERROR) << "No current XKB state"; |
| return false; |
| } |
| xkb_state_update_mask(xkb_state_.get(), xkb_flags, 0, 0, 0, 0, 0); |
| *xkb_keysym = xkb_state_key_get_one_sym(xkb_state_.get(), xkb_keycode); |
| if (*xkb_keysym == XKB_KEY_NoSymbol) |
| return false; |
| *character = xkb_state_key_get_utf32(xkb_state_.get(), xkb_keycode); |
| return true; |
| } |
| |
| KeyboardCode XkbKeyboardLayoutEngine::DifficultKeyboardCode( |
| DomCode dom_code, |
| int ui_flags, |
| xkb_keycode_t xkb_keycode, |
| xkb_mod_mask_t xkb_flags, |
| xkb_keysym_t xkb_keysym, |
| base::char16 character) const { |
| // Get the layout interpretation without modifiers, so that |
| // e.g. Ctrl+D correctly generates VKEY_D. |
| xkb_keysym_t plain_keysym; |
| uint32_t plain_character; |
| if (!XkbLookup(xkb_keycode, 0, &plain_keysym, &plain_character)) |
| return VKEY_UNKNOWN; |
| |
| // If the plain key is non-printable, that determines the VKEY. |
| DomKey plain_key = NonPrintableXKeySymToDomKey(plain_keysym); |
| if (plain_key != ui::DomKey::NONE) |
| return NonPrintableDomKeyToKeyboardCode(plain_key); |
| |
| // Plain ASCII letters and digits map directly to VKEY values. |
| KeyboardCode key_code = AlphanumericKeyboardCode(xkb_keysym, plain_character); |
| if (key_code != VKEY_UNKNOWN) |
| return key_code; |
| |
| // Check the multi-character tables. |
| const PrintableMultiEntry* multi_end = kMultiMap + base::size(kMultiMap); |
| const PrintableMultiEntry* multi = |
| std::lower_bound(kMultiMap, multi_end, plain_character, |
| [](const PrintableMultiEntry& e, base::char16 c) { |
| return e.plain_character < c; |
| }); |
| if ((multi != multi_end) && (multi->plain_character == plain_character)) { |
| const base::char16 kNonCharacter = kAny; |
| base::char16 shift_character = kNonCharacter; |
| base::char16 altgr_character = kNonCharacter; |
| for (size_t i = 0; i < multi->subtable_size; ++i) { |
| if (multi->subtable[i].dom_code != dom_code) |
| continue; |
| if (multi->subtable[i].test_shift) { |
| if (shift_character == kNonCharacter) { |
| shift_character = XkbSubCharacter(xkb_keycode, xkb_flags, character, |
| ui::EF_SHIFT_DOWN); |
| } |
| if (shift_character != multi->subtable[i].shift_character) |
| continue; |
| } |
| if (multi->subtable[i].test_altgr) { |
| if (altgr_character == kNonCharacter) { |
| altgr_character = XkbSubCharacter(xkb_keycode, xkb_flags, character, |
| ui::EF_ALTGR_DOWN); |
| } |
| if (altgr_character != multi->subtable[i].altgr_character) |
| continue; |
| } |
| return multi->subtable[i].key_code; |
| } |
| } |
| |
| // Check the simple character table. |
| const PrintableSimpleEntry* simple_end = kSimpleMap + base::size(kSimpleMap); |
| const PrintableSimpleEntry* simple = |
| std::lower_bound(kSimpleMap, simple_end, plain_character, |
| [](const PrintableSimpleEntry& e, base::char16 c) { |
| return e.plain_character < c; |
| }); |
| if ((simple != simple_end) && (simple->plain_character == plain_character)) |
| return simple->key_code; |
| |
| return VKEY_UNKNOWN; |
| } |
| |
| base::char16 XkbKeyboardLayoutEngine::XkbSubCharacter( |
| xkb_keycode_t xkb_keycode, |
| xkb_mod_mask_t base_flags, |
| base::char16 base_character, |
| int ui_flags) const { |
| xkb_mod_mask_t flags = EventFlagsToXkbFlags(ui_flags); |
| if (flags == base_flags) |
| return base_character; |
| xkb_keysym_t keysym; |
| uint32_t character = 0; |
| if (!XkbLookup(xkb_keycode, flags, &keysym, &character)) |
| character = kNone; |
| return character; |
| } |
| |
| void XkbKeyboardLayoutEngine::ParseLayoutName(const std::string& layout_name, |
| std::string* layout_id, |
| std::string* layout_variant) { |
| size_t dash_index = layout_name.find('-'); |
| size_t parentheses_index = layout_name.find('('); |
| *layout_id = layout_name; |
| *layout_variant = ""; |
| if (parentheses_index != std::string::npos) { |
| *layout_id = layout_name.substr(0, parentheses_index); |
| size_t close_index = layout_name.find(')', parentheses_index); |
| if (close_index == std::string::npos) |
| close_index = layout_name.size(); |
| *layout_variant = layout_name.substr(parentheses_index + 1, |
| close_index - parentheses_index - 1); |
| } else if (dash_index != std::string::npos) { |
| *layout_id = layout_name.substr(0, dash_index); |
| *layout_variant = layout_name.substr(dash_index + 1); |
| } |
| } |
| |
| } // namespace ui |