From 0eb08bb04e1d4b45a93a5cd8474f7370c62a841a Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Thu, 11 Sep 2025 21:12:40 +1000 Subject: [PATCH 01/26] rename init_tree -> init_cursetree --- cli/tree.c | 13 ++----------- cli/tree.h | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/cli/tree.c b/cli/tree.c index 0c0b975..2db7063 100644 --- a/cli/tree.c +++ b/cli/tree.c @@ -207,28 +207,19 @@ static void collapse_abstract_node(struct crs_node **node, /* */ static struct crs_node *init_root_node(void) { - struct crs_nodedims rootdims; WINDOW *rootwin; - // rootdims = term_dims(); - // rootwin = new_window(rootdims.x, rootdims.y, - // rootdims.height, rootdims.height); - // rootwin = new_window(0, 0, 0, 0); rootwin = root_window(); return init_window_node(rootwin); } -int init_tree(struct crs_tree **const tree) { +int init_cursetree(struct crs_tree **const tree) { *tree = (struct crs_tree *)malloc(sizeof(struct crs_tree)); - /* Initialise NCurses Library & Standard Screen */ + /* Initialise NCurses Library & Root Node */ init_ncurses(); - (*tree)->root = init_root_node(); - // (*tree)->root = __alloc_node(NODE_WINDOW); - // crs_init(&(*tree)->root->win); - return EXIT_SUCCESS; } diff --git a/cli/tree.h b/cli/tree.h index c2263f1..b1a63d6 100644 --- a/cli/tree.h +++ b/cli/tree.h @@ -51,7 +51,7 @@ struct crs_tree { }; /* === External Interface === */ -int init_tree(struct crs_tree **const tree); +int init_cursetree(struct crs_tree **const tree); void destroy_tree(struct crs_tree *const tree); void resize_tree(struct crs_tree *const tree, struct crs_nodedims *const dims); From 973cefeeb8beca6d8f0c55f832ac6f6341d89d9a Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Thu, 11 Sep 2025 22:16:09 +1000 Subject: [PATCH 02/26] ignore logging directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 840bb0f..915d39f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ bin/ build/ +logs/ sandbox/ # clangd From f2d4060b2b054dd46d4d3a8afca58ca2f85b356d Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Thu, 11 Sep 2025 22:17:17 +1000 Subject: [PATCH 03/26] fix scripts/test fail if program doesn't exist before build phase --- scripts/test | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/test b/scripts/test index 6da6b71..7ec921e 100755 --- a/scripts/test +++ b/scripts/test @@ -13,12 +13,6 @@ TARGET=all PROGRAM="$1" MODE_ARG="$2" -if [[ ! -x "$PROGRAM" ]]; then - echo "[!] Invalid program: \"$PROGRAM\"" - echo "$USAGE" - exit 1 -fi - if [[ "$MODE_ARG" =~ run.* ]]; then MODE="$MODE_ARG" REBUILD=false @@ -39,4 +33,11 @@ fi if [[ "$REBUILD" == true ]]; then make clean $TARGET fi + +if [[ ! -x "$PROGRAM" ]]; then + echo "[!] Invalid program: \"$PROGRAM\"" + echo "$USAGE" + exit 1 +fi + LD_LIBRARY_PATH=./lib $PROXY "$PROGRAM" $@ From 15650ef26c3f99eede317e8e29a54db002476718 Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Thu, 11 Sep 2025 22:18:06 +1000 Subject: [PATCH 04/26] fix scripts/test not compiling with DWARF symbols --- scripts/test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test b/scripts/test index 7ec921e..4c7b47d 100755 --- a/scripts/test +++ b/scripts/test @@ -9,7 +9,7 @@ fi MODE= REBUILD=true VALGRIND=false -TARGET=all +TARGET=debug PROGRAM="$1" MODE_ARG="$2" From 8451df7c927b72eb337f338d893609711a957ee7 Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Thu, 11 Sep 2025 22:18:35 +1000 Subject: [PATCH 05/26] scripts/test now outputs a valgrind log --- scripts/test | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/test b/scripts/test index 4c7b47d..d651df0 100755 --- a/scripts/test +++ b/scripts/test @@ -20,7 +20,7 @@ fi if [[ "$MODE_ARG" =~ .*valgrind ]]; then MODE="$MODE_ARG" VALGRIND=true - PROXY='valgrind -s --leak-check=full --show-leak-kinds=all' + PROXY='valgrind -s --log-file=logs/valgrind.log --leak-check=full --show-leak-kinds=all' TARGET=debug fi @@ -40,4 +40,5 @@ if [[ ! -x "$PROGRAM" ]]; then exit 1 fi +mkdir -p logs LD_LIBRARY_PATH=./lib $PROXY "$PROGRAM" $@ From fc43b6a8de2511f653c11ea1505749a1aebddce8 Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Thu, 11 Sep 2025 22:19:30 +1000 Subject: [PATCH 06/26] fix scripts/test doesn't re-init termios(3x) attrs (ie on SIGSEGV) --- scripts/test | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/test b/scripts/test index d651df0..2e00268 100755 --- a/scripts/test +++ b/scripts/test @@ -42,3 +42,7 @@ fi mkdir -p logs LD_LIBRARY_PATH=./lib $PROXY "$PROGRAM" $@ + +# If ncurses doesn't close gracefully, termios attributes +# will require reinitialisation (not a reset! don't clear the screen!) +tput init From 4dcd8f09f58ced16a29ce50d078b072c7e8453ec Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Fri, 12 Sep 2025 00:15:29 +1000 Subject: [PATCH 07/26] update TODO --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index 36d892f..b982af8 100644 --- a/TODO.md +++ b/TODO.md @@ -9,4 +9,6 @@ then fork and exec shfx as child (+ close ptmx), then close pts as parent. - [ ] Should I compile ncurses with --with-pthread? (see `man 3x ncurses`) What about --enable-widec? (are these just ligatures?) +- [ ] cursetree shits itself if width/height are 0 on bifurcation +- [ ] ensure NDEBUG is set for preprocessor to avoid statements if not debugging From a90b99baa3fb4aa59b4887091559866c83b7b6c7 Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Fri, 12 Sep 2025 00:15:53 +1000 Subject: [PATCH 08/26] fix Makefile attempts to build shfx (removed) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 36b9358..9446e37 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ $(LD) -r $(LDFLAGS) -o $@ $(call objpath, $?) endef # === BUILD TARGETS === -all: $(BUILD) $(BIN) $(addprefix $(BIN)/,pw-test shfx dorne) +all: $(BUILD) $(BIN) $(addprefix $(BIN)/,pw-test dorne) $(BIN)/pw-test: $(addprefix $(LIB)/, main.c) $(CC) $(CFLAGS) -o $(BUILD)/pw-test.o -c $^ $(CLD) $(CLDFLAGS) -lpipewire-0.3 -o $@ $(BUILD)/pw-test.o From 373081a1b36e5bd71d7aaf95096ca2fbe92505de Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Fri, 12 Sep 2025 00:17:48 +1000 Subject: [PATCH 09/26] add (terrible) support for auto window resizing --- cli/curse.h | 2 + cli/main.c | 50 +++------------------ cli/tree.c | 124 +++++++++++++++++++++++++++++++++------------------- cli/tree.h | 8 ++-- 4 files changed, 94 insertions(+), 90 deletions(-) diff --git a/cli/curse.h b/cli/curse.h index 72f0e7e..f08d891 100644 --- a/cli/curse.h +++ b/cli/curse.h @@ -15,6 +15,8 @@ enum crs_termmode { TMODE_NORAW, }; +#define termsize(width, height) (width=COLS); (height=LINES) + int termmode(const enum crs_termmode mode); void init_ncurses(void); diff --git a/cli/main.c b/cli/main.c index 956a25e..6c79397 100644 --- a/cli/main.c +++ b/cli/main.c @@ -23,60 +23,24 @@ int main(int argc, char **argv) { // spawnchild(&child); struct crs_tree *tree; - init_tree(&tree); + ct_init(&tree); init_pair(1, COLOR_CYAN, COLOR_CYAN); init_pair(2, COLOR_RED, COLOR_RED); - init_pair(3, COLOR_MAGENTA, COLOR_MAGENTA); - init_pair(4, COLOR_YELLOW, COLOR_YELLOW); - init_pair(5, COLOR_GREEN, COLOR_GREEN); - /* BUG TODO : PROGRAM SHITS ITSELF IF RATIO MAKES WIDTH/HEIGHT 0, MAYBE CLAMP? : TODO BUG*/ - /* BUG TODO : PROGRAM SHITS ITSELF IF RATIO MAKES WIDTH/HEIGHT 0, MAYBE CLAMP? : TODO BUG*/ - /* BUG TODO : PROGRAM SHITS ITSELF IF RATIO MAKES WIDTH/HEIGHT 0, MAYBE CLAMP? : TODO BUG*/ - /* BUG TODO : PROGRAM SHITS ITSELF IF RATIO MAKES WIDTH/HEIGHT 0, MAYBE CLAMP? : TODO BUG*/ - /* BUG TODO : PROGRAM SHITS ITSELF IF RATIO MAKES WIDTH/HEIGHT 0, MAYBE CLAMP? : TODO BUG*/ - /* BUG TODO : PROGRAM SHITS ITSELF IF RATIO MAKES WIDTH/HEIGHT 0, MAYBE CLAMP? : TODO BUG*/ - /* BUG TODO : PROGRAM SHITS ITSELF IF RATIO MAKES WIDTH/HEIGHT 0, MAYBE CLAMP? : TODO BUG*/ - /* BUG TODO : PROGRAM SHITS ITSELF IF RATIO MAKES WIDTH/HEIGHT 0, MAYBE CLAMP? : TODO BUG*/ - /* BUG TODO : PROGRAM SHITS ITSELF IF RATIO MAKES WIDTH/HEIGHT 0, MAYBE CLAMP? : TODO BUG*/ - /* BUG TODO : PROGRAM SHITS ITSELF IF RATIO MAKES WIDTH/HEIGHT 0, MAYBE CLAMP? : TODO BUG*/ - /* BUG TODO : PROGRAM SHITS ITSELF IF RATIO MAKES WIDTH/HEIGHT 0, MAYBE CLAMP? : TODO BUG*/ - /* BUG TODO : PROGRAM SHITS ITSELF IF RATIO MAKES WIDTH/HEIGHT 0, MAYBE CLAMP? : TODO BUG*/ bifurcate_window_node(&tree->root, AXIS_X, FALSE, 0.5); - bifurcate_window_node(&tree->root->child[0], AXIS_X, FALSE, 0.05); - bifurcate_window_node(&tree->root->child[1], AXIS_Y, FALSE, 0.7); - bifurcate_window_node(&tree->root->child[1]->child[1], AXIS_X, FALSE, 0.9); - - wbkgd(tree->root->child[0]->child[0]->win, COLOR_PAIR(5)); - wbkgd(tree->root->child[0]->child[1]->win, COLOR_PAIR(3)); - wbkgd(tree->root->child[1]->child[0]->win, COLOR_PAIR(2)); - wbkgd(tree->root->child[1]->child[1]->child[0]->win, COLOR_PAIR(1)); - wbkgd(tree->root->child[1]->child[1]->child[1]->win, COLOR_PAIR(4)); - wrefresh(tree->root->child[0]->child[0]->win); - wrefresh(tree->root->child[0]->child[1]->win); - wrefresh(tree->root->child[1]->child[0]->win); - wrefresh(tree->root->child[1]->child[1]->child[0]->win); - wrefresh(tree->root->child[1]->child[1]->child[1]->win); - - /* NOTE : THIS IS VALID : NOTE */ - // refresh(); - // WINDOW *my_win = newwin(0, 0, 0, 0); - // wbkgd(my_win, COLOR_PAIR(1)); - // wrefresh(my_win); - - /* NOTE : THIS IS VALID : NOTE */ - // wbkgd(tree->root->win, COLOR_PAIR(2)); - // wrefresh(tree->root->win); + wbkgd(tree->root->child[0]->win, COLOR_PAIR(1)); + wbkgd(tree->root->child[1]->win, COLOR_PAIR(2)); + wrefresh(tree->root->child[0]->win); + wrefresh(tree->root->child[1]->win); while (1) { + ct_update(tree); // refresh(); - usleep(100000); + // usleep(100000); } destroy_tree(tree); - // endwin(); - // free(tree); return EXIT_SUCCESS; } diff --git a/cli/tree.c b/cli/tree.c index 2db7063..177338f 100644 --- a/cli/tree.c +++ b/cli/tree.c @@ -22,16 +22,17 @@ static struct crs_node *init_window_node(WINDOW *const win) { return node; } -static struct crs_node *auto_window_node(const struct crs_nodedims *dims) { +static struct crs_node * +auto_window_node(const struct crs_nodedims *const dims) { WINDOW *win = newwin(dims->height, dims->width, dims->y, dims->x); return init_window_node(win); } -static struct crs_node *init_abstract_node(struct crs_node *node0, - struct crs_node *node1, +static struct crs_node *init_abstract_node(struct crs_node *const node0, + struct crs_node *const node1, const enum crs_axis axis, const float ratio, - struct crs_nodedims *dims) { + struct crs_nodedims *const dims) { struct crs_node *node = __alloc_node(NODE_ABSTRACT); node->axis = axis; node->ratio = ratio; @@ -42,7 +43,7 @@ static struct crs_node *init_abstract_node(struct crs_node *node0, return node; } -static void destroy_node(struct crs_node *node) { +static void destroy_node(struct crs_node *const node) { if (node->type == NODE_WINDOW) { /* Window Node */ delwin(node->win); @@ -58,7 +59,22 @@ end: free(node); } -static inline struct crs_nodedims *__dup_dims(const struct crs_nodedims *dims) { +static inline struct crs_nodedims *__alloc_dims(int x, int y, int width, + int height) { + struct crs_nodedims *dims; + + dims = (struct crs_nodedims *)malloc(sizeof(struct crs_nodedims)); + *dims = (struct crs_nodedims){ + .x = x, + .y = y, + .width = width, + .height = height, + }; + return dims; +} + +static inline struct crs_nodedims * +__dup_dims(const struct crs_nodedims *const dims) { struct crs_nodedims *dup; dup = (struct crs_nodedims *)malloc(sizeof(struct crs_nodedims)); memcpy(dup, dims, sizeof(struct crs_nodedims)); @@ -66,18 +82,33 @@ static inline struct crs_nodedims *__dup_dims(const struct crs_nodedims *dims) { return dup; } -static struct crs_nodedims *get_dims(const struct crs_node *node) { +static inline struct crs_nodedims *termdims(void) { + struct crs_nodedims *dims = __alloc_dims(0, 0, 0, 0); + termsize(dims->width, dims->height); + + return dims; +} + +static inline void nodesize(const struct crs_node *const node, int *const width, + int *const height) { + if (node->type == NODE_WINDOW) { + /* Window Node */ + getmaxyx(node->win, *height, *width); + } else { + /* Abstract Node */ + assert(node->type == NODE_ABSTRACT); + *width = node->dims->width; + *height = node->dims->height; + } +} + +static inline struct crs_nodedims *nodedims(const struct crs_node *const node) { struct crs_nodedims *dims; if (node->type == NODE_WINDOW) { /* Window Node */ dims = (struct crs_nodedims *)malloc(sizeof(struct crs_nodedims)); - // GET_WNODEDIMS(); getbegyx(node->win, dims->y, dims->x); getmaxyx(node->win, dims->height, dims->width); - printf("getx: %d\n", dims->x); - printf("gety: %d\n", dims->y); - printf("getwidth: %d\n", dims->width); - printf("getheight: %d\n", dims->height); } else { /* Abstract Node */ assert(node->type == NODE_ABSTRACT); @@ -87,30 +118,20 @@ static struct crs_nodedims *get_dims(const struct crs_node *node) { return dims; } -struct crs_nodedims term_dims(void) { - struct crs_nodedims dims; - getbegyx(stdscr, dims.y, dims.x); - getmaxyx(stdscr, dims.height, dims.width); - return dims; -} - /* Calculate the dimensions for nodes resulting from a bifurcation. * Returns 0 on success, and 1 on failure if any width/height are 0 characters. * WARNING: This function does not guarantee the x,y positions returned * WARNING: are valid screen coordinates. */ -static int bifurcate_dims(const struct crs_node *parent, +static int bifurcate_dims(const struct crs_nodedims *const parent_dims, const enum crs_axis axis, const float ratio, - struct crs_nodedims **dims0, - struct crs_nodedims **dims1) { + struct crs_nodedims **const dims0, + struct crs_nodedims **const dims1) { assert(0 < ratio && ratio < 1); struct crs_nodedims *_dims0, *_dims1; - _dims0 = get_dims(parent); - _dims1 = __dup_dims(_dims0); - - printf("widthP: %d\n", _dims0->width); - printf("heightP: %d\n", _dims0->height); + _dims0 = __dup_dims(parent_dims); + _dims1 = __dup_dims(parent_dims); if (axis == AXIS_X) { _dims0->width *= ratio; @@ -122,13 +143,8 @@ static int bifurcate_dims(const struct crs_node *parent, _dims1->y += _dims0->height; } - if (!_dims0->width || !_dims0->height || !_dims1->width || !_dims1->height) { - printf("width0: %d\n", _dims0->width); - printf("height0: %d\n", _dims0->height); - printf("width1: %d\n", _dims1->width); - printf("height1: %d\n", _dims1->height); + if (!_dims0->width || !_dims0->height || !_dims1->width || !_dims1->height) return 1; - } // propagate bifurcated dimensions *dims0 = _dims0; @@ -136,11 +152,13 @@ static int bifurcate_dims(const struct crs_node *parent, return 0; } -static void resize_node(struct crs_node *node, struct crs_nodedims *new_dims) { +static void resize_node(struct crs_node *const node, + struct crs_nodedims *const new_dims) { if (node->type == NODE_WINDOW) { /* Window Node */ resizemv_window(new_dims->x, new_dims->y, new_dims->width, new_dims->height, node->win); + wrefresh(node->win); free(new_dims); } else { /* Abstract Node */ @@ -149,7 +167,7 @@ static void resize_node(struct crs_node *node, struct crs_nodedims *new_dims) { free(node->dims); node->dims = new_dims; - bifurcate_dims(node, node->axis, node->ratio, &dims0, &dims1); + bifurcate_dims(new_dims, node->axis, node->ratio, &dims0, &dims1); resize_node(node->child[0], dims0); resize_node(node->child[1], dims1); @@ -162,17 +180,21 @@ static void resize_node(struct crs_node *node, struct crs_nodedims *new_dims) { * axis - controls which direction the subdivision occurs * invert_axis - invert index of the original node in the new abstract node */ -void bifurcate_window_node(struct crs_node **node, const enum crs_axis axis, - const int invert_axis, const float ratio) { +void bifurcate_window_node(struct crs_node **const node, + const enum crs_axis axis, const int invert_axis, + const float ratio) { assert((*node)->type == NODE_WINDOW); struct crs_nodedims *dims0, *dims1; struct crs_node *node0, *node1; - if (bifurcate_dims(*node, axis, ratio, &dims0, &dims1)) { - printf("FAILED TERRIBLY THE FUCK\n"); + struct crs_nodedims *original_dims = nodedims(*node); + if (bifurcate_dims(original_dims, axis, ratio, &dims0, &dims1)) { + /* TODO: handle this error properly */ + free(original_dims); exit(1); return; } + if (invert_axis) { /* Inverted Bifurcation */ node0 = auto_window_node(dims0); @@ -185,13 +207,13 @@ void bifurcate_window_node(struct crs_node **node, const enum crs_axis axis, resize_node(node0, dims0); } - *node = init_abstract_node(node0, node1, axis, ratio, (*node)->dims); + *node = init_abstract_node(node0, node1, axis, ratio, original_dims); } /* Collapse an abstract node, killing one child node and resizing * the other to take its place. */ -static void collapse_abstract_node(struct crs_node **node, +static void collapse_abstract_node(struct crs_node **const node, const int collapse_i) { assert((*node)->type == NODE_ABSTRACT); assert(0 <= collapse_i && collapse_i < NODE_CHILD_N); @@ -213,7 +235,7 @@ static struct crs_node *init_root_node(void) { return init_window_node(rootwin); } -int init_cursetree(struct crs_tree **const tree) { +int ct_init(struct crs_tree **const tree) { *tree = (struct crs_tree *)malloc(sizeof(struct crs_tree)); /* Initialise NCurses Library & Root Node */ @@ -224,9 +246,7 @@ int init_cursetree(struct crs_tree **const tree) { } void destroy_tree(struct crs_tree *const tree) { - /* WARNING: is it ok to delwin(stdscr) ?? */ destroy_node(tree->root); - endwin(); free(tree); } @@ -234,3 +254,19 @@ void destroy_tree(struct crs_tree *const tree) { void resize_tree(struct crs_tree *const tree, struct crs_nodedims *const dims) { resize_node(tree->root, dims); } + +void ct_update(struct crs_tree *const tree) { + struct crs_nodedims *term_dims; + + int term_width, term_height, win_width, win_height; + termsize(term_width, term_height); + nodesize(tree->root, &win_width, &win_height); + /* Check if window size has changed, I assume ncurses already binds + * a SIGWINCH handler so we can do this instead (TODO: sighandler?) */ + if (win_width != term_width || win_height != term_height) { + term_dims = __alloc_dims(0, 0, term_width, term_height); + resize_tree(tree, term_dims); + /* TODO: why the fuck does this line make everything work?? */ + wrefresh(tree->root->child[0]->win); + } +} diff --git a/cli/tree.h b/cli/tree.h index b1a63d6..3418a0f 100644 --- a/cli/tree.h +++ b/cli/tree.h @@ -51,11 +51,13 @@ struct crs_tree { }; /* === External Interface === */ -int init_cursetree(struct crs_tree **const tree); +int ct_init(struct crs_tree **const tree); void destroy_tree(struct crs_tree *const tree); void resize_tree(struct crs_tree *const tree, struct crs_nodedims *const dims); +void ct_update(struct crs_tree *const tree); -void bifurcate_window_node(struct crs_node **node, const enum crs_axis axis, - const int invert_axis, const float ratio); +void bifurcate_window_node(struct crs_node **const node, + const enum crs_axis axis, const int invert_axis, + const float ratio); #endif /* DORNE_TREE_H */ From 6cc80f32a3043eae13db2582c6b73b5ab835932d Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Fri, 12 Sep 2025 01:51:47 +1000 Subject: [PATCH 10/26] README should specify ncurses extensions --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 271ecdf..191dc54 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,11 @@ A personal functional music programming framework written in Haskell & C and packaged with Nix. ### Setup +Ensure that your system's version of ncurses has the following extensions: +- wide character support +- term resizing (see `man 3x resizeterm`) +> TODO: actually list which extensions are used and how to compile with them + ```bash nix build && nix develop -i ``` From 2ad6e4e104fac69422856d82d343f30be185d1e4 Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Fri, 12 Sep 2025 01:53:20 +1000 Subject: [PATCH 11/26] AUTO RESIZING NOW WORKS WELL --- cli/curse.c | 32 +------------------------------- cli/main.c | 18 ++++-------------- cli/tree.c | 33 ++++++++++++++++++++++++--------- 3 files changed, 29 insertions(+), 54 deletions(-) diff --git a/cli/curse.c b/cli/curse.c index 3c465fe..0549c9a 100644 --- a/cli/curse.c +++ b/cli/curse.c @@ -43,6 +43,7 @@ void init_ncurses(void) { /* NCurses Init */ initscr(); + /* WARNING: no you shouldn't delwin(stdscr) it breaks everything... */ __conf_window(stdscr); start_color(); @@ -81,34 +82,3 @@ int resizemv_window(const int x, const int y, const int width, const int height, WINDOW *const win) { return wresize(win, height, width) || mvwin(win, y, x); } - -/* -int main(const int argc, const char *const argv[]) { - init_ncurses(); - WINDOW *stdscr = new_window(0, 0, 0, 0); - - // Set background - wbkgd(stdscr, COLOR_PAIR(1)); - - attron(COLOR_PAIR(1)); - attron(A_BOLD); - int counter = 0; - while (1) { - mvprintw(0, 0, "COUNTER %d", counter++); - refresh(); - - switch (getch()) { - case 'q': - goto end; - default: - break; - } - - usleep(BASE_DELAY); - } - -end: - endwin(); - return 0; -} -*/ diff --git a/cli/main.c b/cli/main.c index 6c79397..32811e3 100644 --- a/cli/main.c +++ b/cli/main.c @@ -4,20 +4,8 @@ #include -// #include "child.h" -#include "curse.h" #include "tree.h" -// struct d_window { -// int ptmx; // fd -// }; - -// struct d_window new_window() { -// struct d_window w = { -// .ptmx = posix_openpt(O_RDWR | O_NOCTTY), -// }; -// } - int main(int argc, char **argv) { // struct d_child child; // spawnchild(&child); @@ -34,10 +22,12 @@ int main(int argc, char **argv) { wrefresh(tree->root->child[0]->win); wrefresh(tree->root->child[1]->win); + // wbkgd(tree->root->win, COLOR_PAIR(1)); + // wrefresh(tree->root->win); + while (1) { ct_update(tree); - // refresh(); - // usleep(100000); + usleep(100000); } destroy_tree(tree); diff --git a/cli/tree.c b/cli/tree.c index 177338f..aadc024 100644 --- a/cli/tree.c +++ b/cli/tree.c @@ -3,6 +3,7 @@ #include #include "curse.h" +#include "ncurses.h" #include "tree.h" /* Internal allocator method for crs_node structures. @@ -152,14 +153,19 @@ static int bifurcate_dims(const struct crs_nodedims *const parent_dims, return 0; } +/* NOTE: resize_node calls wnoutrefresh(3x), which expects + * NOTE: a call doupdate(3x) call afterwards to flush ncurses + * NOTE: virtual screen to the physical screen. + */ static void resize_node(struct crs_node *const node, struct crs_nodedims *const new_dims) { if (node->type == NODE_WINDOW) { /* Window Node */ resizemv_window(new_dims->x, new_dims->y, new_dims->width, new_dims->height, node->win); - wrefresh(node->win); free(new_dims); + + wnoutrefresh(node->win); } else { /* Abstract Node */ assert(node->type == NODE_ABSTRACT); @@ -256,17 +262,26 @@ void resize_tree(struct crs_tree *const tree, struct crs_nodedims *const dims) { } void ct_update(struct crs_tree *const tree) { - struct crs_nodedims *term_dims; + struct crs_nodedims * term_dims; + int term_width, term_height; + const int key = wgetch(curscr); - int term_width, term_height, win_width, win_height; - termsize(term_width, term_height); - nodesize(tree->root, &win_width, &win_height); - /* Check if window size has changed, I assume ncurses already binds - * a SIGWINCH handler so we can do this instead (TODO: sighandler?) */ - if (win_width != term_width || win_height != term_height) { + /* ncurses binds a SIGWINCH handler if SIGWINCH has SIG_DFL disposition + * when initscr(3x) is called. This handler emits KEY_RESIZE (decimal 410) to stdin. + * REF: manpages -> resizeterm(3x) initscr(3x) wgetch(3x) + */ + switch (key) { + case -1: + return; + case KEY_RESIZE: + termsize(term_width, term_height); term_dims = __alloc_dims(0, 0, term_width, term_height); resize_tree(tree, term_dims); + // flush ncurses virtual screen -> physical screen + doupdate(); /* TODO: why the fuck does this line make everything work?? */ - wrefresh(tree->root->child[0]->win); + // printf("hola\n"); + default: + break; } } From 878be07a7126676531c2a53ede1fac91accb637a Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Sat, 13 Sep 2025 10:39:58 +1000 Subject: [PATCH 12/26] rename cli/ -> cursetree/ --- Makefile | 13 ++++++------ cli/main.c | 31 ++++++++++++++++++++--------- {cli => cursetree}/_pty.c | 0 {cli => cursetree}/_pty.h | 0 {cli => cursetree}/child.c | 0 {cli => cursetree}/child.h | 0 {cli => cursetree}/curse.c | 0 {cli => cursetree}/curse.h | 0 cli/tree.c => cursetree/cursetree.c | 11 +++++++--- cli/tree.h => cursetree/cursetree.h | 0 {cli => cursetree}/epty.c | 0 {cli => cursetree}/epty.h | 0 12 files changed, 37 insertions(+), 18 deletions(-) rename {cli => cursetree}/_pty.c (100%) rename {cli => cursetree}/_pty.h (100%) rename {cli => cursetree}/child.c (100%) rename {cli => cursetree}/child.h (100%) rename {cli => cursetree}/curse.c (100%) rename {cli => cursetree}/curse.h (100%) rename cli/tree.c => cursetree/cursetree.c (96%) rename cli/tree.h => cursetree/cursetree.h (100%) rename {cli => cursetree}/epty.c (100%) rename {cli => cursetree}/epty.h (100%) diff --git a/Makefile b/Makefile index 9446e37..9f43b25 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ BUILD := build LIB := lib CLI := cli +CT := cursetree # === MACRO DEFINITIONS === define objpath @@ -18,7 +19,7 @@ endef define mkobj $(foreach DEP, $?, mkdir -p $(dir $(call objpath, $(DEP))) - $(CC) $(CFLAGS) -o $(call objpath, $(DEP)) -c $(DEP)) + $(CC) $(CFLAGS) $1 -o $(call objpath, $(DEP)) -c $(DEP)) $(LD) -r $(LDFLAGS) -o $@ $(call objpath, $?) endef @@ -36,11 +37,11 @@ $(BUILD) $(BIN): mkdir -p $@ $(BUILD)/dorne.o: $(addprefix $(CLI)/, main.c) - $(mkobj) -$(BUILD)/cursetree.o: $(addprefix $(CLI)/, tree.c curse.c) - $(mkobj) -$(BUILD)/epty.o: $(addprefix $(CLI)/, child.c epty.c _pty.c) - $(mkobj) + $(call mkobj,-Icursetree) +$(BUILD)/cursetree.o: $(addprefix $(CT)/, cursetree.c curse.c) + $(call mkobj,) +$(BUILD)/epty.o: $(addprefix $(CT)/, child.c epty.c _pty.c) + $(call mkobj,) # === DEVELOPMENT TARGETS === .PHONY: debug run test diff --git a/cli/main.c b/cli/main.c index 32811e3..6c852ba 100644 --- a/cli/main.c +++ b/cli/main.c @@ -4,12 +4,9 @@ #include -#include "tree.h" +#include "cursetree.h" int main(int argc, char **argv) { - // struct d_child child; - // spawnchild(&child); - struct crs_tree *tree; ct_init(&tree); @@ -17,17 +14,33 @@ int main(int argc, char **argv) { init_pair(2, COLOR_RED, COLOR_RED); bifurcate_window_node(&tree->root, AXIS_X, FALSE, 0.5); - wbkgd(tree->root->child[0]->win, COLOR_PAIR(1)); - wbkgd(tree->root->child[1]->win, COLOR_PAIR(2)); - wrefresh(tree->root->child[0]->win); - wrefresh(tree->root->child[1]->win); + // wbkgd(tree->root->child[0]->win, COLOR_PAIR(1)); + // wbkgd(tree->root->child[1]->win, COLOR_PAIR(2)); + // wrefresh(tree->root->child[0]->win); + // wrefresh(tree->root->child[1]->win); // wbkgd(tree->root->win, COLOR_PAIR(1)); // wrefresh(tree->root->win); + int i = 1; while (1) { + /* TODO: UNCOMMENT START */ + // wclear(tree->root->child[0]->win); + // wclear(tree->root->child[1]->win); + // wbkgd(tree->root->child[0]->win, COLOR_PAIR(i)); + // wbkgd(tree->root->child[1]->win, COLOR_PAIR(3 - i)); + + // wnoutrefresh(tree->root->child[0]->win); + // wnoutrefresh(tree->root->child[1]->win); + // doupdate(); + + // wrefresh(tree->root->child[0]->win); + // wrefresh(tree->root->child[1]->win); + i = 3 - i; + /* TODO: UNCOMMENT END */ + ct_update(tree); - usleep(100000); + // usleep(100000); } destroy_tree(tree); diff --git a/cli/_pty.c b/cursetree/_pty.c similarity index 100% rename from cli/_pty.c rename to cursetree/_pty.c diff --git a/cli/_pty.h b/cursetree/_pty.h similarity index 100% rename from cli/_pty.h rename to cursetree/_pty.h diff --git a/cli/child.c b/cursetree/child.c similarity index 100% rename from cli/child.c rename to cursetree/child.c diff --git a/cli/child.h b/cursetree/child.h similarity index 100% rename from cli/child.h rename to cursetree/child.h diff --git a/cli/curse.c b/cursetree/curse.c similarity index 100% rename from cli/curse.c rename to cursetree/curse.c diff --git a/cli/curse.h b/cursetree/curse.h similarity index 100% rename from cli/curse.h rename to cursetree/curse.h diff --git a/cli/tree.c b/cursetree/cursetree.c similarity index 96% rename from cli/tree.c rename to cursetree/cursetree.c index aadc024..b179f86 100644 --- a/cli/tree.c +++ b/cursetree/cursetree.c @@ -4,7 +4,7 @@ #include "curse.h" #include "ncurses.h" -#include "tree.h" +#include "cursetree.h" /* Internal allocator method for crs_node structures. */ @@ -272,6 +272,9 @@ void ct_update(struct crs_tree *const tree) { */ switch (key) { case -1: + wclear(tree->root->child[0]->win); + mvwprintw(tree->root->child[0]->win, 0, 0, " \r-1\n"); + wrefresh(tree->root->child[0]->win); return; case KEY_RESIZE: termsize(term_width, term_height); @@ -279,9 +282,11 @@ void ct_update(struct crs_tree *const tree) { resize_tree(tree, term_dims); // flush ncurses virtual screen -> physical screen doupdate(); - /* TODO: why the fuck does this line make everything work?? */ - // printf("hola\n"); + break; default: + wclear(tree->root->child[0]->win); + mvwprintw(tree->root->child[0]->win, 0, 0, " \r%d\n", key); + wrefresh(tree->root->child[0]->win); break; } } diff --git a/cli/tree.h b/cursetree/cursetree.h similarity index 100% rename from cli/tree.h rename to cursetree/cursetree.h diff --git a/cli/epty.c b/cursetree/epty.c similarity index 100% rename from cli/epty.c rename to cursetree/epty.c diff --git a/cli/epty.h b/cursetree/epty.h similarity index 100% rename from cli/epty.h rename to cursetree/epty.h From 69745c81f7fba5e869fc4c88429a9a93697c70c6 Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Sat, 13 Sep 2025 10:47:48 +1000 Subject: [PATCH 13/26] mv cursetree/pty subdir --- Makefile | 2 +- cursetree/{ => pty}/_pty.c | 0 cursetree/{ => pty}/_pty.h | 0 cursetree/{ => pty}/child.c | 0 cursetree/{ => pty}/child.h | 0 cursetree/{ => pty}/epty.c | 0 cursetree/{ => pty}/epty.h | 0 7 files changed, 1 insertion(+), 1 deletion(-) rename cursetree/{ => pty}/_pty.c (100%) rename cursetree/{ => pty}/_pty.h (100%) rename cursetree/{ => pty}/child.c (100%) rename cursetree/{ => pty}/child.h (100%) rename cursetree/{ => pty}/epty.c (100%) rename cursetree/{ => pty}/epty.h (100%) diff --git a/Makefile b/Makefile index 9f43b25..21034d3 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ $(BUILD)/dorne.o: $(addprefix $(CLI)/, main.c) $(call mkobj,-Icursetree) $(BUILD)/cursetree.o: $(addprefix $(CT)/, cursetree.c curse.c) $(call mkobj,) -$(BUILD)/epty.o: $(addprefix $(CT)/, child.c epty.c _pty.c) +$(BUILD)/epty.o: $(addprefix $(CT)/, pty/child.c pty/epty.c pty/_pty.c) $(call mkobj,) # === DEVELOPMENT TARGETS === diff --git a/cursetree/_pty.c b/cursetree/pty/_pty.c similarity index 100% rename from cursetree/_pty.c rename to cursetree/pty/_pty.c diff --git a/cursetree/_pty.h b/cursetree/pty/_pty.h similarity index 100% rename from cursetree/_pty.h rename to cursetree/pty/_pty.h diff --git a/cursetree/child.c b/cursetree/pty/child.c similarity index 100% rename from cursetree/child.c rename to cursetree/pty/child.c diff --git a/cursetree/child.h b/cursetree/pty/child.h similarity index 100% rename from cursetree/child.h rename to cursetree/pty/child.h diff --git a/cursetree/epty.c b/cursetree/pty/epty.c similarity index 100% rename from cursetree/epty.c rename to cursetree/pty/epty.c diff --git a/cursetree/epty.h b/cursetree/pty/epty.h similarity index 100% rename from cursetree/epty.h rename to cursetree/pty/epty.h From daaaaf979cd6a2b2c33f096f5e7faa5ee9036c1d Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Sat, 13 Sep 2025 10:58:31 +1000 Subject: [PATCH 14/26] change DORNE_*_H namespacing to CURSETREE_*_H --- Makefile | 2 +- cursetree/cursetree.c | 3 +- cursetree/cursetree.h | 6 ++-- cursetree/{curse.c => ncrswrap.c} | 3 +- cursetree/{curse.h => ncrswrap.h} | 6 ++-- cursetree/node.h | 51 +++++++++++++++++++++++++++++++ cursetree/pty/_pty.h | 6 ++-- cursetree/pty/child.h | 6 ++-- cursetree/pty/epty.h | 6 ++-- 9 files changed, 69 insertions(+), 20 deletions(-) rename cursetree/{curse.c => ncrswrap.c} (97%) rename cursetree/{curse.h => ncrswrap.h} (87%) create mode 100644 cursetree/node.h diff --git a/Makefile b/Makefile index 21034d3..4894efc 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ $(BUILD) $(BIN): $(BUILD)/dorne.o: $(addprefix $(CLI)/, main.c) $(call mkobj,-Icursetree) -$(BUILD)/cursetree.o: $(addprefix $(CT)/, cursetree.c curse.c) +$(BUILD)/cursetree.o: $(addprefix $(CT)/, cursetree.c ncrswrap.c) $(call mkobj,) $(BUILD)/epty.o: $(addprefix $(CT)/, pty/child.c pty/epty.c pty/_pty.c) $(call mkobj,) diff --git a/cursetree/cursetree.c b/cursetree/cursetree.c index b179f86..8d80831 100644 --- a/cursetree/cursetree.c +++ b/cursetree/cursetree.c @@ -2,8 +2,7 @@ #include #include -#include "curse.h" -#include "ncurses.h" +#include "ncrswrap.h" #include "cursetree.h" /* Internal allocator method for crs_node structures. diff --git a/cursetree/cursetree.h b/cursetree/cursetree.h index 3418a0f..74d804f 100644 --- a/cursetree/cursetree.h +++ b/cursetree/cursetree.h @@ -1,5 +1,5 @@ -#ifndef DORNE_TREE_H -#define DORNE_TREE_H +#ifndef CURSETREE_CURSETREE_H +#define CURSETREE_CURSETREE_H #ifndef __NCURSES_H typedef struct _win_st WINDOW; @@ -60,4 +60,4 @@ void bifurcate_window_node(struct crs_node **const node, const enum crs_axis axis, const int invert_axis, const float ratio); -#endif /* DORNE_TREE_H */ +#endif /* CURSETREE_CURSETREE_H */ diff --git a/cursetree/curse.c b/cursetree/ncrswrap.c similarity index 97% rename from cursetree/curse.c rename to cursetree/ncrswrap.c index 0549c9a..9623447 100644 --- a/cursetree/curse.c +++ b/cursetree/ncrswrap.c @@ -1,9 +1,8 @@ #include #include -#include "curse.h" +#include "ncrswrap.h" -#define BASE_DELAY 1000000 #define CRS_LOCALE "en_US.UTF-8" /* Set ncurses terminal mode (buffering, character processing, diff --git a/cursetree/curse.h b/cursetree/ncrswrap.h similarity index 87% rename from cursetree/curse.h rename to cursetree/ncrswrap.h index f08d891..b73f4b5 100644 --- a/cursetree/curse.h +++ b/cursetree/ncrswrap.h @@ -1,5 +1,5 @@ -#ifndef DORNE_CURSE_H -#define DORNE_CURSE_H +#ifndef CURSETREE_NCRSWRAP_H +#define CURSETREE_NCRSWRAP_H /* libncurses with wide-character support. */ #include @@ -26,4 +26,4 @@ WINDOW *root_window(void); int resizemv_window(const int x, const int y, const int width, const int height, WINDOW *const win); -#endif /* DORNE_CURSE_H */ +#endif /* CURSETREE_NCRSWRAP_H */ diff --git a/cursetree/node.h b/cursetree/node.h new file mode 100644 index 0000000..bf789c3 --- /dev/null +++ b/cursetree/node.h @@ -0,0 +1,51 @@ +#ifndef CURSETREE_NODE_H +#define CURSETREE_NODE_H + +#ifndef __NCURSES_H +typedef struct _win_st WINDOW; +#endif /* __NCURSES_H */ + +#define NODE_CHILD_N 2 + +/* MACRO: + * Get widnow node start x,y coordinates, width, & height. + * void NODEDIMS(struct crs_node *node, struct crs_nodedims dims); + */ +#define GET_WNODEDIMS(dims, node) \ + (getbegyx((node)->win, dims.y, dims.x)); \ + getmaxyx((node)->win, dims.y, dims.x) + +enum crs_nodetype { + NODE_WINDOW, + NODE_ABSTRACT, +}; + +/* Stores a node's starting x,y coordinates, width, & height. + * NOTE: Intended for interfunction communication. + */ +struct crs_nodedims { + int x, y, width, height; +}; + +enum crs_axis { + AXIS_X, + AXIS_Y, +}; + +struct crs_node { + enum crs_nodetype type; + // union value depends on crs_node.type + union { + WINDOW *win; + struct { + enum crs_axis axis; + float ratio; + struct crs_nodedims *dims; + struct crs_node *child[NODE_CHILD_N]; + }; + }; +}; + +/* === External Interface === */ + +#endif /* CURSETREE_NODE_H */ diff --git a/cursetree/pty/_pty.h b/cursetree/pty/_pty.h index 3c80b95..652178f 100644 --- a/cursetree/pty/_pty.h +++ b/cursetree/pty/_pty.h @@ -1,5 +1,5 @@ -#ifndef DORNE_MKPTY_H -#define DORNE_MKPTY_H +#ifndef CURSETREE_PTY_H +#define CURSETREE_PTY_H #include #include @@ -27,4 +27,4 @@ int setptsxy(const unsigned short rows, const unsigned short cols, const int fds); int getptsxy(unsigned short *rows, unsigned short *cols, const int fds); -#endif /* DORNE_MKPTY_H */ +#endif /* CURSETREE_PTY_H */ diff --git a/cursetree/pty/child.h b/cursetree/pty/child.h index 6ec3978..294b1c3 100644 --- a/cursetree/pty/child.h +++ b/cursetree/pty/child.h @@ -1,5 +1,5 @@ -#ifndef DORNE_CHILD_H -#define DORNE_CHILD_H +#ifndef CURSETREE_CHILD_H +#define CURSETREE_CHILD_H #include @@ -11,4 +11,4 @@ struct d_child { int spawnchild(struct d_child *child); -#endif /* DORNE_CHILD_H */ +#endif /* CURSETREE_CHILD_H */ diff --git a/cursetree/pty/epty.h b/cursetree/pty/epty.h index 8dd18e4..fa013bb 100644 --- a/cursetree/pty/epty.h +++ b/cursetree/pty/epty.h @@ -1,8 +1,8 @@ -#ifndef DORNE_EPTY_H -#define DORNE_EPTY_H +#ifndef CURSETREE_EPTY_H +#define CURSETREE_EPTY_H #include pid_t forkepty(int *fdmx, int *fderr); -#endif /* DORNE_EPTY_H */ +#endif /* CURSETREE_EPTY_H */ From 2d76f8221e9f04b988ed774007de52aead1a6353 Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Sat, 13 Sep 2025 11:16:28 +1000 Subject: [PATCH 15/26] refactor cursetree into node.c & tree.c --- cursetree/cursetree.c | 252 +----------------------------------------- cursetree/cursetree.h | 55 +-------- cursetree/node.c | 232 ++++++++++++++++++++++++++++++++++++++ cursetree/node.h | 8 ++ cursetree/tree.c | 30 +++++ cursetree/tree.h | 15 +++ 6 files changed, 288 insertions(+), 304 deletions(-) create mode 100644 cursetree/node.c create mode 100644 cursetree/tree.c create mode 100644 cursetree/tree.h diff --git a/cursetree/cursetree.c b/cursetree/cursetree.c index 8d80831..a5f178f 100644 --- a/cursetree/cursetree.c +++ b/cursetree/cursetree.c @@ -1,264 +1,16 @@ -#include #include -#include #include "ncrswrap.h" -#include "cursetree.h" - -/* Internal allocator method for crs_node structures. - */ -static inline struct crs_node *__alloc_node(const enum crs_nodetype type) { - struct crs_node *node = (struct crs_node *)malloc(sizeof(struct crs_node)); - node->type = type; - return node; -} - -/* Construct a new window node (crs_node of type NODE_WIN). - */ -static struct crs_node *init_window_node(WINDOW *const win) { - struct crs_node *node = __alloc_node(NODE_WINDOW); - node->win = win; - - return node; -} - -static struct crs_node * -auto_window_node(const struct crs_nodedims *const dims) { - WINDOW *win = newwin(dims->height, dims->width, dims->y, dims->x); - return init_window_node(win); -} - -static struct crs_node *init_abstract_node(struct crs_node *const node0, - struct crs_node *const node1, - const enum crs_axis axis, - const float ratio, - struct crs_nodedims *const dims) { - struct crs_node *node = __alloc_node(NODE_ABSTRACT); - node->axis = axis; - node->ratio = ratio; - node->dims = dims; - - node->child[0] = node0; - node->child[1] = node1; - return node; -} - -static void destroy_node(struct crs_node *const node) { - if (node->type == NODE_WINDOW) { - /* Window Node */ - delwin(node->win); - goto end; - } - /* Abstract Node */ - assert(node->type == NODE_ABSTRACT); - destroy_node(node->child[0]); - destroy_node(node->child[1]); - free(node->dims); - -end: - free(node); -} - -static inline struct crs_nodedims *__alloc_dims(int x, int y, int width, - int height) { - struct crs_nodedims *dims; - - dims = (struct crs_nodedims *)malloc(sizeof(struct crs_nodedims)); - *dims = (struct crs_nodedims){ - .x = x, - .y = y, - .width = width, - .height = height, - }; - return dims; -} - -static inline struct crs_nodedims * -__dup_dims(const struct crs_nodedims *const dims) { - struct crs_nodedims *dup; - dup = (struct crs_nodedims *)malloc(sizeof(struct crs_nodedims)); - memcpy(dup, dims, sizeof(struct crs_nodedims)); - - return dup; -} - -static inline struct crs_nodedims *termdims(void) { - struct crs_nodedims *dims = __alloc_dims(0, 0, 0, 0); - termsize(dims->width, dims->height); - - return dims; -} - -static inline void nodesize(const struct crs_node *const node, int *const width, - int *const height) { - if (node->type == NODE_WINDOW) { - /* Window Node */ - getmaxyx(node->win, *height, *width); - } else { - /* Abstract Node */ - assert(node->type == NODE_ABSTRACT); - *width = node->dims->width; - *height = node->dims->height; - } -} - -static inline struct crs_nodedims *nodedims(const struct crs_node *const node) { - struct crs_nodedims *dims; - if (node->type == NODE_WINDOW) { - /* Window Node */ - dims = (struct crs_nodedims *)malloc(sizeof(struct crs_nodedims)); - getbegyx(node->win, dims->y, dims->x); - getmaxyx(node->win, dims->height, dims->width); - } else { - /* Abstract Node */ - assert(node->type == NODE_ABSTRACT); - dims = __dup_dims(node->dims); - } - - return dims; -} - -/* Calculate the dimensions for nodes resulting from a bifurcation. - * Returns 0 on success, and 1 on failure if any width/height are 0 characters. - * WARNING: This function does not guarantee the x,y positions returned - * WARNING: are valid screen coordinates. - */ -static int bifurcate_dims(const struct crs_nodedims *const parent_dims, - const enum crs_axis axis, const float ratio, - struct crs_nodedims **const dims0, - struct crs_nodedims **const dims1) { - assert(0 < ratio && ratio < 1); - struct crs_nodedims *_dims0, *_dims1; - - _dims0 = __dup_dims(parent_dims); - _dims1 = __dup_dims(parent_dims); - - if (axis == AXIS_X) { - _dims0->width *= ratio; - _dims1->width -= _dims0->width; - _dims1->x += _dims0->width; - } else { - _dims0->height *= ratio; - _dims1->height -= _dims0->height; - _dims1->y += _dims0->height; - } - - if (!_dims0->width || !_dims0->height || !_dims1->width || !_dims1->height) - return 1; - - // propagate bifurcated dimensions - *dims0 = _dims0; - *dims1 = _dims1; - return 0; -} - -/* NOTE: resize_node calls wnoutrefresh(3x), which expects - * NOTE: a call doupdate(3x) call afterwards to flush ncurses - * NOTE: virtual screen to the physical screen. - */ -static void resize_node(struct crs_node *const node, - struct crs_nodedims *const new_dims) { - if (node->type == NODE_WINDOW) { - /* Window Node */ - resizemv_window(new_dims->x, new_dims->y, new_dims->width, new_dims->height, - node->win); - free(new_dims); - - wnoutrefresh(node->win); - } else { - /* Abstract Node */ - assert(node->type == NODE_ABSTRACT); - struct crs_nodedims *dims0, *dims1; - - free(node->dims); - node->dims = new_dims; - bifurcate_dims(new_dims, node->axis, node->ratio, &dims0, &dims1); - - resize_node(node->child[0], dims0); - resize_node(node->child[1], dims1); - } -} - -/* Subdivide a window node's allocated region into two window nodes - * replacing the original node with an abstract node. - * Parameters: - * axis - controls which direction the subdivision occurs - * invert_axis - invert index of the original node in the new abstract node - */ -void bifurcate_window_node(struct crs_node **const node, - const enum crs_axis axis, const int invert_axis, - const float ratio) { - assert((*node)->type == NODE_WINDOW); - struct crs_nodedims *dims0, *dims1; - struct crs_node *node0, *node1; - - struct crs_nodedims *original_dims = nodedims(*node); - if (bifurcate_dims(original_dims, axis, ratio, &dims0, &dims1)) { - /* TODO: handle this error properly */ - free(original_dims); - exit(1); - return; - } - - if (invert_axis) { - /* Inverted Bifurcation */ - node0 = auto_window_node(dims0); - node1 = *node; - resize_node(node1, dims1); - } else { - /* Non-Inverted Bifurcation */ - node0 = *node; - node1 = auto_window_node(dims1); - resize_node(node0, dims0); - } - - *node = init_abstract_node(node0, node1, axis, ratio, original_dims); -} - -/* Collapse an abstract node, killing one child node and resizing - * the other to take its place. - */ -static void collapse_abstract_node(struct crs_node **const node, - const int collapse_i) { - assert((*node)->type == NODE_ABSTRACT); - assert(0 <= collapse_i && collapse_i < NODE_CHILD_N); - - // WARNING: only works for NODE_CHILD_N=2 (binary trees) - destroy_node((*node)->child[!collapse_i]); - - struct crs_node *collapse_target = (*node)->child[collapse_i]; - free(*node); - *node = collapse_target; -} - -/* - */ -static struct crs_node *init_root_node(void) { - WINDOW *rootwin; - - rootwin = root_window(); - return init_window_node(rootwin); -} +#include "tree.h" int ct_init(struct crs_tree **const tree) { - *tree = (struct crs_tree *)malloc(sizeof(struct crs_tree)); - /* Initialise NCurses Library & Root Node */ init_ncurses(); - (*tree)->root = init_root_node(); + init_tree(tree); return EXIT_SUCCESS; } -void destroy_tree(struct crs_tree *const tree) { - destroy_node(tree->root); - endwin(); - free(tree); -} - -void resize_tree(struct crs_tree *const tree, struct crs_nodedims *const dims) { - resize_node(tree->root, dims); -} void ct_update(struct crs_tree *const tree) { struct crs_nodedims * term_dims; diff --git a/cursetree/cursetree.h b/cursetree/cursetree.h index 74d804f..abbf9e9 100644 --- a/cursetree/cursetree.h +++ b/cursetree/cursetree.h @@ -1,63 +1,10 @@ #ifndef CURSETREE_CURSETREE_H #define CURSETREE_CURSETREE_H -#ifndef __NCURSES_H -typedef struct _win_st WINDOW; -#endif /* __NCURSES_H */ - -#define NODE_CHILD_N 2 - -/* MACRO: - * Get widnow node start x,y coordinates, width, & height. - * void NODEDIMS(struct crs_node *node, struct crs_nodedims dims); - */ -#define GET_WNODEDIMS(dims, node) \ - (getbegyx((node)->win, dims.y, dims.x)); \ - getmaxyx((node)->win, dims.y, dims.x) - -enum crs_nodetype { - NODE_WINDOW, - NODE_ABSTRACT, -}; - -/* Stores a node's starting x,y coordinates, width, & height. - * NOTE: Intended for interfunction communication. - */ -struct crs_nodedims { - int x, y, width, height; -}; - -enum crs_axis { - AXIS_X, - AXIS_Y, -}; - -struct crs_node { - enum crs_nodetype type; - // union value depends on crs_node.type - union { - WINDOW *win; - struct { - enum crs_axis axis; - float ratio; - struct crs_nodedims *dims; - struct crs_node *child[NODE_CHILD_N]; - }; - }; -}; - -struct crs_tree { - struct crs_node *root; -}; +#include "tree.h" /* === External Interface === */ int ct_init(struct crs_tree **const tree); -void destroy_tree(struct crs_tree *const tree); -void resize_tree(struct crs_tree *const tree, struct crs_nodedims *const dims); void ct_update(struct crs_tree *const tree); -void bifurcate_window_node(struct crs_node **const node, - const enum crs_axis axis, const int invert_axis, - const float ratio); - #endif /* CURSETREE_CURSETREE_H */ diff --git a/cursetree/node.c b/cursetree/node.c new file mode 100644 index 0000000..38f7129 --- /dev/null +++ b/cursetree/node.c @@ -0,0 +1,232 @@ +#include +#include +#include + +#include "ncrswrap.h" +#include "node.h" + +/* Internal allocator method for crs_node structures. + */ +static inline struct crs_node *__alloc_node(const enum crs_nodetype type) { + struct crs_node *node = (struct crs_node *)malloc(sizeof(struct crs_node)); + node->type = type; + return node; +} + +/* Construct a new window node (crs_node of type NODE_WIN). + */ +struct crs_node *init_window_node(WINDOW *const win) { + struct crs_node *node = __alloc_node(NODE_WINDOW); + node->win = win; + + return node; +} + +static struct crs_node * +auto_window_node(const struct crs_nodedims *const dims) { + WINDOW *win = newwin(dims->height, dims->width, dims->y, dims->x); + return init_window_node(win); +} + +static struct crs_node *init_abstract_node(struct crs_node *const node0, + struct crs_node *const node1, + const enum crs_axis axis, + const float ratio, + struct crs_nodedims *const dims) { + struct crs_node *node = __alloc_node(NODE_ABSTRACT); + node->axis = axis; + node->ratio = ratio; + node->dims = dims; + + node->child[0] = node0; + node->child[1] = node1; + return node; +} + +void destroy_node(struct crs_node *const node) { + if (node->type == NODE_WINDOW) { + /* Window Node */ + delwin(node->win); + goto end; + } + /* Abstract Node */ + assert(node->type == NODE_ABSTRACT); + destroy_node(node->child[0]); + destroy_node(node->child[1]); + free(node->dims); + +end: + free(node); +} + +static inline struct crs_nodedims *__alloc_dims(int x, int y, int width, + int height) { + struct crs_nodedims *dims; + + dims = (struct crs_nodedims *)malloc(sizeof(struct crs_nodedims)); + *dims = (struct crs_nodedims){ + .x = x, + .y = y, + .width = width, + .height = height, + }; + return dims; +} + +static inline struct crs_nodedims * +__dup_dims(const struct crs_nodedims *const dims) { + struct crs_nodedims *dup; + dup = (struct crs_nodedims *)malloc(sizeof(struct crs_nodedims)); + memcpy(dup, dims, sizeof(struct crs_nodedims)); + + return dup; +} + +static inline struct crs_nodedims *termdims(void) { + struct crs_nodedims *dims = __alloc_dims(0, 0, 0, 0); + termsize(dims->width, dims->height); + + return dims; +} + +static inline void nodesize(const struct crs_node *const node, int *const width, + int *const height) { + if (node->type == NODE_WINDOW) { + /* Window Node */ + getmaxyx(node->win, *height, *width); + } else { + /* Abstract Node */ + assert(node->type == NODE_ABSTRACT); + *width = node->dims->width; + *height = node->dims->height; + } +} + +static inline struct crs_nodedims *nodedims(const struct crs_node *const node) { + struct crs_nodedims *dims; + if (node->type == NODE_WINDOW) { + /* Window Node */ + dims = (struct crs_nodedims *)malloc(sizeof(struct crs_nodedims)); + getbegyx(node->win, dims->y, dims->x); + getmaxyx(node->win, dims->height, dims->width); + } else { + /* Abstract Node */ + assert(node->type == NODE_ABSTRACT); + dims = __dup_dims(node->dims); + } + + return dims; +} + +/* Calculate the dimensions for nodes resulting from a bifurcation. + * Returns 0 on success, and 1 on failure if any width/height are 0 characters. + * WARNING: This function does not guarantee the x,y positions returned + * WARNING: are valid screen coordinates. + */ +static int bifurcate_dims(const struct crs_nodedims *const parent_dims, + const enum crs_axis axis, const float ratio, + struct crs_nodedims **const dims0, + struct crs_nodedims **const dims1) { + assert(0 < ratio && ratio < 1); + struct crs_nodedims *_dims0, *_dims1; + + _dims0 = __dup_dims(parent_dims); + _dims1 = __dup_dims(parent_dims); + + if (axis == AXIS_X) { + _dims0->width *= ratio; + _dims1->width -= _dims0->width; + _dims1->x += _dims0->width; + } else { + _dims0->height *= ratio; + _dims1->height -= _dims0->height; + _dims1->y += _dims0->height; + } + + if (!_dims0->width || !_dims0->height || !_dims1->width || !_dims1->height) + return 1; + + // propagate bifurcated dimensions + *dims0 = _dims0; + *dims1 = _dims1; + return 0; +} + +/* NOTE: resize_node calls wnoutrefresh(3x), which expects + * NOTE: a call doupdate(3x) call afterwards to flush ncurses + * NOTE: virtual screen to the physical screen. + */ +void resize_node(struct crs_node *const node, + struct crs_nodedims *const new_dims) { + if (node->type == NODE_WINDOW) { + /* Window Node */ + resizemv_window(new_dims->x, new_dims->y, new_dims->width, new_dims->height, + node->win); + free(new_dims); + + wnoutrefresh(node->win); + } else { + /* Abstract Node */ + assert(node->type == NODE_ABSTRACT); + struct crs_nodedims *dims0, *dims1; + + free(node->dims); + node->dims = new_dims; + bifurcate_dims(new_dims, node->axis, node->ratio, &dims0, &dims1); + + resize_node(node->child[0], dims0); + resize_node(node->child[1], dims1); + } +} + +/* Subdivide a window node's allocated region into two window nodes + * replacing the original node with an abstract node. + * Parameters: + * axis - controls which direction the subdivision occurs + * invert_axis - invert index of the original node in the new abstract node + */ +void bifurcate_window_node(struct crs_node **const node, + const enum crs_axis axis, const int invert_axis, + const float ratio) { + assert((*node)->type == NODE_WINDOW); + struct crs_nodedims *dims0, *dims1; + struct crs_node *node0, *node1; + + struct crs_nodedims *original_dims = nodedims(*node); + if (bifurcate_dims(original_dims, axis, ratio, &dims0, &dims1)) { + /* TODO: handle this error properly */ + free(original_dims); + exit(1); + return; + } + + if (invert_axis) { + /* Inverted Bifurcation */ + node0 = auto_window_node(dims0); + node1 = *node; + resize_node(node1, dims1); + } else { + /* Non-Inverted Bifurcation */ + node0 = *node; + node1 = auto_window_node(dims1); + resize_node(node0, dims0); + } + + *node = init_abstract_node(node0, node1, axis, ratio, original_dims); +} + +/* Collapse an abstract node, killing one child node and resizing + * the other to take its place. + */ +static void collapse_abstract_node(struct crs_node **const node, + const int collapse_i) { + assert((*node)->type == NODE_ABSTRACT); + assert(0 <= collapse_i && collapse_i < NODE_CHILD_N); + + // WARNING: only works for NODE_CHILD_N=2 (binary trees) + destroy_node((*node)->child[!collapse_i]); + + struct crs_node *collapse_target = (*node)->child[collapse_i]; + free(*node); + *node = collapse_target; +} diff --git a/cursetree/node.h b/cursetree/node.h index bf789c3..bf94f2b 100644 --- a/cursetree/node.h +++ b/cursetree/node.h @@ -47,5 +47,13 @@ struct crs_node { }; /* === External Interface === */ +struct crs_node *init_window_node(WINDOW *const win); +void destroy_node(struct crs_node *const node); +void resize_node(struct crs_node *const node, + struct crs_nodedims *const new_dims); + +void bifurcate_window_node(struct crs_node **const node, + const enum crs_axis axis, const int invert_axis, + const float ratio); #endif /* CURSETREE_NODE_H */ diff --git a/cursetree/tree.c b/cursetree/tree.c new file mode 100644 index 0000000..61c268b --- /dev/null +++ b/cursetree/tree.c @@ -0,0 +1,30 @@ +#include + +#include "ncrswrap.h" +#include "tree.h" + +/* + */ +static struct crs_node *init_root_node(void) { + WINDOW *rootwin; + + rootwin = root_window(); + return init_window_node(rootwin); +} + +int init_tree(struct crs_tree **const tree) { + *tree = (struct crs_tree *)malloc(sizeof(struct crs_tree)); + (*tree)->root = init_root_node(); +return EXIT_SUCCESS; +} + +void destroy_tree(struct crs_tree *const tree) { + destroy_node(tree->root); + endwin(); + free(tree); +} + +void resize_tree(struct crs_tree *const tree, struct crs_nodedims *const dims) { + resize_node(tree->root, dims); +} + diff --git a/cursetree/tree.h b/cursetree/tree.h new file mode 100644 index 0000000..4b07547 --- /dev/null +++ b/cursetree/tree.h @@ -0,0 +1,15 @@ +#ifndef CURSETREE_TREE_H +#define CURSETREE_TREE_H + +#include "node.h" + +struct crs_tree { + struct crs_node *root; +}; + +/* === External Interface === */ +int init_tree(struct crs_tree **const tree); +void destroy_tree(struct crs_tree *const tree); +void resize_tree(struct crs_tree *const tree, struct crs_nodedims *const dims); + +#endif /* CURSETREE_TREE_H */ From 1dd5dd79c824740bc7a06432f8d1849e5d80ea18 Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Sat, 13 Sep 2025 11:21:34 +1000 Subject: [PATCH 16/26] rename crs_* types to ct_* --- cursetree/cursetree.c | 6 +-- cursetree/cursetree.h | 4 +- cursetree/node.c | 88 +++++++++++++++++++++---------------------- cursetree/node.h | 32 ++++++++-------- cursetree/tree.c | 10 ++--- cursetree/tree.h | 10 ++--- 6 files changed, 75 insertions(+), 75 deletions(-) diff --git a/cursetree/cursetree.c b/cursetree/cursetree.c index a5f178f..4e7befd 100644 --- a/cursetree/cursetree.c +++ b/cursetree/cursetree.c @@ -3,7 +3,7 @@ #include "ncrswrap.h" #include "tree.h" -int ct_init(struct crs_tree **const tree) { +int ct_init(struct ct_tree **const tree) { /* Initialise NCurses Library & Root Node */ init_ncurses(); init_tree(tree); @@ -12,8 +12,8 @@ int ct_init(struct crs_tree **const tree) { } -void ct_update(struct crs_tree *const tree) { - struct crs_nodedims * term_dims; +void ct_update(struct ct_tree *const tree) { + struct ct_dims * term_dims; int term_width, term_height; const int key = wgetch(curscr); diff --git a/cursetree/cursetree.h b/cursetree/cursetree.h index abbf9e9..2a31717 100644 --- a/cursetree/cursetree.h +++ b/cursetree/cursetree.h @@ -4,7 +4,7 @@ #include "tree.h" /* === External Interface === */ -int ct_init(struct crs_tree **const tree); -void ct_update(struct crs_tree *const tree); +int ct_init(struct ct_tree **const tree); +void ct_update(struct ct_tree *const tree); #endif /* CURSETREE_CURSETREE_H */ diff --git a/cursetree/node.c b/cursetree/node.c index 38f7129..282b363 100644 --- a/cursetree/node.c +++ b/cursetree/node.c @@ -5,35 +5,35 @@ #include "ncrswrap.h" #include "node.h" -/* Internal allocator method for crs_node structures. +/* Internal allocator method for ct_node structures. */ -static inline struct crs_node *__alloc_node(const enum crs_nodetype type) { - struct crs_node *node = (struct crs_node *)malloc(sizeof(struct crs_node)); +static inline struct ct_node *__alloc_node(const enum ct_nodetype type) { + struct ct_node *node = (struct ct_node *)malloc(sizeof(struct ct_node)); node->type = type; return node; } -/* Construct a new window node (crs_node of type NODE_WIN). +/* Construct a new window node (ct_node of type NODE_WIN). */ -struct crs_node *init_window_node(WINDOW *const win) { - struct crs_node *node = __alloc_node(NODE_WINDOW); +struct ct_node *init_window_node(WINDOW *const win) { + struct ct_node *node = __alloc_node(NODE_WINDOW); node->win = win; return node; } -static struct crs_node * -auto_window_node(const struct crs_nodedims *const dims) { +static struct ct_node * +auto_window_node(const struct ct_dims *const dims) { WINDOW *win = newwin(dims->height, dims->width, dims->y, dims->x); return init_window_node(win); } -static struct crs_node *init_abstract_node(struct crs_node *const node0, - struct crs_node *const node1, - const enum crs_axis axis, +static struct ct_node *init_abstract_node(struct ct_node *const node0, + struct ct_node *const node1, + const enum ct_axis axis, const float ratio, - struct crs_nodedims *const dims) { - struct crs_node *node = __alloc_node(NODE_ABSTRACT); + struct ct_dims *const dims) { + struct ct_node *node = __alloc_node(NODE_ABSTRACT); node->axis = axis; node->ratio = ratio; node->dims = dims; @@ -43,7 +43,7 @@ static struct crs_node *init_abstract_node(struct crs_node *const node0, return node; } -void destroy_node(struct crs_node *const node) { +void destroy_node(struct ct_node *const node) { if (node->type == NODE_WINDOW) { /* Window Node */ delwin(node->win); @@ -59,12 +59,12 @@ end: free(node); } -static inline struct crs_nodedims *__alloc_dims(int x, int y, int width, +static inline struct ct_dims *__alloc_dims(int x, int y, int width, int height) { - struct crs_nodedims *dims; + struct ct_dims *dims; - dims = (struct crs_nodedims *)malloc(sizeof(struct crs_nodedims)); - *dims = (struct crs_nodedims){ + dims = (struct ct_dims *)malloc(sizeof(struct ct_dims)); + *dims = (struct ct_dims){ .x = x, .y = y, .width = width, @@ -73,23 +73,23 @@ static inline struct crs_nodedims *__alloc_dims(int x, int y, int width, return dims; } -static inline struct crs_nodedims * -__dup_dims(const struct crs_nodedims *const dims) { - struct crs_nodedims *dup; - dup = (struct crs_nodedims *)malloc(sizeof(struct crs_nodedims)); - memcpy(dup, dims, sizeof(struct crs_nodedims)); +static inline struct ct_dims * +__dup_dims(const struct ct_dims *const dims) { + struct ct_dims *dup; + dup = (struct ct_dims *)malloc(sizeof(struct ct_dims)); + memcpy(dup, dims, sizeof(struct ct_dims)); return dup; } -static inline struct crs_nodedims *termdims(void) { - struct crs_nodedims *dims = __alloc_dims(0, 0, 0, 0); +static inline struct ct_dims *termdims(void) { + struct ct_dims *dims = __alloc_dims(0, 0, 0, 0); termsize(dims->width, dims->height); return dims; } -static inline void nodesize(const struct crs_node *const node, int *const width, +static inline void nodesize(const struct ct_node *const node, int *const width, int *const height) { if (node->type == NODE_WINDOW) { /* Window Node */ @@ -102,11 +102,11 @@ static inline void nodesize(const struct crs_node *const node, int *const width, } } -static inline struct crs_nodedims *nodedims(const struct crs_node *const node) { - struct crs_nodedims *dims; +static inline struct ct_dims *nodedims(const struct ct_node *const node) { + struct ct_dims *dims; if (node->type == NODE_WINDOW) { /* Window Node */ - dims = (struct crs_nodedims *)malloc(sizeof(struct crs_nodedims)); + dims = (struct ct_dims *)malloc(sizeof(struct ct_dims)); getbegyx(node->win, dims->y, dims->x); getmaxyx(node->win, dims->height, dims->width); } else { @@ -123,12 +123,12 @@ static inline struct crs_nodedims *nodedims(const struct crs_node *const node) { * WARNING: This function does not guarantee the x,y positions returned * WARNING: are valid screen coordinates. */ -static int bifurcate_dims(const struct crs_nodedims *const parent_dims, - const enum crs_axis axis, const float ratio, - struct crs_nodedims **const dims0, - struct crs_nodedims **const dims1) { +static int bifurcate_dims(const struct ct_dims *const parent_dims, + const enum ct_axis axis, const float ratio, + struct ct_dims **const dims0, + struct ct_dims **const dims1) { assert(0 < ratio && ratio < 1); - struct crs_nodedims *_dims0, *_dims1; + struct ct_dims *_dims0, *_dims1; _dims0 = __dup_dims(parent_dims); _dims1 = __dup_dims(parent_dims); @@ -156,8 +156,8 @@ static int bifurcate_dims(const struct crs_nodedims *const parent_dims, * NOTE: a call doupdate(3x) call afterwards to flush ncurses * NOTE: virtual screen to the physical screen. */ -void resize_node(struct crs_node *const node, - struct crs_nodedims *const new_dims) { +void resize_node(struct ct_node *const node, + struct ct_dims *const new_dims) { if (node->type == NODE_WINDOW) { /* Window Node */ resizemv_window(new_dims->x, new_dims->y, new_dims->width, new_dims->height, @@ -168,7 +168,7 @@ void resize_node(struct crs_node *const node, } else { /* Abstract Node */ assert(node->type == NODE_ABSTRACT); - struct crs_nodedims *dims0, *dims1; + struct ct_dims *dims0, *dims1; free(node->dims); node->dims = new_dims; @@ -185,14 +185,14 @@ void resize_node(struct crs_node *const node, * axis - controls which direction the subdivision occurs * invert_axis - invert index of the original node in the new abstract node */ -void bifurcate_window_node(struct crs_node **const node, - const enum crs_axis axis, const int invert_axis, +void bifurcate_window_node(struct ct_node **const node, + const enum ct_axis axis, const int invert_axis, const float ratio) { assert((*node)->type == NODE_WINDOW); - struct crs_nodedims *dims0, *dims1; - struct crs_node *node0, *node1; + struct ct_dims *dims0, *dims1; + struct ct_node *node0, *node1; - struct crs_nodedims *original_dims = nodedims(*node); + struct ct_dims *original_dims = nodedims(*node); if (bifurcate_dims(original_dims, axis, ratio, &dims0, &dims1)) { /* TODO: handle this error properly */ free(original_dims); @@ -218,7 +218,7 @@ void bifurcate_window_node(struct crs_node **const node, /* Collapse an abstract node, killing one child node and resizing * the other to take its place. */ -static void collapse_abstract_node(struct crs_node **const node, +static void collapse_abstract_node(struct ct_node **const node, const int collapse_i) { assert((*node)->type == NODE_ABSTRACT); assert(0 <= collapse_i && collapse_i < NODE_CHILD_N); @@ -226,7 +226,7 @@ static void collapse_abstract_node(struct crs_node **const node, // WARNING: only works for NODE_CHILD_N=2 (binary trees) destroy_node((*node)->child[!collapse_i]); - struct crs_node *collapse_target = (*node)->child[collapse_i]; + struct ct_node *collapse_target = (*node)->child[collapse_i]; free(*node); *node = collapse_target; } diff --git a/cursetree/node.h b/cursetree/node.h index bf94f2b..439d452 100644 --- a/cursetree/node.h +++ b/cursetree/node.h @@ -9,13 +9,13 @@ typedef struct _win_st WINDOW; /* MACRO: * Get widnow node start x,y coordinates, width, & height. - * void NODEDIMS(struct crs_node *node, struct crs_nodedims dims); + * void NODEDIMS(struct ct_node *node, struct ct_dims dims); */ #define GET_WNODEDIMS(dims, node) \ (getbegyx((node)->win, dims.y, dims.x)); \ getmaxyx((node)->win, dims.y, dims.x) -enum crs_nodetype { +enum ct_nodetype { NODE_WINDOW, NODE_ABSTRACT, }; @@ -23,37 +23,37 @@ enum crs_nodetype { /* Stores a node's starting x,y coordinates, width, & height. * NOTE: Intended for interfunction communication. */ -struct crs_nodedims { +struct ct_dims { int x, y, width, height; }; -enum crs_axis { +enum ct_axis { AXIS_X, AXIS_Y, }; -struct crs_node { - enum crs_nodetype type; - // union value depends on crs_node.type +struct ct_node { + enum ct_nodetype type; + // union value depends on ct_node.type union { WINDOW *win; struct { - enum crs_axis axis; + enum ct_axis axis; float ratio; - struct crs_nodedims *dims; - struct crs_node *child[NODE_CHILD_N]; + struct ct_dims *dims; + struct ct_node *child[NODE_CHILD_N]; }; }; }; /* === External Interface === */ -struct crs_node *init_window_node(WINDOW *const win); -void destroy_node(struct crs_node *const node); -void resize_node(struct crs_node *const node, - struct crs_nodedims *const new_dims); +struct ct_node *init_window_node(WINDOW *const win); +void destroy_node(struct ct_node *const node); +void resize_node(struct ct_node *const node, + struct ct_dims *const new_dims); -void bifurcate_window_node(struct crs_node **const node, - const enum crs_axis axis, const int invert_axis, +void bifurcate_window_node(struct ct_node **const node, + const enum ct_axis axis, const int invert_axis, const float ratio); #endif /* CURSETREE_NODE_H */ diff --git a/cursetree/tree.c b/cursetree/tree.c index 61c268b..acb2a25 100644 --- a/cursetree/tree.c +++ b/cursetree/tree.c @@ -5,26 +5,26 @@ /* */ -static struct crs_node *init_root_node(void) { +static struct ct_node *init_root_node(void) { WINDOW *rootwin; rootwin = root_window(); return init_window_node(rootwin); } -int init_tree(struct crs_tree **const tree) { - *tree = (struct crs_tree *)malloc(sizeof(struct crs_tree)); +int init_tree(struct ct_tree **const tree) { + *tree = (struct ct_tree *)malloc(sizeof(struct ct_tree)); (*tree)->root = init_root_node(); return EXIT_SUCCESS; } -void destroy_tree(struct crs_tree *const tree) { +void destroy_tree(struct ct_tree *const tree) { destroy_node(tree->root); endwin(); free(tree); } -void resize_tree(struct crs_tree *const tree, struct crs_nodedims *const dims) { +void resize_tree(struct ct_tree *const tree, struct ct_dims *const dims) { resize_node(tree->root, dims); } diff --git a/cursetree/tree.h b/cursetree/tree.h index 4b07547..a170143 100644 --- a/cursetree/tree.h +++ b/cursetree/tree.h @@ -3,13 +3,13 @@ #include "node.h" -struct crs_tree { - struct crs_node *root; +struct ct_tree { + struct ct_node *root; }; /* === External Interface === */ -int init_tree(struct crs_tree **const tree); -void destroy_tree(struct crs_tree *const tree); -void resize_tree(struct crs_tree *const tree, struct crs_nodedims *const dims); +int init_tree(struct ct_tree **const tree); +void destroy_tree(struct ct_tree *const tree); +void resize_tree(struct ct_tree *const tree, struct ct_dims *const dims); #endif /* CURSETREE_TREE_H */ From b04f0b4aa3cd91342ca62dbfc981362096e1a137 Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Sat, 13 Sep 2025 12:44:58 +1000 Subject: [PATCH 17/26] hide behind ncrswrap.* isolate ct_dims implementation to dims.* --- cursetree/cursetree.c | 6 +-- cursetree/dims.c | 59 ++++++++++++++++++++++++++++ cursetree/dims.h | 23 +++++++++++ cursetree/ncrswrap.c | 36 +++++++++++++++-- cursetree/ncrswrap.h | 22 ++++++++--- cursetree/node.c | 89 +++---------------------------------------- cursetree/node.h | 18 ++------- cursetree/tree.c | 2 +- 8 files changed, 142 insertions(+), 113 deletions(-) create mode 100644 cursetree/dims.c create mode 100644 cursetree/dims.h diff --git a/cursetree/cursetree.c b/cursetree/cursetree.c index 4e7befd..6d9a87e 100644 --- a/cursetree/cursetree.c +++ b/cursetree/cursetree.c @@ -13,8 +13,6 @@ int ct_init(struct ct_tree **const tree) { void ct_update(struct ct_tree *const tree) { - struct ct_dims * term_dims; - int term_width, term_height; const int key = wgetch(curscr); /* ncurses binds a SIGWINCH handler if SIGWINCH has SIG_DFL disposition @@ -28,9 +26,7 @@ void ct_update(struct ct_tree *const tree) { wrefresh(tree->root->child[0]->win); return; case KEY_RESIZE: - termsize(term_width, term_height); - term_dims = __alloc_dims(0, 0, term_width, term_height); - resize_tree(tree, term_dims); + resize_tree(tree, termdims()); // flush ncurses virtual screen -> physical screen doupdate(); break; diff --git a/cursetree/dims.c b/cursetree/dims.c new file mode 100644 index 0000000..beaf2d9 --- /dev/null +++ b/cursetree/dims.c @@ -0,0 +1,59 @@ +#include +#include +#include + +#include "dims.h" + +struct ct_dims *new_dims(int x, int y, int width, int height) { + struct ct_dims *dims; + + dims = (struct ct_dims *)malloc(sizeof(struct ct_dims)); + *dims = (struct ct_dims){ + .x = x, + .y = y, + .width = width, + .height = height, + }; + return dims; +} + +struct ct_dims *dup_dims(const struct ct_dims *const dims) { + struct ct_dims *dup; + dup = (struct ct_dims *)malloc(sizeof(struct ct_dims)); + memcpy(dup, dims, sizeof(struct ct_dims)); + + return dup; +} + +/* Calculate the dimensions for nodes resulting from a bifurcation. + * Returns 0 on success, and 1 on failure if any width/height are 0 characters. + * WARNING: This function does not guarantee the x,y positions returned + * WARNING: are valid screen coordinates. + */ +int bifurcate_dims(const struct ct_dims *const parent_dims, + const enum ct_axis axis, const float ratio, + struct ct_dims **const dims0, struct ct_dims **const dims1) { + assert(0 < ratio && ratio < 1); + struct ct_dims *_dims0, *_dims1; + + _dims0 = dup_dims(parent_dims); + _dims1 = dup_dims(parent_dims); + + if (axis == AXIS_X) { + _dims0->width *= ratio; + _dims1->width -= _dims0->width; + _dims1->x += _dims0->width; + } else { + _dims0->height *= ratio; + _dims1->height -= _dims0->height; + _dims1->y += _dims0->height; + } + + if (!_dims0->width || !_dims0->height || !_dims1->width || !_dims1->height) + return 1; + + // propagate bifurcated dimensions + *dims0 = _dims0; + *dims1 = _dims1; + return 0; +} diff --git a/cursetree/dims.h b/cursetree/dims.h new file mode 100644 index 0000000..9ad42eb --- /dev/null +++ b/cursetree/dims.h @@ -0,0 +1,23 @@ +#ifndef CURSETREE_DIMS_H +#define CURSETREE_DIMS_H + +enum ct_axis { + AXIS_X, + AXIS_Y, +}; + +/* Stores a node's starting x,y coordinates, width, & height. + * NOTE: Intended for interfunction communication. + */ +struct ct_dims { + int x, y, width, height; +}; + +struct ct_dims *new_dims(int x, int y, int width, int height); +struct ct_dims *dup_dims(const struct ct_dims *const dims); + +int bifurcate_dims(const struct ct_dims *const parent_dims, + const enum ct_axis axis, const float ratio, + struct ct_dims **const dims0, struct ct_dims **const dims1); + +#endif /* CURSETREE_DIMS_H */ diff --git a/cursetree/ncrswrap.c b/cursetree/ncrswrap.c index 9623447..d3b89c8 100644 --- a/cursetree/ncrswrap.c +++ b/cursetree/ncrswrap.c @@ -1,6 +1,9 @@ #include #include +/* libncurses with wide-character support. */ +#include + #include "ncrswrap.h" #define CRS_LOCALE "en_US.UTF-8" @@ -24,6 +27,18 @@ int termmode(const enum crs_termmode mode) { } } +void termsize(int *const width, int *const height) { + *width = COLS; + *height = LINES; +} + +struct ct_dims *termdims(void) { + struct ct_dims *dims = new_dims(0, 0, 0, 0); + termsize(&dims->width, &dims->height); + + return dims; +} + /* Apply a default IO configuration for an ncurses WINDOW. */ static inline void __conf_window(WINDOW *const win) { @@ -40,7 +55,7 @@ void init_ncurses(void) { // ncurses expects a locale for consistent behaviour setlocale(LC_ALL, CRS_LOCALE); - /* NCurses Init */ + /* NCurses Initialisation */ initscr(); /* WARNING: no you shouldn't delwin(stdscr) it breaks everything... */ __conf_window(stdscr); @@ -63,15 +78,28 @@ WINDOW *new_window(const int x, const int y, const int width, return win; } -/* Initialise (with default IO configuration) the root ncurses WINDOW. - * NOTE: This is typically done via initscr(3x) and called "stdscr". +/* Initialise (with default IO configuration) a fullscreen ncurses WINDOW. */ -WINDOW *root_window(void) { +WINDOW *new_window_fs(void) { WINDOW *rootwin = new_window(0, 0, COLS, LINES); __conf_window(rootwin); return rootwin; } +void destroy_window(WINDOW *) __attribute__((alias("delwin"))); + +int winposx(WINDOW *) __attribute__((alias("getbegy"))); +int winposy(WINDOW *) __attribute__((alias("getbegy"))); +int winwidth(WINDOW *) __attribute__((alias("getmaxx"))); +int winheight(WINDOW *) __attribute__((alias("getmaxy"))); + +struct ct_dims *windims(WINDOW *win) { + int x, y, width, height; + winpos(win, x, y); + winsize(win, width, height); + return new_dims(x, y, width, height); +} + /* Resize and move (if resized successfully) an ncurses WINDOW. * Returns ERR (1) on fail, and OK (0) on success. * NOTE: Failure occurs if width or height <= 0, diff --git a/cursetree/ncrswrap.h b/cursetree/ncrswrap.h index b73f4b5..900a914 100644 --- a/cursetree/ncrswrap.h +++ b/cursetree/ncrswrap.h @@ -1,8 +1,11 @@ #ifndef CURSETREE_NCRSWRAP_H #define CURSETREE_NCRSWRAP_H -/* libncurses with wide-character support. */ -#include +#include "dims.h" + +# ifndef __NCURSES_H +typedef struct _win_st WINDOW; +# endif /* __NCURSES_H */ enum crs_termmode { /* tty cbreak mode */ @@ -15,13 +18,22 @@ enum crs_termmode { TMODE_NORAW, }; -#define termsize(width, height) (width=COLS); (height=LINES) - int termmode(const enum crs_termmode mode); +void termsize(int *const width, int *const height); void init_ncurses(void); + WINDOW *new_window(const int x, const int y, const int width, const int height); -WINDOW *root_window(void); +WINDOW *new_window_fs(void); +void destroy_window(WINDOW *); + +#define winpos(win, x, y) (x = winposx(win), y = winposy(win)) +#define winsize(win, width, height) (width = winwidth(win), height=winheight(win)) +int winposx(WINDOW *const); +int winposy(WINDOW *const); +int winwidth(WINDOW *const); +int winheight(WINDOW *const); +struct ct_dims *windims(WINDOW *win); int resizemv_window(const int x, const int y, const int width, const int height, WINDOW *const win); diff --git a/cursetree/node.c b/cursetree/node.c index 282b363..3a91945 100644 --- a/cursetree/node.c +++ b/cursetree/node.c @@ -24,7 +24,7 @@ struct ct_node *init_window_node(WINDOW *const win) { static struct ct_node * auto_window_node(const struct ct_dims *const dims) { - WINDOW *win = newwin(dims->height, dims->width, dims->y, dims->x); + WINDOW *win = new_window(dims->x, dims->y, dims->width, dims->height); return init_window_node(win); } @@ -46,7 +46,7 @@ static struct ct_node *init_abstract_node(struct ct_node *const node0, void destroy_node(struct ct_node *const node) { if (node->type == NODE_WINDOW) { /* Window Node */ - delwin(node->win); + destroy_window(node->win); goto end; } /* Abstract Node */ @@ -59,99 +59,20 @@ end: free(node); } -static inline struct ct_dims *__alloc_dims(int x, int y, int width, - int height) { - struct ct_dims *dims; - - dims = (struct ct_dims *)malloc(sizeof(struct ct_dims)); - *dims = (struct ct_dims){ - .x = x, - .y = y, - .width = width, - .height = height, - }; - return dims; -} - -static inline struct ct_dims * -__dup_dims(const struct ct_dims *const dims) { - struct ct_dims *dup; - dup = (struct ct_dims *)malloc(sizeof(struct ct_dims)); - memcpy(dup, dims, sizeof(struct ct_dims)); - - return dup; -} - -static inline struct ct_dims *termdims(void) { - struct ct_dims *dims = __alloc_dims(0, 0, 0, 0); - termsize(dims->width, dims->height); - - return dims; -} - -static inline void nodesize(const struct ct_node *const node, int *const width, - int *const height) { - if (node->type == NODE_WINDOW) { - /* Window Node */ - getmaxyx(node->win, *height, *width); - } else { - /* Abstract Node */ - assert(node->type == NODE_ABSTRACT); - *width = node->dims->width; - *height = node->dims->height; - } -} - static inline struct ct_dims *nodedims(const struct ct_node *const node) { struct ct_dims *dims; if (node->type == NODE_WINDOW) { /* Window Node */ - dims = (struct ct_dims *)malloc(sizeof(struct ct_dims)); - getbegyx(node->win, dims->y, dims->x); - getmaxyx(node->win, dims->height, dims->width); + dims = windims(node->win); } else { /* Abstract Node */ assert(node->type == NODE_ABSTRACT); - dims = __dup_dims(node->dims); + dims = dup_dims(node->dims); } return dims; } -/* Calculate the dimensions for nodes resulting from a bifurcation. - * Returns 0 on success, and 1 on failure if any width/height are 0 characters. - * WARNING: This function does not guarantee the x,y positions returned - * WARNING: are valid screen coordinates. - */ -static int bifurcate_dims(const struct ct_dims *const parent_dims, - const enum ct_axis axis, const float ratio, - struct ct_dims **const dims0, - struct ct_dims **const dims1) { - assert(0 < ratio && ratio < 1); - struct ct_dims *_dims0, *_dims1; - - _dims0 = __dup_dims(parent_dims); - _dims1 = __dup_dims(parent_dims); - - if (axis == AXIS_X) { - _dims0->width *= ratio; - _dims1->width -= _dims0->width; - _dims1->x += _dims0->width; - } else { - _dims0->height *= ratio; - _dims1->height -= _dims0->height; - _dims1->y += _dims0->height; - } - - if (!_dims0->width || !_dims0->height || !_dims1->width || !_dims1->height) - return 1; - - // propagate bifurcated dimensions - *dims0 = _dims0; - *dims1 = _dims1; - return 0; -} - /* NOTE: resize_node calls wnoutrefresh(3x), which expects * NOTE: a call doupdate(3x) call afterwards to flush ncurses * NOTE: virtual screen to the physical screen. @@ -185,7 +106,7 @@ void resize_node(struct ct_node *const node, * axis - controls which direction the subdivision occurs * invert_axis - invert index of the original node in the new abstract node */ -void bifurcate_window_node(struct ct_node **const node, +static void __bifurcate_window_node(struct ct_node **const node, const enum ct_axis axis, const int invert_axis, const float ratio) { assert((*node)->type == NODE_WINDOW); diff --git a/cursetree/node.h b/cursetree/node.h index 439d452..f10b70c 100644 --- a/cursetree/node.h +++ b/cursetree/node.h @@ -1,9 +1,11 @@ #ifndef CURSETREE_NODE_H #define CURSETREE_NODE_H -#ifndef __NCURSES_H +#include "dims.h" + +# ifndef __NCURSES_H typedef struct _win_st WINDOW; -#endif /* __NCURSES_H */ +# endif /* __NCURSES_H */ #define NODE_CHILD_N 2 @@ -20,18 +22,6 @@ enum ct_nodetype { NODE_ABSTRACT, }; -/* Stores a node's starting x,y coordinates, width, & height. - * NOTE: Intended for interfunction communication. - */ -struct ct_dims { - int x, y, width, height; -}; - -enum ct_axis { - AXIS_X, - AXIS_Y, -}; - struct ct_node { enum ct_nodetype type; // union value depends on ct_node.type diff --git a/cursetree/tree.c b/cursetree/tree.c index acb2a25..26175c3 100644 --- a/cursetree/tree.c +++ b/cursetree/tree.c @@ -8,7 +8,7 @@ static struct ct_node *init_root_node(void) { WINDOW *rootwin; - rootwin = root_window(); + rootwin = new_window_fs(); return init_window_node(rootwin); } From 759920a9cce0c522a4c9c7a9feba6db1e2d906d2 Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Sat, 13 Sep 2025 23:45:56 +1000 Subject: [PATCH 18/26] (UNDER CONSTRUCTION) cursetree is now a non-binary tree yippiegit add . also abstracted ncurses WINDOW into the ct_surface structure. and added ct_bounds to complement ct_dims. --- cursetree/dims.c | 109 +++++++++++--- cursetree/dims.h | 29 +++- cursetree/ncrswrap.c | 8 +- cursetree/ncrswrap.h | 13 +- cursetree/node.c | 342 ++++++++++++++++++++++++++++--------------- cursetree/node.h | 70 ++++----- cursetree/surface.c | 46 ++++++ cursetree/surface.h | 26 ++++ cursetree/tree.c | 26 ++-- 9 files changed, 476 insertions(+), 193 deletions(-) create mode 100644 cursetree/surface.c create mode 100644 cursetree/surface.h diff --git a/cursetree/dims.c b/cursetree/dims.c index beaf2d9..4fa4895 100644 --- a/cursetree/dims.c +++ b/cursetree/dims.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -25,35 +26,97 @@ struct ct_dims *dup_dims(const struct ct_dims *const dims) { return dup; } +static struct ct_bounds *__bounds(const enum ct_boundtype type, const int wmin, + const int wmax, const int hmin, + const int hmax) { + struct ct_bounds *bounds; + + bounds = (struct ct_bounds *)malloc(sizeof(struct ct_bounds)); + *bounds = (struct ct_bounds){ + .type = type, + .wmin = wmin, + .wmax = wmax, + .hmin = hmin, + .hmax = hmax, + }; + return bounds; +} + +static inline void clamp(int *const val, const int min, const int max, + const int do_ceiling) { + if (*val == __BOUND_UNLIMITED) + *val = do_ceiling ? max : min; + else if (*val > max) + *val = max; + else if (*val < min) + *val = min; +} +#define CLAMP_ABS(val, is_max) \ + (clamp(&val, __BOUND_ABS_MIN, __BOUND_ABS_MAX, is_max)) +#define CLAMP_REL(val, is_max) \ + (clamp(&val, __BOUND_REL_MIN, __BOUND_REL_MAX, is_max)) + +struct ct_bounds *bounds_none(void) { + return __bounds(BOUND_NONE, __BOUND_ABS_MIN, __BOUND_ABS_MAX, __BOUND_ABS_MIN, + __BOUND_ABS_MAX); +} + +struct ct_bounds *bounds_absolute(int wmin, int wmax, + int hmin, int hmax) { + CLAMP_ABS(wmin, false); + CLAMP_ABS(wmax, true); + CLAMP_ABS(hmin, false); + CLAMP_ABS(hmax, true); + return __bounds(BOUND_ABSOLUTE, wmin, wmax, hmin, hmax); +} + +struct ct_bounds *bounds_relative(int wmin, int wmax, + int hmin, int hmax) { + CLAMP_REL(wmin, false); + CLAMP_REL(wmax, true); + CLAMP_REL(hmin, false); + CLAMP_REL(hmax, true); + return __bounds(BOUND_RELATIVE, wmin, wmax, hmin, hmax); +} + +struct ct_bounds *dup_bounds(const struct ct_bounds *const bounds) { + struct ct_bounds *dup; + dup = (struct ct_bounds *)malloc(sizeof(struct ct_bounds)); + memcpy(dup, bounds, sizeof(struct ct_bounds)); + + return dup; +} + /* Calculate the dimensions for nodes resulting from a bifurcation. * Returns 0 on success, and 1 on failure if any width/height are 0 characters. * WARNING: This function does not guarantee the x,y positions returned * WARNING: are valid screen coordinates. */ -int bifurcate_dims(const struct ct_dims *const parent_dims, - const enum ct_axis axis, const float ratio, - struct ct_dims **const dims0, struct ct_dims **const dims1) { - assert(0 < ratio && ratio < 1); - struct ct_dims *_dims0, *_dims1; +// int bifurcate_dims(const struct ct_dims *const parent_dims, +// const enum ct_axis axis, const float ratio, +// struct ct_dims **const dims0, struct ct_dims **const +// dims1) { +// assert(0 < ratio && ratio < 1); +// struct ct_dims *_dims0, *_dims1; - _dims0 = dup_dims(parent_dims); - _dims1 = dup_dims(parent_dims); +// _dims0 = dup_dims(parent_dims); +// _dims1 = dup_dims(parent_dims); - if (axis == AXIS_X) { - _dims0->width *= ratio; - _dims1->width -= _dims0->width; - _dims1->x += _dims0->width; - } else { - _dims0->height *= ratio; - _dims1->height -= _dims0->height; - _dims1->y += _dims0->height; - } +// if (axis == AXIS_X) { +// _dims0->width *= ratio; +// _dims1->width -= _dims0->width; +// _dims1->x += _dims0->width; +// } else { +// _dims0->height *= ratio; +// _dims1->height -= _dims0->height; +// _dims1->y += _dims0->height; +// } - if (!_dims0->width || !_dims0->height || !_dims1->width || !_dims1->height) - return 1; +// if (!_dims0->width || !_dims0->height || !_dims1->width || !_dims1->height) +// return 1; - // propagate bifurcated dimensions - *dims0 = _dims0; - *dims1 = _dims1; - return 0; -} +// // propagate bifurcated dimensions +// *dims0 = _dims0; +// *dims1 = _dims1; +// return 0; +// } diff --git a/cursetree/dims.h b/cursetree/dims.h index 9ad42eb..ec4d5a3 100644 --- a/cursetree/dims.h +++ b/cursetree/dims.h @@ -1,11 +1,23 @@ #ifndef CURSETREE_DIMS_H #define CURSETREE_DIMS_H +#define __BOUND_UNLIMITED (-1) +#define __BOUND_ABS_MIN (1) +#define __BOUND_ABS_MAX (INT_MAX) +#define __BOUND_REL_MIN ((float)0) +#define __BOUND_REL_MAX ((float)1) + enum ct_axis { AXIS_X, AXIS_Y, }; +enum ct_boundtype { + BOUND_NONE, + BOUND_ABSOLUTE, + BOUND_RELATIVE, +}; + /* Stores a node's starting x,y coordinates, width, & height. * NOTE: Intended for interfunction communication. */ @@ -13,11 +25,22 @@ struct ct_dims { int x, y, width, height; }; +struct ct_bounds { + enum ct_boundtype type; + int wmin, wmax, hmin, hmax; +}; + struct ct_dims *new_dims(int x, int y, int width, int height); struct ct_dims *dup_dims(const struct ct_dims *const dims); -int bifurcate_dims(const struct ct_dims *const parent_dims, - const enum ct_axis axis, const float ratio, - struct ct_dims **const dims0, struct ct_dims **const dims1); +struct ct_bounds *bounds_none(void); +struct ct_bounds *bounds_absolute(int wmin, int wmax, int hmin, int hmax); +struct ct_bounds *bounds_relative(int wmin, int wmax, int hmin, int hmax); +struct ct_bounds *dup_bounds(const struct ct_bounds *const bounds); + +// int bifurcate_dims(const struct ct_dims *const parent_dims, +// const enum ct_axis axis, const float ratio, +// struct ct_dims **const dims0, struct ct_dims **const +// dims1); #endif /* CURSETREE_DIMS_H */ diff --git a/cursetree/ncrswrap.c b/cursetree/ncrswrap.c index d3b89c8..c74e270 100644 --- a/cursetree/ncrswrap.c +++ b/cursetree/ncrswrap.c @@ -68,6 +68,10 @@ void init_ncurses(void) { curs_set(0); // hide cursor } +void end_ncurses(void) { + endwin(); +} + /* Initialise (with default IO configuration) a new ncurses WINDOW. */ WINDOW *new_window(const int x, const int y, const int width, @@ -105,7 +109,7 @@ struct ct_dims *windims(WINDOW *win) { * NOTE: Failure occurs if width or height <= 0, * NOTE: or if the x,y coordinate is outside the screen bounds. */ -int resizemv_window(const int x, const int y, const int width, const int height, - WINDOW *const win) { +int resizemv_window(WINDOW *const win, const int x, const int y, + const int width, const int height) { return wresize(win, height, width) || mvwin(win, y, x); } diff --git a/cursetree/ncrswrap.h b/cursetree/ncrswrap.h index 900a914..457936e 100644 --- a/cursetree/ncrswrap.h +++ b/cursetree/ncrswrap.h @@ -3,9 +3,9 @@ #include "dims.h" -# ifndef __NCURSES_H +#ifndef __NCURSES_H typedef struct _win_st WINDOW; -# endif /* __NCURSES_H */ +#endif /* __NCURSES_H */ enum crs_termmode { /* tty cbreak mode */ @@ -20,22 +20,25 @@ enum crs_termmode { int termmode(const enum crs_termmode mode); void termsize(int *const width, int *const height); +struct ct_dims *termdims(void); void init_ncurses(void); +void end_ncurses(void); WINDOW *new_window(const int x, const int y, const int width, const int height); WINDOW *new_window_fs(void); void destroy_window(WINDOW *); #define winpos(win, x, y) (x = winposx(win), y = winposy(win)) -#define winsize(win, width, height) (width = winwidth(win), height=winheight(win)) +#define winsize(win, width, height) \ + (width = winwidth(win), height = winheight(win)) int winposx(WINDOW *const); int winposy(WINDOW *const); int winwidth(WINDOW *const); int winheight(WINDOW *const); struct ct_dims *windims(WINDOW *win); -int resizemv_window(const int x, const int y, const int width, const int height, - WINDOW *const win); +int resizemv_window(WINDOW *const win, const int x, const int y, + const int width, const int height); #endif /* CURSETREE_NCRSWRAP_H */ diff --git a/cursetree/node.c b/cursetree/node.c index 3a91945..f7d786a 100644 --- a/cursetree/node.c +++ b/cursetree/node.c @@ -1,153 +1,259 @@ #include #include -#include -#include "ncrswrap.h" +#include "dims.h" #include "node.h" +#include "surface.h" /* Internal allocator method for ct_node structures. */ -static inline struct ct_node *__alloc_node(const enum ct_nodetype type) { +static inline struct ct_node *__alloc_node(void) { struct ct_node *node = (struct ct_node *)malloc(sizeof(struct ct_node)); - node->type = type; return node; } -/* Construct a new window node (ct_node of type NODE_WIN). +/* Returns NULL if memory allocation failed. */ -struct ct_node *init_window_node(WINDOW *const win) { - struct ct_node *node = __alloc_node(NODE_WINDOW); - node->win = win; +struct ct_node *new_node(struct ct_dims *const dims, + struct ct_bounds *const bounds, + struct ct_node *const parent) { + struct ct_node *node = __alloc_node(); + if (node == NULL) { + *node = (struct ct_node){ + .surface = new_surface(dims, bounds), - return node; -} - -static struct ct_node * -auto_window_node(const struct ct_dims *const dims) { - WINDOW *win = new_window(dims->x, dims->y, dims->width, dims->height); - return init_window_node(win); -} - -static struct ct_node *init_abstract_node(struct ct_node *const node0, - struct ct_node *const node1, - const enum ct_axis axis, - const float ratio, - struct ct_dims *const dims) { - struct ct_node *node = __alloc_node(NODE_ABSTRACT); - node->axis = axis; - node->ratio = ratio; - node->dims = dims; - - node->child[0] = node0; - node->child[1] = node1; - return node; -} - -void destroy_node(struct ct_node *const node) { - if (node->type == NODE_WINDOW) { - /* Window Node */ - destroy_window(node->win); - goto end; + .parent = parent, + .child = (struct ct_node **)malloc(NODE_INIT_CHILDREN * + sizeof(struct ct_node *)), + .csize = NODE_INIT_CHILDREN, + .cindex = 0, + .axis = AXIS_X, + .ratio = 0, + }; } - /* Abstract Node */ - assert(node->type == NODE_ABSTRACT); - destroy_node(node->child[0]); - destroy_node(node->child[1]); - free(node->dims); -end: + return node; +} + +/* WARNING: Do NOT use __destroy_node() to destroy a node's children! + * WARNING: Use the destroy_child_node() function instead. + */ +void __destroy_node(struct ct_node *const node) { + if (node == NULL) + return; + + // destroy children first + for (int j = 0; j < node->cindex; j++) { + __destroy_node(node->child[j]); + } + + destroy_surface(node->surface); free(node); } -static inline struct ct_dims *nodedims(const struct ct_node *const node) { - struct ct_dims *dims; - if (node->type == NODE_WINDOW) { - /* Window Node */ - dims = windims(node->win); - } else { - /* Abstract Node */ - assert(node->type == NODE_ABSTRACT); - dims = dup_dims(node->dims); +void satisfy_node(struct ct_node *const node, struct ct_dims *const dims) { + double axismax; + if (IS_PARENT_NODE(node)) { + for (int i = 0; i < node->cindex; i++) { + if (node->axis == AXIS_X) { + axismax += dims->width * node->child[i]->surface->bounds->wmax; + } else { + assert(node->axis == AXIS_Y); + axismax += dims->width * node->child[i]->surface->bounds->hmax; + } + } } - - return dims; } -/* NOTE: resize_node calls wnoutrefresh(3x), which expects - * NOTE: a call doupdate(3x) call afterwards to flush ncurses - * NOTE: virtual screen to the physical screen. +/* */ -void resize_node(struct ct_node *const node, - struct ct_dims *const new_dims) { - if (node->type == NODE_WINDOW) { - /* Window Node */ - resizemv_window(new_dims->x, new_dims->y, new_dims->width, new_dims->height, - node->win); - free(new_dims); +void resize_node(struct ct_node *const node, struct ct_dims *const dims) { + int cwidth, cheight; // child dimensions - wnoutrefresh(node->win); + if (IS_PARENT_NODE(node)) { + dims->width / node->cindex; + } + + resize_surface(node->surface, dims); + + for (int j = 0; j < node->cindex; j++) { + /* TODO */ + resize_node(node->child[j], TODO); + } + // bifurcate_dims(dims, node->axis, node->ratio, &dims0, &dims1); + + // resize_node(node->child[0], dims0); + // resize_node(node->child[1], dims1); +} + +static int __set_cbounds(struct ct_node *const parent, + const struct ct_node *const child, + const bool additive) { + /* child and parent w/h min aliases */ + int c_wmin, c_hmin; + int p_wmin_rel, p_hmin_rel; + + c_wmin = child->surface->bounds->wmin; + c_hmin = child->surface->bounds->hmin; + + if (!additive) { + c_wmin *= -1; + c_hmin *= -1; + } + + if (child->surface->bounds->type == BOUND_RELATIVE) { + parent->cbounds.wmin_abs++; + parent->cbounds.hmin_abs++; + + p_wmin_rel = parent->cbounds.wmin_rel + c_wmin; + p_hmin_rel = parent->cbounds.hmin_rel + c_wmin; + if (p_wmin_rel > __BOUND_REL_MAX || p_hmin_rel >= __BOUND_REL_MAX) + return 1; + + parent->cbounds.wmin_rel = p_wmin_rel; + parent->cbounds.hmin_rel = p_hmin_rel; + assert(parent->cbounds.wmin_rel > __BOUND_REL_MIN); + assert(parent->cbounds.hmin_rel > __BOUND_REL_MIN); } else { - /* Abstract Node */ - assert(node->type == NODE_ABSTRACT); - struct ct_dims *dims0, *dims1; - - free(node->dims); - node->dims = new_dims; - bifurcate_dims(new_dims, node->axis, node->ratio, &dims0, &dims1); - - resize_node(node->child[0], dims0); - resize_node(node->child[1], dims1); + parent->cbounds.wmin_abs += c_wmin; + parent->cbounds.hmin_abs += c_hmin; + assert(parent->cbounds.wmin_abs > __BOUND_ABS_MIN); + assert(parent->cbounds.hmin_abs > __BOUND_ABS_MIN); } + return 0; } -/* Subdivide a window node's allocated region into two window nodes - * replacing the original node with an abstract node. - * Parameters: - * axis - controls which direction the subdivision occurs - * invert_axis - invert index of the original node in the new abstract node +/* Returns: + * 0 -> success + * 1 -> failed (max child limit reached) + * 2 -> failed (cumulative relative bounds surpasses 100%) */ -static void __bifurcate_window_node(struct ct_node **const node, - const enum ct_axis axis, const int invert_axis, - const float ratio) { - assert((*node)->type == NODE_WINDOW); - struct ct_dims *dims0, *dims1; - struct ct_node *node0, *node1; +int insert_child_node(struct ct_node *const parent, struct ct_node *const child, + const cindex i) { + if (parent->cindex == CINDEX_MAX) + return 1; + else if (__set_cbounds(parent, child, true)) + return 2; + else if (parent->cindex == parent->csize) { + // grow child array size and clamp maximum + parent->csize *= NODE_CHILDREN_GROWTH; + if (parent->csize > CINDEX_MAX) + parent->csize = CINDEX_MAX; - struct ct_dims *original_dims = nodedims(*node); - if (bifurcate_dims(original_dims, axis, ratio, &dims0, &dims1)) { - /* TODO: handle this error properly */ - free(original_dims); - exit(1); - return; + parent->child = + reallocarray(parent->child, parent->csize, sizeof(struct ct_node *)); } - if (invert_axis) { - /* Inverted Bifurcation */ - node0 = auto_window_node(dims0); - node1 = *node; - resize_node(node1, dims1); + // shift all children up for insertion + for (int j = parent->cindex; j > i; j--) { + parent->child[j] = parent->child[j - 1]; + } + // do insertion + parent->child[i] = child; + parent->cindex++; + return EXIT_SUCCESS; +} + +/* Returns: + * 0 -> success + * 1 -> failed (max child limit reached) + * 2 -> failed (cumulative relative bounds surpasses 100%) + */ +int append_child_node(struct ct_node *const parent, + struct ct_node *const child) { + return insert_child_node(parent, child, parent->cindex); +} + +/* Remove a child node from a parent node by index. + * Returns NULL if an invalid index was provided, otherwise returns + * a pointer to the child node removed. + * NOTE: This does NOT destroy the child node and should be + * NOTE: used when moving the child somewhere else. + * NOTE: Otherwise use destroy_child_node() instead. + */ +struct ct_node *remove_child_node(struct ct_node *const parent, + const cindex i) { + struct ct_node *child; + + if (i >= parent->cindex) + return NULL; + else if (parent->cindex <= parent->csize / NODE_CHILDREN_GROWTH) { + // shrink child array to avoid memory bloat + parent->csize /= NODE_CHILDREN_GROWTH; + parent->child = + reallocarray(parent->child, parent->csize, sizeof(struct ct_node *)); + } + + child = &parent[i]; + // shift all children down to fill removal + for (int j = i; j < parent->cindex; j++) { + parent->child[j] = parent->child[j + 1]; + } + parent->cindex--; + __set_cbounds(parent, child, false); + return child; +} + +/* Remove and destroy a child node by index. + * Returns 1 on failure (invalid index provided), and 0 on success. + * NOTE: Use remove_child_node() instead if the child should live. + */ +int destroy_child_node(struct ct_node *const parent, const cindex i) { + struct ct_node *child = remove_child_node(parent, i); + __destroy_node(child); + return (child == NULL); +} + +/* + * Returns: + * 0 -> success + * 1 -> failed (node has no parent) + * 2 -> failed (parent has no reference to node, CRITICAL) + */ +static int __index_as_child(const struct ct_node *const node, + int *const index) { + if (node->parent == NULL) + return 1; + + for (int i = 0; i < node->parent->cindex; i++) { + if (node->parent->child[i] == node) { + *index = i; + return 0; + } + } + + return 2; +} + +/* + * If preserve_bounds is set then upon collapse the new node maintains the + * original node's bounds struct. Otherwise the bounds of the child collapsed + * onto are used. + */ +void collapse_node(struct ct_node **const node, const int i, + const bool preserve_bounds) { + assert(0 <= i && i < (*node)->cindex); + int parent_index; + struct ct_node *const parent = (*node)->parent; + struct ct_node *collapse_target = remove_child_node(*node, i); + struct ct_dims *dims = dup_dims((*node)->surface->dims); + struct ct_bounds *bounds; + + if (preserve_bounds) { + bounds = dup_bounds((*node)->surface->bounds); + rebind_surface(collapse_target->surface, bounds); + } + + /* Destroy original node and point to the collapse_target */ + if (__index_as_child(*node, &parent_index)) { + /* __index_as_child fails (typically) because + * the *node is cursetree's root node. */ + __destroy_node(*node); + *node = collapse_target; } else { - /* Non-Inverted Bifurcation */ - node0 = *node; - node1 = auto_window_node(dims1); - resize_node(node0, dims0); + destroy_child_node(parent, parent_index); + insert_child_node(parent, collapse_target, parent_index); } - *node = init_abstract_node(node0, node1, axis, ratio, original_dims); -} - -/* Collapse an abstract node, killing one child node and resizing - * the other to take its place. - */ -static void collapse_abstract_node(struct ct_node **const node, - const int collapse_i) { - assert((*node)->type == NODE_ABSTRACT); - assert(0 <= collapse_i && collapse_i < NODE_CHILD_N); - - // WARNING: only works for NODE_CHILD_N=2 (binary trees) - destroy_node((*node)->child[!collapse_i]); - - struct ct_node *collapse_target = (*node)->child[collapse_i]; - free(*node); - *node = collapse_target; + resize_node(collapse_target, dims); } diff --git a/cursetree/node.h b/cursetree/node.h index f10b70c..9f713e7 100644 --- a/cursetree/node.h +++ b/cursetree/node.h @@ -2,48 +2,52 @@ #define CURSETREE_NODE_H #include "dims.h" +#include "limits.h" +#include "surface.h" -# ifndef __NCURSES_H +#ifndef __NCURSES_H typedef struct _win_st WINDOW; -# endif /* __NCURSES_H */ +#endif /* __NCURSES_H */ -#define NODE_CHILD_N 2 +#define NODE_INIT_CHILDREN 4 +#define NODE_CHILDREN_GROWTH 1.5 +#define CINDEX_MAX UCHAR_MAX -/* MACRO: - * Get widnow node start x,y coordinates, width, & height. - * void NODEDIMS(struct ct_node *node, struct ct_dims dims); - */ -#define GET_WNODEDIMS(dims, node) \ - (getbegyx((node)->win, dims.y, dims.x)); \ - getmaxyx((node)->win, dims.y, dims.x) - -enum ct_nodetype { - NODE_WINDOW, - NODE_ABSTRACT, -}; +/* Child Index */ +typedef unsigned char cindex; struct ct_node { - enum ct_nodetype type; - // union value depends on ct_node.type - union { - WINDOW *win; - struct { - enum ct_axis axis; - float ratio; - struct ct_dims *dims; - struct ct_node *child[NODE_CHILD_N]; - }; - }; + struct ct_surface *surface; + + struct ct_node *parent; + enum ct_axis axis; + struct ct_node **child; + cindex csize, cindex; + /* child imposed minimum bounds */ + struct { + int wmin_abs; + int hmin_abs; + int wmin_rel; + int hmin_rel; + } cbounds; + + float ratio; }; /* === External Interface === */ -struct ct_node *init_window_node(WINDOW *const win); -void destroy_node(struct ct_node *const node); -void resize_node(struct ct_node *const node, - struct ct_dims *const new_dims); +#define IS_PARENT_NODE(node) (node->cindex != 0) -void bifurcate_window_node(struct ct_node **const node, - const enum ct_axis axis, const int invert_axis, - const float ratio); +struct ct_node *new_node(struct ct_dims *const dims, + struct ct_bounds *const bounds, + struct ct_node *const parent); +void destroy_node(struct ct_node *const node); +void resize_node(struct ct_node *const node, struct ct_dims *const new_dims); + +void collapse_node(struct ct_node **const node, const int i, + const bool preserve_bounds); + +// void bifurcate_window_node(struct ct_node **const node, const enum ct_axis +// axis, +// const int invert_axis, const float ratio); #endif /* CURSETREE_NODE_H */ diff --git a/cursetree/surface.c b/cursetree/surface.c new file mode 100644 index 0000000..ac1ee50 --- /dev/null +++ b/cursetree/surface.c @@ -0,0 +1,46 @@ +#include + +#include "ncrswrap.h" +#include "surface.h" + +static inline struct ct_surface *__surface(struct ct_dims *const dims, + struct ct_bounds *const bounds, + WINDOW *const win) { + struct ct_surface *surface; + + surface = (struct ct_surface *)malloc(sizeof(struct ct_surface)); + *surface = (struct ct_surface){ + .dims = dims, + .bounds = bounds, + .win = win, + .updatereq = true, + }; + + return surface; +} + +struct ct_surface *new_surface(struct ct_dims *const dims, + struct ct_bounds *const bounds) { + WINDOW *const win = new_window(dims->x, dims->y, dims->width, dims->height); + return __surface(dims, bounds, win); +} + +void destroy_surface(struct ct_surface *const surface) { + destroy_window(surface->win); + free(surface->dims); +} + +void resize_surface(struct ct_surface *const surface, + struct ct_dims *const dims) { + free(surface->dims); + surface->dims = dims; + surface->updatereq = true; + + resizemv_window(surface->win, dims->x, dims->y, dims->width, dims->height); +} + +void rebind_surface(struct ct_surface *const surface, + struct ct_bounds *const bounds) { + free(surface->bounds); + surface->bounds = bounds; +} diff --git a/cursetree/surface.h b/cursetree/surface.h new file mode 100644 index 0000000..b130cd7 --- /dev/null +++ b/cursetree/surface.h @@ -0,0 +1,26 @@ +#ifndef CURSETREE_SURFACE_H +#define CURSETREE_SURFACE_H + +#include "dims.h" + +#ifndef __NCURSES_H +typedef struct _win_st WINDOW; +#endif /* __NCURSES_H */ + +struct ct_surface { + struct ct_dims *dims; + struct ct_bounds *bounds; + WINDOW *win; + unsigned char updatereq; +}; + +struct ct_surface *new_surface(struct ct_dims *const dims, + struct ct_bounds *const bounds); +void destroy_surface(struct ct_surface *const surface); + +void resize_surface(struct ct_surface *const surface, + struct ct_dims *const dims); +void rebind_surface(struct ct_surface *const surface, + struct ct_bounds *const bounds); + +#endif /* CURSETREE_SURFACE_H */ diff --git a/cursetree/tree.c b/cursetree/tree.c index 26175c3..6a91937 100644 --- a/cursetree/tree.c +++ b/cursetree/tree.c @@ -5,22 +5,19 @@ /* */ -static struct ct_node *init_root_node(void) { - WINDOW *rootwin; - - rootwin = new_window_fs(); - return init_window_node(rootwin); +static inline struct ct_node *__root_node(void) { + return new_node(termdims(), NULL); } int init_tree(struct ct_tree **const tree) { - *tree = (struct ct_tree *)malloc(sizeof(struct ct_tree)); - (*tree)->root = init_root_node(); -return EXIT_SUCCESS; + *tree = (struct ct_tree *)malloc(sizeof(struct ct_tree)); + (*tree)->root = __root_node(); + return EXIT_SUCCESS; } void destroy_tree(struct ct_tree *const tree) { destroy_node(tree->root); - endwin(); + end_ncurses(); free(tree); } @@ -28,3 +25,14 @@ void resize_tree(struct ct_tree *const tree, struct ct_dims *const dims) { resize_node(tree->root, dims); } +void switch_nodes(struct ct_node **const node0, struct ct_node **const node1) { + struct ct_node *const node0ptr = *node0; + struct ct_dims *const node0dims = dup_dims((*node0)->dims); + struct ct_dims *const node1dims = dup_dims((*node1)->dims); + + *node0 = *node1; + resize_node(*node0, node1dims); + + *node1 = node0ptr; + resize_node(*node1, node0dims); +} From fa97c73522fa3b3c87cf175000f25350fee4d0f0 Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Sun, 14 Sep 2025 00:13:55 +1000 Subject: [PATCH 19/26] add NFLAGS to ct_node NFLAGS will be used to avoid unnecessary CPU usage for events like resizing windows, a bitflag will be instead set to notify cursetree at the end of each ct_update --- cursetree/node.c | 16 +++++++++++++--- cursetree/node.h | 6 ++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/cursetree/node.c b/cursetree/node.c index f7d786a..4251afd 100644 --- a/cursetree/node.c +++ b/cursetree/node.c @@ -13,14 +13,21 @@ static inline struct ct_node *__alloc_node(void) { } /* Returns NULL if memory allocation failed. + * TODO: should dims be given as a parameter, or lazily computed in ct_update? */ struct ct_node *new_node(struct ct_dims *const dims, struct ct_bounds *const bounds, struct ct_node *const parent) { struct ct_node *node = __alloc_node(); - if (node == NULL) { + if (node != NULL) { *node = (struct ct_node){ - .surface = new_surface(dims, bounds), + /* copy the parent's dimensions for now and request + * cursetree resize this node appropriately afterwards + * WARNING: new_node doesn't set the NFLAG_RESIZE request flag + * WARNING: that should be done by a function calling new_node + */ + .surface = new_surface(parent->surface->dims, bounds), + .flags = NFLAG_EMPTY, .parent = parent, .child = (struct ct_node **)malloc(NODE_INIT_CHILDREN * @@ -28,7 +35,6 @@ struct ct_node *new_node(struct ct_dims *const dims, .csize = NODE_INIT_CHILDREN, .cindex = 0, .axis = AXIS_X, - .ratio = 0, }; } @@ -151,6 +157,8 @@ int insert_child_node(struct ct_node *const parent, struct ct_node *const child, // do insertion parent->child[i] = child; parent->cindex++; + // request cursetree recompute dimensions recursively from parent + parent->flags |= NFLAG_RESIZE; return EXIT_SUCCESS; } @@ -191,6 +199,8 @@ struct ct_node *remove_child_node(struct ct_node *const parent, } parent->cindex--; __set_cbounds(parent, child, false); + // request cursetree recompute dimensions recursively from parent + parent->flags |= NFLAG_RESIZE; return child; } diff --git a/cursetree/node.h b/cursetree/node.h index 9f713e7..b9917fc 100644 --- a/cursetree/node.h +++ b/cursetree/node.h @@ -13,11 +13,15 @@ typedef struct _win_st WINDOW; #define NODE_CHILDREN_GROWTH 1.5 #define CINDEX_MAX UCHAR_MAX +#define NFLAG_EMPTY (0) +#define NFLAG_RESIZE (1<<0) + /* Child Index */ typedef unsigned char cindex; struct ct_node { struct ct_surface *surface; + unsigned char flags; struct ct_node *parent; enum ct_axis axis; @@ -30,8 +34,6 @@ struct ct_node { int wmin_rel; int hmin_rel; } cbounds; - - float ratio; }; /* === External Interface === */ From 6b5bcff1a490b02a0bed8fe873cbea6aab3d954e Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Mon, 15 Sep 2025 17:11:06 +1000 Subject: [PATCH 20/26] (UNDER CONSTRUCTION) implementing general child nodes bifurcation was limited and I needed something more powerful --- cursetree/node.c | 100 ++++++++++++++++++++++++++++++++++++++++++----- cursetree/node.h | 5 ++- cursetree/tree.c | 6 +-- 3 files changed, 97 insertions(+), 14 deletions(-) diff --git a/cursetree/node.c b/cursetree/node.c index 4251afd..bc198f9 100644 --- a/cursetree/node.c +++ b/cursetree/node.c @@ -1,5 +1,8 @@ #include +#include +#include #include +#include #include "dims.h" #include "node.h" @@ -71,25 +74,104 @@ void satisfy_node(struct ct_node *const node, struct ct_dims *const dims) { } } +struct ct_whdims { + // Main Axis & Orthogonal Axis Sizes + int axm_size, axo_size; + bool fixed; + int min, max; +}; + /* */ -void resize_node(struct ct_node *const node, struct ct_dims *const dims) { - int cwidth, cheight; // child dimensions - - if (IS_PARENT_NODE(node)) { - dims->width / node->cindex; - } +int resize_node(struct ct_node *const node, struct ct_dims *const dims) { + // if (IS_PARENT_NODE(node)) { + // dims->width / node->cindex; + // } resize_surface(node->surface, dims); - for (int j = 0; j < node->cindex; j++) { - /* TODO */ - resize_node(node->child[j], TODO); + if (node->cbounds.wmin_abs + node->cbounds.wmin_rel * dims->width > + dims->width || + node->cbounds.hmin_abs + node->cbounds.hmin_rel * dims->height > + dims->height) { + node->flags |= NFLAG_TOOSMALL; + return 1; } + + struct ct_whdims cwhdims[node->cindex]; + struct ct_whdims *const parts[node->cindex]; + int cunfixedn = node->cindex; // number of non-fixed children + memset(cwhdims, 0, sizeof(struct ct_whdims) * node->cindex); + + int axm_size, axm_free, axo_size; + size_t axm_min_offset, axm_max_offset; + if (node->axis == AXIS_X) { + axm_free = dims->width; + axm_size = dims->width; + axo_size = dims->height; + + axm_min_offset = offsetof(struct ct_bounds, wmin); + axm_max_offset = offsetof(struct ct_bounds, wmax); + } else { + assert(node->axis == AXIS_Y); + axm_free = dims->height; + axm_size = dims->height; + axo_size = dims->width; + + axm_min_offset = offsetof(struct ct_bounds, hmin); + axm_max_offset = offsetof(struct ct_bounds, hmax); + } + + int split = axm_free / node->cindex; + for (int j = 0; j < node->cindex; j++) { + cwhdims[j].min = *(int*)(node->child[j]->surface->bounds + axm_min_offset); + cwhdims[j].max = *(int*)(node->child[j]->surface->bounds + axm_max_offset); + if (node->child[j]->surface->bounds->type == BOUND_RELATIVE) { + cwhdims[j].min *= axm_size; + cwhdims[j].max *= axm_size; + } + + cwhdims[j].axm_size = fmax(fmin(split, cwhdims[j].max), cwhdims[j].min); + cwhdims[j].axo_size = axo_size; + + axm_free -= cwhdims[j].axm_size; + } + + int delta; + while (axm_free) { + if (axm_free < cunfixedn) { + int progress = 0; + for (int j = 0; j < node->cindex; j++) { + if (cwhdims[j].fixed) + continue; + cwhdims[j].axm_size--; + axm_free--; + } + } + + // split is guaranteed to be >0 + split = axm_free / node->cindex; + for (int j = 0; j < node->cindex; j++) { + if (cwhdims[j].fixed) + continue; + + delta = cwhdims[j].axm_size - fmax(cwhdims[j].axm_size - split, bounds->wmin); + if (delta == 0) { + cwhdims[j].fixed = true; + cunfixedn++; + continue; + } + + cwhdims[j].axm_size -= delta; + axm_free -= delta; + } + } + // bifurcate_dims(dims, node->axis, node->ratio, &dims0, &dims1); // resize_node(node->child[0], dims0); // resize_node(node->child[1], dims1); + return 0; } static int __set_cbounds(struct ct_node *const parent, diff --git a/cursetree/node.h b/cursetree/node.h index b9917fc..650f98f 100644 --- a/cursetree/node.h +++ b/cursetree/node.h @@ -13,8 +13,9 @@ typedef struct _win_st WINDOW; #define NODE_CHILDREN_GROWTH 1.5 #define CINDEX_MAX UCHAR_MAX -#define NFLAG_EMPTY (0) -#define NFLAG_RESIZE (1<<0) +#define NFLAG_EMPTY (0) +#define NFLAG_RESIZE (1<<0) +#define NFLAG_TOOSMALL (1<<1) /* Child Index */ typedef unsigned char cindex; diff --git a/cursetree/tree.c b/cursetree/tree.c index 6a91937..b0b3b1a 100644 --- a/cursetree/tree.c +++ b/cursetree/tree.c @@ -6,7 +6,7 @@ /* */ static inline struct ct_node *__root_node(void) { - return new_node(termdims(), NULL); + return new_node(termdims(), bounds_none(), NULL); } int init_tree(struct ct_tree **const tree) { @@ -27,8 +27,8 @@ void resize_tree(struct ct_tree *const tree, struct ct_dims *const dims) { void switch_nodes(struct ct_node **const node0, struct ct_node **const node1) { struct ct_node *const node0ptr = *node0; - struct ct_dims *const node0dims = dup_dims((*node0)->dims); - struct ct_dims *const node1dims = dup_dims((*node1)->dims); + struct ct_dims *const node0dims = dup_dims((*node0)->surface->dims); + struct ct_dims *const node1dims = dup_dims((*node1)->surface->dims); *node0 = *node1; resize_node(*node0, node1dims); From 330755591bc0c78fb6625aa9979bffe6ab82d26a Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Mon, 15 Sep 2025 19:26:04 +1000 Subject: [PATCH 21/26] got nonbinary partitioning working yippie --- cursetree/dims.c | 13 ++-- cursetree/node.c | 163 ++++++++++++++++++++++++++++++------------- cursetree/node.h | 2 +- cursetree/nsparted.c | 11 +++ cursetree/nsparted.h | 26 +++++++ cursetree/util.h | 12 ++++ 6 files changed, 170 insertions(+), 57 deletions(-) create mode 100644 cursetree/nsparted.c create mode 100644 cursetree/nsparted.h create mode 100644 cursetree/util.h diff --git a/cursetree/dims.c b/cursetree/dims.c index 4fa4895..7d7401e 100644 --- a/cursetree/dims.c +++ b/cursetree/dims.c @@ -4,6 +4,7 @@ #include #include "dims.h" +#include "util.h" struct ct_dims *new_dims(int x, int y, int width, int height) { struct ct_dims *dims; @@ -42,19 +43,17 @@ static struct ct_bounds *__bounds(const enum ct_boundtype type, const int wmin, return bounds; } -static inline void clamp(int *const val, const int min, const int max, +static inline void __clamp(int *const val, const int min, const int max, const int do_ceiling) { if (*val == __BOUND_UNLIMITED) *val = do_ceiling ? max : min; - else if (*val > max) - *val = max; - else if (*val < min) - *val = min; + else + *val = clampi(*val, min, max); } #define CLAMP_ABS(val, is_max) \ - (clamp(&val, __BOUND_ABS_MIN, __BOUND_ABS_MAX, is_max)) + (__clamp(&val, __BOUND_ABS_MIN, __BOUND_ABS_MAX, is_max)) #define CLAMP_REL(val, is_max) \ - (clamp(&val, __BOUND_REL_MIN, __BOUND_REL_MAX, is_max)) + (__clamp(&val, __BOUND_REL_MIN, __BOUND_REL_MAX, is_max)) struct ct_bounds *bounds_none(void) { return __bounds(BOUND_NONE, __BOUND_ABS_MIN, __BOUND_ABS_MAX, __BOUND_ABS_MIN, diff --git a/cursetree/node.c b/cursetree/node.c index bc198f9..cd43598 100644 --- a/cursetree/node.c +++ b/cursetree/node.c @@ -7,6 +7,7 @@ #include "dims.h" #include "node.h" #include "surface.h" +#include "util.h" /* Internal allocator method for ct_node structures. */ @@ -74,9 +75,12 @@ void satisfy_node(struct ct_node *const node, struct ct_dims *const dims) { } } -struct ct_whdims { +/* Surface Partition Dimensions */ +/* TODO: can I use unsigned short instead of int? */ +struct ct_spdims { // Main Axis & Orthogonal Axis Sizes - int axm_size, axo_size; + int axm_size; + // int axo_size; bool fixed; int min, max; }; @@ -98,75 +102,136 @@ int resize_node(struct ct_node *const node, struct ct_dims *const dims) { return 1; } - struct ct_whdims cwhdims[node->cindex]; - struct ct_whdims *const parts[node->cindex]; - int cunfixedn = node->cindex; // number of non-fixed children - memset(cwhdims, 0, sizeof(struct ct_whdims) * node->cindex); - - int axm_size, axm_free, axo_size; - size_t axm_min_offset, axm_max_offset; + int axm_size, axm_free; + // int axo_size; + size_t bounds_axm_min_offset, bounds_axm_max_offset; + size_t dims_axm_pos_offset, dims_axm_size_offset; + // size_t dims_axo_pos_offset, dims_axo_size_offset; + if (node->axis == AXIS_X) { axm_free = dims->width; axm_size = dims->width; - axo_size = dims->height; + // axo_size = dims->height; - axm_min_offset = offsetof(struct ct_bounds, wmin); - axm_max_offset = offsetof(struct ct_bounds, wmax); + bounds_axm_min_offset = offsetof(struct ct_bounds, wmin); + bounds_axm_max_offset = offsetof(struct ct_bounds, wmax); + + dims_axm_pos_offset = offsetof(struct ct_dims, x); + // dims_axo_pos_offset = offsetof(struct ct_dims, y); + dims_axm_size_offset = offsetof(struct ct_dims, width); + // dims_axo_size_offset = offsetof(struct ct_dims, height); } else { assert(node->axis == AXIS_Y); axm_free = dims->height; axm_size = dims->height; - axo_size = dims->width; + // axo_size = dims->width; - axm_min_offset = offsetof(struct ct_bounds, hmin); - axm_max_offset = offsetof(struct ct_bounds, hmax); + bounds_axm_min_offset = offsetof(struct ct_bounds, hmin); + bounds_axm_max_offset = offsetof(struct ct_bounds, hmax); + + dims_axm_pos_offset = offsetof(struct ct_dims, y); + // dims_axo_pos_offset = offsetof(struct ct_dims, x); + dims_axm_size_offset = offsetof(struct ct_dims, height); + // dims_axo_size_offset = offsetof(struct ct_dims, width); } - int split = axm_free / node->cindex; - for (int j = 0; j < node->cindex; j++) { - cwhdims[j].min = *(int*)(node->child[j]->surface->bounds + axm_min_offset); - cwhdims[j].max = *(int*)(node->child[j]->surface->bounds + axm_max_offset); - if (node->child[j]->surface->bounds->type == BOUND_RELATIVE) { - cwhdims[j].min *= axm_size; - cwhdims[j].max *= axm_size; + struct ct_spdims cspdims[node->cindex]; + // struct ct_spdims *parts[node->cindex]; + cindex parts_n = node->cindex; + memset(cspdims, 0, sizeof(struct ct_spdims) * node->cindex); + for (int i = 0; i < node->cindex; i++) { + cspdims[i].min = *(int *)(node->child[i]->surface->bounds + bounds_axm_min_offset); + cspdims[i].max = *(int *)(node->child[i]->surface->bounds + bounds_axm_max_offset); + if (node->child[i]->surface->bounds->type == BOUND_RELATIVE) { + cspdims[i].min *= axm_size; + cspdims[i].max *= axm_size; } - cwhdims[j].axm_size = fmax(fmin(split, cwhdims[j].max), cwhdims[j].min); - cwhdims[j].axo_size = axo_size; - - axm_free -= cwhdims[j].axm_size; + cspdims[i].axm_size = 0; + // cspdims[i].axo_size = axo_size; + + // parts[i] = &cspdims[i]; } - int delta; - while (axm_free) { - if (axm_free < cunfixedn) { - int progress = 0; - for (int j = 0; j < node->cindex; j++) { - if (cwhdims[j].fixed) - continue; - cwhdims[j].axm_size--; - axm_free--; - } - } - - // split is guaranteed to be >0 - split = axm_free / node->cindex; - for (int j = 0; j < node->cindex; j++) { - if (cwhdims[j].fixed) + int split, new_size; + while (parts_n) { + split = axm_free / parts_n; + for (int i = 0; i < node->cindex; i++) { + if (cspdims[i].fixed) continue; - - delta = cwhdims[j].axm_size - fmax(cwhdims[j].axm_size - split, bounds->wmin); - if (delta == 0) { - cwhdims[j].fixed = true; - cunfixedn++; + + new_size = + clampi(cspdims[i].axm_size + split, cspdims[i].min, cspdims[i].max); + if (new_size == cspdims[i].axm_size) { + cspdims[i].fixed = true; + parts_n--; continue; } - cwhdims[j].axm_size -= delta; - axm_free -= delta; + axm_free -= (new_size - cspdims[i].axm_size); + cspdims[i].axm_size = new_size; } } + /* TODO: using axo_size this way means cspdims doesn't need an axo field!! */ + struct ct_dims * cdims = dup_dims(dims); + /* NOTE: the statements below are done implicitly by dup_dims */ + // *(int*)(cdims + dims_axm_pos_offset) = *(int*)(dims + dims_axm_pos_offset); + // *(int*)(cdims + dims_axo_pos_offset) = *(int*)(dims + dims_axo_pos_offset); + // *(int*)(cdims + dims_axo_size_offset) = axo_size; + for (int i = 0; i < node->cindex; i++) { + *(int*)(cdims + dims_axm_size_offset) = cspdims[i].axm_size; + resize_node(node->child[i], dup_dims(cdims)); + *(int*)(cdims + dims_axm_pos_offset) += cspdims[i].axm_size; + } + free(cdims); + + // int split = axm_free / node->cindex; + // for (int j = 0; j < node->cindex; j++) { + // cwhdims[j].min = *(int*)(node->child[j]->surface->bounds + + // axm_min_offset); cwhdims[j].max = *(int*)(node->child[j]->surface->bounds + // + axm_max_offset); if (node->child[j]->surface->bounds->type == + // BOUND_RELATIVE) { + // cwhdims[j].min *= axm_size; + // cwhdims[j].max *= axm_size; + // } + + // cwhdims[j].axm_size = fmax(fmin(split, cwhdims[j].max), cwhdims[j].min); + // cwhdims[j].axo_size = axo_size; + + // axm_free -= cwhdims[j].axm_size; + // } + + // int delta; + // while (axm_free) { + // if (axm_free < cunfixedn) { + // int progress = 0; + // for (int j = 0; j < node->cindex; j++) { + // if (cwhdims[j].fixed) + // continue; + // cwhdims[j].axm_size--; + // axm_free--; + // } + // } + + // // split is guaranteed to be >0 + // split = axm_free / node->cindex; + // for (int j = 0; j < node->cindex; j++) { + // if (cwhdims[j].fixed) + // continue; + + // delta = cwhdims[j].axm_size - fmax(cwhdims[j].axm_size - split, + // bounds->wmin); if (delta == 0) { + // cwhdims[j].fixed = true; + // cunfixedn++; + // continue; + // } + + // cwhdims[j].axm_size -= delta; + // axm_free -= delta; + // } + // } + // bifurcate_dims(dims, node->axis, node->ratio, &dims0, &dims1); // resize_node(node->child[0], dims0); diff --git a/cursetree/node.h b/cursetree/node.h index 650f98f..9906af0 100644 --- a/cursetree/node.h +++ b/cursetree/node.h @@ -44,7 +44,7 @@ struct ct_node *new_node(struct ct_dims *const dims, struct ct_bounds *const bounds, struct ct_node *const parent); void destroy_node(struct ct_node *const node); -void resize_node(struct ct_node *const node, struct ct_dims *const new_dims); +int resize_node(struct ct_node *const node, struct ct_dims *const new_dims); void collapse_node(struct ct_node **const node, const int i, const bool preserve_bounds); diff --git a/cursetree/nsparted.c b/cursetree/nsparted.c new file mode 100644 index 0000000..67231c3 --- /dev/null +++ b/cursetree/nsparted.c @@ -0,0 +1,11 @@ +#include "nsparted.h" + +struct ct_nsparted init_nsparted(struct ct_node *const node, + struct ct_dims *const dims) { + struct ct_nsparted nsparted = (struct ct_nsparted) { + .node = node, + .dims = dims, + . + } +} + diff --git a/cursetree/nsparted.h b/cursetree/nsparted.h new file mode 100644 index 0000000..c4fda2d --- /dev/null +++ b/cursetree/nsparted.h @@ -0,0 +1,26 @@ +#ifndef CURSETREE_NSPARTED_H +#define CURSETREE_NSPARTED_H + +#include "node.h" + +/* Surface Partition Dimensions */ +struct ct_spdims { + // Main Axis & Orthogonal Axis Sizes + int axm_size, axo_size; + bool fixed; + int min, max; +}; + +/* Node Surface Partitioner */ +struct ct_nsparted { + struct ct_node *const node; + struct ct_dims *const dims; + + /* Child Surface Partition Dimensions */ + struct ct_spdims *const cspdims; +}; + +struct ct_nsparted *init_nsparted(struct ct_node *const node, + struct ct_dims *const dims); + +#endif /* CURSETREE_NSPARTED_H */ diff --git a/cursetree/util.h b/cursetree/util.h new file mode 100644 index 0000000..6b97a12 --- /dev/null +++ b/cursetree/util.h @@ -0,0 +1,12 @@ +#ifndef CURSETREE_UTIL_H +#define CURSETREE_UTIL_H + +static inline int clampi(int val, int min, int max) { + if (val > max) + return max; + else if (val < min) + return min; + return val; +} + +#endif /* CURSETREE_UTIL_H */ From b16590fd5a1fe99ba275f71fc6e64412d7e6c100 Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Tue, 16 Sep 2025 16:48:02 +1000 Subject: [PATCH 22/26] nonbinary-tree(TM) now works in a very minimal instance --- Makefile | 4 +- cli/main.c | 26 +------- cursetree/_ncurses.h | 6 ++ cursetree/cursetree.c | 13 ++-- cursetree/dims.c | 2 +- cursetree/dims.h | 7 +-- cursetree/ncrswrap.c | 25 ++++---- cursetree/ncrswrap.h | 16 ++--- cursetree/node.c | 143 ++++++++++++------------------------------ cursetree/node.h | 21 ++++--- cursetree/nsparted.c | 11 ---- cursetree/nsparted.h | 26 -------- cursetree/surface.c | 33 +++++++++- cursetree/surface.h | 11 +++- cursetree/tree.c | 4 +- 15 files changed, 132 insertions(+), 216 deletions(-) create mode 100644 cursetree/_ncurses.h delete mode 100644 cursetree/nsparted.c delete mode 100644 cursetree/nsparted.h diff --git a/Makefile b/Makefile index 4894efc..40dc834 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ $(BUILD) $(BIN): $(BUILD)/dorne.o: $(addprefix $(CLI)/, main.c) $(call mkobj,-Icursetree) -$(BUILD)/cursetree.o: $(addprefix $(CT)/, cursetree.c ncrswrap.c) +$(BUILD)/cursetree.o: $(addprefix $(CT)/, cursetree.c tree.c node.c surface.c dims.c ncrswrap.c) $(call mkobj,) $(BUILD)/epty.o: $(addprefix $(CT)/, pty/child.c pty/epty.c pty/_pty.c) $(call mkobj,) @@ -56,4 +56,4 @@ test: clean run # === UTILITY TARGETS === .PHONY: clean clean: - - rm -rf $(BUILD) $(BIN) vgcore.* &>/dev/null + - rm -rf $(BUILD) $(BIN) vgcore.* &>/dev/null logs/ diff --git a/cli/main.c b/cli/main.c index 6c852ba..f78dee2 100644 --- a/cli/main.c +++ b/cli/main.c @@ -7,40 +7,18 @@ #include "cursetree.h" int main(int argc, char **argv) { - struct crs_tree *tree; + struct ct_tree *tree; ct_init(&tree); init_pair(1, COLOR_CYAN, COLOR_CYAN); init_pair(2, COLOR_RED, COLOR_RED); - bifurcate_window_node(&tree->root, AXIS_X, FALSE, 0.5); - // wbkgd(tree->root->child[0]->win, COLOR_PAIR(1)); - // wbkgd(tree->root->child[1]->win, COLOR_PAIR(2)); - // wrefresh(tree->root->child[0]->win); - // wrefresh(tree->root->child[1]->win); - // wbkgd(tree->root->win, COLOR_PAIR(1)); // wrefresh(tree->root->win); int i = 1; - while (1) { - /* TODO: UNCOMMENT START */ - // wclear(tree->root->child[0]->win); - // wclear(tree->root->child[1]->win); - // wbkgd(tree->root->child[0]->win, COLOR_PAIR(i)); - // wbkgd(tree->root->child[1]->win, COLOR_PAIR(3 - i)); - - // wnoutrefresh(tree->root->child[0]->win); - // wnoutrefresh(tree->root->child[1]->win); - // doupdate(); - - // wrefresh(tree->root->child[0]->win); - // wrefresh(tree->root->child[1]->win); - i = 3 - i; - /* TODO: UNCOMMENT END */ - + while (1) { ct_update(tree); - // usleep(100000); } destroy_tree(tree); diff --git a/cursetree/_ncurses.h b/cursetree/_ncurses.h new file mode 100644 index 0000000..dc92562 --- /dev/null +++ b/cursetree/_ncurses.h @@ -0,0 +1,6 @@ +/* This file exists to isolate the ncurses instance being used + * ie ncurses, ncursesw, ncursest, ncursestw + */ + +/* libncurses with wide-character support. */ +#include diff --git a/cursetree/cursetree.c b/cursetree/cursetree.c index 6d9a87e..90b1476 100644 --- a/cursetree/cursetree.c +++ b/cursetree/cursetree.c @@ -2,6 +2,7 @@ #include "ncrswrap.h" #include "tree.h" +#include "_ncurses.h" int ct_init(struct ct_tree **const tree) { /* Initialise NCurses Library & Root Node */ @@ -21,9 +22,9 @@ void ct_update(struct ct_tree *const tree) { */ switch (key) { case -1: - wclear(tree->root->child[0]->win); - mvwprintw(tree->root->child[0]->win, 0, 0, " \r-1\n"); - wrefresh(tree->root->child[0]->win); + wclear(tree->root->surface->win); + mvwprintw(tree->root->surface->win, 0, 0, " \r-1\n"); + wrefresh(tree->root->surface->win); return; case KEY_RESIZE: resize_tree(tree, termdims()); @@ -31,9 +32,9 @@ void ct_update(struct ct_tree *const tree) { doupdate(); break; default: - wclear(tree->root->child[0]->win); - mvwprintw(tree->root->child[0]->win, 0, 0, " \r%d\n", key); - wrefresh(tree->root->child[0]->win); + wclear(tree->root->surface->win); + mvwprintw(tree->root->surface->win, 0, 0, " \r%d\n", key); + wrefresh(tree->root->surface->win); break; } } diff --git a/cursetree/dims.c b/cursetree/dims.c index 7d7401e..e4073a2 100644 --- a/cursetree/dims.c +++ b/cursetree/dims.c @@ -6,7 +6,7 @@ #include "dims.h" #include "util.h" -struct ct_dims *new_dims(int x, int y, int width, int height) { +struct ct_dims *new_dims(const int x, const int y, const int width, const int height) { struct ct_dims *dims; dims = (struct ct_dims *)malloc(sizeof(struct ct_dims)); diff --git a/cursetree/dims.h b/cursetree/dims.h index ec4d5a3..25e6ccc 100644 --- a/cursetree/dims.h +++ b/cursetree/dims.h @@ -30,7 +30,7 @@ struct ct_bounds { int wmin, wmax, hmin, hmax; }; -struct ct_dims *new_dims(int x, int y, int width, int height); +struct ct_dims *new_dims(const int x, const int y, const int width, const int height); struct ct_dims *dup_dims(const struct ct_dims *const dims); struct ct_bounds *bounds_none(void); @@ -38,9 +38,4 @@ struct ct_bounds *bounds_absolute(int wmin, int wmax, int hmin, int hmax); struct ct_bounds *bounds_relative(int wmin, int wmax, int hmin, int hmax); struct ct_bounds *dup_bounds(const struct ct_bounds *const bounds); -// int bifurcate_dims(const struct ct_dims *const parent_dims, -// const enum ct_axis axis, const float ratio, -// struct ct_dims **const dims0, struct ct_dims **const -// dims1); - #endif /* CURSETREE_DIMS_H */ diff --git a/cursetree/ncrswrap.c b/cursetree/ncrswrap.c index c74e270..d81cc4b 100644 --- a/cursetree/ncrswrap.c +++ b/cursetree/ncrswrap.c @@ -1,8 +1,7 @@ #include #include -/* libncurses with wide-character support. */ -#include +#include "_ncurses.h" #include "ncrswrap.h" @@ -90,19 +89,19 @@ WINDOW *new_window_fs(void) { return rootwin; } -void destroy_window(WINDOW *) __attribute__((alias("delwin"))); +// void destroy_window(WINDOW *) __attribute__((alias("delwin"))); -int winposx(WINDOW *) __attribute__((alias("getbegy"))); -int winposy(WINDOW *) __attribute__((alias("getbegy"))); -int winwidth(WINDOW *) __attribute__((alias("getmaxx"))); -int winheight(WINDOW *) __attribute__((alias("getmaxy"))); +// int winposx(WINDOW *) __attribute__((alias("getbegy"))); +// int winposy(WINDOW *) __attribute__((alias("getbegy"))); +// int winwidth(WINDOW *) __attribute__((alias("getmaxx"))); +// int winheight(WINDOW *) __attribute__((alias("getmaxy"))); -struct ct_dims *windims(WINDOW *win) { - int x, y, width, height; - winpos(win, x, y); - winsize(win, width, height); - return new_dims(x, y, width, height); -} +// struct ct_dims *windims(WINDOW *win) { +// int x, y, width, height; +// winpos(win, x, y); +// winsize(win, width, height); +// return new_dims(x, y, width, height); +// } /* Resize and move (if resized successfully) an ncurses WINDOW. * Returns ERR (1) on fail, and OK (0) on success. diff --git a/cursetree/ncrswrap.h b/cursetree/ncrswrap.h index 457936e..c6845e0 100644 --- a/cursetree/ncrswrap.h +++ b/cursetree/ncrswrap.h @@ -29,14 +29,14 @@ WINDOW *new_window(const int x, const int y, const int width, const int height); WINDOW *new_window_fs(void); void destroy_window(WINDOW *); -#define winpos(win, x, y) (x = winposx(win), y = winposy(win)) -#define winsize(win, width, height) \ - (width = winwidth(win), height = winheight(win)) -int winposx(WINDOW *const); -int winposy(WINDOW *const); -int winwidth(WINDOW *const); -int winheight(WINDOW *const); -struct ct_dims *windims(WINDOW *win); +// #define winpos(win, x, y) (x = winposx(win), y = winposy(win)) +// #define winsize(win, width, height) \ +// (width = winwidth(win), height = winheight(win)) +// int winposx(WINDOW *const); +// int winposy(WINDOW *const); +// int winwidth(WINDOW *const); +// int winheight(WINDOW *const); +// struct ct_dims *windims(WINDOW *win); int resizemv_window(WINDOW *const win, const int x, const int y, const int width, const int height); diff --git a/cursetree/node.c b/cursetree/node.c index cd43598..4c5a300 100644 --- a/cursetree/node.c +++ b/cursetree/node.c @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -19,18 +18,13 @@ static inline struct ct_node *__alloc_node(void) { /* Returns NULL if memory allocation failed. * TODO: should dims be given as a parameter, or lazily computed in ct_update? */ -struct ct_node *new_node(struct ct_dims *const dims, - struct ct_bounds *const bounds, - struct ct_node *const parent) { +struct ct_node *__node(struct ct_dims *const dims, + struct ct_bounds *const bounds, + struct ct_node *const parent) { struct ct_node *node = __alloc_node(); if (node != NULL) { *node = (struct ct_node){ - /* copy the parent's dimensions for now and request - * cursetree resize this node appropriately afterwards - * WARNING: new_node doesn't set the NFLAG_RESIZE request flag - * WARNING: that should be done by a function calling new_node - */ - .surface = new_surface(parent->surface->dims, bounds), + .surface = new_surface(dims, bounds), .flags = NFLAG_EMPTY, .parent = parent, @@ -45,6 +39,16 @@ struct ct_node *new_node(struct ct_dims *const dims, return node; } +struct ct_node *new_node(struct ct_bounds *const bounds, + struct ct_node *const parent) { + /* copy the parent's dimensions for now and request + * cursetree resize this node appropriately afterwards + * WARNING: new_node doesn't set the NFLAG_RESIZE request flag + * WARNING: that should be done by a function calling new_node + */ + return __node(parent->surface->dims, bounds, parent); +} + /* WARNING: Do NOT use __destroy_node() to destroy a node's children! * WARNING: Use the destroy_child_node() function instead. */ @@ -61,26 +65,11 @@ void __destroy_node(struct ct_node *const node) { free(node); } -void satisfy_node(struct ct_node *const node, struct ct_dims *const dims) { - double axismax; - if (IS_PARENT_NODE(node)) { - for (int i = 0; i < node->cindex; i++) { - if (node->axis == AXIS_X) { - axismax += dims->width * node->child[i]->surface->bounds->wmax; - } else { - assert(node->axis == AXIS_Y); - axismax += dims->width * node->child[i]->surface->bounds->hmax; - } - } - } -} - /* Surface Partition Dimensions */ /* TODO: can I use unsigned short instead of int? */ struct ct_spdims { // Main Axis & Orthogonal Axis Sizes int axm_size; - // int axo_size; bool fixed; int min, max; }; @@ -88,69 +77,66 @@ struct ct_spdims { /* */ int resize_node(struct ct_node *const node, struct ct_dims *const dims) { - // if (IS_PARENT_NODE(node)) { - // dims->width / node->cindex; - // } - resize_surface(node->surface, dims); + if (node->surface->bounds->wmin > dims->width || + node->surface->bounds->hmin > dims->height) { + node->flags |= NFLAG_SMALL; + return 1; + } else if (node->surface->bounds->wmax < dims->width || + node->surface->bounds->hmax < dims->height) { + node->flags |= NFLAG_LARGE; + return 1; + } + + if (!IS_PARENT_NODE(node)) + return 0; + if (node->cbounds.wmin_abs + node->cbounds.wmin_rel * dims->width > dims->width || node->cbounds.hmin_abs + node->cbounds.hmin_rel * dims->height > dims->height) { - node->flags |= NFLAG_TOOSMALL; + node->flags |= NFLAG_SMALLCHILD; return 1; } int axm_size, axm_free; - // int axo_size; size_t bounds_axm_min_offset, bounds_axm_max_offset; size_t dims_axm_pos_offset, dims_axm_size_offset; - // size_t dims_axo_pos_offset, dims_axo_size_offset; if (node->axis == AXIS_X) { axm_free = dims->width; axm_size = dims->width; - // axo_size = dims->height; - - bounds_axm_min_offset = offsetof(struct ct_bounds, wmin); - bounds_axm_max_offset = offsetof(struct ct_bounds, wmax); dims_axm_pos_offset = offsetof(struct ct_dims, x); - // dims_axo_pos_offset = offsetof(struct ct_dims, y); dims_axm_size_offset = offsetof(struct ct_dims, width); - // dims_axo_size_offset = offsetof(struct ct_dims, height); + bounds_axm_min_offset = offsetof(struct ct_bounds, wmin); + bounds_axm_max_offset = offsetof(struct ct_bounds, wmax); } else { assert(node->axis == AXIS_Y); axm_free = dims->height; axm_size = dims->height; - // axo_size = dims->width; - - bounds_axm_min_offset = offsetof(struct ct_bounds, hmin); - bounds_axm_max_offset = offsetof(struct ct_bounds, hmax); dims_axm_pos_offset = offsetof(struct ct_dims, y); - // dims_axo_pos_offset = offsetof(struct ct_dims, x); dims_axm_size_offset = offsetof(struct ct_dims, height); - // dims_axo_size_offset = offsetof(struct ct_dims, width); + bounds_axm_min_offset = offsetof(struct ct_bounds, hmin); + bounds_axm_max_offset = offsetof(struct ct_bounds, hmax); } struct ct_spdims cspdims[node->cindex]; - // struct ct_spdims *parts[node->cindex]; cindex parts_n = node->cindex; memset(cspdims, 0, sizeof(struct ct_spdims) * node->cindex); for (int i = 0; i < node->cindex; i++) { - cspdims[i].min = *(int *)(node->child[i]->surface->bounds + bounds_axm_min_offset); - cspdims[i].max = *(int *)(node->child[i]->surface->bounds + bounds_axm_max_offset); + cspdims[i].min = + *(int *)(node->child[i]->surface->bounds + bounds_axm_min_offset); + cspdims[i].max = + *(int *)(node->child[i]->surface->bounds + bounds_axm_max_offset); if (node->child[i]->surface->bounds->type == BOUND_RELATIVE) { cspdims[i].min *= axm_size; cspdims[i].max *= axm_size; } cspdims[i].axm_size = 0; - // cspdims[i].axo_size = axo_size; - - // parts[i] = &cspdims[i]; } int split, new_size; @@ -173,69 +159,18 @@ int resize_node(struct ct_node *const node, struct ct_dims *const dims) { } } - /* TODO: using axo_size this way means cspdims doesn't need an axo field!! */ - struct ct_dims * cdims = dup_dims(dims); + struct ct_dims *cdims = dup_dims(dims); /* NOTE: the statements below are done implicitly by dup_dims */ // *(int*)(cdims + dims_axm_pos_offset) = *(int*)(dims + dims_axm_pos_offset); // *(int*)(cdims + dims_axo_pos_offset) = *(int*)(dims + dims_axo_pos_offset); // *(int*)(cdims + dims_axo_size_offset) = axo_size; for (int i = 0; i < node->cindex; i++) { - *(int*)(cdims + dims_axm_size_offset) = cspdims[i].axm_size; + *(int *)(cdims + dims_axm_size_offset) = cspdims[i].axm_size; resize_node(node->child[i], dup_dims(cdims)); - *(int*)(cdims + dims_axm_pos_offset) += cspdims[i].axm_size; + *(int *)(cdims + dims_axm_pos_offset) += cspdims[i].axm_size; } free(cdims); - // int split = axm_free / node->cindex; - // for (int j = 0; j < node->cindex; j++) { - // cwhdims[j].min = *(int*)(node->child[j]->surface->bounds + - // axm_min_offset); cwhdims[j].max = *(int*)(node->child[j]->surface->bounds - // + axm_max_offset); if (node->child[j]->surface->bounds->type == - // BOUND_RELATIVE) { - // cwhdims[j].min *= axm_size; - // cwhdims[j].max *= axm_size; - // } - - // cwhdims[j].axm_size = fmax(fmin(split, cwhdims[j].max), cwhdims[j].min); - // cwhdims[j].axo_size = axo_size; - - // axm_free -= cwhdims[j].axm_size; - // } - - // int delta; - // while (axm_free) { - // if (axm_free < cunfixedn) { - // int progress = 0; - // for (int j = 0; j < node->cindex; j++) { - // if (cwhdims[j].fixed) - // continue; - // cwhdims[j].axm_size--; - // axm_free--; - // } - // } - - // // split is guaranteed to be >0 - // split = axm_free / node->cindex; - // for (int j = 0; j < node->cindex; j++) { - // if (cwhdims[j].fixed) - // continue; - - // delta = cwhdims[j].axm_size - fmax(cwhdims[j].axm_size - split, - // bounds->wmin); if (delta == 0) { - // cwhdims[j].fixed = true; - // cunfixedn++; - // continue; - // } - - // cwhdims[j].axm_size -= delta; - // axm_free -= delta; - // } - // } - - // bifurcate_dims(dims, node->axis, node->ratio, &dims0, &dims1); - - // resize_node(node->child[0], dims0); - // resize_node(node->child[1], dims1); return 0; } diff --git a/cursetree/node.h b/cursetree/node.h index 9906af0..f0341f6 100644 --- a/cursetree/node.h +++ b/cursetree/node.h @@ -13,9 +13,11 @@ typedef struct _win_st WINDOW; #define NODE_CHILDREN_GROWTH 1.5 #define CINDEX_MAX UCHAR_MAX -#define NFLAG_EMPTY (0) -#define NFLAG_RESIZE (1<<0) -#define NFLAG_TOOSMALL (1<<1) +#define NFLAG_EMPTY (0) +#define NFLAG_RESIZE (1 << 0) +#define NFLAG_SMALL (1 << 1) +#define NFLAG_SMALLCHILD (1 << 2) +#define NFLAG_LARGE (1 << 3) /* Child Index */ typedef unsigned char cindex; @@ -40,17 +42,16 @@ struct ct_node { /* === External Interface === */ #define IS_PARENT_NODE(node) (node->cindex != 0) -struct ct_node *new_node(struct ct_dims *const dims, - struct ct_bounds *const bounds, +struct ct_node *__node(struct ct_dims *const dims, + struct ct_bounds *const bounds, + struct ct_node *const parent); +struct ct_node *new_node(struct ct_bounds *const bounds, struct ct_node *const parent); -void destroy_node(struct ct_node *const node); + +void __destroy_node(struct ct_node *const node); int resize_node(struct ct_node *const node, struct ct_dims *const new_dims); void collapse_node(struct ct_node **const node, const int i, const bool preserve_bounds); -// void bifurcate_window_node(struct ct_node **const node, const enum ct_axis -// axis, -// const int invert_axis, const float ratio); - #endif /* CURSETREE_NODE_H */ diff --git a/cursetree/nsparted.c b/cursetree/nsparted.c deleted file mode 100644 index 67231c3..0000000 --- a/cursetree/nsparted.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "nsparted.h" - -struct ct_nsparted init_nsparted(struct ct_node *const node, - struct ct_dims *const dims) { - struct ct_nsparted nsparted = (struct ct_nsparted) { - .node = node, - .dims = dims, - . - } -} - diff --git a/cursetree/nsparted.h b/cursetree/nsparted.h deleted file mode 100644 index c4fda2d..0000000 --- a/cursetree/nsparted.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef CURSETREE_NSPARTED_H -#define CURSETREE_NSPARTED_H - -#include "node.h" - -/* Surface Partition Dimensions */ -struct ct_spdims { - // Main Axis & Orthogonal Axis Sizes - int axm_size, axo_size; - bool fixed; - int min, max; -}; - -/* Node Surface Partitioner */ -struct ct_nsparted { - struct ct_node *const node; - struct ct_dims *const dims; - - /* Child Surface Partition Dimensions */ - struct ct_spdims *const cspdims; -}; - -struct ct_nsparted *init_nsparted(struct ct_node *const node, - struct ct_dims *const dims); - -#endif /* CURSETREE_NSPARTED_H */ diff --git a/cursetree/surface.c b/cursetree/surface.c index ac1ee50..c003faa 100644 --- a/cursetree/surface.c +++ b/cursetree/surface.c @@ -2,6 +2,7 @@ #include "ncrswrap.h" #include "surface.h" +#include "_ncurses.h" static inline struct ct_surface *__surface(struct ct_dims *const dims, struct ct_bounds *const bounds, @@ -25,8 +26,8 @@ struct ct_surface *new_surface(struct ct_dims *const dims, return __surface(dims, bounds, win); } -void destroy_surface(struct ct_surface *const surface) { - destroy_window(surface->win); +void destroy_surface(const struct ct_surface *const surface) { + delwin(surface->win); free(surface->dims); } @@ -44,3 +45,31 @@ void rebind_surface(struct ct_surface *const surface, free(surface->bounds); surface->bounds = bounds; } + +void sfclear(struct ct_surface *const surface) { + wclear(surface->win); +} + +int sfwidth(const struct ct_surface *const surface) { + return getmaxx(surface->win); +} + +int sfheight(const struct ct_surface *const surface) { + return getmaxy(surface->win); +} + +int sfposx(const struct ct_surface *const surface) { + return getbegx(surface->win); +} + +int sfposy(const struct ct_surface *const surface) { + return getbegy(surface->win); +} + +struct ct_dims *sfdims(const struct ct_surface *const surface) { + int x, y, width, height; + sfpos(surface, x, y); + sfsize(surface, width, height); + return new_dims(x, y, width, height); +} + diff --git a/cursetree/surface.h b/cursetree/surface.h index b130cd7..727c489 100644 --- a/cursetree/surface.h +++ b/cursetree/surface.h @@ -16,11 +16,20 @@ struct ct_surface { struct ct_surface *new_surface(struct ct_dims *const dims, struct ct_bounds *const bounds); -void destroy_surface(struct ct_surface *const surface); +void destroy_surface(const struct ct_surface *const surface); void resize_surface(struct ct_surface *const surface, struct ct_dims *const dims); void rebind_surface(struct ct_surface *const surface, struct ct_bounds *const bounds); +#define sfpos(surface, x, y) (x = sfposx(surface), y = sfposy(surface)) +#define sfsize(surface, width, height) (width = sfwidth(surface), height = sfheight(surface)) + +void sclear(struct ct_surface *const surface); +int swidth(const struct ct_surface *const surface); +int sheight(const struct ct_surface *const surface); +int sposx(const struct ct_surface *const surface); +int sposy(const struct ct_surface *const surface); + #endif /* CURSETREE_SURFACE_H */ diff --git a/cursetree/tree.c b/cursetree/tree.c index b0b3b1a..4f49aff 100644 --- a/cursetree/tree.c +++ b/cursetree/tree.c @@ -6,7 +6,7 @@ /* */ static inline struct ct_node *__root_node(void) { - return new_node(termdims(), bounds_none(), NULL); + return __node(termdims(), bounds_none(), NULL); } int init_tree(struct ct_tree **const tree) { @@ -16,7 +16,7 @@ int init_tree(struct ct_tree **const tree) { } void destroy_tree(struct ct_tree *const tree) { - destroy_node(tree->root); + __destroy_node(tree->root); end_ncurses(); free(tree); } From 5e99b09f8a3584e294494d466b1e00baa38f4c9b Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Tue, 16 Sep 2025 18:46:01 +1000 Subject: [PATCH 23/26] (UNDER CONSTRUCTION) fixed bad free + testing AXIS_X bifurcation --- cli/main.c | 33 ++++++++++++++++++----- cursetree/cursetree.c | 18 ++++++++++++- cursetree/dims.c | 1 + cursetree/dims.h | 2 +- cursetree/ncrswrap.h | 9 ------- cursetree/node.c | 61 +++++++++++++++++++++++++------------------ cursetree/node.h | 14 +++++++--- 7 files changed, 92 insertions(+), 46 deletions(-) diff --git a/cli/main.c b/cli/main.c index f78dee2..cd79176 100644 --- a/cli/main.c +++ b/cli/main.c @@ -10,15 +10,34 @@ int main(int argc, char **argv) { struct ct_tree *tree; ct_init(&tree); - init_pair(1, COLOR_CYAN, COLOR_CYAN); - init_pair(2, COLOR_RED, COLOR_RED); + init_pair(1, COLOR_BLACK, COLOR_CYAN); + init_pair(2, COLOR_BLACK, COLOR_RED); - // wbkgd(tree->root->win, COLOR_PAIR(1)); - // wrefresh(tree->root->win); - - int i = 1; - while (1) { + // wbkgd(tree->root->surface->win, COLOR_PAIR(1)); + // wrefresh(tree->root->surface->win); + + struct ct_node *const child1 = new_node(bounds_none(), tree->root); + struct ct_node *const child2 = new_node(bounds_none(), tree->root); + append_child_node(tree->root, child1); + append_child_node(tree->root, child2); + + wbkgd(child1->surface->win, COLOR_PAIR(1)); + wbkgd(child2->surface->win, COLOR_PAIR(2)); + + // wrefresh(child1->surface->win); + + mvwprintw(child2->surface->win, 0, 0, "1@(%d,%d) %dx%d\n", + child1->surface->dims->x, child1->surface->dims->y, + child1->surface->dims->width, child1->surface->dims->height); + mvwprintw(child2->surface->win, 1, 0, "2@(%d,%d) %dx%d\n", + child2->surface->dims->x, child2->surface->dims->y, + child2->surface->dims->width, child2->surface->dims->height); + wrefresh(child2->surface->win); + + + while (1) { ct_update(tree); + napms(100); } destroy_tree(tree); diff --git a/cursetree/cursetree.c b/cursetree/cursetree.c index 90b1476..5c79652 100644 --- a/cursetree/cursetree.c +++ b/cursetree/cursetree.c @@ -1,6 +1,7 @@ #include #include "ncrswrap.h" +#include "node.h" #include "tree.h" #include "_ncurses.h" @@ -12,6 +13,19 @@ int ct_init(struct ct_tree **const tree) { return EXIT_SUCCESS; } +/* Recursively search the tree for update requests. + */ +static void __update_rec(struct ct_node *const node) { + if (node->flags & NFLAG_RESIZE) { + /* TODO: the child has requested a resizing, but resize_node() + * TODO: wastes CPU time resizing itself! + */ + resize_node(node, dup_dims(node->surface->dims)); + } + for (int i=0; i < node->cindex; i++) { + __update_rec(node->child[i]); + } +} void ct_update(struct ct_tree *const tree) { const int key = wgetch(curscr); @@ -32,9 +46,11 @@ void ct_update(struct ct_tree *const tree) { doupdate(); break; default: - wclear(tree->root->surface->win); + // wclear(tree->root->surface->win); mvwprintw(tree->root->surface->win, 0, 0, " \r%d\n", key); wrefresh(tree->root->surface->win); break; } + + __update_rec(tree->root); } diff --git a/cursetree/dims.c b/cursetree/dims.c index e4073a2..f0bb0ef 100644 --- a/cursetree/dims.c +++ b/cursetree/dims.c @@ -4,6 +4,7 @@ #include #include "dims.h" +#include "node.h" #include "util.h" struct ct_dims *new_dims(const int x, const int y, const int width, const int height) { diff --git a/cursetree/dims.h b/cursetree/dims.h index 25e6ccc..86417ba 100644 --- a/cursetree/dims.h +++ b/cursetree/dims.h @@ -3,7 +3,7 @@ #define __BOUND_UNLIMITED (-1) #define __BOUND_ABS_MIN (1) -#define __BOUND_ABS_MAX (INT_MAX) +#define __BOUND_ABS_MAX (INT_MAX / CINDEX_MAX - 1) #define __BOUND_REL_MIN ((float)0) #define __BOUND_REL_MAX ((float)1) diff --git a/cursetree/ncrswrap.h b/cursetree/ncrswrap.h index c6845e0..f21ffbf 100644 --- a/cursetree/ncrswrap.h +++ b/cursetree/ncrswrap.h @@ -29,15 +29,6 @@ WINDOW *new_window(const int x, const int y, const int width, const int height); WINDOW *new_window_fs(void); void destroy_window(WINDOW *); -// #define winpos(win, x, y) (x = winposx(win), y = winposy(win)) -// #define winsize(win, width, height) \ -// (width = winwidth(win), height = winheight(win)) -// int winposx(WINDOW *const); -// int winposy(WINDOW *const); -// int winwidth(WINDOW *const); -// int winheight(WINDOW *const); -// struct ct_dims *windims(WINDOW *win); - int resizemv_window(WINDOW *const win, const int x, const int y, const int width, const int height); diff --git a/cursetree/node.c b/cursetree/node.c index 4c5a300..3f1abaf 100644 --- a/cursetree/node.c +++ b/cursetree/node.c @@ -46,7 +46,7 @@ struct ct_node *new_node(struct ct_bounds *const bounds, * WARNING: new_node doesn't set the NFLAG_RESIZE request flag * WARNING: that should be done by a function calling new_node */ - return __node(parent->surface->dims, bounds, parent); + return __node(dup_dims(parent->surface->dims), bounds, parent); } /* WARNING: Do NOT use __destroy_node() to destroy a node's children! @@ -76,8 +76,9 @@ struct ct_spdims { /* */ -int resize_node(struct ct_node *const node, struct ct_dims *const dims) { +int resize_node(struct ct_node *const node, struct ct_dims *dims) { resize_surface(node->surface, dims); + dims = node->surface->dims; if (node->surface->bounds->wmin > dims->width || node->surface->bounds->hmin > dims->height) { @@ -101,36 +102,43 @@ int resize_node(struct ct_node *const node, struct ct_dims *const dims) { } int axm_size, axm_free; - size_t bounds_axm_min_offset, bounds_axm_max_offset; - size_t dims_axm_pos_offset, dims_axm_size_offset; + /* DEBUG: WARNING :DEBUG */ + // size_t bounds_axm_min_offset, bounds_axm_max_offset; + // size_t dims_axm_pos_offset, dims_axm_size_offset; if (node->axis == AXIS_X) { axm_free = dims->width; axm_size = dims->width; - dims_axm_pos_offset = offsetof(struct ct_dims, x); - dims_axm_size_offset = offsetof(struct ct_dims, width); - bounds_axm_min_offset = offsetof(struct ct_bounds, wmin); - bounds_axm_max_offset = offsetof(struct ct_bounds, wmax); + /* DEBUG: WARNING :DEBUG */ + // dims_axm_pos_offset = offsetof(struct ct_dims, x); + // dims_axm_size_offset = offsetof(struct ct_dims, width); + // bounds_axm_min_offset = offsetof(struct ct_bounds, wmin); + // bounds_axm_max_offset = offsetof(struct ct_bounds, wmax); } else { assert(node->axis == AXIS_Y); axm_free = dims->height; axm_size = dims->height; - - dims_axm_pos_offset = offsetof(struct ct_dims, y); - dims_axm_size_offset = offsetof(struct ct_dims, height); - bounds_axm_min_offset = offsetof(struct ct_bounds, hmin); - bounds_axm_max_offset = offsetof(struct ct_bounds, hmax); + + /* DEBUG: WARNING :DEBUG */ + // dims_axm_pos_offset = offsetof(struct ct_dims, y); + // dims_axm_size_offset = offsetof(struct ct_dims, height); + // bounds_axm_min_offset = offsetof(struct ct_bounds, hmin); + // bounds_axm_max_offset = offsetof(struct ct_bounds, hmax); } struct ct_spdims cspdims[node->cindex]; cindex parts_n = node->cindex; memset(cspdims, 0, sizeof(struct ct_spdims) * node->cindex); for (int i = 0; i < node->cindex; i++) { - cspdims[i].min = - *(int *)(node->child[i]->surface->bounds + bounds_axm_min_offset); - cspdims[i].max = - *(int *)(node->child[i]->surface->bounds + bounds_axm_max_offset); + /* DEBUG: WARNING :DEBUG */ + // cspdims[i].min = + // *(int *)(node->child[i]->surface->bounds + bounds_axm_min_offset); + // cspdims[i].max = + // *(int *)(node->child[i]->surface->bounds + bounds_axm_max_offset); + cspdims[i].min = node->child[i]->surface->bounds->wmin; + cspdims[i].max = node->child[i]->surface->bounds->wmax; + if (node->child[i]->surface->bounds->type == BOUND_RELATIVE) { cspdims[i].min *= axm_size; cspdims[i].max *= axm_size; @@ -165,9 +173,13 @@ int resize_node(struct ct_node *const node, struct ct_dims *const dims) { // *(int*)(cdims + dims_axo_pos_offset) = *(int*)(dims + dims_axo_pos_offset); // *(int*)(cdims + dims_axo_size_offset) = axo_size; for (int i = 0; i < node->cindex; i++) { - *(int *)(cdims + dims_axm_size_offset) = cspdims[i].axm_size; + /* DEBUG: WARNING :DEBUG */ + // *(int *)(cdims + dims_axm_size_offset) = cspdims[i].axm_size; + cdims->width = cspdims[i].axm_size; resize_node(node->child[i], dup_dims(cdims)); - *(int *)(cdims + dims_axm_pos_offset) += cspdims[i].axm_size; + /* DEBUG: WARNING :DEBUG */ + // *(int *)(cdims + dims_axm_pos_offset) += cspdims[i].axm_size; + cdims->x += cspdims[i].axm_size; } free(cdims); @@ -200,13 +212,13 @@ static int __set_cbounds(struct ct_node *const parent, parent->cbounds.wmin_rel = p_wmin_rel; parent->cbounds.hmin_rel = p_hmin_rel; - assert(parent->cbounds.wmin_rel > __BOUND_REL_MIN); - assert(parent->cbounds.hmin_rel > __BOUND_REL_MIN); + assert(parent->cbounds.wmin_rel >= __BOUND_REL_MIN); + assert(parent->cbounds.hmin_rel >= __BOUND_REL_MIN); } else { parent->cbounds.wmin_abs += c_wmin; parent->cbounds.hmin_abs += c_hmin; - assert(parent->cbounds.wmin_abs > __BOUND_ABS_MIN); - assert(parent->cbounds.hmin_abs > __BOUND_ABS_MIN); + assert(parent->cbounds.wmin_abs >= __BOUND_ABS_MIN); + assert(parent->cbounds.hmin_abs >= __BOUND_ABS_MIN); } return 0; } @@ -317,8 +329,7 @@ static int __index_as_child(const struct ct_node *const node, return 2; } -/* - * If preserve_bounds is set then upon collapse the new node maintains the +/* If preserve_bounds is set then upon collapse the new node maintains the * original node's bounds struct. Otherwise the bounds of the child collapsed * onto are used. */ diff --git a/cursetree/node.h b/cursetree/node.h index f0341f6..b62438b 100644 --- a/cursetree/node.h +++ b/cursetree/node.h @@ -11,7 +11,7 @@ typedef struct _win_st WINDOW; #define NODE_INIT_CHILDREN 4 #define NODE_CHILDREN_GROWTH 1.5 -#define CINDEX_MAX UCHAR_MAX +#define CINDEX_MAX (UCHAR_MAX) #define NFLAG_EMPTY (0) #define NFLAG_RESIZE (1 << 0) @@ -47,9 +47,17 @@ struct ct_node *__node(struct ct_dims *const dims, struct ct_node *const parent); struct ct_node *new_node(struct ct_bounds *const bounds, struct ct_node *const parent); - void __destroy_node(struct ct_node *const node); -int resize_node(struct ct_node *const node, struct ct_dims *const new_dims); + +int resize_node(struct ct_node *const node, struct ct_dims *dims); + +int insert_child_node(struct ct_node *const parent, struct ct_node *const child, + const cindex i); +int append_child_node(struct ct_node *const parent, + struct ct_node *const child); +struct ct_node *remove_child_node(struct ct_node *const parent, + const cindex i); +int destroy_child_node(struct ct_node *const parent, const cindex i); void collapse_node(struct ct_node **const node, const int i, const bool preserve_bounds); From dce97e6b3ebe30480cd2ae6247826cd1a5304ded Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Tue, 16 Sep 2025 21:30:14 +1000 Subject: [PATCH 24/26] bug fixes + general X axis splitting! --- cli/main.c | 12 +++++++--- cursetree/cursetree.c | 51 +++++++++++++++++++++++++++++-------------- cursetree/node.c | 11 +++++----- cursetree/node.h | 1 + cursetree/surface.c | 14 ++++++++---- cursetree/surface.h | 13 ++++++----- 6 files changed, 69 insertions(+), 33 deletions(-) diff --git a/cli/main.c b/cli/main.c index cd79176..a140f0b 100644 --- a/cli/main.c +++ b/cli/main.c @@ -12,17 +12,22 @@ int main(int argc, char **argv) { init_pair(1, COLOR_BLACK, COLOR_CYAN); init_pair(2, COLOR_BLACK, COLOR_RED); + init_pair(3, COLOR_BLACK, COLOR_GREEN); + // wbkgd(tree->root->surface->win, COLOR_PAIR(1)); // wrefresh(tree->root->surface->win); struct ct_node *const child1 = new_node(bounds_none(), tree->root); struct ct_node *const child2 = new_node(bounds_none(), tree->root); + struct ct_node *const child3 = new_node(bounds_none(), tree->root); append_child_node(tree->root, child1); append_child_node(tree->root, child2); + append_child_node(tree->root, child3); wbkgd(child1->surface->win, COLOR_PAIR(1)); wbkgd(child2->surface->win, COLOR_PAIR(2)); + wbkgd(child3->surface->win, COLOR_PAIR(3)); // wrefresh(child1->surface->win); @@ -32,12 +37,13 @@ int main(int argc, char **argv) { mvwprintw(child2->surface->win, 1, 0, "2@(%d,%d) %dx%d\n", child2->surface->dims->x, child2->surface->dims->y, child2->surface->dims->width, child2->surface->dims->height); - wrefresh(child2->surface->win); - + mvwprintw(child2->surface->win, 1, 0, "2@(%d,%d) %dx%d\n", + child3->surface->dims->x, child3->surface->dims->y, + child3->surface->dims->width, child3->surface->dims->height); while (1) { ct_update(tree); - napms(100); + // napms(100); } destroy_tree(tree); diff --git a/cursetree/cursetree.c b/cursetree/cursetree.c index 5c79652..47cef7a 100644 --- a/cursetree/cursetree.c +++ b/cursetree/cursetree.c @@ -1,9 +1,9 @@ #include +#include "_ncurses.h" #include "ncrswrap.h" #include "node.h" #include "tree.h" -#include "_ncurses.h" int ct_init(struct ct_tree **const tree) { /* Initialise NCurses Library & Root Node */ @@ -13,44 +13,63 @@ int ct_init(struct ct_tree **const tree) { return EXIT_SUCCESS; } +void ct_redraw(void) { + /* TODO: hide doupdate() behind some other method */ + /* flush ncurses virtual screen -> physical screen */ + doupdate(); +} + /* Recursively search the tree for update requests. + * Returns: + * 0 - success (no action required) + * 1 - success (request: redraw the screen) */ -static void __update_rec(struct ct_node *const node) { +static int __update_rec(struct ct_node *const node) { + int result = 0; + struct ct_dims *dims; + if (node->flags & NFLAG_RESIZE) { /* TODO: the child has requested a resizing, but resize_node() * TODO: wastes CPU time resizing itself! */ - resize_node(node, dup_dims(node->surface->dims)); + dims = IS_ROOT_NODE(node) ? termdims() : dup_dims(node->surface->dims); + resize_node(node, dims); + result = 1; } - for (int i=0; i < node->cindex; i++) { - __update_rec(node->child[i]); + if (node->surface->updatereq) { + sfflush(node->surface); } + + for (int i = 0; i < node->cindex; i++) { + result |= __update_rec(node->child[i]); + } + return result; } void ct_update(struct ct_tree *const tree) { const int key = wgetch(curscr); /* ncurses binds a SIGWINCH handler if SIGWINCH has SIG_DFL disposition - * when initscr(3x) is called. This handler emits KEY_RESIZE (decimal 410) to stdin. - * REF: manpages -> resizeterm(3x) initscr(3x) wgetch(3x) + * when initscr(3x) is called. This handler emits KEY_RESIZE (decimal 410) to + * stdin. REF: manpages -> resizeterm(3x) initscr(3x) wgetch(3x) */ switch (key) { case -1: - wclear(tree->root->surface->win); - mvwprintw(tree->root->surface->win, 0, 0, " \r-1\n"); - wrefresh(tree->root->surface->win); + // wclear(tree->root->surface->win); + // mvwprintw(tree->root->surface->win, 0, 0, " \r-1\n"); + // wrefresh(tree->root->surface->win); return; case KEY_RESIZE: - resize_tree(tree, termdims()); - // flush ncurses virtual screen -> physical screen - doupdate(); + tree->root->flags |= NFLAG_RESIZE; break; default: // wclear(tree->root->surface->win); - mvwprintw(tree->root->surface->win, 0, 0, " \r%d\n", key); - wrefresh(tree->root->surface->win); + // mvwprintw(tree->root->surface->win, 0, 0, " \r%d\n", key); + // wrefresh(tree->root->surface->win); break; } - __update_rec(tree->root); + if (__update_rec(tree->root)) { + ct_redraw(); + } } diff --git a/cursetree/node.c b/cursetree/node.c index 3f1abaf..9fbcfeb 100644 --- a/cursetree/node.c +++ b/cursetree/node.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -119,7 +120,7 @@ int resize_node(struct ct_node *const node, struct ct_dims *dims) { assert(node->axis == AXIS_Y); axm_free = dims->height; axm_size = dims->height; - + /* DEBUG: WARNING :DEBUG */ // dims_axm_pos_offset = offsetof(struct ct_dims, y); // dims_axm_size_offset = offsetof(struct ct_dims, height); @@ -138,7 +139,7 @@ int resize_node(struct ct_node *const node, struct ct_dims *dims) { // *(int *)(node->child[i]->surface->bounds + bounds_axm_max_offset); cspdims[i].min = node->child[i]->surface->bounds->wmin; cspdims[i].max = node->child[i]->surface->bounds->wmax; - + if (node->child[i]->surface->bounds->type == BOUND_RELATIVE) { cspdims[i].min *= axm_size; cspdims[i].max *= axm_size; @@ -236,9 +237,9 @@ int insert_child_node(struct ct_node *const parent, struct ct_node *const child, return 2; else if (parent->cindex == parent->csize) { // grow child array size and clamp maximum - parent->csize *= NODE_CHILDREN_GROWTH; - if (parent->csize > CINDEX_MAX) - parent->csize = CINDEX_MAX; + cindex new_csize = parent->csize * NODE_CHILDREN_GROWTH; + // check overflow + parent->csize = (parent->csize <= new_csize) ? new_csize : CINDEX_MAX; parent->child = reallocarray(parent->child, parent->csize, sizeof(struct ct_node *)); diff --git a/cursetree/node.h b/cursetree/node.h index b62438b..316b8e3 100644 --- a/cursetree/node.h +++ b/cursetree/node.h @@ -40,6 +40,7 @@ struct ct_node { }; /* === External Interface === */ +#define IS_ROOT_NODE(node) (node->parent == NULL) #define IS_PARENT_NODE(node) (node->cindex != 0) struct ct_node *__node(struct ct_dims *const dims, diff --git a/cursetree/surface.c b/cursetree/surface.c index c003faa..a028bc8 100644 --- a/cursetree/surface.c +++ b/cursetree/surface.c @@ -3,6 +3,7 @@ #include "ncrswrap.h" #include "surface.h" #include "_ncurses.h" +#include "ncurses.h" static inline struct ct_surface *__surface(struct ct_dims *const dims, struct ct_bounds *const bounds, @@ -46,10 +47,6 @@ void rebind_surface(struct ct_surface *const surface, surface->bounds = bounds; } -void sfclear(struct ct_surface *const surface) { - wclear(surface->win); -} - int sfwidth(const struct ct_surface *const surface) { return getmaxx(surface->win); } @@ -73,3 +70,12 @@ struct ct_dims *sfdims(const struct ct_surface *const surface) { return new_dims(x, y, width, height); } +void sfclear(struct ct_surface *const surface) { + wclear(surface->win); + surface->updatereq = true; +} + +void sfflush(struct ct_surface *const surface) { + wnoutrefresh(surface->win); + surface->updatereq = false; +} diff --git a/cursetree/surface.h b/cursetree/surface.h index 727c489..c84f7ee 100644 --- a/cursetree/surface.h +++ b/cursetree/surface.h @@ -26,10 +26,13 @@ void rebind_surface(struct ct_surface *const surface, #define sfpos(surface, x, y) (x = sfposx(surface), y = sfposy(surface)) #define sfsize(surface, width, height) (width = sfwidth(surface), height = sfheight(surface)) -void sclear(struct ct_surface *const surface); -int swidth(const struct ct_surface *const surface); -int sheight(const struct ct_surface *const surface); -int sposx(const struct ct_surface *const surface); -int sposy(const struct ct_surface *const surface); +int sfwidth(const struct ct_surface *const surface); +int sfheight(const struct ct_surface *const surface); +int sfposx(const struct ct_surface *const surface); +int sfposy(const struct ct_surface *const surface); +struct ct_dims *sfdims(const struct ct_surface *const surface); + +void sfclear(struct ct_surface *const surface); +void sfflush(struct ct_surface *const surface); #endif /* CURSETREE_SURFACE_H */ From 9a6786b2b5b9c1f1dc13afc137c4006c263aeb25 Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Tue, 16 Sep 2025 23:08:44 +1000 Subject: [PATCH 25/26] fixed missing gaps showing --- cli/main.c | 48 +++++++++++++++++++++++++++-------------- cursetree/dims.c | 34 ----------------------------- cursetree/node.c | 56 ++++++++++++++++++++++-------------------------- 3 files changed, 58 insertions(+), 80 deletions(-) diff --git a/cli/main.c b/cli/main.c index a140f0b..00662e5 100644 --- a/cli/main.c +++ b/cli/main.c @@ -5,6 +5,12 @@ #include #include "cursetree.h" +#include "surface.h" + +#define nprintsfdims(nout, i, label, nin) \ + (mvwprintw((nout)->surface->win, (i), 0, "%s@(%d,%d) %dx%d", (label), \ + (nin)->surface->dims->x, (nin)->surface->dims->y, \ + (nin)->surface->dims->width, (nin)->surface->dims->height)) int main(int argc, char **argv) { struct ct_tree *tree; @@ -13,37 +19,47 @@ int main(int argc, char **argv) { init_pair(1, COLOR_BLACK, COLOR_CYAN); init_pair(2, COLOR_BLACK, COLOR_RED); init_pair(3, COLOR_BLACK, COLOR_GREEN); - - - // wbkgd(tree->root->surface->win, COLOR_PAIR(1)); - // wrefresh(tree->root->surface->win); + init_pair(4, COLOR_BLACK, COLOR_MAGENTA); + init_pair(5, COLOR_BLACK, COLOR_YELLOW); struct ct_node *const child1 = new_node(bounds_none(), tree->root); struct ct_node *const child2 = new_node(bounds_none(), tree->root); struct ct_node *const child3 = new_node(bounds_none(), tree->root); + struct ct_node *const child4 = new_node(bounds_none(), tree->root); + struct ct_node *const child5 = new_node(bounds_none(), tree->root); append_child_node(tree->root, child1); append_child_node(tree->root, child2); append_child_node(tree->root, child3); + append_child_node(tree->root, child4); + append_child_node(tree->root, child5); wbkgd(child1->surface->win, COLOR_PAIR(1)); wbkgd(child2->surface->win, COLOR_PAIR(2)); wbkgd(child3->surface->win, COLOR_PAIR(3)); + wbkgd(child4->surface->win, COLOR_PAIR(4)); + wbkgd(child5->surface->win, COLOR_PAIR(5)); - // wrefresh(child1->surface->win); - - mvwprintw(child2->surface->win, 0, 0, "1@(%d,%d) %dx%d\n", - child1->surface->dims->x, child1->surface->dims->y, - child1->surface->dims->width, child1->surface->dims->height); - mvwprintw(child2->surface->win, 1, 0, "2@(%d,%d) %dx%d\n", - child2->surface->dims->x, child2->surface->dims->y, - child2->surface->dims->width, child2->surface->dims->height); - mvwprintw(child2->surface->win, 1, 0, "2@(%d,%d) %dx%d\n", - child3->surface->dims->x, child3->surface->dims->y, - child3->surface->dims->width, child3->surface->dims->height); + child3->axis = AXIS_Y; + struct ct_node *const child3_1 = new_node(bounds_none(), child3); + struct ct_node *const child3_2 = new_node(bounds_none(), child3); + append_child_node(child3, child3_1); + append_child_node(child3, child3_2); + wbkgd(child3_1->surface->win, COLOR_PAIR(1)); + wbkgd(child3_2->surface->win, COLOR_PAIR(5)); while (1) { + nprintsfdims(child2, 0, "R", tree->root); + nprintsfdims(child2, 1, "1", child1); + nprintsfdims(child2, 2, "2", child2); + nprintsfdims(child2, 3, "3", child3); + nprintsfdims(child2, 4, "4", child4); + nprintsfdims(child2, 5, "5", child5); + + nprintsfdims(child2, 7, "3_1", child3_1); + nprintsfdims(child2, 8, "3_2", child3_2); + + ct_update(tree); - // napms(100); } destroy_tree(tree); diff --git a/cursetree/dims.c b/cursetree/dims.c index f0bb0ef..771c68a 100644 --- a/cursetree/dims.c +++ b/cursetree/dims.c @@ -86,37 +86,3 @@ struct ct_bounds *dup_bounds(const struct ct_bounds *const bounds) { return dup; } - -/* Calculate the dimensions for nodes resulting from a bifurcation. - * Returns 0 on success, and 1 on failure if any width/height are 0 characters. - * WARNING: This function does not guarantee the x,y positions returned - * WARNING: are valid screen coordinates. - */ -// int bifurcate_dims(const struct ct_dims *const parent_dims, -// const enum ct_axis axis, const float ratio, -// struct ct_dims **const dims0, struct ct_dims **const -// dims1) { -// assert(0 < ratio && ratio < 1); -// struct ct_dims *_dims0, *_dims1; - -// _dims0 = dup_dims(parent_dims); -// _dims1 = dup_dims(parent_dims); - -// if (axis == AXIS_X) { -// _dims0->width *= ratio; -// _dims1->width -= _dims0->width; -// _dims1->x += _dims0->width; -// } else { -// _dims0->height *= ratio; -// _dims1->height -= _dims0->height; -// _dims1->y += _dims0->height; -// } - -// if (!_dims0->width || !_dims0->height || !_dims1->width || !_dims1->height) -// return 1; - -// // propagate bifurcated dimensions -// *dims0 = _dims0; -// *dims1 = _dims1; -// return 0; -// } diff --git a/cursetree/node.c b/cursetree/node.c index 9fbcfeb..d3c4422 100644 --- a/cursetree/node.c +++ b/cursetree/node.c @@ -103,42 +103,36 @@ int resize_node(struct ct_node *const node, struct ct_dims *dims) { } int axm_size, axm_free; - /* DEBUG: WARNING :DEBUG */ - // size_t bounds_axm_min_offset, bounds_axm_max_offset; - // size_t dims_axm_pos_offset, dims_axm_size_offset; + size_t bounds_axm_min_offset, bounds_axm_max_offset; + size_t dims_axm_pos_offset, dims_axm_size_offset; if (node->axis == AXIS_X) { axm_free = dims->width; axm_size = dims->width; - /* DEBUG: WARNING :DEBUG */ - // dims_axm_pos_offset = offsetof(struct ct_dims, x); - // dims_axm_size_offset = offsetof(struct ct_dims, width); - // bounds_axm_min_offset = offsetof(struct ct_bounds, wmin); - // bounds_axm_max_offset = offsetof(struct ct_bounds, wmax); + dims_axm_pos_offset = offsetof(struct ct_dims, x); + dims_axm_size_offset = offsetof(struct ct_dims, width); + bounds_axm_min_offset = offsetof(struct ct_bounds, wmin); + bounds_axm_max_offset = offsetof(struct ct_bounds, wmax); } else { assert(node->axis == AXIS_Y); axm_free = dims->height; axm_size = dims->height; - /* DEBUG: WARNING :DEBUG */ - // dims_axm_pos_offset = offsetof(struct ct_dims, y); - // dims_axm_size_offset = offsetof(struct ct_dims, height); - // bounds_axm_min_offset = offsetof(struct ct_bounds, hmin); - // bounds_axm_max_offset = offsetof(struct ct_bounds, hmax); + dims_axm_pos_offset = offsetof(struct ct_dims, y); + dims_axm_size_offset = offsetof(struct ct_dims, height); + bounds_axm_min_offset = offsetof(struct ct_bounds, hmin); + bounds_axm_max_offset = offsetof(struct ct_bounds, hmax); } struct ct_spdims cspdims[node->cindex]; cindex parts_n = node->cindex; memset(cspdims, 0, sizeof(struct ct_spdims) * node->cindex); for (int i = 0; i < node->cindex; i++) { - /* DEBUG: WARNING :DEBUG */ - // cspdims[i].min = - // *(int *)(node->child[i]->surface->bounds + bounds_axm_min_offset); - // cspdims[i].max = - // *(int *)(node->child[i]->surface->bounds + bounds_axm_max_offset); - cspdims[i].min = node->child[i]->surface->bounds->wmin; - cspdims[i].max = node->child[i]->surface->bounds->wmax; + cspdims[i].min = *(int *)((char *)node->child[i]->surface->bounds + + bounds_axm_min_offset); + cspdims[i].max = *(int *)((char *)node->child[i]->surface->bounds + + bounds_axm_max_offset); if (node->child[i]->surface->bounds->type == BOUND_RELATIVE) { cspdims[i].min *= axm_size; @@ -149,14 +143,15 @@ int resize_node(struct ct_node *const node, struct ct_dims *dims) { } int split, new_size; - while (parts_n) { - split = axm_free / parts_n; + while (axm_free && parts_n) { + split = (axm_free > parts_n) ? (axm_free / parts_n) : 1; for (int i = 0; i < node->cindex; i++) { if (cspdims[i].fixed) continue; new_size = clampi(cspdims[i].axm_size + split, cspdims[i].min, cspdims[i].max); + if (new_size == cspdims[i].axm_size) { cspdims[i].fixed = true; parts_n--; @@ -165,22 +160,23 @@ int resize_node(struct ct_node *const node, struct ct_dims *dims) { axm_free -= (new_size - cspdims[i].axm_size); cspdims[i].axm_size = new_size; + + if (axm_free == 0) + break; } } struct ct_dims *cdims = dup_dims(dims); /* NOTE: the statements below are done implicitly by dup_dims */ - // *(int*)(cdims + dims_axm_pos_offset) = *(int*)(dims + dims_axm_pos_offset); - // *(int*)(cdims + dims_axo_pos_offset) = *(int*)(dims + dims_axo_pos_offset); + // *(int*)(cdims + dims_axm_pos_offset) = *(int*)(dims + + // dims_axm_pos_offset); + // *(int*)(cdims + dims_axo_pos_offset) = *(int*)(dims + + // dims_axo_pos_offset); // *(int*)(cdims + dims_axo_size_offset) = axo_size; for (int i = 0; i < node->cindex; i++) { - /* DEBUG: WARNING :DEBUG */ - // *(int *)(cdims + dims_axm_size_offset) = cspdims[i].axm_size; - cdims->width = cspdims[i].axm_size; + *(int *)((char *)cdims + dims_axm_size_offset) = cspdims[i].axm_size; resize_node(node->child[i], dup_dims(cdims)); - /* DEBUG: WARNING :DEBUG */ - // *(int *)(cdims + dims_axm_pos_offset) += cspdims[i].axm_size; - cdims->x += cspdims[i].axm_size; + *(int *)((char *)cdims + dims_axm_pos_offset) += cspdims[i].axm_size; } free(cdims); From 7a35f96a9a18a903b197fedc821a28cc35b22941 Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Thu, 18 Sep 2025 12:24:33 +1000 Subject: [PATCH 26/26] attempt to lessen screentearing --- TODO.md | 6 +++++ cli/main.c | 61 ++++++++++++++++++++--------------------- cursetree/cursetree.c | 63 +++++++++++++++++++++++++++++++++++++++---- cursetree/cursetree.h | 1 + 4 files changed, 96 insertions(+), 35 deletions(-) diff --git a/TODO.md b/TODO.md index b982af8..f103fc7 100644 --- a/TODO.md +++ b/TODO.md @@ -12,3 +12,9 @@ then fork and exec shfx as child (+ close ptmx), then close pts as parent. - [ ] cursetree shits itself if width/height are 0 on bifurcation - [ ] ensure NDEBUG is set for preprocessor to avoid statements if not debugging + + +- [ ] resize_node() is quite wasteful and results could definitely be cached for the next resize_node() call + +- I think this should help! + https://tldp.org/HOWTO/NCURSES-Programming-HOWTO/windows.html#OTHERBORDERFUNCS diff --git a/cli/main.c b/cli/main.c index 00662e5..dd4f338 100644 --- a/cli/main.c +++ b/cli/main.c @@ -23,43 +23,44 @@ int main(int argc, char **argv) { init_pair(5, COLOR_BLACK, COLOR_YELLOW); struct ct_node *const child1 = new_node(bounds_none(), tree->root); - struct ct_node *const child2 = new_node(bounds_none(), tree->root); - struct ct_node *const child3 = new_node(bounds_none(), tree->root); - struct ct_node *const child4 = new_node(bounds_none(), tree->root); - struct ct_node *const child5 = new_node(bounds_none(), tree->root); + // struct ct_node *const child2 = new_node(bounds_none(), tree->root); + // struct ct_node *const child3 = new_node(bounds_none(), tree->root); + // struct ct_node *const child4 = new_node(bounds_none(), tree->root); + // struct ct_node *const child5 = new_node(bounds_none(), tree->root); append_child_node(tree->root, child1); - append_child_node(tree->root, child2); - append_child_node(tree->root, child3); - append_child_node(tree->root, child4); - append_child_node(tree->root, child5); + // append_child_node(tree->root, child2); + // append_child_node(tree->root, child3); + // append_child_node(tree->root, child4); + // append_child_node(tree->root, child5); wbkgd(child1->surface->win, COLOR_PAIR(1)); - wbkgd(child2->surface->win, COLOR_PAIR(2)); - wbkgd(child3->surface->win, COLOR_PAIR(3)); - wbkgd(child4->surface->win, COLOR_PAIR(4)); - wbkgd(child5->surface->win, COLOR_PAIR(5)); + // wbkgd(child2->surface->win, COLOR_PAIR(2)); + // wbkgd(child3->surface->win, COLOR_PAIR(3)); + // wbkgd(child4->surface->win, COLOR_PAIR(4)); + // wbkgd(child5->surface->win, COLOR_PAIR(5)); - child3->axis = AXIS_Y; - struct ct_node *const child3_1 = new_node(bounds_none(), child3); - struct ct_node *const child3_2 = new_node(bounds_none(), child3); - append_child_node(child3, child3_1); - append_child_node(child3, child3_2); - wbkgd(child3_1->surface->win, COLOR_PAIR(1)); - wbkgd(child3_2->surface->win, COLOR_PAIR(5)); + // child3->axis = AXIS_Y; + // struct ct_node *const child3_1 = new_node(bounds_none(), child3); + // struct ct_node *const child3_2 = new_node(bounds_none(), child3); + // append_child_node(child3, child3_1); + // append_child_node(child3, child3_2); + // wbkgd(child3_1->surface->win, COLOR_PAIR(1)); + // wbkgd(child3_2->surface->win, COLOR_PAIR(5)); while (1) { - nprintsfdims(child2, 0, "R", tree->root); - nprintsfdims(child2, 1, "1", child1); - nprintsfdims(child2, 2, "2", child2); - nprintsfdims(child2, 3, "3", child3); - nprintsfdims(child2, 4, "4", child4); - nprintsfdims(child2, 5, "5", child5); - - nprintsfdims(child2, 7, "3_1", child3_1); - nprintsfdims(child2, 8, "3_2", child3_2); - - ct_update(tree); + + // nprintsfdims(child2, 0, "R", tree->root); + // nprintsfdims(child2, 1, "1", child1); + // nprintsfdims(child2, 2, "2", child2); + // nprintsfdims(child2, 3, "3", child3); + // nprintsfdims(child2, 4, "4", child4); + // nprintsfdims(child2, 5, "5", child5); + + // nprintsfdims(child2, 7, "3_1", child3_1); + // nprintsfdims(child2, 8, "3_2", child3_2); + + ct_process(tree); } destroy_tree(tree); diff --git a/cursetree/cursetree.c b/cursetree/cursetree.c index 47cef7a..31504a8 100644 --- a/cursetree/cursetree.c +++ b/cursetree/cursetree.c @@ -1,11 +1,45 @@ +#include #include +#include #include "_ncurses.h" #include "ncrswrap.h" +#include "ncurses.h" #include "node.h" #include "tree.h" +volatile sig_atomic_t got_winch = 0; + +void handle_SIGWINCH(int sig) { + // endwin(); + // Needs to be called after an endwin() so ncurses will initialize + // itself with the new terminal dimensions. + got_winch = 1; +} + +/* + * If ncurses is configured to supply its own SIGWINCH handler, + * - on receipt of a SIGWINCH, the handler sets a flag + * - which is tested in wgetch(3X), doupdate(3X) and restartterm(3X), + * - in turn, calling the resizeterm function, + * - which ungetch's a KEY_RESIZE which will be read on the next call to + * wgetch. REF: `man resizeterm(3x)` + */ +static void bind_SIGWINCH(void) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handle_SIGWINCH; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; /* Restart functions if + interrupted by handler */ + if (sigaction(SIGWINCH, &sa, NULL) == -1) { + perror("bind_SIGWINCH"); + exit(2); + } +} + int ct_init(struct ct_tree **const tree) { + // bind_SIGWINCH(); /* Initialise NCurses Library & Root Node */ init_ncurses(); init_tree(tree); @@ -34,10 +68,13 @@ static int __update_rec(struct ct_node *const node) { */ dims = IS_ROOT_NODE(node) ? termdims() : dup_dims(node->surface->dims); resize_node(node, dims); + node->flags &= ~NFLAG_RESIZE; result = 1; } if (node->surface->updatereq) { - sfflush(node->surface); + // TODO: is this necessary or does mvresize_win do this for us? + wnoutrefresh(node->surface->win); + node->surface->updatereq = 0; } for (int i = 0; i < node->cindex; i++) { @@ -47,7 +84,17 @@ static int __update_rec(struct ct_node *const node) { } void ct_update(struct ct_tree *const tree) { + // WARNING: update_rec does not flush the screen, wgetch will do that for us + if (__update_rec(tree->root)) { + doupdate(); + // redrawwin(curscr); + // wrefresh(curscr); + } +} + +void ct_process(struct ct_tree *const tree) { const int key = wgetch(curscr); + // int key = -1; /* ncurses binds a SIGWINCH handler if SIGWINCH has SIG_DFL disposition * when initscr(3x) is called. This handler emits KEY_RESIZE (decimal 410) to @@ -58,8 +105,9 @@ void ct_update(struct ct_tree *const tree) { // wclear(tree->root->surface->win); // mvwprintw(tree->root->surface->win, 0, 0, " \r-1\n"); // wrefresh(tree->root->surface->win); - return; + break; case KEY_RESIZE: + // got_winch = 1; tree->root->flags |= NFLAG_RESIZE; break; default: @@ -69,7 +117,12 @@ void ct_update(struct ct_tree *const tree) { break; } - if (__update_rec(tree->root)) { - ct_redraw(); - } + // if (got_winch == 1) { + // tree->root->flags |= NFLAG_RESIZE; + // got_winch = 0; + // } + + // if (__update_rec(tree->root)) { + // ct_redraw(); + // } } diff --git a/cursetree/cursetree.h b/cursetree/cursetree.h index 2a31717..d066429 100644 --- a/cursetree/cursetree.h +++ b/cursetree/cursetree.h @@ -6,5 +6,6 @@ /* === External Interface === */ int ct_init(struct ct_tree **const tree); void ct_update(struct ct_tree *const tree); +void ct_process(struct ct_tree *const tree); #endif /* CURSETREE_CURSETREE_H */