Skip to content

Commit ff6ceaf

Browse files
committed
feat(reaper): rework reaper logic to make it faster
# This is a combination of 4 commits. # This is the 1st commit message: This commit changes the way the reaper works. First this commit changed the `known_hwnds` held by the `WindowManager` to be a HashMap of window handles (isize) to a pair of monitor_idx, workspace_idx (usize, usize). This commit then changes the reaper to have a cache of hwnds which is updated by the `WindowManager` when they change. The reaper has a thread that is continuously checking this cache to see if there is any window handle that no longer exists. When it finds them, the thread sends a notification to a channel which is then received by the reaper on another thread that actually does the work on the `WindowManager` by removing said windows. This means that the reaper no longer tries to access and lock the `WindowManager` every second like it used to, but instead it only does it when it actually needs, when a window actually needs to be reaped. This means that we can make the thread that checks for orphan windows run much more frequently since it won't influence the rest of komorebi. # This is the commit message n.2: refactor(wm): move update of known_hwnds to a function # This is the commit message n.3: feat(wm): update known_hwnds on process_command # This is the commit message n.4: refactor(wm): simplify loops on monitor/workspaces This commit uses the new `known_hwnds` hashmap to simplify looking for windows on all the monitors and all the workspaces. Since now the `known_hwnds` have the monitor/workspace index pair of the window we can simply get that info from the map and immediately access that monitor/workspace or use that index info. This commit already does this on some situations from the `process_event`.
1 parent 8bc04f0 commit ff6ceaf

File tree

7 files changed

+283
-119
lines changed

7 files changed

+283
-119
lines changed

komorebi/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ fn main() -> Result<()> {
291291
transparency_manager::listen_for_notifications(wm.clone());
292292
workspace_reconciliator::listen_for_notifications(wm.clone());
293293
monitor_reconciliator::listen_for_notifications(wm.clone())?;
294-
reaper::watch_for_orphans(wm.clone());
294+
reaper::listen_for_notifications(wm.clone());
295295
focus_manager::listen_for_notifications(wm.clone());
296296
theme_manager::listen_for_notifications();
297297

komorebi/src/monitor_reconciliator/mod.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
385385
}
386386

387387
// Update known_hwnds
388-
wm.known_hwnds.retain(|i| !windows_to_remove.contains(i));
388+
wm.known_hwnds.retain(|i, _| !windows_to_remove.contains(i));
389389

390390
if !newly_removed_displays.is_empty() {
391391
// After we have cached them, remove them from our state
@@ -481,7 +481,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
481481
{
482482
container.windows_mut().retain(|window| {
483483
window.exe().is_ok()
484-
&& !known_hwnds.contains(&window.hwnd)
484+
&& !known_hwnds.contains_key(&window.hwnd)
485485
});
486486

487487
if container.windows().is_empty() {
@@ -519,7 +519,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
519519

520520
if let Some(window) = workspace.maximized_window() {
521521
if window.exe().is_err()
522-
|| known_hwnds.contains(&window.hwnd)
522+
|| known_hwnds.contains_key(&window.hwnd)
523523
{
524524
workspace.set_maximized_window(None);
525525
} else if is_focused_workspace {
@@ -530,7 +530,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
530530
if let Some(container) = workspace.monocle_container_mut() {
531531
container.windows_mut().retain(|window| {
532532
window.exe().is_ok()
533-
&& !known_hwnds.contains(&window.hwnd)
533+
&& !known_hwnds.contains_key(&window.hwnd)
534534
});
535535

536536
if container.windows().is_empty() {
@@ -552,7 +552,8 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
552552
}
553553

554554
workspace.floating_windows_mut().retain(|window| {
555-
window.exe().is_ok() && !known_hwnds.contains(&window.hwnd)
555+
window.exe().is_ok()
556+
&& !known_hwnds.contains_key(&window.hwnd)
556557
});
557558

558559
if is_focused_workspace {

komorebi/src/process_command.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1835,6 +1835,9 @@ impl WindowManager {
18351835
| SocketMessage::IdentifyBorderOverflowApplication(_, _) => {}
18361836
};
18371837

1838+
// Update list of known_hwnds and their monitor/workspace index pair
1839+
self.update_known_hwnds();
1840+
18381841
notify_subscribers(
18391842
Notification {
18401843
event: NotificationEvent::Socket(message.clone()),

komorebi/src/process_event.rs

+32-73
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::fs::OpenOptions;
21
use std::sync::atomic::Ordering;
32
use std::sync::Arc;
43
use std::time::Duration;
@@ -33,7 +32,6 @@ use crate::workspace_reconciliator::ALT_TAB_HWND_INSTANT;
3332
use crate::Notification;
3433
use crate::NotificationEvent;
3534
use crate::State;
36-
use crate::DATA_DIR;
3735
use crate::FLOATING_APPLICATIONS;
3836
use crate::HIDDEN_HWNDS;
3937
use crate::REGEX_IDENTIFIERS;
@@ -307,30 +305,28 @@ impl WindowManager {
307305

308306
let mut needs_reconciliation = false;
309307

310-
for (i, monitors) in self.monitors().iter().enumerate() {
311-
for (j, workspace) in monitors.workspaces().iter().enumerate() {
312-
if workspace.contains_window(window.hwnd) && focused_pair != (i, j) {
313-
// At this point we know we are going to send a notification to the workspace reconciliator
314-
// So we get the topmost window returned by EnumWindows, which is almost always the window
315-
// that has been selected by alt-tab
316-
if let Ok(alt_tab_windows) = WindowsApi::alt_tab_windows() {
317-
if let Some(first) =
318-
alt_tab_windows.iter().find(|w| w.title().is_ok())
319-
{
320-
// If our record of this HWND hasn't been updated in over a minute
321-
let mut instant = ALT_TAB_HWND_INSTANT.lock();
322-
if instant.elapsed().gt(&Duration::from_secs(1)) {
323-
// Update our record with the HWND we just found
324-
ALT_TAB_HWND.store(Some(first.hwnd));
325-
// Update the timestamp of our record
326-
*instant = Instant::now();
327-
}
308+
if let Some((m_idx, w_idx)) = self.known_hwnds.get(&window.hwnd) {
309+
if focused_pair != (*m_idx, *w_idx) {
310+
// At this point we know we are going to send a notification to the workspace reconciliator
311+
// So we get the topmost window returned by EnumWindows, which is almost always the window
312+
// that has been selected by alt-tab
313+
if let Ok(alt_tab_windows) = WindowsApi::alt_tab_windows() {
314+
if let Some(first) =
315+
alt_tab_windows.iter().find(|w| w.title().is_ok())
316+
{
317+
// If our record of this HWND hasn't been updated in over a minute
318+
let mut instant = ALT_TAB_HWND_INSTANT.lock();
319+
if instant.elapsed().gt(&Duration::from_secs(1)) {
320+
// Update our record with the HWND we just found
321+
ALT_TAB_HWND.store(Some(first.hwnd));
322+
// Update the timestamp of our record
323+
*instant = Instant::now();
328324
}
329325
}
330-
331-
workspace_reconciliator::send_notification(i, j);
332-
needs_reconciliation = true;
333326
}
327+
328+
workspace_reconciliator::send_notification(*m_idx, *w_idx);
329+
needs_reconciliation = true;
334330
}
335331
}
336332

@@ -341,11 +337,14 @@ impl WindowManager {
341337
// duplicates across multiple workspaces, as it results in ghost layout tiles.
342338
let mut proceed = true;
343339

344-
for (i, monitor) in self.monitors().iter().enumerate() {
345-
for (j, workspace) in monitor.workspaces().iter().enumerate() {
346-
if workspace.contains_window(window.hwnd)
347-
&& i != self.focused_monitor_idx()
348-
&& j != monitor.focused_workspace_idx()
340+
if let Some((m_idx, w_idx)) = self.known_hwnds.get(&window.hwnd) {
341+
if let Some(focused_workspace_idx) = self
342+
.monitors()
343+
.get(*m_idx)
344+
.map(|m| m.focused_workspace_idx())
345+
{
346+
if *m_idx != self.focused_monitor_idx()
347+
&& *w_idx != focused_workspace_idx
349348
{
350349
tracing::debug!(
351350
"ignoring show event for window already associated with another workspace"
@@ -504,15 +503,9 @@ impl WindowManager {
504503
// This will be true if we have moved to another monitor
505504
let mut moved_across_monitors = false;
506505

507-
for (i, monitors) in self.monitors().iter().enumerate() {
508-
for workspace in monitors.workspaces() {
509-
if workspace.contains_window(window.hwnd) && i != target_monitor_idx {
510-
moved_across_monitors = true;
511-
break;
512-
}
513-
}
514-
if moved_across_monitors {
515-
break;
506+
if let Some((m_idx, _)) = self.known_hwnds.get(&window.hwnd) {
507+
if *m_idx != target_monitor_idx {
508+
moved_across_monitors = true;
516509
}
517510
}
518511

@@ -716,42 +709,8 @@ impl WindowManager {
716709
window.center(&self.focused_monitor_work_area()?)?;
717710
}
718711

719-
tracing::trace!("updating list of known hwnds");
720-
let mut known_hwnds = vec![];
721-
for monitor in self.monitors() {
722-
for workspace in monitor.workspaces() {
723-
for container in workspace.containers() {
724-
for window in container.windows() {
725-
known_hwnds.push(window.hwnd);
726-
}
727-
}
728-
729-
for window in workspace.floating_windows() {
730-
known_hwnds.push(window.hwnd);
731-
}
732-
733-
if let Some(window) = workspace.maximized_window() {
734-
known_hwnds.push(window.hwnd);
735-
}
736-
737-
if let Some(container) = workspace.monocle_container() {
738-
for window in container.windows() {
739-
known_hwnds.push(window.hwnd);
740-
}
741-
}
742-
}
743-
}
744-
745-
let hwnd_json = DATA_DIR.join("komorebi.hwnd.json");
746-
let file = OpenOptions::new()
747-
.write(true)
748-
.truncate(true)
749-
.create(true)
750-
.open(hwnd_json)?;
751-
752-
serde_json::to_writer_pretty(&file, &known_hwnds)?;
753-
754-
self.known_hwnds = known_hwnds;
712+
// Update list of known_hwnds and their monitor/workspace index pair
713+
self.update_known_hwnds();
755714

756715
notify_subscribers(
757716
Notification {

0 commit comments

Comments
 (0)