|
|
@ -5,16 +5,39 @@ use std::collections::HashMap; |
|
|
|
use std::fmt; |
|
|
|
use std::fmt; |
|
|
|
use std::os::unix::net::UnixStream; |
|
|
|
use std::os::unix::net::UnixStream; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn get_tree() -> ipc::Node { |
|
|
|
|
|
|
|
let output = util::swaymsg(vec!["-t", "get_tree"]); |
|
|
|
|
|
|
|
let result = serde_json::from_str(output.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); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
#[derive(Debug)] |
|
|
|
pub struct Window<'a> { |
|
|
|
pub struct Window<'a> { |
|
|
|
node: &'a ipc::Node, |
|
|
|
node: &'a ipc::Node, |
|
|
|
workspace: &'a ipc::Node, |
|
|
|
workspace: &'a ipc::Node, |
|
|
|
win_props: Option<ipc::WindowProps>, |
|
|
|
con_props: Option<ipc::ConProps>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl Window<'_> { |
|
|
|
impl Window<'_> { |
|
|
|
pub fn get_id(&self) -> ipc::Id { |
|
|
|
pub fn get_id(&self) -> &ipc::Id { |
|
|
|
self.node.id |
|
|
|
&self.node.id |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn get_app_name(&self) -> &str { |
|
|
|
pub fn get_app_name(&self) -> &str { |
|
|
@ -38,7 +61,7 @@ impl Window<'_> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl PartialEq for Window<'_> { |
|
|
|
impl PartialEq for Window<'_> { |
|
|
|
fn eq(&self, other: &Window) -> bool { |
|
|
|
fn eq(&self, other: &Self) -> bool { |
|
|
|
self.get_id() == other.get_id() |
|
|
|
self.get_id() == other.get_id() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -59,9 +82,9 @@ impl Ord for Window<'_> { |
|
|
|
std::cmp::Ordering::Greater |
|
|
|
std::cmp::Ordering::Greater |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
let lru_a = |
|
|
|
let lru_a = |
|
|
|
self.win_props.as_ref().map_or(0, |wp| wp.last_focus_time); |
|
|
|
self.con_props.as_ref().map_or(0, |wp| wp.last_focus_time); |
|
|
|
let lru_b = |
|
|
|
let lru_b = |
|
|
|
other.win_props.as_ref().map_or(0, |wp| wp.last_focus_time); |
|
|
|
other.con_props.as_ref().map_or(0, |wp| wp.last_focus_time); |
|
|
|
lru_a.cmp(&lru_b).reverse() |
|
|
|
lru_a.cmp(&lru_b).reverse() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -95,15 +118,15 @@ impl<'a> fmt::Display for Window<'a> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn build_windows( |
|
|
|
fn build_windows( |
|
|
|
tree: &ipc::Node, |
|
|
|
root: &ipc::Node, |
|
|
|
mut win_props: HashMap<ipc::Id, ipc::WindowProps>, |
|
|
|
mut con_props: HashMap<ipc::Id, ipc::ConProps>, |
|
|
|
) -> Vec<Window> { |
|
|
|
) -> Vec<Window> { |
|
|
|
let mut v = vec![]; |
|
|
|
let mut v = vec![]; |
|
|
|
for workspace in tree.workspaces() { |
|
|
|
for workspace in root.workspaces() { |
|
|
|
for n in workspace.windows() { |
|
|
|
for n in workspace.windows() { |
|
|
|
v.push(Window { |
|
|
|
v.push(Window { |
|
|
|
node: &n, |
|
|
|
node: &n, |
|
|
|
win_props: win_props.remove(&n.id), |
|
|
|
con_props: con_props.remove(&n.id), |
|
|
|
workspace: &workspace, |
|
|
|
workspace: &workspace, |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
@ -111,8 +134,37 @@ fn build_windows( |
|
|
|
v |
|
|
|
v |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn get_window_props( |
|
|
|
fn build_workspaces( |
|
|
|
) -> Result<HashMap<ipc::Id, ipc::WindowProps>, serde_json::Error> { |
|
|
|
root: &ipc::Node, |
|
|
|
|
|
|
|
mut con_props: HashMap<ipc::Id, ipc::ConProps>, |
|
|
|
|
|
|
|
include_scratchpad: bool, |
|
|
|
|
|
|
|
) -> Vec<Workspace> { |
|
|
|
|
|
|
|
let mut v = vec![]; |
|
|
|
|
|
|
|
for workspace in root.workspaces() { |
|
|
|
|
|
|
|
if !include_scratchpad |
|
|
|
|
|
|
|
&& workspace.name.as_ref().unwrap().eq("__i3_scratch") |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
v.push(Workspace { |
|
|
|
|
|
|
|
node: &workspace, |
|
|
|
|
|
|
|
con_props: con_props.remove(&workspace.id), |
|
|
|
|
|
|
|
windows: workspace |
|
|
|
|
|
|
|
.windows() |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.map(|w| Window { |
|
|
|
|
|
|
|
node: &w, |
|
|
|
|
|
|
|
con_props: con_props.remove(&w.id), |
|
|
|
|
|
|
|
workspace: &workspace, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect(), |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
v |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn get_con_props() -> Result<HashMap<ipc::Id, ipc::ConProps>, serde_json::Error> |
|
|
|
|
|
|
|
{ |
|
|
|
if let Ok(sock) = UnixStream::connect(util::get_swayr_socket_path()) { |
|
|
|
if let Ok(sock) = UnixStream::connect(util::get_swayr_socket_path()) { |
|
|
|
serde_json::from_reader(sock) |
|
|
|
serde_json::from_reader(sock) |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -121,16 +173,44 @@ fn get_window_props( |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Gets all application windows of the tree.
|
|
|
|
/// Gets all application windows of the tree.
|
|
|
|
pub fn get_windows(root_node: &ipc::Node) -> Vec<Window> { |
|
|
|
pub fn get_windows(root: &ipc::Node) -> Vec<Window> { |
|
|
|
let win_props = match get_window_props() { |
|
|
|
let con_props = match get_con_props() { |
|
|
|
Ok(win_props) => Some(win_props), |
|
|
|
Ok(con_props) => Some(con_props), |
|
|
|
|
|
|
|
Err(e) => { |
|
|
|
|
|
|
|
eprintln!("Got no con_props: {:?}", e); |
|
|
|
|
|
|
|
None |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
build_windows(root, con_props.unwrap_or_default()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Gets all application windows of the tree.
|
|
|
|
|
|
|
|
pub fn get_workspaces( |
|
|
|
|
|
|
|
root: &ipc::Node, |
|
|
|
|
|
|
|
include_scratchpad: bool, |
|
|
|
|
|
|
|
) -> Vec<Workspace> { |
|
|
|
|
|
|
|
let con_props = match get_con_props() { |
|
|
|
|
|
|
|
Ok(con_props) => Some(con_props), |
|
|
|
Err(e) => { |
|
|
|
Err(e) => { |
|
|
|
eprintln!("Got no win_props: {:?}", e); |
|
|
|
eprintln!("Got no con_props: {:?}", e); |
|
|
|
None |
|
|
|
None |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
build_windows(&root_node, win_props.unwrap_or_default()) |
|
|
|
build_workspaces(root, con_props.unwrap_or_default(), include_scratchpad) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn test_get_windows() { |
|
|
|
|
|
|
|
let root = get_tree(); |
|
|
|
|
|
|
|
let cons = get_windows(&root); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
println!("There are {} cons.", cons.len()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for c in cons { |
|
|
|
|
|
|
|
println!(" {}", c); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn select_window<'a>( |
|
|
|
pub fn select_window<'a>( |
|
|
@ -139,3 +219,66 @@ pub fn select_window<'a>( |
|
|
|
) -> Option<&'a Window<'a>> { |
|
|
|
) -> Option<&'a Window<'a>> { |
|
|
|
util::wofi_select(prompt, windows) |
|
|
|
util::wofi_select(prompt, windows) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn select_workspace<'a>( |
|
|
|
|
|
|
|
prompt: &'a str, |
|
|
|
|
|
|
|
workspaces: &'a [Workspace], |
|
|
|
|
|
|
|
) -> Option<&'a Workspace<'a>> { |
|
|
|
|
|
|
|
util::wofi_select(prompt, workspaces) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub struct Workspace<'a> { |
|
|
|
|
|
|
|
node: &'a ipc::Node, |
|
|
|
|
|
|
|
con_props: Option<ipc::ConProps>, |
|
|
|
|
|
|
|
windows: Vec<Window<'a>>, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Workspace<'_> { |
|
|
|
|
|
|
|
pub fn get_name(&self) -> &str { |
|
|
|
|
|
|
|
self.node.name.as_ref().unwrap() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn get_id(&self) -> &ipc::Id { |
|
|
|
|
|
|
|
&self.node.id |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl PartialEq for Workspace<'_> { |
|
|
|
|
|
|
|
fn eq(&self, other: &Self) -> bool { |
|
|
|
|
|
|
|
self.get_id() == other.get_id() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Eq for Workspace<'_> {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Ord for Workspace<'_> { |
|
|
|
|
|
|
|
fn cmp(&self, other: &Self) -> cmp::Ordering { |
|
|
|
|
|
|
|
if self == other { |
|
|
|
|
|
|
|
cmp::Ordering::Equal |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
let lru_a = |
|
|
|
|
|
|
|
self.con_props.as_ref().map_or(0, |wp| wp.last_focus_time); |
|
|
|
|
|
|
|
let lru_b = |
|
|
|
|
|
|
|
other.con_props.as_ref().map_or(0, |wp| wp.last_focus_time); |
|
|
|
|
|
|
|
lru_a.cmp(&lru_b).reverse() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl PartialOrd for Workspace<'_> { |
|
|
|
|
|
|
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { |
|
|
|
|
|
|
|
Some(self.cmp(other)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<'a> fmt::Display for Workspace<'a> { |
|
|
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { |
|
|
|
|
|
|
|
write!( |
|
|
|
|
|
|
|
f, |
|
|
|
|
|
|
|
"<span font_weight=\"bold\">“Workspace {}”</span> \ |
|
|
|
|
|
|
|
<span alpha=\"20000\">id {}</span>", // Almost hide ID!
|
|
|
|
|
|
|
|
self.get_name(), |
|
|
|
|
|
|
|
self.get_id() |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|