Page 1 of 1

Multimonitor window management. (Move window between multimonitor)

Posted: 13 Jul 2018, 23:39
by sobuj53
Hi,

I use below script everyday to move window between multimonitor. Thought I'd share it here, if anyone needs it.
Credit. All credit goes to this awesome community without their help it was not possible.

Use: Ctrl+Shift+M

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
;Move selected window to swap monitor
^+M::
DetectHiddenWindows, Off
WindArray := {}
SysGet, OutputVar, MonitorCount
; This will be the GUI name
WinName = Move Window Between Monitor
WinGet, OpenWindow, List 

	If OutputVar = 1
	{
		MsgBox, 4112, No multimonitor connected, Minimum 2 display unit is required to execute this script. Please connect to another display., 4
		Exit
	}
	If !WinExist(WinName)
		 {
			Gui,+AlwaysOnTop
			Gui, Font, s11, Arial
			Gui, Add, DropDownList, gShowToolTip x7 y9 w260 vWindowMove,Pick a Window||
			Gui, Add, Button, x282 y8 w50 h28 gPosChoice, Swap
		 }
	Else
		 {
			Gui, Destroy
			Gui,+AlwaysOnTop
			Gui, Font, s11, Arial	
			Gui, Add, DropDownList, gShowToolTip x7 y9 w260 vWindowMove,Pick a Window||
			Gui, Add, Button, x282 y8 w50 h28 gPosChoice, Swap
		 }
	
	Loop, %OpenWindow% 
	{ 
		
		id := OpenWindow%A_Index%
		WinGetTitle Title, ahk_id %id%
		WinGet, style, style, ahk_id %id%
		WinGet, ClsID, ID, ahk_id %id%
		
		If !(style & 0xC00000) or (title = "")
			continue
		WinGetClass class, ahk_id %id%	
		If (class = "ApplicationFrameWindow") 
		{
			WinGetText, text, ahk_id %id%		
			If (text = "")
			{
				WinGet, style, style, ahk_id %id%
				If !(style = "0xB4CF0000")	 ; the window isn't minimized
					continue
			}
		}

		GuiControl,,WindowMove, %Title%
		WindArray.Insert(Title, ClsID)
	   

	} 

Gui, Show,  h45 w345, %WinName%
hwnd:=WinExist(WinName)
Return

ShowToolTip:
Gui,Submit,NoHide
;remove any previous tooltip
GoSub RemoveToolTip

if (InStr(WindowMove, "Pick a Window", true) = 0 and WindowMove!="")
{

	ControlGetPos,x,y,w,h,ComboBox1,ahk_id %hwnd%

	ToolTip %WindowMove%,x,y+h,10

	SetTimer,RemoveToolTip,-3000
}
Return


PosChoice:
Gui, Submit, NoHide
if (InStr(WindowMove, "Pick a Window", true) = 0 and WindowMove!="")
{
	;return Associative array value
	IDValue:= WindArray[WindowMove]

	if WinExist("ahk_id " IDValue)
	{
		;WinActivate, Ahk_ID %IDValue%
		MoveIt(IDValue)
	}
	else
	{	
		
		GoSub ^+M ; Recall it again to refresh status

	}
	GoSub RemoveToolTip
}
Return

RemoveToolTip:
	ToolTip,,,,10
Return

GuiClose:
	Gui, Destroy
Return

/*
Function to move a given window from 1 monitor to another.
use WinGet, activeWindowHwnd, ID, A or ahk_class Notepad++
MoveIt(activeWindowHwnd) 
*/

MoveIt(activeWindowHwnd)
{
	; Count number of active monitor.
	SysGet, OutputVar, MonitorCount
	
	if OutputVar > 1 
	{	
		
		;WinGet, activeWindowHwnd, ID, A
		WinActivate, Ahk_ID %activeWindowHwnd%
		activeMonitorHwnd := MDMF_FromHWND(activeWindowHwnd)
		monitors := MDMF_Enum()

		monitorHwndList := []
		For currentMonitorHwnd, info In monitors
			monitorHwndList[A_Index] := currentMonitorHwnd

		nextMonitorHwnd := ""
		For currentMonitorHwnd, info In monitors
			If (currentMonitorHwnd = activeMonitorHwnd)
				nextMonitorHwnd := (A_Index=monitorHwndList.MaxIndex() ? monitorHwndList[1] : monitorHwndList[A_Index+1])

		activeMonitor := MDMF_GetInfo(activeMonitorHwnd)
		nextMonitor := MDMF_GetInfo(nextMonitorHwnd)

		WinGetPos, x, y, w, h, ahk_id %activeWindowHwnd%
		activeWindow := {Left:x, Top:y, Right:x+w, Bottom:y+h}

		relativePercPos := {}
		relativePercPos.Left := (activeWindow.Left-activeMonitor.Left)/(activeMonitor.Right-activeMonitor.Left)
		relativePercPos.Top := (activeWindow.Top-activeMonitor.Top)/(activeMonitor.Bottom-activeMonitor.Top)
		relativePercPos.Right := (activeWindow.Right-activeMonitor.Left)/(activeMonitor.Right-activeMonitor.Left)
		relativePercPos.Bottom := (activeWindow.Bottom-activeMonitor.Top)/(activeMonitor.Bottom-activeMonitor.Top)

		;MsgBox % activeWindow.Top "`n" activeWindow.Left " - " activeWindow.Right "`n" activeWindow.Bottom
		;MsgBox % relativePercPos.Top*100 "`n" relativePercPos.Left*100 " - " relativePercPos.Right*100 "`n" relativePercPos.Bottom*100

		activeWindowNewPos := {}
		activeWindowNewPos.Left := nextMonitor.Left+(nextMonitor.Right-nextMonitor.Left)*relativePercPos.Left
		activeWindowNewPos.Top := nextMonitor.Top+(nextMonitor.Bottom-nextMonitor.Top)*relativePercPos.Top

		WinMove, Ahk_ID %activeWindowHwnd%,, activeWindowNewPos.Left, activeWindowNewPos.Top
	}
	
	winmaximize, Ahk_ID %activeWindowHwnd%
Return
}
;Credits to "just me" for the following code:

; ======================================================================================================================
; Multiple Display Monitors Functions -> msdn.microsoft.com/en-us/library/dd145072(v=vs.85).aspx =======================
; ======================================================================================================================
; Enumerates display monitors and returns an object containing the properties of all monitors or the specified monitor.
; ======================================================================================================================
MDMF_Enum(HMON := "") {
   Static EnumProc := RegisterCallback("MDMF_EnumProc")
   Static Monitors := {}
   If (HMON = "") ; new enumeration
      Monitors := {}
   If (Monitors.MaxIndex() = "") ; enumerate
      If !DllCall("User32.dll\EnumDisplayMonitors", "Ptr", 0, "Ptr", 0, "Ptr", EnumProc, "Ptr", &Monitors, "UInt")
         Return False
   Return (HMON = "") ? Monitors : Monitors.HasKey(HMON) ? Monitors[HMON] : False
}
; ======================================================================================================================
;  Callback function that is called by the MDMF_Enum function.
; ======================================================================================================================
MDMF_EnumProc(HMON, HDC, PRECT, ObjectAddr) {
   Monitors := Object(ObjectAddr)
   Monitors[HMON] := MDMF_GetInfo(HMON)
   Return True
}
; ======================================================================================================================
;  Retrieves the display monitor that has the largest area of intersection with a specified window.
; ======================================================================================================================
MDMF_FromHWND(HWND) {
   Return DllCall("User32.dll\MonitorFromWindow", "Ptr", HWND, "UInt", 0, "UPtr")
}
; ======================================================================================================================
; Retrieves the display monitor that contains a specified point.
; If either X or Y is empty, the function will use the current cursor position for this value.
; ======================================================================================================================
MDMF_FromPoint(X := "", Y := "") {
   VarSetCapacity(PT, 8, 0)
   If (X = "") || (Y = "") {
      DllCall("User32.dll\GetCursorPos", "Ptr", &PT)
      If (X = "")
         X := NumGet(PT, 0, "Int")
      If (Y = "")
         Y := NumGet(PT, 4, "Int")
   }
   Return DllCall("User32.dll\MonitorFromPoint", "Int64", (X & 0xFFFFFFFF) | (Y << 32), "UInt", 0, "UPtr")
}
; ======================================================================================================================
; Retrieves the display monitor that has the largest area of intersection with a specified rectangle.
; Parameters are consistent with the common AHK definition of a rectangle, which is X, Y, W, H instead of
; Left, Top, Right, Bottom.
; ======================================================================================================================
MDMF_FromRect(X, Y, W, H) {
   VarSetCapacity(RC, 16, 0)
   NumPut(X, RC, 0, "Int"), NumPut(Y, RC, 4, Int), NumPut(X + W, RC, 8, "Int"), NumPut(Y + H, RC, 12, "Int")
   Return DllCall("User32.dll\MonitorFromRect", "Ptr", &RC, "UInt", 0, "UPtr")
}
; ======================================================================================================================
; Retrieves information about a display monitor.
; ======================================================================================================================
MDMF_GetInfo(HMON) {
   NumPut(VarSetCapacity(MIEX, 40 + (32 << !!A_IsUnicode)), MIEX, 0, "UInt")
   If DllCall("User32.dll\GetMonitorInfo", "Ptr", HMON, "Ptr", &MIEX) {
      MonName := StrGet(&MIEX + 40, 32)    ; CCHDEVICENAME = 32
      MonNum := RegExReplace(MonName, ".*(\d+)$", "$1")
      Return {Name:      (Name := StrGet(&MIEX + 40, 32))
            , Num:       RegExReplace(Name, ".*(\d+)$", "$1")
            , Left:      NumGet(MIEX, 4, "Int")    ; display rectangle
            , Top:       NumGet(MIEX, 8, "Int")    ; "
            , Right:     NumGet(MIEX, 12, "Int")   ; "
            , Bottom:    NumGet(MIEX, 16, "Int")   ; "
            , WALeft:    NumGet(MIEX, 20, "Int")   ; work area
            , WATop:     NumGet(MIEX, 24, "Int")   ; "
            , WARight:   NumGet(MIEX, 28, "Int")   ; "
            , WABottom:  NumGet(MIEX, 32, "Int")   ; "
            , Primary:   NumGet(MIEX, 36, "UInt")} ; contains a non-zero value for the primary monitor.
   }
   Return False
}

Re: Multimonitor window management. (Move window between multimonitor)

Posted: 26 Jul 2018, 06:35
by elModo7
Hey man, greatly appreciated!