https://gitlab.synchro.net/main/sbbs/-/commit/637e9e5bb5a98c2bae8d2122
Added Files:
src/syncterm/scripts/connected.wren console.wren src/syncterm/wren/LICENSE README.md src/syncterm/wren/include/wren.h wren.hpp src/syncterm/wren/optional/wren_opt_meta.c wren_opt_meta.h wren_opt_meta.wren wren_opt_meta.wren.inc wren_opt_random.c wren_opt_random.h wren_opt_random.wren wren_opt_random.wren.inc src/syncterm/wren/vm/wren_common.h wren_compiler.c wren_compiler.h wren_core.c wren_core.h wren_core.wren wren_core.wren.inc wren_debug.c wren_debug.h wren_math.h wren_opcodes.h wren_primitive.c wren_primitive.h wren_utils.c wren_utils.h wren_value.c wren_value.h wren_vm.c wren_vm.h src/syncterm/wren_bind.c wren_embed_gen.c wren_host.c wren_host.h wren_host_internal.h
Modified Files:
src/build/Common.gmake src/conio/cg_events.m ciolib.h retro.c sdl_con.c win32cio.c wl_events.c x_events.c src/syncterm/CMakeLists.txt DarwinWrappers.m GNUmakefile conn.c syncterm.c syncterm.h term.c
Log Message:
SyncTERM: Wren scripting host
Embed the Wren VM as a per-connection scripting host. Each connection
spins up its own VM at start, runs scripts, and tears it down on
disconnect, so per-connection isolation falls out for free.
Scripts come from two sources:
* Embedded Ä shipped in the binary. scripts/*.wren is preprocessed
at build time by wren_embed_gen (a small C99 codegen tool, host-CC
compiled so cross-builds work) into a name+source table compiled
alongside wren_host.c. Two ship today: console.wren (the REPL)
and connected.wren (Alt+L send-login, extracted from term.c).
* User overrides Ä files placed in the platform's user-data scripts
directory (XDG \$XDG_DATA_HOME on Linux/BSD; Win32, macOS, and
Haiku branches in syncterm.c) override the embedded script of the
same module name, or add new ones.
Six dispatch points are wired into doterm() / conn_send():
keyboard, mouse, inbound bytes, outbound bytes, status text, and a
periodic timer. Hooks consume or pass through; consumed events skip
the rest of the pipeline. Owner thread is captured at init and
non-owner dispatches short-circuit to pass-through (background SFTP
and SSH writes call conn_send from worker threads).
Foreign classes exposed to scripts:
* Screen Ä global console ops (save/restore, attr, supports, font,
palette, cursor, videoFlags, color, hyperlinkId, window scope).
* Input Ä event-driven: next / next(ms) / poll return a KeyEvent or
MouseEvent foreign object; nextEvent parks the calling Wren fiber
and resumes it on the next event, giving scripts modal input
without busy-spinning. While a fiber is parked, the remote-byte
pump is gated off so server output can't paint over a dialog.
Key.* constants (escape, f1-f12, shiftTab, mouse, ...) come from
CIO_KEY_* in conio/ciolib.h; Esc-by-scancode is normalized to
0x001B in the KeyEvent ctor so scripts only ever see one Esc.
Input.unget(ev) routes by event type.
* Clipboard Ä text getter/setter.
* CTerm Ä read-only window into cterm state (cursor, origin,
margins, color, fonts, scrollback geometry, mode flags), with
ExtAttr / LastColumnFlag bitfield-snapshot classes and LogMode
/ StatusDisplay / Emulation / Codepage enum classes.
* Conn Ä pending/queued/peek/recv/send/sendRaw. send() routes
through conn_send so telnet IAC escaping and onOutput hooks
fire; sendRaw is the bypass. peek/recv clamp to the actual
inbuf size.
* BBS Ä read-only struct bbslist getters (~30 fields) plus
password / syspass and ConnType / FlowControl enum classes.
* Cell / Cells / Font / Hyperlinks Ä vmem_gettext / puttext
bindings. Cell wraps struct vmem_cell with separate
palette/rgb getters and setters that preserve bits 24-30 of
fg/bg; ch round-trips through CP437. Hyperlinks is Map-like
with [id], containsKey, add, params. Font has named built-in
indices plus runtime name(i) / available(i).
* Cache Ä opaque Directory + File pair with strict 1..64-char
[A-Za-z0-9._-] filename policy (rejects leading dot/dash,
trailing dot, "..", Windows reserved device names). File
covers open/close/read/write/delete/seek with deleted-handle
poisoning.
* Console Ä ring-buffered print/error log indexed by monotonic
sequence so incremental rendering survives ring eviction.
* Hook Ä registration API for the six dispatch events.
REPL:
* Ctrl+\` opens an immediate-mode REPL (console.wren). Compiles
user input via wrenCompileSource directly so vars persist across
submissions; runtime aborts caught with Fiber.try() and walked
for a real stack trace. Compile-time errors that only mean
"input wasn't an expression" are filtered through a side-log so
expression-form printing works without dumping the noise.
* Expression results print quoted with C-style escapes Ä "7" the
string and 7 the number are visibly distinct.
* Module-aware /in <module> moves the eval target. All embedded
scripts (and any user override matching an embedded basename)
share the "syncterm" module so console.wren can use Screen /
Input / Cell / etc. directly and the REPL can inspect anything
those scripts define. Pure user scripts keep file-per-module
isolation.
* /quit, /help, command history (Up/Down with prefix filter when
text was typed first), Ctrl+W backward-kill-word, middle-click
paste (LF-split, line-by-line submit), Alt+drag handoff to the
existing mousedrag select-and-copy.
Two pre-existing key-write bugs surfaced by adding the Ctrl+\`
keytable entry (CIO_KEY_WREN_CONSOLE = 0x29E0) and are fixed here:
both X11 and Wayland used "low byte non-zero -> 1 byte" as a
stand-in for "ASCII", which fails for synthetic keys with both
bytes set (low = 0xE0 marker, high = scancode). Aligned with
cg_cio.m / win32gdi.c / sdl_con.c: write 2 bytes whenever the
high byte is non-zero.
GNUmakefile uses per-pattern -UPREFIX to silence Wren's PREFIX
function-like macro colliding with SYNCTERM_PATH_PREFIX.
The Wren VM (wren/) is vendored verbatim from the upstream Wren
repository, MIT-licensed; LICENSE and README are checked in.
Co-Authored-By: Claude Opus 4.7 (1M context) <
noreply@anthropic.com>
---
þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net