pipewire_native/proxy/
client.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_spa as spa;
9
10use crate::{
11    core::Core,
12    new_refcounted, permission,
13    properties::Properties,
14    protocol,
15    proxy::{HasProxy, Proxy},
16    proxy_object_invoke, refcounted, types, HookId, Id, Refcounted,
17};
18
19refcounted! {
20    /// Proxy that represents a client that is connected to the server.
21    pub struct Client {
22        proxy: RwLock<Option<Proxy<Client>>>,
23        methods: Arc<Mutex<ClientMethods<Client>>>,
24        hooks: Arc<Mutex<spa::hook::HookList<ClientEvents>>>,
25    }
26}
27
28#[allow(clippy::type_complexity)]
29pub(crate) struct ClientMethods<T: HasProxy + Refcounted> {
30    pub(crate) error: Box<dyn FnMut(&Proxy<T>, u32, u32, &str) -> std::io::Result<()>>,
31    pub(crate) update_properties: Box<dyn FnMut(&Proxy<T>, &Properties) -> std::io::Result<()>>,
32    pub(crate) get_permissions: Box<dyn FnMut(&Proxy<T>, u32, u32) -> std::io::Result<()>>,
33    pub(crate) update_permissions:
34        Box<dyn FnMut(&Proxy<T>, &[permission::Permission]) -> std::io::Result<()>>,
35}
36
37bitflags! {
38    /// A bit mask of changes signalled in the [ClientEvents::info] event.
39    #[repr(C)]
40    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
41    pub struct ClientChangeMask : u32 {
42        /// Client properties changed.
43        const PROPS = (1 << 0);
44    }
45}
46
47/// Client information that is provided in a [ClientEvents::info] event.
48pub struct ClientInfo<'a> {
49    /// The ID of the client.
50    pub id: Id,
51    /// What changed since the last call.
52    pub mask: ClientChangeMask,
53    /// The client's properties.
54    pub props: &'a Properties,
55}
56
57/// Client events that can be subscribed to.
58#[allow(clippy::type_complexity)]
59#[derive(Default)]
60pub struct ClientEvents {
61    /// Client information became available, or changed.
62    pub info: Option<Box<dyn FnMut(&ClientInfo<'_>) + Send>>,
63    /// Client permissions, notified due to a [Client::permissions()] call.
64    pub permissions: Option<Box<dyn FnMut(u32, &[permission::Permission]) + Send>>,
65}
66
67impl HasProxy for Client {
68    fn type_(&self) -> types::ObjectType {
69        types::interface::CLIENT
70    }
71
72    fn version(&self) -> u32 {
73        3
74    }
75
76    fn proxy(&self) -> Proxy<Self> {
77        self.inner
78            .proxy
79            .read()
80            .unwrap()
81            .as_ref()
82            .expect("Client proxy should be initialised on creation")
83            .clone()
84    }
85}
86
87impl Client {
88    pub(crate) fn new(core: &Core) -> Self {
89        let this = Self {
90            inner: new_refcounted(InnerClient::new(core)),
91        };
92
93        let id = core.next_proxy_id();
94        this.inner
95            .proxy
96            .write()
97            .unwrap()
98            .replace(Proxy::new(id, &this));
99        core.add_proxy(&this, id);
100
101        this
102    }
103
104    /// Register for notifications of client events.
105    pub fn add_listener(&self, events: ClientEvents) -> HookId {
106        self.inner.hooks.lock().unwrap().append(events)
107    }
108
109    /// Remove a set of event listeners.
110    pub fn remove_listener(&self, hook_id: HookId) {
111        self.inner.hooks.lock().unwrap().remove(hook_id);
112    }
113
114    /// Signal an error to the client.
115    pub fn error(&self, id: u32, res: u32, message: &str) -> std::io::Result<()> {
116        let proxy = self.proxy();
117        proxy_object_invoke!(proxy, error, id, res, message)
118    }
119
120    /// Retrieve permissions of a client.
121    pub fn permissions(&self, index: u32, num: u32) -> std::io::Result<()> {
122        let proxy = self.proxy();
123        proxy_object_invoke!(proxy, get_permissions, index, num)
124    }
125
126    /// Update permissions of a client.
127    pub fn update_permissions(
128        &self,
129        permissions: &[permission::Permission],
130    ) -> std::io::Result<()> {
131        let proxy = self.proxy();
132        proxy_object_invoke!(proxy, update_permissions, permissions)
133    }
134
135    pub(crate) fn methods(&self) -> Arc<Mutex<ClientMethods<Client>>> {
136        self.inner.methods.clone()
137    }
138
139    pub(crate) fn events(&self) -> Arc<Mutex<spa::hook::HookList<ClientEvents>>> {
140        self.inner.hooks.clone()
141    }
142}
143
144impl InnerClient {
145    fn new(core: &Core) -> Self {
146        Self {
147            proxy: RwLock::new(None),
148            methods: Arc::new(Mutex::new(protocol::marshal::client::Methods::marshal(
149                core.connection(),
150            ))),
151            hooks: spa::hook::HookList::new(),
152        }
153    }
154}