This change adds four new properties to touch events.
major: diameter of the touch ellipse along the major axis
minor: diameter perpendicular to major axis
pressure: a pressure value mapped into the range [0,1]
orientation: the angle between major and the x axis

Those values are optionally supported by multi-touch drivers, so
zero as a default value is used if the information is missing.

This change also correctly bumps the ABI version to 0.20.0 and
adds the new libinput symbols there.

Signed-off-by: Andreas Pokorny <[email protected]>
---
 src/evdev.c            | 192 +++++++++++++++++++++++++++++++++++++++++-------
 src/evdev.h            |  24 ++++++
 src/libinput-private.h |  10 ++-
 src/libinput.c         | 137 +++++++++++++++++++++++++++++++++-
 src/libinput.h         | 194 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/libinput.sym       |  10 +++
 test/touch.c           | 162 +++++++++++++++++++++++++++++++++++++++++
 7 files changed, 697 insertions(+), 32 deletions(-)

diff --git a/src/evdev.c b/src/evdev.c
index 346f11a..1dbfc61 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -45,6 +45,10 @@
 
 #define DEFAULT_WHEEL_CLICK_ANGLE 15
 #define DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT 200
+#define DEFAULT_TOUCH_PRESSURE 0
+#define DEFAULT_TOUCH_ORIENTATION 0
+#define DEFAULT_TOUCH_MAJOR 0
+#define DEFAULT_TOUCH_MINOR 0
 
 enum evdev_key_type {
        EVDEV_KEY_TYPE_NONE,
@@ -87,6 +91,12 @@ static const struct evdev_udev_tag_match 
evdev_udev_tag_matches[] = {
        { 0 },
 };
 
+static inline double
+deg2rad(double angle)
+{
+       return angle * M_PI/180.0;
+}
+
 static void
 hw_set_key_down(struct evdev_device *device, int code, int pressed)
 {
@@ -245,6 +255,80 @@ evdev_device_transform_y(struct evdev_device *device,
        return scale_axis(device->abs.absinfo_y, y, height);
 }
 
+double
+evdev_device_transform_touch_point_to_mm(struct evdev_device *device,
+                                        int32_t axis_value,
+                                        double axis_angle)
+{
+       double x_res = device->abs.absinfo_x->resolution;
+       double y_res = device->abs.absinfo_y->resolution;
+
+       if (x_res == y_res)
+               return axis_value / x_res;
+
+       if (device->abs.absinfo_orientation == NULL)
+               return axis_value * 2.0 / (x_res + y_res);
+
+       return axis_value / (y_res*fabs(cos(deg2rad(axis_angle))) +
+                            x_res*fabs(sin(deg2rad(axis_angle))));
+}
+
+double
+evdev_device_transform_touch_point(struct evdev_device *device,
+                                  int32_t axis_value,
+                                  double axis_angle,
+                                  uint32_t width,
+                                  uint32_t height)
+{
+       double x_res = device->abs.absinfo_x->resolution;
+       double y_res = device->abs.absinfo_y->resolution;
+       double x_scale = width / (device->abs.dimensions.x + 1.0);
+       double y_scale = height / (device->abs.dimensions.y + 1.0);
+
+       if (x_res == y_res)
+               return axis_value * x_scale;
+
+       if (device->abs.absinfo_orientation == NULL)
+               return axis_value * (x_scale + y_scale) / 2.0;
+
+       return axis_value * (y_scale*fabs(cos(deg2rad(axis_angle))) +
+                            x_scale*fabs(sin(deg2rad(axis_angle))));
+}
+
+double
+evdev_device_transform_orientation(struct evdev_device *device,
+                                  int32_t orientation)
+{
+       const struct input_absinfo *orientation_info =
+           device->abs.absinfo_orientation;
+
+       /* ABS_MT_ORIENTATION is defined as a clockwise rotation - zero
+        * (instead of minimum) is mapped to the y-axis, and maximum is
+        * mapped to the x-axis. So minimum is likely to be negative but
+        * plays no role in scaling the value to degrees.*/
+       if (orientation_info)
+               return (90.0 * orientation) / orientation_info->maximum;
+       else
+               return DEFAULT_TOUCH_ORIENTATION;
+}
+
+double
+evdev_device_transform_pressure(struct evdev_device *device,
+                               int32_t pressure)
+{
+       const struct input_absinfo *pressure_info = 
device->abs.absinfo_pressure;
+
+       if (pressure_info) {
+               double max_pressure = pressure_info->maximum;
+               double min_pressure = pressure_info->minimum;
+               return (double)(pressure - min_pressure) /
+                       (max_pressure - min_pressure);
+       }
+       else {
+               return DEFAULT_TOUCH_PRESSURE;
+       }
+}
+
 static inline void
 normalize_delta(struct evdev_device *device,
                const struct device_coords *delta,
@@ -282,8 +366,14 @@ evdev_flush_pending_event(struct evdev_device *device, 
uint64_t time)
        struct normalized_coords accel, unaccel;
        struct device_coords point;
        struct device_float_coords raw;
+       struct mt_slot *slot_data;
+       struct device_coords default_touch = {
+               .x = DEFAULT_TOUCH_MAJOR,
+               .y = DEFAULT_TOUCH_MINOR
+       };
 
        slot = device->mt.slot;
+       slot_data = &device->mt.slots[slot];
 
        switch (device->pending_event) {
        case EVDEV_NONE:
@@ -314,7 +404,7 @@ evdev_flush_pending_event(struct evdev_device *device, 
uint64_t time)
                if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
                        break;
 
-               if (device->mt.slots[slot].seat_slot != -1) {
+               if (slot_data->seat_slot != -1) {
                        log_bug_kernel(libinput,
                                       "%s: Driver sent multiple touch down for 
the "
                                       "same slot",
@@ -323,38 +413,52 @@ evdev_flush_pending_event(struct evdev_device *device, 
uint64_t time)
                }
 
                seat_slot = ffs(~seat->slot_map) - 1;
-               device->mt.slots[slot].seat_slot = seat_slot;
+               slot_data->seat_slot = seat_slot;
 
                if (seat_slot == -1)
                        break;
 
                seat->slot_map |= 1 << seat_slot;
-               point = device->mt.slots[slot].point;
+               point = slot_data->point;
                transform_absolute(device, &point);
 
-               touch_notify_touch_down(base, time, slot, seat_slot,
-                                       &point);
+               touch_notify_touch_down(base,
+                                       time,
+                                       slot,
+                                       seat_slot,
+                                       &point,
+                                       &slot_data->touch_point,
+                                       slot_data->orientation,
+                                       slot_data->pressure);
                break;
        case EVDEV_ABSOLUTE_MT_MOTION:
                if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
                        break;
 
-               seat_slot = device->mt.slots[slot].seat_slot;
-               point = device->mt.slots[slot].point;
+               seat_slot = slot_data->seat_slot;
+
+               point = slot_data->point;
 
                if (seat_slot == -1)
                        break;
 
                transform_absolute(device, &point);
-               touch_notify_touch_motion(base, time, slot, seat_slot,
-                                         &point);
+
+               touch_notify_touch_motion(base,
+                                         time,
+                                         slot,
+                                         seat_slot,
+                                         &point,
+                                         &slot_data->touch_point,
+                                         slot_data->orientation,
+                                         slot_data->pressure);
                break;
        case EVDEV_ABSOLUTE_MT_UP:
                if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
                        break;
 
-               seat_slot = device->mt.slots[slot].seat_slot;
-               device->mt.slots[slot].seat_slot = -1;
+               seat_slot = slot_data->seat_slot;
+               slot_data->seat_slot = -1;
 
                if (seat_slot == -1)
                        break;
@@ -386,7 +490,14 @@ evdev_flush_pending_event(struct evdev_device *device, 
uint64_t time)
                point = device->abs.point;
                transform_absolute(device, &point);
 
-               touch_notify_touch_down(base, time, -1, seat_slot, &point);
+               touch_notify_touch_down(base,
+                                       time,
+                                       -1,
+                                       seat_slot,
+                                       &point,
+                                       &default_touch,
+                                       DEFAULT_TOUCH_ORIENTATION,
+                                       DEFAULT_TOUCH_PRESSURE);
                break;
        case EVDEV_ABSOLUTE_MOTION:
                point = device->abs.point;
@@ -398,8 +509,14 @@ evdev_flush_pending_event(struct evdev_device *device, 
uint64_t time)
                        if (seat_slot == -1)
                                break;
 
-                       touch_notify_touch_motion(base, time, -1, seat_slot,
-                                                 &point);
+                       touch_notify_touch_motion(base,
+                                                 time,
+                                                 -1,
+                                                 seat_slot,
+                                                 &point,
+                                                 &default_touch,
+                                                 DEFAULT_TOUCH_ORIENTATION,
+                                                 DEFAULT_TOUCH_PRESSURE);
                } else if (device->seat_caps & EVDEV_DEVICE_POINTER) {
                        pointer_notify_motion_absolute(base, time, &point);
                }
@@ -559,8 +676,9 @@ evdev_process_touch(struct evdev_device *device,
                    struct input_event *e,
                    uint64_t time)
 {
-       switch (e->code) {
-       case ABS_MT_SLOT:
+       struct mt_slot *current_slot = &device->mt.slots[device->mt.slot];
+
+       if (e->code == ABS_MT_SLOT) {
                if ((size_t)e->value >= device->mt.slots_len) {
                        log_bug_libinput(device->base.seat->libinput,
                                         "%s exceeds slots (%d of %d)\n",
@@ -571,8 +689,7 @@ evdev_process_touch(struct evdev_device *device,
                }
                evdev_flush_pending_event(device, time);
                device->mt.slot = e->value;
-               break;
-       case ABS_MT_TRACKING_ID:
+       } else if(e->code == ABS_MT_TRACKING_ID) {
                if (device->pending_event != EVDEV_NONE &&
                    device->pending_event != EVDEV_ABSOLUTE_MT_MOTION)
                        evdev_flush_pending_event(device, time);
@@ -580,17 +697,34 @@ evdev_process_touch(struct evdev_device *device,
                        device->pending_event = EVDEV_ABSOLUTE_MT_DOWN;
                else
                        device->pending_event = EVDEV_ABSOLUTE_MT_UP;
-               break;
-       case ABS_MT_POSITION_X:
-               device->mt.slots[device->mt.slot].point.x = e->value;
-               if (device->pending_event == EVDEV_NONE)
-                       device->pending_event = EVDEV_ABSOLUTE_MT_MOTION;
-               break;
-       case ABS_MT_POSITION_Y:
-               device->mt.slots[device->mt.slot].point.y = e->value;
-               if (device->pending_event == EVDEV_NONE)
+       } else {
+               bool needs_wake = true;
+
+               switch(e->code) {
+               case ABS_MT_POSITION_X:
+                       current_slot->point.x = e->value;
+                       break;
+               case ABS_MT_POSITION_Y:
+                       current_slot->point.y = e->value;
+                       break;
+               case ABS_MT_TOUCH_MAJOR:
+                       current_slot->touch_point.x = e->value;
+                       break;
+               case ABS_MT_TOUCH_MINOR:
+                       current_slot->touch_point.y = e->value;
+                       break;
+               case ABS_MT_ORIENTATION:
+                       current_slot->orientation = e->value;
+                       break;
+               case ABS_MT_PRESSURE:
+                       current_slot->pressure = e->value;
+                       break;
+               default:
+                       needs_wake = false;
+                       break;
+               }
+               if (needs_wake && device->pending_event == EVDEV_NONE)
                        device->pending_event = EVDEV_ABSOLUTE_MT_MOTION;
-               break;
        }
 }
 
@@ -1921,6 +2055,8 @@ evdev_configure_device(struct evdev_device *device)
                        return -1;
                }
        }
+       device->abs.absinfo_orientation = libevdev_get_abs_info(evdev, 
ABS_MT_ORIENTATION);
+       device->abs.absinfo_pressure = libevdev_get_abs_info(evdev, 
ABS_MT_PRESSURE);
 
        if (udev_tags & EVDEV_UDEV_TAG_TOUCHPAD) {
                device->dispatch = evdev_mt_touchpad_create(device);
diff --git a/src/evdev.h b/src/evdev.h
index aa548d2..746a3df 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -109,6 +109,9 @@ enum evdev_device_model {
 struct mt_slot {
        int32_t seat_slot;
        struct device_coords point;
+       struct device_coords touch_point;
+       int32_t orientation;
+       int32_t pressure;
 };
 
 struct evdev_device {
@@ -125,6 +128,7 @@ struct evdev_device {
        int fd;
        struct {
                const struct input_absinfo *absinfo_x, *absinfo_y;
+               const struct input_absinfo *absinfo_pressure, 
*absinfo_orientation;
                int fake_resolution;
 
                struct device_coords point;
@@ -344,6 +348,26 @@ double
 evdev_device_transform_y(struct evdev_device *device,
                         double y,
                         uint32_t height);
+double
+evdev_device_transform_touch_point_to_mm(struct evdev_device *device,
+                                        int32_t axis_value,
+                                        double axis_angle);
+
+double
+evdev_device_transform_touch_point(struct evdev_device *device,
+                                  int32_t axis_value,
+                                  double axis_angle,
+                                  uint32_t width,
+                                  uint32_t height);
+
+double
+evdev_device_transform_orientation(struct evdev_device *device,
+                                  int32_t orientation);
+
+double
+evdev_device_transform_pressure(struct evdev_device *device,
+                               int32_t pressure);
+
 int
 evdev_device_suspend(struct evdev_device *device);
 
diff --git a/src/libinput-private.h b/src/libinput-private.h
index d11f000..d960a82 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -350,14 +350,20 @@ touch_notify_touch_down(struct libinput_device *device,
                        uint64_t time,
                        int32_t slot,
                        int32_t seat_slot,
-                       const struct device_coords *point);
+                       const struct device_coords *point,
+                       const struct device_coords *touch_point,
+                       int32_t orientation,
+                       int32_t pressure);
 
 void
 touch_notify_touch_motion(struct libinput_device *device,
                          uint64_t time,
                          int32_t slot,
                          int32_t seat_slot,
-                         const struct device_coords *point);
+                         const struct device_coords *point,
+                         const struct device_coords *touch_point,
+                         int32_t orientation,
+                         int32_t pressure);
 
 void
 touch_notify_touch_up(struct libinput_device *device,
diff --git a/src/libinput.c b/src/libinput.c
index d164604..32ed817 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -112,6 +112,9 @@ struct libinput_event_touch {
        int32_t slot;
        int32_t seat_slot;
        struct device_coords point;
+       struct device_coords touch_point;
+       int32_t orientation;
+       int32_t pressure;
 };
 
 static void
@@ -637,6 +640,124 @@ libinput_event_touch_get_y(struct libinput_event_touch 
*event)
        return evdev_convert_to_mm(device->abs.absinfo_y, event->point.y);
 }
 
+LIBINPUT_EXPORT double
+libinput_event_touch_get_major(struct libinput_event_touch *event)
+{
+       struct evdev_device *device =
+               (struct evdev_device *) event->base.device;
+       double angle = evdev_device_transform_orientation(device,
+                                                         event->orientation);
+
+       require_event_type(libinput_event_get_context(&event->base),
+                          event->base.type,
+                          0,
+                          LIBINPUT_EVENT_TOUCH_DOWN,
+                          LIBINPUT_EVENT_TOUCH_MOTION);
+
+       return evdev_device_transform_touch_point_to_mm(device,
+                                                       event->touch_point.x,
+                                                       angle);
+}
+
+LIBINPUT_EXPORT double
+libinput_event_touch_get_major_transformed(struct libinput_event_touch *event,
+                                          uint32_t width,
+                                          uint32_t height)
+{
+       struct evdev_device *device =
+               (struct evdev_device *) event->base.device;
+       double angle = evdev_device_transform_orientation(device,
+                                                         event->orientation);
+
+       require_event_type(libinput_event_get_context(&event->base),
+                          event->base.type,
+                          0,
+                          LIBINPUT_EVENT_TOUCH_DOWN,
+                          LIBINPUT_EVENT_TOUCH_MOTION);
+
+       return evdev_device_transform_touch_point(device,
+                                                 event->touch_point.x,
+                                                 angle,
+                                                 width,
+                                                 height);
+}
+
+LIBINPUT_EXPORT double
+libinput_event_touch_get_minor(struct libinput_event_touch *event)
+{
+       struct evdev_device *device =
+               (struct evdev_device *) event->base.device;
+       double angle = evdev_device_transform_orientation(device,
+                                                        event->orientation);
+
+       require_event_type(libinput_event_get_context(&event->base),
+                          event->base.type,
+                          0,
+                          LIBINPUT_EVENT_TOUCH_DOWN,
+                          LIBINPUT_EVENT_TOUCH_MOTION);
+
+       return evdev_device_transform_touch_point_to_mm(device,
+                                                       event->touch_point.y,
+                                                       angle + 90.0);
+
+}
+
+LIBINPUT_EXPORT double
+libinput_event_touch_get_minor_transformed(struct libinput_event_touch *event,
+                                          uint32_t width,
+                                          uint32_t height)
+{
+       struct evdev_device *device =
+               (struct evdev_device *) event->base.device;
+       double angle =
+               evdev_device_transform_orientation(device,
+                                                  event->orientation);
+
+       require_event_type(libinput_event_get_context(&event->base),
+                          event->base.type,
+                          0,
+                          LIBINPUT_EVENT_TOUCH_DOWN,
+                          LIBINPUT_EVENT_TOUCH_MOTION);
+
+       return evdev_device_transform_touch_point(device,
+                                                 event->touch_point.y,
+                                                 angle + 90.0,
+                                                 width,
+                                                 height);
+}
+
+LIBINPUT_EXPORT double
+libinput_event_touch_get_orientation(struct libinput_event_touch *event)
+{
+       struct evdev_device *device =
+               (struct evdev_device *) event->base.device;
+
+       require_event_type(libinput_event_get_context(&event->base),
+                          event->base.type,
+                          0,
+                          LIBINPUT_EVENT_TOUCH_DOWN,
+                          LIBINPUT_EVENT_TOUCH_MOTION);
+
+       return evdev_device_transform_orientation(device,
+                                                 event->orientation);
+}
+
+LIBINPUT_EXPORT double
+libinput_event_touch_get_pressure(struct libinput_event_touch *event)
+{
+       struct evdev_device *device =
+               (struct evdev_device *) event->base.device;
+
+       require_event_type(libinput_event_get_context(&event->base),
+                          event->base.type,
+                          0,
+                          LIBINPUT_EVENT_TOUCH_DOWN,
+                          LIBINPUT_EVENT_TOUCH_MOTION);
+
+       return evdev_device_transform_pressure(device,
+                                              event->pressure);
+}
+
 struct libinput_source *
 libinput_add_fd(struct libinput *libinput,
                int fd,
@@ -1243,7 +1364,10 @@ touch_notify_touch_down(struct libinput_device *device,
                        uint64_t time,
                        int32_t slot,
                        int32_t seat_slot,
-                       const struct device_coords *point)
+                       const struct device_coords *point,
+                       const struct device_coords *touch_point,
+                       int32_t orientation,
+                       int32_t pressure)
 {
        struct libinput_event_touch *touch_event;
 
@@ -1259,6 +1383,9 @@ touch_notify_touch_down(struct libinput_device *device,
                .slot = slot,
                .seat_slot = seat_slot,
                .point = *point,
+               .touch_point = *touch_point,
+               .orientation = orientation,
+               .pressure = pressure,
        };
 
        post_device_event(device, time,
@@ -1271,7 +1398,10 @@ touch_notify_touch_motion(struct libinput_device *device,
                          uint64_t time,
                          int32_t slot,
                          int32_t seat_slot,
-                         const struct device_coords *point)
+                         const struct device_coords *point,
+                         const struct device_coords *touch_point,
+                         int32_t orientation,
+                         int32_t pressure)
 {
        struct libinput_event_touch *touch_event;
 
@@ -1287,6 +1417,9 @@ touch_notify_touch_motion(struct libinput_device *device,
                .slot = slot,
                .seat_slot = seat_slot,
                .point = *point,
+               .touch_point = *touch_point,
+               .orientation = orientation,
+               .pressure = pressure,
        };
 
        post_device_event(device, time,
diff --git a/src/libinput.h b/src/libinput.h
index 5df7183..c705398 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -919,6 +919,200 @@ libinput_event_touch_get_y_transformed(struct 
libinput_event_touch *event,
 /**
  * @ingroup event_touch
  *
+ * @verbatim
+ *                                   screen space
+ *        major
+ *  _______/\________
+ * /                 \
+ *       +++++++
+ *    +++       +++
+ *  ++             ++
+ * +     touch       +
+ * +       ellipse   +   ---> finger pointing direction
+ *  ++             ++
+ *    +++       +++
+ *       +++++++
+ * @endverbatim
+ *
+ * Return the diameter of the major axis of the touch ellipse in mm.
+ * This value might not be provided by the device, in that case the value
+ * 0.0 is returned
+ *
+ * For events not of type @ref LIBINPUT_EVENT_TOUCH_DOWN, @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION, this function returns 0.
+ *
+ * @note It is an application bug to call this function for events of type
+ * @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref LIBINPUT_EVENT_TOUCH_MOTION.
+ *
+ * @param event The libinput touch event
+ * @return The current touch major value
+ */
+double
+libinput_event_touch_get_major(struct libinput_event_touch *event);
+
+/**
+ * @ingroup event_touch
+ *
+ * @verbatim
+ *                                   screen space
+ *        major
+ *  _______/\________
+ * /                 \
+ *       +++++++
+ *    +++       +++
+ *  ++             ++
+ * +     touch       +
+ * +       ellipse   +   ---> finger pointing direction
+ *  ++             ++
+ *    +++       +++
+ *       +++++++
+ * @endverbatim
+ *
+ * Return the diameter of the major axis of the touch ellipse in screen
+ * space. This value might not be provided by the device, in that case the
+ * value 0.0 is returned.
+ *
+ * For events not of type @ref LIBINPUT_EVENT_TOUCH_DOWN, @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION, this function returns 0.
+ *
+ * @note It is an application bug to call this function for events of type
+ * @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref LIBINPUT_EVENT_TOUCH_MOTION.
+ *
+ * @param event The libinput touch event
+ * @param width The current output screen width
+ * @param height The current output screen height
+ * @return The current touch major value
+ */
+double
+libinput_event_touch_get_major_transformed(struct libinput_event_touch *event,
+                                          uint32_t width,
+                                          uint32_t height);
+
+/**
+ * @ingroup event_touch
+ *
+ * @verbatim
+ *                                     screen space
+ *
+ *       +++++++       -
+ *    +++       +++     \
+ *  ++             ++   |
+ * +    touch        +  =- minor
+ * +      ellipse    +  |        ---> finger pointing direction
+ *  ++             ++   |
+ *    +++       +++     /
+ *       +++++++       -
+ *
+ * @endverbatim
+ *
+ * Return the diameter of the minor axis of the touch ellipse in mm.
+ * This value might not be provided by the device, in this case the value
+ * 0.0 is returned.
+ *
+ * For events not of type @ref LIBINPUT_EVENT_TOUCH_DOWN, @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION, this function returns 0.
+ *
+ * @note It is an application bug to call this function for events of type
+ * @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref LIBINPUT_EVENT_TOUCH_MOTION.
+ *
+ * @param event The libinput touch event
+ * @return The current touch minor value
+ */
+double
+libinput_event_touch_get_minor(struct libinput_event_touch *event);
+
+/**
+ * @ingroup event_touch
+ *
+ * @verbatim
+ *                                     screen space
+ *
+ *       +++++++       -
+ *    +++       +++     \
+ *  ++             ++   |
+ * +    touch        +  =- minor
+ * +      ellipse    +  |        ---> finger pointing direction
+ *  ++             ++   |
+ *    +++       +++     /
+ *       +++++++       -
+ *
+ * @endverbatim
+ *
+ * Return the diameter of the minor axis of the touch ellipse in screen
+ * space. This value might not be provided by the device, in this case
+ * the value 0.0 is returned.
+ *
+ * For events not of type @ref LIBINPUT_EVENT_TOUCH_DOWN, @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION, this function returns 0.
+ *
+ * @note It is an application bug to call this function for events of type
+ * @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref LIBINPUT_EVENT_TOUCH_MOTION.
+ *
+ * @param event The libinput touch event
+ * @param width The current output screen width
+ * @param height The current output screen height
+ * @return The current touch minor value
+ */
+double
+libinput_event_touch_get_minor_transformed(struct libinput_event_touch *event,
+                                          uint32_t width,
+                                          uint32_t height);
+
+/**
+ * @ingroup event_touch
+ *
+ * Return the current pressure value touch point normalized to the range
+ * [0,1]. If this value is not provided by the device, it is always 0.0.
+ *
+ * For events not of type @ref LIBINPUT_EVENT_TOUCH_DOWN, @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION, this function returns 0.
+ *
+ * @note It is an application bug to call this function for events of type
+ * @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref LIBINPUT_EVENT_TOUCH_MOTION.
+ *
+ * @param event The libinput touch event
+ * @return The current pressure value
+ */
+double
+libinput_event_touch_get_pressure(struct libinput_event_touch *event);
+
+/**
+ * @ingroup event_touch
+ * @verbatim
+ *
+ *    major     ∧ y
+ *    axis \    |
+ *          \++ |
+ *         + \ ++
+ *         +  \ |+
+ *          +  \| +
+ *           +  0--+---------------> x
+ *            +     +
+ *             ++   +
+ *               +++
+ *
+ * @endverbatim
+ *
+ * Return the major axis rotation in degrees, clock wise from the logical north
+ * of the touch screen. In the example drawn above the value would be 315".
+ * This orientation might not be measured by the device, or only measured
+ * in coarse steps (e.g only indicating alignment with either of the axes).
+ *
+ * For events not of type @ref LIBINPUT_EVENT_TOUCH_DOWN, @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION, this function returns 0.
+ *
+ * @note It is an application bug to call this function for events of type
+ * @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref LIBINPUT_EVENT_TOUCH_MOTION.
+ *
+ * @param event The libinput touch event
+ * @return The current orientation value
+ */
+double
+libinput_event_touch_get_orientation(struct libinput_event_touch *event);
+
+/**
+ * @ingroup event_touch
+ *
  * @return The generic libinput_event of this event
  */
 struct libinput_event *
diff --git a/src/libinput.sym b/src/libinput.sym
index 773a7a4..60e4ebd 100644
--- a/src/libinput.sym
+++ b/src/libinput.sym
@@ -147,3 +147,13 @@ global:
        libinput_device_config_tap_get_drag_lock_enabled;
        libinput_device_config_tap_get_default_drag_lock_enabled;
 } LIBINPUT_0.15.0;
+
+LIBINPUT_0.20.0 {
+global:
+       libinput_event_touch_get_minor;
+       libinput_event_touch_get_major;
+       libinput_event_touch_get_minor_transformed;
+       libinput_event_touch_get_major_transformed;
+       libinput_event_touch_get_orientation;
+       libinput_event_touch_get_pressure;
+} LIBINPUT_0.19.0;
diff --git a/test/touch.c b/test/touch.c
index b519613..1e47289 100644
--- a/test/touch.c
+++ b/test/touch.c
@@ -33,6 +33,13 @@
 #include "libinput-util.h"
 #include "litest.h"
 
+static inline void
+fix_resolution(struct input_absinfo *info)
+{
+       if (info->resolution == 0)
+               info->resolution = 1;
+}
+
 START_TEST(touch_frame_events)
 {
        struct litest_device *dev = litest_current_device();
@@ -652,6 +659,157 @@ START_TEST(touch_initial_state)
 }
 END_TEST
 
+START_TEST(touch_point_properties)
+{
+       struct litest_device *dev = litest_current_device();
+       struct libinput *li = dev->libinput;
+       struct libinput_event *ev;
+       struct libinput_event_touch *tev;
+       struct axis_replacement down_values[] = {
+               {ABS_MT_PRESSURE, 128},
+               {ABS_MT_ORIENTATION, 64},
+               {ABS_MT_TOUCH_MAJOR, 14},
+               {ABS_MT_TOUCH_MINOR, 8}};
+       struct axis_replacement move_values[] = {
+               {ABS_MT_ORIENTATION, 128},
+               {ABS_MT_TOUCH_MAJOR, 30}};
+       struct input_absinfo const *orientation_info =
+               libevdev_get_abs_info(dev->evdev, ABS_MT_ORIENTATION);
+       struct input_absinfo const *pressure_info =
+               libevdev_get_abs_info(dev->evdev, ABS_MT_PRESSURE);
+       struct input_absinfo const *x_info =
+               libevdev_get_abs_info(dev->evdev, ABS_MT_POSITION_X);
+       struct input_absinfo const *y_info =
+               libevdev_get_abs_info(dev->evdev, ABS_MT_POSITION_Y);
+       double x_res, y_res, touch_minor_scale, touch_major_scale;
+       double expected_orientation;
+
+       if (!orientation_info || !pressure_info || !x_info || !y_info ||
+           !libevdev_has_event_code(dev->evdev, EV_ABS, ABS_MT_TOUCH_MAJOR)||
+           !libevdev_has_event_code(dev->evdev, EV_ABS, ABS_MT_TOUCH_MINOR))
+               return;
+
+       fix_resolution(x_info);
+       fix_resolution(y_info);
+
+       expected_orientation = 90.0*64.0/orientation_info->maximum;
+       x_res = x_info->resolution;
+       y_res = y_info->resolution;
+
+       touch_major_scale = (x_res == y_res)
+               ? x_res
+               : (y_res*fabs(cos(M_PI * expected_orientation / 180.0)) +
+                  x_res*fabs(sin(M_PI * expected_orientation / 180.0)));
+       touch_minor_scale = (x_res == y_res)
+               ? x_res
+               : (y_res*fabs(sin(M_PI * expected_orientation / 180.0)) +
+                  x_res*fabs(cos(M_PI * expected_orientation / 180.0)));
+
+       litest_drain_events(li);
+
+       litest_touch_down_extended(dev, 0, 5, 95, down_values);
+
+       litest_wait_for_event_of_type(li, LIBINPUT_EVENT_TOUCH_DOWN, -1);
+
+       ev = libinput_get_event(li);
+        tev = litest_is_touch_event(ev, 0);
+
+       ck_assert_double_eq(libinput_event_touch_get_pressure(tev), 128.0/
+                           (pressure_info->maximum - pressure_info->minimum));
+       ck_assert_double_eq(libinput_event_touch_get_orientation(tev),
+                           expected_orientation);
+       ck_assert_double_eq(libinput_event_touch_get_major(tev), 
14.0/touch_major_scale);
+       ck_assert_double_eq(libinput_event_touch_get_minor(tev), 
8.0/touch_minor_scale);
+
+       libinput_event_destroy(ev);
+
+       litest_touch_move_extended(dev, 0, 5, 95, move_values);
+       litest_wait_for_event_of_type(li, LIBINPUT_EVENT_TOUCH_MOTION, -1);
+
+
+       expected_orientation = 90.0*128.0/orientation_info->maximum;
+       touch_major_scale = (x_res == y_res)
+               ? x_res
+               : (y_res*fabs(cos(M_PI * expected_orientation / 180.0)) +
+                  x_res*fabs(sin(M_PI * expected_orientation / 180.0)));
+       touch_minor_scale = (x_res == y_res)
+               ? x_res
+               : (y_res*fabs(sin(M_PI * expected_orientation / 180.0)) +
+                  x_res*fabs(cos(M_PI * expected_orientation / 180.0)));
+
+       ev = libinput_get_event(li);
+        tev = litest_is_touch_event(ev, 0);
+
+       ck_assert_double_eq(libinput_event_touch_get_pressure(tev), 128.0/
+                           (pressure_info->maximum - pressure_info->minimum));
+       ck_assert_double_eq(libinput_event_touch_get_orientation(tev),
+                           expected_orientation);
+       ck_assert_double_eq(libinput_event_touch_get_major(tev), 
30.0/touch_major_scale);
+       ck_assert_double_eq(libinput_event_touch_get_minor(tev), 
8.0/touch_minor_scale);
+
+       libinput_event_destroy(ev);
+}
+END_TEST
+
+START_TEST(touch_point_no_minor_or_orienation)
+{
+       struct litest_device *dev = litest_current_device();
+       struct libinput *li = dev->libinput;
+       struct libinput_event *ev;
+       struct libinput_event_touch *tev;
+       struct axis_replacement down_values[] = {
+               {ABS_MT_PRESSURE, 43},
+               {ABS_MT_TOUCH_MAJOR, 23}};
+       struct axis_replacement move_values[] = {
+               {ABS_MT_TOUCH_MAJOR, 30}};
+
+       struct input_absinfo const *orientation_info =
+               libevdev_get_abs_info(dev->evdev, ABS_MT_ORIENTATION);
+       struct input_absinfo const *pressure_info =
+               libevdev_get_abs_info(dev->evdev, ABS_MT_PRESSURE);
+       struct input_absinfo const *x_info =
+               libevdev_get_abs_info(dev->evdev, ABS_MT_POSITION_X);
+       double x_res;
+
+       if (orientation_info || !pressure_info || !x_info ||
+           !libevdev_has_event_code(dev->evdev, EV_ABS, ABS_MT_TOUCH_MAJOR)||
+           libevdev_has_event_code(dev->evdev, EV_ABS, ABS_MT_TOUCH_MINOR))
+               return;
+
+       fix_resolution(x_info);
+
+       x_res = x_info->resolution;
+
+       litest_drain_events(li);
+
+       litest_touch_down_extended(dev, 0, 5, 95, down_values);
+
+       litest_wait_for_event_of_type(li, LIBINPUT_EVENT_TOUCH_DOWN, -1);
+
+       ev = libinput_get_event(li);
+        tev = litest_is_touch_event(ev, 0);
+
+       ck_assert_double_eq(libinput_event_touch_get_orientation(tev), 0.0);
+       ck_assert_double_eq(libinput_event_touch_get_pressure(tev), 43.0 /
+                           (pressure_info->maximum - pressure_info->minimum));
+       ck_assert_double_eq(libinput_event_touch_get_major(tev), 23.0/x_res);
+       ck_assert_double_eq(libinput_event_touch_get_minor(tev), 0.0);
+
+       libinput_event_destroy(ev);
+
+       litest_touch_move_extended(dev, 0, 5, 95, move_values);
+       litest_wait_for_event_of_type(li, LIBINPUT_EVENT_TOUCH_MOTION, -1);
+
+       ev = libinput_get_event(li);
+        tev = litest_is_touch_event(ev, 0);
+
+       ck_assert_double_eq(libinput_event_touch_get_major(tev), 30.0/x_res);
+       ck_assert_double_eq(libinput_event_touch_get_minor(tev), 0.0);
+
+       libinput_event_destroy(ev);
+}
+END_TEST
+
 void
 litest_setup_tests(void)
 {
@@ -676,6 +834,10 @@ litest_setup_tests(void)
        litest_add("touch:protocol a", touch_protocol_a_init, 
LITEST_PROTOCOL_A, LITEST_ANY);
        litest_add("touch:protocol a", touch_protocol_a_touch, 
LITEST_PROTOCOL_A, LITEST_ANY);
        litest_add("touch:protocol a", touch_protocol_a_2fg_touch, 
LITEST_PROTOCOL_A, LITEST_ANY);
+       litest_add("touch:properties", touch_point_properties, 
LITEST_TOUCH|LITEST_ELLIPSE,
+                  LITEST_ANY);
+       litest_add("touch:propertiees", touch_point_no_minor_or_orienation,
+                  LITEST_TOUCH|LITEST_ELLIPSE, LITEST_ANY);
 
        litest_add_ranged("touch:state", touch_initial_state, LITEST_TOUCH, 
LITEST_PROTOCOL_A, &axes);
 }
-- 
2.1.4

_______________________________________________
wayland-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to