Commit Graph
4 Commits
Author SHA1 Message Date
Rejdukien 98122bd9d4 fix(wm): prevent window-removal race after display changes
This prevents a race where OS-initiated minimizes prematurely
remove windows that should be transiently restored.

Problem: a display-change can trigger a a fast reconciliation path (same
count), and shortly after Windows may emit a SystemMinimizeStart for
affected windows.  The minimize handler treated those as user-initiated
and removed the window, making later reconciliation unable to restore
it.

Fix: timestamp display-change notifications and add a
display_change_in_progress(period) check to the minimize handler.  While
that grace period is active the minimize handler skips remove_window(),
preserving windows so the reconciliator can restore them.
2026-02-07 15:28:46 -08:00
Rejdukien 9741b387a7 fix(wm): add verification step in monitor reconciliator to handle noisy Win32 events
This change adds verification steps and robustness improvements
to the monitor reconciliator.

- Retry display enumeration if it fails, up to 5 times, instead of silently
  ignoring errors.
- When a potential monitor removal is detected, drop locks and wait briefly,
  then re-query the Win32 display API. If the monitor reappears, treat the
  event as transient and do not remove the monitor.
- Re-acquire locks when needed, so the verification wait doesn't hold
  internal state locks.

Why:
- Win32 device/display notifications are noisy and not always immediately
  available; enumeration can fail (e.g. "Invalid monitor handle" / HRESULT
  0x80070585), or multiple DBT_DEVICEARRIVAL/DBT_DEVICEREMOVECOMPLETE events
  can arrive in quick succession.
- Without verification the reconciliator could treat transient events (for
  example, power-plan–induced monitor sleep where Removes+Adds occur within
  milliseconds) as real removals, resulting in fewer detected monitors than
  are actually present.
2026-02-07 15:28:40 -08:00
Rejdukien dbde351e22 fix(border): release resources before destroying window to prevent access violations
When a border is destroyed, the main thread was forcefully releasing D2D
resources, while the border window thread might still be trying to use
them in its message loop.

Releasing the RenderTarget on one thread while another is calling
EndDraw on it leads to a use-after-free scenario or invalid state for
the COM object, resulting in the crash.

This commit applies a fix that moves the resource cleanup logic from the
main thread to the window thread. Now, resources are only released
during the WM_DESTROY message processing, which guarantees
synchronization with other window messages like WM_PAINT.
2026-01-30 20:18:43 -08:00
Rejdukien 0b39551470 fix(wm): clear last error before calling GetWindowLongPtrW
Apparently there is a quirk of GetWindowLongPtr when querying styles. If
the style bits are genuinely 0, the API returns 0 but does not clear the
last error.

If a previous API call set an error, GetWindowLongPtr might define
"failure" as "return 0 AND GetLastError != 0".

Hence, it is important to clear the last/previous error before calling
GetWindowLongPtrW to ensure that we don't misinterpret a valid 0 return
value as an error.
2026-01-13 08:26:23 -08:00