neovim

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

vterm.c (8039B)


      1 #include <stdarg.h>
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 #include <string.h>
      5 
      6 #include "auto/config.h"
      7 #include "nvim/memory.h"
      8 #include "nvim/vterm/screen.h"
      9 #include "nvim/vterm/state.h"
     10 #include "nvim/vterm/vterm.h"
     11 #include "nvim/vterm/vterm_internal_defs.h"
     12 
     13 #include "vterm/vterm.c.generated.h"
     14 
     15 // *****************
     16 // * API functions *
     17 // *****************
     18 
     19 static void *default_malloc(size_t size, void *allocdata)
     20 {
     21  void *ptr = xmalloc(size);
     22  if (ptr) {
     23    memset(ptr, 0, size);
     24  }
     25  return ptr;
     26 }
     27 
     28 static void default_free(void *ptr, void *allocdata)
     29 {
     30  xfree(ptr);
     31 }
     32 
     33 static VTermAllocatorFunctions default_allocator = {
     34  .malloc = &default_malloc,
     35  .free = &default_free,
     36 };
     37 
     38 /// Convenient shortcut for default cases
     39 VTerm *vterm_new(int rows, int cols)
     40 {
     41  return vterm_build(&(const struct VTermBuilder){
     42    .rows = rows,
     43    .cols = cols,
     44  });
     45 }
     46 
     47 // A handy macro for defaulting values out of builder fields
     48 #define DEFAULT(v, def)  ((v) ? (v) : (def))
     49 
     50 VTerm *vterm_build(const struct VTermBuilder *builder)
     51 {
     52  const VTermAllocatorFunctions *allocator = DEFAULT(builder->allocator, &default_allocator);
     53 
     54  // Need to bootstrap using the allocator function directly
     55  VTerm *vt = (*allocator->malloc)(sizeof(VTerm), builder->allocdata);
     56 
     57  vt->allocator = allocator;
     58  vt->allocdata = builder->allocdata;
     59 
     60  vt->rows = builder->rows;
     61  vt->cols = builder->cols;
     62 
     63  vt->parser.state = NORMAL;
     64 
     65  vt->parser.callbacks = NULL;
     66  vt->parser.cbdata = NULL;
     67 
     68  vt->parser.emit_nul = false;
     69 
     70  vt->outfunc = NULL;
     71  vt->outdata = NULL;
     72 
     73  vt->outbuffer_len = DEFAULT(builder->outbuffer_len, 4096);
     74  vt->outbuffer_cur = 0;
     75  vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
     76 
     77  vt->tmpbuffer_len = DEFAULT(builder->tmpbuffer_len, 4096);
     78  vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len);
     79 
     80  return vt;
     81 }
     82 
     83 void vterm_free(VTerm *vt)
     84 {
     85  if (vt->screen) {
     86    vterm_screen_free(vt->screen);
     87  }
     88 
     89  if (vt->state) {
     90    vterm_state_free(vt->state);
     91  }
     92 
     93  vterm_allocator_free(vt, vt->outbuffer);
     94  vterm_allocator_free(vt, vt->tmpbuffer);
     95 
     96  vterm_allocator_free(vt, vt);
     97 }
     98 
     99 void *vterm_allocator_malloc(VTerm *vt, size_t size)
    100 {
    101  return (*vt->allocator->malloc)(size, vt->allocdata);
    102 }
    103 
    104 void vterm_allocator_free(VTerm *vt, void *ptr)
    105 {
    106  (*vt->allocator->free)(ptr, vt->allocdata);
    107 }
    108 
    109 void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
    110 {
    111  if (rowsp) {
    112    *rowsp = vt->rows;
    113  }
    114  if (colsp) {
    115    *colsp = vt->cols;
    116  }
    117 }
    118 
    119 void vterm_set_size(VTerm *vt, int rows, int cols)
    120 {
    121  if (rows < 1 || cols < 1) {
    122    return;
    123  }
    124 
    125  vt->rows = rows;
    126  vt->cols = cols;
    127 
    128  if (vt->parser.callbacks && vt->parser.callbacks->resize) {
    129    (*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata);
    130  }
    131 }
    132 
    133 void vterm_set_utf8(VTerm *vt, int is_utf8)
    134 {
    135  vt->mode.utf8 = (unsigned)is_utf8;
    136 }
    137 
    138 void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user)
    139 {
    140  vt->outfunc = func;
    141  vt->outdata = user;
    142 }
    143 
    144 void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
    145 {
    146  if (vt->outfunc) {
    147    (vt->outfunc)(bytes, len, vt->outdata);
    148    return;
    149  }
    150 
    151  if (len > vt->outbuffer_len - vt->outbuffer_cur) {
    152    return;
    153  }
    154 
    155  memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len);
    156  vt->outbuffer_cur += len;
    157 }
    158 
    159 void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
    160  FUNC_ATTR_PRINTF(2, 3)
    161 {
    162  va_list args;
    163  va_start(args, format);
    164  size_t len = (size_t)vsnprintf(vt->tmpbuffer, vt->tmpbuffer_len, format, args);
    165  vterm_push_output_bytes(vt, vt->tmpbuffer, len);
    166  va_end(args);
    167 }
    168 
    169 void vterm_push_output_sprintf_ctrl(VTerm *vt, uint8_t ctrl, const char *fmt, ...)
    170  FUNC_ATTR_PRINTF(3, 4)
    171 {
    172  size_t cur;
    173 
    174  if (ctrl >= 0x80 && !vt->mode.ctrl8bit) {
    175    cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, ESC_S "%c", ctrl - 0x40);
    176  } else {
    177    cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, "%c", ctrl);
    178  }
    179 
    180  if (cur >= vt->tmpbuffer_len) {
    181    return;
    182  }
    183 
    184  va_list args;
    185  va_start(args, fmt);
    186  cur += (size_t)vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, fmt, args);
    187  va_end(args);
    188 
    189  if (cur >= vt->tmpbuffer_len) {
    190    return;
    191  }
    192 
    193  vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
    194 }
    195 
    196 void vterm_push_output_sprintf_str(VTerm *vt, uint8_t ctrl, bool term, const char *fmt, ...)
    197  FUNC_ATTR_PRINTF(4, 5)
    198 {
    199  size_t cur = 0;
    200 
    201  if (ctrl) {
    202    if (ctrl >= 0x80 && !vt->mode.ctrl8bit) {
    203      cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, ESC_S "%c", ctrl - 0x40);
    204    } else {
    205      cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, "%c", ctrl);
    206    }
    207 
    208    if (cur >= vt->tmpbuffer_len) {
    209      return;
    210    }
    211  }
    212 
    213  va_list args;
    214  va_start(args, fmt);
    215  cur += (size_t)vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, fmt, args);
    216  va_end(args);
    217 
    218  if (cur >= vt->tmpbuffer_len) {
    219    return;
    220  }
    221 
    222  if (term) {
    223    cur += (size_t)snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
    224                            vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\");  // ST
    225 
    226    if (cur >= vt->tmpbuffer_len) {
    227      return;
    228    }
    229  }
    230 
    231  vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
    232 }
    233 
    234 VTermValueType vterm_get_attr_type(VTermAttr attr)
    235 {
    236  switch (attr) {
    237  case VTERM_ATTR_BOLD:
    238    return VTERM_VALUETYPE_BOOL;
    239  case VTERM_ATTR_UNDERLINE:
    240    return VTERM_VALUETYPE_INT;
    241  case VTERM_ATTR_ITALIC:
    242    return VTERM_VALUETYPE_BOOL;
    243  case VTERM_ATTR_BLINK:
    244    return VTERM_VALUETYPE_BOOL;
    245  case VTERM_ATTR_REVERSE:
    246    return VTERM_VALUETYPE_BOOL;
    247  case VTERM_ATTR_CONCEAL:
    248    return VTERM_VALUETYPE_BOOL;
    249  case VTERM_ATTR_STRIKE:
    250    return VTERM_VALUETYPE_BOOL;
    251  case VTERM_ATTR_FONT:
    252    return VTERM_VALUETYPE_INT;
    253  case VTERM_ATTR_FOREGROUND:
    254    return VTERM_VALUETYPE_COLOR;
    255  case VTERM_ATTR_BACKGROUND:
    256    return VTERM_VALUETYPE_COLOR;
    257  case VTERM_ATTR_SMALL:
    258    return VTERM_VALUETYPE_BOOL;
    259  case VTERM_ATTR_BASELINE:
    260    return VTERM_VALUETYPE_INT;
    261  case VTERM_ATTR_URI:
    262    return VTERM_VALUETYPE_INT;
    263  case VTERM_ATTR_DIM:
    264    return VTERM_VALUETYPE_BOOL;
    265  case VTERM_ATTR_OVERLINE:
    266    return VTERM_VALUETYPE_BOOL;
    267 
    268  case VTERM_N_ATTRS:
    269    return 0;
    270  }
    271  return 0;  // UNREACHABLE
    272 }
    273 
    274 void vterm_scroll_rect(VTermRect rect, int downward, int rightward,
    275                       int (*moverect)(VTermRect src, VTermRect dest, void *user),
    276                       int (*eraserect)(VTermRect rect, int selective, void *user), void *user)
    277 {
    278  VTermRect src;
    279  VTermRect dest;
    280 
    281  if (abs(downward) >= rect.end_row - rect.start_row
    282      || abs(rightward) >= rect.end_col - rect.start_col) {
    283    // Scroll more than area; just erase the lot
    284    (*eraserect)(rect, 0, user);
    285    return;
    286  }
    287 
    288  if (rightward >= 0) {
    289    // rect: [XXX................]
    290    // src:     [----------------]
    291    // dest: [----------------]
    292    dest.start_col = rect.start_col;
    293    dest.end_col = rect.end_col - rightward;
    294    src.start_col = rect.start_col + rightward;
    295    src.end_col = rect.end_col;
    296  } else {
    297    // rect: [................XXX]
    298    // src:  [----------------]
    299    // dest:    [----------------]
    300    int leftward = -rightward;
    301    dest.start_col = rect.start_col + leftward;
    302    dest.end_col = rect.end_col;
    303    src.start_col = rect.start_col;
    304    src.end_col = rect.end_col - leftward;
    305  }
    306 
    307  if (downward >= 0) {
    308    dest.start_row = rect.start_row;
    309    dest.end_row = rect.end_row - downward;
    310    src.start_row = rect.start_row + downward;
    311    src.end_row = rect.end_row;
    312  } else {
    313    int upward = -downward;
    314    dest.start_row = rect.start_row + upward;
    315    dest.end_row = rect.end_row;
    316    src.start_row = rect.start_row;
    317    src.end_row = rect.end_row - upward;
    318  }
    319 
    320  if (moverect) {
    321    (*moverect)(dest, src, user);
    322  }
    323 
    324  if (downward > 0) {
    325    rect.start_row = rect.end_row - downward;
    326  } else if (downward < 0) {
    327    rect.end_row = rect.start_row - downward;
    328  }
    329 
    330  if (rightward > 0) {
    331    rect.start_col = rect.end_col - rightward;
    332  } else if (rightward < 0) {
    333    rect.end_col = rect.start_col - rightward;
    334  }
    335 
    336  (*eraserect)(rect, 0, user);
    337 }