gol

Implementation of Conway's Game of Life writen in C
git clone git://git.dimitrijedobrota.com/gol.git
Log | Files | Refs | README

commit 13229b25c4cd705fee1bccfeb14f626346ffcf42
parent dd16dc66dd4b3fccc27c3f9c4f71808eeaec0b7d
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Wed, 18 May 2022 20:33:42 +0200

Cleanup and Responsive application resize

- Contents of the main.c has been split into multiple files based on the role
- windows.c has been added to handle the window-splitting behavior
- All of the menus and the game itself are fully responsive in both
  Linux and Windows
- All the menus have been tied together as well as streamlined to better
  fit the task at hand

Diffstat:
Ainclude/display.h | 32++++++++++++++++++++++++++++++++
Ainclude/file.h | 13+++++++++++++
Ainclude/utils.h | 22++++++++++++++++++++++
Ainclude/window.h | 27+++++++++++++++++++++++++++
Asrc/display.c | 214+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/file.c | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/main.c | 439++++++++++++++++++++-----------------------------------------------------------
Asrc/utils.c | 4++++
Asrc/window.c | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 735 insertions(+), 328 deletions(-)

diff --git a/include/display.h b/include/display.h @@ -0,0 +1,32 @@ +#ifndef DISPLAY_H +#define DISPLAY_H + +#include "window.h" + +extern window_T MAIN_w; + +typedef int (*input_f)(int); + +struct menu_T { + void (*callback)(window_T, char *); + char *name; +}; + +struct imenu_T { + char *message; + int size; + input_f crit; + char *buffer; +}; + +int input(WINDOW *win, char *buffer, int size, input_f crit); + +int display_start(void); +int display_stop(void); + +void display_menu(window_T win, struct menu_T *items, int size); +int display_imenu(window_T wind, struct imenu_T *items, int size); + +void handle_winch(int sig); + +#endif diff --git a/include/file.h b/include/file.h @@ -0,0 +1,13 @@ +#ifndef FILE_H +#define FILE_H + +#include "display.h" + +int file_setup(void); +void load_files(void); +void free_files(void); +int file_select(char *ext, char ***buffer); + +void load_file(window_T win, char *pass); + +#endif diff --git a/include/utils.h b/include/utils.h @@ -0,0 +1,22 @@ +#ifndef UTILS_H +#define UTILS_H + +#include <curses.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))) + +#ifdef _WIN32 +#define is_term_resized(a, b) is_termresized() +#define usleep(a) _sleep(0.5) +#define HANDLE_RESIZE \ + { \ + resize_term(0, 0); \ + handle_winch(10); \ + goto redraw; \ + } +#else +#define HANDLE_RESIZE goto redraw; +#endif +#endif diff --git a/include/window.h b/include/window.h @@ -0,0 +1,27 @@ +#ifndef WINDOW_H +#define WINDOW_H + +#include <curses.h> + +#define window_T T +typedef struct T *T; + +T window_new(void); +void window_free(T self); +T window_init(T self); +T window_split(T self, int hor, int a, int b); +void window_update_children(T self); + +T window_sibiling(T self); +int window_height(T self); +int window_wight(T self); +WINDOW *window_win(T self); +void window_settings(WINDOW *win); +void window_clear(T self); + +void wcenter_horizontal(T window, int y, int n); +int wcenter_vertical(T window, int n); +void cursor_offset(WINDOW *win, int oy, int ox); + +#undef T +#endif diff --git a/src/display.c b/src/display.c @@ -0,0 +1,214 @@ +#include <curses.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> + +#include "display.h" +#include "utils.h" +#include "window.h" + +#define center_vertical(n) wcenter_vertical(MAIN_W, n); +#define center_horizontal(y, n) wcenter_horizontal(MAIN_W, y, n); + +window_T MAIN_w = NULL; + +int input(WINDOW *win, char *buffer, int size, input_f crit) { + int CLINES = LINES, CCOLS = COLS; + int ch, read = strlen(buffer); + + while ((ch = getch()) != '\n') { + switch (ch) { + case 27: + buffer[read] = '\0'; + return 100; + case KEY_BACKSPACE: + case KEY_LEFT: + if (read > 0) { + cursor_offset(win, 0, -1); + wprintw(win, " "); + cursor_offset(win, 0, -1); + read--; + } + break; + case KEY_UP: + buffer[read] = '\0'; + return -1; + case KEY_DOWN: + buffer[read] = '\0'; + return +1; + default: + if (read < size && crit && crit(ch)) { + wprintw(win, "%c", ch); + buffer[read++] = ch; + } + break; + } + if (is_term_resized(CLINES, CCOLS)) { + buffer[read] = '\0'; + return -100; + } + wrefresh(win); + } + buffer[read] = '\0'; + return 0; +} + +int display_imenu(window_T wind, struct imenu_T *items, int size) { + WINDOW *win; + int y_offset; + int current = 0; + + for (int i = 0; i < size; i++) + items[i].buffer = calloc(5, sizeof(char)); + + int maxi = 0, len = 0; + for (int i = 0; i < size; i++) + if ((len = strlen(items[i].message)) > maxi) + maxi = len; + + curs_set(1); + window_clear(wind); +redraw:; + win = window_win(wind); + y_offset = wcenter_vertical(wind, size); + + for (int i = 0; i < size; i++) { + wcenter_horizontal(wind, y_offset + 2 * i, 20); + wprintw(win, "%*s: %s", -maxi, items[i].message, items[i].buffer); + } + wrefresh(win); + + while (TRUE) { + for (int i = 0; i < size; i++) { + if (current != i) + continue; + + wcenter_horizontal(wind, y_offset + 2 * i, 20); + wprintw(win, "%*s: %s", -maxi, items[i].message, items[i].buffer); + switch (input(win, items[i].buffer, items[i].size, items[i].crit)) { + case -1: + current--; + break; + case 0: + if (++current == size) { + curs_set(0); + return 1; + } + break; + case 1: + current++; + break; + case 100: + curs_set(0); + return 0; + case -100: + HANDLE_RESIZE; + goto redraw; + } + CLAMP(current, 0, size - 1); + } + } + + curs_set(0); +} + +void display_menu(window_T wind, struct menu_T *items, int size) { + WINDOW *win; + int current = 0; + + int maxi = 0, len = 0; + for (int i = 0; i < size; i++) + if ((len = strlen(items[i].name)) > maxi) + maxi = len; + +redraw:; + win = window_win(wind); + int CLINES = LINES, CCOLS = COLS; + int y_offset = wcenter_vertical(wind, size * 2); + while (TRUE) { + CLAMP(current, 0, size - 1); + + for (int i = 0; i < size; i++) { + wattrset(win, COLOR_PAIR(i == current ? 1 : 0)); + wcenter_horizontal(wind, y_offset + i * 2, maxi); + wprintw(win, "%s", items[i].name); + } + wrefresh(win); + + while (TRUE) { + int c = getch(); + if (c == 'k' || c == KEY_UP) { + current--; + break; + } else if (c == 'j' || c == KEY_DOWN) { + current++; + break; + } else if (c == '\n') { + wattrset(win, COLOR_PAIR(0)); + window_clear(wind); + items[current].callback(wind, items[current].name); + /* window_clear(wind); */ + return; + } else if (c == 27) + return; + if (is_term_resized(CLINES, CCOLS)) { + HANDLE_RESIZE; + goto redraw; + } + } + } +} + +void curses_start(void) { + initscr(); + start_color(); + use_default_colors(); + + curs_set(0); + noecho(); + nodelay(stdscr, 1); + + window_settings(stdscr); + + init_pair(0, COLOR_WHITE, -1); + init_pair(1, COLOR_RED, -1); + + init_pair(2, COLOR_WHITE, COLOR_WHITE); + init_pair(3, COLOR_BLACK, -1); +} + +void curses_stop(void) { + window_free(MAIN_w); + endwin(); +} + +void handle_winch(int sig) { + endwin(); + refresh(); + clear(); + + refresh(); + window_init(MAIN_w); + window_update_children(MAIN_w); +} + +int display_start(void) { +#ifndef _WIN32 + signal(SIGWINCH, handle_winch); +#endif + + curses_start(); + MAIN_w = window_init(window_new()); + +#ifdef _WIN32 + resize_term(0, 0); + handle_winch(10); +#endif + + return 1; +} + +int display_stop(void) { + curses_stop(); + return 1; +} diff --git a/src/file.c b/src/file.c @@ -0,0 +1,135 @@ +#include <stdlib.h> +#include <string.h> + +#include <dirent.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "display.h" + +#ifdef _WIN32 +#define SETTINGS_DIR "C:\\GoL" // without trailing '\' +#define MAKE_DIR(dir) (mkdir(dir)) +#else +#define SETTINGS_DIR "/home/magaknuto/GoL" +#define MAKE_DIR(dir) (mkdir(dir, 0777)) +#endif // _WIN32 + +typedef struct file_T *file_T; +struct file_T { + file_T next; + char *name; +}; + +file_T loaded_files; + +file_T file_new(char *name) { + file_T f; + f = malloc(sizeof(*f)); + f->next = NULL; + if (name == NULL) { + f->name = NULL; + return f; + } + + f->name = malloc((strlen(name) + 1) * sizeof(char)); + strcpy(f->name, name); + return f; +} + +void file_free(file_T self) { + if (self == NULL) + return; + file_free(self->next); + free(self->name); + free(self); +} + +file_T file_add(file_T self, char *name) { + for (; self->next != NULL; self = self->next) + ; + self->next = file_new(name); + return self->next; +} + +int DirectoryExists(const char *path) { + struct stat stats; + stat(path, &stats); + + return S_ISDIR(stats.st_mode); +} + +file_T file_fromDirectory(void) { + file_T base = file_new(NULL); + + struct dirent *de; + DIR *dr = opendir("."); + if (dr == NULL) + return NULL; + + while ((de = readdir(dr)) != NULL) + file_add(base, de->d_name); + + file_T n = base->next; + + free(base); + free(dr); + + return n; +} + +file_T file_find(file_T self, char *name) { + for (; self != NULL; self = self->next) { + if (!strcmp(self->name, name)) + return self; + } + return NULL; +} + +int file_setup(void) { + if (!DirectoryExists(SETTINGS_DIR)) { + if (MAKE_DIR(SETTINGS_DIR) != 0) { + return 0; + } + } + + if (chdir(SETTINGS_DIR) != 0) { + return 0; + } + return 1; +} + +int file_select(char *ext, char ***buffer) { + int maxsize = 4; + int size = 0; + char **tmp; + file_T current = loaded_files; + + *buffer = malloc(maxsize * sizeof(char *)); + for (; current != NULL; current = current->next) { + char *dot = strrchr(current->name, '.'); + if (dot == NULL) + continue; + if (!strcmp(dot + 1, ext)) { + *dot = '\0'; + (*buffer)[size++] = current->name; + if (size == maxsize) { + maxsize *= 2; + tmp = realloc(*buffer, maxsize * sizeof(char *)); + if (!tmp) + exit(1); + *buffer = tmp; + } + } + } + tmp = realloc(*buffer, size * sizeof(char *)); + if (!tmp) + exit(1); + *buffer = tmp; + return size; +} + +void load_files(void) { loaded_files = file_fromDirectory(); } +void free_files(void) { file_free(loaded_files); } +void load_file(window_T win, char *pass) { (void)3; } diff --git a/src/main.c b/src/main.c @@ -1,234 +1,50 @@ // This is not a final version of the code -// This is just a quick proof of concept -// Many things need to be addressed later +// While I've cleaned this file up, +// all of the game logic is still here #include <ctype.h> #include <curses.h> #include <locale.h> -#include <signal.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include <wchar.h> -#include <dirent.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> +#include "display.h" +#include "file.h" +#include "utils.h" +#include "window.h" + +extern window_T MAIN_w; WINDOW *game_w, *board_w, *status_w; int top_space = 5; -typedef struct file_T *file_T; -struct file_T { - file_T next; - char *name; -}; - -file_T file_new(char *name) { - file_T f; - f = malloc(sizeof(*f)); - f->next = NULL; - if (name == NULL) { - f->name = NULL; - return f; - } - - f->name = malloc((strlen(name) + 1) * sizeof(char)); - strcpy(f->name, name); - return f; -} - -void file_free(file_T self) { - if (self == NULL) - return; - file_free(self->next); - free(self->name); - free(self); -} - -file_T file_add(file_T self, char *name) { - for (; self->next != NULL; self = self->next) - ; - self->next = file_new(name); - return self->next; -} - -void file_list(file_T self) { - for (; self != NULL; self = self->next) - printw("%s ", self->name); -} - -int DirectoryExists(const char *path) { - struct stat stats; - stat(path, &stats); - - return S_ISDIR(stats.st_mode); -} - -file_T file_fromDirectory(void) { - file_T base = file_new(NULL); - - struct dirent *de; - DIR *dr = opendir("."); - if (dr == NULL) - return NULL; - - move(0, 0); - while ((de = readdir(dr)) != NULL) - file_add(base, de->d_name); - - file_T n = base->next; - - free(base); - free(dr); - - return n; -} - -file_T file_find(file_T self, char *name) { - for (; self != NULL; self = self->next) { - if (!strcmp(self->name, name)) - return self; - } - return NULL; -} - -struct extension_T { - char *ext; - file_T list; -}; - -void file_filter(file_T self, struct extension_T *ext, int ext_s) { - file_T *tmp = malloc(ext_s * sizeof(file_T)); - for (int i = 0; i < ext_s; i++) { - tmp[i] = ext[i].list = file_new(NULL); - } - - move(10, 0); - for (; self != NULL; self = self->next) { - char *dot = strrchr(self->name, '.'); - if (dot == NULL) - continue; - for (int i = 0; i < ext_s; i++) { - if (!strcmp(ext[i].ext, dot)) { - tmp[i] = tmp[i]->next = file_new(self->name); - break; - } - } - } - - for (int i = 0; i < ext_s; i++) { - printw("\nExtension %s: ", ext[i].ext); - file_T n = ext[i].list->next; - free(ext[i].list); - file_list(ext[i].list = n); - } - free(tmp); -} - -#ifdef _WIN32 -#define WINDOWS 1 -#define UNIX 0 -#define SETTINGS_DIR "C:\\GoL" // without trailing '\' -#define MAKE_DIR(dir) (mkdir(dir)) -#else -#define WINDOWS 0 -#define UNIX 1 -#define SETTINGS_DIR "/home/magaknuto/GoL" -#define MAKE_DIR(dir) (mkdir(dir, 0777)) -#endif // _WIN32 - -void setup(char *pass) { - if (!DirectoryExists(SETTINGS_DIR)) { - printw("%s does not exist, trying to create it....\n", SETTINGS_DIR); - if (MAKE_DIR(SETTINGS_DIR) != 0) { - printw("Unfortunately, can't create a directory: %s", SETTINGS_DIR); - getch(); - exit(1); - } - printw("Success!\n"); - } else - printw("%s already exists\n", SETTINGS_DIR); - - if (chdir(SETTINGS_DIR) != 0) { - printw("chdir(%s): not successfull!\n", SETTINGS_DIR); - getch(); - exit(1); - } else - printw("Directory has successfully been changed to %s!\n", SETTINGS_DIR); -} - -#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))) - -void cursor_offset(int oy, int ox) { - int y, x; - getyx(stdscr, y, x); - move(y + oy, x + ox); -} - -typedef int (*input_f)(int); -void input(char *buffer, int size, input_f crit) { - int ch; - int read = 0; - while ((ch = getch()) != '\n') { - switch (ch) { - case KEY_BACKSPACE: - if (read > 0) { - cursor_offset(0, -1); - delch(); - read--; - } - default: - if (read < size && crit && crit(ch)) { - printw("%c", ch); - buffer[read++] = ch; - } - } - } - printw("\n"); - buffer[read] = '\0'; -} - -void wcenter_horizontal(WINDOW *win, int y, int n) { - int mx, my; - getmaxyx(win, my, mx); - wmove(win, y, (mx - n) / 2); -} +void show(window_T wind, unsigned **univ, int w, int h, int y, int x) { + WINDOW *win = window_win(wind); + wattrset(win, COLOR_PAIR(0)); -int wcenter_vertical(WINDOW *win, int n) { - int mx, my; - getmaxyx(win, my, mx); - return (my - n) / 2; -} -#define center_vertical(n) wcenter_vertical(stdscr, n); -#define center_horizontal(y, n) wcenter_horizontal(stdscr, y, n); - -void show(WINDOW *win, unsigned **univ, int w, int h, int y, int x) { - attrset(COLOR_PAIR(0)); int ph = h, pw = w, mh, mw; - getmaxyx(win, mh, mw); + mh = window_height(wind); + mw = window_wight(wind) / 2; CLAMP(ph, 0, mh); CLAMP(pw, 0, mw); - int top = wcenter_vertical(win, ph); + int top = wcenter_vertical(wind, ph); for (int i = 0; i < ph; i++) { - wcenter_horizontal(win, top + i, pw); + wcenter_horizontal(wind, top + i, pw * 2); for (int j = 0; j < pw; j++) { #ifdef _WIN32 wattrset(win, COLOR_PAIR((univ[(i + y + h) % h][(j + x + w) % w]) ? 2 : 3)); wprintw(win, " "); #else - /* if (univ[(i + y + h) % h][(j + x + w) % w]) */ - /* wprintw(win, "%lc", 0x2B1B); */ - /* else */ - /* wprintw(win, " ", 0x2B1C); */ if (univ[(i + y + h) % h][(j + x + w) % w]) - wprintw(win, "%lc", (wchar_t)L'\u2B1B'); + wprintw(win, "%lc ", (wchar_t)L'\u23FA'); else - wprintw(win, " ", (wchar_t)L'\u2B1C'); + wprintw(win, " "); + /* wprintw(win, "%lc", (wchar_t)L'\u2B1B'); */ + /* wprintw(win, " ", (wchar_t)L'\u2B1C'); */ #endif } } @@ -262,20 +78,10 @@ void evolve(unsigned **univ, unsigned **new, int w, int h, int step) { univ[i][j] = new[i][j]; } -void game(int w, int h) { - clear(); - box(stdscr, ACS_VLINE, ACS_HLINE); - refresh(); - - box(game_w, ACS_VLINE, ACS_HLINE); - wrefresh(game_w); - - box(status_w, ACS_VLINE, ACS_HLINE); - wbkgd(status_w, '.'); - wrefresh(status_w); - - if (!w || !h) { - getmaxyx(board_w, h, w); +void game(window_T wind, int w, int h) { + if (w <= 0 || h <= 0) { + h = window_height(wind); + w = window_wight(wind); w /= 2; } @@ -292,16 +98,19 @@ void game(int w, int h) { new[i] = malloc(w * sizeof(unsigned)); } - nodelay(status_w, 1); - keypad(status_w, TRUE); int oy = 0, ox = 0; int in = 3; int gen_step = 1; - while (1) { - show(board_w, univ, w, h, oy, ox); + + window_clear(wind); +redraw:; + /* window_clear(wind); */ + int CLINES = LINES, CCOLS = COLS; + while (TRUE) { + show(wind, univ, w, h, oy, ox); evolve(univ, new, w, h, gen_step); for (int i = 0; i < in; i++) { - int c = wgetch(status_w); + int c = getch(); switch (c) { case KEY_A1: case KEY_C1: @@ -332,138 +141,112 @@ void game(int w, int h) { case KEY_END: case KEY_NPAGE: oy++; - break; + } + break; + switch (c) { + case 27: + return; } } ox = (ox + w) % w; oy = (oy + h) % h; + flushinp(); - usleep(150000); + + usleep(200000); + if (is_term_resized(CLINES, CCOLS)) { + HANDLE_RESIZE; + goto redraw; + } } } -void start(char *pass) { - char buffer[100]; - int row, column; - printw("Unesi broj redova: "); - input(buffer, 4, isdigit); - row = atoi(buffer); - printw("Unesi broj kolona: "); - input(buffer, 4, isdigit); - column = atoi(buffer); - game(row, column); - refresh(); -} +struct imenu_T imenu_items[] = { + {"Unesi broj redova", 4, isdigit, NULL}, + {"Unesi broj kolona", 4, isdigit, NULL}, +}; +int imenu_items_s = sizeof(imenu_items) / sizeof(struct imenu_T); -void load(char *pass) { - printw("Load\n"); - getch(); -} +void settings(window_T wind, char *pass) { + if (display_imenu(wind, imenu_items, imenu_items_s)) { + int row = atoi(imenu_items[0].buffer); + int column = atoi(imenu_items[1].buffer); + game(wind, row, column); + } -void exitp(char *pass) { - endwin(); - exit(0); + for (int i = 0; i < imenu_items_s; i++) + free(imenu_items[i].buffer); } -struct extension_T ext[] = { - { ".c", NULL}, - { ".h", NULL}, - {".part", NULL}, - { ".all", NULL} -}; -int ext_s = sizeof(ext) / sizeof(struct extension_T); - -void list(char *pass) { - file_T files = file_fromDirectory(); - file_filter(files, ext, ext_s); - for (int i = 0; i < ext_s; i++) - file_free(ext[i].list); - file_free(files); -} +void load(window_T wind, char *pass) { + char **buffer; + int n; -struct menu_T { - void (*callback)(char *); - char *name; -}; + load_files(); + n = file_select("all", &buffer); -struct menu_T menu_items[] = { - {start, "Start"}, - {setup, "Setup"}, - { list, "List"}, - { load, "Load"}, - {exitp, "Exit"} -}; -int menu_items_s = sizeof(menu_items) / sizeof(struct menu_T); - -void display_menu(struct menu_T *items, int size) { - int current = 0; + struct menu_T *file_items = malloc(n * sizeof(struct menu_T)); + for (int i = 0; i < n; i++) { + file_items[i].name = buffer[i]; + file_items[i].callback = load_file; + } + free(buffer); - int *lens = malloc(size * sizeof(int)); - for (int i = 0; i < size; i++) - lens[i] = strlen(items[i].name); + display_menu(wind, file_items, n); + game(wind, 0, 0); - int y_offset = center_vertical(size * 2); - while (TRUE) { - CLAMP(current, 0, size - 1); - for (int i = 0; i < size; i++) { - attrset(COLOR_PAIR(i == current ? 1 : 0)); - center_horizontal(y_offset + i * 2, lens[i]); - printw("%s", items[i].name); - } - refresh(); - switch (getch()) { - case 'k': - case KEY_UP: - current--; - break; - case KEY_DOWN: - case 'j': - current++; - break; - case '\n': - move(16, 0); - attrset(COLOR_PAIR(0)); - items[current].callback(NULL); - } - } - free(lens); + free(file_items); + free_files(); } -void ncurses_window_setup(void) { - game_w = newwin(LINES - 2 - top_space, COLS - 4, 1 + top_space, 2); - board_w = newwin(LINES - 4 - top_space, COLS - 6, 2 + top_space, 3); - status_w = newwin(top_space, COLS - 4, 1, 2); +void exitp(window_T wind, char *pass) { + display_stop(); + exit(0); } -void ncurses_window_end(void) { - delwin(game_w); - delwin(board_w); - delwin(status_w); -} +struct menu_T mode_items[] = { + {settings, "Normalan"}, + {settings, "Koegzistencija"}, + {settings, "Predator i pljen"}, + {settings, "Virus"}, + {settings, "Nepoznanost"}, +}; +int mode_items_s = sizeof(mode_items) / sizeof(struct menu_T); -void ncurses_start(void) { - initscr(); - start_color(); - use_default_colors(); - curs_set(0); - noecho(); - keypad(stdscr, TRUE); - init_pair(0, COLOR_WHITE, -1); - init_pair(1, COLOR_RED, -1); - - init_pair(2, COLOR_WHITE, COLOR_WHITE); - init_pair(3, COLOR_BLACK, -1); +void mode_select(window_T wind, char *pass) { + display_menu(wind, mode_items, mode_items_s); } +struct menu_T menu_items[] = { + {mode_select, "Start"}, + { load, "Load"}, + { exitp, "Exit"} +}; +int menu_items_s = sizeof(menu_items) / sizeof(struct menu_T); + +int state = 0; + int main(void) { setlocale(LC_ALL, ""); - ncurses_start(); - ncurses_window_setup(); + if (!display_start()) { + printf("Couldn't start the display!\n"); + } + if (!file_setup()) { + printf("File setup error\n"); + } + + T t1, t2; + t1 = window_split(MAIN_w, 1, 5, 0); + t2 = window_sibiling(t1); - display_menu(menu_items, menu_items_s); - getch(); + while (TRUE) { + display_menu(t2, menu_items, menu_items_s); + window_clear(t2); + } - endwin(); + if (!display_stop()) { + printf("Couldn't stop the display!\n"); + } return 0; } diff --git a/src/utils.c b/src/utils.c @@ -0,0 +1,4 @@ +#include <curses.h> + +#include "utils.h" + diff --git a/src/window.c b/src/window.c @@ -0,0 +1,177 @@ +#include <curses.h> +#include <stdlib.h> +#include <string.h> + +#include "window.h" + +#define H(c) c->param[0] +#define W(c) c->param[1] +#define Y(c) c->param[2] +#define X(c) c->param[3] + +#define window_T T +typedef struct T *T; + +struct T { + WINDOW *win; + T c1; + T c2; + T sibiling; + + int param[4]; + int mod[3]; +}; + +void WINDOW_init(WINDOW *win) { + window_settings(win); + box(win, ACS_VLINE, ACS_HLINE); + wrefresh(win); +} + +void WINDOW_new(T self) { + self->win = newwin(H(self), W(self), Y(self), X(self)); + WINDOW_init(self->win); +} + +T window_new(void) { + T self = malloc(sizeof(*self)); + self->win = NULL; + self->c1 = NULL; + self->c2 = NULL; + self->sibiling = NULL; + H(self) = 0; + W(self) = 0; + Y(self) = 0; + X(self) = 0; + return self; +} + +T window_sibiling(T self) { return self->sibiling; } +int window_height(T self) { return H(self) - 2; } +int window_wight(T self) { return W(self) - 2; } +WINDOW *window_win(T self) { return self->win; } + +T window_init(T self) { + self->win = stdscr; + box(self->win, ACS_VLINE, ACS_HLINE); + wrefresh(self->win); + H(self) = LINES; + W(self) = COLS; + return self; +} + +void window_free(T self) { + if (self == NULL) + return; + window_free(self->c1); + window_free(self->c2); + delwin(self->win); + free(self); +} + +void window_calc(T self) { + T c1, c2, f, nf; + c1 = self->c1; + c2 = self->c2; + int fixed = 0, *fv; + int hor = self->mod[0], a = self->mod[1], b = self->mod[2]; + + if (a == 0 && b == 0) + return; + + if (a == 0) { + f = c2; + nf = c1; + fv = &b; + fixed = 1; + } else if (b == 0) { + f = c1; + nf = c2; + fv = &a; + fixed = 1; + } + + if (hor) { + W(c1) = W(c2) = W(self) - 2; + + if (!fixed) { + H(c1) = ((H(self) - 2) / (a + b)) * a; + } else { + H(f) = *fv; + } + H(nf) = (H(self) - 2) - H(f); + + X(c1) = X(c2) = X(self) + 1; + Y(c1) = Y(self) + 1; + Y(c2) = Y(self) + H(c1) + 1; + } else { + H(c1) = H(c2) = H(self) - 2; + + if (!fixed) { + W(c1) = ((W(self) - 2) / (a + b)) * a; + } else { + W(f) = *fv; + } + W(nf) = (W(self) - 2) - W(f); + + Y(c1) = Y(c2) = Y(self) + 1; + X(c1) = X(self) + 1; + X(c2) = X(self) + W(c1) + 1; + } +} + +T window_split(T self, int hor, int a, int b) { + self->c1 = window_new(); + self->c2 = window_new(); + + self->c1 = window_new(); + self->c2 = window_new(); + + self->c1->sibiling = self->c2; + self->c2->sibiling = self->c1; + + self->mod[0] = hor; + self->mod[1] = a; + self->mod[2] = b; + + window_calc(self); + + WINDOW_new(self->c1); + WINDOW_new(self->c2); + + return self->c1; +} + +void window_update_children(T self) { + if (self == NULL || self->c1 == NULL || self->c2 == NULL) + return; + + delwin(self->c1->win); + delwin(self->c2->win); + window_calc(self); + WINDOW_new(self->c1); + WINDOW_new(self->c2); + window_update_children(self->c1); + window_update_children(self->c2); +} + +void window_clear(T self) { + wclear(self->win); + WINDOW_init(self->win); +} + +void window_settings(WINDOW *win) { keypad(win, TRUE); } + +void wcenter_horizontal(T window, int y, int n) { + wmove(window_win(window), y, (W(window) - n - 2) / 2 + 1); +} + +int wcenter_vertical(T window, int n) { return (H(window) - n - 2) / 2 + 1; } + +void cursor_offset(WINDOW *win, int oy, int ox) { + int y, x; + getyx(win, y, x); + wmove(win, y + oy, x + ox); +} + +#undef T