blob: f0f36d755cd7fbd0bd738b5bb1973905bdf7daef [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>
David Sodman8ef20062015-01-06 09:23:40 -08008#include <errno.h>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -07009#include <fcntl.h>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070010#include <stdint.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
Dominik Behr93899452014-08-18 22:16:21 -070014#include <sys/select.h>
David Sodman8ef20062015-01-06 09:23:40 -080015#include <unistd.h>
16
David Sodmanbbcb0522014-09-19 10:34:07 -070017#include "dbus.h"
Stéphane Marchesin6cbb7332016-01-07 21:18:43 -080018#include "dbus_interface.h"
Stéphane Marchesin62561a12015-12-11 17:32:37 -080019#include "input.h"
David Sodmanbf3f2842014-11-12 08:26:58 -080020#include "keysym.h"
Dominik Behr46c567f2016-03-08 15:11:48 -080021#include "main.h"
David Sodmanbbcb0522014-09-19 10:34:07 -070022#include "util.h"
David Sodman8ef20062015-01-06 09:23:40 -080023
Dominik Behr44e07e62016-01-13 19:43:57 -080024struct input_key_event {
25 uint16_t code;
26 unsigned char value;
27};
28
Dominik Behr93899452014-08-18 22:16:21 -070029struct input_dev {
30 int fd;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080031 char* path;
Dominik Behr93899452014-08-18 22:16:21 -070032};
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070033
David Sodmanbf3f2842014-11-12 08:26:58 -080034struct keyboard_state {
Dominik Behr92a05072016-03-23 20:04:43 -070035 bool left_shift_state;
36 bool right_shift_state;
37 bool left_control_state;
38 bool right_control_state;
39 bool left_alt_state;
40 bool right_alt_state;
41 bool search_state;
David Sodmanbf3f2842014-11-12 08:26:58 -080042};
43
Dylan Reid342aed02015-06-18 12:15:52 -070044/*
45 * structure to keep input state:
Dylan Reid342aed02015-06-18 12:15:52 -070046 * ndevs - number of input devices.
47 * devs - input devices to listen to.
48 * kbd_state - tracks modifier keys that are pressed.
Dylan Reid342aed02015-06-18 12:15:52 -070049 */
Dominik Behr93899452014-08-18 22:16:21 -070050struct {
Dominik Behr93899452014-08-18 22:16:21 -070051 unsigned int ndevs;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080052 struct input_dev* devs;
David Sodmanbf3f2842014-11-12 08:26:58 -080053 struct keyboard_state kbd_state;
Dominik Behr93899452014-08-18 22:16:21 -070054} input = {
Dominik Behr93899452014-08-18 22:16:21 -070055 .ndevs = 0,
56 .devs = NULL,
Dominik Behr93899452014-08-18 22:16:21 -070057};
58
Dominik Behr92a05072016-03-23 20:04:43 -070059static bool is_shift_pressed(struct keyboard_state* k)
60{
61 return k->left_shift_state || k->right_shift_state;
62}
63
64static bool is_control_pressed(struct keyboard_state* k)
65{
66 return k->left_control_state || k->right_control_state;
67}
68
69static bool is_alt_pressed(struct keyboard_state* k)
70{
71 return k->left_alt_state || k->right_alt_state;
72}
73
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080074static int input_special_key(struct input_key_event* ev)
David Sodmanbf3f2842014-11-12 08:26:58 -080075{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080076 terminal_t* terminal;
David Sodmanbf3f2842014-11-12 08:26:58 -080077
78 uint32_t ignore_keys[] = {
79 BTN_TOUCH, // touchpad events
80 BTN_TOOL_FINGER,
81 BTN_TOOL_DOUBLETAP,
82 BTN_TOOL_TRIPLETAP,
83 BTN_TOOL_QUADTAP,
84 BTN_TOOL_QUINTTAP,
85 BTN_LEFT, // mouse buttons
86 BTN_RIGHT,
87 BTN_MIDDLE,
88 BTN_SIDE,
89 BTN_EXTRA,
90 BTN_FORWARD,
91 BTN_BACK,
92 BTN_TASK
93 };
94
Dominik Behr4defb362016-01-13 12:36:14 -080095 terminal = term_get_current_terminal();
David Sodmanafba0d92015-01-27 19:07:46 -080096
Stéphane Marchesinac14d292015-12-14 15:27:18 -080097 for (unsigned int i = 0; i < ARRAY_SIZE(ignore_keys); i++)
David Sodmanbf3f2842014-11-12 08:26:58 -080098 if (ev->code == ignore_keys[i])
99 return 1;
100
101 switch (ev->code) {
102 case KEY_LEFTSHIFT:
Dominik Behr92a05072016-03-23 20:04:43 -0700103 input.kbd_state.left_shift_state = ! !ev->value;
104 return 1;
David Sodmanbf3f2842014-11-12 08:26:58 -0800105 case KEY_RIGHTSHIFT:
Dominik Behr92a05072016-03-23 20:04:43 -0700106 input.kbd_state.right_shift_state = ! !ev->value;
David Sodmanbf3f2842014-11-12 08:26:58 -0800107 return 1;
108 case KEY_LEFTCTRL:
Dominik Behr92a05072016-03-23 20:04:43 -0700109 input.kbd_state.left_control_state = ! !ev->value;
110 return 1;
David Sodmanbf3f2842014-11-12 08:26:58 -0800111 case KEY_RIGHTCTRL:
Dominik Behr92a05072016-03-23 20:04:43 -0700112 input.kbd_state.right_control_state = ! !ev->value;
David Sodmanbf3f2842014-11-12 08:26:58 -0800113 return 1;
114 case KEY_LEFTALT:
Dominik Behr92a05072016-03-23 20:04:43 -0700115 input.kbd_state.left_alt_state = ! !ev->value;
116 return 1;
David Sodmanbf3f2842014-11-12 08:26:58 -0800117 case KEY_RIGHTALT:
Dominik Behr92a05072016-03-23 20:04:43 -0700118 input.kbd_state.right_alt_state = ! !ev->value;
David Sodmanbf3f2842014-11-12 08:26:58 -0800119 return 1;
David Sodmane0cc8312014-11-18 11:16:36 -0800120 case KEY_LEFTMETA: // search key
121 input.kbd_state.search_state = ! !ev->value;
122 return 1;
David Sodmanbf3f2842014-11-12 08:26:58 -0800123 }
124
David Sodmanafba0d92015-01-27 19:07:46 -0800125 if (term_is_active(terminal)) {
Dominik Behr92a05072016-03-23 20:04:43 -0700126 if (is_shift_pressed(&input.kbd_state) && ev->value) {
David Sodmanafba0d92015-01-27 19:07:46 -0800127 switch (ev->code) {
128 case KEY_PAGEUP:
Stéphane Marchesin08c08e72016-01-07 16:44:08 -0800129 term_page_up(terminal);
David Sodmane0cc8312014-11-18 11:16:36 -0800130 return 1;
David Sodmanafba0d92015-01-27 19:07:46 -0800131 case KEY_PAGEDOWN:
Stéphane Marchesin08c08e72016-01-07 16:44:08 -0800132 term_page_down(terminal);
David Sodmane0cc8312014-11-18 11:16:36 -0800133 return 1;
David Sodmanafba0d92015-01-27 19:07:46 -0800134 case KEY_UP:
Dominik Behr58bb8e12015-09-22 14:30:41 -0700135 if (input.kbd_state.search_state)
Stéphane Marchesin08c08e72016-01-07 16:44:08 -0800136 term_page_up(terminal);
Dominik Behr58bb8e12015-09-22 14:30:41 -0700137 else
Stéphane Marchesin08c08e72016-01-07 16:44:08 -0800138 term_line_up(terminal);
David Sodmanafba0d92015-01-27 19:07:46 -0800139 return 1;
140 case KEY_DOWN:
Dominik Behr58bb8e12015-09-22 14:30:41 -0700141 if (input.kbd_state.search_state)
Stéphane Marchesin08c08e72016-01-07 16:44:08 -0800142 term_page_down(terminal);
Dominik Behr58bb8e12015-09-22 14:30:41 -0700143 else
Stéphane Marchesin08c08e72016-01-07 16:44:08 -0800144 term_line_down(terminal);
Dominik Behr58bb8e12015-09-22 14:30:41 -0700145 return 1;
David Sodmanafba0d92015-01-27 19:07:46 -0800146 }
147 }
148
Haixia Shi95285872016-11-08 15:26:35 -0800149 if (!is_alt_pressed(&input.kbd_state) &&
150 is_control_pressed(&input.kbd_state) &&
151 is_shift_pressed(&input.kbd_state) && ev->value) {
152 switch (ev->code) {
153 case KEY_MINUS:
154 term_zoom(false);
155 return 1;
156 case KEY_EQUAL:
157 term_zoom(true);
158 return 1;
159 }
160 }
161
Dominik Behr92a05072016-03-23 20:04:43 -0700162 if (!(input.kbd_state.search_state ||
163 is_alt_pressed(&input.kbd_state) ||
164 is_control_pressed(&input.kbd_state)) &&
165 ev->value && (ev->code >= KEY_F1) && (ev->code <= KEY_F10)) {
David Sodmanafba0d92015-01-27 19:07:46 -0800166 switch (ev->code) {
167 case KEY_F1:
168 case KEY_F2:
169 case KEY_F3:
170 case KEY_F4:
171 case KEY_F5:
172 break;
173 case KEY_F6:
174 case KEY_F7:
Dominik Behr797a3832016-01-11 15:53:11 -0800175 dbus_report_user_activity(USER_ACTIVITY_BRIGHTNESS_DOWN_KEY_PRESS -
176 (ev->code - KEY_F6));
David Sodmanafba0d92015-01-27 19:07:46 -0800177 break;
178 case KEY_F8:
179 case KEY_F9:
180 case KEY_F10:
181 break;
182 }
183 return 1;
David Sodmane0cc8312014-11-18 11:16:36 -0800184 }
185 }
186
Dominik Behr46c567f2016-03-08 15:11:48 -0800187 if (command_flags.enable_vts &&
Dominik Behr92a05072016-03-23 20:04:43 -0700188 is_alt_pressed(&input.kbd_state) &&
189 is_control_pressed(&input.kbd_state) &&
190 ev->value) {
David Sodman1d1c67f2015-03-12 11:01:08 -0700191 /*
192 * Special case for key sequence that is used by external program. Just
193 * explicitly ignore here and do nothing.
194 */
Dominik Behr92a05072016-03-23 20:04:43 -0700195 if (is_shift_pressed(&input.kbd_state))
David Sodman1d1c67f2015-03-12 11:01:08 -0700196 return 1;
197
Dominik Behrda6df412016-08-02 12:56:42 -0700198 if ((ev->code >= KEY_F1) && (ev->code < KEY_F1 + term_num_terminals)) {
199 term_switch_to(ev->code - KEY_F1);
David Sodmanbf3f2842014-11-12 08:26:58 -0800200 }
201
202 return 1;
David Sodmanbf3f2842014-11-12 08:26:58 -0800203 }
204
205 return 0;
206}
207
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800208static void input_get_keysym_and_unicode(struct input_key_event* event,
209 uint32_t* keysym, uint32_t* unicode)
David Sodmanbf3f2842014-11-12 08:26:58 -0800210{
211 struct {
212 uint32_t code;
213 uint32_t keysym;
Dominik Behr58bb8e12015-09-22 14:30:41 -0700214 } search_keys[] = {
215 { KEY_F1, KEYSYM_F1},
216 { KEY_F2, KEYSYM_F2},
217 { KEY_F3, KEYSYM_F3},
218 { KEY_F4, KEYSYM_F4},
219 { KEY_F5, KEYSYM_F5},
220 { KEY_F6, KEYSYM_F6},
221 { KEY_F7, KEYSYM_F7},
222 { KEY_F8, KEYSYM_F8},
223 { KEY_F9, KEYSYM_F8},
224 { KEY_F10, KEYSYM_F10},
225 { KEY_UP, KEYSYM_PAGEUP},
226 { KEY_DOWN, KEYSYM_PAGEDOWN},
227 { KEY_LEFT, KEYSYM_HOME},
228 { KEY_RIGHT, KEYSYM_END},
229 };
230
231 struct {
232 uint32_t code;
233 uint32_t keysym;
David Sodmanbf3f2842014-11-12 08:26:58 -0800234 } non_ascii_keys[] = {
235 { KEY_ESC, KEYSYM_ESC},
236 { KEY_HOME, KEYSYM_HOME},
237 { KEY_LEFT, KEYSYM_LEFT},
238 { KEY_UP, KEYSYM_UP},
239 { KEY_RIGHT, KEYSYM_RIGHT},
240 { KEY_DOWN, KEYSYM_DOWN},
241 { KEY_PAGEUP, KEYSYM_PAGEUP},
242 { KEY_PAGEDOWN, KEYSYM_PAGEDOWN},
243 { KEY_END, KEYSYM_END},
244 { KEY_INSERT, KEYSYM_INSERT},
245 { KEY_DELETE, KEYSYM_DELETE},
246 };
247
Dominik Behr58bb8e12015-09-22 14:30:41 -0700248 if (input.kbd_state.search_state) {
249 for (unsigned i = 0; i < ARRAY_SIZE(search_keys); i++) {
250 if (search_keys[i].code == event->code) {
251 *keysym = search_keys[i].keysym;
252 *unicode = -1;
253 return;
254 }
255 }
256 }
257
David Sodmanbf3f2842014-11-12 08:26:58 -0800258 for (unsigned i = 0; i < ARRAY_SIZE(non_ascii_keys); i++) {
259 if (non_ascii_keys[i].code == event->code) {
260 *keysym = non_ascii_keys[i].keysym;
261 *unicode = -1;
262 return;
263 }
264 }
265
266 if (event->code >= ARRAY_SIZE(keysym_table) / 2) {
267 *keysym = '?';
268 } else {
Dominik Behr92a05072016-03-23 20:04:43 -0700269 *keysym = keysym_table[event->code * 2 + is_shift_pressed(&input.kbd_state)];
270 if (is_control_pressed(&input.kbd_state) && isascii(*keysym))
David Sodmanbf3f2842014-11-12 08:26:58 -0800271 *keysym = tolower(*keysym) - 'a' + 1;
272 }
273
274 *unicode = *keysym;
275}
276
Dominik Behr5239cca2016-01-21 18:22:04 -0800277int input_add(const char* devname)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700278{
Dominik Behr93899452014-08-18 22:16:21 -0700279 int ret = 0, fd = -1;
Stéphane Marchesinedece332015-12-14 15:10:58 -0800280
Dominik Behr93899452014-08-18 22:16:21 -0700281 /* for some reason every device has a null enumerations and notifications
282 of every device come with NULL string first */
283 if (!devname) {
284 ret = -EINVAL;
285 goto errorret;
286 }
Haixia Shid288e032015-09-14 18:33:11 -0700287 /* check for duplicates */
Stéphane Marchesinac14d292015-12-14 15:27:18 -0800288 for (unsigned int i = 0; i < input.ndevs; ++i) {
Haixia Shid288e032015-09-14 18:33:11 -0700289 if (strcmp(devname, input.devs[i].path) == 0) {
Stéphane Marchesinc02260b2016-01-07 22:46:45 -0800290 LOG(INFO, "Skipping duplicate input device %s", devname);
Haixia Shid288e032015-09-14 18:33:11 -0700291 ret = -EINVAL;
292 goto errorret;
293 }
294 }
Dominik Behr93899452014-08-18 22:16:21 -0700295 ret = fd = open(devname, O_RDONLY);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700296 if (fd < 0)
Dominik Behr93899452014-08-18 22:16:21 -0700297 goto errorret;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700298
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800299 ret = ioctl(fd, EVIOCGRAB, (void*) 1);
David Sodmaneef3fd22015-08-20 15:08:52 -0700300 if (!ret) {
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800301 ret = ioctl(fd, EVIOCGRAB, (void*) 0);
David Sodmaneef3fd22015-08-20 15:08:52 -0700302 if (ret)
303 LOG(ERROR,
304 "EVIOCGRAB succeeded but the corresponding ungrab failed: %m");
305 } else {
306 LOG(ERROR, "Evdev device %s grabbed by another process",
307 devname);
308 ret = -EBUSY;
309 goto closefd;
310 }
311
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800312 struct input_dev* newdevs =
Dominik Behr93899452014-08-18 22:16:21 -0700313 realloc(input.devs, (input.ndevs + 1) * sizeof (struct input_dev));
314 if (!newdevs) {
315 ret = -ENOMEM;
316 goto closefd;
317 }
318 input.devs = newdevs;
319 input.devs[input.ndevs].fd = fd;
320 input.devs[input.ndevs].path = strdup(devname);
321 if (!input.devs[input.ndevs].path) {
322 ret = -ENOMEM;
323 goto closefd;
324 }
325 input.ndevs++;
326
327 return fd;
328
329closefd:
330 close(fd);
331errorret:
332 return ret;
333}
334
Dominik Behr5239cca2016-01-21 18:22:04 -0800335void input_remove(const char* devname)
Dominik Behr93899452014-08-18 22:16:21 -0700336{
Dominik Behr93899452014-08-18 22:16:21 -0700337 unsigned int u;
Stéphane Marchesinedece332015-12-14 15:10:58 -0800338
339 if (!devname)
340 return;
341
Dominik Behr93899452014-08-18 22:16:21 -0700342 for (u = 0; u < input.ndevs; u++) {
343 if (!strcmp(devname, input.devs[u].path)) {
344 free(input.devs[u].path);
345 close(input.devs[u].fd);
346 input.ndevs--;
347 if (u != input.ndevs) {
348 input.devs[u] = input.devs[input.ndevs];
349 }
350 return;
351 }
352 }
353}
354
Dominik Behr93899452014-08-18 22:16:21 -0700355int input_init()
356{
Dominik Behr93899452014-08-18 22:16:21 -0700357 if (!isatty(fileno(stdout)))
358 setbuf(stdout, NULL);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700359 return 0;
360}
361
362void input_close()
363{
Dominik Behr93899452014-08-18 22:16:21 -0700364 unsigned int u;
Stéphane Marchesinedece332015-12-14 15:10:58 -0800365
Dominik Behr93899452014-08-18 22:16:21 -0700366 for (u = 0; u < input.ndevs; u++) {
367 free(input.devs[u].path);
368 close(input.devs[u].fd);
369 }
370 free(input.devs);
371 input.devs = NULL;
372 input.ndevs = 0;
David Sodmanbbcb0522014-09-19 10:34:07 -0700373}
374
Dominik Behrd7112672016-01-20 16:59:34 -0800375void input_add_fds(fd_set* read_set, fd_set* exception_set, int *maxfd)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700376{
Dominik Behr93899452014-08-18 22:16:21 -0700377 unsigned int u;
Stéphane Marchesinedece332015-12-14 15:10:58 -0800378
Dominik Behr93899452014-08-18 22:16:21 -0700379 for (u = 0; u < input.ndevs; u++) {
380 FD_SET(input.devs[u].fd, read_set);
381 FD_SET(input.devs[u].fd, exception_set);
Dominik Behrd7112672016-01-20 16:59:34 -0800382 if (input.devs[u].fd > *maxfd)
383 *maxfd = input.devs[u].fd;
Dominik Behr93899452014-08-18 22:16:21 -0700384 }
Dominik Behr93899452014-08-18 22:16:21 -0700385}
386
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800387struct input_key_event* input_get_event(fd_set* read_set,
388 fd_set* exception_set)
Dominik Behr93899452014-08-18 22:16:21 -0700389{
390 unsigned int u;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700391 struct input_event ev;
392 int ret;
393
Dominik Behr93899452014-08-18 22:16:21 -0700394 for (u = 0; u < input.ndevs; u++) {
395 if (FD_ISSET(input.devs[u].fd, read_set)
396 && !FD_ISSET(input.devs[u].fd, exception_set)) {
397 ret =
398 read(input.devs[u].fd, &ev, sizeof (struct input_event));
Michael Spang24d20122015-04-22 13:58:16 -0400399 if (ret < 0) {
400 if (errno == EINTR || errno == EAGAIN)
401 continue;
402 if (errno != ENODEV) {
403 LOG(ERROR, "read: %s: %s", input.devs[u].path,
404 strerror(errno));
405 }
406 input_remove(input.devs[u].path);
407 return NULL;
408 } else if (ret < (int) sizeof (struct input_event)) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700409 LOG(ERROR, "expected %d bytes, got %d",
Dominik Behr93899452014-08-18 22:16:21 -0700410 (int) sizeof (struct input_event), ret);
411 return NULL;
412 }
413
414 if (ev.type == EV_KEY) {
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800415 struct input_key_event* event =
Dominik Behr93899452014-08-18 22:16:21 -0700416 malloc(sizeof (*event));
417 event->code = ev.code;
418 event->value = ev.value;
Dominik Behr93899452014-08-18 22:16:21 -0700419 return event;
Dominik Behrd2cc4d22016-01-29 17:31:52 -0800420 } else if (ev.type == EV_SW && ev.code == SW_LID) {
421 /* TODO(dbehr), abstract this in input_key_event if we ever parse more than one */
422 term_monitor_hotplug();
Dominik Behr93899452014-08-18 22:16:21 -0700423 }
424 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700425 }
426
427 return NULL;
428}
429
Dominik Behr4defb362016-01-13 12:36:14 -0800430void input_put_event(struct input_key_event* event)
431{
432 free(event);
433}
434
Dominik Behr44e07e62016-01-13 19:43:57 -0800435void input_dispatch_io(fd_set* read_set, fd_set* exception_set)
David Sodmanf0a925a2015-05-04 11:19:19 -0700436{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800437 terminal_t* terminal;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800438 struct input_key_event* event;
Dominik Behr44e07e62016-01-13 19:43:57 -0800439
440 event = input_get_event(read_set, exception_set);
David Sodmanf0a925a2015-05-04 11:19:19 -0700441 if (event) {
442 if (!input_special_key(event) && event->value) {
443 uint32_t keysym, unicode;
444 // current_terminal can possibly change during
445 // execution of input_special_key
Dominik Behr4defb362016-01-13 12:36:14 -0800446 terminal = term_get_current_terminal();
David Sodmanf0a925a2015-05-04 11:19:19 -0700447 if (term_is_active(terminal)) {
448 // Only report user activity when the terminal is active
Dominik Behr797a3832016-01-11 15:53:11 -0800449 dbus_report_user_activity(USER_ACTIVITY_OTHER);
David Sodmanf0a925a2015-05-04 11:19:19 -0700450 input_get_keysym_and_unicode(
451 event, &keysym, &unicode);
452 term_key_event(terminal,
453 keysym, unicode);
454 }
455 }
456 input_put_event(event);
457 }
David Sodmanbf3f2842014-11-12 08:26:58 -0800458}
Dominik Behrd2cc4d22016-01-29 17:31:52 -0800459
460#define BITS_PER_LONG (sizeof(long) * 8)
461#define BITS_TO_LONGS(bits) (((bits) - 1) / BITS_PER_LONG + 1)
462#define BITMASK_GET_BIT(bitmask, bit) \
463 ((bitmask[bit / BITS_PER_LONG] >> (bit % BITS_PER_LONG)) & 1)
464
465static const int kMaxBit = MAX(MAX(EV_MAX, KEY_MAX), SW_MAX);
466
467static bool has_event_bit(int fd, int event_type, int bit)
468{
469 unsigned long bitmask[BITS_TO_LONGS(kMaxBit+1)];
470 memset(bitmask, 0, sizeof(bitmask));
471
472 if (ioctl(fd, EVIOCGBIT(event_type, sizeof(bitmask)), bitmask) < 0)
473 return false;
474
475 return BITMASK_GET_BIT(bitmask, bit);
476}
477
478static int get_switch_bit(int fd, int bit) {
479 unsigned long bitmask[BITS_TO_LONGS(SW_MAX+1)];
480 memset(bitmask, 0, sizeof(bitmask));
481 if (ioctl(fd, EVIOCGSW(sizeof(bitmask)), bitmask) < 0)
482 return -1;
483
484 return BITMASK_GET_BIT(bitmask, bit);
485}
486
487static bool is_lid_switch(int fd)
488{
489 return has_event_bit(fd, 0, EV_SW) && has_event_bit(fd, EV_SW, SW_LID);
490}
491
492int input_check_lid_state(void)
493{
494 unsigned int u;
495
496 for (u = 0; u < input.ndevs; u++) {
497 if (is_lid_switch(input.devs[u].fd)) {
498 return get_switch_bit(input.devs[u].fd, SW_LID);
499 }
500 }
501 return -ENODEV;
502}
503