// Compilation command: // - release: gcc main.c -Wall -O2 -m64 -lncurses -o ttt // - debug : gcc main.c -Wall -g3 -m64 -lncurses -o ttt -D DEBUG #include #include #include #include #include #include #include #include #include #include #define STR_(X) #X // Convert to string. #define STR(X) STR_(X) // Force argument expansions before converting to string. #define MAX_TASK_NAME 64 // Maximum task name length, including null-terminator. #define FIRST_DAY_OF_WEEK 1 // (0-6, Sunday = 0) #define LOG_FILE_NAME "log.txt" #define DB_BIN_PATH_NAME "./database.bin" #define DB_CSV_PATH_NAME "./database.csv" typedef struct /*__attribute__((__packed__))*/ { uint32_t time[7]; char name[MAX_TASK_NAME]; } task_t; typedef struct /*__attribute__((__packed__))*/ { uint64_t modified_on; uint32_t count; uint32_t capacity; task_t* active_task; task_t* tasks; } database_t; const char* DB_BIN_FILE_SIGNATURE = "TTT:B:01"; const uint8_t DB_BIN_FILE_SIGNATURE_SIZE = sizeof(DB_BIN_FILE_SIGNATURE); const size_t SIZEOF_TASK_T = sizeof(task_t); const size_t SIZEOF_DATABASE_T = sizeof(database_t); const char* DAYS_OF_WEEK[] = { "sun", "mon", "tue", "wed", "thu", "fri", "sat" }; const uint8_t DAYS_ON_WEEK = sizeof(DAYS_OF_WEEK)/sizeof(char*); uint32_t selected_task; database_t database; // Given an UTF8 encoded string, truncate it to length without breaking any UTF8 character. // The string should have capacity for at least length number of items. // The terminating null byte ('\0') is included in length. // The function returns the amount of items that got discarded counting from length. size_t truncate_string_utf8(char* string, size_t length) { // Check for special cases where no truncation is required. if (length == 0 || string[length-1] == '\0') { return 0; } // Search for a non-UTF8-sequence-item so we can truncate the string. size_t idx = length - 1; while(idx > 0 && ((string[idx] & 0xC0) == 0x80)) { idx--; } string[idx] = '\0'; return length - idx; } // Uses strchr to replace all instances of find by replace. // Returns string. char* replace_char(char* string, char find, char replace) { char* idx = string; while((idx = strchr(idx, find)) != NULL) { *idx = replace; idx++; } return string; } void print_task(const task_t* task) { printf("name: '%s'\n", task->name); printf("t[0]: '%" PRIu32 "'\n", task->time[0]); printf("t[1]: '%" PRIu32 "'\n", task->time[1]); printf("t[2]: '%" PRIu32 "'\n", task->time[2]); printf("t[3]: '%" PRIu32 "'\n", task->time[3]); printf("t[4]: '%" PRIu32 "'\n", task->time[4]); printf("t[5]: '%" PRIu32 "'\n", task->time[5]); printf("t[6]: '%" PRIu32 "'\n", task->time[6]); } // Adds task to database. If necessary, expands database capacity. // Returns success. bool add_task(database_t* db, const task_t* task) { assert(db != NULL); assert(task != NULL); if (db->count == UINT32_MAX) { fprintf(stderr, "Database reached maximum capacity, discarding task '%s'.\n", task->name); return false; } // If necessary, expand database capacity. uint32_t current_capacity = db->capacity; if((db->count + 1) > current_capacity) { uint32_t new_capacity = current_capacity == 0 ? 2 : current_capacity >= UINT32_MAX >> 2 ? UINT32_MAX : current_capacity << 1; task_t* new_tasks = realloc(db->tasks, new_capacity * SIZEOF_TASK_T); if (new_tasks == NULL) { fprintf(stderr, "Failed to expand database, discarding task '%s'.\n", task->name); return false; } db->capacity = new_capacity; db->tasks = new_tasks; } // Store new task. memcpy(&(db->tasks[db->count]), task, SIZEOF_TASK_T); db->count++; return true; } // Removes task by index from database. If possible, shrinks database capacity. // Returns success. bool remove_task(database_t* db, uint32_t index) { assert(db != NULL); assert(index < db->count); if (index >= db->count) { fprintf(stderr, "Failed to remove out-of-bounds index '%" PRIu32 "'.", index); return false; } // Move tasks after the index position to their new positions. memmove(&db->tasks[index], &db->tasks[index+1], (db->count - index - 1) * SIZEOF_TASK_T); db->count--; // If possible, shrink database capacity. uint32_t current_capacity = db->capacity; if (db->count <= (current_capacity >> 2)) { uint32_t new_capacity = current_capacity >> 1; task_t* new_tasks = realloc(db->tasks, new_capacity * SIZEOF_TASK_T); if (new_tasks == NULL) { fprintf(stderr, "Failed to shrink database.\n"); return false; } db->capacity = new_capacity; db->tasks = new_tasks; } return true; } void clear_database(database_t* db) { free(db->tasks); memset(db, 0, SIZEOF_TASK_T); } // Stores data from database into binary file. // Returns success. bool store_database(const database_t* db, const char* path_name) { assert(db != NULL); assert(path_name != NULL); // Open file. FILE* file = fopen(path_name, "w"); if (file == NULL) { fprintf(stderr, "Failed to open file '%s' while storing database: %s.\n", path_name, strerror(errno)); return false; } fwrite(DB_BIN_FILE_SIGNATURE, sizeof(char), DB_BIN_FILE_SIGNATURE_SIZE, file); fwrite(db, SIZEOF_DATABASE_T, 1, file); fwrite(db->tasks, SIZEOF_TASK_T, db->count, file); fclose(file); return true; } // Loads data from binary file into database. // Returns success. bool load_database(database_t* db, const char* path_name) { assert(db != NULL); assert(path_name != NULL); // Open file. FILE* file = fopen(path_name, "r"); if (file == NULL) { fprintf(stderr, "Failed to open file '%s' while loading database: %s.\n", path_name, strerror(errno)); return false; } // Validate file signature. char file_signature[DB_BIN_FILE_SIGNATURE_SIZE]; fread(&file_signature, sizeof(char), DB_BIN_FILE_SIGNATURE_SIZE, file); if (strncmp(file_signature, DB_BIN_FILE_SIGNATURE, DB_BIN_FILE_SIGNATURE_SIZE) != 0) { fprintf(stderr, "Invalid file signature.\n"); return false; } // Read database structure. fread(db, SIZEOF_DATABASE_T, 1, file); // Keep track of last active task. ptrdiff_t active_task_index = db->active_task - db->tasks; // Restore database capacity. db->tasks = calloc(db->capacity, SIZEOF_TASK_T); // Restore last active task. if (db->active_task != NULL) { db->active_task = db->tasks + active_task_index; } // Read database entries. fread(db->tasks, SIZEOF_TASK_T, db->count, file); // Make sure we are reading all the file. assert(fgetc(file) == EOF); fclose(file); return true; } // Exports data into CSV file. // Returns success. bool export_to_csv(const database_t* db, const char* path_name) { assert(db != NULL); assert(path_name != NULL); FILE* file = fopen(path_name, "w"); if (file == NULL) { fprintf(stderr, "Failed to open file '%s' while exporting to CSV: %s.\n", path_name, strerror(errno)); return false; } fprintf(file, "%s,%s,%s,%s,%s,%s,%s,%s\n", "task", DAYS_OF_WEEK[0], DAYS_OF_WEEK[1], DAYS_OF_WEEK[2], DAYS_OF_WEEK[3], DAYS_OF_WEEK[4], DAYS_OF_WEEK[5], DAYS_OF_WEEK[6] ); for (uint32_t idx = 0; idx < db->count; idx++) { const task_t* task = &db->tasks[idx]; fprintf(file, "%s,%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 "\n", task->name, task->time[0], task->time[1], task->time[2], task->time[3], task->time[4], task->time[5], task->time[6] ); } fclose(file); return true; } // Imports CSV file into database. // Returns success. bool import_from_csv(database_t* db, const char* path_name) { assert(db != NULL); assert(path_name != NULL); FILE* file = fopen(path_name, "r"); if (file == NULL) { fprintf(stderr, "Failed to open file '%s' while importing from CSV: %s.\n", path_name, strerror(errno)); return false; } uint32_t number_of_entries = 0; task_t task; char *line_buffer = NULL; size_t line_buffer_size = 0; ssize_t read_characters = 0; char* name_delimiter; // Skip header line. fscanf(file, "%*[^\n]\n"); // Parse CSV file. while(true) { read_characters = getline(&line_buffer, &line_buffer_size, file); // Check if reached EOF. if (read_characters == -1) { break; } // Clear task struct. memset(&task, 0, SIZEOF_TASK_T); // Find task name string limits. name_delimiter = strchr(line_buffer, ','); size_t name_length = (name_delimiter - line_buffer) + 1; if (name_length > MAX_TASK_NAME) { name_length = MAX_TASK_NAME; } // Import task name. memcpy(task.name, line_buffer, name_length); truncate_string_utf8(task.name, name_length); // Parse task times. if(sscanf(name_delimiter+1, "%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32, &task.time[0], &task.time[1], &task.time[2], &task.time[3], &task.time[4], &task.time[5], &task.time[6] ) != 7) { replace_char(line_buffer, '\n', ' '); fprintf(stderr, "Discarding invalid line '%s' and continuing.\n", line_buffer); continue; } // Add new database entry. number_of_entries++; add_task(db, &task); } fclose(file); free(line_buffer); return true; } enum TEST { T_NONE = 0x00, T_UTC = 0x01, T_DAY = 0x02, T_SBIN = 0x04, T_LBIN = 0x08, T_ECSV = 0x10, T_ICSV = 0x20, T_SOS = 0x40, T_ALL = 0xFF, }; void prototype(int level) { const char* done = "# -- done -- -- -- /\n"; /////////////////////////////////////////////////////////////////////////// // Get current UTC time. if (level & T_UTC) { fprintf(stderr, "# current UTC time --------------------------- \\\n"); time_t x = time(NULL); printf("%s", ctime(&x)); fprintf(stderr, done); } /////////////////////////////////////////////////////////////////////////// // Get current day of the week. if (level & T_DAY) { fprintf(stderr, "# current day of the week --------------------- \\\n"); time_t now_ut = time(NULL); uint8_t week_day = localtime(&now_ut)->tm_wday; fprintf(stderr, "%d\n", week_day); fprintf(stderr, done); } /////////////////////////////////////////////////////////////////////////// // Prepare some data for testing. /* task_t tmp[] = { { .name = "ALPHA-TASK", .time = { 0, 0, 0, 0, 0, 0, 0 }, }, { .name = "BETA-TASK", .time = { 1, 1, 1, 1, 1, 1, 1 }, }, { .name = "DELTA-TASK", .time = { 2, 2, 2, 2, 2, 2, 2 }, } }; uint32_t number_of_items = sizeof(tmp) / sizeof(task_t); database.tasks = calloc(number_of_items, SIZEOF_TASK_T); database.capacity = number_of_items; database.count = number_of_items; memcpy(database.tasks, &tmp, SIZEOF_TASK_T * number_of_items); */ /////////////////////////////////////////////////////////////////////////// // Store database from memory to binary file. if (level & T_SBIN) { fprintf(stderr, "# store database ------------------------------ \\\n"); store_database(&database, DB_BIN_PATH_NAME); fprintf(stderr, done); } /////////////////////////////////////////////////////////////////////////// // Load database from binary file to memory. if (level & T_LBIN) { fprintf(stderr, "# load database ------------------------------- \\\n"); database_t db_load; memset(&db_load, 0, SIZEOF_DATABASE_T); load_database(&db_load, DB_BIN_PATH_NAME); fprintf(stderr, "loaded %" PRIu32 " entries.\n", db_load.count); for (uint32_t idx = 0; idx < db_load.count; idx++) { print_task(&db_load.tasks[idx]); } clear_database(&db_load); fprintf(stderr, done); } /////////////////////////////////////////////////////////////////////////// // Export database to CSV file. if (level & T_ECSV) { fprintf(stderr, "# export to CSV ------------------------------- \\\n"); export_to_csv(&database, DB_CSV_PATH_NAME); fprintf(stderr, done); } /////////////////////////////////////////////////////////////////////////// // Import database from CSV file. if (level & T_ICSV) { fprintf(stderr, "# import from CSV ----------------------------- \\\n"); database_t db_import; memset(&db_import, 0, SIZEOF_DATABASE_T); import_from_csv(&db_import, DB_CSV_PATH_NAME); fprintf(stderr, "imported %" PRIu32 " entries.\n", db_import.count); for (uint32_t idx = 0; idx < db_import.count; idx++) { print_task(&db_import.tasks[idx]); } clear_database(&db_import); fprintf(stderr, done); } /////////////////////////////////////////////////////////////////////////// // Check size of structs if (level & T_SOS) { size_t size; char* name; fprintf(stderr, "# check structs sizes ------------------------- \\\n"); name = "database_t"; size = sizeof(database_t); fprintf(stderr, "sizeof(%s) = %zu bytes (%zu bits : %6.3f W64b)\n", name, size, size*8, ((double)size)*8.0/64.0); name = "task_t"; size = sizeof(task_t); fprintf(stderr, "sizeof(%s) = %zu bytes (%zu bits : %6.3f W64b)\n", name, size, size*8, ((double)size)*8.0/64.0); fprintf(stderr, done); } } char* line_buffer; int size_x, size_y, pos_x, pos_y; uint8_t selected_layout = 0; #define TABLE_HEADERS_SIZE 9 typedef struct { int table_size; int table_headers_size; char* table_headers[TABLE_HEADERS_SIZE]; } layout_t; layout_t* layouts = NULL; void initialize_layouts() { layout_t* layout = NULL; layouts = calloc(2, sizeof(layout_t)); // Layout : 0 : normal. layout = &layouts[0]; layout->table_size = 8; layout->table_headers_size = TABLE_HEADERS_SIZE; // TODO Headers must be dynamic according to FIRST_DAY_OF_WEEK layout->table_headers[0] = " Task "; layout->table_headers[1] = " Mon "; layout->table_headers[2] = " Tue "; layout->table_headers[3] = " Wed "; layout->table_headers[4] = " Thu "; layout->table_headers[5] = " Fri "; layout->table_headers[6] = " Sat "; layout->table_headers[7] = " Sun "; layout->table_headers[8] = " Total "; // layout->table_headers = { " Task ", " Mon ", " Tue ", " Wed ", " Thu ", " Fri ", " Sat ", " Sun ", " Total " }; // Layout : 1 : compact. layout = &layouts[1]; layout->table_size = 6; layout->table_headers_size = TABLE_HEADERS_SIZE; // TODO Headers must be dynamic according to FIRST_DAY_OF_WEEK layout->table_headers[0] = " Task "; layout->table_headers[1] = " M "; layout->table_headers[2] = " T "; layout->table_headers[3] = " W "; layout->table_headers[4] = " T "; layout->table_headers[5] = " F "; layout->table_headers[6] = " S "; layout->table_headers[7] = " S "; layout->table_headers[8] = " # "; // layout->table_headers = { " T ", " M ", " T ", " W ", " T ", " F ", " S ", " S ", " # " }; } void initialization() { selected_task = 0; if (load_database(&database, DB_BIN_PATH_NAME) == false) { memset(&database, 0, SIZEOF_DATABASE_T); } } void free_memory() { clear_database(&database); free(line_buffer); line_buffer = NULL; free(layouts); layouts = NULL; } void draw_header() { layout_t* layout = &layouts[selected_layout]; int table_size = layout->table_size; char** table_headers = layout->table_headers; mvaddch(0, 0, ACS_ULCORNER); for (int idx = 1; idx < size_x-1; idx++) { addch(ACS_HLINE); } addch(ACS_URCORNER); int start_columns = size_x - 1 - 8*table_size - 2; int columns[] = { 0, start_columns+(table_size*0), start_columns+(table_size*1), start_columns+(table_size*2), start_columns+(table_size*3), start_columns+(table_size*4), start_columns+(table_size*5), start_columns+(table_size*6), start_columns+(table_size*7), }; mvaddstr(0, columns[0]+2, table_headers[0]); for (int idx = 1; idx < 9; idx++) { mvaddch(0, columns[idx], ACS_TTEE); mvaddstr(0, columns[idx]+2, table_headers[idx]); } } void draw_table() { task_t* task; task_t* active_task; task_t* selected_taskx; // TODO Improve naming. layout_t* layout = &layouts[selected_layout]; int table_size = layout->table_size; // char** table_headers = layout->table_headers; // TODO Maybe move this to side function? start_color(); init_pair(1, COLOR_BLUE, COLOR_BLACK); init_pair(2, COLOR_BLACK, COLOR_CYAN); init_pair(3, COLOR_WHITE, COLOR_BLUE); int color_pair; int start_columns = size_x - 1 - 8*table_size - 2; int columns[] = { 0, start_columns+(table_size*0), start_columns+(table_size*1), start_columns+(table_size*2), start_columns+(table_size*3), start_columns+(table_size*4), start_columns+(table_size*5), start_columns+(table_size*6), start_columns+(table_size*7), size_x-1, }; // Draw rows with tasks. move(1, 0); active_task = database.active_task; selected_taskx = &database.tasks[selected_task]; for (uint32_t idx = 0; idx < database.count; idx++){ task = &database.tasks[idx]; // Enable highlight color on selected entry. color_pair = 0; if (task == active_task) { color_pair = 1; } if (task == selected_taskx) { color_pair = 2; } if (task == active_task && task == selected_taskx) { color_pair = 3; } if (color_pair > 0) { attron(COLOR_PAIR(color_pair)); } attron(COLOR_PAIR(color_pair)); // Clear line_buffer and add string termination. memset(line_buffer, ' ', size_x * sizeof(char)); line_buffer[size_x-1] = '\0'; // Check maximum available space for task name and print it accordingly. size_t task_name_length = strlen(task->name)*sizeof(char); size_t max_column_length = start_columns - 2; size_t copy_length = task_name_length < max_column_length ? task_name_length : max_column_length; memcpy(line_buffer+2, task->name, copy_length); addstr(line_buffer); // Disable highlight color on selected entry. if (color_pair > 0) { attroff(COLOR_PAIR(color_pair)); } // Print columns separators. pos_y = getcury(stdscr); for (int c_idx = 0; c_idx < sizeof(columns)/sizeof(int); c_idx++) { mvaddch(pos_y, columns[c_idx], ACS_VLINE); } // Go to next line. pos_y++; move(pos_y, 0); } // Draw empty rows. // TODO Do this properly. int dummy = 100; while (dummy > 0) { // Clear current line. move(pos_y, 0); clrtoeol(); // Print columns separators. pos_y = getcury(stdscr); for (int c_idx = 0; c_idx < sizeof(columns)/sizeof(int); c_idx++) { mvaddch(pos_y, columns[c_idx], ACS_VLINE); } pos_y++; move(pos_y, 0); dummy--; } } void draw_footer() { const char *app_name = "Task Tracker"; const char *app_version = "v1.0"; int row, col; /* to store the number of rows and the number of colums of the screen */ initscr(); /* start the curses mode */ getmaxyx(stdscr,row,col); /* get the number of rows and columns */ mvaddch(row-1, 0, ACS_LLCORNER); addch(' '); printw(app_name); addch(' '); for (int idx = strlen(app_name) + 3; idx < col - strlen(app_version) - 3; idx ++) { addch(ACS_HLINE); } addch(' '); printw(app_version); addch(' '); addch(ACS_LRCORNER); // TODO This code is now repeated accross the header, table and footer. Clean this up. layout_t* layout = &layouts[selected_layout]; int table_size = layout->table_size; char** table_headers = layout->table_headers; int start_columns = size_x - 1 - 8*table_size - 2; int columns[] = { 0, start_columns+(table_size*0), start_columns+(table_size*1), start_columns+(table_size*2), start_columns+(table_size*3), start_columns+(table_size*4), start_columns+(table_size*5), start_columns+(table_size*6), start_columns+(table_size*7), }; mvaddstr(row-1, columns[0]+2, table_headers[0]); for (int idx = 1; idx < 9; idx++) { mvaddch(row-1, columns[idx], ACS_BTEE); } } WINDOW *create_newwin(int height, int width, int starty, int startx); void destroy_win(WINDOW *local_win); int main(int argc, char *argv[]) { // Make sure architecture uses 8bits per char. assert(CHAR_BIT == 8); // TODO Parse commands using: https://stackoverflow.com/questions/9642732/parsing-command-line-arguments-in-c if (argc > 1) { char* action; bool do_action = false; for (int idx = 1; idx < argc; idx++) { action = "--version"; do_action = strncmp(argv[idx], action, strlen(action)) == 0; action = "-v"; do_action |= strncmp(argv[idx], action, strlen(action)) == 0; if (do_action) { fprintf(stdout, "Task Time Tracker v1.0\n"); return EXIT_SUCCESS; } action = "--fake_bin"; do_action = strncmp(argv[idx], action, strlen(action)) == 0; if (do_action) { prototype(T_SBIN); return EXIT_SUCCESS; } action = "--t_sos"; do_action = strncmp(argv[idx], action, strlen(action)) == 0; if (do_action) { prototype(T_SOS); return EXIT_SUCCESS; } action = "--t_icsv"; do_action = strncmp(argv[idx], action, strlen(action)) == 0; if (do_action) { prototype(T_ICSV); return EXIT_SUCCESS; } action = "--t_lbin"; do_action = strncmp(argv[idx], action, strlen(action)) == 0; if (do_action) { prototype(T_LBIN); return EXIT_SUCCESS; } action = "--t_csv2bin"; do_action = strncmp(argv[idx], action, strlen(action)) == 0; if (do_action) { clear_database(&database); import_from_csv(&database, DB_CSV_PATH_NAME); store_database(&database, DB_BIN_PATH_NAME); return EXIT_SUCCESS; } action = "--test"; do_action = strncmp(argv[idx], action, strlen(action)) == 0; if (do_action) { prototype(T_ALL); return EXIT_SUCCESS; } } fprintf(stdout, "Unkown command '%s'.\nUse '%s --help' for list of commands.\n", argv[1], argv[0]); return EXIT_FAILURE; } initialization(); initialize_layouts(); WINDOW *my_win; int startx, starty, width, height; int ch; initscr(); // Start curses mode. cbreak(); // Line buffering disabled, Pass on everty thing to me. keypad(stdscr, TRUE); // I need that nifty F1 curs_set(0); // Set cursor invisible. height = 3; width = 10; starty = (LINES - height) / 2; // Calculating for a center placement of the window. startx = (COLS - width) / 2; // printw("Press F1 to exit"); refresh(); my_win = create_newwin(height, width, starty, startx); ch = KEY_RESIZE; ESCDELAY = 0; do { switch(ch) { case KEY_F(1): { task_t new_task; add_task(&database, &new_task); move(database.count, 2); curs_set(1); getnstr(database.tasks[database.count-1].name, MAX_TASK_NAME-1); curs_set(0); break; } case KEY_F(2): remove_task(&database, selected_task); break; case '\n': case ' ': if (true) { task_t* current_task = database.active_task; task_t* next_task = &database.tasks[selected_task]; if (current_task != NULL) { // TODO Add remaining time to task. database.active_task = NULL; } if (current_task != next_task) { database.active_task = next_task; } database.modified_on = time(NULL); store_database(&database, DB_BIN_PATH_NAME); } break; case KEY_RESIZE: erase(); if (line_buffer != NULL) { free(line_buffer); } getmaxyx(stdscr, size_y, size_x); line_buffer = malloc(size_x * sizeof(char)); break; case KEY_LEFT: destroy_win(my_win); my_win = create_newwin(height, width, starty,--startx); break; case KEY_RIGHT: destroy_win(my_win); my_win = create_newwin(height, width, starty,++startx); break; case KEY_UP: selected_task = selected_task == 0 ? 0 : selected_task - 1; break; case KEY_DOWN: selected_task = (selected_task+1) == database.count ? selected_task : selected_task + 1; break; } if (size_x >= 60 && size_y > 2) { selected_layout = size_x > 100 ? 0 : 1; draw_header(); draw_table(); draw_footer(); } else { const char* INVALID_WINDOW_MESSAGE = "Please expand window."; mvaddstr(size_y / 2, (size_x - strlen(INVALID_WINDOW_MESSAGE)) / 2, INVALID_WINDOW_MESSAGE); } // TEST // if (ch == KEY_F(1) || ch == KEY_F(2)) { // mvprintw(1, 1, "db:'%u'/'%u'", database.count, database.capacity); // } } while((ch = getch()) != 'q'); store_database(&database, DB_BIN_PATH_NAME); free_memory(); endwin(); return EXIT_SUCCESS; } WINDOW *create_newwin(int height, int width, int starty, int startx) { WINDOW *local_win; local_win = newwin(height, width, starty, startx); box(local_win, 0 , 0); /* 0, 0 gives default characters * for the vertical and horizontal * lines */ wrefresh(local_win); /* Show that box */ return local_win; } void destroy_win(WINDOW *local_win) { /* box(local_win, ' ', ' '); : This won't produce the desired * result of erasing the window. It will leave it's four corners * and so an ugly remnant of window. */ wborder(local_win, ' ', ' ', ' ',' ',' ',' ',' ',' '); /* The parameters taken are * 1. win: the window on which to operate * 2. ls: character to be used for the left side of the window * 3. rs: character to be used for the right side of the window * 4. ts: character to be used for the top side of the window * 5. bs: character to be used for the bottom side of the window * 6. tl: character to be used for the top left corner of the window * 7. tr: character to be used for the top right corner of the window * 8. bl: character to be used for the bottom left corner of the window * 9. br: character to be used for the bottom right corner of the window */ wrefresh(local_win); delwin(local_win); }