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 && timer_was_active {
82 if let Some(cmd) = &config.done_cmd {
83 spawn_shell(cmd).error("done_cmd error")?;
84 }
85 }
86 timer_was_active = is_timer_active;
87
88 let mut values = map!(
89 "icon" => Value::icon("tea"),
90 );
91
92 if is_timer_active {
93 values.insert("time".into(), Value::duration(remaining_time));
94 let mut seconds = remaining_time.as_secs();
95
96 if format.contains_key("hours") {
97 let hours = seconds / 3_600;
98 values.insert("hours".into(), Value::text(format!("{hours:02}")));
99 seconds %= 3_600;
100 }
101
102 if format.contains_key("minutes") {
103 let minutes = seconds / 60;
104 values.insert("minutes".into(), Value::text(format!("{minutes:02}")));
105 seconds %= 60;
106 }
107
108 values.insert("seconds".into(), Value::text(format!("{seconds:02}")));
109 }
110
111 widget.set_values(values);
112
113 api.set_widget(widget)?;
114
115 select! {
116 _ = timer.tick(), if is_timer_active => (),
117 _ = api.wait_for_update_request() => (),
118 Some(action) = actions.recv() => {
119 let now = Instant::now();
120 match action.as_ref() {
121 "increment" if is_timer_active => timer_end += increment,
122 "increment" => timer_end = now + increment,
123 "decrement" if is_timer_active => timer_end -= increment,
124 "reset" => timer_end = now,
125 _ => (),
126 }
127 }
128 }
129 }
130}