parent
59fa701ab5
commit
37c85880e7
8 changed files with 245 additions and 232 deletions
@ -1 +0,0 @@ |
||||
- Switch from lazy_static to once_cell once the latter is in stable rust. |
@ -0,0 +1,175 @@ |
||||
// Copyright (C) 2021-2022 Tassilo Horn <tsdh@gnu.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation, either version 3 of the License, or (at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
// more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Basic sway IPC.
|
||||
|
||||
use std::{cell::RefCell, sync::Mutex}; |
||||
|
||||
use once_cell::sync::Lazy; |
||||
use swayipc as s; |
||||
|
||||
static SWAY_IPC_CONNECTION: Lazy<Mutex<RefCell<s::Connection>>> = |
||||
Lazy::new(|| { |
||||
Mutex::new(RefCell::new( |
||||
s::Connection::new().expect("Could not open sway IPC connection."), |
||||
)) |
||||
}); |
||||
|
||||
pub fn get_root_node(include_scratchpad: bool) -> s::Node { |
||||
let mut root = match SWAY_IPC_CONNECTION.lock() { |
||||
Ok(cell) => cell.borrow_mut().get_tree().expect("Couldn't get tree"), |
||||
Err(err) => panic!("{}", err), |
||||
}; |
||||
|
||||
if !include_scratchpad { |
||||
root.nodes.retain(|o| !o.is_scratchpad()); |
||||
} |
||||
root |
||||
} |
||||
|
||||
/// Immutable Node Iterator
|
||||
///
|
||||
/// Iterates nodes in depth-first order, tiled nodes before floating nodes.
|
||||
pub struct NodeIter<'a> { |
||||
stack: Vec<&'a s::Node>, |
||||
} |
||||
|
||||
impl<'a> NodeIter<'a> { |
||||
pub fn new(node: &'a s::Node) -> NodeIter { |
||||
NodeIter { stack: vec![node] } |
||||
} |
||||
} |
||||
|
||||
impl<'a> Iterator for NodeIter<'a> { |
||||
type Item = &'a s::Node; |
||||
|
||||
fn next(&mut self) -> Option<Self::Item> { |
||||
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 |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)] |
||||
pub enum Type { |
||||
Root, |
||||
Output, |
||||
Workspace, |
||||
Container, |
||||
Window, |
||||
} |
||||
|
||||
/// Extension methods for [`swayipc::Node`].
|
||||
pub trait NodeMethods { |
||||
fn iter(&self) -> NodeIter; |
||||
fn get_type(&self) -> Type; |
||||
fn get_app_name(&self) -> &str; |
||||
fn nodes_of_type(&self, t: Type) -> Vec<&s::Node>; |
||||
fn get_name(&self) -> &str; |
||||
fn is_scratchpad(&self) -> bool; |
||||
fn is_floating(&self) -> bool; |
||||
fn is_current(&self) -> bool; |
||||
} |
||||
|
||||
impl NodeMethods for s::Node { |
||||
fn iter(&self) -> NodeIter { |
||||
NodeIter::new(self) |
||||
} |
||||
|
||||
fn get_type(&self) -> Type { |
||||
match self.node_type { |
||||
s::NodeType::Root => Type::Root, |
||||
s::NodeType::Output => Type::Output, |
||||
s::NodeType::Workspace => Type::Workspace, |
||||
s::NodeType::FloatingCon => Type::Window, |
||||
_ => { |
||||
if self.node_type == s::NodeType::Con |
||||
&& self.name.is_none() |
||||
&& self.app_id.is_none() |
||||
&& self.pid.is_none() |
||||
&& self.shell.is_none() |
||||
&& self.window_properties.is_none() |
||||
&& self.layout != s::NodeLayout::None |
||||
{ |
||||
Type::Container |
||||
} else if (self.node_type == s::NodeType::Con |
||||
|| self.node_type == s::NodeType::FloatingCon) |
||||
// Apparently there can be windows without app_id, name,
|
||||
// and window_properties.class, e.g., dolphin-emu-nogui.
|
||||
&& self.pid.is_some() |
||||
// FIXME: While technically correct, old sway versions (up to
|
||||
// at least sway-1.4) don't expose shell in IPC. So comment in
|
||||
// again when all major distros have a recent enough sway
|
||||
// package.
|
||||
//&& self.shell.is_some()
|
||||
{ |
||||
Type::Window |
||||
} else { |
||||
panic!( |
||||
"Don't know type of node with id {} and node_type {:?}\n{:?}", |
||||
self.id, self.node_type, self |
||||
) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn get_name(&self) -> &str { |
||||
if let Some(name) = &self.name { |
||||
name.as_ref() |
||||
} else { |
||||
"<unnamed>" |
||||
} |
||||
} |
||||
|
||||
fn get_app_name(&self) -> &str { |
||||
if let Some(app_id) = &self.app_id { |
||||
app_id |
||||
} else if let Some(wp_class) = self |
||||
.window_properties |
||||
.as_ref() |
||||
.and_then(|wp| wp.class.as_ref()) |
||||
{ |
||||
wp_class |
||||
} else { |
||||
"<unknown_app>" |
||||
} |
||||
} |
||||
|
||||
fn is_scratchpad(&self) -> bool { |
||||
let name = self.get_name(); |
||||
name.eq("__i3") || name.eq("__i3_scratch") |
||||
} |
||||
|
||||
fn nodes_of_type(&self, t: Type) -> Vec<&s::Node> { |
||||
self.iter().filter(|n| n.get_type() == t).collect() |
||||
} |
||||
|
||||
fn is_floating(&self) -> bool { |
||||
self.node_type == s::NodeType::FloatingCon |
||||
} |
||||
|
||||
fn is_current(&self) -> bool { |
||||
self.iter().any(|n| n.focused) |
||||
} |
||||
} |
Loading…
Reference in new issue