libinput-debug-gui.c 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038
  1. /*
  2. * Copyright © 2014 Red Hat, Inc.
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice (including the next
  12. * paragraph) shall be included in all copies or substantial portions of the
  13. * Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  18. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21. * DEALINGS IN THE SOFTWARE.
  22. */
  23. #include <config.h>
  24. #include <assert.h>
  25. #include <cairo.h>
  26. #include <errno.h>
  27. #include <fcntl.h>
  28. #include <getopt.h>
  29. #include <glib-unix.h>
  30. #include <glib.h>
  31. #include <gtk/gtk.h>
  32. #include <libevdev/libevdev.h>
  33. #include <libinput.h>
  34. #include <linux/input.h>
  35. #include <math.h>
  36. #include <stdarg.h>
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. #include <string.h>
  40. #include <unistd.h>
  41. #include "util-files.h"
  42. #include "util-list.h"
  43. #include "util-macros.h"
  44. #include "util-strings.h"
  45. #include "shared.h"
  46. #ifdef HAVE_GTK_WAYLAND
  47. #include <wayland-client.h>
  48. #include "pointer-constraints-unstable-v1-client-protocol.h"
  49. #ifdef HAVE_GTK4
  50. #include <gdk/wayland/gdkwayland.h>
  51. #else
  52. #include <gdk/gdkwayland.h>
  53. #endif
  54. #endif
  55. #ifdef HAVE_GTK_X11
  56. #include <X11/X.h>
  57. #include <X11/Xlib.h>
  58. #ifdef HAVE_GTK4
  59. #include <gdk/x11/gdkx.h>
  60. #else
  61. #include <gdk/gdkx.h>
  62. #endif
  63. #endif
  64. #define clip(val_, min_, max_) min((max_), max((min_), (val_)))
  65. enum touch_state {
  66. TOUCH_ACTIVE,
  67. TOUCH_ENDED,
  68. TOUCH_CANCELLED,
  69. };
  70. struct touch {
  71. enum touch_state state;
  72. int x, y;
  73. };
  74. struct point {
  75. double x, y;
  76. };
  77. struct evdev_device {
  78. struct list node;
  79. struct libevdev *evdev;
  80. struct libinput_device *libinput_device;
  81. int fd;
  82. guint source_id;
  83. };
  84. struct window {
  85. bool grab;
  86. struct tools_options options;
  87. struct list evdev_devices;
  88. GMainLoop *event_loop;
  89. GtkWidget *win;
  90. GtkWidget *area;
  91. int width, height; /* of window */
  92. /* sprite position */
  93. struct point pointer;
  94. struct point unaccelerated;
  95. /* these are for the delta coordinates, but they're not
  96. * deltas, they are converted into abs positions */
  97. size_t ndeltas;
  98. struct point deltas[64];
  99. /* abs position */
  100. struct point abs;
  101. /* Wayland and X11 pointer locking */
  102. struct {
  103. bool locked;
  104. #ifdef HAVE_GTK_WAYLAND
  105. struct zwp_pointer_constraints_v1 *wayland_pointer_constraints;
  106. struct zwp_locked_pointer_v1 *wayland_locked_pointer;
  107. #endif
  108. } lock_pointer;
  109. /* scroll bar positions */
  110. struct {
  111. double vx, vy;
  112. double hx, hy;
  113. double vx_discrete, vy_discrete;
  114. double hx_discrete, hy_discrete;
  115. } scroll;
  116. /* touch positions */
  117. struct touch touches[32];
  118. /* l/m/r mouse buttons */
  119. struct {
  120. bool l, m, r;
  121. bool other;
  122. const char *other_name;
  123. } buttons;
  124. /* touchpad swipe */
  125. struct {
  126. int nfingers;
  127. double x, y;
  128. } swipe;
  129. struct {
  130. int nfingers;
  131. double scale;
  132. double angle;
  133. double x, y;
  134. } pinch;
  135. struct {
  136. int nfingers;
  137. bool active;
  138. } hold;
  139. struct {
  140. double x, y;
  141. double x_in, y_in;
  142. double x_down, y_down;
  143. double x_up, y_up;
  144. double pressure;
  145. double distance;
  146. double tilt_x, tilt_y;
  147. double rotation;
  148. double size_major, size_minor;
  149. bool is_down;
  150. /* these are for the delta coordinates, but they're not
  151. * deltas, they are converted into abs positions */
  152. size_t ndeltas;
  153. struct point deltas[64];
  154. } tool;
  155. struct {
  156. struct {
  157. double position;
  158. int number;
  159. } ring;
  160. struct {
  161. double position;
  162. int number;
  163. } strip;
  164. struct {
  165. double position;
  166. int number;
  167. } dial;
  168. } pad;
  169. struct {
  170. int rel_x, rel_y; /* REL_X/Y */
  171. int x, y; /* ABS_X/Y */
  172. struct {
  173. int x, y; /* ABS_MT_POSITION_X/Y */
  174. bool active;
  175. } slots[16];
  176. unsigned int slot; /* ABS_MT_SLOT */
  177. /* So we know when to re-fetch the abs axes */
  178. uintptr_t device, last_device;
  179. } evdev;
  180. struct libinput_device *devices[50];
  181. };
  182. #ifdef HAVE_GTK_WAYLAND
  183. static void
  184. wayland_registry_global(void *data,
  185. struct wl_registry *registry,
  186. uint32_t name,
  187. const char *interface,
  188. uint32_t version)
  189. {
  190. struct window *w = data;
  191. if (!g_strcmp0(interface, "zwp_pointer_constraints_v1")) {
  192. w->lock_pointer.wayland_pointer_constraints =
  193. wl_registry_bind(registry,
  194. name,
  195. &zwp_pointer_constraints_v1_interface,
  196. 1);
  197. }
  198. }
  199. static void
  200. wayland_registry_global_remove(void *data,
  201. struct wl_registry *wl_registry,
  202. uint32_t name)
  203. {
  204. }
  205. static struct wl_registry_listener registry_listener = {
  206. wayland_registry_global,
  207. wayland_registry_global_remove
  208. };
  209. static bool
  210. wayland_lock_pointer(struct window *w)
  211. {
  212. GdkDisplay *gdk_display;
  213. GdkSeat *gdk_seat;
  214. GdkDevice *gdk_device;
  215. struct wl_display *display;
  216. struct wl_registry *registry;
  217. struct wl_pointer *wayland_pointer;
  218. struct wl_surface *surface;
  219. w->lock_pointer.wayland_pointer_constraints = NULL;
  220. gdk_display = gdk_display_get_default();
  221. display = gdk_wayland_display_get_wl_display(gdk_display);
  222. gdk_seat = gdk_display_get_default_seat(gdk_display);
  223. gdk_device = gdk_seat_get_pointer(gdk_seat);
  224. wayland_pointer = gdk_wayland_device_get_wl_pointer(gdk_device);
  225. registry = wl_display_get_registry(display);
  226. wl_registry_add_listener(registry, &registry_listener, w);
  227. wl_display_roundtrip(display);
  228. if (!w->lock_pointer.wayland_pointer_constraints)
  229. return false;
  230. #ifdef HAVE_GTK4
  231. GtkNative *window = gtk_widget_get_native(w->win);
  232. GdkSurface *gdk_surface = gtk_native_get_surface(window);
  233. surface = gdk_wayland_surface_get_wl_surface(gdk_surface);
  234. #else
  235. GdkWindow *window = gtk_widget_get_window(w->win);
  236. surface = gdk_wayland_window_get_wl_surface(window);
  237. #endif
  238. w->lock_pointer.wayland_locked_pointer =
  239. zwp_pointer_constraints_v1_lock_pointer(
  240. w->lock_pointer.wayland_pointer_constraints,
  241. surface,
  242. wayland_pointer,
  243. NULL,
  244. ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
  245. return true;
  246. }
  247. static void
  248. wayland_unlock_pointer(struct window *w)
  249. {
  250. w->lock_pointer.wayland_pointer_constraints = NULL;
  251. zwp_locked_pointer_v1_destroy(w->lock_pointer.wayland_locked_pointer);
  252. }
  253. static inline bool
  254. backend_is_wayland(void)
  255. {
  256. return GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default());
  257. }
  258. #endif /* HAVE_GTK_WAYLAND */
  259. #ifdef HAVE_GTK_X11
  260. static bool
  261. x_lock_pointer(struct window *w)
  262. {
  263. Display *x_display;
  264. Window x_win;
  265. int result;
  266. /* gdk_display_get_default() is deprecated but probably won't be removed
  267. * before GTK X11 is removed completely so this whole section will
  268. * be gone anyway. Meanwhile, disable the warning
  269. */
  270. #pragma GCC diagnostic push
  271. #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  272. x_display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
  273. #pragma GCC diagnostic pop
  274. #ifdef HAVE_GTK4
  275. GtkNative *window = gtk_widget_get_native(w->win);
  276. GdkSurface *surface = gtk_native_get_surface(window);
  277. #pragma GCC diagnostic push
  278. #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  279. x_win = GDK_SURFACE_XID(surface);
  280. #pragma GCC diagnostic pop
  281. #else
  282. GdkWindow *window = gtk_widget_get_window(w->win);
  283. x_win = GDK_WINDOW_XID(window);
  284. #endif
  285. result = XGrabPointer(x_display,
  286. x_win,
  287. False,
  288. NoEventMask,
  289. GrabModeAsync,
  290. GrabModeAsync,
  291. x_win,
  292. None,
  293. CurrentTime);
  294. return (result == GrabSuccess);
  295. }
  296. static void
  297. x_unlock_pointer(struct window *w)
  298. {
  299. Display *x_display;
  300. #pragma GCC diagnostic push
  301. #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  302. x_display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
  303. #pragma GCC diagnostic pop
  304. XUngrabPointer(x_display, CurrentTime);
  305. }
  306. static inline bool
  307. backend_is_x11(void)
  308. {
  309. return GDK_IS_X11_DISPLAY(gdk_display_get_default());
  310. }
  311. #endif /* HAVE_GTK_X11 */
  312. static bool
  313. window_lock_pointer(struct window *w)
  314. {
  315. if (w->lock_pointer.locked)
  316. return true;
  317. #ifdef HAVE_GTK_WAYLAND
  318. if (backend_is_wayland())
  319. w->lock_pointer.locked = wayland_lock_pointer(w);
  320. #endif
  321. #ifdef HAVE_GTK_X11
  322. if (backend_is_x11())
  323. w->lock_pointer.locked = x_lock_pointer(w);
  324. #endif
  325. return w->lock_pointer.locked;
  326. }
  327. static void
  328. window_unlock_pointer(struct window *w)
  329. {
  330. if (!w->lock_pointer.locked)
  331. return;
  332. w->lock_pointer.locked = false;
  333. #ifdef HAVE_GTK_WAYLAND
  334. if (backend_is_wayland())
  335. wayland_unlock_pointer(w);
  336. #endif
  337. #ifdef HAVE_GTK_X11
  338. if (backend_is_x11())
  339. x_unlock_pointer(w);
  340. #endif
  341. }
  342. LIBINPUT_ATTRIBUTE_PRINTF(1, 2)
  343. static inline void
  344. msg(const char *fmt, ...)
  345. {
  346. va_list args;
  347. printf("info: ");
  348. va_start(args, fmt);
  349. vprintf(fmt, args);
  350. va_end(args);
  351. }
  352. static inline void
  353. draw_evdev_rel(struct window *w, cairo_t *cr)
  354. {
  355. int center_x, center_y;
  356. cairo_save(cr);
  357. cairo_set_source_rgb(cr, .2, .2, .8);
  358. center_x = w->width / 2 - 400;
  359. center_y = w->height / 2;
  360. cairo_arc(cr, center_x, center_y, 10, 0, 2 * M_PI);
  361. cairo_stroke(cr);
  362. if (w->evdev.rel_x) {
  363. int dir = w->evdev.rel_x > 0 ? 1 : -1;
  364. for (int i = 0; i < abs(w->evdev.rel_x); i++) {
  365. cairo_move_to(cr, center_x + (i + 1) * 20 * dir, center_y - 20);
  366. cairo_rel_line_to(cr, 0, 40);
  367. cairo_rel_line_to(cr, 20 * dir, -20);
  368. cairo_rel_line_to(cr, -20 * dir, -20);
  369. cairo_fill(cr);
  370. }
  371. }
  372. if (w->evdev.rel_y) {
  373. int dir = w->evdev.rel_y > 0 ? 1 : -1;
  374. for (int i = 0; i < abs(w->evdev.rel_y); i++) {
  375. cairo_move_to(cr, center_x - 20, center_y + (i + 1) * 20 * dir);
  376. cairo_rel_line_to(cr, 40, 0);
  377. cairo_rel_line_to(cr, -20, 20 * dir);
  378. cairo_rel_line_to(cr, -20, -20 * dir);
  379. cairo_fill(cr);
  380. }
  381. }
  382. cairo_restore(cr);
  383. }
  384. static inline void
  385. draw_evdev_abs(struct window *w, cairo_t *cr)
  386. {
  387. static const struct input_absinfo *ax = NULL, *ay = NULL;
  388. const int normalized_width = 200;
  389. int outline_width = normalized_width, outline_height = normalized_width * 0.75;
  390. int center_x, center_y;
  391. int width, height;
  392. int x, y;
  393. cairo_save(cr);
  394. center_x = w->width / 2 + 400;
  395. center_y = w->height / 2;
  396. /* Always the outline even if we didn't get any abs events yet so it
  397. * doesn't just appear out of nowhere */
  398. if (w->evdev.device == 0)
  399. goto draw_outline;
  400. /* device has changed, so the abs proportions/dimensions have
  401. * changed. */
  402. if (w->evdev.device != w->evdev.last_device) {
  403. struct evdev_device *d;
  404. ax = NULL;
  405. ay = NULL;
  406. list_for_each(d, &w->evdev_devices, node) {
  407. if ((uintptr_t)d->libinput_device != w->evdev.device)
  408. continue;
  409. ax = libevdev_get_abs_info(d->evdev, ABS_X);
  410. ay = libevdev_get_abs_info(d->evdev, ABS_Y);
  411. w->evdev.last_device = w->evdev.device;
  412. }
  413. }
  414. if (ax == NULL || ay == NULL)
  415. goto draw_outline;
  416. width = ax->maximum - ax->minimum;
  417. height = ay->maximum - ay->minimum;
  418. outline_height = 1.0 * height / width * normalized_width;
  419. outline_width = normalized_width;
  420. cairo_set_source_rgb(cr, .2, .2, .8);
  421. x = 1.0 * (w->evdev.x - ax->minimum) / width * outline_width;
  422. y = 1.0 * (w->evdev.y - ay->minimum) / height * outline_height;
  423. x += center_x - outline_width / 2;
  424. y += center_y - outline_height / 2;
  425. cairo_arc(cr, x, y, 10, 0, 2 * M_PI);
  426. cairo_fill(cr);
  427. for (size_t i = 0; i < ARRAY_LENGTH(w->evdev.slots); i++) {
  428. if (!w->evdev.slots[i].active)
  429. continue;
  430. cairo_set_source_rgb(cr,
  431. .2 + .2 * (i % 5),
  432. .2 + .2 * (i % 5),
  433. .8 - .2 * (i % 5));
  434. x = w->evdev.slots[i].x;
  435. y = w->evdev.slots[i].y;
  436. x = 1.0 * (x - ax->minimum) / width * outline_width;
  437. y = 1.0 * (y - ay->minimum) / height * outline_height;
  438. x += center_x - outline_width / 2;
  439. y += center_y - outline_height / 2;
  440. cairo_arc(cr, x, y, 10, 0, 2 * M_PI);
  441. cairo_fill(cr);
  442. char finger_text[3];
  443. cairo_text_extents_t finger_text_extents;
  444. snprintf(finger_text, 3, "%zu", i);
  445. cairo_set_source_rgb(cr, 1.f, 1.f, 1.f);
  446. cairo_set_font_size(cr, 12.0);
  447. cairo_text_extents(cr, finger_text, &finger_text_extents);
  448. cairo_move_to(cr,
  449. x - finger_text_extents.width / 2,
  450. y + finger_text_extents.height / 2);
  451. cairo_show_text(cr, finger_text);
  452. }
  453. draw_outline:
  454. /* The touchpad outline */
  455. cairo_set_source_rgb(cr, .2, .2, .8);
  456. cairo_rectangle(cr,
  457. center_x - outline_width / 2,
  458. center_y - outline_height / 2,
  459. outline_width,
  460. outline_height);
  461. cairo_stroke(cr);
  462. cairo_restore(cr);
  463. }
  464. static inline void
  465. draw_gestures(struct window *w, cairo_t *cr)
  466. {
  467. int offset;
  468. /* swipe */
  469. cairo_save(cr);
  470. cairo_translate(cr, w->swipe.x, w->swipe.y);
  471. for (int i = 0; i < w->swipe.nfingers; i++) {
  472. cairo_set_source_rgb(cr, .8, .8, .4);
  473. cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI);
  474. cairo_fill(cr);
  475. }
  476. for (int i = 0; i < 4; i++) { /* 4 fg max */
  477. cairo_set_source_rgb(cr, 0, 0, 0);
  478. cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI);
  479. cairo_stroke(cr);
  480. }
  481. cairo_restore(cr);
  482. /* pinch */
  483. cairo_save(cr);
  484. offset = w->pinch.scale * 100;
  485. cairo_translate(cr, w->pinch.x, w->pinch.y);
  486. cairo_rotate(cr, w->pinch.angle * M_PI / 180.0);
  487. if (w->pinch.nfingers > 0) {
  488. cairo_set_source_rgb(cr, .4, .4, .8);
  489. cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI);
  490. cairo_arc(cr, -offset, offset, 20, 0, 2 * M_PI);
  491. cairo_fill(cr);
  492. }
  493. cairo_set_source_rgb(cr, 0, 0, 0);
  494. cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI);
  495. cairo_stroke(cr);
  496. cairo_arc(cr, -offset, offset, 20, 0, 2 * M_PI);
  497. cairo_stroke(cr);
  498. cairo_restore(cr);
  499. /* hold */
  500. cairo_save(cr);
  501. cairo_translate(cr, w->width / 2, w->height / 2 + 100);
  502. for (int i = 4; i > 0; i--) { /* 4 fg max */
  503. double r, g, b, hold_alpha;
  504. r = .4 + .2 * (i % 2);
  505. g = .2;
  506. b = .2;
  507. hold_alpha = (w->hold.active && i <= w->hold.nfingers) ? 1 : .5;
  508. cairo_set_source_rgba(cr, r, g, b, hold_alpha);
  509. cairo_arc(cr, 0, 0, 20 * i, 0, 2 * M_PI);
  510. cairo_fill(cr);
  511. cairo_set_source_rgba(cr, 0, 0, 0, hold_alpha);
  512. cairo_arc(cr, 0, 0, 20 * i, 0, 2 * M_PI);
  513. cairo_stroke(cr);
  514. }
  515. cairo_restore(cr);
  516. }
  517. static inline void
  518. draw_scrollbars(struct window *w, cairo_t *cr)
  519. {
  520. /* normal scrollbars */
  521. cairo_save(cr);
  522. cairo_set_source_rgb(cr, .4, .8, 0);
  523. cairo_rectangle(cr, w->scroll.vx - 10, w->scroll.vy - 20, 20, 40);
  524. cairo_rectangle(cr, w->scroll.hx - 20, w->scroll.hy - 10, 40, 20);
  525. cairo_fill(cr);
  526. /* discrete scrollbars */
  527. cairo_set_source_rgb(cr, .8, .4, 0);
  528. cairo_rectangle(cr,
  529. w->scroll.vx_discrete - 5,
  530. w->scroll.vy_discrete - 10,
  531. 10,
  532. 20);
  533. cairo_rectangle(cr,
  534. w->scroll.hx_discrete - 10,
  535. w->scroll.hy_discrete - 5,
  536. 20,
  537. 10);
  538. cairo_fill(cr);
  539. cairo_restore(cr);
  540. }
  541. static inline void
  542. draw_touchpoints(struct window *w, cairo_t *cr)
  543. {
  544. cairo_save(cr);
  545. ARRAY_FOR_EACH(w->touches, t) {
  546. if (t->state == TOUCH_ACTIVE)
  547. cairo_set_source_rgb(cr, .8, .2, .2);
  548. else
  549. cairo_set_source_rgb(cr, .8, .4, .4);
  550. cairo_arc(cr, t->x, t->y, 10, 0, 2 * M_PI);
  551. if (t->state == TOUCH_CANCELLED)
  552. cairo_stroke(cr);
  553. else
  554. cairo_fill(cr);
  555. }
  556. cairo_restore(cr);
  557. }
  558. static inline void
  559. draw_abs_pointer(struct window *w, cairo_t *cr)
  560. {
  561. cairo_save(cr);
  562. cairo_set_source_rgb(cr, .2, .4, .8);
  563. cairo_arc(cr, w->abs.x, w->abs.y, 10, 0, 2 * M_PI);
  564. cairo_fill(cr);
  565. cairo_restore(cr);
  566. }
  567. static inline void
  568. draw_text(cairo_t *cr, const char *text, double x, double y)
  569. {
  570. cairo_text_extents_t te;
  571. cairo_font_extents_t fe;
  572. cairo_text_extents(cr, text, &te);
  573. cairo_font_extents(cr, &fe);
  574. /* center of the rectangle */
  575. cairo_move_to(cr, x, y);
  576. cairo_rel_move_to(cr, -te.width / 2, -fe.descent + te.height / 2);
  577. cairo_show_text(cr, text);
  578. }
  579. static inline void
  580. draw_other_button(struct window *w, cairo_t *cr)
  581. {
  582. const char *name = w->buttons.other_name;
  583. cairo_save(cr);
  584. if (!w->buttons.other)
  585. goto outline;
  586. if (!name)
  587. name = "undefined";
  588. cairo_set_source_rgb(cr, .2, .8, .8);
  589. cairo_rectangle(cr, w->width / 2 - 40, w->height - 150, 80, 30);
  590. cairo_fill(cr);
  591. cairo_set_source_rgb(cr, 0, 0, 0);
  592. draw_text(cr, name, w->width / 2, w->height - 150 + 15);
  593. outline:
  594. cairo_set_source_rgb(cr, 0, 0, 0);
  595. cairo_rectangle(cr, w->width / 2 - 40, w->height - 150, 80, 30);
  596. cairo_stroke(cr);
  597. cairo_restore(cr);
  598. }
  599. static inline void
  600. draw_buttons(struct window *w, cairo_t *cr)
  601. {
  602. cairo_save(cr);
  603. if (w->buttons.l || w->buttons.m || w->buttons.r) {
  604. cairo_set_source_rgb(cr, .2, .8, .8);
  605. if (w->buttons.l)
  606. cairo_rectangle(cr,
  607. w->width / 2 - 100,
  608. w->height - 200,
  609. 70,
  610. 30);
  611. if (w->buttons.m)
  612. cairo_rectangle(cr, w->width / 2 - 20, w->height - 200, 40, 30);
  613. if (w->buttons.r)
  614. cairo_rectangle(cr, w->width / 2 + 30, w->height - 200, 70, 30);
  615. cairo_fill(cr);
  616. }
  617. cairo_set_source_rgb(cr, 0, 0, 0);
  618. cairo_rectangle(cr, w->width / 2 - 100, w->height - 200, 70, 30);
  619. cairo_rectangle(cr, w->width / 2 - 20, w->height - 200, 40, 30);
  620. cairo_rectangle(cr, w->width / 2 + 30, w->height - 200, 70, 30);
  621. cairo_stroke(cr);
  622. cairo_restore(cr);
  623. draw_other_button(w, cr);
  624. }
  625. static inline void
  626. draw_pad(struct window *w, cairo_t *cr)
  627. {
  628. double rx, ry;
  629. double pos;
  630. char number[3];
  631. rx = w->width / 2 - 200;
  632. ry = w->height / 2 + 100;
  633. cairo_save(cr);
  634. /* outer ring (for ring) */
  635. cairo_set_source_rgb(cr, .7, .7, .0);
  636. cairo_arc(cr, rx, ry, 50, 0, 2 * M_PI);
  637. cairo_fill(cr);
  638. /* inner ring (for dial) */
  639. cairo_set_source_rgb(cr, 1., 1., 1.);
  640. cairo_arc(cr, rx, ry, 30, 0, 2 * M_PI);
  641. cairo_fill(cr);
  642. /* marker */
  643. /* libinput has degrees and 0 is north, cairo has radians and 0 is
  644. * east */
  645. if (w->pad.ring.position != -1) {
  646. pos = (w->pad.ring.position + 270) * M_PI / 180.0;
  647. cairo_set_source_rgb(cr, .0, .0, .0);
  648. cairo_set_line_width(cr, 20);
  649. cairo_arc(cr, rx, ry, 40, pos - M_PI / 8, pos + M_PI / 8);
  650. cairo_stroke(cr);
  651. snprintf(number, sizeof(number), "%d", w->pad.ring.number);
  652. cairo_set_source_rgb(cr, .0, .0, .0);
  653. draw_text(cr, number, rx, ry);
  654. }
  655. if (w->pad.dial.position != -1) {
  656. const int degrees_per_click = 15.0;
  657. double degrees =
  658. fmod(w->pad.dial.position / 120 * degrees_per_click, 360);
  659. pos = (degrees + 270) * M_PI / 180.0;
  660. cairo_set_source_rgb(cr, .0, .0, .0);
  661. cairo_set_line_width(cr, 20);
  662. cairo_arc(cr, rx, ry, 20, pos - M_PI / 12, pos + M_PI / 12);
  663. cairo_stroke(cr);
  664. snprintf(number, sizeof(number), "%d", w->pad.dial.number);
  665. cairo_set_source_rgb(cr, .0, .0, .0);
  666. draw_text(cr, number, rx, ry);
  667. }
  668. cairo_restore(cr);
  669. rx = w->width / 2 - 300;
  670. ry = w->height / 2 + 50;
  671. cairo_save(cr);
  672. cairo_set_source_rgb(cr, .7, .7, .0);
  673. cairo_rectangle(cr, rx, ry, 20, 100);
  674. cairo_fill(cr);
  675. if (w->pad.strip.position != -1) {
  676. pos = w->pad.strip.position * 80;
  677. cairo_set_source_rgb(cr, .0, .0, .0);
  678. cairo_rectangle(cr, rx, ry + pos, 20, 20);
  679. cairo_fill(cr);
  680. snprintf(number, sizeof(number), "%d", w->pad.strip.number);
  681. cairo_set_source_rgb(cr, .0, .0, .0);
  682. draw_text(cr, number, rx + 10, ry - 10);
  683. }
  684. cairo_restore(cr);
  685. }
  686. static inline void
  687. draw_tablet(struct window *w, cairo_t *cr)
  688. {
  689. double x, y;
  690. int first, last;
  691. size_t mask;
  692. int rx, ry;
  693. /* pressure/distance bars */
  694. rx = w->width / 2 + 100;
  695. ry = w->height / 2 + 50;
  696. cairo_save(cr);
  697. cairo_set_source_rgb(cr, .2, .6, .6);
  698. cairo_rectangle(cr, rx, ry, 20, 100);
  699. cairo_stroke(cr);
  700. if (w->tool.distance > 0) {
  701. double pos = w->tool.distance * 100;
  702. cairo_rectangle(cr, rx, ry + 100 - pos, 20, 5);
  703. cairo_fill(cr);
  704. }
  705. if (w->tool.pressure > 0) {
  706. double pos = w->tool.pressure * 100;
  707. if (w->tool.is_down)
  708. cairo_rectangle(cr, rx + 25, ry + 95, 5, 5);
  709. cairo_rectangle(cr, rx, ry + 100 - pos, 20, pos);
  710. cairo_fill(cr);
  711. }
  712. cairo_restore(cr);
  713. /* tablet tool, square for prox-in location */
  714. cairo_save(cr);
  715. cairo_set_source_rgb(cr, .2, .6, .6);
  716. if (w->tool.x_in && w->tool.y_in) {
  717. cairo_rectangle(cr, w->tool.x_in - 15, w->tool.y_in - 15, 30, 30);
  718. cairo_stroke(cr);
  719. }
  720. if (w->tool.x_down && w->tool.y_down) {
  721. cairo_rectangle(cr, w->tool.x_down - 10, w->tool.y_down - 10, 20, 20);
  722. cairo_stroke(cr);
  723. }
  724. if (w->tool.x_up && w->tool.y_up) {
  725. cairo_rectangle(cr, w->tool.x_up - 10, w->tool.y_up - 10, 20, 20);
  726. cairo_stroke(cr);
  727. }
  728. if (w->tool.pressure)
  729. cairo_set_source_rgb(cr, .2, .8, .8);
  730. cairo_translate(cr, w->tool.x, w->tool.y);
  731. /* scale of 2.5 is large enough to make the marker visible around the
  732. physical totem */
  733. cairo_scale(cr, 1.0 + w->tool.size_major * 2.5, 1.0 + w->tool.size_minor * 2.5);
  734. cairo_scale(cr, 1.0 + w->tool.tilt_x / 30.0, 1.0 + w->tool.tilt_y / 30.0);
  735. if (w->tool.rotation)
  736. cairo_rotate(cr, w->tool.rotation * M_PI / 180.0);
  737. if (w->tool.pressure)
  738. cairo_set_source_rgb(cr, .8, .8, .2);
  739. cairo_arc(cr,
  740. 0,
  741. 0,
  742. 1 + 10 * max(w->tool.pressure, w->tool.distance),
  743. 0,
  744. 2 * M_PI);
  745. cairo_fill(cr);
  746. cairo_restore(cr);
  747. /* The line to indicate the origin */
  748. if (w->tool.size_major) {
  749. cairo_save(cr);
  750. cairo_scale(cr, 1.0, 1.0);
  751. cairo_translate(cr, w->tool.x, w->tool.y);
  752. if (w->tool.rotation)
  753. cairo_rotate(cr, w->tool.rotation * M_PI / 180.0);
  754. cairo_set_source_rgb(cr, .0, .0, .0);
  755. cairo_move_to(cr, 0, 0);
  756. cairo_rel_line_to(cr, 0, -w->tool.size_major * 2.5);
  757. cairo_stroke(cr);
  758. cairo_restore(cr);
  759. }
  760. /* tablet deltas */
  761. mask = ARRAY_LENGTH(w->tool.deltas);
  762. first = max(w->tool.ndeltas + 1, mask) - mask;
  763. last = w->tool.ndeltas;
  764. cairo_save(cr);
  765. cairo_set_source_rgb(cr, .8, .8, .2);
  766. x = w->tool.deltas[first % mask].x;
  767. y = w->tool.deltas[first % mask].y;
  768. cairo_move_to(cr, x, y);
  769. for (int i = first + 1; i < last; i++) {
  770. x = w->tool.deltas[i % mask].x;
  771. y = w->tool.deltas[i % mask].y;
  772. cairo_line_to(cr, x, y);
  773. }
  774. cairo_stroke(cr);
  775. cairo_restore(cr);
  776. }
  777. static inline void
  778. draw_pointer(struct window *w, cairo_t *cr)
  779. {
  780. double x, y;
  781. int first, last;
  782. size_t mask;
  783. /* draw pointer sprite */
  784. cairo_set_source_rgb(cr, 0, 0, 0);
  785. cairo_save(cr);
  786. cairo_move_to(cr, w->pointer.x, w->pointer.y);
  787. cairo_rel_line_to(cr, 10, 15);
  788. cairo_rel_line_to(cr, -10, 0);
  789. cairo_rel_line_to(cr, 0, -15);
  790. cairo_fill(cr);
  791. /* draw unaccelerated sprite */
  792. cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
  793. cairo_save(cr);
  794. cairo_move_to(cr, w->unaccelerated.x, w->unaccelerated.y);
  795. cairo_rel_line_to(cr, -5, -10);
  796. cairo_rel_line_to(cr, 10, 0);
  797. cairo_rel_line_to(cr, -5, 10);
  798. cairo_fill(cr);
  799. /* pointer deltas */
  800. mask = ARRAY_LENGTH(w->deltas);
  801. first = max(w->ndeltas + 1, mask) - mask;
  802. last = w->ndeltas;
  803. cairo_set_source_rgb(cr, .8, .5, .2);
  804. x = w->deltas[first % mask].x;
  805. y = w->deltas[first % mask].y;
  806. cairo_move_to(cr, x, y);
  807. for (int i = first + 1; i < last; i++) {
  808. x = w->deltas[i % mask].x;
  809. y = w->deltas[i % mask].y;
  810. cairo_line_to(cr, x, y);
  811. }
  812. cairo_stroke(cr);
  813. cairo_restore(cr);
  814. }
  815. static inline void
  816. draw_background(struct window *w, cairo_t *cr)
  817. {
  818. int x1, x2, y1, y2, x3, y3, x4, y4;
  819. int cols;
  820. /* 10px and 5px grids */
  821. cairo_save(cr);
  822. cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
  823. x1 = w->width / 2 - 200;
  824. y1 = w->height / 2 - 200;
  825. x2 = w->width / 2 + 200;
  826. y2 = w->height / 2 - 200;
  827. for (cols = 1; cols < 10; cols++) {
  828. cairo_move_to(cr, x1 + 10 * cols, y1);
  829. cairo_rel_line_to(cr, 0, 100);
  830. cairo_move_to(cr, x1, y1 + 10 * cols);
  831. cairo_rel_line_to(cr, 100, 0);
  832. cairo_move_to(cr, x2 + 5 * cols, y2);
  833. cairo_rel_line_to(cr, 0, 50);
  834. cairo_move_to(cr, x2, y2 + 5 * cols);
  835. cairo_rel_line_to(cr, 50, 0);
  836. }
  837. /* 3px horiz/vert bar codes */
  838. x3 = w->width / 2 - 200;
  839. y3 = w->height / 2 + 200;
  840. x4 = w->width / 2 + 200;
  841. y4 = w->height / 2 + 100;
  842. for (cols = 0; cols < 50; cols++) {
  843. cairo_move_to(cr, x3 + 3 * cols, y3);
  844. cairo_rel_line_to(cr, 0, 20);
  845. cairo_move_to(cr, x4, y4 + 3 * cols);
  846. cairo_rel_line_to(cr, 20, 0);
  847. }
  848. cairo_stroke(cr);
  849. /* round targets */
  850. for (int i = 0; i <= 3; i++) {
  851. x1 = w->width * i / 4.0;
  852. x2 = w->width * i / 4.0;
  853. y1 = w->height * 1.0 / 4.0;
  854. y2 = w->height * 3.0 / 4.0;
  855. cairo_arc(cr, x1, y1, 10, 0, 2 * M_PI);
  856. cairo_stroke(cr);
  857. cairo_arc(cr, x2, y2, 10, 0, 2 * M_PI);
  858. cairo_stroke(cr);
  859. }
  860. cairo_restore(cr);
  861. }
  862. static gboolean
  863. draw(GtkWidget *widget, cairo_t *cr, gpointer data)
  864. {
  865. struct window *w = data;
  866. cairo_set_font_size(cr, 12.0);
  867. cairo_set_source_rgb(cr, 1, 1, 1);
  868. cairo_rectangle(cr, 0, 0, w->width, w->height);
  869. cairo_fill(cr);
  870. draw_background(w, cr);
  871. draw_evdev_rel(w, cr);
  872. draw_evdev_abs(w, cr);
  873. draw_pad(w, cr);
  874. draw_tablet(w, cr);
  875. draw_gestures(w, cr);
  876. draw_scrollbars(w, cr);
  877. draw_touchpoints(w, cr);
  878. draw_abs_pointer(w, cr);
  879. draw_buttons(w, cr);
  880. draw_pointer(w, cr);
  881. return TRUE;
  882. }
  883. #ifdef HAVE_GTK4
  884. static void
  885. draw_gtk4(GtkDrawingArea *widget, cairo_t *cr, int width, int height, gpointer data)
  886. {
  887. draw(GTK_WIDGET(widget), cr, data);
  888. }
  889. #endif
  890. static void
  891. window_place_ui_elements(GtkWidget *widget, struct window *w)
  892. {
  893. #ifdef HAVE_GTK4
  894. w->width = gtk_widget_get_width(w->area);
  895. w->height = gtk_widget_get_height(w->area);
  896. #else
  897. gtk_window_get_size(GTK_WINDOW(widget), &w->width, &w->height);
  898. #endif
  899. w->pointer.x = w->width / 2;
  900. w->pointer.y = w->height / 2;
  901. w->unaccelerated.x = w->width / 2;
  902. w->unaccelerated.y = w->height / 2;
  903. w->deltas[0].x = w->pointer.x;
  904. w->deltas[0].y = w->pointer.y;
  905. w->scroll.vx = w->width / 2;
  906. w->scroll.vy = w->height / 2;
  907. w->scroll.hx = w->width / 2;
  908. w->scroll.hy = w->height / 2;
  909. w->scroll.vx_discrete = w->width / 2;
  910. w->scroll.vy_discrete = w->height / 2;
  911. w->scroll.hx_discrete = w->width / 2;
  912. w->scroll.hy_discrete = w->height / 2;
  913. w->swipe.x = w->width / 2;
  914. w->swipe.y = w->height / 2;
  915. w->pinch.scale = 1.0;
  916. w->pinch.x = w->width / 2;
  917. w->pinch.y = w->height / 2;
  918. }
  919. #ifdef HAVE_GTK4
  920. static void
  921. map_event_cb(GtkDrawingArea *widget, int width, int height, gpointer data)
  922. {
  923. struct window *w = data;
  924. window_place_ui_elements(GTK_WIDGET(widget), w);
  925. gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(w->area), draw_gtk4, w, NULL);
  926. gtk_widget_set_cursor_from_name(w->win, "none");
  927. window_lock_pointer(w);
  928. }
  929. #else
  930. static void
  931. map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
  932. {
  933. struct window *w = data;
  934. GdkDisplay *display;
  935. GdkWindow *window;
  936. window_place_ui_elements(widget, w);
  937. g_signal_connect(G_OBJECT(w->area), "draw", G_CALLBACK(draw), w);
  938. window = gdk_event_get_window(event);
  939. display = gdk_window_get_display(window);
  940. gdk_window_set_cursor(gtk_widget_get_window(w->win),
  941. gdk_cursor_new_for_display(display, GDK_BLANK_CURSOR));
  942. window_lock_pointer(w);
  943. }
  944. #endif
  945. static void
  946. window_quit(struct window *w)
  947. {
  948. g_main_loop_quit(w->event_loop);
  949. }
  950. #ifdef HAVE_GTK4
  951. static gboolean
  952. window_delete_event_cb(GtkWindow *window, gpointer data)
  953. {
  954. struct window *w = data;
  955. window_quit(w);
  956. return TRUE;
  957. }
  958. #else
  959. static void
  960. window_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
  961. {
  962. struct window *w = data;
  963. window_quit(w);
  964. }
  965. #endif
  966. static void
  967. window_init(struct window *w)
  968. {
  969. list_init(&w->evdev_devices);
  970. #ifdef HAVE_GTK4
  971. w->win = gtk_window_new();
  972. #else
  973. w->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  974. #endif
  975. if (getenv("LIBINPUT_RUNNING_TEST_SUITE")) {
  976. #ifdef HAVE_GTK4
  977. gtk_window_minimize(GTK_WINDOW(w->win));
  978. #else
  979. gtk_window_iconify(GTK_WINDOW(w->win));
  980. #endif
  981. }
  982. gtk_window_set_title(GTK_WINDOW(w->win), "libinput debugging tool");
  983. gtk_window_set_default_size(GTK_WINDOW(w->win), 1024, 768);
  984. gtk_window_maximize(GTK_WINDOW(w->win));
  985. gtk_window_set_resizable(GTK_WINDOW(w->win), TRUE);
  986. gtk_widget_realize(w->win);
  987. w->area = gtk_drawing_area_new();
  988. #ifdef HAVE_GTK4
  989. g_signal_connect(G_OBJECT(w->area), "resize", G_CALLBACK(map_event_cb), w);
  990. g_signal_connect(G_OBJECT(w->win),
  991. "close-request",
  992. G_CALLBACK(window_delete_event_cb),
  993. w);
  994. gtk_window_set_child(GTK_WINDOW(w->win), w->area);
  995. gtk_widget_set_visible(w->win, TRUE);
  996. #else
  997. g_signal_connect(G_OBJECT(w->win), "map-event", G_CALLBACK(map_event_cb), w);
  998. g_signal_connect(G_OBJECT(w->win),
  999. "delete-event",
  1000. G_CALLBACK(window_delete_event_cb),
  1001. w);
  1002. gtk_widget_set_events(w->win, 0);
  1003. gtk_widget_set_events(w->area, 0);
  1004. gtk_container_add(GTK_CONTAINER(w->win), w->area);
  1005. gtk_widget_show_all(w->win);
  1006. #endif
  1007. w->pad.ring.position = -1;
  1008. w->pad.strip.position = -1;
  1009. w->pad.dial.position = -1;
  1010. }
  1011. static void
  1012. window_cleanup(struct window *w)
  1013. {
  1014. ARRAY_FOR_EACH(w->devices, dev) {
  1015. if (*dev)
  1016. libinput_device_unref(*dev);
  1017. }
  1018. }
  1019. static void
  1020. change_ptraccel(struct window *w, double amount)
  1021. {
  1022. ARRAY_FOR_EACH(w->devices, dev) {
  1023. double speed;
  1024. enum libinput_config_status status;
  1025. if (*dev == NULL)
  1026. continue;
  1027. if (!libinput_device_config_accel_is_available(*dev))
  1028. continue;
  1029. speed = libinput_device_config_accel_get_speed(*dev);
  1030. speed = clip(speed + amount, -1, 1);
  1031. status = libinput_device_config_accel_set_speed(*dev, speed);
  1032. if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) {
  1033. msg("%s: failed to change accel to %.2f (%s)\n",
  1034. libinput_device_get_name(*dev),
  1035. speed,
  1036. libinput_config_status_to_str(status));
  1037. } else {
  1038. printf("%s: speed is %.2f\n",
  1039. libinput_device_get_name(*dev),
  1040. speed);
  1041. }
  1042. }
  1043. }
  1044. static int
  1045. handle_event_evdev(GIOChannel *source, GIOCondition condition, gpointer data)
  1046. {
  1047. struct libinput_device *dev = data;
  1048. struct libinput *li = libinput_device_get_context(dev);
  1049. struct window *w = libinput_get_user_data(li);
  1050. struct evdev_device *d, *device = NULL;
  1051. struct input_event e;
  1052. int rc;
  1053. list_for_each(d, &w->evdev_devices, node) {
  1054. if (d->libinput_device == dev) {
  1055. device = d;
  1056. break;
  1057. }
  1058. }
  1059. if (device == NULL) {
  1060. msg("Unknown device: %s\n", libinput_device_get_name(dev));
  1061. return FALSE;
  1062. }
  1063. do {
  1064. rc = libevdev_next_event(device->evdev, LIBEVDEV_READ_FLAG_NORMAL, &e);
  1065. if (rc == -EAGAIN) {
  1066. break;
  1067. } else if (rc == LIBEVDEV_READ_STATUS_SYNC) {
  1068. msg("SYN_DROPPED received\n");
  1069. goto out;
  1070. } else if (rc != LIBEVDEV_READ_STATUS_SUCCESS) {
  1071. msg("Error reading event: %s\n", strerror(-rc));
  1072. goto out;
  1073. }
  1074. #define EVENT(t_, c_) (t_ << 16 | c_)
  1075. switch (EVENT(e.type, e.code)) {
  1076. case EVENT(EV_REL, REL_X):
  1077. w->evdev.rel_x = e.value;
  1078. break;
  1079. case EVENT(EV_REL, REL_Y):
  1080. w->evdev.rel_y = e.value;
  1081. break;
  1082. case EVENT(EV_ABS, ABS_MT_SLOT):
  1083. w->evdev.slot = min((unsigned int)e.value,
  1084. ARRAY_LENGTH(w->evdev.slots) - 1);
  1085. w->evdev.device = (uintptr_t)dev;
  1086. break;
  1087. case EVENT(EV_ABS, ABS_MT_TRACKING_ID):
  1088. w->evdev.slots[w->evdev.slot].active = (e.value != -1);
  1089. w->evdev.device = (uintptr_t)dev;
  1090. break;
  1091. case EVENT(EV_ABS, ABS_X):
  1092. w->evdev.x = e.value;
  1093. w->evdev.device = (uintptr_t)dev;
  1094. break;
  1095. case EVENT(EV_ABS, ABS_Y):
  1096. w->evdev.y = e.value;
  1097. w->evdev.device = (uintptr_t)dev;
  1098. break;
  1099. case EVENT(EV_ABS, ABS_MT_POSITION_X):
  1100. w->evdev.slots[w->evdev.slot].x = e.value;
  1101. w->evdev.device = (uintptr_t)dev;
  1102. break;
  1103. case EVENT(EV_ABS, ABS_MT_POSITION_Y):
  1104. w->evdev.slots[w->evdev.slot].y = e.value;
  1105. w->evdev.device = (uintptr_t)dev;
  1106. break;
  1107. }
  1108. } while (rc == LIBEVDEV_READ_STATUS_SUCCESS);
  1109. gtk_widget_queue_draw(w->area);
  1110. out:
  1111. return TRUE;
  1112. }
  1113. static void
  1114. register_evdev_device(struct window *w, struct libinput_device *dev)
  1115. {
  1116. GIOChannel *c;
  1117. struct udev_device *ud;
  1118. struct libevdev *evdev;
  1119. const char *device_node;
  1120. int fd;
  1121. struct evdev_device *d;
  1122. ud = libinput_device_get_udev_device(dev);
  1123. device_node = udev_device_get_devnode(ud);
  1124. fd = open(device_node, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
  1125. if (fd == -1) {
  1126. msg("failed to open %s, evdev events unavailable\n", device_node);
  1127. goto out;
  1128. }
  1129. if (libevdev_new_from_fd(fd, &evdev) != 0) {
  1130. msg("failed to create context for %s, evdev events unavailable\n",
  1131. device_node);
  1132. goto out;
  1133. }
  1134. d = zalloc(sizeof *d);
  1135. list_append(&w->evdev_devices, &d->node);
  1136. d->fd = fd;
  1137. d->evdev = evdev;
  1138. d->libinput_device = libinput_device_ref(dev);
  1139. c = g_io_channel_unix_new(fd);
  1140. g_io_channel_set_encoding(c, NULL, NULL);
  1141. d->source_id =
  1142. g_io_add_watch(c, G_IO_IN, handle_event_evdev, d->libinput_device);
  1143. fd = -1;
  1144. out:
  1145. xclose(&fd);
  1146. udev_device_unref(ud);
  1147. }
  1148. static void
  1149. unregister_evdev_device(struct window *w, struct libinput_device *dev)
  1150. {
  1151. struct evdev_device *d;
  1152. list_for_each_safe(d, &w->evdev_devices, node) {
  1153. if (d->libinput_device != dev)
  1154. continue;
  1155. list_remove(&d->node);
  1156. g_source_remove(d->source_id);
  1157. free(libinput_device_get_user_data(d->libinput_device));
  1158. libinput_device_unref(d->libinput_device);
  1159. libevdev_free(d->evdev);
  1160. xclose(&d->fd);
  1161. free(d);
  1162. w->evdev.last_device = 0;
  1163. break;
  1164. }
  1165. }
  1166. static void
  1167. handle_event_device_notify(struct libinput_event *ev)
  1168. {
  1169. struct libinput_device *dev = libinput_event_get_device(ev);
  1170. struct libinput *li;
  1171. struct window *w;
  1172. const char *type;
  1173. li = libinput_event_get_context(ev);
  1174. w = libinput_get_user_data(li);
  1175. if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) {
  1176. type = "added";
  1177. register_evdev_device(w, dev);
  1178. tools_device_apply_config(libinput_event_get_device(ev), &w->options);
  1179. } else {
  1180. type = "removed";
  1181. unregister_evdev_device(w, dev);
  1182. }
  1183. msg("%s %-30s %s\n",
  1184. libinput_device_get_sysname(dev),
  1185. libinput_device_get_name(dev),
  1186. type);
  1187. if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) {
  1188. ARRAY_FOR_EACH(w->devices, device) {
  1189. if (*device == NULL) {
  1190. *device = libinput_device_ref(dev);
  1191. break;
  1192. }
  1193. }
  1194. } else {
  1195. ARRAY_FOR_EACH(w->devices, device) {
  1196. if (*device == dev) {
  1197. libinput_device_unref(*device);
  1198. *device = NULL;
  1199. break;
  1200. }
  1201. }
  1202. }
  1203. }
  1204. static void
  1205. handle_event_motion(struct libinput_event *ev, struct window *w)
  1206. {
  1207. struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
  1208. double dx = libinput_event_pointer_get_dx(p),
  1209. dy = libinput_event_pointer_get_dy(p);
  1210. double dx_unaccel = libinput_event_pointer_get_dx_unaccelerated(p),
  1211. dy_unaccel = libinput_event_pointer_get_dy_unaccelerated(p);
  1212. struct point point;
  1213. const int mask = ARRAY_LENGTH(w->deltas);
  1214. size_t idx;
  1215. w->pointer.x = clip(w->pointer.x + dx, 0.0, w->width);
  1216. w->pointer.y = clip(w->pointer.y + dy, 0.0, w->height);
  1217. w->unaccelerated.x = clip(w->unaccelerated.x + dx_unaccel, 0.0, w->width);
  1218. w->unaccelerated.y = clip(w->unaccelerated.y + dy_unaccel, 0.0, w->height);
  1219. idx = w->ndeltas % mask;
  1220. point = w->deltas[idx];
  1221. idx = (w->ndeltas + 1) % mask;
  1222. point.x += dx_unaccel;
  1223. point.y += dy_unaccel;
  1224. w->deltas[idx] = point;
  1225. w->ndeltas++;
  1226. }
  1227. static void
  1228. handle_event_absmotion(struct libinput_event *ev, struct window *w)
  1229. {
  1230. struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
  1231. double x = libinput_event_pointer_get_absolute_x_transformed(p, w->width),
  1232. y = libinput_event_pointer_get_absolute_y_transformed(p, w->height);
  1233. w->abs.x = x;
  1234. w->abs.y = y;
  1235. }
  1236. static void
  1237. handle_event_touch(struct libinput_event *ev, struct window *w)
  1238. {
  1239. struct libinput_event_touch *t = libinput_event_get_touch_event(ev);
  1240. int slot = libinput_event_touch_get_seat_slot(t);
  1241. struct touch *touch;
  1242. double x, y;
  1243. if (slot == -1 || slot >= (int)ARRAY_LENGTH(w->touches))
  1244. return;
  1245. touch = &w->touches[slot];
  1246. switch (libinput_event_get_type(ev)) {
  1247. case LIBINPUT_EVENT_TOUCH_UP:
  1248. touch->state = TOUCH_ENDED;
  1249. return;
  1250. case LIBINPUT_EVENT_TOUCH_CANCEL:
  1251. touch->state = TOUCH_CANCELLED;
  1252. return;
  1253. default:
  1254. break;
  1255. }
  1256. x = libinput_event_touch_get_x_transformed(t, w->width),
  1257. y = libinput_event_touch_get_y_transformed(t, w->height);
  1258. touch->state = TOUCH_ACTIVE;
  1259. touch->x = (int)x;
  1260. touch->y = (int)y;
  1261. }
  1262. static void
  1263. handle_event_axis(struct libinput_event *ev, struct window *w)
  1264. {
  1265. struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
  1266. double value;
  1267. enum libinput_pointer_axis axis;
  1268. enum libinput_event_type type;
  1269. type = libinput_event_get_type(ev);
  1270. axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
  1271. if (libinput_event_pointer_has_axis(p, axis)) {
  1272. value = libinput_event_pointer_get_scroll_value(p, axis);
  1273. w->scroll.vy += value;
  1274. w->scroll.vy = clip(w->scroll.vy, 0, w->height);
  1275. if (type == LIBINPUT_EVENT_POINTER_SCROLL_WHEEL) {
  1276. w->scroll.vy_discrete += value;
  1277. w->scroll.vy_discrete =
  1278. clip(w->scroll.vy_discrete, 0, w->height);
  1279. }
  1280. }
  1281. axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
  1282. if (libinput_event_pointer_has_axis(p, axis)) {
  1283. value = libinput_event_pointer_get_scroll_value(p, axis);
  1284. w->scroll.hx += value;
  1285. w->scroll.hx = clip(w->scroll.hx, 0, w->width);
  1286. if (type == LIBINPUT_EVENT_POINTER_SCROLL_WHEEL) {
  1287. w->scroll.hx_discrete += value;
  1288. w->scroll.hx_discrete =
  1289. clip(w->scroll.hx_discrete, 0, w->width);
  1290. }
  1291. }
  1292. }
  1293. static int
  1294. handle_event_keyboard(struct libinput_event *ev, struct window *w)
  1295. {
  1296. struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev);
  1297. unsigned int key = libinput_event_keyboard_get_key(k);
  1298. if (libinput_event_keyboard_get_key_state(k) == LIBINPUT_KEY_STATE_RELEASED)
  1299. return 0;
  1300. switch (key) {
  1301. case KEY_ESC:
  1302. return 1;
  1303. case KEY_UP:
  1304. change_ptraccel(w, 0.1);
  1305. break;
  1306. case KEY_DOWN:
  1307. change_ptraccel(w, -0.1);
  1308. break;
  1309. default:
  1310. break;
  1311. }
  1312. return 0;
  1313. }
  1314. static void
  1315. handle_event_button(struct libinput_event *ev, struct window *w)
  1316. {
  1317. struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
  1318. unsigned int button = libinput_event_pointer_get_button(p);
  1319. bool is_press;
  1320. is_press = libinput_event_pointer_get_button_state(p) ==
  1321. LIBINPUT_BUTTON_STATE_PRESSED;
  1322. switch (button) {
  1323. case BTN_LEFT:
  1324. w->buttons.l = is_press;
  1325. break;
  1326. case BTN_RIGHT:
  1327. w->buttons.r = is_press;
  1328. break;
  1329. case BTN_MIDDLE:
  1330. w->buttons.m = is_press;
  1331. break;
  1332. default:
  1333. w->buttons.other = is_press;
  1334. w->buttons.other_name = libevdev_event_code_get_name(EV_KEY, button);
  1335. }
  1336. }
  1337. static void
  1338. handle_event_swipe(struct libinput_event *ev, struct window *w)
  1339. {
  1340. struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev);
  1341. int nfingers;
  1342. double dx, dy;
  1343. nfingers = libinput_event_gesture_get_finger_count(g);
  1344. switch (libinput_event_get_type(ev)) {
  1345. case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
  1346. w->swipe.nfingers = nfingers;
  1347. w->swipe.x = w->width / 2;
  1348. w->swipe.y = w->height / 2;
  1349. break;
  1350. case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
  1351. dx = libinput_event_gesture_get_dx(g);
  1352. dy = libinput_event_gesture_get_dy(g);
  1353. w->swipe.x += dx;
  1354. w->swipe.y += dy;
  1355. break;
  1356. case LIBINPUT_EVENT_GESTURE_SWIPE_END:
  1357. w->swipe.nfingers = 0;
  1358. w->swipe.x = w->width / 2;
  1359. w->swipe.y = w->height / 2;
  1360. break;
  1361. default:
  1362. abort();
  1363. }
  1364. }
  1365. static void
  1366. handle_event_pinch(struct libinput_event *ev, struct window *w)
  1367. {
  1368. struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev);
  1369. int nfingers;
  1370. double dx, dy;
  1371. nfingers = libinput_event_gesture_get_finger_count(g);
  1372. switch (libinput_event_get_type(ev)) {
  1373. case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
  1374. w->pinch.nfingers = nfingers;
  1375. w->pinch.x = w->width / 2;
  1376. w->pinch.y = w->height / 2;
  1377. break;
  1378. case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
  1379. dx = libinput_event_gesture_get_dx(g);
  1380. dy = libinput_event_gesture_get_dy(g);
  1381. w->pinch.x += dx;
  1382. w->pinch.y += dy;
  1383. w->pinch.scale = libinput_event_gesture_get_scale(g);
  1384. w->pinch.angle += libinput_event_gesture_get_angle_delta(g);
  1385. break;
  1386. case LIBINPUT_EVENT_GESTURE_PINCH_END:
  1387. w->pinch.nfingers = 0;
  1388. w->pinch.x = w->width / 2;
  1389. w->pinch.y = w->height / 2;
  1390. w->pinch.angle = 0.0;
  1391. w->pinch.scale = 1.0;
  1392. break;
  1393. default:
  1394. abort();
  1395. }
  1396. }
  1397. static void
  1398. handle_event_hold(struct libinput_event *ev, struct window *w)
  1399. {
  1400. struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev);
  1401. int nfingers;
  1402. nfingers = libinput_event_gesture_get_finger_count(g);
  1403. switch (libinput_event_get_type(ev)) {
  1404. case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN:
  1405. w->hold.nfingers = nfingers;
  1406. w->hold.active = true;
  1407. break;
  1408. case LIBINPUT_EVENT_GESTURE_HOLD_END:
  1409. w->hold.nfingers = nfingers;
  1410. w->hold.active = false;
  1411. break;
  1412. default:
  1413. abort();
  1414. }
  1415. }
  1416. static void
  1417. handle_event_tablet(struct libinput_event *ev, struct window *w)
  1418. {
  1419. struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(ev);
  1420. struct libinput_tablet_tool *tool = libinput_event_tablet_tool_get_tool(t);
  1421. double x, y;
  1422. struct point point;
  1423. int idx;
  1424. const int mask = ARRAY_LENGTH(w->tool.deltas);
  1425. bool is_press;
  1426. unsigned int button;
  1427. x = libinput_event_tablet_tool_get_x_transformed(t, w->width);
  1428. y = libinput_event_tablet_tool_get_y_transformed(t, w->height);
  1429. switch (libinput_event_get_type(ev)) {
  1430. case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
  1431. tools_tablet_tool_apply_config(tool, &w->options);
  1432. if (libinput_event_tablet_tool_get_proximity_state(t) ==
  1433. LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT) {
  1434. w->tool.x_in = 0;
  1435. w->tool.y_in = 0;
  1436. w->tool.x_down = 0;
  1437. w->tool.y_down = 0;
  1438. w->tool.x_up = 0;
  1439. w->tool.y_up = 0;
  1440. } else {
  1441. w->tool.x_in = x;
  1442. w->tool.y_in = y;
  1443. w->tool.ndeltas = 0;
  1444. w->tool.deltas[0].x = w->width / 2;
  1445. w->tool.deltas[0].y = w->height / 2;
  1446. }
  1447. break;
  1448. case LIBINPUT_EVENT_TABLET_TOOL_TIP:
  1449. w->tool.pressure = libinput_event_tablet_tool_get_pressure(t);
  1450. w->tool.distance = libinput_event_tablet_tool_get_distance(t);
  1451. w->tool.tilt_x = libinput_event_tablet_tool_get_tilt_x(t);
  1452. w->tool.tilt_y = libinput_event_tablet_tool_get_tilt_y(t);
  1453. if (libinput_event_tablet_tool_get_tip_state(t) ==
  1454. LIBINPUT_TABLET_TOOL_TIP_DOWN) {
  1455. w->tool.x_down = x;
  1456. w->tool.y_down = y;
  1457. w->tool.is_down = true;
  1458. } else {
  1459. w->tool.x_up = x;
  1460. w->tool.y_up = y;
  1461. w->tool.is_down = false;
  1462. }
  1463. _fallthrough_;
  1464. case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
  1465. w->tool.x = x;
  1466. w->tool.y = y;
  1467. w->tool.pressure = libinput_event_tablet_tool_get_pressure(t);
  1468. w->tool.distance = libinput_event_tablet_tool_get_distance(t);
  1469. w->tool.tilt_x = libinput_event_tablet_tool_get_tilt_x(t);
  1470. w->tool.tilt_y = libinput_event_tablet_tool_get_tilt_y(t);
  1471. w->tool.rotation = libinput_event_tablet_tool_get_rotation(t);
  1472. w->tool.size_major = libinput_event_tablet_tool_get_size_major(t);
  1473. w->tool.size_minor = libinput_event_tablet_tool_get_size_minor(t);
  1474. /* Add the delta to the last position and store them as abs
  1475. * coordinates */
  1476. idx = w->tool.ndeltas % mask;
  1477. point = w->tool.deltas[idx];
  1478. idx = (w->tool.ndeltas + 1) % mask;
  1479. point.x += libinput_event_tablet_tool_get_dx(t);
  1480. point.y += libinput_event_tablet_tool_get_dy(t);
  1481. w->tool.deltas[idx] = point;
  1482. w->tool.ndeltas++;
  1483. break;
  1484. case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
  1485. is_press = libinput_event_tablet_tool_get_button_state(t) ==
  1486. LIBINPUT_BUTTON_STATE_PRESSED;
  1487. button = libinput_event_tablet_tool_get_button(t);
  1488. w->buttons.other = is_press;
  1489. w->buttons.other_name = libevdev_event_code_get_name(EV_KEY, button);
  1490. break;
  1491. default:
  1492. abort();
  1493. }
  1494. }
  1495. static void
  1496. handle_event_tablet_pad(struct libinput_event *ev, struct window *w)
  1497. {
  1498. struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
  1499. bool is_press;
  1500. unsigned int button;
  1501. static const char *pad_buttons[] = { "Pad 0", "Pad 1", "Pad 2", "Pad 3",
  1502. "Pad 4", "Pad 5", "Pad 6", "Pad 7",
  1503. "Pad 8", "Pad 9", "Pad >= 10" };
  1504. double position, delta;
  1505. double number;
  1506. switch (libinput_event_get_type(ev)) {
  1507. case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
  1508. is_press = libinput_event_tablet_pad_get_button_state(p) ==
  1509. LIBINPUT_BUTTON_STATE_PRESSED;
  1510. button = libinput_event_tablet_pad_get_button_number(p);
  1511. w->buttons.other = is_press;
  1512. w->buttons.other_name = pad_buttons[min(button, 10)];
  1513. break;
  1514. case LIBINPUT_EVENT_TABLET_PAD_RING:
  1515. position = libinput_event_tablet_pad_get_ring_position(p);
  1516. number = libinput_event_tablet_pad_get_ring_number(p);
  1517. w->pad.ring.number = number;
  1518. w->pad.ring.position = position;
  1519. break;
  1520. case LIBINPUT_EVENT_TABLET_PAD_STRIP:
  1521. position = libinput_event_tablet_pad_get_strip_position(p);
  1522. number = libinput_event_tablet_pad_get_strip_number(p);
  1523. w->pad.strip.number = number;
  1524. w->pad.strip.position = position;
  1525. break;
  1526. case LIBINPUT_EVENT_TABLET_PAD_DIAL:
  1527. delta = libinput_event_tablet_pad_get_dial_delta_v120(p);
  1528. number = libinput_event_tablet_pad_get_dial_number(p);
  1529. if (w->pad.dial.number != number)
  1530. w->pad.dial.position = -delta;
  1531. w->pad.dial.number = number;
  1532. w->pad.dial.position += delta;
  1533. break;
  1534. default:
  1535. abort();
  1536. }
  1537. }
  1538. static gboolean
  1539. handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)
  1540. {
  1541. struct libinput *li = data;
  1542. struct window *w = libinput_get_user_data(li);
  1543. struct libinput_event *ev;
  1544. tools_dispatch(li);
  1545. while ((ev = libinput_get_event(li))) {
  1546. switch (libinput_event_get_type(ev)) {
  1547. case LIBINPUT_EVENT_NONE:
  1548. abort();
  1549. case LIBINPUT_EVENT_DEVICE_ADDED:
  1550. case LIBINPUT_EVENT_DEVICE_REMOVED:
  1551. handle_event_device_notify(ev);
  1552. break;
  1553. case LIBINPUT_EVENT_POINTER_MOTION:
  1554. handle_event_motion(ev, w);
  1555. break;
  1556. case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
  1557. handle_event_absmotion(ev, w);
  1558. break;
  1559. case LIBINPUT_EVENT_TOUCH_DOWN:
  1560. case LIBINPUT_EVENT_TOUCH_MOTION:
  1561. case LIBINPUT_EVENT_TOUCH_UP:
  1562. case LIBINPUT_EVENT_TOUCH_CANCEL:
  1563. handle_event_touch(ev, w);
  1564. break;
  1565. case LIBINPUT_EVENT_TOUCH_FRAME:
  1566. break;
  1567. case LIBINPUT_EVENT_POINTER_AXIS:
  1568. /* ignore */
  1569. break;
  1570. case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
  1571. case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
  1572. case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS:
  1573. handle_event_axis(ev, w);
  1574. break;
  1575. case LIBINPUT_EVENT_POINTER_BUTTON:
  1576. handle_event_button(ev, w);
  1577. break;
  1578. case LIBINPUT_EVENT_KEYBOARD_KEY:
  1579. if (handle_event_keyboard(ev, w)) {
  1580. libinput_event_destroy(ev);
  1581. window_quit(w);
  1582. return FALSE;
  1583. }
  1584. break;
  1585. case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
  1586. case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
  1587. case LIBINPUT_EVENT_GESTURE_SWIPE_END:
  1588. handle_event_swipe(ev, w);
  1589. break;
  1590. case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
  1591. case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
  1592. case LIBINPUT_EVENT_GESTURE_PINCH_END:
  1593. handle_event_pinch(ev, w);
  1594. break;
  1595. case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN:
  1596. case LIBINPUT_EVENT_GESTURE_HOLD_END:
  1597. handle_event_hold(ev, w);
  1598. break;
  1599. case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
  1600. case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
  1601. case LIBINPUT_EVENT_TABLET_TOOL_TIP:
  1602. case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
  1603. handle_event_tablet(ev, w);
  1604. break;
  1605. case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
  1606. case LIBINPUT_EVENT_TABLET_PAD_RING:
  1607. case LIBINPUT_EVENT_TABLET_PAD_STRIP:
  1608. case LIBINPUT_EVENT_TABLET_PAD_DIAL:
  1609. handle_event_tablet_pad(ev, w);
  1610. break;
  1611. case LIBINPUT_EVENT_TABLET_PAD_KEY:
  1612. break;
  1613. case LIBINPUT_EVENT_SWITCH_TOGGLE:
  1614. break;
  1615. }
  1616. libinput_event_destroy(ev);
  1617. }
  1618. gtk_widget_queue_draw(w->area);
  1619. return TRUE;
  1620. }
  1621. static void
  1622. sockets_init(struct libinput *li)
  1623. {
  1624. GIOChannel *c = g_io_channel_unix_new(libinput_get_fd(li));
  1625. g_io_channel_set_encoding(c, NULL, NULL);
  1626. g_io_add_watch(c, G_IO_IN, handle_event_libinput, li);
  1627. }
  1628. static void
  1629. usage(struct option *opts)
  1630. {
  1631. printf("Usage: libinput debug-gui [options] [--udev <seat>|[--device] /dev/input/event0]\n");
  1632. if (opts)
  1633. tools_print_usage_option_list(opts);
  1634. }
  1635. static gboolean
  1636. signal_handler(void *data)
  1637. {
  1638. struct libinput *li = data;
  1639. struct window *w = libinput_get_user_data(li);
  1640. window_quit(w);
  1641. return FALSE;
  1642. }
  1643. int
  1644. main(int argc, char **argv)
  1645. {
  1646. struct window w = { 0 };
  1647. struct tools_options options;
  1648. struct libinput *li;
  1649. enum tools_backend backend = BACKEND_NONE;
  1650. const char *seat_or_device[2] = { "seat0", NULL };
  1651. bool verbose = false;
  1652. bool gtk_init = false;
  1653. #ifdef HAVE_GTK4
  1654. gtk_init = gtk_init_check();
  1655. #else
  1656. gtk_init = gtk_init_check(&argc, &argv);
  1657. #endif
  1658. if (!gtk_init)
  1659. return 77;
  1660. tools_init_options(&options);
  1661. while (1) {
  1662. int c;
  1663. int option_index = 0;
  1664. enum {
  1665. OPT_DEVICE = 1,
  1666. OPT_UDEV,
  1667. OPT_GRAB,
  1668. OPT_VERBOSE,
  1669. };
  1670. /* clang-format off */
  1671. static struct option opts[] = {
  1672. CONFIGURATION_OPTIONS,
  1673. { "help", no_argument, 0, 'h' },
  1674. { "device", required_argument, 0, OPT_DEVICE },
  1675. { "udev", required_argument, 0, OPT_UDEV },
  1676. { "grab", no_argument, 0, OPT_GRAB },
  1677. { "verbose", no_argument, 0, OPT_VERBOSE },
  1678. { 0, 0, 0, 0},
  1679. };
  1680. /* clang-format on */
  1681. c = getopt_long(argc, argv, "h", opts, &option_index);
  1682. if (c == -1)
  1683. break;
  1684. switch (c) {
  1685. case '?':
  1686. exit(EXIT_INVALID_USAGE);
  1687. break;
  1688. case 'h':
  1689. usage(opts);
  1690. exit(0);
  1691. break;
  1692. case OPT_DEVICE:
  1693. backend = BACKEND_DEVICE;
  1694. seat_or_device[0] = optarg;
  1695. break;
  1696. case OPT_UDEV:
  1697. backend = BACKEND_UDEV;
  1698. seat_or_device[0] = optarg;
  1699. break;
  1700. case OPT_GRAB:
  1701. w.grab = true;
  1702. break;
  1703. case OPT_VERBOSE:
  1704. verbose = true;
  1705. break;
  1706. default:
  1707. if (tools_parse_option(c, optarg, &options) != 0) {
  1708. usage(NULL);
  1709. return EXIT_INVALID_USAGE;
  1710. }
  1711. break;
  1712. }
  1713. }
  1714. if (optind < argc) {
  1715. if (optind < argc - 1 || backend != BACKEND_NONE) {
  1716. usage(NULL);
  1717. return EXIT_INVALID_USAGE;
  1718. }
  1719. backend = BACKEND_DEVICE;
  1720. seat_or_device[0] = argv[optind];
  1721. } else if (backend == BACKEND_NONE) {
  1722. backend = BACKEND_UDEV;
  1723. }
  1724. bool with_plugins = (options.plugins == 1);
  1725. li = tools_open_backend(backend,
  1726. seat_or_device,
  1727. verbose,
  1728. &w.grab,
  1729. with_plugins,
  1730. steal(&options.plugin_paths));
  1731. if (!li)
  1732. return EXIT_FAILURE;
  1733. libinput_set_user_data(li, &w);
  1734. g_unix_signal_add(SIGINT, signal_handler, li);
  1735. window_init(&w);
  1736. w.options = options;
  1737. sockets_init(li);
  1738. handle_event_libinput(NULL, 0, li);
  1739. w.event_loop = g_main_loop_new(NULL, FALSE);
  1740. g_main_loop_run(w.event_loop);
  1741. window_unlock_pointer(&w);
  1742. window_cleanup(&w);
  1743. libinput_unref(li);
  1744. return EXIT_SUCCESS;
  1745. }