pipewire_native/proxy/
port.rs

1// SPDX-License-Identifier: MIT
2// SPDX-FileCopyrightText: Copyright (c) 2025 Asymptotic Inc.
3// SPDX-FileCopyrightText: Copyright (c) 2025 Arun Raghavan
4
5use std::sync::{Arc, Mutex, RwLock};
6
7use bitflags::bitflags;
8use pipewire_native_macros as macros;
9use pipewire_native_spa as spa;
10
11use crate::{
12    core::Core,
13    new_refcounted,
14    properties::Properties,
15    protocol,
16    proxy::{HasProxy, Proxy},
17    proxy_object_invoke, refcounted,
18    types::{self, params::ParamBuilder},
19    HookId, Id, Refcounted,
20};
21
22refcounted! {
23    /// Proxy that represents a port that is connected to the server.
24    pub struct Port {
25        proxy: RwLock<Option<Proxy<Port>>>,
26        methods: Arc<Mutex<PortMethods<Port>>>,
27        hooks: Arc<Mutex<spa::hook::HookList<PortEvents>>>,
28    }
29}
30
31#[allow(clippy::type_complexity)]
32pub(crate) struct PortMethods<T: HasProxy + Refcounted> {
33    pub(crate) subscribe_params:
34        Box<dyn FnMut(&Proxy<T>, &[spa::param::ParamType]) -> std::io::Result<()>>,
35    pub(crate) enum_params: Box<
36        dyn FnMut(
37            &Proxy<T>,
38            u32,
39            Option<spa::param::ParamType>,
40            u32,
41            u32,
42            Option<ParamBuilder>,
43        ) -> std::io::Result<()>,
44    >,
45}
46
47bitflags! {
48    /// A bit mask of changes signalled in the [PortEvents::info] event.
49    #[repr(C)]
50    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
51    pub struct PortChangeMask : u32 {
52        /// Port properties changed.
53        const PROPS = (1 << 0);
54        /// Port params changed.
55        const PARAMS = (1 << 1);
56    }
57}
58
59#[repr(u32)]
60#[derive(Clone, Copy, Debug, Eq, PartialEq, macros::EnumU32)]
61/// Represents the direction of data flow for the port.
62pub enum PortDirection {
63    /// Input port.
64    Input,
65    /// Output port.
66    Output,
67}
68
69/// Port information that is provided in a [PortEvents::info] event.
70pub struct PortInfo<'a> {
71    /// The ID of the port.
72    pub id: Id,
73    /// The direction of the port
74    pub direction: PortDirection,
75    /// What changed since the last call.
76    pub mask: PortChangeMask,
77    /// The port's properties.
78    pub props: &'a Properties,
79    /// Port parameters that changed.
80    pub params: &'a [(spa::param::ParamType, spa::param::ParamInfoFlags)],
81}
82
83/// Port events that can be subscribed to.
84#[allow(clippy::type_complexity)]
85#[derive(Default)]
86pub struct PortEvents {
87    /// Port information became available, or changed.
88    pub info: Option<Box<dyn FnMut(&PortInfo<'_>) + Send>>,
89    /// Port permissions, notified due to a [Port::subscribe_params] or [Port::enum_params]
90    /// call.
91    pub param:
92        Option<Box<dyn FnMut(u32, spa::param::ParamType, u32, u32, &spa::pod::RawPodOwned) + Send>>,
93}
94
95impl HasProxy for Port {
96    fn type_(&self) -> types::ObjectType {
97        types::interface::PORT
98    }
99
100    fn version(&self) -> u32 {
101        3
102    }
103
104    fn proxy(&self) -> Proxy<Self> {
105        self.inner
106            .proxy
107            .read()
108            .unwrap()
109            .as_ref()
110            .expect("Port proxy should be initialised on creation")
111            .clone()
112    }
113}
114
115impl Port {
116    pub(crate) fn new(core: &Core) -> Self {
117        let this = Self {
118            inner: new_refcounted(InnerPort::new(core)),
119        };
120
121        let id = core.next_proxy_id();
122        this.inner
123            .proxy
124            .write()
125            .unwrap()
126            .replace(Proxy::new(id, &this));
127        core.add_proxy(&this, id);
128
129        this
130    }
131
132    /// Register for notifications of port events.
133    pub fn add_listener(&self, events: PortEvents) -> HookId {
134        self.inner.hooks.lock().unwrap().append(events)
135    }
136
137    /// Remove a set of event listeners.
138    pub fn remove_listener(&self, hook_id: HookId) {
139        self.inner.hooks.lock().unwrap().remove(hook_id);
140    }
141
142    /// Register for notifications of the specified param types.
143    pub fn subscribe_params(&self, ids: &[spa::param::ParamType]) -> std::io::Result<()> {
144        let proxy = self.proxy();
145        proxy_object_invoke!(proxy, subscribe_params, ids)
146    }
147
148    /// Enumerate params (via [PortEvents::param]). Set `id` to [None] to query all param types.
149    pub fn enum_params(
150        &self,
151        seq: u32,
152        id: Option<spa::param::ParamType>,
153        start: u32,
154        num: u32,
155        filter: Option<ParamBuilder>,
156    ) -> std::io::Result<()> {
157        let proxy = self.proxy();
158        proxy_object_invoke!(proxy, enum_params, seq, id, start, num, filter)
159    }
160
161    pub(crate) fn methods(&self) -> Arc<Mutex<PortMethods<Port>>> {
162        self.inner.methods.clone()
163    }
164
165    pub(crate) fn events(&self) -> Arc<Mutex<spa::hook::HookList<PortEvents>>> {
166        self.inner.hooks.clone()
167    }
168}
169
170impl InnerPort {
171    fn new(core: &Core) -> Self {
172        Self {
173            proxy: RwLock::new(None),
174            methods: Arc::new(Mutex::new(protocol::marshal::port::Methods::marshal(
175                core.connection(),
176            ))),
177            hooks: spa::hook::HookList::new(),
178        }
179    }
180}