From a27dfdc058caf4ca0578a7cf98282c1998c94e6b Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sun, 14 Dec 2025 08:10:57 +0900 Subject: [PATCH] internal/pipewire: implement Core::CreateObject Nothing uses this right now, this would have to be called by wrapper methods on Registry that would search the objects Signed-off-by: Ophestra --- internal/pipewire/core.go | 52 +++++++++++++++++++++++++++++++ internal/pipewire/core_test.go | 56 +++++++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/internal/pipewire/core.go b/internal/pipewire/core.go index 80bd217..6adcac1 100644 --- a/internal/pipewire/core.go +++ b/internal/pipewire/core.go @@ -572,6 +572,58 @@ func (ctx *Context) GetRegistry() (*Registry, error) { ) } +// CoreCreateObject is sent when the client requests to create a +// new object from a factory of a certain type. +// +// The client allocates a new_id for the proxy. The server will +// allocate a new resource with the same new_id and from then on, +// Methods and Events will be exchanged between the new object of +// the given type. +type CoreCreateObject struct { + // The name of a server factory object to use. + FactoryName String `json:"factory_name"` + // The type of the object to create, this is also the type of + // the interface of the new_id proxy. + Type String `json:"type"` + // Undocumented, assumed to be the local version of the proxy. + Version Int `json:"version"` + // Extra properties to create the object. + Properties *SPADict `json:"props"` + // The proxy id of the new object. + NewID Int `json:"new_id"` +} + +// Opcode satisfies [Message] with a constant value. +func (c *CoreCreateObject) Opcode() byte { return PW_CORE_METHOD_CREATE_OBJECT } + +// FileCount satisfies [Message] with a constant value. +func (c *CoreCreateObject) FileCount() Int { return 0 } + +// Size satisfies [KnownSize] with a value computed at runtime. +func (c *CoreCreateObject) Size() Word { + return SizePrefix + + SizeString[Word](c.FactoryName) + + SizeString[Word](c.Type) + + Size(SizeInt) + + c.Properties.Size() + + Size(SizeInt) +} + +// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal]. +func (c *CoreCreateObject) MarshalBinary() ([]byte, error) { return Marshal(c) } + +// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal]. +func (c *CoreCreateObject) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) } + +// createObject queues a [CoreCreateObject] message for the PipeWire server. +// This is not safe to use directly, callers should use typed wrapper methods on [Registry] instead. +func (core *Core) createObject(factoryName, typeName String, version Int, props SPADict, newId Int) error { + return core.ctx.writeMessage( + PW_ID_CORE, + &CoreCreateObject{factoryName, typeName, version, &props, newId}, + ) +} + // A RegistryGlobal event is emitted to notify a client about a new global object. type RegistryGlobal struct { // The global id. diff --git a/internal/pipewire/core_test.go b/internal/pipewire/core_test.go index 6d73b5c..7785a74 100644 --- a/internal/pipewire/core_test.go +++ b/internal/pipewire/core_test.go @@ -171,7 +171,7 @@ func TestCoreError(t *testing.T) { /* padding */ 0, 0, 0, 0, /* size: 0x1b bytes */ 0x1b, 0, 0, 0, - /*type: String */ 8, 0, 0, 0, + /* type: String */ 8, 0, 0, 0, // value: "no permission to destroy 0\x00" 0x6e, 0x6f, 0x20, 0x70, @@ -306,6 +306,60 @@ func TestCoreGetRegistry(t *testing.T) { }.run(t) } +func TestCoreCreateObject(t *testing.T) { + t.Parallel() + + encodingTestCases[pipewire.CoreCreateObject, *pipewire.CoreCreateObject]{ + {"sample", []byte{ + /* size: rest of data */ 0x70, 0, 0, 0, + /* type: Struct */ 0xe, 0, 0, 0, + + /* size: 0x13 bytes */ 0x13, 0, 0, 0, + /* type: String */ 8, 0, 0, 0, + + // value: "spa-device-factory\x00" + 0x73, 0x70, 0x61, 0x2d, + 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x2d, 0x66, + 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x79, 0, 0, + 0, 0, 0, 0, + + /* size: 0x1a bytes */ 0x1a, 0, 0, 0, + /* type: String */ 8, 0, 0, 0, + + // value: "PipeWire:Interface:Device\x00" + 0x50, 0x69, 0x70, 0x65, + 0x57, 0x69, 0x72, 0x65, + 0x3a, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x3a, 0x44, + 0x65, 0x76, 0x69, 0x63, + 0x65, 0, 0, 0, + 0, 0, 0, 0, + + /* size: 4 bytes */ 4, 0, 0, 0, + /* type: Int */ 4, 0, 0, 0, + /* value: 3 */ 3, 0, 0, 0, + /* padding */ 0, 0, 0, 0, + + /* size */ 0, 0, 0, 0, + /* type: None */ 1, 0, 0, 0, + + /* size: 4 bytes */ 4, 0, 0, 0, + /* type: Int */ 4, 0, 0, 0, + /* value: 0xbad */ 0xad, 0xb, 0, 0, + /* padding */ 0, 0, 0, 0, + }, pipewire.CoreCreateObject{ + FactoryName: "spa-device-factory", + Type: pipewire.PW_TYPE_INTERFACE_Device, + Version: pipewire.PW_VERSION_FACTORY, + Properties: new(pipewire.SPADict), + NewID: 0xbad, + }, nil}, + }.run(t) +} + func TestRegistryGlobal(t *testing.T) { t.Parallel()