|
#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") |
|
} |