1use bitflags::bitflags;
5use libc::c_void;
6use std::ops::Deref;
7use std::pin::Pin;
8use std::{ffi::CStr, ptr};
9use std::{fmt, mem};
10
11use crate::{
12 proxy::{Listener, Proxy, ProxyT},
13 types::ObjectType,
14};
15use spa::spa_interface_call_method;
16
17#[derive(Debug)]
18pub struct Module {
19 proxy: Proxy,
20}
21
22impl ProxyT for Module {
23 fn type_() -> ObjectType {
24 ObjectType::Module
25 }
26
27 fn upcast(self) -> Proxy {
28 self.proxy
29 }
30
31 fn upcast_ref(&self) -> &Proxy {
32 &self.proxy
33 }
34
35 unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
36 where
37 Self: Sized,
38 {
39 Self { proxy }
40 }
41}
42
43impl Module {
44 #[must_use]
46 pub fn add_listener_local(&self) -> ModuleListenerLocalBuilder<'_> {
47 ModuleListenerLocalBuilder {
48 module: self,
49 cbs: ListenerLocalCallbacks::default(),
50 }
51 }
52}
53
54#[derive(Default)]
55struct ListenerLocalCallbacks {
56 #[allow(clippy::type_complexity)]
57 info: Option<Box<dyn Fn(&ModuleInfoRef)>>,
58}
59
60pub struct ModuleListenerLocalBuilder<'a> {
61 module: &'a Module,
62 cbs: ListenerLocalCallbacks,
63}
64
65#[repr(transparent)]
66pub struct ModuleInfoRef(pw_sys::pw_module_info);
67
68impl ModuleInfoRef {
69 pub fn as_raw(&self) -> &pw_sys::pw_module_info {
70 &self.0
71 }
72
73 pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_module_info {
74 std::ptr::addr_of!(self.0).cast_mut()
75 }
76
77 pub fn id(&self) -> u32 {
78 self.0.id
79 }
80
81 pub fn name(&self) -> &str {
82 unsafe { CStr::from_ptr(self.0.name).to_str().unwrap() }
83 }
84
85 pub fn filename(&self) -> &str {
86 unsafe { CStr::from_ptr(self.0.name).to_str().unwrap() }
87 }
88
89 pub fn args(&self) -> Option<&str> {
90 let args = self.0.args;
91 if args.is_null() {
92 None
93 } else {
94 Some(unsafe { CStr::from_ptr(args).to_str().unwrap() })
95 }
96 }
97
98 pub fn change_mask(&self) -> ModuleChangeMask {
99 ModuleChangeMask::from_bits(self.0.change_mask).expect("invalid change_mask")
100 }
101
102 pub fn props(&self) -> Option<&spa::utils::dict::DictRef> {
103 let props_ptr: *mut spa::utils::dict::DictRef = self.0.props.cast();
104 ptr::NonNull::new(props_ptr).map(|ptr| unsafe { ptr.as_ref() })
105 }
106}
107
108impl fmt::Debug for ModuleInfoRef {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 f.debug_struct("ModuleInfoRef")
111 .field("id", &self.id())
112 .field("filename", &self.filename())
113 .field("args", &self.args())
114 .field("change_mask", &self.change_mask())
115 .field("props", &self.props())
116 .finish()
117 }
118}
119
120pub struct ModuleInfo {
121 ptr: ptr::NonNull<pw_sys::pw_module_info>,
122}
123
124impl ModuleInfo {
125 pub fn new(ptr: ptr::NonNull<pw_sys::pw_module_info>) -> Self {
126 Self { ptr }
127 }
128
129 pub fn from_raw(raw: *mut pw_sys::pw_module_info) -> Self {
130 Self {
131 ptr: ptr::NonNull::new(raw).expect("Provided pointer is null"),
132 }
133 }
134
135 pub fn into_raw(self) -> *mut pw_sys::pw_module_info {
136 std::mem::ManuallyDrop::new(self).ptr.as_ptr()
137 }
138}
139
140impl Drop for ModuleInfo {
141 fn drop(&mut self) {
142 unsafe { pw_sys::pw_module_info_free(self.ptr.as_ptr()) }
143 }
144}
145
146impl std::ops::Deref for ModuleInfo {
147 type Target = ModuleInfoRef;
148
149 fn deref(&self) -> &Self::Target {
150 unsafe { self.ptr.cast::<ModuleInfoRef>().as_ref() }
151 }
152}
153
154impl AsRef<ModuleInfoRef> for ModuleInfo {
155 fn as_ref(&self) -> &ModuleInfoRef {
156 self.deref()
157 }
158}
159
160bitflags! {
161 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
162 pub struct ModuleChangeMask: u64 {
163 const PROPS = pw_sys::PW_MODULE_CHANGE_MASK_PROPS as u64;
164 }
165}
166
167impl fmt::Debug for ModuleInfo {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 f.debug_struct("ModuleInfo")
170 .field("id", &self.id())
171 .field("filename", &self.filename())
172 .field("args", &self.args())
173 .field("change_mask", &self.change_mask())
174 .field("props", &self.props())
175 .finish()
176 }
177}
178
179pub struct ModuleListener {
180 #[allow(dead_code)]
182 events: Pin<Box<pw_sys::pw_module_events>>,
183 listener: Pin<Box<spa_sys::spa_hook>>,
184 #[allow(dead_code)]
185 data: Box<ListenerLocalCallbacks>,
186}
187
188impl Listener for ModuleListener {}
189
190impl Drop for ModuleListener {
191 fn drop(&mut self) {
192 spa::utils::hook::remove(*self.listener);
193 }
194}
195
196impl<'a> ModuleListenerLocalBuilder<'a> {
197 #[must_use]
198 pub fn info<F>(mut self, info: F) -> Self
199 where
200 F: Fn(&ModuleInfoRef) + 'static,
201 {
202 self.cbs.info = Some(Box::new(info));
203 self
204 }
205
206 #[must_use]
207 pub fn register(self) -> ModuleListener {
208 unsafe extern "C" fn module_events_info(
209 data: *mut c_void,
210 info: *const pw_sys::pw_module_info,
211 ) {
212 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
213 let info =
214 ptr::NonNull::new(info as *mut pw_sys::pw_module_info).expect("info is NULL");
215 let info = info.cast::<ModuleInfoRef>().as_ref();
216 callbacks.info.as_ref().unwrap()(info);
217 }
218
219 let e = unsafe {
220 let mut e: Pin<Box<pw_sys::pw_module_events>> = Box::pin(mem::zeroed());
221 e.version = pw_sys::PW_VERSION_MODULE_EVENTS;
222
223 if self.cbs.info.is_some() {
224 e.info = Some(module_events_info);
225 }
226
227 e
228 };
229
230 let (listener, data) = unsafe {
231 let module = &self.module.proxy.as_ptr();
232
233 let data = Box::into_raw(Box::new(self.cbs));
234 let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
235 let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
236
237 spa_interface_call_method!(
238 module,
239 pw_sys::pw_module_methods,
240 add_listener,
241 listener_ptr.cast(),
242 e.as_ref().get_ref(),
243 data as *mut _
244 );
245
246 (listener, Box::from_raw(data))
247 };
248
249 ModuleListener {
250 events: e,
251 listener,
252 data,
253 }
254 }
255}