| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038 |
- /*
- * Copyright © 2014 Red Hat, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
- #include <config.h>
- #include <assert.h>
- #include <cairo.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <getopt.h>
- #include <glib-unix.h>
- #include <glib.h>
- #include <gtk/gtk.h>
- #include <libevdev/libevdev.h>
- #include <libinput.h>
- #include <linux/input.h>
- #include <math.h>
- #include <stdarg.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include "util-files.h"
- #include "util-list.h"
- #include "util-macros.h"
- #include "util-strings.h"
- #include "shared.h"
- #ifdef HAVE_GTK_WAYLAND
- #include <wayland-client.h>
- #include "pointer-constraints-unstable-v1-client-protocol.h"
- #ifdef HAVE_GTK4
- #include <gdk/wayland/gdkwayland.h>
- #else
- #include <gdk/gdkwayland.h>
- #endif
- #endif
- #ifdef HAVE_GTK_X11
- #include <X11/X.h>
- #include <X11/Xlib.h>
- #ifdef HAVE_GTK4
- #include <gdk/x11/gdkx.h>
- #else
- #include <gdk/gdkx.h>
- #endif
- #endif
- #define clip(val_, min_, max_) min((max_), max((min_), (val_)))
- enum touch_state {
- TOUCH_ACTIVE,
- TOUCH_ENDED,
- TOUCH_CANCELLED,
- };
- struct touch {
- enum touch_state state;
- int x, y;
- };
- struct point {
- double x, y;
- };
- struct evdev_device {
- struct list node;
- struct libevdev *evdev;
- struct libinput_device *libinput_device;
- int fd;
- guint source_id;
- };
- struct window {
- bool grab;
- struct tools_options options;
- struct list evdev_devices;
- GMainLoop *event_loop;
- GtkWidget *win;
- GtkWidget *area;
- int width, height; /* of window */
- /* sprite position */
- struct point pointer;
- struct point unaccelerated;
- /* these are for the delta coordinates, but they're not
- * deltas, they are converted into abs positions */
- size_t ndeltas;
- struct point deltas[64];
- /* abs position */
- struct point abs;
- /* Wayland and X11 pointer locking */
- struct {
- bool locked;
- #ifdef HAVE_GTK_WAYLAND
- struct zwp_pointer_constraints_v1 *wayland_pointer_constraints;
- struct zwp_locked_pointer_v1 *wayland_locked_pointer;
- #endif
- } lock_pointer;
- /* scroll bar positions */
- struct {
- double vx, vy;
- double hx, hy;
- double vx_discrete, vy_discrete;
- double hx_discrete, hy_discrete;
- } scroll;
- /* touch positions */
- struct touch touches[32];
- /* l/m/r mouse buttons */
- struct {
- bool l, m, r;
- bool other;
- const char *other_name;
- } buttons;
- /* touchpad swipe */
- struct {
- int nfingers;
- double x, y;
- } swipe;
- struct {
- int nfingers;
- double scale;
- double angle;
- double x, y;
- } pinch;
- struct {
- int nfingers;
- bool active;
- } hold;
- struct {
- double x, y;
- double x_in, y_in;
- double x_down, y_down;
- double x_up, y_up;
- double pressure;
- double distance;
- double tilt_x, tilt_y;
- double rotation;
- double size_major, size_minor;
- bool is_down;
- /* these are for the delta coordinates, but they're not
- * deltas, they are converted into abs positions */
- size_t ndeltas;
- struct point deltas[64];
- } tool;
- struct {
- struct {
- double position;
- int number;
- } ring;
- struct {
- double position;
- int number;
- } strip;
- struct {
- double position;
- int number;
- } dial;
- } pad;
- struct {
- int rel_x, rel_y; /* REL_X/Y */
- int x, y; /* ABS_X/Y */
- struct {
- int x, y; /* ABS_MT_POSITION_X/Y */
- bool active;
- } slots[16];
- unsigned int slot; /* ABS_MT_SLOT */
- /* So we know when to re-fetch the abs axes */
- uintptr_t device, last_device;
- } evdev;
- struct libinput_device *devices[50];
- };
- #ifdef HAVE_GTK_WAYLAND
- static void
- wayland_registry_global(void *data,
- struct wl_registry *registry,
- uint32_t name,
- const char *interface,
- uint32_t version)
- {
- struct window *w = data;
- if (!g_strcmp0(interface, "zwp_pointer_constraints_v1")) {
- w->lock_pointer.wayland_pointer_constraints =
- wl_registry_bind(registry,
- name,
- &zwp_pointer_constraints_v1_interface,
- 1);
- }
- }
- static void
- wayland_registry_global_remove(void *data,
- struct wl_registry *wl_registry,
- uint32_t name)
- {
- }
- static struct wl_registry_listener registry_listener = {
- wayland_registry_global,
- wayland_registry_global_remove
- };
- static bool
- wayland_lock_pointer(struct window *w)
- {
- GdkDisplay *gdk_display;
- GdkSeat *gdk_seat;
- GdkDevice *gdk_device;
- struct wl_display *display;
- struct wl_registry *registry;
- struct wl_pointer *wayland_pointer;
- struct wl_surface *surface;
- w->lock_pointer.wayland_pointer_constraints = NULL;
- gdk_display = gdk_display_get_default();
- display = gdk_wayland_display_get_wl_display(gdk_display);
- gdk_seat = gdk_display_get_default_seat(gdk_display);
- gdk_device = gdk_seat_get_pointer(gdk_seat);
- wayland_pointer = gdk_wayland_device_get_wl_pointer(gdk_device);
- registry = wl_display_get_registry(display);
- wl_registry_add_listener(registry, ®istry_listener, w);
- wl_display_roundtrip(display);
- if (!w->lock_pointer.wayland_pointer_constraints)
- return false;
- #ifdef HAVE_GTK4
- GtkNative *window = gtk_widget_get_native(w->win);
- GdkSurface *gdk_surface = gtk_native_get_surface(window);
- surface = gdk_wayland_surface_get_wl_surface(gdk_surface);
- #else
- GdkWindow *window = gtk_widget_get_window(w->win);
- surface = gdk_wayland_window_get_wl_surface(window);
- #endif
- w->lock_pointer.wayland_locked_pointer =
- zwp_pointer_constraints_v1_lock_pointer(
- w->lock_pointer.wayland_pointer_constraints,
- surface,
- wayland_pointer,
- NULL,
- ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
- return true;
- }
- static void
- wayland_unlock_pointer(struct window *w)
- {
- w->lock_pointer.wayland_pointer_constraints = NULL;
- zwp_locked_pointer_v1_destroy(w->lock_pointer.wayland_locked_pointer);
- }
- static inline bool
- backend_is_wayland(void)
- {
- return GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default());
- }
- #endif /* HAVE_GTK_WAYLAND */
- #ifdef HAVE_GTK_X11
- static bool
- x_lock_pointer(struct window *w)
- {
- Display *x_display;
- Window x_win;
- int result;
- /* gdk_display_get_default() is deprecated but probably won't be removed
- * before GTK X11 is removed completely so this whole section will
- * be gone anyway. Meanwhile, disable the warning
- */
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- x_display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
- #pragma GCC diagnostic pop
- #ifdef HAVE_GTK4
- GtkNative *window = gtk_widget_get_native(w->win);
- GdkSurface *surface = gtk_native_get_surface(window);
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- x_win = GDK_SURFACE_XID(surface);
- #pragma GCC diagnostic pop
- #else
- GdkWindow *window = gtk_widget_get_window(w->win);
- x_win = GDK_WINDOW_XID(window);
- #endif
- result = XGrabPointer(x_display,
- x_win,
- False,
- NoEventMask,
- GrabModeAsync,
- GrabModeAsync,
- x_win,
- None,
- CurrentTime);
- return (result == GrabSuccess);
- }
- static void
- x_unlock_pointer(struct window *w)
- {
- Display *x_display;
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- x_display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
- #pragma GCC diagnostic pop
- XUngrabPointer(x_display, CurrentTime);
- }
- static inline bool
- backend_is_x11(void)
- {
- return GDK_IS_X11_DISPLAY(gdk_display_get_default());
- }
- #endif /* HAVE_GTK_X11 */
- static bool
- window_lock_pointer(struct window *w)
- {
- if (w->lock_pointer.locked)
- return true;
- #ifdef HAVE_GTK_WAYLAND
- if (backend_is_wayland())
- w->lock_pointer.locked = wayland_lock_pointer(w);
- #endif
- #ifdef HAVE_GTK_X11
- if (backend_is_x11())
- w->lock_pointer.locked = x_lock_pointer(w);
- #endif
- return w->lock_pointer.locked;
- }
- static void
- window_unlock_pointer(struct window *w)
- {
- if (!w->lock_pointer.locked)
- return;
- w->lock_pointer.locked = false;
- #ifdef HAVE_GTK_WAYLAND
- if (backend_is_wayland())
- wayland_unlock_pointer(w);
- #endif
- #ifdef HAVE_GTK_X11
- if (backend_is_x11())
- x_unlock_pointer(w);
- #endif
- }
- LIBINPUT_ATTRIBUTE_PRINTF(1, 2)
- static inline void
- msg(const char *fmt, ...)
- {
- va_list args;
- printf("info: ");
- va_start(args, fmt);
- vprintf(fmt, args);
- va_end(args);
- }
- static inline void
- draw_evdev_rel(struct window *w, cairo_t *cr)
- {
- int center_x, center_y;
- cairo_save(cr);
- cairo_set_source_rgb(cr, .2, .2, .8);
- center_x = w->width / 2 - 400;
- center_y = w->height / 2;
- cairo_arc(cr, center_x, center_y, 10, 0, 2 * M_PI);
- cairo_stroke(cr);
- if (w->evdev.rel_x) {
- int dir = w->evdev.rel_x > 0 ? 1 : -1;
- for (int i = 0; i < abs(w->evdev.rel_x); i++) {
- cairo_move_to(cr, center_x + (i + 1) * 20 * dir, center_y - 20);
- cairo_rel_line_to(cr, 0, 40);
- cairo_rel_line_to(cr, 20 * dir, -20);
- cairo_rel_line_to(cr, -20 * dir, -20);
- cairo_fill(cr);
- }
- }
- if (w->evdev.rel_y) {
- int dir = w->evdev.rel_y > 0 ? 1 : -1;
- for (int i = 0; i < abs(w->evdev.rel_y); i++) {
- cairo_move_to(cr, center_x - 20, center_y + (i + 1) * 20 * dir);
- cairo_rel_line_to(cr, 40, 0);
- cairo_rel_line_to(cr, -20, 20 * dir);
- cairo_rel_line_to(cr, -20, -20 * dir);
- cairo_fill(cr);
- }
- }
- cairo_restore(cr);
- }
- static inline void
- draw_evdev_abs(struct window *w, cairo_t *cr)
- {
- static const struct input_absinfo *ax = NULL, *ay = NULL;
- const int normalized_width = 200;
- int outline_width = normalized_width, outline_height = normalized_width * 0.75;
- int center_x, center_y;
- int width, height;
- int x, y;
- cairo_save(cr);
- center_x = w->width / 2 + 400;
- center_y = w->height / 2;
- /* Always the outline even if we didn't get any abs events yet so it
- * doesn't just appear out of nowhere */
- if (w->evdev.device == 0)
- goto draw_outline;
- /* device has changed, so the abs proportions/dimensions have
- * changed. */
- if (w->evdev.device != w->evdev.last_device) {
- struct evdev_device *d;
- ax = NULL;
- ay = NULL;
- list_for_each(d, &w->evdev_devices, node) {
- if ((uintptr_t)d->libinput_device != w->evdev.device)
- continue;
- ax = libevdev_get_abs_info(d->evdev, ABS_X);
- ay = libevdev_get_abs_info(d->evdev, ABS_Y);
- w->evdev.last_device = w->evdev.device;
- }
- }
- if (ax == NULL || ay == NULL)
- goto draw_outline;
- width = ax->maximum - ax->minimum;
- height = ay->maximum - ay->minimum;
- outline_height = 1.0 * height / width * normalized_width;
- outline_width = normalized_width;
- cairo_set_source_rgb(cr, .2, .2, .8);
- x = 1.0 * (w->evdev.x - ax->minimum) / width * outline_width;
- y = 1.0 * (w->evdev.y - ay->minimum) / height * outline_height;
- x += center_x - outline_width / 2;
- y += center_y - outline_height / 2;
- cairo_arc(cr, x, y, 10, 0, 2 * M_PI);
- cairo_fill(cr);
- for (size_t i = 0; i < ARRAY_LENGTH(w->evdev.slots); i++) {
- if (!w->evdev.slots[i].active)
- continue;
- cairo_set_source_rgb(cr,
- .2 + .2 * (i % 5),
- .2 + .2 * (i % 5),
- .8 - .2 * (i % 5));
- x = w->evdev.slots[i].x;
- y = w->evdev.slots[i].y;
- x = 1.0 * (x - ax->minimum) / width * outline_width;
- y = 1.0 * (y - ay->minimum) / height * outline_height;
- x += center_x - outline_width / 2;
- y += center_y - outline_height / 2;
- cairo_arc(cr, x, y, 10, 0, 2 * M_PI);
- cairo_fill(cr);
- char finger_text[3];
- cairo_text_extents_t finger_text_extents;
- snprintf(finger_text, 3, "%zu", i);
- cairo_set_source_rgb(cr, 1.f, 1.f, 1.f);
- cairo_set_font_size(cr, 12.0);
- cairo_text_extents(cr, finger_text, &finger_text_extents);
- cairo_move_to(cr,
- x - finger_text_extents.width / 2,
- y + finger_text_extents.height / 2);
- cairo_show_text(cr, finger_text);
- }
- draw_outline:
- /* The touchpad outline */
- cairo_set_source_rgb(cr, .2, .2, .8);
- cairo_rectangle(cr,
- center_x - outline_width / 2,
- center_y - outline_height / 2,
- outline_width,
- outline_height);
- cairo_stroke(cr);
- cairo_restore(cr);
- }
- static inline void
- draw_gestures(struct window *w, cairo_t *cr)
- {
- int offset;
- /* swipe */
- cairo_save(cr);
- cairo_translate(cr, w->swipe.x, w->swipe.y);
- for (int i = 0; i < w->swipe.nfingers; i++) {
- cairo_set_source_rgb(cr, .8, .8, .4);
- cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI);
- cairo_fill(cr);
- }
- for (int i = 0; i < 4; i++) { /* 4 fg max */
- cairo_set_source_rgb(cr, 0, 0, 0);
- cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI);
- cairo_stroke(cr);
- }
- cairo_restore(cr);
- /* pinch */
- cairo_save(cr);
- offset = w->pinch.scale * 100;
- cairo_translate(cr, w->pinch.x, w->pinch.y);
- cairo_rotate(cr, w->pinch.angle * M_PI / 180.0);
- if (w->pinch.nfingers > 0) {
- cairo_set_source_rgb(cr, .4, .4, .8);
- cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI);
- cairo_arc(cr, -offset, offset, 20, 0, 2 * M_PI);
- cairo_fill(cr);
- }
- cairo_set_source_rgb(cr, 0, 0, 0);
- cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI);
- cairo_stroke(cr);
- cairo_arc(cr, -offset, offset, 20, 0, 2 * M_PI);
- cairo_stroke(cr);
- cairo_restore(cr);
- /* hold */
- cairo_save(cr);
- cairo_translate(cr, w->width / 2, w->height / 2 + 100);
- for (int i = 4; i > 0; i--) { /* 4 fg max */
- double r, g, b, hold_alpha;
- r = .4 + .2 * (i % 2);
- g = .2;
- b = .2;
- hold_alpha = (w->hold.active && i <= w->hold.nfingers) ? 1 : .5;
- cairo_set_source_rgba(cr, r, g, b, hold_alpha);
- cairo_arc(cr, 0, 0, 20 * i, 0, 2 * M_PI);
- cairo_fill(cr);
- cairo_set_source_rgba(cr, 0, 0, 0, hold_alpha);
- cairo_arc(cr, 0, 0, 20 * i, 0, 2 * M_PI);
- cairo_stroke(cr);
- }
- cairo_restore(cr);
- }
- static inline void
- draw_scrollbars(struct window *w, cairo_t *cr)
- {
- /* normal scrollbars */
- cairo_save(cr);
- cairo_set_source_rgb(cr, .4, .8, 0);
- cairo_rectangle(cr, w->scroll.vx - 10, w->scroll.vy - 20, 20, 40);
- cairo_rectangle(cr, w->scroll.hx - 20, w->scroll.hy - 10, 40, 20);
- cairo_fill(cr);
- /* discrete scrollbars */
- cairo_set_source_rgb(cr, .8, .4, 0);
- cairo_rectangle(cr,
- w->scroll.vx_discrete - 5,
- w->scroll.vy_discrete - 10,
- 10,
- 20);
- cairo_rectangle(cr,
- w->scroll.hx_discrete - 10,
- w->scroll.hy_discrete - 5,
- 20,
- 10);
- cairo_fill(cr);
- cairo_restore(cr);
- }
- static inline void
- draw_touchpoints(struct window *w, cairo_t *cr)
- {
- cairo_save(cr);
- ARRAY_FOR_EACH(w->touches, t) {
- if (t->state == TOUCH_ACTIVE)
- cairo_set_source_rgb(cr, .8, .2, .2);
- else
- cairo_set_source_rgb(cr, .8, .4, .4);
- cairo_arc(cr, t->x, t->y, 10, 0, 2 * M_PI);
- if (t->state == TOUCH_CANCELLED)
- cairo_stroke(cr);
- else
- cairo_fill(cr);
- }
- cairo_restore(cr);
- }
- static inline void
- draw_abs_pointer(struct window *w, cairo_t *cr)
- {
- cairo_save(cr);
- cairo_set_source_rgb(cr, .2, .4, .8);
- cairo_arc(cr, w->abs.x, w->abs.y, 10, 0, 2 * M_PI);
- cairo_fill(cr);
- cairo_restore(cr);
- }
- static inline void
- draw_text(cairo_t *cr, const char *text, double x, double y)
- {
- cairo_text_extents_t te;
- cairo_font_extents_t fe;
- cairo_text_extents(cr, text, &te);
- cairo_font_extents(cr, &fe);
- /* center of the rectangle */
- cairo_move_to(cr, x, y);
- cairo_rel_move_to(cr, -te.width / 2, -fe.descent + te.height / 2);
- cairo_show_text(cr, text);
- }
- static inline void
- draw_other_button(struct window *w, cairo_t *cr)
- {
- const char *name = w->buttons.other_name;
- cairo_save(cr);
- if (!w->buttons.other)
- goto outline;
- if (!name)
- name = "undefined";
- cairo_set_source_rgb(cr, .2, .8, .8);
- cairo_rectangle(cr, w->width / 2 - 40, w->height - 150, 80, 30);
- cairo_fill(cr);
- cairo_set_source_rgb(cr, 0, 0, 0);
- draw_text(cr, name, w->width / 2, w->height - 150 + 15);
- outline:
- cairo_set_source_rgb(cr, 0, 0, 0);
- cairo_rectangle(cr, w->width / 2 - 40, w->height - 150, 80, 30);
- cairo_stroke(cr);
- cairo_restore(cr);
- }
- static inline void
- draw_buttons(struct window *w, cairo_t *cr)
- {
- cairo_save(cr);
- if (w->buttons.l || w->buttons.m || w->buttons.r) {
- cairo_set_source_rgb(cr, .2, .8, .8);
- if (w->buttons.l)
- cairo_rectangle(cr,
- w->width / 2 - 100,
- w->height - 200,
- 70,
- 30);
- if (w->buttons.m)
- cairo_rectangle(cr, w->width / 2 - 20, w->height - 200, 40, 30);
- if (w->buttons.r)
- cairo_rectangle(cr, w->width / 2 + 30, w->height - 200, 70, 30);
- cairo_fill(cr);
- }
- cairo_set_source_rgb(cr, 0, 0, 0);
- cairo_rectangle(cr, w->width / 2 - 100, w->height - 200, 70, 30);
- cairo_rectangle(cr, w->width / 2 - 20, w->height - 200, 40, 30);
- cairo_rectangle(cr, w->width / 2 + 30, w->height - 200, 70, 30);
- cairo_stroke(cr);
- cairo_restore(cr);
- draw_other_button(w, cr);
- }
- static inline void
- draw_pad(struct window *w, cairo_t *cr)
- {
- double rx, ry;
- double pos;
- char number[3];
- rx = w->width / 2 - 200;
- ry = w->height / 2 + 100;
- cairo_save(cr);
- /* outer ring (for ring) */
- cairo_set_source_rgb(cr, .7, .7, .0);
- cairo_arc(cr, rx, ry, 50, 0, 2 * M_PI);
- cairo_fill(cr);
- /* inner ring (for dial) */
- cairo_set_source_rgb(cr, 1., 1., 1.);
- cairo_arc(cr, rx, ry, 30, 0, 2 * M_PI);
- cairo_fill(cr);
- /* marker */
- /* libinput has degrees and 0 is north, cairo has radians and 0 is
- * east */
- if (w->pad.ring.position != -1) {
- pos = (w->pad.ring.position + 270) * M_PI / 180.0;
- cairo_set_source_rgb(cr, .0, .0, .0);
- cairo_set_line_width(cr, 20);
- cairo_arc(cr, rx, ry, 40, pos - M_PI / 8, pos + M_PI / 8);
- cairo_stroke(cr);
- snprintf(number, sizeof(number), "%d", w->pad.ring.number);
- cairo_set_source_rgb(cr, .0, .0, .0);
- draw_text(cr, number, rx, ry);
- }
- if (w->pad.dial.position != -1) {
- const int degrees_per_click = 15.0;
- double degrees =
- fmod(w->pad.dial.position / 120 * degrees_per_click, 360);
- pos = (degrees + 270) * M_PI / 180.0;
- cairo_set_source_rgb(cr, .0, .0, .0);
- cairo_set_line_width(cr, 20);
- cairo_arc(cr, rx, ry, 20, pos - M_PI / 12, pos + M_PI / 12);
- cairo_stroke(cr);
- snprintf(number, sizeof(number), "%d", w->pad.dial.number);
- cairo_set_source_rgb(cr, .0, .0, .0);
- draw_text(cr, number, rx, ry);
- }
- cairo_restore(cr);
- rx = w->width / 2 - 300;
- ry = w->height / 2 + 50;
- cairo_save(cr);
- cairo_set_source_rgb(cr, .7, .7, .0);
- cairo_rectangle(cr, rx, ry, 20, 100);
- cairo_fill(cr);
- if (w->pad.strip.position != -1) {
- pos = w->pad.strip.position * 80;
- cairo_set_source_rgb(cr, .0, .0, .0);
- cairo_rectangle(cr, rx, ry + pos, 20, 20);
- cairo_fill(cr);
- snprintf(number, sizeof(number), "%d", w->pad.strip.number);
- cairo_set_source_rgb(cr, .0, .0, .0);
- draw_text(cr, number, rx + 10, ry - 10);
- }
- cairo_restore(cr);
- }
- static inline void
- draw_tablet(struct window *w, cairo_t *cr)
- {
- double x, y;
- int first, last;
- size_t mask;
- int rx, ry;
- /* pressure/distance bars */
- rx = w->width / 2 + 100;
- ry = w->height / 2 + 50;
- cairo_save(cr);
- cairo_set_source_rgb(cr, .2, .6, .6);
- cairo_rectangle(cr, rx, ry, 20, 100);
- cairo_stroke(cr);
- if (w->tool.distance > 0) {
- double pos = w->tool.distance * 100;
- cairo_rectangle(cr, rx, ry + 100 - pos, 20, 5);
- cairo_fill(cr);
- }
- if (w->tool.pressure > 0) {
- double pos = w->tool.pressure * 100;
- if (w->tool.is_down)
- cairo_rectangle(cr, rx + 25, ry + 95, 5, 5);
- cairo_rectangle(cr, rx, ry + 100 - pos, 20, pos);
- cairo_fill(cr);
- }
- cairo_restore(cr);
- /* tablet tool, square for prox-in location */
- cairo_save(cr);
- cairo_set_source_rgb(cr, .2, .6, .6);
- if (w->tool.x_in && w->tool.y_in) {
- cairo_rectangle(cr, w->tool.x_in - 15, w->tool.y_in - 15, 30, 30);
- cairo_stroke(cr);
- }
- if (w->tool.x_down && w->tool.y_down) {
- cairo_rectangle(cr, w->tool.x_down - 10, w->tool.y_down - 10, 20, 20);
- cairo_stroke(cr);
- }
- if (w->tool.x_up && w->tool.y_up) {
- cairo_rectangle(cr, w->tool.x_up - 10, w->tool.y_up - 10, 20, 20);
- cairo_stroke(cr);
- }
- if (w->tool.pressure)
- cairo_set_source_rgb(cr, .2, .8, .8);
- cairo_translate(cr, w->tool.x, w->tool.y);
- /* scale of 2.5 is large enough to make the marker visible around the
- physical totem */
- cairo_scale(cr, 1.0 + w->tool.size_major * 2.5, 1.0 + w->tool.size_minor * 2.5);
- cairo_scale(cr, 1.0 + w->tool.tilt_x / 30.0, 1.0 + w->tool.tilt_y / 30.0);
- if (w->tool.rotation)
- cairo_rotate(cr, w->tool.rotation * M_PI / 180.0);
- if (w->tool.pressure)
- cairo_set_source_rgb(cr, .8, .8, .2);
- cairo_arc(cr,
- 0,
- 0,
- 1 + 10 * max(w->tool.pressure, w->tool.distance),
- 0,
- 2 * M_PI);
- cairo_fill(cr);
- cairo_restore(cr);
- /* The line to indicate the origin */
- if (w->tool.size_major) {
- cairo_save(cr);
- cairo_scale(cr, 1.0, 1.0);
- cairo_translate(cr, w->tool.x, w->tool.y);
- if (w->tool.rotation)
- cairo_rotate(cr, w->tool.rotation * M_PI / 180.0);
- cairo_set_source_rgb(cr, .0, .0, .0);
- cairo_move_to(cr, 0, 0);
- cairo_rel_line_to(cr, 0, -w->tool.size_major * 2.5);
- cairo_stroke(cr);
- cairo_restore(cr);
- }
- /* tablet deltas */
- mask = ARRAY_LENGTH(w->tool.deltas);
- first = max(w->tool.ndeltas + 1, mask) - mask;
- last = w->tool.ndeltas;
- cairo_save(cr);
- cairo_set_source_rgb(cr, .8, .8, .2);
- x = w->tool.deltas[first % mask].x;
- y = w->tool.deltas[first % mask].y;
- cairo_move_to(cr, x, y);
- for (int i = first + 1; i < last; i++) {
- x = w->tool.deltas[i % mask].x;
- y = w->tool.deltas[i % mask].y;
- cairo_line_to(cr, x, y);
- }
- cairo_stroke(cr);
- cairo_restore(cr);
- }
- static inline void
- draw_pointer(struct window *w, cairo_t *cr)
- {
- double x, y;
- int first, last;
- size_t mask;
- /* draw pointer sprite */
- cairo_set_source_rgb(cr, 0, 0, 0);
- cairo_save(cr);
- cairo_move_to(cr, w->pointer.x, w->pointer.y);
- cairo_rel_line_to(cr, 10, 15);
- cairo_rel_line_to(cr, -10, 0);
- cairo_rel_line_to(cr, 0, -15);
- cairo_fill(cr);
- /* draw unaccelerated sprite */
- cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
- cairo_save(cr);
- cairo_move_to(cr, w->unaccelerated.x, w->unaccelerated.y);
- cairo_rel_line_to(cr, -5, -10);
- cairo_rel_line_to(cr, 10, 0);
- cairo_rel_line_to(cr, -5, 10);
- cairo_fill(cr);
- /* pointer deltas */
- mask = ARRAY_LENGTH(w->deltas);
- first = max(w->ndeltas + 1, mask) - mask;
- last = w->ndeltas;
- cairo_set_source_rgb(cr, .8, .5, .2);
- x = w->deltas[first % mask].x;
- y = w->deltas[first % mask].y;
- cairo_move_to(cr, x, y);
- for (int i = first + 1; i < last; i++) {
- x = w->deltas[i % mask].x;
- y = w->deltas[i % mask].y;
- cairo_line_to(cr, x, y);
- }
- cairo_stroke(cr);
- cairo_restore(cr);
- }
- static inline void
- draw_background(struct window *w, cairo_t *cr)
- {
- int x1, x2, y1, y2, x3, y3, x4, y4;
- int cols;
- /* 10px and 5px grids */
- cairo_save(cr);
- cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
- x1 = w->width / 2 - 200;
- y1 = w->height / 2 - 200;
- x2 = w->width / 2 + 200;
- y2 = w->height / 2 - 200;
- for (cols = 1; cols < 10; cols++) {
- cairo_move_to(cr, x1 + 10 * cols, y1);
- cairo_rel_line_to(cr, 0, 100);
- cairo_move_to(cr, x1, y1 + 10 * cols);
- cairo_rel_line_to(cr, 100, 0);
- cairo_move_to(cr, x2 + 5 * cols, y2);
- cairo_rel_line_to(cr, 0, 50);
- cairo_move_to(cr, x2, y2 + 5 * cols);
- cairo_rel_line_to(cr, 50, 0);
- }
- /* 3px horiz/vert bar codes */
- x3 = w->width / 2 - 200;
- y3 = w->height / 2 + 200;
- x4 = w->width / 2 + 200;
- y4 = w->height / 2 + 100;
- for (cols = 0; cols < 50; cols++) {
- cairo_move_to(cr, x3 + 3 * cols, y3);
- cairo_rel_line_to(cr, 0, 20);
- cairo_move_to(cr, x4, y4 + 3 * cols);
- cairo_rel_line_to(cr, 20, 0);
- }
- cairo_stroke(cr);
- /* round targets */
- for (int i = 0; i <= 3; i++) {
- x1 = w->width * i / 4.0;
- x2 = w->width * i / 4.0;
- y1 = w->height * 1.0 / 4.0;
- y2 = w->height * 3.0 / 4.0;
- cairo_arc(cr, x1, y1, 10, 0, 2 * M_PI);
- cairo_stroke(cr);
- cairo_arc(cr, x2, y2, 10, 0, 2 * M_PI);
- cairo_stroke(cr);
- }
- cairo_restore(cr);
- }
- static gboolean
- draw(GtkWidget *widget, cairo_t *cr, gpointer data)
- {
- struct window *w = data;
- cairo_set_font_size(cr, 12.0);
- cairo_set_source_rgb(cr, 1, 1, 1);
- cairo_rectangle(cr, 0, 0, w->width, w->height);
- cairo_fill(cr);
- draw_background(w, cr);
- draw_evdev_rel(w, cr);
- draw_evdev_abs(w, cr);
- draw_pad(w, cr);
- draw_tablet(w, cr);
- draw_gestures(w, cr);
- draw_scrollbars(w, cr);
- draw_touchpoints(w, cr);
- draw_abs_pointer(w, cr);
- draw_buttons(w, cr);
- draw_pointer(w, cr);
- return TRUE;
- }
- #ifdef HAVE_GTK4
- static void
- draw_gtk4(GtkDrawingArea *widget, cairo_t *cr, int width, int height, gpointer data)
- {
- draw(GTK_WIDGET(widget), cr, data);
- }
- #endif
- static void
- window_place_ui_elements(GtkWidget *widget, struct window *w)
- {
- #ifdef HAVE_GTK4
- w->width = gtk_widget_get_width(w->area);
- w->height = gtk_widget_get_height(w->area);
- #else
- gtk_window_get_size(GTK_WINDOW(widget), &w->width, &w->height);
- #endif
- w->pointer.x = w->width / 2;
- w->pointer.y = w->height / 2;
- w->unaccelerated.x = w->width / 2;
- w->unaccelerated.y = w->height / 2;
- w->deltas[0].x = w->pointer.x;
- w->deltas[0].y = w->pointer.y;
- w->scroll.vx = w->width / 2;
- w->scroll.vy = w->height / 2;
- w->scroll.hx = w->width / 2;
- w->scroll.hy = w->height / 2;
- w->scroll.vx_discrete = w->width / 2;
- w->scroll.vy_discrete = w->height / 2;
- w->scroll.hx_discrete = w->width / 2;
- w->scroll.hy_discrete = w->height / 2;
- w->swipe.x = w->width / 2;
- w->swipe.y = w->height / 2;
- w->pinch.scale = 1.0;
- w->pinch.x = w->width / 2;
- w->pinch.y = w->height / 2;
- }
- #ifdef HAVE_GTK4
- static void
- map_event_cb(GtkDrawingArea *widget, int width, int height, gpointer data)
- {
- struct window *w = data;
- window_place_ui_elements(GTK_WIDGET(widget), w);
- gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(w->area), draw_gtk4, w, NULL);
- gtk_widget_set_cursor_from_name(w->win, "none");
- window_lock_pointer(w);
- }
- #else
- static void
- map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
- {
- struct window *w = data;
- GdkDisplay *display;
- GdkWindow *window;
- window_place_ui_elements(widget, w);
- g_signal_connect(G_OBJECT(w->area), "draw", G_CALLBACK(draw), w);
- window = gdk_event_get_window(event);
- display = gdk_window_get_display(window);
- gdk_window_set_cursor(gtk_widget_get_window(w->win),
- gdk_cursor_new_for_display(display, GDK_BLANK_CURSOR));
- window_lock_pointer(w);
- }
- #endif
- static void
- window_quit(struct window *w)
- {
- g_main_loop_quit(w->event_loop);
- }
- #ifdef HAVE_GTK4
- static gboolean
- window_delete_event_cb(GtkWindow *window, gpointer data)
- {
- struct window *w = data;
- window_quit(w);
- return TRUE;
- }
- #else
- static void
- window_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
- {
- struct window *w = data;
- window_quit(w);
- }
- #endif
- static void
- window_init(struct window *w)
- {
- list_init(&w->evdev_devices);
- #ifdef HAVE_GTK4
- w->win = gtk_window_new();
- #else
- w->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
- #endif
- if (getenv("LIBINPUT_RUNNING_TEST_SUITE")) {
- #ifdef HAVE_GTK4
- gtk_window_minimize(GTK_WINDOW(w->win));
- #else
- gtk_window_iconify(GTK_WINDOW(w->win));
- #endif
- }
- gtk_window_set_title(GTK_WINDOW(w->win), "libinput debugging tool");
- gtk_window_set_default_size(GTK_WINDOW(w->win), 1024, 768);
- gtk_window_maximize(GTK_WINDOW(w->win));
- gtk_window_set_resizable(GTK_WINDOW(w->win), TRUE);
- gtk_widget_realize(w->win);
- w->area = gtk_drawing_area_new();
- #ifdef HAVE_GTK4
- g_signal_connect(G_OBJECT(w->area), "resize", G_CALLBACK(map_event_cb), w);
- g_signal_connect(G_OBJECT(w->win),
- "close-request",
- G_CALLBACK(window_delete_event_cb),
- w);
- gtk_window_set_child(GTK_WINDOW(w->win), w->area);
- gtk_widget_set_visible(w->win, TRUE);
- #else
- g_signal_connect(G_OBJECT(w->win), "map-event", G_CALLBACK(map_event_cb), w);
- g_signal_connect(G_OBJECT(w->win),
- "delete-event",
- G_CALLBACK(window_delete_event_cb),
- w);
- gtk_widget_set_events(w->win, 0);
- gtk_widget_set_events(w->area, 0);
- gtk_container_add(GTK_CONTAINER(w->win), w->area);
- gtk_widget_show_all(w->win);
- #endif
- w->pad.ring.position = -1;
- w->pad.strip.position = -1;
- w->pad.dial.position = -1;
- }
- static void
- window_cleanup(struct window *w)
- {
- ARRAY_FOR_EACH(w->devices, dev) {
- if (*dev)
- libinput_device_unref(*dev);
- }
- }
- static void
- change_ptraccel(struct window *w, double amount)
- {
- ARRAY_FOR_EACH(w->devices, dev) {
- double speed;
- enum libinput_config_status status;
- if (*dev == NULL)
- continue;
- if (!libinput_device_config_accel_is_available(*dev))
- continue;
- speed = libinput_device_config_accel_get_speed(*dev);
- speed = clip(speed + amount, -1, 1);
- status = libinput_device_config_accel_set_speed(*dev, speed);
- if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) {
- msg("%s: failed to change accel to %.2f (%s)\n",
- libinput_device_get_name(*dev),
- speed,
- libinput_config_status_to_str(status));
- } else {
- printf("%s: speed is %.2f\n",
- libinput_device_get_name(*dev),
- speed);
- }
- }
- }
- static int
- handle_event_evdev(GIOChannel *source, GIOCondition condition, gpointer data)
- {
- struct libinput_device *dev = data;
- struct libinput *li = libinput_device_get_context(dev);
- struct window *w = libinput_get_user_data(li);
- struct evdev_device *d, *device = NULL;
- struct input_event e;
- int rc;
- list_for_each(d, &w->evdev_devices, node) {
- if (d->libinput_device == dev) {
- device = d;
- break;
- }
- }
- if (device == NULL) {
- msg("Unknown device: %s\n", libinput_device_get_name(dev));
- return FALSE;
- }
- do {
- rc = libevdev_next_event(device->evdev, LIBEVDEV_READ_FLAG_NORMAL, &e);
- if (rc == -EAGAIN) {
- break;
- } else if (rc == LIBEVDEV_READ_STATUS_SYNC) {
- msg("SYN_DROPPED received\n");
- goto out;
- } else if (rc != LIBEVDEV_READ_STATUS_SUCCESS) {
- msg("Error reading event: %s\n", strerror(-rc));
- goto out;
- }
- #define EVENT(t_, c_) (t_ << 16 | c_)
- switch (EVENT(e.type, e.code)) {
- case EVENT(EV_REL, REL_X):
- w->evdev.rel_x = e.value;
- break;
- case EVENT(EV_REL, REL_Y):
- w->evdev.rel_y = e.value;
- break;
- case EVENT(EV_ABS, ABS_MT_SLOT):
- w->evdev.slot = min((unsigned int)e.value,
- ARRAY_LENGTH(w->evdev.slots) - 1);
- w->evdev.device = (uintptr_t)dev;
- break;
- case EVENT(EV_ABS, ABS_MT_TRACKING_ID):
- w->evdev.slots[w->evdev.slot].active = (e.value != -1);
- w->evdev.device = (uintptr_t)dev;
- break;
- case EVENT(EV_ABS, ABS_X):
- w->evdev.x = e.value;
- w->evdev.device = (uintptr_t)dev;
- break;
- case EVENT(EV_ABS, ABS_Y):
- w->evdev.y = e.value;
- w->evdev.device = (uintptr_t)dev;
- break;
- case EVENT(EV_ABS, ABS_MT_POSITION_X):
- w->evdev.slots[w->evdev.slot].x = e.value;
- w->evdev.device = (uintptr_t)dev;
- break;
- case EVENT(EV_ABS, ABS_MT_POSITION_Y):
- w->evdev.slots[w->evdev.slot].y = e.value;
- w->evdev.device = (uintptr_t)dev;
- break;
- }
- } while (rc == LIBEVDEV_READ_STATUS_SUCCESS);
- gtk_widget_queue_draw(w->area);
- out:
- return TRUE;
- }
- static void
- register_evdev_device(struct window *w, struct libinput_device *dev)
- {
- GIOChannel *c;
- struct udev_device *ud;
- struct libevdev *evdev;
- const char *device_node;
- int fd;
- struct evdev_device *d;
- ud = libinput_device_get_udev_device(dev);
- device_node = udev_device_get_devnode(ud);
- fd = open(device_node, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
- if (fd == -1) {
- msg("failed to open %s, evdev events unavailable\n", device_node);
- goto out;
- }
- if (libevdev_new_from_fd(fd, &evdev) != 0) {
- msg("failed to create context for %s, evdev events unavailable\n",
- device_node);
- goto out;
- }
- d = zalloc(sizeof *d);
- list_append(&w->evdev_devices, &d->node);
- d->fd = fd;
- d->evdev = evdev;
- d->libinput_device = libinput_device_ref(dev);
- c = g_io_channel_unix_new(fd);
- g_io_channel_set_encoding(c, NULL, NULL);
- d->source_id =
- g_io_add_watch(c, G_IO_IN, handle_event_evdev, d->libinput_device);
- fd = -1;
- out:
- xclose(&fd);
- udev_device_unref(ud);
- }
- static void
- unregister_evdev_device(struct window *w, struct libinput_device *dev)
- {
- struct evdev_device *d;
- list_for_each_safe(d, &w->evdev_devices, node) {
- if (d->libinput_device != dev)
- continue;
- list_remove(&d->node);
- g_source_remove(d->source_id);
- free(libinput_device_get_user_data(d->libinput_device));
- libinput_device_unref(d->libinput_device);
- libevdev_free(d->evdev);
- xclose(&d->fd);
- free(d);
- w->evdev.last_device = 0;
- break;
- }
- }
- static void
- handle_event_device_notify(struct libinput_event *ev)
- {
- struct libinput_device *dev = libinput_event_get_device(ev);
- struct libinput *li;
- struct window *w;
- const char *type;
- li = libinput_event_get_context(ev);
- w = libinput_get_user_data(li);
- if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) {
- type = "added";
- register_evdev_device(w, dev);
- tools_device_apply_config(libinput_event_get_device(ev), &w->options);
- } else {
- type = "removed";
- unregister_evdev_device(w, dev);
- }
- msg("%s %-30s %s\n",
- libinput_device_get_sysname(dev),
- libinput_device_get_name(dev),
- type);
- if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) {
- ARRAY_FOR_EACH(w->devices, device) {
- if (*device == NULL) {
- *device = libinput_device_ref(dev);
- break;
- }
- }
- } else {
- ARRAY_FOR_EACH(w->devices, device) {
- if (*device == dev) {
- libinput_device_unref(*device);
- *device = NULL;
- break;
- }
- }
- }
- }
- static void
- handle_event_motion(struct libinput_event *ev, struct window *w)
- {
- struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
- double dx = libinput_event_pointer_get_dx(p),
- dy = libinput_event_pointer_get_dy(p);
- double dx_unaccel = libinput_event_pointer_get_dx_unaccelerated(p),
- dy_unaccel = libinput_event_pointer_get_dy_unaccelerated(p);
- struct point point;
- const int mask = ARRAY_LENGTH(w->deltas);
- size_t idx;
- w->pointer.x = clip(w->pointer.x + dx, 0.0, w->width);
- w->pointer.y = clip(w->pointer.y + dy, 0.0, w->height);
- w->unaccelerated.x = clip(w->unaccelerated.x + dx_unaccel, 0.0, w->width);
- w->unaccelerated.y = clip(w->unaccelerated.y + dy_unaccel, 0.0, w->height);
- idx = w->ndeltas % mask;
- point = w->deltas[idx];
- idx = (w->ndeltas + 1) % mask;
- point.x += dx_unaccel;
- point.y += dy_unaccel;
- w->deltas[idx] = point;
- w->ndeltas++;
- }
- static void
- handle_event_absmotion(struct libinput_event *ev, struct window *w)
- {
- struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
- double x = libinput_event_pointer_get_absolute_x_transformed(p, w->width),
- y = libinput_event_pointer_get_absolute_y_transformed(p, w->height);
- w->abs.x = x;
- w->abs.y = y;
- }
- static void
- handle_event_touch(struct libinput_event *ev, struct window *w)
- {
- struct libinput_event_touch *t = libinput_event_get_touch_event(ev);
- int slot = libinput_event_touch_get_seat_slot(t);
- struct touch *touch;
- double x, y;
- if (slot == -1 || slot >= (int)ARRAY_LENGTH(w->touches))
- return;
- touch = &w->touches[slot];
- switch (libinput_event_get_type(ev)) {
- case LIBINPUT_EVENT_TOUCH_UP:
- touch->state = TOUCH_ENDED;
- return;
- case LIBINPUT_EVENT_TOUCH_CANCEL:
- touch->state = TOUCH_CANCELLED;
- return;
- default:
- break;
- }
- x = libinput_event_touch_get_x_transformed(t, w->width),
- y = libinput_event_touch_get_y_transformed(t, w->height);
- touch->state = TOUCH_ACTIVE;
- touch->x = (int)x;
- touch->y = (int)y;
- }
- static void
- handle_event_axis(struct libinput_event *ev, struct window *w)
- {
- struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
- double value;
- enum libinput_pointer_axis axis;
- enum libinput_event_type type;
- type = libinput_event_get_type(ev);
- axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
- if (libinput_event_pointer_has_axis(p, axis)) {
- value = libinput_event_pointer_get_scroll_value(p, axis);
- w->scroll.vy += value;
- w->scroll.vy = clip(w->scroll.vy, 0, w->height);
- if (type == LIBINPUT_EVENT_POINTER_SCROLL_WHEEL) {
- w->scroll.vy_discrete += value;
- w->scroll.vy_discrete =
- clip(w->scroll.vy_discrete, 0, w->height);
- }
- }
- axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
- if (libinput_event_pointer_has_axis(p, axis)) {
- value = libinput_event_pointer_get_scroll_value(p, axis);
- w->scroll.hx += value;
- w->scroll.hx = clip(w->scroll.hx, 0, w->width);
- if (type == LIBINPUT_EVENT_POINTER_SCROLL_WHEEL) {
- w->scroll.hx_discrete += value;
- w->scroll.hx_discrete =
- clip(w->scroll.hx_discrete, 0, w->width);
- }
- }
- }
- static int
- handle_event_keyboard(struct libinput_event *ev, struct window *w)
- {
- struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev);
- unsigned int key = libinput_event_keyboard_get_key(k);
- if (libinput_event_keyboard_get_key_state(k) == LIBINPUT_KEY_STATE_RELEASED)
- return 0;
- switch (key) {
- case KEY_ESC:
- return 1;
- case KEY_UP:
- change_ptraccel(w, 0.1);
- break;
- case KEY_DOWN:
- change_ptraccel(w, -0.1);
- break;
- default:
- break;
- }
- return 0;
- }
- static void
- handle_event_button(struct libinput_event *ev, struct window *w)
- {
- struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
- unsigned int button = libinput_event_pointer_get_button(p);
- bool is_press;
- is_press = libinput_event_pointer_get_button_state(p) ==
- LIBINPUT_BUTTON_STATE_PRESSED;
- switch (button) {
- case BTN_LEFT:
- w->buttons.l = is_press;
- break;
- case BTN_RIGHT:
- w->buttons.r = is_press;
- break;
- case BTN_MIDDLE:
- w->buttons.m = is_press;
- break;
- default:
- w->buttons.other = is_press;
- w->buttons.other_name = libevdev_event_code_get_name(EV_KEY, button);
- }
- }
- static void
- handle_event_swipe(struct libinput_event *ev, struct window *w)
- {
- struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev);
- int nfingers;
- double dx, dy;
- nfingers = libinput_event_gesture_get_finger_count(g);
- switch (libinput_event_get_type(ev)) {
- case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
- w->swipe.nfingers = nfingers;
- w->swipe.x = w->width / 2;
- w->swipe.y = w->height / 2;
- break;
- case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
- dx = libinput_event_gesture_get_dx(g);
- dy = libinput_event_gesture_get_dy(g);
- w->swipe.x += dx;
- w->swipe.y += dy;
- break;
- case LIBINPUT_EVENT_GESTURE_SWIPE_END:
- w->swipe.nfingers = 0;
- w->swipe.x = w->width / 2;
- w->swipe.y = w->height / 2;
- break;
- default:
- abort();
- }
- }
- static void
- handle_event_pinch(struct libinput_event *ev, struct window *w)
- {
- struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev);
- int nfingers;
- double dx, dy;
- nfingers = libinput_event_gesture_get_finger_count(g);
- switch (libinput_event_get_type(ev)) {
- case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
- w->pinch.nfingers = nfingers;
- w->pinch.x = w->width / 2;
- w->pinch.y = w->height / 2;
- break;
- case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
- dx = libinput_event_gesture_get_dx(g);
- dy = libinput_event_gesture_get_dy(g);
- w->pinch.x += dx;
- w->pinch.y += dy;
- w->pinch.scale = libinput_event_gesture_get_scale(g);
- w->pinch.angle += libinput_event_gesture_get_angle_delta(g);
- break;
- case LIBINPUT_EVENT_GESTURE_PINCH_END:
- w->pinch.nfingers = 0;
- w->pinch.x = w->width / 2;
- w->pinch.y = w->height / 2;
- w->pinch.angle = 0.0;
- w->pinch.scale = 1.0;
- break;
- default:
- abort();
- }
- }
- static void
- handle_event_hold(struct libinput_event *ev, struct window *w)
- {
- struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev);
- int nfingers;
- nfingers = libinput_event_gesture_get_finger_count(g);
- switch (libinput_event_get_type(ev)) {
- case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN:
- w->hold.nfingers = nfingers;
- w->hold.active = true;
- break;
- case LIBINPUT_EVENT_GESTURE_HOLD_END:
- w->hold.nfingers = nfingers;
- w->hold.active = false;
- break;
- default:
- abort();
- }
- }
- static void
- handle_event_tablet(struct libinput_event *ev, struct window *w)
- {
- struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(ev);
- struct libinput_tablet_tool *tool = libinput_event_tablet_tool_get_tool(t);
- double x, y;
- struct point point;
- int idx;
- const int mask = ARRAY_LENGTH(w->tool.deltas);
- bool is_press;
- unsigned int button;
- x = libinput_event_tablet_tool_get_x_transformed(t, w->width);
- y = libinput_event_tablet_tool_get_y_transformed(t, w->height);
- switch (libinput_event_get_type(ev)) {
- case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
- tools_tablet_tool_apply_config(tool, &w->options);
- if (libinput_event_tablet_tool_get_proximity_state(t) ==
- LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT) {
- w->tool.x_in = 0;
- w->tool.y_in = 0;
- w->tool.x_down = 0;
- w->tool.y_down = 0;
- w->tool.x_up = 0;
- w->tool.y_up = 0;
- } else {
- w->tool.x_in = x;
- w->tool.y_in = y;
- w->tool.ndeltas = 0;
- w->tool.deltas[0].x = w->width / 2;
- w->tool.deltas[0].y = w->height / 2;
- }
- break;
- case LIBINPUT_EVENT_TABLET_TOOL_TIP:
- w->tool.pressure = libinput_event_tablet_tool_get_pressure(t);
- w->tool.distance = libinput_event_tablet_tool_get_distance(t);
- w->tool.tilt_x = libinput_event_tablet_tool_get_tilt_x(t);
- w->tool.tilt_y = libinput_event_tablet_tool_get_tilt_y(t);
- if (libinput_event_tablet_tool_get_tip_state(t) ==
- LIBINPUT_TABLET_TOOL_TIP_DOWN) {
- w->tool.x_down = x;
- w->tool.y_down = y;
- w->tool.is_down = true;
- } else {
- w->tool.x_up = x;
- w->tool.y_up = y;
- w->tool.is_down = false;
- }
- _fallthrough_;
- case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
- w->tool.x = x;
- w->tool.y = y;
- w->tool.pressure = libinput_event_tablet_tool_get_pressure(t);
- w->tool.distance = libinput_event_tablet_tool_get_distance(t);
- w->tool.tilt_x = libinput_event_tablet_tool_get_tilt_x(t);
- w->tool.tilt_y = libinput_event_tablet_tool_get_tilt_y(t);
- w->tool.rotation = libinput_event_tablet_tool_get_rotation(t);
- w->tool.size_major = libinput_event_tablet_tool_get_size_major(t);
- w->tool.size_minor = libinput_event_tablet_tool_get_size_minor(t);
- /* Add the delta to the last position and store them as abs
- * coordinates */
- idx = w->tool.ndeltas % mask;
- point = w->tool.deltas[idx];
- idx = (w->tool.ndeltas + 1) % mask;
- point.x += libinput_event_tablet_tool_get_dx(t);
- point.y += libinput_event_tablet_tool_get_dy(t);
- w->tool.deltas[idx] = point;
- w->tool.ndeltas++;
- break;
- case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
- is_press = libinput_event_tablet_tool_get_button_state(t) ==
- LIBINPUT_BUTTON_STATE_PRESSED;
- button = libinput_event_tablet_tool_get_button(t);
- w->buttons.other = is_press;
- w->buttons.other_name = libevdev_event_code_get_name(EV_KEY, button);
- break;
- default:
- abort();
- }
- }
- static void
- handle_event_tablet_pad(struct libinput_event *ev, struct window *w)
- {
- struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
- bool is_press;
- unsigned int button;
- static const char *pad_buttons[] = { "Pad 0", "Pad 1", "Pad 2", "Pad 3",
- "Pad 4", "Pad 5", "Pad 6", "Pad 7",
- "Pad 8", "Pad 9", "Pad >= 10" };
- double position, delta;
- double number;
- switch (libinput_event_get_type(ev)) {
- case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
- is_press = libinput_event_tablet_pad_get_button_state(p) ==
- LIBINPUT_BUTTON_STATE_PRESSED;
- button = libinput_event_tablet_pad_get_button_number(p);
- w->buttons.other = is_press;
- w->buttons.other_name = pad_buttons[min(button, 10)];
- break;
- case LIBINPUT_EVENT_TABLET_PAD_RING:
- position = libinput_event_tablet_pad_get_ring_position(p);
- number = libinput_event_tablet_pad_get_ring_number(p);
- w->pad.ring.number = number;
- w->pad.ring.position = position;
- break;
- case LIBINPUT_EVENT_TABLET_PAD_STRIP:
- position = libinput_event_tablet_pad_get_strip_position(p);
- number = libinput_event_tablet_pad_get_strip_number(p);
- w->pad.strip.number = number;
- w->pad.strip.position = position;
- break;
- case LIBINPUT_EVENT_TABLET_PAD_DIAL:
- delta = libinput_event_tablet_pad_get_dial_delta_v120(p);
- number = libinput_event_tablet_pad_get_dial_number(p);
- if (w->pad.dial.number != number)
- w->pad.dial.position = -delta;
- w->pad.dial.number = number;
- w->pad.dial.position += delta;
- break;
- default:
- abort();
- }
- }
- static gboolean
- handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)
- {
- struct libinput *li = data;
- struct window *w = libinput_get_user_data(li);
- struct libinput_event *ev;
- tools_dispatch(li);
- while ((ev = libinput_get_event(li))) {
- switch (libinput_event_get_type(ev)) {
- case LIBINPUT_EVENT_NONE:
- abort();
- case LIBINPUT_EVENT_DEVICE_ADDED:
- case LIBINPUT_EVENT_DEVICE_REMOVED:
- handle_event_device_notify(ev);
- break;
- case LIBINPUT_EVENT_POINTER_MOTION:
- handle_event_motion(ev, w);
- break;
- case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
- handle_event_absmotion(ev, w);
- break;
- case LIBINPUT_EVENT_TOUCH_DOWN:
- case LIBINPUT_EVENT_TOUCH_MOTION:
- case LIBINPUT_EVENT_TOUCH_UP:
- case LIBINPUT_EVENT_TOUCH_CANCEL:
- handle_event_touch(ev, w);
- break;
- case LIBINPUT_EVENT_TOUCH_FRAME:
- break;
- case LIBINPUT_EVENT_POINTER_AXIS:
- /* ignore */
- break;
- case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
- case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
- case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS:
- handle_event_axis(ev, w);
- break;
- case LIBINPUT_EVENT_POINTER_BUTTON:
- handle_event_button(ev, w);
- break;
- case LIBINPUT_EVENT_KEYBOARD_KEY:
- if (handle_event_keyboard(ev, w)) {
- libinput_event_destroy(ev);
- window_quit(w);
- return FALSE;
- }
- break;
- case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
- case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
- case LIBINPUT_EVENT_GESTURE_SWIPE_END:
- handle_event_swipe(ev, w);
- break;
- case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
- case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
- case LIBINPUT_EVENT_GESTURE_PINCH_END:
- handle_event_pinch(ev, w);
- break;
- case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN:
- case LIBINPUT_EVENT_GESTURE_HOLD_END:
- handle_event_hold(ev, w);
- break;
- case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
- case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
- case LIBINPUT_EVENT_TABLET_TOOL_TIP:
- case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
- handle_event_tablet(ev, w);
- break;
- case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
- case LIBINPUT_EVENT_TABLET_PAD_RING:
- case LIBINPUT_EVENT_TABLET_PAD_STRIP:
- case LIBINPUT_EVENT_TABLET_PAD_DIAL:
- handle_event_tablet_pad(ev, w);
- break;
- case LIBINPUT_EVENT_TABLET_PAD_KEY:
- break;
- case LIBINPUT_EVENT_SWITCH_TOGGLE:
- break;
- }
- libinput_event_destroy(ev);
- }
- gtk_widget_queue_draw(w->area);
- return TRUE;
- }
- static void
- sockets_init(struct libinput *li)
- {
- GIOChannel *c = g_io_channel_unix_new(libinput_get_fd(li));
- g_io_channel_set_encoding(c, NULL, NULL);
- g_io_add_watch(c, G_IO_IN, handle_event_libinput, li);
- }
- static void
- usage(struct option *opts)
- {
- printf("Usage: libinput debug-gui [options] [--udev <seat>|[--device] /dev/input/event0]\n");
- if (opts)
- tools_print_usage_option_list(opts);
- }
- static gboolean
- signal_handler(void *data)
- {
- struct libinput *li = data;
- struct window *w = libinput_get_user_data(li);
- window_quit(w);
- return FALSE;
- }
- int
- main(int argc, char **argv)
- {
- struct window w = { 0 };
- struct tools_options options;
- struct libinput *li;
- enum tools_backend backend = BACKEND_NONE;
- const char *seat_or_device[2] = { "seat0", NULL };
- bool verbose = false;
- bool gtk_init = false;
- #ifdef HAVE_GTK4
- gtk_init = gtk_init_check();
- #else
- gtk_init = gtk_init_check(&argc, &argv);
- #endif
- if (!gtk_init)
- return 77;
- tools_init_options(&options);
- while (1) {
- int c;
- int option_index = 0;
- enum {
- OPT_DEVICE = 1,
- OPT_UDEV,
- OPT_GRAB,
- OPT_VERBOSE,
- };
- /* clang-format off */
- static struct option opts[] = {
- CONFIGURATION_OPTIONS,
- { "help", no_argument, 0, 'h' },
- { "device", required_argument, 0, OPT_DEVICE },
- { "udev", required_argument, 0, OPT_UDEV },
- { "grab", no_argument, 0, OPT_GRAB },
- { "verbose", no_argument, 0, OPT_VERBOSE },
- { 0, 0, 0, 0},
- };
- /* clang-format on */
- c = getopt_long(argc, argv, "h", opts, &option_index);
- if (c == -1)
- break;
- switch (c) {
- case '?':
- exit(EXIT_INVALID_USAGE);
- break;
- case 'h':
- usage(opts);
- exit(0);
- break;
- case OPT_DEVICE:
- backend = BACKEND_DEVICE;
- seat_or_device[0] = optarg;
- break;
- case OPT_UDEV:
- backend = BACKEND_UDEV;
- seat_or_device[0] = optarg;
- break;
- case OPT_GRAB:
- w.grab = true;
- break;
- case OPT_VERBOSE:
- verbose = true;
- break;
- default:
- if (tools_parse_option(c, optarg, &options) != 0) {
- usage(NULL);
- return EXIT_INVALID_USAGE;
- }
- break;
- }
- }
- if (optind < argc) {
- if (optind < argc - 1 || backend != BACKEND_NONE) {
- usage(NULL);
- return EXIT_INVALID_USAGE;
- }
- backend = BACKEND_DEVICE;
- seat_or_device[0] = argv[optind];
- } else if (backend == BACKEND_NONE) {
- backend = BACKEND_UDEV;
- }
- bool with_plugins = (options.plugins == 1);
- li = tools_open_backend(backend,
- seat_or_device,
- verbose,
- &w.grab,
- with_plugins,
- steal(&options.plugin_paths));
- if (!li)
- return EXIT_FAILURE;
- libinput_set_user_data(li, &w);
- g_unix_signal_add(SIGINT, signal_handler, li);
- window_init(&w);
- w.options = options;
- sockets_init(li);
- handle_event_libinput(NULL, 0, li);
- w.event_loop = g_main_loop_new(NULL, FALSE);
- g_main_loop_run(w.event_loop);
- window_unlock_pointer(&w);
- window_cleanup(&w);
- libinput_unref(li);
- return EXIT_SUCCESS;
- }
|