From efd31ad6219455fb7019d005c3e558a290a826fe Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Thu, 24 Apr 2025 19:40:38 +0200 Subject: [PATCH] io/input/pointer: ignore grab commands for tags that don't have the pointer Without this fix, two gestures that both issue GrabCmd on the same frame will cancel each other. With the fix, the first will win the grab, and the other ignored. References: https://todo.sr.ht/~eliasnaur/gio/647 Signed-off-by: Elias Naur --- io/input/pointer.go | 11 +++++++++++ io/input/pointer_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/io/input/pointer.go b/io/input/pointer.go index e084e780..292e892d 100644 --- a/io/input/pointer.go +++ b/io/input/pointer.go @@ -257,6 +257,17 @@ func (q *pointerQueue) grab(state pointerState, req pointer.GrabCmd) (pointerSta if !p.pressed || p.id != req.ID { continue } + // Verify that the grabber is among the handlers. + found := false + for _, tag := range p.handlers { + if tag == req.Tag { + found = true + break + } + } + if !found { + continue + } // Drop other handlers that lost their grab. for i := len(p.handlers) - 1; i >= 0; i-- { if tag := p.handlers[i]; tag != req.Tag { diff --git a/io/input/pointer_test.go b/io/input/pointer_test.go index ed3bb74a..335f8c5c 100644 --- a/io/input/pointer_test.go +++ b/io/input/pointer_test.go @@ -97,6 +97,39 @@ func TestPointerDragNegative(t *testing.T) { assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Enter, pointer.Press, pointer.Leave, pointer.Drag) } +func TestIgnoredGrab(t *testing.T) { + handler1 := new(int) + handler2 := new(int) + var ops op.Ops + + filter := func(t event.Tag) event.Filter { + return pointer.Filter{Target: t, Kinds: pointer.Press | pointer.Release | pointer.Cancel} + } + + event.Op(&ops, handler1) + event.Op(&ops, handler2) + var r Router + assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Cancel) + assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel) + r.Frame(&ops) + r.Queue( + pointer.Event{ + Kind: pointer.Press, + Position: f32.Pt(50, 50), + }, + pointer.Event{ + Kind: pointer.Release, + Position: f32.Pt(50, 50), + }, + ) + assertEventPointerTypeSequence(t, events(&r, 1, filter(handler1)), pointer.Press) + assertEventPointerTypeSequence(t, events(&r, 1, filter(handler2)), pointer.Press) + r.Source().Execute(pointer.GrabCmd{Tag: handler1}) + r.Source().Execute(pointer.GrabCmd{Tag: handler2}) + assertEventPointerTypeSequence(t, events(&r, 1, filter(handler1)), pointer.Release) + assertEventPointerTypeSequence(t, events(&r, 1, filter(handler2)), pointer.Cancel) +} + func TestPointerGrab(t *testing.T) { handler1 := new(int) handler2 := new(int)