From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LCbvC-0004SR-5x for qemu-devel@nongnu.org; Tue, 16 Dec 2008 10:33:18 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1LCbv8-0004QA-Qh for qemu-devel@nongnu.org; Tue, 16 Dec 2008 10:33:16 -0500 Received: from [199.232.76.173] (port=35977 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LCbv8-0004Pq-Aq for qemu-devel@nongnu.org; Tue, 16 Dec 2008 10:33:14 -0500 Received: from rcsinet12.oracle.com ([148.87.113.124]:18640 helo=rgminet12.oracle.com) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1LCbv7-0004h2-1g for qemu-devel@nongnu.org; Tue, 16 Dec 2008 10:33:13 -0500 Received: from rgminet15.oracle.com (rcsinet15.oracle.com [148.87.113.117]) by rgminet12.oracle.com (Switch-3.3.1/Switch-3.3.1) with ESMTP id mBGFWp2m017485 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Tue, 16 Dec 2008 15:32:53 GMT Received: from acsmt703.oracle.com (acsmt703.oracle.com [141.146.40.81]) by rgminet15.oracle.com (Switch-3.3.1/Switch-3.3.1) with ESMTP id mBGFXCTK018807 for ; Tue, 16 Dec 2008 15:33:14 GMT Message-ID: <4947CA2B.5030401@oracle.com> Date: Tue, 16 Dec 2008 15:32:59 +0000 From: John Haxby MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------010104010300090209080402" Subject: [Qemu-devel] [PATCH 0 of 3] Fix keymap handling for vnc console Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu developers This is a multi-part message in MIME format. --------------010104010300090209080402 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: quoted-printable X-MIME-Autoconverted: from 8bit to quoted-printable by rgminet12.oracle.com id mBGFWp2m017485 This is a version of a patch that I originally posted to xen-devel. =20 The xen vnc console and the qemu vnc console have diverged a little, but=20 the problems and subsequent fixes apply equally to both. There are presently a few problems dogging keymap handling for the vnc=20 console: * Some keystrokes that generate a symbol locally are ignored. * Some keystrokes in some keymaps generate the wrong scancode and=20 thence the wrong character * The capslock key is handled inconsistently The first problem is typically caused by missing entries in the keymap. =20 For example, the "Q" key in many European keyboards generates "@" and=20 "=CE=A9" in addition to the normal "q" and "Q" when used with the AltGr k= ey. =20 However, the keysym defintions in vnc_keysym.h are incomplete and so,=20 for example, "=CE=A9" will never be recognised even if it is in the keyma= p=20 definition (which it often isn't). The second problem is a little more subtle. The code presently assumes=20 that there is a many-to-one mapping from keysyms to scancodes. So, for=20 example, "q", "Q", "@" and "=CE=A9" all generate the scancode 0x10. =20 However, on a UK keyboard the "@" keysym can arise from typing either=20 AltGr-Q (scancode 0x10) or Shift-apostrophe (scancode 0x28). For some=20 keymaps this is particularly damaging -- for example in=20 http://article.gmane.org/gmane.comp.emulators.qemu/32241 the "1", "4"=20 and "6" keys should give "&", "'" (apostrophe) and "=C2=A7" respectively = but=20 actually give "k", "b" and "s". The final problem is related to the numlock handling problem that was=20 fixed fairly recently. For that particular problem, the various keysyms=20 that are generated when the only when the numlock key is pressed or=20 released result in an implicit press or release of the numlock key in=20 order to change the state of the key. In the case of the capslock key,=20 however, it isn't, in general, possible to distinguish between a "A"=20 generated from a capslock and one generated with a shift sequence. The following patches are designed to fix these problems. The first uses five keysym to scancode keymaps for plain, shifted,=20 AltGr'd, shift-AltGr'd and numlocked keys to handle the many-to-many=20 mapping from keysyms to scancodes. The current state of the shift and=20 altgr keys that the guest is aware of is tracked and a keysym looked up=20 in the corresponding map to find the right scancode. If the scancode is=20 not defined in that keymap then the other keymaps are checked and=20 implicit keypress or keyrelease events for shift and AltGr sent to=20 produce the right scancode and modifier set to generate the correct=20 keysym in the guest's application. The first lookup means that the=20 right scancode will be generated for a keysym in all but the most=20 pathological of cases; the second lookup means that, for example, a=20 client UK keyboard will work correctly against a guest Belgian keyboard=20 because the implicit shift and AltGr keypresses and releases will do the=20 necessary shuffling behind the scenes. The second patch adds the missing keysym names to vnc_keysym.h. Not=20 all the X keysym names are in here, but there are enough to satisfy all=20 the keysyms used by the keymaps presently defined. The third and final patch corrects most of the keymap definitions. The=20 ones I haven't changed are the ones I am unsure of -- the names do not=20 always correspond that well to the xkb keymap names and for the=20 non-european keyboards I have very little confidence that what I am=20 typing is what is intended! There is a remaining problem with the numlock key handling which is=20 beyond the scope of these patches: if software running in the guest=20 toggles the numlock key (for example, the X server) then the behaviour=20 of the numlock key may become inverted. Finally, the two attached programs -- evkey.c and evconv.c -- can be=20 used to help generate and test keymap definitions. The former, evkey,=20 is Linux specific and matches up keysyms received through a small X=20 window with the scancodes retrieved from a PS/2 keyboard (and it must be=20 a PS/2 keyboard, USB keyboards generate different scancodes). The=20 latter, evconv, uses the qemu keymap to find the scancode and can be=20 used to check that a keymap definition is going to give the expected=20 scancodes for a given (X) keyboard mapping. Hopefully the comments at=20 the head of each file will describe the intention sufficiently well. jch --------------010104010300090209080402 Content-Type: text/x-csrc; name="evkey.c" Content-Disposition: inline; filename="evkey.c" Content-Transfer-Encoding: 7bit /* * Generate Xen keymaps * * Compile: * cc -o evkey evkey.c -lX11 * * Usage: * evkey /usr/share/xen/qemu/keymaps/ * * The prototype keymap typically has "include common" and not much * else. keysym to scancode lines not already in the protoype (or * anything it includes) will be emitted on the corresponding keypress * events. The keypress MUST come from a PS/2 keyboard so the correct * scancode can be found. * * A complete and correct keymap that matches the current xkb keymap * will produce no output at all for any keypress. * */ #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_SCANCODE 255 typedef struct { int plain[MAX_SCANCODE]; int shift[MAX_SCANCODE]; int altgr[MAX_SCANCODE]; int shift_altgr[MAX_SCANCODE]; int numlock[MAX_SCANCODE]; } keysym_map_t; const char *keyboard_name = "AT Translated Set 2 keyboard"; static Display *open_display (char *display_name, int argc, char **argv); static void get_keyboard_info (Display *dpy, int *numlock_mask, int *altgr_mask); static int open_keyboard(void); static int get_lastkeypress (int fd); static void load_keymap (char *path, keysym_map_t *map); static void load_keymap1 (char *file, keysym_map_t *map); static char *check_scancode (keysym_map_t *map, KeySym ks, int scancode, bool shift, bool altgr, bool numlock); static void handler (int sig); static volatile bool done = false; int main (int argc, char **argv) { Display *dpy; int numlock_mask = 0; int altgr_mask = 0; int evfd; keysym_map_t map; dpy = open_display(NULL, argc, argv); evfd = open_keyboard(); get_keyboard_info(dpy, &numlock_mask, &altgr_mask); memset(&map, 0, sizeof(map)); if (argv[1]) load_keymap(argv[1], &map); sigaction(SIGINT, &((struct sigaction) { .sa_handler = handler }), NULL); while (!done) { XEvent event; XNextEvent(dpy, &event); if (event.type == KeyPress) { XKeyEvent *ev = (XKeyEvent *) &event; KeySym ks = NoSymbol; char *keyname; int scancode; char *msg = NULL; char str[256]; XLookupString(ev, str, sizeof(str)-1, &ks, NULL); keyname = XKeysymToString(ks); if (!keyname) continue; if (ev->state & LockMask) fprintf(stderr, "warning: capslock pressed\n"); scancode = get_lastkeypress(evfd); if (IsModifierKey(ks)) msg = check_scancode(&map, ks, scancode, false, false, false); else if (ks != NoSymbol) msg = check_scancode(&map, ks, scancode, (ev->state & ShiftMask), (ev->state & altgr_mask), (ev->state & numlock_mask)); if (msg) { if (ks == NoSymbol) printf("%sNoSymbol\n", msg); else if (IsModifierKey(ks)) printf("%s%s 0x%02x\n", msg, keyname, scancode); else if (IsKeypadKey(ks)) printf("%s%s 0x%02x%s\n", msg, keyname, scancode, (ev->state & numlock_mask) ? " numlock" : ""); else printf("%s%s 0x%02x%s%s\n", msg, keyname, scancode, (ev->state & ShiftMask) ? " shift" : "", (ev->state & altgr_mask) ? " altgr" : ""); fflush(stdout); } } } exit(0); } static Display *open_display (char *display_name, int argc, char **argv) { Display *dpy; XSizeHints hints; XSetWindowAttributes attr; Window w; if (!(dpy = XOpenDisplay(display_name))) { fprintf(stderr, "Can't open display\n"); exit(1); } hints.width = hints.min_width = 100; hints.height = hints.min_height = 100; hints.flags = PMinSize; hints.x = hints.y = 0; memset(&attr, 0, sizeof(attr)); attr.background_pixel = WhitePixel(dpy, DefaultScreen(dpy)); attr.border_pixel = BlackPixel(dpy, DefaultScreen(dpy)); attr.event_mask = KeyPressMask | KeyReleaseMask; w = XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, 100, 100, 0, 0, InputOutput, CopyFromParent, CWBackPixel | CWBorderPixel | CWEventMask, &attr); XSetStandardProperties(dpy, w, "evkey", NULL, 0, argv, argc, &hints); XMapWindow(dpy, w); return dpy; } static void get_keyboard_info (Display *dpy, int *numlock_mask, int *altgr_mask) { int min_keycode, max_keycode, keysyms_per_keycode; XModifierKeymap *map; int i, j; XDisplayKeycodes(dpy, &min_keycode, &max_keycode); XGetKeyboardMapping(dpy, min_keycode, max_keycode - min_keycode + 1, &keysyms_per_keycode); map = XGetModifierMapping(dpy); for (i = 0; i < 8*map->max_keypermod; i += map->max_keypermod) { for (j = 0; j < map->max_keypermod; j++) { if (map->modifiermap[i+j]) { int index = 0; KeySym ks = NoSymbol; while (ks == NoSymbol && index < keysyms_per_keycode) ks = XKeycodeToKeysym(dpy, map->modifiermap[i+j], index++); if (*numlock_mask == 0 && ks == XK_Num_Lock) *numlock_mask = 1 << (i / map->max_keypermod); else if (*altgr_mask == 0 && ks == XK_ISO_Level3_Shift) *altgr_mask = 1 << (i / map->max_keypermod); } } } } static int selector (const struct dirent *dirent) { int n; return (sscanf(dirent->d_name, "event%d", &n) == 1); } static int cmp (const void *d1, const void *d2) { int n1 = 0, n2 = 0; sscanf((*((struct dirent **) d1))->d_name, "event%d", &n1); sscanf((*((struct dirent **) d2))->d_name, "event%d", &n2); return n1 - n2; } static int open_keyboard () { struct dirent **namelist; int fd = -1, i, n; if ((n = scandir("/dev/input", &namelist, selector, cmp)) < 0) { perror("/dev/input"); exit(1); } for (i = 0; i < n; i++) { char file[sizeof("/dev/input/") + strlen(namelist[i]->d_name)]; int version; char name[256]; sprintf(file, "/dev/input/%s", namelist[i]->d_name); if ((fd = open(file, O_RDONLY)) < 0) { perror(file); exit(1); } if (ioctl(fd, EVIOCGVERSION, &version) >= 0 && ioctl(fd, EVIOCGNAME(sizeof(name)), name) >= 0 && strcasecmp(name, keyboard_name) == 0) return fd; close(fd); } fprintf(stderr, "Cannot find input device for \"%s\"\n", keyboard_name); exit(1); } static int get_lastkeypress (int fd) { struct input_event ev[64]; int keycode = -1; int scancode = -1; int i, n; if ((n = read(fd, ev, sizeof(ev))) < sizeof(struct input_event)) { perror("read event"); exit(1); } for (i = 0; i < n/sizeof(struct input_event); i++) { if (ev[i].type == EV_MSC && ev[i].code == MSC_SCAN) scancode = ev[i].value; if (ev[i].type == EV_KEY && ev[i].value == 1 && scancode >= 0) { keycode = scancode; scancode = -1; } } return keycode; } static void load_keymap (char *path, keysym_map_t *map) { if (strchr(path, '/')) { char *dir = strdup(path); char *file = strrchr(dir, '/'); *file++ = '\0'; chdir(dir); load_keymap1(file, map); free(dir); } else { load_keymap1(path, map); } } static void load_keymap1 (char *file, keysym_map_t *map) { char line[1024]; FILE *f; if (!(f = fopen(file, "r"))) { perror(file); exit(1); } while (fgets(line, sizeof(line), f)) { char *ptr = strchr(line, '#'); char *keyname, *p1, *p2; KeySym ks; int code = -1; bool shift = false; bool altgr = false; bool addupper = false; bool numlock = false; bool inhibit = false; if (ptr) *ptr-- = '\0'; else ptr = &line[strlen(line)-1]; while (isspace(*ptr)) *ptr-- = '\0'; if (sscanf(line, "include %as", &p1) == 1) { load_keymap(p1, map); free(p1); continue; } else if (sscanf(line, "map %i", &code) == 1) { continue; } else if (sscanf(line, "%as %i %as %as", &keyname, &code, &p1, &p2) == 4) { altgr = (strcmp(p1, "altgr") == 0 || strcmp(p2, "altgr") == 0); shift = (strcmp(p1, "shift") == 0 || strcmp(p2, "shift") == 0); free(p1); free(p2); } else if (sscanf(line, "%as %i %as", &keyname, &code, &p1) == 3) { altgr = (strcmp(p1, "altgr") == 0); shift = (strcmp(p1, "shift") == 0); addupper = (strcmp(p1, "addupper") == 0); numlock = (strcmp(p1, "numlock") == 0); inhibit = (strcmp(p1, "inhibit") == 0); free(p1); } else if (sscanf(line, "%as %i", &keyname, &code) != 2) { continue; } if (!keyname) { fprintf(stderr, "malformed line: \"%s\"\n", line); continue; } if ((ks = XStringToKeysym(keyname)) == NoSymbol) { fprintf(stderr, "Unknown keysym \"%s\"\n", keyname); free(keyname); continue; } free(keyname); if (inhibit) continue; if (code <= 0 || code >= MAX_SCANCODE) { fprintf(stderr, "Scancode out of range at \"%s\"\n", line); continue; } if (numlock) map->numlock[code] = ks; else if (shift && altgr) map->shift_altgr[code] = ks; else if (shift) map->shift[code] = ks; else if (altgr) map->altgr[code] = ks; else { map->plain[code] = ks; if (addupper) /* this is somewhat dubious */ map->shift[code] = ks + 'A' - 'a'; } } } static char *check_scancode (keysym_map_t *map, KeySym ks, int scancode, bool shift, bool altgr, bool numlock) { static char msg[128]; int *mmap; if (numlock) mmap = map->numlock; else if (shift & altgr) mmap = map->shift_altgr; else if (shift) mmap = map->shift; else if (altgr) mmap = map->altgr; else mmap = map->plain; if (scancode <= 0 || scancode >= MAX_SCANCODE) snprintf(msg, sizeof(msg), "# scancode %#x out of range\n# ", scancode); else if (mmap[scancode] == 0) return ""; else if (mmap[scancode] != ks) return ""; else return NULL; return msg; } static void handler (int sig) { done = true; exit(0); } --------------010104010300090209080402 Content-Type: text/x-csrc; name="evconv.c" Content-Disposition: inline; filename="evconv.c" Content-Transfer-Encoding: 7bit /* * Test Xen keymaps * * Compile: * cc -o evconv evconv.c -lX11 * * Usage: * evconv /usr/share/xen/qemu/keymaps/ * * The keysyms coming from a keypress or keyrelease in a small X * window are matched to the corresponding scancode from a PS/2 * keyboard (and it must be a PS/2 keyboard: USB keyboards won't * work). Each distinct keypress or keyrelese is printed out as the * corresponding scancode and keysym. For example, "Q", "Shift-Q" * "AltGr-Q" and "Shift-Altgr-Q" should all produce the scancode 0x10 * although they will, of course, result in four different keysyms. * */ #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_VNC_KEYSYM #include "vnc_keysym.h" #endif #define MAX_SCANCODE 255 struct ks { int keysym; int scancode; }; typedef struct { int n; struct ks k[MAX_SCANCODE]; } keysym_map_t; #define KEY_LOCALSTATE 0x1 #define KEY_KEYPAD 0x2 typedef struct { keysym_map_t plain; keysym_map_t shift; keysym_map_t altgr; keysym_map_t shift_altgr; keysym_map_t numlock; unsigned char flags [MAX_SCANCODE]; } keysym_maps_t; const char *keyboard_name = "AT Translated Set 2 keyboard"; static Display *open_display (char *display_name, int argc, char **argv); static void get_keyboard_info (Display *dpy, int *numlock_mask, int *altgr_mask); static void load_keymap (char *path, keysym_maps_t *map); static void load_keymap1 (char *file, keysym_maps_t *map); static void handler (int sig); static void prepare_keymap(keysym_maps_t *); static int lookup_scancode(keysym_map_t *map, int keysym); static int lookup_keysym (const char *name); static volatile bool done = false; int main (int argc, char **argv) { Display *dpy; int numlock_mask = 0; int altgr_mask = 0; keysym_maps_t map; KeySym last = NoSymbol; dpy = open_display(NULL, argc, argv); get_keyboard_info(dpy, &numlock_mask, &altgr_mask); memset(&map, 0, sizeof(map)); if (argv[1]) load_keymap(argv[1], &map); prepare_keymap(&map); sigaction(SIGINT, &((struct sigaction) { .sa_handler = handler }), NULL); while (!done) { XEvent event; XNextEvent(dpy, &event); if (event.type == KeyPress || event.type == KeyRelease) { XKeyEvent *ev = (XKeyEvent *) &event; KeySym ks = NoSymbol; char *keyname; char str[256]; int scancode; XLookupString(ev, str, sizeof(str)-1, &ks, NULL); if (last == ks) { if (event.type == KeyRelease) last = NoSymbol; continue; } if (event.type == KeyPress) last = ks; keyname = XKeysymToString(ks) ?: "VoidSymbol"; if ((scancode = lookup_scancode(&map.plain, ks))) printf("%02x %s%s%s\n", scancode, keyname, (map.flags[scancode] & KEY_LOCALSTATE) ? " (localstate)" : "", (map.flags[scancode] & KEY_KEYPAD) ? " (numlock off)" : ""); if ((scancode = lookup_scancode(&map.shift, ks))) printf("%02x %s (shift)\n", scancode, keyname); if ((scancode = lookup_scancode(&map.altgr, ks))) printf("%02x %s (altgr)\n", scancode, keyname); if ((scancode = lookup_scancode(&map.shift_altgr, ks))) printf("%02x %s (shift altgr)\n", scancode, keyname); if ((scancode = lookup_scancode(&map.numlock, ks))) printf("%02x %s (numlock on)\n", scancode, keyname); fflush(stdout); } } exit(0); } static Display *open_display (char *display_name, int argc, char **argv) { Display *dpy; XSizeHints hints; XSetWindowAttributes attr; Window w; if (!(dpy = XOpenDisplay(display_name))) { fprintf(stderr, "Can't open display\n"); exit(1); } hints.width = hints.min_width = 100; hints.height = hints.min_height = 100; hints.flags = PMinSize; hints.x = hints.y = 0; memset(&attr, 0, sizeof(attr)); attr.background_pixel = WhitePixel(dpy, DefaultScreen(dpy)); attr.border_pixel = BlackPixel(dpy, DefaultScreen(dpy)); attr.event_mask = KeyPressMask | KeyReleaseMask; w = XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, 100, 100, 0, 0, InputOutput, CopyFromParent, CWBackPixel | CWBorderPixel | CWEventMask, &attr); XSetStandardProperties(dpy, w, "evkey", NULL, 0, argv, argc, &hints); XMapWindow(dpy, w); return dpy; } static void get_keyboard_info (Display *dpy, int *numlock_mask, int *altgr_mask) { int min_keycode, max_keycode, keysyms_per_keycode; XModifierKeymap *map; int i, j; XDisplayKeycodes(dpy, &min_keycode, &max_keycode); XGetKeyboardMapping(dpy, min_keycode, max_keycode - min_keycode + 1, &keysyms_per_keycode); map = XGetModifierMapping(dpy); for (i = 0; i < 8*map->max_keypermod; i += map->max_keypermod) { for (j = 0; j < map->max_keypermod; j++) { if (map->modifiermap[i+j]) { int index = 0; KeySym ks = NoSymbol; while (ks == NoSymbol && index < keysyms_per_keycode) ks = XKeycodeToKeysym(dpy, map->modifiermap[i+j], index++); if (*numlock_mask == 0 && ks == XK_Num_Lock) *numlock_mask = 1 << (i / map->max_keypermod); else if (*altgr_mask == 0 && ks == XK_ISO_Level3_Shift) *altgr_mask = 1 << (i / map->max_keypermod); } } } } static void load_keymap (char *path, keysym_maps_t *map) { if (strchr(path, '/')) { char *dir = strdup(path); char *file = strrchr(dir, '/'); *file++ = '\0'; chdir(dir); load_keymap1(file, map); free(dir); } else { load_keymap1(path, map); } } static void load_keymap1 (char *file, keysym_maps_t *map) { char line[1024]; FILE *f; if (!(f = fopen(file, "r"))) { perror(file); exit(1); } while (fgets(line, sizeof(line), f)) { char *ptr = strchr(line, '#'); char *keyname, *p1, *p2; KeySym ks; int code = -1; bool shift = false; bool altgr = false; bool addupper = false; bool numlock = false; bool inhibit = false; if (ptr) *ptr-- = '\0'; else ptr = &line[strlen(line)-1]; while (isspace(*ptr)) *ptr-- = '\0'; if (sscanf(line, "include %as", &p1) == 1) { load_keymap(p1, map); free(p1); continue; } else if (sscanf(line, "map %i", &code) == 1) { continue; } else if (sscanf(line, "%as %i %as %as", &keyname, &code, &p1, &p2) == 4) { altgr = (strcmp(p1, "altgr") == 0 || strcmp(p2, "altgr") == 0); shift = (strcmp(p1, "shift") == 0 || strcmp(p2, "shift") == 0); free(p1); free(p2); } else if (sscanf(line, "%as %i %as", &keyname, &code, &p1) == 3) { altgr = (strcmp(p1, "altgr") == 0); shift = (strcmp(p1, "shift") == 0); addupper = (strcmp(p1, "addupper") == 0); numlock = (strcmp(p1, "numlock") == 0); inhibit = (strcmp(p1, "inhibit") == 0); free(p1); } else if (sscanf(line, "%as %i", &keyname, &code) != 2) { continue; } if (!keyname) { fprintf(stderr, "malformed line: \"%s\"\n", line); continue; } if ((ks = lookup_keysym(keyname)) == 0) { fprintf(stderr, "Unknown keysym \"%s\"\n", keyname); free(keyname); continue; } free(keyname); if (inhibit) continue; if (code <= 0 || code >= MAX_SCANCODE) { fprintf(stderr, "Scancode out of range at \"%s\"\n", line); continue; } if (numlock) map->numlock.k[code].keysym = ks; else if (shift && altgr) map->shift_altgr.k[code].keysym = ks; else if (shift) map->shift.k[code].keysym = ks; else if (altgr) map->altgr.k[code].keysym = ks; else { map->plain.k[code].keysym = ks; if (addupper) /* this is somewhat dubious */ map->shift.k[code].keysym = ks + 'A' - 'a'; } } } static void handler (int sig) { done = true; exit(0); } int cmp (const void *a, const void *b) { return ((struct ks *) b)->keysym - ((struct ks *) a)->keysym; } static void sort_map (keysym_map_t *map) { int i; for (i = 0; i < MAX_SCANCODE; i++) map->k[i].scancode = i; qsort(map->k, MAX_SCANCODE, sizeof(struct ks), cmp); for (map->n = 0; map->n < MAX_SCANCODE; map->n++) if (!map->k[map->n].keysym) break; } static void prepare_keymap (keysym_maps_t *map) { int i; for (i = 0; i < MAX_SCANCODE; i++) { if (!(map->shift.k[i].keysym || map->altgr.k[i].keysym || map->shift_altgr.k[i].keysym)) map->flags[i] |= KEY_LOCALSTATE; if (map->numlock.k[i].keysym) map->flags[i] |= KEY_KEYPAD; } sort_map(&map->plain); sort_map(&map->shift); sort_map(&map->altgr); sort_map(&map->shift_altgr); sort_map(&map->numlock); } static int lookup_scancode (keysym_map_t *map, int keysym) { int l = 0, r = map->n-1; while (l <= r) { int m = (l + r)/2; if (map->k[m].keysym == keysym) return map->k[m].scancode; else if (map->k[m].keysym < keysym) r = m-1; else l = m+1; } return 0; } #ifdef USE_VNC_KEYSYM static int keysym_cmp (const void *a, const void *b) { return strcmp(((name2keysym_t *) a)->name, ((name2keysym_t *) b)->name); } static int lookup_keysym (const char *name) { int l, r; static int n = -1; if (n < 0) { for (n = 0; name2keysym[n].name; n++); qsort(name2keysym, n, sizeof(name2keysym_t), keysym_cmp); } l = 0; r = n-1; while (l <= r) { int m = (l + r)/2; int cmp = strcmp(name2keysym[m].name, name); if (cmp < 0) l = m + 1; else if (cmp > 0) r = m - 1; else return name2keysym[m].keysym; } if (name[0] == 'U') { /* unicode symbol key */ char *ptr; int k = strtol(name+1, &ptr, 16); return *ptr ? 0 : k; } return 0; } #else static int lookup_keysym (const char *name) { return XStringToKeysym(name); } #endif --------------010104010300090209080402--