summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Pitt <martin.pitt@ubuntu.com>2009-05-09 12:07:58 +0200
committerMartin Pitt <martin.pitt@ubuntu.com>2009-05-09 12:07:58 +0200
commit489d381c50a2c639ea27bc8fac7f58c145ac8679 (patch)
tree689ce008509d361200d30f5ed9cb6af37ce42c8b
parentfa361b47d790c0ab63dc6c9d3bd18e8c437abbd6 (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
-rw-r--r--Makefile.am1
-rw-r--r--configure.ac1
-rw-r--r--keymap/Makefile.am52
-rw-r--r--keymap/keymap.c259
4 files changed, 313 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index 74d87fb..502dcbb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,6 +4,7 @@ SUBDIRS = \
rules.d \
modem-modeswitch \
usb-db \
+ keymap \
udev-acl
ACLOCAL_AMFLAGS = -I m4
diff --git a/configure.ac b/configure.ac
index ccf982d..45931b4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -51,6 +51,7 @@ Makefile
rules.d/Makefile
modem-modeswitch/Makefile
udev-acl/Makefile
+keymap/Makefile
usb-db/Makefile
])
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;
+}