vis

a vi-like editor based on Plan 9's structural regular expressions

git clone https://9o.is/git/vis.git

ui-terminal-curses.c

(7773B)


      1 /* This file is included from ui-terminal.c */
      2 #include <stdio.h>
      3 #include <curses.h>
      4 
      5 #define UI_TERMKEY_FLAGS (TERMKEY_FLAG_UTF8|TERMKEY_FLAG_NOTERMIOS)
      6 
      7 #define CELL_COLOR_BLACK   COLOR_BLACK
      8 #define CELL_COLOR_RED     COLOR_RED
      9 #define CELL_COLOR_GREEN   COLOR_GREEN
     10 #define CELL_COLOR_YELLOW  COLOR_YELLOW
     11 #define CELL_COLOR_BLUE    COLOR_BLUE
     12 #define CELL_COLOR_MAGENTA COLOR_MAGENTA
     13 #define CELL_COLOR_CYAN    COLOR_CYAN
     14 #define CELL_COLOR_WHITE   COLOR_WHITE
     15 #define CELL_COLOR_DEFAULT (-1)
     16 
     17 #ifndef A_ITALIC
     18 #define A_ITALIC A_NORMAL
     19 #endif
     20 #define CELL_ATTR_NORMAL    A_NORMAL
     21 #define CELL_ATTR_UNDERLINE A_UNDERLINE
     22 #define CELL_ATTR_REVERSE   A_REVERSE
     23 #define CELL_ATTR_BLINK     A_BLINK
     24 #define CELL_ATTR_BOLD      A_BOLD
     25 #define CELL_ATTR_ITALIC    A_ITALIC
     26 #define CELL_ATTR_DIM       A_DIM
     27 
     28 #ifdef NCURSES_VERSION
     29 # ifndef NCURSES_EXT_COLORS
     30 #  define NCURSES_EXT_COLORS 0
     31 # endif
     32 # if !NCURSES_EXT_COLORS
     33 #  define MAX_COLOR_PAIRS MIN(COLOR_PAIRS, 256)
     34 # endif
     35 #endif
     36 #ifndef MAX_COLOR_PAIRS
     37 # define MAX_COLOR_PAIRS COLOR_PAIRS
     38 #endif
     39 
     40 #define MAX_COLOR_CLOBBER 240
     41 
     42 static int change_colors = -1;
     43 static short default_fg = -1;
     44 static short default_bg = -1;
     45 
     46 static inline bool cell_color_equal(CellColor c1, CellColor c2) {
     47 	return c1 == c2;
     48 }
     49 
     50 /* Calculate r,g,b components of one of the standard upper 240 colors */
     51 static void get_6cube_rgb(unsigned int n, int *r, int *g, int *b)
     52 {
     53 	if (n < 16) {
     54 		return;
     55 	} else if (n < 232) {
     56 		n -= 16;
     57 		*r = (n / 36) ? (n / 36) * 40 + 55 : 0;
     58 		*g = ((n / 6) % 6) ? ((n / 6) % 6) * 40 + 55 : 0;
     59 		*b = (n % 6) ? (n % 6) * 40 + 55 : 0;
     60 	} else if (n < 256) {
     61 		n -= 232;
     62 		*r = n * 10 + 8;
     63 		*g = n * 10 + 8;
     64 		*b = n * 10 + 8;
     65 	}
     66 }
     67 
     68 /* Reset color palette to default values using OSC 104 */
     69 static void undo_palette(void)
     70 {
     71 	fputs("\033]104;\a", stderr);
     72 	fflush(stderr);
     73 }
     74 
     75 /* Work out the nearest color from the 256 color set, or perhaps exactly. */
     76 static CellColor color_rgb(Ui *ui, uint8_t r, uint8_t g, uint8_t b)
     77 {
     78 	static short color_clobber_idx = 0;
     79 	static uint32_t clobbering_colors[MAX_COLOR_CLOBBER];
     80 
     81 	if (change_colors == -1)
     82 		change_colors = ui->vis->change_colors && can_change_color() && COLORS >= 256;
     83 	if (change_colors) {
     84 		uint32_t hexrep = (r << 16) | (g << 8) | b;
     85 		for (short i = 0; i < MAX_COLOR_CLOBBER; ++i) {
     86 			if (clobbering_colors[i] == hexrep)
     87 				return i + 16;
     88 			else if (!clobbering_colors[i])
     89 				break;
     90 		}
     91 
     92 		short i = color_clobber_idx;
     93 		clobbering_colors[i] = hexrep;
     94 		init_color(i + 16, (r * 1000) / 0xff, (g * 1000) / 0xff,
     95 		           (b * 1000) / 0xff);
     96 
     97 		/* in the unlikely case a user requests this many colors, reuse old slots */
     98 		if (++color_clobber_idx >= MAX_COLOR_CLOBBER)
     99 			color_clobber_idx = 0;
    100 
    101 		return i + 16;
    102 	}
    103 
    104 	static const unsigned char color_256_to_16[256] = {
    105 		 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
    106 		 0,  4,  4,  4, 12, 12,  2,  6,  4,  4, 12, 12,  2,  2,  6,  4,
    107 		12, 12,  2,  2,  2,  6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
    108 		10, 10, 10, 14,  1,  5,  4,  4, 12, 12,  3,  8,  4,  4, 12, 12,
    109 		 2,  2,  6,  4, 12, 12,  2,  2,  2,  6, 12, 12, 10, 10, 10, 10,
    110 		14, 12, 10, 10, 10, 10, 10, 14,  1,  1,  5,  4, 12, 12,  1,  1,
    111 		 5,  4, 12, 12,  3,  3,  8,  4, 12, 12,  2,  2,  2,  6, 12, 12,
    112 		10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,  1,  1,  1,  5,
    113 		12, 12,  1,  1,  1,  5, 12, 12,  1,  1,  1,  5, 12, 12,  3,  3,
    114 		 3,  7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,
    115 		 9,  9,  9,  9, 13, 12,  9,  9,  9,  9, 13, 12,  9,  9,  9,  9,
    116 		13, 12,  9,  9,  9,  9, 13, 12, 11, 11, 11, 11,  7, 12, 10, 10,
    117 		10, 10, 10, 14,  9,  9,  9,  9,  9, 13,  9,  9,  9,  9,  9, 13,
    118 		 9,  9,  9,  9,  9, 13,  9,  9,  9,  9,  9, 13,  9,  9,  9,  9,
    119 		 9, 13, 11, 11, 11, 11, 11, 15,  0,  0,  0,  0,  0,  0,  8,  8,
    120 		 8,  8,  8,  8,  7,  7,  7,  7,  7,  7, 15, 15, 15, 15, 15, 15
    121 	};
    122 
    123 	int i = 0;
    124 	if ((!r || (r - 55) % 40 == 0) &&
    125 	    (!g || (g - 55) % 40 == 0) &&
    126 	    (!b || (b - 55) % 40 == 0)) {
    127 		i = 16;
    128 		i += r ? ((r - 55) / 40) * 36 : 0;
    129 		i += g ? ((g - 55) / 40) * 6 : 0;
    130 		i += g ? ((b - 55) / 40) : 0;
    131 	} else if (r == g && g == b && (r - 8) % 10 == 0 && r < 239) {
    132 		i = 232 + ((r - 8) / 10);
    133 	} else {
    134 		unsigned lowest = UINT_MAX;
    135 		for (int j = 16; j < 256; ++j) {
    136 			int jr = 0, jg = 0, jb = 0;
    137 			get_6cube_rgb(j, &jr, &jg, &jb);
    138 			int dr = jr - r;
    139 			int dg = jg - g;
    140 			int db = jb - b;
    141 			unsigned int distance = dr * dr + dg * dg + db * db;
    142 			if (distance < lowest) {
    143 				lowest = distance;
    144 				i = j;
    145 			}
    146 		}
    147 	}
    148 
    149 	if (COLORS <= 16)
    150 		return color_256_to_16[i];
    151 	return i;
    152 }
    153 
    154 static CellColor color_terminal(Ui *ui, uint8_t index) {
    155 	return index;
    156 }
    157 
    158 static inline unsigned int color_pair_hash(short fg, short bg) {
    159 	if (fg == CELL_COLOR_DEFAULT)
    160 		fg = COLORS;
    161 	if (bg == CELL_COLOR_DEFAULT)
    162 		bg = COLORS + 1;
    163 	return fg * (COLORS + 2) + bg;
    164 }
    165 
    166 static short color_pair_get(short fg, short bg) {
    167 	static bool has_default_colors;
    168 	static short *color2palette;
    169 	static short color_pairs_max, color_pair_current;
    170 
    171 	if (!color2palette) {
    172 		pair_content(0, &default_fg, &default_bg);
    173 		has_default_colors = (use_default_colors() == OK);
    174 		color_pairs_max = MIN(MAX_COLOR_PAIRS, SHRT_MAX);
    175 		if (COLORS)
    176 			color2palette = calloc((COLORS + 2) * (COLORS + 2), sizeof(short));
    177 	}
    178 
    179 	if (fg >= COLORS)
    180 		fg = default_fg;
    181 	if (bg >= COLORS)
    182 		bg = default_bg;
    183 
    184 	if (!has_default_colors) {
    185 		if (fg == -1)
    186 			fg = default_fg;
    187 		if (bg == -1)
    188 			bg = default_bg;
    189 	}
    190 
    191 	if (!color2palette)
    192 		return 0;
    193 
    194 	unsigned int index = color_pair_hash(fg, bg);
    195 	if (color2palette[index] == 0) {
    196 		short oldfg, oldbg;
    197 		if (++color_pair_current >= color_pairs_max)
    198 			color_pair_current = 1;
    199 		pair_content(color_pair_current, &oldfg, &oldbg);
    200 		unsigned int old_index = color_pair_hash(oldfg, oldbg);
    201 		if (init_pair(color_pair_current, fg, bg) == OK) {
    202 			color2palette[old_index] = 0;
    203 			color2palette[index] = color_pair_current;
    204 		}
    205 	}
    206 
    207 	return color2palette[index];
    208 }
    209 
    210 static inline attr_t style_to_attr(CellStyle *style) {
    211 	return style->attr | COLOR_PAIR(color_pair_get(style->fg, style->bg));
    212 }
    213 
    214 static void ui_term_backend_blit(Ui *tui) {
    215 	int w = tui->width, h = tui->height;
    216 	Cell *cell = tui->cells;
    217 	for (int y = 0; y < h; y++) {
    218 		for (int x = 0; x < w; x++) {
    219 			attrset(style_to_attr(&cell->style));
    220 			mvaddstr(y, x, cell->data);
    221 			cell++;
    222 		}
    223 	}
    224 	wnoutrefresh(stdscr);
    225 	if (tui->doupdate)
    226 		doupdate();
    227 }
    228 
    229 static void ui_term_backend_clear(Ui *tui) {
    230 	clear();
    231 }
    232 
    233 static bool ui_term_backend_resize(Ui *tui, int width, int height) {
    234 	return resizeterm(height, width) == OK &&
    235 	       wresize(stdscr, height, width) == OK;
    236 }
    237 
    238 static void ui_term_backend_save(Ui *tui, bool fscr) {
    239 	curs_set(1);
    240 	if (fscr) {
    241 		def_prog_mode();
    242 		endwin();
    243 	} else {
    244 		reset_shell_mode();
    245 	}
    246 }
    247 
    248 static void ui_term_backend_restore(Ui *tui) {
    249 	reset_prog_mode();
    250 	redrawwin(stdscr);
    251 	curs_set(0);
    252 }
    253 
    254 int ui_terminal_colors(void) {
    255 	return COLORS;
    256 }
    257 
    258 static bool ui_term_backend_init(Ui *tui, char *term) {
    259 	if (!newterm(term, stderr, stdin)) {
    260 		snprintf(tui->info, sizeof(tui->info), "Warning: unknown term `%s'", term);
    261 		if (!newterm(strstr(term, "-256color") ? "xterm-256color" : "xterm", stderr, stdin))
    262 			return false;
    263 	}
    264 	start_color();
    265 	use_default_colors();
    266 	cbreak();
    267 	noecho();
    268 	nonl();
    269 	keypad(stdscr, TRUE);
    270 	meta(stdscr, TRUE);
    271 	curs_set(0);
    272 	return true;
    273 }
    274 
    275 static bool ui_backend_init(Ui *ui) {
    276 	return true;
    277 }
    278 
    279 void ui_terminal_resume(Ui *term) { }
    280 
    281 static void ui_term_backend_suspend(Ui *term) {
    282 	if (change_colors == 1)
    283 		undo_palette();
    284 }
    285 
    286 static void ui_term_backend_free(Ui *term) {
    287 	ui_term_backend_suspend(term);
    288 	endwin();
    289 }
    290 
    291 static bool is_default_color(CellColor c) {
    292 	return c == CELL_COLOR_DEFAULT;
    293 }
    294 
    295 static void cursor_visible(bool visible) {
    296 	curs_set(visible);
    297 }