1 #include "vterm_internal.h"
2
3 #include <stdio.h>
4 #include <string.h>
5
6 #define strneq(a,b,n) (strncmp(a,b,n)==0)
7
8 #if defined(DEBUG) && DEBUG > 1
9 # define DEBUG_GLYPH_COMBINE
10 #endif
11
12 static int on_resize(int rows, int cols, void *user);
13
14 /* Some convenient wrappers to make callback functions easier */
15
putglyph(VTermState * state,const uint32_t chars[],int width,VTermPos pos)16 static void putglyph(VTermState *state, const uint32_t chars[], int width, VTermPos pos)
17 {
18 VTermGlyphInfo info;
19
20 info.chars = chars;
21 info.width = width;
22 info.protected_cell = state->protected_cell;
23 info.dwl = state->lineinfo[pos.row].doublewidth;
24 info.dhl = state->lineinfo[pos.row].doubleheight;
25
26 if(state->callbacks && state->callbacks->putglyph)
27 if((*state->callbacks->putglyph)(&info, pos, state->cbdata))
28 return;
29
30 DEBUG_LOG3("libvterm: Unhandled putglyph U+%04x at (%d,%d)\n", chars[0], pos.col, pos.row);
31 }
32
updatecursor(VTermState * state,VTermPos * oldpos,int cancel_phantom)33 static void updatecursor(VTermState *state, VTermPos *oldpos, int cancel_phantom)
34 {
35 if(state->pos.col == oldpos->col && state->pos.row == oldpos->row)
36 return;
37
38 if(cancel_phantom)
39 state->at_phantom = 0;
40
41 if(state->callbacks && state->callbacks->movecursor)
42 if((*state->callbacks->movecursor)(state->pos, *oldpos, state->mode.cursor_visible, state->cbdata))
43 return;
44 }
45
erase(VTermState * state,VTermRect rect,int selective)46 static void erase(VTermState *state, VTermRect rect, int selective)
47 {
48 if(rect.end_col == state->cols) {
49 int row;
50 /* If we're erasing the final cells of any lines, cancel the continuation
51 * marker on the subsequent line
52 */
53 for(row = rect.start_row + 1; row < rect.end_row + 1 && row < state->rows; row++)
54 state->lineinfo[row].continuation = 0;
55 }
56
57 if(state->callbacks && state->callbacks->erase)
58 if((*state->callbacks->erase)(rect, selective, state->cbdata))
59 return;
60 }
61
vterm_state_new(VTerm * vt)62 static VTermState *vterm_state_new(VTerm *vt)
63 {
64 VTermState *state = vterm_allocator_malloc(vt, sizeof(VTermState));
65
66 if (state == NULL)
67 return NULL;
68 state->vt = vt;
69
70 state->rows = vt->rows;
71 state->cols = vt->cols;
72
73 state->mouse_col = 0;
74 state->mouse_row = 0;
75 state->mouse_buttons = 0;
76
77 state->mouse_protocol = MOUSE_X10;
78
79 state->callbacks = NULL;
80 state->cbdata = NULL;
81
82 vterm_state_newpen(state);
83
84 state->bold_is_highbright = 0;
85
86 state->combine_chars_size = 16;
87 state->combine_chars = vterm_allocator_malloc(state->vt, state->combine_chars_size * sizeof(state->combine_chars[0]));
88
89 state->tabstops = vterm_allocator_malloc(state->vt, (state->cols + 7) / 8);
90
91 state->lineinfos[BUFIDX_PRIMARY] = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo));
92 /* TODO: Make an 'enable' function */
93 state->lineinfos[BUFIDX_ALTSCREEN] = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo));
94 state->lineinfo = state->lineinfos[BUFIDX_PRIMARY];
95
96 state->encoding_utf8.enc = vterm_lookup_encoding(ENC_UTF8, 'u');
97 if(state->encoding_utf8.enc->init)
98 (*state->encoding_utf8.enc->init)(state->encoding_utf8.enc, state->encoding_utf8.data);
99
100 return state;
101 }
102
vterm_state_free(VTermState * state)103 INTERNAL void vterm_state_free(VTermState *state)
104 {
105 vterm_allocator_free(state->vt, state->tabstops);
106 vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_PRIMARY]);
107 if(state->lineinfos[BUFIDX_ALTSCREEN])
108 vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_ALTSCREEN]);
109 vterm_allocator_free(state->vt, state->combine_chars);
110 vterm_allocator_free(state->vt, state);
111 }
112
scroll(VTermState * state,VTermRect rect,int downward,int rightward)113 static void scroll(VTermState *state, VTermRect rect, int downward, int rightward)
114 {
115 int rows;
116 int cols;
117 if(!downward && !rightward)
118 return;
119
120 rows = rect.end_row - rect.start_row;
121 if(downward > rows)
122 downward = rows;
123 else if(downward < -rows)
124 downward = -rows;
125
126 cols = rect.end_col - rect.start_col;
127 if(rightward > cols)
128 rightward = cols;
129 else if(rightward < -cols)
130 rightward = -cols;
131
132 // Update lineinfo if full line
133 if(rect.start_col == 0 && rect.end_col == state->cols && rightward == 0) {
134 int height = rect.end_row - rect.start_row - abs(downward);
135 int row;
136 VTermLineInfo zeroLineInfo = {0x0};
137
138 if(downward > 0) {
139 memmove(state->lineinfo + rect.start_row,
140 state->lineinfo + rect.start_row + downward,
141 height * sizeof(state->lineinfo[0]));
142 for(row = rect.end_row - downward; row < rect.end_row; row++)
143 state->lineinfo[row] = zeroLineInfo;
144 }
145 else {
146 memmove(state->lineinfo + rect.start_row - downward,
147 state->lineinfo + rect.start_row,
148 height * sizeof(state->lineinfo[0]));
149 for(row = rect.start_row; row < rect.start_row - downward; row++)
150 state->lineinfo[row] = zeroLineInfo;
151 }
152 }
153
154 if(state->callbacks && state->callbacks->scrollrect)
155 if((*state->callbacks->scrollrect)(rect, downward, rightward, state->cbdata))
156 return;
157
158 if(state->callbacks)
159 vterm_scroll_rect(rect, downward, rightward,
160 state->callbacks->moverect, state->callbacks->erase, state->cbdata);
161 }
162
linefeed(VTermState * state)163 static void linefeed(VTermState *state)
164 {
165 if(state->pos.row == SCROLLREGION_BOTTOM(state) - 1) {
166 VTermRect rect;
167 rect.start_row = state->scrollregion_top;
168 rect.end_row = SCROLLREGION_BOTTOM(state);
169 rect.start_col = SCROLLREGION_LEFT(state);
170 rect.end_col = SCROLLREGION_RIGHT(state);
171
172 scroll(state, rect, 1, 0);
173 }
174 else if(state->pos.row < state->rows-1)
175 state->pos.row++;
176 }
177
grow_combine_buffer(VTermState * state)178 static void grow_combine_buffer(VTermState *state)
179 {
180 size_t new_size = state->combine_chars_size * 2;
181 uint32_t *new_chars = vterm_allocator_malloc(state->vt, new_size * sizeof(new_chars[0]));
182
183 memcpy(new_chars, state->combine_chars, state->combine_chars_size * sizeof(new_chars[0]));
184
185 vterm_allocator_free(state->vt, state->combine_chars);
186
187 state->combine_chars = new_chars;
188 state->combine_chars_size = new_size;
189 }
190
set_col_tabstop(VTermState * state,int col)191 static void set_col_tabstop(VTermState *state, int col)
192 {
193 unsigned char mask = 1 << (col & 7);
194 state->tabstops[col >> 3] |= mask;
195 }
196
clear_col_tabstop(VTermState * state,int col)197 static void clear_col_tabstop(VTermState *state, int col)
198 {
199 unsigned char mask = 1 << (col & 7);
200 state->tabstops[col >> 3] &= ~mask;
201 }
202
is_col_tabstop(VTermState * state,int col)203 static int is_col_tabstop(VTermState *state, int col)
204 {
205 unsigned char mask = 1 << (col & 7);
206 return state->tabstops[col >> 3] & mask;
207 }
208
is_cursor_in_scrollregion(const VTermState * state)209 static int is_cursor_in_scrollregion(const VTermState *state)
210 {
211 if(state->pos.row < state->scrollregion_top ||
212 state->pos.row >= SCROLLREGION_BOTTOM(state))
213 return 0;
214 if(state->pos.col < SCROLLREGION_LEFT(state) ||
215 state->pos.col >= SCROLLREGION_RIGHT(state))
216 return 0;
217
218 return 1;
219 }
220
tab(VTermState * state,int count,int direction)221 static void tab(VTermState *state, int count, int direction)
222 {
223 while(count > 0) {
224 if(direction > 0) {
225 if(state->pos.col >= THISROWWIDTH(state)-1)
226 return;
227
228 state->pos.col++;
229 }
230 else if(direction < 0) {
231 if(state->pos.col < 1)
232 return;
233
234 state->pos.col--;
235 }
236
237 if(is_col_tabstop(state, state->pos.col))
238 count--;
239 }
240 }
241
242 #define NO_FORCE 0
243 #define FORCE 1
244
245 #define DWL_OFF 0
246 #define DWL_ON 1
247
248 #define DHL_OFF 0
249 #define DHL_TOP 1
250 #define DHL_BOTTOM 2
251
set_lineinfo(VTermState * state,int row,int force,int dwl,int dhl)252 static void set_lineinfo(VTermState *state, int row, int force, int dwl, int dhl)
253 {
254 VTermLineInfo info = state->lineinfo[row];
255
256 if(dwl == DWL_OFF)
257 info.doublewidth = DWL_OFF;
258 else if(dwl == DWL_ON)
259 info.doublewidth = DWL_ON;
260 // else -1 to ignore
261
262 if(dhl == DHL_OFF)
263 info.doubleheight = DHL_OFF;
264 else if(dhl == DHL_TOP)
265 info.doubleheight = DHL_TOP;
266 else if(dhl == DHL_BOTTOM)
267 info.doubleheight = DHL_BOTTOM;
268
269 if((state->callbacks &&
270 state->callbacks->setlineinfo &&
271 (*state->callbacks->setlineinfo)(row, &info, state->lineinfo + row, state->cbdata))
272 || force)
273 state->lineinfo[row] = info;
274 }
275
on_text(const char bytes[],size_t len,void * user)276 static int on_text(const char bytes[], size_t len, void *user)
277 {
278 VTermState *state = user;
279 uint32_t *codepoints;
280 int npoints = 0;
281 size_t eaten = 0;
282 VTermEncodingInstance *encoding;
283 int i = 0;
284
285 VTermPos oldpos = state->pos;
286
287 // We'll have at most len codepoints, plus one from a previous incomplete
288 // sequence.
289 codepoints = vterm_allocator_malloc(state->vt, (len + 1) * sizeof(uint32_t));
290 if (codepoints == NULL)
291 return 0;
292
293 encoding =
294 state->gsingle_set ? &state->encoding[state->gsingle_set] :
295 !(bytes[eaten] & 0x80) ? &state->encoding[state->gl_set] :
296 state->vt->mode.utf8 ? &state->encoding_utf8 :
297 &state->encoding[state->gr_set];
298
299 (*encoding->enc->decode)(encoding->enc, encoding->data,
300 codepoints, &npoints, state->gsingle_set ? 1 : (int)len,
301 bytes, &eaten, len);
302
303 /* There's a chance an encoding (e.g. UTF-8) hasn't found enough bytes yet
304 * for even a single codepoint
305 */
306 if(!npoints)
307 {
308 vterm_allocator_free(state->vt, codepoints);
309 return (int)eaten;
310 }
311
312 if(state->gsingle_set && npoints)
313 state->gsingle_set = 0;
314
315 /* This is a combining char. that needs to be merged with the previous
316 * glyph output */
317 if(vterm_unicode_is_combining(codepoints[i])) {
318 /* See if the cursor has moved since */
319 if(state->pos.row == state->combine_pos.row && state->pos.col == state->combine_pos.col + state->combine_width) {
320 #ifdef DEBUG_GLYPH_COMBINE
321 int printpos;
322 printf("DEBUG: COMBINING SPLIT GLYPH of chars {");
323 for(printpos = 0; state->combine_chars[printpos]; printpos++)
324 printf("U+%04x ", state->combine_chars[printpos]);
325 printf("} + {");
326 #endif
327
328 /* Find where we need to append these combining chars */
329 int saved_i = 0;
330 while(state->combine_chars[saved_i])
331 saved_i++;
332
333 /* Add extra ones */
334 while(i < npoints && vterm_unicode_is_combining(codepoints[i])) {
335 if(saved_i >= (int)state->combine_chars_size)
336 grow_combine_buffer(state);
337 state->combine_chars[saved_i++] = codepoints[i++];
338 }
339 if(saved_i >= (int)state->combine_chars_size)
340 grow_combine_buffer(state);
341 state->combine_chars[saved_i] = 0;
342
343 #ifdef DEBUG_GLYPH_COMBINE
344 for(; state->combine_chars[printpos]; printpos++)
345 printf("U+%04x ", state->combine_chars[printpos]);
346 printf("}\n");
347 #endif
348
349 /* Now render it */
350 putglyph(state, state->combine_chars, state->combine_width, state->combine_pos);
351 }
352 else {
353 DEBUG_LOG("libvterm: TODO: Skip over split char+combining\n");
354 }
355 }
356
357 for(; i < npoints; i++) {
358 // Try to find combining characters following this
359 int glyph_starts = i;
360 int glyph_ends;
361 int width = 0;
362 uint32_t *chars;
363
364 for(glyph_ends = i + 1; glyph_ends < npoints; glyph_ends++)
365 if(!vterm_unicode_is_combining(codepoints[glyph_ends]))
366 break;
367
368 chars = vterm_allocator_malloc(state->vt, (glyph_ends - glyph_starts + 1) * sizeof(uint32_t));
369 if (chars == NULL)
370 break;
371
372 for( ; i < glyph_ends; i++) {
373 int this_width;
374 if(vterm_get_special_pty_type() == 2) {
375 state->vt->in_backspace -= (state->vt->in_backspace > 0) ? 1 : 0;
376 if(state->vt->in_backspace == 1)
377 codepoints[i] = 0; // codepoints under this condition must be 0
378 }
379 chars[i - glyph_starts] = codepoints[i];
380 this_width = vterm_unicode_width(codepoints[i]);
381 #ifdef DEBUG
382 if(this_width < 0) {
383 fprintf(stderr, "Text with negative-width codepoint U+%04x\n", codepoints[i]);
384 abort();
385 }
386 #endif
387 if (i == glyph_starts || this_width > width)
388 width = this_width;
389 }
390
391 chars[glyph_ends - glyph_starts] = 0;
392 i--;
393
394 #ifdef DEBUG_GLYPH_COMBINE
395 int printpos;
396 printf("DEBUG: COMBINED GLYPH of %d chars {", glyph_ends - glyph_starts);
397 for(printpos = 0; printpos < glyph_ends - glyph_starts; printpos++)
398 printf("U+%04x ", chars[printpos]);
399 printf("}, onscreen width %d\n", width);
400 #endif
401
402 if(state->at_phantom || state->pos.col + width > THISROWWIDTH(state)) {
403 linefeed(state);
404 state->pos.col = 0;
405 state->at_phantom = 0;
406 state->lineinfo[state->pos.row].continuation = 1;
407 }
408
409 if(state->mode.insert) {
410 // TODO: This will be a little inefficient for large bodies of text, as
411 // it'll have to 'ICH' effectively before every glyph. We should scan
412 // ahead and ICH as many times as required
413 VTermRect rect;
414 rect.start_row = state->pos.row;
415 rect.end_row = state->pos.row + 1;
416 rect.start_col = state->pos.col;
417 rect.end_col = THISROWWIDTH(state);
418 scroll(state, rect, 0, -1);
419 }
420
421 putglyph(state, chars, width, state->pos);
422
423 if(i == npoints - 1) {
424 /* End of the buffer. Save the chars in case we have to combine with
425 * more on the next call */
426 int save_i;
427 for(save_i = 0; chars[save_i]; save_i++) {
428 if(save_i >= (int)state->combine_chars_size)
429 grow_combine_buffer(state);
430 state->combine_chars[save_i] = chars[save_i];
431 }
432 if(save_i >= (int)state->combine_chars_size)
433 grow_combine_buffer(state);
434 state->combine_chars[save_i] = 0;
435 state->combine_width = width;
436 state->combine_pos = state->pos;
437 }
438
439 if(state->pos.col + width >= THISROWWIDTH(state)) {
440 if(state->mode.autowrap)
441 state->at_phantom = 1;
442 }
443 else {
444 state->pos.col += width;
445 }
446 vterm_allocator_free(state->vt, chars);
447 }
448
449 updatecursor(state, &oldpos, 0);
450
451 #ifdef DEBUG
452 if(state->pos.row < 0 || state->pos.row >= state->rows ||
453 state->pos.col < 0 || state->pos.col >= state->cols) {
454 fprintf(stderr, "Position out of bounds after text: (%d,%d)\n",
455 state->pos.row, state->pos.col);
456 abort();
457 }
458 #endif
459
460 vterm_allocator_free(state->vt, codepoints);
461 return (int)eaten;
462 }
463
on_control(unsigned char control,void * user)464 static int on_control(unsigned char control, void *user)
465 {
466 VTermState *state = user;
467
468 VTermPos oldpos = state->pos;
469
470 VTermScreenCell cell;
471
472 // Preparing to see the leading byte
473 VTermPos leadpos = state->pos;
474 leadpos.col -= (leadpos.col >= 2 ? 2 : 0);
475
476 switch(control) {
477 case 0x07: // BEL - ECMA-48 8.3.3
478 if(state->callbacks && state->callbacks->bell)
479 (*state->callbacks->bell)(state->cbdata);
480 break;
481
482 case 0x08: // BS - ECMA-48 8.3.5
483 if(state->pos.col > 0)
484 state->pos.col--;
485 if(vterm_get_special_pty_type() == 2) {
486 // In 2 cell letters, go back 2 cells
487 vterm_screen_get_cell(state->vt->screen, leadpos, &cell);
488 if(vterm_unicode_width(cell.chars[0]) == 2)
489 state->pos.col--;
490 }
491 break;
492
493 case 0x09: // HT - ECMA-48 8.3.60
494 tab(state, 1, +1);
495 break;
496
497 case 0x0a: // LF - ECMA-48 8.3.74
498 case 0x0b: // VT
499 case 0x0c: // FF
500 linefeed(state);
501 if(state->mode.newline)
502 state->pos.col = 0;
503 break;
504
505 case 0x0d: // CR - ECMA-48 8.3.15
506 state->pos.col = 0;
507 break;
508
509 case 0x0e: // LS1 - ECMA-48 8.3.76
510 state->gl_set = 1;
511 break;
512
513 case 0x0f: // LS0 - ECMA-48 8.3.75
514 state->gl_set = 0;
515 break;
516
517 case 0x84: // IND - DEPRECATED but implemented for completeness
518 linefeed(state);
519 break;
520
521 case 0x85: // NEL - ECMA-48 8.3.86
522 linefeed(state);
523 state->pos.col = 0;
524 break;
525
526 case 0x88: // HTS - ECMA-48 8.3.62
527 set_col_tabstop(state, state->pos.col);
528 break;
529
530 case 0x8d: // RI - ECMA-48 8.3.104
531 if(state->pos.row == state->scrollregion_top) {
532 VTermRect rect;
533 rect.start_row = state->scrollregion_top;
534 rect.end_row = SCROLLREGION_BOTTOM(state);
535 rect.start_col = SCROLLREGION_LEFT(state);
536 rect.end_col = SCROLLREGION_RIGHT(state);
537
538 scroll(state, rect, -1, 0);
539 }
540 else if(state->pos.row > 0)
541 state->pos.row--;
542 break;
543
544 case 0x8e: // SS2 - ECMA-48 8.3.141
545 state->gsingle_set = 2;
546 break;
547
548 case 0x8f: // SS3 - ECMA-48 8.3.142
549 state->gsingle_set = 3;
550 break;
551
552 default:
553 if(state->fallbacks && state->fallbacks->control)
554 if((*state->fallbacks->control)(control, state->fbdata))
555 return 1;
556
557 return 0;
558 }
559
560 updatecursor(state, &oldpos, 1);
561
562 #ifdef DEBUG
563 if(state->pos.row < 0 || state->pos.row >= state->rows ||
564 state->pos.col < 0 || state->pos.col >= state->cols) {
565 fprintf(stderr, "Position out of bounds after Ctrl %02x: (%d,%d)\n",
566 control, state->pos.row, state->pos.col);
567 abort();
568 }
569 #endif
570
571 return 1;
572 }
573
settermprop_bool(VTermState * state,VTermProp prop,int v)574 static int settermprop_bool(VTermState *state, VTermProp prop, int v)
575 {
576 VTermValue val;
577 val.boolean = v;
578 return vterm_state_set_termprop(state, prop, &val);
579 }
580
settermprop_int(VTermState * state,VTermProp prop,int v)581 static int settermprop_int(VTermState *state, VTermProp prop, int v)
582 {
583 VTermValue val;
584 val.number = v;
585 return vterm_state_set_termprop(state, prop, &val);
586 }
587
settermprop_string(VTermState * state,VTermProp prop,VTermStringFragment frag)588 static int settermprop_string(VTermState *state, VTermProp prop, VTermStringFragment frag)
589 {
590 VTermValue val;
591
592 val.string = frag;
593 return vterm_state_set_termprop(state, prop, &val);
594 }
595
savecursor(VTermState * state,int save)596 static void savecursor(VTermState *state, int save)
597 {
598 if(save) {
599 state->saved.pos = state->pos;
600 state->saved.mode.cursor_visible = state->mode.cursor_visible;
601 state->saved.mode.cursor_blink = state->mode.cursor_blink;
602 state->saved.mode.cursor_shape = state->mode.cursor_shape;
603
604 vterm_state_savepen(state, 1);
605 }
606 else {
607 VTermPos oldpos = state->pos;
608
609 state->pos = state->saved.pos;
610
611 settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, state->saved.mode.cursor_visible);
612 settermprop_bool(state, VTERM_PROP_CURSORBLINK, state->saved.mode.cursor_blink);
613 settermprop_int (state, VTERM_PROP_CURSORSHAPE, state->saved.mode.cursor_shape);
614
615 vterm_state_savepen(state, 0);
616
617 updatecursor(state, &oldpos, 1);
618 }
619 }
620
on_escape(const char * bytes,size_t len,void * user)621 static int on_escape(const char *bytes, size_t len, void *user)
622 {
623 VTermState *state = user;
624
625 /* Easier to decode this from the first byte, even though the final
626 * byte terminates it
627 */
628 switch(bytes[0]) {
629 case ' ':
630 if(len != 2)
631 return 0;
632
633 switch(bytes[1]) {
634 case 'F': // S7C1T
635 state->vt->mode.ctrl8bit = 0;
636 break;
637
638 case 'G': // S8C1T
639 state->vt->mode.ctrl8bit = 1;
640 break;
641
642 default:
643 return 0;
644 }
645 return 2;
646
647 case '#':
648 if(len != 2)
649 return 0;
650
651 switch(bytes[1]) {
652 case '3': // DECDHL top
653 if(state->mode.leftrightmargin)
654 break;
655 set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_TOP);
656 break;
657
658 case '4': // DECDHL bottom
659 if(state->mode.leftrightmargin)
660 break;
661 set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_BOTTOM);
662 break;
663
664 case '5': // DECSWL
665 if(state->mode.leftrightmargin)
666 break;
667 set_lineinfo(state, state->pos.row, NO_FORCE, DWL_OFF, DHL_OFF);
668 break;
669
670 case '6': // DECDWL
671 if(state->mode.leftrightmargin)
672 break;
673 set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_OFF);
674 break;
675
676 case '8': // DECALN
677 {
678 VTermPos pos;
679 uint32_t E[] = { 'E', 0 };
680 for(pos.row = 0; pos.row < state->rows; pos.row++)
681 for(pos.col = 0; pos.col < ROWWIDTH(state, pos.row); pos.col++)
682 putglyph(state, E, 1, pos);
683 break;
684 }
685
686 default:
687 return 0;
688 }
689 return 2;
690
691 case '(': case ')': case '*': case '+': // SCS
692 if(len != 2)
693 return 0;
694
695 {
696 int setnum = bytes[0] - 0x28;
697 VTermEncoding *newenc = vterm_lookup_encoding(ENC_SINGLE_94, bytes[1]);
698
699 if(newenc) {
700 state->encoding[setnum].enc = newenc;
701
702 if(newenc->init)
703 (*newenc->init)(newenc, state->encoding[setnum].data);
704 }
705 }
706
707 return 2;
708
709 case '7': // DECSC
710 savecursor(state, 1);
711 return 1;
712
713 case '8': // DECRC
714 savecursor(state, 0);
715 return 1;
716
717 case '<': // Ignored by VT100. Used in VT52 mode to switch up to VT100
718 return 1;
719
720 case '=': // DECKPAM
721 state->mode.keypad = 1;
722 return 1;
723
724 case '>': // DECKPNM
725 state->mode.keypad = 0;
726 return 1;
727
728 case 'c': // RIS - ECMA-48 8.3.105
729 {
730 VTermPos oldpos = state->pos;
731 vterm_state_reset(state, 1);
732 if(state->callbacks && state->callbacks->movecursor)
733 (*state->callbacks->movecursor)(state->pos, oldpos, state->mode.cursor_visible, state->cbdata);
734 return 1;
735 }
736
737 case 'n': // LS2 - ECMA-48 8.3.78
738 state->gl_set = 2;
739 return 1;
740
741 case 'o': // LS3 - ECMA-48 8.3.80
742 state->gl_set = 3;
743 return 1;
744
745 case '~': // LS1R - ECMA-48 8.3.77
746 state->gr_set = 1;
747 return 1;
748
749 case '}': // LS2R - ECMA-48 8.3.79
750 state->gr_set = 2;
751 return 1;
752
753 case '|': // LS3R - ECMA-48 8.3.81
754 state->gr_set = 3;
755 return 1;
756
757 default:
758 return 0;
759 }
760 }
761
set_mode(VTermState * state,int num,int val)762 static void set_mode(VTermState *state, int num, int val)
763 {
764 switch(num) {
765 case 4: // IRM - ECMA-48 7.2.10
766 state->mode.insert = val;
767 break;
768
769 case 20: // LNM - ANSI X3.4-1977
770 state->mode.newline = val;
771 break;
772
773 default:
774 DEBUG_LOG1("libvterm: Unknown mode %d\n", num);
775 return;
776 }
777 }
778
set_dec_mode(VTermState * state,int num,int val)779 static void set_dec_mode(VTermState *state, int num, int val)
780 {
781 switch(num) {
782 case 1:
783 state->mode.cursor = val;
784 break;
785
786 case 5: // DECSCNM - screen mode
787 settermprop_bool(state, VTERM_PROP_REVERSE, val);
788 break;
789
790 case 6: // DECOM - origin mode
791 {
792 VTermPos oldpos = state->pos;
793 state->mode.origin = val;
794 state->pos.row = state->mode.origin ? state->scrollregion_top : 0;
795 state->pos.col = state->mode.origin ? SCROLLREGION_LEFT(state) : 0;
796 updatecursor(state, &oldpos, 1);
797 }
798 break;
799
800 case 7:
801 state->mode.autowrap = val;
802 break;
803
804 case 12:
805 settermprop_bool(state, VTERM_PROP_CURSORBLINK, val);
806 break;
807
808 case 25:
809 settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, val);
810 break;
811
812 case 69: // DECVSSM - vertical split screen mode
813 // DECLRMM - left/right margin mode
814 state->mode.leftrightmargin = val;
815 if(val) {
816 int row;
817
818 // Setting DECVSSM must clear doublewidth/doubleheight state of every line
819 for(row = 0; row < state->rows; row++)
820 set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
821 }
822
823 break;
824
825 case 1000:
826 case 1002:
827 case 1003:
828 settermprop_int(state, VTERM_PROP_MOUSE,
829 !val ? VTERM_PROP_MOUSE_NONE :
830 (num == 1000) ? VTERM_PROP_MOUSE_CLICK :
831 (num == 1002) ? VTERM_PROP_MOUSE_DRAG :
832 VTERM_PROP_MOUSE_MOVE);
833 break;
834
835 case 1004:
836 state->mode.report_focus = val;
837 break;
838
839 case 1005:
840 state->mouse_protocol = val ? MOUSE_UTF8 : MOUSE_X10;
841 break;
842
843 case 1006:
844 state->mouse_protocol = val ? MOUSE_SGR : MOUSE_X10;
845 break;
846
847 case 1015:
848 state->mouse_protocol = val ? MOUSE_RXVT : MOUSE_X10;
849 break;
850
851 case 1047:
852 settermprop_bool(state, VTERM_PROP_ALTSCREEN, val);
853 break;
854
855 case 1048:
856 savecursor(state, val);
857 break;
858
859 case 1049:
860 settermprop_bool(state, VTERM_PROP_ALTSCREEN, val);
861 savecursor(state, val);
862 break;
863
864 case 2004:
865 state->mode.bracketpaste = val;
866 break;
867
868 default:
869 DEBUG_LOG1("libvterm: Unknown DEC mode %d\n", num);
870 return;
871 }
872 }
873
request_dec_mode(VTermState * state,int num)874 static void request_dec_mode(VTermState *state, int num)
875 {
876 int reply;
877
878 switch(num) {
879 case 1:
880 reply = state->mode.cursor;
881 break;
882
883 case 5:
884 reply = state->mode.screen;
885 break;
886
887 case 6:
888 reply = state->mode.origin;
889 break;
890
891 case 7:
892 reply = state->mode.autowrap;
893 break;
894
895 case 12:
896 reply = state->mode.cursor_blink;
897 break;
898
899 case 25:
900 reply = state->mode.cursor_visible;
901 break;
902
903 case 69:
904 reply = state->mode.leftrightmargin;
905 break;
906
907 case 1000:
908 reply = state->mouse_flags == MOUSE_WANT_CLICK;
909 break;
910
911 case 1002:
912 reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_DRAG);
913 break;
914
915 case 1003:
916 reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_MOVE);
917 break;
918
919 case 1004:
920 reply = state->mode.report_focus;
921 break;
922
923 case 1005:
924 reply = state->mouse_protocol == MOUSE_UTF8;
925 break;
926
927 case 1006:
928 reply = state->mouse_protocol == MOUSE_SGR;
929 break;
930
931 case 1015:
932 reply = state->mouse_protocol == MOUSE_RXVT;
933 break;
934
935 case 1047:
936 reply = state->mode.alt_screen;
937 break;
938
939 case 2004:
940 reply = state->mode.bracketpaste;
941 break;
942
943 default:
944 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, 0);
945 return;
946 }
947
948 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, reply ? 1 : 2);
949 }
950
on_csi(const char * leader,const long args[],int argcount,const char * intermed,char command,void * user)951 static int on_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user)
952 {
953 VTermState *state = user;
954 int leader_byte = 0;
955 int intermed_byte = 0;
956 int cancel_phantom = 1;
957 VTermPos oldpos = state->pos;
958 int handled = 1;
959
960 // Some temporaries for later code
961 int count, val;
962 int row, col;
963 VTermRect rect;
964 int selective;
965
966 if(leader && leader[0]) {
967 if(leader[1]) // longer than 1 char
968 return 0;
969
970 switch(leader[0]) {
971 case '?':
972 case '>':
973 leader_byte = leader[0];
974 break;
975 default:
976 return 0;
977 }
978 }
979
980 if(intermed && intermed[0]) {
981 if(intermed[1]) // longer than 1 char
982 return 0;
983
984 switch(intermed[0]) {
985 case ' ':
986 case '"':
987 case '$':
988 case '\'':
989 intermed_byte = intermed[0];
990 break;
991 default:
992 return 0;
993 }
994 }
995
996 oldpos = state->pos;
997
998 #define LBOUND(v,min) if((v) < (min)) (v) = (min)
999 #define UBOUND(v,max) if((v) > (max)) (v) = (max)
1000
1001 #define LEADER(l,b) ((l << 8) | b)
1002 #define INTERMED(i,b) ((i << 16) | b)
1003
1004 switch(intermed_byte << 16 | leader_byte << 8 | command) {
1005 case 0x40: // ICH - ECMA-48 8.3.64
1006 count = CSI_ARG_COUNT(args[0]);
1007
1008 if(!is_cursor_in_scrollregion(state))
1009 break;
1010
1011 rect.start_row = state->pos.row;
1012 rect.end_row = state->pos.row + 1;
1013 rect.start_col = state->pos.col;
1014 if(state->mode.leftrightmargin)
1015 rect.end_col = SCROLLREGION_RIGHT(state);
1016 else
1017 rect.end_col = THISROWWIDTH(state);
1018
1019 scroll(state, rect, 0, -count);
1020
1021 break;
1022
1023 case 0x41: // CUU - ECMA-48 8.3.22
1024 count = CSI_ARG_COUNT(args[0]);
1025 state->pos.row -= count;
1026 state->at_phantom = 0;
1027 break;
1028
1029 case 0x42: // CUD - ECMA-48 8.3.19
1030 count = CSI_ARG_COUNT(args[0]);
1031 state->pos.row += count;
1032 state->at_phantom = 0;
1033 break;
1034
1035 case 0x43: // CUF - ECMA-48 8.3.20
1036 count = CSI_ARG_COUNT(args[0]);
1037 state->pos.col += count;
1038 state->at_phantom = 0;
1039 break;
1040
1041 case 0x44: // CUB - ECMA-48 8.3.18
1042 count = CSI_ARG_COUNT(args[0]);
1043 state->pos.col -= count;
1044 state->at_phantom = 0;
1045 break;
1046
1047 case 0x45: // CNL - ECMA-48 8.3.12
1048 count = CSI_ARG_COUNT(args[0]);
1049 state->pos.col = 0;
1050 state->pos.row += count;
1051 state->at_phantom = 0;
1052 break;
1053
1054 case 0x46: // CPL - ECMA-48 8.3.13
1055 count = CSI_ARG_COUNT(args[0]);
1056 state->pos.col = 0;
1057 state->pos.row -= count;
1058 state->at_phantom = 0;
1059 break;
1060
1061 case 0x47: // CHA - ECMA-48 8.3.9
1062 val = CSI_ARG_OR(args[0], 1);
1063 state->pos.col = val-1;
1064 state->at_phantom = 0;
1065 break;
1066
1067 case 0x48: // CUP - ECMA-48 8.3.21
1068 row = CSI_ARG_OR(args[0], 1);
1069 col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
1070 // zero-based
1071 if(vterm_get_special_pty_type() == 2) {
1072 // Fix a sequence that is not correct right now
1073 if(state->pos.row == row - 1) {
1074 int cnt, ptr = 0;
1075 for(cnt = 0; cnt < col - 1; ++cnt) {
1076 VTermPos p;
1077 VTermScreenCell c0, c1;
1078 p.row = row - 1;
1079 p.col = ptr;
1080 vterm_screen_get_cell(state->vt->screen, p, &c0);
1081 p.col++;
1082 vterm_screen_get_cell(state->vt->screen, p, &c1);
1083 ptr += (c1.chars[0] == (uint32_t)-1) // double cell?
1084 ? (vterm_unicode_is_ambiguous(c0.chars[0])) // is ambiguous?
1085 ? vterm_unicode_width(0x00a1) : 1 // &ambiwidth
1086 : 1; // not ambiguous
1087 }
1088 col = ptr + 1;
1089 }
1090 }
1091 state->pos.row = row-1;
1092 state->pos.col = col-1;
1093 if(state->mode.origin) {
1094 state->pos.row += state->scrollregion_top;
1095 state->pos.col += SCROLLREGION_LEFT(state);
1096 }
1097 state->at_phantom = 0;
1098 break;
1099
1100 case 0x49: // CHT - ECMA-48 8.3.10
1101 count = CSI_ARG_COUNT(args[0]);
1102 tab(state, count, +1);
1103 break;
1104
1105 case 0x4a: // ED - ECMA-48 8.3.39
1106 case LEADER('?', 0x4a): // DECSED - Selective Erase in Display
1107 selective = (leader_byte == '?');
1108 switch(CSI_ARG(args[0])) {
1109 case CSI_ARG_MISSING:
1110 case 0:
1111 rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
1112 rect.start_col = state->pos.col; rect.end_col = state->cols;
1113 if(rect.end_col > rect.start_col)
1114 erase(state, rect, selective);
1115
1116 rect.start_row = state->pos.row + 1; rect.end_row = state->rows;
1117 rect.start_col = 0;
1118 for(row = rect.start_row; row < rect.end_row; row++)
1119 set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
1120 if(rect.end_row > rect.start_row)
1121 erase(state, rect, selective);
1122 break;
1123
1124 case 1:
1125 rect.start_row = 0; rect.end_row = state->pos.row;
1126 rect.start_col = 0; rect.end_col = state->cols;
1127 for(row = rect.start_row; row < rect.end_row; row++)
1128 set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
1129 if(rect.end_col > rect.start_col)
1130 erase(state, rect, selective);
1131
1132 rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
1133 rect.end_col = state->pos.col + 1;
1134 if(rect.end_row > rect.start_row)
1135 erase(state, rect, selective);
1136 break;
1137
1138 case 2:
1139 rect.start_row = 0; rect.end_row = state->rows;
1140 rect.start_col = 0; rect.end_col = state->cols;
1141 for(row = rect.start_row; row < rect.end_row; row++)
1142 set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
1143 erase(state, rect, selective);
1144 break;
1145 }
1146 break;
1147
1148 case 0x4b: // EL - ECMA-48 8.3.41
1149 case LEADER('?', 0x4b): // DECSEL - Selective Erase in Line
1150 selective = (leader_byte == '?');
1151 rect.start_row = state->pos.row;
1152 rect.end_row = state->pos.row + 1;
1153
1154 switch(CSI_ARG(args[0])) {
1155 case CSI_ARG_MISSING:
1156 case 0:
1157 rect.start_col = state->pos.col; rect.end_col = THISROWWIDTH(state); break;
1158 case 1:
1159 rect.start_col = 0; rect.end_col = state->pos.col + 1; break;
1160 case 2:
1161 rect.start_col = 0; rect.end_col = THISROWWIDTH(state); break;
1162 default:
1163 return 0;
1164 }
1165
1166 if(rect.end_col > rect.start_col)
1167 erase(state, rect, selective);
1168
1169 break;
1170
1171 case 0x4c: // IL - ECMA-48 8.3.67
1172 count = CSI_ARG_COUNT(args[0]);
1173
1174 if(!is_cursor_in_scrollregion(state))
1175 break;
1176
1177 rect.start_row = state->pos.row;
1178 rect.end_row = SCROLLREGION_BOTTOM(state);
1179 rect.start_col = SCROLLREGION_LEFT(state);
1180 rect.end_col = SCROLLREGION_RIGHT(state);
1181
1182 scroll(state, rect, -count, 0);
1183
1184 break;
1185
1186 case 0x4d: // DL - ECMA-48 8.3.32
1187 count = CSI_ARG_COUNT(args[0]);
1188
1189 if(!is_cursor_in_scrollregion(state))
1190 break;
1191
1192 rect.start_row = state->pos.row;
1193 rect.end_row = SCROLLREGION_BOTTOM(state);
1194 rect.start_col = SCROLLREGION_LEFT(state);
1195 rect.end_col = SCROLLREGION_RIGHT(state);
1196
1197 scroll(state, rect, count, 0);
1198
1199 break;
1200
1201 case 0x50: // DCH - ECMA-48 8.3.26
1202 count = CSI_ARG_COUNT(args[0]);
1203
1204 if(!is_cursor_in_scrollregion(state))
1205 break;
1206
1207 rect.start_row = state->pos.row;
1208 rect.end_row = state->pos.row + 1;
1209 rect.start_col = state->pos.col;
1210 if(state->mode.leftrightmargin)
1211 rect.end_col = SCROLLREGION_RIGHT(state);
1212 else
1213 rect.end_col = THISROWWIDTH(state);
1214
1215 scroll(state, rect, 0, count);
1216
1217 break;
1218
1219 case 0x53: // SU - ECMA-48 8.3.147
1220 count = CSI_ARG_COUNT(args[0]);
1221
1222 rect.start_row = state->scrollregion_top;
1223 rect.end_row = SCROLLREGION_BOTTOM(state);
1224 rect.start_col = SCROLLREGION_LEFT(state);
1225 rect.end_col = SCROLLREGION_RIGHT(state);
1226
1227 scroll(state, rect, count, 0);
1228
1229 break;
1230
1231 case 0x54: // SD - ECMA-48 8.3.113
1232 count = CSI_ARG_COUNT(args[0]);
1233
1234 rect.start_row = state->scrollregion_top;
1235 rect.end_row = SCROLLREGION_BOTTOM(state);
1236 rect.start_col = SCROLLREGION_LEFT(state);
1237 rect.end_col = SCROLLREGION_RIGHT(state);
1238
1239 scroll(state, rect, -count, 0);
1240
1241 break;
1242
1243 case 0x58: // ECH - ECMA-48 8.3.38
1244 count = CSI_ARG_COUNT(args[0]);
1245
1246 rect.start_row = state->pos.row;
1247 rect.end_row = state->pos.row + 1;
1248 rect.start_col = state->pos.col;
1249 rect.end_col = state->pos.col + count;
1250 UBOUND(rect.end_col, THISROWWIDTH(state));
1251
1252 erase(state, rect, 0);
1253 break;
1254
1255 case 0x5a: // CBT - ECMA-48 8.3.7
1256 count = CSI_ARG_COUNT(args[0]);
1257 tab(state, count, -1);
1258 break;
1259
1260 case 0x60: // HPA - ECMA-48 8.3.57
1261 col = CSI_ARG_OR(args[0], 1);
1262 state->pos.col = col-1;
1263 state->at_phantom = 0;
1264 break;
1265
1266 case 0x61: // HPR - ECMA-48 8.3.59
1267 count = CSI_ARG_COUNT(args[0]);
1268 state->pos.col += count;
1269 state->at_phantom = 0;
1270 break;
1271
1272 case 0x62: { // REP - ECMA-48 8.3.103
1273 const int row_width = THISROWWIDTH(state);
1274 count = CSI_ARG_COUNT(args[0]);
1275 col = state->pos.col + count;
1276 UBOUND(col, row_width);
1277 while (state->pos.col < col) {
1278 putglyph(state, state->combine_chars, state->combine_width, state->pos);
1279 state->pos.col += state->combine_width;
1280 }
1281 if (state->pos.col + state->combine_width >= row_width) {
1282 if (state->mode.autowrap) {
1283 state->at_phantom = 1;
1284 cancel_phantom = 0;
1285 }
1286 }
1287 break;
1288 }
1289
1290 case 0x63: // DA - ECMA-48 8.3.24
1291 val = CSI_ARG_OR(args[0], 0);
1292 if(val == 0)
1293 // DEC VT100 response
1294 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?1;2c");
1295 break;
1296
1297 case LEADER('>', 0x63): // DEC secondary Device Attributes
1298 // This returns xterm version number 100.
1299 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, ">%d;%d;%dc", 0, 100, 0);
1300 break;
1301
1302 case 0x64: // VPA - ECMA-48 8.3.158
1303 row = CSI_ARG_OR(args[0], 1);
1304 state->pos.row = row-1;
1305 if(state->mode.origin)
1306 state->pos.row += state->scrollregion_top;
1307 state->at_phantom = 0;
1308 break;
1309
1310 case 0x65: // VPR - ECMA-48 8.3.160
1311 count = CSI_ARG_COUNT(args[0]);
1312 state->pos.row += count;
1313 state->at_phantom = 0;
1314 break;
1315
1316 case 0x66: // HVP - ECMA-48 8.3.63
1317 row = CSI_ARG_OR(args[0], 1);
1318 col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
1319 // zero-based
1320 state->pos.row = row-1;
1321 state->pos.col = col-1;
1322 if(state->mode.origin) {
1323 state->pos.row += state->scrollregion_top;
1324 state->pos.col += SCROLLREGION_LEFT(state);
1325 }
1326 state->at_phantom = 0;
1327 break;
1328
1329 case 0x67: // TBC - ECMA-48 8.3.154
1330 val = CSI_ARG_OR(args[0], 0);
1331
1332 switch(val) {
1333 case 0:
1334 clear_col_tabstop(state, state->pos.col);
1335 break;
1336 case 3:
1337 case 5:
1338 for(col = 0; col < state->cols; col++)
1339 clear_col_tabstop(state, col);
1340 break;
1341 case 1:
1342 case 2:
1343 case 4:
1344 break;
1345 /* TODO: 1, 2 and 4 aren't meaningful yet without line tab stops */
1346 default:
1347 return 0;
1348 }
1349 break;
1350
1351 case 0x68: // SM - ECMA-48 8.3.125
1352 if(!CSI_ARG_IS_MISSING(args[0]))
1353 set_mode(state, CSI_ARG(args[0]), 1);
1354 break;
1355
1356 case LEADER('?', 0x68): // DEC private mode set
1357 if(!CSI_ARG_IS_MISSING(args[0]))
1358 set_dec_mode(state, CSI_ARG(args[0]), 1);
1359 break;
1360
1361 case 0x6a: // HPB - ECMA-48 8.3.58
1362 count = CSI_ARG_COUNT(args[0]);
1363 state->pos.col -= count;
1364 state->at_phantom = 0;
1365 break;
1366
1367 case 0x6b: // VPB - ECMA-48 8.3.159
1368 count = CSI_ARG_COUNT(args[0]);
1369 state->pos.row -= count;
1370 state->at_phantom = 0;
1371 break;
1372
1373 case 0x6c: // RM - ECMA-48 8.3.106
1374 if(!CSI_ARG_IS_MISSING(args[0]))
1375 set_mode(state, CSI_ARG(args[0]), 0);
1376 break;
1377
1378 case LEADER('?', 0x6c): // DEC private mode reset
1379 if(!CSI_ARG_IS_MISSING(args[0]))
1380 set_dec_mode(state, CSI_ARG(args[0]), 0);
1381 break;
1382
1383 case 0x6d: // SGR - ECMA-48 8.3.117
1384 vterm_state_setpen(state, args, argcount);
1385 break;
1386
1387 case LEADER('>', 0x6d): // xterm resource modifyOtherKeys
1388 if (argcount == 2 && args[0] == 4)
1389 state->mode.modify_other_keys = args[1] == 2;
1390 break;
1391
1392 case 0x6e: // DSR - ECMA-48 8.3.35
1393 case LEADER('?', 0x6e): // DECDSR
1394 val = CSI_ARG_OR(args[0], 0);
1395
1396 {
1397 char *qmark = (leader_byte == '?') ? "?" : "";
1398
1399 switch(val) {
1400 case 0: case 1: case 2: case 3: case 4:
1401 // ignore - these are replies
1402 break;
1403 case 5:
1404 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s0n", qmark);
1405 break;
1406 case 6: // CPR - cursor position report
1407 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s%d;%dR", qmark, state->pos.row + 1, state->pos.col + 1);
1408 break;
1409 }
1410 }
1411 break;
1412
1413
1414 case LEADER('!', 0x70): // DECSTR - DEC soft terminal reset
1415 vterm_state_reset(state, 0);
1416 break;
1417
1418 case LEADER('?', INTERMED('$', 0x70)):
1419 request_dec_mode(state, CSI_ARG(args[0]));
1420 break;
1421
1422 case INTERMED(' ', 0x71): // DECSCUSR - DEC set cursor shape
1423 val = CSI_ARG_OR(args[0], 1);
1424
1425 switch(val) {
1426 case 0: case 1:
1427 settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
1428 settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
1429 break;
1430 case 2:
1431 settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
1432 settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
1433 break;
1434 case 3:
1435 settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
1436 settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE);
1437 break;
1438 case 4:
1439 settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
1440 settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE);
1441 break;
1442 case 5:
1443 settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
1444 settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT);
1445 break;
1446 case 6:
1447 settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
1448 settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT);
1449 break;
1450 }
1451
1452 break;
1453
1454 case INTERMED('"', 0x71): // DECSCA - DEC select character protection attribute
1455 val = CSI_ARG_OR(args[0], 0);
1456
1457 switch(val) {
1458 case 0: case 2:
1459 state->protected_cell = 0;
1460 break;
1461 case 1:
1462 state->protected_cell = 1;
1463 break;
1464 }
1465
1466 break;
1467
1468 case 0x72: // DECSTBM - DEC custom
1469 state->scrollregion_top = CSI_ARG_OR(args[0], 1) - 1;
1470 state->scrollregion_bottom = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
1471 LBOUND(state->scrollregion_top, 0);
1472 UBOUND(state->scrollregion_top, state->rows);
1473 LBOUND(state->scrollregion_bottom, -1);
1474 if(state->scrollregion_top == 0 && state->scrollregion_bottom == state->rows)
1475 state->scrollregion_bottom = -1;
1476 else
1477 UBOUND(state->scrollregion_bottom, state->rows);
1478
1479 if(SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) {
1480 // Invalid
1481 state->scrollregion_top = 0;
1482 state->scrollregion_bottom = -1;
1483 }
1484
1485 // Setting the scrolling region restores the cursor to the home position
1486 state->pos.row = 0;
1487 state->pos.col = 0;
1488 if(state->mode.origin) {
1489 state->pos.row += state->scrollregion_top;
1490 state->pos.col += SCROLLREGION_LEFT(state);
1491 }
1492
1493 break;
1494
1495 case 0x73: // DECSLRM - DEC custom
1496 // Always allow setting these margins, just they won't take effect without DECVSSM
1497 state->scrollregion_left = CSI_ARG_OR(args[0], 1) - 1;
1498 state->scrollregion_right = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
1499 LBOUND(state->scrollregion_left, 0);
1500 UBOUND(state->scrollregion_left, state->cols);
1501 LBOUND(state->scrollregion_right, -1);
1502 if(state->scrollregion_left == 0 && state->scrollregion_right == state->cols)
1503 state->scrollregion_right = -1;
1504 else
1505 UBOUND(state->scrollregion_right, state->cols);
1506
1507 if(state->scrollregion_right > -1 &&
1508 state->scrollregion_right <= state->scrollregion_left) {
1509 // Invalid
1510 state->scrollregion_left = 0;
1511 state->scrollregion_right = -1;
1512 }
1513
1514 // Setting the scrolling region restores the cursor to the home position
1515 state->pos.row = 0;
1516 state->pos.col = 0;
1517 if(state->mode.origin) {
1518 state->pos.row += state->scrollregion_top;
1519 state->pos.col += SCROLLREGION_LEFT(state);
1520 }
1521
1522 break;
1523
1524 case 0x74:
1525 switch(CSI_ARG(args[0])) {
1526 case 8: // CSI 8 ; rows ; cols t set size
1527 if (argcount == 3)
1528 on_resize(CSI_ARG(args[1]), CSI_ARG(args[2]), state);
1529 break;
1530 default:
1531 handled = 0;
1532 break;
1533 }
1534 break;
1535
1536 case INTERMED('\'', 0x7D): // DECIC
1537 count = CSI_ARG_COUNT(args[0]);
1538
1539 if(!is_cursor_in_scrollregion(state))
1540 break;
1541
1542 rect.start_row = state->scrollregion_top;
1543 rect.end_row = SCROLLREGION_BOTTOM(state);
1544 rect.start_col = state->pos.col;
1545 rect.end_col = SCROLLREGION_RIGHT(state);
1546
1547 scroll(state, rect, 0, -count);
1548
1549 break;
1550
1551 case INTERMED('\'', 0x7E): // DECDC
1552 count = CSI_ARG_COUNT(args[0]);
1553
1554 if(!is_cursor_in_scrollregion(state))
1555 break;
1556
1557 rect.start_row = state->scrollregion_top;
1558 rect.end_row = SCROLLREGION_BOTTOM(state);
1559 rect.start_col = state->pos.col;
1560 rect.end_col = SCROLLREGION_RIGHT(state);
1561
1562 scroll(state, rect, 0, count);
1563
1564 break;
1565
1566 default:
1567 handled = 0;
1568 break;
1569 }
1570
1571 if (!handled) {
1572 if(state->fallbacks && state->fallbacks->csi)
1573 if((*state->fallbacks->csi)(leader, args, argcount, intermed, command, state->fbdata))
1574 return 1;
1575
1576 return 0;
1577 }
1578
1579 if(state->mode.origin) {
1580 LBOUND(state->pos.row, state->scrollregion_top);
1581 UBOUND(state->pos.row, SCROLLREGION_BOTTOM(state)-1);
1582 LBOUND(state->pos.col, SCROLLREGION_LEFT(state));
1583 UBOUND(state->pos.col, SCROLLREGION_RIGHT(state)-1);
1584 }
1585 else {
1586 LBOUND(state->pos.row, 0);
1587 UBOUND(state->pos.row, state->rows-1);
1588 LBOUND(state->pos.col, 0);
1589 UBOUND(state->pos.col, THISROWWIDTH(state)-1);
1590 }
1591
1592 updatecursor(state, &oldpos, cancel_phantom);
1593
1594 #ifdef DEBUG
1595 if(state->pos.row < 0 || state->pos.row >= state->rows ||
1596 state->pos.col < 0 || state->pos.col >= state->cols) {
1597 fprintf(stderr, "Position out of bounds after CSI %c: (%d,%d)\n",
1598 command, state->pos.row, state->pos.col);
1599 abort();
1600 }
1601
1602 if(SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) {
1603 fprintf(stderr, "Scroll region height out of bounds after CSI %c: %d <= %d\n",
1604 command, SCROLLREGION_BOTTOM(state), state->scrollregion_top);
1605 abort();
1606 }
1607
1608 if(SCROLLREGION_RIGHT(state) <= SCROLLREGION_LEFT(state)) {
1609 fprintf(stderr, "Scroll region width out of bounds after CSI %c: %d <= %d\n",
1610 command, SCROLLREGION_RIGHT(state), SCROLLREGION_LEFT(state));
1611 abort();
1612 }
1613 #endif
1614
1615 return 1;
1616 }
1617
on_osc(int command,VTermStringFragment frag,void * user)1618 static int on_osc(int command, VTermStringFragment frag, void *user)
1619 {
1620 VTermState *state = user;
1621
1622 switch(command) {
1623 case 0:
1624 settermprop_string(state, VTERM_PROP_ICONNAME, frag);
1625 settermprop_string(state, VTERM_PROP_TITLE, frag);
1626 return 1;
1627
1628 case 1:
1629 settermprop_string(state, VTERM_PROP_ICONNAME, frag);
1630 return 1;
1631
1632 case 2:
1633 settermprop_string(state, VTERM_PROP_TITLE, frag);
1634 return 1;
1635
1636 case 10:
1637 {
1638 // request foreground color: <Esc>]10;?<0x07>
1639 int red = state->default_fg.red;
1640 int blue = state->default_fg.blue;
1641 int green = state->default_fg.green;
1642 vterm_push_output_sprintf_ctrl(state->vt, C1_OSC, "10;rgb:%02x%02x/%02x%02x/%02x%02x\x07", red, red, green, green, blue, blue);
1643 return 1;
1644 }
1645
1646 case 11:
1647 {
1648 // request background color: <Esc>]11;?<0x07>
1649 int red = state->default_bg.red;
1650 int blue = state->default_bg.blue;
1651 int green = state->default_bg.green;
1652 vterm_push_output_sprintf_ctrl(state->vt, C1_OSC, "11;rgb:%02x%02x/%02x%02x/%02x%02x\x07", red, red, green, green, blue, blue);
1653 return 1;
1654 }
1655 case 12:
1656 settermprop_string(state, VTERM_PROP_CURSORCOLOR, frag);
1657 return 1;
1658
1659 default:
1660 if(state->fallbacks && state->fallbacks->osc)
1661 if((*state->fallbacks->osc)(command, frag, state->fbdata))
1662 return 1;
1663 }
1664
1665 return 0;
1666 }
1667
request_status_string(VTermState * state,VTermStringFragment frag)1668 static void request_status_string(VTermState *state, VTermStringFragment frag)
1669 {
1670 VTerm *vt = state->vt;
1671
1672 char *tmp = state->tmp.decrqss;
1673 size_t i = 0;
1674
1675 if(frag.initial)
1676 tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
1677
1678 while(i < sizeof(state->tmp.decrqss)-1 && tmp[i])
1679 i++;
1680 while(i < sizeof(state->tmp.decrqss)-1 && frag.len--)
1681 tmp[i++] = (frag.str++)[0];
1682 tmp[i] = 0;
1683
1684 if(!frag.final)
1685 return;
1686
1687 switch(tmp[0] | tmp[1]<<8 | tmp[2]<<16) {
1688 case 'm': {
1689 // Query SGR
1690 long args[20];
1691 int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0]));
1692 size_t cur = 0;
1693 int argi;
1694
1695 cur += SNPRINTF(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
1696 vt->mode.ctrl8bit ? "\x90" "1$r" : ESC_S "P" "1$r"); // DCS 1$r ...
1697 if(cur >= vt->tmpbuffer_len)
1698 return;
1699
1700 for(argi = 0; argi < argc; argi++) {
1701 cur += SNPRINTF(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
1702 argi == argc - 1 ? "%ld" :
1703 CSI_ARG_HAS_MORE(args[argi]) ? "%ld:" :
1704 "%ld;",
1705 CSI_ARG(args[argi]));
1706 if(cur >= vt->tmpbuffer_len)
1707 return;
1708 }
1709
1710 cur += SNPRINTF(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
1711 vt->mode.ctrl8bit ? "m" "\x9C" : "m" ESC_S "\\"); // ... m ST
1712 if(cur >= vt->tmpbuffer_len)
1713 return;
1714
1715 vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
1716 return;
1717 }
1718
1719 case 'r':
1720 // Query DECSTBM
1721 vterm_push_output_sprintf_dcs(vt, "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state));
1722 return;
1723
1724 case 's':
1725 // Query DECSLRM
1726 vterm_push_output_sprintf_dcs(vt, "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state));
1727 return;
1728
1729 case ' '|('q'<<8): {
1730 // Query DECSCUSR
1731 int reply;
1732 switch(state->mode.cursor_shape) {
1733 case VTERM_PROP_CURSORSHAPE_BLOCK: reply = 2; break;
1734 case VTERM_PROP_CURSORSHAPE_UNDERLINE: reply = 4; break;
1735 default: /* VTERM_PROP_CURSORSHAPE_BAR_LEFT */ reply = 6; break;
1736 }
1737 if(state->mode.cursor_blink)
1738 reply--;
1739 vterm_push_output_sprintf_dcs(vt, "1$r%d q", reply);
1740 return;
1741 }
1742
1743 case '\"'|('q'<<8):
1744 // Query DECSCA
1745 vterm_push_output_sprintf_dcs(vt, "1$r%d\"q", state->protected_cell ? 1 : 2);
1746 return;
1747 }
1748
1749 vterm_push_output_sprintf_dcs(state->vt, "0$r%s", tmp);
1750 }
1751
on_dcs(const char * command,size_t commandlen,VTermStringFragment frag,void * user)1752 static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
1753 {
1754 VTermState *state = user;
1755
1756 if(commandlen == 2 && strneq(command, "$q", 2)) {
1757 request_status_string(state, frag);
1758 return 1;
1759 }
1760 else if(state->fallbacks && state->fallbacks->dcs)
1761 if((*state->fallbacks->dcs)(command, commandlen, frag, state->fbdata))
1762 return 1;
1763
1764 DEBUG_LOG2("libvterm: Unhandled DCS %.*s\n", (int)commandlen, command);
1765 return 0;
1766 }
1767
on_resize(int rows,int cols,void * user)1768 static int on_resize(int rows, int cols, void *user)
1769 {
1770 VTermState *state = user;
1771 VTermPos oldpos = state->pos;
1772 VTermStateFields fields;
1773
1774 if(cols != state->cols) {
1775 int col;
1776 unsigned char *newtabstops = vterm_allocator_malloc(state->vt, (cols + 7) / 8);
1777 if (newtabstops == NULL)
1778 return 0;
1779
1780 /* TODO: This can all be done much more efficiently bytewise */
1781 for(col = 0; col < state->cols && col < cols; col++) {
1782 unsigned char mask = 1 << (col & 7);
1783 if(state->tabstops[col >> 3] & mask)
1784 newtabstops[col >> 3] |= mask;
1785 else
1786 newtabstops[col >> 3] &= ~mask;
1787 }
1788
1789 for( ; col < cols; col++) {
1790 unsigned char mask = 1 << (col & 7);
1791 if(col % 8 == 0)
1792 newtabstops[col >> 3] |= mask;
1793 else
1794 newtabstops[col >> 3] &= ~mask;
1795 }
1796
1797 vterm_allocator_free(state->vt, state->tabstops);
1798 state->tabstops = newtabstops;
1799 }
1800
1801 if(rows != state->rows) {
1802 int bufidx;
1803 for(bufidx = BUFIDX_PRIMARY; bufidx <= BUFIDX_ALTSCREEN; bufidx++) {
1804 int row;
1805 VTermLineInfo *oldlineinfo = state->lineinfos[bufidx];
1806 VTermLineInfo *newlineinfo;
1807 if(!oldlineinfo)
1808 continue;
1809
1810 newlineinfo = vterm_allocator_malloc(state->vt, rows * sizeof(VTermLineInfo));
1811
1812 for(row = 0; row < state->rows && row < rows; row++) {
1813 newlineinfo[row] = oldlineinfo[row];
1814 }
1815
1816 for( ; row < rows; row++) {
1817 VTermLineInfo lineInfo = {0x0};
1818 newlineinfo[row] = lineInfo;
1819 }
1820
1821 vterm_allocator_free(state->vt, state->lineinfos[bufidx]);
1822 state->lineinfos[bufidx] = newlineinfo;
1823 }
1824
1825 state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
1826 }
1827
1828 state->rows = rows;
1829 state->cols = cols;
1830
1831 if(state->scrollregion_bottom > -1)
1832 UBOUND(state->scrollregion_bottom, state->rows);
1833 if(state->scrollregion_right > -1)
1834 UBOUND(state->scrollregion_right, state->cols);
1835
1836 fields.pos = state->pos;
1837
1838 if(state->callbacks && state->callbacks->resize)
1839 (*state->callbacks->resize)(rows, cols, &fields, state->cbdata);
1840
1841 state->pos = fields.pos;
1842
1843 if(state->at_phantom && state->pos.col < cols-1) {
1844 state->at_phantom = 0;
1845 state->pos.col++;
1846 }
1847
1848 if(state->pos.row < 0)
1849 state->pos.row = 0;
1850 if(state->pos.row >= rows)
1851 state->pos.row = rows - 1;
1852 if(state->pos.col < 0)
1853 state->pos.col = 0;
1854 if(state->pos.col >= cols)
1855 state->pos.col = cols - 1;
1856
1857 updatecursor(state, &oldpos, 1);
1858
1859 return 1;
1860 }
1861
1862 static const VTermParserCallbacks parser_callbacks = {
1863 on_text, // text
1864 on_control, // control
1865 on_escape, // escape
1866 on_csi, // csi
1867 on_osc, // osc
1868 on_dcs, // dcs
1869 on_resize // resize
1870 };
1871
1872 /*
1873 * Return the existing state or create a new one.
1874 * Returns NULL when out of memory.
1875 */
vterm_obtain_state(VTerm * vt)1876 VTermState *vterm_obtain_state(VTerm *vt)
1877 {
1878 VTermState *state;
1879 if(vt->state)
1880 return vt->state;
1881
1882 state = vterm_state_new(vt);
1883 if (state == NULL)
1884 return NULL;
1885 vt->state = state;
1886
1887 vterm_parser_set_callbacks(vt, &parser_callbacks, state);
1888
1889 return state;
1890 }
1891
vterm_state_reset(VTermState * state,int hard)1892 void vterm_state_reset(VTermState *state, int hard)
1893 {
1894 VTermEncoding *default_enc;
1895
1896 state->scrollregion_top = 0;
1897 state->scrollregion_bottom = -1;
1898 state->scrollregion_left = 0;
1899 state->scrollregion_right = -1;
1900
1901 state->mode.keypad = 0;
1902 state->mode.cursor = 0;
1903 state->mode.autowrap = 1;
1904 state->mode.insert = 0;
1905 state->mode.newline = 0;
1906 state->mode.alt_screen = 0;
1907 state->mode.origin = 0;
1908 state->mode.leftrightmargin = 0;
1909 state->mode.bracketpaste = 0;
1910 state->mode.report_focus = 0;
1911
1912 state->vt->mode.ctrl8bit = 0;
1913
1914 {
1915 int col;
1916 for(col = 0; col < state->cols; col++)
1917 if(col % 8 == 0)
1918 set_col_tabstop(state, col);
1919 else
1920 clear_col_tabstop(state, col);
1921 }
1922
1923 {
1924 int row;
1925 for(row = 0; row < state->rows; row++)
1926 set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
1927 }
1928
1929 if(state->callbacks && state->callbacks->initpen)
1930 (*state->callbacks->initpen)(state->cbdata);
1931
1932 vterm_state_resetpen(state);
1933
1934 default_enc = state->vt->mode.utf8 ?
1935 vterm_lookup_encoding(ENC_UTF8, 'u') :
1936 vterm_lookup_encoding(ENC_SINGLE_94, 'B');
1937
1938 {
1939 int i;
1940 for(i = 0; i < 4; i++) {
1941 state->encoding[i].enc = default_enc;
1942 if(default_enc->init)
1943 (*default_enc->init)(default_enc, state->encoding[i].data);
1944 }
1945 }
1946
1947 state->gl_set = 0;
1948 state->gr_set = 1;
1949 state->gsingle_set = 0;
1950
1951 state->protected_cell = 0;
1952
1953 // Initialise the props
1954 settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, 1);
1955 settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
1956 settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
1957
1958 if(hard) {
1959 VTermRect rect = { 0, 0, 0, 0 };
1960
1961 state->pos.row = 0;
1962 state->pos.col = 0;
1963 state->at_phantom = 0;
1964
1965 rect.end_row = state->rows;
1966 rect.end_col = state->cols;
1967 erase(state, rect, 0);
1968 }
1969 }
1970
vterm_state_get_cursorpos(const VTermState * state,VTermPos * cursorpos)1971 void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos)
1972 {
1973 *cursorpos = state->pos;
1974 }
1975
vterm_state_get_mousestate(const VTermState * state,VTermMouseState * mousestate)1976 void vterm_state_get_mousestate(const VTermState *state, VTermMouseState *mousestate)
1977 {
1978 mousestate->pos.col = state->mouse_col;
1979 mousestate->pos.row = state->mouse_row;
1980 mousestate->buttons = state->mouse_buttons;
1981 mousestate->flags = state->mouse_flags;
1982 }
1983
vterm_state_set_callbacks(VTermState * state,const VTermStateCallbacks * callbacks,void * user)1984 void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user)
1985 {
1986 if(callbacks) {
1987 state->callbacks = callbacks;
1988 state->cbdata = user;
1989
1990 if(state->callbacks && state->callbacks->initpen)
1991 (*state->callbacks->initpen)(state->cbdata);
1992 }
1993 else {
1994 state->callbacks = NULL;
1995 state->cbdata = NULL;
1996 }
1997 }
1998
vterm_state_get_cbdata(VTermState * state)1999 void *vterm_state_get_cbdata(VTermState *state)
2000 {
2001 return state->cbdata;
2002 }
2003
vterm_state_set_unrecognised_fallbacks(VTermState * state,const VTermStateFallbacks * fallbacks,void * user)2004 void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user)
2005 {
2006 if(fallbacks) {
2007 state->fallbacks = fallbacks;
2008 state->fbdata = user;
2009 }
2010 else {
2011 state->fallbacks = NULL;
2012 state->fbdata = NULL;
2013 }
2014 }
2015
vterm_state_get_unrecognised_fbdata(VTermState * state)2016 void *vterm_state_get_unrecognised_fbdata(VTermState *state)
2017 {
2018 return state->fbdata;
2019 }
2020
vterm_state_set_termprop(VTermState * state,VTermProp prop,VTermValue * val)2021 int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val)
2022 {
2023 /* Only store the new value of the property if usercode said it was happy.
2024 * This is especially important for altscreen switching */
2025 if(state->callbacks && state->callbacks->settermprop)
2026 if(!(*state->callbacks->settermprop)(prop, val, state->cbdata))
2027 return 0;
2028
2029 switch(prop) {
2030 case VTERM_PROP_TITLE:
2031 case VTERM_PROP_ICONNAME:
2032 case VTERM_PROP_CURSORCOLOR:
2033 // we don't store these, just transparently pass through
2034 return 1;
2035 case VTERM_PROP_CURSORVISIBLE:
2036 state->mode.cursor_visible = val->boolean;
2037 return 1;
2038 case VTERM_PROP_CURSORBLINK:
2039 state->mode.cursor_blink = val->boolean;
2040 return 1;
2041 case VTERM_PROP_CURSORSHAPE:
2042 state->mode.cursor_shape = val->number;
2043 return 1;
2044 case VTERM_PROP_REVERSE:
2045 state->mode.screen = val->boolean;
2046 return 1;
2047 case VTERM_PROP_ALTSCREEN:
2048 state->mode.alt_screen = val->boolean;
2049 state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
2050 if(state->mode.alt_screen) {
2051 VTermRect rect = {0, 0, 0, 0};
2052 rect.end_row = state->rows;
2053 rect.end_col = state->cols;
2054 erase(state, rect, 0);
2055 }
2056 return 1;
2057 case VTERM_PROP_MOUSE:
2058 state->mouse_flags = 0;
2059 if(val->number)
2060 state->mouse_flags |= MOUSE_WANT_CLICK;
2061 if(val->number == VTERM_PROP_MOUSE_DRAG)
2062 state->mouse_flags |= MOUSE_WANT_DRAG;
2063 if(val->number == VTERM_PROP_MOUSE_MOVE)
2064 state->mouse_flags |= MOUSE_WANT_MOVE;
2065 return 1;
2066
2067 case VTERM_N_PROPS:
2068 return 0;
2069 }
2070
2071 return 0;
2072 }
2073
vterm_state_focus_in(VTermState * state)2074 void vterm_state_focus_in(VTermState *state)
2075 {
2076 if(state->mode.report_focus)
2077 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "I");
2078 }
2079
vterm_state_focus_out(VTermState * state)2080 void vterm_state_focus_out(VTermState *state)
2081 {
2082 if(state->mode.report_focus)
2083 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "O");
2084 }
2085
vterm_state_get_lineinfo(const VTermState * state,int row)2086 const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row)
2087 {
2088 return state->lineinfo + row;
2089 }
2090