pipewire/
lib.rs

1// Copyright The pipewire-rs Contributors.
2// SPDX-License-Identifier: MIT
3
4//! # Rust bindings for pipewire
5//! `pipewire` is a crate offering a rustic bindings for `libpipewire`, the library for interacting
6//! with the pipewire server.
7//!
8//! Programs that interact with pipewire usually react to events from the server by registering callbacks
9//! and invoke methods on objects on the server by calling methods on local proxy objects.
10//!
11//! ## Getting started
12//! Most programs that interact with pipewire will need the same few basic objects:
13//! - A [`MainLoop`](`main_loop::MainLoop`) that drives the program, reacting to any incoming events and dispatching method calls.
14//!   Most of a time, the program/thread will sit idle in this loop, waiting on events to occur.
15//! - A [`Context`](`context::Context`) that keeps track of any pipewire resources.
16//! - A [`Core`](`core::Core`) that is a proxy for the remote pipewire instance, used to send messages to and receive events from the
17//!   remote server.
18//! - Optionally, a [`Registry`](`registry::Registry`) that can be used to manage and track available objects on the server.
19//!
20//! This is how they can be created:
21//! ```no_run
22//! use pipewire::{main_loop::MainLoopBox, context::ContextBox};
23//!
24//! fn main() -> Result<(), Box<dyn std::error::Error>> {
25//!     let mainloop = MainLoopBox::new(None)?;
26//!     let context = ContextBox::new(&mainloop.loop_(), None)?;
27//!     let core = context.connect(None)?;
28//!     let registry = core.get_registry()?;
29//!
30//!     Ok(())
31//! }
32//! ```
33//!
34//! The example above uses [`std::boxed::Box`]-like smart pointers to create the needed objects.
35//! Those boxes use lifetimes to ensure the objects dependencies (e.g. `Core` depends on `MainLoop`)
36//! outlive the object itself.
37//! If more flexibility is needed, [`std::rc::Rc`]-like reference-counting smart pointers also exist.
38//! Those will automatically keep the objects dependencies alive until the object is destroyed.
39//!
40//! Both of these kinds of types will automatically dereference to non-owning references for shared
41//! functionality, e.g. [`&MainLoop`](`main_loop::MainLoop`) or [`&Core`](`core::Core`).
42//!
43//! The same example as above, but using `Rc` types:
44//! ```no_run
45//! use pipewire::{main_loop::MainLoopRc, context::ContextRc};
46//!
47//! fn main() -> Result<(), Box<dyn std::error::Error>> {
48//!     let mainloop = MainLoopRc::new(None)?;
49//!     let context = ContextRc::new(&mainloop, None)?;
50//!     let core = context.connect_rc(None)?;
51//!     let registry = core.get_registry_rc()?;
52//!
53//!     Ok(())
54//! }
55//! ```
56//!
57//! Once the needed objects are created, you can start hooking up different kinds of callbacks to
58//! them to react to events, and call methods to change the state of the remote.
59//! ```no_run
60//! use pipewire::{main_loop::MainLoopBox, context::ContextBox};
61//!
62//! fn main() -> Result<(), Box<dyn std::error::Error>> {
63//!     let mainloop = MainLoopBox::new(None)?;
64//!     let context = ContextBox::new(&mainloop.loop_(), None)?;
65//!     let core = context.connect(None)?;
66//!     let registry = core.get_registry()?;
67//!
68//!     // Register a callback to the `global` event on the registry, which notifies of any new global objects
69//!     // appearing on the remote.
70//!     // The callback will only get called as long as we keep the returned listener alive.
71//!     let _listener = registry
72//!         .add_listener_local()
73//!         .global(|global| println!("New global: {:?}", global))
74//!         .register();
75//!
76//!     // Calling the `destroy_global` method on the registry will destroy the object with the specified id on the remote.
77//!     // We don't have a specific object to destroy now, so this is commented out.
78//!     # // FIXME: Find a better method for this example we can actually call.
79//!     // registry.destroy_global(313).into_result()?;
80//!
81//!     mainloop.run();
82//!
83//!     Ok(())
84//! }
85//! ```
86//! Note that registering any callback requires the closure to have the `'static` lifetime, so if you need to capture
87//! any variables, use `move ||` closures, and use `std::rc::Rc`s to access shared variables
88//! and some `std::cell` variant if you need to mutate them.
89//!
90//! Also note that we called `mainloop.run()` at the end.
91//! This will enter the loop, and won't return until we call `mainloop.quit()` from some event.
92//! If we didn't run the loop, events and method invocations would not be processed, so the program would terminate
93//! without doing much.
94//!
95//! ## The main loop
96//! Sometimes, other stuff needs to be done even though we are waiting inside the main loop. \
97//! This can be done by adding sources to the loop.
98//!
99//! For example, we can call a function on an interval:
100//!
101//! ```no_run
102//! use pipewire::main_loop::MainLoopBox;
103//! use std::time::Duration;
104//!
105//! fn main() -> Result<(), Box<dyn std::error::Error>> {
106//!     let mainloop = MainLoopBox::new(None)?;
107//!
108//!     let timer = mainloop.loop_().add_timer(|_| println!("Hello"));
109//!     // Call the first time in half a second, and then in a one second interval.
110//!     timer.update_timer(Some(Duration::from_millis(500)), Some(Duration::from_secs(1))).into_result()?;
111//!
112//!     mainloop.run();
113//!
114//!     Ok(())
115//! }
116//! ```
117//! This program will print out "Hello" every second forever.
118//!
119//! Using similar methods, you can also react to IO or Signals, or call a callback whenever the loop is idle.
120//!
121//! ## Multithreading
122//! The pipewire library is not really thread-safe, so pipewire objects do not implement [`Send`](`std::marker::Send`)
123//! or [`Sync`](`std::marker::Sync`).
124//!
125//! However, you can spawn a [`MainLoop`](`main_loop::MainLoop`) in another thread and do bidirectional communication using two channels.
126//!
127//! To send messages to the main thread, we can easily use a [`std::sync::mpsc`].
128//! Because we are stuck in the main loop in the pipewire thread and can't just block on receiving a message,
129//! we use a [`pipewire::channel`](`crate::channel`) instead.
130//!
131//! See the [`pipewire::channel`](`crate::channel`) module for details.
132
133pub mod buffer;
134pub mod channel;
135pub mod client;
136pub mod constants;
137pub mod context;
138pub mod core;
139pub mod device;
140pub mod factory;
141pub mod keys;
142pub mod link;
143pub mod loop_;
144pub mod main_loop;
145pub mod metadata;
146pub mod module;
147pub mod node;
148pub mod permissions;
149pub mod port;
150pub mod properties;
151pub mod proxy;
152pub mod registry;
153pub mod stream;
154pub mod thread_loop;
155pub mod types;
156
157mod error;
158pub use error::*;
159
160mod utils;
161
162pub use pw_sys as sys;
163pub use spa;
164
165use std::ptr;
166
167/// Initialize PipeWire
168///
169/// Initialize the PipeWire system and set up debugging
170/// through the environment variable `PIPEWIRE_DEBUG`.
171pub fn init() {
172    use once_cell::sync::OnceCell;
173    static INITIALIZED: OnceCell<()> = OnceCell::new();
174    INITIALIZED.get_or_init(|| unsafe { pw_sys::pw_init(ptr::null_mut(), ptr::null_mut()) });
175}
176
177/// Deinitialize PipeWire
178///
179/// # Safety
180/// This must only be called once during the lifetime of the process, once no PipeWire threads
181/// are running anymore and all PipeWire resources are released.
182pub unsafe fn deinit() {
183    pw_sys::pw_deinit()
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189
190    #[test]
191    fn test_init() {
192        init();
193        unsafe {
194            deinit();
195        }
196    }
197}