aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pipewire/core.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-12-14 09:20:58 +0900
committerOphestra <cat@gensokyo.uk>2025-12-14 09:20:58 +0900
commitb0f2ab6fffd48580d758de80165f8d97438b4066 (patch)
treee58678218b33bc2e4fccc1582214b70d0f1bd8b6 /internal/pipewire/core.go
parent00a5bdf006ba800e0797ff4c2b732e5df4fa28ff (diff)
internal/pipewire: implement Core::Destroy
This change also implements pending destructible check on Sync. Destruction method should always be implemented as a wrapper of destructible.destroy. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/pipewire/core.go')
-rw-r--r--internal/pipewire/core.go72
1 files changed, 72 insertions, 0 deletions
diff --git a/internal/pipewire/core.go b/internal/pipewire/core.go
index 6adcac1d..517ef2c3 100644
--- a/internal/pipewire/core.go
+++ b/internal/pipewire/core.go
@@ -382,6 +382,41 @@ func (s *removable) mustCheckDestroy() {
}
}
+// destructible is embedded by proxies that can be targeted by the [CoreRemoveId] event and the
+// [CoreDestroy] method and requires no cleanup. destructible purposefully does not override
+// removable.mustCheckDestroy because it is used by unexported methods called during event handling
+// and are exempt from the destruction check.
+type destructible struct {
+ destroyed bool
+
+ removable
+}
+
+// checkDestroy overrides removable.checkDestroy to also check the destroyed field.
+func (s *destructible) checkDestroy() error {
+ if s.destroyed {
+ return ErrProxyDestroyed
+ }
+ if err := s.removable.checkDestroy(); err != nil {
+ return err
+ }
+ return nil
+}
+
+// destroy calls removable.checkDestroy then queues a [CoreDestroy] event if it succeeds.
+func (s *destructible) destroy(ctx *Context, id Int) error {
+ if err := s.checkDestroy(); err != nil {
+ return err
+ }
+ l := len(ctx.pendingDestruction)
+ ctx.pendingDestruction[id] = struct{}{}
+ if len(ctx.pendingDestruction) != l+1 {
+ return ErrProxyDestroyed
+ }
+ s.destroyed = true
+ return ctx.GetCore().destroy(id)
+}
+
// An InconsistentIdError describes an inconsistent state where the server claims an impossible
// proxy or global id. This is only generated by the [CoreBoundProps] event.
type InconsistentIdError struct {
@@ -504,6 +539,10 @@ func (core *Core) Sync() error {
core.ctx.closeReceivedFiles()
return &ProxyFatalError{Err: UnacknowledgedProxyError(slices.Collect(maps.Keys(core.ctx.pendingIds))), ProxyErrs: core.ctx.cloneAsProxyErrors()}
}
+ if len(core.ctx.pendingDestruction) != 0 {
+ core.ctx.closeReceivedFiles()
+ return &ProxyFatalError{Err: UnacknowledgedProxyDestructionError(slices.Collect(maps.Keys(core.ctx.pendingDestruction))), ProxyErrs: core.ctx.cloneAsProxyErrors()}
+ }
return core.ctx.doSyncComplete()
}
@@ -624,6 +663,37 @@ func (core *Core) createObject(factoryName, typeName String, version Int, props
)
}
+// CoreDestroy is sent when the client requests to destroy an object.
+type CoreDestroy struct {
+ // The proxy id of the object to destroy.
+ ID Int `json:"id"`
+}
+
+// Opcode satisfies [Message] with a constant value.
+func (c *CoreDestroy) Opcode() byte { return PW_CORE_METHOD_DESTROY }
+
+// FileCount satisfies [Message] with a constant value.
+func (c *CoreDestroy) FileCount() Int { return 0 }
+
+// Size satisfies [KnownSize] with a constant value.
+func (c *CoreDestroy) Size() Word { return SizePrefix + Size(SizeInt) }
+
+// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
+func (c *CoreDestroy) MarshalBinary() ([]byte, error) { return Marshal(c) }
+
+// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
+func (c *CoreDestroy) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
+
+// destroy queues a [CoreDestroy] message for the PipeWire server.
+// This is not safe to use directly, callers should use the exported method
+// on the proxy implementation instead.
+func (core *Core) destroy(id Int) error {
+ return core.ctx.writeMessage(
+ PW_ID_CORE,
+ &CoreDestroy{id},
+ )
+}
+
// A RegistryGlobal event is emitted to notify a client about a new global object.
type RegistryGlobal struct {
// The global id.
@@ -889,6 +959,8 @@ func (core *Core) consume(opcode byte, files []int, unmarshal func(v any)) error
panic(&UnknownProxyIdError[*CoreRemoveId]{Id: coreRemoveId.ID, Event: &coreRemoveId})
} else {
delete(core.ctx.proxy, coreRemoveId.ID)
+ // not always populated so this is not checked
+ delete(core.ctx.pendingDestruction, coreRemoveId.ID)
return proxy.remove()
}