https://gitlab.synchro.net/main/sbbs/-/commit/d0fd3b5d129f7a20f419edfb
Modified Files:
src/doors/syncconquer/door/door_io.c src/doors/syncdoom/syncdoom.c src/doors/syncduke/syncduke_io.c src/doors/syncmoo1/syncmoo1.h syncmoo1_geom.c syncmoo1_geom.h syncmoo1_input.c syncmoo1_io.c src/doors/syncmoo1/tests/test_geom.c src/doors/syncretro/syncretro.h syncretro_input.c syncretro_io.c src/doors/termgfx/geometry.c geometry.h termgfx_termio.c src/doors/termgfx/test/test_termgfx_termio_fitcanvas.c
Log Message:
doors: measure the terminal's canvas instead of assuming 640x400
Every graphical door starts from a 640x400 canvas and corrects it from the ESC[4;h;wt reply to ESC[14t. SyncTERM never sends that reply -- its CSI t is the CTerm-private palette setter -- so on SyncTERM the assumption stood, and every fit, centering, cell-size and pointer calculation was built on it.
It is only true in 80x25. SyncTERM's 80x43 canvas is 640x350 and its
132x43 is 1056x350, and the door had no idea. Four defects followed, each
found by a captured session rather than by reading the source:
- 80x43 emitted a 400-line frame into a 350-line canvas. SyncTERM 1.9
discards an oversized JXL blob WHOLE, so the screen stayed blank;
1.10 clips it, so the picture lost its bottom. The terminal had been
answering XTSMGRAPHICS with its real drawable area all along, but the
reply was filed as a sixel ceiling and never used as the canvas.
- The cell size was derived from the assumed canvas: 400/42 = 10 against
a real 8, which misplaced the sixel tier's CUP origin and every
dirty-rect box.
- The mouse mapping carried its own 8x16 guess. Converting a text cell
to canvas pixels with an over-tall cell walks off the bottom of the
image and clamps, so on an 80x43 terminal every row below about 21 of
42 mapped to the last game row -- the pointer drifting further out the
further down the screen it moved.
- 132-column modes are 1056 wide, i.e. LARGER than the assumption, where
clamping to the reported area does nothing at all. The picture was fit
and centered inside a phantom 640-wide canvas and sat against the left
edge with the right half of the screen empty.
So the reported area is now ADOPTED as the canvas when no ESC[14t answer arrived, and only clamped to when the canvas is genuinely known -- the
xterm case, where the raster ceiling is smaller than the window it
reports.
Separately, a door emits pixels but does not decide their SHAPE. SyncTERM scales its framebuffer to the video mode's display aspect
(vidmodes.c aspect_width/height, applied by bitmap_get_scaled_win_size_nomax
aspect_fix_wc), so a canvas pixel is 0.833 as wide as tall in the classic
640x400 and 0.442 in a 132-column 1056x350 -- more than twice as tall as square. Doors were calibrated on the 640x400 shape, where 320x200 mode-13h
art lands right because it was drawn for that same squeeze; everywhere else
the picture was stretched, 14% in 80x43 and 88% in 132x43.
termgfx_geom_par_correct() returns the factor to widen the source by so it keeps the shape it has on that reference canvas. It is exactly 1.0 for any 640x400-shaped canvas, so the mode every door was tuned in stays
bit-identical. It assumes a 4:3 display, which is exact for 48 of SyncTERM's
52 modes; the display aspect cancels out of the ratio, so only the current
and reference modes need share it. The four that differ cannot be told apart from their 4:3 twins by anything a door can query -- C80 and LCD80X25 are
both 80x25 with 8x16 cells on a 640x400 framebuffer, with pixel aspects of 0.833 and 1.000 -- so no door-side rule can separate them, and a terminal
that reported its pixel aspect would retire the assumption.
The correction is gated on the peer being SyncTERM: every other terminal
here draws its canvas with square pixels, where this would be the
distortion rather than the cure.
The two doors that CACHE their geometry needed one thing more. SyncTERM
sends its probe replies in one burst with the identification LAST, after
the canvas and grid replies that trigger a recompute -- so the fit was
already decided by the time the door knew who it was talking to, and the correction stayed gated off for the whole session. Both now re-fit when the identification lands. SyncRetro only appeared to escape this because sr_io_set_aspect() re-fits afterwards, and that early-returns when the
core's aspect is unchanged.
SyncMOO1 also gains the per-geometry-change log line SyncRetro already
prints. The canvas-vs-page-vs-cell chain is where every wrong-size bug in
these doors has lived, and it is what identified this one: canvas, grid,
cell and page were all correct and only the SyncTERM flag was wrong.
Live-verified on SyncTERM 1.9rc4 and 1.10a in 80x25, 80x43 and 132x43: SyncSCUMM, SyncRPG, SyncMOO1, SyncRetro, SyncDOOM. 80x25 with the status
line hidden is bit-identical; with it shown the image is 15px shorter and
no longer runs under the status bar, which is the same assumption failing
in the mode it was made for.
Co-Authored-By: Claude Opus 5 (1M context) <
noreply@anthropic.com>
---
þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net