diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index 0e92ee47..db3f1768 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -103,6 +103,7 @@ tablet_process_absolute(struct tablet_dispatch *tablet, switch (e->code) { case ABS_X: case ABS_Y: + case ABS_Z: case ABS_PRESSURE: case ABS_TILT_X: case ABS_TILT_Y: @@ -240,6 +241,16 @@ convert_tilt_to_rotation(struct tablet_dispatch *tablet) set_bit(tablet->changed_axes, LIBINPUT_TABLET_AXIS_ROTATION_Z); } +static double +convert_to_degrees(const struct input_absinfo *absinfo, double offset) +{ + /* range is [0, 360[, i.e. range + 1 */ + double range = absinfo->maximum - absinfo->minimum + 1; + double value = (absinfo->value - absinfo->minimum) / range; + + return fmod(value * 360.0 + offset, 360.0); +} + static void tablet_check_notify_axes(struct tablet_dispatch *tablet, struct evdev_device *device, @@ -263,14 +274,13 @@ tablet_check_notify_axes(struct tablet_dispatch *tablet, /* ROTATION_Z is higher than TILT_X/Y so we know that the tilt axes are already normalized and set */ - if (a == LIBINPUT_TABLET_AXIS_ROTATION_Z) { - if (tablet->current_tool_type == LIBINPUT_TOOL_MOUSE || - tablet->current_tool_type == LIBINPUT_TOOL_LENS) { - convert_tilt_to_rotation(tablet); - axes[LIBINPUT_TABLET_AXIS_TILT_X] = 0; - axes[LIBINPUT_TABLET_AXIS_TILT_Y] = 0; - axes[a] = tablet->axes[a]; - } + if (a == LIBINPUT_TABLET_AXIS_ROTATION_Z && + (tablet->current_tool_type == LIBINPUT_TOOL_MOUSE || + tablet->current_tool_type == LIBINPUT_TOOL_LENS)) { + convert_tilt_to_rotation(tablet); + axes[LIBINPUT_TABLET_AXIS_TILT_X] = 0; + axes[LIBINPUT_TABLET_AXIS_TILT_Y] = 0; + axes[a] = tablet->axes[a]; continue; } @@ -294,6 +304,10 @@ tablet_check_notify_axes(struct tablet_dispatch *tablet, case LIBINPUT_TABLET_AXIS_TILT_Y: tablet->axes[a] = normalize_tilt(absinfo); break; + case LIBINPUT_TABLET_AXIS_ROTATION_Z: + /* artpen has 0 with buttons pointing east */ + tablet->axes[a] = convert_to_degrees(absinfo, 90); + break; default: log_bug_libinput(device->base.seat->libinput, "Invalid axis update: %d\n", a); @@ -507,6 +521,10 @@ tool_set_bits_from_libwacom(const struct tablet_dispatch *tablet, copy_axis_cap(tablet, tool, LIBINPUT_TABLET_AXIS_SLIDER); /* fall-through */ case WSTYLUS_MARKER: + if (type == WSTYLUS_MARKER) + copy_axis_cap(tablet, tool, + LIBINPUT_TABLET_AXIS_ROTATION_Z); + /* fallthrough */ case WSTYLUS_GENERAL: case WSTYLUS_INKING: case WSTYLUS_CLASSIC: @@ -556,6 +574,7 @@ tool_set_bits(const struct tablet_dispatch *tablet, copy_axis_cap(tablet, tool, LIBINPUT_TABLET_AXIS_TILT_X); copy_axis_cap(tablet, tool, LIBINPUT_TABLET_AXIS_TILT_Y); copy_axis_cap(tablet, tool, LIBINPUT_TABLET_AXIS_SLIDER); + copy_axis_cap(tablet, tool, LIBINPUT_TABLET_AXIS_ROTATION_Z); break; case LIBINPUT_TOOL_MOUSE: case LIBINPUT_TOOL_LENS: diff --git a/src/evdev-tablet.h b/src/evdev-tablet.h index d86bf9e6..7c472cfc 100644 --- a/src/evdev-tablet.h +++ b/src/evdev-tablet.h @@ -75,6 +75,9 @@ evcode_to_axis(const uint32_t evcode) case ABS_Y: axis = LIBINPUT_TABLET_AXIS_Y; break; + case ABS_Z: + axis = LIBINPUT_TABLET_AXIS_ROTATION_Z; + break; case ABS_DISTANCE: axis = LIBINPUT_TABLET_AXIS_DISTANCE; break; @@ -122,6 +125,9 @@ axis_to_evcode(const enum libinput_tablet_axis axis) case LIBINPUT_TABLET_AXIS_TILT_Y: evcode = ABS_TILT_Y; break; + case LIBINPUT_TABLET_AXIS_ROTATION_Z: + evcode = ABS_Z; + break; case LIBINPUT_TABLET_AXIS_SLIDER: evcode = ABS_WHEEL; break; diff --git a/src/libinput.h b/src/libinput.h index a74b651a..502253c6 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1056,7 +1056,8 @@ libinput_event_tablet_axis_has_changed(struct libinput_event_tablet *event, * degrees, clockwise from the tool's logical neutral position. For the * @ref LIBINPUT_TOOL_MOUSE and @ref LIBINPUT_TOOL_LENS tools the logical * neutral position is pointing to the current logical north of the - * tablet. + * tablet. For the @ref LIBINPUT_TOOL_BRUSH tool, the logical neutral + * position is with the buttons pointing up. * - @ref LIBINPUT_TABLET_AXIS_SLIDER - A slider on the tool, normalized * from 0 to 1. e.g. the wheel-like tool on the Wacom Airbrush. * diff --git a/test/tablet.c b/test/tablet.c index fef4fc50..afc654eb 100644 --- a/test/tablet.c +++ b/test/tablet.c @@ -1359,6 +1359,99 @@ START_TEST(airbrush_wheel) } END_TEST +START_TEST(artpen_tool) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet *tev; + struct libinput_tool *tool; + + if (!libevdev_has_event_code(dev->evdev, + EV_ABS, + ABS_Z)) + return; + + litest_drain_events(li); + + litest_event(dev, EV_KEY, BTN_TOOL_PEN, 1); + litest_event(dev, EV_ABS, ABS_MISC, 0x804); /* Art Pen */ + litest_event(dev, EV_MSC, MSC_SERIAL, 1000); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + litest_wait_for_event_of_type(li, + LIBINPUT_EVENT_TABLET_PROXIMITY, + -1); + event = libinput_get_event(li); + tev = libinput_event_get_tablet_event(event); + tool = libinput_event_tablet_get_tool(tev); + ck_assert_notnull(tool); + ck_assert_int_eq(libinput_tool_get_type(tool), + LIBINPUT_TOOL_PEN); + ck_assert(libinput_tool_has_axis(tool, + LIBINPUT_TABLET_AXIS_ROTATION_Z)); + + libinput_event_destroy(event); +} +END_TEST + +START_TEST(artpen_rotation) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet *tev; + const struct input_absinfo *abs; + double val; + double scale; + int angle; + + if (!libevdev_has_event_code(dev->evdev, + EV_ABS, + ABS_Z)) + return; + + litest_drain_events(li); + + abs = libevdev_get_abs_info(dev->evdev, ABS_Z); + ck_assert_notnull(abs); + + litest_event(dev, EV_KEY, BTN_TOOL_BRUSH, 1); + litest_event(dev, EV_ABS, ABS_MISC, 0x804); /* Art Pen */ + litest_event(dev, EV_MSC, MSC_SERIAL, 1000); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + /* start with non-zero */ + litest_event(dev, EV_ABS, ABS_Z, 10); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + litest_drain_events(li); + + scale = (abs->maximum - abs->minimum + 1)/360.0; + for (angle = 0; angle < 360; angle += 8) { + int a = angle * scale + abs->minimum; + + litest_event(dev, EV_ABS, ABS_Z, a); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + litest_wait_for_event_of_type(li, + LIBINPUT_EVENT_TABLET_AXIS, + -1); + event = libinput_get_event(li); + tev = libinput_event_get_tablet_event(event); + ck_assert(libinput_event_tablet_axis_has_changed(tev, + LIBINPUT_TABLET_AXIS_ROTATION_Z)); + val = libinput_event_tablet_get_axis_value(tev, + LIBINPUT_TABLET_AXIS_ROTATION_Z); + + /* artpen has a 90 deg offset cw */ + ck_assert_int_eq(round(val), (angle + 90) % 360); + libinput_event_destroy(event); + litest_assert_empty_queue(li); + } +} +END_TEST + int main(int argc, char **argv) { @@ -1384,6 +1477,8 @@ main(int argc, char **argv) litest_add("tablet:mouse", mouse_rotation, LITEST_TABLET, LITEST_ANY); litest_add("tablet:airbrush", airbrush_tool, LITEST_TABLET, LITEST_ANY); litest_add("tablet:airbrush", airbrush_wheel, LITEST_TABLET, LITEST_ANY); + litest_add("tablet:artpen", artpen_tool, LITEST_TABLET, LITEST_ANY); + litest_add("tablet:artpen", artpen_rotation, LITEST_TABLET, LITEST_ANY); return litest_run(argc, argv); }