Start SDL text input: the AKGL build's keyboard was dead

SDL3 has text input off by default and per-window, so without
SDL_StartTextInput() it emits no SDL_EVENT_TEXT_INPUT at all and every
keystroke reaches libakgl's ring with an empty text field. The editor had just
been changed to prefer that composed text over the keycode, so it read every
key as not-a-character: no echo in the window, and nothing on stdout either,
because RUN could never be typed. One cause, both symptoms.

The editor now falls back to the keycode when there is no composed text, so a
host that forgets to start text input gets a worse keyboard rather than none.

The suite missed this because every keyboard test pushes the text-input event
into SDL's queue by hand -- which is what a real keyboard produces, but only
once text input has been started. Synthesising the end of a chain cannot test
the beginning of it. The new test asserts SDL_TextInputActive() directly, and
both halves were checked by reverting each and watching it fail.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-31 14:23:07 -04:00
parent fca9ad4a89
commit 024a33dbba
4 changed files with 115 additions and 2 deletions

View File

@@ -402,6 +402,62 @@ static akerr_ErrorContext AKERR_NOIGNORE *test_line_editor(void)
SUCCEED_RETURN(errctx);
}
/**
* @brief The frontend starts SDL text input, and the editor survives it not being on.
*
* **This is the regression test for a bug the rest of this file could not catch.**
* Every other test here pushes SDL_EVENT_TEXT_INPUT into the queue by hand,
* which is what a real keyboard produces -- but only once text input has been
* *started*. SDL3 has it off by default and per-window, so a host that never
* calls SDL_StartTextInput() gets no such events at all, every keystroke reaches
* the ring with an empty `text`, and an editor that treats that as "not a
* character" is silently dead. That is exactly what shipped: no echo in the
* window, and nothing on stdout either, because RUN could never be typed.
*
* Two assertions, because there are two independent things wrong with that:
* the frontend must turn text input on, and the editor must not be helpless if
* it is ever off.
*/
static akerr_ErrorContext AKERR_NOIGNORE *test_text_input_is_started(void)
{
PREPARE_ERROR(errctx);
char line[64];
bool eof = true;
PASS(errctx, start_frontend(NULL));
/* The frontend's job: SDL will not emit a text-input event without this. */
TEST_REQUIRE(SDL_TextInputActive(FRONTEND.window),
"the frontend must start SDL text input, or nothing can be typed");
/*
* The editor's job: keystrokes carrying *no* composed text still type,
* folded to upper case, which is all a bare keycode can offer. push_key()
* sends the key-down alone, so this is precisely the shape the broken build
* saw for every single key.
*/
PASS(errctx, FRONTEND.input.flush_keys(&FRONTEND.input));
push_key('r');
push_key('u');
push_key('n');
push_key('\r');
PASS(errctx, FRONTEND.akglsink.readline(&FRONTEND.akglsink, line, sizeof(line), &eof));
TEST_REQUIRE(!eof, "a text-less line is still a line");
TEST_REQUIRE_STR(line, "RUN");
TEST_REQUIRE_STR(FRONTEND.akglstate.text[0], "RUN");
/* And a text-less key that is not printable is still not a character. */
push_key(SDLK_F1);
push_key(SDLK_RIGHT);
push_key('a');
push_key('\r');
PASS(errctx, FRONTEND.akglsink.readline(&FRONTEND.akglsink, line, sizeof(line), &eof));
TEST_REQUIRE_STR(line, "A");
stop_frontend();
SUCCEED_RETURN(errctx);
}
/**
* @brief A whole REPL session typed at the window, ending with QUIT.
*
@@ -478,6 +534,7 @@ int main(void)
CATCH(errctx, test_pump_feeds_input());
CATCH(errctx, test_window_close_stops());
CATCH(errctx, test_line_editor());
CATCH(errctx, test_text_input_is_started());
CATCH(errctx, test_repl_session());
} CLEANUP {
stop_frontend();