tui.c (4403B)
1 #include "ui.h" 2 3 #include <assert.h> 4 #include <stdarg.h> 5 #include <stdio.h> 6 #include <locale.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <sys/ioctl.h> 10 #include <termios.h> 11 #include <unistd.h> 12 13 #define CURPOS "\33[%d;%dH" 14 //#define CLEARLEFT "\33[1K" 15 #define CLEARRIGHT "\33[0K" 16 #define CURHIDE "\33[?25l" 17 #define CURSHOW "\33[?25h" 18 19 typedef struct { 20 char *buf; 21 int len; 22 int cap; 23 } Abuf; 24 25 struct termios origti; 26 struct winsize ws; 27 Abuf frame; 28 29 extern void *ecalloc(size_t nmemb, size_t size); 30 extern void *erealloc(void *p, size_t size); 31 extern void die(const char *fmt, ...); 32 extern char *cell_get_text(Cell *cell, char *pool_base); 33 34 /* function declarations */ 35 void ab_free(Abuf *ab); 36 void ab_ensure_cap(Abuf *ab, size_t addlen); 37 void ab_write(Abuf *ab, const char *s, size_t len); 38 int ab_printf(Abuf *ab, const char *fmt, ...); 39 void ab_flush(Abuf *ab); 40 void tui_frame_start(void); 41 void tui_frame_flush(void); 42 int tui_text_width(char *s, int len, int x); 43 void tui_get_window_size(int *rows, int *cols); 44 void tui_exit(void); 45 void tui_move_cursor(int x, int y); 46 void tui_draw_line(UI *ui, int x, int y, Cell *cells, int screen_cols); 47 void tui_draw_symbol(int r, int c, Symbol sym); 48 void tui_init(void); 49 50 /* function implementations */ 51 void 52 ab_free(Abuf *ab) { 53 if(!ab->buf) 54 return; 55 free(ab->buf); 56 ab->buf = NULL; 57 ab->len = 0; 58 ab->cap = 0; 59 } 60 61 void 62 ab_ensure_cap(Abuf *ab, size_t addlen) { 63 size_t newlen = ab->len + addlen; 64 65 if(newlen <= ab->cap) 66 return; 67 while(newlen > ab->cap) 68 ab->cap = ab->cap ? ab->cap * 2 : 8; 69 ab->buf = erealloc(ab->buf, ab->cap); 70 } 71 72 void 73 ab_write(Abuf *ab, const char *s, size_t len) { 74 ab_ensure_cap(&frame, len); 75 memcpy(ab->buf + ab->len, s, len); 76 ab->len += len; 77 } 78 79 int 80 ab_printf(Abuf *ab, const char *fmt, ...) { 81 va_list ap; 82 int len; 83 84 va_start(ap, fmt); 85 len = vsnprintf(NULL, 0, fmt, ap); 86 assert(len >= 0); 87 va_end(ap); 88 89 ab_ensure_cap(ab, len + 1); 90 91 va_start(ap, fmt); 92 vsnprintf(ab->buf + ab->len, len + 1, fmt, ap); 93 va_end(ap); 94 95 ab->len += len; 96 return len; 97 } 98 99 void 100 ab_flush(Abuf *ab) { 101 write(STDOUT_FILENO, ab->buf, ab->len); 102 ab_free(ab); 103 } 104 105 void 106 tui_frame_start(void) { 107 ab_printf(&frame, CURHIDE); 108 } 109 110 void 111 tui_frame_flush(void) { 112 ab_printf(&frame, CURSHOW); 113 ab_flush(&frame); 114 } 115 116 int 117 tui_text_width(char *s, int len, int x) { 118 int tabstop = 8; 119 int w = 0, i; 120 121 for(i = 0; i < len; i++) { 122 if(s[i] == '\t') 123 w += tabstop - x % tabstop; 124 else 125 ++w; 126 x += w; 127 } 128 return w; 129 } 130 131 void 132 tui_get_window_size(int *rows, int *cols) { 133 *rows = ws.ws_row; 134 *cols = ws.ws_col; 135 } 136 137 void 138 tui_exit(void) { 139 tcsetattr(0, TCSANOW, &origti); 140 printf(CURPOS CLEARRIGHT, ws.ws_row, 0); 141 } 142 143 void 144 tui_move_cursor(int c, int r) { 145 int x = c + 1, y = r + 1; /* TERM coords are 1-based */ 146 ab_printf(&frame, CURPOS, y, x); 147 } 148 149 void 150 tui_draw_line(UI *ui, int x, int y, Cell *cells, int count) { 151 assert(x < ws.ws_col && y < ws.ws_row); 152 int w = 0, i; 153 char *txt; 154 155 tui_move_cursor(x, y); 156 for(i = 0; i < count; i++) { 157 w += cells[i].width; 158 txt = cell_get_text(&cells[i], ui->pool.data); 159 160 /* TODO: temp code for testing, we'll se how to deal with this later */ 161 if(txt[0] == '\t') 162 ab_printf(&frame, "%*s", cells[i].width, " "); 163 else 164 ab_write(&frame, txt, cells[i].len); 165 } 166 ab_write(&frame, CLEARRIGHT, strlen(CLEARRIGHT)); 167 } 168 169 void 170 tui_draw_symbol(int c, int r, Symbol sym) { 171 int symch; 172 173 switch(sym) { 174 case SYM_EMPTYLINE: symch = '~'; break; 175 default: symch = '?'; break; 176 } 177 178 tui_move_cursor(c, r); 179 ab_printf(&frame, "%c" CLEARRIGHT, symch); 180 } 181 182 void 183 tui_init(void) { 184 struct termios ti; 185 186 setlocale(LC_CTYPE, ""); 187 tcgetattr(0, &origti); 188 cfmakeraw(&ti); 189 ti.c_iflag |= ICRNL; 190 ti.c_cc[VMIN] = 1; 191 ti.c_cc[VTIME] = 0; 192 tcsetattr(0, TCSAFLUSH, &ti); 193 setbuf(stdout, NULL); 194 ioctl(0, TIOCGWINSZ, &ws); 195 } 196 197 int 198 tui_read_byte(void) { 199 char c; 200 201 if (read(STDIN_FILENO, &c, 1) == 1) 202 return c; 203 return -1; 204 } 205 206 Event 207 tui_next_event(void) { 208 Event ev; 209 int c = tui_read_byte(); 210 211 if(c == 0x1B) { 212 ev.type = EV_UKN; 213 return ev; 214 } 215 216 ev.type = EV_KEY; 217 ev.key = c; 218 return ev; 219 } 220 221 UI ui_tui = { 222 .name = "TUI", 223 .init = tui_init, 224 .exit = tui_exit, 225 .frame_start = tui_frame_start, 226 .frame_flush = tui_frame_flush, 227 .text_width = tui_text_width, 228 .move_cursor = tui_move_cursor, 229 .draw_line = tui_draw_line, 230 .draw_symbol = tui_draw_symbol, 231 .get_window_size = tui_get_window_size, 232 .next_event = tui_next_event 233 };