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