detached 84d935b

fix(input): send clients accelerated gesture deltas and a monotonic end time

Clients get libinput's accelerated swipe and pinch deltas, as on other
compositors; the compositor's own three- and four-finger swipes keep the
unaccelerated ones. A gesture ended by a removed touchpad carries no time:
core sends the last one seen, so the client's clock never runs backwards.
commit
84d935bf2fd0855be1ffcb226213edfa96a9254a
author
brice · · 2026-10-08 09:46 UTC
committer
brice · · 2026-10-08 09:59 UTC
parent
9c0be44
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
6 files · +44 -17
M internal/adapters/libinput/libinput.go +8 -4
@@ -80,6 +80,8 @@ var (
8080 gestureCanceled func(gev uintptr) int32
8181 gestureDX func(gev uintptr) float64
8282 gestureDY func(gev uintptr) float64
83+ gestureAccelDX func(gev uintptr) float64
84+ gestureAccelDY func(gev uintptr) float64
8385 gestureUsec func(gev uintptr) uint64
8486 gestureScale func(gev uintptr) float64
8587 gestureAngle func(gev uintptr) float64
@@ -155,6 +157,8 @@ func load() error {
155157 reg(&gestureCanceled, "event_gesture_get_cancelled")
156158 reg(&gestureDX, "event_gesture_get_dx_unaccelerated")
157159 reg(&gestureDY, "event_gesture_get_dy_unaccelerated")
160+ reg(&gestureAccelDX, "event_gesture_get_dx")
161+ reg(&gestureAccelDY, "event_gesture_get_dy")
158162 reg(&gestureUsec, "event_gesture_get_time_usec")
159163 reg(&gestureScale, "event_gesture_get_scale")
160164 reg(&gestureAngle, "event_gesture_get_angle_delta")
@@ -517,11 +521,11 @@ func (g *gestures) running(dev uintptr, kind ports.GestureKind) bool {
517521 return dev != 0 && g.owner == dev && g.kind == kind
518522 }
519523
520-func (g *gestures) swipeUpdate(dev uintptr, dx, dy float64, at time.Duration) ports.InputEvent {
524+func (g *gestures) swipeUpdate(dev uintptr, dx, dy, accelDX, accelDY float64, at time.Duration) ports.InputEvent {
521525 if !g.running(dev, ports.GestureSwipe) {
522526 return nil
523527 }
524- return ports.SwipeUpdate{DX: dx, DY: dy, Time: at}
528+ return ports.SwipeUpdate{DX: dx, DY: dy, AccelDX: accelDX, AccelDY: accelDY, Time: at}
525529 }
526530
527531 func (g *gestures) pinchUpdate(dev uintptr, dx, dy, scale, rotation float64, at time.Duration) ports.InputEvent {
@@ -615,10 +619,10 @@ func translateEvent(ev uintptr, opts Options, p *pointer, in *inputState, state
615619 return in.gestures.begin(eventDevice(ev), kind, int(gestureFingers(ge)), usec(gestureUsec(ge))), nil
616620 case evSwipeUpdate:
617621 ge := gestureEvent(ev)
618- return in.gestures.swipeUpdate(eventDevice(ev), gestureDX(ge), gestureDY(ge), usec(gestureUsec(ge))), nil
622+ return in.gestures.swipeUpdate(eventDevice(ev), gestureDX(ge), gestureDY(ge), gestureAccelDX(ge), gestureAccelDY(ge), usec(gestureUsec(ge))), nil
619623 case evPinchUpdate:
620624 ge := gestureEvent(ev)
621- return in.gestures.pinchUpdate(eventDevice(ev), gestureDX(ge), gestureDY(ge), gestureScale(ge), gestureAngle(ge), usec(gestureUsec(ge))), nil
625+ return in.gestures.pinchUpdate(eventDevice(ev), gestureAccelDX(ge), gestureAccelDY(ge), gestureScale(ge), gestureAngle(ge), usec(gestureUsec(ge))), nil
622626 case evSwipeEnd:
623627 ge := gestureEvent(ev)
624628 return in.gestures.end(eventDevice(ev), ports.GestureSwipe, gestureCanceled(ge) != 0, usec(gestureUsec(ge))), nil
@@ -80,6 +80,8 @@ var (
80 gestureCanceled func(gev uintptr) int3280 gestureCanceled func(gev uintptr) int32
81 gestureDX func(gev uintptr) float6481 gestureDX func(gev uintptr) float64
82 gestureDY func(gev uintptr) float6482 gestureDY func(gev uintptr) float64
83+ gestureAccelDX func(gev uintptr) float64
84+ gestureAccelDY func(gev uintptr) float64
83 gestureUsec func(gev uintptr) uint6485 gestureUsec func(gev uintptr) uint64
84 gestureScale func(gev uintptr) float6486 gestureScale func(gev uintptr) float64
85 gestureAngle func(gev uintptr) float6487 gestureAngle func(gev uintptr) float64
@@ -155,6 +157,8 @@ func load() error {
155 reg(&gestureCanceled, "event_gesture_get_cancelled")157 reg(&gestureCanceled, "event_gesture_get_cancelled")
156 reg(&gestureDX, "event_gesture_get_dx_unaccelerated")158 reg(&gestureDX, "event_gesture_get_dx_unaccelerated")
157 reg(&gestureDY, "event_gesture_get_dy_unaccelerated")159 reg(&gestureDY, "event_gesture_get_dy_unaccelerated")
160+ reg(&gestureAccelDX, "event_gesture_get_dx")
161+ reg(&gestureAccelDY, "event_gesture_get_dy")
158 reg(&gestureUsec, "event_gesture_get_time_usec")162 reg(&gestureUsec, "event_gesture_get_time_usec")
159 reg(&gestureScale, "event_gesture_get_scale")163 reg(&gestureScale, "event_gesture_get_scale")
160 reg(&gestureAngle, "event_gesture_get_angle_delta")164 reg(&gestureAngle, "event_gesture_get_angle_delta")
@@ -517,11 +521,11 @@ func (g *gestures) running(dev uintptr, kind ports.GestureKind) bool {
517 return dev != 0 && g.owner == dev && g.kind == kind521 return dev != 0 && g.owner == dev && g.kind == kind
518 }522 }
519 523
520-func (g *gestures) swipeUpdate(dev uintptr, dx, dy float64, at time.Duration) ports.InputEvent {524+func (g *gestures) swipeUpdate(dev uintptr, dx, dy, accelDX, accelDY float64, at time.Duration) ports.InputEvent {
521 if !g.running(dev, ports.GestureSwipe) {525 if !g.running(dev, ports.GestureSwipe) {
522 return nil526 return nil
523 }527 }
524- return ports.SwipeUpdate{DX: dx, DY: dy, Time: at}528+ return ports.SwipeUpdate{DX: dx, DY: dy, AccelDX: accelDX, AccelDY: accelDY, Time: at}
525 }529 }
526 530
527 func (g *gestures) pinchUpdate(dev uintptr, dx, dy, scale, rotation float64, at time.Duration) ports.InputEvent {531 func (g *gestures) pinchUpdate(dev uintptr, dx, dy, scale, rotation float64, at time.Duration) ports.InputEvent {
@@ -615,10 +619,10 @@ func translateEvent(ev uintptr, opts Options, p *pointer, in *inputState, state
615 return in.gestures.begin(eventDevice(ev), kind, int(gestureFingers(ge)), usec(gestureUsec(ge))), nil619 return in.gestures.begin(eventDevice(ev), kind, int(gestureFingers(ge)), usec(gestureUsec(ge))), nil
616 case evSwipeUpdate:620 case evSwipeUpdate:
617 ge := gestureEvent(ev)621 ge := gestureEvent(ev)
618- return in.gestures.swipeUpdate(eventDevice(ev), gestureDX(ge), gestureDY(ge), usec(gestureUsec(ge))), nil622+ return in.gestures.swipeUpdate(eventDevice(ev), gestureDX(ge), gestureDY(ge), gestureAccelDX(ge), gestureAccelDY(ge), usec(gestureUsec(ge))), nil
619 case evPinchUpdate:623 case evPinchUpdate:
620 ge := gestureEvent(ev)624 ge := gestureEvent(ev)
621- return in.gestures.pinchUpdate(eventDevice(ev), gestureDX(ge), gestureDY(ge), gestureScale(ge), gestureAngle(ge), usec(gestureUsec(ge))), nil625+ return in.gestures.pinchUpdate(eventDevice(ev), gestureAccelDX(ge), gestureAccelDY(ge), gestureScale(ge), gestureAngle(ge), usec(gestureUsec(ge))), nil
622 case evSwipeEnd:626 case evSwipeEnd:
623 ge := gestureEvent(ev)627 ge := gestureEvent(ev)
624 return in.gestures.end(eventDevice(ev), ports.GestureSwipe, gestureCanceled(ge) != 0, usec(gestureUsec(ge))), nil628 return in.gestures.end(eventDevice(ev), ports.GestureSwipe, gestureCanceled(ge) != 0, usec(gestureUsec(ge))), nil
M internal/adapters/libinput/libinput_test.go +5 -5
@@ -49,13 +49,13 @@ func TestGesturesIgnoreSecondTouchpad(t *testing.T) {
4949 if ev := g.begin(other, ports.GestureSwipe, 3, time.Second); ev != nil {
5050 t.Fatalf("second touchpad began: %v", ev)
5151 }
52- if ev := g.swipeUpdate(other, 1, 1, time.Second); ev != nil {
52+ if ev := g.swipeUpdate(other, 1, 1, 1, 1, time.Second); ev != nil {
5353 t.Fatalf("second touchpad moved: %v", ev)
5454 }
5555 if ev := g.end(other, ports.GestureSwipe, false, time.Second); ev != nil {
5656 t.Fatalf("second touchpad ended the swipe: %v", ev)
5757 }
58- if ev := g.swipeUpdate(pad, 1, 1, 2*time.Second); ev == nil {
58+ if ev := g.swipeUpdate(pad, 1, 1, 1, 1, 2*time.Second); ev == nil {
5959 t.Fatal("first touchpad lost its swipe")
6060 }
6161 g.end(pad, ports.GestureSwipe, false, 3*time.Second)
@@ -70,13 +70,13 @@ func TestGesturesStreamSwipe(t *testing.T) {
7070 if ev := g.begin(pad, ports.GestureSwipe, 0, time.Second); ev != nil {
7171 t.Fatalf("no fingers: %v", ev)
7272 }
73- if ev := g.swipeUpdate(pad, 1, 1, time.Second); ev != nil {
73+ if ev := g.swipeUpdate(pad, 1, 1, 1, 1, time.Second); ev != nil {
7474 t.Fatalf("update without a swipe: %v", ev)
7575 }
7676 if ev := g.begin(pad, ports.GestureSwipe, 3, time.Second); ev != (ports.SwipeBegin{Fingers: 3, Time: time.Second}) {
7777 t.Fatalf("begin: %v", ev)
7878 }
79- if ev := g.swipeUpdate(pad, -3, 2, 2*time.Second); ev != (ports.SwipeUpdate{DX: -3, DY: 2, Time: 2 * time.Second}) {
79+ if ev := g.swipeUpdate(pad, -3, 2, -6, 4, 2*time.Second); ev != (ports.SwipeUpdate{DX: -3, DY: 2, AccelDX: -6, AccelDY: 4, Time: 2 * time.Second}) {
8080 t.Fatalf("update: %v", ev)
8181 }
8282 if ev := g.end(other, ports.GestureSwipe, false, 0); ev != nil {
@@ -109,7 +109,7 @@ func TestGesturesStreamPinch(t *testing.T) {
109109 if ev := g.begin(pad, ports.GesturePinch, 2, time.Second); ev != (ports.PinchBegin{Fingers: 2, Time: time.Second}) {
110110 t.Fatalf("begin: %v", ev)
111111 }
112- if ev := g.swipeUpdate(pad, 1, 1, time.Second); ev != nil {
112+ if ev := g.swipeUpdate(pad, 1, 1, 1, 1, time.Second); ev != nil {
113113 t.Fatalf("swipe update during a pinch: %v", ev)
114114 }
115115 if ev := g.end(pad, ports.GestureHold, false, time.Second); ev != nil {
@@ -49,13 +49,13 @@ func TestGesturesIgnoreSecondTouchpad(t *testing.T) {
49 if ev := g.begin(other, ports.GestureSwipe, 3, time.Second); ev != nil {49 if ev := g.begin(other, ports.GestureSwipe, 3, time.Second); ev != nil {
50 t.Fatalf("second touchpad began: %v", ev)50 t.Fatalf("second touchpad began: %v", ev)
51 }51 }
52- if ev := g.swipeUpdate(other, 1, 1, time.Second); ev != nil {52+ if ev := g.swipeUpdate(other, 1, 1, 1, 1, time.Second); ev != nil {
53 t.Fatalf("second touchpad moved: %v", ev)53 t.Fatalf("second touchpad moved: %v", ev)
54 }54 }
55 if ev := g.end(other, ports.GestureSwipe, false, time.Second); ev != nil {55 if ev := g.end(other, ports.GestureSwipe, false, time.Second); ev != nil {
56 t.Fatalf("second touchpad ended the swipe: %v", ev)56 t.Fatalf("second touchpad ended the swipe: %v", ev)
57 }57 }
58- if ev := g.swipeUpdate(pad, 1, 1, 2*time.Second); ev == nil {58+ if ev := g.swipeUpdate(pad, 1, 1, 1, 1, 2*time.Second); ev == nil {
59 t.Fatal("first touchpad lost its swipe")59 t.Fatal("first touchpad lost its swipe")
60 }60 }
61 g.end(pad, ports.GestureSwipe, false, 3*time.Second)61 g.end(pad, ports.GestureSwipe, false, 3*time.Second)
@@ -70,13 +70,13 @@ func TestGesturesStreamSwipe(t *testing.T) {
70 if ev := g.begin(pad, ports.GestureSwipe, 0, time.Second); ev != nil {70 if ev := g.begin(pad, ports.GestureSwipe, 0, time.Second); ev != nil {
71 t.Fatalf("no fingers: %v", ev)71 t.Fatalf("no fingers: %v", ev)
72 }72 }
73- if ev := g.swipeUpdate(pad, 1, 1, time.Second); ev != nil {73+ if ev := g.swipeUpdate(pad, 1, 1, 1, 1, time.Second); ev != nil {
74 t.Fatalf("update without a swipe: %v", ev)74 t.Fatalf("update without a swipe: %v", ev)
75 }75 }
76 if ev := g.begin(pad, ports.GestureSwipe, 3, time.Second); ev != (ports.SwipeBegin{Fingers: 3, Time: time.Second}) {76 if ev := g.begin(pad, ports.GestureSwipe, 3, time.Second); ev != (ports.SwipeBegin{Fingers: 3, Time: time.Second}) {
77 t.Fatalf("begin: %v", ev)77 t.Fatalf("begin: %v", ev)
78 }78 }
79- if ev := g.swipeUpdate(pad, -3, 2, 2*time.Second); ev != (ports.SwipeUpdate{DX: -3, DY: 2, Time: 2 * time.Second}) {79+ if ev := g.swipeUpdate(pad, -3, 2, -6, 4, 2*time.Second); ev != (ports.SwipeUpdate{DX: -3, DY: 2, AccelDX: -6, AccelDY: 4, Time: 2 * time.Second}) {
80 t.Fatalf("update: %v", ev)80 t.Fatalf("update: %v", ev)
81 }81 }
82 if ev := g.end(other, ports.GestureSwipe, false, 0); ev != nil {82 if ev := g.end(other, ports.GestureSwipe, false, 0); ev != nil {
@@ -109,7 +109,7 @@ func TestGesturesStreamPinch(t *testing.T) {
109 if ev := g.begin(pad, ports.GesturePinch, 2, time.Second); ev != (ports.PinchBegin{Fingers: 2, Time: time.Second}) {109 if ev := g.begin(pad, ports.GesturePinch, 2, time.Second); ev != (ports.PinchBegin{Fingers: 2, Time: time.Second}) {
110 t.Fatalf("begin: %v", ev)110 t.Fatalf("begin: %v", ev)
111 }111 }
112- if ev := g.swipeUpdate(pad, 1, 1, time.Second); ev != nil {112+ if ev := g.swipeUpdate(pad, 1, 1, 1, 1, time.Second); ev != nil {
113 t.Fatalf("swipe update during a pinch: %v", ev)113 t.Fatalf("swipe update during a pinch: %v", ev)
114 }114 }
115 if ev := g.end(pad, ports.GestureHold, false, time.Second); ev != nil {115 if ev := g.end(pad, ports.GestureHold, false, time.Second); ev != nil {
M internal/core/clientgesture.go +5 -0
@@ -73,6 +73,11 @@ func (c *Core) gestureEnd(ctx context.Context, kind ports.GestureKind, cancelled
7373 if g.id == 0 {
7474 return nil
7575 }
76+ if at == 0 {
77+ // A removed device's end has no time: the client's clock must
78+ // not run backwards.
79+ at = g.at
80+ }
7681 return c.command(ctx, ports.GestureEndTo{ID: g.id, Kind: kind, Cancelled: cancelled || g.id != c.pointer, Time: at})
7782 }
7883
@@ -73,6 +73,11 @@ func (c *Core) gestureEnd(ctx context.Context, kind ports.GestureKind, cancelled
73 if g.id == 0 {73 if g.id == 0 {
74 return nil74 return nil
75 }75 }
76+ if at == 0 {
77+ // A removed device's end has no time: the client's clock must
78+ // not run backwards.
79+ at = g.at
80+ }
76 return c.command(ctx, ports.GestureEndTo{ID: g.id, Kind: kind, Cancelled: cancelled || g.id != c.pointer, Time: at})81 return c.command(ctx, ports.GestureEndTo{ID: g.id, Kind: kind, Cancelled: cancelled || g.id != c.pointer, Time: at})
77 }82 }
78 83
M internal/core/clientgesture_test.go +17 -2
@@ -75,6 +75,20 @@ func TestPinchGoesToPointerFocus(t *testing.T) {
7575 }
7676 }
7777
78+// A removed touchpad ends its gesture with no time: the client gets the
79+// last one, so its clock never runs backwards.
80+func TestGestureEndWithoutTime(t *testing.T) {
81+ r := gestureRig(t)
82+ r.input <- ports.PinchBegin{Fingers: 2, Time: time.Second}
83+ r.input <- ports.PinchUpdate{Scale: 1, Time: 2 * time.Second}
84+ r.input <- ports.PinchEnd{Cancelled: true}
85+ pointerCommand(t, r.commands)
86+ pointerCommand(t, r.commands)
87+ if v := pointerCommand(t, r.commands); v != (ports.GestureEndTo{ID: 1, Kind: ports.GesturePinch, Cancelled: true, Time: 2 * time.Second}) {
88+ t.Fatal(v)
89+ }
90+}
91+
7892 func TestHoldGoesToPointerFocus(t *testing.T) {
7993 r := gestureRig(t)
8094 r.input <- ports.HoldBegin{Fingers: 3, Time: time.Second}
@@ -93,11 +107,12 @@ func TestSwipeFingerCounts(t *testing.T) {
93107 for _, fingers := range []int{2, 5} {
94108 r := gestureRig(t)
95109 r.input <- ports.SwipeBegin{Fingers: fingers, Time: time.Second}
96- r.input <- ports.SwipeUpdate{DX: 3, DY: 4, Time: 2 * time.Second}
110+ // The client gets the accelerated deltas.
111+ r.input <- ports.SwipeUpdate{DX: 3, DY: 4, AccelDX: 6, AccelDY: 8, Time: 2 * time.Second}
97112 r.input <- ports.SwipeEnd{Time: 3 * time.Second}
98113 for _, want := range []ports.ClientCommand{
99114 ports.GestureBeginTo{ID: 1, Kind: ports.GestureSwipe, Fingers: fingers, Time: time.Second},
100- ports.GestureUpdateTo{ID: 1, Kind: ports.GestureSwipe, DX: 3, DY: 4, Time: 2 * time.Second},
115+ ports.GestureUpdateTo{ID: 1, Kind: ports.GestureSwipe, DX: 6, DY: 8, Time: 2 * time.Second},
101116 ports.GestureEndTo{ID: 1, Kind: ports.GestureSwipe, Time: 3 * time.Second},
102117 } {
103118 if v := pointerCommand(t, r.commands); v != want {
@@ -75,6 +75,20 @@ func TestPinchGoesToPointerFocus(t *testing.T) {
75 }75 }
76 }76 }
77 77
78+// A removed touchpad ends its gesture with no time: the client gets the
79+// last one, so its clock never runs backwards.
80+func TestGestureEndWithoutTime(t *testing.T) {
81+ r := gestureRig(t)
82+ r.input <- ports.PinchBegin{Fingers: 2, Time: time.Second}
83+ r.input <- ports.PinchUpdate{Scale: 1, Time: 2 * time.Second}
84+ r.input <- ports.PinchEnd{Cancelled: true}
85+ pointerCommand(t, r.commands)
86+ pointerCommand(t, r.commands)
87+ if v := pointerCommand(t, r.commands); v != (ports.GestureEndTo{ID: 1, Kind: ports.GesturePinch, Cancelled: true, Time: 2 * time.Second}) {
88+ t.Fatal(v)
89+ }
90+}
91+
78 func TestHoldGoesToPointerFocus(t *testing.T) {92 func TestHoldGoesToPointerFocus(t *testing.T) {
79 r := gestureRig(t)93 r := gestureRig(t)
80 r.input <- ports.HoldBegin{Fingers: 3, Time: time.Second}94 r.input <- ports.HoldBegin{Fingers: 3, Time: time.Second}
@@ -93,11 +107,12 @@ func TestSwipeFingerCounts(t *testing.T) {
93 for _, fingers := range []int{2, 5} {107 for _, fingers := range []int{2, 5} {
94 r := gestureRig(t)108 r := gestureRig(t)
95 r.input <- ports.SwipeBegin{Fingers: fingers, Time: time.Second}109 r.input <- ports.SwipeBegin{Fingers: fingers, Time: time.Second}
96- r.input <- ports.SwipeUpdate{DX: 3, DY: 4, Time: 2 * time.Second}110+ // The client gets the accelerated deltas.
111+ r.input <- ports.SwipeUpdate{DX: 3, DY: 4, AccelDX: 6, AccelDY: 8, Time: 2 * time.Second}
97 r.input <- ports.SwipeEnd{Time: 3 * time.Second}112 r.input <- ports.SwipeEnd{Time: 3 * time.Second}
98 for _, want := range []ports.ClientCommand{113 for _, want := range []ports.ClientCommand{
99 ports.GestureBeginTo{ID: 1, Kind: ports.GestureSwipe, Fingers: fingers, Time: time.Second},114 ports.GestureBeginTo{ID: 1, Kind: ports.GestureSwipe, Fingers: fingers, Time: time.Second},
100- ports.GestureUpdateTo{ID: 1, Kind: ports.GestureSwipe, DX: 3, DY: 4, Time: 2 * time.Second},115+ ports.GestureUpdateTo{ID: 1, Kind: ports.GestureSwipe, DX: 6, DY: 8, Time: 2 * time.Second},
101 ports.GestureEndTo{ID: 1, Kind: ports.GestureSwipe, Time: 3 * time.Second},116 ports.GestureEndTo{ID: 1, Kind: ports.GestureSwipe, Time: 3 * time.Second},
102 } {117 } {
103 if v := pointerCommand(t, r.commands); v != want {118 if v := pointerCommand(t, r.commands); v != want {
M internal/core/input.go +1 -1
@@ -38,7 +38,7 @@ func (c *Core) handleInput(ctx context.Context, ev ports.InputEvent) error {
3838 return nil
3939 case ports.SwipeUpdate:
4040 if c.gesture.on {
41- return c.gestureUpdate(ctx, ports.GestureUpdateTo{Kind: ports.GestureSwipe, DX: v.DX, DY: v.DY, Time: v.Time})
41+ return c.gestureUpdate(ctx, ports.GestureUpdateTo{Kind: ports.GestureSwipe, DX: v.AccelDX, DY: v.AccelDY, Time: v.Time})
4242 }
4343 before := c.cur().mon.Current()
4444 if c.swipeUpdate(v) {
@@ -38,7 +38,7 @@ func (c *Core) handleInput(ctx context.Context, ev ports.InputEvent) error {
38 return nil38 return nil
39 case ports.SwipeUpdate:39 case ports.SwipeUpdate:
40 if c.gesture.on {40 if c.gesture.on {
41- return c.gestureUpdate(ctx, ports.GestureUpdateTo{Kind: ports.GestureSwipe, DX: v.DX, DY: v.DY, Time: v.Time})41+ return c.gestureUpdate(ctx, ports.GestureUpdateTo{Kind: ports.GestureSwipe, DX: v.AccelDX, DY: v.AccelDY, Time: v.Time})
42 }42 }
43 before := c.cur().mon.Current()43 before := c.cur().mon.Current()
44 if c.swipeUpdate(v) {44 if c.swipeUpdate(v) {
M internal/ports/input_events.go +8 -5
@@ -98,10 +98,13 @@ type SwipeBegin struct {
9898 }
9999
100100 // SwipeUpdate moves the fingers by the unaccelerated deltas DX, DY
101-// (touchpad units, as libinput reports them, not natural-scroll inverted).
101+// (touchpad units, as libinput reports them, not natural-scroll inverted),
102+// which the compositor's own swipes follow. AccelDX, AccelDY are the same
103+// move with pointer acceleration, what a client gets.
102104 type SwipeUpdate struct {
103- DX, DY float64
104- Time time.Duration
105+ DX, DY float64
106+ AccelDX, AccelDY float64
107+ Time time.Duration
105108 }
106109
107110 // SwipeEnd lifts the fingers. Cancelled is set when libinput cancelled
@@ -123,8 +126,8 @@ type PinchBegin struct {
123126 Time time.Duration
124127 }
125128
126-// PinchUpdate moves a pinch: the fingers' centre by the unaccelerated
127-// deltas DX, DY (touchpad units), Scale is the spread of the fingers
129+// PinchUpdate moves a pinch: the fingers' centre by the deltas DX, DY with
130+// pointer acceleration, as clients get them, Scale is the spread of the fingers
128131 // relative to the begin (1.0 there) and Rotation the angle turned since the
129132 // previous event, in degrees clockwise.
130133 type PinchUpdate struct {
@@ -98,10 +98,13 @@ type SwipeBegin struct {
98 }98 }
99 99
100 // SwipeUpdate moves the fingers by the unaccelerated deltas DX, DY100 // SwipeUpdate moves the fingers by the unaccelerated deltas DX, DY
101-// (touchpad units, as libinput reports them, not natural-scroll inverted).101+// (touchpad units, as libinput reports them, not natural-scroll inverted),
102+// which the compositor's own swipes follow. AccelDX, AccelDY are the same
103+// move with pointer acceleration, what a client gets.
102 type SwipeUpdate struct {104 type SwipeUpdate struct {
103- DX, DY float64105+ DX, DY float64
104- Time time.Duration106+ AccelDX, AccelDY float64
107+ Time time.Duration
105 }108 }
106 109
107 // SwipeEnd lifts the fingers. Cancelled is set when libinput cancelled110 // SwipeEnd lifts the fingers. Cancelled is set when libinput cancelled
@@ -123,8 +126,8 @@ type PinchBegin struct {
123 Time time.Duration126 Time time.Duration
124 }127 }
125 128
126-// PinchUpdate moves a pinch: the fingers' centre by the unaccelerated129+// PinchUpdate moves a pinch: the fingers' centre by the deltas DX, DY with
127-// deltas DX, DY (touchpad units), Scale is the spread of the fingers130+// pointer acceleration, as clients get them, Scale is the spread of the fingers
128 // relative to the begin (1.0 there) and Rotation the angle turned since the131 // relative to the begin (1.0 there) and Rotation the angle turned since the
129 // previous event, in degrees clockwise.132 // previous event, in degrees clockwise.
130 type PinchUpdate struct {133 type PinchUpdate struct {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 25

Tags 10

↑↓ moveenter openesc close