LF OS
Hobby operating system for amd64 with high ambitions
Loading...
Searching...
No Matches
state.c
Go to the documentation of this file.
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/* Some convenient wrappers to make callback functions easier */
13
14static void putglyph(VTermState *state, const uint32_t chars[], int width, VTermPos pos)
15{
17 .chars = chars,
18 .width = width,
19 .protected_cell = state->protected_cell,
20 .dwl = state->lineinfo[pos.row].doublewidth,
21 .dhl = state->lineinfo[pos.row].doubleheight,
22 };
23
25 if((*state->callbacks->putglyph)(&info, pos, state->cbdata))
26 return;
27
28 DEBUG_LOG("libvterm: Unhandled putglyph U+%04x at (%d,%d)\n", chars[0], pos.col, pos.row);
29}
30
31static void updatecursor(VTermState *state, VTermPos *oldpos, int cancel_phantom)
32{
33 if(state->pos.col == oldpos->col && state->pos.row == oldpos->row)
34 return;
35
36 if(cancel_phantom)
37 state->at_phantom = 0;
38
40 if((*state->callbacks->movecursor)(state->pos, *oldpos, state->mode.cursor_visible, state->cbdata))
41 return;
42}
43
44static void erase(VTermState *state, VTermRect rect, int selective)
45{
46 if(rect.end_col == state->cols) {
47 /* If we're erasing the final cells of any lines, cancel the continuation
48 * marker on the subsequent line
49 */
50 for(int row = rect.start_row + 1; row < rect.end_row + 1 && row < state->rows; row++)
51 state->lineinfo[row].continuation = 0;
52 }
53
55 if((*state->callbacks->erase)(rect, selective, state->cbdata))
56 return;
57}
58
101
111
112static void scroll(VTermState *state, VTermRect rect, int downward, int rightward)
113{
114 if(!downward && !rightward)
115 return;
116
117 int rows = rect.end_row - rect.start_row;
118 if(downward > rows)
119 downward = rows;
120 else if(downward < -rows)
121 downward = -rows;
122
123 int cols = rect.end_col - rect.start_col;
124 if(rightward > cols)
125 rightward = cols;
126 else if(rightward < -cols)
127 rightward = -cols;
128
129 // Update lineinfo if full line
130 if(rect.start_col == 0 && rect.end_col == state->cols && rightward == 0) {
131 int height = rect.end_row - rect.start_row - abs(downward);
132
133 if(downward > 0) {
135 state->lineinfo + rect.start_row + downward,
136 height * sizeof(state->lineinfo[0]));
137 for(int row = rect.end_row - downward; row < rect.end_row; row++)
138 state->lineinfo[row] = (VTermLineInfo){ 0 };
139 }
140 else {
141 memmove(state->lineinfo + rect.start_row - downward,
142 state->lineinfo + rect.start_row,
143 height * sizeof(state->lineinfo[0]));
144 for(int row = rect.start_row; row < rect.start_row - downward; row++)
145 state->lineinfo[row] = (VTermLineInfo){ 0 };
146 }
147 }
148
150 if((*state->callbacks->scrollrect)(rect, downward, rightward, state->cbdata))
151 return;
152
153 if(state->callbacks)
154 vterm_scroll_rect(rect, downward, rightward,
156}
157
159{
160 if(state->pos.row == SCROLLREGION_BOTTOM(state) - 1) {
161 VTermRect rect = {
163 .end_row = SCROLLREGION_BOTTOM(state),
164 .start_col = SCROLLREGION_LEFT(state),
165 .end_col = SCROLLREGION_RIGHT(state),
166 };
167
168 scroll(state, rect, 1, 0);
169 }
170 else if(state->pos.row < state->rows-1)
171 state->pos.row++;
172}
173
175{
176 size_t new_size = state->combine_chars_size * 2;
177 uint32_t *new_chars = vterm_allocator_malloc(state->vt, new_size * sizeof(new_chars[0]));
178
179 memcpy(new_chars, state->combine_chars, state->combine_chars_size * sizeof(new_chars[0]));
180
182
183 state->combine_chars = new_chars;
184 state->combine_chars_size = new_size;
185}
186
187static void set_col_tabstop(VTermState *state, int col)
188{
189 unsigned char mask = 1 << (col & 7);
190 state->tabstops[col >> 3] |= mask;
191}
192
193static void clear_col_tabstop(VTermState *state, int col)
194{
195 unsigned char mask = 1 << (col & 7);
196 state->tabstops[col >> 3] &= ~mask;
197}
198
199static int is_col_tabstop(VTermState *state, int col)
200{
201 unsigned char mask = 1 << (col & 7);
202 return state->tabstops[col >> 3] & mask;
203}
204
206{
209 return 0;
212 return 0;
213
214 return 1;
215}
216
217static void tab(VTermState *state, int count, int direction)
218{
219 while(count > 0) {
220 if(direction > 0) {
221 if(state->pos.col >= THISROWWIDTH(state)-1)
222 return;
223
224 state->pos.col++;
225 }
226 else if(direction < 0) {
227 if(state->pos.col < 1)
228 return;
229
230 state->pos.col--;
231 }
232
234 count--;
235 }
236}
237
238#define NO_FORCE 0
239#define FORCE 1
240
241#define DWL_OFF 0
242#define DWL_ON 1
243
244#define DHL_OFF 0
245#define DHL_TOP 1
246#define DHL_BOTTOM 2
247
248static void set_lineinfo(VTermState *state, int row, int force, int dwl, int dhl)
249{
251
252 if(dwl == DWL_OFF)
254 else if(dwl == DWL_ON)
255 info.doublewidth = DWL_ON;
256 // else -1 to ignore
257
258 if(dhl == DHL_OFF)
259 info.doubleheight = DHL_OFF;
260 else if(dhl == DHL_TOP)
261 info.doubleheight = DHL_TOP;
262 else if(dhl == DHL_BOTTOM)
263 info.doubleheight = DHL_BOTTOM;
264
265 if((state->callbacks &&
268 || force)
269 state->lineinfo[row] = info;
270}
271
272static int on_text(const char bytes[], size_t len, void *user)
273{
274 VTermState *state = user;
275
276 VTermPos oldpos = state->pos;
277
278 uint32_t *codepoints = (uint32_t *)(state->vt->tmpbuffer);
279 size_t maxpoints = (state->vt->tmpbuffer_len) / sizeof(uint32_t);
280
281 int npoints = 0;
282 size_t eaten = 0;
283
286 !(bytes[eaten] & 0x80) ? &state->encoding[state->gl_set] :
287 state->vt->mode.utf8 ? &state->encoding_utf8 :
289
291 codepoints, &npoints, state->gsingle_set ? 1 : maxpoints,
292 bytes, &eaten, len);
293
294 /* There's a chance an encoding (e.g. UTF-8) hasn't found enough bytes yet
295 * for even a single codepoint
296 */
297 if(!npoints)
298 return eaten;
299
300 if(state->gsingle_set && npoints)
301 state->gsingle_set = 0;
302
303 int i = 0;
304
305 /* This is a combining char. that needs to be merged with the previous
306 * glyph output */
307 if(vterm_unicode_is_combining(codepoints[i])) {
308 /* See if the cursor has moved since */
310#ifdef DEBUG_GLYPH_COMBINE
311 int printpos;
312 printf("DEBUG: COMBINING SPLIT GLYPH of chars {");
313 for(printpos = 0; state->combine_chars[printpos]; printpos++)
314 printf("U+%04x ", state->combine_chars[printpos]);
315 printf("} + {");
316#endif
317
318 /* Find where we need to append these combining chars */
319 int saved_i = 0;
320 while(state->combine_chars[saved_i])
321 saved_i++;
322
323 /* Add extra ones */
324 while(i < npoints && vterm_unicode_is_combining(codepoints[i])) {
325 if(saved_i >= state->combine_chars_size)
327 state->combine_chars[saved_i++] = codepoints[i++];
328 }
329 if(saved_i >= state->combine_chars_size)
331 state->combine_chars[saved_i] = 0;
332
333#ifdef DEBUG_GLYPH_COMBINE
334 for(; state->combine_chars[printpos]; printpos++)
335 printf("U+%04x ", state->combine_chars[printpos]);
336 printf("}\n");
337#endif
338
339 /* Now render it */
341 }
342 else {
343 DEBUG_LOG("libvterm: TODO: Skip over split char+combining\n");
344 }
345 }
346
347 for(; i < npoints; i++) {
348 // Try to find combining characters following this
349 int glyph_starts = i;
350 int glyph_ends;
351 for(glyph_ends = i + 1;
352 (glyph_ends < npoints) && (glyph_ends < glyph_starts + VTERM_MAX_CHARS_PER_CELL);
353 glyph_ends++)
354 if(!vterm_unicode_is_combining(codepoints[glyph_ends]))
355 break;
356
357 int width = 0;
358
360
361 for( ; i < glyph_ends; i++) {
362 chars[i - glyph_starts] = codepoints[i];
363 int this_width = vterm_unicode_width(codepoints[i]);
364#ifdef DEBUG
365 if(this_width < 0) {
366 fprintf(stderr, "Text with negative-width codepoint U+%04x\n", codepoints[i]);
367 abort();
368 }
369#endif
370 width += this_width;
371 }
372
373 while(i < npoints && vterm_unicode_is_combining(codepoints[i]))
374 i++;
375
376 chars[glyph_ends - glyph_starts] = 0;
377 i--;
378
379#ifdef DEBUG_GLYPH_COMBINE
380 int printpos;
381 printf("DEBUG: COMBINED GLYPH of %d chars {", glyph_ends - glyph_starts);
382 for(printpos = 0; printpos < glyph_ends - glyph_starts; printpos++)
383 printf("U+%04x ", chars[printpos]);
384 printf("}, onscreen width %d\n", width);
385#endif
386
389 state->pos.col = 0;
390 state->at_phantom = 0;
392 }
393
394 if(state->mode.insert) {
395 /* TODO: This will be a little inefficient for large bodies of text, as
396 * it'll have to 'ICH' effectively before every glyph. We should scan
397 * ahead and ICH as many times as required
398 */
399 VTermRect rect = {
401 .end_row = state->pos.row + 1,
402 .start_col = state->pos.col,
403 .end_col = THISROWWIDTH(state),
404 };
405 scroll(state, rect, 0, -1);
406 }
407
408 putglyph(state, chars, width, state->pos);
409
410 if(i == npoints - 1) {
411 /* End of the buffer. Save the chars in case we have to combine with
412 * more on the next call */
413 int save_i;
414 for(save_i = 0; chars[save_i]; save_i++) {
415 if(save_i >= state->combine_chars_size)
417 state->combine_chars[save_i] = chars[save_i];
418 }
419 if(save_i >= state->combine_chars_size)
421 state->combine_chars[save_i] = 0;
424 }
425
426 if(state->pos.col + width >= THISROWWIDTH(state)) {
427 if(state->mode.autowrap)
428 state->at_phantom = 1;
429 }
430 else {
431 state->pos.col += width;
432 }
433 }
434
435 updatecursor(state, &oldpos, 0);
436
437#ifdef DEBUG
438 if(state->pos.row < 0 || state->pos.row >= state->rows ||
439 state->pos.col < 0 || state->pos.col >= state->cols) {
440 fprintf(stderr, "Position out of bounds after text: (%d,%d)\n",
441 state->pos.row, state->pos.col);
442 abort();
443 }
444#endif
445
446 return eaten;
447}
448
449static int on_control(unsigned char control, void *user)
450{
451 VTermState *state = user;
452
453 VTermPos oldpos = state->pos;
454
455 switch(control) {
456 case 0x07: // BEL - ECMA-48 8.3.3
459 break;
460
461 case 0x08: // BS - ECMA-48 8.3.5
462 if(state->pos.col > 0)
463 state->pos.col--;
464 break;
465
466 case 0x09: // HT - ECMA-48 8.3.60
467 tab(state, 1, +1);
468 break;
469
470 case 0x0a: // LF - ECMA-48 8.3.74
471 case 0x0b: // VT
472 case 0x0c: // FF
474 if(state->mode.newline)
475 state->pos.col = 0;
476 break;
477
478 case 0x0d: // CR - ECMA-48 8.3.15
479 state->pos.col = 0;
480 break;
481
482 case 0x0e: // LS1 - ECMA-48 8.3.76
483 state->gl_set = 1;
484 break;
485
486 case 0x0f: // LS0 - ECMA-48 8.3.75
487 state->gl_set = 0;
488 break;
489
490 case 0x84: // IND - DEPRECATED but implemented for completeness
492 break;
493
494 case 0x85: // NEL - ECMA-48 8.3.86
496 state->pos.col = 0;
497 break;
498
499 case 0x88: // HTS - ECMA-48 8.3.62
501 break;
502
503 case 0x8d: // RI - ECMA-48 8.3.104
505 VTermRect rect = {
507 .end_row = SCROLLREGION_BOTTOM(state),
508 .start_col = SCROLLREGION_LEFT(state),
509 .end_col = SCROLLREGION_RIGHT(state),
510 };
511
512 scroll(state, rect, -1, 0);
513 }
514 else if(state->pos.row > 0)
515 state->pos.row--;
516 break;
517
518 case 0x8e: // SS2 - ECMA-48 8.3.141
519 state->gsingle_set = 2;
520 break;
521
522 case 0x8f: // SS3 - ECMA-48 8.3.142
523 state->gsingle_set = 3;
524 break;
525
526 default:
528 if((*state->fallbacks->control)(control, state->fbdata))
529 return 1;
530
531 return 0;
532 }
533
534 updatecursor(state, &oldpos, 1);
535
536#ifdef DEBUG
537 if(state->pos.row < 0 || state->pos.row >= state->rows ||
538 state->pos.col < 0 || state->pos.col >= state->cols) {
539 fprintf(stderr, "Position out of bounds after Ctrl %02x: (%d,%d)\n",
540 control, state->pos.row, state->pos.col);
541 abort();
542 }
543#endif
544
545 return 1;
546}
547
549{
550 VTermValue val = { .boolean = v };
551 return vterm_state_set_termprop(state, prop, &val);
552}
553
554static int settermprop_int(VTermState *state, VTermProp prop, int v)
555{
556 VTermValue val = { .number = v };
557 return vterm_state_set_termprop(state, prop, &val);
558}
559
561{
562 VTermValue val = { .string = frag };
563 return vterm_state_set_termprop(state, prop, &val);
564}
565
566static void savecursor(VTermState *state, int save)
567{
568 if(save) {
569 state->saved.pos = state->pos;
570 state->saved.mode.cursor_visible = state->mode.cursor_visible;
571 state->saved.mode.cursor_blink = state->mode.cursor_blink;
572 state->saved.mode.cursor_shape = state->mode.cursor_shape;
573
575 }
576 else {
577 VTermPos oldpos = state->pos;
578
579 state->pos = state->saved.pos;
580
584
586
587 updatecursor(state, &oldpos, 1);
588 }
589}
590
591static int on_escape(const char *bytes, size_t len, void *user)
592{
593 VTermState *state = user;
594
595 /* Easier to decode this from the first byte, even though the final
596 * byte terminates it
597 */
598 switch(bytes[0]) {
599 case ' ':
600 if(len != 2)
601 return 0;
602
603 switch(bytes[1]) {
604 case 'F': // S7C1T
605 state->vt->mode.ctrl8bit = 0;
606 break;
607
608 case 'G': // S8C1T
609 state->vt->mode.ctrl8bit = 1;
610 break;
611
612 default:
613 return 0;
614 }
615 return 2;
616
617 case '#':
618 if(len != 2)
619 return 0;
620
621 switch(bytes[1]) {
622 case '3': // DECDHL top
623 if(state->mode.leftrightmargin)
624 break;
626 break;
627
628 case '4': // DECDHL bottom
629 if(state->mode.leftrightmargin)
630 break;
632 break;
633
634 case '5': // DECSWL
635 if(state->mode.leftrightmargin)
636 break;
638 break;
639
640 case '6': // DECDWL
641 if(state->mode.leftrightmargin)
642 break;
644 break;
645
646 case '8': // DECALN
647 {
648 VTermPos pos;
649 uint32_t E[] = { 'E', 0 };
650 for(pos.row = 0; pos.row < state->rows; pos.row++)
651 for(pos.col = 0; pos.col < ROWWIDTH(state, pos.row); pos.col++)
652 putglyph(state, E, 1, pos);
653 break;
654 }
655
656 default:
657 return 0;
658 }
659 return 2;
660
661 case '(': case ')': case '*': case '+': // SCS
662 if(len != 2)
663 return 0;
664
665 {
666 int setnum = bytes[0] - 0x28;
668
669 if(newenc) {
670 state->encoding[setnum].enc = newenc;
671
672 if(newenc->init)
673 (*newenc->init)(newenc, state->encoding[setnum].data);
674 }
675 }
676
677 return 2;
678
679 case '7': // DECSC
680 savecursor(state, 1);
681 return 1;
682
683 case '8': // DECRC
684 savecursor(state, 0);
685 return 1;
686
687 case '<': // Ignored by VT100. Used in VT52 mode to switch up to VT100
688 return 1;
689
690 case '=': // DECKPAM
691 state->mode.keypad = 1;
692 return 1;
693
694 case '>': // DECKPNM
695 state->mode.keypad = 0;
696 return 1;
697
698 case 'c': // RIS - ECMA-48 8.3.105
699 {
700 VTermPos oldpos = state->pos;
703 (*state->callbacks->movecursor)(state->pos, oldpos, state->mode.cursor_visible, state->cbdata);
704 return 1;
705 }
706
707 case 'n': // LS2 - ECMA-48 8.3.78
708 state->gl_set = 2;
709 return 1;
710
711 case 'o': // LS3 - ECMA-48 8.3.80
712 state->gl_set = 3;
713 return 1;
714
715 case '~': // LS1R - ECMA-48 8.3.77
716 state->gr_set = 1;
717 return 1;
718
719 case '}': // LS2R - ECMA-48 8.3.79
720 state->gr_set = 2;
721 return 1;
722
723 case '|': // LS3R - ECMA-48 8.3.81
724 state->gr_set = 3;
725 return 1;
726
727 default:
728 return 0;
729 }
730}
731
732static void set_mode(VTermState *state, int num, int val)
733{
734 switch(num) {
735 case 4: // IRM - ECMA-48 7.2.10
736 state->mode.insert = val;
737 break;
738
739 case 20: // LNM - ANSI X3.4-1977
740 state->mode.newline = val;
741 break;
742
743 default:
744 DEBUG_LOG("libvterm: Unknown mode %d\n", num);
745 return;
746 }
747}
748
749static void set_dec_mode(VTermState *state, int num, int val)
750{
751 switch(num) {
752 case 1:
753 state->mode.cursor = val;
754 break;
755
756 case 5: // DECSCNM - screen mode
758 break;
759
760 case 6: // DECOM - origin mode
761 {
762 VTermPos oldpos = state->pos;
763 state->mode.origin = val;
764 state->pos.row = state->mode.origin ? state->scrollregion_top : 0;
765 state->pos.col = state->mode.origin ? SCROLLREGION_LEFT(state) : 0;
766 updatecursor(state, &oldpos, 1);
767 }
768 break;
769
770 case 7:
771 state->mode.autowrap = val;
772 break;
773
774 case 12:
776 break;
777
778 case 25:
780 break;
781
782 case 69: // DECVSSM - vertical split screen mode
783 // DECLRMM - left/right margin mode
784 state->mode.leftrightmargin = val;
785 if(val) {
786 // Setting DECVSSM must clear doublewidth/doubleheight state of every line
787 for(int row = 0; row < state->rows; row++)
789 }
790
791 break;
792
793 case 1000:
794 case 1002:
795 case 1003:
797 !val ? VTERM_PROP_MOUSE_NONE :
798 (num == 1000) ? VTERM_PROP_MOUSE_CLICK :
799 (num == 1002) ? VTERM_PROP_MOUSE_DRAG :
801 break;
802
803 case 1004:
805 state->mode.report_focus = val;
806 break;
807
808 case 1005:
809 state->mouse_protocol = val ? MOUSE_UTF8 : MOUSE_X10;
810 break;
811
812 case 1006:
813 state->mouse_protocol = val ? MOUSE_SGR : MOUSE_X10;
814 break;
815
816 case 1015:
817 state->mouse_protocol = val ? MOUSE_RXVT : MOUSE_X10;
818 break;
819
820 case 1047:
822 break;
823
824 case 1048:
825 savecursor(state, val);
826 break;
827
828 case 1049:
830 savecursor(state, val);
831 break;
832
833 case 2004:
834 state->mode.bracketpaste = val;
835 break;
836
837 default:
838 DEBUG_LOG("libvterm: Unknown DEC mode %d\n", num);
839 return;
840 }
841}
842
844{
845 int reply;
846
847 switch(num) {
848 case 1:
849 reply = state->mode.cursor;
850 break;
851
852 case 5:
853 reply = state->mode.screen;
854 break;
855
856 case 6:
857 reply = state->mode.origin;
858 break;
859
860 case 7:
861 reply = state->mode.autowrap;
862 break;
863
864 case 12:
865 reply = state->mode.cursor_blink;
866 break;
867
868 case 25:
869 reply = state->mode.cursor_visible;
870 break;
871
872 case 69:
873 reply = state->mode.leftrightmargin;
874 break;
875
876 case 1000:
878 break;
879
880 case 1002:
882 break;
883
884 case 1003:
886 break;
887
888 case 1004:
889 reply = state->mode.report_focus;
890 break;
891
892 case 1005:
893 reply = state->mouse_protocol == MOUSE_UTF8;
894 break;
895
896 case 1006:
897 reply = state->mouse_protocol == MOUSE_SGR;
898 break;
899
900 case 1015:
901 reply = state->mouse_protocol == MOUSE_RXVT;
902 break;
903
904 case 1047:
905 reply = state->mode.alt_screen;
906 break;
907
908 case 2004:
909 reply = state->mode.bracketpaste;
910 break;
911
912 default:
914 return;
915 }
916
917 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, reply ? 1 : 2);
918}
919
925
926static int on_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user)
927{
928 VTermState *state = user;
929 int leader_byte = 0;
930 int intermed_byte = 0;
931 int cancel_phantom = 1;
932
933 if(leader && leader[0]) {
934 if(leader[1]) // longer than 1 char
935 return 0;
936
937 switch(leader[0]) {
938 case '?':
939 case '>':
940 leader_byte = leader[0];
941 break;
942 default:
943 return 0;
944 }
945 }
946
947 if(intermed && intermed[0]) {
948 if(intermed[1]) // longer than 1 char
949 return 0;
950
951 switch(intermed[0]) {
952 case ' ':
953 case '!':
954 case '"':
955 case '$':
956 case '\'':
957 intermed_byte = intermed[0];
958 break;
959 default:
960 return 0;
961 }
962 }
963
964 VTermPos oldpos = state->pos;
965
966 // Some temporaries for later code
967 int count, val;
968 int row, col;
969 VTermRect rect;
970 int selective;
971
972#define LBOUND(v,min) if((v) < (min)) (v) = (min)
973#define UBOUND(v,max) if((v) > (max)) (v) = (max)
974
975#define LEADER(l,b) ((l << 8) | b)
976#define INTERMED(i,b) ((i << 16) | b)
977
978 switch(intermed_byte << 16 | leader_byte << 8 | command) {
979 case 0x40: // ICH - ECMA-48 8.3.64
980 count = CSI_ARG_COUNT(args[0]);
981
983 break;
984
985 rect.start_row = state->pos.row;
986 rect.end_row = state->pos.row + 1;
987 rect.start_col = state->pos.col;
988 if(state->mode.leftrightmargin)
990 else
991 rect.end_col = THISROWWIDTH(state);
992
993 scroll(state, rect, 0, -count);
994
995 break;
996
997 case 0x41: // CUU - ECMA-48 8.3.22
998 count = CSI_ARG_COUNT(args[0]);
999 state->pos.row -= count;
1000 state->at_phantom = 0;
1001 break;
1002
1003 case 0x42: // CUD - ECMA-48 8.3.19
1004 count = CSI_ARG_COUNT(args[0]);
1005 state->pos.row += count;
1006 state->at_phantom = 0;
1007 break;
1008
1009 case 0x43: // CUF - ECMA-48 8.3.20
1010 count = CSI_ARG_COUNT(args[0]);
1011 state->pos.col += count;
1012 state->at_phantom = 0;
1013 break;
1014
1015 case 0x44: // CUB - ECMA-48 8.3.18
1016 count = CSI_ARG_COUNT(args[0]);
1017 state->pos.col -= count;
1018 state->at_phantom = 0;
1019 break;
1020
1021 case 0x45: // CNL - ECMA-48 8.3.12
1022 count = CSI_ARG_COUNT(args[0]);
1023 state->pos.col = 0;
1024 state->pos.row += count;
1025 state->at_phantom = 0;
1026 break;
1027
1028 case 0x46: // CPL - ECMA-48 8.3.13
1029 count = CSI_ARG_COUNT(args[0]);
1030 state->pos.col = 0;
1031 state->pos.row -= count;
1032 state->at_phantom = 0;
1033 break;
1034
1035 case 0x47: // CHA - ECMA-48 8.3.9
1036 val = CSI_ARG_OR(args[0], 1);
1037 state->pos.col = val-1;
1038 state->at_phantom = 0;
1039 break;
1040
1041 case 0x48: // CUP - ECMA-48 8.3.21
1042 row = CSI_ARG_OR(args[0], 1);
1043 col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
1044 // zero-based
1045 state->pos.row = row-1;
1046 state->pos.col = col-1;
1047 if(state->mode.origin) {
1050 }
1051 state->at_phantom = 0;
1052 break;
1053
1054 case 0x49: // CHT - ECMA-48 8.3.10
1055 count = CSI_ARG_COUNT(args[0]);
1056 tab(state, count, +1);
1057 break;
1058
1059 case 0x4a: // ED - ECMA-48 8.3.39
1060 case LEADER('?', 0x4a): // DECSED - Selective Erase in Display
1061 selective = (leader_byte == '?');
1062 switch(CSI_ARG(args[0])) {
1063 case CSI_ARG_MISSING:
1064 case 0:
1065 rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
1066 rect.start_col = state->pos.col; rect.end_col = state->cols;
1067 if(rect.end_col > rect.start_col)
1068 erase(state, rect, selective);
1069
1070 rect.start_row = state->pos.row + 1; rect.end_row = state->rows;
1071 rect.start_col = 0;
1072 for(int row = rect.start_row; row < rect.end_row; row++)
1074 if(rect.end_row > rect.start_row)
1075 erase(state, rect, selective);
1076 break;
1077
1078 case 1:
1079 rect.start_row = 0; rect.end_row = state->pos.row;
1080 rect.start_col = 0; rect.end_col = state->cols;
1081 for(int row = rect.start_row; row < rect.end_row; row++)
1083 if(rect.end_col > rect.start_col)
1084 erase(state, rect, selective);
1085
1086 rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
1087 rect.end_col = state->pos.col + 1;
1088 if(rect.end_row > rect.start_row)
1089 erase(state, rect, selective);
1090 break;
1091
1092 case 2:
1093 rect.start_row = 0; rect.end_row = state->rows;
1094 rect.start_col = 0; rect.end_col = state->cols;
1095 for(int row = rect.start_row; row < rect.end_row; row++)
1097 erase(state, rect, selective);
1098 break;
1099
1100 case 3:
1103 return 1;
1104 break;
1105 }
1106 break;
1107
1108 case 0x4b: // EL - ECMA-48 8.3.41
1109 case LEADER('?', 0x4b): // DECSEL - Selective Erase in Line
1110 selective = (leader_byte == '?');
1111 rect.start_row = state->pos.row;
1112 rect.end_row = state->pos.row + 1;
1113
1114 switch(CSI_ARG(args[0])) {
1115 case CSI_ARG_MISSING:
1116 case 0:
1117 rect.start_col = state->pos.col; rect.end_col = THISROWWIDTH(state); break;
1118 case 1:
1119 rect.start_col = 0; rect.end_col = state->pos.col + 1; break;
1120 case 2:
1121 rect.start_col = 0; rect.end_col = THISROWWIDTH(state); break;
1122 default:
1123 return 0;
1124 }
1125
1126 if(rect.end_col > rect.start_col)
1127 erase(state, rect, selective);
1128
1129 break;
1130
1131 case 0x4c: // IL - ECMA-48 8.3.67
1132 count = CSI_ARG_COUNT(args[0]);
1133
1135 break;
1136
1137 rect.start_row = state->pos.row;
1141
1142 scroll(state, rect, -count, 0);
1143
1144 break;
1145
1146 case 0x4d: // DL - ECMA-48 8.3.32
1147 count = CSI_ARG_COUNT(args[0]);
1148
1150 break;
1151
1152 rect.start_row = state->pos.row;
1156
1157 scroll(state, rect, count, 0);
1158
1159 break;
1160
1161 case 0x50: // DCH - ECMA-48 8.3.26
1162 count = CSI_ARG_COUNT(args[0]);
1163
1165 break;
1166
1167 rect.start_row = state->pos.row;
1168 rect.end_row = state->pos.row + 1;
1169 rect.start_col = state->pos.col;
1170 if(state->mode.leftrightmargin)
1172 else
1173 rect.end_col = THISROWWIDTH(state);
1174
1175 scroll(state, rect, 0, count);
1176
1177 break;
1178
1179 case 0x53: // SU - ECMA-48 8.3.147
1180 count = CSI_ARG_COUNT(args[0]);
1181
1186
1187 scroll(state, rect, count, 0);
1188
1189 break;
1190
1191 case 0x54: // SD - ECMA-48 8.3.113
1192 count = CSI_ARG_COUNT(args[0]);
1193
1198
1199 scroll(state, rect, -count, 0);
1200
1201 break;
1202
1203 case 0x58: // ECH - ECMA-48 8.3.38
1204 count = CSI_ARG_COUNT(args[0]);
1205
1206 rect.start_row = state->pos.row;
1207 rect.end_row = state->pos.row + 1;
1208 rect.start_col = state->pos.col;
1209 rect.end_col = state->pos.col + count;
1211
1212 erase(state, rect, 0);
1213 break;
1214
1215 case 0x5a: // CBT - ECMA-48 8.3.7
1216 count = CSI_ARG_COUNT(args[0]);
1217 tab(state, count, -1);
1218 break;
1219
1220 case 0x60: // HPA - ECMA-48 8.3.57
1221 col = CSI_ARG_OR(args[0], 1);
1222 state->pos.col = col-1;
1223 state->at_phantom = 0;
1224 break;
1225
1226 case 0x61: // HPR - ECMA-48 8.3.59
1227 count = CSI_ARG_COUNT(args[0]);
1228 state->pos.col += count;
1229 state->at_phantom = 0;
1230 break;
1231
1232 case 0x62: { // REP - ECMA-48 8.3.103
1233 const int row_width = THISROWWIDTH(state);
1234 count = CSI_ARG_COUNT(args[0]);
1235 col = state->pos.col + count;
1236 UBOUND(col, row_width);
1237 while (state->pos.col < col) {
1240 }
1241 if (state->pos.col + state->combine_width >= row_width) {
1242 if (state->mode.autowrap) {
1243 state->at_phantom = 1;
1244 cancel_phantom = 0;
1245 }
1246 }
1247 break;
1248 }
1249
1250 case 0x63: // DA - ECMA-48 8.3.24
1251 val = CSI_ARG_OR(args[0], 0);
1252 if(val == 0)
1253 // DEC VT100 response
1255 break;
1256
1257 case LEADER('>', 0x63): // DEC secondary Device Attributes
1258 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, ">%d;%d;%dc", 0, 100, 0);
1259 break;
1260
1261 case 0x64: // VPA - ECMA-48 8.3.158
1262 row = CSI_ARG_OR(args[0], 1);
1263 state->pos.row = row-1;
1264 if(state->mode.origin)
1266 state->at_phantom = 0;
1267 break;
1268
1269 case 0x65: // VPR - ECMA-48 8.3.160
1270 count = CSI_ARG_COUNT(args[0]);
1271 state->pos.row += count;
1272 state->at_phantom = 0;
1273 break;
1274
1275 case 0x66: // HVP - ECMA-48 8.3.63
1276 row = CSI_ARG_OR(args[0], 1);
1277 col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
1278 // zero-based
1279 state->pos.row = row-1;
1280 state->pos.col = col-1;
1281 if(state->mode.origin) {
1284 }
1285 state->at_phantom = 0;
1286 break;
1287
1288 case 0x67: // TBC - ECMA-48 8.3.154
1289 val = CSI_ARG_OR(args[0], 0);
1290
1291 switch(val) {
1292 case 0:
1294 break;
1295 case 3:
1296 case 5:
1297 for(col = 0; col < state->cols; col++)
1299 break;
1300 case 1:
1301 case 2:
1302 case 4:
1303 break;
1304 /* TODO: 1, 2 and 4 aren't meaningful yet without line tab stops */
1305 default:
1306 return 0;
1307 }
1308 break;
1309
1310 case 0x68: // SM - ECMA-48 8.3.125
1311 if(!CSI_ARG_IS_MISSING(args[0]))
1312 set_mode(state, CSI_ARG(args[0]), 1);
1313 break;
1314
1315 case LEADER('?', 0x68): // DEC private mode set
1316 for(int i = 0; i < argcount; i++) {
1317 if(!CSI_ARG_IS_MISSING(args[i]))
1318 set_dec_mode(state, CSI_ARG(args[i]), 1);
1319 }
1320 break;
1321
1322 case 0x6a: // HPB - ECMA-48 8.3.58
1323 count = CSI_ARG_COUNT(args[0]);
1324 state->pos.col -= count;
1325 state->at_phantom = 0;
1326 break;
1327
1328 case 0x6b: // VPB - ECMA-48 8.3.159
1329 count = CSI_ARG_COUNT(args[0]);
1330 state->pos.row -= count;
1331 state->at_phantom = 0;
1332 break;
1333
1334 case 0x6c: // RM - ECMA-48 8.3.106
1335 if(!CSI_ARG_IS_MISSING(args[0]))
1336 set_mode(state, CSI_ARG(args[0]), 0);
1337 break;
1338
1339 case LEADER('?', 0x6c): // DEC private mode reset
1340 for(int i = 0; i < argcount; i++) {
1341 if(!CSI_ARG_IS_MISSING(args[i]))
1342 set_dec_mode(state, CSI_ARG(args[i]), 0);
1343 }
1344 break;
1345
1346 case 0x6d: // SGR - ECMA-48 8.3.117
1347 vterm_state_setpen(state, args, argcount);
1348 break;
1349
1350 case LEADER('?', 0x6d): // DECSGR
1351 /* No actual DEC terminal recognised these, but some printers did. These
1352 * are alternative ways to request subscript/superscript/off
1353 */
1354 for(int argi = 0; argi < argcount; argi++) {
1355 long arg;
1356 switch(arg = CSI_ARG(args[argi])) {
1357 case 4: // Superscript on
1358 arg = 73;
1359 vterm_state_setpen(state, &arg, 1);
1360 break;
1361 case 5: // Subscript on
1362 arg = 74;
1363 vterm_state_setpen(state, &arg, 1);
1364 break;
1365 case 24: // Super+subscript off
1366 arg = 75;
1367 vterm_state_setpen(state, &arg, 1);
1368 break;
1369 }
1370 }
1371 break;
1372
1373 case 0x6e: // DSR - ECMA-48 8.3.35
1374 case LEADER('?', 0x6e): // DECDSR
1375 val = CSI_ARG_OR(args[0], 0);
1376
1377 {
1378 char *qmark = (leader_byte == '?') ? "?" : "";
1379
1380 switch(val) {
1381 case 0: case 1: case 2: case 3: case 4:
1382 // ignore - these are replies
1383 break;
1384 case 5:
1386 break;
1387 case 6: // CPR - cursor position report
1388 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s%d;%dR", qmark, state->pos.row + 1, state->pos.col + 1);
1389 break;
1390 }
1391 }
1392 break;
1393
1394
1395 case INTERMED('!', 0x70): // DECSTR - DEC soft terminal reset
1397 break;
1398
1399 case LEADER('?', INTERMED('$', 0x70)):
1400 request_dec_mode(state, CSI_ARG(args[0]));
1401 break;
1402
1403 case LEADER('>', 0x71): // XTVERSION - xterm query version string
1405 break;
1406
1407 case INTERMED(' ', 0x71): // DECSCUSR - DEC set cursor shape
1408 val = CSI_ARG_OR(args[0], 1);
1409
1410 switch(val) {
1411 case 0: case 1:
1414 break;
1415 case 2:
1418 break;
1419 case 3:
1422 break;
1423 case 4:
1426 break;
1427 case 5:
1430 break;
1431 case 6:
1434 break;
1435 }
1436
1437 break;
1438
1439 case INTERMED('"', 0x71): // DECSCA - DEC select character protection attribute
1440 val = CSI_ARG_OR(args[0], 0);
1441
1442 switch(val) {
1443 case 0: case 2:
1444 state->protected_cell = 0;
1445 break;
1446 case 1:
1447 state->protected_cell = 1;
1448 break;
1449 }
1450
1451 break;
1452
1453 case 0x72: // DECSTBM - DEC custom
1454 state->scrollregion_top = CSI_ARG_OR(args[0], 1) - 1;
1455 state->scrollregion_bottom = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
1461 else
1463
1465 // Invalid
1468 }
1469
1470 // Setting the scrolling region restores the cursor to the home position
1471 state->pos.row = 0;
1472 state->pos.col = 0;
1473 if(state->mode.origin) {
1476 }
1477
1478 break;
1479
1480 case 0x73: // DECSLRM - DEC custom
1481 // Always allow setting these margins, just they won't take effect without DECVSSM
1482 state->scrollregion_left = CSI_ARG_OR(args[0], 1) - 1;
1483 state->scrollregion_right = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
1489 else
1491
1492 if(state->scrollregion_right > -1 &&
1494 // Invalid
1497 }
1498
1499 // Setting the scrolling region restores the cursor to the home position
1500 state->pos.row = 0;
1501 state->pos.col = 0;
1502 if(state->mode.origin) {
1505 }
1506
1507 break;
1508
1509 case INTERMED('\'', 0x7D): // DECIC
1510 count = CSI_ARG_COUNT(args[0]);
1511
1513 break;
1514
1517 rect.start_col = state->pos.col;
1519
1520 scroll(state, rect, 0, -count);
1521
1522 break;
1523
1524 case INTERMED('\'', 0x7E): // DECDC
1525 count = CSI_ARG_COUNT(args[0]);
1526
1528 break;
1529
1532 rect.start_col = state->pos.col;
1534
1535 scroll(state, rect, 0, count);
1536
1537 break;
1538
1539 default:
1541 if((*state->fallbacks->csi)(leader, args, argcount, intermed, command, state->fbdata))
1542 return 1;
1543
1544 return 0;
1545 }
1546
1547 if(state->mode.origin) {
1552 }
1553 else {
1554 LBOUND(state->pos.row, 0);
1555 UBOUND(state->pos.row, state->rows-1);
1556 LBOUND(state->pos.col, 0);
1558 }
1559
1560 updatecursor(state, &oldpos, cancel_phantom);
1561
1562#ifdef DEBUG
1563 if(state->pos.row < 0 || state->pos.row >= state->rows ||
1564 state->pos.col < 0 || state->pos.col >= state->cols) {
1565 fprintf(stderr, "Position out of bounds after CSI %c: (%d,%d)\n",
1567 abort();
1568 }
1569
1571 fprintf(stderr, "Scroll region height out of bounds after CSI %c: %d <= %d\n",
1573 abort();
1574 }
1575
1577 fprintf(stderr, "Scroll region width out of bounds after CSI %c: %d <= %d\n",
1579 abort();
1580 }
1581#endif
1582
1583 return 1;
1584}
1585
1586static char base64_one(uint8_t b)
1587{
1588 if(b < 26)
1589 return 'A' + b;
1590 else if(b < 52)
1591 return 'a' + b - 26;
1592 else if(b < 62)
1593 return '0' + b - 52;
1594 else if(b == 62)
1595 return '+';
1596 else if(b == 63)
1597 return '/';
1598 return 0;
1599}
1600
1601static uint8_t unbase64one(char c)
1602{
1603 if(c >= 'A' && c <= 'Z')
1604 return c - 'A';
1605 else if(c >= 'a' && c <= 'z')
1606 return c - 'a' + 26;
1607 else if(c >= '0' && c <= '9')
1608 return c - '0' + 52;
1609 else if(c == '+')
1610 return 62;
1611 else if(c == '/')
1612 return 63;
1613
1614 return 0xFF;
1615}
1616
1618{
1619 if(frag.initial) {
1620 state->tmp.selection.mask = 0;
1621 state->tmp.selection.state = SELECTION_INITIAL;
1622 }
1623
1624 while(!state->tmp.selection.state && frag.len) {
1625 /* Parse selection parameter */
1626 switch(frag.str[0]) {
1627 case 'c':
1629 break;
1630 case 'p':
1632 break;
1633 case 'q':
1635 break;
1636 case 's':
1638 break;
1639 case '0':
1640 case '1':
1641 case '2':
1642 case '3':
1643 case '4':
1644 case '5':
1645 case '6':
1646 case '7':
1647 state->tmp.selection.mask |= (VTERM_SELECTION_CUT0 << (frag.str[0] - '0'));
1648 break;
1649
1650 case ';':
1651 state->tmp.selection.state = SELECTION_SELECTED;
1652 if(!state->tmp.selection.mask)
1654 break;
1655 }
1656
1657 frag.str++;
1658 frag.len--;
1659 }
1660
1661 if(!frag.len) {
1662 /* Clear selection if we're already finished but didn't do anything */
1663 if(frag.final && state->selection.callbacks->set) {
1665 .str = NULL,
1666 .len = 0,
1667 .initial = state->tmp.selection.state != SELECTION_SET,
1668 .final = true,
1669 }, state->selection.user);
1670 }
1671 return;
1672 }
1673
1674 if(state->tmp.selection.state == SELECTION_SELECTED) {
1675 if(frag.str[0] == '?') {
1676 state->tmp.selection.state = SELECTION_QUERY;
1677 }
1678 else {
1679 state->tmp.selection.state = SELECTION_SET_INITIAL;
1680 state->tmp.selection.recvpartial = 0;
1681 }
1682 }
1683
1684 if(state->tmp.selection.state == SELECTION_QUERY) {
1685 if(state->selection.callbacks->query)
1686 (*state->selection.callbacks->query)(state->tmp.selection.mask, state->selection.user);
1687 return;
1688 }
1689
1690 if(state->tmp.selection.state == SELECTION_INVALID)
1691 return;
1692
1693 if(state->selection.callbacks->set) {
1694 size_t bufcur = 0;
1695 char *buffer = state->selection.buffer;
1696
1697 uint32_t x = 0; /* Current decoding value */
1698 int n = 0; /* Number of sextets consumed */
1699
1700 if(state->tmp.selection.recvpartial) {
1701 n = state->tmp.selection.recvpartial >> 24;
1702 x = state->tmp.selection.recvpartial & 0x03FFFF; /* could be up to 18 bits of state in here */
1703
1704 state->tmp.selection.recvpartial = 0;
1705 }
1706
1707 while((state->selection.buflen - bufcur) >= 3 && frag.len) {
1708 if(frag.str[0] == '=') {
1709 if(n == 2) {
1710 buffer[0] = (x >> 4) & 0xFF;
1711 buffer += 1, bufcur += 1;
1712 }
1713 if(n == 3) {
1714 buffer[0] = (x >> 10) & 0xFF;
1715 buffer[1] = (x >> 2) & 0xFF;
1716 buffer += 2, bufcur += 2;
1717 }
1718
1719 while(frag.len && frag.str[0] == '=')
1720 frag.str++, frag.len--;
1721
1722 n = 0;
1723 }
1724 else {
1725 uint8_t b = unbase64one(frag.str[0]);
1726 if(b == 0xFF) {
1727 DEBUG_LOG("base64decode bad input %02X\n", (uint8_t)frag.str[0]);
1728
1729 state->tmp.selection.state = SELECTION_INVALID;
1730 if(state->selection.callbacks->set) {
1732 .str = NULL,
1733 .len = 0,
1734 .initial = true,
1735 .final = true,
1736 }, state->selection.user);
1737 }
1738 break;
1739 }
1740
1741 x = (x << 6) | b;
1742 n++;
1743 frag.str++, frag.len--;
1744
1745 if(n == 4) {
1746 buffer[0] = (x >> 16) & 0xFF;
1747 buffer[1] = (x >> 8) & 0xFF;
1748 buffer[2] = (x >> 0) & 0xFF;
1749
1750 buffer += 3, bufcur += 3;
1751 x = 0;
1752 n = 0;
1753 }
1754 }
1755
1756 if(!frag.len || (state->selection.buflen - bufcur) < 3) {
1757 if(bufcur) {
1759 .str = state->selection.buffer,
1760 .len = bufcur,
1761 .initial = state->tmp.selection.state == SELECTION_SET_INITIAL,
1762 .final = frag.final && !frag.len,
1763 }, state->selection.user);
1764 state->tmp.selection.state = SELECTION_SET;
1765 }
1766
1767 buffer = state->selection.buffer;
1768 bufcur = 0;
1769 }
1770 }
1771
1772 if(n)
1773 state->tmp.selection.recvpartial = (n << 24) | x;
1774 }
1775}
1776
1777static int on_osc(int command, VTermStringFragment frag, void *user)
1778{
1779 VTermState *state = user;
1780
1781 switch(command) {
1782 case 0:
1785 return 1;
1786
1787 case 1:
1789 return 1;
1790
1791 case 2:
1793 return 1;
1794
1795 case 52:
1797 osc_selection(state, frag);
1798
1799 return 1;
1800
1801 default:
1803 if((*state->fallbacks->osc)(command, frag, state->fbdata))
1804 return 1;
1805 }
1806
1807 return 0;
1808}
1809
1811{
1812 VTerm *vt = state->vt;
1813
1814 char *tmp = state->tmp.decrqss;
1815
1816 if(frag.initial)
1817 tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
1818
1819 int i = 0;
1820 while(i < sizeof(state->tmp.decrqss)-1 && tmp[i])
1821 i++;
1822 while(i < sizeof(state->tmp.decrqss)-1 && frag.len--)
1823 tmp[i++] = (frag.str++)[0];
1824 tmp[i] = 0;
1825
1826 if(!frag.final)
1827 return;
1828
1829 switch(tmp[0] | tmp[1]<<8 | tmp[2]<<16) {
1830 case 'm': {
1831 // Query SGR
1832 long args[20];
1833 int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0]));
1834 size_t cur = 0;
1835
1836 cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
1837 vt->mode.ctrl8bit ? "\x90" "1$r" : ESC_S "P" "1$r"); // DCS 1$r ...
1838 if(cur >= vt->tmpbuffer_len)
1839 return;
1840
1841 for(int argi = 0; argi < argc; argi++) {
1842 cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
1843 argi == argc - 1 ? "%ld" :
1844 CSI_ARG_HAS_MORE(args[argi]) ? "%ld:" :
1845 "%ld;",
1846 CSI_ARG(args[argi]));
1847 if(cur >= vt->tmpbuffer_len)
1848 return;
1849 }
1850
1851 cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
1852 vt->mode.ctrl8bit ? "m" "\x9C" : "m" ESC_S "\\"); // ... m ST
1853 if(cur >= vt->tmpbuffer_len)
1854 return;
1855
1857 return;
1858 }
1859
1860 case 'r':
1861 // Query DECSTBM
1864 return;
1865
1866 case 's':
1867 // Query DECSLRM
1870 return;
1871
1872 case ' '|('q'<<8): {
1873 // Query DECSCUSR
1874 int reply;
1875 switch(state->mode.cursor_shape) {
1876 case VTERM_PROP_CURSORSHAPE_BLOCK: reply = 2; break;
1877 case VTERM_PROP_CURSORSHAPE_UNDERLINE: reply = 4; break;
1878 case VTERM_PROP_CURSORSHAPE_BAR_LEFT: reply = 6; break;
1879 }
1880 if(state->mode.cursor_blink)
1881 reply--;
1883 "1$r%d q", reply);
1884 return;
1885 }
1886
1887 case '\"'|('q'<<8):
1888 // Query DECSCA
1890 "1$r%d\"q", state->protected_cell ? 1 : 2);
1891 return;
1892 }
1893
1895}
1896
1897static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
1898{
1899 VTermState *state = user;
1900
1901 if(commandlen == 2 && strneq(command, "$q", 2)) {
1903 return 1;
1904 }
1905 else if(state->fallbacks && state->fallbacks->dcs)
1906 if((*state->fallbacks->dcs)(command, commandlen, frag, state->fbdata))
1907 return 1;
1908
1909 DEBUG_LOG("libvterm: Unhandled DCS %.*s\n", (int)commandlen, command);
1910 return 0;
1911}
1912
1913static int on_apc(VTermStringFragment frag, void *user)
1914{
1915 VTermState *state = user;
1916
1918 if((*state->fallbacks->apc)(frag, state->fbdata))
1919 return 1;
1920
1921 /* No DEBUG_LOG because all APCs are unhandled */
1922 return 0;
1923}
1924
1925static int on_pm(VTermStringFragment frag, void *user)
1926{
1927 VTermState *state = user;
1928
1929 if(state->fallbacks && state->fallbacks->pm)
1930 if((*state->fallbacks->pm)(frag, state->fbdata))
1931 return 1;
1932
1933 /* No DEBUG_LOG because all PMs are unhandled */
1934 return 0;
1935}
1936
1937static int on_sos(VTermStringFragment frag, void *user)
1938{
1939 VTermState *state = user;
1940
1942 if((*state->fallbacks->sos)(frag, state->fbdata))
1943 return 1;
1944
1945 /* No DEBUG_LOG because all SOSs are unhandled */
1946 return 0;
1947}
1948
1949static int on_resize(int rows, int cols, void *user)
1950{
1951 VTermState *state = user;
1952 VTermPos oldpos = state->pos;
1953
1954 if(cols != state->cols) {
1955 unsigned char *newtabstops = vterm_allocator_malloc(state->vt, (cols + 7) / 8);
1956
1957 /* TODO: This can all be done much more efficiently bytewise */
1958 int col;
1959 for(col = 0; col < state->cols && col < cols; col++) {
1960 unsigned char mask = 1 << (col & 7);
1961 if(state->tabstops[col >> 3] & mask)
1962 newtabstops[col >> 3] |= mask;
1963 else
1964 newtabstops[col >> 3] &= ~mask;
1965 }
1966
1967 for( ; col < cols; col++) {
1968 unsigned char mask = 1 << (col & 7);
1969 if(col % 8 == 0)
1970 newtabstops[col >> 3] |= mask;
1971 else
1972 newtabstops[col >> 3] &= ~mask;
1973 }
1974
1976 state->tabstops = newtabstops;
1977 }
1978
1979 state->rows = rows;
1980 state->cols = cols;
1981
1982 if(state->scrollregion_bottom > -1)
1984 if(state->scrollregion_right > -1)
1986
1987 VTermStateFields fields = {
1988 .pos = state->pos,
1989 .lineinfos = { [0] = state->lineinfos[0], [1] = state->lineinfos[1] },
1990 };
1991
1993 (*state->callbacks->resize)(rows, cols, &fields, state->cbdata);
1994 state->pos = fields.pos;
1995
1996 state->lineinfos[0] = fields.lineinfos[0];
1997 state->lineinfos[1] = fields.lineinfos[1];
1998 }
1999 else {
2000 if(rows != state->rows) {
2001 for(int bufidx = BUFIDX_PRIMARY; bufidx <= BUFIDX_ALTSCREEN; bufidx++) {
2002 VTermLineInfo *oldlineinfo = state->lineinfos[bufidx];
2003 if(!oldlineinfo)
2004 continue;
2005
2006 VTermLineInfo *newlineinfo = vterm_allocator_malloc(state->vt, rows * sizeof(VTermLineInfo));
2007
2008 int row;
2009 for(row = 0; row < state->rows && row < rows; row++) {
2010 newlineinfo[row] = oldlineinfo[row];
2011 }
2012
2013 for( ; row < rows; row++) {
2014 newlineinfo[row] = (VTermLineInfo){
2015 .doublewidth = 0,
2016 };
2017 }
2018
2020 state->lineinfos[bufidx] = newlineinfo;
2021 }
2022 }
2023 }
2024
2026
2027 if(state->at_phantom && state->pos.col < cols-1) {
2028 state->at_phantom = 0;
2029 state->pos.col++;
2030 }
2031
2032 if(state->pos.row < 0)
2033 state->pos.row = 0;
2034 if(state->pos.row >= rows)
2035 state->pos.row = rows - 1;
2036 if(state->pos.col < 0)
2037 state->pos.col = 0;
2038 if(state->pos.col >= cols)
2039 state->pos.col = cols - 1;
2040
2041 updatecursor(state, &oldpos, 1);
2042
2043 return 1;
2044}
2045
2047 .text = on_text,
2048 .control = on_control,
2049 .escape = on_escape,
2050 .csi = on_csi,
2051 .osc = on_osc,
2052 .dcs = on_dcs,
2053 .apc = on_apc,
2054 .pm = on_pm,
2055 .sos = on_sos,
2056 .resize = on_resize,
2057};
2058
2060{
2061 if(vt->state)
2062 return vt->state;
2063
2065 vt->state = state;
2066
2068
2069 return state;
2070}
2071
2073{
2078
2079 state->mode.keypad = 0;
2080 state->mode.cursor = 0;
2081 state->mode.autowrap = 1;
2082 state->mode.insert = 0;
2083 state->mode.newline = 0;
2084 state->mode.alt_screen = 0;
2085 state->mode.origin = 0;
2086 state->mode.leftrightmargin = 0;
2087 state->mode.bracketpaste = 0;
2088 state->mode.report_focus = 0;
2089
2090 state->mouse_flags = 0;
2091
2092 state->vt->mode.ctrl8bit = 0;
2093
2094 for(int col = 0; col < state->cols; col++)
2095 if(col % 8 == 0)
2096 set_col_tabstop(state, col);
2097 else
2099
2100 for(int row = 0; row < state->rows; row++)
2102
2105
2107
2108 VTermEncoding *default_enc = state->vt->mode.utf8 ?
2111
2112 for(int i = 0; i < 4; i++) {
2113 state->encoding[i].enc = default_enc;
2114 if(default_enc->init)
2115 (*default_enc->init)(default_enc, state->encoding[i].data);
2116 }
2117
2118 state->gl_set = 0;
2119 state->gr_set = 1;
2120 state->gsingle_set = 0;
2121
2122 state->protected_cell = 0;
2123
2124 // Initialise the props
2128
2129 if(hard) {
2130 state->pos.row = 0;
2131 state->pos.col = 0;
2132 state->at_phantom = 0;
2133
2134 VTermRect rect = { 0, state->rows, 0, state->cols };
2135 erase(state, rect, 0);
2136 }
2137}
2138
2140{
2141 *cursorpos = state->pos;
2142}
2143
2145{
2146 if(callbacks) {
2147 state->callbacks = callbacks;
2148 state->cbdata = user;
2149
2152 }
2153 else {
2154 state->callbacks = NULL;
2155 state->cbdata = NULL;
2156 }
2157}
2158
2160{
2161 return state->cbdata;
2162}
2163
2165{
2166 if(fallbacks) {
2168 state->fbdata = user;
2169 }
2170 else {
2171 state->fallbacks = NULL;
2172 state->fbdata = NULL;
2173 }
2174}
2175
2180
2182{
2183 /* Only store the new value of the property if usercode said it was happy.
2184 * This is especially important for altscreen switching */
2186 if(!(*state->callbacks->settermprop)(prop, val, state->cbdata))
2187 return 0;
2188
2189 switch(prop) {
2190 case VTERM_PROP_TITLE:
2192 // we don't store these, just transparently pass through
2193 return 1;
2195 state->mode.cursor_visible = val->boolean;
2196 return 1;
2198 state->mode.cursor_blink = val->boolean;
2199 return 1;
2201 state->mode.cursor_shape = val->number;
2202 return 1;
2203 case VTERM_PROP_REVERSE:
2204 state->mode.screen = val->boolean;
2205 return 1;
2207 state->mode.alt_screen = val->boolean;
2209 if(state->mode.alt_screen) {
2210 VTermRect rect = {
2211 .start_row = 0,
2212 .start_col = 0,
2213 .end_row = state->rows,
2214 .end_col = state->cols,
2215 };
2216 erase(state, rect, 0);
2217 }
2218 return 1;
2219 case VTERM_PROP_MOUSE:
2220 state->mouse_flags = 0;
2221 if(val->number)
2223 if(val->number == VTERM_PROP_MOUSE_DRAG)
2225 if(val->number == VTERM_PROP_MOUSE_MOVE)
2227 return 1;
2229 state->mode.report_focus = val->boolean;
2230 return 1;
2231
2232 case VTERM_N_PROPS:
2233 return 0;
2234 }
2235
2236 return 0;
2237}
2238
2240{
2241 if(state->mode.report_focus)
2243}
2244
2246{
2247 if(state->mode.report_focus)
2249}
2250
2252{
2253 return state->lineinfo + row;
2254}
2255
2257 char *buffer, size_t buflen)
2258{
2259 if(buflen && !buffer)
2260 buffer = vterm_allocator_malloc(state->vt, buflen);
2261
2262 state->selection.callbacks = callbacks;
2263 state->selection.user = user;
2264 state->selection.buffer = buffer;
2265 state->selection.buflen = buflen;
2266}
2267
2269{
2270 VTerm *vt = state->vt;
2271
2272 if(frag.initial) {
2273 /* TODO: support sending more than one mask bit */
2274 const static char selection_chars[] = "cpqs";
2275 int idx;
2276 for(idx = 0; idx < 4; idx++)
2277 if(mask & (1 << idx))
2278 break;
2279
2280 vterm_push_output_sprintf_str(vt, C1_OSC, false, "52;%c;", selection_chars[idx]);
2281
2282 state->tmp.selection.sendpartial = 0;
2283 }
2284
2285 if(frag.len) {
2286 size_t bufcur = 0;
2287 char *buffer = state->selection.buffer;
2288
2289 uint32_t x = 0;
2290 int n = 0;
2291
2292 if(state->tmp.selection.sendpartial) {
2293 n = state->tmp.selection.sendpartial >> 24;
2294 x = state->tmp.selection.sendpartial & 0xFFFFFF;
2295
2296 state->tmp.selection.sendpartial = 0;
2297 }
2298
2299 while((state->selection.buflen - bufcur) >= 4 && frag.len) {
2300 x = (x << 8) | frag.str[0];
2301 n++;
2302 frag.str++, frag.len--;
2303
2304 if(n == 3) {
2305 buffer[0] = base64_one((x >> 18) & 0x3F);
2306 buffer[1] = base64_one((x >> 12) & 0x3F);
2307 buffer[2] = base64_one((x >> 6) & 0x3F);
2308 buffer[3] = base64_one((x >> 0) & 0x3F);
2309
2310 buffer += 4, bufcur += 4;
2311 x = 0;
2312 n = 0;
2313 }
2314
2315 if(!frag.len || (state->selection.buflen - bufcur) < 4) {
2316 if(bufcur)
2317 vterm_push_output_bytes(vt, state->selection.buffer, bufcur);
2318
2319 buffer = state->selection.buffer;
2320 bufcur = 0;
2321 }
2322 }
2323
2324 if(n)
2325 state->tmp.selection.sendpartial = (n << 24) | x;
2326 }
2327
2328 if(frag.final) {
2329 if(state->tmp.selection.sendpartial) {
2330 int n = state->tmp.selection.sendpartial >> 24;
2331 uint32_t x = state->tmp.selection.sendpartial & 0xFFFFFF;
2332 char *buffer = state->selection.buffer;
2333
2334 /* n is either 1 or 2 now */
2335 x <<= (n == 1) ? 16 : 8;
2336
2337 buffer[0] = base64_one((x >> 18) & 0x3F);
2338 buffer[1] = base64_one((x >> 12) & 0x3F);
2339 buffer[2] = (n == 1) ? '=' : base64_one((x >> 6) & 0x3F);
2340 buffer[3] = '=';
2341
2342 vterm_push_output_sprintf_str(vt, 0, true, "%.*s", 4, buffer);
2343 }
2344 else
2345 vterm_push_output_sprintf_str(vt, 0, true, "");
2346 }
2347}
unsigned int uint32_t
Definition arch.h:11
unsigned char uint8_t
Definition arch.h:5
VTermEncoding * vterm_lookup_encoding(VTermEncodingType type, char designation)
Definition encoding.c:224
static VTermState * state
Definition harness.c:105
static VTermEncodingInstance encoding
Definition harness.c:108
static VTermStateFallbacks fallbacks
Definition harness.c:297
void * memcpy(void *dest, void const *source, size_t size)
Definition string.c:80
void * memmove(void *dest, const void *source, size_t size)
Definition string.c:106
uint32_t info
Definition elf.h:7
INTERNAL void vterm_state_savepen(VTermState *state, int save)
Definition pen.c:187
INTERNAL int vterm_state_getpen(VTermState *state, long args[], int argcount)
Definition pen.c:504
INTERNAL void vterm_state_resetpen(VTermState *state)
Definition pen.c:170
INTERNAL void vterm_state_newpen(VTermState *state)
Definition pen.c:159
INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argcount)
Definition pen.c:276
static uint8_t unbase64one(char c)
Definition state.c:1601
void vterm_state_send_selection(VTermState *state, VTermSelectionMask mask, VTermStringFragment frag)
Definition state.c:2268
#define LEADER(l, b)
static void request_status_string(VTermState *state, VTermStringFragment frag)
Definition state.c:1810
void * vterm_state_get_cbdata(VTermState *state)
Definition state.c:2159
static void savecursor(VTermState *state, int save)
Definition state.c:566
static void scroll(VTermState *state, VTermRect rect, int downward, int rightward)
Definition state.c:112
static int on_apc(VTermStringFragment frag, void *user)
Definition state.c:1913
void vterm_state_set_selection_callbacks(VTermState *state, const VTermSelectionCallbacks *callbacks, void *user, char *buffer, size_t buflen)
Definition state.c:2256
#define NO_FORCE
Definition state.c:238
void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user)
Definition state.c:2144
static int on_sos(VTermStringFragment frag, void *user)
Definition state.c:1937
static int settermprop_bool(VTermState *state, VTermProp prop, int v)
Definition state.c:548
static void set_lineinfo(VTermState *state, int row, int force, int dwl, int dhl)
Definition state.c:248
static void set_dec_mode(VTermState *state, int num, int val)
Definition state.c:749
static int on_control(unsigned char control, void *user)
Definition state.c:449
static void set_mode(VTermState *state, int num, int val)
Definition state.c:732
#define DWL_OFF
Definition state.c:241
static void putglyph(VTermState *state, const uint32_t chars[], int width, VTermPos pos)
Definition state.c:14
void * vterm_state_get_unrecognised_fbdata(VTermState *state)
Definition state.c:2176
int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val)
Definition state.c:2181
static void grow_combine_buffer(VTermState *state)
Definition state.c:174
#define INTERMED(i, b)
static void linefeed(VTermState *state)
Definition state.c:158
static int settermprop_string(VTermState *state, VTermProp prop, VTermStringFragment frag)
Definition state.c:560
#define DHL_BOTTOM
Definition state.c:246
void vterm_state_reset(VTermState *state, int hard)
Definition state.c:2072
INTERNAL void vterm_state_free(VTermState *state)
Definition state.c:102
static void request_dec_mode(VTermState *state, int num)
Definition state.c:843
void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos)
Definition state.c:2139
static int on_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user)
Definition state.c:926
static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
Definition state.c:1897
static char base64_one(uint8_t b)
Definition state.c:1586
#define DWL_ON
Definition state.c:242
static void updatecursor(VTermState *state, VTermPos *oldpos, int cancel_phantom)
Definition state.c:31
static const VTermParserCallbacks parser_callbacks
Definition state.c:2046
#define FORCE
Definition state.c:239
static int settermprop_int(VTermState *state, VTermProp prop, int v)
Definition state.c:554
static void request_version_string(VTermState *state)
Definition state.c:920
static void tab(VTermState *state, int count, int direction)
Definition state.c:217
#define DHL_OFF
Definition state.c:244
static void erase(VTermState *state, VTermRect rect, int selective)
Definition state.c:44
#define strneq(a, b, n)
Definition state.c:6
const VTermLineInfo * vterm_state_get_lineinfo(const VTermState *state, int row)
Definition state.c:2251
static int on_pm(VTermStringFragment frag, void *user)
Definition state.c:1925
static VTermState * vterm_state_new(VTerm *vt)
Definition state.c:59
static int is_cursor_in_scrollregion(const VTermState *state)
Definition state.c:205
void vterm_state_focus_out(VTermState *state)
Definition state.c:2245
static int on_text(const char bytes[], size_t len, void *user)
Definition state.c:272
#define LBOUND(v, min)
VTermState * vterm_obtain_state(VTerm *vt)
Definition state.c:2059
static int on_resize(int rows, int cols, void *user)
Definition state.c:1949
void vterm_state_focus_in(VTermState *state)
Definition state.c:2239
#define UBOUND(v, max)
static void clear_col_tabstop(VTermState *state, int col)
Definition state.c:193
static int is_col_tabstop(VTermState *state, int col)
Definition state.c:199
static int on_escape(const char *bytes, size_t len, void *user)
Definition state.c:591
#define DHL_TOP
Definition state.c:245
void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user)
Definition state.c:2164
static int on_osc(int command, VTermStringFragment frag, void *user)
Definition state.c:1777
static void set_col_tabstop(VTermState *state, int col)
Definition state.c:187
static void osc_selection(VTermState *state, VTermStringFragment frag)
Definition state.c:1617
void(* init)(VTermEncoding *enc, void *data)
void(* decode)(VTermEncoding *enc, void *data, uint32_t cp[], int *cpi, int cplen, const char bytes[], size_t *pos, size_t len)
int(* text)(const char *bytes, size_t len, void *user)
Definition vterm.h:403
int(* settermprop)(VTermProp prop, VTermValue *val, void *user)
Definition vterm.h:435
int(* initpen)(void *user)
Definition vterm.h:433
int(* erase)(VTermRect rect, int selective, void *user)
Definition vterm.h:432
int(* bell)(void *user)
Definition vterm.h:436
int(* resize)(int rows, int cols, VTermStateFields *fields, void *user)
Definition vterm.h:437
int(* moverect)(VTermRect dest, VTermRect src, void *user)
Definition vterm.h:431
int(* putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user)
Definition vterm.h:428
int(* scrollrect)(VTermRect rect, int downward, int rightward, void *user)
Definition vterm.h:430
int(* sb_clear)(void *user)
Definition vterm.h:439
int(* setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user)
Definition vterm.h:438
int(* movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user)
Definition vterm.h:429
int(* control)(unsigned char control, void *user)
Definition vterm.h:443
int(* csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user)
Definition vterm.h:444
int(* sos)(VTermStringFragment frag, void *user)
Definition vterm.h:449
int(* pm)(VTermStringFragment frag, void *user)
Definition vterm.h:448
int(* apc)(VTermStringFragment frag, void *user)
Definition vterm.h:447
int(* dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
Definition vterm.h:446
int(* osc)(int command, VTermStringFragment frag, void *user)
Definition vterm.h:445
struct VTermState::@19 selection
VTermEncodingInstance encoding[4]
VTermLineInfo * lineinfos[2]
unsigned int protected_cell
union VTermState::@18 tmp
int scrollregion_right
size_t combine_chars_size
unsigned char * tabstops
const VTermStateCallbacks * callbacks
VTermPos combine_pos
int scrollregion_left
VTermEncodingInstance encoding_utf8
int scrollregion_bottom
enum VTermState::@15 mouse_protocol
struct VTermState::@17 saved
struct VTermState::@16 mode
const VTermStateFallbacks * fallbacks
VTermLineInfo * lineinfo
uint32_t * combine_chars
VTermPos pos
static uint16_t uint16_t * height
Definition syscalls.h:104
static uint16_t * width
Definition syscalls.h:104
static uint16_t num
Definition syscalls.h:126
INTERNAL int vterm_unicode_is_combining(uint32_t codepoint)
Definition unicode.c:310
INTERNAL int vterm_unicode_width(uint32_t codepoint)
Definition unicode.c:302
static int rows
Definition unterm.c:31
static int cols
Definition unterm.c:30
static VTerm * vt
Definition unterm.c:27
INTERNAL void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt,...)
Definition vterm.c:174
INTERNAL void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, bool term, const char *fmt,...)
Definition vterm.c:200
INTERNAL void * vterm_allocator_malloc(VTerm *vt, size_t size)
Definition vterm.c:98
INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
Definition vterm.c:144
INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr)
Definition vterm.c:103
@ VTERM_PROP_MOUSE_NONE
Definition vterm.h:276
@ VTERM_PROP_MOUSE_MOVE
Definition vterm.h:279
@ VTERM_PROP_MOUSE_CLICK
Definition vterm.h:277
@ VTERM_PROP_MOUSE_DRAG
Definition vterm.h:278
#define VTERM_MAX_CHARS_PER_CELL
Definition vterm.h:24
#define CSI_ARG_OR(a, def)
Definition vterm.h:399
const char * str
Definition vterm.h:221
#define VTERM_VERSION_MAJOR
Definition vterm.h:14
int boolean
Definition vterm.h:228
VTermLineInfo * lineinfos[2]
Definition vterm.h:313
unsigned int doubleheight
Definition vterm.h:302
VTermPos pos
Definition vterm.h:312
#define CSI_ARG_HAS_MORE(a)
Definition vterm.h:392
int end_row
Definition vterm.h:45
int col
Definition vterm.h:32
void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
Definition parser.c:388
@ VTERM_PROP_CURSORSHAPE_BLOCK
Definition vterm.h:268
@ VTERM_PROP_CURSORSHAPE_UNDERLINE
Definition vterm.h:269
@ VTERM_PROP_CURSORSHAPE_BAR_LEFT
Definition vterm.h:270
int end_col
Definition vterm.h:47
const uint32_t * chars
Definition vterm.h:293
int row
Definition vterm.h:31
void vterm_scroll_rect(VTermRect rect, int downward, int rightward, int(*moverect)(VTermRect src, VTermRect dest, void *user), int(*eraserect)(VTermRect rect, int selective, void *user), void *user)
Definition vterm.c:305
#define CSI_ARG_MISSING
Definition vterm.h:396
int number
Definition vterm.h:229
unsigned int continuation
Definition vterm.h:303
#define VTERM_VERSION_MINOR
Definition vterm.h:15
VTermStringFragment string
Definition vterm.h:230
int start_row
Definition vterm.h:44
#define CSI_ARG(a)
Definition vterm.h:393
#define CSI_ARG_IS_MISSING(a)
Definition vterm.h:398
int start_col
Definition vterm.h:46
unsigned int doublewidth
Definition vterm.h:301
VTermProp
Definition vterm.h:252
@ VTERM_PROP_REVERSE
Definition vterm.h:259
@ VTERM_PROP_CURSORBLINK
Definition vterm.h:255
@ VTERM_PROP_ALTSCREEN
Definition vterm.h:256
@ VTERM_PROP_FOCUSREPORT
Definition vterm.h:262
@ VTERM_N_PROPS
Definition vterm.h:264
@ VTERM_PROP_CURSORVISIBLE
Definition vterm.h:254
@ VTERM_PROP_ICONNAME
Definition vterm.h:258
@ VTERM_PROP_TITLE
Definition vterm.h:257
@ VTERM_PROP_CURSORSHAPE
Definition vterm.h:260
@ VTERM_PROP_MOUSE
Definition vterm.h:261
VTermSelectionMask
Definition vterm.h:284
@ VTERM_SELECTION_CLIPBOARD
Definition vterm.h:285
@ VTERM_SELECTION_SELECT
Definition vterm.h:288
@ VTERM_SELECTION_SECONDARY
Definition vterm.h:287
@ VTERM_SELECTION_CUT0
Definition vterm.h:289
@ VTERM_SELECTION_PRIMARY
Definition vterm.h:286
#define CSI_ARG_COUNT(a)
Definition vterm.h:400
#define INTERNAL
@ C1_CSI
@ C1_DCS
@ C1_OSC
#define DEBUG_LOG(...)
size_t tmpbuffer_len
VTermState * state
#define SCROLLREGION_RIGHT(state)
#define THISROWWIDTH(state)
#define ESC_S
VTermEncoding * enc
char * tmpbuffer
@ ENC_UTF8
@ ENC_SINGLE_94
char data[4 *sizeof(uint32_t)]
#define BUFIDX_ALTSCREEN
struct VTerm::@23 mode
#define SCROLLREGION_LEFT(state)
#define MOUSE_WANT_MOVE
#define SCROLLREGION_BOTTOM(state)
#define ROWWIDTH(state, row)
#define BUFIDX_PRIMARY
#define MOUSE_WANT_DRAG
#define MOUSE_WANT_CLICK