From da0bb937c36f9e30146b729b241cb26d835fc94d Mon Sep 17 00:00:00 2001 From: rommeswi Date: Fri, 22 May 2026 15:03:17 +0900 Subject: [PATCH] Add configurable touchscreen swipe gesture to switch windows Clutter.PanGesture-based so it works over windows; 4-finger default. Adds enable, finger-count and sensitivity preferences. Co-Authored-By: Claude Opus 4.7 --- Settings.ui | 153 ++++++++++++++++++ gestures.js | 75 +++++++++ prefs.js | 9 ++ schemas/gschemas.compiled | Bin 12333 -> 12525 bytes ...gnome.shell.extensions.paperwm.gschema.xml | 15 ++ settings.js | 3 + 6 files changed, 255 insertions(+) diff --git a/Settings.ui b/Settings.ui index 5a59c5079..c65c045d5 100644 --- a/Settings.ui +++ b/Settings.ui @@ -30,6 +30,12 @@ 0.1 10 + + 0.5 + 5 + 0.1 + 1 + 100 1 @@ -510,6 +516,153 @@ + + + start + True + Touchscreen Gestures + + + + + + + + False + none + 16 + + + + False + False + + + False + Enables / disables the PaperWM touchscreen swipe gesture for moving the tiling left/right. + 12 + 12 + 6 + 6 + 32 + + + False + 1 + Enable Touchscreen Gestures + 1 + 0 + + 0 + 0 + + + + + + + 1 + 0 + + + + + + + + + + False + False + + + False + Number of fingers for the touchscreen tiling swipe. 3 fingers conflicts with GNOME's workspace gesture. + 12 + 12 + 6 + 6 + 32 + + + False + 1 + Swipe tiling windows <i>(swipe left/right)</i> + 1 + 0 + + 0 + 0 + + + + + + False + 0 + 106 + + 4 Fingers + 5 Fingers + + + + + + + + + + False + False + + + False + How far the tiling moves per unit of finger travel. Higher means a shorter swipe switches window. + 12 + 12 + 6 + 6 + 32 + + + False + 1 + Swipe sensitivity + 1 + 0 + + 0 + 0 + + + + + + True + True + 2 + 2 + 0 + touch-gesture-sensitivity-adjustment + 1 + 1 + 1 + always + + 1 + 0 + + + + + + + + + start diff --git a/gestures.js b/gestures.js index ecad1e38e..e31960a28 100644 --- a/gestures.js +++ b/gestures.js @@ -15,6 +15,7 @@ const DIRECTIONS = { }; let vy, time, vState, navigator, direction, signals; +let touchPanGesture; // touchscreen 4-finger pan gesture (local addition) let handoffToOverview = false; // 1 is natural scrolling, -1 is unnatural let natural = 1; @@ -172,6 +173,76 @@ export function enable(extension) { } return Clutter.EVENT_PROPAGATE; }); + + /** + Touchscreen pan -> scroll the window strip. + + PaperWM natively only handles touchpad swipes (TOUCHPAD_SWIPE events) + and a single-finger top-bar touch scroll; a touchscreen never emits + TOUCHPAD_SWIPE. This adds a touchscreen equivalent via Clutter's gesture + framework. Unlike a raw captured-event handler, a gesture recognised by + the framework intercepts touch even over windows. + + Defaults to 4 fingers: GNOME owns the 3-finger touchscreen swipe + (workspace switching) and wins gesture recognition. Finger count, + sensitivity and enabled state are configurable via the touch-gesture-* + settings. + */ + touchPanGesture = new Clutter.PanGesture(); + touchPanGesture.set_pan_axis(Clutter.PanAxis.X); + let panSpace = null; + + // Push finger count and enabled state to the gesture; re-apply on change. + // Sensitivity is read live per pan-update (see below). + const applyTouchGestureSettings = () => { + const fingers = Settings.prefs.touch_gesture_fingers; + touchPanGesture.set_min_n_points(fingers); + touchPanGesture.set_max_n_points(fingers); + touchPanGesture.set_enabled(Settings.prefs.touch_gesture_enabled); + }; + applyTouchGestureSettings(); + signals.connect(gsettings, 'changed::touch-gesture-enabled', applyTouchGestureSettings); + signals.connect(gsettings, 'changed::touch-gesture-fingers', applyTouchGestureSettings); + + signals.connect(touchPanGesture, 'recognize', () => { + const space = Tiling.spaces.activeSpace ?? Tiling.spaces.selectedSpace; + panSpace = space ?? null; + if (!panSpace) + return; + panSpace.vx = 0; + dxs = []; + dts = []; + start = panSpace.targetX; + panSpace.hState = Clutter.TouchpadGesturePhase.UPDATE; + direction = DIRECTIONS.Horizontal; + Easer.removeEase(panSpace.cloneContainer); + swipeTrackersEnable(false); + navigator = Navigator.getNavigator(); + update(panSpace, 0, GLib.get_monotonic_time() / 1000); + }); + + signals.connect(touchPanGesture, 'pan-update', gesture => { + if (!panSpace) + return; + const delta = gesture.get_delta(); + update(panSpace, -delta.get_x() * Settings.prefs.touch_gesture_sensitivity, + GLib.get_monotonic_time() / 1000); + }); + + const finishPan = () => { + if (!panSpace) + return; + panSpace.hState = Clutter.TouchpadGesturePhase.END; + done(panSpace); + direction = undefined; + dxs = []; + dts = []; + panSpace = null; + }; + signals.connect(touchPanGesture, 'end', finishPan); + signals.connect(touchPanGesture, 'cancel', finishPan); + + global.stage.add_action(touchPanGesture); } function shouldPropagate(fingers) { @@ -206,6 +277,10 @@ export function disable() { signals = null; Utils.timeout_remove(endVerticalTimeout); endVerticalTimeout = null; + if (touchPanGesture) { + global.stage.remove_action(touchPanGesture); + touchPanGesture = null; + } touchpadSettings = null; } diff --git a/prefs.js b/prefs.js index b2cab90e7..bc9e06136 100644 --- a/prefs.js +++ b/prefs.js @@ -447,6 +447,15 @@ class SettingsWidget { const fingerNumberDefault = 0; enumOptionsChanged('gesture-horizontal-fingers', fingerOptions, fingerOptionDefault, fingerNumberDefault); enumOptionsChanged('gesture-workspace-fingers', fingerOptions, fingerOptionDefault, fingerNumberDefault); + + // Touchscreen gesture settings + booleanStateChanged('touch-gesture-enabled'); + enumOptionsChanged( + 'touch-gesture-fingers', + { 'four-fingers': 4, 'five-fingers': 5 }, + 'four-fingers', + 4); + doubleValueChanged('touch-gesture-sensitivity', 'touch-gesture-sensitivity'); enumOptionsChanged( 'default-focus-mode', { diff --git a/schemas/gschemas.compiled b/schemas/gschemas.compiled index 484bc32864f8f3279e98bd501039d8a7dd491ad9..7d91a414711cad725fed4aa05535fc3081824603 100644 GIT binary patch delta 3201 zcmZvedu&rx0LIV8$_5*3zy=-bSO@F4ecY`)rYMXp1PmI9Gl~!eW7pnoH>|z2?H&-I zkVrxzBnyW{RE#q)P=aw25dHu}5yL}dMlr+)!T1O!f=iG%5!CNH?R0;5lV88@e&^nE z?s?pv`|b-j16^H?0+$euqH`xQgb+)rh46``A^~0H7eW?P&hEcfRhq0vArD@Z2}X@z zH2g5x@MF^CLdSveX$qhdMlcbc6oScM3b3ashLWkkAw*J?!HmR-b5ahbr>TIVGoljA zOyhnEN*l4w+4~3p=z9Y&OnLw4t1S zJn{zUDv-InB*~n9EbhJPVYoM1ijDPYT5bNXE51JF@!%>UQgjgMF`3ag0?IH4L? zG4|;^=Jf2@Qs^?UGTa|$PR}**0<;Z0yKqh$bNWK$UC?f@`>pe8%IG;|$Dk*`)uE0j zY-nGdOhLgFhM@G)@yA#ZJ%@S-dLKMrwzrizJ$vT#;NieDZG8rFdM>Vd=xT87x7Yq) zPH#uv3EcpuyIv17r%!U|dSH6N-rAcdSwPPlPC?HA*EUV9cKUJ1uRwnQ`#*YL4JmyN z@-k`7Y>QaCG0I zQs(r${uuN!xO;oQS`+kKE6E!$gW%8du$lsTuHGE{hmHgDOSZJLB6?1Z2K9q`(Wfey z({pO-p-aGmxzDJ#hMuok19T;*t#PZ@lAiTPp`BHj|N9@T*v^V@A||1r3)&4fUO%K7 zpyv&J&|~1FZ0h0l^z7LH^jq+kKSzxmJ?D4`$_=2dw^ywpdi>!C4XQIgw<2Ie4eEpq z*CXUMDWkq>dmv(l0}0cL=m|4ypr+I@AU|{!OTV{NhFu!p-3%}XlmhNFIe=Rowpg{5 z&VbGX+#(+a3jlW>Zi`qGYA3=LARYkRrN)A3fDc~+xC?UkbAtIG8;l0rLX-LU;qHZp zW=DhzU{A#`qI+OGCy%xrE+aBA={zxc{!Pi2XXCvN$sZ$>s}j5mJeosp@< zWmaWfMXGs2Z?R(LIxCVui%ob;BNoS{{2=1>FL`IkyKb+%>UPQ>Jq~%!nX64HD?Me|VUvp~Di69!WSu)d84EP)p_ti}(Bo0V2;pK5 zFo!68r55awUDCaP+BdPjR4;Z!L zrh1ba1FC*mh&kEfEt21Oawq-w3_+{aQUfmE*7wV0TG{A9)elbTn6@Y@#v%XED&#V^ zU3R;wCkEFBTQO?}=Ua~_jA&e*beBCiVj5Em=ik+mdafob*cR8r7KYdiS`k*O){3P& zs?)RW9$pf|y;mI{F=^=;laK4g0r(n+VB>j;}&yQ5< X58#o!*VnTm`Ycp*1A0@wTD|ihj;eSt delta 2957 zcmZ9OZA?>F0EQ0?1_;P!5u}xZl-@!wRDsP+5#w~hWkeUJ?!y=gy_5#}VOv0rv+M_n z%Vy$sG&7TBEXqKqnM|Ymp$6wz{K!U^&CNd=lEsDnz?i5RI+=Ul3pH*xdHTM6&$;KG zd(OG{+)3y0roDTOXG?`J8ZMuU6GHS>3Sk!w;uN&6N(dQM77yR7G)3xA7(1SUiIE6T zo}DEHnhMfE2FL{8IcSXM;mHf&MX(es1H9?R0Ye}g*B2b71)@ugLnpwy+NJDlRG^=Wd+@-fZWUsSdSjs$ecbC`4Z>~FxI!HnK^wi@;0a!j1JbXVoskGK|v49e$XAN zZDj$y3HdkB??C*9!U%KvImn~X>!5yMK)(PzFTi0;1bK%p{>l3EsmL>-F9FZKn%9`q z7a(tfMmq4btY%{x3+Q>mHfRs{$B}%OIX&$A|(mQ~{O`6#b6r(~&QMt^$czZI5x#3-PL+ zITRsoOm(kiJM{BVzX93^PF1{phB?9O^Ph%-9)!bSP5bu^ETA_d zzYe_tOk-zXVNTD6rl8ZHYwQPogy;*A=VJw%zO>0BTsL{1IX&lc1#~s|yV$EQB6>E|1l2%^<)D}K zb&q^EvOQqe_{mb{^sGMwJpnT88Ajw0{SP0T5rm^)Yx@=lPoU?Jjze#O)5nGl%<1{K zCRbo>fU7Gj&oZZHeFf?Oi612-FsJ7TH9#A|^yy7|nA7Ko2<`wz1IKp!KE?ui-f<7K z7i^mIy8d*~^TI>W
JEBacY=URzEuYvvBoF~{0Js0&9bQ;uO-sEIXpMyLTZ#qK+ zKf~WvL|I^fXHOl_TJZ4h-~;CLoXZC2IZfsPhW(*0on-aYHj*R&~xtFpaHP7u(yl#;p_AZ>_vD0+;v^r%mR9@i6Q7(yzI|> zvC_nxUcX}~cQhHbT9S|p#bcB8mb}Dhy--{-qoM%sY`mZNgCbYZ=~i7qPit5W1vJeS zKby-FWybWN$K4iYfFn-6XR*spwJN>a<8%4DRkt&MuFD6NHF8v0E*%x7f>!;w%?qL7 zCvA#&nPttFla)#CIcy4-4f_Ml&YMW?QHYmzlj< zz#sC2J$|2x(_-aaS}^QsadxO)XVC5O@w{CAC?5I9Rvby3@iwo==kYoNxQtV8zFI#n z>b>Ot&s~q(Ft_K$fLn1sz*Dbp`KeIuns~VMt(-e1W?%44%^j3mI(N`TfLneoKtCdQ z-OhXqa7?+kbKvv95`ftg2_PBpjm<5ZgOdp`dxG0H_Aapy@W4wHZ(}afnQx1^P&_Pn zy11aZ70hh+{QltgMG_W+{(-^|5AN+LK%TSZCva%xZMzv=yrYjnySBXoOk=&xrD{*U~aA#0c+x$V#4!#!AE)C>Ye8JDEG}OrJ8fp!Xj9}sxmn&(^SMIx4(%*2m&0W_@7Bjta>9yofW#(kfr~eT$%bw4<<6-6pCC*Q)G;+#jRvQ9a_mg{Q@pt%x zA=zLbOl#TR(t$zN_)N-b3|CaK%dJ(_5q`6BqaK1D2Un`^S3Qf?==FULItm_U#qVQI zpMv^P=ryovaa{&;dcGUSp|?Qqyj^bQ^!&EE1HBJEH5}3F)1y8f4SZ)NgV-ksJwHLH WrQqeXgFD#}eF7R%psIY+vF~3mJ2iO# diff --git a/schemas/org.gnome.shell.extensions.paperwm.gschema.xml b/schemas/org.gnome.shell.extensions.paperwm.gschema.xml index 1819e96b0..71e244ae0 100644 --- a/schemas/org.gnome.shell.extensions.paperwm.gschema.xml +++ b/schemas/org.gnome.shell.extensions.paperwm.gschema.xml @@ -768,6 +768,21 @@ Number of fingers for workspace switching. 0:DISABLED 3:THREE_FINGERS 4:FOUR_FINGERS + + true + Enables the PaperWM touchscreen swipe gesture + + + + 4 + Number of fingers for the touchscreen tiling swipe. 4:FOUR_FINGERS 5:FIVE_FINGERS + + + + 1.8 + Touchscreen swipe sensitivity: how far the tiling moves per unit of finger travel + + diff --git a/settings.js b/settings.js index c35d34c23..d1baba964 100644 --- a/settings.js +++ b/settings.js @@ -67,6 +67,9 @@ export function enable(extension) { 'gesture-enabled', 'gesture-horizontal-fingers', 'gesture-workspace-fingers', + 'touch-gesture-enabled', + 'touch-gesture-fingers', + 'touch-gesture-sensitivity', 'open-window-position', 'overview-ensure-viewport-animation', 'overview-min-windows-per-row',