@bobfrankston/winpos 2.0.20

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/windows.ts ADDED
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Window enumeration and filtering
3
+ */
4
+
5
+ import { user32, RECT, isBun, SW_RESTORE, SW_MINIMIZE, SW_MAXIMIZE, SW_SHOWNORMAL } from './ffi-wrapper.js';
6
+ import { readFileSync } from 'fs';
7
+ import { join } from 'path';
8
+
9
+ export { SW_RESTORE, SW_MINIMIZE, SW_MAXIMIZE, SW_SHOWNORMAL, SW_HIDE, SW_SHOW } from './ffi-wrapper.js';
10
+
11
+ export interface WindowInfo {
12
+ hwnd: bigint;
13
+ title: string;
14
+ rect: RECT;
15
+ }
16
+
17
+ let ignores: string[] = [];
18
+ let ignoreStarts: string[] = [];
19
+
20
+ export function loadIgnorePatterns(filePath: string) {
21
+ try {
22
+ const lines = readFileSync(filePath, 'utf-8').split('\n');
23
+ for (const line of lines) {
24
+ const trimmed = line.trim();
25
+ if (!trimmed || trimmed.startsWith('#'))
26
+ continue;
27
+ if (trimmed.endsWith('*')) {
28
+ ignoreStarts.push(trimmed.slice(0, -1));
29
+ } else {
30
+ ignores.push(trimmed);
31
+ }
32
+ }
33
+ } catch (error: any) {
34
+ // Ignore file doesn't exist - that's OK
35
+ }
36
+ }
37
+
38
+ function shouldIgnoreWindow(title: string): boolean {
39
+ const lowerTitle = title.toLowerCase();
40
+ return ignores.some(ign => lowerTitle === ign.toLowerCase()) ||
41
+ ignoreStarts.some(start => lowerTitle.startsWith(start.toLowerCase()));
42
+ }
43
+
44
+ export function enumerateWindows(includeIgnored: boolean = false): WindowInfo[] {
45
+ const windows: WindowInfo[] = [];
46
+
47
+ if (isBun) {
48
+ // Bun implementation
49
+ const { ptr } = (globalThis as any).Bun;
50
+
51
+ const callback = (hwnd: bigint, lParam: bigint) => {
52
+ const textBuf = new Uint16Array(256);
53
+ const textPtr = ptr(textBuf);
54
+ const length = user32.GetWindowTextW(hwnd, textPtr, 256);
55
+
56
+ if (length > 0) {
57
+ const title = String.fromCharCode(...textBuf.slice(0, length));
58
+
59
+ if (includeIgnored || !shouldIgnoreWindow(title)) {
60
+ const rect: RECT = { Left: 0, Top: 0, Right: 0, Bottom: 0 };
61
+ const rectBuf = new Int32Array(4);
62
+ const rectPtr = ptr(rectBuf);
63
+
64
+ if (user32.GetWindowRect(hwnd, rectPtr)) {
65
+ rect.Left = rectBuf[0];
66
+ rect.Top = rectBuf[1];
67
+ rect.Right = rectBuf[2];
68
+ rect.Bottom = rectBuf[3];
69
+
70
+ if (rect.Top !== rect.Bottom && rect.Left !== rect.Right) {
71
+ windows.push({ hwnd, title, rect });
72
+ }
73
+ }
74
+ }
75
+ }
76
+ return true;
77
+ };
78
+
79
+ user32.EnumWindows(callback, 0n);
80
+ } else {
81
+ // Node.js with koffi
82
+ const callback = (hwnd: bigint, lParam: bigint) => {
83
+ const textBuf = Buffer.alloc(512);
84
+ const length = user32.GetWindowTextW(hwnd, textBuf as any, 256);
85
+
86
+ if (length > 0) {
87
+ const title = textBuf.toString('utf16le', 0, length * 2);
88
+
89
+ if (includeIgnored || !shouldIgnoreWindow(title)) {
90
+ const rect: RECT = { Left: 0, Top: 0, Right: 0, Bottom: 0 };
91
+
92
+ if (user32.GetWindowRect(hwnd, rect)) {
93
+ if (rect.Top !== rect.Bottom && rect.Left !== rect.Right) {
94
+ windows.push({ hwnd, title, rect });
95
+ }
96
+ }
97
+ }
98
+ }
99
+ return true;
100
+ };
101
+
102
+ user32.EnumWindows(callback, 0n);
103
+ }
104
+
105
+ return windows;
106
+ }
107
+
108
+ export function findWindowsByPattern(pattern: string): WindowInfo[] {
109
+ const allWindows = enumerateWindows();
110
+
111
+ if (pattern.startsWith('/') && pattern.endsWith('/')) {
112
+ // Regex match
113
+ const regex = new RegExp(pattern.slice(1, -1), 'i');
114
+ return allWindows.filter(w => regex.test(w.title));
115
+ } else if (pattern.endsWith('*')) {
116
+ // Prefix match
117
+ const prefix = pattern.slice(0, -1).toLowerCase();
118
+ return allWindows.filter(w => w.title.toLowerCase().startsWith(prefix));
119
+ } else {
120
+ // Exact match
121
+ return allWindows.filter(w => w.title.toLowerCase() === pattern.toLowerCase());
122
+ }
123
+ }
124
+
125
+ // Window state management API
126
+
127
+ export enum WindowState {
128
+ Normal = 'normal',
129
+ Minimized = 'minimized',
130
+ Maximized = 'maximized'
131
+ }
132
+
133
+ /**
134
+ * Get the current state of a window
135
+ */
136
+ export function getWindowState(hwnd: bigint): WindowState {
137
+ if (user32.IsIconic(hwnd))
138
+ return WindowState.Minimized;
139
+ if (user32.IsZoomed(hwnd))
140
+ return WindowState.Maximized;
141
+ return WindowState.Normal;
142
+ }
143
+
144
+ /**
145
+ * Set the state of a window
146
+ */
147
+ export function setWindowState(hwnd: bigint, state: WindowState): boolean {
148
+ switch (state) {
149
+ case WindowState.Minimized:
150
+ return user32.ShowWindow(hwnd, SW_MINIMIZE);
151
+ case WindowState.Maximized:
152
+ return user32.ShowWindow(hwnd, SW_MAXIMIZE);
153
+ case WindowState.Normal:
154
+ return user32.ShowWindow(hwnd, SW_RESTORE);
155
+ default:
156
+ return false;
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Check if a window is minimized
162
+ */
163
+ export function isMinimized(hwnd: bigint): boolean {
164
+ return user32.IsIconic(hwnd);
165
+ }
166
+
167
+ /**
168
+ * Check if a window is maximized
169
+ */
170
+ export function isMaximized(hwnd: bigint): boolean {
171
+ return user32.IsZoomed(hwnd);
172
+ }
173
+
174
+ /**
175
+ * Check if a window is in normal state
176
+ */
177
+ export function isNormal(hwnd: bigint): boolean {
178
+ return !user32.IsIconic(hwnd) && !user32.IsZoomed(hwnd);
179
+ }
180
+
181
+ /**
182
+ * Minimize a window
183
+ */
184
+ export function minimizeWindow(hwnd: bigint): boolean {
185
+ return user32.ShowWindow(hwnd, SW_MINIMIZE);
186
+ }
187
+
188
+ /**
189
+ * Maximize a window
190
+ */
191
+ export function maximizeWindow(hwnd: bigint): boolean {
192
+ return user32.ShowWindow(hwnd, SW_MAXIMIZE);
193
+ }
194
+
195
+ /**
196
+ * Restore a window to normal state
197
+ */
198
+ export function restoreWindow(hwnd: bigint): boolean {
199
+ return user32.ShowWindow(hwnd, SW_RESTORE);
200
+ }