Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate

Multi-monitor mode switching script


  • Please log in to reply
2 replies to this topic
jvfff
  • Guests
  • Last active:
  • Joined: --
Just thought I'd share the script. I designed it to switch preset modes in a loop using a single hotkey. It's not 100% generic and based on certain assumptions. Go ahead and modify whatever it is that doesn't fit your requirements or missing.

I have added a mouse pointer speed adjustment based on the primary monitor selected - I slow down the pointer if the Primary monitor is selected to be my TV, which I use in pair with IR keyboard that has a very sensitive mouse.

I also always move the taskbar to the primary monitor.

There are some delays that could be too long or too short for your particular machine. Those may need some tweaking.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Initialize multi-monitor states.

InitMultiMonitor()


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set next configuration.

#F12:: SetNextDisplayConfiguration()


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Initialization.

InitMultiMonitor()
{
  ; Declare global variables.
  global MMTest1
  global MMTest2

  ; Reset counts.
  global MultiMonitorDisplayCount := 0
  global MultiMonitorModeCount    := 0
  global MultiMonitorConfigCount  := 0
  global MultiMonitorConfigIndex  := -1

  ; Add devices.
  FlatPanel := AddDisplay("\\.\DISPLAY1", "Iiyama Flat Panel", 14)
  TVMonitor := AddDisplay("\\.\DISPLAY2", "Sony XBR 40",        1)

  ; Add modes.
  MonitorOff       := AddDisplayMode(   0,    0,  0,  0)
  Mode720x480x32   := AddDisplayMode( 720,  480, 32, 60)
  Mode800x600x32   := AddDisplayMode( 800,  600, 32, 60)
  Mode1024x768x32  := AddDisplayMode(1024,  768, 32, 60)
  Mode1280x1024x32 := AddDisplayMode(1280, 1024, 32, 60)

  ; Add configurations.
  AddDisplayConfig(FlatPanel, Mode1280x1024x32, TVMonitor, Mode1280x1024x32)
  AddDisplayConfig(TVMonitor, Mode720x480x32,   FlatPanel, Mode1024x768x32)
  AddDisplayConfig(FlatPanel, Mode1024x768x32,  TVMonitor, Mode720x480x32)

  ; Define status window.
  Gui, 5:Font, s16, Arial
  Gui, 5:Add, Text,, The following configuration is being set:
  Gui, 5:Add, Text, vMMTest1 w540,
  Gui, 5:Add, Text, vMMTest2 w540,
  Gui, 5:Show, w550 Hide, Please Wait...

  ; Set configuration.
  SetNextDisplayConfiguration()
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Create new configuration entry.

AddDisplayConfig(PrimaryDisplay, PrimaryMode, SecondaryDisplay, SecondaryMode)
{                     
  ; All variables in this function are global.
  global

  ; New item index.
  Result := MultiMonitorConfigCount

  ; Store new item
  Config%MultiMonitorConfigCount%PrimaryDisplay   := PrimaryDisplay
  Config%MultiMonitorConfigCount%PrimaryMode      := PrimaryMode
  Config%MultiMonitorConfigCount%SecondaryDisplay := SecondaryDisplay
  Config%MultiMonitorConfigCount%SecondaryMode    := SecondaryMode

  ; Advance the count.
  MultiMonitorConfigCount := MultiMonitorConfigCount + 1

  ; Return item's index.
  return Result
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Create new display entry.

AddDisplay(Name, Description, MouseSpeed)
{                     
  ; All variables in this function are global.
  global

  ; New item index.
  Result := MultiMonitorDisplayCount

  ; Store new item
  Monitor%MultiMonitorDisplayCount%Name        := Name
  Monitor%MultiMonitorDisplayCount%Description := Description
  Monitor%MultiMonitorDisplayCount%MouseSpeed  := MouseSpeed

  ; Advance the count.
  MultiMonitorDisplayCount := MultiMonitorDisplayCount + 1

  ; Return item's index.
  return Result
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Create new mode entry.

AddDisplayMode(Width, Height, Depth, Refresh)
{                     
  ; All variables in this function are global.
  global

  ; New item index.
  Result := MultiMonitorModeCount

  ; Store new item
  Mode%MultiMonitorModeCount%Width   := Width
  Mode%MultiMonitorModeCount%Height  := Height
  Mode%MultiMonitorModeCount%Depth   := Depth
  Mode%MultiMonitorModeCount%Refresh := Refresh

  ; Advance the count.
  MultiMonitorModeCount := MultiMonitorModeCount + 1

  ; Return item's index.
  return Result
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Returns formatted mode description.

GetModeDescription(DisplayDescription, Width, Height, Depth, Refresh)
{
  if ((Width == 0) or (Height == 0))
  {
    Result := "Off"
  }
  else
  {
    Result := DisplayDescription
            . " at "
            . Width . "x" . Height
            . "x" . Depth
            . "@" . Refresh . "Hz"
  }

  return Result
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Returns display info by its index.

GetDisplayInfo(Index, ByRef Name, ByRef Description, ByRef MouseSpeed = 0)
{                     
  ; All variables in this function are global.
  global

  Name        := Monitor%Index%Name
  Description := Monitor%Index%Description
  MouseSpeed  := Monitor%Index%MouseSpeed
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Returns display info by its index.

GetModeInfo(Index, ByRef Width, ByRef Height, ByRef Depth, ByRef Refresh)
{                     
  ; All variables in this function are global.
  global

  Width   := Mode%Index%Width
  Height  := Mode%Index%Height
  Depth   := Mode%Index%Depth
  Refresh := Mode%Index%Refresh
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Returns display info by its index.

GetConfigInfo( Index
             , ByRef PrimaryDisplay, ByRef PrimaryMode
             , ByRef SecondaryDisplay, ByRef SecondaryMode)
{                     
  ; All variables in this function are global.
  global

  PrimaryDisplay   := Config%Index%PrimaryDisplay
  PrimaryMode      := Config%Index%PrimaryMode
  SecondaryDisplay := Config%Index%SecondaryDisplay
  SecondaryMode    := Config%Index%SecondaryMode
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Returns the position and size of the specified display.

GetDisplayPosition(DeviceName, ByRef X, ByRef Y, ByRef Width, ByRef Height)
{
  ; DEVMODE size.
  DevModeSize := 156

  ; Reserve the space for the structure and clear with zeros.
  VarSetCapacity(DevMode, DevModeSize, 0)

  ; DEVMODE.dmSize = sizeof(DEVMODE).
  NumPut(DevModeSize, DevMode, 36, UShort)

  ; Query current settings.
  status := DllCall( "EnumDisplaySettings"
                   , "str",  DeviceName
                   , "uint", -1         ; ENUM_CURRENT_SETTINGS
                   , "uint", &DevMode)

  ; Verify the status.
  if (!status)
  {
    return False
  }

  ; Fetch the values.
  X := NumGet(DevMode, 44)
  Y := NumGet(DevMode, 48)

  Width  := NumGet(DevMode, 108)
  Height := NumGet(DevMode, 112)

  ; Success.
  return True
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Schedule but do not perform a display mode change.

ScheduleModeChange( DeviceName
                  , Primary
                  , X, Y
                  , Width, Height
                  , Depth = 0
                  , Refresh = 0)
{
  ; Set default flags.
  ModeFlags := 0x00000001  ; CDS_UPDATEREGISTRY
             | 0x10000000  ; CDS_NORESET

  ; Set primary.
  if (Primary)
  {
    ModeFlags := ModeFlags
               | 0x00000010
  }

  ; DEVMODE size.
  DevModeSize := 156

  ; Reserve the space for the structure and clear with zeros.
  VarSetCapacity(DevMode, DevModeSize, 0)

  ; DEVMODE.dmSize = sizeof(DEVMODE).
  NumPut(DevModeSize, DevMode, 36, UShort)

  ; Set additional default mode flags.
  DevModeFlags := 0x00000020  ; DM_POSITION
                | 0x00080000  ; DM_PELSWIDTH
                | 0x00100000  ; DM_PELSHEIGHT

  ; Is color depth specified?
  if (Depth)
  {
    DevModeFlags := DevModeFlags
                  | 0x00040000  ; DM_BITSPERPEL

    ; DEVMODE.dmBitsPerPel = Depth.
    NumPut(Depth, DevMode, 104)
  }

  ; Is refresh rate specified?
  if (Refresh)
  {
    DevModeFlags := DevModeFlags
                  | 0x00400000  ; DM_DISPLAYFREQUENCY

    ; DEVMODE.dmDisplayFrequency = Refresh.
    NumPut(Refresh, DevMode, 120)
  }

  ; DEVMODE.dmFields = DevModeFlags.
  NumPut(DevModeFlags, DevMode, 40)

  ; DEVMODE.dmPosition.(x, y) = (X, Y).
  NumPut(X, DevMode, 44)
  NumPut(Y, DevMode, 48)

  ; DEVMODE.(dmPelsWidth, dmPelsHeight) = (Width, Height).
  NumPut(Width,  DevMode, 108)
  NumPut(Height, DevMode, 112)

  ; Change mode.
  status := DllCall( "ChangeDisplaySettingsEx"
                   , "str",  DeviceName  ; Name of display device.
                   , "uint", &DevMode    ; Graphics mode.
                   , "uint", 0           ; Not used; must be NULL.
                   , "uint", ModeFlags   ; Mode flags.
                   , "uint", 0)          ; No video parameters.

  ; Check the status.
  if (status)
  {
    MsgBox,
    (
      Failed to schedule a mode change:
      Code returned: %status%
      Device name: %DeviceName%
      Primary: %Primary%
      Position: (%X%, %Y%)
      Mode size: (%Width%x%Height%)
      Color depth: %Depth%
      Refresh Rate: %Refresh%
    )
  }

  ; Return result.
  return status
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Set specified mode for two monitors at the same time.
;
; Possible return values:
;   DISP_CHANGE_SUCCESSFUL  =  0
;   DISP_CHANGE_RESTART     =  1
;   DISP_CHANGE_FAILED      = -1
;   DISP_CHANGE_BADMODE     = -2
;   DISP_CHANGE_NOTUPDATED  = -3
;   DISP_CHANGE_BADFLAGS    = -4
;   DISP_CHANGE_BADPARAM    = -5
;   DISP_CHANGE_BADDUALVIEW = -6

SetMultiMonitorConfiguration(ConfigIndex)
{
  ; Show the status window.
  Gui, 5:Show

  ; Get the configuration details.
  GetConfigInfo( ConfigIndex
               , PrimaryDeviceIndex, PrimaryModeIndex
               , SecondaryDeviceIndex, SecondaryModeIndex)

  ; Fetch display info.
  GetDisplayInfo( PrimaryDeviceIndex
                , PrimaryName, PrimaryDescription, MouseSpeed)
  GetDisplayInfo( SecondaryDeviceIndex
                , SecondaryName, SecondaryDescription)

  ; Fetch mode info.
  GetModeInfo( PrimaryModeIndex
             , PrimaryWidth, PrimaryHeight
             , PrimaryDepth, PrimaryRefresh)
  GetModeInfo( SecondaryModeIndex
             , SecondaryWidth, SecondaryHeight
             , SecondaryDepth, SecondaryRefresh)

  ; Get descriptions of the modes for the user.
  PrimaryModeDescription := GetModeDescription( PrimaryDescription
                                              , PrimaryWidth, PrimaryHeight
                                              , PrimaryDepth, PrimaryRefresh)

  SecondaryModeDescription := GetModeDescription( SecondaryDescription
                                                , SecondaryWidth, SecondaryHeight
                                                , SecondaryDepth, SecondaryRefresh)

  ; Update the dialog.
  GuiControl, 5:Text, MMTest1, Primary: %PrimaryModeDescription%
  GuiControl, 5:Text, MMTest2, Secondary: %SecondaryModeDescription%

  ; Assume a failure.
  status := -1

  ; Use a loop for easy error handling.
  Loop
  {
    ; Primary has to be valid.
    if ((PrimaryWidth == 0) or (PrimaryHeight == 0))
    {
      Break
    }

    ; Determine the requested mode validity.
    SecondaryValid := ((SecondaryWidth == 0) or (SecondaryHeight == 0))
      ? False
      : True

    ; Schedule primary mode change.
    status := ScheduleModeChange( PrimaryName, True
                                , 0, 0
                                , PrimaryWidth, PrimaryHeight
                                , PrimaryDepth, PrimaryRefresh)
    if (status)
    {
      break
    }

    ; Schedule secondary mode change.
    status := ScheduleModeChange( SecondaryName, False
                                , PrimaryWidth, 0
                                , SecondaryWidth, SecondaryHeight
                                , SecondaryDepth, SecondaryRefresh)
    if (status)
    {
      break
    }

    ; Apply the changes.
    status := DllCall( "ChangeDisplaySettingsEx"
                     , "uint", 0
                     , "uint", 0
                     , "uint", 0
                     , "uint", 0
                     , "uint", 0)
    if (status)
    {
      MsgBox, Failed to apply mode changes: %status%
      Break
    }

    ; Wait for everything to settle.
    Sleep, 5000

    ; Move the taskbar to the primary display.
    MoveTaskBar(PrimaryName)

    ; Set mouse pointer speed.
    DllCall( "SystemParametersInfo"
           , "uint", 0x0071    ; SPI_SETMOUSESPEED
           , "uint", 0
           , "uint", MouseSpeed
           , "uint", 0)

    ; Terminate the loop.
    break
  }

  ; Hide the status window.
  Gui, 5:Hide
  SoundPlay, *-1

  return status
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Unlocks the taskbar.

UnlockTaskbar(Unlock = True, Wait = True)
{
  ; Get the taskbar window dimensions.
  WinGetPos x, y, width, height, ahk_class Shell_TrayWnd

  ; Determine the taskbar status.
  ; (the height of a locked task bar should be a multiple of 30)
  locked := Mod(height, 30) == 0

  ; Should we still toggle?
  if ((Unlock ^ locked) == 0)
  {
    ; All mouse coordinates are now relative to the screen.
    CoordMode, Mouse, Screen

    ; Save current mouse position.
    MouseGetPos, currX, currY

    ; Calculate the click point.
    clkX := x + width  - 1
    clkY := y + height - 1

    ; Invoke the menu and select 'Lock the Taskbar'
    Click Right %clkX%, %clkY%
    Send, l

    ; Move the mouse to the original position.
    MouseMove, %currX%, %currY%, 0

    ; Restore default.
    CoordMode, Mouse, Relative

    ; Wait until everything is settled.
    if (Wait)
    {
      Sleep, 2000
    }
  }
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Moves the taskbar to the bottom of the specified display.

MoveTaskBar(DeviceName)
{
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ; Get positions of the display and the taskbar.

  ; Get the display position.
  if (!GetDisplayPosition(DeviceName, dspX, dspY, dspWidth, dspHeight))
  {
    return
  }

  ; Get the taskbar window dimensions.
  WinGetPos tbrX, tbrY, tbrWidth, tbrHeight, ahk_class Shell_TrayWnd


  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ; Verify whether the taskbar is already within the display.

  if (    (tbrX >= dspX) and (tbrX + tbrWidth  <= dspX + dspWidth)
      and (tbrY >= dspY) and (tbrY + tbrHeight <= dspY + dspHeight))
  {
    return
  }


  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ; Prepare for the move.

  ; Unlock the taskbar.
  UnlockTaskbar()

  ; Change to absolute coordinate system.
  CoordMode, Mouse, Screen

  ; Save the current mouse position.
  MouseGetPos, currX, currY


  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ; Move the taskbar.

  ; Get the taskbar window unlocked dimensions.
  WinGetPos tbrX, tbrY, tbrWidth, tbrHeight, ahk_class Shell_TrayWnd

  ; Determine target mouse coordinates.
  trgX := dspX + dspWidth // 2
  trgY := dspY + dspHeight - tbrHeight

  ; Initiate taskbar moving.
  Send, {LWin}!{Space}m{Right}
  Sleep, 500

  ; Move the mouse and the taskbar with it.
  MouseMove, %trgX%, %trgY%, 0
  Send, {Enter}
  Sleep, 1000


  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ; Sometimes during the move the size of the bar gets reset.
  ; Here we make sure the size stays the same.

  ; Get the new size of the taskbar.
  WinGetPos newX, newY, newWidth, newHeight, ahk_class Shell_TrayWnd

  ; Compute the size delta.
  sizeDelta := newHeight - tbrHeight

  ; Adjust if required.
  if (sizeDelta)
  {
    ; Make the cursor go a bit more to make sure the size changes.
    if (sizeDelta < 0)
    {
      sizeDelta := sizeDelta - 1
    }
    else
    {
      sizeDelta := sizeDelta + 1
    }

    ; Initiate taskbar sizing.
    Send, {LWin}!{Space}s{Up}
    Sleep, 500

    ; Move the mouse and resize the taskbar.
    MouseMove, 0, %sizeDelta%, 0, R
    Send, {Enter}
    Sleep, 1000
  }

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ; Finalize the move.

  ; Move the mouse to the original position.
  MouseMove, %currX%, %currY%, 0

  ; Restore relative coordinate system.
  CoordMode, Mouse, Relative

  ; Lock the taskbar.
  UnlockTaskbar(False)
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Set next display configuration.

SetNextDisplayConfiguration()
{
  ; Access global variables.
  global MultiMonitorConfigIndex
  global MultiMonitorConfigCount

  ; Advance to the next configuration.
  MultiMonitorConfigIndex := MultiMonitorConfigIndex + 1

  ; Rewind.
  if (MultiMonitorConfigIndex == MultiMonitorConfigCount)
  {
    MultiMonitorConfigIndex := 0
  }

  ; Set configuration.
  SetMultiMonitorConfiguration(MultiMonitorConfigIndex)
}


Wlk
  • Guests
  • Last active:
  • Joined: --
Great piece of code, just as i'm not programmer myself and cant modify this, is there any chance you can help me do it? I need simple macro for moving taskbar between two displays. My problem is that I have 2xLCD and TV setup and switching between Nvidia profiles for 2xLCD and TV alone. Unfortunately after switch back to displays taskbar is moved to primary screen, but I want it on secondary.

Any chance to make some piece of code to fit my problem? I can fill display names or other stuff, or modify it a bit, but with actual code you posted I'm lost :(

amik
  • Guests
  • Last active:
  • Joined: --
[quote]My problem is that I have 2xLCD and TV setup and switching between Nvidia profiles for 2xLCD and TV alone.[/quote]
Can you or anyone please tell me how to configure this? I have exactly the same monitor configuration, but althrough I've tried every logical configuration this script always just shows a window saying
"
Failed to schedule a mode change.
code returned: -2
which I have found that means BAD MODE. But the mode (1280*1024) is definitely supported by my monitor, I am using it at the moment.
My configuration:

; Add devices.
MainLCD := AddDisplay("\\.\DISPLAY1", "W2353", 1)
SmallLCD := AddDisplay("\\.\DISPLAY2", "L1730B", 1)
TVMonitor := AddDisplay("\\.\DISPLAY3", "LG TV", 1)

; Add modes.
MonitorOff := AddDisplayMode( 0, 0, 0, 0)
Mode1280 := AddDisplayMode(1280, 1024, 32, 60)
Mode1920 := AddDisplayMode(1920, 1080, 32, 60)

; Add configurations.
AddDisplayConfig(MainLCD, Mode1920, SmallLCD, Mode1280)
AddDisplayConfig(MainLCD, Mode1920, TVMonitor, Mode1920)


please send me your working variant or at least tell me what is the mistake.[/quote]