neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

pen.c (19092B)


      1 #include <stdio.h>
      2 
      3 #include "nvim/vterm/pen.h"
      4 #include "nvim/vterm/vterm.h"
      5 #include "nvim/vterm/vterm_internal_defs.h"
      6 
      7 #include "vterm/pen.c.generated.h"
      8 
      9 // Structure used to store RGB triples without the additional metadata stored in VTermColor.
     10 typedef struct {
     11  uint8_t red, green, blue;
     12 } VTermRGB;
     13 
     14 static const VTermRGB ansi_colors[] = {
     15  // R    G    B
     16  {   0,   0,   0 },  // black
     17  { 224,   0,   0 },  // red
     18  {   0, 224,   0 },  // green
     19  { 224, 224,   0 },  // yellow
     20  {   0,   0, 224 },  // blue
     21  { 224,   0, 224 },  // magenta
     22  {   0, 224, 224 },  // cyan
     23  { 224, 224, 224 },  // white == light grey
     24 
     25  // high intensity
     26  { 128, 128, 128 },  // black
     27  { 255,  64,  64 },  // red
     28  {  64, 255,  64 },  // green
     29  { 255, 255,  64 },  // yellow
     30  {  64,  64, 255 },  // blue
     31  { 255,  64, 255 },  // magenta
     32  {  64, 255, 255 },  // cyan
     33  { 255, 255, 255 },  // white for real
     34 };
     35 
     36 static uint8_t ramp6[] = {
     37  0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF,
     38 };
     39 
     40 static uint8_t ramp24[] = {
     41  0x00, 0x0B, 0x16, 0x21, 0x2C, 0x37, 0x42, 0x4D, 0x58, 0x63, 0x6E, 0x79,
     42  0x85, 0x90, 0x9B, 0xA6, 0xB1, 0xBC, 0xC7, 0xD2, 0xDD, 0xE8, 0xF3, 0xFF,
     43 };
     44 
     45 static void lookup_default_colour_ansi(long idx, VTermColor *col)
     46 {
     47  if (idx >= 0 && idx < 16) {
     48    vterm_color_rgb(col,
     49                    ansi_colors[idx].red, ansi_colors[idx].green, ansi_colors[idx].blue);
     50  }
     51 }
     52 
     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 
     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  } else if (index >= 16 && index < 232) {
     69    // 216-colour cube
     70    index -= 16;
     71 
     72    vterm_color_rgb(col, ramp6[index/6/6 % 6],
     73                    ramp6[index/6   % 6],
     74                    ramp6[index     % 6]);
     75 
     76    return true;
     77  } else if (index >= 232 && index < 256) {
     78    // 24 greyscales
     79    index -= 232;
     80 
     81    vterm_color_rgb(col, ramp24[index], ramp24[index], ramp24[index]);
     82 
     83    return true;
     84  }
     85 
     86  return false;
     87 }
     88 
     89 static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount,
     90                         VTermColor *col)
     91 {
     92  switch (palette) {
     93  case 2:  // RGB mode - 3 args contain colour values directly
     94    if (argcount < 3) {
     95      return argcount;
     96    }
     97 
     98    vterm_color_rgb(col, (uint8_t)CSI_ARG(args[0]), (uint8_t)CSI_ARG(args[1]),
     99                    (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    vterm_color_indexed(col, (uint8_t)args[0]);
    109 
    110    return argcount ? 1 : 0;
    111 
    112  default:
    113    DEBUG_LOG("Unrecognised colour palette %d\n", palette);
    114    return 0;
    115  }
    116 }
    117 
    118 // Some conveniences
    119 
    120 static void setpenattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val)
    121 {
    122 #ifdef DEBUG
    123  if (type != vterm_get_attr_type(attr)) {
    124    DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n",
    125              attr, vterm_get_attr_type(attr), type);
    126    return;
    127  }
    128 #endif
    129  if (state->callbacks && state->callbacks->setpenattr) {
    130    (*state->callbacks->setpenattr)(attr, val, state->cbdata);
    131  }
    132 }
    133 
    134 static void setpenattr_bool(VTermState *state, VTermAttr attr, int boolean)
    135 {
    136  VTermValue val = { .boolean = boolean };
    137  setpenattr(state, attr, VTERM_VALUETYPE_BOOL, &val);
    138 }
    139 
    140 static void setpenattr_int(VTermState *state, VTermAttr attr, int number)
    141 {
    142  VTermValue val = { .number = number };
    143  setpenattr(state, attr, VTERM_VALUETYPE_INT, &val);
    144 }
    145 
    146 static void setpenattr_col(VTermState *state, VTermAttr attr, VTermColor color)
    147 {
    148  VTermValue val = { .color = color };
    149  setpenattr(state, attr, VTERM_VALUETYPE_COLOR, &val);
    150 }
    151 
    152 static void set_pen_col_ansi(VTermState *state, VTermAttr attr, long col)
    153 {
    154  VTermColor *colp = (attr == VTERM_ATTR_BACKGROUND) ? &state->pen.bg : &state->pen.fg;
    155 
    156  vterm_color_indexed(colp, (uint8_t)col);
    157 
    158  setpenattr_col(state, attr, *colp);
    159 }
    160 
    161 void vterm_state_newpen(VTermState *state)
    162 {
    163  // 90% grey so that pure white is brighter
    164  vterm_color_rgb(&state->default_fg, 240, 240, 240);
    165  vterm_color_rgb(&state->default_bg, 0, 0, 0);
    166  vterm_state_set_default_colors(state, &state->default_fg, &state->default_bg);
    167 
    168  for (int col = 0; col < 16; col++) {
    169    lookup_default_colour_ansi(col, &state->colors[col]);
    170  }
    171 }
    172 
    173 void vterm_state_resetpen(VTermState *state)
    174 {
    175  state->pen.bold = 0;      setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
    176  state->pen.underline = 0; setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
    177  state->pen.italic = 0;    setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
    178  state->pen.blink = 0;     setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
    179  state->pen.reverse = 0;   setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
    180  state->pen.conceal = 0;   setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
    181  state->pen.strike = 0;    setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
    182  state->pen.font = 0;      setpenattr_int(state, VTERM_ATTR_FONT, 0);
    183  state->pen.small = 0;     setpenattr_bool(state, VTERM_ATTR_SMALL, 0);
    184  state->pen.baseline = 0;  setpenattr_int(state, VTERM_ATTR_BASELINE, 0);
    185  state->pen.dim = 0;       setpenattr_bool(state, VTERM_ATTR_DIM, 0);
    186  state->pen.overline = 0;  setpenattr_bool(state, VTERM_ATTR_OVERLINE, 0);
    187 
    188  state->pen.fg = state->default_fg;
    189  setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg);
    190  state->pen.bg = state->default_bg;
    191  setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
    192 
    193  state->pen.uri = 0; setpenattr_int(state, VTERM_ATTR_URI, 0);
    194 }
    195 
    196 void vterm_state_savepen(VTermState *state, int save)
    197 {
    198  if (save) {
    199    state->saved.pen = state->pen;
    200  } else {
    201    state->pen = state->saved.pen;
    202 
    203    setpenattr_bool(state, VTERM_ATTR_BOLD,      state->pen.bold);
    204    setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
    205    setpenattr_bool(state, VTERM_ATTR_ITALIC,    state->pen.italic);
    206    setpenattr_bool(state, VTERM_ATTR_BLINK,     state->pen.blink);
    207    setpenattr_bool(state, VTERM_ATTR_REVERSE,   state->pen.reverse);
    208    setpenattr_bool(state, VTERM_ATTR_CONCEAL,   state->pen.conceal);
    209    setpenattr_bool(state, VTERM_ATTR_STRIKE,    state->pen.strike);
    210    setpenattr_int(state, VTERM_ATTR_FONT,      state->pen.font);
    211    setpenattr_bool(state, VTERM_ATTR_SMALL,     state->pen.small);
    212    setpenattr_int(state, VTERM_ATTR_BASELINE,  state->pen.baseline);
    213    setpenattr_bool(state, VTERM_ATTR_DIM,      state->pen.dim);
    214    setpenattr_bool(state, VTERM_ATTR_OVERLINE, state->pen.overline);
    215 
    216    setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
    217    setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
    218 
    219    setpenattr_int(state, VTERM_ATTR_URI, state->pen.uri);
    220  }
    221 }
    222 
    223 void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg,
    224                                    const VTermColor *default_bg)
    225 {
    226  if (default_fg) {
    227    state->default_fg = *default_fg;
    228    state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK)
    229                             | VTERM_COLOR_DEFAULT_FG;
    230  }
    231 
    232  if (default_bg) {
    233    state->default_bg = *default_bg;
    234    state->default_bg.type = (state->default_bg.type & ~VTERM_COLOR_DEFAULT_MASK)
    235                             | VTERM_COLOR_DEFAULT_BG;
    236  }
    237 }
    238 
    239 void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col)
    240 {
    241  if (index >= 0 && index < 16) {
    242    state->colors[index] = *col;
    243  }
    244 }
    245 
    246 /// Makes sure that the given color `col` is indeed an RGB colour. After this
    247 /// function returns, VTERM_COLOR_IS_RGB(col) will return true, while all other
    248 /// flags stored in `col->type` will have been reset.
    249 ///
    250 /// @param state is the VTermState instance from which the colour palette should
    251 /// be extracted.
    252 /// @param col is a pointer at the VTermColor instance that should be converted
    253 /// to an RGB colour.
    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 
    262 void vterm_state_setpen(VTermState *state, const long args[], int argcount)
    263 {
    264  // SGR - ECMA-48 8.3.117
    265 
    266  int argi = 0;
    267  int value;
    268 
    269  while (argi < argcount) {
    270    // This logic is easier to do 'done' backwards; set it true, and make it
    271    // false again in the 'default' case
    272    int done = 1;
    273 
    274    long arg;
    275    switch (arg = CSI_ARG(args[argi])) {
    276    case CSI_ARG_MISSING:
    277    case 0:  // Reset
    278      vterm_state_resetpen(state);
    279      break;
    280 
    281    case 1: {  // Bold on
    282      const VTermColor *fg = &state->pen.fg;
    283      state->pen.bold = 1;
    284      setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
    285      if (!VTERM_COLOR_IS_DEFAULT_FG(fg) && VTERM_COLOR_IS_INDEXED(fg) && fg->indexed.idx < 8
    286          && state->bold_is_highbright) {
    287        set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, fg->indexed.idx + (state->pen.bold ? 8 : 0));
    288      }
    289      break;
    290    }
    291 
    292    case 2:  // Dim/faint on
    293      state->pen.dim = 1;
    294      setpenattr_bool(state, VTERM_ATTR_DIM, 1);
    295      break;
    296 
    297    case 3:  // Italic on
    298      state->pen.italic = 1;
    299      setpenattr_bool(state, VTERM_ATTR_ITALIC, 1);
    300      break;
    301 
    302    case 4:  // Underline
    303      state->pen.underline = VTERM_UNDERLINE_SINGLE;
    304      if (CSI_ARG_HAS_MORE(args[argi])) {
    305        argi++;
    306        switch (CSI_ARG(args[argi])) {
    307        case 0:
    308          state->pen.underline = 0;
    309          break;
    310        case 1:
    311          state->pen.underline = VTERM_UNDERLINE_SINGLE;
    312          break;
    313        case 2:
    314          state->pen.underline = VTERM_UNDERLINE_DOUBLE;
    315          break;
    316        case 3:
    317          state->pen.underline = VTERM_UNDERLINE_CURLY;
    318          break;
    319        }
    320      }
    321      setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
    322      break;
    323 
    324    case 5:  // Blink
    325      state->pen.blink = 1;
    326      setpenattr_bool(state, VTERM_ATTR_BLINK, 1);
    327      break;
    328 
    329    case 7:  // Reverse on
    330      state->pen.reverse = 1;
    331      setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
    332      break;
    333 
    334    case 8:  // Conceal on
    335      state->pen.conceal = 1;
    336      setpenattr_bool(state, VTERM_ATTR_CONCEAL, 1);
    337      break;
    338 
    339    case 9:  // Strikethrough on
    340      state->pen.strike = 1;
    341      setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
    342      break;
    343 
    344    case 10:
    345    case 11:
    346    case 12:
    347    case 13:
    348    case 14:
    349    case 15:
    350    case 16:
    351    case 17:
    352    case 18:
    353    case 19:  // Select font
    354      state->pen.font = CSI_ARG(args[argi]) - 10;
    355      setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
    356      break;
    357 
    358    case 21:  // Underline double
    359      state->pen.underline = VTERM_UNDERLINE_DOUBLE;
    360      setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
    361      break;
    362 
    363    case 22:  // Normal intensity (bold and dim off)
    364      state->pen.bold = 0;
    365      setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
    366      state->pen.dim = 0;
    367      setpenattr_bool(state, VTERM_ATTR_DIM, 0);
    368      break;
    369 
    370    case 23:  // Italic and Gothic (currently unsupported) off
    371      state->pen.italic = 0;
    372      setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
    373      break;
    374 
    375    case 24:  // Underline off
    376      state->pen.underline = 0;
    377      setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
    378      break;
    379 
    380    case 25:  // Blink off
    381      state->pen.blink = 0;
    382      setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
    383      break;
    384 
    385    case 27:  // Reverse off
    386      state->pen.reverse = 0;
    387      setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
    388      break;
    389 
    390    case 28:  // Conceal off (Reveal)
    391      state->pen.conceal = 0;
    392      setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
    393      break;
    394 
    395    case 29:  // Strikethrough off
    396      state->pen.strike = 0;
    397      setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
    398      break;
    399 
    400    case 30:
    401    case 31:
    402    case 32:
    403    case 33:
    404    case 34:
    405    case 35:
    406    case 36:
    407    case 37:  // Foreground colour palette
    408      value = CSI_ARG(args[argi]) - 30;
    409      if (state->pen.bold && state->bold_is_highbright) {
    410        value += 8;
    411      }
    412      set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
    413      break;
    414 
    415    case 38:  // Foreground colour alternative palette
    416      if (argcount - argi < 1) {
    417        return;
    418      }
    419      argi += 1 + lookup_colour(state, CSI_ARG(args[argi + 1]), args + argi + 2,
    420                                argcount - argi - 2, &state->pen.fg);
    421      setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
    422      break;
    423 
    424    case 39:  // Foreground colour default
    425      state->pen.fg = state->default_fg;
    426      setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
    427      break;
    428 
    429    case 40:
    430    case 41:
    431    case 42:
    432    case 43:
    433    case 44:
    434    case 45:
    435    case 46:
    436    case 47:  // Background colour palette
    437      value = CSI_ARG(args[argi]) - 40;
    438      set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
    439      break;
    440 
    441    case 48:  // Background colour alternative palette
    442      if (argcount - argi < 1) {
    443        return;
    444      }
    445      argi += 1 + lookup_colour(state, CSI_ARG(args[argi + 1]), args + argi + 2,
    446                                argcount - argi - 2, &state->pen.bg);
    447      setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
    448      break;
    449 
    450    case 49:  // Default background
    451      state->pen.bg = state->default_bg;
    452      setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
    453      break;
    454 
    455    case 53:  // Overline on
    456      state->pen.overline = 1;
    457      setpenattr_bool(state, VTERM_ATTR_OVERLINE, 1);
    458      break;
    459 
    460    case 55:  // Overline off
    461      state->pen.overline = 0;
    462      setpenattr_bool(state, VTERM_ATTR_OVERLINE, 0);
    463      break;
    464 
    465    case 73:  // Superscript
    466    case 74:  // Subscript
    467    case 75:  // Superscript/subscript off
    468      state->pen.small = (arg != 75);
    469      state->pen.baseline =
    470        (arg == 73) ? VTERM_BASELINE_RAISE
    471                    : (arg == 74) ? VTERM_BASELINE_LOWER
    472                                  : VTERM_BASELINE_NORMAL;
    473      setpenattr_bool(state, VTERM_ATTR_SMALL,    state->pen.small);
    474      setpenattr_int(state, VTERM_ATTR_BASELINE, state->pen.baseline);
    475      break;
    476 
    477    case 90:
    478    case 91:
    479    case 92:
    480    case 93:
    481    case 94:
    482    case 95:
    483    case 96:
    484    case 97:  // Foreground colour high-intensity palette
    485      value = CSI_ARG(args[argi]) - 90 + 8;
    486      set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
    487      break;
    488 
    489    case 100:
    490    case 101:
    491    case 102:
    492    case 103:
    493    case 104:
    494    case 105:
    495    case 106:
    496    case 107:  // Background colour high-intensity palette
    497      value = CSI_ARG(args[argi]) - 100 + 8;
    498      set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
    499      break;
    500 
    501    default:
    502      done = 0;
    503      break;
    504    }
    505 
    506    if (!done) {
    507      DEBUG_LOG("libvterm: Unhandled CSI SGR %ld\n", arg);
    508    }
    509 
    510    while (CSI_ARG_HAS_MORE(args[argi++])) {}
    511  }
    512 }
    513 
    514 static int vterm_state_getpen_color(const VTermColor *col, int argi, long args[], int fg)
    515 {
    516  // Do nothing if the given color is the default color
    517  if ((fg && VTERM_COLOR_IS_DEFAULT_FG(col))
    518      || (!fg && VTERM_COLOR_IS_DEFAULT_BG(col))) {
    519    return argi;
    520  }
    521 
    522  // Decide whether to send an indexed color or an RGB color
    523  if (VTERM_COLOR_IS_INDEXED(col)) {
    524    const uint8_t idx = col->indexed.idx;
    525    if (idx < 8) {
    526      args[argi++] = (idx + (fg ? 30 : 40));
    527    } else if (idx < 16) {
    528      args[argi++] = (idx - 8 + (fg ? 90 : 100));
    529    } else {
    530      args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
    531      args[argi++] = CSI_ARG_FLAG_MORE | 5;
    532      args[argi++] = idx;
    533    }
    534  } else if (VTERM_COLOR_IS_RGB(col)) {
    535    args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
    536    args[argi++] = CSI_ARG_FLAG_MORE | 2;
    537    args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.red;
    538    args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.green;
    539    args[argi++] = col->rgb.blue;
    540  }
    541  return argi;
    542 }
    543 
    544 int vterm_state_getpen(VTermState *state, long args[], int argcount)
    545 {
    546  int argi = 0;
    547 
    548  if (state->pen.bold) {
    549    args[argi++] = 1;
    550  }
    551 
    552  if (state->pen.dim) {
    553    args[argi++] = 2;
    554  }
    555 
    556  if (state->pen.italic) {
    557    args[argi++] = 3;
    558  }
    559 
    560  if (state->pen.underline == VTERM_UNDERLINE_SINGLE) {
    561    args[argi++] = 4;
    562  }
    563  if (state->pen.underline == VTERM_UNDERLINE_CURLY) {
    564    args[argi++] = 4 | CSI_ARG_FLAG_MORE, args[argi++] = 3;
    565  }
    566 
    567  if (state->pen.blink) {
    568    args[argi++] = 5;
    569  }
    570 
    571  if (state->pen.reverse) {
    572    args[argi++] = 7;
    573  }
    574 
    575  if (state->pen.conceal) {
    576    args[argi++] = 8;
    577  }
    578 
    579  if (state->pen.strike) {
    580    args[argi++] = 9;
    581  }
    582 
    583  if (state->pen.font) {
    584    args[argi++] = 10 + state->pen.font;
    585  }
    586 
    587  if (state->pen.underline == VTERM_UNDERLINE_DOUBLE) {
    588    args[argi++] = 21;
    589  }
    590 
    591  argi = vterm_state_getpen_color(&state->pen.fg, argi, args, true);
    592 
    593  argi = vterm_state_getpen_color(&state->pen.bg, argi, args, false);
    594 
    595  if (state->pen.overline) {
    596    args[argi++] = 53;
    597  }
    598 
    599  if (state->pen.small) {
    600    if (state->pen.baseline == VTERM_BASELINE_RAISE) {
    601      args[argi++] = 73;
    602    } else if (state->pen.baseline == VTERM_BASELINE_LOWER) {
    603      args[argi++] = 74;
    604    }
    605  }
    606 
    607  return argi;
    608 }
    609 
    610 int vterm_state_set_penattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val)
    611 {
    612  if (!val) {
    613    return 0;
    614  }
    615 
    616  if (type != vterm_get_attr_type(attr)) {
    617    DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n",
    618              attr, vterm_get_attr_type(attr), type);
    619    return 0;
    620  }
    621 
    622  switch (attr) {
    623  case VTERM_ATTR_BOLD:
    624    state->pen.bold = (unsigned)val->boolean;
    625    break;
    626  case VTERM_ATTR_UNDERLINE:
    627    state->pen.underline = (unsigned)val->number;
    628    break;
    629  case VTERM_ATTR_ITALIC:
    630    state->pen.italic = (unsigned)val->boolean;
    631    break;
    632  case VTERM_ATTR_BLINK:
    633    state->pen.blink = (unsigned)val->boolean;
    634    break;
    635  case VTERM_ATTR_REVERSE:
    636    state->pen.reverse = (unsigned)val->boolean;
    637    break;
    638  case VTERM_ATTR_CONCEAL:
    639    state->pen.conceal = (unsigned)val->boolean;
    640    break;
    641  case VTERM_ATTR_STRIKE:
    642    state->pen.strike = (unsigned)val->boolean;
    643    break;
    644  case VTERM_ATTR_FONT:
    645    state->pen.font = (unsigned)val->number;
    646    break;
    647  case VTERM_ATTR_FOREGROUND:
    648    state->pen.fg = val->color;
    649    break;
    650  case VTERM_ATTR_BACKGROUND:
    651    state->pen.bg = val->color;
    652    break;
    653  case VTERM_ATTR_SMALL:
    654    state->pen.small = (unsigned)val->boolean;
    655    break;
    656  case VTERM_ATTR_BASELINE:
    657    state->pen.baseline = (unsigned)val->number;
    658    break;
    659  case VTERM_ATTR_URI:
    660    state->pen.uri = val->number;
    661    break;
    662  case VTERM_ATTR_DIM:
    663    state->pen.dim = (unsigned)val->boolean;
    664    break;
    665  case VTERM_ATTR_OVERLINE:
    666    state->pen.overline = (unsigned)val->boolean;
    667    break;
    668  default:
    669    return 0;
    670  }
    671 
    672  if (state->callbacks && state->callbacks->setpenattr) {
    673    (*state->callbacks->setpenattr)(attr, val, state->cbdata);
    674  }
    675 
    676  return 1;
    677 }