Is there a proof-of-concept Objective-C executable that enters some text into an application and then clicks the mouse, using Apple events and not AppleScript?
e.g. the AppleScript equivalent of
tell application "System Events"
tell process "Safari"
keystroke "Hello World"
click
end tell
end tell
It should work on Mac OS X 10.9, preferably be future oriented (backwards compatibility doesn't matter). The context is that I will be calling the Objective-C code from another language.
I'm saying this because I read that:
As of Mac OS X 10.7, the low-level Cocoa API (NSAppleEventDescriptor)
still lacks essential functionality (e.g. the ability to send Apple
events), while the high-level Cocoa API (Scripting Bridge) is too flawed and
limited to be a viable foundation for an appscript-style wrapper.
and:
NSAppleScript can safely be used only on the main thread
so, my goals are:
- any application (by name or if current)
- any keyboard input or mouse
- from C or Objective-C
- within a few hundred milliseconds
thanks!
Rather than using AppleEvents, the CGEvent API in CoreGraphics framework <https://developer.apple.com/library/mac/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html> lets you post low-level mouse and keyboard events to the window server.
#include <CoreGraphics/CoreGraphics.h>
NSArray *launchedApplications = [[NSWorkspace sharedWorkspace] launchedApplications]; // depreciated but I couldn't find a modern way to get the Carbon PSN
NSPredicate *filter = [NSPredicate predicateWithFormat:@"NSApplicationName = \"TextEdit\""];
NSDictionary *appInfo = [[launchedApplications filteredArrayUsingPredicate:filter] firstObject];
ProcessSerialNumber psn;
psn.highLongOfPSN = [[appInfo objectForKey:@"NSApplicationProcessSerialNumberHigh"] unsignedIntValue];
psn.lowLongOfPSN = [[appInfo objectForKey:@"NSApplicationProcessSerialNumberLow"] unsignedIntValue];
CGEventRef event1 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, true); // 'z' key down
CGEventRef event2 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, false); // 'z' key up
CGEventPostToPSN(&psn, event1);
CGEventPostToPSN(&psn, event2);
You might also consider writing a Service <https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/SysServices/introduction.html>, which lets you provide functionality to other applications through the Service menu in the application menu. Note that you can even assign keyboard shortcuts to Service menu items. Services work via the system pasteboard; this approach may be easier than dealing with raw window server events if you simply need to be able to paste some canned or generated data into another application.
I'm not sure if this is what you're looking for, but you may be interested in setting up an NSInvocation object:
- (void)invokeWithTarget:(id)anObject
If you're looking to run some code and 'simulate' a UX environment, it may be valuable to save an invocation and run it.
(Automator?)
The best way for achieving your result is using Automator,
See https://developer.apple.com/library/mac/documentation/AppleApplications/Conceptual/AutomatorConcepts/AutomatorConcepts.pdf
If you want to achieve this through ObjectiveC, you need to understand "Distributed Objects Architecture".
By pairing NSPort and NSInvocation, you can do amazing things, like cross-process and cross-machine method calling.
Here is a guide for that
https://developer.apple.com/librarY/prerelease/mac/documentation/Cocoa/Conceptual/DistrObjects/Concepts/architecture.html