Rexx-inspired scripting & shell for CopperlineOS.
arexx-next is a tiny, embeddable scripting language and command-line tool for automating CopperlineOS via message ports. It revives the classic ARexx idea—every app has a port; everything is scriptable—with modern touches: JSON helpers, event subscriptions, and first-class Unix socket IPC.
TL;DR: write small scripts that talk directly to
copperd,compositord,blitterd, andaudiomixerd, orchestrating frame-accurate visuals and audio.
- Phase-0 MVP: interpreter + standard library for ports/JSON/env/files.
- Targets Linux-hosted CopperlineOS services (JSON protocol v0).
- License: MIT OR Apache-2.0.
CopperlineOS is about deterministic media and scriptable everything. You shouldn’t need Rust or C to:
- create a layer, bind an image, and animate it;
- build a quick audio graph; or
- connect multiple services into a scene.
ARexx on the Amiga did this brilliantly. arexx-next brings that spirit back, tuned for modern IPC.
arexx-next/
├─ src/ # interpreter & stdlib
├─ stdlib/ # reusable scripts/macros
├─ examples/ # runnable demos
├─ docs/ # language reference
└─ arx # CLI entrypoint (symlink or bin name)
The CLI binary is referred to as arx in examples below.
- Rexx-like syntax: dynamic variables,
SAYfor print,CALLfor functions,PROCEDUREfor local scope. - Comments:
/* block comment */(Rexx-style). - Strings:
"double-quoted"; concatenation via||. - Numbers: arbitrary precision integers; decimal floats.
- Errors: non‑zero
RC(return code) and exceptions abort script unless trapped.
SAY "Hello, Copperline!"name = ARG(1)
IF name = "" THEN name = "world"
CALL greet name
EXIT 0
greet: PROCEDURE
PARSE ARG who
SAY "Hello," who || "!"
RETURNRun: arx examples/hello.rexx Darren
- PORTSEND(sock, json) → string response (or empty for event streams).
- PORTSUB(sock, filter_json) → stream handle id (read via
PORTREAD(id)). - JSON(obj_or_text) → canonical JSON string;
JSONGET(text, key)→ value (simple paths). - ENV(name) → environment var; SLEEP(ms) → sleep milliseconds.
- FSREAD(path) / FSWRITE(path, text).
- TIMEUS() → microseconds since epoch (monotonic on supported hosts).
Note: JSON helpers are intentionally small—use tools like jq for heavy manipulation if needed.
By default, arexx-next maps well-known service names to sockets (override with env vars):
| Name | Socket | Env override |
|---|---|---|
copperd |
/run/copperline/copperd.sock |
COPPERD_SOCKET |
compositord |
/run/copperline/compositord.sock |
COMPOSITORD_SOCKET |
blitterd |
/run/copperline/blitterd.sock |
BLITTERD_SOCKET |
audiomixerd |
/run/copperline/audiomixerd.sock |
AUDIOMIXERD_SOCKET |
Helper: ADDRESS pseudo-statement addresses a port by name.
ADDRESS copperd '{ "cmd":"ping" }'Equivalent to: CALL PORTSEND "/run/copperline/copperd.sock", '{ "cmd":"ping" }'
This mirrors the SDK examples using copperd + compositord.
/* demo.rexx — move a sprite 4 px per frame */
compos = "/run/copperline/compositord.sock"
copper = "/run/copperline/copperd.sock"
/* 1) Create a layer and bind an image */
CALL PORTSEND compos, '{"cmd":"create_layer"}'
CALL PORTSEND compos, '{"cmd":"bind_image","id":1,"path":"/tmp/sprite.rgba","w":128,"h":128,"format":"RGBA8"}'
CALL PORTSEND compos, '{"cmd":"set","id":1,"x":100,"y":360,"alpha":1.0,"z":10,"visible":true}'
/* 2) Load a tiny Copper program */
prog = '{ "version":1, "program":[' ||
'{ "op":"MOVE","reg":"layer[1].x","value":100 },' ||
'{ "op":"MOVE","reg":"layer[1].y","value":360 },' ||
'{ "op":"LOOP","count":-1,"label":"loop" },' ||
'{ "label":"loop" },' ||
'{ "op":"WAIT","vsync":true },' ||
'{ "op":"ADD","reg":"layer[1].x","delta":4 },' ||
'{ "op":"IRQ","tag":"tick" },' ||
'{ "op":"JUMP","label":"loop" }] }'
resp = PORTSEND(copper, '{ "cmd":"load","program":' || prog || ' }')
id = JSONGET(resp, "id")
CALL PORTSEND copper, '{ "cmd":"start","id":' || id || ' }'
SAY "Animating layer 1; press Ctrl+C to stop."Run: arx demo.rexx
Listen to vsync and log frame times:
sock = "/run/copperline/compositord.sock"
sub = PORTSUB(sock, '{"cmd":"subscribe","events":["vsync"]}')
DO FOREVER
line = PORTREAD(sub)
IF line = "" THEN LEAVE
usec = JSONGET(line, "usec")
frame = JSONGET(line, "frame")
SAY "vsync: frame=" frame " usec=" usec
ENDarx FILE [args...] # run a script
arx -e 'SAY "hi"' # eval one-liner
arx -i # interactive REPL
arx --std path # add search path to stdlib
arx --trace # trace statements
arx --version
Scripts receive positional parameters via ARG(n). RC holds last return code.
The interpreter is a small C/Rust library (libarexx_next) you can link into apps/services to expose custom functions (e.g., to drive your own port).
- Register native functions (
rex_native("PORTSEND", c_fn_portsend)etc.). - Expose key-value environment/state to scripts.
- Bind to your service’s port for in-process automation.
(Embedding API stabilises after Phase-0.)
- By default, an unhandled error aborts the script with non-zero exit code.
- Use
SIGNAL ON ERRORandCALL ON ERROR(planned) to trap/handle errors. PORTSEND/PORTSUBreturn errors inRCand may set a globalLASTERRORstring.
Best practice: keep scripts idempotent and small; offload heavy loops to services via Copper programs or audio graphs.
- Scripts are local automation. There is no network transport in Phase-0.
- File access is unrestricted by default; for sandboxed setups, run
arxunder a restricted user or sandbox (bubblewrap, flatpak, systemd-run). - Services may require capability tokens; pass them via script variables or env (e.g.,
capsfield in requests).
- v0.1: ports/JSON stdlib, basic language, REPL, examples.
- v0.2: pattern-matching
PARSEhelpers, better JSON path (JSONPATH), timers (EVERY ms DO ... END). - v0.3: module system, package manager for stdlib macros.
- v0.4: debugger hooks (breakpoints, step), profiling for long scripts.
RFCs tracked in CopperlineOS/rfcs.
- Keep examples tiny and runnable on a stock Linux host with the Phase-0 services.
- Language changes require tests in
tests/and a short design note indocs/. - Please format code and scripts; see
CONTRIBUTING.mdandCODE_OF_CONDUCT.md.
Dual-licensed under Apache-2.0 OR MIT.
copperd– timeline enginecompositord– Vulkan/KMS compositorblitterd– 2D blitteraudiomixerd– audio graphsdk-rs·sdk-c