Update for toplevel-info cctk changes

This commit is contained in:
Ian Douglas Scott 2025-02-13 13:03:13 -08:00 • committed by Michael Murphy
parent 0e01b09ddd
commit 926abeb373
3 changed files with 73 additions and 51 deletions

6
Cargo.lock generated
View file

@ -571,7 +571,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]] [[package]]
name = "cosmic-client-toolkit" name = "cosmic-client-toolkit"
version = "0.1.0" 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 = [ dependencies = [
"cosmic-protocols", "cosmic-protocols",
"libc", "libc",
@ -583,7 +583,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-protocols" name = "cosmic-protocols"
version = "0.1.0" 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 = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"wayland-backend", "wayland-backend",
@ -1485,7 +1485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-targets 0.52.6", "windows-targets 0.48.5",
] ]
[[package]] [[package]]

View file

@ -2,11 +2,10 @@ mod toplevel_handler;
use cctk::cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::State; use cctk::cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::State;
use cctk::wayland_client::Proxy; use cctk::wayland_client::Proxy;
use cctk::{cosmic_protocols, sctk::reexports::calloop, toplevel_info::ToplevelInfo}; use cctk::{sctk::reexports::calloop, toplevel_info::ToplevelInfo};
use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1;
use fde::DesktopEntry; use fde::DesktopEntry;
use freedesktop_desktop_entry as fde; use freedesktop_desktop_entry as fde;
use toplevel_handler::TopLevelsUpdate; use toplevel_handler::ToplevelUpdate;
use tracing::{debug, error, info, warn}; use tracing::{debug, error, info, warn};
use crate::desktop_entries::utils::{get_description, is_session_cosmic}; use crate::desktop_entries::utils::{get_description, is_session_cosmic};
@ -70,26 +69,33 @@ pub async fn main() {
next_event = toplevel_rx.next(); next_event = toplevel_rx.next();
next_request = second_to_next_request; next_request = second_to_next_request;
for (handle, info) in updates { for update in updates {
match info { match update {
Some(info) => { ToplevelUpdate::Info(info) => {
if let Some(pos) = app.toplevels.iter().position(|t| t.0 == handle) { if let Some(pos) = app
.toplevels
.iter()
.position(|t| t.foreign_toplevel == info.foreign_toplevel)
{
if info.state.contains(&State::Activated) { if info.state.contains(&State::Activated) {
app.toplevels.remove(pos); app.toplevels.remove(pos);
app.toplevels.push((handle, Box::new(info))); app.toplevels.push(Box::new(info));
} else { } else {
app.toplevels[pos].1 = Box::new(info); app.toplevels[pos] = Box::new(info);
} }
} else { } else {
app.toplevels.push((handle, Box::new(info))); app.toplevels.push(Box::new(info));
} }
} }
// no info means remove ToplevelUpdate::Remove(foreign_toplevel) => {
None => { if let Some(pos) = app
if let Some(pos) = app.toplevels.iter().position(|t| t.0 == handle) { .toplevels
.iter()
.position(|t| t.foreign_toplevel == foreign_toplevel)
{
app.toplevels.remove(pos); app.toplevels.remove(pos);
// ignore requests for this id until after the next search // 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 { } else {
warn!("no toplevel to remove"); warn!("no toplevel to remove");
} }
@ -106,13 +112,13 @@ struct App<W> {
locales: Vec<String>, locales: Vec<String>,
desktop_entries: Vec<DesktopEntry<'static>>, desktop_entries: Vec<DesktopEntry<'static>>,
ids_to_ignore: Vec<u32>, ids_to_ignore: Vec<u32>,
toplevels: Vec<(ZcosmicToplevelHandleV1, Box<ToplevelInfo>)>, toplevels: Vec<Box<ToplevelInfo>>,
calloop_tx: calloop::channel::Sender<ToplevelAction>, calloop_tx: calloop::channel::Sender<ToplevelAction>,
tx: W, tx: W,
} }
impl<W: AsyncWrite + Unpin> App<W> { impl<W: AsyncWrite + Unpin> App<W> {
fn new(tx: W) -> (Self, mpsc::UnboundedReceiver<TopLevelsUpdate>) { fn new(tx: W) -> (Self, mpsc::UnboundedReceiver<Vec<ToplevelUpdate>>) {
let (toplevels_tx, toplevel_rx) = mpsc::unbounded(); let (toplevels_tx, toplevel_rx) = mpsc::unbounded();
let (calloop_tx, calloop_rx) = calloop::channel::channel(); let (calloop_tx, calloop_rx) = calloop::channel::channel();
let _handle = std::thread::spawn(move || toplevel_handler(toplevels_tx, calloop_rx)); let _handle = std::thread::spawn(move || toplevel_handler(toplevels_tx, calloop_rx));
@ -144,8 +150,8 @@ impl<W: AsyncWrite + Unpin> App<W> {
return; return;
} }
if let Some(handle) = self.toplevels.iter().find_map(|t| { if let Some(handle) = self.toplevels.iter().find_map(|t| {
if t.0.id().protocol_id() == id { if t.foreign_toplevel.id().protocol_id() == id {
Some(t.0.clone()) Some(t.foreign_toplevel.clone())
} else { } else {
None None
} }
@ -160,8 +166,8 @@ impl<W: AsyncWrite + Unpin> App<W> {
return; return;
} }
if let Some(handle) = self.toplevels.iter().find_map(|t| { if let Some(handle) = self.toplevels.iter().find_map(|t| {
if t.0.id().protocol_id() == id { if t.foreign_toplevel.id().protocol_id() == id {
Some(t.0.clone()) Some(t.foreign_toplevel.clone())
} else { } else {
None None
} }
@ -173,7 +179,7 @@ impl<W: AsyncWrite + Unpin> App<W> {
async fn search(&mut self, query: &str) { async fn search(&mut self, query: &str) {
let query = query.to_ascii_lowercase(); 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() { let entry = if query.is_empty() {
fde::matching::get_best_match( fde::matching::get_best_match(
&[&info.app_id, &info.title], &[&info.app_id, &info.title],
@ -214,8 +220,8 @@ impl<W: AsyncWrite + Unpin> App<W> {
let response = PluginResponse::Append(PluginSearchResult { let response = PluginResponse::Append(PluginSearchResult {
// XXX protocol id may be re-used later // XXX protocol id may be re-used later
id: handle.id().protocol_id(), id: info.foreign_toplevel.id().protocol_id(),
window: Some((0, handle.id().clone().protocol_id())), window: Some((0, info.foreign_toplevel.id().protocol_id())),
description: info.title.clone(), description: info.title.clone(),
name: get_description(de, &self.locales), name: get_description(de, &self.locales),
icon: Some(IconSource::Name(icon_name)), icon: Some(IconSource::Name(icon_name)),

View file

@ -5,6 +5,7 @@ use cctk::{
toplevel_info::{ToplevelInfo, ToplevelInfoHandler, ToplevelInfoState}, toplevel_info::{ToplevelInfo, ToplevelInfoHandler, ToplevelInfoState},
toplevel_management::{ToplevelManagerHandler, ToplevelManagerState}, toplevel_management::{ToplevelManagerHandler, ToplevelManagerState},
wayland_client::{self, WEnum}, wayland_client::{self, WEnum},
wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
}; };
use sctk::{ use sctk::{
self, self,
@ -15,7 +16,7 @@ use sctk::{
}; };
use cosmic_protocols::{ 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, toplevel_management::v1::client::zcosmic_toplevel_manager_v1,
}; };
use futures::channel::mpsc::UnboundedSender; use futures::channel::mpsc::UnboundedSender;
@ -25,23 +26,35 @@ use wayland_client::{globals::registry_queue_init, Connection, QueueHandle};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ToplevelAction { pub enum ToplevelAction {
Activate(ZcosmicToplevelHandleV1), Activate(ExtForeignToplevelHandleV1),
Close(ZcosmicToplevelHandleV1), Close(ExtForeignToplevelHandleV1),
} }
pub type TopLevelsUpdate = Vec<( pub enum ToplevelUpdate {
zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, Info(ToplevelInfo),
Option<ToplevelInfo>, Remove(ExtForeignToplevelHandleV1),
)>; }
struct AppData { struct AppData {
exit: bool, exit: bool,
tx: UnboundedSender<TopLevelsUpdate>, tx: UnboundedSender<Vec<ToplevelUpdate>>,
registry_state: RegistryState, registry_state: RegistryState,
toplevel_info_state: ToplevelInfoState, toplevel_info_state: ToplevelInfoState,
toplevel_manager_state: ToplevelManagerState, toplevel_manager_state: ToplevelManagerState,
seat_state: SeatState, seat_state: SeatState,
pending_update: HashSet<zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1>, pending_update: HashSet<ExtForeignToplevelHandleV1>,
}
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 { impl ProvidesRegistryState for AppData {
@ -103,7 +116,7 @@ impl ToplevelInfoHandler for AppData {
&mut self, &mut self,
_conn: &Connection, _conn: &Connection,
_qh: &QueueHandle<Self>, _qh: &QueueHandle<Self>,
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, toplevel: &ExtForeignToplevelHandleV1,
) { ) {
self.pending_update.insert(toplevel.clone()); self.pending_update.insert(toplevel.clone());
} }
@ -112,7 +125,7 @@ impl ToplevelInfoHandler for AppData {
&mut self, &mut self,
_conn: &Connection, _conn: &Connection,
_qh: &QueueHandle<Self>, _qh: &QueueHandle<Self>,
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, toplevel: &ExtForeignToplevelHandleV1,
) { ) {
self.pending_update.insert(toplevel.clone()); self.pending_update.insert(toplevel.clone());
} }
@ -121,20 +134,20 @@ impl ToplevelInfoHandler for AppData {
&mut self, &mut self,
_conn: &Connection, _conn: &Connection,
_qh: &QueueHandle<Self>, _qh: &QueueHandle<Self>,
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, toplevel: &ExtForeignToplevelHandleV1,
) { ) {
self.pending_update.insert(toplevel.clone()); self.pending_update.insert(toplevel.clone());
} }
fn info_done(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>) { fn info_done(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>) {
let mut res = Vec::with_capacity(self.pending_update.len()); let res = self
.pending_update
for toplevel_handle in self.pending_update.drain() { .drain()
res.push(( .map(|handle| match self.toplevel_info_state.info(&handle) {
toplevel_handle.clone(), Some(info) => ToplevelUpdate::Info(info.clone()),
self.toplevel_info_state.info(&toplevel_handle).cloned(), None => ToplevelUpdate::Remove(handle),
)); })
} .collect();
if let Err(err) = self.tx.unbounded_send(res) { if let Err(err) = self.tx.unbounded_send(res) {
warn!("{err}"); warn!("{err}");
@ -143,7 +156,7 @@ impl ToplevelInfoHandler for AppData {
} }
pub(crate) fn toplevel_handler( pub(crate) fn toplevel_handler(
tx: UnboundedSender<TopLevelsUpdate>, tx: UnboundedSender<Vec<ToplevelUpdate>>,
rx: calloop::channel::Channel<ToplevelAction>, rx: calloop::channel::Channel<ToplevelAction>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let conn = Connection::connect_to_env()?; let conn = Connection::connect_to_env()?;
@ -159,15 +172,18 @@ pub(crate) fn toplevel_handler(
calloop::channel::Event::Msg(req) => match req { calloop::channel::Event::Msg(req) => match req {
ToplevelAction::Activate(handle) => { ToplevelAction::Activate(handle) => {
let manager = &state.toplevel_manager_state.manager; let manager = &state.toplevel_manager_state.manager;
let state = &state.seat_state;
// TODO Ashley how to choose the seat in a multi-seat setup? // TODO Ashley how to choose the seat in a multi-seat setup?
for s in state.seats() { if let Some(cosmic_toplevel) = state.cosmic_toplevel_for_foreign(&handle) {
manager.activate(&handle, &s); for s in state.seat_state.seats() {
manager.activate(cosmic_toplevel, &s);
}
} }
} }
ToplevelAction::Close(handle) => { ToplevelAction::Close(handle) => {
let manager = &state.toplevel_manager_state.manager; 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 => { calloop::channel::Event::Closed => {