Windows 10 Desktop manager

Post your working scripts, libraries and tools for AHK v1.1 and older
jpginc
Posts: 124
Joined: 29 Sep 2013, 22:35

Windows 10 Desktop manager

20 Aug 2015, 07:50

If you have moved to Windows 10 already then you have to make use of the new Virtual desktop environment!

Below is a script that lets you quickly switch and move windows between desktops. If you just copy paste the code you get the following shortcuts

Capslock + number: Move to desktop number
windows key + shift + number: Move active window to desktop number
Capslock + w: Move to the next desktop
Capslock + q: Move to the previous desktop
Capslcok + s: Move active window to the next desktop
Capslock + a: Move active window to the previous desktop

I'll will be updating this script as I start using it more. Keep up to date here: https://github.com/jpginc/windows10DesktopManager

If your using the script and like it please star the project on github, I like knowing that stuff I make gets used :dance:

Code: Select all

#SingleInstance
 /*
  * the next 10 lines setup the hotkeys for the script. 
  */
globalDesktopManager := new JPGIncDesktopManagerClass()
globalDesktopManager.setGoToDesktop("Capslock")
    .setMoveWindowToDesktop("+#")
    .afterGoToDesktop("turnCapslockOff")
    .afterMoveWindowToDesktop("turnCapslockOff")
    .setGoToNextDesktop("Capslock & w")
    .setGoToPreviousDesktop("Capslock & q")
    .setMoveWindowToNextDesktop("Capslock & s")
    .setMoveWindowToPreviousDesktop("Capslock & a")
	;~ .setCloseDesktop("Capslock & x")
	;~ .setNewDesktop("Capslock & n")

return

#c::ExitApp

debugger(message) 
{
	;~ ToolTip, % message
	;~ sleep 100
	return
}
turnCapslockOff()
{
	;if the capslock key is down then set the capslock state to on so that
	;when the user lets go it will change the state to off
	if(GetKeyState("Capslock", "P"))
	{
		SetCapsLockState, On
	} else
	{
		SetCapsLockState , Off
	}
	return
}

/*
 * If we send the keystrokes too quickly you sometimes get a flickering of the screen
 */
send(toSend)
{
	oldDelay := A_KeyDelay
	SetKeyDelay, 30
	
	send, % toSend
	
	SetKeyDelay, % oldDelay
	return 
}

closeMultitaskingViewFrame()
{
	IfWinActive, ahk_class MultitaskingViewFrame
	{
		send("#{tab}")
	}
	return 
}

	
openMultitaskingViewFrame()
{
	IfWinNotActive, ahk_class MultitaskingViewFrame
	{
		send("#{tab}")
		WinWaitActive, ahk_class MultitaskingViewFrame
	}
	return
}


callFunction(possibleFunction)
{
	if(IsFunc(possibleFunction)) 
	{
		%possibleFunction%()
	} else if(IsObject(possibleFunction))
	{
		possibleFunction.Call()
	} else if(IsLabel(possibleFunction))
	{
		gosub, % possibleFunction
	}
	return
}

getDesktopNumberFromHotkey(keyCombo)
{
	number := RegExReplace(keyCombo, "[^\d]", "")
	return number == 0 ? 10 : number
}

getIndexFromArray(searchFor, array) 
{
	loop, % array.MaxIndex()
	{
		if(array[A_index] == searchFor) 
		{
			return A_index
		}
	}
	return -1
}
/* 
 * this function is used when a hotkey combo is pressed. It directs the program to the appropriate function in the appropriate class
 */ 
JPGIncDesktopManagerCallback(desktopManager, functionName, keyCombo)
{
	desktopManager[functionName](getDesktopNumberFromHotkey(keyCombo))
	return
}

class JPGIncHotkeyManager
{
	_notAnAutohotkeyModKeyRegex := "[^#!^+<>*~$]"

	__new() 
	{
		return this
	}

	setupNumberedHotkey(callbackClass, callbackFunctionName, hotkeyKey)
	{
		if(this._doesHotkeyRequireCustomHotkeySyntax(hotkeyKey))
		{
			hotkeyKey .= " & "
		}
		Loop, 10
		{
			this.setupHotkey(callbackClass, callbackFunctionName, hotkeyKey (A_Index - 1))
		}
		return this
	}
	
	setupHotkey(callbackClass, callbackFunctionName, hotkeyKey)
	{
		;~ debugger("Setting up callback: " callbackFunctionName " as " hotkeyKey)
		callback := Func("JPGIncDesktopManagerCallback").Bind(callbackClass, callbackFunctionName, hotkeyKey)
		Hotkey, % hotkeyKey, % callback, On
		return this
	}
	
	/*
	 * If the modifier key used is only a modifier symbol then we don't need to do anything (https://autohotkey.com/docs/Hotkeys.htm#Symbols)
	 * but if it contains any other characters then it means that the hotkey is a combination hotkey then we need to add " & " 
	 */
	_doesHotkeyRequireCustomHotkeySyntax(key)
	{
		return RegExMatch(key, this._notAnAutohotkeyModKeyRegex)
	}
}
;taken from optimist__prime https://autohotkey.com/boards/viewtopic.php?t=9224
class MonitorMapperClass
{
	; This part figures out how many times we need to hit Tab to get to the
	; monitor with the window we are trying to send to another desktop.	
	getRequiredTabCount(hwnd)
	{
		activemonitor := this.getWindowsMonitorNumber(hwnd)
		
		SysGet, monitorcount, MonitorCount
		SysGet, primarymonitor, MonitorPrimary
	 
		If (activemonitor > primarymonitor)
		{
			tabCount := activemonitor - primarymonitor
		}
		else If (activemonitor < primarymonitor)
		{
			tabCount := monitorcount - primarymonitor + activemonitor
		}
		else
		{
			tabCount := 0
		}
		tabCount *= 2	
		
		return tabCount
	}
	
	/*
	 * This function returns the monitor number of the window with the given hwnd
	 */
	getWindowsMonitorNumber(hwnd)
	{
		WinGetPos, x, y, width, height, % "Ahk_id" hwnd
		debugger("Window Position/Size:`nX: " X "`nY: " Y "`nWidth: " width "`nHeight: " height)	
		SysGet, monitorcount, MonitorCount
		SysGet, primarymonitor, MonitorPrimary	
		debugger("Monitor Count: " MonitorCount)	
		Loop %monitorcount%
		{
			SysGet, mon, Monitor, %a_index%
			debugger("Primary Monitor: " primarymonitor "`nDistance between monitor #" a_index "'s right border and Primary monitor's left border (Left < 0, Right > 0):`n" monRight "px")	
			If (x < monRight - width / 2 || monitorcount = a_index)
			{
				return %a_index%
			}
		}
	}
}

class VirtualDesktopManagerClass
{
	__new()
	{	
		debugger("creating th vdm")
		;https://msdn.microsoft.com/en-us/library/windows/desktop/mt186440(v=vs.85).aspx
		CLSID := "{aa509086-5ca9-4c25-8f95-589d3c07b48a}" ;search VirtualDesktopManager clsid
		IID := "{a5cd92ff-29be-454c-8d04-d82879fb3f1b}" ;search IID_IVirtualDesktopManager
		this.iVirtualDesktopManager := ComObjCreate(CLSID, IID)
		
		this.isWindowOnCurrentVirtualDesktopAddress := NumGet(NumGet(this.iVirtualDesktopManager+0), 3*A_PtrSize)
		this.getWindowDesktopIdAddress := NumGet(NumGet(this.iVirtualDesktopManager+0), 4*A_PtrSize)
		this.moveWindowToDesktopAddress := NumGet(NumGet(this.iVirtualDesktopManager+0), 5*A_PtrSize)
		
		return this
	}
	
	getWindowDesktopId(hWnd, tryAgain := true) 
	{
		desktopId := ""
		VarSetCapacity(desktopID, 16, 0)
		;IVirtualDesktopManager::GetWindowDesktopId  method
		;https://msdn.microsoft.com/en-us/library/windows/desktop/mt186441(v=vs.85).aspx
 
		Error := DllCall(this.getWindowDesktopIdAddress, "Ptr", this.iVirtualDesktopManager, "Ptr", hWnd, "Ptr", &desktopID)	
		if(Error != 0) {
			if(tryAgain) 
			{
				return this.getWindowDesktopId(hwnd, false)
			}
			msgbox % "error in getWindowDesktopId " Error
			clipboard := error
		}
 
		return &desktopID
	}
	
	getDesktopGuid(hwnd)
	{
		debugger("getting the guid")
		return this._guidToStr(this.getWindowDesktopId(hwnd))
	}
	; https://github.com/cocobelgica/AutoHotkey-Util/blob/master/Guid.ahk#L36
	_guidToStr(ByRef VarOrAddress)
	{
		;~ debugger(&VarOrAddress " address")
		pGuid := IsByRef(VarOrAddress) ? &VarOrAddress : VarOrAddress
		VarSetCapacity(sGuid, 78) ; (38 + 1) * 2
		if !DllCall("ole32\StringFromGUID2", "Ptr", pGuid, "Ptr", &sGuid, "Int", 39)
			throw Exception("Invalid GUID", -1, Format("<at {1:p}>", pGuid))
		return StrGet(&sGuid, "UTF-16")
	}
	
	
	isDesktopCurrentlyActive(hWnd) 
	{
		;IVirtualDesktopManager::IsWindowOnCurrentVirtualDesktop method
		;Indicates whether the provided window is on the currently active virtual desktop.
		;https://msdn.microsoft.com/en-us/library/windows/desktop/mt186442(v=vs.85).aspx
		Error := DllCall(this.isWindowOnCurrentVirtualDesktopAddress, "Ptr", this.iVirtualDesktopManager, "Ptr", hWnd, "IntP", onCurrentDesktop)
		if(Error != 0) {
			msgbox % "error in isDesktopCurrentlyActive " Error
			clipboard := error
		}

		return onCurrentDesktop
	}
	
	moveWindowToDesktop(hWnd, ByRef desktopId)
	{
		Error := DllCall(this.moveWindowToDesktopAddress, "Ptr", this.iVirtualDesktopManager, "Ptr", activeHwnd, "Ptr", &desktopId)
		if(Error != 0) {
			msgbox % "error in moveWindowToDesktop " Error "but no error?"
			clipboard := error
		}
		return this
	}
	
	getCurrentDesktop(hWnd) 
	{
		desktopId := ""
		VarSetCapacity(desktopID, 16, 0)

		Error := DllCall(this.getCurrentDesktopAddress, "Ptr", this.iVirtualDesktopManagerInternal, "Ptr", &desktopID)	
		if(Error != 0) {
			msgbox % "error in getWindowDesktopId " Error
			clipboard := error
		}

		return &desktopID
	}
}
class DesktopMapperClass
{	
	desktopIds := []
	
	__new(virtualDesktopManager)
	{
		this._setupGui()
		this.virtualDesktopManager := virtualDesktopManager
		return this
	}
	
	/*
	 * Populates the desktopIds array with the current virtual deskops according to the registry key
	 */
	mapVirtualDesktops() 
	{
		regIdLength := 32
		RegRead, DesktopList, HKEY_CURRENT_USER, SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VirtualDesktops, VirtualDesktopIDs
	
		this.desktopIds := []
		while, true
		{
			desktopId := SubStr(DesktopList, ((A_index-1) * regIdLength) + 1, regIdLength)
			if(desktopId) 
			{
				this.desktopIds.Insert(this._idFromReg(desktopId))
			} else
			{
				break
			}
		}
		debugger("there are " this.desktopIds.MaxIndex() " things")
		return this
	}
	
	/*
	 * Gets the desktop id of the current desktop
	 */
	getCurrentDesktopId()
	{
		hwnd := this.hwnd
		Gui %hwnd%:show, NA ;show but don't activate
		winwait, % "Ahk_id " hwnd
		
		guid := this.virtualDesktopManager.getDesktopGuid(hwnd)

		Gui %hwnd%:hide 
		;if you don't wait until it closes (and sleep a little) then the desktop the gui is on can get focus
		WinWaitClose,  % "Ahk_id " hwnd
		sleep 50 

		return this._idFromGuid(guid)
	}
	
	getNumberOfDesktops() 
	{
		this.mapVirtualDesktops()
		return this.desktopIds.maxIndex()
	}
	
	/*
	 * returns the number of the current desktop
	 */
	getDesktopNumber()
	{
		this.mapVirtualDesktops()
		currentDesktop := this.getCurrentDesktopId()
		
		return this._indexOfId(currentDesktop)
	}
	
	/*
	 * takes an ID from the registry and extracts the last 16 characters (which matches the last 16 characters of the GUID)
	 */
	_idFromReg(regString) 
	{
		return SubStr(regString, 17)
	}
	
	/*
	 * takes an ID from microsofts IVirtualDesktopManager and extracts the last 16 characters (which matches the last 16 characters of the ID from the registry)
	 */
	_idFromGuid(guidString)
	{
		return SubStr(RegExReplace(guidString, "[-{}]"), 17)
	}

	_indexOfId(guid) 
	{
		loop, % this.desktopIds.MaxIndex()
		{
			debugger("looking for `n" guid "`n" this.desktopIds[A_index])
			if(this.desktopIds[A_index] == guid) 
			{
				debugger("Found it! desktop is " A_Index)
				return A_Index
			}
		}
		return -1
	}

	_setupGui()
	{
		Gui, new
		Gui, show
		Gui, +HwndMyGuiHwnd
		this.hwnd := MyGuiHwnd
		Gui, hide
		return this
	}
}
class JPGIncWindowMoverClass
{
	moveActiveWindowToDesktopFunctionName := "moveActiveWindowToDesktop"
	moveToNextFunctionName := "moveActiveWindowToNextDesktop"
	moveToPreviousFunctionName := "moveActiveWindowToPreviousDesktop"
	_postMoveWindowFunctionName := ""
	
	__new()
	{
		this.desktopMapper := new DesktopMapperClass(new VirtualDesktopManagerClass())
		this.monitorMapper := new MonitorMapperClass()
		return this
	}
	
	doPostMoveWindow() 
	{
		callFunction(this._postMoveWindowFunctionName)
		return this
	}
	
	moveActiveWindowToDesktop(targetDesktop, follow := false)
	{
		currentDesktop := this.desktopMapper.getDesktopNumber()
		if(currentDesktop == targetDesktop) 
		{
			return this
		}
		numberOfTabsNeededToSelectActiveMonitor := this.monitorMapper.getRequiredTabCount(WinActive("A"))
		numberOfDownsNeededToSelectDesktop := this.getNumberOfDownsNeededToSelectDesktop(targetDesktop, currentDesktop)
		
		openMultitaskingViewFrame()
		send("{tab " numberOfTabsNeededToSelectActiveMonitor "}")
		send("{Appskey}m{Down " numberOfDownsNeededToSelectDesktop "}{Enter}")
		closeMultitaskingViewFrame()
		this.doPostMoveWindow()
		
		return	this
	}
	
	moveActiveWindowToNextDesktop(follow := false)
	{
		currentDesktop := this.desktopMapper.getDesktopNumber()
		return this.moveActiveWindowToDesktop(currentDesktop + 1, follow)
	}
	
	moveActiveWindowToPreviousDesktop(follow := false)
	{
		currentDesktop := this.desktopMapper.getDesktopNumber()
		if(currentDesktop == 1) 
		{
			return this
		}
		return this.moveActiveWindowToDesktop(currentDesktop - 1, follow)
	}	
	
	getNumberOfDownsNeededToSelectDesktop(targetDesktop, currentDesktop)
	{
		; This part figures out how many times we need to push down within the context menu to get the desktop we want.	
		if (targetDesktop > currentDesktop)
		{
			targetDesktop -= 2
		}
		else
		{
			targetdesktop--
		}
		return targetDesktop
	}
}
class JPGIncDesktopChangerClass
{
	goToDesktopCallbackFunctionName := "goToDesktop"
	nextDesktopFunctionName := "goToNextDesktop"
	PreviousDesktopFunctionName := "goToPreviousDesktop"
	_postGoToDesktopFunctionName := ""
	
	__new() 
	{
		this.desktopMapper := new DesktopMapperClass(new VirtualDesktopManagerClass())
		return this
	}

	goToNextDesktop(keyCombo := "")
	{
		send("^#{right}")
		return this.doPostGoToDesktop()
	}
	
	goToPreviousDesktop(keyCombo := "")
	{
		send("^#{left}")
		return this.doPostGoToDesktop()
	}
	
	/*
	 *	swap to the given virtual desktop number
	 */
	goToDesktop(newDesktopNumber) 
	{
		debugger("in go to desktop changing to " newDesktopNumber)
		this._makeDesktopsIfRequired(newDesktopNumber)
			._goToDesktop(newDesktopNumber)
		closeMultitaskingViewFrame()
		this.doPostGoToDesktop()
		return this
	}
	
	_makeDesktopsIfRequired(minimumNumberOfDesktops)
	{
		currentNumberOfDesktops := this.desktopMapper.getNumberOfDesktops()
		loop, % minimumNumberOfDesktops - currentNumberOfDesktops
		{
			send("#^d")
		}
		
		return this
	}

	_goToDesktop(newDesktopNumber)
	{
		currentDesktop := this.desktopMapper.getDesktopNumber()
		direction := currentDesktop - newDesktopNumber
		distance := Abs(direction)
		debugger("distance to move is " distance "`ndirectin" direction)
		if(direction < 0)
		{
			debugger("Sending right! " distance "times")
			send("^#{right " distance "}")
		} else
		{
			send("^#{left " distance "}")
		}
		return this
	}
	
	doPostGoToDesktop() 
	{
		this._activateTopMostWindow()
		callFunction(this.postGoToDesktopFunctionName)
		return this
	}
	
	_activateTopMostWindow()
	{
		If(WinActive("ahk_exe explorer.exe"))
		{
			send !{tab}
		}
		return this
	}
}

class JPGIncDesktopManagerClass
{	
	__new()
	{
		this._desktopChanger := new JPGIncDesktopChangerClass()
		this._windowMover := new JPGIncWindowMoverClass()
		this.hotkeyManager := new JPGIncHotkeyManager()
		
		this._setupDefaultHotkeys()
		return this
	}
	
	/*
	 * Public API to setup virtual desktop hotkeys and callbacks
	 */
	setGoToDesktop(hotkeyKey)
	{
		this.hotkeyManager.setupNumberedHotkey(this._desktopChanger, this._desktopChanger.goToDesktopCallbackFunctionName, hotkeyKey)
		return this
	}
	setMoveWindowToDesktop(hotkeyKey)
	{
		this.hotkeyManager.setupNumberedHotkey(this._windowMover, this._windowMover.moveActiveWindowToDesktopFunctionName, hotkeyKey)
		return this
	}
	
	setGoToNextDesktop(hotkeyKey)
	{
		this.hotkeyManager.setupHotkey(this._desktopChanger, this._desktopChanger.nextDesktopFunctionName, hotkeyKey)
		return this
	}
	
	setGoToPreviousDesktop(hotkeyKey)
	{
		this.hotkeyManager.setupHotkey(this._desktopChanger, this._desktopChanger.PreviousDesktopFunctionName, hotkeyKey)
		return this
	}
	
	setMoveWindowToNextDesktop(hotkeyKey)
	{
		this.hotkeyManager.setupHotkey(this._windowMover, this._windowMover.moveToNextFunctionName, hotkeyKey)
		return this
	}
	
	setMoveWindowToPreviousDesktop(hotkeyKey)
	{
		this.hotkeyManager.setupHotkey(this._windowMover, this._windowMover.moveToPreviousFunctionName, hotkeyKey)
		return this
	}
	
	setCloseDesktop(hotkeyKey)
	{
		this.hotkeyManager.setupHotkey(this, "closeDesktop", hotkeyKey)
		return this
	}
	
	setNewDesktop(hotkeyKey)
	{
		this.hotkeyManager.setupHotkey(this, "newDesktop", hotkeyKey)
		return this
	}
	
	afterGoToDesktop(functionLabelOrClassWithCallMethodName)
	{
		this._desktopChanger.postGoToDesktopFunctionName := functionLabelOrClassWithCallMethodName
		return this
	}
	
	afterMoveWindowToDesktop(functionLabelOrClassWithCallMethodName)
	{
		this._windowMover.postMoveWindowFunctionName := functionLabelOrClassWithCallMethodName
		return this
	}
	
	/*
	 * end public api
	 */
	
	newDesktop(hotkeyCombo := "")
	{
		send("^#d")
		return this
	}
	
	closeDesktop(hotkeyCombo := "")
	{
		send("^#{f4}")
		return this
	}
	
	_setupDefaultHotkeys()
	{
		Hotkey, IfWinActive, ahk_class MultitaskingViewFrame
		this.hotkeyManager.setupNumberedHotkey(this._desktopChanger, this._desktopChanger.goToDesktopCallbackFunctionName, "")
		Hotkey, If
		return this
	}
}
Last edited by jpginc on 29 May 2016, 18:35, edited 4 times in total.
User avatar
fischgeek
Posts: 435
Joined: 29 Jan 2014, 21:39

Re: Windows 10 Desktop manager

20 Aug 2015, 08:56

Cool. Though, the win+shift+number doesn't move the window to that desktop. I just see the task switching screen.
jpginc
Posts: 124
Joined: 29 Sep 2013, 22:35

Re: Windows 10 Desktop manager

20 Aug 2015, 17:58

By "switching screen" do you mean that you have two monitors and it just jumps to the other monitor?
User avatar
fischgeek
Posts: 435
Joined: 29 Jan 2014, 21:39

Re: Windows 10 Desktop manager

25 Aug 2015, 15:09

No, I don't mean that. I have 3 monitors and when I attempted win+shift+number just shows the same screen as when I push win+tab
jpginc
Posts: 124
Joined: 29 Sep 2013, 22:35

Re: Windows 10 Desktop manager

25 Aug 2015, 18:41

Hmmm I added an error message to the code in the original post that will alert if it's an issue with the context menu do you want to try that.

Also, i think it needs administrator privileges to work properly (also added to the original post).

Let me know how it goes
yabbas
Posts: 1
Joined: 07 Sep 2015, 11:37

Re: Windows 10 Desktop manager

07 Sep 2015, 11:45

Thanks for this.

Can you extend it with the following features:
Right Mousebutton Drag Right -> go to next desktop. (you might want to use middle click drag)
Right Mousebutton drag Left -> go to previous desktop. (you might want to use middle click drag)
Mouse Wheel Right Tilt -> Move current selected window to next desktop maintaining same size, do not move to VD.
Mouse Wheel Left Tilt -> Move current selected window to previous desktop maintaining same size, do not move to VD.
Drag Window to Left Edge Continuous -> transfer window to previous VD, and move to that VD.
Drag Window to Right Edge continuous -> transfer window to next VD, and move to that VD.
Drag Window to corner -> pin to corner.
jpginc
Posts: 124
Joined: 29 Sep 2013, 22:35

Re: Windows 10 Desktop manager

19 Sep 2015, 02:49

Not entirely sure how i would go about doing that...Any ideas?
lexikos
Posts: 9690
Joined: 30 Sep 2013, 04:07
Contact:

Re: Windows 10 Desktop manager

19 Sep 2015, 07:36

The first two are easy with my mouse gesture script; just add this to Gestures_User.ahk:

Code: Select all

Gesture_L = ^#{Left}
Gesture_R = ^#{Right}
Mouse wheel tilt can usually be detected with WheelLeft:: and WheelRight:: hotkeys, unless you're using something like SetPoint/IntelliPoint.
thermiderp

Re: Windows 10 Desktop manager

22 Sep 2015, 00:54

jpginc wrote:Hmmm I added an error message to the code in the original post that will alert if it's an issue with the context menu do you want to try that.

Also, i think it needs administrator privileges to work properly (also added to the original post).

Let me know how it goes
It seems that I have the same issue. After I run the script and try to move a window with a +#1, the tab+win view is opened and nothing else happens.
This is in the executed lines view:
055: {
056: StringTrimLeft,newDesktopNumber,A_ThisHotkey,2
057: moveActiveWindowToDesktop(newDesktopNumber)
071: currentDesktopNumber := getCurrentDesktopNumber()
096: currentDesktopNumber := 0
097: Send,#{tab} (0.01)
098: WinWait,ahk_class MultitaskingViewFrame (3.39)

The hotkey I pressed (+#1) then stops doing anything. I can then press the same combination with any other number once and the same thing is reproduced. I don't seem to get any error messages.

Any ideas why it doesn't work? I'm using the Win10 Insider Preview build 10547, if that's of any concern.
thermiderp

Re: Windows 10 Desktop manager

22 Sep 2015, 01:50

thermiderp wrote:
jpginc wrote:Hmmm I added an error message to the code in the original post that will alert if it's an issue with the context menu do you want to try that.

Also, i think it needs administrator privileges to work properly (also added to the original post).

Let me know how it goes
It seems that I have the same issue. After I run the script and try to move a window with a +#1, the tab+win view is opened and nothing else happens.
This is in the executed lines view:
055: {
056: StringTrimLeft,newDesktopNumber,A_ThisHotkey,2
057: moveActiveWindowToDesktop(newDesktopNumber)
071: currentDesktopNumber := getCurrentDesktopNumber()
096: currentDesktopNumber := 0
097: Send,#{tab} (0.01)
098: WinWait,ahk_class MultitaskingViewFrame (3.39)

The hotkey I pressed (+#1) then stops doing anything. I can then press the same combination with any other number once and the same thing is reproduced. I don't seem to get any error messages.

Any ideas why it doesn't work? I'm using the Win10 Insider Preview build 10547, if that's of any concern.
And now that I examined it some more, I noticed that getCurrentDesktopNumber() doesn't seem to work, currentDesktopNumber gets assigned 0 regardless of which desktop I'm on when I press the hotkey.
jpginc
Posts: 124
Joined: 29 Sep 2013, 22:35

Re: Windows 10 Desktop manager

27 Sep 2015, 19:38

I'll have a look into this one in a week or two. Just super busy with uni at the moment i'm afraid...
thermiderp
Posts: 1
Joined: 22 Sep 2015, 01:53

Re: Windows 10 Desktop manager

28 Sep 2015, 03:02

I recently got it working somewhat by replasing all the WinWaits by sleeps of varying lengths. I wonder why the WinWaits didn't work?
jpginc
Posts: 124
Joined: 29 Sep 2013, 22:35

Re: Windows 10 Desktop manager

28 Sep 2015, 03:45

There must be some better (and more reliable) way of telling the window than checking the contents of the context menu though...
JRelland
Posts: 4
Joined: 20 Nov 2015, 01:57

Re: Windows 10 Desktop manager

02 Jan 2016, 03:42

Hi, thank you for this.

Is there a simple way (... say in one line :) ) to open an application (say Excel) in a dedicated virtual desktop with AHK with W10?

I would like to open this in a dedicated VD.

Code: Select all

#b::  				; Touche Win + b minuscule pour Blender
run, C:\Program Files\Blender Foundation\Blender\blender.exe
run, https://openclassrooms.com/courses/debutez-dans-la-3d-avec-blender
run, "C:\Program Files (x86)\Evernote\Evernote\ENScript.exe" showNotes /q "Tuto Blender OpenClassRoom"
Return
Thank you for your time and consideration.
sierratango
Posts: 2
Joined: 15 Jan 2016, 17:03

Re: Windows 10 Desktop manager

15 Jan 2016, 17:09

Code: Select all

SetTitleMatchMode 2

F1::WinHide Notepad
F2::WinShow Notepad
This works great for moving windows between virtual desktops in Windows 10.
optimist__prime
Posts: 3
Joined: 10 Mar 2016, 10:33

Re: Windows 10 Desktop manager

01 Apr 2016, 14:06

Using the script from https://github.com/pmb6tz/windows-desktop-switcher by pmb6tz, and the realization that there is a context menu for sending a window to a desktop, I was able to create the ultimate Windows 10 Desktop Manager that takes the features of your script and the reliability of his.
I also took care of some bugs that he had in his script.
Thanks to pmb6tz, this script maps the current and total desktop counts from the registry, making it very reliable and fast!
I completely re-wrote the send window to desktop feature for multi monitor support.
This works for multi monitor setups in a horizontal configuration only, let me know if you need vertical multi monitor support.
The send window to desktop feature is not designed to work while the TaskView screen is open, but while the desired window is active.

Hotkeys:

Code: Select all

WinKey + Number        = Goto desktop number                  (Works while in TaskView)
Ctrl + WinKey + Number = Send active window to desktop number (Doesn't work while in TaskView)
Ctrl + WinKey + D      = Create new virtual desktop           (Works while in TaskView)
WinKey + F4            = Delete current virtual desktop       (Works while in TaskView)
Script:

Code: Select all

; User config! 
; This section binds the key combo to the switch/create/delete actions
#1::
#2::
#3::
#4::
#5::
#6::
#7::
#8::
#9::
{
     StringTrimLeft, number, A_ThisHotkey, 1
     switchDesktopByNumber(number)
     return
}
#0::switchDesktopByNumber(10)
^#d::createVirtualDesktop()
#f4::deleteVirtualDesktop()

; This section binds the key combo to send a window to a specific desktop
^#1::
^#2::
^#3::
^#4::
^#5::
^#6::
^#7::
^#8::
^#9::
{
     StringTrimLeft, number, A_ThisHotkey, 2
     sendWindowToDesktop(number)
     return
}
^#0::sendWindowToDesktop(10)

; Globals
DesktopCount = 1        ; Windows starts with however many desktops were last open at boot, we just set this to 1 because we map the desktopcount from the registry later
CurrentDesktop = 1      ; Desktop count is 1-indexed (Microsoft numbers them this way)
CurrentDesktopId = 0

;
; This function examines the registry to build an accurate list of the current virtual desktops and which one we're currently on.
; Current desktop UUID appears to be in HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SessionInfo\1\VirtualDesktops
; List of desktops appears to be in HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VirtualDesktops
;
mapDesktopsFromRegistry() {
    global CurrentDesktop, DesktopCount, CurrentDesktopId
    
    
    ; Get the current session ID so we know where to look for the desktop UUID
	; https://msdn.microsoft.com/en-us/library/windows/desktop/aa383835
	SessionInfoNumber := DllCall("WTSGetActiveConsoleSessionId")
	
	; Get the current desktop UUID. Length should be 32 always, but there's no guarantee this couldn't change in a later Windows release so we check.
    RegRead, CurrentDesktopId, HKEY_CURRENT_USER, SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SessionInfo\%SessionInfoNumber%\VirtualDesktops, CurrentVirtualDesktop
    IdLength := StrLen(CurrentDesktopId)


    ; Get a list of the UUIDs for all virtual desktops on the system
    RegRead, DesktopList, HKEY_CURRENT_USER, SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VirtualDesktops, VirtualDesktopIDs
    DesktopListLength := StrLen(DesktopList)

    ; Figure out how many virtual desktops there are
    DesktopCount := DesktopListLength/IdLength

    ; Parse the REG_DATA string that stores the array of UUID's for virtual desktops in the registry.
    i := 0
    while (i < DesktopCount) {
        StartPos := (i * IdLength) + 1
        DesktopIter := SubStr(DesktopList, StartPos, IdLength)
        OutputDebug, The iterator is pointing at %DesktopIter% and count is %i%.
		
        ; Break out if we find a match in the list. If we didn't find anything, keep the 
        ; old guess and pray we're still correct :-D.
        if (DesktopIter = CurrentDesktopId) {
            CurrentDesktop := i + 1
            OutputDebug, Current desktop number is %CurrentDesktop% with an ID of %DesktopIter%.
            break
        }
        i++
    }
}

;
; This function switches to the desktop number provided.
;
switchDesktopByNumber(targetDesktop)
{
    global CurrentDesktop, DesktopCount
    
    ; Re-generate the list of desktops and where we fit in that. We do this because 
    ; the user may have switched desktops via some other means than the script.
    mapDesktopsFromRegistry()
    
    ; If the user is trying to swap to a desktop that isn't valid, don't let
    ; them because we'd lose track of numbering. Unfortunately, we have to assume
    ; that this script was started when there were three desktops because we have
    ; no way to check.
    if (targetDesktop > DesktopCount) {
        return
    }
    
    ; Go right until we reach the desktop we want
    while(CurrentDesktop < targetDesktop) {
        Send ^#{Right}  
        CurrentDesktop++
        OutputDebug, [right] target: %targetDesktop% current: %CurrentDesktop%
    }
    
    ; Go left until we reach the desktop we want
    while(CurrentDesktop > targetDesktop) {
        Send ^#{Left}
        CurrentDesktop--
        OutputDebug, [left] target: %targetDesktop% current: %CurrentDesktop%
    }
}

;
; This function sends the current window to the target desktop
;
sendWindowToDesktop(targetDesktop)
{
    global CurrentDesktop, DesktopCount, CurrentDesktopID
    
    ; Re-generate the list of desktops and where we fit in that. We do this because 
    ; the user may have switched desktops via some other means than the script.
    mapDesktopsFromRegistry()
    
	; If the user is trying to send the window to a desktop that doesn't exist,
	; or the one we're already on, don't do anything.	
    if (targetDesktop > DesktopCount || CurrentDesktop == targetDesktop)
    {
        return
    }	
	
	; This part figures out how many times we need to hit Tab to get to the
	; monitor with the window we are trying to send to another desktop.	
	activemonitor := MonitorFromWindow()
	SysGet, monitorcount, MonitorCount
	SysGet, primarymonitor, MonitorPrimary
	
	If (activemonitor > primarymonitor)
	{
		monitoriter := activemonitor - primarymonitor
	}
	else If (activemonitor < primarymonitor)
	{
		monitoriter := monitorcount - primarymonitor + activemonitor
	}
	else
	{
		monitoriter := 0
	}
	monitoriter *= 2
	
	; This part figures out how many times we need to push down within the context menu to get the desktop we want.	
	if (targetDesktop > CurrentDesktop)
	{
	    targetDesktop -= 2
	}
	else
	{
	    targetdesktop--
	}
	
	; This part does the dirty work and sends the keypresses needed to send the
	; window to another desktop based on the variables we figured out above.	
	Send #{tab}
	winwait, ahk_class MultitaskingViewFrame
	
	Send {Tab %monitoriter%}
	
	Send {Appskey}m{Down %targetDesktop%}{Enter}
	Send #{tab}
}

;
; This function returns the monitor number of the current window
;
MonitorFromWindow()
{
	WinGetActiveTitle, activewindow
	WinGetPos, x, y, width, height, %activewindow%	
	; MsgBox, Window Position/Size:`nX: %X%`nY: %Y%`nWidth: %width%`nHeight: %height%	
	SysGet, monitorcount, MonitorCount
	SysGet, primarymonitor, MonitorPrimary	
	; MsgBox, Monitor Count: %MonitorCount%	
	Loop %monitorcount%
	{
		SysGet, mon, Monitor, %a_index%
		; MsgBox, Primary Monitor: %primarymonitor%`nDistance between monitor #%a_index%'s right border and Primary monitor's left border (Left < 0, Right > 0):`n%monRight%px		
		If (x < monRight - width / 2 || monitorcount = a_index)
		{
			return %a_index%
		}
	}
}

;
; This function creates a new virtual desktop and switches to it
;
createVirtualDesktop()
{
    global CurrentDesktop, DesktopCount
    if (DesktopCount = 10) {
        return
    }
    Send, #^d
    DesktopCount++
    CurrentDesktop = %DesktopCount%
    OutputDebug, [create] desktops: %DesktopCount% current: %CurrentDesktop%
}

;
; This function deletes the current virtual desktop
;
deleteVirtualDesktop()
{
    global CurrentDesktop, DesktopCount
    Send, #^{F4}
    DesktopCount--
    CurrentDesktop--
    OutputDebug, [delete] desktops: %DesktopCount% current: %CurrentDesktop%
}

; Main

mapDesktopsFromRegistry()
OutputDebug, [loading] desktops: %DesktopCount% current: %CurrentDesktop%
Last edited by optimist__prime on 14 Apr 2016, 05:24, edited 2 times in total.
b0bi
Posts: 7
Joined: 07 Apr 2016, 13:11

Re: Windows 10 Desktop manager

07 Apr 2016, 13:39

optimist__prime wrote:Using the script from https://github.com/pmb6tz/windows-desktop-switcher by pmb6tz, and the realization that there is a context menu for sending a window to a desktop, I was able to create the ultimate Windows 10 Desktop Manager that takes the features of your script and the reliability of his.
Thanks for your code, it work well on my configuration :D

However, I'm trying to modify your script (I want another behavior), but it can't make it, I'm still a beginner in AHK :( Maybe you could help me create this behavior ? :roll:

- on my keyboard, I never use the "tilde" key (the one on top of TAB)(http://s30.postimg.org/8nnux37tt/Capture.png)
- I only use 2 virtuals keyboard

here is thebahavior I'm trying to achieve:
- if I press tild key, I go to the other desktop
- if I press control + tild key, the window is moved to the other desktop

on my swiss keyboard, the hook for this key is:

Code: Select all

Sc029::
but maybe on other keyboard it will be something like this:

Code: Select all

`::
jpginc
Posts: 124
Joined: 29 Sep 2013, 22:35

Re: Windows 10 Desktop manager

07 Apr 2016, 18:11

@optimist__prime I like what you have done with the script. Unfortunately the registry key thing doesn't work when there is more than one session...Also the monitor function doesn't work with my monitor configuration for some reason. I'll put a bit more work into this script over the weekend and hopefully post an updated script. There is a dll that looks interesting but i cant get it working https://msdn.microsoft.com/en-us/librar ... s.85).aspx. someone else has tried here https://autohotkey.com/boards/viewtopic.php?t=12388

@b0bi this isn't tested but you should be able to replace the config section of optimist__prime's script with something like this

Code: Select all

; Globals
DesktopCount = 1        ; Windows starts with 2 desktops at boot
CurrentDesktop = 1      ; Desktop count is 1-indexed (Microsoft numbers them this way)
mapDesktopsFromRegistry()

`::switchDesktopByNumber(Mod(CurrentDesktop + 1, 2) + 1)
^`::sendWindowToDesktop(Mod(CurrentDesktop + 1, 2) + 1)

jpginc
Posts: 124
Joined: 29 Sep 2013, 22:35

Re: Windows 10 Desktop manager

13 Apr 2016, 18:08

I'm still playing around with the IVirtualDesktopManager dll but haven't quite got it working yet...
optimist__prime
Posts: 3
Joined: 10 Mar 2016, 10:33

Re: Windows 10 Desktop manager

14 Apr 2016, 05:23

jpginc wrote:I'm still playing around with the IVirtualDesktopManager dll but haven't quite got it working yet...
Ahahahaaaa tell me about it!! I spent a lot of time yesterday trying to figure it out! The Microsoft documentation is quite limited though and I came to the conclusion, from other forums, that the IVirtualDesktopManager Interface API is intended for applications to send their own windows to other desktops. I don't think it's possible with AHK at the moment. I was trying to get the MoveWindowToDesktop method of IVirtualDesktopManager to work so we could make this script "cleaner".

This guy seems to have figured out how to switch desktops without simulating keypresses, https://autohotkey.com/boards/viewtopic.php?t=14881. I need to learn more about COM interfaces, I'm not quite so far yet.

I figured out how to use Windows DLLs though, which enabled me to fix one of the problems! :D Thanks for your feedback about the multiple sessions I didn't even realize until you said something what the number under session info represented!! :lol:
So, by adding a DllCall I was able to get the active session and use it to get the registry values, I updated the script in my last post.

As for the multiple desktop function, I'm having a hard time thinking of what might have gone wrong, it seems pretty solid and works perfectly for me. I added three message boxes in comments to the MonitorFromWindow() function, you can just remove the ; if you want to try to debug it a bit. Maybe the SysGet isn't working on your machine or something.

@b0bi: The code jpginc posted should definitely work for you! :bravo:

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: Spikea and 239 guests