On Mon, Nov 02, 2015 at 04:56:13PM +0100, Andreas Pokorny wrote: > 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>
did you get my patch to squash in with the documentation updates? none of those appear to be in here. Cheers, Peter > --- > 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 @@ > +/** > +@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, > +@ref LIBINPUT_EVENT_TOUCH_MOTION and @ref LIBINPUT_EVENT_TOUCH_CANCEL. The > +touchscreen events @ref LIBINPUT_EVENT_TOUCH_DOWN and > +@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. > + > +@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 > _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel