diff --git a/.gitignore b/.gitignore index 840bb0f..915d39f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ bin/ build/ +logs/ sandbox/ # clangd diff --git a/Makefile b/Makefile index 36b9358..40dc834 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ BUILD := build LIB := lib CLI := cli +CT := cursetree # === MACRO DEFINITIONS === define objpath @@ -18,13 +19,13 @@ 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 # === 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 @@ -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 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,) # === DEVELOPMENT TARGETS === .PHONY: debug run test @@ -55,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/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 ``` diff --git a/TODO.md b/TODO.md index 36d892f..f103fc7 100644 --- a/TODO.md +++ b/TODO.md @@ -9,4 +9,12 @@ 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 + + +- [ ] 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/curse.h b/cli/curse.h deleted file mode 100644 index 72f0e7e..0000000 --- a/cli/curse.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef DORNE_CURSE_H -#define DORNE_CURSE_H - -/* libncurses with wide-character support. */ -#include - -enum crs_termmode { - /* tty cbreak mode */ - TMODE_CBREAK, - /* tty raw mode */ - TMODE_RAW, - /* tty cooked mode (ISIG & IXON not modified)*/ - TMODE_NOCBREAK, - /* tty cooked mode (ISIG & IXON set) */ - TMODE_NORAW, -}; - -int termmode(const enum crs_termmode mode); - -void init_ncurses(void); -WINDOW *new_window(const int x, const int y, const int width, const int height); -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 */ diff --git a/cli/epty.h b/cli/epty.h deleted file mode 100644 index 8dd18e4..0000000 --- a/cli/epty.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef DORNE_EPTY_H -#define DORNE_EPTY_H - -#include - -pid_t forkepty(int *fdmx, int *fderr); - -#endif /* DORNE_EPTY_H */ diff --git a/cli/main.c b/cli/main.c index 956a25e..dd4f338 100644 --- a/cli/main.c +++ b/cli/main.c @@ -4,79 +4,66 @@ #include -// #include "child.h" -#include "curse.h" -#include "tree.h" +#include "cursetree.h" +#include "surface.h" -// struct d_window { -// int ptmx; // fd -// }; - -// struct d_window new_window() { -// struct d_window w = { -// .ptmx = posix_openpt(O_RDWR | O_NOCTTY), -// }; -// } +#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 d_child child; - // spawnchild(&child); + struct ct_tree *tree; + ct_init(&tree); - struct crs_tree *tree; - init_tree(&tree); + init_pair(1, COLOR_BLACK, COLOR_CYAN); + init_pair(2, COLOR_BLACK, COLOR_RED); + init_pair(3, COLOR_BLACK, COLOR_GREEN); + init_pair(4, COLOR_BLACK, COLOR_MAGENTA); + init_pair(5, COLOR_BLACK, COLOR_YELLOW); - 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); + 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); - /* 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(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(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); + // 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) { - // refresh(); - usleep(100000); + 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); - // endwin(); - // free(tree); return EXIT_SUCCESS; } diff --git a/cli/tree.c b/cli/tree.c deleted file mode 100644 index 0c0b975..0000000 --- a/cli/tree.c +++ /dev/null @@ -1,245 +0,0 @@ -#include -#include -#include - -#include "curse.h" -#include "tree.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 *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, - const enum crs_axis axis, - const float ratio, - struct crs_nodedims *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 *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 *__dup_dims(const struct crs_nodedims *dims) { - struct crs_nodedims *dup; - dup = (struct crs_nodedims *)malloc(sizeof(struct crs_nodedims)); - memcpy(dup, dims, sizeof(struct crs_nodedims)); - - return dup; -} - -static struct crs_nodedims *get_dims(const struct crs_node *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); - dims = __dup_dims(node->dims); - } - - 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, - const enum crs_axis axis, const float ratio, - struct crs_nodedims **dims0, - struct crs_nodedims **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); - - 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) { - printf("width0: %d\n", _dims0->width); - printf("height0: %d\n", _dims0->height); - printf("width1: %d\n", _dims1->width); - printf("height1: %d\n", _dims1->height); - return 1; - } - - // propagate bifurcated dimensions - *dims0 = _dims0; - *dims1 = _dims1; - return 0; -} - -static void resize_node(struct crs_node *node, struct crs_nodedims *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); - } else { - /* Abstract Node */ - assert(node->type == NODE_ABSTRACT); - struct crs_nodedims *dims0, *dims1; - - free(node->dims); - node->dims = new_dims; - bifurcate_dims(node, 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 **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"); - 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, (*node)->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, - 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) { - 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) { - *tree = (struct crs_tree *)malloc(sizeof(struct crs_tree)); - - /* Initialise NCurses Library & Standard Screen */ - init_ncurses(); - - (*tree)->root = init_root_node(); - - // (*tree)->root = __alloc_node(NODE_WINDOW); - // crs_init(&(*tree)->root->win); - - return EXIT_SUCCESS; -} - -void destroy_tree(struct crs_tree *const tree) { - /* WARNING: is it ok to delwin(stdscr) ?? */ - 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/cli/tree.h b/cli/tree.h deleted file mode 100644 index c2263f1..0000000 --- a/cli/tree.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef DORNE_TREE_H -#define DORNE_TREE_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; -}; - -/* === 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); - -void bifurcate_window_node(struct crs_node **node, const enum crs_axis axis, - const int invert_axis, const float ratio); - -#endif /* DORNE_TREE_H */ 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 new file mode 100644 index 0000000..31504a8 --- /dev/null +++ b/cursetree/cursetree.c @@ -0,0 +1,128 @@ +#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); + + 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 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! + */ + 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) { + // 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++) { + result |= __update_rec(node->child[i]); + } + return result; +} + +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 + * 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); + break; + case KEY_RESIZE: + // got_winch = 1; + 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); + break; + } + + // 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 new file mode 100644 index 0000000..d066429 --- /dev/null +++ b/cursetree/cursetree.h @@ -0,0 +1,11 @@ +#ifndef CURSETREE_CURSETREE_H +#define CURSETREE_CURSETREE_H + +#include "tree.h" + +/* === 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 */ diff --git a/cursetree/dims.c b/cursetree/dims.c new file mode 100644 index 0000000..771c68a --- /dev/null +++ b/cursetree/dims.c @@ -0,0 +1,88 @@ +#include +#include +#include +#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) { + 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; +} + +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 + *val = clampi(*val, min, max); +} +#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; +} diff --git a/cursetree/dims.h b/cursetree/dims.h new file mode 100644 index 0000000..86417ba --- /dev/null +++ b/cursetree/dims.h @@ -0,0 +1,41 @@ +#ifndef CURSETREE_DIMS_H +#define CURSETREE_DIMS_H + +#define __BOUND_UNLIMITED (-1) +#define __BOUND_ABS_MIN (1) +#define __BOUND_ABS_MAX (INT_MAX / CINDEX_MAX - 1) +#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. + */ +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(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); +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); + +#endif /* CURSETREE_DIMS_H */ diff --git a/cli/curse.c b/cursetree/ncrswrap.c similarity index 62% rename from cli/curse.c rename to cursetree/ncrswrap.c index 3c465fe..d81cc4b 100644 --- a/cli/curse.c +++ b/cursetree/ncrswrap.c @@ -1,9 +1,10 @@ #include #include -#include "curse.h" +#include "_ncurses.h" + +#include "ncrswrap.h" -#define BASE_DELAY 1000000 #define CRS_LOCALE "en_US.UTF-8" /* Set ncurses terminal mode (buffering, character processing, @@ -25,6 +26,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) { @@ -41,8 +54,9 @@ 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); start_color(); @@ -53,6 +67,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, @@ -63,52 +81,34 @@ 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, * 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); } - -/* -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/cursetree/ncrswrap.h b/cursetree/ncrswrap.h new file mode 100644 index 0000000..f21ffbf --- /dev/null +++ b/cursetree/ncrswrap.h @@ -0,0 +1,35 @@ +#ifndef CURSETREE_NCRSWRAP_H +#define CURSETREE_NCRSWRAP_H + +#include "dims.h" + +#ifndef __NCURSES_H +typedef struct _win_st WINDOW; +#endif /* __NCURSES_H */ + +enum crs_termmode { + /* tty cbreak mode */ + TMODE_CBREAK, + /* tty raw mode */ + TMODE_RAW, + /* tty cooked mode (ISIG & IXON not modified)*/ + TMODE_NOCBREAK, + /* tty cooked mode (ISIG & IXON set) */ + TMODE_NORAW, +}; + +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 *); + +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 new file mode 100644 index 0000000..d3c4422 --- /dev/null +++ b/cursetree/node.c @@ -0,0 +1,359 @@ +#include +#include +#include +#include +#include + +#include "dims.h" +#include "node.h" +#include "surface.h" +#include "util.h" + +/* Internal allocator method for ct_node structures. + */ +static inline struct ct_node *__alloc_node(void) { + struct ct_node *node = (struct ct_node *)malloc(sizeof(struct ct_node)); + return node; +} + +/* Returns NULL if memory allocation failed. + * TODO: should dims be given as a parameter, or lazily computed in ct_update? + */ +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){ + .surface = new_surface(dims, bounds), + .flags = NFLAG_EMPTY, + + .parent = parent, + .child = (struct ct_node **)malloc(NODE_INIT_CHILDREN * + sizeof(struct ct_node *)), + .csize = NODE_INIT_CHILDREN, + .cindex = 0, + .axis = AXIS_X, + }; + } + + 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(dup_dims(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. + */ +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); +} + +/* Surface Partition Dimensions */ +/* TODO: can I use unsigned short instead of int? */ +struct ct_spdims { + // Main Axis & Orthogonal Axis Sizes + int axm_size; + bool fixed; + int min, max; +}; + +/* + */ +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) { + 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_SMALLCHILD; + return 1; + } + + 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; + + 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); + } 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); + } + + 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 *)((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; + cspdims[i].max *= axm_size; + } + + cspdims[i].axm_size = 0; + } + + int split, new_size; + 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--; + continue; + } + + 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_axo_size_offset) = axo_size; + for (int i = 0; i < node->cindex; i++) { + *(int *)((char *)cdims + dims_axm_size_offset) = cspdims[i].axm_size; + resize_node(node->child[i], dup_dims(cdims)); + *(int *)((char *)cdims + dims_axm_pos_offset) += cspdims[i].axm_size; + } + free(cdims); + + return 0; +} + +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 { + 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; +} + +/* Returns: + * 0 -> success + * 1 -> failed (max child limit reached) + * 2 -> failed (cumulative relative bounds surpasses 100%) + */ +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 + 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 *)); + } + + // 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++; + // request cursetree recompute dimensions recursively from parent + parent->flags |= NFLAG_RESIZE; + 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); + // request cursetree recompute dimensions recursively from parent + parent->flags |= NFLAG_RESIZE; + 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 { + destroy_child_node(parent, parent_index); + insert_child_node(parent, collapse_target, parent_index); + } + + resize_node(collapse_target, dims); +} diff --git a/cursetree/node.h b/cursetree/node.h new file mode 100644 index 0000000..316b8e3 --- /dev/null +++ b/cursetree/node.h @@ -0,0 +1,66 @@ +#ifndef CURSETREE_NODE_H +#define CURSETREE_NODE_H + +#include "dims.h" +#include "limits.h" +#include "surface.h" + +#ifndef __NCURSES_H +typedef struct _win_st WINDOW; +#endif /* __NCURSES_H */ + +#define NODE_INIT_CHILDREN 4 +#define NODE_CHILDREN_GROWTH 1.5 +#define CINDEX_MAX (UCHAR_MAX) + +#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; + +struct ct_node { + struct ct_surface *surface; + unsigned char flags; + + 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; +}; + +/* === 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, + 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); + +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); + +#endif /* CURSETREE_NODE_H */ diff --git a/cli/_pty.c b/cursetree/pty/_pty.c similarity index 100% rename from cli/_pty.c rename to cursetree/pty/_pty.c diff --git a/cli/_pty.h b/cursetree/pty/_pty.h similarity index 90% rename from cli/_pty.h rename to cursetree/pty/_pty.h index 3c80b95..652178f 100644 --- a/cli/_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/cli/child.c b/cursetree/pty/child.c similarity index 100% rename from cli/child.c rename to cursetree/pty/child.c diff --git a/cli/child.h b/cursetree/pty/child.h similarity index 72% rename from cli/child.h rename to cursetree/pty/child.h index 6ec3978..294b1c3 100644 --- a/cli/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/cli/epty.c b/cursetree/pty/epty.c similarity index 100% rename from cli/epty.c rename to cursetree/pty/epty.c diff --git a/cursetree/pty/epty.h b/cursetree/pty/epty.h new file mode 100644 index 0000000..fa013bb --- /dev/null +++ b/cursetree/pty/epty.h @@ -0,0 +1,8 @@ +#ifndef CURSETREE_EPTY_H +#define CURSETREE_EPTY_H + +#include + +pid_t forkepty(int *fdmx, int *fderr); + +#endif /* CURSETREE_EPTY_H */ diff --git a/cursetree/surface.c b/cursetree/surface.c new file mode 100644 index 0000000..a028bc8 --- /dev/null +++ b/cursetree/surface.c @@ -0,0 +1,81 @@ +#include + +#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, + 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(const struct ct_surface *const surface) { + delwin(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; +} + +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); +} + +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 new file mode 100644 index 0000000..c84f7ee --- /dev/null +++ b/cursetree/surface.h @@ -0,0 +1,38 @@ +#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(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)) + +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 */ diff --git a/cursetree/tree.c b/cursetree/tree.c new file mode 100644 index 0000000..4f49aff --- /dev/null +++ b/cursetree/tree.c @@ -0,0 +1,38 @@ +#include + +#include "ncrswrap.h" +#include "tree.h" + +/* + */ +static inline struct ct_node *__root_node(void) { + return __node(termdims(), bounds_none(), NULL); +} + +int init_tree(struct ct_tree **const tree) { + *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); + end_ncurses(); + free(tree); +} + +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)->surface->dims); + struct ct_dims *const node1dims = dup_dims((*node1)->surface->dims); + + *node0 = *node1; + resize_node(*node0, node1dims); + + *node1 = node0ptr; + resize_node(*node1, node0dims); +} diff --git a/cursetree/tree.h b/cursetree/tree.h new file mode 100644 index 0000000..a170143 --- /dev/null +++ b/cursetree/tree.h @@ -0,0 +1,15 @@ +#ifndef CURSETREE_TREE_H +#define CURSETREE_TREE_H + +#include "node.h" + +struct ct_tree { + struct ct_node *root; +}; + +/* === External Interface === */ +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 */ 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 */ diff --git a/scripts/test b/scripts/test index 6da6b71..2e00268 100755 --- a/scripts/test +++ b/scripts/test @@ -9,16 +9,10 @@ fi MODE= REBUILD=true VALGRIND=false -TARGET=all +TARGET=debug 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 @@ -26,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 @@ -39,4 +33,16 @@ fi if [[ "$REBUILD" == true ]]; then make clean $TARGET fi + +if [[ ! -x "$PROGRAM" ]]; then + echo "[!] Invalid program: \"$PROGRAM\"" + echo "$USAGE" + exit 1 +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