Skip to content

Commit a9a35c1

Browse files
authored
Support --duration close timer for windows (#884)
1 parent 393f7fa commit a9a35c1

File tree

6 files changed

+69
-12
lines changed

6 files changed

+69
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ All notable changes to eww will be listed here, starting at changes since versio
1919
- Add `EWW_TIME` magic variable (By: Erenoit)
2020
- Add trigonometric functions (`sin`, `cos`, `tan`, `cot`) and degree/radian conversions (`degtorad`, `radtodeg`) (By: end-4)
2121
- Add `substring` function to simplexpr
22+
- Add `--duration` flag to `eww open`
2223

2324
## [0.4.0] (04.09.2022)
2425

Cargo.lock

Lines changed: 5 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/eww/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ simple-signal = "1.1"
5050
unescape = "0.1"
5151

5252
tokio = { version = "1.31.0", features = ["full"] }
53-
futures-core = "0.3.28"
54-
futures-util = "0.3.28"
53+
futures = "0.3.28"
5554
tokio-util = "0.7.8"
5655

5756
sysinfo = "0.29.8"

crates/eww/src/app.rs

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub enum DaemonCommand {
5555
anchor: Option<AnchorPoint>,
5656
screen: Option<MonitorIdentifier>,
5757
should_toggle: bool,
58+
duration: Option<std::time::Duration>,
5859
sender: DaemonResponseSender,
5960
},
6061
CloseWindows {
@@ -114,6 +115,9 @@ pub struct App<B> {
114115
pub app_evt_send: UnboundedSender<DaemonCommand>,
115116
pub script_var_handler: ScriptVarHandlerHandle,
116117

118+
/// Senders that will cancel a windows auto-close timer when started with --duration.
119+
pub window_close_timer_abort_senders: HashMap<String, futures::channel::oneshot::Sender<()>>,
120+
117121
pub paths: EwwPaths,
118122
}
119123

@@ -181,20 +185,27 @@ impl<B: DisplayBackend> App<B> {
181185
if should_toggle && self.open_windows.contains_key(w) {
182186
self.close_window(w)
183187
} else {
184-
self.open_window(w, None, None, None, None)
188+
self.open_window(w, None, None, None, None, None)
185189
}
186190
})
187191
.filter_map(Result::err);
188192
sender.respond_with_error_list(errors)?;
189193
}
190-
DaemonCommand::OpenWindow { window_name, pos, size, anchor, screen: monitor, should_toggle, sender } => {
194+
DaemonCommand::OpenWindow {
195+
window_name,
196+
pos,
197+
size,
198+
anchor,
199+
screen: monitor,
200+
should_toggle,
201+
duration,
202+
sender,
203+
} => {
191204
let is_open = self.open_windows.contains_key(&window_name);
192-
let result = if !is_open {
193-
self.open_window(&window_name, pos, size, monitor, anchor)
194-
} else if should_toggle {
205+
let result = if should_toggle && is_open {
195206
self.close_window(&window_name)
196207
} else {
197-
Ok(())
208+
self.open_window(&window_name, pos, size, monitor, anchor, duration)
198209
};
199210
sender.respond_with_result(result)?;
200211
}
@@ -294,6 +305,9 @@ impl<B: DisplayBackend> App<B> {
294305

295306
/// Close a window and do all the required cleanups in the scope_graph and script_var_handler
296307
fn close_window(&mut self, window_name: &str) -> Result<()> {
308+
if let Some(old_abort_send) = self.window_close_timer_abort_senders.remove(window_name) {
309+
_ = old_abort_send.send(());
310+
}
297311
let eww_window = self
298312
.open_windows
299313
.remove(window_name)
@@ -320,11 +334,13 @@ impl<B: DisplayBackend> App<B> {
320334
size: Option<Coords>,
321335
monitor: Option<MonitorIdentifier>,
322336
anchor: Option<AnchorPoint>,
337+
duration: Option<std::time::Duration>,
323338
) -> Result<()> {
324339
self.failed_windows.remove(window_name);
325340
log::info!("Opening window {}", window_name);
326341

327342
// if an instance of this is already running, close it
343+
// TODO make reopening optional via a --no-reopen flag?
328344
if self.open_windows.contains_key(window_name) {
329345
self.close_window(window_name)?;
330346
}
@@ -380,6 +396,34 @@ impl<B: DisplayBackend> App<B> {
380396
}
381397
}));
382398

399+
if let Some(duration) = duration {
400+
let app_evt_sender = self.app_evt_send.clone();
401+
let window_name = window_name.to_string();
402+
403+
let (abort_send, abort_recv) = futures::channel::oneshot::channel();
404+
405+
glib::MainContext::default().spawn_local({
406+
let window_name = window_name.clone();
407+
async move {
408+
tokio::select! {
409+
_ = glib::timeout_future(duration) => {
410+
let (response_sender, mut response_recv) = daemon_response::create_pair();
411+
let command = DaemonCommand::CloseWindows { windows: vec![window_name], sender: response_sender };
412+
if let Err(err) = app_evt_sender.send(command) {
413+
log::error!("Error sending close window command to daemon after gtk window destroy event: {}", err);
414+
}
415+
_ = response_recv.recv().await;
416+
}
417+
_ = abort_recv => {}
418+
}
419+
}
420+
});
421+
422+
if let Some(old_abort_send) = self.window_close_timer_abort_senders.insert(window_name, abort_send) {
423+
_ = old_abort_send.send(());
424+
}
425+
}
426+
383427
self.open_windows.insert(window_name.to_string(), eww_window);
384428
};
385429

@@ -407,7 +451,7 @@ impl<B: DisplayBackend> App<B> {
407451
let window_names: Vec<String> =
408452
self.open_windows.keys().cloned().chain(self.failed_windows.iter().cloned()).dedup().collect();
409453
for window_name in &window_names {
410-
self.open_window(window_name, None, None, None, None)?;
454+
self.open_window(window_name, None, None, None, None, None)?;
411455
}
412456
Ok(())
413457
}

crates/eww/src/opts.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ pub enum ActionWithServer {
120120
/// If the window is already open, close it instead
121121
#[arg(long = "toggle")]
122122
should_toggle: bool,
123+
124+
/// Automatically close the window after a specified amount of time, i.e.: 1s
125+
#[arg(long, value_parser=parse_duration)]
126+
duration: Option<std::time::Duration>,
123127
},
124128

125129
/// Open multiple windows at once.
@@ -218,14 +222,15 @@ impl ActionWithServer {
218222
ActionWithServer::OpenMany { windows, should_toggle } => {
219223
return with_response_channel(|sender| app::DaemonCommand::OpenMany { windows, should_toggle, sender });
220224
}
221-
ActionWithServer::OpenWindow { window_name, pos, size, screen, anchor, should_toggle } => {
225+
ActionWithServer::OpenWindow { window_name, pos, size, screen, anchor, should_toggle, duration } => {
222226
return with_response_channel(|sender| app::DaemonCommand::OpenWindow {
223227
window_name,
224228
pos,
225229
size,
226230
anchor,
227231
screen,
228232
should_toggle,
233+
duration,
229234
sender,
230235
})
231236
}
@@ -254,3 +259,7 @@ where
254259
let (sender, recv) = daemon_response::create_pair();
255260
(f(sender), Some(recv))
256261
}
262+
263+
fn parse_duration(s: &str) -> Result<std::time::Duration, simplexpr::dynval::ConversionError> {
264+
DynVal::from_string(s.to_owned()).as_duration()
265+
}

crates/eww/src/server.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ pub fn initialize_server<B: DisplayBackend>(
8484
css_provider: gtk::CssProvider::new(),
8585
script_var_handler,
8686
app_evt_send: ui_send.clone(),
87+
window_close_timer_abort_senders: HashMap::new(),
8788
paths,
8889
};
8990

0 commit comments

Comments
 (0)