Hi,I took a stab at this one and could improve Matthias' patch so helvum now builds and runs fine with current unstable/testing crates. Please note I haven't tested it beyond starting helvum and checking the displayed graph made sense on my system.
Attached is the new patch version. Cheers, Arnaud
From: Matthias Geiger <[email protected]> Date: Sun, 7 Sep 2025 19:14:51 +0200 Subject: [PATCH] feat: Port to gtk-rs 0.10 Co-authored-by: Arnaud Ferraris <[email protected]> --- Cargo.toml | 4 +- src/application.rs | 44 ++++++---- src/graph_manager.rs | 19 +++-- src/pipewire_connection/mod.rs | 188 ++++++++++++++++++++++++++++------------- src/ui/graph/graph_view.rs | 47 +++++++---- src/ui/graph/node.rs | 3 +- src/ui/graph/port.rs | 3 +- src/ui/graph/port_handle.rs | 3 +- src/ui/graph/zoomentry.rs | 79 ++++++++++------- src/ui/window.rs | 2 +- 10 files changed, 261 insertions(+), 131 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 291e82a..b469cc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,8 @@ categories = ["gui", "multimedia"] [dependencies] pipewire = "0.8.0" -adw = { version = ">= 0.6, <= 0.8", package = "libadwaita", features = ["v1_4"] } -glib = { version = ">= 0.19, <= 0.21", features = ["log"] } +adw = { version = "0.8", package = "libadwaita", features = ["v1_4"] } +glib = { version = "0.21", features = ["log"] } async-channel = "2.2" log = "0.4.11" diff --git a/src/application.rs b/src/application.rs index 9daf385..7e1997d 100644 --- a/src/application.rs +++ b/src/application.rs @@ -64,10 +64,14 @@ mod imp { let zoom_set_action = gio::SimpleAction::new("set-zoom", Some(&f64::static_variant_type())); - zoom_set_action.connect_activate(clone!(@weak graphview => move|_, param| { - let zoom_factor = param.unwrap().get::<f64>().unwrap(); - graphview.set_zoom_factor(zoom_factor, None) - })); + zoom_set_action.connect_activate(clone!( + #[weak] + graphview, + move|_, param| { + let zoom_factor = param.unwrap().get::<f64>().unwrap(); + graphview.set_zoom_factor(zoom_factor, None) + } + )); self.window.add_action(&zoom_set_action); self.window.show(); @@ -101,9 +105,13 @@ mod imp { // Add <Control-Q> shortcut for quitting the application. let quit = gtk::gio::SimpleAction::new("quit", None); - quit.connect_activate(clone!(@weak obj => move |_, _| { - obj.quit(); - })); + quit.connect_activate(clone!( + #[weak] + obj, + move |_, _| { + obj.quit(); + } + )); obj.set_accels_for_action("app.quit", &["<Control>Q"]); obj.add_action(&quit); @@ -148,16 +156,20 @@ mod imp { ); let current_remote_label = obj.imp().window.current_remote_label(); - obj.connect_handle_local_options(clone!(@strong pw_sender => move |_, opts| { - match opts.lookup::<String>("socket") { - Ok(p) => { - current_remote_label.set_label(p.as_deref().unwrap_or(DEFAULT_REMOTE_NAME)); - pw_sender.send(GtkMessage::Connect(p)).unwrap(); - }, - Err(e) => error!("Invalid socket path: {e}"), + obj.connect_handle_local_options(clone!( + #[strong] + pw_sender, + move |_, opts| { + match opts.lookup::<String>("socket") { + Ok(p) => { + current_remote_label.set_label(p.as_deref().unwrap_or(DEFAULT_REMOTE_NAME)); + pw_sender.send(GtkMessage::Connect(p)).unwrap(); + }, + Err(e) => error!("Invalid socket path: {e}"), + } + std::ops::ControlFlow::Continue(()) } - -1 - })); + )); } } } diff --git a/src/graph_manager.rs b/src/graph_manager.rs index b80f0d3..480d801 100644 --- a/src/graph_manager.rs +++ b/src/graph_manager.rs @@ -176,15 +176,20 @@ mod imp { port.connect_local( "port_toggled", false, - glib::clone!(@weak self as app => @default-return None, move |args| { - // Args always look like this: &[widget, id_port_from, id_port_to] - let port_from = args[1].get::<u32>().unwrap(); - let port_to = args[2].get::<u32>().unwrap(); + glib::clone!( + #[weak(rename_to = app)] + self, + #[upgrade_or_default] + move |args| { + // Args always look like this: &[widget, id_port_from, id_port_to] + let port_from = args[1].get::<u32>().unwrap(); + let port_to = args[2].get::<u32>().unwrap(); - app.toggle_link(port_from, port_to); + app.toggle_link(port_from, port_to); - None - }), + None + } + ), ); items.insert(id, port.clone().upcast()); diff --git a/src/pipewire_connection/mod.rs b/src/pipewire_connection/mod.rs index 6141087..7d898a4 100644 --- a/src/pipewire_connection/mod.rs +++ b/src/pipewire_connection/mod.rs @@ -92,8 +92,12 @@ pub(super) fn thread_main( // Wait PipeWire service to connect from command line arguments. let receiver = pw_receiver.attach(mainloop.loop_(), { - clone!(@strong mainloop, @strong loop_state => move |msg| - if loop_state.borrow_mut().handle_message(msg) { + clone!( + #[strong] + mainloop, + #[strong] + loop_state, + move |msg| if loop_state.borrow_mut().handle_message(msg) { mainloop.quit(); } ) @@ -119,19 +123,29 @@ pub(super) fn thread_main( let timer = mainloop .loop_() - .add_timer(clone!(@strong mainloop => move |_| { - mainloop.quit(); - })); + .add_timer(clone!( + #[strong] + mainloop, + move |_| { + mainloop.quit(); + } + )); timer.update_timer(interval, None).into_result().unwrap(); let receiver = pw_receiver.attach(mainloop.loop_(), { - clone!(@strong mainloop, @strong loop_state => move |msg| - if loop_state.borrow_mut().handle_message(msg) { - mainloop.quit(); - } - ) - }); + clone!( + #[strong] + mainloop, + #[strong] + loop_state, + move |msg| + if loop_state.borrow_mut().handle_message(msg) { + mainloop.quit(); + } + ) + } + ); mainloop.run(); pw_receiver = receiver.deattach(); @@ -154,38 +168,64 @@ pub(super) fn thread_main( let state = Rc::new(RefCell::new(State::new())); let receiver = pw_receiver.attach(mainloop.loop_(), { - clone!(@strong mainloop, @weak core, @weak registry, @strong state, @strong loop_state => move |msg| match msg { - GtkMessage::ToggleLink { port_from, port_to } => toggle_link(port_from, port_to, &core, ®istry, &state), - GtkMessage::Terminate | GtkMessage::Connect(_) => { - loop_state.borrow_mut().handle_message(msg); - mainloop.quit(); + clone!( + #[strong] + mainloop, + #[weak] + core, + #[weak] + registry, + #[strong] + state, + #[strong] + loop_state, + move |msg| match msg { + GtkMessage::ToggleLink { port_from, port_to } => toggle_link(port_from, port_to, &core, ®istry, &state), + GtkMessage::Terminate | GtkMessage::Connect(_) => { + loop_state.borrow_mut().handle_message(msg); + mainloop.quit(); + } } - }) + ) }); let _listener = core .add_listener_local() .error( - clone!(@strong mainloop, @strong gtk_sender => move |id, _seq, res, message| { - if id != PW_ID_CORE { - return; - } + clone!( + #[strong] + mainloop, + #[strong] + gtk_sender, + move |id, _seq, res, message| { + if id != PW_ID_CORE { + return; + } - if res == -libc::EPIPE { - gtk_sender.send_blocking(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}"); + if res == -libc::EPIPE { + gtk_sender.send_blocking(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 => + .global(clone!( + #[strong] + gtk_sender, + #[weak] + registry, + #[strong] + proxies, + #[strong] + state, move |global| match global.type_ { ObjectType::Node => handle_node(global, >k_sender, ®istry, &proxies, &state), ObjectType::Port => handle_port(global, >k_sender, ®istry, &proxies, &state), @@ -195,22 +235,30 @@ pub(super) fn thread_main( } } )) - .global_remove(clone!(@strong gtk_sender, @strong proxies, @strong state => move |id| { - if let Some(item) = state.borrow_mut().remove(id) { - gtk_sender.send_blocking(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 - ); - } + .global_remove(clone!( + #[strong] + gtk_sender, + #[strong] + proxies, + #[strong] + state, + move |id| { + if let Some(item) = state.borrow_mut().remove(id) { + gtk_sender.send_blocking(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); - })) + proxies.borrow_mut().remove(&id); + } + )) .register(); mainloop.run(); @@ -279,9 +327,15 @@ fn handle_node( let proxy: Node = registry.bind(node).expect("Failed to bind to node proxy"); let listener = proxy .add_listener_local() - .info(clone!(@strong sender, @strong proxies => move |info| { - handle_node_info(info, &sender, &proxies); - })) + .info(clone!( + #[strong] + sender, + #[strong] + proxies, + move |info| { + handle_node_info(info, &sender, &proxies); + } + )) .register(); proxies.borrow_mut().insert( @@ -334,15 +388,27 @@ fn handle_port( let listener = proxy .add_listener_local() .info( - clone!(@strong proxies, @strong state, @strong sender => move |info| { - handle_port_info(info, &proxies, &state, &sender); - }), + clone!( + #[strong] + proxies, + #[strong] + state, + #[strong] + sender, + move |info| { + handle_port_info(info, &proxies, &state, &sender); + } + ), ) - .param(clone!(@strong sender => move |_, param_id, _, _, param| { - if param_id == ParamType::EnumFormat { - handle_port_enum_format(port_id, param, &sender) + .param(clone!( + #[strong] + sender, + move |_, param_id, _, _, param| { + if param_id == ParamType::EnumFormat { + handle_port_enum_format(port_id, param, &sender) + } } - })) + )) .register(); proxies.borrow_mut().insert( @@ -443,9 +509,15 @@ fn handle_link( let proxy: Link = registry.bind(link).expect("Failed to bind to link proxy"); let listener = proxy .add_listener_local() - .info(clone!(@strong state, @strong sender => move |info| { - handle_link_info(info, &state, &sender); - })) + .info(clone!( + #[strong] + state, + #[strong] + sender, + move |info| { + handle_link_info(info, &state, &sender); + } + )) .register(); proxies.borrow_mut().insert( diff --git a/src/ui/graph/graph_view.rs b/src/ui/graph/graph_view.rs index 55334cc..6d8de65 100644 --- a/src/ui/graph/graph_view.rs +++ b/src/ui/graph/graph_view.rs @@ -386,14 +386,18 @@ mod imp { Port::static_type(), glib::Priority::DEFAULT, Option::<&gio::Cancellable>::None, - clone!(@weak self as imp => move|value| { - let Ok(value) = value else { - return; - }; - let port: &Port = value.get().expect("Value should contain a port"); + clone!( + #[weak(rename_to = imp)] + self, + move|value| { + let Ok(value) = value else { + return; + }; + let port: &Port = value.get().expect("Value should contain a port"); - imp.dragged_port.set(Some(port)); - }), + imp.dragged_port.set(Some(port)); + } + ), ); self.obj().queue_draw(); @@ -689,7 +693,11 @@ mod imp { if let Some(adjustment) = adjustment { adjustment - .connect_value_changed(clone!(@weak obj => move |_| obj.queue_allocate() )); + .connect_value_changed(clone!( + #[weak] + obj, + move |_| obj.queue_allocate() + )); } } @@ -720,7 +728,8 @@ mod imp { glib::wrapper! { pub struct GraphView(ObjectSubclass<imp::GraphView>) - @extends gtk::Widget; + @extends gtk::Widget, + @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Scrollable; } impl GraphView { @@ -838,15 +847,23 @@ impl GraphView { pub fn add_link(&self, link: Link) { link.connect_notify_local( Some("active"), - glib::clone!(@weak self as graph => move |_, _| { - graph.queue_draw(); - }), + glib::clone!( + #[weak(rename_to = graph)] + self, + move |_, _| { + graph.queue_draw(); + } + ), ); link.connect_notify_local( Some("media-type"), - glib::clone!(@weak self as graph => move |_, _| { - graph.queue_draw(); - }), + glib::clone!( + #[weak(rename_to = graph)] + self, + move |_, _| { + graph.queue_draw(); + } + ), ); self.imp().links.borrow_mut().insert(link); self.queue_draw(); diff --git a/src/ui/graph/node.rs b/src/ui/graph/node.rs index 79eaa8d..30a3768 100644 --- a/src/ui/graph/node.rs +++ b/src/ui/graph/node.rs @@ -149,7 +149,8 @@ mod imp { glib::wrapper! { pub struct Node(ObjectSubclass<imp::Node>) - @extends gtk::Widget; + @extends gtk::Widget, + @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget; } impl Node { diff --git a/src/ui/graph/port.rs b/src/ui/graph/port.rs index b84aae3..ce48a1e 100644 --- a/src/ui/graph/port.rs +++ b/src/ui/graph/port.rs @@ -328,7 +328,8 @@ mod imp { glib::wrapper! { pub struct Port(ObjectSubclass<imp::Port>) - @extends gtk::Widget; + @extends gtk::Widget, + @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget; } impl Port { diff --git a/src/ui/graph/port_handle.rs b/src/ui/graph/port_handle.rs index a9ca8cc..2024d4c 100644 --- a/src/ui/graph/port_handle.rs +++ b/src/ui/graph/port_handle.rs @@ -61,7 +61,8 @@ mod imp { glib::wrapper! { pub struct PortHandle(ObjectSubclass<imp::PortHandle>) - @extends gtk::Widget; + @extends gtk::Widget, + @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget; } impl PortHandle { diff --git a/src/ui/graph/zoomentry.rs b/src/ui/graph/zoomentry.rs index 667236b..4109676 100644 --- a/src/ui/graph/zoomentry.rs +++ b/src/ui/graph/zoomentry.rs @@ -66,36 +66,52 @@ mod imp { self.parent_constructed(); self.zoom_out_button - .connect_clicked(clone!(@weak self as imp => move |_| { - let graphview = imp.graphview.borrow(); - if let Some(ref graphview) = *graphview { - graphview.set_zoom_factor(graphview.zoom_factor() - 0.1, None); - } - })); - - self.zoom_in_button - .connect_clicked(clone!(@weak self as imp => move |_| { - let graphview = imp.graphview.borrow(); - if let Some(ref graphview) = *graphview { - graphview.set_zoom_factor(graphview.zoom_factor() + 0.1, None); - } - })); - - self.entry - .connect_activate(clone!(@weak self as imp => move |entry| { - if let Ok(zoom_factor) = entry.text().trim_matches('%').parse::<f64>() { + .connect_clicked(clone!( + #[weak(rename_to = imp)] + self, + move |_| { let graphview = imp.graphview.borrow(); if let Some(ref graphview) = *graphview { - graphview.set_zoom_factor(zoom_factor / 100.0, None); + graphview.set_zoom_factor(graphview.zoom_factor() - 0.1, None); } } - })); - self.entry - .connect_icon_press(clone!(@weak self as imp => move |_, pos| { - if pos == gtk::EntryIconPosition::Secondary { - imp.popover.show(); + )); + + self.zoom_in_button + .connect_clicked(clone!( + #[weak(rename_to = imp)] + self, + move |_| { + let graphview = imp.graphview.borrow(); + if let Some(ref graphview) = *graphview { + graphview.set_zoom_factor(graphview.zoom_factor() + 0.1, None); + } } - })); + )); + + self.entry + .connect_activate(clone!( + #[weak(rename_to = imp)] + self, + move |entry| { + if let Ok(zoom_factor) = entry.text().trim_matches('%').parse::<f64>() { + let graphview = imp.graphview.borrow(); + if let Some(ref graphview) = *graphview { + graphview.set_zoom_factor(zoom_factor / 100.0, None); + } + } + } + )); + self.entry + .connect_icon_press(clone!( + #[weak(rename_to = imp)] + self, + move |_, pos| { + if pos == gtk::EntryIconPosition::Secondary { + imp.popover.show(); + } + } + )); self.popover.set_parent(&self.entry.get()); } @@ -132,9 +148,13 @@ mod imp { if let Some(ref widget) = widget { widget.connect_notify_local( Some("zoom-factor"), - clone!(@weak self as imp => move |graphview, _| { - imp.update_zoom_factor_text(graphview.zoom_factor()); - }), + clone!( + #[weak(rename_to = imp)] + self, + move |graphview, _| { + imp.update_zoom_factor_text(graphview.zoom_factor()); + } + ), ); self.update_zoom_factor_text(widget.zoom_factor()); } @@ -161,7 +181,8 @@ mod imp { glib::wrapper! { pub struct ZoomEntry(ObjectSubclass<imp::ZoomEntry>) - @extends gtk::Box, gtk::Widget; + @extends gtk::Box, gtk::Widget, + @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget; } impl ZoomEntry { diff --git a/src/ui/window.rs b/src/ui/window.rs index 7c10354..a35f102 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -52,7 +52,7 @@ mod imp { glib::wrapper! { pub struct Window(ObjectSubclass<imp::Window>) @extends adw::ApplicationWindow, gtk::ApplicationWindow, gtk::Window, gtk::Widget, - @implements gio::ActionGroup, gio::ActionMap; + @implements gio::ActionGroup, gio::ActionMap, gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager; } impl Window {
OpenPGP_0xD3EBB5966BB99196.asc
Description: OpenPGP public key
OpenPGP_signature.asc
Description: OpenPGP digital signature

