detached 3e99709

fix(protocol): harden dispatch lifecycle FD parsing and generation gates

commit
3e997094e0cb5a9bd492290b592f54e92bc44fd8
author
brice · · 2026-09-28 07:41 UTC
parent
f69b87f
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
11 files · +635 -71
M client.go +85 -30
@@ -49,8 +49,12 @@ type Display struct {
4949 unix *net.UnixConn // set when conn is a Unix socket, nil otherwise
5050 objects sync.Map // map[uint32]Object
5151 nextID uint32
52+ idMu sync.Mutex
53+ freeIDs []uint32
54+ retiredIDs map[uint32]bool
5255 sendMu sync.Mutex
5356 recvMu sync.Mutex
57+ dispatchMu sync.Mutex // serialize readers without holding receive state across handlers
5458 signatures sync.Map
5559 listenerMu sync.Mutex
5660 listeners sync.Map // map[uint32]map[uint16][]func([]byte)
@@ -192,8 +196,6 @@ func (d *Display) Close() error {
192196 }
193197 d.context.closed.Store(true)
194198 err := d.conn.Close()
195- d.sendMu.Lock()
196- d.sendMu.Unlock()
197199 d.recvMu.Lock()
198200 d.closePendingFDs()
199201 d.recvMu.Unlock()
@@ -217,7 +219,20 @@ func (d *Display) RegisterEventHandler(objectID uint32, opcode uint16, handler E
217219
218220 // allocateID allocates a new object ID
219221 func (d *Display) allocateID() uint32 {
220- 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
221236 }
222237
223238 // AllocateID allocates a new object ID (public method)
@@ -338,14 +353,17 @@ func (d *Display) marshalArg(buf *bytes.Buffer, arg interface{}) error {
338353 // Dispatch returns net.ErrClosed after Close, a typed ProtocolError for a
339354 // malformed frame, and io.ErrUnexpectedEOF when the peer closes mid-frame.
340355 func (d *Display) Dispatch() error {
356+ d.dispatchMu.Lock()
357+ defer d.dispatchMu.Unlock()
341358 d.recvMu.Lock()
342- defer d.recvMu.Unlock()
343359
344360 if d.closed.Load() {
361+ d.recvMu.Unlock()
345362 return net.ErrClosed
346363 }
347364
348365 frame, err := d.nextFrame()
366+ d.recvMu.Unlock()
349367 if d.closed.Load() {
350368 return net.ErrClosed
351369 }
@@ -362,6 +380,8 @@ func (d *Display) fail(err error) error {
362380 if err == nil {
363381 return nil
364382 }
383+ d.recvMu.Lock()
384+ defer d.recvMu.Unlock()
365385 var perr *ProtocolError
366386 if errors.As(err, &perr) {
367387 d.closePendingFDs()
@@ -448,11 +468,45 @@ type signatureProvider interface{ EventSignature(uint16) (string, bool) }
448468
449469 // dispatchFrame delivers one complete frame with precisely its signature's FDs.
450470 func (d *Display) dispatchFrame(f receivedFrame) error {
471+ d.recvMu.Lock()
472+ obj, ev, kind, err := d.prepareFrame(f)
473+ d.recvMu.Unlock()
474+ if err != nil {
475+ return err
476+ }
477+ switch kind {
478+ case 1:
479+ return d.handleDisplayEvent(f.opcode, f.body)
480+ case 2:
481+ d.notifyListeners(f.object, f.opcode, f.body)
482+ return nil
483+ }
484+ defer func() {
485+ for _, fd := range ev.fds {
486+ _ = fd.Close()
487+ }
488+ *ev = Event{}
489+ eventPool.Put(ev)
490+ }()
491+ if proxy, ok := obj.(Proxy); ok {
492+ proxy.Dispatch(ev)
493+ } else {
494+ d.dispatcher.Dispatch(f.object, f.opcode, f.body)
495+ d.notifyListeners(f.object, f.opcode, f.body)
496+ }
497+ return nil
498+}
499+
500+// prepareFrame assigns owned descriptors under recvMu, but never calls user code.
501+func (d *Display) prepareFrame(f receivedFrame) (Object, *Event, uint8, error) {
502+ if d.closed.Load() {
503+ return nil, nil, 0, net.ErrClosed
504+ }
451505 if f.object == displayObjectID {
452506 if len(d.pendingFDs) != 0 && len(d.rbuf) == 0 {
453- return &ProtocolError{Kind: "extra_fd", Err: ErrMalformedFrame}
507+ return nil, nil, 0, &ProtocolError{Kind: "extra_fd", Err: ErrMalformedFrame}
454508 }
455- return d.handleDisplayEvent(f.opcode, f.body)
509+ return nil, nil, 1, nil
456510 }
457511 if f.object == d.registry.id {
458512 var sig string
@@ -462,20 +516,19 @@ func (d *Display) dispatchFrame(f receivedFrame) error {
462516 case 1:
463517 sig = "uint,"
464518 default:
465- return &ProtocolError{Kind: "unknown_opcode", Object: f.object, Opcode: f.opcode, Err: ErrUnknownOpcode}
519+ return nil, nil, 0, &ProtocolError{Kind: "unknown_opcode", Object: f.object, Opcode: f.opcode, Err: ErrUnknownOpcode}
466520 }
467521 if err := validateEventBody(sig, f.body); err != nil {
468- return &ProtocolError{Kind: "malformed_payload", Object: f.object, Opcode: f.opcode, Err: err}
522+ return nil, nil, 0, &ProtocolError{Kind: "malformed_payload", Object: f.object, Opcode: f.opcode, Err: err}
469523 }
470524 if len(d.pendingFDs) != 0 && len(d.rbuf) == 0 {
471- return &ProtocolError{Kind: "extra_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}
525+ return nil, nil, 0, &ProtocolError{Kind: "extra_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}
472526 }
473- d.notifyListeners(f.object, f.opcode, f.body)
474- return nil
527+ return nil, nil, 2, nil
475528 }
476529 obj, ok := d.objects.Load(f.object)
477530 if !ok {
478- return &ProtocolError{Kind: "unknown_object", Object: f.object, Opcode: f.opcode, Err: ErrUnknownObject}
531+ return nil, nil, 0, &ProtocolError{Kind: "unknown_object", Object: f.object, Opcode: f.opcode, Err: ErrUnknownObject}
479532 }
480533 sig, valid := "", false
481534 p, isGenerated := obj.(signatureProvider)
@@ -489,14 +542,14 @@ func (d *Display) dispatchFrame(f receivedFrame) error {
489542 }
490543 }
491544 if !valid {
492- return &ProtocolError{Kind: "unknown_opcode", Object: f.object, Opcode: f.opcode, Err: ErrUnknownOpcode}
545+ return nil, nil, 0, &ProtocolError{Kind: "unknown_opcode", Object: f.object, Opcode: f.opcode, Err: ErrUnknownOpcode}
493546 }
494547 if err := validateEventBody(sig, f.body); err != nil {
495- return &ProtocolError{Kind: "malformed_payload", Object: f.object, Opcode: f.opcode, Err: err}
548+ return nil, nil, 0, &ProtocolError{Kind: "malformed_payload", Object: f.object, Opcode: f.opcode, Err: err}
496549 }
497550 count := strings.Count(sig, "fd,")
498551 if len(d.pendingFDs) < count {
499- return &ProtocolError{Kind: "missing_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}
552+ return nil, nil, 0, &ProtocolError{Kind: "missing_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}
500553 }
501554 ev := eventPool.Get().(*Event)
502555 *ev = Event{ProxyID: f.object, Opcode: f.opcode, data: f.body, display: d}
@@ -507,25 +560,17 @@ func (d *Display) dispatchFrame(f receivedFrame) error {
507560 }
508561 d.pendingFDs = d.pendingFDs[count:]
509562 }
510- defer func() {
563+ // FDs from a single recvmsg can accompany multiple coalesced frames. Once
564+ // all buffered bytes have been framed, surplus descriptors are not legal.
565+ if len(d.rbuf) == 0 && len(d.pendingFDs) != 0 {
511566 for _, fd := range ev.fds {
512567 _ = fd.Close()
513568 }
514569 *ev = Event{}
515570 eventPool.Put(ev)
516- }()
517- // FDs from a single recvmsg can accompany multiple coalesced frames. Once
518- // all buffered bytes have been framed, surplus descriptors are not legal.
519- if len(d.rbuf) == 0 && len(d.pendingFDs) != 0 {
520- return &ProtocolError{Kind: "extra_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}
571+ return nil, nil, 0, &ProtocolError{Kind: "extra_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}
521572 }
522- if proxy, ok := obj.(Proxy); ok {
523- proxy.Dispatch(ev)
524- } else {
525- d.dispatcher.Dispatch(f.object, f.opcode, f.body)
526- d.notifyListeners(f.object, f.opcode, f.body)
527- }
528- return nil
573+ return obj.(Object), ev, 0, nil
529574 }
530575
531576 type signatureKey struct {
@@ -585,8 +630,18 @@ func (d *Display) handleDisplayEvent(opcode uint16, data []byte) error {
585630 return &ProtocolError{Kind: "short_delete_id", Object: displayObjectID, Opcode: opcode, Size: uint32(len(data)) + HeaderSize, Err: ErrMalformedFrame}
586631 }
587632 id := binary.LittleEndian.Uint32(data[0:4])
588- d.objects.Delete(id)
589- d.context.proxies.Delete(id)
633+ d.idMu.Lock()
634+ _, live := d.objects.Load(id)
635+ if id < 2 || id >= 0xff000000 || id >= d.nextID || live || d.retiredIDs[id] {
636+ d.idMu.Unlock()
637+ return &ProtocolError{Kind: "invalid_delete_id", Object: id, Err: ErrMalformedFrame}
638+ }
639+ if d.retiredIDs == nil {
640+ d.retiredIDs = make(map[uint32]bool)
641+ }
642+ d.retiredIDs[id] = true
643+ d.freeIDs = append(d.freeIDs, id)
644+ d.idMu.Unlock()
590645 return nil
591646
592647 default:
@@ -49,8 +49,12 @@ type Display struct {
49 unix *net.UnixConn // set when conn is a Unix socket, nil otherwise49 unix *net.UnixConn // set when conn is a Unix socket, nil otherwise
50 objects sync.Map // map[uint32]Object50 objects sync.Map // map[uint32]Object
51 nextID uint3251 nextID uint32
52+ idMu sync.Mutex
53+ freeIDs []uint32
54+ retiredIDs map[uint32]bool
52 sendMu sync.Mutex55 sendMu sync.Mutex
53 recvMu sync.Mutex56 recvMu sync.Mutex
57+ dispatchMu sync.Mutex // serialize readers without holding receive state across handlers
54 signatures sync.Map58 signatures sync.Map
55 listenerMu sync.Mutex59 listenerMu sync.Mutex
56 listeners sync.Map // map[uint32]map[uint16][]func([]byte)60 listeners sync.Map // map[uint32]map[uint16][]func([]byte)
@@ -192,8 +196,6 @@ func (d *Display) Close() error {
192 }196 }
193 d.context.closed.Store(true)197 d.context.closed.Store(true)
194 err := d.conn.Close()198 err := d.conn.Close()
195- d.sendMu.Lock()
196- d.sendMu.Unlock()
197 d.recvMu.Lock()199 d.recvMu.Lock()
198 d.closePendingFDs()200 d.closePendingFDs()
199 d.recvMu.Unlock()201 d.recvMu.Unlock()
@@ -217,7 +219,20 @@ func (d *Display) RegisterEventHandler(objectID uint32, opcode uint16, handler E
217 219
218 // allocateID allocates a new object ID220 // allocateID allocates a new object ID
219 func (d *Display) allocateID() uint32 {221 func (d *Display) allocateID() uint32 {
220- return atomic.AddUint32(&d.nextID, 1) - 1222+ 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
221 }236 }
222 237
223 // AllocateID allocates a new object ID (public method)238 // AllocateID allocates a new object ID (public method)
@@ -338,14 +353,17 @@ func (d *Display) marshalArg(buf *bytes.Buffer, arg interface{}) error {
338 // Dispatch returns net.ErrClosed after Close, a typed ProtocolError for a353 // Dispatch returns net.ErrClosed after Close, a typed ProtocolError for a
339 // malformed frame, and io.ErrUnexpectedEOF when the peer closes mid-frame.354 // malformed frame, and io.ErrUnexpectedEOF when the peer closes mid-frame.
340 func (d *Display) Dispatch() error {355 func (d *Display) Dispatch() error {
356+ d.dispatchMu.Lock()
357+ defer d.dispatchMu.Unlock()
341 d.recvMu.Lock()358 d.recvMu.Lock()
342- defer d.recvMu.Unlock()
343 359
344 if d.closed.Load() {360 if d.closed.Load() {
361+ d.recvMu.Unlock()
345 return net.ErrClosed362 return net.ErrClosed
346 }363 }
347 364
348 frame, err := d.nextFrame()365 frame, err := d.nextFrame()
366+ d.recvMu.Unlock()
349 if d.closed.Load() {367 if d.closed.Load() {
350 return net.ErrClosed368 return net.ErrClosed
351 }369 }
@@ -362,6 +380,8 @@ func (d *Display) fail(err error) error {
362 if err == nil {380 if err == nil {
363 return nil381 return nil
364 }382 }
383+ d.recvMu.Lock()
384+ defer d.recvMu.Unlock()
365 var perr *ProtocolError385 var perr *ProtocolError
366 if errors.As(err, &perr) {386 if errors.As(err, &perr) {
367 d.closePendingFDs()387 d.closePendingFDs()
@@ -448,11 +468,45 @@ type signatureProvider interface{ EventSignature(uint16) (string, bool) }
448 468
449 // dispatchFrame delivers one complete frame with precisely its signature's FDs.469 // dispatchFrame delivers one complete frame with precisely its signature's FDs.
450 func (d *Display) dispatchFrame(f receivedFrame) error {470 func (d *Display) dispatchFrame(f receivedFrame) error {
471+ d.recvMu.Lock()
472+ obj, ev, kind, err := d.prepareFrame(f)
473+ d.recvMu.Unlock()
474+ if err != nil {
475+ return err
476+ }
477+ switch kind {
478+ case 1:
479+ return d.handleDisplayEvent(f.opcode, f.body)
480+ case 2:
481+ d.notifyListeners(f.object, f.opcode, f.body)
482+ return nil
483+ }
484+ defer func() {
485+ for _, fd := range ev.fds {
486+ _ = fd.Close()
487+ }
488+ *ev = Event{}
489+ eventPool.Put(ev)
490+ }()
491+ if proxy, ok := obj.(Proxy); ok {
492+ proxy.Dispatch(ev)
493+ } else {
494+ d.dispatcher.Dispatch(f.object, f.opcode, f.body)
495+ d.notifyListeners(f.object, f.opcode, f.body)
496+ }
497+ return nil
498+}
499+
500+// prepareFrame assigns owned descriptors under recvMu, but never calls user code.
501+func (d *Display) prepareFrame(f receivedFrame) (Object, *Event, uint8, error) {
502+ if d.closed.Load() {
503+ return nil, nil, 0, net.ErrClosed
504+ }
451 if f.object == displayObjectID {505 if f.object == displayObjectID {
452 if len(d.pendingFDs) != 0 && len(d.rbuf) == 0 {506 if len(d.pendingFDs) != 0 && len(d.rbuf) == 0 {
453- return &ProtocolError{Kind: "extra_fd", Err: ErrMalformedFrame}507+ return nil, nil, 0, &ProtocolError{Kind: "extra_fd", Err: ErrMalformedFrame}
454 }508 }
455- return d.handleDisplayEvent(f.opcode, f.body)509+ return nil, nil, 1, nil
456 }510 }
457 if f.object == d.registry.id {511 if f.object == d.registry.id {
458 var sig string512 var sig string
@@ -462,20 +516,19 @@ func (d *Display) dispatchFrame(f receivedFrame) error {
462 case 1:516 case 1:
463 sig = "uint,"517 sig = "uint,"
464 default:518 default:
465- return &ProtocolError{Kind: "unknown_opcode", Object: f.object, Opcode: f.opcode, Err: ErrUnknownOpcode}519+ return nil, nil, 0, &ProtocolError{Kind: "unknown_opcode", Object: f.object, Opcode: f.opcode, Err: ErrUnknownOpcode}
466 }520 }
467 if err := validateEventBody(sig, f.body); err != nil {521 if err := validateEventBody(sig, f.body); err != nil {
468- return &ProtocolError{Kind: "malformed_payload", Object: f.object, Opcode: f.opcode, Err: err}522+ return nil, nil, 0, &ProtocolError{Kind: "malformed_payload", Object: f.object, Opcode: f.opcode, Err: err}
469 }523 }
470 if len(d.pendingFDs) != 0 && len(d.rbuf) == 0 {524 if len(d.pendingFDs) != 0 && len(d.rbuf) == 0 {
471- return &ProtocolError{Kind: "extra_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}525+ return nil, nil, 0, &ProtocolError{Kind: "extra_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}
472 }526 }
473- d.notifyListeners(f.object, f.opcode, f.body)527+ return nil, nil, 2, nil
474- return nil
475 }528 }
476 obj, ok := d.objects.Load(f.object)529 obj, ok := d.objects.Load(f.object)
477 if !ok {530 if !ok {
478- return &ProtocolError{Kind: "unknown_object", Object: f.object, Opcode: f.opcode, Err: ErrUnknownObject}531+ return nil, nil, 0, &ProtocolError{Kind: "unknown_object", Object: f.object, Opcode: f.opcode, Err: ErrUnknownObject}
479 }532 }
480 sig, valid := "", false533 sig, valid := "", false
481 p, isGenerated := obj.(signatureProvider)534 p, isGenerated := obj.(signatureProvider)
@@ -489,14 +542,14 @@ func (d *Display) dispatchFrame(f receivedFrame) error {
489 }542 }
490 }543 }
491 if !valid {544 if !valid {
492- return &ProtocolError{Kind: "unknown_opcode", Object: f.object, Opcode: f.opcode, Err: ErrUnknownOpcode}545+ return nil, nil, 0, &ProtocolError{Kind: "unknown_opcode", Object: f.object, Opcode: f.opcode, Err: ErrUnknownOpcode}
493 }546 }
494 if err := validateEventBody(sig, f.body); err != nil {547 if err := validateEventBody(sig, f.body); err != nil {
495- return &ProtocolError{Kind: "malformed_payload", Object: f.object, Opcode: f.opcode, Err: err}548+ return nil, nil, 0, &ProtocolError{Kind: "malformed_payload", Object: f.object, Opcode: f.opcode, Err: err}
496 }549 }
497 count := strings.Count(sig, "fd,")550 count := strings.Count(sig, "fd,")
498 if len(d.pendingFDs) < count {551 if len(d.pendingFDs) < count {
499- return &ProtocolError{Kind: "missing_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}552+ return nil, nil, 0, &ProtocolError{Kind: "missing_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}
500 }553 }
501 ev := eventPool.Get().(*Event)554 ev := eventPool.Get().(*Event)
502 *ev = Event{ProxyID: f.object, Opcode: f.opcode, data: f.body, display: d}555 *ev = Event{ProxyID: f.object, Opcode: f.opcode, data: f.body, display: d}
@@ -507,25 +560,17 @@ func (d *Display) dispatchFrame(f receivedFrame) error {
507 }560 }
508 d.pendingFDs = d.pendingFDs[count:]561 d.pendingFDs = d.pendingFDs[count:]
509 }562 }
510- defer func() {563+ // FDs from a single recvmsg can accompany multiple coalesced frames. Once
564+ // all buffered bytes have been framed, surplus descriptors are not legal.
565+ if len(d.rbuf) == 0 && len(d.pendingFDs) != 0 {
511 for _, fd := range ev.fds {566 for _, fd := range ev.fds {
512 _ = fd.Close()567 _ = fd.Close()
513 }568 }
514 *ev = Event{}569 *ev = Event{}
515 eventPool.Put(ev)570 eventPool.Put(ev)
516- }()571+ return nil, nil, 0, &ProtocolError{Kind: "extra_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}
517- // FDs from a single recvmsg can accompany multiple coalesced frames. Once
518- // all buffered bytes have been framed, surplus descriptors are not legal.
519- if len(d.rbuf) == 0 && len(d.pendingFDs) != 0 {
520- return &ProtocolError{Kind: "extra_fd", Object: f.object, Opcode: f.opcode, Err: ErrMalformedFrame}
521 }572 }
522- if proxy, ok := obj.(Proxy); ok {573+ return obj.(Object), ev, 0, nil
523- proxy.Dispatch(ev)
524- } else {
525- d.dispatcher.Dispatch(f.object, f.opcode, f.body)
526- d.notifyListeners(f.object, f.opcode, f.body)
527- }
528- return nil
529 }574 }
530 575
531 type signatureKey struct {576 type signatureKey struct {
@@ -585,8 +630,18 @@ func (d *Display) handleDisplayEvent(opcode uint16, data []byte) error {
585 return &ProtocolError{Kind: "short_delete_id", Object: displayObjectID, Opcode: opcode, Size: uint32(len(data)) + HeaderSize, Err: ErrMalformedFrame}630 return &ProtocolError{Kind: "short_delete_id", Object: displayObjectID, Opcode: opcode, Size: uint32(len(data)) + HeaderSize, Err: ErrMalformedFrame}
586 }631 }
587 id := binary.LittleEndian.Uint32(data[0:4])632 id := binary.LittleEndian.Uint32(data[0:4])
588- d.objects.Delete(id)633+ d.idMu.Lock()
589- d.context.proxies.Delete(id)634+ _, live := d.objects.Load(id)
635+ if id < 2 || id >= 0xff000000 || id >= d.nextID || live || d.retiredIDs[id] {
636+ d.idMu.Unlock()
637+ return &ProtocolError{Kind: "invalid_delete_id", Object: id, Err: ErrMalformedFrame}
638+ }
639+ if d.retiredIDs == nil {
640+ d.retiredIDs = make(map[uint32]bool)
641+ }
642+ d.retiredIDs[id] = true
643+ d.freeIDs = append(d.freeIDs, id)
644+ d.idMu.Unlock()
590 return nil645 return nil
591 646
592 default:647 default:
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+}
@@ -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 +18 -3
@@ -60,18 +60,33 @@ func NewContext(display *Display) *Context {
6060
6161 // SendRequest sends a request through the context
6262 func (c *Context) SendRequest(proxy Proxy, opcode uint32, args ...interface{}) error {
63- if c.closed.Load() {
64- return errors.New("context is closed")
63+ if err := c.CheckProxy(proxy); err != nil {
64+ return err
6565 }
6666 return c.display.SendRequest(proxy.ID(), uint16(opcode), args...)
6767 }
6868
6969 // SendRequestWithFDs sends a request with file descriptors through the context
7070 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.SendRequestWithFDs(proxy.ID(), uint16(opcode), fds, args...)
75+}
76+
77+// CheckProxy rejects stale and foreign proxies before any bytes are written.
78+func (c *Context) CheckProxy(proxy Proxy) error {
7179 if c.closed.Load() {
7280 return errors.New("context is closed")
7381 }
74- 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
7590 }
7691
7792 // Register registers a proxy object
@@ -60,18 +60,33 @@ func NewContext(display *Display) *Context {
60 60
61 // SendRequest sends a request through the context61 // SendRequest sends a request through the context
62 func (c *Context) SendRequest(proxy Proxy, opcode uint32, args ...interface{}) error {62 func (c *Context) SendRequest(proxy Proxy, opcode uint32, args ...interface{}) error {
63- if c.closed.Load() {63+ if err := c.CheckProxy(proxy); err != nil {
64- return errors.New("context is closed")64+ return err
65 }65 }
66 return c.display.SendRequest(proxy.ID(), uint16(opcode), args...)66 return c.display.SendRequest(proxy.ID(), uint16(opcode), args...)
67 }67 }
68 68
69 // SendRequestWithFDs sends a request with file descriptors through the context69 // SendRequestWithFDs sends a request with file descriptors through the context
70 func (c *Context) SendRequestWithFDs(proxy Proxy, opcode uint32, fds []int, args ...interface{}) error {70 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.SendRequestWithFDs(proxy.ID(), uint16(opcode), fds, args...)
75+}
76+
77+// CheckProxy rejects stale and foreign proxies before any bytes are written.
78+func (c *Context) CheckProxy(proxy Proxy) error {
71 if c.closed.Load() {79 if c.closed.Load() {
72 return errors.New("context is closed")80 return errors.New("context is closed")
73 }81 }
74- 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
75 }90 }
76 91
77 // Register registers a proxy object92 // Register registers a proxy object
M fd_test.go +69 -0
@@ -456,3 +456,72 @@ func TestDisplayFD_ParseFailureClosesQueuedDescriptor(t *testing.T) {
456456 t.Fatalf("read after malformed frame = %v, want %v", err, io.EOF)
457457 }
458458 }
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+}
@@ -456,3 +456,72 @@ func TestDisplayFD_ParseFailureClosesQueuedDescriptor(t *testing.T) {
456 t.Fatalf("read after malformed frame = %v, want %v", err, io.EOF)456 t.Fatalf("read after malformed frame = %v, want %v", err, io.EOF)
457 }457 }
458 }458 }
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+}
M internal/scanner/scanner.go +7 -7
@@ -611,28 +611,28 @@ func (s *Scanner) goTypeForArg(arg Arg) (string, error) {
611611 case "fd":
612612 return "int", nil
613613 case "object", "new_id":
614- return s.goTypeForInterface(arg.Interface), nil
614+ return s.goTypeForInterface(arg.Interface)
615615 }
616616 return "", fmt.Errorf("unsupported argument type %q", arg.Type)
617617 }
618618
619-func (s *Scanner) goTypeForInterface(iface string) string {
619+func (s *Scanner) goTypeForInterface(iface string) (string, error) {
620620 if iface == "" {
621- return "wl.BaseProxy"
621+ return "wl.BaseProxy", nil
622622 }
623623 // The XML package owns its own definitions even when the name starts wl_.
624624 for _, local := range s.protocol.Interfaces {
625625 if local.Name == iface && iface != "wl_display" && iface != "wl_registry" {
626- return s.toGoName(iface)
626+ return s.toGoName(iface), nil
627627 }
628628 }
629629 if bootstrapTypes[s.toGoName(iface)] {
630- return "wl." + s.toGoName(iface)
630+ return "wl." + s.toGoName(iface), nil
631631 }
632632 if _, ok := s.CrossPackage[iface]; ok {
633- return "cross_" + strings.ReplaceAll(iface, "-", "_") + "." + s.toGoName(iface)
633+ return "cross_" + strings.ReplaceAll(iface, "-", "_") + "." + s.toGoName(iface), nil
634634 }
635- return "wl.BaseProxy"
635+ return "", fmt.Errorf("unresolved interface %q: add CrossPackage mapping", iface)
636636 }
637637
638638 // paramName turns a protocol argument name into a Go parameter name.
@@ -611,28 +611,28 @@ func (s *Scanner) goTypeForArg(arg Arg) (string, error) {
611 case "fd":611 case "fd":
612 return "int", nil612 return "int", nil
613 case "object", "new_id":613 case "object", "new_id":
614- return s.goTypeForInterface(arg.Interface), nil614+ return s.goTypeForInterface(arg.Interface)
615 }615 }
616 return "", fmt.Errorf("unsupported argument type %q", arg.Type)616 return "", fmt.Errorf("unsupported argument type %q", arg.Type)
617 }617 }
618 618
619-func (s *Scanner) goTypeForInterface(iface string) string {619+func (s *Scanner) goTypeForInterface(iface string) (string, error) {
620 if iface == "" {620 if iface == "" {
621- return "wl.BaseProxy"621+ return "wl.BaseProxy", nil
622 }622 }
623 // The XML package owns its own definitions even when the name starts wl_.623 // The XML package owns its own definitions even when the name starts wl_.
624 for _, local := range s.protocol.Interfaces {624 for _, local := range s.protocol.Interfaces {
625 if local.Name == iface && iface != "wl_display" && iface != "wl_registry" {625 if local.Name == iface && iface != "wl_display" && iface != "wl_registry" {
626- return s.toGoName(iface)626+ return s.toGoName(iface), nil
627 }627 }
628 }628 }
629 if bootstrapTypes[s.toGoName(iface)] {629 if bootstrapTypes[s.toGoName(iface)] {
630- return "wl." + s.toGoName(iface)630+ return "wl." + s.toGoName(iface), nil
631 }631 }
632 if _, ok := s.CrossPackage[iface]; ok {632 if _, ok := s.CrossPackage[iface]; ok {
633- return "cross_" + strings.ReplaceAll(iface, "-", "_") + "." + s.toGoName(iface)633+ return "cross_" + strings.ReplaceAll(iface, "-", "_") + "." + s.toGoName(iface), nil
634 }634 }
635- return "wl.BaseProxy"635+ return "", fmt.Errorf("unresolved interface %q: add CrossPackage mapping", iface)
636 }636 }
637 637
638 // paramName turns a protocol argument name into a Go parameter name.638 // paramName turns a protocol argument name into a Go parameter name.
M internal/scanner/scanner_test.go +88 -0
@@ -2,6 +2,7 @@ package scanner
22
33 import (
44 "os"
5+ "os/exec"
56 "path/filepath"
67 "strings"
78 "testing"
@@ -73,3 +74,90 @@ func TestExternalChildFactory(t *testing.T) {
7374 t.Fatal("foreign child was constructed without its package factory")
7475 }
7576 }
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+}
@@ -2,6 +2,7 @@ package scanner
2 2
3 import (3 import (
4 "os"4 "os"
5+ "os/exec"
5 "path/filepath"6 "path/filepath"
6 "strings"7 "strings"
7 "testing"8 "testing"
@@ -73,3 +74,90 @@ func TestExternalChildFactory(t *testing.T) {
73 t.Fatal("foreign child was constructed without its package factory")74 t.Fatal("foreign child was constructed without its package factory")
74 }75 }
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+}
M internal/scanner/template.go +1 -0
@@ -50,6 +50,7 @@ func New{{$iface.GoName}}(ctx *wl.Context) *{{$iface.GoName}} {
5050 // {{.Doc}}
5151 {{- end}}
5252 func (o *{{$iface.GoName}}) {{.GoName}}({{.Params}}) {{.Results}} {
53+if err := o.Context().CheckProxy(o); err != nil { return {{.ErrorReturn}} }
5354 {{- range .ArgPreparations}}
5455 {{.}}
5556 {{- end}}
@@ -50,6 +50,7 @@ func New{{$iface.GoName}}(ctx *wl.Context) *{{$iface.GoName}} {
50 // {{.Doc}}50 // {{.Doc}}
51 {{- end}}51 {{- end}}
52 func (o *{{$iface.GoName}}) {{.GoName}}({{.Params}}) {{.Results}} {52 func (o *{{$iface.GoName}}) {{.GoName}}({{.Params}}) {{.Results}} {
53+if err := o.Context().CheckProxy(o); err != nil { return {{.ErrorReturn}} }
53 {{- range .ArgPreparations}}54 {{- range .ArgPreparations}}
54 {{.}}55 {{.}}
55 {{- end}}56 {{- end}}
M protocol/core/core_test.go +32 -0
@@ -399,3 +399,35 @@ func TestUnclaimedFirstHandlerFDClosed(t *testing.T) {
399399 t.Fatalf("unclaimed FD not closed: %d %v", n, e)
400400 }
401401 }
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+}
@@ -399,3 +399,35 @@ func TestUnclaimedFirstHandlerFDClosed(t *testing.T) {
399 t.Fatalf("unclaimed FD not closed: %d %v", n, e)399 t.Fatalf("unclaimed FD not closed: %d %v", n, e)
400 }400 }
401 }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+}
M protocol/core/wayland_generated.go +207 -0
@@ -773,6 +773,9 @@ func NewCompositor(ctx *wl.Context) *Compositor {
773773
774774 // Ask the compositor to create a new surface.
775775 func (o *Compositor) CreateSurface() (*Surface, error) {
776+ if err := o.Context().CheckProxy(o); err != nil {
777+ return nil, err
778+ }
776779 child := &Surface{}
777780 child.SetContext(o.Context())
778781 child.SetID(o.Context().AllocateID())
@@ -786,6 +789,9 @@ func (o *Compositor) CreateSurface() (*Surface, error) {
786789
787790 // Ask the compositor to create a new region.
788791 func (o *Compositor) CreateRegion() (*Region, error) {
792+ if err := o.Context().CheckProxy(o); err != nil {
793+ return nil, err
794+ }
789795 child := &Region{}
790796 child.SetContext(o.Context())
791797 child.SetID(o.Context().AllocateID())
@@ -799,6 +805,9 @@ func (o *Compositor) CreateRegion() (*Region, error) {
799805
800806 // This request destroys the wl_compositor. This has no effect on any other objects.
801807 func (o *Compositor) Release() error {
808+ if err := o.Context().CheckProxy(o); err != nil {
809+ return err
810+ }
802811 if err := o.Context().SendRequest(o, 2); err != nil {
803812 return err
804813 }
@@ -839,6 +848,9 @@ func NewShmPool(ctx *wl.Context) *ShmPool {
839848
840849 // 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.
841850 func (o *ShmPool) CreateBuffer(offset int32, width int32, height int32, stride int32, format uint32) (*Buffer, error) {
851+ if err := o.Context().CheckProxy(o); err != nil {
852+ return nil, err
853+ }
842854 child := &Buffer{}
843855 child.SetContext(o.Context())
844856 child.SetID(o.Context().AllocateID())
@@ -852,6 +864,9 @@ func (o *ShmPool) CreateBuffer(offset int32, width int32, height int32, stride i
852864
853865 // Destroy the shared memory pool. The mmapped memory will be released when all buffers that have been created from this pool are gone.
854866 func (o *ShmPool) Destroy() error {
867+ if err := o.Context().CheckProxy(o); err != nil {
868+ return err
869+ }
855870 if err := o.Context().SendRequest(o, 1); err != nil {
856871 return err
857872 }
@@ -861,6 +876,9 @@ func (o *ShmPool) Destroy() error {
861876
862877 // 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.
863878 func (o *ShmPool) Resize(size int32) error {
879+ if err := o.Context().CheckProxy(o); err != nil {
880+ return err
881+ }
864882 if err := o.Context().SendRequest(o, 2, size); err != nil {
865883 return err
866884 }
@@ -904,6 +922,9 @@ func NewShm(ctx *wl.Context) *Shm {
904922
905923 // 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.
906924 func (o *Shm) CreatePool(fd int, size int32) (*ShmPool, error) {
925+ if err := o.Context().CheckProxy(o); err != nil {
926+ return nil, err
927+ }
907928 child := &ShmPool{}
908929 child.SetContext(o.Context())
909930 child.SetID(o.Context().AllocateID())
@@ -918,6 +939,9 @@ func (o *Shm) CreatePool(fd int, size int32) (*ShmPool, error) {
918939
919940 // 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.
920941 func (o *Shm) Release() error {
942+ if err := o.Context().CheckProxy(o); err != nil {
943+ return err
944+ }
921945 if err := o.Context().SendRequest(o, 1); err != nil {
922946 return err
923947 }
@@ -993,6 +1017,9 @@ func NewBuffer(ctx *wl.Context) *Buffer {
9931017
9941018 // 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.
9951019 func (o *Buffer) Destroy() error {
1020+ if err := o.Context().CheckProxy(o); err != nil {
1021+ return err
1022+ }
9961023 if err := o.Context().SendRequest(o, 0); err != nil {
9971024 return err
9981025 }
@@ -1069,6 +1096,9 @@ func NewDataOffer(ctx *wl.Context) *DataOffer {
10691096
10701097 // 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.
10711098 func (o *DataOffer) Accept(serial uint32, mimeType string) error {
1099+ if err := o.Context().CheckProxy(o); err != nil {
1100+ return err
1101+ }
10721102 if err := o.Context().SendRequest(o, 0, serial, mimeType); err != nil {
10731103 return err
10741104 }
@@ -1077,6 +1107,9 @@ func (o *DataOffer) Accept(serial uint32, mimeType string) error {
10771107
10781108 // 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.
10791109 func (o *DataOffer) Receive(mimeType string, fd int) error {
1110+ if err := o.Context().CheckProxy(o); err != nil {
1111+ return err
1112+ }
10801113 if err := o.Context().SendRequestWithFDs(o, 1, []int{fd}, mimeType, uintptr(fd)); err != nil {
10811114 return err
10821115 }
@@ -1086,6 +1119,9 @@ func (o *DataOffer) Receive(mimeType string, fd int) error {
10861119
10871120 // Destroy the data offer.
10881121 func (o *DataOffer) Destroy() error {
1122+ if err := o.Context().CheckProxy(o); err != nil {
1123+ return err
1124+ }
10891125 if err := o.Context().SendRequest(o, 2); err != nil {
10901126 return err
10911127 }
@@ -1095,6 +1131,9 @@ func (o *DataOffer) Destroy() error {
10951131
10961132 // 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.
10971133 func (o *DataOffer) Finish() error {
1134+ if err := o.Context().CheckProxy(o); err != nil {
1135+ return err
1136+ }
10981137 if err := o.Context().SendRequest(o, 3); err != nil {
10991138 return err
11001139 }
@@ -1103,6 +1142,9 @@ func (o *DataOffer) Finish() error {
11031142
11041143 // 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.
11051144 func (o *DataOffer) SetActions(dndActions uint32, preferredAction uint32) error {
1145+ if err := o.Context().CheckProxy(o); err != nil {
1146+ return err
1147+ }
11061148 if err := o.Context().SendRequest(o, 4, dndActions, preferredAction); err != nil {
11071149 return err
11081150 }
@@ -1244,6 +1286,9 @@ func NewDataSource(ctx *wl.Context) *DataSource {
12441286
12451287 // This request adds a mime type to the set of mime types advertised to targets. Can be called several times to offer multiple types.
12461288 func (o *DataSource) Offer(mimeType string) error {
1289+ if err := o.Context().CheckProxy(o); err != nil {
1290+ return err
1291+ }
12471292 if err := o.Context().SendRequest(o, 0, mimeType); err != nil {
12481293 return err
12491294 }
@@ -1252,6 +1297,9 @@ func (o *DataSource) Offer(mimeType string) error {
12521297
12531298 // Destroy the data source.
12541299 func (o *DataSource) Destroy() error {
1300+ if err := o.Context().CheckProxy(o); err != nil {
1301+ return err
1302+ }
12551303 if err := o.Context().SendRequest(o, 1); err != nil {
12561304 return err
12571305 }
@@ -1261,6 +1309,9 @@ func (o *DataSource) Destroy() error {
12611309
12621310 // 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.
12631311 func (o *DataSource) SetActions(dndActions uint32) error {
1312+ if err := o.Context().CheckProxy(o); err != nil {
1313+ return err
1314+ }
12641315 if err := o.Context().SendRequest(o, 2, dndActions); err != nil {
12651316 return err
12661317 }
@@ -1497,6 +1548,9 @@ func NewDataDevice(ctx *wl.Context) *DataDevice {
14971548
14981549 // 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.
14991550 func (o *DataDevice) StartDrag(source *DataSource, origin *Surface, icon *Surface, serial uint32) error {
1551+ if err := o.Context().CheckProxy(o); err != nil {
1552+ return err
1553+ }
15001554 var arg0 wl.Object
15011555 if source != nil {
15021556 arg0 = source
@@ -1517,6 +1571,9 @@ func (o *DataDevice) StartDrag(source *DataSource, origin *Surface, icon *Surfac
15171571
15181572 // 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.
15191573 func (o *DataDevice) SetSelection(source *DataSource, serial uint32) error {
1574+ if err := o.Context().CheckProxy(o); err != nil {
1575+ return err
1576+ }
15201577 var arg0 wl.Object
15211578 if source != nil {
15221579 arg0 = source
@@ -1529,6 +1586,9 @@ func (o *DataDevice) SetSelection(source *DataSource, serial uint32) error {
15291586
15301587 // This request destroys the data device.
15311588 func (o *DataDevice) Release() error {
1589+ if err := o.Context().CheckProxy(o); err != nil {
1590+ return err
1591+ }
15321592 if err := o.Context().SendRequest(o, 2); err != nil {
15331593 return err
15341594 }
@@ -1763,6 +1823,9 @@ func NewDataDeviceManager(ctx *wl.Context) *DataDeviceManager {
17631823
17641824 // Create a new data source.
17651825 func (o *DataDeviceManager) CreateDataSource() (*DataSource, error) {
1826+ if err := o.Context().CheckProxy(o); err != nil {
1827+ return nil, err
1828+ }
17661829 child := &DataSource{}
17671830 child.SetContext(o.Context())
17681831 child.SetID(o.Context().AllocateID())
@@ -1776,6 +1839,9 @@ func (o *DataDeviceManager) CreateDataSource() (*DataSource, error) {
17761839
17771840 // Create a new data device for a given seat.
17781841 func (o *DataDeviceManager) GetDataDevice(seat *Seat) (*DataDevice, error) {
1842+ if err := o.Context().CheckProxy(o); err != nil {
1843+ return nil, err
1844+ }
17791845 var arg1 wl.Object
17801846 if seat != nil {
17811847 arg1 = seat
@@ -1793,6 +1859,9 @@ func (o *DataDeviceManager) GetDataDevice(seat *Seat) (*DataDevice, error) {
17931859
17941860 // This request destroys the wl_data_device_manager. This has no effect on any other objects.
17951861 func (o *DataDeviceManager) Release() error {
1862+ if err := o.Context().CheckProxy(o); err != nil {
1863+ return err
1864+ }
17961865 if err := o.Context().SendRequest(o, 2); err != nil {
17971866 return err
17981867 }
@@ -1833,6 +1902,9 @@ func NewShell(ctx *wl.Context) *Shell {
18331902
18341903 // 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.
18351904 func (o *Shell) GetShellSurface(surface *Surface) (*ShellSurface, error) {
1905+ if err := o.Context().CheckProxy(o); err != nil {
1906+ return nil, err
1907+ }
18361908 var arg1 wl.Object
18371909 if surface != nil {
18381910 arg1 = surface
@@ -1887,6 +1959,9 @@ func NewShellSurface(ctx *wl.Context) *ShellSurface {
18871959
18881960 // A client must respond to a ping event with a pong request or the client may be deemed unresponsive.
18891961 func (o *ShellSurface) Pong(serial uint32) error {
1962+ if err := o.Context().CheckProxy(o); err != nil {
1963+ return err
1964+ }
18901965 if err := o.Context().SendRequest(o, 0, serial); err != nil {
18911966 return err
18921967 }
@@ -1895,6 +1970,9 @@ func (o *ShellSurface) Pong(serial uint32) error {
18951970
18961971 // 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).
18971972 func (o *ShellSurface) Move(seat *Seat, serial uint32) error {
1973+ if err := o.Context().CheckProxy(o); err != nil {
1974+ return err
1975+ }
18981976 var arg0 wl.Object
18991977 if seat != nil {
19001978 arg0 = seat
@@ -1907,6 +1985,9 @@ func (o *ShellSurface) Move(seat *Seat, serial uint32) error {
19071985
19081986 // 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).
19091987 func (o *ShellSurface) Resize(seat *Seat, serial uint32, edges uint32) error {
1988+ if err := o.Context().CheckProxy(o); err != nil {
1989+ return err
1990+ }
19101991 var arg0 wl.Object
19111992 if seat != nil {
19121993 arg0 = seat
@@ -1919,6 +2000,9 @@ func (o *ShellSurface) Resize(seat *Seat, serial uint32, edges uint32) error {
19192000
19202001 // Map the surface as a toplevel surface. A toplevel surface is not fullscreen, maximized or transient.
19212002 func (o *ShellSurface) SetToplevel() error {
2003+ if err := o.Context().CheckProxy(o); err != nil {
2004+ return err
2005+ }
19222006 if err := o.Context().SendRequest(o, 3); err != nil {
19232007 return err
19242008 }
@@ -1927,6 +2011,9 @@ func (o *ShellSurface) SetToplevel() error {
19272011
19282012 // 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.
19292013 func (o *ShellSurface) SetTransient(parent *Surface, x int32, y int32, flags uint32) error {
2014+ if err := o.Context().CheckProxy(o); err != nil {
2015+ return err
2016+ }
19302017 var arg0 wl.Object
19312018 if parent != nil {
19322019 arg0 = parent
@@ -1939,6 +2026,9 @@ func (o *ShellSurface) SetTransient(parent *Surface, x int32, y int32, flags uin
19392026
19402027 // 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.
19412028 func (o *ShellSurface) SetFullscreen(method uint32, framerate uint32, output *Output) error {
2029+ if err := o.Context().CheckProxy(o); err != nil {
2030+ return err
2031+ }
19422032 var arg2 wl.Object
19432033 if output != nil {
19442034 arg2 = output
@@ -1951,6 +2041,9 @@ func (o *ShellSurface) SetFullscreen(method uint32, framerate uint32, output *Ou
19512041
19522042 // 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.
19532043 func (o *ShellSurface) SetPopup(seat *Seat, serial uint32, parent *Surface, x int32, y int32, flags uint32) error {
2044+ if err := o.Context().CheckProxy(o); err != nil {
2045+ return err
2046+ }
19542047 var arg0 wl.Object
19552048 if seat != nil {
19562049 arg0 = seat
@@ -1967,6 +2060,9 @@ func (o *ShellSurface) SetPopup(seat *Seat, serial uint32, parent *Surface, x in
19672060
19682061 // 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.
19692062 func (o *ShellSurface) SetMaximized(output *Output) error {
2063+ if err := o.Context().CheckProxy(o); err != nil {
2064+ return err
2065+ }
19702066 var arg0 wl.Object
19712067 if output != nil {
19722068 arg0 = output
@@ -1979,6 +2075,9 @@ func (o *ShellSurface) SetMaximized(output *Output) error {
19792075
19802076 // 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.
19812077 func (o *ShellSurface) SetTitle(title string) error {
2078+ if err := o.Context().CheckProxy(o); err != nil {
2079+ return err
2080+ }
19822081 if err := o.Context().SendRequest(o, 8, title); err != nil {
19832082 return err
19842083 }
@@ -1987,6 +2086,9 @@ func (o *ShellSurface) SetTitle(title string) error {
19872086
19882087 // 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.
19892088 func (o *ShellSurface) SetClass(class string) error {
2089+ if err := o.Context().CheckProxy(o); err != nil {
2090+ return err
2091+ }
19902092 if err := o.Context().SendRequest(o, 9, class); err != nil {
19912093 return err
19922094 }
@@ -2127,6 +2229,9 @@ func NewSurface(ctx *wl.Context) *Surface {
21272229
21282230 // Deletes the surface and invalidates its object ID.
21292231 func (o *Surface) Destroy() error {
2232+ if err := o.Context().CheckProxy(o); err != nil {
2233+ return err
2234+ }
21302235 if err := o.Context().SendRequest(o, 0); err != nil {
21312236 return err
21322237 }
@@ -2136,6 +2241,9 @@ func (o *Surface) Destroy() error {
21362241
21372242 // 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.
21382243 func (o *Surface) Attach(buffer *Buffer, x int32, y int32) error {
2244+ if err := o.Context().CheckProxy(o); err != nil {
2245+ return err
2246+ }
21392247 var arg0 wl.Object
21402248 if buffer != nil {
21412249 arg0 = buffer
@@ -2148,6 +2256,9 @@ func (o *Surface) Attach(buffer *Buffer, x int32, y int32) error {
21482256
21492257 // 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.
21502258 func (o *Surface) Damage(x int32, y int32, width int32, height int32) error {
2259+ if err := o.Context().CheckProxy(o); err != nil {
2260+ return err
2261+ }
21512262 if err := o.Context().SendRequest(o, 2, x, y, width, height); err != nil {
21522263 return err
21532264 }
@@ -2156,6 +2267,9 @@ func (o *Surface) Damage(x int32, y int32, width int32, height int32) error {
21562267
21572268 // 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.
21582269 func (o *Surface) Frame() (*Callback, error) {
2270+ if err := o.Context().CheckProxy(o); err != nil {
2271+ return nil, err
2272+ }
21592273 child := &Callback{}
21602274 child.SetContext(o.Context())
21612275 child.SetID(o.Context().AllocateID())
@@ -2169,6 +2283,9 @@ func (o *Surface) Frame() (*Callback, error) {
21692283
21702284 // 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.
21712285 func (o *Surface) SetOpaqueRegion(region *Region) error {
2286+ if err := o.Context().CheckProxy(o); err != nil {
2287+ return err
2288+ }
21722289 var arg0 wl.Object
21732290 if region != nil {
21742291 arg0 = region
@@ -2181,6 +2298,9 @@ func (o *Surface) SetOpaqueRegion(region *Region) error {
21812298
21822299 // 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.
21832300 func (o *Surface) SetInputRegion(region *Region) error {
2301+ if err := o.Context().CheckProxy(o); err != nil {
2302+ return err
2303+ }
21842304 var arg0 wl.Object
21852305 if region != nil {
21862306 arg0 = region
@@ -2193,6 +2313,9 @@ func (o *Surface) SetInputRegion(region *Region) error {
21932313
21942314 // 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.
21952315 func (o *Surface) Commit() error {
2316+ if err := o.Context().CheckProxy(o); err != nil {
2317+ return err
2318+ }
21962319 if err := o.Context().SendRequest(o, 6); err != nil {
21972320 return err
21982321 }
@@ -2201,6 +2324,9 @@ func (o *Surface) Commit() error {
22012324
22022325 // 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.
22032326 func (o *Surface) SetBufferTransform(transform int32) error {
2327+ if err := o.Context().CheckProxy(o); err != nil {
2328+ return err
2329+ }
22042330 if err := o.Context().SendRequest(o, 7, transform); err != nil {
22052331 return err
22062332 }
@@ -2209,6 +2335,9 @@ func (o *Surface) SetBufferTransform(transform int32) error {
22092335
22102336 // 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.
22112337 func (o *Surface) SetBufferScale(scale int32) error {
2338+ if err := o.Context().CheckProxy(o); err != nil {
2339+ return err
2340+ }
22122341 if err := o.Context().SendRequest(o, 8, scale); err != nil {
22132342 return err
22142343 }
@@ -2217,6 +2346,9 @@ func (o *Surface) SetBufferScale(scale int32) error {
22172346
22182347 // 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.
22192348 func (o *Surface) DamageBuffer(x int32, y int32, width int32, height int32) error {
2349+ if err := o.Context().CheckProxy(o); err != nil {
2350+ return err
2351+ }
22202352 if err := o.Context().SendRequest(o, 9, x, y, width, height); err != nil {
22212353 return err
22222354 }
@@ -2225,6 +2357,9 @@ func (o *Surface) DamageBuffer(x int32, y int32, width int32, height int32) erro
22252357
22262358 // 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.
22272359 func (o *Surface) Offset(x int32, y int32) error {
2360+ if err := o.Context().CheckProxy(o); err != nil {
2361+ return err
2362+ }
22282363 if err := o.Context().SendRequest(o, 10, x, y); err != nil {
22292364 return err
22302365 }
@@ -2233,6 +2368,9 @@ func (o *Surface) Offset(x int32, y int32) error {
22332368
22342369 // 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.
22352370 func (o *Surface) GetRelease() (*Callback, error) {
2371+ if err := o.Context().CheckProxy(o); err != nil {
2372+ return nil, err
2373+ }
22362374 child := &Callback{}
22372375 child.SetContext(o.Context())
22382376 child.SetID(o.Context().AllocateID())
@@ -2406,6 +2544,9 @@ func NewSeat(ctx *wl.Context) *Seat {
24062544
24072545 // 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.
24082546 func (o *Seat) GetPointer() (*Pointer, error) {
2547+ if err := o.Context().CheckProxy(o); err != nil {
2548+ return nil, err
2549+ }
24092550 child := &Pointer{}
24102551 child.SetContext(o.Context())
24112552 child.SetID(o.Context().AllocateID())
@@ -2419,6 +2560,9 @@ func (o *Seat) GetPointer() (*Pointer, error) {
24192560
24202561 // 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.
24212562 func (o *Seat) GetKeyboard() (*Keyboard, error) {
2563+ if err := o.Context().CheckProxy(o); err != nil {
2564+ return nil, err
2565+ }
24222566 child := &Keyboard{}
24232567 child.SetContext(o.Context())
24242568 child.SetID(o.Context().AllocateID())
@@ -2432,6 +2576,9 @@ func (o *Seat) GetKeyboard() (*Keyboard, error) {
24322576
24332577 // 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.
24342578 func (o *Seat) GetTouch() (*Touch, error) {
2579+ if err := o.Context().CheckProxy(o); err != nil {
2580+ return nil, err
2581+ }
24352582 child := &Touch{}
24362583 child.SetContext(o.Context())
24372584 child.SetID(o.Context().AllocateID())
@@ -2445,6 +2592,9 @@ func (o *Seat) GetTouch() (*Touch, error) {
24452592
24462593 // Using this request a client can tell the server that it is not going to use the seat object anymore.
24472594 func (o *Seat) Release() error {
2595+ if err := o.Context().CheckProxy(o); err != nil {
2596+ return err
2597+ }
24482598 if err := o.Context().SendRequest(o, 3); err != nil {
24492599 return err
24502600 }
@@ -2562,6 +2712,9 @@ func NewPointer(ctx *wl.Context) *Pointer {
25622712
25632713 // 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.
25642714 func (o *Pointer) SetCursor(serial uint32, surface *Surface, hotspotX int32, hotspotY int32) error {
2715+ if err := o.Context().CheckProxy(o); err != nil {
2716+ return err
2717+ }
25652718 var arg1 wl.Object
25662719 if surface != nil {
25672720 arg1 = surface
@@ -2574,6 +2727,9 @@ func (o *Pointer) SetCursor(serial uint32, surface *Surface, hotspotX int32, hot
25742727
25752728 // 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.
25762729 func (o *Pointer) Release() error {
2730+ if err := o.Context().CheckProxy(o); err != nil {
2731+ return err
2732+ }
25772733 if err := o.Context().SendRequest(o, 1); err != nil {
25782734 return err
25792735 }
@@ -3010,6 +3166,9 @@ func NewKeyboard(ctx *wl.Context) *Keyboard {
30103166
30113167 // release the keyboard object
30123168 func (o *Keyboard) Release() error {
3169+ if err := o.Context().CheckProxy(o); err != nil {
3170+ return err
3171+ }
30133172 if err := o.Context().SendRequest(o, 0); err != nil {
30143173 return err
30153174 }
@@ -3263,6 +3422,9 @@ func NewTouch(ctx *wl.Context) *Touch {
32633422
32643423 // release the touch object
32653424 func (o *Touch) Release() error {
3425+ if err := o.Context().CheckProxy(o); err != nil {
3426+ return err
3427+ }
32663428 if err := o.Context().SendRequest(o, 0); err != nil {
32673429 return err
32683430 }
@@ -3540,6 +3702,9 @@ func NewOutput(ctx *wl.Context) *Output {
35403702
35413703 // Using this request a client can tell the server that it is not going to use the output object anymore.
35423704 func (o *Output) Release() error {
3705+ if err := o.Context().CheckProxy(o); err != nil {
3706+ return err
3707+ }
35433708 if err := o.Context().SendRequest(o, 0); err != nil {
35443709 return err
35453710 }
@@ -3775,6 +3940,9 @@ func NewRegion(ctx *wl.Context) *Region {
37753940
37763941 // Destroy the region. This will invalidate the object ID.
37773942 func (o *Region) Destroy() error {
3943+ if err := o.Context().CheckProxy(o); err != nil {
3944+ return err
3945+ }
37783946 if err := o.Context().SendRequest(o, 0); err != nil {
37793947 return err
37803948 }
@@ -3784,6 +3952,9 @@ func (o *Region) Destroy() error {
37843952
37853953 // Add the specified rectangle to the region.
37863954 func (o *Region) Add(x int32, y int32, width int32, height int32) error {
3955+ if err := o.Context().CheckProxy(o); err != nil {
3956+ return err
3957+ }
37873958 if err := o.Context().SendRequest(o, 1, x, y, width, height); err != nil {
37883959 return err
37893960 }
@@ -3792,6 +3963,9 @@ func (o *Region) Add(x int32, y int32, width int32, height int32) error {
37923963
37933964 // Subtract the specified rectangle from the region.
37943965 func (o *Region) Subtract(x int32, y int32, width int32, height int32) error {
3966+ if err := o.Context().CheckProxy(o); err != nil {
3967+ return err
3968+ }
37953969 if err := o.Context().SendRequest(o, 2, x, y, width, height); err != nil {
37963970 return err
37973971 }
@@ -3831,6 +4005,9 @@ func NewSubcompositor(ctx *wl.Context) *Subcompositor {
38314005
38324006 // 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.
38334007 func (o *Subcompositor) Destroy() error {
4008+ if err := o.Context().CheckProxy(o); err != nil {
4009+ return err
4010+ }
38344011 if err := o.Context().SendRequest(o, 0); err != nil {
38354012 return err
38364013 }
@@ -3840,6 +4017,9 @@ func (o *Subcompositor) Destroy() error {
38404017
38414018 // 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.
38424019 func (o *Subcompositor) GetSubsurface(surface *Surface, parent *Surface) (*Subsurface, error) {
4020+ if err := o.Context().CheckProxy(o); err != nil {
4021+ return nil, err
4022+ }
38434023 var arg1 wl.Object
38444024 if surface != nil {
38454025 arg1 = surface
@@ -3892,6 +4072,9 @@ func NewSubsurface(ctx *wl.Context) *Subsurface {
38924072
38934073 // 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.
38944074 func (o *Subsurface) Destroy() error {
4075+ if err := o.Context().CheckProxy(o); err != nil {
4076+ return err
4077+ }
38954078 if err := o.Context().SendRequest(o, 0); err != nil {
38964079 return err
38974080 }
@@ -3901,6 +4084,9 @@ func (o *Subsurface) Destroy() error {
39014084
39024085 // 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.
39034086 func (o *Subsurface) SetPosition(x int32, y int32) error {
4087+ if err := o.Context().CheckProxy(o); err != nil {
4088+ return err
4089+ }
39044090 if err := o.Context().SendRequest(o, 1, x, y); err != nil {
39054091 return err
39064092 }
@@ -3909,6 +4095,9 @@ func (o *Subsurface) SetPosition(x int32, y int32) error {
39094095
39104096 // 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.
39114097 func (o *Subsurface) PlaceAbove(sibling *Surface) error {
4098+ if err := o.Context().CheckProxy(o); err != nil {
4099+ return err
4100+ }
39124101 var arg0 wl.Object
39134102 if sibling != nil {
39144103 arg0 = sibling
@@ -3921,6 +4110,9 @@ func (o *Subsurface) PlaceAbove(sibling *Surface) error {
39214110
39224111 // The sub-surface is placed just below the reference surface. See wl_subsurface.place_above.
39234112 func (o *Subsurface) PlaceBelow(sibling *Surface) error {
4113+ if err := o.Context().CheckProxy(o); err != nil {
4114+ return err
4115+ }
39244116 var arg0 wl.Object
39254117 if sibling != nil {
39264118 arg0 = sibling
@@ -3933,6 +4125,9 @@ func (o *Subsurface) PlaceBelow(sibling *Surface) error {
39334125
39344126 // Change the commit behaviour of the sub-surface to synchronized mode. See wl_subsurface and wl_surface.commit for more information.
39354127 func (o *Subsurface) SetSync() error {
4128+ if err := o.Context().CheckProxy(o); err != nil {
4129+ return err
4130+ }
39364131 if err := o.Context().SendRequest(o, 4); err != nil {
39374132 return err
39384133 }
@@ -3941,6 +4136,9 @@ func (o *Subsurface) SetSync() error {
39414136
39424137 // Change the commit behaviour of the sub-surface to desynchronized mode. See wl_subsurface and wl_surface.commit for more information.
39434138 func (o *Subsurface) SetDesync() error {
4139+ if err := o.Context().CheckProxy(o); err != nil {
4140+ return err
4141+ }
39444142 if err := o.Context().SendRequest(o, 5); err != nil {
39454143 return err
39464144 }
@@ -3980,6 +4178,9 @@ func NewFixes(ctx *wl.Context) *Fixes {
39804178
39814179 // destroys this object
39824180 func (o *Fixes) Destroy() error {
4181+ if err := o.Context().CheckProxy(o); err != nil {
4182+ return err
4183+ }
39834184 if err := o.Context().SendRequest(o, 0); err != nil {
39844185 return err
39854186 }
@@ -3989,6 +4190,9 @@ func (o *Fixes) Destroy() error {
39894190
39904191 // 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.
39914192 func (o *Fixes) DestroyRegistry(registry *wl.Registry) error {
4193+ if err := o.Context().CheckProxy(o); err != nil {
4194+ return err
4195+ }
39924196 var arg0 wl.Object
39934197 if registry != nil {
39944198 arg0 = registry
@@ -4001,6 +4205,9 @@ func (o *Fixes) DestroyRegistry(registry *wl.Registry) error {
40014205
40024206 // 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.
40034207 func (o *Fixes) AckGlobalRemove(registry *wl.Registry, name uint32) error {
4208+ if err := o.Context().CheckProxy(o); err != nil {
4209+ return err
4210+ }
40044211 var arg0 wl.Object
40054212 if registry != nil {
40064213 arg0 = registry
@@ -773,6 +773,9 @@ func NewCompositor(ctx *wl.Context) *Compositor {
773 773
774 // Ask the compositor to create a new surface.774 // Ask the compositor to create a new surface.
775 func (o *Compositor) CreateSurface() (*Surface, error) {775 func (o *Compositor) CreateSurface() (*Surface, error) {
776+ if err := o.Context().CheckProxy(o); err != nil {
777+ return nil, err
778+ }
776 child := &Surface{}779 child := &Surface{}
777 child.SetContext(o.Context())780 child.SetContext(o.Context())
778 child.SetID(o.Context().AllocateID())781 child.SetID(o.Context().AllocateID())
@@ -786,6 +789,9 @@ func (o *Compositor) CreateSurface() (*Surface, error) {
786 789
787 // Ask the compositor to create a new region.790 // Ask the compositor to create a new region.
788 func (o *Compositor) CreateRegion() (*Region, error) {791 func (o *Compositor) CreateRegion() (*Region, error) {
792+ if err := o.Context().CheckProxy(o); err != nil {
793+ return nil, err
794+ }
789 child := &Region{}795 child := &Region{}
790 child.SetContext(o.Context())796 child.SetContext(o.Context())
791 child.SetID(o.Context().AllocateID())797 child.SetID(o.Context().AllocateID())
@@ -799,6 +805,9 @@ func (o *Compositor) CreateRegion() (*Region, error) {
799 805
800 // This request destroys the wl_compositor. This has no effect on any other objects.806 // This request destroys the wl_compositor. This has no effect on any other objects.
801 func (o *Compositor) Release() error {807 func (o *Compositor) Release() error {
808+ if err := o.Context().CheckProxy(o); err != nil {
809+ return err
810+ }
802 if err := o.Context().SendRequest(o, 2); err != nil {811 if err := o.Context().SendRequest(o, 2); err != nil {
803 return err812 return err
804 }813 }
@@ -839,6 +848,9 @@ func NewShmPool(ctx *wl.Context) *ShmPool {
839 848
840 // 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 // 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.
841 func (o *ShmPool) CreateBuffer(offset int32, width int32, height int32, stride int32, format uint32) (*Buffer, error) {850 func (o *ShmPool) CreateBuffer(offset int32, width int32, height int32, stride int32, format uint32) (*Buffer, error) {
851+ if err := o.Context().CheckProxy(o); err != nil {
852+ return nil, err
853+ }
842 child := &Buffer{}854 child := &Buffer{}
843 child.SetContext(o.Context())855 child.SetContext(o.Context())
844 child.SetID(o.Context().AllocateID())856 child.SetID(o.Context().AllocateID())
@@ -852,6 +864,9 @@ func (o *ShmPool) CreateBuffer(offset int32, width int32, height int32, stride i
852 864
853 // Destroy the shared memory pool. The mmapped memory will be released when all buffers that have been created from this pool are gone.865 // Destroy the shared memory pool. The mmapped memory will be released when all buffers that have been created from this pool are gone.
854 func (o *ShmPool) Destroy() error {866 func (o *ShmPool) Destroy() error {
867+ if err := o.Context().CheckProxy(o); err != nil {
868+ return err
869+ }
855 if err := o.Context().SendRequest(o, 1); err != nil {870 if err := o.Context().SendRequest(o, 1); err != nil {
856 return err871 return err
857 }872 }
@@ -861,6 +876,9 @@ func (o *ShmPool) Destroy() error {
861 876
862 // 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.877 // 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.
863 func (o *ShmPool) Resize(size int32) error {878 func (o *ShmPool) Resize(size int32) error {
879+ if err := o.Context().CheckProxy(o); err != nil {
880+ return err
881+ }
864 if err := o.Context().SendRequest(o, 2, size); err != nil {882 if err := o.Context().SendRequest(o, 2, size); err != nil {
865 return err883 return err
866 }884 }
@@ -904,6 +922,9 @@ func NewShm(ctx *wl.Context) *Shm {
904 922
905 // 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.923 // 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.
906 func (o *Shm) CreatePool(fd int, size int32) (*ShmPool, error) {924 func (o *Shm) CreatePool(fd int, size int32) (*ShmPool, error) {
925+ if err := o.Context().CheckProxy(o); err != nil {
926+ return nil, err
927+ }
907 child := &ShmPool{}928 child := &ShmPool{}
908 child.SetContext(o.Context())929 child.SetContext(o.Context())
909 child.SetID(o.Context().AllocateID())930 child.SetID(o.Context().AllocateID())
@@ -918,6 +939,9 @@ func (o *Shm) CreatePool(fd int, size int32) (*ShmPool, error) {
918 939
919 // 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.940 // 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.
920 func (o *Shm) Release() error {941 func (o *Shm) Release() error {
942+ if err := o.Context().CheckProxy(o); err != nil {
943+ return err
944+ }
921 if err := o.Context().SendRequest(o, 1); err != nil {945 if err := o.Context().SendRequest(o, 1); err != nil {
922 return err946 return err
923 }947 }
@@ -993,6 +1017,9 @@ func NewBuffer(ctx *wl.Context) *Buffer {
993 1017
994 // 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.1018 // 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.
995 func (o *Buffer) Destroy() error {1019 func (o *Buffer) Destroy() error {
1020+ if err := o.Context().CheckProxy(o); err != nil {
1021+ return err
1022+ }
996 if err := o.Context().SendRequest(o, 0); err != nil {1023 if err := o.Context().SendRequest(o, 0); err != nil {
997 return err1024 return err
998 }1025 }
@@ -1069,6 +1096,9 @@ func NewDataOffer(ctx *wl.Context) *DataOffer {
1069 1096
1070 // 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.1097 // 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.
1071 func (o *DataOffer) Accept(serial uint32, mimeType string) error {1098 func (o *DataOffer) Accept(serial uint32, mimeType string) error {
1099+ if err := o.Context().CheckProxy(o); err != nil {
1100+ return err
1101+ }
1072 if err := o.Context().SendRequest(o, 0, serial, mimeType); err != nil {1102 if err := o.Context().SendRequest(o, 0, serial, mimeType); err != nil {
1073 return err1103 return err
1074 }1104 }
@@ -1077,6 +1107,9 @@ func (o *DataOffer) Accept(serial uint32, mimeType string) error {
1077 1107
1078 // 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.1108 // 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.
1079 func (o *DataOffer) Receive(mimeType string, fd int) error {1109 func (o *DataOffer) Receive(mimeType string, fd int) error {
1110+ if err := o.Context().CheckProxy(o); err != nil {
1111+ return err
1112+ }
1080 if err := o.Context().SendRequestWithFDs(o, 1, []int{fd}, mimeType, uintptr(fd)); err != nil {1113 if err := o.Context().SendRequestWithFDs(o, 1, []int{fd}, mimeType, uintptr(fd)); err != nil {
1081 return err1114 return err
1082 }1115 }
@@ -1086,6 +1119,9 @@ func (o *DataOffer) Receive(mimeType string, fd int) error {
1086 1119
1087 // Destroy the data offer.1120 // Destroy the data offer.
1088 func (o *DataOffer) Destroy() error {1121 func (o *DataOffer) Destroy() error {
1122+ if err := o.Context().CheckProxy(o); err != nil {
1123+ return err
1124+ }
1089 if err := o.Context().SendRequest(o, 2); err != nil {1125 if err := o.Context().SendRequest(o, 2); err != nil {
1090 return err1126 return err
1091 }1127 }
@@ -1095,6 +1131,9 @@ func (o *DataOffer) Destroy() error {
1095 1131
1096 // 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.1132 // 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.
1097 func (o *DataOffer) Finish() error {1133 func (o *DataOffer) Finish() error {
1134+ if err := o.Context().CheckProxy(o); err != nil {
1135+ return err
1136+ }
1098 if err := o.Context().SendRequest(o, 3); err != nil {1137 if err := o.Context().SendRequest(o, 3); err != nil {
1099 return err1138 return err
1100 }1139 }
@@ -1103,6 +1142,9 @@ func (o *DataOffer) Finish() error {
1103 1142
1104 // 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.1143 // 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.
1105 func (o *DataOffer) SetActions(dndActions uint32, preferredAction uint32) error {1144 func (o *DataOffer) SetActions(dndActions uint32, preferredAction uint32) error {
1145+ if err := o.Context().CheckProxy(o); err != nil {
1146+ return err
1147+ }
1106 if err := o.Context().SendRequest(o, 4, dndActions, preferredAction); err != nil {1148 if err := o.Context().SendRequest(o, 4, dndActions, preferredAction); err != nil {
1107 return err1149 return err
1108 }1150 }
@@ -1244,6 +1286,9 @@ func NewDataSource(ctx *wl.Context) *DataSource {
1244 1286
1245 // This request adds a mime type to the set of mime types advertised to targets. Can be called several times to offer multiple types.1287 // This request adds a mime type to the set of mime types advertised to targets. Can be called several times to offer multiple types.
1246 func (o *DataSource) Offer(mimeType string) error {1288 func (o *DataSource) Offer(mimeType string) error {
1289+ if err := o.Context().CheckProxy(o); err != nil {
1290+ return err
1291+ }
1247 if err := o.Context().SendRequest(o, 0, mimeType); err != nil {1292 if err := o.Context().SendRequest(o, 0, mimeType); err != nil {
1248 return err1293 return err
1249 }1294 }
@@ -1252,6 +1297,9 @@ func (o *DataSource) Offer(mimeType string) error {
1252 1297
1253 // Destroy the data source.1298 // Destroy the data source.
1254 func (o *DataSource) Destroy() error {1299 func (o *DataSource) Destroy() error {
1300+ if err := o.Context().CheckProxy(o); err != nil {
1301+ return err
1302+ }
1255 if err := o.Context().SendRequest(o, 1); err != nil {1303 if err := o.Context().SendRequest(o, 1); err != nil {
1256 return err1304 return err
1257 }1305 }
@@ -1261,6 +1309,9 @@ func (o *DataSource) Destroy() error {
1261 1309
1262 // 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.1310 // 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.
1263 func (o *DataSource) SetActions(dndActions uint32) error {1311 func (o *DataSource) SetActions(dndActions uint32) error {
1312+ if err := o.Context().CheckProxy(o); err != nil {
1313+ return err
1314+ }
1264 if err := o.Context().SendRequest(o, 2, dndActions); err != nil {1315 if err := o.Context().SendRequest(o, 2, dndActions); err != nil {
1265 return err1316 return err
1266 }1317 }
@@ -1497,6 +1548,9 @@ func NewDataDevice(ctx *wl.Context) *DataDevice {
1497 1548
1498 // 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.1549 // 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.
1499 func (o *DataDevice) StartDrag(source *DataSource, origin *Surface, icon *Surface, serial uint32) error {1550 func (o *DataDevice) StartDrag(source *DataSource, origin *Surface, icon *Surface, serial uint32) error {
1551+ if err := o.Context().CheckProxy(o); err != nil {
1552+ return err
1553+ }
1500 var arg0 wl.Object1554 var arg0 wl.Object
1501 if source != nil {1555 if source != nil {
1502 arg0 = source1556 arg0 = source
@@ -1517,6 +1571,9 @@ func (o *DataDevice) StartDrag(source *DataSource, origin *Surface, icon *Surfac
1517 1571
1518 // 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.1572 // 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.
1519 func (o *DataDevice) SetSelection(source *DataSource, serial uint32) error {1573 func (o *DataDevice) SetSelection(source *DataSource, serial uint32) error {
1574+ if err := o.Context().CheckProxy(o); err != nil {
1575+ return err
1576+ }
1520 var arg0 wl.Object1577 var arg0 wl.Object
1521 if source != nil {1578 if source != nil {
1522 arg0 = source1579 arg0 = source
@@ -1529,6 +1586,9 @@ func (o *DataDevice) SetSelection(source *DataSource, serial uint32) error {
1529 1586
1530 // This request destroys the data device.1587 // This request destroys the data device.
1531 func (o *DataDevice) Release() error {1588 func (o *DataDevice) Release() error {
1589+ if err := o.Context().CheckProxy(o); err != nil {
1590+ return err
1591+ }
1532 if err := o.Context().SendRequest(o, 2); err != nil {1592 if err := o.Context().SendRequest(o, 2); err != nil {
1533 return err1593 return err
1534 }1594 }
@@ -1763,6 +1823,9 @@ func NewDataDeviceManager(ctx *wl.Context) *DataDeviceManager {
1763 1823
1764 // Create a new data source.1824 // Create a new data source.
1765 func (o *DataDeviceManager) CreateDataSource() (*DataSource, error) {1825 func (o *DataDeviceManager) CreateDataSource() (*DataSource, error) {
1826+ if err := o.Context().CheckProxy(o); err != nil {
1827+ return nil, err
1828+ }
1766 child := &DataSource{}1829 child := &DataSource{}
1767 child.SetContext(o.Context())1830 child.SetContext(o.Context())
1768 child.SetID(o.Context().AllocateID())1831 child.SetID(o.Context().AllocateID())
@@ -1776,6 +1839,9 @@ func (o *DataDeviceManager) CreateDataSource() (*DataSource, error) {
1776 1839
1777 // Create a new data device for a given seat.1840 // Create a new data device for a given seat.
1778 func (o *DataDeviceManager) GetDataDevice(seat *Seat) (*DataDevice, error) {1841 func (o *DataDeviceManager) GetDataDevice(seat *Seat) (*DataDevice, error) {
1842+ if err := o.Context().CheckProxy(o); err != nil {
1843+ return nil, err
1844+ }
1779 var arg1 wl.Object1845 var arg1 wl.Object
1780 if seat != nil {1846 if seat != nil {
1781 arg1 = seat1847 arg1 = seat
@@ -1793,6 +1859,9 @@ func (o *DataDeviceManager) GetDataDevice(seat *Seat) (*DataDevice, error) {
1793 1859
1794 // This request destroys the wl_data_device_manager. This has no effect on any other objects.1860 // This request destroys the wl_data_device_manager. This has no effect on any other objects.
1795 func (o *DataDeviceManager) Release() error {1861 func (o *DataDeviceManager) Release() error {
1862+ if err := o.Context().CheckProxy(o); err != nil {
1863+ return err
1864+ }
1796 if err := o.Context().SendRequest(o, 2); err != nil {1865 if err := o.Context().SendRequest(o, 2); err != nil {
1797 return err1866 return err
1798 }1867 }
@@ -1833,6 +1902,9 @@ func NewShell(ctx *wl.Context) *Shell {
1833 1902
1834 // 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.1903 // 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.
1835 func (o *Shell) GetShellSurface(surface *Surface) (*ShellSurface, error) {1904 func (o *Shell) GetShellSurface(surface *Surface) (*ShellSurface, error) {
1905+ if err := o.Context().CheckProxy(o); err != nil {
1906+ return nil, err
1907+ }
1836 var arg1 wl.Object1908 var arg1 wl.Object
1837 if surface != nil {1909 if surface != nil {
1838 arg1 = surface1910 arg1 = surface
@@ -1887,6 +1959,9 @@ func NewShellSurface(ctx *wl.Context) *ShellSurface {
1887 1959
1888 // A client must respond to a ping event with a pong request or the client may be deemed unresponsive.1960 // A client must respond to a ping event with a pong request or the client may be deemed unresponsive.
1889 func (o *ShellSurface) Pong(serial uint32) error {1961 func (o *ShellSurface) Pong(serial uint32) error {
1962+ if err := o.Context().CheckProxy(o); err != nil {
1963+ return err
1964+ }
1890 if err := o.Context().SendRequest(o, 0, serial); err != nil {1965 if err := o.Context().SendRequest(o, 0, serial); err != nil {
1891 return err1966 return err
1892 }1967 }
@@ -1895,6 +1970,9 @@ func (o *ShellSurface) Pong(serial uint32) error {
1895 1970
1896 // 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).1971 // 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).
1897 func (o *ShellSurface) Move(seat *Seat, serial uint32) error {1972 func (o *ShellSurface) Move(seat *Seat, serial uint32) error {
1973+ if err := o.Context().CheckProxy(o); err != nil {
1974+ return err
1975+ }
1898 var arg0 wl.Object1976 var arg0 wl.Object
1899 if seat != nil {1977 if seat != nil {
1900 arg0 = seat1978 arg0 = seat
@@ -1907,6 +1985,9 @@ func (o *ShellSurface) Move(seat *Seat, serial uint32) error {
1907 1985
1908 // 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).1986 // 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).
1909 func (o *ShellSurface) Resize(seat *Seat, serial uint32, edges uint32) error {1987 func (o *ShellSurface) Resize(seat *Seat, serial uint32, edges uint32) error {
1988+ if err := o.Context().CheckProxy(o); err != nil {
1989+ return err
1990+ }
1910 var arg0 wl.Object1991 var arg0 wl.Object
1911 if seat != nil {1992 if seat != nil {
1912 arg0 = seat1993 arg0 = seat
@@ -1919,6 +2000,9 @@ func (o *ShellSurface) Resize(seat *Seat, serial uint32, edges uint32) error {
1919 2000
1920 // Map the surface as a toplevel surface. A toplevel surface is not fullscreen, maximized or transient.2001 // Map the surface as a toplevel surface. A toplevel surface is not fullscreen, maximized or transient.
1921 func (o *ShellSurface) SetToplevel() error {2002 func (o *ShellSurface) SetToplevel() error {
2003+ if err := o.Context().CheckProxy(o); err != nil {
2004+ return err
2005+ }
1922 if err := o.Context().SendRequest(o, 3); err != nil {2006 if err := o.Context().SendRequest(o, 3); err != nil {
1923 return err2007 return err
1924 }2008 }
@@ -1927,6 +2011,9 @@ func (o *ShellSurface) SetToplevel() error {
1927 2011
1928 // 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.2012 // 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.
1929 func (o *ShellSurface) SetTransient(parent *Surface, x int32, y int32, flags uint32) error {2013 func (o *ShellSurface) SetTransient(parent *Surface, x int32, y int32, flags uint32) error {
2014+ if err := o.Context().CheckProxy(o); err != nil {
2015+ return err
2016+ }
1930 var arg0 wl.Object2017 var arg0 wl.Object
1931 if parent != nil {2018 if parent != nil {
1932 arg0 = parent2019 arg0 = parent
@@ -1939,6 +2026,9 @@ func (o *ShellSurface) SetTransient(parent *Surface, x int32, y int32, flags uin
1939 2026
1940 // 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.2027 // 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.
1941 func (o *ShellSurface) SetFullscreen(method uint32, framerate uint32, output *Output) error {2028 func (o *ShellSurface) SetFullscreen(method uint32, framerate uint32, output *Output) error {
2029+ if err := o.Context().CheckProxy(o); err != nil {
2030+ return err
2031+ }
1942 var arg2 wl.Object2032 var arg2 wl.Object
1943 if output != nil {2033 if output != nil {
1944 arg2 = output2034 arg2 = output
@@ -1951,6 +2041,9 @@ func (o *ShellSurface) SetFullscreen(method uint32, framerate uint32, output *Ou
1951 2041
1952 // 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.2042 // 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.
1953 func (o *ShellSurface) SetPopup(seat *Seat, serial uint32, parent *Surface, x int32, y int32, flags uint32) error {2043 func (o *ShellSurface) SetPopup(seat *Seat, serial uint32, parent *Surface, x int32, y int32, flags uint32) error {
2044+ if err := o.Context().CheckProxy(o); err != nil {
2045+ return err
2046+ }
1954 var arg0 wl.Object2047 var arg0 wl.Object
1955 if seat != nil {2048 if seat != nil {
1956 arg0 = seat2049 arg0 = seat
@@ -1967,6 +2060,9 @@ func (o *ShellSurface) SetPopup(seat *Seat, serial uint32, parent *Surface, x in
1967 2060
1968 // 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.2061 // 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.
1969 func (o *ShellSurface) SetMaximized(output *Output) error {2062 func (o *ShellSurface) SetMaximized(output *Output) error {
2063+ if err := o.Context().CheckProxy(o); err != nil {
2064+ return err
2065+ }
1970 var arg0 wl.Object2066 var arg0 wl.Object
1971 if output != nil {2067 if output != nil {
1972 arg0 = output2068 arg0 = output
@@ -1979,6 +2075,9 @@ func (o *ShellSurface) SetMaximized(output *Output) error {
1979 2075
1980 // 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.2076 // 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.
1981 func (o *ShellSurface) SetTitle(title string) error {2077 func (o *ShellSurface) SetTitle(title string) error {
2078+ if err := o.Context().CheckProxy(o); err != nil {
2079+ return err
2080+ }
1982 if err := o.Context().SendRequest(o, 8, title); err != nil {2081 if err := o.Context().SendRequest(o, 8, title); err != nil {
1983 return err2082 return err
1984 }2083 }
@@ -1987,6 +2086,9 @@ func (o *ShellSurface) SetTitle(title string) error {
1987 2086
1988 // 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.2087 // 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.
1989 func (o *ShellSurface) SetClass(class string) error {2088 func (o *ShellSurface) SetClass(class string) error {
2089+ if err := o.Context().CheckProxy(o); err != nil {
2090+ return err
2091+ }
1990 if err := o.Context().SendRequest(o, 9, class); err != nil {2092 if err := o.Context().SendRequest(o, 9, class); err != nil {
1991 return err2093 return err
1992 }2094 }
@@ -2127,6 +2229,9 @@ func NewSurface(ctx *wl.Context) *Surface {
2127 2229
2128 // Deletes the surface and invalidates its object ID.2230 // Deletes the surface and invalidates its object ID.
2129 func (o *Surface) Destroy() error {2231 func (o *Surface) Destroy() error {
2232+ if err := o.Context().CheckProxy(o); err != nil {
2233+ return err
2234+ }
2130 if err := o.Context().SendRequest(o, 0); err != nil {2235 if err := o.Context().SendRequest(o, 0); err != nil {
2131 return err2236 return err
2132 }2237 }
@@ -2136,6 +2241,9 @@ func (o *Surface) Destroy() error {
2136 2241
2137 // 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.2242 // 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.
2138 func (o *Surface) Attach(buffer *Buffer, x int32, y int32) error {2243 func (o *Surface) Attach(buffer *Buffer, x int32, y int32) error {
2244+ if err := o.Context().CheckProxy(o); err != nil {
2245+ return err
2246+ }
2139 var arg0 wl.Object2247 var arg0 wl.Object
2140 if buffer != nil {2248 if buffer != nil {
2141 arg0 = buffer2249 arg0 = buffer
@@ -2148,6 +2256,9 @@ func (o *Surface) Attach(buffer *Buffer, x int32, y int32) error {
2148 2256
2149 // 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.2257 // 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.
2150 func (o *Surface) Damage(x int32, y int32, width int32, height int32) error {2258 func (o *Surface) Damage(x int32, y int32, width int32, height int32) error {
2259+ if err := o.Context().CheckProxy(o); err != nil {
2260+ return err
2261+ }
2151 if err := o.Context().SendRequest(o, 2, x, y, width, height); err != nil {2262 if err := o.Context().SendRequest(o, 2, x, y, width, height); err != nil {
2152 return err2263 return err
2153 }2264 }
@@ -2156,6 +2267,9 @@ func (o *Surface) Damage(x int32, y int32, width int32, height int32) error {
2156 2267
2157 // 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.2268 // 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.
2158 func (o *Surface) Frame() (*Callback, error) {2269 func (o *Surface) Frame() (*Callback, error) {
2270+ if err := o.Context().CheckProxy(o); err != nil {
2271+ return nil, err
2272+ }
2159 child := &Callback{}2273 child := &Callback{}
2160 child.SetContext(o.Context())2274 child.SetContext(o.Context())
2161 child.SetID(o.Context().AllocateID())2275 child.SetID(o.Context().AllocateID())
@@ -2169,6 +2283,9 @@ func (o *Surface) Frame() (*Callback, error) {
2169 2283
2170 // 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.2284 // 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.
2171 func (o *Surface) SetOpaqueRegion(region *Region) error {2285 func (o *Surface) SetOpaqueRegion(region *Region) error {
2286+ if err := o.Context().CheckProxy(o); err != nil {
2287+ return err
2288+ }
2172 var arg0 wl.Object2289 var arg0 wl.Object
2173 if region != nil {2290 if region != nil {
2174 arg0 = region2291 arg0 = region
@@ -2181,6 +2298,9 @@ func (o *Surface) SetOpaqueRegion(region *Region) error {
2181 2298
2182 // 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.2299 // 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.
2183 func (o *Surface) SetInputRegion(region *Region) error {2300 func (o *Surface) SetInputRegion(region *Region) error {
2301+ if err := o.Context().CheckProxy(o); err != nil {
2302+ return err
2303+ }
2184 var arg0 wl.Object2304 var arg0 wl.Object
2185 if region != nil {2305 if region != nil {
2186 arg0 = region2306 arg0 = region
@@ -2193,6 +2313,9 @@ func (o *Surface) SetInputRegion(region *Region) error {
2193 2313
2194 // 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.2314 // 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.
2195 func (o *Surface) Commit() error {2315 func (o *Surface) Commit() error {
2316+ if err := o.Context().CheckProxy(o); err != nil {
2317+ return err
2318+ }
2196 if err := o.Context().SendRequest(o, 6); err != nil {2319 if err := o.Context().SendRequest(o, 6); err != nil {
2197 return err2320 return err
2198 }2321 }
@@ -2201,6 +2324,9 @@ func (o *Surface) Commit() error {
2201 2324
2202 // 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.2325 // 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.
2203 func (o *Surface) SetBufferTransform(transform int32) error {2326 func (o *Surface) SetBufferTransform(transform int32) error {
2327+ if err := o.Context().CheckProxy(o); err != nil {
2328+ return err
2329+ }
2204 if err := o.Context().SendRequest(o, 7, transform); err != nil {2330 if err := o.Context().SendRequest(o, 7, transform); err != nil {
2205 return err2331 return err
2206 }2332 }
@@ -2209,6 +2335,9 @@ func (o *Surface) SetBufferTransform(transform int32) error {
2209 2335
2210 // 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.2336 // 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.
2211 func (o *Surface) SetBufferScale(scale int32) error {2337 func (o *Surface) SetBufferScale(scale int32) error {
2338+ if err := o.Context().CheckProxy(o); err != nil {
2339+ return err
2340+ }
2212 if err := o.Context().SendRequest(o, 8, scale); err != nil {2341 if err := o.Context().SendRequest(o, 8, scale); err != nil {
2213 return err2342 return err
2214 }2343 }
@@ -2217,6 +2346,9 @@ func (o *Surface) SetBufferScale(scale int32) error {
2217 2346
2218 // 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.2347 // 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.
2219 func (o *Surface) DamageBuffer(x int32, y int32, width int32, height int32) error {2348 func (o *Surface) DamageBuffer(x int32, y int32, width int32, height int32) error {
2349+ if err := o.Context().CheckProxy(o); err != nil {
2350+ return err
2351+ }
2220 if err := o.Context().SendRequest(o, 9, x, y, width, height); err != nil {2352 if err := o.Context().SendRequest(o, 9, x, y, width, height); err != nil {
2221 return err2353 return err
2222 }2354 }
@@ -2225,6 +2357,9 @@ func (o *Surface) DamageBuffer(x int32, y int32, width int32, height int32) erro
2225 2357
2226 // 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.2358 // 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.
2227 func (o *Surface) Offset(x int32, y int32) error {2359 func (o *Surface) Offset(x int32, y int32) error {
2360+ if err := o.Context().CheckProxy(o); err != nil {
2361+ return err
2362+ }
2228 if err := o.Context().SendRequest(o, 10, x, y); err != nil {2363 if err := o.Context().SendRequest(o, 10, x, y); err != nil {
2229 return err2364 return err
2230 }2365 }
@@ -2233,6 +2368,9 @@ func (o *Surface) Offset(x int32, y int32) error {
2233 2368
2234 // 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.2369 // 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.
2235 func (o *Surface) GetRelease() (*Callback, error) {2370 func (o *Surface) GetRelease() (*Callback, error) {
2371+ if err := o.Context().CheckProxy(o); err != nil {
2372+ return nil, err
2373+ }
2236 child := &Callback{}2374 child := &Callback{}
2237 child.SetContext(o.Context())2375 child.SetContext(o.Context())
2238 child.SetID(o.Context().AllocateID())2376 child.SetID(o.Context().AllocateID())
@@ -2406,6 +2544,9 @@ func NewSeat(ctx *wl.Context) *Seat {
2406 2544
2407 // 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.2545 // 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.
2408 func (o *Seat) GetPointer() (*Pointer, error) {2546 func (o *Seat) GetPointer() (*Pointer, error) {
2547+ if err := o.Context().CheckProxy(o); err != nil {
2548+ return nil, err
2549+ }
2409 child := &Pointer{}2550 child := &Pointer{}
2410 child.SetContext(o.Context())2551 child.SetContext(o.Context())
2411 child.SetID(o.Context().AllocateID())2552 child.SetID(o.Context().AllocateID())
@@ -2419,6 +2560,9 @@ func (o *Seat) GetPointer() (*Pointer, error) {
2419 2560
2420 // 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.2561 // 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.
2421 func (o *Seat) GetKeyboard() (*Keyboard, error) {2562 func (o *Seat) GetKeyboard() (*Keyboard, error) {
2563+ if err := o.Context().CheckProxy(o); err != nil {
2564+ return nil, err
2565+ }
2422 child := &Keyboard{}2566 child := &Keyboard{}
2423 child.SetContext(o.Context())2567 child.SetContext(o.Context())
2424 child.SetID(o.Context().AllocateID())2568 child.SetID(o.Context().AllocateID())
@@ -2432,6 +2576,9 @@ func (o *Seat) GetKeyboard() (*Keyboard, error) {
2432 2576
2433 // 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.2577 // 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.
2434 func (o *Seat) GetTouch() (*Touch, error) {2578 func (o *Seat) GetTouch() (*Touch, error) {
2579+ if err := o.Context().CheckProxy(o); err != nil {
2580+ return nil, err
2581+ }
2435 child := &Touch{}2582 child := &Touch{}
2436 child.SetContext(o.Context())2583 child.SetContext(o.Context())
2437 child.SetID(o.Context().AllocateID())2584 child.SetID(o.Context().AllocateID())
@@ -2445,6 +2592,9 @@ func (o *Seat) GetTouch() (*Touch, error) {
2445 2592
2446 // Using this request a client can tell the server that it is not going to use the seat object anymore.2593 // Using this request a client can tell the server that it is not going to use the seat object anymore.
2447 func (o *Seat) Release() error {2594 func (o *Seat) Release() error {
2595+ if err := o.Context().CheckProxy(o); err != nil {
2596+ return err
2597+ }
2448 if err := o.Context().SendRequest(o, 3); err != nil {2598 if err := o.Context().SendRequest(o, 3); err != nil {
2449 return err2599 return err
2450 }2600 }
@@ -2562,6 +2712,9 @@ func NewPointer(ctx *wl.Context) *Pointer {
2562 2712
2563 // 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.2713 // 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.
2564 func (o *Pointer) SetCursor(serial uint32, surface *Surface, hotspotX int32, hotspotY int32) error {2714 func (o *Pointer) SetCursor(serial uint32, surface *Surface, hotspotX int32, hotspotY int32) error {
2715+ if err := o.Context().CheckProxy(o); err != nil {
2716+ return err
2717+ }
2565 var arg1 wl.Object2718 var arg1 wl.Object
2566 if surface != nil {2719 if surface != nil {
2567 arg1 = surface2720 arg1 = surface
@@ -2574,6 +2727,9 @@ func (o *Pointer) SetCursor(serial uint32, surface *Surface, hotspotX int32, hot
2574 2727
2575 // 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.2728 // 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.
2576 func (o *Pointer) Release() error {2729 func (o *Pointer) Release() error {
2730+ if err := o.Context().CheckProxy(o); err != nil {
2731+ return err
2732+ }
2577 if err := o.Context().SendRequest(o, 1); err != nil {2733 if err := o.Context().SendRequest(o, 1); err != nil {
2578 return err2734 return err
2579 }2735 }
@@ -3010,6 +3166,9 @@ func NewKeyboard(ctx *wl.Context) *Keyboard {
3010 3166
3011 // release the keyboard object3167 // release the keyboard object
3012 func (o *Keyboard) Release() error {3168 func (o *Keyboard) Release() error {
3169+ if err := o.Context().CheckProxy(o); err != nil {
3170+ return err
3171+ }
3013 if err := o.Context().SendRequest(o, 0); err != nil {3172 if err := o.Context().SendRequest(o, 0); err != nil {
3014 return err3173 return err
3015 }3174 }
@@ -3263,6 +3422,9 @@ func NewTouch(ctx *wl.Context) *Touch {
3263 3422
3264 // release the touch object3423 // release the touch object
3265 func (o *Touch) Release() error {3424 func (o *Touch) Release() error {
3425+ if err := o.Context().CheckProxy(o); err != nil {
3426+ return err
3427+ }
3266 if err := o.Context().SendRequest(o, 0); err != nil {3428 if err := o.Context().SendRequest(o, 0); err != nil {
3267 return err3429 return err
3268 }3430 }
@@ -3540,6 +3702,9 @@ func NewOutput(ctx *wl.Context) *Output {
3540 3702
3541 // Using this request a client can tell the server that it is not going to use the output object anymore.3703 // Using this request a client can tell the server that it is not going to use the output object anymore.
3542 func (o *Output) Release() error {3704 func (o *Output) Release() error {
3705+ if err := o.Context().CheckProxy(o); err != nil {
3706+ return err
3707+ }
3543 if err := o.Context().SendRequest(o, 0); err != nil {3708 if err := o.Context().SendRequest(o, 0); err != nil {
3544 return err3709 return err
3545 }3710 }
@@ -3775,6 +3940,9 @@ func NewRegion(ctx *wl.Context) *Region {
3775 3940
3776 // Destroy the region. This will invalidate the object ID.3941 // Destroy the region. This will invalidate the object ID.
3777 func (o *Region) Destroy() error {3942 func (o *Region) Destroy() error {
3943+ if err := o.Context().CheckProxy(o); err != nil {
3944+ return err
3945+ }
3778 if err := o.Context().SendRequest(o, 0); err != nil {3946 if err := o.Context().SendRequest(o, 0); err != nil {
3779 return err3947 return err
3780 }3948 }
@@ -3784,6 +3952,9 @@ func (o *Region) Destroy() error {
3784 3952
3785 // Add the specified rectangle to the region.3953 // Add the specified rectangle to the region.
3786 func (o *Region) Add(x int32, y int32, width int32, height int32) error {3954 func (o *Region) Add(x int32, y int32, width int32, height int32) error {
3955+ if err := o.Context().CheckProxy(o); err != nil {
3956+ return err
3957+ }
3787 if err := o.Context().SendRequest(o, 1, x, y, width, height); err != nil {3958 if err := o.Context().SendRequest(o, 1, x, y, width, height); err != nil {
3788 return err3959 return err
3789 }3960 }
@@ -3792,6 +3963,9 @@ func (o *Region) Add(x int32, y int32, width int32, height int32) error {
3792 3963
3793 // Subtract the specified rectangle from the region.3964 // Subtract the specified rectangle from the region.
3794 func (o *Region) Subtract(x int32, y int32, width int32, height int32) error {3965 func (o *Region) Subtract(x int32, y int32, width int32, height int32) error {
3966+ if err := o.Context().CheckProxy(o); err != nil {
3967+ return err
3968+ }
3795 if err := o.Context().SendRequest(o, 2, x, y, width, height); err != nil {3969 if err := o.Context().SendRequest(o, 2, x, y, width, height); err != nil {
3796 return err3970 return err
3797 }3971 }
@@ -3831,6 +4005,9 @@ func NewSubcompositor(ctx *wl.Context) *Subcompositor {
3831 4005
3832 // 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.4006 // 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.
3833 func (o *Subcompositor) Destroy() error {4007 func (o *Subcompositor) Destroy() error {
4008+ if err := o.Context().CheckProxy(o); err != nil {
4009+ return err
4010+ }
3834 if err := o.Context().SendRequest(o, 0); err != nil {4011 if err := o.Context().SendRequest(o, 0); err != nil {
3835 return err4012 return err
3836 }4013 }
@@ -3840,6 +4017,9 @@ func (o *Subcompositor) Destroy() error {
3840 4017
3841 // 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.4018 // 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.
3842 func (o *Subcompositor) GetSubsurface(surface *Surface, parent *Surface) (*Subsurface, error) {4019 func (o *Subcompositor) GetSubsurface(surface *Surface, parent *Surface) (*Subsurface, error) {
4020+ if err := o.Context().CheckProxy(o); err != nil {
4021+ return nil, err
4022+ }
3843 var arg1 wl.Object4023 var arg1 wl.Object
3844 if surface != nil {4024 if surface != nil {
3845 arg1 = surface4025 arg1 = surface
@@ -3892,6 +4072,9 @@ func NewSubsurface(ctx *wl.Context) *Subsurface {
3892 4072
3893 // 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.4073 // 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.
3894 func (o *Subsurface) Destroy() error {4074 func (o *Subsurface) Destroy() error {
4075+ if err := o.Context().CheckProxy(o); err != nil {
4076+ return err
4077+ }
3895 if err := o.Context().SendRequest(o, 0); err != nil {4078 if err := o.Context().SendRequest(o, 0); err != nil {
3896 return err4079 return err
3897 }4080 }
@@ -3901,6 +4084,9 @@ func (o *Subsurface) Destroy() error {
3901 4084
3902 // 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.4085 // 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.
3903 func (o *Subsurface) SetPosition(x int32, y int32) error {4086 func (o *Subsurface) SetPosition(x int32, y int32) error {
4087+ if err := o.Context().CheckProxy(o); err != nil {
4088+ return err
4089+ }
3904 if err := o.Context().SendRequest(o, 1, x, y); err != nil {4090 if err := o.Context().SendRequest(o, 1, x, y); err != nil {
3905 return err4091 return err
3906 }4092 }
@@ -3909,6 +4095,9 @@ func (o *Subsurface) SetPosition(x int32, y int32) error {
3909 4095
3910 // 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.4096 // 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.
3911 func (o *Subsurface) PlaceAbove(sibling *Surface) error {4097 func (o *Subsurface) PlaceAbove(sibling *Surface) error {
4098+ if err := o.Context().CheckProxy(o); err != nil {
4099+ return err
4100+ }
3912 var arg0 wl.Object4101 var arg0 wl.Object
3913 if sibling != nil {4102 if sibling != nil {
3914 arg0 = sibling4103 arg0 = sibling
@@ -3921,6 +4110,9 @@ func (o *Subsurface) PlaceAbove(sibling *Surface) error {
3921 4110
3922 // The sub-surface is placed just below the reference surface. See wl_subsurface.place_above.4111 // The sub-surface is placed just below the reference surface. See wl_subsurface.place_above.
3923 func (o *Subsurface) PlaceBelow(sibling *Surface) error {4112 func (o *Subsurface) PlaceBelow(sibling *Surface) error {
4113+ if err := o.Context().CheckProxy(o); err != nil {
4114+ return err
4115+ }
3924 var arg0 wl.Object4116 var arg0 wl.Object
3925 if sibling != nil {4117 if sibling != nil {
3926 arg0 = sibling4118 arg0 = sibling
@@ -3933,6 +4125,9 @@ func (o *Subsurface) PlaceBelow(sibling *Surface) error {
3933 4125
3934 // Change the commit behaviour of the sub-surface to synchronized mode. See wl_subsurface and wl_surface.commit for more information.4126 // Change the commit behaviour of the sub-surface to synchronized mode. See wl_subsurface and wl_surface.commit for more information.
3935 func (o *Subsurface) SetSync() error {4127 func (o *Subsurface) SetSync() error {
4128+ if err := o.Context().CheckProxy(o); err != nil {
4129+ return err
4130+ }
3936 if err := o.Context().SendRequest(o, 4); err != nil {4131 if err := o.Context().SendRequest(o, 4); err != nil {
3937 return err4132 return err
3938 }4133 }
@@ -3941,6 +4136,9 @@ func (o *Subsurface) SetSync() error {
3941 4136
3942 // Change the commit behaviour of the sub-surface to desynchronized mode. See wl_subsurface and wl_surface.commit for more information.4137 // Change the commit behaviour of the sub-surface to desynchronized mode. See wl_subsurface and wl_surface.commit for more information.
3943 func (o *Subsurface) SetDesync() error {4138 func (o *Subsurface) SetDesync() error {
4139+ if err := o.Context().CheckProxy(o); err != nil {
4140+ return err
4141+ }
3944 if err := o.Context().SendRequest(o, 5); err != nil {4142 if err := o.Context().SendRequest(o, 5); err != nil {
3945 return err4143 return err
3946 }4144 }
@@ -3980,6 +4178,9 @@ func NewFixes(ctx *wl.Context) *Fixes {
3980 4178
3981 // destroys this object4179 // destroys this object
3982 func (o *Fixes) Destroy() error {4180 func (o *Fixes) Destroy() error {
4181+ if err := o.Context().CheckProxy(o); err != nil {
4182+ return err
4183+ }
3983 if err := o.Context().SendRequest(o, 0); err != nil {4184 if err := o.Context().SendRequest(o, 0); err != nil {
3984 return err4185 return err
3985 }4186 }
@@ -3989,6 +4190,9 @@ func (o *Fixes) Destroy() error {
3989 4190
3990 // 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.4191 // 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.
3991 func (o *Fixes) DestroyRegistry(registry *wl.Registry) error {4192 func (o *Fixes) DestroyRegistry(registry *wl.Registry) error {
4193+ if err := o.Context().CheckProxy(o); err != nil {
4194+ return err
4195+ }
3992 var arg0 wl.Object4196 var arg0 wl.Object
3993 if registry != nil {4197 if registry != nil {
3994 arg0 = registry4198 arg0 = registry
@@ -4001,6 +4205,9 @@ func (o *Fixes) DestroyRegistry(registry *wl.Registry) error {
4001 4205
4002 // 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.4206 // 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.
4003 func (o *Fixes) AckGlobalRemove(registry *wl.Registry, name uint32) error {4207 func (o *Fixes) AckGlobalRemove(registry *wl.Registry, name uint32) error {
4208+ if err := o.Context().CheckProxy(o); err != nil {
4209+ return err
4210+ }
4004 var arg0 wl.Object4211 var arg0 wl.Object
4005 if registry != nil {4212 if registry != nil {
4006 arg0 = registry4213 arg0 = registry
M scm_linux.go +44 -27
@@ -10,6 +10,7 @@ import (
1010 "net"
1111 "sync"
1212 "syscall"
13+ "unsafe"
1314
1415 "golang.org/x/sys/unix"
1516 )
@@ -19,10 +20,12 @@ import (
1920 // instead of silently dropping descriptors.
2021 const maxRecvFDs = 64
2122
23+type controlBuffer struct{ data []byte }
24+
2225 // controlBufferPool provides control-message buffers sized for maxRecvFDs.
2326 var controlBufferPool = sync.Pool{
2427 New: func() interface{} {
25- return make([]byte, unix.CmsgSpace(maxRecvFDs*4))
28+ return &controlBuffer{data: make([]byte, unix.CmsgSpace(maxRecvFDs*4))}
2629 },
2730 }
2831
@@ -43,8 +46,9 @@ func (d *Display) readChunk() (int, error) {
4346 return n, err
4447 }
4548
46- oob := controlBufferPool.Get().([]byte)
47- defer controlBufferPool.Put(oob)
49+ buffer := controlBufferPool.Get().(*controlBuffer)
50+ defer controlBufferPool.Put(buffer)
51+ oob := buffer.data
4852
4953 n, oobn, flags, _, err := d.unix.ReadMsgUnix(d.recvBuf, oob)
5054 if n < 0 {
@@ -54,12 +58,12 @@ func (d *Display) readChunk() (int, error) {
5458 if oobn > 0 {
5559 fds, perr := parseFDs(oob[:oobn])
5660 d.queueFDs(fds)
57- if perr != nil && err == nil {
58- err = perr
61+ if perr != nil {
62+ err = &ProtocolError{Kind: "malformed_control", Err: perr}
5963 }
6064 }
6165
62- if flags&unix.MSG_CTRUNC != 0 && err == nil {
66+ if flags&unix.MSG_CTRUNC != 0 {
6367 // Descriptors were dropped by the kernel, so the connection can no
6468 // longer be demarshalled correctly: treat it as a protocol failure so
6569 // the caller closes the descriptors that did arrive.
@@ -71,37 +75,50 @@ func (d *Display) readChunk() (int, error) {
7175 // parseFDs extracts the file descriptors carried by socket control messages.
7276 // Descriptors parsed before an error are returned so the caller can close them.
7377 func parseFDs(oob []byte) ([]int, error) {
74- scms, err := syscall.ParseSocketControlMessage(oob)
75- if err != nil {
76- return nil, fmt.Errorf("parse control message: %w", err)
77- }
78-
7978 var fds []int
80- for i := range scms {
81- scm := &scms[i]
82- if scm.Header.Type != syscall.SCM_RIGHTS {
83- continue
79+ for len(oob) > 0 {
80+ if len(oob) < unix.SizeofCmsghdr {
81+ return fds, fmt.Errorf("short control header")
8482 }
85- parsed, err := syscall.ParseUnixRights(scm)
86- if err != nil {
87- 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)
8892 }
89- fds = append(fds, parsed...)
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...)
100+ }
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:]
90106 }
91107 return fds, nil
92108 }
93109
94-// queueFDs is called only by the active Dispatch reader.
95-func (d *Display) queueFDs(fds []int) { d.pendingFDs = append(d.pendingFDs, fds...) }
96-func (d *Display) nextFD() (int, bool) {
97- if len(d.pendingFDs) == 0 {
98- return -1, false
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")
99114 }
100- fd := d.pendingFDs[0]
101- d.pendingFDs = d.pendingFDs[1:]
102- return fd, true
115+ *hdr = *(*unix.Cmsghdr)(unsafe.Pointer(&data[0]))
116+ return nil
103117 }
104118
119+// queueFDs is called only by the active Dispatch reader.
120+func (d *Display) queueFDs(fds []int) { d.pendingFDs = append(d.pendingFDs, fds...) }
121+
105122 // closePendingFDs is called under recvMu (including during shutdown).
106123 func (d *Display) closePendingFDs() {
107124 fds := d.pendingFDs
@@ -10,6 +10,7 @@ import (
10 "net"10 "net"
11 "sync"11 "sync"
12 "syscall"12 "syscall"
13+ "unsafe"
13 14
14 "golang.org/x/sys/unix"15 "golang.org/x/sys/unix"
15 )16 )
@@ -19,10 +20,12 @@ import (
19 // instead of silently dropping descriptors.20 // instead of silently dropping descriptors.
20 const maxRecvFDs = 6421 const maxRecvFDs = 64
21 22
23+type controlBuffer struct{ data []byte }
24+
22 // controlBufferPool provides control-message buffers sized for maxRecvFDs.25 // controlBufferPool provides control-message buffers sized for maxRecvFDs.
23 var controlBufferPool = sync.Pool{26 var controlBufferPool = sync.Pool{
24 New: func() interface{} {27 New: func() interface{} {
25- return make([]byte, unix.CmsgSpace(maxRecvFDs*4))28+ return &controlBuffer{data: make([]byte, unix.CmsgSpace(maxRecvFDs*4))}
26 },29 },
27 }30 }
28 31
@@ -43,8 +46,9 @@ func (d *Display) readChunk() (int, error) {
43 return n, err46 return n, err
44 }47 }
45 48
46- oob := controlBufferPool.Get().([]byte)49+ buffer := controlBufferPool.Get().(*controlBuffer)
47- defer controlBufferPool.Put(oob)50+ defer controlBufferPool.Put(buffer)
51+ oob := buffer.data
48 52
49 n, oobn, flags, _, err := d.unix.ReadMsgUnix(d.recvBuf, oob)53 n, oobn, flags, _, err := d.unix.ReadMsgUnix(d.recvBuf, oob)
50 if n < 0 {54 if n < 0 {
@@ -54,12 +58,12 @@ func (d *Display) readChunk() (int, error) {
54 if oobn > 0 {58 if oobn > 0 {
55 fds, perr := parseFDs(oob[:oobn])59 fds, perr := parseFDs(oob[:oobn])
56 d.queueFDs(fds)60 d.queueFDs(fds)
57- if perr != nil && err == nil {61+ if perr != nil {
58- err = perr62+ err = &ProtocolError{Kind: "malformed_control", Err: perr}
59 }63 }
60 }64 }
61 65
62- if flags&unix.MSG_CTRUNC != 0 && err == nil {66+ if flags&unix.MSG_CTRUNC != 0 {
63 // Descriptors were dropped by the kernel, so the connection can no67 // Descriptors were dropped by the kernel, so the connection can no
64 // longer be demarshalled correctly: treat it as a protocol failure so68 // longer be demarshalled correctly: treat it as a protocol failure so
65 // the caller closes the descriptors that did arrive.69 // the caller closes the descriptors that did arrive.
@@ -71,37 +75,50 @@ func (d *Display) readChunk() (int, error) {
71 // parseFDs extracts the file descriptors carried by socket control messages.75 // parseFDs extracts the file descriptors carried by socket control messages.
72 // Descriptors parsed before an error are returned so the caller can close them.76 // Descriptors parsed before an error are returned so the caller can close them.
73 func parseFDs(oob []byte) ([]int, error) {77 func parseFDs(oob []byte) ([]int, error) {
74- scms, err := syscall.ParseSocketControlMessage(oob)
75- if err != nil {
76- return nil, fmt.Errorf("parse control message: %w", err)
77- }
78-
79 var fds []int78 var fds []int
80- for i := range scms {79+ for len(oob) > 0 {
81- scm := &scms[i]80+ if len(oob) < unix.SizeofCmsghdr {
82- if scm.Header.Type != syscall.SCM_RIGHTS {81+ return fds, fmt.Errorf("short control header")
83- continue
84 }82 }
85- parsed, err := syscall.ParseUnixRights(scm)83+ var hdr unix.Cmsghdr
86- if err != nil {84+ // Parse each header independently: a malformed later header must not
87- return fds, fmt.Errorf("parse unix rights: %w", err)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)
88 }92 }
89- fds = append(fds, parsed...)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...)
100+ }
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:]
90 }106 }
91 return fds, nil107 return fds, nil
92 }108 }
93 109
94-// queueFDs is called only by the active Dispatch reader.110+func readControlHeader(data []byte, hdr *unix.Cmsghdr) error {
95-func (d *Display) queueFDs(fds []int) { d.pendingFDs = append(d.pendingFDs, fds...) }111+ // Linux cmsghdr has a native-endian size_t followed by two int32s.
96-func (d *Display) nextFD() (int, bool) {112+ if len(data) < unix.SizeofCmsghdr {
97- if len(d.pendingFDs) == 0 {113+ return fmt.Errorf("short control header")
98- return -1, false
99 }114 }
100- fd := d.pendingFDs[0]115+ *hdr = *(*unix.Cmsghdr)(unsafe.Pointer(&data[0]))
101- d.pendingFDs = d.pendingFDs[1:]116+ return nil
102- return fd, true
103 }117 }
104 118
119+// queueFDs is called only by the active Dispatch reader.
120+func (d *Display) queueFDs(fds []int) { d.pendingFDs = append(d.pendingFDs, fds...) }
121+
105 // closePendingFDs is called under recvMu (including during shutdown).122 // closePendingFDs is called under recvMu (including during shutdown).
106 func (d *Display) closePendingFDs() {123 func (d *Display) closePendingFDs() {
107 fds := d.pendingFDs124 fds := d.pendingFDs
M transport_test.go +40 -4
@@ -93,10 +93,12 @@ func newChunkDisplay(chunks [][]byte) (*Display, *[]EventRecord) {
9393 records: &records,
9494 })
9595 for _, opcode := range []uint16{3, 4, 5} {
96- signature := "uint,"
97- if opcode == 3 { signature = "uint,uint," }
98- d.RegisterEventSignature(7, opcode, signature)
99- }
96+ signature := "uint,"
97+ if opcode == 3 {
98+ signature = "uint,uint,"
99+ }
100+ d.RegisterEventSignature(7, opcode, signature)
101+ }
100102 return d, &records
101103 }
102104
@@ -279,6 +281,10 @@ func TestDisplayDispatch_DisplayError(t *testing.T) {
279281
280282 func TestDisplayDispatch_DeleteID(t *testing.T) {
281283 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))
282288
283289 if err := d.Dispatch(); err != nil {
284290 t.Fatalf("Dispatch: %v", err)
@@ -355,3 +361,33 @@ func TestReadFrame(t *testing.T) {
355361 t.Fatalf("readFrame(truncated) error = %v, want %v", err, io.ErrUnexpectedEOF)
356362 }
357363 }
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+}
@@ -93,10 +93,12 @@ func newChunkDisplay(chunks [][]byte) (*Display, *[]EventRecord) {
93 records: &records,93 records: &records,
94 })94 })
95 for _, opcode := range []uint16{3, 4, 5} {95 for _, opcode := range []uint16{3, 4, 5} {
96- signature := "uint,"96+ signature := "uint,"
97- if opcode == 3 { signature = "uint,uint," }97+ if opcode == 3 {
98- d.RegisterEventSignature(7, opcode, signature)98+ signature = "uint,uint,"
99- }99+ }
100+ d.RegisterEventSignature(7, opcode, signature)
101+ }
100 return d, &records102 return d, &records
101 }103 }
102 104
@@ -279,6 +281,10 @@ func TestDisplayDispatch_DisplayError(t *testing.T) {
279 281
280 func TestDisplayDispatch_DeleteID(t *testing.T) {282 func TestDisplayDispatch_DeleteID(t *testing.T) {
281 d, records := newChunkDisplay([][]byte{message(displayObjectID, 1, []byte{7, 0, 0, 0})})283 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))
282 288
283 if err := d.Dispatch(); err != nil {289 if err := d.Dispatch(); err != nil {
284 t.Fatalf("Dispatch: %v", err)290 t.Fatalf("Dispatch: %v", err)
@@ -355,3 +361,33 @@ func TestReadFrame(t *testing.T) {
355 t.Fatalf("readFrame(truncated) error = %v, want %v", err, io.ErrUnexpectedEOF)361 t.Fatalf("readFrame(truncated) error = %v, want %v", err, io.ErrUnexpectedEOF)
356 }362 }
357 }363 }
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+}

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