diff options
author | Martin Pitt <martin.pitt@ubuntu.com> | 2009-05-09 12:07:58 +0200 |
---|---|---|
committer | Martin Pitt <martin.pitt@ubuntu.com> | 2009-05-09 12:07:58 +0200 |
commit | 489d381c50a2c639ea27bc8fac7f58c145ac8679 (patch) | |
tree | 689ce008509d361200d30f5ed9cb6af37ce42c8b /keymap | |
parent | fa361b47d790c0ab63dc6c9d3bd18e8c437abbd6 (diff) |
add keymap directory and tool
keymap assigns keycodes to scancodes, or dumps the current mapping of
an evdev keyboard device. It replaces hal-setup-keymap.
The former keymap fdi files from hal-info will be converted to udev
rules for vendor/product matching, and simple text files for mapping
scancodes to keycodes.
See this discussion for details:
http://lists.freedesktop.org/archives/devkit-devel/2009-May/000152.html
Diffstat (limited to 'keymap')
-rw-r--r-- | keymap/Makefile.am | 52 | ||||
-rw-r--r-- | keymap/keymap.c | 259 |
2 files changed, 311 insertions, 0 deletions
diff --git a/keymap/Makefile.am b/keymap/Makefile.am new file mode 100644 index 0000000..10be735 --- /dev/null +++ b/keymap/Makefile.am @@ -0,0 +1,52 @@ +# 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 $(top_srcdir)/Makefile.am.inc + +# rules and keymaps + +udevrulesdir = $(udev_prefix)/lib/udev/rules.d +dist_udevrules_DATA = 95-keymap.rules + +udevkeymapdir = $(udevhomedir)/keymaps +udevkeymap_DATA = keymaps/* + +# keymap program + +udevhomedir = $(udev_prefix)/lib/udev +udevhome_PROGRAMS = keymap + +keymap_SOURCES = keymap.c keys-from-name.h keys-to-name.h +keymap_CPPFLAGS = $(AM_CPPFLAGS) + +BUILT_SOURCES = keys-from-name.h keys-to-name.h +CLEANFILES = keys.txt keys-from-name.gperf keys-from-name.h keys-to-name.h + +# +# generation of keys-{from,to}-name.h from linux/input.h and gperf +# + +keys.txt: /usr/include/linux/input.h + awk '/^#define.*KEY_/ { if ($$2 != "KEY_MAX" && $$2 != "KEY_CNT") { print $$2 } }' < $< > $@ + +keys-from-name.gperf: keys.txt + awk 'BEGIN{ print "struct key { const char* name; unsigned short id; };"; print "%null-strings"; print "%%";} { print $$1 ", " $$1 }' < $< > $@ + +keys-from-name.h: keys-from-name.gperf Makefile + gperf -t --ignore-case -N lookup_key -H hash_key_name -p -C < $< > $@ + +keys-to-name.h: keys.txt Makefile + awk 'BEGIN{ print "const char* const key_names[KEY_CNT] = { "} { print "[" $$1 "] = \"" $$1 "\"," } END{print "};"}' < $< > $@ + + diff --git a/keymap/keymap.c b/keymap/keymap.c new file mode 100644 index 0000000..b7ed547 --- /dev/null +++ b/keymap/keymap.c @@ -0,0 +1,259 @@ +/* + * keymap - dump keymap of an evdev device or set a new keymap from a file + * + * Based on keyfuzz by Lennart Poettering <mzqrovna@0pointer.net> + * Adapted for udev-extras by Martin Pitt <martin.pitt@ubuntu.com> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#include <linux/input.h> + +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 + +/* If keymap file is given without a path, assume this one; must end with '/' * */ +#define DEFAULT_PATH "/lib/udev/keymaps/" + +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, "open('%s'): %s\n", dev, strerror(errno)); + 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: %s\n", strerror(errno)); + 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: %s\n", strerror(errno)); + 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: %s\n", strerror(errno)); + 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: %s\n", strerror(errno)); + 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) + 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 int merge_table(int fd, const char *filename) { + int r = 0; + int line = 0; + FILE* f; + + f = fopen(filename, "r"); + if (!f) { + perror(filename); + r = -1; + goto fail; + } + 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: + + return r; +} + +static const char* default_keymap_path(const char* path) { + static char result[PATH_MAX]; + + if (!strchr(path, '/')) { + snprintf(result, sizeof(result), "%s%s", DEFAULT_PATH, path); + return result; + } + return path; +} + +int main(int argc, char **argv) +{ + int fd = -1; + + if (argc < 2 || argc > 3) { + printf("Usage: %s <event device> [<map file>]\n", argv[0]); + exit(1); + } + + if ((fd = evdev_open(argv[1])) < 0) + return 1; + + if (argc == 3) + merge_table(fd, default_keymap_path(argv[2])); + else + dump_table(fd); + + return 0; +} |