20#include <freerdp/config.h> 
   22#include "keyboard_xkbfile.h" 
   29#include <winpr/input.h> 
   31#include <freerdp/locale/keyboard.h> 
   33#include "keyboard_x11.h" 
   34#include "xkb_layout_ids.h" 
   39#include <X11/XKBlib.h> 
   40#include <X11/extensions/XKBfile.h> 
   41#include <X11/extensions/XKBrules.h> 
   45  const char* xkb_keyname; 
 
   47} XKB_KEY_NAME_SCANCODE;
 
   49static const XKB_KEY_NAME_SCANCODE XKB_KEY_NAME_SCANCODE_TABLE[] = {
 
   50  { 
"", RDP_SCANCODE_UNKNOWN },                 
 
   51  { 
"ESC", RDP_SCANCODE_ESCAPE },               
 
   52  { 
"AE01", RDP_SCANCODE_KEY_1 },               
 
   53  { 
"AE02", RDP_SCANCODE_KEY_2 },               
 
   54  { 
"AE03", RDP_SCANCODE_KEY_3 },               
 
   55  { 
"AE04", RDP_SCANCODE_KEY_4 },               
 
   56  { 
"AE05", RDP_SCANCODE_KEY_5 },               
 
   57  { 
"AE06", RDP_SCANCODE_KEY_6 },               
 
   58  { 
"AE07", RDP_SCANCODE_KEY_7 },               
 
   59  { 
"AE08", RDP_SCANCODE_KEY_8 },               
 
   60  { 
"AE09", RDP_SCANCODE_KEY_9 },               
 
   61  { 
"AE10", RDP_SCANCODE_KEY_0 },               
 
   62  { 
"AE11", RDP_SCANCODE_OEM_MINUS },           
 
   63  { 
"AE12", RDP_SCANCODE_OEM_PLUS },            
 
   64  { 
"BKSP", RDP_SCANCODE_BACKSPACE },           
 
   65  { 
"TAB", RDP_SCANCODE_TAB },                  
 
   66  { 
"AD01", RDP_SCANCODE_KEY_Q },               
 
   67  { 
"AD02", RDP_SCANCODE_KEY_W },               
 
   68  { 
"AD03", RDP_SCANCODE_KEY_E },               
 
   69  { 
"AD04", RDP_SCANCODE_KEY_R },               
 
   70  { 
"AD05", RDP_SCANCODE_KEY_T },               
 
   71  { 
"AD06", RDP_SCANCODE_KEY_Y },               
 
   72  { 
"AD07", RDP_SCANCODE_KEY_U },               
 
   73  { 
"AD08", RDP_SCANCODE_KEY_I },               
 
   74  { 
"AD09", RDP_SCANCODE_KEY_O },               
 
   75  { 
"AD10", RDP_SCANCODE_KEY_P },               
 
   76  { 
"AD11", RDP_SCANCODE_OEM_4 },               
 
   77  { 
"AD12", RDP_SCANCODE_OEM_6 },               
 
   78  { 
"RTRN", RDP_SCANCODE_RETURN },              
 
   79  { 
"LCTL", RDP_SCANCODE_LCONTROL },            
 
   80  { 
"AC01", RDP_SCANCODE_KEY_A },               
 
   81  { 
"AC02", RDP_SCANCODE_KEY_S },               
 
   82  { 
"AC03", RDP_SCANCODE_KEY_D },               
 
   83  { 
"AC04", RDP_SCANCODE_KEY_F },               
 
   84  { 
"AC05", RDP_SCANCODE_KEY_G },               
 
   85  { 
"AC06", RDP_SCANCODE_KEY_H },               
 
   86  { 
"AC07", RDP_SCANCODE_KEY_J },               
 
   87  { 
"AC08", RDP_SCANCODE_KEY_K },               
 
   88  { 
"AC09", RDP_SCANCODE_KEY_L },               
 
   89  { 
"AC10", RDP_SCANCODE_OEM_1 },               
 
   90  { 
"AC11", RDP_SCANCODE_OEM_7 },               
 
   91  { 
"TLDE", RDP_SCANCODE_OEM_3 },               
 
   92  { 
"LFSH", RDP_SCANCODE_LSHIFT },              
 
   93  { 
"BKSL", RDP_SCANCODE_OEM_5 },               
 
   94  { 
"AB01", RDP_SCANCODE_KEY_Z },               
 
   95  { 
"AB02", RDP_SCANCODE_KEY_X },               
 
   96  { 
"AB03", RDP_SCANCODE_KEY_C },               
 
   97  { 
"AB04", RDP_SCANCODE_KEY_V },               
 
   98  { 
"AB05", RDP_SCANCODE_KEY_B },               
 
   99  { 
"AB06", RDP_SCANCODE_KEY_N },               
 
  100  { 
"AB07", RDP_SCANCODE_KEY_M },               
 
  101  { 
"AB08", RDP_SCANCODE_OEM_COMMA },           
 
  102  { 
"AB09", RDP_SCANCODE_OEM_PERIOD },          
 
  103  { 
"AB10", RDP_SCANCODE_OEM_2 },               
 
  104  { 
"RTSH", RDP_SCANCODE_RSHIFT },              
 
  105  { 
"KPMU", RDP_SCANCODE_MULTIPLY },            
 
  106  { 
"LALT", RDP_SCANCODE_LMENU },               
 
  107  { 
"SPCE", RDP_SCANCODE_SPACE },               
 
  108  { 
"CAPS", RDP_SCANCODE_CAPSLOCK },            
 
  109  { 
"FK01", RDP_SCANCODE_F1 },                  
 
  110  { 
"FK02", RDP_SCANCODE_F2 },                  
 
  111  { 
"FK03", RDP_SCANCODE_F3 },                  
 
  112  { 
"FK04", RDP_SCANCODE_F4 },                  
 
  113  { 
"FK05", RDP_SCANCODE_F5 },                  
 
  114  { 
"FK06", RDP_SCANCODE_F6 },                  
 
  115  { 
"FK07", RDP_SCANCODE_F7 },                  
 
  116  { 
"FK08", RDP_SCANCODE_F8 },                  
 
  117  { 
"FK09", RDP_SCANCODE_F9 },                  
 
  118  { 
"FK10", RDP_SCANCODE_F10 },                 
 
  119  { 
"NMLK", RDP_SCANCODE_NUMLOCK },             
 
  120  { 
"SCLK", RDP_SCANCODE_SCROLLLOCK },          
 
  121  { 
"KP7", RDP_SCANCODE_NUMPAD7 },              
 
  122  { 
"KP8", RDP_SCANCODE_NUMPAD8 },              
 
  123  { 
"KP9", RDP_SCANCODE_NUMPAD9 },              
 
  124  { 
"KPSU", RDP_SCANCODE_SUBTRACT },            
 
  125  { 
"KP4", RDP_SCANCODE_NUMPAD4 },              
 
  126  { 
"KP5", RDP_SCANCODE_NUMPAD5 },              
 
  127  { 
"KP6", RDP_SCANCODE_NUMPAD6 },              
 
  128  { 
"KPAD", RDP_SCANCODE_ADD },                 
 
  129  { 
"KP1", RDP_SCANCODE_NUMPAD1 },              
 
  130  { 
"KP2", RDP_SCANCODE_NUMPAD2 },              
 
  131  { 
"KP3", RDP_SCANCODE_NUMPAD3 },              
 
  132  { 
"KP0", RDP_SCANCODE_NUMPAD0 },              
 
  133  { 
"KPDL", RDP_SCANCODE_DECIMAL },             
 
  134  { 
"LVL3", RDP_SCANCODE_RMENU },               
 
  135  { 
"", RDP_SCANCODE_UNKNOWN },                 
 
  136  { 
"LSGT", RDP_SCANCODE_OEM_102 },             
 
  137  { 
"FK11", RDP_SCANCODE_F11 },                 
 
  138  { 
"FK12", RDP_SCANCODE_F12 },                 
 
  139  { 
"AB11", RDP_SCANCODE_ABNT_C1 },             
 
  140  { 
"KATA", RDP_SCANCODE_KANA_HANGUL },         
 
  141  { 
"HIRA", RDP_SCANCODE_HIRAGANA },            
 
  142  { 
"HENK", RDP_SCANCODE_CONVERT_JP },          
 
  143  { 
"HKTG", RDP_SCANCODE_HIRAGANA },            
 
  144  { 
"MUHE", RDP_SCANCODE_NONCONVERT_JP },       
 
  145  { 
"JPCM", RDP_SCANCODE_UNKNOWN },             
 
  146  { 
"KPEN", RDP_SCANCODE_RETURN_KP },           
 
  147  { 
"RCTL", RDP_SCANCODE_RCONTROL },            
 
  148  { 
"KPDV", RDP_SCANCODE_DIVIDE },              
 
  149  { 
"PRSC", RDP_SCANCODE_PRINTSCREEN },         
 
  150  { 
"RALT", RDP_SCANCODE_RMENU },               
 
  151  { 
"LNFD", RDP_SCANCODE_UNKNOWN },             
 
  152  { 
"HOME", RDP_SCANCODE_HOME },                
 
  153  { 
"UP", RDP_SCANCODE_UP },                    
 
  154  { 
"PGUP", RDP_SCANCODE_PRIOR },               
 
  155  { 
"LEFT", RDP_SCANCODE_LEFT },                
 
  156  { 
"RGHT", RDP_SCANCODE_RIGHT },               
 
  157  { 
"END", RDP_SCANCODE_END },                  
 
  158  { 
"DOWN", RDP_SCANCODE_DOWN },                
 
  159  { 
"PGDN", RDP_SCANCODE_NEXT },                
 
  160  { 
"INS", RDP_SCANCODE_INSERT },               
 
  161  { 
"DELE", RDP_SCANCODE_DELETE },              
 
  162  { 
"I120", RDP_SCANCODE_UNKNOWN },             
 
  163  { 
"MUTE", RDP_SCANCODE_VOLUME_MUTE },         
 
  164  { 
"VOL-", RDP_SCANCODE_VOLUME_DOWN },         
 
  165  { 
"VOL+", RDP_SCANCODE_VOLUME_UP },           
 
  166  { 
"POWR", RDP_SCANCODE_UNKNOWN },             
 
  167  { 
"KPEQ", RDP_SCANCODE_UNKNOWN },             
 
  168  { 
"I126", RDP_SCANCODE_UNKNOWN },             
 
  169  { 
"PAUS", RDP_SCANCODE_PAUSE },               
 
  170  { 
"I128", RDP_SCANCODE_LAUNCH_MEDIA_SELECT }, 
 
  171  { 
"I129", RDP_SCANCODE_ABNT_C2 },             
 
  172  { 
"HNGL", RDP_SCANCODE_HANGUL },              
 
  173  { 
"HJCV", RDP_SCANCODE_HANJA },               
 
  174  { 
"AE13", RDP_SCANCODE_BACKSLASH_JP },        
 
  175  { 
"LWIN", RDP_SCANCODE_LWIN },                
 
  176  { 
"RWIN", RDP_SCANCODE_RWIN },                
 
  177  { 
"COMP", RDP_SCANCODE_APPS },                
 
  178  { 
"STOP", RDP_SCANCODE_BROWSER_STOP },        
 
  179  { 
"AGAI", RDP_SCANCODE_UNKNOWN },             
 
  180  { 
"PROP", RDP_SCANCODE_UNKNOWN },             
 
  181  { 
"UNDO", RDP_SCANCODE_UNKNOWN },             
 
  182  { 
"FRNT", RDP_SCANCODE_UNKNOWN },             
 
  183  { 
"COPY", RDP_SCANCODE_UNKNOWN },             
 
  184  { 
"OPEN", RDP_SCANCODE_UNKNOWN },             
 
  185  { 
"PAST", RDP_SCANCODE_UNKNOWN },             
 
  186  { 
"FIND", RDP_SCANCODE_UNKNOWN },             
 
  187  { 
"CUT", RDP_SCANCODE_UNKNOWN },              
 
  188  { 
"HELP", RDP_SCANCODE_HELP },                
 
  189  { 
"I147", RDP_SCANCODE_UNKNOWN },             
 
  190  { 
"I148", RDP_SCANCODE_UNKNOWN },             
 
  191  { 
"I149", RDP_SCANCODE_UNKNOWN },             
 
  192  { 
"I150", RDP_SCANCODE_SLEEP },               
 
  193  { 
"I151", RDP_SCANCODE_UNKNOWN },             
 
  194  { 
"I152", RDP_SCANCODE_UNKNOWN },             
 
  195  { 
"I153", RDP_SCANCODE_UNKNOWN },             
 
  196  { 
"I154", RDP_SCANCODE_UNKNOWN },             
 
  197  { 
"I155", RDP_SCANCODE_UNKNOWN },             
 
  198  { 
"I156", RDP_SCANCODE_LAUNCH_APP1 },         
 
  199  { 
"I157", RDP_SCANCODE_LAUNCH_APP2 },         
 
  200  { 
"I158", RDP_SCANCODE_BROWSER_HOME },        
 
  201  { 
"I159", RDP_SCANCODE_UNKNOWN },             
 
  202  { 
"I160", RDP_SCANCODE_UNKNOWN },             
 
  203  { 
"I161", RDP_SCANCODE_UNKNOWN },             
 
  204  { 
"I162", RDP_SCANCODE_UNKNOWN },             
 
  205  { 
"I163", RDP_SCANCODE_LAUNCH_MAIL },         
 
  206  { 
"I164", RDP_SCANCODE_BROWSER_FAVORITES },   
 
  207  { 
"I165", RDP_SCANCODE_UNKNOWN },             
 
  208  { 
"I166", RDP_SCANCODE_BROWSER_BACK },        
 
  209  { 
"I167", RDP_SCANCODE_BROWSER_FORWARD },     
 
  210  { 
"I168", RDP_SCANCODE_UNKNOWN },             
 
  211  { 
"I169", RDP_SCANCODE_UNKNOWN },             
 
  212  { 
"I170", RDP_SCANCODE_UNKNOWN },             
 
  213  { 
"I171", RDP_SCANCODE_MEDIA_NEXT_TRACK },    
 
  214  { 
"I172", RDP_SCANCODE_MEDIA_PLAY_PAUSE },    
 
  215  { 
"I173", RDP_SCANCODE_MEDIA_PREV_TRACK },    
 
  216  { 
"I174", RDP_SCANCODE_MEDIA_STOP },          
 
  217  { 
"I175", RDP_SCANCODE_UNKNOWN },             
 
  218  { 
"I176", RDP_SCANCODE_UNKNOWN },             
 
  219  { 
"I177", RDP_SCANCODE_UNKNOWN },             
 
  220  { 
"I178", RDP_SCANCODE_UNKNOWN },             
 
  221  { 
"I179", RDP_SCANCODE_UNKNOWN },             
 
  222  { 
"I180", RDP_SCANCODE_BROWSER_HOME },        
 
  223  { 
"I181", RDP_SCANCODE_BROWSER_REFRESH },     
 
  224  { 
"I182", RDP_SCANCODE_UNKNOWN },             
 
  225  { 
"I183", RDP_SCANCODE_UNKNOWN },             
 
  226  { 
"I184", RDP_SCANCODE_UNKNOWN },             
 
  227  { 
"I185", RDP_SCANCODE_UNKNOWN },             
 
  228  { 
"I186", RDP_SCANCODE_UNKNOWN },             
 
  229  { 
"I187", RDP_SCANCODE_UNKNOWN },             
 
  230  { 
"I188", RDP_SCANCODE_UNKNOWN },             
 
  231  { 
"I189", RDP_SCANCODE_UNKNOWN },             
 
  232  { 
"I190", RDP_SCANCODE_UNKNOWN },             
 
  233  { 
"FK13", RDP_SCANCODE_F13 },                 
 
  234  { 
"FK14", RDP_SCANCODE_F14 },                 
 
  235  { 
"FK15", RDP_SCANCODE_F15 },                 
 
  236  { 
"FK16", RDP_SCANCODE_F16 },                 
 
  237  { 
"FK17", RDP_SCANCODE_F17 },                 
 
  238  { 
"FK18", RDP_SCANCODE_F18 },                 
 
  239  { 
"FK19", RDP_SCANCODE_F19 },                 
 
  240  { 
"FK20", RDP_SCANCODE_F20 },                 
 
  241  { 
"FK21", RDP_SCANCODE_F21 },                 
 
  242  { 
"FK22", RDP_SCANCODE_F22 },                 
 
  243  { 
"FK23", RDP_SCANCODE_F23 },                 
 
  244  { 
"FK24", RDP_SCANCODE_F24 },                 
 
  245  { 
"LVL5", RDP_SCANCODE_UNKNOWN },             
 
  246  { 
"ALT", RDP_SCANCODE_LMENU },                
 
  247  { 
"META", RDP_SCANCODE_LMENU },               
 
  248  { 
"SUPR", RDP_SCANCODE_LWIN },                
 
  249  { 
"HYPR", RDP_SCANCODE_LWIN },                
 
  250  { 
"I208", RDP_SCANCODE_MEDIA_PLAY_PAUSE },    
 
  251  { 
"I209", RDP_SCANCODE_MEDIA_PLAY_PAUSE },    
 
  252  { 
"I210", RDP_SCANCODE_UNKNOWN },             
 
  253  { 
"I211", RDP_SCANCODE_UNKNOWN },             
 
  254  { 
"I212", RDP_SCANCODE_UNKNOWN },             
 
  255  { 
"I213", RDP_SCANCODE_UNKNOWN },             
 
  256  { 
"I214", RDP_SCANCODE_UNKNOWN },             
 
  257  { 
"I215", RDP_SCANCODE_MEDIA_PLAY_PAUSE },    
 
  258  { 
"I216", RDP_SCANCODE_MEDIA_NEXT_TRACK },    
 
  259  { 
"I217", RDP_SCANCODE_UNKNOWN },             
 
  260  { 
"I218", RDP_SCANCODE_UNKNOWN },             
 
  261  { 
"I219", RDP_SCANCODE_UNKNOWN },             
 
  262  { 
"I220", RDP_SCANCODE_UNKNOWN },             
 
  263  { 
"I221", RDP_SCANCODE_UNKNOWN },             
 
  264  { 
"I222", RDP_SCANCODE_UNKNOWN },             
 
  265  { 
"I223", RDP_SCANCODE_LAUNCH_MAIL },         
 
  266  { 
"I224", RDP_SCANCODE_UNKNOWN },             
 
  267  { 
"I225", RDP_SCANCODE_BROWSER_SEARCH },      
 
  268  { 
"I226", RDP_SCANCODE_UNKNOWN },             
 
  269  { 
"I227", RDP_SCANCODE_UNKNOWN },             
 
  270  { 
"I228", RDP_SCANCODE_UNKNOWN },             
 
  271  { 
"I229", RDP_SCANCODE_UNKNOWN },             
 
  272  { 
"I230", RDP_SCANCODE_UNKNOWN },             
 
  273  { 
"I231", RDP_SCANCODE_UNKNOWN },             
 
  274  { 
"I232", RDP_SCANCODE_UNKNOWN },             
 
  275  { 
"I233", RDP_SCANCODE_UNKNOWN },             
 
  276  { 
"I234", RDP_SCANCODE_LAUNCH_MEDIA_SELECT }, 
 
  277  { 
"I235", RDP_SCANCODE_UNKNOWN },             
 
  278  { 
"I236", RDP_SCANCODE_UNKNOWN },             
 
  279  { 
"I237", RDP_SCANCODE_UNKNOWN },             
 
  280  { 
"I238", RDP_SCANCODE_UNKNOWN },             
 
  281  { 
"I239", RDP_SCANCODE_UNKNOWN },             
 
  282  { 
"I240", RDP_SCANCODE_UNKNOWN },             
 
  283  { 
"I241", RDP_SCANCODE_UNKNOWN },             
 
  284  { 
"I242", RDP_SCANCODE_UNKNOWN },             
 
  285  { 
"I243", RDP_SCANCODE_UNKNOWN },             
 
  286  { 
"I244", RDP_SCANCODE_UNKNOWN },             
 
  287  { 
"I245", RDP_SCANCODE_UNKNOWN },             
 
  288  { 
"I246", RDP_SCANCODE_UNKNOWN },             
 
  289  { 
"I247", RDP_SCANCODE_UNKNOWN },             
 
  290  { 
"I248", RDP_SCANCODE_UNKNOWN },             
 
  291  { 
"I249", RDP_SCANCODE_UNKNOWN },             
 
  292  { 
"I250", RDP_SCANCODE_UNKNOWN },             
 
  293  { 
"I251", RDP_SCANCODE_UNKNOWN },             
 
  294  { 
"I252", RDP_SCANCODE_UNKNOWN },             
 
  295  { 
"I253", RDP_SCANCODE_UNKNOWN },             
 
  296  { 
"I254", RDP_SCANCODE_UNKNOWN },             
 
  297  { 
"I255", RDP_SCANCODE_UNKNOWN }              
 
  300static int detect_keyboard_layout_from_xkbfile(
void* display, DWORD* keyboardLayoutId);
 
  301static int freerdp_keyboard_load_map_from_xkbfile(
void* display, DWORD* x11_keycode_to_rdp_scancode,
 
  304static void* freerdp_keyboard_xkb_init(
void)
 
  308  Display* display = XOpenDisplay(NULL);
 
  313  status = XkbQueryExtension(display, NULL, NULL, NULL, NULL, NULL);
 
  318  return (
void*)display;
 
  321int freerdp_keyboard_init_xkbfile(DWORD* keyboardLayoutId, DWORD* x11_keycode_to_rdp_scancode,
 
  324  WINPR_ASSERT(keyboardLayoutId);
 
  325  WINPR_ASSERT(x11_keycode_to_rdp_scancode);
 
  326  ZeroMemory(x11_keycode_to_rdp_scancode, 
sizeof(DWORD) * count);
 
  328  void* display = freerdp_keyboard_xkb_init();
 
  332    DEBUG_KBD(
"Error initializing xkb");
 
  336  if (*keyboardLayoutId == 0)
 
  338    detect_keyboard_layout_from_xkbfile(display, keyboardLayoutId);
 
  339    DEBUG_KBD(
"detect_keyboard_layout_from_xkb: %" PRIu32 
" (0x%08" PRIX32 
")",
 
  340              *keyboardLayoutId, *keyboardLayoutId);
 
  344      freerdp_keyboard_load_map_from_xkbfile(display, x11_keycode_to_rdp_scancode, count);
 
  346  XCloseDisplay(display);
 
  352static char* comma_substring(
char* s, 
size_t n)
 
  361    if (!(p = strchr(s, 
',')))
 
  367  if ((p = strchr(s, 
',')))
 
  373int detect_keyboard_layout_from_xkbfile(
void* display, DWORD* keyboardLayoutId)
 
  375  DEBUG_KBD(
"display: %p", display);
 
  380  XkbRF_VarDefsRec rules_names = { 0 };
 
  381  const Bool rc = XkbRF_GetNamesProp(display, &rules, &rules_names);
 
  384    DEBUG_KBD(
"XkbRF_GetNamesProp == False");
 
  388    DEBUG_KBD(
"rules: %s", rules ? rules : 
"");
 
  389    DEBUG_KBD(
"model: %s", rules_names.model ? rules_names.model : 
"");
 
  390    DEBUG_KBD(
"layouts: %s", rules_names.layout ? rules_names.layout : 
"");
 
  391    DEBUG_KBD(
"variants: %s", rules_names.variant ? rules_names.variant : 
"");
 
  394    XkbStateRec state = { 0 };
 
  395    XKeyboardState coreKbdState = { 0 };
 
  396    XGetKeyboardControl(display, &coreKbdState);
 
  398    if (XkbGetState(display, XkbUseCoreKbd, &state) == Success)
 
  401    DEBUG_KBD(
"group: %u", state.group);
 
  403    const char* layout = comma_substring(rules_names.layout, group);
 
  404    const char* variant = comma_substring(rules_names.variant, group);
 
  406    DEBUG_KBD(
"layout: %s", layout ? layout : 
"");
 
  407    DEBUG_KBD(
"variant: %s", variant ? variant : 
"");
 
  409    *keyboardLayoutId = find_keyboard_layout_in_xorg_rules(layout, variant);
 
  411  free(rules_names.model);
 
  412  free(rules_names.layout);
 
  413  free(rules_names.variant);
 
  414  free(rules_names.options);
 
  420static int xkb_cmp(
const void* pva, 
const void* pvb)
 
  422  const XKB_KEY_NAME_SCANCODE* a = pva;
 
  423  const XKB_KEY_NAME_SCANCODE* b = pvb;
 
  431  return strcmp(a->xkb_keyname, b->xkb_keyname);
 
  434static BOOL try_add(
size_t offset, 
const char* xkb_keyname, DWORD* x11_keycode_to_rdp_scancode,
 
  436                    WINPR_ATTR_UNUSED 
size_t count)
 
  438  static BOOL initialized = FALSE;
 
  439  static XKB_KEY_NAME_SCANCODE copy[ARRAYSIZE(XKB_KEY_NAME_SCANCODE_TABLE)] = { 0 };
 
  442    memcpy(copy, XKB_KEY_NAME_SCANCODE_TABLE, 
sizeof(copy));
 
  443    qsort(copy, ARRAYSIZE(copy), 
sizeof(XKB_KEY_NAME_SCANCODE), xkb_cmp);
 
  447  XKB_KEY_NAME_SCANCODE key = { 0 };
 
  448  key.xkb_keyname = xkb_keyname;
 
  449  XKB_KEY_NAME_SCANCODE* found =
 
  450      bsearch(&key, copy, ARRAYSIZE(copy), 
sizeof(XKB_KEY_NAME_SCANCODE), xkb_cmp);
 
  453    DEBUG_KBD(
"%4s: keycode: 0x%02" PRIuz 
" -> rdp scancode: 0x%08" PRIx32 
"", xkb_keyname,
 
  454              offset, found->rdp_scancode);
 
  455    x11_keycode_to_rdp_scancode[offset] = found->rdp_scancode;
 
  461int freerdp_keyboard_load_map_from_xkbfile(
void* display, DWORD* x11_keycode_to_rdp_scancode,
 
  469  XkbDescPtr xkb = XkbGetMap(display, 0, XkbUseCoreKbd);
 
  472    DEBUG_KBD(
"XkbGetMap() == NULL");
 
  476  if (XkbGetNames(display, XkbKeyNamesMask, xkb) != Success)
 
  478    DEBUG_KBD(
"XkbGetNames() != Success");
 
  482    char xkb_keyname[XkbKeyNameLength + 1] = { 42, 42, 42, 42,
 
  485    DEBUG_KBD(
"XkbGetNames() == Success, min=%" PRIu8 
", max=%" PRIu8, xkb->min_key_code,
 
  487    for (
size_t i = xkb->min_key_code; i <= MIN(xkb->max_key_code, count); i++)
 
  490      strncpy(xkb_keyname, xkb->names->keys[i].name, XkbKeyNameLength);
 
  492      DEBUG_KBD(
"KeyCode %" PRIuz 
" -> %s", i, xkb_keyname);
 
  493      if (strnlen(xkb_keyname, ARRAYSIZE(xkb_keyname)) < 1)
 
  496      found = try_add(i, xkb_keyname, x11_keycode_to_rdp_scancode, count);
 
  500        DEBUG_KBD(
"%4s: keycode: 0x%02X -> no RDP scancode found", xkb_keyname, i);
 
  507  XkbFreeKeyboard(xkb, 0, 1);