Skip to content

Commit

Permalink
Merge branch 'grouped_nodes'
Browse files Browse the repository at this point in the history
- Pipewire Nodes with the same name are grouped together and rendered as a single node ( #1 ), in the debug view(on pressing Ctrl), the description and node id of the underlying pipewire node is shown

- Support cycles ( #5 ), graphs in cycles in them are a supported now

- Add support for customizing node background colors (with transparency)

- Add an Arrange button ( related to #7 ), which relayouts the nodes automatically when clicked, the layout algorithm (topological sort) is temporary, will be replaced in the future

- Updated README.md to include better build instructions
  • Loading branch information
Ax9D committed Nov 24, 2022
1 parent d792a07 commit b39a9c3
Show file tree
Hide file tree
Showing 15 changed files with 587 additions and 309 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/target
.vscode/*
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ simple_logger = "1.13.0"
# egui stuff
eframe = { version = "0.15.0", features = ["persistence"] }
egui = "0.15.0"
egui_nodes = {git = "https://github.com/Ax9D/egui_nodes", rev="74eccc4"}
egui_nodes = {git = "https://github.com/Ax9D/egui_nodes", rev="3544d61"}
serde = { version = "1", features = ["derive"] }

[profile.release]
Expand Down
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,22 @@
This is still a WIP, node layouting is kinda jank at the moment.

# Installation

A compiled binary is available on the [releases page](https://github.com/Ax9D/pw-viz/releases).

## Building from source
Clone the repo:
To build pw-viz, you will need to have Rust installed. The recommended way to install Rust is from the [official download page](https://www.rust-lang.org/tools/install), using rustup.

### Stable Release
Download and extract the source code to the latest release over on the [releases page](https://github.com/Ax9D/pw-viz/releases).

### Main branch
Alternatively, you can clone the main branch, although its NOT guaranteed to be stable or bug free.
```
git clone https://github.com/Ax9D/pw-viz
cd pw-viz
```
To build pw-viz, you will need to have Rust installed. The recommended way to install Rust is from the [official download page](https://www.rust-lang.org/tools/install), using rustup.

Once Rust is installed, you can build pw-viz:

### Build
Next, `cd` into your source folder and then start the build using:
```
cargo build --release
```
Expand All @@ -47,8 +50,8 @@ Zooming is not supported currently
* A modified fork of [egui-nodes](https://github.com/haighcam/egui_nodes): A egui port of [imnodes](https://github.com/Nelarius/imnodes)

# Thanks / Alternatives
Pipewire connection code is inspired by helvum's implementation,
[helvum](https://gitlab.freedesktop.org/ryuukyu/helvum): A GTK patchbay for pipewire.
Pipewire connection code is inspired by helvum's implementation
* [helvum](https://gitlab.freedesktop.org/ryuukyu/helvum): A GTK patchbay for pipewire.

# License
pw-viz is licensed under the terms of the GNU General Public License v3.0. See LICENSE for more information.
Binary file modified assets/demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file removed src/id.rs
Empty file.
14 changes: 9 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let (sender, receiver) = std::sync::mpsc::channel();
let (pwsender, pwreciever) = pipewire::channel::channel();

// Set up pipewire thread
let pw_thread_handle = thread::spawn(move || {
let sender = Rc::new(sender);
pipewire_impl::thread_main(sender, pwreciever).expect("Failed to init pipewire client");
});
//Set's up pipewire thread
let pw_thread_handle = thread::Builder::new()
.name("Pipewire".to_string())
.spawn(move || {
let sender = Rc::new(sender);

pipewire_impl::thread_main(sender, pwreciever).expect("Failed to init pipewire client");
})
.expect("Failed to create pipewire thread");

ui::run_graph_ui(receiver, pwsender);

Expand Down
98 changes: 80 additions & 18 deletions src/pipewire_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,20 @@ pub enum PipewireMessage {
NodeAdded {
id: u32,
name: String,
description: Option<String>,
media_type: Option<MediaType>,
},
PortAdded {
node_name: String,
node_id: u32,
id: u32,
name: String,
port_type: PortType,
},
LinkAdded {
id: u32,
from_node: u32,
to_node: u32,
from_node_name: String,
to_node_name: String,

from_port: u32,
to_port: u32,
Expand All @@ -37,9 +39,11 @@ pub enum PipewireMessage {
active: bool,
},
NodeRemoved {
name: String,
id: u32,
},
PortRemoved {
node_name: String,
node_id: u32,
id: u32,
},
Expand Down Expand Up @@ -113,11 +117,17 @@ pub fn thread_main(
.global_remove(move |id| match state_rm.borrow_mut().remove(id) {
Some(object) => {
let message = match object {
state::GlobalObject::Node => PipewireMessage::NodeRemoved { id },
state::GlobalObject::Node { name } => PipewireMessage::NodeRemoved { name, id },
state::GlobalObject::Link => PipewireMessage::LinkRemoved { id },
state::GlobalObject::Port { node_id, id } => {
PipewireMessage::PortRemoved { node_id, id }
}
state::GlobalObject::Port {
node_name,
node_id,
id,
} => PipewireMessage::PortRemoved {
node_name,
node_id,
id,
},
};
sender_rm
.send(message)
Expand All @@ -141,12 +151,9 @@ pub fn thread_main(
UiMessage::RemoveLink(link_id) => {
remove_link(link_id, &state, &registry);
}
UiMessage::AddLink {
from_node,
to_node,
from_port,
to_port,
} => add_link(from_port, to_port, from_node, to_node, &core),
UiMessage::AddLink { from_port, to_port } => {
add_link(&state, from_port, to_port, &core)
}
UiMessage::Exit => mainloop.quit(),
}
});
Expand All @@ -166,9 +173,11 @@ fn handle_node(
.as_ref()
.expect("Node object doesn't have properties");

let description = props.get("node.description");

let name = props
.get("node.nick")
.or_else(|| props.get("node.description"))
.or(description)
.or_else(|| props.get("node.name"))
.unwrap_or_default()
.to_string();
Expand All @@ -185,12 +194,16 @@ fn handle_node(
}
});

state.borrow_mut().add(node.id, state::GlobalObject::Node);
state
.borrow_mut()
.add(node.id, state::GlobalObject::Node { name: name.clone() });

let description = description.map(|desc| desc.to_string());
sender
.send(PipewireMessage::NodeAdded {
id: node.id,
name,
description,
media_type,
})
.expect("Failed to send pipewire message");
Expand Down Expand Up @@ -219,6 +232,16 @@ fn handle_link(
let to_port = info.input_port_id();

let mut state = state.borrow_mut();

let from_node_name = match state.get(from_node).expect("Id wasn't registered") {
state::GlobalObject::Node { name } => name.clone(),
_ => unreachable!(),
};
let to_node_name = match state.get(to_node).expect("Id wasn't registered") {
state::GlobalObject::Node { name } => name.clone(),
_ => unreachable!(),
};

if let Some(&state::GlobalObject::Link) = state.get(id) {
if info.change_mask().contains(LinkChangeMask::STATE) {
sender
Expand All @@ -230,8 +253,8 @@ fn handle_link(
log::debug!("New pipewire link was added : {}", id);
sender
.send(PipewireMessage::LinkAdded {
from_node,
to_node,
from_node_name,
to_node_name,
from_port,
to_port,
id,
Expand All @@ -245,8 +268,32 @@ fn handle_link(
.borrow_mut()
.insert(link.id, ProxyLink { proxy, listener });
}
fn add_link(state: &Rc<RefCell<State>>, from_port: u32, to_port: u32, core: &Rc<Core>) {
let state = state.borrow();
let from_port_ob = state
.get(from_port)
.expect(&format!("Port with id {} was never registered", from_port));
let from_node = *match from_port_ob {
state::GlobalObject::Port {
node_name: _,
node_id,
id: _,
} => node_id,
_ => unreachable!(),
};

let to_port_ob = state
.get(to_port)
.expect(&format!("Port with id {} was never registered", to_port));
let to_node = *match to_port_ob {
state::GlobalObject::Port {
node_name: _,
node_id,
id: _,
} => node_id,
_ => unreachable!(),
};

fn add_link(from_port: u32, to_port: u32, from_node: u32, to_node: u32, core: &Rc<Core>) {
core.create_object::<pipewire::link::Link, _>(
"link-factory",
&pipewire::properties! {
Expand Down Expand Up @@ -288,22 +335,37 @@ fn handle_port(
.parse::<u32>()
.expect("Couldn't parse node.id as u32");

let mut state = state.borrow_mut();

let node_name = match state
.get(node_id)
.expect(&format!("Node with id {} was never registered", node_id))
{
state::GlobalObject::Node { name } => name,
_ => {
unreachable!()
}
}
.clone();

let port_type = match props.get("port.direction") {
Some("in") => PortType::Input,
Some("out") => PortType::Output,
_ => PortType::Unknown,
};

state.borrow_mut().add(
state.add(
port.id,
state::GlobalObject::Port {
node_name: node_name.clone(),
node_id,
id: port.id,
},
);

sender
.send(PipewireMessage::PortAdded {
node_name,
node_id,
id: port.id,
name,
Expand Down
10 changes: 8 additions & 2 deletions src/pipewire_impl/state.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
use std::collections::HashMap;

pub enum GlobalObject {
Node,
Node {
name: String,
},
Link,
Port { node_id: u32, id: u32 },
Port {
node_name: String,
node_id: u32,
id: u32,
},
}

/// For internal state tracking, this has to be done because pipewire only provides ids of the objects it removes,
Expand Down
Loading

0 comments on commit b39a9c3

Please sign in to comment.