Skip to content

Commit

Permalink
tools: draw evdev events in the debug-gui
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
whot committed Nov 7, 2018
1 parent 4e46929 commit b033bc2
Showing 1 changed file with 296 additions and 5 deletions.
301 changes: 296 additions & 5 deletions tools/libinput-debug-gui.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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;
Expand Down Expand Up @@ -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];
};

Expand All @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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)
{
Expand All @@ -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);

Expand Down

0 comments on commit b033bc2

Please sign in to comment.