--- sourceforge.net/trunk/rdesktop/xkeymap.c 2002/09/24 07:33:17 190 +++ sourceforge.net/trunk/rdesktop/xkeymap.c 2003/11/03 13:33:35 543 @@ -1,7 +1,9 @@ /* rdesktop: A Remote Desktop Protocol client. User interface services - X keyboard mapping - Copyright (C) Matthew Chapman 1999-2001 + + Copyright (C) Matthew Chapman 1999-2002 + Copyright (C) Peter Astrand 2003 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,35 +14,44 @@ 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 -#define XK_MISCELLANY -#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 *display; +extern Display *g_display; +extern Window g_wnd; extern char keymapname[16]; extern int keylayout; -extern BOOL enable_compose; +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(uint16 modifiers, BOOL pressed); +static void update_modifier_state(uint8 scancode, BOOL pressed); static void add_to_keymap(char *keyname, uint8 scancode, uint16 modifiers, char *mapname) @@ -50,7 +61,7 @@ keysym = XStringToKeysym(keyname); if (keysym == NoSymbol) { - error("Bad keysym %s in keymap %s\n", keyname, mapname); + DEBUG_KBD(("Bad keysym \"%s\" in keymap %s (ignoring)\n", keyname, mapname)); return; } @@ -68,7 +79,8 @@ xkeymap_read(char *mapname) { FILE *fp; - char line[KEYMAP_MAX_LINE_LENGTH], 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; @@ -83,8 +95,16 @@ 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 */ @@ -125,7 +145,7 @@ if (strncmp(line, "enable_compose", 15) == 0) { DEBUG_KBD(("Enabling compose handling\n")); - enable_compose = True; + g_enable_compose = True; continue; } @@ -166,11 +186,6 @@ 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); @@ -188,7 +203,7 @@ /* Automatically add uppercase key, with same modifiers plus shift */ for (p = keyname; *p; p++) - *p = toupper(*p); + *p = toupper((int) *p); MASK_ADD_BITS(modifiers, MapLeftShiftMask); add_to_keymap(keyname, scancode, modifiers, mapname); } @@ -204,46 +219,138 @@ xkeymap_init(void) { unsigned int max_keycode; + char *mapname_ptr; + + /* 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); +} + +static void +send_winkey(uint32 ev_time, BOOL pressed, BOOL leftkey) +{ + uint8 winkey; + + if (leftkey) + winkey = SCANCODE_CHAR_LWIN; + else + winkey = SCANCODE_CHAR_RWIN; - XDisplayKeycodes(display, &min_keycode, (int *) &max_keycode); + 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, uint32 ev_time, BOOL pressed) +handle_special_keys(uint32 keysym, unsigned int state, uint32 ev_time, BOOL pressed) { switch (keysym) { - case XK_Break: /* toggle full screen */ - if (pressed && (get_key_state(XK_Alt_L) || get_key_state(XK_Alt_R))) + 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))) { - xwin_toggle_fullscreen(); + /* Ctrl-Alt-Enter: toggle full screen */ + if (pressed) + xwin_toggle_fullscreen(); return True; } break; - case XK_Meta_L: /* Windows keys */ - case XK_Super_L: - case XK_Hyper_L: - case XK_Meta_R: - case XK_Super_R: - case XK_Hyper_R: + case XK_Break: + /* Send Break sequence E0 46 E0 C6 */ if (pressed) { - rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_LCTRL); - rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_ESC); + 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 { - rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_ESC); - rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LCTRL); + /* Release Left Ctrl */ + rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYRELEASE, + 0x1d, 0); } return True; - break; + + 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; } @@ -276,14 +383,15 @@ if (tr.scancode != 0) { DEBUG_KBD(("Found key translation, scancode=0x%x, modifiers=0x%x\n", - tr.scancode, tr.modifiers)); + tr.scancode, tr.modifiers)); return tr; } - DEBUG_KBD(("No translation for (keysym 0x%lx, %s)\n", keysym, get_ksname(keysym))); + 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 ((keycode >= min_keycode) && (keycode <= 0x60)) + if (((int) keycode >= min_keycode) && (keycode <= 0x60)) { tr.scancode = keycode - min_keycode; @@ -338,12 +446,10 @@ return ksname; } - -void -ensure_remote_modifiers(uint32 ev_time, key_translation tr) +static BOOL +is_modifier(uint8 scancode) { - /* If this key is a modifier, do nothing */ - switch (tr.scancode) + switch (scancode) { case SCANCODE_CHAR_LSHIFT: case SCANCODE_CHAR_RSHIFT: @@ -354,10 +460,41 @@ case SCANCODE_CHAR_LWIN: case SCANCODE_CHAR_RWIN: case SCANCODE_CHAR_NUMLOCK: - return; + return True; default: break; } + return False; +} + +void +save_remote_modifiers(uint8 scancode) +{ + if (is_modifier(scancode)) + return; + + saved_remote_modifier_state = remote_modifier_state; +} + +void +restore_remote_modifiers(uint32 ev_time, uint8 scancode) +{ + key_translation dummy; + + if (is_modifier(scancode)) + return; + + 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 */ + if (is_modifier(tr.scancode)) + return; /* Shift. Left shift and right shift are treated as equal; either is fine. */ if (MASK_HAS_BITS(tr.modifiers, MapShiftMask) @@ -403,59 +540,72 @@ } } - /* 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 = 0; - if (MASK_HAS_BITS(tr.modifiers, MapNumLockMask)) - { - DEBUG_KBD(("Remote NumLock state is incorrect, activating NumLock.\n")); - new_remote_state |= KBD_FLAG_NUMLOCK; - } - else - { - DEBUG_KBD(("Remote NumLock state is incorrect, deactivating NumLock.\n")); - } +} - rdp_send_input(0, RDP_INPUT_SYNCHRONIZE, 0, new_remote_state, 0); - update_modifier_state(SCANCODE_CHAR_NUMLOCK, True); - } + +unsigned int +read_keyboard_state() +{ + unsigned int state; + Window wdummy; + int dummy; + + XQueryPointer(g_display, g_wnd, &wdummy, &wdummy, &dummy, &dummy, &dummy, &dummy, &state); + return state; +} + + +uint16 +ui_get_numlock_state(unsigned int state) +{ + uint16 numlock_state = 0; + + if (get_key_state(state, XK_Num_Lock)) + numlock_state = KBD_FLAG_NUMLOCK; + + return numlock_state; } void reset_modifier_keys() { + unsigned int state = read_keyboard_state(); + /* reset keys */ uint32 ev_time; ev_time = time(NULL); - if (MASK_HAS_BITS(remote_modifier_state, MapLeftShiftMask) && !get_key_state(XK_Shift_L)) + 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(XK_Shift_R)) + 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(XK_Control_L)) + 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(XK_Control_R)) + 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(XK_Alt_L)) + 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(XK_Alt_R) && !get_key_state(XK_Mode_switch)) + !get_key_state(state, XK_Alt_R) && !get_key_state(state, XK_Mode_switch)) rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RALT); + + rdp_send_input(ev_time, RDP_INPUT_SYNCHRONIZE, 0, ui_get_numlock_state(state), 0); } static void -update_modifier_state(uint16 modifiers, BOOL pressed) +update_modifier_state(uint8 scancode, BOOL pressed) { #ifdef WITH_DEBUG_KBD uint16 old_modifier_state; @@ -463,7 +613,7 @@ old_modifier_state = remote_modifier_state; #endif - switch (modifiers) + switch (scancode) { case SCANCODE_CHAR_LSHIFT: MASK_CHANGE_BIT(remote_modifier_state, MapLeftShiftMask, pressed); @@ -489,19 +639,6 @@ 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 @@ -517,7 +654,7 @@ /* Send keyboard input */ void -rdp_send_scancode(uint32 time, uint16 flags, uint16 scancode) +rdp_send_scancode(uint32 time, uint16 flags, uint8 scancode) { update_modifier_state(scancode, !(flags & RDP_KEYRELEASE));