Previous Page
Next Page

3.1. Apple Events

AppleScript would be pointless without Apple events. Apple events lie at the heart of what AppleScript is, what it does, how it works, and why you're going to use it. From writing more efficient AppleScript code to understanding an application's dictionary, a basic acquaintance with Apple events will help you.

A long time ago, in a galaxy far awayactually it was probably in about 1989, in Cupertino, Californiasome very smart people were completely redesigning and modernizing the Macintosh operating system, creating what was to become System 7 (released in May 1991). And some of these people had a brilliant idea. The system, they decided, should support a messaging system, a way of letting one running application communicate with another. Such communication is called interapplication communication , and the messages sent between applications are called Apple events.

There are two parties to an interapplication communication: the application that initiates the message, and the application that receives it. I like to refer to these as the sender and the target , respectively; I find this clearer and more instructive than the more technical terms "client" and "server."

An Apple event is an astonishingly powerful thing. Hermes-like, it crosses borders: two completely independent applications, with no prior arrangement or synchronization, are suddenly talking to each other. What's more, Apple events work across a network, including the Internet, so these two applications can be on different computers. Or, just the opposite, an application can send an Apple event to itself. (Why would it want to do that? You'll find out, in the section "Recordability," later in this chapter.)

The breadth of what may be expressed in an Apple event is also quite amazing. Their structure amounts to a remarkably sophisticated grammar: Apple events are like little sentences, possessing (so to speak) verbs and nouns and modifiers, and this grammar is so cleverly and flexibly devised that individual Apple events can be constructed to say surprisingly complicated things, such as (speaking to a word processing program), "Look at the text of your first window and give me a reference to every line of it whose second word begins with the letter t," or (speaking to an email program), "Look in the mailbox where the incoming mail is, find the first mail message with a subject that starts with the word 'applescript,' and move it into the 'AppleScript' mailbox." Much of the grammar of the AppleScript language is as it is because of the grammar of Apple events.

3.1.1. Reply

Every interapplication communication (under normal circumstances) results in a reply from the target application. The reply is itself an Apple event.

This comes as something of a surprise to the naïve user. As human beings, we naturally tend to feel that there are, broadly speaking, two reasons for sending an interapplication communication: either we tell the target to do something or we ask the target a question. Therefore an interapplication communication can be thought of as either a command or a query . We expect a reply from a querythat's the purpose of the querybut not from a command. Under the hood, however, there is no real technical distinction here; either way, it's the same kind of message, and either way, there will be a reply.

What's the use of a reply from a command? Well, for one thing, even a command might result in some information useful to the sender. For example, the AppleScript make command creates a new entitya document or a word, for examplebut it also generates a reply, which is a reference to the newly created entity. That's useful because the usual reason for creating something is to do something with it, and it might be tricky to get a reference to the newly created entity otherwise.

There is also a solid technical reason why every interapplication communication generates a reply. Remember, these two applications are running independently, so they have to be coordinated somehow if they are to interact coherently. The sender, having sent a command to the target, typically doesn't want to proceed to its own next step until the target has finished obeying that command. The reply informs the sender that the command has been carried out (or, alternatively, that an error has occurred).

When two independently running applications communicate with each other, things can go wrong. The sender sends a message to the target, and then what? The target application might try to obey the message, and crashleaving the sender in a state of limbo, waiting for a reply that will never come. The target might obey the message, but require a great deal of time to do so. The target might be busy or otherwise not in a position to receive the message in the first place. The sender needs a way to hedge his bets in order to cope with such possibilities. Apple events provide some bet-hedging mechanisms.


Timeout value

The sender may attach to the message a timeout value, a statement of how long he is willing to wait for an answer. If a reply doesn't come back within the specified time, the sender receives a reply anywaya reply saying that, for one reason or another, no reply came back in time. Thus the sender has an opportunity to respond coherently to the situation. (Meanwhile the target is probably still performing his time-consuming task, blissfully unaware that the sender has lost interest.)


Ignore reply

The sender may signal up front that he won't be interested in the reply (presumably this is a command, not a query); he doesn't care to know what the reply is or whether there is one, or even whether the command was carried out. In this case the sender does not wait; the message is sent, and the sender immediately proceeds to the next step of his own process. The sender will never find out in any direct way what became of the Apple event. This devil-may-care approach is rather rarely used, but there are times when it comes in very handy.

3.1.2. Scriptability

Not just any old Apple event can be sent to any old application. Well, it can, but the result could easily be an error message instead of the desired result. The target application needs to have been constructed in the first place in such a way that the particular Apple event you send is one to which it is prepared to respond. Such an application defines internally a repertory of Apple events that it understands. The application is then said to be scriptable .

The thing to understand clearly here is that a scriptable application is scriptable just with respect to the particular repertory of Apple events that the application itself defines. To a remarkable degree, every scriptable application gets to make up its own repertory; one scriptable application's repertory of acceptable Apple events doesn't necessarily resemble that of any other scriptable application. This presents something of a problem for the sender, as every possible target application is picky in a different way about what can be said to it.

This problem washes over into AppleScript, and is in fact one of the single greatest challenges facing the AppleScript programmer. It would be an exaggeration to claim that the AppleScript language is different with respect to every scriptable applicationit's the vocabulary that changes, while the underlying language remains the samebut certainly a programmer trying to drive a particular target application for the first time often feels that all previous experience has suddenly been made irrelevant. (For a vivid account of a real-life user struggling with this challenge, see Appendix A.)

The knowledge of what Apple events a scriptable application can respond to, and what it will do in response to them, is an implicit fact built into its workings, not an explicit fact written somehow on its face. How, then, is it possible to know what a scriptable application's repertory is? Some secondary document is clearly needed to expose this information. In the AppleScript world, this document is the application's dictionary, a resource built in to the application for the specific purpose of describing its repertory. There is a section about dictionaries later in this chapter, and an entire chapter devoted to them later in the book (Chapter 20).

3.1.3. The Life of an Apple Event

There's obviously more to the story of interapplication communication than just the sender application and the target application. For example, earlier it was said that the sender normally receives a reply even if the target isn't even listening. How is this possible? It's possible because the system itself functions as the intermediary through which all interapplication communications happen. The sender doesn't speak directly to the target, but to the system. It is the system that is responsible for passing the message on to the target, and for letting the sender know how things went.

Figure 3-1 shows in more detail the process whereby an Apple event is sent and a reply is returned.

  1. The sender application (at the left of the figure) constructs the Apple event. The Apple event is rather like a letter inside an envelope that you post in the mail. It has information about how it is to be directedwho the target application is, and whether the sender intends to wait around for the reply, and if so, what the timeout value is. This information is intended for the system, and is rather like the stuff that goes on the outside of the envelope. Then there is the contentthe details as to what kind of Apple event this is and the particular data that it involves. This information is intended for the target application, and is rather like the letter inside the envelope.

    Figure 3-1. Life of an Apple event

  2. The sender application calls the system (in the middle of the figure) and hands it the Apple event . The system, rather like the postal service, examines the Apple event and looks at the information about how it is to be directed. Using this information, the system tries to locate the target application. Let's presume that it succeeds in doing this.

  3. The target application (at the right of the figure) is portrayed as having a repertory of Apple events to which it is prepared to respond. These Apple events are listed using pairs of four-letter codes. (Apple events really are identified by pairs of four-letter codes, and the Apple events shown in the diagram are genuine, common Apple events.)

  4. The system contacts the target application, handing it the Apple event supplied by the sender. The system also attaches to this Apple event a reply Apple event. It is rather as if, when the post office delivers a letter to you, it were to provide a stamped addressed envelope for you to put your reply into. The system holds out this reply event to the target application, but doesn't let go of it.

  5. The target application (presuming it is well behaved) responds to whatever the Apple event asks it to do, either doing it successfully or generating an error message; then it puts the result into the reply event. There are two parts to this result. First, the target application must supply a value signifying whether things succeeded. Second, the target application may insert into the reply any other information to be returned. If there was an error, it can insert a message describing the problem; if things succeeded and a result is expected, it can insert that result.

  6. The target application now signs off, and the system is left holding the reply Apple event (of which, as we said, it never let go). The system now delivers the reply Apple event to the sender application, and the story ends.

3.1.4. What an Apple Event Looks Like

An Apple event was never meant for human eyes. It is meant to be machine-constructible and machine-parsable. Nevertheless, it is possible to encode all the facts about an Apple event in a textual format. One commonly used format is called AEPrint . Let's examine an AEPrint representation of a real live Apple event, to get an idea of what a typical Apple event looks like. The Apple event we'll use is a rather elaborate one I mentioned earlier, an Apple event that means: "Look in the mailbox where the incoming mail is, find the first mail message with a subject that starts with the word 'applescript', and move it into the 'AppleScript' mailbox." Example 3-1 displays that Apple event in AEPrint format .

Example 3-1. A raw Apple event
    core\move{
        insh:insl{
            kobj:obj {
                form:'name',
                want:'Mbox',
                seld:"appleScript",
                from:'null'( )
            },
            kpos:'end '
        },
        ----:obj {
            form:'indx',
            want:'cobj',
            seld:1,
            from:obj {
                form:'test',
                want:'msg ',
                from:obj {
                    form:'prop',
                    want:'prop',
                    seld:'unBX',
                    from:'null'( )
                },
                seld:cmpd{
                    relo:'bgwt',
                    obj1:obj {
                        form:'prop',
                        want:'prop',
                        seld:'subj',
                        from:exmn($$)
                    },
                    obj2:"applescript"
                }
            }
        }
    }

I don't want to burden you with a full explanation of Example 3-1 (especially since the whole point of AppleScript is that you shouldn't have to worry about what a raw Apple event looks like), but a few characteristic points are of interest.

First, notice the predominance of four-letter codes. Nearly everything seems to consist of exactly four letters: core, move, insh, insl, kobj, form, want, seld, from, prop, kpos, indx, cobj, test, and so forth. Even some things that appear to be only three letters are actually four letters: obj and end and msg, for example, actually have a fourth character (a space).

These four-letter codes are actually integers. An integer is four bytes, while a character from the ASCII range is one byte, so an integer can express four "packed" characters. The expression of this integer as a four-letter string is simply a convenience for the human reader. The use of single quotes to delimit a four-letter code is a standard convention, and I'll use it in later parts of this book.


Second, observe the overall structure of the Apple event, which is actually quite simple. The command itself is specified in the first line: core\move, exactly as shown in Figure 3-1. Just about every Apple event has at least one parameter, known as the direct object ; this is symbolized by ---- (can you find it?), and this particular Apple event also has a second parameter, symbolized by insh.

Finally, you've probably already spotted the repeating pattern form, want, seld, from, which appears throughout the Apple event. This is how an Apple event specifies an object (such as "the 'AppleScript' mailbox"), something that it very commonly needs to do; we'll talk more about the AppleScript avatar of this pattern later on ("Element Specifiers" in Chapter 11).

3.1.5. Go and Catch an Apple Event

What I did to capture the Apple event displayed in Example 3-1 was to turn on the Apple Event Log in Script Debugger , which has an option to display Apple events sent from Script Debugger in AEPrint format. Even if you don't have Script Debugger, you can do something similar. In fact, you can do something even better: you can capture and view any Apple event as it flies through the system on its way from sender to target. The system, as we have seen, plays the central role of postman whenever an Apple event is sent. Now imagine that Apple events are secret messages, and that you are an international spy who would like to get a look at them when they are sent. In effect, you would like to waylay the postman, bonk him over the head, snatch the letter out of his hand, and glance at its contents. Well, you can.

First, open the Console; that's where certain Apple events are going to be reported to us. Next, go into the Terminal and enter the following (I'm assuming your shell is bash, the default in both Panther and Tiger):

% export AEDebugSends=1; export AEDebugReceives=1

These commands turn on the environment settings that cause Apple events to be intercepted and reported. These settings will apply only within this shell session, and only with respect to applications that are launched from within this process. So, let's launch one:

% open /Applications/Safari.app

Now any Apple events sent to Safari will be logged. Let's send one. Possibly you were unaware that the Unix open command is implemented in Mac OS X with Apple events; you're about to discover that it is. Say this:

% open http://www.apple.com

(I'm assuming here that Safari is your default browser.)

Within the Terminal, the process started by the open command sends an Apple event, and this fact is reported within the Terminal. Immediately afterwards, Safari receives this Apple event, and this fact is reported within the Console. The two Apple events are exactly the same event. As reported in the Console, when Safari receives it, it will look something like this:

AE2000 (556): Received an event:
------oo start of event oo------
{ 1 } 'aevt':  GURL/GURL {
          return id: 38666240 (0x24e0000)
     transaction id: 0 (0x0)
  interaction level: 112 (0x70)
     reply required: 0 (0x0)
  target:
    { 1 } 'psn ':  8 bytes {
      { 0x0, 0x3e0001 } (open)
    }
  optional attributes:
    < empty record >
  event data:
    { 1 } 'aevt':  - 1 items {
      key '----' -
        { 1 } 'TEXT':  20 bytes {
          "http://www.apple.com"
        }
    }
}
 
------oo  end of event  oo------

The Apple event is displayed in a slightly different text format from Example 3-1. I don't know the official name for this text format, so let's call it AEDebug format . AEDebug format is more verbose (and more informative) than AEPrint format, but the same basic elements are clearly present: this is a GURL\GURL event with just one parameter, namely the direct object designated by ---- (which turns out to be the actual URL that Safari was asked to open).

Another way to send an Apple event from the Terminal is to use AppleScript directly, by way of the osascript command (already mentioned under "Unix" in Chapter 2, and formally discussed in Chapter 25). In the Terminal, enter this:

% osascript -e 'tell app "Finder" to get disks'

This command causes the Terminal to spew out large amounts of information, most of it having to do with the mechanics of compiling and running AppleScript code, and culminating in the Apple event sent to the Finder, along with the Finder's reply. It should look something like this:

AE2000 (811): Sending an event:
------oo start of event oo------
{ 1 } 'aevt':  core/getd {
          return id: 53149700 (0x32b0004)
     transaction id: 0 (0x0)
  interaction level: 64 (0x40)
     reply required: 1 (0x1)
  target:
    { 2 } 'psn ':  8 bytes {
      { 0x0, 0xc0001 } (Finder)
    }
  optional attributes:
    { 1 } 'reco':  - 1 items {
      key 'csig' -
        { 1 } 'magn':  4 bytes {
          65536l (0x10000)
        }
    }
 
  event data:
    { 1 } 'aevt':  - 1 items {
      key '----' -
        { 1 } 'obj ':  - 4 items {
          key 'form' -
            { 1 } 'enum':  4 bytes {
              'indx'
            }
          key 'want' -
            { 1 } 'type':  4 bytes {
              'cdis'
            }
          key 'seld' -
            { 1 } 'abso':  4 bytes {
              'all '
            }
          key 'from' -
            { 4 } 'null':  null descriptor
        }
    }
}
 
------oo  end of event  oo------

Once more you can see the characteristic parts of an Apple event. This event is called core\getd; it has one parameter, the direct object designated by ----; and that direct object is an object specifier comprising the standard form, want, seld, and from.

3.1.6. What All This Has to Do with AppleScript

A raw Apple event in AEPrint or AEDebug format isn't impossible to read, but it isn't exactly easy either. Constructing one is even harder. Raw Apple events are meant primarily for computers, not for humans, to construct and to read. But now look at this:

move item 1 of (every message of incoming mail 
whose subject begins with "applescript")
to end of mailbox "appleScript"

That is the very same Apple event from Example 3-1, but this time it's expressed in an English-like form. It's quite legible, and you can probably imagine constructing something like this for yourself. I certainly hope you can imagine it, because that's what this book is all about. That code is AppleScript.

Now you understand why AppleScript exists. AppleScript is a programming language whose chief purpose is to allow Apple events to be constructed and presented in an English-like form that is fairly intuitive and accessible to a human being. Thanks to AppleScript, you can take advantage of the power of Apple events, constructing and sending them for yourself.


Previous Page
Next Page