FreeRDP/libfreerdp/locale/keyboard_xkbfile.c
2012-10-09 03:26:39 -04:00

324 lines
8.4 KiB
C

/**
* FreeRDP: A Remote Desktop Protocol Implementation
* XKB Keyboard Mapping
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <freerdp/locale/keyboard.h>
#include <freerdp/utils/memory.h>
#include "keyboard_x11.h"
#include "xkb_layout_ids.h"
#include "liblocale.h"
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XKBfile.h>
#include <X11/extensions/XKBrules.h>
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;
}