Make GRAPHIC select the text plane
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m22s
akbasic CI Build / sanitizers (push) Failing after 4m43s
akbasic CI Build / coverage (push) Failing after 3m43s
akbasic CI Build / akgl_build (push) Failing after 4m48s
akbasic CI Build / mutation_test (push) Failing after 3m31s
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m22s
akbasic CI Build / sanitizers (push) Failing after 4m43s
akbasic CI Build / coverage (push) Failing after 3m43s
akbasic CI Build / akgl_build (push) Failing after 4m48s
akbasic CI Build / mutation_test (push) Failing after 3m31s
Co-authored-by: Andrew Kesterson <andrew@aklabs.net>
This commit is contained in:
@@ -155,24 +155,24 @@ static akerr_ErrorContext *draw_line(akbasic_Runtime *obj, double x1, double y1,
|
||||
akerr_ErrorContext *akbasic_cmd_graphic(akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value *lval, akbasic_Value *rval, akbasic_Value **dest)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
double args[2];
|
||||
double args[3];
|
||||
int count = 0;
|
||||
int mode = 0;
|
||||
int split = -1;
|
||||
akbasic_Color background;
|
||||
|
||||
(void)lval; (void)rval;
|
||||
PASS(errctx, require_graphics(obj, "GRAPHIC"));
|
||||
PASS(errctx, akbasic_args_numbers(obj, expr, "GRAPHIC", args, 2, &count));
|
||||
PASS(errctx, akbasic_args_numbers(obj, expr, "GRAPHIC", args, 3, &count));
|
||||
FAIL_ZERO_RETURN(errctx, (count >= 1), AKBASIC_ERR_SYNTAX, "GRAPHIC expected a mode");
|
||||
|
||||
mode = (int)args[0];
|
||||
/*
|
||||
* BASIC 7.0 numbers five modes plus a CLR. They differ in bitmap resolution
|
||||
* and in whether the bottom of the screen stays text, neither of which means
|
||||
* anything against a host's renderer -- so the mode is recorded and only its
|
||||
* one observable consequence is honoured: mode 0 is text, and text mode does
|
||||
* not draw. Refusing an out-of-range mode still matters, because that is a
|
||||
* typo the program author can fix.
|
||||
* and in whether the bottom of the screen stays text. Resolution belongs to
|
||||
* the graphics backend, while the latter is a text-sink decision: an AKGL
|
||||
* sink hides its text plane for a full bitmap and moves it below the split.
|
||||
* A stream has no plane, so it simply leaves that optional entry point NULL.
|
||||
*/
|
||||
FAIL_ZERO_RETURN(errctx, (mode >= 0 && mode <= 5), AKBASIC_ERR_BOUNDS,
|
||||
"GRAPHIC mode %d out of range (0 to 5)", mode);
|
||||
@@ -180,11 +180,22 @@ akerr_ErrorContext *akbasic_cmd_graphic(akbasic_Runtime *obj, akbasic_ASTLeaf *e
|
||||
/* GRAPHIC CLR: drop the saved shapes and go back to text. */
|
||||
PASS(errctx, obj->graphics->free_shapes(obj->graphics));
|
||||
PASS(errctx, akbasic_graphics_state_init(&obj->gfx));
|
||||
if ( obj->sink != NULL && obj->sink->graphic != NULL ) {
|
||||
PASS(errctx, obj->sink->graphic(obj->sink, 0, -1));
|
||||
}
|
||||
SUCCEED_TRUE(obj, dest);
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
if ( count >= 3 ) {
|
||||
split = (int)args[2];
|
||||
FAIL_ZERO_RETURN(errctx, (split >= 0 && split <= 25), AKBASIC_ERR_BOUNDS,
|
||||
"GRAPHIC split %d out of range (0 to 25)", split);
|
||||
}
|
||||
obj->gfx.mode = mode;
|
||||
if ( obj->sink != NULL && obj->sink->graphic != NULL ) {
|
||||
PASS(errctx, obj->sink->graphic(obj->sink, mode, split));
|
||||
}
|
||||
if ( count >= 2 && args[1] != 0.0 ) {
|
||||
PASS(errctx, akbasic_graphics_source_color(&obj->gfx, 0, &background));
|
||||
PASS(errctx, obj->graphics->clear(obj->graphics, background));
|
||||
|
||||
@@ -475,7 +475,7 @@ static akerr_ErrorContext *sink_window(akbasic_TextSink *self, int left, int top
|
||||
"The sink has no character grid to window");
|
||||
|
||||
maxcols = state->fullwidth / state->cellw;
|
||||
maxrows = state->fullheight / state->cellh;
|
||||
maxrows = (state->fullheight / state->cellh) - state->texttop;
|
||||
if ( left < 0 ) { left = 0; }
|
||||
if ( top < 0 ) { top = 0; }
|
||||
if ( right >= maxcols ) { right = maxcols - 1; }
|
||||
@@ -484,7 +484,7 @@ static akerr_ErrorContext *sink_window(akbasic_TextSink *self, int left, int top
|
||||
"WINDOW asks for no cells at all");
|
||||
|
||||
state->x = state->fullx + (left * state->cellw);
|
||||
state->y = state->fully + (top * state->cellh);
|
||||
state->y = state->fully + ((state->texttop + top) * state->cellh);
|
||||
state->columns = (right - left) + 1;
|
||||
state->rows = (bottom - top) + 1;
|
||||
state->width = state->columns * state->cellw;
|
||||
@@ -494,6 +494,78 @@ static akerr_ErrorContext *sink_window(akbasic_TextSink *self, int left, int top
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Apply GRAPHIC's text-plane half without teaching the runtime about SDL.
|
||||
*
|
||||
* Modes 1 and 3 are full bitmaps, so the retained text grid is hidden. Modes 2
|
||||
* and 4 have a bitmap above and text below. A C128 gives an omitted split its
|
||||
* bottom six rows; an explicit zero means all text, and the host clamps a C128
|
||||
* row number to however many measured cells its own window has.
|
||||
*/
|
||||
static akerr_ErrorContext *sink_graphic(akbasic_TextSink *self, int mode, int split)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
akbasic_AkglSink *state = NULL;
|
||||
int maxcols = 0;
|
||||
int maxrows = 0;
|
||||
|
||||
FAIL_ZERO_RETURN(errctx, (self != NULL), AKERR_NULLPOINTER, "NULL sink in graphic");
|
||||
state = (akbasic_AkglSink *)self->self;
|
||||
FAIL_ZERO_RETURN(errctx, (state != NULL), AKERR_NULLPOINTER, "akgl sink has no state");
|
||||
maxcols = state->fullwidth / state->cellw;
|
||||
maxrows = state->fullheight / state->cellh;
|
||||
|
||||
state->graphicmode = mode;
|
||||
if ( mode == 0 ) {
|
||||
/* Text mode owns the whole screen again, not the last split's rows. */
|
||||
state->texttop = 0;
|
||||
state->x = state->fullx;
|
||||
state->y = state->fully;
|
||||
state->width = state->fullwidth;
|
||||
state->height = state->fullheight;
|
||||
state->columns = maxcols;
|
||||
state->rows = maxrows;
|
||||
if ( state->columns > SINK_MAX_COLUMNS - 1 ) {
|
||||
state->columns = SINK_MAX_COLUMNS - 1;
|
||||
}
|
||||
if ( state->rows > SINK_MAX_ROWS ) {
|
||||
state->rows = SINK_MAX_ROWS;
|
||||
}
|
||||
state->cursorcol = 0;
|
||||
state->cursorrow = 0;
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
if ( mode != 2 && mode != 4 ) {
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
if ( split < 0 ) {
|
||||
split = maxrows - 6;
|
||||
}
|
||||
if ( split < 0 ) { split = 0; }
|
||||
if ( split >= maxrows ) {
|
||||
/* A split below the final row is C128 all-bitmap mode, not a zero-row grid. */
|
||||
state->graphicmode = 1;
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
state->texttop = split;
|
||||
state->x = state->fullx;
|
||||
state->y = state->fully + (split * state->cellh);
|
||||
state->width = state->fullwidth;
|
||||
state->height = (maxrows - split) * state->cellh;
|
||||
state->columns = maxcols;
|
||||
state->rows = maxrows - split;
|
||||
if ( state->columns > SINK_MAX_COLUMNS - 1 ) {
|
||||
state->columns = SINK_MAX_COLUMNS - 1;
|
||||
}
|
||||
if ( state->rows > SINK_MAX_ROWS ) {
|
||||
state->rows = SINK_MAX_ROWS;
|
||||
}
|
||||
state->cursorcol = 0;
|
||||
state->cursorrow = 0;
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
akerr_ErrorContext *akbasic_sink_init_akgl(akbasic_TextSink *obj, akbasic_AkglSink *state, akgl_RenderBackend *renderer, TTF_Font *font, int w, int h)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
@@ -554,6 +626,8 @@ akerr_ErrorContext *akbasic_sink_init_akgl(akbasic_TextSink *obj, akbasic_AkglSi
|
||||
state->cellh = cellh;
|
||||
state->columns = w / cellw;
|
||||
state->rows = h / cellh;
|
||||
state->graphicmode = 0;
|
||||
state->texttop = 0;
|
||||
if ( state->columns > SINK_MAX_COLUMNS - 1 ) {
|
||||
state->columns = SINK_MAX_COLUMNS - 1;
|
||||
}
|
||||
@@ -569,6 +643,7 @@ akerr_ErrorContext *akbasic_sink_init_akgl(akbasic_TextSink *obj, akbasic_AkglSi
|
||||
obj->moveto = sink_moveto;
|
||||
obj->window = sink_window;
|
||||
obj->grid = sink_grid;
|
||||
obj->graphic = sink_graphic;
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
@@ -584,6 +659,11 @@ akerr_ErrorContext *akbasic_sink_akgl_render(akbasic_TextSink *obj)
|
||||
FAIL_ZERO_RETURN(errctx, (state != NULL), AKERR_NULLPOINTER,
|
||||
"akgl sink has no state");
|
||||
|
||||
/* A full bitmap owns every display row; the text buffer remains intact. */
|
||||
if ( state->graphicmode == 1 || state->graphicmode == 3 ) {
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* **Repaint every row of the text area, every frame.** Not just the rows
|
||||
* that changed -- that was tried, with a `drawn[]` array marking which rows
|
||||
|
||||
@@ -109,5 +109,6 @@ akerr_ErrorContext *akbasic_sink_init_stdio(akbasic_TextSink *obj, akbasic_Stdio
|
||||
obj->moveto = NULL;
|
||||
obj->window = NULL;
|
||||
obj->grid = NULL;
|
||||
obj->graphic = NULL;
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
@@ -143,6 +143,22 @@ static akerr_ErrorContext *tee_window(akbasic_TextSink *self, int left, int top,
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
/** @brief Forward GRAPHIC's display-mode decision to the half with a display. */
|
||||
static akerr_ErrorContext *tee_graphic(akbasic_TextSink *self, int mode, int split)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
akbasic_TeeSink *state = NULL;
|
||||
|
||||
PASS(errctx, tee_state(self, &state));
|
||||
if ( state->primary != NULL && state->primary->graphic != NULL ) {
|
||||
PASS(errctx, state->primary->graphic(state->primary, mode, split));
|
||||
}
|
||||
if ( state->mirror != NULL && state->mirror->graphic != NULL ) {
|
||||
PASS(errctx, state->mirror->graphic(state->mirror, mode, split));
|
||||
}
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Report the grid of whichever half has one.
|
||||
*
|
||||
@@ -202,6 +218,7 @@ akerr_ErrorContext *akbasic_sink_init_tee(akbasic_TextSink *obj, akbasic_TeeSink
|
||||
obj->moveto = NULL;
|
||||
obj->window = NULL;
|
||||
obj->grid = NULL;
|
||||
obj->graphic = NULL;
|
||||
/*
|
||||
* Offered only when a half can actually do it, so CHAR's refusal against a
|
||||
* stdio-only driver still reads correctly through a tee.
|
||||
@@ -221,5 +238,9 @@ akerr_ErrorContext *akbasic_sink_init_tee(akbasic_TextSink *obj, akbasic_TeeSink
|
||||
(mirror != NULL && mirror->grid != NULL) ) {
|
||||
obj->grid = tee_grid;
|
||||
}
|
||||
if ( (primary != NULL && primary->graphic != NULL) ||
|
||||
(mirror != NULL && mirror->graphic != NULL) ) {
|
||||
obj->graphic = tee_graphic;
|
||||
}
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user