diff --git a/src/application.rs b/src/application.rs index b6dc3c5..04edf2e 100644 --- a/src/application.rs +++ b/src/application.rs @@ -11,7 +11,7 @@ use pipewire::{channel::Sender, spa::Direction}; use crate::{ view::{self}, - GtkMessage, MediaType, PipewireLink, PipewireMessage, + GtkMessage, MediaType, NodeType, PipewireLink, PipewireMessage, }; static STYLE: &str = include_str!("style.css"); @@ -108,7 +108,7 @@ impl Application { @weak app => @default-return Continue(true), move |msg| { match msg { - PipewireMessage::NodeAdded{ id, name } => app.add_node(id, name.as_str()), + PipewireMessage::NodeAdded{ id, name, node_type } => app.add_node(id, name.as_str(), node_type), PipewireMessage::PortAdded{ id, node_id, name, direction, media_type } => app.add_port(id, name.as_str(), node_id, direction, media_type), PipewireMessage::LinkAdded{ id, node_from, port_from, node_to, port_to, active} => app.add_link(id, node_from, port_from, node_to, port_to, active), PipewireMessage::LinkStateChanged { id, active } => app.link_state_changed(id, active), // TODO @@ -125,12 +125,14 @@ impl Application { } /// Add a new node to the view. - fn add_node(&self, id: u32, name: &str) { + fn add_node(&self, id: u32, name: &str, node_type: Option) { info!("Adding node to graph: id {}", id); - imp::Application::from_instance(self) - .graphview - .add_node(id, view::Node::new(name)); + imp::Application::from_instance(self).graphview.add_node( + id, + view::Node::new(name), + node_type, + ); } /// Add a new port to the view. diff --git a/src/main.rs b/src/main.rs index 239f39d..0357860 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ enum PipewireMessage { NodeAdded { id: u32, name: String, + node_type: Option, }, PortAdded { id: u32, @@ -55,6 +56,12 @@ enum PipewireMessage { }, } +#[derive(Debug, Clone)] +pub enum NodeType { + Input, + Output, +} + #[derive(Debug, Copy, Clone)] pub enum MediaType { Audio, diff --git a/src/pipewire_connection.rs b/src/pipewire_connection.rs index 7088309..9d966f6 100644 --- a/src/pipewire_connection.rs +++ b/src/pipewire_connection.rs @@ -14,7 +14,7 @@ use pipewire::{ Context, Core, MainLoop, }; -use crate::{GtkMessage, MediaType, PipewireMessage}; +use crate::{GtkMessage, MediaType, NodeType, PipewireMessage}; use state::{Item, State}; enum ProxyItem { @@ -112,6 +112,27 @@ fn handle_node( } }); + let media_class = |class: &str| { + if class.contains("Sink") || class.contains("Input") { + Some(NodeType::Input) + } else if class.contains("Source") || class.contains("Output") { + Some(NodeType::Output) + } else { + None + } + }; + + let node_type = props + .get("media.category") + .and_then(|class| { + if class.contains("Duplex") { + None + } else { + props.get("media.class").and_then(media_class) + } + }) + .or_else(|| props.get("media.class").and_then(media_class)); + state.borrow_mut().insert( node.id, Item::Node { @@ -121,7 +142,11 @@ fn handle_node( ); sender - .send(PipewireMessage::NodeAdded { id: node.id, name }) + .send(PipewireMessage::NodeAdded { + id: node.id, + name, + node_type, + }) .expect("Failed to send message"); } diff --git a/src/view/graph_view.rs b/src/view/graph_view.rs index 228831a..ff0740f 100644 --- a/src/view/graph_view.rs +++ b/src/view/graph_view.rs @@ -8,7 +8,9 @@ use gtk::{ }; use log::{error, warn}; -use std::collections::HashMap; +use std::{cmp::Ordering, collections::HashMap}; + +use crate::NodeType; mod imp { use super::*; @@ -239,14 +241,37 @@ impl GraphView { glib::Object::new(&[]).expect("Failed to create GraphView") } - pub fn add_node(&self, id: u32, node: Node) { + pub fn add_node(&self, id: u32, node: Node, node_type: Option) { let private = imp::GraphView::from_instance(self); node.set_parent(self); - // Place widgets in colums of 4, growing down, then right. - // TODO: Make a better positioning algorithm. - let x = ((private.nodes.borrow().len() / 4) as f32 * 400.0) + 20.0; // This relies on integer division rounding down. - let y = (private.nodes.borrow().len() as f32 % 4.0 * 100.0) + 20.0; + // Place widgets in colums of 3, growing down + let x = if let Some(node_type) = node_type { + match node_type { + NodeType::Output => 20.0, + NodeType::Input => 820.0, + } + } else { + 420.0 + }; + + let y = private + .nodes + .borrow() + .values() + .map(|node| { + //Map nodes to locations + self.get_node_position(&node.clone().upcast()) + }) + .filter(|&(x2, _y)| { + //Only look in our column + (x - x2).abs() < 50.0 + }) + .max_by(|y1, y2| { + //Get max in column + y1.partial_cmp(y2).unwrap_or(Ordering::Equal) + }) + .map_or(20_f32, |(_x, y)| y + 100.0); self.move_node(&node.clone().upcast(), x, y);