xref: /vim-8.2.3635/src/libvterm/src/pen.c (revision a53618dd)
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, 0x5F, 0x87, 0xAF, 0xD7, 0xFF,
37 };
38 
39 // Use 0x81 instead of 0x80 to be able to distinguish from ansi black
40 static int ramp24[] = {
41   0x08, 0x12, 0x1C, 0x26, 0x30, 0x3A, 0x44, 0x4E, 0x58, 0x62, 0x6C, 0x76,
42   0x81, 0x8A, 0x94, 0x9E, 0xA8, 0xB2, 0xBC, 0xC6, 0xD0, 0xDA, 0xE4, 0xEE,
43 };
44 
lookup_default_colour_ansi(long idx,VTermColor * col)45 static void lookup_default_colour_ansi(long idx, VTermColor *col)
46 {
47   vterm_color_rgb(
48       col,
49       ansi_colors[idx].red, ansi_colors[idx].green, ansi_colors[idx].blue);
50   col->index = (uint8_t)idx;
51   col->type = VTERM_COLOR_INDEXED;
52 }
53 
lookup_colour_ansi(const VTermState * state,long index,VTermColor * col)54 static int lookup_colour_ansi(const VTermState *state, long index, VTermColor *col)
55 {
56   if(index >= 0 && index < 16) {
57     *col = state->colors[index];
58     return TRUE;
59   }
60 
61   return FALSE;
62 }
63 
lookup_colour_palette(const VTermState * state,long index,VTermColor * col)64 static int lookup_colour_palette(const VTermState *state, long index, VTermColor *col)
65 {
66   if(index >= 0 && index < 16) {
67     // Normal 8 colours or high intensity - parse as palette 0
68     return lookup_colour_ansi(state, index, col);
69   }
70   else if(index >= 16 && index < 232) {
71     // 216-colour cube
72     index -= 16;
73 
74     vterm_color_rgb(col, ramp6[index/6/6 % 6],
75                          ramp6[index/6   % 6],
76                          ramp6[index     % 6]);
77 
78     return TRUE;
79   }
80   else if(index >= 232 && index < 256) {
81     // 24 greyscales
82     index -= 232;
83 
84     vterm_color_rgb(col, ramp24[index], ramp24[index], ramp24[index]);
85 
86     return TRUE;
87   }
88 
89   return FALSE;
90 }
91 
lookup_colour(const VTermState * state,int palette,const long args[],int argcount,VTermColor * col)92 static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount, VTermColor *col)
93 {
94   switch(palette) {
95   case 2: // RGB mode - 3 args contain colour values directly
96     if(argcount < 3)
97       return argcount;
98 
99     vterm_color_rgb(col, (uint8_t)CSI_ARG(args[0]), (uint8_t)CSI_ARG(args[1]), (uint8_t)CSI_ARG(args[2]));
100 
101     return 3;
102 
103   case 5: // XTerm 256-colour mode
104     if (!argcount || CSI_ARG_IS_MISSING(args[0])) {
105       return argcount ? 1 : 0;
106     }
107 
108     lookup_colour_palette(state, args[0], col);
109     return 1;
110 
111   default:
112     DEBUG_LOG1("Unrecognised colour palette %d\n", palette);
113     return 0;
114   }
115 }
116 
117 // Some conveniences
118 
setpenattr(VTermState * state,VTermAttr attr,VTermValueType type UNUSED,VTermValue * val)119 static void setpenattr(VTermState *state, VTermAttr attr, VTermValueType type UNUSED, VTermValue *val)
120 {
121 #ifdef DEBUG
122   if(type != vterm_get_attr_type(attr)) {
123     DEBUG_LOG3("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;
135   val.boolean = boolean;
136   setpenattr(state, attr, VTERM_VALUETYPE_BOOL, &val);
137 }
138 
setpenattr_int(VTermState * state,VTermAttr attr,int number)139 static void setpenattr_int(VTermState *state, VTermAttr attr, int number)
140 {
141   VTermValue val;
142   val.number = number;
143   setpenattr(state, attr, VTERM_VALUETYPE_INT, &val);
144 }
145 
setpenattr_col(VTermState * state,VTermAttr attr,VTermColor color)146 static void setpenattr_col(VTermState *state, VTermAttr attr, VTermColor color)
147 {
148   VTermValue val;
149   val.color = color;
150   setpenattr(state, attr, VTERM_VALUETYPE_COLOR, &val);
151 }
152 
set_pen_col_ansi(VTermState * state,VTermAttr attr,long col)153 static void set_pen_col_ansi(VTermState *state, VTermAttr attr, long col)
154 {
155   VTermColor *colp = (attr == VTERM_ATTR_BACKGROUND) ? &state->pen.bg : &state->pen.fg;
156 
157   lookup_colour_ansi(state, col, colp);
158 
159   setpenattr_col(state, attr, *colp);
160 }
161 
vterm_state_newpen(VTermState * state)162 INTERNAL void vterm_state_newpen(VTermState *state)
163 {
164   int col;
165 
166   // 90% grey so that pure white is brighter
167   vterm_color_rgb(&state->default_fg, 240, 240, 240);
168   vterm_color_rgb(&state->default_bg, 0, 0, 0);
169   vterm_state_set_default_colors(state, &state->default_fg, &state->default_bg);
170 
171   for(col = 0; col < 16; col++)
172     lookup_default_colour_ansi(col, &state->colors[col]);
173 }
174 
vterm_state_resetpen(VTermState * state)175 INTERNAL void vterm_state_resetpen(VTermState *state)
176 {
177   state->pen.bold = 0;      setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
178   state->pen.underline = 0; setpenattr_int( state, VTERM_ATTR_UNDERLINE, 0);
179   state->pen.italic = 0;    setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
180   state->pen.blink = 0;     setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
181   state->pen.reverse = 0;   setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
182   state->pen.conceal = 0;   setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
183   state->pen.strike = 0;    setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
184   state->pen.font = 0;      setpenattr_int( state, VTERM_ATTR_FONT, 0);
185 
186   state->pen.fg = state->default_fg;  setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg);
187   state->pen.bg = state->default_bg;  setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
188 }
189 
vterm_state_savepen(VTermState * state,int save)190 INTERNAL void vterm_state_savepen(VTermState *state, int save)
191 {
192   if(save) {
193     state->saved.pen = state->pen;
194   }
195   else {
196     state->pen = state->saved.pen;
197 
198     setpenattr_bool(state, VTERM_ATTR_BOLD,       state->pen.bold);
199     setpenattr_int( state, VTERM_ATTR_UNDERLINE,  state->pen.underline);
200     setpenattr_bool(state, VTERM_ATTR_ITALIC,     state->pen.italic);
201     setpenattr_bool(state, VTERM_ATTR_BLINK,      state->pen.blink);
202     setpenattr_bool(state, VTERM_ATTR_REVERSE,    state->pen.reverse);
203     setpenattr_bool(state, VTERM_ATTR_CONCEAL,    state->pen.conceal);
204     setpenattr_bool(state, VTERM_ATTR_STRIKE,     state->pen.strike);
205     setpenattr_int( state, VTERM_ATTR_FONT,       state->pen.font);
206     setpenattr_col( state, VTERM_ATTR_FOREGROUND, state->pen.fg);
207     setpenattr_col( state, VTERM_ATTR_BACKGROUND, state->pen.bg);
208   }
209 }
210 
vterm_color_rgb(VTermColor * col,uint8_t red,uint8_t green,uint8_t blue)211 void vterm_color_rgb(VTermColor *col, uint8_t red, uint8_t green, uint8_t blue)
212 {
213   col->type = VTERM_COLOR_RGB;
214   col->red   = red;
215   col->green = green;
216   col->blue  = blue;
217 }
218 
vterm_color_indexed(VTermColor * col,uint8_t idx)219 void vterm_color_indexed(VTermColor *col, uint8_t idx)
220 {
221   col->type = VTERM_COLOR_INDEXED;
222   col->index = idx;
223 }
224 
vterm_color_is_equal(const VTermColor * a,const VTermColor * b)225 int vterm_color_is_equal(const VTermColor *a, const VTermColor *b)
226 {
227   /* First make sure that the two colours are of the same type (RGB/Indexed) */
228   if (a->type != b->type) {
229     return FALSE;
230   }
231 
232   /* Depending on the type inspect the corresponding members */
233   if (VTERM_COLOR_IS_INDEXED(a)) {
234     return a->index == b->index;
235   }
236   else if (VTERM_COLOR_IS_RGB(a)) {
237     return    (a->red   == b->red)
238            && (a->green == b->green)
239            && (a->blue  == b->blue);
240   }
241 
242   return 0;
243 }
244 
vterm_state_get_default_colors(const VTermState * state,VTermColor * default_fg,VTermColor * default_bg)245 void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg)
246 {
247   *default_fg = state->default_fg;
248   *default_bg = state->default_bg;
249 }
250 
vterm_state_get_palette_color(const VTermState * state,int index,VTermColor * col)251 void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col)
252 {
253   lookup_colour_palette(state, index, col);
254 }
255 
vterm_state_set_default_colors(VTermState * state,const VTermColor * default_fg,const VTermColor * default_bg)256 void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg)
257 {
258   /* Copy the given colors */
259   state->default_fg = *default_fg;
260   state->default_bg = *default_bg;
261 
262   /* Make sure the correct type flags are set */
263   state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK)
264                          | VTERM_COLOR_DEFAULT_FG;
265   state->default_bg.type = (state->default_bg.type & ~VTERM_COLOR_DEFAULT_MASK)
266                          | VTERM_COLOR_DEFAULT_BG;
267 }
268 
vterm_state_set_palette_color(VTermState * state,int index,const VTermColor * col)269 void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col)
270 {
271   if(index >= 0 && index < 16)
272   {
273     state->colors[index] = *col;
274     state->colors[index].index = index + 1;
275   }
276 }
277 
vterm_state_convert_color_to_rgb(const VTermState * state,VTermColor * col)278 void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col)
279 {
280   if (VTERM_COLOR_IS_INDEXED(col)) { /* Convert indexed colors to RGB */
281     lookup_colour_palette(state, col->index, col);
282   }
283   col->type &= VTERM_COLOR_TYPE_MASK; /* Reset any metadata but the type */
284 }
285 
vterm_state_set_bold_highbright(VTermState * state,int bold_is_highbright)286 void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright)
287 {
288   state->bold_is_highbright = bold_is_highbright;
289 }
290 
vterm_state_setpen(VTermState * state,const long args[],int argcount)291 INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argcount)
292 {
293   // SGR - ECMA-48 8.3.117
294 
295   int argi = 0;
296   int value;
297 
298   while(argi < argcount) {
299     // This logic is easier to do 'done' backwards; set it true, and make it
300     // false again in the 'default' case
301     int done = 1;
302 
303     long arg;
304     switch(arg = CSI_ARG(args[argi])) {
305     case CSI_ARG_MISSING:
306     case 0: // Reset
307       vterm_state_resetpen(state);
308       break;
309 
310     case 1: { // Bold on
311       const VTermColor *fg = &state->pen.fg;
312       state->pen.bold = 1;
313       setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
314       if(!VTERM_COLOR_IS_DEFAULT_FG(fg) && VTERM_COLOR_IS_INDEXED(fg) && fg->index < 8 && state->bold_is_highbright)
315         set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, fg->index + (state->pen.bold ? 8 : 0));
316       break;
317     }
318 
319     case 3: // Italic on
320       state->pen.italic = 1;
321       setpenattr_bool(state, VTERM_ATTR_ITALIC, 1);
322       break;
323 
324     case 4: // Underline
325       state->pen.underline = VTERM_UNDERLINE_SINGLE;
326       if(CSI_ARG_HAS_MORE(args[argi])) {
327         argi++;
328         switch(CSI_ARG(args[argi])) {
329           case 0:
330             state->pen.underline = 0;
331             break;
332           case 1:
333             state->pen.underline = VTERM_UNDERLINE_SINGLE;
334             break;
335           case 2:
336             state->pen.underline = VTERM_UNDERLINE_DOUBLE;
337             break;
338           case 3:
339             state->pen.underline = VTERM_UNDERLINE_CURLY;
340             break;
341         }
342       }
343       setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
344       break;
345 
346     case 5: // Blink
347       state->pen.blink = 1;
348       setpenattr_bool(state, VTERM_ATTR_BLINK, 1);
349       break;
350 
351     case 7: // Reverse on
352       state->pen.reverse = 1;
353       setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
354       break;
355 
356     case 8: // Conceal on
357       state->pen.conceal = 1;
358       setpenattr_bool(state, VTERM_ATTR_CONCEAL, 1);
359       break;
360 
361     case 9: // Strikethrough on
362       state->pen.strike = 1;
363       setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
364       break;
365 
366     case 10: case 11: case 12: case 13: case 14:
367     case 15: case 16: case 17: case 18: case 19: // Select font
368       state->pen.font = CSI_ARG(args[argi]) - 10;
369       setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
370       break;
371 
372     case 21: // Underline double
373       state->pen.underline = VTERM_UNDERLINE_DOUBLE;
374       setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
375       break;
376 
377     case 22: // Bold off
378       state->pen.bold = 0;
379       setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
380       break;
381 
382     case 23: // Italic and Gothic (currently unsupported) off
383       state->pen.italic = 0;
384       setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
385       break;
386 
387     case 24: // Underline off
388       state->pen.underline = 0;
389       setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
390       break;
391 
392     case 25: // Blink off
393       state->pen.blink = 0;
394       setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
395       break;
396 
397     case 27: // Reverse off
398       state->pen.reverse = 0;
399       setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
400       break;
401 
402     case 28: // Conceal off (Reveal)
403       state->pen.conceal = 0;
404       setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
405       break;
406 
407     case 29: // Strikethrough off
408       state->pen.strike = 0;
409       setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
410       break;
411 
412     case 30: case 31: case 32: case 33:
413     case 34: case 35: case 36: case 37: // Foreground colour palette
414       value = CSI_ARG(args[argi]) - 30;
415       if(state->pen.bold && state->bold_is_highbright)
416         value += 8;
417       set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
418       break;
419 
420     case 38: // Foreground colour alternative palette
421       if(argcount - argi < 1)
422         return;
423       argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.fg);
424       setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
425       break;
426 
427     case 39: // Foreground colour default
428       state->pen.fg = state->default_fg;
429       setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
430       break;
431 
432     case 40: case 41: case 42: case 43:
433     case 44: case 45: case 46: case 47: // Background colour palette
434       value = CSI_ARG(args[argi]) - 40;
435       set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
436       break;
437 
438     case 48: // Background colour alternative palette
439       if(argcount - argi < 1)
440         return;
441       argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.bg);
442       setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
443       break;
444 
445     case 49: // Default background
446       state->pen.bg = state->default_bg;
447       setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
448       break;
449 
450     case 90: case 91: case 92: case 93:
451     case 94: case 95: case 96: case 97: // Foreground colour high-intensity palette
452       value = CSI_ARG(args[argi]) - 90 + 8;
453       set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
454       break;
455 
456     case 100: case 101: case 102: case 103:
457     case 104: case 105: case 106: case 107: // Background colour high-intensity palette
458       value = CSI_ARG(args[argi]) - 100 + 8;
459       set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
460       break;
461 
462     default:
463       done = 0;
464       break;
465     }
466 
467     if(!done)
468     {
469       DEBUG_LOG1("libvterm: Unhandled CSI SGR %ld\n", arg);
470     }
471 
472     while(CSI_ARG_HAS_MORE(args[argi++]))
473       ;
474   }
475 }
476 
vterm_state_getpen_color(const VTermColor * col,int argi,long args[],int fg)477 static int vterm_state_getpen_color(const VTermColor *col, int argi, long args[], int fg)
478 {
479     /* Do nothing if the given color is the default color */
480     if (( fg && VTERM_COLOR_IS_DEFAULT_FG(col)) ||
481         (!fg && VTERM_COLOR_IS_DEFAULT_BG(col))) {
482         return argi;
483     }
484 
485     /* Decide whether to send an indexed color or an RGB color */
486     if (VTERM_COLOR_IS_INDEXED(col)) {
487         const uint8_t idx = col->index;
488         if (idx < 8) {
489             args[argi++] = (idx + (fg ? 30 : 40));
490         }
491         else if (idx < 16) {
492             args[argi++] = (idx - 8 + (fg ? 90 : 100));
493         }
494         else {
495             args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
496             args[argi++] = CSI_ARG_FLAG_MORE | 5;
497             args[argi++] = idx;
498         }
499     }
500     else if (VTERM_COLOR_IS_RGB(col)) {
501         args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
502         args[argi++] = CSI_ARG_FLAG_MORE | 2;
503         args[argi++] = CSI_ARG_FLAG_MORE | col->red;
504         args[argi++] = CSI_ARG_FLAG_MORE | col->green;
505         args[argi++] = col->blue;
506     }
507     return argi;
508 }
509 
vterm_state_getpen(VTermState * state,long args[],int argcount UNUSED)510 INTERNAL int vterm_state_getpen(VTermState *state, long args[], int argcount UNUSED)
511 {
512   int argi = 0;
513 
514   if(state->pen.bold)
515     args[argi++] = 1;
516 
517   if(state->pen.italic)
518     args[argi++] = 3;
519 
520   if(state->pen.underline == VTERM_UNDERLINE_SINGLE)
521     args[argi++] = 4;
522   if(state->pen.underline == VTERM_UNDERLINE_CURLY)
523     args[argi++] = 4 | CSI_ARG_FLAG_MORE, args[argi++] = 3;
524 
525   if(state->pen.blink)
526     args[argi++] = 5;
527 
528   if(state->pen.reverse)
529     args[argi++] = 7;
530 
531   if(state->pen.conceal)
532     args[argi++] = 8;
533 
534   if(state->pen.strike)
535     args[argi++] = 9;
536 
537   if(state->pen.font)
538     args[argi++] = 10 + state->pen.font;
539 
540   if(state->pen.underline == VTERM_UNDERLINE_DOUBLE)
541     args[argi++] = 21;
542 
543   argi = vterm_state_getpen_color(&state->pen.fg, argi, args, TRUE);
544 
545   argi = vterm_state_getpen_color(&state->pen.bg, argi, args, FALSE);
546 
547   return argi;
548 }
549 
vterm_state_get_penattr(const VTermState * state,VTermAttr attr,VTermValue * val)550 int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val)
551 {
552   switch(attr) {
553   case VTERM_ATTR_BOLD:
554     val->boolean = state->pen.bold;
555     return 1;
556 
557   case VTERM_ATTR_UNDERLINE:
558     val->number = state->pen.underline;
559     return 1;
560 
561   case VTERM_ATTR_ITALIC:
562     val->boolean = state->pen.italic;
563     return 1;
564 
565   case VTERM_ATTR_BLINK:
566     val->boolean = state->pen.blink;
567     return 1;
568 
569   case VTERM_ATTR_REVERSE:
570     val->boolean = state->pen.reverse;
571     return 1;
572 
573   case VTERM_ATTR_CONCEAL:
574     val->boolean = state->pen.conceal;
575     return 1;
576 
577   case VTERM_ATTR_STRIKE:
578     val->boolean = state->pen.strike;
579     return 1;
580 
581   case VTERM_ATTR_FONT:
582     val->number = state->pen.font;
583     return 1;
584 
585   case VTERM_ATTR_FOREGROUND:
586     val->color = state->pen.fg;
587     return 1;
588 
589   case VTERM_ATTR_BACKGROUND:
590     val->color = state->pen.bg;
591     return 1;
592 
593   case VTERM_N_ATTRS:
594     return 0;
595   }
596 
597   return 0;
598 }
599