Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Send NSEvent to background app

I need to send the key combo ^⌘C to a background app with the bundle identifier com.company.app. The key combo should then activate a menu item in that application.

Unfortunately, I have no clue how to do that. Some research pointed me to the NSEvent and CGEvent API using CGEventPostToPSN(), but I was unable to get it work correctly, as I don't know how to set up the key combo. CGEventPost() didn't seem to work with the events I created, even if the desired app is the active one.

Here is the code I eventually came up with but that doesn't work:

CGWindowID windowNumber;
NSEvent *event = [NSEvent keyEventWithType:NSKeyUp
                                  location:NSZeroPoint
                             modifierFlags:(NSControlKeyMask | NSCommandKeyMask)
                                 timestamp:[[NSProcessInfo processInfo] systemUptime]
                              windowNumber:windowNumber
                                   context:[NSGraphicsContext currentContext]
                                characters:@"c"
               charactersIgnoringModifiers:@"c"
                                 isARepeat:NO
                                   keyCode:8];
CGEventRef eventRef = [event CGEvent];

What am I supposed to do with that event now? Why is there no NSEvent equivalent for CGEventPost()? Is there even an easier way to activate that menu item than posting an event? I can easily get an instance of NSRunningApplication, but there is no suitable API to accomplish my task.

Update: I got it working:

- (void) postFakedKeyboardEventForCopyScreenToPasteboardToPSN:(ProcessSerialNumber)psn {
    CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStatePrivate);
    CGEventRef keyDownEvent = CGEventCreateKeyboardEvent(source, (CGKeyCode)8, true);
    CGEventSetFlags(keyDownEvent, (kCGEventFlagMaskControl | kCGEventFlagMaskCommand));
    CGEventRef keyUpEvent = CGEventCreateKeyboardEvent(source, (CGKeyCode)8, false);
    CGEventSetFlags(keyUpEvent, (kCGEventFlagMaskControl | kCGEventFlagMaskCommand));
    CFRelease(source);

    CGEventPostToPSN(&psn, keyDownEvent);
    CFRelease(keyDownEvent);
    CGEventPostToPSN(&psn, keyUpEvent);
    CFRelease(keyUpEvent);
}

...

OSStatus err = noErr;
ProcessSerialNumber psn;
err = GetProcessForPID([simulator processIdentifier], &psn);
if (err == noErr)
    [self postFakedKeyboardEventForCopyScreenToPasteboardToPSN:psn];
like image 282
Fabian Kreiser Avatar asked Sep 15 '11 14:09

Fabian Kreiser


1 Answers

A few notes:

  1. Send a keyDown event, too, not just keyUp. That way, it will be more like a real keypress.

  2. You need to call CGEventPostToPSN with that CGEvent, or at least SOME call that posts the event.

  3. Have you tried calling CGPostKeyboardEvent() instead?
like image 199
uliwitness Avatar answered Sep 21 '22 09:09

uliwitness