1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
use swayipc::{Connection, WindowChange, EventType};
use std::{env,fs::File, io::{Read, self}, num, fmt::Display};
use regex::Regex;
struct WindowHider {
term: Vec<String>,
regex: Regex,
}
enum Error {
IoError(io::Error),
ParseIntError(num::ParseIntError),
RegexParseError(i32),
SwayIpcError(swayipc::Error)
}
impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Self::IoError(value)
}
}
impl From<num::ParseIntError> for Error {
fn from(value: num::ParseIntError) -> Self {
Self::ParseIntError(value)
}
}
impl From<swayipc::Error> for Error {
fn from(value: swayipc::Error) -> Self {
Self::SwayIpcError(value)
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::IoError(err) => err.fmt(f),
Error::ParseIntError(err) => err.fmt(f),
Error::SwayIpcError(err) => err.fmt(f),
Error::RegexParseError(pid) => write!(f, "Failed to regex match for pid {pid}"),
}
}
}
impl WindowHider {
fn get_parent_term_pid(&self, pid: i32, is_root: bool) -> Result<Option<i32>, Error> {
let mut buf = String::new();
File::open(format!("/proc/{pid}/stat"))?.read_to_string(&mut buf)?;
let Some((_, [name, ppid])) = self.regex.captures(&buf).map(|caps| caps.extract()) else {
return Err(Error::RegexParseError(pid));
};
let ppid = ppid.parse::<i32>()?;
match name {
"init" => Ok(None),
_ if self.term.contains(&name.to_string()) && !is_root => Ok(Some(pid)),
_ => self.get_parent_term_pid(ppid, false)
}
}
fn hide(&self, conn: &mut Connection, pid: i32) -> Result<(), Error> {
let ppid = self.get_parent_term_pid(pid, true)?;
if let Some(ppid) = ppid {
conn.run_command(format!("[pid={ppid}] mark --add {pid}, move scratchpad"))?;
}
Ok(())
}
fn show(&self, conn: &mut Connection, pid: i32) -> Result<(), Error> {
conn.run_command(format!("[con_mark={pid}] move workspace current"))?;
conn.run_command(format!("[con_mark={pid}] floating toggle"))?;
Ok(())
}
}
fn main() -> Result<(), swayipc::Error> {
let mut conn = Connection::new().expect("couldn't connect to sway's socket");
let evs = Connection::new()?.subscribe([EventType::Window])?; // one can't use the same
// connection for events and for
// running commands. :(
let mut args: Vec<String> = env::args().collect();
let nterms = args.len() - 1;
if nterms < 1 {
eprintln!("usage: swhd <list of terminals>");
std::process::exit(-1);
}
let wh = WindowHider {
term: args.drain(1..=nterms).collect(),
regex: Regex::new(r"\d+ \((.*)\) \w (\d+) \d+").unwrap()
};
for item in evs {
if let Ok(item) = item {
match item {
swayipc::Event::Window(win) => {
if win.change == WindowChange::New {
if let Some(pid) = win.container.pid {
match wh.hide(&mut conn, pid) {
Ok(_) => {},
Err(err) => eprintln!("failed to hide terminal {pid}: {err}")
};
}
} else if win.change == WindowChange::Close {
if let Some(pid) = win.container.pid {
match wh.show(&mut conn, pid) {
Ok(_) => {},
Err(err) => eprintln!("failed to show terminal {pid}: {err}")
};
}
}
}
_ => {}
}
}
}
Ok(())
}
|