Repository files navigation

DisplayDefaults

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.

Why

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.

Install

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 displaysturn the whole thing on or off
Place new displayabove / below / left / right of the primary, or let Windows decide
Forget known displaystreat every monitor as new again
Start with Windowswrites an HKCU\...\Run value

Uninstalling

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.

Build

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.

Layout

File
DisplayTopology.csCCD interop: monitor identity, extend, placement
NewDisplayWatcher.csthe feature — Start/Stop, seen-set, event wiring
TrayApp.cstray UI
Program.cshost, selftest, --uninstall
installer.issper-user installer; [UninstallRun] is why it exists
.github/workflows/release.ymltag → 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.

License

MIT. Same as PowerToys, so NewDisplayWatcher can be lifted into a module there without a relicensing conversation.

Known limits

  • 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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

DisplayDefaults

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.

Why

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.

Install

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 displaysturn the whole thing on or off
Place new displayabove / below / left / right of the primary, or let Windows decide
Forget known displaystreat every monitor as new again
Start with Windowswrites an HKCU\...\Run value

Uninstalling

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.

Build

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.

Layout

File
DisplayTopology.csCCD interop: monitor identity, extend, placement
NewDisplayWatcher.csthe feature — Start/Stop, seen-set, event wiring
TrayApp.cstray UI
Program.cshost, selftest, --uninstall
installer.issper-user installer; [UninstallRun] is why it exists
.github/workflows/release.ymltag → 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.

License

MIT. Same as PowerToys, so NewDisplayWatcher can be lifted into a module there without a relicensing conversation.

Known limits

  • 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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

DisplayDefaults

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.

Why

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.

Install

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 displaysturn the whole thing on or off
Place new displayabove / below / left / right of the primary, or let Windows decide
Forget known displaystreat every monitor as new again
Start with Windowswrites an HKCU\...\Run value

Uninstalling

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.

Build

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.

Layout

File
DisplayTopology.csCCD interop: monitor identity, extend, placement
NewDisplayWatcher.csthe feature — Start/Stop, seen-set, event wiring
TrayApp.cstray UI
Program.cshost, selftest, --uninstall
installer.issper-user installer; [UninstallRun] is why it exists
.github/workflows/release.ymltag → 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.

License

MIT. Same as PowerToys, so NewDisplayWatcher can be lifted into a module there without a relicensing conversation.

Known limits

  • 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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

DisplayDefaults

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.

Why

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.

Install

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 displaysturn the whole thing on or off
Place new displayabove / below / left / right of the primary, or let Windows decide
Forget known displaystreat every monitor as new again
Start with Windowswrites an HKCU\...\Run value

Uninstalling

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.

Build

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.

Layout

File
DisplayTopology.csCCD interop: monitor identity, extend, placement
NewDisplayWatcher.csthe feature — Start/Stop, seen-set, event wiring
TrayApp.cstray UI
Program.cshost, selftest, --uninstall
installer.issper-user installer; [UninstallRun] is why it exists
.github/workflows/release.ymltag → 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.

License

MIT. Same as PowerToys, so NewDisplayWatcher can be lifted into a module there without a relicensing conversation.

Known limits

  • 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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

DisplayDefaults

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.

Why

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.

Install

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 displaysturn the whole thing on or off
Place new displayabove / below / left / right of the primary, or let Windows decide
Forget known displaystreat every monitor as new again
Start with Windowswrites an HKCU\...\Run value

Uninstalling

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.

Build

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.

Layout

File
DisplayTopology.csCCD interop: monitor identity, extend, placement
NewDisplayWatcher.csthe feature — Start/Stop, seen-set, event wiring
TrayApp.cstray UI
Program.cshost, selftest, --uninstall
installer.issper-user installer; [UninstallRun] is why it exists
.github/workflows/release.ymltag → 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.

License

MIT. Same as PowerToys, so NewDisplayWatcher can be lifted into a module there without a relicensing conversation.

Known limits

  • 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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

DisplayDefaults

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.

Why

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.

Install

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 displaysturn the whole thing on or off
Place new displayabove / below / left / right of the primary, or let Windows decide
Forget known displaystreat every monitor as new again
Start with Windowswrites an HKCU\...\Run value

Uninstalling

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.

Build

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.

Layout

File
DisplayTopology.csCCD interop: monitor identity, extend, placement
NewDisplayWatcher.csthe feature — Start/Stop, seen-set, event wiring
TrayApp.cstray UI
Program.cshost, selftest, --uninstall
installer.issper-user installer; [UninstallRun] is why it exists
.github/workflows/release.ymltag → 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.

License

MIT. Same as PowerToys, so NewDisplayWatcher can be lifted into a module there without a relicensing conversation.

Known limits

  • 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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

DisplayDefaults

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.

Why

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.

Install

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 displaysturn the whole thing on or off
Place new displayabove / below / left / right of the primary, or let Windows decide
Forget known displaystreat every monitor as new again
Start with Windowswrites an HKCU\...\Run value

Uninstalling

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.

Build

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.

Layout

File
DisplayTopology.csCCD interop: monitor identity, extend, placement
NewDisplayWatcher.csthe feature — Start/Stop, seen-set, event wiring
TrayApp.cstray UI
Program.cshost, selftest, --uninstall
installer.issper-user installer; [UninstallRun] is why it exists
.github/workflows/release.ymltag → 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.

License

MIT. Same as PowerToys, so NewDisplayWatcher can be lifted into a module there without a relicensing conversation.

Known limits

  • 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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

DisplayDefaults

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.

Why

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.

Install

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 displaysturn the whole thing on or off
Place new displayabove / below / left / right of the primary, or let Windows decide
Forget known displaystreat every monitor as new again
Start with Windowswrites an HKCU\...\Run value

Uninstalling

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.

Build

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.

Layout

File
DisplayTopology.csCCD interop: monitor identity, extend, placement
NewDisplayWatcher.csthe feature — Start/Stop, seen-set, event wiring
TrayApp.cstray UI
Program.cshost, selftest, --uninstall
installer.issper-user installer; [UninstallRun] is why it exists
.github/workflows/release.ymltag → 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.

License

MIT. Same as PowerToys, so NewDisplayWatcher can be lifted into a module there without a relicensing conversation.

Known limits

  • 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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages