diff --git a/plugins/src/cosmic_toplevel/mod.rs b/plugins/src/cosmic_toplevel/mod.rs index 84bc421..38b2dfd 100644 --- a/plugins/src/cosmic_toplevel/mod.rs +++ b/plugins/src/cosmic_toplevel/mod.rs @@ -6,6 +6,7 @@ use cctk::{cosmic_protocols, sctk::reexports::calloop, toplevel_info::ToplevelIn use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1; use fde::DesktopEntry; use freedesktop_desktop_entry as fde; +use toplevel_handler::TopLevelsUpdate; use tracing::{debug, error, info, warn}; use crate::desktop_entries::utils::{get_description, is_session_cosmic}; @@ -21,10 +22,9 @@ use pop_launcher::{ }; use std::borrow::Cow; use std::iter; -use std::collections::VecDeque; use tokio::io::{AsyncWrite, AsyncWriteExt}; -use self::toplevel_handler::{toplevel_handler, ToplevelAction, ToplevelEvent}; +use self::toplevel_handler::{toplevel_handler, ToplevelAction}; pub async fn main() { let mut tx = async_stdout(); @@ -66,40 +66,33 @@ pub async fn main() { } }; } - Either::Right((Some(event), second_to_next_request)) => { + Either::Right((Some(updates), second_to_next_request)) => { next_event = toplevel_rx.next(); next_request = second_to_next_request; - match event { - ToplevelEvent::Add(handle, info) => { - debug!("add {}", &info.app_id); - app.toplevels.retain(|t| t.0 != handle); - app.toplevels.push_front((handle, info)); - } - ToplevelEvent::Remove(handle) => { - if let Some(pos) = app.toplevels.iter().position(|t| t.0 == handle) { - app.toplevels.remove(pos); - // ignore requests for this id until after the next search - app.ids_to_ignore.push(handle.id().protocol_id()); - } else { - warn!("ToplevelEvent::Remove, no toplevel found"); - } - } - ToplevelEvent::Update(handle, info) => { - debug!("Update {}", &info.app_id); - debug!("Update {:?}", &info.state); - - if let Some(pos) = app.toplevels.iter().position(|t| t.0 == handle) { - if info.state.contains(&State::Activated) { - debug!("Update {:?}: push front", &info.app_id); - app.toplevels.remove(pos); - app.toplevels.push_front((handle, info)); + for (handle, info) in updates { + match info { + Some(info) => { + if let Some(pos) = app.toplevels.iter().position(|t| t.0 == handle) { + if info.state.contains(&State::Activated) { + app.toplevels.remove(pos); + app.toplevels.push((handle, Box::new(info))); + } else { + app.toplevels[pos].1 = Box::new(info); + } } else { - app.toplevels[pos].1 = info; + app.toplevels.push((handle, Box::new(info))); + } + } + // no info means remove + None => { + if let Some(pos) = app.toplevels.iter().position(|t| t.0 == handle) { + app.toplevels.remove(pos); + // ignore requests for this id until after the next search + app.ids_to_ignore.push(handle.id().protocol_id()); + } else { + warn!("no toplevel to remove"); } - } else { - warn!("ToplevelEvent::Update, no toplevel found"); - app.toplevels.push_front((handle, info)); } } } @@ -113,14 +106,13 @@ struct App { locales: Vec, desktop_entries: Vec>, ids_to_ignore: Vec, - // XXX: use LinkedList, and Box the tuple because it will be re ordered a lot? - toplevels: VecDeque<(ZcosmicToplevelHandleV1, ToplevelInfo)>, + toplevels: Vec<(ZcosmicToplevelHandleV1, Box)>, calloop_tx: calloop::channel::Sender, tx: W, } impl App { - fn new(tx: W) -> (Self, mpsc::UnboundedReceiver) { + fn new(tx: W) -> (Self, mpsc::UnboundedReceiver) { let (toplevels_tx, toplevel_rx) = mpsc::unbounded(); let (calloop_tx, calloop_rx) = calloop::channel::channel(); let _handle = std::thread::spawn(move || toplevel_handler(toplevels_tx, calloop_rx)); @@ -138,7 +130,7 @@ impl App { locales, desktop_entries, ids_to_ignore: Vec::new(), - toplevels: VecDeque::new(), + toplevels: Vec::new(), calloop_tx, tx, }, @@ -181,7 +173,7 @@ impl App { async fn search(&mut self, query: &str) { let query = query.to_ascii_lowercase(); - for (handle, info) in &self.toplevels { + for (handle, info) in self.toplevels.iter().rev() { let entry = if query.is_empty() { fde::matching::get_best_match( &[&info.app_id, &info.title], diff --git a/plugins/src/cosmic_toplevel/toplevel_handler.rs b/plugins/src/cosmic_toplevel/toplevel_handler.rs index e7d29ef..b0c1f0a 100644 --- a/plugins/src/cosmic_toplevel/toplevel_handler.rs +++ b/plugins/src/cosmic_toplevel/toplevel_handler.rs @@ -1,8 +1,10 @@ +use std::collections::HashSet; + use cctk::{ cosmic_protocols, toplevel_info::{ToplevelInfo, ToplevelInfoHandler, ToplevelInfoState}, toplevel_management::{ToplevelManagerHandler, ToplevelManagerState}, - wayland_client::{self, protocol::wl_output::WlOutput, WEnum}, + wayland_client::{self, WEnum}, }; use sctk::{ self, @@ -15,10 +17,10 @@ use sctk::{ use cosmic_protocols::{ toplevel_info::v1::client::zcosmic_toplevel_handle_v1::{self, ZcosmicToplevelHandleV1}, toplevel_management::v1::client::zcosmic_toplevel_manager_v1, - workspace::v1::server::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1, }; use futures::channel::mpsc::UnboundedSender; use sctk::registry::{ProvidesRegistryState, RegistryState}; +use tracing::warn; use wayland_client::{globals::registry_queue_init, Connection, QueueHandle}; #[derive(Debug, Clone)] @@ -27,31 +29,19 @@ pub enum ToplevelAction { Close(ZcosmicToplevelHandleV1), } -#[derive(Debug, Clone)] -pub enum ToplevelEvent { - Add(ZcosmicToplevelHandleV1, ToplevelInfo), - Remove(ZcosmicToplevelHandleV1), - Update(ZcosmicToplevelHandleV1, ToplevelInfo), -} - -#[allow(dead_code)] -#[derive(Debug, Clone)] -pub struct Toplevel { - pub name: String, - pub app_id: String, - pub toplevel_handle: ZcosmicToplevelHandleV1, - pub states: Vec, - pub output: Option, - pub workspace: Option, -} +pub type TopLevelsUpdate = Vec<( + zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, + Option, +)>; struct AppData { exit: bool, - tx: UnboundedSender, + tx: UnboundedSender, registry_state: RegistryState, toplevel_info_state: ToplevelInfoState, toplevel_manager_state: ToplevelManagerState, seat_state: SeatState, + pending_update: HashSet, } impl ProvidesRegistryState for AppData { @@ -115,11 +105,7 @@ impl ToplevelInfoHandler for AppData { _qh: &QueueHandle, toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, ) { - if let Some(info) = self.toplevel_info_state.info(toplevel) { - let _ = self - .tx - .unbounded_send(ToplevelEvent::Add(toplevel.clone(), info.clone())); - } + self.pending_update.insert(toplevel.clone()); } fn update_toplevel( @@ -128,11 +114,7 @@ impl ToplevelInfoHandler for AppData { _qh: &QueueHandle, toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, ) { - if let Some(info) = self.toplevel_info_state.info(toplevel) { - let _ = self - .tx - .unbounded_send(ToplevelEvent::Update(toplevel.clone(), info.clone())); - } + self.pending_update.insert(toplevel.clone()); } fn toplevel_closed( @@ -141,14 +123,27 @@ impl ToplevelInfoHandler for AppData { _qh: &QueueHandle, toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, ) { - let _ = self - .tx - .unbounded_send(ToplevelEvent::Remove(toplevel.clone())); + self.pending_update.insert(toplevel.clone()); + } + + fn info_done(&mut self, _conn: &Connection, _qh: &QueueHandle) { + let mut res = Vec::with_capacity(self.pending_update.len()); + + for toplevel_handle in self.pending_update.drain() { + res.push(( + toplevel_handle.clone(), + self.toplevel_info_state.info(&toplevel_handle).cloned(), + )); + } + + if let Err(err) = self.tx.unbounded_send(res) { + warn!("{err}"); + } } } pub(crate) fn toplevel_handler( - tx: UnboundedSender, + tx: UnboundedSender, rx: calloop::channel::Channel, ) -> anyhow::Result<()> { let conn = Connection::connect_to_env()?; @@ -188,6 +183,7 @@ pub(crate) fn toplevel_handler( toplevel_info_state: ToplevelInfoState::new(®istry_state, &qh), toplevel_manager_state: ToplevelManagerState::new(®istry_state, &qh), registry_state, + pending_update: HashSet::new(), }; loop {