Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS 7 Hardware Keyboard Events

Now that we have iOS 7, Apple apparently has removed the option to receive keyboard notifications from -sendEvent:. This is a huge pain to people who are wanting to write something that captures all key events and sends it off to a remote computer, E.G. a VNC client. UIKeyCommand does not provide the functionality needed. There are many bug reports submitted to apple, but they will not listen. The bug report apple is closing all reports as a duplicate is rdar://14129420.

What is the best solution for this?

like image 727
GRMrGecko Avatar asked Sep 11 '13 17:09

GRMrGecko


2 Answers

I have at least been able to get these events back the private API way, however the keyup doesn't return any useful info such as the key that was released. Maybe this is something which can be pulled from UIEvent?

The code that's needed to be added is the following definition of UIPhysicalKeyboardEvent.

@interface PhysicalKeyboardEvent : UIEvent {//UIPhysicalButtonsEvent
    int _inputFlags;
    NSString *_modifiedInput;
    NSString *_unmodifiedInput;
    NSString *_shiftModifiedInput;
    NSString *_commandModifiedInput;
    NSString *_markedInput;
    long long _modifierFlags;
    NSString *_privateInput;
}
+ (id)_eventWithInput:(id)arg1 inputFlags:(int)arg2;
@property(retain, nonatomic) NSString *_privateInput; // @synthesize _privateInput;
@property(nonatomic) int _inputFlags; // @synthesize _inputFlags;
@property(nonatomic) long long _modifierFlags; // @synthesize _modifierFlags;
@property(retain, nonatomic) NSString *_markedInput; // @synthesize _markedInput;
@property(retain, nonatomic) NSString *_commandModifiedInput; // @synthesize _commandModifiedInput;
@property(retain, nonatomic) NSString *_shiftModifiedInput; // @synthesize _shiftModifiedInput;
@property(retain, nonatomic) NSString *_unmodifiedInput; // @synthesize _unmodifiedInput;
@property(retain, nonatomic) NSString *_modifiedInput; // @synthesize _modifiedInput;
@property(readonly, nonatomic) long long _gsModifierFlags;
- (void)_privatizeInput;
- (void)dealloc;
- (id)_cloneEvent;
- (_Bool)isEqual:(id)arg1;
- (_Bool)_matchesKeyCommand:(id)arg1;
//- (void)_setHIDEvent:(struct __IOHIDEvent *)arg1 keyboard:(struct __GSKeyboard *)arg2;
@property(readonly, nonatomic) long long _keyCode;
@property(readonly, nonatomic) _Bool _isKeyDown;
- (long long)type;
@end

To listen for events, use the following in a UIResponder. I am not sure if the responder needs to be key or not.

- (id)_keyCommandForEvent:(PhysicalKeyboardEvent *)event {
    //Some reason it gets called twice and it's not because of keyup. Keyup seems to not mention it's original key.
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    [self performSelector:@selector(processEvent:) withObject:event afterDelay:0];
    return [super _keyCommandForEvent:event];
}
- (void)processEvent:(PhysicalKeyboardEvent *)event {
    NSLog(@"%@", [event _unmodifiedInput]);
    NSLog(@"%d", [event _isKeyDown]);
    NSLog(@"%d", [event _inputFlags]);
    if ([event _isKeyDown] && [[event _unmodifiedInput] isEqualToString:@"s"] && [event _modifierFlags]==206158430208) {
        NSLog(@"Hello");
    }
}

I'm hoping that this code will at least be of some help to people who needs this. You can determine when the command key, option key, and control key is pressed using _modifierFlags. I haven't played around with it much, but seems to be an good way to get the events.

You could possibly borrow code from http://nacho4d-nacho4d.blogspot.com/2012/01/catching-keyboard-events-in-ios.html to improve. If anyone else finds a better way, please post them!

Since this is using private APIs, it might be better for you to wrap everything in respondsToSelector. Things such as _unmodifiedInput.

I am not sure if apple would accept an application that implements this, so use at your own risk.

like image 181
GRMrGecko Avatar answered Nov 09 '22 11:11

GRMrGecko


Override this method on UIApplication and have fun :)

- (void)handleKeyUIEvent:(UIEvent *)event
{
    size_t s = malloc_size((__bridge const void *)(event));
    NSLog(@"%s enter... %ld", __func__, s);
//    unsigned char *ptr = (unsigned char *)(__bridge void *)event;

    unsigned long *ptr = (unsigned long *)(__bridge void *)event;
//
//#define OFF_KEY_MASK 12
//#define OFF_KEY_SCANCODE 15
//#define OFF_KEY_CHAR 17

//    NSLog(@"type: %lx off: %d", *(ptr + 2), 2);
//    NSLog(@"MASK: %lx off: %d", *(ptr + OFF_KEY_MASK), OFF_KEY_MASK);
//    NSLog(@"SCAN: %lx off: %d", *(ptr + OFF_KEY_SCANCODE), OFF_KEY_SCANCODE);
//    NSLog(@"CHAR: %lx off: %d", *(ptr + OFF_KEY_CHAR), OFF_KEY_CHAR);

    NSLog(@"sizeof unsigned long: %lx", sizeof(unsigned long));

    for (int i = 0; i < s / 4; ++i) {
//        NSLog(@"... [%d] = %x", i, *(unsigned char *)(ptr + i));
        NSLog(@"... [%d] = %lx", i, *(unsigned long *)(ptr + i));
    }

#define OFF_DUMP 8

    unsigned long *dump = (unsigned long *) *(ptr + OFF_DUMP);
    s = malloc_size((const void *)*(ptr + OFF_DUMP));

    NSLog(@"... *[%d] size: %ld", OFF_DUMP, malloc_size((const void *)*(ptr + OFF_DUMP)));

    for (int i = 0; i < s / 4; ++i) {
        NSLog(@"..... [%d] = %lx", i, *(unsigned long *)(dump + i));
    }

    struct objc_super super_data = { self, [UIApplication class] };
    objc_msgSendSuper(&super_data, @selector(handleKeyUIEvent:), event);
//    [super handleKeyUIEvent:event];
}
like image 23
Jack Seraph Mu Avatar answered Nov 09 '22 09:11

Jack Seraph Mu