Implement Long-Press-to-Right-Click on Touchscreen Linux Device with Xorg or Wayland
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

input.c 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. #include "input.h"
  2. #include "uinput.h"
  3. #include "rce.h"
  4. #include <errno.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <sys/select.h>
  8. #include <sys/timerfd.h>
  9. #include <unistd.h>
  10. struct input_state_t {
  11. int pressed_device_id;
  12. int pos_x, pos_y;
  13. int pressed_pos_x, pressed_pos_y;
  14. int fd_timer; // The timer used to execute right click on timeout
  15. struct libevdev_uinput *uinput;
  16. };
  17. void free_evdev(struct libevdev *evdev) {
  18. int fd = libevdev_get_fd(evdev);
  19. libevdev_free(evdev);
  20. close(fd);
  21. }
  22. // Build the fd set used by `select`
  23. // return: nfds to be used in select()
  24. int build_fd_set(fd_set *fds, int fd_timer,
  25. int num, struct libevdev **evdev) {
  26. FD_ZERO(fds);
  27. FD_SET(fd_timer, fds);
  28. int fd = -1, max_fd = fd_timer;
  29. for (unsigned int i = 0; i < num; i++) {
  30. fd = libevdev_get_fd(evdev[i]);
  31. FD_SET(fd, fds);
  32. if (fd > max_fd)
  33. max_fd = fd;
  34. }
  35. return max_fd + 1;
  36. }
  37. void arm_delayed_rclick(struct input_state_t *state, int dev_id) {
  38. // Record the position where the touch event began
  39. state->pressed_pos_x = state->pos_x;
  40. state->pressed_pos_y = state->pos_y;
  41. state->pressed_device_id = dev_id;
  42. // Start the timer; once timeout reached,
  43. // fire the right click event
  44. timerfd_settime(state->fd_timer, 0, &(struct itimerspec) {
  45. .it_value = LONG_CLICK_INTERVAL,
  46. }, NULL);
  47. }
  48. void unarm_delayed_rclick(struct input_state_t *state) {
  49. state->pressed_device_id = -1;
  50. // Force cancel the timer if finger released
  51. timerfd_settime(state->fd_timer, 0, &(struct itimerspec) {
  52. .it_value = {
  53. .tv_sec = 0,
  54. .tv_nsec = 0,
  55. },
  56. }, NULL);
  57. }
  58. void on_input_event(struct input_state_t *state,
  59. struct input_event *ev, int dev_id) {
  60. // If we are during a long-press event,
  61. // do not interruput with another device
  62. if (state->pressed_device_id != -1 && dev_id != state->pressed_device_id)
  63. return;
  64. if (ev->type == EV_ABS) {
  65. // Absolute position event
  66. // Record the current position
  67. if (ev->code == ABS_X || ev->code == ABS_MT_POSITION_X) {
  68. state->pos_x = ev->value;
  69. } else if (ev->code == ABS_Y || ev->code == ABS_MT_POSITION_Y) {
  70. state->pos_y = ev->value;
  71. }
  72. } else if (ev->type == EV_KEY && ev->code == BTN_TOUCH) {
  73. // Touch event
  74. if (ev->value == 1) {
  75. // Schedule a delayed right click event
  76. // so that if anything happens before the long-press timeout,
  77. // it can be canceled
  78. arm_delayed_rclick(state, dev_id);
  79. } else {
  80. // Finger released. It is no longer considered a long-press
  81. unarm_delayed_rclick(state);
  82. }
  83. }
  84. }
  85. void on_timer_expire(struct input_state_t *state) {
  86. unsigned int dx = abs(state->pos_x - state->pressed_pos_x);
  87. unsigned int dy = abs(state->pos_y - state->pressed_pos_y);
  88. // Only consider movement within a range to be "still"
  89. // i.e. if movement is within this value during timeout
  90. // , then it is a long click
  91. if (dx <= LONG_CLICK_FUZZ && dy <= LONG_CLICK_FUZZ)
  92. uinput_send_right_click(state->uinput);
  93. // In Linux implementation of timerfd, the fd becomes always "readable"
  94. // after the timeout. So we have to unarm it after we receive the event.
  95. unarm_delayed_rclick(state);
  96. }
  97. void process_evdev_input(int num, struct libevdev **evdev) {
  98. // Initialize everything to -1
  99. struct input_state_t state = {
  100. .pressed_device_id = -1,
  101. .pos_x = -1, .pos_y = -1,
  102. .pressed_pos_x = -1, .pressed_pos_y = -1,
  103. .fd_timer = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK),
  104. .uinput = uinput_initialize(),
  105. };
  106. // Check if uinput device was created
  107. if (state.uinput == NULL) {
  108. fprintf(stderr, "Failed to create uinput device\n");
  109. exit(-1);
  110. return;
  111. }
  112. // Initialize the set of fds
  113. fd_set fds;
  114. int nfds = build_fd_set(&fds, state.fd_timer, num, evdev);
  115. struct input_event ev;
  116. // Detect events from the fds
  117. while (select(nfds, &fds, NULL, NULL, NULL) > 0) {
  118. // The timer fd
  119. if (FD_ISSET(state.fd_timer, &fds))
  120. on_timer_expire(&state);
  121. for (unsigned int i = 0; i < num; i++) {
  122. // Can we read an event?
  123. if (!FD_ISSET(libevdev_get_fd(evdev[i]), &fds))
  124. continue;
  125. // Read all the available events
  126. while (libevdev_next_event(evdev[i],
  127. LIBEVDEV_READ_FLAG_NORMAL, &ev) == 0) {
  128. on_input_event(&state, &ev, i);
  129. }
  130. }
  131. // Reset the fd_set for next iteration
  132. nfds = build_fd_set(&fds, state.fd_timer, num, evdev);
  133. }
  134. // Cleanup
  135. close(state.fd_timer);
  136. libevdev_uinput_destroy(state.uinput);
  137. for (int i = 0; i < num; i++) {
  138. free_evdev(evdev[i]);
  139. }
  140. }