Skip to content

Instantly share code, notes, and snippets.

@RJNY
Last active January 6, 2026 01:29
Show Gist options
  • Select an option

  • Save RJNY/517f580b927762ff306303e66a562bb8 to your computer and use it in GitHub Desktop.

Select an option

Save RJNY/517f580b927762ff306303e66a562bb8 to your computer and use it in GitHub Desktop.
#Requires AutoHotkey v2.0
#SingleInstance Force
DetectHiddenWindows true
LockFile := A_Temp "\AutoPauseResume.lock"
; Check if the lock file exists before deleting it
if FileExist(LockFile)
FileDelete(LockFile)
; --------------------------------------------------------------------------
; 1) Define excluded processes as a map (key-value)
; - The key is the process name in lowercase, mapped to `true`.
; --------------------------------------------------------------------------
excludedProcesses := Map()
excludedProcesses["explorer.exe"] := true
excludedProcesses["csrss.exe"] := true
excludedProcesses["winlogon.exe"] := true
excludedProcesses["svchost.exe"] := true
excludedProcesses["dwm.exe"] := true
excludedProcesses["cmd.exe"] := true
excludedProcesses["chrome.exe"] := true
excludedProcesses["powershell.exe"] := true
excludedProcesses["sublime_text.exe"] := true
excludedProcesses["sublime_merge.exe"] := true
excludedProcesses["steamwebhelper.exe"] := true
excludedProcesses["openconsole.exe"] := true
excludedProcesses["windowsterminal.exe"] := true
excludedProcesses["playnite.desktopapp.exe"] := true
excludedProcesses["playnite.fullscreenapp.exe"] := true
; --------------------------------------------------------------------------
; 2) Get the active window's PID & Process Name, confirm it's not excluded
; --------------------------------------------------------------------------
if !WinExist("A")
{
MsgBox "No active window detected. Please focus a window to suspend."
ExitApp
}
pid := WinGetPID("A")
if !pid
{
MsgBox "Failed to get the active window's PID."
ExitApp
}
processName := WinGetProcessName()
if !processName
{
MsgBox "Failed to retrieve the active window's process name."
ExitApp
}
; Convert the process name to lowercase for case-insensitive matching
procLower := StrLower(processName)
; Check if `excludedProcesses` has this process in its keys, and if the value is true
if excludedProcesses.Has(procLower) && excludedProcesses[procLower]
{
; Excluded process, so do nothing and exit.
ExitApp
}
; --------------------------------------------------------------------------
; 3) Suspend the process & save PID to file, then exit
; --------------------------------------------------------------------------
SuspendProcess(pid)
PidFile := A_Temp "\AutoPauseResume.pid"
FileAppend(pid, PidFile)
ExitApp
; --------------------------------------------------------------------------
SuspendProcess(pid)
{
PROCESS_ALL_ACCESS := 0x1F0FFF
hProc := DllCall("OpenProcess", "UInt", PROCESS_ALL_ACCESS, "Int", 0, "UInt", pid, "Ptr")
if !hProc
{
MsgBox "Failed to open process (PID: " pid ")."
ExitApp
}
hNtdll := DllCall("GetModuleHandle", "Str", "ntdll.dll", "Ptr")
if !hNtdll
{
MsgBox "Failed to get module handle for ntdll.dll."
ExitApp
}
NtSuspendProcess := DllCall("GetProcAddress", "Ptr", hNtdll, "AStr", "NtSuspendProcess", "Ptr")
if !NtSuspendProcess
{
MsgBox "Failed to locate NtSuspendProcess in ntdll.dll."
ExitApp
}
DllCall(NtSuspendProcess, "Ptr", hProc)
DllCall("CloseHandle", "Ptr", hProc)
}
; --------------------------------------------------------------------------
ResumeProcess(pid)
{
PROCESS_ALL_ACCESS := 0x1F0FFF
hProc := DllCall("OpenProcess", "UInt", PROCESS_ALL_ACCESS, "Int", 0, "UInt", pid, "Ptr")
if !hProc
{
; Process may no longer exist, don't do anything.
; MsgBox "Failed to open process (PID: " pid ")."
ExitApp
}
hNtdll := DllCall("GetModuleHandle", "Str", "ntdll.dll", "Ptr")
if !hNtdll
{
MsgBox "Failed to get module handle for ntdll.dll."
ExitApp
}
NtResumeProcess := DllCall("GetProcAddress", "Ptr", hNtdll, "AStr", "NtResumeProcess", "Ptr")
if !NtResumeProcess
{
MsgBox "Failed to locate NtResumeProcess in ntdll.dll."
ExitApp
}
DllCall(NtResumeProcess, "Ptr", hProc)
DllCall("CloseHandle", "Ptr", hProc)
}
#Requires AutoHotkey v2.0
#SingleInstance Force
PidFile := A_Temp "\AutoPauseResume.pid"
; Check if PID file exists
if !FileExist(PidFile)
{
ExitApp
}
; Read the PID from the file
pid := FileRead(PidFile)
pid := Trim(pid)
; Delete the PID file
FileDelete(PidFile)
; Resume the process
if pid
{
ResumeProcess(pid)
}
ExitApp
; --------------------------------------------------------------------------
ResumeProcess(pid)
{
PROCESS_ALL_ACCESS := 0x1F0FFF
hProc := DllCall("OpenProcess", "UInt", PROCESS_ALL_ACCESS, "Int", 0, "UInt", pid, "Ptr")
if !hProc
{
return
}
hNtdll := DllCall("GetModuleHandle", "Str", "ntdll.dll", "Ptr")
if !hNtdll
{
DllCall("CloseHandle", "Ptr", hProc)
return
}
NtResumeProcess := DllCall("GetProcAddress", "Ptr", hNtdll, "AStr", "NtResumeProcess", "Ptr")
if !NtResumeProcess
{
DllCall("CloseHandle", "Ptr", hProc)
return
}
DllCall(NtResumeProcess, "Ptr", hProc)
DllCall("CloseHandle", "Ptr", hProc)
}
@RJNY
Copy link
Author

RJNY commented Jan 6, 2026

Auto Pause/Resume Scripts (Modified for Windows Sleep Support)

AutoHotkey v2.0 scripts for automatically pausing games when disconnecting from Apollo/Sunshine streams and allowing Windows sleep/hibernate timers to activate.

How It Works

pause.ahk

  • Suspends the active window's process (game/application) when you disconnect
  • Saves the process ID (PID) to a temporary file
  • Exits immediately to allow Windows sleep timers to activate
  • Includes an exclusion list for system processes

resume.ahk

  • Reads the saved PID from the temporary file
  • Resumes the suspended process when you reconnect
  • Cleans up the PID file and exits

Key Differences from Original Script

Original Modified
Pause script stayed running with a 500ms timer checking for a lock file Pause script exits immediately after suspending and saving PID
Resume script only created a lock file to signal the pause script Resume script directly resumes the process by reading the PID file
Pause script prevented Windows sleep due to active timer loop No active script = Windows sleep/hibernate timers work naturally
Auto-resumed game if pause script was manually terminated No automatic recovery if resume script isn't called

Benefits

  • Windows can sleep/hibernate after configured idle timeout
  • Lower resource usage when game is paused
  • Simpler architecture with no timer loops

⚠️ Potential Risk

No automatic recovery: If the resume script fails to run on reconnect (due to Apollo misconfiguration, network issues, etc.), the game will remain suspended indefinitely. You would need to:

  • Manually run the resume script to resume the game, or
  • Kill and restart the suspended game process

Mitigation: Ensure Apollo/Sunshine is correctly configured to execute the resume script as the "resume/undo" command for your stream configuration.

Requirements

  • AutoHotkey v2.0
  • Apollo/Sunshine configured to run pause script on disconnect and resume script on connect
  • Windows power settings configured for desired sleep/hibernate timeout

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment