fix(wayland): sum relative pointer deltas when collapsing queued motions
- commit
- a589526e41b1148ba827652728db82a8139f29b6
- author
- brice · · 2026-09-26 14:24 UTC
- parent
- bc84cb3
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +13 -5
M internal/adapters/wayland/commands_test.go +5 -4
| @@ -12,16 +12,17 @@ import ( | |||
| 12 | 12 | ||
| 13 | 13 | func TestDrainCommandsCollapsesMotion(t *testing.T) { | |
| 14 | 14 | cmds := make(chan ports.ClientCommand, 16) | |
| 15 | - | cmds <- ports.PointerMotionTo{ID: 1, X: 2} | |
| 16 | - | cmds <- ports.PointerMotionTo{ID: 1, X: 3} | |
| 15 | + | cmds <- ports.PointerMotionTo{ID: 1, X: 2, DX: 0.25, UnaccelDX: 0.5} | |
| 16 | + | cmds <- ports.PointerMotionTo{ID: 1, X: 3, DX: 0.25, DY: -0.5, UnaccelDX: 0.5, UnaccelDY: -1} | |
| 17 | 17 | cmds <- ports.PointerButtonTo{ID: 1, Button: 272, Pressed: true} | |
| 18 | 18 | cmds <- ports.PointerMotionTo{ID: 1, X: 4} | |
| 19 | 19 | cmds <- ports.PointerFocus{ID: 2} | |
| 20 | 20 | cmds <- ports.PointerMotionTo{ID: 2, X: 5} | |
| 21 | 21 | cmds <- ports.PointerMotionTo{ID: 2, X: 6} | |
| 22 | - | batch, open := drainCommands(ports.PointerMotionTo{ID: 1, X: 1}, cmds) | |
| 22 | + | batch, open := drainCommands(ports.PointerMotionTo{ID: 1, X: 1, DX: 0.25, UnaccelDX: 0.5}, cmds) | |
| 23 | 23 | want := []ports.ClientCommand{ | |
| 24 | - | ports.PointerMotionTo{ID: 1, X: 3}, | |
| 24 | + | // Slow motions add up: none of the sub-pixel deltas is lost. | |
| 25 | + | ports.PointerMotionTo{ID: 1, X: 3, DX: 0.75, DY: -0.5, UnaccelDX: 1.5, UnaccelDY: -1}, | |
| 25 | 26 | ports.PointerButtonTo{ID: 1, Button: 272, Pressed: true}, | |
| 26 | 27 | ports.PointerMotionTo{ID: 1, X: 4}, | |
| 27 | 28 | ports.PointerFocus{ID: 2}, | |
| @@ -12,16 +12,17 @@ import ( | |||||
| 12 | 12 | ||||
| 13 | func TestDrainCommandsCollapsesMotion(t *testing.T) { | 13 | func TestDrainCommandsCollapsesMotion(t *testing.T) { | ||
| 14 | cmds := make(chan ports.ClientCommand, 16) | 14 | cmds := make(chan ports.ClientCommand, 16) | ||
| 15 | - | cmds <- ports.PointerMotionTo{ID: 1, X: 2} | 15 | + | cmds <- ports.PointerMotionTo{ID: 1, X: 2, DX: 0.25, UnaccelDX: 0.5} |
| 16 | - | cmds <- ports.PointerMotionTo{ID: 1, X: 3} | 16 | + | cmds <- ports.PointerMotionTo{ID: 1, X: 3, DX: 0.25, DY: -0.5, UnaccelDX: 0.5, UnaccelDY: -1} |
| 17 | cmds <- ports.PointerButtonTo{ID: 1, Button: 272, Pressed: true} | 17 | cmds <- ports.PointerButtonTo{ID: 1, Button: 272, Pressed: true} | ||
| 18 | cmds <- ports.PointerMotionTo{ID: 1, X: 4} | 18 | cmds <- ports.PointerMotionTo{ID: 1, X: 4} | ||
| 19 | cmds <- ports.PointerFocus{ID: 2} | 19 | cmds <- ports.PointerFocus{ID: 2} | ||
| 20 | cmds <- ports.PointerMotionTo{ID: 2, X: 5} | 20 | cmds <- ports.PointerMotionTo{ID: 2, X: 5} | ||
| 21 | cmds <- ports.PointerMotionTo{ID: 2, X: 6} | 21 | cmds <- ports.PointerMotionTo{ID: 2, X: 6} | ||
| 22 | - | batch, open := drainCommands(ports.PointerMotionTo{ID: 1, X: 1}, cmds) | 22 | + | batch, open := drainCommands(ports.PointerMotionTo{ID: 1, X: 1, DX: 0.25, UnaccelDX: 0.5}, cmds) |
| 23 | want := []ports.ClientCommand{ | 23 | want := []ports.ClientCommand{ | ||
| 24 | - | ports.PointerMotionTo{ID: 1, X: 3}, | 24 | + | // Slow motions add up: none of the sub-pixel deltas is lost. |
| 25 | + | ports.PointerMotionTo{ID: 1, X: 3, DX: 0.75, DY: -0.5, UnaccelDX: 1.5, UnaccelDY: -1}, | |||
| 25 | ports.PointerButtonTo{ID: 1, Button: 272, Pressed: true}, | 26 | ports.PointerButtonTo{ID: 1, Button: 272, Pressed: true}, | ||
| 26 | ports.PointerMotionTo{ID: 1, X: 4}, | 27 | ports.PointerMotionTo{ID: 1, X: 4}, | ||
| 27 | ports.PointerFocus{ID: 2}, | 28 | ports.PointerFocus{ID: 2}, | ||
M internal/adapters/wayland/server.go +8 -1
| @@ -432,7 +432,8 @@ func (s *Server) forwardContents(ctx context.Context) { | |||
| 432 | 432 | const maxCommandBatch = 256 | |
| 433 | 433 | ||
| 434 | 434 | // drainCommands returns first plus the commands already queued, with runs of | |
| 435 | - | // pointer motion for the same window collapsed into the latest one. open is | |
| 435 | + | // pointer motion for the same window collapsed into the latest one, their | |
| 436 | + | // relative deltas summed. open is | |
| 436 | 437 | // false once the channel is closed. | |
| 437 | 438 | func drainCommands(first ports.ClientCommand, cmds <-chan ports.ClientCommand) (batch []ports.ClientCommand, open bool) { | |
| 438 | 439 | batch = append(batch, first) | |
| @@ -444,6 +445,12 @@ func drainCommands(first ports.ClientCommand, cmds <-chan ports.ClientCommand) ( | |||
| 444 | 445 | } | |
| 445 | 446 | if m, isMotion := c.(ports.PointerMotionTo); isMotion { | |
| 446 | 447 | if last, lastMotion := batch[len(batch)-1].(ports.PointerMotionTo); lastMotion && last.ID == m.ID { | |
| 448 | + | // The position is the latest; relative deltas add up so | |
| 449 | + | // locked pointers (games) lose no movement. | |
| 450 | + | m.DX += last.DX | |
| 451 | + | m.DY += last.DY | |
| 452 | + | m.UnaccelDX += last.UnaccelDX | |
| 453 | + | m.UnaccelDY += last.UnaccelDY | |
| 447 | 454 | batch[len(batch)-1] = m | |
| 448 | 455 | continue | |
| 449 | 456 | } | |
| @@ -432,7 +432,8 @@ func (s *Server) forwardContents(ctx context.Context) { | |||||
| 432 | const maxCommandBatch = 256 | 432 | const maxCommandBatch = 256 | ||
| 433 | 433 | ||||
| 434 | // drainCommands returns first plus the commands already queued, with runs of | 434 | // drainCommands returns first plus the commands already queued, with runs of | ||
| 435 | - | // pointer motion for the same window collapsed into the latest one. open is | 435 | + | // pointer motion for the same window collapsed into the latest one, their |
| 436 | + | // relative deltas summed. open is | |||
| 436 | // false once the channel is closed. | 437 | // false once the channel is closed. | ||
| 437 | func drainCommands(first ports.ClientCommand, cmds <-chan ports.ClientCommand) (batch []ports.ClientCommand, open bool) { | 438 | func drainCommands(first ports.ClientCommand, cmds <-chan ports.ClientCommand) (batch []ports.ClientCommand, open bool) { | ||
| 438 | batch = append(batch, first) | 439 | batch = append(batch, first) | ||
| @@ -444,6 +445,12 @@ func drainCommands(first ports.ClientCommand, cmds <-chan ports.ClientCommand) ( | |||||
| 444 | } | 445 | } | ||
| 445 | if m, isMotion := c.(ports.PointerMotionTo); isMotion { | 446 | if m, isMotion := c.(ports.PointerMotionTo); isMotion { | ||
| 446 | if last, lastMotion := batch[len(batch)-1].(ports.PointerMotionTo); lastMotion && last.ID == m.ID { | 447 | if last, lastMotion := batch[len(batch)-1].(ports.PointerMotionTo); lastMotion && last.ID == m.ID { | ||
| 448 | + | // The position is the latest; relative deltas add up so | |||
| 449 | + | // locked pointers (games) lose no movement. | |||
| 450 | + | m.DX += last.DX | |||
| 451 | + | m.DY += last.DY | |||
| 452 | + | m.UnaccelDX += last.UnaccelDX | |||
| 453 | + | m.UnaccelDY += last.UnaccelDY | |||
| 447 | batch[len(batch)-1] = m | 454 | batch[len(batch)-1] = m | ||
| 448 | continue | 455 | continue | ||
| 449 | } | 456 | } | ||