1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/test/chromedriver/keycode_text_conversion.h" 6 7 #include <algorithm> 8 #include <X11/keysym.h> 9 #include <X11/XKBlib.h> 10 #include <X11/Xlib.h> 11 #include <X11/Xutil.h> 12 13 #include "base/strings/utf_string_conversions.h" 14 #include "chrome/test/chromedriver/chrome/ui_events.h" 15 #include "ui/base/x/x11_util.h" 16 #include "ui/events/keycodes/keyboard_code_conversion_x.h" 17 18 namespace { 19 20 struct KeyCodeAndXKeyCode { 21 ui::KeyboardCode key_code; 22 int x_key_code; 23 }; 24 25 // Contains a list of keyboard codes, in order, with their corresponding 26 // X key code. This list is not complete. 27 // TODO(kkania): Merge this table with the existing one in 28 // keyboard_code_conversion_x.cc. 29 KeyCodeAndXKeyCode kKeyCodeToXKeyCode[] = { 30 { ui::VKEY_BACK, 22 }, 31 { ui::VKEY_TAB, 23 }, 32 { ui::VKEY_RETURN, 36 }, 33 { ui::VKEY_SHIFT, 50 }, 34 { ui::VKEY_CONTROL, 37 }, 35 { ui::VKEY_MENU, 64 }, 36 { ui::VKEY_CAPITAL, 66 }, 37 { ui::VKEY_HANGUL, 130 }, 38 { ui::VKEY_HANJA, 131 }, 39 { ui::VKEY_ESCAPE, 9 }, 40 { ui::VKEY_SPACE, 65 }, 41 { ui::VKEY_PRIOR, 112 }, 42 { ui::VKEY_NEXT, 117 }, 43 { ui::VKEY_END, 115 }, 44 { ui::VKEY_HOME, 110 }, 45 { ui::VKEY_LEFT, 113 }, 46 { ui::VKEY_UP, 111 }, 47 { ui::VKEY_RIGHT, 114 }, 48 { ui::VKEY_DOWN, 116 }, 49 { ui::VKEY_INSERT, 118 }, 50 { ui::VKEY_DELETE, 119 }, 51 { ui::VKEY_0, 19 }, 52 { ui::VKEY_1, 10 }, 53 { ui::VKEY_2, 11 }, 54 { ui::VKEY_3, 12 }, 55 { ui::VKEY_4, 13 }, 56 { ui::VKEY_5, 14 }, 57 { ui::VKEY_6, 15 }, 58 { ui::VKEY_7, 16 }, 59 { ui::VKEY_8, 17 }, 60 { ui::VKEY_9, 18 }, 61 { ui::VKEY_A, 38 }, 62 { ui::VKEY_B, 56 }, 63 { ui::VKEY_C, 54 }, 64 { ui::VKEY_D, 40 }, 65 { ui::VKEY_E, 26 }, 66 { ui::VKEY_F, 41 }, 67 { ui::VKEY_G, 42 }, 68 { ui::VKEY_H, 43 }, 69 { ui::VKEY_I, 31 }, 70 { ui::VKEY_J, 44 }, 71 { ui::VKEY_K, 45 }, 72 { ui::VKEY_L, 46 }, 73 { ui::VKEY_M, 58 }, 74 { ui::VKEY_N, 57 }, 75 { ui::VKEY_O, 32 }, 76 { ui::VKEY_P, 33 }, 77 { ui::VKEY_Q, 24 }, 78 { ui::VKEY_R, 27 }, 79 { ui::VKEY_S, 39 }, 80 { ui::VKEY_T, 28 }, 81 { ui::VKEY_U, 30 }, 82 { ui::VKEY_V, 55 }, 83 { ui::VKEY_W, 25 }, 84 { ui::VKEY_X, 53 }, 85 { ui::VKEY_Y, 29 }, 86 { ui::VKEY_Z, 52 }, 87 { ui::VKEY_LWIN, 133 }, 88 { ui::VKEY_NUMPAD0, 90 }, 89 { ui::VKEY_NUMPAD1, 87 }, 90 { ui::VKEY_NUMPAD2, 88 }, 91 { ui::VKEY_NUMPAD3, 89 }, 92 { ui::VKEY_NUMPAD4, 83 }, 93 { ui::VKEY_NUMPAD5, 84 }, 94 { ui::VKEY_NUMPAD6, 85 }, 95 { ui::VKEY_NUMPAD7, 79 }, 96 { ui::VKEY_NUMPAD8, 80 }, 97 { ui::VKEY_NUMPAD9, 81 }, 98 { ui::VKEY_MULTIPLY, 63 }, 99 { ui::VKEY_ADD, 86 }, 100 { ui::VKEY_SUBTRACT, 82 }, 101 { ui::VKEY_DECIMAL, 129 }, 102 { ui::VKEY_DIVIDE, 106 }, 103 { ui::VKEY_F1, 67 }, 104 { ui::VKEY_F2, 68 }, 105 { ui::VKEY_F3, 69 }, 106 { ui::VKEY_F4, 70 }, 107 { ui::VKEY_F5, 71 }, 108 { ui::VKEY_F6, 72 }, 109 { ui::VKEY_F7, 73 }, 110 { ui::VKEY_F8, 74 }, 111 { ui::VKEY_F9, 75 }, 112 { ui::VKEY_F10, 76 }, 113 { ui::VKEY_F11, 95 }, 114 { ui::VKEY_F12, 96 }, 115 { ui::VKEY_NUMLOCK, 77 }, 116 { ui::VKEY_SCROLL, 78 }, 117 { ui::VKEY_OEM_1, 47 }, 118 { ui::VKEY_OEM_PLUS, 21 }, 119 { ui::VKEY_OEM_COMMA, 59 }, 120 { ui::VKEY_OEM_MINUS, 20 }, 121 { ui::VKEY_OEM_PERIOD, 60 }, 122 { ui::VKEY_OEM_2, 61 }, 123 { ui::VKEY_OEM_3, 49 }, 124 { ui::VKEY_OEM_4, 34 }, 125 { ui::VKEY_OEM_5, 51 }, 126 { ui::VKEY_OEM_6, 35 }, 127 { ui::VKEY_OEM_7, 48 } 128 }; 129 130 // Uses to compare two KeyCodeAndXKeyCode structs based on their key code. 131 bool operator<(const KeyCodeAndXKeyCode& a, const KeyCodeAndXKeyCode& b) { 132 return a.key_code < b.key_code; 133 } 134 135 // Returns the equivalent X key code for the given key code. Returns -1 if 136 // no X equivalent was found. 137 int KeyboardCodeToXKeyCode(ui::KeyboardCode key_code) { 138 KeyCodeAndXKeyCode find; 139 find.key_code = key_code; 140 const KeyCodeAndXKeyCode* found = std::lower_bound( 141 kKeyCodeToXKeyCode, kKeyCodeToXKeyCode + arraysize(kKeyCodeToXKeyCode), 142 find); 143 if (found >= kKeyCodeToXKeyCode + arraysize(kKeyCodeToXKeyCode) || 144 found->key_code != key_code) 145 return -1; 146 return found->x_key_code; 147 } 148 149 // Gets the X modifier mask (Mod1Mask through Mod5Mask) for the given 150 // modifier. Only checks the alt, meta, and num lock keys currently. 151 // Returns true on success. 152 bool GetXModifierMask(Display* display, int modifier, int* x_modifier) { 153 XModifierKeymap* mod_map = XGetModifierMapping(display); 154 bool found = false; 155 int max_mod_keys = mod_map->max_keypermod; 156 for (int mod_index = 0; mod_index <= 8; ++mod_index) { 157 for (int key_index = 0; key_index < max_mod_keys; ++key_index) { 158 int key = mod_map->modifiermap[mod_index * max_mod_keys + key_index]; 159 int keysym = XkbKeycodeToKeysym(display, key, 0, 0); 160 if (modifier == kAltKeyModifierMask) 161 found = keysym == XK_Alt_L || keysym == XK_Alt_R; 162 else if (modifier == kMetaKeyModifierMask) 163 found = keysym == XK_Meta_L || keysym == XK_Meta_R; 164 else if (modifier == kNumLockKeyModifierMask) 165 found = keysym == XK_Num_Lock; 166 if (found) { 167 *x_modifier = 1 << mod_index; 168 break; 169 } 170 } 171 if (found) 172 break; 173 } 174 XFreeModifiermap(mod_map); 175 return found; 176 } 177 178 } // namespace 179 180 bool ConvertKeyCodeToText( 181 ui::KeyboardCode key_code, int modifiers, std::string* text, 182 std::string* error_msg) { 183 *error_msg = std::string(); 184 int x_key_code = KeyboardCodeToXKeyCode(key_code); 185 if (x_key_code == -1) { 186 *text = std::string(); 187 return true; 188 } 189 190 XEvent event; 191 memset(&event, 0, sizeof(XEvent)); 192 XKeyEvent* key_event = &event.xkey; 193 XDisplay* display = gfx::GetXDisplay(); 194 if (!display) { 195 *error_msg = 196 "an X display is required for keycode conversions, consider using Xvfb"; 197 *text = std::string(); 198 return false; 199 } 200 key_event->display = display; 201 key_event->keycode = x_key_code; 202 if (modifiers & kShiftKeyModifierMask) 203 key_event->state |= ShiftMask; 204 if (modifiers & kControlKeyModifierMask) 205 key_event->state |= ControlMask; 206 207 // Make a best attempt for non-standard modifiers. 208 int x_modifier; 209 if (modifiers & kAltKeyModifierMask && 210 GetXModifierMask(display, kAltKeyModifierMask, &x_modifier)) { 211 key_event->state |= x_modifier; 212 } 213 if (modifiers & kMetaKeyModifierMask && 214 GetXModifierMask(display, kMetaKeyModifierMask, &x_modifier)) { 215 key_event->state |= x_modifier; 216 } 217 if (modifiers & kNumLockKeyModifierMask && 218 GetXModifierMask(display, kNumLockKeyModifierMask, &x_modifier)) { 219 key_event->state |= x_modifier; 220 } 221 key_event->type = KeyPress; 222 uint16 character = ui::GetCharacterFromXEvent(&event); 223 224 if (!character) 225 *text = std::string(); 226 else 227 *text = UTF16ToUTF8(string16(1, character)); 228 return true; 229 } 230 231 bool ConvertCharToKeyCode( 232 char16 key, 233 ui::KeyboardCode* key_code, 234 int* necessary_modifiers, 235 std::string* error_msg) { 236 std::string key_string(UTF16ToUTF8(string16(1, key))); 237 bool found = false; 238 ui::KeyboardCode test_code; 239 int test_modifiers; 240 *error_msg = std::string(); 241 std::string conv_string; 242 for (size_t i = 0; i < arraysize(kKeyCodeToXKeyCode); ++i) { 243 test_code = kKeyCodeToXKeyCode[i].key_code; 244 // Skip the numpad keys. 245 if (test_code >= ui::VKEY_NUMPAD0 && test_code <= ui::VKEY_DIVIDE) 246 continue; 247 test_modifiers = 0; 248 if (!ConvertKeyCodeToText( 249 test_code, test_modifiers, &conv_string, error_msg)) 250 return false; 251 if (conv_string == key_string) { 252 found = true; 253 break; 254 } 255 test_modifiers = kShiftKeyModifierMask; 256 if (!ConvertKeyCodeToText( 257 test_code, test_modifiers, &conv_string, error_msg)) 258 return false; 259 if (conv_string == key_string) { 260 found = true; 261 break; 262 } 263 } 264 if (found) { 265 *key_code = test_code; 266 *necessary_modifiers = test_modifiers; 267 } 268 return found; 269 } 270