--- sourceforge.net/trunk/rdesktop/xkeymap.c 2002/04/05 07:57:43 39 +++ sourceforge.net/trunk/rdesktop/xkeymap.c 2003/08/31 19:45:56 452 @@ -1,7 +1,7 @@ /* rdesktop: A Remote Desktop Protocol client. User interface services - X keyboard mapping - Copyright (C) Matthew Chapman 1999-2001 + Copyright (C) Matthew Chapman 1999-2002 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -12,37 +12,79 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifdef RDP2VNC +#include "vnc/x11stubs.h" +#else #include #include -#include -#include -#include +#endif + +#include #include +#include #include "rdesktop.h" +#include "scancodes.h" -#define KEYMAP_SIZE 4096 -#define KEYMAP_MASK (KEYMAP_SIZE - 1) +#define KEYMAP_SIZE 0xffff+1 +#define KEYMAP_MASK 0xffff +#define KEYMAP_MAX_LINE_LENGTH 80 +extern Display *g_display; extern char keymapname[16]; extern int keylayout; +extern int g_win_button_size; +extern BOOL g_enable_compose; +extern BOOL g_use_rdp5; + +static BOOL keymap_loaded; +static key_translation keymap[KEYMAP_SIZE]; +static int min_keycode; +static uint16 remote_modifier_state = 0; +static uint16 saved_remote_modifier_state = 0; + +static void update_modifier_state(uint8 scancode, BOOL pressed); + +static void +add_to_keymap(char *keyname, uint8 scancode, uint16 modifiers, char *mapname) +{ + KeySym keysym; + + keysym = XStringToKeysym(keyname); + if (keysym == NoSymbol) + { + DEBUG_KBD(("Bad keysym \"%s\" in keymap %s (ignoring)\n", keyname, mapname)); + return; + } + + DEBUG_KBD(("Adding translation, keysym=0x%x, scancode=0x%x, " + "modifiers=0x%x\n", (unsigned int) keysym, scancode, modifiers)); -static uint8 keymap[KEYMAP_SIZE]; -static unsigned int min_keycode; + keymap[keysym & KEYMAP_MASK].scancode = scancode; + keymap[keysym & KEYMAP_MASK].modifiers = modifiers; -static BOOL xkeymap_read(char *mapname) + return; +} + + +static BOOL +xkeymap_read(char *mapname) { FILE *fp; - char line[PATH_MAX], path[PATH_MAX]; + char line[KEYMAP_MAX_LINE_LENGTH]; + char path[PATH_MAX], inplace_path[PATH_MAX]; + unsigned int line_num = 0; + unsigned int line_length = 0; char *keyname, *p; - KeySym keysym; - unsigned char keycode; - unsigned int mapcode = 0; + char *line_rest; + uint8 scancode; + uint16 modifiers; + strcpy(path, KEYMAP_PATH); strncat(path, mapname, sizeof(path) - sizeof(KEYMAP_PATH)); @@ -50,47 +92,122 @@ fp = fopen(path, "r"); if (fp == NULL) { - error("Failed to open keymap %s\n", path); - return False; + /* in case we are running from the source tree */ + strcpy(inplace_path, "keymaps/"); + strncat(inplace_path, mapname, sizeof(inplace_path) - sizeof("keymaps/")); + + fp = fopen(inplace_path, "r"); + if (fp == NULL) + { + error("Failed to open keymap %s\n", path); + return False; + } } + /* FIXME: More tolerant on white space */ while (fgets(line, sizeof(line), fp) != NULL) { + line_num++; + + /* Replace the \n with \0 */ p = strchr(line, '\n'); if (p != NULL) *p = 0; - keycode = strtol(line, &keyname, 16); - if ((keycode != 0) && (*keyname == ' ')) + line_length = strlen(line); + + /* Completely empty line */ + if (strspn(line, " \t\n\r\f\v") == line_length) { - do - { - keyname++; - p = strchr(keyname, ' '); - if (p != NULL) - *p = 0; + continue; + } - keysym = XStringToKeysym(keyname); - if (keysym == NoSymbol) - error("Bad keysym %s in keymap %s\n", keyname, mapname); + /* Include */ + if (strncmp(line, "include ", 8) == 0) + { + if (!xkeymap_read(line + 8)) + return False; + continue; + } - keymap[keysym & KEYMAP_MASK] = keycode; - keyname = p; + /* map */ + if (strncmp(line, "map ", 4) == 0) + { + keylayout = strtol(line + 4, NULL, 16); + DEBUG_KBD(("Keylayout 0x%x\n", keylayout)); + continue; + } - } while (keyname != NULL); + /* compose */ + if (strncmp(line, "enable_compose", 15) == 0) + { + DEBUG_KBD(("Enabling compose handling\n")); + g_enable_compose = True; + continue; } - else if (strncmp(line, "include ", 8) == 0) + + /* Comment */ + if (line[0] == '#') { - if (!xkeymap_read(line+8)) - return False; + continue; + } + + /* Normal line */ + keyname = line; + p = strchr(line, ' '); + if (p == NULL) + { + error("Bad line %d in keymap %s\n", line_num, mapname); + continue; } - else if (strncmp(line, "map ", 4) == 0) + else { - keylayout = strtol(line+4, NULL, 16); + *p = 0; } - else if (line[0] != '#') + + /* scancode */ + p++; + scancode = strtol(p, &line_rest, 16); + + /* flags */ + /* FIXME: Should allow case-insensitive flag names. + Fix by using lex+yacc... */ + modifiers = 0; + if (strstr(line_rest, "altgr")) { - error("Malformed line in keymap %s\n", mapname); + MASK_ADD_BITS(modifiers, MapAltGrMask); + } + + if (strstr(line_rest, "shift")) + { + MASK_ADD_BITS(modifiers, MapLeftShiftMask); + } + + if (strstr(line_rest, "numlock")) + { + MASK_ADD_BITS(modifiers, MapNumLockMask); + } + + if (strstr(line_rest, "localstate")) + { + MASK_ADD_BITS(modifiers, MapLocalStateMask); + } + + if (strstr(line_rest, "inhibit")) + { + MASK_ADD_BITS(modifiers, MapInhibitMask); + } + + add_to_keymap(keyname, scancode, modifiers, mapname); + + if (strstr(line_rest, "addupper")) + { + /* Automatically add uppercase key, with same modifiers + plus shift */ + for (p = keyname; *p; p++) + *p = toupper((int) *p); + MASK_ADD_BITS(modifiers, MapLeftShiftMask); + add_to_keymap(keyname, scancode, modifiers, mapname); } } @@ -98,85 +215,455 @@ return True; } -void xkeymap_init(Display *display) + +/* Before connecting and creating UI */ +void +xkeymap_init(void) { unsigned int max_keycode; + char *mapname_ptr; - XDisplayKeycodes(display, &min_keycode, &max_keycode); + /* Make keymapname lowercase */ + mapname_ptr = keymapname; + while (*mapname_ptr) + { + *mapname_ptr = tolower((int) *mapname_ptr); + mapname_ptr++; + } if (strcmp(keymapname, "none")) - xkeymap_read(keymapname); + { + if (xkeymap_read(keymapname)) + keymap_loaded = True; + } + + XDisplayKeycodes(g_display, &min_keycode, (int *) &max_keycode); } -uint8 xkeymap_translate_key(KeySym keysym, unsigned int keycode) +static void +send_winkey(uint32 ev_time, BOOL pressed, BOOL leftkey) { - uint8 scancode; + uint8 winkey; + + if (leftkey) + winkey = SCANCODE_CHAR_LWIN; + else + winkey = SCANCODE_CHAR_RWIN; + + if (pressed) + { + if (g_use_rdp5) + { + rdp_send_scancode(ev_time, RDP_KEYPRESS, winkey); + } + else + { + /* RDP4 doesn't support winkey. Fake with Ctrl-Esc */ + rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_LCTRL); + rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_ESC); + } + } + else + { + /* key released */ + if (g_use_rdp5) + { + rdp_send_scancode(ev_time, RDP_KEYRELEASE, winkey); + } + else + { + rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_ESC); + rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LCTRL); + } + } +} + +/* Handles, for example, multi-scancode keypresses (which is not + possible via keymap-files) */ +BOOL +handle_special_keys(uint32 keysym, unsigned int state, uint32 ev_time, BOOL pressed) +{ + switch (keysym) + { + case XK_Return: + if ((get_key_state(state, XK_Alt_L) || get_key_state(state, XK_Alt_R)) + && (get_key_state(state, XK_Control_L) + || get_key_state(state, XK_Control_R))) + { + /* Ctrl-Alt-Enter: toggle full screen */ + if (pressed) + xwin_toggle_fullscreen(); + return True; + } + break; + + case XK_Break: + /* Send Break sequence E0 46 E0 C6 */ + if (pressed) + { + rdp_send_scancode(ev_time, RDP_KEYPRESS, + (SCANCODE_EXTENDED | 0x46)); + rdp_send_scancode(ev_time, RDP_KEYPRESS, + (SCANCODE_EXTENDED | 0xc6)); + } + /* No release sequence */ + return True; + + case XK_Pause: + /* According to MS Keyboard Scan Code + Specification, pressing Pause should result + in E1 1D 45 E1 9D C5. I'm not exactly sure + of how this is supposed to be sent via + RDP. The code below seems to work, but with + the side effect that Left Ctrl stays + down. Therefore, we release it when Pause + is released. */ + if (pressed) + { + rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0xe1, 0); + rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0x1d, 0); + rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0x45, 0); + rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0xe1, 0); + rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0x9d, 0); + rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0xc5, 0); + } + else + { + /* Release Left Ctrl */ + rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYRELEASE, + 0x1d, 0); + } + return True; + + case XK_Meta_L: /* Windows keys */ + case XK_Super_L: + case XK_Hyper_L: + send_winkey(ev_time, pressed, True); + return True; + + case XK_Meta_R: + case XK_Super_R: + case XK_Hyper_R: + send_winkey(ev_time, pressed, False); + return True; + + case XK_space: + /* Prevent access to the Windows system menu in single app mode */ + if (g_win_button_size + && (get_key_state(state, XK_Alt_L) || get_key_state(state, XK_Alt_R))) + return True; + + } + return False; +} + + +key_translation +xkeymap_translate_key(uint32 keysym, unsigned int keycode, unsigned int state) +{ + key_translation tr = { 0, 0 }; + + tr = keymap[keysym & KEYMAP_MASK]; + + if (tr.modifiers & MapInhibitMask) + { + DEBUG_KBD(("Inhibiting key\n")); + tr.scancode = 0; + return tr; + } + + if (tr.modifiers & MapLocalStateMask) + { + /* The modifiers to send for this key should be obtained + from the local state. Currently, only shift is implemented. */ + if (state & ShiftMask) + { + tr.modifiers = MapLeftShiftMask; + } + } + + if (tr.scancode != 0) + { + DEBUG_KBD(("Found key translation, scancode=0x%x, modifiers=0x%x\n", + tr.scancode, tr.modifiers)); + return tr; + } - scancode = keymap[keysym & KEYMAP_MASK]; - if (scancode != 0) - return scancode; + if (keymap_loaded) + warning("No translation for (keysym 0x%lx, %s)\n", keysym, get_ksname(keysym)); /* not in keymap, try to interpret the raw scancode */ + if (((int) keycode >= min_keycode) && (keycode <= 0x60)) + { + tr.scancode = keycode - min_keycode; - if ((keycode >= min_keycode) && (keycode <= 0x60)) - return (uint8)(keycode - min_keycode); + /* The modifiers to send for this key should be + obtained from the local state. Currently, only + shift is implemented. */ + if (state & ShiftMask) + { + tr.modifiers = MapLeftShiftMask; + } - switch (keycode) + DEBUG_KBD(("Sending guessed scancode 0x%x\n", tr.scancode)); + } + else { - case 0x61: /* home */ - return 0x47 | 0x80; - case 0x62: /* up arrow */ - return 0x48 | 0x80; - case 0x63: /* page up */ - return 0x49 | 0x80; - case 0x64: /* left arrow */ - return 0x4b | 0x80; - case 0x66: /* right arrow */ - return 0x4d | 0x80; - case 0x67: /* end */ - return 0x4f | 0x80; - case 0x68: /* down arrow */ - return 0x50 | 0x80; - case 0x69: /* page down */ - return 0x51 | 0x80; - case 0x6a: /* insert */ - return 0x52 | 0x80; - case 0x6b: /* delete */ - return 0x53 | 0x80; - case 0x6c: /* keypad enter */ - return 0x1c | 0x80; - case 0x6d: /* right ctrl */ - return 0x1d | 0x80; - case 0x6f: /* ctrl - print screen */ - return 0x37 | 0x80; - case 0x70: /* keypad '/' */ - return 0x35 | 0x80; - case 0x71: /* right alt */ - return 0x38 | 0x80; - case 0x72: /* ctrl break */ - return 0x46 | 0x80; - case 0x73: /* left window key */ - return 0x5b | 0x80; - case 0x74: /* right window key */ - return 0x5c | 0x80; - case 0x75: /* menu key */ - return 0x5d | 0x80; + DEBUG_KBD(("No good guess for keycode 0x%x found\n", keycode)); } - return 0; + return tr; } -uint16 xkeymap_translate_button(unsigned int button) +uint16 +xkeymap_translate_button(unsigned int button) { switch (button) { - case Button1: /* left */ + case Button1: /* left */ return MOUSE_FLAG_BUTTON1; - case Button2: /* middle */ + case Button2: /* middle */ return MOUSE_FLAG_BUTTON3; - case Button3: /* right */ + case Button3: /* right */ return MOUSE_FLAG_BUTTON2; + case Button4: /* wheel up */ + return MOUSE_FLAG_BUTTON4; + case Button5: /* wheel down */ + return MOUSE_FLAG_BUTTON5; } return 0; } + +char * +get_ksname(uint32 keysym) +{ + char *ksname = NULL; + + if (keysym == NoSymbol) + ksname = "NoSymbol"; + else if (!(ksname = XKeysymToString(keysym))) + ksname = "(no name)"; + + return ksname; +} + +void +save_remote_modifiers() +{ + saved_remote_modifier_state = remote_modifier_state; +} + +void +restore_remote_modifiers(uint32 ev_time) +{ + key_translation dummy; + + dummy.scancode = 0; + dummy.modifiers = saved_remote_modifier_state; + ensure_remote_modifiers(ev_time, dummy); +} + +void +ensure_remote_modifiers(uint32 ev_time, key_translation tr) +{ + /* If this key is a modifier, do nothing */ + switch (tr.scancode) + { + case SCANCODE_CHAR_LSHIFT: + case SCANCODE_CHAR_RSHIFT: + case SCANCODE_CHAR_LCTRL: + case SCANCODE_CHAR_RCTRL: + case SCANCODE_CHAR_LALT: + case SCANCODE_CHAR_RALT: + case SCANCODE_CHAR_LWIN: + case SCANCODE_CHAR_RWIN: + case SCANCODE_CHAR_NUMLOCK: + return; + default: + break; + } + + /* NumLock */ + if (MASK_HAS_BITS(tr.modifiers, MapNumLockMask) + != MASK_HAS_BITS(remote_modifier_state, MapNumLockMask)) + { + /* The remote modifier state is not correct */ + uint16 new_remote_state; + + if (MASK_HAS_BITS(tr.modifiers, MapNumLockMask)) + { + DEBUG_KBD(("Remote NumLock state is incorrect, activating NumLock.\n")); + new_remote_state = KBD_FLAG_NUMLOCK; + remote_modifier_state = MapNumLockMask; + } + else + { + DEBUG_KBD(("Remote NumLock state is incorrect, deactivating NumLock.\n")); + new_remote_state = 0; + remote_modifier_state = 0; + } + + rdp_send_input(0, RDP_INPUT_SYNCHRONIZE, 0, new_remote_state, 0); + } + + /* Shift. Left shift and right shift are treated as equal; either is fine. */ + if (MASK_HAS_BITS(tr.modifiers, MapShiftMask) + != MASK_HAS_BITS(remote_modifier_state, MapShiftMask)) + { + /* The remote modifier state is not correct */ + if (MASK_HAS_BITS(tr.modifiers, MapLeftShiftMask)) + { + /* Needs left shift. Send down. */ + rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_LSHIFT); + } + else if (MASK_HAS_BITS(tr.modifiers, MapRightShiftMask)) + { + /* Needs right shift. Send down. */ + rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_RSHIFT); + } + else + { + /* Should not use this modifier. Send up for shift currently pressed. */ + if (MASK_HAS_BITS(remote_modifier_state, MapLeftShiftMask)) + /* Left shift is down */ + rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LSHIFT); + else + /* Right shift is down */ + rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RSHIFT); + } + } + + /* AltGr */ + if (MASK_HAS_BITS(tr.modifiers, MapAltGrMask) + != MASK_HAS_BITS(remote_modifier_state, MapAltGrMask)) + { + /* The remote modifier state is not correct */ + if (MASK_HAS_BITS(tr.modifiers, MapAltGrMask)) + { + /* Needs this modifier. Send down. */ + rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_RALT); + } + else + { + /* Should not use this modifier. Send up. */ + rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RALT); + } + } + + +} + + +void +reset_modifier_keys(unsigned int state) +{ + /* reset keys */ + uint32 ev_time; + ev_time = time(NULL); + + if (MASK_HAS_BITS(remote_modifier_state, MapLeftShiftMask) + && !get_key_state(state, XK_Shift_L)) + rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LSHIFT); + + if (MASK_HAS_BITS(remote_modifier_state, MapRightShiftMask) + && !get_key_state(state, XK_Shift_R)) + rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RSHIFT); + + if (MASK_HAS_BITS(remote_modifier_state, MapLeftCtrlMask) + && !get_key_state(state, XK_Control_L)) + rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LCTRL); + + if (MASK_HAS_BITS(remote_modifier_state, MapRightCtrlMask) + && !get_key_state(state, XK_Control_R)) + rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RCTRL); + + if (MASK_HAS_BITS(remote_modifier_state, MapLeftAltMask) && !get_key_state(state, XK_Alt_L)) + rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LALT); + + if (MASK_HAS_BITS(remote_modifier_state, MapRightAltMask) && + !get_key_state(state, XK_Alt_R) && !get_key_state(state, XK_Mode_switch)) + rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RALT); +} + + +static void +update_modifier_state(uint8 scancode, BOOL pressed) +{ +#ifdef WITH_DEBUG_KBD + uint16 old_modifier_state; + + old_modifier_state = remote_modifier_state; +#endif + + switch (scancode) + { + case SCANCODE_CHAR_LSHIFT: + MASK_CHANGE_BIT(remote_modifier_state, MapLeftShiftMask, pressed); + break; + case SCANCODE_CHAR_RSHIFT: + MASK_CHANGE_BIT(remote_modifier_state, MapRightShiftMask, pressed); + break; + case SCANCODE_CHAR_LCTRL: + MASK_CHANGE_BIT(remote_modifier_state, MapLeftCtrlMask, pressed); + break; + case SCANCODE_CHAR_RCTRL: + MASK_CHANGE_BIT(remote_modifier_state, MapRightCtrlMask, pressed); + break; + case SCANCODE_CHAR_LALT: + MASK_CHANGE_BIT(remote_modifier_state, MapLeftAltMask, pressed); + break; + case SCANCODE_CHAR_RALT: + MASK_CHANGE_BIT(remote_modifier_state, MapRightAltMask, pressed); + break; + case SCANCODE_CHAR_LWIN: + MASK_CHANGE_BIT(remote_modifier_state, MapLeftWinMask, pressed); + break; + case SCANCODE_CHAR_RWIN: + MASK_CHANGE_BIT(remote_modifier_state, MapRightWinMask, pressed); + break; + case SCANCODE_CHAR_NUMLOCK: + /* KeyReleases for NumLocks are sent immediately. Toggle the + modifier state only on Keypress */ + if (pressed) + { + BOOL newNumLockState; + newNumLockState = + (MASK_HAS_BITS + (remote_modifier_state, MapNumLockMask) == False); + MASK_CHANGE_BIT(remote_modifier_state, + MapNumLockMask, newNumLockState); + } + break; + } + +#ifdef WITH_DEBUG_KBD + if (old_modifier_state != remote_modifier_state) + { + DEBUG_KBD(("Before updating modifier_state:0x%x, pressed=0x%x\n", + old_modifier_state, pressed)); + DEBUG_KBD(("After updating modifier_state:0x%x\n", remote_modifier_state)); + } +#endif + +} + +/* Send keyboard input */ +void +rdp_send_scancode(uint32 time, uint16 flags, uint8 scancode) +{ + update_modifier_state(scancode, !(flags & RDP_KEYRELEASE)); + + if (scancode & SCANCODE_EXTENDED) + { + DEBUG_KBD(("Sending extended scancode=0x%x, flags=0x%x\n", + scancode & ~SCANCODE_EXTENDED, flags)); + rdp_send_input(time, RDP_INPUT_SCANCODE, flags | KBD_FLAG_EXT, + scancode & ~SCANCODE_EXTENDED, 0); + } + else + { + DEBUG_KBD(("Sending scancode=0x%x, flags=0x%x\n", scancode, flags)); + rdp_send_input(time, RDP_INPUT_SCANCODE, flags, scancode, 0); + } +}