commit 2ad2a19cc37a2aa1e13be9949ce6abb43e16643a
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date: Wed, 31 Aug 2022 01:16:59 +0200
Added menu structure
Diffstat:
A | .clang-format | | | 120 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | .gitignore | | | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | Makefile | | | 74 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | bin/main | | | 0 | |
A | include/pane.h | | | 39 | +++++++++++++++++++++++++++++++++++++++ |
A | include/utils.h | | | 24 | ++++++++++++++++++++++++ |
A | src/main.c | | | 67 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/pane.c | | | 347 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
8 files changed, 727 insertions(+), 0 deletions(-)
diff --git a/.clang-format b/.clang-format
@@ -0,0 +1,120 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# clang-format configuration file. Intended for clang-format >= 11.
+#
+# For more information, see:
+#
+# Documentation/process/clang-format.rst
+# https://clang.llvm.org/docs/ClangFormat.html
+# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+#
+---
+AccessModifierOffset: -4
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlines: Left
+AlignOperands: true
+AlignTrailingComments: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+ AfterClass: false
+ AfterControlStatement: false
+ AfterEnum: false
+ AfterFunction: true
+ AfterNamespace: true
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ AfterExternBlock: false
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+ SplitEmptyFunction: true
+ SplitEmptyRecord: true
+ SplitEmptyNamespace: true
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeInheritanceComma: false
+BreakBeforeTernaryOperators: false
+BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: BeforeComma
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: false
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 8
+ContinuationIndentWidth: 8
+Cpp11BracedListStyle: false
+DerivePointerAlignment: false
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: false
+
+IncludeBlocks: Preserve
+IncludeCategories:
+ - Regex: '.*'
+ Priority: 1
+IncludeIsMainRegex: '(Test)?$'
+IndentCaseLabels: false
+IndentGotoLabels: false
+IndentPPDirectives: None
+IndentWidth: 8
+IndentWrappedFunctionNames: false
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: false
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 8
+ObjCSpaceAfterProperty: true
+ObjCSpaceBeforeProtocolList: true
+
+# Taken from git's rules
+PenaltyBreakAssignment: 10
+PenaltyBreakBeforeFirstCallParameter: 30
+PenaltyBreakComment: 10
+PenaltyBreakFirstLessLess: 0
+PenaltyBreakString: 10
+PenaltyExcessCharacter: 100
+PenaltyReturnTypeOnItsOwnLine: 60
+
+PointerAlignment: Right
+ReflowComments: false
+SortIncludes: false
+SortUsingDeclarations: false
+SpaceAfterCStyleCast: false
+SpaceAfterTemplateKeyword: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatementsExceptForEachMacros
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInContainerLiterals: false
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp03
+TabWidth: 8
+UseTab: Always
+...
+
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,56 @@
+.ccls-cache/
+.ccls
+docs/*
+
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
diff --git a/Makefile b/Makefile
@@ -0,0 +1,74 @@
+# GNU Makefile for C Interfaces and Implementations
+#
+# Usage: make [-f path\Makefile] [DEBUG=Y] target
+
+NAME = display
+CC = gcc
+
+SRC = src
+OBJ = .
+BIN = bin
+INCLUDE = include
+LATEX = docs/latex
+
+EXCLUDE = $(SRC)/main.c
+
+SRCS=$(wildcard $(SRC)/*.c)
+
+SRCS := $(filter-out $(EXCLUDE), $(SRCS))
+OBJS=$(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SRCS))
+CFLAGS = -I$(INCLUDE)
+
+ifeq ($(DEBUG),Y)
+ CFLAGS += -Wall -ggdb
+ SRCS := $(filter-out $(SRC)/mem.c, $(SRCS))
+ LIBNAME =$(NAME)d
+ LIB = $(OBJ)/lib$(LIBNAME)
+else
+ CFLAGS += -DNDEBUG
+ SRCS := $(filter-out $(SRC)/memchk.c, $(SRCS))
+ LIBNAME = $(NAME)
+ LIB = $(OBJ)/lib$(LIBNAME)
+endif
+
+all: $(LIB) main
+
+$(LIB): $(OBJS)
+ ar crs $@.a $^
+
+main: main.o
+ gcc $(SRC)/main.c -o $(BIN)/main $(CFLAGS) $(LDFLAGS) -L. -l$(LIBNAME)
+
+$(OBJ)/%.o: $(SRC)/%.c
+ $(CC) -c $< -o $@ $(CFLAGS) $(LDFLAGS)
+
+install:
+ install -d /usr/local/include/$(NAME)
+ install -p -m 644 $(INCLUDE)/* /usr/local/include/$(NAME)
+ install -d /usr/local/lib
+ install -p -m 644 $(LIB).a /usr/local/lib
+
+clean:
+ -$(RM) $(OBJS) $(LIB).a
+
+docs:
+ doxygen
+ make -C $(LATEX)
+
+help:
+ @echo "Terbox2 based display library"
+ @echo
+ @echo "Usage: make [-f path\Makefile] [DEBUG=Y] target"
+ @echo
+ @echo "Target rules:"
+ @echo " all - Compiles libary file [Default]"
+ @echo " install - Installs the static library and header files"
+ @echo " clean - Clean the project by removing binaries"
+ @echo " help - Prints a help message with target rules"
+ @echo " docs - Compile html and pdf documentation using doxygen and pdflatex"
+ @echo
+ @echo "Optional parameters:"
+ @echo " DEBUG - Compile binary file with debug flags enabled"
+ @echo
+
+.PHONY: all clean help docs
diff --git a/bin/main b/bin/main
Binary files differ.
diff --git a/include/pane.h b/include/pane.h
@@ -0,0 +1,39 @@
+#ifndef PANE_H
+#define PANE_H
+
+#define T Pane_T
+typedef struct T *T;
+
+int pane_start(void);
+int pane_stop(void);
+int pane_handle_resize(void);
+int pane_resize(T self);
+T pane_child(T self, int index);
+
+T *pane_split(T self, size_t count, ...);
+T *pane_vsplit(T self, size_t count, ...);
+
+void pane_setTitle(T self, char *title);
+
+struct menuItem_T {
+ void (*callback)(char *, int);
+ char *name;
+};
+
+typedef struct menu_T *menu_T;
+struct menu_T {
+ T pane;
+ char *title;
+ char *separator;
+
+ int padding;
+ int spacing;
+
+ int items_size;
+ struct menuItem_T items[];
+};
+
+void pane_menu(menu_T self);
+
+#undef T
+#endif
diff --git a/include/utils.h b/include/utils.h
@@ -0,0 +1,24 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <stdio.h>
+
+#define MAX(a, b) ((a > b) ? a : b)
+#define MIN(a, b) ((a < b) ? a : b)
+#define CLAMP(a, x, y) ((a) = (MAX(x, MIN(a, y))))
+#define ACLAMP(a, x, y) (MAX(x, MIN(a, y)))
+
+#define UNIMPLEMENTED \
+ do { \
+ fprintf(stderr, "%s:%d: UNIMPLEMENTED\n", __FILE__, __LINE__); \
+ exit(1); \
+ } while (0);
+
+#define UNUSED(x) (void)(x)
+
+#define HANDLE_RESIZE \
+ { \
+ pane_handle_resize(); \
+ }
+
+#endif
diff --git a/src/main.c b/src/main.c
@@ -0,0 +1,67 @@
+#define TB_IMPL
+#define TB_OPT_TRUECOLOR
+
+#include <termbox.h>
+#include <stdio.h>
+
+#include "pane.h"
+#include "utils.h"
+
+extern Pane_T MAIN;
+
+void load(char *pass, int index)
+{
+ pane_stop();
+ UNIMPLEMENTED;
+}
+
+struct menu_T mainMenu = {
+ .pane=NULL,
+ .title = "Main Menu",
+ .separator="<------------->",
+ .padding = 5,
+ .spacing = 2,
+
+ .items = {
+ { load, "Start" },
+ { load, "Help" },
+ { load, "Exit" },
+ },
+ .items_size = 3
+};
+
+int main(int argc, char **argv)
+{
+ struct tb_event ev;
+ int y = 0;
+
+ pane_start();
+ {
+ Pane_T *sections, *mids, *bottoms;
+
+ sections = pane_vsplit(MAIN, 3, 1, 1, 1);
+ mids = pane_split(sections[1], 2, 1, 2);
+ bottoms = pane_split(sections[2], 2, 1, 1);
+
+ pane_vsplit(mids[0], 2, 1, 1);
+ mainMenu.pane = mids[1];
+ pane_menu(&mainMenu);
+
+ tb_present();
+ while (1) {
+ tb_poll_event(&ev);
+ switch (ev.type) {
+ case TB_EVENT_RESIZE:
+ HANDLE_RESIZE;
+ break;
+ case TB_EVENT_KEY:
+ switch (ev.ch)
+ case 'q':
+ goto end;
+ }
+ }
+ }
+end:
+ pane_stop();
+ return 0;
+}
diff --git a/src/pane.c b/src/pane.c
@@ -0,0 +1,347 @@
+#include <termbox.h>
+#include <stdarg.h>
+#include <mem.h>
+
+#include "pane.h"
+#include "utils.h"
+
+#define T Pane_T
+
+struct T {
+ int children_num;
+ T *children;
+ int horizontal;
+ int *rules;
+
+ char *title;
+
+ int width;
+ int height;
+
+ int x;
+ int y;
+};
+
+Pane_T MAIN;
+
+#define BORDER_HORIZONTAL "─"
+#define BORDER_VERTICAL "│"
+#define BORDER_CORNER_1 "┌"
+#define BORDER_CORNER_2 "┐"
+#define BORDER_CORNER_3 "└"
+#define BORDER_CORNER_4 "┘"
+
+void draw_title(T self)
+{
+ if (self->title)
+ tb_printf(self->x + 1, self->y, TB_RED, 0, " %s ", self->title);
+}
+
+void draw_border(T self)
+{
+ size_t i;
+
+ int x_max = self->x + self->width - 1;
+ int y_max = self->y + self->height - 1;
+
+ for (i = self->x + 1; i < x_max; i++) {
+ tb_print(i, self->y, TB_GREEN, 0, BORDER_HORIZONTAL);
+ tb_print(i, y_max, TB_GREEN, 0, BORDER_HORIZONTAL);
+ }
+
+ for (i = self->y + 1; i < y_max; i++) {
+ tb_print(self->x, i, TB_GREEN, 0, BORDER_VERTICAL);
+ tb_print(x_max, i, TB_GREEN, 0, BORDER_VERTICAL);
+ }
+
+ tb_print(self->x, self->y, TB_GREEN, 0, BORDER_CORNER_1);
+ tb_print(x_max, self->y, TB_GREEN, 0, BORDER_CORNER_2);
+ tb_print(self->x, y_max, TB_GREEN, 0, BORDER_CORNER_3);
+ tb_print(x_max, y_max, TB_GREEN, 0, BORDER_CORNER_4);
+
+ tb_printf(self->x + 1, self->y + 1, TB_RED, 0, "%d-%d", self->x,
+ self->y, self->width, self->height);
+
+ draw_title(self);
+}
+
+#define SPLIT_FORMULA(ac, ad, sc, sd) \
+ { \
+ int total = 0, part, next; \
+ size_t i; \
+ int available = self->ad; \
+ int last_split = -1; \
+ \
+ for (i = 0; i < self->children_num; i++) { \
+ self->children[i]->sc = self->sc; \
+ self->children[i]->sd = self->sd; \
+ if (self->rules[i] > 0) { \
+ total += self->rules[i]; \
+ last_split = i; \
+ } else { \
+ available += self->rules[i]; \
+ } \
+ } \
+ \
+ part = available / total; \
+ next = self->ac; \
+ total = 0; \
+ for (i = 0; i < last_split; i++) { \
+ self->children[i]->ac = next; \
+ if (self->rules[i] > 0) { \
+ total += self->children[i]->ad = \
+ part * self->rules[i]; \
+ } else { \
+ self->children[i]->ad = -self->rules[i]; \
+ } \
+ next += self->children[i]->ad; \
+ } \
+ \
+ self->children[i]->ac = next; \
+ self->children[i]->ad = available - total; \
+ next += self->children[i]->ad; \
+ \
+ for (i = i + 1; i < self->children_num; i++) { \
+ self->children[i]->ac = next; \
+ self->children[i]->ad = -self->rules[i]; \
+ next += self->children[i]->ad; \
+ } \
+ }
+
+int calc_children(T self)
+{
+ if (self->horizontal)
+ SPLIT_FORMULA(x, width, y, height)
+ else
+ SPLIT_FORMULA(y, height, x, width)
+
+ return 1;
+}
+
+int pane_resize(T self)
+{
+ size_t i;
+
+ if (self->children_num == 0) {
+ draw_border(self);
+ tb_present();
+ /* usleep(200000); */
+ return 1;
+ }
+
+ calc_children(self);
+ for (i = 0; i < self->children_num; i++)
+ pane_resize(self->children[i]);
+
+ return 1;
+}
+
+int pane_handle_resize(void)
+{
+ MAIN->height = tb_height();
+ MAIN->width = tb_width();
+ MAIN->x = 0;
+ MAIN->y = 0;
+
+ tb_clear();
+ pane_resize(MAIN);
+
+ return 1;
+}
+
+int pane_start(void)
+{
+ tb_init();
+ MAIN = calloc(1, sizeof(*MAIN));
+ pane_handle_resize();
+
+ return 1;
+}
+
+int pane_stop(void)
+{
+ return tb_shutdown();
+}
+
+T pane_child(T self, int index)
+{
+ return self->children[index];
+}
+
+void split(T self, size_t count, va_list ap)
+{
+ int current;
+ size_t i;
+
+ self->children_num = count;
+ self->children = malloc(self->children_num * sizeof(T));
+ self->rules = malloc(self->children_num * sizeof(int));
+
+ for (i = 0; i < self->children_num; i++) {
+ self->children[i] = calloc(1, sizeof(struct T));
+ self->rules[i] = va_arg(ap, int);
+ }
+
+ pane_resize(self);
+}
+
+T *pane_split(T self, size_t count, ...)
+{
+ va_list ap;
+
+ self->horizontal = 1;
+ va_start(ap, count);
+ split(self, count, ap);
+ va_end(ap);
+
+ return self->children;
+}
+
+T *pane_vsplit(T self, size_t count, ...)
+{
+ va_list ap;
+
+ self->horizontal = 0;
+ va_start(ap, count);
+ split(self, count, ap);
+ va_end(ap);
+
+ return self->children;
+}
+
+void pane_setTitle(T self, char *title)
+{
+ self->title = title;
+ if (self->children_num == 0)
+ draw_title(self);
+}
+
+int centerVertical(T self, int len)
+{
+ return (self->height - len) / 2;
+}
+
+int centerHorisontal(T self, int len)
+{
+ return (self->width - len - 2) / 2 + 1;
+}
+
+void pane_clear(T self, int clear_border)
+{
+ size_t i;
+ /* int border = self->children_num != 0; */
+ int border = !clear_border;
+ int width = self->width - 3 * border;
+ char *line = malloc(width * sizeof(char));
+ memset(line, ' ', width);
+
+ for (i = self->y + border; i < self->y + self->height - border; i++)
+ tb_print(self->x + border, i, TB_BLACK, 0, line);
+}
+
+void pane_menu(menu_T menu)
+{
+ struct tb_event ev;
+ int rl_crnt = 0;
+ int i, len, maxi = 0;
+ int sep_len = strlen(menu->separator);
+
+ for (i = 0; i < menu->items_size; i++)
+ if ((len = strlen(menu->items[i].name)) > maxi)
+ maxi = len;
+
+ pane_setTitle(menu->pane, menu->title);
+
+ int ab_crnt, items_num, y_start;
+ ab_crnt = 0;
+redraw:;
+
+ // optimal number of elements to display on the screen
+ int max_items =
+ (menu->pane->height - menu->padding * 2 + menu->spacing) /
+ (menu->spacing + 1);
+ // number of items displayed cant exceed actual number of items in the menu
+ items_num = ACLAMP(max_items, 0, menu->items_size);
+ // staring point for displaying these items for them to be centered
+ y_start = centerVertical(menu->pane, items_num * (menu->spacing + 1) -
+ menu->spacing);
+
+ while (1) {
+ pane_clear(menu->pane, 0);
+ int y = menu->pane->y + y_start;
+ int x = menu->pane->x + centerHorisontal(menu->pane, maxi);
+ int x_separator =
+ menu->pane->x + centerHorisontal(menu->pane, sep_len);
+
+ if (rl_crnt == -1)
+ ab_crnt--;
+
+ if (rl_crnt == items_num)
+ ab_crnt++;
+
+ CLAMP(ab_crnt, 0, menu->items_size - items_num);
+ CLAMP(rl_crnt, 0, items_num - 1);
+
+ if (!menu->items_size) {
+ char *message = "NOTHING TO DISPLAY HERE!";
+ int x = centerHorisontal(menu->pane, strlen(message));
+ tb_print(x, y, TB_BLUE, 0, message);
+ tb_present();
+ tb_poll_event(&ev);
+ return;
+ }
+
+ if (items_num < menu->items_size && ab_crnt == 0) {
+ tb_print(x_separator, y - 1, TB_BLUE, 0,
+ menu->separator);
+ }
+
+ int index = ab_crnt;
+ for (i = 0; i < items_num; i++) {
+ int color = (i == rl_crnt) ? TB_RED : TB_BLUE;
+ tb_printf(x, y, color, 0, "%s",
+ menu->items[ab_crnt + i].name);
+ y += menu->spacing + 1;
+ }
+
+ if (items_num < menu->items_size &&
+ ab_crnt == menu->items_size - items_num) {
+ tb_print(x_separator, y - menu->spacing, TB_BLUE, 0,
+ menu->separator);
+ }
+
+ tb_present();
+
+ while (1) {
+ tb_poll_event(&ev);
+
+ if (ev.type == TB_EVENT_RESIZE) {
+ HANDLE_RESIZE;
+ goto redraw;
+ } else if (ev.type == TB_EVENT_KEY) {
+ if (ev.ch == 'k' || ev.ch == 'w' ||
+ ev.key == TB_KEY_ARROW_UP) {
+ rl_crnt--;
+ break;
+ }
+ if (ev.ch == 'j' || ev.ch == 's' ||
+ ev.key == TB_KEY_ARROW_DOWN) {
+ rl_crnt++;
+ break;
+ }
+ if (ev.ch == 'q' || ev.key == TB_KEY_ESC) {
+ pane_clear(menu->pane, 1);
+ tb_present();
+ return;
+ }
+ if (ev.key == TB_KEY_ENTER) {
+ int index = ab_crnt + rl_crnt;
+ struct menuItem_T *item =
+ menu->items + index;
+ item->callback(item->name, index);
+ return;
+ }
+ }
+ }
+ }
+}