feat: reliable alt tab switching !!
This commit is contained in:
parent
49c5ef2651
commit
0541d906dd
2 changed files with 58 additions and 70 deletions
|
|
@ -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],
|
||||
|
|
|
|||
|
|
@ -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(®istry_state, &qh),
|
||||
toplevel_manager_state: ToplevelManagerState::new(®istry_state, &qh),
|
||||
registry_state,
|
||||
pending_update: HashSet::new(),
|
||||
};
|
||||
|
||||
loop {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue