simen-keyboard-listener 1.0.5 → 1.0.6

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/dist/index.d.mts CHANGED
@@ -23,5 +23,14 @@ declare function getGlobalKeyboardListener(): IGlobalKeyboardListener;
23
23
  * This function is kept for backward compatibility but now returns the singleton.
24
24
  */
25
25
  declare function createGlobalKeyboardListener(): IGlobalKeyboardListener;
26
+ /**
27
+ * Check if the app has permission to listen to global keyboard events.
28
+ * - On macOS: checks accessibility permission (System Preferences > Security & Privacy > Accessibility)
29
+ * - On Windows: always returns true (no special permission needed)
30
+ * - On other platforms: returns false
31
+ *
32
+ * Call this before starting the listener to provide better UX when permission is missing.
33
+ */
34
+ declare function checkKeyboardPermission(): boolean;
26
35
 
27
- export { type IGlobalKeyDownMap, type IGlobalKeyEvent, type IGlobalKeyListener, type IGlobalKeyState, type IGlobalKeyboardListener, createGlobalKeyboardListener, getGlobalKeyboardListener };
36
+ export { type IGlobalKeyDownMap, type IGlobalKeyEvent, type IGlobalKeyListener, type IGlobalKeyState, type IGlobalKeyboardListener, checkKeyboardPermission, createGlobalKeyboardListener, getGlobalKeyboardListener };
package/dist/index.d.ts CHANGED
@@ -23,5 +23,14 @@ declare function getGlobalKeyboardListener(): IGlobalKeyboardListener;
23
23
  * This function is kept for backward compatibility but now returns the singleton.
24
24
  */
25
25
  declare function createGlobalKeyboardListener(): IGlobalKeyboardListener;
26
+ /**
27
+ * Check if the app has permission to listen to global keyboard events.
28
+ * - On macOS: checks accessibility permission (System Preferences > Security & Privacy > Accessibility)
29
+ * - On Windows: always returns true (no special permission needed)
30
+ * - On other platforms: returns false
31
+ *
32
+ * Call this before starting the listener to provide better UX when permission is missing.
33
+ */
34
+ declare function checkKeyboardPermission(): boolean;
26
35
 
27
- export { type IGlobalKeyDownMap, type IGlobalKeyEvent, type IGlobalKeyListener, type IGlobalKeyState, type IGlobalKeyboardListener, createGlobalKeyboardListener, getGlobalKeyboardListener };
36
+ export { type IGlobalKeyDownMap, type IGlobalKeyEvent, type IGlobalKeyListener, type IGlobalKeyState, type IGlobalKeyboardListener, checkKeyboardPermission, createGlobalKeyboardListener, getGlobalKeyboardListener };
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ checkKeyboardPermission: () => checkKeyboardPermission,
33
34
  createGlobalKeyboardListener: () => createGlobalKeyboardListener,
34
35
  getGlobalKeyboardListener: () => getGlobalKeyboardListener
35
36
  });
@@ -161,8 +162,20 @@ function getGlobalKeyboardListener() {
161
162
  function createGlobalKeyboardListener() {
162
163
  return getGlobalKeyboardListener();
163
164
  }
165
+ function checkKeyboardPermission() {
166
+ if (process.platform !== "darwin" && process.platform !== "win32") {
167
+ return false;
168
+ }
169
+ try {
170
+ const addon = getNativeAddon();
171
+ return addon.checkPermission();
172
+ } catch {
173
+ return false;
174
+ }
175
+ }
164
176
  // Annotate the CommonJS export names for ESM import in node:
165
177
  0 && (module.exports = {
178
+ checkKeyboardPermission,
166
179
  createGlobalKeyboardListener,
167
180
  getGlobalKeyboardListener
168
181
  });
package/dist/index.mjs CHANGED
@@ -132,7 +132,19 @@ function getGlobalKeyboardListener() {
132
132
  function createGlobalKeyboardListener() {
133
133
  return getGlobalKeyboardListener();
134
134
  }
135
+ function checkKeyboardPermission() {
136
+ if (process.platform !== "darwin" && process.platform !== "win32") {
137
+ return false;
138
+ }
139
+ try {
140
+ const addon = getNativeAddon();
141
+ return addon.checkPermission();
142
+ } catch {
143
+ return false;
144
+ }
145
+ }
135
146
  export {
147
+ checkKeyboardPermission,
136
148
  createGlobalKeyboardListener,
137
149
  getGlobalKeyboardListener
138
150
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simen-keyboard-listener",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Native global keyboard listener for macOS and Windows",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -23,7 +23,7 @@
23
23
  "build:native": "node-gyp rebuild --directory=src/native",
24
24
  "build:ts": "tsup src/index.ts --format cjs,esm --dts --clean",
25
25
  "build": "npm run build:native && npm run build:ts",
26
- "prebuild": "prebuildify --napi --strip --cwd src/native",
26
+ "prebuild": "prebuildify --napi --strip --cwd src/native --out ../../prebuilds",
27
27
  "prepublishOnly": "npm run build:ts"
28
28
  },
29
29
  "keywords": [
@@ -70,6 +70,16 @@ void emitKeyEvent(const std::string& name, const std::string& state) {
70
70
 
71
71
  namespace {
72
72
 
73
+ // Check if the app has accessibility permission (required for CGEventTap)
74
+ static bool macHasAccessibilityPermission() {
75
+ // AXIsProcessTrusted returns true if the app has accessibility permission
76
+ return AXIsProcessTrusted();
77
+ }
78
+
79
+ } // namespace
80
+
81
+ namespace {
82
+
73
83
  CFMachPortRef g_eventTap = nullptr;
74
84
  CFRunLoopSourceRef g_runLoopSource = nullptr;
75
85
  CFRunLoopRef g_runLoop = nullptr;
@@ -226,6 +236,13 @@ static CGEventRef macEventTapCallback(CGEventTapProxy /*proxy*/, CGEventType typ
226
236
  }
227
237
 
228
238
  static void macThreadMain() {
239
+ // Pre-check accessibility permission before attempting to create event tap
240
+ if (!macHasAccessibilityPermission()) {
241
+ g_running.store(false);
242
+ setStartResult(-1);
243
+ return;
244
+ }
245
+
229
246
  CGEventMask mask = CGEventMaskBit(kCGEventKeyDown) |
230
247
  CGEventMaskBit(kCGEventKeyUp) |
231
248
  CGEventMaskBit(kCGEventFlagsChanged);
@@ -617,10 +634,26 @@ static Napi::Value IsRunning(const Napi::CallbackInfo& info) {
617
634
  return Napi::Boolean::New(info.Env(), g_running.load());
618
635
  }
619
636
 
637
+ // Check if the app has permission to listen to global keyboard events
638
+ // Returns: true if permission granted, false otherwise
639
+ // On Windows, always returns true (no special permission needed)
640
+ // On macOS, checks accessibility permission
641
+ static Napi::Value CheckPermission(const Napi::CallbackInfo& info) {
642
+ #if defined(__APPLE__)
643
+ return Napi::Boolean::New(info.Env(), macHasAccessibilityPermission());
644
+ #elif defined(_WIN32)
645
+ // Windows doesn't require special permission for keyboard hooks
646
+ return Napi::Boolean::New(info.Env(), true);
647
+ #else
648
+ return Napi::Boolean::New(info.Env(), false);
649
+ #endif
650
+ }
651
+
620
652
  static Napi::Object Init(Napi::Env env, Napi::Object exports) {
621
653
  exports.Set("start", Napi::Function::New(env, Start));
622
654
  exports.Set("stop", Napi::Function::New(env, Stop));
623
655
  exports.Set("isRunning", Napi::Function::New(env, IsRunning));
656
+ exports.Set("checkPermission", Napi::Function::New(env, CheckPermission));
624
657
  return exports;
625
658
  }
626
659