detached c83f653

merge: generated core bindings and hardened transport (Veya P2)

commit
c83f653555760c2695fdf83ac68e470d4d385fb0
author
brice · · 2026-09-28 07:48 UTC
merge of
ab733e5 + 1cc5bed · diff against the first parent
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
24 files · +10254 -762
A benchmark_test.go +26 -0
@@ -0,0 +1,26 @@
1+package wlturbo
2+
3+import "testing"
4+
5+// BenchmarkWireDispatch isolates framed typed dispatch from socket scheduling.
6+func BenchmarkWireDispatch(b *testing.B) {
7+ d := newDisplay(&chunkConn{})
8+ p := &benchProxy{BaseProxy: BaseProxy{id: 7, context: d.context}}
9+ d.context.Register(p)
10+ f := receivedFrame{object: 7, opcode: 0, body: []byte{1, 0, 0, 0}}
11+ b.ReportAllocs()
12+ b.ResetTimer()
13+ for i := 0; i < b.N; i++ {
14+ if err := d.dispatchFrame(f); err != nil {
15+ b.Fatal(err)
16+ }
17+ }
18+}
19+
20+type benchProxy struct {
21+ BaseProxy
22+ value uint32
23+}
24+
25+func (p *benchProxy) EventSignature(op uint16) (string, bool) { return "uint,", op == 0 }
26+func (p *benchProxy) Dispatch(e *Event) { p.value = e.Uint32() }
M client.go +259 -228
@@ -13,6 +13,7 @@ import (
1313 "net"
1414 "os"
1515 "path/filepath"
16+ "strings"
1617 "sync"
1718 "sync/atomic"
1819 )
@@ -44,13 +45,19 @@ type Object interface {
4445
4546 // Display represents a connection to the Wayland display
4647 type Display struct {
47- conn net.Conn
48- unix *net.UnixConn // set when conn is a Unix socket, nil otherwise
49- objects sync.Map // map[uint32]Object
50- nextID uint32
51- sendMu sync.Mutex
52- recvMu sync.Mutex
53- listeners sync.Map // map[uint32]map[uint16][]func([]byte)
48+ conn net.Conn
49+ unix *net.UnixConn // set when conn is a Unix socket, nil otherwise
50+ objects sync.Map // map[uint32]Object
51+ nextID uint32
52+ idMu sync.Mutex
53+ freeIDs []uint32
54+ retiredIDs map[uint32]bool
55+ sendMu sync.Mutex
56+ recvMu sync.Mutex
57+ dispatchMu sync.Mutex // serialize readers without holding receive state across handlers
58+ signatures sync.Map
59+ listenerMu sync.Mutex
60+ listeners sync.Map // map[uint32]map[uint16][]func([]byte)
5461
5562 // High-performance event dispatcher
5663 dispatcher *EventDispatcher
@@ -73,7 +80,6 @@ type Display struct {
7380 readErr error
7481 maxMsgLen uint32
7582 closed atomic.Bool
76- fdMu sync.Mutex
7783 pendingFDs []int
7884 }
7985
@@ -103,11 +109,12 @@ func newDisplay(conn net.Conn) *Display {
103109
104110 // Registry represents the global registry
105111 type Registry struct {
106- id uint32
107- display *Display
108- globals map[uint32]Global
109- mu sync.RWMutex
110- handlers map[string]GlobalHandler
112+ id uint32
113+ display *Display
114+ globals map[uint32]Global
115+ mu sync.RWMutex
116+ handlers map[string]GlobalHandler
117+ removeHandlers []RegistryGlobalRemoveHandler
111118 }
112119
113120 // callbackObject represents a wl_callback object
@@ -119,6 +126,12 @@ type callbackObject struct {
119126 func (c *callbackObject) ID() uint32 {
120127 return c.id
121128 }
129+func (c *callbackObject) EventSignature(op uint16) (string, bool) {
130+ if op == 0 {
131+ return "uint,", true
132+ }
133+ return "", false
134+}
122135
123136 // Dispatch handles callback events (opcode 0 = done)
124137 func (c *callbackObject) Dispatch(event *Event) {
@@ -126,6 +139,7 @@ func (c *callbackObject) Dispatch(event *Event) {
126139 return
127140 }
128141 c.display.notifyListeners(c.id, 0, event.data)
142+ c.display.context.Unregister(c)
129143 }
130144
131145 // Global represents a global object
@@ -138,6 +152,16 @@ type Global struct {
138152 // GlobalHandler is called when a global is announced
139153 type GlobalHandler func(registry *Registry, name uint32, version uint32)
140154
155+// ConnectFromConn attaches to an established Wayland socket and requests its registry.
156+func ConnectFromConn(conn net.Conn) (*Display, error) {
157+ d := newDisplay(conn)
158+ if err := d.getRegistry(); err != nil {
159+ _ = d.Close()
160+ return nil, fmt.Errorf("get registry: %w", err)
161+ }
162+ return d, nil
163+}
164+
141165 // Connect connects to the Wayland display and requests the registry.
142166 func Connect(socketPath string) (*Display, error) {
143167 if socketPath == "" {
@@ -161,16 +185,7 @@ func Connect(socketPath string) (*Display, error) {
161185 return nil, fmt.Errorf("failed to connect to Wayland: %w", err)
162186 }
163187
164- d := newDisplay(conn)
165-
166- // Get registry
167- if err := d.getRegistry(); err != nil {
168- _ = d.Close()
169- return nil, fmt.Errorf("failed to get registry: %w", err)
170- }
171-
172- // Don't do initial roundtrip here - let the caller do it after setting up handlers
173- return d, nil
188+ return ConnectFromConn(conn)
174189 }
175190
176191 // Close closes the display connection. It is safe to call more than once;
@@ -179,8 +194,12 @@ func (d *Display) Close() error {
179194 if !d.closed.CompareAndSwap(false, true) {
180195 return nil
181196 }
197+ d.context.closed.Store(true)
198+ err := d.conn.Close()
199+ d.recvMu.Lock()
182200 d.closePendingFDs()
183- return d.conn.Close()
201+ d.recvMu.Unlock()
202+ return err
184203 }
185204
186205 // Closed reports whether the connection has been closed.
@@ -200,7 +219,20 @@ func (d *Display) RegisterEventHandler(objectID uint32, opcode uint16, handler E
200219
201220 // allocateID allocates a new object ID
202221 func (d *Display) allocateID() uint32 {
203- return atomic.AddUint32(&d.nextID, 1) - 1
222+ d.idMu.Lock()
223+ defer d.idMu.Unlock()
224+ if n := len(d.freeIDs); n != 0 {
225+ id := d.freeIDs[n-1]
226+ d.freeIDs = d.freeIDs[:n-1]
227+ delete(d.retiredIDs, id)
228+ return id
229+ }
230+ if d.nextID == 0 || d.nextID >= 0xff000000 {
231+ return 0 // exhausted; never allocate a reserved server ID
232+ }
233+ id := d.nextID
234+ d.nextID++
235+ return id
204236 }
205237
206238 // AllocateID allocates a new object ID (public method)
@@ -215,6 +247,12 @@ func (d *Display) SendRequest(objectID uint32, opcode uint16, args ...interface{
215247
216248 // SendRequestWithFDs sends a request with file descriptors
217249 func (d *Display) SendRequestWithFDs(objectID uint32, opcode uint16, fds []int, args ...interface{}) error {
250+ return d.sendRequest(objectID, opcode, fds, nil, args)
251+}
252+
253+// sendRequest marshals a request, then runs guard and writes the message
254+// under the send lock. Guard failures and marshaling errors write nothing.
255+func (d *Display) sendRequest(objectID uint32, opcode uint16, fds []int, guard func() error, args []interface{}) error {
218256 // Get buffer from pool
219257 buf := bufferPool.Get().(*bytes.Buffer)
220258 defer func() {
@@ -235,8 +273,8 @@ func (d *Display) SendRequestWithFDs(objectID uint32, opcode uint16, fds []int,
235273
236274 // Update header with actual size in bytes
237275 bufLen := buf.Len()
238- if bufLen > 0xFFFF {
239- return fmt.Errorf("message too large: %d bytes", bufLen)
276+ if bufLen > 0xFFFF || bufLen%4 != 0 {
277+ return fmt.Errorf("invalid message size: %d bytes", bufLen)
240278 }
241279 size := uint32(bufLen) // Safe: checked above
242280 binary.LittleEndian.PutUint32(header[0:4], objectID)
@@ -248,7 +286,7 @@ func (d *Display) SendRequestWithFDs(objectID uint32, opcode uint16, fds []int,
248286 copy(data[0:8], header)
249287
250288 // Send message with optional file descriptors
251- return d.sendmsgWithFDs(data, fds)
289+ return d.sendmsgWithFDs(data, fds, guard)
252290 }
253291
254292 // marshalArg marshals a single argument
@@ -263,7 +301,7 @@ func (d *Display) marshalArg(buf *bytes.Buffer, arg interface{}) error {
263301 case string:
264302 // String format: length (including null) + string + null + padding
265303 strlen := len(v) + 1
266- if strlen > 0xFFFFFFFF {
304+ if strlen > 0xffff-HeaderSize {
267305 return fmt.Errorf("string too long: %d bytes", strlen)
268306 }
269307 if err := binary.Write(buf, binary.LittleEndian, uint32(strlen)); err != nil { // Safe: checked above
@@ -279,7 +317,7 @@ func (d *Display) marshalArg(buf *bytes.Buffer, arg interface{}) error {
279317 case []byte:
280318 // Array format: length + data + padding
281319 arrlen := len(v)
282- if arrlen > 0xFFFFFFFF {
320+ if arrlen > 0xffff-HeaderSize {
283321 return fmt.Errorf("array too long: %d bytes", arrlen)
284322 }
285323 if err := binary.Write(buf, binary.LittleEndian, uint32(arrlen)); err != nil {
@@ -321,14 +359,20 @@ func (d *Display) marshalArg(buf *bytes.Buffer, arg interface{}) error {
321359 // Dispatch returns net.ErrClosed after Close, a typed ProtocolError for a
322360 // malformed frame, and io.ErrUnexpectedEOF when the peer closes mid-frame.
323361 func (d *Display) Dispatch() error {
362+ d.dispatchMu.Lock()
363+ defer d.dispatchMu.Unlock()
324364 d.recvMu.Lock()
325- defer d.recvMu.Unlock()
326365
327366 if d.closed.Load() {
367+ d.recvMu.Unlock()
328368 return net.ErrClosed
329369 }
330370
331371 frame, err := d.nextFrame()
372+ d.recvMu.Unlock()
373+ if d.closed.Load() {
374+ return net.ErrClosed
375+ }
332376 if err != nil {
333377 return d.fail(err)
334378 }
@@ -342,10 +386,15 @@ func (d *Display) fail(err error) error {
342386 if err == nil {
343387 return nil
344388 }
389+ d.recvMu.Lock()
390+ defer d.recvMu.Unlock()
345391 var perr *ProtocolError
346392 if errors.As(err, &perr) {
347393 d.closePendingFDs()
348394 }
395+ if errors.Is(err, io.ErrUnexpectedEOF) {
396+ d.closePendingFDs()
397+ }
349398 return err
350399 }
351400
@@ -376,6 +425,9 @@ func (d *Display) nextFrame() (receivedFrame, error) {
376425 }
377426 }
378427 if err := d.fill(); err != nil {
428+ if d.closed.Load() {
429+ return receivedFrame{}, net.ErrClosed
430+ }
379431 return receivedFrame{}, err
380432 }
381433 }
@@ -416,40 +468,129 @@ func (d *Display) fill() error {
416468 return nil
417469 }
418470
419-// dispatchFrame delivers one complete frame to the object that owns it.
471+// signatureProvider is implemented by generated objects; every opcode must
472+// have a known signature before any descriptor can be assigned or dispatched.
473+type signatureProvider interface{ EventSignature(uint16) (string, bool) }
474+
475+// dispatchFrame delivers one complete frame with precisely its signature's FDs.
420476 func (d *Display) dispatchFrame(f receivedFrame) error {
421- if f.object == displayObjectID {
477+ d.recvMu.Lock()
478+ obj, ev, kind, err := d.prepareFrame(f)
479+ d.recvMu.Unlock()
480+ if err != nil {
481+ return err
482+ }
483+ switch kind {
484+ case 1:
422485 return d.handleDisplayEvent(f.opcode, f.body)
486+ case 2:
487+ d.notifyListeners(f.object, f.opcode, f.body)
488+ return nil
489+ }
490+ defer func() {
491+ for _, fd := range ev.fds {
492+ _ = fd.Close()
493+ }
494+ *ev = Event{}
495+ eventPool.Put(ev)
496+ }()
497+ if proxy, ok := obj.(Proxy); ok {
498+ proxy.Dispatch(ev)
499+ } else {
500+ d.dispatcher.Dispatch(f.object, f.opcode, f.body)
501+ d.notifyListeners(f.object, f.opcode, f.body)
423502 }
503+ return nil
504+}
424505
506+// prepareFrame assigns owned descriptors under recvMu, but never calls user code.
507+func (d *Display) prepareFrame(f receivedFrame) (Object, *Event, uint8, error) {
508+ if d.closed.Load() {
509+ return nil, nil, 0, net.ErrClosed
510+ }
511+ if f.object == displayObjectID {
512+ if len(d.pendingFDs) != 0 && len(d.rbuf) == 0 {
513+ return nil, nil, 0, &ProtocolError{Kind: "extra_fd", Err: ErrMalformedFrame}
514+ }
515+ return nil, nil, 1, nil
516+ }
517+ if f.object == d.registry.id {
518+ var sig string
519+ switch f.opcode {
520+ case 0:
521+ sig = "uint,string,uint,"
522+ case 1:
523+ sig = "uint,"
524+ default:
525+ return nil, nil, 0, &ProtocolError{Kind: "unknown_opcode", Object: f.object, Opcode: f.opcode, Err: ErrUnknownOpcode}
526+ }
527+ if err := validateEventBody(sig, f.body); err != nil {
528+ return nil, nil, 0, &ProtocolError{Kind: "malformed_payload", Object: f.object, Opcode: f.opcode, Err: err}
529+ }
530+ if len(d.pendingFDs) != 0 && len(d.rbuf) == 0 {
531+ return nil, nil, 0, &ProtocolError{Kind: "extra_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}
532+ }
533+ return nil, nil, 2, nil
534+ }
425535 obj, ok := d.objects.Load(f.object)
426536 if !ok {
427- return &ProtocolError{
428- Kind: "unknown_object",
429- Object: f.object,
430- Opcode: f.opcode,
431- Size: uint32(len(f.body)) + HeaderSize,
432- Err: ErrUnknownObject,
537+ return nil, nil, 0, &ProtocolError{Kind: "unknown_object", Object: f.object, Opcode: f.opcode, Err: ErrUnknownObject}
538+ }
539+ sig, valid := "", false
540+ p, isGenerated := obj.(signatureProvider)
541+ if isGenerated {
542+ sig, valid = p.EventSignature(f.opcode)
543+ }
544+ // Legacy custom proxies may declare their signatures with RegisterEventSignature.
545+ if !valid && !isGenerated {
546+ if v, ok := d.signatures.Load(signatureKey{f.object, f.opcode}); ok {
547+ sig, valid = v.(string), true
548+ }
549+ }
550+ if !valid {
551+ return nil, nil, 0, &ProtocolError{Kind: "unknown_opcode", Object: f.object, Opcode: f.opcode, Err: ErrUnknownOpcode}
552+ }
553+ if err := validateEventBody(sig, f.body); err != nil {
554+ return nil, nil, 0, &ProtocolError{Kind: "malformed_payload", Object: f.object, Opcode: f.opcode, Err: err}
555+ }
556+ count := strings.Count(sig, "fd,")
557+ if len(d.pendingFDs) < count {
558+ return nil, nil, 0, &ProtocolError{Kind: "missing_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}
559+ }
560+ ev := eventPool.Get().(*Event)
561+ *ev = Event{ProxyID: f.object, Opcode: f.opcode, data: f.body, display: d}
562+ if count != 0 {
563+ ev.fds = make([]*OwnedFD, count)
564+ for i := range ev.fds {
565+ ev.fds[i] = &OwnedFD{fd: d.pendingFDs[i]}
566+ }
567+ d.pendingFDs = d.pendingFDs[count:]
568+ }
569+ // FDs from a single recvmsg can accompany multiple coalesced frames. Once
570+ // all buffered bytes have been framed, surplus descriptors are not legal.
571+ if len(d.rbuf) == 0 && len(d.pendingFDs) != 0 {
572+ for _, fd := range ev.fds {
573+ _ = fd.Close()
433574 }
575+ *ev = Event{}
576+ eventPool.Put(ev)
577+ return nil, nil, 0, &ProtocolError{Kind: "extra_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}
434578 }
579+ return obj.(Object), ev, 0, nil
580+}
435581
436- // Server-created objects are announced by an event on a known object.
437- d.handleServerObject(f.object, f.opcode, f.body)
582+type signatureKey struct {
583+ object uint32
584+ opcode uint16
585+}
438586
439- if proxy, ok := obj.(Proxy); ok && proxy != nil {
440- proxy.Dispatch(&Event{
441- ProxyID: f.object,
442- Opcode: f.opcode,
443- data: f.body,
444- display: d,
445- })
446- return nil
587+// RegisterEventSignature defines a signature for a legacy proxy. Generated
588+// proxies supply it themselves and cannot be overridden.
589+func (d *Display) RegisterEventSignature(object uint32, opcode uint16, signature string) {
590+ if object == 0 || object == displayObjectID {
591+ return
447592 }
448-
449- // High-performance dispatcher, then compatibility listeners.
450- d.dispatcher.Dispatch(f.object, f.opcode, f.body)
451- d.notifyListeners(f.object, f.opcode, f.body)
452- return nil
593+ d.signatures.Store(signatureKey{object, opcode}, signature)
453594 }
454595
455596 // notifyListeners runs the listeners registered for an object and opcode.
@@ -466,11 +607,7 @@ func (d *Display) notifyListeners(objectID uint32, opcode uint16, body []byte) {
466607 if !ok {
467608 return
468609 }
469- handlerSlice, ok := handlers.(*[]func([]byte))
470- if !ok || handlerSlice == nil {
471- return
472- }
473- for _, handler := range *handlerSlice {
610+ for _, handler := range handlers.([]func([]byte)) {
474611 if handler != nil {
475612 handler(body)
476613 }
@@ -481,8 +618,8 @@ func (d *Display) notifyListeners(objectID uint32, opcode uint16, body []byte) {
481618 func (d *Display) handleDisplayEvent(opcode uint16, data []byte) error {
482619 switch opcode {
483620 case 0: // error
484- if len(data) < 8 {
485- return &ProtocolError{Kind: "short_display_error", Object: displayObjectID, Opcode: opcode, Size: uint32(len(data)) + HeaderSize, Err: ErrMalformedFrame}
621+ if err := validateEventBody("object,uint,string,", data); err != nil {
622+ return &ProtocolError{Kind: "malformed_display_error", Err: err}
486623 }
487624 event := &Event{ProxyID: displayObjectID, Opcode: opcode, data: data, display: d}
488625 objectID := event.Uint32()
@@ -495,11 +632,22 @@ func (d *Display) handleDisplayEvent(opcode uint16, data []byte) error {
495632 return d.lastError
496633
497634 case 1: // delete_id
498- if len(data) < 4 {
635+ if len(data) != 4 {
499636 return &ProtocolError{Kind: "short_delete_id", Object: displayObjectID, Opcode: opcode, Size: uint32(len(data)) + HeaderSize, Err: ErrMalformedFrame}
500637 }
501638 id := binary.LittleEndian.Uint32(data[0:4])
502- d.objects.Delete(id)
639+ d.idMu.Lock()
640+ _, live := d.objects.Load(id)
641+ if id < 2 || id >= 0xff000000 || id >= d.nextID || live || d.retiredIDs[id] {
642+ d.idMu.Unlock()
643+ return &ProtocolError{Kind: "invalid_delete_id", Object: id, Err: ErrMalformedFrame}
644+ }
645+ if d.retiredIDs == nil {
646+ d.retiredIDs = make(map[uint32]bool)
647+ }
648+ d.retiredIDs[id] = true
649+ d.freeIDs = append(d.freeIDs, id)
650+ d.idMu.Unlock()
503651 return nil
504652
505653 default:
@@ -507,55 +655,42 @@ func (d *Display) handleDisplayEvent(opcode uint16, data []byte) error {
507655 }
508656 }
509657
510-// Roundtrip performs a synchronous roundtrip to the compositor
658+// Roundtrip waits for one wl_display.sync callback. Concurrent event loops
659+// must not call Roundtrip while another goroutine dispatches this connection.
511660 func (d *Display) Roundtrip() error {
512- callbackID := d.allocateID()
513- done := make(chan error, 1)
514-
515- d.AddListener(callbackID, 0, func(_ []byte) {
516- d.objects.Delete(callbackID)
517- done <- nil
518- })
519-
520- // Send sync request (opcode 0)
521- if err := d.SendRequest(displayObjectID, 0, callbackID); err != nil {
661+ callback, err := d.Sync()
662+ if err != nil {
522663 return err
523664 }
524-
525- d.objects.Store(callbackID, &callbackObject{
526- BaseProxy: BaseProxy{
527- context: d.Context(),
528- id: callbackID,
529- },
530- display: d,
531- })
532-
533- for i := 0; i < 1000; i++ {
534- if err := d.Dispatch(); err != nil {
665+ for {
666+ if err = d.Dispatch(); err != nil {
535667 return err
536668 }
537- select {
538- case err := <-done:
539- return err
540- default:
669+ if _, exists := d.objects.Load(callback.ID()); !exists {
670+ return nil
541671 }
542672 }
543-
544- return fmt.Errorf("roundtrip failed: max iterations reached")
545673 }
546674
547675 // AddListener adds an event listener for an object
548676 func (d *Display) AddListener(objectID uint32, opcode uint16, handler func([]byte)) {
677+ if handler == nil {
678+ return
679+ }
549680 // Load or create listener map for object
550681 listeners, _ := d.listeners.LoadOrStore(objectID, &sync.Map{})
551682 opcodeMap := listeners.(*sync.Map)
552683
553- // Load or create handlers slice for opcode
554- handlers, _ := opcodeMap.LoadOrStore(opcode, &[]func([]byte){})
555- handlerSlice := handlers.(*[]func([]byte))
556-
557- // Add handler (thread-safe)
558- *handlerSlice = append(*handlerSlice, handler)
684+ d.listenerMu.Lock()
685+ defer d.listenerMu.Unlock()
686+ var handlers []func([]byte)
687+ if old, ok := opcodeMap.Load(opcode); ok {
688+ handlers = old.([]func([]byte))
689+ }
690+ next := make([]func([]byte), len(handlers)+1)
691+ copy(next, handlers)
692+ next[len(handlers)] = handler
693+ opcodeMap.Store(opcode, next)
559694 }
560695
561696 // getRegistry gets the global registry
@@ -581,15 +716,13 @@ func (r *Registry) ID() uint32 {
581716 // handleGlobal handles global announcements
582717 func (r *Registry) handleGlobal(data []byte) {
583718 if len(data) < 8 {
584- // log.Printf("wlclient: handleGlobal: data too short (%d bytes)", len(data))
585719 return
586720 }
587721
588722 name := binary.LittleEndian.Uint32(data[0:4])
589723 ifaceLen := binary.LittleEndian.Uint32(data[4:8])
590724
591- if len(data) < 8+int(ifaceLen)+4 {
592- // log.Printf("wlclient: handleGlobal: insufficient data for interface string (need %d, have %d)", 8+int(ifaceLen)+4, len(data))
725+ if ifaceLen == 0 || uint64(ifaceLen)+12 > uint64(len(data)) || data[8+ifaceLen-1] != 0 {
593726 return
594727 }
595728
@@ -600,15 +733,12 @@ func (r *Registry) handleGlobal(data []byte) {
600733 padding := (4 - (ifaceLen % 4)) % 4
601734 versionOffset := 8 + int(ifaceLen) + int(padding)
602735
603- if len(data) < versionOffset+4 {
604- // log.Printf("wlclient: handleGlobal: insufficient data for version (need %d, have %d)", versionOffset+4, len(data))
736+ if len(data) != versionOffset+4 {
605737 return
606738 }
607739
608740 version := binary.LittleEndian.Uint32(data[versionOffset:])
609741
610- // log.Printf("wlclient: global announced: %s v%d (name=%d)", iface, version, name)
611-
612742 // Store global
613743 r.mu.Lock()
614744 r.globals[name] = Global{
@@ -619,15 +749,19 @@ func (r *Registry) handleGlobal(data []byte) {
619749 r.mu.Unlock()
620750
621751 // Call specific handler if registered
622- if handler, ok := r.handlers[iface]; ok {
623- // log.Printf("wlclient: calling handler for interface %s", iface)
752+ r.mu.RLock()
753+ handler, specific := r.handlers[iface]
754+ wildcard, all := r.handlers["*"]
755+ r.mu.RUnlock()
756+ if specific {
757+
624758 handler(r, name, version)
625759 }
626760
627761 // Call wildcard handler if registered
628- if handler, ok := r.handlers["*"]; ok {
629- // log.Printf("wlclient: calling wildcard handler for interface %s", iface)
630- handler(r, name, version)
762+ if all {
763+
764+ wildcard(r, name, version)
631765 }
632766 }
633767
@@ -641,11 +775,17 @@ func (r *Registry) handleGlobalRemove(data []byte) {
641775
642776 r.mu.Lock()
643777 delete(r.globals, name)
778+ handlers := append([]RegistryGlobalRemoveHandler(nil), r.removeHandlers...)
644779 r.mu.Unlock()
780+ for _, handler := range handlers {
781+ handler.HandleRegistryGlobalRemove(RegistryGlobalRemoveEvent{Registry: r, Name: name})
782+ }
645783 }
646784
647785 // AddHandler adds a handler for a specific interface
648786 func (r *Registry) AddHandler(iface string, handler GlobalHandler) {
787+ r.mu.Lock()
788+ defer r.mu.Unlock()
649789 r.handlers[iface] = handler
650790 }
651791
@@ -656,7 +796,7 @@ func (r *Registry) Bind(name uint32, iface string, version uint32, proxy Proxy)
656796 proxy.SetID(r.display.allocateID())
657797 }
658798
659- // Ensure proxy has a context set - CRITICAL FIX
799+ // Bootstrap a generated proxy with this display's context.
660800 if proxy.Context() == nil {
661801 if baseProxy, ok := proxy.(*BaseProxy); ok {
662802 baseProxy.SetContext(r.display.Context())
@@ -667,12 +807,18 @@ func (r *Registry) Bind(name uint32, iface string, version uint32, proxy Proxy)
667807 }
668808 }
669809
810+ if proxy.Context() != r.display.Context() {
811+ return fmt.Errorf("proxy belongs to another display")
812+ }
813+ if proxy.ID() == displayObjectID || proxy.ID() == r.id {
814+ return fmt.Errorf("reserved object ID")
815+ }
816+ if old, exists := r.display.objects.Load(proxy.ID()); exists && old != proxy {
817+ return fmt.Errorf("object ID %d already registered", proxy.ID())
818+ }
670819 // Register the proxy
671820 proxy.Context().Register(proxy)
672821
673- // Also register directly in display objects map
674- r.display.objects.Store(proxy.ID(), proxy)
675-
676822 // Send bind request (opcode 0) with proper arguments
677823 if err := r.display.SendRequest(r.id, 0, name, iface, version, proxy.ID()); err != nil {
678824 proxy.Context().Unregister(proxy)
@@ -718,118 +864,3 @@ func (r *Registry) FindGlobal(iface string) (Global, bool) {
718864 }
719865 return Global{}, false
720866 }
721-
722-// handleServerObject creates and registers server-allocated objects from events
723-func (d *Display) handleServerObject(objectID uint32, opcode uint16, body []byte) bool {
724- // Handle specific known protocols that create server objects
725- obj, ok := d.objects.Load(objectID)
726- if !ok {
727- return false
728- }
729-
730- // Check if this is a known object type that creates server objects
731- switch obj.(type) {
732- case *Registry:
733- // Registry doesn't create server objects directly
734- return false
735-
736- default:
737- // Check if this might be an output manager (interface zwlr_output_manager_v1)
738- if objectID == 5 { // This is likely the output manager based on your logs
739- return d.handleOutputManagerEvent(objectID, opcode, body)
740- }
741- }
742-
743- return false
744-}
745-
746-// handleOutputManagerEvent handles events from zwlr_output_manager_v1
747-func (d *Display) handleOutputManagerEvent(objectID uint32, opcode uint16, body []byte) bool {
748- switch opcode {
749- case 0: // head event - creates new zwlr_output_head_v1 object
750- if len(body) < 4 {
751- return false
752- }
753-
754- // Parse the new_id for the head object
755- headID := binary.LittleEndian.Uint32(body[0:4])
756-
757- // Create a proper OutputHead object
758- headProxy := &OutputHead{
759- BaseProxy: BaseProxy{
760- id: headID,
761- context: d.Context(),
762- },
763- }
764-
765- // Register the new head object
766- d.objects.Store(headID, headProxy)
767- return true
768-
769- case 1: // done event
770- return false
771-
772- case 2: // finished event
773- return false
774- }
775-
776- return false
777-}
778-
779-// OutputHead represents a zwlr_output_head_v1 object
780-type OutputHead struct {
781- BaseProxy
782- name string
783- description string
784- width int32
785- height int32
786-}
787-
788-// Dispatch handles head events
789-func (h *OutputHead) Dispatch(event *Event) {
790- switch event.Opcode {
791- case 0: // name
792- h.name = event.String()
793- case 1: // description
794- h.description = event.String()
795- case 2: // physical_size
796- h.width = event.Int32()
797- h.height = event.Int32()
798- case 3: // mode (creates new mode object)
799- modeID := event.Uint32()
800- // Create mode object
801- mode := &OutputMode{
802- BaseProxy: BaseProxy{
803- id: modeID,
804- context: h.context,
805- },
806- }
807- h.context.display.objects.Store(modeID, mode)
808- case 9: // finished
809- h.context.Unregister(h)
810- default:
811- }
812-}
813-
814-// OutputMode represents a zwlr_output_mode_v1 object
815-type OutputMode struct {
816- BaseProxy
817- width int32
818- height int32
819- refresh int32
820-}
821-
822-// Dispatch handles mode events
823-func (m *OutputMode) Dispatch(event *Event) {
824- switch event.Opcode {
825- case 0: // size
826- m.width = event.Int32()
827- m.height = event.Int32()
828- case 1: // refresh
829- m.refresh = event.Int32()
830- case 2: // preferred
831- case 3: // finished
832- m.context.Unregister(m)
833- default:
834- }
835-}
A cmd/wlturbo-scanner/main.go +132 -0
@@ -0,0 +1,132 @@
1+// Command wlturbo-scanner generates Go bindings from Wayland protocol XML files.
2+//
3+// Usage:
4+//
5+// wlturbo-scanner [flags] protocol.xml
6+//
7+// Flags:
8+//
9+// -o, --output Output file path (default: <protocol>_generated.go)
10+// -p, --package Go package name (default: derived from protocol name)
11+// -h, --help Show help
12+//
13+// Examples:
14+//
15+// # Generate bindings for virtual pointer protocol
16+// wlturbo-scanner -o virtual_pointer.go ../wlr-protocols/unstable/wlr-virtual-pointer-unstable-v1.xml
17+//
18+// # Generate with custom package name
19+// wlturbo-scanner -p vpointer -o vpointer.go protocol.xml
20+package main
21+
22+import (
23+ "flag"
24+ "fmt"
25+ "log"
26+ "os"
27+ "path/filepath"
28+ "strings"
29+
30+ "github.com/bnema/wlturbo/internal/scanner"
31+)
32+
33+func main() {
34+ var (
35+ outputPath string
36+ packageName string
37+ showHelp bool
38+ crossImports crossFlags
39+ )
40+
41+ flag.StringVar(&outputPath, "o", "", "Output file path")
42+ flag.StringVar(&outputPath, "output", "", "Output file path")
43+ flag.StringVar(&packageName, "p", "", "Go package name")
44+ flag.StringVar(&packageName, "package", "", "Go package name")
45+ flag.Var(&crossImports, "import", "Interface=Go import path (repeatable)")
46+ flag.BoolVar(&showHelp, "h", false, "Show help")
47+ flag.BoolVar(&showHelp, "help", false, "Show help")
48+
49+ flag.Usage = func() {
50+ fmt.Fprintf(os.Stderr, "Usage: %s [flags] protocol.xml\n\n", os.Args[0])
51+ fmt.Fprintf(os.Stderr, "Generates Go bindings from Wayland protocol XML files.\n\n")
52+ fmt.Fprintf(os.Stderr, "Flags:\n")
53+ flag.PrintDefaults()
54+ fmt.Fprintf(os.Stderr, "\nExamples:\n")
55+ fmt.Fprintf(os.Stderr, " # Generate bindings for virtual pointer protocol\n")
56+ fmt.Fprintf(os.Stderr, " %s -o virtual_pointer.go wlr-virtual-pointer-unstable-v1.xml\n\n", os.Args[0])
57+ fmt.Fprintf(os.Stderr, " # Generate with custom package name\n")
58+ fmt.Fprintf(os.Stderr, " %s -p vpointer -o vpointer.go protocol.xml\n", os.Args[0])
59+ }
60+
61+ flag.Parse()
62+
63+ if showHelp || flag.NArg() == 0 {
64+ flag.Usage()
65+ if showHelp {
66+ os.Exit(0)
67+ }
68+ os.Exit(1)
69+ }
70+
71+ xmlPath := flag.Arg(0)
72+
73+ // Derive output path if not specified
74+ if outputPath == "" {
75+ base := filepath.Base(xmlPath)
76+ base = strings.TrimSuffix(base, filepath.Ext(base))
77+ outputPath = base + "_generated.go"
78+ }
79+
80+ // Derive package name if not specified
81+ if packageName == "" {
82+ base := filepath.Base(xmlPath)
83+ base = strings.TrimSuffix(base, filepath.Ext(base))
84+
85+ // Clean up common prefixes/suffixes
86+ base = strings.TrimPrefix(base, "wlr-")
87+ base = strings.TrimPrefix(base, "zwlr-")
88+ base = strings.TrimSuffix(base, "-unstable-v1")
89+ base = strings.TrimSuffix(base, "-unstable-v2")
90+ base = strings.TrimSuffix(base, "-stable-v1")
91+
92+ // Convert to valid Go package name
93+ packageName = strings.ReplaceAll(base, "-", "_")
94+ }
95+
96+ // Create scanner
97+ s := scanner.NewScanner()
98+ s.CrossPackage = crossImports
99+
100+ // Parse XML
101+ if err := s.ParseXML(xmlPath); err != nil {
102+ log.Fatalf("Failed to parse XML: %v", err)
103+ }
104+
105+ // Generate code
106+ code, err := s.Generate(packageName)
107+ if err != nil {
108+ log.Fatalf("Failed to generate code: %v", err)
109+ }
110+
111+ // Write output with secure permissions
112+ if err := os.WriteFile(outputPath, code, 0600); err != nil {
113+ log.Fatalf("Failed to write output: %v", err)
114+ }
115+
116+ fmt.Printf("Generated %s (package %s)\n", outputPath, packageName)
117+}
118+
119+type crossFlags map[string]string
120+
121+func (f *crossFlags) String() string { return fmt.Sprint(map[string]string(*f)) }
122+func (f *crossFlags) Set(v string) error {
123+ parts := strings.SplitN(v, "=", 2)
124+ if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
125+ return fmt.Errorf("-import must be interface=GoImportPath")
126+ }
127+ if *f == nil {
128+ *f = make(map[string]string)
129+ }
130+ (*f)[parts[0]] = parts[1]
131+ return nil
132+}
A consumer_test.go +44 -0
@@ -0,0 +1,44 @@
1+//go:build linux
2+
3+package wlturbo
4+
5+import (
6+ "os"
7+ "os/exec"
8+ "path/filepath"
9+ "testing"
10+)
11+
12+// Run the public socketpair integration against a separate module, with no
13+// imports from internal/ and no worktree replacement outside its temp go.mod.
14+func TestExternalConsumer(t *testing.T) {
15+ goBin, err := exec.LookPath("go")
16+ if err != nil {
17+ t.Skip("go executable unavailable: external module integration requires go")
18+ }
19+ root, err := os.Getwd()
20+ if err != nil {
21+ t.Fatal(err)
22+ }
23+ temp := t.TempDir()
24+ mod := "module example.com/wlturbo-consumer\n\ngo 1.27\n\nrequire github.com/bnema/wlturbo v0.0.0\nreplace github.com/bnema/wlturbo => " + root + "\n"
25+ if err := os.WriteFile(filepath.Join(temp, "go.mod"), []byte(mod), 0600); err != nil {
26+ t.Fatal(err)
27+ }
28+ source, err := os.ReadFile(filepath.Join(root, "protocol/core/core_test.go"))
29+ if err != nil {
30+ t.Fatal(err)
31+ }
32+ // The external module has no access to the package's test helpers except
33+ // the ones copied into this temporary module. Compile and exercise only
34+ // the public integration scenarios, including typed key, frame and release.
35+ if err := os.WriteFile(filepath.Join(temp, "consumer_test.go"), source, 0600); err != nil {
36+ t.Fatal(err)
37+ }
38+ cmd := exec.Command(goBin, "test", "-run", "TestGeneratedCoreOverSocketpair|TestBufferReleaseAndOfferChild", "./...")
39+ cmd.Dir = temp
40+ cmd.Env = append(os.Environ(), "GOWORK=off", "GOFLAGS=-mod=mod")
41+ if output, err := cmd.CombinedOutput(); err != nil {
42+ t.Fatalf("external consumer: %v\n%s", err, output)
43+ }
44+}
M core.go +127 -379
@@ -3,10 +3,10 @@ package wlturbo
33 import (
44 "encoding/binary"
55 "errors"
6-
7- // "log"
86 "sync"
97 "sync/atomic"
8+
9+ "golang.org/x/sys/unix"
1010 )
1111
1212 // Context provides a compatibility layer for wl.Context
@@ -37,6 +37,8 @@ type Event struct {
3737 data []byte
3838 offset int
3939 display *Display
40+ fds []*OwnedFD
41+ fdIndex int
4042 }
4143
4244 // Data returns the raw event data
@@ -56,33 +58,75 @@ func NewContext(display *Display) *Context {
5658 }
5759 }
5860
59-// SendRequest sends a request through the context
61+// SendRequest sends a request through the context. The proxy is validated
62+// under the connection send lock, so no request reaches the wire after a
63+// destructor for the same proxy.
6064 func (c *Context) SendRequest(proxy Proxy, opcode uint32, args ...interface{}) error {
61- if c.closed.Load() {
62- return errors.New("context is closed")
63- }
64- return c.display.SendRequest(proxy.ID(), uint16(opcode), args...)
65+ return c.SendRequestWithFDs(proxy, opcode, nil, args...)
6566 }
6667
67-// SendRequestWithFDs sends a request with file descriptors through the context
68+// SendRequestWithFDs sends a request with file descriptors through the context.
69+// On error the FDs remain owned by the caller.
6870 func (c *Context) SendRequestWithFDs(proxy Proxy, opcode uint32, fds []int, args ...interface{}) error {
71+ if err := c.CheckProxy(proxy); err != nil {
72+ return err
73+ }
74+ return c.display.sendRequest(proxy.ID(), uint16(opcode), fds, func() error { return c.CheckProxy(proxy) }, args)
75+}
76+
77+// CheckProxy rejects stale and foreign proxies before any bytes are written.
78+func (c *Context) CheckProxy(proxy Proxy) error {
6979 if c.closed.Load() {
7080 return errors.New("context is closed")
7181 }
72- return c.display.SendRequestWithFDs(proxy.ID(), uint16(opcode), fds, args...)
82+ if proxy == nil || proxy.ID() == 0 || proxy.Context() != c {
83+ return errors.New("invalid proxy")
84+ }
85+ registered, ok := c.proxies.Load(proxy.ID())
86+ if !ok || registered != proxy {
87+ return errors.New("proxy is not registered")
88+ }
89+ return nil
90+}
91+
92+// SendDestructor sends a destructor request exactly once. The proxy is
93+// claimed (unregistered) under the connection send lock, after marshaling
94+// and immediately before the write: concurrent destructors and later requests
95+// on the proxy fail without writing. A marshaling error leaves the proxy
96+// registered. A write error after the claim leaves the proxy unregistered;
97+// such an error means the connection is broken and must be closed.
98+func (c *Context) SendDestructor(proxy Proxy, opcode uint32, args ...interface{}) error {
99+ return c.SendDestructorWithFDs(proxy, opcode, nil, args...)
100+}
101+
102+// SendDestructorWithFDs is SendDestructor for requests carrying FDs. On error
103+// the FDs remain owned by the caller.
104+func (c *Context) SendDestructorWithFDs(proxy Proxy, opcode uint32, fds []int, args ...interface{}) error {
105+ if err := c.CheckProxy(proxy); err != nil {
106+ return err
107+ }
108+ return c.display.sendRequest(proxy.ID(), uint16(opcode), fds, func() error { return c.claimDestroy(proxy) }, args)
109+}
110+
111+func (c *Context) claimDestroy(proxy Proxy) error {
112+ if err := c.CheckProxy(proxy); err != nil {
113+ return err
114+ }
115+ if !c.proxies.CompareAndDelete(proxy.ID(), proxy) {
116+ return errors.New("proxy is not registered")
117+ }
118+ c.display.objects.CompareAndDelete(proxy.ID(), proxy)
119+ return nil
73120 }
74121
75122 // Register registers a proxy object
76123 func (c *Context) Register(proxy Proxy) {
77- if proxy != nil && proxy.ID() != 0 {
124+ if proxy != nil && proxy.ID() != 0 && !c.closed.Load() {
125+ if proxy.Context() != c {
126+ return
127+ }
78128 c.proxies.Store(proxy.ID(), proxy)
79129 c.display.objects.Store(proxy.ID(), proxy)
80- // Debug large IDs
81- // if proxy.ID() > 1000000 {
82- // log.Printf("wlclient: WARNING: Registering proxy with suspicious ID %d (0x%x)", proxy.ID(), proxy.ID())
83- // } else if proxy.ID() == 5 {
84- // log.Printf("wlclient: Registered output manager proxy with ID 5")
85- // }
86130 }
87131 }
88132
@@ -221,19 +265,61 @@ func (e *Event) Array() []byte {
221265 return arr
222266 }
223267
224-// Fd reads a file descriptor that was delivered out-of-band for this event.
225-// Descriptors are queued on the connection in arrival order, as the Wayland
226-// wire protocol requires. The returned descriptor is owned by the caller,
227-// which must close it. It returns 0 when the event carried no descriptor.
268+// OwnedFD owns one received descriptor. Close is idempotent; Take transfers
269+// ownership to the caller (who must close it). Descriptor zero is valid.
270+type OwnedFD struct {
271+ mu sync.Mutex
272+ fd int
273+}
274+
275+func (f *OwnedFD) Take() (int, error) {
276+ if f == nil {
277+ return -1, errors.New("missing descriptor")
278+ }
279+ f.mu.Lock()
280+ defer f.mu.Unlock()
281+ if f.fd < 0 {
282+ return -1, errors.New("descriptor already taken or closed")
283+ }
284+ n := f.fd
285+ f.fd = -1
286+ return n, nil
287+}
288+func (f *OwnedFD) Close() error {
289+ if f == nil {
290+ return nil
291+ }
292+ f.mu.Lock()
293+ defer f.mu.Unlock()
294+ if f.fd < 0 {
295+ return nil
296+ }
297+ n := f.fd
298+ f.fd = -1
299+ return unix.Close(n)
300+}
301+
302+// FD transfers ownership of the next descriptor assigned to this event.
303+// An unclaimed descriptor is closed when dispatch completes.
304+func (e *Event) FD() *OwnedFD {
305+ if e.fdIndex >= len(e.fds) {
306+ return nil
307+ }
308+ fd := e.fds[e.fdIndex]
309+ e.fds[e.fdIndex] = nil
310+ e.fdIndex++
311+ return fd
312+}
313+
314+// Fd is the legacy raw descriptor API; prefer FD for explicit ownership.
228315 func (e *Event) Fd() uintptr {
229- if e.display != nil {
230- if fd, ok := e.display.nextFD(); ok {
231- return uintptr(fd)
232- }
316+ fd := e.FD()
317+ if fd == nil {
318+ _ = e.Uint32()
319+ return 0
233320 }
234- // No descriptor was delivered: consume the placeholder argument.
235- _ = e.Uint32()
236- return 0
321+ n, _ := fd.Take()
322+ return uintptr(n)
237323 }
238324
239325 // NewId reads a new object ID from the event
@@ -278,32 +364,24 @@ type RegistryGlobalRemoveEvent struct {
278364
279365 // AddGlobalHandler adds a global handler to the registry
280366 func (r *Registry) AddGlobalHandler(handler RegistryGlobalHandler) {
281- // Store the handler to be called for ALL globals
282- r.mu.Lock()
283- defer r.mu.Unlock()
284-
285- // Initialize handlers map if needed
286- if r.handlers == nil {
287- r.handlers = make(map[string]GlobalHandler)
367+ if handler == nil {
368+ return
288369 }
289-
290370 r.AddHandler("*", func(registry *Registry, name uint32, version uint32) {
291- global, ok := r.FindGlobalByName(name)
292- if ok {
293- event := RegistryGlobalEvent{
294- Registry: r,
295- Name: name,
296- Interface: global.Interface,
297- Version: version,
298- }
299- handler.HandleRegistryGlobal(event)
371+ if global, ok := r.FindGlobalByName(name); ok {
372+ handler.HandleRegistryGlobal(RegistryGlobalEvent{Registry: r, Name: name, Interface: global.Interface, Version: version})
300373 }
301374 })
302375 }
303376
304-// AddGlobalRemoveHandler adds a global remove handler (placeholder)
377+// AddGlobalRemoveHandler registers a handler for registry removals.
305378 func (r *Registry) AddGlobalRemoveHandler(handler RegistryGlobalRemoveHandler) {
306- // Would need to implement removal handling
379+ if handler == nil {
380+ return
381+ }
382+ r.mu.Lock()
383+ r.removeHandlers = append(r.removeHandlers, handler)
384+ r.mu.Unlock()
307385 }
308386
309387 // FindGlobalByName finds a global by its name ID
@@ -340,342 +418,12 @@ func (d *Display) Sync() (Object, error) {
340418 display: d,
341419 }
342420
343- // Send sync request (opcode 0)
421+ // Store before sending: the compositor may reply immediately.
422+ d.context.Register(callback)
344423 if err := d.SendRequest(1, 0, callbackID); err != nil {
345- return nil, err
346- }
347-
348- // Store the callback object
349- d.objects.Store(callbackID, callback)
350-
351- return callback, nil
352-}
353-
354-// Additional protocol object types needed for compatibility
355-
356-// Seat capability constants
357-const (
358- SeatCapabilityPointer = 1
359- SeatCapabilityKeyboard = 2
360- SeatCapabilityTouch = 4
361-)
362-
363-// Seat represents a wl_seat
364-type Seat struct {
365- BaseProxy
366- capabilities uint32
367- name string
368-}
369-
370-// SeatCapabilitiesHandler handles seat capabilities events
371-type SeatCapabilitiesHandler interface {
372- HandleSeatCapabilities(seat *Seat, capabilities uint32)
373-}
374-
375-// SeatNameHandler handles seat name events
376-type SeatNameHandler interface {
377- HandleSeatName(seat *Seat, name string)
378-}
379-
380-// NewSeat creates a new seat proxy
381-func NewSeat(ctx *Context) *Seat {
382- return &Seat{
383- BaseProxy: BaseProxy{
384- context: ctx,
385- },
386- }
387-}
388-
389-// GetPointer gets the pointer device
390-func (s *Seat) GetPointer() (*Pointer, error) {
391- pointer := &Pointer{
392- BaseProxy: BaseProxy{
393- context: s.context,
394- id: s.context.display.allocateID(),
395- },
396- }
397-
398- // Register pointer before sending request
399- s.context.Register(pointer)
400-
401- // Send get_pointer request (opcode 0)
402- err := s.context.SendRequest(s, 0, pointer.id)
403- if err != nil {
404- s.context.Unregister(pointer)
405- return nil, err
406- }
407-
408- return pointer, nil
409-}
410-
411-// GetKeyboard gets the keyboard device
412-func (s *Seat) GetKeyboard() (*Keyboard, error) {
413- keyboard := &Keyboard{
414- BaseProxy: BaseProxy{
415- context: s.context,
416- id: s.context.display.allocateID(),
417- },
418- }
419-
420- // Register keyboard before sending request
421- s.context.Register(keyboard)
422-
423- // Send get_keyboard request (opcode 1)
424- err := s.context.SendRequest(s, 1, keyboard.id)
425- if err != nil {
426- s.context.Unregister(keyboard)
427- return nil, err
428- }
429-
430- return keyboard, nil
431-}
432-
433-// GetTouch gets the touch device
434-func (s *Seat) GetTouch() (*Touch, error) {
435- touch := &Touch{
436- BaseProxy: BaseProxy{
437- context: s.context,
438- id: s.context.display.allocateID(),
439- },
440- }
441-
442- // Register touch before sending request
443- s.context.Register(touch)
444-
445- // Send get_touch request (opcode 2)
446- err := s.context.SendRequest(s, 2, touch.id)
447- if err != nil {
448- s.context.Unregister(touch)
449- return nil, err
450- }
451-
452- return touch, nil
453-}
454-
455-// Release releases the seat
456-func (s *Seat) Release() error {
457- err := s.context.SendRequest(s, 3) // opcode 3
458- if err == nil {
459- s.context.Unregister(s)
460- }
461- return err
462-}
463-
464-// Capabilities returns the seat capabilities
465-func (s *Seat) Capabilities() uint32 {
466- return s.capabilities
467-}
468-
469-// Name returns the seat name
470-func (s *Seat) Name() string {
471- return s.name
472-}
473-
474-// Dispatch handles events for the seat
475-func (s *Seat) Dispatch(event *Event) {
476- // Handle seat events
477- switch event.Opcode {
478- case 0: // capabilities
479- s.capabilities = event.Uint32()
480- // TODO: Call registered handlers
481- case 1: // name
482- s.name = event.String()
483- // TODO: Call registered handlers
484- }
485-}
486-
487-// Surface represents a wl_surface
488-type Surface struct {
489- BaseProxy
490-}
491-
492-// NewSurface creates a new surface proxy
493-func NewSurface(ctx *Context) *Surface {
494- return &Surface{
495- BaseProxy: BaseProxy{
496- context: ctx,
497- },
498- }
499-}
500-
501-// Destroy destroys the surface
502-func (s *Surface) Destroy() error {
503- err := s.context.SendRequest(s, 0) // opcode 0
504- if err == nil {
505- s.context.Unregister(s)
506- }
507- return err
508-}
509-
510-// Attach attaches a buffer to the surface
511-func (s *Surface) Attach(buffer Object, x, y int32) error {
512- return s.context.SendRequest(s, 1, buffer, x, y) // opcode 1
513-}
514-
515-// Damage marks a region of the surface as damaged
516-func (s *Surface) Damage(x, y, width, height int32) error {
517- return s.context.SendRequest(s, 2, x, y, width, height) // opcode 2
518-}
519-
520-// Frame requests a frame callback
521-func (s *Surface) Frame() (Object, error) {
522- callback := &callbackObject{
523- BaseProxy: BaseProxy{
524- context: s.context,
525- id: s.context.display.allocateID(),
526- },
527- display: s.context.display,
528- }
529- s.context.Register(callback)
530-
531- err := s.context.SendRequest(s, 3, callback.id) // opcode 3
532- if err != nil {
533- s.context.Unregister(callback)
424+ d.context.Unregister(callback)
534425 return nil, err
535426 }
536427
537428 return callback, nil
538429 }
539-
540-// SetOpaqueRegion sets the opaque region
541-func (s *Surface) SetOpaqueRegion(region *Region) error {
542- return s.context.SendRequest(s, 4, region) // opcode 4
543-}
544-
545-// SetInputRegion sets the input region
546-func (s *Surface) SetInputRegion(region *Region) error {
547- return s.context.SendRequest(s, 5, region) // opcode 5
548-}
549-
550-// Commit commits pending surface state
551-func (s *Surface) Commit() error {
552- return s.context.SendRequest(s, 6) // opcode 6
553-}
554-
555-// SetBufferTransform sets the buffer transform
556-func (s *Surface) SetBufferTransform(transform int32) error {
557- return s.context.SendRequest(s, 7, transform) // opcode 7
558-}
559-
560-// SetBufferScale sets the buffer scale
561-func (s *Surface) SetBufferScale(scale int32) error {
562- return s.context.SendRequest(s, 8, scale) // opcode 8
563-}
564-
565-// DamageBuffer marks a region of the buffer as damaged
566-func (s *Surface) DamageBuffer(x, y, width, height int32) error {
567- return s.context.SendRequest(s, 9, x, y, width, height) // opcode 9
568-}
569-
570-// Offset sets the buffer offset
571-func (s *Surface) Offset(x, y int32) error {
572- return s.context.SendRequest(s, 10, x, y) // opcode 10
573-}
574-
575-// Dispatch handles surface events
576-func (s *Surface) Dispatch(event *Event) {
577- // Surface can receive enter/leave/preferred_buffer_scale/preferred_buffer_transform events
578- // But for now we don't handle them
579-}
580-
581-// Pointer represents a wl_pointer
582-type Pointer struct {
583- BaseProxy
584-}
585-
586-// Keyboard represents a wl_keyboard
587-type Keyboard struct {
588- BaseProxy
589-}
590-
591-// Touch represents a wl_touch
592-type Touch struct {
593- BaseProxy
594-}
595-
596-// Output represents a wl_output
597-type Output struct {
598- BaseProxy
599-}
600-
601-// Region represents a wl_region
602-type Region struct {
603- BaseProxy
604-}
605-
606-// Add adds a rectangle to the region
607-func (r *Region) Add(x, y, width, height int32) error {
608- return r.context.SendRequest(r, 0, x, y, width, height) // opcode 0
609-}
610-
611-// Subtract subtracts a rectangle from the region
612-func (r *Region) Subtract(x, y, width, height int32) error {
613- return r.context.SendRequest(r, 1, x, y, width, height) // opcode 1
614-}
615-
616-// Destroy destroys the region
617-func (r *Region) Destroy() error {
618- err := r.context.SendRequest(r, 2) // opcode 2
619- if err == nil {
620- r.context.Unregister(r)
621- }
622- return err
623-}
624-
625-// Compositor represents a wl_compositor
626-type Compositor struct {
627- BaseProxy
628-}
629-
630-// NewCompositor creates a new compositor proxy
631-func NewCompositor(ctx *Context) *Compositor {
632- return &Compositor{
633- BaseProxy: BaseProxy{
634- context: ctx,
635- },
636- }
637-}
638-
639-// CreateSurface creates a new surface
640-func (c *Compositor) CreateSurface() (*Surface, error) {
641- surface := &Surface{
642- BaseProxy: BaseProxy{
643- context: c.context,
644- id: c.context.display.allocateID(),
645- },
646- }
647-
648- // Register surface before sending request
649- c.context.Register(surface)
650-
651- // Send create_surface request (opcode 0)
652- err := c.context.SendRequest(c, 0, surface.id)
653- if err != nil {
654- c.context.Unregister(surface)
655- return nil, err
656- }
657-
658- return surface, nil
659-}
660-
661-// CreateRegion creates a new region
662-func (c *Compositor) CreateRegion() (*Region, error) {
663- region := &Region{
664- BaseProxy: BaseProxy{
665- context: c.context,
666- id: c.context.display.allocateID(),
667- },
668- }
669-
670- // Register region before sending request
671- c.context.Register(region)
672-
673- // Send create_region request (opcode 1)
674- err := c.context.SendRequest(c, 1, region.id)
675- if err != nil {
676- c.context.Unregister(region)
677- return nil, err
678- }
679-
680- return region, nil
681-}
A docs/protocol-migration.md +29 -0
@@ -0,0 +1,29 @@
1+# Canonical core / migration (v0.x)
2+
3+`protocol/core` replaces root-package and `wl` shim core object types. Bootstrap
4+`Display`, `Registry`, `Context`, and `BaseProxy` remain in the root/`wl`
5+transport. Bind globals using `core.NewCompositor(display.Context())`,
6+`core.NewSeat(display.Context())`, etc. and `display.Registry().Bind(name,
7+core.CompositorInterface, version, compositor)`. Requests create typed registered
8+children (`seat.GetKeyboard()`, `surface.Frame()`, `shm.CreatePool()`) and
9+server-side `new_id` events register concrete objects before invoking handlers.
10+Do not overwrite registered objects with a second wrapper.
11+
12+Breaking changes: `wlturbo`/`wl` `Seat`, `Surface`, `Pointer`, `Keyboard`,
13+`Touch`, `Output`, `Region`, `Compositor`, their constructors, and seat
14+capability aliases have moved to `protocol/core`. Generated methods accept
15+concrete core objects and return concrete children, not `wl.Object`. Legacy
16+raw `Event.Fd()` is not for generated events; generated FD handlers receive
17+`*wlturbo.OwnedFD` with `Take() (int,error)` or `Close()`; second and later
18+handlers receive nil FD. On a successful full send a generated FD request
19+closes the local descriptor; on error the caller keeps it. `Registry.BindID`
20+remains for legacy callers but cannot register typed event dispatch: prefer
21+`Bind` and generated constructors. Unrecognized custom-proxy event opcodes
22+now fail unless declared via `Display.RegisterEventSignature`.
23+
24+The scanner accepts `-import wl_surface=github.com/bnema/wlturbo/protocol/core`
25+(repeat for each external type); local XML definitions take precedence, then
26+explicit external mapping, then only bootstrap display/registry. Generate with
27+`GOWORK=off go generate ./...`; metadata and XML license are in
28+`protocol/SOURCE.md`. P3 extensions will use the same scanner and public core
29+mappings; no generator-owned compositor policy is introduced here.
M event_pool.go +0 -34
@@ -27,10 +27,6 @@ type EventDispatcher struct {
2727
2828 // Extended handlers for object IDs >= 1024 (rare case)
2929 extHandlers sync.Map
30-
31- // Pre-allocated event objects to avoid allocations
32- eventCache [64]Event
33- eventIndex atomic.Uint32
3430 }
3531
3632 // handlerEntry stores handlers for a specific object
@@ -166,36 +162,6 @@ type RawEvent struct {
166162 Data []byte
167163 }
168164
169-// DirectDispatcher provides the absolute fastest dispatch path for hot events
170-type DirectDispatcher struct {
171- // Direct function pointers for the hottest paths (e.g., pointer motion)
172- pointerMotion func(surfaceX, surfaceY Fixed)
173- pointerButton func(button, state uint32)
174- keyboardKey func(key, state uint32)
175- frameCallback func(callbackData uint32)
176-
177- // Fallback to regular dispatcher
178- fallback *EventDispatcher
179-}
180-
181-// DispatchPointerMotion dispatches pointer motion events with zero overhead
182-//
183-//go:inline
184-func (d *DirectDispatcher) DispatchPointerMotion(surfaceX, surfaceY Fixed) {
185- if d.pointerMotion != nil {
186- d.pointerMotion(surfaceX, surfaceY)
187- }
188-}
189-
190-// DispatchPointerButton dispatches pointer button events with zero overhead
191-//
192-//go:inline
193-func (d *DirectDispatcher) DispatchPointerButton(button, state uint32) {
194- if d.pointerButton != nil {
195- d.pointerButton(button, state)
196- }
197-}
198-
199165 // Ensure cache line alignment for hot data structures
200166 type cacheLinePad [64]byte
201167
M fd_test.go +92 -48
@@ -90,6 +90,8 @@ func newFDDisplay(t *testing.T, client *net.UnixConn) (*Display, *[]int) {
9090
9191 d := newDisplay(client)
9292 fds := []int{}
93+ d.RegisterEventSignature(7, 3, "uint,fd,")
94+ d.RegisterEventSignature(7, 4, "uint,")
9395 d.objects.Store(uint32(7), &fdRecordingProxy{
9496 BaseProxy: BaseProxy{id: 7, context: d.context},
9597 fds: &fds,
@@ -146,49 +148,19 @@ func TestDisplayFD_Single(t *testing.T) {
146148 readSentinel(t, r, "single-fd-sentinel")
147149 }
148150
149-func TestDisplayFD_MultipleInOneControlMessage(t *testing.T) {
151+func TestDisplayFD_ExtraDescriptorRejected(t *testing.T) {
150152 client, peer := unixSocketPair(t)
151- d, received := newFDDisplay(t, client)
152-
153- firstR, firstW, err := os.Pipe()
154- if err != nil {
155- t.Fatalf("pipe: %v", err)
156- }
157- defer firstR.Close()
158- secondR, secondW, err := os.Pipe()
153+ d, _ := newFDDisplay(t, client)
154+ r, w, err := os.Pipe()
159155 if err != nil {
160- t.Fatalf("pipe: %v", err)
156+ t.Fatal(err)
161157 }
162- defer secondR.Close()
163-
164- sendMessageWithFDs(t, peer, message(7, 3, []byte{0, 0, 0, 0}),
165- []int{int(firstW.Fd()), int(secondW.Fd())})
166- firstW.Close()
167- secondW.Close()
168-
169- if err := d.Dispatch(); err != nil {
170- t.Fatalf("Dispatch: %v", err)
171- }
172-
173- // A handler consumes one descriptor per fd argument, so the test drains
174- // whatever the connection still holds, in arrival order.
175- for {
176- fd, ok := d.nextFD()
177- if !ok {
178- break
179- }
180- *received = append(*received, fd)
181- }
182-
183- if len(*received) != 2 {
184- t.Fatalf("received %d descriptors, want 2", len(*received))
158+ defer r.Close()
159+ defer w.Close()
160+ sendMessageWithFDs(t, peer, message(7, 3, nil), []int{int(w.Fd()), int(w.Fd())})
161+ if err := d.Dispatch(); !errors.Is(err, ErrMalformedFrame) {
162+ t.Fatalf("extra FD: %v", err)
185163 }
186-
187- writeSentinel(t, uintptr((*received)[0]), "first-pipe")
188- writeSentinel(t, uintptr((*received)[1]), "second-pipe")
189-
190- readSentinel(t, firstR, "first-pipe")
191- readSentinel(t, secondR, "second-pipe")
192164 }
193165
194166 func TestDisplayFD_EventWithoutFDsAfterOneWithFDs(t *testing.T) {
@@ -304,9 +276,9 @@ func TestDisplayFD_TruncatedControlData(t *testing.T) {
304276 }
305277
306278 // A truncated control message must not leave descriptors queued.
307- d.fdMu.Lock()
279+ d.recvMu.Lock()
308280 pending := len(d.pendingFDs)
309- d.fdMu.Unlock()
281+ d.recvMu.Unlock()
310282 if pending != 0 {
311283 t.Fatalf("%d descriptors still queued after a truncated control message", pending)
312284 }
@@ -315,6 +287,7 @@ func TestDisplayFD_TruncatedControlData(t *testing.T) {
315287 func TestDisplayFD_UnconsumedDescriptorClosedOnShutdown(t *testing.T) {
316288 client, peer := unixSocketPair(t)
317289 d := newDisplay(client)
290+ d.RegisterEventSignature(7, 3, "uint,fd,")
318291 d.objects.Store(uint32(7), &fdIgnoringProxy{
319292 BaseProxy: BaseProxy{id: 7, context: d.context},
320293 })
@@ -333,12 +306,12 @@ func TestDisplayFD_UnconsumedDescriptorClosedOnShutdown(t *testing.T) {
333306 t.Fatalf("Dispatch: %v", err)
334307 }
335308
336- // While the display still holds the descriptor the pipe stays open.
337- if err := r.SetReadDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
338- t.Fatalf("SetReadDeadline: %v", err)
309+ // An ignored FD is closed at the end of the event, not at shutdown.
310+ if err := r.SetReadDeadline(time.Now().Add(2 * time.Second)); err != nil {
311+ t.Fatal(err)
339312 }
340- if _, err := r.Read(make([]byte, 1)); !errors.Is(err, os.ErrDeadlineExceeded) {
341- t.Fatalf("read with the display holding the descriptor = %v, want %v", err, os.ErrDeadlineExceeded)
313+ if _, err := r.Read(make([]byte, 1)); !errors.Is(err, io.EOF) {
314+ t.Fatalf("ignored FD: %v", err)
342315 }
343316
344317 // Closing the display must release it, so the read end sees EOF.
@@ -421,6 +394,7 @@ func runFDLeakIteration(t *testing.T, devNull *os.File) {
421394
422395 client, peer := rawSocketPair(t)
423396 d := newDisplay(client)
397+ d.RegisterEventSignature(7, 3, "uint,fd,")
424398 d.objects.Store(uint32(7), &fdIgnoringProxy{
425399 BaseProxy: BaseProxy{id: 7, context: d.context},
426400 })
@@ -448,6 +422,7 @@ func runFDLeakIteration(t *testing.T, devNull *os.File) {
448422 func TestDisplayFD_ParseFailureClosesQueuedDescriptor(t *testing.T) {
449423 client, peer := unixSocketPair(t)
450424 d := newDisplay(client)
425+ d.RegisterEventSignature(7, 3, "uint,fd,")
451426 d.objects.Store(uint32(7), &fdIgnoringProxy{
452427 BaseProxy: BaseProxy{id: 7, context: d.context},
453428 })
@@ -467,9 +442,9 @@ func TestDisplayFD_ParseFailureClosesQueuedDescriptor(t *testing.T) {
467442 t.Fatalf("Dispatch error = %v, want %v", err, ErrMalformedFrame)
468443 }
469444
470- d.fdMu.Lock()
445+ d.recvMu.Lock()
471446 pending := len(d.pendingFDs)
472- d.fdMu.Unlock()
447+ d.recvMu.Unlock()
473448 if pending != 0 {
474449 t.Fatalf("%d descriptors still queued after a malformed frame", pending)
475450 }
@@ -481,3 +456,72 @@ func TestDisplayFD_ParseFailureClosesQueuedDescriptor(t *testing.T) {
481456 t.Fatalf("read after malformed frame = %v, want %v", err, io.EOF)
482457 }
483458 }
459+
460+// Closing in a callback must not wait for the dispatch reader's state lock.
461+type closingProxy struct {
462+ BaseProxy
463+ d *Display
464+ returned bool
465+}
466+
467+func (p *closingProxy) EventSignature(op uint16) (string, bool) { return "fd,", op == 0 }
468+func (p *closingProxy) Dispatch(*Event) { p.returned = p.d.Close() == nil }
469+
470+func TestCloseInsideHandlerClosesQueuedFDs(t *testing.T) {
471+ client, peer := unixSocketPair(t)
472+ d := newDisplay(client)
473+ proxy := &closingProxy{BaseProxy: BaseProxy{id: 7, context: d.context}, d: d}
474+ d.context.Register(proxy)
475+ r, w, err := os.Pipe()
476+ if err != nil {
477+ t.Fatal(err)
478+ }
479+ defer r.Close()
480+ extra, xw, err := os.Pipe()
481+ if err != nil {
482+ t.Fatal(err)
483+ }
484+ defer extra.Close()
485+ // Two frames in one read; second FD remains queued while first handler runs.
486+ payload := append(message(7, 0, nil), message(7, 0, nil)...)
487+ sendMessageWithFDs(t, peer, payload, []int{int(w.Fd()), int(xw.Fd())})
488+ w.Close()
489+ xw.Close()
490+ if err := d.Dispatch(); err != nil {
491+ t.Fatal(err)
492+ }
493+ if !proxy.returned {
494+ t.Fatal("Close did not return from handler")
495+ }
496+ for _, reader := range []*os.File{r, extra} {
497+ if err := reader.SetReadDeadline(time.Now().Add(time.Second)); err != nil {
498+ t.Fatal(err)
499+ }
500+ if _, err := reader.Read(make([]byte, 1)); !errors.Is(err, io.EOF) {
501+ t.Fatalf("fd not closed: %v", err)
502+ }
503+ }
504+}
505+
506+func TestParseFDsKeepsRightsBeforeMalformedHeader(t *testing.T) {
507+ r, w, err := os.Pipe()
508+ if err != nil {
509+ t.Fatal(err)
510+ }
511+ defer r.Close()
512+ defer w.Close()
513+ rights := unix.UnixRights(int(w.Fd()))
514+ oob := append(rights, make([]byte, unix.SizeofCmsghdr)...)
515+ fds, err := parseFDs(oob)
516+ if err == nil || len(fds) != 1 {
517+ t.Fatalf("fds=%v err=%v", fds, err)
518+ }
519+ if _, err := unix.FcntlInt(uintptr(fds[0]), unix.F_GETFD, 0); err != nil {
520+ t.Fatal(err)
521+ }
522+ for _, fd := range fds {
523+ if err := unix.Close(fd); err != nil {
524+ t.Fatal(err)
525+ }
526+ }
527+}
A fd_zero_test.go +14 -0
@@ -0,0 +1,14 @@
1+package wlturbo
2+
3+import "testing"
4+
5+func TestOwnedFDZero(t *testing.T) {
6+ fd := &OwnedFD{fd: 0}
7+ n, e := fd.Take()
8+ if e != nil || n != 0 {
9+ t.Fatalf("Take fd zero = %d %v", n, e)
10+ }
11+ if e := fd.Close(); e != nil {
12+ t.Fatal(e)
13+ }
14+}
A internal/scanner/scanner.go +731 -0
@@ -0,0 +1,731 @@
1+// Package scanner generates WLTurbo-backed Go bindings from Wayland protocol
2+// XML files.
3+//
4+// The generated code owns protocol semantics only: it embeds wl.BaseProxy,
5+// allocates and registers child objects through the wl.Context, sends requests
6+// through that context, and dispatches typed events to registered handlers.
7+// Message framing, descriptor passing and connection lifetime stay in the
8+// transport, so a generated binding never opens a socket or writes a header.
9+package scanner
10+
11+import (
12+ "bytes"
13+ "encoding/xml"
14+ "fmt"
15+ "go/format"
16+ "io"
17+ "os"
18+ "path/filepath"
19+ "sort"
20+ "strings"
21+ "text/template"
22+)
23+
24+// TransportImport is the import path generated bindings use for the Wayland
25+// transport.
26+const TransportImport = "github.com/bnema/wlturbo/wl"
27+
28+// Protocol represents a Wayland protocol specification
29+type Protocol struct {
30+ XMLName xml.Name `xml:"protocol"`
31+ Name string `xml:"name,attr"`
32+ Copyright string `xml:"copyright"`
33+ Description *Description `xml:"description"`
34+ Interfaces []Interface `xml:"interface"`
35+}
36+
37+// Interface represents a Wayland interface in the protocol
38+type Interface struct {
39+ Name string `xml:"name,attr"`
40+ Version int `xml:"version,attr"`
41+ Description *Description `xml:"description"`
42+ Requests []Request `xml:"request"`
43+ Events []Event `xml:"event"`
44+ Enums []Enum `xml:"enum"`
45+}
46+
47+// Request represents a client-to-server message
48+type Request struct {
49+ Name string `xml:"name,attr"`
50+ Type string `xml:"type,attr"` // "destructor" or empty
51+ Since int `xml:"since,attr"`
52+ Description *Description `xml:"description"`
53+ Args []Arg `xml:"arg"`
54+}
55+
56+// Event represents a server-to-client message
57+type Event struct {
58+ Type string `xml:"type,attr"`
59+ Name string `xml:"name,attr"`
60+ Since int `xml:"since,attr"`
61+ Description *Description `xml:"description"`
62+ Args []Arg `xml:"arg"`
63+}
64+
65+// Arg represents a message argument
66+type Arg struct {
67+ Name string `xml:"name,attr"`
68+ Type string `xml:"type,attr"`
69+ Summary string `xml:"summary,attr"`
70+ Interface string `xml:"interface,attr"`
71+ AllowNull bool `xml:"allow-null,attr"`
72+ Enum string `xml:"enum,attr"`
73+}
74+
75+// Enum represents an enumeration type
76+type Enum struct {
77+ Name string `xml:"name,attr"`
78+ Since int `xml:"since,attr"`
79+ Bitfield bool `xml:"bitfield,attr"`
80+ Description *Description `xml:"description"`
81+ Entries []Entry `xml:"entry"`
82+}
83+
84+// Entry represents an enum value
85+type Entry struct {
86+ Name string `xml:"name,attr"`
87+ Value string `xml:"value,attr"`
88+ Summary string `xml:"summary,attr"`
89+ Since int `xml:"since,attr"`
90+}
91+
92+// Description represents documentation
93+type Description struct {
94+ Summary string `xml:"summary,attr"`
95+ Text string `xml:",chardata"`
96+}
97+
98+// Scanner generates Go bindings from Wayland protocol XML
99+type Scanner struct {
100+ protocol *Protocol
101+ source string
102+ // CrossPackage maps interface names to Go package import paths. Local wins.
103+ CrossPackage map[string]string
104+}
105+
106+// NewScanner creates a new protocol scanner
107+func NewScanner() *Scanner {
108+ return &Scanner{}
109+}
110+
111+// ParseXML parses a Wayland protocol XML file
112+func (s *Scanner) ParseXML(path string) error {
113+ file, err := os.Open(path)
114+ if err != nil {
115+ return fmt.Errorf("failed to open XML file: %w", err)
116+ }
117+ defer func() { _ = file.Close() }()
118+
119+ data, err := io.ReadAll(file)
120+ if err != nil {
121+ return fmt.Errorf("failed to read XML file: %w", err)
122+ }
123+
124+ var protocol Protocol
125+ if err := xml.Unmarshal(data, &protocol); err != nil {
126+ return fmt.Errorf("failed to parse XML: %w", err)
127+ }
128+
129+ s.protocol = &protocol
130+ // Only the base name is recorded: generated output must not depend on the
131+ // path the XML happened to be read from.
132+ s.source = filepath.Base(path)
133+ return nil
134+}
135+
136+// Generate creates Go source code for the parsed protocol
137+func (s *Scanner) Generate(packageName string) ([]byte, error) {
138+ if s.protocol == nil {
139+ return nil, fmt.Errorf("no protocol parsed")
140+ }
141+
142+ data, err := s.prepareTemplateData(packageName)
143+ if err != nil {
144+ return nil, err
145+ }
146+
147+ var buf bytes.Buffer
148+ tmpl := template.Must(template.New("protocol").Parse(protocolTemplate))
149+ if err := tmpl.Execute(&buf, data); err != nil {
150+ return nil, fmt.Errorf("failed to execute template: %w", err)
151+ }
152+
153+ formatted, err := format.Source(buf.Bytes())
154+ if err != nil {
155+ return buf.Bytes(), fmt.Errorf("failed to format generated code: %w", err)
156+ }
157+ return formatted, nil
158+}
159+
160+// templateData holds data for template generation
161+type templateData struct {
162+ Package string
163+ Protocol string
164+ Source string
165+ ImportBlock string
166+ Constants []constantData
167+ Interfaces []interfaceData
168+}
169+
170+type interfaceData struct {
171+ Name string
172+ GoName string
173+ Version int
174+ HasEvents bool
175+ SignatureCases []signatureCase
176+ Requests []requestData
177+ Events []eventData
178+}
179+
180+type signatureCase struct {
181+ Opcode int
182+ Signature string
183+}
184+type requestData struct {
185+ Name string
186+ GoName string
187+ Doc string
188+ Opcode int
189+ Destructor bool
190+ Params string
191+ Results string
192+ ArgPreparations []string
193+ ArgExprs []string
194+ CreatesChild bool
195+ ChildVar string
196+ ChildType string
197+ ChildFactory string
198+ ErrorReturn string
199+ FDArgs []string
200+ HasFDs bool
201+ SendCall string
202+}
203+
204+// argSlot is one positional argument of a request, in declaration order. A
205+// new_id slot is filled in once the child object has been created.
206+type argSlot struct {
207+ newID bool
208+ expr string
209+}
210+
211+type eventData struct {
212+ HasFD bool
213+ ServerDestroy bool
214+ FDNames []string
215+ Name string
216+ GoName string
217+ Opcode int
218+ HandlerField string
219+ HandlerAccessor string
220+ HandlerParams string
221+ HandlerArgs string
222+ DecodeLines []string
223+}
224+
225+type constantData struct {
226+ Name string
227+ Value string
228+ Type string
229+ Enum string
230+ Entry string
231+}
232+
233+// Only display and registry have handwritten bootstrap bindings.
234+var bootstrapTypes = map[string]bool{
235+ "Display": true,
236+ "Registry": true,
237+}
238+
239+// goKeywords are avoided as parameter names because generated identifiers
240+// must not collide with the language.
241+var goKeywords = map[string]bool{
242+ "break": true, "case": true, "chan": true, "const": true, "continue": true,
243+ "default": true, "defer": true, "else": true, "fallthrough": true, "for": true,
244+ "func": true, "go": true, "goto": true, "if": true, "import": true,
245+ "interface": true, "map": true, "package": true, "range": true, "return": true,
246+ "select": true, "struct": true, "switch": true, "type": true, "var": true,
247+}
248+
249+func (s *Scanner) prepareTemplateData(packageName string) (templateData, error) {
250+ data := templateData{
251+ Package: packageName,
252+ Protocol: s.protocol.Name,
253+ Source: s.source,
254+ }
255+
256+ imports := []string{TransportImport}
257+ usedCross := make(map[string]bool)
258+ for _, candidate := range s.protocol.Interfaces {
259+ for _, req := range candidate.Requests {
260+ for _, arg := range req.Args {
261+ if arg.Type == "object" || arg.Type == "new_id" {
262+ usedCross[arg.Interface] = true
263+ }
264+ }
265+ }
266+ for _, ev := range candidate.Events {
267+ for _, arg := range ev.Args {
268+ if arg.Type == "object" || arg.Type == "new_id" {
269+ usedCross[arg.Interface] = true
270+ }
271+ }
272+ }
273+ }
274+ for iface, path := range s.CrossPackage {
275+ local := false
276+ for _, candidate := range s.protocol.Interfaces {
277+ if candidate.Name == iface {
278+ local = true
279+ break
280+ }
281+ }
282+ if local || !usedCross[iface] || bootstrapTypes[s.toGoName(iface)] {
283+ continue
284+ }
285+ alias := "cross_" + strings.ReplaceAll(iface, "-", "_")
286+ imports = append(imports, alias+" "+path)
287+ }
288+ sort.Strings(imports)
289+ stdImports := []string{}
290+ needsSync := false
291+
292+ // Enum constants are package-level declarations, so two interfaces that
293+ // declare the same enum entry (for example xdg_surface.error.invalid_size
294+ // and xdg_toplevel.error.invalid_size) would collide. Entries whose name
295+ // occurs in more than one interface are qualified with their interface
296+ // name below; unique names keep their historical, unprefixed form.
297+ enumEntryCounts := map[string]int{}
298+ for _, iface := range s.protocol.Interfaces {
299+ for _, enum := range iface.Enums {
300+ for _, entry := range enum.Entries {
301+ enumEntryCounts[constantName(enum.Name, entry.Name)]++
302+ }
303+ }
304+ }
305+
306+ for _, iface := range s.protocol.Interfaces {
307+ if packageName == "core" && (iface.Name == "wl_display" || iface.Name == "wl_registry") {
308+ continue
309+ }
310+ ifaceData, err := s.processInterface(iface)
311+ if err != nil {
312+ return data, err
313+ }
314+ data.Interfaces = append(data.Interfaces, ifaceData)
315+ if len(iface.Events) > 0 {
316+ needsSync = true
317+ }
318+
319+ for _, enum := range iface.Enums {
320+ for _, entry := range enum.Entries {
321+ name := constantName(enum.Name, entry.Name)
322+ if enumEntryCounts[name] > 1 {
323+ name = qualifiedConstantName(iface.Name, enum.Name, entry.Name)
324+ }
325+ // Enum values are emitted untyped: the same enum travels as a uint
326+ // in one protocol and as an int in another, and an untyped constant
327+ // assigns to either without casts at the call site.
328+ data.Constants = append(data.Constants, constantData{
329+ Name: name,
330+ Value: entry.Value,
331+ Enum: enum.Name,
332+ Entry: entry.Name,
333+ })
334+ }
335+ }
336+ }
337+
338+ // Handlers are registered and dispatched from different goroutines, so an
339+ // interface with events needs a mutex.
340+ if needsSync {
341+ stdImports = append(stdImports, "sync")
342+ }
343+
344+ var block strings.Builder
345+ block.WriteString("import (\n")
346+ for _, imp := range stdImports {
347+ fmt.Fprintf(&block, "\t%q\n", imp)
348+ }
349+ if len(stdImports) > 0 && len(imports) > 0 {
350+ block.WriteString("\n")
351+ }
352+ for _, imp := range imports {
353+ if strings.Contains(imp, " ") {
354+ parts := strings.SplitN(imp, " ", 2)
355+ fmt.Fprintf(&block, "\t%s %q\n", parts[0], parts[1])
356+ } else {
357+ fmt.Fprintf(&block, "\t%q\n", imp)
358+ }
359+ }
360+ block.WriteString(")")
361+ data.ImportBlock = block.String()
362+
363+ return data, nil
364+}
365+
366+func (s *Scanner) processInterface(iface Interface) (interfaceData, error) {
367+ data := interfaceData{
368+ Name: iface.Name,
369+ GoName: s.toGoName(iface.Name),
370+ Version: iface.Version,
371+ HasEvents: len(iface.Events) > 0,
372+ }
373+
374+ for i, req := range iface.Requests {
375+ request, err := s.processRequest(iface, req, i)
376+ if err != nil {
377+ return data, err
378+ }
379+ data.Requests = append(data.Requests, request)
380+ }
381+
382+ for i, event := range iface.Events {
383+ var sig strings.Builder
384+ for _, arg := range event.Args {
385+ sig.WriteString(arg.Type)
386+ sig.WriteByte(',')
387+ }
388+ data.SignatureCases = append(data.SignatureCases, signatureCase{i, sig.String()})
389+ eventData, err := s.processEvent(event, i)
390+ if iface.Name == "wl_callback" && event.Name == "done" {
391+ eventData.ServerDestroy = true
392+ }
393+ if err != nil {
394+ return data, err
395+ }
396+ data.Events = append(data.Events, eventData)
397+ }
398+
399+ return data, nil
400+}
401+
402+func (s *Scanner) processRequest(iface Interface, req Request, opcode int) (requestData, error) {
403+ data := requestData{
404+ Name: req.Name,
405+ GoName: s.toGoName(req.Name),
406+ Doc: s.formatDescription(req.Description),
407+ Opcode: opcode,
408+ Destructor: req.Type == "destructor",
409+ ChildVar: "child",
410+ }
411+
412+ var (
413+ params []string
414+ argSlots []argSlot
415+ argExprs []string
416+ fdExprs []string
417+ newIDArg *Arg
418+ )
419+
420+ for i, arg := range req.Args {
421+ if arg.Type == "new_id" {
422+ if newIDArg != nil {
423+ return data, fmt.Errorf("%s.%s: more than one new_id argument is not supported", iface.Name, req.Name)
424+ }
425+ newIDArg = &arg
426+ // The child object is created later, but it keeps its declared
427+ // position: the wire order is the argument order.
428+ argSlots = append(argSlots, argSlot{newID: true})
429+ continue
430+ }
431+
432+ name := s.paramName(arg.Name, i)
433+ goType, err := s.goTypeForArg(arg)
434+ if err != nil {
435+ return data, fmt.Errorf("%s.%s: %w", iface.Name, req.Name, err)
436+ }
437+
438+ expr := name
439+ switch arg.Type {
440+ case "object":
441+ // Objects are pointer types, and travel as wl.Object so a nil
442+ // pointer is never sent as a non-nil interface holding a typed nil.
443+ params = append(params, name+" *"+goType)
444+ holder := fmt.Sprintf("arg%d", i)
445+ data.ArgPreparations = append(data.ArgPreparations,
446+ fmt.Sprintf("var %s wl.Object", holder),
447+ fmt.Sprintf("if %s != nil {", name),
448+ fmt.Sprintf("\t%s = %s", holder, name),
449+ "}")
450+ expr = holder
451+
452+ case "fd":
453+ // Descriptors travel out of band: the body carries no value for
454+ // them, and the descriptor list is attached to the request.
455+ params = append(params, name+" int")
456+ fdExprs = append(fdExprs, name)
457+ data.FDArgs = append(data.FDArgs, name)
458+ expr = "uintptr(" + name + ")"
459+
460+ default:
461+ params = append(params, name+" "+goType)
462+ }
463+
464+ argSlots = append(argSlots, argSlot{expr: expr})
465+ }
466+
467+ if newIDArg != nil {
468+ childType, err := s.goTypeForArg(*newIDArg)
469+ if err != nil {
470+ return data, fmt.Errorf("%s.%s: %w", iface.Name, req.Name, err)
471+ }
472+ data.CreatesChild = true
473+ data.ChildType = childType
474+ if strings.Contains(childType, ".") {
475+ data.ChildFactory = childType[:strings.LastIndex(childType, ".")+1] + "New" + s.toGoName(newIDArg.Interface)
476+ }
477+ }
478+
479+ for _, slot := range argSlots {
480+ if slot.newID {
481+ argExprs = append(argExprs, data.ChildVar)
482+ continue
483+ }
484+ argExprs = append(argExprs, slot.expr)
485+ }
486+
487+ data.Params = strings.Join(params, ", ")
488+ data.ArgExprs = argExprs
489+ args := ""
490+ if len(argExprs) > 0 {
491+ args = ", " + strings.Join(argExprs, ", ")
492+ }
493+ // Destructors claim the proxy atomically before sending so a concurrent
494+ // second destroy cannot reach the wire.
495+ send := "SendRequest"
496+ if data.Destructor {
497+ send = "SendDestructor"
498+ }
499+ if len(fdExprs) > 0 {
500+ data.HasFDs = true
501+ data.SendCall = fmt.Sprintf("%sWithFDs(o, %d, []int{%s}%s)", send, opcode, strings.Join(fdExprs, ", "), args)
502+ } else {
503+ data.SendCall = fmt.Sprintf("%s(o, %d%s)", send, opcode, args)
504+ }
505+ if data.CreatesChild {
506+ data.Results = "(*" + data.ChildType + ", error)"
507+ data.ErrorReturn = "nil, err"
508+ } else {
509+ data.Results = "error"
510+ data.ErrorReturn = "err"
511+ }
512+
513+ return data, nil
514+}
515+
516+func (s *Scanner) processEvent(event Event, opcode int) (eventData, error) {
517+ goName := s.toGoName(event.Name)
518+ data := eventData{
519+ ServerDestroy: event.Type == "destructor",
520+ Name: event.Name,
521+ GoName: goName,
522+ Opcode: opcode,
523+ HandlerField: "on" + goName,
524+ HandlerAccessor: "handlersFor" + goName,
525+ }
526+
527+ var params, args []string
528+
529+ for i, arg := range event.Args {
530+ name := s.paramName(arg.Name, i)
531+
532+ switch arg.Type {
533+ case "new_id":
534+ childType, err := s.goTypeForArg(arg)
535+ if err != nil {
536+ return data, fmt.Errorf("%s.%s: %w", event.Name, arg.Name, err)
537+ }
538+ child := name + "Object"
539+ data.DecodeLines = append(data.DecodeLines, fmt.Sprintf("%sID := event.Uint32()", name))
540+ if strings.Contains(childType, ".") {
541+ factory := childType[:strings.LastIndex(childType, ".")+1] + "New" + s.toGoName(arg.Interface)
542+ data.DecodeLines = append(data.DecodeLines, fmt.Sprintf("%s := %s(o.Context())", child, factory))
543+ } else {
544+ data.DecodeLines = append(data.DecodeLines,
545+ fmt.Sprintf("%s := &%s{}", child, childType),
546+ fmt.Sprintf("%s.SetContext(o.Context())", child),
547+ )
548+ }
549+ data.DecodeLines = append(data.DecodeLines,
550+ fmt.Sprintf("%s.SetID(%sID)", child, name),
551+ fmt.Sprintf("o.Context().Register(%s)", child),
552+ )
553+ params = append(params, child+" *"+childType)
554+ args = append(args, child)
555+
556+ case "object":
557+ // Object references are reported as raw object IDs: the generated
558+ // layer does not own a registry of foreign proxies.
559+ data.DecodeLines = append(data.DecodeLines, fmt.Sprintf("%sID := event.Uint32()", name))
560+ params = append(params, name+"ID uint32")
561+ args = append(args, name+"ID")
562+
563+ default:
564+ if arg.Type == "fd" {
565+ data.HasFD = true
566+ data.FDNames = append(data.FDNames, name)
567+ }
568+ goType, decoder, err := s.scalarDecoder(arg)
569+ if err != nil {
570+ return data, fmt.Errorf("%s.%s: %w", event.Name, arg.Name, err)
571+ }
572+ data.DecodeLines = append(data.DecodeLines, fmt.Sprintf("%s := event.%s()", name, decoder))
573+ params = append(params, name+" "+goType)
574+ args = append(args, name)
575+ }
576+ }
577+
578+ data.HandlerParams = strings.Join(params, ", ")
579+ data.HandlerArgs = strings.Join(args, ", ")
580+ return data, nil
581+}
582+
583+// scalarDecoder maps a simple argument to its Go type and event decoder.
584+func (s *Scanner) scalarDecoder(arg Arg) (goType, decoder string, err error) {
585+ switch arg.Type {
586+ case "int":
587+ return "int32", "Int32", nil
588+ case "uint":
589+ return "uint32", "Uint32", nil
590+ case "fixed":
591+ return "wl.Fixed", "Fixed", nil
592+ case "string":
593+ return "string", "String", nil
594+ case "array":
595+ return "[]byte", "Array", nil
596+ case "fd":
597+ return "*wl.OwnedFD", "FD", nil
598+ }
599+ return "", "", fmt.Errorf("unsupported event argument type %q", arg.Type)
600+}
601+
602+// goTypeForArg returns the Go type of an object-like argument. Protocol-local
603+// interfaces map to generated types, explicit external references to their
604+// mapped package, and bootstrap display/registry to transport wrappers.
605+func (s *Scanner) goTypeForArg(arg Arg) (string, error) {
606+ switch arg.Type {
607+ case "int":
608+ return "int32", nil
609+ case "uint":
610+ return "uint32", nil
611+ case "fixed":
612+ return "wl.Fixed", nil
613+ case "string":
614+ return "string", nil
615+ case "array":
616+ return "[]byte", nil
617+ case "fd":
618+ return "int", nil
619+ case "object", "new_id":
620+ return s.goTypeForInterface(arg.Interface)
621+ }
622+ return "", fmt.Errorf("unsupported argument type %q", arg.Type)
623+}
624+
625+func (s *Scanner) goTypeForInterface(iface string) (string, error) {
626+ if iface == "" {
627+ return "wl.BaseProxy", nil
628+ }
629+ // The XML package owns its own definitions even when the name starts wl_.
630+ for _, local := range s.protocol.Interfaces {
631+ if local.Name == iface && iface != "wl_display" && iface != "wl_registry" {
632+ return s.toGoName(iface), nil
633+ }
634+ }
635+ if bootstrapTypes[s.toGoName(iface)] {
636+ return "wl." + s.toGoName(iface), nil
637+ }
638+ if _, ok := s.CrossPackage[iface]; ok {
639+ return "cross_" + strings.ReplaceAll(iface, "-", "_") + "." + s.toGoName(iface), nil
640+ }
641+ return "", fmt.Errorf("unresolved interface %q: add CrossPackage mapping", iface)
642+}
643+
644+// paramName turns a protocol argument name into a Go parameter name.
645+func (s *Scanner) paramName(name string, index int) string {
646+ if name == "" {
647+ return fmt.Sprintf("arg%d", index)
648+ }
649+
650+ parts := strings.Split(name, "_")
651+ for i, part := range parts {
652+ if part == "" {
653+ continue
654+ }
655+ if i == 0 {
656+ parts[i] = strings.ToLower(part[:1]) + part[1:]
657+ continue
658+ }
659+ parts[i] = strings.ToUpper(part[:1]) + part[1:]
660+ }
661+ result := strings.Join(parts, "")
662+ if result == "" {
663+ return fmt.Sprintf("arg%d", index)
664+ }
665+ if goKeywords[result] {
666+ return result + "Arg"
667+ }
668+ return result
669+}
670+
671+func (s *Scanner) toGoName(name string) string {
672+ name = strings.TrimPrefix(name, "zwlr_")
673+ name = strings.TrimPrefix(name, "zwp_")
674+ name = strings.TrimPrefix(name, "wl_")
675+ name = strings.TrimSuffix(name, "_v1")
676+ name = strings.TrimSuffix(name, "_v2")
677+
678+ parts := strings.Split(name, "_")
679+ for i, part := range parts {
680+ if part != "" {
681+ parts[i] = strings.ToUpper(part[:1]) + part[1:]
682+ }
683+ }
684+ return strings.Join(parts, "")
685+}
686+
687+// constantName renders an enum entry as the unprefixed constant name.
688+func constantName(enum, entry string) string {
689+ return strings.Join(append(joinName(enum), joinName(entry)...), "_")
690+}
691+
692+// qualifiedConstantName prefixes an enum entry constant with its interface
693+// name. It is used for entries that several interfaces declare, which would
694+// otherwise collide in the generated package.
695+func qualifiedConstantName(iface, enum, entry string) string {
696+ return strings.Join(append(joinName(iface), constantName(enum, entry)), "_")
697+}
698+
699+// joinName upper-cases the underscore-separated parts of a protocol name.
700+func joinName(value string) []string {
701+ parts := strings.Split(value, "_")
702+ out := make([]string, 0, len(parts))
703+ for _, part := range parts {
704+ if part != "" {
705+ out = append(out, strings.ToUpper(part))
706+ }
707+ }
708+ return out
709+}
710+
711+// formatDescription renders an argument or request description as a single
712+// documentation line.
713+func (s *Scanner) formatDescription(desc *Description) string {
714+ if desc == nil {
715+ return ""
716+ }
717+
718+ text := strings.TrimSpace(desc.Text)
719+ if text == "" {
720+ return desc.Summary
721+ }
722+
723+ lines := strings.Split(text, "\n")
724+ formatted := make([]string, 0, len(lines))
725+ for _, line := range lines {
726+ if line = strings.TrimSpace(line); line != "" {
727+ formatted = append(formatted, line)
728+ }
729+ }
730+ return strings.Join(formatted, " ")
731+}
A internal/scanner/scanner_test.go +163 -0
@@ -0,0 +1,163 @@
1+package scanner
2+
3+import (
4+ "os"
5+ "os/exec"
6+ "path/filepath"
7+ "strings"
8+ "testing"
9+)
10+
11+func TestResolution(t *testing.T) {
12+ p := filepath.Join(t.TempDir(), "fixture.xml")
13+ xml := `<protocol name="sample"><interface name="wl_surface" version="1"><event name="done"><arg name="value" type="uint"/></event></interface><interface name="custom_thing" version="1"><request name="make"><arg name="id" type="new_id" interface="wl_surface"/><arg name="output" type="object" interface="wl_output"/></request></interface></protocol>`
14+ if err := os.WriteFile(p, []byte(xml), 0600); err != nil {
15+ t.Fatal(err)
16+ }
17+ s := NewScanner()
18+ if err := s.ParseXML(p); err != nil {
19+ t.Fatal(err)
20+ }
21+ s.CrossPackage = map[string]string{"wl_surface": "github.com/bnema/wlturbo/protocol/core", "wl_output": "github.com/bnema/wlturbo/protocol/core"}
22+ b, err := s.Generate("fixture")
23+ if err != nil {
24+ t.Fatal(err)
25+ }
26+ text := string(b)
27+ if !strings.Contains(text, "(*Surface, error)") || !strings.Contains(text, "output *cross_wl_output.Output") || strings.Contains(text, "cross_wl_surface") {
28+ t.Fatalf("wrong resolution: %s", text)
29+ }
30+}
31+func TestCoreGeneration(t *testing.T) {
32+ s := NewScanner()
33+ if err := s.ParseXML("../../protocol/wayland.xml"); err != nil {
34+ t.Fatal(err)
35+ }
36+ b, err := s.Generate("core")
37+ if err != nil {
38+ t.Fatal(err)
39+ }
40+ for _, want := range []string{"type Keyboard struct", "type Subsurface struct", "type Buffer struct", "type Callback struct", "return \"uint,fd,uint,\", true", "child := &Keyboard{}"} {
41+ if !strings.Contains(string(b), want) {
42+ t.Errorf("missing %s", want)
43+ }
44+ }
45+ if strings.Contains(string(b), "type Display struct") || strings.Contains(string(b), "type Registry struct") {
46+ t.Fatal("bootstrap generated twice")
47+ }
48+}
49+
50+// P3 uses wl_buffer as both an event-created child and a request-created child.
51+// Verify the generated code uses the public constructor instead of embedding
52+// an inaccessible BaseProxy context field across package boundaries.
53+func TestExternalChildFactory(t *testing.T) {
54+ path := filepath.Join(t.TempDir(), "external.xml")
55+ xml := `<protocol name="fixture"><interface name="ext_params" version="1"><request name="create"><arg name="id" type="new_id" interface="wl_buffer"/></request><event name="created"><arg name="id" type="new_id" interface="wl_buffer"/></event></interface></protocol>`
56+ if err := os.WriteFile(path, []byte(xml), 0600); err != nil {
57+ t.Fatal(err)
58+ }
59+ s := NewScanner()
60+ if err := s.ParseXML(path); err != nil {
61+ t.Fatal(err)
62+ }
63+ s.CrossPackage = map[string]string{"wl_buffer": "github.com/bnema/wlturbo/protocol/core"}
64+ generated, err := s.Generate("ext")
65+ if err != nil {
66+ t.Fatal(err)
67+ }
68+ for _, part := range []string{"cross_wl_buffer.NewBuffer(o.Context())", "(*cross_wl_buffer.Buffer, error)", "idObject.SetID(idID)"} {
69+ if !strings.Contains(string(generated), part) {
70+ t.Errorf("generated code missing %q", part)
71+ }
72+ }
73+ if strings.Contains(string(generated), "&cross_wl_buffer.Buffer{}") {
74+ t.Fatal("foreign child was constructed without its package factory")
75+ }
76+}
77+
78+func TestUnresolvedExternalInterface(t *testing.T) {
79+ path := filepath.Join(t.TempDir(), "extension.xml")
80+ xml := `<protocol name="ext"><interface name="ext_parent" version="1"><request name="make"><arg name="id" type="new_id" interface="wl_surface"/></request></interface></protocol>`
81+ if err := os.WriteFile(path, []byte(xml), 0600); err != nil {
82+ t.Fatal(err)
83+ }
84+ s := NewScanner()
85+ if err := s.ParseXML(path); err != nil {
86+ t.Fatal(err)
87+ }
88+ if _, err := s.Generate("ext"); err == nil || !strings.Contains(err.Error(), "wl_surface") {
89+ t.Fatalf("unmapped external reference: %v", err)
90+ }
91+}
92+
93+func TestMappedExtensionCompilesAndDispatchesChild(t *testing.T) {
94+ goBin, err := exec.LookPath("go")
95+ if err != nil {
96+ t.Skip("go unavailable: generated extension compilation requires go")
97+ }
98+ root, err := filepath.Abs("../..")
99+ if err != nil {
100+ t.Fatal(err)
101+ }
102+ dir := t.TempDir()
103+ xml := `<protocol name="ext"><interface name="ext_parent" version="1"><event name="child"><arg name="id" type="new_id" interface="wl_buffer"/></event></interface></protocol>`
104+ path := filepath.Join(dir, "ext.xml")
105+ if err := os.WriteFile(path, []byte(xml), 0600); err != nil {
106+ t.Fatal(err)
107+ }
108+ s := NewScanner()
109+ if err := s.ParseXML(path); err != nil {
110+ t.Fatal(err)
111+ }
112+ s.CrossPackage = map[string]string{"wl_buffer": "github.com/bnema/wlturbo/protocol/core"}
113+ generated, err := s.Generate("ext")
114+ if err != nil {
115+ t.Fatal(err)
116+ }
117+ if err := os.WriteFile(filepath.Join(dir, "ext.go"), generated, 0600); err != nil {
118+ t.Fatal(err)
119+ }
120+ mod := "module example.com/ext\n\ngo 1.27\n\nrequire github.com/bnema/wlturbo v0.0.0\nreplace github.com/bnema/wlturbo => " + root + "\n"
121+ if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte(mod), 0600); err != nil {
122+ t.Fatal(err)
123+ }
124+ test := `package ext
125+import (
126+ "encoding/binary"
127+ "net"
128+ "os"
129+ "testing"
130+ "github.com/bnema/wlturbo"
131+ "github.com/bnema/wlturbo/protocol/core"
132+ "golang.org/x/sys/unix"
133+)
134+func TestTypedChild(t *testing.T) {
135+ f, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM, 0); if err != nil { t.Fatal(err) }
136+ a, b := os.NewFile(uintptr(f[0]), "a"), os.NewFile(uintptr(f[1]), "b")
137+ c, err := net.FileConn(a); a.Close(); if err != nil { t.Fatal(err) }; defer c.Close()
138+ p, err := net.FileConn(b); b.Close(); if err != nil { t.Fatal(err) }; defer p.Close()
139+ d, err := wlturbo.ConnectFromConn(c); if err != nil { t.Fatal(err) }; defer d.Close()
140+ parent := NewExtParent(d.Context()); parent.SetID(d.AllocateID()); d.Context().Register(parent)
141+ var child *core.Buffer
142+ parent.OnChild(func(b *core.Buffer) { child = b })
143+ frame := make([]byte, 12); binary.LittleEndian.PutUint32(frame, parent.ID()); binary.LittleEndian.PutUint32(frame[4:], 12<<16); binary.LittleEndian.PutUint32(frame[8:], 0xff000001)
144+ if _, err := p.Write(frame); err != nil { t.Fatal(err) }
145+ if err := d.Dispatch(); err != nil { t.Fatal(err) }
146+ if child == nil || child.ID() != 0xff000001 { t.Fatalf("child: %v", child) }
147+ called := false; child.OnRelease(func(){ called = true })
148+ frame = frame[:8]; binary.LittleEndian.PutUint32(frame, child.ID()); binary.LittleEndian.PutUint32(frame[4:], 8<<16)
149+ if _, err := p.Write(frame); err != nil { t.Fatal(err) }
150+ if err := d.Dispatch(); err != nil { t.Fatal(err) }
151+ if !called { t.Fatal("typed child did not dispatch release") }
152+}
153+`
154+ if err := os.WriteFile(filepath.Join(dir, "ext_test.go"), []byte(test), 0600); err != nil {
155+ t.Fatal(err)
156+ }
157+ cmd := exec.Command(goBin, "test", "./...")
158+ cmd.Dir = dir
159+ cmd.Env = append(os.Environ(), "GOWORK=off", "GOFLAGS=-mod=mod")
160+ if out, err := cmd.CombinedOutput(); err != nil {
161+ t.Fatalf("generated extension test: %v\n%s", err, out)
162+ }
163+}
A internal/scanner/template.go +142 -0
@@ -0,0 +1,142 @@
1+package scanner
2+
3+// protocolTemplate is the template for generating WLTurbo-backed Go bindings.
4+//
5+// Generated objects embed wl.BaseProxy, allocate and register child objects
6+// through the wl.Context, send requests through that context, and dispatch
7+// typed events to registered handlers. Nothing in the generated code opens a
8+// socket or frames messages itself: that is the transport's job.
9+const protocolTemplate = `// Code generated by wlturbo-scanner. DO NOT EDIT.
10+// Protocol: {{.Protocol}}
11+// Source: {{.Source}}
12+
13+package {{.Package}}
14+
15+{{.ImportBlock}}
16+
17+{{range .Constants}}
18+// {{.Name}} is the {{.Enum}} {{.Entry}} value.
19+const {{.Name}}{{if .Type}} {{.Type}}{{end}} = {{.Value}}
20+{{- end}}
21+{{range .Interfaces}}
22+{{- $iface := .}}
23+
24+// {{$iface.GoName}}Interface is the protocol interface name.
25+const {{$iface.GoName}}Interface = "{{$iface.Name}}"
26+
27+// {{$iface.GoName}} is the generated binding for {{$iface.Name}} version {{$iface.Version}}.
28+type {{$iface.GoName}} struct {
29+ wl.BaseProxy
30+{{- if $iface.HasEvents}}
31+ // handlersMu guards the handler slices: a client may register or replace
32+ // handlers while another goroutine dispatches events.
33+ handlersMu sync.Mutex
34+{{- end}}
35+{{- range $iface.Events}}
36+ {{.HandlerField}} []func({{.HandlerParams}})
37+{{- end}}
38+}
39+
40+// New{{$iface.GoName}} returns an unbound {{$iface.GoName}}.
41+func New{{$iface.GoName}}(ctx *wl.Context) *{{$iface.GoName}} {
42+ obj := &{{$iface.GoName}}{}
43+ obj.SetContext(ctx)
44+ ctx.Register(obj)
45+ return obj
46+}
47+{{- range $iface.Requests}}
48+
49+{{- if .Doc}}
50+// {{.Doc}}
51+{{- end}}
52+func (o *{{$iface.GoName}}) {{.GoName}}({{.Params}}) {{.Results}} {
53+if err := o.Context().CheckProxy(o); err != nil { return {{.ErrorReturn}} }
54+{{- range .ArgPreparations}}
55+ {{.}}
56+{{- end}}
57+{{- if .CreatesChild}}
58+{{- if .ChildFactory}}
59+ {{.ChildVar}} := {{.ChildFactory}}(o.Context())
60+{{- else}}
61+ {{.ChildVar}} := &{{.ChildType}}{}
62+ {{.ChildVar}}.SetContext(o.Context())
63+{{- end}}
64+ {{.ChildVar}}.SetID(o.Context().AllocateID())
65+ o.Context().Register({{.ChildVar}})
66+{{- end}}
67+ if err := o.Context().{{.SendCall}}; err != nil {
68+{{- if .CreatesChild}}
69+ o.Context().Unregister({{.ChildVar}})
70+{{- end}}
71+ return {{.ErrorReturn}}
72+ }
73+{{- range .FDArgs}}
74+ _ = wl.CloseSentFD({{.}})
75+{{- end}}
76+{{- if .CreatesChild}}
77+ return {{.ChildVar}}, nil
78+{{- else}}
79+ return nil
80+{{- end}}
81+}
82+{{- end}}
83+{{- range $iface.Events}}
84+
85+// On{{.GoName}} registers a handler for the {{.Name}} event.
86+//
87+// Handlers are appended, so a second call adds another handler rather than
88+// replacing the first. Registration is safe while another goroutine dispatches.
89+func (o *{{$iface.GoName}}) On{{.GoName}}(handler func({{.HandlerParams}})) {
90+ if handler == nil { return }
91+ o.handlersMu.Lock()
92+ defer o.handlersMu.Unlock()
93+ o.{{.HandlerField}} = append(o.{{.HandlerField}}, handler)
94+}
95+
96+// {{.HandlerAccessor}} returns the handlers registered for {{.Name}}, taken
97+// under the lock so a handler may register another handler while events are
98+// being dispatched.
99+func (o *{{$iface.GoName}}) {{.HandlerAccessor}}() []func({{.HandlerParams}}) {
100+ o.handlersMu.Lock()
101+ defer o.handlersMu.Unlock()
102+ return o.{{.HandlerField}}
103+}
104+{{- end}}
105+
106+// EventSignature returns the declared event argument types in wire order.
107+func (o *{{$iface.GoName}}) EventSignature(opcode uint16) (string, bool) {
108+ switch opcode {
109+ {{- range $iface.SignatureCases}}
110+ case {{.Opcode}}: return "{{.Signature}}", true
111+ {{- end}}
112+ default: return "", false
113+ }
114+}
115+
116+// Dispatch decodes one event on {{$iface.Name}} and calls the registered handlers.
117+func (o *{{$iface.GoName}}) Dispatch(event *wl.Event) {
118+ switch event.Opcode {
119+{{- range $iface.Events}}
120+ case {{.Opcode}}: // {{.Name}}
121+{{- range .DecodeLines}}
122+ {{.}}
123+{{- end}}
124+ {{if .HasFD}}{{range .FDNames}}owned{{.}} := {{.}}; defer owned{{.}}.Close()
125+ {{end}}{{end}}
126+ for i, handler := range o.{{.HandlerAccessor}}() {
127+ {{- if .HasFD}}
128+ if i > 0 { {{range .FDNames}}{{.}} = nil; {{end}} }
129+ {{- else}}
130+ _ = i
131+ {{- end}}
132+ handler({{.HandlerArgs}})
133+ }
134+{{- if .ServerDestroy}}
135+ o.Context().Unregister(o)
136+{{- end}}
137+{{- end}}
138+ default:
139+ }
140+}
141+{{end}}
142+`
A internal/scanner/testdata/protocol_fixture.xml +112 -0
@@ -0,0 +1,112 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<protocol name="wlvision_fixture">
3+ <copyright>
4+ Synthetic protocol used to test the wlvision protocol generator. It exists
5+ only in this repository's tests.
6+ </copyright>
7+
8+ <interface name="zwlr_fixture_manager_v1" version="2">
9+ <description summary="fixture manager">
10+ Exercises new_id requests, object references, fixed point numbers,
11+ strings, arrays, file descriptors and destructors.
12+ </description>
13+
14+ <request name="create_thing">
15+ <description summary="create a fixture object"/>
16+ <arg name="seat" type="object" interface="wl_seat"/>
17+ <arg name="id" type="new_id" interface="zwlr_fixture_thing_v1"/>
18+ </request>
19+
20+ <request name="set_ratio">
21+ <arg name="ratio" type="fixed"/>
22+ </request>
23+
24+ <request name="set_label">
25+ <arg name="label" type="string"/>
26+ </request>
27+
28+ <request name="set_region">
29+ <arg name="region" type="object" interface="wl_region"/>
30+ </request>
31+
32+ <request name="set_buffer">
33+ <description summary="reference an interface without a typed wrapper"/>
34+ <arg name="buffer" type="object" interface="wl_buffer"/>
35+ </request>
36+
37+ <request name="set_optional_output">
38+ <arg name="output" type="object" interface="wl_output" allow-null="true"/>
39+ </request>
40+
41+ <request name="attach_raw">
42+ <arg name="data" type="array"/>
43+ </request>
44+
45+ <request name="pass_fd">
46+ <arg name="fd" type="fd"/>
47+ </request>
48+
49+ <request name="destroy" type="destructor">
50+ <description summary="destroy the manager"/>
51+ </request>
52+
53+ <request name="create_labeled_thing">
54+ <description summary="new_id argument that is not the last one"/>
55+ <arg name="id" type="new_id" interface="zwlr_fixture_thing_v1"/>
56+ <arg name="label" type="string"/>
57+ <arg name="ratio" type="fixed"/>
58+ </request>
59+ </interface>
60+
61+ <interface name="zwlr_fixture_thing_v1" version="1">
62+ <description summary="fixture object"/>
63+
64+ <enum name="fixture_state">
65+ <entry name="idle" value="0"/>
66+ <entry name="busy" value="1"/>
67+ </enum>
68+
69+ <request name="set_scale">
70+ <arg name="scale" type="fixed"/>
71+ </request>
72+
73+ <request name="destroy" type="destructor"/>
74+
75+ <event name="ready">
76+ <arg name="serial" type="uint"/>
77+ </event>
78+
79+ <event name="label_changed">
80+ <arg name="label" type="string"/>
81+ </event>
82+
83+ <event name="geometry">
84+ <arg name="x" type="int"/>
85+ <arg name="y" type="int"/>
86+ <arg name="width" type="uint"/>
87+ <arg name="height" type="uint"/>
88+ </event>
89+
90+ <event name="mode">
91+ <arg name="mode" type="new_id" interface="zwlr_fixture_mode_v1"/>
92+ </event>
93+
94+ <event name="peer">
95+ <arg name="surface" type="object" interface="wl_surface"/>
96+ </event>
97+
98+ <event name="scaled">
99+ <arg name="scale" type="fixed"/>
100+ </event>
101+
102+ <event name="blob">
103+ <arg name="data" type="array"/>
104+ </event>
105+ </interface>
106+
107+ <interface name="zwlr_fixture_mode_v1" version="1">
108+ <description summary="fixture mode object"/>
109+
110+ <request name="destroy" type="destructor"/>
111+ </interface>
112+</protocol>
A protocol/SOURCE.md +7 -0
@@ -0,0 +1,7 @@
1+# Core protocol source
2+
3+`wayland.xml` is from Wayland 1.26.0, upstream GitLab
4+`https://gitlab.freedesktop.org/wayland/wayland`, tag `1.26.0`, peeled commit
5+`87cc8a8728a923fc57938faa81ba0e74f34ecdc7`.
6+SHA-256: `cc860987e54f8d85c940e97fa1270c69b6e4ad31fbcf5a7f00107ce1157f5e07`.
7+License: MIT (full copyright and grant in `wayland.xml` `<copyright>`).
A protocol/core/core_test.go +532 -0
@@ -0,0 +1,532 @@
1+//go:build linux
2+
3+package core_test
4+
5+import (
6+ "encoding/binary"
7+ "errors"
8+ "io"
9+ "net"
10+ "os"
11+ "testing"
12+ "time"
13+
14+ "github.com/bnema/wlturbo"
15+ "github.com/bnema/wlturbo/protocol/core"
16+ "golang.org/x/sys/unix"
17+)
18+
19+func pair(t *testing.T) (*net.UnixConn, *net.UnixConn) {
20+ t.Helper()
21+ f, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
22+ if err != nil {
23+ t.Fatal(err)
24+ }
25+ a := os.NewFile(uintptr(f[0]), "a")
26+ b := os.NewFile(uintptr(f[1]), "b")
27+ ca, e := net.FileConn(a)
28+ a.Close()
29+ if e != nil {
30+ t.Fatal(e)
31+ }
32+ cb, e := net.FileConn(b)
33+ b.Close()
34+ if e != nil {
35+ t.Fatal(e)
36+ }
37+ t.Cleanup(func() { ca.Close(); cb.Close() })
38+ return ca.(*net.UnixConn), cb.(*net.UnixConn)
39+}
40+func frame(id uint32, op uint16, body ...uint32) []byte {
41+ p := make([]byte, 8+4*len(body))
42+ binary.LittleEndian.PutUint32(p, id)
43+ binary.LittleEndian.PutUint32(p[4:], uint32(len(p))<<16|uint32(op))
44+ for i, v := range body {
45+ binary.LittleEndian.PutUint32(p[8+i*4:], v)
46+ }
47+ return p
48+}
49+func receiveRequest(t *testing.T, p *net.UnixConn) {
50+ t.Helper()
51+ p.SetReadDeadline(testDeadline())
52+ b := make([]byte, 256)
53+ if _, e := p.Read(b); e != nil {
54+ t.Fatal(e)
55+ }
56+}
57+func testDeadline() (d time.Time) { return time.Now().Add(2 * time.Second) }
58+func send(t *testing.T, p *net.UnixConn, msg []byte, fd ...int) {
59+ t.Helper()
60+ var oob []byte
61+ if len(fd) > 0 {
62+ oob = unix.UnixRights(fd...)
63+ }
64+ if _, _, e := p.WriteMsgUnix(msg, oob, nil); e != nil {
65+ t.Fatal(e)
66+ }
67+}
68+func TestGeneratedCoreOverSocketpair(t *testing.T) {
69+ c, p := pair(t)
70+ d, err := wlturbo.ConnectFromConn(c)
71+ if err != nil {
72+ t.Fatal(err)
73+ }
74+ defer d.Close()
75+ receiveRequest(t, p) // get_registry
76+ seat := core.NewSeat(d.Context())
77+ if err := d.Registry().Bind(1, core.SeatInterface, 1, seat); err != nil {
78+ t.Fatal(err)
79+ }
80+ receiveRequest(t, p)
81+ kb, err := seat.GetKeyboard()
82+ if err != nil {
83+ t.Fatal(err)
84+ }
85+ receiveRequest(t, p)
86+ var key uint32
87+ kb.OnKey(func(_, _, k, _ uint32) { key = k })
88+ send(t, p, frame(kb.ID(), 3, 1, 2, 42, 1))
89+ if err := d.Dispatch(); err != nil {
90+ t.Fatal(err)
91+ }
92+ if key != 42 {
93+ t.Fatalf("key=%d", key)
94+ }
95+ compositor := core.NewCompositor(d.Context())
96+ if err := d.Registry().Bind(2, core.CompositorInterface, 1, compositor); err != nil {
97+ t.Fatal(err)
98+ }
99+ receiveRequest(t, p)
100+ surface, err := compositor.CreateSurface()
101+ if err != nil {
102+ t.Fatal(err)
103+ }
104+ receiveRequest(t, p)
105+ callback, err := surface.Frame()
106+ if err != nil {
107+ t.Fatal(err)
108+ }
109+ receiveRequest(t, p)
110+ var done uint32
111+ callback.OnDone(func(ms uint32) { done = ms })
112+ send(t, p, frame(callback.ID(), 0, 123))
113+ if err := d.Dispatch(); err != nil {
114+ t.Fatal(err)
115+ }
116+ if done != 123 {
117+ t.Fatalf("done=%d", done)
118+ }
119+ send(t, p, frame(callback.ID(), 0, 124))
120+ if err := d.Dispatch(); !errors.Is(err, wlturbo.ErrUnknownObject) {
121+ t.Fatalf("destroyed callback: %v", err)
122+ }
123+}
124+func TestKeymapFDNoHandlerAndMultipleHandlers(t *testing.T) {
125+ c, p := pair(t)
126+ d, e := wlturbo.ConnectFromConn(c)
127+ if e != nil {
128+ t.Fatal(e)
129+ }
130+ defer d.Close()
131+ receiveRequest(t, p)
132+ kb := core.NewKeyboard(d.Context())
133+ kb.SetID(d.AllocateID())
134+ d.Context().Register(kb)
135+ r, w, e := os.Pipe()
136+ if e != nil {
137+ t.Fatal(e)
138+ }
139+ defer r.Close()
140+ send(t, p, frame(kb.ID(), 0, 1, 8), int(w.Fd()))
141+ w.Close()
142+ if e := d.Dispatch(); e != nil {
143+ t.Fatal(e)
144+ }
145+ buf := make([]byte, 1)
146+ if n, e := r.Read(buf); n != 0 || e != io.EOF {
147+ t.Fatalf("unclaimed FD n=%d err=%v", n, e)
148+ }
149+ r2, w2, e := os.Pipe()
150+ if e != nil {
151+ t.Fatal(e)
152+ }
153+ defer r2.Close()
154+ received := 0
155+ kb.OnKeymap(func(_ uint32, fd *wlturbo.OwnedFD, _ uint32) {
156+ if fd == nil {
157+ t.Fatal("first FD missing")
158+ }
159+ n, e := fd.Take()
160+ if e != nil {
161+ t.Fatal(e)
162+ }
163+ received++
164+ unix.Close(n)
165+ })
166+ kb.OnKeymap(func(_ uint32, fd *wlturbo.OwnedFD, _ uint32) {
167+ if fd != nil {
168+ t.Fatal("duplicated owner")
169+ }
170+ received++
171+ })
172+ send(t, p, frame(kb.ID(), 0, 1, 8), int(w2.Fd()))
173+ w2.Close()
174+ if e := d.Dispatch(); e != nil {
175+ t.Fatal(e)
176+ }
177+ if received != 2 {
178+ t.Fatalf("handlers=%d", received)
179+ }
180+}
181+func TestBufferReleaseAndOfferChild(t *testing.T) {
182+ c, p := pair(t)
183+ d, e := wlturbo.ConnectFromConn(c)
184+ if e != nil {
185+ t.Fatal(e)
186+ }
187+ defer d.Close()
188+ receiveRequest(t, p)
189+ b := core.NewBuffer(d.Context())
190+ b.SetID(d.AllocateID())
191+ d.Context().Register(b)
192+ releases := 0
193+ b.OnRelease(func() { releases++ })
194+ send(t, p, frame(b.ID(), 0))
195+ if e := d.Dispatch(); e != nil {
196+ t.Fatal(e)
197+ }
198+ if releases != 1 {
199+ t.Fatal("release not dispatched")
200+ }
201+ device := core.NewDataDevice(d.Context())
202+ device.SetID(d.AllocateID())
203+ d.Context().Register(device)
204+ var offer *core.DataOffer
205+ device.OnDataOffer(func(o *core.DataOffer) { offer = o })
206+ send(t, p, frame(device.ID(), 0, 0xff000001))
207+ if e := d.Dispatch(); e != nil {
208+ t.Fatal(e)
209+ }
210+ if offer == nil || offer.ID() != 0xff000001 {
211+ t.Fatalf("offer=%v", offer)
212+ }
213+}
214+
215+func TestMissingKeymapDescriptor(t *testing.T) {
216+ c, p := pair(t)
217+ d, e := wlturbo.ConnectFromConn(c)
218+ if e != nil {
219+ t.Fatal(e)
220+ }
221+ defer d.Close()
222+ receiveRequest(t, p)
223+ kb := core.NewKeyboard(d.Context())
224+ kb.SetID(d.AllocateID())
225+ d.Context().Register(kb)
226+ send(t, p, frame(kb.ID(), 0, 1, 8))
227+ if e := d.Dispatch(); !errors.Is(e, wlturbo.ErrMalformedFrame) {
228+ t.Fatalf("missing FD: %v", e)
229+ }
230+}
231+
232+func TestInterleavedFDAndPlainMessages(t *testing.T) {
233+ c, p := pair(t)
234+ d, e := wlturbo.ConnectFromConn(c)
235+ if e != nil {
236+ t.Fatal(e)
237+ }
238+ defer d.Close()
239+ receiveRequest(t, p)
240+ kb := core.NewKeyboard(d.Context())
241+ kb.SetID(d.AllocateID())
242+ d.Context().Register(kb)
243+ calls := 0
244+ kb.OnKeymap(func(_ uint32, fd *wlturbo.OwnedFD, _ uint32) {
245+ if fd == nil {
246+ t.Fatal("FD not assigned")
247+ }
248+ _ = fd.Close()
249+ calls++
250+ })
251+ kb.OnKey(func(_, _, key, _ uint32) {
252+ if key != 33 {
253+ t.Errorf("key=%d", key)
254+ }
255+ calls++
256+ })
257+ r, w, e := os.Pipe()
258+ if e != nil {
259+ t.Fatal(e)
260+ }
261+ defer r.Close()
262+ send(t, p, frame(kb.ID(), 0, 1, 8), int(w.Fd()))
263+ w.Close()
264+ send(t, p, frame(kb.ID(), 3, 1, 2, 33, 1))
265+ if e := d.Dispatch(); e != nil {
266+ t.Fatal(e)
267+ }
268+ if e := d.Dispatch(); e != nil {
269+ t.Fatal(e)
270+ }
271+ if calls != 2 {
272+ t.Fatalf("calls=%d", calls)
273+ }
274+}
275+func TestMalformedGeneratedPayload(t *testing.T) {
276+ c, p := pair(t)
277+ d, e := wlturbo.ConnectFromConn(c)
278+ if e != nil {
279+ t.Fatal(e)
280+ }
281+ defer d.Close()
282+ receiveRequest(t, p)
283+ kb := core.NewKeyboard(d.Context())
284+ kb.SetID(d.AllocateID())
285+ d.Context().Register(kb)
286+ send(t, p, frame(kb.ID(), 3, 1, 2))
287+ if e := d.Dispatch(); !errors.Is(e, wlturbo.ErrMalformedFrame) {
288+ t.Fatalf("truncated key: %v", e)
289+ }
290+}
291+
292+func TestServerChildEventsAfterOffer(t *testing.T) {
293+ c, p := pair(t)
294+ d, e := wlturbo.ConnectFromConn(c)
295+ if e != nil {
296+ t.Fatal(e)
297+ }
298+ defer d.Close()
299+ receiveRequest(t, p)
300+ device := core.NewDataDevice(d.Context())
301+ device.SetID(d.AllocateID())
302+ d.Context().Register(device)
303+ const offerID = 0xff000011
304+ var offer *core.DataOffer
305+ device.OnDataOffer(func(o *core.DataOffer) { offer = o })
306+ send(t, p, frame(device.ID(), 0, offerID))
307+ if e := d.Dispatch(); e != nil {
308+ t.Fatal(e)
309+ }
310+ if offer == nil {
311+ t.Fatal("missing concrete child")
312+ }
313+ got := ""
314+ offer.OnOffer(func(mime string) { got = mime })
315+ payload := append(frame(offerID, 0), 4, 0, 0, 0, 'a', 'b', 'c', 0)
316+ binary.LittleEndian.PutUint32(payload[4:], uint32(len(payload))<<16)
317+ send(t, p, payload)
318+ if e := d.Dispatch(); e != nil {
319+ t.Fatal(e)
320+ }
321+ if got != "abc" {
322+ t.Fatalf("typed child event: %q", got)
323+ }
324+}
325+
326+func TestRegistryBootstrapAnnouncements(t *testing.T) {
327+ c, p := pair(t)
328+ d, e := wlturbo.ConnectFromConn(c)
329+ if e != nil {
330+ t.Fatal(e)
331+ }
332+ defer d.Close()
333+ receiveRequest(t, p)
334+ var got wlturbo.Global
335+ d.Registry().AddHandler("wl_compositor", func(r *wlturbo.Registry, n, v uint32) { got, _ = r.FindGlobalByName(n) })
336+ name := "wl_compositor"
337+ body := make([]byte, 4+4+((len(name)+1+3)&^3)+4)
338+ binary.LittleEndian.PutUint32(body, 17)
339+ binary.LittleEndian.PutUint32(body[4:], uint32(len(name)+1))
340+ copy(body[8:], name)
341+ binary.LittleEndian.PutUint32(body[len(body)-4:], 7)
342+ msg := append(frame(d.Registry().ID(), 0), body...)
343+ binary.LittleEndian.PutUint32(msg[4:], uint32(len(msg))<<16)
344+ send(t, p, msg)
345+ if e := d.Dispatch(); e != nil {
346+ t.Fatal(e)
347+ }
348+ if got.Name != 17 || got.Interface != name || got.Version != 7 {
349+ t.Fatalf("global: %+v", got)
350+ }
351+ send(t, p, frame(d.Registry().ID(), 1, 17))
352+ if e := d.Dispatch(); e != nil {
353+ t.Fatal(e)
354+ }
355+ if _, ok := d.Registry().FindGlobalByName(17); ok {
356+ t.Fatal("global not removed")
357+ }
358+ send(t, p, frame(d.Registry().ID(), 2))
359+ if e := d.Dispatch(); !errors.Is(e, wlturbo.ErrUnknownOpcode) {
360+ t.Fatalf("unknown registry opcode: %v", e)
361+ }
362+}
363+func TestUnclaimedFirstHandlerFDClosed(t *testing.T) {
364+ c, p := pair(t)
365+ d, e := wlturbo.ConnectFromConn(c)
366+ if e != nil {
367+ t.Fatal(e)
368+ }
369+ defer d.Close()
370+ receiveRequest(t, p)
371+ kb := core.NewKeyboard(d.Context())
372+ kb.SetID(d.AllocateID())
373+ d.Context().Register(kb)
374+ kb.OnKeymap(func(_ uint32, fd *wlturbo.OwnedFD, _ uint32) {
375+ if fd == nil {
376+ t.Fatal("missing first owner")
377+ }
378+ })
379+ kb.OnKeymap(func(_ uint32, fd *wlturbo.OwnedFD, _ uint32) {
380+ if fd != nil {
381+ t.Fatal("duplicate owner")
382+ }
383+ })
384+ r, w, e := os.Pipe()
385+ if e != nil {
386+ t.Fatal(e)
387+ }
388+ defer r.Close()
389+ send(t, p, frame(kb.ID(), 0, 1, 8), int(w.Fd()))
390+ w.Close()
391+ if e := d.Dispatch(); e != nil {
392+ t.Fatal(e)
393+ }
394+ if e := r.SetReadDeadline(testDeadline()); e != nil {
395+ t.Fatal(e)
396+ }
397+ var b [1]byte
398+ if n, e := r.Read(b[:]); n != 0 || e != io.EOF {
399+ t.Fatalf("unclaimed FD not closed: %d %v", n, e)
400+ }
401+}
402+
403+func TestDestroyedProxyRejectsRequests(t *testing.T) {
404+ c, p := pair(t)
405+ d, err := wlturbo.ConnectFromConn(c)
406+ if err != nil {
407+ t.Fatal(err)
408+ }
409+ defer d.Close()
410+ receiveRequest(t, p)
411+ surface := core.NewSurface(d.Context())
412+ surface.SetID(d.AllocateID())
413+ d.Context().Register(surface)
414+ if err := surface.Destroy(); err != nil {
415+ t.Fatal(err)
416+ }
417+ receiveRequest(t, p)
418+ if err := surface.Destroy(); err == nil {
419+ t.Fatal("second destroy succeeded")
420+ }
421+ if err := surface.Commit(); err == nil {
422+ t.Fatal("request on destroyed surface succeeded")
423+ }
424+ if _, err := surface.Frame(); err == nil {
425+ t.Fatal("child request on destroyed surface succeeded")
426+ }
427+ p.SetReadDeadline(time.Now().Add(20 * time.Millisecond))
428+ _, err = p.Read(make([]byte, 32))
429+ ne, ok := err.(net.Error)
430+ if !ok || !ne.Timeout() {
431+ t.Fatalf("unexpected bytes on wire: %v", err)
432+ }
433+}
434+
435+func TestConcurrentDestroySendsOnce(t *testing.T) {
436+ c, p := pair(t)
437+ d, err := wlturbo.ConnectFromConn(c)
438+ if err != nil {
439+ t.Fatal(err)
440+ }
441+ defer d.Close()
442+ receiveRequest(t, p)
443+ const rounds, destroyers, committers = 100, 4, 4
444+ totalCommits := 0
445+ for round := 0; round < rounds; round++ {
446+ surface := core.NewSurface(d.Context())
447+ surface.SetID(d.AllocateID())
448+ d.Context().Register(surface)
449+ start := make(chan struct{})
450+ destroyed := make(chan error, destroyers)
451+ committed := make(chan error, committers)
452+ for i := 0; i < destroyers; i++ {
453+ go func() { <-start; destroyed <- surface.Destroy() }()
454+ }
455+ for i := 0; i < committers; i++ {
456+ go func() { <-start; committed <- surface.Commit() }()
457+ }
458+ close(start)
459+ okDestroy, okCommit := 0, 0
460+ for i := 0; i < destroyers; i++ {
461+ if <-destroyed == nil {
462+ okDestroy++
463+ }
464+ }
465+ for i := 0; i < committers; i++ {
466+ if <-committed == nil {
467+ okCommit++
468+ }
469+ }
470+ if okDestroy != 1 {
471+ t.Fatalf("round %d: %d destroys succeeded, want 1", round, okDestroy)
472+ }
473+ totalCommits += okCommit
474+ // Decode every frame: exactly okCommit commits (opcode 6), then one
475+ // destroy (opcode 0) for this surface, and nothing after it.
476+ frames := readFrames(t, p, okCommit+1)
477+ for i, f := range frames {
478+ if f.id != surface.ID() {
479+ t.Fatalf("round %d: frame %d targets object %d", round, i, f.id)
480+ }
481+ last := i == len(frames)-1
482+ if last && f.opcode != 0 || !last && f.opcode != 6 {
483+ t.Fatalf("round %d: frame %d opcode %d (last=%v)", round, i, f.opcode, last)
484+ }
485+ }
486+ p.SetReadDeadline(time.Now().Add(2 * time.Millisecond))
487+ if n, err := p.Read(make([]byte, 8)); n != 0 {
488+ t.Fatalf("round %d: request after destroy (%d bytes, %v)", round, n, err)
489+ }
490+ p.SetReadDeadline(time.Time{})
491+ }
492+ // Ordering is only exercised when some commits won the race.
493+ if totalCommits == 0 {
494+ t.Fatal("no commit succeeded in any round; ordering was not exercised")
495+ }
496+}
497+
498+type wireFrame struct {
499+ id uint32
500+ opcode uint16
501+}
502+
503+func readFrames(t *testing.T, p *net.UnixConn, n int) []wireFrame {
504+ t.Helper()
505+ p.SetReadDeadline(testDeadline())
506+ defer p.SetReadDeadline(time.Time{})
507+ var out []wireFrame
508+ var buf []byte
509+ tmp := make([]byte, 4096)
510+ for len(out) < n {
511+ for len(buf) >= 8 {
512+ size := int(binary.LittleEndian.Uint32(buf[4:8]) >> 16)
513+ if size < 8 || len(buf) < size {
514+ break
515+ }
516+ out = append(out, wireFrame{binary.LittleEndian.Uint32(buf[0:4]), uint16(binary.LittleEndian.Uint32(buf[4:8]))})
517+ buf = buf[size:]
518+ }
519+ if len(out) >= n {
520+ break
521+ }
522+ m, err := p.Read(tmp)
523+ if err != nil {
524+ t.Fatalf("read frames: got %d of %d: %v", len(out), n, err)
525+ }
526+ buf = append(buf, tmp[:m]...)
527+ }
528+ if len(out) != n || len(buf) != 0 {
529+ t.Fatalf("got %d frames (+%d bytes), want %d", len(out), len(buf), n)
530+ }
531+ return out
532+}
A protocol/core/doc.go +8 -0
@@ -0,0 +1,8 @@
1+// Package core contains canonical Wayland core protocol bindings generated from
2+// pinned wayland.xml. Bind globals with display.Registry().Bind and the
3+// corresponding New* constructor. Client-created and server-created children
4+// are registered with their concrete generated types; never replace them with
5+// transport wrappers. On* handlers receive typed values. Received OwnedFDs
6+// are closed after the event unless a handler calls Take; request FDs are
7+// transferred (and closed locally) only after a complete successful send.
8+package core
A protocol/core/wayland_generated.go +4216 -0
@@ -0,0 +1,4216 @@
1+// Code generated by wlturbo-scanner. DO NOT EDIT.
2+// Protocol: wayland
3+// Source: wayland.xml
4+
5+package core
6+
7+import (
8+ "sync"
9+
10+ "github.com/bnema/wlturbo/wl"
11+)
12+
13+// WL_SHM_POOL_ERROR_INVALID_FORMAT is the error invalid_format value.
14+const WL_SHM_POOL_ERROR_INVALID_FORMAT = 0
15+
16+// WL_SHM_POOL_ERROR_INVALID_STRIDE is the error invalid_stride value.
17+const WL_SHM_POOL_ERROR_INVALID_STRIDE = 1
18+
19+// WL_SHM_ERROR_INVALID_FORMAT is the error invalid_format value.
20+const WL_SHM_ERROR_INVALID_FORMAT = 0
21+
22+// WL_SHM_ERROR_INVALID_STRIDE is the error invalid_stride value.
23+const WL_SHM_ERROR_INVALID_STRIDE = 1
24+
25+// ERROR_INVALID_FD is the error invalid_fd value.
26+const ERROR_INVALID_FD = 2
27+
28+// FORMAT_ARGB8888 is the format argb8888 value.
29+const FORMAT_ARGB8888 = 0
30+
31+// FORMAT_XRGB8888 is the format xrgb8888 value.
32+const FORMAT_XRGB8888 = 1
33+
34+// FORMAT_C8 is the format c8 value.
35+const FORMAT_C8 = 0x20203843
36+
37+// FORMAT_RGB332 is the format rgb332 value.
38+const FORMAT_RGB332 = 0x38424752
39+
40+// FORMAT_BGR233 is the format bgr233 value.
41+const FORMAT_BGR233 = 0x38524742
42+
43+// FORMAT_XRGB4444 is the format xrgb4444 value.
44+const FORMAT_XRGB4444 = 0x32315258
45+
46+// FORMAT_XBGR4444 is the format xbgr4444 value.
47+const FORMAT_XBGR4444 = 0x32314258
48+
49+// FORMAT_RGBX4444 is the format rgbx4444 value.
50+const FORMAT_RGBX4444 = 0x32315852
51+
52+// FORMAT_BGRX4444 is the format bgrx4444 value.
53+const FORMAT_BGRX4444 = 0x32315842
54+
55+// FORMAT_ARGB4444 is the format argb4444 value.
56+const FORMAT_ARGB4444 = 0x32315241
57+
58+// FORMAT_ABGR4444 is the format abgr4444 value.
59+const FORMAT_ABGR4444 = 0x32314241
60+
61+// FORMAT_RGBA4444 is the format rgba4444 value.
62+const FORMAT_RGBA4444 = 0x32314152
63+
64+// FORMAT_BGRA4444 is the format bgra4444 value.
65+const FORMAT_BGRA4444 = 0x32314142
66+
67+// FORMAT_XRGB1555 is the format xrgb1555 value.
68+const FORMAT_XRGB1555 = 0x35315258
69+
70+// FORMAT_XBGR1555 is the format xbgr1555 value.
71+const FORMAT_XBGR1555 = 0x35314258
72+
73+// FORMAT_RGBX5551 is the format rgbx5551 value.
74+const FORMAT_RGBX5551 = 0x35315852
75+
76+// FORMAT_BGRX5551 is the format bgrx5551 value.
77+const FORMAT_BGRX5551 = 0x35315842
78+
79+// FORMAT_ARGB1555 is the format argb1555 value.
80+const FORMAT_ARGB1555 = 0x35315241
81+
82+// FORMAT_ABGR1555 is the format abgr1555 value.
83+const FORMAT_ABGR1555 = 0x35314241
84+
85+// FORMAT_RGBA5551 is the format rgba5551 value.
86+const FORMAT_RGBA5551 = 0x35314152
87+
88+// FORMAT_BGRA5551 is the format bgra5551 value.
89+const FORMAT_BGRA5551 = 0x35314142
90+
91+// FORMAT_RGB565 is the format rgb565 value.
92+const FORMAT_RGB565 = 0x36314752
93+
94+// FORMAT_BGR565 is the format bgr565 value.
95+const FORMAT_BGR565 = 0x36314742
96+
97+// FORMAT_RGB888 is the format rgb888 value.
98+const FORMAT_RGB888 = 0x34324752
99+
100+// FORMAT_BGR888 is the format bgr888 value.
101+const FORMAT_BGR888 = 0x34324742
102+
103+// FORMAT_XBGR8888 is the format xbgr8888 value.
104+const FORMAT_XBGR8888 = 0x34324258
105+
106+// FORMAT_RGBX8888 is the format rgbx8888 value.
107+const FORMAT_RGBX8888 = 0x34325852
108+
109+// FORMAT_BGRX8888 is the format bgrx8888 value.
110+const FORMAT_BGRX8888 = 0x34325842
111+
112+// FORMAT_ABGR8888 is the format abgr8888 value.
113+const FORMAT_ABGR8888 = 0x34324241
114+
115+// FORMAT_RGBA8888 is the format rgba8888 value.
116+const FORMAT_RGBA8888 = 0x34324152
117+
118+// FORMAT_BGRA8888 is the format bgra8888 value.
119+const FORMAT_BGRA8888 = 0x34324142
120+
121+// FORMAT_XRGB2101010 is the format xrgb2101010 value.
122+const FORMAT_XRGB2101010 = 0x30335258
123+
124+// FORMAT_XBGR2101010 is the format xbgr2101010 value.
125+const FORMAT_XBGR2101010 = 0x30334258
126+
127+// FORMAT_RGBX1010102 is the format rgbx1010102 value.
128+const FORMAT_RGBX1010102 = 0x30335852
129+
130+// FORMAT_BGRX1010102 is the format bgrx1010102 value.
131+const FORMAT_BGRX1010102 = 0x30335842
132+
133+// FORMAT_ARGB2101010 is the format argb2101010 value.
134+const FORMAT_ARGB2101010 = 0x30335241
135+
136+// FORMAT_ABGR2101010 is the format abgr2101010 value.
137+const FORMAT_ABGR2101010 = 0x30334241
138+
139+// FORMAT_RGBA1010102 is the format rgba1010102 value.
140+const FORMAT_RGBA1010102 = 0x30334152
141+
142+// FORMAT_BGRA1010102 is the format bgra1010102 value.
143+const FORMAT_BGRA1010102 = 0x30334142
144+
145+// FORMAT_YUYV is the format yuyv value.
146+const FORMAT_YUYV = 0x56595559
147+
148+// FORMAT_YVYU is the format yvyu value.
149+const FORMAT_YVYU = 0x55595659
150+
151+// FORMAT_UYVY is the format uyvy value.
152+const FORMAT_UYVY = 0x59565955
153+
154+// FORMAT_VYUY is the format vyuy value.
155+const FORMAT_VYUY = 0x59555956
156+
157+// FORMAT_AYUV is the format ayuv value.
158+const FORMAT_AYUV = 0x56555941
159+
160+// FORMAT_NV12 is the format nv12 value.
161+const FORMAT_NV12 = 0x3231564e
162+
163+// FORMAT_NV21 is the format nv21 value.
164+const FORMAT_NV21 = 0x3132564e
165+
166+// FORMAT_NV16 is the format nv16 value.
167+const FORMAT_NV16 = 0x3631564e
168+
169+// FORMAT_NV61 is the format nv61 value.
170+const FORMAT_NV61 = 0x3136564e
171+
172+// FORMAT_YUV410 is the format yuv410 value.
173+const FORMAT_YUV410 = 0x39565559
174+
175+// FORMAT_YVU410 is the format yvu410 value.
176+const FORMAT_YVU410 = 0x39555659
177+
178+// FORMAT_YUV411 is the format yuv411 value.
179+const FORMAT_YUV411 = 0x31315559
180+
181+// FORMAT_YVU411 is the format yvu411 value.
182+const FORMAT_YVU411 = 0x31315659
183+
184+// FORMAT_YUV420 is the format yuv420 value.
185+const FORMAT_YUV420 = 0x32315559
186+
187+// FORMAT_YVU420 is the format yvu420 value.
188+const FORMAT_YVU420 = 0x32315659
189+
190+// FORMAT_YUV422 is the format yuv422 value.
191+const FORMAT_YUV422 = 0x36315559
192+
193+// FORMAT_YVU422 is the format yvu422 value.
194+const FORMAT_YVU422 = 0x36315659
195+
196+// FORMAT_YUV444 is the format yuv444 value.
197+const FORMAT_YUV444 = 0x34325559
198+
199+// FORMAT_YVU444 is the format yvu444 value.
200+const FORMAT_YVU444 = 0x34325659
201+
202+// FORMAT_R8 is the format r8 value.
203+const FORMAT_R8 = 0x20203852
204+
205+// FORMAT_R16 is the format r16 value.
206+const FORMAT_R16 = 0x20363152
207+
208+// FORMAT_RG88 is the format rg88 value.
209+const FORMAT_RG88 = 0x38384752
210+
211+// FORMAT_GR88 is the format gr88 value.
212+const FORMAT_GR88 = 0x38385247
213+
214+// FORMAT_RG1616 is the format rg1616 value.
215+const FORMAT_RG1616 = 0x32334752
216+
217+// FORMAT_GR1616 is the format gr1616 value.
218+const FORMAT_GR1616 = 0x32335247
219+
220+// FORMAT_XRGB16161616F is the format xrgb16161616f value.
221+const FORMAT_XRGB16161616F = 0x48345258
222+
223+// FORMAT_XBGR16161616F is the format xbgr16161616f value.
224+const FORMAT_XBGR16161616F = 0x48344258
225+
226+// FORMAT_ARGB16161616F is the format argb16161616f value.
227+const FORMAT_ARGB16161616F = 0x48345241
228+
229+// FORMAT_ABGR16161616F is the format abgr16161616f value.
230+const FORMAT_ABGR16161616F = 0x48344241
231+
232+// FORMAT_XYUV8888 is the format xyuv8888 value.
233+const FORMAT_XYUV8888 = 0x56555958
234+
235+// FORMAT_VUY888 is the format vuy888 value.
236+const FORMAT_VUY888 = 0x34325556
237+
238+// FORMAT_VUY101010 is the format vuy101010 value.
239+const FORMAT_VUY101010 = 0x30335556
240+
241+// FORMAT_Y210 is the format y210 value.
242+const FORMAT_Y210 = 0x30313259
243+
244+// FORMAT_Y212 is the format y212 value.
245+const FORMAT_Y212 = 0x32313259
246+
247+// FORMAT_Y216 is the format y216 value.
248+const FORMAT_Y216 = 0x36313259
249+
250+// FORMAT_Y410 is the format y410 value.
251+const FORMAT_Y410 = 0x30313459
252+
253+// FORMAT_Y412 is the format y412 value.
254+const FORMAT_Y412 = 0x32313459
255+
256+// FORMAT_Y416 is the format y416 value.
257+const FORMAT_Y416 = 0x36313459
258+
259+// FORMAT_XVYU2101010 is the format xvyu2101010 value.
260+const FORMAT_XVYU2101010 = 0x30335658
261+
262+// FORMAT_XVYU12_16161616 is the format xvyu12_16161616 value.
263+const FORMAT_XVYU12_16161616 = 0x36335658
264+
265+// FORMAT_XVYU16161616 is the format xvyu16161616 value.
266+const FORMAT_XVYU16161616 = 0x38345658
267+
268+// FORMAT_Y0L0 is the format y0l0 value.
269+const FORMAT_Y0L0 = 0x304c3059
270+
271+// FORMAT_X0L0 is the format x0l0 value.
272+const FORMAT_X0L0 = 0x304c3058
273+
274+// FORMAT_Y0L2 is the format y0l2 value.
275+const FORMAT_Y0L2 = 0x324c3059
276+
277+// FORMAT_X0L2 is the format x0l2 value.
278+const FORMAT_X0L2 = 0x324c3058
279+
280+// FORMAT_YUV420_8BIT is the format yuv420_8bit value.
281+const FORMAT_YUV420_8BIT = 0x38305559
282+
283+// FORMAT_YUV420_10BIT is the format yuv420_10bit value.
284+const FORMAT_YUV420_10BIT = 0x30315559
285+
286+// FORMAT_XRGB8888_A8 is the format xrgb8888_a8 value.
287+const FORMAT_XRGB8888_A8 = 0x38415258
288+
289+// FORMAT_XBGR8888_A8 is the format xbgr8888_a8 value.
290+const FORMAT_XBGR8888_A8 = 0x38414258
291+
292+// FORMAT_RGBX8888_A8 is the format rgbx8888_a8 value.
293+const FORMAT_RGBX8888_A8 = 0x38415852
294+
295+// FORMAT_BGRX8888_A8 is the format bgrx8888_a8 value.
296+const FORMAT_BGRX8888_A8 = 0x38415842
297+
298+// FORMAT_RGB888_A8 is the format rgb888_a8 value.
299+const FORMAT_RGB888_A8 = 0x38413852
300+
301+// FORMAT_BGR888_A8 is the format bgr888_a8 value.
302+const FORMAT_BGR888_A8 = 0x38413842
303+
304+// FORMAT_RGB565_A8 is the format rgb565_a8 value.
305+const FORMAT_RGB565_A8 = 0x38413552
306+
307+// FORMAT_BGR565_A8 is the format bgr565_a8 value.
308+const FORMAT_BGR565_A8 = 0x38413542
309+
310+// FORMAT_NV24 is the format nv24 value.
311+const FORMAT_NV24 = 0x3432564e
312+
313+// FORMAT_NV42 is the format nv42 value.
314+const FORMAT_NV42 = 0x3234564e
315+
316+// FORMAT_P210 is the format p210 value.
317+const FORMAT_P210 = 0x30313250
318+
319+// FORMAT_P010 is the format p010 value.
320+const FORMAT_P010 = 0x30313050
321+
322+// FORMAT_P012 is the format p012 value.
323+const FORMAT_P012 = 0x32313050
324+
325+// FORMAT_P016 is the format p016 value.
326+const FORMAT_P016 = 0x36313050
327+
328+// FORMAT_AXBXGXRX106106106106 is the format axbxgxrx106106106106 value.
329+const FORMAT_AXBXGXRX106106106106 = 0x30314241
330+
331+// FORMAT_NV15 is the format nv15 value.
332+const FORMAT_NV15 = 0x3531564e
333+
334+// FORMAT_Q410 is the format q410 value.
335+const FORMAT_Q410 = 0x30313451
336+
337+// FORMAT_Q401 is the format q401 value.
338+const FORMAT_Q401 = 0x31303451
339+
340+// FORMAT_XRGB16161616 is the format xrgb16161616 value.
341+const FORMAT_XRGB16161616 = 0x38345258
342+
343+// FORMAT_XBGR16161616 is the format xbgr16161616 value.
344+const FORMAT_XBGR16161616 = 0x38344258
345+
346+// FORMAT_ARGB16161616 is the format argb16161616 value.
347+const FORMAT_ARGB16161616 = 0x38345241
348+
349+// FORMAT_ABGR16161616 is the format abgr16161616 value.
350+const FORMAT_ABGR16161616 = 0x38344241
351+
352+// FORMAT_C1 is the format c1 value.
353+const FORMAT_C1 = 0x20203143
354+
355+// FORMAT_C2 is the format c2 value.
356+const FORMAT_C2 = 0x20203243
357+
358+// FORMAT_C4 is the format c4 value.
359+const FORMAT_C4 = 0x20203443
360+
361+// FORMAT_D1 is the format d1 value.
362+const FORMAT_D1 = 0x20203144
363+
364+// FORMAT_D2 is the format d2 value.
365+const FORMAT_D2 = 0x20203244
366+
367+// FORMAT_D4 is the format d4 value.
368+const FORMAT_D4 = 0x20203444
369+
370+// FORMAT_D8 is the format d8 value.
371+const FORMAT_D8 = 0x20203844
372+
373+// FORMAT_R1 is the format r1 value.
374+const FORMAT_R1 = 0x20203152
375+
376+// FORMAT_R2 is the format r2 value.
377+const FORMAT_R2 = 0x20203252
378+
379+// FORMAT_R4 is the format r4 value.
380+const FORMAT_R4 = 0x20203452
381+
382+// FORMAT_R10 is the format r10 value.
383+const FORMAT_R10 = 0x20303152
384+
385+// FORMAT_R12 is the format r12 value.
386+const FORMAT_R12 = 0x20323152
387+
388+// FORMAT_AVUY8888 is the format avuy8888 value.
389+const FORMAT_AVUY8888 = 0x59555641
390+
391+// FORMAT_XVUY8888 is the format xvuy8888 value.
392+const FORMAT_XVUY8888 = 0x59555658
393+
394+// FORMAT_P030 is the format p030 value.
395+const FORMAT_P030 = 0x30333050
396+
397+// FORMAT_RGB161616 is the format rgb161616 value.
398+const FORMAT_RGB161616 = 0x38344752
399+
400+// FORMAT_BGR161616 is the format bgr161616 value.
401+const FORMAT_BGR161616 = 0x38344742
402+
403+// FORMAT_R16F is the format r16f value.
404+const FORMAT_R16F = 0x48202052
405+
406+// FORMAT_GR1616F is the format gr1616f value.
407+const FORMAT_GR1616F = 0x48205247
408+
409+// FORMAT_BGR161616F is the format bgr161616f value.
410+const FORMAT_BGR161616F = 0x48524742
411+
412+// FORMAT_R32F is the format r32f value.
413+const FORMAT_R32F = 0x46202052
414+
415+// FORMAT_GR3232F is the format gr3232f value.
416+const FORMAT_GR3232F = 0x46205247
417+
418+// FORMAT_BGR323232F is the format bgr323232f value.
419+const FORMAT_BGR323232F = 0x46524742
420+
421+// FORMAT_ABGR32323232F is the format abgr32323232f value.
422+const FORMAT_ABGR32323232F = 0x46384241
423+
424+// FORMAT_NV20 is the format nv20 value.
425+const FORMAT_NV20 = 0x3032564e
426+
427+// FORMAT_NV30 is the format nv30 value.
428+const FORMAT_NV30 = 0x3033564e
429+
430+// FORMAT_S010 is the format s010 value.
431+const FORMAT_S010 = 0x30313053
432+
433+// FORMAT_S210 is the format s210 value.
434+const FORMAT_S210 = 0x30313253
435+
436+// FORMAT_S410 is the format s410 value.
437+const FORMAT_S410 = 0x30313453
438+
439+// FORMAT_S012 is the format s012 value.
440+const FORMAT_S012 = 0x32313053
441+
442+// FORMAT_S212 is the format s212 value.
443+const FORMAT_S212 = 0x32313253
444+
445+// FORMAT_S412 is the format s412 value.
446+const FORMAT_S412 = 0x32313453
447+
448+// FORMAT_S016 is the format s016 value.
449+const FORMAT_S016 = 0x36313053
450+
451+// FORMAT_S216 is the format s216 value.
452+const FORMAT_S216 = 0x36313253
453+
454+// FORMAT_S416 is the format s416 value.
455+const FORMAT_S416 = 0x36313453
456+
457+// FORMAT_XVUY2101010 is the format xvuy2101010 value.
458+const FORMAT_XVUY2101010 = 0x30335958
459+
460+// FORMAT_P230 is the format p230 value.
461+const FORMAT_P230 = 0x30333250
462+
463+// FORMAT_T430 is the format t430 value.
464+const FORMAT_T430 = 0x30333454
465+
466+// FORMAT_Y8 is the format y8 value.
467+const FORMAT_Y8 = 0x59455247
468+
469+// FORMAT_XYYY2101010 is the format xyyy2101010 value.
470+const FORMAT_XYYY2101010 = 0x34415059
471+
472+// ERROR_INVALID_FINISH is the error invalid_finish value.
473+const ERROR_INVALID_FINISH = 0
474+
475+// WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK is the error invalid_action_mask value.
476+const WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK = 1
477+
478+// ERROR_INVALID_ACTION is the error invalid_action value.
479+const ERROR_INVALID_ACTION = 2
480+
481+// ERROR_INVALID_OFFER is the error invalid_offer value.
482+const ERROR_INVALID_OFFER = 3
483+
484+// WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK is the error invalid_action_mask value.
485+const WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK = 0
486+
487+// ERROR_INVALID_SOURCE is the error invalid_source value.
488+const ERROR_INVALID_SOURCE = 1
489+
490+// WL_DATA_DEVICE_ERROR_ROLE is the error role value.
491+const WL_DATA_DEVICE_ERROR_ROLE = 0
492+
493+// ERROR_USED_SOURCE is the error used_source value.
494+const ERROR_USED_SOURCE = 1
495+
496+// DND_ACTION_NONE is the dnd_action none value.
497+const DND_ACTION_NONE = 0
498+
499+// DND_ACTION_COPY is the dnd_action copy value.
500+const DND_ACTION_COPY = 1
501+
502+// DND_ACTION_MOVE is the dnd_action move value.
503+const DND_ACTION_MOVE = 2
504+
505+// DND_ACTION_ASK is the dnd_action ask value.
506+const DND_ACTION_ASK = 4
507+
508+// WL_SHELL_ERROR_ROLE is the error role value.
509+const WL_SHELL_ERROR_ROLE = 0
510+
511+// RESIZE_NONE is the resize none value.
512+const RESIZE_NONE = 0
513+
514+// RESIZE_TOP is the resize top value.
515+const RESIZE_TOP = 1
516+
517+// RESIZE_BOTTOM is the resize bottom value.
518+const RESIZE_BOTTOM = 2
519+
520+// RESIZE_LEFT is the resize left value.
521+const RESIZE_LEFT = 4
522+
523+// RESIZE_TOP_LEFT is the resize top_left value.
524+const RESIZE_TOP_LEFT = 5
525+
526+// RESIZE_BOTTOM_LEFT is the resize bottom_left value.
527+const RESIZE_BOTTOM_LEFT = 6
528+
529+// RESIZE_RIGHT is the resize right value.
530+const RESIZE_RIGHT = 8
531+
532+// RESIZE_TOP_RIGHT is the resize top_right value.
533+const RESIZE_TOP_RIGHT = 9
534+
535+// RESIZE_BOTTOM_RIGHT is the resize bottom_right value.
536+const RESIZE_BOTTOM_RIGHT = 10
537+
538+// TRANSIENT_INACTIVE is the transient inactive value.
539+const TRANSIENT_INACTIVE = 0x1
540+
541+// FULLSCREEN_METHOD_DEFAULT is the fullscreen_method default value.
542+const FULLSCREEN_METHOD_DEFAULT = 0
543+
544+// FULLSCREEN_METHOD_SCALE is the fullscreen_method scale value.
545+const FULLSCREEN_METHOD_SCALE = 1
546+
547+// FULLSCREEN_METHOD_DRIVER is the fullscreen_method driver value.
548+const FULLSCREEN_METHOD_DRIVER = 2
549+
550+// FULLSCREEN_METHOD_FILL is the fullscreen_method fill value.
551+const FULLSCREEN_METHOD_FILL = 3
552+
553+// ERROR_INVALID_SCALE is the error invalid_scale value.
554+const ERROR_INVALID_SCALE = 0
555+
556+// ERROR_INVALID_TRANSFORM is the error invalid_transform value.
557+const ERROR_INVALID_TRANSFORM = 1
558+
559+// ERROR_INVALID_SIZE is the error invalid_size value.
560+const ERROR_INVALID_SIZE = 2
561+
562+// ERROR_INVALID_OFFSET is the error invalid_offset value.
563+const ERROR_INVALID_OFFSET = 3
564+
565+// ERROR_DEFUNCT_ROLE_OBJECT is the error defunct_role_object value.
566+const ERROR_DEFUNCT_ROLE_OBJECT = 4
567+
568+// ERROR_NO_BUFFER is the error no_buffer value.
569+const ERROR_NO_BUFFER = 5
570+
571+// CAPABILITY_POINTER is the capability pointer value.
572+const CAPABILITY_POINTER = 1
573+
574+// CAPABILITY_KEYBOARD is the capability keyboard value.
575+const CAPABILITY_KEYBOARD = 2
576+
577+// CAPABILITY_TOUCH is the capability touch value.
578+const CAPABILITY_TOUCH = 4
579+
580+// ERROR_MISSING_CAPABILITY is the error missing_capability value.
581+const ERROR_MISSING_CAPABILITY = 0
582+
583+// WL_POINTER_ERROR_ROLE is the error role value.
584+const WL_POINTER_ERROR_ROLE = 0
585+
586+// BUTTON_STATE_RELEASED is the button_state released value.
587+const BUTTON_STATE_RELEASED = 0
588+
589+// BUTTON_STATE_PRESSED is the button_state pressed value.
590+const BUTTON_STATE_PRESSED = 1
591+
592+// AXIS_VERTICAL_SCROLL is the axis vertical_scroll value.
593+const AXIS_VERTICAL_SCROLL = 0
594+
595+// AXIS_HORIZONTAL_SCROLL is the axis horizontal_scroll value.
596+const AXIS_HORIZONTAL_SCROLL = 1
597+
598+// AXIS_SOURCE_WHEEL is the axis_source wheel value.
599+const AXIS_SOURCE_WHEEL = 0
600+
601+// AXIS_SOURCE_FINGER is the axis_source finger value.
602+const AXIS_SOURCE_FINGER = 1
603+
604+// AXIS_SOURCE_CONTINUOUS is the axis_source continuous value.
605+const AXIS_SOURCE_CONTINUOUS = 2
606+
607+// AXIS_SOURCE_WHEEL_TILT is the axis_source wheel_tilt value.
608+const AXIS_SOURCE_WHEEL_TILT = 3
609+
610+// AXIS_RELATIVE_DIRECTION_IDENTICAL is the axis_relative_direction identical value.
611+const AXIS_RELATIVE_DIRECTION_IDENTICAL = 0
612+
613+// AXIS_RELATIVE_DIRECTION_INVERTED is the axis_relative_direction inverted value.
614+const AXIS_RELATIVE_DIRECTION_INVERTED = 1
615+
616+// KEYMAP_FORMAT_NO_KEYMAP is the keymap_format no_keymap value.
617+const KEYMAP_FORMAT_NO_KEYMAP = 0
618+
619+// KEYMAP_FORMAT_XKB_V1 is the keymap_format xkb_v1 value.
620+const KEYMAP_FORMAT_XKB_V1 = 1
621+
622+// KEY_STATE_RELEASED is the key_state released value.
623+const KEY_STATE_RELEASED = 0
624+
625+// KEY_STATE_PRESSED is the key_state pressed value.
626+const KEY_STATE_PRESSED = 1
627+
628+// KEY_STATE_REPEATED is the key_state repeated value.
629+const KEY_STATE_REPEATED = 2
630+
631+// SUBPIXEL_UNKNOWN is the subpixel unknown value.
632+const SUBPIXEL_UNKNOWN = 0
633+
634+// SUBPIXEL_NONE is the subpixel none value.
635+const SUBPIXEL_NONE = 1
636+
637+// SUBPIXEL_HORIZONTAL_RGB is the subpixel horizontal_rgb value.
638+const SUBPIXEL_HORIZONTAL_RGB = 2
639+
640+// SUBPIXEL_HORIZONTAL_BGR is the subpixel horizontal_bgr value.
641+const SUBPIXEL_HORIZONTAL_BGR = 3
642+
643+// SUBPIXEL_VERTICAL_RGB is the subpixel vertical_rgb value.
644+const SUBPIXEL_VERTICAL_RGB = 4
645+
646+// SUBPIXEL_VERTICAL_BGR is the subpixel vertical_bgr value.
647+const SUBPIXEL_VERTICAL_BGR = 5
648+
649+// TRANSFORM_NORMAL is the transform normal value.
650+const TRANSFORM_NORMAL = 0
651+
652+// TRANSFORM_90 is the transform 90 value.
653+const TRANSFORM_90 = 1
654+
655+// TRANSFORM_180 is the transform 180 value.
656+const TRANSFORM_180 = 2
657+
658+// TRANSFORM_270 is the transform 270 value.
659+const TRANSFORM_270 = 3
660+
661+// TRANSFORM_FLIPPED is the transform flipped value.
662+const TRANSFORM_FLIPPED = 4
663+
664+// TRANSFORM_FLIPPED_90 is the transform flipped_90 value.
665+const TRANSFORM_FLIPPED_90 = 5
666+
667+// TRANSFORM_FLIPPED_180 is the transform flipped_180 value.
668+const TRANSFORM_FLIPPED_180 = 6
669+
670+// TRANSFORM_FLIPPED_270 is the transform flipped_270 value.
671+const TRANSFORM_FLIPPED_270 = 7
672+
673+// MODE_CURRENT is the mode current value.
674+const MODE_CURRENT = 0x1
675+
676+// MODE_PREFERRED is the mode preferred value.
677+const MODE_PREFERRED = 0x2
678+
679+// WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE is the error bad_surface value.
680+const WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE = 0
681+
682+// ERROR_BAD_PARENT is the error bad_parent value.
683+const ERROR_BAD_PARENT = 1
684+
685+// WL_SUBSURFACE_ERROR_BAD_SURFACE is the error bad_surface value.
686+const WL_SUBSURFACE_ERROR_BAD_SURFACE = 0
687+
688+// ERROR_INVALID_ACK_REMOVE is the error invalid_ack_remove value.
689+const ERROR_INVALID_ACK_REMOVE = 0
690+
691+// CallbackInterface is the protocol interface name.
692+const CallbackInterface = "wl_callback"
693+
694+// Callback is the generated binding for wl_callback version 1.
695+type Callback struct {
696+ wl.BaseProxy
697+ // handlersMu guards the handler slices: a client may register or replace
698+ // handlers while another goroutine dispatches events.
699+ handlersMu sync.Mutex
700+ onDone []func(callbackData uint32)
701+}
702+
703+// NewCallback returns an unbound Callback.
704+func NewCallback(ctx *wl.Context) *Callback {
705+ obj := &Callback{}
706+ obj.SetContext(ctx)
707+ ctx.Register(obj)
708+ return obj
709+}
710+
711+// OnDone registers a handler for the done event.
712+//
713+// Handlers are appended, so a second call adds another handler rather than
714+// replacing the first. Registration is safe while another goroutine dispatches.
715+func (o *Callback) OnDone(handler func(callbackData uint32)) {
716+ if handler == nil {
717+ return
718+ }
719+ o.handlersMu.Lock()
720+ defer o.handlersMu.Unlock()
721+ o.onDone = append(o.onDone, handler)
722+}
723+
724+// handlersForDone returns the handlers registered for done, taken
725+// under the lock so a handler may register another handler while events are
726+// being dispatched.
727+func (o *Callback) handlersForDone() []func(callbackData uint32) {
728+ o.handlersMu.Lock()
729+ defer o.handlersMu.Unlock()
730+ return o.onDone
731+}
732+
733+// EventSignature returns the declared event argument types in wire order.
734+func (o *Callback) EventSignature(opcode uint16) (string, bool) {
735+ switch opcode {
736+ case 0:
737+ return "uint,", true
738+ default:
739+ return "", false
740+ }
741+}
742+
743+// Dispatch decodes one event on wl_callback and calls the registered handlers.
744+func (o *Callback) Dispatch(event *wl.Event) {
745+ switch event.Opcode {
746+ case 0: // done
747+ callbackData := event.Uint32()
748+
749+ for i, handler := range o.handlersForDone() {
750+ _ = i
751+ handler(callbackData)
752+ }
753+ o.Context().Unregister(o)
754+ default:
755+ }
756+}
757+
758+// CompositorInterface is the protocol interface name.
759+const CompositorInterface = "wl_compositor"
760+
761+// Compositor is the generated binding for wl_compositor version 7.
762+type Compositor struct {
763+ wl.BaseProxy
764+}
765+
766+// NewCompositor returns an unbound Compositor.
767+func NewCompositor(ctx *wl.Context) *Compositor {
768+ obj := &Compositor{}
769+ obj.SetContext(ctx)
770+ ctx.Register(obj)
771+ return obj
772+}
773+
774+// Ask the compositor to create a new surface.
775+func (o *Compositor) CreateSurface() (*Surface, error) {
776+ if err := o.Context().CheckProxy(o); err != nil {
777+ return nil, err
778+ }
779+ child := &Surface{}
780+ child.SetContext(o.Context())
781+ child.SetID(o.Context().AllocateID())
782+ o.Context().Register(child)
783+ if err := o.Context().SendRequest(o, 0, child); err != nil {
784+ o.Context().Unregister(child)
785+ return nil, err
786+ }
787+ return child, nil
788+}
789+
790+// Ask the compositor to create a new region.
791+func (o *Compositor) CreateRegion() (*Region, error) {
792+ if err := o.Context().CheckProxy(o); err != nil {
793+ return nil, err
794+ }
795+ child := &Region{}
796+ child.SetContext(o.Context())
797+ child.SetID(o.Context().AllocateID())
798+ o.Context().Register(child)
799+ if err := o.Context().SendRequest(o, 1, child); err != nil {
800+ o.Context().Unregister(child)
801+ return nil, err
802+ }
803+ return child, nil
804+}
805+
806+// This request destroys the wl_compositor. This has no effect on any other objects.
807+func (o *Compositor) Release() error {
808+ if err := o.Context().CheckProxy(o); err != nil {
809+ return err
810+ }
811+ if err := o.Context().SendDestructor(o, 2); err != nil {
812+ return err
813+ }
814+ return nil
815+}
816+
817+// EventSignature returns the declared event argument types in wire order.
818+func (o *Compositor) EventSignature(opcode uint16) (string, bool) {
819+ switch opcode {
820+ default:
821+ return "", false
822+ }
823+}
824+
825+// Dispatch decodes one event on wl_compositor and calls the registered handlers.
826+func (o *Compositor) Dispatch(event *wl.Event) {
827+ switch event.Opcode {
828+ default:
829+ }
830+}
831+
832+// ShmPoolInterface is the protocol interface name.
833+const ShmPoolInterface = "wl_shm_pool"
834+
835+// ShmPool is the generated binding for wl_shm_pool version 3.
836+type ShmPool struct {
837+ wl.BaseProxy
838+}
839+
840+// NewShmPool returns an unbound ShmPool.
841+func NewShmPool(ctx *wl.Context) *ShmPool {
842+ obj := &ShmPool{}
843+ obj.SetContext(ctx)
844+ ctx.Register(obj)
845+ return obj
846+}
847+
848+// Create a wl_buffer object from the pool. The buffer is created offset bytes into the pool and has width and height as specified. The stride argument specifies the number of bytes from the beginning of one row to the beginning of the next. The format is the pixel format of the buffer and must be one of those advertised through the wl_shm.format event. A buffer will keep a reference to the pool it was created from so it is valid to destroy the pool immediately after creating a buffer from it.
849+func (o *ShmPool) CreateBuffer(offset int32, width int32, height int32, stride int32, format uint32) (*Buffer, error) {
850+ if err := o.Context().CheckProxy(o); err != nil {
851+ return nil, err
852+ }
853+ child := &Buffer{}
854+ child.SetContext(o.Context())
855+ child.SetID(o.Context().AllocateID())
856+ o.Context().Register(child)
857+ if err := o.Context().SendRequest(o, 0, child, offset, width, height, stride, format); err != nil {
858+ o.Context().Unregister(child)
859+ return nil, err
860+ }
861+ return child, nil
862+}
863+
864+// Destroy the shared memory pool. The mmapped memory will be released when all buffers that have been created from this pool are gone.
865+func (o *ShmPool) Destroy() error {
866+ if err := o.Context().CheckProxy(o); err != nil {
867+ return err
868+ }
869+ if err := o.Context().SendDestructor(o, 1); err != nil {
870+ return err
871+ }
872+ return nil
873+}
874+
875+// This request will cause the server to remap the backing memory for the pool from the file descriptor passed when the pool was created, but using the new size. This request can only be used to make the pool bigger. This request only changes the amount of bytes that are mmapped by the server and does not touch the file corresponding to the file descriptor passed at creation time. It is the client's responsibility to ensure that the file is at least as big as the new pool size.
876+func (o *ShmPool) Resize(size int32) error {
877+ if err := o.Context().CheckProxy(o); err != nil {
878+ return err
879+ }
880+ if err := o.Context().SendRequest(o, 2, size); err != nil {
881+ return err
882+ }
883+ return nil
884+}
885+
886+// EventSignature returns the declared event argument types in wire order.
887+func (o *ShmPool) EventSignature(opcode uint16) (string, bool) {
888+ switch opcode {
889+ default:
890+ return "", false
891+ }
892+}
893+
894+// Dispatch decodes one event on wl_shm_pool and calls the registered handlers.
895+func (o *ShmPool) Dispatch(event *wl.Event) {
896+ switch event.Opcode {
897+ default:
898+ }
899+}
900+
901+// ShmInterface is the protocol interface name.
902+const ShmInterface = "wl_shm"
903+
904+// Shm is the generated binding for wl_shm version 3.
905+type Shm struct {
906+ wl.BaseProxy
907+ // handlersMu guards the handler slices: a client may register or replace
908+ // handlers while another goroutine dispatches events.
909+ handlersMu sync.Mutex
910+ onFormat []func(format uint32)
911+}
912+
913+// NewShm returns an unbound Shm.
914+func NewShm(ctx *wl.Context) *Shm {
915+ obj := &Shm{}
916+ obj.SetContext(ctx)
917+ ctx.Register(obj)
918+ return obj
919+}
920+
921+// Create a new wl_shm_pool object. The pool can be used to create shared memory based buffer objects. The server will mmap size bytes of the passed file descriptor, to use as backing memory for the pool.
922+func (o *Shm) CreatePool(fd int, size int32) (*ShmPool, error) {
923+ if err := o.Context().CheckProxy(o); err != nil {
924+ return nil, err
925+ }
926+ child := &ShmPool{}
927+ child.SetContext(o.Context())
928+ child.SetID(o.Context().AllocateID())
929+ o.Context().Register(child)
930+ if err := o.Context().SendRequestWithFDs(o, 0, []int{fd}, child, uintptr(fd), size); err != nil {
931+ o.Context().Unregister(child)
932+ return nil, err
933+ }
934+ _ = wl.CloseSentFD(fd)
935+ return child, nil
936+}
937+
938+// Using this request a client can tell the server that it is not going to use the shm object anymore. Objects created via this interface remain unaffected.
939+func (o *Shm) Release() error {
940+ if err := o.Context().CheckProxy(o); err != nil {
941+ return err
942+ }
943+ if err := o.Context().SendDestructor(o, 1); err != nil {
944+ return err
945+ }
946+ return nil
947+}
948+
949+// OnFormat registers a handler for the format event.
950+//
951+// Handlers are appended, so a second call adds another handler rather than
952+// replacing the first. Registration is safe while another goroutine dispatches.
953+func (o *Shm) OnFormat(handler func(format uint32)) {
954+ if handler == nil {
955+ return
956+ }
957+ o.handlersMu.Lock()
958+ defer o.handlersMu.Unlock()
959+ o.onFormat = append(o.onFormat, handler)
960+}
961+
962+// handlersForFormat returns the handlers registered for format, taken
963+// under the lock so a handler may register another handler while events are
964+// being dispatched.
965+func (o *Shm) handlersForFormat() []func(format uint32) {
966+ o.handlersMu.Lock()
967+ defer o.handlersMu.Unlock()
968+ return o.onFormat
969+}
970+
971+// EventSignature returns the declared event argument types in wire order.
972+func (o *Shm) EventSignature(opcode uint16) (string, bool) {
973+ switch opcode {
974+ case 0:
975+ return "uint,", true
976+ default:
977+ return "", false
978+ }
979+}
980+
981+// Dispatch decodes one event on wl_shm and calls the registered handlers.
982+func (o *Shm) Dispatch(event *wl.Event) {
983+ switch event.Opcode {
984+ case 0: // format
985+ format := event.Uint32()
986+
987+ for i, handler := range o.handlersForFormat() {
988+ _ = i
989+ handler(format)
990+ }
991+ default:
992+ }
993+}
994+
995+// BufferInterface is the protocol interface name.
996+const BufferInterface = "wl_buffer"
997+
998+// Buffer is the generated binding for wl_buffer version 1.
999+type Buffer struct {
1000+ wl.BaseProxy
1001+ // handlersMu guards the handler slices: a client may register or replace
1002+ // handlers while another goroutine dispatches events.
1003+ handlersMu sync.Mutex
1004+ onRelease []func()
1005+}
1006+
1007+// NewBuffer returns an unbound Buffer.
1008+func NewBuffer(ctx *wl.Context) *Buffer {
1009+ obj := &Buffer{}
1010+ obj.SetContext(ctx)
1011+ ctx.Register(obj)
1012+ return obj
1013+}
1014+
1015+// Destroy a buffer. If and how you need to release the backing storage is defined by the buffer factory interface. For possible side-effects to a surface, see wl_surface.attach.
1016+func (o *Buffer) Destroy() error {
1017+ if err := o.Context().CheckProxy(o); err != nil {
1018+ return err
1019+ }
1020+ if err := o.Context().SendDestructor(o, 0); err != nil {
1021+ return err
1022+ }
1023+ return nil
1024+}
1025+
1026+// OnRelease registers a handler for the release event.
1027+//
1028+// Handlers are appended, so a second call adds another handler rather than
1029+// replacing the first. Registration is safe while another goroutine dispatches.
1030+func (o *Buffer) OnRelease(handler func()) {
1031+ if handler == nil {
1032+ return
1033+ }
1034+ o.handlersMu.Lock()
1035+ defer o.handlersMu.Unlock()
1036+ o.onRelease = append(o.onRelease, handler)
1037+}
1038+
1039+// handlersForRelease returns the handlers registered for release, taken
1040+// under the lock so a handler may register another handler while events are
1041+// being dispatched.
1042+func (o *Buffer) handlersForRelease() []func() {
1043+ o.handlersMu.Lock()
1044+ defer o.handlersMu.Unlock()
1045+ return o.onRelease
1046+}
1047+
1048+// EventSignature returns the declared event argument types in wire order.
1049+func (o *Buffer) EventSignature(opcode uint16) (string, bool) {
1050+ switch opcode {
1051+ case 0:
1052+ return "", true
1053+ default:
1054+ return "", false
1055+ }
1056+}
1057+
1058+// Dispatch decodes one event on wl_buffer and calls the registered handlers.
1059+func (o *Buffer) Dispatch(event *wl.Event) {
1060+ switch event.Opcode {
1061+ case 0: // release
1062+
1063+ for i, handler := range o.handlersForRelease() {
1064+ _ = i
1065+ handler()
1066+ }
1067+ default:
1068+ }
1069+}
1070+
1071+// DataOfferInterface is the protocol interface name.
1072+const DataOfferInterface = "wl_data_offer"
1073+
1074+// DataOffer is the generated binding for wl_data_offer version 4.
1075+type DataOffer struct {
1076+ wl.BaseProxy
1077+ // handlersMu guards the handler slices: a client may register or replace
1078+ // handlers while another goroutine dispatches events.
1079+ handlersMu sync.Mutex
1080+ onOffer []func(mimeType string)
1081+ onSourceActions []func(sourceActions uint32)
1082+ onAction []func(dndAction uint32)
1083+}
1084+
1085+// NewDataOffer returns an unbound DataOffer.
1086+func NewDataOffer(ctx *wl.Context) *DataOffer {
1087+ obj := &DataOffer{}
1088+ obj.SetContext(ctx)
1089+ ctx.Register(obj)
1090+ return obj
1091+}
1092+
1093+// Indicate that the client can accept the given mime type, or NULL for not accepted. For objects of version 2 or older, this request is used by the client to give feedback whether the client can receive the given mime type, or NULL if none is accepted; the feedback does not determine whether the drag-and-drop operation succeeds or not. For objects of version 3 or newer, this request determines the final result of the drag-and-drop operation. If the end result is that no mime types were accepted, the drag-and-drop operation will be cancelled and the corresponding drag source will receive wl_data_source.cancelled. Clients may still use this event in conjunction with wl_data_source.action for feedback.
1094+func (o *DataOffer) Accept(serial uint32, mimeType string) error {
1095+ if err := o.Context().CheckProxy(o); err != nil {
1096+ return err
1097+ }
1098+ if err := o.Context().SendRequest(o, 0, serial, mimeType); err != nil {
1099+ return err
1100+ }
1101+ return nil
1102+}
1103+
1104+// To transfer the offered data, the client issues this request and indicates the mime type it wants to receive. The transfer happens through the passed file descriptor (typically created with the pipe system call). The source client writes the data in the mime type representation requested and then closes the file descriptor. The receiving client reads from the read end of the pipe until EOF and then closes its end, at which point the transfer is complete. This request may happen multiple times for different mime types, both before and after wl_data_device.drop. Drag-and-drop destination clients may preemptively fetch data or examine it more closely to determine acceptance.
1105+func (o *DataOffer) Receive(mimeType string, fd int) error {
1106+ if err := o.Context().CheckProxy(o); err != nil {
1107+ return err
1108+ }
1109+ if err := o.Context().SendRequestWithFDs(o, 1, []int{fd}, mimeType, uintptr(fd)); err != nil {
1110+ return err
1111+ }
1112+ _ = wl.CloseSentFD(fd)
1113+ return nil
1114+}
1115+
1116+// Destroy the data offer.
1117+func (o *DataOffer) Destroy() error {
1118+ if err := o.Context().CheckProxy(o); err != nil {
1119+ return err
1120+ }
1121+ if err := o.Context().SendDestructor(o, 2); err != nil {
1122+ return err
1123+ }
1124+ return nil
1125+}
1126+
1127+// Notifies the compositor that the drag destination successfully finished the drag-and-drop operation. Upon receiving this request, the compositor will emit wl_data_source.dnd_finished on the drag source client. It is a client error to perform other requests than wl_data_offer.destroy after this one. It is also an error to perform this request after a NULL mime type has been set in wl_data_offer.accept or no action was received through wl_data_offer.action. If wl_data_offer.finish request is received for a non drag and drop operation, the invalid_finish protocol error is raised.
1128+func (o *DataOffer) Finish() error {
1129+ if err := o.Context().CheckProxy(o); err != nil {
1130+ return err
1131+ }
1132+ if err := o.Context().SendRequest(o, 3); err != nil {
1133+ return err
1134+ }
1135+ return nil
1136+}
1137+
1138+// Sets the actions that the destination side client supports for this operation. This request may trigger the emission of wl_data_source.action and wl_data_offer.action events if the compositor needs to change the selected action. This request can be called multiple times throughout the drag-and-drop operation, typically in response to wl_data_device.enter or wl_data_device.motion events. This request determines the final result of the drag-and-drop operation. If the end result is that no action is accepted, the drag source will receive wl_data_source.cancelled. The dnd_actions argument must contain only values expressed in the wl_data_device_manager.dnd_actions enum, and the preferred_action argument must only contain one of those values set, otherwise it will result in a protocol error. While managing an "ask" action, the destination drag-and-drop client may perform further wl_data_offer.receive requests, and is expected to perform one last wl_data_offer.set_actions request with a preferred action other than "ask" (and optionally wl_data_offer.accept) before requesting wl_data_offer.finish, in order to convey the action selected by the user. If the preferred action is not in the wl_data_offer.source_actions mask, an error will be raised. If the "ask" action is dismissed (e.g. user cancellation), the client is expected to perform wl_data_offer.destroy right away. This request can only be made on drag-and-drop offers, a protocol error will be raised otherwise.
1139+func (o *DataOffer) SetActions(dndActions uint32, preferredAction uint32) error {
1140+ if err := o.Context().CheckProxy(o); err != nil {
1141+ return err
1142+ }
1143+ if err := o.Context().SendRequest(o, 4, dndActions, preferredAction); err != nil {
1144+ return err
1145+ }
1146+ return nil
1147+}
1148+
1149+// OnOffer registers a handler for the offer event.
1150+//
1151+// Handlers are appended, so a second call adds another handler rather than
1152+// replacing the first. Registration is safe while another goroutine dispatches.
1153+func (o *DataOffer) OnOffer(handler func(mimeType string)) {
1154+ if handler == nil {
1155+ return
1156+ }
1157+ o.handlersMu.Lock()
1158+ defer o.handlersMu.Unlock()
1159+ o.onOffer = append(o.onOffer, handler)
1160+}
1161+
1162+// handlersForOffer returns the handlers registered for offer, taken
1163+// under the lock so a handler may register another handler while events are
1164+// being dispatched.
1165+func (o *DataOffer) handlersForOffer() []func(mimeType string) {
1166+ o.handlersMu.Lock()
1167+ defer o.handlersMu.Unlock()
1168+ return o.onOffer
1169+}
1170+
1171+// OnSourceActions registers a handler for the source_actions event.
1172+//
1173+// Handlers are appended, so a second call adds another handler rather than
1174+// replacing the first. Registration is safe while another goroutine dispatches.
1175+func (o *DataOffer) OnSourceActions(handler func(sourceActions uint32)) {
1176+ if handler == nil {
1177+ return
1178+ }
1179+ o.handlersMu.Lock()
1180+ defer o.handlersMu.Unlock()
1181+ o.onSourceActions = append(o.onSourceActions, handler)
1182+}
1183+
1184+// handlersForSourceActions returns the handlers registered for source_actions, taken
1185+// under the lock so a handler may register another handler while events are
1186+// being dispatched.
1187+func (o *DataOffer) handlersForSourceActions() []func(sourceActions uint32) {
1188+ o.handlersMu.Lock()
1189+ defer o.handlersMu.Unlock()
1190+ return o.onSourceActions
1191+}
1192+
1193+// OnAction registers a handler for the action event.
1194+//
1195+// Handlers are appended, so a second call adds another handler rather than
1196+// replacing the first. Registration is safe while another goroutine dispatches.
1197+func (o *DataOffer) OnAction(handler func(dndAction uint32)) {
1198+ if handler == nil {
1199+ return
1200+ }
1201+ o.handlersMu.Lock()
1202+ defer o.handlersMu.Unlock()
1203+ o.onAction = append(o.onAction, handler)
1204+}
1205+
1206+// handlersForAction returns the handlers registered for action, taken
1207+// under the lock so a handler may register another handler while events are
1208+// being dispatched.
1209+func (o *DataOffer) handlersForAction() []func(dndAction uint32) {
1210+ o.handlersMu.Lock()
1211+ defer o.handlersMu.Unlock()
1212+ return o.onAction
1213+}
1214+
1215+// EventSignature returns the declared event argument types in wire order.
1216+func (o *DataOffer) EventSignature(opcode uint16) (string, bool) {
1217+ switch opcode {
1218+ case 0:
1219+ return "string,", true
1220+ case 1:
1221+ return "uint,", true
1222+ case 2:
1223+ return "uint,", true
1224+ default:
1225+ return "", false
1226+ }
1227+}
1228+
1229+// Dispatch decodes one event on wl_data_offer and calls the registered handlers.
1230+func (o *DataOffer) Dispatch(event *wl.Event) {
1231+ switch event.Opcode {
1232+ case 0: // offer
1233+ mimeType := event.String()
1234+
1235+ for i, handler := range o.handlersForOffer() {
1236+ _ = i
1237+ handler(mimeType)
1238+ }
1239+ case 1: // source_actions
1240+ sourceActions := event.Uint32()
1241+
1242+ for i, handler := range o.handlersForSourceActions() {
1243+ _ = i
1244+ handler(sourceActions)
1245+ }
1246+ case 2: // action
1247+ dndAction := event.Uint32()
1248+
1249+ for i, handler := range o.handlersForAction() {
1250+ _ = i
1251+ handler(dndAction)
1252+ }
1253+ default:
1254+ }
1255+}
1256+
1257+// DataSourceInterface is the protocol interface name.
1258+const DataSourceInterface = "wl_data_source"
1259+
1260+// DataSource is the generated binding for wl_data_source version 4.
1261+type DataSource struct {
1262+ wl.BaseProxy
1263+ // handlersMu guards the handler slices: a client may register or replace
1264+ // handlers while another goroutine dispatches events.
1265+ handlersMu sync.Mutex
1266+ onTarget []func(mimeType string)
1267+ onSend []func(mimeType string, fd *wl.OwnedFD)
1268+ onCancelled []func()
1269+ onDndDropPerformed []func()
1270+ onDndFinished []func()
1271+ onAction []func(dndAction uint32)
1272+}
1273+
1274+// NewDataSource returns an unbound DataSource.
1275+func NewDataSource(ctx *wl.Context) *DataSource {
1276+ obj := &DataSource{}
1277+ obj.SetContext(ctx)
1278+ ctx.Register(obj)
1279+ return obj
1280+}
1281+
1282+// This request adds a mime type to the set of mime types advertised to targets. Can be called several times to offer multiple types.
1283+func (o *DataSource) Offer(mimeType string) error {
1284+ if err := o.Context().CheckProxy(o); err != nil {
1285+ return err
1286+ }
1287+ if err := o.Context().SendRequest(o, 0, mimeType); err != nil {
1288+ return err
1289+ }
1290+ return nil
1291+}
1292+
1293+// Destroy the data source.
1294+func (o *DataSource) Destroy() error {
1295+ if err := o.Context().CheckProxy(o); err != nil {
1296+ return err
1297+ }
1298+ if err := o.Context().SendDestructor(o, 1); err != nil {
1299+ return err
1300+ }
1301+ return nil
1302+}
1303+
1304+// Sets the actions that the source side client supports for this operation. This request may trigger wl_data_source.action and wl_data_offer.action events if the compositor needs to change the selected action. The dnd_actions argument must contain only values expressed in the wl_data_device_manager.dnd_actions enum, otherwise it will result in a protocol error. This request must be made once only, and can only be made on sources used in drag-and-drop, so it must be performed before wl_data_device.start_drag. Attempting to use the source other than for drag-and-drop will raise a protocol error.
1305+func (o *DataSource) SetActions(dndActions uint32) error {
1306+ if err := o.Context().CheckProxy(o); err != nil {
1307+ return err
1308+ }
1309+ if err := o.Context().SendRequest(o, 2, dndActions); err != nil {
1310+ return err
1311+ }
1312+ return nil
1313+}
1314+
1315+// OnTarget registers a handler for the target event.
1316+//
1317+// Handlers are appended, so a second call adds another handler rather than
1318+// replacing the first. Registration is safe while another goroutine dispatches.
1319+func (o *DataSource) OnTarget(handler func(mimeType string)) {
1320+ if handler == nil {
1321+ return
1322+ }
1323+ o.handlersMu.Lock()
1324+ defer o.handlersMu.Unlock()
1325+ o.onTarget = append(o.onTarget, handler)
1326+}
1327+
1328+// handlersForTarget returns the handlers registered for target, taken
1329+// under the lock so a handler may register another handler while events are
1330+// being dispatched.
1331+func (o *DataSource) handlersForTarget() []func(mimeType string) {
1332+ o.handlersMu.Lock()
1333+ defer o.handlersMu.Unlock()
1334+ return o.onTarget
1335+}
1336+
1337+// OnSend registers a handler for the send event.
1338+//
1339+// Handlers are appended, so a second call adds another handler rather than
1340+// replacing the first. Registration is safe while another goroutine dispatches.
1341+func (o *DataSource) OnSend(handler func(mimeType string, fd *wl.OwnedFD)) {
1342+ if handler == nil {
1343+ return
1344+ }
1345+ o.handlersMu.Lock()
1346+ defer o.handlersMu.Unlock()
1347+ o.onSend = append(o.onSend, handler)
1348+}
1349+
1350+// handlersForSend returns the handlers registered for send, taken
1351+// under the lock so a handler may register another handler while events are
1352+// being dispatched.
1353+func (o *DataSource) handlersForSend() []func(mimeType string, fd *wl.OwnedFD) {
1354+ o.handlersMu.Lock()
1355+ defer o.handlersMu.Unlock()
1356+ return o.onSend
1357+}
1358+
1359+// OnCancelled registers a handler for the cancelled event.
1360+//
1361+// Handlers are appended, so a second call adds another handler rather than
1362+// replacing the first. Registration is safe while another goroutine dispatches.
1363+func (o *DataSource) OnCancelled(handler func()) {
1364+ if handler == nil {
1365+ return
1366+ }
1367+ o.handlersMu.Lock()
1368+ defer o.handlersMu.Unlock()
1369+ o.onCancelled = append(o.onCancelled, handler)
1370+}
1371+
1372+// handlersForCancelled returns the handlers registered for cancelled, taken
1373+// under the lock so a handler may register another handler while events are
1374+// being dispatched.
1375+func (o *DataSource) handlersForCancelled() []func() {
1376+ o.handlersMu.Lock()
1377+ defer o.handlersMu.Unlock()
1378+ return o.onCancelled
1379+}
1380+
1381+// OnDndDropPerformed registers a handler for the dnd_drop_performed event.
1382+//
1383+// Handlers are appended, so a second call adds another handler rather than
1384+// replacing the first. Registration is safe while another goroutine dispatches.
1385+func (o *DataSource) OnDndDropPerformed(handler func()) {
1386+ if handler == nil {
1387+ return
1388+ }
1389+ o.handlersMu.Lock()
1390+ defer o.handlersMu.Unlock()
1391+ o.onDndDropPerformed = append(o.onDndDropPerformed, handler)
1392+}
1393+
1394+// handlersForDndDropPerformed returns the handlers registered for dnd_drop_performed, taken
1395+// under the lock so a handler may register another handler while events are
1396+// being dispatched.
1397+func (o *DataSource) handlersForDndDropPerformed() []func() {
1398+ o.handlersMu.Lock()
1399+ defer o.handlersMu.Unlock()
1400+ return o.onDndDropPerformed
1401+}
1402+
1403+// OnDndFinished registers a handler for the dnd_finished event.
1404+//
1405+// Handlers are appended, so a second call adds another handler rather than
1406+// replacing the first. Registration is safe while another goroutine dispatches.
1407+func (o *DataSource) OnDndFinished(handler func()) {
1408+ if handler == nil {
1409+ return
1410+ }
1411+ o.handlersMu.Lock()
1412+ defer o.handlersMu.Unlock()
1413+ o.onDndFinished = append(o.onDndFinished, handler)
1414+}
1415+
1416+// handlersForDndFinished returns the handlers registered for dnd_finished, taken
1417+// under the lock so a handler may register another handler while events are
1418+// being dispatched.
1419+func (o *DataSource) handlersForDndFinished() []func() {
1420+ o.handlersMu.Lock()
1421+ defer o.handlersMu.Unlock()
1422+ return o.onDndFinished
1423+}
1424+
1425+// OnAction registers a handler for the action event.
1426+//
1427+// Handlers are appended, so a second call adds another handler rather than
1428+// replacing the first. Registration is safe while another goroutine dispatches.
1429+func (o *DataSource) OnAction(handler func(dndAction uint32)) {
1430+ if handler == nil {
1431+ return
1432+ }
1433+ o.handlersMu.Lock()
1434+ defer o.handlersMu.Unlock()
1435+ o.onAction = append(o.onAction, handler)
1436+}
1437+
1438+// handlersForAction returns the handlers registered for action, taken
1439+// under the lock so a handler may register another handler while events are
1440+// being dispatched.
1441+func (o *DataSource) handlersForAction() []func(dndAction uint32) {
1442+ o.handlersMu.Lock()
1443+ defer o.handlersMu.Unlock()
1444+ return o.onAction
1445+}
1446+
1447+// EventSignature returns the declared event argument types in wire order.
1448+func (o *DataSource) EventSignature(opcode uint16) (string, bool) {
1449+ switch opcode {
1450+ case 0:
1451+ return "string,", true
1452+ case 1:
1453+ return "string,fd,", true
1454+ case 2:
1455+ return "", true
1456+ case 3:
1457+ return "", true
1458+ case 4:
1459+ return "", true
1460+ case 5:
1461+ return "uint,", true
1462+ default:
1463+ return "", false
1464+ }
1465+}
1466+
1467+// Dispatch decodes one event on wl_data_source and calls the registered handlers.
1468+func (o *DataSource) Dispatch(event *wl.Event) {
1469+ switch event.Opcode {
1470+ case 0: // target
1471+ mimeType := event.String()
1472+
1473+ for i, handler := range o.handlersForTarget() {
1474+ _ = i
1475+ handler(mimeType)
1476+ }
1477+ case 1: // send
1478+ mimeType := event.String()
1479+ fd := event.FD()
1480+ ownedfd := fd
1481+ defer ownedfd.Close()
1482+
1483+ for i, handler := range o.handlersForSend() {
1484+ if i > 0 {
1485+ fd = nil
1486+ }
1487+ handler(mimeType, fd)
1488+ }
1489+ case 2: // cancelled
1490+
1491+ for i, handler := range o.handlersForCancelled() {
1492+ _ = i
1493+ handler()
1494+ }
1495+ case 3: // dnd_drop_performed
1496+
1497+ for i, handler := range o.handlersForDndDropPerformed() {
1498+ _ = i
1499+ handler()
1500+ }
1501+ case 4: // dnd_finished
1502+
1503+ for i, handler := range o.handlersForDndFinished() {
1504+ _ = i
1505+ handler()
1506+ }
1507+ case 5: // action
1508+ dndAction := event.Uint32()
1509+
1510+ for i, handler := range o.handlersForAction() {
1511+ _ = i
1512+ handler(dndAction)
1513+ }
1514+ default:
1515+ }
1516+}
1517+
1518+// DataDeviceInterface is the protocol interface name.
1519+const DataDeviceInterface = "wl_data_device"
1520+
1521+// DataDevice is the generated binding for wl_data_device version 4.
1522+type DataDevice struct {
1523+ wl.BaseProxy
1524+ // handlersMu guards the handler slices: a client may register or replace
1525+ // handlers while another goroutine dispatches events.
1526+ handlersMu sync.Mutex
1527+ onDataOffer []func(idObject *DataOffer)
1528+ onEnter []func(serial uint32, surfaceID uint32, x wl.Fixed, y wl.Fixed, idID uint32)
1529+ onLeave []func()
1530+ onMotion []func(time uint32, x wl.Fixed, y wl.Fixed)
1531+ onDrop []func()
1532+ onSelection []func(idID uint32)
1533+}
1534+
1535+// NewDataDevice returns an unbound DataDevice.
1536+func NewDataDevice(ctx *wl.Context) *DataDevice {
1537+ obj := &DataDevice{}
1538+ obj.SetContext(ctx)
1539+ ctx.Register(obj)
1540+ return obj
1541+}
1542+
1543+// This request asks the compositor to start a drag-and-drop operation on behalf of the client. The source argument is the data source that provides the data for the eventual data transfer. If source is NULL, enter, leave and motion events are sent only to the client that initiated the drag and the client is expected to handle the data passing internally. If source is destroyed, the drag-and-drop session will be cancelled. The origin surface is the surface where the drag originates and the client must have an active implicit grab that matches the serial. The icon surface is an optional (can be NULL) surface that provides an icon to be moved around with the cursor. Initially, the top-left corner of the icon surface is placed at the cursor hotspot, but subsequent wl_surface.offset requests can move the relative position. Attach requests must be confirmed with wl_surface.commit as usual. The icon surface is given the role of a drag-and-drop icon. If the icon surface already has another role, it raises a protocol error. The input region is ignored for wl_surfaces with the role of a drag-and-drop icon. The given source may not be used in any further set_selection or start_drag requests. Attempting to reuse a previously-used source may send a used_source error.
1544+func (o *DataDevice) StartDrag(source *DataSource, origin *Surface, icon *Surface, serial uint32) error {
1545+ if err := o.Context().CheckProxy(o); err != nil {
1546+ return err
1547+ }
1548+ var arg0 wl.Object
1549+ if source != nil {
1550+ arg0 = source
1551+ }
1552+ var arg1 wl.Object
1553+ if origin != nil {
1554+ arg1 = origin
1555+ }
1556+ var arg2 wl.Object
1557+ if icon != nil {
1558+ arg2 = icon
1559+ }
1560+ if err := o.Context().SendRequest(o, 0, arg0, arg1, arg2, serial); err != nil {
1561+ return err
1562+ }
1563+ return nil
1564+}
1565+
1566+// This request asks the compositor to set the selection to the data from the source on behalf of the client. To unset the selection, set the source to NULL. The given source may not be used in any further set_selection or start_drag requests. Attempting to reuse a previously-used source may send a used_source error.
1567+func (o *DataDevice) SetSelection(source *DataSource, serial uint32) error {
1568+ if err := o.Context().CheckProxy(o); err != nil {
1569+ return err
1570+ }
1571+ var arg0 wl.Object
1572+ if source != nil {
1573+ arg0 = source
1574+ }
1575+ if err := o.Context().SendRequest(o, 1, arg0, serial); err != nil {
1576+ return err
1577+ }
1578+ return nil
1579+}
1580+
1581+// This request destroys the data device.
1582+func (o *DataDevice) Release() error {
1583+ if err := o.Context().CheckProxy(o); err != nil {
1584+ return err
1585+ }
1586+ if err := o.Context().SendDestructor(o, 2); err != nil {
1587+ return err
1588+ }
1589+ return nil
1590+}
1591+
1592+// OnDataOffer registers a handler for the data_offer event.
1593+//
1594+// Handlers are appended, so a second call adds another handler rather than
1595+// replacing the first. Registration is safe while another goroutine dispatches.
1596+func (o *DataDevice) OnDataOffer(handler func(idObject *DataOffer)) {
1597+ if handler == nil {
1598+ return
1599+ }
1600+ o.handlersMu.Lock()
1601+ defer o.handlersMu.Unlock()
1602+ o.onDataOffer = append(o.onDataOffer, handler)
1603+}
1604+
1605+// handlersForDataOffer returns the handlers registered for data_offer, taken
1606+// under the lock so a handler may register another handler while events are
1607+// being dispatched.
1608+func (o *DataDevice) handlersForDataOffer() []func(idObject *DataOffer) {
1609+ o.handlersMu.Lock()
1610+ defer o.handlersMu.Unlock()
1611+ return o.onDataOffer
1612+}
1613+
1614+// OnEnter registers a handler for the enter event.
1615+//
1616+// Handlers are appended, so a second call adds another handler rather than
1617+// replacing the first. Registration is safe while another goroutine dispatches.
1618+func (o *DataDevice) OnEnter(handler func(serial uint32, surfaceID uint32, x wl.Fixed, y wl.Fixed, idID uint32)) {
1619+ if handler == nil {
1620+ return
1621+ }
1622+ o.handlersMu.Lock()
1623+ defer o.handlersMu.Unlock()
1624+ o.onEnter = append(o.onEnter, handler)
1625+}
1626+
1627+// handlersForEnter returns the handlers registered for enter, taken
1628+// under the lock so a handler may register another handler while events are
1629+// being dispatched.
1630+func (o *DataDevice) handlersForEnter() []func(serial uint32, surfaceID uint32, x wl.Fixed, y wl.Fixed, idID uint32) {
1631+ o.handlersMu.Lock()
1632+ defer o.handlersMu.Unlock()
1633+ return o.onEnter
1634+}
1635+
1636+// OnLeave registers a handler for the leave event.
1637+//
1638+// Handlers are appended, so a second call adds another handler rather than
1639+// replacing the first. Registration is safe while another goroutine dispatches.
1640+func (o *DataDevice) OnLeave(handler func()) {
1641+ if handler == nil {
1642+ return
1643+ }
1644+ o.handlersMu.Lock()
1645+ defer o.handlersMu.Unlock()
1646+ o.onLeave = append(o.onLeave, handler)
1647+}
1648+
1649+// handlersForLeave returns the handlers registered for leave, taken
1650+// under the lock so a handler may register another handler while events are
1651+// being dispatched.
1652+func (o *DataDevice) handlersForLeave() []func() {
1653+ o.handlersMu.Lock()
1654+ defer o.handlersMu.Unlock()
1655+ return o.onLeave
1656+}
1657+
1658+// OnMotion registers a handler for the motion event.
1659+//
1660+// Handlers are appended, so a second call adds another handler rather than
1661+// replacing the first. Registration is safe while another goroutine dispatches.
1662+func (o *DataDevice) OnMotion(handler func(time uint32, x wl.Fixed, y wl.Fixed)) {
1663+ if handler == nil {
1664+ return
1665+ }
1666+ o.handlersMu.Lock()
1667+ defer o.handlersMu.Unlock()
1668+ o.onMotion = append(o.onMotion, handler)
1669+}
1670+
1671+// handlersForMotion returns the handlers registered for motion, taken
1672+// under the lock so a handler may register another handler while events are
1673+// being dispatched.
1674+func (o *DataDevice) handlersForMotion() []func(time uint32, x wl.Fixed, y wl.Fixed) {
1675+ o.handlersMu.Lock()
1676+ defer o.handlersMu.Unlock()
1677+ return o.onMotion
1678+}
1679+
1680+// OnDrop registers a handler for the drop event.
1681+//
1682+// Handlers are appended, so a second call adds another handler rather than
1683+// replacing the first. Registration is safe while another goroutine dispatches.
1684+func (o *DataDevice) OnDrop(handler func()) {
1685+ if handler == nil {
1686+ return
1687+ }
1688+ o.handlersMu.Lock()
1689+ defer o.handlersMu.Unlock()
1690+ o.onDrop = append(o.onDrop, handler)
1691+}
1692+
1693+// handlersForDrop returns the handlers registered for drop, taken
1694+// under the lock so a handler may register another handler while events are
1695+// being dispatched.
1696+func (o *DataDevice) handlersForDrop() []func() {
1697+ o.handlersMu.Lock()
1698+ defer o.handlersMu.Unlock()
1699+ return o.onDrop
1700+}
1701+
1702+// OnSelection registers a handler for the selection event.
1703+//
1704+// Handlers are appended, so a second call adds another handler rather than
1705+// replacing the first. Registration is safe while another goroutine dispatches.
1706+func (o *DataDevice) OnSelection(handler func(idID uint32)) {
1707+ if handler == nil {
1708+ return
1709+ }
1710+ o.handlersMu.Lock()
1711+ defer o.handlersMu.Unlock()
1712+ o.onSelection = append(o.onSelection, handler)
1713+}
1714+
1715+// handlersForSelection returns the handlers registered for selection, taken
1716+// under the lock so a handler may register another handler while events are
1717+// being dispatched.
1718+func (o *DataDevice) handlersForSelection() []func(idID uint32) {
1719+ o.handlersMu.Lock()
1720+ defer o.handlersMu.Unlock()
1721+ return o.onSelection
1722+}
1723+
1724+// EventSignature returns the declared event argument types in wire order.
1725+func (o *DataDevice) EventSignature(opcode uint16) (string, bool) {
1726+ switch opcode {
1727+ case 0:
1728+ return "new_id,", true
1729+ case 1:
1730+ return "uint,object,fixed,fixed,object,", true
1731+ case 2:
1732+ return "", true
1733+ case 3:
1734+ return "uint,fixed,fixed,", true
1735+ case 4:
1736+ return "", true
1737+ case 5:
1738+ return "object,", true
1739+ default:
1740+ return "", false
1741+ }
1742+}
1743+
1744+// Dispatch decodes one event on wl_data_device and calls the registered handlers.
1745+func (o *DataDevice) Dispatch(event *wl.Event) {
1746+ switch event.Opcode {
1747+ case 0: // data_offer
1748+ idID := event.Uint32()
1749+ idObject := &DataOffer{}
1750+ idObject.SetContext(o.Context())
1751+ idObject.SetID(idID)
1752+ o.Context().Register(idObject)
1753+
1754+ for i, handler := range o.handlersForDataOffer() {
1755+ _ = i
1756+ handler(idObject)
1757+ }
1758+ case 1: // enter
1759+ serial := event.Uint32()
1760+ surfaceID := event.Uint32()
1761+ x := event.Fixed()
1762+ y := event.Fixed()
1763+ idID := event.Uint32()
1764+
1765+ for i, handler := range o.handlersForEnter() {
1766+ _ = i
1767+ handler(serial, surfaceID, x, y, idID)
1768+ }
1769+ case 2: // leave
1770+
1771+ for i, handler := range o.handlersForLeave() {
1772+ _ = i
1773+ handler()
1774+ }
1775+ case 3: // motion
1776+ time := event.Uint32()
1777+ x := event.Fixed()
1778+ y := event.Fixed()
1779+
1780+ for i, handler := range o.handlersForMotion() {
1781+ _ = i
1782+ handler(time, x, y)
1783+ }
1784+ case 4: // drop
1785+
1786+ for i, handler := range o.handlersForDrop() {
1787+ _ = i
1788+ handler()
1789+ }
1790+ case 5: // selection
1791+ idID := event.Uint32()
1792+
1793+ for i, handler := range o.handlersForSelection() {
1794+ _ = i
1795+ handler(idID)
1796+ }
1797+ default:
1798+ }
1799+}
1800+
1801+// DataDeviceManagerInterface is the protocol interface name.
1802+const DataDeviceManagerInterface = "wl_data_device_manager"
1803+
1804+// DataDeviceManager is the generated binding for wl_data_device_manager version 4.
1805+type DataDeviceManager struct {
1806+ wl.BaseProxy
1807+}
1808+
1809+// NewDataDeviceManager returns an unbound DataDeviceManager.
1810+func NewDataDeviceManager(ctx *wl.Context) *DataDeviceManager {
1811+ obj := &DataDeviceManager{}
1812+ obj.SetContext(ctx)
1813+ ctx.Register(obj)
1814+ return obj
1815+}
1816+
1817+// Create a new data source.
1818+func (o *DataDeviceManager) CreateDataSource() (*DataSource, error) {
1819+ if err := o.Context().CheckProxy(o); err != nil {
1820+ return nil, err
1821+ }
1822+ child := &DataSource{}
1823+ child.SetContext(o.Context())
1824+ child.SetID(o.Context().AllocateID())
1825+ o.Context().Register(child)
1826+ if err := o.Context().SendRequest(o, 0, child); err != nil {
1827+ o.Context().Unregister(child)
1828+ return nil, err
1829+ }
1830+ return child, nil
1831+}
1832+
1833+// Create a new data device for a given seat.
1834+func (o *DataDeviceManager) GetDataDevice(seat *Seat) (*DataDevice, error) {
1835+ if err := o.Context().CheckProxy(o); err != nil {
1836+ return nil, err
1837+ }
1838+ var arg1 wl.Object
1839+ if seat != nil {
1840+ arg1 = seat
1841+ }
1842+ child := &DataDevice{}
1843+ child.SetContext(o.Context())
1844+ child.SetID(o.Context().AllocateID())
1845+ o.Context().Register(child)
1846+ if err := o.Context().SendRequest(o, 1, child, arg1); err != nil {
1847+ o.Context().Unregister(child)
1848+ return nil, err
1849+ }
1850+ return child, nil
1851+}
1852+
1853+// This request destroys the wl_data_device_manager. This has no effect on any other objects.
1854+func (o *DataDeviceManager) Release() error {
1855+ if err := o.Context().CheckProxy(o); err != nil {
1856+ return err
1857+ }
1858+ if err := o.Context().SendDestructor(o, 2); err != nil {
1859+ return err
1860+ }
1861+ return nil
1862+}
1863+
1864+// EventSignature returns the declared event argument types in wire order.
1865+func (o *DataDeviceManager) EventSignature(opcode uint16) (string, bool) {
1866+ switch opcode {
1867+ default:
1868+ return "", false
1869+ }
1870+}
1871+
1872+// Dispatch decodes one event on wl_data_device_manager and calls the registered handlers.
1873+func (o *DataDeviceManager) Dispatch(event *wl.Event) {
1874+ switch event.Opcode {
1875+ default:
1876+ }
1877+}
1878+
1879+// ShellInterface is the protocol interface name.
1880+const ShellInterface = "wl_shell"
1881+
1882+// Shell is the generated binding for wl_shell version 1.
1883+type Shell struct {
1884+ wl.BaseProxy
1885+}
1886+
1887+// NewShell returns an unbound Shell.
1888+func NewShell(ctx *wl.Context) *Shell {
1889+ obj := &Shell{}
1890+ obj.SetContext(ctx)
1891+ ctx.Register(obj)
1892+ return obj
1893+}
1894+
1895+// Create a shell surface for an existing surface. This gives the wl_surface the role of a shell surface. If the wl_surface already has another role, it raises a protocol error. Only one shell surface can be associated with a given surface.
1896+func (o *Shell) GetShellSurface(surface *Surface) (*ShellSurface, error) {
1897+ if err := o.Context().CheckProxy(o); err != nil {
1898+ return nil, err
1899+ }
1900+ var arg1 wl.Object
1901+ if surface != nil {
1902+ arg1 = surface
1903+ }
1904+ child := &ShellSurface{}
1905+ child.SetContext(o.Context())
1906+ child.SetID(o.Context().AllocateID())
1907+ o.Context().Register(child)
1908+ if err := o.Context().SendRequest(o, 0, child, arg1); err != nil {
1909+ o.Context().Unregister(child)
1910+ return nil, err
1911+ }
1912+ return child, nil
1913+}
1914+
1915+// EventSignature returns the declared event argument types in wire order.
1916+func (o *Shell) EventSignature(opcode uint16) (string, bool) {
1917+ switch opcode {
1918+ default:
1919+ return "", false
1920+ }
1921+}
1922+
1923+// Dispatch decodes one event on wl_shell and calls the registered handlers.
1924+func (o *Shell) Dispatch(event *wl.Event) {
1925+ switch event.Opcode {
1926+ default:
1927+ }
1928+}
1929+
1930+// ShellSurfaceInterface is the protocol interface name.
1931+const ShellSurfaceInterface = "wl_shell_surface"
1932+
1933+// ShellSurface is the generated binding for wl_shell_surface version 1.
1934+type ShellSurface struct {
1935+ wl.BaseProxy
1936+ // handlersMu guards the handler slices: a client may register or replace
1937+ // handlers while another goroutine dispatches events.
1938+ handlersMu sync.Mutex
1939+ onPing []func(serial uint32)
1940+ onConfigure []func(edges uint32, width int32, height int32)
1941+ onPopupDone []func()
1942+}
1943+
1944+// NewShellSurface returns an unbound ShellSurface.
1945+func NewShellSurface(ctx *wl.Context) *ShellSurface {
1946+ obj := &ShellSurface{}
1947+ obj.SetContext(ctx)
1948+ ctx.Register(obj)
1949+ return obj
1950+}
1951+
1952+// A client must respond to a ping event with a pong request or the client may be deemed unresponsive.
1953+func (o *ShellSurface) Pong(serial uint32) error {
1954+ if err := o.Context().CheckProxy(o); err != nil {
1955+ return err
1956+ }
1957+ if err := o.Context().SendRequest(o, 0, serial); err != nil {
1958+ return err
1959+ }
1960+ return nil
1961+}
1962+
1963+// Start a pointer-driven move of the surface. This request must be used in response to a button press event. The server may ignore move requests depending on the state of the surface (e.g. fullscreen or maximized).
1964+func (o *ShellSurface) Move(seat *Seat, serial uint32) error {
1965+ if err := o.Context().CheckProxy(o); err != nil {
1966+ return err
1967+ }
1968+ var arg0 wl.Object
1969+ if seat != nil {
1970+ arg0 = seat
1971+ }
1972+ if err := o.Context().SendRequest(o, 1, arg0, serial); err != nil {
1973+ return err
1974+ }
1975+ return nil
1976+}
1977+
1978+// Start a pointer-driven resizing of the surface. This request must be used in response to a button press event. The server may ignore resize requests depending on the state of the surface (e.g. fullscreen or maximized).
1979+func (o *ShellSurface) Resize(seat *Seat, serial uint32, edges uint32) error {
1980+ if err := o.Context().CheckProxy(o); err != nil {
1981+ return err
1982+ }
1983+ var arg0 wl.Object
1984+ if seat != nil {
1985+ arg0 = seat
1986+ }
1987+ if err := o.Context().SendRequest(o, 2, arg0, serial, edges); err != nil {
1988+ return err
1989+ }
1990+ return nil
1991+}
1992+
1993+// Map the surface as a toplevel surface. A toplevel surface is not fullscreen, maximized or transient.
1994+func (o *ShellSurface) SetToplevel() error {
1995+ if err := o.Context().CheckProxy(o); err != nil {
1996+ return err
1997+ }
1998+ if err := o.Context().SendRequest(o, 3); err != nil {
1999+ return err
2000+ }
2001+ return nil
2002+}
2003+
2004+// Map the surface relative to an existing surface. The x and y arguments specify the location of the upper left corner of the surface relative to the upper left corner of the parent surface, in surface-local coordinates. The flags argument controls details of the transient behaviour.
2005+func (o *ShellSurface) SetTransient(parent *Surface, x int32, y int32, flags uint32) error {
2006+ if err := o.Context().CheckProxy(o); err != nil {
2007+ return err
2008+ }
2009+ var arg0 wl.Object
2010+ if parent != nil {
2011+ arg0 = parent
2012+ }
2013+ if err := o.Context().SendRequest(o, 4, arg0, x, y, flags); err != nil {
2014+ return err
2015+ }
2016+ return nil
2017+}
2018+
2019+// Map the surface as a fullscreen surface. If an output parameter is given then the surface will be made fullscreen on that output. If the client does not specify the output then the compositor will apply its policy - usually choosing the output on which the surface has the biggest surface area. The client may specify a method to resolve a size conflict between the output size and the surface size - this is provided through the method parameter. The framerate parameter is used only when the method is set to "driver", to indicate the preferred framerate. A value of 0 indicates that the client does not care about framerate. The framerate is specified in mHz, that is framerate of 60000 is 60Hz. A method of "scale" or "driver" implies a scaling operation of the surface, either via a direct scaling operation or a change of the output mode. This will override any kind of output scaling, so that mapping a surface with a buffer size equal to the mode can fill the screen independent of buffer_scale. A method of "fill" means we don't scale up the buffer, however any output scale is applied. This means that you may run into an edge case where the application maps a buffer with the same size of the output mode but buffer_scale 1 (thus making a surface larger than the output). In this case it is allowed to downscale the results to fit the screen. The compositor must reply to this request with a configure event with the dimensions for the output on which the surface will be made fullscreen.
2020+func (o *ShellSurface) SetFullscreen(method uint32, framerate uint32, output *Output) error {
2021+ if err := o.Context().CheckProxy(o); err != nil {
2022+ return err
2023+ }
2024+ var arg2 wl.Object
2025+ if output != nil {
2026+ arg2 = output
2027+ }
2028+ if err := o.Context().SendRequest(o, 5, method, framerate, arg2); err != nil {
2029+ return err
2030+ }
2031+ return nil
2032+}
2033+
2034+// Map the surface as a popup. A popup surface is a transient surface with an added pointer grab. An existing implicit grab will be changed to owner-events mode, and the popup grab will continue after the implicit grab ends (i.e. releasing the mouse button does not cause the popup to be unmapped). The popup grab continues until the window is destroyed or a mouse button is pressed in any other client's window. A click in any of the client's surfaces is reported as normal, however, clicks in other clients' surfaces will be discarded and trigger the callback. The x and y arguments specify the location of the upper left corner of the surface relative to the upper left corner of the parent surface, in surface-local coordinates.
2035+func (o *ShellSurface) SetPopup(seat *Seat, serial uint32, parent *Surface, x int32, y int32, flags uint32) error {
2036+ if err := o.Context().CheckProxy(o); err != nil {
2037+ return err
2038+ }
2039+ var arg0 wl.Object
2040+ if seat != nil {
2041+ arg0 = seat
2042+ }
2043+ var arg2 wl.Object
2044+ if parent != nil {
2045+ arg2 = parent
2046+ }
2047+ if err := o.Context().SendRequest(o, 6, arg0, serial, arg2, x, y, flags); err != nil {
2048+ return err
2049+ }
2050+ return nil
2051+}
2052+
2053+// Map the surface as a maximized surface. If an output parameter is given then the surface will be maximized on that output. If the client does not specify the output then the compositor will apply its policy - usually choosing the output on which the surface has the biggest surface area. The compositor will reply with a configure event telling the expected new surface size. The operation is completed on the next buffer attach to this surface. A maximized surface typically fills the entire output it is bound to, except for desktop elements such as panels. This is the main difference between a maximized shell surface and a fullscreen shell surface. The details depend on the compositor implementation.
2054+func (o *ShellSurface) SetMaximized(output *Output) error {
2055+ if err := o.Context().CheckProxy(o); err != nil {
2056+ return err
2057+ }
2058+ var arg0 wl.Object
2059+ if output != nil {
2060+ arg0 = output
2061+ }
2062+ if err := o.Context().SendRequest(o, 7, arg0); err != nil {
2063+ return err
2064+ }
2065+ return nil
2066+}
2067+
2068+// Set a short title for the surface. This string may be used to identify the surface in a task bar, window list, or other user interface elements provided by the compositor. The string must be encoded in UTF-8.
2069+func (o *ShellSurface) SetTitle(title string) error {
2070+ if err := o.Context().CheckProxy(o); err != nil {
2071+ return err
2072+ }
2073+ if err := o.Context().SendRequest(o, 8, title); err != nil {
2074+ return err
2075+ }
2076+ return nil
2077+}
2078+
2079+// Set a class for the surface. The surface class identifies the general class of applications to which the surface belongs. A common convention is to use the file name (or the full path if it is a non-standard location) of the application's .desktop file as the class.
2080+func (o *ShellSurface) SetClass(class string) error {
2081+ if err := o.Context().CheckProxy(o); err != nil {
2082+ return err
2083+ }
2084+ if err := o.Context().SendRequest(o, 9, class); err != nil {
2085+ return err
2086+ }
2087+ return nil
2088+}
2089+
2090+// OnPing registers a handler for the ping event.
2091+//
2092+// Handlers are appended, so a second call adds another handler rather than
2093+// replacing the first. Registration is safe while another goroutine dispatches.
2094+func (o *ShellSurface) OnPing(handler func(serial uint32)) {
2095+ if handler == nil {
2096+ return
2097+ }
2098+ o.handlersMu.Lock()
2099+ defer o.handlersMu.Unlock()
2100+ o.onPing = append(o.onPing, handler)
2101+}
2102+
2103+// handlersForPing returns the handlers registered for ping, taken
2104+// under the lock so a handler may register another handler while events are
2105+// being dispatched.
2106+func (o *ShellSurface) handlersForPing() []func(serial uint32) {
2107+ o.handlersMu.Lock()
2108+ defer o.handlersMu.Unlock()
2109+ return o.onPing
2110+}
2111+
2112+// OnConfigure registers a handler for the configure event.
2113+//
2114+// Handlers are appended, so a second call adds another handler rather than
2115+// replacing the first. Registration is safe while another goroutine dispatches.
2116+func (o *ShellSurface) OnConfigure(handler func(edges uint32, width int32, height int32)) {
2117+ if handler == nil {
2118+ return
2119+ }
2120+ o.handlersMu.Lock()
2121+ defer o.handlersMu.Unlock()
2122+ o.onConfigure = append(o.onConfigure, handler)
2123+}
2124+
2125+// handlersForConfigure returns the handlers registered for configure, taken
2126+// under the lock so a handler may register another handler while events are
2127+// being dispatched.
2128+func (o *ShellSurface) handlersForConfigure() []func(edges uint32, width int32, height int32) {
2129+ o.handlersMu.Lock()
2130+ defer o.handlersMu.Unlock()
2131+ return o.onConfigure
2132+}
2133+
2134+// OnPopupDone registers a handler for the popup_done event.
2135+//
2136+// Handlers are appended, so a second call adds another handler rather than
2137+// replacing the first. Registration is safe while another goroutine dispatches.
2138+func (o *ShellSurface) OnPopupDone(handler func()) {
2139+ if handler == nil {
2140+ return
2141+ }
2142+ o.handlersMu.Lock()
2143+ defer o.handlersMu.Unlock()
2144+ o.onPopupDone = append(o.onPopupDone, handler)
2145+}
2146+
2147+// handlersForPopupDone returns the handlers registered for popup_done, taken
2148+// under the lock so a handler may register another handler while events are
2149+// being dispatched.
2150+func (o *ShellSurface) handlersForPopupDone() []func() {
2151+ o.handlersMu.Lock()
2152+ defer o.handlersMu.Unlock()
2153+ return o.onPopupDone
2154+}
2155+
2156+// EventSignature returns the declared event argument types in wire order.
2157+func (o *ShellSurface) EventSignature(opcode uint16) (string, bool) {
2158+ switch opcode {
2159+ case 0:
2160+ return "uint,", true
2161+ case 1:
2162+ return "uint,int,int,", true
2163+ case 2:
2164+ return "", true
2165+ default:
2166+ return "", false
2167+ }
2168+}
2169+
2170+// Dispatch decodes one event on wl_shell_surface and calls the registered handlers.
2171+func (o *ShellSurface) Dispatch(event *wl.Event) {
2172+ switch event.Opcode {
2173+ case 0: // ping
2174+ serial := event.Uint32()
2175+
2176+ for i, handler := range o.handlersForPing() {
2177+ _ = i
2178+ handler(serial)
2179+ }
2180+ case 1: // configure
2181+ edges := event.Uint32()
2182+ width := event.Int32()
2183+ height := event.Int32()
2184+
2185+ for i, handler := range o.handlersForConfigure() {
2186+ _ = i
2187+ handler(edges, width, height)
2188+ }
2189+ case 2: // popup_done
2190+
2191+ for i, handler := range o.handlersForPopupDone() {
2192+ _ = i
2193+ handler()
2194+ }
2195+ default:
2196+ }
2197+}
2198+
2199+// SurfaceInterface is the protocol interface name.
2200+const SurfaceInterface = "wl_surface"
2201+
2202+// Surface is the generated binding for wl_surface version 7.
2203+type Surface struct {
2204+ wl.BaseProxy
2205+ // handlersMu guards the handler slices: a client may register or replace
2206+ // handlers while another goroutine dispatches events.
2207+ handlersMu sync.Mutex
2208+ onEnter []func(outputID uint32)
2209+ onLeave []func(outputID uint32)
2210+ onPreferredBufferScale []func(factor int32)
2211+ onPreferredBufferTransform []func(transform uint32)
2212+}
2213+
2214+// NewSurface returns an unbound Surface.
2215+func NewSurface(ctx *wl.Context) *Surface {
2216+ obj := &Surface{}
2217+ obj.SetContext(ctx)
2218+ ctx.Register(obj)
2219+ return obj
2220+}
2221+
2222+// Deletes the surface and invalidates its object ID.
2223+func (o *Surface) Destroy() error {
2224+ if err := o.Context().CheckProxy(o); err != nil {
2225+ return err
2226+ }
2227+ if err := o.Context().SendDestructor(o, 0); err != nil {
2228+ return err
2229+ }
2230+ return nil
2231+}
2232+
2233+// Set a buffer as the content of this surface. The new size of the surface is calculated based on the buffer size transformed by the inverse buffer_transform and the inverse buffer_scale. This means that at commit time the supplied buffer size must be an integer multiple of the buffer_scale. If that's not the case, an invalid_size error is sent. The x and y arguments specify the location of the new pending buffer's upper left corner, relative to the current buffer's upper left corner, in surface-local coordinates. In other words, the x and y, combined with the new surface size define in which directions the surface's size changes. Setting anything other than 0 as x and y arguments is discouraged, and should instead be replaced with using the separate wl_surface.offset request. When the bound wl_surface version is 5 or higher, passing any non-zero x or y is a protocol violation, and will result in an 'invalid_offset' error being raised. The x and y arguments are ignored and do not change the pending state. To achieve equivalent semantics, use wl_surface.offset. Surface contents are double-buffered state, see wl_surface.commit. The initial surface contents are void; there is no content. wl_surface.attach assigns the given wl_buffer as the pending wl_buffer. wl_surface.commit makes the pending wl_buffer the new surface contents, and the size of the surface becomes the size calculated from the wl_buffer, as described above. After commit, there is no pending buffer until the next attach. Committing a pending wl_buffer allows the compositor to read the pixels in the wl_buffer. The compositor may access the pixels at any time after the wl_surface.commit request. When the compositor will not access the pixels anymore, it will send the wl_buffer.release event. Only after receiving wl_buffer.release, the client may reuse the wl_buffer. A wl_buffer that has been attached and then replaced by another attach instead of committed will not receive a release event, and is not used by the compositor. If a pending wl_buffer has been committed to more than one wl_surface, the delivery of wl_buffer.release events becomes undefined. A well behaved client should not rely on wl_buffer.release events in this case. Instead, clients hitting this case should use wl_surface.get_release or use a protocol extension providing per-commit release notifications (if none of these options are available, a fallback can be implemented by creating multiple wl_buffer objects from the same backing storage). Destroying the wl_buffer after wl_buffer.release does not change the surface contents. Destroying the wl_buffer before wl_buffer.release is allowed as long as the underlying buffer storage isn't re-used (this can happen e.g. on client process termination). However, if the client destroys the wl_buffer before receiving the wl_buffer.release event and mutates the underlying buffer storage, the surface contents become undefined immediately. If wl_surface.attach is sent with a NULL wl_buffer, the following wl_surface.commit will remove the surface content. If a pending wl_buffer has been destroyed, the result is not specified. Many compositors are known to remove the surface content on the following wl_surface.commit, but this behaviour is not universal. Clients seeking to maximise compatibility should not destroy pending buffers and should ensure that they explicitly remove content from surfaces, even after destroying buffers.
2234+func (o *Surface) Attach(buffer *Buffer, x int32, y int32) error {
2235+ if err := o.Context().CheckProxy(o); err != nil {
2236+ return err
2237+ }
2238+ var arg0 wl.Object
2239+ if buffer != nil {
2240+ arg0 = buffer
2241+ }
2242+ if err := o.Context().SendRequest(o, 1, arg0, x, y); err != nil {
2243+ return err
2244+ }
2245+ return nil
2246+}
2247+
2248+// This request is used to describe the regions where the pending buffer is different from the current surface contents, and where the surface therefore needs to be repainted. The compositor ignores the parts of the damage that fall outside of the surface. Damage is double-buffered state, see wl_surface.commit. The damage rectangle is specified in surface-local coordinates, where x and y specify the upper left corner of the damage rectangle. The initial value for pending damage is empty: no damage. wl_surface.damage adds pending damage: the new pending damage is the union of old pending damage and the given rectangle. wl_surface.commit assigns pending damage as the current damage, and clears pending damage. The server will clear the current damage as it repaints the surface. Note! New clients should not use this request. Instead damage can be posted with wl_surface.damage_buffer which uses buffer coordinates instead of surface coordinates.
2249+func (o *Surface) Damage(x int32, y int32, width int32, height int32) error {
2250+ if err := o.Context().CheckProxy(o); err != nil {
2251+ return err
2252+ }
2253+ if err := o.Context().SendRequest(o, 2, x, y, width, height); err != nil {
2254+ return err
2255+ }
2256+ return nil
2257+}
2258+
2259+// Request a notification when it is a good time to start drawing a new frame, by creating a frame callback. This is useful for throttling redrawing operations, and driving animations. When a client is animating on a wl_surface, it can use the 'frame' request to get notified when it is a good time to draw and commit the next frame of animation. If the client commits an update earlier than that, it is likely that some updates will not make it to the display, and the client is wasting resources by drawing too often. The frame request will take effect on the next wl_surface.commit. The notification will only be posted for one frame unless requested again. For a wl_surface, the notifications are posted in the order the frame requests were committed. The server must send the notifications so that a client will not send excessive updates, while still allowing the highest possible update rate for clients that wait for the reply before drawing again. The server should give some time for the client to draw and commit after sending the frame callback events to let it hit the next output refresh. A server should avoid signaling the frame callbacks if the surface is not visible in any way, e.g. the surface is off-screen, or completely obscured by other opaque surfaces. The object returned by this request will be destroyed by the compositor after the callback is fired and as such the client must not attempt to use it after that point. The callback_data passed in the callback is the current time, in milliseconds, with an undefined base.
2260+func (o *Surface) Frame() (*Callback, error) {
2261+ if err := o.Context().CheckProxy(o); err != nil {
2262+ return nil, err
2263+ }
2264+ child := &Callback{}
2265+ child.SetContext(o.Context())
2266+ child.SetID(o.Context().AllocateID())
2267+ o.Context().Register(child)
2268+ if err := o.Context().SendRequest(o, 3, child); err != nil {
2269+ o.Context().Unregister(child)
2270+ return nil, err
2271+ }
2272+ return child, nil
2273+}
2274+
2275+// This request sets the region of the surface that contains opaque content. The opaque region is an optimization hint for the compositor that lets it optimize the redrawing of content behind opaque regions. Setting an opaque region is not required for correct behaviour, but marking transparent content as opaque will result in repaint artifacts. The opaque region is specified in surface-local coordinates. The compositor ignores the parts of the opaque region that fall outside of the surface. Opaque region is double-buffered state, see wl_surface.commit. wl_surface.set_opaque_region changes the pending opaque region. wl_surface.commit copies the pending region to the current region. Otherwise, the pending and current regions are never changed. The initial value for an opaque region is empty. Setting the pending opaque region has copy semantics, and the wl_region object can be destroyed immediately. A NULL wl_region causes the pending opaque region to be set to empty.
2276+func (o *Surface) SetOpaqueRegion(region *Region) error {
2277+ if err := o.Context().CheckProxy(o); err != nil {
2278+ return err
2279+ }
2280+ var arg0 wl.Object
2281+ if region != nil {
2282+ arg0 = region
2283+ }
2284+ if err := o.Context().SendRequest(o, 4, arg0); err != nil {
2285+ return err
2286+ }
2287+ return nil
2288+}
2289+
2290+// This request sets the region of the surface that can receive pointer and touch events. Input events happening outside of this region will try the next surface in the server surface stack. The compositor ignores the parts of the input region that fall outside of the surface. The input region is specified in surface-local coordinates. Input region is double-buffered state, see wl_surface.commit. wl_surface.set_input_region changes the pending input region. wl_surface.commit copies the pending region to the current region. Otherwise the pending and current regions are never changed, except cursor and icon surfaces are special cases, see wl_pointer.set_cursor and wl_data_device.start_drag. The initial value for an input region is infinite. That means the whole surface will accept input. Setting the pending input region has copy semantics, and the wl_region object can be destroyed immediately. A NULL wl_region causes the input region to be set to infinite.
2291+func (o *Surface) SetInputRegion(region *Region) error {
2292+ if err := o.Context().CheckProxy(o); err != nil {
2293+ return err
2294+ }
2295+ var arg0 wl.Object
2296+ if region != nil {
2297+ arg0 = region
2298+ }
2299+ if err := o.Context().SendRequest(o, 5, arg0); err != nil {
2300+ return err
2301+ }
2302+ return nil
2303+}
2304+
2305+// Surface state (input, opaque, and damage regions, attached buffers, etc.) is double-buffered. Protocol requests modify the pending state, as opposed to the active state in use by the compositor. All requests that need a commit to become effective are documented to affect double-buffered state. Other interfaces may add further double-buffered surface state. A commit request atomically creates a Content Update (CU) from the pending state, even if the pending state has not been touched. The content update is placed at the end of a per-surface queue until it becomes active. After commit, the new pending state is as documented for each related request. A CU is either a Desync Content Update (DCU) or a Sync Content Update (SCU). If the surface is effectively synchronized at the commit request, it is a SCU, otherwise a DCU. When a surface transitions from effectively synchronized to effectively desynchronized, all SCUs in its queue which are not reachable by any DCU become DCUs and dependency edges from outside the queue to these CUs are removed. See wl_subsurface for the definition of 'effectively synchronized' and 'effectively desynchronized'. When a CU is placed in the queue, the CU has a dependency on the CU in front of it and to the SCU at end of the queue of every direct child surface if that SCU exists and does not have another dependent. This can form a directed acyclic graph of CUs with dependencies as edges. In addition to surface state, the CU can have constraints that must be satisfied before it can be applied. Other interfaces may add CU constraints. All DCUs which do not have a SCU in front of themselves in their queue, are candidates. If the graph that's reachable by a candidate does not have any unsatisfied constraints, the entire graph must be applied atomically. When a CU is applied, the wl_buffer is applied before all other state. This means that all coordinates in double-buffered state are relative to the newly attached wl_buffers, except for wl_surface.attach itself. If there is no newly attached wl_buffer, the coordinates are relative to the previous content update.
2306+func (o *Surface) Commit() error {
2307+ if err := o.Context().CheckProxy(o); err != nil {
2308+ return err
2309+ }
2310+ if err := o.Context().SendRequest(o, 6); err != nil {
2311+ return err
2312+ }
2313+ return nil
2314+}
2315+
2316+// This request sets the transformation that the client has already applied to the content of the buffer. The accepted values for the transform parameter are the values for wl_output.transform. The compositor applies the inverse of this transformation whenever it uses the buffer contents. Buffer transform is double-buffered state, see wl_surface.commit. A newly created surface has its buffer transformation set to normal. wl_surface.set_buffer_transform changes the pending buffer transformation. wl_surface.commit copies the pending buffer transformation to the current one. Otherwise, the pending and current values are never changed. The purpose of this request is to allow clients to render content according to the output transform, thus permitting the compositor to use certain optimizations even if the display is rotated. Using hardware overlays and scanning out a client buffer for fullscreen surfaces are examples of such optimizations. Those optimizations are highly dependent on the compositor implementation, so the use of this request should be considered on a case-by-case basis. Note that if the transform value includes 90 or 270 degree rotation, the width of the buffer will become the surface height and the height of the buffer will become the surface width. If transform is not one of the values from the wl_output.transform enum the invalid_transform protocol error is raised.
2317+func (o *Surface) SetBufferTransform(transform int32) error {
2318+ if err := o.Context().CheckProxy(o); err != nil {
2319+ return err
2320+ }
2321+ if err := o.Context().SendRequest(o, 7, transform); err != nil {
2322+ return err
2323+ }
2324+ return nil
2325+}
2326+
2327+// This request sets an optional scaling factor on how the compositor interprets the contents of the buffer attached to the window. Buffer scale is double-buffered state, see wl_surface.commit. A newly created surface has its buffer scale set to 1. wl_surface.set_buffer_scale changes the pending buffer scale. wl_surface.commit copies the pending buffer scale to the current one. Otherwise, the pending and current values are never changed. The purpose of this request is to allow clients to supply higher resolution buffer data for use on high resolution outputs. It is intended that you pick the same buffer scale as the scale of the output that the surface is displayed on. This means the compositor can avoid scaling when rendering the surface on that output. Note that if the scale is larger than 1, then you have to attach a buffer that is larger (by a factor of scale in each dimension) than the desired surface size. If scale is not greater than 0 the invalid_scale protocol error is raised.
2328+func (o *Surface) SetBufferScale(scale int32) error {
2329+ if err := o.Context().CheckProxy(o); err != nil {
2330+ return err
2331+ }
2332+ if err := o.Context().SendRequest(o, 8, scale); err != nil {
2333+ return err
2334+ }
2335+ return nil
2336+}
2337+
2338+// This request is used to describe the regions where the pending buffer is different from the current surface contents, and where the surface therefore needs to be repainted. The compositor ignores the parts of the damage that fall outside of the surface. Damage is double-buffered state, see wl_surface.commit. The damage rectangle is specified in buffer coordinates, where x and y specify the upper left corner of the damage rectangle. The initial value for pending damage is empty: no damage. wl_surface.damage_buffer adds pending damage: the new pending damage is the union of old pending damage and the given rectangle. wl_surface.commit assigns pending damage as the current damage, and clears pending damage. The server will clear the current damage as it repaints the surface. This request differs from wl_surface.damage in only one way - it takes damage in buffer coordinates instead of surface-local coordinates. While this generally is more intuitive than surface coordinates, it is especially desirable when using wp_viewport or when a drawing library (like EGL) is unaware of buffer scale and buffer transform. Note: Because buffer transformation changes and damage requests may be interleaved in the protocol stream, it is impossible to determine the actual mapping between surface and buffer damage until wl_surface.commit time. Therefore, compositors wishing to take both kinds of damage into account will have to accumulate damage from the two requests separately and only transform from one to the other after receiving the wl_surface.commit.
2339+func (o *Surface) DamageBuffer(x int32, y int32, width int32, height int32) error {
2340+ if err := o.Context().CheckProxy(o); err != nil {
2341+ return err
2342+ }
2343+ if err := o.Context().SendRequest(o, 9, x, y, width, height); err != nil {
2344+ return err
2345+ }
2346+ return nil
2347+}
2348+
2349+// The x and y arguments specify the location of the new pending buffer's upper left corner, relative to the current buffer's upper left corner, in surface-local coordinates. In other words, the x and y, combined with the new surface size define in which directions the surface's size changes. The exact semantics of wl_surface.offset are role-specific. Refer to the documentation of specific roles for more information. Surface location offset is double-buffered state, see wl_surface.commit. This request is semantically equivalent to and the replaces the x and y arguments in the wl_surface.attach request in wl_surface versions prior to 5. See wl_surface.attach for details.
2350+func (o *Surface) Offset(x int32, y int32) error {
2351+ if err := o.Context().CheckProxy(o); err != nil {
2352+ return err
2353+ }
2354+ if err := o.Context().SendRequest(o, 10, x, y); err != nil {
2355+ return err
2356+ }
2357+ return nil
2358+}
2359+
2360+// Create a callback for the release of the buffer attached by the client with wl_surface.attach. The compositor will release the buffer when it has finished its usage of the underlying storage for the relevant commit. Once the client receives this event, and assuming the associated buffer is not pending release from other wl_surface.commit requests, the client can safely re-use the buffer. Release callbacks are double-buffered state, and will be associated with the pending buffer at wl_surface.commit time. The callback_data passed in the wl_callback.done event is unused and is always zero. Sending this request without attaching a non-null buffer in the same content update is a protocol error. The compositor will send the no_buffer error in this case.
2361+func (o *Surface) GetRelease() (*Callback, error) {
2362+ if err := o.Context().CheckProxy(o); err != nil {
2363+ return nil, err
2364+ }
2365+ child := &Callback{}
2366+ child.SetContext(o.Context())
2367+ child.SetID(o.Context().AllocateID())
2368+ o.Context().Register(child)
2369+ if err := o.Context().SendRequest(o, 11, child); err != nil {
2370+ o.Context().Unregister(child)
2371+ return nil, err
2372+ }
2373+ return child, nil
2374+}
2375+
2376+// OnEnter registers a handler for the enter event.
2377+//
2378+// Handlers are appended, so a second call adds another handler rather than
2379+// replacing the first. Registration is safe while another goroutine dispatches.
2380+func (o *Surface) OnEnter(handler func(outputID uint32)) {
2381+ if handler == nil {
2382+ return
2383+ }
2384+ o.handlersMu.Lock()
2385+ defer o.handlersMu.Unlock()
2386+ o.onEnter = append(o.onEnter, handler)
2387+}
2388+
2389+// handlersForEnter returns the handlers registered for enter, taken
2390+// under the lock so a handler may register another handler while events are
2391+// being dispatched.
2392+func (o *Surface) handlersForEnter() []func(outputID uint32) {
2393+ o.handlersMu.Lock()
2394+ defer o.handlersMu.Unlock()
2395+ return o.onEnter
2396+}
2397+
2398+// OnLeave registers a handler for the leave event.
2399+//
2400+// Handlers are appended, so a second call adds another handler rather than
2401+// replacing the first. Registration is safe while another goroutine dispatches.
2402+func (o *Surface) OnLeave(handler func(outputID uint32)) {
2403+ if handler == nil {
2404+ return
2405+ }
2406+ o.handlersMu.Lock()
2407+ defer o.handlersMu.Unlock()
2408+ o.onLeave = append(o.onLeave, handler)
2409+}
2410+
2411+// handlersForLeave returns the handlers registered for leave, taken
2412+// under the lock so a handler may register another handler while events are
2413+// being dispatched.
2414+func (o *Surface) handlersForLeave() []func(outputID uint32) {
2415+ o.handlersMu.Lock()
2416+ defer o.handlersMu.Unlock()
2417+ return o.onLeave
2418+}
2419+
2420+// OnPreferredBufferScale registers a handler for the preferred_buffer_scale event.
2421+//
2422+// Handlers are appended, so a second call adds another handler rather than
2423+// replacing the first. Registration is safe while another goroutine dispatches.
2424+func (o *Surface) OnPreferredBufferScale(handler func(factor int32)) {
2425+ if handler == nil {
2426+ return
2427+ }
2428+ o.handlersMu.Lock()
2429+ defer o.handlersMu.Unlock()
2430+ o.onPreferredBufferScale = append(o.onPreferredBufferScale, handler)
2431+}
2432+
2433+// handlersForPreferredBufferScale returns the handlers registered for preferred_buffer_scale, taken
2434+// under the lock so a handler may register another handler while events are
2435+// being dispatched.
2436+func (o *Surface) handlersForPreferredBufferScale() []func(factor int32) {
2437+ o.handlersMu.Lock()
2438+ defer o.handlersMu.Unlock()
2439+ return o.onPreferredBufferScale
2440+}
2441+
2442+// OnPreferredBufferTransform registers a handler for the preferred_buffer_transform event.
2443+//
2444+// Handlers are appended, so a second call adds another handler rather than
2445+// replacing the first. Registration is safe while another goroutine dispatches.
2446+func (o *Surface) OnPreferredBufferTransform(handler func(transform uint32)) {
2447+ if handler == nil {
2448+ return
2449+ }
2450+ o.handlersMu.Lock()
2451+ defer o.handlersMu.Unlock()
2452+ o.onPreferredBufferTransform = append(o.onPreferredBufferTransform, handler)
2453+}
2454+
2455+// handlersForPreferredBufferTransform returns the handlers registered for preferred_buffer_transform, taken
2456+// under the lock so a handler may register another handler while events are
2457+// being dispatched.
2458+func (o *Surface) handlersForPreferredBufferTransform() []func(transform uint32) {
2459+ o.handlersMu.Lock()
2460+ defer o.handlersMu.Unlock()
2461+ return o.onPreferredBufferTransform
2462+}
2463+
2464+// EventSignature returns the declared event argument types in wire order.
2465+func (o *Surface) EventSignature(opcode uint16) (string, bool) {
2466+ switch opcode {
2467+ case 0:
2468+ return "object,", true
2469+ case 1:
2470+ return "object,", true
2471+ case 2:
2472+ return "int,", true
2473+ case 3:
2474+ return "uint,", true
2475+ default:
2476+ return "", false
2477+ }
2478+}
2479+
2480+// Dispatch decodes one event on wl_surface and calls the registered handlers.
2481+func (o *Surface) Dispatch(event *wl.Event) {
2482+ switch event.Opcode {
2483+ case 0: // enter
2484+ outputID := event.Uint32()
2485+
2486+ for i, handler := range o.handlersForEnter() {
2487+ _ = i
2488+ handler(outputID)
2489+ }
2490+ case 1: // leave
2491+ outputID := event.Uint32()
2492+
2493+ for i, handler := range o.handlersForLeave() {
2494+ _ = i
2495+ handler(outputID)
2496+ }
2497+ case 2: // preferred_buffer_scale
2498+ factor := event.Int32()
2499+
2500+ for i, handler := range o.handlersForPreferredBufferScale() {
2501+ _ = i
2502+ handler(factor)
2503+ }
2504+ case 3: // preferred_buffer_transform
2505+ transform := event.Uint32()
2506+
2507+ for i, handler := range o.handlersForPreferredBufferTransform() {
2508+ _ = i
2509+ handler(transform)
2510+ }
2511+ default:
2512+ }
2513+}
2514+
2515+// SeatInterface is the protocol interface name.
2516+const SeatInterface = "wl_seat"
2517+
2518+// Seat is the generated binding for wl_seat version 11.
2519+type Seat struct {
2520+ wl.BaseProxy
2521+ // handlersMu guards the handler slices: a client may register or replace
2522+ // handlers while another goroutine dispatches events.
2523+ handlersMu sync.Mutex
2524+ onCapabilities []func(capabilities uint32)
2525+ onName []func(name string)
2526+}
2527+
2528+// NewSeat returns an unbound Seat.
2529+func NewSeat(ctx *wl.Context) *Seat {
2530+ obj := &Seat{}
2531+ obj.SetContext(ctx)
2532+ ctx.Register(obj)
2533+ return obj
2534+}
2535+
2536+// The ID provided will be initialized to the wl_pointer interface for this seat. This request only takes effect if the seat has the pointer capability, or has had the pointer capability in the past. It is a protocol violation to issue this request on a seat that has never had the pointer capability. The missing_capability error will be sent in this case.
2537+func (o *Seat) GetPointer() (*Pointer, error) {
2538+ if err := o.Context().CheckProxy(o); err != nil {
2539+ return nil, err
2540+ }
2541+ child := &Pointer{}
2542+ child.SetContext(o.Context())
2543+ child.SetID(o.Context().AllocateID())
2544+ o.Context().Register(child)
2545+ if err := o.Context().SendRequest(o, 0, child); err != nil {
2546+ o.Context().Unregister(child)
2547+ return nil, err
2548+ }
2549+ return child, nil
2550+}
2551+
2552+// The ID provided will be initialized to the wl_keyboard interface for this seat. This request only takes effect if the seat has the keyboard capability, or has had the keyboard capability in the past. It is a protocol violation to issue this request on a seat that has never had the keyboard capability. The missing_capability error will be sent in this case.
2553+func (o *Seat) GetKeyboard() (*Keyboard, error) {
2554+ if err := o.Context().CheckProxy(o); err != nil {
2555+ return nil, err
2556+ }
2557+ child := &Keyboard{}
2558+ child.SetContext(o.Context())
2559+ child.SetID(o.Context().AllocateID())
2560+ o.Context().Register(child)
2561+ if err := o.Context().SendRequest(o, 1, child); err != nil {
2562+ o.Context().Unregister(child)
2563+ return nil, err
2564+ }
2565+ return child, nil
2566+}
2567+
2568+// The ID provided will be initialized to the wl_touch interface for this seat. This request only takes effect if the seat has the touch capability, or has had the touch capability in the past. It is a protocol violation to issue this request on a seat that has never had the touch capability. The missing_capability error will be sent in this case.
2569+func (o *Seat) GetTouch() (*Touch, error) {
2570+ if err := o.Context().CheckProxy(o); err != nil {
2571+ return nil, err
2572+ }
2573+ child := &Touch{}
2574+ child.SetContext(o.Context())
2575+ child.SetID(o.Context().AllocateID())
2576+ o.Context().Register(child)
2577+ if err := o.Context().SendRequest(o, 2, child); err != nil {
2578+ o.Context().Unregister(child)
2579+ return nil, err
2580+ }
2581+ return child, nil
2582+}
2583+
2584+// Using this request a client can tell the server that it is not going to use the seat object anymore.
2585+func (o *Seat) Release() error {
2586+ if err := o.Context().CheckProxy(o); err != nil {
2587+ return err
2588+ }
2589+ if err := o.Context().SendDestructor(o, 3); err != nil {
2590+ return err
2591+ }
2592+ return nil
2593+}
2594+
2595+// OnCapabilities registers a handler for the capabilities event.
2596+//
2597+// Handlers are appended, so a second call adds another handler rather than
2598+// replacing the first. Registration is safe while another goroutine dispatches.
2599+func (o *Seat) OnCapabilities(handler func(capabilities uint32)) {
2600+ if handler == nil {
2601+ return
2602+ }
2603+ o.handlersMu.Lock()
2604+ defer o.handlersMu.Unlock()
2605+ o.onCapabilities = append(o.onCapabilities, handler)
2606+}
2607+
2608+// handlersForCapabilities returns the handlers registered for capabilities, taken
2609+// under the lock so a handler may register another handler while events are
2610+// being dispatched.
2611+func (o *Seat) handlersForCapabilities() []func(capabilities uint32) {
2612+ o.handlersMu.Lock()
2613+ defer o.handlersMu.Unlock()
2614+ return o.onCapabilities
2615+}
2616+
2617+// OnName registers a handler for the name event.
2618+//
2619+// Handlers are appended, so a second call adds another handler rather than
2620+// replacing the first. Registration is safe while another goroutine dispatches.
2621+func (o *Seat) OnName(handler func(name string)) {
2622+ if handler == nil {
2623+ return
2624+ }
2625+ o.handlersMu.Lock()
2626+ defer o.handlersMu.Unlock()
2627+ o.onName = append(o.onName, handler)
2628+}
2629+
2630+// handlersForName returns the handlers registered for name, taken
2631+// under the lock so a handler may register another handler while events are
2632+// being dispatched.
2633+func (o *Seat) handlersForName() []func(name string) {
2634+ o.handlersMu.Lock()
2635+ defer o.handlersMu.Unlock()
2636+ return o.onName
2637+}
2638+
2639+// EventSignature returns the declared event argument types in wire order.
2640+func (o *Seat) EventSignature(opcode uint16) (string, bool) {
2641+ switch opcode {
2642+ case 0:
2643+ return "uint,", true
2644+ case 1:
2645+ return "string,", true
2646+ default:
2647+ return "", false
2648+ }
2649+}
2650+
2651+// Dispatch decodes one event on wl_seat and calls the registered handlers.
2652+func (o *Seat) Dispatch(event *wl.Event) {
2653+ switch event.Opcode {
2654+ case 0: // capabilities
2655+ capabilities := event.Uint32()
2656+
2657+ for i, handler := range o.handlersForCapabilities() {
2658+ _ = i
2659+ handler(capabilities)
2660+ }
2661+ case 1: // name
2662+ name := event.String()
2663+
2664+ for i, handler := range o.handlersForName() {
2665+ _ = i
2666+ handler(name)
2667+ }
2668+ default:
2669+ }
2670+}
2671+
2672+// PointerInterface is the protocol interface name.
2673+const PointerInterface = "wl_pointer"
2674+
2675+// Pointer is the generated binding for wl_pointer version 11.
2676+type Pointer struct {
2677+ wl.BaseProxy
2678+ // handlersMu guards the handler slices: a client may register or replace
2679+ // handlers while another goroutine dispatches events.
2680+ handlersMu sync.Mutex
2681+ onEnter []func(serial uint32, surfaceID uint32, surfaceX wl.Fixed, surfaceY wl.Fixed)
2682+ onLeave []func(serial uint32, surfaceID uint32)
2683+ onMotion []func(time uint32, surfaceX wl.Fixed, surfaceY wl.Fixed)
2684+ onButton []func(serial uint32, time uint32, button uint32, state uint32)
2685+ onAxis []func(time uint32, axis uint32, value wl.Fixed)
2686+ onFrame []func()
2687+ onAxisSource []func(axisSource uint32)
2688+ onAxisStop []func(time uint32, axis uint32)
2689+ onAxisDiscrete []func(axis uint32, discrete int32)
2690+ onAxisValue120 []func(axis uint32, value120 int32)
2691+ onAxisRelativeDirection []func(axis uint32, direction uint32)
2692+ onWarp []func(surfaceX wl.Fixed, surfaceY wl.Fixed)
2693+}
2694+
2695+// NewPointer returns an unbound Pointer.
2696+func NewPointer(ctx *wl.Context) *Pointer {
2697+ obj := &Pointer{}
2698+ obj.SetContext(ctx)
2699+ ctx.Register(obj)
2700+ return obj
2701+}
2702+
2703+// Set the pointer surface, i.e., the surface that contains the pointer image (cursor). This request gives the surface the role of a cursor. If the surface already has another role, it raises a protocol error. The cursor actually changes only if the pointer focus for this device is one of the requesting client's surfaces or the surface parameter is the current pointer surface. If there was a previous surface set with this request it is replaced. If surface is NULL, the pointer image is hidden. The parameters hotspot_x and hotspot_y define the position of the pointer surface relative to the pointer location. Its top-left corner is always at (x, y) - (hotspot_x, hotspot_y), where (x, y) are the coordinates of the pointer location, in surface-local coordinates. On wl_surface.offset requests to the pointer surface, hotspot_x and hotspot_y are decremented by the x and y parameters passed to the request. The offset must be applied by wl_surface.commit as usual. The hotspot can also be updated by passing the currently set pointer surface to this request with new values for hotspot_x and hotspot_y. The input region is ignored for wl_surfaces with the role of a cursor. When the use as a cursor ends, the wl_surface is unmapped. The serial parameter must match the latest wl_pointer.enter serial number sent to the client. Otherwise the request will be ignored.
2704+func (o *Pointer) SetCursor(serial uint32, surface *Surface, hotspotX int32, hotspotY int32) error {
2705+ if err := o.Context().CheckProxy(o); err != nil {
2706+ return err
2707+ }
2708+ var arg1 wl.Object
2709+ if surface != nil {
2710+ arg1 = surface
2711+ }
2712+ if err := o.Context().SendRequest(o, 0, serial, arg1, hotspotX, hotspotY); err != nil {
2713+ return err
2714+ }
2715+ return nil
2716+}
2717+
2718+// Using this request a client can tell the server that it is not going to use the pointer object anymore. This request destroys the pointer proxy object, so clients must not call wl_pointer_destroy() after using this request.
2719+func (o *Pointer) Release() error {
2720+ if err := o.Context().CheckProxy(o); err != nil {
2721+ return err
2722+ }
2723+ if err := o.Context().SendDestructor(o, 1); err != nil {
2724+ return err
2725+ }
2726+ return nil
2727+}
2728+
2729+// OnEnter registers a handler for the enter event.
2730+//
2731+// Handlers are appended, so a second call adds another handler rather than
2732+// replacing the first. Registration is safe while another goroutine dispatches.
2733+func (o *Pointer) OnEnter(handler func(serial uint32, surfaceID uint32, surfaceX wl.Fixed, surfaceY wl.Fixed)) {
2734+ if handler == nil {
2735+ return
2736+ }
2737+ o.handlersMu.Lock()
2738+ defer o.handlersMu.Unlock()
2739+ o.onEnter = append(o.onEnter, handler)
2740+}
2741+
2742+// handlersForEnter returns the handlers registered for enter, taken
2743+// under the lock so a handler may register another handler while events are
2744+// being dispatched.
2745+func (o *Pointer) handlersForEnter() []func(serial uint32, surfaceID uint32, surfaceX wl.Fixed, surfaceY wl.Fixed) {
2746+ o.handlersMu.Lock()
2747+ defer o.handlersMu.Unlock()
2748+ return o.onEnter
2749+}
2750+
2751+// OnLeave registers a handler for the leave event.
2752+//
2753+// Handlers are appended, so a second call adds another handler rather than
2754+// replacing the first. Registration is safe while another goroutine dispatches.
2755+func (o *Pointer) OnLeave(handler func(serial uint32, surfaceID uint32)) {
2756+ if handler == nil {
2757+ return
2758+ }
2759+ o.handlersMu.Lock()
2760+ defer o.handlersMu.Unlock()
2761+ o.onLeave = append(o.onLeave, handler)
2762+}
2763+
2764+// handlersForLeave returns the handlers registered for leave, taken
2765+// under the lock so a handler may register another handler while events are
2766+// being dispatched.
2767+func (o *Pointer) handlersForLeave() []func(serial uint32, surfaceID uint32) {
2768+ o.handlersMu.Lock()
2769+ defer o.handlersMu.Unlock()
2770+ return o.onLeave
2771+}
2772+
2773+// OnMotion registers a handler for the motion event.
2774+//
2775+// Handlers are appended, so a second call adds another handler rather than
2776+// replacing the first. Registration is safe while another goroutine dispatches.
2777+func (o *Pointer) OnMotion(handler func(time uint32, surfaceX wl.Fixed, surfaceY wl.Fixed)) {
2778+ if handler == nil {
2779+ return
2780+ }
2781+ o.handlersMu.Lock()
2782+ defer o.handlersMu.Unlock()
2783+ o.onMotion = append(o.onMotion, handler)
2784+}
2785+
2786+// handlersForMotion returns the handlers registered for motion, taken
2787+// under the lock so a handler may register another handler while events are
2788+// being dispatched.
2789+func (o *Pointer) handlersForMotion() []func(time uint32, surfaceX wl.Fixed, surfaceY wl.Fixed) {
2790+ o.handlersMu.Lock()
2791+ defer o.handlersMu.Unlock()
2792+ return o.onMotion
2793+}
2794+
2795+// OnButton registers a handler for the button event.
2796+//
2797+// Handlers are appended, so a second call adds another handler rather than
2798+// replacing the first. Registration is safe while another goroutine dispatches.
2799+func (o *Pointer) OnButton(handler func(serial uint32, time uint32, button uint32, state uint32)) {
2800+ if handler == nil {
2801+ return
2802+ }
2803+ o.handlersMu.Lock()
2804+ defer o.handlersMu.Unlock()
2805+ o.onButton = append(o.onButton, handler)
2806+}
2807+
2808+// handlersForButton returns the handlers registered for button, taken
2809+// under the lock so a handler may register another handler while events are
2810+// being dispatched.
2811+func (o *Pointer) handlersForButton() []func(serial uint32, time uint32, button uint32, state uint32) {
2812+ o.handlersMu.Lock()
2813+ defer o.handlersMu.Unlock()
2814+ return o.onButton
2815+}
2816+
2817+// OnAxis registers a handler for the axis event.
2818+//
2819+// Handlers are appended, so a second call adds another handler rather than
2820+// replacing the first. Registration is safe while another goroutine dispatches.
2821+func (o *Pointer) OnAxis(handler func(time uint32, axis uint32, value wl.Fixed)) {
2822+ if handler == nil {
2823+ return
2824+ }
2825+ o.handlersMu.Lock()
2826+ defer o.handlersMu.Unlock()
2827+ o.onAxis = append(o.onAxis, handler)
2828+}
2829+
2830+// handlersForAxis returns the handlers registered for axis, taken
2831+// under the lock so a handler may register another handler while events are
2832+// being dispatched.
2833+func (o *Pointer) handlersForAxis() []func(time uint32, axis uint32, value wl.Fixed) {
2834+ o.handlersMu.Lock()
2835+ defer o.handlersMu.Unlock()
2836+ return o.onAxis
2837+}
2838+
2839+// OnFrame registers a handler for the frame event.
2840+//
2841+// Handlers are appended, so a second call adds another handler rather than
2842+// replacing the first. Registration is safe while another goroutine dispatches.
2843+func (o *Pointer) OnFrame(handler func()) {
2844+ if handler == nil {
2845+ return
2846+ }
2847+ o.handlersMu.Lock()
2848+ defer o.handlersMu.Unlock()
2849+ o.onFrame = append(o.onFrame, handler)
2850+}
2851+
2852+// handlersForFrame returns the handlers registered for frame, taken
2853+// under the lock so a handler may register another handler while events are
2854+// being dispatched.
2855+func (o *Pointer) handlersForFrame() []func() {
2856+ o.handlersMu.Lock()
2857+ defer o.handlersMu.Unlock()
2858+ return o.onFrame
2859+}
2860+
2861+// OnAxisSource registers a handler for the axis_source event.
2862+//
2863+// Handlers are appended, so a second call adds another handler rather than
2864+// replacing the first. Registration is safe while another goroutine dispatches.
2865+func (o *Pointer) OnAxisSource(handler func(axisSource uint32)) {
2866+ if handler == nil {
2867+ return
2868+ }
2869+ o.handlersMu.Lock()
2870+ defer o.handlersMu.Unlock()
2871+ o.onAxisSource = append(o.onAxisSource, handler)
2872+}
2873+
2874+// handlersForAxisSource returns the handlers registered for axis_source, taken
2875+// under the lock so a handler may register another handler while events are
2876+// being dispatched.
2877+func (o *Pointer) handlersForAxisSource() []func(axisSource uint32) {
2878+ o.handlersMu.Lock()
2879+ defer o.handlersMu.Unlock()
2880+ return o.onAxisSource
2881+}
2882+
2883+// OnAxisStop registers a handler for the axis_stop event.
2884+//
2885+// Handlers are appended, so a second call adds another handler rather than
2886+// replacing the first. Registration is safe while another goroutine dispatches.
2887+func (o *Pointer) OnAxisStop(handler func(time uint32, axis uint32)) {
2888+ if handler == nil {
2889+ return
2890+ }
2891+ o.handlersMu.Lock()
2892+ defer o.handlersMu.Unlock()
2893+ o.onAxisStop = append(o.onAxisStop, handler)
2894+}
2895+
2896+// handlersForAxisStop returns the handlers registered for axis_stop, taken
2897+// under the lock so a handler may register another handler while events are
2898+// being dispatched.
2899+func (o *Pointer) handlersForAxisStop() []func(time uint32, axis uint32) {
2900+ o.handlersMu.Lock()
2901+ defer o.handlersMu.Unlock()
2902+ return o.onAxisStop
2903+}
2904+
2905+// OnAxisDiscrete registers a handler for the axis_discrete event.
2906+//
2907+// Handlers are appended, so a second call adds another handler rather than
2908+// replacing the first. Registration is safe while another goroutine dispatches.
2909+func (o *Pointer) OnAxisDiscrete(handler func(axis uint32, discrete int32)) {
2910+ if handler == nil {
2911+ return
2912+ }
2913+ o.handlersMu.Lock()
2914+ defer o.handlersMu.Unlock()
2915+ o.onAxisDiscrete = append(o.onAxisDiscrete, handler)
2916+}
2917+
2918+// handlersForAxisDiscrete returns the handlers registered for axis_discrete, taken
2919+// under the lock so a handler may register another handler while events are
2920+// being dispatched.
2921+func (o *Pointer) handlersForAxisDiscrete() []func(axis uint32, discrete int32) {
2922+ o.handlersMu.Lock()
2923+ defer o.handlersMu.Unlock()
2924+ return o.onAxisDiscrete
2925+}
2926+
2927+// OnAxisValue120 registers a handler for the axis_value120 event.
2928+//
2929+// Handlers are appended, so a second call adds another handler rather than
2930+// replacing the first. Registration is safe while another goroutine dispatches.
2931+func (o *Pointer) OnAxisValue120(handler func(axis uint32, value120 int32)) {
2932+ if handler == nil {
2933+ return
2934+ }
2935+ o.handlersMu.Lock()
2936+ defer o.handlersMu.Unlock()
2937+ o.onAxisValue120 = append(o.onAxisValue120, handler)
2938+}
2939+
2940+// handlersForAxisValue120 returns the handlers registered for axis_value120, taken
2941+// under the lock so a handler may register another handler while events are
2942+// being dispatched.
2943+func (o *Pointer) handlersForAxisValue120() []func(axis uint32, value120 int32) {
2944+ o.handlersMu.Lock()
2945+ defer o.handlersMu.Unlock()
2946+ return o.onAxisValue120
2947+}
2948+
2949+// OnAxisRelativeDirection registers a handler for the axis_relative_direction event.
2950+//
2951+// Handlers are appended, so a second call adds another handler rather than
2952+// replacing the first. Registration is safe while another goroutine dispatches.
2953+func (o *Pointer) OnAxisRelativeDirection(handler func(axis uint32, direction uint32)) {
2954+ if handler == nil {
2955+ return
2956+ }
2957+ o.handlersMu.Lock()
2958+ defer o.handlersMu.Unlock()
2959+ o.onAxisRelativeDirection = append(o.onAxisRelativeDirection, handler)
2960+}
2961+
2962+// handlersForAxisRelativeDirection returns the handlers registered for axis_relative_direction, taken
2963+// under the lock so a handler may register another handler while events are
2964+// being dispatched.
2965+func (o *Pointer) handlersForAxisRelativeDirection() []func(axis uint32, direction uint32) {
2966+ o.handlersMu.Lock()
2967+ defer o.handlersMu.Unlock()
2968+ return o.onAxisRelativeDirection
2969+}
2970+
2971+// OnWarp registers a handler for the warp event.
2972+//
2973+// Handlers are appended, so a second call adds another handler rather than
2974+// replacing the first. Registration is safe while another goroutine dispatches.
2975+func (o *Pointer) OnWarp(handler func(surfaceX wl.Fixed, surfaceY wl.Fixed)) {
2976+ if handler == nil {
2977+ return
2978+ }
2979+ o.handlersMu.Lock()
2980+ defer o.handlersMu.Unlock()
2981+ o.onWarp = append(o.onWarp, handler)
2982+}
2983+
2984+// handlersForWarp returns the handlers registered for warp, taken
2985+// under the lock so a handler may register another handler while events are
2986+// being dispatched.
2987+func (o *Pointer) handlersForWarp() []func(surfaceX wl.Fixed, surfaceY wl.Fixed) {
2988+ o.handlersMu.Lock()
2989+ defer o.handlersMu.Unlock()
2990+ return o.onWarp
2991+}
2992+
2993+// EventSignature returns the declared event argument types in wire order.
2994+func (o *Pointer) EventSignature(opcode uint16) (string, bool) {
2995+ switch opcode {
2996+ case 0:
2997+ return "uint,object,fixed,fixed,", true
2998+ case 1:
2999+ return "uint,object,", true
3000+ case 2:
3001+ return "uint,fixed,fixed,", true
3002+ case 3:
3003+ return "uint,uint,uint,uint,", true
3004+ case 4:
3005+ return "uint,uint,fixed,", true
3006+ case 5:
3007+ return "", true
3008+ case 6:
3009+ return "uint,", true
3010+ case 7:
3011+ return "uint,uint,", true
3012+ case 8:
3013+ return "uint,int,", true
3014+ case 9:
3015+ return "uint,int,", true
3016+ case 10:
3017+ return "uint,uint,", true
3018+ case 11:
3019+ return "fixed,fixed,", true
3020+ default:
3021+ return "", false
3022+ }
3023+}
3024+
3025+// Dispatch decodes one event on wl_pointer and calls the registered handlers.
3026+func (o *Pointer) Dispatch(event *wl.Event) {
3027+ switch event.Opcode {
3028+ case 0: // enter
3029+ serial := event.Uint32()
3030+ surfaceID := event.Uint32()
3031+ surfaceX := event.Fixed()
3032+ surfaceY := event.Fixed()
3033+
3034+ for i, handler := range o.handlersForEnter() {
3035+ _ = i
3036+ handler(serial, surfaceID, surfaceX, surfaceY)
3037+ }
3038+ case 1: // leave
3039+ serial := event.Uint32()
3040+ surfaceID := event.Uint32()
3041+
3042+ for i, handler := range o.handlersForLeave() {
3043+ _ = i
3044+ handler(serial, surfaceID)
3045+ }
3046+ case 2: // motion
3047+ time := event.Uint32()
3048+ surfaceX := event.Fixed()
3049+ surfaceY := event.Fixed()
3050+
3051+ for i, handler := range o.handlersForMotion() {
3052+ _ = i
3053+ handler(time, surfaceX, surfaceY)
3054+ }
3055+ case 3: // button
3056+ serial := event.Uint32()
3057+ time := event.Uint32()
3058+ button := event.Uint32()
3059+ state := event.Uint32()
3060+
3061+ for i, handler := range o.handlersForButton() {
3062+ _ = i
3063+ handler(serial, time, button, state)
3064+ }
3065+ case 4: // axis
3066+ time := event.Uint32()
3067+ axis := event.Uint32()
3068+ value := event.Fixed()
3069+
3070+ for i, handler := range o.handlersForAxis() {
3071+ _ = i
3072+ handler(time, axis, value)
3073+ }
3074+ case 5: // frame
3075+
3076+ for i, handler := range o.handlersForFrame() {
3077+ _ = i
3078+ handler()
3079+ }
3080+ case 6: // axis_source
3081+ axisSource := event.Uint32()
3082+
3083+ for i, handler := range o.handlersForAxisSource() {
3084+ _ = i
3085+ handler(axisSource)
3086+ }
3087+ case 7: // axis_stop
3088+ time := event.Uint32()
3089+ axis := event.Uint32()
3090+
3091+ for i, handler := range o.handlersForAxisStop() {
3092+ _ = i
3093+ handler(time, axis)
3094+ }
3095+ case 8: // axis_discrete
3096+ axis := event.Uint32()
3097+ discrete := event.Int32()
3098+
3099+ for i, handler := range o.handlersForAxisDiscrete() {
3100+ _ = i
3101+ handler(axis, discrete)
3102+ }
3103+ case 9: // axis_value120
3104+ axis := event.Uint32()
3105+ value120 := event.Int32()
3106+
3107+ for i, handler := range o.handlersForAxisValue120() {
3108+ _ = i
3109+ handler(axis, value120)
3110+ }
3111+ case 10: // axis_relative_direction
3112+ axis := event.Uint32()
3113+ direction := event.Uint32()
3114+
3115+ for i, handler := range o.handlersForAxisRelativeDirection() {
3116+ _ = i
3117+ handler(axis, direction)
3118+ }
3119+ case 11: // warp
3120+ surfaceX := event.Fixed()
3121+ surfaceY := event.Fixed()
3122+
3123+ for i, handler := range o.handlersForWarp() {
3124+ _ = i
3125+ handler(surfaceX, surfaceY)
3126+ }
3127+ default:
3128+ }
3129+}
3130+
3131+// KeyboardInterface is the protocol interface name.
3132+const KeyboardInterface = "wl_keyboard"
3133+
3134+// Keyboard is the generated binding for wl_keyboard version 11.
3135+type Keyboard struct {
3136+ wl.BaseProxy
3137+ // handlersMu guards the handler slices: a client may register or replace
3138+ // handlers while another goroutine dispatches events.
3139+ handlersMu sync.Mutex
3140+ onKeymap []func(format uint32, fd *wl.OwnedFD, size uint32)
3141+ onEnter []func(serial uint32, surfaceID uint32, keys []byte)
3142+ onLeave []func(serial uint32, surfaceID uint32)
3143+ onKey []func(serial uint32, time uint32, key uint32, state uint32)
3144+ onModifiers []func(serial uint32, modsDepressed uint32, modsLatched uint32, modsLocked uint32, group uint32)
3145+ onRepeatInfo []func(rate int32, delay int32)
3146+}
3147+
3148+// NewKeyboard returns an unbound Keyboard.
3149+func NewKeyboard(ctx *wl.Context) *Keyboard {
3150+ obj := &Keyboard{}
3151+ obj.SetContext(ctx)
3152+ ctx.Register(obj)
3153+ return obj
3154+}
3155+
3156+// release the keyboard object
3157+func (o *Keyboard) Release() error {
3158+ if err := o.Context().CheckProxy(o); err != nil {
3159+ return err
3160+ }
3161+ if err := o.Context().SendDestructor(o, 0); err != nil {
3162+ return err
3163+ }
3164+ return nil
3165+}
3166+
3167+// OnKeymap registers a handler for the keymap event.
3168+//
3169+// Handlers are appended, so a second call adds another handler rather than
3170+// replacing the first. Registration is safe while another goroutine dispatches.
3171+func (o *Keyboard) OnKeymap(handler func(format uint32, fd *wl.OwnedFD, size uint32)) {
3172+ if handler == nil {
3173+ return
3174+ }
3175+ o.handlersMu.Lock()
3176+ defer o.handlersMu.Unlock()
3177+ o.onKeymap = append(o.onKeymap, handler)
3178+}
3179+
3180+// handlersForKeymap returns the handlers registered for keymap, taken
3181+// under the lock so a handler may register another handler while events are
3182+// being dispatched.
3183+func (o *Keyboard) handlersForKeymap() []func(format uint32, fd *wl.OwnedFD, size uint32) {
3184+ o.handlersMu.Lock()
3185+ defer o.handlersMu.Unlock()
3186+ return o.onKeymap
3187+}
3188+
3189+// OnEnter registers a handler for the enter event.
3190+//
3191+// Handlers are appended, so a second call adds another handler rather than
3192+// replacing the first. Registration is safe while another goroutine dispatches.
3193+func (o *Keyboard) OnEnter(handler func(serial uint32, surfaceID uint32, keys []byte)) {
3194+ if handler == nil {
3195+ return
3196+ }
3197+ o.handlersMu.Lock()
3198+ defer o.handlersMu.Unlock()
3199+ o.onEnter = append(o.onEnter, handler)
3200+}
3201+
3202+// handlersForEnter returns the handlers registered for enter, taken
3203+// under the lock so a handler may register another handler while events are
3204+// being dispatched.
3205+func (o *Keyboard) handlersForEnter() []func(serial uint32, surfaceID uint32, keys []byte) {
3206+ o.handlersMu.Lock()
3207+ defer o.handlersMu.Unlock()
3208+ return o.onEnter
3209+}
3210+
3211+// OnLeave registers a handler for the leave event.
3212+//
3213+// Handlers are appended, so a second call adds another handler rather than
3214+// replacing the first. Registration is safe while another goroutine dispatches.
3215+func (o *Keyboard) OnLeave(handler func(serial uint32, surfaceID uint32)) {
3216+ if handler == nil {
3217+ return
3218+ }
3219+ o.handlersMu.Lock()
3220+ defer o.handlersMu.Unlock()
3221+ o.onLeave = append(o.onLeave, handler)
3222+}
3223+
3224+// handlersForLeave returns the handlers registered for leave, taken
3225+// under the lock so a handler may register another handler while events are
3226+// being dispatched.
3227+func (o *Keyboard) handlersForLeave() []func(serial uint32, surfaceID uint32) {
3228+ o.handlersMu.Lock()
3229+ defer o.handlersMu.Unlock()
3230+ return o.onLeave
3231+}
3232+
3233+// OnKey registers a handler for the key event.
3234+//
3235+// Handlers are appended, so a second call adds another handler rather than
3236+// replacing the first. Registration is safe while another goroutine dispatches.
3237+func (o *Keyboard) OnKey(handler func(serial uint32, time uint32, key uint32, state uint32)) {
3238+ if handler == nil {
3239+ return
3240+ }
3241+ o.handlersMu.Lock()
3242+ defer o.handlersMu.Unlock()
3243+ o.onKey = append(o.onKey, handler)
3244+}
3245+
3246+// handlersForKey returns the handlers registered for key, taken
3247+// under the lock so a handler may register another handler while events are
3248+// being dispatched.
3249+func (o *Keyboard) handlersForKey() []func(serial uint32, time uint32, key uint32, state uint32) {
3250+ o.handlersMu.Lock()
3251+ defer o.handlersMu.Unlock()
3252+ return o.onKey
3253+}
3254+
3255+// OnModifiers registers a handler for the modifiers event.
3256+//
3257+// Handlers are appended, so a second call adds another handler rather than
3258+// replacing the first. Registration is safe while another goroutine dispatches.
3259+func (o *Keyboard) OnModifiers(handler func(serial uint32, modsDepressed uint32, modsLatched uint32, modsLocked uint32, group uint32)) {
3260+ if handler == nil {
3261+ return
3262+ }
3263+ o.handlersMu.Lock()
3264+ defer o.handlersMu.Unlock()
3265+ o.onModifiers = append(o.onModifiers, handler)
3266+}
3267+
3268+// handlersForModifiers returns the handlers registered for modifiers, taken
3269+// under the lock so a handler may register another handler while events are
3270+// being dispatched.
3271+func (o *Keyboard) handlersForModifiers() []func(serial uint32, modsDepressed uint32, modsLatched uint32, modsLocked uint32, group uint32) {
3272+ o.handlersMu.Lock()
3273+ defer o.handlersMu.Unlock()
3274+ return o.onModifiers
3275+}
3276+
3277+// OnRepeatInfo registers a handler for the repeat_info event.
3278+//
3279+// Handlers are appended, so a second call adds another handler rather than
3280+// replacing the first. Registration is safe while another goroutine dispatches.
3281+func (o *Keyboard) OnRepeatInfo(handler func(rate int32, delay int32)) {
3282+ if handler == nil {
3283+ return
3284+ }
3285+ o.handlersMu.Lock()
3286+ defer o.handlersMu.Unlock()
3287+ o.onRepeatInfo = append(o.onRepeatInfo, handler)
3288+}
3289+
3290+// handlersForRepeatInfo returns the handlers registered for repeat_info, taken
3291+// under the lock so a handler may register another handler while events are
3292+// being dispatched.
3293+func (o *Keyboard) handlersForRepeatInfo() []func(rate int32, delay int32) {
3294+ o.handlersMu.Lock()
3295+ defer o.handlersMu.Unlock()
3296+ return o.onRepeatInfo
3297+}
3298+
3299+// EventSignature returns the declared event argument types in wire order.
3300+func (o *Keyboard) EventSignature(opcode uint16) (string, bool) {
3301+ switch opcode {
3302+ case 0:
3303+ return "uint,fd,uint,", true
3304+ case 1:
3305+ return "uint,object,array,", true
3306+ case 2:
3307+ return "uint,object,", true
3308+ case 3:
3309+ return "uint,uint,uint,uint,", true
3310+ case 4:
3311+ return "uint,uint,uint,uint,uint,", true
3312+ case 5:
3313+ return "int,int,", true
3314+ default:
3315+ return "", false
3316+ }
3317+}
3318+
3319+// Dispatch decodes one event on wl_keyboard and calls the registered handlers.
3320+func (o *Keyboard) Dispatch(event *wl.Event) {
3321+ switch event.Opcode {
3322+ case 0: // keymap
3323+ format := event.Uint32()
3324+ fd := event.FD()
3325+ size := event.Uint32()
3326+ ownedfd := fd
3327+ defer ownedfd.Close()
3328+
3329+ for i, handler := range o.handlersForKeymap() {
3330+ if i > 0 {
3331+ fd = nil
3332+ }
3333+ handler(format, fd, size)
3334+ }
3335+ case 1: // enter
3336+ serial := event.Uint32()
3337+ surfaceID := event.Uint32()
3338+ keys := event.Array()
3339+
3340+ for i, handler := range o.handlersForEnter() {
3341+ _ = i
3342+ handler(serial, surfaceID, keys)
3343+ }
3344+ case 2: // leave
3345+ serial := event.Uint32()
3346+ surfaceID := event.Uint32()
3347+
3348+ for i, handler := range o.handlersForLeave() {
3349+ _ = i
3350+ handler(serial, surfaceID)
3351+ }
3352+ case 3: // key
3353+ serial := event.Uint32()
3354+ time := event.Uint32()
3355+ key := event.Uint32()
3356+ state := event.Uint32()
3357+
3358+ for i, handler := range o.handlersForKey() {
3359+ _ = i
3360+ handler(serial, time, key, state)
3361+ }
3362+ case 4: // modifiers
3363+ serial := event.Uint32()
3364+ modsDepressed := event.Uint32()
3365+ modsLatched := event.Uint32()
3366+ modsLocked := event.Uint32()
3367+ group := event.Uint32()
3368+
3369+ for i, handler := range o.handlersForModifiers() {
3370+ _ = i
3371+ handler(serial, modsDepressed, modsLatched, modsLocked, group)
3372+ }
3373+ case 5: // repeat_info
3374+ rate := event.Int32()
3375+ delay := event.Int32()
3376+
3377+ for i, handler := range o.handlersForRepeatInfo() {
3378+ _ = i
3379+ handler(rate, delay)
3380+ }
3381+ default:
3382+ }
3383+}
3384+
3385+// TouchInterface is the protocol interface name.
3386+const TouchInterface = "wl_touch"
3387+
3388+// Touch is the generated binding for wl_touch version 11.
3389+type Touch struct {
3390+ wl.BaseProxy
3391+ // handlersMu guards the handler slices: a client may register or replace
3392+ // handlers while another goroutine dispatches events.
3393+ handlersMu sync.Mutex
3394+ onDown []func(serial uint32, time uint32, surfaceID uint32, id int32, x wl.Fixed, y wl.Fixed)
3395+ onUp []func(serial uint32, time uint32, id int32)
3396+ onMotion []func(time uint32, id int32, x wl.Fixed, y wl.Fixed)
3397+ onFrame []func()
3398+ onCancel []func()
3399+ onShape []func(id int32, major wl.Fixed, minor wl.Fixed)
3400+ onOrientation []func(id int32, orientation wl.Fixed)
3401+}
3402+
3403+// NewTouch returns an unbound Touch.
3404+func NewTouch(ctx *wl.Context) *Touch {
3405+ obj := &Touch{}
3406+ obj.SetContext(ctx)
3407+ ctx.Register(obj)
3408+ return obj
3409+}
3410+
3411+// release the touch object
3412+func (o *Touch) Release() error {
3413+ if err := o.Context().CheckProxy(o); err != nil {
3414+ return err
3415+ }
3416+ if err := o.Context().SendDestructor(o, 0); err != nil {
3417+ return err
3418+ }
3419+ return nil
3420+}
3421+
3422+// OnDown registers a handler for the down event.
3423+//
3424+// Handlers are appended, so a second call adds another handler rather than
3425+// replacing the first. Registration is safe while another goroutine dispatches.
3426+func (o *Touch) OnDown(handler func(serial uint32, time uint32, surfaceID uint32, id int32, x wl.Fixed, y wl.Fixed)) {
3427+ if handler == nil {
3428+ return
3429+ }
3430+ o.handlersMu.Lock()
3431+ defer o.handlersMu.Unlock()
3432+ o.onDown = append(o.onDown, handler)
3433+}
3434+
3435+// handlersForDown returns the handlers registered for down, taken
3436+// under the lock so a handler may register another handler while events are
3437+// being dispatched.
3438+func (o *Touch) handlersForDown() []func(serial uint32, time uint32, surfaceID uint32, id int32, x wl.Fixed, y wl.Fixed) {
3439+ o.handlersMu.Lock()
3440+ defer o.handlersMu.Unlock()
3441+ return o.onDown
3442+}
3443+
3444+// OnUp registers a handler for the up event.
3445+//
3446+// Handlers are appended, so a second call adds another handler rather than
3447+// replacing the first. Registration is safe while another goroutine dispatches.
3448+func (o *Touch) OnUp(handler func(serial uint32, time uint32, id int32)) {
3449+ if handler == nil {
3450+ return
3451+ }
3452+ o.handlersMu.Lock()
3453+ defer o.handlersMu.Unlock()
3454+ o.onUp = append(o.onUp, handler)
3455+}
3456+
3457+// handlersForUp returns the handlers registered for up, taken
3458+// under the lock so a handler may register another handler while events are
3459+// being dispatched.
3460+func (o *Touch) handlersForUp() []func(serial uint32, time uint32, id int32) {
3461+ o.handlersMu.Lock()
3462+ defer o.handlersMu.Unlock()
3463+ return o.onUp
3464+}
3465+
3466+// OnMotion registers a handler for the motion event.
3467+//
3468+// Handlers are appended, so a second call adds another handler rather than
3469+// replacing the first. Registration is safe while another goroutine dispatches.
3470+func (o *Touch) OnMotion(handler func(time uint32, id int32, x wl.Fixed, y wl.Fixed)) {
3471+ if handler == nil {
3472+ return
3473+ }
3474+ o.handlersMu.Lock()
3475+ defer o.handlersMu.Unlock()
3476+ o.onMotion = append(o.onMotion, handler)
3477+}
3478+
3479+// handlersForMotion returns the handlers registered for motion, taken
3480+// under the lock so a handler may register another handler while events are
3481+// being dispatched.
3482+func (o *Touch) handlersForMotion() []func(time uint32, id int32, x wl.Fixed, y wl.Fixed) {
3483+ o.handlersMu.Lock()
3484+ defer o.handlersMu.Unlock()
3485+ return o.onMotion
3486+}
3487+
3488+// OnFrame registers a handler for the frame event.
3489+//
3490+// Handlers are appended, so a second call adds another handler rather than
3491+// replacing the first. Registration is safe while another goroutine dispatches.
3492+func (o *Touch) OnFrame(handler func()) {
3493+ if handler == nil {
3494+ return
3495+ }
3496+ o.handlersMu.Lock()
3497+ defer o.handlersMu.Unlock()
3498+ o.onFrame = append(o.onFrame, handler)
3499+}
3500+
3501+// handlersForFrame returns the handlers registered for frame, taken
3502+// under the lock so a handler may register another handler while events are
3503+// being dispatched.
3504+func (o *Touch) handlersForFrame() []func() {
3505+ o.handlersMu.Lock()
3506+ defer o.handlersMu.Unlock()
3507+ return o.onFrame
3508+}
3509+
3510+// OnCancel registers a handler for the cancel event.
3511+//
3512+// Handlers are appended, so a second call adds another handler rather than
3513+// replacing the first. Registration is safe while another goroutine dispatches.
3514+func (o *Touch) OnCancel(handler func()) {
3515+ if handler == nil {
3516+ return
3517+ }
3518+ o.handlersMu.Lock()
3519+ defer o.handlersMu.Unlock()
3520+ o.onCancel = append(o.onCancel, handler)
3521+}
3522+
3523+// handlersForCancel returns the handlers registered for cancel, taken
3524+// under the lock so a handler may register another handler while events are
3525+// being dispatched.
3526+func (o *Touch) handlersForCancel() []func() {
3527+ o.handlersMu.Lock()
3528+ defer o.handlersMu.Unlock()
3529+ return o.onCancel
3530+}
3531+
3532+// OnShape registers a handler for the shape event.
3533+//
3534+// Handlers are appended, so a second call adds another handler rather than
3535+// replacing the first. Registration is safe while another goroutine dispatches.
3536+func (o *Touch) OnShape(handler func(id int32, major wl.Fixed, minor wl.Fixed)) {
3537+ if handler == nil {
3538+ return
3539+ }
3540+ o.handlersMu.Lock()
3541+ defer o.handlersMu.Unlock()
3542+ o.onShape = append(o.onShape, handler)
3543+}
3544+
3545+// handlersForShape returns the handlers registered for shape, taken
3546+// under the lock so a handler may register another handler while events are
3547+// being dispatched.
3548+func (o *Touch) handlersForShape() []func(id int32, major wl.Fixed, minor wl.Fixed) {
3549+ o.handlersMu.Lock()
3550+ defer o.handlersMu.Unlock()
3551+ return o.onShape
3552+}
3553+
3554+// OnOrientation registers a handler for the orientation event.
3555+//
3556+// Handlers are appended, so a second call adds another handler rather than
3557+// replacing the first. Registration is safe while another goroutine dispatches.
3558+func (o *Touch) OnOrientation(handler func(id int32, orientation wl.Fixed)) {
3559+ if handler == nil {
3560+ return
3561+ }
3562+ o.handlersMu.Lock()
3563+ defer o.handlersMu.Unlock()
3564+ o.onOrientation = append(o.onOrientation, handler)
3565+}
3566+
3567+// handlersForOrientation returns the handlers registered for orientation, taken
3568+// under the lock so a handler may register another handler while events are
3569+// being dispatched.
3570+func (o *Touch) handlersForOrientation() []func(id int32, orientation wl.Fixed) {
3571+ o.handlersMu.Lock()
3572+ defer o.handlersMu.Unlock()
3573+ return o.onOrientation
3574+}
3575+
3576+// EventSignature returns the declared event argument types in wire order.
3577+func (o *Touch) EventSignature(opcode uint16) (string, bool) {
3578+ switch opcode {
3579+ case 0:
3580+ return "uint,uint,object,int,fixed,fixed,", true
3581+ case 1:
3582+ return "uint,uint,int,", true
3583+ case 2:
3584+ return "uint,int,fixed,fixed,", true
3585+ case 3:
3586+ return "", true
3587+ case 4:
3588+ return "", true
3589+ case 5:
3590+ return "int,fixed,fixed,", true
3591+ case 6:
3592+ return "int,fixed,", true
3593+ default:
3594+ return "", false
3595+ }
3596+}
3597+
3598+// Dispatch decodes one event on wl_touch and calls the registered handlers.
3599+func (o *Touch) Dispatch(event *wl.Event) {
3600+ switch event.Opcode {
3601+ case 0: // down
3602+ serial := event.Uint32()
3603+ time := event.Uint32()
3604+ surfaceID := event.Uint32()
3605+ id := event.Int32()
3606+ x := event.Fixed()
3607+ y := event.Fixed()
3608+
3609+ for i, handler := range o.handlersForDown() {
3610+ _ = i
3611+ handler(serial, time, surfaceID, id, x, y)
3612+ }
3613+ case 1: // up
3614+ serial := event.Uint32()
3615+ time := event.Uint32()
3616+ id := event.Int32()
3617+
3618+ for i, handler := range o.handlersForUp() {
3619+ _ = i
3620+ handler(serial, time, id)
3621+ }
3622+ case 2: // motion
3623+ time := event.Uint32()
3624+ id := event.Int32()
3625+ x := event.Fixed()
3626+ y := event.Fixed()
3627+
3628+ for i, handler := range o.handlersForMotion() {
3629+ _ = i
3630+ handler(time, id, x, y)
3631+ }
3632+ case 3: // frame
3633+
3634+ for i, handler := range o.handlersForFrame() {
3635+ _ = i
3636+ handler()
3637+ }
3638+ case 4: // cancel
3639+
3640+ for i, handler := range o.handlersForCancel() {
3641+ _ = i
3642+ handler()
3643+ }
3644+ case 5: // shape
3645+ id := event.Int32()
3646+ major := event.Fixed()
3647+ minor := event.Fixed()
3648+
3649+ for i, handler := range o.handlersForShape() {
3650+ _ = i
3651+ handler(id, major, minor)
3652+ }
3653+ case 6: // orientation
3654+ id := event.Int32()
3655+ orientation := event.Fixed()
3656+
3657+ for i, handler := range o.handlersForOrientation() {
3658+ _ = i
3659+ handler(id, orientation)
3660+ }
3661+ default:
3662+ }
3663+}
3664+
3665+// OutputInterface is the protocol interface name.
3666+const OutputInterface = "wl_output"
3667+
3668+// Output is the generated binding for wl_output version 4.
3669+type Output struct {
3670+ wl.BaseProxy
3671+ // handlersMu guards the handler slices: a client may register or replace
3672+ // handlers while another goroutine dispatches events.
3673+ handlersMu sync.Mutex
3674+ onGeometry []func(x int32, y int32, physicalWidth int32, physicalHeight int32, subpixel int32, make string, model string, transform int32)
3675+ onMode []func(flags uint32, width int32, height int32, refresh int32)
3676+ onDone []func()
3677+ onScale []func(factor int32)
3678+ onName []func(name string)
3679+ onDescription []func(description string)
3680+}
3681+
3682+// NewOutput returns an unbound Output.
3683+func NewOutput(ctx *wl.Context) *Output {
3684+ obj := &Output{}
3685+ obj.SetContext(ctx)
3686+ ctx.Register(obj)
3687+ return obj
3688+}
3689+
3690+// Using this request a client can tell the server that it is not going to use the output object anymore.
3691+func (o *Output) Release() error {
3692+ if err := o.Context().CheckProxy(o); err != nil {
3693+ return err
3694+ }
3695+ if err := o.Context().SendDestructor(o, 0); err != nil {
3696+ return err
3697+ }
3698+ return nil
3699+}
3700+
3701+// OnGeometry registers a handler for the geometry event.
3702+//
3703+// Handlers are appended, so a second call adds another handler rather than
3704+// replacing the first. Registration is safe while another goroutine dispatches.
3705+func (o *Output) OnGeometry(handler func(x int32, y int32, physicalWidth int32, physicalHeight int32, subpixel int32, make string, model string, transform int32)) {
3706+ if handler == nil {
3707+ return
3708+ }
3709+ o.handlersMu.Lock()
3710+ defer o.handlersMu.Unlock()
3711+ o.onGeometry = append(o.onGeometry, handler)
3712+}
3713+
3714+// handlersForGeometry returns the handlers registered for geometry, taken
3715+// under the lock so a handler may register another handler while events are
3716+// being dispatched.
3717+func (o *Output) handlersForGeometry() []func(x int32, y int32, physicalWidth int32, physicalHeight int32, subpixel int32, make string, model string, transform int32) {
3718+ o.handlersMu.Lock()
3719+ defer o.handlersMu.Unlock()
3720+ return o.onGeometry
3721+}
3722+
3723+// OnMode registers a handler for the mode event.
3724+//
3725+// Handlers are appended, so a second call adds another handler rather than
3726+// replacing the first. Registration is safe while another goroutine dispatches.
3727+func (o *Output) OnMode(handler func(flags uint32, width int32, height int32, refresh int32)) {
3728+ if handler == nil {
3729+ return
3730+ }
3731+ o.handlersMu.Lock()
3732+ defer o.handlersMu.Unlock()
3733+ o.onMode = append(o.onMode, handler)
3734+}
3735+
3736+// handlersForMode returns the handlers registered for mode, taken
3737+// under the lock so a handler may register another handler while events are
3738+// being dispatched.
3739+func (o *Output) handlersForMode() []func(flags uint32, width int32, height int32, refresh int32) {
3740+ o.handlersMu.Lock()
3741+ defer o.handlersMu.Unlock()
3742+ return o.onMode
3743+}
3744+
3745+// OnDone registers a handler for the done event.
3746+//
3747+// Handlers are appended, so a second call adds another handler rather than
3748+// replacing the first. Registration is safe while another goroutine dispatches.
3749+func (o *Output) OnDone(handler func()) {
3750+ if handler == nil {
3751+ return
3752+ }
3753+ o.handlersMu.Lock()
3754+ defer o.handlersMu.Unlock()
3755+ o.onDone = append(o.onDone, handler)
3756+}
3757+
3758+// handlersForDone returns the handlers registered for done, taken
3759+// under the lock so a handler may register another handler while events are
3760+// being dispatched.
3761+func (o *Output) handlersForDone() []func() {
3762+ o.handlersMu.Lock()
3763+ defer o.handlersMu.Unlock()
3764+ return o.onDone
3765+}
3766+
3767+// OnScale registers a handler for the scale event.
3768+//
3769+// Handlers are appended, so a second call adds another handler rather than
3770+// replacing the first. Registration is safe while another goroutine dispatches.
3771+func (o *Output) OnScale(handler func(factor int32)) {
3772+ if handler == nil {
3773+ return
3774+ }
3775+ o.handlersMu.Lock()
3776+ defer o.handlersMu.Unlock()
3777+ o.onScale = append(o.onScale, handler)
3778+}
3779+
3780+// handlersForScale returns the handlers registered for scale, taken
3781+// under the lock so a handler may register another handler while events are
3782+// being dispatched.
3783+func (o *Output) handlersForScale() []func(factor int32) {
3784+ o.handlersMu.Lock()
3785+ defer o.handlersMu.Unlock()
3786+ return o.onScale
3787+}
3788+
3789+// OnName registers a handler for the name event.
3790+//
3791+// Handlers are appended, so a second call adds another handler rather than
3792+// replacing the first. Registration is safe while another goroutine dispatches.
3793+func (o *Output) OnName(handler func(name string)) {
3794+ if handler == nil {
3795+ return
3796+ }
3797+ o.handlersMu.Lock()
3798+ defer o.handlersMu.Unlock()
3799+ o.onName = append(o.onName, handler)
3800+}
3801+
3802+// handlersForName returns the handlers registered for name, taken
3803+// under the lock so a handler may register another handler while events are
3804+// being dispatched.
3805+func (o *Output) handlersForName() []func(name string) {
3806+ o.handlersMu.Lock()
3807+ defer o.handlersMu.Unlock()
3808+ return o.onName
3809+}
3810+
3811+// OnDescription registers a handler for the description event.
3812+//
3813+// Handlers are appended, so a second call adds another handler rather than
3814+// replacing the first. Registration is safe while another goroutine dispatches.
3815+func (o *Output) OnDescription(handler func(description string)) {
3816+ if handler == nil {
3817+ return
3818+ }
3819+ o.handlersMu.Lock()
3820+ defer o.handlersMu.Unlock()
3821+ o.onDescription = append(o.onDescription, handler)
3822+}
3823+
3824+// handlersForDescription returns the handlers registered for description, taken
3825+// under the lock so a handler may register another handler while events are
3826+// being dispatched.
3827+func (o *Output) handlersForDescription() []func(description string) {
3828+ o.handlersMu.Lock()
3829+ defer o.handlersMu.Unlock()
3830+ return o.onDescription
3831+}
3832+
3833+// EventSignature returns the declared event argument types in wire order.
3834+func (o *Output) EventSignature(opcode uint16) (string, bool) {
3835+ switch opcode {
3836+ case 0:
3837+ return "int,int,int,int,int,string,string,int,", true
3838+ case 1:
3839+ return "uint,int,int,int,", true
3840+ case 2:
3841+ return "", true
3842+ case 3:
3843+ return "int,", true
3844+ case 4:
3845+ return "string,", true
3846+ case 5:
3847+ return "string,", true
3848+ default:
3849+ return "", false
3850+ }
3851+}
3852+
3853+// Dispatch decodes one event on wl_output and calls the registered handlers.
3854+func (o *Output) Dispatch(event *wl.Event) {
3855+ switch event.Opcode {
3856+ case 0: // geometry
3857+ x := event.Int32()
3858+ y := event.Int32()
3859+ physicalWidth := event.Int32()
3860+ physicalHeight := event.Int32()
3861+ subpixel := event.Int32()
3862+ make := event.String()
3863+ model := event.String()
3864+ transform := event.Int32()
3865+
3866+ for i, handler := range o.handlersForGeometry() {
3867+ _ = i
3868+ handler(x, y, physicalWidth, physicalHeight, subpixel, make, model, transform)
3869+ }
3870+ case 1: // mode
3871+ flags := event.Uint32()
3872+ width := event.Int32()
3873+ height := event.Int32()
3874+ refresh := event.Int32()
3875+
3876+ for i, handler := range o.handlersForMode() {
3877+ _ = i
3878+ handler(flags, width, height, refresh)
3879+ }
3880+ case 2: // done
3881+
3882+ for i, handler := range o.handlersForDone() {
3883+ _ = i
3884+ handler()
3885+ }
3886+ case 3: // scale
3887+ factor := event.Int32()
3888+
3889+ for i, handler := range o.handlersForScale() {
3890+ _ = i
3891+ handler(factor)
3892+ }
3893+ case 4: // name
3894+ name := event.String()
3895+
3896+ for i, handler := range o.handlersForName() {
3897+ _ = i
3898+ handler(name)
3899+ }
3900+ case 5: // description
3901+ description := event.String()
3902+
3903+ for i, handler := range o.handlersForDescription() {
3904+ _ = i
3905+ handler(description)
3906+ }
3907+ default:
3908+ }
3909+}
3910+
3911+// RegionInterface is the protocol interface name.
3912+const RegionInterface = "wl_region"
3913+
3914+// Region is the generated binding for wl_region version 7.
3915+type Region struct {
3916+ wl.BaseProxy
3917+}
3918+
3919+// NewRegion returns an unbound Region.
3920+func NewRegion(ctx *wl.Context) *Region {
3921+ obj := &Region{}
3922+ obj.SetContext(ctx)
3923+ ctx.Register(obj)
3924+ return obj
3925+}
3926+
3927+// Destroy the region. This will invalidate the object ID.
3928+func (o *Region) Destroy() error {
3929+ if err := o.Context().CheckProxy(o); err != nil {
3930+ return err
3931+ }
3932+ if err := o.Context().SendDestructor(o, 0); err != nil {
3933+ return err
3934+ }
3935+ return nil
3936+}
3937+
3938+// Add the specified rectangle to the region.
3939+func (o *Region) Add(x int32, y int32, width int32, height int32) error {
3940+ if err := o.Context().CheckProxy(o); err != nil {
3941+ return err
3942+ }
3943+ if err := o.Context().SendRequest(o, 1, x, y, width, height); err != nil {
3944+ return err
3945+ }
3946+ return nil
3947+}
3948+
3949+// Subtract the specified rectangle from the region.
3950+func (o *Region) Subtract(x int32, y int32, width int32, height int32) error {
3951+ if err := o.Context().CheckProxy(o); err != nil {
3952+ return err
3953+ }
3954+ if err := o.Context().SendRequest(o, 2, x, y, width, height); err != nil {
3955+ return err
3956+ }
3957+ return nil
3958+}
3959+
3960+// EventSignature returns the declared event argument types in wire order.
3961+func (o *Region) EventSignature(opcode uint16) (string, bool) {
3962+ switch opcode {
3963+ default:
3964+ return "", false
3965+ }
3966+}
3967+
3968+// Dispatch decodes one event on wl_region and calls the registered handlers.
3969+func (o *Region) Dispatch(event *wl.Event) {
3970+ switch event.Opcode {
3971+ default:
3972+ }
3973+}
3974+
3975+// SubcompositorInterface is the protocol interface name.
3976+const SubcompositorInterface = "wl_subcompositor"
3977+
3978+// Subcompositor is the generated binding for wl_subcompositor version 1.
3979+type Subcompositor struct {
3980+ wl.BaseProxy
3981+}
3982+
3983+// NewSubcompositor returns an unbound Subcompositor.
3984+func NewSubcompositor(ctx *wl.Context) *Subcompositor {
3985+ obj := &Subcompositor{}
3986+ obj.SetContext(ctx)
3987+ ctx.Register(obj)
3988+ return obj
3989+}
3990+
3991+// Informs the server that the client will not be using this protocol object anymore. This does not affect any other objects, wl_subsurface objects included.
3992+func (o *Subcompositor) Destroy() error {
3993+ if err := o.Context().CheckProxy(o); err != nil {
3994+ return err
3995+ }
3996+ if err := o.Context().SendDestructor(o, 0); err != nil {
3997+ return err
3998+ }
3999+ return nil
4000+}
4001+
4002+// Create a sub-surface interface for the given surface, and associate it with the given parent surface. This turns a plain wl_surface into a sub-surface. The to-be sub-surface must not already have another role, and it must not have an existing wl_subsurface object. Otherwise the bad_surface protocol error is raised. Adding sub-surfaces to a parent is a double-buffered operation on the parent (see wl_surface.commit). The effect of adding a sub-surface becomes visible on the next time the state of the parent surface is applied. The parent surface must not be one of the child surface's descendants, and the parent must be different from the child surface, otherwise the bad_parent protocol error is raised. This request modifies the behaviour of wl_surface.commit request on the sub-surface, see the documentation on wl_subsurface interface.
4003+func (o *Subcompositor) GetSubsurface(surface *Surface, parent *Surface) (*Subsurface, error) {
4004+ if err := o.Context().CheckProxy(o); err != nil {
4005+ return nil, err
4006+ }
4007+ var arg1 wl.Object
4008+ if surface != nil {
4009+ arg1 = surface
4010+ }
4011+ var arg2 wl.Object
4012+ if parent != nil {
4013+ arg2 = parent
4014+ }
4015+ child := &Subsurface{}
4016+ child.SetContext(o.Context())
4017+ child.SetID(o.Context().AllocateID())
4018+ o.Context().Register(child)
4019+ if err := o.Context().SendRequest(o, 1, child, arg1, arg2); err != nil {
4020+ o.Context().Unregister(child)
4021+ return nil, err
4022+ }
4023+ return child, nil
4024+}
4025+
4026+// EventSignature returns the declared event argument types in wire order.
4027+func (o *Subcompositor) EventSignature(opcode uint16) (string, bool) {
4028+ switch opcode {
4029+ default:
4030+ return "", false
4031+ }
4032+}
4033+
4034+// Dispatch decodes one event on wl_subcompositor and calls the registered handlers.
4035+func (o *Subcompositor) Dispatch(event *wl.Event) {
4036+ switch event.Opcode {
4037+ default:
4038+ }
4039+}
4040+
4041+// SubsurfaceInterface is the protocol interface name.
4042+const SubsurfaceInterface = "wl_subsurface"
4043+
4044+// Subsurface is the generated binding for wl_subsurface version 1.
4045+type Subsurface struct {
4046+ wl.BaseProxy
4047+}
4048+
4049+// NewSubsurface returns an unbound Subsurface.
4050+func NewSubsurface(ctx *wl.Context) *Subsurface {
4051+ obj := &Subsurface{}
4052+ obj.SetContext(ctx)
4053+ ctx.Register(obj)
4054+ return obj
4055+}
4056+
4057+// The sub-surface interface is removed from the wl_surface object that was turned into a sub-surface with a wl_subcompositor.get_subsurface request. The wl_surface's association to the parent is deleted. The wl_surface is unmapped immediately.
4058+func (o *Subsurface) Destroy() error {
4059+ if err := o.Context().CheckProxy(o); err != nil {
4060+ return err
4061+ }
4062+ if err := o.Context().SendDestructor(o, 0); err != nil {
4063+ return err
4064+ }
4065+ return nil
4066+}
4067+
4068+// This sets the position of the sub-surface, relative to the parent surface. The sub-surface will be moved so that its origin (top left corner pixel) will be at the location x, y of the parent surface coordinate system. The coordinates are not restricted to the parent surface area. Negative values are allowed. The initial position is 0, 0. Position is double-buffered state on the parent surface, see wl_subsurface and wl_surface.commit for more information.
4069+func (o *Subsurface) SetPosition(x int32, y int32) error {
4070+ if err := o.Context().CheckProxy(o); err != nil {
4071+ return err
4072+ }
4073+ if err := o.Context().SendRequest(o, 1, x, y); err != nil {
4074+ return err
4075+ }
4076+ return nil
4077+}
4078+
4079+// This sub-surface is taken from the stack, and put back just above the reference surface, changing the z-order of the sub-surfaces. The reference surface must be one of the sibling surfaces, or the parent surface. Using any other surface, including this sub-surface, will cause a protocol error. A new sub-surface is initially added as the top-most in the stack of its siblings and parent. Z-order is double-buffered state on the parent surface, see wl_subsurface and wl_surface.commit for more information.
4080+func (o *Subsurface) PlaceAbove(sibling *Surface) error {
4081+ if err := o.Context().CheckProxy(o); err != nil {
4082+ return err
4083+ }
4084+ var arg0 wl.Object
4085+ if sibling != nil {
4086+ arg0 = sibling
4087+ }
4088+ if err := o.Context().SendRequest(o, 2, arg0); err != nil {
4089+ return err
4090+ }
4091+ return nil
4092+}
4093+
4094+// The sub-surface is placed just below the reference surface. See wl_subsurface.place_above.
4095+func (o *Subsurface) PlaceBelow(sibling *Surface) error {
4096+ if err := o.Context().CheckProxy(o); err != nil {
4097+ return err
4098+ }
4099+ var arg0 wl.Object
4100+ if sibling != nil {
4101+ arg0 = sibling
4102+ }
4103+ if err := o.Context().SendRequest(o, 3, arg0); err != nil {
4104+ return err
4105+ }
4106+ return nil
4107+}
4108+
4109+// Change the commit behaviour of the sub-surface to synchronized mode. See wl_subsurface and wl_surface.commit for more information.
4110+func (o *Subsurface) SetSync() error {
4111+ if err := o.Context().CheckProxy(o); err != nil {
4112+ return err
4113+ }
4114+ if err := o.Context().SendRequest(o, 4); err != nil {
4115+ return err
4116+ }
4117+ return nil
4118+}
4119+
4120+// Change the commit behaviour of the sub-surface to desynchronized mode. See wl_subsurface and wl_surface.commit for more information.
4121+func (o *Subsurface) SetDesync() error {
4122+ if err := o.Context().CheckProxy(o); err != nil {
4123+ return err
4124+ }
4125+ if err := o.Context().SendRequest(o, 5); err != nil {
4126+ return err
4127+ }
4128+ return nil
4129+}
4130+
4131+// EventSignature returns the declared event argument types in wire order.
4132+func (o *Subsurface) EventSignature(opcode uint16) (string, bool) {
4133+ switch opcode {
4134+ default:
4135+ return "", false
4136+ }
4137+}
4138+
4139+// Dispatch decodes one event on wl_subsurface and calls the registered handlers.
4140+func (o *Subsurface) Dispatch(event *wl.Event) {
4141+ switch event.Opcode {
4142+ default:
4143+ }
4144+}
4145+
4146+// FixesInterface is the protocol interface name.
4147+const FixesInterface = "wl_fixes"
4148+
4149+// Fixes is the generated binding for wl_fixes version 2.
4150+type Fixes struct {
4151+ wl.BaseProxy
4152+}
4153+
4154+// NewFixes returns an unbound Fixes.
4155+func NewFixes(ctx *wl.Context) *Fixes {
4156+ obj := &Fixes{}
4157+ obj.SetContext(ctx)
4158+ ctx.Register(obj)
4159+ return obj
4160+}
4161+
4162+// destroys this object
4163+func (o *Fixes) Destroy() error {
4164+ if err := o.Context().CheckProxy(o); err != nil {
4165+ return err
4166+ }
4167+ if err := o.Context().SendDestructor(o, 0); err != nil {
4168+ return err
4169+ }
4170+ return nil
4171+}
4172+
4173+// This request destroys a wl_registry object. The client should no longer use the wl_registry after making this request. The compositor will emit a wl_display.delete_id event with the object ID of the registry and will no longer emit any events on the registry. The client should re-use the object ID once it receives the wl_display.delete_id event.
4174+func (o *Fixes) DestroyRegistry(registry *wl.Registry) error {
4175+ if err := o.Context().CheckProxy(o); err != nil {
4176+ return err
4177+ }
4178+ var arg0 wl.Object
4179+ if registry != nil {
4180+ arg0 = registry
4181+ }
4182+ if err := o.Context().SendRequest(o, 1, arg0); err != nil {
4183+ return err
4184+ }
4185+ return nil
4186+}
4187+
4188+// Acknowledge the removal of the specified global. If no global with the specified name exists or the global is not removed, the wl_fixes.invalid_ack_remove protocol error will be posted. Due to the Wayland protocol being asynchronous, the wl_global objects cannot be destroyed immediately. For example, if a wl_global is removed and a client attempts to bind that global around same time, it can result in a protocol error due to an unknown global name in the bind request. In order to avoid crashing clients, the compositor should remove the wl_global once it is guaranteed that no more bind requests will come. The wl_fixes.ack_global_remove() request is used to signal to the compositor that the client will not bind the given global anymore. After all clients acknowledge the removal of the global, the compositor can safely destroy it. The client must call the wl_fixes.ack_global_remove() request in response to a wl_registry.global_remove() event even if it did not bind the corresponding global.
4189+func (o *Fixes) AckGlobalRemove(registry *wl.Registry, name uint32) error {
4190+ if err := o.Context().CheckProxy(o); err != nil {
4191+ return err
4192+ }
4193+ var arg0 wl.Object
4194+ if registry != nil {
4195+ arg0 = registry
4196+ }
4197+ if err := o.Context().SendRequest(o, 2, arg0, name); err != nil {
4198+ return err
4199+ }
4200+ return nil
4201+}
4202+
4203+// EventSignature returns the declared event argument types in wire order.
4204+func (o *Fixes) EventSignature(opcode uint16) (string, bool) {
4205+ switch opcode {
4206+ default:
4207+ return "", false
4208+ }
4209+}
4210+
4211+// Dispatch decodes one event on wl_fixes and calls the registered handlers.
4212+func (o *Fixes) Dispatch(event *wl.Event) {
4213+ switch event.Opcode {
4214+ default:
4215+ }
4216+}
A protocol/generate.go +3 -0
@@ -0,0 +1,3 @@
1+package protocol
2+
3+//go:generate go run ../cmd/wlturbo-scanner -p core -o core/wayland_generated.go wayland.xml
A protocol/wayland.xml +3442 -0 large diff (3442 lines changed): collapsed
M scm_linux.go +77 -49
@@ -6,9 +6,13 @@ package wlturbo
66 import (
77 "errors"
88 "fmt"
9- "golang.org/x/sys/unix"
9+ "io"
10+ "net"
1011 "sync"
1112 "syscall"
13+ "unsafe"
14+
15+ "golang.org/x/sys/unix"
1216 )
1317
1418 // maxRecvFDs bounds how many ancillary file descriptors a single read accepts.
@@ -16,10 +20,12 @@ import (
1620 // instead of silently dropping descriptors.
1721 const maxRecvFDs = 64
1822
23+type controlBuffer struct{ data []byte }
24+
1925 // controlBufferPool provides control-message buffers sized for maxRecvFDs.
2026 var controlBufferPool = sync.Pool{
2127 New: func() interface{} {
22- return make([]byte, unix.CmsgSpace(maxRecvFDs*4))
28+ return &controlBuffer{data: make([]byte, unix.CmsgSpace(maxRecvFDs*4))}
2329 },
2430 }
2531
@@ -40,8 +46,9 @@ func (d *Display) readChunk() (int, error) {
4046 return n, err
4147 }
4248
43- oob := controlBufferPool.Get().([]byte)
44- defer controlBufferPool.Put(oob)
49+ buffer := controlBufferPool.Get().(*controlBuffer)
50+ defer controlBufferPool.Put(buffer)
51+ oob := buffer.data
4552
4653 n, oobn, flags, _, err := d.unix.ReadMsgUnix(d.recvBuf, oob)
4754 if n < 0 {
@@ -51,12 +58,12 @@ func (d *Display) readChunk() (int, error) {
5158 if oobn > 0 {
5259 fds, perr := parseFDs(oob[:oobn])
5360 d.queueFDs(fds)
54- if perr != nil && err == nil {
55- err = perr
61+ if perr != nil {
62+ err = &ProtocolError{Kind: "malformed_control", Err: perr}
5663 }
5764 }
5865
59- if flags&unix.MSG_CTRUNC != 0 && err == nil {
66+ if flags&unix.MSG_CTRUNC != 0 {
6067 // Descriptors were dropped by the kernel, so the connection can no
6168 // longer be demarshalled correctly: treat it as a protocol failure so
6269 // the caller closes the descriptors that did arrive.
@@ -68,55 +75,54 @@ func (d *Display) readChunk() (int, error) {
6875 // parseFDs extracts the file descriptors carried by socket control messages.
6976 // Descriptors parsed before an error are returned so the caller can close them.
7077 func parseFDs(oob []byte) ([]int, error) {
71- scms, err := syscall.ParseSocketControlMessage(oob)
72- if err != nil {
73- return nil, fmt.Errorf("parse control message: %w", err)
74- }
75-
7678 var fds []int
77- for i := range scms {
78- scm := &scms[i]
79- if scm.Header.Type != syscall.SCM_RIGHTS {
80- continue
79+ for len(oob) > 0 {
80+ if len(oob) < unix.SizeofCmsghdr {
81+ return fds, fmt.Errorf("short control header")
8182 }
82- parsed, err := syscall.ParseUnixRights(scm)
83- if err != nil {
84- return fds, fmt.Errorf("parse unix rights: %w", err)
83+ var hdr unix.Cmsghdr
84+ // Parse each header independently: a malformed later header must not
85+ // discard rights already decoded from earlier messages.
86+ if err := readControlHeader(oob, &hdr); err != nil {
87+ return fds, err
88+ }
89+ length := int(hdr.Len)
90+ if length < unix.SizeofCmsghdr || length > len(oob) {
91+ return fds, fmt.Errorf("invalid control length %d", length)
92+ }
93+ if hdr.Level == unix.SOL_SOCKET && hdr.Type == unix.SCM_RIGHTS {
94+ message := syscall.SocketControlMessage{Header: syscall.Cmsghdr{Len: uint64(length), Level: int32(hdr.Level), Type: int32(hdr.Type)}, Data: oob[unix.SizeofCmsghdr:length]}
95+ parsed, err := syscall.ParseUnixRights(&message)
96+ if err != nil {
97+ return fds, fmt.Errorf("parse unix rights: %w", err)
98+ }
99+ fds = append(fds, parsed...)
85100 }
86- fds = append(fds, parsed...)
101+ step := unix.CmsgSpace(length - unix.SizeofCmsghdr)
102+ if step > len(oob) {
103+ break
104+ } // final control message may omit trailing padding
105+ oob = oob[step:]
87106 }
88107 return fds, nil
89108 }
90109
91-// queueFDs appends descriptors to this connection's pending list.
92-func (d *Display) queueFDs(fds []int) {
93- if len(fds) == 0 {
94- return
110+func readControlHeader(data []byte, hdr *unix.Cmsghdr) error {
111+ // Linux cmsghdr has a native-endian size_t followed by two int32s.
112+ if len(data) < unix.SizeofCmsghdr {
113+ return fmt.Errorf("short control header")
95114 }
96- d.fdMu.Lock()
97- d.pendingFDs = append(d.pendingFDs, fds...)
98- d.fdMu.Unlock()
115+ *hdr = *(*unix.Cmsghdr)(unsafe.Pointer(&data[0]))
116+ return nil
99117 }
100118
101-// nextFD removes and returns the oldest pending descriptor.
102-func (d *Display) nextFD() (int, bool) {
103- d.fdMu.Lock()
104- defer d.fdMu.Unlock()
105- if len(d.pendingFDs) == 0 {
106- return -1, false
107- }
108- fd := d.pendingFDs[0]
109- d.pendingFDs = d.pendingFDs[1:]
110- return fd, true
111-}
119+// queueFDs is called only by the active Dispatch reader.
120+func (d *Display) queueFDs(fds []int) { d.pendingFDs = append(d.pendingFDs, fds...) }
112121
113-// closePendingFDs closes every descriptor this connection still holds.
122+// closePendingFDs is called under recvMu (including during shutdown).
114123 func (d *Display) closePendingFDs() {
115- d.fdMu.Lock()
116124 fds := d.pendingFDs
117125 d.pendingFDs = nil
118- d.fdMu.Unlock()
119-
120126 for _, fd := range fds {
121127 if fd >= 0 {
122128 _ = unix.Close(fd)
@@ -124,20 +130,42 @@ func (d *Display) closePendingFDs() {
124130 }
125131 }
126132
133+// CloseSentFD releases a descriptor after a complete successful Wayland send.
134+func CloseSentFD(fd int) error { return unix.Close(fd) }
135+
127136 // sendmsgWithFDs sends a message, attaching file descriptors when present.
128-func (d *Display) sendmsgWithFDs(buf []byte, fds []int) error {
137+// A non-nil guard runs under the send lock, so its checks and the write are
138+// ordered atomically with every other request on the connection.
139+func (d *Display) sendmsgWithFDs(buf []byte, fds []int, guard func() error) error {
129140 d.sendMu.Lock()
130141 defer d.sendMu.Unlock()
131142
132- if len(fds) == 0 {
133- _, err := d.conn.Write(buf)
134- return err
143+ if d.closed.Load() {
144+ return net.ErrClosed
135145 }
136- if d.unix == nil {
146+ if len(fds) > 0 && d.unix == nil {
137147 return errors.New("wlturbo: cannot send file descriptors over a non-Unix connection")
138148 }
139-
140- _, _, err := d.unix.WriteMsgUnix(buf, unix.UnixRights(fds...), nil)
149+ if guard != nil {
150+ if err := guard(); err != nil {
151+ return err
152+ }
153+ }
154+ var n int
155+ var err error
156+ if len(fds) == 0 {
157+ n, err = d.conn.Write(buf)
158+ } else {
159+ n, _, err = d.unix.WriteMsgUnix(buf, unix.UnixRights(fds...), nil)
160+ }
161+ if err == nil && n != len(buf) {
162+ err = io.ErrShortWrite
163+ }
164+ if err != nil {
165+ // A failed or partial write desynchronizes the stream: shut the socket
166+ // down so every later send and receive fails instead of corrupting it.
167+ _ = d.conn.Close()
168+ }
141169 return err
142170 }
143171
M shm.go +0 -1
@@ -7,7 +7,6 @@ import (
77
88 // ShmPool represents a shared memory pool
99 type ShmPool struct {
10- id uint32
1110 fd int
1211 size int
1312 data []byte
A signature.go +50 -0
@@ -0,0 +1,50 @@
1+package wlturbo
2+
3+import (
4+ "encoding/binary"
5+ "fmt"
6+)
7+
8+// validateEventBody rejects truncated, unterminated and trailing wire values
9+// before generated code decodes or registers any children.
10+func validateEventBody(sig string, body []byte) error {
11+ pos := 0
12+ for len(sig) > 0 {
13+ end := 0
14+ for end < len(sig) && sig[end] != ',' {
15+ end++
16+ }
17+ kind := sig[:end]
18+ if end == len(sig) {
19+ return fmt.Errorf("invalid signature %q", sig)
20+ }
21+ sig = sig[end+1:]
22+ if kind == "fd" {
23+ continue
24+ }
25+ if len(body)-pos < 4 {
26+ return ErrMalformedFrame
27+ }
28+ switch kind {
29+ case "int", "uint", "fixed", "object", "new_id":
30+ pos += 4
31+ case "string", "array":
32+ n := uint64(binary.LittleEndian.Uint32(body[pos:]))
33+ pos += 4
34+ padded := (n + 3) &^ uint64(3)
35+ if padded > uint64(len(body)-pos) {
36+ return ErrMalformedFrame
37+ }
38+ if kind == "string" && n > 0 && body[pos+int(n)-1] != 0 {
39+ return ErrMalformedFrame
40+ }
41+ pos += int(padded)
42+ default:
43+ return fmt.Errorf("invalid argument kind %q: %w", kind, ErrMalformedFrame)
44+ }
45+ }
46+ if pos != len(body) {
47+ return ErrMalformedFrame
48+ }
49+ return nil
50+}
M transport_test.go +46 -5
@@ -92,6 +92,13 @@ func newChunkDisplay(chunks [][]byte) (*Display, *[]EventRecord) {
9292 BaseProxy: BaseProxy{id: 7, context: d.context},
9393 records: &records,
9494 })
95+ for _, opcode := range []uint16{3, 4, 5} {
96+ signature := "uint,"
97+ if opcode == 3 {
98+ signature = "uint,uint,"
99+ }
100+ d.RegisterEventSignature(7, opcode, signature)
101+ }
95102 return d, &records
96103 }
97104
@@ -132,7 +139,7 @@ func TestDisplayDispatch_Fragmented(t *testing.T) {
132139 }
133140
134141 func TestDisplayDispatch_Coalesced(t *testing.T) {
135- first := message(7, 3, []byte{0x11, 0x12, 0x13, 0x14})
142+ first := message(7, 4, []byte{0x11, 0x12, 0x13, 0x14})
136143 second := message(7, 4, []byte{0x21, 0x22, 0x23, 0x24})
137144
138145 d, records := newChunkDisplay([][]byte{append(append([]byte{}, first...), second...)})
@@ -146,7 +153,7 @@ func TestDisplayDispatch_Coalesced(t *testing.T) {
146153 }
147154
148155 want := []EventRecord{
149- {Object: 7, Opcode: 3, Body: []byte{0x11, 0x12, 0x13, 0x14}},
156+ {Object: 7, Opcode: 4, Body: []byte{0x11, 0x12, 0x13, 0x14}},
150157 {Object: 7, Opcode: 4, Body: []byte{0x21, 0x22, 0x23, 0x24}},
151158 }
152159 if !reflect.DeepEqual(*records, want) {
@@ -157,7 +164,7 @@ func TestDisplayDispatch_Coalesced(t *testing.T) {
157164 func TestDisplayDispatch_HalfCoalesced(t *testing.T) {
158165 // A read boundary that ends between the header and the body of the second
159166 // message must not be mistaken for a frame boundary.
160- whole := append(message(7, 3, []byte{0x01, 0x02, 0x03, 0x04}),
167+ whole := append(message(7, 4, []byte{0x01, 0x02, 0x03, 0x04}),
161168 message(7, 5, []byte{0x05, 0x06, 0x07, 0x08})...)
162169
163170 split := HeaderSize + 4 + 4 // mid-body of the second message
@@ -171,7 +178,7 @@ func TestDisplayDispatch_HalfCoalesced(t *testing.T) {
171178 }
172179
173180 want := []EventRecord{
174- {Object: 7, Opcode: 3, Body: []byte{0x01, 0x02, 0x03, 0x04}},
181+ {Object: 7, Opcode: 4, Body: []byte{0x01, 0x02, 0x03, 0x04}},
175182 {Object: 7, Opcode: 5, Body: []byte{0x05, 0x06, 0x07, 0x08}},
176183 }
177184 if !reflect.DeepEqual(*records, want) {
@@ -274,6 +281,10 @@ func TestDisplayDispatch_DisplayError(t *testing.T) {
274281
275282 func TestDisplayDispatch_DeleteID(t *testing.T) {
276283 d, records := newChunkDisplay([][]byte{message(displayObjectID, 1, []byte{7, 0, 0, 0})})
284+ for d.nextID <= 7 {
285+ d.allocateID()
286+ }
287+ d.objects.Delete(uint32(7))
277288
278289 if err := d.Dispatch(); err != nil {
279290 t.Fatalf("Dispatch: %v", err)
@@ -299,7 +310,7 @@ func TestDisplayDispatch_PeerClose(t *testing.T) {
299310 },
300311 {
301312 name: "closed after a complete message",
302- chunks: [][]byte{message(7, 3, []byte{0x01, 0x02, 0x03, 0x04})},
313+ chunks: [][]byte{message(7, 4, []byte{0x01, 0x02, 0x03, 0x04})},
303314 },
304315 }
305316
@@ -350,3 +361,33 @@ func TestReadFrame(t *testing.T) {
350361 t.Fatalf("readFrame(truncated) error = %v, want %v", err, io.ErrUnexpectedEOF)
351362 }
352363 }
364+
365+func TestDeleteIDReuseAndInvalid(t *testing.T) {
366+ d := newDisplay(&chunkConn{})
367+ id := d.AllocateID()
368+ body := make([]byte, 4)
369+ binary.LittleEndian.PutUint32(body, id)
370+ if err := d.handleDisplayEvent(1, body); err != nil {
371+ t.Fatal(err)
372+ }
373+ if err := d.handleDisplayEvent(1, body); err == nil {
374+ t.Fatal("duplicate delete_id accepted")
375+ }
376+ d.nextID = 0xff000000 // simulate exhaustion
377+ if got := d.AllocateID(); got != id {
378+ t.Fatalf("reused %d, want %d", got, id)
379+ }
380+ if got := d.AllocateID(); got != 0 {
381+ t.Fatalf("allocated server-range ID %x", got)
382+ }
383+ d.objects.Store(id, d)
384+ if err := d.handleDisplayEvent(1, body); err == nil {
385+ t.Fatal("live ID deleted")
386+ }
387+ for _, bad := range []uint32{0, 1, 0xff000001} {
388+ binary.LittleEndian.PutUint32(body, bad)
389+ if err := d.handleDisplayEvent(1, body); err == nil {
390+ t.Fatalf("accepted reserved ID %x", bad)
391+ }
392+ }
393+}
M wl/wl.go +2 -18
@@ -21,14 +21,7 @@ type (
2121 RegistryGlobalRemoveHandler = wlturbo.RegistryGlobalRemoveHandler
2222 RegistryGlobalEvent = wlturbo.RegistryGlobalEvent
2323 RegistryGlobalRemoveEvent = wlturbo.RegistryGlobalRemoveEvent
24- Seat = wlturbo.Seat
25- Surface = wlturbo.Surface
26- Pointer = wlturbo.Pointer
27- Keyboard = wlturbo.Keyboard
28- Touch = wlturbo.Touch
29- Output = wlturbo.Output
30- Region = wlturbo.Region
31- Compositor = wlturbo.Compositor
24+ OwnedFD = wlturbo.OwnedFD
3225
3326 // Transport failures. Without these, a caller that imports this shim has to
3427 // import the root package as well just to classify an error.
@@ -40,10 +33,8 @@ type (
4033 var (
4134 Connect = wlturbo.Connect
4235 NewFixed = wlturbo.NewFixed
43- NewSeat = wlturbo.NewSeat
4436 NewContext = wlturbo.NewContext
45- NewSurface = wlturbo.NewSurface
46- NewCompositor = wlturbo.NewCompositor
37+ CloseSentFD = wlturbo.CloseSentFD
4738 CreateAnonymousFile = wlturbo.CreateAnonymousFile
4839 MapMemory = wlturbo.MapMemory
4940 UnmapMemory = wlturbo.UnmapMemory
@@ -57,10 +48,3 @@ var (
5748 ErrUnknownOpcode = wlturbo.ErrUnknownOpcode
5849 ErrDisplayError = wlturbo.ErrDisplayError
5950 )
60-
61-// Seat capability constants
62-const (
63- SeatCapabilityPointer = wlturbo.SeatCapabilityPointer
64- SeatCapabilityKeyboard = wlturbo.SeatCapabilityKeyboard
65- SeatCapabilityTouch = wlturbo.SeatCapabilityTouch
66-)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 8

↑↓ moveenter openesc close