keyspy 1.0.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/LICENSE +21 -0
- package/README.md +287 -0
- package/build/index.d.ts +53 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +149 -0
- package/build/ts/MacKeyServer.d.ts +45 -0
- package/build/ts/MacKeyServer.d.ts.map +1 -0
- package/build/ts/MacKeyServer.js +159 -0
- package/build/ts/WinKeyServer.d.ts +30 -0
- package/build/ts/WinKeyServer.d.ts.map +1 -0
- package/build/ts/WinKeyServer.js +88 -0
- package/build/ts/X11KeyServer.d.ts +45 -0
- package/build/ts/X11KeyServer.d.ts.map +1 -0
- package/build/ts/X11KeyServer.js +159 -0
- package/build/ts/_data/MacGlobalKeyLookup.d.ts +3 -0
- package/build/ts/_data/MacGlobalKeyLookup.d.ts.map +1 -0
- package/build/ts/_data/MacGlobalKeyLookup.js +155 -0
- package/build/ts/_data/WinGlobalKeyLookup.d.ts +6 -0
- package/build/ts/_data/WinGlobalKeyLookup.d.ts.map +1 -0
- package/build/ts/_data/WinGlobalKeyLookup.js +181 -0
- package/build/ts/_data/X11GlobalKeyLookup.d.ts +3 -0
- package/build/ts/_data/X11GlobalKeyLookup.d.ts.map +1 -0
- package/build/ts/_data/X11GlobalKeyLookup.js +144 -0
- package/build/ts/_types/IConfig.d.ts +15 -0
- package/build/ts/_types/IConfig.d.ts.map +1 -0
- package/build/ts/_types/IConfig.js +3 -0
- package/build/ts/_types/IGlobalKey.d.ts +19 -0
- package/build/ts/_types/IGlobalKey.d.ts.map +1 -0
- package/build/ts/_types/IGlobalKey.js +3 -0
- package/build/ts/_types/IGlobalKeyDownMap.d.ts +5 -0
- package/build/ts/_types/IGlobalKeyDownMap.d.ts.map +1 -0
- package/build/ts/_types/IGlobalKeyDownMap.js +3 -0
- package/build/ts/_types/IGlobalKeyEvent.d.ts +17 -0
- package/build/ts/_types/IGlobalKeyEvent.d.ts.map +1 -0
- package/build/ts/_types/IGlobalKeyEvent.js +3 -0
- package/build/ts/_types/IGlobalKeyListener.d.ts +14 -0
- package/build/ts/_types/IGlobalKeyListener.d.ts.map +1 -0
- package/build/ts/_types/IGlobalKeyListener.js +3 -0
- package/build/ts/_types/IGlobalKeyListenerRaw.d.ts +7 -0
- package/build/ts/_types/IGlobalKeyListenerRaw.d.ts.map +1 -0
- package/build/ts/_types/IGlobalKeyListenerRaw.js +3 -0
- package/build/ts/_types/IGlobalKeyLookup.d.ts +9 -0
- package/build/ts/_types/IGlobalKeyLookup.d.ts.map +1 -0
- package/build/ts/_types/IGlobalKeyLookup.js +3 -0
- package/build/ts/_types/IGlobalKeyResult.d.ts +12 -0
- package/build/ts/_types/IGlobalKeyResult.d.ts.map +1 -0
- package/build/ts/_types/IGlobalKeyResult.js +3 -0
- package/build/ts/_types/IGlobalKeyServer.d.ts +16 -0
- package/build/ts/_types/IGlobalKeyServer.d.ts.map +1 -0
- package/build/ts/_types/IGlobalKeyServer.js +3 -0
- package/build/ts/_types/IMacConfig.d.ts +10 -0
- package/build/ts/_types/IMacConfig.d.ts.map +1 -0
- package/build/ts/_types/IMacConfig.js +3 -0
- package/build/ts/_types/IWindowsConfig.d.ts +10 -0
- package/build/ts/_types/IWindowsConfig.d.ts.map +1 -0
- package/build/ts/_types/IWindowsConfig.js +3 -0
- package/build/ts/_types/IX11Config.d.ts +10 -0
- package/build/ts/_types/IX11Config.d.ts.map +1 -0
- package/build/ts/_types/IX11Config.js +3 -0
- package/build/ts/isSpawnEventSupported.d.ts +6 -0
- package/build/ts/isSpawnEventSupported.d.ts.map +1 -0
- package/build/ts/isSpawnEventSupported.js +18 -0
- package/package.json +80 -0
- package/scripts/download-binaries.js +191 -0
package/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2021 LaunchMenu
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
@@ -0,0 +1,287 @@
|
|
1
|
+
# KeySpy 🕵️
|
2
|
+
|
3
|
+
> A powerful, cross-platform keyboard and mouse event listener for Node.js
|
4
|
+
|
5
|
+
KeySpy is a modern, lightweight library that provides global keyboard and mouse event monitoring across Windows, macOS, and Linux. Unlike other solutions, KeySpy uses pre-compiled native binaries and a multi-process architecture for maximum stability and compatibility.
|
6
|
+
|
7
|
+
## ✨ Features
|
8
|
+
|
9
|
+
- 🌍 **Cross-platform**: Works on Windows, macOS, and Linux (X11)
|
10
|
+
- 🚀 **Zero compilation**: Pre-compiled binaries downloaded automatically, no node-gyp required
|
11
|
+
- 🔒 **System-level capture**: Can intercept system shortcuts like Ctrl+Alt+Delete, Cmd+Space
|
12
|
+
- 🎯 **Event blocking**: Prevent captured events from reaching other applications
|
13
|
+
- 📦 **TypeScript ready**: Full TypeScript support with comprehensive type definitions
|
14
|
+
- 🏗️ **Modern architecture**: Multi-process design for enhanced stability
|
15
|
+
- ⚡ **High performance**: Optimized native implementations for each platform
|
16
|
+
- 📥 **Smart installation**: Automatically downloads platform-specific binaries from GitHub Releases
|
17
|
+
|
18
|
+
## 🔧 Platform Support
|
19
|
+
|
20
|
+
| Platform | Status | Implementation | Tested On |
|
21
|
+
|----------|--------|---------------|-----------|
|
22
|
+
| Windows | ✅ Full | C++ (Low-level hooks) | Windows 10/11 |
|
23
|
+
| macOS | ✅ Full | Swift (CGEventTap) | macOS 10.14+ |
|
24
|
+
| Linux | ✅ X11 Only | C++ (XInput2) | Ubuntu, Arch Linux |
|
25
|
+
|
26
|
+
## 🚀 Quick Start
|
27
|
+
|
28
|
+
```ts
|
29
|
+
import { GlobalKeyboardListener } from "keyspy";
|
30
|
+
|
31
|
+
const keyspy = new GlobalKeyboardListener();
|
32
|
+
|
33
|
+
// Listen to all keyboard events
|
34
|
+
keyspy.addListener((e, down) => {
|
35
|
+
console.log(`${e.name} ${e.state} [${e.rawKey._nameRaw}]`);
|
36
|
+
});
|
37
|
+
```
|
38
|
+
|
39
|
+
## 📖 Usage Examples
|
40
|
+
|
41
|
+
### Basic Event Logging
|
42
|
+
|
43
|
+
```ts
|
44
|
+
import { GlobalKeyboardListener } from "keyspy";
|
45
|
+
|
46
|
+
const keyspy = new GlobalKeyboardListener();
|
47
|
+
|
48
|
+
keyspy.addListener((e, down) => {
|
49
|
+
console.log(`Key: ${e.name}, State: ${e.state}, Location: ${e.location}`);
|
50
|
+
});
|
51
|
+
```
|
52
|
+
|
53
|
+
### Capturing System Shortcuts
|
54
|
+
|
55
|
+
```ts
|
56
|
+
// Capture Cmd+Space (macOS) or Win+Space (Windows)
|
57
|
+
keyspy.addListener((e, down) => {
|
58
|
+
if (e.state === "DOWN" && e.name === "SPACE" &&
|
59
|
+
(down["LEFT META"] || down["RIGHT META"])) {
|
60
|
+
console.log("System shortcut captured!");
|
61
|
+
return true; // Prevent the event from reaching other apps
|
62
|
+
}
|
63
|
+
});
|
64
|
+
|
65
|
+
// Capture Alt+F4
|
66
|
+
keyspy.addListener((e, down) => {
|
67
|
+
if (e.state === "DOWN" && e.name === "F4" &&
|
68
|
+
(down["LEFT ALT"] || down["RIGHT ALT"])) {
|
69
|
+
console.log("Alt+F4 intercepted!");
|
70
|
+
return true;
|
71
|
+
}
|
72
|
+
});
|
73
|
+
```
|
74
|
+
|
75
|
+
### Advanced Configuration
|
76
|
+
|
77
|
+
```ts
|
78
|
+
const keyspy = new GlobalKeyboardListener({
|
79
|
+
windows: {
|
80
|
+
onError: (errorCode) => console.error("Windows error:", errorCode),
|
81
|
+
onInfo: (info) => console.info("Windows info:", info)
|
82
|
+
},
|
83
|
+
mac: {
|
84
|
+
onError: (errorCode) => console.error("macOS error:", errorCode),
|
85
|
+
},
|
86
|
+
x11: {
|
87
|
+
onError: (errorCode) => console.error("Linux error:", errorCode),
|
88
|
+
}
|
89
|
+
});
|
90
|
+
```
|
91
|
+
|
92
|
+
### Cleanup and Resource Management
|
93
|
+
|
94
|
+
```ts
|
95
|
+
// Remove specific listener
|
96
|
+
const myListener = (e, down) => { /* ... */ };
|
97
|
+
keyspy.addListener(myListener);
|
98
|
+
keyspy.removeListener(myListener);
|
99
|
+
|
100
|
+
// Clean shutdown
|
101
|
+
keyspy.kill(); // Removes all listeners and stops the key server
|
102
|
+
```
|
103
|
+
|
104
|
+
## 📦 Installation
|
105
|
+
|
106
|
+
```bash
|
107
|
+
npm install keyspy
|
108
|
+
# or
|
109
|
+
pnpm add keyspy
|
110
|
+
# or
|
111
|
+
yarn add keyspy
|
112
|
+
```
|
113
|
+
|
114
|
+
**What happens during installation:**
|
115
|
+
1. 📦 Downloads the main package (TypeScript code)
|
116
|
+
2. 🔍 Detects your platform (Windows/macOS/Linux) and architecture
|
117
|
+
3. 📥 Automatically downloads the appropriate pre-compiled binary from GitHub Releases
|
118
|
+
4. ✅ Ready to use immediately!
|
119
|
+
|
120
|
+
## 🤔 Why KeySpy?
|
121
|
+
|
122
|
+
Choosing the right keyboard listener for your Node.js project can be challenging. Here's how KeySpy compares to other popular solutions:
|
123
|
+
|
124
|
+
| Feature | Electron globalShortcut | IOHook | **KeySpy** |
|
125
|
+
|---------|------------------------|--------|------------|
|
126
|
+
| **Setup Complexity** | Simple | Complex (node-gyp) | **Simple** |
|
127
|
+
| **System Shortcuts** | ❌ Limited | ✅ Full | **✅ Full** |
|
128
|
+
| **Event Blocking** | ❌ No | ✅ Yes | **✅ Yes** |
|
129
|
+
| **Node.js Compatibility** | ❌ Electron only | ⚠️ Version dependent | **✅ All versions** |
|
130
|
+
| **Compilation Required** | ❌ No | ❌ Yes | **✅ No** |
|
131
|
+
| **Arbitrary Key Support** | ❌ Limited | ⚠️ Limited | **✅ Full** |
|
132
|
+
| **Process Architecture** | In-process | In-process | **Multi-process** |
|
133
|
+
|
134
|
+
### 🎯 **KeySpy Advantages**
|
135
|
+
|
136
|
+
- **🔧 Zero Setup**: Pre-compiled binaries work out of the box
|
137
|
+
- **🌐 Universal**: Compatible with all Node.js versions (14+)
|
138
|
+
- **🔒 System-Level**: Capture any key combination, including OS shortcuts
|
139
|
+
- **🛡️ Stable**: Multi-process architecture prevents crashes
|
140
|
+
- **📝 TypeScript**: Full type definitions included
|
141
|
+
- **🎮 Flexible**: Listen to individual keys or complex combinations
|
142
|
+
|
143
|
+
### ⚠️ **Considerations**
|
144
|
+
|
145
|
+
- **Permissions**: macOS requires Accessibility permissions
|
146
|
+
- **Antivirus**: Some antivirus software may flag the native binaries
|
147
|
+
- **Performance**: Small overhead due to inter-process communication
|
148
|
+
|
149
|
+
## 🛠️ Development
|
150
|
+
|
151
|
+
### Quick Development Setup
|
152
|
+
|
153
|
+
```bash
|
154
|
+
# Clone and setup
|
155
|
+
git clone https://github.com/teomyth/keyspy.git
|
156
|
+
cd keyspy
|
157
|
+
pnpm install
|
158
|
+
|
159
|
+
# Development workflow
|
160
|
+
pnpm dev # Watch mode compilation
|
161
|
+
pnpm build # Production build
|
162
|
+
pnpm test # Run all tests
|
163
|
+
pnpm test:manual # Interactive testing
|
164
|
+
```
|
165
|
+
|
166
|
+
### Building Native Binaries
|
167
|
+
|
168
|
+
KeySpy includes pre-compiled binaries, but you can rebuild them if needed:
|
169
|
+
|
170
|
+
```bash
|
171
|
+
# Platform-specific builds
|
172
|
+
pnpm build:win # Windows (requires MinGW)
|
173
|
+
pnpm build:swift # macOS (requires Xcode)
|
174
|
+
pnpm build:x11 # Linux (requires X11 dev libraries)
|
175
|
+
```
|
176
|
+
|
177
|
+
### Code Quality
|
178
|
+
|
179
|
+
```bash
|
180
|
+
pnpm lint # Check code quality with Biome
|
181
|
+
pnpm lint:fix # Auto-fix issues
|
182
|
+
pnpm format # Format code
|
183
|
+
pnpm clean # Remove build artifacts
|
184
|
+
```
|
185
|
+
|
186
|
+
### Testing
|
187
|
+
|
188
|
+
```bash
|
189
|
+
pnpm test:unit # Unit tests
|
190
|
+
pnpm test:integration # Integration tests
|
191
|
+
pnpm test:manual # Manual interactive testing
|
192
|
+
```
|
193
|
+
|
194
|
+
## 📋 API Reference
|
195
|
+
|
196
|
+
### GlobalKeyboardListener
|
197
|
+
|
198
|
+
#### Constructor
|
199
|
+
|
200
|
+
```ts
|
201
|
+
new GlobalKeyboardListener(config?: IConfig)
|
202
|
+
```
|
203
|
+
|
204
|
+
#### Methods
|
205
|
+
|
206
|
+
- `addListener(listener: IGlobalKeyListener): Promise<void>` - Add event listener
|
207
|
+
- `removeListener(listener: IGlobalKeyListener): void` - Remove event listener
|
208
|
+
- `kill(): void` - Stop all listeners and cleanup
|
209
|
+
|
210
|
+
#### Event Object
|
211
|
+
|
212
|
+
```ts
|
213
|
+
interface IGlobalKeyEvent {
|
214
|
+
name: string; // Key name (e.g., "A", "SPACE", "F1")
|
215
|
+
state: "UP" | "DOWN"; // Key state
|
216
|
+
rawKey: IGlobalKey; // Raw key information
|
217
|
+
vKey: number; // Virtual key code
|
218
|
+
scanCode: number; // Scan code
|
219
|
+
location: [number, number]; // Mouse location (for mouse events)
|
220
|
+
}
|
221
|
+
```
|
222
|
+
|
223
|
+
## 🔒 Security & Permissions
|
224
|
+
|
225
|
+
### macOS
|
226
|
+
- Requires **Accessibility** permissions in System Preferences
|
227
|
+
- First run will prompt for permission automatically
|
228
|
+
|
229
|
+
### Windows
|
230
|
+
- May require administrator privileges for system-wide hooks
|
231
|
+
- Some antivirus software may flag the binary (false positive)
|
232
|
+
|
233
|
+
### Linux
|
234
|
+
- Requires X11 display server
|
235
|
+
- May need to run with appropriate user permissions
|
236
|
+
|
237
|
+
## 🔧 Troubleshooting
|
238
|
+
|
239
|
+
### Binary Download Issues
|
240
|
+
|
241
|
+
If the automatic binary download fails during installation:
|
242
|
+
|
243
|
+
```bash
|
244
|
+
# Option 1: Manual download from GitHub Releases
|
245
|
+
# Visit: https://github.com/teomyth/keyspy/releases
|
246
|
+
# Download the appropriate file for your platform
|
247
|
+
|
248
|
+
# Option 2: Build from source
|
249
|
+
npm run build:swift # macOS
|
250
|
+
npm run build:x11 # Linux
|
251
|
+
npm run build:win # Windows
|
252
|
+
|
253
|
+
# Option 3: Skip download in CI environments
|
254
|
+
export KEYSPY_SKIP_DOWNLOAD=true
|
255
|
+
npm install keyspy
|
256
|
+
```
|
257
|
+
|
258
|
+
### Platform Support
|
259
|
+
|
260
|
+
- **macOS**: ARM64 (Apple Silicon) and x64 (Intel) - Universal binary
|
261
|
+
- **Linux**: x64 only, requires X11
|
262
|
+
- **Windows**: x64 only
|
263
|
+
|
264
|
+
### Common Issues
|
265
|
+
|
266
|
+
1. **"Binary not found"**: Run the appropriate build command for your platform
|
267
|
+
2. **Permission denied**: Make sure the binary has execute permissions (`chmod +x`)
|
268
|
+
3. **Network issues**: Check your internet connection and GitHub access
|
269
|
+
|
270
|
+
## 🤝 Contributing
|
271
|
+
|
272
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
273
|
+
|
274
|
+
1. Fork the repository
|
275
|
+
2. Create a feature branch
|
276
|
+
3. Make your changes
|
277
|
+
4. Add tests if applicable
|
278
|
+
5. Submit a pull request
|
279
|
+
|
280
|
+
## 📄 License
|
281
|
+
|
282
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
283
|
+
|
284
|
+
## 🙏 Acknowledgments
|
285
|
+
|
286
|
+
- Original concept inspired by [LaunchMenu](https://github.com/LaunchMenu/LaunchMenu)
|
287
|
+
- Built with modern tooling: TypeScript, Biome, Jest, and Turbo
|
package/build/index.d.ts
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
import type { IConfig } from "./ts/_types/IConfig";
|
2
|
+
import type { IGlobalKeyListener } from "./ts/_types/IGlobalKeyListener";
|
3
|
+
import type { IGlobalKeyServer } from "./ts/_types/IGlobalKeyServer";
|
4
|
+
export * from "./ts/_types/IGlobalKeyListener";
|
5
|
+
export * from "./ts/_types/IGlobalKeyEvent";
|
6
|
+
export * from "./ts/_types/IGlobalKey";
|
7
|
+
export * from "./ts/_types/IGlobalKeyDownMap";
|
8
|
+
export * from "./ts/_types/IWindowsConfig";
|
9
|
+
export * from "./ts/_types/IConfig";
|
10
|
+
export * from "./ts/_types/IGlobalKeyResult";
|
11
|
+
/**
|
12
|
+
* A cross-platform global keyboard listener. Ideal for setting up global keyboard shortcuts
|
13
|
+
* and key-loggers (usually for automation).
|
14
|
+
* This keyserver uses low-level hooks on Windows OS and Event Taps on Mac OS, which allows
|
15
|
+
* event propagation to be halted to the rest of the operating system as well as allowing
|
16
|
+
* any key to be used for shortcuts.
|
17
|
+
*/
|
18
|
+
export declare class GlobalKeyboardListener {
|
19
|
+
/** The underlying keyServer used to listen and halt propagation of events */
|
20
|
+
protected keyServer: IGlobalKeyServer;
|
21
|
+
protected listeners: Array<IGlobalKeyListener>;
|
22
|
+
protected config: IConfig;
|
23
|
+
/** Whether the server is currently running */
|
24
|
+
protected isRunning: boolean;
|
25
|
+
protected stopTimeoutID: NodeJS.Timeout | number;
|
26
|
+
/** The underlying map of keys that are being held down */
|
27
|
+
private readonly isDown;
|
28
|
+
/**
|
29
|
+
* Creates a new keyboard listener
|
30
|
+
* @param config The optional configuration for the key listener
|
31
|
+
*/
|
32
|
+
constructor(config?: IConfig);
|
33
|
+
/**
|
34
|
+
* Add a global keyboard listener to the global keyboard listener server.
|
35
|
+
* @param listener The listener to add to the global keyboard listener
|
36
|
+
* @throws An exception if the process could not be started
|
37
|
+
*/
|
38
|
+
addListener(listener: IGlobalKeyListener): Promise<void>;
|
39
|
+
/**
|
40
|
+
* Remove a global keyboard listener from the global keyboard listener server.
|
41
|
+
* @param listener The listener to remove from the global keyboard listener
|
42
|
+
*/
|
43
|
+
removeListener(listener: IGlobalKeyListener): void;
|
44
|
+
/** Removes all listeners and destroys the key server */
|
45
|
+
kill(): void;
|
46
|
+
/** Start the key server */
|
47
|
+
protected start(): Promise<void>;
|
48
|
+
/** Stop the key server */
|
49
|
+
protected stop(): void;
|
50
|
+
/** The following listener is used to monitor which keys are being held down */
|
51
|
+
private baseListener;
|
52
|
+
}
|
53
|
+
//# sourceMappingURL=index.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,8BAA8B,CAAC;AAE7C;;;;;;GAMG;AACH,qBAAa,sBAAsB;IACjC,6EAA6E;IAC7E,SAAS,CAAC,SAAS,EAAE,gBAAgB,CAAC;IACtC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC/C,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAE1B,8CAA8C;IAC9C,SAAS,CAAC,SAAS,UAAS;IAC5B,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,CAAK;IAErD,0DAA0D;IAC1D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAE3C;;;OAGG;gBACgB,MAAM,GAAE,OAAY;IAmBvC;;;;OAIG;IACU,WAAW,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQrE;;;OAGG;IACI,cAAc,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI;IAWzD,wDAAwD;IACjD,IAAI;IAKX,2BAA2B;IAC3B,SAAS,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOhC,0BAA0B;IAC1B,SAAS,CAAC,IAAI,IAAI,IAAI;IAKtB,+EAA+E;IAC/E,OAAO,CAAC,YAAY,CA+BlB;CACH"}
|
package/build/index.js
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
15
|
+
};
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
18
|
+
};
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
20
|
+
exports.GlobalKeyboardListener = void 0;
|
21
|
+
const node_os_1 = __importDefault(require("node:os"));
|
22
|
+
const MacKeyServer_1 = require("./ts/MacKeyServer");
|
23
|
+
const WinKeyServer_1 = require("./ts/WinKeyServer");
|
24
|
+
const X11KeyServer_1 = require("./ts/X11KeyServer");
|
25
|
+
__exportStar(require("./ts/_types/IGlobalKeyListener"), exports);
|
26
|
+
__exportStar(require("./ts/_types/IGlobalKeyEvent"), exports);
|
27
|
+
__exportStar(require("./ts/_types/IGlobalKey"), exports);
|
28
|
+
__exportStar(require("./ts/_types/IGlobalKeyDownMap"), exports);
|
29
|
+
__exportStar(require("./ts/_types/IWindowsConfig"), exports);
|
30
|
+
__exportStar(require("./ts/_types/IConfig"), exports);
|
31
|
+
__exportStar(require("./ts/_types/IGlobalKeyResult"), exports);
|
32
|
+
/**
|
33
|
+
* A cross-platform global keyboard listener. Ideal for setting up global keyboard shortcuts
|
34
|
+
* and key-loggers (usually for automation).
|
35
|
+
* This keyserver uses low-level hooks on Windows OS and Event Taps on Mac OS, which allows
|
36
|
+
* event propagation to be halted to the rest of the operating system as well as allowing
|
37
|
+
* any key to be used for shortcuts.
|
38
|
+
*/
|
39
|
+
class GlobalKeyboardListener {
|
40
|
+
/**
|
41
|
+
* Creates a new keyboard listener
|
42
|
+
* @param config The optional configuration for the key listener
|
43
|
+
*/
|
44
|
+
constructor(config = {}) {
|
45
|
+
/** Whether the server is currently running */
|
46
|
+
this.isRunning = false;
|
47
|
+
this.stopTimeoutID = 0;
|
48
|
+
/** The following listener is used to monitor which keys are being held down */
|
49
|
+
this.baseListener = (event) => {
|
50
|
+
if (event.name) {
|
51
|
+
switch (event.state) {
|
52
|
+
case "DOWN":
|
53
|
+
this.isDown[event.name] = true;
|
54
|
+
break;
|
55
|
+
case "UP":
|
56
|
+
this.isDown[event.name] = false;
|
57
|
+
break;
|
58
|
+
}
|
59
|
+
}
|
60
|
+
let stopPropagation = false;
|
61
|
+
for (const onKey of this.listeners) {
|
62
|
+
//Forward event
|
63
|
+
try {
|
64
|
+
const res = onKey(event, this.isDown);
|
65
|
+
//Handle catch data
|
66
|
+
if (res instanceof Object) {
|
67
|
+
if (res.stopPropagation)
|
68
|
+
stopPropagation = true;
|
69
|
+
if (res.stopImmediatePropagation)
|
70
|
+
break;
|
71
|
+
}
|
72
|
+
else if (res) {
|
73
|
+
stopPropagation = true;
|
74
|
+
}
|
75
|
+
}
|
76
|
+
catch (e) {
|
77
|
+
console.error(e);
|
78
|
+
}
|
79
|
+
}
|
80
|
+
return stopPropagation;
|
81
|
+
};
|
82
|
+
this.listeners = [];
|
83
|
+
this.isDown = {};
|
84
|
+
this.config = config;
|
85
|
+
switch (node_os_1.default.platform()) {
|
86
|
+
case "win32":
|
87
|
+
this.keyServer = new WinKeyServer_1.WinKeyServer(this.baseListener, config.windows);
|
88
|
+
break;
|
89
|
+
case "darwin":
|
90
|
+
this.keyServer = new MacKeyServer_1.MacKeyServer(this.baseListener, config.mac);
|
91
|
+
break;
|
92
|
+
case "linux":
|
93
|
+
this.keyServer = new X11KeyServer_1.X11KeyServer(this.baseListener, config.x11);
|
94
|
+
break;
|
95
|
+
default:
|
96
|
+
throw Error("This OS is not supported");
|
97
|
+
}
|
98
|
+
}
|
99
|
+
/**
|
100
|
+
* Add a global keyboard listener to the global keyboard listener server.
|
101
|
+
* @param listener The listener to add to the global keyboard listener
|
102
|
+
* @throws An exception if the process could not be started
|
103
|
+
*/
|
104
|
+
async addListener(listener) {
|
105
|
+
this.listeners.push(listener);
|
106
|
+
if (this.listeners.length === 1) {
|
107
|
+
clearTimeout(this.stopTimeoutID);
|
108
|
+
await this.start();
|
109
|
+
}
|
110
|
+
}
|
111
|
+
/**
|
112
|
+
* Remove a global keyboard listener from the global keyboard listener server.
|
113
|
+
* @param listener The listener to remove from the global keyboard listener
|
114
|
+
*/
|
115
|
+
removeListener(listener) {
|
116
|
+
var _a;
|
117
|
+
const index = this.listeners.indexOf(listener);
|
118
|
+
if (index !== -1) {
|
119
|
+
this.listeners.splice(index, 1);
|
120
|
+
if (this.listeners.length === 0) {
|
121
|
+
if (this.config.disposeDelay === -1)
|
122
|
+
this.stop();
|
123
|
+
else
|
124
|
+
this.stopTimeoutID = setTimeout(() => this.stop(), (_a = this.config.disposeDelay) !== null && _a !== void 0 ? _a : 100);
|
125
|
+
}
|
126
|
+
}
|
127
|
+
}
|
128
|
+
/** Removes all listeners and destroys the key server */
|
129
|
+
kill() {
|
130
|
+
this.listeners = [];
|
131
|
+
this.stop();
|
132
|
+
}
|
133
|
+
/** Start the key server */
|
134
|
+
start() {
|
135
|
+
let promise = Promise.resolve();
|
136
|
+
if (!this.isRunning)
|
137
|
+
promise = this.keyServer.start();
|
138
|
+
this.isRunning = true;
|
139
|
+
return promise;
|
140
|
+
}
|
141
|
+
/** Stop the key server */
|
142
|
+
stop() {
|
143
|
+
if (this.isRunning)
|
144
|
+
this.keyServer.stop();
|
145
|
+
this.isRunning = false;
|
146
|
+
}
|
147
|
+
}
|
148
|
+
exports.GlobalKeyboardListener = GlobalKeyboardListener;
|
149
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxzREFBeUI7QUFDekIsb0RBQWlEO0FBQ2pELG9EQUFpRDtBQUNqRCxvREFBaUQ7QUFPakQsaUVBQStDO0FBQy9DLDhEQUE0QztBQUM1Qyx5REFBdUM7QUFDdkMsZ0VBQThDO0FBQzlDLDZEQUEyQztBQUMzQyxzREFBb0M7QUFDcEMsK0RBQTZDO0FBRTdDOzs7Ozs7R0FNRztBQUNILE1BQWEsc0JBQXNCO0lBYWpDOzs7T0FHRztJQUNILFlBQW1CLFNBQWtCLEVBQUU7UUFYdkMsOENBQThDO1FBQ3BDLGNBQVMsR0FBRyxLQUFLLENBQUM7UUFDbEIsa0JBQWEsR0FBNEIsQ0FBQyxDQUFDO1FBNEVyRCwrRUFBK0U7UUFDdkUsaUJBQVksR0FBMEIsQ0FBQyxLQUFLLEVBQUUsRUFBRTtZQUN0RCxJQUFJLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQztnQkFDZixRQUFRLEtBQUssQ0FBQyxLQUFLLEVBQUUsQ0FBQztvQkFDcEIsS0FBSyxNQUFNO3dCQUNULElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxHQUFHLElBQUksQ0FBQzt3QkFDL0IsTUFBTTtvQkFDUixLQUFLLElBQUk7d0JBQ1AsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsS0FBSyxDQUFDO3dCQUNoQyxNQUFNO2dCQUNWLENBQUM7WUFDSCxDQUFDO1lBRUQsSUFBSSxlQUFlLEdBQUcsS0FBSyxDQUFDO1lBQzVCLEtBQUssTUFBTSxLQUFLLElBQUksSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO2dCQUNuQyxlQUFlO2dCQUNmLElBQUksQ0FBQztvQkFDSCxNQUFNLEdBQUcsR0FBRyxLQUFLLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztvQkFF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
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import type { IGlobalKeyServer } from "./_types/IGlobalKeyServer";
|
2
|
+
import type { IGlobalKeyListenerRaw } from "./_types/IGlobalKeyListenerRaw";
|
3
|
+
import type { IGlobalKeyEvent } from "./_types/IGlobalKeyEvent";
|
4
|
+
import type { IMacConfig } from "./_types/IMacConfig";
|
5
|
+
/** Use this class to listen to key events on Mac OS */
|
6
|
+
export declare class MacKeyServer implements IGlobalKeyServer {
|
7
|
+
protected listener: IGlobalKeyListenerRaw;
|
8
|
+
private proc;
|
9
|
+
private config;
|
10
|
+
private running;
|
11
|
+
private restarting;
|
12
|
+
/**
|
13
|
+
* Creates a new key server for mac
|
14
|
+
* @param listener The callback to report key events to
|
15
|
+
* @param config Additional optional configuration for the server
|
16
|
+
*/
|
17
|
+
constructor(listener: IGlobalKeyListenerRaw, config?: IMacConfig);
|
18
|
+
/**
|
19
|
+
* Start the Key server and listen for keypresses
|
20
|
+
* @param skipPerms Whether to skip attempting to add permissions
|
21
|
+
*/
|
22
|
+
start(skipPerms?: boolean): Promise<void>;
|
23
|
+
/**
|
24
|
+
* Deals with the startup process of the server, possibly adding perms if required and restarting
|
25
|
+
* @param skipPerms Whether to skip attempting to add permissions
|
26
|
+
*/
|
27
|
+
protected handleStartup(skipPerms: boolean): Promise<void>;
|
28
|
+
/**
|
29
|
+
* Makes sure that the given path is executable
|
30
|
+
* @param path The path to add the perms to
|
31
|
+
*/
|
32
|
+
protected addPerms(path: string): Promise<void>;
|
33
|
+
/** Stop the Key server */
|
34
|
+
stop(): void;
|
35
|
+
/**
|
36
|
+
* Obtains a IGlobalKeyEvent from stdout buffer data
|
37
|
+
* @param data Data from stdout
|
38
|
+
* @returns The standardized key event data
|
39
|
+
*/
|
40
|
+
protected _getEventData(data: Buffer): {
|
41
|
+
event: IGlobalKeyEvent;
|
42
|
+
eventId: string;
|
43
|
+
}[];
|
44
|
+
}
|
45
|
+
//# sourceMappingURL=MacKeyServer.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"MacKeyServer.d.ts","sourceRoot":"","sources":["../../src/ts/MacKeyServer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAGhE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAKtD,uDAAuD;AACvD,qBAAa,YAAa,YAAW,gBAAgB;IACnD,SAAS,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IAC1C,OAAO,CAAC,IAAI,CAAe;IAC3B,OAAO,CAAC,MAAM,CAAa;IAE3B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAS;IAE3B;;;;OAIG;gBACS,QAAQ,EAAE,qBAAqB,EAAE,MAAM,GAAE,UAAe;IAKpE;;;OAGG;IACI,KAAK,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BhD;;;OAGG;IACH,SAAS,CAAC,aAAa,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAwC1D;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB/C,0BAA0B;IACnB,IAAI;IAMX;;;;OAIG;IACH,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,eAAe,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE;CAiCrF"}
|