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