use std::any::Any; use std::ffi::CString; use wayrs_client::connection::Connection; use wayrs_client::global::*; use super::*; use crate::protocol::*; use crate::state::State; pub(super) struct RiverInfoProvider { status_manager: ZriverStatusManagerV1, control: ZriverControlV1, output_statuses: Vec, callback: WmInfoCallback, } struct OutputStatus { output: WlOutput, status: ZriverOutputStatusV1, focused_tags: u32, urgent_tags: u32, active_tags: u32, layout_name: Option, } impl RiverInfoProvider { pub(super) fn bind( conn: &mut Connection, globals: &Globals, callback: WmInfoCallback, ) -> Option { Some(Self { status_manager: globals.bind(conn, 1..=4).ok()?, control: globals.bind(conn, 1..=1).ok()?, output_statuses: Vec::new(), callback, }) } } impl WmInfoProvider for RiverInfoProvider { fn as_any(&mut self) -> &mut dyn Any { self } fn new_outut(&mut self, conn: &mut Connection, output: WlOutput) { let status = self.status_manager .get_river_output_status_with_cb(conn, output, output_status_cb); self.output_statuses.push(OutputStatus { output, status, focused_tags: 0, urgent_tags: 0, active_tags: 0, layout_name: None, }); } fn output_removed(&mut self, conn: &mut Connection, output: WlOutput) { let index = self .output_statuses .iter() .position(|s| s.output == output) .unwrap(); let output_status = self.output_statuses.swap_remove(index); output_status.status.destroy(conn); } fn left_click_on_tag( &mut self, conn: &mut Connection, _: WlOutput, seat: WlSeat, tag: &str, ) { let tag: u32 = tag.parse().unwrap(); self.control .add_argument(conn, CString::new("set-focused-tags").unwrap()); self.control .add_argument(conn, CString::new((1u32 << (tag - 1)).to_string()).unwrap()); self.control .run_command_with_cb(conn, seat, river_command_cb); } fn right_click_on_tag( &mut self, conn: &mut Connection, _: WlOutput, seat: WlSeat, tag: &str, ) { let tag: u32 = tag.parse().unwrap(); self.control .add_argument(conn, CString::new("toggle-focused-tags").unwrap()); self.control .add_argument(conn, CString::new((1u32 << (tag - 1)).to_string()).unwrap()); self.control .run_command_with_cb(conn, seat, river_command_cb); } } fn output_status_cb( conn: &mut Connection, state: &mut State, output_status: ZriverOutputStatusV1, event: zriver_output_status_v1::Event, ) { let river: &mut RiverInfoProvider = state .shared_state .wm_info_provider .as_mut() .unwrap() .as_any() .downcast_mut() .unwrap(); let status = river .output_statuses .iter_mut() .find(|s| s.status == output_status) .unwrap(); use zriver_output_status_v1::Event; match event { Event::FocusedTags(tags) => status.focused_tags = tags, 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); } Event::UrgentTags(tags) => status.urgent_tags = tags, Event::LayoutName(ln) => status.layout_name = Some(ln.to_string_lossy().into()), Event::LayoutNameClear => status.layout_name = None, } let info = WmInfo { layout_name: status.layout_name.clone(), tags: (1..10) .map(|tag| Tag { 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(), }; let output = status.output; (river.callback)(conn, state, output, info); } fn river_command_cb( conn: &mut Connection, state: &mut State, _: ZriverCommandCallbackV1, event: zriver_command_callback_v1::Event, ) { if let zriver_command_callback_v1::Event::Failure(msg) = event { state.set_error(conn, msg.to_string_lossy()) } }