mirror of
https://gitlab.freedesktop.org/pipewire/helvum
synced 2026-03-15 11:36:11 +08:00
Add initial project.
This commit is contained in:
61
src/main.rs
Normal file
61
src/main.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
mod view;
|
||||
|
||||
use glib::clone;
|
||||
use gio::prelude::*;
|
||||
use gtk::prelude::*;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct PipewireLink {
|
||||
pub node_from: u32,
|
||||
pub port_from: u32,
|
||||
pub node_to: u32,
|
||||
pub port_to: u32
|
||||
}
|
||||
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
gtk::init()?;
|
||||
let mut graphview = view::GraphView::new();
|
||||
|
||||
// For UI Testing purposes
|
||||
let mut node = view::PipewireNode::new("Test Node");
|
||||
node.add_ingoing_port(10, gtk::Button::with_label("Ingoing Port"));
|
||||
node.add_outgoing_port(11, gtk::Button::with_label("Outgoing Port"));
|
||||
node.add_outgoing_port(12, gtk::Button::with_label("Outgoing Port 2"));
|
||||
|
||||
let mut node2 = view::PipewireNode::new("Test Node 2");
|
||||
node2.add_ingoing_port(13, gtk::Button::with_label("Ingoing Port"));
|
||||
node2.add_outgoing_port(14, gtk::Button::with_label("Outgoing Port"));
|
||||
node2.add_outgoing_port(15, gtk::Button::with_label("Outgoing Port 2"));
|
||||
|
||||
graphview.add_node(0, node);
|
||||
graphview.add_node(1, node2);
|
||||
graphview.add_link(2, PipewireLink {
|
||||
node_from: 0,
|
||||
port_from: 12,
|
||||
node_to: 1,
|
||||
port_to: 13
|
||||
});
|
||||
// End UI Testing
|
||||
|
||||
let graphview = Rc::new(graphview);
|
||||
|
||||
let app = gtk::Application::new(
|
||||
Some("org.freedesktop.pipewire.graphui"),
|
||||
gio::ApplicationFlags::FLAGS_NONE,
|
||||
)
|
||||
.expect("Application creation failed");
|
||||
|
||||
app.connect_activate(clone!(@strong graphview => move |app| {
|
||||
let window = gtk::ApplicationWindow::new(app);
|
||||
window.set_default_size(800, 600);
|
||||
window.set_title("Pipewire Graph Editor");
|
||||
window.add(&graphview.widget);
|
||||
window.show_all();
|
||||
}));
|
||||
|
||||
app.run(&std::env::args().collect::<Vec<_>>());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
96
src/view/graph_view.rs
Normal file
96
src/view/graph_view.rs
Normal file
@@ -0,0 +1,96 @@
|
||||
use super::PipewireNode;
|
||||
use cairo::Context;
|
||||
use glib::clone;
|
||||
use gtk::{prelude::*, LayoutExt};
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
pub struct GraphView {
|
||||
pub(crate) widget: gtk::Layout,
|
||||
nodes: Rc<RefCell<HashMap<u32, PipewireNode>>>,
|
||||
links: Rc<RefCell<HashMap<u32, crate::PipewireLink>>>,
|
||||
}
|
||||
|
||||
impl GraphView {
|
||||
pub fn new() -> Self {
|
||||
let result = Self {
|
||||
widget: gtk::Layout::new::<gtk::Adjustment, gtk::Adjustment>(None, None),
|
||||
nodes: Rc::new(RefCell::new(HashMap::new())),
|
||||
links: Rc::new(RefCell::new(HashMap::new())),
|
||||
};
|
||||
|
||||
result.widget.connect_draw(clone!(
|
||||
@weak result.nodes as nodes, @weak result.links as links => @default-panic,
|
||||
move |_, cr| {
|
||||
draw(nodes, links, cr);
|
||||
Inhibit(false)
|
||||
}));
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn add_node(&mut self, id: u32, node: PipewireNode) {
|
||||
// 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_mut().insert(id, node);
|
||||
}
|
||||
|
||||
/// 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,
|
||||
/// `from` and `to` are the id's of the ingoing and outgoing port, respectively.
|
||||
pub fn add_link(&mut self, link_id: u32, link: crate::PipewireLink) {
|
||||
self.links.borrow_mut().insert(link_id, link);
|
||||
self.widget.queue_draw();
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(
|
||||
nodes: Rc<RefCell<HashMap<u32, PipewireNode>>>,
|
||||
links: Rc<RefCell<HashMap<u32, crate::PipewireLink>>>,
|
||||
cr: &Context,
|
||||
) {
|
||||
cr.set_line_width(2.0);
|
||||
cr.set_source_rgb(255.0, 255.0, 255.0);
|
||||
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);
|
||||
|
||||
cr.move_to(
|
||||
(from_alloc.x + from_alloc.width).into(),
|
||||
(from_alloc.y + (from_alloc.height / 2)).into()
|
||||
);
|
||||
cr.line_to(
|
||||
to_alloc.x.into(),
|
||||
(to_alloc.y + (to_alloc.height / 2)).into()
|
||||
);
|
||||
|
||||
cr.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
fn get_allocs(
|
||||
nodes: Rc<RefCell<HashMap<u32, PipewireNode>>>,
|
||||
link: &crate::PipewireLink,
|
||||
) -> (gtk::Allocation, gtk::Allocation) {
|
||||
let from_alloc = &nodes
|
||||
.borrow()
|
||||
.get(&link.node_from)
|
||||
.unwrap()
|
||||
.get_outgoing_port(link.port_from)
|
||||
.unwrap()
|
||||
.get_allocation();
|
||||
let to_alloc = &nodes
|
||||
.borrow()
|
||||
.get(&link.node_to)
|
||||
.unwrap()
|
||||
.get_ingoing_port(link.port_to)
|
||||
.unwrap()
|
||||
.get_allocation();
|
||||
|
||||
(from_alloc.to_owned(), to_alloc.to_owned())
|
||||
}
|
||||
5
src/view/mod.rs
Normal file
5
src/view/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod graph_view;
|
||||
mod pipewire_node;
|
||||
|
||||
pub use pipewire_node::PipewireNode;
|
||||
pub use graph_view::GraphView;
|
||||
43
src/view/pipewire_node.rs
Normal file
43
src/view/pipewire_node.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use gtk::GridExt;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct PipewireNode {
|
||||
pub(super) widget: gtk::Grid,
|
||||
_label: gtk::Label,
|
||||
ingoing_ports: HashMap<u32, gtk::Button>,
|
||||
outgoing_ports: HashMap<u32, gtk::Button>,
|
||||
}
|
||||
|
||||
impl PipewireNode {
|
||||
pub fn new(name: &str) -> Self {
|
||||
let widget = gtk::Grid::new();
|
||||
let label = gtk::Label::new(Some(name));
|
||||
widget.attach(&label, 0, 0, 2, 1);
|
||||
|
||||
Self {
|
||||
widget,
|
||||
_label: label,
|
||||
ingoing_ports: HashMap::new(),
|
||||
outgoing_ports: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
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_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<>k::Button> {
|
||||
self.ingoing_ports.get(&id)
|
||||
}
|
||||
|
||||
pub fn get_outgoing_port(&self, id: u32) -> Option<>k::Button> {
|
||||
self.outgoing_ports.get(&id)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user