From ff292c3477497e03d4cffc1467691e1e6ce54a5c Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Thu, 16 Nov 2023 17:44:47 -0600 Subject: initial commit --- .gitignore | 3 + .lsproot | 0 meson.build | 29 +++++++ meson.options | 2 + src/config.h.in | 10 +++ src/main.c | 19 +++++ src/meson.build | 4 + src/ui.h | 19 +++++ src/ui/base.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/ui/dock.c | 183 +++++++++++++++++++++++++++++++++++++++++ src/ui/ui.internal.h | 81 ++++++++++++++++++ 11 files changed, 576 insertions(+) create mode 100644 .gitignore create mode 100644 .lsproot create mode 100644 meson.build create mode 100644 meson.options create mode 100644 src/config.h.in create mode 100644 src/main.c create mode 100644 src/meson.build create mode 100644 src/ui.h create mode 100644 src/ui/base.c create mode 100644 src/ui/dock.c create mode 100644 src/ui/ui.internal.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dceeaad --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +builddir/ +.cache/ +compile_commands.json diff --git a/.lsproot b/.lsproot new file mode 100644 index 0000000..e69de29 diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..52ff637 --- /dev/null +++ b/meson.build @@ -0,0 +1,29 @@ +project('umps', 'c') + +conf_data = configuration_data() + +opt_ncurses_narrow = get_option('ncurses_narrow') + +if opt_ncurses_narrow + curses_dep = dependency('ncurses') + ncurses_is_wide = false +else + curses_dep = dependency('ncursesw', required : false) + if not curses_dep.found() + curses_dep = dependency('ncurses') + ncurses_is_wide = false + else + ncurses_is_wide = true + endif +endif + +if ncurses_is_wide + conf_data.set('NCURSES_WIDE', true) + conf_data.set('NCURSES_INCLUDE', '') +else + conf_data.set('NCURSES_WIDE', false) + conf_data.set('NCURSES_INCLUDE', '') +endif + +subdir('src') +executable('umps', umps_srcs, dependencies : [curses_dep], include_directories : umps_config_inc) diff --git a/meson.options b/meson.options new file mode 100644 index 0000000..0a36339 --- /dev/null +++ b/meson.options @@ -0,0 +1,2 @@ +option('ncurses_wide', type : 'boolean', value : false, + description : 'set to true to prevent usage of ncursesw') diff --git a/src/config.h.in b/src/config.h.in new file mode 100644 index 0000000..4e9f870 --- /dev/null +++ b/src/config.h.in @@ -0,0 +1,10 @@ +#mesondefine NCURSES_WIDE +#mesondefine NCURSES_INCLUDE + +#ifdef NCURSES_WIDE +#define NCURSES_TEXT(_s) L ## _s +#else +#define NCURSES_TEXT(_s) _s +#endif + +#define NS(_s) NCURSES_TEXT(_s) diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..28823eb --- /dev/null +++ b/src/main.c @@ -0,0 +1,19 @@ +#include +#include +#include + +#include "ui.h" + +int main(void) +{ + setlocale(LC_ALL, ""); + + printf("%d\n", getpid()); + #if 0 + volatile int dbg = 1; + while (dbg); + #endif + + ui_init(); + ui_handle(); +} diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..d90d7c1 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,4 @@ +umps_srcs = files('main.c', 'ui/base.c', 'ui/dock.c') + +configure_file(input : 'config.h.in', output : 'config.h', configuration : conf_data) +umps_config_inc = include_directories('.') diff --git a/src/ui.h b/src/ui.h new file mode 100644 index 0000000..7d78934 --- /dev/null +++ b/src/ui.h @@ -0,0 +1,19 @@ +#ifndef UMPS_UI_H_INCLUDED +#define UMPS_UI_H_INCLUDED + +/* window data types */ + +#include "config.h" + +/* base window type */ +struct ui_window_base; /* base window type */ +struct ui_window_dock; /* dock window type: has windows docked at the four cardinal directions and center */ +struct ui_window_root; /* the root window: a special dock window with a possible list of floating/dialog windows */ + +void ui_init(void); /* sets up the UI */ + +void ui_handle(void); /* handles the UI */ + +extern struct ui_window_root *ui_root; + +#endif /* include guard */ diff --git a/src/ui/base.c b/src/ui/base.c new file mode 100644 index 0000000..74855b1 --- /dev/null +++ b/src/ui/base.c @@ -0,0 +1,226 @@ +#include +#include +#include + +#include "ui.internal.h" + +struct ui_window_root *ui_root = NULL; + +void ui__default_draw_proc(struct ui_window_base *base) +{ + redrawwin(base->cwindow); + wrefresh(base->cwindow); +} + +void ui__init_window_base(struct ui_window_base *base) +{ + base->type = UI__WINDOW_TYPE_BASE; + base->parent = NULL; + base->cwindow = NULL; + base->draw_proc = &ui__default_draw_proc; + base->layout_proc = NULL; +} + +void ui__init_window_dock(struct ui_window_dock *dock) +{ + ui__init_window_base(&dock->super); + dock->super.type = UI__WINDOW_TYPE_DOCK; + dock->super.draw_proc = &ui__dock_default_draw_proc; + dock->super.layout_proc = &ui__dock_default_layout_proc; + + for (unsigned i = 0; i < UI__WINDOW_DOCK_MAX; ++i) + { + dock->children[i] = NULL; + dock->childsizes[i] = 0.0; + } + + dock->focus = UI__WINDOW_FOCUS_NONE; +} + +void ui__init_window_root(struct ui_window_root *root) +{ + ui__init_window_dock(&root->super); + root->super.super.type = UI__WINDOW_TYPE_ROOT; + root->super.super.draw_proc = &ui__root_draw_proc; + root->super.super.layout_proc = &ui__root_layout_proc; + + root->floating = NULL; +} + +/* type-specific destructors */ +void ui__destroy_window_base(struct ui_window_base *base); +void ui__destroy_window_dock(struct ui_window_dock *dock); + +void ui__window_destroy_root(struct ui_window_root *root) +{ + if (root->floating) + ui__destroy_window(root->floating); + + ui__destroy_window_dock(&root->super); +} + +void ui__destroy_window_dock(struct ui_window_dock *dock) +{ + for (unsigned i = 0; i < UI__WINDOW_DOCK_MAX; ++i) + { + if (dock->children[i]) ui__destroy_window(dock->children[i]); + dock->children[i] = NULL; + } + + ui__destroy_window_base(&dock->super); +} + +void ui__destroy_window_base(struct ui_window_base *base) +{ + delwin(base->cwindow); + free(base); +} + +void ui__destroy_window(struct ui_window_base *base) +{ + if (!base) return; + + switch (base->type) + { + case UI__WINDOW_TYPE_ROOT: + ui__window_destroy_root((struct ui_window_root *)base); + break; + case UI__WINDOW_TYPE_DOCK: + ui__destroy_window_dock((struct ui_window_dock *)base); + break; + case UI__WINDOW_TYPE_BASE: + ui__destroy_window_base(base); + } +} + +/* the focused window MUST be a leaf window, or the screen will get clobbered. + * This is because the focused window will have methods like getch() called on it, + * which brings it to the "foreground", squashing whatever is behind it. */ +struct ui_window_base *ui__find_focused(void) +{ + struct ui_window_base *window = (struct ui_window_base *)ui_root; + if (ui_root->floating) return ui_root->floating; + + while (true) + { + switch (window->type) + { + case UI__WINDOW_TYPE_DOCK: + case UI__WINDOW_TYPE_ROOT: + { + struct ui_window_dock *dock = (struct ui_window_dock *)window; + + if (dock->focus == UI__WINDOW_FOCUS_NONE || !dock->children[dock->focus]) + { + for (unsigned i = 0; i < UI__WINDOW_DOCK_MAX; ++i) + { + if (dock->children[i]) + { + dock->focus = i; /* focus the child (skip fallback return statement) */ + goto childfound; + } + } + + return (struct ui_window_base *)dock; /* the dock is focused if it is really a leaf */ + } + +childfound: + window = dock->children[dock->focus]; + break; + } + default: + return window; /* a leaf window is focused */ + } + } +} + +void ui__call_draw_proc(struct ui_window_base *base) +{ + if (base->draw_proc) + { + (*base->draw_proc)(base); + } +} + +void ui__call_layout_proc(struct ui_window_base *base) +{ + if (base->layout_proc) + { + (*base->layout_proc)(base); + } +} + +void ui__traces_draw_proc(struct ui_window_base *base) +{ + box(base->cwindow, 0, 0); + int maxy, maxx; + + mvwaddstr(base->cwindow, 0, 2, "Traces"); + getmaxyx(base->cwindow, maxy, maxx); + for (int i = 1; i < maxy-1; ++i) + { + mvwhline(base->cwindow, i, 1, '%', maxx-2); + } + touchwin(base->cwindow); + wrefresh(base->cwindow); +} + +void ui_init(void) +{ + ui_root = malloc(sizeof(struct ui_window_root)); /* TODO: check */ + assert(ui_root); + + initscr(); + + raw(); + noecho(); + keypad(stdscr, TRUE); + + curs_set(0); + + ui__init_window_root(ui_root); + ui_root->super.super.cwindow = stdscr; /* initialize the ncurses window ourselves */ + + /* set up UI */ + + for (int i = 0; i < UI__WINDOW_DOCK_MAX; ++i) + { + if (i == UI__WINDOW_DOCK_LEFT) continue; + struct ui_window_base *win_traces = malloc(sizeof(struct ui_window_base)); + ui__init_window_base(win_traces); + ui__dock_add_child((struct ui_window_dock *)ui_root, win_traces, i, 1./5); + win_traces->draw_proc = &ui__traces_draw_proc; + } + + ui__call_draw_proc((struct ui_window_base *)ui_root); +} + +void ui_handle(void) +{ + while (true) + { + struct ui_window_base *window = ui__find_focused(); + +#ifdef NCURSES_WIDE + wint_t inp; + wget_wch(window->cwindow, &inp); +#else + int inp = wgetch(window->cwindow); +#endif + + if (inp == NS('q')) + { + break; + } + else if (inp == KEY_RESIZE) + { + ui__call_layout_proc((struct ui_window_base *)ui_root); + ui__call_draw_proc((struct ui_window_base *)ui_root); + } + } + + ui__window_destroy_root(ui_root); + ui_root = NULL; + + endwin(); +} diff --git a/src/ui/dock.c b/src/ui/dock.c new file mode 100644 index 0000000..5b59b71 --- /dev/null +++ b/src/ui/dock.c @@ -0,0 +1,183 @@ +#include +#include + +#include "ui.internal.h" + +unsigned ui__dock_position_opposite(unsigned position) +{ + switch (position) + { + case UI__WINDOW_DOCK_TOP: + return UI__WINDOW_DOCK_BOTTOM; + case UI__WINDOW_DOCK_BOTTOM: + return UI__WINDOW_DOCK_TOP; + case UI__WINDOW_DOCK_LEFT: + return UI__WINDOW_DOCK_RIGHT; + case UI__WINDOW_DOCK_RIGHT: + return UI__WINDOW_DOCK_LEFT; + default: + assert(false); /* trap: this function should never be called here! */ + } +} + +void ui__dock_adjust(struct ui_window_dock *dock, unsigned flags, int *maxy, int *maxx, int *begy, int *begx) +{ + if (flags & 1) + { + if (dock->children[UI__WINDOW_DOCK_TOP]) + { + unsigned top_maxy = getmaxy(dock->children[UI__WINDOW_DOCK_TOP]->cwindow); + *maxy -= top_maxy; + *begy += top_maxy; + } + + if (dock->children[UI__WINDOW_DOCK_BOTTOM]) + { + unsigned bottom_maxy = getmaxy(dock->children[UI__WINDOW_DOCK_BOTTOM]->cwindow); + *maxy -= bottom_maxy; + } + } + + if (flags & 2) + { + if (dock->children[UI__WINDOW_DOCK_LEFT]) + { + unsigned left_maxx = getmaxx(dock->children[UI__WINDOW_DOCK_LEFT]->cwindow); + *maxx -= left_maxx; + *begx += left_maxx; + } + + if (dock->children[UI__WINDOW_DOCK_RIGHT]) + { + unsigned right_maxx = getmaxx(dock->children[UI__WINDOW_DOCK_RIGHT]->cwindow); + *maxx -= right_maxx; + } + } +} + +/* This is hand-coded to fit with the window priorities, as specified here in ascending order: + * - Top/bottom + * - Left/right + * - Center + * + * Note that other methods (layout proc namely) require this same order to be present in the values of the dock constants. */ +WINDOW *ui__dock_place_window(struct ui_window_dock *dock, unsigned position, float size) +{ + int maxy, maxx; + int begy, begx; + + int out_lines, out_cols; + int out_y, out_x; + + getmaxyx(dock->super.cwindow, maxy, maxx); + getbegyx(dock->super.cwindow, begy, begx); + + switch (position) + { + case UI__WINDOW_DOCK_TOP: + out_lines = maxy * size; + out_y = begy; + out_cols = maxx; + out_x = begx; + break; + case UI__WINDOW_DOCK_BOTTOM: + out_lines = maxy * size; + out_y = maxy - out_lines + begy; + out_cols = maxx; + out_x = begx; + break; + case UI__WINDOW_DOCK_LEFT: + ui__dock_adjust(dock, 1, &maxy, &maxx, &begy, &begx); + out_lines = maxy; + out_y = begy; + out_cols = maxx * size; + out_x = begx; + break; + case UI__WINDOW_DOCK_RIGHT: + ui__dock_adjust(dock, 1, &maxy, &maxx, &begy, &begx); + out_lines = maxy; + out_y = begy; + out_cols = maxx * size; + out_x = maxx - out_cols + begx; + break; + case UI__WINDOW_DOCK_CENTER: + ui__dock_adjust(dock, 3, &maxy, &maxx, &begy, &begx); + out_lines = maxy; + out_y = begy; + out_cols = maxx; + out_x = begx; + break; + default: + assert(false); + } + + return newwin(out_lines, out_cols, out_y, out_x); +} + +void ui__dock_add_child(struct ui_window_dock *dock, struct ui_window_base *child, unsigned position, float size) +{ + assert(!dock->children[position]); /* TODO: handle gracefully (technically invalid usage) */ + assert(!child->parent); /* TODO: take this window from its current parent */ + assert(!child->cwindow); + + child->parent = (struct ui_window_base *)dock; + dock->children[position] = child; + dock->childsizes[position] = size; + + if (position != UI__WINDOW_DOCK_CENTER) + { + unsigned opposite = ui__dock_position_opposite(position); + if (dock->children[opposite] && size + dock->childsizes[opposite] > 1) + { + dock->childsizes[position] = 1 - dock->childsizes[opposite]; + } + } + + /* now set up the window for this child */ + child->cwindow = ui__dock_place_window(dock, position, size); +} + +void ui__dock_default_draw_proc(struct ui_window_base *base) +{ + struct ui_window_dock *dock = (struct ui_window_dock *)base; + + wrefresh(dock->super.cwindow); + for (unsigned i = 0; i < UI__WINDOW_DOCK_MAX; ++i) + { + if (dock->children[i]) + ui__call_draw_proc(dock->children[i]); + } +} + +void ui__dock_default_layout_proc(struct ui_window_base *base) +{ + struct ui_window_dock *dock = (struct ui_window_dock *)base; + + /* fix the layout of children */ + for (unsigned i = 0; i < UI__WINDOW_DOCK_MAX; ++i) + { + struct ui_window_base *child = dock->children[i]; + if (!child) continue; + + delwin(child->cwindow); + child->cwindow = ui__dock_place_window(dock, i, dock->childsizes[i]); + ui__call_layout_proc(child); + } +} + +void ui__root_draw_proc(struct ui_window_base *base) +{ + struct ui_window_root *root = (struct ui_window_root *)base; + ui__dock_default_draw_proc(base); + + if (root->floating) ui__call_draw_proc(root->floating); +} + +void ui__root_layout_proc(struct ui_window_base *base) +{ + struct ui_window_root *root = (struct ui_window_root *)base; + ui__dock_default_layout_proc(base); + + /* TODO: adjust floating window position :) */ + if (root->floating) ui__call_layout_proc(root->floating); +} diff --git a/src/ui/ui.internal.h b/src/ui/ui.internal.h new file mode 100644 index 0000000..7c53bd4 --- /dev/null +++ b/src/ui/ui.internal.h @@ -0,0 +1,81 @@ +#ifndef UMPS_UI_INTERNAL_H_INCLUDED +#define UMPS_UI_INTERNAL_H_INCLUDED + +#include "../ui.h" +#include "config.h" +#include NCURSES_INCLUDE + +#define UI__WINDOW_DOCK_TOP (0u) +#define UI__WINDOW_DOCK_BOTTOM (1u) +#define UI__WINDOW_DOCK_LEFT (2u) +#define UI__WINDOW_DOCK_RIGHT (3u) +#define UI__WINDOW_DOCK_CENTER (4u) + +#define UI__WINDOW_FOCUS_NONE (999u) + +#define UI__WINDOW_TYPE_BASE (0u) +#define UI__WINDOW_TYPE_DOCK (1u) +#define UI__WINDOW_TYPE_ROOT (2u) + +#define UI__WINDOW_DOCK_MAX UI__WINDOW_DOCK_CENTER+1 + +/* concrete type definitions */ + +/* called to refresh the window (should refresh children as well) */ +typedef void (ui__draw_proc)(struct ui_window_base *); + +/* called to recalculate the layout of the window (for resize) */ +typedef void (ui__layout_proc)(struct ui_window_base *); + +struct ui_window_base { + unsigned type; + struct ui_window_base *parent; /* the parent of a window manages its memory */ + WINDOW *cwindow; /* ncurses window */ + + ui__draw_proc *draw_proc; + ui__layout_proc *layout_proc; +}; + +struct ui_window_dock { + struct ui_window_base super; + + struct ui_window_base *children[UI__WINDOW_DOCK_MAX]; + float childsizes[UI__WINDOW_DOCK_MAX]; + + unsigned focus; +}; + +struct ui_window_root { + struct ui_window_dock super; + + struct ui_window_base *floating; +}; + +/* internal utils */ + +/* in-place constructors */ +void ui__init_window_base(struct ui_window_base *); +void ui__init_window_dock(struct ui_window_dock *); +void ui__init_window_root(struct ui_window_root *); + +struct ui_window_base *ui__find_focused(void); + +/* callback utils */ +void ui__call_draw_proc(struct ui_window_base *); +void ui__call_layout_proc(struct ui_window_base *); + +/* destructor */ + +void ui__destroy_window(struct ui_window_base *); + +/* docked window utils */ + +void ui__dock_add_child(struct ui_window_dock *, struct ui_window_base *, unsigned position, float size); +void ui__dock_default_draw_proc(struct ui_window_base *base); +void ui__dock_default_layout_proc(struct ui_window_base *base); + +/* root window hooks */ +void ui__root_draw_proc(struct ui_window_base *); +void ui__root_layout_proc(struct ui_window_base *); + +#endif /* include guard */ -- cgit v1.2.3-70-g09d2