diff --git a/internal/pipewire/core.go b/internal/pipewire/core.go index 7c2b4a9..99e4137 100644 --- a/internal/pipewire/core.go +++ b/internal/pipewire/core.go @@ -136,7 +136,7 @@ type CoreDone struct { // Passed from [CoreSync.ID]. ID Int `json:"id"` // Passed from [CoreSync.Sequence]. - Sequence Int `json:"sequence"` + Sequence Int `json:"seq"` } // Size satisfies [KnownSize] with a constant value. @@ -148,6 +148,25 @@ func (c *CoreDone) MarshalBinary() ([]byte, error) { return Marshal(c) } // UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal]. func (c *CoreDone) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) } +// The CorePing event is emitted by the server when it wants to check if a client is +// alive or ensure that it has processed the previous events. +type CorePing struct { + // The object id to ping. + ID Int `json:"id"` + // Usually automatically generated. + // The client should pass this in the Pong method reply. + Sequence Int `json:"seq"` +} + +// Size satisfies [KnownSize] with a constant value. +func (c *CorePing) Size() Word { return SizePrefix + Size(SizeInt) + Size(SizeInt) } + +// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal]. +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 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 { @@ -200,7 +219,7 @@ type CoreSync struct { // The id will be returned in the Done event. ID Int `json:"id"` // Usually generated automatically and will be returned in the Done event. - Sequence Int `json:"sequence"` + Sequence Int `json:"seq"` } // Size satisfies [KnownSize] with a constant value. @@ -212,6 +231,23 @@ func (c *CoreSync) MarshalBinary() ([]byte, error) { return Marshal(c) } // UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal]. func (c *CoreSync) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) } +// The CorePong message is sent from the client to the server when the server emits the Ping event. +type CorePong struct { + // Copied from [CorePing.ID]. + ID Int `json:"id"` + // Copied from [CorePing.Sequence] + Sequence Int `json:"seq"` +} + +// Size satisfies [KnownSize] with a constant value. +func (c *CorePong) Size() Word { return SizePrefix + Size(SizeInt) + Size(SizeInt) } + +// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal]. +func (c *CorePong) MarshalBinary() ([]byte, error) { return Marshal(c) } + +// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal]. +func (c *CorePong) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) } + // CoreGetRegistry is sent when a client requests to bind to the // registry object and list the available objects on the server. // diff --git a/internal/pipewire/core_test.go b/internal/pipewire/core_test.go index be3f1f9..d2a1a0d 100644 --- a/internal/pipewire/core_test.go +++ b/internal/pipewire/core_test.go @@ -119,6 +119,31 @@ func TestCoreDone(t *testing.T) { }.run(t) } +func TestCorePing(t *testing.T) { + t.Parallel() + + encodingTestCases[pipewire.CorePing, *pipewire.CorePing]{ + // handmade sample + {"sample", []byte{ + /* size: rest of data */ 0x20, 0, 0, 0, + /* type: Struct */ pipewire.SPA_TYPE_Struct, 0, 0, 0, + + /* size: 4 bytes */ 4, 0, 0, 0, + /* type: Int */ pipewire.SPA_TYPE_Int, 0, 0, 0, + /* value: -1 */ 0xff, 0xff, 0xff, 0xff, + /* padding */ 0, 0, 0, 0, + + /* size: 4 bytes */ 4, 0, 0, 0, + /* type: Int */ pipewire.SPA_TYPE_Int, 0, 0, 0, + /* value: 0 */ 0, 0, 0, 0, + /* padding */ 0, 0, 0, 0, + }, pipewire.CorePing{ + ID: -1, + Sequence: 0, + }, nil}, + }.run(t) +} + func TestCoreBoundProps(t *testing.T) { t.Parallel() @@ -177,6 +202,31 @@ func TestCoreSync(t *testing.T) { }.run(t) } +func TestCorePong(t *testing.T) { + t.Parallel() + + encodingTestCases[pipewire.CorePong, *pipewire.CorePong]{ + // handmade sample + {"sample", []byte{ + /* size: rest of data */ 0x20, 0, 0, 0, + /* type: Struct */ pipewire.SPA_TYPE_Struct, 0, 0, 0, + + /* size: 4 bytes */ 4, 0, 0, 0, + /* type: Int */ pipewire.SPA_TYPE_Int, 0, 0, 0, + /* value: -1 */ 0xff, 0xff, 0xff, 0xff, + /* padding */ 0, 0, 0, 0, + + /* size: 4 bytes */ 4, 0, 0, 0, + /* type: Int */ pipewire.SPA_TYPE_Int, 0, 0, 0, + /* value: 0 */ 0, 0, 0, 0, + /* padding */ 0, 0, 0, 0, + }, pipewire.CorePong{ + ID: -1, + Sequence: 0, + }, nil}, + }.run(t) +} + func TestCoreGetRegistry(t *testing.T) { t.Parallel() diff --git a/internal/pipewire/header_test.go b/internal/pipewire/header_test.go index f0bd20d..8dc58b9 100644 --- a/internal/pipewire/header_test.go +++ b/internal/pipewire/header_test.go @@ -335,6 +335,56 @@ func TestHeader(t *testing.T) { Opcode: pipewire.PW_CORE_EVENT_DONE, Size: 0x28, Sequence: 44, FileCount: 0, }, nil}, + + /* handmade samples */ + + {"PW_CORE_EVENT_PING", []byte{ + /* Id: */ 0, 0, 0, 0, + /* size: */ 0xed, 0xb, 0, + /* opcode: */ 2, + /* seq: */ 0xff, 0xff, 0, 0, + /* n_fds: */ 0xfe, 0xca, 0, 0, + }, pipewire.Header{ + ID: pipewire.PW_ID_CORE, + Opcode: pipewire.PW_CORE_EVENT_PING, + 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, + /* opcode: */ 3, + /* seq: */ 0xff, 0xff, 0, 0, + /* n_fds: */ 0xfe, 0xca, 0, 0, + }, pipewire.Header{ + ID: pipewire.PW_ID_CORE, + Opcode: pipewire.PW_CORE_METHOD_PONG, + Size: 0xbed, Sequence: 0xffff, FileCount: 0xcafe, + }, nil}, + + {"PW_CORE_METHOD_ERROR", []byte{ + /* Id: */ 0, 0, 0, 0, + /* size: */ 0xad, 0xb, 0, + /* opcode: */ 4, + /* seq: */ 0xfe, 0xfe, 0, 0, + /* n_fds: */ 0xfe, 0xca, 0, 0, + }, pipewire.Header{ + ID: pipewire.PW_ID_CORE, + Opcode: pipewire.PW_CORE_METHOD_ERROR, + Size: 0xbad, Sequence: 0xfefe, FileCount: 0xcafe, + }, nil}, }.run(t) t.Run("size range", func(t *testing.T) {