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>
This commit is contained in:
Peter Hutterer 2018-11-07 11:05:05 +10:00
parent 4e469291b1
commit b033bc2677

View file

@ -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];
};
@ -127,6 +149,128 @@ msg(const char *fmt, ...)
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);
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)
{
@ -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);