/** * FreeRDP: A Remote Desktop Protocol Implementation * XKB Keyboard Mapping * * Copyright 2009-2012 Marc-Andre Moreau * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "keyboard_xkbfile.h" #include #include #include #include #include #include "keyboard_x11.h" #include "xkb_layout_ids.h" #include "liblocale.h" #include #include #include #include struct _XKB_KEY_NAME_SCANCODE { const char* xkb_keyname; /* XKB keyname */ RDP_SCANCODE rdp_scancode; }; typedef struct _XKB_KEY_NAME_SCANCODE XKB_KEY_NAME_SCANCODE; XKB_KEY_NAME_SCANCODE XKB_KEY_NAME_SCANCODE_TABLE[] = { { "BKSP", RDP_SCANCODE_BACKSPACE}, { "TAB", RDP_SCANCODE_TAB}, { "RTRN", RDP_SCANCODE_RETURN}, // not KP { "LFSH", RDP_SCANCODE_LSHIFT}, { "LALT", RDP_SCANCODE_LMENU}, { "CAPS", RDP_SCANCODE_CAPSLOCK}, { "ESC", RDP_SCANCODE_ESCAPE}, { "SPCE", RDP_SCANCODE_SPACE}, { "AE10", RDP_SCANCODE_KEY_0}, { "AE01", RDP_SCANCODE_KEY_1}, { "AE02", RDP_SCANCODE_KEY_2}, { "AE03", RDP_SCANCODE_KEY_3}, { "AE04", RDP_SCANCODE_KEY_4}, { "AE05", RDP_SCANCODE_KEY_5}, { "AE06", RDP_SCANCODE_KEY_6}, { "AE07", RDP_SCANCODE_KEY_7}, { "AE08", RDP_SCANCODE_KEY_8}, { "AE09", RDP_SCANCODE_KEY_9}, { "AC01", RDP_SCANCODE_KEY_A}, { "AB05", RDP_SCANCODE_KEY_B}, { "AB03", RDP_SCANCODE_KEY_C}, { "AC03", RDP_SCANCODE_KEY_D}, { "AD03", RDP_SCANCODE_KEY_E}, { "AC04", RDP_SCANCODE_KEY_F}, { "AC05", RDP_SCANCODE_KEY_G}, { "AC06", RDP_SCANCODE_KEY_H}, { "AD08", RDP_SCANCODE_KEY_I}, { "AC07", RDP_SCANCODE_KEY_J}, { "AC08", RDP_SCANCODE_KEY_K}, { "AC09", RDP_SCANCODE_KEY_L}, { "AB07", RDP_SCANCODE_KEY_M}, { "AB06", RDP_SCANCODE_KEY_N}, { "AD09", RDP_SCANCODE_KEY_O}, { "AD10", RDP_SCANCODE_KEY_P}, { "AD01", RDP_SCANCODE_KEY_Q}, { "AD04", RDP_SCANCODE_KEY_R}, { "AC02", RDP_SCANCODE_KEY_S}, { "AD05", RDP_SCANCODE_KEY_T}, { "AD07", RDP_SCANCODE_KEY_U}, { "AB04", RDP_SCANCODE_KEY_V}, { "AD02", RDP_SCANCODE_KEY_W}, { "AB02", RDP_SCANCODE_KEY_X}, { "AD06", RDP_SCANCODE_KEY_Y}, { "AB01", RDP_SCANCODE_KEY_Z}, { "KP0", RDP_SCANCODE_NUMPAD0}, { "KP1", RDP_SCANCODE_NUMPAD1}, { "KP2", RDP_SCANCODE_NUMPAD2}, { "KP3", RDP_SCANCODE_NUMPAD3}, { "KP4", RDP_SCANCODE_NUMPAD4}, { "KP5", RDP_SCANCODE_NUMPAD5}, { "KP6", RDP_SCANCODE_NUMPAD6}, { "KP7", RDP_SCANCODE_NUMPAD7}, { "KP8", RDP_SCANCODE_NUMPAD8}, { "KP9", RDP_SCANCODE_NUMPAD9}, { "KPMU", RDP_SCANCODE_MULTIPLY}, { "KPAD", RDP_SCANCODE_ADD}, { "KPSU", RDP_SCANCODE_SUBTRACT}, { "KPDL", RDP_SCANCODE_DECIMAL}, { "AB10", RDP_SCANCODE_OEM_2}, // not KP, not RDP_SCANCODE_DIVIDE { "FK01", RDP_SCANCODE_F1}, { "FK02", RDP_SCANCODE_F2}, { "FK03", RDP_SCANCODE_F3}, { "FK04", RDP_SCANCODE_F4}, { "FK05", RDP_SCANCODE_F5}, { "FK06", RDP_SCANCODE_F6}, { "FK07", RDP_SCANCODE_F7}, { "FK08", RDP_SCANCODE_F8}, { "FK09", RDP_SCANCODE_F9}, { "FK10", RDP_SCANCODE_F10}, { "FK11", RDP_SCANCODE_F11}, { "FK12", RDP_SCANCODE_F12}, { "NMLK", RDP_SCANCODE_NUMLOCK}, { "SCLK", RDP_SCANCODE_SCROLLLOCK}, { "RTSH", RDP_SCANCODE_RSHIFT}, { "LCTL", RDP_SCANCODE_LCONTROL}, { "AC10", RDP_SCANCODE_OEM_1}, { "AE12", RDP_SCANCODE_OEM_PLUS}, { "AB08", RDP_SCANCODE_OEM_COMMA}, { "AE11", RDP_SCANCODE_OEM_MINUS}, { "AB09", RDP_SCANCODE_OEM_PERIOD}, { "TLDE", RDP_SCANCODE_OEM_3}, { "AB11", RDP_SCANCODE_ABNT_C1}, { "I129", RDP_SCANCODE_ABNT_C2}, { "AD11", RDP_SCANCODE_OEM_4}, { "BKSL", RDP_SCANCODE_OEM_5}, { "AD12", RDP_SCANCODE_OEM_6}, { "AC11", RDP_SCANCODE_OEM_7}, { "LSGT", RDP_SCANCODE_OEM_102}, { "KPEN", RDP_SCANCODE_RETURN_KP}, // KP! { "PAUS", RDP_SCANCODE_PAUSE}, { "PGUP", RDP_SCANCODE_PRIOR}, { "PGDN", RDP_SCANCODE_NEXT}, { "END", RDP_SCANCODE_END}, { "HOME", RDP_SCANCODE_HOME}, { "LEFT", RDP_SCANCODE_LEFT}, { "UP", RDP_SCANCODE_UP}, { "RGHT", RDP_SCANCODE_RIGHT}, { "DOWN", RDP_SCANCODE_DOWN}, { "PRSC", RDP_SCANCODE_PRINTSCREEN}, { "INS", RDP_SCANCODE_INSERT}, { "DELE", RDP_SCANCODE_DELETE}, { "LWIN", RDP_SCANCODE_LWIN}, { "RWIN", RDP_SCANCODE_RWIN}, { "COMP", RDP_SCANCODE_APPS}, { "KPDV", RDP_SCANCODE_DIVIDE}, // KP! { "RCTL", RDP_SCANCODE_RCONTROL}, { "RALT", RDP_SCANCODE_RMENU}, { "AE13", RDP_SCANCODE_BACKSLASH_JP}, // JP { "HKTG", RDP_SCANCODE_HIRAGANA}, // JP { "HENK", RDP_SCANCODE_CONVERT_JP}, // JP { "MUHE", RDP_SCANCODE_NONCONVERT_JP} // JP /* { "LVL3", 0x54} */ }; void* freerdp_keyboard_xkb_init() { int status; Display* display = XOpenDisplay(NULL); if (display == NULL) return NULL; status = XkbQueryExtension(display, NULL, NULL, NULL, NULL, NULL); if (!status) return NULL; return (void*) display; } UINT32 freerdp_keyboard_init_xkbfile(UINT32 keyboardLayoutId, RDP_SCANCODE x11_keycode_to_rdp_scancode[256]) { void* display; memset(x11_keycode_to_rdp_scancode, 0, sizeof(x11_keycode_to_rdp_scancode)); display = freerdp_keyboard_xkb_init(); if (!display) { DEBUG_KBD("Error initializing xkb"); return 0; } if (keyboardLayoutId == 0) { keyboardLayoutId = detect_keyboard_layout_from_xkbfile(display); DEBUG_KBD("detect_keyboard_layout_from_xkb: %X", keyboardLayoutId); } freerdp_keyboard_load_map_from_xkbfile(display, x11_keycode_to_rdp_scancode); XCloseDisplay(display); return keyboardLayoutId; } /* return substring starting after nth comma, ending at following comma */ static char* comma_substring(char* s, int n) { char *p; if (!s) return ""; while (n-- > 0) { if (!(p = strchr(s, ','))) break; s = p + 1; } if ((p = strchr(s, ','))) *p = 0; return s; } UINT32 detect_keyboard_layout_from_xkbfile(void* display) { char* layout; char* variant; UINT32 group = 0; UINT32 keyboard_layout = 0; XkbRF_VarDefsRec rules_names; XKeyboardState coreKbdState; XkbStateRec state; DEBUG_KBD("display: %p", display); if (display && XkbRF_GetNamesProp(display, NULL, &rules_names)) { DEBUG_KBD("layouts: %s", rules_names.layout ? rules_names.layout : ""); DEBUG_KBD("variants: %s", rules_names.variant ? rules_names.variant : ""); XGetKeyboardControl(display, &coreKbdState); if (XkbGetState(display, XkbUseCoreKbd, &state) == Success) group = state.group; DEBUG_KBD("group: %d", state.group); layout = comma_substring(rules_names.layout, group); variant = comma_substring(rules_names.variant, group); DEBUG_KBD("layout: %s", layout ? layout : ""); DEBUG_KBD("variant: %s", variant ? variant : ""); keyboard_layout = find_keyboard_layout_in_xorg_rules(layout, variant); free(rules_names.model); free(rules_names.layout); free(rules_names.variant); free(rules_names.options); } return keyboard_layout; } int freerdp_keyboard_load_map_from_xkbfile(void* display, RDP_SCANCODE x11_keycode_to_rdp_scancode[256]) { int i, j; BOOL found; XkbDescPtr xkb; BOOL status = FALSE; if (display && (xkb = XkbGetMap(display, 0, XkbUseCoreKbd))) { if (XkbGetNames(display, XkbKeyNamesMask, xkb) == Success) { char xkb_keyname[5] = { 42, 42, 42, 42, 0 }; /* end-of-string at index 5 */ for (i = xkb->min_key_code; i <= xkb->max_key_code; i++) { found = FALSE; memcpy(xkb_keyname, xkb->names->keys[i].name, 4); if (strlen(xkb_keyname) < 1) continue; for (j = 0; j < ARRAY_SIZE(XKB_KEY_NAME_SCANCODE_TABLE); j++) { if (!strcmp(xkb_keyname, XKB_KEY_NAME_SCANCODE_TABLE[j].xkb_keyname)) { DEBUG_KBD("%4s: keycode: 0x%02X -> rdp scancode: 0x%04X", xkb_keyname, i, XKB_KEY_NAME_SCANCODE_TABLE[j].rdp_scancode); if (found) { DEBUG_KBD("Internal error! duplicate key %s!", xkb_keyname); } x11_keycode_to_rdp_scancode[i] = XKB_KEY_NAME_SCANCODE_TABLE[j].rdp_scancode; found = TRUE; } } if (!found) { DEBUG_KBD("%4s: keycode: 0x%02X -> no RDP scancode found", xkb_keyname, i); } } status = TRUE; } XkbFreeKeyboard(xkb, 0, 1); } return status; }