feat: reliable alt tab switching !!

This commit is contained in:
wiiznokes 2024-09-22 23:09:20 +02:00 committed by Ashley Wulber
parent 49c5ef2651
commit 0541d906dd
2 changed files with 58 additions and 70 deletions

View file

@ -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<W> {
locales: Vec<String>,
desktop_entries: Vec<DesktopEntry<'static>>,
ids_to_ignore: Vec<u32>,
// XXX: use LinkedList, and Box the tuple because it will be re ordered a lot?
toplevels: VecDeque<(ZcosmicToplevelHandleV1, ToplevelInfo)>,
toplevels: Vec<(ZcosmicToplevelHandleV1, Box<ToplevelInfo>)>,
calloop_tx: calloop::channel::Sender<ToplevelAction>,
tx: W,
}
impl<W: AsyncWrite + Unpin> App<W> {
fn new(tx: W) -> (Self, mpsc::UnboundedReceiver<ToplevelEvent>) {
fn new(tx: W) -> (Self, mpsc::UnboundedReceiver<TopLevelsUpdate>) {
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<W: AsyncWrite + Unpin> App<W> {
locales,
desktop_entries,
ids_to_ignore: Vec::new(),
toplevels: VecDeque::new(),
toplevels: Vec::new(),
calloop_tx,
tx,
},
@ -181,7 +173,7 @@ impl<W: AsyncWrite + Unpin> App<W> {
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],

View file

@ -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<zcosmic_toplevel_handle_v1::State>,
pub output: Option<WlOutput>,
pub workspace: Option<ZcosmicWorkspaceHandleV1>,
}
pub type TopLevelsUpdate = Vec<(
zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
Option<ToplevelInfo>,
)>;
struct AppData {
exit: bool,
tx: UnboundedSender<ToplevelEvent>,
tx: UnboundedSender<TopLevelsUpdate>,
registry_state: RegistryState,
toplevel_info_state: ToplevelInfoState,
toplevel_manager_state: ToplevelManagerState,
seat_state: SeatState,
pending_update: HashSet<zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1>,
}
impl ProvidesRegistryState for AppData {
@ -115,11 +105,7 @@ impl ToplevelInfoHandler for AppData {
_qh: &QueueHandle<Self>,
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<Self>,
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<Self>,
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<Self>) {
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<ToplevelEvent>,
tx: UnboundedSender<TopLevelsUpdate>,
rx: calloop::channel::Channel<ToplevelAction>,
) -> anyhow::Result<()> {
let conn = Connection::connect_to_env()?;
@ -188,6 +183,7 @@ pub(crate) fn toplevel_handler(
toplevel_info_state: ToplevelInfoState::new(&registry_state, &qh),
toplevel_manager_state: ToplevelManagerState::new(&registry_state, &qh),
registry_state,
pending_update: HashSet::new(),
};
loop {