Home | History | Annotate | Download | only in chromedriver
      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/keycodes/keyboard_code_conversion_x.h"
     16 #include "ui/base/x/x11_util.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   Display* display = ui::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