@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/.editorconfig +20 -0
- package/.gitattributes +16 -0
- package/DEVELOPMENT.md +148 -0
- package/README.md +126 -0
- package/ffi-wrapper.d.ts +45 -0
- package/ffi-wrapper.d.ts.map +1 -0
- package/ffi-wrapper.js +172 -0
- package/ffi-wrapper.js.map +1 -0
- package/ffi-wrapper.ts +213 -0
- package/ignores.txt +75 -0
- package/index.d.ts +12 -0
- package/index.d.ts.map +1 -0
- package/index.js +213 -0
- package/index.js.map +1 -0
- package/index.ts +294 -0
- package/package.json +41 -0
- package/screens.d.ts +18 -0
- package/screens.d.ts.map +1 -0
- package/screens.js +83 -0
- package/screens.js.map +1 -0
- package/screens.ts +101 -0
- package/tabs.d.ts +42 -0
- package/tabs.d.ts.map +1 -0
- package/tabs.js +115 -0
- package/tabs.js.map +1 -0
- package/tabs.ts +133 -0
- package/tsconfig.json +28 -0
- package/windows.d.ts +51 -0
- package/windows.d.ts.map +1 -0
- package/windows.js +170 -0
- package/windows.js.map +1 -0
- package/windows.ts +200 -0
package/.editorconfig
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# EditorConfig is awesome: https://EditorConfig.org
|
|
2
|
+
|
|
3
|
+
# top-most EditorConfig file
|
|
4
|
+
root = true
|
|
5
|
+
|
|
6
|
+
# Unix-style newlines with a newline ending every file
|
|
7
|
+
[*]
|
|
8
|
+
end_of_line = lf
|
|
9
|
+
insert_final_newline = true
|
|
10
|
+
charset = utf-8
|
|
11
|
+
trim_trailing_whitespace = true
|
|
12
|
+
|
|
13
|
+
# TypeScript/JavaScript files
|
|
14
|
+
[*.{ts,js,json}]
|
|
15
|
+
indent_style = space
|
|
16
|
+
indent_size = 4
|
|
17
|
+
|
|
18
|
+
# Markdown files
|
|
19
|
+
[*.md]
|
|
20
|
+
trim_trailing_whitespace = false
|
package/.gitattributes
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Set default behavior to automatically normalize line endings to LF
|
|
2
|
+
* text=auto eol=lf
|
|
3
|
+
|
|
4
|
+
# Explicitly declare text files you want to always be normalized and converted to LF
|
|
5
|
+
*.ts text eol=lf
|
|
6
|
+
*.js text eol=lf
|
|
7
|
+
*.json text eol=lf
|
|
8
|
+
*.md text eol=lf
|
|
9
|
+
*.txt text eol=lf
|
|
10
|
+
|
|
11
|
+
# Denote all files that are truly binary and should not be modified
|
|
12
|
+
*.png binary
|
|
13
|
+
*.jpg binary
|
|
14
|
+
*.ico binary
|
|
15
|
+
*.exe binary
|
|
16
|
+
*.dll binary
|
package/DEVELOPMENT.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Development Notes for tswinpos
|
|
2
|
+
|
|
3
|
+
## VSCode Setup
|
|
4
|
+
|
|
5
|
+
### Running/Debugging
|
|
6
|
+
- Use VSCode debugger (F5) instead of terminal commands
|
|
7
|
+
- Breakpoints in .ts files work correctly with source maps
|
|
8
|
+
- TypeScript compiles automatically with `tsc -w`
|
|
9
|
+
|
|
10
|
+
### Quick Commands
|
|
11
|
+
```bash
|
|
12
|
+
# Watch mode (keep running in terminal)
|
|
13
|
+
npm run watch
|
|
14
|
+
|
|
15
|
+
# Test CLI
|
|
16
|
+
node index.js "*" # List all windows
|
|
17
|
+
node index.js "Chrome*" 100 100 0 # Move Chrome to position
|
|
18
|
+
|
|
19
|
+
# Debug mode
|
|
20
|
+
node index.js -d "*"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Project Structure
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
tswinpos/
|
|
27
|
+
├── index.ts # Main entry + CLI + exported API
|
|
28
|
+
├── ffi-wrapper.ts # Windows API FFI (Node/Bun runtime detection)
|
|
29
|
+
├── screens.ts # Screen enumeration
|
|
30
|
+
├── windows.ts # Window enumeration + state management
|
|
31
|
+
├── tabs.ts # Tab enumeration (placeholder - COM interop needed)
|
|
32
|
+
├── ignores.txt # Window title filters
|
|
33
|
+
├── package.json # @bobfrankston/tswinpos
|
|
34
|
+
└── tsconfig.json # Standard config
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Key Implementation Details
|
|
38
|
+
|
|
39
|
+
### FFI Pattern (following sqlunified)
|
|
40
|
+
```typescript
|
|
41
|
+
// 1. Load library
|
|
42
|
+
const koffi = (await import('koffi')).default;
|
|
43
|
+
const lib = koffi.load('user32.dll');
|
|
44
|
+
|
|
45
|
+
// 2. Define functions
|
|
46
|
+
const GetWindowRect = lib.func('bool __stdcall GetWindowRect(void *hWnd, RECT *lpRect)');
|
|
47
|
+
|
|
48
|
+
// 3. Use Buffer for structures (simpler than koffi.alloc/decode)
|
|
49
|
+
const rectBuf = Buffer.alloc(16); // 4 ints
|
|
50
|
+
GetWindowRect(hwnd, rectBuf);
|
|
51
|
+
rect.Left = rectBuf.readInt32LE(0);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Differences from sqlunified
|
|
55
|
+
- **sqlunified**: Uses `koffi.alloc()` and `koffi.decode()` for memory management
|
|
56
|
+
- **tswinpos**: Uses Node.js Buffer directly for simpler struct handling
|
|
57
|
+
- **Both**: Use same `.func()` and `koffi.register()` patterns
|
|
58
|
+
|
|
59
|
+
### Runtime Detection
|
|
60
|
+
```typescript
|
|
61
|
+
const isBun = typeof Bun !== 'undefined';
|
|
62
|
+
|
|
63
|
+
if (isBun) {
|
|
64
|
+
// Use bun:ffi
|
|
65
|
+
} else {
|
|
66
|
+
// Use koffi
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### CLI + Library Pattern
|
|
71
|
+
```typescript
|
|
72
|
+
// Export API for library use
|
|
73
|
+
export { enumerateWindows, findWindowsByPattern, ... };
|
|
74
|
+
|
|
75
|
+
// CLI entry point
|
|
76
|
+
if (import.meta.main) {
|
|
77
|
+
run(process.argv.slice(2));
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Common Tasks
|
|
82
|
+
|
|
83
|
+
### Adding New Windows API Functions
|
|
84
|
+
1. Add to FFI wrapper interface in `ffi-wrapper.ts`
|
|
85
|
+
2. Implement for both Bun and Node.js paths
|
|
86
|
+
3. Export from appropriate module
|
|
87
|
+
4. Re-export from `index.ts` if public API
|
|
88
|
+
|
|
89
|
+
### Testing
|
|
90
|
+
```bash
|
|
91
|
+
# List windows (test enumeration)
|
|
92
|
+
node index.js "*"
|
|
93
|
+
|
|
94
|
+
# Test positioning
|
|
95
|
+
node index.js "Notepad" 0 0 0
|
|
96
|
+
|
|
97
|
+
# Test with debug
|
|
98
|
+
node index.js -d "Chrome*" 100 100 1
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Shell Gotcha
|
|
102
|
+
⚠️ **IMPORTANT**: Quote the asterisk!
|
|
103
|
+
```bash
|
|
104
|
+
node index.js "*" # ✅ Correct - lists windows
|
|
105
|
+
node index.js * # ❌ Wrong - shell expands to all files
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Known Limitations
|
|
109
|
+
|
|
110
|
+
### Tab Enumeration
|
|
111
|
+
- Windows has no standard tab enumeration API
|
|
112
|
+
- Would require COM interop with UI Automation
|
|
113
|
+
- Placeholder API in `tabs.ts` with documentation
|
|
114
|
+
- See `TAB_ENUMERATION_NOTE` for details
|
|
115
|
+
|
|
116
|
+
### Screen Enumeration
|
|
117
|
+
- Currently simplified (single screen)
|
|
118
|
+
- Full multi-monitor via EnumDisplayMonitors would need:
|
|
119
|
+
- Complex koffi struct handling
|
|
120
|
+
- MONITORINFOEXW structure
|
|
121
|
+
- Callback registration
|
|
122
|
+
|
|
123
|
+
## Future Enhancements
|
|
124
|
+
|
|
125
|
+
1. **Full Multi-Monitor Support**
|
|
126
|
+
- Implement proper EnumDisplayMonitors
|
|
127
|
+
- Parse MONITORINFOEXW structure
|
|
128
|
+
|
|
129
|
+
2. **Tab Enumeration**
|
|
130
|
+
- Requires UI Automation COM interop
|
|
131
|
+
- Consider PowerShell bridge alternative
|
|
132
|
+
|
|
133
|
+
3. **Window Classes**
|
|
134
|
+
- Add GetClassName API
|
|
135
|
+
- Filter by window class
|
|
136
|
+
|
|
137
|
+
4. **Window Properties**
|
|
138
|
+
- Process ID (GetWindowThreadProcessId)
|
|
139
|
+
- Window styles (GetWindowLong)
|
|
140
|
+
- Z-order information
|
|
141
|
+
|
|
142
|
+
## Notes for Future Claude Code Sessions
|
|
143
|
+
|
|
144
|
+
- All .ts/.js/.d.ts/.map files are co-located (no dist folder)
|
|
145
|
+
- Use `tsc -w` for continuous compilation
|
|
146
|
+
- Test with `node index.js` directly (source maps work)
|
|
147
|
+
- Follow patterns from sqlunified for koffi usage
|
|
148
|
+
- Remember: Buffer.readInt32LE() for struct parsing
|
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# @bobfrankston/tswinpos
|
|
2
|
+
|
|
3
|
+
TypeScript implementation of WinPos - A Windows window positioning utility for multi-monitor setups.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Cross-Runtime**: Works with both Node.js (24+) and Bun
|
|
8
|
+
- **FFI-based**: Direct Windows API calls via `koffi` (Node.js) or `bun:ffi` (Bun)
|
|
9
|
+
- **Multi-Monitor**: Full support for multiple displays
|
|
10
|
+
- **Flexible Matching**: Find windows by exact title, prefix, or regex
|
|
11
|
+
- **Window State Management**: Minimize, maximize, restore windows programmatically
|
|
12
|
+
- **Auto-Restore**: Windows are automatically restored from minimized/maximized state when positioning
|
|
13
|
+
- **Library or CLI**: Use as a command-line tool or import as a library
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @bobfrankston/tswinpos
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or with Bun:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bun add @bobfrankston/tswinpos
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
### Command Line
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Move window to screen 0 at position (100, 200)
|
|
33
|
+
tswinpos "Notepad" 100 200 0
|
|
34
|
+
|
|
35
|
+
# Move window using percentages
|
|
36
|
+
tswinpos "Chrome*" 50% 50% 1
|
|
37
|
+
|
|
38
|
+
# Set window size as well
|
|
39
|
+
tswinpos "Visual Studio Code" 0 0 0 1920 1080
|
|
40
|
+
|
|
41
|
+
# List all windows
|
|
42
|
+
tswinpos *
|
|
43
|
+
|
|
44
|
+
# Get screen count
|
|
45
|
+
tswinpos -c
|
|
46
|
+
|
|
47
|
+
# Debug mode
|
|
48
|
+
tswinpos -d "Firefox" 0 0 1
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Pattern Matching
|
|
52
|
+
|
|
53
|
+
- **Exact match**: `tswinpos "Notepad" 0 0 0`
|
|
54
|
+
- **Prefix match**: `tswinpos "Chrome*" 0 0 0`
|
|
55
|
+
- **Regex match**: `tswinpos "/Visual.*Code/" 0 0 0`
|
|
56
|
+
|
|
57
|
+
### As a Library
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { run } from '@bobfrankston/tswinpos';
|
|
61
|
+
|
|
62
|
+
// Move a window programmatically
|
|
63
|
+
run(['Chrome*', '0', '0', '1', '1920', '1080']);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Screen Numbering
|
|
67
|
+
|
|
68
|
+
Screens are numbered starting from 0, sorted by position:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
+---+---+
|
|
72
|
+
| 2 | 3 |
|
|
73
|
+
+---+---+
|
|
74
|
+
| 0 | 1 |
|
|
75
|
+
+---+---+
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- 0: Lower right (primary)
|
|
79
|
+
- 1: Lower left
|
|
80
|
+
- 2: Upper left
|
|
81
|
+
- 3: Upper right
|
|
82
|
+
|
|
83
|
+
## Options
|
|
84
|
+
|
|
85
|
+
- `-d` - Debug mode (shows detailed information)
|
|
86
|
+
- `-c` - Return screen count
|
|
87
|
+
- `*` - List all windows
|
|
88
|
+
|
|
89
|
+
## Parameters
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
tswinpos [-d -c *] <window title> <x> <y> <screen> [<width> <height>]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- `<window title>` - Window title (exact, prefix with `*`, or regex with `/.../ `)
|
|
96
|
+
- `<x>` - X position (pixels or percentage like `50%`)
|
|
97
|
+
- `<y>` - Y position (pixels or percentage)
|
|
98
|
+
- `<screen>` - Screen index (0 to N-1)
|
|
99
|
+
- `<width>` - Optional width (pixels, percentage, or 0 to keep current)
|
|
100
|
+
- `<height>` - Optional height (pixels, percentage, or 0 to keep current)
|
|
101
|
+
|
|
102
|
+
## Requirements
|
|
103
|
+
|
|
104
|
+
- Windows OS
|
|
105
|
+
- Node.js 24+ or Bun 1.0+
|
|
106
|
+
|
|
107
|
+
## Development
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Install dependencies
|
|
111
|
+
npm install
|
|
112
|
+
|
|
113
|
+
# Build
|
|
114
|
+
npm run build
|
|
115
|
+
|
|
116
|
+
# Watch mode
|
|
117
|
+
npm run watch
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT
|
|
123
|
+
|
|
124
|
+
## Author
|
|
125
|
+
|
|
126
|
+
Bob Frankston
|
package/ffi-wrapper.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FFI Wrapper - Runtime-agnostic FFI for Node.js and Bun
|
|
3
|
+
* Provides a unified interface for calling Windows API functions
|
|
4
|
+
*/
|
|
5
|
+
declare const isBun: boolean;
|
|
6
|
+
export interface RECT {
|
|
7
|
+
Left: number;
|
|
8
|
+
Top: number;
|
|
9
|
+
Right: number;
|
|
10
|
+
Bottom: number;
|
|
11
|
+
}
|
|
12
|
+
export declare const SW_HIDE = 0;
|
|
13
|
+
export declare const SW_SHOWNORMAL = 1;
|
|
14
|
+
export declare const SW_SHOWMINIMIZED = 2;
|
|
15
|
+
export declare const SW_SHOWMAXIMIZED = 3;
|
|
16
|
+
export declare const SW_MAXIMIZE = 3;
|
|
17
|
+
export declare const SW_SHOWNOACTIVATE = 4;
|
|
18
|
+
export declare const SW_SHOW = 5;
|
|
19
|
+
export declare const SW_MINIMIZE = 6;
|
|
20
|
+
export declare const SW_SHOWMINNOACTIVE = 7;
|
|
21
|
+
export declare const SW_SHOWNA = 8;
|
|
22
|
+
export declare const SW_RESTORE = 9;
|
|
23
|
+
export interface MONITORINFO {
|
|
24
|
+
cbSize: number;
|
|
25
|
+
rcMonitor: RECT;
|
|
26
|
+
rcWork: RECT;
|
|
27
|
+
dwFlags: number;
|
|
28
|
+
}
|
|
29
|
+
export declare const MONITORINFOF_PRIMARY = 1;
|
|
30
|
+
export interface WindowsAPI {
|
|
31
|
+
EnumWindows: (callback: (hwnd: bigint, lParam: bigint) => boolean, lParam: bigint) => boolean;
|
|
32
|
+
GetWindowTextW: (hwnd: bigint, text: any, maxCount: number) => number;
|
|
33
|
+
GetWindowRect: (hwnd: bigint, rect: any) => boolean;
|
|
34
|
+
MoveWindow: (hwnd: bigint, x: number, y: number, width: number, height: number, repaint: boolean) => boolean;
|
|
35
|
+
SetForegroundWindow: (hwnd: bigint) => boolean;
|
|
36
|
+
ShowWindow: (hwnd: bigint, nCmdShow: number) => boolean;
|
|
37
|
+
IsZoomed: (hwnd: bigint) => boolean;
|
|
38
|
+
IsIconic: (hwnd: bigint) => boolean;
|
|
39
|
+
GetWindowPlacement: (hwnd: bigint, placement: any) => boolean;
|
|
40
|
+
EnumDisplayMonitors: (callback: (hMonitor: bigint, hdcMonitor: bigint, lprcMonitor: any, dwData: bigint) => boolean, dwData: bigint) => boolean;
|
|
41
|
+
GetMonitorInfoW: (hMonitor: bigint, lpmi: any) => boolean;
|
|
42
|
+
}
|
|
43
|
+
declare const user32: WindowsAPI;
|
|
44
|
+
export { user32, isBun };
|
|
45
|
+
//# sourceMappingURL=ffi-wrapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ffi-wrapper.d.ts","sourceRoot":"","sources":["ffi-wrapper.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,QAAA,MAAM,KAAK,SAA6B,CAAC;AAGzC,MAAM,WAAW,IAAI;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAClB;AAGD,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,iBAAiB,IAAI,CAAC;AACnC,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,UAAU,IAAI,CAAC;AAE5B,MAAM,WAAW,WAAW;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,IAAI,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,MAAM,WAAW,UAAU;IACvB,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9F,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IACtE,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC;IACpD,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;IAC7G,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC/C,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IACxD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,OAAO,CAAC;IAC9D,mBAAmB,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAChJ,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC;CAC7D;AAgKD,QAAA,MAAM,MAAM,YAAwB,CAAC;AAErC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC"}
|
package/ffi-wrapper.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FFI Wrapper - Runtime-agnostic FFI for Node.js and Bun
|
|
3
|
+
* Provides a unified interface for calling Windows API functions
|
|
4
|
+
*/
|
|
5
|
+
// @ts-ignore - Bun global may not exist
|
|
6
|
+
const isBun = typeof Bun !== 'undefined';
|
|
7
|
+
// Window state constants
|
|
8
|
+
export const SW_HIDE = 0;
|
|
9
|
+
export const SW_SHOWNORMAL = 1;
|
|
10
|
+
export const SW_SHOWMINIMIZED = 2;
|
|
11
|
+
export const SW_SHOWMAXIMIZED = 3;
|
|
12
|
+
export const SW_MAXIMIZE = 3;
|
|
13
|
+
export const SW_SHOWNOACTIVATE = 4;
|
|
14
|
+
export const SW_SHOW = 5;
|
|
15
|
+
export const SW_MINIMIZE = 6;
|
|
16
|
+
export const SW_SHOWMINNOACTIVE = 7;
|
|
17
|
+
export const SW_SHOWNA = 8;
|
|
18
|
+
export const SW_RESTORE = 9;
|
|
19
|
+
export const MONITORINFOF_PRIMARY = 1;
|
|
20
|
+
async function initializeFFI() {
|
|
21
|
+
if (isBun) {
|
|
22
|
+
// @ts-ignore - bun:ffi is only available in Bun
|
|
23
|
+
const { dlopen, FFIType } = await import('bun:ffi');
|
|
24
|
+
const { symbols } = dlopen('user32.dll', {
|
|
25
|
+
EnumWindows: {
|
|
26
|
+
args: [FFIType.ptr, FFIType.u64],
|
|
27
|
+
returns: FFIType.bool,
|
|
28
|
+
},
|
|
29
|
+
GetWindowTextW: {
|
|
30
|
+
args: [FFIType.ptr, FFIType.ptr, FFIType.i32],
|
|
31
|
+
returns: FFIType.i32,
|
|
32
|
+
},
|
|
33
|
+
GetWindowRect: {
|
|
34
|
+
args: [FFIType.ptr, FFIType.ptr],
|
|
35
|
+
returns: FFIType.bool,
|
|
36
|
+
},
|
|
37
|
+
MoveWindow: {
|
|
38
|
+
args: [FFIType.ptr, FFIType.i32, FFIType.i32, FFIType.i32, FFIType.i32, FFIType.bool],
|
|
39
|
+
returns: FFIType.bool,
|
|
40
|
+
},
|
|
41
|
+
SetForegroundWindow: {
|
|
42
|
+
args: [FFIType.ptr],
|
|
43
|
+
returns: FFIType.bool,
|
|
44
|
+
},
|
|
45
|
+
ShowWindow: {
|
|
46
|
+
args: [FFIType.ptr, FFIType.i32],
|
|
47
|
+
returns: FFIType.bool,
|
|
48
|
+
},
|
|
49
|
+
IsZoomed: {
|
|
50
|
+
args: [FFIType.ptr],
|
|
51
|
+
returns: FFIType.bool,
|
|
52
|
+
},
|
|
53
|
+
IsIconic: {
|
|
54
|
+
args: [FFIType.ptr],
|
|
55
|
+
returns: FFIType.bool,
|
|
56
|
+
},
|
|
57
|
+
GetWindowPlacement: {
|
|
58
|
+
args: [FFIType.ptr, FFIType.ptr],
|
|
59
|
+
returns: FFIType.bool,
|
|
60
|
+
},
|
|
61
|
+
EnumDisplayMonitors: {
|
|
62
|
+
args: [FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.u64],
|
|
63
|
+
returns: FFIType.bool,
|
|
64
|
+
},
|
|
65
|
+
GetMonitorInfoW: {
|
|
66
|
+
args: [FFIType.ptr, FFIType.ptr],
|
|
67
|
+
returns: FFIType.bool,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
return symbols;
|
|
71
|
+
}
|
|
72
|
+
// Node.js with koffi (following sqlunified pattern)
|
|
73
|
+
const koffi = (await import('koffi')).default;
|
|
74
|
+
const lib = koffi.load('user32.dll');
|
|
75
|
+
// Define callback type for EnumWindows
|
|
76
|
+
const EnumWindowsProc = koffi.proto('bool __stdcall EnumWindowsProc(void *hwnd, void *lParam)');
|
|
77
|
+
// Define RECT structure
|
|
78
|
+
const RECT_Struct = koffi.struct('RECT', {
|
|
79
|
+
Left: 'int',
|
|
80
|
+
Top: 'int',
|
|
81
|
+
Right: 'int',
|
|
82
|
+
Bottom: 'int',
|
|
83
|
+
});
|
|
84
|
+
// Define all user32 functions
|
|
85
|
+
const EnumWindows = lib.func('bool __stdcall EnumWindows(EnumWindowsProc *enumProc, void *lParam)');
|
|
86
|
+
const GetWindowTextW = lib.func('int __stdcall GetWindowTextW(void *hWnd, uint16_t *lpString, int nMaxCount)');
|
|
87
|
+
const GetWindowRect = lib.func('bool __stdcall GetWindowRect(void *hWnd, RECT *lpRect)');
|
|
88
|
+
const MoveWindow = lib.func('bool __stdcall MoveWindow(void *hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint)');
|
|
89
|
+
const SetForegroundWindow = lib.func('bool __stdcall SetForegroundWindow(void *hWnd)');
|
|
90
|
+
const ShowWindow = lib.func('bool __stdcall ShowWindow(void *hWnd, int nCmdShow)');
|
|
91
|
+
const IsZoomed = lib.func('bool __stdcall IsZoomed(void *hWnd)');
|
|
92
|
+
const IsIconic = lib.func('bool __stdcall IsIconic(void *hWnd)');
|
|
93
|
+
const GetWindowPlacement = lib.func('bool __stdcall GetWindowPlacement(void *hWnd, void *lpwndpl)');
|
|
94
|
+
// Monitor enumeration functions
|
|
95
|
+
const MonitorEnumProc = koffi.proto('bool __stdcall MonitorEnumProc(void *hMonitor, void *hdcMonitor, RECT *lprcMonitor, void *dwData)');
|
|
96
|
+
const EnumDisplayMonitors = lib.func('bool __stdcall EnumDisplayMonitors(void *hdc, RECT *lprcClip, MonitorEnumProc *lpfnEnum, void *dwData)');
|
|
97
|
+
const GetMonitorInfoW = lib.func('bool __stdcall GetMonitorInfoW(void *hMonitor, void *lpmi)');
|
|
98
|
+
// Keep references to registered callbacks while native calls are in progress
|
|
99
|
+
let __enumWindowsCbRef = null;
|
|
100
|
+
let __enumDisplayMonitorsCbRef = null;
|
|
101
|
+
return {
|
|
102
|
+
EnumWindows: (callback, lParam) => {
|
|
103
|
+
__enumWindowsCbRef = koffi.register(callback, koffi.pointer(EnumWindowsProc));
|
|
104
|
+
try {
|
|
105
|
+
return EnumWindows(__enumWindowsCbRef, lParam);
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
// release reference so GC can collect after native call finishes
|
|
109
|
+
__enumWindowsCbRef = null;
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
GetWindowTextW,
|
|
113
|
+
GetWindowRect: (hwnd, rect) => {
|
|
114
|
+
// Use Buffer for RECT structure (4 ints = 16 bytes)
|
|
115
|
+
const rectBuf = Buffer.alloc(16);
|
|
116
|
+
const result = GetWindowRect(hwnd, rectBuf);
|
|
117
|
+
if (result) {
|
|
118
|
+
rect.Left = rectBuf.readInt32LE(0);
|
|
119
|
+
rect.Top = rectBuf.readInt32LE(4);
|
|
120
|
+
rect.Right = rectBuf.readInt32LE(8);
|
|
121
|
+
rect.Bottom = rectBuf.readInt32LE(12);
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
},
|
|
125
|
+
MoveWindow,
|
|
126
|
+
SetForegroundWindow,
|
|
127
|
+
ShowWindow,
|
|
128
|
+
IsZoomed,
|
|
129
|
+
IsIconic,
|
|
130
|
+
GetWindowPlacement,
|
|
131
|
+
EnumDisplayMonitors: (callback, dwData) => {
|
|
132
|
+
__enumDisplayMonitorsCbRef = koffi.register(callback, koffi.pointer(MonitorEnumProc));
|
|
133
|
+
try {
|
|
134
|
+
return EnumDisplayMonitors(null, null, __enumDisplayMonitorsCbRef, null);
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
__enumDisplayMonitorsCbRef = null;
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
GetMonitorInfoW: (hMonitor, lpmi) => {
|
|
141
|
+
// MONITORINFO structure:
|
|
142
|
+
// DWORD cbSize (4 bytes)
|
|
143
|
+
// RECT rcMonitor (16 bytes)
|
|
144
|
+
// RECT rcWork (16 bytes)
|
|
145
|
+
// DWORD dwFlags (4 bytes)
|
|
146
|
+
// Total: 40 bytes
|
|
147
|
+
const buffer = Buffer.alloc(40);
|
|
148
|
+
buffer.writeUInt32LE(40, 0); // cbSize
|
|
149
|
+
const result = GetMonitorInfoW(hMonitor, buffer);
|
|
150
|
+
if (result) {
|
|
151
|
+
lpmi.cbSize = buffer.readUInt32LE(0);
|
|
152
|
+
lpmi.rcMonitor = {
|
|
153
|
+
Left: buffer.readInt32LE(4),
|
|
154
|
+
Top: buffer.readInt32LE(8),
|
|
155
|
+
Right: buffer.readInt32LE(12),
|
|
156
|
+
Bottom: buffer.readInt32LE(16),
|
|
157
|
+
};
|
|
158
|
+
lpmi.rcWork = {
|
|
159
|
+
Left: buffer.readInt32LE(20),
|
|
160
|
+
Top: buffer.readInt32LE(24),
|
|
161
|
+
Right: buffer.readInt32LE(28),
|
|
162
|
+
Bottom: buffer.readInt32LE(32),
|
|
163
|
+
};
|
|
164
|
+
lpmi.dwFlags = buffer.readUInt32LE(36);
|
|
165
|
+
}
|
|
166
|
+
return result;
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const user32 = await initializeFFI();
|
|
171
|
+
export { user32, isBun };
|
|
172
|
+
//# sourceMappingURL=ffi-wrapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ffi-wrapper.js","sourceRoot":"","sources":["ffi-wrapper.ts"],"names":[],"mappings":"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"}
|