[PATCH libinput 1/2] libinput: add orientation and size of touch point and pressure to the API
Andreas Pokorny
andreas.pokorny at canonical.com
Mon Nov 2 07:56:13 PST 2015
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 [0, 360]
Those values are optionally supported by multi-touch drivers, so default values
are used if the information is missing. The existance of each of the properties
can be queried at the event using another set of libinput_event_touch_has_*
functions.
Explanation of those values was added to the touch screen page.
Signed-off-by: Andreas Pokorny <andreas.pokorny at canonical.com>
---
doc/Makefile.am | 2 +
doc/page-hierarchy.dox | 1 +
doc/svg/touchscreen-touch-event-properties.svg | 347 +++++++++++++++++++++++++
doc/touch-event-properties.dox | 42 +++
src/evdev.c | 224 ++++++++++++++--
src/evdev.h | 24 ++
src/libinput-private.h | 13 +-
src/libinput-util.h | 6 +
src/libinput.c | 212 ++++++++++++++-
src/libinput.h | 222 ++++++++++++++++
src/libinput.sym | 13 +
test/touch.c | 241 +++++++++++++++++
12 files changed, 1315 insertions(+), 32 deletions(-)
create mode 100644 doc/svg/touchscreen-touch-event-properties.svg
create mode 100644 doc/touch-event-properties.dox
diff --git a/doc/Makefile.am b/doc/Makefile.am
index fe70f6a..c0b4f73 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -26,6 +26,7 @@ header_files = \
$(srcdir)/tapping.dox \
$(srcdir)/test-suite.dox \
$(srcdir)/tools.dox \
+ $(srcdir)/touch-event-properties.dox \
$(srcdir)/touchpads.dox
diagram_files = \
@@ -50,6 +51,7 @@ diagram_files = \
$(srcdir)/svg/thumb-detection.svg \
$(srcdir)/svg/top-software-buttons.svg \
$(srcdir)/svg/touchscreen-gestures.svg \
+ $(srcdir)/svg/touchscreen-touch-event-properties.svg \
$(srcdir)/svg/twofinger-scrolling.svg
style_files = \
diff --git a/doc/page-hierarchy.dox b/doc/page-hierarchy.dox
index 3fdb1f7..aadc87f 100644
--- a/doc/page-hierarchy.dox
+++ b/doc/page-hierarchy.dox
@@ -11,6 +11,7 @@
@page touchscreens Touchscreens
- @subpage absolute_axes
+- @subpage touch_event_properties
@page pointers Mice, Trackballs, etc.
diff --git a/doc/svg/touchscreen-touch-event-properties.svg b/doc/svg/touchscreen-touch-event-properties.svg
new file mode 100644
index 0000000..b728f40
--- /dev/null
+++ b/doc/svg/touchscreen-touch-event-properties.svg
@@ -0,0 +1,347 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="81.778557mm"
+ height="107.62305mm"
+ viewBox="0 0 289.76655 381.34154"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="touchscreen-touch-event-properties.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="DotL"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="DotL"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4259"
+ d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="matrix(0.8,0,0,0.8,5.92,0.8)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="CurveOut"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="CurveOut"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4385"
+ d="m -5.4129913,-5.0456926 c 2.76,0 4.99999999,2.24 4.99999999,5.00000002 0,2.75999998 -2.23999999,4.99999998 -4.99999999,4.99999998"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="scale(0.6,0.6)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="StopL"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="StopL"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4367"
+ d="M 0,5.65 0,-5.65"
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="scale(0.8,0.8)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="DistanceStart"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="DistanceStart"
+ inkscape:isstock="true">
+ <g
+ id="g2300"
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-opacity:1">
+ <path
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.14999998;stroke-linecap:square;stroke-opacity:1"
+ d="M 0,0 2,0"
+ id="path2306"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-opacity:1"
+ d="M 0,0 13,4 9,0 13,-4 0,0 Z"
+ id="path2302"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-opacity:1"
+ d="M 0,-4 0,40"
+ id="path2304"
+ inkscape:connector-curvature="0" />
+ </g>
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4225"
+ style="fill:#cb004e;fill-opacity:1;fill-rule:evenodd;stroke:#cb004e;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4219"
+ style="fill:#cb004e;fill-opacity:1;fill-rule:evenodd;stroke:#cb004e;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleInL"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleInL"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4331"
+ d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#b3b3b3;stroke-width:1pt;stroke-opacity:1"
+ transform="scale(-0.8,-0.8)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4216"
+ style="fill:#cb004e;fill-opacity:1;fill-rule:evenodd;stroke:#cb004e;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <inkscape:path-effect
+ effect="powerstroke"
+ id="path-effect4140"
+ is_visible="true"
+ offset_points="0,0.5"
+ sort_points="true"
+ interpolator_type="Linear"
+ interpolator_beta="0.2"
+ start_linecap_type="zerowidth"
+ linejoin_type="round"
+ miter_limit="4"
+ end_linecap_type="zerowidth"
+ cusp_linecap_type="round" />
+ <marker
+ inkscape:stockid="TriangleInL"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleInL-1"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4331-8"
+ d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#b3b3b3;stroke-width:1pt;stroke-opacity:1"
+ transform="scale(-0.8,-0.8)" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleInL"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleInL-1-3"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4331-8-7"
+ d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#b3b3b3;stroke-width:1pt;stroke-opacity:1"
+ transform="scale(-0.8,-0.8)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.99999999"
+ inkscape:cx="123.83444"
+ inkscape:cy="279.21547"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="false"
+ inkscape:window-width="2560"
+ inkscape:window-height="1056"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-99.549825,-70.836892)">
+ <path
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#b3b3b3;stroke-width:1.79780054px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#TriangleInL-1-3)"
+ d="m 231.28087,80.931744 -0.008,371.246666"
+ id="path4144-1-8-1"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ id="g6309"
+ inkscape:transform-center-x="-9.527809"
+ inkscape:transform-center-y="-8.1612127"
+ transform="matrix(1.171972,1.3632932,-1.3632932,1.171972,275.33248,-179.00364)">
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path4144-1-8"
+ d="m 172.88767,70.631028 -0.004,206.500482"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#b3b3b3;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#TriangleInL-1)" />
+ <ellipse
+ ry="77.321434"
+ rx="45.89286"
+ cy="180.93364"
+ cx="172.85715"
+ id="path4136"
+ style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path4142"
+ d="m 126.9449,180.93396 91.84596,0.007"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path4144"
+ d="m 172.84766,103.6564 -0.004,154.59727"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4146"
+ y="188.01213"
+ x="128.08986"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.5px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="188.01213"
+ x="128.08986"
+ id="tspan4148"
+ sodipodi:role="line">minor axis</tspan></text>
+ <text
+ transform="matrix(0,-1,1,0,0,0)"
+ sodipodi:linespacing="125%"
+ id="text4150"
+ y="169.33234"
+ x="-256.35562"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.5px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="169.33234"
+ x="-256.35562"
+ id="tspan4152"
+ sodipodi:role="line">major axis</tspan></text>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:71.91202545px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="44.285629"
+ y="369.12045"
+ id="text6877"
+ sodipodi:linespacing="125%"
+ transform="matrix(0.76306478,-0.64632201,0.64632201,0.76306478,0,0)"><tspan
+ sodipodi:role="line"
+ id="tspan6879"
+ x="44.285629"
+ y="369.12045"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.4835043px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#b3b3b3;fill-opacity:1">pointing direction</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.4835043px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="237.00804"
+ y="99.788658"
+ id="text6887"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan6889"
+ x="237.00804"
+ y="99.788658">y axis</tspan></text>
+ <path
+ style="fill:none;fill-opacity:1;stroke:#cb004e;stroke-width:1.79780054;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
+ id="path7011"
+ sodipodi:type="arc"
+ sodipodi:cx="231.14914"
+ sodipodi:cy="268.54077"
+ sodipodi:rx="79.092262"
+ sodipodi:ry="79.092262"
+ sodipodi:start="4.7157629"
+ sodipodi:end="5.5461565"
+ d="m 231.41599,189.44896 a 79.092262,79.092262 0 0 1 58.2985,25.93463"
+ sodipodi:open="true" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8.98900318px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#cb004e;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="232.66777"
+ y="184.40468"
+ id="text7119"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan7121"
+ x="232.66777"
+ y="184.40468"
+ style="font-size:13.4835043px;fill:#cb004e;fill-opacity:1">orientation</tspan></text>
+ </g>
+</svg>
diff --git a/doc/touch-event-properties.dox b/doc/touch-event-properties.dox
new file mode 100644
index 0000000..bf326f7
--- /dev/null
+++ b/doc/touch-event-properties.dox
@@ -0,0 +1,42 @@
+/**
+ at page touch_event_properties Properties of a touch event
+
+This page gives some overview on touchscreen events. With libinput touchscreens
+provide the event types @ref LIBINPUT_EVENT_TOUCH_DOWN, @ref LIBINPUT_EVENT_TOUCH_UP,
+ at ref LIBINPUT_EVENT_TOUCH_MOTION and @ref LIBINPUT_EVENT_TOUCH_CANCEL. The
+touchscreen events @ref LIBINPUT_EVENT_TOUCH_DOWN and
+ at ref LIBINPUT_EVENT_TOUCH_MOTION provide alongside the actual state change and
+the absolute coordinates (@ref absolute_axes_handling) additional information
+about the touch contact.
+
+ at image html touchscreen-touch-event-properties.svg "Properties of a touch screen contact"
+
+Assuming the interaction of fingers with a touch screen, touch contacts are
+approximated with an ellipse. The major axis of the ellipse describes the
+pointing direction of the finger. The minor axis is the perpendicular
+extent of the touching shape. The orientation angle is the clockwise rotation of
+the pointing direction against the y-axis of the touchscreen.
+
+Additionally to the values shown in the drawing above, most touchscreens also
+provide a pressure value to indicate the force applied with the contact point.
+
+The support for those contact properties varies between the different
+available touchscreens. In the case of pressure libinput will return the
+maximum pressure value, which is 1.0. If only the major axis but no minor axis
+is present, libinput will assume a circular shape and return the major axis
+value for convenience. If also no major axis value is known 0.0 is returned.
+If the orientation is not available libinput will return 0.0 degrees.
+
+For querying the touch properties see:
+- libinput_event_touch_get_major_transformed()
+- libinput_event_touch_get_minor_transformed()
+- libinput_event_touch_get_orientation()
+- libinput_event_touch_get_pressure()
+
+For testing which of the touch properties are available see:
+- libinput_event_touch_has_major()
+- libinput_event_touch_has_minor()
+- libinput_event_touch_has_orientation()
+- libinput_event_touch_has_pressure()
+
+*/
diff --git a/src/evdev.c b/src/evdev.c
index ec3abc6..ea6be05 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 ms2us(200)
+#define DEFAULT_TOUCH_PRESSURE 1.0
+#define DEFAULT_TOUCH_ORIENTATION 0.0
+#define DEFAULT_TOUCH_MAJOR 0.0
+#define DEFAULT_TOUCH_MINOR 0.0
enum evdev_key_type {
EVDEV_KEY_TYPE_NONE,
@@ -245,6 +249,100 @@ evdev_device_transform_y(struct evdev_device *device,
return scale_axis(device->abs.absinfo_y, y, height);
}
+double
+evdev_device_transform_ellipse_diameter_to_mm(struct evdev_device *device,
+ int diameter,
+ 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 diameter / x_res;
+
+ /* resolution differs but no orientation available
+ * -> estimate resolution using the average */
+ if (device->abs.absinfo_orientation == NULL) {
+ return diameter * 2.0 / (x_res + y_res);
+ } else {
+ /* Why scale x using sine of angle?
+ * axis_angle = 0 indicates that the given diameter
+ * is aligned with the y-axis. */
+ double x_scaling_ratio = fabs(sin(deg2rad(axis_angle)));
+ double y_scaling_ratio = fabs(cos(deg2rad(axis_angle)));
+
+ return diameter / hypotf(y_res * y_scaling_ratio,
+ x_res * x_scaling_ratio);
+ }
+}
+
+double
+evdev_device_transform_ellipse_diameter(struct evdev_device *device,
+ int diameter,
+ 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 diameter * x_scale;
+
+ /* no orientation available -> estimate resolution using the
+ * average */
+ if (device->abs.absinfo_orientation == NULL) {
+ return diameter * (x_scale + y_scale) / 2.0;
+ } else {
+ /* Why scale x using sine of angle?
+ * axis_angle = 0 indicates that the given diameter
+ * is aligned with the y-axis. */
+ double x_scaling_ratio = fabs(sin(deg2rad(axis_angle)));
+ double y_scaling_ratio = fabs(cos(deg2rad(axis_angle)));
+
+ return diameter * (y_scale * y_scaling_ratio +
+ x_scale * x_scaling_ratio);
+ }
+}
+
+double
+evdev_device_transform_orientation(struct evdev_device *device,
+ int32_t orientation)
+{
+ const struct input_absinfo *orientation_info =
+ device->abs.absinfo_orientation;
+
+ double angle = DEFAULT_TOUCH_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)
+ angle = (90.0 * orientation) / orientation_info->maximum;
+
+ return fmod(360.0 + angle, 360.0);
+}
+
+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 (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 +380,15 @@ 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 ellipse default_touch = {
+ .major = DEFAULT_TOUCH_MAJOR,
+ .minor = DEFAULT_TOUCH_MINOR,
+ .orientation = DEFAULT_TOUCH_ORIENTATION
+ };
slot = device->mt.slot;
+ slot_data = &device->mt.slots[slot];
switch (device->pending_event) {
case EVDEV_NONE:
@@ -324,7 +429,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",
@@ -333,38 +438,50 @@ 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->area,
+ 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->area,
+ 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;
@@ -396,7 +513,13 @@ 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_PRESSURE);
break;
case EVDEV_ABSOLUTE_MOTION:
point = device->abs.point;
@@ -408,8 +531,13 @@ 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_PRESSURE);
} else if (device->seat_caps & EVDEV_DEVICE_POINTER) {
pointer_notify_motion_absolute(base, time, &point);
}
@@ -569,8 +697,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 %zd)\n",
@@ -581,8 +710,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);
@@ -590,17 +718,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->area.major = e->value;
+ break;
+ case ABS_MT_TOUCH_MINOR:
+ current_slot->area.minor = e->value;
+ break;
+ case ABS_MT_ORIENTATION:
+ current_slot->area.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;
}
}
@@ -1954,6 +2099,20 @@ evdev_configure_mt_device(struct evdev_device *device)
slots[slot].point.y = libevdev_get_slot_value(evdev,
slot,
ABS_MT_POSITION_Y);
+ slots[slot].area.major =
+ libevdev_get_slot_value(evdev,
+ slot,
+ ABS_MT_TOUCH_MAJOR);
+ slots[slot].area.minor =
+ libevdev_get_slot_value(evdev,
+ slot,
+ ABS_MT_TOUCH_MINOR);
+ slots[slot].area.orientation =
+ libevdev_get_slot_value(evdev,
+ slot,
+ ABS_MT_ORIENTATION);
+ slots[slot].pressure =
+ libevdev_get_slot_value(evdev, slot, ABS_MT_PRESSURE);
}
device->mt.slots = slots;
device->mt.slots_len = num_slots;
@@ -2024,6 +2183,15 @@ 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);
+ device->abs.absinfo_major =
+ libevdev_get_abs_info(evdev, ABS_MT_TOUCH_MAJOR);
+ device->abs.absinfo_minor =
+ libevdev_get_abs_info(evdev, ABS_MT_TOUCH_MINOR);
+
if (!evdev_is_fake_mt_device(device))
evdev_fix_android_mt(device);
diff --git a/src/evdev.h b/src/evdev.h
index e44a65d..7b8d0e6 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -112,6 +112,8 @@ enum evdev_device_model {
struct mt_slot {
int32_t seat_slot;
struct device_coords point;
+ struct ellipse area;
+ int32_t pressure;
};
struct evdev_device {
@@ -128,6 +130,8 @@ struct evdev_device {
int fd;
struct {
const struct input_absinfo *absinfo_x, *absinfo_y;
+ const struct input_absinfo *absinfo_major, *absinfo_minor,
+ *absinfo_pressure, *absinfo_orientation;
int fake_resolution;
struct device_coords point;
@@ -349,6 +353,26 @@ double
evdev_device_transform_y(struct evdev_device *device,
double y,
uint32_t height);
+double
+evdev_device_transform_ellipse_diameter_to_mm(struct evdev_device *device,
+ int32_t diameter,
+ double axis_angle);
+
+double
+evdev_device_transform_ellipse_diameter(struct evdev_device *device,
+ int32_t diameter,
+ 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 e146c26..b90c21d 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -40,6 +40,11 @@ struct device_coords {
int x, y;
};
+/* Ellipse parameters in device coordinates */
+struct ellipse {
+ int major, minor, orientation;
+};
+
/*
* A coordinate pair in device coordinates, capable of holding non discrete
* values, this is necessary e.g. when device coordinates get averaged.
@@ -396,14 +401,18 @@ 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 ellipse *area,
+ 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 ellipse *area,
+ int32_t pressure);
void
touch_notify_touch_up(struct libinput_device *device,
diff --git a/src/libinput-util.h b/src/libinput-util.h
index ba253b5..9495fa6 100644
--- a/src/libinput-util.h
+++ b/src/libinput-util.h
@@ -118,6 +118,12 @@ msleep(unsigned int ms)
usleep(ms * 1000);
}
+static inline double
+deg2rad(double angle)
+{
+ return angle * M_PI/180.0;
+}
+
static inline int
long_bit_is_set(const unsigned long *array, int bit)
{
diff --git a/src/libinput.c b/src/libinput.c
index 24f2b69..bdd3d41 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -112,6 +112,8 @@ struct libinput_event_touch {
int32_t slot;
int32_t seat_slot;
struct device_coords point;
+ struct ellipse area;
+ int32_t pressure;
};
struct libinput_event_gesture {
@@ -732,6 +734,204 @@ 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;
+
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ 0,
+ LIBINPUT_EVENT_TOUCH_DOWN,
+ LIBINPUT_EVENT_TOUCH_MOTION);
+
+ angle = evdev_device_transform_orientation(device,
+ event->area.orientation);
+
+ return evdev_device_transform_ellipse_diameter_to_mm(device,
+ event->area.major,
+ 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;
+
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ 0,
+ LIBINPUT_EVENT_TOUCH_DOWN,
+ LIBINPUT_EVENT_TOUCH_MOTION);
+
+ angle = evdev_device_transform_orientation(device,
+ event->area.orientation);
+
+ return evdev_device_transform_ellipse_diameter(device,
+ event->area.major,
+ angle,
+ width,
+ height);
+}
+
+LIBINPUT_EXPORT int
+libinput_event_touch_has_major(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 device->abs.absinfo_major != 0;
+}
+
+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;
+
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ 0,
+ LIBINPUT_EVENT_TOUCH_DOWN,
+ LIBINPUT_EVENT_TOUCH_MOTION);
+
+ angle = evdev_device_transform_orientation(device,
+ event->area.orientation);
+
+ /* angle + 90 since the minor diameter is perpendicular to the
+ * major axis */
+ return evdev_device_transform_ellipse_diameter_to_mm(device,
+ event->area.minor,
+ 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;
+ int diameter;
+
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ 0,
+ LIBINPUT_EVENT_TOUCH_DOWN,
+ LIBINPUT_EVENT_TOUCH_MOTION);
+
+ angle = evdev_device_transform_orientation(device,
+ event->area.orientation);
+
+ /* use major diameter if minor is not available, but if it is
+ * add 90 since the minor diameter is perpendicular to the
+ * major axis */
+ if (device->abs.absinfo_minor) {
+ diameter = event->area.minor,
+ angle += 90.0;
+ } else {
+ diameter = event->area.major;
+ }
+
+ return evdev_device_transform_ellipse_diameter(device,
+ diameter,
+ angle,
+ width,
+ height);
+}
+
+LIBINPUT_EXPORT int
+libinput_event_touch_has_minor(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 device->abs.absinfo_minor != 0;
+}
+
+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->area.orientation);
+}
+
+LIBINPUT_EXPORT int
+libinput_event_touch_has_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 device->abs.absinfo_orientation != 0;
+}
+
+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);
+}
+
+LIBINPUT_EXPORT int
+libinput_event_touch_has_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 device->abs.absinfo_pressure != 0;
+}
+
LIBINPUT_EXPORT uint32_t
libinput_event_gesture_get_time(struct libinput_event_gesture *event)
{
@@ -1512,7 +1712,9 @@ 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 ellipse *area,
+ int32_t pressure)
{
struct libinput_event_touch *touch_event;
@@ -1528,6 +1730,8 @@ touch_notify_touch_down(struct libinput_device *device,
.slot = slot,
.seat_slot = seat_slot,
.point = *point,
+ .area = *area,
+ .pressure = pressure,
};
post_device_event(device, time,
@@ -1540,7 +1744,9 @@ 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 ellipse *area,
+ int32_t pressure)
{
struct libinput_event_touch *touch_event;
@@ -1556,6 +1762,8 @@ touch_notify_touch_motion(struct libinput_device *device,
.slot = slot,
.seat_slot = seat_slot,
.point = *point,
+ .area = *area,
+ .pressure = pressure,
};
post_device_event(device, time,
diff --git a/src/libinput.h b/src/libinput.h
index 9057446..1c50c58 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -968,6 +968,228 @@ libinput_event_touch_get_y_transformed(struct libinput_event_touch *event,
/**
* @ingroup event_touch
*
+ * 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.
+ *
+ * A more detailed explanation can be found in @ref touch_event_properties.
+ *
+ * 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
+ * other than @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION.
+ *
+ * @param event The libinput touch event
+ * @return The current major axis diameter
+ */
+double
+libinput_event_touch_get_major(struct libinput_event_touch *event);
+
+/**
+ * @ingroup event_touch
+ *
+ * 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.
+ *
+ * A more detailed explanation can be found in @ref touch_event_properties.
+ *
+ * For events not of type @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION this function returns 0.
+ *
+ * @note It is an application bug to call this function for events of type
+ * other than @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 major axis diameter
+ */
+double
+libinput_event_touch_get_major_transformed(struct libinput_event_touch *event,
+ uint32_t width,
+ uint32_t height);
+
+/**
+ * @ingroup event_touch
+ *
+ * Return if the event contains a major axis value of the touch ellipse.
+ *
+ * A more detailed explanation can be found in @ref touch_event_properties.
+ *
+ * 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
+ * other than @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION.
+ *
+ * @param event The libinput touch event
+ * @return Non-zero when a major diameter is available
+ */
+int
+libinput_event_touch_has_major(struct libinput_event_touch *event);
+
+/**
+ * @ingroup event_touch
+ *
+ * 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.
+ *
+ * A more detailed explanation can be found in @ref touch_event_properties.
+ *
+ * For events not of type @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION this function returns 0.
+ *
+ * @note It is an application bug to call this function for events of type
+ * other than @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION.
+ *
+ * @param event The libinput touch event
+ * @return The current minor diameter
+ */
+double
+libinput_event_touch_get_minor(struct libinput_event_touch *event);
+
+/**
+ * @ingroup event_touch
+ *
+ * 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.
+ *
+ * A more detailed explanation can be found in @ref touch_event_properties.
+ *
+ * For events not of type @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION this function returns 0.
+ *
+ * @note It is an application bug to call this function for events of type
+ * other than @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 minor diameter
+ */
+double
+libinput_event_touch_get_minor_transformed(struct libinput_event_touch *event,
+ uint32_t width,
+ uint32_t height);
+/**
+ * @ingroup event_touch
+ *
+ * Return if the event contains a minor axis value of the touch ellipse.
+ *
+ * A more detailed explanation can be found in @ref touch_event_properties.
+ *
+ * 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
+ * other than @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION.
+ *
+ * @param event The libinput touch event
+ * @return Non-zero when a minor diameter is available
+ */
+int
+libinput_event_touch_has_minor(struct libinput_event_touch *event);
+
+/**
+ * @ingroup event_touch
+ *
+ * Return the pressure value applied to the touch contact normalized to the
+ * range [0, 1]. If this value is not available the function returns the maximum
+ * value 1.0.
+ *
+ * A more detailed explanation can be found in @ref touch_event_properties.
+ *
+ * For events not of type @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION this function returns 0.
+ *
+ * @note It is an application bug to call this function for events of type
+ * other than @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
+ *
+ * Return if the event contains a pressure value for the touch contact.
+ *
+ * A more detailed explanation can be found in @ref touch_event_properties.
+ *
+ * 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
+ * other than @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION.
+ *
+ * @param event The libinput touch event
+ * @return Non-zero when a pressure value is available
+ */
+int
+libinput_event_touch_has_pressure(struct libinput_event_touch *event);
+
+/**
+ * @ingroup event_touch
+ *
+ * Return the major axis rotation in degrees, clockwise from the logical north
+ * of the touch screen.
+ *
+ * @note Even when the orientation is measured by the device, it might be only
+ * available in coarse steps (e.g only indicating alignment with either of the
+ * axes).
+ *
+ * A more detailed explanation can be found in @ref touch_event_properties.
+ *
+ * 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
+ * other than @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 if the event contains a orientation value for the touch contact.
+ *
+ * A more detailed explanation can be found in @ref touch_event_properties.
+ *
+ * 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
+ * other than @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref
+ * LIBINPUT_EVENT_TOUCH_MOTION.
+ *
+ * @param event The libinput touch event
+ * @return Non-zero when an orientation value is available
+ */
+int
+libinput_event_touch_has_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 15203c8..23d7975 100644
--- a/src/libinput.sym
+++ b/src/libinput.sym
@@ -179,3 +179,16 @@ LIBINPUT_1.1 {
libinput_device_config_accel_get_default_profile;
libinput_device_config_accel_set_profile;
} LIBINPUT_0.21.0;
+
+LIBINPUT_1.1_unreleased {
+ libinput_event_touch_get_major;
+ libinput_event_touch_get_major_transformed;
+ libinput_event_touch_get_minor;
+ libinput_event_touch_get_minor_transformed;
+ libinput_event_touch_get_orientation;
+ libinput_event_touch_get_pressure;
+ libinput_event_touch_has_major;
+ libinput_event_touch_has_minor;
+ libinput_event_touch_has_orientation;
+ libinput_event_touch_has_pressure;
+} LIBINPUT_1.1;
diff --git a/test/touch.c b/test/touch.c
index eae8007..ab30914 100644
--- a/test/touch.c
+++ b/test/touch.c
@@ -673,6 +673,245 @@ START_TEST(touch_time_usec)
}
END_TEST
+static inline double calc_diameter_scale(double x_res,
+ double y_res,
+ double orientation)
+{
+ double orientation_rad = deg2rad(orientation);
+ if (x_res == y_res)
+ return x_res;
+
+ return hypotf(y_res * fabs(cos(orientation_rad)),
+ x_res * fabs(sin(orientation_rad)));
+}
+
+static inline bool exceeds_range(const struct input_absinfo *info,
+ int value)
+{
+ return !info || (info->minimum > value) || (info->maximum < value);
+}
+
+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;
+
+ const int touch_x = 50;
+ const int touch_y = 90;
+ const int input_orientation = 64;
+ const int input_pressure = 128;
+ const int input_major = 14;
+ const int input_minor= 8;
+
+ const int input_orientation_2 = 128;
+ const int input_major_2 = 30;
+
+ struct axis_replacement down_values[] = {
+ {ABS_MT_PRESSURE, input_pressure},
+ {ABS_MT_ORIENTATION, input_orientation},
+ {ABS_MT_TOUCH_MAJOR, input_major},
+ {ABS_MT_TOUCH_MINOR, input_minor},
+ {-1, -1}};
+ struct axis_replacement move_values[] = {
+ {ABS_MT_ORIENTATION, input_orientation_2},
+ {ABS_MT_TOUCH_MAJOR, input_major_2},
+ {-1, -1}};
+ const struct input_absinfo *orientation_info;
+ const struct input_absinfo *pressure_info;
+ const struct input_absinfo *major_info;
+ const struct input_absinfo *minor_info;
+ const struct input_absinfo *x_info, *y_info;
+
+ double x_res, y_res, touch_minor_scale, touch_major_scale;
+
+ double expected_major;
+ double expected_minor;
+ double expected_orientation;
+ double expected_pressure;
+
+ pressure_info = libevdev_get_abs_info(dev->evdev, ABS_MT_PRESSURE);
+ orientation_info = libevdev_get_abs_info(dev->evdev, ABS_MT_ORIENTATION);
+ major_info = libevdev_get_abs_info(dev->evdev, ABS_MT_TOUCH_MAJOR);
+ minor_info = libevdev_get_abs_info(dev->evdev, ABS_MT_TOUCH_MINOR);
+ x_info = libevdev_get_abs_info(dev->evdev, ABS_MT_POSITION_X);
+ y_info = libevdev_get_abs_info(dev->evdev, ABS_MT_POSITION_Y);
+
+ if (!orientation_info || !pressure_info || !major_info || !minor_info)
+ return;
+
+ if (exceeds_range(pressure_info, input_pressure) ||
+ exceeds_range(major_info, input_major) ||
+ exceeds_range(major_info, input_major_2) ||
+ exceeds_range(minor_info, input_minor) ||
+ exceeds_range(orientation_info, input_orientation) ||
+ exceeds_range(orientation_info, input_orientation_2)) {
+ fprintf(stderr,
+ "%s does not support the required value ranges\n",
+ libinput_device_get_name(dev->libinput_device));
+ return;
+ }
+
+ x_res = x_info->resolution ? x_info->resolution : 1;
+ y_res = y_info->resolution ? y_info->resolution : 1;
+
+ expected_orientation = 90.0 * input_orientation /
+ orientation_info->maximum;
+ touch_major_scale = calc_diameter_scale(x_res,
+ y_res,
+ expected_orientation);
+ touch_minor_scale = calc_diameter_scale(x_res,
+ y_res,
+ expected_orientation + 90.0);
+ expected_major = input_major / touch_major_scale;
+ expected_minor = input_minor / touch_minor_scale;
+ expected_pressure = (double)input_pressure / (pressure_info->maximum -
+ pressure_info->minimum);
+
+ litest_drain_events(li);
+
+ litest_touch_down_extended(dev, 0, touch_x, touch_y, down_values);
+
+ litest_wait_for_event(li);
+ ev = libinput_get_event(li);
+ tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_DOWN);
+
+ ck_assert_double_eq(libinput_event_touch_get_pressure(tev),
+ expected_pressure);
+ ck_assert_double_eq(libinput_event_touch_get_orientation(tev),
+ expected_orientation);
+ ck_assert_double_eq(libinput_event_touch_get_major(tev),
+ expected_major);
+ ck_assert_double_eq(libinput_event_touch_get_minor(tev),
+ expected_minor);
+
+ litest_touch_move_extended(dev, 0, touch_x, touch_y, move_values);
+
+ do {
+ libinput_event_destroy(ev);
+ litest_wait_for_event(li);
+ ev = libinput_get_event(li);
+ } while (libinput_event_get_type(ev) == LIBINPUT_EVENT_TOUCH_FRAME);
+
+ tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_MOTION);
+
+ expected_orientation = 90.0 * input_orientation_2 /
+ orientation_info->maximum;
+ touch_major_scale = calc_diameter_scale(x_res,
+ y_res,
+ expected_orientation);
+ expected_major = input_major_2 / touch_major_scale;
+
+ ck_assert_double_eq(libinput_event_touch_get_pressure(tev),
+ expected_pressure);
+ ck_assert_double_eq(libinput_event_touch_get_orientation(tev),
+ expected_orientation);
+ ck_assert_double_eq(libinput_event_touch_get_major(tev),
+ expected_major);
+ ck_assert_double_eq(libinput_event_touch_get_minor(tev),
+ expected_minor);
+
+ libinput_event_destroy(ev);
+}
+END_TEST
+
+START_TEST(touch_point_no_minor_or_orientation)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *ev;
+ struct libinput_event_touch *tev;
+ const int touch_x = 50;
+ const int touch_y = 90;
+ const int input_pressure = 43;
+ const int input_major = 23;
+ const int input_major_2 = 30;
+
+ struct axis_replacement down_values[] = {
+ {ABS_MT_PRESSURE, input_pressure},
+ {ABS_MT_TOUCH_MAJOR, input_major},
+ {-1, -1}};
+ struct axis_replacement move_values[] = {
+ {ABS_MT_TOUCH_MAJOR, input_major_2},
+ {-1, -1}};
+
+ const struct input_absinfo *orientation_info;
+ const struct input_absinfo *pressure_info;
+ const struct input_absinfo *x_info;
+ const struct input_absinfo *major_info;
+ double touch_major_scale;
+
+ double expected_major;
+ double expected_minor;
+ double expected_orientation = 0.0;
+ double expected_pressure;
+
+ x_info = libevdev_get_abs_info(dev->evdev, ABS_MT_POSITION_X);
+ pressure_info = libevdev_get_abs_info(dev->evdev, ABS_MT_PRESSURE);
+ major_info = libevdev_get_abs_info(dev->evdev, ABS_MT_TOUCH_MAJOR);
+ orientation_info = libevdev_get_abs_info(dev->evdev,
+ ABS_MT_ORIENTATION);
+
+ if (orientation_info || !pressure_info || !x_info || !major_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;
+
+ if (exceeds_range(pressure_info, input_pressure) ||
+ exceeds_range(major_info, input_major) ||
+ exceeds_range(major_info, input_major_2)) {
+ fprintf(stderr,
+ "%s does not support the required value ranges\n",
+ libinput_device_get_name(dev->libinput_device));
+ return;
+ }
+
+ expected_pressure = (double) input_pressure /
+ (pressure_info->maximum - pressure_info->minimum);
+ touch_major_scale = x_info->resolution ? x_info->resolution : 1;
+ expected_major = input_major / touch_major_scale;
+ expected_minor = expected_major;
+
+ litest_drain_events(li);
+
+ litest_touch_down_extended(dev, 0, touch_x, touch_y, down_values);
+
+ litest_wait_for_event(li);
+ ev = libinput_get_event(li);
+ tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_DOWN);
+
+ ck_assert_double_eq(libinput_event_touch_get_orientation(tev),
+ expected_orientation);
+ ck_assert_double_eq(libinput_event_touch_get_pressure(tev),
+ expected_pressure);
+ ck_assert_double_eq(libinput_event_touch_get_major(tev),
+ expected_major);
+ ck_assert_double_eq(libinput_event_touch_get_minor(tev),
+ expected_minor);
+
+ expected_major = input_major_2 / touch_major_scale;
+ expected_minor = expected_major;
+
+ litest_touch_move_extended(dev, 0, touch_x, touch_y, move_values);
+
+ do {
+ libinput_event_destroy(ev);
+ litest_wait_for_event(li);
+ ev = libinput_get_event(li);
+ } while (libinput_event_get_type(ev) == LIBINPUT_EVENT_TOUCH_FRAME);
+
+ tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_MOTION);
+
+ ck_assert_double_eq(libinput_event_touch_get_major(tev),
+ expected_major);
+ ck_assert_double_eq(libinput_event_touch_get_minor(tev),
+ expected_minor);
+
+ libinput_event_destroy(ev);
+}
+END_TEST
+
void
litest_setup_tests(void)
{
@@ -697,6 +936,8 @@ 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:properties", touch_point_no_minor_or_orientation, LITEST_TOUCH|LITEST_ELLIPSE, LITEST_ANY);
litest_add_ranged("touch:state", touch_initial_state, LITEST_TOUCH, LITEST_PROTOCOL_A, &axes);
--
2.5.0
More information about the wayland-devel
mailing list