Home | History | Annotate | Download | only in src
      1 #include "vterm_internal.h"
      2 
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <stdarg.h>
      6 #include <string.h>
      7 
      8 /*****************
      9  * API functions *
     10  *****************/
     11 
     12 static void *default_malloc(size_t size, void *allocdata)
     13 {
     14   void *ptr = malloc(size);
     15   if(ptr)
     16     memset(ptr, 0, size);
     17   return ptr;
     18 }
     19 
     20 static void default_free(void *ptr, void *allocdata)
     21 {
     22   free(ptr);
     23 }
     24 
     25 static VTermAllocatorFunctions default_allocator = {
     26   .malloc = &default_malloc,
     27   .free   = &default_free,
     28 };
     29 
     30 VTerm *vterm_new(int rows, int cols)
     31 {
     32   return vterm_new_with_allocator(rows, cols, &default_allocator, NULL);
     33 }
     34 
     35 VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata)
     36 {
     37   /* Need to bootstrap using the allocator function directly */
     38   VTerm *vt = (*funcs->malloc)(sizeof(VTerm), allocdata);
     39 
     40   vt->allocator = funcs;
     41   vt->allocdata = allocdata;
     42 
     43   vt->rows = rows;
     44   vt->cols = cols;
     45 
     46   vt->parser.state = NORMAL;
     47 
     48   vt->parser.callbacks = NULL;
     49   vt->parser.cbdata    = NULL;
     50 
     51   vt->parser.strbuffer_len = 64;
     52   vt->parser.strbuffer_cur = 0;
     53   vt->parser.strbuffer = vterm_allocator_malloc(vt, vt->parser.strbuffer_len);
     54 
     55   vt->outbuffer_len = 64;
     56   vt->outbuffer_cur = 0;
     57   vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
     58 
     59   return vt;
     60 }
     61 
     62 void vterm_free(VTerm *vt)
     63 {
     64   if(vt->screen)
     65     vterm_screen_free(vt->screen);
     66 
     67   if(vt->state)
     68     vterm_state_free(vt->state);
     69 
     70   vterm_allocator_free(vt, vt->parser.strbuffer);
     71   vterm_allocator_free(vt, vt->outbuffer);
     72 
     73   vterm_allocator_free(vt, vt);
     74 }
     75 
     76 INTERNAL void *vterm_allocator_malloc(VTerm *vt, size_t size)
     77 {
     78   return (*vt->allocator->malloc)(size, vt->allocdata);
     79 }
     80 
     81 INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr)
     82 {
     83   (*vt->allocator->free)(ptr, vt->allocdata);
     84 }
     85 
     86 void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
     87 {
     88   if(rowsp)
     89     *rowsp = vt->rows;
     90   if(colsp)
     91     *colsp = vt->cols;
     92 }
     93 
     94 void vterm_set_size(VTerm *vt, int rows, int cols)
     95 {
     96   vt->rows = rows;
     97   vt->cols = cols;
     98 
     99   if(vt->parser.callbacks && vt->parser.callbacks->resize)
    100     (*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata);
    101 }
    102 
    103 int vterm_get_utf8(const VTerm *vt)
    104 {
    105   return vt->mode.utf8;
    106 }
    107 
    108 void vterm_set_utf8(VTerm *vt, int is_utf8)
    109 {
    110   vt->mode.utf8 = is_utf8;
    111 }
    112 
    113 INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
    114 {
    115   if(len > vt->outbuffer_len - vt->outbuffer_cur) {
    116     DEBUG_LOG("vterm_push_output(): buffer overflow; truncating output\n");
    117     len = vt->outbuffer_len - vt->outbuffer_cur;
    118   }
    119 
    120   memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len);
    121   vt->outbuffer_cur += len;
    122 }
    123 
    124 static int outbuffer_is_full(VTerm *vt)
    125 {
    126   return vt->outbuffer_cur >= vt->outbuffer_len - 1;
    127 }
    128 
    129 INTERNAL void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args)
    130 {
    131   if(outbuffer_is_full(vt)) {
    132     DEBUG_LOG("vterm_push_output(): buffer overflow; truncating output\n");
    133     return;
    134   }
    135 
    136   int written = vsnprintf(vt->outbuffer + vt->outbuffer_cur,
    137       vt->outbuffer_len - vt->outbuffer_cur,
    138       format, args);
    139 
    140   if(written == vt->outbuffer_len - vt->outbuffer_cur) {
    141     /* output was truncated */
    142     vt->outbuffer_cur = vt->outbuffer_len - 1;
    143   }
    144   else
    145     vt->outbuffer_cur += written;
    146 }
    147 
    148 INTERNAL void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
    149 {
    150   va_list args;
    151   va_start(args, format);
    152   vterm_push_output_vsprintf(vt, format, args);
    153   va_end(args);
    154 }
    155 
    156 INTERNAL void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...)
    157 {
    158   size_t orig_cur = vt->outbuffer_cur;
    159 
    160   if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
    161     vterm_push_output_sprintf(vt, ESC_S "%c", ctrl - 0x40);
    162   else
    163     vterm_push_output_sprintf(vt, "%c", ctrl);
    164 
    165   va_list args;
    166   va_start(args, fmt);
    167   vterm_push_output_vsprintf(vt, fmt, args);
    168   va_end(args);
    169 
    170   if(outbuffer_is_full(vt))
    171     vt->outbuffer_cur = orig_cur;
    172 }
    173 
    174 INTERNAL void vterm_push_output_sprintf_dcs(VTerm *vt, const char *fmt, ...)
    175 {
    176   size_t orig_cur = vt->outbuffer_cur;
    177 
    178   if(!vt->mode.ctrl8bit)
    179     vterm_push_output_sprintf(vt, ESC_S "%c", C1_DCS - 0x40);
    180   else
    181     vterm_push_output_sprintf(vt, "%c", C1_DCS);
    182 
    183   va_list args;
    184   va_start(args, fmt);
    185   vterm_push_output_vsprintf(vt, fmt, args);
    186   va_end(args);
    187 
    188   vterm_push_output_sprintf_ctrl(vt, C1_ST, "");
    189 
    190   if(outbuffer_is_full(vt))
    191     vt->outbuffer_cur = orig_cur;
    192 }
    193 
    194 size_t vterm_output_get_buffer_size(const VTerm *vt)
    195 {
    196   return vt->outbuffer_len;
    197 }
    198 
    199 size_t vterm_output_get_buffer_current(const VTerm *vt)
    200 {
    201   return vt->outbuffer_cur;
    202 }
    203 
    204 size_t vterm_output_get_buffer_remaining(const VTerm *vt)
    205 {
    206   return vt->outbuffer_len - vt->outbuffer_cur;
    207 }
    208 
    209 size_t vterm_output_read(VTerm *vt, char *buffer, size_t len)
    210 {
    211   if(len > vt->outbuffer_cur)
    212     len = vt->outbuffer_cur;
    213 
    214   memcpy(buffer, vt->outbuffer, len);
    215 
    216   if(len < vt->outbuffer_cur)
    217     memmove(vt->outbuffer, vt->outbuffer + len, vt->outbuffer_cur - len);
    218 
    219   vt->outbuffer_cur -= len;
    220 
    221   return len;
    222 }
    223 
    224 VTermValueType vterm_get_attr_type(VTermAttr attr)
    225 {
    226   switch(attr) {
    227     case VTERM_ATTR_BOLD:       return VTERM_VALUETYPE_BOOL;
    228     case VTERM_ATTR_UNDERLINE:  return VTERM_VALUETYPE_INT;
    229     case VTERM_ATTR_ITALIC:     return VTERM_VALUETYPE_BOOL;
    230     case VTERM_ATTR_BLINK:      return VTERM_VALUETYPE_BOOL;
    231     case VTERM_ATTR_REVERSE:    return VTERM_VALUETYPE_BOOL;
    232     case VTERM_ATTR_STRIKE:     return VTERM_VALUETYPE_BOOL;
    233     case VTERM_ATTR_FONT:       return VTERM_VALUETYPE_INT;
    234     case VTERM_ATTR_FOREGROUND: return VTERM_VALUETYPE_COLOR;
    235     case VTERM_ATTR_BACKGROUND: return VTERM_VALUETYPE_COLOR;
    236 
    237     case VTERM_N_ATTRS: return 0;
    238   }
    239   return 0; /* UNREACHABLE */
    240 }
    241 
    242 VTermValueType vterm_get_prop_type(VTermProp prop)
    243 {
    244   switch(prop) {
    245     case VTERM_PROP_CURSORVISIBLE: return VTERM_VALUETYPE_BOOL;
    246     case VTERM_PROP_CURSORBLINK:   return VTERM_VALUETYPE_BOOL;
    247     case VTERM_PROP_ALTSCREEN:     return VTERM_VALUETYPE_BOOL;
    248     case VTERM_PROP_TITLE:         return VTERM_VALUETYPE_STRING;
    249     case VTERM_PROP_ICONNAME:      return VTERM_VALUETYPE_STRING;
    250     case VTERM_PROP_REVERSE:       return VTERM_VALUETYPE_BOOL;
    251     case VTERM_PROP_CURSORSHAPE:   return VTERM_VALUETYPE_INT;
    252     case VTERM_PROP_MOUSE:         return VTERM_VALUETYPE_INT;
    253 
    254     case VTERM_N_PROPS: return 0;
    255   }
    256   return 0; /* UNREACHABLE */
    257 }
    258 
    259 void vterm_scroll_rect(VTermRect rect,
    260     int downward,
    261     int rightward,
    262     int (*moverect)(VTermRect src, VTermRect dest, void *user),
    263     int (*eraserect)(VTermRect rect, int selective, void *user),
    264     void *user)
    265 {
    266   VTermRect src;
    267   VTermRect dest;
    268 
    269   if(abs(downward)  >= rect.end_row - rect.start_row ||
    270      abs(rightward) >= rect.end_col - rect.start_col) {
    271     /* Scroll more than area; just erase the lot */
    272     (*eraserect)(rect, 0, user);
    273     return;
    274   }
    275 
    276   if(rightward >= 0) {
    277     /* rect: [XXX................]
    278      * src:     [----------------]
    279      * dest: [----------------]
    280      */
    281     dest.start_col = rect.start_col;
    282     dest.end_col   = rect.end_col   - rightward;
    283     src.start_col  = rect.start_col + rightward;
    284     src.end_col    = rect.end_col;
    285   }
    286   else {
    287     /* rect: [................XXX]
    288      * src:  [----------------]
    289      * dest:    [----------------]
    290      */
    291     int leftward = -rightward;
    292     dest.start_col = rect.start_col + leftward;
    293     dest.end_col   = rect.end_col;
    294     src.start_col  = rect.start_col;
    295     src.end_col    = rect.end_col - leftward;
    296   }
    297 
    298   if(downward >= 0) {
    299     dest.start_row = rect.start_row;
    300     dest.end_row   = rect.end_row   - downward;
    301     src.start_row  = rect.start_row + downward;
    302     src.end_row    = rect.end_row;
    303   }
    304   else {
    305     int upward = -downward;
    306     dest.start_row = rect.start_row + upward;
    307     dest.end_row   = rect.end_row;
    308     src.start_row  = rect.start_row;
    309     src.end_row    = rect.end_row - upward;
    310   }
    311 
    312   if(moverect)
    313     (*moverect)(dest, src, user);
    314 
    315   if(downward > 0)
    316     rect.start_row = rect.end_row - downward;
    317   else if(downward < 0)
    318     rect.end_row = rect.start_row - downward;
    319 
    320   if(rightward > 0)
    321     rect.start_col = rect.end_col - rightward;
    322   else if(rightward < 0)
    323     rect.end_col = rect.start_col - rightward;
    324 
    325   (*eraserect)(rect, 0, user);
    326 }
    327 
    328 void vterm_copy_cells(VTermRect dest,
    329     VTermRect src,
    330     void (*copycell)(VTermPos dest, VTermPos src, void *user),
    331     void *user)
    332 {
    333   int downward  = src.start_row - dest.start_row;
    334   int rightward = src.start_col - dest.start_col;
    335 
    336   int init_row, test_row, init_col, test_col;
    337   int inc_row, inc_col;
    338 
    339   if(downward < 0) {
    340     init_row = dest.end_row - 1;
    341     test_row = dest.start_row - 1;
    342     inc_row = -1;
    343   }
    344   else /* downward >= 0 */ {
    345     init_row = dest.start_row;
    346     test_row = dest.end_row;
    347     inc_row = +1;
    348   }
    349 
    350   if(rightward < 0) {
    351     init_col = dest.end_col - 1;
    352     test_col = dest.start_col - 1;
    353     inc_col = -1;
    354   }
    355   else /* rightward >= 0 */ {
    356     init_col = dest.start_col;
    357     test_col = dest.end_col;
    358     inc_col = +1;
    359   }
    360 
    361   VTermPos pos;
    362   for(pos.row = init_row; pos.row != test_row; pos.row += inc_row)
    363     for(pos.col = init_col; pos.col != test_col; pos.col += inc_col) {
    364       VTermPos srcpos = { pos.row + downward, pos.col + rightward };
    365       (*copycell)(pos, srcpos, user);
    366     }
    367 }
    368