diff options
-rw-r--r-- | doc/Makefile.am | 2 | ||||
-rw-r--r-- | doc/svg/tablet-cintiq24hd-modes.svg | 460 | ||||
-rw-r--r-- | doc/svg/tablet-intuos-modes.svg | 321 | ||||
-rw-r--r-- | doc/tablet-support.dox | 59 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/evdev-tablet-pad-leds.c | 177 | ||||
-rw-r--r-- | src/evdev-tablet-pad.c | 76 | ||||
-rw-r--r-- | src/evdev-tablet-pad.h | 12 | ||||
-rw-r--r-- | src/evdev.h | 16 | ||||
-rw-r--r-- | src/libinput-private.h | 28 | ||||
-rw-r--r-- | src/libinput-util.h | 21 | ||||
-rw-r--r-- | src/libinput.c | 181 | ||||
-rw-r--r-- | src/libinput.h | 576 | ||||
-rw-r--r-- | src/libinput.sym | 15 | ||||
-rw-r--r-- | test/Makefile.am | 2 | ||||
-rw-r--r-- | test/litest-device-wacom-cintiq-24hdt-pad.c | 148 | ||||
-rw-r--r-- | test/litest-device-wacom-ekr.c | 132 | ||||
-rw-r--r-- | test/litest.c | 7 | ||||
-rw-r--r-- | test/litest.h | 2 | ||||
-rw-r--r-- | test/pad.c | 232 | ||||
-rw-r--r-- | tools/event-debug.c | 61 | ||||
-rw-r--r-- | tools/event-gui.c | 1 |
22 files changed, 2367 insertions, 163 deletions
diff --git a/doc/Makefile.am b/doc/Makefile.am index eb5d3c0..9341489 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -49,7 +49,9 @@ diagram_files = \ $(srcdir)/svg/software-buttons.svg \ $(srcdir)/svg/swipe-gestures.svg \ $(srcdir)/svg/tablet-axes.svg \ + $(srcdir)/svg/tablet-cintiq24hd-modes.svg \ $(srcdir)/svg/tablet-interfaces.svg \ + $(srcdir)/svg/tablet-intuos-modes.svg \ $(srcdir)/svg/tablet-left-handed.svg \ $(srcdir)/svg/tablet-out-of-bounds.svg \ $(srcdir)/svg/tablet.svg \ diff --git a/doc/svg/tablet-cintiq24hd-modes.svg b/doc/svg/tablet-cintiq24hd-modes.svg new file mode 100644 index 0000000..f1be53e --- /dev/null +++ b/doc/svg/tablet-cintiq24hd-modes.svg @@ -0,0 +1,460 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="134.12471mm" + height="73.121918mm" + viewBox="0 0 475.24503 259.09341" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="tablet-cintiq24hd-modes.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="2.8" + inkscape:cx="235.34348" + inkscape:cy="122.76858" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + showguides="true" + inkscape:guide-bbox="true" + inkscape:window-width="1920" + inkscape:window-height="1136" + 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"> + <sodipodi:guide + position="63.613208,240.91603" + orientation="0,1" + id="guide4164" /> + <sodipodi:guide + position="61.087828,13.126636" + orientation="0,1" + id="guide4166" /> + <sodipodi:guide + position="455.7142,176.16061" + orientation="0,1" + id="guide5966" /> + <sodipodi:guide + position="457.99098,166.42846" + orientation="0,1" + id="guide5968" /> + <sodipodi:guide + position="457.05348,156.47311" + orientation="-0.00010854417,-0.99999999" + id="guide5970" /> + <sodipodi:guide + position="454.4642,169.19632" + orientation="1,0" + id="guide5972" /> + <sodipodi:guide + position="22.365981,176.16061" + orientation="1,0" + id="guide5974" /> + </sodipodi:namedview> + <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(-139.42745,-156.36228)"> + <rect + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.92000002;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect4136" + width="474.32504" + height="258.1734" + x="139.88745" + y="156.82228" + rx="5" /> + <rect + y="175.42407" + x="199.33878" + height="226.52563" + width="357.34042" + id="rect4140" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.74800003;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <g + id="g5913"> + <g + style="stroke-width:1.87739301;stroke-miterlimit:4;stroke-dasharray:none" + transform="matrix(0.53265351,0,0,0.53265351,104.30809,96.440418)" + id="g7023"> + <circle + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:1.87739301;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path4158" + cx="135.61298" + cy="287.06125" + r="22.98097" /> + <ellipse + cy="287.06125" + cx="135.61298" + id="circle4160" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:1.87739301;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + rx="11.5985" + ry="12.608653" /> + </g> + <rect + rx="0.5" + y="268.93671" + x="175.8243" + height="8.9902887" + width="13.786156" + id="rect4162" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.29022256;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <rect + rx="0.5" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.29022256;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect7027" + width="13.786156" + height="8.9902887" + x="175.8243" + y="280.97675" /> + <rect + rx="0.5" + y="293.01678" + x="175.8243" + height="8.9902887" + width="13.786156" + id="rect7029" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.29022256;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <rect + rx="0.5" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.44849709;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect7031" + width="9.1119308" + height="32.483532" + x="161.90343" + y="269.44443" /> + <rect + rx="0.5" + y="269.44443" + x="149.90343" + height="32.483532" + width="9.1119308" + id="rect7033" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.44849709;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <rect + rx="0.5" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.19463234;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect7035" + width="9.3657951" + height="5.9516821" + x="149.77649" + y="256.10321" /> + <rect + rx="0.5" + y="246.10321" + x="149.77649" + height="5.9516821" + width="9.3657951" + id="rect7037" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.19463234;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <rect + rx="0.5" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.19463234;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect7039" + width="9.3657951" + height="5.9516821" + x="149.77649" + y="236.10321" /> + </g> + <g + id="g5889"> + <g + id="g7056" + transform="matrix(-0.53265351,0,0,0.53265351,651.12665,96.440418)" + style="fill:#4e4e4e;fill-opacity:0.99215686;stroke-width:1.87739301;stroke-miterlimit:4;stroke-dasharray:none"> + <circle + r="22.98097" + cy="287.06125" + cx="135.61298" + id="circle7058" + style="opacity:0.92000002;fill:#4e4e4e;fill-opacity:0.99215686;stroke:#4d4d4d;stroke-width:1.87739301;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <ellipse + ry="12.608653" + rx="11.5985" + style="opacity:0.92000002;fill:#4e4e4e;fill-opacity:0.99215686;stroke:#4d4d4d;stroke-width:1.87739301;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="ellipse7060" + cx="135.61298" + cy="287.06125" /> + </g> + <rect + transform="scale(-1,1)" + style="opacity:0.92000002;fill:#4e4e4e;fill-opacity:0.99215686;stroke:#4d4d4d;stroke-width:0.29022256;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect7062" + width="13.786156" + height="8.9902887" + x="-579.61047" + y="268.93671" + rx="0.5" /> + <rect + transform="scale(-1,1)" + y="280.97675" + x="-579.61047" + height="8.9902887" + width="13.786156" + id="rect7064" + style="opacity:0.92000002;fill:#4e4e4e;fill-opacity:0.99215686;stroke:#4d4d4d;stroke-width:0.29022256;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + rx="0.5" /> + <rect + transform="scale(-1,1)" + style="opacity:0.92000002;fill:#4e4e4e;fill-opacity:0.99215686;stroke:#4d4d4d;stroke-width:0.29022256;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect7066" + width="13.786156" + height="8.9902887" + x="-579.61047" + y="293.01678" + rx="0.5" /> + <rect + transform="scale(-1,1)" + y="269.44443" + x="-593.53131" + height="32.483532" + width="9.1119308" + id="rect7068" + style="opacity:0.92000002;fill:#4e4e4e;fill-opacity:0.99215686;stroke:#4d4d4d;stroke-width:0.44849709;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + rx="0.5" /> + <rect + transform="scale(-1,1)" + style="opacity:0.92000002;fill:#4e4e4e;fill-opacity:0.99215686;stroke:#4d4d4d;stroke-width:0.44849709;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect7070" + width="9.1119308" + height="32.483532" + x="-605.53131" + y="269.44443" + rx="0.5" /> + <rect + transform="scale(-1,1)" + y="256.10321" + x="-605.65826" + height="5.9516821" + width="9.3657951" + id="rect7072" + style="opacity:0.92000002;fill:#4e4e4e;fill-opacity:0.99215686;stroke:#4d4d4d;stroke-width:0.19463234;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + rx="0.5" /> + <rect + transform="scale(-1,1)" + style="opacity:0.92000002;fill:#4e4e4e;fill-opacity:0.99215686;stroke:#4d4d4d;stroke-width:0.19463234;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect7074" + width="9.3657951" + height="5.9516821" + x="-605.65826" + y="246.10321" + rx="0.5" /> + <rect + transform="scale(-1,1)" + y="236.10321" + x="-605.65826" + height="5.9516821" + width="9.3657951" + id="rect7076" + style="opacity:0.92000002;fill:#4e4e4e;fill-opacity:0.99215686;stroke:#4d4d4d;stroke-width:0.19463234;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + rx="0.5" /> + </g> + <circle + r="1.0714241" + cy="239.25041" + cx="593.89166" + id="circle5003" + style="opacity:1;fill:#858585;fill-opacity:1;stroke:none;stroke-width:0.56541353;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <circle + style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.56541353;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="circle5941" + cx="593.89166" + cy="249.01828" + r="1.0714241" /> + <circle + r="1.0714241" + cy="258.98257" + cx="593.89166" + id="circle5943" + style="opacity:1;fill:#858585;fill-opacity:1;stroke:none;stroke-width:0.56541353;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <circle + r="1.0714241" + cy="239.27275" + cx="161.79344" + id="circle5003-7" + style="display:inline;opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.56541353;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <circle + style="display:inline;opacity:1;fill:#858585;fill-opacity:1;stroke:none;stroke-width:0.56541353;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="circle5941-3" + cx="161.79344" + cy="249.04956" + r="1.0714241" /> + <circle + r="1.0714241" + cy="259.00488" + cx="161.79344" + id="circle5943-4" + style="display:inline;opacity:1;fill:#858585;fill-opacity:1;stroke:none;stroke-width:0.56541353;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:125%;font-family:'sans serif';-inkscape-font-specification:'sans serif';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="231.68831" + y="209.7363" + id="text5031" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5033" + x="231.68831" + y="209.7363" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;font-family:'sans serif';-inkscape-font-specification:'sans serif';fill:#ff0000;fill-opacity:1">mode indicators</tspan></text> + <text + sodipodi:linespacing="125%" + id="text6033" + y="209.7363" + x="421.35587" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:125%;font-family:'sans serif';-inkscape-font-specification:'sans serif';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;font-family:'sans serif';-inkscape-font-specification:'sans serif';fill:#ff0000;fill-opacity:1" + y="209.7363" + x="421.35587" + id="tspan6035" + sodipodi:role="line">mode indicators</tspan></text> + <text + sodipodi:linespacing="125%" + id="text6037" + y="291.72427" + x="204.56126" + style="font-style:normal;font-weight:normal;font-size:15px;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 + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;font-family:'sans serif';-inkscape-font-specification:'sans serif';fill:#ff0000;fill-opacity:1" + y="291.72427" + x="204.56126" + id="tspan6039" + sodipodi:role="line">mode toggle buttons</tspan></text> + <g + id="g6047"> + <path + inkscape:connector-curvature="0" + id="path6041" + d="m 163.79343,258.98257 27.45536,0 38.75,-44.41964 40.98214,0.26786" + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path6043" + d="m 163.79343,249.02722 35.9375,0" + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path6045" + d="m 163.79343,239.29507 44.41964,0" + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> + <g + id="g6052" + transform="matrix(-1,0,0,1,755.65383,-0.02232143)"> + <path + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 163.79343,258.98257 27.45536,0 38.75,-44.41964 40.98214,0.26786" + id="path6054" + inkscape:connector-curvature="0" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 163.79343,249.02722 35.9375,0" + id="path6056" + inkscape:connector-curvature="0" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 163.79343,239.29507 44.41964,0" + id="path6058" + inkscape:connector-curvature="0" /> + </g> + <g + id="g6096"> + <path + inkscape:connector-curvature="0" + id="path6088" + d="m 145.10947,239.29507 0,55.95257 78.53936,0" + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path6090" + d="m 145.23574,239.29507 4.98762,0" + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 145.23574,259.29507 4.98762,0" + id="path6092" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6094" + d="m 145.23574,249.01096 4.98762,0" + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> + <g + id="g6102" + transform="matrix(-1,0,0,1,754.76113,0)"> + <path + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 145.10947,239.29507 0,55.95257 78.53936,0" + id="path6104" + inkscape:connector-curvature="0" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 145.23574,239.29507 4.98762,0" + id="path6106" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6108" + d="m 145.23574,259.29507 4.98762,0" + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 145.23574,249.01096 4.98762,0" + id="path6110" + inkscape:connector-curvature="0" /> + </g> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:15px;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="420.56128" + y="291.72427" + id="text6112" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan6114" + x="420.56128" + y="291.72427" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;font-family:'sans serif';-inkscape-font-specification:'sans serif';fill:#ff0000;fill-opacity:1">mode toggle buttons</tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="layer3" + inkscape:label="stylus" + style="display:inline" + transform="translate(-139.42745,-156.36228)" /> +</svg> diff --git a/doc/svg/tablet-intuos-modes.svg b/doc/svg/tablet-intuos-modes.svg new file mode 100644 index 0000000..74ef836 --- /dev/null +++ b/doc/svg/tablet-intuos-modes.svg @@ -0,0 +1,321 @@ +<?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="111.70494mm" + height="65.496956mm" + viewBox="0 0 395.80491 232.07583" + id="svg4249" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="tablet-intuos-modes.svg"> + <defs + id="defs4251"> + <marker + inkscape:isstock="true" + style="overflow:visible" + id="marker5774" + refX="0" + refY="0" + orient="auto" + inkscape:stockid="Arrow1Lend"> + <path + transform="matrix(-0.8,0,0,-0.8,-10,0)" + style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt;stroke-opacity:1" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z" + id="path5776" + inkscape:connector-curvature="0" /> + </marker> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4294" + id="linearGradient4300" + x1="465.81339" + y1="666.13727" + x2="454.82117" + y2="658.65521" + gradientUnits="userSpaceOnUse" /> + <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> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="16" + inkscape:cx="77.55251" + inkscape:cy="104.65462" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + showguides="true" + inkscape:window-width="1920" + inkscape:window-height="1136" + inkscape:window-x="0" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:guide-bbox="true" + fit-margin-left="0.1" + fit-margin-top="0" + fit-margin-right="0" + fit-margin-bottom="0"> + <inkscape:grid + type="xygrid" + id="grid4897" + originx="-56.206096" + originy="-132.53353" /> + <sodipodi:guide + position="71.578207,133.89422" + orientation="0,1" + id="guide4991" /> + <sodipodi:guide + position="28.258193,123.80577" + orientation="1,0" + id="guide4993" /> + <sodipodi:guide + position="63.704627,120.85934" + orientation="1,0" + id="guide4995" /> + <sodipodi:guide + position="48.704626,98.44863" + orientation="0,1" + id="guide4997" /> + <sodipodi:guide + position="54.937501,116.125" + orientation="0,1" + id="guide5057" /> + <sodipodi:guide + position="46.000001,127.5625" + orientation="1,0" + id="guide5059" /> + </sodipodi:namedview> + <metadata + id="metadata4254"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-56.206094,-687.75286)"> + <g + id="g5039"> + <rect + y="688.15009" + x="56.95763" + height="231.28139" + width="394.65616" + id="rect4136" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.79444152;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <rect + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.56541353;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect4140" + width="270.06381" + height="171.19916" + x="150.33304" + y="719.33636" /> + <rect + y="719.56689" + x="77.601257" + height="16.734699" + width="49.124439" + id="rect4142" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.74744725;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <rect + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.74744725;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect4148" + width="49.124439" + height="16.734699" + x="77.601257" + y="740.72821" /> + <rect + y="761.88947" + x="77.601257" + height="16.734699" + width="49.124439" + id="rect4150" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.74744725;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <rect + y="831.41949" + x="77.601257" + height="16.734699" + width="49.124439" + id="rect4154" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.74744725;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <rect + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.74744725;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect4156" + width="49.124439" + height="16.734699" + x="77.601257" + y="852.58075" /> + <circle + r="17.368113" + cy="803.70734" + cx="102.17159" + id="path4158" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.74744725;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <ellipse + ry="9.5291233" + rx="8.7656898" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.39332128;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="circle4160" + cx="102.17159" + cy="803.70734" /> + <rect + y="873.74207" + x="77.601257" + height="16.734699" + width="49.124439" + id="rect4162" + style="opacity:0.92000002;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.74744725;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + </g> + <g + id="g4304" + transform="matrix(0.2806137,0.07519021,-0.0726958,0.27130439,247.48122,626.86299)"> + <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> + <circle + style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.56541353;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path5001" + cx="119.91072" + cy="785.93445" + r="1.0714241" /> + <circle + r="1.0714241" + cy="785.93445" + cx="84.458878" + id="circle5003" + style="opacity:1;fill:#858585;fill-opacity:1;stroke:none;stroke-width:0.56541353;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <circle + r="1.0714241" + cy="821.38873" + cx="119.91072" + id="circle5005" + style="opacity:1;fill:#858585;fill-opacity:1;stroke:none;stroke-width:0.56541353;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <circle + style="opacity:1;fill:#858585;fill-opacity:1;stroke:none;stroke-width:0.56541353;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="circle5007" + cx="84.458878" + cy="821.38873" + r="1.0714241" /> + <g + id="g5021"> + <path + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4.0999999;stroke-dasharray:none;stroke-opacity:1" + d="M 121.60675,784.75904 144.71431,766.2249" + id="path5015" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + sodipodi:nodetypes="cccc" + inkscape:connector-curvature="0" + d="m 109.26292,766.15758 101.60169,0.0205 m -124.715997,18.514 23.107557,-18.53414" + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path5017" /> + </g> + <g + id="g5025" + transform="matrix(1,0,0,-1,0,1607.4699)"> + <path + sodipodi:nodetypes="cc" + inkscape:connector-curvature="0" + id="path5027" + d="M 121.60675,784.75904 144.71431,766.2249" + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4.0999999;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path5029" + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 109.26292,766.15758 101.60169,0.0205 m -124.715997,18.514 23.107557,-18.53414" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccc" /> + </g> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:125%;font-family:'sans serif';-inkscape-font-specification:'sans serif';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="134.17352" + y="761.21094" + id="text5031" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5033" + x="134.17352" + y="761.21094" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;font-family:'sans serif';-inkscape-font-specification:'sans serif';fill:#ff0000;fill-opacity:1">mode indicators</tspan></text> + <text + sodipodi:linespacing="125%" + id="text5035" + y="857.37744" + x="134.17352" + style="font-style:normal;font-weight:normal;font-size:15px;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 + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;font-family:'sans serif';-inkscape-font-specification:'sans serif';fill:#ff0000;fill-opacity:1" + y="857.37744" + x="134.17352" + id="tspan5037" + sodipodi:role="line">mode indicators</tspan></text> + <path + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.40000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 102.39358,803.70368 61,0" + id="path5051" + inkscape:connector-curvature="0" /> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:15px;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="167.36102" + y="806.87744" + id="text5053" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5055" + x="167.36102" + y="806.87744" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;font-family:'sans serif';-inkscape-font-specification:'sans serif';fill:#ff0000;fill-opacity:1">mode toggle button</tspan></text> + </g> +</svg> diff --git a/doc/tablet-support.dox b/doc/tablet-support.dox index c555cea..cda0d70 100644 --- a/doc/tablet-support.dox +++ b/doc/tablet-support.dox @@ -240,8 +240,7 @@ 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 +switch between modes, see @ref tablet-pad-modes. Callers should use external sources like libwacom to identify which buttons have semantic behaviors. @@ -276,4 +275,60 @@ symmetric and thus do not support left-handed mode. libinput requires libwacom to determine if a tablet is capable of being switched to left-handed mode. +@section tablet-pad-modes Tablet pad modes + +Tablet pad modes are virtual groupings of button, ring and strip +functionality. A caller may assign different functionalities depending on +the mode the tablet is in. For example, in mode 0 the touch ring may emulate +scrolling, in mode 1 the touch ring may emulate zooming, etc. libinput +handles the modes and mode switching but does not assign specific +functionality to buttons, rings or strips based on the mode. It is up to the +caller to decide whether the mode only applies to buttons, rings and strips +or only to rings and strips (this is the case with the Wacom OS X and +Windows driver). + +The availability of modes on a touchpad usually depends on visual feedback +such as LEDs around the touch ring. If no visual feedback is available, only +one mode may be available. + +Mode switching is controlled by libinput and usually toggled by one or +more buttons on the device. For example, on the Wacom Intuos 4, 5, and +Pro series tablets the mode button is the button centered in the touch +ring and toggles the modes sequentially. On the Wacom Cintiq 24HD the +three buttons next to each touch ring allow for directly changing the +mode to the desired setting. + +Multiple modes may exist on the tablet, libinput uses the term "mode group" +for such groupings of buttons that share a mode and mode toggle. For +example, the Wacom Cintiq 24HD has two separate mode groups, one for the +left set of buttons, strips, and touch rings and one for the right set. +libinput handles the mode groups independently and returns the mode for each +button as appropriate. The mode group is static for the lifetime of the +device. + +@image html tablet-intuos-modes.svg "Modes on an Intuos Pro-like tablet" + +In the image above, the Intuos Pro-like tablet provides 4 LEDs to indicate +the currently active modes. The button inside the touch ring cycles through +the modes in a clockwise fashion. The upper-right LED indicates that the +currently active mode is 1, based on 0-indexed mode numbering. +libinput_event_tablet_pad_get_mode() would thus return 1 for all button and +ring events on this tablet. When the center button is pressed, the mode +switches to mode 2, the LED changes to the bottom-right and +libinput_event_tablet_pad_get_mode() returns 2 for the center button event +and all subsequent events. + +@image html tablet-cintiq24hd-modes.svg "Modes on an Cintiq 24HD-like tablet" + +In the image above, the Cintiq 24HD-like tablet provides 3 LEDs on each side +of the tablet to indicate the currently active mode for that group of +buttons and the respective ring. The buttons next to the touch ring select +the mode directly. The two LEDs indicate that the mode for the left set of +buttons is currently 0, the mode for the right set of buttons is currently +1, based on 0-indexed mode numbering. libinput_event_tablet_pad_get_mode() +would thus return 0 for all button and ring events on the left and 1 for all +button and ring events on the right. When one of the three mode toggle +buttons on the right is pressed, the right mode switches to that button's +mode but the left mode remains unchanged. + */ diff --git a/src/Makefile.am b/src/Makefile.am index 39c22c2..48e704a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,6 +22,7 @@ libinput_la_SOURCES = \ evdev-tablet.h \ evdev-tablet-pad.c \ evdev-tablet-pad.h \ + evdev-tablet-pad-leds.c \ filter.c \ filter.h \ filter-private.h \ diff --git a/src/evdev-tablet-pad-leds.c b/src/evdev-tablet-pad-leds.c new file mode 100644 index 0000000..8b162a6 --- /dev/null +++ b/src/evdev-tablet-pad-leds.c @@ -0,0 +1,177 @@ +/* + * 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 <assert.h> +#include <limits.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "evdev-tablet-pad.h" + +struct pad_led_group { + struct libinput_tablet_pad_mode_group base; +}; + +static void +pad_led_group_destroy(struct libinput_tablet_pad_mode_group *g) +{ + struct pad_led_group *group = (struct pad_led_group *)g; + + free(group); +} + +static struct pad_led_group * +pad_group_new_basic(struct pad_dispatch *pad, + unsigned int group_index, + int nleds) +{ + struct pad_led_group *group; + + group = zalloc(sizeof *group); + if (!group) + return NULL; + + group->base.device = &pad->device->base; + group->base.refcount = 1; + group->base.index = group_index; + group->base.current_mode = 0; + group->base.num_modes = nleds; + group->base.destroy = pad_led_group_destroy; + + return group; +} + +static inline struct libinput_tablet_pad_mode_group * +pad_get_mode_group(struct pad_dispatch *pad, unsigned int index) +{ + struct libinput_tablet_pad_mode_group *group; + + list_for_each(group, &pad->modes.mode_group_list, link) { + if (group->index == index) + return group; + } + + return NULL; +} + +static int +pad_init_fallback_group(struct pad_dispatch *pad) +{ + struct pad_led_group *group; + + group = pad_group_new_basic(pad, 0, 1); + if (!group) + return 1; + + /* If we only have one group, all buttons/strips/rings are part of + * that group. We rely on the other layers to filter out invalid + * indices */ + group->base.button_mask = -1; + group->base.strip_mask = -1; + group->base.ring_mask = -1; + group->base.toggle_button_mask = 0; + + list_insert(&pad->modes.mode_group_list, &group->base.link); + + return 0; +} + +int +pad_init_leds(struct pad_dispatch *pad, + struct evdev_device *device) +{ + int rc = 1; + + list_init(&pad->modes.mode_group_list); + + if (pad->nbuttons > 32) { + log_bug_libinput(device->base.seat->libinput, + "Too many pad buttons for modes %d\n", + pad->nbuttons); + return rc; + } + + /* Eventually we slot the libwacom-based led detection in here. That + * requires getting the kernel ready first. For now we just init the + * fallback single-mode group. + */ + rc = pad_init_fallback_group(pad); + + return rc; +} + +void +pad_destroy_leds(struct pad_dispatch *pad) +{ + struct libinput_tablet_pad_mode_group *group, *tmpgrp; + + list_for_each_safe(group, tmpgrp, &pad->modes.mode_group_list, link) + libinput_tablet_pad_mode_group_unref(group); +} + +void +pad_button_update_mode(struct libinput_tablet_pad_mode_group *g, + unsigned int button_index, + enum libinput_button_state state) +{ + struct pad_led_group *group = (struct pad_led_group*)g; + + if (state != LIBINPUT_BUTTON_STATE_PRESSED) + return; + + if (!libinput_tablet_pad_mode_group_button_is_toggle(g, button_index)) + return; + + log_bug_libinput(group->base.device->seat->libinput, + "Button %d should not be a toggle button", + button_index); +} + +int +evdev_device_tablet_pad_get_num_mode_groups(struct evdev_device *device) +{ + struct pad_dispatch *pad = (struct pad_dispatch*)device->dispatch; + struct libinput_tablet_pad_mode_group *group; + int num_groups = 0; + + if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD)) + return -1; + + list_for_each(group, &pad->modes.mode_group_list, link) + num_groups++; + + return num_groups; +} + +struct libinput_tablet_pad_mode_group * +evdev_device_tablet_pad_get_mode_group(struct evdev_device *device, + unsigned int index) +{ + struct pad_dispatch *pad = (struct pad_dispatch*)device->dispatch; + + if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD)) + return NULL; + + return pad_get_mode_group(pad, index); +} diff --git a/src/evdev-tablet-pad.c b/src/evdev-tablet-pad.c index b7a2950..0e95408 100644 --- a/src/evdev-tablet-pad.c +++ b/src/evdev-tablet-pad.c @@ -209,12 +209,45 @@ pad_handle_strip(struct pad_dispatch *pad, return pos; } +static inline struct libinput_tablet_pad_mode_group * +pad_ring_get_mode_group(struct pad_dispatch *pad, + unsigned int ring) +{ + struct libinput_tablet_pad_mode_group *group; + + list_for_each(group, &pad->modes.mode_group_list, link) { + if (libinput_tablet_pad_mode_group_has_ring(group, ring)) + return group; + } + + assert(!"Unable to find ring mode group"); + + return NULL; +} + +static inline struct libinput_tablet_pad_mode_group * +pad_strip_get_mode_group(struct pad_dispatch *pad, + unsigned int strip) +{ + struct libinput_tablet_pad_mode_group *group; + + list_for_each(group, &pad->modes.mode_group_list, link) { + if (libinput_tablet_pad_mode_group_has_strip(group, strip)) + return group; + } + + assert(!"Unable to find strip mode group"); + + return NULL; +} + static void pad_check_notify_axes(struct pad_dispatch *pad, struct evdev_device *device, uint64_t time) { struct libinput_device *base = &device->base; + struct libinput_tablet_pad_mode_group *group; double value; bool send_finger_up = false; @@ -229,11 +262,13 @@ pad_check_notify_axes(struct pad_dispatch *pad, if (send_finger_up) value = -1.0; + group = pad_ring_get_mode_group(pad, 0); tablet_pad_notify_ring(base, time, 0, value, - LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER); + LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER, + group); } if (pad->changed_axes & PAD_AXIS_RING2) { @@ -241,11 +276,13 @@ pad_check_notify_axes(struct pad_dispatch *pad, if (send_finger_up) value = -1.0; + group = pad_ring_get_mode_group(pad, 1); tablet_pad_notify_ring(base, time, 1, value, - LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER); + LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER, + group); } if (pad->changed_axes & PAD_AXIS_STRIP1) { @@ -253,11 +290,13 @@ pad_check_notify_axes(struct pad_dispatch *pad, if (send_finger_up) value = -1.0; + group = pad_strip_get_mode_group(pad, 0); tablet_pad_notify_strip(base, time, 0, value, - LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER); + LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER, + group); } if (pad->changed_axes & PAD_AXIS_STRIP2) { @@ -265,11 +304,13 @@ pad_check_notify_axes(struct pad_dispatch *pad, if (send_finger_up) value = -1.0; + group = pad_strip_get_mode_group(pad, 1); tablet_pad_notify_strip(base, time, 1, value, - LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER); + LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER, + group); } pad->changed_axes = PAD_AXIS_NONE; @@ -288,6 +329,22 @@ pad_process_key(struct pad_dispatch *pad, pad_button_set_down(pad, button, is_press); } +static inline struct libinput_tablet_pad_mode_group * +pad_button_get_mode_group(struct pad_dispatch *pad, + unsigned int button) +{ + struct libinput_tablet_pad_mode_group *group; + + list_for_each(group, &pad->modes.mode_group_list, link) { + if (libinput_tablet_pad_mode_group_has_button(group, button)) + return group; + } + + assert(!"Unable to find button mode group\n"); + + return NULL; +} + static void pad_notify_button_mask(struct pad_dispatch *pad, struct evdev_device *device, @@ -296,6 +353,7 @@ pad_notify_button_mask(struct pad_dispatch *pad, enum libinput_button_state state) { struct libinput_device *base = &device->base; + struct libinput_tablet_pad_mode_group *group; int32_t code; unsigned int i; @@ -315,8 +373,11 @@ pad_notify_button_mask(struct pad_dispatch *pad, continue; map = pad->button_map[code - 1]; - if (map != -1) - tablet_pad_notify_button(base, time, map, state); + if (map != -1) { + group = pad_button_get_mode_group(pad, map); + pad_button_update_mode(group, map, state); + tablet_pad_notify_button(base, time, map, state, group); + } } } } @@ -437,6 +498,7 @@ pad_destroy(struct evdev_dispatch *dispatch) { struct pad_dispatch *pad = (struct pad_dispatch*)dispatch; + pad_destroy_leds(pad); free(pad); } @@ -500,6 +562,8 @@ pad_init(struct pad_dispatch *pad, struct evdev_device *device) pad_init_buttons(pad, device); pad_init_left_handed(device); + if (pad_init_leds(pad, device) != 0) + return 1; return 0; } diff --git a/src/evdev-tablet-pad.h b/src/evdev-tablet-pad.h index 84324bc..9002fca 100644 --- a/src/evdev-tablet-pad.h +++ b/src/evdev-tablet-pad.h @@ -64,6 +64,10 @@ struct pad_dispatch { struct libinput_device_config_send_events config; enum libinput_config_send_events_mode current_mode; } sendevents; + + struct { + struct list mode_group_list; + } modes; }; static inline struct libinput * @@ -72,4 +76,12 @@ pad_libinput_context(const struct pad_dispatch *pad) return evdev_libinput_context(pad->device); } +int +pad_init_leds(struct pad_dispatch *pad, struct evdev_device *device); +void +pad_destroy_leds(struct pad_dispatch *pad); +void +pad_button_update_mode(struct libinput_tablet_pad_mode_group *g, + unsigned int pressed_button, + enum libinput_button_state state); #endif diff --git a/src/evdev.h b/src/evdev.h index 2b60faa..939a0b8 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -389,6 +389,22 @@ evdev_device_tablet_pad_get_num_rings(struct evdev_device *device); int evdev_device_tablet_pad_get_num_strips(struct evdev_device *device); +int +evdev_device_tablet_pad_get_num_mode_groups(struct evdev_device *device); + +struct libinput_tablet_pad_mode_group * +evdev_device_tablet_pad_get_mode_group(struct evdev_device *device, + unsigned int index); + +unsigned int +evdev_device_tablet_pad_mode_group_get_button_target( + struct libinput_tablet_pad_mode_group *g, + unsigned int button_index); + +struct libinput_tablet_pad_led * +evdev_device_tablet_pad_get_led(struct evdev_device *device, + unsigned int led); + double evdev_device_transform_x(struct evdev_device *device, double x, diff --git a/src/libinput-private.h b/src/libinput-private.h index 1052212..b325707 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -329,6 +329,25 @@ struct libinput_tablet_tool { bool has_pressure_offset; }; +struct libinput_tablet_pad_mode_group { + struct libinput_device *device; + struct list link; + int refcount; + void *user_data; + + unsigned int index; + unsigned int num_modes; + unsigned int current_mode; + + uint32_t button_mask; + uint32_t ring_mask; + uint32_t strip_mask; + + uint32_t toggle_button_mask; + + void (*destroy)(struct libinput_tablet_pad_mode_group *group); +}; + struct libinput_event { enum libinput_event_type type; struct libinput_device *device; @@ -566,19 +585,22 @@ void tablet_pad_notify_button(struct libinput_device *device, uint64_t time, int32_t button, - enum libinput_button_state state); + enum libinput_button_state state, + struct libinput_tablet_pad_mode_group *group); 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); + enum libinput_tablet_pad_ring_axis_source source, + struct libinput_tablet_pad_mode_group *group); 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); + enum libinput_tablet_pad_strip_axis_source source, + struct libinput_tablet_pad_mode_group *group); static inline uint64_t libinput_now(struct libinput *libinput) diff --git a/src/libinput-util.h b/src/libinput-util.h index 701fe07..a7ebc30 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -27,6 +27,7 @@ #include <assert.h> #include <unistd.h> +#include <limits.h> #include <math.h> #include <stdarg.h> #include <stdbool.h> @@ -86,6 +87,7 @@ int list_empty(const struct list *list); pos = tmp, \ tmp = container_of(pos->member.next, tmp, member)) +#define NBITS(b) (b * 8) #define LONG_BITS (sizeof(long) * 8) #define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS) #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) @@ -399,4 +401,23 @@ us2ms(uint64_t us) return (uint32_t)(us / 1000); } +static inline bool +safe_atoi(const char *str, int *val) +{ + char *endptr; + long v; + + v = strtol(str, &endptr, 10); + if (str == endptr) + return false; + if (*str != '\0' && *endptr != '\0') + return false; + + if (v > INT_MAX || v < INT_MIN) + return false; + + *val = v; + return true; +} + #endif /* LIBINPUT_UTIL_H */ diff --git a/src/libinput.c b/src/libinput.c index 6ff4e9a..cf0ed5b 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -140,6 +140,8 @@ struct libinput_event_tablet_tool { struct libinput_event_tablet_pad { struct libinput_event base; + unsigned int mode; + struct libinput_tablet_pad_mode_group *mode_group; uint64_t time; struct { uint32_t number; @@ -1666,6 +1668,12 @@ libinput_event_tablet_tool_destroy(struct libinput_event_tablet_tool *event) libinput_tablet_tool_unref(event->tool); } +static void +libinput_event_tablet_pad_destroy(struct libinput_event_tablet_pad *event) +{ + libinput_tablet_pad_mode_group_unref(event->mode_group); +} + LIBINPUT_EXPORT void libinput_event_destroy(struct libinput_event *event) { @@ -1680,6 +1688,13 @@ libinput_event_destroy(struct libinput_event *event) libinput_event_tablet_tool_destroy( libinput_event_get_tablet_tool_event(event)); break; + case LIBINPUT_EVENT_TABLET_PAD_RING: + case LIBINPUT_EVENT_TABLET_PAD_STRIP: + case LIBINPUT_EVENT_TABLET_PAD_BUTTON: + case LIBINPUT_EVENT_TABLET_PAD_MODE: + libinput_event_tablet_pad_destroy( + libinput_event_get_tablet_pad_event(event)); + break; default: break; } @@ -2400,18 +2415,24 @@ void tablet_pad_notify_button(struct libinput_device *device, uint64_t time, int32_t button, - enum libinput_button_state state) + enum libinput_button_state state, + struct libinput_tablet_pad_mode_group *group) { struct libinput_event_tablet_pad *button_event; + unsigned int mode; button_event = zalloc(sizeof *button_event); if (!button_event) return; + mode = libinput_tablet_pad_mode_group_get_mode(group); + *button_event = (struct libinput_event_tablet_pad) { .time = time, .button.number = button, .button.state = state, + .mode_group = libinput_tablet_pad_mode_group_ref(group), + .mode = mode, }; post_device_event(device, @@ -2425,19 +2446,25 @@ tablet_pad_notify_ring(struct libinput_device *device, uint64_t time, unsigned int number, double value, - enum libinput_tablet_pad_ring_axis_source source) + enum libinput_tablet_pad_ring_axis_source source, + struct libinput_tablet_pad_mode_group *group) { struct libinput_event_tablet_pad *ring_event; + unsigned int mode; ring_event = zalloc(sizeof *ring_event); if (!ring_event) return; + mode = libinput_tablet_pad_mode_group_get_mode(group); + *ring_event = (struct libinput_event_tablet_pad) { .time = time, .ring.number = number, .ring.position = value, .ring.source = source, + .mode_group = libinput_tablet_pad_mode_group_ref(group), + .mode = mode, }; post_device_event(device, @@ -2451,19 +2478,25 @@ tablet_pad_notify_strip(struct libinput_device *device, uint64_t time, unsigned int number, double value, - enum libinput_tablet_pad_strip_axis_source source) + enum libinput_tablet_pad_strip_axis_source source, + struct libinput_tablet_pad_mode_group *group) { struct libinput_event_tablet_pad *strip_event; + unsigned int mode; strip_event = zalloc(sizeof *strip_event); if (!strip_event) return; + mode = libinput_tablet_pad_mode_group_get_mode(group); + *strip_event = (struct libinput_event_tablet_pad) { .time = time, .strip.number = number, .strip.position = value, .strip.source = source, + .mode_group = libinput_tablet_pad_mode_group_ref(group), + .mode = mode, }; post_device_event(device, @@ -2580,6 +2613,7 @@ event_type_to_str(enum libinput_event_type type) 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_TABLET_PAD_MODE); CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN); CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE); CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_END); @@ -2829,6 +2863,121 @@ 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 int +libinput_device_tablet_pad_get_num_mode_groups(struct libinput_device *device) +{ + return evdev_device_tablet_pad_get_num_mode_groups((struct evdev_device *)device); +} + +LIBINPUT_EXPORT struct libinput_tablet_pad_mode_group* +libinput_device_tablet_pad_get_mode_group(struct libinput_device *device, + unsigned int index) +{ + return evdev_device_tablet_pad_get_mode_group((struct evdev_device *)device, + index); +} + +LIBINPUT_EXPORT unsigned int +libinput_tablet_pad_mode_group_get_num_modes( + struct libinput_tablet_pad_mode_group *group) +{ + return group->num_modes; +} + +LIBINPUT_EXPORT unsigned int +libinput_tablet_pad_mode_group_get_mode(struct libinput_tablet_pad_mode_group *group) +{ + return group->current_mode; +} + +LIBINPUT_EXPORT unsigned int +libinput_tablet_pad_mode_group_get_index(struct libinput_tablet_pad_mode_group *group) +{ + return group->index; +} + +LIBINPUT_EXPORT int +libinput_tablet_pad_mode_group_has_button(struct libinput_tablet_pad_mode_group *group, + unsigned int button) +{ + if ((int)button >= + libinput_device_tablet_pad_get_num_buttons(group->device)) + return 0; + + return !!(group->button_mask & (1 << button)); +} + +LIBINPUT_EXPORT int +libinput_tablet_pad_mode_group_has_ring(struct libinput_tablet_pad_mode_group *group, + unsigned int ring) +{ + if ((int)ring >= + libinput_device_tablet_pad_get_num_rings(group->device)) + return 0; + + return !!(group->ring_mask & (1 << ring)); +} + +LIBINPUT_EXPORT int +libinput_tablet_pad_mode_group_has_strip(struct libinput_tablet_pad_mode_group *group, + unsigned int strip) +{ + if ((int)strip >= + libinput_device_tablet_pad_get_num_strips(group->device)) + return 0; + + return !!(group->strip_mask & (1 << strip)); +} + +LIBINPUT_EXPORT int +libinput_tablet_pad_mode_group_button_is_toggle(struct libinput_tablet_pad_mode_group *group, + unsigned int button) +{ + if ((int)button >= + libinput_device_tablet_pad_get_num_buttons(group->device)) + return 0; + + return !!(group->toggle_button_mask & (1 << button)); +} + +LIBINPUT_EXPORT struct libinput_tablet_pad_mode_group * +libinput_tablet_pad_mode_group_ref( + struct libinput_tablet_pad_mode_group *group) +{ + group->refcount++; + return group; +} + +LIBINPUT_EXPORT struct libinput_tablet_pad_mode_group * +libinput_tablet_pad_mode_group_unref( + struct libinput_tablet_pad_mode_group *group) +{ + assert(group->refcount > 0); + + group->refcount--; + if (group->refcount > 0) + return group; + + list_remove(&group->link); + group->destroy(group); + return NULL; +} + +LIBINPUT_EXPORT void +libinput_tablet_pad_mode_group_set_user_data( + struct libinput_tablet_pad_mode_group *group, + void *user_data) +{ + group->user_data = user_data; +} + +LIBINPUT_EXPORT void * +libinput_tablet_pad_mode_group_get_user_data( + struct libinput_tablet_pad_mode_group *group) +{ + return group->user_data; +} + LIBINPUT_EXPORT struct libinput_event * libinput_event_device_notify_get_base_event(struct libinput_event_device_notify *event) { @@ -2989,6 +3138,32 @@ libinput_event_tablet_pad_get_button_state(struct libinput_event_tablet_pad *eve return event->button.state; } +LIBINPUT_EXPORT unsigned int +libinput_event_tablet_pad_get_mode(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->mode; +} + +LIBINPUT_EXPORT struct libinput_tablet_pad_mode_group * +libinput_event_tablet_pad_get_mode_group(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->mode_group; +} + LIBINPUT_EXPORT uint32_t libinput_event_tablet_pad_get_time(struct libinput_event_tablet_pad *event) { diff --git a/src/libinput.h b/src/libinput.h index abc737e..491eee3 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -39,6 +39,132 @@ extern "C" { /** * @ingroup base + * @struct libinput + * + * A handle for accessing libinput. This struct is refcounted, use + * libinput_ref() and libinput_unref(). + */ +struct libinput; + +/** + * @ingroup device + * @struct libinput_device + * + * A base handle for accessing libinput devices. This struct is + * refcounted, use libinput_device_ref() and libinput_device_unref(). + */ +struct libinput_device; + +/** + * @ingroup device + * @struct libinput_device_group + * + * A base handle for accessing libinput device groups. This struct is + * refcounted, use libinput_device_group_ref() and + * libinput_device_group_unref(). + */ +struct libinput_device_group; + +/** + * @ingroup seat + * @struct libinput_seat + * + * The base handle for accessing libinput seats. This struct is + * refcounted, use libinput_seat_ref() and libinput_seat_unref(). + */ +struct libinput_seat; + +/** + * @ingroup device + * @struct libinput_tablet_tool + * + * An object representing a tool being used by a device with the @ref + * LIBINPUT_DEVICE_CAP_TABLET_TOOL capability. + * + * Tablet events generated by such a device are bound to a specific tool + * rather than coming from the device directly. Depending on the hardware it + * is possible to track the same physical tool across multiple + * struct libinput_device devices, see @ref tablet-serial-numbers. + * + * This struct is refcounted, use libinput_tablet_tool_ref() and + * libinput_tablet_tool_unref(). + */ +struct libinput_tablet_tool; + +/** + * @ingroup event + * @struct libinput_event + * + * The base event type. Use libinput_event_get_pointer_event() or similar to + * get the actual event type. + * + * @warning Unlike other structs events are considered transient and + * <b>not</b> refcounted. + */ +struct libinput_event; + +/** + * @ingroup event + * @struct libinput_event_device_notify + * + * An event notifying the caller of a device being added or removed. + */ +struct libinput_event_device_notify; + +/** + * @ingroup event_keyboard + * @struct libinput_event_keyboard + * + * A keyboard event representing a key press/release. + */ +struct libinput_event_keyboard; + +/** + * @ingroup event_pointer + * @struct libinput_event_pointer + * + * A pointer event representing relative or absolute pointer movement, + * a button press/release or scroll axis events. + */ +struct libinput_event_pointer; + +/** + * @ingroup event_touch + * @struct libinput_event_touch + * + * Touch event representing a touch down, move or up, as well as a touch + * cancel and touch frame events. Valid event types for this event are @ref + * LIBINPUT_EVENT_TOUCH_DOWN, @ref LIBINPUT_EVENT_TOUCH_MOTION, @ref + * LIBINPUT_EVENT_TOUCH_UP, @ref LIBINPUT_EVENT_TOUCH_CANCEL and @ref + * LIBINPUT_EVENT_TOUCH_FRAME. + */ +struct libinput_event_touch; + +/** + * @ingroup event_tablet + * @struct libinput_event_tablet_tool + * + * Tablet tool event representing an axis update, button press, or tool + * update. Valid event types for this event are @ref + * LIBINPUT_EVENT_TABLET_TOOL_AXIS, @ref + * LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY and @ref + * LIBINPUT_EVENT_TABLET_TOOL_BUTTON. + */ +struct libinput_event_tablet_tool; + +/** + * @ingroup event_tablet_pad + * @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; + +/** + * @ingroup base * * Log priority for internal logging messages. */ @@ -136,7 +262,7 @@ enum libinput_pointer_axis_source { }; /** - * @ingroup event_tablet + * @ingroup event_tablet_pad * * The source for a @ref LIBINPUT_EVENT_TABLET_PAD_RING event. See * libinput_event_tablet_pad_get_ring_source() for details. @@ -151,7 +277,7 @@ enum libinput_tablet_pad_ring_axis_source { }; /** - * @ingroup event_tablet + * @ingroup event_tablet_pad * * The source for a @ref LIBINPUT_EVENT_TABLET_PAD_STRIP event. See * libinput_event_tablet_pad_get_strip_source() for details. @@ -167,23 +293,6 @@ enum libinput_tablet_pad_strip_axis_source { /** * @ingroup device - * @struct libinput_tablet_tool - * - * An object representing a tool being used by a device with the @ref - * LIBINPUT_DEVICE_CAP_TABLET_TOOL capability. - * - * Tablet events generated by such a device are bound to a specific tool - * rather than coming from the device directly. Depending on the hardware it - * is possible to track the same physical tool across multiple - * struct libinput_device devices, see @ref tablet-serial-numbers. - * - * This struct is refcounted, use libinput_tablet_tool_ref() and - * libinput_tablet_tool_unref(). - */ -struct libinput_tablet_tool; - -/** - * @ingroup device * * Available tool types for a device with the @ref * LIBINPUT_DEVICE_CAP_TABLET_TOOL capability. The tool type defines the default @@ -247,6 +356,236 @@ enum libinput_tablet_tool_tip_state { }; /** + * @defgroup tablet_pad_modes Tablet pad modes + * + * Handling the virtual mode groups of buttons, strips and rings on tablet + * pad devices. See @ref tablet-pad-modes for details. + */ + +/** + * @ingroup tablet_pad_modes + * @struct libinput_tablet_pad_mode_group + * + * A mode on a tablet pad is a virtual grouping of functionality, usually + * based on some visual feedback like LEDs on the pad. The set of buttons, + * rings and strips that share the same mode are a "mode group". Whenever + * the mode changes, all buttons, rings and strips within this mode group + * are affected. See @ref tablet-pad-modes for detail. + * + * Most tablets only have a single mode group, some tablets provide multiple + * mode groups through independent banks of LEDs (e.g. the Wacom Cintiq + * 24HD). libinput guarantees that at least one mode group is always + * available. + * + * This struct is refcounted, use libinput_tablet_pad_mode_group_ref() and + * libinput_tablet_pad_mode_group_unref(). + */ +struct libinput_tablet_pad_mode_group; + +/** + * @ingroup tablet_pad_modes + * + * Most devices only provide a single mode group, however devices such as + * the Wacom Cintiq 22HD provide two mode groups. If multiple mode groups + * are available, a caller should use + * libinput_tablet_pad_mode_group_has_button(), + * libinput_tablet_pad_mode_group_has_ring() and + * libinput_tablet_pad_mode_group_has_strip() to associate each button, + * ring and strip with the correct mode group. + * + * @return the number of mode groups available on this device + */ +int +libinput_device_tablet_pad_get_num_mode_groups(struct libinput_device *device); + +/** + * @ingroup tablet_pad_modes + * + * The returned mode group is not refcounted and may become invalid after + * the next call to libinput. Use libinput_tablet_pad_mode_group_ref() and + * libinput_tablet_pad_mode_group_unref() to continue using the handle + * outside of the immediate scope. + * + * While at least one reference is kept by the caller, the returned mode + * group will be identical for each subsequent call of this function with + * the same index and that same struct is returned from + * libinput_event_tablet_pad_get_mode_group(), provided the event was + * generated by this mode group. + * + * @param device A device with the @ref LIBINPUT_DEVICE_CAP_TABLET_PAD + * capability + * @param index A mode group index + * @return the mode group with the given index or NULL if an invalid index + * is given. + */ +struct libinput_tablet_pad_mode_group* +libinput_device_tablet_pad_get_mode_group(struct libinput_device *device, + unsigned int index); + +/** + * @ingroup tablet_pad_modes + * + * The returned number is the same index as passed to + * libinput_device_tablet_pad_get_mode_group(). For tablets with only one + * mode this number is always 0. + * + * @param group A previously obtained mode group + * @return the numeric index this mode group represents, starting at 0 + */ +unsigned int +libinput_tablet_pad_mode_group_get_index(struct libinput_tablet_pad_mode_group *group); + +/** + * @ingroup tablet_pad_modes + * + * Query the mode group for the number of available modes. This number is + * usually decided by the number of physical LEDs available on the device. + * Different mode groups may have a different number of modes. + * Use libinput_tablet_pad_mode_group_get_mode() to get the currently active + * mode. + * + * libinput guarantees that at least one mode is available. A device without + * mode switching capability has a single mode group and a single mode. + * + * @param group A previously obtained mode group + * @return the number of modes available in this mode group + */ +unsigned int +libinput_tablet_pad_mode_group_get_num_modes(struct libinput_tablet_pad_mode_group *group); + +/** + * @ingroup tablet_pad_modes + * + * Return the current mode this mode group is in. Note that the returned + * mode is the mode valid as of completing the last libinput_dispatch(). + * The returned mode may thus be different to the mode returned by + * libinput_event_tablet_pad_get_mode(). + * + * For example, if the mode was toggled three times between the call to + * libinput_dispatch(), this function returns the third mode but the events + * in the event queue will return the modes 1, 2 and 3, respectively. + * + * @param group A previously obtained mode group + * @return the numeric index of the current mode in this group, starting at 0 + * + * @see libinput_event_tablet_pad_get_mode + */ +unsigned int +libinput_tablet_pad_mode_group_get_mode(struct libinput_tablet_pad_mode_group *group); + +/** + * @ingroup tablet_pad_modes + * + * Devices without mode switching capabilities return true for every button. + * + * @param group A previously obtained mode group + * @param button A button index, starting at 0 + * @return true if the given button index is part of this mode group or + * false otherwise + */ +int +libinput_tablet_pad_mode_group_has_button(struct libinput_tablet_pad_mode_group *group, + unsigned int button); + +/** + * @ingroup tablet_pad_modes + * + * Devices without mode switching capabilities return true for every ring. + * + * @param group A previously obtained mode group + * @param ring A ring index, starting at 0 + * @return true if the given ring index is part of this mode group or + * false otherwise + */ +int +libinput_tablet_pad_mode_group_has_ring(struct libinput_tablet_pad_mode_group *group, + unsigned int ring); + +/** + * @ingroup tablet_pad_modes + * + * Devices without mode switching capabilities return true for every strip. + * + * @param group A previously obtained mode group + * @param strip A strip index, starting at 0 + * @return true if the given strip index is part of this mode group or + * false otherwise + */ +int +libinput_tablet_pad_mode_group_has_strip(struct libinput_tablet_pad_mode_group *group, + unsigned int strip); + +/** + * @ingroup tablet_pad_modes + * + * Devices without mode switching capabilities return false for every button. + * + * @param group A previously obtained mode group + * @param button A button index, starting at 0 + * @retval non-zero if the button is a mode toggle button for this group, or + * zero otherwise + */ +int +libinput_tablet_pad_mode_group_button_is_toggle(struct libinput_tablet_pad_mode_group *group, + unsigned int button); + +/** + * @ingroup tablet_pad_modes + * + * Increase the refcount of the mode group. A mode device group will be + * freed whenever the refcount reaches 0. + * + * @param group A previously obtained mode group + * @return The passed mode group + */ +struct libinput_tablet_pad_mode_group * +libinput_tablet_pad_mode_group_ref( + struct libinput_tablet_pad_mode_group *group); + +/** + * @ingroup tablet_pad_modes + * + * Decrease the refcount of the mode group. A mode device group will be + * freed whenever the refcount reaches 0. + * + * @param group A previously obtained mode group + * @return NULL if the group was destroyed, otherwise the passed mode group + */ +struct libinput_tablet_pad_mode_group * +libinput_tablet_pad_mode_group_unref( + struct libinput_tablet_pad_mode_group *group); + +/** + * @ingroup tablet_pad_modes + * + * Set caller-specific data associated with this mode group. libinput does + * not manage, look at, or modify this data. The caller must ensure the + * data is valid. + * + * @param group A previously obtained mode group + * @param user_data Caller-specific data pointer + * @see libinput_tablet_pad_mode_group_get_user_data + * + */ +void +libinput_tablet_pad_mode_group_set_user_data( + struct libinput_tablet_pad_mode_group *group, + void *user_data); + +/** + * @ingroup tablet_pad_modes + * + * Get the caller-specific data associated with this input device, if any. + * + * @param group A previously obtained mode group + * @return Caller-specific data pointer or NULL if none was set + * @see libinput_tablet_pad_mode_group_set_user_data + */ +void * +libinput_tablet_pad_mode_group_get_user_data( + struct libinput_tablet_pad_mode_group *group); + +/** * @ingroup base * * Event type for events returned by libinput_get_event(). @@ -395,6 +734,19 @@ enum libinput_event_type { */ LIBINPUT_EVENT_TABLET_PAD_STRIP, + /** + * A mode change on a device with the @ref + * LIBINPUT_DEVICE_CAP_TABLET_PAD capability. + * + * This event is triggered when the mode is changed through + * external means. The event reflects a mode change (see @ref + * tablet-pad-modes) occurring as a result other than that of + * pressing a mode toggle button. + * + * @note Support for this event is not yet implemented. + */ + LIBINPUT_EVENT_TABLET_PAD_MODE, + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN = 800, LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, LIBINPUT_EVENT_GESTURE_SWIPE_END, @@ -404,115 +756,6 @@ enum libinput_event_type { }; /** - * @ingroup base - * @struct libinput - * - * A handle for accessing libinput. This struct is refcounted, use - * libinput_ref() and libinput_unref(). - */ -struct libinput; - -/** - * @ingroup device - * @struct libinput_device - * - * A base handle for accessing libinput devices. This struct is - * refcounted, use libinput_device_ref() and libinput_device_unref(). - */ -struct libinput_device; - -/** - * @ingroup device - * @struct libinput_device_group - * - * A base handle for accessing libinput device groups. This struct is - * refcounted, use libinput_device_group_ref() and - * libinput_device_group_unref(). - */ -struct libinput_device_group; - -/** - * @ingroup seat - * @struct libinput_seat - * - * The base handle for accessing libinput seats. This struct is - * refcounted, use libinput_seat_ref() and libinput_seat_unref(). - */ -struct libinput_seat; - -/** - * @ingroup event - * @struct libinput_event - * - * The base event type. Use libinput_event_get_pointer_event() or similar to - * get the actual event type. - * - * @warning Unlike other structs events are considered transient and - * <b>not</b> refcounted. - */ -struct libinput_event; - -/** - * @ingroup event - * @struct libinput_event_device_notify - * - * An event notifying the caller of a device being added or removed. - */ -struct libinput_event_device_notify; - -/** - * @ingroup event_keyboard - * @struct libinput_event_keyboard - * - * A keyboard event representing a key press/release. - */ -struct libinput_event_keyboard; - -/** - * @ingroup event_pointer - * @struct libinput_event_pointer - * - * A pointer event representing relative or absolute pointer movement, - * a button press/release or scroll axis events. - */ -struct libinput_event_pointer; - -/** - * @ingroup event_touch - * @struct libinput_event_touch - * - * Touch event representing a touch down, move or up, as well as a touch - * cancel and touch frame events. Valid event types for this event are @ref - * LIBINPUT_EVENT_TOUCH_DOWN, @ref LIBINPUT_EVENT_TOUCH_MOTION, @ref - * LIBINPUT_EVENT_TOUCH_UP, @ref LIBINPUT_EVENT_TOUCH_CANCEL and @ref - * LIBINPUT_EVENT_TOUCH_FRAME. - */ -struct libinput_event_touch; - -/** - * @ingroup event_tablet - * @struct libinput_event_tablet_tool - * - * Tablet tool event representing an axis update, button press, or tool - * update. Valid event types for this event are @ref - * LIBINPUT_EVENT_TABLET_TOOL_AXIS, @ref - * LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY and @ref - * LIBINPUT_EVENT_TABLET_TOOL_BUTTON. - */ -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 */ @@ -1449,7 +1692,8 @@ libinput_event_gesture_get_angle_delta(struct libinput_event_gesture *event); /** * @defgroup event_tablet Tablet events * - * Events that come from tools on or the pad of tablet devices. + * Events that come from tools on tablet devices. For events from the pad, + * see @ref event_tablet_pad. * * Events from tablet devices are exposed by two interfaces, tools and pads. * Tool events originate (usually) from a stylus-like device, pad events @@ -2212,7 +2456,14 @@ libinput_tablet_tool_set_user_data(struct libinput_tablet_tool *tool, void *user_data); /** - * @ingroup event_tablet + * @defgroup event_tablet_pad Tablet pad events + * + * Events that come from the pad of tablet devices. For events from the + * tablet tools, see @ref event_tablet. + */ + +/** + * @ingroup event_tablet_pad * * @return The generic libinput_event of this event */ @@ -2220,7 +2471,7 @@ struct libinput_event * libinput_event_tablet_pad_get_base_event(struct libinput_event_tablet_pad *event); /** - * @ingroup event_tablet + * @ingroup event_tablet_pad * * Returns the current position of the ring, in degrees counterclockwise * from the northern-most point of the ring in the tablet's current logical @@ -2243,7 +2494,7 @@ double libinput_event_tablet_pad_get_ring_position(struct libinput_event_tablet_pad *event); /** - * @ingroup event_tablet + * @ingroup event_tablet_pad * * 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 @@ -2260,7 +2511,7 @@ unsigned int libinput_event_tablet_pad_get_ring_number(struct libinput_event_tablet_pad *event); /** - * @ingroup event_tablet + * @ingroup event_tablet_pad * * Returns the source of the interaction with the ring. If the source is * @ref LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER, libinput sends a ring @@ -2277,7 +2528,7 @@ enum libinput_tablet_pad_ring_axis_source libinput_event_tablet_pad_get_ring_source(struct libinput_event_tablet_pad *event); /** - * @ingroup event_tablet + * @ingroup event_tablet_pad * * 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 @@ -2300,7 +2551,7 @@ double libinput_event_tablet_pad_get_strip_position(struct libinput_event_tablet_pad *event); /** - * @ingroup event_tablet + * @ingroup event_tablet_pad * * 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 @@ -2317,7 +2568,7 @@ unsigned int libinput_event_tablet_pad_get_strip_number(struct libinput_event_tablet_pad *event); /** - * @ingroup event_tablet + * @ingroup event_tablet_pad * * Returns the source of the interaction with the strip. If the source is * @ref LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER, libinput sends a strip @@ -2334,7 +2585,7 @@ enum libinput_tablet_pad_strip_axis_source libinput_event_tablet_pad_get_strip_source(struct libinput_event_tablet_pad *event); /** - * @ingroup event_tablet + * @ingroup event_tablet_pad * * Return the button number that triggered this event, starting at 0. * For events that are not of type @ref LIBINPUT_EVENT_TABLET_PAD_BUTTON, @@ -2355,7 +2606,7 @@ uint32_t libinput_event_tablet_pad_get_button_number(struct libinput_event_tablet_pad *event); /** - * @ingroup event_tablet + * @ingroup event_tablet_pad * * Return the button state of the event. * @@ -2370,6 +2621,53 @@ enum libinput_button_state libinput_event_tablet_pad_get_button_state(struct libinput_event_tablet_pad *event); /** + * @ingroup event_tablet_pad + * + * Returns the current mode this button, ring, or strip is considered in. + * The mode is a virtual grouping of functionality, usually based on some + * visual feedback like LEDs on the pad. See @ref tablet-pad-modes for + * details. Mode indices start at 0, a device that does not support modes + * always returns 0. + * + * Mode switching is controlled by libinput and more than one mode may exist + * on the tablet. This function returns the mode that this event's button, + * ring or strip is logically grouped in. If the button is the mode toggle + * button and the button event caused a new mode to be toggled, the mode + * returned is the new mode the button is in. + * + * Note that the returned mode is the mode valid as of the time of the + * event. The returned mode may thus be different to the mode returned by + * libinput_tablet_pad_mode_group_get_mode(). See + * libinput_tablet_pad_mode_group_get_mode() for details. + * + * @param event The libinput tablet pad event + * @return the current 0-indexed mode of this button, ring or strip + * + * @see libinput_tablet_pad_mode_group_get_mode + */ +unsigned int +libinput_event_tablet_pad_get_mode(struct libinput_event_tablet_pad *event); + +/** + * @ingroup event_tablet_pad + * + * Returns the current mode group this button, ring, or strip is considered in. + * The mode is a virtual grouping of functionality, usually based on some + * visual feedback like LEDs on the pad. See @ref tablet-pad-modes for + * details. + * + * The returned mode group is not refcounted and may become invalid after + * the next call to libinput. Use libinput_tablet_pad_mode_group_ref() and + * libinput_tablet_pad_mode_group_unref() to continue using the handle + * outside of the immediate scope. + * + * @param event The libinput tablet pad event + * @return the current 0-indexed mode of this button, ring or strip + */ +struct libinput_tablet_pad_mode_group * +libinput_event_tablet_pad_get_mode_group(struct libinput_event_tablet_pad *event); + +/** * @ingroup event_tablet * * @param event The libinput tablet pad event @@ -2379,7 +2677,7 @@ uint32_t libinput_event_tablet_pad_get_time(struct libinput_event_tablet_pad *event); /** - * @ingroup event_tablet + * @ingroup event_tablet_pad * * @param event The libinput tablet pad event * @return The event time for this event in microseconds diff --git a/src/libinput.sym b/src/libinput.sym index c6a0e4c..cb3f2b8 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -258,4 +258,19 @@ LIBINPUT_1.4 { libinput_device_config_rotation_get_default_angle; libinput_device_config_rotation_is_available; libinput_device_config_rotation_set_angle; + libinput_device_tablet_pad_get_mode_group; + libinput_device_tablet_pad_get_num_mode_groups; + libinput_event_tablet_pad_get_mode; + libinput_event_tablet_pad_get_mode_group; + libinput_tablet_pad_mode_group_button_is_toggle; + libinput_tablet_pad_mode_group_get_index; + libinput_tablet_pad_mode_group_get_mode; + libinput_tablet_pad_mode_group_get_num_modes; + libinput_tablet_pad_mode_group_get_user_data; + libinput_tablet_pad_mode_group_has_button; + libinput_tablet_pad_mode_group_has_strip; + libinput_tablet_pad_mode_group_has_ring; + libinput_tablet_pad_mode_group_ref; + libinput_tablet_pad_mode_group_set_user_data; + libinput_tablet_pad_mode_group_unref; } LIBINPUT_1.3; diff --git a/test/Makefile.am b/test/Makefile.am index 681b906..d9223c6 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -50,6 +50,8 @@ liblitest_la_SOURCES = \ litest-device-wacom-bamboo-tablet.c \ litest-device-wacom-cintiq-tablet.c \ litest-device-wacom-cintiq-24hd.c \ + litest-device-wacom-cintiq-24hdt-pad.c \ + litest-device-wacom-ekr.c \ litest-device-wacom-intuos-tablet.c \ litest-device-wacom-intuos3-pad.c \ litest-device-wacom-intuos5-pad.c \ diff --git a/test/litest-device-wacom-cintiq-24hdt-pad.c b/test/litest-device-wacom-cintiq-24hdt-pad.c new file mode 100644 index 0000000..d20fbd5 --- /dev/null +++ b/test/litest-device-wacom-cintiq-24hdt-pad.c @@ -0,0 +1,148 @@ +/* + * 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 <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "litest.h" +#include "litest-int.h" + +static void litest_wacom_cintiq_pad_setup(void) +{ + struct litest_device *d; + + d = litest_create_device(LITEST_WACOM_CINTIQ_24HDT_PAD); + + litest_set_current_device(d); +} + +static void +litest_wacom_cintiq_pad_teardown(void) +{ + litest_generic_device_teardown(); +} + +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_THROTTLE, 0, 71, 0, 0, 0 }, + { ABS_MISC, 0, 0, 0, 0, 0 }, + { .value = -1 }, +}; + +static struct input_id input_id = { + .bustype = 0x3, + .vendor = 0x56a, + .product = 0xf8, + .version = 0x110, +}; + +static int events[] = { + EV_KEY, KEY_PROG1, + EV_KEY, KEY_PROG2, + EV_KEY, KEY_PROG3, + 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_9, + EV_KEY, BTN_SOUTH, + EV_KEY, BTN_EAST, + EV_KEY, BTN_C, + EV_KEY, BTN_NORTH, + EV_KEY, BTN_WEST, + EV_KEY, BTN_Z, + 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 Cintiq 24 HD touch Pad*\",\\\n" +" ENV{ID_INPUT_TABLET_PAD}=\"1\"\n" +"\n" +"LABEL=\"pad_end\""; + +struct litest_test_device litest_wacom_cintiq_24hdt_pad_device = { + .type = LITEST_WACOM_CINTIQ_24HDT_PAD, + .features = LITEST_TABLET_PAD | LITEST_RING, + .shortname = "wacom-cintiq-24hdt-pad", + .setup = litest_wacom_cintiq_pad_setup, + .teardown = litest_wacom_cintiq_pad_teardown, + .interface = &interface, + + .name = "Wacom Cintiq 24 HD touch Pad", + .id = &input_id, + .events = events, + .absinfo = absinfo, + .udev_rule = udev_rule, +}; diff --git a/test/litest-device-wacom-ekr.c b/test/litest-device-wacom-ekr.c new file mode 100644 index 0000000..fa73927 --- /dev/null +++ b/test/litest-device-wacom-ekr.c @@ -0,0 +1,132 @@ +/* + * 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_ekr_setup(void) +{ + struct litest_device *d = litest_create_device(LITEST_WACOM_EKR); + 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, 0 }, + { .value = -1 }, +}; + +static struct input_id input_id = { + .bustype = 0x3, + .vendor = 0x56a, + .product = 0x331, +}; + +static int events[] = { + EV_KEY, BTN_0, + EV_KEY, BTN_1, + EV_KEY, BTN_2, + EV_KEY, BTN_3, + 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_9, + EV_KEY, BTN_BASE, + EV_KEY, BTN_BASE2, + EV_KEY, BTN_SOUTH, + EV_KEY, BTN_EAST, + EV_KEY, BTN_C, + EV_KEY, BTN_NORTH, + EV_KEY, BTN_WEST, + EV_KEY, BTN_Z, + 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 Express Key Remote Pad*\",\\\n" +" ENV{ID_INPUT_TABLET_PAD}=\"1\"\n" +"\n" +"LABEL=\"pad_end\""; + +struct litest_test_device litest_wacom_ekr_device = { + .type = LITEST_WACOM_EKR, + .features = LITEST_TABLET_PAD | LITEST_RING, + .shortname = "wacom-ekr", + .setup = litest_wacom_ekr_setup, + .interface = &interface, + + .name = "Wacom Express Key Remote Pad", + .id = &input_id, + .events = events, + .absinfo = absinfo, + .udev_rule = udev_rule, +}; diff --git a/test/litest.c b/test/litest.c index d5e33dc..916677f 100644 --- a/test/litest.c +++ b/test/litest.c @@ -384,6 +384,8 @@ extern struct litest_test_device litest_wacom_intuos3_pad_device; extern struct litest_test_device litest_wacom_intuos5_pad_device; extern struct litest_test_device litest_keyboard_all_codes_device; extern struct litest_test_device litest_magicmouse_device; +extern struct litest_test_device litest_wacom_ekr_device; +extern struct litest_test_device litest_wacom_cintiq_24hdt_pad_device; struct litest_test_device* devices[] = { &litest_synaptics_clickpad_device, @@ -434,6 +436,8 @@ struct litest_test_device* devices[] = { &litest_wacom_intuos5_pad_device, &litest_keyboard_all_codes_device, &litest_magicmouse_device, + &litest_wacom_ekr_device, + &litest_wacom_cintiq_24hdt_pad_device, NULL, }; @@ -2083,6 +2087,9 @@ litest_event_type_str(struct libinput_event *event) case LIBINPUT_EVENT_TABLET_PAD_STRIP: str = "TABLET PAD STRIP"; break; + case LIBINPUT_EVENT_TABLET_PAD_MODE: + str = "TABLET PAD MODE"; + break; } return str; } diff --git a/test/litest.h b/test/litest.h index dd299bb..863a113 100644 --- a/test/litest.h +++ b/test/litest.h @@ -201,6 +201,8 @@ enum litest_device_type { LITEST_WACOM_INTUOS5_PAD, LITEST_KEYBOARD_ALL_CODES, LITEST_MAGICMOUSE, + LITEST_WACOM_EKR, + LITEST_WACOM_CINTIQ_24HDT_PAD, }; enum litest_device_feature { @@ -407,6 +407,230 @@ START_TEST(pad_left_handed_ring) } END_TEST +START_TEST(pad_mode_groups) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + struct libinput_tablet_pad_mode_group *group; + int ngroups; + int i; + + ngroups = libinput_device_tablet_pad_get_num_mode_groups(device); + ck_assert_int_eq(ngroups, 1); + + for (i = 0; i < ngroups; i++) { + group = libinput_device_tablet_pad_get_mode_group(device, i); + ck_assert_notnull(group); + ck_assert_int_eq(libinput_tablet_pad_mode_group_get_index(group), + i); + } + + group = libinput_device_tablet_pad_get_mode_group(device, ngroups); + ck_assert(group == NULL); + group = libinput_device_tablet_pad_get_mode_group(device, ngroups + 1); + ck_assert(group == NULL); +} +END_TEST + +START_TEST(pad_mode_groups_userdata) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + struct libinput_tablet_pad_mode_group *group; + int rc; + void *userdata = &rc; + + group = libinput_device_tablet_pad_get_mode_group(device, 0); + ck_assert(libinput_tablet_pad_mode_group_get_user_data(group) == + NULL); + libinput_tablet_pad_mode_group_set_user_data(group, userdata); + ck_assert(libinput_tablet_pad_mode_group_get_user_data(group) == + &rc); + + libinput_tablet_pad_mode_group_set_user_data(group, NULL); + ck_assert(libinput_tablet_pad_mode_group_get_user_data(group) == + NULL); +} +END_TEST + +START_TEST(pad_mode_groups_ref) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + struct libinput_tablet_pad_mode_group *group, *g; + + group = libinput_device_tablet_pad_get_mode_group(device, 0); + g = libinput_tablet_pad_mode_group_ref(group); + ck_assert_ptr_eq(g, group); + + /* We don't expect this to be freed. Any leaks should be caught by + * valgrind. */ + g = libinput_tablet_pad_mode_group_unref(group); + ck_assert_ptr_eq(g, group); +} +END_TEST + +START_TEST(pad_mode_group_mode) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + struct libinput_tablet_pad_mode_group *group; + int ngroups; + unsigned int nmodes, mode; + + ngroups = libinput_device_tablet_pad_get_num_mode_groups(device); + ck_assert_int_ge(ngroups, 1); + + group = libinput_device_tablet_pad_get_mode_group(device, 0); + + nmodes = libinput_tablet_pad_mode_group_get_num_modes(group); + ck_assert_int_eq(nmodes, 1); + + mode = libinput_tablet_pad_mode_group_get_mode(group); + ck_assert_int_lt(mode, nmodes); +} +END_TEST + +START_TEST(pad_mode_group_has) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + struct libinput_tablet_pad_mode_group *group; + int ngroups, nbuttons, nrings, nstrips; + int i, b, r, s; + + ngroups = libinput_device_tablet_pad_get_num_mode_groups(device); + ck_assert_int_ge(ngroups, 1); + + nbuttons = libinput_device_tablet_pad_get_num_buttons(device); + nrings = libinput_device_tablet_pad_get_num_rings(device); + nstrips = libinput_device_tablet_pad_get_num_strips(device); + + for (b = 0; b < nbuttons; b++) { + bool found = false; + for (i = 0; i < ngroups; i++) { + group = libinput_device_tablet_pad_get_mode_group(device, + i); + if (libinput_tablet_pad_mode_group_has_button(group, + b)) { + ck_assert(!found); + found = true; + } + } + ck_assert(found); + } + + for (s = 0; s < nstrips; s++) { + bool found = false; + for (i = 0; i < ngroups; i++) { + group = libinput_device_tablet_pad_get_mode_group(device, + i); + if (libinput_tablet_pad_mode_group_has_strip(group, + s)) { + ck_assert(!found); + found = true; + } + } + ck_assert(found); + } + + for (r = 0; r < nrings; r++) { + bool found = false; + for (i = 0; i < ngroups; i++) { + group = libinput_device_tablet_pad_get_mode_group(device, + i); + if (libinput_tablet_pad_mode_group_has_ring(group, + r)) { + ck_assert(!found); + found = true; + } + } + ck_assert(found); + } +} +END_TEST + +START_TEST(pad_mode_group_has_invalid) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + struct libinput_tablet_pad_mode_group* group; + int ngroups, nbuttons, nrings, nstrips; + int i; + int rc; + + ngroups = libinput_device_tablet_pad_get_num_mode_groups(device); + ck_assert_int_ge(ngroups, 1); + + nbuttons = libinput_device_tablet_pad_get_num_buttons(device); + nrings = libinput_device_tablet_pad_get_num_rings(device); + nstrips = libinput_device_tablet_pad_get_num_strips(device); + + for (i = 0; i < ngroups; i++) { + group = libinput_device_tablet_pad_get_mode_group(device, i); + rc = libinput_tablet_pad_mode_group_has_button(group, + nbuttons); + ck_assert_int_eq(rc, 0); + rc = libinput_tablet_pad_mode_group_has_button(group, + nbuttons + 1); + ck_assert_int_eq(rc, 0); + rc = libinput_tablet_pad_mode_group_has_button(group, + 0x1000000); + ck_assert_int_eq(rc, 0); + } + + for (i = 0; i < ngroups; i++) { + group = libinput_device_tablet_pad_get_mode_group(device, i); + rc = libinput_tablet_pad_mode_group_has_strip(group, + nstrips); + ck_assert_int_eq(rc, 0); + rc = libinput_tablet_pad_mode_group_has_strip(group, + nstrips + 1); + ck_assert_int_eq(rc, 0); + rc = libinput_tablet_pad_mode_group_has_strip(group, + 0x1000000); + ck_assert_int_eq(rc, 0); + } + + for (i = 0; i < ngroups; i++) { + group = libinput_device_tablet_pad_get_mode_group(device, i); + rc = libinput_tablet_pad_mode_group_has_ring(group, + nrings); + ck_assert_int_eq(rc, 0); + rc = libinput_tablet_pad_mode_group_has_ring(group, + nrings + 1); + ck_assert_int_eq(rc, 0); + rc = libinput_tablet_pad_mode_group_has_ring(group, + 0x1000000); + ck_assert_int_eq(rc, 0); + } +} +END_TEST + +START_TEST(pad_mode_group_has_no_toggle) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + struct libinput_tablet_pad_mode_group* group; + int ngroups, nbuttons; + int i, b; + + ngroups = libinput_device_tablet_pad_get_num_mode_groups(device); + ck_assert_int_ge(ngroups, 1); + + /* Button must not be toggle buttons */ + nbuttons = libinput_device_tablet_pad_get_num_buttons(device); + for (i = 0; i < ngroups; i++) { + group = libinput_device_tablet_pad_get_mode_group(device, i); + for (b = 0; b < nbuttons; b++) { + ck_assert(!libinput_tablet_pad_mode_group_button_is_toggle( + group, + b)); + } + } +} +END_TEST + void litest_setup_tests(void) { @@ -428,4 +652,12 @@ litest_setup_tests(void) 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 */ + + litest_add("pad:modes", pad_mode_groups, LITEST_TABLET_PAD, LITEST_ANY); + litest_add("pad:modes", pad_mode_groups_userdata, LITEST_TABLET_PAD, LITEST_ANY); + litest_add("pad:modes", pad_mode_groups_ref, LITEST_TABLET_PAD, LITEST_ANY); + litest_add("pad:modes", pad_mode_group_mode, LITEST_TABLET_PAD, LITEST_ANY); + litest_add("pad:modes", pad_mode_group_has, LITEST_TABLET_PAD, LITEST_ANY); + litest_add("pad:modes", pad_mode_group_has_invalid, LITEST_TABLET_PAD, LITEST_ANY); + litest_add("pad:modes", pad_mode_group_has_no_toggle, LITEST_TABLET_PAD, LITEST_ANY); } diff --git a/tools/event-debug.c b/tools/event-debug.c index a5608d2..70f4268 100644 --- a/tools/event-debug.c +++ b/tools/event-debug.c @@ -130,6 +130,9 @@ print_event_header(struct libinput_event *ev) case LIBINPUT_EVENT_TABLET_PAD_STRIP: type = "TABLET_PAD_STRIP"; break; + case LIBINPUT_EVENT_TABLET_PAD_MODE: + type = "TABLET_PAD_MODE"; + break; } printf("%-7s %-16s ", libinput_device_get_sysname(dev), type); @@ -232,16 +235,18 @@ print_device_notify(struct libinput_event *ev) if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TABLET_PAD)) { - int nbuttons, nstrips, nrings; + int nbuttons, nstrips, nrings, ngroups; nbuttons = libinput_device_tablet_pad_get_num_buttons(dev); nstrips = libinput_device_tablet_pad_get_num_strips(dev); nrings = libinput_device_tablet_pad_get_num_rings(dev); + ngroups = libinput_device_tablet_pad_get_num_mode_groups(dev); - printf(" buttons:%d strips:%d rings:%d", + printf(" buttons:%d strips:%d rings:%d mode groups:%d", nbuttons, nstrips, - nrings); + nrings, + ngroups); } printf("\n"); @@ -604,14 +609,25 @@ static void print_tablet_pad_button_event(struct libinput_event *ev) { struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev); + struct libinput_tablet_pad_mode_group *group; enum libinput_button_state state; + unsigned int button, mode; print_event_time(libinput_event_tablet_pad_get_time(p)); + button = libinput_event_tablet_pad_get_button_number(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"); + mode = libinput_event_tablet_pad_get_mode(p); + printf("%3d %s (mode %d)", + button, + state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released", + mode); + + group = libinput_event_tablet_pad_get_mode_group(p); + if (libinput_tablet_pad_mode_group_button_is_toggle(group, button)) + printf(" <mode toggle>"); + + printf("\n"); } static void @@ -619,6 +635,7 @@ 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>"; + unsigned int mode; print_event_time(libinput_event_tablet_pad_get_time(p)); @@ -631,10 +648,12 @@ print_tablet_pad_ring_event(struct libinput_event *ev) break; } - printf("ring %d position %.2f (source %s)\n", + mode = libinput_event_tablet_pad_get_mode(p); + printf("ring %d position %.2f (source %s) (mode %d)\n", libinput_event_tablet_pad_get_ring_number(p), libinput_event_tablet_pad_get_ring_position(p), - source); + source, + mode); } static void @@ -642,6 +661,7 @@ 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>"; + unsigned int mode; print_event_time(libinput_event_tablet_pad_get_time(p)); @@ -654,10 +674,28 @@ print_tablet_pad_strip_event(struct libinput_event *ev) break; } - printf("strip %d position %.2f (source %s)\n", + mode = libinput_event_tablet_pad_get_mode(p); + printf("strip %d position %.2f (source %s) (mode %d)\n", libinput_event_tablet_pad_get_strip_number(p), libinput_event_tablet_pad_get_strip_position(p), - source); + source, + mode); +} + +static void +print_tablet_pad_mode_event(struct libinput_event *ev) +{ + struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev); + struct libinput_tablet_pad_mode_group *group; + unsigned int mode; + + print_event_time(libinput_event_tablet_pad_get_time(p)); + + group = libinput_event_tablet_pad_get_mode_group(p); + mode = libinput_event_tablet_pad_get_mode(p); + printf("group %d mode %d\n", + libinput_tablet_pad_mode_group_get_index(group), + mode); } static int @@ -748,6 +786,9 @@ handle_and_print_events(struct libinput *li) case LIBINPUT_EVENT_TABLET_PAD_STRIP: print_tablet_pad_strip_event(ev); break; + case LIBINPUT_EVENT_TABLET_PAD_MODE: + print_tablet_pad_mode_event(ev); + break; } libinput_event_destroy(ev); diff --git a/tools/event-gui.c b/tools/event-gui.c index b67ca45..8b4e3e9 100644 --- a/tools/event-gui.c +++ b/tools/event-gui.c @@ -843,6 +843,7 @@ handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data) case LIBINPUT_EVENT_TABLET_PAD_BUTTON: case LIBINPUT_EVENT_TABLET_PAD_RING: case LIBINPUT_EVENT_TABLET_PAD_STRIP: + case LIBINPUT_EVENT_TABLET_PAD_MODE: break; } |