diff --git a/Cargo.lock b/Cargo.lock index 90d8a41..1c51260 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -571,7 +571,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cosmic-client-toolkit" version = "0.1.0" -source = "git+https://github.com/pop-os//cosmic-protocols#d218c76b58c7a3b20dd5e7943f93fc306a1b81b8" +source = "git+https://github.com/pop-os//cosmic-protocols#178eb0b14a0e5c192f64f6dee6c40341a8e5ee51" dependencies = [ "cosmic-protocols", "libc", @@ -583,7 +583,7 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.1.0" -source = "git+https://github.com/pop-os//cosmic-protocols#d218c76b58c7a3b20dd5e7943f93fc306a1b81b8" +source = "git+https://github.com/pop-os//cosmic-protocols#178eb0b14a0e5c192f64f6dee6c40341a8e5ee51" dependencies = [ "bitflags 2.6.0", "wayland-backend", @@ -1485,7 +1485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] diff --git a/plugins/src/cosmic_toplevel/mod.rs b/plugins/src/cosmic_toplevel/mod.rs index 38b2dfd..f299697 100644 --- a/plugins/src/cosmic_toplevel/mod.rs +++ b/plugins/src/cosmic_toplevel/mod.rs @@ -2,11 +2,10 @@ mod toplevel_handler; use cctk::cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::State; use cctk::wayland_client::Proxy; -use cctk::{cosmic_protocols, sctk::reexports::calloop, toplevel_info::ToplevelInfo}; -use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1; +use cctk::{sctk::reexports::calloop, toplevel_info::ToplevelInfo}; use fde::DesktopEntry; use freedesktop_desktop_entry as fde; -use toplevel_handler::TopLevelsUpdate; +use toplevel_handler::ToplevelUpdate; use tracing::{debug, error, info, warn}; use crate::desktop_entries::utils::{get_description, is_session_cosmic}; @@ -70,26 +69,33 @@ pub async fn main() { next_event = toplevel_rx.next(); next_request = second_to_next_request; - for (handle, info) in updates { - match info { - Some(info) => { - if let Some(pos) = app.toplevels.iter().position(|t| t.0 == handle) { + for update in updates { + match update { + ToplevelUpdate::Info(info) => { + if let Some(pos) = app + .toplevels + .iter() + .position(|t| t.foreign_toplevel == info.foreign_toplevel) + { if info.state.contains(&State::Activated) { app.toplevels.remove(pos); - app.toplevels.push((handle, Box::new(info))); + app.toplevels.push(Box::new(info)); } else { - app.toplevels[pos].1 = Box::new(info); + app.toplevels[pos] = Box::new(info); } } else { - app.toplevels.push((handle, Box::new(info))); + app.toplevels.push(Box::new(info)); } } - // no info means remove - None => { - if let Some(pos) = app.toplevels.iter().position(|t| t.0 == handle) { + ToplevelUpdate::Remove(foreign_toplevel) => { + if let Some(pos) = app + .toplevels + .iter() + .position(|t| t.foreign_toplevel == foreign_toplevel) + { app.toplevels.remove(pos); // ignore requests for this id until after the next search - app.ids_to_ignore.push(handle.id().protocol_id()); + app.ids_to_ignore.push(foreign_toplevel.id().protocol_id()); } else { warn!("no toplevel to remove"); } @@ -106,13 +112,13 @@ struct App { locales: Vec, desktop_entries: Vec>, ids_to_ignore: Vec, - toplevels: Vec<(ZcosmicToplevelHandleV1, Box)>, + toplevels: Vec>, 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)); @@ -144,8 +150,8 @@ impl App { return; } if let Some(handle) = self.toplevels.iter().find_map(|t| { - if t.0.id().protocol_id() == id { - Some(t.0.clone()) + if t.foreign_toplevel.id().protocol_id() == id { + Some(t.foreign_toplevel.clone()) } else { None } @@ -160,8 +166,8 @@ impl App { return; } if let Some(handle) = self.toplevels.iter().find_map(|t| { - if t.0.id().protocol_id() == id { - Some(t.0.clone()) + if t.foreign_toplevel.id().protocol_id() == id { + Some(t.foreign_toplevel.clone()) } else { None } @@ -173,7 +179,7 @@ impl App { async fn search(&mut self, query: &str) { let query = query.to_ascii_lowercase(); - for (handle, info) in self.toplevels.iter().rev() { + for info in self.toplevels.iter().rev() { let entry = if query.is_empty() { fde::matching::get_best_match( &[&info.app_id, &info.title], @@ -214,8 +220,8 @@ impl App { let response = PluginResponse::Append(PluginSearchResult { // XXX protocol id may be re-used later - id: handle.id().protocol_id(), - window: Some((0, handle.id().clone().protocol_id())), + id: info.foreign_toplevel.id().protocol_id(), + window: Some((0, info.foreign_toplevel.id().protocol_id())), description: info.title.clone(), name: get_description(de, &self.locales), icon: Some(IconSource::Name(icon_name)), diff --git a/plugins/src/cosmic_toplevel/toplevel_handler.rs b/plugins/src/cosmic_toplevel/toplevel_handler.rs index b0c1f0a..fc08d4d 100644 --- a/plugins/src/cosmic_toplevel/toplevel_handler.rs +++ b/plugins/src/cosmic_toplevel/toplevel_handler.rs @@ -5,6 +5,7 @@ use cctk::{ toplevel_info::{ToplevelInfo, ToplevelInfoHandler, ToplevelInfoState}, toplevel_management::{ToplevelManagerHandler, ToplevelManagerState}, wayland_client::{self, WEnum}, + wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, }; use sctk::{ self, @@ -15,7 +16,7 @@ use sctk::{ }; use cosmic_protocols::{ - toplevel_info::v1::client::zcosmic_toplevel_handle_v1::{self, ZcosmicToplevelHandleV1}, + toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, toplevel_management::v1::client::zcosmic_toplevel_manager_v1, }; use futures::channel::mpsc::UnboundedSender; @@ -25,23 +26,35 @@ use wayland_client::{globals::registry_queue_init, Connection, QueueHandle}; #[derive(Debug, Clone)] pub enum ToplevelAction { - Activate(ZcosmicToplevelHandleV1), - Close(ZcosmicToplevelHandleV1), + Activate(ExtForeignToplevelHandleV1), + Close(ExtForeignToplevelHandleV1), } -pub type TopLevelsUpdate = Vec<( - zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, - Option, -)>; +pub enum ToplevelUpdate { + Info(ToplevelInfo), + Remove(ExtForeignToplevelHandleV1), +} 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, + pending_update: HashSet, +} + +impl AppData { + fn cosmic_toplevel_for_foreign( + &self, + foreign_toplevel: &ExtForeignToplevelHandleV1, + ) -> Option<&ZcosmicToplevelHandleV1> { + self.toplevel_info_state + .info(foreign_toplevel)? + .cosmic_toplevel + .as_ref() + } } impl ProvidesRegistryState for AppData { @@ -103,7 +116,7 @@ impl ToplevelInfoHandler for AppData { &mut self, _conn: &Connection, _qh: &QueueHandle, - toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, + toplevel: &ExtForeignToplevelHandleV1, ) { self.pending_update.insert(toplevel.clone()); } @@ -112,7 +125,7 @@ impl ToplevelInfoHandler for AppData { &mut self, _conn: &Connection, _qh: &QueueHandle, - toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, + toplevel: &ExtForeignToplevelHandleV1, ) { self.pending_update.insert(toplevel.clone()); } @@ -121,20 +134,20 @@ impl ToplevelInfoHandler for AppData { &mut self, _conn: &Connection, _qh: &QueueHandle, - toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, + toplevel: &ExtForeignToplevelHandleV1, ) { 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(), - )); - } + let res = self + .pending_update + .drain() + .map(|handle| match self.toplevel_info_state.info(&handle) { + Some(info) => ToplevelUpdate::Info(info.clone()), + None => ToplevelUpdate::Remove(handle), + }) + .collect(); if let Err(err) = self.tx.unbounded_send(res) { warn!("{err}"); @@ -143,7 +156,7 @@ impl ToplevelInfoHandler for AppData { } pub(crate) fn toplevel_handler( - tx: UnboundedSender, + tx: UnboundedSender>, rx: calloop::channel::Channel, ) -> anyhow::Result<()> { let conn = Connection::connect_to_env()?; @@ -159,15 +172,18 @@ pub(crate) fn toplevel_handler( calloop::channel::Event::Msg(req) => match req { ToplevelAction::Activate(handle) => { let manager = &state.toplevel_manager_state.manager; - let state = &state.seat_state; // TODO Ashley how to choose the seat in a multi-seat setup? - for s in state.seats() { - manager.activate(&handle, &s); + if let Some(cosmic_toplevel) = state.cosmic_toplevel_for_foreign(&handle) { + for s in state.seat_state.seats() { + manager.activate(cosmic_toplevel, &s); + } } } ToplevelAction::Close(handle) => { let manager = &state.toplevel_manager_state.manager; - manager.close(&handle); + if let Some(cosmic_toplevel) = state.cosmic_toplevel_for_foreign(&handle) { + manager.close(cosmic_toplevel); + } } }, calloop::channel::Event::Closed => {