-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathSnoozeGuard.ps1
More file actions
207 lines (172 loc) · 6.96 KB
/
SnoozeGuard.ps1
File metadata and controls
207 lines (172 loc) · 6.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# Script that takes 5 arguments, latter three having default values.
# "-requiresSystem" followed by a list of process names and "-requiresDisplay" followed by a list of process names.
# "-requiresSystem" will prevent the system going to sleep while the process is running,
# "-requiresDisplay" will prevent the display from going to sleep while the process is running, and also implies that the system won't go to sleep either.
# (in other words, -requiresDisplay will effectivelly set both flags)
#
# Parameters are:
# -requiresSystem "<process list>" - processes that will prevent the system from going to sleep
# -requiresDisplay "<process list>" - processes that will prevent the display from going to sleep
# -pollingRate <rate in seconds> - how often to poll for these processes to apply the ExecutionState power-state change. Default value is 120.
# -focusOnly <boolean - $True, $False, 1 or 0> - should the ExecutionState power-state change happen only if the process is focused, or just visible. Default falue is 'true'
# -oneTime <boolean - $True, $False, 1 or 0> - should the script actually poll and rerun itself periodically or just run once. Default value is 'false'
#
# USAGE: SnoozeGuard.ps1 -requiresDisplay "mstsc" -requiresSystem "handbrake" -focusOnly 0 -pollingRate 120 -oneTime 0
param (
[string[]]$requiresSystem,
[string[]]$requiresDisplay,
[int]$pollingRate = 120,
[bool]$focusOnly = $true,
[bool]$oneTime = $false
)
# Check if PowerState type is already defined (GPT suggests this, not sure why, since for me it does need to do this: possible for compatibility reasons?)
if (-not ([System.Management.Automation.PSTypeName]'PowerState').Type) {
# Define the PowerState enumeration
Add-Type @"
using System;
[Flags]
public enum PowerState : uint
{
ES_CONTINUOUS = 0x80000000,
ES_SYSTEM_REQUIRED = 0x00000001,
ES_DISPLAY_REQUIRED = 0x00000002
}
"@
}
# Check if NativeMethods type is already defined
if (-not ([System.Management.Automation.PSTypeName]'NativeMethods').Type) {
# Define the NativeMethods class
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class NativeMethods
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint SetThreadExecutionState(uint esFlags);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
}
"@
}
if (-not ([System.Management.Automation.PSTypeName]'WindowMethods').Type) {
# DEfine WindowMethods class
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class WindowMethods {
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsIconic(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWindowVisible(IntPtr hWnd);
}
"@
}
function SetExecutionState {
param (
[PowerState]$esFlags
)
$state = [PowerState]::ES_CONTINUOUS -bor $esFlags
# Suppress the output of SetThreadExecutionState
$result = [NativeMethods]::SetThreadExecutionState($state)
if ($result -eq 0) {
Write-Host "Error: Unable to set execution state." -ForegroundColor Red
}
Get-StateNames -Value ([NativeMethods]::SetThreadExecutionState(0)) # Get the current state
}
function Get-StateNames {
param (
[uint32]$Value
)
if ($Value -band [PowerState]::ES_SYSTEM_REQUIRED -and $Value -band [PowerState]::ES_DISPLAY_REQUIRED) {
Write-Host "Fully awake" -ForegroundColor DarkGreen
}
elseif ($Value -band [PowerState]::ES_SYSTEM_REQUIRED) {
Write-Host "System is awake" -ForegroundColor DarkYellow
}
elseif ($Value -band [PowerState]::ES_DISPLAY_REQUIRED) {
Write-Host "Display is awake" -ForegroundColor DarkBlue
}
else {
Write-Host "Feeling sleepy" -ForegroundColor Gray
}
}
function IsProcessRunning {
param (
[string[]]$processName
)
foreach ($name in $processName) {
Write-Host "Searching for ""$name""..." -ForegroundColor Gray
$process = Get-Process -Name $name -ErrorAction SilentlyContinue
if ($process) {
Write-Host "Process ""$name"" found" -ForegroundColor Gray
return $true
}
}
return $false
}
function IsProcessFocused {
param (
[string]$name
)
$foregroundWindow = [NativeMethods]::GetForegroundWindow()
if ($foregroundWindow -eq [IntPtr]::Zero) {
return $false
}
$processId = 0
[NativeMethods]::GetWindowThreadProcessId($foregroundWindow, [ref]$processId) | Out-Null
$focusedProcess = Get-Process -Id $processId -ErrorAction SilentlyContinue
if ($focusedProcess -and $focusedProcess.ProcessName -eq $name) {
return $true
}
return $false
}
function IsProcessVisible {
param (
[string]$processName
)
# Get the process by name
$process = Get-Process -Name $processName -ErrorAction SilentlyContinue
# Check if the process is found
if ($process -ne $null) {
# Check if the main window is minimized or hidden
if ($process.MainWindowHandle -ne [IntPtr]::Zero) {
$mainWindow = $process.MainWindowHandle
$isMinimized = [WindowMethods]::IsIconic($mainWindow)
$isVisible = [WindowMethods]::IsWindowVisible($mainWindow)
if ($isMinimized -or -not $isVisible) {
return $false
} else {
return $true
}
}
}
return $false
}
while (($requiresSystem -ne $null -and $requiresSystem.Length -gt 0) -or ($requiresDisplay -ne $null -and $requiresDisplay.Length -gt 0)) {
if (IsProcessRunning $requiresDisplay) {
$executionState = [PowerState]::ES_SYSTEM_REQUIRED
foreach ($name in $requiresDisplay) {
if (($focusOnly -and (IsProcessFocused $name)) -or (-not $focusOnly -and ((IsProcessFocused $name) -or (IsProcessVisible $name)))) {
Write-Host "Process ""$name"" wants display" -ForegroundColor Gray
$executionState = $executionState -bor [PowerState]::ES_DISPLAY_REQUIRED
break # Exit the loop once one focused process is found
}
}
SetExecutionState -esFlags $executionState
} elseif (IsProcessRunning $requiresSystem) {
SetExecutionState -esFlags ES_SYSTEM_REQUIRED
} else {
Write-Host "No processes found" -ForegroundColor Gray
}
if ($oneTime) {
break
} else {
Start-Sleep -Seconds $pollingRate
}
}
# Release the execution state when the process is not running
SetExecutionState -esFlags ES_CONTINUOUS | Out-Null