#include "arc/console/view.h" #include "key.h" #include "arc/console/element.h" #include "arc/std/bool.h" #include "arc/std/errno.h" #include "arc/std/vector.h" #include "arc/std/string.h" #include #include #include uint8_t arc_ncurses_win_size = 0; struct ARC_ConsoleView { WINDOW *window; ARC_Rect bounds; ARC_Bool echo; ARC_Vector *elements; }; void ARC_ConsoleView_Create(ARC_ConsoleView **view, ARC_Rect bounds){ if(arc_ncurses_win_size == ~(uint8_t)0){ arc_errno = ARC_ERRNO_OVERFLOW; ARC_DEBUG_ERR("ARC_NCurses_Create(ncurses), max num of ARC_NCurses have been created, consider making arc_ncurses_win_size a uint32_t to increase the max"); *view = NULL; return; } //if this is the first ncurses, init ncurses if(arc_ncurses_win_size == 0){ setlocale(LC_ALL, ""); initscr(); //start_color(); cbreak(); keypad(stdscr, TRUE); refresh(); } *view = (ARC_ConsoleView *)malloc(sizeof(ARC_ConsoleView)); ARC_Rect viewBounds = { 0, 0, COLS, LINES }; if(bounds.w != 0 && bounds.h != 0){ viewBounds = bounds; } (*view)->window = newwin(viewBounds.h, viewBounds.w, viewBounds.y, viewBounds.x); (*view)->bounds = viewBounds; keypad((*view)->window, TRUE); noecho(); (*view)->echo = false; ARC_Vector_Create(&(*view)->elements); wrefresh((*view)->window); arc_ncurses_win_size++; } void ARC_ConsoleView_Destroy(ARC_ConsoleView *ncurses){ arc_ncurses_win_size--; ARC_Vector_Destroy(ncurses->elements); delwin(ncurses->window); free(ncurses); if(arc_ncurses_win_size == 0){ endwin(); } } void ARC_ConsoleView_AddElement(ARC_ConsoleView *view, ARC_ConsoleElement *element){ ARC_Vector_Add(view->elements, (void *)element); } void ARC_ConsoleView_RemoveElement(ARC_ConsoleView *view, uint32_t index){ ARC_Vector_RemoveIndex(view->elements, index); } void ARC_ConsoleView_Clear(ARC_ConsoleView *view){ wclear(view->window); wrefresh(view->window); } void ARC_ConsoleView_RenderCharAt(ARC_ConsoleView *view, char character, ARC_Point pos){ mvwprintw(view->window, pos.y, pos.x, "%c", character); wrefresh(view->window); } void ARC_ConsoleView_RenderWCharAt(ARC_ConsoleView *view, wchar_t character, ARC_Point pos){ mvwprintw(view->window, pos.y, pos.x, "%lc", character); wrefresh(view->window); } void ARC_ConsoleView_RenderKeyAt(ARC_ConsoleView *view, ARC_ConsoleKey key, ARC_Point pos){ mvwprintw(view->window, pos.y, pos.x, "%c", (char)key.key); wrefresh(view->window); } void ARC_ConsoleView_RenderUint32At(ARC_ConsoleView *view, uint32_t uint32, ARC_Point pos){ mvwprintw(view->window, pos.y, pos.x, "%d", uint32); wrefresh(view->window); } void ARC_ConsoleView_RenderStringAt(ARC_ConsoleView *view, ARC_String *text, ARC_Point pos){ mvwprintw(view->window, pos.y, pos.x, "%s", text->data); wrefresh(view->window); } void ARC_ConsoleView_RenderCStringWithStrlenAt(ARC_ConsoleView *view, char *cstr, ARC_Point pos){ mvwprintw(view->window, pos.y, pos.x, "%s", cstr); wrefresh(view->window); } void ARC_ConsoleView_RenderRect(ARC_ConsoleView *view, ARC_Rect bounds){ //render corners ARC_ConsoleView_RenderWCharAt(view, L'┌', (ARC_Point){ bounds.x, bounds.y }); ARC_ConsoleView_RenderWCharAt(view, L'└', (ARC_Point){ bounds.x, (bounds.h - 1) + bounds.y }); ARC_ConsoleView_RenderWCharAt(view, L'┐', (ARC_Point){ (bounds.w - 1) + bounds.x, bounds.y }); ARC_ConsoleView_RenderWCharAt(view, L'┘', (ARC_Point){ (bounds.w - 1) + bounds.x, (bounds.h - 1) + bounds.y }); //render virticle lines for(int32_t x = 1; x < bounds.w - 1; x++){ ARC_ConsoleView_RenderWCharAt(view, L'─', (ARC_Point){ bounds.x + x, bounds.y }); ARC_ConsoleView_RenderWCharAt(view, L'─', (ARC_Point){ bounds.x + x, (bounds.h - 1) + bounds.y }); } //render horizontal lines for(int32_t y = 1; y < bounds.h - 1; y++){ ARC_ConsoleView_RenderWCharAt(view, L'│', (ARC_Point){ bounds.x, bounds.y + y }); ARC_ConsoleView_RenderWCharAt(view, L'│', (ARC_Point){ (bounds.w - 1) + bounds.x, bounds.y + y }); } } void ARC_ConsoleView_RenderElements(ARC_ConsoleView *view){ for(uint32_t i = 0; i < ARC_Vector_Size(view->elements); i++){ ARC_ConsoleElement *element = (ARC_ConsoleElement *)ARC_Vector_Get(view->elements, i); element->renderFn(view, element); wrefresh(view->window); } } ARC_Rect ARC_ConsoleView_GetBounds(ARC_ConsoleView *view){ return view->bounds; } ARC_ConsoleElement *ARC_ConsoleView_GetElement(ARC_ConsoleView *view, uint32_t index){ return (ARC_ConsoleElement *)ARC_Vector_Get(view->elements, index); } char ARC_ConsoleView_GetChar(ARC_ConsoleView *view){ return wgetch(view->window); } char ARC_ConsoleView_GetCharAt(ARC_ConsoleView *view, ARC_Point pos){ return mvwgetch(view->window, pos.y, pos.x); } ARC_ConsoleKey ARC_ConsoleView_GetConsoleKeyAt(ARC_ConsoleView *view, ARC_Point pos){ return (ARC_ConsoleKey){ mvwgetch(view->window, pos.y, pos.x) }; } ARC_ConsoleKey *ARC_ConsoleView_GetCreateConsoleKeyAt(ARC_ConsoleView *view, ARC_Point pos){ ARC_ConsoleKey *key; ARC_ConsoleKey_Create(&key, NULL); key->key = mvwgetch(view->window, pos.y, pos.x); return key; } ARC_String *ARC_ConsoleView_GetStringInput(ARC_ConsoleView *view, ARC_Point pos, ARC_ConsoleView_OverrideCharInputFn *overrideCharInputFn, void *userdata){ noecho(); uint32_t cstringSize = view->bounds.w - pos.x; char cstring[view->bounds.w - pos.x]; ARC_ConsoleKey temp = ARC_ConsoleView_GetConsoleKeyAt(view, pos); uint32_t index = 0; while(temp.key != '\n'){ //store the last size to be able to clear efficeintly uint32_t lastSize = index; //if override function exists and it overrode the current char if(overrideCharInputFn != NULL && (*overrideCharInputFn)(&temp, cstring, &index, cstringSize, userdata)){ for(uint32_t i = 0; i < lastSize; i++){ ARC_ConsoleView_RenderCharAt(view, ' ', (ARC_Point){ pos.x + i, pos.y }); } for(uint32_t i = 0; i < index; i++){ ARC_ConsoleView_RenderCharAt(view, cstring[i], (ARC_Point){ pos.x + i, pos.y }); } temp = ARC_ConsoleView_GetConsoleKeyAt(view, (ARC_Point){ pos.x + index, pos.y }); continue; } if(temp.key == KEY_BACKSPACE || temp.key == KEY_DC || temp.key == 127){ if(index == 0){ temp = ARC_ConsoleView_GetConsoleKeyAt(view, (ARC_Point){ pos.x + index, pos.y }); continue; } index--; ARC_ConsoleView_RenderCharAt(view, ' ', (ARC_Point){ pos.x + index, pos.y }); cstring[index] = '\0'; temp = ARC_ConsoleView_GetConsoleKeyAt(view, (ARC_Point){ pos.x + index, pos.y }); continue; } if(index < (view->bounds.w - 1) - pos.x){ ARC_ConsoleView_RenderCharAt(view, (char)(temp.key), (ARC_Point){ pos.x + index, pos.y }); cstring[index] = (char)(temp.key); index++; } temp = ARC_ConsoleView_GetConsoleKeyAt(view, (ARC_Point){ pos.x + index, pos.y }); } if(view->echo){ echo(); } if(index == 0){ return NULL; } ARC_String *string; ARC_String_Create(&string, cstring, index); return string; } void ARC_ConsoleView_SetCursorVisibility(ARC_ConsoleView *view, uint8_t visibility){ switch(visibility){ case ARC_CONSOLE_VIEW_CURSOR_HIDDEN: curs_set(0); break; case ARC_CONSOLE_VIEW_CURSOR_VISIBLE: curs_set(1); break; } } void ARC_ConsoleView_SetBorder(ARC_ConsoleView *view, uint32_t border){ switch(border){ case ARC_CONSOLE_VIEW_BORDER_NONE: wborder(view->window, ' ', ' ', ' ',' ',' ',' ',' ',' '); break; case ARC_CONSOLE_VIEW_BORDER_DEFAULT: box(view->window, 0, 0); break; } wrefresh(view->window); } void ARC_ConsoleView_SetAttribute(ARC_ConsoleView *view, uint32_t attribute){ switch(attribute){ case ARC_CONSOLE_VIEW_ATTRIBUTE_NONE: wattroff(view->window, A_REVERSE); break; case ARC_CONSOLE_VIEW_ATTRIBUTE_REVERSE: wattron(view->window, A_REVERSE); break; } }