1 #include "vterm_internal.h"
2
3 #include <stdio.h>
4
5 /**
6 * Structure used to store RGB triples without the additional metadata stored in
7 * VTermColor.
8 */
9 typedef struct {
10 uint8_t red, green, blue;
11 } VTermRGB;
12
13 static const VTermRGB ansi_colors[] = {
14 /* R G B */
15 { 0, 0, 0 }, // black
16 { 224, 0, 0 }, // red
17 { 0, 224, 0 }, // green
18 { 224, 224, 0 }, // yellow
19 { 0, 0, 224 }, // blue
20 { 224, 0, 224 }, // magenta
21 { 0, 224, 224 }, // cyan
22 { 224, 224, 224 }, // white == light grey
23
24 // high intensity
25 { 128, 128, 128 }, // black
26 { 255, 64, 64 }, // red
27 { 64, 255, 64 }, // green
28 { 255, 255, 64 }, // yellow
29 { 64, 64, 255 }, // blue
30 { 255, 64, 255 }, // magenta
31 { 64, 255, 255 }, // cyan
32 { 255, 255, 255 }, // white for real
33 };
34
35 static int ramp6[] = {
36 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF,
37 };
38
39 static int ramp24[] = {
40 0x00, 0x0B, 0x16, 0x21, 0x2C, 0x37, 0x42, 0x4D, 0x58, 0x63, 0x6E, 0x79,
41 0x85, 0x90, 0x9B, 0xA6, 0xB1, 0xBC, 0xC7, 0xD2, 0xDD, 0xE8, 0xF3, 0xFF,
42 };
43
lookup_default_colour_ansi(long idx,VTermColor * col)44 static void lookup_default_colour_ansi(long idx, VTermColor *col)
45 {
46 if (idx >= 0 && idx < 16) {
47 vterm_color_rgb(
48 col,
49 ansi_colors[idx].red, ansi_colors[idx].green, ansi_colors[idx].blue);
50 }
51 }
52
lookup_colour_ansi(const VTermState * state,long index,VTermColor * col)53 static bool lookup_colour_ansi(const VTermState *state, long index, VTermColor *col)
54 {
55 if(index >= 0 && index < 16) {
56 *col = state->colors[index];
57 return true;
58 }
59
60 return false;
61 }
62
lookup_colour_palette(const VTermState * state,long index,VTermColor * col)63 static bool lookup_colour_palette(const VTermState *state, long index, VTermColor *col)
64 {
65 if(index >= 0 && index < 16) {
66 // Normal 8 colours or high intensity - parse as palette 0
67 return lookup_colour_ansi(state, index, col);
68 }
69 else if(index >= 16 && index < 232) {
70 // 216-colour cube
71 index -= 16;
72
73 vterm_color_rgb(col, ramp6[index/6/6 % 6],
74 ramp6[index/6 % 6],
75 ramp6[index % 6]);
76
77 return true;
78 }
79 else if(index >= 232 && index < 256) {
80 // 24 greyscales
81 index -= 232;
82
83 vterm_color_rgb(col, ramp24[index], ramp24[index], ramp24[index]);
84
85 return true;
86 }
87
88 return false;
89 }
90
lookup_colour(const VTermState * state,int palette,const long args[],int argcount,VTermColor * col)91 static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount, VTermColor *col)
92 {
93 switch(palette) {
94 case 2: // RGB mode - 3 args contain colour values directly
95 if(argcount < 3)
96 return argcount;
97
98 vterm_color_rgb(col, CSI_ARG(args[0]), CSI_ARG(args[1]), CSI_ARG(args[2]));
99
100 return 3;
101
102 case 5: // XTerm 256-colour mode
103 if (!argcount || CSI_ARG_IS_MISSING(args[0])) {
104 return argcount ? 1 : 0;
105 }
106
107 vterm_color_indexed(col, args[0]);
108
109 return argcount ? 1 : 0;
110
111 default:
112 DEBUG_LOG("Unrecognised colour palette %d\n", palette);
113 return 0;
114 }
115 }
116
117 // Some conveniences
118
setpenattr(VTermState * state,VTermAttr attr,VTermValueType type,VTermValue * val)119 static void setpenattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val)
120 {
121 #ifdef DEBUG
122 if(type != vterm_get_attr_type(attr)) {
123 DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n",
124 attr, vterm_get_attr_type(attr), type);
125 return;
126 }
127 #endif
128 if(state->callbacks && state->callbacks->setpenattr)
129 (*state->callbacks->setpenattr)(attr, val, state->cbdata);
130 }
131
setpenattr_bool(VTermState * state,VTermAttr attr,int boolean)132 static void setpenattr_bool(VTermState *state, VTermAttr attr, int boolean)
133 {
134 VTermValue val = { .boolean = boolean };
135 setpenattr(state, attr, VTERM_VALUETYPE_BOOL, &val);
136 }
137
setpenattr_int(VTermState * state,VTermAttr attr,int number)138 static void setpenattr_int(VTermState *state, VTermAttr attr, int number)
139 {
140 VTermValue val = { .number = number };
141 setpenattr(state, attr, VTERM_VALUETYPE_INT, &val);
142 }
143
setpenattr_col(VTermState * state,VTermAttr attr,VTermColor color)144 static void setpenattr_col(VTermState *state, VTermAttr attr, VTermColor color)
145 {
146 VTermValue val = { .color = color };
147 setpenattr(state, attr, VTERM_VALUETYPE_COLOR, &val);
148 }
149
set_pen_col_ansi(VTermState * state,VTermAttr attr,long col)150 static void set_pen_col_ansi(VTermState *state, VTermAttr attr, long col)
151 {
152 VTermColor *colp = (attr == VTERM_ATTR_BACKGROUND) ? &state->pen.bg : &state->pen.fg;
153
154 vterm_color_indexed(colp, col);
155
156 setpenattr_col(state, attr, *colp);
157 }
158
vterm_state_newpen(VTermState * state)159 INTERNAL void vterm_state_newpen(VTermState *state)
160 {
161 // 90% grey so that pure white is brighter
162 vterm_color_rgb(&state->default_fg, 240, 240, 240);
163 vterm_color_rgb(&state->default_bg, 0, 0, 0);
164 vterm_state_set_default_colors(state, &state->default_fg, &state->default_bg);
165
166 for(int col = 0; col < 16; col++)
167 lookup_default_colour_ansi(col, &state->colors[col]);
168 }
169
vterm_state_resetpen(VTermState * state)170 INTERNAL void vterm_state_resetpen(VTermState *state)
171 {
172 state->pen.bold = 0; setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
173 state->pen.underline = 0; setpenattr_int( state, VTERM_ATTR_UNDERLINE, 0);
174 state->pen.italic = 0; setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
175 state->pen.blink = 0; setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
176 state->pen.reverse = 0; setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
177 state->pen.strike = 0; setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
178 state->pen.font = 0; setpenattr_int( state, VTERM_ATTR_FONT, 0);
179
180 state->pen.fg = state->default_fg; setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg);
181 state->pen.bg = state->default_bg; setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
182 }
183
vterm_state_savepen(VTermState * state,int save)184 INTERNAL void vterm_state_savepen(VTermState *state, int save)
185 {
186 if(save) {
187 state->saved.pen = state->pen;
188 }
189 else {
190 state->pen = state->saved.pen;
191
192 setpenattr_bool(state, VTERM_ATTR_BOLD, state->pen.bold);
193 setpenattr_int( state, VTERM_ATTR_UNDERLINE, state->pen.underline);
194 setpenattr_bool(state, VTERM_ATTR_ITALIC, state->pen.italic);
195 setpenattr_bool(state, VTERM_ATTR_BLINK, state->pen.blink);
196 setpenattr_bool(state, VTERM_ATTR_REVERSE, state->pen.reverse);
197 setpenattr_bool(state, VTERM_ATTR_STRIKE, state->pen.strike);
198 setpenattr_int( state, VTERM_ATTR_FONT, state->pen.font);
199 setpenattr_col( state, VTERM_ATTR_FOREGROUND, state->pen.fg);
200 setpenattr_col( state, VTERM_ATTR_BACKGROUND, state->pen.bg);
201 }
202 }
203
vterm_color_is_equal(const VTermColor * a,const VTermColor * b)204 int vterm_color_is_equal(const VTermColor *a, const VTermColor *b)
205 {
206 /* First make sure that the two colours are of the same type (RGB/Indexed) */
207 if (a->type != b->type) {
208 return false;
209 }
210
211 /* Depending on the type inspect the corresponding members */
212 if (VTERM_COLOR_IS_INDEXED(a)) {
213 return a->indexed.idx == b->indexed.idx;
214 }
215 else if (VTERM_COLOR_IS_RGB(a)) {
216 return (a->rgb.red == b->rgb.red)
217 && (a->rgb.green == b->rgb.green)
218 && (a->rgb.blue == b->rgb.blue);
219 }
220
221 return 0;
222 }
223
vterm_state_get_default_colors(const VTermState * state,VTermColor * default_fg,VTermColor * default_bg)224 void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg)
225 {
226 *default_fg = state->default_fg;
227 *default_bg = state->default_bg;
228 }
229
vterm_state_get_palette_color(const VTermState * state,int index,VTermColor * col)230 void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col)
231 {
232 lookup_colour_palette(state, index, col);
233 }
234
vterm_state_set_default_colors(VTermState * state,const VTermColor * default_fg,const VTermColor * default_bg)235 void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg)
236 {
237 /* Copy the given colors */
238 state->default_fg = *default_fg;
239 state->default_bg = *default_bg;
240
241 /* Make sure the correct type flags are set */
242 state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK)
243 | VTERM_COLOR_DEFAULT_FG;
244 state->default_bg.type = (state->default_bg.type & ~VTERM_COLOR_DEFAULT_MASK)
245 | VTERM_COLOR_DEFAULT_BG;
246 }
247
vterm_state_set_palette_color(VTermState * state,int index,const VTermColor * col)248 void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col)
249 {
250 if(index >= 0 && index < 16)
251 state->colors[index] = *col;
252 }
253
vterm_state_convert_color_to_rgb(const VTermState * state,VTermColor * col)254 void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col)
255 {
256 if (VTERM_COLOR_IS_INDEXED(col)) { /* Convert indexed colors to RGB */
257 lookup_colour_palette(state, col->indexed.idx, col);
258 }
259 col->type &= VTERM_COLOR_TYPE_MASK; /* Reset any metadata but the type */
260 }
261
vterm_state_set_bold_highbright(VTermState * state,int bold_is_highbright)262 void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright)
263 {
264 state->bold_is_highbright = bold_is_highbright;
265 }
266
vterm_state_setpen(VTermState * state,const long args[],int argcount)267 INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argcount)
268 {
269 // SGR - ECMA-48 8.3.117
270
271 int argi = 0;
272 int value;
273
274 while(argi < argcount) {
275 // This logic is easier to do 'done' backwards; set it true, and make it
276 // false again in the 'default' case
277 int done = 1;
278
279 long arg;
280 switch(arg = CSI_ARG(args[argi])) {
281 case CSI_ARG_MISSING:
282 case 0: // Reset
283 vterm_state_resetpen(state);
284 break;
285
286 case 1: { // Bold on
287 const VTermColor *fg = &state->pen.fg;
288 state->pen.bold = 1;
289 setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
290 if(!VTERM_COLOR_IS_DEFAULT_FG(fg) && VTERM_COLOR_IS_INDEXED(fg) && fg->indexed.idx < 8 && state->bold_is_highbright)
291 set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, fg->indexed.idx + (state->pen.bold ? 8 : 0));
292 break;
293 }
294
295 case 3: // Italic on
296 state->pen.italic = 1;
297 setpenattr_bool(state, VTERM_ATTR_ITALIC, 1);
298 break;
299
300 case 4: // Underline single
301 state->pen.underline = 1;
302 setpenattr_int(state, VTERM_ATTR_UNDERLINE, 1);
303 break;
304
305 case 5: // Blink
306 state->pen.blink = 1;
307 setpenattr_bool(state, VTERM_ATTR_BLINK, 1);
308 break;
309
310 case 7: // Reverse on
311 state->pen.reverse = 1;
312 setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
313 break;
314
315 case 9: // Strikethrough on
316 state->pen.strike = 1;
317 setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
318 break;
319
320 case 10: case 11: case 12: case 13: case 14:
321 case 15: case 16: case 17: case 18: case 19: // Select font
322 state->pen.font = CSI_ARG(args[argi]) - 10;
323 setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
324 break;
325
326 case 21: // Underline double
327 state->pen.underline = 2;
328 setpenattr_int(state, VTERM_ATTR_UNDERLINE, 2);
329 break;
330
331 case 22: // Bold off
332 state->pen.bold = 0;
333 setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
334 break;
335
336 case 23: // Italic and Gothic (currently unsupported) off
337 state->pen.italic = 0;
338 setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
339 break;
340
341 case 24: // Underline off
342 state->pen.underline = 0;
343 setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
344 break;
345
346 case 25: // Blink off
347 state->pen.blink = 0;
348 setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
349 break;
350
351 case 27: // Reverse off
352 state->pen.reverse = 0;
353 setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
354 break;
355
356 case 29: // Strikethrough off
357 state->pen.strike = 0;
358 setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
359 break;
360
361 case 30: case 31: case 32: case 33:
362 case 34: case 35: case 36: case 37: // Foreground colour palette
363 value = CSI_ARG(args[argi]) - 30;
364 if(state->pen.bold && state->bold_is_highbright)
365 value += 8;
366 set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
367 break;
368
369 case 38: // Foreground colour alternative palette
370 if(argcount - argi < 1)
371 return;
372 argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.fg);
373 setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
374 break;
375
376 case 39: // Foreground colour default
377 state->pen.fg = state->default_fg;
378 setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
379 break;
380
381 case 40: case 41: case 42: case 43:
382 case 44: case 45: case 46: case 47: // Background colour palette
383 value = CSI_ARG(args[argi]) - 40;
384 set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
385 break;
386
387 case 48: // Background colour alternative palette
388 if(argcount - argi < 1)
389 return;
390 argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.bg);
391 setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
392 break;
393
394 case 49: // Default background
395 state->pen.bg = state->default_bg;
396 setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
397 break;
398
399 case 90: case 91: case 92: case 93:
400 case 94: case 95: case 96: case 97: // Foreground colour high-intensity palette
401 value = CSI_ARG(args[argi]) - 90 + 8;
402 set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
403 break;
404
405 case 100: case 101: case 102: case 103:
406 case 104: case 105: case 106: case 107: // Background colour high-intensity palette
407 value = CSI_ARG(args[argi]) - 100 + 8;
408 set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
409 break;
410
411 default:
412 done = 0;
413 break;
414 }
415
416 if(!done)
417 DEBUG_LOG("libvterm: Unhandled CSI SGR %lu\n", arg);
418
419 while(CSI_ARG_HAS_MORE(args[argi++]));
420 }
421 }
422
vterm_state_getpen_color(const VTermColor * col,int argi,long args[],int fg)423 static int vterm_state_getpen_color(const VTermColor *col, int argi, long args[], int fg)
424 {
425 /* Do nothing if the given color is the default color */
426 if (( fg && VTERM_COLOR_IS_DEFAULT_FG(col)) ||
427 (!fg && VTERM_COLOR_IS_DEFAULT_BG(col))) {
428 return argi;
429 }
430
431 /* Decide whether to send an indexed color or an RGB color */
432 if (VTERM_COLOR_IS_INDEXED(col)) {
433 const uint8_t idx = col->indexed.idx;
434 if (idx < 8) {
435 args[argi++] = (idx + (fg ? 30 : 40));
436 }
437 else if (idx < 16) {
438 args[argi++] = (idx - 8 + (fg ? 90 : 100));
439 }
440 else {
441 args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
442 args[argi++] = CSI_ARG_FLAG_MORE | 5;
443 args[argi++] = idx;
444 }
445 }
446 else if (VTERM_COLOR_IS_RGB(col)) {
447 args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
448 args[argi++] = CSI_ARG_FLAG_MORE | 2;
449 args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.red;
450 args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.green;
451 args[argi++] = col->rgb.blue;
452 }
453 return argi;
454 }
455
vterm_state_getpen(VTermState * state,long args[],int argcount)456 INTERNAL int vterm_state_getpen(VTermState *state, long args[], int argcount)
457 {
458 int argi = 0;
459
460 if(state->pen.bold)
461 args[argi++] = 1;
462
463 if(state->pen.italic)
464 args[argi++] = 3;
465
466 if(state->pen.underline == 1)
467 args[argi++] = 4;
468
469 if(state->pen.blink)
470 args[argi++] = 5;
471
472 if(state->pen.reverse)
473 args[argi++] = 7;
474
475 if(state->pen.strike)
476 args[argi++] = 9;
477
478 if(state->pen.font)
479 args[argi++] = 10 + state->pen.font;
480
481 if(state->pen.underline == 2)
482 args[argi++] = 21;
483
484 argi = vterm_state_getpen_color(&state->pen.fg, argi, args, true);
485
486 argi = vterm_state_getpen_color(&state->pen.bg, argi, args, false);
487
488 return argi;
489 }
490
vterm_state_get_penattr(const VTermState * state,VTermAttr attr,VTermValue * val)491 int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val)
492 {
493 switch(attr) {
494 case VTERM_ATTR_BOLD:
495 val->boolean = state->pen.bold;
496 return 1;
497
498 case VTERM_ATTR_UNDERLINE:
499 val->number = state->pen.underline;
500 return 1;
501
502 case VTERM_ATTR_ITALIC:
503 val->boolean = state->pen.italic;
504 return 1;
505
506 case VTERM_ATTR_BLINK:
507 val->boolean = state->pen.blink;
508 return 1;
509
510 case VTERM_ATTR_REVERSE:
511 val->boolean = state->pen.reverse;
512 return 1;
513
514 case VTERM_ATTR_STRIKE:
515 val->boolean = state->pen.strike;
516 return 1;
517
518 case VTERM_ATTR_FONT:
519 val->number = state->pen.font;
520 return 1;
521
522 case VTERM_ATTR_FOREGROUND:
523 val->color = state->pen.fg;
524 return 1;
525
526 case VTERM_ATTR_BACKGROUND:
527 val->color = state->pen.bg;
528 return 1;
529
530 case VTERM_N_ATTRS:
531 return 0;
532 }
533
534 return 0;
535 }
536