i3status_rs/blocks/focused_window/
wlr_toplevel_management.rs

1use super::{Backend, Info};
2use crate::blocks::prelude::*;
3
4use wayrs_client::{Connection, EventCtx};
5use wayrs_protocols::wlr_foreign_toplevel_management_unstable_v1::*;
6
7pub(super) struct WlrToplevelManagement {
8    conn: Connection<State>,
9    state: State,
10}
11
12#[derive(Default)]
13struct State {
14    error: Option<Error>,
15    new_title: Option<String>,
16    toplevels: HashMap<ZwlrForeignToplevelHandleV1, Toplevel>,
17    active_toplevel: Option<ZwlrForeignToplevelHandleV1>,
18}
19
20#[derive(Default)]
21struct Toplevel {
22    title: Option<String>,
23    is_active: bool,
24}
25
26impl WlrToplevelManagement {
27    pub(super) async fn new() -> Result<Self> {
28        let mut conn = Connection::connect().error("failed to connect to wayland")?;
29
30        conn.async_roundtrip()
31            .await
32            .error("wayland roundrip failed")?;
33
34        let _: ZwlrForeignToplevelManagerV1 = conn
35            .bind_singleton_with_cb(1..=3, toplevel_manager_cb)
36            .error("unsupported compositor")?;
37
38        Ok(Self {
39            conn,
40            state: default(),
41        })
42    }
43}
44
45#[async_trait]
46impl Backend for WlrToplevelManagement {
47    async fn get_info(&mut self) -> Result<Info> {
48        loop {
49            self.conn.async_flush().await.error("wayland error")?;
50            self.conn.async_recv_events().await.error("wayland error")?;
51            self.conn.dispatch_events(&mut self.state);
52
53            if let Some(err) = self.state.error.take() {
54                return Err(err);
55            }
56
57            if let Some(title) = self.state.new_title.take() {
58                return Ok(Info {
59                    title,
60                    marks: default(),
61                });
62            }
63        }
64    }
65}
66
67fn toplevel_manager_cb(ctx: EventCtx<State, ZwlrForeignToplevelManagerV1>) {
68    use zwlr_foreign_toplevel_manager_v1::Event;
69    match ctx.event {
70        Event::Toplevel(toplevel) => {
71            ctx.state.toplevels.insert(toplevel, default());
72            ctx.conn.set_callback_for(toplevel, toplevel_cb);
73        }
74        Event::Finished => {
75            ctx.state.error = Some(Error::new("unexpected 'finished' event"));
76            ctx.conn.break_dispatch_loop();
77        }
78        _ => (),
79    }
80}
81
82fn toplevel_cb(ctx: EventCtx<State, ZwlrForeignToplevelHandleV1>) {
83    use zwlr_foreign_toplevel_handle_v1::Event;
84
85    let Some(toplevel) = ctx.state.toplevels.get_mut(&ctx.proxy) else {
86        return;
87    };
88
89    match ctx.event {
90        Event::Title(title) => {
91            toplevel.title = Some(String::from_utf8_lossy(title.as_bytes()).into());
92        }
93        Event::State(state) => {
94            toplevel.is_active = state
95                .chunks_exact(4)
96                .map(|b| u32::from_ne_bytes(b.try_into().unwrap()))
97                .any(|s| s == zwlr_foreign_toplevel_handle_v1::State::Activated as u32);
98        }
99        Event::Closed => {
100            if ctx.state.active_toplevel == Some(ctx.proxy) {
101                ctx.state.active_toplevel = None;
102                ctx.state.new_title = Some(default());
103            }
104
105            ctx.proxy.destroy(ctx.conn);
106            ctx.state.toplevels.remove(&ctx.proxy);
107        }
108        Event::Done => {
109            if toplevel.is_active {
110                ctx.state.active_toplevel = Some(ctx.proxy);
111                ctx.state.new_title = Some(toplevel.title.clone().unwrap_or_default());
112            } else if ctx.state.active_toplevel == Some(ctx.proxy) {
113                ctx.state.active_toplevel = None;
114                ctx.state.new_title = Some(default());
115            }
116        }
117        _ => (),
118    }
119}