pipewire_native/proxy/
link.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    proxy::{HasProxy, Proxy},
16    refcounted, types, HookId, Id,
17};
18
19refcounted! {
20    /// Proxy that represents a link that is loaded on the server.
21    pub struct Link {
22        proxy: RwLock<Option<Proxy<Link>>>,
23        hooks: Arc<Mutex<spa::hook::HookList<LinkEvents>>>,
24    }
25}
26
27bitflags! {
28    /// A bit mask of changes signalled in the [LinkEvents::info] event.
29    #[repr(C)]
30    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
31    pub struct LinkChangeMask : u32 {
32        /// Link properties changed.
33        const PROPS = (1 << 0);
34    }
35}
36
37#[repr(u32)]
38#[derive(Clone, Copy, Debug, Eq, PartialEq, macros::EnumU32)]
39/// Represents the current state of a link.
40pub enum LinkState {
41    /// Link is in an error state.
42    Error,
43    /// Link is unlinked.
44    Unlinked,
45    /// Link is initialised.
46    Init,
47    /// Link is negotiation formats.
48    Negotiation,
49    /// Link is allocating buffers.
50    Allocating,
51    /// Link is paused.
52    Paused,
53    /// Link is active.
54    Active,
55}
56
57/// Link information that is provided in a [LinkEvents::info] event.
58pub struct LinkInfo<'a> {
59    /// The ID of the link.
60    pub id: Id,
61    /// Global ID of the output node for this link.
62    pub output_node_id: Id,
63    /// Global ID of the output port for this link.
64    pub output_port_id: Id,
65    /// Global ID of the input node for this link.
66    pub input_node_id: Id,
67    /// Global ID of the input port for this link.
68    pub input_port_id: Id,
69    /// What changed since the last call.
70    pub mask: LinkChangeMask,
71    /// Current state of the link.
72    pub state: LinkState,
73    /// Error if `state` is [LinkState::Error].
74    pub error: Option<&'a str>,
75    /// Format of the link, if negotiated.
76    pub format: Option<&'a spa::pod::RawPodOwned>,
77    /// The link's properties.
78    pub props: &'a Properties,
79}
80
81/// Link events that can be subscribed to.
82#[allow(clippy::type_complexity)]
83#[derive(Default)]
84pub struct LinkEvents {
85    /// Link information became available, or changed.
86    pub info: Option<Box<dyn FnMut(&LinkInfo<'_>) + Send>>,
87}
88
89impl HasProxy for Link {
90    fn type_(&self) -> types::ObjectType {
91        types::interface::LINK
92    }
93
94    fn version(&self) -> u32 {
95        3
96    }
97
98    fn proxy(&self) -> Proxy<Self> {
99        self.inner
100            .proxy
101            .read()
102            .unwrap()
103            .as_ref()
104            .expect("Link proxy should be initialised on creation")
105            .clone()
106    }
107}
108
109impl Link {
110    pub(crate) fn new(core: &Core) -> Self {
111        let this = Self {
112            inner: new_refcounted(InnerLink::new()),
113        };
114
115        let id = core.next_proxy_id();
116        this.inner
117            .proxy
118            .write()
119            .unwrap()
120            .replace(Proxy::new(id, &this));
121        core.add_proxy(&this, id);
122
123        this
124    }
125
126    /// Register for notifications of link events.
127    pub fn add_listener(&self, events: LinkEvents) -> HookId {
128        self.inner.hooks.lock().unwrap().append(events)
129    }
130
131    /// Remove a set of event listeners.
132    pub fn remove_listener(&self, hook_id: HookId) {
133        self.inner.hooks.lock().unwrap().remove(hook_id);
134    }
135
136    pub(crate) fn events(&self) -> Arc<Mutex<spa::hook::HookList<LinkEvents>>> {
137        self.inner.hooks.clone()
138    }
139}
140
141impl InnerLink {
142    fn new() -> Self {
143        Self {
144            proxy: RwLock::new(None),
145            hooks: spa::hook::HookList::new(),
146        }
147    }
148}