;WinPosSaver.ahk ; Save and restore window positions ;Skrommel @ 2017 ; with mouser mods 7/26/17 #NoEnv #SingleInstance,Force SetBatchLines,-1 SetWindelay,0 DetectHiddenWindows,Off ; MOUSER: keep track of last monitor off, dont restore until one happens global LastMonitorOff = 0 ; How many milliseconds to wait after monitor on signal? This is a bit of trial and error, 2 seconds seems safe but a bit long to wait; 1000 may work fine global WaitTimeBeforeLoadInMsec = 2000 FileAppend,`n----------------------------------------------------------------------`n,log.txt applicationname=WinPosSaver OnExit,EXIT Gosub,MENU VarSetCapacity(newGUID,16,0) If A_OsVersion in WIN_8,WIN_8.1,WIN_10 DllCall("Rpcrt4\UuidFromString","Str","6fe69556-704a-47a0-8f24-c28d936fda47","UInt",&newGUID) ;GUID_CONSOLE_DISPLAY_STATE:="6fe69556-704a-47a0-8f24-c28d936fda47" Else DllCall("Rpcrt4\UuidFromString","Str","02731015-4510-4526-99e6-e5a17ebd1aea","UInt",&newGUID) ;GUID_MONITOR_POWER_ON:="02731015-4510-4526-99e6-e5a17ebd1aea" rhandle:=DllCall("RegisterPowerSettingNotification","UInt",a_scriptHwnd,"Str",strGet(&newGUID),"Int",0) OnMessage(0x218,"WM_POWERBROADCAST") ; MOUSER no need for timer same ;SetTimer,SAVE,-60000 Gosub,SAVE Return ;Stolen from lexikos at https://autohotkey.com/boards/viewtopic.php?t=14543 ;Stolen from Masjonjar13 at https://autohotkey.com/boards/viewtopic.php?t=17457 WM_POWERBROADCAST(wparam,lparam) { Global newGUID Static changeText := {10: "PBT_APMPOWERSTATUSCHANGE", 18: "PBT_APMRESUMEAUTOMATIC", 7: "PBT_APMRESUMESUSPEND", 4: "PBT_APMSUSPEND", 32787: "PBT_POWERSETTINGCHANGE"} change := changeText[wparam] If !change change := wparam If change In PBT_POWERSETTINGCHANGE { If (SubStr(StrGet(lParam),1,StrLen(StrGet(lParam))-1)=StrGet(&newGUID)) { monitorStatus:=NumGet(lParam+0,20,"UInt")?"On":"Off" FileAppend,%A_Now% - in monitoronoff change with Monitor: %monitorStatus%`n,log.txt If (monitorStatus="Off") { FileAppend,%A_Now% - monitor turning off - setting save timer`n,log.txt ; mouser: save when monitor goes off (use timer since it needs to happen async) SetTimer,SAVE,-1000 ; and now we can turn off timer saving? ;SetTimer,SAVE,Off ; MOUSER: keep track of last time monitor off LastMonitorOff := A_TickCount FileAppend,%LastMonitorOff% - monitor turning off - recording lasttime and setting save timer.. `n,log.txt } Else { FileAppend,%A_Now% - monitor turning on`n,log.txt FileAppend,%A_Now% - lastmon = %LastMonitorOff%.`n,log.txt if (LastMonitorOff > 0) { ; reset this LastMonitorOff = 0 FileAppend,%A_Now% - monitor turning on and we have saved before.. Starting load timer..`n,log.txt ; mouser: monitors are wakeing up, set time to LOAD after some delay ;SetTimer,SAVE,Off timeperiod := 0 - WaitTimeBeforeLoadInMsec SetTimer,LOAD, %timeperiod% ; then dont do anything else ;SetTimer,SAVE,-60000 ;Gosub,LOAD } } } } If change In PBT_APMSUSPEND { ; mouser disabled this ;SetTimer,SAVE,Off } If change In PBT_APMRESUMESUSPEND,PBT_APMRESUMEAUTOMATIC { ; mouser disabled this ;SetTimer,SAVE,-60000 ;Gosub,LOAD } FileAppend,%A_Now% - %change% - Monitor: %monitorStatus%`n,log.txt } SAVE: FileAppend,%A_Now% - Saving settings now.`n,log.txt start:=A_TickCount filename:=applicationname ".ini" SysGet,count,MonitorCount WinGet,list,List ini:="" lastid:=0 Loop,% list { id:=list%A_Index% Loop { parent:=DllCall("GetParent","UInt",id) If parent=0 Break id:=parent } ; MOUSER kludge to fix duples in hex ids ; MOUSER test to force hex to decimal id:= id + 0 ; check if dupe FileAppend,%A_Now% - Comparind id = %id% vs lastid = %lastid%.`n,log.txt if (id == lastid) { FileAppend,%A_Now% - Skipping dupe id.`n,log.txt continue } lastid := id WinGetPos,x,y,w,h,Ahk_id %id% WinGet,path,ProcessPath,Ahk_id %id% WinGet,pid,PID,Ahk_id %id% line:=GetCommandLine(pid) WinGet,minmax,MinMax,Ahk_id %id% If minmax<>0 WinGetNormalPos(id,nx,ny,nw,nh) Else { nx:=x ny:=y nw:=w nh:=h } WinGetClass,class,Ahk_id %id% WinGetTitle,title,Ahk_id %id% key:=id ;path "|" class "|" title "|" count ini.="[" key "]`n" ini.="x=" x "`n" ini.="y=" y "`n" ini.="w=" w "`n" ini.="h=" h "`n" ini.="nx=" nx "`n" ini.="ny=" ny "`n" ini.="nw=" nw "`n" ini.="nh=" nh "`n" ini.="minmax=" minmax "`n" ini.="id=" id "`n" ini.="class=" class "`n" ini.="title=" title "`n" ini.="path=" path "`n" ini.="line=" line "`n" ini.="count=" count "`n`n" } FileDelete,%filename% FileAppend,%ini%,%filename% ; MOUSER - dont reset timer to go off again ;SetTimer,SAVE,-60000 Return LOAD: FileAppend,%A_Now% - Loading settings now.`n,log.txt filename:=applicationname ".ini" SysGet,count,MonitorCount WinGet,list,List lastid = 0 Loop,% list { id:=list%A_Index% Loop { parent:=DllCall("GetParent","UInt",id) If parent=0 Break id:=parent } ; MOUSER kludge to fix duples in hex ids ; MOUSER test to force hex to decimal id:= id + 0 ; check if dupe FileAppend,%A_Now% - Comparind id = %id% vs lastid = %lastid%.`n,log.txt if (id == lastid) { FileAppend,%A_Now% - Skipping dupe id.`n,log.txt continue } lastid := id WinGet,path,ProcessPath,Ahk_id %id% WinGetClass,class,Ahk_id %id% WinGetTitle,title,Ahk_id %id% key:=id ;path "|" class "|" title "|" count IniRead,x ,% filename,% key,x IniRead,y ,% filename,% key,y IniRead,w ,% filename,% key,w IniRead,h ,% filename,% key,h IniRead,nx ,% filename,% key,nx IniRead,ny ,% filename,% key,ny IniRead,nw ,% filename,% key,nw IniRead,nh ,% filename,% key,nh IniRead,minmax,% filename,% key,minmax ; IniRead,id ,% filename,% key,id ; IniRead,class ,% filename,% key,class ; IniRead,title ,% filename,% key,title ; IniRead,path ,% filename,% key,path ; IniRead,line ,% filename,% key,line ; IniRead,count ,% filename,% key,count If minmax=Error Continue ; MOUSER - get CURRENT real location (even maximimized) WinGetPos,newx,newy,neww,newh,Ahk_id %id% WinGet,newminmax,MinMax,Ahk_id %id% FileAppend,%A_Now% - In load for window %id% with newminmax = %newminmax% and minmax = %minmax%.`n,log.txt If (minmax<>newminmax) { ; min-max state has changed WinRestore,Ahk_id %id% Loop,10 { WinGet,newminmax,MinMax,Ahk_id %id% If newminmax=0 Break Sleep,0 } } If minmax=-1 { WinMinimize,Ahk_id %id% Loop,10 { WinGet,newminmax,MinMax,Ahk_id %id% If newminmax=-1 Break Sleep,0 } WinSetNormalPos(id,nx,ny,nw,nh) } Else If minmax=1 { ;; window should be maximized ; we can't just maximize it, because it may already be maximized BUT be on wrong monitor due to OS screwup ; check if there was a move of monitors if (x!=newx OR y!=newy OR h!=newh OR w!=neww) { ; HERE IS WHERE the OS has gone insane and moved a window to a new monitor while keeping it maximized but moved where ; ATTN: Mouser so the trick is to UNMINIMIZE (RESTORE) then move to old normal pos, which will change monitor, then drop down to remaximize) if (1==1) { FileAppend,%A_Now% - B trying direct set of max win pos t`n,log.txt WinSetNormalPos(id,nx,ny,nw,nh) if (newminmax==1) { ; already maximized so we need do this to move the maximized to it's original location WinMove,Ahk_id %id%,,% x,% y,% w,% h } } Else { FileAppend,%A_Now% - B Forcing normal wind pos first`n,log.txt WinRestore,Ahk_id %id% ; Loop,10 ; { ; WinGet,newminmax,MinMax,Ahk_id %id% ; If newminmax=0 ; Break ; Sleep,0 ; } WinSetNormalPos(id,nx,ny,nw,nh) } } FileAppend,%A_Now% - B Now forcing wind max`n,log.txt WinMaximize,Ahk_id %id% Loop,10 { FileAppend,%A_Now% - B Waiting on win to be max`n,log.txt WinGet,newminmax,MinMax,Ahk_id %id% If newminmax=1 Break Sleep,0 } FileAppend,%A_Now% - B done waiting for win to be max`n,log.txt ;WinSetNormalPos(id,nx,ny,nw,nh) } Else WinMove,Ahk_id %id%,,% nx,% ny,% nw,% nh } Return EXIT: ExitApp MENU: Menu,Tray,Click,1 Menu,Tray,NoStandard Menu,Tray,Add,%applicationname%,LOAD Menu,Tray,Add Menu,Tray,Add,&Load layout,LOAD Menu,Tray,Add,&Save layout,SAVE Menu,Tray,Add,&About...,ABOUT Menu,Tray,Add,E&xit,EXIT Menu,Tray,Default,%applicationname% Menu,Tray,Tip,%applicationname% Return ABOUT: Gui,99:Destroy Gui,99:Margin,20,20 Gui,99:Add,Picture,xm Icon1,%applicationname%.exe Gui,99:Font,Bold Gui,99:Add,Text,x+10 yp+10,%applicationname% v0.9m Gui,99:Font Gui,99:Add,Text,y+10,Save and restore window positions Gui,99:Add,Text,y+10,- Saves layout every minute Gui,99:Add,Text,y+10,- Restores layout after sleep and hibernation Gui,99:Add,Picture,xm y+20 Icon2,%applicationname%.exe Gui,99:Font,Bold Gui,99:Add,Text,x+10 yp+10,1 Hour Software by Skrommel Gui,99:Font Gui,99:Add,Text,y+10,For more tools, information and donations, please visit Gui,99:Font,CBlue Underline Gui,99:Add,Text,y+5 G1HOURSOFTWARE,www.1HourSoftware.no Gui,99:Font Gui,99:Add,Picture,xm y+20 Icon3,%applicationname%.exe Gui,99:Font,Bold Gui,99:Add,Text,x+10 yp+10,DonationCoder Gui,99:Font Gui,99:Add,Text,y+10,Please support the contributors at Gui,99:Font,CBlue Underline Gui,99:Add,Text,y+5 GDONATIONCODER,www.DonationCoder.com Gui,99:Font Gui,99:Add,Picture,xm y+20 Icon4,%applicationname%.exe Gui,99:Font,Bold Gui,99:Add,Text,x+10 yp+10,AutoHotkey Gui,99:Font Gui,99:Add,Text,y+10,This tool was made using the powerful Gui,99:Font,CBlue Underline Gui,99:Add,Text,y+5 GAUTOHOTKEY,www.AutoHotkey.com Gui,99:Font Gui,99:Show,,%applicationname% About hCur:=DllCall("LoadCursor","UInt",NULL,"Int",32649,"UInt") ;IDC_HAND OnMessage(0x200,"WM_MOUSEMOVE") Return 1HOURSOFTWARE: Run,http://www.1hoursoftware.no,,UseErrorLevel Return DONATIONCODER: Run,http://www.donationcoder.com,,UseErrorLevel Return AUTOHOTKEY: Run,http://www.autohotkey.com,,UseErrorLevel Return 99GuiClose: Gui,99:Destroy OnMessage(0x200,"") DllCall("DestroyCursor","Uint",hCur) Return WM_MOUSEMOVE(wParam,lParam) { Global hCur MouseGetPos,,,,ctrl If ctrl in Static9,Static13,Static17 DllCall("SetCursor","UInt",hCur) Return } Return GetCommandLine(pid) { wmi:=ComObjGet("winmgmts:") ; Get WMI service object queryEnum:=wmi.ExecQuery("" ; Run query to retrieve matching process(es) . "Select * from Win32_Process where ProcessId=" . pid) ._NewEnum() queryEnum[process] ; Get first matching process Return process.CommandLine } ;Stolen from Lexikos at https://autohotkey.com/board/topic/25204-original-window-positionsize/ WinGetNormalPos(hwnd,ByRef x,ByRef y,ByRef w="",ByRef h="") { VarSetCapacity(wp,44) NumPut(44,wp) DllCall("GetWindowPlacement","UInt",hwnd,"UInt",&wp) x:=NumGet(wp,28,"int") y:=NumGet(wp,32,"int") w:=NumGet(wp,36,"int")-x h:=NumGet(wp,40,"int")-y } WinSetNormalPos(hwnd,ByRef x,ByRef y,ByRef w="",ByRef h="") { VarSetCapacity(wp,44) NumPut(44,wp) DllCall("GetWindowPlacement","UInt",hwnd,"UInt",&wp) NumPut(x,wp,28) NumPut(y,wp,32) NumPut(x+w,wp,36) NumPut(y+h,wp,40) DllCall("SetWindowPlacement","uint",hwnd,"UInt",&wp) }