use super::manager::ToplevelManagerState; use crate::lock; use crate::unique_id::get_unique_usize; use std::collections::HashSet; use std::sync::{Arc, Mutex}; use tracing::trace; use wayland_client::protocol::wl_output::WlOutput; use wayland_client::protocol::wl_seat::WlSeat; use wayland_client::{Connection, Dispatch, Proxy, QueueHandle}; use wayland_protocols_wlr::foreign_toplevel::v1::client::zwlr_foreign_toplevel_handle_v1::{ Event, ZwlrForeignToplevelHandleV1, }; #[derive(Debug, Clone)] pub struct ToplevelHandle { pub handle: ZwlrForeignToplevelHandleV1, } impl PartialEq for ToplevelHandle { fn eq(&self, other: &Self) -> bool { self.handle == other.handle } } impl ToplevelHandle { pub fn info(&self) -> Option { trace!("Retrieving handle info"); let data = self.handle.data::()?; data.info() } pub fn focus(&self, seat: &WlSeat) { trace!("Activating handle"); self.handle.activate(seat); } } #[derive(Debug, Default)] pub struct ToplevelHandleData { pub inner: Arc>, } impl ToplevelHandleData { fn info(&self) -> Option { lock!(self.inner).current_info.clone() } } #[derive(Debug, Default)] pub struct ToplevelHandleDataInner { initial_done: bool, output: Option, current_info: Option, pending_info: ToplevelInfo, } #[derive(Debug, Clone)] pub struct ToplevelInfo { pub id: usize, pub app_id: String, pub title: String, pub fullscreen: bool, pub focused: bool, } impl Default for ToplevelInfo { fn default() -> Self { Self { id: get_unique_usize(), app_id: String::new(), title: String::new(), fullscreen: false, focused: false, } } } pub trait ToplevelHandleDataExt { fn toplevel_handle_data(&self) -> &ToplevelHandleData; } impl ToplevelHandleDataExt for ToplevelHandleData { fn toplevel_handle_data(&self) -> &ToplevelHandleData { self } } pub trait ToplevelHandleHandler: Sized { fn new_handle(&mut self, conn: &Connection, qh: &QueueHandle, handle: ToplevelHandle); fn update_handle(&mut self, conn: &Connection, qh: &QueueHandle, handle: ToplevelHandle); fn remove_handle(&mut self, conn: &Connection, qh: &QueueHandle, handle: ToplevelHandle); } impl Dispatch for ToplevelManagerState where D: Dispatch + ToplevelHandleHandler, U: ToplevelHandleDataExt, { fn event( state: &mut D, handle: &ZwlrForeignToplevelHandleV1, event: Event, data: &U, conn: &Connection, qh: &QueueHandle, ) { const STATE_ACTIVE: u32 = 2; const STATE_FULLSCREEN: u32 = 3; let data = data.toplevel_handle_data(); trace!("Processing handle event: {event:?}"); match event { Event::Title { title } => { lock!(data.inner).pending_info.title = title; } Event::AppId { app_id } => lock!(data.inner).pending_info.app_id = app_id, Event::State { state } => { // state is received as a `Vec` where every 4 bytes make up a `u32` // the u32 then represents a value in the `State` enum. assert_eq!(state.len() % 4, 0); let state = (0..state.len() / 4) .map(|i| { let slice: [u8; 4] = state[i * 4..i * 4 + 4] .try_into() .expect("Received invalid state length"); u32::from_le_bytes(slice) }) .collect::>(); lock!(data.inner).pending_info.focused = state.contains(&STATE_ACTIVE); lock!(data.inner).pending_info.fullscreen = state.contains(&STATE_FULLSCREEN); } Event::OutputEnter { output } => lock!(data.inner).output = Some(output), Event::OutputLeave { output: _ } => lock!(data.inner).output = None, Event::Closed => state.remove_handle( conn, qh, ToplevelHandle { handle: handle.clone(), }, ), Event::Done => { { let pending_info = lock!(data.inner).pending_info.clone(); lock!(data.inner).current_info = Some(pending_info); } if lock!(data.inner).initial_done { state.update_handle( conn, qh, ToplevelHandle { handle: handle.clone(), }, ); } else { lock!(data.inner).initial_done = true; state.new_handle( conn, qh, ToplevelHandle { handle: handle.clone(), }, ); } } _ => {} } trace!("Event processed"); } }