aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.lsproot0
-rw-r--r--meson.build29
-rw-r--r--meson.options2
-rw-r--r--src/config.h.in10
-rw-r--r--src/main.c19
-rw-r--r--src/meson.build4
-rw-r--r--src/ui.h19
-rw-r--r--src/ui/base.c226
-rw-r--r--src/ui/dock.c183
-rw-r--r--src/ui/ui.internal.h81
11 files changed, 576 insertions, 0 deletions
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
--- /dev/null
+++ b/.lsproot
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', '<ncursesw/ncurses.h>')
+else
+ conf_data.set('NCURSES_WIDE', false)
+ conf_data.set('NCURSES_INCLUDE', '<ncurses.h>')
+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 <locale.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#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 <curses.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#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 <stdlib.h>
+#include <assert.h>
+
+#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 */