| // Copyright (c) 2013 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 "chrome/test/chromedriver/keycode_text_conversion.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <algorithm> |
| |
| #include "base/stl_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/test/chromedriver/chrome/ui_events.h" |
| #include "ui/base/x/x11_util.h" |
| #include "ui/events/keycodes/keyboard_code_conversion_x.h" |
| #include "ui/gfx/x/x11.h" |
| |
| namespace { |
| |
| struct KeyCodeAndXKeyCode { |
| ui::KeyboardCode key_code; |
| int x_key_code; |
| }; |
| |
| // Contains a list of keyboard codes, in order, with their corresponding |
| // X key code. This list is not complete. |
| // TODO(kkania): Merge this table with the existing one in |
| // keyboard_code_conversion_x.cc. |
| KeyCodeAndXKeyCode kKeyCodeToXKeyCode[] = { |
| { ui::VKEY_BACK, 22 }, |
| { ui::VKEY_TAB, 23 }, |
| { ui::VKEY_RETURN, 36 }, |
| { ui::VKEY_SHIFT, 50 }, |
| { ui::VKEY_CONTROL, 37 }, |
| { ui::VKEY_MENU, 64 }, |
| { ui::VKEY_CAPITAL, 66 }, |
| { ui::VKEY_HANGUL, 130 }, |
| { ui::VKEY_HANJA, 131 }, |
| { ui::VKEY_ESCAPE, 9 }, |
| { ui::VKEY_SPACE, 65 }, |
| { ui::VKEY_PRIOR, 112 }, |
| { ui::VKEY_NEXT, 117 }, |
| { ui::VKEY_END, 115 }, |
| { ui::VKEY_HOME, 110 }, |
| { ui::VKEY_LEFT, 113 }, |
| { ui::VKEY_UP, 111 }, |
| { ui::VKEY_RIGHT, 114 }, |
| { ui::VKEY_DOWN, 116 }, |
| { ui::VKEY_INSERT, 118 }, |
| { ui::VKEY_DELETE, 119 }, |
| { ui::VKEY_0, 19 }, |
| { ui::VKEY_1, 10 }, |
| { ui::VKEY_2, 11 }, |
| { ui::VKEY_3, 12 }, |
| { ui::VKEY_4, 13 }, |
| { ui::VKEY_5, 14 }, |
| { ui::VKEY_6, 15 }, |
| { ui::VKEY_7, 16 }, |
| { ui::VKEY_8, 17 }, |
| { ui::VKEY_9, 18 }, |
| { ui::VKEY_A, 38 }, |
| { ui::VKEY_B, 56 }, |
| { ui::VKEY_C, 54 }, |
| { ui::VKEY_D, 40 }, |
| { ui::VKEY_E, 26 }, |
| { ui::VKEY_F, 41 }, |
| { ui::VKEY_G, 42 }, |
| { ui::VKEY_H, 43 }, |
| { ui::VKEY_I, 31 }, |
| { ui::VKEY_J, 44 }, |
| { ui::VKEY_K, 45 }, |
| { ui::VKEY_L, 46 }, |
| { ui::VKEY_M, 58 }, |
| { ui::VKEY_N, 57 }, |
| { ui::VKEY_O, 32 }, |
| { ui::VKEY_P, 33 }, |
| { ui::VKEY_Q, 24 }, |
| { ui::VKEY_R, 27 }, |
| { ui::VKEY_S, 39 }, |
| { ui::VKEY_T, 28 }, |
| { ui::VKEY_U, 30 }, |
| { ui::VKEY_V, 55 }, |
| { ui::VKEY_W, 25 }, |
| { ui::VKEY_X, 53 }, |
| { ui::VKEY_Y, 29 }, |
| { ui::VKEY_Z, 52 }, |
| { ui::VKEY_LWIN, 133 }, |
| { ui::VKEY_NUMPAD0, 90 }, |
| { ui::VKEY_NUMPAD1, 87 }, |
| { ui::VKEY_NUMPAD2, 88 }, |
| { ui::VKEY_NUMPAD3, 89 }, |
| { ui::VKEY_NUMPAD4, 83 }, |
| { ui::VKEY_NUMPAD5, 84 }, |
| { ui::VKEY_NUMPAD6, 85 }, |
| { ui::VKEY_NUMPAD7, 79 }, |
| { ui::VKEY_NUMPAD8, 80 }, |
| { ui::VKEY_NUMPAD9, 81 }, |
| { ui::VKEY_MULTIPLY, 63 }, |
| { ui::VKEY_ADD, 86 }, |
| { ui::VKEY_SUBTRACT, 82 }, |
| { ui::VKEY_DECIMAL, 129 }, |
| { ui::VKEY_DIVIDE, 106 }, |
| { ui::VKEY_F1, 67 }, |
| { ui::VKEY_F2, 68 }, |
| { ui::VKEY_F3, 69 }, |
| { ui::VKEY_F4, 70 }, |
| { ui::VKEY_F5, 71 }, |
| { ui::VKEY_F6, 72 }, |
| { ui::VKEY_F7, 73 }, |
| { ui::VKEY_F8, 74 }, |
| { ui::VKEY_F9, 75 }, |
| { ui::VKEY_F10, 76 }, |
| { ui::VKEY_F11, 95 }, |
| { ui::VKEY_F12, 96 }, |
| { ui::VKEY_NUMLOCK, 77 }, |
| { ui::VKEY_SCROLL, 78 }, |
| { ui::VKEY_OEM_1, 47 }, |
| { ui::VKEY_OEM_PLUS, 21 }, |
| { ui::VKEY_OEM_COMMA, 59 }, |
| { ui::VKEY_OEM_MINUS, 20 }, |
| { ui::VKEY_OEM_PERIOD, 60 }, |
| { ui::VKEY_OEM_2, 61 }, |
| { ui::VKEY_OEM_3, 49 }, |
| { ui::VKEY_OEM_4, 34 }, |
| { ui::VKEY_OEM_5, 51 }, |
| { ui::VKEY_OEM_6, 35 }, |
| { ui::VKEY_OEM_7, 48 } |
| }; |
| |
| // Uses to compare two KeyCodeAndXKeyCode structs based on their key code. |
| bool operator<(const KeyCodeAndXKeyCode& a, const KeyCodeAndXKeyCode& b) { |
| return a.key_code < b.key_code; |
| } |
| |
| // Returns the equivalent X key code for the given key code. Returns -1 if |
| // no X equivalent was found. |
| int KeyboardCodeToXKeyCode(ui::KeyboardCode key_code) { |
| KeyCodeAndXKeyCode find; |
| find.key_code = key_code; |
| const KeyCodeAndXKeyCode* found = std::lower_bound( |
| kKeyCodeToXKeyCode, kKeyCodeToXKeyCode + base::size(kKeyCodeToXKeyCode), |
| find); |
| if (found >= kKeyCodeToXKeyCode + base::size(kKeyCodeToXKeyCode) || |
| found->key_code != key_code) |
| return -1; |
| return found->x_key_code; |
| } |
| |
| // Gets the X modifier mask (Mod1Mask through Mod5Mask) for the given |
| // modifier. Only checks the alt, meta, and num lock keys currently. |
| // Returns true on success. |
| bool GetXModifierMask(Display* display, int modifier, int* x_modifier) { |
| XModifierKeymap* mod_map = XGetModifierMapping(display); |
| bool found = false; |
| int max_mod_keys = mod_map->max_keypermod; |
| for (int mod_index = 0; mod_index <= 8; ++mod_index) { |
| for (int key_index = 0; key_index < max_mod_keys; ++key_index) { |
| int key = mod_map->modifiermap[mod_index * max_mod_keys + key_index]; |
| int keysym = XkbKeycodeToKeysym(display, key, 0, 0); |
| if (modifier == kAltKeyModifierMask) |
| found = keysym == XK_Alt_L || keysym == XK_Alt_R; |
| else if (modifier == kMetaKeyModifierMask) |
| found = keysym == XK_Meta_L || keysym == XK_Meta_R; |
| else if (modifier == kNumLockKeyModifierMask) |
| found = keysym == XK_Num_Lock; |
| if (found) { |
| *x_modifier = 1 << mod_index; |
| break; |
| } |
| } |
| if (found) |
| break; |
| } |
| XFreeModifiermap(mod_map); |
| return found; |
| } |
| |
| } // namespace |
| |
| bool ConvertKeyCodeToText( |
| ui::KeyboardCode key_code, int modifiers, std::string* text, |
| std::string* error_msg) { |
| XDisplay* display = gfx::GetXDisplay(); |
| if (!display) { |
| return ConvertKeyCodeToTextOzone(key_code, modifiers, text, error_msg); |
| } |
| |
| *error_msg = std::string(); |
| int x_key_code = KeyboardCodeToXKeyCode(key_code); |
| if (x_key_code == -1) { |
| *text = std::string(); |
| return true; |
| } |
| |
| XEvent event; |
| memset(&event, 0, sizeof(XEvent)); |
| XKeyEvent* key_event = &event.xkey; |
| key_event->display = display; |
| key_event->keycode = x_key_code; |
| if (modifiers & kShiftKeyModifierMask) |
| key_event->state |= ShiftMask; |
| if (modifiers & kControlKeyModifierMask) |
| key_event->state |= ControlMask; |
| |
| // Make a best attempt for non-standard modifiers. |
| int x_modifier; |
| if (modifiers & kAltKeyModifierMask && |
| GetXModifierMask(display, kAltKeyModifierMask, &x_modifier)) { |
| key_event->state |= x_modifier; |
| } |
| if (modifiers & kMetaKeyModifierMask && |
| GetXModifierMask(display, kMetaKeyModifierMask, &x_modifier)) { |
| key_event->state |= x_modifier; |
| } |
| if (modifiers & kNumLockKeyModifierMask && |
| GetXModifierMask(display, kNumLockKeyModifierMask, &x_modifier)) { |
| key_event->state |= x_modifier; |
| } |
| key_event->type = KeyPress; |
| uint16_t character = ui::GetCharacterFromXEvent(&event); |
| |
| if (!character) |
| *text = std::string(); |
| else |
| *text = base::UTF16ToUTF8(base::string16(1, character)); |
| return true; |
| } |
| |
| bool ConvertCharToKeyCode( |
| base::char16 key, |
| ui::KeyboardCode* key_code, |
| int* necessary_modifiers, |
| std::string* error_msg) { |
| XDisplay* display = gfx::GetXDisplay(); |
| if (!display) { |
| return ConvertCharToKeyCodeOzone(key, key_code, necessary_modifiers, |
| error_msg); |
| } |
| |
| std::string key_string(base::UTF16ToUTF8(base::string16(1, key))); |
| bool found = false; |
| ui::KeyboardCode test_code; |
| int test_modifiers; |
| *error_msg = std::string(); |
| std::string conv_string; |
| for (size_t i = 0; i < base::size(kKeyCodeToXKeyCode); ++i) { |
| test_code = kKeyCodeToXKeyCode[i].key_code; |
| // Skip the numpad keys. |
| if (test_code >= ui::VKEY_NUMPAD0 && test_code <= ui::VKEY_DIVIDE) |
| continue; |
| test_modifiers = 0; |
| if (!ConvertKeyCodeToText( |
| test_code, test_modifiers, &conv_string, error_msg)) |
| return false; |
| if (conv_string == key_string) { |
| found = true; |
| break; |
| } |
| test_modifiers = kShiftKeyModifierMask; |
| if (!ConvertKeyCodeToText( |
| test_code, test_modifiers, &conv_string, error_msg)) |
| return false; |
| if (conv_string == key_string) { |
| found = true; |
| break; |
| } |
| } |
| if (found) { |
| *key_code = test_code; |
| *necessary_modifiers = test_modifiers; |
| } |
| return found; |
| } |