Skip to content

Commit 1d49735

Browse files
committed
feat: add askForInputMonitoringAccess()
1 parent 188b6f2 commit 1d49735

File tree

4 files changed

+58
-3
lines changed

4 files changed

+58
-3
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,28 @@ askForCameraAccess().then(status => {
230230
})
231231
```
232232

233+
## `permissions.askForInputMonitoringAccess()`
234+
235+
Returns `Promise<String>` - Current permission status; can be `authorized` or `denied`.
236+
237+
Checks the authorization status for input monitoring access. If the status check returns:
238+
239+
* `not determined` - A dialog will be displayed directing the user to the `Security & Privacy` System Preferences window , where the user can approve your app to monitor keyboard events in the background. The Promise is resolved as `denied`.
240+
* `denied` - The `Security & Privacy` System Preferences window is opened with the Input Monitoring privacy key highlighted. On open of the `Security & Privacy` window, the Promise is resolved as `denied`.
241+
242+
**Note:**
243+
244+
- `status` will be resolved back as `authorized` prior to macOS 10.15, as the underlying API was not introduced until that version.
245+
246+
Example:
247+
```js
248+
const { askForInputMonitoringAccess } = require('node-mac-permissions')
249+
250+
askForInputMonitoringAccess().then(status => {
251+
console.log(`Access to Input Monitoring is ${status}`)
252+
})
253+
```
254+
233255
## `permissions.askForMicrophoneAccess()`
234256

235257
Returns `Promise<String>` - Current permission status; can be `authorized`, `denied`, or `restricted`.

index.d.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
// Type definitions for node-mac-permissions
22
// Project: node-mac-permissions
33

4+
export function askForAccessibilityAccess(): undefined
45
export function askForCalendarAccess(): Promise<Omit<PermissionType, 'restricted'>>
6+
export function askForCameraAccess(): Promise<PermissionType>
57
export function askForContactsAccess(): Promise<Omit<PermissionType, 'restricted'>>
68
export function askForFoldersAccess(): Promise<Omit<PermissionType, 'restricted'>>
79
export function askForFullDiskAccess(): undefined
8-
export function askForRemindersAccess(): Promise<Omit<PermissionType, 'restricted'>>
9-
export function askForCameraAccess(): Promise<PermissionType>
10+
export function askForInputMonitoringAccess(): Promise<Omit<PermissionType, 'restricted'>>
1011
export function askForMicrophoneAccess(): Promise<PermissionType>
1112
export function askForPhotosAccess(): Promise<PermissionType>
13+
export function askForRemindersAccess(): Promise<Omit<PermissionType, 'restricted'>>
1214
export function askForSpeechRecognitionAccess(): Promise<Omit<PermissionType, 'restricted'>>
1315
export function askForScreenCaptureAccess(): undefined
14-
export function askForAccessibilityAccess(): undefined
1516
export function getAuthStatus(authType: AuthType): PermissionType | 'not determined'
1617

1718
export type AuthType =

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ module.exports = {
4242
askForContactsAccess: permissions.askForContactsAccess,
4343
askForFoldersAccess,
4444
askForFullDiskAccess: permissions.askForFullDiskAccess,
45+
askForInputMonitoringAccess: permissions.askForInputMonitoringAccess,
4546
askForRemindersAccess: permissions.askForRemindersAccess,
4647
askForMicrophoneAccess: permissions.askForMicrophoneAccess,
4748
askForMusicLibraryAccess: permissions.askForMusicLibraryAccess,

permissions.mm

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,35 @@ void AskForFullDiskAccess(const Napi::CallbackInfo &info) {
676676
return deferred.Promise();
677677
}
678678

679+
// Request Input Monitoring access.
680+
Napi::Promise AskForInputMonitoringAccess(const Napi::CallbackInfo &info) {
681+
Napi::Env env = info.Env();
682+
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
683+
684+
if (@available(macOS 10.16, *)) {
685+
std::string auth_status = InputMonitoringAuthStatus();
686+
687+
if (auth_status == kNotDetermined) {
688+
IOHIDRequestAccess(kIOHIDRequestTypeListenEvent);
689+
deferred.Resolve(Napi::String::New(env, kDenied));
690+
} else if (auth_status == kDenied) {
691+
NSWorkspace *workspace = [[NSWorkspace alloc] init];
692+
NSString *pref_string = @"x-apple.systempreferences:com.apple.preference."
693+
@"security?Privacy_ListenEvent";
694+
695+
[workspace openURL:[NSURL URLWithString:pref_string]];
696+
697+
deferred.Resolve(Napi::String::New(env, kDenied));
698+
} else {
699+
deferred.Resolve(Napi::String::New(env, auth_status));
700+
}
701+
} else {
702+
deferred.Resolve(Napi::String::New(env, kAuthorized));
703+
}
704+
705+
return deferred.Promise();
706+
}
707+
679708
// Request Apple Music Library access.
680709
Napi::Promise AskForMusicLibraryAccess(const Napi::CallbackInfo &info) {
681710
Napi::Env env = info.Env();
@@ -790,6 +819,8 @@ void AskForAccessibilityAccess(const Napi::CallbackInfo &info) {
790819
Napi::Function::New(env, AskForScreenCaptureAccess));
791820
exports.Set(Napi::String::New(env, "askForAccessibilityAccess"),
792821
Napi::Function::New(env, AskForAccessibilityAccess));
822+
exports.Set(Napi::String::New(env, "askForInputMonitoringAccess"),
823+
Napi::Function::New(env, AskForInputMonitoringAccess));
793824

794825
return exports;
795826
}

0 commit comments

Comments
 (0)