SPIEL (Simple, Platform-Inclusive Event Lode) is a stream protocol for user input. It’s used to convey user input events between processes, possibly running on different hosts, possibly on different operating systems.

Each message consists of a length byte followed by zero or more data bytes (up to 255). A message also has a type code, which is either equal to the length (if less than 8) or specified in the first data byte.

Type 0: Null message

A single zero byte is the null message:

0x00  # null

Null messages carry no semantic meaning, but can be useful for session maintainence purposes.

Type 1: ASCII synthesis

The simplest non-null message is the ASCII synthesis event:

0x01 0x48  0x01 0x69  # This spells "Hi"

This event is intended for use with curses front ends, where there’s no concept of modifier keys or distinct down/up events. It’s defined for all graphical ASCII characters, with additional mappings from various other keys to certain control characters.

0x01 0x08  # Delete/Backspace
0x01 0x09  # Tab
0x01 0x0D  # Return
0x01 0x1B  # Esc
0x01 0x20  # Space
0x01 0x7F  # FwdDel

Note that the sequence 0x01 0x1B always codes for a press of the Escape key, and never part of an escape sequence. Receivers should process it immediately, as is. Any debouncing required (e.g. by curses front ends) will be done on the sender’s side.

(Message type 2 is reserved. Message types 3 and 4 are discussed further below.)

Type 5: Pointer location

Message type 5 indicates pointer location. This is an absolute position, not a relative motion (which will be addressed by a different message type). The payload consists of a one-byte device field, which can be zero when there’s only one pointing device. The remaining four bytes are two 16-bit integers (ordered big-endian), X and Y. X is distance to the right from the left edge; Y is distance down from the top. (Sender and receiver must agree on screen resolution; how to negotiate this is TBD.)

0x05 0x00 0x00 0x00 0x00 0x00  # Upper left
0x05 0x00 0x01 0xFF 0x01 0x55  # Lower right of 9" Macintosh screen

Senders should only send pointer location events when a pointer’s location has changed. Multiple pointers may be used simultaneously by assigning each one a unique device ID.

Type 3: Pointer action

Message type 3 is for pointer events (other than motion), i.e. clicking or tapping. The payload consists of three one-byte fields: modes, attributes, and device/button ID.

The following general modes are defined:

These modes are valid for both pointer and key events (described later). The rationale for this selection deserves its own document, but to summarize: Modes are behavior, not keys. This is why Alt and Option are separate modes, and there’s no left/right key distinction. I’ve never seen Meta on any keyboard manufactured in the last two decades, but there are applications using it as a modifier key nonetheless — so it needs to be supported. The benefit of baking it into the protocol is that it isolates the problem near the physical keyboard. You might map Fn-Control or right-Control to Meta, depending on whether you have a Fn key or a second Control key. Generating it in the first place is up to you, but once you do, SPIEL can carry it.

Pointer action events don’t include location information; precede them with a pointer location event to provide it.

The only pointer attribute currently defined is a two-bit action code: 1 for down, 2 for up, and 0 for an atomic button press, i.e. a combined down/up action. (This can be convenient if you’re injecting simulated input instead of actually tracking a human using a pointing device.)

The button subfield should be zero if the sender doesn’t distinguish mouse buttons. Otherwise, the primary button has an ID of 1 and the secondary button has ID 2, regardless of which one is on the left or the right. Note that a Control-click(0) could be interpreted as the equivalent of a secondary click (a convention used in Mac OS 8 and 9), but Control-click(1) should never be. (Also, these issues are best resolved near the input device.)

0x03 0x00 0x00 0x00  # Simple synthesized button press
0x03 0x00 0x00 0x02  # Synthesized secondary button press
0x03 0x02 0x01 0x00  # Shift-mouse-down
0x03 0x02 0x02 0x00  # Shift-mouse-up
0x03 0x1B 0x00 0x01  # Shift-Control-Option-Command-click (primary button)

Type 4: Key event

Message type 4 is for keyboard events. The payload consists of four one-byte fields: character code, modes, attributes, and device ID. The character and modes fields are identical to their counterparts in message types 1 and 3 respectively.

The key attributes field is similar to the pointer attributes field: The lower two bits contain an action code with the same semantics (0 for atomic press, 1 for key-down, 2 for key-up), but also defining a value of 3 as an auto-key event. Additionally, a key-only mode is defined: Bit 2 is set for Alpha mode (a.k.a. Caps mode). The remaining five bits are reserved.

The device field can be used to distinguish between multiple keyboards attached to the same computer (which not only is possible now with USB, but was possible in 1987 with ADB), potentially useful for a two-keyboards-one-screen scenario, or between a keyboard and a device pretending to be one, such as a barcode scanner.

0x04 0x08 0x10 0x00 0x00  # Send this for Ctrl-H, not 0x01 0x08
0x04 0x73 0x01 0x00 0x00  # Command-S

0x04 0x61 0x00 0x01 0x00  # A down (i.e. the key labeled 'A')
0x04 0x7A 0x00 0x01 0x00  # Z down — recognizable chord in effect
0x04 0x61 0x00 0x02 0x00  # A up
0x04 0x7A 0x00 0x02 0x00  # Z up

0x04 0x20 0x00 0x01 0x01  # Race to tap the Space bar first,
0x04 0x20 0x00 0x01 0x02  # using two keyboards

0x04 0x41 0x02 0x03 0x00  # AAAAAAAAAAAAAAAAAAAAAA (Shift)
0x04 0x41 0x00 0x07 0x00  # AAAAAAAAAAAAAAAAAAAAAA (Alpha)

The auto-key action mode exists for a reason: The semantics of an auto-key event versus a distinct keypress is best determined by the receiving application. But the desired timing depends on human factors and therefore should be determined close to the keyboard. In addition, synthesizing auto-key events at the receiving end ould be subject to latency — if a key-up event got delayed, the receiver would continue generating auto-key events.


SPLode is a library implementing SPIEL.

MacRelix's implementation of FORGE includes an eventtap view, which captures user input and serves it via the SPIEL protocol through the file "stream".

v68k-macos' implementation of the Mac OS Event Manager reads SPIEL data from a host-provided file descriptor and converts them into EventRecord structures.