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

Peter Hutterer peter.hutterer at who-t.net
Sun Mar 1 22:53:10 PST 2015


On Thu, Feb 26, 2015 at 04:20:49PM -0500, Benjamin Tissoires wrote:
> 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.

I'll add the distance to the axes exported in libwacom. Not sure what you
mean with the relative mouse mode though, can you explain further?

Cheers,
   Peter

> >         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