/* * keymap - dump keymap of an evdev device or set a new keymap from a file * * Based on keyfuzz by Lennart Poettering * Adapted for udev-extras by Martin Pitt * * Copyright (C) 2006, Lennart Poettering * Copyright (C) 2009, Canonical Ltd. * * keymap 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. * * keymap 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 keymap; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include const struct key* lookup_key (const char *str, unsigned int len); #include "keys-from-name.h" #include "keys-to-name.h" #define MAX_SCANCODES 1024 static int evdev_open(const char *dev) { int fd; char fn[PATH_MAX]; if (strncmp(dev, "/dev", 4) != 0) { snprintf(fn, sizeof(fn), "/dev/%s", dev); dev = fn; } if ((fd = open(dev, O_RDWR)) < 0) { fprintf(stderr, "error open('%s'): %m\n", dev); return -1; } return fd; } static int evdev_get_keycode(int fd, int scancode, int e) { int codes[2]; codes[0] = scancode; if (ioctl(fd, EVIOCGKEYCODE, codes) < 0) { if (e && errno == EINVAL) { return -2; } else { fprintf(stderr, "EVIOCGKEYCODE: %m\n"); return -1; } } return codes[1]; } static int evdev_set_keycode(int fd, int scancode, int keycode) { int codes[2]; codes[0] = scancode; codes[1] = keycode; if (ioctl(fd, EVIOCSKEYCODE, codes) < 0) { fprintf(stderr, "EVIOCSKEYCODE: %m\n"); return -1; } return 0; } static int evdev_driver_version(int fd, char *v, size_t l) { int version; if (ioctl(fd, EVIOCGVERSION, &version)) { fprintf(stderr, "EVIOCGVERSION: %m\n"); return -1; } snprintf(v, l, "%i.%i.%i.", version >> 16, (version >> 8) & 0xff, version & 0xff); return 0; } static int evdev_device_name(int fd, char *n, size_t l) { if (ioctl(fd, EVIOCGNAME(l), n) < 0) { fprintf(stderr, "EVIOCGNAME: %m\n"); return -1; } return 0; } /* Return a lower-case string with KEY_ prefix removed */ static const char* format_keyname(const char* key) { static char result[101]; const char* s; int len; for (s = key+4, len = 0; *s && len < 100; ++len, ++s) result[len] = tolower(*s); result[len] = '\0'; return result; } static int dump_table(int fd) { char version[256], name[256]; int scancode, r = -1; if (evdev_driver_version(fd, version, sizeof(version)) < 0) goto fail; if (evdev_device_name(fd, name, sizeof(name)) < 0) goto fail; printf("### evdev %s, driver '%s'\n", version, name); r = 0; for (scancode = 0; scancode < MAX_SCANCODES; scancode++) { int keycode; if ((keycode = evdev_get_keycode(fd, scancode, 1)) < 0) { if (keycode == -2) continue; r = -1; break; } if (keycode < KEY_MAX && key_names[keycode]) printf("0x%03x %s\n", scancode, format_keyname(key_names[keycode])); else printf("0x%03x 0x%03x\n", scancode, keycode); } fail: return r; } static void set_key(int fd, const char* scancode_str, const char* keyname) { unsigned scancode; char *endptr; char t[105] = "KEY_UNKNOWN"; const struct key *k; scancode = (unsigned) strtol(scancode_str, &endptr, 0); if (*endptr != '\0') { fprintf(stderr, "ERROR: Invalid scancode\n"); exit(1); } snprintf(t, sizeof(t), "KEY_%s", keyname); if (!(k = lookup_key(t, strlen(t)))) { fprintf(stderr, "ERROR: Unknown key name '%s'\n", keyname); exit(1); } if (evdev_set_keycode(fd, scancode, k->id) < 0) fprintf(stderr, "setting scancode 0x%2X to key code %i failed\n", scancode, k->id); else printf("setting scancode 0x%2X to key code %i\n", scancode, k->id); } static int merge_table(int fd, const char *filename) { int r = 0; int line = 0; FILE* f; f = fopen(filename, "r"); if (!f) { perror(filename); return -1; } while (!feof(f)) { char s[256], *p; int scancode, new_keycode, old_keycode; if (!fgets(s, sizeof(s), f)) break; line++; p = s+strspn(s, "\t "); if (*p == '#' || *p == '\n') continue; if (sscanf(p, "%i %i", &scancode, &new_keycode) != 2) { char t[105] = "KEY_UNKNOWN"; const struct key *k; if (sscanf(p, "%i %100s", &scancode, t+4) != 2) { fprintf(stderr, "WARNING: Parse failure at line %i, ignoring.\n", line); r = -1; continue; } if (!(k = lookup_key(t, strlen(t)))) { fprintf(stderr, "WARNING: Unknown key '%s' at line %i, ignoring.\n", t, line); r = -1; continue; } new_keycode = k->id; } if ((old_keycode = evdev_get_keycode(fd, scancode, 0)) < 0) { r = -1; goto fail; } if (evdev_set_keycode(fd, scancode, new_keycode) < 0) { r = -1; goto fail; } if (new_keycode != old_keycode) fprintf(stderr, "Remapped scancode 0x%02x to 0x%02x (prior: 0x%02x)\n", scancode, new_keycode, old_keycode); } fail: fclose(f); return r; } static const char* default_keymap_path(const char* path) { static char result[PATH_MAX]; /* If keymap file is given without a path, assume udev directory; must end with '/' * */ if (!strchr(path, '/')) { snprintf(result, sizeof(result), "%s%s", LIBEXECDIR "/keymaps/", path); return result; } return path; } /* read one event; return 1 if valid */ static int read_event(int fd, struct input_event* ev) { int ret; ret = read(fd, ev, sizeof(struct input_event)); if (ret < 0) { perror("read"); return 0; } if (ret != sizeof(struct input_event)) { fprintf(stderr, "did not get enough data for event struct, aborting\n"); return 0; } return 1; } static void print_key(uint32_t scancode, uint16_t keycode, int has_scan, int has_key) { const char *keyname; /* ignore key release events */ if (has_key == 1) return; if (has_key == 0 && has_scan != 0) { fprintf(stderr, "got scan code event 0x%02X without a key code event\n", scancode); return; } if (has_scan != 0) printf("scan code: 0x%02X ", scancode); else printf("(no scan code received) "); keyname = key_names[keycode]; if (keyname != NULL) printf("key code: %s\n", format_keyname(keyname)); else printf("key code: %03X\n", keycode); } static void interactive(int fd) { struct input_event ev; uint32_t last_scan = 0; uint16_t last_key = 0; int has_scan; /* boolean */ int has_key; /* 0: none, 1: release, 2: press */ /* grab input device */ ioctl(fd, EVIOCGRAB, 1); puts("Press ESC to finish, or Control-C if this device is not your primary keyboard"); has_scan = has_key = 0; while (read_event(fd, &ev)) { /* Drivers usually send the scan code first, then the key code, * then a SYN. Some drivers (like thinkpad_acpi) send the key * code first, and some drivers might not send SYN events, so * keep a robust state machine which can deal with any of those */ if (ev.type == EV_MSC && ev.code == MSC_SCAN) { if (has_scan) { fputs("driver did not send SYN event in between key events; previous event:\n", stderr); print_key(last_scan, last_key, has_scan, has_key); has_key = 0; } last_scan = ev.value; has_scan = 1; /*printf("--- got scan %u; has scan %i key %i\n", last_scan, has_scan, has_key); */ } else if (ev.type == EV_KEY) { if (has_key) { fputs("driver did not send SYN event in between key events; previous event:\n", stderr); print_key(last_scan, last_key, has_scan, has_key); has_scan = 0; } last_key = ev.code; has_key = 1 + ev.value; /*printf("--- got key %hu; has scan %i key %i\n", last_key, has_scan, has_key);*/ /* Stop on ESC */ if (ev.code == KEY_ESC && ev.value == 0) break; } else if (ev.type == EV_SYN) { /*printf("--- got SYN; has scan %i key %i\n", has_scan, has_key);*/ print_key(last_scan, last_key, has_scan, has_key); has_scan = has_key = 0; } } /* release input device */ ioctl(fd, EVIOCGRAB, 0); } static void help(int error) { const char* h = "Usage: keymap []\n" " keymap scancode keyname [...]\n" " keymap -i \n"; if (error) { fputs(h, stderr); exit(2); } else { fputs(h, stdout); exit(0); } } int main(int argc, char **argv) { static const struct option options[] = { { "help", no_argument, NULL, 'h' }, { "interactive", no_argument, NULL, 'i' }, {} }; int fd = -1; int opt_interactive = 0; int i; while (1) { int option; option = getopt_long(argc, argv, "hi", options, NULL); if (option == -1) break; switch (option) { case 'h': help(0); case 'i': opt_interactive = 1; break; default: return 1; } } if (argc < optind+1) help (1); if ((fd = evdev_open(argv[optind])) < 0) return 3; /* one argument (device): dump or interactive */ if (argc == optind+1) { if (opt_interactive) interactive(fd); else dump_table(fd); return 0; } /* two arguments (device, mapfile): set map file */ if (argc == optind+2) { merge_table(fd, default_keymap_path(argv[optind+1])); return 0; } /* more arguments (device, scancode/keyname pairs): set keys directly */ if ((argc - optind - 1) % 2 == 0) { for (i = optind+1; i < argc; i += 2) set_key(fd, argv[i], argv[i+1]); return 0; } /* invalid number of arguments */ help(1); return 1; /* not reached */ }