xref: /vim-8.2.3635/src/libvterm/src/vterm.c (revision b5b49a3b)
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->parser.cbdata    = NULL;
54 
55   vt->parser.strbuffer_len = 64;
56   vt->parser.strbuffer_cur = 0;
57   vt->parser.strbuffer = vterm_allocator_malloc(vt, vt->parser.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->parser.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->parser.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 VTermValueType vterm_get_attr_type(VTermAttr attr)
261 {
262   switch(attr) {
263     case VTERM_ATTR_BOLD:       return VTERM_VALUETYPE_BOOL;
264     case VTERM_ATTR_UNDERLINE:  return VTERM_VALUETYPE_INT;
265     case VTERM_ATTR_ITALIC:     return VTERM_VALUETYPE_BOOL;
266     case VTERM_ATTR_BLINK:      return VTERM_VALUETYPE_BOOL;
267     case VTERM_ATTR_REVERSE:    return VTERM_VALUETYPE_BOOL;
268     case VTERM_ATTR_STRIKE:     return VTERM_VALUETYPE_BOOL;
269     case VTERM_ATTR_FONT:       return VTERM_VALUETYPE_INT;
270     case VTERM_ATTR_FOREGROUND: return VTERM_VALUETYPE_COLOR;
271     case VTERM_ATTR_BACKGROUND: return VTERM_VALUETYPE_COLOR;
272 
273     case VTERM_N_ATTRS: return 0;
274   }
275   return 0; /* UNREACHABLE */
276 }
277 
278 VTermValueType vterm_get_prop_type(VTermProp prop)
279 {
280   switch(prop) {
281     case VTERM_PROP_CURSORVISIBLE: return VTERM_VALUETYPE_BOOL;
282     case VTERM_PROP_CURSORBLINK:   return VTERM_VALUETYPE_BOOL;
283     case VTERM_PROP_ALTSCREEN:     return VTERM_VALUETYPE_BOOL;
284     case VTERM_PROP_TITLE:         return VTERM_VALUETYPE_STRING;
285     case VTERM_PROP_ICONNAME:      return VTERM_VALUETYPE_STRING;
286     case VTERM_PROP_REVERSE:       return VTERM_VALUETYPE_BOOL;
287     case VTERM_PROP_CURSORSHAPE:   return VTERM_VALUETYPE_INT;
288     case VTERM_PROP_MOUSE:         return VTERM_VALUETYPE_INT;
289     case VTERM_PROP_CURSORCOLOR:   return VTERM_VALUETYPE_STRING;
290 
291     case VTERM_N_PROPS: return 0;
292   }
293   return 0; /* UNREACHABLE */
294 }
295 
296 void vterm_scroll_rect(VTermRect rect,
297     int downward,
298     int rightward,
299     int (*moverect)(VTermRect src, VTermRect dest, void *user),
300     int (*eraserect)(VTermRect rect, int selective, void *user),
301     void *user)
302 {
303   VTermRect src;
304   VTermRect dest;
305 
306   if(abs(downward)  >= rect.end_row - rect.start_row ||
307      abs(rightward) >= rect.end_col - rect.start_col) {
308     /* Scroll more than area; just erase the lot */
309     (*eraserect)(rect, 0, user);
310     return;
311   }
312 
313   if(rightward >= 0) {
314     /* rect: [XXX................]
315      * src:     [----------------]
316      * dest: [----------------]
317      */
318     dest.start_col = rect.start_col;
319     dest.end_col   = rect.end_col   - rightward;
320     src.start_col  = rect.start_col + rightward;
321     src.end_col    = rect.end_col;
322   }
323   else {
324     /* rect: [................XXX]
325      * src:  [----------------]
326      * dest:    [----------------]
327      */
328     int leftward = -rightward;
329     dest.start_col = rect.start_col + leftward;
330     dest.end_col   = rect.end_col;
331     src.start_col  = rect.start_col;
332     src.end_col    = rect.end_col - leftward;
333   }
334 
335   if(downward >= 0) {
336     dest.start_row = rect.start_row;
337     dest.end_row   = rect.end_row   - downward;
338     src.start_row  = rect.start_row + downward;
339     src.end_row    = rect.end_row;
340   }
341   else {
342     int upward = -downward;
343     dest.start_row = rect.start_row + upward;
344     dest.end_row   = rect.end_row;
345     src.start_row  = rect.start_row;
346     src.end_row    = rect.end_row - upward;
347   }
348 
349   if(moverect)
350     (*moverect)(dest, src, user);
351 
352   if(downward > 0)
353     rect.start_row = rect.end_row - downward;
354   else if(downward < 0)
355     rect.end_row = rect.start_row - downward;
356 
357   if(rightward > 0)
358     rect.start_col = rect.end_col - rightward;
359   else if(rightward < 0)
360     rect.end_col = rect.start_col - rightward;
361 
362   (*eraserect)(rect, 0, user);
363 }
364 
365 void vterm_copy_cells(VTermRect dest,
366     VTermRect src,
367     void (*copycell)(VTermPos dest, VTermPos src, void *user),
368     void *user)
369 {
370   int downward  = src.start_row - dest.start_row;
371   int rightward = src.start_col - dest.start_col;
372 
373   int init_row, test_row, init_col, test_col;
374   int inc_row, inc_col;
375 
376   VTermPos pos;
377 
378   if(downward < 0) {
379     init_row = dest.end_row - 1;
380     test_row = dest.start_row - 1;
381     inc_row = -1;
382   }
383   else /* downward >= 0 */ {
384     init_row = dest.start_row;
385     test_row = dest.end_row;
386     inc_row = +1;
387   }
388 
389   if(rightward < 0) {
390     init_col = dest.end_col - 1;
391     test_col = dest.start_col - 1;
392     inc_col = -1;
393   }
394   else /* rightward >= 0 */ {
395     init_col = dest.start_col;
396     test_col = dest.end_col;
397     inc_col = +1;
398   }
399 
400   for(pos.row = init_row; pos.row != test_row; pos.row += inc_row)
401     for(pos.col = init_col; pos.col != test_col; pos.col += inc_col) {
402       VTermPos srcpos;
403       srcpos.row = pos.row + downward;
404       srcpos.col = pos.col + rightward;
405       (*copycell)(pos, srcpos, user);
406     }
407 }
408