Skip to content

Commit a373436

Browse files
fix up old kinput subsystem
1 parent 9f6455e commit a373436

5 files changed

Lines changed: 139 additions & 110 deletions

File tree

include/basic/context.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "buddy_allocator.h"
1010
#include "audio.h"
1111
#include "data.h"
12+
#include <input.h>
1213

1314
typedef enum memory_model_t {
1415
mm_small = 25,
@@ -168,6 +169,11 @@ typedef struct basic_ctx {
168169
*/
169170
const char* error_handler;
170171

172+
/**
173+
* @brief Context for INPUT statement
174+
*/
175+
buffered_input_context_t input;
176+
171177
/**
172178
* @brief True if the program has "claimed flipping".
173179
*

include/input.h

Lines changed: 54 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,81 @@
11
/**
22
* @file input.h
33
* @author Craig Edwards (craigedwards@brainbox.cc)
4-
* @brief Console line input API for Retro Rocket.
5-
* @copyright Copyright (c) 2012-2026
4+
* @brief Simple line input API backed by a per-context buffer.
65
*
7-
* This interface provides buffered, line-based input for consoles. It handles
8-
* character input, backspace editing, line completion detection, and safe
9-
* buffer allocation. The functions in this header are backed by the
10-
* implementation in @ref input.c.
6+
* Provides minimal, line-based input suitable for BASIC `INPUT` and
7+
* similar use cases. Input is accumulated into a caller-owned context
8+
* and completed when a carriage return is received.
119
*
12-
* ## Usage Lifecycle
13-
* 1. Call `kinput(maxlen, cons)` repeatedly until it returns `1` (line complete).
14-
* 2. Retrieve the full line with `kgetinput(cons)`.
15-
* 3. Process the string as needed.
16-
* 4. Call `kfreeinput(cons)` when finished to release the internal buffer.
17-
*
18-
* The buffer is lazily allocated by `kinput()` when the first character arrives,
19-
* so you must not call `kgetinput()` until after at least one successful call to
20-
* `kinput()`.
10+
* ## Usage
11+
* 1. Initialise with `kinitinput(ctx)`.
12+
* 2. Call `kinput(ctx)` until it returns true (line complete).
13+
* 3. Read the line with `kgetinput(ctx)`.
14+
* 4. Call `kfreeinput(ctx)` when finished.
2115
*
2216
* ## Behaviour
23-
* - Input is null-terminated automatically.
24-
* - Carriage return (`'\r'`) signals end-of-line and is echoed as newline (`'\n'`).
25-
* - Backspace (`8`) removes the previous character (with console cursor handling).
26-
* - Arrow keys (↑ ↓ ← →) are recognised but ignored.
27-
* - If the buffer limit is reached, no more characters are added and a beep is
28-
* emitted as feedback.
29-
*
30-
* @note This input API is retained primarily to support the BASIC INPUT
31-
* statement and similar minimal line-reading requirements. Modern
32-
* interactive shells and tools use the ANSI console library, which
33-
* provides line editing, history, and richer input handling.
17+
* - Input is null-terminated.
18+
* - `'\r'` completes the line and is echoed as `'\n'`.
19+
* - Backspace (`8`) deletes the previous character with cursor update.
20+
* - Arrow keys are ignored.
21+
* - Buffer grows dynamically as needed.
3422
*
35-
* `kinput()` is intentionally simple and blocking; it does not support
36-
* advanced editing or multiple input sources.
23+
* @note This API is polled and intended for cooperative scheduling.
24+
* It performs no advanced line editing.
3725
*/
3826

3927
#pragma once
4028

29+
struct basic_ctx;
30+
4131
/**
42-
* @brief Poll for a single character of input from the console.
32+
* @brief Per-input context state.
4333
*
44-
* This function reads a single character from the console associated
45-
* with the specified process, storing it in the input buffer.
46-
*
47-
* When the user completes a line (e.g. by pressing Enter), the function
48-
* returns 1 to indicate the input line is ready for retrieval.
49-
* Until then, it returns 0, allowing the caller to yield or retry later.
34+
* Stores the buffer and cursor state for a single line input operation.
35+
*/
36+
typedef struct buffered_input_context_t {
37+
bool stopped; /**< Line completion flag */
38+
uint8_t last; /**< Last character read */
39+
char* internalbuffer; /**< Base pointer to allocated buffer */
40+
char* buffer; /**< Current write pointer */
41+
size_t bufcnt; /**< Number of characters in buffer */
42+
size_t buflen; /**< Allocated buffer size */
43+
} buffered_input_context_t;
44+
45+
/**
46+
* @brief Initialise an input context.
5047
*
51-
* @note This function is typically called repeatedly inside a loop or
52-
* idle callback, until a full line of input is received.
48+
* Clears all state. Must be called before first use.
5349
*
54-
* @param maxlen Maximum length of the input line.
55-
* @param cons Console to read input from.
56-
* @return 1 if a full line is available, 0 otherwise.
50+
* @param ctx Input context
5751
*/
58-
size_t kinput(size_t maxlen);
52+
void kinitinput(struct buffered_input_context_t* ctx);
5953

6054
/**
61-
* @brief Free the internal input buffer associated with a console.
55+
* @brief Poll for input and update the buffer.
56+
*
57+
* Reads a single character and updates the context buffer.
6258
*
63-
* This function releases the memory used to store a pending line of
64-
* input for the specified console. It should be called after input
65-
* has been processed, to prevent memory leaks and prepare the console
66-
* for the next `kinput()` cycle.
59+
* @param basic BASIC context
60+
* @param ctx Input context
61+
* @return true if a full line has been entered, false otherwise
62+
*/
63+
bool kinput(struct basic_ctx* basic, struct buffered_input_context_t* ctx);
64+
65+
/**
66+
* @brief Free any allocated input buffer and reset the context.
6767
*
68-
* @param cons Console whose input buffer should be freed.
68+
* @param basic BASIC context
69+
* @param ctx Input context
6970
*/
70-
void kfreeinput();
71+
void kfreeinput(struct basic_ctx* basic, struct buffered_input_context_t* ctx);
7172

7273
/**
73-
* @brief Retrieve the accumulated input string for a console.
74+
* @brief Get the current input buffer.
7475
*
75-
* @param cons Pointer to the console.
76-
* @return Pointer to the null-terminated input string, or NULL if no buffer exists.
76+
* @param ctx Input context
77+
* @return Pointer to null-terminated string, or NULL if not initialised
7778
*
78-
* @warning Do not free the returned pointer directly; use @ref kfreeinput().
79+
* @warning Do not free the returned pointer directly
7980
*/
80-
char* kgetinput();
81+
const char* kgetinput(struct buffered_input_context_t* ctx);

src/basic/console.c

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,21 +83,23 @@ int64_t basic_capslock(struct basic_ctx* ctx)
8383
/**
8484
* Check if input is complete yet
8585
* @param proc process
86+
* @param opaque BASIC context
8687
* @return true if input is still waiting for completion, false if done
8788
*/
8889
bool check_input_in_progress(process_t* proc, void* opaque)
8990
{
91+
basic_ctx* ctx = opaque;
9092
if (basic_esc()) {
9193
return false;
9294
}
93-
return kinput(MAX_STRINGLEN) == 0;
95+
return kinput(ctx, &ctx->input) == 0;
9496
}
9597

9698
/**
9799
* @brief Deferred cooperative input via idle process callback.
98100
*
99101
* When a BASIC `INPUT` statement is encountered, the interpreter checks whether
100-
* a complete line of user input is available via `kinput()`. If not, the process
102+
* a complete line of user input is available via `kinput(ctx, )`. If not, the process
101103
* voluntarily yields using `proc_set_idle()` and assigns a custom callback
102104
* (`check_input_in_progress`) to periodically test for input readiness.
103105
*
@@ -120,9 +122,9 @@ void input_statement(struct basic_ctx* ctx)
120122

121123
process_t* proc = ctx->proc;
122124

123-
/* Clear buffer */
124-
if (kinput(MAX_STRINGLEN) == 0) {
125-
proc_set_idle(proc, check_input_in_progress, NULL);
125+
/* Poll for pending input */
126+
if (kinput(ctx, &ctx->input) == 0) {
127+
proc_set_idle(proc, check_input_in_progress, ctx);
126128
jump_linenum(ctx->current_linenum, ctx);
127129
proc->state = PROC_IO_BOUND;
128130
return;
@@ -132,44 +134,44 @@ void input_statement(struct basic_ctx* ctx)
132134

133135
if (varname_is_int_array_access(ctx, var)) {
134136
int64_t index = arr_target_index(ctx);
135-
int64_t value = atoll(kgetinput(), 10);
137+
int64_t value = atoll(kgetinput(&ctx->input), 10);
136138

137139
if (index == -1) {
138140
basic_set_int_array(var, value, ctx);
139141
} else {
140142
basic_set_int_array_variable(var, index, value, ctx);
141143
}
142144

143-
kfreeinput();
145+
kfreeinput(ctx, &ctx->input);
144146
accept_or_return(NEWLINE, ctx);
145147
proc->state = PROC_RUNNING;
146148
return;
147149
} else if (varname_is_string_array_access(ctx, var)) {
148150
int64_t index = arr_target_index(ctx);
149-
const char* value = kgetinput();
151+
const char* value = kgetinput(&ctx->input);
150152

151153
if (index == -1) {
152154
basic_set_string_array(var, value, ctx);
153155
} else {
154156
basic_set_string_array_variable(var, index, value, ctx);
155157
}
156158

157-
kfreeinput();
159+
kfreeinput(ctx, &ctx->input);
158160
accept_or_return(NEWLINE, ctx);
159161
proc->state = PROC_RUNNING;
160162
return;
161163
} else if (varname_is_double_array_access(ctx, var)) {
162164
int64_t index = arr_target_index(ctx);
163165
double value = 0;
164-
atof(kgetinput(), &value);
166+
atof(kgetinput(&ctx->input), &value);
165167

166168
if (index == -1) {
167169
basic_set_double_array(var, value, ctx);
168170
} else {
169171
basic_set_double_array_variable(var, index, value, ctx);
170172
}
171173

172-
kfreeinput();
174+
kfreeinput(ctx, &ctx->input);
173175
accept_or_return(NEWLINE, ctx);
174176
proc->state = PROC_RUNNING;
175177
return;
@@ -179,21 +181,21 @@ void input_statement(struct basic_ctx* ctx)
179181

180182
switch (var[var_length - 1]) {
181183
case '$':
182-
basic_set_string_variable(var, kgetinput(), ctx, false, false);
184+
basic_set_string_variable(var, kgetinput(&ctx->input), ctx, false, false);
183185
break;
184186

185187
case '#': {
186188
double f = 0;
187-
atof(kgetinput(), &f);
189+
atof(kgetinput(&ctx->input), &f);
188190
basic_set_double_variable(var, f, ctx, false, false);
189191
break;
190192
}
191193

192194
default:
193-
basic_set_int_variable(var, atoll(kgetinput(), 10), ctx, false, false);
195+
basic_set_int_variable(var, atoll(kgetinput(&ctx->input), 10), ctx, false, false);
194196
break;
195197
}
196-
kfreeinput();
198+
kfreeinput(ctx, &ctx->input);
197199
accept_or_return(NEWLINE, ctx);
198200
proc->state = PROC_RUNNING;
199201
}
@@ -647,9 +649,9 @@ char* printable_syntax(struct basic_ctx* ctx)
647649
return NULL;
648650
}
649651

650-
char* result = gc_strdup(ctx, out);
652+
const char* result = gc_strdup(ctx, out);
651653
buddy_free(ctx->allocator, out);
652-
return result;
654+
return (char*)result;
653655
}
654656

655657
void keymap_statement(struct basic_ctx* ctx)

src/basic/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ struct basic_ctx *basic_init(const char *program, uint32_t pid, const char *file
304304
memset(&ctx->last_packet, 0, sizeof(queued_udp_packet));
305305
memset(&ctx->audio_streams, 0, sizeof(ctx->audio_streams));
306306
memset(&ctx->envelopes, 0, sizeof(ctx->envelopes));
307+
kinitinput(&ctx->input);
307308
ctx->sounds = NULL;
308309
ctx->if_nest_level = 0;
309310
ctx->errored = false;
@@ -552,6 +553,7 @@ struct basic_ctx *basic_clone(struct basic_ctx *old) {
552553
ctx->sleep_until = old->sleep_until;
553554
ctx->match_ctx = NULL;
554555
ctx->proc = old->proc;
556+
kinitinput(&ctx->input);
555557
memset(&ctx->last_packet, 0, sizeof(queued_udp_packet));
556558
ctx->int_variables = old->int_variables;
557559
ctx->str_variables = old->str_variables;

0 commit comments

Comments
 (0)