1 /**
2  * @file lv_libinput.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "../../indev/lv_indev_private.h"
11 #include "lv_libinput_private.h"
12 
13 #if LV_USE_LIBINPUT
14 
15 #include "../../display/lv_display_private.h"
16 
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <linux/limits.h>
20 #include <fcntl.h>
21 #include <errno.h>
22 #include <stdbool.h>
23 #include <dirent.h>
24 #include <libinput.h>
25 #include <pthread.h>
26 #include <string.h>
27 
28 #if LV_LIBINPUT_BSD
29     #include <dev/evdev/input.h>
30 #else
31     #include <linux/input.h>
32 #endif
33 
34 /*********************
35  *      DEFINES
36  *********************/
37 
38 /**********************
39  *      TYPEDEFS
40  **********************/
41 
42 struct _lv_libinput_device {
43     lv_libinput_capability capabilities;
44     char * path;
45 };
46 
47 /**********************
48  *  STATIC PROTOTYPES
49  **********************/
50 
51 static bool _rescan_devices(void);
52 static bool _add_scanned_device(char * path, lv_libinput_capability capabilities);
53 static void _reset_scanned_devices(void);
54 
55 static void * _poll_thread(void * data);
56 
57 lv_libinput_event_t * _get_event(lv_libinput_t * state);
58 bool _event_pending(lv_libinput_t * state);
59 lv_libinput_event_t * _create_event(lv_libinput_t * state);
60 
61 static void _read(lv_indev_t * indev, lv_indev_data_t * data);
62 static void _read_pointer(lv_libinput_t * state, struct libinput_event * event);
63 static void _read_keypad(lv_libinput_t * state, struct libinput_event * event);
64 
65 static int _open_restricted(const char * path, int flags, void * user_data);
66 static void _close_restricted(int fd, void * user_data);
67 
68 static void _delete(lv_libinput_t * dsc);
69 
70 /**********************
71  *  STATIC VARIABLES
72  **********************/
73 
74 static struct _lv_libinput_device * devices = NULL;
75 static size_t num_devices = 0;
76 
77 static const int timeout = 100; // ms
78 static const nfds_t nfds = 1;
79 
80 static const struct libinput_interface interface = {
81     .open_restricted = _open_restricted,
82     .close_restricted = _close_restricted,
83 };
84 
85 /**********************
86  *      MACROS
87  **********************/
88 
89 /**********************
90  *   GLOBAL FUNCTIONS
91  **********************/
92 
lv_libinput_query_capability(struct libinput_device * device)93 lv_libinput_capability lv_libinput_query_capability(struct libinput_device * device)
94 {
95     lv_libinput_capability capability = LV_LIBINPUT_CAPABILITY_NONE;
96     if(libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD)
97        && (libinput_device_keyboard_has_key(device, KEY_ENTER) || libinput_device_keyboard_has_key(device, KEY_KPENTER))) {
98         capability |= LV_LIBINPUT_CAPABILITY_KEYBOARD;
99     }
100     if(libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER)) {
101         capability |= LV_LIBINPUT_CAPABILITY_POINTER;
102     }
103     if(libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TOUCH)) {
104         capability |= LV_LIBINPUT_CAPABILITY_TOUCH;
105     }
106     return capability;
107 }
108 
lv_libinput_find_dev(lv_libinput_capability capabilities,bool force_rescan)109 char * lv_libinput_find_dev(lv_libinput_capability capabilities, bool force_rescan)
110 {
111     char * path = NULL;
112     lv_libinput_find_devs(capabilities, &path, 1, force_rescan);
113     return path;
114 }
115 
lv_libinput_find_devs(lv_libinput_capability capabilities,char ** found,size_t count,bool force_rescan)116 size_t lv_libinput_find_devs(lv_libinput_capability capabilities, char ** found, size_t count, bool force_rescan)
117 {
118     if((!devices || force_rescan) && !_rescan_devices()) {
119         return 0;
120     }
121 
122     size_t num_found = 0;
123 
124     for(size_t i = 0; i < num_devices && num_found < count; ++i) {
125         if(devices[i].capabilities & capabilities) {
126             found[num_found] = devices[i].path;
127             num_found++;
128         }
129     }
130 
131     return num_found;
132 }
133 
lv_libinput_create(lv_indev_type_t indev_type,const char * dev_path)134 lv_indev_t * lv_libinput_create(lv_indev_type_t indev_type, const char * dev_path)
135 {
136     lv_libinput_t * dsc = lv_malloc_zeroed(sizeof(lv_libinput_t));
137     LV_ASSERT_MALLOC(dsc);
138     if(dsc == NULL) return NULL;
139 
140     dsc->libinput_context = libinput_path_create_context(&interface, NULL);
141     if(!dsc->libinput_context) {
142         LV_LOG_ERROR("libinput_path_create_context failed: %s", strerror(errno));
143         _delete(dsc);
144         return NULL;
145     }
146 
147     dsc->libinput_device = libinput_path_add_device(dsc->libinput_context, dev_path);
148     if(!dsc->libinput_device) {
149         _delete(dsc);
150         return NULL;
151     }
152 
153     dsc->libinput_device = libinput_device_ref(dsc->libinput_device);
154     if(!dsc->libinput_device) {
155         _delete(dsc);
156         return NULL;
157     }
158 
159     dsc->fd = libinput_get_fd(dsc->libinput_context);
160 
161     /* Prepare poll */
162     dsc->fds[0].fd = dsc->fd;
163     dsc->fds[0].events = POLLIN;
164     dsc->fds[0].revents = 0;
165 
166 #if LV_LIBINPUT_XKB
167     struct xkb_rule_names names = LV_LIBINPUT_XKB_KEY_MAP;
168     lv_xkb_init(&(dsc->xkb), names);
169 #endif /* LV_LIBINPUT_XKB */
170 
171     /* Create indev */
172     lv_indev_t * indev = lv_indev_create();
173     if(!indev) {
174         _delete(dsc);
175         return NULL;
176     }
177     lv_indev_set_type(indev, indev_type);
178     lv_indev_set_read_cb(indev, _read);
179     lv_indev_set_driver_data(indev, dsc);
180 
181     /* Set up thread & lock */
182     pthread_mutex_init(&dsc->event_lock, NULL);
183     pthread_create(&dsc->worker_thread, NULL, _poll_thread, dsc);
184 
185     return indev;
186 }
187 
lv_libinput_delete(lv_indev_t * indev)188 void lv_libinput_delete(lv_indev_t * indev)
189 {
190     _delete(lv_indev_get_driver_data(indev));
191     lv_indev_delete(indev);
192 }
193 
194 /**********************
195  *   STATIC FUNCTIONS
196  **********************/
197 
198 /**
199  * rescan all attached evdev devices and store capable ones into the static devices array for quick later filtering
200  * @return true if the operation succeeded
201  */
_rescan_devices(void)202 static bool _rescan_devices(void)
203 {
204     _reset_scanned_devices();
205 
206     DIR * dir;
207     struct dirent * ent;
208     if(!(dir = opendir("/dev/input"))) {
209         perror("unable to open directory /dev/input");
210         return false;
211     }
212 
213     struct libinput * context = libinput_path_create_context(&interface, NULL);
214 
215     while((ent = readdir(dir))) {
216         if(strncmp(ent->d_name, "event", 5) != 0) {
217             continue;
218         }
219 
220         /* 11 characters for /dev/input/ + length of name + 1 NUL terminator */
221         char * path = malloc((11 + strlen(ent->d_name) + 1) * sizeof(char));
222         if(!path) {
223             perror("could not allocate memory for device node path");
224             libinput_unref(context);
225             _reset_scanned_devices();
226             return false;
227         }
228         strcpy(path, "/dev/input/");
229         strcat(path, ent->d_name);
230 
231         struct libinput_device * device = libinput_path_add_device(context, path);
232         if(!device) {
233             perror("unable to add device to libinput context");
234             free(path);
235             continue;
236         }
237 
238         /* The device pointer is guaranteed to be valid until the next libinput_dispatch. Since we're not dispatching events
239          * as part of this function, we don't have to increase its reference count to keep it alive.
240          * https://wayland.freedesktop.org/libinput/doc/latest/api/group__base.html#gaa797496f0150b482a4e01376bd33a47b */
241 
242         lv_libinput_capability capabilities = lv_libinput_query_capability(device);
243 
244         libinput_path_remove_device(device);
245 
246         if(capabilities == LV_LIBINPUT_CAPABILITY_NONE) {
247             free(path);
248             continue;
249         }
250 
251         if(!_add_scanned_device(path, capabilities)) {
252             free(path);
253             libinput_unref(context);
254             _reset_scanned_devices();
255             return false;
256         }
257     }
258 
259     libinput_unref(context);
260     return true;
261 }
262 
263 /**
264  * add a new scanned device to the static devices array, growing its size when necessary
265  * @param path device file path
266  * @param capabilities device input capabilities
267  * @return true if the operation succeeded
268  */
_add_scanned_device(char * path,lv_libinput_capability capabilities)269 static bool _add_scanned_device(char * path, lv_libinput_capability capabilities)
270 {
271     /* Double array size every 2^n elements */
272     if((num_devices & (num_devices + 1)) == 0) {
273         struct _lv_libinput_device * tmp = realloc(devices, (2 * num_devices + 1) * sizeof(struct _lv_libinput_device));
274         if(!tmp) {
275             perror("could not reallocate memory for devices array");
276             return false;
277         }
278         devices = tmp;
279     }
280 
281     devices[num_devices].path = path;
282     devices[num_devices].capabilities = capabilities;
283     num_devices++;
284 
285     return true;
286 }
287 
288 /**
289  * reset the array of scanned devices and free any dynamically allocated memory
290  */
_reset_scanned_devices(void)291 static void _reset_scanned_devices(void)
292 {
293     if(!devices) {
294         return;
295     }
296 
297     for(size_t i = 0; i < num_devices; ++i) {
298         free(devices[i].path);
299     }
300     free(devices);
301 
302     devices = NULL;
303     num_devices = 0;
304 }
305 
_poll_thread(void * data)306 static void * _poll_thread(void * data)
307 {
308     lv_libinput_t * dsc = (lv_libinput_t *)data;
309     struct libinput_event * event;
310     int rc = 0;
311 
312     LV_LOG_INFO("libinput: poll worker started");
313 
314     while(true) {
315         rc = poll(dsc->fds, nfds, timeout);
316         switch(rc) {
317             case -1:
318                 perror(NULL);
319                 __attribute__((fallthrough));
320             case 0:
321                 if(dsc->deinit) {
322                     dsc->deinit = false; /* Signal that we're done */
323                     return NULL;
324                 }
325                 continue;
326             default:
327                 break;
328         }
329         libinput_dispatch(dsc->libinput_context);
330         pthread_mutex_lock(&dsc->event_lock);
331         while((event = libinput_get_event(dsc->libinput_context)) != NULL) {
332             _read_pointer(dsc, event);
333             _read_keypad(dsc, event);
334             libinput_event_destroy(event);
335         }
336         pthread_mutex_unlock(&dsc->event_lock);
337         LV_LOG_INFO("libinput: event read");
338     }
339 
340     return NULL;
341 }
342 
_get_event(lv_libinput_t * dsc)343 lv_libinput_event_t * _get_event(lv_libinput_t * dsc)
344 {
345     if(dsc->start == dsc->end) {
346         return NULL;
347     }
348 
349     lv_libinput_event_t * evt = &dsc->points[dsc->start];
350 
351     if(++dsc->start == LV_LIBINPUT_MAX_EVENTS)
352         dsc->start = 0;
353 
354     return evt;
355 }
356 
_event_pending(lv_libinput_t * dsc)357 bool _event_pending(lv_libinput_t * dsc)
358 {
359     return dsc->start != dsc->end;
360 }
361 
_create_event(lv_libinput_t * dsc)362 lv_libinput_event_t * _create_event(lv_libinput_t * dsc)
363 {
364     lv_libinput_event_t * evt = &dsc->points[dsc->end];
365 
366     if(++dsc->end == LV_LIBINPUT_MAX_EVENTS)
367         dsc->end = 0;
368 
369     /* We have overflowed the buffer, start overwriting
370      * old events.
371      */
372     if(dsc->end == dsc->start) {
373         LV_LOG_INFO("libinput: overflowed event buffer!");
374         if(++dsc->start == LV_LIBINPUT_MAX_EVENTS)
375             dsc->start = 0;
376     }
377 
378     memset(evt, 0, sizeof(lv_libinput_event_t));
379 
380     return evt;
381 }
382 
_read(lv_indev_t * indev,lv_indev_data_t * data)383 static void _read(lv_indev_t * indev, lv_indev_data_t * data)
384 {
385     lv_libinput_t * dsc = lv_indev_get_driver_data(indev);
386     LV_ASSERT_NULL(dsc);
387 
388     pthread_mutex_lock(&dsc->event_lock);
389 
390     lv_libinput_event_t * evt = _get_event(dsc);
391 
392     if(!evt)
393         evt = &dsc->last_event; /* indev expects us to report the most recent state */
394 
395     data->point = evt->point;
396     data->state = evt->pressed;
397     data->key = evt->key_val;
398     data->continue_reading = _event_pending(dsc);
399 
400     dsc->last_event = *evt; /* Remember the last event for the next call */
401 
402     pthread_mutex_unlock(&dsc->event_lock);
403 
404     if(evt)
405         LV_LOG_TRACE("libinput_read: (%04d, %04d): %d continue_reading? %d", data->point.x, data->point.y, data->state,
406                      data->continue_reading);
407 }
408 
_read_pointer(lv_libinput_t * dsc,struct libinput_event * event)409 static void _read_pointer(lv_libinput_t * dsc, struct libinput_event * event)
410 {
411     struct libinput_event_touch * touch_event = NULL;
412     struct libinput_event_pointer * pointer_event = NULL;
413     lv_libinput_event_t * evt = NULL;
414     enum libinput_event_type type = libinput_event_get_type(event);
415     int slot = 0;
416 
417     switch(type) {
418         case LIBINPUT_EVENT_TOUCH_MOTION:
419         case LIBINPUT_EVENT_TOUCH_DOWN:
420         case LIBINPUT_EVENT_TOUCH_UP:
421             touch_event = libinput_event_get_touch_event(event);
422             break;
423         case LIBINPUT_EVENT_POINTER_MOTION:
424         case LIBINPUT_EVENT_POINTER_BUTTON:
425         case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
426             pointer_event = libinput_event_get_pointer_event(event);
427             break;
428         default:
429             return; /* We don't care about this events */
430     }
431 
432     /* We need to read unrotated display dimensions directly from the driver because libinput won't account
433      * for any rotation inside of LVGL */
434     lv_display_t * disp = lv_display_get_default();
435 
436     /* ignore more than 2 fingers as it will only confuse LVGL */
437     if(touch_event && (slot = libinput_event_touch_get_slot(touch_event)) > 1)
438         return;
439 
440     evt = _create_event(dsc);
441 
442     const int32_t hor_res = disp->physical_hor_res > 0 ? disp->physical_hor_res : disp->hor_res;
443     const int32_t ver_res = disp->physical_ver_res > 0 ? disp->physical_ver_res : disp->ver_res;
444 
445     switch(type) {
446         case LIBINPUT_EVENT_TOUCH_MOTION:
447         case LIBINPUT_EVENT_TOUCH_DOWN: {
448                 lv_point_t point;
449                 point.x = (int32_t)LV_CLAMP(INT32_MIN, libinput_event_touch_get_x_transformed(touch_event, hor_res) - disp->offset_x,
450                                             INT32_MAX);
451                 point.y = (int32_t)LV_CLAMP(INT32_MIN, libinput_event_touch_get_y_transformed(touch_event, ver_res) - disp->offset_y,
452                                             INT32_MAX);
453                 if(point.x < 0 || point.x > disp->hor_res || point.y < 0 || point.y > disp->ver_res) {
454                     break; /* ignore touches that are out of bounds */
455                 }
456                 evt->point = point;
457                 evt->pressed = LV_INDEV_STATE_PRESSED;
458                 dsc->slots[slot].point = evt->point;
459                 dsc->slots[slot].pressed = evt->pressed;
460                 break;
461             }
462         case LIBINPUT_EVENT_TOUCH_UP:
463             /*
464              * We don't support "multitouch", but libinput does. To make fast typing with two thumbs
465              * on a keyboard feel good, it's necessary to handle two fingers individually. The edge
466              * case here is if you press a key with one finger and then press a second key with another
467              * finger. No matter which finger you release, it will count as the second finger releasing
468              * and ignore the first because LVGL only stores a single (the latest) pressed state.
469              *
470              * To work around this, we detect the case where one finger is released while the other is
471              * still pressed and insert dummy events so that both release events trigger at the correct
472              * position.
473              */
474             if(slot == 0 && dsc->slots[1].pressed == LV_INDEV_STATE_PRESSED) {
475                 /* The first finger is released while the second finger is still pressed.
476                  * We turn P1 > P2 > R1 > R2 into P1 > P2 > (P1) > R1 > (P2) > R2.
477                  */
478 
479                 /* Inject the dummy press event for the first finger */
480                 lv_libinput_event_t * synth_evt = evt;
481                 synth_evt->pressed = LV_INDEV_STATE_PRESSED;
482                 synth_evt->point = dsc->slots[0].point;
483 
484                 /* Append the real release event for the first finger */
485                 evt = _create_event(dsc);
486                 evt->pressed = LV_INDEV_STATE_RELEASED;
487                 evt->point = dsc->slots[0].point;
488 
489                 /* Inject the dummy press event for the second finger */
490                 synth_evt = _create_event(dsc);
491                 synth_evt->pressed = LV_INDEV_STATE_PRESSED;
492                 synth_evt->point = dsc->slots[1].point;
493             }
494             else if(slot == 1 && dsc->slots[0].pressed == LV_INDEV_STATE_PRESSED) {
495                 /* The second finger is released while the first finger is still pressed.
496                  * We turn P1 > P2 > R2 > R1 into P1 > P2 > R2 > (P1) > R1.
497                  */
498 
499                 /* Append the real release event for the second finger */
500                 evt->pressed = LV_INDEV_STATE_RELEASED;
501                 evt->point = dsc->slots[1].point;
502 
503                 /* Inject the dummy press event for the first finger */
504                 lv_libinput_event_t * synth_evt = _create_event(dsc);
505                 synth_evt->pressed = LV_INDEV_STATE_PRESSED;
506                 synth_evt->point = dsc->slots[0].point;
507             }
508             else {
509                 evt->pressed = LV_INDEV_STATE_RELEASED;
510                 evt->point = dsc->slots[slot].point;
511             }
512 
513             dsc->slots[slot].pressed = evt->pressed;
514             break;
515         case LIBINPUT_EVENT_POINTER_MOTION:
516             dsc->pointer_position.x = (int32_t)LV_CLAMP(0, dsc->pointer_position.x + libinput_event_pointer_get_dx(pointer_event),
517                                                         disp->hor_res - 1);
518             dsc->pointer_position.y = (int32_t)LV_CLAMP(0, dsc->pointer_position.y + libinput_event_pointer_get_dy(pointer_event),
519                                                         disp->ver_res - 1);
520             evt->point.x = dsc->pointer_position.x;
521             evt->point.y = dsc->pointer_position.y;
522             evt->pressed = dsc->pointer_button_down;
523             break;
524         case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: {
525                 lv_point_t point;
526                 point.x = (int32_t)LV_CLAMP(INT32_MIN, libinput_event_pointer_get_absolute_x_transformed(pointer_event,
527                                                                                                          hor_res) - disp->offset_x, INT32_MAX);
528                 point.y = (int32_t)LV_CLAMP(INT32_MIN, libinput_event_pointer_get_absolute_y_transformed(pointer_event,
529                                                                                                          ver_res) - disp->offset_y, INT32_MAX);
530                 if(point.x < 0 || point.x > disp->hor_res || point.y < 0 || point.y > disp->ver_res) {
531                     break; /* ignore pointer events that are out of bounds */
532                 }
533                 evt->point = point;
534                 evt->pressed = dsc->pointer_button_down;
535                 break;
536             }
537         case LIBINPUT_EVENT_POINTER_BUTTON: {
538                 enum libinput_button_state button_state = libinput_event_pointer_get_button_state(pointer_event);
539                 dsc->pointer_button_down = button_state == LIBINPUT_BUTTON_STATE_RELEASED ? LV_INDEV_STATE_RELEASED :
540                                            LV_INDEV_STATE_PRESSED;
541                 evt->point.x = dsc->pointer_position.x;
542                 evt->point.y = dsc->pointer_position.y;
543                 evt->pressed = dsc->pointer_button_down;
544             }
545         default:
546             break;
547     }
548 }
549 
_read_keypad(lv_libinput_t * dsc,struct libinput_event * event)550 static void _read_keypad(lv_libinput_t * dsc, struct libinput_event * event)
551 {
552     struct libinput_event_keyboard * keyboard_event = NULL;
553     enum libinput_event_type type = libinput_event_get_type(event);
554     lv_libinput_event_t * evt = NULL;
555     switch(type) {
556         case LIBINPUT_EVENT_KEYBOARD_KEY:
557             evt = _create_event(dsc);
558             keyboard_event = libinput_event_get_keyboard_event(event);
559             enum libinput_key_state key_state = libinput_event_keyboard_get_key_state(keyboard_event);
560             uint32_t code = libinput_event_keyboard_get_key(keyboard_event);
561 #if LV_LIBINPUT_XKB
562             evt->key_val = lv_xkb_process_key(&(dsc->xkb), code, key_state == LIBINPUT_KEY_STATE_PRESSED);
563 #else
564             switch(code) {
565                 case KEY_BACKSPACE:
566                     evt->key_val = LV_KEY_BACKSPACE;
567                     break;
568                 case KEY_ENTER:
569                     evt->key_val = LV_KEY_ENTER;
570                     break;
571                 case KEY_PREVIOUS:
572                     evt->key_val = LV_KEY_PREV;
573                     break;
574                 case KEY_NEXT:
575                     evt->key_val = LV_KEY_NEXT;
576                     break;
577                 case KEY_UP:
578                     evt->key_val = LV_KEY_UP;
579                     break;
580                 case KEY_LEFT:
581                     evt->key_val = LV_KEY_LEFT;
582                     break;
583                 case KEY_RIGHT:
584                     evt->key_val = LV_KEY_RIGHT;
585                     break;
586                 case KEY_DOWN:
587                     evt->key_val = LV_KEY_DOWN;
588                     break;
589                 case KEY_TAB:
590                     evt->key_val = LV_KEY_NEXT;
591                     break;
592                 case KEY_HOME:
593                     evt->key_val = LV_KEY_HOME;
594                     break;
595                 case KEY_END:
596                     evt->key_val = LV_KEY_END;
597                     break;
598                 case KEY_ESC:
599                     evt->key_val = LV_KEY_ESC;
600                     break;
601                 default:
602                     evt->key_val = 0;
603                     break;
604             }
605 #endif /* LV_LIBINPUT_XKB */
606             if(evt->key_val != 0) {
607                 /* Only record button state when actual output is produced to prevent widgets from refreshing */
608                 evt->pressed = (key_state == LIBINPUT_KEY_STATE_RELEASED) ? LV_INDEV_STATE_RELEASED : LV_INDEV_STATE_PRESSED;
609 
610                 // just release the key immediately after it got pressed.
611                 // but don't handle special keys where holding a key makes sense
612                 if(evt->key_val != LV_KEY_BACKSPACE &&
613                    evt->key_val != LV_KEY_UP &&
614                    evt->key_val != LV_KEY_LEFT &&
615                    evt->key_val != LV_KEY_RIGHT &&
616                    evt->key_val != LV_KEY_DOWN &&
617                    key_state == LIBINPUT_KEY_STATE_PRESSED) {
618                     lv_libinput_event_t * release_evt = _create_event(dsc);
619                     release_evt->pressed = LV_INDEV_STATE_RELEASED;
620                     release_evt->key_val = evt->key_val;
621                 }
622             }
623             break;
624         default:
625             break;
626     }
627 }
628 
_open_restricted(const char * path,int flags,void * user_data)629 static int _open_restricted(const char * path, int flags, void * user_data)
630 {
631     LV_UNUSED(user_data);
632     int fd = open(path, flags);
633     return fd < 0 ? -errno : fd;
634 }
635 
_close_restricted(int fd,void * user_data)636 static void _close_restricted(int fd, void * user_data)
637 {
638     LV_UNUSED(user_data);
639     close(fd);
640 }
641 
_delete(lv_libinput_t * dsc)642 static void _delete(lv_libinput_t * dsc)
643 {
644     if(dsc->fd)
645         dsc->deinit = true;
646 
647     /* Give worker thread a whole second to quit */
648     for(int i = 0; i < 100; i++) {
649         if(!dsc->deinit)
650             break;
651         usleep(10000);
652     }
653 
654     if(dsc->deinit) {
655         LV_LOG_ERROR("libinput worker thread did not quit in time, cancelling it");
656         pthread_cancel(dsc->worker_thread);
657     }
658 
659     if(dsc->libinput_device) {
660         libinput_path_remove_device(dsc->libinput_device);
661         libinput_device_unref(dsc->libinput_device);
662     }
663 
664     if(dsc->libinput_context) {
665         libinput_unref(dsc->libinput_context);
666     }
667 
668 #if LV_LIBINPUT_XKB
669     lv_xkb_deinit(&(dsc->xkb));
670 #endif /* LV_LIBINPUT_XKB */
671 
672     lv_free(dsc);
673 }
674 
675 #endif /* LV_USE_LIBINPUT */
676