summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Pitt <martin.pitt@ubuntu.com>2011-01-16 21:10:03 +0100
committerMartin Pitt <martin.pitt@ubuntu.com>2011-01-16 21:10:03 +0100
commit90f182c3d69a4451bb1ea6f79d8d7fe4895cdf89 (patch)
tree1b636dc75d7db14658be19e78bc14757b3fd9e94
parent45019c0e88d76c9568f581582cdb985bb9393354 (diff)
keymap: More robust state machine
Some drivers, like thinkpad_acpi, do not send a scan code at all (for known keys), and some send the key code first, then the scan code. Implement a better state machine which acceps them in any order and wait until a SYN event. If the driver does not send SYN events, keymap will also handle this and print out that fact. Thanks to Seth Forshee for pointing out how this really works! https://launchpad.net/bugs/702407
-rw-r--r--extras/keymap/keymap.c85
1 files changed, 66 insertions, 19 deletions
diff --git a/extras/keymap/keymap.c b/extras/keymap/keymap.c
index 2d68e7f0c2..ed6b69d5f1 100644
--- a/extras/keymap/keymap.c
+++ b/extras/keymap/keymap.c
@@ -25,6 +25,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdint.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
@@ -274,41 +275,87 @@ static int read_event(int fd, struct input_event* ev)
return 1;
}
-static void print_key(struct input_event *event)
+static void print_key(uint32_t scancode, uint16_t keycode, int has_scan, int has_key)
{
- static int cur_scancode = 0;
const char *keyname;
- /* save scan code for next EV_KEY event */
- if (event->type == EV_MSC && event->code == MSC_SCAN)
- cur_scancode = event->value;
+ /* ignore key release events */
+ if (has_key == 1)
+ return;
- /* key press */
- if (event->type == EV_KEY && event->value) {
- keyname = key_names[event->code];
- if (keyname != NULL)
- printf("scan code: 0x%02X key code: %s\n", cur_scancode,
- format_keyname(key_names[event->code]));
- else
- printf("scan code: 0x%02X key code: %03X\n", cur_scancode,
- event->code);
+ 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");
+
+ has_scan = has_key = 0;
while (read_event(fd, &ev)) {
- print_key(&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;
+ }
- /* stop on Escape key release */
- if (ev.type == EV_KEY && ev.code == KEY_ESC && ev.value == 0)
- break;
}
/* release input device */