Let a caller take keystrokes without owning the event loop
akbasic's GET and GETKEY ask "is there a keystroke waiting, yes or no" and must not require the interpreter to pump SDL events itself. akgl_controller_poll_key drains one key per call from a fixed 32-entry ring that akgl_controller_handle_event fills, so the host keeps pumping and the embedded interpreter reads at its own pace. akgl_controller_flush_keys discards a backlog. The recording happens before the control-map scan, not after: that scan returns as soon as a binding claims the event, so recording afterwards would have dropped exactly the keys a game also acts on. A full buffer refuses the newest keystroke rather than overwriting the oldest, so a caller reading a line gets what was typed first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,39 @@
|
||||
|
||||
akgl_ControlMap GAME_ControlMaps[AKGL_MAX_CONTROL_MAPS];
|
||||
|
||||
/*
|
||||
* Keystrokes waiting for akgl_controller_poll_key(). Filled by
|
||||
* akgl_controller_handle_event() before it consults the control maps, so a key
|
||||
* that also drives an actor is still delivered to a polling caller.
|
||||
*
|
||||
* head is the next slot to read, count is how many are waiting. Both index a
|
||||
* fixed array rather than a queue object, which is the whole point: a host that
|
||||
* never polls cannot make this grow.
|
||||
*/
|
||||
static SDL_Keycode keybuffer[AKGL_CONTROLLER_KEY_BUFFER];
|
||||
static int keybuffer_head = 0;
|
||||
static int keybuffer_count = 0;
|
||||
|
||||
/**
|
||||
* @brief Record one keystroke, discarding it if the buffer is already full.
|
||||
*
|
||||
* Dropping the newest rather than overwriting the oldest is deliberate. A
|
||||
* caller reading a line of input wants the characters that were typed first;
|
||||
* overwriting would hand it the tail of what the user typed and silently lose
|
||||
* the head.
|
||||
*/
|
||||
static void keybuffer_push(SDL_Keycode key)
|
||||
{
|
||||
int tail = 0;
|
||||
|
||||
if ( keybuffer_count >= AKGL_CONTROLLER_KEY_BUFFER ) {
|
||||
return;
|
||||
}
|
||||
tail = (keybuffer_head + keybuffer_count) % AKGL_CONTROLLER_KEY_BUFFER;
|
||||
keybuffer[tail] = key;
|
||||
keybuffer_count += 1;
|
||||
}
|
||||
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_list_keyboards(void)
|
||||
{
|
||||
int count;
|
||||
@@ -65,6 +98,13 @@ akerr_ErrorContext *akgl_controller_handle_event(void *appstate, SDL_Event *even
|
||||
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
|
||||
FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
|
||||
|
||||
// Before the control maps, not after: a key bound to an actor is still a
|
||||
// key an interpreter polling with akgl_controller_poll_key() wants to see,
|
||||
// and the scan below returns as soon as a binding claims the event.
|
||||
if ( event->type == SDL_EVENT_KEY_DOWN ) {
|
||||
keybuffer_push(event->key.key);
|
||||
}
|
||||
|
||||
ATTEMPT {
|
||||
for ( i = 0 ; i < AKGL_MAX_CONTROL_MAPS; i++ ) {
|
||||
curmap = &GAME_ControlMaps[i];
|
||||
@@ -386,3 +426,32 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_default(int controlmapid, cha
|
||||
} PROCESS(errctx) {
|
||||
} FINISH(errctx, true);
|
||||
}
|
||||
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_poll_key(int *keycode, bool *available)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
FAIL_ZERO_RETURN(errctx, keycode, AKERR_NULLPOINTER, "NULL keycode destination");
|
||||
FAIL_ZERO_RETURN(errctx, available, AKERR_NULLPOINTER, "NULL availability destination");
|
||||
|
||||
if ( keybuffer_count == 0 ) {
|
||||
// An empty buffer is the ordinary case, not a failure: the caller is
|
||||
// asking whether a key is waiting, and the answer is no.
|
||||
*keycode = 0;
|
||||
*available = false;
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
*keycode = (int)keybuffer[keybuffer_head];
|
||||
*available = true;
|
||||
keybuffer_head = (keybuffer_head + 1) % AKGL_CONTROLLER_KEY_BUFFER;
|
||||
keybuffer_count -= 1;
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_flush_keys(void)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
keybuffer_head = 0;
|
||||
keybuffer_count = 0;
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user