i3status_rs/protocol/
i3bar_event.rs1use std::os::unix::io::FromRawFd as _;
2use std::time::Duration;
3
4use serde::Deserialize;
5
6use futures::StreamExt as _;
7use tokio::fs::File;
8use tokio::io::{AsyncBufReadExt as _, BufReader};
9
10use crate::BoxedStream;
11use crate::click::MouseButton;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct I3BarEvent {
15 pub id: usize,
16 pub instance: Option<String>,
17 pub button: MouseButton,
18}
19
20fn unprocessed_events_stream(invert_scrolling: bool) -> BoxedStream<I3BarEvent> {
21 let stdin = unsafe { File::from_raw_fd(0) };
24 let lines = BufReader::new(stdin).lines();
25
26 futures::stream::unfold(lines, move |mut lines| async move {
27 loop {
28 let line = lines.next_line().await.ok().flatten()?;
30 let line = line.trim_start_matches(|c| c != '{');
31 let line = line.trim_end_matches(|c| c != '}');
32
33 if line.is_empty() {
34 continue;
35 }
36
37 #[derive(Deserialize)]
38 struct I3BarEventRaw {
39 instance: Option<String>,
40 button: MouseButton,
41 }
42
43 let event: I3BarEventRaw = match serde_json::from_str(line) {
44 Ok(event) => event,
45 Err(err) => {
46 eprintln!("Failed to deserialize click event.\nData: {line}\nError: {err}");
47 continue;
48 }
49 };
50
51 let (id, instance) = match event.instance {
52 Some(name) => {
53 let (id, instance) = name.split_once(':').unwrap();
54 let instance = if instance.is_empty() {
55 None
56 } else {
57 Some(instance.to_owned())
58 };
59 (id.parse().unwrap(), instance)
60 }
61 None => continue,
62 };
63
64 use MouseButton::*;
65 let button = match (event.button, invert_scrolling) {
66 (WheelUp, false) | (WheelDown, true) => WheelUp,
67 (WheelUp, true) | (WheelDown, false) => WheelDown,
68 (other, _) => other,
69 };
70
71 let event = I3BarEvent {
72 id,
73 instance,
74 button,
75 };
76
77 break Some((event, lines));
78 }
79 })
80 .boxed_local()
81}
82
83pub fn events_stream(
84 invert_scrolling: bool,
85 double_click_delay: Duration,
86) -> BoxedStream<I3BarEvent> {
87 let events = unprocessed_events_stream(invert_scrolling);
88 futures::stream::unfold((events, None), move |(mut events, pending)| async move {
89 if let Some(pending) = pending {
90 return Some((pending, (events, None)));
91 }
92
93 let mut event = events.next().await?;
94
95 if event.button == MouseButton::Left && !double_click_delay.is_zero() {
97 if let Ok(new_event) = tokio::time::timeout(double_click_delay, events.next()).await {
98 let new_event = new_event?;
99 if event == new_event {
100 event.button = MouseButton::DoubleLeft;
101 } else {
102 return Some((event, (events, Some(new_event))));
103 }
104 }
105 }
106
107 Some((event, (events, None)))
108 })
109 .fuse()
110 .boxed_local()
111}