use std::ffi::CString; use wayrs_client::global::*; use wayrs_client::proxy::Proxy; use wayrs_client::EventCtx; use super::*; pub struct RiverInfoProvider { status_manager: ZriverStatusManagerV1, control: ZriverControlV1, output_statuses: Vec, max_tag: u8, seat_status: SeatStatus, } struct OutputStatus { output: WlOutput, status: ZriverOutputStatusV1, focused_tags: u32, urgent_tags: u32, active_tags: u32, layout_name: Option, } struct SeatStatus { _status: ZriverSeatStatusV1, mode: Option, } impl RiverInfoProvider { pub fn bind( conn: &mut Connection, globals: &Globals, config: &WmConfig, ) -> Option { let status_manager: ZriverStatusManagerV1 = globals.bind(conn, 1..=4).ok()?; let wl_seat: WlSeat = globals.bind(conn, ..=5).ok()?; // river supports just one seat let seat_status = status_manager.get_river_seat_status_with_cb(conn, wl_seat, seat_status_cb); if wl_seat.version() >= 5 { wl_seat.release(conn); } Some(Self { status_manager, control: globals.bind(conn, 1).ok()?, output_statuses: Vec::new(), max_tag: config.river.max_tag, seat_status: SeatStatus { _status: seat_status, mode: None, }, }) } } impl WmInfoProvider for RiverInfoProvider { fn new_ouput(&mut self, conn: &mut Connection, output: &Output) { let status = self.status_manager .get_river_output_status_with_cb(conn, output.wl, output_status_cb); self.output_statuses.push(OutputStatus { output: output.wl, status, focused_tags: 0, urgent_tags: 0, active_tags: 0, layout_name: None, }); } fn output_removed(&mut self, conn: &mut Connection, output: &Output) { let index = self .output_statuses .iter() .position(|s| s.output == output.wl) .unwrap(); let output_status = self.output_statuses.swap_remove(index); output_status.status.destroy(conn); } fn get_tags(&self, output: &Output) -> Vec { let Some(status) = self.output_statuses.iter().find(|s| s.output == output.wl) else { return Vec::new(); }; (1..=self.max_tag) .map(|tag| Tag { id: tag as u32, name: tag.to_string(), is_focused: status.focused_tags & (1 << (tag - 1)) != 0, is_active: status.active_tags & (1 << (tag - 1)) != 0, is_urgent: status.urgent_tags & (1 << (tag - 1)) != 0, }) .collect() } fn get_layout_name(&self, output: &Output) -> Option { let status = self .output_statuses .iter() .find(|s| s.output == output.wl)?; status.layout_name.clone() } fn get_mode_name(&self, _output: &Output) -> Option { self.seat_status.mode.clone() } fn click_on_tag( &mut self, conn: &mut Connection, _output: WlOutput, seat: WlSeat, tag_id: u32, btn: PointerBtn, ) { let cmd = match btn { PointerBtn::Left => c"set-focused-tags", PointerBtn::Right => c"toggle-focused-tags", _ => return, }; self.control.add_argument(conn, cmd.to_owned()); self.control.add_argument( conn, CString::new((1u32 << (tag_id - 1)).to_string()).unwrap(), ); self.control .run_command_with_cb(conn, seat, river_command_cb); } fn as_any(&mut self) -> &mut dyn Any { self } } fn output_status_cb(ctx: EventCtx) { let river = ctx.state.shared_state.get_river().unwrap(); let status = river .output_statuses .iter_mut() .find(|s| s.status == ctx.proxy) .unwrap(); let output = status.output; use zriver_output_status_v1::Event; match ctx.event { Event::FocusedTags(tags) => { status.focused_tags = tags; ctx.state.tags_updated(ctx.conn, Some(output)); } Event::ViewTags(tags) => { status.active_tags = tags .chunks_exact(4) .map(|bytes| u32::from_ne_bytes(bytes.try_into().unwrap())) .fold(0, |a, b| a | b); ctx.state.tags_updated(ctx.conn, Some(output)); } Event::UrgentTags(tags) => { status.urgent_tags = tags; ctx.state.tags_updated(ctx.conn, Some(output)); } Event::LayoutName(ln) => { status.layout_name = Some(ln.to_string_lossy().into()); ctx.state.layout_name_updated(ctx.conn, Some(output)); } Event::LayoutNameClear => { status.layout_name = None; ctx.state.layout_name_updated(ctx.conn, Some(output)); } } } fn seat_status_cb(ctx: EventCtx) { if let zriver_seat_status_v1::Event::Mode(mode) = ctx.event { let river = ctx.state.shared_state.get_river().unwrap(); let mode = mode.to_string_lossy().into_owned(); river.seat_status.mode = (mode != "normal").then_some(mode); ctx.state.mode_name_updated(ctx.conn, None); } } fn river_command_cb(ctx: EventCtx) { if let zriver_command_callback_v1::Event::Failure(msg) = ctx.event { ctx.state.set_error(ctx.conn, msg.to_string_lossy()) } }