#include #include #include #include #include #include #include #include #define SHOW_ALTERNATIVE_SCREEN "\033[?1049h" #define SHOW_NORMAL_SCREEN "\033[?1049l" #define HIDE_CURSOR "\033[?25l" #define SHOW_CURSOR "\033[?25h" #define CLEAR_SCREEN "\033[2J" #define MOVE_CURSOR_HOME "\033[H" #define MOVE_CURSOR_UP "\x1b[1A" #define MOVE_CURSOR_DOWN "\x1b[1B" #define MOVE_CURSOR_RIGHT "\x1b[1C" #define MOVE_CURSOR_LEFT "\x1b[1D" // man console_codes // Directions // A - Up // B - Down // C - Right // D - Left typedef struct { int line; int column; int number_of_lines; int number_of_columns; bool resized; bool should_exit; struct termios original_termios; } State; static State state = {0}; void sigwinch_handler(int signal) { fprintf(stderr, "INFO: received signal \"%s\"\n", strsignal(signal)); state.resized = true; } bool get_terminal_size() { struct winsize ws; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)) { return false; } state.number_of_columns = ws.ws_col; state.number_of_lines = ws.ws_row; return true; } void enable_raw_mode(void) { struct termios t = state.original_termios; if (tcgetattr(STDIN_FILENO, &state.original_termios) == -1) { perror("tcgetattr"); exit(1); } t = state.original_termios; t.c_lflag &= ~(ECHO | ICANON | ISIG); // turn off echo, canonical mode, signals t.c_iflag &= ~(IXON | ICRNL); // disable Ctrl-S/Q and CR->NL t.c_oflag &= ~(OPOST); // disable all output processing t.c_cc[VMIN] = 1; // read() returns after 1 byte t.c_cc[VTIME] = 0; // no timeout if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t) == -1) { perror("tcsetattr"); exit(1); } } int main(void) { // TODO: switch to sigaction signal(SIGWINCH, sigwinch_handler); signal(SIGINT, SIG_IGN); enable_raw_mode(); printf(SHOW_ALTERNATIVE_SCREEN); printf(HIDE_CURSOR); printf(MOVE_CURSOR_HOME); fflush(stdout); get_terminal_size(&state); char c; while (true) { ssize_t n = read(STDIN_FILENO, &c, 1); if (n == -1) continue; if (c == 'q') break; switch (c) { case 'h': printf("%s%s", MOVE_CURSOR_LEFT, MOVE_CURSOR_LEFT); break; case 'j': printf("%s%s", MOVE_CURSOR_DOWN, MOVE_CURSOR_LEFT); break; case 'k': printf("%s%s", MOVE_CURSOR_UP, MOVE_CURSOR_LEFT); break; case 'l': break; } if (c == 'h') printf(MOVE_CURSOR_LEFT); if (c == 'j') printf(MOVE_CURSOR_DOWN); if (c == 'k') printf(MOVE_CURSOR_UP); if (c == 'l') printf(MOVE_CURSOR_RIGHT); printf(CLEAR_SCREEN); printf("\033[43m \033[0m"); fflush(stdout); } tcsetattr(STDIN_FILENO, TCSAFLUSH, &state.original_termios); printf(SHOW_NORMAL_SCREEN); printf(SHOW_CURSOR); fflush(stdout); return 0; }