fix(monitor): skip modal/owned windows and their parents#6
Conversation
The action loop reached close/minimize for any non-foreground window past its idle timeout, ignoring is_owned on WindowInfo. A parent window could be closed while an owned modal (Save As, auth prompt, etc.) was active. Now skip entries where is_owned is true, and skip any window whose PID has a visible owned window in the current snapshot.
filter: - add is_cloaked + is_on_current_desktop to WindowInfo; short-circuit in is_system_window so DWM-cloaked surfaces (UWP background, virtual-desktop residue, shell-hidden) never reach the action loop. winapi: - add WindowApi::get_foreground_hwnd for HWND-level checks; default for the trait + null for non-Windows; real impl uses GetForegroundWindow(). - populate is_cloaked via DwmGetWindowAttribute(DWMWA_CLOAKED). The cloaked flag also covers windows on other virtual desktops (DWM_CLOAKED_SHELL), so explicit IVirtualDesktopManager COM is not required for this fix. - broaden is_fullscreen with a near-full coverage path (>=99% of monitor rect) so DXGI/borderless-windowed-fullscreen titles are protected. - cache PID -> process name across a single enumeration cycle. - split unsafe regions in enum_window_callback_v2 and tag each with a brief invariant note. monitor: - HWND-level foreground guard runs immediately before action dispatch, in addition to the existing process-name check. - detect monotonic gaps between polls (>= 5x interval or >= 60s) and rebase every foreground timestamp to now, so resume-from-sleep / lock / hibernation does not fire a wave of closes the second the user unlocks. - record rule source (AppRule vs Bucket) and timeout_mins on every action log entry; surface both in the runtime info log line. - redact window title from the info-level action log (titles may contain sensitive data); preserve them at debug level + in the GUI action log. config: - ResolvedRule now reports source (AppRule | Bucket). - save() fsyncs the temp file before rename and best-effort fsyncs the parent directory after rename so an atomic rename is also durable across power loss. main: - startup GUI hydration no longer unwraps the config / search-state RwLocks (poison would have panicked the tray); fall back to defaults instead. tests: - foreground HWND guard, cloaked-window filter, off-desktop filter.
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ecd7a12699
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let pids_with_owned: HashSet<u32> = self | ||
| .current_windows | ||
| .iter() | ||
| .filter(|e| e.info.is_owned && !filter::is_system_window(&e.info)) |
There was a problem hiding this comment.
Include all owned windows when shielding parent processes
The PID guard only tracks owned windows that also satisfy !filter::is_system_window, which means owned dialogs filtered as system (for example empty-title or tool-style owned windows) are excluded from pids_with_owned. In that case, the parent window for the same PID is still eligible for close/minimize, so this patch can still interrupt modal/owned flows in the exact scenario it aims to prevent. Build the owned-PID set from all owned visible windows (or apply system filtering after parent shielding) so any owned dialog suppresses parent actions.
Useful? React with 👍 / 👎.
…tered Addresses Codex P2 feedback on PR #6: pre-filtering pids_with_owned with is_system_window let empty-title or tool-style owned dialogs slip past the guard, leaving their parent eligible for action — exactly the modal flow this check exists to protect.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f73c961c85
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .current_windows | ||
| .iter() | ||
| .filter(|e| e.info.is_owned) | ||
| .map(|e| e.pid) | ||
| .collect(); |
There was a problem hiding this comment.
Restrict owned-window skip to modal dialogs
This marks a process as non-actionable whenever any owned window exists, but is_owned/GW_OWNER does not imply modality; many apps keep modeless owned helpers (for example find/replace or floating tool windows) open during normal use. In that case the parent PID is skipped forever at pids_with_owned.contains(&entry.pid), so configured minimize/close actions never fire for that app. The guard should distinguish modal/interactive blocking dialogs from generic owned windows before suppressing the whole process.
Useful? React with 👍 / 👎.
Previously any owned window on a process caused fade to skip every window owned by that process. This swept up floating helpers (find / replace, color pickers, tool palettes) that leave the parent fully interactive, so e.g. Notepad with Find open would never be minimized. Windows distinguishes true app-modal dialogs by disabling the owner (IsWindowEnabled returns FALSE on the parent — the grey-out you see behind a Save As). Track that explicitly via disables_owner on WindowInfo and gate the parent-process shield on it. Owned windows themselves are still never direct action targets.
|
@chatgpt-codex-connector please review. Addresses the P2 comment on Generated by Claude Code |
|
@codex review Latest commits address the outstanding P2 ("Restrict owned-window skip to modal dialogs") by gating the parent-process shield on Generated by Claude Code |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3ea2e26b6d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .current_windows | ||
| .iter() | ||
| .filter(|e| e.info.disables_owner) | ||
| .map(|e| e.pid) |
There was a problem hiding this comment.
Shield modal owners by owner PID, not dialog PID
The modal-protection set is built from each owned dialog's own PID (.map(|e| e.pid)), and the action loop skips windows only when their PID is in that set. This misses valid Win32 cases where an owned modal is hosted out-of-process (owner and owned windows have different PIDs), so the true owner window is not shielded and can still be minimized/closed while the modal is active. Please derive the shield set from the owner window's PID (or include both owner and dialog PIDs).
Useful? React with 👍 / 👎.
When a modal dialog is hosted in a different process from its owner (shell-hosted picker dialogs, security prompts, UWP modals over a Win32 app), the dialog's PID alone is not enough to protect the parent. Look up the owner window's PID via GetWindowThreadProcessId on the owner HWND and include both PIDs in the shield set.
|
@codex review |
|
Codex Review: Didn't find any major issues. Another round soon, please! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
The action loop reached close/minimize for any non-foreground window past
its idle timeout, ignoring is_owned on WindowInfo. A parent window could
be closed while an owned modal (Save As, auth prompt, etc.) was active.
Now skip entries where is_owned is true, and skip any window whose PID
has a visible owned window in the current snapshot.