i3status_rs/blocks/
tea_timer.rs1use super::prelude::*;
42use crate::subprocess::spawn_shell;
43
44use std::time::{Duration, Instant};
45
46#[derive(Deserialize, Debug, SmartDefault)]
47#[serde(deny_unknown_fields, default)]
48pub struct Config {
49 pub format: FormatConfig,
50 pub increment: Option<u64>,
51 pub done_cmd: Option<String>,
52}
53
54pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
55 let mut actions = api.get_actions()?;
56 api.set_default_actions(&[
57 (MouseButton::Left, None, "increment"),
58 (MouseButton::WheelUp, None, "increment"),
59 (MouseButton::WheelDown, None, "decrement"),
60 (MouseButton::Right, None, "reset"),
61 ])?;
62
63 let interval: Seconds = 1.into();
64 let mut timer = interval.timer();
65
66 let format = config
67 .format
68 .with_default(" $icon {$time.duration(hms:true) |}")?;
69
70 let increment = Duration::from_secs(config.increment.unwrap_or(30));
71 let mut timer_end = Instant::now();
72
73 let mut timer_was_active = false;
74
75 loop {
76 let mut widget = Widget::new().with_format(format.clone());
77
78 let remaining_time = timer_end - Instant::now();
79 let is_timer_active = !remaining_time.is_zero();
80
81 if !is_timer_active
82 && timer_was_active
83 && let Some(cmd) = &config.done_cmd
84 {
85 spawn_shell(cmd).error("done_cmd error")?;
86 }
87 timer_was_active = is_timer_active;
88
89 let mut values = map!(
90 "icon" => Value::icon("tea"),
91 );
92
93 if is_timer_active {
94 values.insert("time".into(), Value::duration(remaining_time));
95 let mut seconds = remaining_time.as_secs();
96
97 if format.contains_key("hours") {
98 let hours = seconds / 3_600;
99 values.insert("hours".into(), Value::text(format!("{hours:02}")));
100 seconds %= 3_600;
101 }
102
103 if format.contains_key("minutes") {
104 let minutes = seconds / 60;
105 values.insert("minutes".into(), Value::text(format!("{minutes:02}")));
106 seconds %= 60;
107 }
108
109 values.insert("seconds".into(), Value::text(format!("{seconds:02}")));
110 }
111
112 widget.set_values(values);
113
114 api.set_widget(widget)?;
115
116 select! {
117 _ = timer.tick(), if is_timer_active => (),
118 _ = api.wait_for_update_request() => (),
119 Some(action) = actions.recv() => {
120 let now = Instant::now();
121 match action.as_ref() {
122 "increment" if is_timer_active => timer_end += increment,
123 "increment" => timer_end = now + increment,
124 "decrement" if is_timer_active => timer_end -= increment,
125 "reset" => timer_end = now,
126 _ => (),
127 }
128 }
129 }
130 }
131}