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 Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
6 files · +44 -17
M internal/adapters/libinput/libinput.go +8 -4
| @@ -80,6 +80,8 @@ var ( | |||
| 80 | 80 | gestureCanceled func(gev uintptr) int32 | |
| 81 | 81 | gestureDX func(gev uintptr) float64 | |
| 82 | 82 | gestureDY func(gev uintptr) float64 | |
| 83 | + | gestureAccelDX func(gev uintptr) float64 | |
| 84 | + | gestureAccelDY func(gev uintptr) float64 | |
| 83 | 85 | gestureUsec func(gev uintptr) uint64 | |
| 84 | 86 | gestureScale func(gev uintptr) float64 | |
| 85 | 87 | gestureAngle func(gev uintptr) float64 | |
| @@ -155,6 +157,8 @@ func load() error { | |||
| 155 | 157 | reg(&gestureCanceled, "event_gesture_get_cancelled") | |
| 156 | 158 | reg(&gestureDX, "event_gesture_get_dx_unaccelerated") | |
| 157 | 159 | reg(&gestureDY, "event_gesture_get_dy_unaccelerated") | |
| 160 | + | reg(&gestureAccelDX, "event_gesture_get_dx") | |
| 161 | + | reg(&gestureAccelDY, "event_gesture_get_dy") | |
| 158 | 162 | reg(&gestureUsec, "event_gesture_get_time_usec") | |
| 159 | 163 | reg(&gestureScale, "event_gesture_get_scale") | |
| 160 | 164 | reg(&gestureAngle, "event_gesture_get_angle_delta") | |
| @@ -517,11 +521,11 @@ func (g *gestures) running(dev uintptr, kind ports.GestureKind) bool { | |||
| 517 | 521 | 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 | 525 | if !g.running(dev, ports.GestureSwipe) { | |
| 522 | 526 | 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 | 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 | 619 | return in.gestures.begin(eventDevice(ev), kind, int(gestureFingers(ge)), usec(gestureUsec(ge))), nil | |
| 616 | 620 | case evSwipeUpdate: | |
| 617 | 621 | 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 | |
| 619 | 623 | case evPinchUpdate: | |
| 620 | 624 | 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 | |
| 622 | 626 | case evSwipeEnd: | |
| 623 | 627 | ge := gestureEvent(ev) | |
| 624 | 628 | 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) int32 | 80 | gestureCanceled func(gev uintptr) int32 | ||
| 81 | gestureDX func(gev uintptr) float64 | 81 | gestureDX func(gev uintptr) float64 | ||
| 82 | gestureDY func(gev uintptr) float64 | 82 | gestureDY func(gev uintptr) float64 | ||
| 83 | + | gestureAccelDX func(gev uintptr) float64 | |||
| 84 | + | gestureAccelDY func(gev uintptr) float64 | |||
| 83 | gestureUsec func(gev uintptr) uint64 | 85 | gestureUsec func(gev uintptr) uint64 | ||
| 84 | gestureScale func(gev uintptr) float64 | 86 | gestureScale func(gev uintptr) float64 | ||
| 85 | gestureAngle func(gev uintptr) float64 | 87 | 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 == kind | 521 | 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 nil | 526 | 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))), nil | 619 | 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))), nil | 622 | + | 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))), nil | 625 | + | 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))), nil | 628 | 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) { | |||
| 49 | 49 | if ev := g.begin(other, ports.GestureSwipe, 3, time.Second); ev != nil { | |
| 50 | 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 | 53 | t.Fatalf("second touchpad moved: %v", ev) | |
| 54 | 54 | } | |
| 55 | 55 | if ev := g.end(other, ports.GestureSwipe, false, time.Second); ev != nil { | |
| 56 | 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 | 59 | t.Fatal("first touchpad lost its swipe") | |
| 60 | 60 | } | |
| 61 | 61 | g.end(pad, ports.GestureSwipe, false, 3*time.Second) | |
| @@ -70,13 +70,13 @@ func TestGesturesStreamSwipe(t *testing.T) { | |||
| 70 | 70 | if ev := g.begin(pad, ports.GestureSwipe, 0, time.Second); ev != nil { | |
| 71 | 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 | 74 | t.Fatalf("update without a swipe: %v", ev) | |
| 75 | 75 | } | |
| 76 | 76 | if ev := g.begin(pad, ports.GestureSwipe, 3, time.Second); ev != (ports.SwipeBegin{Fingers: 3, Time: time.Second}) { | |
| 77 | 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 | 80 | t.Fatalf("update: %v", ev) | |
| 81 | 81 | } | |
| 82 | 82 | if ev := g.end(other, ports.GestureSwipe, false, 0); ev != nil { | |
| @@ -109,7 +109,7 @@ func TestGesturesStreamPinch(t *testing.T) { | |||
| 109 | 109 | if ev := g.begin(pad, ports.GesturePinch, 2, time.Second); ev != (ports.PinchBegin{Fingers: 2, Time: time.Second}) { | |
| 110 | 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 | 113 | t.Fatalf("swipe update during a pinch: %v", ev) | |
| 114 | 114 | } | |
| 115 | 115 | 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 | |||
| 73 | 73 | if g.id == 0 { | |
| 74 | 74 | 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 | 81 | return c.command(ctx, ports.GestureEndTo{ID: g.id, Kind: kind, Cancelled: cancelled || g.id != c.pointer, Time: at}) | |
| 77 | 82 | } | |
| 78 | 83 | ||
| @@ -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 nil | 74 | 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) { | |||
| 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 | 92 | func TestHoldGoesToPointerFocus(t *testing.T) { | |
| 79 | 93 | r := gestureRig(t) | |
| 80 | 94 | r.input <- ports.HoldBegin{Fingers: 3, Time: time.Second} | |
| @@ -93,11 +107,12 @@ func TestSwipeFingerCounts(t *testing.T) { | |||
| 93 | 107 | for _, fingers := range []int{2, 5} { | |
| 94 | 108 | r := gestureRig(t) | |
| 95 | 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 | 112 | r.input <- ports.SwipeEnd{Time: 3 * time.Second} | |
| 98 | 113 | for _, want := range []ports.ClientCommand{ | |
| 99 | 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 | 116 | ports.GestureEndTo{ID: 1, Kind: ports.GestureSwipe, Time: 3 * time.Second}, | |
| 102 | 117 | } { | |
| 103 | 118 | 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 { | |||
| 38 | 38 | return nil | |
| 39 | 39 | case ports.SwipeUpdate: | |
| 40 | 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 | 43 | before := c.cur().mon.Current() | |
| 44 | 44 | if c.swipeUpdate(v) { | |
| @@ -38,7 +38,7 @@ func (c *Core) handleInput(ctx context.Context, ev ports.InputEvent) error { | |||||
| 38 | return nil | 38 | 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 { | |||
| 98 | 98 | } | |
| 99 | 99 | ||
| 100 | 100 | // 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 | 104 | type SwipeUpdate struct { | |
| 103 | - | DX, DY float64 | |
| 104 | - | Time time.Duration | |
| 105 | + | DX, DY float64 | |
| 106 | + | AccelDX, AccelDY float64 | |
| 107 | + | Time time.Duration | |
| 105 | 108 | } | |
| 106 | 109 | ||
| 107 | 110 | // SwipeEnd lifts the fingers. Cancelled is set when libinput cancelled | |
| @@ -123,8 +126,8 @@ type PinchBegin struct { | |||
| 123 | 126 | Time time.Duration | |
| 124 | 127 | } | |
| 125 | 128 | ||
| 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 | |
| 128 | 131 | // relative to the begin (1.0 there) and Rotation the angle turned since the | |
| 129 | 132 | // previous event, in degrees clockwise. | |
| 130 | 133 | type PinchUpdate struct { | |
| @@ -98,10 +98,13 @@ type SwipeBegin struct { | |||||
| 98 | } | 98 | } | ||
| 99 | 99 | ||||
| 100 | // SwipeUpdate moves the fingers by the unaccelerated deltas DX, DY | 100 | // 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 float64 | 105 | + | DX, DY float64 |
| 104 | - | Time time.Duration | 106 | + | AccelDX, AccelDY float64 |
| 107 | + | Time time.Duration | |||
| 105 | } | 108 | } | ||
| 106 | 109 | ||||
| 107 | // SwipeEnd lifts the fingers. Cancelled is set when libinput cancelled | 110 | // SwipeEnd lifts the fingers. Cancelled is set when libinput cancelled | ||
| @@ -123,8 +126,8 @@ type PinchBegin struct { | |||||
| 123 | Time time.Duration | 126 | Time time.Duration | ||
| 124 | } | 127 | } | ||
| 125 | 128 | ||||
| 126 | - | // PinchUpdate moves a pinch: the fingers' centre by the unaccelerated | 129 | + | // 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 fingers | 130 | + | // 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 the | 131 | // 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 { | ||