You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Console Mode bundles two NirSoft tools inside the executable: MultiMonitorTool (displays) and SoundVolumeView (audio). They are free but closed source, and that blocks two things:
Code signing. The free SignPath Foundation program requires no proprietary code in the signed package. Unsigned releases show Windows SmartScreen's "unknown publisher" warning, which likely scares off more users than any bug. The repo side is ready (docs/CODE_SIGNING.md and the gated steps in release.yml, chore: prepare SignPath code signing (policy page, gated release steps) #90); this is the remaining blocker.
Antivirus false positives. The README already warns that antivirus may flag the bundled tools.
Replacing them with our own code (Win32 / Core Audio via P/Invoke) removes both problems and the dependency on nirsoft.net at build time.
Scope
Every call goes through two wrappers, so the migration can happen behind them:
Displays: MonitorService.InvokeMmt (~20 calls)
MultiMonitorTool command
Used for
Native replacement
/scomma + /HideInactiveMonitors 0
List monitors (active and inactive)
QueryDisplayConfig (CCD) + EnumDisplayDevices
/SaveConfig, /LoadConfig
Back up and restore the desk layout
Store CCD paths/modes ourselves; SetDisplayConfig to restore
/enable, /disable, /SetMonitors
Turn displays on/off, set positions
SetDisplayConfig (CCD topology)
/SetPrimary
Make the TV primary
ChangeDisplaySettingsEx with CDS_SET_PRIMARY + repositioning
/TurnOn, /TurnOff
Power displays (DDC/CI)
SetVCPFeature (dxva2 Monitor Configuration API)
/MoveWindow
Move a window to a monitor
SetWindowPos
Native/CcdHelper.cs already talks to CCD (ExtendAll), so there is a starting point.
IPolicyConfig (undocumented COM, the same one NirSoft, EarTrumpet and AudioSwitcher use)
/Enable
Enable a disabled output
IPolicyConfig::SetEndpointVisibility
/GetPercent, /SetVolume, /Mute, /Unmute
Session menu volume
IAudioEndpointVolume
Plan
Audio first. Smaller, well-known APIs, easy to verify by ear. One PR.
Displays in steps: listing → enable/disable/primary → backup/restore → DDC/CI power. Keep the NirSoft path as a fallback behind a setting until each step is verified on real hardware, then remove it.
Remove Get-NirSoftTools.ps1, the embedding in Publish-ConsoleMode.ps1 / EmbeddedFiles.cs, the MmtMissing / SvvMissing strings and the NirSoft section of THIRD_PARTY_NOTICES.md.
Apply to SignPath.
Testing
None of this can be checked in CI; it needs Windows with real displays. Each PR adds its manual checks to docs/TESTING.md, covering at least: two desk monitors + a TV that starts off, disconnect vs. black-overlay vs. DDC/CI modes, restore after Big Picture / Playnite exit, and an HDMI audio output that only appears once the TV is on. Pure logic (config parsing, layout math) goes into linked files with unit tests.
Done when
The release build downloads and embeds no NirSoft binary.
The manual checklist passes on at least one multi-monitor + TV setup.
Why
Console Mode bundles two NirSoft tools inside the executable: MultiMonitorTool (displays) and SoundVolumeView (audio). They are free but closed source, and that blocks two things:
docs/CODE_SIGNING.mdand the gated steps inrelease.yml, chore: prepare SignPath code signing (policy page, gated release steps) #90); this is the remaining blocker.Replacing them with our own code (Win32 / Core Audio via P/Invoke) removes both problems and the dependency on nirsoft.net at build time.
Scope
Every call goes through two wrappers, so the migration can happen behind them:
Displays:
MonitorService.InvokeMmt(~20 calls)/scomma+/HideInactiveMonitors 0QueryDisplayConfig(CCD) +EnumDisplayDevices/SaveConfig,/LoadConfigSetDisplayConfigto restore/enable,/disable,/SetMonitorsSetDisplayConfig(CCD topology)/SetPrimaryChangeDisplaySettingsExwithCDS_SET_PRIMARY+ repositioning/TurnOn,/TurnOffSetVCPFeature(dxva2 Monitor Configuration API)/MoveWindowSetWindowPosNative/CcdHelper.csalready talks to CCD (ExtendAll), so there is a starting point.Audio:
AudioService.InvokeSvv(~10 calls)/scomma+/ShowDisabledDevices/ShowUnpluggedDevicesIMMDeviceEnumerator(Core Audio)/SetDefault … allIPolicyConfig(undocumented COM, the same one NirSoft, EarTrumpet and AudioSwitcher use)/EnableIPolicyConfig::SetEndpointVisibility/GetPercent,/SetVolume,/Mute,/UnmuteIAudioEndpointVolumePlan
Get-NirSoftTools.ps1, the embedding inPublish-ConsoleMode.ps1/EmbeddedFiles.cs, theMmtMissing/SvvMissingstrings and the NirSoft section ofTHIRD_PARTY_NOTICES.md.Testing
None of this can be checked in CI; it needs Windows with real displays. Each PR adds its manual checks to
docs/TESTING.md, covering at least: two desk monitors + a TV that starts off, disconnect vs. black-overlay vs. DDC/CI modes, restore after Big Picture / Playnite exit, and an HDMI audio output that only appears once the TV is on. Pure logic (config parsing, layout math) goes into linked files with unit tests.Done when