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