Merge branch 'wip/tablet-pad-support'

This commit is contained in:
Peter Hutterer 2016-04-18 13:31:46 +10:00
commit 064c72a52a
22 changed files with 2843 additions and 57 deletions

View file

@ -0,0 +1,325 @@
<?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:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="229.5488mm"
height="86.66362mm"
viewBox="0 0 813.36188 307.07582"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="tablet-interfaces.svg">
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient4294">
<stop
style="stop-color:#1a1a1a;stop-opacity:1;"
offset="0"
id="stop4296" />
<stop
style="stop-color:#808080;stop-opacity:1"
offset="1"
id="stop4298" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4294"
id="linearGradient4300"
x1="465.81339"
y1="666.13727"
x2="454.82117"
y2="658.65521"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="587.87559"
inkscape:cy="104.80851"
inkscape:document-units="px"
inkscape:current-layer="g3663-9-5"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1016"
inkscape:window-x="0"
inkscape:window-y="27"
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="tablet"
inkscape:groupmode="layer"
id="layer1"
style="display:inline"
transform="translate(67.109152,-133.63374)">
<g
id="g4309"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<rect
y="134.15933"
x="75.787216"
height="306.02466"
width="522.19733"
id="rect4136"
style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:1.05118144;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:4.20472551, 1.05118138;stroke-dashoffset:0;stroke-opacity:1" />
<rect
style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.74813837;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:2.99255325, 0.74813831;stroke-dashoffset:0;stroke-opacity:1"
id="rect4140"
width="357.34042"
height="226.52563"
x="199.33878"
y="175.42407" />
<rect
y="175.72914"
x="103.10225"
height="22.142857"
width="65"
id="rect4142"
style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.98900002;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:3.956, 0.989;stroke-dashoffset:0;stroke-opacity:1" />
<rect
style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.98900002;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:3.956, 0.989;stroke-dashoffset:0;stroke-opacity:1"
id="rect4148"
width="65"
height="22.142857"
x="103.10225"
y="203.72914" />
<rect
y="231.72913"
x="103.10225"
height="22.142857"
width="65"
id="rect4150"
style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.98900002;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:3.956, 0.989;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="323.72913"
x="103.10225"
height="22.142857"
width="65"
id="rect4154"
style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.98900002;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:3.956, 0.989;stroke-dashoffset:0;stroke-opacity:1" />
<rect
style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.98900002;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:3.956, 0.989;stroke-dashoffset:0;stroke-opacity:1"
id="rect4156"
width="65"
height="22.142857"
x="103.10225"
y="351.72913" />
<circle
r="22.98097"
cy="287.06125"
cx="135.61298"
id="path4158"
style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.98900002;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:3.956, 0.989;stroke-dashoffset:0;stroke-opacity:1" />
<ellipse
ry="12.608653"
rx="11.5985"
style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.52043104;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:2.08172421, 0.52043105;stroke-dashoffset:0;stroke-opacity:1"
id="circle4160"
cx="135.61298"
cy="287.06125" />
<rect
y="379.72913"
x="103.10225"
height="22.142857"
width="65"
id="rect4162"
style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.98900002;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:3.956, 0.989;stroke-dashoffset:0;stroke-opacity:1" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="stylus"
style="display:inline"
transform="translate(67.109152,-133.63374)">
<g
id="g4304"
transform="matrix(0.37129971,0.09948946,-0.09618892,0.35898192,295.60339,7.6883643)">
<path
sodipodi:nodetypes="czcc"
inkscape:connector-curvature="0"
id="path4286"
d="m 387.83544,799.76093 c -1.1128,3.61694 -3.2211,13.05163 -1.08543,14.07769 2.13567,1.02606 7.81039,-3.72162 10.99756,-6.69095 z"
style="display:inline;fill:#cccccc;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="ssssccssscsssssssssssssssssss"
inkscape:connector-curvature="0"
id="path4283"
d="m 392.64431,804.79039 c -8.52094,-5.90399 -8.49394,-11.01546 0.22879,-43.30647 1.03999,-3.85 2.46829,-9.67602 3.17399,-12.9467 0.99731,-4.62219 2.39455,-7.29497 6.27321,-12 2.74456,-3.32932 5.25157,-6.2783 5.57113,-6.5533 40.78433,-60.97488 80.48307,-125.1652 118.27253,-184 9.86283,-15.675 26.59424,-42.225 37.18089,-59 10.58666,-16.775 34.01422,-53.9 52.06125,-82.5 18.04703,-28.6 35.04505,-55.31677 37.77338,-59.37059 l 4.9606,-7.3706 4.1828,0.57332 c 4.16371,0.5707 4.19706,0.54958 7.30887,-4.62941 3.75631,-6.2516 8.82067,-11.57582 12.2516,-12.88026 5.99391,-2.27888 14.03303,2.9506 14.03303,9.12854 0,3.90203 -2.51704,10.62127 -6.02878,16.09385 -1.63417,2.54664 -2.97122,4.85949 -2.97122,5.13969 0,0.28019 0.9,1.54715 2,2.81546 2.28453,2.63408 2.47267,4.21918 0.86833,7.31574 -1.28218,2.47476 -26.61383,45.18798 -55.85724,94.18426 -10.83283,18.15 -25.72943,43.1137 -33.10357,55.47489 -7.37413,12.3612 -13.69273,23.17153 -14.04131,24.02297 -0.34859,0.85144 -7.50972,12.78774 -15.91363,26.52511 -15.54138,25.40455 -32.24417,52.9052 -70.74345,116.47703 -40.26028,66.47968 -43.66308,72.46026 -49.21634,86.5 -1.74036,4.4 -3.92035,8.675 -4.8444,9.5 -0.92405,0.825 -4.36246,3.75 -7.6409,6.5 -3.27845,2.75 -9.57132,8.3067 -13.98415,12.34823 -10.62726,9.73304 -16.99729,13.87361 -22.52334,14.64034 -3.99187,0.55386 -5.03885,0.251 -9.27207,-2.6821 z"
style="display:inline;fill:#000000" />
<path
sodipodi:nodetypes="scccs"
inkscape:connector-curvature="0"
id="path4292"
d="m 450.89044,688.88586 c 8.71518,5.62513 45.74035,-59.18436 43.57923,-75.43494 l -7.07107,-6.56599 c -29.93081,25.86352 -47.78438,74.72281 -47.78438,74.72281 0,0 0,0 11.27622,7.27812 z"
style="fill:url(#linearGradient4300);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<g
style="display:inline"
transform="matrix(0.98314313,0.18283763,-0.18283763,0.98314313,124.70585,-392.20667)"
id="g3663-9-5">
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2.39000654;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect6268"
width="98.462646"
height="99.005119"
x="-37.056244"
y="554.24286"
transform="matrix(0.98314312,-0.18283763,0.18283763,0.98314312,0,0)" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="m 71.874136,599.56332 -149.77963,27.8549"
id="path6270"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Sans;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"
x="-193.01566"
y="595.05914"
id="text6272"
sodipodi:linespacing="125%"
transform="matrix(0.98314312,-0.18283763,0.18283763,0.98314312,0,0)"><tspan
sodipodi:role="line"
id="tspan6274"
x="-193.01566"
y="595.05914">Pad buttons</tspan></text>
<rect
transform="matrix(0.98314312,-0.18283763,0.18283763,0.98314312,0,0)"
y="706.24286"
x="-37.056244"
height="99.005119"
width="98.462646"
id="rect6276"
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2.39000654;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
transform="matrix(0.98314312,-0.18283763,0.18283763,0.98314312,0,0)"
sodipodi:linespacing="125%"
id="text6282"
y="755.05914"
x="-193.01566"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Sans;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="755.05914"
x="-193.01566"
id="tspan6284"
sodipodi:role="line">Pad buttons</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Sans;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"
x="-193.01566"
y="755.05914"
id="text6286"
sodipodi:linespacing="125%"
transform="matrix(0.98314312,-0.18283763,0.18283763,0.98314312,0,0)"><tspan
sodipodi:role="line"
id="tspan6288"
x="-193.01566"
y="755.05914">Pad buttons</tspan></text>
<text
transform="matrix(0.98314312,-0.18283763,0.18283763,0.98314312,0,0)"
sodipodi:linespacing="125%"
id="text6290"
y="675.05914"
x="-193.01566"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Sans;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="675.05914"
x="-193.01566"
id="tspan6292"
sodipodi:role="line">Pad ring</tspan></text>
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path6296"
d="M 125.82687,670.90125 -63.278482,706.06966"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="M 101.12816,756.8662 -48.65147,784.7211"
id="path6298"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="M 734.27856,551.64315 407.53319,612.40883"
id="path6300"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Sans;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"
x="-193.01566"
y="675.05914"
id="text6302"
sodipodi:linespacing="125%"
transform="matrix(0.98314312,-0.18283763,0.18283763,0.98314312,0,0)"><tspan
sodipodi:role="line"
id="tspan6304"
x="-193.01566"
y="675.05914">Pad ring</tspan></text>
<text
transform="matrix(0.98314312,-0.18283763,0.18283763,0.98314312,0,0)"
sodipodi:linespacing="125%"
id="text6306"
y="669.05914"
x="514.98431"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Sans;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="669.05914"
x="514.98431"
id="tspan6308"
sodipodi:role="line">Tool buttons</tspan></text>
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path6310"
d="M 744.15182,604.7329 368.24929,674.64046"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Sans;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"
x="558.98431"
y="723.05914"
id="text6312"
sodipodi:linespacing="125%"
transform="matrix(0.98314312,-0.18283763,0.18283763,0.98314312,0,0)"><tspan
sodipodi:role="line"
id="tspan6314"
x="558.98431"
y="723.05914">Tool tip</tspan></text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -8,15 +8,23 @@ Apple iPad.
@image html tablet.svg "Illustration of a graphics tablet"
@section tablet-tools Tablet buttons vs. tablet tools
@section tablet-tools Pad buttons vs. tablet tools
Most tablets provide two types of devices. The pysical tablet often provides
a number of buttons and a touch ring or strip. Interaction on the drawing
surface of the tablet requires a tool, usually in the shape of a stylus.
The libinput interface exposed by devices with the @ref
Most tablets provide two types of devices. The physical tablet often
provides a number of buttons and a touch ring or strip. Interaction on the
drawing surface of the tablet requires a tool, usually in the shape of a
stylus. The libinput interface exposed by devices with the @ref
LIBINPUT_DEVICE_CAP_TABLET_TOOL capability applies only to events generated
by tools.
Buttons, rings or strips on the physical tablet hardware (the "pad") are
exposed by devices with the @ref LIBINPUT_DEVICE_CAP_TABLET_PAD capability.
Pad events do not require a tool to be in proximity. Note that both
capabilities may exist on the same device though usually they are split
across multiple kernel devices.
@image html tablet-interfaces.svg "Difference between Pad and Tool buttons"
Touch events on the tablet integrated into a screen itself are exposed
through the @ref LIBINPUT_DEVICE_CAP_TOUCH capability. Touch events on a
standalone tablet are exposed through the @ref LIBINPUT_DEVICE_CAP_POINTER
@ -25,7 +33,7 @@ node for the touch device, resulting in a separate libinput device.
See libinput_device_get_device_group() for information on how to associate
the touch part with other devices exposed by the same physical hardware.
@section tablet-tip Tool tip events vs. button events
@section tablet-tip Tool tip events vs. tool button events
The primary use of a tablet tool is to draw on the surface of the tablet.
When the tool tip comes into contact with the surface, libinput sends an
@ -113,6 +121,8 @@ tablets however extends further than the user may lift the mouse, i.e. the
tool may not be lifted out of physical proximity. For such tools, libinput
provides software-emulated proximity.
Events from the pad do not require proximity, they may be sent any time.
@section tablet-pressure-offset Pressure offset on worn-out tools
When a tool is used for an extended period it can wear down physically. A
@ -209,4 +219,30 @@ libinput_event_tablet_tool_get_y_transformed() the resulting value may be
less than 0 or larger than the upper range provided. It is up to the caller
to test for this and handle or ignore these events accordingly.
@section tablet-pad-buttons Tablet pad button numbers
Tablet Pad buttons are numbered sequentially, starting with button 0. Thus
button numbers returned by libinput_event_tablet_pad_get_button_number()
have no semantic meaning, a notable difference to the button codes returned
by other libinput interfaces (e.g. libinput_event_tablet_tool_get_button()).
The Linux kernel requires all input events to have semantic event codes,
but generic buttons like those on a pad cannot easily be assigned semantic
codes. The kernel supports generic codes in the form of BTN_0 through to
BTN_9 and additional unnamed space up until code 0x10f. Additional generic
buttons are available as BTN_A in the range dedicated for gamepads and
joysticks. Thus, tablet with a large number of buttons have to map across
two semantic ranges, have to use unnamed kernel button codes or risk leaking
into an unrelated range. libinput transparently maps the kernel event codes
into a sequential button range on the pad. Callers should use external
sources like libwacom to associate button numbers to their position on the
tablet.
Some buttons may have expected default behaviors. For example, on Wacom
Intuos Pro series tablets, the button inside the touch ring is expected to
switch between a mode switch. Mode switching is a feature implemented in the
caller and libinput does not provide specific handling. Callers should use
external sources like libwacom to identify which buttons have semantic
behaviors.
*/

View file

@ -20,6 +20,7 @@ libinput_la_SOURCES = \
evdev-mt-touchpad-gestures.c \
evdev-tablet.c \
evdev-tablet.h \
evdev-tablet-pad.c \
filter.c \
filter.h \
filter-private.h \

620
src/evdev-tablet-pad.c Normal file
View file

@ -0,0 +1,620 @@
/*
* Copyright © 2016 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include "evdev-tablet-pad.h"
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#define pad_set_status(pad_,s_) (pad_)->status |= (s_)
#define pad_unset_status(pad_,s_) (pad_)->status &= ~(s_)
#define pad_has_status(pad_,s_) (!!((pad_)->status & (s_)))
static void
pad_get_buttons_pressed(struct pad_dispatch *pad,
struct button_state *buttons)
{
struct button_state *state = &pad->button_state;
struct button_state *prev_state = &pad->prev_button_state;
unsigned int i;
for (i = 0; i < sizeof(buttons->bits); i++)
buttons->bits[i] = state->bits[i] & ~(prev_state->bits[i]);
}
static void
pad_get_buttons_released(struct pad_dispatch *pad,
struct button_state *buttons)
{
struct button_state *state = &pad->button_state;
struct button_state *prev_state = &pad->prev_button_state;
unsigned int i;
for (i = 0; i < sizeof(buttons->bits); i++)
buttons->bits[i] = prev_state->bits[i] & ~(state->bits[i]);
}
static inline bool
pad_button_is_down(const struct pad_dispatch *pad,
uint32_t button)
{
return bit_is_set(pad->button_state.bits, button);
}
static inline bool
pad_any_button_down(const struct pad_dispatch *pad)
{
const struct button_state *state = &pad->button_state;
unsigned int i;
for (i = 0; i < sizeof(state->bits); i++)
if (state->bits[i] != 0)
return true;
return false;
}
static inline void
pad_button_set_down(struct pad_dispatch *pad,
uint32_t button,
bool is_down)
{
struct button_state *state = &pad->button_state;
if (is_down) {
set_bit(state->bits, button);
pad_set_status(pad, PAD_BUTTONS_PRESSED);
} else {
clear_bit(state->bits, button);
pad_set_status(pad, PAD_BUTTONS_RELEASED);
}
}
static void
pad_process_absolute(struct pad_dispatch *pad,
struct evdev_device *device,
struct input_event *e,
uint64_t time)
{
switch (e->code) {
case ABS_WHEEL:
pad->changed_axes |= PAD_AXIS_RING1;
pad_set_status(pad, PAD_AXES_UPDATED);
break;
case ABS_THROTTLE:
pad->changed_axes |= PAD_AXIS_RING2;
pad_set_status(pad, PAD_AXES_UPDATED);
break;
case ABS_RX:
pad->changed_axes |= PAD_AXIS_STRIP1;
pad_set_status(pad, PAD_AXES_UPDATED);
break;
case ABS_RY:
pad->changed_axes |= PAD_AXIS_STRIP2;
pad_set_status(pad, PAD_AXES_UPDATED);
break;
case ABS_MISC:
/* The wacom driver always sends a 0 axis event on finger
up, but we also get an ABS_MISC 15 on touch down and
ABS_MISC 0 on touch up, on top of the actual event. This
is kernel behavior for xf86-input-wacom backwards
compatibility after the 3.17 wacom HID move.
We use that event to tell when we truly went a full
rotation around the wheel vs. a finger release.
FIXME: On the Intuos5 and later the kernel merges all
states into that event, so if any finger is down on any
button, the wheel release won't trigger the ABS_MISC 0
but still send a 0 event. We can't currently detect this.
*/
pad->have_abs_misc_terminator = true;
break;
default:
log_info(device->base.seat->libinput,
"Unhandled EV_ABS event code %#x\n", e->code);
break;
}
}
static inline double
normalize_ring(const struct input_absinfo *absinfo)
{
/* libinput has 0 as the ring's northernmost point in the device's
current logical rotation, increasing clockwise to 1. Wacom has
0 on the left-most wheel position.
*/
double range = absinfo->maximum - absinfo->minimum + 1;
double value = (absinfo->value - absinfo->minimum) / range - 0.25;
if (value < 0.0)
value += 1.0;
return value;
}
static inline double
normalize_strip(const struct input_absinfo *absinfo)
{
/* strip axes don't use a proper value, they just shift the bit left
* for each position. 0 isn't a real value either, it's only sent on
* finger release */
double min = 0,
max = log2(absinfo->maximum);
double range = max - min;
double value = (log2(absinfo->value) - min) / range;
return value;
}
static inline double
pad_handle_ring(struct pad_dispatch *pad,
struct evdev_device *device,
unsigned int code)
{
const struct input_absinfo *absinfo;
double degrees;
absinfo = libevdev_get_abs_info(device->evdev, code);
assert(absinfo);
degrees = normalize_ring(absinfo) * 360;
if (device->left_handed.enabled)
degrees = fmod(degrees + 180, 360);
return degrees;
}
static inline double
pad_handle_strip(struct pad_dispatch *pad,
struct evdev_device *device,
unsigned int code)
{
const struct input_absinfo *absinfo;
double pos;
absinfo = libevdev_get_abs_info(device->evdev, code);
assert(absinfo);
if (absinfo->value == 0)
return 0.0;
pos = normalize_strip(absinfo);
if (device->left_handed.enabled)
pos = 1.0 - pos;
return pos;
}
static void
pad_check_notify_axes(struct pad_dispatch *pad,
struct evdev_device *device,
uint64_t time)
{
struct libinput_device *base = &device->base;
double value;
bool send_finger_up = false;
/* Suppress the reset to 0 on finger up. See the
comment in pad_process_absolute */
if (pad->have_abs_misc_terminator &&
libevdev_get_event_value(device->evdev, EV_ABS, ABS_MISC) == 0)
send_finger_up = true;
if (pad->changed_axes & PAD_AXIS_RING1) {
value = pad_handle_ring(pad, device, ABS_WHEEL);
if (send_finger_up)
value = -1.0;
tablet_pad_notify_ring(base,
time,
0,
value,
LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
}
if (pad->changed_axes & PAD_AXIS_RING2) {
value = pad_handle_ring(pad, device, ABS_THROTTLE);
if (send_finger_up)
value = -1.0;
tablet_pad_notify_ring(base,
time,
1,
value,
LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
}
if (pad->changed_axes & PAD_AXIS_STRIP1) {
value = pad_handle_strip(pad, device, ABS_RX);
if (send_finger_up)
value = -1.0;
tablet_pad_notify_strip(base,
time,
0,
value,
LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
}
if (pad->changed_axes & PAD_AXIS_STRIP2) {
value = pad_handle_strip(pad, device, ABS_RY);
if (send_finger_up)
value = -1.0;
tablet_pad_notify_strip(base,
time,
1,
value,
LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
}
pad->changed_axes = PAD_AXIS_NONE;
pad->have_abs_misc_terminator = false;
}
static void
pad_process_key(struct pad_dispatch *pad,
struct evdev_device *device,
struct input_event *e,
uint64_t time)
{
uint32_t button = e->code;
uint32_t is_press = e->value != 0;
pad_button_set_down(pad, button, is_press);
}
static void
pad_notify_button_mask(struct pad_dispatch *pad,
struct evdev_device *device,
uint64_t time,
const struct button_state *buttons,
enum libinput_button_state state)
{
struct libinput_device *base = &device->base;
int32_t code;
unsigned int i;
for (i = 0; i < sizeof(buttons->bits); i++) {
unsigned char buttons_slice = buttons->bits[i];
code = i * 8;
while (buttons_slice) {
int enabled;
char map;
code++;
enabled = (buttons_slice & 1);
buttons_slice >>= 1;
if (!enabled)
continue;
map = pad->button_map[code - 1];
if (map != -1)
tablet_pad_notify_button(base, time, map, state);
}
}
}
static void
pad_notify_buttons(struct pad_dispatch *pad,
struct evdev_device *device,
uint64_t time,
enum libinput_button_state state)
{
struct button_state buttons;
if (state == LIBINPUT_BUTTON_STATE_PRESSED)
pad_get_buttons_pressed(pad, &buttons);
else
pad_get_buttons_released(pad, &buttons);
pad_notify_button_mask(pad, device, time, &buttons, state);
}
static void
pad_change_to_left_handed(struct evdev_device *device)
{
struct pad_dispatch *pad = (struct pad_dispatch*)device->dispatch;
if (device->left_handed.enabled == device->left_handed.want_enabled)
return;
if (pad_any_button_down(pad))
return;
device->left_handed.enabled = device->left_handed.want_enabled;
}
static void
pad_flush(struct pad_dispatch *pad,
struct evdev_device *device,
uint64_t time)
{
if (pad_has_status(pad, PAD_AXES_UPDATED)) {
pad_check_notify_axes(pad, device, time);
pad_unset_status(pad, PAD_AXES_UPDATED);
}
if (pad_has_status(pad, PAD_BUTTONS_RELEASED)) {
pad_notify_buttons(pad,
device,
time,
LIBINPUT_BUTTON_STATE_RELEASED);
pad_unset_status(pad, PAD_BUTTONS_RELEASED);
pad_change_to_left_handed(device);
}
if (pad_has_status(pad, PAD_BUTTONS_PRESSED)) {
pad_notify_buttons(pad,
device,
time,
LIBINPUT_BUTTON_STATE_PRESSED);
pad_unset_status(pad, PAD_BUTTONS_PRESSED);
}
/* Update state */
memcpy(&pad->prev_button_state,
&pad->button_state,
sizeof(pad->button_state));
}
static void
pad_process(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct input_event *e,
uint64_t time)
{
struct pad_dispatch *pad = (struct pad_dispatch *)dispatch;
switch (e->type) {
case EV_ABS:
pad_process_absolute(pad, device, e, time);
break;
case EV_KEY:
pad_process_key(pad, device, e, time);
break;
case EV_SYN:
pad_flush(pad, device, time);
break;
default:
log_error(device->base.seat->libinput,
"Unexpected event type %s (%#x)\n",
libevdev_event_type_get_name(e->type),
e->type);
break;
}
}
static void
pad_suspend(struct evdev_dispatch *dispatch,
struct evdev_device *device)
{
struct pad_dispatch *pad = (struct pad_dispatch *)dispatch;
struct libinput *libinput = device->base.seat->libinput;
unsigned int code;
for (code = KEY_ESC; code < KEY_CNT; code++) {
if (pad_button_is_down(pad, code))
pad_button_set_down(pad, code, false);
}
pad_flush(pad, device, libinput_now(libinput));
}
static void
pad_destroy(struct evdev_dispatch *dispatch)
{
struct pad_dispatch *pad = (struct pad_dispatch*)dispatch;
free(pad);
}
static struct evdev_dispatch_interface pad_interface = {
pad_process,
pad_suspend, /* suspend */
NULL, /* remove */
pad_destroy,
NULL, /* device_added */
NULL, /* device_removed */
NULL, /* device_suspended */
NULL, /* device_resumed */
NULL, /* post_added */
};
static void
pad_init_buttons(struct pad_dispatch *pad,
struct evdev_device *device)
{
unsigned int code;
size_t i;
int map = 0;
for (i = 0; i < ARRAY_LENGTH(pad->button_map); i++)
pad->button_map[i] = -1;
/* we match wacom_report_numbered_buttons() from the kernel */
for (code = BTN_0; code < BTN_0 + 10; code++) {
if (libevdev_has_event_code(device->evdev, EV_KEY, code))
pad->button_map[code] = map++;
}
for (code = BTN_A; code < BTN_A + 6; code++) {
if (libevdev_has_event_code(device->evdev, EV_KEY, code))
pad->button_map[code] = map++;
}
for (code = BTN_BASE; code < BTN_BASE + 2; code++) {
if (libevdev_has_event_code(device->evdev, EV_KEY, code))
pad->button_map[code] = map++;
}
pad->nbuttons = map;
}
static void
pad_init_left_handed(struct evdev_device *device)
{
if (evdev_tablet_has_left_handed(device))
evdev_init_left_handed(device,
pad_change_to_left_handed);
}
static int
pad_init(struct pad_dispatch *pad, struct evdev_device *device)
{
pad->base.interface = &pad_interface;
pad->device = device;
pad->status = PAD_NONE;
pad->changed_axes = PAD_AXIS_NONE;
pad_init_buttons(pad, device);
pad_init_left_handed(device);
return 0;
}
static uint32_t
pad_sendevents_get_modes(struct libinput_device *device)
{
return LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
}
static enum libinput_config_status
pad_sendevents_set_mode(struct libinput_device *device,
enum libinput_config_send_events_mode mode)
{
struct evdev_device *evdev = (struct evdev_device*)device;
struct pad_dispatch *pad = (struct pad_dispatch*)evdev->dispatch;
if (mode == pad->sendevents.current_mode)
return LIBINPUT_CONFIG_STATUS_SUCCESS;
switch(mode) {
case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
break;
case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
pad_suspend(evdev->dispatch, evdev);
break;
default:
return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
}
pad->sendevents.current_mode = mode;
return LIBINPUT_CONFIG_STATUS_SUCCESS;
}
static enum libinput_config_send_events_mode
pad_sendevents_get_mode(struct libinput_device *device)
{
struct evdev_device *evdev = (struct evdev_device*)device;
struct pad_dispatch *dispatch = (struct pad_dispatch*)evdev->dispatch;
return dispatch->sendevents.current_mode;
}
static enum libinput_config_send_events_mode
pad_sendevents_get_default_mode(struct libinput_device *device)
{
return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
}
struct evdev_dispatch *
evdev_tablet_pad_create(struct evdev_device *device)
{
struct pad_dispatch *pad;
pad = zalloc(sizeof *pad);
if (!pad)
return NULL;
if (pad_init(pad, device) != 0) {
pad_destroy(&pad->base);
return NULL;
}
device->base.config.sendevents = &pad->sendevents.config;
pad->sendevents.current_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
pad->sendevents.config.get_modes = pad_sendevents_get_modes;
pad->sendevents.config.set_mode = pad_sendevents_set_mode;
pad->sendevents.config.get_mode = pad_sendevents_get_mode;
pad->sendevents.config.get_default_mode = pad_sendevents_get_default_mode;
return &pad->base;
}
int
evdev_device_tablet_pad_get_num_buttons(struct evdev_device *device)
{
struct pad_dispatch *pad = (struct pad_dispatch*)device->dispatch;
if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
return -1;
return pad->nbuttons;
}
int
evdev_device_tablet_pad_get_num_rings(struct evdev_device *device)
{
int nrings = 0;
if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
return -1;
if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_WHEEL)) {
nrings++;
if (libevdev_has_event_code(device->evdev,
EV_ABS,
ABS_THROTTLE))
nrings++;
}
return nrings;
}
int
evdev_device_tablet_pad_get_num_strips(struct evdev_device *device)
{
int nstrips = 0;
if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
return -1;
if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_RX)) {
nstrips++;
if (libevdev_has_event_code(device->evdev,
EV_ABS,
ABS_RY))
nstrips++;
}
return nstrips;
}

69
src/evdev-tablet-pad.h Normal file
View file

@ -0,0 +1,69 @@
/*
* Copyright © 2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef EVDEV_BUTTONSET_WACOM_H
#define EVDEV_BUTTONSET_WACOM_H
#include "evdev.h"
#define LIBINPUT_BUTTONSET_AXIS_NONE 0
enum pad_status {
PAD_NONE = 0,
PAD_AXES_UPDATED = 1 << 0,
PAD_BUTTONS_PRESSED = 1 << 1,
PAD_BUTTONS_RELEASED = 1 << 2,
};
enum pad_axes {
PAD_AXIS_NONE = 0,
PAD_AXIS_RING1 = 1 << 0,
PAD_AXIS_RING2 = 1 << 1,
PAD_AXIS_STRIP1 = 1 << 2,
PAD_AXIS_STRIP2 = 1 << 3,
};
struct button_state {
unsigned char bits[NCHARS(KEY_CNT)];
};
struct pad_dispatch {
struct evdev_dispatch base;
struct evdev_device *device;
unsigned char status;
uint32_t changed_axes;
struct button_state button_state;
struct button_state prev_button_state;
char button_map[KEY_CNT];
unsigned int nbuttons;
bool have_abs_misc_terminator;
struct {
struct libinput_device_config_send_events config;
enum libinput_config_send_events_mode current_mode;
} sendevents;
};
#endif

View file

@ -1606,45 +1606,9 @@ tablet_init_accel(struct tablet_dispatch *tablet, struct evdev_device *device)
static void
tablet_init_left_handed(struct evdev_device *device)
{
#if HAVE_LIBWACOM
struct libinput *libinput = device->base.seat->libinput;
WacomDeviceDatabase *db;
WacomDevice *d = NULL;
WacomError *error;
const char *devnode;
db = libwacom_database_new();
if (!db) {
log_info(libinput,
"Failed to initialize libwacom context.\n");
return;
}
error = libwacom_error_new();
devnode = udev_device_get_devnode(device->udev_device);
d = libwacom_new_from_path(db,
devnode,
WFALLBACK_NONE,
error);
if (d) {
if (libwacom_is_reversible(d))
if (evdev_tablet_has_left_handed(device))
evdev_init_left_handed(device,
tablet_change_to_left_handed);
} else if (libwacom_error_get_code(error) == WERROR_UNKNOWN_MODEL) {
log_info(libinput, "Tablet unknown to libwacom\n");
} else {
log_error(libinput,
"libwacom error: %s\n",
libwacom_error_get_message(error));
}
if (error)
libwacom_error_free(&error);
if (d)
libwacom_destroy(d);
libwacom_database_destroy(db);
#endif
}
static int

View file

@ -43,6 +43,10 @@
#include "filter.h"
#include "libinput-private.h"
#if HAVE_LIBWACOM
#include <libwacom/libwacom.h>
#endif
#define DEFAULT_WHEEL_CLICK_ANGLE 15
#define DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT ms2us(200)
@ -61,7 +65,7 @@ enum evdev_device_udev_tags {
EVDEV_UDEV_TAG_TABLET = (1 << 5),
EVDEV_UDEV_TAG_JOYSTICK = (1 << 6),
EVDEV_UDEV_TAG_ACCELEROMETER = (1 << 7),
EVDEV_UDEV_TAG_BUTTONSET = (1 << 8),
EVDEV_UDEV_TAG_TABLET_PAD = (1 << 8),
EVDEV_UDEV_TAG_POINTINGSTICK = (1 << 9),
};
@ -78,7 +82,7 @@ static const struct evdev_udev_tag_match evdev_udev_tag_matches[] = {
{"ID_INPUT_TOUCHPAD", EVDEV_UDEV_TAG_TOUCHPAD},
{"ID_INPUT_TOUCHSCREEN", EVDEV_UDEV_TAG_TOUCHSCREEN},
{"ID_INPUT_TABLET", EVDEV_UDEV_TAG_TABLET},
{"ID_INPUT_TABLET_PAD", EVDEV_UDEV_TAG_BUTTONSET},
{"ID_INPUT_TABLET_PAD", EVDEV_UDEV_TAG_TABLET_PAD},
{"ID_INPUT_JOYSTICK", EVDEV_UDEV_TAG_JOYSTICK},
{"ID_INPUT_ACCELEROMETER", EVDEV_UDEV_TAG_ACCELEROMETER},
{"ID_INPUT_POINTINGSTICK", EVDEV_UDEV_TAG_POINTINGSTICK},
@ -2082,7 +2086,7 @@ evdev_configure_device(struct evdev_device *device)
udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK ? " Pointingstick" : "",
udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "",
udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : "",
udev_tags & EVDEV_UDEV_TAG_BUTTONSET ? " Buttonset" : "");
udev_tags & EVDEV_UDEV_TAG_TABLET_PAD ? " TabletPad" : "");
if (udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER) {
log_info(libinput,
@ -2100,14 +2104,6 @@ evdev_configure_device(struct evdev_device *device)
return -1;
}
/* libwacom assigns tablet _and_ tablet_pad to the pad devices */
if (udev_tags & EVDEV_UDEV_TAG_BUTTONSET) {
log_info(libinput,
"input device '%s', %s is a buttonset, ignoring\n",
device->devname, devnode);
return -1;
}
if (evdev_reject_device(device) == -1) {
log_info(libinput,
"input device '%s', %s was rejected.\n",
@ -2143,7 +2139,17 @@ evdev_configure_device(struct evdev_device *device)
tablet_tags = EVDEV_UDEV_TAG_TABLET |
EVDEV_UDEV_TAG_TOUCHPAD |
EVDEV_UDEV_TAG_TOUCHSCREEN;
if ((udev_tags & tablet_tags) == EVDEV_UDEV_TAG_TABLET) {
/* libwacom assigns tablet _and_ tablet_pad to the pad devices */
if (udev_tags & EVDEV_UDEV_TAG_TABLET_PAD) {
device->dispatch = evdev_tablet_pad_create(device);
device->seat_caps |= EVDEV_DEVICE_TABLET_PAD;
log_info(libinput,
"input device '%s', %s is a tablet pad\n",
device->devname, devnode);
return device->dispatch == NULL ? -1 : 0;
} else if ((udev_tags & tablet_tags) == EVDEV_UDEV_TAG_TABLET) {
device->dispatch = evdev_tablet_create(device);
device->seat_caps |= EVDEV_DEVICE_TABLET;
log_info(libinput,
@ -2572,6 +2578,8 @@ evdev_device_has_capability(struct evdev_device *device,
return !!(device->seat_caps & EVDEV_DEVICE_GESTURE);
case LIBINPUT_DEVICE_CAP_TABLET_TOOL:
return !!(device->seat_caps & EVDEV_DEVICE_TABLET);
case LIBINPUT_DEVICE_CAP_TABLET_PAD:
return !!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD);
default:
return 0;
}
@ -2913,3 +2921,51 @@ evdev_device_destroy(struct evdev_device *device)
free(device->mt.slots);
free(device);
}
bool
evdev_tablet_has_left_handed(struct evdev_device *device)
{
bool has_left_handed = false;
#if HAVE_LIBWACOM
struct libinput *libinput = device->base.seat->libinput;
WacomDeviceDatabase *db;
WacomDevice *d = NULL;
WacomError *error;
const char *devnode;
db = libwacom_database_new();
if (!db) {
log_info(libinput,
"Failed to initialize libwacom context.\n");
goto out;
}
error = libwacom_error_new();
devnode = udev_device_get_devnode(device->udev_device);
d = libwacom_new_from_path(db,
devnode,
WFALLBACK_NONE,
error);
if (d) {
if (libwacom_is_reversible(d))
has_left_handed = true;
} else if (libwacom_error_get_code(error) == WERROR_UNKNOWN_MODEL) {
log_info(libinput, "Tablet unknown to libwacom\n");
} else {
log_error(libinput,
"libwacom error: %s\n",
libwacom_error_get_message(error));
}
if (error)
libwacom_error_free(&error);
if (d)
libwacom_destroy(d);
libwacom_database_destroy(db);
out:
#endif
return has_left_handed;
}

View file

@ -61,6 +61,7 @@ enum evdev_device_seat_capability {
EVDEV_DEVICE_KEYBOARD = (1 << 1),
EVDEV_DEVICE_TOUCH = (1 << 2),
EVDEV_DEVICE_TABLET = (1 << 3),
EVDEV_DEVICE_TABLET_PAD = (1 << 4),
EVDEV_DEVICE_GESTURE = (1 << 5),
};
@ -322,6 +323,9 @@ evdev_mt_touchpad_create(struct evdev_device *device);
struct evdev_dispatch *
evdev_tablet_create(struct evdev_device *device);
struct evdev_dispatch *
evdev_tablet_pad_create(struct evdev_device *device);
void
evdev_tag_touchpad(struct evdev_device *device,
struct udev_device *udev_device);
@ -372,6 +376,15 @@ evdev_device_has_button(struct evdev_device *device, uint32_t code);
int
evdev_device_has_key(struct evdev_device *device, uint32_t code);
int
evdev_device_tablet_pad_get_num_buttons(struct evdev_device *device);
int
evdev_device_tablet_pad_get_num_rings(struct evdev_device *device);
int
evdev_device_tablet_pad_get_num_strips(struct evdev_device *device);
double
evdev_device_transform_x(struct evdev_device *device,
double x,
@ -459,6 +472,9 @@ int
evdev_init_left_handed(struct evdev_device *device,
void (*change_to_left_handed)(struct evdev_device *));
bool
evdev_tablet_has_left_handed(struct evdev_device *device);
static inline uint32_t
evdev_to_left_handed(struct evdev_device *device,
uint32_t button)

View file

@ -544,6 +544,24 @@ tablet_notify_button(struct libinput_device *device,
int32_t button,
enum libinput_button_state state);
void
tablet_pad_notify_button(struct libinput_device *device,
uint64_t time,
int32_t button,
enum libinput_button_state state);
void
tablet_pad_notify_ring(struct libinput_device *device,
uint64_t time,
unsigned int number,
double value,
enum libinput_tablet_pad_ring_axis_source source);
void
tablet_pad_notify_strip(struct libinput_device *device,
uint64_t time,
unsigned int number,
double value,
enum libinput_tablet_pad_strip_axis_source source);
static inline uint64_t
libinput_now(struct libinput *libinput)
{

View file

@ -138,6 +138,23 @@ struct libinput_event_tablet_tool {
enum libinput_tablet_tool_tip_state tip_state;
};
struct libinput_event_tablet_pad {
struct libinput_event base;
uint32_t button;
enum libinput_button_state state;
uint64_t time;
struct {
enum libinput_tablet_pad_ring_axis_source source;
double position;
int number;
} ring;
struct {
enum libinput_tablet_pad_strip_axis_source source;
double position;
int number;
} strip;
};
static void
libinput_default_log_func(struct libinput *libinput,
enum libinput_log_priority priority,
@ -318,6 +335,19 @@ libinput_event_get_tablet_tool_event(struct libinput_event *event)
return (struct libinput_event_tablet_tool *) event;
}
LIBINPUT_EXPORT struct libinput_event_tablet_pad *
libinput_event_get_tablet_pad_event(struct libinput_event *event)
{
require_event_type(libinput_event_get_context(event),
event->type,
NULL,
LIBINPUT_EVENT_TABLET_PAD_RING,
LIBINPUT_EVENT_TABLET_PAD_STRIP,
LIBINPUT_EVENT_TABLET_PAD_BUTTON);
return (struct libinput_event_tablet_pad *) event;
}
LIBINPUT_EXPORT struct libinput_event_device_notify *
libinput_event_get_device_notify_event(struct libinput_event *event)
{
@ -1956,6 +1986,9 @@ device_has_cap(struct libinput_device *device,
case LIBINPUT_DEVICE_CAP_TABLET_TOOL:
capability = "CAP_TABLET_TOOL";
break;
case LIBINPUT_DEVICE_CAP_TABLET_PAD:
capability = "CAP_TABLET_PAD";
break;
}
log_bug_libinput(device->seat->libinput,
@ -2343,6 +2376,82 @@ tablet_notify_button(struct libinput_device *device,
&button_event->base);
}
void
tablet_pad_notify_button(struct libinput_device *device,
uint64_t time,
int32_t button,
enum libinput_button_state state)
{
struct libinput_event_tablet_pad *button_event;
button_event = zalloc(sizeof *button_event);
if (!button_event)
return;
*button_event = (struct libinput_event_tablet_pad) {
.time = time,
.button = button,
.state = state,
};
post_device_event(device,
time,
LIBINPUT_EVENT_TABLET_PAD_BUTTON,
&button_event->base);
}
void
tablet_pad_notify_ring(struct libinput_device *device,
uint64_t time,
unsigned int number,
double value,
enum libinput_tablet_pad_ring_axis_source source)
{
struct libinput_event_tablet_pad *ring_event;
ring_event = zalloc(sizeof *ring_event);
if (!ring_event)
return;
*ring_event = (struct libinput_event_tablet_pad) {
.time = time,
.ring.number = number,
.ring.position = value,
.ring.source = source,
};
post_device_event(device,
time,
LIBINPUT_EVENT_TABLET_PAD_RING,
&ring_event->base);
}
void
tablet_pad_notify_strip(struct libinput_device *device,
uint64_t time,
unsigned int number,
double value,
enum libinput_tablet_pad_strip_axis_source source)
{
struct libinput_event_tablet_pad *strip_event;
strip_event = zalloc(sizeof *strip_event);
if (!strip_event)
return;
*strip_event = (struct libinput_event_tablet_pad) {
.time = time,
.strip.number = number,
.strip.position = value,
.strip.source = source,
};
post_device_event(device,
time,
LIBINPUT_EVENT_TABLET_PAD_STRIP,
&strip_event->base);
}
static void
gesture_notify(struct libinput_device *device,
uint64_t time,
@ -2448,6 +2557,9 @@ event_type_to_str(enum libinput_event_type type)
CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_TOOL_TIP);
CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_BUTTON);
CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_RING);
CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_STRIP);
CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN);
CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE);
CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_END);
@ -2679,6 +2791,24 @@ libinput_device_keyboard_has_key(struct libinput_device *device, uint32_t code)
return evdev_device_has_key((struct evdev_device *)device, code);
}
LIBINPUT_EXPORT int
libinput_device_tablet_pad_get_num_buttons(struct libinput_device *device)
{
return evdev_device_tablet_pad_get_num_buttons((struct evdev_device *)device);
}
LIBINPUT_EXPORT int
libinput_device_tablet_pad_get_num_rings(struct libinput_device *device)
{
return evdev_device_tablet_pad_get_num_rings((struct evdev_device *)device);
}
LIBINPUT_EXPORT int
libinput_device_tablet_pad_get_num_strips(struct libinput_device *device)
{
return evdev_device_tablet_pad_get_num_strips((struct evdev_device *)device);
}
LIBINPUT_EXPORT struct libinput_event *
libinput_event_device_notify_get_base_event(struct libinput_event_device_notify *event)
{
@ -2751,6 +2881,133 @@ libinput_event_tablet_tool_get_base_event(struct libinput_event_tablet_tool *eve
return &event->base;
}
LIBINPUT_EXPORT double
libinput_event_tablet_pad_get_ring_position(struct libinput_event_tablet_pad *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0.0,
LIBINPUT_EVENT_TABLET_PAD_RING);
return event->ring.position;
}
LIBINPUT_EXPORT unsigned int
libinput_event_tablet_pad_get_ring_number(struct libinput_event_tablet_pad *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0,
LIBINPUT_EVENT_TABLET_PAD_RING);
return event->ring.number;
}
LIBINPUT_EXPORT enum libinput_tablet_pad_ring_axis_source
libinput_event_tablet_pad_get_ring_source(struct libinput_event_tablet_pad *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
LIBINPUT_TABLET_PAD_RING_SOURCE_UNKNOWN,
LIBINPUT_EVENT_TABLET_PAD_RING);
return event->ring.source;
}
LIBINPUT_EXPORT double
libinput_event_tablet_pad_get_strip_position(struct libinput_event_tablet_pad *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0.0,
LIBINPUT_EVENT_TABLET_PAD_STRIP);
return event->strip.position;
}
LIBINPUT_EXPORT unsigned int
libinput_event_tablet_pad_get_strip_number(struct libinput_event_tablet_pad *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0,
LIBINPUT_EVENT_TABLET_PAD_STRIP);
return event->strip.number;
}
LIBINPUT_EXPORT enum libinput_tablet_pad_strip_axis_source
libinput_event_tablet_pad_get_strip_source(struct libinput_event_tablet_pad *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
LIBINPUT_TABLET_PAD_STRIP_SOURCE_UNKNOWN,
LIBINPUT_EVENT_TABLET_PAD_STRIP);
return event->strip.source;
}
LIBINPUT_EXPORT uint32_t
libinput_event_tablet_pad_get_button_number(struct libinput_event_tablet_pad *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0,
LIBINPUT_EVENT_TABLET_PAD_BUTTON);
return event->button;
}
LIBINPUT_EXPORT enum libinput_button_state
libinput_event_tablet_pad_get_button_state(struct libinput_event_tablet_pad *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
LIBINPUT_BUTTON_STATE_RELEASED,
LIBINPUT_EVENT_TABLET_PAD_BUTTON);
return event->state;
}
LIBINPUT_EXPORT uint32_t
libinput_event_tablet_pad_get_time(struct libinput_event_tablet_pad *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0,
LIBINPUT_EVENT_TABLET_PAD_RING,
LIBINPUT_EVENT_TABLET_PAD_STRIP,
LIBINPUT_EVENT_TABLET_PAD_BUTTON);
return us2ms(event->time);
}
LIBINPUT_EXPORT uint64_t
libinput_event_tablet_pad_get_time_usec(struct libinput_event_tablet_pad *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0,
LIBINPUT_EVENT_TABLET_PAD_RING,
LIBINPUT_EVENT_TABLET_PAD_STRIP,
LIBINPUT_EVENT_TABLET_PAD_BUTTON);
return event->time;
}
LIBINPUT_EXPORT struct libinput_event *
libinput_event_tablet_pad_get_base_event(struct libinput_event_tablet_pad *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
NULL,
LIBINPUT_EVENT_TABLET_PAD_RING,
LIBINPUT_EVENT_TABLET_PAD_STRIP,
LIBINPUT_EVENT_TABLET_PAD_BUTTON);
return &event->base;
}
LIBINPUT_EXPORT struct libinput_device_group *
libinput_device_group_ref(struct libinput_device_group *group)
{

View file

@ -59,6 +59,7 @@ enum libinput_device_capability {
LIBINPUT_DEVICE_CAP_POINTER = 1,
LIBINPUT_DEVICE_CAP_TOUCH = 2,
LIBINPUT_DEVICE_CAP_TABLET_TOOL = 3,
LIBINPUT_DEVICE_CAP_TABLET_PAD = 4,
LIBINPUT_DEVICE_CAP_GESTURE = 5,
};
@ -134,6 +135,36 @@ enum libinput_pointer_axis_source {
LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
};
/**
* @ingroup event_tablet
*
* The source for a @ref LIBINPUT_EVENT_TABLET_PAD_RING event. See
* libinput_event_tablet_pad_get_ring_source() for details.
*/
enum libinput_tablet_pad_ring_axis_source {
LIBINPUT_TABLET_PAD_RING_SOURCE_UNKNOWN = 1,
/**
* The event is caused by the movement of one or more fingers on
* the ring.
*/
LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER,
};
/**
* @ingroup event_tablet
*
* The source for a @ref LIBINPUT_EVENT_TABLET_PAD_STRIP event. See
* libinput_event_tablet_pad_get_strip_source() for details.
*/
enum libinput_tablet_pad_strip_axis_source {
LIBINPUT_TABLET_PAD_STRIP_SOURCE_UNKNOWN = 1,
/**
* The event is caused by the movement of one or more fingers on
* the strip.
*/
LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
};
/**
* @ingroup device
* @struct libinput_tablet_tool
@ -336,9 +367,34 @@ enum libinput_event_type {
* same logical hardware event, the order of the @ref
* LIBINPUT_EVENT_TABLET_TOOL_BUTTON and @ref
* LIBINPUT_EVENT_TABLET_TOOL_AXIS event is device-specific.
*
* This event is not to be confused with the button events emitted
* by the tablet pad. See @ref LIBINPUT_EVENT_TABLET_PAD_BUTTON.
*
* @see LIBINPUT_EVENT_TABLET_BUTTON
*/
LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
/**
* A button pressed on a device with the @ref
* LIBINPUT_DEVICE_CAP_TABLET_PAD capability.
*
* This event is not to be confused with the button events emitted
* by tools on a tablet. See @ref LIBINPUT_EVENT_TABLET_TOOL_BUTTON.
*/
LIBINPUT_EVENT_TABLET_PAD_BUTTON = 700,
/**
* A status change on a tablet ring with the
* LIBINPUT_DEVICE_CAP_TABLET_PAD capability.
*/
LIBINPUT_EVENT_TABLET_PAD_RING,
/**
* A status change on a strip on a device with the @ref
* LIBINPUT_DEVICE_CAP_TABLET_PAD capability.
*/
LIBINPUT_EVENT_TABLET_PAD_STRIP,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN = 800,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
LIBINPUT_EVENT_GESTURE_SWIPE_END,
@ -445,6 +501,17 @@ struct libinput_event_touch;
*/
struct libinput_event_tablet_tool;
/**
* @ingroup event_tablet
* @struct libinput_event_tablet_pad
*
* Tablet pad event representing a button press, or ring/strip update on
* the tablet pad itself. Valid event types for this event are @ref
* LIBINPUT_EVENT_TABLET_PAD_BUTTON, @ref LIBINPUT_EVENT_TABLET_PAD_RING and
* @ref LIBINPUT_EVENT_TABLET_PAD_STRIP.
*/
struct libinput_event_tablet_pad;
/**
* @defgroup event Accessing and destruction of events
*/
@ -566,6 +633,19 @@ libinput_event_get_gesture_event(struct libinput_event *event);
struct libinput_event_tablet_tool *
libinput_event_get_tablet_tool_event(struct libinput_event *event);
/**
* @ingroup event
*
* Return the tablet pad event that is this input event. If the event type does not
* match the tablet pad event types, this function returns NULL.
*
* The inverse of this function is libinput_event_tablet_pad_get_base_event().
*
* @return A tablet pad event, or NULL for other events
*/
struct libinput_event_tablet_pad *
libinput_event_get_tablet_pad_event(struct libinput_event *event);
/**
* @ingroup event
*
@ -1357,7 +1437,17 @@ libinput_event_gesture_get_angle_delta(struct libinput_event_gesture *event);
/**
* @defgroup event_tablet Tablet events
*
* Events that come from tools on tablet devices.
* Events that come from tools on or the pad of tablet devices.
*
* Events from tablet devices are exposed by two interfaces, tools and pads.
* Tool events originate (usually) from a stylus-like device, pad events
* reflect any events originating from the physical tablet itself.
*
* Note that many tablets support touch events. These are exposed through
* the @ref LIBINPUT_DEVICE_CAP_POINTER interface (for external touchpad-like
* devices such as the Wacom Intuos series) or @ref
* LIBINPUT_DEVICE_CAP_TOUCH interface (for built-in touchscreen-like
* devices such as the Wacom Cintiq series).
*/
/**
@ -2105,6 +2195,182 @@ void
libinput_tablet_tool_set_user_data(struct libinput_tablet_tool *tool,
void *user_data);
/**
* @ingroup event_tablet
*
* @return The generic libinput_event of this event
*/
struct libinput_event *
libinput_event_tablet_pad_get_base_event(struct libinput_event_tablet_pad *event);
/**
* @ingroup event_tablet
*
* Returns the current position of the ring, in degrees counterclockwise
* from the northern-most point of the ring in the tablet's current logical
* orientation.
*
* If the source is @ref LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER,
* libinput sends a terminating event with a ring value of -1 when the
* finger is lifted from the ring. A caller may use this information to e.g.
* determine if kinetic scrolling should be triggered.
*
* @note It is an application bug to call this function for events other than
* @ref LIBINPUT_EVENT_TABLET_PAD_RING. For other events, this function
* returns 0.
*
* @param event The libinput tablet pad event
* @return The current value of the the axis
* @retval -1 The finger was lifted
*/
double
libinput_event_tablet_pad_get_ring_position(struct libinput_event_tablet_pad *event);
/**
* @ingroup event_tablet
*
* Returns the number of the ring that has changed state, with 0 being the
* first ring. On tablets with only one ring, this function always returns
* 0.
*
* @note It is an application bug to call this function for events other than
* @ref LIBINPUT_EVENT_TABLET_PAD_RING. For other events, this function
* returns 0.
*
* @param event The libinput tablet pad event
* @return The index of the ring that changed state
*/
unsigned int
libinput_event_tablet_pad_get_ring_number(struct libinput_event_tablet_pad *event);
/**
* @ingroup event_tablet
*
* Returns the source of the interaction with the ring. If the source is
* @ref LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER, libinput sends a ring
* position value of -1 to terminate the current interaction.
*
* @note It is an application bug to call this function for events other than
* @ref LIBINPUT_EVENT_TABLET_PAD_RING. For other events, this function
* returns 0.
*
* @param event The libinput tablet pad event
* @return The source of the ring interaction
*/
enum libinput_tablet_pad_ring_axis_source
libinput_event_tablet_pad_get_ring_source(struct libinput_event_tablet_pad *event);
/**
* @ingroup event_tablet
*
* Returns the current position of the strip, normalized to the range
* [0, 1], with 0 being the top/left-most point in the tablet's current
* logical orientation.
*
* If the source is @ref LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
* libinput sends a terminating event with a ring value of -1 when the
* finger is lifted from the ring. A caller may use this information to e.g.
* determine if kinetic scrolling should be triggered.
*
* @note It is an application bug to call this function for events other than
* @ref LIBINPUT_EVENT_TABLET_PAD_STRIP. For other events, this function
* returns 0.
*
* @param event The libinput tablet pad event
* @return The current value of the the axis
* @retval -1 The finger was lifted
*/
double
libinput_event_tablet_pad_get_strip_position(struct libinput_event_tablet_pad *event);
/**
* @ingroup event_tablet
*
* Returns the number of the strip that has changed state, with 0 being the
* first strip. On tablets with only one strip, this function always returns
* 0.
*
* @note It is an application bug to call this function for events other than
* @ref LIBINPUT_EVENT_TABLET_PAD_STRIP. For other events, this function
* returns 0.
*
* @param event The libinput tablet pad event
* @return The index of the strip that changed state
*/
unsigned int
libinput_event_tablet_pad_get_strip_number(struct libinput_event_tablet_pad *event);
/**
* @ingroup event_tablet
*
* Returns the source of the interaction with the strip. If the source is
* @ref LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER, libinput sends a strip
* position value of -1 to terminate the current interaction.
*
* @note It is an application bug to call this function for events other than
* @ref LIBINPUT_EVENT_TABLET_PAD_STRIP. For other events, this function
* returns 0.
*
* @param event The libinput tablet pad event
* @return The source of the strip interaction
*/
enum libinput_tablet_pad_strip_axis_source
libinput_event_tablet_pad_get_strip_source(struct libinput_event_tablet_pad *event);
/**
* @ingroup event_tablet
*
* Return the button number that triggered this event, starting at 0.
* For events that are not of type @ref LIBINPUT_EVENT_TABLET_PAD_BUTTON,
* this function returns 0.
*
* Note that the number returned is a generic sequential button number and
* not a semantic button code as defined in linux/input.h.
* See @ref tablet-pad-buttons for more details.
*
* @note It is an application bug to call this function for events other than
* @ref LIBINPUT_EVENT_TABLET_PAD_BUTTON. For other events, this function
* returns 0.
*
* @param event The libinput tablet pad event
* @return the button triggering this event
*/
uint32_t
libinput_event_tablet_pad_get_button_number(struct libinput_event_tablet_pad *event);
/**
* @ingroup event_tablet
*
* Return the button state of the event.
*
* @note It is an application bug to call this function for events other than
* @ref LIBINPUT_EVENT_TABLET_PAD_BUTTON. For other events, this function
* returns 0.
*
* @param event The libinput tablet pad event
* @return the button state triggering this event
*/
enum libinput_button_state
libinput_event_tablet_pad_get_button_state(struct libinput_event_tablet_pad *event);
/**
* @ingroup event_tablet
*
* @param event The libinput tablet pad event
* @return The event time for this event
*/
uint32_t
libinput_event_tablet_pad_get_time(struct libinput_event_tablet_pad *event);
/**
* @ingroup event_tablet
*
* @param event The libinput tablet pad event
* @return The event time for this event in microseconds
*/
uint64_t
libinput_event_tablet_pad_get_time_usec(struct libinput_event_tablet_pad *event);
/**
* @defgroup base Initialization and manipulation of libinput contexts
*/
@ -2931,6 +3197,51 @@ int
libinput_device_keyboard_has_key(struct libinput_device *device,
uint32_t code);
/**
* @ingroup device
*
* Return the number of buttons on a device with the
* @ref LIBINPUT_DEVICE_CAP_TABLET_PAD capability.
* Buttons on a pad device are numbered sequentially, see @ref
* tablet-pad-buttons for details.
*
* @param device A current input device
*
* @return The number of buttons supported by the device.
*/
int
libinput_device_tablet_pad_get_num_buttons(struct libinput_device *device);
/**
* @ingroup device
*
* Return the number of rings a device with the @ref
* LIBINPUT_DEVICE_CAP_TABLET_PAD capability provides.
*
* @param device A current input device
*
* @return The number of rings or 0 if the device has no rings.
*
* @see libinput_event_tablet_pad_get_ring_number
*/
int
libinput_device_tablet_pad_get_num_rings(struct libinput_device *device);
/**
* @ingroup device
*
* Return the number of strips a device with the @ref
* LIBINPUT_DEVICE_CAP_TABLET_PAD capability provides.
*
* @param device A current input device
*
* @return The number of strips or 0 if the device has no strips.
*
* @see libinput_event_tablet_pad_get_strip_number
*/
int
libinput_device_tablet_pad_get_num_strips(struct libinput_device *device);
/**
* @ingroup device
*

View file

@ -234,3 +234,21 @@ LIBINPUT_1.2 {
libinput_tablet_tool_set_user_data;
libinput_tablet_tool_unref;
} LIBINPUT_1.1;
LIBINPUT_1.3 {
libinput_device_tablet_pad_get_num_buttons;
libinput_device_tablet_pad_get_num_rings;
libinput_device_tablet_pad_get_num_strips;
libinput_event_get_tablet_pad_event;
libinput_event_tablet_pad_get_base_event;
libinput_event_tablet_pad_get_button_number;
libinput_event_tablet_pad_get_button_state;
libinput_event_tablet_pad_get_ring_position;
libinput_event_tablet_pad_get_ring_number;
libinput_event_tablet_pad_get_ring_source;
libinput_event_tablet_pad_get_strip_position;
libinput_event_tablet_pad_get_strip_number;
libinput_event_tablet_pad_get_strip_source;
libinput_event_tablet_pad_get_time;
libinput_event_tablet_pad_get_time_usec;
} LIBINPUT_1.2;

View file

@ -49,6 +49,8 @@ liblitest_la_SOURCES = \
litest-device-wacom-cintiq-tablet.c \
litest-device-wacom-cintiq-24hd.c \
litest-device-wacom-intuos-tablet.c \
litest-device-wacom-intuos3-pad.c \
litest-device-wacom-intuos5-pad.c \
litest-device-wacom-isdv4-tablet.c \
litest-device-wacom-touch.c \
litest-device-wacom-intuos-finger.c \
@ -72,6 +74,7 @@ run_tests = \
test-touchpad \
test-touchpad-tap \
test-touchpad-buttons \
test-pad \
test-tablet \
test-device \
test-gestures \
@ -121,6 +124,10 @@ test_tablet_SOURCES = tablet.c
test_tablet_LDADD = $(TEST_LIBS)
test_tablet_LDFLAGS = -static
test_pad_SOURCES = pad.c
test_pad_LDADD = $(TEST_LIBS)
test_pad_LDFLAGS = -static
test_touchpad_SOURCES = touchpad.c
test_touchpad_LDADD = $(TEST_LIBS)
test_touchpad_LDFLAGS = -no-install

View file

@ -0,0 +1,117 @@
/*
* Copyright © 2016 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "litest.h"
#include "litest-int.h"
static void
litest_wacom_intuos3_pad_setup(void)
{
struct litest_device *d = litest_create_device(LITEST_WACOM_INTUOS3_PAD);
litest_set_current_device(d);
}
static struct input_event down[] = {
{ .type = -1, .code = -1 },
};
static struct input_event move[] = {
{ .type = -1, .code = -1 },
};
static struct input_event strip_start[] = {
{ .type = EV_ABS, .code = ABS_RX, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MISC, .value = 15 },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
} ;
static struct input_event strip_change[] = {
{ .type = EV_ABS, .code = ABS_RX, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
} ;
static struct input_event strip_end[] = {
{ .type = EV_ABS, .code = ABS_RX, .value = 0 },
{ .type = EV_ABS, .code = ABS_MISC, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
} ;
static struct litest_device_interface interface = {
.touch_down_events = down,
.touch_move_events = move,
.pad_strip_start_events = strip_start,
.pad_strip_change_events = strip_change,
.pad_strip_end_events = strip_end,
};
static struct input_absinfo absinfo[] = {
{ ABS_X, 0, 1, 0, 0, 0 },
{ ABS_Y, 0, 1, 0, 0, 0 },
{ ABS_RX, 0, 4096, 0, 0, 0 },
{ ABS_MISC, 0, 0, 0, 0, 0 },
{ .value = -1 },
};
static struct input_id input_id = {
.bustype = 0x3,
.vendor = 0x56a,
.product = 0xb7,
};
static int events[] = {
EV_KEY, BTN_0,
EV_KEY, BTN_1,
EV_KEY, BTN_2,
EV_KEY, BTN_3,
EV_KEY, BTN_STYLUS,
-1, -1,
};
static const char udev_rule[] =
"ACTION==\"remove\", GOTO=\"pad_end\"\n"
"KERNEL!=\"event*\", GOTO=\"pad_end\"\n"
"\n"
"ATTRS{name}==\"litest Wacom Intuos3 4x6 Pad*\",\\\n"
" ENV{ID_INPUT_TABLET_PAD}=\"1\"\n"
"\n"
"LABEL=\"pad_end\"";
struct litest_test_device litest_wacom_intuos3_pad_device = {
.type = LITEST_WACOM_INTUOS3_PAD,
.features = LITEST_TABLET_PAD | LITEST_STRIP,
.shortname = "wacom-intuos3-pad",
.setup = litest_wacom_intuos3_pad_setup,
.interface = &interface,
.name = "Wacom Intuos3 4x6 Pad",
.id = &input_id,
.events = events,
.absinfo = absinfo,
.udev_rule = udev_rule,
};

View file

@ -0,0 +1,122 @@
/*
* Copyright © 2016 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "litest.h"
#include "litest-int.h"
static void
litest_wacom_intuos5_pad_setup(void)
{
struct litest_device *d = litest_create_device(LITEST_WACOM_INTUOS5_PAD);
litest_set_current_device(d);
}
static struct input_event down[] = {
{ .type = -1, .code = -1 },
};
static struct input_event move[] = {
{ .type = -1, .code = -1 },
};
static struct input_event ring_start[] = {
{ .type = EV_ABS, .code = ABS_WHEEL, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MISC, .value = 15 },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
} ;
static struct input_event ring_change[] = {
{ .type = EV_ABS, .code = ABS_WHEEL, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
} ;
static struct input_event ring_end[] = {
{ .type = EV_ABS, .code = ABS_WHEEL, .value = 0 },
{ .type = EV_ABS, .code = ABS_MISC, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
} ;
static struct litest_device_interface interface = {
.touch_down_events = down,
.touch_move_events = move,
.pad_ring_start_events = ring_start,
.pad_ring_change_events = ring_change,
.pad_ring_end_events = ring_end,
};
static struct input_absinfo absinfo[] = {
{ ABS_X, 0, 1, 0, 0, 0 },
{ ABS_Y, 0, 1, 0, 0, 0 },
{ ABS_WHEEL, 0, 71, 0, 0, 0 },
{ ABS_MISC, 0, 0, 0, 0, 10 },
{ .value = -1 },
};
static struct input_id input_id = {
.bustype = 0x3,
.vendor = 0x56a,
.product = 0x27,
};
static int events[] = {
EV_KEY, BTN_0,
EV_KEY, BTN_1,
EV_KEY, BTN_2,
EV_KEY, BTN_3,
EV_KEY, BTN_4,
EV_KEY, BTN_5,
EV_KEY, BTN_6,
EV_KEY, BTN_7,
EV_KEY, BTN_8,
EV_KEY, BTN_STYLUS,
-1, -1,
};
static const char udev_rule[] =
"ACTION==\"remove\", GOTO=\"pad_end\"\n"
"KERNEL!=\"event*\", GOTO=\"pad_end\"\n"
"\n"
"ATTRS{name}==\"litest Wacom Intuos5 touch M Pad*\",\\\n"
" ENV{ID_INPUT_TABLET_PAD}=\"1\"\n"
"\n"
"LABEL=\"pad_end\"";
struct litest_test_device litest_wacom_intuos5_pad_device = {
.type = LITEST_WACOM_INTUOS5_PAD,
.features = LITEST_TABLET_PAD | LITEST_RING,
.shortname = "wacom-pad",
.setup = litest_wacom_intuos5_pad_setup,
.interface = &interface,
.name = "Wacom Intuos5 touch M Pad",
.id = &input_id,
.events = events,
.absinfo = absinfo,
.udev_rule = udev_rule,
};

View file

@ -105,6 +105,22 @@ struct litest_device_interface {
struct input_event *tablet_proximity_out_events;
struct input_event *tablet_motion_events;
/**
* Pad events, LITEST_AUTO_ASSIGN is allowed on event values
* for ABS_WHEEL
*/
struct input_event *pad_ring_start_events;
struct input_event *pad_ring_change_events;
struct input_event *pad_ring_end_events;
/**
* Pad events, LITEST_AUTO_ASSIGN is allowed on event values
* for ABS_RX
*/
struct input_event *pad_strip_start_events;
struct input_event *pad_strip_change_events;
struct input_event *pad_strip_end_events;
int min[2]; /* x/y axis minimum */
int max[2]; /* x/y axis maximum */
};

View file

@ -380,6 +380,8 @@ extern struct litest_test_device litest_yubikey_device;
extern struct litest_test_device litest_synaptics_i2c_device;
extern struct litest_test_device litest_wacom_cintiq_24hd_device;
extern struct litest_test_device litest_multitouch_fuzz_screen_device;
extern struct litest_test_device litest_wacom_intuos3_pad_device;
extern struct litest_test_device litest_wacom_intuos5_pad_device;
struct litest_test_device* devices[] = {
&litest_synaptics_clickpad_device,
@ -426,6 +428,8 @@ struct litest_test_device* devices[] = {
&litest_synaptics_i2c_device,
&litest_wacom_cintiq_24hd_device,
&litest_multitouch_fuzz_screen_device,
&litest_wacom_intuos3_pad_device,
&litest_wacom_intuos5_pad_device,
NULL,
};
@ -1811,6 +1815,15 @@ litest_scale_axis(const struct litest_device *d,
return (abs->maximum - abs->minimum) * val/100.0 + abs->minimum;
}
static inline int
litest_scale_range(int min, int max, double val)
{
litest_assert_int_ge((int)val, 0);
litest_assert_int_le((int)val, 100);
return (max - min) * val/100.0 + min;
}
int
litest_scale(const struct litest_device *d, unsigned int axis, double val)
{
@ -1821,12 +1834,120 @@ litest_scale(const struct litest_device *d, unsigned int axis, double val)
if (axis <= ABS_Y) {
min = d->interface->min[axis];
max = d->interface->max[axis];
return (max - min) * val/100.0 + min;
return litest_scale_range(min, max, val);
} else {
return litest_scale_axis(d, axis, val);
}
}
static inline int
auto_assign_pad_value(struct litest_device *dev,
struct input_event *ev,
double value)
{
const struct input_absinfo *abs;
if (ev->value != LITEST_AUTO_ASSIGN ||
ev->type != EV_ABS)
return value;
abs = libevdev_get_abs_info(dev->evdev, ev->code);
litest_assert_notnull(abs);
if (ev->code == ABS_RX || ev->code == ABS_RY) {
double min = abs->minimum != 0 ? log2(abs->minimum) : 0,
max = abs->maximum != 0 ? log2(abs->maximum) : 0;
/* Value 0 is reserved for finger up, so a value of 0% is
* actually 1 */
if (value == 0.0) {
return 1;
} else {
value = litest_scale_range(min, max, value);
return pow(2, value);
}
} else {
return litest_scale_range(abs->minimum, abs->maximum, value);
}
}
void
litest_pad_ring_start(struct litest_device *d, double value)
{
struct input_event *ev;
ev = d->interface->pad_ring_start_events;
while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
value = auto_assign_pad_value(d, ev, value);
litest_event(d, ev->type, ev->code, value);
ev++;
}
}
void
litest_pad_ring_change(struct litest_device *d, double value)
{
struct input_event *ev;
ev = d->interface->pad_ring_change_events;
while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
value = auto_assign_pad_value(d, ev, value);
litest_event(d, ev->type, ev->code, value);
ev++;
}
}
void
litest_pad_ring_end(struct litest_device *d)
{
struct input_event *ev;
ev = d->interface->pad_ring_end_events;
while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
litest_event(d, ev->type, ev->code, ev->value);
ev++;
}
}
void
litest_pad_strip_start(struct litest_device *d, double value)
{
struct input_event *ev;
ev = d->interface->pad_strip_start_events;
while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
value = auto_assign_pad_value(d, ev, value);
litest_event(d, ev->type, ev->code, value);
ev++;
}
}
void
litest_pad_strip_change(struct litest_device *d, double value)
{
struct input_event *ev;
ev = d->interface->pad_strip_change_events;
while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
value = auto_assign_pad_value(d, ev, value);
litest_event(d, ev->type, ev->code, value);
ev++;
}
}
void
litest_pad_strip_end(struct litest_device *d)
{
struct input_event *ev;
ev = d->interface->pad_strip_end_events;
while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
litest_event(d, ev->type, ev->code, ev->value);
ev++;
}
}
void
litest_wait_for_event(struct libinput *li)
{
@ -1965,6 +2086,15 @@ litest_event_type_str(struct libinput_event *event)
case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
str = "TABLET TOOL BUTTON";
break;
case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
str = "TABLET PAD BUTTON";
break;
case LIBINPUT_EVENT_TABLET_PAD_RING:
str = "TABLET PAD RING";
break;
case LIBINPUT_EVENT_TABLET_PAD_STRIP:
str = "TABLET PAD STRIP";
break;
}
return str;
}
@ -1974,6 +2104,7 @@ litest_print_event(struct libinput_event *event)
{
struct libinput_event_pointer *p;
struct libinput_event_tablet_tool *t;
struct libinput_event_tablet_pad *pad;
struct libinput_device *dev;
enum libinput_event_type type;
double x, y;
@ -2035,6 +2166,26 @@ litest_print_event(struct libinput_event *event)
libinput_event_tablet_tool_get_button(t),
libinput_event_tablet_tool_get_button_state(t));
break;
case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
pad = libinput_event_get_tablet_pad_event(event);
fprintf(stderr, "button %d state %d",
libinput_event_tablet_pad_get_button_number(pad),
libinput_event_tablet_pad_get_button_state(pad));
break;
case LIBINPUT_EVENT_TABLET_PAD_RING:
pad = libinput_event_get_tablet_pad_event(event);
fprintf(stderr, "ring %d position %.2f source %d",
libinput_event_tablet_pad_get_ring_number(pad),
libinput_event_tablet_pad_get_ring_position(pad),
libinput_event_tablet_pad_get_ring_source(pad));
break;
case LIBINPUT_EVENT_TABLET_PAD_STRIP:
pad = libinput_event_get_tablet_pad_event(event);
fprintf(stderr, "strip %d position %.2f source %d",
libinput_event_tablet_pad_get_ring_number(pad),
libinput_event_tablet_pad_get_ring_position(pad),
libinput_event_tablet_pad_get_ring_source(pad));
break;
default:
break;
}
@ -2456,6 +2607,81 @@ void litest_assert_tablet_proximity_event(struct libinput *li,
libinput_event_destroy(event);
}
struct libinput_event_tablet_pad *
litest_is_pad_button_event(struct libinput_event *event,
unsigned int button,
enum libinput_button_state state)
{
struct libinput_event_tablet_pad *p;
enum libinput_event_type type = LIBINPUT_EVENT_TABLET_PAD_BUTTON;
litest_assert(event != NULL);
litest_assert_int_eq(libinput_event_get_type(event), type);
p = libinput_event_get_tablet_pad_event(event);
litest_assert(p != NULL);
litest_assert_int_eq(libinput_event_tablet_pad_get_button_number(p),
button);
return p;
}
struct libinput_event_tablet_pad *
litest_is_pad_ring_event(struct libinput_event *event,
unsigned int number,
enum libinput_tablet_pad_ring_axis_source source)
{
struct libinput_event_tablet_pad *p;
enum libinput_event_type type = LIBINPUT_EVENT_TABLET_PAD_RING;
litest_assert(event != NULL);
litest_assert_int_eq(libinput_event_get_type(event), type);
p = libinput_event_get_tablet_pad_event(event);
litest_assert_int_eq(libinput_event_tablet_pad_get_ring_number(p),
number);
litest_assert_int_eq(libinput_event_tablet_pad_get_ring_source(p),
source);
return p;
}
struct libinput_event_tablet_pad *
litest_is_pad_strip_event(struct libinput_event *event,
unsigned int number,
enum libinput_tablet_pad_strip_axis_source source)
{
struct libinput_event_tablet_pad *p;
enum libinput_event_type type = LIBINPUT_EVENT_TABLET_PAD_STRIP;
litest_assert(event != NULL);
litest_assert_int_eq(libinput_event_get_type(event), type);
p = libinput_event_get_tablet_pad_event(event);
litest_assert_int_eq(libinput_event_tablet_pad_get_strip_number(p),
number);
litest_assert_int_eq(libinput_event_tablet_pad_get_strip_source(p),
source);
return p;
}
void
litest_assert_pad_button_event(struct libinput *li,
unsigned int button,
enum libinput_button_state state)
{
struct libinput_event *event;
struct libinput_event_tablet_pad *pev;
litest_wait_for_event(li);
event = libinput_get_event(li);
pev = litest_is_pad_button_event(event, button, state);
libinput_event_destroy(libinput_event_tablet_pad_get_base_event(pev));
}
void
litest_assert_scroll(struct libinput *li,
enum libinput_pointer_axis axis,

View file

@ -197,6 +197,8 @@ enum litest_device_type {
LITEST_SYNAPTICS_I2C = -43,
LITEST_WACOM_CINTIQ_24HD = -44,
LITEST_MULTITOUCH_FUZZ_SCREEN = -45,
LITEST_WACOM_INTUOS3_PAD = -46,
LITEST_WACOM_INTUOS5_PAD = -47,
};
enum litest_device_feature {
@ -223,6 +225,9 @@ enum litest_device_feature {
LITEST_DISTANCE = 1 << 18,
LITEST_TOOL_SERIAL = 1 << 19,
LITEST_TILT = 1 << 20,
LITEST_TABLET_PAD = 1 << 21,
LITEST_RING = 1 << 22,
LITEST_STRIP = 1 << 23,
};
struct litest_device {
@ -440,6 +445,24 @@ litest_tablet_motion(struct litest_device *d,
int x, int y,
struct axis_replacement *axes);
void
litest_pad_ring_start(struct litest_device *d, double value);
void
litest_pad_ring_change(struct litest_device *d, double value);
void
litest_pad_ring_end(struct litest_device *d);
void
litest_pad_strip_start(struct litest_device *d, double value);
void
litest_pad_strip_change(struct litest_device *d, double value);
void
litest_pad_strip_end(struct litest_device *d);
void
litest_hover_start(struct litest_device *d,
unsigned int slot,
@ -526,6 +549,19 @@ struct libinput_event_tablet_tool *
litest_is_tablet_event(struct libinput_event *event,
enum libinput_event_type type);
struct libinput_event_tablet_pad *
litest_is_pad_button_event(struct libinput_event *event,
unsigned int button,
enum libinput_button_state state);
struct libinput_event_tablet_pad *
litest_is_pad_ring_event(struct libinput_event *event,
unsigned int number,
enum libinput_tablet_pad_ring_axis_source source);
struct libinput_event_tablet_pad *
litest_is_pad_strip_event(struct libinput_event *event,
unsigned int number,
enum libinput_tablet_pad_strip_axis_source source);
void
litest_assert_button_event(struct libinput *li,
unsigned int button,
@ -549,6 +585,10 @@ void
litest_assert_tablet_proximity_event(struct libinput *li,
enum libinput_tablet_tool_proximity_state state);
void
litest_assert_pad_button_event(struct libinput *li,
unsigned int button,
enum libinput_button_state state);
struct libevdev_uinput *
litest_create_uinput_device(const char *name,
struct input_id *id,

View file

@ -134,6 +134,7 @@ START_TEST(event_conversion_device_notify)
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
litest_restore_log_handler(li);
}
@ -190,6 +191,7 @@ START_TEST(event_conversion_pointer)
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
litest_restore_log_handler(li);
}
libinput_event_destroy(event);
@ -240,6 +242,7 @@ START_TEST(event_conversion_pointer_abs)
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
litest_restore_log_handler(li);
}
libinput_event_destroy(event);
@ -283,6 +286,7 @@ START_TEST(event_conversion_key)
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
litest_restore_log_handler(li);
}
libinput_event_destroy(event);
@ -333,6 +337,7 @@ START_TEST(event_conversion_touch)
ck_assert(libinput_event_get_keyboard_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
litest_restore_log_handler(li);
}
libinput_event_destroy(event);
@ -381,6 +386,7 @@ START_TEST(event_conversion_gesture)
ck_assert(libinput_event_get_pointer_event(event) == NULL);
ck_assert(libinput_event_get_keyboard_event(event) == NULL);
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
litest_restore_log_handler(li);
}
libinput_event_destroy(event);
@ -427,6 +433,50 @@ START_TEST(event_conversion_tablet)
ck_assert(libinput_event_get_pointer_event(event) == NULL);
ck_assert(libinput_event_get_keyboard_event(event) == NULL);
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
litest_restore_log_handler(li);
}
libinput_event_destroy(event);
}
ck_assert_int_gt(events, 0);
}
END_TEST
START_TEST(event_conversion_tablet_pad)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
int events = 0;
litest_button_click(dev, BTN_0, true);
litest_pad_ring_start(dev, 10);
litest_pad_ring_end(dev);
libinput_dispatch(li);
while ((event = libinput_get_event(li))) {
enum libinput_event_type type;
type = libinput_event_get_type(event);
if (type >= LIBINPUT_EVENT_TABLET_PAD_BUTTON &&
type <= LIBINPUT_EVENT_TABLET_PAD_STRIP) {
struct libinput_event_tablet_pad *p;
struct libinput_event *base;
p = libinput_event_get_tablet_pad_event(event);
base = libinput_event_tablet_pad_get_base_event(p);
ck_assert(event == base);
events++;
litest_disable_log_handler(li);
ck_assert(libinput_event_get_device_notify_event(event) == NULL);
ck_assert(libinput_event_get_pointer_event(event) == NULL);
ck_assert(libinput_event_get_keyboard_event(event) == NULL);
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
litest_restore_log_handler(li);
}
libinput_event_destroy(event);
@ -895,6 +945,7 @@ litest_setup_tests(void)
litest_add_for_device("events:conversion", event_conversion_touch, LITEST_WACOM_TOUCH);
litest_add_for_device("events:conversion", event_conversion_gesture, LITEST_BCM5974);
litest_add_for_device("events:conversion", event_conversion_tablet, LITEST_WACOM_CINTIQ);
litest_add_for_device("events:conversion", event_conversion_tablet_pad, LITEST_WACOM_INTUOS5_PAD);
litest_add_no_device("bitfield_helpers", bitfield_helpers);
litest_add_no_device("context:refcount", context_ref_counting);

431
test/pad.c Normal file
View file

@ -0,0 +1,431 @@
/*
* Copyright © 2016 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <check.h>
#include <errno.h>
#include <fcntl.h>
#include <libinput.h>
#include <unistd.h>
#include <stdbool.h>
#include "libinput-util.h"
#include "litest.h"
START_TEST(pad_cap)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
ck_assert(libinput_device_has_capability(device,
LIBINPUT_DEVICE_CAP_TABLET_PAD));
}
END_TEST
START_TEST(pad_no_cap)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
ck_assert(!libinput_device_has_capability(device,
LIBINPUT_DEVICE_CAP_TABLET_PAD));
}
END_TEST
START_TEST(pad_num_buttons)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
unsigned int code;
unsigned int nbuttons = 0;
for (code = BTN_0; code < KEY_MAX; code++) {
/* BTN_STYLUS is set for compatibility reasons but not
* actually hooked up */
if (code == BTN_STYLUS)
continue;
if (libevdev_has_event_code(dev->evdev, EV_KEY, code))
nbuttons++;
}
ck_assert_int_eq(libinput_device_tablet_pad_get_num_buttons(device),
nbuttons);
}
END_TEST
START_TEST(pad_button)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
unsigned int code;
unsigned int expected_number = 0;
struct libinput_event *ev;
struct libinput_event_tablet_pad *pev;
litest_drain_events(li);
for (code = BTN_LEFT; code < KEY_MAX; code++) {
if (!libevdev_has_event_code(dev->evdev, EV_KEY, code))
continue;
litest_button_click(dev, code, 1);
litest_button_click(dev, code, 0);
libinput_dispatch(li);
switch (code) {
case BTN_STYLUS:
litest_assert_empty_queue(li);
continue;
default:
break;
}
ev = libinput_get_event(li);
pev = litest_is_pad_button_event(ev,
expected_number,
LIBINPUT_BUTTON_STATE_PRESSED);
ev = libinput_event_tablet_pad_get_base_event(pev);
libinput_event_destroy(ev);
ev = libinput_get_event(li);
pev = litest_is_pad_button_event(ev,
expected_number,
LIBINPUT_BUTTON_STATE_RELEASED);
ev = libinput_event_tablet_pad_get_base_event(pev);
libinput_event_destroy(ev);
expected_number++;
}
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(pad_has_ring)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
int nrings;
nrings = libinput_device_tablet_pad_get_num_rings(device);
ck_assert_int_ge(nrings, 1);
}
END_TEST
START_TEST(pad_ring)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *ev;
struct libinput_event_tablet_pad *pev;
int val;
double degrees, expected;
litest_pad_ring_start(dev, 10);
litest_drain_events(li);
/* Wacom's 0 value is at 275 degrees */
expected = 270;
for (val = 0; val < 100; val += 10) {
litest_pad_ring_change(dev, val);
libinput_dispatch(li);
ev = libinput_get_event(li);
pev = litest_is_pad_ring_event(ev,
0,
LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
degrees = libinput_event_tablet_pad_get_ring_position(pev);
ck_assert_double_ge(degrees, 0.0);
ck_assert_double_lt(degrees, 360.0);
/* rounding errors, mostly caused by small physical range */
ck_assert_double_ge(degrees, expected - 2);
ck_assert_double_le(degrees, expected + 2);
libinput_event_destroy(ev);
expected = fmod(degrees + 36, 360);
}
litest_pad_ring_end(dev);
}
END_TEST
START_TEST(pad_ring_finger_up)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *ev;
struct libinput_event_tablet_pad *pev;
double degrees;
litest_pad_ring_start(dev, 10);
litest_drain_events(li);
litest_pad_ring_end(dev);
libinput_dispatch(li);
ev = libinput_get_event(li);
pev = litest_is_pad_ring_event(ev,
0,
LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
degrees = libinput_event_tablet_pad_get_ring_position(pev);
ck_assert_double_eq(degrees, -1.0);
libinput_event_destroy(ev);
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(pad_has_strip)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
int nstrips;
nstrips = libinput_device_tablet_pad_get_num_strips(device);
ck_assert_int_ge(nstrips, 1);
}
END_TEST
START_TEST(pad_strip)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *ev;
struct libinput_event_tablet_pad *pev;
int val;
double pos, expected;
litest_pad_strip_start(dev, 10);
litest_drain_events(li);
expected = 0;
/* 9.5 works with the generic axis scaling without jumping over a
* value. */
for (val = 0; val < 100; val += 9.5) {
litest_pad_strip_change(dev, val);
libinput_dispatch(li);
ev = libinput_get_event(li);
pev = litest_is_pad_strip_event(ev,
0,
LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
pos = libinput_event_tablet_pad_get_strip_position(pev);
ck_assert_double_ge(pos, 0.0);
ck_assert_double_lt(pos, 1.0);
/* rounding errors, mostly caused by small physical range */
ck_assert_double_ge(pos, expected - 0.02);
ck_assert_double_le(pos, expected + 0.02);
libinput_event_destroy(ev);
expected = pos + 0.08;
}
litest_pad_strip_change(dev, 100);
libinput_dispatch(li);
ev = libinput_get_event(li);
pev = litest_is_pad_strip_event(ev,
0,
LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
pos = libinput_event_tablet_pad_get_strip_position(pev);
ck_assert_double_eq(pos, 1.0);
libinput_event_destroy(ev);
litest_pad_strip_end(dev);
}
END_TEST
START_TEST(pad_strip_finger_up)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *ev;
struct libinput_event_tablet_pad *pev;
double pos;
litest_pad_strip_start(dev, 10);
litest_drain_events(li);
litest_pad_strip_end(dev);
libinput_dispatch(li);
ev = libinput_get_event(li);
pev = litest_is_pad_strip_event(ev,
0,
LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
pos = libinput_event_tablet_pad_get_strip_position(pev);
ck_assert_double_eq(pos, -1.0);
libinput_event_destroy(ev);
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(pad_left_handed_default)
{
#if HAVE_LIBWACOM
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
enum libinput_config_status status;
ck_assert(libinput_device_config_left_handed_is_available(device));
ck_assert_int_eq(libinput_device_config_left_handed_get_default(device),
0);
ck_assert_int_eq(libinput_device_config_left_handed_get(device),
0);
status = libinput_device_config_left_handed_set(dev->libinput_device, 1);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
ck_assert_int_eq(libinput_device_config_left_handed_get(device),
1);
ck_assert_int_eq(libinput_device_config_left_handed_get_default(device),
0);
status = libinput_device_config_left_handed_set(dev->libinput_device, 0);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
ck_assert_int_eq(libinput_device_config_left_handed_get(device),
0);
ck_assert_int_eq(libinput_device_config_left_handed_get_default(device),
0);
#endif
}
END_TEST
START_TEST(pad_no_left_handed)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
enum libinput_config_status status;
ck_assert(!libinput_device_config_left_handed_is_available(device));
ck_assert_int_eq(libinput_device_config_left_handed_get_default(device),
0);
ck_assert_int_eq(libinput_device_config_left_handed_get(device),
0);
status = libinput_device_config_left_handed_set(dev->libinput_device, 1);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
ck_assert_int_eq(libinput_device_config_left_handed_get(device),
0);
ck_assert_int_eq(libinput_device_config_left_handed_get_default(device),
0);
status = libinput_device_config_left_handed_set(dev->libinput_device, 0);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
ck_assert_int_eq(libinput_device_config_left_handed_get(device),
0);
ck_assert_int_eq(libinput_device_config_left_handed_get_default(device),
0);
}
END_TEST
START_TEST(pad_left_handed_ring)
{
#if HAVE_LIBWACOM
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *ev;
struct libinput_event_tablet_pad *pev;
int val;
double degrees, expected;
libinput_device_config_left_handed_set(dev->libinput_device, 1);
litest_pad_ring_start(dev, 10);
litest_drain_events(li);
/* Wacom's 0 value is at 275 degrees -> 90 in left-handed mode*/
expected = 90;
for (val = 0; val < 100; val += 10) {
litest_pad_ring_change(dev, val);
libinput_dispatch(li);
ev = libinput_get_event(li);
pev = litest_is_pad_ring_event(ev,
0,
LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
degrees = libinput_event_tablet_pad_get_ring_position(pev);
ck_assert_double_ge(degrees, 0.0);
ck_assert_double_lt(degrees, 360.0);
/* rounding errors, mostly caused by small physical range */
ck_assert_double_ge(degrees, expected - 2);
ck_assert_double_le(degrees, expected + 2);
libinput_event_destroy(ev);
expected = fmod(degrees + 36, 360);
}
litest_pad_ring_end(dev);
#endif
}
END_TEST
void
litest_setup_tests(void)
{
litest_add("pad:cap", pad_cap, LITEST_TABLET_PAD, LITEST_ANY);
litest_add("pad:cap", pad_no_cap, LITEST_ANY, LITEST_TABLET_PAD);
litest_add("pad:button", pad_num_buttons, LITEST_TABLET_PAD, LITEST_ANY);
litest_add("pad:button", pad_button, LITEST_TABLET_PAD, LITEST_ANY);
litest_add("pad:ring", pad_has_ring, LITEST_RING, LITEST_ANY);
litest_add("pad:ring", pad_ring, LITEST_RING, LITEST_ANY);
litest_add("pad:ring", pad_ring_finger_up, LITEST_RING, LITEST_ANY);
litest_add("pad:strip", pad_has_strip, LITEST_STRIP, LITEST_ANY);
litest_add("pad:strip", pad_strip, LITEST_STRIP, LITEST_ANY);
litest_add("pad:strip", pad_strip_finger_up, LITEST_STRIP, LITEST_ANY);
litest_add_for_device("pad:left_handed", pad_left_handed_default, LITEST_WACOM_INTUOS5_PAD);
litest_add_for_device("pad:left_handed", pad_no_left_handed, LITEST_WACOM_INTUOS3_PAD);
litest_add_for_device("pad:left_handed", pad_left_handed_ring, LITEST_WACOM_INTUOS5_PAD);
/* None of the current strip tablets are left-handed */
}

View file

@ -121,6 +121,15 @@ print_event_header(struct libinput_event *ev)
case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
type = "TABLET_TOOL_BUTTON";
break;
case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
type = "TABLET_PAD_BUTTON";
break;
case LIBINPUT_EVENT_TABLET_PAD_RING:
type = "TABLET_PAD_RING";
break;
case LIBINPUT_EVENT_TABLET_PAD_STRIP:
type = "TABLET_PAD_STRIP";
break;
}
printf("%-7s %-16s ", libinput_device_get_sysname(dev), type);
@ -172,6 +181,9 @@ print_device_notify(struct libinput_event *ev)
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_TABLET_TOOL))
printf("T");
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_TABLET_PAD))
printf("P");
if (libinput_device_get_size(dev, &w, &h) == 0)
printf("\tsize %.2f/%.2fmm", w, h);
@ -574,6 +586,66 @@ print_gesture_event_with_coords(struct libinput_event *ev)
}
}
static void
print_tablet_pad_button_event(struct libinput_event *ev)
{
struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
enum libinput_button_state state;
print_event_time(libinput_event_tablet_pad_get_time(p));
state = libinput_event_tablet_pad_get_button_state(p);
printf("%3d %s\n",
libinput_event_tablet_pad_get_button_number(p),
state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released");
}
static void
print_tablet_pad_ring_event(struct libinput_event *ev)
{
struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
const char *source = "<invalid>";
print_event_time(libinput_event_tablet_pad_get_time(p));
switch (libinput_event_tablet_pad_get_ring_source(p)) {
case LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER:
source = "finger";
break;
case LIBINPUT_TABLET_PAD_RING_SOURCE_UNKNOWN:
source = "unknown";
break;
}
printf("ring %d position %.2f (source %s)\n",
libinput_event_tablet_pad_get_ring_number(p),
libinput_event_tablet_pad_get_ring_position(p),
source);
}
static void
print_tablet_pad_strip_event(struct libinput_event *ev)
{
struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
const char *source = "<invalid>";
print_event_time(libinput_event_tablet_pad_get_time(p));
switch (libinput_event_tablet_pad_get_strip_source(p)) {
case LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER:
source = "finger";
break;
case LIBINPUT_TABLET_PAD_STRIP_SOURCE_UNKNOWN:
source = "unknown";
break;
}
printf("strip %d position %.2f (source %s)\n",
libinput_event_tablet_pad_get_strip_number(p),
libinput_event_tablet_pad_get_strip_position(p),
source);
}
static int
handle_and_print_events(struct libinput *li)
{
@ -653,6 +725,15 @@ handle_and_print_events(struct libinput *li)
case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
print_tablet_button_event(ev);
break;
case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
print_tablet_pad_button_event(ev);
break;
case LIBINPUT_EVENT_TABLET_PAD_RING:
print_tablet_pad_ring_event(ev);
break;
case LIBINPUT_EVENT_TABLET_PAD_STRIP:
print_tablet_pad_strip_event(ev);
break;
}
libinput_event_destroy(ev);

View file

@ -798,6 +798,10 @@ handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)
case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
handle_event_tablet(ev, w);
break;
case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
case LIBINPUT_EVENT_TABLET_PAD_RING:
case LIBINPUT_EVENT_TABLET_PAD_STRIP:
break;
}
libinput_event_destroy(ev);