Modify architecture to run pipewire loop in second thread.

The pipewire loop now runs without interruption in a second thread and communicates with
the GTK thread via a channel in each direction, instead of checking for events once a second and using callbacks.

This allows changes to appear instantly in the view, instead of having to wait.
This commit is contained in:
Tom A. Wagner
2021-05-05 21:43:52 +02:00
parent 75aa0a30d0
commit 076fec7eb4
9 changed files with 292 additions and 346 deletions

View File

@@ -4,7 +4,38 @@ mod view;
use std::rc::Rc;
use gtk::{glib, prelude::*};
use controller::MediaType;
use gtk::glib::{self, PRIORITY_DEFAULT};
use pipewire::spa::Direction;
/// Messages used GTK thread to command the pipewire thread.
#[derive(Debug)]
enum GtkMessage {
/// Quit the event loop and let the thread finish.
Terminate,
}
/// Messages used pipewire thread to notify the GTK thread.
#[derive(Debug)]
enum PipewireMessage {
/// A new node has appeared.
NodeAdded {
id: u32,
name: String,
media_type: Option<MediaType>,
},
/// A new port has appeared.
PortAdded {
id: u32,
node_id: u32,
name: String,
direction: Direction,
},
/// A new link has appeared.
LinkAdded { id: u32, link: PipewireLink },
/// An object was removed
ObjectRemoved { id: u32 },
}
#[derive(Debug)]
pub struct PipewireLink {
@@ -18,20 +49,22 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
gtk::init()?;
let view = Rc::new(view::View::new());
let pw_con = pipewire_connection::PipewireConnection::new()?;
let _controller = controller::Controller::new(view.clone(), pw_con.clone());
// Start the pipewire thread with channels in both directions.
let (gtk_sender, gtk_receiver) = glib::MainContext::channel(PRIORITY_DEFAULT);
let (pw_sender, pw_receiver) = pipewire::channel::channel();
let pw_thread =
std::thread::spawn(move || pipewire_connection::thread_main(gtk_sender, pw_receiver));
// Do an initial roundtrip before showing the view,
// so that the graph is already populated when the window opens.
pw_con.borrow().roundtrip();
// From now on, call roundtrip() every second.
glib::timeout_add_seconds_local(1, move || {
pw_con.borrow().roundtrip();
Continue(true)
});
let view = Rc::new(view::View::new());
let _controller = controller::Controller::new(view.clone(), gtk_receiver);
view.run();
pw_sender
.send(GtkMessage::Terminate)
.expect("Failed to send message");
pw_thread.join().expect("Pipewire thread panicked");
Ok(())
}