A PowerShell script that toggles between primary monitors and audio devices, perfect for switching between a desk setup and TV setup from the couch.
- Monitor Toggle: Toggles primary display between chosen monitors
- Window Management: Automatically moves all windows to the new primary monitor
- Audio Toggle: Switches default audio device between chosen audio devices
- State Persistence: Remembers the last selected monitor and audio device
- Simple Logging: Uses text files to track current state
- Window Customizations: Toggleable per-user window customizations for robust window management (must write you own nircmd.exe commands)
- PowerShell: Windows PowerShell (included with Windows)
- AutoHotkey v2 (Optional): Download and install AutoHotkey v2 for keyboard shortcuts
- Audio Devices: Ensure your audio device names match in
config.jsonand in Windows audio settings
Note: NirCmd and MultiMonitorTool are included in this repository - no additional downloads required! These tools are freeware and can be freely redistributed.
- Download or clone this repository
- Configure your audio devices:
- Open Windows Sound settings
- Rename your devices to "TV" and "Speakers" (or update
config.jsonto match your device names)
- Test the setup:
.\switch.ps1 - Setup Scheduled Task (Optional):
- Run the following command from the project directory as Administrator:
schtasks /Create /XML "MonitorSwitcher-Task.xml" /TN "MonitorSwitcher"
- This enables moving admin windows. See "Admin Window Movement Fix" section below for details.
- Install AutoHotkey v2 (Optional):
- Download and install AutoHotkey v2
- This enables keyboard shortcuts (Ctrl+Alt+M or Ctrl+Alt+S) to switch monitors
- See "AutoHotkey v2 Setup and Usage" section below for full details
That's it! All required tools (except AutoHotkey) are included in the repository.
- Install AutoHotkey v2 if you haven't already
- Run
switch.ahkby double-clicking it or running it from command line - Press Ctrl+Alt+M or Ctrl+Alt+S to toggle monitors and audio
- The script will show a brief tooltip notification when switching
- Add a shortcut to
switch.ahkinC:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUpif you want it to run on startup
Run the script from PowerShell:
.\switch.ps1Double-click the switch.ps1 script file in Windows Explorer.
Add a function to your PowerShell profile for easy command-line access:
-
Open your PowerShell profile:
notepad $PROFILE -
Add this function:
function switchmon { & "\path\to\switch.ps1" }
-
Save and reload:
. $PROFILE
-
Use the command:
switchmon
- Toggles between chosen displays in
config.json - Uses MultiMonitorTool's
/SetPrimarycommand - Automatically moves all windows to the new primary monitor using MultiMonitorTool's
/MoveWindow Primary Allcommand - State is stored in
./SwapMonitorsLog.txtby default
- Toggles between "TV" and "Headphones" audio devices
- Uses NirCmd's
setdefaultsounddevicecommand - State is stored in
./SwapAudioLog.txtby default
The script uses a config.json file for easy customization. No need to edit the PowerShell script directly!
{
"monitors": {
"primary": 1,
"secondary": 4
},
"audio": {
"device1": "TV",
"device2": "Headphones"
},
"paths": {
"monitorLog": "/path/to/log/folder/SwapMonitorsLog.txt",
"audioLog": "/path/to/log/folder/SwapAudioLog.txt"
},
"notifications": {
"enabled": true,
"monitorMessage": "Switched to Monitor {0}",
"audioMessage": "Switched to {0}"
}
}Note: If your monitors don't seem to match Windows display settings, use get_monitor_numbers.ps1 to get their actual numbers or use trial and error.
primary: First monitor number (default: 1)secondary: Second monitor number (default: 4)
device1: First audio device name (default: "TV")device2: Second audio device name (default: "Headphones")
monitorLog: Path for monitor state log (supports environment variables)audioLog: Path for audio state log (supports environment variables)
enabled: Enable/disable console notifications (default: true)monitorMessage: Message format for monitor switches (use {0} for monitor number)audioMessage: Message format for audio switches (use {0} for device name)
enabled: Enable/disable per-user window customizations (default: true)- Set this to
falseuntil you define your own customizations
- Set this to
rules: Array of customization rules that run when switching to the primary monitor
The window customizations feature allows you to automatically position specific windows at custom locations, sizes, or monitors when switching to your primary monitor (desk setup). This is perfect for multi-monitor setups where you want certain apps to always go to specific monitors.
When you switch to your primary monitor (typically your desk setup), the customization rules run after all windows are moved to the primary display. Each rule can:
- Move a specific window to a different monitor
- Run custom NirCmd commands to resize, maximize, or reposition the window
- Or run any other arbirtrary PowerShell commands
When you switch to your secondary monitor (typically your TV), all windows simply move to that monitor and are maximized with no customizations applied.
Each rule in the windowCustomizations.rules array requires:
findMethod: How to find the window"Process"- Find by process name (e.g., "Discord.exe")"Title"- Find by window title (e.g., "WhatsApp")
findValue: The process name or window title to matchmonitor: Target monitor number to move the window tocommand(optional): Additional PowerShell command to run for advanced customization
{
"windowCustomizations": {
"enabled": true,
"rules": [
{
"findMethod": "Process",
"findValue": "Discord.exe",
"monitor": 3,
"command": "if ($customization.findValue -eq 'Discord.exe') { & nircmd.exe win max process 'Discord.exe'; & Write-Host 'Discord maximized' }"
},
{
"findMethod": "Title",
"findValue": "WhatsApp",
"monitor": 4,
"_comment": "if WhatsApp, make normal, activate, and resize to right half of screen",
"command": "if ($customization.findValue -eq 'WhatsApp') { & nircmd.exe win normal ititle 'WhatsApp'; & nircmd.exe win activate ititle 'WhatsApp'; & nircmd.exe win setsize ititle 'WhatsApp' -1287 0 1294 1039; & Write-Host 'WhatsApp snapped to right' }"
}
]
}
}Move Discord to a specific monitor:
{
"findMethod": "Process",
"findValue": "Discord.exe",
"monitor": 3
}Move and maximize Spotify:
{
"findMethod": "Process",
"findValue": "Spotify.exe",
"monitor": 4,
"command": "if ($customization.findValue -eq 'Spotify.exe') { & nircmd.exe win max process 'Spotify.exe' }"
}Snap a window to the right half of the screen:
{
"findMethod": "Title",
"findValue": "WhatsApp",
"monitor": 4,
"command": "if ($customization.findValue -eq 'WhatsApp') { & nircmd.exe win normal ititle 'WhatsApp'; & nircmd.exe win setsize ititle 'WhatsApp' -1287 0 1294 1039 }"
}The command field supports any NirCmd window commands. Important: Always wrap commands in an if statement that checks $customization.findValue to ensure the command only applies to the intended window.
Common command patterns:
- Maximize:
if ($customization.findValue -eq 'Discord.exe') { & nircmd.exe win max process 'Discord.exe' }
- Minimize:
if ($customization.findValue -eq 'Spotify.exe') { & nircmd.exe win min process 'Spotify.exe' }
- Normal/Restore:
if ($customization.findValue -eq 'WhatsApp') { & nircmd.exe win normal ititle 'WhatsApp' }
- Activate/Focus:
if ($customization.findValue -eq 'WhatsApp') { & nircmd.exe win activate ititle 'WhatsApp' }
- Resize:
if ($customization.findValue -eq 'WhatsApp') { & nircmd.exe win setsize ititle 'WhatsApp' x y width height }
- Multiple commands (chain with semicolons):
if ($customization.findValue -eq 'WhatsApp') { & nircmd.exe win normal ititle 'WhatsApp'; & nircmd.exe win activate ititle 'WhatsApp'; & nircmd.exe win setsize ititle 'WhatsApp' -1287 0 1294 1039 }
For more NirCmd commands, see the NirCmd documentation.
- Use
_commentfields: Add comments to your rules for documentation (they're ignored by the script) - Test your commands: Run NirCmd commands manually first to get the right coordinates
- Find process names: Use Task Manager to find the exact process name
- Find window titles: Check in MultiMonitorTool GUI
- Disable temporarily: Set
"enabled": falseto disable all customizations without deleting rules - Order matters: Rules run in the order they appear in the array
{
"monitors": {
"primary": 2,
"secondary": 3
}
}{
"audio": {
"device1": "Speakers",
"device2": "Headset"
}
}{
"paths": {
"monitorLog": "C:\\Logs\\monitor_state.txt",
"audioLog": "C:\\Logs\\audio_state.txt"
}
}{
"notifications": {
"enabled": false
}
}If you notice that some windows (especially admin windows like Terminal, Task Manager, TreeSize) don't move when switching monitors, this is due to Windows security restrictions (UIPI). The solution is to run the script with elevated privileges using Task Scheduler.
- Open PowerShell as Administrator
- Navigate to the monitor_switcher directory
- Import the scheduled task:
schtasks /Create /XML "MonitorSwitcher-Task.xml" /TN "MonitorSwitcher"
Once configured, you can run the elevated version without UAC prompts:
From AutoHotkey: The Ctrl+Alt+S hotkey in switch.ahk already uses the elevated method by calling admin_scheduled_task_switch.ps1 which in turn calls the scheduled task
From command line:
.\admin_scheduled_task_switch.ps1schtasks /Delete /TN "MonitorSwitcher" /F- Ctrl+Alt+M: Uses original
switch.ps1(may not move admin windows) - Ctrl+Alt+S: Uses
admin_scheduled_task_switch.ps1via Task Scheduler (moves all windows including admin)
- "nircmd.exe not found": Ensure
nircmd.exeis in the same folder asswitch.ps1 - "MultiMonitorTool.exe not found": Ensure
MultiMonitorTool.exeis in the same folder asswitch.ps1 - Audio device not switching: Check that your audio devices are named exactly as configured in
config.json - Monitor not switching: Verify the display numbers in
config.jsonmatch your setup - Admin windows not moving: Follow the "Admin Window Movement Fix" section above to set up Task Scheduler
- "Configuration file not found": Ensure
config.jsonis in the same directory asswitch.ps1 - "Failed to parse configuration file": Check that
config.jsonis valid JSON syntax - "Invalid JSON": Use a JSON validator to check your configuration file
- Settings not applying: Restart the script after changing
config.json
- Device names must match exactly: Check Windows Sound settings for exact device names
- Case sensitive: Device names are case-sensitive in the configuration
- Special characters: Escape quotes and special characters in device names
- Test audio switching: Use NirCmd directly to test:
nircmd setdefaultsounddevice "YourDeviceName" 1
The script creates two log files in the project root directory:
SwapMonitorsLog.txt- Tracks current monitor stateSwapAudioLog.txt- Tracks current audio device state
The included switch.ahk script provides convenient keyboard shortcuts for monitor switching.
- AutoHotkey v2: Download from autohotkey.com
- Installation: Run the installer and ensure AutoHotkey v2 is properly installed
- Verification: Right-click any
.ahkfile and verify "Run Script" appears in the context menu
- Ctrl+Alt+M: Toggle monitors and audio (primary shortcut)
- Ctrl+Alt+S: Toggle monitors and audio (scheduled task)
- Feel free to edit these to your liking in
switch.ahk
- Feel free to edit these to your liking in
- Background Execution: Runs the PowerShell script without visible windows
- Visual Feedback: Shows tooltip notifications when switching
- Single Instance: Prevents multiple copies from running simultaneously
- Startup Confirmation: Displays notification when script loads
- Modern Syntax: Uses AutoHotkey v2 for better performance and compatibility
- Navigate to the script folder
- Double-click
switch.ahk - Look for the tooltip notification confirming the script loaded
- The script will run until you close it or restart Windows
# Navigate to script directory
cd "C:\path\to\monitor_switcher"
# Run the script
switch.ahk- Right-click
switch.ahk - Select "Run Script" from the context menu
- Press
Win + R, typeshell:startup, press Enter - Right-click in the folder → New → Shortcut
- Browse to and select
switch.ahk - Name it "Monitor Switcher" and click Finish
- The script will now start automatically with Windows
- Icon: Look for the AutoHotkey icon in the system tray
- Right-click: Access context menu with options to pause, reload, or exit
- Exit: Right-click tray icon → Exit to stop the script
- Reload: Right-click tray icon → Reload Script (useful after editing)
- Pause: Right-click tray icon → Pause Script (temporarily disable hotkeys)
- Edit: Right-click tray icon → Edit Script (opens in default editor)
This project is open source and available under the MIT License.