mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-26 14:30:06 +01:00
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:
parent
4e469291b1
commit
b033bc2677
1 changed files with 296 additions and 5 deletions
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue