From b4fd33a43b1523b91328f1349e252d09facfa243 Mon Sep 17 00:00:00 2001 From: Brian Barto Date: Wed, 18 Jan 2017 18:51:10 -0500 Subject: [PATCH] Create an abstraction layer between terminal and main nms code so we can substitute ncurses functions without duplicating code. modified: Makefile deleted: src/nms-ncurses.c modified: src/nms.c new file: src/nmsterm.c new file: src/nmsterm.h new file: src/nmsterm_ncurses.c modified: src/sneakers.c --- Makefile | 8 +- src/nms-ncurses.c | 442 ------------------------------------------ src/nms.c | 255 ++++-------------------- src/nmsterm.c | 229 ++++++++++++++++++++++ src/nmsterm.h | 28 +++ src/nmsterm_ncurses.c | 107 ++++++++++ src/sneakers.c | 1 + 7 files changed, 404 insertions(+), 666 deletions(-) delete mode 100644 src/nms-ncurses.c create mode 100644 src/nmsterm.c create mode 100644 src/nmsterm.h create mode 100644 src/nmsterm_ncurses.c diff --git a/Makefile b/Makefile index fad258c..80e060a 100644 --- a/Makefile +++ b/Makefile @@ -17,20 +17,20 @@ CFLAGS ?= -Wextra -Wall .PHONY: all install uninstall clean -nms: $(OBJ)/nms.o $(OBJ)/main.o | $(BIN) +nms: $(OBJ)/nmsterm.o $(OBJ)/nms.o $(OBJ)/main.o | $(BIN) $(CC) $(CFLAGS) -o $(BIN)/$@ $^ -sneakers: $(OBJ)/nms.o $(OBJ)/sneakers.o | $(BIN) +sneakers: $(OBJ)/nmsterm.o $(OBJ)/nms.o $(OBJ)/sneakers.o | $(BIN) $(CC) $(CFLAGS) -o $(BIN)/$@ $^ all: nms sneakers all-ncurses: nms-ncurses sneakers-ncurses -nms-ncurses: $(OBJ)/nms-ncurses.o $(OBJ)/main.o | $(BIN) +nms-ncurses: $(OBJ)/nmsterm_ncurses.o $(OBJ)/nms.o $(OBJ)/main.o | $(BIN) $(CC) $(CFLAGS) -o $(BIN)/nms $^ -lncurses -sneakers-ncurses: $(OBJ)/nms-ncurses.o $(OBJ)/sneakers.o | $(BIN) +sneakers-ncurses: $(OBJ)/nmsterm_ncurses.o $(OBJ)/nms.o $(OBJ)/sneakers.o | $(BIN) $(CC) $(CFLAGS) -o $(BIN)/sneakers $^ -lncurses $(OBJ)/%.o: $(SRC)/%.c | $(OBJ) diff --git a/src/nms-ncurses.c b/src/nms-ncurses.c deleted file mode 100644 index 82528ed..0000000 --- a/src/nms-ncurses.c +++ /dev/null @@ -1,442 +0,0 @@ -/* - * Copyright (c) 2017 Brian Barto - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the MIT License. See LICENSE for more details. - */ - -/* - * DESCRIPTION - * - * This library encapsulates the functionality required to produce the - * famous data decryption effect from the 1992 hacker movie Sneakers. - */ - -#define _XOPEN_SOURCE 700 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "nms.h" - -// Program settings -#define TYPE_EFFECT_SPEED 4 // miliseconds per char -#define JUMBLE_SECONDS 2 // number of seconds for jumble effect -#define JUMBLE_LOOP_SPEED 35 // miliseconds between each jumble -#define REVEAL_LOOP_SPEED 50 // miliseconds between each reveal loop -#define MASK_CHAR_COUNT 218 // Total characters in maskCharTable[] array. - -// Character attribute structure, linked list. Keeps track of every -// character's attributes required for rendering and decryption effect. -struct charAttr { - char *source; - char *mask; - int width; - int is_space; - int time; - struct charAttr *next; -}; - -// Static function prototypes -static void nms_sleep(int); - -// NMS settings -static int foregroundColor = COLOR_BLUE; // Foreground color setting -static int autoDecrypt = 0; // Auto-decrypt flag -static char *returnOpts = NULL; // Return option setting -static int inputPositionX = -1; // X coordinate for input position -static int inputPositionY = -1; // Y coordinate for input position - -// Character table representing the character set know as CP437 used by -// the original IBM PC - https://en.wikipedia.org/wiki/Code_page_437 -static char *maskCharTable[] = { - "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", "~", - ".", "/", ":", ";", "<", "=", ">", "?", "[", "\\", "]", "_", "{", "}", - "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", - "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", - "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", - "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", - "\xc3\x87", "\xc3\xbc", "\xc3\xa9", "\xc3\xa2", "\xc3\xa4", "\xc3\xa0", - "\xc3\xa5", "\xc3\xa7", "\xc3\xaa", "\xc3\xab", "\xc3\xa8", "\xc3\xaf", - "\xc3\xae", "\xc3\xac", "\xc3\x84", "\xc3\x85", "\xc3\x89", "\xc3\xa6", - "\xc3\x86", "\xc3\xb4", "\xc3\xb6", "\xc3\xb2", "\xc3\xbb", "\xc3\xb9", - "\xc3\xbf", "\xc3\x96", "\xc3\x9c", "\xc2\xa2", "\xc2\xa3", "\xc2\xa5", - "\xc6\x92", "\xc3\xa1", "\xc3\xad", "\xc3\xb3", "\xc3\xba", "\xc3\xb1", - "\xc3\x91", "\xc2\xaa", "\xc2\xba", "\xc2\xbf", "\xc2\xac", "\xc2\xbd", - "\xc2\xbc", "\xc2\xa1", "\xc2\xab", "\xc2\xbb", "\xce\xb1", "\xc3\x9f", - "\xce\x93", "\xcf\x80", "\xce\xa3", "\xcf\x83", "\xc2\xb5", "\xcf\x84", - "\xce\xa6", "\xce\x98", "\xce\xa9", "\xce\xb4", "\xcf\x86", "\xce\xb5", - "\xc2\xb1", "\xc3\xb7", "\xc2\xb0", "\xc2\xb7", "\xc2\xb2", "\xc2\xb6", - "\xe2\x8c\x90", "\xe2\x82\xa7", "\xe2\x96\x91", "\xe2\x96\x92", - "\xe2\x96\x93", "\xe2\x94\x82", "\xe2\x94\xa4", "\xe2\x95\xa1", - "\xe2\x95\xa2", "\xe2\x95\x96", "\xe2\x95\x95", "\xe2\x95\xa3", - "\xe2\x95\x91", "\xe2\x95\x97", "\xe2\x95\x9d", "\xe2\x95\x9c", - "\xe2\x95\x9b", "\xe2\x94\x90", "\xe2\x94\x94", "\xe2\x94\xb4", - "\xe2\x94\xac", "\xe2\x94\x9c", "\xe2\x94\x80", "\xe2\x94\xbc", - "\xe2\x95\x9e", "\xe2\x95\x9f", "\xe2\x95\x9a", "\xe2\x95\x94", - "\xe2\x95\xa9", "\xe2\x95\xa6", "\xe2\x95\xa0", "\xe2\x95\x90", - "\xe2\x95\xac", "\xe2\x95\xa7", "\xe2\x95\xa8", "\xe2\x95\xa4", - "\xe2\x95\xa7", "\xe2\x95\x99", "\xe2\x95\x98", "\xe2\x95\x92", - "\xe2\x95\x93", "\xe2\x95\xab", "\xe2\x95\xaa", "\xe2\x94\x98", - "\xe2\x94\x8c", "\xe2\x96\x88", "\xe2\x96\x84", "\xe2\x96\x8c", - "\xe2\x96\x90", "\xe2\x96\x80", "\xe2\x88\x9e", "\xe2\x88\xa9", - "\xe2\x89\xa1", "\xe2\x89\xa5", "\xe2\x89\xa4", "\xe2\x8c\xa0", - "\xe2\x8c\xa1", "\xe2\x89\x88", "\xe2\x88\x99", "\xe2\x88\x9a", - "\xe2\x81\xbf", "\xe2\x96\xa0" -}; - -/* - * nms_exec() - This function is passed a pointer to a character string - * and displays the contents of the string in a way that mimicks the - * "decrypting text" effect in the 1992 movie Sneakers. It returns the - * last character pressed by the user. - */ -char nms_exec(char *string) { - struct charAttr *list_pointer = NULL; - struct charAttr *list_head = NULL; - struct charAttr *list_temp = NULL; - int i, revealed = 0; - int maxRows, maxCols, curRow = 0, curCol = 0; - char ret = 0; - - // Error if we have an empty string. - if (string == NULL || string[0] == '\0') { - fprintf(stderr, "Error. Empty string.\n"); - return 0; - } - - // Reassociate STDIN to the terminal is needed - if (!isatty(STDIN_FILENO) && !freopen ("/dev/tty", "r", stdin)) { - fprintf(stderr, "Error. Can't associate STDIN with terminal.\n"); - return 0; - } - - // Needed for UTF-8 support - setlocale(LC_ALL, ""); - - // Initialize terminal and settings - initscr(); - cbreak(); - noecho(); - curs_set(0); - move(0, 0); - if (has_colors()) { - start_color(); - init_pair(1, foregroundColor, COLOR_BLACK); - } - - // Get terminal window size - getmaxyx(stdscr, maxRows, maxCols); - - // Seed my random number generator with the current time - srand(time(NULL)); - - // Geting input - for (i = 0; string[i] != '\0'; ++i) { - - // Don't go beyond maxRows - if (curRow >= maxRows - 1) { - break; - } - - // Allocate memory for next list link - if (list_pointer == NULL) { - list_pointer = malloc(sizeof(struct charAttr)); - list_head = list_pointer; - } else { - list_pointer->next = malloc(sizeof(struct charAttr)); - list_pointer = list_pointer->next; - } - - // Get character's byte-length and store character. - if (mblen(&string[i], 4) > 0) { - list_pointer->source = malloc(mblen(&string[i], 4) + 1); - strncpy(list_pointer->source, &string[i], mblen(&string[i], 4)); - list_pointer->source[mblen(&string[i], 4)] = '\0'; - i += (mblen(&string[i], 4) - 1); - } else { - endwin(); - fprintf(stderr, "Unknown character encountered. Quitting.\n"); - return 0; - } - - // Set flag if we have a whitespace character - if (strlen(list_pointer->source) == 1 && isspace(list_pointer->source[0])) - list_pointer->is_space = 1; - else - list_pointer->is_space = 0; - - // Set initial mask chharacter - list_pointer->mask = maskCharTable[rand() % MASK_CHAR_COUNT]; - - // Set reveal time - list_pointer->time = rand() % 5000; - - // Set character column width - wchar_t widec[sizeof(list_pointer->source)] = {}; - mbstowcs(widec, list_pointer->source, sizeof(list_pointer->source)); - list_pointer->width = wcwidth(*widec); - - // Set next node to null - list_pointer->next = NULL; - - // Track row count - if (string[i] == '\n' || (curCol += list_pointer->width) > maxCols) { - curCol = 0; - curRow++; - } - } - - // Print mask characters with 'type effect' - for (list_pointer = list_head; list_pointer != NULL; list_pointer = list_pointer->next) { - - // Print mask character (or space) - if (list_pointer->is_space) { - addstr(list_pointer->source); - continue; - } - - // print mask character - addstr(list_pointer->mask); - if (list_pointer->width == 2) { - addstr(maskCharTable[rand() % MASK_CHAR_COUNT]); - } - - // flush output and sleep - refresh(); - nms_sleep(TYPE_EFFECT_SPEED); - } - - // Flush any input up to this point - flushinp(); - - // If autoDecrypt flag is set, we sleep. Otherwise require user to - // press a key to continue. - if (autoDecrypt) - sleep(1); - else - getch(); - - // Jumble loop - for (i = 0; i < (JUMBLE_SECONDS * 1000) / JUMBLE_LOOP_SPEED; ++i) { - - // Move cursor to home position - move(0, 0); - - // Print new mask for all characters - for (list_pointer = list_head; list_pointer != NULL; list_pointer = list_pointer->next) { - - // Print mask character (or space) - if (list_pointer->is_space) { - addstr(list_pointer->source); - continue; - } - - // print new mask character - addstr(maskCharTable[rand() % MASK_CHAR_COUNT]); - if (list_pointer->width == 2) { - addstr(maskCharTable[rand() % MASK_CHAR_COUNT]); - } - } - - // flush output and sleep - refresh(); - nms_sleep(JUMBLE_LOOP_SPEED); - } - - // Reveal loop - while (!revealed) { - - // Move cursor to home position - move(0, 0); - - // Set revealed flag - revealed = 1; - - for (list_pointer = list_head; list_pointer != NULL; list_pointer = list_pointer->next) { - - // Print mask character (or space) - if (list_pointer->is_space) { - addstr(list_pointer->source); - continue; - } - - // If we still have time before the char is revealed, display the mask - if (list_pointer->time > 0) { - - // Change the mask randomly - if (list_pointer->time < 500) { - if (rand() % 3 == 0) { - list_pointer->mask = maskCharTable[rand() % MASK_CHAR_COUNT]; - } - } else { - if (rand() % 10 == 0) { - list_pointer->mask = maskCharTable[rand() % MASK_CHAR_COUNT]; - } - } - - // Print mask - addstr(list_pointer->mask); - - // Decrement reveal time - list_pointer->time -= REVEAL_LOOP_SPEED; - - // Unset revealed flag - revealed = 0; - } else { - - // Set bold and foreground color for character reveal - attron(A_BOLD); - if (has_colors()) - attron(COLOR_PAIR(1)); - - // print source character - addstr(list_pointer->source); - - // Unset foreground color - if (has_colors()) - attroff(COLOR_PAIR(1)); - attroff(A_BOLD); - } - } - - // flush output and sleep - refresh(); - nms_sleep(REVEAL_LOOP_SPEED); - } - - // Flush any input up to this point - flushinp(); - - // Check if user must select from a set of options - if (returnOpts != NULL && strlen(returnOpts) > 0) { - - // Position cursor if necessary - if (inputPositionY >= 0 && inputPositionX >= 0) { - move(inputPositionY, inputPositionX); - } - - // Turn on cursor - curs_set(1); - refresh(); - - // Get and validate user selection - while (strchr(returnOpts, ret = getch()) == NULL) { - beep(); - } - - } - - // User must press a key to continue when clearSrc is set - // without returnOpts - else { - getch(); - } - - // End curses mode - endwin(); - - // Freeing the list. - list_pointer = list_head; - while (list_pointer != NULL) { - list_temp = list_pointer; - list_pointer = list_pointer->next; - free(list_temp->source); - free(list_temp); - } - - return ret; -} - -/* - * nms_set_foreground_color() sets the foreground color of the unencrypted - * characters as they are revealed to the color indicated by the 'color' - * argument. Valid arguments are "white", "yellow", "magenta", "blue", - * "green", "red", and "cyan". This function will default to blue if - * passed an invalid color. No value is returned. - */ -void nms_set_foreground_color(char *color) { - - if(strcmp("white", color) == 0) - foregroundColor = COLOR_WHITE; - else if(strcmp("yellow", color) == 0) - foregroundColor = COLOR_YELLOW; - else if(strcmp("black", color) == 0) - foregroundColor = COLOR_BLACK; - else if(strcmp("magenta", color) == 0) - foregroundColor = COLOR_MAGENTA; - else if(strcmp("blue", color) == 0) - foregroundColor = COLOR_BLUE; - else if(strcmp("green", color) == 0) - foregroundColor = COLOR_GREEN; - else if(strcmp("red", color) == 0) - foregroundColor = COLOR_RED; - else if(strcmp("cyan", color) == 0) - foregroundColor = COLOR_CYAN; - else - foregroundColor = COLOR_BLUE; -} - -/* - * nms_set_return_opts() takes a character sting and copies it to the - * returnOpts setting used by nms_exec(). - */ -void nms_set_return_opts(char *opts) { - returnOpts = realloc(returnOpts, strlen(opts) + 1); - strcpy(returnOpts, opts); -} - -/* - * nms_set_auto_decrypt() sets the autoDecrypt flag according to the - * true/false value of the 'setting' argument. - */ -void nms_set_auto_decrypt(int setting) { - if (setting) - autoDecrypt = 1; - else - autoDecrypt = 0; -} - -/* - * nms_set_clear_scr() sets the clearScr flag according to the - * true/false value of the 'setting' argument. NOTE: FOR NCURSES THIS - * IS NOT USED. - */ -void nms_set_clear_scr(int setting) { - (void) setting; - return; -} - -/* - * nms_set_input_position() sets the desired coordinate of the cursor in - * the terminal when accepting user input after nms_exec() reveals the - * unencrypted characters. - */ -void nms_set_input_position(int x, int y) { - if (x >= 0 && y >= 0) { - inputPositionX = x; - inputPositionY = y; - } -} - -/* - * nms_sleep() sleeps for the number of miliseconds indicated by 't'. - */ -static void nms_sleep(int t) { - struct timespec ts; - - ts.tv_sec = t / 1000; - ts.tv_nsec = (t % 1000) * 1000000; - - nanosleep(&ts, NULL); -} diff --git a/src/nms.c b/src/nms.c index 2fa15dd..8f76706 100644 --- a/src/nms.c +++ b/src/nms.c @@ -17,15 +17,13 @@ #include #include #include -#include #include -#include #include -#include #include #include #include #include "nms.h" +#include "nmsterm.h" // Color identifiers #define COLOR_BLACK 0 @@ -37,21 +35,6 @@ #define COLOR_CYAN 6 #define COLOR_WHITE 7 -// Macros for VT100 codes -#define CLEAR_SCR() printf("\033[2J") // Clear Screen -#define CURSOR_HOME() printf("\033[H") // Move cursor to home position (0,0) -#define CURSOR_MOVE(x,y) printf("\033[%i;%iH", x, y) // Move cursor to x,y -#define BEEP() printf("\a"); // terminal bell -#define BOLD() printf("\033[1m") // Cursor bold -#define FOREGROUND_COLOR(x) printf("\033[3%im", x) // Set foreground color -#define CLEAR_ATTR() printf("\033[0m") // Clear bold/color attributes -#define SCREEN_SAVE() printf("\033[?47h") // Save screen display -#define SCREEN_RESTORE() printf("\033[?47l") // Restore screen to previously saved state -#define CURSOR_SAVE() printf("\033[s") // Save cursor position -#define CURSOR_RESTORE() printf("\033[u") // Restore cursor position -#define CURSOR_HIDE() printf("\033[?25l") // Hide cursor -#define CURSOR_SHOW() printf("\033[?25h") // Unhide cursor - // Program settings #define TYPE_EFFECT_SPEED 4 // miliseconds per char #define JUMBLE_SECONDS 2 // number of seconds for jumble effect @@ -72,12 +55,6 @@ struct charAttr { // Static function prototypes static void nms_sleep(int); -static int nms_term_rows(void); -static int nms_term_cols(void); -static void nms_set_terminal(int s); -static void nms_clear_input(void); -static char nms_get_char(void); -static int nms_get_cursor_row(void); // NMS settings static int foregroundColor = COLOR_BLUE; // Foreground color setting @@ -153,33 +130,24 @@ char nms_exec(char *string) { return 0; } - // Turn off terminal echo and line buffering - nms_set_terminal(0); - // Needed for UTF-8 support setlocale(LC_ALL, ""); + + // Initialize terminal + origRow = nmsterm_init_terminal(foregroundColor, clearScr); // Get terminal window rows/cols - maxRows = nms_term_rows(); - maxCols = nms_term_cols(); + maxRows = nmsterm_get_rows(); + maxCols = nmsterm_get_cols(); // Seed my random number generator with the current time srand(time(NULL)); - - // Get cursor position if we are not clearing the screen - if (!clearScr) { - origRow = nms_get_cursor_row(); - - // nms_get_cursor_row() may display output in some terminals. So - // we need to reposition the cursor to the start of the row. - CURSOR_MOVE(origRow, origCol); - } // Assign current row/col positions curRow = origRow; curCol = origCol; - // Geting input + // Processing input for (i = 0; string[i] != '\0'; ++i) { // Don't go beyond maxRows @@ -204,7 +172,7 @@ char nms_exec(char *string) { i += (mblen(&string[i], 4) - 1); } else { fprintf(stderr, "Unknown character encountered. Quitting.\n"); - nms_set_terminal(1); + nmsterm_restore_terminal(); return 0; } @@ -239,85 +207,68 @@ char nms_exec(char *string) { } } - // Save terminal state, clear screen, and home/hide the cursor - if (clearScr) { - CURSOR_SAVE(); - SCREEN_SAVE(); - CLEAR_SCR(); - CURSOR_HOME(); - CURSOR_HIDE(); - } - // Print mask characters with 'type effect' for (list_pointer = list_head; list_pointer != NULL; list_pointer = list_pointer->next) { // Print mask character (or space) if (list_pointer->is_space) { - printf("%s", list_pointer->source); + nmsterm_print_string(list_pointer->source); continue; } // print mask character - printf("%s", list_pointer->mask); + nmsterm_print_string(list_pointer->mask); if (list_pointer->width == 2) { - printf("%s", maskCharTable[rand() % MASK_CHAR_COUNT]); + nmsterm_print_string(maskCharTable[rand() % MASK_CHAR_COUNT]); } // flush output and sleep - fflush(stdout); + nmsterm_refresh(); nms_sleep(TYPE_EFFECT_SPEED); } // Flush any input up to this point - nms_clear_input(); + nmsterm_clear_input(); // If autoDecrypt flag is set, we sleep. Otherwise require user to // press a key to continue. if (autoDecrypt) sleep(1); else - nms_get_char(); + nmsterm_get_char(); // Jumble loop for (i = 0; i < (JUMBLE_SECONDS * 1000) / JUMBLE_LOOP_SPEED; ++i) { - // Move cursor to home position - if (clearScr) { - CURSOR_HOME(); - } else { - CURSOR_MOVE(origRow, origCol); - } + // Move cursor to start position + nmsterm_move_cursor(origRow, origCol); // Print new mask for all characters for (list_pointer = list_head; list_pointer != NULL; list_pointer = list_pointer->next) { // Print mask character (or space) if (list_pointer->is_space) { - printf("%s", list_pointer->source); + nmsterm_print_string(list_pointer->source); continue; } // print new mask character - printf("%s", maskCharTable[rand() % MASK_CHAR_COUNT]); + nmsterm_print_string(maskCharTable[rand() % MASK_CHAR_COUNT]); if (list_pointer->width == 2) { - printf("%s", maskCharTable[rand() % MASK_CHAR_COUNT]); + nmsterm_print_string(maskCharTable[rand() % MASK_CHAR_COUNT]); } } // flush output and sleep - fflush(stdout); + nmsterm_refresh(); nms_sleep(JUMBLE_LOOP_SPEED); } // Reveal loop while (!revealed) { - // Move cursor to home position - if (clearScr) { - CURSOR_HOME(); - } else { - CURSOR_MOVE(origRow, origCol); - } + // Move cursor to start position + nmsterm_move_cursor(origRow, origCol); // Set revealed flag revealed = 1; @@ -326,7 +277,7 @@ char nms_exec(char *string) { // Print mask character (or space) if (list_pointer->is_space) { - printf("%s", list_pointer->source); + nmsterm_print_string(list_pointer->source); continue; } @@ -345,7 +296,7 @@ char nms_exec(char *string) { } // Print mask - printf("%s", list_pointer->mask); + nmsterm_print_string(list_pointer->mask); // Decrement reveal time list_pointer->time -= REVEAL_LOOP_SPEED; @@ -354,60 +305,44 @@ char nms_exec(char *string) { revealed = 0; } else { - // Set bold and foreground color for character reveal - BOLD(); - if (colorOn) { - FOREGROUND_COLOR(foregroundColor); - } - // print source character - printf("%s", list_pointer->source); - - // Unset foreground color - CLEAR_ATTR(); + nmsterm_print_reveal_string(list_pointer->source, foregroundColor, colorOn); } } // flush output and sleep - fflush(stdout); + nmsterm_refresh(); nms_sleep(REVEAL_LOOP_SPEED); } // Flush any input up to this point - nms_clear_input(); + nmsterm_clear_input(); // Check if user must select from a set of options if (returnOpts != NULL && strlen(returnOpts) > 0) { // Position cursor if necessary if (inputPositionY >= 0 && inputPositionX >= 0) { - CURSOR_MOVE(inputPositionY, inputPositionX); + nmsterm_move_cursor(inputPositionY, inputPositionX); } - CURSOR_SHOW(); + nmsterm_show_cursor(); // Get and validate user selection - while (strchr(returnOpts, ret = nms_get_char()) == NULL) { - BEEP(); + while (strchr(returnOpts, ret = nmsterm_get_char()) == NULL) { + nmsterm_beep(); } } // User must press a key to continue when clearSrc is set // without returnOpts - else if (clearScr) { - nms_get_char(); + else if (nmsterm_get_clearscr()) { + nmsterm_get_char(); } - // Restore screen and cursor is clearSrc is set - if (clearScr) { - SCREEN_RESTORE(); - CURSOR_SHOW(); - CURSOR_RESTORE(); - } - - // Turn on terminal echo and line buffering - nms_set_terminal(1); + // Restore terminal + nmsterm_restore_terminal(); // Freeing the list. list_pointer = list_head; @@ -515,123 +450,3 @@ static void nms_sleep(int t) { nanosleep(&ts, NULL); } - -/* - * nms_term_rows() gets and returns the number of rows in the current - * terminal window. - */ -static int nms_term_rows(void) { - struct winsize w; - ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); - - return w.ws_row; -} - -/* - * nms_term_cols() gets and returns the number of cols in the current - * terminal window. - */ -static int nms_term_cols(void) { - struct winsize w; - ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); - - return w.ws_col; -} - -/* - * nms_set_terminal() turns off terminal echo and line buffering when - * passed an integer value that evaluates to true. It restores the - * original terminal values when passed an integer value that evaluates - * to false. - */ -static void nms_set_terminal(int s) { - struct termios tp; - static struct termios save; - static int state = 1; - - if (s == 0) { - if (tcgetattr(STDIN_FILENO, &tp) == -1) { - return; - } - - save = tp; - - tp.c_lflag &=(~ICANON & ~ECHO); - - if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tp) == -1) { - return; - } - } else { - if (state == 0 && tcsetattr(STDIN_FILENO, TCSANOW, &save) == -1) - return; - } - - state = s; -} - -/* - * nms_clear_input() clears the input buffer of all characters up to - * the EOF character. - */ -static void nms_clear_input(void) { - int i; - - ioctl(STDIN_FILENO, FIONREAD, &i); - - while (i > 0) { - getchar(); - --i; - } -} - -/* - * nms_get_char() returns the next character in the input buffer. In the - * case of an EOF character, it blocks until the user presses a key. - */ -static char nms_get_char(void) { - struct timespec ts; - int t = 50; - char c; - - ts.tv_sec = t / 1000; - ts.tv_nsec = (t % 1000) * 1000000; - - while ((c = getchar()) == EOF) { - nanosleep(&ts, NULL); - } - - return c; -} - -/* - * nms_get_cursor_row() returns the row position of the cursor as reported - * by the terminal program via the ANSI escape code - */ -static int nms_get_cursor_row(void) { - int i, r = 0; - int row = 0; - char buf[10]; - char *cmd = "\033[6n"; - - memset(buf, 0, sizeof(buf)); - - write(STDOUT_FILENO, cmd, sizeof(cmd)); - - r = read(STDIN_FILENO, buf, sizeof(buf)); - - for (i = 0; i < r; ++i) { - if (buf[i] == 27 || buf[i] == '[') { - continue; - } - - if (buf[i] >= '0' && buf[i] <= '9') { - row = (row * 10) + (buf[i] - '0'); - } - - if (buf[i] == ';' || buf[i] == 'R' || buf[i] == 0) { - break; - } - } - - return row; -} diff --git a/src/nmsterm.c b/src/nmsterm.c new file mode 100644 index 0000000..2398246 --- /dev/null +++ b/src/nmsterm.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2017 Brian Barto + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the MIT License. See LICENSE for more details. + */ + +#include +#include +#include +#include +#include +#include + +// Static settings +static int clearScr = 0; // clearScr flag + +// Macros for VT100 codes +#define CLEAR_SCR() printf("\033[2J") // Clear Screen +#define CURSOR_HOME() printf("\033[H") // Move cursor to home position (0,0) +#define CURSOR_MOVE(y,x) printf("\033[%i;%iH", y, x) // Move cursor to x,y +#define BEEP() printf("\a"); // terminal bell +#define BOLD() printf("\033[1m") // Cursor bold +#define FOREGROUND_COLOR(x) printf("\033[3%im", x) // Set foreground color +#define CLEAR_ATTR() printf("\033[0m") // Clear bold/color attributes +#define SCREEN_SAVE() printf("\033[?47h") // Save screen display +#define SCREEN_RESTORE() printf("\033[?47l") // Restore screen to previously saved state +#define CURSOR_SAVE() printf("\033[s") // Save cursor position +#define CURSOR_RESTORE() printf("\033[u") // Restore cursor position +#define CURSOR_HIDE() printf("\033[?25l") // Hide cursor +#define CURSOR_SHOW() printf("\033[?25h") // Unhide cursor + +// Function prototypes +static void nmsterm_set_terminal(int); +static int nmsterm_get_cursor_row(void); + +// Initialize terminal window +int nmsterm_init_terminal(int foregroundColor, int c) { + (void) foregroundColor; + int origRow = 0; + + // Set clearScr flag + clearScr = c; + + // Turn off line buffering and echo + nmsterm_set_terminal(0); + + // Save terminal state, clear screen, and home/hide the cursor + if (clearScr) { + CURSOR_SAVE(); + SCREEN_SAVE(); + CLEAR_SCR(); + CURSOR_HOME(); + CURSOR_HIDE(); + } else { + + // Get current row position + origRow = nmsterm_get_cursor_row(); + + // nms_get_cursor_row() may display output in some terminals. So + // we need to reposition the cursor to the start of the row. + CURSOR_MOVE(origRow, 0); + } + + // Return cursor row position + return origRow; +} + +void nmsterm_restore_terminal(void) { + + // Restore screen and cursor is clearSrc is set + if (clearScr) { + SCREEN_RESTORE(); + CURSOR_SHOW(); + CURSOR_RESTORE(); + } + + // Turn on line buffering and echo + nmsterm_set_terminal(1); +} + +/* + * nms_term_rows() gets and returns the number of rows in the current + * terminal window. + */ +int nmsterm_get_rows(void) { + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + + return w.ws_row; +} + +/* + * nms_term_cols() gets and returns the number of cols in the current + * terminal window. + */ +int nmsterm_get_cols(void) { + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + + return w.ws_col; +} + +void nmsterm_move_cursor(int y, int x) { + CURSOR_MOVE(y, x); +} + +void nmsterm_print_string(char *s) { + printf("%s", s); +} + +void nmsterm_refresh(void) { + fflush(stdout); +} + +void nmsterm_clear_input(void) { + int i; + + ioctl(STDIN_FILENO, FIONREAD, &i); + + while (i-- > 0) { + getchar(); + } +} + +char nmsterm_get_char(void) { + struct timespec ts; + int t = 50; + char c; + + ts.tv_sec = t / 1000; + ts.tv_nsec = (t % 1000) * 1000000; + + while ((c = getchar()) == EOF) { + nanosleep(&ts, NULL); + } + + return c; +} + +void nmsterm_print_reveal_string(char *s, int foregroundColor, int colorOn) { + + // Set bold and foreground color + BOLD(); + if (colorOn) { + FOREGROUND_COLOR(foregroundColor); + } + + // print string + printf("%s", s); + + // Unset bold and foreground color + CLEAR_ATTR(); +} + +void nmsterm_show_cursor(void) { + CURSOR_SHOW(); +} + +void nmsterm_beep(void) { + BEEP(); +} + +int nmsterm_get_clearscr(void) { + return clearScr; +} + +/* + * nmsterm_set_terminal() turns off terminal echo and line buffering when + * passed an integer value that evaluates to true. It restores the + * original terminal values when passed an integer value that evaluates + * to false. + */ +static void nmsterm_set_terminal(int s) { + struct termios tp; + static struct termios save; + static int state = 1; + + if (s == 0) { + if (tcgetattr(STDIN_FILENO, &tp) == -1) { + return; + } + + save = tp; + + tp.c_lflag &=(~ICANON & ~ECHO); + + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tp) == -1) { + return; + } + } else { + if (state == 0 && tcsetattr(STDIN_FILENO, TCSANOW, &save) == -1) + return; + } + + state = s; +} +/* + * nms_get_cursor_row() returns the row position of the cursor as reported + * by the terminal program via the ANSI escape code + */ +static int nmsterm_get_cursor_row(void) { + int i, r = 0; + int row = 0; + char buf[10]; + char *cmd = "\033[6n"; + + memset(buf, 0, sizeof(buf)); + + write(STDOUT_FILENO, cmd, sizeof(cmd)); + + r = read(STDIN_FILENO, buf, sizeof(buf)); + + for (i = 0; i < r; ++i) { + if (buf[i] == 27 || buf[i] == '[') { + continue; + } + + if (buf[i] >= '0' && buf[i] <= '9') { + row = (row * 10) + (buf[i] - '0'); + } + + if (buf[i] == ';' || buf[i] == 'R' || buf[i] == 0) { + break; + } + } + + return row; +} diff --git a/src/nmsterm.h b/src/nmsterm.h new file mode 100644 index 0000000..61843b2 --- /dev/null +++ b/src/nmsterm.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017 Brian Barto + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the MIT License. See LICENSE for more details. + */ + +#ifndef NMSTERM_H +#define NMSTERM_H 1 + +// Function prototypes +int nmsterm_init_terminal(int, int); +void nmsterm_restore_terminal(void); +int nmsterm_get_rows(void); +int nmsterm_get_cols(void); +int nmsterm_get_cursor_row(void); +void nmsterm_move_cursor(int, int); +void nmsterm_print_string(char *); +void nmsterm_refresh(void); +void nmsterm_clear_input(void); +char nmsterm_get_char(void); +void nmsterm_print_reveal_string(char *, int, int); +void nmsterm_show_cursor(void); +void nmsterm_beep(void); +int nmsterm_get_clearscr(void); + + +#endif diff --git a/src/nmsterm_ncurses.c b/src/nmsterm_ncurses.c new file mode 100644 index 0000000..ca384aa --- /dev/null +++ b/src/nmsterm_ncurses.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017 Brian Barto + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the MIT License. See LICENSE for more details. + */ + +#include + +// Static settings +static int clearScr = 1; // clearScr flag + +// Initialize terminal window +int nmsterm_init_terminal(int foregroundColor, int c) { + (void) c; + + initscr(); + cbreak(); + noecho(); + curs_set(0); + move(0, 0); + if (has_colors()) { + start_color(); + init_pair(1, foregroundColor, COLOR_BLACK); + } + + // Since ncurses always clears the screen, we always return zero. + return 0; +} + +void nmsterm_restore_terminal(void) { + endwin(); +} + +/* + * nms_term_rows() gets and returns the number of rows in the current + * terminal window. + */ +int nmsterm_get_rows(void) { + return getmaxy(stdscr); +} + +/* + * nms_term_cols() gets and returns the number of cols in the current + * terminal window. + */ +int nmsterm_get_cols(void) { + return getmaxx(stdscr); +} + +/* + * nms_get_cursor_row() returns the row position of the cursor + */ +int nmsterm_get_cursor_row(void) { + return getcury(stdscr); +} + +void nmsterm_move_cursor(int y, int x) { + move(y, x); +} + +void nmsterm_print_string(char *s) { + addstr(s); +} + +void nmsterm_refresh(void) { + refresh(); +} + +void nmsterm_clear_input(void) { + flushinp(); +} + +void nmsterm_get_char(void) { + getch(); +} + +void nmsterm_print_reveal_string(char *s, int foregroundColor, int colorOn) { + (void) colorOn; + (void) foregroundColor; + + // Set bold and foreground color for character reveal + attron(A_BOLD); + if (has_colors()) + attron(COLOR_PAIR(1)); + + // print source character + addstr(s); + + // Unset foreground color + if (has_colors()) + attroff(COLOR_PAIR(1)); + attroff(A_BOLD); +} + +void nmsterm_show_cursor(void) { + curs_set(1); + refresh(); +} + +void nmsterm_beep(void) { + beep(); +} + +int nmsterm_get_clearscr(void) { + return clearScr; +} diff --git a/src/sneakers.c b/src/sneakers.c index 58c037b..df9a1a4 100644 --- a/src/sneakers.c +++ b/src/sneakers.c @@ -10,6 +10,7 @@ #include #include #include "nms.h" +#include "nmsterm.h" int main(void) { int termCols, spaces = 0;