diff --git a/internal/pipewire/core.go b/internal/pipewire/core.go index 99e4137..a15d080 100644 --- a/internal/pipewire/core.go +++ b/internal/pipewire/core.go @@ -167,6 +167,42 @@ func (c *CorePing) MarshalBinary() ([]byte, error) { return Marshal(c) } // UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal]. func (c *CorePing) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) } +// The CoreError can be emitted by both the client and the server. +// +// When emitted by the server, the error event is sent out when a fatal +// (non-recoverable) error has occurred. The id argument is the proxy +// object where the error occurred, most often in response to a request +// to that object. The message is a brief description of the error, for +// (debugging) convenience. +// +// When emitted by the client, it indicates an error occurred in an +// object on the client. +type CoreError struct { + // The id of the resource (proxy if emitted by the client) that is in error. + ID Int `json:"id"` + // A seq number from the failing request (if any). + Sequence Int `json:"seq"` + // A negative errno style error code. + Result Int `json:"res"` + // An error message. + Message String `json:"message"` +} + +// Size satisfies [KnownSize] with a value computed at runtime. +func (c *CoreError) Size() Word { + return SizePrefix + + Size(SizeInt) + + Size(SizeInt) + + Size(SizeInt) + + SizeString[Word](c.Message) +} + +// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal]. +func (c *CoreError) MarshalBinary() ([]byte, error) { return Marshal(c) } + +// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal]. +func (c *CoreError) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) } + // The CoreBoundProps event is emitted when a local object ID is bound to a global ID. // It is emitted before the global becomes visible in the registry. type CoreBoundProps struct { diff --git a/internal/pipewire/core_test.go b/internal/pipewire/core_test.go index d2a1a0d..9771eff 100644 --- a/internal/pipewire/core_test.go +++ b/internal/pipewire/core_test.go @@ -144,6 +144,52 @@ func TestCorePing(t *testing.T) { }.run(t) } +func TestCoreError(t *testing.T) { + t.Parallel() + + encodingTestCases[pipewire.CoreError, *pipewire.CoreError]{ + // captured from pw-cli + {"pw-cli", []byte{ + /* size: rest of data */ 0x58, 0, 0, 0, + /* type: Struct */ 0xe, 0, 0, 0, + + /* size: 4 bytes */ 4, 0, 0, 0, + /* type: Int */ 4, 0, 0, 0, + /* value: 2 */ 2, 0, 0, 0, + /* padding */ 0, 0, 0, 0, + + /* size: 4 bytes */ 4, 0, 0, 0, + /* type: Int */ 4, 0, 0, 0, + /* value: 0x67 */ 0x67, 0, 0, 0, + /* padding */ 0, 0, 0, 0, + + /* size: 4 bytes */ 4, 0, 0, 0, + /* type: Int */ 4, 0, 0, 0, + /* value: -1 */ 0xff, 0xff, 0xff, 0xff, + /* padding */ 0, 0, 0, 0, + + /* size: 0x1b bytes */ 0x1b, 0, 0, 0, + /*type: String*/ 8, 0, 0, 0, + + // value: "no permission to destroy 0\x00" + 0x6e, 0x6f, 0x20, 0x70, + 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x20, 0x74, 0x6f, + 0x20, 0x64, 0x65, 0x73, + 0x74, 0x72, 0x6f, 0x79, + 0x20, 0x30, 0, + + /* padding */ 0, 0, 0, 0, 0, + }, pipewire.CoreError{ + ID: 2, + Sequence: 0x67, + Result: -1, + Message: "no permission to destroy 0", + }, nil}, + }.run(t) +} + func TestCoreBoundProps(t *testing.T) { t.Parallel() diff --git a/internal/pipewire/header_test.go b/internal/pipewire/header_test.go index 8dc58b9..eca12d8 100644 --- a/internal/pipewire/header_test.go +++ b/internal/pipewire/header_test.go @@ -336,6 +336,20 @@ func TestHeader(t *testing.T) { Size: 0x28, Sequence: 44, FileCount: 0, }, nil}, + /* excerpts */ + + {"PW_CORE_EVENT_ERROR", []byte{ + /* Id: */ 0, 0, 0, 0, + /* size: */ 0x60, 0, 0, + /* opcode: */ 3, + /* seq: */ 0xf5, 0, 0, 0, + /* n_fds: */ 0, 0, 0, 0, + }, pipewire.Header{ + ID: pipewire.PW_ID_CORE, + Opcode: pipewire.PW_CORE_EVENT_ERROR, + Size: 0x60, Sequence: 0xf5, FileCount: 0, + }, nil}, + /* handmade samples */ {"PW_CORE_EVENT_PING", []byte{ @@ -350,18 +364,6 @@ func TestHeader(t *testing.T) { Size: 0xbed, Sequence: 0xffff, FileCount: 0xcafe, }, nil}, - {"PW_CORE_EVENT_ERROR", []byte{ - /* Id: */ 0, 0, 0, 0, - /* size: */ 0xad, 0xb, 0, - /* opcode: */ 3, - /* seq: */ 0xfe, 0xfe, 0, 0, - /* n_fds: */ 0xfe, 0xca, 0, 0, - }, pipewire.Header{ - ID: pipewire.PW_ID_CORE, - Opcode: pipewire.PW_CORE_EVENT_ERROR, - Size: 0xbad, Sequence: 0xfefe, FileCount: 0xcafe, - }, nil}, - {"PW_CORE_METHOD_PONG", []byte{ /* Id: */ 0, 0, 0, 0, /* size: */ 0xed, 0xb, 0,