https://gitlab.synchro.net/main/sbbs/-/commit/20100f171c1ac34728d57557
Modified Files:
src/doors/syncdoom/syncdoom.c src/doors/syncduke/syncduke.h syncduke_input.c syncduke_io.c src/doors/syncmoo1/syncmoo1.h syncmoo1_input.c syncmoo1_io.c src/doors/syncretro/syncretro.h syncretro_input.c syncretro_io.c test_axis.c src/doors/termgfx/sixel.c sixel.h term.c term.h src/doors/termgfx/test/test_term_sdm.c
Log Message:
doors: measure the mode-80 sense rather than infer it, where a version can't
b3caee6dd4 (2026-08-04) picks the DECSDM sequence that means
sixel-at-cursor from the peer's CTerm revision. That only answers for
CTerm. The VT340 manual documents mode 80 backwards, so a terminal built
from those specs reads the two sequences the way CTerm <= 1.327 does and
says nothing about itself in its DA1 reply -- and the doors were assuming
?80l for every such client.
There is a way to tell without asking. A terminal drawing at the cursor
leaves it at the bottom of the image; one anchoring at the screen origin
leaves it untouched. That difference is visible over the wire where the
mode's polarity is not. The technique is hackerb9's testdecsdm.sh (github.com/hackerb9/vt340test), pointed out by Deuce.
termgfx_sixel_vscale_probe() already measured cursor advance across two slivers, to decide whether the terminal honours the raster pan -- and had
to assume a mode to do it. It now runs that measurement TWICE, once under
each sequence, and reports six cursor rows instead of three. The half that moved the cursor is the half that drew at it: that answers the polarity,
and its own two advances still answer the pan question. One write, one
round trip, no probe that any door did not already send.
termgfx_term_sixel_at_cursor_probed() puts the measurement ahead of the revision. The two never both apply in practice -- a client that reports a revision is not probed -- so this is precedence for the code's sake rather
than a contested case.
SyncRetro had no such probe to extend, and gains one: emitted before the
first frame, with a collector in its CSI 'R' path that claims the six
reports ahead of the DSR pace-ack handler. Frames wait for the answer, so nothing is ever painted at the wrong anchor.
The termgfx_termio doors (SyncSCUMM, SyncRPG) stay on the revision alone.
The probe is itself a sixel, so its slivers necessarily precede the first frame, and that module's tests pin "the first DCS on the wire is the
frame" across five raster parsers and an image count. Correct on every
CTerm client and on every terminal reading DECSDM the modern way; wrong
only on a non-CTerm one built from the manual, for which no named example
is known. sixel.h records the reasoning.
A door that tracks the sequence it last sent must record what the probe
leaves behind (termgfx_sixel_probe_trailing_sdm), because the probe sets
the mode behind that bookkeeping: believing the older value, a door skips
the very re-assert the probe made necessary and spends the session in the
wrong mode. That is intermittent, not deterministic -- it needs the probe
to have run at all -- and cost a repeated-run test to see.
Verified against a fake terminal that models each polarity rather than
echoing canned replies: it tracks the mode in effect and advances its
cursor only for sixels drawn at one, so the reports are computed from the behaviour under test. All four doors settle on the right sequence for both polarities, with no CTerm version on offer.
Co-Authored-By: Claude Opus 5 (1M context) <
noreply@anthropic.com>
---
þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net