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.poko...@canonical.com> --- doc/Makefile.am | 2 + doc/page-hierarchy.dox | 1 + doc/svg/touchscreen-touch-event-properties.svg | 347 +++++++++++++++++++++++++ doc/touch-event-properties.dox | 66 +++++ 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, 1339 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..c3fe03a --- /dev/null +++ b/doc/touch-event-properties.dox @@ -0,0 +1,66 @@ +/** +@page touch_event_properties Properties of a touch event + +This page gives some overview on touchscreen events. libinput requires all +supported touchscreen to provide positional information and per-finger +tracking. Positional information is provided for the events +@ref LIBINPUT_EVENT_TOUCH_DOWN and @ref LIBINPUT_EVENT_TOUCH_MOTION with the +functions libinput_event_touch_get_x() and libinput_event_touch_get_y(), as +well as libinput_event_touch_get_x_transformed() and +libinput_event_touch_get_y_transformed(). See also @ref absolute_axes. + +Some touchscreen provide additional information, specifically @ref +touch_major_minor, @ref touch_orientation, and @ref touch_pressure. + +@image html touchscreen-touch-event-properties.svg "Properties of a touch screen contact" + +Note that the events @ref LIBINPUT_EVENT_TOUCH_UP and @ref +LIBINPUT_EVENT_TOUCH_CANCEL do not provide any positional information or +extended properties. + +@section touch_major_minor Major and minor axes + +A finger contact on a touch screen provides an approximately elliptical +contact area. The major axis of this ellipse is along the pointing +direction of the finger, the minor axis is perpendicular to the major axis. + +libinput provides both major and minor axes as a physical dimension. See +- libinput_event_touch_get_major() +- libinput_event_touch_get_minor() +- libinput_event_touch_get_major_transformed() +- libinput_event_touch_get_minor_transformed() + +Note that the granularity of the ellipse is device-specific and usually +less accurate than the positional information. + +Not all touch screens support touch ellipse detection. Where unavailable, +libinput returns zero as value for the major and minor axes. Where only +a major axis is available, libinput assumes a circular contact, the minor +axis is then always equivalent to the major axis. +Use libinput_event_touch_has_major() and libinput_event_touch_has_minor() +to determine if the values are available. + +@section touch_orientation Touch point orientation + +The orientation of a touch is the angle (clockwise) between the major +axis and the y-axis of the touchscreen. The granularity of the orientation +value is device-specific, some devices may only detect alignment of the +ellipse with the y or x axis (i.e. 0 or 90 degrees rotation). + +Use libinput_event_touch_has_orientation() to determine if the value is +available and libinput_event_touch_get_orientation() to get the value. If +the device does not support orientation, the orientation is always 0. + +@section touch_pressure Touch point pressure + +The pressure of a touch is the force applied to each contact point. Note +that as touch pressure increases, the finger flattens and the contact +surface area usually increases as well. + +Use libinput_event_touch_get_pressure() to get the pressure value. This +value is normalized to the range [0.0, 1.0]. Use +libinput_event_touch_has_pressure() to determine if the value is available. +A device that does not support pressure always returns a pressure value of +1.0. + +*/ 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..d349c1d 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 whether 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 whether 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 whether 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 whether 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 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel