1package neferclient
2
3import (
4 "sync"
5
6 "golang.org/x/sys/unix"
7)
8
9const (
10 ringSize = 256 // events queued between the reader and Dispatch
11 maxNameLen = 64 // longest output name kept; longer names are truncated
12)
13
14type eventKind uint8
15
16const (
17 evGlobal eventKind = iota + 1 // registry global (wl_output only): global, version
18 evGlobalRemove // registry global_remove: global
19 evOutputName // name, global
20 evOutputScale // a = factor, global
21 evOutputMode // flags, a = width, b = height, global
22 evOutputDone // global
23 evFDReady // fd (epoll data)
24
25 // Surface events. id is the wl_surface object id (SurfaceID).
26 evPing // serial (xdg_wm_base)
27 evXdgToplevel // id, a = width, b = height (before the matching evXdgConfigure)
28 evXdgConfigure // id, serial
29 evLayerConfigure // id, serial, a = width, b = height
30 evLockConfigure // id, serial, a = width, b = height
31 evClosed // id
32 evFrame // id
33 evPreferredScale // id, a = fractional scale in 120ths
34 evBufferScale // id, a = integer factor
35 evFeedbackTable // id, fd = owned descriptor, flags = size
36 evFeedbackMainDevice // id, dev
37 evFeedbackFormats // id, name = one chunk of tranche format indices
38 evFeedbackDone // id
39 evLocked // id = lock generation
40 evLockFinished // id = lock generation
41 evBadEvent // id, a = badEvent code: the reader met a malformed event
42
43 // Seat events (kind >= evSeatCaps). Pointer and keyboard events carry the
44 // generation of the wl_pointer/wl_keyboard in version, so events of a
45 // released device that are still queued are ignored. global is a wl_surface
46 // object id where a surface is named.
47 evSeatCaps // a = capabilities
48 evSeatBad // a = bad code: a seat device event was malformed
49 evPtrEnter // global = surface, serial, a, b = fixed x, y
50 evPtrLeave // global = surface
51 evPtrMotion // a, b = fixed x, y
52 evPtrButton // flags = button, a = state
53 evPtrAxis // flags = axis, a = fixed value
54 evPtrAxisValue // flags = axis, a = value120
55 evPtrAxisStop // flags = axis
56 evKbKeymap // fd = owned descriptor, flags = size, a = format
57 evKbEnter // global = surface, serial
58 evKbLeave // global = surface
59 evKbKey // flags = evdev code, a = state
60 evKbModifiers // flags = depressed, dev = latched | locked<<32, a = group
61 evKbRepeat // a = rate, b = delay
62)
63
64// event is the only thing the reader and the epoll goroutine hand to the
65// owner. It holds no pointers except through fixed storage, so queuing one
66// never allocates.
67type event struct {
68 kind eventKind
69 nameLen uint8
70 global uint32
71 version uint32
72 flags uint32
73 id uint32 // wl_surface object id for surface events
74 serial uint32
75 fd int32 // epoll data, or a received descriptor owned by the event
76 a, b int32
77 dev uint64
78 name [maxNameLen]byte
79}
80
81// hasFD reports whether the event owns a received descriptor that must be
82// closed if the event is dropped.
83func (e *event) hasFD() bool { return (e.kind == evFeedbackTable || e.kind == evKbKeymap) && e.fd >= 0 }
84
85func (e *event) closeFD() {
86 if e.hasFD() {
87 _ = unix.Close(int(e.fd))
88 e.fd = -1
89 }
90}
91
92func (e *event) setBytes(b []byte) {
93 e.nameLen = uint8(copy(e.name[:], b))
94}
95
96func (e *event) setName(s string) {
97 e.nameLen = uint8(copy(e.name[:], s))
98}
99
100func (e *event) nameBytes() []byte { return e.name[:e.nameLen] }
101
102// queue is the bounded hand-off between producers (the reader goroutine and
103// the epoll goroutine) and the owner goroutine, plus the reader pause gate.
104//
105// A producer facing a full ring waits on cond; Dispatch releases it as it
106// drains. A pause request also releases it: while pausing, producers park
107// events in held instead of waiting, so neither side can block on the other.
108// Parked events are re-posted, in order, by the reader once it resumes.
109type queue struct {
110 mu sync.Mutex
111 cond sync.Cond // L = μ one cond for every state change, all are rare
112
113 ring [ringSize]event
114 head int
115 n int
116
117 pausing bool
118 readerParked bool
119 readerExit bool
120 stopped bool
121 held []event
122 spare []event // capacity reused by the next held generation
123 fatal error
124
125 wake chan struct{}
126}
127
128func newQueue(pausing bool) *queue {
129 q := &queue{pausing: pausing, wake: make(chan struct{}, 1)}
130 q.cond.L = &q.mu
131 return q
132}
133
134func (q *queue) signal() {
135 select {
136 case q.wake <- struct{}{}:
137 default:
138 }
139}
140
141// post queues ev. It blocks while the ring is full and not pausing.
142func (q *queue) post(ev *event) {
143 q.mu.Lock()
144 for {
145 switch {
146 case q.stopped:
147 q.mu.Unlock()
148 ev.closeFD()
149 return
150 case q.pausing:
151 q.held = append(q.held, *ev)
152 q.mu.Unlock()
153 return
154 case q.n < ringSize:
155 q.ring[(q.head+q.n)%ringSize] = *ev
156 q.n++
157 q.mu.Unlock()
158 q.signal()
159 return
160 }
161 q.cond.Wait()
162 }
163}
164
165// pop moves the oldest queued event into ev.
166func (q *queue) pop(ev *event) bool {
167 q.mu.Lock()
168 if q.n == 0 {
169 q.mu.Unlock()
170 return false
171 }
172 *ev = q.ring[q.head]
173 q.ring[q.head] = event{} // ownership of a carried descriptor moved to ev; leave no key data behind
174 q.head = (q.head + 1) % ringSize
175 full := q.n == ringSize
176 q.n--
177 if full {
178 q.cond.Broadcast()
179 }
180 q.mu.Unlock()
181 return true
182}
183
184// setFatal records the first transport failure; Dispatch returns it once the
185// events queued before it are drained.
186func (q *queue) setFatal(err error) {
187 q.mu.Lock()
188 if q.fatal == nil && !q.stopped {
189 q.fatal = err
190 }
191 q.mu.Unlock()
192 q.signal()
193}
194
195func (q *queue) fatalErr() error {
196 q.mu.Lock()
197 err := q.fatal
198 q.mu.Unlock()
199 return err
200}
201
202func (q *queue) isStopped() bool {
203 q.mu.Lock()
204 s := q.stopped
205 q.mu.Unlock()
206 return s
207}
208
209func (q *queue) stop() {
210 q.mu.Lock()
211 q.stopped = true
212 q.cond.Broadcast()
213 q.mu.Unlock()
214}
215
216// setPausing starts or ends a pause.
217func (q *queue) setPausing(v bool) {
218 q.mu.Lock()
219 q.pausing = v
220 q.cond.Broadcast()
221 q.mu.Unlock()
222}
223
224// endPause ends a pause started by setPausing(true). Events producers parked
225// meanwhile are moved into the ring, in order, as far as it has room, so that
226// everything handled during the pause is visible to the next Dispatch. The
227// remainder stays at the front of held; the reader re-posts it when it resumes.
228func (q *queue) endPause() {
229 q.mu.Lock()
230 m := min(len(q.held), ringSize-q.n)
231 for i := range m {
232 q.ring[(q.head+q.n)%ringSize] = q.held[i]
233 q.n++
234 }
235 if m > 0 {
236 q.held = q.held[:copy(q.held, q.held[m:])]
237 }
238 q.pausing = false
239 q.cond.Broadcast()
240 q.mu.Unlock()
241 if m > 0 {
242 q.signal()
243 }
244}
245
246// awaitParked blocks until the reader is parked or gone.
247func (q *queue) awaitParked() {
248 q.mu.Lock()
249 for !q.readerParked && !q.readerExit {
250 q.cond.Wait()
251 }
252 q.mu.Unlock()
253}
254
255// gate runs on the reader goroutine before every read. It parks the reader
256// while a pause is requested and re-posts what producers parked meanwhile.
257// It reports false when the connection is closing.
258func (q *queue) gate() bool {
259 q.mu.Lock()
260 for q.pausing && !q.stopped {
261 if !q.readerParked {
262 q.readerParked = true
263 q.cond.Broadcast()
264 }
265 q.cond.Wait()
266 }
267 wasParked := q.readerParked
268 q.readerParked = false
269 stopped := q.stopped
270 q.mu.Unlock()
271 if stopped {
272 return false
273 }
274 if wasParked {
275 q.flushHeld()
276 }
277 return true
278}
279
280func (q *queue) flushHeld() {
281 q.mu.Lock()
282 held := q.held
283 q.held = q.spare[:0]
284 q.mu.Unlock()
285 for i := range held {
286 q.post(&held[i])
287 }
288 q.mu.Lock()
289 q.spare = held[:0]
290 q.mu.Unlock()
291}
292
293// takeHeld returns and clears the parked events. Only valid while no reader
294// runs (during setup).
295func (q *queue) takeHeld() []event {
296 q.mu.Lock()
297 held := q.held
298 q.held = nil
299 q.mu.Unlock()
300 return held
301}
302
303// hasLocked reports whether an evLocked for lock generation gen is queued or
304// parked.
305func (q *queue) hasLocked(gen uint32) bool {
306 q.mu.Lock()
307 defer q.mu.Unlock()
308 for i := range q.n {
309 if e := &q.ring[(q.head+i)%ringSize]; e.kind == evLocked && e.id == gen {
310 return true
311 }
312 }
313 for i := range q.held {
314 if q.held[i].kind == evLocked && q.held[i].id == gen {
315 return true
316 }
317 }
318 return false
319}
320
321// closeFDs closes the descriptors owned by events nobody will drain. Only
322// valid once both producers have stopped.
323func (q *queue) closeFDs() {
324 q.mu.Lock()
325 for i := range q.n {
326 q.ring[(q.head+i)%ringSize].closeFD()
327 }
328 for i := range q.held {
329 q.held[i].closeFD()
330 }
331 q.mu.Unlock()
332}
333
334// length reports how many events are queued.
335func (q *queue) length() int {
336 q.mu.Lock()
337 n := q.n
338 q.mu.Unlock()
339 return n
340}
341
342// pending reports whether events are queued.
343func (q *queue) pending() bool {
344 q.mu.Lock()
345 n := q.n
346 q.mu.Unlock()
347 return n > 0
348}
349
350func (q *queue) readerDone() {
351 q.mu.Lock()
352 q.readerExit = true
353 q.cond.Broadcast()
354 q.mu.Unlock()
355}