aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pipewire
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-12-10 00:59:01 +0900
committerOphestra <cat@gensokyo.uk>2025-12-10 01:00:03 +0900
commitd8417e292762eebbbe7203aa98eb34eb5c34434e (patch)
tree006d34dd9197106a6fea9597eb3bd3578bf3f29f /internal/pipewire
parentccc0d98bd72e84b066fcbf9677a75293e2b850e1 (diff)
internal/pipewire: implement Registry::GlobalRemove
This is emitted by PipeWire when a global object disappears, because PipeWire insists that all clients that had called Core::GetRegistry must constantly sync its local registry state with the remote. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/pipewire')
-rw-r--r--internal/pipewire/core.go43
-rw-r--r--internal/pipewire/core_test.go17
-rw-r--r--internal/pipewire/pipewire_test.go8
3 files changed, 68 insertions, 0 deletions
diff --git a/internal/pipewire/core.go b/internal/pipewire/core.go
index a6d47256..fc3edd4e 100644
--- a/internal/pipewire/core.go
+++ b/internal/pipewire/core.go
@@ -533,6 +533,30 @@ 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) }
+// A RegistryGlobalRemove event is emitted when a global with id was removed.
+type RegistryGlobalRemove struct {
+ // The global id that was removed.
+ ID Int `json:"id"`
+}
+
+// Opcode satisfies [Message] with a constant value.
+func (c *RegistryGlobalRemove) Opcode() byte { return PW_REGISTRY_EVENT_GLOBAL_REMOVE }
+
+// FileCount satisfies [Message] with a constant value.
+func (c *RegistryGlobalRemove) FileCount() Int { return 0 }
+
+// Size satisfies [KnownSize] with a constant value.
+func (c *RegistryGlobalRemove) Size() Word {
+ return SizePrefix +
+ Size(SizeInt)
+}
+
+// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
+func (c *RegistryGlobalRemove) MarshalBinary() ([]byte, error) { return Marshal(c) }
+
+// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
+func (c *RegistryGlobalRemove) 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
@@ -714,6 +738,14 @@ func (e *GlobalIDCollisionError) Error() string {
" stepping on previous id " + strconv.Itoa(int(e.ID)) + " for " + e.Previous.Type
}
+// An UnknownGlobalIDRemoveError describes a [RegistryGlobalRemove] event announcing the removal of
+// a global id that is not yet known to [Registry] or was already deleted.
+type UnknownGlobalIDRemoveError Int
+
+func (e UnknownGlobalIDRemoveError) Error() string {
+ return "Registry::GlobalRemove event targets unknown id " + strconv.Itoa(int(e))
+}
+
func (registry *Registry) consume(opcode byte, files []int, unmarshal func(v any)) error {
closeReceivedFiles(files...)
switch opcode {
@@ -727,6 +759,17 @@ func (registry *Registry) consume(opcode byte, files []int, unmarshal func(v any
registry.Objects[global.ID] = global
return nil
+ case PW_REGISTRY_EVENT_GLOBAL_REMOVE:
+ var globalRemove RegistryGlobalRemove
+ unmarshal(&globalRemove)
+ l := len(registry.Objects)
+ delete(registry.Objects, globalRemove.ID)
+ if len(registry.Objects) != l-1 {
+ // this should never happen so is non-recoverable if it does
+ panic(UnknownGlobalIDRemoveError(globalRemove.ID))
+ }
+ return nil
+
default:
return &UnsupportedOpcodeError{opcode, registry.String()}
}
diff --git a/internal/pipewire/core_test.go b/internal/pipewire/core_test.go
index a6b6729a..42b18b98 100644
--- a/internal/pipewire/core_test.go
+++ b/internal/pipewire/core_test.go
@@ -745,6 +745,23 @@ func TestRegistryGlobal(t *testing.T) {
}.run(t)
}
+func TestRegistryGlobalRemove(t *testing.T) {
+ t.Parallel()
+
+ encodingTestCases[pipewire.RegistryGlobalRemove, *pipewire.RegistryGlobalRemove]{
+ {"sample", []byte{
+ /* size: rest of data*/ 0x10, 0, 0, 0,
+ /* type: Struct */ 0xe, 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.RegistryGlobalRemove{
+ ID: 0xbad,
+ }, nil},
+ }.run(t)
+}
+
func TestRegistryBind(t *testing.T) {
t.Parallel()
diff --git a/internal/pipewire/pipewire_test.go b/internal/pipewire/pipewire_test.go
index 535bac03..7c2cc332 100644
--- a/internal/pipewire/pipewire_test.go
+++ b/internal/pipewire/pipewire_test.go
@@ -862,6 +862,14 @@ func TestContextErrors(t *testing.T) {
ID: 0xbad,
Sequence: 0xcafe,
}, "received Core::Ping seq 51966 targeting unknown proxy id 2989"},
+
+ {"GlobalIDCollisionError", &pipewire.GlobalIDCollisionError{
+ ID: 0xbad,
+ Previous: &pipewire.RegistryGlobal{Type: "PipeWire:Interface:Invalid"},
+ Current: &pipewire.RegistryGlobal{Type: "PipeWire:Interface:NewInvalid"},
+ }, "new Registry::Global event for PipeWire:Interface:NewInvalid stepping on previous id 2989 for PipeWire:Interface:Invalid"},
+
+ {"UnknownGlobalIDRemoveError", pipewire.UnknownGlobalIDRemoveError(0xbad), "Registry::GlobalRemove event targets unknown id 2989"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {