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