From f4fdf289941a9ee4749446e7014393e0b7f44cc2 Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Mon, 18 May 2026 10:44:41 -0700 Subject: [PATCH] rename 'menu' demo to 'bfr' build out more render helpers --- build_bfr.sh | 9 ++ build_menu.sh | 9 -- project.4coder | 6 +- src/bfr.main.c | 242 ++++++++++++++++++++++++++++++++ src/{menu.main.h => bfr.main.h} | 14 +- src/menu.main.c | 168 ---------------------- 6 files changed, 264 insertions(+), 184 deletions(-) create mode 100755 build_bfr.sh delete mode 100755 build_menu.sh create mode 100644 src/bfr.main.c rename src/{menu.main.h => bfr.main.h} (78%) delete mode 100644 src/menu.main.c diff --git a/build_bfr.sh b/build_bfr.sh new file mode 100755 index 0000000..7e5a0a9 --- /dev/null +++ b/build_bfr.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +code="$PWD" +opts="-g -Wno-switch -I$code/src/mr4th/dependencies" + +mkdir -p build +cd build +echo bfr +clang $opts $code/src/bfr.main.c -lncurses -o bfr diff --git a/build_menu.sh b/build_menu.sh deleted file mode 100755 index 093e8a4..0000000 --- a/build_menu.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -code="$PWD" -opts="-g -Wno-switch" - -mkdir -p build -cd build -echo menu -clang $opts $code/src/menu.main.c -lncurses -o menu diff --git a/project.4coder b/project.4coder index 76546a2..2cddf44 100644 --- a/project.4coder +++ b/project.4coder @@ -34,8 +34,8 @@ commands = { .build_resize = { .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .win = "build_resize.bat", .linux = "./build_resize.sh", .mac = "./build_resize.sh", }, - .build_menu = { .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, - .win = "build_menu.bat", .linux = "./build_menu.sh", .mac = "./build_menu.sh", }, + .build_bfr = { .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, + .win = "build_bfr.bat", .linux = "./build_bfr.sh", .mac = "./build_bfr.sh", }, }; fkey_command = { @@ -43,5 +43,5 @@ fkey_command = { .F2 = "build_animate", .F3 = "build_colors", .F4 = "build_resize", -.F5 = "build_menu", +.F5 = "build_bfr", }; diff --git a/src/bfr.main.c b/src/bfr.main.c new file mode 100644 index 0000000..da4bf38 --- /dev/null +++ b/src/bfr.main.c @@ -0,0 +1,242 @@ +#include "mr4th/mr4th_base.h" +#include "mr4th/mr4th_stdio.h" +#include "mr4th/mr4th_prng.h" +#include + +#include "bfr.main.h" + +#include "mr4th/mr4th_base.c" +#include "mr4th/mr4th_stdio.c" +#include "mr4th/mr4th_prng.c" + +MR4TH_SYMBOL NCRS_Bfr +ncrs_bfr_alloc(Arena *arena, U32 w, U32 h){ + NCRS_Bfr result = { w, h }; + result.chr = push_array(arena, U8, w*h); + result.color = push_array(arena, U8, w*h); + result.clip.x1 = w; + result.clip.y1 = h; + return(result); +} + +MR4TH_SYMBOL void +ncrs_bfr_clip_clear(NCRS_Bfr *bfr){ + bfr->clip.x0 = 0; + bfr->clip.y0 = 0; + bfr->clip.x1 = bfr->w; + bfr->clip.y1 = bfr->h; +} + +MR4TH_SYMBOL void +ncrs_bfr_clip(NCRS_Bfr *bfr, RectS32 rect){ + bfr->clip.x0 = ClampBot(0, rect.x0); + bfr->clip.y0 = ClampBot(0, rect.y0); + bfr->clip.x1 = ClampTop(rect.x1, bfr->w); + bfr->clip.y1 = ClampTop(rect.y1, bfr->h); +} + +MR4TH_SYMBOL void +ncrs_bfr_fill_rect(NCRS_Bfr *bfr, RectS32 rect, U8 chr, U8 color){ + S32 x0 = ClampBot(rect.x0, bfr->clip.x0); + S32 x1 = ClampTop(rect.x1, bfr->clip.x1); + S32 y0 = ClampBot(rect.y0, bfr->clip.y0); + S32 y1 = ClampTop(rect.y1, bfr->clip.y1); + + for (S32 iy = y0; iy < y1; iy += 1){ + S32 iyb = iy*bfr->w; + for (S32 ix = x0; ix < x1; ix += 1){ + bfr->chr[iyb + ix] = chr; + bfr->color[iyb + ix] = color; + } + } +} + +MR4TH_SYMBOL void +ncrs_bfr_outline_rect(NCRS_Bfr *bfr, RectS32 rect, U8 chr, U8 color){ + S32 x0 = ClampBot(rect.x0, bfr->clip.x0); + S32 x1 = ClampTop(rect.x1, bfr->clip.x1); + S32 y0 = ClampBot(rect.y0, bfr->clip.y0); + S32 y1 = ClampTop(rect.y1, bfr->clip.y1); + + // top + if (bfr->clip.y0 <= rect.y0 && rect.y0 < bfr->clip.y1){ + S32 iyb = rect.y0*bfr->w; + for (S32 ix = x0; ix < x1; ix += 1){ + bfr->chr[iyb + ix] = chr; + bfr->color[iyb + ix] = color; + } + } + + // bottom + if (bfr->clip.y0 < rect.y1 && rect.y1 <= bfr->clip.y1){ + S32 iyb = (rect.y1 - 1)*bfr->w; + for (S32 ix = x0; ix < x1; ix += 1){ + bfr->chr[iyb + ix] = chr; + bfr->color[iyb + ix] = color; + } + } + + // left + if (bfr->clip.x0 <= rect.x0 && rect.x0 < bfr->clip.x1){ + S32 ix = rect.x0; + for (S32 iy = y0; iy < y1; iy += 1){ + S32 iyb = iy*bfr->w; + bfr->chr[iyb + ix] = chr; + bfr->color[iyb + ix] = color; + } + } + + // right + if (bfr->clip.x0 < rect.x1 && rect.x1 <= bfr->clip.x1){ + S32 ix = rect.x1 - 1; + for (S32 iy = y0; iy < y1; iy += 1){ + S32 iyb = iy*bfr->w; + bfr->chr[iyb + ix] = chr; + bfr->color[iyb + ix] = color; + } + } + +} + +MR4TH_SYMBOL void +ncrs_bfr_write(NCRS_Bfr *bfr, S32 x, S32 y, String8 str, U8 color){ + if (str.size > 0 && + bfr->clip.x0 < x + str.size && x < bfr->clip.x1 && + bfr->clip.y0 <= y && y < bfr->clip.y1){ + S32 iyb = y*bfr->w; + S32 x1 = ClampTop(x + str.size, bfr->clip.x1); + S32 xskip = ClampBot(0, bfr->clip.x0 - x); + for (U32 xi = x + xskip, i = xskip; xi < x1; xi += 1, i += 1){ + bfr->chr[iyb + xi] = str.str[i]; + bfr->color[iyb + xi] = color; + } + } +} + +MR4TH_SYMBOL void +ncrs_bfr_point(NCRS_Bfr *bfr, S32 x, S32 y, U8 chr, U8 color){ + if (bfr->clip.x0 <= x && x < bfr->clip.x1 && + bfr->clip.y0 <= y && y < bfr->clip.y1){ + bfr->chr[y*bfr->w + x] = chr; + bfr->color[y*bfr->w + x] = color; + } +} + +MR4TH_SYMBOL void +ncrs_bfr_to_window(NCRS_Bfr *bfr, WINDOW *window){ + S32 w = 0; + S32 h = 0; + getmaxyx(window, h, w); + + S32 x1 = ClampTop(bfr->w, w); + S32 y1 = ClampTop(bfr->h, h); + + for (U32 iy = 0; iy < y1; iy += 1){ + S32 iyb = iy*bfr->w; + wmove(window, iy, 0); + for (U32 ix = 0; ix < x1; ix += 1){ + S16 color = (S16)bfr->color[iyb + ix]; + if (color != 0){ + attron(COLOR_PAIR(color)); + } + waddch(window, bfr->chr[iyb + ix]); + attroff(COLOR_PAIR(color)); + } + } + + wrefresh(window); +} + +int main(){ + ArenaTemp scratch = arena_get_scratch(0, 0); + + // prng + PRNG prng = {0}; + { + PRNG_Seed seed; + os_get_entropy(&seed, sizeof(seed)); + prng = prng_from_seed(&seed); + } + + // initialize ncurses + initscr(); + + if (!has_colors()){ + goto no_color; + } + start_color(); + + noecho(); + cbreak(); + curs_set(0); + + // initialize ncurses colors + { +#define X(F,B) init_pair(NCRS_ColorPair_##F##_##B, COLOR_##F, COLOR_##B); + NCRS_ColorPair_XList(X) +#undef X + } + + // virtual buffer + NCRS_Bfr bfr = ncrs_bfr_alloc(scratch.arena, 160, 40); + + // background decorations + static U8 chr_array[] = { + '#', '%', '+', '~', + '*', '|', '-', '$', + '<', '\\', '/', '>', + ',', ',', '.', '.', + ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', + }; + + static U8 color_array[] = { + NCRS_ColorPair_RED_BLACK, NCRS_ColorPair_GREEN_BLACK, NCRS_ColorPair_BLUE_BLACK, + NCRS_ColorPair_YELLOW_BLACK, NCRS_ColorPair_MAGENTA_BLACK, NCRS_ColorPair_CYAN_BLACK, + }; + + // program + for (;;){ + // render to bfr + ncrs_bfr_fill_rect(&bfr, (RectS32){0, 0, 160, 40}, ' ', 0); + ncrs_bfr_outline_rect(&bfr, (RectS32){0, 0, 160, 40}, + '#', NCRS_ColorPair_WHITE_BLACK); + + ncrs_bfr_clip(&bfr, (RectS32){1, 1, 159, 39}); + for (S32 iy = 0; iy < bfr.h; iy += 1){ + for (S32 ix = 0; ix < bfr.w; ix += 1){ + U8 chr = chr_array[prng_u32_bounded(&prng, ArrayCount(chr_array))]; + U8 color = color_array[prng_u32_bounded(&prng, ArrayCount(color_array))]; + ncrs_bfr_point(&bfr, ix, iy, chr, color); + } + } + ncrs_bfr_clip_clear(&bfr); + + ncrs_bfr_fill_rect(&bfr, (RectS32){58, 8, 88, 19}, ' ', NCRS_ColorPair_WHITE_BLACK); + ncrs_bfr_outline_rect(&bfr, (RectS32){58, 8, 88, 19}, '#', NCRS_ColorPair_WHITE_BLACK); + ncrs_bfr_write(&bfr, 60, 10, str8_lit("Continue"), NCRS_ColorPair_BLACK_WHITE); + ncrs_bfr_write(&bfr, 60, 12, str8_lit("New Game (Abandon Current)"), NCRS_ColorPair_WHITE_BLACK); + ncrs_bfr_write(&bfr, 60, 14, str8_lit("Journal"), NCRS_ColorPair_WHITE_BLACK); + ncrs_bfr_write(&bfr, 60, 16, str8_lit("Quit"), NCRS_ColorPair_WHITE_BLACK); + + // bfr -> screen + ncrs_bfr_to_window(&bfr, stdscr); + + int c = getch(); + } + + // end ncurses + endwin(); + + if (0){ no_color: + endwin(); + printf("no ncurses color support\n"); + } + + arena_release_scratch(&scratch); + return(0); +} diff --git a/src/menu.main.h b/src/bfr.main.h similarity index 78% rename from src/menu.main.h rename to src/bfr.main.h index 63e64ce..42049f7 100644 --- a/src/menu.main.h +++ b/src/bfr.main.h @@ -1,5 +1,5 @@ -#ifndef MENU_MAIN_H -#define MENU_MAIN_H +#ifndef BFR_MAIN_H +#define BFR_MAIN_H /****************************************************************************** * NCRS Types * @@ -36,6 +36,8 @@ typedef struct NCRS_Bfr{ U32 h; U8 *chr; U8 *color; + + RectS32 clip; } NCRS_Bfr; /****************************************************************************** @@ -44,10 +46,14 @@ typedef struct NCRS_Bfr{ MR4TH_SYMBOL NCRS_Bfr ncrs_bfr_alloc(Arena *arena, U32 w, U32 h); +MR4TH_SYMBOL void ncrs_bfr_clip_clear(NCRS_Bfr *bfr); +MR4TH_SYMBOL void ncrs_bfr_clip(NCRS_Bfr *bfr, RectS32 rect); + MR4TH_SYMBOL void ncrs_bfr_fill_rect(NCRS_Bfr *bfr, RectS32 rect, U8 chr, U8 color); MR4TH_SYMBOL void ncrs_bfr_outline_rect(NCRS_Bfr *bfr, RectS32 rect, U8 chr, U8 color); -MR4TH_SYMBOL void ncrs_bfr_write(NCRS_Bfr *bfr, S32 x, S32 y, S32 xmax, String8 str, U8 color); +MR4TH_SYMBOL void ncrs_bfr_write(NCRS_Bfr *bfr, S32 x, S32 y, String8 str, U8 color); +MR4TH_SYMBOL void ncrs_bfr_point(NCRS_Bfr *bfr, S32 x, S32 y, U8 chr, U8 color); MR4TH_SYMBOL void ncrs_bfr_to_window(NCRS_Bfr *bfr, WINDOW *w); -#endif //MENU_MAIN_H +#endif //BFR_MAIN_H diff --git a/src/menu.main.c b/src/menu.main.c deleted file mode 100644 index 8e2e7d7..0000000 --- a/src/menu.main.c +++ /dev/null @@ -1,168 +0,0 @@ -#include "mr4th/mr4th_base.h" -#include - -#include "menu.main.h" - -#include "mr4th/mr4th_base.c" - -MR4TH_SYMBOL NCRS_Bfr -ncrs_bfr_alloc(Arena *arena, U32 w, U32 h){ - NCRS_Bfr result = { w, h }; - result.chr = push_array(arena, U8, w*h); - result.color = push_array(arena, U8, w*h); - return(result); -} - -MR4TH_SYMBOL void -ncrs_bfr_fill_rect(NCRS_Bfr *bfr, RectS32 rect, U8 chr, U8 color){ - S32 x0 = ClampBot(rect.x0, 0); - S32 x1 = ClampTop(rect.x1, bfr->w); - S32 y0 = ClampBot(rect.y0, 0); - S32 y1 = ClampTop(rect.y1, bfr->h); - - for (S32 iy = y0; iy < y1; iy += 1){ - S32 iyb = iy*bfr->w; - for (S32 ix = x0; ix < x1; ix += 1){ - bfr->chr[iyb + ix] = chr; - bfr->color[iyb + ix] = color; - } - } -} - -MR4TH_SYMBOL void -ncrs_bfr_outline_rect(NCRS_Bfr *bfr, RectS32 rect, U8 chr, U8 color){ - S32 x0 = ClampBot(rect.x0, 0); - S32 x1 = ClampTop(rect.x1, bfr->w); - S32 y0 = ClampBot(rect.y0, 0); - S32 y1 = ClampTop(rect.y1, bfr->h); - - // top - if (0 <= rect.y0 && rect.y0 < bfr->h){ - S32 iyb = rect.y0*bfr->w; - for (S32 ix = x0; ix < x1; ix += 1){ - bfr->chr[iyb + ix] = chr; - bfr->color[iyb + ix] = color; - } - } - - // bottom - if (0 < rect.y1 && rect.y1 <= bfr->h){ - S32 iyb = (rect.y1 - 1)*bfr->w; - for (S32 ix = x0; ix < x1; ix += 1){ - bfr->chr[iyb + ix] = chr; - bfr->color[iyb + ix] = color; - } - } - - // left - if (0 <= rect.x0 && rect.x0 < bfr->w){ - S32 ix = rect.x0; - for (S32 iy = y0; iy < y1; iy += 1){ - S32 iyb = iy*bfr->w; - bfr->chr[iyb + ix] = chr; - bfr->color[iyb + ix] = color; - } - } - - // right - if (0 < rect.x1 && rect.x1 <= bfr->w){ - S32 ix = rect.x1 - 1; - for (S32 iy = y0; iy < y1; iy += 1){ - S32 iyb = iy*bfr->w; - bfr->chr[iyb + ix] = chr; - bfr->color[iyb + ix] = color; - } - } - -} - -MR4TH_SYMBOL void -ncrs_bfr_write(NCRS_Bfr *bfr, S32 x, S32 y, S32 xmax, String8 str, U8 color){ - if (0 <= x && x < bfr->w && x < xmax && - 0 <= y && y < bfr->h){ - S32 iyb = y*bfr->w; - S32 x1 = ClampTop(x + str.size, xmax); - for (U32 xi = x, i = 0; xi < x1; xi += 1, i += 1){ - bfr->chr[iyb + xi] = str.str[i]; - bfr->color[iyb + xi] = color; - } - } -} - -MR4TH_SYMBOL void -ncrs_bfr_to_window(NCRS_Bfr *bfr, WINDOW *window){ - S32 w = 0; - S32 h = 0; - getmaxyx(window, h, w); - - S32 x1 = ClampTop(bfr->w, w); - S32 y1 = ClampTop(bfr->h, h); - - for (U32 iy = 0; iy < y1; iy += 1){ - S32 iyb = iy*bfr->w; - wmove(window, iy, 0); - for (U32 ix = 0; ix < x1; ix += 1){ - S16 color = (S16)bfr->color[iyb + ix]; - if (color != 0){ - attron(COLOR_PAIR(color)); - } - waddch(window, bfr->chr[iyb + ix]); - attroff(COLOR_PAIR(color)); - } - } - - wrefresh(window); -} - -int main(){ - ArenaTemp scratch = arena_get_scratch(0, 0); - - // initializes ncurses - initscr(); - - if (!has_colors()){ - goto no_color; - } - start_color(); - - noecho(); - cbreak(); - curs_set(0); - - // colors - { -#define X(F,B) init_pair(NCRS_ColorPair_##F##_##B, COLOR_##F, COLOR_##B); - NCRS_ColorPair_XList(X) -#undef X - } - - // virtual buffer - NCRS_Bfr bfr = ncrs_bfr_alloc(scratch.arena, 160, 40); - ncrs_bfr_fill_rect(&bfr, (RectS32){0, 0, 160, 40}, ' ', 0); - ncrs_bfr_outline_rect(&bfr, (RectS32){0, 0, 160, 40}, - '#', NCRS_ColorPair_WHITE_BLACK); - - ncrs_bfr_write(&bfr, 2, 2, 30, str8_lit("Continue"), NCRS_ColorPair_BLACK_WHITE); - ncrs_bfr_write(&bfr, 2, 4, 30, str8_lit("New Game (Abandon Current)"), NCRS_ColorPair_WHITE_BLACK); - ncrs_bfr_write(&bfr, 2, 6, 30, str8_lit("Custom Game Mode"), NCRS_ColorPair_WHITE_BLACK); - ncrs_bfr_write(&bfr, 2, 8, 30, str8_lit("Quite"), NCRS_ColorPair_WHITE_BLACK); - - // program - for (;;){ - // bfr -> screen - ncrs_bfr_to_window(&bfr, stdscr); - - int c = getch(); - } - - // end ncurses - endwin(); - - if (0){ no_color: - endwin(); - printf("no ncurses color support\n"); - } - - arena_release_scratch(&scratch); - return(0); -}