mirror of
https://github.com/libratbag/piper.git
synced 2026-05-19 04:16:08 -04:00
ButtonDialog: add key mapping stack page
ratbag's key mapping signature is forcing us to treat modifiers different from regular keys. To detect them, we need several workarounds over Gdk.EventKey which makes the code uglier than it could be. Perhaps we should just skip this altogether and implement macros only; since they don't differentiate between modifiers and regular keys all these workaround can then be removed while providing the same functionality.
This commit is contained in:
committed by
Peter Hutterer
parent
0773bbcbca
commit
847d8fcf3a
245
data/enter-keyboard-shortcut.svg
Normal file
245
data/enter-keyboard-shortcut.svg
Normal file
@@ -0,0 +1,245 @@
|
||||
<?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="256"
|
||||
height="72"
|
||||
viewBox="0 0 256 72.000001"
|
||||
id="svg3611"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="enter-keyboard-shortcut.svg">
|
||||
<defs
|
||||
id="defs3613" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.8"
|
||||
inkscape:cx="137.98997"
|
||||
inkscape:cy="34.663602"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="704"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata3616">
|
||||
<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="Camada 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-980.36216)">
|
||||
<g
|
||||
id="g3715"
|
||||
transform="translate(-503.23415,689.94658)">
|
||||
<path
|
||||
d="m 509.66363,325.47627 c 5.53002,1.4185 18.51389,1.4185 24.29359,0 0.80721,-0.19813 1.43306,0.67185 1.50029,1.50028 l 1.99751,13.05863 -1.50028,0 -28.28862,0 -1.50029,0 c 0.66583,-4.35287 1.33167,-8.70575 1.99752,-13.05863 0.12564,-0.82161 0.69518,-1.70681 1.50028,-1.50028 z"
|
||||
id="path27275"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 543.62146,325.47627 c 5.53,1.4185 18.51387,1.4185 24.29357,0 0.80721,-0.19813 1.43308,0.67185 1.50029,1.50028 l 1.99753,13.05863 -1.5003,0 -28.28862,0 -1.50029,0 c 0.66583,-4.35287 1.33168,-8.70575 1.99752,-13.05863 0.12564,-0.82161 0.69518,-1.70681 1.5003,-1.50028 z"
|
||||
id="path27277"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 577.57927,325.47627 c 5.53,1.4185 18.51387,1.4185 24.29357,0 0.80721,-0.19813 1.43308,0.67185 1.50029,1.50028 l 1.99753,13.05863 -1.5003,0 -28.28862,0 -1.50029,0 c 0.66583,-4.35287 1.33168,-8.70575 1.99752,-13.05863 0.12564,-0.82161 0.6952,-1.70681 1.5003,-1.50028 z"
|
||||
id="path27279"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 611.12326,325.47627 c 5.53002,1.4185 18.51389,1.4185 24.29359,0 0.80721,-0.19813 1.43306,0.67185 1.50029,1.50028 l 1.99751,13.05863 -1.50028,0 -28.28862,0 -1.50029,0 c 0.66583,-4.35287 1.33167,-8.70575 1.99752,-13.05863 0.12564,-0.82161 0.69518,-1.70681 1.50028,-1.50028 z"
|
||||
id="path5218"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 645.08109,325.47627 c 5.53,1.4185 18.51387,1.4185 24.29357,0 0.80721,-0.19813 1.43308,0.67185 1.50029,1.50028 l 1.99753,13.05863 -1.5003,0 -28.28862,0 -1.50029,0 c 0.66583,-4.35287 1.33168,-8.70575 1.99751,-13.05863 0.12565,-0.82161 0.69519,-1.70681 1.50031,-1.50028 z"
|
||||
id="path5220"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 679.0389,325.47627 c 5.53,1.4185 18.51387,1.4185 24.29357,0 0.80721,-0.19813 1.43308,0.67185 1.50029,1.50028 l 1.99753,13.05863 -1.5003,0 -28.28862,0 -1.50029,0 c 0.66583,-4.35287 1.33168,-8.70575 1.99751,-13.05863 0.12565,-0.82161 0.69521,-1.70681 1.50031,-1.50028 z"
|
||||
id="path5222"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 712.58289,325.47627 c 5.53002,1.4185 18.51389,1.4185 24.29359,0 0.80721,-0.19813 1.43306,0.67185 1.50029,1.50028 l 1.99751,13.05863 -1.50028,0 -28.28862,0 -1.50029,0 c 0.66583,-4.35287 1.33167,-8.70575 1.99751,-13.05863 0.12565,-0.82161 0.69519,-1.70681 1.50029,-1.50028 z"
|
||||
id="path4829"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.60000002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 516.19044,335.26648 c 5.53002,1.41851 18.51389,1.41851 24.2936,0 0.8072,-0.19812 1.43306,0.67186 1.50028,1.50029 l 1.99752,13.05863 -1.50029,0 -28.28862,0 -1.50029,0 c 0.66584,-4.35288 1.33167,-8.70575 1.99752,-13.05863 0.12564,-0.82161 0.69518,-1.70681 1.50028,-1.50029 z"
|
||||
id="path3662"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 550.14827,335.26648 c 5.53,1.41851 18.51387,1.41851 24.29358,0 0.8072,-0.19812 1.43307,0.67186 1.50028,1.50029 l 1.99753,13.05863 -1.5003,0 -28.28862,0 -1.50029,0 c 0.66584,-4.35288 1.33168,-8.70575 1.99752,-13.05863 0.12564,-0.82161 0.69518,-1.70681 1.5003,-1.50029 z"
|
||||
id="path3664"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 584.10608,335.26648 c 5.53,1.41851 18.51387,1.41851 24.29357,0 0.80721,-0.19812 1.43308,0.67186 1.50029,1.50029 l 1.99753,13.05863 -1.5003,0 -28.28862,0 -1.50029,0 c 0.66584,-4.35288 1.33168,-8.70575 1.99752,-13.05863 0.12564,-0.82161 0.6952,-1.70681 1.5003,-1.50029 z"
|
||||
id="path3666"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 617.65007,335.26648 c 5.53002,1.41851 18.51389,1.41851 24.29359,0 0.80721,-0.19812 1.43306,0.67186 1.50029,1.50029 l 1.99751,13.05863 -1.50028,0 -28.28862,0 -1.50029,0 c 0.66584,-4.35288 1.33167,-8.70575 1.99752,-13.05863 0.12564,-0.82161 0.69518,-1.70681 1.50028,-1.50029 z"
|
||||
id="path3668"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 651.6079,335.26648 c 5.53,1.41851 18.51387,1.41851 24.29357,0 0.80721,-0.19812 1.43308,0.67186 1.50029,1.50029 l 1.99753,13.05863 -1.5003,0 -28.28862,0 -1.50029,0 c 0.66583,-4.35288 1.33168,-8.70575 1.99752,-13.05863 0.12564,-0.82161 0.69518,-1.70681 1.5003,-1.50029 z"
|
||||
id="path3670"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 685.56571,335.26648 c 5.53,1.41851 18.51387,1.41851 24.29357,0 0.80721,-0.19812 1.43308,0.67186 1.50029,1.50029 l 1.99753,13.05863 -1.5003,0 -28.28862,0 -1.50029,0 c 0.66583,-4.35288 1.33168,-8.70575 1.99752,-13.05863 0.12564,-0.82161 0.6952,-1.70681 1.5003,-1.50029 z"
|
||||
id="path3672"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 719.1097,335.26648 c 5.53002,1.41851 18.51389,1.41851 24.29359,0 0.80721,-0.19812 1.43306,0.67186 1.50029,1.50029 l 1.99751,13.05863 -1.50028,0 -28.28862,0 -1.50029,0 c 0.66583,-4.35288 1.33167,-8.70575 1.99752,-13.05863 0.12564,-0.82161 0.69518,-1.70681 1.50028,-1.50029 z"
|
||||
id="path3674"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.60000002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 525.98066,345.0567 c 5.53002,1.4185 18.51389,1.4185 24.29359,0 0.80721,-0.19812 1.43306,0.67185 1.50029,1.50029 l 1.99751,13.05863 -1.50028,0 -28.28862,0 -1.50029,0 c 0.66583,-4.35288 1.33167,-8.70576 1.99752,-13.05863 0.12564,-0.82162 0.69518,-1.70681 1.50028,-1.50029 z"
|
||||
id="path3676"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 559.93849,345.0567 c 5.53,1.4185 18.51387,1.4185 24.29357,0 0.80721,-0.19812 1.43308,0.67185 1.50029,1.50029 l 1.99753,13.05863 -1.5003,0 -28.28862,0 -1.50029,0 c 0.66583,-4.35288 1.33168,-8.70576 1.99751,-13.05863 0.12565,-0.82162 0.69519,-1.70681 1.50031,-1.50029 z"
|
||||
id="path3678"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 593.8963,345.0567 c 5.53,1.4185 18.51387,1.4185 24.29357,0 0.80721,-0.19812 1.43308,0.67185 1.50029,1.50029 l 1.99753,13.05863 -1.5003,0 -28.28862,0 -1.50029,0 c 0.66583,-4.35288 1.33168,-8.70576 1.99751,-13.05863 0.12565,-0.82162 0.69521,-1.70681 1.50031,-1.50029 z"
|
||||
id="path3680"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 627.44029,345.0567 c 5.53002,1.4185 18.51389,1.4185 24.29359,0 0.80721,-0.19812 1.43306,0.67185 1.50029,1.50029 l 1.99751,13.05863 -1.50028,0 -28.28862,0 -1.50029,0 c 0.66583,-4.35288 1.33167,-8.70576 1.99751,-13.05863 0.12565,-0.82162 0.69519,-1.70681 1.50029,-1.50029 z"
|
||||
id="path3682"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 661.39812,345.0567 c 5.53,1.4185 18.51387,1.4185 24.29357,0 0.80721,-0.19812 1.43308,0.67185 1.50029,1.50029 l 1.99753,13.05863 -1.5003,0 -28.28863,0 -1.50028,0 c 0.66583,-4.35288 1.33168,-8.70576 1.99751,-13.05863 0.12564,-0.82162 0.69519,-1.70681 1.50031,-1.50029 z"
|
||||
id="path3684"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 695.35593,345.0567 c 5.53,1.4185 18.51387,1.4185 24.29357,0 0.8072,-0.19812 1.43308,0.67185 1.50029,1.50029 l 1.99753,13.05863 -1.5003,0 -28.28863,0 -1.50028,0 c 0.66583,-4.35288 1.33168,-8.70576 1.99751,-13.05863 0.12564,-0.82162 0.69521,-1.70681 1.50031,-1.50029 z"
|
||||
id="path3686"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.59999979;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 728.89992,345.0567 c 5.53002,1.4185 18.51389,1.4185 24.29359,0 0.8072,-0.19812 1.43306,0.67185 1.50029,1.50029 l 1.99751,13.05863 -1.50028,0 -28.28863,0 -1.50028,0 c 0.66583,-4.35288 1.33166,-8.70576 1.99751,-13.05863 0.12564,-0.82162 0.69519,-1.70681 1.50029,-1.50029 z"
|
||||
id="path3688"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.60000002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="sssccccss"
|
||||
inkscape:connector-curvature="0" />
|
||||
<g
|
||||
id="g3713"
|
||||
transform="matrix(1.359752,0,0,1.359752,418.09336,-671.08525)">
|
||||
<path
|
||||
d="m 95.250257,715.10933 0,1.09089 c -1.31e-4,0.0113 -5.02e-4,0.0227 0,0.0341 0.01222,0.27812 0.140266,0.55621 0.340902,0.74999 l 5.693061,5.76124 5.65897,-5.76124 c 0.20529,-0.20532 0.30681,-0.49473 0.30681,-0.78413 l 0,-1.09089 -1.09088,0 c -0.28941,0 -0.57881,0.10156 -0.78408,0.30681 l -4.09082,4.15901 -4.124913,-4.15901 c -0.212319,-0.22989 -0.511898,-0.33071 -0.818164,-0.30681 l -1.090886,0 z"
|
||||
id="path3715"
|
||||
style="color:#bebebe;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:'Andale Mono';-inkscape-font-specification:'Andale Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#000100;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.78124988;marker:none;enable-background:new"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
height="11.999745"
|
||||
id="rect3717"
|
||||
rx="0"
|
||||
ry="0"
|
||||
style="color:#bebebe;display:inline;overflow:visible;visibility:visible;fill:#000100;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;enable-background:accumulate"
|
||||
transform="scale(-1,1)"
|
||||
width="2.1817718"
|
||||
x="-102.34102"
|
||||
y="708.92743" />
|
||||
</g>
|
||||
<g
|
||||
id="g3740"
|
||||
transform="matrix(1.359752,0,0,1.359752,492.12198,-661.29504)">
|
||||
<path
|
||||
d="m 95.250257,715.10933 0,1.09089 c -1.31e-4,0.0113 -5.02e-4,0.0227 0,0.0341 0.01222,0.27812 0.140266,0.55621 0.340902,0.74999 l 5.693061,5.76124 5.65897,-5.76124 c 0.20529,-0.20532 0.30681,-0.49473 0.30681,-0.78413 l 0,-1.09089 -1.09088,0 c -0.28941,0 -0.57881,0.10156 -0.78408,0.30681 l -4.09082,4.15901 -4.124913,-4.15901 c -0.212319,-0.22989 -0.511898,-0.33071 -0.818164,-0.30681 l -1.090886,0 z"
|
||||
id="path3742"
|
||||
style="color:#bebebe;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:'Andale Mono';-inkscape-font-specification:'Andale Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#000100;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.78124988;marker:none;enable-background:new"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
height="11.999745"
|
||||
id="rect3744"
|
||||
rx="0"
|
||||
ry="0"
|
||||
style="color:#bebebe;display:inline;overflow:visible;visibility:visible;fill:#000100;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;enable-background:accumulate"
|
||||
transform="scale(-1,1)"
|
||||
width="2.1817718"
|
||||
x="-102.34102"
|
||||
y="708.92743" />
|
||||
</g>
|
||||
<g
|
||||
id="g3746"
|
||||
transform="matrix(1.359752,0,0,1.359752,593.58161,-661.29504)">
|
||||
<path
|
||||
d="m 95.250257,715.10933 0,1.09089 c -1.31e-4,0.0113 -5.02e-4,0.0227 0,0.0341 0.01222,0.27812 0.140266,0.55621 0.340902,0.74999 l 5.693061,5.76124 5.65897,-5.76124 c 0.20529,-0.20532 0.30681,-0.49473 0.30681,-0.78413 l 0,-1.09089 -1.09088,0 c -0.28941,0 -0.57881,0.10156 -0.78408,0.30681 l -4.09082,4.15901 -4.124913,-4.15901 c -0.212319,-0.22989 -0.511898,-0.33071 -0.818164,-0.30681 l -1.090886,0 z"
|
||||
id="path3748"
|
||||
style="color:#bebebe;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:'Andale Mono';-inkscape-font-specification:'Andale Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#000100;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.78124988;marker:none;enable-background:new"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
height="11.999745"
|
||||
id="rect3750"
|
||||
rx="0"
|
||||
ry="0"
|
||||
style="color:#bebebe;display:inline;overflow:visible;visibility:visible;fill:#000100;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;enable-background:accumulate"
|
||||
transform="scale(-1,1)"
|
||||
width="2.1817718"
|
||||
x="-102.34102"
|
||||
y="708.92743" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 18 KiB |
@@ -2,6 +2,7 @@
|
||||
<gresources>
|
||||
<gresource prefix="/org/freedesktop/Piper">
|
||||
<file>404.svg</file>
|
||||
<file>enter-keyboard-shortcut.svg</file>
|
||||
|
||||
<file preprocess="xml-stripblanks">AboutDialog.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/ButtonDialog.ui</file>
|
||||
|
||||
@@ -41,77 +41,205 @@
|
||||
<property name="can_focus">False</property>
|
||||
<property name="transition_type">slide-left-right</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<object class="GtkStack" id="stack_mapping">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Button- and key mappings allow you to assign to a physical mouse button another
|
||||
logical mouse button or a sequence of keypresses. This way you may for example
|
||||
make the right mouse button perform a left click, or execute the keystroke Ctrl+R with
|
||||
a click on the right mouse button.</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="track_visited_links">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="combo_mapping">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Assign another logical mouse button to this button</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="has_entry">True</property>
|
||||
<signal name="changed" handler="_on_mapping_changed" swapped="no"/>
|
||||
<child internal-child="entry">
|
||||
<object class="GtkEntry">
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Assign a button mapping:</property>
|
||||
<property name="label" translatable="yes">Button- and key mappings allow you to assign to a physical mouse button another
|
||||
logical mouse button or a sequence of keypresses. This way you may for example
|
||||
make the right mouse button perform a left click, or execute the keystroke Ctrl+R with
|
||||
a click on the right mouse button.</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="track_visited_links">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="combo_mapping">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Assign another logical mouse button to this button</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="has_entry">True</property>
|
||||
<signal name="changed" handler="_on_mapping_changed" swapped="no"/>
|
||||
<child internal-child="entry">
|
||||
<object class="GtkEntry">
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Assign a button mapping:</property>
|
||||
<property name="track_visited_links">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Assign a keystroke to this button</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="relief">none</property>
|
||||
<signal name="clicked" handler="_on_capture_keystroke_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkShortcutLabel" id="label_keystroke">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="disabled-text" translatable="yes">None</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Assign a key stroke:</property>
|
||||
<property name="track_visited_links">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="name">overview</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">18</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Enter a new key sequence for the mapping, starting with zero or more modifiers and finishing with one regular key.</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="wrap_mode">word-char</property>
|
||||
<property name="width_chars">15</property>
|
||||
<property name="max_width_chars">20</property>
|
||||
<property name="track_visited_links">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkShortcutLabel" id="label_preview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="hexpand">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="resource">/org/freedesktop/Piper/enter-keyboard-shortcut.svg</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Press Backspace to reset the key mapping.</property>
|
||||
<property name="wrap">True</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">capture</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
|
||||
@@ -14,14 +14,17 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import sys
|
||||
|
||||
from gettext import gettext as _
|
||||
|
||||
from .gi_composites import GtkTemplate
|
||||
from .ratbagd import RatbagdButton
|
||||
from .keystroke import KeyStroke
|
||||
|
||||
import gi
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import GObject, Gtk
|
||||
from gi.repository import Gdk, GObject, Gtk
|
||||
|
||||
|
||||
@GtkTemplate(ui="/org/freedesktop/Piper/ui/ButtonDialog.ui")
|
||||
@@ -38,8 +41,27 @@ class ButtonDialog(Gtk.Dialog):
|
||||
RatbagdButton.ACTION_TYPE_MACRO: "macro",
|
||||
}
|
||||
|
||||
_MODIFIERS = [
|
||||
Gdk.KEY_Shift_L,
|
||||
Gdk.KEY_Shift_R,
|
||||
Gdk.KEY_Shift_Lock,
|
||||
Gdk.KEY_Hyper_L,
|
||||
Gdk.KEY_Hyper_R,
|
||||
Gdk.KEY_Meta_L,
|
||||
Gdk.KEY_Meta_R,
|
||||
Gdk.KEY_Control_L,
|
||||
Gdk.KEY_Control_R,
|
||||
Gdk.KEY_Super_L,
|
||||
Gdk.KEY_Super_R,
|
||||
Gdk.KEY_Alt_L,
|
||||
Gdk.KEY_Alt_R,
|
||||
]
|
||||
|
||||
stack = GtkTemplate.Child()
|
||||
combo_mapping = GtkTemplate.Child()
|
||||
stack_mapping = GtkTemplate.Child()
|
||||
label_keystroke = GtkTemplate.Child()
|
||||
label_preview = GtkTemplate.Child()
|
||||
|
||||
def __init__(self, ratbagd_button, buttons, *args, **kwargs):
|
||||
"""Instantiates a new ButtonDialog.
|
||||
@@ -49,9 +71,12 @@ class ButtonDialog(Gtk.Dialog):
|
||||
"""
|
||||
Gtk.Dialog.__init__(self, *args, **kwargs)
|
||||
self.init_template()
|
||||
self._grab_pointer = None
|
||||
self._keystroke = KeyStroke()
|
||||
self._button = ratbagd_button
|
||||
self._action_type = self._button.action_type
|
||||
self._button_mapping = ratbagd_button.mapping
|
||||
self._key_mapping = ratbagd_button.key
|
||||
|
||||
self._init_mapping_page(buttons)
|
||||
self._activate_current_page()
|
||||
@@ -75,6 +100,14 @@ class ButtonDialog(Gtk.Dialog):
|
||||
if self._button_mapping > 0 and button == self._button:
|
||||
self.combo_mapping.set_active_id(key)
|
||||
|
||||
self._keystroke.connect("keystroke-set", self._on_keystroke_set)
|
||||
self._keystroke.connect("keystroke-cleared", self._on_keystroke_set)
|
||||
self._keystroke.bind_property("accelerator", self.label_keystroke, "accelerator")
|
||||
self._keystroke.bind_property("accelerator", self.label_preview, "accelerator")
|
||||
if self._button.type == RatbagdButton.ACTION_TYPE_KEY:
|
||||
keys = self._button.key
|
||||
self._keystroke.set_from_evdev(keys[0], keys[1:])
|
||||
|
||||
def _get_button_key_and_name(self, button):
|
||||
if button.index in RatbagdButton.BUTTON_DESCRIPTION:
|
||||
name = RatbagdButton.BUTTON_DESCRIPTION[button.index]
|
||||
@@ -82,6 +115,95 @@ class ButtonDialog(Gtk.Dialog):
|
||||
name = _("Button {} click").format(button.index)
|
||||
return str(button.index + 1), name # Logical buttons are 1-indexed.
|
||||
|
||||
def _grab_seat(self):
|
||||
# Grabs the keyboard seat. Returns True on success, False on failure.
|
||||
# Gratefully copied from GNOME Control Center's keyboard panel.
|
||||
window = self.get_window()
|
||||
if window is None:
|
||||
return False
|
||||
display = window.get_display()
|
||||
seats = display.list_seats()
|
||||
if len(seats) == 0:
|
||||
return False
|
||||
device = seats[0].get_keyboard()
|
||||
if device is None:
|
||||
return False
|
||||
if device.get_source == Gdk.InputSource.KEYBOARD:
|
||||
pointer = device.get_associated_device()
|
||||
if pointer is None:
|
||||
return False
|
||||
else:
|
||||
pointer = device
|
||||
status = pointer.get_seat().grab(window, Gdk.SeatCapabilities.KEYBOARD,
|
||||
False, None, None, None, None)
|
||||
if status != Gdk.GrabStatus.SUCCESS:
|
||||
return False
|
||||
self._grab_pointer = pointer
|
||||
self.grab_add()
|
||||
return True
|
||||
|
||||
def _release_grab(self):
|
||||
# Releases a previously grabbed keyboard seat, if any.
|
||||
if self._grab_pointer is None:
|
||||
return
|
||||
self._grab_pointer.get_seat().ungrab()
|
||||
self._grab_pointer = None
|
||||
self.grab_remove()
|
||||
|
||||
def do_key_press_event(self, event):
|
||||
# Overrides Gtk.Widget's standard key press event callback, so we can
|
||||
# capture the pressed buttons in capture mode. Gratefully copied from
|
||||
# GNOME Control Center's keyboard panel.
|
||||
# Don't process key events when we're not in capture mode.
|
||||
if self.stack_mapping.get_visible_child_name() == "overview":
|
||||
return Gtk.Widget.do_key_press_event(self, event)
|
||||
|
||||
# TODO: remove this workaround when libratbag removes its keycode
|
||||
# contraints. When that happens, we just cache all keypresses in the
|
||||
# order they arrive and set the keystroke upon Return.
|
||||
# GdkEventKey.is_modified isn't exposed through PyGObject (see
|
||||
# https://bugzilla.gnome.org/show_bug.cgi?id=752784), so we have to
|
||||
# approximate its behaviour ourselves. This selection is from Gtk's
|
||||
# default mod mask and should be fine for now for most use cases.
|
||||
event.is_modifier = event.keyval in self._MODIFIERS
|
||||
|
||||
# We only want to bind keystrokes using the default modifiers, so that
|
||||
# our workaround above and the one in KeyStroke._update_accelerator()
|
||||
# work.
|
||||
event.state &= Gtk.accelerator_get_default_mod_mask()
|
||||
|
||||
# Put shift back if it changed the case of the key, not otherwise.
|
||||
keyval_lower = Gdk.keyval_to_lower(event.keyval)
|
||||
if keyval_lower != event.keyval:
|
||||
event.state |= Gdk.ModifierType.SHIFT_MASK
|
||||
event.keyval = keyval_lower
|
||||
|
||||
# Normalize tab.
|
||||
if event.keyval == Gdk.KEY_ISO_Left_Tab:
|
||||
event.keyval = Gdk.KEY_Tab
|
||||
|
||||
# HACK: we don't want to use SysRq as a keybinding, but we do want
|
||||
# Al+Print, so we avoid translating Alt+Print to SysRq.
|
||||
if event.keyval == Gdk.KEY_Sys_Req and (event.state & Gdk.ModifierType.MOD1_MASK):
|
||||
event.keyval = Gdk.KEY_Print
|
||||
|
||||
# Backspace clears the current keystroke.
|
||||
if not event.is_modifier and event.state == 0 and event.keyval == Gdk.KEY_BackSpace:
|
||||
self._keystroke.clear()
|
||||
return Gdk.EVENT_STOP
|
||||
|
||||
# Anything else we process as a regular key event.
|
||||
self._keystroke.process_event(event)
|
||||
|
||||
return Gdk.EVENT_STOP
|
||||
|
||||
def _on_keystroke_set(self, keystroke):
|
||||
# A keystroke has been set or cleared; update accordingly.
|
||||
self._action_type = RatbagdButton.ACTION_TYPE_KEY
|
||||
self._key_mapping = self._keystroke.get_keys()
|
||||
self.stack_mapping.set_visible_child_name("overview")
|
||||
self._release_grab()
|
||||
|
||||
@GtkTemplate.Callback
|
||||
def _on_mapping_changed(self, combo):
|
||||
tree_iter = combo.get_active_iter()
|
||||
@@ -91,6 +213,16 @@ class ButtonDialog(Gtk.Dialog):
|
||||
mapping = int(model[tree_iter][1])
|
||||
if mapping != self._button_mapping:
|
||||
self._button_mapping = mapping
|
||||
self._action_type = RatbagdButton.ACTION_TYPE_BUTTON
|
||||
|
||||
@GtkTemplate.Callback
|
||||
def _on_capture_keystroke_clicked(self, button):
|
||||
# Switches to the capture stack page and grabs the keyboard seat to
|
||||
# capture all key presses.
|
||||
self.stack_mapping.set_visible_child_name("capture")
|
||||
if self._grab_seat() is not True:
|
||||
print("Unable to grab keyboard, can't set keystroke", file=sys.stderr)
|
||||
self.stack_mapping.set_visible_child_name("overview")
|
||||
|
||||
@GObject.Property
|
||||
def action_type(self):
|
||||
@@ -99,3 +231,7 @@ class ButtonDialog(Gtk.Dialog):
|
||||
@GObject.Property
|
||||
def button_mapping(self):
|
||||
return self._button_mapping
|
||||
|
||||
@GObject.Property
|
||||
def key_mapping(self):
|
||||
return self._key_mapping
|
||||
|
||||
@@ -70,6 +70,8 @@ class ButtonsPage(Gtk.Box):
|
||||
if response == Gtk.ResponseType.APPLY:
|
||||
if dialog.action_type == RatbagdButton.ACTION_TYPE_BUTTON:
|
||||
ratbagd_button.mapping = dialog.button_mapping
|
||||
elif dialog.action_type == RatbagdButton.ACTION_TYPE_KEY:
|
||||
ratbagd_button.key = dialog.key_mapping
|
||||
dialog.destroy()
|
||||
|
||||
def _find_active_profile(self):
|
||||
|
||||
172
piper/keystroke.py
Normal file
172
piper/keystroke.py
Normal file
@@ -0,0 +1,172 @@
|
||||
# Copyright (C) 2017 Jente Hidskes <hjdskes@gmail.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import sys
|
||||
|
||||
import gi
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gdk, GObject, Gtk
|
||||
|
||||
|
||||
# TODO: this file needs to be checked for its Wayland support.
|
||||
class KeyStroke(GObject.Object):
|
||||
"""The KeyStroke object represents a keyboard shortcut as pressed by the
|
||||
user in the key mapping's capture mode. Note that internally it uses
|
||||
keycodes (and keyvalues) as defined by Gdk, not evdev. It does have the
|
||||
ability to import from and export as evdev keycodes, as required by
|
||||
libratbagd. See the get_keys() and set_from_evdev() methods."""
|
||||
|
||||
# Gdk uses an offset of 8 from the keycodes defined in linux/input.h as
|
||||
# used by evdev.
|
||||
_XORG_KEYCODE_OFFSET = 8
|
||||
|
||||
__gsignals__ = {
|
||||
'keystroke-set': (GObject.SIGNAL_RUN_FIRST, None, ()),
|
||||
'keystroke-cleared': (GObject.SIGNAL_RUN_FIRST, None, ()),
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""Intantiates a new KeyStroke."""
|
||||
GObject.Object.__init__(self, **kwargs)
|
||||
self._accelerator = ""
|
||||
self._keycode = 0
|
||||
self._keyval = 0
|
||||
self._modifiers = []
|
||||
|
||||
@GObject.Property(type=str)
|
||||
def accelerator(self):
|
||||
"""The accelerator of the current keystroke, as generated by
|
||||
Gtk.accelerator_name(keyval, mask)."""
|
||||
return self._accelerator
|
||||
|
||||
def process_event(self, event):
|
||||
"""Processes the given key event to update the current keystroke.
|
||||
|
||||
@param event The event to process, as Gdk.EventKey.
|
||||
"""
|
||||
if not self._check_key(event.hardware_keycode, event.keyval):
|
||||
return
|
||||
if event.is_modifier:
|
||||
if (event.hardware_keycode, event.keyval) not in self._modifiers:
|
||||
self._modifiers.append((event.hardware_keycode, event.keyval))
|
||||
else:
|
||||
self._keycode = event.hardware_keycode
|
||||
self._keyval = event.keyval
|
||||
# Since keystrokes may only contain a single regular keycode, we
|
||||
# accept as soon as the user enters one. TODO: we should assign the
|
||||
# modifiers and keycode+keyvalue to self._cur_* so that we can add
|
||||
# cancel functionality to the button dialog on Escape.
|
||||
self.emit("keystroke-set")
|
||||
self._update_accelerator()
|
||||
|
||||
def clear(self):
|
||||
"""Clears the current keystroke."""
|
||||
self._keycode = 0
|
||||
self._keyval = 0
|
||||
self._modifiers = []
|
||||
self._update_accelerator()
|
||||
self.emit("keystroke-cleared")
|
||||
|
||||
def set_from_evdev(self, keycode, modifiers):
|
||||
"""Converts the given evdev keycodes to their Gdk counterparts and sets
|
||||
them as the current keystroke.
|
||||
|
||||
@param keycode The evdev keycode, as int.
|
||||
@param modifiers A list of modifier keycodes, as [int].
|
||||
"""
|
||||
keycode += self._XORG_KEYCODE_OFFSET
|
||||
keyval = self._keycode_to_keyval(keycode)
|
||||
if not self._check_key(keycode, keyval):
|
||||
return
|
||||
self._keycode = keycode
|
||||
self._keyval = keyval
|
||||
for modifier in modifiers:
|
||||
keycode = modifier + self._XORG_KEYCODE_OFFSET
|
||||
keyval = self._keycode_to_keyval(keycode)
|
||||
if not self._check_key(keycode, keyval):
|
||||
continue
|
||||
if (keycode, keyval) not in self._modifiers:
|
||||
self._modifiers.append((keycode, keyval))
|
||||
self._update_accelerator()
|
||||
|
||||
def _keycode_to_keyval(self, keycode):
|
||||
# Attempts to retrieve a Gdk keyval belonging to a Gdk keycode. In case
|
||||
# none can be found, 0 is returned. Note that there is some inaccuracy
|
||||
# here in that the keycode can correspond to several keyvals (due to
|
||||
# levels and groups) and that we return the keyval belonging to level
|
||||
# and group 0, if any.
|
||||
keymap = Gdk.Keymap.get_default()
|
||||
ok, keys, keyvals = keymap.get_entries_for_keycode(keycode)
|
||||
if ok:
|
||||
for key, keyval in zip(keys, keyvals):
|
||||
if key.level == 0 and key.group == 0:
|
||||
return keyval
|
||||
print("Could not translate keycode to keyval, using 0", file=sys.stderr)
|
||||
# This default will be skipped when printing the accelerator, and won't
|
||||
# pass self._check_key in which case it will be skipped altogether.
|
||||
return 0
|
||||
|
||||
def get_keys(self):
|
||||
"""Returns a list of keycodes with the first being the pressed key and
|
||||
the rest modifiers, if any. These keycodes map to those as defined by
|
||||
evdev as opposed to those defined by Gdk, thus the returned list is
|
||||
directly usable by libratbag."""
|
||||
if self._keycode > self._XORG_KEYCODE_OFFSET:
|
||||
ret = [self._keycode - self._XORG_KEYCODE_OFFSET]
|
||||
else:
|
||||
# TODO: make ratbag reset the key mapping on an empty list or
|
||||
# something like that and return that value here instead. Currently
|
||||
# this returns an unmappable key.
|
||||
ret = [self._keycode]
|
||||
return ret + [keycode - self._XORG_KEYCODE_OFFSET for keycode, _ in self._modifiers]
|
||||
|
||||
def _check_key(self, keycode, keyval):
|
||||
# Checks if the given keycode and keyval are valid through a simply
|
||||
# range check.
|
||||
if not self._XORG_KEYCODE_OFFSET <= keycode <= 255:
|
||||
print("Keycode is not within the valid range.", file=sys.stderr)
|
||||
return False
|
||||
elif not Gdk.KEY_space <= keyval <= Gdk.KEY_AudioMicMute:
|
||||
print("Keyval is not within the valid range.", file=sys.stderr)
|
||||
return False
|
||||
return True
|
||||
|
||||
def _update_accelerator(self):
|
||||
# Updates the accelerator property, so that any GObject bindings to
|
||||
# Gtk.ShortcutLabels' accelerator property are updated automatically.
|
||||
mask = Gdk.ModifierType(0)
|
||||
for (_, keyval) in self._modifiers:
|
||||
# The following are GTK+'s default modifiers that are allowed
|
||||
# through in ButtonDialog.on_key_press_event, hence we check for
|
||||
# these modifiers only -- it is unlikely we will find others. If
|
||||
# this needs to be made more accurate, we can iterate over the mask
|
||||
# returned by Gtk.accelerator_get_default_mod_mask().
|
||||
# The default mod mask depends on the Gdk backend in use, but will
|
||||
# typically include Control, Shift, Alt, Super, Hyper and Meta.
|
||||
if keyval == Gdk.KEY_Shift_L or keyval == Gdk.KEY_Shift_R or keyval == Gdk.KEY_Shift_Lock:
|
||||
mask |= Gdk.ModifierType.SHIFT_MASK
|
||||
elif keyval == Gdk.KEY_Hyper_L or keyval == Gdk.KEY_Hyper_R:
|
||||
mask |= Gdk.ModifierType.HYPER_MASK
|
||||
elif keyval == Gdk.KEY_Meta_L or keyval == Gdk.KEY_Meta_R:
|
||||
mask |= Gdk.ModifierType.META_MASK
|
||||
elif keyval == Gdk.KEY_Control_L or keyval == Gdk.KEY_Control_R:
|
||||
mask |= Gdk.ModifierType.CONTROL_MASK
|
||||
elif keyval == Gdk.KEY_Super_L or keyval == Gdk.KEY_Super_R:
|
||||
mask |= Gdk.ModifierType.SUPER_MASK
|
||||
elif keyval == Gdk.KEY_Alt_L or keyval == Gdk.KEY_Alt_R:
|
||||
mask |= Gdk.ModifierType.MOD1_MASK
|
||||
self._accelerator = Gtk.accelerator_name(self._keyval, mask)
|
||||
self.notify("accelerator")
|
||||
Reference in New Issue
Block a user