@gurugokul/doomscroller 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/LICENSE +21 -0
- package/README.md +323 -2
- package/dist/adapters/claude-code-hooks.js +27 -0
- package/dist/adapters/claude-code-hooks.js.map +1 -0
- package/dist/adapters/cli-wrapper.js +230 -0
- package/dist/adapters/cli-wrapper.js.map +1 -0
- package/dist/adapters/command-code-hooks.js +32 -0
- package/dist/adapters/command-code-hooks.js.map +1 -0
- package/dist/adapters/file-activity.js +78 -0
- package/dist/adapters/file-activity.js.map +1 -0
- package/dist/adapters/hook-settings.js +164 -0
- package/dist/adapters/hook-settings.js.map +1 -0
- package/dist/adapters/hook.js +17 -0
- package/dist/adapters/hook.js.map +1 -0
- package/dist/adapters/hotkey.js +50 -0
- package/dist/adapters/hotkey.js.map +1 -0
- package/dist/adapters/ide-activity.js +245 -0
- package/dist/adapters/ide-activity.js.map +1 -0
- package/dist/adapters/ports.js +3 -0
- package/dist/adapters/ports.js.map +1 -0
- package/dist/adapters/wrapper-tracker.js +77 -0
- package/dist/adapters/wrapper-tracker.js.map +1 -0
- package/dist/cli.js +343 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/config.js +192 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/daemon.js +156 -0
- package/dist/core/daemon.js.map +1 -0
- package/dist/core/ipc.js +215 -0
- package/dist/core/ipc.js.map +1 -0
- package/dist/core/logger.js +53 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/sound.js +38 -0
- package/dist/core/sound.js.map +1 -0
- package/dist/core/state-machine.js +272 -0
- package/dist/core/state-machine.js.map +1 -0
- package/dist/core/stats.js +109 -0
- package/dist/core/stats.js.map +1 -0
- package/dist/core/targets.js +27 -0
- package/dist/core/targets.js.map +1 -0
- package/dist/core/types.js +6 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/util/errors.js +16 -0
- package/dist/util/errors.js.map +1 -0
- package/dist/util/paths.js +77 -0
- package/dist/util/paths.js.map +1 -0
- package/dist/windows/cpu-probe.js +108 -0
- package/dist/windows/cpu-probe.js.map +1 -0
- package/dist/windows/key-listener.js +198 -0
- package/dist/windows/key-listener.js.map +1 -0
- package/dist/windows/win32.js +158 -0
- package/dist/windows/win32.js.map +1 -0
- package/dist/windows/window-controller.js +3 -0
- package/dist/windows/window-controller.js.map +1 -0
- package/package.json +57 -4
- package/scripts/cpu-sampler.ps1 +83 -0
- package/scripts/key-watcher.ps1 +132 -0
- package/scripts/win32-window.ps1 +344 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[int]$ParentPid = 0,
|
|
3
|
+
[string]$HotkeyKey = 'D',
|
|
4
|
+
[int]$IntervalMs = 15
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
$ErrorActionPreference = 'Stop'
|
|
8
|
+
[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
|
|
9
|
+
$OutputEncoding = New-Object System.Text.UTF8Encoding($false)
|
|
10
|
+
|
|
11
|
+
Add-Type -TypeDefinition @'
|
|
12
|
+
using System;
|
|
13
|
+
using System.Text;
|
|
14
|
+
using System.Runtime.InteropServices;
|
|
15
|
+
|
|
16
|
+
public class DSDoomKeys
|
|
17
|
+
{
|
|
18
|
+
[DllImport("user32.dll")] public static extern short GetAsyncKeyState(int vKey);
|
|
19
|
+
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
|
|
20
|
+
[DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
|
|
21
|
+
[DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int GetWindowTextLength(IntPtr hWnd);
|
|
22
|
+
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint pid);
|
|
23
|
+
|
|
24
|
+
public static string Title(IntPtr h)
|
|
25
|
+
{
|
|
26
|
+
int length = GetWindowTextLength(h);
|
|
27
|
+
if (length <= 0) return string.Empty;
|
|
28
|
+
var builder = new StringBuilder(length + 1);
|
|
29
|
+
GetWindowText(h, builder, builder.Capacity);
|
|
30
|
+
return builder.ToString();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public static uint ProcessId(IntPtr h)
|
|
34
|
+
{
|
|
35
|
+
uint processId;
|
|
36
|
+
GetWindowThreadProcessId(h, out processId);
|
|
37
|
+
return processId;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
'@
|
|
41
|
+
|
|
42
|
+
$specialKeys = @{
|
|
43
|
+
'ENTER' = 0x0D
|
|
44
|
+
'SPACE' = 0x20
|
|
45
|
+
'TAB' = 0x09
|
|
46
|
+
'ESC' = 0x1B
|
|
47
|
+
'BACKSPACE' = 0x08
|
|
48
|
+
}
|
|
49
|
+
for ($index = 0; $index -lt 26; $index++) {
|
|
50
|
+
$specialKeys[[string][char](65 + $index)] = 65 + $index
|
|
51
|
+
}
|
|
52
|
+
for ($index = 0; $index -lt 10; $index++) {
|
|
53
|
+
$specialKeys[[string][char](48 + $index)] = 48 + $index
|
|
54
|
+
}
|
|
55
|
+
for ($index = 1; $index -le 12; $index++) {
|
|
56
|
+
$specialKeys["F$index"] = 111 + $index
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
$requested = ([string]$HotkeyKey).Trim().ToUpperInvariant()
|
|
60
|
+
if ($requested.StartsWith('VK_')) { $requested = $requested.Substring(3) }
|
|
61
|
+
if (-not $specialKeys.ContainsKey($requested)) { $requested = 'D' }
|
|
62
|
+
|
|
63
|
+
$watch = @{ 'ENTER' = 0x0D }
|
|
64
|
+
if (-not $watch.ContainsKey($requested)) { $watch[$requested] = $specialKeys[$requested] }
|
|
65
|
+
|
|
66
|
+
function Write-Result([object]$Value) {
|
|
67
|
+
$json = ConvertTo-Json -InputObject $Value -Compress -Depth 4
|
|
68
|
+
[Console]::Out.WriteLine($json)
|
|
69
|
+
[Console]::Out.Flush()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Write-Result @{ type = 'ready'; keys = @($watch.Keys) }
|
|
73
|
+
|
|
74
|
+
$down = @{}
|
|
75
|
+
$iteration = 0
|
|
76
|
+
|
|
77
|
+
while ($true) {
|
|
78
|
+
foreach ($keyName in @($watch.Keys)) {
|
|
79
|
+
$virtualKey = $watch[$keyName]
|
|
80
|
+
$isDown = (([DSDoomKeys]::GetAsyncKeyState($virtualKey)) -band 0x8000) -ne 0
|
|
81
|
+
|
|
82
|
+
if ($isDown -and -not $down.ContainsKey($keyName)) {
|
|
83
|
+
$down[$keyName] = $true
|
|
84
|
+
|
|
85
|
+
$ctrl = (([DSDoomKeys]::GetAsyncKeyState(0x11)) -band 0x8000) -ne 0
|
|
86
|
+
$alt = (([DSDoomKeys]::GetAsyncKeyState(0x12)) -band 0x8000) -ne 0
|
|
87
|
+
$shift = (([DSDoomKeys]::GetAsyncKeyState(0x10)) -band 0x8000) -ne 0
|
|
88
|
+
|
|
89
|
+
$windowHandle = [DSDoomKeys]::GetForegroundWindow()
|
|
90
|
+
$windowTitle = ''
|
|
91
|
+
$processNameValue = ''
|
|
92
|
+
$processIdValue = 0
|
|
93
|
+
|
|
94
|
+
if ($windowHandle -ne [IntPtr]::Zero) {
|
|
95
|
+
$windowTitle = [DSDoomKeys]::Title($windowHandle)
|
|
96
|
+
$processIdValue = [int][DSDoomKeys]::ProcessId($windowHandle)
|
|
97
|
+
if ($processIdValue -ne 0) {
|
|
98
|
+
try {
|
|
99
|
+
$processNameValue = (Get-Process -Id $processIdValue -ErrorAction Stop).ProcessName
|
|
100
|
+
} catch {
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
Write-Result @{
|
|
106
|
+
type = 'key'
|
|
107
|
+
key = $keyName
|
|
108
|
+
ctrl = $ctrl
|
|
109
|
+
alt = $alt
|
|
110
|
+
shift = $shift
|
|
111
|
+
hwnd = $windowHandle.ToInt64().ToString([System.Globalization.CultureInfo]::InvariantCulture)
|
|
112
|
+
title = $windowTitle
|
|
113
|
+
process = $processNameValue
|
|
114
|
+
pid = $processIdValue
|
|
115
|
+
at = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
|
|
116
|
+
}
|
|
117
|
+
} elseif (-not $isDown -and $down.ContainsKey($keyName)) {
|
|
118
|
+
$down.Remove($keyName)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
$iteration++
|
|
123
|
+
if ($ParentPid -gt 0 -and ($iteration % 100 -eq 0)) {
|
|
124
|
+
try {
|
|
125
|
+
$null = Get-Process -Id $ParentPid -ErrorAction Stop
|
|
126
|
+
} catch {
|
|
127
|
+
break
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
[System.Threading.Thread]::Sleep($IntervalMs)
|
|
132
|
+
}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[Parameter(Mandatory = $true)]
|
|
3
|
+
[ValidateSet('get-foreground', 'minimize', 'restore', 'focus', 'find', 'open-url')]
|
|
4
|
+
[string]$Action,
|
|
5
|
+
[string]$Handle = '',
|
|
6
|
+
[string]$Title = '',
|
|
7
|
+
[string]$ProcessName = '',
|
|
8
|
+
[string]$Url = '',
|
|
9
|
+
[ValidateSet('auto', 'edge', 'chrome', 'brave', 'firefox', 'default')]
|
|
10
|
+
[string]$Browser = 'auto'
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
$ErrorActionPreference = 'Stop'
|
|
14
|
+
[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
|
|
15
|
+
$OutputEncoding = New-Object System.Text.UTF8Encoding($false)
|
|
16
|
+
|
|
17
|
+
Add-Type -TypeDefinition @'
|
|
18
|
+
using System;
|
|
19
|
+
using System.Text;
|
|
20
|
+
using System.Collections.Generic;
|
|
21
|
+
using System.Runtime.InteropServices;
|
|
22
|
+
|
|
23
|
+
public class DSDoomWin32
|
|
24
|
+
{
|
|
25
|
+
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
|
26
|
+
|
|
27
|
+
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
|
|
28
|
+
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);
|
|
29
|
+
[DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
30
|
+
[DllImport("user32.dll")] public static extern bool BringWindowToTop(IntPtr hWnd);
|
|
31
|
+
[DllImport("user32.dll")] public static extern bool IsWindow(IntPtr hWnd);
|
|
32
|
+
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
|
|
33
|
+
[DllImport("user32.dll")] public static extern bool IsIconic(IntPtr hWnd);
|
|
34
|
+
[DllImport("user32.dll")] public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
|
|
35
|
+
[DllImport("user32.dll")] public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
|
36
|
+
[DllImport("kernel32.dll")] public static extern uint GetCurrentThreadId();
|
|
37
|
+
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
|
38
|
+
[DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int maxCount);
|
|
39
|
+
[DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int GetWindowTextLength(IntPtr hWnd);
|
|
40
|
+
[DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
|
41
|
+
|
|
42
|
+
public static List<IntPtr> Windows()
|
|
43
|
+
{
|
|
44
|
+
var list = new List<IntPtr>();
|
|
45
|
+
EnumWindows(delegate(IntPtr h, IntPtr l) { list.Add(h); return true; }, IntPtr.Zero);
|
|
46
|
+
return list;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public static string Title(IntPtr h)
|
|
50
|
+
{
|
|
51
|
+
int length = GetWindowTextLength(h);
|
|
52
|
+
if (length <= 0) return string.Empty;
|
|
53
|
+
var builder = new StringBuilder(length + 1);
|
|
54
|
+
GetWindowText(h, builder, builder.Capacity);
|
|
55
|
+
return builder.ToString();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public static uint ProcessId(IntPtr h)
|
|
59
|
+
{
|
|
60
|
+
uint processId;
|
|
61
|
+
GetWindowThreadProcessId(h, out processId);
|
|
62
|
+
return processId;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public static bool Focus(IntPtr h, bool altTap)
|
|
66
|
+
{
|
|
67
|
+
if (h == IntPtr.Zero || !IsWindow(h)) return false;
|
|
68
|
+
if (IsIconic(h)) ShowWindow(h, 9);
|
|
69
|
+
|
|
70
|
+
IntPtr foreground = GetForegroundWindow();
|
|
71
|
+
uint foregroundPid;
|
|
72
|
+
uint foregroundThread = GetWindowThreadProcessId(foreground, out foregroundPid);
|
|
73
|
+
uint currentThread = GetCurrentThreadId();
|
|
74
|
+
bool attached = false;
|
|
75
|
+
if (foregroundThread != 0 && foregroundThread != currentThread)
|
|
76
|
+
{
|
|
77
|
+
attached = AttachThreadInput(currentThread, foregroundThread, true);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (altTap)
|
|
81
|
+
{
|
|
82
|
+
// Windows refuses SetForegroundWindow unless the calling process owns
|
|
83
|
+
// the foreground lock. A synthetic Alt tap releases that lock.
|
|
84
|
+
keybd_event(0x12, 0, 0, UIntPtr.Zero);
|
|
85
|
+
System.Threading.Thread.Sleep(45);
|
|
86
|
+
keybd_event(0x12, 0, 2, UIntPtr.Zero);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
BringWindowToTop(h);
|
|
90
|
+
bool focused = SetForegroundWindow(h);
|
|
91
|
+
|
|
92
|
+
if (attached)
|
|
93
|
+
{
|
|
94
|
+
AttachThreadInput(currentThread, foregroundThread, false);
|
|
95
|
+
}
|
|
96
|
+
return focused;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
'@
|
|
100
|
+
|
|
101
|
+
function Write-Result([object]$Value) {
|
|
102
|
+
$json = ConvertTo-Json -InputObject $Value -Compress -Depth 6
|
|
103
|
+
[Console]::Out.WriteLine($json)
|
|
104
|
+
[Console]::Out.Flush()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function Get-WindowInfo([IntPtr]$WindowHandle) {
|
|
108
|
+
$processIdValue = [DSDoomWin32]::ProcessId($WindowHandle)
|
|
109
|
+
$processNameValue = ''
|
|
110
|
+
if ($processIdValue -ne 0) {
|
|
111
|
+
try {
|
|
112
|
+
$processNameValue = (Get-Process -Id ([int]$processIdValue) -ErrorAction Stop).ProcessName
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
[PSCustomObject]@{
|
|
117
|
+
id = $WindowHandle.ToInt64().ToString([System.Globalization.CultureInfo]::InvariantCulture)
|
|
118
|
+
title = [DSDoomWin32]::Title($WindowHandle)
|
|
119
|
+
processName = $processNameValue
|
|
120
|
+
pid = [int]$processIdValue
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function Test-UsableWindow([IntPtr]$WindowHandle) {
|
|
125
|
+
if ($WindowHandle -eq [IntPtr]::Zero) { return $false }
|
|
126
|
+
if (-not [DSDoomWin32]::IsWindowVisible($WindowHandle)) { return $false }
|
|
127
|
+
return $true
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function Get-MatchingWindows {
|
|
131
|
+
$wantedProcess = $ProcessName.Trim().ToLowerInvariant()
|
|
132
|
+
if ($wantedProcess.EndsWith('.exe')) {
|
|
133
|
+
$wantedProcess = $wantedProcess.Substring(0, $wantedProcess.Length - 4)
|
|
134
|
+
}
|
|
135
|
+
$wantedTitle = $Title.Trim().ToLowerInvariant()
|
|
136
|
+
|
|
137
|
+
$result = New-Object System.Collections.ArrayList
|
|
138
|
+
if ($wantedProcess -eq '' -and $wantedTitle -eq '') {
|
|
139
|
+
return @($result)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
foreach ($windowHandle in [DSDoomWin32]::Windows()) {
|
|
143
|
+
if (-not (Test-UsableWindow $windowHandle)) { continue }
|
|
144
|
+
$info = Get-WindowInfo $windowHandle
|
|
145
|
+
if ($info.pid -eq 0) { continue }
|
|
146
|
+
if ([string]::IsNullOrWhiteSpace($info.title)) { continue }
|
|
147
|
+
if ($wantedProcess -ne '' -and $info.processName.ToLowerInvariant() -ne $wantedProcess) { continue }
|
|
148
|
+
if ($wantedTitle -ne '' -and -not $info.title.ToLowerInvariant().Contains($wantedTitle)) { continue }
|
|
149
|
+
[void]$result.Add($info)
|
|
150
|
+
}
|
|
151
|
+
return @($result)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function Resolve-BrowserExe([string]$Choice) {
|
|
155
|
+
$programFiles = [Environment]::GetFolderPath('ProgramFiles')
|
|
156
|
+
$programFilesX86 = [Environment]::GetFolderPath('ProgramFilesX86')
|
|
157
|
+
$localAppData = [Environment]::GetFolderPath('LocalApplicationData')
|
|
158
|
+
if ([string]::IsNullOrWhiteSpace($programFilesX86)) { $programFilesX86 = ${env:ProgramFiles(x86)} }
|
|
159
|
+
|
|
160
|
+
$candidates = @()
|
|
161
|
+
switch ($Choice) {
|
|
162
|
+
'edge' {
|
|
163
|
+
$candidates = @(
|
|
164
|
+
(Join-Path $programFilesX86 'Microsoft\Edge\Application\msedge.exe'),
|
|
165
|
+
(Join-Path $programFiles 'Microsoft\Edge\Application\msedge.exe')
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
'chrome' {
|
|
169
|
+
$candidates = @(
|
|
170
|
+
(Join-Path $programFiles 'Google\Chrome\Application\chrome.exe'),
|
|
171
|
+
(Join-Path $programFilesX86 'Google\Chrome\Application\chrome.exe'),
|
|
172
|
+
(Join-Path $localAppData 'Google\Chrome\Application\chrome.exe')
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
'brave' {
|
|
176
|
+
$candidates = @(
|
|
177
|
+
(Join-Path $programFiles 'BraveSoftware\Brave-Browser\Application\brave.exe'),
|
|
178
|
+
(Join-Path $programFilesX86 'BraveSoftware\Brave-Browser\Application\brave.exe'),
|
|
179
|
+
(Join-Path $localAppData 'BraveSoftware\Brave-Browser\Application\brave.exe')
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
'firefox' {
|
|
183
|
+
$candidates = @(
|
|
184
|
+
(Join-Path $programFiles 'Mozilla Firefox\firefox.exe'),
|
|
185
|
+
(Join-Path $programFilesX86 'Mozilla Firefox\firefox.exe')
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
foreach ($candidate in $candidates) {
|
|
191
|
+
if ($candidate -and (Test-Path -LiteralPath $candidate)) { return $candidate }
|
|
192
|
+
}
|
|
193
|
+
return $null
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function Get-DefaultBrowserKey {
|
|
197
|
+
try {
|
|
198
|
+
$choice = Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice' -ErrorAction Stop
|
|
199
|
+
$progId = [string]$choice.ProgId
|
|
200
|
+
if ($progId -match 'ChromeHTML') { return 'chrome' }
|
|
201
|
+
if ($progId -match 'MSEdge') { return 'edge' }
|
|
202
|
+
if ($progId -match 'Brave') { return 'brave' }
|
|
203
|
+
if ($progId -match 'Firefox') { return 'firefox' }
|
|
204
|
+
} catch {
|
|
205
|
+
}
|
|
206
|
+
return $null
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function Get-BrowserWindowIds([string[]]$ProcessNames) {
|
|
210
|
+
$ids = @{}
|
|
211
|
+
foreach ($windowHandle in [DSDoomWin32]::Windows()) {
|
|
212
|
+
if (-not (Test-UsableWindow $windowHandle)) { continue }
|
|
213
|
+
$info = Get-WindowInfo $windowHandle
|
|
214
|
+
if ($info.pid -eq 0) { continue }
|
|
215
|
+
if ($ProcessNames -contains $info.processName.ToLowerInvariant()) {
|
|
216
|
+
$ids[$info.id] = $true
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return $ids
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function Invoke-OpenUrl {
|
|
223
|
+
$knownBrowsers = @('msedge', 'chrome', 'brave', 'firefox', 'opera', 'vivaldi', 'arc', 'iexplore')
|
|
224
|
+
$resolvedChoice = $Browser
|
|
225
|
+
if ($resolvedChoice -eq 'auto') {
|
|
226
|
+
$resolvedChoice = Get-DefaultBrowserKey
|
|
227
|
+
if (-not $resolvedChoice) { $resolvedChoice = 'edge' }
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
$exe = $null
|
|
231
|
+
if ($resolvedChoice -ne 'default') {
|
|
232
|
+
$exe = Resolve-BrowserExe $resolvedChoice
|
|
233
|
+
if (-not $exe) {
|
|
234
|
+
$fallback = Resolve-BrowserExe 'edge'
|
|
235
|
+
if (-not $fallback) { $fallback = Resolve-BrowserExe 'chrome' }
|
|
236
|
+
$exe = $fallback
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
$expectedProcess = $null
|
|
241
|
+
if ($exe) {
|
|
242
|
+
$expectedProcess = [System.IO.Path]::GetFileNameWithoutExtension($exe).ToLowerInvariant()
|
|
243
|
+
if (-not $knownBrowsers.Contains($expectedProcess)) {
|
|
244
|
+
$knownBrowsers += $expectedProcess
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
$before = Get-BrowserWindowIds $knownBrowsers
|
|
249
|
+
|
|
250
|
+
if ($exe) {
|
|
251
|
+
if ($expectedProcess -eq 'firefox') {
|
|
252
|
+
Start-Process -FilePath $exe -ArgumentList @('-new-window', $Url) | Out-Null
|
|
253
|
+
} else {
|
|
254
|
+
Start-Process -FilePath $exe -ArgumentList @('--new-window', $Url) | Out-Null
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
Start-Process $Url | Out-Null
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
$deadline = (Get-Date).AddSeconds(8)
|
|
261
|
+
$found = $null
|
|
262
|
+
while ((Get-Date) -lt $deadline) {
|
|
263
|
+
Start-Sleep -Milliseconds 150
|
|
264
|
+
foreach ($windowHandle in [DSDoomWin32]::Windows()) {
|
|
265
|
+
if (-not (Test-UsableWindow $windowHandle)) { continue }
|
|
266
|
+
$info = Get-WindowInfo $windowHandle
|
|
267
|
+
if ($info.pid -eq 0) { continue }
|
|
268
|
+
if ($before.ContainsKey($info.id)) { continue }
|
|
269
|
+
$processNameValue = $info.processName.ToLowerInvariant()
|
|
270
|
+
if ($expectedProcess) {
|
|
271
|
+
if ($processNameValue -ne $expectedProcess) { continue }
|
|
272
|
+
} elseif (-not $knownBrowsers.Contains($processNameValue)) {
|
|
273
|
+
continue
|
|
274
|
+
}
|
|
275
|
+
if ([string]::IsNullOrWhiteSpace($info.title)) { continue }
|
|
276
|
+
$found = $info
|
|
277
|
+
break
|
|
278
|
+
}
|
|
279
|
+
if ($found) { break }
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return $found
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
switch ($Action) {
|
|
287
|
+
'get-foreground' {
|
|
288
|
+
$windowHandle = [DSDoomWin32]::GetForegroundWindow()
|
|
289
|
+
if (-not (Test-UsableWindow $windowHandle)) {
|
|
290
|
+
Write-Result @{ ok = $false; error = 'no foreground window' }
|
|
291
|
+
} else {
|
|
292
|
+
Write-Result @{ ok = $true; window = (Get-WindowInfo $windowHandle) }
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
'find' {
|
|
296
|
+
Write-Result @{ ok = $true; windows = @(Get-MatchingWindows) }
|
|
297
|
+
}
|
|
298
|
+
'minimize' {
|
|
299
|
+
$windowHandle = [IntPtr]([Int64]$Handle)
|
|
300
|
+
if (-not [DSDoomWin32]::IsWindow($windowHandle)) {
|
|
301
|
+
Write-Result @{ ok = $false; error = 'invalid window handle' }
|
|
302
|
+
} else {
|
|
303
|
+
[void][DSDoomWin32]::ShowWindow($windowHandle, 6)
|
|
304
|
+
Write-Result @{ ok = $true }
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
'restore' {
|
|
308
|
+
$windowHandle = [IntPtr]([Int64]$Handle)
|
|
309
|
+
if (-not [DSDoomWin32]::IsWindow($windowHandle)) {
|
|
310
|
+
Write-Result @{ ok = $false; error = 'invalid window handle' }
|
|
311
|
+
} else {
|
|
312
|
+
[void][DSDoomWin32]::ShowWindow($windowHandle, 9)
|
|
313
|
+
Write-Result @{ ok = $true }
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
'focus' {
|
|
317
|
+
$windowHandle = [IntPtr]([Int64]$Handle)
|
|
318
|
+
if (-not [DSDoomWin32]::IsWindow($windowHandle)) {
|
|
319
|
+
Write-Result @{ ok = $false; error = 'invalid window handle' }
|
|
320
|
+
} else {
|
|
321
|
+
$focused = [DSDoomWin32]::Focus($windowHandle, $true)
|
|
322
|
+
if (-not $focused) {
|
|
323
|
+
Start-Sleep -Milliseconds 120
|
|
324
|
+
$focused = [DSDoomWin32]::Focus($windowHandle, $true)
|
|
325
|
+
}
|
|
326
|
+
Write-Result @{ ok = $true; focused = $focused }
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
'open-url' {
|
|
330
|
+
$windowInfo = Invoke-OpenUrl
|
|
331
|
+
if ($windowInfo) {
|
|
332
|
+
Write-Result @{ ok = $true; window = $windowInfo }
|
|
333
|
+
} else {
|
|
334
|
+
Write-Result @{ ok = $false; error = 'browser window was not detected' }
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
default {
|
|
338
|
+
Write-Result @{ ok = $false; error = "unknown action: $Action" }
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
} catch {
|
|
342
|
+
Write-Result @{ ok = $false; error = $_.Exception.Message }
|
|
343
|
+
exit 0
|
|
344
|
+
}
|