Extends the desktop when a monitor Windows has never seen before shows up, instead of duplicating it, and optionally puts it above / below / left / right of the primary.
Runs from the tray. No admin rights, nothing machine-wide.
Windows already remembers per-monitor layout — arrange your screens once and reconnecting restores it. What it does not have is a default for a display it has never seen. That first connection duplicates, and you fix it by hand every time you meet a new monitor.
Because the monitor identity Windows uses carries the EDID model and the adapter output but no serial number, identical setups collide by design: arrange one desk in an open space and every identical desk is already arranged.
Run DisplayDefaults-x.y.z-setup.exe from the Releases page. A monitor icon
appears in the notification area.
There is no UAC prompt. The installer is per-user: it puts the exe in
%LOCALAPPDATA%\Programs\DisplayDefaults, not Program Files, and registers nothing
machine-wide.
The download is unsigned, so SmartScreen will show "Windows protected your PC". More info → Run anyway if you trust it, or build it yourself from source (below).
Right-click the tray icon to:
| Extend new displays | turn the whole thing on or off |
| Place new display | above / below / left / right of the primary, or let Windows decide |
| Forget known displays | treat every monitor as new again |
| Start with Windows | writes an HKCU\...\Run value |
Uninstall from Add/Remove Programs. Besides its own folder, the app writes an autostart value
under HKCU\...\Run and its settings in %LOCALAPPDATA%\DisplayDefaults; the uninstaller
removes both.
If you built from source and are running the exe directly, nothing knows about those two things, so clean them up yourself before deleting it:
DisplayDefaults.exe --uninstall
Safe to run twice, and it prints what it removed. This is why the release is an installer
rather than a bare exe: a loose exe has no uninstall hook, so winget uninstall would delete
it and leave the autostart value pointing at a file that is no longer there.
Your display arrangement is deliberately not reverted. That lives in Windows' own configuration store, and it is the arrangement you chose.
Needs the .NET 9 SDK. No other dependencies.
dotnet run -- --selftest # checks the logic, then prints the monitors it can see
dotnet run # tray icon
--selftest exits non-zero on failure, which is what gates the release in CI.
To reproduce a release build, publish the exe and then wrap it — the installer expects
publish\DisplayDefaults.exe to exist already:
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:Version=1.2.3 -o publish
iscc /DAppVersion=1.2.3 installer.iss
The exe is ~48 MB, almost entirely the .NET desktop runtime bundled so users need nothing
installed; the installer compresses it to ~43 MB. EnableCompressionInSingleFile in the
csproj already halves the exe from 107 MB, and trimming would go further but WinForms rejects
it (NETSDK1175), so that is the floor while the tray is WinForms.
| File | |
|---|---|
DisplayTopology.cs |
CCD interop: monitor identity, extend, placement |
NewDisplayWatcher.cs |
the feature — Start/Stop, seen-set, event wiring |
TrayApp.cs |
tray UI |
Program.cs |
host, selftest, --uninstall |
installer.iss |
per-user installer; [UninstallRun] is why it exists |
.github/workflows/release.yml |
tag → selftest → publish → installer → release |
winget/ |
manifests submitted to winget-pkgs |
After a release, point wingetcreate at the new asset to refresh winget/ and open the
update PR. The workflow prints the SHA256 in its job summary.
wingetcreate update pawneeranger.DisplayDefaults --version 1.2.3 --urls <installer-url>
NewDisplayWatcher is deliberately UI-free and has Start/Stop so it can drop into a
PowerToys module's enable/disable unchanged. Everything else is replaceable.
MIT. Same as PowerToys, so NewDisplayWatcher can be lifted into a module there without a
relicensing conversation.
- Placement is skipped when two monitors arrive at once; they would stack on the primary.
- Positions are physical pixels, so a mixed-DPI pair may get nudged by Windows afterwards.
- A monitor already connected and duplicated when the app starts is recorded as known, so it is never extended. Starting up should not yank your topology.