首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何通过F7和Power/Eject在F7键盘上点击(钩子)

如何通过F7和Power/Eject在F7键盘上点击(钩子)
EN

Stack Overflow用户
提问于 2015-05-21 11:48:10
回答 2查看 1K关注 0票数 5

这个问题来自于如何在OSX上挂载/重新映射任意键盘事件?

到目前为止,我能够点击修饰符键和大多数其他键使用:

代码语言:javascript
复制
    _eventTap = CGEventTapCreate( kCGHIDEventTap, 
                                  kCGHeadInsertEventTap,
                                  kCGEventTapOptionDefault,
                                       CGEventMaskBit( kCGEventKeyDown )
                                     | CGEventMaskBit( kCGEventFlagsChanged )
                                     ,
                                  (CGEventTapCallBack)_tapCallback,
                                  (__bridge void *)(self));

值得注意的是,F3在采取行动之前正确地报告了一个关键代码(160)。也就是说,我可以通过让我的事件处理程序返回NULL (从而无法传播该事件)来禁用该操作。

但是,F7通过F12和Eject/Power不会触发回调。

如果我加上:

代码语言:javascript
复制
                                     | CGEventMaskBit( NSSystemDefined )

..。现在剩下的Fx键确实会触发回调(虽然Power/Eject仍然不会),但是我无法访问事件的keyCode方法。

它会产生一个错误:

2015-05-21 12:30:02.044 tap_k16532:698660 NSSystemDefined: 0 2015-05-21 12:30:02.044 tap_k16532:698660 * -NSEvent keyCode中的断言失败原因:“发送给事件的无效消息"NSEvent: type=SysDefined loc=(882,687) time=118943.3 flags=0 win=0x0 winNum=0 ctxt=0x0 subtype=8 data1=2560 data2 2=-1”

因此,要么是:

(1)我需要其他方法从NSEvent中提取一些唯一标识符,或者

(2)我需要在较低的水平上拍打/钩。

使用(1)时,我注意到NSEvent有一个data1属性。这是以十六进制表示的日志记录:

代码语言:javascript
复制
2015-05-21 12:40:05.428 tap_k[16576:704298] NSSystemDefined: 140b00
2015-05-21 12:40:06.914 tap_k[16576:704298] NSSystemDefined: 100a00
2015-05-21 12:40:06.992 tap_k[16576:704298] NSSystemDefined: 100b00
2015-05-21 12:40:07.600 tap_k[16576:704298] NSSystemDefined: 130a00
2015-05-21 12:40:07.690 tap_k[16576:704298] NSSystemDefined: 130b00
2015-05-21 12:40:08.219 tap_k[16576:704298] NSSystemDefined: 70a00
2015-05-21 12:40:08.277 tap_k[16576:704298] NSSystemDefined: 70b00
2015-05-21 12:40:09.062 tap_k[16576:704298] NSSystemDefined: 10a00
2015-05-21 12:40:09.186 tap_k[16576:704298] NSSystemDefined: 10b00
2015-05-21 12:40:09.637 tap_k[16576:704298] NSSystemDefined: a00
2015-05-21 12:40:09.726 tap_k[16576:704298] NSSystemDefined: b00

。。当我按下/输入F6 F7 F8 F9 F10 F11 F12.

(同样,最后一个值更改为1表示重复)。

因此,我想我可以只吃这些值的事件,并传递其他NSSystemDefined事件通过。

它仍然不能解决弹出/捕捉能量的问题。

但有没有一种更清洁/更好的方法?

如果有人有兴趣玩,下面是完整的代码:

代码语言:javascript
复制
// compile and run from the commandline with:
//    clang -fobjc-arc -framework Cocoa  ./foo.m  -o foo
//    sudo ./foo

#import <Foundation/Foundation.h>
#import <AppKit/NSEvent.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]
            _runLoopSource = CFMachPortCreateRunLoopSource( kCFAllocatorDefault,
                                                            _eventTap, 0);
            // Add to the current run loop.
            CFRunLoopAddSource( CFRunLoopGetCurrent(), _runLoopSource,
                                kCFRunLoopCommonModes);

            NSLog(@"Registering event tap as run loop source.");
            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];

    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: %x", 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;
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-05-21 16:10:25

半途而废。它打印出每一把钥匙的扫描码。它确实可以用ObjC/ARC重写来清理保留/释放的筒管。

有人想试试吗?

我从使用IOHIDManager获取修饰符键事件调出了代码

代码语言:javascript
复制
// compile and run from the commandline with:
//    clang  -framework coreFoundation  -framework IOKit  ./HID.c  -o hid
//    sudo ./hid

// This code works with the IOHID library to get notified of keys.
//   Still haven't figured out how to truly intercept with
//   substitution.

#include <IOKit/hid/IOHIDValue.h>
#include <IOKit/hid/IOHIDManager.h>

void myHIDKeyboardCallback( void* context,  IOReturn result,  void* sender,  IOHIDValueRef value )
{
    IOHIDElementRef elem = IOHIDValueGetElement( value );

    if (IOHIDElementGetUsagePage(elem) != 0x07)
        return;

    uint32_t scancode = IOHIDElementGetUsage( elem );

    if (scancode < 4 || scancode > 231)
        return;

    long pressed = IOHIDValueGetIntegerValue( value );

    printf( "scancode: %d, pressed: %ld\n", scancode, pressed );
}


CFMutableDictionaryRef myCreateDeviceMatchingDictionary( UInt32 usagePage,  UInt32 usage )
{
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
                                                            kCFAllocatorDefault, 0
                                                        , & kCFTypeDictionaryKeyCallBacks
                                                        , & kCFTypeDictionaryValueCallBacks );
    if ( ! dict )
        return NULL;

    CFNumberRef pageNumberRef = CFNumberCreate( kCFAllocatorDefault, kCFNumberIntType, & usagePage );
    if ( ! pageNumberRef ) {
        CFRelease( dict );
        return NULL;
    }

    CFDictionarySetValue( dict, CFSTR(kIOHIDDeviceUsagePageKey), pageNumberRef );
    CFRelease( pageNumberRef );

    CFNumberRef usageNumberRef = CFNumberCreate( kCFAllocatorDefault, kCFNumberIntType, & usage );

    if ( ! usageNumberRef ) {
        CFRelease( dict );
        return NULL;
    }

    CFDictionarySetValue( dict, CFSTR(kIOHIDDeviceUsageKey), usageNumberRef );
    CFRelease( usageNumberRef );

    return dict;
}


int main(void)
{
    IOHIDManagerRef hidManager = IOHIDManagerCreate( kCFAllocatorDefault, kIOHIDOptionsTypeNone );

    CFArrayRef matches;
    {
        CFMutableDictionaryRef keyboard = myCreateDeviceMatchingDictionary( 0x01, 6 );
        CFMutableDictionaryRef keypad   = myCreateDeviceMatchingDictionary( 0x01, 7 );

        CFMutableDictionaryRef matchesList[] = { keyboard, keypad };

        matches = CFArrayCreate( kCFAllocatorDefault, (const void **)matchesList, 2, NULL );
    }

    IOHIDManagerSetDeviceMatchingMultiple( hidManager, matches );

    IOHIDManagerRegisterInputValueCallback( hidManager, myHIDKeyboardCallback, NULL );

    IOHIDManagerScheduleWithRunLoop( hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode );

    IOHIDManagerOpen( hidManager, kIOHIDOptionsTypeNone );

    CFRunLoopRun(); // spins
}
票数 1
EN

Stack Overflow用户

发布于 2015-05-21 14:03:15

我只想指出,如果你没有,你应该看看MASShortcut,看看它是如何连接到F7-F12键。尽管如此,我不相信它能识别电源+弹出键。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30372852

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档