#include #include #include #include // for setlocale() #include // for strncpy() and strlen() #include // for isalnum() #include // for malloc() and free() #include #include "fun_menu.h" #include "utils.h" #include "richwin.h" #include "stack.h" #include "cycle.h" // macros #define ARRAY_LENGTH(arr) (sizeof (arr) / sizeof ((arr)[0])) #define MEMBER_SIZE(arr) (sizeof ((arr)[0])) // default strings/characters #define DEFAULT_WORD "hello" #define DEFAULT_PROMPT "Enter word to draw" #define INDICATOR_CHAR_LEFT '>' #define INDICATOR_CHAR_RIGHT '<' #define INPUT_PLACEHOLDER '_' #define TEXT_CONTINUATION '-' #define INPUT_CONTINUATION '+' // default numbers #define WORD_MAXLEN 20 #define INDICATORS 2 #define BORDERS 2 #define PADDINGS 2 #define INDICATOR INDICATORS/2 #define BORDER BORDERS/2 #define PADDING PADDINGS/2 enum WindowType { BOARD_WIN, MENU_WIN, PROMPT_WIN }; enum AppActionType { APP_DRAW, APP_CLEAN, APP_CHANGE_COLOR, APP_CHANGE_WORD, APP_END}; /* structures */ // MainWin: pointer to one of the following windows and it's type. used for // converting to/from void * struct MainWin { void *sub; enum WindowType type; struct RichWin *richwin; struct RichWin *(*new_richwin)(void *sub); void *(*draw_window)(void *sub); }; // BoardWin: a resizable drawing board with stats of the word to draw, // background color, whether it's enables/disabled and whether drawing on it is // done struct BoardWin { struct MainWin base; char *word; Cycle *colors; bool on; bool done; }; // MenuWin: a resizable menu with a list of options, and the currently // choosen option from that list struct MenuWin { struct MainWin base; Cycle *options; }; // PromptWin: a resizable user prompt with prompt text, prompt input buffer, // and it's size struct PromptWin { struct MainWin base; const char *prompt; char *input; const int input_maxlen; const int content_minlen; }; // MenuOption: text for a menu option used in a MenuWin and accociated function // to call on choosing that option struct MenuOption { struct BoardWin *(*func)(struct BoardWin *boardwin); enum AppActionType type; const char *text; }; /* function declarations */ // misc functions static int prompt_user(const char *prompt, char *buf, size_t len); static int mvwaddcstr(WINDOW *win, int y, int x, const char *str, int len, const char break_char); // cleanup functions static void cleanup_main(Stack *win_stack, struct BoardWin *boardwin, struct MenuWin *menuwin); static void cleanup_prompt_user(struct PromptWin *promptwin, bool on_stack); // error handling functions static void write_error(enum ErrorCode code, int line, const char *func_name); static int print_error(void); // window methds: // MainWin methods static void *mainwin_resize(void *window); static void mainwin_cleanup(struct MainWin *mainwin); // BoardWin methods static struct RichWin *boardwin_new_richwin(void *window); static void *boardwin_draw(void *window); static void boardwin_cleanup(struct BoardWin *boardwin); // MenuWin methods static struct RichWin *menuwin_new_richwin(void *window); static void *menuwin_draw(void *window); static void menuwin_cleanup(struct MenuWin *menuwin); // PromptWin methods static struct RichWin *promptwin_new_richwin(void *window); static void *promptwin_draw(void *window); static void promptwin_cleanup(struct PromptWin *promptwin); // application actions static struct BoardWin *app_draw(struct BoardWin *boardwin); static struct BoardWin *app_clean(struct BoardWin *boardwin); static struct BoardWin *app_change_word(struct BoardWin *boardwin); static struct BoardWin *app_change_color(struct BoardWin *boardwin); static struct BoardWin *app_end(struct BoardWin *boardwin); /* globals */ // holds curses errors static int err; // remembers whether the terminal supports color static bool colorize; // keeps track of every window for responding to a terminal resize static Stack *window_stack; /* constants */ // holds error info static struct ErrorInfo { enum ErrorCode code; int line; const char *func_name; } error_info = { .code = ERROR_OK, .line = 0, .func_name = NULL, }; // text representation of error codes static const char *error_texts[ERROR_MAX] = { [ERROR_LOCALE_SETTING] = "Failed to set locale!", [ERROR_COLOR_STARTING] = "Failed to start colors!", [ERROR_CURSES_SETTING] = "Failed to set curses settings!", [ERROR_MEMORY_ALLOCATION] = "Failed to allocate memory!", [ERROR_CHARACTER_INPUT] = "Failed to recieve character from window!", [ERROR_CHARACTER_OUTPUT] = "Failed to print character to window!", [ERROR_WINDOW_CREATION] = "Failed to create a window!", [ERROR_WINDOW_DRAWING] = "Failed to write to a window!", [ERROR_WINDOW_RENDERING] = "Failed to render a window!", [ERROR_COLOR_PAIR_DEFINITION] = "Failed to define a color pair!", [ERROR_OPTION_SETTING] = "Failed to set an option for a window!", [ERROR_UNKNOWN] = "Unknown error!", }; // holds BoardWin's colors to cycle through static const short colors[] = { COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, }; // holds an iterable vtable static const struct MenuOption menu_options[] = { { .func = app_draw, .type = APP_DRAW, .text = "draw", }, { .func = app_clean, .type = APP_CLEAN, .text = "clean", }, { .func = app_change_color, .type = APP_CHANGE_COLOR, .text = "change color", }, { .func = app_change_word, .type = APP_CHANGE_WORD, .text = "change word", }, { .func = app_end, .type = APP_END, .text = "exit", }, }; /* a "little" arrow-driven/hjkl menu practice */ int main(void) { struct BoardWin *boardwin = NULL; struct MenuWin *menuwin = NULL; // set locale from environment if (setlocale(LC_ALL, "") == NULL) { write_error(ERROR_LOCALE_SETTING, __LINE__, __func__); goto error; } // start curses (void) initscr(); // start color colorize = has_colors(); if (colorize) { if (start_color() == ERR) { write_error(ERROR_COLOR_STARTING, __LINE__, __func__); goto error; } } // set options if (noecho() == ERR || raw() == ERR || nl() == ERR || leaveok(stdscr, true) == ERR || curs_set(0) == ERR) { write_error(ERROR_CURSES_SETTING, __LINE__, __func__); goto error; } // set color pairs if (colorize) { if (init_pair(1, COLOR_BLACK, COLOR_WHITE) == ERR ||init_pair(2, COLOR_BLACK, colors[0]) == ERR) { write_error(ERROR_COLOR_PAIR_DEFINITION, __LINE__, __func__); goto error; } } // create window stack if ((window_stack = stack_new()) == NULL) { write_error(ERROR_MEMORY_ALLOCATION, __LINE__, __func__); goto error; } // create board window struct BoardWin boardwin_struct = { .word = malloc(WORD_MAXLEN), .colors = cycle_new(colors, ARRAY_LENGTH(colors), MEMBER_SIZE(colors)), .on = false, .done = false, }; boardwin = &boardwin_struct; // verify allocations if (boardwin->word == NULL || boardwin->colors == NULL) { write_error(ERROR_MEMORY_ALLOCATION, __LINE__, __func__); goto error; } // create base struct MainWin boardwin_base = { .sub = boardwin, .type = BOARD_WIN, .richwin = boardwin_new_richwin(NULL), .new_richwin = boardwin_new_richwin, .draw_window = boardwin_draw, }; boardwin->base = boardwin_base; // error checking if (boardwin->base.richwin == NULL) goto error; // push boardwin to window stack if (stack_push(window_stack, boardwin) == NULL) { write_error(ERROR_MEMORY_ALLOCATION, __LINE__, __func__); goto error; } // write default word into it strncpy(boardwin->word, DEFAULT_WORD, WORD_MAXLEN); boardwin->word[WORD_MAXLEN-1] = '\0'; // create menu window struct MenuWin menuwin_struct = { .options = cycle_new(menu_options, ARRAY_LENGTH(menu_options), MEMBER_SIZE(menu_options)), }; menuwin = &menuwin_struct; // verify allocations if (menuwin->options == NULL) { write_error(ERROR_MEMORY_ALLOCATION, __LINE__, __func__); goto error; } // create base struct MainWin menuwin_base = { .sub = menuwin, .type = MENU_WIN, .richwin = menuwin_new_richwin(NULL), .new_richwin = menuwin_new_richwin, .draw_window = menuwin_draw, }; menuwin->base = menuwin_base; // error checking if (menuwin->base.richwin == NULL) goto error; // push it to window stack if (stack_push(window_stack, menuwin) == NULL) { write_error(ERROR_MEMORY_ALLOCATION, __LINE__, __func__); goto error; } // main loop if (menuwin_draw(menuwin) == NULL) goto error; if (doupdate() == ERR) { write_error(ERROR_WINDOW_RENDERING, __LINE__, __func__); goto error; } int ch; while (!boardwin->done) { ch = wgetch(menuwin->base.richwin->win); if (ch == ERR) { write_error(ERROR_CHARACTER_INPUT, __LINE__, __func__); goto error; } switch(ch) { case KEY_RESIZE: // resize all windows if (stack_iter_reverse(window_stack, mainwin_resize) == NULL) { goto error; } // refresh if (doupdate() == ERR) { write_error(ERROR_WINDOW_RENDERING, __LINE__, __func__); goto error; } break; case 'j': case KEY_DOWN: // go down cycle_next(menuwin->options); break; case 'k': case KEY_UP: // go up cycle_prev(menuwin->options); break; case '\n': { // execute an option const struct MenuOption *menu_option = cycle_current( menuwin->options); if (menu_option->func(boardwin) == NULL) goto error; break; } } // render if (menuwin_draw(menuwin) == NULL) goto error; if (doupdate() == ERR) { write_error(ERROR_WINDOW_RENDERING, __LINE__, __func__); goto error; } } // cleanup and exit cleanup_main(window_stack, boardwin, menuwin); return 0; error: cleanup_main(window_stack, boardwin, menuwin); print_error(); return 1; } /*== misc functions =========================================================*/ /* * prompt_user: open a prompt window with text 'prompt' and store the result * in buffer 'buffer' of size 'buffer_size'. number of characters read from * user is returned or -1 if prompt failed */ static int prompt_user(const char *prompt, char *buffer, size_t buffer_size) { assert(prompt != NULL); assert(buffer != NULL); assert(buffer_size > 0); struct PromptWin *promptwin = NULL; bool on_stack = false; const int input_maxlen = (int)buffer_size - 1, content_minlen = (int) max_size_t(strlen(prompt), buffer_size); // allocate buffer for user input char *input = malloc(buffer_size); if (input == NULL) { write_error(ERROR_MEMORY_ALLOCATION, __LINE__, __func__); goto error; } // initialize buffer to placholders { int i; for (i = 0; i < input_maxlen; i++) { input[i] = INPUT_PLACEHOLDER; } input[i] = '\0'; } // create prompt window struct PromptWin promptwin_struct = { .prompt = prompt, .input = input, .input_maxlen = input_maxlen, .content_minlen = content_minlen, }; promptwin = &promptwin_struct; // create base struct MainWin promptwin_base = { .sub = promptwin, .type = PROMPT_WIN, .richwin = promptwin_new_richwin(promptwin), .new_richwin = promptwin_new_richwin, .draw_window = promptwin_draw, }; promptwin->base = promptwin_base; // verify data allocation if (promptwin->base.richwin == NULL) goto error; if (stack_push(window_stack, promptwin) == NULL) { cleanup_prompt_user(promptwin, on_stack); write_error(ERROR_MEMORY_ALLOCATION, __LINE__, __func__); return -1; } on_stack = true; // prompt loop // draw promptwin if (promptwin_draw(promptwin) == NULL) goto error; // refresh if (doupdate() == ERR) { write_error(ERROR_WINDOW_RENDERING, __LINE__, __func__); goto error; } // keep reading into buffer until a newline is read int ch, i = 0; while ((ch = wgetch(promptwin->base.richwin->win)) != '\n') { if (ch == ERR) { // failed to read character write_error(ERROR_CHARACTER_INPUT, __LINE__, __func__); goto error; } else if (ch == KEY_RESIZE) { // resize all windows from first to last if (stack_iter_reverse(window_stack, mainwin_resize) == NULL) { goto error; } // refresh if (doupdate() == ERR) { write_error(ERROR_WINDOW_RENDERING, __LINE__, __func__); goto error; } } else if ((ch == '\b' || ch == KEY_BACKSPACE) && i > 0) { // remove current character and go back by one input[--i] = INPUT_PLACEHOLDER; } else if (i < input_maxlen && isalnum(ch)) { // read character into input buffer input[i++] = (char) ch; } // draw promptwin if (promptwin_draw(promptwin) == NULL) goto error; if (doupdate() == ERR) { write_error(ERROR_WINDOW_RENDERING, __LINE__, __func__); goto error; } } if (i != 0) { // copy visual input to the user's buffer input[i] = '\0'; strncpy(buffer, input, buffer_size); buffer[buffer_size-1] = '\0'; } // remove promptwin from program wclear(promptwin->base.richwin->win); wrefresh(promptwin->base.richwin->win); // cleanup cleanup_prompt_user(promptwin, on_stack); return i; error: cleanup_prompt_user(promptwin, on_stack); return -1; } /* mvwaddcstr: like mvwaddstr but also centers 'str' on area of length 'n' */ static int mvwaddcstr(WINDOW *win, int y, int x, const char *str, int n, char break_char) { assert(win != NULL); assert(y >= 0); assert(x >= 0); assert(str != NULL); const int text_len = (int) strlen(str); if (text_len <= 0) { // there's no text return x; } if (n <= 0) { // cant write on that area return x; } if (text_len <= n) { // text fits in area n -= x; x = x + (n-text_len) / 2; // print text centered err = mvwaddnstr(win, y, x, str, n); if (err == ERR) return -1; } else { // text is longer than area: // print text truncated to length of area err = mvwaddnstr(win, y, x, str, n-1); if (err == ERR) return -1; // replace last character of text with specified text break character err = waddch(win, (const chtype) break_char); if (err == ERR) return -1; } return x; } /* * highlight_str: change background color or write indicators on edges of area * on screen starting at 'y' and 'x' of length 'n' */ static int highlight_str(WINDOW *win, int y, int x, int n, int pair) { assert(win != NULL); assert(y >= 0); assert(x >= 0); assert(n >= 1); assert(pair >= 0); if (colorize) { // colorize area of length 'n' err = mvwchgat(win, y, x, n, A_NORMAL, 1, NULL); if (err == ERR) { write_error(ERROR_WINDOW_DRAWING, __LINE__, __func__); return -1; } } else { // add indicators on edges of area of length 'n' err = mvwaddch(win, y, x, INDICATOR_CHAR_LEFT); if (err == ERR) { write_error(ERROR_CHARACTER_OUTPUT, __LINE__, __func__); return -1; } err = mvwaddch(win, y, x+n-1, INDICATOR_CHAR_RIGHT); if (err == ERR) { write_error(ERROR_CHARACTER_OUTPUT, __LINE__, __func__); return -1; } } return 0; } /*== cleanup functions ======================================================*/ /* cleanup_main: free resources allocated by main() */ static void cleanup_main(Stack *win_stack, struct BoardWin *boardwin, struct MenuWin *menuwin) { if (win_stack != NULL) stack_delete(window_stack); if (boardwin != NULL) boardwin_cleanup(boardwin); if (menuwin != NULL) menuwin_cleanup(menuwin); endwin(); } /* cleanup_prompt_user: cleanup resources allocated by prompt_user() */ static void cleanup_prompt_user(struct PromptWin *promptwin, bool on_stack) { assert(window_stack != NULL); if (promptwin != NULL) promptwin_cleanup(promptwin); if (on_stack == true) stack_pop(window_stack); } /*== error handling functions ===============================================*/ /* error: write error information to global struct 'error_info' */ static void write_error(enum ErrorCode code, int line, const char *func_name) { error_info.code = code; error_info.line = line; error_info.func_name = func_name; } /* print_error: print error stored in global struct 'error_info' */ static int print_error(void) { assert(error_info.code >= 1 && error_info.code < ERROR_MAX); return fprintf((stderr), "%s:%d:%s(): %s\n", __FILE__, error_info.line, error_info.func_name, (error_texts[error_info.code] == NULL) ? error_texts[ERROR_UNKNOWN] : error_texts[error_info.code]); } /* dump_core: crashes the program to generate debugging information */ void dump_core(void) { // suppress -Wunused-function (void) dump_core; int *crash = NULL; *crash = 7; } /*== window methods =========================================================*/ /*-- MainWin methods --------------------------------------------------------*/ /* mainwin_resize: resize any main window pointed to by 'window' */ static void * mainwin_resize(void *window) { assert(window != NULL); struct MainWin *mainwin = window; // clear richwin err = wclear(mainwin->richwin->win); if (err == ERR) { write_error(ERROR_WINDOW_DRAWING, __LINE__, __func__); return NULL; } // delete richwin err = richwin_del(mainwin->richwin); if (err == ERR) return NULL; // create new richwin mainwin->richwin = mainwin->new_richwin(mainwin->sub); if (mainwin->richwin == NULL) return NULL; // redraw window if (mainwin->draw_window(mainwin->sub) == NULL) return NULL; return mainwin; } /* mainwin_cleanup: free resources used by a MainWin window */ static void mainwin_cleanup(struct MainWin *mainwin) { assert(mainwin != NULL); richwin_del(mainwin->richwin); mainwin->richwin = NULL; } /*-- BoardWin methods -------------------------------------------------------*/ /* boardwin_new_richwin: defaults for board rich window */ static struct RichWin * boardwin_new_richwin(void *window) { (void) window; struct RichWin *richwin; // create window richwin = richwin_new(LINES, COLS, 0, 0, NULL); if (richwin == NULL) { write_error(richwin_error_code, __LINE__, __func__); return NULL; } // set options err = leaveok(richwin->win, true); if (err == ERR) { richwin_del(richwin); write_error(ERROR_OPTION_SETTING, __LINE__, __func__); return NULL; } return richwin; } /* boardwin_draw: draw cool stuff on 'boardwin' in the color choosen by user */ static void * boardwin_draw(void *window) { assert(window != NULL); struct BoardWin *boardwin = window; struct RichWin *richwin = boardwin->base.richwin; const int colstep = (int) strlen(boardwin->word); // clear previous stuff err = wclear(richwin->win); if (err == ERR) { write_error(ERROR_WINDOW_DRAWING, __LINE__, __func__); return NULL; } if (!boardwin->on) { // save current screen and exit if board is turned off // write to virtual screen err = wnoutrefresh(richwin->win); if (err == ERR) { write_error(ERROR_WINDOW_RENDERING, __LINE__, __func__); return NULL; } return boardwin; } if (colstep <= 0) { // no appropriate word to draw return boardwin; } // set color if (colorize) { err = wbkgd(richwin->win, COLOR_PAIR(2)); if (err == ERR) { write_error(ERROR_WINDOW_DRAWING, __LINE__, __func__); return NULL; } } // write 'word' all over 'boardwin' const int left = (richwin->cols%colstep) / 2, colmax = richwin->cols - colstep; for (int line = 0; line < richwin->lines; line++) { // print 'word' repeatedly until end of line for (int col = left; col <= colmax; col+=colstep) { err = mvwaddnstr(richwin->win, line, col, boardwin->word, colstep); /* error is ignored when last character of word is written at the * lower right margin of screen (addch succeeds but returns an * error) */ if (err == ERR && richwin->cols-col != colstep) { dump_core(); write_error(ERROR_CHARACTER_OUTPUT, __LINE__, __func__); return NULL; } } } // write to virtual screen err = wnoutrefresh(richwin->win); if (err == ERR) { write_error(ERROR_WINDOW_RENDERING, __LINE__, __func__); return NULL; } return boardwin; } /* boardwin_cleanup: free resources used by a BoardWin window */ static void boardwin_cleanup(struct BoardWin *boardwin) { assert(boardwin != NULL); free(boardwin->word); boardwin->word = NULL; cycle_free(boardwin->colors); boardwin->colors = NULL; mainwin_cleanup(&boardwin->base); } /*-- MenuWin methods --------------------------------------------------------*/ /* menuwin_new_richwin: defaults for menu rich window */ static struct RichWin * menuwin_new_richwin(void *window) { (void) window; int lines = min_int(ARRAY_LENGTH(menu_options)+BORDERS, LINES), cols = COLS/2; // ser borders struct WinBorders winborders = { .ls = '|', .rs = '|', .ts = '-', .bs = '-', .tl = '+', .tr = '+', .bl = '+', .br = '+', }; struct RichWin *richwin; // create window richwin = richwin_new_centered(lines, cols, &winborders); if (richwin == NULL) { write_error(richwin_error_code, __LINE__, __func__); return NULL; } // set options if (keypad(richwin->win, true) == ERR ||leaveok(richwin->win, true) == ERR) { richwin_del(richwin); write_error(ERROR_OPTION_SETTING, __LINE__, __func__); return NULL; } return richwin; } /* * menuwin_draw: draw 'menuwin' and it's options each centered on their own * line, then highlight the selected option */ static void * menuwin_draw(void *window) { assert(window != NULL); static const int option_left = BORDER + INDICATOR, highlight_left = BORDER; struct MenuWin *menuwin = window; struct RichWin *richwin = menuwin->base.richwin; // calculate number of menu options to draw and width of each one const int option_maxnum = richwin->lines - BORDERS, option_num = min_int(option_maxnum, ARRAY_LENGTH(menu_options)), option_width = richwin->cols - BORDERS - INDICATORS; // clear menuwin err = werase(richwin->win); if (err == ERR) { write_error(ERROR_WINDOW_DRAWING, __LINE__, __func__); return NULL; } // border menuwin err = richwin_border(richwin); if (err == ERR) { write_error(ERROR_WINDOW_DRAWING, __LINE__, __func__); return NULL; } if (option_width > 0 && option_num > 0) { // draw menu options centered on window for (int i = 0; i < option_num; i++) { const int option_top = BORDER + i; const char *option_text = menu_options[i].text; // write centered menu option err = mvwaddcstr(richwin->win, option_top, option_left, option_text, option_width, TEXT_CONTINUATION); if (err == ERR) { write_error(ERROR_CHARACTER_OUTPUT, __LINE__, __func__); return NULL; } } // highlight selected option const int highlight_top = BORDER + (int) cycle_index(menuwin->options), highlight_width = option_width + INDICATORS; if (highlight_top < BORDER+option_maxnum) { highlight_str(richwin->win, highlight_top, highlight_left, highlight_width, 1); } } // write to virtual screen err = wnoutrefresh(richwin->win); if (err == ERR) { write_error(ERROR_WINDOW_RENDERING, __LINE__, __func__); return NULL; } return menuwin; } /* menuwin_cleanup: free resources used by a MenuWin window */ static void menuwin_cleanup(struct MenuWin *menuwin) { assert(menuwin != NULL); cycle_free(menuwin->options); menuwin->options = NULL; mainwin_cleanup(&menuwin->base); } /*-- PromptWin methods ------------------------------------------------------*/ /* promptwin_new_richwin: defaults for prompt rich window */ static struct RichWin * promptwin_new_richwin(void *window) { assert(window != NULL); struct PromptWin *promptwin = window; const int lines = min_int(BORDERS+2, LINES), needed_cols = promptwin->content_minlen + PADDINGS + BORDERS, cols = min_int(needed_cols, COLS/2); // set borders struct WinBorders winborders = { .ls = '|', .rs = '|', .ts = '-', .bs = '-', .tl = '+', .tr = '+', .bl = '+', .br = '+', }; struct RichWin *richwin; // create window richwin = richwin_new_centered(lines, cols, &winborders); if (richwin == NULL) { write_error(richwin_error_code, __LINE__, __func__); return NULL; } // set options err = keypad(richwin->win, true); if (err == ERR) { richwin_del(richwin); write_error(ERROR_OPTION_SETTING, __LINE__, __func__); return NULL; } return richwin; } /* promptwin_draw: draw 'propmtwin', it's prompt, and input field */ static void * promptwin_draw(void *window) { assert(window != NULL); struct PromptWin *promptwin = window; struct RichWin *richwin = promptwin->base.richwin; static const int content_left = BORDER + PADDING, prompt_top = BORDER; const int content_width = richwin->cols - BORDERS - PADDING, input_top = prompt_top + 1; // clear window err = werase(richwin->win) == ERR; if (err == ERR) { write_error(ERROR_WINDOW_DRAWING, __LINE__, __func__); return NULL; } // border window err = richwin_border(richwin) == ERR; if (err == ERR) { write_error(ERROR_WINDOW_DRAWING, __LINE__, __func__); return NULL; } if (content_width > 0 && richwin->lines > input_top) { // print prompt err = mvwaddcstr(richwin->win, prompt_top, content_left, promptwin->prompt, content_width, TEXT_CONTINUATION); if (err == ERR) { dump_core(); write_error(ERROR_CHARACTER_OUTPUT, __LINE__, __func__); return NULL; } // print input field err = mvwaddcstr(richwin->win, input_top, content_left, promptwin->input, content_width, INPUT_CONTINUATION); if (err == ERR) { write_error(ERROR_CHARACTER_OUTPUT, __LINE__, __func__); return NULL; } } // write to virtual screen err = wnoutrefresh(richwin->win); if (err == ERR) { write_error(ERROR_WINDOW_RENDERING, __LINE__, __func__); return NULL; } return promptwin; } /* promptwin_cleanup: free resources used by a PromptWin window */ static void promptwin_cleanup(struct PromptWin *promptwin) { assert(promptwin != NULL); free(promptwin->input); promptwin->input = NULL; mainwin_cleanup(&promptwin->base); } /*== application actions ====================================================*/ /* app_draw: enable drawing and draw board */ static struct BoardWin * app_draw(struct BoardWin *boardwin) { assert(boardwin != NULL); // turn board on and draw board boardwin->on = true; if (boardwin_draw(boardwin) == NULL) return NULL; return boardwin; } /* app_clean: erase stuff drwan on 'boardwin' */ static struct BoardWin * app_clean(struct BoardWin *boardwin) { assert(boardwin != NULL); struct RichWin *richwin = boardwin->base.richwin; // turn off board and clear it boardwin->on = false; err = wclear(richwin->win); if (err == ERR) { write_error(ERROR_WINDOW_DRAWING, __LINE__, __func__); return NULL; } // restore background color to original if (colorize) { err = wbkgd(richwin->win, COLOR_PAIR(0)); if (err == ERR) { write_error(ERROR_WINDOW_DRAWING, __LINE__, __func__); return NULL; } } // write to virtual screen err = wnoutrefresh(richwin->win); if (err == ERR) { write_error(ERROR_WINDOW_RENDERING, __LINE__, __func__); return NULL; } return boardwin; } /* app_change_word: replace word to draw with new one from user */ static struct BoardWin * app_change_word(struct BoardWin *boardwin) { assert(boardwin != NULL); // prompt user for new word if (prompt_user(DEFAULT_PROMPT, boardwin->word, WORD_MAXLEN) < 0) { return NULL; } // draw board if (boardwin->on) { if (boardwin_draw(boardwin) == NULL) return NULL; } return boardwin; } /* app_change_color: change the color to use when drawing 'boardwin' */ static struct BoardWin * app_change_color(struct BoardWin *boardwin) { assert(boardwin != NULL); if (!colorize) { return boardwin; } // modify board drawing color pair const short bg_color = *(const short *) cycle_next(boardwin->colors); err = init_pair(2, COLOR_BLACK, bg_color); if (err == ERR) { write_error(ERROR_COLOR_PAIR_DEFINITION, __LINE__, __func__); return NULL; } // draw board if (boardwin->on) { if (boardwin_draw(boardwin) == NULL) return NULL; } return boardwin; } /* app_end: mark 'boardwin' done being used so program can exit */ static struct BoardWin * app_end(struct BoardWin *boardwin) { assert(boardwin != NULL); boardwin->done = true; return boardwin; }