X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=extras%2Fkeymap%2Fkeymap.c;h=597a53b467c20094b0233fb4d234f2ec38d438d2;hb=b3ca87a04a6bc32d852774211b35313ec8a09da1;hp=8fc731debf728083ddbb10fd17a7cff9ef63098f;hpb=b02c9fa19bb3da9cd1c4684322333732cc2996de;p=elogind.git diff --git a/extras/keymap/keymap.c b/extras/keymap/keymap.c index 8fc731deb..597a53b46 100644 --- a/extras/keymap/keymap.c +++ b/extras/keymap/keymap.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include const struct key* lookup_key (const char *str, unsigned int len); @@ -139,8 +141,9 @@ static int dump_table(int fd) { int keycode; if ((keycode = evdev_get_keycode(fd, scancode, 1)) < 0) { - if (keycode != -2) - r = -1; + if (keycode == -2) + continue; + r = -1; break; } @@ -153,6 +156,34 @@ 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; @@ -161,8 +192,7 @@ static int merge_table(int fd, const char *filename) { f = fopen(filename, "r"); if (!f) { perror(filename); - r = -1; - goto fail; + return -1; } while (!feof(f)) { @@ -212,6 +242,7 @@ static int merge_table(int fd, const char *filename) { scancode, new_keycode, old_keycode); } fail: + fclose(f); return r; } @@ -219,7 +250,7 @@ static const char* default_keymap_path(const char* path) { static char result[PATH_MAX]; - /* If keymap file is given without a path, assume udev diretory; must end with '/' * */ + /* 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; @@ -227,45 +258,105 @@ static const char* default_keymap_path(const char* path) return path; } -static void print_key(struct input_event *event) +/* read one event; return 1 if valid */ +static int read_event(int fd, struct input_event* ev) { - static int cur_scancode = 0; + int ret; + ret = read(fd, ev, sizeof(struct input_event)); - /* save scan code for next EV_KEY event */ - if (event->type == EV_MSC && event->code == MSC_SCAN) - cur_scancode = event->value; + 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; + } - /* key press */ - if (event->type == EV_KEY && event->value) - printf("scan code: 0x%02X key code: %s\n", cur_scancode, - format_keyname(key_names[event->code])); + 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; - int run = 1; + 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; + } - puts("Press ESC to finish"); - while (run) { - switch (read(fd, &ev, sizeof(ev))) { - case -1: - perror("read"); - run = 0; - break; - case 0: - run = 0; - break; - default: - print_key(&ev); - /* stop on Escape key release */ - if (ev.type == EV_KEY && ev.code == KEY_ESC && ev.value == 0) - run = 0; - break; + 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 */ @@ -275,6 +366,7 @@ static void interactive(int fd) static void help(int error) { const char* h = "Usage: keymap []\n" + " keymap scancode keyname [...]\n" " keymap -i \n"; if (error) { fputs(h, stderr); @@ -294,6 +386,7 @@ int main(int argc, char **argv) }; int fd = -1; int opt_interactive = 0; + int i; while (1) { int option; @@ -314,19 +407,35 @@ int main(int argc, char **argv) } } - if (argc < optind+1 || argc > optind+2) + if (argc < optind+1) help (1); if ((fd = evdev_open(argv[optind])) < 0) return 3; - if (argc == optind+2) - merge_table(fd, default_keymap_path(argv[optind+1])); - else { + /* one argument (device): dump or interactive */ + if (argc == optind+1) { if (opt_interactive) interactive(fd); else dump_table(fd); + return 0; } - 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 */ }