Skip to content

Follow-ups to #198 and #199: BGI fonts and embedded-fonts, dotted music pauses, RIS and CSI = Ps M - #200

Merged
mkrueger merged 3 commits into
mkrueger:masterfrom
NuSkooler:pr/followups
Oct 28, 2026
Merged

mkrueger merged 3 commits into
mkrueger:masterfrom
NuSkooler:pr/followups

Conversation

@NuSkooler

Copy link
Copy Markdown
Contributor

These are three small fixes to #198 (embedder API) and #199 (CTerm conformance) that I found while moving enig-term onto master. Each is its own commit with its own test, and each test fails without its change.

cargo test -p icy_engine -p icy_parser_core passes, and so do the icy_engine lib tests with --no-default-features. With the feature off, 49 icy_engine integration tests that need the bundled fonts fail, the same 49 as on master. cargo fmt --check is clean, clippy has no new warnings in icy_engine, icy_parser_core or icy_engine_gui (with and without default features), and cargo check --workspace passes.

Let the BGI stroke fonts follow embedded-fonts

The BGI .CHR stroke fonts under palette_screen_buffer/bgi/fonts are not under data/fonts, so #198's embedded-fonts feature missed them: they were still compiled in with include_bytes!, about 125 KB, in a --no-default-features build.

They now go through font_bytes! like the other font data. With the feature off, Font::load(&[]) fails, so instead of unwrapping it the font list falls back to a new Font::empty(), which has no characters and draws nothing.

bgi_fonts_absent_do_not_panic (feature off only) resolves every FontType and checks it is an empty font. I also checked the build: a 48-byte sample from GOTH.CHR, SANS.CHR and BOLD.CHR is in libicy_engine.rlib before the change and gone after it.

Make a dotted ANSI music pause 1.5 times as long as a plain one

A dotted pause was stored as Pause(tempo * length * 2 / 3) and played for 240000 / that ms. The two integer divisions round differently from a dotted note's 360000 / (tempo * length), so at some tempos the pause and the note didn't match: T97 P1. lasted 3750 ms instead of 3711, and T121 P4. 745 ms instead of 743.

MusicAction::Pause now carries a dotted flag like PlayNote, and get_duration handles both the same way. This changes the shape of a public enum, so an embedder matching Pause(len) needs Pause(len, dotted) or Pause(..); the one match in icy_engine_gui is updated. If you'd rather keep Pause(i32) as it is, the other option I saw was storing the duration in ms, which I liked less.

test_dotted_pause_lasts_one_and_a_half_times_as_long checks the T97 and T121 values above, and then for every tempo (32 to 255) and length (1 to 64) that a pause and a note last 240000 / (tempo * length) ms, or 360000 / (tempo * length) dotted. Note durations are unchanged.

Restore the configured music option on RIS

RIS resets the music state that carries over between strings, but not the introducers CSI = Ps M picked. After CSI = 0 M, an ESC c left CSI M and CSI N off for the rest of the session.

AnsiParser now remembers the option set_music_option configured and returns to it on RIS. The three icy_engine call sites that assigned the music_option field directly now call set_music_option, so RIS returns to what they set. The field is still public; its doc points to the setter.

test_ris_restores_the_configured_music_option checks that RIS undoes CSI = 0 M, and that after set_music_option(Banana) and CSI = 2 M, RIS returns to Banana and CSI M is Delete Line again.

The .CHR fonts under palette_screen_buffer/bgi/fonts were compiled in
with include_bytes!, so a build without embedded-fonts still carried
about 125 KB of them. They now go through font_bytes! like the fonts
under data/fonts. A font that does not load is Font::empty(), which
draws nothing, instead of a panic on Font::load(&[]).unwrap().
A dotted pause was stored as Pause(tempo * length * 2 / 3) and played
for 240000 / that, so the two integer divisions rounded it differently
from a dotted note: T97 P1. lasted 3750 ms instead of 3711, and T121
P4. 745 ms instead of 743. Pause now carries the dotted flag like
PlayNote, and both last 240000 / (tempo * length) ms, or 360000 /
(tempo * length) dotted.
ESC c reset the music state that carries over between strings, but not
the introducers CSI = Ps M picked, so after CSI = 0 M an RIS left
CSI M and CSI N off for the rest of the session. AnsiParser now keeps
the option set_music_option configured and returns to it on RIS. The
icy_engine call sites that assigned the music_option field directly
use set_music_option instead.
@mkrueger
mkrueger merged commit 24e6a4b into mkrueger:master Oct 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants