Optimize Highlighter performance using dirty rectangles. (#19344)
Fixed: #17434
### Summary of the issue:
Previously, we called `InvalidateRect` every 100 milliseconds to refresh
the entire window. This caused `dwm.exe` to perform extensive
calculations, resulting in high GPU or CPU utilization—particularly
noticeable on low-performance machines.
### Description of user facing changes:
Enabling the highlighter does not cause `dwm.exe` to consume excessive
resources continuously, especially when no objects are being updated.
### Description of developer facing changes:
* **Type Hinting Improvements:** Updated type hints across
`autoSettings.py`, `mouseHandler.py`, and `NVDAHighlighter.py` to use
modern Python syntax (e.g., standard collection generics like
`list[...]` instead of `typing.List`, and `type` aliases). Added
`override` decorators where appropriate.
* **Formatting import** Use ruff to format import statement.
* **NVDAHighlighter Refactor:**
* `HighlightStyle` is now a `NamedTuple` with explicit type annotations.
* Refactored `HighlightWindow` to separate logic for coordinate mapping
(`_mapRectToClient`) and determining draw targets (`_getDrawRects`).
* Introduced state tracking (`_prevContextRects`) to the highlighter
window to facilitate delta updates.
### Description of development approach:
To address the performance issue with `dwm.exe`:
1. **Dirty Rectangle Invalidation:** Instead of blindly invalidating the
entire screen overlay window every refresh cycle (`InvalidateRect(...,
None, ...)`), the approach was changed to calculate specific "dirty"
regions.
2. **State Tracking:** The `HighlightWindow` now caches the rectangles
rendered in the previous frame.
3. **Delta Calculation:** During `refresh()`:
* The current required rectangles are calculated.
* They are compared with the cached previous state.
* If a context's rectangle has changed or was removed, the **old**
screen region is invalidated (to clear the artifact).
* If a context's rectangle is new or changed, the **new** screen region
is invalidated (to draw the new highlight).
4. **Region Calculation:** A helper method `_invalidateContextRect` maps
the logical object rectangle to client coordinates and adds necessary
padding (accounting for the pen width and anti-aliasing) to ensure the
invalidation rect covers the entire visual drawing.
This minimizes the surface area that the Windows DWM needs to recompose,
drastically reducing GPU/CPU usage.
### Testing strategy:
Manual Testing:
1. Build and install the self-signed launcher.
2. Enable the highlighter tool and move the focus to check for drawing
errors. (especially drawn on the start menu)