Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions .github/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,17 @@ keeps them unless you say otherwise.

Requires 64-bit Windows 10 or 11.

## Two things to know before you download
## One thing to know before you download

**This build is unsigned.** SmartScreen will warn on first run: choose *More info* then
**This installer is unsigned.** SmartScreen will warn on first run: choose *More info* then
*Run anyway*. Some browsers and most managed work computers block the download outright,
which a signature is the only real fix for; one is being arranged.

**Being unsigned costs two features**, because Windows only grants UIAccess to a signed
binary in a protected folder:

- zoom shortcuts do not work while an elevated window has focus (Task Manager, regedit, an
elevated terminal)
- the desktop transform path stays off, so the desktop is magnified by the render engine

Everything else works normally. Wind detects this at startup and picks the right engine on
its own, so there is nothing to configure.
That does not cost you UIAccess, though: Setup signs Wind for UIAccess on your own PC during
install, using a certificate it generates and trusts locally, then deletes right away - so
zoom shortcuts keep working with an elevated window focused (Task Manager, regedit, an
elevated terminal), and the desktop uses the same compositor transform engine as a game.
There is nothing to configure either way.

## What is in it

Expand All @@ -35,6 +31,8 @@ its own, so there is nothing to configure.
- Automatic engine choice per zoom, between a DWM fullscreen transform and its own
DXGI + Direct3D 11 renderer
- Named settings profiles, and a Settings app with guided first-run setup
- Tracking modes: follow the text caret or the keyboard-focused control instead of the
pointer, with a smooth glide, plus a mouse edge mode
- Multi-monitor and HDR aware

## Verify your download
Expand Down
36 changes: 26 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ you keep clicking and using the screen while zoomed.
- **Zoom lock detection** - games that pin the mouse to the screen center (DOOM-style
mouselook) would drag the zoom back with it; listed apps (Settings > Cursor) pan from raw
mouse motion instead.
- **Tracking modes** - the view can follow you instead of only the pointer: it recenters on
the text caret as you type (on by default) or on the keyboard-focused control (off by
default), gliding smoothly to each new target; a mouse edge mode keeps the pointer from
reaching the view's border. Settings > Tracking.

## Magnifier models (`model=`)
Selected with the `model` ini key or the "Magnifier engine" row in Settings. `model` is
Expand Down Expand Up @@ -107,20 +111,25 @@ you sign in, and installs the WebView2 runtime if Settings has no browser engine
Your settings, profiles and logs stay in `%LOCALAPPDATA%\Wind`, and uninstalling keeps them
unless you say otherwise.

**Signing.** Release builds are currently **unsigned**, so Windows SmartScreen will warn on
first run, and the UIAccess-only behaviour above is switched off (Wind detects this at startup
and stays on the render path for the desktop; everything else works normally). Being unsigned
is also why some browsers, and most managed work computers, refuse the download outright. A
certificate is being arranged; the release pipeline already signs when one is configured, via
**Signing.** The installer package itself is currently **unsigned**, so Windows SmartScreen
will warn on first run, and that is also why some browsers, and most managed work computers,
refuse the download outright. A certificate for that is being arranged.

That does not cost you UIAccess, though. Setup generates a one-time local signing certificate
on each PC it installs to, trusts it there, signs the UIAccess build with it, and deletes the
private key right away - so a normal install gets UIAccess (elevated-window shortcuts keep
working, and the desktop uses the transform engine) without needing a purchased certificate. If
that per-PC signing step ever fails, Setup falls back to the ordinary, non-UIAccess build.

The release pipeline also signs with a real certificate when one is configured, via
`WIND_SIGN_THUMBPRINT`, or `WIND_SIGN_PFX` plus `WIND_SIGN_PASSWORD`:

```
pwsh -File tools\release.ps1
```

With a certificate it builds the UIAccess variant, signs both executables and the installer,
and writes `dist\Wind-Setup-x64-<version>.exe`. Without one it builds the ordinary variant and
says so. `src\version.h` is the only place the version is declared.
With a certificate it signs both executables and the installer up front and skips the per-PC
step entirely. `src\version.h` is the only place the version is declared.

## Build
Requires Visual Studio 2022+ Build Tools (Desktop development with C++). From any shell:
Expand Down Expand Up @@ -161,10 +170,17 @@ Profiles (tray -> Profiles, or the Settings titlebar) snapshot the whole file pe
- Pacing/perf: `vsync` (default on), `dwmFlush` (default 0), `gameFpsCap`, `gpuPriority`.
- `model` - `hybrid` (default) / `render` / `transform` / `magnify`. Restart to switch.
- `multiMonitor` - 0 (default, primary only) or 1 (follow the cursor's monitor per zoom-in).
- `desktopTransform` - experimental, ini-only: use the game (compositor) engine on the
desktop too (signed install only, primary monitor only, Auto model).
- `desktopTransform` - default **1**: use the game (compositor) engine on the desktop too
(primary monitor only, Auto model), whenever UIAccess is available; every normal install
gets that from the per-PC signing described above. Set it to `0` to keep the desktop on the
render engine.
- `lockApps` - per-app zoom lock detection (Settings > Cursor > "Zoom lock detection");
`warpLock=1` extends the detection heuristics to unlisted games.
- `trackCaret` (default 1) / `trackFocus` (default 0) - follow the text caret or the
keyboard-focused control instead of the pointer; `trackGlideMs` (default 200) sets how fast
the view glides to a new target. `mouseAlign=1` switches ordinary mouse tracking to an edge
mode where the pointer may approach the view's border instead of staying centered.
Settings > Tracking.
- Advanced: `zorderBand`, `transformExclude`, `noSwallowApps`, `profile`, `launchQuiesce`
(default 1; 0 disables the ~1.5s write hold on a freshly launched fullscreen cover - a test
knob for issue #247, it unguards the #187 DWM crash class, do not ship it off).
Expand Down
38 changes: 23 additions & 15 deletions docs/KNOWN-ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
**Status:** Issue 1 **FIXED** (UIAccess). Issue 2 root cause confirmed (missing
`MagSetInputTransform`) and then refined to a **DPI coordinate-space mismatch** at 225%
scale; logical-coordinate fix implemented, pending test. Issue 3 (flicker) **fixed**
(unit-tested), pending user confirmation. Issue 4 unchanged. See per-issue "Resolution".
(unit-tested), pending user confirmation. Issue 4 **resolved** by the transform engine
(native-Magnifier composition parity measured; see docs/HITCH-FINDINGS.md, 2026-09-28), not by
the render-pipeline injection this doc once framed as the only fix. See per-issue "Resolution".

**Live-test results (2026-05-25):**
- After UIAccess + `MagSetInputTransform`: **Issue 1 fixed** (zoom buttons now work over
Expand Down Expand Up @@ -32,7 +34,7 @@ problem (view flicker, Issue 3 below), not that FPS ceiling.
| 1 | Zoom side-buttons do nothing | Task Manager, some apps | UIPI-class: input not reaching Wind over those windows. UIAccess resolved it. | **FIXED** (UIAccess) |
| 2 | Partial / position-dependent clickability while zoomed (which targets work depends on window position) | Any window, when zoomed, at non-100% scale | `MagSetInputTransform` rects were passed in **physical** px, but input maps in **logical** (DPI-scaled) px. At 225% they were 2.25x too large -> click offset grows with screen position. | Logical-coordinate fix implemented; **pending test** |
| 3 | Magnified view flickers / jumps while moving the cursor (off-centers and recenters rapidly) | GPU-rendered windows: Windows Terminal, browser, launcher | `Tracker::update` free/locked heuristic flip-flopped between snapping to `GetCursorPos` and integrating raw deltas | **Fixed** (hysteresis lock detector), unit-tested; user confirming |
| 4 | Large FPS drop when panning/zooming in games | Borderless games (KCD2 etc.) | Public API scales in DWM, drops game off the GPU fast path | Unchanged (see PERFORMANCE-FINDINGS.md); direction decision open |
| 4 | Large FPS drop when panning/zooming in games | Borderless games (KCD2 etc.) | Public API scales in DWM, drops game off the GPU fast path | **Resolved** via the transform engine (native-Magnifier composition parity, see HITCH-FINDINGS.md); a smaller residual felt-smoothness gap is still tracked there |

Issues **2 and 3 are very likely the same root cause** (the tracker's center diverging
from the true cursor), showing up as both a visual symptom (flicker) and an interaction
Expand Down Expand Up @@ -272,24 +274,30 @@ follows). Commit on `fix/interaction-bugs`.

## Issue 4 - In-game FPS hitching (cross-reference)

Documented and concluded in [`PERFORMANCE-FINDINGS.md`](PERFORMANCE-FINDINGS.md): the
large FPS drop while panning/zooming in borderless games is a ceiling of the public
Magnification API (scaling happens in DWM, dropping the game off its GPU fast path).
Only render-pipeline injection fully fixes it. **The direction decision (accept the
limit and finalize v1, vs. pivot to injection) is still open and not part of this
round.** Listed here only so the four issues live in one place.
Documented in [`PERFORMANCE-FINDINGS.md`](PERFORMANCE-FINDINGS.md), which concluded (for the
Magnification-API engine that existed at the time) that the large FPS drop while
panning/zooming in borderless games was a ceiling of the public API and that only
render-pipeline injection would fully fix it. **Resolved differently**: the transform engine
(issue #148, revived) drives DWM's own magnification channel the way native Magnifier does,
instead of the render engine's DXGI-capture pipeline, and the hybrid model picks it
automatically for fullscreen bordered games. Measured composition-rate parity with native
Magnifier over a real game (docs/HITCH-FINDINGS.md, 2026-09-28); a smaller residual
felt-smoothness gap (cursor handling, micro-holds) is still open there, but the large
architecture-level FPS drop this issue documented is fixed. Listed here only so the four
issues live in one place.

---

## Cross-cutting note: visual-only vs. input transform

Wind magnifies visually but does not remap input (`MagSetInputTransform` is
deliberately unused; it needs UIAccess). This is correct and click-accurate **as long
as the view stays centered on the true cursor**. Issues 2 and 3 both come back to the
center diverging from the true cursor, which breaks that assumption. If we ever do want
true decoupled-lens interaction (clicking the magnified target while the lens is offset
from the real cursor), that would require `MagSetInputTransform` and therefore working
UIAccess. Not needed to fix Issues 2/3 if we keep the center on the cursor.
This described the original Magnification-API engine these issues were filed against, which
magnified visually but never remapped input. It is no longer accurate for the current
transform engine: when UIAccess is available, the transform engine actively publishes
`MagSetInputTransform` per source-rect change (`magInputTransform=1`, default; issue #185,
docs/POINTER-HITTEST-FINDINGS.md) specifically to fix pointer-framework hover dead zones on
the desktop. The render engine still has no input transform and instead keeps the real cursor
welded/synced to the drawn one, so it stays correct and click-accurate only as long as that
weld keeps the view centered on the true cursor.

---

Expand Down
60 changes: 31 additions & 29 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,51 @@
Items that are agreed direction but not yet scheduled. One line of context each; details live
in the referenced issues/specs.

## One default engine (agreed direction, 2026-08-13)
## One default engine (agreed direction, 2026-08-13; desktop half shipped 2026-09-28)
Converge on the TRANSFORM engine as the single DEFAULT for every use case; the other models
stay shipped as deliberate alternatives ("second best"), never deleted. Prerequisites before
flipping any default: extended field testing of `desktopTransform=1` (endurance, the #189 perf
levers validated: ixDecimate / txKeepAliveMaxLevel / txMaxStepPct A/Bs), the spriteBand16
constant-size-cursor verdict, and the launch-quiesce (#187) holding across more game launches.
stay shipped as deliberate alternatives ("second best"), never deleted. The desktop half of
this is done: `desktopTransform=1` shipped as the default (issue #271/#272, owner decision
2026-09-28), so hybrid now picks transform on the desktop whenever the input-transform
availability probe succeeds. What is left before transform becomes the default for GAMES too:
the spriteBand16 constant-size-cursor verdict, and the launch-quiesce (#187) holding across
more game launches (`txKeepAliveMaxLevel` is retired - warm-keeping now runs through
`txWarmMode`/`txWarmHz`, see CLAUDE.md).
Spec: `docs/superpowers/specs/2026-08-12-one-model-transform-design.md` (P3/P4);
mechanism record: `docs/POINTER-HITTEST-FINDINGS.md`.

## Installer / public release
- **Bundle the NVIDIA MPO mitigation** (issue #148). The transform model's full zoom range over
games requires MPO hardware overlay planes to be OFF on NVIDIA systems; otherwise the driver's
16-bit plane-programming field overflows (|srcX*level| > 32767) and resets the GPU. Wind
detects the boot state and pan-walls the unsafe strip when MPO is on, but the BEST experience
needs the registry edit. The installer must:
- offer an opt-in step (checked by default on NVIDIA GPUs) that sets
`HKLM\SOFTWARE\Microsoft\Windows\Dwm\OverlayTestMode = DWORD 5` and explains the
reboot-to-apply + how to undo (delete the value);
- never set it silently (system-wide display setting; users must know it exists);
- the uninstaller should offer to remove the value.
Also report the underlying bug to NVIDIA with the minimal repro (issue #148 has the full
forensics: signed UIAccess rig, gl_churn/gl_stress stressors, event-log verdicts).
- **MPO-buster alternative** (unbuilt): a fullscreen alpha-1 click-through layered window shown
only during transform game sessions would force DWM to composite the game (off the hardware
plane), removing the need for the registry edit entirely. Prototype and A/B against the
registry route before the installer ships (evidence it works: the render model's alpha-1
primeReveal forces exactly this demotion, issue #90).
- **NVIDIA MPO mitigation** (issue #148): shipped, but through Settings rather than the
installer. WindConfig.exe's "Disable MPO" toggle (issue #164) writes
`HKLM\SOFTWARE\Microsoft\Windows\Dwm\OverlayTestMode = DWORD 5` via an elevated `reg.exe`
call, re-reads the real state instead of assuming it applied, and is boot-state aware (DWM
only reads the value at boot, so the UI says a restart is needed rather than implying the
toggle is instant). The installer itself still does not offer this as a first-run step; an
install-time prompt remains open if one is wanted. Also report the underlying bug to NVIDIA
with the minimal repro (issue #148 has the full forensics: signed UIAccess rig,
gl_churn/gl_stress stressors, event-log verdicts).
- **MPO buster** (issue #191): built and shipped, on by default (`mpoBuster=1`). During a
transform game session exposed to the MPO bug, Wind shows a fullscreen alpha-1
click-through ghost window that forces DWM to composite the game off the hardware overlay
plane, lifting the pan wall once the ghost settles. It runs alongside the registry route
rather than replacing it: the pan wall still applies unconditionally whenever sampling is
`nearest` and MPO is on (issue #243).

## Next session - start here (2026-07-26, updated at checkpoint 8a52040)
## Next session - start here (2026-07-26, updated at checkpoint 8a52040; size decision closed 2026-09-18)

**Cursor is DONE and field-verified**: the transform model welds the real OS cursor to the lens
point, so hover, dragging and clicks are all native. Items 1 below is therefore closed; what
remains of the cursor work is the SIZE decision:
point, so hover, dragging and clicks are all native. Items 1 below is therefore closed, and the
SIZE decision below is now closed too:

- With the transform engine a pointer can be **correctly placed OR constant size, never both**.
DWM magnifies layered windows too, so a screen-space marker lands off-screen once transformed
(verified: the pointer vanished at high zoom); in desktop space it sits exactly on target but
grows with the zoom, like the native Magnifier. The hardware pointer is the only constant-size
surface and it draws at its raw desktop position - the wrong place.
- So the standing "constant on-screen size" rule cannot be met by the transform engine. The
render engine does meet it (it draws its own frame) but runs its own loop at ~92fps with many
hitches while panning, versus 144fps/1 hitch for transform. **Max to decide**: live with a
growing pointer in games, or use render there.
- So the standing "constant on-screen size" rule cannot be met by the transform engine.
**Decided (owner decision, issue #253, 2026-09-18): the cursor grows with the zoom in every
engine**, including games via transform - this replaces the old constant-size rule.
`cursorConstantSize` (default 0) is an opt-in, render-only escape hatch for anyone who wants
the old constant-size look back.

## Superseded (kept for the reasoning)

Expand Down
Loading
Loading