use wayrs_client::global::*; use wayrs_client::object::ObjectId; use wayrs_client::proxy::Proxy; use wayrs_client::Connection; use super::*; use crate::state::State; pub struct ExtWorkspaceUnstable { manager: ZextWorkspaceManagerV1, groups: Vec, known_outputs: Vec, callback: WmInfoCallback, } #[derive(Debug)] struct Group { group_handle: ZextWorkspaceGroupHandleV1, workspaces: Vec, outputs: Vec, } #[derive(Debug)] struct Workspace { workspace_handle: ZextWorkspaceHandleV1, name: Option, is_focused: bool, is_urgent: bool, } impl ExtWorkspaceUnstable { pub fn bind( conn: &mut Connection, globals: &Globals, callback: WmInfoCallback, ) -> Option { Some(Self { manager: globals.bind_with_cb(conn, 1..=1, manager_cb).ok()?, groups: Vec::new(), known_outputs: Vec::new(), callback, }) } } impl ExtWorkspaceUnstable { pub fn new_ouput(&mut self, _: &mut Connection, output: WlOutput) { self.known_outputs.push(output); } pub fn output_removed(&mut self, _: &mut Connection, output: WlOutput) { self.known_outputs.retain(|&o| o != output); for group in &mut self.groups { group.outputs.retain(|&id| id != output.id()); } } pub fn click_on_tag( &mut self, conn: &mut Connection, _: WlOutput, _: WlSeat, tag: &str, btn: PointerBtn, ) { if btn != PointerBtn::Left { return; } let Some(ws) = self .groups .iter() .find_map(|g| g.workspaces.iter().find(|w| w.name.as_deref() == Some(tag))) else { return; }; ws.workspace_handle.activate(conn); self.manager.commit(conn); } } fn manager_cb( conn: &mut Connection, s: &mut State, _: ZextWorkspaceManagerV1, event: zext_workspace_manager_v1::Event, ) { let WmInfoProvider::Ewu(state) = &mut s.shared_state.wm_info_provider else { unreachable!() }; match event { zext_workspace_manager_v1::Event::WorkspaceGroup(group_handle) => { conn.set_callback_for(group_handle, group_cb); state.groups.push(Group { group_handle, workspaces: Vec::new(), outputs: Vec::new(), }); } zext_workspace_manager_v1::Event::Done => { let mut events = Vec::new(); for &output in &state.known_outputs { if let Some(group) = state .groups .iter() .find(|g| g.outputs.contains(&output.id())) { let mut tags: Vec<_> = group .workspaces .iter() .flat_map(|w| { let name = w.name.clone()?; Some(Tag { name, is_focused: w.is_focused, is_active: true, is_urgent: w.is_urgent, }) }) .collect(); tags.sort_unstable_by(|a, b| a.name.cmp(&b.name)); events.push((output, tags)); } } let cb = state.callback; for (output, tags) in events { let info = WmInfo { layout_name: None, tags, }; (cb)(conn, s, output, info); } } zext_workspace_manager_v1::Event::Finished => unreachable!(), } } fn group_cb( conn: &mut Connection, state: &mut State, group_handle: ZextWorkspaceGroupHandleV1, event: zext_workspace_group_handle_v1::Event, ) { let WmInfoProvider::Ewu(state) = &mut state.shared_state.wm_info_provider else { unreachable!() }; let group = state .groups .iter_mut() .find(|g| g.group_handle == group_handle) .unwrap(); match event { zext_workspace_group_handle_v1::Event::OutputEnter(output) => { group.outputs.push(output); } zext_workspace_group_handle_v1::Event::OutputLeave(_) => todo!(), zext_workspace_group_handle_v1::Event::Workspace(workspace_handle) => { conn.set_callback_for(workspace_handle, workspace_cb); group.workspaces.push(Workspace { workspace_handle, name: None, is_focused: false, is_urgent: false, }); } zext_workspace_group_handle_v1::Event::Remove => { todo!(); } } } fn workspace_cb( conn: &mut Connection, state: &mut State, workspace_handle: ZextWorkspaceHandleV1, event: zext_workspace_handle_v1::Event, ) { let WmInfoProvider::Ewu(state) = &mut state.shared_state.wm_info_provider else { unreachable!() }; let group = state .groups .iter_mut() .find(|g| { g.workspaces .iter() .any(|w| w.workspace_handle == workspace_handle) }) .unwrap(); let workspace = group .workspaces .iter_mut() .find(|w| w.workspace_handle == workspace_handle) .unwrap(); match event { zext_workspace_handle_v1::Event::Name(name) => { workspace.name = Some(name.to_string_lossy().into_owned()); } zext_workspace_handle_v1::Event::Coordinates(_) => (), zext_workspace_handle_v1::Event::State(state) => { workspace.is_focused = false; workspace.is_urgent = false; for state in state .chunks_exact(4) .map(|b| u32::from_ne_bytes(b.try_into().unwrap())) { workspace.is_focused |= state == zext_workspace_handle_v1::State::Active as u32; workspace.is_urgent |= state == zext_workspace_handle_v1::State::Urgent as u32; } } zext_workspace_handle_v1::Event::Remove => { group .workspaces .retain(|w| w.workspace_handle != workspace_handle); workspace_handle.destroy(conn); } } }