diff --git a/src/con.rs b/src/con.rs new file mode 100644 index 0000000..b3255d0 --- /dev/null +++ b/src/con.rs @@ -0,0 +1,47 @@ +use crate::ipc; + +#[allow(dead_code)] +pub struct Con<'a> { + name: &'a str, + id: ipc::Id, + app_id: Option<&'a str>, +} + +impl<'a> std::fmt::Display for Con<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "{} — {}", self.app_id.unwrap_or(""), self.name) + } +} + +/// Gets all cons (aka, application windows) of the tree. +pub fn get_cons<'a>(tree: &'a ipc::Node) -> Vec> { + let mut v = vec![]; + for n in tree.iter() { + if n.r#type == ipc::NodeType::Con || n.r#type == ipc::NodeType::FloatingCon { + v.push(Con { + name: &n + .name + .as_ref() + .expect(format!("Con without name. id = {}", n.id).as_str()), + id: n.id, + app_id: match &n.app_id { + Some(s) => Some(s.as_ref()), + None => None, + }, + }) + } + } + v +} + +#[test] +fn test_get_cons() { + let tree = ipc::get_tree(); + let cons = get_cons(&tree); + + println!("There are {} cons.", cons.len()); + + for c in cons { + println!(" {}", c); + } +} diff --git a/src/ipc.rs b/src/ipc.rs new file mode 100644 index 0000000..3910035 --- /dev/null +++ b/src/ipc.rs @@ -0,0 +1,175 @@ +extern crate serde; +extern crate serde_json; + +use serde::Deserialize; + +use std::process as proc; + +pub type Id = u32; +pub type Dim = u16; +pub type Pid = u16; + +#[derive(Deserialize)] +#[allow(dead_code)] +pub struct Rect { + pub x: Dim, + pub y: Dim, + pub width: Dim, + pub height: Dim, +} + +// TODO: Maybe there are more? +#[derive(Deserialize)] +pub enum Border { + #[serde(rename = "none")] + None, + #[serde(rename = "pixel")] + Pixel, + #[serde(rename = "csd")] + Csd, +} + +// TODO: Maybe there are more? +#[derive(Deserialize)] +pub enum Layout { + #[serde(rename = "splith")] + SplitH, + #[serde(rename = "splitv")] + SplitV, + #[serde(rename = "tabbed")] + Tabbed, + #[serde(rename = "stacked")] + Stacked, + #[serde(rename = "output")] + Output, + #[serde(rename = "none")] + None, +} + +#[derive(Deserialize)] +pub enum Orientation { + #[serde(rename = "horizontal")] + Horizontal, + #[serde(rename = "vertical")] + Vertical, + #[serde(rename = "none")] + None, +} + +#[derive(Deserialize, PartialEq, Debug)] +pub enum NodeType { + #[serde(rename = "root")] + Root, + #[serde(rename = "workspace")] + Workspace, + #[serde(rename = "output")] + Output, + #[serde(rename = "con")] + Con, + #[serde(rename = "floating_con")] + FloatingCon, +} + +#[derive(Deserialize)] +pub enum ShellType { + #[serde(rename = "xdg_shell")] + XdgShell, + #[serde(rename = "xwayland")] + XWayland, +} + +#[derive(Deserialize)] +#[allow(dead_code)] +pub struct Node { + pub id: Id, + pub name: Option, + pub rect: Rect, + pub focused: bool, + pub focus: Vec, + pub border: Border, + pub current_border_width: Dim, + pub layout: Layout, + pub orientation: Orientation, + pub percent: Option, + pub window_rect: Rect, + pub deco_rect: Rect, + pub geometry: Rect, + pub window: Option, + pub urgent: bool, + pub marks: Vec, + pub fullscreen_mode: u8, // TODO: actually, it's 0 or 1, i.e., a bool + pub nodes: Vec, + pub floating_nodes: Vec, + pub sticky: bool, + pub r#type: NodeType, + pub app_id: Option, + pub visible: Option, + pub max_render_time: Option, + pub pid: Option, + pub shell: Option, +} + +impl Node { + pub fn iter(&self) -> PreOrderNodeIter { + PreOrderNodeIter::new(self) + } +} + +pub struct PreOrderNodeIter<'a> { + stack: Vec<&'a Node>, +} + +impl<'a> PreOrderNodeIter<'a> { + fn new(node: &'a Node) -> PreOrderNodeIter { + PreOrderNodeIter { stack: vec![node] } + } +} + +impl<'a> Iterator for PreOrderNodeIter<'a> { + type Item = &'a Node; + + fn next(&mut self) -> Option { + if let Some(node) = self.stack.pop() { + for n in &node.floating_nodes { + self.stack.push(&n); + } + for n in &node.nodes { + self.stack.push(&n); + } + Some(node) + } else { + None + } + } +} + +pub fn get_tree() -> Node { + let output = proc::Command::new("swaymsg") + .arg("-t") + .arg("get_tree") + .output() + .expect("Error running swaymsg!"); + let result = serde_json::from_str( + String::from_utf8(output.stdout) + .expect("Wrong string data!") + .as_str(), + ); + + match result { + Ok(node) => node, + Err(e) => { + eprintln!("Error: {}", e); + panic!() + } + } +} + +#[test] +fn test_get_tree() { + let tree = get_tree(); + + println!("Those IDs are in get_tree():"); + for n in tree.iter() { + println!(" id: {}, type: {:?}", n.id, n.r#type); + } +} diff --git a/src/lib.rs b/src/lib.rs index f477dd4..71ce250 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,217 +1,2 @@ -extern crate serde; -extern crate serde_json; - -use serde::Deserialize; - -use std::process as proc; - -pub type Id = u32; -pub type Dim = u16; -pub type Pid = u16; - -#[derive(Deserialize)] -#[allow(dead_code)] -pub struct Rect { - x: Dim, - y: Dim, - width: Dim, - height: Dim, -} - -// TODO: Maybe there are more? -#[derive(Deserialize)] -pub enum Border { - #[serde(rename = "none")] - None, - #[serde(rename = "pixel")] - Pixel, - #[serde(rename = "csd")] - Csd, -} - -// TODO: Maybe there are more? -#[derive(Deserialize)] -pub enum Layout { - #[serde(rename = "splith")] - SplitH, - #[serde(rename = "splitv")] - SplitV, - #[serde(rename = "tabbed")] - Tabbed, - #[serde(rename = "stacked")] - Stacked, - #[serde(rename = "output")] - Output, - #[serde(rename = "none")] - None, -} - -#[derive(Deserialize)] -pub enum Orientation { - #[serde(rename = "horizontal")] - Horizontal, - #[serde(rename = "vertical")] - Vertical, - #[serde(rename = "none")] - None, -} - -#[derive(Deserialize, PartialEq)] -pub enum NodeType { - #[serde(rename = "root")] - Root, - #[serde(rename = "workspace")] - Workspace, - #[serde(rename = "output")] - Output, - #[serde(rename = "con")] - Con, - #[serde(rename = "floating_con")] - FloatingCon, -} - -#[derive(Deserialize)] -pub enum ShellType { - #[serde(rename = "xdg_shell")] - XdgShell, - #[serde(rename = "xwayland")] - XWayland, -} - -#[derive(Deserialize)] -#[allow(dead_code)] -pub struct Node { - id: Id, - name: Option, - rect: Rect, - focused: bool, - focus: Vec, - border: Border, - current_border_width: Dim, - layout: Layout, - orientation: Orientation, - percent: Option, - window_rect: Rect, - deco_rect: Rect, - geometry: Rect, - window: Option, - urgent: bool, - marks: Vec, - fullscreen_mode: u8, // TODO: actually, it's 0 or 1, i.e., a bool - nodes: Vec, - floating_nodes: Vec, - sticky: bool, - r#type: NodeType, - app_id: Option, - visible: Option, - max_render_time: Option, - pid: Option, - shell: Option, -} - -impl Node { - fn iter(&self) -> PreOrderNodeIter { - PreOrderNodeIter::new(self) - } -} - -struct PreOrderNodeIter<'a> { - stack: Vec<&'a Node>, -} - -impl<'a> PreOrderNodeIter<'a> { - fn new(node: &'a Node) -> PreOrderNodeIter { - PreOrderNodeIter { stack: vec![node] } - } -} - -impl<'a> Iterator for PreOrderNodeIter<'a> { - type Item = &'a Node; - - fn next(&mut self) -> Option { - if let Some(node) = self.stack.pop() { - for n in &node.nodes { - self.stack.push(&n); - } - for n in &node.floating_nodes { - self.stack.push(&n); - } - Some(node) - } else { - None - } - } -} - -pub fn get_tree() -> Node { - let output = proc::Command::new("swaymsg") - .arg("-t") - .arg("get_tree") - .output() - .expect("Error running swaymsg!"); - let result = serde_json::from_str( - String::from_utf8(output.stdout) - .expect("Wrong string data!") - .as_str(), - ); - - match result { - Ok(node) => node, - Err(e) => { - eprintln!("Error: {}", e); - panic!() - } - } -} - -#[allow(dead_code)] -pub struct Con<'a> { - name: &'a str, - id: Id, - app_id: Option<&'a str>, -} - -impl<'a> std::fmt::Display for Con<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - write!(f, "{} — {}", self.app_id.unwrap_or(""), self.name) - } -} - -/// Gets all cons (aka, application windows) of the tree. -pub fn get_cons<'a>(tree: &'a Node) -> Vec> { - let mut v = vec![]; - for n in tree.iter() { - if n.r#type == NodeType::Con || n.r#type == NodeType::FloatingCon { - v.push(Con { - name: &n - .name - .as_ref() - .expect(format!("Con without name. id = {}", n.id).as_str()), - id: n.id, - app_id: match &n.app_id { - Some(s) => Some(s.as_ref()), - None => None, - }, - }) - } - } - v -} - -#[test] -fn test_get_cons() { - let tree = get_tree(); - - println!("Those IDs are in get_tree():"); - for n in tree.iter() { - println!(" id: {}", n.id); - } - - let cons = get_cons(&tree); - - println!("There are {} cons.", cons.len()); - - for c in cons { - println!("{}", c); - } -} +pub mod con; +pub mod ipc; diff --git a/src/main.rs b/src/main.rs index ab1a21e..1ba096b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,9 @@ +use swayr::con; +use swayr::ipc; + fn main() { - let root_node = swayr::get_tree(); - for con in swayr::get_cons(&root_node) { + let root_node = ipc::get_tree(); + for con in con::get_cons(&root_node) { println!("{}", con); } println!("Yes!")