libterm.c (4330B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <termios.h> 6 #include <sys/ioctl.h> 7 #include <errno.h> 8 #include <stdarg.h> 9 #include <poll.h> 10 11 #include "libterm.h" 12 13 struct termios termios; 14 term_t term = { NULL, 0 }; 15 16 void term_panic(const char *str) { 17 write(STDOUT_FILENO, "\x1b[2J", 4); 18 write(STDOUT_FILENO, "\x1b[H", 3); 19 write(STDOUT_FILENO, "\x1b[?25h", 6); 20 perror(str); 21 exit(EXIT_FAILURE); 22 } 23 24 void term_disable_raw_mode() { 25 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios) == -1) { 26 term_panic("tcsetattr"); 27 } 28 } 29 30 void term_enable_raw_mode() { 31 if (tcgetattr(STDIN_FILENO, &termios) == -1) { 32 term_panic("tcgetattr"); 33 } 34 atexit(term_disable_raw_mode); 35 struct termios raw = termios; 36 raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); 37 raw.c_oflag &= ~(OPOST); 38 raw.c_cflag |= (CS8); 39 raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); 40 raw.c_cc[VMIN] = 0; 41 raw.c_cc[VTIME] = 1; 42 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) { 43 term_panic("tcsetattr"); 44 } 45 } 46 47 int term_read_cursor_pos(int *rows, int *cols) { 48 char buf[32]; 49 unsigned int i = 0; 50 if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) { 51 return -1; 52 } 53 while (i < sizeof(buf) - 1) { 54 if (read(STDIN_FILENO, &buf[i], 1) != 1) { 55 break; 56 } 57 if (buf[i] == 'R') { 58 break; 59 } 60 i++; 61 } 62 buf[i] = '\0'; 63 if (buf[0] != '\x1b' || buf[1] != '[') { 64 return -1; 65 } 66 if (sscanf(&buf[2], "%d;%d", rows, cols) != 2) { 67 return -1; 68 } 69 return -0; 70 } 71 72 int term_read_window_size(int *rows, int *cols) { 73 struct winsize ws; 74 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { 75 if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12) { 76 return -1; 77 } 78 return term_read_cursor_pos(rows, cols); 79 } 80 else { 81 *cols = ws.ws_col; 82 *rows = ws.ws_row; 83 return 0; 84 } 85 } 86 87 int term_poll_key(int timeout) { 88 struct pollfd fds[1]; 89 fds[0].fd = STDIN_FILENO; 90 fds[0].events = POLLIN | POLLPRI; 91 if (poll(fds, 1, timeout)) { 92 return term_read_key(); 93 } 94 return 0; 95 } 96 97 int term_read_key(void) { 98 int len; 99 char c; 100 while ((len = read(STDIN_FILENO, &c, 1)) != 1) { 101 if (len == -1 && errno != EAGAIN) { 102 term_panic("read"); 103 } 104 } 105 if (c == '\x1b') { 106 char seq[3]; 107 if (read(STDIN_FILENO, &seq[0], 1) != 1) { 108 return '\x1b'; 109 } 110 if (read(STDIN_FILENO, &seq[1], 1) != 1) { 111 return '\x1b'; 112 } 113 if (seq[0] == '[') { 114 if (seq[1] >= '0' && seq[1] <= '9') { 115 if (read(STDIN_FILENO, &seq[2], 1) != 1) { 116 return '\x1b'; 117 } 118 if (seq[2] == '~') { 119 switch (seq[1]) { 120 case '1': 121 return HOME_KEY; 122 case '3': 123 return DEL_KEY; 124 case '4': 125 return END_KEY; 126 case '5': 127 return PAGE_UP; 128 case '6': 129 return PAGE_DOWN; 130 case '7': 131 return HOME_KEY; 132 case '8': 133 return END_KEY; 134 } 135 } 136 } 137 else { 138 switch (seq[1]) { 139 case 'A': 140 return ARROW_UP; 141 case 'B': 142 return ARROW_DOWN; 143 case 'C': 144 return ARROW_RIGHT; 145 case 'D': 146 return ARROW_LEFT; 147 case 'H': 148 return HOME_KEY; 149 case 'F': 150 return END_KEY; 151 } 152 return '\x1b'; 153 } 154 } 155 else if (seq[0] == 'O') { 156 switch (seq[1]) { 157 case 'H': 158 return HOME_KEY; 159 case 'F': 160 return END_KEY; 161 } 162 } 163 } 164 return c; 165 } 166 167 void term_write(char *str) { 168 int str_len = strlen(str); 169 char *new_buffer = realloc(term.buffer, sizeof(char) * (term.len + str_len + 1)); 170 if (new_buffer == NULL) { 171 fprintf(stderr, "Cannot realloc memory!\n"); 172 return; 173 } 174 term.buffer = new_buffer; 175 memcpy(term.buffer + term.len, str, str_len); 176 term.len += str_len; 177 term.buffer[term.len] = '\0'; 178 } 179 180 void term_writef(const char *format, ...) { 181 va_list args; 182 va_start(args, format); 183 char str[1000]; 184 vsnprintf(str, sizeof(str), format, args); 185 term_write(str); 186 va_end(args); 187 } 188 189 void term_flush(void) { 190 if (term.len > 0) { 191 write(STDOUT_FILENO, term.buffer, term.len); 192 free(term.buffer); 193 term.buffer = NULL; 194 term.len = 0; 195 } 196 }