diff --git a/internal/pipewire/core.go b/internal/pipewire/core.go index d267781..a53f02d 100644 --- a/internal/pipewire/core.go +++ b/internal/pipewire/core.go @@ -641,6 +641,37 @@ func (registry *Registry) destroy(id Int) error { ) } +// Destroy tries to destroy the global object with id. +func (registry *Registry) Destroy(id Int) error { + if err := registry.destroy(id); err != nil { + return err + } + destroySeq := registry.ctx.currentSeq() + + err := registry.ctx.GetCore().Sync() + if err == nil { + return nil + } + + var coreError *CoreError + if proxyErrors, ok := err.(ProxyConsumeError); !ok || + len(proxyErrors) != 1 || + !errors.As(proxyErrors[0], &coreError) || + coreError == nil || + coreError.ID != registry.ID || + coreError.Sequence != destroySeq { + return err + } + + switch syscall.Errno(-coreError.Result) { + case syscall.EPERM: + return &PermissionError{registry.ID, coreError.Message} + + default: + return coreError + } +} + // An UnsupportedObjectTypeError is the name of a type not known by the server [Registry]. type UnsupportedObjectTypeError string diff --git a/internal/pipewire/pipewire.go b/internal/pipewire/pipewire.go index a87a4e9..5473797 100644 --- a/internal/pipewire/pipewire.go +++ b/internal/pipewire/pipewire.go @@ -636,7 +636,7 @@ func (ctx *Context) roundtrip() (err error) { } // currentSeq returns the current sequence number. -// This must only be called from eventProxy.consume. +// This must only be called immediately after queueing a message. func (ctx *Context) currentSeq() Int { return ctx.sequence - 1 } // currentRemoteSeq returns the current remote sequence number. @@ -786,6 +786,18 @@ func (ctx *Context) Close() (err error) { } } +// A PermissionError describes an error emitted by the server when trying to +// perform an operation that the client has no permission for. +type PermissionError struct { + // The id of the resource (proxy if emitted by the client) that is in error. + ID Int `json:"id"` + // An error message. + Message string `json:"message"` +} + +func (*PermissionError) Unwrap() error { return syscall.EPERM } +func (e *PermissionError) Error() string { return e.Message } + // Remote is the environment (sic) with the remote name. const Remote = "PIPEWIRE_REMOTE" diff --git a/internal/pipewire/pipewire_test.go b/internal/pipewire/pipewire_test.go index 7c2cc33..afa2d7e 100644 --- a/internal/pipewire/pipewire_test.go +++ b/internal/pipewire/pipewire_test.go @@ -870,6 +870,11 @@ func TestContextErrors(t *testing.T) { }, "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"}, + + {"PermissionError", &pipewire.PermissionError{ + ID: 2, + Message: "no permission to destroy 0", + }, "no permission to destroy 0"}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) {