Reacting to events, timers, and messages

Programming guides

An event tells your program something happened, such as a key being pressed. A handler, or event script, is the indented block that reacts to it. The header is its first line, such as when started():. The part after when is an event descriptor: it says what to react to. It belongs in that header, not in an ordinary function call.

Top-level means outside functions, loops, and other blocks. Pliro first runs the ordinary top-level instructions to prepare the program. This is setup, or initialization. Then it gives event scripts their turns. Ready scripts wait in a queue, like people waiting in line. Cooperative means a script lets another ready script take a turn at agreed points, such as a positive wait. See the event-loop example.

Event scripts are top-level when statements:

when started():
    say "Ready"
    broadcast("go")

when after(1):
    say "One second later"

when messageReceived("go"):
    say "Go!"

when keyPressed("space"):
    say "Jump!"

when pointerPressed("primary"):
    say "Pressed at", pointerX(), pointerY()

when pointerReleased("primary"):
    say "Released at", pointerX(), pointerY()

when pointerClicked("primary"):
    say "Clicked at", pointerX(), pointerY()

Timer and scheduling words

A literal is a value written directly in the code, such as 1 or "go". An event header that requires a literal cannot use a variable in its place. Case-sensitive means "go" and "Go" are different messages.

To arm a timer is to start its wait. A one-shot timer becomes ready once; it does not repeat. A deadline is the earliest time it may run. All after timers measure from the same moment after setup. Their monotonic clock measures elapsed time without jumping when the computer’s time-of-day setting changes. Equal deadlines keep their order in the source file. Becoming ready does not interrupt another script in the middle of an instruction.

An invocation is one run of a handler. It gets fresh local variables, its own names inside the block. Global variables, created at program level, are shared. Dispatch means delivering an event and queuing the matching work. To yield is to hand over a turn. A persistent handler keeps a suitable running program ready for future key or pointer input, even when it is doing nothing at that moment. Cancellation stops pending work from running.

The trigger after when must be exactly started(), after(seconds), messageReceived("message"), keyPressed("key"), pointerPressed("primary"), pointerReleased("primary"), or pointerClicked("primary"). after requires one direct finite Number literal from 0 through 10 seconds and starts its script exactly once. A positive value below one millisecond is represented as one millisecond. Message and key descriptors require one non-empty text literal written directly in the header. Message matching is exact and case-sensitive. Key names use the same portable normalization as keyDown; a handler starts only when the key changes from released to pressed, and operating-system repeats are ignored until key-up. pointerPressed, pointerReleased, and pointerClicked accept only the exact direct literal "primary". A press runs when an eligible primary down occurs inside the logical scene and the host claims that pointer. It carries the immutable bounded whole-number down point. A real up from the same claimed pointer next runs a release handler, even outside the scene, using a signed 32-bit logical-plane point. If the up is inside, it also runs a separate click handler with the existing bounded release point. Additional or duplicate downs do not start another press or release. pointerX() and pointerY() report the immutable point owned by that pointer-event task. Event descriptors are valid only directly after a top-level when; they are not ordinary callable functions. Periodic timers, pointer move/drag, GPIO-edge, and sprite event descriptors are not implemented yet.

Ordinary top-level statements initialize shared globals before event scripts run. Started scripts are queued first. All after scripts are then armed once from one shared post-setup monotonic anchor; a zero-delay timer stays behind started work, and equal deadlines retain source order. Ready scripts use deterministic FIFO order, one script at a time. Every invocation has fresh local variables but may read and update shared globals. Inside a script, wait parks only that script and broadcast queues matching message scripts in source order before yielding. A run without persistent descriptors finishes after no ready or sleeping script remains. A keyPressed, pointerPressed, pointerReleased, or pointerClicked script keeps a capable run in state running while idle; Stop, exitApp, failure, or host closure ends it. A runtime without a required keyboard/pointer event capability fails with E4019.

Positive wait calls and completed input requests are cooperative checkpoints. They keep Stop and graphical event processing responsive and start a fresh instruction-budget slice. In an event script, wait parks only the active script. broadcast queues exactly matching message scripts and yields the active script so the deterministic FIFO scheduler can continue other ready work. Put a persistent animation loop in when started(): when other event handlers need to run during its waits; ordinary top-level setup must finish before the event-scheduler phase begins.

# language: en
let running = true

while running:
    if keyDown("space"):
        exitApp()

    # Update and draw one frame here.
    wait(0.04)

Syntax and values · Built-ins · Programming guides