gol

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

window.c (9532B)


      1 /**
      2  * @file window.c
      3  * @author Dimitrije Dobrota
      4  * @date 9 June 2022
      5  * @brief This file contains the implementation of the window interface
      6  *
      7  * This file handles the creation and deletion process of nodes in the binary
      8  * tree of windows used for recalculating the ncurses WINDOW position in
      9  * dimension when the terminal has been resized. It's possible to "split" the
     10  * current window in two using a given ration or fixed size, as well as create
     11  * a centered sub-window. It makes some of the tree data accessible via function
     12  * so that other functions can know about the sizes and adapt to them.
     13  */
     14 
     15 #include <curses.h>
     16 #include <stdlib.h>
     17 #include <string.h>
     18 
     19 #include "utils.h"
     20 #include "window.h"
     21 
     22 #define H(c) c->param[0]
     23 #define W(c) c->param[1]
     24 #define Y(c) c->param[2]
     25 #define X(c) c->param[3]
     26 
     27 #define window_T T
     28 typedef struct T *T;
     29 
     30 /**
     31  * @brief A node in a binary tree of windows
     32  *
     33  * window_T contains all of the information about the size and dimensions of
     34  * ncurses WINDOW, as well as all the information on how to recalculate
     35  * children when a terminal resize occurs.
     36  */
     37 struct window_T {
     38   WINDOW *win;     ///< ncurses WINDOW* that represents current window_T
     39   T       c1;      ///< left child, NULL if none
     40   T       c2;      ///< right child, NULL if none
     41   T       sibling; ///< sibling, NULL if none
     42 
     43   int param[4]; ///<  windows size and dimension, refer to macros
     44   int mod[3];   ///< Information an recreating children
     45 
     46   char *title; ///< Window title to display, NULL if none
     47 };
     48 
     49 /**
     50  * @brief Apply settings, draw a border, print title for a give window
     51  *
     52  * @param title: title to be printed unless NULL
     53  */
     54 void WINDOW_init(WINDOW *win, char *title) {
     55   window_settings(win);
     56   box(win, ACS_VLINE, ACS_HLINE);
     57   if (title)
     58     mvwprintw(win, 0, 1, " %s ", title);
     59 }
     60 
     61 /**
     62  * @brief create new window_T
     63  *
     64  * Allocate the memory for a new window_T and set its fields to the default
     65  * value. Should be the only way new window_T objects are created
     66  */
     67 T window_new(void) {
     68   T self;
     69 
     70   MEM_CHECK(self = malloc(sizeof(*self)));
     71   self->win = NULL;
     72   self->c1 = NULL;
     73   self->c2 = NULL;
     74   self->sibling = NULL;
     75   self->title = NULL;
     76   H(self) = 0;
     77   W(self) = 0;
     78   Y(self) = 0;
     79   X(self) = 0;
     80   return self;
     81 }
     82 
     83 /// Getter: return the sibling of the current window
     84 T window_sibiling(T self) { return self->sibling; }
     85 
     86 /// Getter: return the height of the current window
     87 int window_height(T self) { return H(self) - 2; }
     88 
     89 /// Getter: return the  of a current window
     90 int window_width(T self) { return W(self) - 2; }
     91 
     92 /// Getter: return the x coordinate of the current window
     93 int window_x(T self) { return X(self); }
     94 
     95 /// Getter: return the y coordinate of the current window
     96 int window_y(T self) { return Y(self); }
     97 
     98 /// Getter: return the ncurses WINDOW* of the current window
     99 WINDOW *window_win(T self) { return self->win; }
    100 
    101 /// Setter: set the title of the current window
    102 void window_set_title(T self, char *title) { self->title = title; }
    103 
    104 /**
    105  * @brief create new ncurses WINDOW to the specification with default settings
    106  *
    107  * New ncurses WINDOW is created with newwin and it should be deleted with
    108  * delwin. Setting are applied with the call to WINDOW_INIT
    109  */
    110 WINDOW *window_win_new(T self) {
    111   WINDOW *win = newwin(H(self), W(self), Y(self), X(self));
    112   WINDOW_init(win, self->title);
    113   wrefresh(win);
    114   return win;
    115 }
    116 
    117 /**
    118  * @brief Initialize the window to be used as main
    119  *
    120  * This function will use the ncurses interface directly to set the screen size
    121  * to match the whole terminal. This window object should always be used as a
    122  * root of the tree and under no condition should it be used as a child of
    123  * other window.
    124  *
    125  * There can be multiple instances of windows initialized with this function
    126  */
    127 T window_init(T self) {
    128   self->win = stdscr;
    129   WINDOW_init(stdscr, self->title);
    130   wrefresh(self->win);
    131   H(self) = LINES;
    132   W(self) = COLS;
    133   return self;
    134 }
    135 
    136 /**
    137  * @brief Free the window and it's children recursively
    138  *
    139  * Free the object itself and ncurses WINDOW* with delwin
    140  */
    141 void window_free(T self) {
    142   if (self == NULL)
    143     return;
    144   window_free(self->c1);
    145   window_free(self->c2);
    146   delwin(self->win);
    147   free(self);
    148 }
    149 
    150 /**
    151  * @brief Delete all children and sub-children of a window
    152  */
    153 void window_unsplit(T self) {
    154   window_free(self->c1);
    155   window_free(self->c2);
    156   self->c1 = NULL;
    157   self->c2 = NULL;
    158 }
    159 
    160 /**
    161  * @brief Given the window calculate the position and dimensions of it's
    162  * children
    163  *
    164  * This function takes care of calculation for two windows in a given ration or
    165  * fixed size, and single sub-window.
    166  * If a given windows has 2 children then they are split with the following
    167  * rule:
    168  * - If mod[0] is 0, two created windows will be side by side
    169  * - If mod[0] is not 0, two created windows will be one on top of the other
    170  * - If mod[1] and mod[2] are 0, nothing happens
    171  * - If mod[1] or mod[2] is 0, the window with a non zero value will be of a
    172  *   fixed size, while the other will fill the rest of the screen
    173  * If a given windows has 1 child then:
    174  * - Child dimensions will be mod[1] x mod[2] or made to fit in it's parent
    175  */
    176 void window_calc_children(T self) {
    177   T c1, c2, f, nf;
    178   c1 = self->c1;
    179   c2 = self->c2;
    180   int fixed = 0, *fv;
    181   int hor = self->mod[0], a = self->mod[1], b = self->mod[2];
    182 
    183   if (c2 != NULL) {
    184     if (a == 0 && b == 0)
    185       return;
    186 
    187     if (a == 0) {
    188       f = c2;
    189       nf = c1;
    190       fv = &b;
    191       fixed = 1;
    192     } else if (b == 0) {
    193       f = c1;
    194       nf = c2;
    195       fv = &a;
    196       fixed = 1;
    197     }
    198 
    199     if (hor) {
    200       W(c1) = W(c2) = W(self) - 2;
    201 
    202       if (!fixed) {
    203         H(c1) = ((H(self) - 2) / (a + b)) * a;
    204       } else {
    205         H(f) = *fv;
    206       }
    207       H(nf) = (H(self) - 2) - H(f);
    208 
    209       X(c1) = X(c2) = X(self) + 1;
    210       Y(c1) = Y(self) + 1;
    211       Y(c2) = Y(self) + H(c1) + 1;
    212     } else {
    213       H(c1) = H(c2) = H(self) - 2;
    214 
    215       if (!fixed) {
    216         W(c1) = ((W(self) - 2) / (a + b)) * a;
    217       } else {
    218         W(f) = *fv;
    219       }
    220       W(nf) = (W(self) - 2) - W(f);
    221 
    222       Y(c1) = Y(c2) = Y(self) + 1;
    223       X(c1) = X(self) + 1;
    224       X(c2) = X(self) + W(c1) + 1;
    225     }
    226   } else {
    227     H(c1) = ACLAMP(a + 2, 0, H(self));
    228     W(c1) = ACLAMP(b + 2, 0, W(self));
    229     Y(c1) = Y(self) + (H(self) - H(c1)) / 2;
    230     X(c1) = X(self) + (W(self) - W(c1)) / 2;
    231   }
    232 }
    233 
    234 /**
    235  * @brief Split the window in a given ration into two windows with a name
    236  *
    237  * @param hor: should the split line be horizontal or vertical
    238  * @param a: ration of the first window
    239  * @param b: ration of the second window
    240  * @param name1: name of the first window
    241  * @param name2: name of the second window
    242  *
    243  * @see window_calc_children to see how the ratios are used
    244  */
    245 T window_split(T self, int hor, int a, int b, char *name1, char *name2) {
    246   if (self->c1)
    247     window_free(self->c1);
    248   if (self->c2)
    249     window_free(self->c2);
    250 
    251   self->c1 = window_new();
    252   self->c2 = window_new();
    253 
    254   self->c1->sibling = self->c2;
    255   self->c2->sibling = self->c1;
    256 
    257   self->mod[0] = hor;
    258   self->mod[1] = a;
    259   self->mod[2] = b;
    260 
    261   window_calc_children(self);
    262 
    263   self->c1->title = name1;
    264   self->c2->title = name2;
    265 
    266   self->c1->win = window_win_new(self->c1);
    267   self->c2->win = window_win_new(self->c2);
    268 
    269   return self->c1;
    270 }
    271 
    272 /**
    273  * @brief Create a sub-window that will be of a given size or made to fit it's
    274  * parent
    275  *
    276  * @param h: desired height of a new window_T
    277  * @param w: desired  of a new window_T
    278  * @param name: name of the new window_T
    279  */
    280 T window_center(T self, int h, int w, char *name) {
    281   self->c1 = window_new();
    282   self->c2 = NULL;
    283 
    284   self->mod[0] = -1;
    285   self->mod[1] = h;
    286   self->mod[2] = w;
    287 
    288   window_calc_children(self);
    289 
    290   self->c1->title = name;
    291   self->c1->win = window_win_new(self->c1);
    292 
    293   return self->c1;
    294 }
    295 
    296 /**
    297  * @brief Recursively recalculate the size of each child and make new ncurses
    298  * WINDOW
    299  */
    300 void window_update_children(T self) {
    301   if (self == NULL || self->c1 == NULL)
    302     return;
    303 
    304   window_calc_children(self);
    305 
    306   delwin(self->c1->win);
    307   self->c1->win = window_win_new(self->c1);
    308   window_update_children(self->c1);
    309 
    310   if (self->c2 != NULL) {
    311     delwin(self->c2->win);
    312     self->c2->win = window_win_new(self->c2);
    313     window_update_children(self->c2);
    314   }
    315 }
    316 
    317 /**
    318  * @brief Clear the current window, reset the setting, keep the border call
    319  * ncurses refresh()
    320  */
    321 void window_clear(T self) {
    322   werase(self->win);
    323   WINDOW_init(self->win, self->title);
    324   wrefresh(self->win);
    325 }
    326 
    327 /**
    328  * @brief Same as window_clear but without calling ncurses refresh()
    329  *
    330  * @see window_clear
    331  */
    332 void window_clear_noRefresh(T self) {
    333   werase(self->win);
    334   WINDOW_init(self->win, self->title);
    335 }
    336 
    337 /**
    338  * @brief Default setting for each window
    339  */
    340 void window_settings(WINDOW *win) {
    341   keypad(win, TRUE);
    342   wattrset(win, COLOR_PAIR(0));
    343 }
    344 
    345 /**
    346  * @brief Check if coordinates are in the window
    347  */
    348 int window_clicked(T self, int y, int x) {
    349   if (x <= X(self) || y <= Y(self))
    350     return 0;
    351 
    352   if (x >= X(self) + W(self) || y >= Y(self) + H(self))
    353     return 0;
    354 
    355   return 1;
    356 }
    357 
    358 /**
    359  * @brief Move the cursor to the given why position and x position so that the
    360  * text of len n will be centered
    361  *
    362  * @param y: y position
    363  * @n length of the text to be centered
    364  */
    365 void wcenter_horizontal(T window, int y, int n) {
    366   wmove(window->win, y, (W(window) - n - 2) / 2 + 1);
    367 }
    368 
    369 /**
    370  * @brief Return the y position of the cursor so that n lines will be centered
    371  *
    372  * @param n: number of lines to be centered
    373  */
    374 int wcenter_vertical(T window, int n) { return (H(window) - n - 2) / 2 + 1; }
    375 
    376 #undef T