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