i3status_rs/blocks/focused_window/
wlr_toplevel_management.rs1use 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}