Another update:
Fixed the mutual exclusion lock to properly unlock after execution, instead of waiting for a period and then unlocking.
Fixed two bugs where the Program Manager and the task bar can be sent to another window.
Code:
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Auto timers, usefull when debugging ;
; turn off or set high to save cpu ;
; when debug is done ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SetTimer,AutoReloadScript,1000
; ***** initialization *****
#WinActivateForce
SetBatchLines, -1 ; maximize script speed!
SetWinDelay, -1
OnExit, CleanUp ; clean up in case of error (otherwise some windows will get lost)
numDesktops := 4 ; maximum number of desktops
curDesktop := 1 ; index number of current desktop
USE_PAUSE = 1000 ; Tray Tip Pause Length
lock = 0 ; lock to prevent running both hotkeys at same time
; ***** hotkeys *****
#1::
if(lock = 0)
{
lock = 1
SwitchToDesktop(1)
}
return
#2::
if(lock = 0)
{
lock = 1
SwitchToDesktop(2)
}
return
#3::
if(lock = 0)
{
lock = 1
SwitchToDesktop(3)
}
return
#4::
if(lock = 0)
{
lock = 1
SwitchToDesktop(4)
}
return
^!1::
if(lock = 0)
{
lock = 1
SendActiveToDesktop(1)
}
return
^!2::
if(lock = 0)
{
lock = 1
SendActiveToDesktop(2)
}
return
^!3::
if(lock = 0)
{
lock = 1
SendActiveToDesktop(3)
}
return
^!4::
if(lock = 0)
{
lock = 1
SendActiveToDesktop(4)
}
return
#0::goto, cleanup
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; AutoScriptReload/Suspend ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
AutoReloadScript:
FileGetAttrib,attribs,%A_ScriptFullPath%
IfInString,attribs,A
{
FileSetAttrib,-A,%A_ScriptFullPath%
TrayTip, %A_ScriptName%, updated AutoHotkey script
SetTimer, AutoReloadScriptTrayTip, 2000
}
return
AutoReloadScriptTrayTip:
SetTimer, AutoReloadScriptTrayTip, Off
TrayTip
Reload
return
; ***** functions *****
; switch to the desktop with the given index number
SwitchToDesktop(newDesktop)
{
global
if (curDesktop <> newDesktop)
{
GetCurrentWindows(curDesktop)
ShowHideWindows(curDesktop, false)
ShowHideWindows(newDesktop, true)
activate_window := % active_id%newDesktop%
WinActivate, ahk_id %activate_window%
TrayTip, DesktopSwitch, Switching to desktop %newDesktop%
curDesktop := newDesktop
}
SetTimer, RemoveTrayTip, %USE_PAUSE%
sleep, 1000
lock = 0
return
}
; sends the given window from the current desktop to the given desktop
SendToDesktop(windowID, newDesktop)
{
global
if (curDesktop <> newDesktop)
{
RemoveWindowID(curDesktop, windowID)
; add window to destination desktop
windows%newDesktop% += 1
i := windows%newDesktop%
windows%newDesktop%%i% := windowID
WinHide, ahk_id %windowID%
TrayTip, DesktopSwitch, Window send to desktop %newDesktop%
Send, {ALT DOWN}{TAB}{ALT UP} ; activate the right window
}
SetTimer, RemoveTrayTip, %USE_PAUSE%
}
; sends the currently active window to the given desktop
SendActiveToDesktop(newDesktop)
{
global
WinGet, id, ID, A
WinGetActiveTitle, curWin
sleep, 200
; If Window Does not have a title, do not send it
if( (curWin = "") || (curWin = "Program Manager"))
{
lock = 0
sleep, 1000
return
}
SendToDesktop(id, newDesktop)
sleep, 1000
lock = 0
}
; removes the given window id from the desktop <desktopIdx>
RemoveWindowID(desktopIdx, ID)
{
global
Loop, % windows%desktopIdx%
{
if (windows%desktopIdx%%A_Index% = ID)
{
windows%desktopIdx%%A_Index%= ;Emiel: just empty the array element, array will be emptied by next switch anyway
Break
}
}
}
; this builds a list of all currently visible windows in stores it in desktop <index>
GetCurrentWindows(index)
{
global
WinGet, active_id%index%, ID, A ; get the current active window
emptyString =
StringSplit, windows%index%, emptyString ; delete the entire windows_index_ array
WinGet, windows%index%, List,,, Program Manager ; get list of all visible windows
; remove windows which we want to see on all virtual desktops
Loop, % windows%index%
{
id := % windows%index%%A_Index%
WinGetClass, windowClass, ahk_id %id%
if windowClass = Shell_TrayWnd ; remove task bar window id
windows%index%%A_Index%= ; just empty the array element, array will be emptied by next switch anyway
if windowClass = #32770 ; we also want multimontaskbar on all virtual desktops
windows%index%%A_Index%= ; just empty the array element, array will be emptied by next switch anyway
if windowClass = cygwin/x X rl-xosview-XOsview-0 ; xosviews e.d.
windows%index%%A_Index%=
if windowClass = cygwin/x X rl-xosview-XOsview-1 ; xosviews e.d.
windows%index%%A_Index%=
if windowClass = MozillaUIWindowClass ; Mozilla thunderbird
{
WinGet, ExStyle, ExStyle, ahk_id %id%
if (ExStyle & 0x8) ; 0x8 is WS_EX_TOPMOST.
windows%index%%A_Index%=
}
}
}
; if show=true then shows windows of desktop %index%, otherwise hides them
ShowHideWindows(index, show)
{
global
Loop, % windows%index%
{
id := % windows%index%%A_Index%
if show
WinShow, ahk_id %id%
else
WinHide, ahk_id %id%
}
}
; show all windows from all desktops on exit
CleanUp:
Loop, %numDesktops%
ShowHideWindows(A_Index, true)
ExitApp
RemoveTrayTip:
SetTimer, RemoveTrayTip, Off
TrayTip
return
Only bugs left that I can see is the shadow of hidden windows that stick around sometimes when switching windows. For example, the tooltip shadow will sometimes remain after switching, or the start menu shadow will remain if open during a switch.
If anyone has a suggestion on how to fix this last bug, then let me know.
Best,
John