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