diff --git a/Cargo.toml b/Cargo.toml index bef8079..f795a3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ version = "2" version = "0.2" [dependencies.nix] -version = "0.27" +version = "0.29" features = [ "signal", "fs", diff --git a/src/channel.rs b/src/channel.rs index 3c8c742..672cddc 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -89,7 +89,11 @@ impl Receiver { F: Fn(T) + 'static, { let channel = self.channel.clone(); - let readfd = channel.lock().expect("Channel mutex lock poisoned").readfd; + let readfd = channel + .lock() + .expect("Channel mutex lock poisoned") + .readfd + .as_raw_fd(); // Attach the pipe as an IO source to the loop. // Whenever the pipe is written to, call the users callback with each message in the queue. @@ -97,7 +101,7 @@ impl Receiver { let mut channel = channel.lock().expect("Channel mutex lock poisoned"); // Read from the pipe to make it block until written to again. - let _ = nix::unistd::read(channel.readfd, &mut [0]); + let _ = nix::unistd::read(channel.readfd.as_raw_fd(), &mut [0]); channel.queue.drain(..).for_each(&callback); }); @@ -162,7 +166,7 @@ impl Sender { // If no messages are waiting already, signal the receiver to read some. // Because the channel mutex is locked, it is alright to do this before pushing the message. if channel.queue.is_empty() { - match nix::unistd::write(channel.writefd, &[1u8]) { + match nix::unistd::write(&channel.writefd, &[1u8]) { Ok(_) => (), Err(_) => return Err(t), } @@ -178,21 +182,12 @@ impl Sender { /// Shared state between the [`Sender`]s and the [`Receiver`]. struct Channel { /// A pipe used to signal the loop the receiver is attached to that messages are waiting. - readfd: RawFd, - writefd: RawFd, + readfd: OwnedFd, + writefd: OwnedFd, /// Queue of any messages waiting to be received. queue: VecDeque, } -impl Drop for Channel { - fn drop(&mut self) { - // We do not error check here, because the pipe does not contain any data that might be lost, - // and because there is no way to handle an error in a `Drop` implementation anyways. - let _ = nix::unistd::close(self.readfd); - let _ = nix::unistd::close(self.writefd); - } -} - /// Create a Sender-Receiver pair, where the sender can be used to send messages to the receiver. /// /// This functions similar to [`std::sync::mpsc`], but with a receiver that can be attached to any diff --git a/src/client.rs b/src/client.rs index cd28024..89f71e7 100644 --- a/src/client.rs +++ b/src/client.rs @@ -5,7 +5,10 @@ use bitflags::bitflags; use libc::c_void; use std::ops::Deref; use std::pin::Pin; -use std::{ffi::CString, ptr}; +use std::{ + ffi::{CStr, CString}, + ptr, +}; use std::{fmt, mem}; use crate::{ @@ -53,7 +56,11 @@ impl Client { pub fn error(&self, id: u32, res: i32, message: &str) { let message = CString::new(message).expect("Null byte in message parameter"); + let message_cstr = message.as_c_str(); + Client::error_cstr(self, id, res, message_cstr) + } + pub fn error_cstr(&self, id: u32, res: i32, message: &CStr) { unsafe { spa_interface_call_method!( self.proxy.as_ptr(), diff --git a/src/core.rs b/src/core.rs index 960c3c2..1feb79f 100644 --- a/src/core.rs +++ b/src/core.rs @@ -126,8 +126,17 @@ impl CoreRef { factory_name: &str, properties: &impl AsRef, ) -> Result { - let type_ = P::type_(); let factory_name = CString::new(factory_name).expect("Null byte in factory_name parameter"); + let factory_name_cstr = factory_name.as_c_str(); + CoreRef::create_object_cstr(self, factory_name_cstr, properties) + } + + pub fn create_object_cstr( + &self, + factory_name: &CStr, + properties: &impl AsRef, + ) -> Result { + let type_ = P::type_(); let type_str = CString::new(type_.to_string()) .expect("Null byte in string representation of type_ parameter"); diff --git a/src/lib.rs b/src/lib.rs index e63490c..7f100df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,12 +140,6 @@ mod utils; pub use pw_sys as sys; pub use spa; -// Re-export all the traits in a prelude module, so that applications -// can always "use pipewire::prelude::*" without getting conflicts -pub mod prelude { - pub use spa::prelude::*; -} - use std::ptr; /// Initialize PipeWire diff --git a/src/loop_.rs b/src/loop_.rs index 01560c4..3dfa8bc 100644 --- a/src/loop_.rs +++ b/src/loop_.rs @@ -635,7 +635,14 @@ impl<'l> TimerSource<'l> { fn duration_to_timespec(duration: Duration) -> spa_sys::timespec { spa_sys::timespec { tv_sec: duration.as_secs().try_into().expect("Duration too long"), - tv_nsec: duration.subsec_nanos().try_into().unwrap(), + // `Into` is only implemented on some platforms for these types, + // so use a fallible conversion. + // As there are a limited amount of nanoseconds in a second, this shouldn't fail + #[allow(clippy::unnecessary_fallible_conversions)] + tv_nsec: duration + .subsec_nanos() + .try_into() + .expect("Nanoseconds should fit into timespec"), } } diff --git a/src/metadata.rs b/src/metadata.rs index 64a68e1..ebdc3e7 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -55,6 +55,18 @@ impl Metadata { let key = CString::new(key).expect("Invalid byte in metadata key"); let type_ = type_.map(|t| CString::new(t).expect("Invalid byte in metadata type")); let value = value.map(|v| CString::new(v).expect("Invalid byte in metadata value")); + let key_cstr = key.as_c_str(); + + Metadata::set_property_cstr(self, subject, key_cstr, type_.as_deref(), value.as_deref()) + } + + pub fn set_property_cstr( + &self, + subject: u32, + key: &CStr, + type_: Option<&CStr>, + value: Option<&CStr>, + ) { unsafe { spa::spa_interface_call_method!( self.proxy.as_ptr(), @@ -62,8 +74,8 @@ impl Metadata { set_property, subject, key.as_ptr() as *const _, - type_.as_deref().map_or_else(ptr::null, CStr::as_ptr) as *const _, - value.as_deref().map_or_else(ptr::null, CStr::as_ptr) as *const _ + type_.map_or_else(ptr::null, CStr::as_ptr) as *const _, + value.map_or_else(ptr::null, CStr::as_ptr) as *const _ ); } } diff --git a/src/permissions.rs b/src/permissions.rs index bdb011c..7d445ad 100644 --- a/src/permissions.rs +++ b/src/permissions.rs @@ -16,17 +16,33 @@ bitflags! { } } +#[derive(Clone, Copy)] #[repr(transparent)] pub struct Permission(pw_sys::pw_permission); impl Permission { + pub fn new(id: u32, flags: PermissionFlags) -> Self { + Self(pw_sys::pw_permission { + id, + permissions: flags.bits(), + }) + } + pub fn id(&self) -> u32 { self.0.id } + pub fn set_id(&mut self, id: u32) { + self.0.id = id; + } + pub fn permission_flags(&self) -> PermissionFlags { PermissionFlags::from_bits_retain(self.0.permissions) } + + pub fn set_permission_flags(&mut self, flags: PermissionFlags) { + self.0.permissions = flags.bits(); + } } impl fmt::Debug for Permission { diff --git a/src/properties.rs b/src/properties.rs index c71cf72..008bed6 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -1,4 +1,10 @@ -use std::{ffi::CString, fmt, mem::ManuallyDrop, ops::Deref, ptr}; +use std::{ + ffi::{CStr, CString}, + fmt, + mem::ManuallyDrop, + ops::Deref, + ptr, +}; /// A collection of key/value pairs. /// @@ -36,7 +42,6 @@ pub struct Properties { /// /// Any expression that evaluates to a `impl Into>` can be used for both keys and values. /// ```rust -/// use pipewire::prelude::*; /// use pipewire::properties::properties; /// /// let key = String::from("Key"); @@ -149,6 +154,19 @@ impl Clone for Properties { } } +impl FromIterator<(K, V)> for Properties +where + K: Into>, + V: Into>, +{ + fn from_iter>(iter: T) -> Self { + let mut props = Self::new(); + props.extend(iter); + + props + } +} + impl Drop for Properties { fn drop(&mut self) { unsafe { pw_sys::pw_properties_free(self.ptr.as_ptr()) } @@ -196,6 +214,11 @@ impl PropertiesRef { pub fn get(&self, key: &str) -> Option<&str> { let key = CString::new(key).expect("key contains null byte"); + let key_cstr = key.as_c_str(); + PropertiesRef::get_cstr(self, key_cstr) + } + + pub fn get_cstr(&self, key: &CStr) -> Option<&str> { let res = unsafe { pw_sys::pw_properties_get(self.as_raw_ptr().cast_const(), key.as_ptr()) }; @@ -245,6 +268,18 @@ impl fmt::Debug for PropertiesRef { } } +impl Extend<(K, V)> for PropertiesRef +where + K: Into>, + V: Into>, +{ + fn extend>(&mut self, iter: T) { + for (k, v) in iter { + self.insert(k, v); + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/stream.rs b/src/stream.rs index ff28af8..e04ff64 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -64,6 +64,13 @@ impl Stream { /// Initialises a new stream with the given `name` and `properties`. pub fn new(core: &Core, name: &str, properties: Properties) -> Result { let name = CString::new(name).expect("Invalid byte in stream name"); + + let c_str = name.as_c_str(); + Stream::new_cstr(core, c_str, properties) + } + + /// Initialises a new stream with the given `name` as Cstr and `properties`. + pub fn new_cstr(core: &Core, name: &CStr, properties: Properties) -> Result { let stream = unsafe { pw_sys::pw_stream_new(core.as_raw_ptr(), name.as_ptr(), properties.into_raw()) }; @@ -249,8 +256,18 @@ impl StreamRef { /// pub fn set_error(&mut self, res: i32, error: &str) { let error = CString::new(error).expect("failed to convert error to CString"); + let error_cstr = error.as_c_str(); + StreamRef::set_error_cstr(self, res, error_cstr) + } + + /// Set the stream in error state with CStr + /// + /// # Panics + /// Will panic if `error` contains a 0 byte. + /// + pub fn set_error_cstr(&mut self, res: i32, error: &CStr) { unsafe { - pw_sys::pw_stream_set_error(self.as_raw_ptr(), res, error.as_c_str().as_ptr()); + pw_sys::pw_stream_set_error(self.as_raw_ptr(), res, error.as_ptr()); } }