fix(protocol): harden dispatch lifecycle FD parsing and generation gates
- commit
- 3e997094e0cb5a9bd492290b592f54e92bc44fd8
- author
- brice · · 2026-09-28 07:41 UTC
- parent
- f69b87f
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
11 files · +635 -71
M client.go +85 -30
| @@ -49,8 +49,12 @@ type Display struct { | |||
| 49 | 49 | unix *net.UnixConn // set when conn is a Unix socket, nil otherwise | |
| 50 | 50 | objects sync.Map // map[uint32]Object | |
| 51 | 51 | nextID uint32 | |
| 52 | + | idMu sync.Mutex | |
| 53 | + | freeIDs []uint32 | |
| 54 | + | retiredIDs map[uint32]bool | |
| 52 | 55 | sendMu sync.Mutex | |
| 53 | 56 | recvMu sync.Mutex | |
| 57 | + | dispatchMu sync.Mutex // serialize readers without holding receive state across handlers | |
| 54 | 58 | signatures sync.Map | |
| 55 | 59 | listenerMu sync.Mutex | |
| 56 | 60 | listeners sync.Map // map[uint32]map[uint16][]func([]byte) | |
| @@ -192,8 +196,6 @@ func (d *Display) Close() error { | |||
| 192 | 196 | } | |
| 193 | 197 | d.context.closed.Store(true) | |
| 194 | 198 | err := d.conn.Close() | |
| 195 | - | d.sendMu.Lock() | |
| 196 | - | d.sendMu.Unlock() | |
| 197 | 199 | d.recvMu.Lock() | |
| 198 | 200 | d.closePendingFDs() | |
| 199 | 201 | d.recvMu.Unlock() | |
| @@ -217,7 +219,20 @@ func (d *Display) RegisterEventHandler(objectID uint32, opcode uint16, handler E | |||
| 217 | 219 | ||
| 218 | 220 | // allocateID allocates a new object ID | |
| 219 | 221 | 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 | |
| 221 | 236 | } | |
| 222 | 237 | ||
| 223 | 238 | // AllocateID allocates a new object ID (public method) | |
| @@ -338,14 +353,17 @@ func (d *Display) marshalArg(buf *bytes.Buffer, arg interface{}) error { | |||
| 338 | 353 | // Dispatch returns net.ErrClosed after Close, a typed ProtocolError for a | |
| 339 | 354 | // malformed frame, and io.ErrUnexpectedEOF when the peer closes mid-frame. | |
| 340 | 355 | func (d *Display) Dispatch() error { | |
| 356 | + | d.dispatchMu.Lock() | |
| 357 | + | defer d.dispatchMu.Unlock() | |
| 341 | 358 | d.recvMu.Lock() | |
| 342 | - | defer d.recvMu.Unlock() | |
| 343 | 359 | ||
| 344 | 360 | if d.closed.Load() { | |
| 361 | + | d.recvMu.Unlock() | |
| 345 | 362 | return net.ErrClosed | |
| 346 | 363 | } | |
| 347 | 364 | ||
| 348 | 365 | frame, err := d.nextFrame() | |
| 366 | + | d.recvMu.Unlock() | |
| 349 | 367 | if d.closed.Load() { | |
| 350 | 368 | return net.ErrClosed | |
| 351 | 369 | } | |
| @@ -362,6 +380,8 @@ func (d *Display) fail(err error) error { | |||
| 362 | 380 | if err == nil { | |
| 363 | 381 | return nil | |
| 364 | 382 | } | |
| 383 | + | d.recvMu.Lock() | |
| 384 | + | defer d.recvMu.Unlock() | |
| 365 | 385 | var perr *ProtocolError | |
| 366 | 386 | if errors.As(err, &perr) { | |
| 367 | 387 | d.closePendingFDs() | |
| @@ -448,11 +468,45 @@ type signatureProvider interface{ EventSignature(uint16) (string, bool) } | |||
| 448 | 468 | ||
| 449 | 469 | // dispatchFrame delivers one complete frame with precisely its signature's FDs. | |
| 450 | 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 | 505 | if f.object == displayObjectID { | |
| 452 | 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 | 511 | if f.object == d.registry.id { | |
| 458 | 512 | var sig string | |
| @@ -462,20 +516,19 @@ func (d *Display) dispatchFrame(f receivedFrame) error { | |||
| 462 | 516 | case 1: | |
| 463 | 517 | sig = "uint," | |
| 464 | 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 | 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 | 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) | |
| 474 | - | return nil | |
| 527 | + | return nil, nil, 2, nil | |
| 475 | 528 | } | |
| 476 | 529 | obj, ok := d.objects.Load(f.object) | |
| 477 | 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 | 533 | sig, valid := "", false | |
| 481 | 534 | p, isGenerated := obj.(signatureProvider) | |
| @@ -489,14 +542,14 @@ func (d *Display) dispatchFrame(f receivedFrame) error { | |||
| 489 | 542 | } | |
| 490 | 543 | } | |
| 491 | 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 | 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 | 550 | count := strings.Count(sig, "fd,") | |
| 498 | 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 | 554 | ev := eventPool.Get().(*Event) | |
| 502 | 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 | 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 | 566 | for _, fd := range ev.fds { | |
| 512 | 567 | _ = fd.Close() | |
| 513 | 568 | } | |
| 514 | 569 | *ev = Event{} | |
| 515 | 570 | 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} | |
| 521 | 572 | } | |
| 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 | |
| 529 | 574 | } | |
| 530 | 575 | ||
| 531 | 576 | type signatureKey struct { | |
| @@ -585,8 +630,18 @@ func (d *Display) handleDisplayEvent(opcode uint16, data []byte) error { | |||
| 585 | 630 | return &ProtocolError{Kind: "short_delete_id", Object: displayObjectID, Opcode: opcode, Size: uint32(len(data)) + HeaderSize, Err: ErrMalformedFrame} | |
| 586 | 631 | } | |
| 587 | 632 | 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() | |
| 590 | 645 | return nil | |
| 591 | 646 | ||
| 592 | 647 | default: | |
| @@ -49,8 +49,12 @@ type Display struct { | |||||
| 49 | unix *net.UnixConn // set when conn is a Unix socket, nil otherwise | 49 | unix *net.UnixConn // set when conn is a Unix socket, nil otherwise | ||
| 50 | objects sync.Map // map[uint32]Object | 50 | objects sync.Map // map[uint32]Object | ||
| 51 | nextID uint32 | 51 | nextID uint32 | ||
| 52 | + | idMu sync.Mutex | |||
| 53 | + | freeIDs []uint32 | |||
| 54 | + | retiredIDs map[uint32]bool | |||
| 52 | sendMu sync.Mutex | 55 | sendMu sync.Mutex | ||
| 53 | recvMu sync.Mutex | 56 | recvMu sync.Mutex | ||
| 57 | + | dispatchMu sync.Mutex // serialize readers without holding receive state across handlers | |||
| 54 | signatures sync.Map | 58 | signatures sync.Map | ||
| 55 | listenerMu sync.Mutex | 59 | 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 ID | 220 | // 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) - 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 | |||
| 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 a | 353 | // 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.ErrClosed | 362 | 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.ErrClosed | 368 | 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 nil | 381 | return nil | ||
| 364 | } | 382 | } | ||
| 383 | + | d.recvMu.Lock() | |||
| 384 | + | defer d.recvMu.Unlock() | |||
| 365 | var perr *ProtocolError | 385 | 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 string | 512 | 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 := "", false | 533 | 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 nil | 645 | 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 { | |||
| 60 | 60 | ||
| 61 | 61 | // SendRequest sends a request through the context | |
| 62 | 62 | 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 | |
| 65 | 65 | } | |
| 66 | 66 | return c.display.SendRequest(proxy.ID(), uint16(opcode), args...) | |
| 67 | 67 | } | |
| 68 | 68 | ||
| 69 | 69 | // SendRequestWithFDs sends a request with file descriptors through the context | |
| 70 | 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 | 79 | if c.closed.Load() { | |
| 72 | 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 | 92 | // Register registers a proxy object | |
| @@ -60,18 +60,33 @@ func NewContext(display *Display) *Context { | |||||
| 60 | 60 | ||||
| 61 | // SendRequest sends a request through the context | 61 | // 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 context | 69 | // 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 object | 92 | // Register registers a proxy object | ||
M fd_test.go +69 -0
| @@ -456,3 +456,72 @@ func TestDisplayFD_ParseFailureClosesQueuedDescriptor(t *testing.T) { | |||
| 456 | 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 | + | } | |
| @@ -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) { | |||
| 611 | 611 | case "fd": | |
| 612 | 612 | return "int", nil | |
| 613 | 613 | case "object", "new_id": | |
| 614 | - | return s.goTypeForInterface(arg.Interface), nil | |
| 614 | + | return s.goTypeForInterface(arg.Interface) | |
| 615 | 615 | } | |
| 616 | 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 | 620 | if iface == "" { | |
| 621 | - | return "wl.BaseProxy" | |
| 621 | + | return "wl.BaseProxy", nil | |
| 622 | 622 | } | |
| 623 | 623 | // The XML package owns its own definitions even when the name starts wl_. | |
| 624 | 624 | for _, local := range s.protocol.Interfaces { | |
| 625 | 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 | 629 | if bootstrapTypes[s.toGoName(iface)] { | |
| 630 | - | return "wl." + s.toGoName(iface) | |
| 630 | + | return "wl." + s.toGoName(iface), nil | |
| 631 | 631 | } | |
| 632 | 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 | 638 | // 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", nil | 612 | return "int", nil | ||
| 613 | case "object", "new_id": | 613 | case "object", "new_id": | ||
| 614 | - | return s.goTypeForInterface(arg.Interface), nil | 614 | + | 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 | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "os" | |
| 5 | + | "os/exec" | |
| 5 | 6 | "path/filepath" | |
| 6 | 7 | "strings" | |
| 7 | 8 | "testing" | |
| @@ -73,3 +74,90 @@ func TestExternalChildFactory(t *testing.T) { | |||
| 73 | 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 | + | } | |
| @@ -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}} { | |||
| 50 | 50 | // {{.Doc}} | |
| 51 | 51 | {{- end}} | |
| 52 | 52 | func (o *{{$iface.GoName}}) {{.GoName}}({{.Params}}) {{.Results}} { | |
| 53 | + | if err := o.Context().CheckProxy(o); err != nil { return {{.ErrorReturn}} } | |
| 53 | 54 | {{- range .ArgPreparations}} | |
| 54 | 55 | {{.}} | |
| 55 | 56 | {{- 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) { | |||
| 399 | 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 | + | } | |
| @@ -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 { | |||
| 773 | 773 | ||
| 774 | 774 | // Ask the compositor to create a new surface. | |
| 775 | 775 | func (o *Compositor) CreateSurface() (*Surface, error) { | |
| 776 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 777 | + | return nil, err | |
| 778 | + | } | |
| 776 | 779 | child := &Surface{} | |
| 777 | 780 | child.SetContext(o.Context()) | |
| 778 | 781 | child.SetID(o.Context().AllocateID()) | |
| @@ -786,6 +789,9 @@ func (o *Compositor) CreateSurface() (*Surface, error) { | |||
| 786 | 789 | ||
| 787 | 790 | // Ask the compositor to create a new region. | |
| 788 | 791 | func (o *Compositor) CreateRegion() (*Region, error) { | |
| 792 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 793 | + | return nil, err | |
| 794 | + | } | |
| 789 | 795 | child := &Region{} | |
| 790 | 796 | child.SetContext(o.Context()) | |
| 791 | 797 | child.SetID(o.Context().AllocateID()) | |
| @@ -799,6 +805,9 @@ func (o *Compositor) CreateRegion() (*Region, error) { | |||
| 799 | 805 | ||
| 800 | 806 | // This request destroys the wl_compositor. This has no effect on any other objects. | |
| 801 | 807 | func (o *Compositor) Release() error { | |
| 808 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 809 | + | return err | |
| 810 | + | } | |
| 802 | 811 | if err := o.Context().SendRequest(o, 2); err != nil { | |
| 803 | 812 | return err | |
| 804 | 813 | } | |
| @@ -839,6 +848,9 @@ func NewShmPool(ctx *wl.Context) *ShmPool { | |||
| 839 | 848 | ||
| 840 | 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 | 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 | 854 | child := &Buffer{} | |
| 843 | 855 | child.SetContext(o.Context()) | |
| 844 | 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 | 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 | 866 | func (o *ShmPool) Destroy() error { | |
| 867 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 868 | + | return err | |
| 869 | + | } | |
| 855 | 870 | if err := o.Context().SendRequest(o, 1); err != nil { | |
| 856 | 871 | return err | |
| 857 | 872 | } | |
| @@ -861,6 +876,9 @@ func (o *ShmPool) Destroy() error { | |||
| 861 | 876 | ||
| 862 | 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 | 878 | func (o *ShmPool) Resize(size int32) error { | |
| 879 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 880 | + | return err | |
| 881 | + | } | |
| 864 | 882 | if err := o.Context().SendRequest(o, 2, size); err != nil { | |
| 865 | 883 | return err | |
| 866 | 884 | } | |
| @@ -904,6 +922,9 @@ func NewShm(ctx *wl.Context) *Shm { | |||
| 904 | 922 | ||
| 905 | 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 | 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 | 928 | child := &ShmPool{} | |
| 908 | 929 | child.SetContext(o.Context()) | |
| 909 | 930 | child.SetID(o.Context().AllocateID()) | |
| @@ -918,6 +939,9 @@ func (o *Shm) CreatePool(fd int, size int32) (*ShmPool, error) { | |||
| 918 | 939 | ||
| 919 | 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 | 941 | func (o *Shm) Release() error { | |
| 942 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 943 | + | return err | |
| 944 | + | } | |
| 921 | 945 | if err := o.Context().SendRequest(o, 1); err != nil { | |
| 922 | 946 | return err | |
| 923 | 947 | } | |
| @@ -993,6 +1017,9 @@ func NewBuffer(ctx *wl.Context) *Buffer { | |||
| 993 | 1017 | ||
| 994 | 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 | 1019 | func (o *Buffer) Destroy() error { | |
| 1020 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1021 | + | return err | |
| 1022 | + | } | |
| 996 | 1023 | if err := o.Context().SendRequest(o, 0); err != nil { | |
| 997 | 1024 | return err | |
| 998 | 1025 | } | |
| @@ -1069,6 +1096,9 @@ func NewDataOffer(ctx *wl.Context) *DataOffer { | |||
| 1069 | 1096 | ||
| 1070 | 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 | 1098 | func (o *DataOffer) Accept(serial uint32, mimeType string) error { | |
| 1099 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1100 | + | return err | |
| 1101 | + | } | |
| 1072 | 1102 | if err := o.Context().SendRequest(o, 0, serial, mimeType); err != nil { | |
| 1073 | 1103 | return err | |
| 1074 | 1104 | } | |
| @@ -1077,6 +1107,9 @@ func (o *DataOffer) Accept(serial uint32, mimeType string) error { | |||
| 1077 | 1107 | ||
| 1078 | 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 | 1109 | func (o *DataOffer) Receive(mimeType string, fd int) error { | |
| 1110 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1111 | + | return err | |
| 1112 | + | } | |
| 1080 | 1113 | if err := o.Context().SendRequestWithFDs(o, 1, []int{fd}, mimeType, uintptr(fd)); err != nil { | |
| 1081 | 1114 | return err | |
| 1082 | 1115 | } | |
| @@ -1086,6 +1119,9 @@ func (o *DataOffer) Receive(mimeType string, fd int) error { | |||
| 1086 | 1119 | ||
| 1087 | 1120 | // Destroy the data offer. | |
| 1088 | 1121 | func (o *DataOffer) Destroy() error { | |
| 1122 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1123 | + | return err | |
| 1124 | + | } | |
| 1089 | 1125 | if err := o.Context().SendRequest(o, 2); err != nil { | |
| 1090 | 1126 | return err | |
| 1091 | 1127 | } | |
| @@ -1095,6 +1131,9 @@ func (o *DataOffer) Destroy() error { | |||
| 1095 | 1131 | ||
| 1096 | 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 | 1133 | func (o *DataOffer) Finish() error { | |
| 1134 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1135 | + | return err | |
| 1136 | + | } | |
| 1098 | 1137 | if err := o.Context().SendRequest(o, 3); err != nil { | |
| 1099 | 1138 | return err | |
| 1100 | 1139 | } | |
| @@ -1103,6 +1142,9 @@ func (o *DataOffer) Finish() error { | |||
| 1103 | 1142 | ||
| 1104 | 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 | 1144 | func (o *DataOffer) SetActions(dndActions uint32, preferredAction uint32) error { | |
| 1145 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1146 | + | return err | |
| 1147 | + | } | |
| 1106 | 1148 | if err := o.Context().SendRequest(o, 4, dndActions, preferredAction); err != nil { | |
| 1107 | 1149 | return err | |
| 1108 | 1150 | } | |
| @@ -1244,6 +1286,9 @@ func NewDataSource(ctx *wl.Context) *DataSource { | |||
| 1244 | 1286 | ||
| 1245 | 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 | 1288 | func (o *DataSource) Offer(mimeType string) error { | |
| 1289 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1290 | + | return err | |
| 1291 | + | } | |
| 1247 | 1292 | if err := o.Context().SendRequest(o, 0, mimeType); err != nil { | |
| 1248 | 1293 | return err | |
| 1249 | 1294 | } | |
| @@ -1252,6 +1297,9 @@ func (o *DataSource) Offer(mimeType string) error { | |||
| 1252 | 1297 | ||
| 1253 | 1298 | // Destroy the data source. | |
| 1254 | 1299 | func (o *DataSource) Destroy() error { | |
| 1300 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1301 | + | return err | |
| 1302 | + | } | |
| 1255 | 1303 | if err := o.Context().SendRequest(o, 1); err != nil { | |
| 1256 | 1304 | return err | |
| 1257 | 1305 | } | |
| @@ -1261,6 +1309,9 @@ func (o *DataSource) Destroy() error { | |||
| 1261 | 1309 | ||
| 1262 | 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 | 1311 | func (o *DataSource) SetActions(dndActions uint32) error { | |
| 1312 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1313 | + | return err | |
| 1314 | + | } | |
| 1264 | 1315 | if err := o.Context().SendRequest(o, 2, dndActions); err != nil { | |
| 1265 | 1316 | return err | |
| 1266 | 1317 | } | |
| @@ -1497,6 +1548,9 @@ func NewDataDevice(ctx *wl.Context) *DataDevice { | |||
| 1497 | 1548 | ||
| 1498 | 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 | 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 | 1554 | var arg0 wl.Object | |
| 1501 | 1555 | if source != nil { | |
| 1502 | 1556 | arg0 = source | |
| @@ -1517,6 +1571,9 @@ func (o *DataDevice) StartDrag(source *DataSource, origin *Surface, icon *Surfac | |||
| 1517 | 1571 | ||
| 1518 | 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 | 1573 | func (o *DataDevice) SetSelection(source *DataSource, serial uint32) error { | |
| 1574 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1575 | + | return err | |
| 1576 | + | } | |
| 1520 | 1577 | var arg0 wl.Object | |
| 1521 | 1578 | if source != nil { | |
| 1522 | 1579 | arg0 = source | |
| @@ -1529,6 +1586,9 @@ func (o *DataDevice) SetSelection(source *DataSource, serial uint32) error { | |||
| 1529 | 1586 | ||
| 1530 | 1587 | // This request destroys the data device. | |
| 1531 | 1588 | func (o *DataDevice) Release() error { | |
| 1589 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1590 | + | return err | |
| 1591 | + | } | |
| 1532 | 1592 | if err := o.Context().SendRequest(o, 2); err != nil { | |
| 1533 | 1593 | return err | |
| 1534 | 1594 | } | |
| @@ -1763,6 +1823,9 @@ func NewDataDeviceManager(ctx *wl.Context) *DataDeviceManager { | |||
| 1763 | 1823 | ||
| 1764 | 1824 | // Create a new data source. | |
| 1765 | 1825 | func (o *DataDeviceManager) CreateDataSource() (*DataSource, error) { | |
| 1826 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1827 | + | return nil, err | |
| 1828 | + | } | |
| 1766 | 1829 | child := &DataSource{} | |
| 1767 | 1830 | child.SetContext(o.Context()) | |
| 1768 | 1831 | child.SetID(o.Context().AllocateID()) | |
| @@ -1776,6 +1839,9 @@ func (o *DataDeviceManager) CreateDataSource() (*DataSource, error) { | |||
| 1776 | 1839 | ||
| 1777 | 1840 | // Create a new data device for a given seat. | |
| 1778 | 1841 | func (o *DataDeviceManager) GetDataDevice(seat *Seat) (*DataDevice, error) { | |
| 1842 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1843 | + | return nil, err | |
| 1844 | + | } | |
| 1779 | 1845 | var arg1 wl.Object | |
| 1780 | 1846 | if seat != nil { | |
| 1781 | 1847 | arg1 = seat | |
| @@ -1793,6 +1859,9 @@ func (o *DataDeviceManager) GetDataDevice(seat *Seat) (*DataDevice, error) { | |||
| 1793 | 1859 | ||
| 1794 | 1860 | // This request destroys the wl_data_device_manager. This has no effect on any other objects. | |
| 1795 | 1861 | func (o *DataDeviceManager) Release() error { | |
| 1862 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1863 | + | return err | |
| 1864 | + | } | |
| 1796 | 1865 | if err := o.Context().SendRequest(o, 2); err != nil { | |
| 1797 | 1866 | return err | |
| 1798 | 1867 | } | |
| @@ -1833,6 +1902,9 @@ func NewShell(ctx *wl.Context) *Shell { | |||
| 1833 | 1902 | ||
| 1834 | 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 | 1904 | func (o *Shell) GetShellSurface(surface *Surface) (*ShellSurface, error) { | |
| 1905 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1906 | + | return nil, err | |
| 1907 | + | } | |
| 1836 | 1908 | var arg1 wl.Object | |
| 1837 | 1909 | if surface != nil { | |
| 1838 | 1910 | arg1 = surface | |
| @@ -1887,6 +1959,9 @@ func NewShellSurface(ctx *wl.Context) *ShellSurface { | |||
| 1887 | 1959 | ||
| 1888 | 1960 | // A client must respond to a ping event with a pong request or the client may be deemed unresponsive. | |
| 1889 | 1961 | func (o *ShellSurface) Pong(serial uint32) error { | |
| 1962 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1963 | + | return err | |
| 1964 | + | } | |
| 1890 | 1965 | if err := o.Context().SendRequest(o, 0, serial); err != nil { | |
| 1891 | 1966 | return err | |
| 1892 | 1967 | } | |
| @@ -1895,6 +1970,9 @@ func (o *ShellSurface) Pong(serial uint32) error { | |||
| 1895 | 1970 | ||
| 1896 | 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 | 1972 | func (o *ShellSurface) Move(seat *Seat, serial uint32) error { | |
| 1973 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1974 | + | return err | |
| 1975 | + | } | |
| 1898 | 1976 | var arg0 wl.Object | |
| 1899 | 1977 | if seat != nil { | |
| 1900 | 1978 | arg0 = seat | |
| @@ -1907,6 +1985,9 @@ func (o *ShellSurface) Move(seat *Seat, serial uint32) error { | |||
| 1907 | 1985 | ||
| 1908 | 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 | 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 | 1991 | var arg0 wl.Object | |
| 1911 | 1992 | if seat != nil { | |
| 1912 | 1993 | arg0 = seat | |
| @@ -1919,6 +2000,9 @@ func (o *ShellSurface) Resize(seat *Seat, serial uint32, edges uint32) error { | |||
| 1919 | 2000 | ||
| 1920 | 2001 | // Map the surface as a toplevel surface. A toplevel surface is not fullscreen, maximized or transient. | |
| 1921 | 2002 | func (o *ShellSurface) SetToplevel() error { | |
| 2003 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2004 | + | return err | |
| 2005 | + | } | |
| 1922 | 2006 | if err := o.Context().SendRequest(o, 3); err != nil { | |
| 1923 | 2007 | return err | |
| 1924 | 2008 | } | |
| @@ -1927,6 +2011,9 @@ func (o *ShellSurface) SetToplevel() error { | |||
| 1927 | 2011 | ||
| 1928 | 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 | 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 | 2017 | var arg0 wl.Object | |
| 1931 | 2018 | if parent != nil { | |
| 1932 | 2019 | arg0 = parent | |
| @@ -1939,6 +2026,9 @@ func (o *ShellSurface) SetTransient(parent *Surface, x int32, y int32, flags uin | |||
| 1939 | 2026 | ||
| 1940 | 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 | 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 | 2032 | var arg2 wl.Object | |
| 1943 | 2033 | if output != nil { | |
| 1944 | 2034 | arg2 = output | |
| @@ -1951,6 +2041,9 @@ func (o *ShellSurface) SetFullscreen(method uint32, framerate uint32, output *Ou | |||
| 1951 | 2041 | ||
| 1952 | 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 | 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 | 2047 | var arg0 wl.Object | |
| 1955 | 2048 | if seat != nil { | |
| 1956 | 2049 | arg0 = seat | |
| @@ -1967,6 +2060,9 @@ func (o *ShellSurface) SetPopup(seat *Seat, serial uint32, parent *Surface, x in | |||
| 1967 | 2060 | ||
| 1968 | 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 | 2062 | func (o *ShellSurface) SetMaximized(output *Output) error { | |
| 2063 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2064 | + | return err | |
| 2065 | + | } | |
| 1970 | 2066 | var arg0 wl.Object | |
| 1971 | 2067 | if output != nil { | |
| 1972 | 2068 | arg0 = output | |
| @@ -1979,6 +2075,9 @@ func (o *ShellSurface) SetMaximized(output *Output) error { | |||
| 1979 | 2075 | ||
| 1980 | 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 | 2077 | func (o *ShellSurface) SetTitle(title string) error { | |
| 2078 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2079 | + | return err | |
| 2080 | + | } | |
| 1982 | 2081 | if err := o.Context().SendRequest(o, 8, title); err != nil { | |
| 1983 | 2082 | return err | |
| 1984 | 2083 | } | |
| @@ -1987,6 +2086,9 @@ func (o *ShellSurface) SetTitle(title string) error { | |||
| 1987 | 2086 | ||
| 1988 | 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 | 2088 | func (o *ShellSurface) SetClass(class string) error { | |
| 2089 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2090 | + | return err | |
| 2091 | + | } | |
| 1990 | 2092 | if err := o.Context().SendRequest(o, 9, class); err != nil { | |
| 1991 | 2093 | return err | |
| 1992 | 2094 | } | |
| @@ -2127,6 +2229,9 @@ func NewSurface(ctx *wl.Context) *Surface { | |||
| 2127 | 2229 | ||
| 2128 | 2230 | // Deletes the surface and invalidates its object ID. | |
| 2129 | 2231 | func (o *Surface) Destroy() error { | |
| 2232 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2233 | + | return err | |
| 2234 | + | } | |
| 2130 | 2235 | if err := o.Context().SendRequest(o, 0); err != nil { | |
| 2131 | 2236 | return err | |
| 2132 | 2237 | } | |
| @@ -2136,6 +2241,9 @@ func (o *Surface) Destroy() error { | |||
| 2136 | 2241 | ||
| 2137 | 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 | 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 | 2247 | var arg0 wl.Object | |
| 2140 | 2248 | if buffer != nil { | |
| 2141 | 2249 | arg0 = buffer | |
| @@ -2148,6 +2256,9 @@ func (o *Surface) Attach(buffer *Buffer, x int32, y int32) error { | |||
| 2148 | 2256 | ||
| 2149 | 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 | 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 | 2262 | if err := o.Context().SendRequest(o, 2, x, y, width, height); err != nil { | |
| 2152 | 2263 | return err | |
| 2153 | 2264 | } | |
| @@ -2156,6 +2267,9 @@ func (o *Surface) Damage(x int32, y int32, width int32, height int32) error { | |||
| 2156 | 2267 | ||
| 2157 | 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 | 2269 | func (o *Surface) Frame() (*Callback, error) { | |
| 2270 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2271 | + | return nil, err | |
| 2272 | + | } | |
| 2159 | 2273 | child := &Callback{} | |
| 2160 | 2274 | child.SetContext(o.Context()) | |
| 2161 | 2275 | child.SetID(o.Context().AllocateID()) | |
| @@ -2169,6 +2283,9 @@ func (o *Surface) Frame() (*Callback, error) { | |||
| 2169 | 2283 | ||
| 2170 | 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 | 2285 | func (o *Surface) SetOpaqueRegion(region *Region) error { | |
| 2286 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2287 | + | return err | |
| 2288 | + | } | |
| 2172 | 2289 | var arg0 wl.Object | |
| 2173 | 2290 | if region != nil { | |
| 2174 | 2291 | arg0 = region | |
| @@ -2181,6 +2298,9 @@ func (o *Surface) SetOpaqueRegion(region *Region) error { | |||
| 2181 | 2298 | ||
| 2182 | 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 | 2300 | func (o *Surface) SetInputRegion(region *Region) error { | |
| 2301 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2302 | + | return err | |
| 2303 | + | } | |
| 2184 | 2304 | var arg0 wl.Object | |
| 2185 | 2305 | if region != nil { | |
| 2186 | 2306 | arg0 = region | |
| @@ -2193,6 +2313,9 @@ func (o *Surface) SetInputRegion(region *Region) error { | |||
| 2193 | 2313 | ||
| 2194 | 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 | 2315 | func (o *Surface) Commit() error { | |
| 2316 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2317 | + | return err | |
| 2318 | + | } | |
| 2196 | 2319 | if err := o.Context().SendRequest(o, 6); err != nil { | |
| 2197 | 2320 | return err | |
| 2198 | 2321 | } | |
| @@ -2201,6 +2324,9 @@ func (o *Surface) Commit() error { | |||
| 2201 | 2324 | ||
| 2202 | 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 | 2326 | func (o *Surface) SetBufferTransform(transform int32) error { | |
| 2327 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2328 | + | return err | |
| 2329 | + | } | |
| 2204 | 2330 | if err := o.Context().SendRequest(o, 7, transform); err != nil { | |
| 2205 | 2331 | return err | |
| 2206 | 2332 | } | |
| @@ -2209,6 +2335,9 @@ func (o *Surface) SetBufferTransform(transform int32) error { | |||
| 2209 | 2335 | ||
| 2210 | 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 | 2337 | func (o *Surface) SetBufferScale(scale int32) error { | |
| 2338 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2339 | + | return err | |
| 2340 | + | } | |
| 2212 | 2341 | if err := o.Context().SendRequest(o, 8, scale); err != nil { | |
| 2213 | 2342 | return err | |
| 2214 | 2343 | } | |
| @@ -2217,6 +2346,9 @@ func (o *Surface) SetBufferScale(scale int32) error { | |||
| 2217 | 2346 | ||
| 2218 | 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 | 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 | 2352 | if err := o.Context().SendRequest(o, 9, x, y, width, height); err != nil { | |
| 2221 | 2353 | return err | |
| 2222 | 2354 | } | |
| @@ -2225,6 +2357,9 @@ func (o *Surface) DamageBuffer(x int32, y int32, width int32, height int32) erro | |||
| 2225 | 2357 | ||
| 2226 | 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 | 2359 | func (o *Surface) Offset(x int32, y int32) error { | |
| 2360 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2361 | + | return err | |
| 2362 | + | } | |
| 2228 | 2363 | if err := o.Context().SendRequest(o, 10, x, y); err != nil { | |
| 2229 | 2364 | return err | |
| 2230 | 2365 | } | |
| @@ -2233,6 +2368,9 @@ func (o *Surface) Offset(x int32, y int32) error { | |||
| 2233 | 2368 | ||
| 2234 | 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 | 2370 | func (o *Surface) GetRelease() (*Callback, error) { | |
| 2371 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2372 | + | return nil, err | |
| 2373 | + | } | |
| 2236 | 2374 | child := &Callback{} | |
| 2237 | 2375 | child.SetContext(o.Context()) | |
| 2238 | 2376 | child.SetID(o.Context().AllocateID()) | |
| @@ -2406,6 +2544,9 @@ func NewSeat(ctx *wl.Context) *Seat { | |||
| 2406 | 2544 | ||
| 2407 | 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 | 2546 | func (o *Seat) GetPointer() (*Pointer, error) { | |
| 2547 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2548 | + | return nil, err | |
| 2549 | + | } | |
| 2409 | 2550 | child := &Pointer{} | |
| 2410 | 2551 | child.SetContext(o.Context()) | |
| 2411 | 2552 | child.SetID(o.Context().AllocateID()) | |
| @@ -2419,6 +2560,9 @@ func (o *Seat) GetPointer() (*Pointer, error) { | |||
| 2419 | 2560 | ||
| 2420 | 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 | 2562 | func (o *Seat) GetKeyboard() (*Keyboard, error) { | |
| 2563 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2564 | + | return nil, err | |
| 2565 | + | } | |
| 2422 | 2566 | child := &Keyboard{} | |
| 2423 | 2567 | child.SetContext(o.Context()) | |
| 2424 | 2568 | child.SetID(o.Context().AllocateID()) | |
| @@ -2432,6 +2576,9 @@ func (o *Seat) GetKeyboard() (*Keyboard, error) { | |||
| 2432 | 2576 | ||
| 2433 | 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 | 2578 | func (o *Seat) GetTouch() (*Touch, error) { | |
| 2579 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2580 | + | return nil, err | |
| 2581 | + | } | |
| 2435 | 2582 | child := &Touch{} | |
| 2436 | 2583 | child.SetContext(o.Context()) | |
| 2437 | 2584 | child.SetID(o.Context().AllocateID()) | |
| @@ -2445,6 +2592,9 @@ func (o *Seat) GetTouch() (*Touch, error) { | |||
| 2445 | 2592 | ||
| 2446 | 2593 | // Using this request a client can tell the server that it is not going to use the seat object anymore. | |
| 2447 | 2594 | func (o *Seat) Release() error { | |
| 2595 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2596 | + | return err | |
| 2597 | + | } | |
| 2448 | 2598 | if err := o.Context().SendRequest(o, 3); err != nil { | |
| 2449 | 2599 | return err | |
| 2450 | 2600 | } | |
| @@ -2562,6 +2712,9 @@ func NewPointer(ctx *wl.Context) *Pointer { | |||
| 2562 | 2712 | ||
| 2563 | 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 | 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 | 2718 | var arg1 wl.Object | |
| 2566 | 2719 | if surface != nil { | |
| 2567 | 2720 | arg1 = surface | |
| @@ -2574,6 +2727,9 @@ func (o *Pointer) SetCursor(serial uint32, surface *Surface, hotspotX int32, hot | |||
| 2574 | 2727 | ||
| 2575 | 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 | 2729 | func (o *Pointer) Release() error { | |
| 2730 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 2731 | + | return err | |
| 2732 | + | } | |
| 2577 | 2733 | if err := o.Context().SendRequest(o, 1); err != nil { | |
| 2578 | 2734 | return err | |
| 2579 | 2735 | } | |
| @@ -3010,6 +3166,9 @@ func NewKeyboard(ctx *wl.Context) *Keyboard { | |||
| 3010 | 3166 | ||
| 3011 | 3167 | // release the keyboard object | |
| 3012 | 3168 | func (o *Keyboard) Release() error { | |
| 3169 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 3170 | + | return err | |
| 3171 | + | } | |
| 3013 | 3172 | if err := o.Context().SendRequest(o, 0); err != nil { | |
| 3014 | 3173 | return err | |
| 3015 | 3174 | } | |
| @@ -3263,6 +3422,9 @@ func NewTouch(ctx *wl.Context) *Touch { | |||
| 3263 | 3422 | ||
| 3264 | 3423 | // release the touch object | |
| 3265 | 3424 | func (o *Touch) Release() error { | |
| 3425 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 3426 | + | return err | |
| 3427 | + | } | |
| 3266 | 3428 | if err := o.Context().SendRequest(o, 0); err != nil { | |
| 3267 | 3429 | return err | |
| 3268 | 3430 | } | |
| @@ -3540,6 +3702,9 @@ func NewOutput(ctx *wl.Context) *Output { | |||
| 3540 | 3702 | ||
| 3541 | 3703 | // Using this request a client can tell the server that it is not going to use the output object anymore. | |
| 3542 | 3704 | func (o *Output) Release() error { | |
| 3705 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 3706 | + | return err | |
| 3707 | + | } | |
| 3543 | 3708 | if err := o.Context().SendRequest(o, 0); err != nil { | |
| 3544 | 3709 | return err | |
| 3545 | 3710 | } | |
| @@ -3775,6 +3940,9 @@ func NewRegion(ctx *wl.Context) *Region { | |||
| 3775 | 3940 | ||
| 3776 | 3941 | // Destroy the region. This will invalidate the object ID. | |
| 3777 | 3942 | func (o *Region) Destroy() error { | |
| 3943 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 3944 | + | return err | |
| 3945 | + | } | |
| 3778 | 3946 | if err := o.Context().SendRequest(o, 0); err != nil { | |
| 3779 | 3947 | return err | |
| 3780 | 3948 | } | |
| @@ -3784,6 +3952,9 @@ func (o *Region) Destroy() error { | |||
| 3784 | 3952 | ||
| 3785 | 3953 | // Add the specified rectangle to the region. | |
| 3786 | 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 | 3958 | if err := o.Context().SendRequest(o, 1, x, y, width, height); err != nil { | |
| 3788 | 3959 | return err | |
| 3789 | 3960 | } | |
| @@ -3792,6 +3963,9 @@ func (o *Region) Add(x int32, y int32, width int32, height int32) error { | |||
| 3792 | 3963 | ||
| 3793 | 3964 | // Subtract the specified rectangle from the region. | |
| 3794 | 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 | 3969 | if err := o.Context().SendRequest(o, 2, x, y, width, height); err != nil { | |
| 3796 | 3970 | return err | |
| 3797 | 3971 | } | |
| @@ -3831,6 +4005,9 @@ func NewSubcompositor(ctx *wl.Context) *Subcompositor { | |||
| 3831 | 4005 | ||
| 3832 | 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 | 4007 | func (o *Subcompositor) Destroy() error { | |
| 4008 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 4009 | + | return err | |
| 4010 | + | } | |
| 3834 | 4011 | if err := o.Context().SendRequest(o, 0); err != nil { | |
| 3835 | 4012 | return err | |
| 3836 | 4013 | } | |
| @@ -3840,6 +4017,9 @@ func (o *Subcompositor) Destroy() error { | |||
| 3840 | 4017 | ||
| 3841 | 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 | 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 | 4023 | var arg1 wl.Object | |
| 3844 | 4024 | if surface != nil { | |
| 3845 | 4025 | arg1 = surface | |
| @@ -3892,6 +4072,9 @@ func NewSubsurface(ctx *wl.Context) *Subsurface { | |||
| 3892 | 4072 | ||
| 3893 | 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 | 4074 | func (o *Subsurface) Destroy() error { | |
| 4075 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 4076 | + | return err | |
| 4077 | + | } | |
| 3895 | 4078 | if err := o.Context().SendRequest(o, 0); err != nil { | |
| 3896 | 4079 | return err | |
| 3897 | 4080 | } | |
| @@ -3901,6 +4084,9 @@ func (o *Subsurface) Destroy() error { | |||
| 3901 | 4084 | ||
| 3902 | 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 | 4086 | func (o *Subsurface) SetPosition(x int32, y int32) error { | |
| 4087 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 4088 | + | return err | |
| 4089 | + | } | |
| 3904 | 4090 | if err := o.Context().SendRequest(o, 1, x, y); err != nil { | |
| 3905 | 4091 | return err | |
| 3906 | 4092 | } | |
| @@ -3909,6 +4095,9 @@ func (o *Subsurface) SetPosition(x int32, y int32) error { | |||
| 3909 | 4095 | ||
| 3910 | 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 | 4097 | func (o *Subsurface) PlaceAbove(sibling *Surface) error { | |
| 4098 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 4099 | + | return err | |
| 4100 | + | } | |
| 3912 | 4101 | var arg0 wl.Object | |
| 3913 | 4102 | if sibling != nil { | |
| 3914 | 4103 | arg0 = sibling | |
| @@ -3921,6 +4110,9 @@ func (o *Subsurface) PlaceAbove(sibling *Surface) error { | |||
| 3921 | 4110 | ||
| 3922 | 4111 | // The sub-surface is placed just below the reference surface. See wl_subsurface.place_above. | |
| 3923 | 4112 | func (o *Subsurface) PlaceBelow(sibling *Surface) error { | |
| 4113 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 4114 | + | return err | |
| 4115 | + | } | |
| 3924 | 4116 | var arg0 wl.Object | |
| 3925 | 4117 | if sibling != nil { | |
| 3926 | 4118 | arg0 = sibling | |
| @@ -3933,6 +4125,9 @@ func (o *Subsurface) PlaceBelow(sibling *Surface) error { | |||
| 3933 | 4125 | ||
| 3934 | 4126 | // Change the commit behaviour of the sub-surface to synchronized mode. See wl_subsurface and wl_surface.commit for more information. | |
| 3935 | 4127 | func (o *Subsurface) SetSync() error { | |
| 4128 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 4129 | + | return err | |
| 4130 | + | } | |
| 3936 | 4131 | if err := o.Context().SendRequest(o, 4); err != nil { | |
| 3937 | 4132 | return err | |
| 3938 | 4133 | } | |
| @@ -3941,6 +4136,9 @@ func (o *Subsurface) SetSync() error { | |||
| 3941 | 4136 | ||
| 3942 | 4137 | // Change the commit behaviour of the sub-surface to desynchronized mode. See wl_subsurface and wl_surface.commit for more information. | |
| 3943 | 4138 | func (o *Subsurface) SetDesync() error { | |
| 4139 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 4140 | + | return err | |
| 4141 | + | } | |
| 3944 | 4142 | if err := o.Context().SendRequest(o, 5); err != nil { | |
| 3945 | 4143 | return err | |
| 3946 | 4144 | } | |
| @@ -3980,6 +4178,9 @@ func NewFixes(ctx *wl.Context) *Fixes { | |||
| 3980 | 4178 | ||
| 3981 | 4179 | // destroys this object | |
| 3982 | 4180 | func (o *Fixes) Destroy() error { | |
| 4181 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 4182 | + | return err | |
| 4183 | + | } | |
| 3983 | 4184 | if err := o.Context().SendRequest(o, 0); err != nil { | |
| 3984 | 4185 | return err | |
| 3985 | 4186 | } | |
| @@ -3989,6 +4190,9 @@ func (o *Fixes) Destroy() error { | |||
| 3989 | 4190 | ||
| 3990 | 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 | 4192 | func (o *Fixes) DestroyRegistry(registry *wl.Registry) error { | |
| 4193 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 4194 | + | return err | |
| 4195 | + | } | |
| 3992 | 4196 | var arg0 wl.Object | |
| 3993 | 4197 | if registry != nil { | |
| 3994 | 4198 | arg0 = registry | |
| @@ -4001,6 +4205,9 @@ func (o *Fixes) DestroyRegistry(registry *wl.Registry) error { | |||
| 4001 | 4205 | ||
| 4002 | 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 | 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 | 4211 | var arg0 wl.Object | |
| 4005 | 4212 | if registry != nil { | |
| 4006 | 4213 | 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 err | 812 | 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 err | 871 | 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 err | 883 | 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 err | 946 | 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 err | 1024 | 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 err | 1103 | 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 err | 1114 | 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 err | 1126 | 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 err | 1138 | 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 err | 1149 | 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 err | 1293 | 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 err | 1304 | 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 err | 1316 | 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.Object | 1554 | var arg0 wl.Object | ||
| 1501 | if source != nil { | 1555 | if source != nil { | ||
| 1502 | arg0 = source | 1556 | 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.Object | 1577 | var arg0 wl.Object | ||
| 1521 | if source != nil { | 1578 | if source != nil { | ||
| 1522 | arg0 = source | 1579 | 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 err | 1593 | 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.Object | 1845 | var arg1 wl.Object | ||
| 1780 | if seat != nil { | 1846 | if seat != nil { | ||
| 1781 | arg1 = seat | 1847 | 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 err | 1866 | 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.Object | 1908 | var arg1 wl.Object | ||
| 1837 | if surface != nil { | 1909 | if surface != nil { | ||
| 1838 | arg1 = surface | 1910 | 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 err | 1966 | 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.Object | 1976 | var arg0 wl.Object | ||
| 1899 | if seat != nil { | 1977 | if seat != nil { | ||
| 1900 | arg0 = seat | 1978 | 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.Object | 1991 | var arg0 wl.Object | ||
| 1911 | if seat != nil { | 1992 | if seat != nil { | ||
| 1912 | arg0 = seat | 1993 | 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 err | 2007 | 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.Object | 2017 | var arg0 wl.Object | ||
| 1931 | if parent != nil { | 2018 | if parent != nil { | ||
| 1932 | arg0 = parent | 2019 | 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.Object | 2032 | var arg2 wl.Object | ||
| 1943 | if output != nil { | 2033 | if output != nil { | ||
| 1944 | arg2 = output | 2034 | 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.Object | 2047 | var arg0 wl.Object | ||
| 1955 | if seat != nil { | 2048 | if seat != nil { | ||
| 1956 | arg0 = seat | 2049 | 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.Object | 2066 | var arg0 wl.Object | ||
| 1971 | if output != nil { | 2067 | if output != nil { | ||
| 1972 | arg0 = output | 2068 | 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 err | 2082 | 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 err | 2093 | 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 err | 2236 | 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.Object | 2247 | var arg0 wl.Object | ||
| 2140 | if buffer != nil { | 2248 | if buffer != nil { | ||
| 2141 | arg0 = buffer | 2249 | 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 err | 2263 | 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.Object | 2289 | var arg0 wl.Object | ||
| 2173 | if region != nil { | 2290 | if region != nil { | ||
| 2174 | arg0 = region | 2291 | 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.Object | 2304 | var arg0 wl.Object | ||
| 2185 | if region != nil { | 2305 | if region != nil { | ||
| 2186 | arg0 = region | 2306 | 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 err | 2320 | 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 err | 2331 | 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 err | 2342 | 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 err | 2353 | 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 err | 2364 | 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 err | 2599 | 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.Object | 2718 | var arg1 wl.Object | ||
| 2566 | if surface != nil { | 2719 | if surface != nil { | ||
| 2567 | arg1 = surface | 2720 | 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 err | 2734 | return err | ||
| 2579 | } | 2735 | } | ||
| @@ -3010,6 +3166,9 @@ func NewKeyboard(ctx *wl.Context) *Keyboard { | |||||
| 3010 | 3166 | ||||
| 3011 | // release the keyboard object | 3167 | // 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 err | 3173 | return err | ||
| 3015 | } | 3174 | } | ||
| @@ -3263,6 +3422,9 @@ func NewTouch(ctx *wl.Context) *Touch { | |||||
| 3263 | 3422 | ||||
| 3264 | // release the touch object | 3423 | // 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 err | 3429 | 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 err | 3709 | 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 err | 3947 | 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 err | 3959 | 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 err | 3970 | 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 err | 4012 | 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.Object | 4023 | var arg1 wl.Object | ||
| 3844 | if surface != nil { | 4024 | if surface != nil { | ||
| 3845 | arg1 = surface | 4025 | 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 err | 4079 | 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 err | 4091 | 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.Object | 4101 | var arg0 wl.Object | ||
| 3913 | if sibling != nil { | 4102 | if sibling != nil { | ||
| 3914 | arg0 = sibling | 4103 | 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.Object | 4116 | var arg0 wl.Object | ||
| 3925 | if sibling != nil { | 4117 | if sibling != nil { | ||
| 3926 | arg0 = sibling | 4118 | 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 err | 4132 | 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 err | 4143 | return err | ||
| 3946 | } | 4144 | } | ||
| @@ -3980,6 +4178,9 @@ func NewFixes(ctx *wl.Context) *Fixes { | |||||
| 3980 | 4178 | ||||
| 3981 | // destroys this object | 4179 | // 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 err | 4185 | 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.Object | 4196 | var arg0 wl.Object | ||
| 3993 | if registry != nil { | 4197 | if registry != nil { | ||
| 3994 | arg0 = registry | 4198 | 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.Object | 4211 | var arg0 wl.Object | ||
| 4005 | if registry != nil { | 4212 | if registry != nil { | ||
| 4006 | arg0 = registry | 4213 | arg0 = registry | ||
M scm_linux.go +44 -27
| @@ -10,6 +10,7 @@ import ( | |||
| 10 | 10 | "net" | |
| 11 | 11 | "sync" | |
| 12 | 12 | "syscall" | |
| 13 | + | "unsafe" | |
| 13 | 14 | ||
| 14 | 15 | "golang.org/x/sys/unix" | |
| 15 | 16 | ) | |
| @@ -19,10 +20,12 @@ import ( | |||
| 19 | 20 | // instead of silently dropping descriptors. | |
| 20 | 21 | const maxRecvFDs = 64 | |
| 21 | 22 | ||
| 23 | + | type controlBuffer struct{ data []byte } | |
| 24 | + | ||
| 22 | 25 | // controlBufferPool provides control-message buffers sized for maxRecvFDs. | |
| 23 | 26 | var controlBufferPool = sync.Pool{ | |
| 24 | 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 | 46 | return n, err | |
| 44 | 47 | } | |
| 45 | 48 | ||
| 46 | - | oob := controlBufferPool.Get().([]byte) | |
| 47 | - | defer controlBufferPool.Put(oob) | |
| 49 | + | buffer := controlBufferPool.Get().(*controlBuffer) | |
| 50 | + | defer controlBufferPool.Put(buffer) | |
| 51 | + | oob := buffer.data | |
| 48 | 52 | ||
| 49 | 53 | n, oobn, flags, _, err := d.unix.ReadMsgUnix(d.recvBuf, oob) | |
| 50 | 54 | if n < 0 { | |
| @@ -54,12 +58,12 @@ func (d *Display) readChunk() (int, error) { | |||
| 54 | 58 | if oobn > 0 { | |
| 55 | 59 | fds, perr := parseFDs(oob[:oobn]) | |
| 56 | 60 | d.queueFDs(fds) | |
| 57 | - | if perr != nil && err == nil { | |
| 58 | - | err = perr | |
| 61 | + | if perr != nil { | |
| 62 | + | 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 | 67 | // Descriptors were dropped by the kernel, so the connection can no | |
| 64 | 68 | // longer be demarshalled correctly: treat it as a protocol failure so | |
| 65 | 69 | // the caller closes the descriptors that did arrive. | |
| @@ -71,37 +75,50 @@ func (d *Display) readChunk() (int, error) { | |||
| 71 | 75 | // parseFDs extracts the file descriptors carried by socket control messages. | |
| 72 | 76 | // Descriptors parsed before an error are returned so the caller can close them. | |
| 73 | 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 | 78 | 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") | |
| 84 | 82 | } | |
| 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) | |
| 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 | 107 | return fds, nil | |
| 92 | 108 | } | |
| 93 | 109 | ||
| 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") | |
| 99 | 114 | } | |
| 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 | |
| 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 | 122 | // closePendingFDs is called under recvMu (including during shutdown). | |
| 106 | 123 | func (d *Display) closePendingFDs() { | |
| 107 | 124 | 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 = 64 | 21 | 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, err | 46 | 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 = perr | 62 | + | 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 no | 67 | // Descriptors were dropped by the kernel, so the connection can no | ||
| 64 | // longer be demarshalled correctly: treat it as a protocol failure so | 68 | // 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 []int | 78 | 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, nil | 107 | 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.pendingFDs | 124 | fds := d.pendingFDs | ||
M transport_test.go +40 -4
| @@ -93,10 +93,12 @@ func newChunkDisplay(chunks [][]byte) (*Display, *[]EventRecord) { | |||
| 93 | 93 | records: &records, | |
| 94 | 94 | }) | |
| 95 | 95 | 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 | + | } | |
| 100 | 102 | return d, &records | |
| 101 | 103 | } | |
| 102 | 104 | ||
| @@ -279,6 +281,10 @@ func TestDisplayDispatch_DisplayError(t *testing.T) { | |||
| 279 | 281 | ||
| 280 | 282 | func TestDisplayDispatch_DeleteID(t *testing.T) { | |
| 281 | 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 | 289 | if err := d.Dispatch(); err != nil { | |
| 284 | 290 | t.Fatalf("Dispatch: %v", err) | |
| @@ -355,3 +361,33 @@ func TestReadFrame(t *testing.T) { | |||
| 355 | 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 | + | } | |
| @@ -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, &records | 102 | 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 | + | } | |||