DDirectorWikiDirector & Lingo Encyclopedia

The Message Hierarchy

How Director routes event messages through primary handlers, sprite behaviors, cast member scripts, frame scripts, and movie scripts - with pass, stopEvent, sendSprite, and sendAllSprites.

The core rule

An event becomes a same-named message offered to script locations in a fixed order. The first location that handles it consumes it; lower locations never see it unless the handler calls pass.

For a sprite-targeted event (e.g. mouseUp on a sprite):

text
1. primary event handler property   (the mouseUpScript, if set)
2. behaviors attached to the sprite (in attachment order, ALL of them)
3. the sprite's cast member script
4. the frame script (frame behavior)
5. movie scripts

For frame/movie events (enterFrame, exitFrame, idle...), routing starts at the sprite behaviors for sprite-cycle events or at the frame script for frame-level ones, ending at movie scripts. The exact chain varies by message; when a specific event matters, consult that event's entry in the API reference.

Not DOM bubbling

Director dispatch differs from every mainstream event model, and this is where emulators go wrong:

  1. Within the sprite location, all attached behaviors that define the handler run, in attachment order (not just the first). stopEvent() from one prevents the rest.
  2. Once a location has handled the message, propagation stops by default. There is no bubbling to "parent" sprites; sprites have no parents. The hierarchy is script-kind layers, not spatial containment.
  3. pass explicitly forwards to the next location and exits the current handler immediately (statements after pass never run).
  4. If no location has a matching handler, the message is simply ignored (and default behavior, like typing into an editable field, proceeds).

pass and stopEvent

lingo
on keyDown me
  if "0123456789" contains the key then
    pass                       -- let the editable field receive the digit
  else
    beep                       -- swallow everything else
  end if
end
lingo
on mouseUp me
  if pLocked then _movie.stopEvent()   -- kill it here; later behaviors and
end                                     -- lower locations see nothing
  • pass / pass(): forward to the next hierarchy location. Required in editable-field key handlers, or typing dies.
  • stopEvent(): stop the current event's propagation. Documented as meaningful in primary event handlers, handlers called from them, and multiple sprite scripts; no effect elsewhere.
  • dontPassEvent: the Director 5-era predecessor of stopEvent; still parses and appears in the native symbol tables (old code contains it).

Default behavior interacts with the hierarchy: for keys typed at an editable field, the insertion is the default outcome, which handling keyDown without pass suppresses.

Primary event handlers

Global first-chance interceptors, set as properties:

lingo
the mouseDownScript = "trackClick"    -- handler name (or script text)
the mouseUpScript   = ""
the keyDownScript   = "hotkeys"
the keyUpScript     = ""
the timeoutScript   = "onIdleTimeout" -- with the classic timeout mechanism

The named handler runs before everything. Unless it calls stopEvent() (or the legacy dontPassEvent), the message continues into the normal hierarchy; note this default is the OPPOSITE of normal locations (primary handlers pass by default; script locations consume by default).

Explicit messaging APIs

sendSprite

lingo
_movie.sendSprite(3, #reset, arg1)
sendSprite(sprite "hud", #reset)      -- name/object forms accepted

Delivers #reset to all behaviors of sprite 3; if unhandled there, the message continues down the hierarchy (cast member script, frame script, movie scripts). Returns TRUE if some behavior on the sprite had the handler, FALSE otherwise. Synchronous.

sendAllSprites

lingo
_movie.sendAllSprites(#pause)

Offers the message to every sprite in the current frame in channel order (each sprite's behavior chain), then the normal continuation. Slower and less targeted; returns FALSE when no sprite behavior handled it.

call and callAncestor

lingo
call(#reset, sprite(3).scriptInstanceList[1], arg)
call(#reset, listOfInstances, arg)
callAncestor(#reset, me, arg)

call invokes exactly the given instance(s) with no hierarchy continuation; the precision tool. callAncestor invokes the handler on me's ancestor (the super-call).

Direct handler calls

obj.handlerName(args) and handlerName(obj, args) call one object's handler directly (error if missing; use handler(obj, #name) or objectP guards). Movie-script handlers are callable as bare functions from anywhere: myUtility(5).

Dispatch pseudocode

A compatibility-safe dispatcher:

python
def dispatch(event, target_sprite, args):
    for location in hierarchy_for(event, target_sprite):
        handlers = handlers_matching(location, event)
        if not handlers:
            continue
        handled = False
        for h in handlers:                 # >1 only at the sprite-behaviors location
            ctx = run(h, args)             # ctx.passed / ctx.stopped set by pass/stopEvent
            handled = True
            if ctx.stopped:
                return STOPPED
            if ctx.passed:
                break                      # leave this location immediately
        if handled and not ctx.passed:
            return HANDLED
        # passed: fall through to next location
    return IGNORED                          # default behavior may proceed

The highest-risk details, worth explicit tests: behavior attachment order; stopEvent in the first of several sprite behaviors; pass from a sprite behavior reaching cast member then frame then movie script; primary handler pass-by-default; sendSprite continuation below the sprite.

Handler name resolution

  • Handler names are case-insensitive symbols.
  • A movie-script handler with an event name (on enterFrame in a movie script) acts as the movie-level location for that event every frame; a frame script defining it shadows the movie script for that frame (the message stops at the frame location).
  • Multiple movie scripts: all are searched in cast order; the first defining the handler wins (two movie scripts defining the same handler is an authoring error with deterministic first-wins behavior).
  • Custom messages (sendSprite(#anything)) use the same machinery as builtin events; there is no registry of "valid" events.