aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-10-27 20:13:13 +0900
committerOphestra <cat@gensokyo.uk>2026-10-27 20:13:13 +0900
commitea29e6bad114306b78820cb674863a9bf3041fa5 (patch)
treeda87b09d5b3b57d6744db90b697124c5d4569e8d
parent672cf9abcac6f25abfdd2541cae6455c6da38fc3 (diff)
internal/pipewire: re-format for readability
This package was the first somewhat large piece of code that was primarily written using the built-in display of my laptop. The experience had been deeply unpleasant due to the awkward 3:2 aspect ratio not working well with my setup, which almost always involved having the implementation and tests side-by-side. This experience is what provoked me to re-instate the 80-column limit that I had long resented and rejected. This package however predates the formal re-instating of this limit, and was never updated. Signed-off-by: Ophestra <cat@gensokyo.uk>
-rw-r--r--internal/pipewire/client.go17
-rw-r--r--internal/pipewire/core.go251
-rw-r--r--internal/pipewire/header.go10
-rw-r--r--internal/pipewire/pipewire.go324
-rw-r--r--internal/pipewire/pod.go124
-rw-r--r--internal/pipewire/securitycontext.go51
-rw-r--r--internal/pipewire/spa.go7
7 files changed, 501 insertions, 283 deletions
diff --git a/internal/pipewire/client.go b/internal/pipewire/client.go
index 96beeba3..e67cc22e 100644
--- a/internal/pipewire/client.go
+++ b/internal/pipewire/client.go
@@ -36,13 +36,16 @@ const (
)
// The ClientInfo event provides client information updates.
-// This is emitted when binding to a client or when the client info is updated later.
+//
+// This is emitted when binding to a client or when the client info is updated
+// later.
type ClientInfo struct {
// The global id of the client.
ID Int `json:"id"`
// The changes emitted by this event.
ChangeMask Long `json:"change_mask"`
- // Properties of this object, valid when change_mask has PW_CLIENT_CHANGE_MASK_PROPS.
+ // Properties of this object, valid when change_mask has
+ // PW_CLIENT_CHANGE_MASK_PROPS.
Properties *SPADict `json:"props"`
}
@@ -87,8 +90,11 @@ func (c *ClientUpdateProperties) MarshalBinary() ([]byte, error) { return Marsha
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
func (c *ClientUpdateProperties) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
-// clientUpdateProperties queues a [ClientUpdateProperties] message for the PipeWire server.
-// This method should not be called directly, the New function queues this message.
+// clientUpdateProperties queues a [ClientUpdateProperties] message for the
+// PipeWire server.
+//
+// This method should not be called directly, the New function queues this
+// message.
func (ctx *Context) clientUpdateProperties(props SPADict) error {
return ctx.writeMessage(
PW_ID_CLIENT,
@@ -98,7 +104,8 @@ func (ctx *Context) clientUpdateProperties(props SPADict) error {
// Client holds state of [PW_TYPE_INTERFACE_Client].
type Client struct {
- // Additional information from the server, populated or updated during [Context.Roundtrip].
+ // Additional information from the server, populated or updated during
+ // [Context.Roundtrip].
Info *ClientInfo `json:"info"`
// Populated by [CoreBoundProps] events targeting [Client].
diff --git a/internal/pipewire/core.go b/internal/pipewire/core.go
index 026f75bb..b0d82b44 100644
--- a/internal/pipewire/core.go
+++ b/internal/pipewire/core.go
@@ -84,8 +84,8 @@ const (
FOOTER_CORE_OPCODE_LAST
)
-// The FooterCoreGeneration indicates to the client what is the current
-// registry generation number of the Context on the server side.
+// The FooterCoreGeneration indicates to the client what is the current registry
+// generation number of the Context on the server side.
//
// The server shall include this footer in the next message it sends that
// follows the increment of the registry generation number.
@@ -99,12 +99,12 @@ func (fcg FooterCoreGeneration) Size() Word {
Size(SizeLong)
}
-// The FooterClientGeneration indicates to the server what is the last
-// registry generation number the client has processed.
+// The FooterClientGeneration indicates to the server what is the last registry
+// generation number the client has processed.
//
-// The client shall include this footer in the next message it sends,
-// after it has processed an incoming message whose footer includes a
-// registry generation update.
+// The client shall include this footer in the next message it sends, after it
+// has processed an incoming message whose footer includes a registry generation
+// update.
type FooterClientGeneration struct {
ClientGeneration Long `json:"client_generation"`
}
@@ -115,8 +115,8 @@ func (fcg FooterClientGeneration) Size() Word {
Size(SizeLong)
}
-// A CoreInfo event is emitted by the server upon connection
-// with the more information about the server.
+// A CoreInfo event is emitted by the server upon connection with the more
+// information about the server.
type CoreInfo struct {
// The id of the server (PW_ID_CORE).
ID Int `json:"id"`
@@ -132,7 +132,8 @@ type CoreInfo struct {
Name String `json:"name"`
// A set of bits with changes to the info.
ChangeMask Long `json:"change_mask"`
- // Optional key/value properties, valid when change_mask has PW_CORE_CHANGE_MASK_PROPS.
+ // Optional key/value properties, valid when change_mask has
+ // PW_CORE_CHANGE_MASK_PROPS.
Properties *SPADict `json:"props"`
}
@@ -184,12 +185,13 @@ func (c *CoreDone) MarshalBinary() ([]byte, error) { return Marshal(c) }
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
func (c *CoreDone) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
-// The CorePing event is emitted by the server when it wants to check if a client is
-// alive or ensure that it has processed the previous events.
+// The CorePing event is emitted by the server when it wants to check if a
+// client is alive or ensure that it has processed the previous events.
type CorePing struct {
// The object id to ping.
ID Int `json:"id"`
// Usually automatically generated.
+ //
// The client should pass this in the Pong method reply.
Sequence Int `json:"seq"`
}
@@ -212,13 +214,12 @@ func (c *CorePing) UnmarshalBinary(data []byte) error { return Unmarshal(data, c
// The CoreError can be emitted by both the client and the server.
//
// When emitted by the server, the error event is sent out when a fatal
-// (non-recoverable) error has occurred. The id argument is the proxy
-// object where the error occurred, most often in response to a request
-// to that object. The message is a brief description of the error, for
-// (debugging) convenience.
+// (non-recoverable) error has occurred. The id argument is the proxy object
+// where the error occurred, most often in response to a request to that object.
+// The message is a brief description of the error, for (debugging) convenience.
//
-// When emitted by the client, it indicates an error occurred in an
-// object on the client.
+// When emitted by the client, it indicates an error occurred in an object on
+// the client.
type CoreError struct {
// The id of the resource (proxy if emitted by the client) that is in error.
ID Int `json:"id"`
@@ -270,9 +271,9 @@ func (c *CoreErrorEvent) Opcode() byte { return PW_CORE_EVENT_ERROR }
// The CoreRemoveId event is used internally by the object ID management logic.
//
-// When a client deletes an object, the server will send this event to acknowledge
-// that it has seen the delete request. When the client receives this event, it
-// will know that it can safely reuse the object ID.
+// When a client deletes an object, the server will send this event to
+// acknowledge that it has seen the delete request. When the client receives
+// this event, it will know that it can safely reuse the object ID.
type CoreRemoveId struct {
// A proxy id that was removed.
ID Int `json:"id"`
@@ -293,8 +294,8 @@ func (c *CoreRemoveId) MarshalBinary() ([]byte, error) { return Marshal(c) }
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
func (c *CoreRemoveId) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
-// The CoreBoundProps event is emitted when a local object ID is bound to a global ID.
-// It is emitted before the global becomes visible in the registry.
+// The CoreBoundProps event is emitted when a local object ID is bound to a
+// global ID. It is emitted before the global becomes visible in the registry.
type CoreBoundProps struct {
// A proxy id.
ID Int `json:"id"`
@@ -334,8 +335,8 @@ type noAck struct{}
// setBoundProps should never be called as this proxy should never be targeted by [CoreBoundProps].
func (noAck) setBoundProps(*CoreBoundProps) error { panic(ErrBadBoundProps) }
-// ErrBadRemove is returned when a [CoreRemoveId] event targeting a proxy
-// that should never be targeted is received and processed.
+// ErrBadRemove is returned when a [CoreRemoveId] event targeting a proxy that
+// should never be targeted is received and processed.
var ErrBadRemove = errors.New("attempting to remove a proxy that should never be targeted")
// noRemove is embedded by proxies that are never targeted by [CoreRemoveId].
@@ -344,11 +345,14 @@ type noRemove struct{}
// remove should never be called as this proxy should never be targeted by [CoreRemoveId].
func (noRemove) remove() error { panic(ErrBadRemove) }
-// ErrInvalidRemove is returned when a proxy is somehow removed twice. This is only reached for
-// an implementation error as the proxy struct should no longer be reachable after the first call.
+// ErrInvalidRemove is returned when a proxy is somehow removed twice.
+//
+// This is only reached for an implementation error as the proxy struct should
+// no longer be reachable after the first call.
var ErrInvalidRemove = errors.New("attempting to remove an already freed proxy")
-// removable is embedded by proxies that can be targeted by [CoreRemoveId] and requires no cleanup.
+// removable is embedded by proxies that can be targeted by [CoreRemoveId] and
+// requires no cleanup.
type removable bool
// remove checks against removal of a freed proxy and marks the proxy as removed.
@@ -360,21 +364,27 @@ func (s *removable) remove() error {
return nil
}
-// ErrProxyDestroyed is returned when attempting to use a proxy method when the underlying
-// proxy has already been targeted by a [CoreRemoveId] event.
+// ErrProxyDestroyed is returned when attempting to use a proxy method when the
+// underlying proxy has already been targeted by a [CoreRemoveId] event.
var ErrProxyDestroyed = errors.New("underlying proxy has been removed")
-// checkDestroy returns [ErrProxyDestroyed] if the current proxy has been destroyed.
-// Must be called at the beginning of any exported method of a proxy embedding removable.
+// checkDestroy returns [ErrProxyDestroyed] if the current proxy has been
+// destroyed.
+//
+// Must be called at the beginning of any exported method of a proxy embedding
+// removable.
func (s *removable) checkDestroy() error {
if *s {
- // not fatal: the caller is allowed to recover from this and allocate a new proxy
+ // not fatal: the caller is allowed to recover from this and allocate a
+ // new proxy
return ErrProxyDestroyed
}
return nil
}
-// mustCheckDestroy calls checkDestroy and panics if a non-nil error is returned.
+// mustCheckDestroy calls checkDestroy and panics if a non-nil error is
+// returned.
+//
// This is useful for non-exported methods as they should become unreachable.
func (s *removable) mustCheckDestroy() {
if err := s.checkDestroy(); err != nil {
@@ -382,17 +392,19 @@ 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.
+// 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.
+// checkDestroy overrides removable.checkDestroy to also check the destroyed
+// field.
func (s *destructible) checkDestroy() error {
if s.destroyed {
return ErrProxyDestroyed
@@ -403,7 +415,8 @@ func (s *destructible) checkDestroy() error {
return nil
}
-// destroy calls removable.checkDestroy then queues a [CoreDestroy] event if it succeeds.
+// 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
@@ -417,8 +430,10 @@ func (s *destructible) destroy(ctx *Context, id Int) error {
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.
+// 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 {
// Whether the inconsistent id is the global resource id.
Global bool
@@ -434,8 +449,9 @@ func (e *InconsistentIdError) Error() string {
name = "global"
}
- return name + " id " + strconv.Itoa(int(e.ID)) + " targeting " + e.Proxy.String() +
- " inconsistent with " + strconv.Itoa(int(e.ServerID)) + " claimed by the server"
+ return name + " id " + strconv.Itoa(int(e.ID)) + " targeting " +
+ e.Proxy.String() + " inconsistent with " +
+ strconv.Itoa(int(e.ServerID)) + " claimed by the server"
}
// CoreHello is the first message sent by a client.
@@ -460,7 +476,9 @@ func (c *CoreHello) MarshalBinary() ([]byte, error) { return Marshal(c) }
func (c *CoreHello) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
// hello queues a [CoreHello] message for the PipeWire server.
-// This method should not be called directly, the [New] function queues this message.
+//
+// This method should not be called directly, the [New] function queues this
+// message.
func (core *Core) hello() error {
return core.ctx.writeMessage(
PW_ID_CORE,
@@ -469,13 +487,14 @@ func (core *Core) hello() error {
}
const (
- // CoreSyncSequenceOffset is the offset to [Header.Sequence] to produce [CoreSync.Sequence].
+ // CoreSyncSequenceOffset is the offset to [Header.Sequence] to produce
+ // [CoreSync.Sequence].
CoreSyncSequenceOffset = 0x40000000
)
-// The CoreSync message will result in a Done event from the server.
-// When the Done event is received, the client can be sure that all
-// operations before the Sync method have been completed.
+// The CoreSync message will result in a Done event from the server. When the
+// Done event is received, the client can be sure that all operations before the
+// Sync method have been completed.
type CoreSync struct {
// The id will be returned in the Done event.
ID Int `json:"id"`
@@ -499,6 +518,7 @@ func (c *CoreSync) MarshalBinary() ([]byte, error) { return Marshal(c) }
func (c *CoreSync) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
// sync queues a [CoreSync] message for the PipeWire server.
+//
// This is not safe to use directly, callers should use Sync instead.
func (core *Core) sync(id Int) error {
return core.ctx.writeMessage(
@@ -507,9 +527,11 @@ func (core *Core) sync(id Int) error {
)
}
-// ErrNotDone is returned if [Core.Sync] returns from its [Context.Roundtrip] without
-// receiving a [CoreDone] event targeting the [CoreSync] event it delivered.
-var ErrNotDone = errors.New("did not receive a Core::Done event targeting previously delivered Core::Sync")
+// ErrNotDone is returned if [Core.Sync] returns from its [Context.Roundtrip]
+// without receiving a [CoreDone] event targeting the [CoreSync] event it
+// delivered.
+var ErrNotDone = errors.New("did not receive a Core::Done event " +
+ "targeting previously delivered Core::Sync")
const (
// syncTimeout is the maximum duration [Core.Sync] is allowed to take before
@@ -517,7 +539,8 @@ const (
syncTimeout = RoundtripTimeout + 15*time.Second
)
-// Sync queues a [CoreSync] message for the PipeWire server and initiates a Roundtrip.
+// Sync queues a [CoreSync] message for the PipeWire server and initiates a
+// Roundtrip.
func (core *Core) Sync() error {
core.done = false
if err := core.sync(roundtripSyncID); err != nil {
@@ -537,16 +560,31 @@ func (core *Core) Sync() error {
if len(core.ctx.pendingIds) != 0 {
core.ctx.closeReceivedFiles()
- return &ProxyFatalError{Err: UnacknowledgedProxyError(slices.Collect(maps.Keys(core.ctx.pendingIds))), ProxyErrs: core.ctx.cloneAsProxyErrors()}
+ 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 &ProxyFatalError{
+ Err: UnacknowledgedProxyDestructionError(slices.Collect(
+ maps.Keys(
+ core.ctx.pendingDestruction,
+ )),
+ ),
+ ProxyErrs: core.ctx.cloneAsProxyErrors(),
+ }
}
return core.ctx.doSyncComplete()
}
-// The CorePong message is sent from the client to the server when the server emits the Ping event.
+// The CorePong message is sent from the client to the server when the server
+// emits the Ping event.
type CorePong struct {
// Copied from [CorePing.ID].
ID Int `json:"id"`
@@ -569,18 +607,18 @@ func (c *CorePong) MarshalBinary() ([]byte, error) { return Marshal(c) }
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
func (c *CorePong) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
-// CoreGetRegistry is sent when a client requests to bind to the
-// registry object and list the available objects on the server.
+// CoreGetRegistry is sent when a client requests to bind to the registry object
+// and list the available objects on the server.
//
-// Like with all bindings, first the client allocates a new proxy
-// id and puts this as the new_id field. Methods and Events can
-// then be sent and received on the new_id (in the message Id field).
+// Like with all bindings, first the client allocates a new proxy id and puts
+// this as the new_id field. Methods and Events can then be sent and received on
+// the new_id (in the message Id field).
type CoreGetRegistry struct {
// The version of the registry interface used on the client,
// usually PW_VERSION_REGISTRY.
Version Int `json:"version"`
- // The id of the new proxy with the registry interface,
- // ends up as [Header.ID] in future messages.
+ // The id of the new proxy with the registry interface, ends up as
+ // [Header.ID] in future messages.
NewID Int `json:"new_id"`
}
@@ -599,8 +637,8 @@ func (c *CoreGetRegistry) MarshalBinary() ([]byte, error) { return Marshal(c) }
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
func (c *CoreGetRegistry) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
-// GetRegistry queues a [CoreGetRegistry] message for the PipeWire server
-// and returns the address of the newly allocated [Registry].
+// GetRegistry queues a [CoreGetRegistry] message for the PipeWire server and
+// returns the address of the newly allocated [Registry].
func (ctx *Context) GetRegistry() (*Registry, error) {
registry := Registry{Objects: make(map[Int]RegistryGlobal), ctx: ctx}
newId := ctx.newProxyId(&registry, false)
@@ -611,18 +649,17 @@ 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.
+// 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.
+// 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.
+ // 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"`
@@ -655,7 +692,9 @@ func (c *CoreCreateObject) MarshalBinary() ([]byte, error) { return Marshal(c) }
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.
+//
+// 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,
@@ -685,8 +724,9 @@ func (c *CoreDestroy) MarshalBinary() ([]byte, error) { return Marshal(c) }
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.
+//
+// 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,
@@ -754,10 +794,9 @@ func (c *RegistryGlobalRemove) MarshalBinary() ([]byte, error) { return Marshal(
// 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
-// global object and events can be received.
+// 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"`
@@ -790,8 +829,8 @@ 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) }
-// bind queues a [RegistryBind] message for the PipeWire server
-// and returns the newly allocated proxy id.
+// bind queues a [RegistryBind] message for the PipeWire server and returns the
+// newly allocated proxy id.
func (registry *Registry) bind(proxy eventProxy, id, version Int) (Int, error) {
bind := RegistryBind{
ID: id,
@@ -806,6 +845,7 @@ func (registry *Registry) bind(proxy eventProxy, id, version Int) (Int, error) {
}
// RegistryDestroy is sent to try to destroy the global object with id.
+//
// This might fail when the client does not have permission.
type RegistryDestroy struct {
// The global id to destroy.
@@ -864,7 +904,8 @@ func (registry *Registry) Destroy(id Int) (err error) {
}
}
-// An UnsupportedObjectTypeError is the name of a type not known by the server [Registry].
+// An UnsupportedObjectTypeError is the name of a type not known by the server
+// [Registry].
type UnsupportedObjectTypeError string
func (e UnsupportedObjectTypeError) Error() string { return "unsupported object type " + string(e) }
@@ -872,7 +913,8 @@ func (e UnsupportedObjectTypeError) Message() string { return e.Error() }
// Core holds state of [PW_TYPE_INTERFACE_Core].
type Core struct {
- // Additional information from the server, populated or updated during [Context.Roundtrip].
+ // Additional information from the server, populated or updated during
+ // [Context.Roundtrip].
Info *CoreInfo `json:"info"`
// Whether a [CoreDone] event was received during Sync.
@@ -887,7 +929,8 @@ type Core struct {
// ErrUnexpectedDone is a [CoreDone] event with unexpected values.
var ErrUnexpectedDone = errors.New("multiple Core::Done events targeting Core::Sync")
-// An UnknownProxyIdError describes an event targeting a proxy id that was never allocated.
+// An UnknownProxyIdError describes an event targeting a proxy id that was never
+// allocated.
type UnknownProxyIdError[E any] struct {
// Offending id decoded from Data.
Id Int
@@ -899,7 +942,8 @@ func (e *UnknownProxyIdError[E]) Error() string {
return "unknown proxy id " + strconv.Itoa(int(e.Id))
}
-// An InvalidPingError is a [CorePing] event targeting a proxy id that was never allocated.
+// An InvalidPingError is a [CorePing] event targeting a proxy id that was never
+// allocated.
type InvalidPingError CorePing
func (e *InvalidPingError) Error() string {
@@ -917,7 +961,8 @@ func (core *Core) consume(opcode byte, files []int, unmarshal func(v any)) error
case PW_CORE_EVENT_DONE:
var done CoreDone
unmarshal(&done)
- if done.ID == roundtripSyncID && done.Sequence == CoreSyncSequenceOffset+core.ctx.currentSeq() {
+ if done.ID == roundtripSyncID &&
+ done.Sequence == CoreSyncSequenceOffset+core.ctx.currentSeq() {
if core.done {
return ErrUnexpectedDone
}
@@ -956,7 +1001,10 @@ func (core *Core) consume(opcode byte, files []int, unmarshal func(v any)) error
unmarshal(&coreRemoveId)
if proxy, ok := core.ctx.proxy[coreRemoveId.ID]; !ok {
// this should never happen so is non-recoverable if it does
- panic(&UnknownProxyIdError[*CoreRemoveId]{Id: coreRemoveId.ID, Event: &coreRemoveId})
+ panic(&UnknownProxyIdError[*CoreRemoveId]{
+ Id: coreRemoveId.ID,
+ Event: &coreRemoveId},
+ )
} else {
delete(core.ctx.proxy, coreRemoveId.ID)
// not always populated so this is not checked
@@ -971,7 +1019,10 @@ func (core *Core) consume(opcode byte, files []int, unmarshal func(v any)) error
delete(core.ctx.pendingIds, boundProps.ID)
proxy, ok := core.ctx.proxy[boundProps.ID]
if !ok {
- return &UnknownProxyIdError[*CoreBoundProps]{Id: boundProps.ID, Event: &boundProps}
+ return &UnknownProxyIdError[*CoreBoundProps]{
+ Id: boundProps.ID,
+ Event: &boundProps,
+ }
}
return proxy.setBoundProps(&boundProps)
@@ -989,8 +1040,8 @@ type Registry struct {
// Global objects received via the [RegistryGlobal] event.
//
- // This requires more processing before it can be used, but is not implemented
- // as it is not used by Hakurei.
+ // This requires more processing before it can be used, but is not
+ // implemented as it is not used by Hakurei.
Objects map[Int]RegistryGlobal `json:"objects"`
ctx *Context
@@ -999,7 +1050,8 @@ type Registry struct {
noRemove
}
-// A GlobalIDCollisionError describes a [RegistryGlobal] event stepping on a previous instance of itself.
+// A GlobalIDCollisionError describes a [RegistryGlobal] event stepping on a
+// previous instance of itself.
type GlobalIDCollisionError struct {
// The colliding id.
ID Int
@@ -1012,8 +1064,9 @@ 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.
+// 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 {
@@ -1028,7 +1081,11 @@ func (registry *Registry) consume(opcode byte, files []int, unmarshal func(v any
unmarshal(&global)
if object, ok := registry.Objects[global.ID]; ok {
// this should never happen so is non-recoverable if it does
- panic(&GlobalIDCollisionError{global.ID, &object, &global})
+ panic(&GlobalIDCollisionError{
+ global.ID,
+ &object,
+ &global,
+ })
}
registry.Objects[global.ID] = global
return nil
@@ -1036,8 +1093,8 @@ func (registry *Registry) consume(opcode byte, files []int, unmarshal func(v any
case PW_REGISTRY_EVENT_GLOBAL_REMOVE:
var globalRemove RegistryGlobalRemove
unmarshal(&globalRemove)
- // server emits PW_CORE_EVENT_REMOVE_ID events targeting
- // affected proxies so they do not need to be handled here
+ // server emits PW_CORE_EVENT_REMOVE_ID events targeting affected
+ // proxies so they do not need to be handled here
l := len(registry.Objects)
delete(registry.Objects, globalRemove.ID)
if len(registry.Objects) != l-1 {
diff --git a/internal/pipewire/header.go b/internal/pipewire/header.go
index 3aecfb7d..a627db24 100644
--- a/internal/pipewire/header.go
+++ b/internal/pipewire/header.go
@@ -8,14 +8,17 @@ import (
const (
// SizeHeader is the fixed size of [Header].
SizeHeader = 16
- // SizeMax is the largest value of [Header.Size] that can be represented in its 3-byte segment.
+ // SizeMax is the largest value of [Header.Size] that can be represented in
+ // its 3-byte segment.
SizeMax = 0x00ffffff
)
var (
- // ErrSizeRange indicates that the value of [Header.Size] cannot be represented in its 3-byte segment.
+ // ErrSizeRange indicates that the value of [Header.Size] cannot be
+ // represented in its 3-byte segment.
ErrSizeRange = errors.New("size out of range")
- // ErrBadHeader indicates that the header slice does not have length [HeaderSize].
+ // ErrBadHeader indicates that the header slice does not have length
+ // [SizeHeader].
ErrBadHeader = errors.New("incorrect header size")
)
@@ -26,6 +29,7 @@ type Header struct {
// The opcode on the resource/proxy interface.
Opcode byte `json:"opcode"`
// The size of the payload and optional footer of the message.
+ //
// Note: this value is only 24 bits long in the format.
Size uint32 `json:"size"`
// An increasing sequence number for each message.
diff --git a/internal/pipewire/pipewire.go b/internal/pipewire/pipewire.go
index 3b909336..9357c565 100644
--- a/internal/pipewire/pipewire.go
+++ b/internal/pipewire/pipewire.go
@@ -1,17 +1,25 @@
-// Package pipewire provides a partial implementation of the PipeWire protocol native.
+// Package pipewire provides a partial implementation of the PipeWire protocol
+// native and the SecurityContext extension.
//
-// This implementation is created based on black box analysis and very limited static
-// analysis. The PipeWire documentation is vague and mostly nonexistent, and source code
-// readability is not great due to frequent macro abuse, confusing and inconsistent naming
-// schemes, almost complete absence of comments and the multiple layers of abstractions
-// even internal to the library. The convoluted build system and frequent (mis)use of
-// dlopen(3) further complicates static analysis efforts.
+// This implementation is created based on black box analysis and very limited
+// static analysis. The PipeWire documentation is vague and mostly nonexistent,
+// and source code readability is not great due to frequent macro abuse,
+// confusing and inconsistent naming schemes, almost complete absence of
+// comments and the multiple layers of abstractions even internal to the
+// library. The convoluted build system and frequent (mis)use of dlopen(3)
+// further complicates static analysis efforts.
//
-// Because of this, extreme care must be taken when reusing any code found in this package.
-// While it is extensively tested to be correct for its role within Hakurei, remember that
-// work is only done against PipeWire behaviour specific to this use case, and it is nearly
-// impossible to guarantee that this interpretation of its behaviour is intended, or correct
-// for any other uses of the protocol.
+// Because of this, extreme care must be taken when reusing any code found in
+// this package. While it is extensively tested to be correct for its role
+// within Hakurei, remember that work is only done against PipeWire behaviour
+// specific to this use case, and it is nearly impossible to guarantee that this
+// interpretation of its behaviour is intended, or correct for any other uses of
+// the protocol.
+//
+// This package does not use the network poller. It directly utilises epoll
+// in a way identical to the upstream PipeWire client implementation, validated
+// via syscall tracing. This is not necessary, but aims to avoid potential
+// upstream PipeWire bugs that happen to not affect their own implementation.
package pipewire
import (
@@ -46,9 +54,12 @@ type Conn interface {
Close() error
}
-// The kernel constant SCM_MAX_FD defines a limit on the number of file descriptors in the array.
-// Attempting to send an array larger than this limit causes sendmsg(2) to fail with the error
-// EINVAL. SCM_MAX_FD has the value 253 (or 255 before Linux 2.6.38).
+// The kernel constant SCM_MAX_FD defines a limit on the number of file
+// descriptors in the array.
+//
+// Attempting to send an array larger than this limit causes sendmsg(2) to fail
+// with the error EINVAL. SCM_MAX_FD has the value 253 (or 255 before Linux
+// 2.6.38).
const _SCM_MAX_FD = 253
// A Context holds state of a connection to PipeWire.
@@ -76,7 +87,8 @@ type Context struct {
// Proxy for built-in client events.
client Client
- // Current server-side [Header.Sequence] value, incremented on every event processed.
+ // Current server-side [Header.Sequence] value, incremented on every event
+ // processed.
remoteSequence Int
// Files from the server. This is discarded on every Roundtrip so eventProxy
// implementations must make sure to close them to avoid leaking fds.
@@ -84,8 +96,9 @@ type Context struct {
// These are not automatically set up as [os.File] because it is impossible
// to undo the effects of os.NewFile, which can be inconvenient for some uses.
receivedFiles []int
- // Non-protocol errors encountered during event handling of the current Roundtrip;
- // errors that prevent event processing from continuing must be panicked.
+ // Non-protocol errors encountered during event handling of the current
+ // Roundtrip; errors that prevent event processing from continuing must be
+ // panicked.
proxyErrors ProxyConsumeError
// Pending footer value for the next outgoing message.
// Newer footers appear to simply replace the existing one.
@@ -95,8 +108,9 @@ type Context struct {
deferredPendingFooter KnownSize
// Server side registry generation number.
generation Long
- // Deferred operations ran after a [Core.Sync] completes or Close is called. Errors
- //are reported as part of [ProxyConsumeError] and is not considered fatal unless panicked.
+ // Deferred operations ran after a [Core.Sync] completes or Close is called.
+ // Errors are reported as part of [ProxyConsumeError] and is not considered
+ // fatal unless panicked.
syncComplete []func() error
// Passed to [Conn.Recvmsg]. Not copied if sufficient for all received messages.
@@ -110,7 +124,9 @@ type Context struct {
// cleanup arranges for f to be called after the next [CoreDone] event
// or when [Context] is closed.
-func (ctx *Context) cleanup(f func() error) { ctx.syncComplete = append(ctx.syncComplete, f) }
+func (ctx *Context) cleanup(f func() error) {
+ ctx.syncComplete = append(ctx.syncComplete, f)
+}
// GetCore returns the address of [Core] held by this [Context].
func (ctx *Context) GetCore() *Core { return &ctx.core }
@@ -118,8 +134,11 @@ func (ctx *Context) GetCore() *Core { return &ctx.core }
// GetClient returns the address of [Client] held by this [Context].
func (ctx *Context) GetClient() *Client { return &ctx.client }
-// New initialises [Context] for an already established connection and returns its address.
-// The caller must not call any method of the underlying [Conn] after this function returns.
+// New initialises [Context] for an already established connection and returns
+// its address.
+//
+// The caller must not call any method of the underlying [Conn] after this
+// function returns.
func New(conn Conn, props SPADict) (*Context, error) {
ctx := Context{conn: conn}
ctx.core.ctx = &ctx
@@ -143,14 +162,15 @@ func New(conn Conn, props SPADict) (*Context, error) {
return &ctx, nil
}
-// unixConn is an implementation of the [Conn] interface for connections
-// to Unix domain sockets.
+// unixConn is an implementation of the [Conn] interface for connections to Unix
+// domain sockets.
type unixConn struct {
fd int
// Whether creation of a new epoll instance was attempted.
epoll bool
// File descriptor referring to the new epoll instance.
+ //
// Valid if epoll is true and epollErr is nil.
epollFd int
// Error returned by syscall.EpollCreate1.
@@ -164,9 +184,15 @@ type unixConn struct {
// Dial connects to a Unix domain socket described by name.
func Dial(name string) (Conn, error) {
- if fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC|syscall.SOCK_NONBLOCK, 0); err != nil {
+ if fd, err := syscall.Socket(
+ syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC|syscall.SOCK_NONBLOCK,
+ 0,
+ ); err != nil {
return nil, os.NewSyscallError("socket", err)
- } else if err = syscall.Connect(fd, &syscall.SockaddrUnix{Name: name}); err != nil {
+ } else if err = syscall.Connect(
+ fd,
+ &syscall.SockaddrUnix{Name: name},
+ ); err != nil {
_ = syscall.Close(fd)
return nil, os.NewSyscallError("connect", err)
} else {
@@ -174,8 +200,8 @@ func Dial(name string) (Conn, error) {
}
}
-// MightBlock informs the implementation that the next call
-// might block for a non-zero timeout.
+// MightBlock informs the implementation that the next call might block for a
+// non-zero timeout.
func (conn *unixConn) MightBlock(timeout time.Duration) {
if timeout < 0 {
timeout = 0
@@ -189,10 +215,15 @@ func (conn *unixConn) wantsEpoll() error {
conn.epoll = true
conn.epollFd, conn.epollErr = syscall.EpollCreate1(syscall.EPOLL_CLOEXEC)
if conn.epollErr == nil {
- if conn.epollErr = syscall.EpollCtl(conn.epollFd, syscall.EPOLL_CTL_ADD, conn.fd, &syscall.EpollEvent{
- Events: syscall.EPOLLERR | syscall.EPOLLHUP,
- Fd: int32(conn.fd),
- }); conn.epollErr != nil {
+ if conn.epollErr = syscall.EpollCtl(
+ conn.epollFd,
+ syscall.EPOLL_CTL_ADD,
+ conn.fd,
+ &syscall.EpollEvent{
+ Events: syscall.EPOLLERR | syscall.EPOLLHUP,
+ Fd: int32(conn.fd),
+ },
+ ); conn.epollErr != nil {
_ = syscall.Close(conn.epollFd)
}
}
@@ -209,16 +240,25 @@ func (conn *unixConn) wait(event uint32) (err error) {
deadline := time.Now().Add(conn.timeout)
conn.timeout = 0
- if err = syscall.EpollCtl(conn.epollFd, syscall.EPOLL_CTL_MOD, conn.fd, &syscall.EpollEvent{
- Events: event | syscall.EPOLLERR | syscall.EPOLLHUP,
- Fd: int32(conn.fd),
- }); err != nil {
+ if err = syscall.EpollCtl(
+ conn.epollFd,
+ syscall.EPOLL_CTL_MOD,
+ conn.fd,
+ &syscall.EpollEvent{
+ Events: event | syscall.EPOLLERR | syscall.EPOLLHUP,
+ Fd: int32(conn.fd),
+ },
+ ); err != nil {
return
}
for timeout := deadline.Sub(time.Now()); timeout > 0; timeout = deadline.Sub(time.Now()) {
var n int
- if n, err = syscall.EpollWait(conn.epollFd, conn.epollBuf[:], int(timeout/time.Millisecond)); err != nil {
+ if n, err = syscall.EpollWait(
+ conn.epollFd,
+ conn.epollBuf[:],
+ int(timeout/time.Millisecond),
+ ); err != nil {
return
}
@@ -245,7 +285,10 @@ func (conn *unixConn) wait(event uint32) (err error) {
}
// Recvmsg calls syscall.Recvmsg on the underlying socket.
-func (conn *unixConn) Recvmsg(p, oob []byte, flags int) (n, oobn, recvflags int, err error) {
+func (conn *unixConn) Recvmsg(p, oob []byte, flags int) (
+ n, oobn, recvflags int,
+ err error,
+) {
if err = conn.wantsEpoll(); err != nil {
return
} else if err = conn.wait(syscall.EPOLLIN); err != nil {
@@ -279,8 +322,8 @@ func (conn *unixConn) Close() (err error) {
return conn.epollErr
}
-// MustNew calls [New](conn, props) and panics on error.
-// It is intended for use in tests with hard-coded strings.
+// MustNew calls [New](conn, props) and panics on error. It is intended for use
+// in tests with hard-coded strings.
func MustNew(conn Conn, props SPADict) *Context {
if ctx, err := New(conn, props); err != nil {
panic(err)
@@ -292,20 +335,21 @@ func MustNew(conn Conn, props SPADict) *Context {
// free releases the underlying storage of buf.
func (ctx *Context) free() { ctx.buf = make([]byte, 0) }
-// queueFiles queues some file descriptors to be sent for the next message.
-// It returns the offset of their index for the syscall.SCM_RIGHTS message.
+// queueFiles queues some file descriptors to be sent for the next message. It
+// returns the offset of their index for the syscall.SCM_RIGHTS message.
func (ctx *Context) queueFiles(fds ...int) (offset Fd) {
offset = Fd(len(ctx.pendingFiles))
ctx.pendingFiles = append(ctx.pendingFiles, fds...)
return
}
-// InconsistentFilesError describes an implementation error where an incorrect amount
-// of files is queued between two messages.
+// InconsistentFilesError describes an implementation error where an incorrect
+// amount of files is queued between two messages.
type InconsistentFilesError [2]Int
func (e *InconsistentFilesError) Error() string {
- return "queued " + strconv.Itoa(int(e[0])) + " files instead of the expected " + strconv.Itoa(int(e[1]))
+ return "queued " + strconv.Itoa(int(e[0])) +
+ " files instead of the expected " + strconv.Itoa(int(e[1]))
}
// writeMessage appends the POD representation of v and an optional footer to buf.
@@ -318,7 +362,12 @@ func (ctx *Context) writeMessage(Id Int, v Message) (err error) {
ctx.pendingFooter, ctx.deferredPendingFooter = ctx.deferredPendingFooter, nil
}
- ctx.buf, err = MessageEncoder{v}.AppendMessage(ctx.buf, Id, ctx.sequence, ctx.pendingFooter)
+ ctx.buf, err = MessageEncoder{v}.AppendMessage(
+ ctx.buf,
+ Id,
+ ctx.sequence,
+ ctx.pendingFooter,
+ )
if err == nil {
ctx.headerFiles = len(ctx.pendingFiles)
ctx.pendingFooter = nil
@@ -327,8 +376,8 @@ func (ctx *Context) writeMessage(Id Int, v Message) (err error) {
return
}
-// mustWriteMessage calls writeMessage and panics if a non-nil error is returned.
-// This must only be called from eventProxy.consume.
+// mustWriteMessage calls writeMessage and panics if a non-nil error is
+// returned. This must only be called from eventProxy.consume.
func (ctx *Context) mustWriteMessage(Id Int, v Message) {
if err := ctx.writeMessage(Id, v); err != nil {
panic(err)
@@ -352,8 +401,8 @@ increment:
return newId
}
-// closeReceivedFiles closes all receivedFiles. This is only during protocol error
-// where [Context] is rendered unusable.
+// closeReceivedFiles closes all receivedFiles. This is only during protocol
+// error where [Context] is rendered unusable.
func (ctx *Context) closeReceivedFiles() {
slices.Sort(ctx.receivedFiles)
ctx.receivedFiles = slices.Compact(ctx.receivedFiles)
@@ -377,7 +426,11 @@ func (ctx *Context) recvmsg(remaining []byte) (payload []byte, err error) {
var n, oobn, recvflags int
for {
- n, oobn, recvflags, err = ctx.conn.Recvmsg(ctx.iovecBuf[len(remaining):], ctx.oobBuf[:], recvmsgFlags)
+ n, oobn, recvflags, err = ctx.conn.Recvmsg(
+ ctx.iovecBuf[len(remaining):],
+ ctx.oobBuf[:],
+ recvmsgFlags,
+ )
if oob := ctx.oobBuf[:oobn]; len(oob) > 0 {
var oobErr error
@@ -412,7 +465,10 @@ func (ctx *Context) recvmsg(remaining []byte) (payload []byte, err error) {
}
if err != syscall.EAGAIN && err != syscall.EWOULDBLOCK {
ctx.closeReceivedFiles()
- return nil, &ProxyFatalError{Err: os.NewSyscallError("recvmsg", err), ProxyErrs: ctx.cloneAsProxyErrors()}
+ return nil, &ProxyFatalError{
+ Err: os.NewSyscallError("recvmsg", err),
+ ProxyErrs: ctx.cloneAsProxyErrors(),
+ }
}
}
@@ -449,7 +505,10 @@ func (ctx *Context) sendmsg(p []byte, fds ...int) error {
}
if err != nil && err != syscall.EAGAIN && err != syscall.EWOULDBLOCK {
- return &ProxyFatalError{Err: os.NewSyscallError("sendmsg", err), ProxyErrs: ctx.cloneAsProxyErrors()}
+ return &ProxyFatalError{
+ Err: os.NewSyscallError("sendmsg", err),
+ ProxyErrs: ctx.cloneAsProxyErrors(),
+ }
}
return err
}
@@ -463,7 +522,9 @@ type UnknownIdError struct {
Data string
}
-func (e *UnknownIdError) Error() string { return "unknown proxy id " + strconv.Itoa(int(e.Id)) }
+func (e *UnknownIdError) Error() string {
+ return "unknown proxy id " + strconv.Itoa(int(e.Id))
+}
// UnsupportedOpcodeError describes a message with an unsupported opcode.
type UnsupportedOpcodeError struct {
@@ -484,7 +545,8 @@ func (e UnsupportedFooterOpcodeError) Error() string {
return "unsupported footer opcode " + strconv.Itoa(int(e))
}
-// A RoundtripUnexpectedEOFError describes an unexpected EOF encountered during [Context.Roundtrip].
+// A RoundtripUnexpectedEOFError describes an unexpected EOF encountered during
+// [Context.Roundtrip].
type RoundtripUnexpectedEOFError uintptr
const (
@@ -494,11 +556,11 @@ const (
// ErrRoundtripEOFBody is returned when unexpectedly encountering EOF
// establishing message body bounds.
ErrRoundtripEOFBody
- // ErrRoundtripEOFFooter is like [ErrRoundtripEOFBody], but for when establishing
- // bounds for the footer instead.
+ // ErrRoundtripEOFFooter is like [ErrRoundtripEOFBody], but for when
+ // establishing bounds for the footer instead.
ErrRoundtripEOFFooter
- // ErrRoundtripEOFFooterOpcode is returned when unexpectedly encountering EOF
- // during the footer opcode hack.
+ // ErrRoundtripEOFFooterOpcode is returned when unexpectedly encountering
+ // EOF during the footer opcode hack.
ErrRoundtripEOFFooterOpcode
)
@@ -549,10 +611,10 @@ func (ctx *Context) unmarshal(header *Header, data []byte, v any) error {
data = data[n:header.Size]
if len(data) > 0 {
- /* the footer concrete type is determined by opcode, which cannot be
- decoded directly before the type is known, so this hack is required:
- skip the struct prefix, then the integer prefix, and the next SizeId
- bytes are the encoded opcode value */
+ // the footer concrete type is determined by opcode, which cannot be
+ // decoded directly before the type is known, so this hack is required:
+ // skip the struct prefix, then the integer prefix, and the next SizeId
+ // bytes are the encoded opcode value
if len(data) < int(SizePrefix*2+SizeId) {
return ErrRoundtripEOFFooterOpcode
}
@@ -565,11 +627,14 @@ func (ctx *Context) unmarshal(header *Header, data []byte, v any) error {
if ctx.generation != footer.Payload.RegistryGeneration {
var pendingFooter = Footer[FooterClientGeneration]{
FOOTER_CORE_OPCODE_GENERATION,
- FooterClientGeneration{ClientGeneration: footer.Payload.RegistryGeneration},
+ FooterClientGeneration{
+ ClientGeneration: footer.Payload.RegistryGeneration,
+ },
}
- // this emulates upstream behaviour that pending footer updated on the last message
- // during a roundtrip is pushed back to the first message of the next roundtrip
+ // this emulates upstream behaviour that pending footer updated
+ // on the last message during a roundtrip is pushed back to the
+ // first message of the next roundtrip
if isLastMessage {
ctx.deferredPendingFooter = &pendingFooter
} else {
@@ -592,38 +657,43 @@ func (ctx *Context) unmarshal(header *Header, data []byte, v any) error {
// is, in fact, not the message header.
type UnexpectedSequenceError Int
-func (e UnexpectedSequenceError) Error() string { return "unexpected seq " + strconv.Itoa(int(e)) }
+func (e UnexpectedSequenceError) Error() string {
+ return "unexpected seq " + strconv.Itoa(int(e))
+}
-// An UnexpectedFilesError describes an inconsistent state where file count claimed by
-// [Header] accumulates to a value greater than the total number of files received.
+// An UnexpectedFilesError describes an inconsistent state where file count
+// claimed by [Header] accumulates to a value greater than the total number of
+// files received.
type UnexpectedFilesError int
func (e UnexpectedFilesError) Error() string {
return "server message headers claim to have sent more files than actually received"
}
-// A DanglingFilesError holds onto files that were sent by the server but no [Header]
-// accounts for. These are closed by their finalizers if discarded.
+// A DanglingFilesError holds onto files that were sent by the server but no
+// [Header] accounts for. These are closed by their finalizers if discarded.
type DanglingFilesError []*os.File
func (e DanglingFilesError) Error() string {
return "received " + strconv.Itoa(len(e)) + " dangling files"
}
-// An UnacknowledgedProxyError holds newly allocated proxy ids that the server failed
-// to acknowledge after an otherwise successful [Core.Sync].
+// An UnacknowledgedProxyError holds newly allocated proxy ids that the server
+// failed to acknowledge after an otherwise successful [Core.Sync].
type UnacknowledgedProxyError []Int
func (e UnacknowledgedProxyError) Error() string {
- return "server did not acknowledge " + strconv.Itoa(len(e)) + " proxies"
+ return "server did not acknowledge " +
+ strconv.Itoa(len(e)) + " proxies"
}
-// An UnacknowledgedProxyDestructionError holds destroyed proxy ids that the server failed
-// to acknowledge after an otherwise successful [Core.Sync].
+// An UnacknowledgedProxyDestructionError holds destroyed proxy ids that the
+// server failed to acknowledge after an otherwise successful [Core.Sync].
type UnacknowledgedProxyDestructionError []Int
func (e UnacknowledgedProxyDestructionError) Error() string {
- return "server did not acknowledge " + strconv.Itoa(len(e)) + " proxy destructions"
+ return "server did not acknowledge " +
+ strconv.Itoa(len(e)) + " proxy destructions"
}
// A ProxyFatalError describes an error that terminates event handling during a
@@ -639,14 +709,15 @@ func (e *ProxyFatalError) Unwrap() []error { return append(e.ProxyErrs, e.Err) }
func (e *ProxyFatalError) Error() string {
s := e.Err.Error()
if len(e.ProxyErrs) > 0 {
- s += "; " + strconv.Itoa(len(e.ProxyErrs)) + " additional proxy errors occurred before this point"
+ s += "; " + strconv.Itoa(len(e.ProxyErrs)) +
+ " additional proxy errors occurred before this point"
}
return s
}
-// A ProxyConsumeError is a collection of non-protocol errors returned by proxies
-// during event processing. These do not prevent event handling from continuing but
-// may be considered fatal to the application.
+// A ProxyConsumeError is a collection of non-protocol errors returned by
+// proxies during event processing. These do not prevent event handling from
+// continuing but may be considered fatal to the application.
type ProxyConsumeError []error
func (e ProxyConsumeError) Unwrap() []error { return e }
@@ -658,7 +729,8 @@ func (e ProxyConsumeError) Error() string {
// first error is usually the most relevant one
s := e[0].Error()
if len(e) > 1 {
- s += "; " + strconv.Itoa(len(e)) + " additional proxy errors occurred after this point"
+ s += "; " + strconv.Itoa(len(e)) +
+ " additional proxy errors occurred after this point"
}
return s
}
@@ -684,14 +756,15 @@ func (ctx *Context) cloneProxyErrors() (err error) {
return
}
-// roundtripSyncID is the id passed to Context.coreSync during a [Context.Roundtrip].
+// roundtripSyncID is the id passed to Context.coreSync during a
+// [Context.Roundtrip].
const roundtripSyncID = 0
// Roundtrip sends all pending messages to the server and processes events until
// the server has no more messages.
//
-// For a non-nil error, if the error happens over the network, it has concrete type
-// [os.SyscallError].
+// For a non-nil error, if the error happens over the network, it has concrete
+// type [os.SyscallError].
func (ctx *Context) Roundtrip() (err error) {
err = ctx.roundtrip()
if err == nil {
@@ -725,10 +798,12 @@ func (ctx *Context) roundtrip() (err error) {
danglingFiles = make(DanglingFilesError, 0, len(ctx.receivedFiles))
for _, fd := range ctx.receivedFiles {
- // hold these as *os.File so they are closed if this error never reaches the caller,
- // or the caller discards or otherwise does not handle this error, to avoid leaking fds
+ // hold these as *os.File so they are closed if this error never
+ // reaches the caller, or the caller discards or otherwise does
+ // not handle this error, to avoid leaking fds
danglingFiles = append(danglingFiles, os.NewFile(uintptr(fd),
- "dangling fd "+strconv.Itoa(fd)+" received from PipeWire"))
+ "dangling fd "+strconv.Itoa(fd)+
+ " received from PipeWire"))
}
ctx.receivedFiles = ctx.receivedFiles[:0]
}
@@ -736,7 +811,10 @@ func (ctx *Context) roundtrip() (err error) {
// populated early for finalizers, but does not overwrite existing errors
if len(danglingFiles) > 0 && err == nil {
ctx.closeReceivedFiles()
- err = &ProxyFatalError{Err: danglingFiles, ProxyErrs: ctx.cloneAsProxyErrors()}
+ err = &ProxyFatalError{
+ Err: danglingFiles,
+ ProxyErrs: ctx.cloneAsProxyErrors(),
+ }
return
}
}()
@@ -754,9 +832,15 @@ func (ctx *Context) roundtrip() (err error) {
if len(remaining) == 0 {
err = nil
} else if len(remaining) < SizeHeader {
- err = &ProxyFatalError{Err: ErrRoundtripEOFHeader, ProxyErrs: ctx.cloneAsProxyErrors()}
+ err = &ProxyFatalError{
+ Err: ErrRoundtripEOFHeader,
+ ProxyErrs: ctx.cloneAsProxyErrors(),
+ }
} else {
- err = &ProxyFatalError{Err: ErrRoundtripEOFBody, ProxyErrs: ctx.cloneAsProxyErrors()}
+ err = &ProxyFatalError{
+ Err: ErrRoundtripEOFBody,
+ ProxyErrs: ctx.cloneAsProxyErrors(),
+ }
}
}
return
@@ -772,7 +856,10 @@ func (ctx *Context) currentSeq() Int { return ctx.sequence - 1 }
func (ctx *Context) currentRemoteSeq() Int { return ctx.remoteSequence - 1 }
// consume receives messages from the server and processes events.
-func (ctx *Context) consume(receiveRemaining []byte) (remaining []byte, err error) {
+func (ctx *Context) consume(receiveRemaining []byte) (
+ remaining []byte,
+ err error,
+) {
defer func() {
r := recover()
if r == nil {
@@ -788,7 +875,10 @@ func (ctx *Context) consume(receiveRemaining []byte) (remaining []byte, err erro
panic(&runtime.PanicNilError{})
}
- err = &ProxyFatalError{Err: recoveredErr, ProxyErrs: ctx.cloneAsProxyErrors()}
+ err = &ProxyFatalError{
+ Err: recoveredErr,
+ ProxyErrs: ctx.cloneAsProxyErrors(),
+ }
return
}()
@@ -816,7 +906,10 @@ func (ctx *Context) consume(receiveRemaining []byte) (remaining []byte, err erro
proxy, ok := ctx.proxy[header.ID]
if !ok {
- return remaining, &UnknownIdError{header.ID, string(remaining[:SizeHeader+header.Size])}
+ return remaining, &UnknownIdError{
+ header.ID,
+ string(remaining[:SizeHeader+header.Size]),
+ }
}
fileCount := int(header.FileCount)
@@ -828,7 +921,11 @@ func (ctx *Context) consume(receiveRemaining []byte) (remaining []byte, err erro
remaining = remaining[SizeHeader:]
proxyErr := proxy.consume(header.Opcode, files, func(v any) {
- if unmarshalErr := ctx.unmarshal(&header, remaining, v); unmarshalErr != nil {
+ if unmarshalErr := ctx.unmarshal(
+ &header,
+ remaining,
+ v,
+ ); unmarshalErr != nil {
panic(unmarshalErr)
}
})
@@ -840,16 +937,17 @@ func (ctx *Context) consume(receiveRemaining []byte) (remaining []byte, err erro
return
}
-// An UnexpectedFileCountError is returned as part of a [ProxyFatalError] for an event
-// that received an unexpected number of files.
+// An UnexpectedFileCountError is returned as part of a [ProxyFatalError] for an
+// event that received an unexpected number of files.
type UnexpectedFileCountError [2]int
func (e *UnexpectedFileCountError) Error() string {
return "received " + strconv.Itoa(e[1]) + " files instead of the expected " + strconv.Itoa(e[0])
}
-// closeReceivedFiles closes all received files and panics with [UnexpectedFileCountError]
-// if one or more files are passed. This is used with events that do not expect files.
+// closeReceivedFiles closes all received files and panics with
+// [UnexpectedFileCountError] if one or more files are passed. This is used with
+// events that do not expect files.
func closeReceivedFiles(fds ...int) {
for _, fd := range fds {
_ = syscall.Close(fd)
@@ -859,8 +957,9 @@ func closeReceivedFiles(fds ...int) {
}
}
-// doSyncComplete calls syncComplete functions and collects their errors alongside errors
-// cloned from proxyErrors. A panic is translated into ProxyFatalError.
+// doSyncComplete calls syncComplete functions and collects their errors
+// alongside errors cloned from proxyErrors. A panic is translated into
+// ProxyFatalError.
func (ctx *Context) doSyncComplete() (err error) {
proxyErrors := ctx.cloneAsProxyErrors()
defer func() {
@@ -914,17 +1013,17 @@ func (ctx *Context) Close() (err error) {
}
}
-// expectsCoreError returns a function that inspects an error value and
-// returns the address of a [CoreError] if it is the only error present
-// and targets the specified proxy and sequence.
+// expectsCoreError returns a function that inspects an error value and returns
+// the address of a [CoreError] if it is the only error present and targets the
+// specified proxy and sequence.
//
-// The behaviour of expectsCoreError is only correct for an empty buf
-// prior to calling. If buf is not empty, [Core.Sync] is called, with
-// its return value stored to the value pointed to by errP if not nil,
-// and the function is not populated.
+// The behaviour of expectsCoreError is only correct for an empty buf prior to
+// calling. If buf is not empty, [Core.Sync] is called, with its return value
+// stored to the value pointed to by errP if not nil, and the function is not
+// populated.
//
-// The caller must queue a message and call [Core.Sync] immediately
-// after calling expectsCoreError.
+// The caller must queue a message and call [Core.Sync] immediately after
+// calling expectsCoreError.
func (ctx *Context) expectsCoreError(id Int, errP *error) (asCoreError func() (coreError *CoreError)) {
if len(ctx.buf) > 0 {
if err := ctx.GetCore().Sync(); err != nil {
@@ -967,7 +1066,8 @@ const Remote = "PIPEWIRE_REMOTE"
const DEFAULT_SYSTEM_RUNTIME_DIR = "/run/pipewire"
-// connectName connects to a PipeWire remote by name and returns the resulting [Conn].
+// connectName connects to a PipeWire remote by name and returns the resulting
+// [Conn].
func connectName(name string, manager bool) (conn Conn, err error) {
if manager && !strings.HasSuffix(name, "-manager") {
return connectName(name+"-manager", false)
diff --git a/internal/pipewire/pod.go b/internal/pipewire/pod.go
index e085af3d..3a03540b 100644
--- a/internal/pipewire/pod.go
+++ b/internal/pipewire/pod.go
@@ -12,7 +12,8 @@ import (
type (
// A Word is a 32-bit unsigned integer.
//
- // Values internal to a message appear to always be aligned to 32-bit boundary.
+ // Values internal to a message appear to always be aligned to 32-bit
+ // boundary.
Word = uint32
// A Bool is a boolean value representing SPA_TYPE_Bool.
@@ -40,9 +41,11 @@ const (
// SizeAlign is the boundary which POD starts are always aligned to.
SizeAlign = 8
- // SizeSPrefix is the fixed, unpadded size of the fixed-size prefix encoding POD wire size.
+ // SizeSPrefix is the fixed, unpadded size of the fixed-size prefix encoding
+ // POD wire size.
SizeSPrefix = 4
- // SizeTPrefix is the fixed, unpadded size of the fixed-size prefix encoding POD value type.
+ // SizeTPrefix is the fixed, unpadded size of the fixed-size prefix encoding
+ // POD value type.
SizeTPrefix = 4
// SizePrefix is the fixed, unpadded size of the fixed-size POD prefix.
SizePrefix = SizeSPrefix + SizeTPrefix
@@ -76,11 +79,11 @@ func Size[W Word | int](wireSize W) W { return SizePrefix + PaddedSize(wireSize)
// SizeString returns prefixed and padded size corresponding to a string.
func SizeString[W Word | int](s string) W { return Size(W(len(s)) + 1) }
-// PODMarshaler is the interface implemented by an object that can
-// marshal itself into PipeWire POD encoding.
+// PODMarshaler is the interface implemented by an object that can marshal
+// itself into PipeWire POD encoding.
type PODMarshaler interface {
- // MarshalPOD encodes the receiver into PipeWire POD encoding,
- // appends it to data, and returns the result.
+ // MarshalPOD encodes the receiver into PipeWire POD encoding, appends it to
+ // data, and returns the result.
MarshalPOD(data []byte) ([]byte, error)
}
@@ -90,9 +93,9 @@ type UnsupportedTypeError struct{ Type reflect.Type }
func (e *UnsupportedTypeError) Error() string { return "unsupported type " + e.Type.String() }
-// An UnsupportedSizeError is returned by [Marshal] when attempting
-// to encode a value with its encoded size exceeding what could be
-// represented by the format.
+// An UnsupportedSizeError is returned by [Marshal] when attempting to encode a
+// value with its encoded size exceeding what could be represented by the
+// format.
type UnsupportedSizeError int
func (e UnsupportedSizeError) Error() string { return "size " + strconv.Itoa(int(e)) + " out of range" }
@@ -111,8 +114,8 @@ func MarshalAppend(data []byte, v any) ([]byte, error) {
return marshalValueAppend(data, reflect.ValueOf(v))
}
-// appendInner calls f and handles size prefix and padding around the appended data.
-// f must only append to data.
+// appendInner calls f and handles size prefix and padding around the appended
+// data. f must only append to data.
func appendInner(data []byte, f func(data []byte) ([]byte, error)) ([]byte, error) {
data = append(data, make([]byte, SizeSPrefix)...)
@@ -146,7 +149,8 @@ func marshalValueAppend(data []byte, v reflect.Value) ([]byte, error) {
return appendInner(data, func(data []byte) ([]byte, error) { return marshalValueAppendRaw(data, v) })
}
-// marshalValueAppendRaw implements [MarshalAppend] on [reflect.Value] without the size prefix.
+// marshalValueAppendRaw implements [MarshalAppend] on [reflect.Value] without
+// the size prefix.
func marshalValueAppendRaw(data []byte, v reflect.Value) ([]byte, error) {
if v.CanInterface() {
switch c := v.Interface().(type) {
@@ -202,10 +206,11 @@ func marshalValueAppendRaw(data []byte, v reflect.Value) ([]byte, error) {
}
}
-// PODUnmarshaler is the interface implemented by an object that can
-// unmarshal a PipeWire POD encoding representation of itself.
+// PODUnmarshaler is the interface implemented by an object that can unmarshal a
+// PipeWire POD encoding representation of itself.
type PODUnmarshaler interface {
// UnmarshalPOD must be able to decode the form generated by MarshalPOD.
+ //
// UnmarshalPOD must copy the data if it wishes to retain the data
// after returning.
UnmarshalPOD(data []byte) (Word, error)
@@ -226,15 +231,16 @@ func (e *InvalidUnmarshalError) Error() string {
return "attempting to unmarshal to nil " + e.Type.String()
}
-// UnexpectedEOFError describes an unexpected EOF encountered in the middle of decoding POD data.
+// UnexpectedEOFError describes an unexpected EOF encountered in the middle of
+// decoding POD data.
type UnexpectedEOFError uintptr
const (
- // ErrEOFPrefix is returned when unexpectedly encountering EOF
- // decoding the fixed-size POD prefix.
+ // ErrEOFPrefix is returned when unexpectedly encountering EOF decoding the
+ // fixed-size POD prefix.
ErrEOFPrefix UnexpectedEOFError = iota
- // ErrEOFData is returned when unexpectedly encountering EOF
- // establishing POD data bounds.
+ // ErrEOFData is returned when unexpectedly encountering EOF establishing
+ // POD data bounds.
ErrEOFData
// ErrEOFDataString is returned when unexpectedly encountering EOF
// establishing POD [String] bounds.
@@ -259,9 +265,9 @@ func (e UnexpectedEOFError) Error() string {
return "unexpected EOF " + suffix
}
-// Unmarshal parses the PipeWire POD encoded data and stores the result
-// in the value pointed to by v. If v is nil or not a pointer,
-// Unmarshal returns an [InvalidUnmarshalError].
+// Unmarshal parses the PipeWire POD encoded data and stores the result in the
+// value pointed to by v. If v is nil or not a pointer, Unmarshal returns an
+// [InvalidUnmarshalError].
func Unmarshal(data []byte, v any) error {
if n, err := UnmarshalNext(data, v); err != nil {
return err
@@ -291,8 +297,8 @@ type UnmarshalSetError struct{ Type reflect.Type }
func (u *UnmarshalSetError) Error() string { return "cannot set " + u.Type.String() }
-// A TrailingGarbageError describes extra bytes after decoding
-// has completed during [Unmarshal].
+// A TrailingGarbageError describes extra bytes after decoding has completed
+// during [Unmarshal].
type TrailingGarbageError []byte
func (e TrailingGarbageError) Error() string {
@@ -310,7 +316,8 @@ func (e StringTerminationError) Error() string {
return "got byte " + strconv.Itoa(int(e)) + " instead of NUL"
}
-// unmarshalValue implements [Unmarshal] on [reflect.Value] without compensating for prefix and padding size.
+// unmarshalValue implements [Unmarshal] on [reflect.Value] without compensating
+// for prefix and padding size.
func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error {
if !v.CanSet() {
return &UnmarshalSetError{v.Type()}
@@ -330,7 +337,11 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error {
switch v.Interface().(type) {
case Fd:
*wireSizeP = SizeFd
- if err := unmarshalCheckTypeBounds(&data, SPA_TYPE_Fd, wireSizeP); err != nil {
+ if err := unmarshalCheckTypeBounds(
+ &data,
+ SPA_TYPE_Fd,
+ wireSizeP,
+ ); err != nil {
return err
}
v.SetInt(int64(binary.NativeEndian.Uint64(data)))
@@ -341,7 +352,11 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error {
switch v.Kind() {
case reflect.Uint32:
*wireSizeP = SizeId
- if err := unmarshalCheckTypeBounds(&data, SPA_TYPE_Id, wireSizeP); err != nil {
+ if err := unmarshalCheckTypeBounds(
+ &data,
+ SPA_TYPE_Id,
+ wireSizeP,
+ ); err != nil {
return err
}
v.SetUint(uint64(binary.NativeEndian.Uint32(data)))
@@ -349,7 +364,11 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error {
case reflect.Int32:
*wireSizeP = SizeInt
- if err := unmarshalCheckTypeBounds(&data, SPA_TYPE_Int, wireSizeP); err != nil {
+ if err := unmarshalCheckTypeBounds(
+ &data,
+ SPA_TYPE_Int,
+ wireSizeP,
+ ); err != nil {
return err
}
v.SetInt(int64(binary.NativeEndian.Uint32(data)))
@@ -357,7 +376,11 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error {
case reflect.Int64:
*wireSizeP = SizeLong
- if err := unmarshalCheckTypeBounds(&data, SPA_TYPE_Long, wireSizeP); err != nil {
+ if err := unmarshalCheckTypeBounds(
+ &data,
+ SPA_TYPE_Long,
+ wireSizeP,
+ ); err != nil {
return err
}
v.SetInt(int64(binary.NativeEndian.Uint64(data)))
@@ -365,7 +388,11 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error {
case reflect.Struct:
*wireSizeP = 0
- if err := unmarshalCheckTypeBounds(&data, SPA_TYPE_Struct, wireSizeP); err != nil {
+ if err := unmarshalCheckTypeBounds(
+ &data,
+ SPA_TYPE_Struct,
+ wireSizeP,
+ ); err != nil {
return err
}
@@ -396,7 +423,11 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error {
case reflect.String:
*wireSizeP = 0
- if err := unmarshalCheckTypeBounds(&data, SPA_TYPE_String, wireSizeP); err != nil {
+ if err := unmarshalCheckTypeBounds(
+ &data,
+ SPA_TYPE_String,
+ wireSizeP,
+ ); err != nil {
return err
}
@@ -419,8 +450,9 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error {
}
}
-// unmarshalHandleNone establishes prefix bounds and, for a value of type [SPA_TYPE_None],
-// validates its size and skips the header. This is for unmarshalling values that can be nil.
+// unmarshalHandleNone establishes prefix bounds and, for a value of type
+// [SPA_TYPE_None], validates its size and skips the header. This is for
+// unmarshalling values that can be nil.
func unmarshalHandleNone(data *[]byte, wireSizeP *Word) (bool, error) {
if len(*data) < SizePrefix {
return false, ErrEOFPrefix
@@ -451,15 +483,17 @@ func (e InconsistentSizeError) Error() string {
" for a " + strconv.Itoa(int(e.Expect)) + "-byte long segment"
}
-// An UnexpectedTypeError describes an unexpected type encountered
-// in data passed to [Unmarshal].
+// An UnexpectedTypeError describes an unexpected type encountered in data
+// passed to [Unmarshal].
type UnexpectedTypeError struct{ Type, Expect SPAKind }
func (e UnexpectedTypeError) Error() string {
return "received " + e.Type.String() + " for a value of type " + e.Expect.String()
}
-// unmarshalCheckTypeBounds performs bounds checks on data and validates the type and size prefixes.
+// unmarshalCheckTypeBounds performs bounds checks on data and validates the
+// type and size prefixes.
+//
// An expected size of zero skips further bounds checks.
func unmarshalCheckTypeBounds(data *[]byte, t SPAKind, sizeP *Word) error {
if len(*data) < SizePrefix {
@@ -486,8 +520,8 @@ func unmarshalCheckTypeBounds(data *[]byte, t SPAKind, sizeP *Word) error {
return nil
}
-// The Footer contains additional messages, not directed to
-// the destination object defined by the Id field.
+// The Footer contains additional messages, not directed to the destination
+// object defined by the Id field.
type Footer[P KnownSize] struct {
// The footer opcode.
Opcode Id `json:"opcode"`
@@ -595,7 +629,8 @@ func (d *SPADict) UnmarshalPOD(data []byte) (Word, error) {
return wireSize, nil
}
-// A Message is a value that can be transmitted as a message over PipeWire protocol native.
+// A Message is a value that can be transmitted as a message over PipeWire
+// protocol native.
type Message interface {
// Opcode returns the opcode of this message.
Opcode() byte
@@ -617,8 +652,13 @@ func (m MessageEncoder) SizeMessage(footer KnownSize) (size Word) {
return
}
-// AppendMessage appends the protocol native encoding of Message to dst and returns the appended slice.
-func (m MessageEncoder) AppendMessage(dst []byte, Id, sequence Int, footer KnownSize) (data []byte, err error) {
+// AppendMessage appends the protocol native encoding of Message to dst and
+// returns the appended slice.
+func (m MessageEncoder) AppendMessage(
+ dst []byte,
+ Id, sequence Int,
+ footer KnownSize,
+) (data []byte, err error) {
size := m.SizeMessage(footer)
if size&^SizeMax != 0 {
return dst, ErrSizeRange
diff --git a/internal/pipewire/securitycontext.go b/internal/pipewire/securitycontext.go
index ec19a64a..f277a99e 100644
--- a/internal/pipewire/securitycontext.go
+++ b/internal/pipewire/securitycontext.go
@@ -35,31 +35,31 @@ const (
// SecurityContextCreate is sent to create a new security context.
//
-// Creates a new security context with a socket listening FD.
-// PipeWire will accept new client connections on listen_fd.
+// Creates a new security context with a socket listening FD. PipeWire will
+// accept new client connections on listen_fd.
//
-// listen_fd must be ready to accept new connections when this
-// request is sent by the client. In other words, the client must
-// call bind(2) and listen(2) before sending the FD.
+// listen_fd must be ready to accept new connections when this request is sent
+// by the client. In other words, the client must call bind(2) and listen(2)
+// before sending the FD.
//
-// close_fd is a FD closed by the client when PipeWire should stop
-// accepting new connections on listen_fd.
+// close_fd is a FD closed by the client when PipeWire should stop accepting new
+// connections on listen_fd.
//
-// PipeWire must continue to accept connections on listen_fd when
-// the client which created the security context disconnects.
+// PipeWire must continue to accept connections on listen_fd when the client
+// which created the security context disconnects.
//
-// After sending this request, closing listen_fd and close_fd
-// remains the only valid operation on them.
+// After sending this request, closing listen_fd and close_fd remains the only
+// valid operation on them.
type SecurityContextCreate struct {
- // The offset in the SCM_RIGHTS msg_control message to
- // the fd to listen on for new connections.
+ // The offset in the SCM_RIGHTS msg_control message to the fd to listen on
+ // for new connections.
ListenFd Fd
- // The offset in the SCM_RIGHTS msg_control message to
- // the fd used to stop listening.
+ // The offset in the SCM_RIGHTS msg_control message to the fd used to stop
+ // listening.
CloseFd Fd
- // Extra properties. These will be copied on the client
- // that connects through this context.
+ // Extra properties. These will be copied on the client that connects
+ // through this context.
Properties *SPADict `json:"props"`
}
@@ -170,7 +170,9 @@ func (scc *securityContextCloser) Close() (err error) {
return
}
-// BindAndCreate binds a new socket to the specified pathname and pass it to Create.
+// BindAndCreate binds a new socket to the specified pathname and pass it to
+// Create.
+//
// It returns an [io.Closer] corresponding to [SecurityContextCreate.CloseFd].
func (securityContext *SecurityContext) BindAndCreate(pathname string, props SPADict) (io.Closer, error) {
if err := securityContext.checkDestroy(); err != nil {
@@ -231,10 +233,19 @@ func (securityContext *SecurityContext) consume(opcode byte, files []int, _ func
func (securityContext *SecurityContext) setBoundProps(event *CoreBoundProps) error {
securityContext.mustCheckDestroy()
if securityContext.ID != event.ID {
- return &InconsistentIdError{Proxy: securityContext, ID: securityContext.ID, ServerID: event.ID}
+ return &InconsistentIdError{
+ Proxy: securityContext,
+ ID: securityContext.ID,
+ ServerID: event.ID,
+ }
}
if securityContext.GlobalID != event.GlobalID {
- return &InconsistentIdError{Global: true, Proxy: securityContext, ID: securityContext.GlobalID, ServerID: event.GlobalID}
+ return &InconsistentIdError{
+ Global: true,
+ Proxy: securityContext,
+ ID: securityContext.GlobalID,
+ ServerID: event.GlobalID,
+ }
}
return nil
}
diff --git a/internal/pipewire/spa.go b/internal/pipewire/spa.go
index c44bdb83..b231cc07 100644
--- a/internal/pipewire/spa.go
+++ b/internal/pipewire/spa.go
@@ -7,10 +7,9 @@ import (
// A SPAKind describes the kind of data being encoded right after it.
//
-// These do not always follow the same rules, and encoding/decoding
-// is very much context-dependent. Callers should therefore not
-// attempt to use these values directly and rely on [Marshal] and
-// [Unmarshal] and their variants instead.
+// These do not always follow the same rules, and encoding/decoding is very much
+// context-dependent. Callers should therefore not attempt to use these values
+// directly and rely on [Marshal] and [Unmarshal] and their variants instead.
type SPAKind Word
/* Basic types */