问题描述
我一直在尝试(从事件处理程序中)确定哪个键盘触发了该事件.我一直在使用这两篇文章:
I've been trying to determine (from within the event handler) which keyboard triggered the event. I've been using these two posts:
- http://www.cocoabuilder.com/archive/cocoa/229902-which-keyboard-barcode-scanner-did-the-event-come-from.html
- http://www.cocoabuilder.com/archive/cocoa/274586-getting-keyboard-type-carbon-vs-cg.html#274586
在第二篇文章中,作者使用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
- https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-421.6/IOHIDFamily/IOHIDUsageTables.h.auto.html
- https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-1446.40.16/IOHIDFamily/IOHIDDeviceKeys.h.auto.html
- 设备匹配字典需要这些.
对于键盘,您需要像这样声明匹配字典
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?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!