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