1use bitflags::bitflags;
9use libc::c_void;
10use std::ops::Deref;
11use std::pin::Pin;
12use std::{ffi::CStr, ptr};
13use std::{fmt, mem};
14
15use crate::{
16 proxy::{Listener, Proxy, ProxyT},
17 types::ObjectType,
18};
19use spa::spa_interface_call_method;
20
21#[derive(Debug)]
23pub struct Module {
24 proxy: Proxy,
25}
26
27impl ProxyT for Module {
28 fn type_() -> ObjectType {
29 ObjectType::Module
30 }
31
32 fn upcast(self) -> Proxy {
33 self.proxy
34 }
35
36 fn upcast_ref(&self) -> &Proxy {
37 &self.proxy
38 }
39
40 unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
41 where
42 Self: Sized,
43 {
44 Self { proxy }
45 }
46}
47
48impl Module {
49 #[must_use = "Use the builder to register event callbacks"]
51 pub fn add_listener_local(&self) -> ModuleListenerLocalBuilder<'_> {
52 ModuleListenerLocalBuilder {
53 module: self,
54 cbs: ListenerLocalCallbacks::default(),
55 }
56 }
57}
58
59#[derive(Default)]
60struct ListenerLocalCallbacks {
61 #[allow(clippy::type_complexity)]
62 info: Option<Box<dyn Fn(&ModuleInfoRef)>>,
63}
64
65pub struct ModuleListenerLocalBuilder<'a> {
80 module: &'a Module,
81 cbs: ListenerLocalCallbacks,
82}
83
84#[repr(transparent)]
85pub struct ModuleInfoRef(pw_sys::pw_module_info);
86
87impl ModuleInfoRef {
88 pub fn as_raw(&self) -> &pw_sys::pw_module_info {
89 &self.0
90 }
91
92 pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_module_info {
93 std::ptr::addr_of!(self.0).cast_mut()
94 }
95
96 pub fn id(&self) -> u32 {
97 self.0.id
98 }
99
100 pub fn name(&self) -> &str {
101 unsafe { CStr::from_ptr(self.0.name).to_str().unwrap() }
102 }
103
104 pub fn filename(&self) -> &str {
105 unsafe { CStr::from_ptr(self.0.name).to_str().unwrap() }
106 }
107
108 pub fn args(&self) -> Option<&str> {
109 let args = self.0.args;
110 if args.is_null() {
111 None
112 } else {
113 Some(unsafe { CStr::from_ptr(args).to_str().unwrap() })
114 }
115 }
116
117 pub fn change_mask(&self) -> ModuleChangeMask {
118 ModuleChangeMask::from_bits(self.0.change_mask).expect("invalid change_mask")
119 }
120
121 pub fn props(&self) -> Option<&spa::utils::dict::DictRef> {
122 let props_ptr: *mut spa::utils::dict::DictRef = self.0.props.cast();
123 ptr::NonNull::new(props_ptr).map(|ptr| unsafe { ptr.as_ref() })
124 }
125}
126
127impl fmt::Debug for ModuleInfoRef {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 f.debug_struct("ModuleInfoRef")
130 .field("id", &self.id())
131 .field("filename", &self.filename())
132 .field("args", &self.args())
133 .field("change_mask", &self.change_mask())
134 .field("props", &self.props())
135 .finish()
136 }
137}
138
139pub struct ModuleInfo {
140 ptr: ptr::NonNull<pw_sys::pw_module_info>,
141}
142
143impl ModuleInfo {
144 pub fn new(ptr: ptr::NonNull<pw_sys::pw_module_info>) -> Self {
145 Self { ptr }
146 }
147
148 pub fn from_raw(raw: *mut pw_sys::pw_module_info) -> Self {
149 Self {
150 ptr: ptr::NonNull::new(raw).expect("Provided pointer is null"),
151 }
152 }
153
154 pub fn into_raw(self) -> *mut pw_sys::pw_module_info {
155 std::mem::ManuallyDrop::new(self).ptr.as_ptr()
156 }
157}
158
159impl Drop for ModuleInfo {
160 fn drop(&mut self) {
161 unsafe { pw_sys::pw_module_info_free(self.ptr.as_ptr()) }
162 }
163}
164
165impl std::ops::Deref for ModuleInfo {
166 type Target = ModuleInfoRef;
167
168 fn deref(&self) -> &Self::Target {
169 unsafe { self.ptr.cast::<ModuleInfoRef>().as_ref() }
170 }
171}
172
173impl AsRef<ModuleInfoRef> for ModuleInfo {
174 fn as_ref(&self) -> &ModuleInfoRef {
175 self.deref()
176 }
177}
178
179bitflags! {
180 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
181 pub struct ModuleChangeMask: u64 {
182 const PROPS = pw_sys::PW_MODULE_CHANGE_MASK_PROPS as u64;
183 }
184}
185
186impl fmt::Debug for ModuleInfo {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 f.debug_struct("ModuleInfo")
189 .field("id", &self.id())
190 .field("filename", &self.filename())
191 .field("args", &self.args())
192 .field("change_mask", &self.change_mask())
193 .field("props", &self.props())
194 .finish()
195 }
196}
197
198#[must_use = "Listeners unregister themselves when dropped. Keep the listener alive in order to receive events."]
203pub struct ModuleListener {
204 #[allow(dead_code)]
206 events: Pin<Box<pw_sys::pw_module_events>>,
207 listener: Pin<Box<spa_sys::spa_hook>>,
208 #[allow(dead_code)]
209 data: Box<ListenerLocalCallbacks>,
210}
211
212impl Listener for ModuleListener {}
213
214impl Drop for ModuleListener {
215 fn drop(&mut self) {
216 spa::utils::hook::remove(*self.listener);
217 }
218}
219
220impl<'a> ModuleListenerLocalBuilder<'a> {
221 #[must_use = "Call `.register()` to start receiving events"]
236 pub fn info<F>(mut self, info: F) -> Self
237 where
238 F: Fn(&ModuleInfoRef) + 'static,
239 {
240 self.cbs.info = Some(Box::new(info));
241 self
242 }
243
244 pub fn register(self) -> ModuleListener {
246 unsafe extern "C" fn module_events_info(
247 data: *mut c_void,
248 info: *const pw_sys::pw_module_info,
249 ) {
250 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
251 let info =
252 ptr::NonNull::new(info as *mut pw_sys::pw_module_info).expect("info is NULL");
253 let info = info.cast::<ModuleInfoRef>().as_ref();
254 callbacks.info.as_ref().unwrap()(info);
255 }
256
257 let e = unsafe {
258 let mut e: Pin<Box<pw_sys::pw_module_events>> = Box::pin(mem::zeroed());
259 e.version = pw_sys::PW_VERSION_MODULE_EVENTS;
260
261 if self.cbs.info.is_some() {
262 e.info = Some(module_events_info);
263 }
264
265 e
266 };
267
268 let (listener, data) = unsafe {
269 let module = &self.module.proxy.as_ptr();
270
271 let data = Box::into_raw(Box::new(self.cbs));
272 let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
273 let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
274
275 spa_interface_call_method!(
276 module,
277 pw_sys::pw_module_methods,
278 add_listener,
279 listener_ptr.cast(),
280 e.as_ref().get_ref(),
281 data as *mut _
282 );
283
284 (listener, Box::from_raw(data))
285 };
286
287 ModuleListener {
288 events: e,
289 listener,
290 data,
291 }
292 }
293}