本文介绍了在OSX中,如何确定哪个键盘生成了NSEvent?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在尝试(从事件处理程序中)确定哪个键盘触发了该事件.我一直在使用这两篇​​文章:

I've been trying to determine (from within the event handler) which keyboard triggered the event. I've been using these two posts:

在第二篇文章中,作者使用Carbon技术成功地分离了键盘,但是使用可可粉尝试相同的技巧失败了.

In the second article, the author successfully separate out his keyboards using a Carbon technique, but attempting the same trick using Cocoa fails.

在我自己的系统上,两个都失败了,可能是因为我的无线键盘也是​​苹果制造的,所以报告的标识符与内置键盘相同.

On my own system both fail, maybe because my wireless keyboard is also apple manufactured, so maybe reports the same identifier as the inbuilt keyboard.

在第一篇文章中,有人提出了一种解决方案,该方案在较低级别(可以区分键盘)上监视键盘事件,并将此数据存储在队列中,然后让事件处理程序检索信息.

In the first article someone proposes a solution of watching keyboard events at a lower level (where it IS possible to distinguish the keyboards) and storing this data in a queue, and having the event handler retrieve the information.

这看起来有点毛茸茸,所以我只是在这里检查是否有人发现了更好的东西.

This looks a bit hairy, so I'm just checking here to see if anyone has found something better.

这是我的代码,展示了在事件处理程序中区分键盘的失败:

Here is my code that demonstrates the failing of differentiating the keyboards in the event handler:

// compile and run from the commandline with:
//    clang -fobjc-arc -framework Cocoa -framework Carbon  ./tap_k.m  -o tap_k
//    sudo ./tap_k

#import <Foundation/Foundation.h>
#import <AppKit/NSEvent.h>
//#import <CarbonEventsCore.h>
#include <Carbon/Carbon.h>


typedef CFMachPortRef EventTap;

// - - - - - - - - - - - - - - - - - - - - -

@interface KeyChanger : NSObject
{
@private
    EventTap            _eventTap;
    CFRunLoopSourceRef  _runLoopSource;
    CGEventRef          _lastEvent;
}
@end

// - - - - - - - - - - - - - - - - - - - - -

CGEventRef _tapCallback(
                        CGEventTapProxy proxy,
                        CGEventType     type,
                        CGEventRef      event,
                        KeyChanger*     listener
                        );

// - - - - - - - - - - - - - - - - - - - - -

@implementation KeyChanger

- (BOOL)tapEvents
{
    if (!_eventTap) {
        NSLog(@"Initializing an event tap.");

        // kCGHeadInsertEventTap -- new event tap should be inserted before any pre-existing event taps at the same location,
        _eventTap = CGEventTapCreate( kCGHIDEventTap, // kCGSessionEventTap,
                                      kCGHeadInsertEventTap,
                                      kCGEventTapOptionDefault,
                                           CGEventMaskBit( kCGEventKeyDown )
                                         | CGEventMaskBit( kCGEventFlagsChanged )
                                         | CGEventMaskBit( NSSystemDefined )
                                         ,
                                      (CGEventTapCallBack)_tapCallback,
                                      (__bridge void *)(self));
        if (!_eventTap) {
            NSLog(@"unable to create event tap. must run as root or "
                    "add privlidges for assistive devices to this app.");
            return NO;
        }
    }
    CGEventTapEnable(_eventTap, TRUE);

    return [self isTapActive];
}

- (BOOL)isTapActive
{
    return CGEventTapIsEnabled(_eventTap);
}

- (void)listen
{
    if( ! _runLoopSource ) {
        if( _eventTap ) { // dont use [self tapActive]
            NSLog(@"Registering event tap as run loop source.");
            _runLoopSource = CFMachPortCreateRunLoopSource( kCFAllocatorDefault, _eventTap, 0 );

            // Add to the current run loop.
            CFRunLoopAddSource( CFRunLoopGetCurrent(), _runLoopSource, kCFRunLoopCommonModes );

            CFRunLoopRun();
        }else{
            NSLog(@"No Event tap in place! You will need to call listen after tapEvents to get events.");
        }
    }
}

- (CGEventRef)processEvent:(CGEventRef)cgEvent
{
    NSEvent* event = [NSEvent eventWithCGEvent:cgEvent];


    //NSEventType type = [event type];

    EventRef ce = (EventRef)[event eventRef];

    if(ce)
    {
        unsigned kbt;
        GetEventParameter(
                            ce,
                            kEventParamKeyboardType,
                            typeUInt32, NULL,
                            sizeof kbt, NULL,
                            & kbt
                            );

        NSLog(@"CARBON Keyboard type: %d",kbt);
    }

    CGEventSourceRef evSrc = CGEventCreateSourceFromEvent( cgEvent );

    if(evSrc)
    {
        unsigned kbt = (NSUInteger) CGEventSourceGetKeyboardType( evSrc );
        CFRelease(evSrc);
        NSLog(@"COCOA: %d",kbt);
    }

    //[super sendEvent:anEvent];
    //}


    NSUInteger modifiers = [event modifierFlags] &
        (NSCommandKeyMask | NSAlternateKeyMask | NSShiftKeyMask | NSControlKeyMask);

    enum {
       kVK_ANSI_3 = 0x14,
    };


    switch( event.type ) {
        case NSFlagsChanged:
            NSLog(@"NSFlagsChanged: %d", event.keyCode);
            break;

        case NSSystemDefined:
            NSLog(@"NSSystemDefined: %lx", event.data1);
            return NULL;

        case kCGEventKeyDown:
            NSLog(@"KeyDown: %d", event.keyCode);
            break;

        default:
            NSLog(@"WTF");
    }


    // TODO: add other cases and do proper handling of case
    if (
        //[event.characters caseInsensitiveCompare:@"3"] == NSOrderedSame
        event.keyCode == kVK_ANSI_3
        && modifiers == NSShiftKeyMask
        )
    {
        NSLog(@"Got SHIFT+3");

        event = [NSEvent keyEventWithType: event.type
                                 location: NSZeroPoint
                            modifierFlags: event.modifierFlags & ! NSShiftKeyMask
                                timestamp: event.timestamp
                             windowNumber: event.windowNumber
                                  context: event.context
                               characters: @"#"
              charactersIgnoringModifiers: @"#"
                                isARepeat: event.isARepeat
                                  keyCode: event.keyCode];
    }
    _lastEvent = [event CGEvent];
    CFRetain(_lastEvent); // must retain the event. will be released by the system
    return _lastEvent;
}

- (void)dealloc
{
    if( _runLoopSource ) {
        CFRunLoopRemoveSource( CFRunLoopGetCurrent(), _runLoopSource, kCFRunLoopCommonModes );
        CFRelease( _runLoopSource );
    }
    if( _eventTap ) {
        //kill the event tap
        CGEventTapEnable( _eventTap, FALSE );
        CFRelease( _eventTap );
    }
}

@end

// - - - - - - - - - - - - - - - - - - - - -

CGEventRef _tapCallback(
                        CGEventTapProxy proxy,
                        CGEventType     type,
                        CGEventRef      event,
                        KeyChanger*     listener
                        )
{
    //Do not make the NSEvent here.
    //NSEvent will throw an exception if we try to make an event from the tap timout type
    @autoreleasepool {
        if( type == kCGEventTapDisabledByTimeout ) {
            NSLog(@"event tap has timed out, re-enabling tap");
            [listener tapEvents];
            return nil;
        }
        if( type != kCGEventTapDisabledByUserInput ) {
            return [listener processEvent:event];
        }
    }
    return event;
}

// - - - - - - - - - - - - - - - - - - - - -

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        KeyChanger* keyChanger = [KeyChanger new];
        [keyChanger tapEvents];
        [keyChanger listen];//blocking call.
    }
    return 0;
}

推荐答案

以下是您可以做什么的概述:

Here's an outline of what you could do:

您设置了IOHIDManager并设置了匹配键盘的输入匹配字典.

You set up an IOHIDManager and set input matching dictionaries to match keyboards.

然后,IOHIDManager将为您提供对所有连接的键盘的引用,作为IOHIDDevices.

The IOHIDManager will then provide you with references to all attached keyboards as IOHIDDevices.

然后,最后您可以为IOHIDDevices设置输入回调.现在,每个设备都可以有一个单独的输入回调!

Then finally you can set up input callbacks for the IOHIDDevices.Now you can have a separate input callback for each device!

设置和使用它有点麻烦,并且它不允许您像CGEventTap那样过滤/更改事件.但这是我知道的监视输入的唯一方法,这样您就知道哪个设备导致了哪个输入.

This is a little cumbersome to set up and work with, and it doesn't let you filter/alter events like a CGEventTap would. But it's the only method I know for monitoring input such that you know which device caused which input.

以下是一些起点:

IOHIDManager文档

IOHIDManager docs

IOHIDUsageTables.h和IOHIDDeviceKeys.h

IOHIDUsageTables.h and IOHIDDeviceKeys.h

对于键盘,您需要像这样声明匹配字典

For keyboards you'll want to declare the match dict like this

NSDictionary *matchDict = @{
    @(kIOHIDDeviceUsagePageKey): @(kHIDPage_GenericDesktop),
    @(kIOHIDDeviceUsageKey): @(kHIDUsage_GD_Keyboard),
};

(然后通过免费桥接将其转换为CFDictionaryRef)(不确定是否正确-均未测试)

(And then convert it to CFDictionaryRef via toll-free bridging)(Not sure if correct - none of this is tested)

这篇关于在OSX中,如何确定哪个键盘生成了NSEvent?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-23 01:08
查看更多