blob: 42094c2c84527005f4629a0ccc76cee85d78b41e [file] [log] [blame]
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -07001/*
2 * Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
David Sodmanbf3f2842014-11-12 08:26:58 -08007#include <ctype.h>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -07008#include <fcntl.h>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -07009#include <stdint.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
Dominik Behr93899452014-08-18 22:16:21 -070014#include <sys/select.h>
15#include <errno.h>
16#include <libudev.h>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070017#include "input.h"
David Sodmanbbcb0522014-09-19 10:34:07 -070018#include "dbus_interface.h"
19#include "dbus.h"
David Sodmanbf3f2842014-11-12 08:26:58 -080020#include "keysym.h"
David Sodmanbbcb0522014-09-19 10:34:07 -070021#include "util.h"
Ilja Friedel268188e2014-12-03 18:34:04 +000022
23#define MAX_TERMINALS (5)
David Sodmanbf3f2842014-11-12 08:26:58 -080024
Dominik Behr93899452014-08-18 22:16:21 -070025struct input_dev {
26 int fd;
27 char *path;
28};
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070029
David Sodmanbf3f2842014-11-12 08:26:58 -080030struct keyboard_state {
31 int shift_state;
32 int control_state;
33 int alt_state;
David Sodmane0cc8312014-11-18 11:16:36 -080034 int search_state;
David Sodmanbf3f2842014-11-12 08:26:58 -080035};
36
Dominik Behr93899452014-08-18 22:16:21 -070037struct {
38 struct udev *udev;
39 struct udev_monitor *udev_monitor;
40 int udev_fd;
41 unsigned int ndevs;
42 struct input_dev *devs;
David Sodmanbf3f2842014-11-12 08:26:58 -080043 struct keyboard_state kbd_state;
David Sodmanbbcb0522014-09-19 10:34:07 -070044 dbus_t *dbus;
David Sodmanbf3f2842014-11-12 08:26:58 -080045 uint32_t current_terminal;
46 terminal_t *terminals[MAX_TERMINALS];
Dominik Behr93899452014-08-18 22:16:21 -070047} input = {
48 .udev = NULL,
49 .udev_monitor = NULL,
50 .udev_fd = -1,
51 .ndevs = 0,
52 .devs = NULL,
David Sodmanbf3f2842014-11-12 08:26:58 -080053 .dbus = NULL,
54 .current_terminal = 0
Dominik Behr93899452014-08-18 22:16:21 -070055};
56
David Sodmane0cc8312014-11-18 11:16:36 -080057static void report_user_activity(int activity_type)
58{
59 dbus_method_call1(input.dbus, kPowerManagerServiceName,
60 kPowerManagerServicePath,
61 kPowerManagerInterface,
62 kHandleUserActivityMethod,
63 &activity_type);
64
65 switch (activity_type) {
66 case USER_ACTIVITY_BRIGHTNESS_UP_KEY_PRESS:
67 (void)dbus_method_call0(input.dbus,
68 kPowerManagerServiceName,
69 kPowerManagerServicePath,
70 kPowerManagerInterface,
71 kIncreaseScreenBrightnessMethod);
72 break;
73 case USER_ACTIVITY_BRIGHTNESS_DOWN_KEY_PRESS:
74 (void)dbus_method_call0(input.dbus,
75 kPowerManagerServiceName,
76 kPowerManagerServicePath,
77 kPowerManagerInterface,
78 kDecreaseScreenBrightnessMethod);
79 break;
80 }
81}
82
David Sodmanbf3f2842014-11-12 08:26:58 -080083static int input_special_key(struct input_key_event *ev)
84{
85 unsigned int i;
86
87 uint32_t ignore_keys[] = {
88 BTN_TOUCH, // touchpad events
89 BTN_TOOL_FINGER,
90 BTN_TOOL_DOUBLETAP,
91 BTN_TOOL_TRIPLETAP,
92 BTN_TOOL_QUADTAP,
93 BTN_TOOL_QUINTTAP,
94 BTN_LEFT, // mouse buttons
95 BTN_RIGHT,
96 BTN_MIDDLE,
97 BTN_SIDE,
98 BTN_EXTRA,
99 BTN_FORWARD,
100 BTN_BACK,
101 BTN_TASK
102 };
103
104 for (i = 0; i < ARRAY_SIZE(ignore_keys); i++)
105 if (ev->code == ignore_keys[i])
106 return 1;
107
108 switch (ev->code) {
109 case KEY_LEFTSHIFT:
110 case KEY_RIGHTSHIFT:
111 input.kbd_state.shift_state = ! !ev->value;
112 return 1;
113 case KEY_LEFTCTRL:
114 case KEY_RIGHTCTRL:
115 input.kbd_state.control_state = ! !ev->value;
116 return 1;
117 case KEY_LEFTALT:
118 case KEY_RIGHTALT:
119 input.kbd_state.alt_state = ! !ev->value;
120 return 1;
David Sodmane0cc8312014-11-18 11:16:36 -0800121 case KEY_LEFTMETA: // search key
122 input.kbd_state.search_state = ! !ev->value;
123 return 1;
David Sodmanbf3f2842014-11-12 08:26:58 -0800124 }
125
126 if (input.kbd_state.shift_state && ev->value) {
127 switch (ev->code) {
128 case KEY_PAGEUP:
129 term_page_up(input.terminals[input.current_terminal]);
130 return 1;
131 case KEY_PAGEDOWN:
132 term_page_down(input.terminals[input.current_terminal]);
133 return 1;
134 case KEY_UP:
135 term_line_up(input.terminals[input.current_terminal]);
136 return 1;
137 case KEY_DOWN:
138 term_line_down(input.terminals[input.current_terminal]);
139 return 1;
140 }
141 }
142
David Sodmane0cc8312014-11-18 11:16:36 -0800143 if (input.kbd_state.search_state && ev->value) {
144 switch (ev->code) {
145 case KEY_UP:
146 term_page_up(input.terminals[input.current_terminal]);
147 return 1;
148 case KEY_DOWN:
149 term_page_down(input.terminals[input.current_terminal]);
150 return 1;
151 }
152 }
153
David Sodmanbf3f2842014-11-12 08:26:58 -0800154 if (input.kbd_state.alt_state && input.kbd_state.control_state && ev->value) {
155 switch (ev->code) {
156 case KEY_F1:
157 input_ungrab();
158 input.terminals[input.current_terminal]->active = false;
David Sodman35d6bd82014-11-24 08:23:00 -0800159 video_release(input.terminals[input.current_terminal]->video);
David Sodmanbf3f2842014-11-12 08:26:58 -0800160 (void)dbus_method_call0(input.dbus,
161 kLibCrosServiceName,
162 kLibCrosServicePath,
163 kLibCrosServiceInterface,
164 kTakeDisplayOwnership);
165 break;
166 case KEY_F2:
167 case KEY_F3:
168 case KEY_F4:
169 case KEY_F5:
170 case KEY_F6:
171 case KEY_F7:
172 case KEY_F8:
173 case KEY_F9:
174 case KEY_F10:
175 (void)dbus_method_call0(input.dbus,
176 kLibCrosServiceName,
177 kLibCrosServicePath,
178 kLibCrosServiceInterface,
179 kReleaseDisplayOwnership);
180 break;
181 }
182
Dominik Behr617bb2c2014-12-03 13:25:33 -0800183 if ((ev->code >= KEY_F2) && (ev->code < KEY_F2 + MAX_TERMINALS)) {
David Sodmanbf3f2842014-11-12 08:26:58 -0800184 terminal_t* terminal =
185 input.terminals[input.current_terminal];
186 if (term_is_active(terminal))
187 terminal->active = false;
188 input.current_terminal = ev->code - KEY_F2;
189 terminal = input.terminals[input.current_terminal];
190 if (terminal == NULL) {
191 input.terminals[input.current_terminal] =
Ilja Friedel268188e2014-12-03 18:34:04 +0000192 term_init();
David Sodmanbf3f2842014-11-12 08:26:58 -0800193 terminal =
194 input.terminals[input.current_terminal];
195 if (!term_is_valid(terminal)) {
196 LOG(ERROR, "Term init failed");
197 return 1;
198 }
199 }
200 input.terminals[input.current_terminal]->active = true;
201 input_grab();
202 video_setmode(terminal->video);
203 term_redraw(terminal);
204 }
205
206 return 1;
207
208 }
209
David Sodmane0cc8312014-11-18 11:16:36 -0800210 if ((!input.kbd_state.search_state) && ev->value &&
211 (ev->code >= KEY_F1) && (ev->code <= KEY_F10)) {
212 switch (ev->code) {
213 case KEY_F1:
214 case KEY_F2:
215 case KEY_F3:
216 case KEY_F4:
217 case KEY_F5:
218 break;
219 case KEY_F6:
220 case KEY_F7:
221 report_user_activity(USER_ACTIVITY_BRIGHTNESS_DOWN_KEY_PRESS -
222 (ev->code - KEY_F6));
223 break;
224 case KEY_F8:
225 case KEY_F9:
226 case KEY_F10:
227 break;
228 }
229 return 1;
230 }
231
David Sodmanbf3f2842014-11-12 08:26:58 -0800232 return 0;
233}
234
235static void input_get_keysym_and_unicode(struct input_key_event *event,
236 uint32_t *keysym, uint32_t *unicode)
237{
238 struct {
239 uint32_t code;
240 uint32_t keysym;
241 } non_ascii_keys[] = {
242 { KEY_ESC, KEYSYM_ESC},
243 { KEY_HOME, KEYSYM_HOME},
244 { KEY_LEFT, KEYSYM_LEFT},
245 { KEY_UP, KEYSYM_UP},
246 { KEY_RIGHT, KEYSYM_RIGHT},
247 { KEY_DOWN, KEYSYM_DOWN},
248 { KEY_PAGEUP, KEYSYM_PAGEUP},
249 { KEY_PAGEDOWN, KEYSYM_PAGEDOWN},
250 { KEY_END, KEYSYM_END},
251 { KEY_INSERT, KEYSYM_INSERT},
252 { KEY_DELETE, KEYSYM_DELETE},
253 };
254
255 for (unsigned i = 0; i < ARRAY_SIZE(non_ascii_keys); i++) {
256 if (non_ascii_keys[i].code == event->code) {
257 *keysym = non_ascii_keys[i].keysym;
258 *unicode = -1;
259 return;
260 }
261 }
262
263 if (event->code >= ARRAY_SIZE(keysym_table) / 2) {
264 *keysym = '?';
265 } else {
266 *keysym = keysym_table[event->code * 2 + input.kbd_state.shift_state];
267 if ((input.kbd_state.control_state) && isascii(*keysym))
268 *keysym = tolower(*keysym) - 'a' + 1;
269 }
270
271 *unicode = *keysym;
272}
273
Dominik Behr93899452014-08-18 22:16:21 -0700274static int input_add(const char *devname)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700275{
Dominik Behr93899452014-08-18 22:16:21 -0700276 int ret = 0, fd = -1;
277 /* for some reason every device has a null enumerations and notifications
278 of every device come with NULL string first */
279 if (!devname) {
280 ret = -EINVAL;
281 goto errorret;
282 }
283 ret = fd = open(devname, O_RDONLY);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700284 if (fd < 0)
Dominik Behr93899452014-08-18 22:16:21 -0700285 goto errorret;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700286
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700287 ret = ioctl(fd, EVIOCGRAB, (void *) 1);
288
289 if (!ret) {
290 ioctl(fd, EVIOCGRAB, (void *) 0);
291 } else {
David Sodmanbbcb0522014-09-19 10:34:07 -0700292 LOG(ERROR, "Evdev device %s grabbed by another process",
Dominik Behr93899452014-08-18 22:16:21 -0700293 devname);
294 ret = -EBUSY;
295 goto closefd;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700296 }
297
Dominik Behr93899452014-08-18 22:16:21 -0700298 struct input_dev *newdevs =
299 realloc(input.devs, (input.ndevs + 1) * sizeof (struct input_dev));
300 if (!newdevs) {
301 ret = -ENOMEM;
302 goto closefd;
303 }
304 input.devs = newdevs;
305 input.devs[input.ndevs].fd = fd;
306 input.devs[input.ndevs].path = strdup(devname);
307 if (!input.devs[input.ndevs].path) {
308 ret = -ENOMEM;
309 goto closefd;
310 }
311 input.ndevs++;
312
313 return fd;
314
315closefd:
316 close(fd);
317errorret:
318 return ret;
319}
320
321static void input_remove(const char *devname)
322{
323 if (!devname) {
324 return;
325 }
326 unsigned int u;
327 for (u = 0; u < input.ndevs; u++) {
328 if (!strcmp(devname, input.devs[u].path)) {
329 free(input.devs[u].path);
330 close(input.devs[u].fd);
331 input.ndevs--;
332 if (u != input.ndevs) {
333 input.devs[u] = input.devs[input.ndevs];
334 }
335 return;
336 }
337 }
338}
339
Dominik Behr93899452014-08-18 22:16:21 -0700340
341int input_init()
342{
343 input.udev = udev_new();
344 if (!input.udev)
345 return -ENOENT;
346 input.udev_monitor = udev_monitor_new_from_netlink(input.udev, "udev");
347 if (!input.udev_monitor) {
348 udev_unref(input.udev);
349 return -ENOENT;
350 }
351 udev_monitor_filter_add_match_subsystem_devtype(input.udev_monitor, "input",
352 NULL);
353 udev_monitor_enable_receiving(input.udev_monitor);
354 input.udev_fd = udev_monitor_get_fd(input.udev_monitor);
355
356 struct udev_enumerate *udev_enum;
357 struct udev_list_entry *devices, *deventry;
358 udev_enum = udev_enumerate_new(input.udev);
359 udev_enumerate_add_match_subsystem(udev_enum, "input");
360 udev_enumerate_scan_devices(udev_enum);
361 devices = udev_enumerate_get_list_entry(udev_enum);
362 udev_list_entry_foreach(deventry, devices) {
363 const char *syspath;
364 struct udev_device *dev;
365 syspath = udev_list_entry_get_name(deventry);
366 dev = udev_device_new_from_syspath(input.udev, syspath);
367 input_add(udev_device_get_devnode(dev));
368 udev_device_unref(dev);
369 }
370 udev_enumerate_unref(udev_enum);
371
372 if (!isatty(fileno(stdout)))
373 setbuf(stdout, NULL);
374
David Sodmanbbcb0522014-09-19 10:34:07 -0700375 if (input.ndevs == 0) {
376 LOG(ERROR, "No valid inputs for terminal");
377 exit(EXIT_SUCCESS);
378 }
379
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700380 return 0;
381}
382
383void input_close()
384{
Dominik Behr93899452014-08-18 22:16:21 -0700385 unsigned int u;
386 for (u = 0; u < input.ndevs; u++) {
387 free(input.devs[u].path);
388 close(input.devs[u].fd);
389 }
390 free(input.devs);
391 input.devs = NULL;
392 input.ndevs = 0;
393
394 udev_monitor_unref(input.udev_monitor);
395 input.udev_monitor = NULL;
396 udev_unref(input.udev);
397 input.udev = NULL;
398 input.udev_fd = -1;
399
David Sodmanbbcb0522014-09-19 10:34:07 -0700400 dbus_destroy(input.dbus);
401
402}
403
404void input_set_dbus(dbus_t* dbus)
405{
406 input.dbus = dbus;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700407}
408
Dominik Behr93899452014-08-18 22:16:21 -0700409int input_setfds(fd_set * read_set, fd_set * exception_set)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700410{
Dominik Behr93899452014-08-18 22:16:21 -0700411 unsigned int u;
412 int max = -1;
413 for (u = 0; u < input.ndevs; u++) {
414 FD_SET(input.devs[u].fd, read_set);
415 FD_SET(input.devs[u].fd, exception_set);
416 if (input.devs[u].fd > max)
417 max = input.devs[u].fd;
418 }
419
420 FD_SET(input.udev_fd, read_set);
421 FD_SET(input.udev_fd, exception_set);
422 if (input.udev_fd > max)
423 max = input.udev_fd;
424 return max;
425}
426
Dominik Behr93899452014-08-18 22:16:21 -0700427struct input_key_event *input_get_event(fd_set * read_set,
428 fd_set * exception_set)
429{
430 unsigned int u;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700431 struct input_event ev;
432 int ret;
433
Dominik Behr93899452014-08-18 22:16:21 -0700434 if (FD_ISSET(input.udev_fd, exception_set)) {
435 /* udev died on us? */
David Sodmanbbcb0522014-09-19 10:34:07 -0700436 LOG(ERROR, "Exception on udev fd");
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700437 }
438
Dominik Behr93899452014-08-18 22:16:21 -0700439 if (FD_ISSET(input.udev_fd, read_set)
440 && !FD_ISSET(input.udev_fd, exception_set)) {
441 /* we got an udev notification */
442 struct udev_device *dev =
443 udev_monitor_receive_device(input.udev_monitor);
444 if (dev) {
445 if (!strcmp("add", udev_device_get_action(dev))) {
446 input_add(udev_device_get_devnode(dev));
447 } else
448 if (!strcmp("remove", udev_device_get_action(dev)))
449 {
450 input_remove(udev_device_get_devnode(dev));
451 }
452 udev_device_unref(dev);
453 }
454 }
455
456 for (u = 0; u < input.ndevs; u++) {
457 if (FD_ISSET(input.devs[u].fd, read_set)
458 && !FD_ISSET(input.devs[u].fd, exception_set)) {
459 ret =
460 read(input.devs[u].fd, &ev, sizeof (struct input_event));
461 if (ret < (int) sizeof (struct input_event)) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700462 LOG(ERROR, "expected %d bytes, got %d",
Dominik Behr93899452014-08-18 22:16:21 -0700463 (int) sizeof (struct input_event), ret);
464 return NULL;
465 }
466
467 if (ev.type == EV_KEY) {
468 struct input_key_event *event =
469 malloc(sizeof (*event));
470 event->code = ev.code;
471 event->value = ev.value;
Dominik Behr93899452014-08-18 22:16:21 -0700472 return event;
473 }
474 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700475 }
476
477 return NULL;
478}
479
David Sodmanbf3f2842014-11-12 08:26:58 -0800480int input_run(bool standalone)
481{
482 fd_set read_set, exception_set;
483 terminal_t* terminal;
484
485 if (standalone) {
486 (void)dbus_method_call0(input.dbus,
487 kLibCrosServiceName,
488 kLibCrosServicePath,
489 kLibCrosServiceInterface,
490 kReleaseDisplayOwnership);
491
Ilja Friedel268188e2014-12-03 18:34:04 +0000492 input.terminals[input.current_terminal] = term_init();
David Sodmanbf3f2842014-11-12 08:26:58 -0800493 terminal = input.terminals[input.current_terminal];
494 if (term_is_valid(terminal)) {
495 input_grab();
496 }
497 }
498
499 while (1) {
500 terminal = input.terminals[input.current_terminal];
501
502 FD_ZERO(&read_set);
503 FD_ZERO(&exception_set);
504 term_add_fd(terminal, &read_set, &exception_set);
505
506 int maxfd = input_setfds(&read_set, &exception_set);
507
508 maxfd = MAX(maxfd, term_fd(terminal)) + 1;
509
510 select(maxfd, &read_set, NULL, &exception_set, NULL);
511
512 if (term_exception(terminal, &exception_set))
513 return -1;
514
515 struct input_key_event *event;
516 event = input_get_event(&read_set, &exception_set);
517 if (event) {
518 if (!input_special_key(event) && event->value) {
519 uint32_t keysym, unicode;
520 if (term_is_active(terminal)) {
David Sodmanada98012014-12-03 10:05:19 -0800521 // Only report user activity when the terminal is active
522 report_user_activity(USER_ACTIVITY_OTHER);
David Sodmanbf3f2842014-11-12 08:26:58 -0800523 input_get_keysym_and_unicode(
524 event, &keysym, &unicode);
525 term_key_event(terminal,
526 keysym, unicode);
527 }
528 }
529
530 input_put_event(event);
531 }
532
533
534 term_dispatch_io(terminal, &read_set);
535
536 if (term_is_valid(terminal)) {
537 if (term_is_child_done(terminal)) {
538 term_close(terminal);
Ilja Friedel268188e2014-12-03 18:34:04 +0000539 input.terminals[input.current_terminal] = term_init();
David Sodmanbf3f2842014-11-12 08:26:58 -0800540 terminal = input.terminals[input.current_terminal];
541 if (!term_is_valid(terminal)) {
542 return -1;
543 }
544 }
545 }
546 }
547
548 return 0;
549}
550
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700551void input_put_event(struct input_key_event *event)
552{
553 free(event);
554}
David Sodmanbbcb0522014-09-19 10:34:07 -0700555
556void input_grab()
557{
558 unsigned int i;
559 for (i = 0; i < input.ndevs; i++) {
560 (void)ioctl(input.devs[i].fd, EVIOCGRAB, (void *) 1);
561 }
562}
563
564void input_ungrab()
565{
566 unsigned int i;
567 for (i = 0; i < input.ndevs; i++) {
568 (void)ioctl(input.devs[i].fd, EVIOCGRAB, (void*) 0);
569 }
570}