1package wayland
2
3import (
4 "time"
5
6 "github.com/bnema/go-wayland-bindings/server/pointergestures"
7 "github.com/bnema/go-wayland-bindings/server/wayland"
8 "github.com/bnema/neferwl/internal/ports"
9 "github.com/bnema/purego-libwayland/server"
10)
11
12// zwp_pointer_gestures_v1: touchpad swipes (other than the compositor's own
13// three and four fingers), pinches and holds go to the client under the
14// pointer. A gesture belongs to the window that had the pointer when it
15// began; it ends cancelled when the pointer leaves that window.
16
17// gestureObject is one swipe, pinch or hold object of a client. Exactly one
18// of the three is set.
19type gestureObject struct {
20 swipe *pointergestures.ZwpPointerGestureSwipeV1
21 pinch *pointergestures.ZwpPointerGesturePinchV1
22 hold *pointergestures.ZwpPointerGestureHoldV1
23}
24
25func (g *gestureObject) alive() bool {
26 switch {
27 case g.swipe != nil:
28 return g.swipe.Resource.Alive()
29 case g.pinch != nil:
30 return g.pinch.Resource.Alive()
31 case g.hold != nil:
32 return g.hold.Resource.Alive()
33 }
34 return false
35}
36
37func (g *gestureObject) kind() ports.GestureKind {
38 switch {
39 case g.pinch != nil:
40 return ports.GesturePinch
41 case g.hold != nil:
42 return ports.GestureHold
43 }
44 return ports.GestureSwipe
45}
46
47func (g *gestureObject) begin(serial uint32, at uint32, surf *wayland.Surface, fingers uint32) {
48 switch {
49 case g.swipe != nil:
50 g.swipe.SendBegin(serial, at, surf, fingers)
51 case g.pinch != nil:
52 g.pinch.SendBegin(serial, at, surf, fingers)
53 case g.hold != nil:
54 g.hold.SendBegin(serial, at, surf, fingers)
55 }
56}
57
58func (g *gestureObject) update(u ports.GestureUpdateTo) {
59 at := wireMsec(u.Time)
60 dx, dy := server.FixedFromFloat(u.DX), server.FixedFromFloat(u.DY)
61 switch {
62 case g.swipe != nil:
63 g.swipe.SendUpdate(at, dx, dy)
64 case g.pinch != nil:
65 g.pinch.SendUpdate(at, dx, dy, server.FixedFromFloat(u.Scale), server.FixedFromFloat(u.Rotation))
66 }
67}
68
69func (g *gestureObject) end(serial uint32, at uint32, cancelled int32) {
70 switch {
71 case g.swipe != nil:
72 g.swipe.SendEnd(serial, at, cancelled)
73 case g.pinch != nil:
74 g.pinch.SendEnd(serial, at, cancelled)
75 case g.hold != nil:
76 g.hold.SendEnd(serial, at, cancelled)
77 }
78}
79
80// activeGesture is the gesture sent to a window: the objects that got its
81// begin are the ones that get the rest, even when a client makes more
82// meanwhile.
83type activeGesture struct {
84 id ports.WindowID
85 kind ports.GestureKind
86 objs []*gestureObject
87 // at is the latest device time sent, for a cancellation.
88 at time.Duration
89}
90
91func registerPointerGestures(d *server.Display, s *Server) error {
92 return pointergestures.NewZwpPointerGesturesV1Global(d, 3, func(c server.Client, v, id uint32) {
93 _, _ = pointergestures.NewZwpPointerGesturesV1(c, int32(v), id, gestureManager{s})
94 })
95}
96
97// gestureManager is zwp_pointer_gestures_v1. Releasing it leaves the
98// gesture objects it made alive, as the protocol says.
99type gestureManager struct{ server *Server }
100
101func (gestureManager) Release(*pointergestures.ZwpPointerGesturesV1) {}
102
103// gestureDestroy handles the destroy request of the three gesture objects;
104// the resource's OnDestroy forgets the object.
105type gestureDestroy struct{}
106
107func (gestureDestroy) Destroy(*pointergestures.ZwpPointerGestureSwipeV1) {}
108
109type pinchDestroy struct{}
110
111func (pinchDestroy) Destroy(*pointergestures.ZwpPointerGesturePinchV1) {}
112
113type holdDestroy struct{}
114
115func (holdDestroy) Destroy(*pointergestures.ZwpPointerGestureHoldV1) {}
116
117func (m gestureManager) GetSwipeGesture(r *pointergestures.ZwpPointerGesturesV1, id uint32, _ *wayland.Pointer) {
118 if res, err := pointergestures.NewZwpPointerGestureSwipeV1(r.Client(), r.Version(), id, gestureDestroy{}); err == nil {
119 m.server.addGesture(r.Client(), &gestureObject{swipe: res}, res.Resource)
120 }
121}
122
123func (m gestureManager) GetPinchGesture(r *pointergestures.ZwpPointerGesturesV1, id uint32, _ *wayland.Pointer) {
124 if res, err := pointergestures.NewZwpPointerGesturePinchV1(r.Client(), r.Version(), id, pinchDestroy{}); err == nil {
125 m.server.addGesture(r.Client(), &gestureObject{pinch: res}, res.Resource)
126 }
127}
128
129func (m gestureManager) GetHoldGesture(r *pointergestures.ZwpPointerGesturesV1, id uint32, _ *wayland.Pointer) {
130 if res, err := pointergestures.NewZwpPointerGestureHoldV1(r.Client(), r.Version(), id, holdDestroy{}); err == nil {
131 m.server.addGesture(r.Client(), &gestureObject{hold: res}, res.Resource)
132 }
133}
134
135// addGesture lists a client's gesture object until its resource dies.
136func (s *Server) addGesture(c server.Client, o *gestureObject, res *server.Resource) {
137 s.gestures[c] = append(s.gestures[c], o)
138 res.OnDestroy = func() {
139 if list := removeItem(s.gestures[c], o); len(list) > 0 {
140 s.gestures[c] = list
141 } else {
142 delete(s.gestures, c)
143 }
144 }
145}
146
147// beginGesture sends the begin of a gesture to the objects of the kind that
148// the client of the pointer-focus window made. A gesture still running
149// ends cancelled first. Nothing is recorded when the client has no object.
150func (s *Server) beginGesture(c ports.GestureBeginTo) {
151 s.cancelGesture()
152 if c.ID != s.seat.pointerFocus || c.Fingers <= 0 {
153 return
154 }
155 surf, _, _, _ := s.pointerSurface(c.ID, 0, 0)
156 if surf == nil {
157 return
158 }
159 var objs []*gestureObject
160 for _, o := range s.gestures[surf.Client()] {
161 if o.kind() == c.Kind && o.alive() {
162 objs = append(objs, o)
163 }
164 }
165 if len(objs) == 0 {
166 return
167 }
168 s.seat.gesture = &activeGesture{id: c.ID, kind: c.Kind, objs: objs, at: c.Time}
169 s.serial++
170 for _, o := range objs {
171 o.begin(s.serial, wireMsec(c.Time), surf, uint32(c.Fingers))
172 }
173}
174
175// updateGesture moves the running gesture of the window.
176func (s *Server) updateGesture(c ports.GestureUpdateTo) {
177 g := s.seat.gesture
178 if g == nil || g.id != c.ID || g.kind != c.Kind {
179 return
180 }
181 g.at = c.Time
182 for _, o := range g.objs {
183 if o.alive() {
184 o.update(c)
185 }
186 }
187}
188
189// endGesture ends the running gesture of the window.
190func (s *Server) endGesture(c ports.GestureEndTo) {
191 g := s.seat.gesture
192 if g == nil || g.id != c.ID || g.kind != c.Kind {
193 return
194 }
195 g.at = c.Time
196 s.finishGesture(c.Cancelled)
197}
198
199// cancelGesture ends the running gesture, if any, as cancelled: the
200// pointer left its window or the window went.
201func (s *Server) cancelGesture() {
202 if s.seat.gesture != nil {
203 s.finishGesture(true)
204 }
205}
206
207func (s *Server) finishGesture(cancelled bool) {
208 g := s.seat.gesture
209 s.seat.gesture = nil
210 state := int32(0)
211 if cancelled {
212 state = 1
213 }
214 s.serial++
215 for _, o := range g.objs {
216 if o.alive() {
217 o.end(s.serial, wireMsec(g.at), state)
218 }
219 }
220}