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 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 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};
|
||||||
|
|
@ -21,10 +22,9 @@ use pop_launcher::{
|
||||||
};
|
};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::collections::VecDeque;
|
|
||||||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||||
|
|
||||||
use self::toplevel_handler::{toplevel_handler, ToplevelAction, ToplevelEvent};
|
use self::toplevel_handler::{toplevel_handler, ToplevelAction};
|
||||||
|
|
||||||
pub async fn main() {
|
pub async fn main() {
|
||||||
let mut tx = async_stdout();
|
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_event = toplevel_rx.next();
|
||||||
next_request = second_to_next_request;
|
next_request = second_to_next_request;
|
||||||
|
|
||||||
match event {
|
for (handle, info) in updates {
|
||||||
ToplevelEvent::Add(handle, info) => {
|
match info {
|
||||||
debug!("add {}", &info.app_id);
|
Some(info) => {
|
||||||
app.toplevels.retain(|t| t.0 != handle);
|
if let Some(pos) = app.toplevels.iter().position(|t| t.0 == handle) {
|
||||||
app.toplevels.push_front((handle, info));
|
if info.state.contains(&State::Activated) {
|
||||||
}
|
app.toplevels.remove(pos);
|
||||||
ToplevelEvent::Remove(handle) => {
|
app.toplevels.push((handle, Box::new(info)));
|
||||||
if let Some(pos) = app.toplevels.iter().position(|t| t.0 == handle) {
|
} else {
|
||||||
app.toplevels.remove(pos);
|
app.toplevels[pos].1 = Box::new(info);
|
||||||
// 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));
|
|
||||||
} else {
|
} 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>,
|
locales: Vec<String>,
|
||||||
desktop_entries: Vec<DesktopEntry<'static>>,
|
desktop_entries: Vec<DesktopEntry<'static>>,
|
||||||
ids_to_ignore: Vec<u32>,
|
ids_to_ignore: Vec<u32>,
|
||||||
// XXX: use LinkedList, and Box the tuple because it will be re ordered a lot?
|
toplevels: Vec<(ZcosmicToplevelHandleV1, Box<ToplevelInfo>)>,
|
||||||
toplevels: VecDeque<(ZcosmicToplevelHandleV1, 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<ToplevelEvent>) {
|
fn new(tx: W) -> (Self, mpsc::UnboundedReceiver<TopLevelsUpdate>) {
|
||||||
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));
|
||||||
|
|
@ -138,7 +130,7 @@ impl<W: AsyncWrite + Unpin> App<W> {
|
||||||
locales,
|
locales,
|
||||||
desktop_entries,
|
desktop_entries,
|
||||||
ids_to_ignore: Vec::new(),
|
ids_to_ignore: Vec::new(),
|
||||||
toplevels: VecDeque::new(),
|
toplevels: Vec::new(),
|
||||||
calloop_tx,
|
calloop_tx,
|
||||||
tx,
|
tx,
|
||||||
},
|
},
|
||||||
|
|
@ -181,7 +173,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 {
|
for (handle, 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],
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use cctk::{
|
use cctk::{
|
||||||
cosmic_protocols,
|
cosmic_protocols,
|
||||||
toplevel_info::{ToplevelInfo, ToplevelInfoHandler, ToplevelInfoState},
|
toplevel_info::{ToplevelInfo, ToplevelInfoHandler, ToplevelInfoState},
|
||||||
toplevel_management::{ToplevelManagerHandler, ToplevelManagerState},
|
toplevel_management::{ToplevelManagerHandler, ToplevelManagerState},
|
||||||
wayland_client::{self, protocol::wl_output::WlOutput, WEnum},
|
wayland_client::{self, WEnum},
|
||||||
};
|
};
|
||||||
use sctk::{
|
use sctk::{
|
||||||
self,
|
self,
|
||||||
|
|
@ -15,10 +17,10 @@ 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::{self, ZcosmicToplevelHandleV1},
|
||||||
toplevel_management::v1::client::zcosmic_toplevel_manager_v1,
|
toplevel_management::v1::client::zcosmic_toplevel_manager_v1,
|
||||||
workspace::v1::server::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
|
||||||
};
|
};
|
||||||
use futures::channel::mpsc::UnboundedSender;
|
use futures::channel::mpsc::UnboundedSender;
|
||||||
use sctk::registry::{ProvidesRegistryState, RegistryState};
|
use sctk::registry::{ProvidesRegistryState, RegistryState};
|
||||||
|
use tracing::warn;
|
||||||
use wayland_client::{globals::registry_queue_init, Connection, QueueHandle};
|
use wayland_client::{globals::registry_queue_init, Connection, QueueHandle};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -27,31 +29,19 @@ pub enum ToplevelAction {
|
||||||
Close(ZcosmicToplevelHandleV1),
|
Close(ZcosmicToplevelHandleV1),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
pub type TopLevelsUpdate = Vec<(
|
||||||
pub enum ToplevelEvent {
|
zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
Add(ZcosmicToplevelHandleV1, ToplevelInfo),
|
Option<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>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AppData {
|
struct AppData {
|
||||||
exit: bool,
|
exit: bool,
|
||||||
tx: UnboundedSender<ToplevelEvent>,
|
tx: UnboundedSender<TopLevelsUpdate>,
|
||||||
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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProvidesRegistryState for AppData {
|
impl ProvidesRegistryState for AppData {
|
||||||
|
|
@ -115,11 +105,7 @@ impl ToplevelInfoHandler for AppData {
|
||||||
_qh: &QueueHandle<Self>,
|
_qh: &QueueHandle<Self>,
|
||||||
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
) {
|
) {
|
||||||
if let Some(info) = self.toplevel_info_state.info(toplevel) {
|
self.pending_update.insert(toplevel.clone());
|
||||||
let _ = self
|
|
||||||
.tx
|
|
||||||
.unbounded_send(ToplevelEvent::Add(toplevel.clone(), info.clone()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_toplevel(
|
fn update_toplevel(
|
||||||
|
|
@ -128,11 +114,7 @@ impl ToplevelInfoHandler for AppData {
|
||||||
_qh: &QueueHandle<Self>,
|
_qh: &QueueHandle<Self>,
|
||||||
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
) {
|
) {
|
||||||
if let Some(info) = self.toplevel_info_state.info(toplevel) {
|
self.pending_update.insert(toplevel.clone());
|
||||||
let _ = self
|
|
||||||
.tx
|
|
||||||
.unbounded_send(ToplevelEvent::Update(toplevel.clone(), info.clone()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toplevel_closed(
|
fn toplevel_closed(
|
||||||
|
|
@ -141,14 +123,27 @@ impl ToplevelInfoHandler for AppData {
|
||||||
_qh: &QueueHandle<Self>,
|
_qh: &QueueHandle<Self>,
|
||||||
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
) {
|
) {
|
||||||
let _ = self
|
self.pending_update.insert(toplevel.clone());
|
||||||
.tx
|
}
|
||||||
.unbounded_send(ToplevelEvent::Remove(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(
|
pub(crate) fn toplevel_handler(
|
||||||
tx: UnboundedSender<ToplevelEvent>,
|
tx: UnboundedSender<TopLevelsUpdate>,
|
||||||
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()?;
|
||||||
|
|
@ -188,6 +183,7 @@ pub(crate) fn toplevel_handler(
|
||||||
toplevel_info_state: ToplevelInfoState::new(®istry_state, &qh),
|
toplevel_info_state: ToplevelInfoState::new(®istry_state, &qh),
|
||||||
toplevel_manager_state: ToplevelManagerState::new(®istry_state, &qh),
|
toplevel_manager_state: ToplevelManagerState::new(®istry_state, &qh),
|
||||||
registry_state,
|
registry_state,
|
||||||
|
pending_update: HashSet::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue