mirror of
https://gitlab.freedesktop.org/pipewire/helvum
synced 2026-03-15 03:26:10 +08:00
pipewire connection: Reconnection to PipeWire server
This commit is contained in:
committed by
Tom Wagner
parent
f0da839383
commit
94323510aa
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -500,6 +500,7 @@ version = "0.4.1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"glib",
|
"glib",
|
||||||
"libadwaita",
|
"libadwaita",
|
||||||
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pipewire",
|
"pipewire",
|
||||||
|
|||||||
@@ -22,3 +22,4 @@ log = "0.4.11"
|
|||||||
|
|
||||||
once_cell = "1.7.2"
|
once_cell = "1.7.2"
|
||||||
|
|
||||||
|
libc = "0.2"
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ mod imp {
|
|||||||
PipewireMessage::LinkFormatChanged { id, media_type } => imp.link_format_changed(id, media_type),
|
PipewireMessage::LinkFormatChanged { id, media_type } => imp.link_format_changed(id, media_type),
|
||||||
PipewireMessage::NodeRemoved { id } => imp.remove_node(id),
|
PipewireMessage::NodeRemoved { id } => imp.remove_node(id),
|
||||||
PipewireMessage::PortRemoved { id, node_id } => imp.remove_port(id, node_id),
|
PipewireMessage::PortRemoved { id, node_id } => imp.remove_port(id, node_id),
|
||||||
PipewireMessage::LinkRemoved { id } => imp.remove_link(id)
|
PipewireMessage::LinkRemoved { id } => imp.remove_link(id),
|
||||||
|
PipewireMessage::Disconnected => imp.clear(),
|
||||||
};
|
};
|
||||||
glib::ControlFlow::Continue
|
glib::ControlFlow::Continue
|
||||||
}
|
}
|
||||||
@@ -280,6 +281,11 @@ mod imp {
|
|||||||
|
|
||||||
self.obj().graph().remove_link(&link);
|
self.obj().graph().remove_link(&link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear(&self) {
|
||||||
|
self.items.borrow_mut().clear();
|
||||||
|
self.obj().graph().clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ pub enum PipewireMessage {
|
|||||||
LinkRemoved {
|
LinkRemoved {
|
||||||
id: u32,
|
id: u32,
|
||||||
},
|
},
|
||||||
|
Disconnected,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|||||||
@@ -16,10 +16,15 @@
|
|||||||
|
|
||||||
mod state;
|
mod state;
|
||||||
|
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
use std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
collections::HashMap,
|
||||||
|
rc::Rc,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use adw::glib::{self, clone};
|
use adw::glib::{self, clone};
|
||||||
use log::{debug, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use pipewire::{
|
use pipewire::{
|
||||||
link::{Link, LinkChangeMask, LinkInfo, LinkListener, LinkState},
|
link::{Link, LinkChangeMask, LinkInfo, LinkListener, LinkState},
|
||||||
port::{Port, PortChangeMask, PortInfo, PortListener},
|
port::{Port, PortChangeMask, PortInfo, PortListener},
|
||||||
@@ -28,7 +33,7 @@ use pipewire::{
|
|||||||
registry::{GlobalObject, Registry},
|
registry::{GlobalObject, Registry},
|
||||||
spa::{
|
spa::{
|
||||||
param::{ParamInfoFlags, ParamType},
|
param::{ParamInfoFlags, ParamType},
|
||||||
ForeignDict,
|
ForeignDict, SpaResult,
|
||||||
},
|
},
|
||||||
types::ObjectType,
|
types::ObjectType,
|
||||||
Context, Core, MainLoop,
|
Context, Core, MainLoop,
|
||||||
@@ -51,56 +56,124 @@ enum ProxyItem {
|
|||||||
/// The "main" function of the pipewire thread.
|
/// The "main" function of the pipewire thread.
|
||||||
pub(super) fn thread_main(
|
pub(super) fn thread_main(
|
||||||
gtk_sender: glib::Sender<PipewireMessage>,
|
gtk_sender: glib::Sender<PipewireMessage>,
|
||||||
pw_receiver: pipewire::channel::Receiver<GtkMessage>,
|
mut pw_receiver: pipewire::channel::Receiver<GtkMessage>,
|
||||||
) {
|
) {
|
||||||
let mainloop = MainLoop::new().expect("Failed to create mainloop");
|
let mainloop = MainLoop::new().expect("Failed to create mainloop");
|
||||||
let context = Context::new(&mainloop).expect("Failed to create context");
|
let context = Rc::new(Context::new(&mainloop).expect("Failed to create context"));
|
||||||
let core = Rc::new(context.connect(None).expect("Failed to connect to remote"));
|
let is_stopped = Rc::new(Cell::new(false));
|
||||||
let registry = Rc::new(core.get_registry().expect("Failed to get registry"));
|
|
||||||
|
|
||||||
// Keep proxies and their listeners alive so that we can receive info events.
|
while !is_stopped.get() {
|
||||||
let proxies = Rc::new(RefCell::new(HashMap::new()));
|
// Try to connect
|
||||||
|
let core = match context.connect(None) {
|
||||||
|
Ok(core) => Rc::new(core),
|
||||||
|
Err(_) => {
|
||||||
|
// If connection is failed, try to connect every 200ms
|
||||||
|
let interval = Some(Duration::from_millis(200));
|
||||||
|
|
||||||
let state = Rc::new(RefCell::new(State::new()));
|
let core = Rc::new(RefCell::new(None));
|
||||||
|
let timer = mainloop.add_timer(
|
||||||
let _receiver = pw_receiver.attach(&mainloop, {
|
clone!(@strong mainloop, @strong context, @strong core => move |_| {
|
||||||
clone!(@strong mainloop, @weak core, @weak registry, @strong state => move |msg| match msg {
|
if let Ok(x) = context.connect(None) {
|
||||||
GtkMessage::ToggleLink { port_from, port_to } => toggle_link(port_from, port_to, &core, ®istry, &state),
|
core.replace(Some(x));
|
||||||
GtkMessage::Terminate => mainloop.quit(),
|
mainloop.quit();
|
||||||
})
|
}
|
||||||
});
|
}),
|
||||||
|
|
||||||
let _listener = registry
|
|
||||||
.add_listener_local()
|
|
||||||
.global(clone!(@strong gtk_sender, @weak registry, @strong proxies, @strong state =>
|
|
||||||
move |global| match global.type_ {
|
|
||||||
ObjectType::Node => handle_node(global, >k_sender, &state),
|
|
||||||
ObjectType::Port => handle_port(global, >k_sender, ®istry, &proxies, &state),
|
|
||||||
ObjectType::Link => handle_link(global, >k_sender, ®istry, &proxies, &state),
|
|
||||||
_ => {
|
|
||||||
// Other objects are not interesting to us
|
|
||||||
}
|
|
||||||
}
|
|
||||||
))
|
|
||||||
.global_remove(clone!(@strong proxies, @strong state => move |id| {
|
|
||||||
if let Some(item) = state.borrow_mut().remove(id) {
|
|
||||||
gtk_sender.send(match item {
|
|
||||||
Item::Node { .. } => PipewireMessage::NodeRemoved {id},
|
|
||||||
Item::Port { node_id } => PipewireMessage::PortRemoved {id, node_id},
|
|
||||||
Item::Link { .. } => PipewireMessage::LinkRemoved {id},
|
|
||||||
}).expect("Failed to send message");
|
|
||||||
} else {
|
|
||||||
warn!(
|
|
||||||
"Attempted to remove item with id {} that is not saved in state",
|
|
||||||
id
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
timer
|
||||||
|
.update_timer(interval, interval)
|
||||||
|
.into_result()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let receiver = pw_receiver.attach(&mainloop, {
|
||||||
|
clone!(@strong mainloop, @strong is_stopped => move |msg|
|
||||||
|
if let GtkMessage::Terminate = msg {
|
||||||
|
// main thread requested stop
|
||||||
|
is_stopped.set(true);
|
||||||
|
mainloop.quit();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
mainloop.run();
|
||||||
|
pw_receiver = receiver.deattach();
|
||||||
|
|
||||||
|
if is_stopped.get() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rc::new(core.take().unwrap())
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
proxies.borrow_mut().remove(&id);
|
let registry = Rc::new(core.get_registry().expect("Failed to get registry"));
|
||||||
}))
|
|
||||||
.register();
|
|
||||||
|
|
||||||
mainloop.run();
|
// Keep proxies and their listeners alive so that we can receive info events.
|
||||||
|
let proxies = Rc::new(RefCell::new(HashMap::new()));
|
||||||
|
let state = Rc::new(RefCell::new(State::new()));
|
||||||
|
|
||||||
|
let receiver = pw_receiver.attach(&mainloop, {
|
||||||
|
clone!(@strong mainloop, @weak core, @weak registry, @strong state, @strong is_stopped => move |msg| match msg {
|
||||||
|
GtkMessage::ToggleLink { port_from, port_to } => toggle_link(port_from, port_to, &core, ®istry, &state),
|
||||||
|
GtkMessage::Terminate => {
|
||||||
|
// main thread requested stop
|
||||||
|
is_stopped.set(true);
|
||||||
|
mainloop.quit();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let gtk_sender = gtk_sender.clone();
|
||||||
|
let _listener = core.add_listener_local()
|
||||||
|
.error(clone!(@strong mainloop, @strong gtk_sender, @strong is_stopped => move |id, _seq, res, message| {
|
||||||
|
if id != pipewire::PW_ID_CORE {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if res == -libc::EPIPE {
|
||||||
|
gtk_sender.send(PipewireMessage::Disconnected)
|
||||||
|
.expect("Failed to send message");
|
||||||
|
mainloop.quit();
|
||||||
|
} else {
|
||||||
|
let serr = SpaResult::from_c(res).into_result().unwrap_err();
|
||||||
|
error!("Pipewire Core received error {serr}: {message}");
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.register();
|
||||||
|
|
||||||
|
let _listener = registry
|
||||||
|
.add_listener_local()
|
||||||
|
.global(clone!(@strong gtk_sender, @weak registry, @strong proxies, @strong state =>
|
||||||
|
move |global| match global.type_ {
|
||||||
|
ObjectType::Node => handle_node(global, >k_sender, &state),
|
||||||
|
ObjectType::Port => handle_port(global, >k_sender, ®istry, &proxies, &state),
|
||||||
|
ObjectType::Link => handle_link(global, >k_sender, ®istry, &proxies, &state),
|
||||||
|
_ => {
|
||||||
|
// Other objects are not interesting to us
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
.global_remove(clone!(@strong proxies, @strong state => move |id| {
|
||||||
|
if let Some(item) = state.borrow_mut().remove(id) {
|
||||||
|
gtk_sender.send(match item {
|
||||||
|
Item::Node { .. } => PipewireMessage::NodeRemoved {id},
|
||||||
|
Item::Port { node_id } => PipewireMessage::PortRemoved {id, node_id},
|
||||||
|
Item::Link { .. } => PipewireMessage::LinkRemoved {id},
|
||||||
|
}).expect("Failed to send message");
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"Attempted to remove item with id {} that is not saved in state",
|
||||||
|
id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
proxies.borrow_mut().remove(&id);
|
||||||
|
}))
|
||||||
|
.register();
|
||||||
|
|
||||||
|
mainloop.run();
|
||||||
|
pw_receiver = receiver.deattach();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle a new node being added
|
/// Handle a new node being added
|
||||||
|
|||||||
@@ -796,6 +796,14 @@ impl GraphView {
|
|||||||
self.queue_draw();
|
self.queue_draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.imp().links.borrow_mut().clear();
|
||||||
|
for (node, _) in self.imp().nodes.borrow_mut().drain() {
|
||||||
|
node.unparent();
|
||||||
|
}
|
||||||
|
self.queue_draw();
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the position of the specified node inside the graphview.
|
/// Get the position of the specified node inside the graphview.
|
||||||
///
|
///
|
||||||
/// The returned position is in canvas-space (non-zoomed, (0, 0) fixed in the middle of the canvas).
|
/// The returned position is in canvas-space (non-zoomed, (0, 0) fixed in the middle of the canvas).
|
||||||
|
|||||||
Reference in New Issue
Block a user