[PATCH libinput 19/26] tablet: support z-rotation for the mouse/lens tool

Benjamin Tissoires benjamin.tissoires at gmail.com
Thu Feb 26 13:20:49 PST 2015


On Tue, Feb 24, 2015 at 1:21 AM, Peter Hutterer
<peter.hutterer at who-t.net> wrote:
> Needs to be calculated from the x/y tilt values, the mouse has a fixed offset
> of 175 degrees counterclockwise.
>
> Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> ---
>  src/evdev-tablet.c     | 88 +++++++++++++++++++++++++++++++++++++++++++++-----
>  src/libinput-private.h |  2 +-
>  src/libinput.c         |  1 +
>  src/libinput.h         |  6 ++++
>  test/tablet.c          | 77 +++++++++++++++++++++++++++++++++++++++++++
>  tools/event-debug.c    |  8 +++++
>  6 files changed, 173 insertions(+), 9 deletions(-)
>
> diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c
> index 52742cf..dce7ced 100644
> --- a/src/evdev-tablet.c
> +++ b/src/evdev-tablet.c
> @@ -71,12 +71,25 @@ static int
>  tablet_device_has_axis(struct tablet_dispatch *tablet,
>                        enum libinput_tablet_axis axis)
>  {
> +       struct libevdev *evdev = tablet->device->evdev;
> +       bool has_axis = false;
>         unsigned int code;
>
> -       code = axis_to_evcode(axis);
> -       return libevdev_has_event_code(tablet->device->evdev,
> -                                      EV_ABS,
> -                                      code);
> +       if (axis == LIBINPUT_TABLET_AXIS_ROTATION_Z) {
> +               has_axis = (libevdev_has_event_code(evdev,
> +                                                   EV_ABS,
> +                                                   ABS_TILT_X) &&
> +                           libevdev_has_event_code(evdev,
> +                                                   EV_ABS,
> +                                                   ABS_TILT_Y));
> +       } else {
> +               code = axis_to_evcode(axis);
> +               has_axis = libevdev_has_event_code(evdev,
> +                                                  EV_ABS,
> +                                                  code);
> +       }
> +
> +       return has_axis;
>  }
>
>  static void
> @@ -201,6 +214,32 @@ invert_axis(const struct input_absinfo *absinfo)
>  }
>
>  static void
> +convert_tilt_to_rotation(struct tablet_dispatch *tablet)
> +{
> +       const int offset = 5;
> +       double x, y;
> +       double angle = 0.0;
> +
> +       /* Wacom Intuos 4, 5, Pro mouse calculates rotation from the x/y tilt
> +          values. The device has a 175 degree CCW hardware offset but since we use
> +          atan2 the effective offset is just 5 degrees.
> +          */
> +       x = tablet->axes[LIBINPUT_TABLET_AXIS_TILT_X];
> +       y = tablet->axes[LIBINPUT_TABLET_AXIS_TILT_Y];
> +       clear_bit(tablet->changed_axes, LIBINPUT_TABLET_AXIS_TILT_X);
> +       clear_bit(tablet->changed_axes, LIBINPUT_TABLET_AXIS_TILT_Y);
> +
> +       /* atan2 is CCW, we want CW -> negate x */
> +       if (x || y)
> +               angle = ((180.0 * atan2(-x, y)) / M_PI);
> +
> +       angle = fmod(360 + angle - offset, 360);
> +
> +       tablet->axes[LIBINPUT_TABLET_AXIS_ROTATION_Z] = angle;
> +       set_bit(tablet->changed_axes, LIBINPUT_TABLET_AXIS_ROTATION_Z);
> +}
> +
> +static void
>  tablet_check_notify_axes(struct tablet_dispatch *tablet,
>                          struct evdev_device *device,
>                          uint32_t time,
> @@ -209,12 +248,30 @@ tablet_check_notify_axes(struct tablet_dispatch *tablet,
>         struct libinput_device *base = &device->base;
>         bool axis_update_needed = false;
>         int a;
> +       double axes[LIBINPUT_TABLET_AXIS_MAX + 1] = {0};
>
>         for (a = LIBINPUT_TABLET_AXIS_X; a <= LIBINPUT_TABLET_AXIS_MAX; a++) {
>                 const struct input_absinfo *absinfo;
>
> -               if (!bit_is_set(tablet->changed_axes, a))
> +               if (!bit_is_set(tablet->changed_axes, a)) {
> +                       axes[a] = tablet->axes[a];
>                         continue;
> +               }
> +
> +               axis_update_needed = true;
> +
> +               /* 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];
> +                       }
> +                       continue;
> +               }
>
>                 absinfo = libevdev_get_abs_info(device->evdev,
>                                                 axis_to_evcode(a));
> @@ -241,7 +298,7 @@ tablet_check_notify_axes(struct tablet_dispatch *tablet,
>                         break;
>                 }
>
> -               axis_update_needed = true;
> +               axes[a] = tablet->axes[a];
>         }
>
>         /* We need to make sure that we check that the tool is not out of
> @@ -258,13 +315,13 @@ tablet_check_notify_axes(struct tablet_dispatch *tablet,
>                                                 tool,
>                                                 LIBINPUT_TOOL_PROXIMITY_IN,
>                                                 tablet->changed_axes,
> -                                               tablet->axes);
> +                                               axes);
>                 else
>                         tablet_notify_axis(base,
>                                            time,
>                                            tool,
>                                            tablet->changed_axes,
> -                                          tablet->axes);
> +                                          axes);
>         }
>
>         memset(tablet->changed_axes, 0, sizeof(tablet->changed_axes));
> @@ -454,6 +511,9 @@ tool_set_bits_from_libwacom(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);
>                 break;
> +       case WSTYLUS_PUCK:
> +               copy_axis_cap(tablet, tool, LIBINPUT_TABLET_AXIS_ROTATION_Z);
> +               break;

Further tests showed that the puck needs to export the Distance as
well. Otherwise, it becomes impossible to have a sane relative mouse
mode without asking the user to raise the mouse out of proximity.

Cheers,
Benjamin

>         default:
>                 break;
>         }
> @@ -491,6 +551,10 @@ 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);
>                 break;
> +       case LIBINPUT_TOOL_MOUSE:
> +       case LIBINPUT_TOOL_LENS:
> +               copy_axis_cap(tablet, tool, LIBINPUT_TABLET_AXIS_ROTATION_Z);
> +               break;
>         default:
>                 break;
>         }
> @@ -648,6 +712,14 @@ sanitize_tablet_axes(struct tablet_dispatch *tablet)
>                 else
>                         tablet->axes[LIBINPUT_TABLET_AXIS_PRESSURE] = 0;
>         }
> +
> +       /* If we have a mouse/lens cursor and the tilt changed, the rotation
> +          changed. Mark this, calculate the angle later */
> +       if ((tablet->current_tool_type == LIBINPUT_TOOL_MOUSE ||
> +           tablet->current_tool_type == LIBINPUT_TOOL_LENS) &&
> +           (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_AXIS_TILT_X) ||
> +            bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_AXIS_TILT_Y)))
> +               set_bit(tablet->changed_axes, LIBINPUT_TABLET_AXIS_ROTATION_Z);
>  }
>
>  static void
> diff --git a/src/libinput-private.h b/src/libinput-private.h
> index 9f32655..20d9e43 100644
> --- a/src/libinput-private.h
> +++ b/src/libinput-private.h
> @@ -30,7 +30,7 @@
>  #include "libinput.h"
>  #include "libinput-util.h"
>
> -#define LIBINPUT_TABLET_AXIS_MAX LIBINPUT_TABLET_AXIS_TILT_Y
> +#define LIBINPUT_TABLET_AXIS_MAX LIBINPUT_TABLET_AXIS_ROTATION_Z
>
>  struct libinput_source;
>
> diff --git a/src/libinput.c b/src/libinput.c
> index 5ed3ffc..87ddc69 100644
> --- a/src/libinput.c
> +++ b/src/libinput.c
> @@ -584,6 +584,7 @@ libinput_event_tablet_get_axis_value(struct libinput_event_tablet *event,
>                 case LIBINPUT_TABLET_AXIS_PRESSURE:
>                 case LIBINPUT_TABLET_AXIS_TILT_X:
>                 case LIBINPUT_TABLET_AXIS_TILT_Y:
> +               case LIBINPUT_TABLET_AXIS_ROTATION_Z:
>                         return event->axes[axis];
>                 default:
>                         return 0;
> diff --git a/src/libinput.h b/src/libinput.h
> index 02b2c11..eabc9ac 100644
> --- a/src/libinput.h
> +++ b/src/libinput.h
> @@ -142,6 +142,7 @@ enum libinput_tablet_axis {
>         LIBINPUT_TABLET_AXIS_PRESSURE = 4,
>         LIBINPUT_TABLET_AXIS_TILT_X = 5,
>         LIBINPUT_TABLET_AXIS_TILT_Y = 6,
> +       LIBINPUT_TABLET_AXIS_ROTATION_Z = 7,
>  };
>
>  /**
> @@ -1050,6 +1051,11 @@ libinput_event_tablet_axis_has_changed(struct libinput_event_tablet *event,
>   * - @ref LIBINPUT_TABLET_AXIS_TILT_X and @ref LIBINPUT_TABLET_AXIS_TILT_Y -
>   *   normalized value between -1 and 1 that indicates the X or Y tilt of the
>   *   tool
> + * - @ref LIBINPUT_TABLET_AXIS_ROTATION_Z - The z rotation of the tool in
> + *   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.
>   *
>   * @note This function may be called for a specific axis even if
>   * libinput_event_tablet_axis_has_changed() returns 0 for that axis.
> diff --git a/test/tablet.c b/test/tablet.c
> index 9c3d76d..6eca5c4 100644
> --- a/test/tablet.c
> +++ b/test/tablet.c
> @@ -1197,6 +1197,82 @@ START_TEST(mouse_buttons)
>  }
>  END_TEST
>
> +START_TEST(mouse_rotation)
> +{
> +       struct litest_device *dev = litest_current_device();
> +       struct libinput *li = dev->libinput;
> +       struct libinput_event *event;
> +       struct libinput_event_tablet *tev;
> +       int angle;
> +       int tilt_center_x, tilt_center_y;
> +       const struct input_absinfo *abs;
> +       double val, old_val = 0;
> +
> +       struct axis_replacement axes[] = {
> +               { ABS_DISTANCE, 10 },
> +               { ABS_TILT_X, 0 },
> +               { ABS_TILT_Y, 0 },
> +               { -1, -1 }
> +       };
> +
> +       if (!libevdev_has_event_code(dev->evdev,
> +                                   EV_KEY,
> +                                   BTN_TOOL_MOUSE))
> +               return;
> +
> +       abs = libevdev_get_abs_info(dev->evdev, ABS_TILT_X);
> +       ck_assert_notnull(abs);
> +       tilt_center_x = (abs->maximum - abs->minimum + 1) / 2;
> +
> +       abs = libevdev_get_abs_info(dev->evdev, ABS_TILT_Y);
> +       ck_assert_notnull(abs);
> +       tilt_center_y = (abs->maximum - abs->minimum + 1) / 2;
> +
> +       litest_drain_events(li);
> +
> +       litest_push_event_frame(dev);
> +       litest_tablet_proximity_in(dev, 10, 10, axes);
> +       litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
> +       litest_pop_event_frame(dev);
> +
> +       litest_drain_events(li);
> +
> +       /* cos/sin are 90 degrees offset from the north-is-zero that
> +          libinput uses. 175 is the CCW offset in the mouse HW */
> +       for (angle = 5; angle < 360; angle += 5) {
> +               double a = (angle - 90 - 175)/180.0 * M_PI;
> +               int x, y;
> +
> +               x = cos(a) * 20 + tilt_center_x;
> +               y = sin(a) * 20 + tilt_center_y;
> +
> +               litest_event(dev, EV_ABS, ABS_TILT_X, x);
> +               litest_event(dev, EV_ABS, ABS_TILT_Y, y);
> +               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);
> +
> +               /* rounding error galore, we can't test for anything more
> +                  precise than these */
> +               litest_assert_double_lt(val, 360.0);
> +               litest_assert_double_gt(val, old_val);
> +               litest_assert_double_lt(val, angle + 5);
> +
> +               old_val = val;
> +               libinput_event_destroy(event);
> +               litest_assert_empty_queue(li);
> +       }
> +}
> +END_TEST
> +
>  int
>  main(int argc, char **argv)
>  {
> @@ -1219,6 +1295,7 @@ main(int argc, char **argv)
>         litest_add("tablet:pad", pad_buttons_ignored, LITEST_TABLET, LITEST_ANY);
>         litest_add("tablet:mouse", mouse_tool, LITEST_TABLET, LITEST_ANY);
>         litest_add("tablet:mouse", mouse_buttons, LITEST_TABLET, LITEST_ANY);
> +       litest_add("tablet:mouse", mouse_rotation, LITEST_TABLET, LITEST_ANY);
>
>         return litest_run(argc, argv);
>  }
> diff --git a/tools/event-debug.c b/tools/event-debug.c
> index 09f208c..3503143 100644
> --- a/tools/event-debug.c
> +++ b/tools/event-debug.c
> @@ -293,6 +293,7 @@ print_tablet_axis_event(struct libinput_event *ev)
>         struct libinput_event_tablet *t = libinput_event_get_tablet_event(ev);
>         double x, y;
>         double dist, pressure;
> +       double rotation;
>
>         print_event_time(libinput_event_tablet_get_time(t));
>
> @@ -316,6 +317,13 @@ print_tablet_axis_event(struct libinput_event *ev)
>         else
>                 printf("pressure: %.2f%s",
>                        pressure, tablet_axis_changed_sym(t, LIBINPUT_TABLET_AXIS_PRESSURE));
> +
> +       rotation = libinput_event_tablet_get_axis_value(t,
> +                                       LIBINPUT_TABLET_AXIS_ROTATION_Z);
> +       printf(" rotation: %.2f%s",
> +              rotation,
> +              tablet_axis_changed_sym(t, LIBINPUT_TABLET_AXIS_ROTATION_Z));
> +
>         printf("\n");
>  }
>
> --
> 2.1.0
>
> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel


More information about the wayland-devel mailing list