|
#Requires AutoHotkey v2.0
|
|
#SingleInstance Force
|
|
#UseHook True
|
|
#Warn
|
|
|
|
Persistent
|
|
SetWinDelay(0)
|
|
|
|
; Cycle full-screen Microsoft Dev Box / Cloud PC sessions from the local host.
|
|
; Targets launched remote session windows (msrdc.exe), not the Windows App launcher.
|
|
; Shortcuts:
|
|
; Ctrl+Alt+Home -> next open Dev Box
|
|
; Ctrl+Alt+Shift+Home -> minimize Dev Box sessions and return to host
|
|
|
|
Log("Started")
|
|
|
|
$^!+Home::{
|
|
KeyWait("Home")
|
|
Sleep(50)
|
|
GoToHost("Ctrl+Alt+Shift+Home")
|
|
}
|
|
|
|
$^!Home::{
|
|
KeyWait("Home")
|
|
Sleep(50)
|
|
CycleDevBox(1, "Ctrl+Alt+Home")
|
|
}
|
|
|
|
CycleDevBox(direction, source) {
|
|
windows := GetDevBoxWindows()
|
|
windows := ApplySavedOrder(windows)
|
|
WriteWindowOrder(windows)
|
|
Log(source " pressed; found " windows.Length " Dev Box windows")
|
|
|
|
if (windows.Length = 0) {
|
|
TrayTip("Dev Box switcher", "No Dev Box windows found.", 2)
|
|
return
|
|
}
|
|
|
|
activeTitle := ""
|
|
try activeTitle := WinGetTitle("A")
|
|
|
|
lastTitle := ReadLastTitle()
|
|
currentIndex := 0
|
|
|
|
for index, info in windows {
|
|
if (info.Title = activeTitle) {
|
|
currentIndex := index
|
|
break
|
|
}
|
|
}
|
|
|
|
if (currentIndex = 0 && lastTitle != "") {
|
|
for index, info in windows {
|
|
if (info.Title = lastTitle) {
|
|
currentIndex := index
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if (currentIndex = 0) {
|
|
currentIndex := direction > 0 ? 0 : windows.Length + 1
|
|
}
|
|
|
|
targetIndex := currentIndex + direction
|
|
if (targetIndex > windows.Length) {
|
|
targetIndex := 1
|
|
} else if (targetIndex < 1) {
|
|
targetIndex := windows.Length
|
|
}
|
|
|
|
target := windows[targetIndex]
|
|
WriteLastTitle(target.Title)
|
|
Log("Activating " targetIndex "/" windows.Length ": " target.Title " (active='" activeTitle "', last='" lastTitle "')")
|
|
|
|
try WinRestore("ahk_id " target.Hwnd)
|
|
WinActivate("ahk_id " target.Hwnd)
|
|
|
|
if !WinWaitActive("ahk_id " target.Hwnd, , 0.5) {
|
|
DllCall("SetForegroundWindow", "ptr", target.Hwnd)
|
|
Log("Used SetForegroundWindow fallback")
|
|
}
|
|
}
|
|
|
|
GoToHost(source) {
|
|
windows := GetDevBoxWindows()
|
|
windows := ApplySavedOrder(windows)
|
|
WriteWindowOrder(windows)
|
|
Log(source " pressed; minimizing " windows.Length " Dev Box windows")
|
|
|
|
for info in windows {
|
|
try WinMinimize("ahk_id " info.Hwnd)
|
|
}
|
|
}
|
|
|
|
GetDevBoxWindows() {
|
|
filters := [
|
|
"ahk_exe msrdc.exe",
|
|
"ahk_exe msrdcw.exe",
|
|
"ahk_exe mstsc.exe"
|
|
]
|
|
|
|
windows := []
|
|
seen := Map()
|
|
|
|
for filter in filters {
|
|
for hwnd in WinGetList(filter) {
|
|
if (seen.Has(hwnd)) {
|
|
continue
|
|
}
|
|
|
|
try title := WinGetTitle("ahk_id " hwnd)
|
|
catch {
|
|
continue
|
|
}
|
|
|
|
if (title = "" || IsIgnoredWindowTitle(title)) {
|
|
continue
|
|
}
|
|
|
|
try className := WinGetClass("ahk_id " hwnd)
|
|
catch {
|
|
continue
|
|
}
|
|
|
|
if !IsDevBoxSessionClass(className) {
|
|
continue
|
|
}
|
|
|
|
try style := WinGetStyle("ahk_id " hwnd)
|
|
catch {
|
|
continue
|
|
}
|
|
|
|
if !(style & 0x10000000) {
|
|
continue
|
|
}
|
|
|
|
windows.Push({ Hwnd: hwnd, Title: title })
|
|
seen[hwnd] := true
|
|
}
|
|
}
|
|
|
|
return windows
|
|
}
|
|
|
|
IsIgnoredWindowTitle(title) {
|
|
ignoredTitles := Map(
|
|
"BBar", true,
|
|
"Default IME", true,
|
|
"MSCTFIME UI", true
|
|
)
|
|
|
|
return ignoredTitles.Has(title)
|
|
}
|
|
|
|
IsDevBoxSessionClass(className) {
|
|
return className = "TscShellContainerClass"
|
|
}
|
|
|
|
Log(message) {
|
|
FileAppend(FormatTime(, "yyyy-MM-dd HH:mm:ss") " " message "`n", A_ScriptDir "\CycleDevBoxes.log", "UTF-8")
|
|
}
|
|
|
|
ReadLastTitle() {
|
|
path := A_ScriptDir "\CycleDevBoxes.state"
|
|
if !FileExist(path) {
|
|
return ""
|
|
}
|
|
|
|
try return Trim(FileRead(path, "UTF-8"))
|
|
catch {
|
|
return ""
|
|
}
|
|
}
|
|
|
|
WriteLastTitle(title) {
|
|
path := A_ScriptDir "\CycleDevBoxes.state"
|
|
try FileDelete(path)
|
|
try FileAppend(title, path, "UTF-8")
|
|
}
|
|
|
|
ReadWindowOrder() {
|
|
path := A_ScriptDir "\CycleDevBoxes.order"
|
|
titles := []
|
|
|
|
if !FileExist(path) {
|
|
return titles
|
|
}
|
|
|
|
try text := FileRead(path, "UTF-8")
|
|
catch {
|
|
return titles
|
|
}
|
|
|
|
for title in StrSplit(text, "`n", "`r") {
|
|
title := Trim(title)
|
|
if (title != "") {
|
|
titles.Push(title)
|
|
}
|
|
}
|
|
|
|
return titles
|
|
}
|
|
|
|
ApplySavedOrder(windows) {
|
|
savedTitles := ReadWindowOrder()
|
|
ordered := []
|
|
used := Map()
|
|
|
|
for savedTitle in savedTitles {
|
|
for info in windows {
|
|
if (!used.Has(info.Title) && info.Title = savedTitle) {
|
|
ordered.Push(info)
|
|
used[info.Title] := true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
for info in windows {
|
|
if !used.Has(info.Title) {
|
|
ordered.Push(info)
|
|
used[info.Title] := true
|
|
}
|
|
}
|
|
|
|
return ordered
|
|
}
|
|
|
|
WriteWindowOrder(windows) {
|
|
path := A_ScriptDir "\CycleDevBoxes.order"
|
|
lines := ""
|
|
|
|
for info in windows {
|
|
lines .= info.Title "`n"
|
|
}
|
|
|
|
try FileDelete(path)
|
|
try FileAppend(lines, path, "UTF-8")
|
|
}
|