edo

Experimental text editor.
Log | Files | Refs | LICENSE

tui.c (4713B)


      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 die(const char *fmt, ...);
     31 
     32 /* function declarations */
     33 void ab_free(Abuf *ab);
     34 void ab_ensure_cap(Abuf *ab, size_t addlen);
     35 void ab_write(Abuf *ab, const char *s, size_t len);
     36 int ab_printf(Abuf *ab, const char *fmt, ...);
     37 void ab_flush(Abuf *ab);
     38 void tui_frame_start(void);
     39 void tui_frame_flush(void);
     40 int tui_text_width(char *s, int len);
     41 void tui_get_window_size(int *rows, int *cols);
     42 void tui_exit(void);
     43 void tui_move_cursor(int x, int y);
     44 void tui_draw_line(int c, int r, char *txt, int len);
     45 void tui_draw_symbol(int r, int c, Symbol sym);
     46 void tui_init(void);
     47 
     48 /* function implementations */
     49 void
     50 ab_free(Abuf *ab) {
     51 	if(!ab->buf)
     52 		return;
     53 	free(ab->buf);
     54 	ab->buf = NULL;
     55 	ab->len = 0;
     56 	ab->cap = 0;
     57 }
     58 
     59 void
     60 ab_ensure_cap(Abuf *ab, size_t addlen) {
     61 	size_t newlen = ab->len + addlen;
     62 
     63 	if(newlen <= ab->cap)
     64 		return;
     65 	while(newlen > ab->cap)
     66 		ab->cap = ab->cap ? ab->cap * 2 : 8;
     67 	/* TODO: panic and save the dump the data before exit */
     68 	if(!(ab->buf = realloc(ab->buf, ab->cap)))
     69 		die("Cannot reallocate memory.");
     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) {
    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 - w % tabstop;
    124 		else
    125 			++w;
    126 	}
    127 	return w;
    128 }
    129 
    130 int
    131 tui_text_index_at(char *str, int target_x) {
    132 	int tabstop = 8;
    133 	int x = 0, i = 0, w;
    134 
    135 	while(str[i]) {
    136 		w = (str[i] == '\t') ? tabstop : 1;
    137 		if (x + w > target_x)
    138 			return i;
    139 		x += w;
    140 		i++;
    141 	}
    142 	return i;
    143 }
    144 
    145 void
    146 tui_get_window_size(int *rows, int *cols) {
    147 	*rows = ws.ws_row;
    148 	*cols = ws.ws_col;
    149 }
    150 
    151 void
    152 tui_exit(void) {
    153 	tcsetattr(0, TCSANOW, &origti);
    154 }
    155 
    156 void
    157 tui_move_cursor(int c, int r) {
    158 	int x = c + 1, y = r + 1; /* TERM coords are 1-based */
    159 	ab_printf(&frame, CURPOS, y, x);
    160 }
    161 
    162 void
    163 tui_draw_line(int c, int r, char *txt, int len) {
    164 	if(c >= ws.ws_col || r >= ws.ws_row) return;
    165 
    166 	int x = c;
    167 	int tabstop = 8;
    168 	int i = 0;
    169 	int cw, clen;
    170 
    171 	tui_move_cursor(x < 0 ? 0 : x, r);
    172 	while(i < len) {
    173 		cw = (txt[i] == '\t' ? tabstop : 1);
    174 
    175 		if(x + cw <= 0)
    176 			continue;
    177 
    178 		if(txt[i] == '\t') {
    179 			int spaces = tabstop;
    180 			clen = 1;
    181 
    182 			if(x < 0) spaces += x;
    183 			if(x + cw > ws.ws_col) spaces -= (x + cw - ws.ws_col);
    184 			while(spaces--) ab_write(&frame, " ", 1);
    185 		} else {
    186 			if(x + cw > ws.ws_col) break;
    187 			clen = 1;
    188 			ab_write(&frame, txt + i, clen);
    189 		}
    190 
    191 		x += cw;
    192 		i += clen;
    193 	}
    194 	ab_write(&frame, CLEARRIGHT, strlen(CLEARRIGHT));
    195 }
    196 
    197 void
    198 tui_draw_symbol(int c, int r, Symbol sym) {
    199 	int symch;
    200 
    201 	switch(sym) {
    202 	case SYM_EMPTYLINE: symch = '~'; break;
    203 	default: symch = '?'; break;
    204 	}
    205 
    206 	tui_move_cursor(c, r);
    207 	ab_printf(&frame, "%c" CLEARRIGHT, symch);
    208 }
    209 
    210 void
    211 tui_init(void) {
    212 	struct termios ti;
    213 
    214 	setlocale(LC_CTYPE, "");
    215 	tcgetattr(0, &origti);
    216 	cfmakeraw(&ti);
    217 	ti.c_iflag |= ICRNL;
    218 	ti.c_cc[VMIN] = 1;
    219 	ti.c_cc[VTIME] = 0;
    220 	tcsetattr(0, TCSAFLUSH, &ti);
    221 	setbuf(stdout, NULL);
    222 	ioctl(0, TIOCGWINSZ, &ws);
    223 }
    224 
    225 int
    226 tui_read_byte(void) {
    227 	char c;
    228 
    229 	if (read(STDIN_FILENO, &c, 1) == 1)
    230 		return c;
    231 	return -1;
    232 }
    233 
    234 Event
    235 tui_next_event(void) {
    236 	Event ev;
    237 	int c = tui_read_byte();
    238 
    239 	if(c == 0x1B) {
    240 		ev.type = EV_UKN;
    241 		return ev;
    242 	}
    243 
    244 	ev.type = EV_KEY;
    245 	ev.key = c;
    246 	return ev;
    247 }
    248 
    249 UI ui_tui = {
    250 	.name = "TUI",
    251 	.init = tui_init,
    252 	.exit = tui_exit,
    253 	.frame_start = tui_frame_start,
    254 	.frame_flush = tui_frame_flush,
    255 	.text_width = tui_text_width,
    256 	.text_index_at = tui_text_index_at,
    257 	.move_cursor = tui_move_cursor,
    258 	.draw_line = tui_draw_line,
    259 	.draw_symbol = tui_draw_symbol,
    260 	.get_window_size = tui_get_window_size,
    261 	.next_event = tui_next_event
    262 };