mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-11-15 21:32:26 +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 animation;
|
||||||
pub mod border_manager;
|
pub mod border_manager;
|
||||||
|
pub mod cloaked;
|
||||||
pub mod com;
|
pub mod com;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod ring;
|
pub mod ring;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ use crate::animation::lerp::Lerp;
|
|||||||
use crate::animation::prefix::AnimationPrefix;
|
use crate::animation::prefix::AnimationPrefix;
|
||||||
use crate::animation::prefix::new_animation_key;
|
use crate::animation::prefix::new_animation_key;
|
||||||
use crate::border_manager;
|
use crate::border_manager;
|
||||||
|
use crate::cloaked;
|
||||||
use crate::com::SetCloak;
|
use crate::com::SetCloak;
|
||||||
use crate::core::ApplicationIdentifier;
|
use crate::core::ApplicationIdentifier;
|
||||||
use crate::core::HidingBehaviour;
|
use crate::core::HidingBehaviour;
|
||||||
@@ -705,7 +706,10 @@ impl Window {
|
|||||||
match *hiding_behaviour {
|
match *hiding_behaviour {
|
||||||
HidingBehaviour::Hide => WindowsApi::hide_window(self.hwnd),
|
HidingBehaviour::Hide => WindowsApi::hide_window(self.hwnd),
|
||||||
HidingBehaviour::Minimize => WindowsApi::minimize_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 {
|
if hide_border {
|
||||||
border_manager::hide_border(self.hwnd);
|
border_manager::hide_border(self.hwnd);
|
||||||
@@ -732,7 +736,10 @@ impl Window {
|
|||||||
HidingBehaviour::Hide | HidingBehaviour::Minimize => {
|
HidingBehaviour::Hide | HidingBehaviour::Minimize => {
|
||||||
WindowsApi::restore_window(self.hwnd);
|
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 {
|
if restore_border {
|
||||||
border_manager::show_border(self.hwnd);
|
border_manager::show_border(self.hwnd);
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ impl WindowManager {
|
|||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn init(&mut self) -> eyre::Result<()> {
|
pub fn init(&mut self) -> eyre::Result<()> {
|
||||||
tracing::info!("initialising");
|
tracing::info!("initialising");
|
||||||
|
crate::cloaked::uncloak_left_behind();
|
||||||
WindowsApi::load_monitor_information(self)?;
|
WindowsApi::load_monitor_information(self)?;
|
||||||
WindowsApi::load_workspace_information(&mut self.monitors)
|
WindowsApi::load_workspace_information(&mut self.monitors)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user