Display Nodes/Ports/Links obtained from pipewire server

This commit is contained in:
Tom A. Wagner
2021-01-03 19:01:47 +01:00
parent 438383e92f
commit b129d84fa2
8 changed files with 666 additions and 102 deletions

View File

@@ -1,4 +1,4 @@
use super::PipewireNode;
use super::Node;
use cairo::Context;
use glib::clone;
use gtk::{prelude::*, LayoutExt};
@@ -6,7 +6,7 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc};
pub struct GraphView {
pub(crate) widget: gtk::Layout,
nodes: Rc<RefCell<HashMap<u32, PipewireNode>>>,
nodes: Rc<RefCell<HashMap<u32, Node>>>,
links: Rc<RefCell<HashMap<u32, crate::PipewireLink>>>,
}
@@ -28,16 +28,29 @@ impl GraphView {
result
}
pub fn add_node(&mut self, id: u32, node: PipewireNode) {
pub fn add_node(&mut self, id: u32, node: Node) {
// TODO: Find a free position to put the widget at.
self.widget.put(
&node.widget,
(self.nodes.borrow().len() % 4 * 400) as i32,
(self.nodes.borrow().len() / 4 * 100) as i32,
(self.nodes.borrow().len() / 4 * 400) as i32,
(self.nodes.borrow().len() % 4 * 100) as i32,
);
node.widget.show_all();
self.nodes.borrow_mut().insert(id, node);
}
pub fn add_port_to_node(&mut self, node_id: u32, port_id: u32, port: super::port::Port) {
if let Some(node) = self.nodes.borrow_mut().get_mut(&node_id) {
node.add_port(port_id, port);
} else {
// FIXME: Log this instead
eprintln!(
"Node with id {} not found when trying to add port with id {} to graph",
node_id, port_id
);
}
}
/// Add a link to the graph.
///
/// `add_link` takes three arguments: `link_id` is the id of the link as assigned by the pipewire server,
@@ -49,7 +62,7 @@ impl GraphView {
}
fn draw(
nodes: Rc<RefCell<HashMap<u32, PipewireNode>>>,
nodes: Rc<RefCell<HashMap<u32, Node>>>,
links: Rc<RefCell<HashMap<u32, crate::PipewireLink>>>,
cr: &Context,
) {
@@ -58,44 +71,40 @@ fn draw(
cr.paint();
cr.set_source_rgb(0.0, 0.0, 0.0);
for link in links.borrow().values() {
let (from_alloc, to_alloc) = get_allocs(nodes.clone(), link);
if let Some((from_alloc, to_alloc)) = get_allocs(nodes.clone(), link) {
let from_x: f64 = (from_alloc.x + from_alloc.width).into();
let from_y: f64 = (from_alloc.y + (from_alloc.height / 2)).into();
cr.move_to(from_x, from_y);
let from_x: f64 = (from_alloc.x + from_alloc.width).into();
let from_y: f64 = (from_alloc.y + (from_alloc.height / 2)).into();
cr.move_to(
from_x, from_y
);
let to_x: f64 = to_alloc.x.into();
let to_y: f64 = (to_alloc.y + (to_alloc.height / 2)).into();
cr.curve_to(from_x + 75.0, from_y, to_x - 75.0, to_y, to_x, to_y);
let to_x: f64 = to_alloc.x.into();
let to_y: f64 = (to_alloc.y + (to_alloc.height / 2)).into();
cr.curve_to(
from_x + 75.0, from_y,
to_x - 75.0, to_y,
to_x, to_y
);
cr.stroke();
cr.stroke();
} else {
eprintln!("Could not get allocation of ports of link: {:?}", link);
}
}
}
fn get_allocs(
nodes: Rc<RefCell<HashMap<u32, PipewireNode>>>,
nodes: Rc<RefCell<HashMap<u32, Node>>>,
link: &crate::PipewireLink,
) -> (gtk::Allocation, gtk::Allocation) {
) -> Option<(gtk::Allocation, gtk::Allocation)> {
println!();
let from_alloc = &nodes
.borrow()
.get(&link.node_from)
.unwrap()
.get_outgoing_port(link.port_from)
.unwrap()
.get(&link.node_from)?
.get_port(link.port_from)?
.widget
.get_allocation();
let to_alloc = &nodes
.borrow()
.get(&link.node_to)
.unwrap()
.get_ingoing_port(link.port_to)
.unwrap()
.get(&link.node_to)?
.get_port(link.port_to)?
.widget
.get_allocation();
(from_alloc.to_owned(), to_alloc.to_owned())
Some((from_alloc.to_owned(), to_alloc.to_owned()))
}

View File

@@ -1,5 +1,6 @@
mod graph_view;
mod pipewire_node;
mod node;
pub mod port;
pub use graph_view::GraphView;
pub use pipewire_node::PipewireNode;
pub use node::Node;

View File

@@ -1,40 +1,47 @@
use gtk::prelude::*;
use gdk::prelude::*;
use gtk::prelude::*;
use std::collections::HashMap;
pub struct PipewireNode {
use pipewire::port::Direction;
pub struct Node {
pub(super) widget: gtk::Grid,
label: gtk::Label,
label_box: gtk::EventBox,
ingoing_ports: HashMap<u32, gtk::Button>,
outgoing_ports: HashMap<u32, gtk::Button>,
ports: HashMap<u32, super::port::Port>,
num_ports_in: u32,
num_ports_out: u32,
}
impl PipewireNode {
impl Node {
pub fn new(name: &str) -> Self {
let result = Self {
widget: gtk::Grid::new(),
label: gtk::Label::new(Some(name)),
label_box: gtk::EventBox::new(),
ingoing_ports: HashMap::new(),
outgoing_ports: HashMap::new(),
ports: HashMap::new(),
num_ports_in: 0,
num_ports_out: 0,
};
result.label_box.add(&result.label);
result.widget.attach(&result.label_box, 0, 0, 2, 1);
// Setup needed events for dragging a node.
result
.label_box
.add_events(gdk::EventMask::BUTTON1_MOTION_MASK);
// Setup callback for dragging the node.
result
.label_box
.connect_motion_notify_event(|label, event| {
let grid = label
let node_frame = label
.get_ancestor(gtk::Grid::static_type())
.unwrap()
.dynamic_cast::<gtk::Grid>()
.unwrap();
let graphview = grid
let graphview = node_frame
.get_ancestor(gtk::Layout::static_type())
.unwrap()
.dynamic_cast::<gtk::Layout>()
@@ -47,8 +54,8 @@ impl PipewireNode {
// TODO: Calculate proper values to center the mouse on the label
// instead of using hardcoded offsets.
graphview.set_child_x(&grid, x as i32 - offset_x - 100);
graphview.set_child_y(&grid, y as i32 - offset_y - 50);
graphview.set_child_x(&node_frame, x as i32 - offset_x - 100);
graphview.set_child_y(&node_frame, y as i32 - offset_y - 50);
// FIXME: If links become proper widgets,
// we don't need to redraw the full graph everytime.
@@ -60,23 +67,25 @@ impl PipewireNode {
result
}
pub fn add_ingoing_port(&mut self, id: u32, port: gtk::Button) {
self.widget
.attach(&port, 0, (self.ingoing_ports.len() + 1) as i32, 1, 1);
self.ingoing_ports.insert(id, port);
pub fn add_port(&mut self, id: u32, port: super::port::Port) {
match port.direction {
Direction::Input => {
self.widget
.attach(&port.widget, 0, (self.num_ports_in + 1) as i32, 1, 1);
self.num_ports_in += 1;
}
Direction::Output => {
self.widget
.attach(&port.widget, 1, (self.num_ports_out + 1) as i32, 1, 1);
self.num_ports_out += 1;
}
}
port.widget.show_all();
self.ports.insert(id, port);
}
pub fn add_outgoing_port(&mut self, id: u32, port: gtk::Button) {
self.widget
.attach(&port, 1, (self.outgoing_ports.len() + 1) as i32, 1, 1);
self.outgoing_ports.insert(id, port);
}
pub fn get_ingoing_port(&self, id: u32) -> Option<&gtk::Button> {
self.ingoing_ports.get(&id)
}
pub fn get_outgoing_port(&self, id: u32) -> Option<&gtk::Button> {
self.outgoing_ports.get(&id)
pub fn get_port(&self, id: u32) -> Option<&super::port::Port> {
self.ports.get(&id)
}
}

16
src/view/port.rs Normal file
View File

@@ -0,0 +1,16 @@
/// Graphical representation of a pipewire port.
pub struct Port {
pub(super) widget: gtk::Button,
pub id: u32,
pub direction: pipewire::port::Direction,
}
impl Port {
pub fn new(id: u32, name: &str, direction: pipewire::port::Direction) -> Self {
Self {
widget: gtk::Button::with_label(name),
id,
direction
}
}
}