From 8bf2c5ae16c068b01573ae03a9ceb0421b2e4471 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 2 May 2018 10:50:20 +1000 Subject: [PATCH] tools: libinput-record: print tablet events in --with-libinput Signed-off-by: Peter Hutterer --- tools/libinput-record-verify-yaml.py | 187 +++++++++++++ tools/libinput-record.c | 396 ++++++++++++++++++++++++++- 2 files changed, 582 insertions(+), 1 deletion(-) diff --git a/tools/libinput-record-verify-yaml.py b/tools/libinput-record-verify-yaml.py index 97149082..fb90481c 100755 --- a/tools/libinput-record-verify-yaml.py +++ b/tools/libinput-record-verify-yaml.py @@ -230,6 +230,10 @@ class TestYaml(unittest.TestCase): 'GESTURE_SWIPE_BEGIN', 'GESTURE_SWIPE_UPDATE', 'GESTURE_SWIPE_END', 'GESTURE_PINCH_BEGIN', 'GESTURE_PINCH_UPDATE', 'GESTURE_PINCH_END', + 'TABLET_TOOL_AXIS', 'TABLET_TOOL_PROXIMITY', + 'TABLET_TOOL_BUTTON', 'TABLET_TOOL_TIP', + 'TABLET_PAD_STRIP', 'TABLET_PAD_RING', + 'TABLET_PAD_BUTTON' ] for e in self.libinput_events(): self.assertIn('type', e) @@ -428,6 +432,189 @@ class TestYaml(unittest.TestCase): for d in unaccel: self.assertTrue(isinstance(d, float)) + def test_events_libinput_tablet_pad_button(self): + keys = ['type', 'time', 'button', 'state', 'mode', 'is-toggle'] + + for e in self.libinput_events('TABLET_PAD_BUTTON'): + self.dict_key_crosscheck(e, keys) + + b = e['button'] + self.assertTrue(isinstance(b, int)) + self.assertGreaterEqual(b, 0) + self.assertLessEqual(b, 16) + + state = e['state'] + self.assertIn(state, ['pressed', 'released']) + + m = e['mode'] + self.assertTrue(isinstance(m, int)) + self.assertGreaterEqual(m, 0) + self.assertLessEqual(m, 3) + + t = e['is-toggle'] + self.assertTrue(isinstance(t, bool)) + + def test_events_libinput_tablet_pad_ring(self): + keys = ['type', 'time', 'number', 'position', 'source', 'mode'] + + for e in self.libinput_events('TABLET_PAD_RING'): + self.dict_key_crosscheck(e, keys) + + n = e['number'] + self.assertTrue(isinstance(n, int)) + self.assertGreaterEqual(n, 0) + self.assertLessEqual(n, 4) + + p = e['position'] + self.assertTrue(isinstance(p, float)) + if p != -1.0: # special 'end' case + self.assertGreaterEqual(p, 0.0) + self.assertLess(p, 360.0) + + m = e['mode'] + self.assertTrue(isinstance(m, int)) + self.assertGreaterEqual(m, 0) + self.assertLessEqual(m, 3) + + s = e['source'] + self.assertIn(s, ['finger', 'unknown']) + + def test_events_libinput_tablet_pad_strip(self): + keys = ['type', 'time', 'number', 'position', 'source', 'mode'] + + for e in self.libinput_events('TABLET_PAD_STRIP'): + self.dict_key_crosscheck(e, keys) + + n = e['number'] + self.assertTrue(isinstance(n, int)) + self.assertGreaterEqual(n, 0) + self.assertLessEqual(n, 4) + + p = e['position'] + self.assertTrue(isinstance(p, float)) + if p != -1.0: # special 'end' case + self.assertGreaterEqual(p, 0.0) + self.assertLessEqual(p, 1.0) + + m = e['mode'] + self.assertTrue(isinstance(m, int)) + self.assertGreaterEqual(m, 0) + self.assertLessEqual(m, 3) + + s = e['source'] + self.assertIn(s, ['finger', 'unknown']) + + def test_events_libinput_tablet_tool_proximity(self): + keys = ['type', 'time', 'proximity', 'tool-type', 'serial', 'axes'] + + for e in self.libinput_events('TABLET_TOOL_PROXIMITY'): + for k in keys: + self.assertIn(k, e) + + p = e['proximity'] + self.assertIn(p, ['in', 'out']) + + p = e['tool-type'] + self.assertIn(p, ['pen', 'eraser', 'brush', 'airbrush', 'mouse', + 'lens', 'unknown']) + + s = e['serial'] + self.assertTrue(isinstance(s, int)) + self.assertGreaterEqual(s, 0) + + a = e['axes'] + for ax in e['axes']: + self.assertIn(a, 'pdtrsw') + + def test_events_libinput_tablet_tool(self): + keys = ['type', 'time', 'tip'] + + for e in self.libinput_events(['TABLET_TOOL_AXIS', + 'TABLET_TOOL_TIP']): + for k in keys: + self.assertIn(k, e) + + t = e['tip'] + self.assertIn(t, ['down', 'up']) + + def test_events_libinput_tablet_tool_button(self): + keys = ['type', 'time', 'button', 'state'] + + for e in self.libinput_events('TABLET_TOOL_BUTTON'): + self.dict_key_crosscheck(e, keys) + + b = e['button'] + # STYLUS, STYLUS2, STYLUS3 + self.assertIn(b, [0x14b, 0x14c, 0x139]) + + s = e['state'] + self.assertIn(s, ['pressed', 'released']) + + def test_events_libinput_tablet_tool_axes(self): + for e in self.libinput_events(['TABLET_TOOL_PROXIMITY', + 'TABLET_TOOL_AXIS', + 'TABLET_TOOL_TIP']): + + point = e['point'] + self.assertTrue(isinstance(point, list)) + self.assertEqual(len(point), 2) + for p in point: + self.assertTrue(isinstance(p, float)) + self.assertGreater(p, 0.0) + + try: + tilt = e['tilt'] + self.assertTrue(isinstance(tilt, list)) + self.assertEqual(len(tilt), 2) + for t in tilt: + self.assertTrue(isinstance(t, float)) + except KeyError: + pass + + try: + d = e['distance'] + self.assertTrue(isinstance(d, float)) + self.assertGreaterEqual(d, 0.0) + self.assertNotIn('pressure', e) + except KeyError: + pass + + try: + p = e['pressure'] + self.assertTrue(isinstance(p, float)) + self.assertGreaterEqual(p, 0.0) + self.assertNotIn('distance', e) + except KeyError: + pass + + try: + r = e['rotation'] + self.assertTrue(isinstance(r, float)) + self.assertGreaterEqual(r, 0.0) + except KeyError: + pass + + try: + s = e['slider'] + self.assertTrue(isinstance(s, float)) + self.assertGreaterEqual(s, 0.0) + except KeyError: + pass + + try: + w = e['wheel'] + self.assertTrue(isinstance(w, float)) + self.assertGreaterEqual(w, 0.0) + self.assertIn('wheel-discrete', e) + wd = e['wheel-discrete'] + self.assertTrue(isinstance(wd, 1)) + self.assertGreaterEqual(wd, 0.0) + + def sign(x): (1, -1)[x < 0] + self.assertTrue(sign(w), sign(wd)) + except KeyError: + pass + if __name__ == '__main__': parser = argparse.ArgumentParser(description='Verify a YAML recording') diff --git a/tools/libinput-record.c b/tools/libinput-record.c index 33bb47fb..a2c00cf2 100644 --- a/tools/libinput-record.c +++ b/tools/libinput-record.c @@ -24,6 +24,7 @@ #include "config.h" #include +#include #include #include #include @@ -49,7 +50,7 @@ static const int FILE_VERSION_NUMBER = 1; * cache our events. Simplest way to do this is to just cache the printf * output */ struct li_event { - char msg[128]; + char msg[256]; }; enum event_type { @@ -712,6 +713,382 @@ buffer_gesture_event(struct record_context *ctx, } } +static char * +buffer_tablet_axes(struct libinput_event_tablet_tool *t) +{ + struct libinput_tablet_tool *tool; + char *s = NULL; + int idx = 0; + int len; + double x, y; + char **strv; + + tool = libinput_event_tablet_tool_get_tool(t); + + strv = zalloc(10 * sizeof *strv); + + x = libinput_event_tablet_tool_get_x(t); + y = libinput_event_tablet_tool_get_y(t); + len = xasprintf(&strv[idx++], "point: [%.2f, %.2f]", x, y); + if (len <= 0) + goto out; + + 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); + len = xasprintf(&strv[idx++], "tilt: [%.2f, %.2f]", x, y); + if (len <= 0) + goto out; + } + + if (libinput_tablet_tool_has_distance(tool) || + libinput_tablet_tool_has_pressure(tool)) { + double dist, pressure; + + dist = libinput_event_tablet_tool_get_distance(t); + pressure = libinput_event_tablet_tool_get_pressure(t); + if (dist) + len = xasprintf(&strv[idx++], "distance: %.2f", dist); + else + len = xasprintf(&strv[idx++], "pressure: %.2f", pressure); + if (len <= 0) + goto out; + } + + if (libinput_tablet_tool_has_rotation(tool)) { + double rotation; + + rotation = libinput_event_tablet_tool_get_rotation(t); + len = xasprintf(&strv[idx++], "rotation: %.2f", rotation); + if (len <= 0) + goto out; + } + + if (libinput_tablet_tool_has_slider(tool)) { + double slider; + + slider = libinput_event_tablet_tool_get_slider_position(t); + len = xasprintf(&strv[idx++], "slider: %.2f", slider); + if (len <= 0) + goto out; + + } + + if (libinput_tablet_tool_has_wheel(tool)) { + double wheel; + int delta; + + wheel = libinput_event_tablet_tool_get_wheel_delta(t); + len = xasprintf(&strv[idx++], "wheel: %.2f", wheel); + if (len <= 0) + goto out; + + delta = libinput_event_tablet_tool_get_wheel_delta_discrete(t); + len = xasprintf(&strv[idx++], "wheel-discrete: %d", delta); + if (len <= 0) + goto out; + } + + s = strv_join(strv, ", "); +out: + strv_free(strv); + return s; +} + +static void +buffer_tablet_tool_proximity_event(struct record_context *ctx, + struct libinput_event *e, + struct event *event) +{ + struct libinput_event_tablet_tool *t = + libinput_event_get_tablet_tool_event(e); + struct libinput_tablet_tool *tool = + libinput_event_tablet_tool_get_tool(t); + uint64_t time; + const char *type, *tool_type; + char *axes; + char caps[10] = {0}; + enum libinput_tablet_tool_proximity_state prox; + size_t idx; + + switch (libinput_event_get_type(e)) { + case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: + type = "TABLET_TOOL_PROXIMITY"; + break; + default: + abort(); + } + + switch (libinput_tablet_tool_get_type(tool)) { + case LIBINPUT_TABLET_TOOL_TYPE_PEN: + tool_type = "pen"; + break; + case LIBINPUT_TABLET_TOOL_TYPE_ERASER: + tool_type = "eraser"; + break; + case LIBINPUT_TABLET_TOOL_TYPE_BRUSH: + tool_type = "brush"; + break; + case LIBINPUT_TABLET_TOOL_TYPE_PENCIL: + tool_type = "brush"; + break; + case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH: + tool_type = "airbrush"; + break; + case LIBINPUT_TABLET_TOOL_TYPE_MOUSE: + tool_type = "mouse"; + break; + case LIBINPUT_TABLET_TOOL_TYPE_LENS: + tool_type = "lens"; + break; + default: + tool_type = "unknown"; + break; + } + + prox = libinput_event_tablet_tool_get_proximity_state(t); + + time = ctx->offset ? + libinput_event_tablet_tool_get_time_usec(t) - ctx->offset : 0; + + axes = buffer_tablet_axes(t); + + idx = 0; + if (libinput_tablet_tool_has_pressure(tool)) + caps[idx++] = 'p'; + if (libinput_tablet_tool_has_distance(tool)) + caps[idx++] = 'd'; + if (libinput_tablet_tool_has_tilt(tool)) + caps[idx++] = 't'; + if (libinput_tablet_tool_has_rotation(tool)) + caps[idx++] = 'r'; + if (libinput_tablet_tool_has_slider(tool)) + caps[idx++] = 's'; + if (libinput_tablet_tool_has_wheel(tool)) + caps[idx++] = 'w'; + assert(idx <= ARRAY_LENGTH(caps)); + + event->time = time; + snprintf(event->u.libinput.msg, + sizeof(event->u.libinput.msg), + "{time: %ld.%06ld, type: %s, proximity: %s, tool-type: %s, serial: %" PRIu64 ", axes: %s, %s}", + time / (int)1e6, + time % (int)1e6, + type, + prox ? "in" : "out", + tool_type, + libinput_tablet_tool_get_serial(tool), + caps, + axes); + free(axes); +} + +static void +buffer_tablet_tool_button_event(struct record_context *ctx, + struct libinput_event *e, + struct event *event) +{ + struct libinput_event_tablet_tool *t = + libinput_event_get_tablet_tool_event(e); + uint64_t time; + const char *type; + uint32_t button; + enum libinput_button_state state; + + switch(libinput_event_get_type(e)) { + case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: + type = "TABLET_TOOL_BUTTON"; + break; + default: + abort(); + } + + + button = libinput_event_tablet_tool_get_button(t); + state = libinput_event_tablet_tool_get_button_state(t); + + time = ctx->offset ? + libinput_event_tablet_tool_get_time_usec(t) - ctx->offset : 0; + + event->time = time; + snprintf(event->u.libinput.msg, + sizeof(event->u.libinput.msg), + "{time: %ld.%06ld, type: %s, button: %d, state: %s}", + time / (int)1e6, + time % (int)1e6, + type, + button, + state ? "pressed" : "released"); +} + +static void +buffer_tablet_tool_event(struct record_context *ctx, + struct libinput_event *e, + struct event *event) +{ + struct libinput_event_tablet_tool *t = + libinput_event_get_tablet_tool_event(e); + uint64_t time; + const char *type; + char *axes; + enum libinput_tablet_tool_tip_state tip; + char btn_buffer[30] = {0}; + + switch(libinput_event_get_type(e)) { + case LIBINPUT_EVENT_TABLET_TOOL_AXIS: + type = "TABLET_TOOL_AXIS"; + break; + case LIBINPUT_EVENT_TABLET_TOOL_TIP: + type = "TABLET_TOOL_TIP"; + break; + case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: + type = "TABLET_TOOL_BUTTON"; + break; + default: + abort(); + } + + if (libinput_event_get_type(e) == LIBINPUT_EVENT_TABLET_TOOL_BUTTON) { + uint32_t button; + enum libinput_button_state state; + + button = libinput_event_tablet_tool_get_button(t); + state = libinput_event_tablet_tool_get_button_state(t); + snprintf(btn_buffer, sizeof(btn_buffer), + ", button: %d, state: %s\n", + button, + state ? "pressed" : "released"); + } + + tip = libinput_event_tablet_tool_get_tip_state(t); + + time = ctx->offset ? + libinput_event_tablet_tool_get_time_usec(t) - ctx->offset : 0; + + axes = buffer_tablet_axes(t); + + event->time = time; + snprintf(event->u.libinput.msg, + sizeof(event->u.libinput.msg), + "{time: %ld.%06ld, type: %s%s, tip: %s, %s}", + time / (int)1e6, + time % (int)1e6, + type, + btn_buffer, /* may be empty string */ + tip ? "down" : "up", + axes); + free(axes); +} + +static void +buffer_tablet_pad_button_event(struct record_context *ctx, + struct libinput_event *e, + struct event *event) +{ + struct libinput_event_tablet_pad *p = + libinput_event_get_tablet_pad_event(e); + struct libinput_tablet_pad_mode_group *group; + enum libinput_button_state state; + unsigned int button, mode; + const char *type; + uint64_t time; + + switch(libinput_event_get_type(e)) { + case LIBINPUT_EVENT_TABLET_PAD_BUTTON: + type = "TABLET_PAD_BUTTON"; + break; + default: + abort(); + } + + time = ctx->offset ? + libinput_event_tablet_pad_get_time_usec(p) - ctx->offset : 0; + + 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); + + event->time = time; + snprintf(event->u.libinput.msg, + sizeof(event->u.libinput.msg), + "{time: %ld.%06ld, type: %s, button: %d, state: %s, mode: %d, is-toggle: %s}", + time / (int)1e6, + time % (int)1e6, + type, + button, + state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released", + mode, + libinput_tablet_pad_mode_group_button_is_toggle(group, button) ? "true" : "false" + ); + + +} + +static void +buffer_tablet_pad_ringstrip_event(struct record_context *ctx, + struct libinput_event *e, + struct event *event) +{ + struct libinput_event_tablet_pad *p = + libinput_event_get_tablet_pad_event(e); + const char *source = NULL; + unsigned int mode, number; + const char *type; + uint64_t time; + double pos; + + switch(libinput_event_get_type(e)) { + case LIBINPUT_EVENT_TABLET_PAD_RING: + type = "TABLET_PAD_RING"; + number = libinput_event_tablet_pad_get_ring_number(p); + pos = libinput_event_tablet_pad_get_ring_position(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; + } + break; + case LIBINPUT_EVENT_TABLET_PAD_STRIP: + type = "TABLET_PAD_STRIP"; + number = libinput_event_tablet_pad_get_strip_number(p); + pos = libinput_event_tablet_pad_get_strip_position(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; + } + break; + default: + abort(); + } + + time = ctx->offset ? + libinput_event_tablet_pad_get_time_usec(p) - ctx->offset : 0; + + mode = libinput_event_tablet_pad_get_mode(p); + + event->time = time; + snprintf(event->u.libinput.msg, + sizeof(event->u.libinput.msg), + "{time: %ld.%06ld, type: %s, number: %d, position: %.2f, source: %s, mode: %d}", + time / (int)1e6, + time % (int)1e6, + type, + number, + pos, + source, + mode); +} + static void buffer_libinput_event(struct record_context *ctx, struct libinput_event *e, @@ -754,6 +1131,23 @@ buffer_libinput_event(struct record_context *ctx, case LIBINPUT_EVENT_GESTURE_SWIPE_END: buffer_gesture_event(ctx, e, event); break; + case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: + buffer_tablet_tool_proximity_event(ctx, e, event); + break; + case LIBINPUT_EVENT_TABLET_TOOL_AXIS: + case LIBINPUT_EVENT_TABLET_TOOL_TIP: + buffer_tablet_tool_event(ctx, e, event); + break; + case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: + buffer_tablet_tool_button_event(ctx, e, event); + break; + case LIBINPUT_EVENT_TABLET_PAD_BUTTON: + buffer_tablet_pad_button_event(ctx, e, event); + break; + case LIBINPUT_EVENT_TABLET_PAD_RING: + case LIBINPUT_EVENT_TABLET_PAD_STRIP: + buffer_tablet_pad_ringstrip_event(ctx, e, event); + break; default: break; }