I’m trying to use some Apple Events in my Cocoa project to get access to the Terminal application. I did not want to use embedded AppleScripts or any compiled scripts, so I started looking into the NSAppleEventDescriptor.
I have succeeded in creating a new window using the do script command, and I have succeeded to get the window from the return value. The only thing I want to do right now is get a property of that window.
I had now idea how to get such a property, so I started googling. Unfortunately, there aren’t a lot of good examples how to use Apple Events, and I failed to find what I was looking for.
So I started digging deeper. And the first thing I did was looking for the code for the get command in the Terminal .sdef file. I failed to find it, so I did a grep on my /System/ directory and I found /System/Library/Frameworks/AppleScriptKit.framework/Versions/A/Resources/AppleScriptKit.sdef. I apparently found the core of the AppleScript syntax. That file did indeed have the getd four character code.
So now I know I have to use the getd event from the Core Suite. However, the argument to that get-command was an objectSpecifier. I have searched high and low for an example that uses kAEGetData. But I have failed to find any code from which I could learn anything.
So I am asking here: how do I build such an objectSpecifier descriptor?
This is what I’ve already got:
Code to create and get the tab descriptor
NSAppleEventDescriptor *createEvent;
NSAppleEventDescriptor *scriptParam;
AppleEvent aeReply;
OSErr err;
/* Make the do script event */
createEvent = [NSAppleEventDescriptor
appleEventWithEventClass:kAECoreSuite
eventID:kAEDoScript
targetDescriptor:_applicationDescriptor
returnID:kAutoGenerateReturnID
transactionID:kAnyTransactionID];
if(createEvent == nil) {
NSLog(@"%s Failed to create a do script event",
__PRETTY_FUNCTION__);
return nil;
}
/* Make script parameter */
scriptParam = [NSAppleEventDescriptor descriptorWithString:@"echo Hello World"];
if(scriptParam == nil) {
NSLog(@"%s Failed to create the script parameter",
__PRETTY_FUNCTION__);
return nil;
}
/* Set parameter */
[createEvent setParamDescriptor:scriptParam forKeyword:keyDirectObject];
/* Send event */
err = AESendMessage([createEvent aeDesc], &aeReply,
kAEWaitReply | kAENeverInteract, kAEDefaultTimeout);
if(err != noErr) {
NSLog(@"%s Failed to send the create command",
__PRETTY_FUNCTION__);
return nil;
}
/* Retrieve information */
{
/* SEE BELOW TO SEE HOW I GET THE WINDOW DESCRIPTOR */
}
Now I try and succeed in getting the window of that tab
// NSAppleEventDescriptor *ansr is set to the result of the code above
NSAppleEventDescriptor *mainObj;
NSAppleEventDescriptor *desiredClass;
NSAppleEventDescriptor *window;
mainObj = [ansr paramDescriptorForKeyword:keyDirectObject];
if([mainObj descriptorType] != typeObjectSpecifier) {
NSLog(@"%s Main object was not an object specifier",
__PRETTY_FUNCTION__);
return nil;
}
desiredClass = [mainObj paramDescriptorForKeyword:keyAEDesiredClass];
if([desiredClass typeCodeValue] != kAETerminalTab) {
NSLog(@"%s Main object's desired class was not a Terminal tab",
__PRETTY_FUNCTION__);
return nil;
}
window = [mainObj paramDescriptorForKeyword:keyAEContainer];
if(window == nil) {
NSLog(@"%s Couldn't get container of the tab",
__PRETTY_FUNCTION__);
return nil;
}
desiredClass = [window paramDescriptorForKeyword:keyAEDesiredClass];
if([desiredClass typeCodeValue] != cWindow) {
NSLog(@"%s The container of the tab was not a window",
__PRETTY_FUNCTION__);
return nil;
}
return window;
And now I fail in getting, let’s say, the bounds property
// _windowDescriptor is the result of the code above
NSAppleEventDescriptor *getEvent;
NSAppleEventDescriptor *prop;
AppleEvent aeReply;
NSAppleEventDescriptor *reply;
FourCharCode propName;
OSErr err;
propName = keyAEBounds;
/* Create get event */
getEvent = [NSAppleEventDescriptor
appleEventWithEventClass:kAECoreSuite
eventID:kAEGetData
targetDescriptor:_windowDescriptor
returnID:kAutoGenerateReturnID
transactionID:kAnyTransactionID];
if(getEvent == nil) {
NSLog(@"%s Failed to create a get event",
__PRETTY_FUNCTION__);
return NSZeroRect;
}
/* Get property */
prop = [NSAppleEventDescriptor
descriptorWithDescriptorType:typeProperty
bytes:&propName length:sizeof(propName)];
if(prop == nil) {
NSLog(@"%s Failed to create the bounds property",
__PRETTY_FUNCTION__);
return;
}
/* Set parameter */
[getEvent setParamDescriptor:prop forKeyword:keyDirectObject];
/* Exectue */
err = AESendMessage([getEvent aeDesc], &aeReply,
kAEWaitReply | kAENeverInteract, kAEDefaultTimeout);
if(err != noErr) {
NSLog(@"%s Failed to send the get message",
__PRETTY_FUNCTION__);
return;
}
reply = [[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&aeReply];
[reply autorelease];
NSLog(@"Bounds: %@", reply);
As explained, the above code works, just until the last block.
Thank you in advance for the help.
Thanks to Rob Keniger I’ve succeeded in what I wanted.
Apparently I had to create a record descriptor, set my wanted properties and, then coerce it to a
typeObjectSpecifier.Also, I was wrong in setting the window descriptor as a the receiver of my Apple Event. You always have to address the application itself, and set the
from(keyAEContainer) property of the direct object to the window you want.Here is the working code, with a little bit of
NSLog-statements:I hope this will help someone.