chiark / gitweb /
keymap: move from udev-extras
[elogind.git] / extras / keymap / keymap.c
1 /*
2  * keymap - dump keymap of an evdev device or set a new keymap from a file
3  *
4  * Based on keyfuzz by Lennart Poettering <mzqrovna@0pointer.net>
5  * Adapted for udev-extras by Martin Pitt <martin.pitt@ubuntu.com>
6  *
7  * Copyright (C) 2006, Lennart Poettering
8  * Copyright (C) 2009, Canonical Ltd.
9  *
10  * keymap is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * keymap is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with keymap; if not, write to the Free Software Foundation,
22  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
23  */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <limits.h>
32 #include <fcntl.h>
33 #include <getopt.h>
34 #include <sys/ioctl.h>
35 #include <linux/input.h>
36
37 const struct key* lookup_key (const char *str, unsigned int len);
38
39 #include "keys-from-name.h"
40 #include "keys-to-name.h"
41
42 #define MAX_SCANCODES 1024
43
44 /* If keymap file is given without a path, assume this one; must end with '/' * */
45 #define DEFAULT_PATH "/lib/udev/keymaps/"
46
47 static int evdev_open(const char *dev)
48 {
49         int fd;
50         char fn[PATH_MAX];
51
52         if (strncmp(dev, "/dev", 4) != 0) {
53                 snprintf(fn, sizeof(fn), "/dev/%s", dev);
54                 dev = fn;
55         }
56
57         if ((fd = open(dev, O_RDWR)) < 0) {
58                 fprintf(stderr, "error open('%s'): %m\n", dev);
59                 return -1;
60         }
61         return fd;
62 }
63
64 static int evdev_get_keycode(int fd, int scancode, int e)
65 {
66         int codes[2];
67
68         codes[0] = scancode;
69         if (ioctl(fd, EVIOCGKEYCODE, codes) < 0) {
70                 if (e && errno == EINVAL) {
71                         return -2;
72                 } else {
73                         fprintf(stderr, "EVIOCGKEYCODE: %m\n");
74                         return -1;
75                 }
76         }
77         return codes[1];
78 }
79
80 static int evdev_set_keycode(int fd, int scancode, int keycode)
81 {
82         int codes[2];
83
84         codes[0] = scancode;
85         codes[1] = keycode;
86
87         if (ioctl(fd, EVIOCSKEYCODE, codes) < 0) {
88                 fprintf(stderr, "EVIOCSKEYCODE: %m\n");
89                 return -1;
90         }
91         return 0;
92 }
93
94 static int evdev_driver_version(int fd, char *v, size_t l)
95 {
96         int version;
97
98         if (ioctl(fd, EVIOCGVERSION, &version)) {
99                 fprintf(stderr, "EVIOCGVERSION: %m\n");
100                 return -1;
101         }
102
103         snprintf(v, l, "%i.%i.%i.", version >> 16, (version >> 8) & 0xff, version & 0xff);
104         return 0;
105 }
106
107 static int evdev_device_name(int fd, char *n, size_t l)
108 {
109         if (ioctl(fd, EVIOCGNAME(l), n) < 0) {
110                 fprintf(stderr, "EVIOCGNAME: %m\n");
111                 return -1;
112         }
113         return 0;
114 }
115
116 /* Return a lower-case string with KEY_ prefix removed */
117 static const char* format_keyname(const char* key) {
118         static char result[101];
119         const char* s;
120         int len;
121
122         for (s = key+4, len = 0; *s && len < 100; ++len, ++s)
123                 result[len] = tolower(*s);
124         result[len] = '\0';
125         return result;
126 }
127
128 static int dump_table(int fd) {
129         char version[256], name[256];
130         int scancode, r = -1;
131
132         if (evdev_driver_version(fd, version, sizeof(version)) < 0)
133                 goto fail;
134
135         if (evdev_device_name(fd, name, sizeof(name)) < 0)
136                 goto fail;
137
138         printf("### evdev %s, driver '%s'\n", version, name);
139
140         r = 0;
141         for (scancode = 0; scancode < MAX_SCANCODES; scancode++) {
142                 int keycode;
143
144                 if ((keycode = evdev_get_keycode(fd, scancode, 1)) < 0) {
145                         if (keycode != -2)
146                                 r = -1;
147                         break;
148                 }
149
150                 if (keycode < KEY_MAX && key_names[keycode])
151                         printf("0x%03x %s\n", scancode, format_keyname(key_names[keycode]));
152                 else
153                         printf("0x%03x 0x%03x\n", scancode, keycode);
154         }
155 fail:
156         return r;
157 }
158
159 static int merge_table(int fd, const char *filename) {
160         int r = 0;
161         int line = 0;
162         FILE* f;
163
164         f = fopen(filename, "r");
165         if (!f) {
166                 perror(filename);
167                 r = -1;
168                 goto fail;
169         }
170
171         while (!feof(f)) {
172                 char s[256], *p;
173                 int scancode, new_keycode, old_keycode;
174
175                 if (!fgets(s, sizeof(s), f))
176                         break;
177
178                 line++;
179                 p = s+strspn(s, "\t ");
180                 if (*p == '#' || *p == '\n')
181                         continue;
182
183                 if (sscanf(p, "%i %i", &scancode, &new_keycode) != 2) {
184                         char t[105] = "KEY_UNKNOWN";
185                         const struct key *k;
186
187                         if (sscanf(p, "%i %100s", &scancode, t+4) != 2) {
188                                 fprintf(stderr, "WARNING: Parse failure at line %i, ignoring.\n", line);
189                                 r = -1;
190                                 continue;
191                         }
192
193                         if (!(k = lookup_key(t, strlen(t)))) {
194                                 fprintf(stderr, "WARNING: Unknown key '%s' at line %i, ignoring.\n", t, line);
195                                 r = -1;
196                                 continue;
197                         }
198
199                         new_keycode = k->id;
200                 }
201
202
203                 if ((old_keycode = evdev_get_keycode(fd, scancode, 0)) < 0) {
204                         r = -1;
205                         goto fail;
206                 }
207
208                 if (evdev_set_keycode(fd, scancode, new_keycode) < 0) {
209                         r = -1;
210                         goto fail;
211                 }
212
213                 if (new_keycode != old_keycode)
214                         fprintf(stderr, "Remapped scancode 0x%02x to 0x%02x (prior: 0x%02x)\n",
215                                 scancode, new_keycode, old_keycode);
216         }
217 fail:
218         return r;
219 }
220
221 static const char* default_keymap_path(const char* path)
222 {
223         static char result[PATH_MAX];
224
225         if (!strchr(path, '/')) {
226                 snprintf(result, sizeof(result), "%s%s", DEFAULT_PATH, path);
227                 return result;
228         }
229         return path;
230 }
231
232 static void print_key(struct input_event *event)
233 {
234         static int cur_scancode = 0;
235
236         /* save scan code for next EV_KEY event */
237         if (event->type == EV_MSC && event->code == MSC_SCAN)
238             cur_scancode = event->value;
239
240         /* key press */
241         if (event->type == EV_KEY && event->value)
242             printf("scan code: 0x%02X   key code: %s\n", cur_scancode,
243                     format_keyname(key_names[event->code]));
244 }
245
246 static void interactive(int fd)
247 {
248         struct input_event ev;
249         int run = 1;
250
251         /* grab input device */
252         ioctl(fd, EVIOCGRAB, 1);
253
254         puts("Press ESC to finish");
255         while (run) {
256                 switch (read(fd, &ev, sizeof(ev))) {
257                 case -1:
258                         perror("read");
259                         run = 0;
260                         break;
261                 case 0:
262                         run = 0;
263                         break;
264                 default:
265                         print_key(&ev);
266                         /* stop on Escape key release */
267                         if (ev.type == EV_KEY && ev.code == KEY_ESC && ev.value == 0)
268                                 run = 0;
269                         break;
270                 }
271         }
272
273         /* release input device */
274         ioctl(fd, EVIOCGRAB, 0);
275 }
276
277 int main(int argc, char **argv)
278 {
279         static const struct option options[] = {
280                 { "help", no_argument, NULL, 'h' },
281                 { "interactive", no_argument, NULL, 'i' },
282                 {}
283         };
284         int fd = -1;
285         int opt_interactive = 0;
286
287         while (1) {
288                 int option;
289
290                 option = getopt_long(argc, argv, "hi", options, NULL);
291                 if (option == -1)
292                         break;
293
294                 switch (option) {
295                 case 'h':
296                         printf("Usage: keymap <event device> [<map file>]\n\n");
297                         return 0;
298
299                 case 'i':
300                         opt_interactive = 1;
301                         break;
302                 default:
303                         return 1;
304                 }
305         }
306
307         if (argc < optind+1 || argc > optind+2) {
308                 fprintf(stderr, "Usage: keymap <event device> [<map file>]\n\n");
309                 return 2;
310         }
311
312         if ((fd = evdev_open(argv[optind])) < 0)
313                 return 3;
314
315         if (argc == optind+2)
316                 merge_table(fd, default_keymap_path(argv[optind+1]));
317         else {
318                 if (opt_interactive)
319                         interactive(fd);
320                 else
321                         dump_table(fd);
322         }
323         return 0;
324 }