libinput/tools/libinput-debug-events.c
Peter Hutterer 28e896916a tools/debug-events: separate event handling from event printing
Move the big switch statement into a helper function and reduce it to a
statement that only does that bits that weren't related to printing.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1156>
2025-03-13 06:13:38 +00:00

1287 lines
36 KiB
C

/*
* 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 <errno.h>
#include <inttypes.h>
#include <getopt.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "linux/input.h"
#include <libinput.h>
#include <libevdev/libevdev.h>
#include "libinput-version.h"
#include "util-strings.h"
#include "util-macros.h"
#include "shared.h"
static uint32_t start_time;
static const uint32_t screen_width = 100;
static const uint32_t screen_height = 100;
static struct tools_options options;
static bool show_keycodes;
static volatile sig_atomic_t stop = 0;
static bool be_quiet = false;
static bool compress_motion_events = false;
static bool is_tty = false;
#define printq(...) ({ if (!be_quiet) printf(__VA_ARGS__); })
static const char *
event_type_to_str(enum libinput_event_type evtype)
{
const char *type;
switch(evtype) {
case LIBINPUT_EVENT_NONE:
abort();
case LIBINPUT_EVENT_DEVICE_ADDED:
type = "DEVICE_ADDED";
break;
case LIBINPUT_EVENT_DEVICE_REMOVED:
type = "DEVICE_REMOVED";
break;
case LIBINPUT_EVENT_KEYBOARD_KEY:
type = "KEYBOARD_KEY";
break;
case LIBINPUT_EVENT_POINTER_MOTION:
type = "POINTER_MOTION";
break;
case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
type = "POINTER_MOTION_ABSOLUTE";
break;
case LIBINPUT_EVENT_POINTER_BUTTON:
type = "POINTER_BUTTON";
break;
case LIBINPUT_EVENT_POINTER_AXIS:
type = "POINTER_AXIS";
break;
case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
type = "POINTER_SCROLL_WHEEL";
break;
case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
type = "POINTER_SCROLL_FINGER";
break;
case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS:
type = "POINTER_SCROLL_CONTINUOUS";
break;
case LIBINPUT_EVENT_TOUCH_DOWN:
type = "TOUCH_DOWN";
break;
case LIBINPUT_EVENT_TOUCH_MOTION:
type = "TOUCH_MOTION";
break;
case LIBINPUT_EVENT_TOUCH_UP:
type = "TOUCH_UP";
break;
case LIBINPUT_EVENT_TOUCH_CANCEL:
type = "TOUCH_CANCEL";
break;
case LIBINPUT_EVENT_TOUCH_FRAME:
type = "TOUCH_FRAME";
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
type = "GESTURE_SWIPE_BEGIN";
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
type = "GESTURE_SWIPE_UPDATE";
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_END:
type = "GESTURE_SWIPE_END";
break;
case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
type = "GESTURE_PINCH_BEGIN";
break;
case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
type = "GESTURE_PINCH_UPDATE";
break;
case LIBINPUT_EVENT_GESTURE_PINCH_END:
type = "GESTURE_PINCH_END";
break;
case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN:
type = "GESTURE_HOLD_BEGIN";
break;
case LIBINPUT_EVENT_GESTURE_HOLD_END:
type = "GESTURE_HOLD_END";
break;
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
type = "TABLET_TOOL_AXIS";
break;
case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
type = "TABLET_TOOL_PROXIMITY";
break;
case LIBINPUT_EVENT_TABLET_TOOL_TIP:
type = "TABLET_TOOL_TIP";
break;
case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
type = "TABLET_TOOL_BUTTON";
break;
case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
type = "TABLET_PAD_BUTTON";
break;
case LIBINPUT_EVENT_TABLET_PAD_RING:
type = "TABLET_PAD_RING";
break;
case LIBINPUT_EVENT_TABLET_PAD_STRIP:
type = "TABLET_PAD_STRIP";
break;
case LIBINPUT_EVENT_TABLET_PAD_KEY:
type = "TABLET_PAD_KEY";
break;
case LIBINPUT_EVENT_TABLET_PAD_DIAL:
type = "TABLET_PAD_DIAL";
break;
case LIBINPUT_EVENT_SWITCH_TOGGLE:
type = "SWITCH_TOGGLE";
break;
}
return type;
}
static char *
print_event_header(struct libinput_event *ev, size_t event_count)
{
/* use for pointer value only, do not dereference */
static void *last_device = NULL;
struct libinput_device *dev = libinput_event_get_device(ev);
const char *type = event_type_to_str(libinput_event_get_type(ev));
char count[10];
if (event_count > 1)
snprintf(count, sizeof(count), "%3zd ", event_count);
else
snprintf(count, sizeof(count), " ");
char prefix = (last_device != dev) ? '-' : ' ';
last_device = dev;
return strdup_printf("%c%-7s %-23s %s",
prefix,
libinput_device_get_sysname(dev),
type,
count);
}
static void
print_event_time(char buf[16], uint32_t time)
{
snprintf(buf, 16, "%+6.3fs", start_time ? (time - start_time) / 1000.0 : 0);
}
static inline char *
print_device_options(struct libinput_device *dev)
{
uint32_t scroll_methods, click_methods;
char *tap = NULL,
*scroll = NULL,
*clickm = NULL,
*dwt = NULL,
*dwtp = NULL,
*pad = NULL;
if (libinput_device_config_tap_get_finger_count(dev)) {
tap = strdup_printf(" tap (dl %s)",
onoff(libinput_device_config_tap_get_drag_lock_enabled(dev)));
}
scroll_methods = libinput_device_config_scroll_get_methods(dev);
if (scroll_methods != LIBINPUT_CONFIG_SCROLL_NO_SCROLL) {
scroll = strdup_printf(" scroll%s%s%s",
(scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG) ? "-2fg" : "",
(scroll_methods & LIBINPUT_CONFIG_SCROLL_EDGE) ? "-edge" : "",
(scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) ? "-button" : "");
}
click_methods = libinput_device_config_click_get_methods(dev);
if (click_methods != LIBINPUT_CONFIG_CLICK_METHOD_NONE) {
clickm = strdup_printf(" click%s%s",
(click_methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "-buttonareas" : "",
(click_methods & LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) ? "-clickfinger" : "");
}
if (libinput_device_config_dwt_is_available(dev)) {
dwt = strdup_printf(" dwt-%s",
onoff(libinput_device_config_dwt_get_enabled(dev) == LIBINPUT_CONFIG_DWT_ENABLED));
}
if (libinput_device_config_dwtp_is_available(dev)) {
dwt = strdup_printf(" dwtp-%s",
onoff(libinput_device_config_dwtp_get_enabled(dev) == LIBINPUT_CONFIG_DWTP_ENABLED));
}
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_TABLET_PAD)) {
pad = strdup_printf(" buttons:%d strips:%d rings:%d mode groups:%d",
libinput_device_tablet_pad_get_num_buttons(dev),
libinput_device_tablet_pad_get_num_strips(dev),
libinput_device_tablet_pad_get_num_rings(dev),
libinput_device_tablet_pad_get_num_mode_groups(dev));
}
char *str = strdup_printf("%s%s%s%s%s%s%s%s%s",
tap ? tap : "",
libinput_device_config_left_handed_is_available(dev) ? " left" : "",
libinput_device_config_scroll_has_natural_scroll(dev) ? " scroll-nat" : "",
libinput_device_config_calibration_has_matrix(dev) ? " calib" : "",
scroll ? scroll : "",
clickm ? clickm : "",
dwt ? dwt : "",
dwtp ? dwtp : "",
pad ? pad : "");
free(tap);
free(scroll);
free(clickm);
free(dwt);
free(dwtp);
free(pad);
return str;
}
static char *
print_device_notify(struct libinput_event *ev)
{
struct libinput_device *dev = libinput_event_get_device(ev);
struct libinput_seat *seat = libinput_device_get_seat(dev);
struct libinput_device_group *group;
double w, h;
static int next_group_id = 0;
intptr_t group_id;
char *size = NULL,
*ntouches = NULL,
*options = NULL;
group = libinput_device_get_device_group(dev);
group_id = (intptr_t)libinput_device_group_get_user_data(group);
if (!group_id) {
group_id = ++next_group_id;
libinput_device_group_set_user_data(group, (void*)group_id);
}
if (libinput_device_get_size(dev, &w, &h) == 0)
size = strdup_printf(" size %.0fx%.0fmm", w, h);
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_TOUCH))
ntouches = strdup_printf(" ntouches %d", libinput_device_touch_get_touch_count(dev));
if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED)
options = print_device_options(dev);
char *str = strdup_printf("%-33s %5s %7s group%-2d cap:%s%s%s%s%s%s%s%s%s%s",
libinput_device_get_name(dev),
libinput_seat_get_physical_name(seat),
libinput_seat_get_logical_name(seat),
(int)group_id,
libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_KEYBOARD) ? "k" : "",
libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_POINTER) ? "p" : "",
libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TOUCH) ? "t" : "",
libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_GESTURE) ? "g" : "",
libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TABLET_TOOL) ? "T" : "",
libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TABLET_PAD) ? "P" : "",
libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_SWITCH) ? "S" : "",
size ? size : "",
ntouches ? ntouches : "",
options ? options : "");
free(size);
free(ntouches);
free(options);
return str;
}
static char *
print_key_event(struct libinput_event *ev)
{
struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev);
enum libinput_key_state state;
uint32_t key;
const char *keyname;
char time[16];
print_event_time(time, libinput_event_keyboard_get_time(k));
state = libinput_event_keyboard_get_key_state(k);
key = libinput_event_keyboard_get_key(k);
if (!show_keycodes && (key >= KEY_ESC && key < KEY_ZENKAKUHANKAKU)) {
keyname = "***";
key = -1;
} else {
keyname = libevdev_event_code_get_name(EV_KEY, key);
keyname = keyname ? keyname : "???";
}
return strdup_printf("%s\t%s (%d) %s",
time,
keyname,
key,
state == LIBINPUT_KEY_STATE_PRESSED ? "pressed" : "released");
}
static char *
print_motion_event(struct libinput_event *ev)
{
struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
double x = libinput_event_pointer_get_dx(p);
double y = libinput_event_pointer_get_dy(p);
double ux = libinput_event_pointer_get_dx_unaccelerated(p);
double uy = libinput_event_pointer_get_dy_unaccelerated(p);
char time[16];
print_event_time(time, libinput_event_pointer_get_time(p));
return strdup_printf("%s\t%6.2f/%6.2f (%+6.2f/%+6.2f)", time, x, y, ux, uy);
}
static char *
print_absmotion_event(struct libinput_event *ev)
{
struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
double x = libinput_event_pointer_get_absolute_x_transformed(
p, screen_width);
double y = libinput_event_pointer_get_absolute_y_transformed(
p, screen_height);
char time[16];
print_event_time(time, libinput_event_pointer_get_time(p));
return strdup_printf("%s\t%6.2f/%6.2f", time, x, y);
}
static char *
print_pointer_button_event(struct libinput_event *ev)
{
struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
enum libinput_button_state state;
const char *buttonname;
int button;
char time[16];
print_event_time(time, libinput_event_pointer_get_time(p));
button = libinput_event_pointer_get_button(p);
buttonname = libevdev_event_code_get_name(EV_KEY, button);
state = libinput_event_pointer_get_button_state(p);
return strdup_printf("%s\t%s (%d) %s, seat count: %u",
time,
buttonname ? buttonname : "???",
button,
state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released",
libinput_event_pointer_get_seat_button_count(p));
}
static char *
print_tablet_axes(struct libinput_event_tablet_tool *t)
{
struct libinput_tablet_tool *tool = libinput_event_tablet_tool_get_tool(t);
double x, y;
char *tilt = NULL,
*distance = NULL,
*rot = NULL,
*whl = NULL,
*sld = NULL,
*size = NULL;
#define changed_sym(ev, ax) \
(libinput_event_tablet_tool_##ax##_has_changed(ev) ? "*" : "")
if (libinput_tablet_tool_has_tilt(tool)) {
x = libinput_event_tablet_tool_get_tilt_x(t);
y = libinput_event_tablet_tool_get_tilt_y(t);
tilt = strdup_printf("\ttilt: %.2f%s/%.2f%s",
x, changed_sym(t, tilt_x),
y, changed_sym(t, tilt_y));
}
if (libinput_tablet_tool_has_distance(tool) ||
libinput_tablet_tool_has_pressure(tool)) {
double dist = libinput_event_tablet_tool_get_distance(t);
double pressure = libinput_event_tablet_tool_get_pressure(t);
if (dist)
distance = strdup_printf("\tdistance: %.2f%s",
dist, changed_sym(t, distance));
else
distance = strdup_printf("\tpressure: %.2f%s",
pressure, changed_sym(t, pressure));
}
if (libinput_tablet_tool_has_rotation(tool)) {
double rotation = libinput_event_tablet_tool_get_rotation(t);
rot = strdup_printf("\trotation: %6.2f%s",
rotation, changed_sym(t, rotation));
}
if (libinput_tablet_tool_has_slider(tool)) {
double slider = libinput_event_tablet_tool_get_slider_position(t);
sld = strdup_printf("\tslider: %.2f%s",
slider, changed_sym(t, slider));
}
if (libinput_tablet_tool_has_wheel(tool)) {
double wheel = libinput_event_tablet_tool_get_wheel_delta(t);
double delta = libinput_event_tablet_tool_get_wheel_delta_discrete(t);
whl = strdup_printf("\twheel: %.2f%s (%d)",
wheel, changed_sym(t, wheel),
(int)delta);
}
if (libinput_tablet_tool_has_size(tool)) {
double major = libinput_event_tablet_tool_get_size_major(t);
double minor = libinput_event_tablet_tool_get_size_minor(t);
size = strdup_printf("\tsize: %.2f%s/%.2f%s",
major, changed_sym(t, size_major),
minor, changed_sym(t, size_minor));
}
x = libinput_event_tablet_tool_get_x(t);
y = libinput_event_tablet_tool_get_y(t);
char *str = strdup_printf("\t%.2f%s/%.2f%s%s%s%s%s%s%s",
x, changed_sym(t, x),
y, changed_sym(t, y),
tilt ? tilt : "",
distance ? distance : "",
rot ? rot : "",
whl ? whl : "",
sld ? sld : "",
size ? size : "");
free(tilt);
free(distance);
free(rot);
free(whl);
free(sld);
free(size);
return str;
}
static char *
print_tablet_tip_event(struct libinput_event *ev)
{
struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(ev);
enum libinput_tablet_tool_tip_state state;
char time[16];
print_event_time(time, libinput_event_tablet_tool_get_time(t));
char *axes = print_tablet_axes(t);
state = libinput_event_tablet_tool_get_tip_state(t);
return strdup_printf("%s\t%s %s",
time,
axes,
state == LIBINPUT_TABLET_TOOL_TIP_DOWN ? "down" : "up");
}
static char *
print_tablet_button_event(struct libinput_event *ev)
{
struct libinput_event_tablet_tool *p = libinput_event_get_tablet_tool_event(ev);
enum libinput_button_state state;
const char *buttonname;
int button;
char time[16];
print_event_time(time, libinput_event_tablet_tool_get_time(p));
button = libinput_event_tablet_tool_get_button(p);
buttonname = libevdev_event_code_get_name(EV_KEY, button);
state = libinput_event_tablet_tool_get_button_state(p);
return strdup_printf("%s\ts%3d (%s) %s, seat count: %u",
time,
button,
buttonname ? buttonname : "???",
state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released",
libinput_event_tablet_tool_get_seat_button_count(p));
}
static char *
print_pointer_axis_event(struct libinput_event *ev)
{
struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
double v = 0, h = 0, v120 = 0, h120 = 0;
const char *have_vert = "",
*have_horiz = "";
const char *source = NULL;
enum libinput_pointer_axis axis;
enum libinput_event_type type;
char time[16];
type = libinput_event_get_type(ev);
switch (type) {
case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
source = "wheel";
break;
case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
source = "finger";
break;
case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS:
source = "continuous";
break;
default:
abort();
break;
}
axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
if (libinput_event_pointer_has_axis(p, axis)) {
v = libinput_event_pointer_get_scroll_value(p, axis);
if (type == LIBINPUT_EVENT_POINTER_SCROLL_WHEEL)
v120 = libinput_event_pointer_get_scroll_value_v120(p, axis);
have_vert = "*";
}
axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
if (libinput_event_pointer_has_axis(p, axis)) {
h = libinput_event_pointer_get_scroll_value(p, axis);
if (type == LIBINPUT_EVENT_POINTER_SCROLL_WHEEL)
h120 = libinput_event_pointer_get_scroll_value_v120(p, axis);
have_horiz = "*";
}
print_event_time(time, libinput_event_pointer_get_time(p));
return strdup_printf("%s\tvert %.2f/%.1f%s horiz %.2f/%.1f%s (%s)",
time,
v, v120, have_vert,
h, h120, have_horiz, source);
}
static char *
print_tablet_axis_event(struct libinput_event *ev)
{
struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(ev);
char time[16];
print_event_time(time, libinput_event_tablet_tool_get_time(t));
char *axes = print_tablet_axes(t);
char *str = strdup_printf("%s\t%s", time, axes);
free(axes);
return str;
}
static char *
print_proximity_event(struct libinput_event *ev)
{
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);
enum libinput_tablet_tool_proximity_state state;
const char *tool_str,
*state_str;
char time[16];
char *axes = NULL,
*proxin = NULL;
switch (libinput_tablet_tool_get_type(tool)) {
case LIBINPUT_TABLET_TOOL_TYPE_PEN:
tool_str = "pen";
break;
case LIBINPUT_TABLET_TOOL_TYPE_ERASER:
tool_str = "eraser";
break;
case LIBINPUT_TABLET_TOOL_TYPE_BRUSH:
tool_str = "brush";
break;
case LIBINPUT_TABLET_TOOL_TYPE_PENCIL:
tool_str = "pencil";
break;
case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH:
tool_str = "airbrush";
break;
case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
tool_str = "mouse";
break;
case LIBINPUT_TABLET_TOOL_TYPE_LENS:
tool_str = "lens";
break;
case LIBINPUT_TABLET_TOOL_TYPE_TOTEM:
tool_str = "totem";
break;
default:
abort();
}
state = libinput_event_tablet_tool_get_proximity_state(t);
print_event_time(time, libinput_event_tablet_tool_get_time(t));
if (state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN) {
axes = print_tablet_axes(t);
state_str = "proximity-in";
} else if (state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT) {
axes = print_tablet_axes(t);
state_str = "proximity-out";
} else {
abort();
}
if (state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN) {
proxin = strdup_printf("\taxes:%s%s%s%s%s%s\tbtn:%s%s%s%s%s%s%s%s%s%s",
libinput_tablet_tool_has_distance(tool) ? "d" : "",
libinput_tablet_tool_has_pressure(tool) ? "p" : "",
libinput_tablet_tool_has_tilt(tool) ? "t" : "",
libinput_tablet_tool_has_rotation(tool) ? "r" : "",
libinput_tablet_tool_has_slider(tool) ? "s" : "",
libinput_tablet_tool_has_wheel(tool) ? "w" : "",
libinput_tablet_tool_has_size(tool) ? "S" : "",
libinput_tablet_tool_has_button(tool, BTN_TOUCH) ? "T" : "",
libinput_tablet_tool_has_button(tool, BTN_STYLUS) ? "S" : "",
libinput_tablet_tool_has_button(tool, BTN_STYLUS2) ? "S2" : "",
libinput_tablet_tool_has_button(tool, BTN_LEFT) ? "L" : "",
libinput_tablet_tool_has_button(tool, BTN_MIDDLE) ? "M" : "",
libinput_tablet_tool_has_button(tool, BTN_RIGHT) ? "R" : "",
libinput_tablet_tool_has_button(tool, BTN_SIDE) ? "Sd" : "",
libinput_tablet_tool_has_button(tool, BTN_EXTRA) ? "Ex" : "",
libinput_tablet_tool_has_button(tool, BTN_0) ? "0" : "");
}
char *str = strdup_printf("%s\t%s\t%-8s (%#" PRIx64 ", id %#" PRIx64 ") %s%s",
time,
axes ? axes : "",
tool_str,
libinput_tablet_tool_get_serial(tool),
libinput_tablet_tool_get_tool_id(tool),
state_str,
proxin ? proxin : "");
free(axes);
free(proxin);
return str;
}
static char *
print_touch_event(struct libinput_event *ev)
{
struct libinput_event_touch *t = libinput_event_get_touch_event(ev);
enum libinput_event_type type = libinput_event_get_type(ev);
char time[16];
char *slot = NULL,
*pos = NULL;
print_event_time(time, libinput_event_touch_get_time(t));
if (type != LIBINPUT_EVENT_TOUCH_FRAME) {
slot = strdup_printf("%d (%d)",
libinput_event_touch_get_slot(t),
libinput_event_touch_get_seat_slot(t));
}
if (type == LIBINPUT_EVENT_TOUCH_DOWN ||
type == LIBINPUT_EVENT_TOUCH_MOTION) {
double x = libinput_event_touch_get_x_transformed(t, screen_width);
double y = libinput_event_touch_get_y_transformed(t, screen_height);
double xmm = libinput_event_touch_get_x(t);
double ymm = libinput_event_touch_get_y(t);
pos = strdup_printf(" %5.2f/%5.2f (%5.2f/%5.2fmm)", x, y, xmm, ymm);
}
char *str = strdup_printf("%s\t%s%s",
time,
slot ? slot : "",
pos ? pos : "");
free(slot);
free(pos);
return str;
}
static char *
print_gesture_event_without_coords(struct libinput_event *ev)
{
struct libinput_event_gesture *t = libinput_event_get_gesture_event(ev);
int finger_count = libinput_event_gesture_get_finger_count(t);
int cancelled = 0;
enum libinput_event_type type;
char time[16];
type = libinput_event_get_type(ev);
if (type == LIBINPUT_EVENT_GESTURE_SWIPE_END ||
type == LIBINPUT_EVENT_GESTURE_PINCH_END ||
type == LIBINPUT_EVENT_GESTURE_HOLD_END)
cancelled = libinput_event_gesture_get_cancelled(t);
print_event_time(time, libinput_event_gesture_get_time(t));
return strdup_printf("%s\t%d%s", time, finger_count, cancelled ? " cancelled" : "");
}
static char *
print_gesture_event_with_coords(struct libinput_event *ev)
{
struct libinput_event_gesture *t = libinput_event_get_gesture_event(ev);
double dx = libinput_event_gesture_get_dx(t);
double dy = libinput_event_gesture_get_dy(t);
double dx_unaccel = libinput_event_gesture_get_dx_unaccelerated(t);
double dy_unaccel = libinput_event_gesture_get_dy_unaccelerated(t);
char time[16];
char *pinch = NULL;
print_event_time(time, libinput_event_gesture_get_time(t));
if (libinput_event_get_type(ev) ==
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE) {
double scale = libinput_event_gesture_get_scale(t);
double angle = libinput_event_gesture_get_angle_delta(t);
pinch = strdup_printf(" %5.2f @ %5.2f", scale, angle);
}
char *str = strdup_printf("%s\t%d %5.2f/%5.2f (%5.2f/%5.2f unaccelerated)",
time,
libinput_event_gesture_get_finger_count(t),
dx, dy, dx_unaccel, dy_unaccel);
free(pinch);
return str;
}
static char *
print_tablet_pad_button_event(struct libinput_event *ev)
{
struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
struct libinput_tablet_pad_mode_group *group;
enum libinput_button_state state;
unsigned int button, mode;
char time[16];
char *toggle = NULL;
print_event_time(time, libinput_event_tablet_pad_get_time(p));
button = libinput_event_tablet_pad_get_button_number(p),
state = libinput_event_tablet_pad_get_button_state(p);
mode = libinput_event_tablet_pad_get_mode(p);
group = libinput_event_tablet_pad_get_mode_group(p);
if (libinput_tablet_pad_mode_group_button_is_toggle(group, button))
toggle = " <mode toggle>";
return strdup_printf("%3d %s (mode %d)%s",
button,
state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released",
mode,
toggle ? toggle : "");
}
static char *
print_tablet_pad_ring_event(struct libinput_event *ev)
{
struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
const char *source = NULL;
unsigned int mode;
char time[16];
print_event_time(time, libinput_event_tablet_pad_get_time(p));
switch (libinput_event_tablet_pad_get_ring_source(p)) {
case LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER:
source = "finger";
break;
case LIBINPUT_TABLET_PAD_RING_SOURCE_UNKNOWN:
source = "unknown";
break;
}
mode = libinput_event_tablet_pad_get_mode(p);
return strdup_printf("%s\tring %d position %.2f (source %s) (mode %d)",
time,
libinput_event_tablet_pad_get_ring_number(p),
libinput_event_tablet_pad_get_ring_position(p),
source,
mode);
}
static char *
print_tablet_pad_strip_event(struct libinput_event *ev)
{
struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
const char *source = NULL;
unsigned int mode;
char time[16];
print_event_time(time, libinput_event_tablet_pad_get_time(p));
switch (libinput_event_tablet_pad_get_strip_source(p)) {
case LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER:
source = "finger";
break;
case LIBINPUT_TABLET_PAD_STRIP_SOURCE_UNKNOWN:
source = "unknown";
break;
}
mode = libinput_event_tablet_pad_get_mode(p);
return strdup_printf("%s\tstrip %d position %.2f (source %s) (mode %d)",
time,
libinput_event_tablet_pad_get_strip_number(p),
libinput_event_tablet_pad_get_strip_position(p),
source,
mode);
}
static char *
print_tablet_pad_key_event(struct libinput_event *ev)
{
struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
enum libinput_key_state state;
uint32_t key;
const char *keyname;
char time[16];
print_event_time(time, libinput_event_tablet_pad_get_time(p));
key = libinput_event_tablet_pad_get_key(p);
if (!show_keycodes && (key >= KEY_ESC && key < KEY_ZENKAKUHANKAKU)) {
keyname = "***";
key = -1;
} else {
keyname = libevdev_event_code_get_name(EV_KEY, key);
keyname = keyname ? keyname : "???";
}
state = libinput_event_tablet_pad_get_key_state(p);
return strdup_printf("%s\t%s (%d) %s",
time,
keyname,
key,
state == LIBINPUT_KEY_STATE_PRESSED ? "pressed" : "released");
}
static char *
print_tablet_pad_dial_event(struct libinput_event *ev)
{
struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
unsigned int mode;
char time[16];
print_event_time(time, libinput_event_tablet_pad_get_time(p));
mode = libinput_event_tablet_pad_get_mode(p);
return strdup_printf("%s\tdial %d delta %.2f (mode %d)",
time,
libinput_event_tablet_pad_get_dial_number(p),
libinput_event_tablet_pad_get_dial_delta_v120(p),
mode);
}
static char *
print_switch_event(struct libinput_event *ev)
{
struct libinput_event_switch *sw = libinput_event_get_switch_event(ev);
enum libinput_switch_state state;
const char *which;
char time[16];
print_event_time(time, libinput_event_switch_get_time(sw));
switch (libinput_event_switch_get_switch(sw)) {
case LIBINPUT_SWITCH_LID:
which = "lid";
break;
case LIBINPUT_SWITCH_TABLET_MODE:
which = "tablet-mode";
break;
default:
abort();
}
state = libinput_event_switch_get_switch_state(sw);
return strdup_printf("%s\tswitch %s state %d", time, which, state);
}
static char *
libinput_event_to_str(struct libinput_event *ev, size_t event_repeat_count)
{
enum libinput_event_type type = libinput_event_get_type(ev);
char *event_header = print_event_header(ev, event_repeat_count);
char *event_str = NULL;
switch (type) {
case LIBINPUT_EVENT_NONE:
abort();
case LIBINPUT_EVENT_DEVICE_ADDED:
event_str = print_device_notify(ev);
break;
case LIBINPUT_EVENT_DEVICE_REMOVED:
event_str = print_device_notify(ev);
break;
case LIBINPUT_EVENT_KEYBOARD_KEY:
event_str = print_key_event(ev);
break;
case LIBINPUT_EVENT_POINTER_MOTION:
event_str = print_motion_event(ev);
break;
case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
event_str = print_absmotion_event(ev);
break;
case LIBINPUT_EVENT_POINTER_BUTTON:
event_str = print_pointer_button_event(ev);
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:
event_str = print_pointer_axis_event(ev);
break;
case LIBINPUT_EVENT_TOUCH_DOWN:
case LIBINPUT_EVENT_TOUCH_MOTION:
case LIBINPUT_EVENT_TOUCH_UP:
case LIBINPUT_EVENT_TOUCH_CANCEL:
event_str = print_touch_event(ev);
break;
case LIBINPUT_EVENT_TOUCH_FRAME:
event_str = print_touch_event(ev);
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
event_str = print_gesture_event_without_coords(ev);
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
event_str = print_gesture_event_with_coords(ev);
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_END:
event_str = print_gesture_event_without_coords(ev);
break;
case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
event_str = print_gesture_event_without_coords(ev);
break;
case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
event_str = print_gesture_event_with_coords(ev);
break;
case LIBINPUT_EVENT_GESTURE_PINCH_END:
event_str = print_gesture_event_without_coords(ev);
break;
case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN:
event_str = print_gesture_event_without_coords(ev);
break;
case LIBINPUT_EVENT_GESTURE_HOLD_END:
event_str = print_gesture_event_without_coords(ev);
break;
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
event_str = print_tablet_axis_event(ev);
break;
case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
event_str = print_proximity_event(ev);
break;
case LIBINPUT_EVENT_TABLET_TOOL_TIP:
event_str = print_tablet_tip_event(ev);
break;
case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
event_str = print_tablet_button_event(ev);
break;
case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
event_str = print_tablet_pad_button_event(ev);
break;
case LIBINPUT_EVENT_TABLET_PAD_RING:
event_str = print_tablet_pad_ring_event(ev);
break;
case LIBINPUT_EVENT_TABLET_PAD_STRIP:
event_str = print_tablet_pad_strip_event(ev);
break;
case LIBINPUT_EVENT_TABLET_PAD_KEY:
event_str = print_tablet_pad_key_event(ev);
break;
case LIBINPUT_EVENT_TABLET_PAD_DIAL:
event_str = print_tablet_pad_dial_event(ev);
break;
case LIBINPUT_EVENT_SWITCH_TOGGLE:
event_str = print_switch_event(ev);
break;
}
char *str = strdup_printf("%s %s", event_header, event_str);
free(event_header);
free(event_str);
return str;
}
static int
handle_and_print_events(struct libinput *li)
{
int rc = -1;
struct libinput_event *ev;
static struct libinput_device *last_device = NULL;
static enum libinput_event_type last_event_type = LIBINPUT_EVENT_NONE;
static size_t event_repeat_count = 0;
static uint32_t last_log_serial = 0;
tools_dispatch(li);
while ((ev = libinput_get_event(li))) {
struct libinput_device *device = libinput_event_get_device(ev);
enum libinput_event_type type = libinput_event_get_type(ev);
if (type == LIBINPUT_EVENT_POINTER_AXIS) {
libinput_event_destroy(ev);
continue;
}
bool is_repeat = false;
switch (type) {
case LIBINPUT_EVENT_POINTER_MOTION:
case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS:
case LIBINPUT_EVENT_TOUCH_MOTION:
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
is_repeat = last_event_type == type && last_device == device && log_serial == last_log_serial;
break;
default:
break;
}
if (is_repeat) {
event_repeat_count++;
if (compress_motion_events)
printq("\e[1A");
} else {
event_repeat_count = 0;
}
if (type != LIBINPUT_EVENT_TOUCH_FRAME || !compress_motion_events) {
char *event_str = libinput_event_to_str(ev, event_repeat_count + 1);
switch (type) {
case LIBINPUT_EVENT_DEVICE_ADDED:
tools_device_apply_config(libinput_event_get_device(ev),
&options);
break;
case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: {
struct libinput_event_tablet_tool *tev =
libinput_event_get_tablet_tool_event(ev);
struct libinput_tablet_tool *tool =
libinput_event_tablet_tool_get_tool(tev);
tools_tablet_tool_apply_config(tool, &options);
break;
}
default:
break;
}
printq("%s\n", event_str);
free(event_str);
}
last_device = device;
if (type != LIBINPUT_EVENT_TOUCH_FRAME)
last_event_type = type;
last_log_serial = log_serial;
libinput_event_destroy(ev);
rc = 0;
}
fflush(stdout);
return rc;
}
static void
sighandler(int signal, siginfo_t *siginfo, void *userdata)
{
stop = 1;
}
static void
mainloop(struct libinput *li)
{
struct pollfd fds;
fds.fd = libinput_get_fd(li);
fds.events = POLLIN;
fds.revents = 0;
/* Handle already-pending device added events */
if (handle_and_print_events(li))
fprintf(stderr, "Expected device added events on startup but got none. "
"Maybe you don't have the right permissions?\n");
/* time offset starts with our first received event */
if (poll(&fds, 1, -1) > -1) {
struct timespec tp;
clock_gettime(CLOCK_MONOTONIC, &tp);
start_time = tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
do {
handle_and_print_events(li);
} while (!stop && poll(&fds, 1, -1) > -1);
}
printf("\n");
}
static void
usage(void) {
printf("Usage: libinput debug-events [options] [--udev <seat>|--device /dev/input/event0 ...]\n");
}
int
main(int argc, char **argv)
{
struct libinput *li;
enum tools_backend backend = BACKEND_NONE;
const char *seat_or_devices[60] = {NULL};
size_t ndevices = 0;
bool grab = false;
bool verbose = false;
struct sigaction act;
tools_init_options(&options);
is_tty = isatty(STDOUT_FILENO);
while (1) {
int c;
int option_index = 0;
enum {
OPT_DEVICE = 1,
OPT_UDEV,
OPT_GRAB,
OPT_VERBOSE,
OPT_SHOW_KEYCODES,
OPT_QUIET,
OPT_COMPRESS_MOTION_EVENTS,
};
static struct option opts[] = {
CONFIGURATION_OPTIONS,
{ "help", no_argument, 0, 'h' },
{ "show-keycodes", no_argument, 0, OPT_SHOW_KEYCODES },
{ "device", required_argument, 0, OPT_DEVICE },
{ "udev", required_argument, 0, OPT_UDEV },
{ "grab", no_argument, 0, OPT_GRAB },
{ "verbose", no_argument, 0, OPT_VERBOSE },
{ "quiet", no_argument, 0, OPT_QUIET },
{ "compress-motion-events", no_argument, 0, OPT_COMPRESS_MOTION_EVENTS },
{ 0, 0, 0, 0}
};
c = getopt_long(argc, argv, "h", opts, &option_index);
if (c == -1)
break;
switch(c) {
case '?':
exit(EXIT_INVALID_USAGE);
break;
case 'h':
usage();
exit(EXIT_SUCCESS);
break;
case OPT_SHOW_KEYCODES:
show_keycodes = true;
break;
case OPT_QUIET:
be_quiet = true;
break;
case OPT_DEVICE:
if (backend == BACKEND_UDEV ||
ndevices >= ARRAY_LENGTH(seat_or_devices)) {
usage();
return EXIT_INVALID_USAGE;
}
backend = BACKEND_DEVICE;
seat_or_devices[ndevices++] = optarg;
break;
case OPT_UDEV:
if (backend == BACKEND_DEVICE ||
ndevices >= ARRAY_LENGTH(seat_or_devices)) {
usage();
return EXIT_INVALID_USAGE;
}
backend = BACKEND_UDEV;
seat_or_devices[0] = optarg;
ndevices = 1;
break;
case OPT_GRAB:
grab = true;
break;
case OPT_VERBOSE:
verbose = true;
break;
case OPT_COMPRESS_MOTION_EVENTS:
/* We compress by using ansi escape sequences */
compress_motion_events = is_tty;
break;
default:
if (tools_parse_option(c, optarg, &options) != 0) {
usage();
return EXIT_INVALID_USAGE;
}
break;
}
}
if (optind < argc) {
if (backend == BACKEND_UDEV) {
usage();
return EXIT_INVALID_USAGE;
}
backend = BACKEND_DEVICE;
do {
if (ndevices >= ARRAY_LENGTH(seat_or_devices)) {
usage();
return EXIT_INVALID_USAGE;
}
seat_or_devices[ndevices++] = argv[optind];
} while(++optind < argc);
} else if (backend == BACKEND_NONE) {
backend = BACKEND_UDEV;
seat_or_devices[0] = "seat0";
}
memset(&act, 0, sizeof(act));
act.sa_sigaction = sighandler;
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGINT, &act, NULL) == -1) {
fprintf(stderr, "Failed to set up signal handling (%s)\n",
strerror(errno));
return EXIT_FAILURE;
}
if (verbose)
printf("libinput version: %s\n", LIBINPUT_VERSION);
li = tools_open_backend(backend, seat_or_devices, verbose, &grab);
if (!li)
return EXIT_FAILURE;
mainloop(li);
libinput_unref(li);
return EXIT_SUCCESS;
}