mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-11-14 04:43:42 +01:00
fix(window): uncloak the windows a killed daemon left cloaked
A daemon that is killed does not get to uncloak the windows it hid, and the next one skips them as cloaked, so they stay out of sight and unmanaged. A cloak by komorebi reads the same as one for another virtual desktop or a suspended app, so keep a record of the windows that are cloaked and uncloak those that the last daemon left behind, before looking for windows.
This commit is contained in:
1 parent
0b57006053
commit
dc9b2e9539
4 files changed
+104
-2
No files matched your search
@@ -0,0 +1,93 @@
|
||||
//! A record of the windows komorebi has cloaked, kept on disk.
|
||||
//!
|
||||
//! A daemon that is killed does not get to uncloak the windows it hid, and a window that is
|
||||
//! cloaked looks the same to the next daemon as one on another virtual desktop or a suspended
|
||||
//! app, so it cannot be told apart by looking at it. The next daemon uncloaks the windows that
|
||||
//! the last one wrote down.
|
||||
|
||||
use crate::DATA_DIR;
|
||||
use crate::WindowsApi;
|
||||
use crate::com::SetCloak;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
|
||||
fn record_path() -> PathBuf {
|
||||
DATA_DIR.join("komorebi.cloaked.json")
|
||||
}
|
||||
|
||||
/// Write down the windows that are cloaked
|
||||
///
|
||||
/// Unit tests hide windows that are not real, and must not touch the record of a daemon that is
|
||||
/// running
|
||||
pub fn record(hidden: &[isize]) {
|
||||
if cfg!(test) {
|
||||
return;
|
||||
}
|
||||
|
||||
let path = record_path();
|
||||
let result = if hidden.is_empty() {
|
||||
match std::fs::remove_file(&path) {
|
||||
Err(error) if error.kind() != std::io::ErrorKind::NotFound => Err(error),
|
||||
_ => Ok(()),
|
||||
}
|
||||
} else {
|
||||
serde_json::to_string(hidden)
|
||||
.map_err(std::io::Error::other)
|
||||
.and_then(|json| std::fs::write(&path, json))
|
||||
};
|
||||
|
||||
if let Err(error) = result {
|
||||
tracing::error!("could not record the cloaked windows: {error}");
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(contents: &str) -> Vec<isize> {
|
||||
serde_json::from_str(contents).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Of the recorded windows, the ones that are still there and are still cloaked
|
||||
fn still_cloaked(recorded: Vec<isize>, is_cloaked: impl Fn(isize) -> bool) -> Vec<isize> {
|
||||
recorded
|
||||
.into_iter()
|
||||
.filter(|hwnd| is_cloaked(*hwnd))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Uncloak the windows that a daemon that did not exit cleanly left cloaked, before the windows
|
||||
/// are looked for
|
||||
pub fn uncloak_left_behind() {
|
||||
let path = record_path();
|
||||
let Ok(contents) = std::fs::read_to_string(&path) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let left_behind = still_cloaked(parse(&contents), |hwnd| {
|
||||
WindowsApi::is_window(hwnd) && WindowsApi::is_window_cloaked(hwnd).unwrap_or_default()
|
||||
});
|
||||
|
||||
for hwnd in left_behind {
|
||||
tracing::info!("uncloaking {hwnd}, which a previous run left cloaked");
|
||||
SetCloak(HWND(hwnd as *mut core::ffi::c_void), 1, 0);
|
||||
}
|
||||
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn a_record_that_cannot_be_read_names_no_windows() {
|
||||
assert!(parse("").is_empty());
|
||||
assert!(parse("{not json").is_empty());
|
||||
assert_eq!(parse("[12, 34]"), vec![12, 34]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_a_window_that_is_still_cloaked_is_left_behind() {
|
||||
let left = still_cloaked(vec![1, 2, 3], |hwnd| hwnd != 2);
|
||||
assert_eq!(left, vec![1, 3]);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
pub mod animation;
|
||||
pub mod border_manager;
|
||||
pub mod cloaked;
|
||||
pub mod com;
|
||||
#[macro_use]
|
||||
pub mod ring;
|
||||
|
||||
@@ -27,6 +27,7 @@ use crate::animation::lerp::Lerp;
|
||||
use crate::animation::prefix::AnimationPrefix;
|
||||
use crate::animation::prefix::new_animation_key;
|
||||
use crate::border_manager;
|
||||
use crate::cloaked;
|
||||
use crate::com::SetCloak;
|
||||
use crate::core::ApplicationIdentifier;
|
||||
use crate::core::HidingBehaviour;
|
||||
@@ -705,7 +706,10 @@ impl Window {
|
||||
match *hiding_behaviour {
|
||||
HidingBehaviour::Hide => WindowsApi::hide_window(self.hwnd),
|
||||
HidingBehaviour::Minimize => WindowsApi::minimize_window(self.hwnd),
|
||||
HidingBehaviour::Cloak => SetCloak(self.hwnd(), 1, 2),
|
||||
HidingBehaviour::Cloak => {
|
||||
SetCloak(self.hwnd(), 1, 2);
|
||||
cloaked::record(&programmatically_hidden_hwnds);
|
||||
}
|
||||
}
|
||||
if hide_border {
|
||||
border_manager::hide_border(self.hwnd);
|
||||
@@ -732,7 +736,10 @@ impl Window {
|
||||
HidingBehaviour::Hide | HidingBehaviour::Minimize => {
|
||||
WindowsApi::restore_window(self.hwnd);
|
||||
}
|
||||
HidingBehaviour::Cloak => SetCloak(self.hwnd(), 1, 0),
|
||||
HidingBehaviour::Cloak => {
|
||||
SetCloak(self.hwnd(), 1, 0);
|
||||
cloaked::record(&programmatically_hidden_hwnds);
|
||||
}
|
||||
}
|
||||
if restore_border {
|
||||
border_manager::show_border(self.hwnd);
|
||||
|
||||
@@ -178,6 +178,7 @@ impl WindowManager {
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn init(&mut self) -> eyre::Result<()> {
|
||||
tracing::info!("initialising");
|
||||
crate::cloaked::uncloak_left_behind();
|
||||
WindowsApi::load_monitor_information(self)?;
|
||||
WindowsApi::load_workspace_information(&mut self.monitors)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user