/* * 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); }