From ec8de4a4a7a513b1c61288486e9dde3836321aaa Mon Sep 17 00:00:00 2001 From: "Tom A. Wagner" Date: Tue, 9 Feb 2021 12:51:35 +0100 Subject: [PATCH] Color ports depending on the type of data (audio,video,midi) they carry --- src/main.rs | 29 +++++++++++++++++++ src/pipewire_state.rs | 65 +++++++++++++++++++++++++++++++++++++++---- src/view/port.rs | 2 +- 3 files changed, 89 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7e032c7..e023b03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,24 @@ use gtk::prelude::*; use std::{cell::RefCell, rc::Rc}; +// FIXME: This should be in its own .css file. +static STYLE: &str = " +.audio { + background: rgb(50,100,240); + color: black; +} + +.video { + background: rgb(200,200,0); + color: black; +} + +.midi { + background: rgb(200,0,50); + color: black; +} +"; + #[derive(Debug)] pub struct PipewireLink { pub node_from: u32, @@ -36,6 +54,17 @@ fn main() -> Result<(), Box> { let app = gtk::Application::new(Some("org.freedesktop.pipewire.graphui"), Default::default()) .expect("Application creation failed"); + app.connect_startup(|_| { + // Load CSS from the STYLE variable. + let provider = gtk::CssProvider::new(); + provider.load_from_data(STYLE.as_bytes()); + gtk::StyleContext::add_provider_for_display( + >k::gdk::Display::get_default().expect("Error initializing gtk css provider."), + &provider, + gtk::STYLE_PROVIDER_PRIORITY_APPLICATION, + ); + }); + app.connect_activate(move |app| { let scrollwindow = gtk::ScrolledWindowBuilder::new() .child(&*graphview.borrow()) diff --git a/src/pipewire_state.rs b/src/pipewire_state.rs index ae9e5db..7ab063e 100644 --- a/src/pipewire_state.rs +++ b/src/pipewire_state.rs @@ -1,6 +1,8 @@ use crate::{view, PipewireLink}; +use gtk::WidgetExt; use libspa::dict::ReadableDict; +use log::warn; use pipewire::{ port::Direction, registry::{GlobalObject, ObjectType}, @@ -8,9 +10,20 @@ use pipewire::{ use std::{cell::RefCell, collections::HashMap, rc::Rc}; +enum MediaType { + Audio, + Video, + Midi, +} + enum Item { - Node(view::Node), - Port { node_id: u32 }, + Node { + widget: view::Node, + media_type: Option, + }, + Port { + node_id: u32, + }, Link, } @@ -56,6 +69,7 @@ impl PipewireState { let node_widget = crate::view::Node::new(&format!( "{}", node.props + .as_ref() .map(|dict| String::from( dict.get("node.nick") .or(dict.get("node.description")) @@ -64,12 +78,38 @@ impl PipewireState { )) .unwrap_or_default() )); + + // FIXME: This relies on the node being passed to us by the pipwire server before its port. + let media_type = node + .props + .map(|props| { + props.get("media.class").map(|class| { + if class.contains("Audio") { + Some(MediaType::Audio) + } else if class.contains("Video") { + Some(MediaType::Video) + } else if class.contains("Midi") { + Some(MediaType::Midi) + } else { + None + } + }) + }) + .flatten() + .flatten(); + self.graphview .borrow_mut() .add_node(node.id, node_widget.clone()); // Save the created widget so we can delete ports easier. - self.items.insert(node.id, Item::Node(node_widget)); + self.items.insert( + node.id, + Item::Node { + widget: node_widget, + media_type, + }, + ); } fn add_port(&mut self, port: GlobalObject) { @@ -91,6 +131,18 @@ impl PipewireState { }, ); + // Color the port accordingly to its media class. + if let Some(Item::Node { media_type, .. }) = self.items.get(&node_id) { + match media_type { + Some(MediaType::Audio) => new_port.widget.add_css_class("audio"), + Some(MediaType::Video) => new_port.widget.add_css_class("video"), + Some(MediaType::Midi) => new_port.widget.add_css_class("midi"), + None => {} + } + } else { + warn!("Node not found for Port {}", port.id); + } + self.graphview .borrow_mut() .add_port_to_node(node_id, new_port.id, new_port); @@ -100,6 +152,7 @@ impl PipewireState { } fn add_link(&mut self, link: GlobalObject) { + // FIXME: Links should be colored depending on the data they carry (video, audio, midi) like ports are. self.items.insert(link.id, Item::Link); // Update graph to contain the new link. @@ -139,7 +192,7 @@ impl PipewireState { pub fn global_remove(&mut self, id: u32) { if let Some(item) = self.items.get(&id) { match item { - Item::Node(_) => self.remove_node(id), + Item::Node { .. } => self.remove_node(id), Item::Port { node_id } => self.remove_port(id, *node_id), Item::Link => self.remove_link(id), } @@ -158,8 +211,8 @@ impl PipewireState { } fn remove_port(&self, id: u32, node_id: u32) { - if let Some(Item::Node(node)) = self.items.get(&node_id) { - node.remove_port(id); + if let Some(Item::Node { widget, .. }) = self.items.get(&node_id) { + widget.remove_port(id); } } diff --git a/src/view/port.rs b/src/view/port.rs index e42be88..ddc25f3 100644 --- a/src/view/port.rs +++ b/src/view/port.rs @@ -1,6 +1,6 @@ /// Graphical representation of a pipewire port. pub struct Port { - pub(super) widget: gtk::Button, + pub widget: gtk::Button, pub id: u32, pub direction: pipewire::port::Direction, }