diff options
author | Peter Hutterer <peter.hutterer@who-t.net> | 2018-11-07 11:05:05 +1000 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2018-11-07 15:22:20 +1000 |
commit | b033bc2677e50ef16c3bb8043d61bb99004cc433 (patch) | |
tree | 0eb7407c9eecd401240a5bb44424d9c8b3cd4614 /tools/libinput-debug-gui.c | |
parent | 4e469291b15e00a6faa77b809799b22d6c44ad40 (diff) | |
download | libinput-b033bc2677e50ef16c3bb8043d61bb99004cc433.tar.gz |
tools: draw evdev events in the debug-gui
Listen to the pure evdev events from each device and print them. This makes it
slightly easier to associate certain jumps with the output, or otherwise see
that events are coming in even when libinput doesn't seem to process them
anymore.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Diffstat (limited to 'tools/libinput-debug-gui.c')
-rw-r--r-- | tools/libinput-debug-gui.c | 301 |
1 files changed, 296 insertions, 5 deletions
diff --git a/tools/libinput-debug-gui.c b/tools/libinput-debug-gui.c index cda5be00..b9139fa1 100644 --- a/tools/libinput-debug-gui.c +++ b/tools/libinput-debug-gui.c @@ -38,6 +38,7 @@ #include <gtk/gtk.h> #include <glib.h> #include <glib-unix.h> +#include <libevdev/libevdev.h> #include <libinput.h> #include <libinput-util.h> @@ -55,9 +56,18 @@ 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; GtkWidget *win; GtkWidget *area; @@ -112,6 +122,18 @@ struct window { struct point deltas[64]; } tool; + 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]; }; @@ -128,6 +150,128 @@ msg(const char *fmt, ...) } 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); + cairo_set_source_rgb(cr, .2, .2, .8); + + 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; + + 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; + + 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); + } + +draw_outline: + /* The touchpad outline */ + 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 i; @@ -396,6 +540,8 @@ draw(GtkWidget *widget, cairo_t *cr, gpointer data) cairo_fill(cr); draw_background(w, cr); + draw_evdev_rel(w, cr); + draw_evdev_abs(w, cr); draw_gestures(w, cr); draw_scrollbars(w, cr); @@ -457,6 +603,8 @@ map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data) static void window_init(struct window *w) { + list_init(&w->evdev_devices); + w->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_set_events(w->win, 0); gtk_window_set_title(GTK_WINDOW(w->win), "libinput debugging tool"); @@ -517,6 +665,146 @@ change_ptraccel(struct window *w, double amount) } } +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); + 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: + close(fd); + udev_device_unref(ud); +} + +static void +unregister_evdev_device(struct window *w, struct libinput_device *dev) +{ + struct evdev_device *d; + + list_for_each(d, &w->evdev_devices, node) { + if (d->libinput_device != dev) + continue; + + list_remove(&d->node); + g_source_remove(d->source_id); + libinput_device_unref(d->libinput_device); + libevdev_free(d->evdev); + close(d->fd); + free(d); + w->evdev.last_device = 0; + break; + } +} + static void handle_event_device_notify(struct libinput_event *ev) { @@ -526,19 +814,22 @@ handle_event_device_notify(struct libinput_event *ev) const char *type; size_t i; - if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) + li = libinput_event_get_context(ev); + w = libinput_get_user_data(li); + + if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) { type = "added"; - else + register_evdev_device(w, dev); + } else { type = "removed"; + unregister_evdev_device(w, dev); + } msg("%s %-30s %s\n", libinput_device_get_sysname(dev), libinput_device_get_name(dev), type); - li = libinput_event_get_context(ev); - w = libinput_get_user_data(li); - tools_device_apply_config(libinput_event_get_device(ev), &w->options); |