edo

Experimental text editor.
Log | Files | Refs | LICENSE

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 };