blob: 0b5b60b9db1eea8682509093e3762b0078424451 [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;
159 (void)dbus_method_call0(input.dbus,
160 kLibCrosServiceName,
161 kLibCrosServicePath,
162 kLibCrosServiceInterface,
163 kTakeDisplayOwnership);
164 break;
165 case KEY_F2:
166 case KEY_F3:
167 case KEY_F4:
168 case KEY_F5:
169 case KEY_F6:
170 case KEY_F7:
171 case KEY_F8:
172 case KEY_F9:
173 case KEY_F10:
174 (void)dbus_method_call0(input.dbus,
175 kLibCrosServiceName,
176 kLibCrosServicePath,
177 kLibCrosServiceInterface,
178 kReleaseDisplayOwnership);
179 break;
180 }
181
Dominik Behr617bb2c2014-12-03 13:25:33 -0800182 if ((ev->code >= KEY_F2) && (ev->code < KEY_F2 + MAX_TERMINALS)) {
David Sodmanbf3f2842014-11-12 08:26:58 -0800183 terminal_t* terminal =
184 input.terminals[input.current_terminal];
185 if (term_is_active(terminal))
186 terminal->active = false;
187 input.current_terminal = ev->code - KEY_F2;
188 terminal = input.terminals[input.current_terminal];
189 if (terminal == NULL) {
190 input.terminals[input.current_terminal] =
Ilja Friedel268188e2014-12-03 18:34:04 +0000191 term_init();
David Sodmanbf3f2842014-11-12 08:26:58 -0800192 terminal =
193 input.terminals[input.current_terminal];
194 if (!term_is_valid(terminal)) {
195 LOG(ERROR, "Term init failed");
196 return 1;
197 }
198 }
199 input.terminals[input.current_terminal]->active = true;
200 input_grab();
201 video_setmode(terminal->video);
202 term_redraw(terminal);
203 }
204
205 return 1;
206
207 }
208
David Sodmane0cc8312014-11-18 11:16:36 -0800209 if ((!input.kbd_state.search_state) && ev->value &&
210 (ev->code >= KEY_F1) && (ev->code <= KEY_F10)) {
211 switch (ev->code) {
212 case KEY_F1:
213 case KEY_F2:
214 case KEY_F3:
215 case KEY_F4:
216 case KEY_F5:
217 break;
218 case KEY_F6:
219 case KEY_F7:
220 report_user_activity(USER_ACTIVITY_BRIGHTNESS_DOWN_KEY_PRESS -
221 (ev->code - KEY_F6));
222 break;
223 case KEY_F8:
224 case KEY_F9:
225 case KEY_F10:
226 break;
227 }
228 return 1;
229 }
230
David Sodmanbf3f2842014-11-12 08:26:58 -0800231 return 0;
232}
233
234static void input_get_keysym_and_unicode(struct input_key_event *event,
235 uint32_t *keysym, uint32_t *unicode)
236{
237 struct {
238 uint32_t code;
239 uint32_t keysym;
240 } non_ascii_keys[] = {
241 { KEY_ESC, KEYSYM_ESC},
242 { KEY_HOME, KEYSYM_HOME},
243 { KEY_LEFT, KEYSYM_LEFT},
244 { KEY_UP, KEYSYM_UP},
245 { KEY_RIGHT, KEYSYM_RIGHT},
246 { KEY_DOWN, KEYSYM_DOWN},
247 { KEY_PAGEUP, KEYSYM_PAGEUP},
248 { KEY_PAGEDOWN, KEYSYM_PAGEDOWN},
249 { KEY_END, KEYSYM_END},
250 { KEY_INSERT, KEYSYM_INSERT},
251 { KEY_DELETE, KEYSYM_DELETE},
252 };
253
254 for (unsigned i = 0; i < ARRAY_SIZE(non_ascii_keys); i++) {
255 if (non_ascii_keys[i].code == event->code) {
256 *keysym = non_ascii_keys[i].keysym;
257 *unicode = -1;
258 return;
259 }
260 }
261
262 if (event->code >= ARRAY_SIZE(keysym_table) / 2) {
263 *keysym = '?';
264 } else {
265 *keysym = keysym_table[event->code * 2 + input.kbd_state.shift_state];
266 if ((input.kbd_state.control_state) && isascii(*keysym))
267 *keysym = tolower(*keysym) - 'a' + 1;
268 }
269
270 *unicode = *keysym;
271}
272
Dominik Behr93899452014-08-18 22:16:21 -0700273static int input_add(const char *devname)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700274{
Dominik Behr93899452014-08-18 22:16:21 -0700275 int ret = 0, fd = -1;
276 /* for some reason every device has a null enumerations and notifications
277 of every device come with NULL string first */
278 if (!devname) {
279 ret = -EINVAL;
280 goto errorret;
281 }
282 ret = fd = open(devname, O_RDONLY);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700283 if (fd < 0)
Dominik Behr93899452014-08-18 22:16:21 -0700284 goto errorret;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700285
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700286 ret = ioctl(fd, EVIOCGRAB, (void *) 1);
287
288 if (!ret) {
289 ioctl(fd, EVIOCGRAB, (void *) 0);
290 } else {
David Sodmanbbcb0522014-09-19 10:34:07 -0700291 LOG(ERROR, "Evdev device %s grabbed by another process",
Dominik Behr93899452014-08-18 22:16:21 -0700292 devname);
293 ret = -EBUSY;
294 goto closefd;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700295 }
296
Dominik Behr93899452014-08-18 22:16:21 -0700297 struct input_dev *newdevs =
298 realloc(input.devs, (input.ndevs + 1) * sizeof (struct input_dev));
299 if (!newdevs) {
300 ret = -ENOMEM;
301 goto closefd;
302 }
303 input.devs = newdevs;
304 input.devs[input.ndevs].fd = fd;
305 input.devs[input.ndevs].path = strdup(devname);
306 if (!input.devs[input.ndevs].path) {
307 ret = -ENOMEM;
308 goto closefd;
309 }
310 input.ndevs++;
311
312 return fd;
313
314closefd:
315 close(fd);
316errorret:
317 return ret;
318}
319
320static void input_remove(const char *devname)
321{
322 if (!devname) {
323 return;
324 }
325 unsigned int u;
326 for (u = 0; u < input.ndevs; u++) {
327 if (!strcmp(devname, input.devs[u].path)) {
328 free(input.devs[u].path);
329 close(input.devs[u].fd);
330 input.ndevs--;
331 if (u != input.ndevs) {
332 input.devs[u] = input.devs[input.ndevs];
333 }
334 return;
335 }
336 }
337}
338
Dominik Behr93899452014-08-18 22:16:21 -0700339
340int input_init()
341{
342 input.udev = udev_new();
343 if (!input.udev)
344 return -ENOENT;
345 input.udev_monitor = udev_monitor_new_from_netlink(input.udev, "udev");
346 if (!input.udev_monitor) {
347 udev_unref(input.udev);
348 return -ENOENT;
349 }
350 udev_monitor_filter_add_match_subsystem_devtype(input.udev_monitor, "input",
351 NULL);
352 udev_monitor_enable_receiving(input.udev_monitor);
353 input.udev_fd = udev_monitor_get_fd(input.udev_monitor);
354
355 struct udev_enumerate *udev_enum;
356 struct udev_list_entry *devices, *deventry;
357 udev_enum = udev_enumerate_new(input.udev);
358 udev_enumerate_add_match_subsystem(udev_enum, "input");
359 udev_enumerate_scan_devices(udev_enum);
360 devices = udev_enumerate_get_list_entry(udev_enum);
361 udev_list_entry_foreach(deventry, devices) {
362 const char *syspath;
363 struct udev_device *dev;
364 syspath = udev_list_entry_get_name(deventry);
365 dev = udev_device_new_from_syspath(input.udev, syspath);
366 input_add(udev_device_get_devnode(dev));
367 udev_device_unref(dev);
368 }
369 udev_enumerate_unref(udev_enum);
370
371 if (!isatty(fileno(stdout)))
372 setbuf(stdout, NULL);
373
David Sodmanbbcb0522014-09-19 10:34:07 -0700374 if (input.ndevs == 0) {
375 LOG(ERROR, "No valid inputs for terminal");
376 exit(EXIT_SUCCESS);
377 }
378
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700379 return 0;
380}
381
382void input_close()
383{
Dominik Behr93899452014-08-18 22:16:21 -0700384 unsigned int u;
385 for (u = 0; u < input.ndevs; u++) {
386 free(input.devs[u].path);
387 close(input.devs[u].fd);
388 }
389 free(input.devs);
390 input.devs = NULL;
391 input.ndevs = 0;
392
393 udev_monitor_unref(input.udev_monitor);
394 input.udev_monitor = NULL;
395 udev_unref(input.udev);
396 input.udev = NULL;
397 input.udev_fd = -1;
398
David Sodmanbbcb0522014-09-19 10:34:07 -0700399 dbus_destroy(input.dbus);
400
401}
402
403void input_set_dbus(dbus_t* dbus)
404{
405 input.dbus = dbus;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700406}
407
Dominik Behr93899452014-08-18 22:16:21 -0700408int input_setfds(fd_set * read_set, fd_set * exception_set)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700409{
Dominik Behr93899452014-08-18 22:16:21 -0700410 unsigned int u;
411 int max = -1;
412 for (u = 0; u < input.ndevs; u++) {
413 FD_SET(input.devs[u].fd, read_set);
414 FD_SET(input.devs[u].fd, exception_set);
415 if (input.devs[u].fd > max)
416 max = input.devs[u].fd;
417 }
418
419 FD_SET(input.udev_fd, read_set);
420 FD_SET(input.udev_fd, exception_set);
421 if (input.udev_fd > max)
422 max = input.udev_fd;
423 return max;
424}
425
Dominik Behr93899452014-08-18 22:16:21 -0700426struct input_key_event *input_get_event(fd_set * read_set,
427 fd_set * exception_set)
428{
429 unsigned int u;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700430 struct input_event ev;
431 int ret;
432
Dominik Behr93899452014-08-18 22:16:21 -0700433 if (FD_ISSET(input.udev_fd, exception_set)) {
434 /* udev died on us? */
David Sodmanbbcb0522014-09-19 10:34:07 -0700435 LOG(ERROR, "Exception on udev fd");
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700436 }
437
Dominik Behr93899452014-08-18 22:16:21 -0700438 if (FD_ISSET(input.udev_fd, read_set)
439 && !FD_ISSET(input.udev_fd, exception_set)) {
440 /* we got an udev notification */
441 struct udev_device *dev =
442 udev_monitor_receive_device(input.udev_monitor);
443 if (dev) {
444 if (!strcmp("add", udev_device_get_action(dev))) {
445 input_add(udev_device_get_devnode(dev));
446 } else
447 if (!strcmp("remove", udev_device_get_action(dev)))
448 {
449 input_remove(udev_device_get_devnode(dev));
450 }
451 udev_device_unref(dev);
452 }
453 }
454
455 for (u = 0; u < input.ndevs; u++) {
456 if (FD_ISSET(input.devs[u].fd, read_set)
457 && !FD_ISSET(input.devs[u].fd, exception_set)) {
458 ret =
459 read(input.devs[u].fd, &ev, sizeof (struct input_event));
460 if (ret < (int) sizeof (struct input_event)) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700461 LOG(ERROR, "expected %d bytes, got %d",
Dominik Behr93899452014-08-18 22:16:21 -0700462 (int) sizeof (struct input_event), ret);
463 return NULL;
464 }
465
466 if (ev.type == EV_KEY) {
467 struct input_key_event *event =
468 malloc(sizeof (*event));
469 event->code = ev.code;
470 event->value = ev.value;
Dominik Behr93899452014-08-18 22:16:21 -0700471 return event;
472 }
473 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700474 }
475
476 return NULL;
477}
478
David Sodmanbf3f2842014-11-12 08:26:58 -0800479int input_run(bool standalone)
480{
481 fd_set read_set, exception_set;
482 terminal_t* terminal;
483
484 if (standalone) {
485 (void)dbus_method_call0(input.dbus,
486 kLibCrosServiceName,
487 kLibCrosServicePath,
488 kLibCrosServiceInterface,
489 kReleaseDisplayOwnership);
490
Ilja Friedel268188e2014-12-03 18:34:04 +0000491 input.terminals[input.current_terminal] = term_init();
David Sodmanbf3f2842014-11-12 08:26:58 -0800492 terminal = input.terminals[input.current_terminal];
493 if (term_is_valid(terminal)) {
494 input_grab();
495 }
496 }
497
498 while (1) {
499 terminal = input.terminals[input.current_terminal];
500
501 FD_ZERO(&read_set);
502 FD_ZERO(&exception_set);
503 term_add_fd(terminal, &read_set, &exception_set);
504
505 int maxfd = input_setfds(&read_set, &exception_set);
506
507 maxfd = MAX(maxfd, term_fd(terminal)) + 1;
508
509 select(maxfd, &read_set, NULL, &exception_set, NULL);
510
511 if (term_exception(terminal, &exception_set))
512 return -1;
513
514 struct input_key_event *event;
515 event = input_get_event(&read_set, &exception_set);
516 if (event) {
517 if (!input_special_key(event) && event->value) {
518 uint32_t keysym, unicode;
519 if (term_is_active(terminal)) {
David Sodmanada98012014-12-03 10:05:19 -0800520 // Only report user activity when the terminal is active
521 report_user_activity(USER_ACTIVITY_OTHER);
David Sodmanbf3f2842014-11-12 08:26:58 -0800522 input_get_keysym_and_unicode(
523 event, &keysym, &unicode);
524 term_key_event(terminal,
525 keysym, unicode);
526 }
527 }
528
529 input_put_event(event);
530 }
531
532
533 term_dispatch_io(terminal, &read_set);
534
535 if (term_is_valid(terminal)) {
536 if (term_is_child_done(terminal)) {
537 term_close(terminal);
Ilja Friedel268188e2014-12-03 18:34:04 +0000538 input.terminals[input.current_terminal] = term_init();
David Sodmanbf3f2842014-11-12 08:26:58 -0800539 terminal = input.terminals[input.current_terminal];
540 if (!term_is_valid(terminal)) {
541 return -1;
542 }
543 }
544 }
545 }
546
547 return 0;
548}
549
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700550void input_put_event(struct input_key_event *event)
551{
552 free(event);
553}
David Sodmanbbcb0522014-09-19 10:34:07 -0700554
555void input_grab()
556{
557 unsigned int i;
558 for (i = 0; i < input.ndevs; i++) {
559 (void)ioctl(input.devs[i].fd, EVIOCGRAB, (void *) 1);
560 }
561}
562
563void input_ungrab()
564{
565 unsigned int i;
566 for (i = 0; i < input.ndevs; i++) {
567 (void)ioctl(input.devs[i].fd, EVIOCGRAB, (void*) 0);
568 }
569}