aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-11-29 02:58:44 +0900
committerOphestra <cat@gensokyo.uk>2025-11-29 03:01:53 +0900
commit5a5c4705dd47c7e27f49526b4ccf0d4c7c1bab5a (patch)
treea4f01207e10d1a6f5b545f3e54eaddbac3e12ef8
parentf703aa20a599529aace054ef8befc5128afb59e2 (diff)
internal/pipewire: implement Registry::Bind
This change also adds test cases for messages before this one. Signed-off-by: Ophestra <cat@gensokyo.uk>
-rw-r--r--internal/pipewire/core.go30
-rw-r--r--internal/pipewire/core_test.go58
-rw-r--r--internal/pipewire/header_test.go43
-rw-r--r--internal/pipewire/pod.go10
4 files changed, 131 insertions, 10 deletions
diff --git a/internal/pipewire/core.go b/internal/pipewire/core.go
index 9e3613ba..7c2b4a90 100644
--- a/internal/pipewire/core.go
+++ b/internal/pipewire/core.go
@@ -265,3 +265,33 @@ func (c *RegistryGlobal) MarshalBinary() ([]byte, error) { return Marshal(c) }
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
func (c *RegistryGlobal) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
+
+// RegistryBind is sent when the client requests to bind to the
+// global object with id and use the client proxy with new_id as
+// the proxy. After this call, methods can be sent to the remote
+// global object and events can be received.
+type RegistryBind struct {
+ // The [RegistryGlobal.ID] to bind to.
+ ID Int `json:"id"`
+ // the [RegistryGlobal.Type] of the global id.
+ Type String `json:"type"`
+ // The client version of the interface for type.
+ Version Int `json:"version"`
+ // The client proxy id for the global object.
+ NewID Int `json:"new_id"`
+}
+
+// Size satisfies [KnownSize] with a value computed at runtime.
+func (c *RegistryBind) Size() Word {
+ return SizePrefix +
+ Size(SizeInt) +
+ SizeString[Word](c.Type) +
+ Size(SizeInt) +
+ Size(SizeInt)
+}
+
+// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
+func (c *RegistryBind) MarshalBinary() ([]byte, error) { return Marshal(c) }
+
+// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
+func (c *RegistryBind) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
diff --git a/internal/pipewire/core_test.go b/internal/pipewire/core_test.go
index c6d11573..ffea96e7 100644
--- a/internal/pipewire/core_test.go
+++ b/internal/pipewire/core_test.go
@@ -91,10 +91,16 @@ func TestCoreDone(t *testing.T) {
t.Parallel()
encodingTestCases[pipewire.CoreDone, *pipewire.CoreDone]{
- {"sample", samplePWContainer[1][5][1], pipewire.CoreDone{
+ {"sample0", samplePWContainer[1][5][1], pipewire.CoreDone{
ID: -1,
Sequence: 0,
}, nil},
+
+ // matches the Core::Sync sample
+ {"sample1", samplePWContainer[1][41][1], pipewire.CoreDone{
+ ID: 0,
+ Sequence: pipewire.CoreSyncSequenceOffset + 3,
+ }, nil},
}.run(t)
}
@@ -102,7 +108,10 @@ func TestCoreBoundProps(t *testing.T) {
t.Parallel()
encodingTestCases[pipewire.CoreBoundProps, *pipewire.CoreBoundProps]{
- {"sample", samplePWContainer[1][1][1], pipewire.CoreBoundProps{
+
+ /* recvmsg 0 */
+
+ {"sample0", samplePWContainer[1][1][1], pipewire.CoreBoundProps{
ID: pipewire.PW_ID_CLIENT,
GlobalID: 34,
Properties: &pipewire.SPADict{
@@ -114,6 +123,16 @@ func TestCoreBoundProps(t *testing.T) {
{Key: "pipewire.sec.gid", Value: "100"},
{Key: "pipewire.sec.socket", Value: "pipewire-0-manager"}},
}, nil},
+
+ /* recvmsg 1 */
+
+ {"sample1", samplePWContainer[4][0][1], pipewire.CoreBoundProps{
+ ID: 3,
+ GlobalID: 3,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "3"},
+ },
+ }, nil},
}.run(t)
}
@@ -132,7 +151,7 @@ func TestCoreSync(t *testing.T) {
encodingTestCases[pipewire.CoreSync, *pipewire.CoreSync]{
{"sample", samplePWContainer[0][3][1], pipewire.CoreSync{
- ID: pipewire.PW_ID_CORE,
+ ID: 0,
Sequence: pipewire.CoreSyncSequenceOffset + 3,
}, nil},
}.run(t)
@@ -177,7 +196,7 @@ func TestRegistryGlobal(t *testing.T) {
}, nil},
{"sample2", samplePWContainer[1][8][1], pipewire.RegistryGlobal{
- ID: 3, // registry takes up 2
+ ID: 3,
Permissions: pipewire.PW_SECURITY_CONTEXT_PERM_MASK,
Type: pipewire.PW_TYPE_INTERFACE_SecurityContext,
Version: pipewire.PW_VERSION_SECURITY_CONTEXT,
@@ -586,5 +605,36 @@ func TestRegistryGlobal(t *testing.T) {
{Key: "application.name", Value: "pw-container"},
},
}, nil},
+
+ {"sample35", samplePWContainer[1][42][1], pipewire.RegistryGlobal{
+ ID: 35,
+ Permissions: pipewire.PW_CLIENT_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Client,
+ Version: pipewire.PW_VERSION_CLIENT,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "35"},
+ {Key: "module.id", Value: "2"},
+ {Key: "pipewire.protocol", Value: "protocol-native"},
+ {Key: "pipewire.sec.pid", Value: "1447"},
+ {Key: "pipewire.sec.uid", Value: "1000"},
+ {Key: "pipewire.sec.gid", Value: "100"},
+ {Key: "pipewire.sec.socket", Value: "pipewire-0-manager"},
+ {Key: "pipewire.access", Value: "unrestricted"},
+ {Key: "application.name", Value: "WirePlumber"},
+ },
+ }, nil},
+ }.run(t)
+}
+
+func TestRegistryBind(t *testing.T) {
+ t.Parallel()
+
+ encodingTestCases[pipewire.RegistryBind, *pipewire.RegistryBind]{
+ {"sample", samplePWContainer[3][0][1], pipewire.RegistryBind{
+ ID: 3,
+ Type: pipewire.PW_TYPE_INTERFACE_SecurityContext,
+ Version: pipewire.PW_VERSION_SECURITY_CONTEXT,
+ NewID: 3, // registry takes up 2
+ }, nil},
}.run(t)
}
diff --git a/internal/pipewire/header_test.go b/internal/pipewire/header_test.go
index a35ed983..63692769 100644
--- a/internal/pipewire/header_test.go
+++ b/internal/pipewire/header_test.go
@@ -11,6 +11,9 @@ func TestHeader(t *testing.T) {
t.Parallel()
encodingTestCases[pipewire.Header, *pipewire.Header]{
+
+ /* sendmsg 0 */
+
{"PW_CORE_METHOD_HELLO", samplePWContainer[0][0][0], pipewire.Header{
ID: pipewire.PW_ID_CORE,
Opcode: pipewire.PW_CORE_METHOD_HELLO,
@@ -35,37 +38,39 @@ func TestHeader(t *testing.T) {
Size: 0x28, Sequence: 3, FileCount: 0,
}, nil},
+ /* recvmsg 0 */
+
{"PW_CORE_EVENT_INFO", samplePWContainer[1][0][0], pipewire.Header{
ID: pipewire.PW_ID_CORE,
Opcode: pipewire.PW_CORE_EVENT_INFO,
Size: 0x6b8, Sequence: 0, FileCount: 0,
}, nil},
- {"PW_CORE_EVENT_BOUND_PROPS", samplePWContainer[1][1][0], pipewire.Header{
+ {"PW_CORE_EVENT_BOUND_PROPS 0", samplePWContainer[1][1][0], pipewire.Header{
ID: pipewire.PW_ID_CORE,
Opcode: pipewire.PW_CORE_EVENT_BOUND_PROPS,
Size: 0x198, Sequence: 1, FileCount: 0,
}, nil},
- {"PW_CLIENT_EVENT_INFO", samplePWContainer[1][2][0], pipewire.Header{
+ {"PW_CLIENT_EVENT_INFO 0", samplePWContainer[1][2][0], pipewire.Header{
ID: pipewire.PW_ID_CLIENT,
Opcode: pipewire.PW_CLIENT_EVENT_INFO,
Size: 0x1f0, Sequence: 2, FileCount: 0,
}, nil},
- {"PW_CLIENT_EVENT_INFO*", samplePWContainer[1][3][0], pipewire.Header{
+ {"PW_CLIENT_EVENT_INFO 1", samplePWContainer[1][3][0], pipewire.Header{
ID: pipewire.PW_ID_CLIENT,
Opcode: pipewire.PW_CLIENT_EVENT_INFO,
Size: 0x7a0, Sequence: 3, FileCount: 0,
}, nil},
- {"PW_CLIENT_EVENT_INFO**", samplePWContainer[1][4][0], pipewire.Header{
+ {"PW_CLIENT_EVENT_INFO 2", samplePWContainer[1][4][0], pipewire.Header{
ID: pipewire.PW_ID_CLIENT,
Opcode: pipewire.PW_CLIENT_EVENT_INFO,
Size: 0x7d0, Sequence: 4, FileCount: 0,
}, nil},
- {"PW_CORE_EVENT_DONE", samplePWContainer[1][5][0], pipewire.Header{
+ {"PW_CORE_EVENT_DONE 0", samplePWContainer[1][5][0], pipewire.Header{
ID: pipewire.PW_ID_CORE,
Opcode: pipewire.PW_CORE_EVENT_DONE,
Size: 0x58, Sequence: 5, FileCount: 0,
@@ -281,6 +286,34 @@ func TestHeader(t *testing.T) {
Size: 0x238, Sequence: 40, FileCount: 0,
}, nil},
+ {"PW_CORE_EVENT_DONE 1", samplePWContainer[1][41][0], pipewire.Header{
+ ID: pipewire.PW_ID_CORE,
+ Opcode: pipewire.PW_CORE_EVENT_DONE,
+ Size: 0x28, Sequence: 41, FileCount: 0,
+ }, nil},
+
+ {"PW_REGISTRY_EVENT_GLOBAL 35", samplePWContainer[1][42][0], pipewire.Header{
+ ID: 2,
+ Opcode: pipewire.PW_REGISTRY_EVENT_GLOBAL,
+ Size: 0x268, Sequence: 42, FileCount: 0,
+ }, nil},
+
+ /* sendmsg 1 */
+
+ {"PW_REGISTRY_METHOD_BIND", samplePWContainer[3][0][0], pipewire.Header{
+ ID: 2,
+ Opcode: pipewire.PW_REGISTRY_METHOD_BIND,
+ Size: 0x98, Sequence: 4, FileCount: 0,
+ }, nil},
+
+ /* recvmsg 1 */
+
+ {"PW_CORE_EVENT_BOUND_PROPS 1", samplePWContainer[4][0][0], pipewire.Header{
+ ID: pipewire.PW_ID_CORE,
+ Opcode: pipewire.PW_CORE_EVENT_BOUND_PROPS,
+ Size: 0x68, Sequence: 43, FileCount: 0,
+ }, nil},
+
{"PW_SECURITY_CONTEXT_METHOD_CREATE", []byte{
// Id
3, 0, 0, 0,
diff --git a/internal/pipewire/pod.go b/internal/pipewire/pod.go
index 6e13177d..1abc821a 100644
--- a/internal/pipewire/pod.go
+++ b/internal/pipewire/pod.go
@@ -457,7 +457,15 @@ func (f *Footer[T]) MarshalBinary() ([]byte, error) { return Marshal(f) }
func (f *Footer[T]) UnmarshalBinary(data []byte) error { return Unmarshal(data, f) }
// SPADictItem represents spa_dict_item.
-type SPADictItem struct{ Key, Value string }
+type SPADictItem struct {
+ // Dot-separated string.
+ Key string `json:"key"`
+ // Arbitrary string.
+ //
+ // Integer values are represented in base 10,
+ // boolean values are represented as "true" or "false".
+ Value string `json:"value"`
+}
// SPADict represents spa_dict.
type SPADict []SPADictItem