How to connect F7 via F12 and Power / Eject on a MacBook keyboard

This question follows from How to connect / remap an arbitrary keyboard event in OSX?

So far, I have been able to use modifier keys and most other keys using:

_eventTap = CGEventTapCreate( kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, CGEventMaskBit( kCGEventKeyDown ) | CGEventMaskBit( kCGEventFlagsChanged ) , (CGEventTapCallBack)_tapCallback, (__bridge void *)(self)); 

It is noteworthy that F3 correctly reports the key code (160) before taking action. that is, I can disable the action by returning a NULL event handler (and thus could not propagate the event).

However, F7-F12 and Eject / Power do not activate the callback.

If I add:

  | CGEventMaskBit( NSSystemDefined ) 

... Now the remaining Fx DO keys trigger the callback (although Power / Eject still does not work), but I cannot access the keyCode method of the event.

An error message is displayed:

2015-05-21 12: 30: 02.044 tap_k [16532: 698660] NSSystemDefined: 0 2015-05-21 12: 30: 02.044 tap_k [16532: 698660] * Approval error in - [NCEvent keyCode], / SourceCache / AppKit / AppKit-1347.57 / AppKit.subproj / NSEvent.m: 2471 2015-05-21 12: 30: 02.045 tap_k [16532: 698660] * Application terminated due to an uncaught exception "NSInternalInconsistencyException", reason: 'Invalid message sent to the event "NSEvent: type = SysDefined loc = (882,687) time = 118943.3 flags = 0 win = 0x0 winNum = 0 ctxt = 0x0 subtype = 8 data1 = 2560 data2 = -1" '

So either:
(1) I need another way to retrieve a unique identifier from NSEvent, or
(2) I need to push / hook at a lower level.

Working with (1), I notice that NSEvent has the data1 property. Record in hexadecimal format gives:

 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 

.. When I started / launched F6 F7 F8 F9 F10 F11 F12.

(also the last value changes to 1 for repetitions).

So, I think that I could just eat events with these values ​​and pass other NSSystemDefined events through.

And this still does not solve the problem of catching Eject / Power.

But is there a cleaner / better way?

If anyone is interested in playing, here is the full code:

 // 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; } 
+3
source share
2 answers

I just wanted to point out that if you haven’t done this, you should study MASShortcut to find out how it connects to the F7-F12. However, I do not think that it can recognize Power + Eject keys.

+1
source

This happens halfway. It prints a scancode for each key. This could indeed be due to rewriting in ObjC / ARC to tidy up the reels for storage / release.

Does anyone have a desire to go?

I pulled the code from Using IOHIDManager to get key modifier events

 // 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 } 
0
source

Source: https://habr.com/ru/post/987434/


All Articles