Consume libakgl 0.3.0: every workaround deleted, two capabilities gained

0.3.0 closed all ten API gaps this port had filed. The four workarounds go
with them: the CMake block that declared libakgl's vendored dependencies by
hand, the akgl/actor.h include in three files, and the six vtable pointers
assigned by hand in two more, now akgl_render_bind2d().

Two gaps were capabilities rather than inconveniences, and both are now real:

The line editor takes the composed UTF-8 text the ring carries in preference
to the keycode, so shifted characters, keyboard layouts, compose keys and dead
keys all work. A double quote can be typed, which means a BASIC string literal
can be typed -- the sharp end of the old limitation. Letters are no longer
folded to upper case.

SOUND's dir/min/step reach akgl_audio_sweep instead of being refused. dir 3
sweeps once rather than oscillating and TODO.md section 5 says so. A backend
with no sweep still refuses the swept note and plays the held one.

The adaptors now carry an AKGL_VERSION_AT_LEAST(0, 3, 0) floor, verified by
temporarily demanding 0.4.0 and watching it fire.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-31 13:25:14 -04:00
parent 583c0abbd2
commit fca9ad4a89
16 changed files with 495 additions and 265 deletions

View File

@@ -30,6 +30,22 @@
#include <akerror.h>
#include <akgl/renderer.h>
#include <akgl/version.h>
/*
* Everything in this target now depends on APIs that arrived in libakgl 0.3.0:
* akgl_render_bind2d(), akgl_audio_sweep(), and akgl_Keystroke with
* akgl_controller_poll_keystroke(). Refused here rather than at link time,
* because a missing symbol names a function and this names the release.
*
* The soname carries MAJOR.MINOR while the major is 0, so 0.2 and 0.3 are
* different ABIs and a mismatch is normally caught at load. This catches the
* case the soname cannot: a build against 0.2 headers that happens to find a
* 0.3 library, or the reverse.
*/
#if !AKGL_VERSION_AT_LEAST(0, 3, 0)
#error "akbasic's libakgl adaptors require libakgl 0.3.0 or later"
#endif
#include <akbasic/audio.h>
#include <akbasic/graphics.h>
@@ -164,16 +180,21 @@ akerr_ErrorContext AKERR_NOIGNORE *akbasic_sink_akgl_render(akbasic_TextSink *ob
* collects keystrokes from libakgl's ring -- the one the host's own event pump
* fills -- echoes them into the grid, and returns the line on Return.
*
* **What the editor cannot do, and why.** akgl_controller_poll_key() reports a
* keycode and nothing else: no modifier state and no composed text. So the
* editor cannot see Shift, which puts every shifted character out of reach and
* makes lowercase unreachable. Letters are folded to upper case, which is what a
* C128 does anyway and what the C64 font is drawn for. Filed upstream against
* libakgl; when the ring carries modifiers this becomes a real keyboard.
* **It is a real keyboard as of libakgl 0.3.0.** The ring carries the composed
* UTF-8 text SDL worked out from the keystroke, so shifted characters, a
* keyboard layout, a compose key and a dead key all do what the person typing
* expects -- including the double quote, without which a BASIC string literal
* cannot be typed at all. Until 0.3.0 the ring reported a keycode and nothing
* else, so none of that was reachable and letters were folded to upper case;
* that was libakgl API-gap item 10, filed from here.
*
* Composed text is taken over the keycode wherever there is any, which is what
* makes the layout work: on an AZERTY keyboard the key SDL calls `SDLK_Q`
* composes to "a", and "a" is what the program should see.
*
* Editing is Backspace to erase one character and Escape to abandon the line.
* There is no cursor movement within the line, for the same reason: the ring
* reports the arrow keys, but a script's own GET loop wants them.
* There is no cursor movement within the line: the ring reports the arrow keys,
* but a script's own GET loop wants them.
*
* @param obj The sink to give an editor to.
* @param pump One frame of the host's loop, or NULL to take the editor away.

View File

@@ -67,6 +67,16 @@ typedef struct akbasic_AudioBackend
/** Start a note on a voice, for a bounded duration. */
akerr_ErrorContext AKERR_NOIGNORE *(*tone)(struct akbasic_AudioBackend *self, int voice, double hz, int ms);
/**
* Start a note whose pitch moves, which is what SOUND's `dir`, `min` and
* `step` arguments ask for -- a siren, a laser, a falling bomb.
*
* May be NULL, and a host that leaves it so gets `SOUND` refused with
* AKBASIC_ERR_DEVICE for a swept note and served normally for a held one.
* That is deliberate: this arrived in libakgl 0.3.0, and a backend written
* against an older one is still a valid backend for everything else.
*/
akerr_ErrorContext AKERR_NOIGNORE *(*sweep)(struct akbasic_AudioBackend *self, int voice, double from_hz, double to_hz, double step_hz, int ms);
/** Silence a voice immediately. */
akerr_ErrorContext AKERR_NOIGNORE *(*stop)(struct akbasic_AudioBackend *self, int voice);
/** Select the waveform a voice synthesises. */