diff --git a/Cargo.lock b/Cargo.lock index c3c30e1..f9f926e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,23 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + [[package]] name = "anyhow" version = "1.0.34" @@ -29,7 +47,41 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps", + "system-deps 1.3.2", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "bindgen" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "clap", + "env_logger", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "which", ] [[package]] @@ -61,7 +113,7 @@ checksum = "2ed2639b9ad5f1d6efa76de95558e11339e7318426d84ac4890b86c03e828ca7" dependencies = [ "glib-sys", "libc", - "system-deps", + "system-deps 1.3.2", ] [[package]] @@ -70,12 +122,72 @@ version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40" +[[package]] +name = "cexpr" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0659001ab56b791be01d4b729c44376edc6718cf389a502e579b77b758f3296c" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "env_logger" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "futures" version = "0.3.8" @@ -216,7 +328,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps", + "system-deps 1.3.2", ] [[package]] @@ -233,7 +345,7 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps", + "system-deps 1.3.2", ] [[package]] @@ -266,7 +378,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps", + "system-deps 1.3.2", "winapi", ] @@ -312,9 +424,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e9b997a66e9a23d073f2b1abb4dbfc3925e0b8952f67efd8d9b6e168e4cdc1" dependencies = [ "libc", - "system-deps", + "system-deps 1.3.2", ] +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + [[package]] name = "gobject-sys" version = "0.10.0" @@ -323,7 +441,7 @@ checksum = "952133b60c318a62bf82ee75b93acc7e84028a093e06b9e27981c2b6fe68218c" dependencies = [ "glib-sys", "libc", - "system-deps", + "system-deps 1.3.2", ] [[package]] @@ -335,6 +453,7 @@ dependencies = [ "gio", "glib", "gtk", + "pipewire", ] [[package]] @@ -380,7 +499,7 @@ dependencies = [ "gobject-sys", "libc", "pango-sys", - "system-deps", + "system-deps 1.3.2", ] [[package]] @@ -392,6 +511,21 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "hermit-abi" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" + [[package]] name = "itertools" version = "0.9.0" @@ -401,18 +535,90 @@ dependencies = [ "either", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" +[[package]] +name = "libloading" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9367bdfa836b7e3cf895867f7a570283444da90562980ec2263d6e1569b16bc" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "libspa" +version = "0.1.0" +source = "git+https://gitlab.freedesktop.org/gdesmott/pipewire-rs?branch=proxies#6560d4f769cc0d9937d6091f2f3eb298ef31cbfc" +dependencies = [ + "bitflags", + "libspa-sys", +] + +[[package]] +name = "libspa-sys" +version = "0.1.0" +source = "git+https://gitlab.freedesktop.org/gdesmott/pipewire-rs?branch=proxies#6560d4f769cc0d9937d6091f2f3eb298ef31cbfc" +dependencies = [ + "bindgen", + "system-deps 2.0.2", +] + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if 0.1.10", +] + [[package]] name = "memchr" version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "nix" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" +dependencies = [ + "bitflags", + "cc", + "cfg-if 0.1.10", + "libc", + "void", +] + +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "memchr", + "version_check", +] + [[package]] name = "once_cell" version = "1.5.0" @@ -443,9 +649,15 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps", + "system-deps 1.3.2", ] +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "pin-project" version = "1.0.1" @@ -472,6 +684,31 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pipewire" +version = "0.1.0" +source = "git+https://gitlab.freedesktop.org/gdesmott/pipewire-rs?branch=proxies#6560d4f769cc0d9937d6091f2f3eb298ef31cbfc" +dependencies = [ + "anyhow", + "bitflags", + "libc", + "libspa", + "libspa-sys", + "pipewire-sys", + "signal", + "thiserror", +] + +[[package]] +name = "pipewire-sys" +version = "0.1.0" +source = "git+https://gitlab.freedesktop.org/gdesmott/pipewire-rs?branch=proxies#6560d4f769cc0d9937d6091f2f3eb298ef31cbfc" +dependencies = [ + "bindgen", + "libspa-sys", + "system-deps 2.0.2", +] + [[package]] name = "pkg-config" version = "0.3.19" @@ -541,24 +778,76 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "serde" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +[[package]] +name = "shlex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" + +[[package]] +name = "signal" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f6ce83b159ab6984d2419f495134972b48754d13ff2e3f8c998339942b56ed9" +dependencies = [ + "libc", + "nix", +] + [[package]] name = "slab" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "strum" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" +[[package]] +name = "strum" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c" + [[package]] name = "strum_macros" version = "0.18.0" @@ -571,6 +860,18 @@ dependencies = [ "syn", ] +[[package]] +name = "strum_macros" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "syn" version = "1.0.48" @@ -590,11 +891,44 @@ checksum = "0f3ecc17269a19353b3558b313bba738b25d82993e30d62a18406a24aba4649b" dependencies = [ "heck", "pkg-config", - "strum", - "strum_macros", + "strum 0.18.0", + "strum_macros 0.18.0", "thiserror", "toml", - "version-compare", + "version-compare 0.0.10", +] + +[[package]] +name = "system-deps" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0e2c9cfeb7afa05a18802454f8b467ba12e459301af4b17ea69bce3f63e990" +dependencies = [ + "heck", + "pkg-config", + "strum 0.20.0", + "strum_macros 0.20.1", + "thiserror", + "toml", + "version-compare 0.0.11", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", ] [[package]] @@ -617,6 +951,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + [[package]] name = "toml" version = "0.5.7" @@ -632,24 +975,57 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version-compare" version = "0.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" +[[package]] +name = "version-compare" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" + [[package]] name = "version_check" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "which" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" +dependencies = [ + "libc", +] + [[package]] name = "winapi" version = "0.3.9" @@ -666,6 +1042,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 5adba8d..83260ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,6 @@ cairo-rs = "0.9.1" glib = "0.10.3" gdk = "0.13.2" gtk = "0.9.2" -gio = "0.9.1" \ No newline at end of file +gio = "0.9.1" + +pipewire = { git = "https://gitlab.freedesktop.org/gdesmott/pipewire-rs", branch = "proxies"} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index af00857..00fae46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,13 @@ +mod pipewire_connection; mod view; use gio::prelude::*; use glib::clone; use gtk::prelude::*; -use std::rc::Rc; +use std::{cell::RefCell, rc::Rc}; +#[derive(Debug)] pub struct PipewireLink { pub node_from: u32, pub port_from: u32, @@ -15,33 +17,18 @@ pub struct PipewireLink { fn main() -> Result<(), Box> { gtk::init()?; - let mut graphview = view::GraphView::new(); + let graphview = Rc::new(RefCell::new(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); + // Create the connection to the pipewire server and do an initial roundtrip before showing the view, + // so that the graph is already populated when the window opens. + let pw_con = pipewire_connection::PipewireConnection::new(graphview.clone()) + .expect("Failed to initialize pipewire connection"); + pw_con.roundtrip(); + // From now on, call roundtrip() every second. + glib::timeout_add_seconds_local(1, move || { + pw_con.roundtrip(); + Continue(true) + }); let app = gtk::Application::new( Some("org.freedesktop.pipewire.graphui"), @@ -53,7 +40,7 @@ fn main() -> Result<(), Box> { let window = gtk::ApplicationWindow::new(app); window.set_default_size(800, 600); window.set_title("Pipewire Graph Editor"); - window.add(&graphview.widget); + window.add(&graphview.borrow().widget); window.show_all(); })); diff --git a/src/pipewire_connection.rs b/src/pipewire_connection.rs new file mode 100644 index 0000000..1ef8a75 --- /dev/null +++ b/src/pipewire_connection.rs @@ -0,0 +1,155 @@ +use std::{ + cell::{Cell, RefCell}, + rc::Rc, +}; + +use glib::clone; +use pipewire as pw; +use pw::{port::Direction, registry::ObjectType, PW_ID_CORE}; + +use crate::PipewireLink; + +pub struct PipewireConnection { + mainloop: pw::MainLoop, + _context: pw::Context, + core: pw::Core, + _registry: pw::registry::Registry, + _reg_listeners: pw::registry::Listener, +} + +impl PipewireConnection { + pub fn new(graphview: Rc>) -> Result { + pw::init(); + let mainloop = pw::MainLoop::new().map_err(|_| "Failed to create pipewire mainloop!")?; + let context = + pw::Context::new(&mainloop).map_err(|_| "Failed to create pipewire context")?; + let core = context + .connect() + .map_err(|_| "Failed to connect to pipewire core")?; + let registry = core.get_registry(); + + let reg_listeners = registry + .add_listener_local() + .global(clone!(@weak graphview => @default-panic, move |global| { + PipewireConnection::handle_global(graphview, global) + })) + .global_remove(|_| { /* TODO */ }) + .register(); + + Ok(Self { + mainloop, + _context: context, + core, + _registry: registry, + _reg_listeners: reg_listeners, + }) + } + + pub fn roundtrip(&self) { + let done = Rc::new(Cell::new(false)); + let pending = self.core.sync(0); + + let done_clone = done.clone(); + let loop_clone = self.mainloop.clone(); + + let _listener = self + .core + .add_listener_local() + .done(move |id, seq| { + if id == PW_ID_CORE && seq == pending { + done_clone.set(true); + loop_clone.quit(); + } + }) + .register(); + + while !done.get() { + self.mainloop.run(); + } + } + + fn handle_global( + graphview: Rc>, + global: pw::registry::GlobalObject, + ) { + match global.type_ { + ObjectType::Node => { + let node_widget = crate::view::Node::new(&format!( + "{}", + global + .props + .map(|dict| String::from( + dict.get("node.nick") + .or(dict.get("node.description")) + .or(dict.get("node.name")) + .unwrap_or_default() + )) + .unwrap_or_default() + )); + + graphview.borrow_mut().add_node(global.id, node_widget); + } + ObjectType::Port => { + let props = global.props.expect("Port object is missing properties"); + let port_label = format!("{}", props.get("port.name").unwrap_or_default()); + let node_id: u32 = props + .get("node.id") + .expect("Port has no node.id property!") + .parse() + .expect("Could not parse node.id property"); + let port = crate::view::port::Port::new( + global.id, + &port_label, + if matches!(props.get("port.direction"), Some("in")) { + Direction::Input + } else { + Direction::Output + }, + ); + + graphview + .borrow_mut() + .add_port_to_node(node_id, global.id, port); + } + ObjectType::Link => { + let props = global.props.expect("Link object is missing properties"); + let input_node: u32 = props + .get("link.input.node") + .expect("Link has no link.input.node property") + .parse() + .expect("Could not parse link.input.node property"); + let input_port: u32 = props + .get("link.input.port") + .expect("Link has no link.input.port property") + .parse() + .expect("Could not parse link.input.port property"); + let output_node: u32 = props + .get("link.output.node") + .expect("Link has no link.input.node property") + .parse() + .expect("Could not parse link.input.node property"); + let output_port: u32 = props + .get("link.output.port") + .expect("Link has no link.output.port property") + .parse() + .expect("Could not parse link.output.port property"); + graphview.borrow_mut().add_link( + global.id, + PipewireLink { + node_from: output_node, + port_from: output_port, + node_to: input_node, + port_to: input_port, + }, + ); + } + _ => {} + } + } +} + +/*impl Drop for PipewireConnection { + fn drop(&mut self) { + unsafe { pw::deinit() } + } +}*/ diff --git a/src/view/graph_view.rs b/src/view/graph_view.rs index b72fe6a..472dd0c 100644 --- a/src/view/graph_view.rs +++ b/src/view/graph_view.rs @@ -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>>, + nodes: Rc>>, links: Rc>>, } @@ -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>>, + nodes: Rc>>, links: Rc>>, 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>>, + nodes: Rc>>, 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())) } diff --git a/src/view/mod.rs b/src/view/mod.rs index 1b397df..1635aef 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -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; diff --git a/src/view/pipewire_node.rs b/src/view/node.rs similarity index 57% rename from src/view/pipewire_node.rs rename to src/view/node.rs index 6d70625..e10d0a2 100644 --- a/src/view/pipewire_node.rs +++ b/src/view/node.rs @@ -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, - outgoing_ports: HashMap, + ports: HashMap, + 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::() .unwrap(); - let graphview = grid + let graphview = node_frame .get_ancestor(gtk::Layout::static_type()) .unwrap() .dynamic_cast::() @@ -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<>k::Button> { - self.ingoing_ports.get(&id) - } - - pub fn get_outgoing_port(&self, id: u32) -> Option<>k::Button> { - self.outgoing_ports.get(&id) + pub fn get_port(&self, id: u32) -> Option<&super::port::Port> { + self.ports.get(&id) } } diff --git a/src/view/port.rs b/src/view/port.rs new file mode 100644 index 0000000..981e9dd --- /dev/null +++ b/src/view/port.rs @@ -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 + } + } +}