【问题标题】:OS X count pressed keys without accessibility accessOS X 计算按下的键而没有辅助功能访问
【发布时间】:2017-02-08 20:31:10
【问题描述】:

我发现 upwork.app 可以在没有无障碍访问的情况下计算按下的键,但我不明白它是如何完成的。

我读过很多这样的主题: OSX: Detect system-wide keyDown events?

在所有主题中都说应该信任进程“启用辅助设备访问”,我无法找到 upwork.app 如何在没有这个的情况下跟踪密钥。

这是跟踪事件的官方文档:
https://developer.apple.com/reference/appkit/nsevent/1535472-addglobalmonitorforeventsmatchin

只有在启用了可访问性或您的应用程序在可访问性访问方面受信任(请参阅 AXIsProcessTrusted)时,才能监控与密钥相关的事件。

在苹果邮件列表中也是一样的:
http://lists.apple.com/archives/carbon-dev/2010/Feb/msg00043.html

我认为 upwork.app 使用了一些技巧。

如何在没有无障碍访问的情况下计算按下的键?

【问题讨论】:

  • 如果您使用 HIDManager(它是 IOKit 的一部分,它是一个 macOS 框架),您可以检测键盘本身并在没有可访问性的情况下获取按键事件 :) 如果您仍在寻找回答我可以发布功能来做到这一点。这是一个小代码,但你找不到太多关于它的内容。

标签: objective-c macos


【解决方案1】:

在 cmets 中仍然没有收到您的答案,但因为这可能对其未来的其他人也有帮助,所以我还是决定回答。

使用 IOKit,您可以检测键盘是否有设备,并像设备事件一样获取按键事件。我用它来检测操纵杆事件,但它应该可以很好地与键盘一起使用。我认为我所做的修改已经足够并且应该可以工作,但是我的 Xcode 现在正在更新,所以我还不能测试它。

KeyboardWatcher.h 文件:

#import <Foundation/Foundation.h>
#import <IOKit/hid/IOHIDManager.h>
#import <IOKit/hid/IOHIDKeys.h>

@interface KeyboardWatcher : NSObject{
   IOHIDManagerRef HIDManager;
}

@property (nonatomic) int keysPressedCount;

+(instancetype)sharedWatcher;
-(void)startWatching;
-(void)stopWatching;

@end

KeyboardWatcher.m 文件:

#import "KeyboardWatcher.h"

@implementation KeyboardWatcher

static KeyboardWatcher *_sharedWatcher;

+(instancetype)sharedWatcher {
    @synchronized([self class]) {
        if (!_sharedWatcher){
            _sharedWatcher = [[KeyboardWatcher alloc] init];
        }
        return _sharedWatcher;
    }
    return nil;
}
-(instancetype)init {
    self = [super init];
    if (self){
        self.keysPressedCount = 0;
    }
    return self;
}

-(void)startWatching {
    [self watchDevicesOfType:kHIDUsage_GD_Keyboard];
}
-(void)watchDevicesOfType:(UInt32)deviceType {
    // Create an HID Manager
    HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);

    // Create a Matching Dictionary
    CFMutableDictionaryRef matchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks,
                                                                                         &kCFTypeDictionaryValueCallBacks);

    // That will make the app just return the computer keyboards
    CFDictionarySetValue(matchDict, CFSTR(kIOHIDPrimaryUsageKey), CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &deviceType));

    // Register the Matching Dictionary to the HID Manager
    IOHIDManagerSetDeviceMatching(HIDManager, matchDict);

    // Register the HID Manager on our app’s run loop
    IOHIDManagerScheduleWithRunLoop(HIDManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);

    // Open the HID Manager
    IOReturn IOReturn = IOHIDManagerOpen(HIDManager, kIOHIDOptionsTypeNone);

    // Register input calls to Handle_DeviceEventCallback function
    IOHIDManagerRegisterInputValueCallback(HIDManager, Handle_DeviceEventCallback, nil);

    if (IOReturn) NSLog(@"IOHIDManagerOpen failed.");
}
-(void)stopWatching {
    HIDManager = NULL;
}

static void Handle_DeviceEventCallback (void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef value){
    IOHIDElementRef element = IOHIDValueGetElement(value);          // Keyboard pressed key

    uint32_t uniqueIdentifier = IOHIDElementGetCookie(element);     // Unique ID of key
    int elementValue = (int)IOHIDValueGetIntegerValue(value);       // Actual state of key (1=pressed)

    NSLog(@"Unique ID = %u; Value = %d", uniqueIdentifier, elementValue);

    if (elementValue == 1) KeyboardWatcher.sharedWatcher.keysPressedCount++;
}

@end

如果您想确定哪个唯一 ID 是哪个键,您可以使用这些枚举(而不是导入 Carbon,您可以创建一个 CGKeyboardMapping.h 文件并将它们粘贴到那里): https://stackoverflow.com/a/16125341/4370893

最后,为了使用它,你只需要开始监听键盘事件:

[[KeyboardWatcher sharedWatcher] startWatching];

获取按键次数:

[[KeyboardWatcher sharedWatcher] keysPressedCount];

然后停止:

[[KeyboardWatcher sharedWatcher] stopWatching];

这些是我编写原始操纵杆代码的参考资料:

一旦更新完成,我将测试代码并确定它是否正常工作。

编辑:刚刚经过测试,它正在工作。不要忘记将 IOKit 框架添加到项目中。

【讨论】:

  • 你在 swift 中有相同的实现吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-04-22
  • 1970-01-01
  • 1970-01-01
  • 2022-11-30
  • 2020-12-28
  • 1970-01-01
  • 2012-07-29
相关资源
最近更新 更多