-
-
Notifications
You must be signed in to change notification settings - Fork 3
/
gobble.rs
149 lines (134 loc) · 4.79 KB
/
gobble.rs
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//Gobble by Sebastien MacDougall-Landry
//License is available at
//https://github.com/EmperorPenguin18/gobble/blob/main/LICENSE
extern crate xcb;
use std::{env, process, time};
use xcb::{x, Connection, Xid};
fn main() -> Result<(), anyhow::Error> {
let mut args: Vec<String> = env::args().collect();
let wayland = env::var_os("WAYLAND_DISPLAY");
//Interpret flags
let mut flag_overlap = false;
let mut flag_version = false;
let mut flag_help = false;
while args.len() > 1 {
if args[1].starts_with('-') {
if args[1].chars().nth(1).unwrap() == 'o' {
flag_overlap = true;
} else if args[1].chars().nth(1).unwrap() == 'v' {
flag_version = true;
} else {
flag_help = true;
}
} else {
break;
}
args.remove(1);
}
let exit_code = if flag_help {
println!("gobble - hide your current window while using an external program");
println!();
println!("USAGE:");
println!(" gobble [OPTIONS] CMD ...");
println!();
println!("OPTIONS:");
println!();
println!(" -h Displays the help message you're seeing now");
println!(" -v Displays the software version");
println!(" -o Uses overlap mode");
println!();
println!("See the manual for more information");
0
} else if flag_version {
println!("gobble v1.3");
println!("See https://github.com/EmperorPenguin18/gobble/releases for more info");
0
} else if wayland.is_none() {
gobble_on_x11(flag_overlap, &args)?
} else {
gobble_on_wayland(&args)?
};
process::exit(exit_code);
}
fn gobble_on_wayland(args: &[String]) -> Result<i32, anyhow::Error> {
Ok(command(args)?.wait()?.code().unwrap_or(1))
}
fn gobble_on_x11(flag_overlap: bool, args: &[String]) -> Result<i32, anyhow::Error> {
let (conn, screen_num) = Connection::connect(None)?;
let parent_window = conn
.wait_for_reply(conn.send_request(&x::GetInputFocus {}))?
.focus();
// ensure child was spawned before we hide the window
let mut child: process::Child;
if args.len() > 1 {
child = command(args)?;
} else {
process::exit(0);
}
// If gobble opens a terminal application, it shouldn't hide
let start = time::Instant::now();
let mut child_window = conn
.wait_for_reply(conn.send_request(&x::GetInputFocus {}))?
.focus();
while parent_window == child_window {
// Check timeout or if child already exited
if start.elapsed().as_secs() == 5 || child.try_wait()?.is_some() {
let exit_code = child.wait()?.code().unwrap_or(1);
process::exit(exit_code);
}
child_window = conn
.wait_for_reply(conn.send_request(&x::GetInputFocus {}))?
.focus();
}
// If parent is nothing then skip
if parent_window.resource_id() == 1 {
return Ok(child.wait()?.code().unwrap_or(1))
}
// Overlap mode
Ok(if flag_overlap {
let translate = conn.wait_for_reply(
conn.send_request(&x::TranslateCoordinates {
src_window: parent_window,
dst_window: conn
.get_setup()
.roots()
.nth(screen_num as usize)
.unwrap()
.root(),
src_x: 0,
src_y: 0,
}),
)?; //Translates relative position to absolute position
let geometry = conn.wait_for_reply(conn.send_request(&x::GetGeometry {
drawable: x::Drawable::Window(parent_window),
}))?;
let values = [
x::ConfigWindow::X(i32::from(translate.dst_x())),
x::ConfigWindow::Y(i32::from(translate.dst_y())),
x::ConfigWindow::Width(geometry.width().into()),
x::ConfigWindow::Height(geometry.height().into()),
];
let cookie = conn.send_request_checked(&x::ConfigureWindow {
window: child_window,
value_list: &values,
});
conn.check_request(cookie)?;
child.wait()?.code().unwrap_or(1)
// Default behaviour
} else {
let unmap_attempt = conn.send_request_checked(&x::UnmapWindow {
window: parent_window,
});
conn.check_request(unmap_attempt)?;
let exit_code = child.wait()?.code().unwrap_or(1);
let map_attempt = conn.send_request_checked(&x::MapWindow {
window: parent_window,
});
conn.check_request(map_attempt)?;
exit_code
})
}
fn command(args: &[String]) -> Result<process::Child, anyhow::Error> {
let child = process::Command::new(&args[1]).args(&args[2..]).spawn()?;
Ok(child)
}