📄 xkeymap.c
字号:
/* -*- c-basic-offset: 8 -*- rdesktop: A Remote Desktop Protocol client. User interface services - X keyboard mapping Copyright (C) Matthew Chapman 1999-2005 Copyright (C) Peter Astrand <peter@cendio.se> 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 the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, 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 <X11/Xlib.h>#include <X11/keysym.h>#endif#include <ctype.h>#include <limits.h>#include <time.h>#include <string.h>#include "rdesktop.h"#include "scancodes.h"#define KEYMAP_SIZE 0xffff+1#define KEYMAP_MASK 0xffff#define KEYMAP_MAX_LINE_LENGTH 80extern Display *g_display;extern Window g_wnd;extern char g_keymapname[16];extern unsigned int g_keylayout;extern int g_keyboard_type;extern int g_keyboard_subtype;extern int g_keyboard_functionkeys;extern int g_win_button_size;extern BOOL g_enable_compose;extern BOOL g_use_rdp5;extern BOOL g_numlock_sync;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);/* Free key_translation structure, including linked list */static voidfree_key_translation(key_translation * ptr){ key_translation *next; while (ptr) { next = ptr->next; xfree(ptr); ptr = next; }}static voidadd_to_keymap(char *keyname, uint8 scancode, uint16 modifiers, char *mapname){ KeySym keysym; key_translation *tr; 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)); tr = (key_translation *) xmalloc(sizeof(key_translation)); memset(tr, 0, sizeof(key_translation)); tr->scancode = scancode; tr->modifiers = modifiers; free_key_translation(keymap[keysym & KEYMAP_MASK]); keymap[keysym & KEYMAP_MASK] = tr; return;}static voidadd_sequence(char *rest, char *mapname){ KeySym keysym; key_translation *tr, **prev_next; size_t chars; char keyname[KEYMAP_MAX_LINE_LENGTH]; /* Skip over whitespace after the sequence keyword */ chars = strspn(rest, " \t"); rest += chars; /* Fetch the keysym name */ chars = strcspn(rest, " \t\0"); STRNCPY(keyname, rest, chars + 1); rest += chars; keysym = XStringToKeysym(keyname); if (keysym == NoSymbol) { DEBUG_KBD(("Bad keysym \"%s\" in keymap %s (ignoring line)\n", keyname, mapname)); return; } DEBUG_KBD(("Adding sequence for keysym (0x%lx, %s) -> ", keysym, keyname)); free_key_translation(keymap[keysym & KEYMAP_MASK]); prev_next = &keymap[keysym & KEYMAP_MASK]; while (*rest) { /* Skip whitespace */ chars = strspn(rest, " \t"); rest += chars; /* Fetch the keysym name */ chars = strcspn(rest, " \t\0"); STRNCPY(keyname, rest, chars + 1); rest += chars; keysym = XStringToKeysym(keyname); if (keysym == NoSymbol) { DEBUG_KBD(("Bad keysym \"%s\" in keymap %s (ignoring line)\n", keyname, mapname)); return; } /* Allocate space for key_translation structure */ tr = (key_translation *) xmalloc(sizeof(key_translation)); memset(tr, 0, sizeof(key_translation)); *prev_next = tr; prev_next = &tr->next; tr->seq_keysym = keysym; DEBUG_KBD(("0x%x, ", (unsigned int) keysym)); } DEBUG_KBD(("\n"));}BOOLxkeymap_from_locale(const char *locale){ char *str, *ptr; FILE *fp; /* Create a working copy */ str = xstrdup(locale); /* Truncate at dot and at */ ptr = strrchr(str, '.'); if (ptr) *ptr = '\0'; ptr = strrchr(str, '@'); if (ptr) *ptr = '\0'; /* Replace _ with - */ ptr = strrchr(str, '_'); if (ptr) *ptr = '-'; /* Convert to lowercase */ ptr = str; while (*ptr) { *ptr = tolower((int) *ptr); ptr++; } /* Try to open this keymap (da-dk) */ fp = xkeymap_open(str); if (fp == NULL) { /* Truncate at dash */ ptr = strrchr(str, '-'); if (ptr) *ptr = '\0'; /* Try the short name (da) */ fp = xkeymap_open(str); } if (fp) { fclose(fp); STRNCPY(g_keymapname, str, sizeof(g_keymapname)); xfree(str); return True; } xfree(str); return False;}/* Joins two path components. The result should be freed with xfree(). */static char *pathjoin(const char *a, const char *b){ char *result; result = xmalloc(PATH_MAX * 2 + 1); if (b[0] == '/') { strncpy(result, b, PATH_MAX); } else { strncpy(result, a, PATH_MAX); strcat(result, "/"); strncat(result, b, PATH_MAX); } return result;}/* Try to open a keymap with fopen() */FILE *xkeymap_open(const char *filename){ char *path1, *path2; char *home; FILE *fp; /* Try ~/.rdesktop/keymaps */ home = getenv("HOME"); if (home) { path1 = pathjoin(home, ".rdesktop/keymaps"); path2 = pathjoin(path1, filename); xfree(path1); fp = fopen(path2, "r"); xfree(path2); if (fp) return fp; } /* Try KEYMAP_PATH */ path1 = pathjoin(KEYMAP_PATH, filename); fp = fopen(path1, "r"); xfree(path1); if (fp) return fp; /* Try current directory, in case we are running from the source tree */ path1 = pathjoin("keymaps", filename); fp = fopen(path1, "r"); xfree(path1); if (fp) return fp; return NULL;}static BOOLxkeymap_read(char *mapname){ FILE *fp; char line[KEYMAP_MAX_LINE_LENGTH]; unsigned int line_num = 0; unsigned int line_length = 0; char *keyname, *p; char *line_rest; uint8 scancode; uint16 modifiers; fp = xkeymap_open(mapname); if (fp == NULL) { error("Failed to open keymap %s\n", mapname); 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; line_length = strlen(line); /* Completely empty line */ if (strspn(line, " \t\n\r\f\v") == line_length) { continue; } /* Include */ if (str_startswith(line, "include ")) { if (!xkeymap_read(line + sizeof("include ") - 1)) return False; continue; } /* map */ if (str_startswith(line, "map ")) { g_keylayout = strtoul(line + sizeof("map ") - 1, NULL, 16); DEBUG_KBD(("Keylayout 0x%x\n", g_keylayout)); continue; } /* compose */ if (str_startswith(line, "enable_compose")) { DEBUG_KBD(("Enabling compose handling\n")); g_enable_compose = True; continue; } /* sequence */ if (str_startswith(line, "sequence")) { add_sequence(line + sizeof("sequence") - 1, mapname); continue; } /* keyboard_type */ if (str_startswith(line, "keyboard_type ")) { g_keyboard_type = strtol(line + sizeof("keyboard_type ") - 1, NULL, 16); DEBUG_KBD(("keyboard_type 0x%x\n", g_keyboard_type)); continue; } /* keyboard_subtype */ if (str_startswith(line, "keyboard_subtype ")) { g_keyboard_subtype = strtol(line + sizeof("keyboard_subtype ") - 1, NULL, 16); DEBUG_KBD(("keyboard_subtype 0x%x\n", g_keyboard_subtype)); continue; } /* keyboard_functionkeys */ if (str_startswith(line, "keyboard_functionkeys ")) { g_keyboard_functionkeys = strtol(line + sizeof("keyboard_functionkeys ") - 1, NULL, 16); DEBUG_KBD(("keyboard_functionkeys 0x%x\n", g_keyboard_functionkeys)); continue; } /* Comment */ if (line[0] == '#') { continue; } /* Normal line */ keyname = line; p = strchr(line, ' '); if (p == NULL) { error("Bad line %d in keymap %s\n", line_num, mapname); continue; } else { *p = 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")) { 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); } } fclose(fp); return True;}/* Before connecting and creating UI */voidxkeymap_init(void){ unsigned int max_keycode; if (strcmp(g_keymapname, "none")) { if (xkeymap_read(g_keymapname)) keymap_loaded = True; } XDisplayKeycodes(g_display, &min_keycode, (int *) &max_keycode);}static voidsend_winkey(uint32 ev_time, BOOL pressed, BOOL leftkey){ 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); } }}static voidreset_winkey(uint32 ev_time){ if (g_use_rdp5) { /* For some reason, it seems to suffice to release *either* the left or right winkey. */ rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LWIN); }}/* Handle special key combinations */BOOLhandle_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();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -