blob: 74dd5cfcb898e966dd7118ffbb3fe2ffda25028f [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"
Dominik Behr580462b2014-11-20 13:50:05 -080022#include "main.h"
David Sodmanbf3f2842014-11-12 08:26:58 -080023
Dominik Behr93899452014-08-18 22:16:21 -070024struct input_dev {
25 int fd;
26 char *path;
27};
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070028
David Sodmanbf3f2842014-11-12 08:26:58 -080029struct keyboard_state {
30 int shift_state;
31 int control_state;
32 int alt_state;
David Sodmane0cc8312014-11-18 11:16:36 -080033 int search_state;
David Sodmanbf3f2842014-11-12 08:26:58 -080034};
35
Dominik Behr93899452014-08-18 22:16:21 -070036struct {
37 struct udev *udev;
38 struct udev_monitor *udev_monitor;
39 int udev_fd;
40 unsigned int ndevs;
41 struct input_dev *devs;
David Sodmanbf3f2842014-11-12 08:26:58 -080042 struct keyboard_state kbd_state;
David Sodmanbbcb0522014-09-19 10:34:07 -070043 dbus_t *dbus;
David Sodmanbf3f2842014-11-12 08:26:58 -080044 uint32_t current_terminal;
45 terminal_t *terminals[MAX_TERMINALS];
Dominik Behr93899452014-08-18 22:16:21 -070046} input = {
47 .udev = NULL,
48 .udev_monitor = NULL,
49 .udev_fd = -1,
50 .ndevs = 0,
51 .devs = NULL,
David Sodmanbf3f2842014-11-12 08:26:58 -080052 .dbus = NULL,
53 .current_terminal = 0
Dominik Behr93899452014-08-18 22:16:21 -070054};
55
David Sodmane0cc8312014-11-18 11:16:36 -080056static void report_user_activity(int activity_type)
57{
58 dbus_method_call1(input.dbus, kPowerManagerServiceName,
59 kPowerManagerServicePath,
60 kPowerManagerInterface,
61 kHandleUserActivityMethod,
62 &activity_type);
63
64 switch (activity_type) {
65 case USER_ACTIVITY_BRIGHTNESS_UP_KEY_PRESS:
66 (void)dbus_method_call0(input.dbus,
67 kPowerManagerServiceName,
68 kPowerManagerServicePath,
69 kPowerManagerInterface,
70 kIncreaseScreenBrightnessMethod);
71 break;
72 case USER_ACTIVITY_BRIGHTNESS_DOWN_KEY_PRESS:
73 (void)dbus_method_call0(input.dbus,
74 kPowerManagerServiceName,
75 kPowerManagerServicePath,
76 kPowerManagerInterface,
77 kDecreaseScreenBrightnessMethod);
78 break;
79 }
80}
81
David Sodmanbf3f2842014-11-12 08:26:58 -080082static int input_special_key(struct input_key_event *ev)
83{
84 unsigned int i;
David Sodmanb0697c22014-12-12 10:29:25 -080085 terminal_t *terminal;
David Sodmanbf3f2842014-11-12 08:26:58 -080086
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) {
David Sodmanb0697c22014-12-12 10:29:25 -0800155 if (ev->code == KEY_F1) {
156 terminal = input.terminals[input.current_terminal];
157 if (term_is_active(terminal)) {
David Sodmanbf3f2842014-11-12 08:26:58 -0800158 input_ungrab();
David Sodmanb0697c22014-12-12 10:29:25 -0800159 terminal->active = false;
David Sodman35d6bd82014-11-24 08:23:00 -0800160 video_release(input.terminals[input.current_terminal]->video);
David Sodmanbf3f2842014-11-12 08:26:58 -0800161 (void)dbus_method_call0(input.dbus,
162 kLibCrosServiceName,
163 kLibCrosServicePath,
164 kLibCrosServiceInterface,
165 kTakeDisplayOwnership);
David Sodmanb0697c22014-12-12 10:29:25 -0800166 }
167 } else if ((ev->code >= KEY_F2) && (ev->code < KEY_F2 + MAX_TERMINALS)) {
168 (void)dbus_method_call0(input.dbus,
169 kLibCrosServiceName,
170 kLibCrosServicePath,
171 kLibCrosServiceInterface,
172 kReleaseDisplayOwnership);
173 terminal = input.terminals[input.current_terminal];
David Sodmanbf3f2842014-11-12 08:26:58 -0800174 if (term_is_active(terminal))
175 terminal->active = false;
176 input.current_terminal = ev->code - KEY_F2;
177 terminal = input.terminals[input.current_terminal];
178 if (terminal == NULL) {
179 input.terminals[input.current_terminal] =
Dominik Behr580462b2014-11-20 13:50:05 -0800180 term_init(input.current_terminal);
David Sodmanbf3f2842014-11-12 08:26:58 -0800181 terminal =
182 input.terminals[input.current_terminal];
183 if (!term_is_valid(terminal)) {
184 LOG(ERROR, "Term init failed");
185 return 1;
186 }
187 }
188 input.terminals[input.current_terminal]->active = true;
189 input_grab();
190 video_setmode(terminal->video);
191 term_redraw(terminal);
192 }
193
194 return 1;
195
196 }
197
David Sodmane0cc8312014-11-18 11:16:36 -0800198 if ((!input.kbd_state.search_state) && ev->value &&
199 (ev->code >= KEY_F1) && (ev->code <= KEY_F10)) {
200 switch (ev->code) {
201 case KEY_F1:
202 case KEY_F2:
203 case KEY_F3:
204 case KEY_F4:
205 case KEY_F5:
206 break;
207 case KEY_F6:
208 case KEY_F7:
209 report_user_activity(USER_ACTIVITY_BRIGHTNESS_DOWN_KEY_PRESS -
210 (ev->code - KEY_F6));
211 break;
212 case KEY_F8:
213 case KEY_F9:
214 case KEY_F10:
215 break;
216 }
217 return 1;
218 }
219
David Sodmanbf3f2842014-11-12 08:26:58 -0800220 return 0;
221}
222
223static void input_get_keysym_and_unicode(struct input_key_event *event,
224 uint32_t *keysym, uint32_t *unicode)
225{
226 struct {
227 uint32_t code;
228 uint32_t keysym;
229 } non_ascii_keys[] = {
230 { KEY_ESC, KEYSYM_ESC},
231 { KEY_HOME, KEYSYM_HOME},
232 { KEY_LEFT, KEYSYM_LEFT},
233 { KEY_UP, KEYSYM_UP},
234 { KEY_RIGHT, KEYSYM_RIGHT},
235 { KEY_DOWN, KEYSYM_DOWN},
236 { KEY_PAGEUP, KEYSYM_PAGEUP},
237 { KEY_PAGEDOWN, KEYSYM_PAGEDOWN},
238 { KEY_END, KEYSYM_END},
239 { KEY_INSERT, KEYSYM_INSERT},
240 { KEY_DELETE, KEYSYM_DELETE},
241 };
242
243 for (unsigned i = 0; i < ARRAY_SIZE(non_ascii_keys); i++) {
244 if (non_ascii_keys[i].code == event->code) {
245 *keysym = non_ascii_keys[i].keysym;
246 *unicode = -1;
247 return;
248 }
249 }
250
251 if (event->code >= ARRAY_SIZE(keysym_table) / 2) {
252 *keysym = '?';
253 } else {
254 *keysym = keysym_table[event->code * 2 + input.kbd_state.shift_state];
255 if ((input.kbd_state.control_state) && isascii(*keysym))
256 *keysym = tolower(*keysym) - 'a' + 1;
257 }
258
259 *unicode = *keysym;
260}
261
Dominik Behr93899452014-08-18 22:16:21 -0700262static int input_add(const char *devname)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700263{
Dominik Behr93899452014-08-18 22:16:21 -0700264 int ret = 0, fd = -1;
265 /* for some reason every device has a null enumerations and notifications
266 of every device come with NULL string first */
267 if (!devname) {
268 ret = -EINVAL;
269 goto errorret;
270 }
271 ret = fd = open(devname, O_RDONLY);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700272 if (fd < 0)
Dominik Behr93899452014-08-18 22:16:21 -0700273 goto errorret;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700274
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700275 ret = ioctl(fd, EVIOCGRAB, (void *) 1);
276
277 if (!ret) {
278 ioctl(fd, EVIOCGRAB, (void *) 0);
279 } else {
David Sodmanbbcb0522014-09-19 10:34:07 -0700280 LOG(ERROR, "Evdev device %s grabbed by another process",
Dominik Behr93899452014-08-18 22:16:21 -0700281 devname);
282 ret = -EBUSY;
283 goto closefd;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700284 }
285
Dominik Behr93899452014-08-18 22:16:21 -0700286 struct input_dev *newdevs =
287 realloc(input.devs, (input.ndevs + 1) * sizeof (struct input_dev));
288 if (!newdevs) {
289 ret = -ENOMEM;
290 goto closefd;
291 }
292 input.devs = newdevs;
293 input.devs[input.ndevs].fd = fd;
294 input.devs[input.ndevs].path = strdup(devname);
295 if (!input.devs[input.ndevs].path) {
296 ret = -ENOMEM;
297 goto closefd;
298 }
299 input.ndevs++;
300
301 return fd;
302
303closefd:
304 close(fd);
305errorret:
306 return ret;
307}
308
309static void input_remove(const char *devname)
310{
311 if (!devname) {
312 return;
313 }
314 unsigned int u;
315 for (u = 0; u < input.ndevs; u++) {
316 if (!strcmp(devname, input.devs[u].path)) {
317 free(input.devs[u].path);
318 close(input.devs[u].fd);
319 input.ndevs--;
320 if (u != input.ndevs) {
321 input.devs[u] = input.devs[input.ndevs];
322 }
323 return;
324 }
325 }
326}
327
Dominik Behr93899452014-08-18 22:16:21 -0700328
329int input_init()
330{
331 input.udev = udev_new();
332 if (!input.udev)
333 return -ENOENT;
334 input.udev_monitor = udev_monitor_new_from_netlink(input.udev, "udev");
335 if (!input.udev_monitor) {
336 udev_unref(input.udev);
337 return -ENOENT;
338 }
339 udev_monitor_filter_add_match_subsystem_devtype(input.udev_monitor, "input",
340 NULL);
341 udev_monitor_enable_receiving(input.udev_monitor);
342 input.udev_fd = udev_monitor_get_fd(input.udev_monitor);
343
344 struct udev_enumerate *udev_enum;
345 struct udev_list_entry *devices, *deventry;
346 udev_enum = udev_enumerate_new(input.udev);
347 udev_enumerate_add_match_subsystem(udev_enum, "input");
348 udev_enumerate_scan_devices(udev_enum);
349 devices = udev_enumerate_get_list_entry(udev_enum);
350 udev_list_entry_foreach(deventry, devices) {
351 const char *syspath;
352 struct udev_device *dev;
353 syspath = udev_list_entry_get_name(deventry);
354 dev = udev_device_new_from_syspath(input.udev, syspath);
355 input_add(udev_device_get_devnode(dev));
356 udev_device_unref(dev);
357 }
358 udev_enumerate_unref(udev_enum);
359
360 if (!isatty(fileno(stdout)))
361 setbuf(stdout, NULL);
362
David Sodmanbbcb0522014-09-19 10:34:07 -0700363 if (input.ndevs == 0) {
364 LOG(ERROR, "No valid inputs for terminal");
365 exit(EXIT_SUCCESS);
366 }
367
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700368 return 0;
369}
370
371void input_close()
372{
Dominik Behr93899452014-08-18 22:16:21 -0700373 unsigned int u;
374 for (u = 0; u < input.ndevs; u++) {
375 free(input.devs[u].path);
376 close(input.devs[u].fd);
377 }
378 free(input.devs);
379 input.devs = NULL;
380 input.ndevs = 0;
381
382 udev_monitor_unref(input.udev_monitor);
383 input.udev_monitor = NULL;
384 udev_unref(input.udev);
385 input.udev = NULL;
386 input.udev_fd = -1;
387
David Sodmanbbcb0522014-09-19 10:34:07 -0700388 dbus_destroy(input.dbus);
389
390}
391
392void input_set_dbus(dbus_t* dbus)
393{
394 input.dbus = dbus;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700395}
396
Dominik Behr93899452014-08-18 22:16:21 -0700397int input_setfds(fd_set * read_set, fd_set * exception_set)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700398{
Dominik Behr93899452014-08-18 22:16:21 -0700399 unsigned int u;
400 int max = -1;
401 for (u = 0; u < input.ndevs; u++) {
402 FD_SET(input.devs[u].fd, read_set);
403 FD_SET(input.devs[u].fd, exception_set);
404 if (input.devs[u].fd > max)
405 max = input.devs[u].fd;
406 }
407
408 FD_SET(input.udev_fd, read_set);
409 FD_SET(input.udev_fd, exception_set);
410 if (input.udev_fd > max)
411 max = input.udev_fd;
412 return max;
413}
414
Dominik Behr93899452014-08-18 22:16:21 -0700415struct input_key_event *input_get_event(fd_set * read_set,
416 fd_set * exception_set)
417{
418 unsigned int u;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700419 struct input_event ev;
420 int ret;
421
Dominik Behr93899452014-08-18 22:16:21 -0700422 if (FD_ISSET(input.udev_fd, exception_set)) {
423 /* udev died on us? */
David Sodmanbbcb0522014-09-19 10:34:07 -0700424 LOG(ERROR, "Exception on udev fd");
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700425 }
426
Dominik Behr93899452014-08-18 22:16:21 -0700427 if (FD_ISSET(input.udev_fd, read_set)
428 && !FD_ISSET(input.udev_fd, exception_set)) {
429 /* we got an udev notification */
430 struct udev_device *dev =
431 udev_monitor_receive_device(input.udev_monitor);
432 if (dev) {
433 if (!strcmp("add", udev_device_get_action(dev))) {
434 input_add(udev_device_get_devnode(dev));
435 } else
436 if (!strcmp("remove", udev_device_get_action(dev)))
437 {
438 input_remove(udev_device_get_devnode(dev));
439 }
440 udev_device_unref(dev);
441 }
442 }
443
444 for (u = 0; u < input.ndevs; u++) {
445 if (FD_ISSET(input.devs[u].fd, read_set)
446 && !FD_ISSET(input.devs[u].fd, exception_set)) {
447 ret =
448 read(input.devs[u].fd, &ev, sizeof (struct input_event));
449 if (ret < (int) sizeof (struct input_event)) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700450 LOG(ERROR, "expected %d bytes, got %d",
Dominik Behr93899452014-08-18 22:16:21 -0700451 (int) sizeof (struct input_event), ret);
452 return NULL;
453 }
454
455 if (ev.type == EV_KEY) {
456 struct input_key_event *event =
457 malloc(sizeof (*event));
458 event->code = ev.code;
459 event->value = ev.value;
Dominik Behr93899452014-08-18 22:16:21 -0700460 return event;
461 }
462 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700463 }
464
465 return NULL;
466}
467
David Sodmanbf3f2842014-11-12 08:26:58 -0800468int input_run(bool standalone)
469{
470 fd_set read_set, exception_set;
471 terminal_t* terminal;
472
473 if (standalone) {
474 (void)dbus_method_call0(input.dbus,
475 kLibCrosServiceName,
476 kLibCrosServicePath,
477 kLibCrosServiceInterface,
478 kReleaseDisplayOwnership);
479
Dominik Behr580462b2014-11-20 13:50:05 -0800480 input.terminals[input.current_terminal] = term_init(input.current_terminal);
David Sodmanbf3f2842014-11-12 08:26:58 -0800481 terminal = input.terminals[input.current_terminal];
482 if (term_is_valid(terminal)) {
483 input_grab();
484 }
485 }
486
487 while (1) {
488 terminal = input.terminals[input.current_terminal];
489
490 FD_ZERO(&read_set);
491 FD_ZERO(&exception_set);
492 term_add_fd(terminal, &read_set, &exception_set);
493
494 int maxfd = input_setfds(&read_set, &exception_set);
495
496 maxfd = MAX(maxfd, term_fd(terminal)) + 1;
497
498 select(maxfd, &read_set, NULL, &exception_set, NULL);
499
500 if (term_exception(terminal, &exception_set))
501 return -1;
502
503 struct input_key_event *event;
504 event = input_get_event(&read_set, &exception_set);
505 if (event) {
506 if (!input_special_key(event) && event->value) {
507 uint32_t keysym, unicode;
508 if (term_is_active(terminal)) {
David Sodmanada98012014-12-03 10:05:19 -0800509 // Only report user activity when the terminal is active
510 report_user_activity(USER_ACTIVITY_OTHER);
David Sodmanbf3f2842014-11-12 08:26:58 -0800511 input_get_keysym_and_unicode(
512 event, &keysym, &unicode);
513 term_key_event(terminal,
514 keysym, unicode);
515 }
516 }
517
518 input_put_event(event);
519 }
520
521
522 term_dispatch_io(terminal, &read_set);
523
524 if (term_is_valid(terminal)) {
525 if (term_is_child_done(terminal)) {
David Sodman5e5c37e2014-12-19 14:14:46 -0800526 if (terminal->video) {
527 // necessary in case chrome is playing full screen
528 // video or graphics
529 //TODO: This is still a race with Chrome. This
530 //needs to be fixed with bug 444209
531 video_setmode(terminal->video);
532 }
David Sodmanbf3f2842014-11-12 08:26:58 -0800533 term_close(terminal);
Dominik Behr580462b2014-11-20 13:50:05 -0800534 input.terminals[input.current_terminal] = term_init(input.current_terminal);
David Sodmanbf3f2842014-11-12 08:26:58 -0800535 terminal = input.terminals[input.current_terminal];
536 if (!term_is_valid(terminal)) {
537 return -1;
538 }
539 }
540 }
541 }
542
543 return 0;
544}
545
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700546void input_put_event(struct input_key_event *event)
547{
548 free(event);
549}
David Sodmanbbcb0522014-09-19 10:34:07 -0700550
551void input_grab()
552{
553 unsigned int i;
554 for (i = 0; i < input.ndevs; i++) {
555 (void)ioctl(input.devs[i].fd, EVIOCGRAB, (void *) 1);
556 }
557}
558
559void input_ungrab()
560{
561 unsigned int i;
562 for (i = 0; i < input.ndevs; i++) {
563 (void)ioctl(input.devs[i].fd, EVIOCGRAB, (void*) 0);
564 }
565}