2 * Copyright © 2004 Bruno T. C. de Oliveira
3 * Copyright © 2006 Pierre Habouzit
4 * Copyright © 2008-2013 Marc André Tanner
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
31 #include <sys/ioctl.h>
32 #include <sys/types.h>
35 #if defined(__linux__) || defined(__CYGWIN__)
37 #elif defined(__FreeBSD__)
39 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
46 # include "forkpty-aix.c"
48 # include "forkpty-sunos.c"
51 #ifndef NCURSES_ATTR_SHIFT
52 # define NCURSES_ATTR_SHIFT 8
57 # define NCURSES_ACS(c) (acs_map[(unsigned char)(c)])
58 # else /* BSD curses */
59 # define NCURSES_ACS(c) (_acs_map[(unsigned char)(c)])
63 #ifdef NCURSES_VERSION
64 # ifndef NCURSES_EXT_COLORS
65 # define NCURSES_EXT_COLORS 0
67 # if !NCURSES_EXT_COLORS
68 # define MAX_COLOR_PAIRS 256
71 #ifndef MAX_COLOR_PAIRS
72 # define MAX_COLOR_PAIRS COLOR_PAIRS
75 #if defined _AIX && defined CTRL
79 # define CTRL(k) ((k) & 0x1F)
82 #define IS_CONTROL(ch) !((ch) & 0xffffff60UL)
83 #define MIN(x, y) ((x) < (y) ? (x) : (y))
84 #define sstrlen(str) (sizeof(str) - 1)
86 static bool is_utf8
, has_default_colors
;
87 static short color_pairs_reserved
, color_pairs_max
, color_pair_current
;
88 static short *color2palette
, default_fg
, default_bg
;
89 static char vt_term
[32] = "dvtm";
114 int rows
, cols
, maxcols
;
115 attr_t curattrs
, savattrs
;
116 int curs_col
, curs_srow
, curs_scol
;
117 short curfg
, curbg
, savfg
, savbg
;
121 Buffer buffer_normal
;
122 Buffer buffer_alternate
;
130 unsigned seen_input
:1;
134 unsigned curskeymode
:1;
136 unsigned relposmode
:1;
137 unsigned mousetrack
:1;
138 unsigned graphmode
:1;
139 unsigned savgraphmode
:1;
141 /* buffers and parsing state */
144 unsigned int rlen
, elen
;
145 /* last known start row, start column */
148 /* xterm style window title */
150 vt_title_handler_t title_handler
;
154 static const char *keytable
[KEY_MAX
+1] = {
156 /* for the arrow keys the CSI / SS3 sequences are not stored here
157 * because they depend on the current cursor terminal mode
164 [KEY_SUP
] = "\e[1;2A",
167 [KEY_SDOWN
] = "\e[1;2B",
169 [KEY_SRIGHT
] = "\e[1;2C",
170 [KEY_SLEFT
] = "\e[1;2D",
171 [KEY_BACKSPACE
] = "\177",
174 [KEY_PPAGE
] = "\e[5~",
175 [KEY_NPAGE
] = "\e[6~",
176 [KEY_HOME
] = "\e[7~",
179 [KEY_SUSPEND
] = "\x1A", /* Ctrl+Z gets mapped to this */
180 [KEY_F(1)] = "\e[11~",
181 [KEY_F(2)] = "\e[12~",
182 [KEY_F(3)] = "\e[13~",
183 [KEY_F(4)] = "\e[14~",
184 [KEY_F(5)] = "\e[15~",
185 [KEY_F(6)] = "\e[17~",
186 [KEY_F(7)] = "\e[18~",
187 [KEY_F(8)] = "\e[19~",
188 [KEY_F(9)] = "\e[20~",
189 [KEY_F(10)] = "\e[21~",
190 [KEY_F(11)] = "\e[23~",
191 [KEY_F(12)] = "\e[24~",
192 [KEY_F(13)] = "\e[23~",
193 [KEY_F(14)] = "\e[24~",
194 [KEY_F(15)] = "\e[25~",
195 [KEY_F(16)] = "\e[26~",
196 [KEY_F(17)] = "\e[28~",
197 [KEY_F(18)] = "\e[29~",
198 [KEY_F(19)] = "\e[31~",
199 [KEY_F(20)] = "\e[32~",
200 [KEY_F(21)] = "\e[33~",
201 [KEY_F(22)] = "\e[34~",
208 static void puttab(Vt
*t
, int count
);
209 static void process_nonprinting(Vt
*t
, wchar_t wc
);
210 static void send_curs(Vt
*t
);
211 static void fill_scroll_buf(Buffer
*t
, int s
);
213 __attribute__ ((const))
214 static attr_t
build_attrs(attr_t curattrs
)
216 return ((curattrs
& ~A_COLOR
) | COLOR_PAIR(curattrs
& 0xff))
217 >> NCURSES_ATTR_SHIFT
;
220 static void row_set(Row
*row
, int start
, int len
, Buffer
*t
)
224 .attr
= t
? build_attrs(t
->curattrs
) : 0,
225 .fg
= t
? t
->curfg
: -1,
226 .bg
= t
? t
->curbg
: -1,
229 for (int i
= start
; i
< len
+ start
; i
++)
230 row
->cells
[i
] = cell
;
234 static void row_roll(Row
*start
, Row
*end
, int count
)
243 char buf
[count
* sizeof(Row
)];
244 memcpy(buf
, start
, count
* sizeof(Row
));
245 memmove(start
, start
+ count
, (n
- count
) * sizeof(Row
));
246 memcpy(end
- count
, buf
, count
* sizeof(Row
));
247 for (Row
*row
= start
; row
< end
; row
++)
253 static void buffer_free(Buffer
*t
)
255 for (int i
= 0; i
< t
->rows
; i
++)
256 free(t
->lines
[i
].cells
);
258 for (int i
= 0; i
< t
->scroll_buf_size
; i
++)
259 free(t
->scroll_buf
[i
].cells
);
264 static void buffer_resize(Buffer
*t
, int rows
, int cols
)
266 Row
*lines
= t
->lines
;
268 if (t
->rows
!= rows
) {
269 if (t
->curs_row
>= lines
+ rows
) {
270 /* scroll up instead of simply chopping off bottom */
271 fill_scroll_buf(t
, (t
->curs_row
- t
->lines
) - rows
+ 1);
273 while (t
->rows
> rows
) {
274 free(lines
[t
->rows
- 1].cells
);
278 lines
= realloc(lines
, sizeof(Row
) * rows
);
281 if (t
->maxcols
< cols
) {
282 for (int row
= 0; row
< t
->rows
; row
++) {
283 lines
[row
].cells
= realloc(lines
[row
].cells
, sizeof(Cell
) * cols
);
285 row_set(lines
+ row
, t
->cols
, cols
- t
->cols
, NULL
);
286 lines
[row
].dirty
= true;
288 Row
*sbuf
= t
->scroll_buf
;
289 for (int row
= 0; row
< t
->scroll_buf_size
; row
++) {
290 sbuf
[row
].cells
= realloc(sbuf
[row
].cells
, sizeof(Cell
) * cols
);
292 row_set(sbuf
+ row
, t
->cols
, cols
- t
->cols
, NULL
);
294 t
->tabs
= realloc(t
->tabs
, sizeof(*t
->tabs
) * cols
);
295 for (int col
= t
->cols
; col
< cols
; col
++)
296 t
->tabs
[col
] = !(col
& 7);
299 } else if (t
->cols
!= cols
) {
300 for (int row
= 0; row
< t
->rows
; row
++)
301 lines
[row
].dirty
= true;
306 if (t
->rows
< rows
) {
307 while (t
->rows
< rows
) {
308 lines
[t
->rows
].cells
= calloc(t
->maxcols
, sizeof(Cell
));
309 row_set(lines
+ t
->rows
, 0, t
->maxcols
, t
);
313 /* prepare for backfill */
314 if (t
->curs_row
>= t
->scroll_bot
- 1) {
315 deltarows
= t
->lines
+ rows
- t
->curs_row
- 1;
316 if (deltarows
> t
->scroll_above
)
317 deltarows
= t
->scroll_above
;
321 t
->curs_row
+= lines
- t
->lines
;
322 t
->scroll_top
= lines
;
323 t
->scroll_bot
= lines
+ rows
;
326 /* perform backfill */
328 fill_scroll_buf(t
, -deltarows
);
329 t
->curs_row
+= deltarows
;
333 static bool buffer_init(Buffer
*t
, int rows
, int cols
, int scroll_buf_size
)
335 Row
*lines
, *scroll_buf
;
336 t
->lines
= lines
= calloc(rows
, sizeof(Row
));
339 t
->curattrs
= A_NORMAL
; /* white text over black background */
340 t
->curfg
= t
->curbg
= -1;
341 for (Row
*row
= lines
, *end
= lines
+ rows
; row
< end
; row
++) {
342 row
->cells
= malloc(cols
* sizeof(Cell
));
344 t
->rows
= row
- lines
;
347 row_set(row
, 0, cols
, NULL
);
350 if (scroll_buf_size
< 0)
352 t
->scroll_buf
= scroll_buf
= calloc(scroll_buf_size
, sizeof(Row
));
353 if (!scroll_buf
&& scroll_buf_size
)
355 for (Row
*row
= scroll_buf
, *end
= scroll_buf
+ scroll_buf_size
; row
< end
; row
++) {
356 row
->cells
= calloc(cols
, sizeof(Cell
));
358 t
->scroll_buf_size
= row
- scroll_buf
;
362 t
->tabs
= calloc(cols
, sizeof(*t
->tabs
));
365 for (int col
= 8; col
< cols
; col
+= 8)
369 /* initial scrolling area is the whole window */
370 t
->scroll_top
= lines
;
371 t
->scroll_bot
= lines
+ rows
;
372 t
->scroll_buf_size
= scroll_buf_size
;
373 t
->maxcols
= t
->cols
= cols
;
381 static void buffer_boundry(Buffer
*b
, Row
**bs
, Row
**be
, Row
**as
, Row
**ae
) {
390 if (!b
->scroll_buf_size
)
393 if (b
->scroll_above
) {
395 *bs
= &b
->scroll_buf
[(b
->scroll_cur
- b
->scroll_above
+ b
->scroll_buf_size
) % b
->scroll_buf_size
];
397 *be
= &b
->scroll_buf
[(b
->scroll_cur
-1 + b
->scroll_buf_size
) % b
->scroll_buf_size
];
399 if (b
->scroll_below
) {
401 *as
= &b
->scroll_buf
[b
->scroll_cur
];
403 *ae
= &b
->scroll_buf
[(b
->scroll_cur
+ b
->scroll_below
-1) % b
->scroll_buf_size
];
407 static Row
*buffer_row_first(Buffer
*b
) {
409 if (!b
->scroll_buf_size
|| !b
->scroll_above
)
411 buffer_boundry(b
, &bstart
, NULL
, NULL
, NULL
);
415 static Row
*buffer_row_last(Buffer
*b
) {
417 if (!b
->scroll_buf_size
|| !b
->scroll_below
)
418 return b
->lines
+ b
->rows
- 1;
419 buffer_boundry(b
, NULL
, NULL
, NULL
, &aend
);
423 static Row
*buffer_row_next(Buffer
*b
, Row
*row
)
425 Row
*before_start
, *before_end
, *after_start
, *after_end
;
426 Row
*first
= b
->lines
, *last
= b
->lines
+ b
->rows
- 1;
431 buffer_boundry(b
, &before_start
, &before_end
, &after_start
, &after_end
);
433 if (row
>= first
&& row
< last
)
437 if (row
== before_end
)
439 if (row
== after_end
)
441 if (row
== &b
->scroll_buf
[b
->scroll_buf_size
- 1])
442 return b
->scroll_buf
;
446 static Row
*buffer_row_prev(Buffer
*b
, Row
*row
)
448 Row
*before_start
, *before_end
, *after_start
, *after_end
;
449 Row
*first
= b
->lines
, *last
= b
->lines
+ b
->rows
- 1;
454 buffer_boundry(b
, &before_start
, &before_end
, &after_start
, &after_end
);
456 if (row
> first
&& row
<= last
)
460 if (row
== before_start
)
462 if (row
== after_start
)
464 if (row
== b
->scroll_buf
)
465 return &b
->scroll_buf
[b
->scroll_buf_size
- 1];
470 static void clamp_cursor_to_bounds(Vt
*t
)
472 Buffer
*b
= t
->buffer
;
473 Row
*lines
= t
->relposmode
? b
->scroll_top
: b
->lines
;
474 int rows
= t
->relposmode
? b
->scroll_bot
- b
->scroll_top
: b
->rows
;
476 if (b
->curs_row
< lines
)
478 if (b
->curs_row
>= lines
+ rows
)
479 b
->curs_row
= lines
+ rows
- 1;
482 if (b
->curs_col
>= b
->cols
)
483 b
->curs_col
= b
->cols
- 1;
486 static void save_curs(Vt
*t
)
488 Buffer
*b
= t
->buffer
;
489 b
->curs_srow
= b
->curs_row
- b
->lines
;
490 b
->curs_scol
= b
->curs_col
;
493 static void restore_curs(Vt
*t
)
495 Buffer
*b
= t
->buffer
;
496 b
->curs_row
= b
->lines
+ b
->curs_srow
;
497 b
->curs_col
= b
->curs_scol
;
498 clamp_cursor_to_bounds(t
);
501 static void save_attrs(Vt
*t
)
503 Buffer
*b
= t
->buffer
;
504 b
->savattrs
= b
->curattrs
;
507 t
->savgraphmode
= t
->graphmode
;
510 static void restore_attrs(Vt
*t
)
512 Buffer
*b
= t
->buffer
;
513 b
->curattrs
= b
->savattrs
;
516 t
->graphmode
= t
->savgraphmode
;
519 static void fill_scroll_buf(Buffer
*t
, int s
)
521 /* work in screenfuls */
522 int ssz
= t
->scroll_bot
- t
->scroll_top
;
524 fill_scroll_buf(t
, ssz
);
525 fill_scroll_buf(t
, s
- ssz
);
529 fill_scroll_buf(t
, -ssz
);
530 fill_scroll_buf(t
, s
+ ssz
);
534 t
->scroll_above
+= s
;
535 if (t
->scroll_above
>= t
->scroll_buf_size
)
536 t
->scroll_above
= t
->scroll_buf_size
;
538 if (s
> 0 && t
->scroll_buf_size
) {
539 for (int i
= 0; i
< s
; i
++) {
540 Row tmp
= t
->scroll_top
[i
];
541 t
->scroll_top
[i
] = t
->scroll_buf
[t
->scroll_cur
];
542 t
->scroll_buf
[t
->scroll_cur
] = tmp
;
545 if (t
->scroll_cur
== t
->scroll_buf_size
)
549 row_roll(t
->scroll_top
, t
->scroll_bot
, s
);
550 if (s
< 0 && t
->scroll_buf_size
) {
551 for (int i
= (-s
) - 1; i
>= 0; i
--) {
553 if (t
->scroll_cur
== -1)
554 t
->scroll_cur
= t
->scroll_buf_size
- 1;
556 Row tmp
= t
->scroll_top
[i
];
557 t
->scroll_top
[i
] = t
->scroll_buf
[t
->scroll_cur
];
558 t
->scroll_buf
[t
->scroll_cur
] = tmp
;
559 t
->scroll_top
[i
].dirty
= true;
564 static void cursor_line_down(Vt
*t
)
566 Buffer
*b
= t
->buffer
;
567 row_set(b
->curs_row
, b
->cols
, b
->maxcols
- b
->cols
, NULL
);
569 if (b
->curs_row
< b
->scroll_bot
)
574 b
->curs_row
= b
->scroll_bot
- 1;
575 fill_scroll_buf(b
, 1);
576 row_set(b
->curs_row
, 0, b
->cols
, b
);
579 static void new_escape_sequence(Vt
*t
)
586 static void cancel_escape_sequence(Vt
*t
)
593 static bool is_valid_csi_ender(int c
)
595 return (c
>= 'a' && c
<= 'z')
596 || (c
>= 'A' && c
<= 'Z')
597 || (c
== '@' || c
== '`');
600 /* interprets a 'set attribute' (SGR) CSI escape sequence */
601 static void interpret_csi_sgr(Vt
*t
, int param
[], int pcount
)
603 Buffer
*b
= t
->buffer
;
605 /* special case: reset attributes */
606 b
->curattrs
= A_NORMAL
;
607 b
->curfg
= b
->curbg
= -1;
611 for (int i
= 0; i
< pcount
; i
++) {
614 b
->curattrs
= A_NORMAL
;
615 b
->curfg
= b
->curbg
= -1;
618 b
->curattrs
|= A_BOLD
;
621 b
->curattrs
|= A_DIM
;
625 b
->curattrs
|= A_ITALIC
;
629 b
->curattrs
|= A_UNDERLINE
;
632 b
->curattrs
|= A_BLINK
;
635 b
->curattrs
|= A_REVERSE
;
638 b
->curattrs
|= A_INVIS
;
641 b
->curattrs
&= ~(A_BOLD
| A_DIM
);
645 b
->curattrs
&= ~A_ITALIC
;
649 b
->curattrs
&= ~A_UNDERLINE
;
652 b
->curattrs
&= ~A_BLINK
;
655 b
->curattrs
&= ~A_REVERSE
;
658 b
->curattrs
&= ~A_INVIS
;
660 case 30 ... 37: /* fg */
661 b
->curfg
= param
[i
] - 30;
664 if ((i
+ 2) < pcount
&& param
[i
+ 1] == 5) {
665 b
->curfg
= param
[i
+ 2];
672 case 40 ... 47: /* bg */
673 b
->curbg
= param
[i
] - 40;
676 if ((i
+ 2) < pcount
&& param
[i
+ 1] == 5) {
677 b
->curbg
= param
[i
+ 2];
684 case 90 ... 97: /* hi fg */
685 b
->curfg
= param
[i
] - 82;
687 case 100 ... 107: /* hi bg */
688 b
->curbg
= param
[i
] - 92;
696 /* interprets an 'erase display' (ED) escape sequence */
697 static void interpret_csi_ed(Vt
*t
, int param
[], int pcount
)
699 Row
*row
, *start
, *end
;
700 Buffer
*b
= t
->buffer
;
703 b
->curattrs
= A_NORMAL
;
704 b
->curfg
= b
->curbg
= -1;
706 if (pcount
&& param
[0] == 2) {
708 end
= b
->lines
+ b
->rows
;
709 } else if (pcount
&& param
[0] == 1) {
712 row_set(b
->curs_row
, 0, b
->curs_col
+ 1, b
);
714 row_set(b
->curs_row
, b
->curs_col
, b
->cols
- b
->curs_col
, b
);
715 start
= b
->curs_row
+ 1;
716 end
= b
->lines
+ b
->rows
;
719 for (row
= start
; row
< end
; row
++)
720 row_set(row
, 0, b
->cols
, b
);
725 /* interprets a 'move cursor' (CUP) escape sequence */
726 static void interpret_csi_cup(Vt
*t
, int param
[], int pcount
)
728 Buffer
*b
= t
->buffer
;
729 Row
*lines
= t
->relposmode
? b
->scroll_top
: b
->lines
;
734 } else if (pcount
== 1) {
735 b
->curs_row
= lines
+ param
[0] - 1;
738 b
->curs_row
= lines
+ param
[0] - 1;
739 b
->curs_col
= param
[1] - 1;
742 clamp_cursor_to_bounds(t
);
745 /* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL,
746 * CPL, CHA, HPR, VPA, VPR, HPA */
747 static void interpret_csi_c(Vt
*t
, char verb
, int param
[], int pcount
)
749 Buffer
*b
= t
->buffer
;
750 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
777 b
->curs_col
= param
[0] - 1;
780 b
->curs_row
= b
->lines
+ param
[0] - 1;
784 clamp_cursor_to_bounds(t
);
787 /* Interpret the 'erase line' escape sequence */
788 static void interpret_csi_el(Vt
*t
, int param
[], int pcount
)
790 Buffer
*b
= t
->buffer
;
791 switch (pcount
? param
[0] : 0) {
793 row_set(b
->curs_row
, 0, b
->curs_col
+ 1, b
);
796 row_set(b
->curs_row
, 0, b
->cols
, b
);
799 row_set(b
->curs_row
, b
->curs_col
, b
->cols
- b
->curs_col
, b
);
804 /* Interpret the 'insert blanks' sequence (ICH) */
805 static void interpret_csi_ich(Vt
*t
, int param
[], int pcount
)
807 Buffer
*b
= t
->buffer
;
808 Row
*row
= b
->curs_row
;
809 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
811 if (b
->curs_col
+ n
> b
->cols
)
812 n
= b
->cols
- b
->curs_col
;
814 for (int i
= b
->cols
- 1; i
>= b
->curs_col
+ n
; i
--)
815 row
->cells
[i
] = row
->cells
[i
- n
];
817 row_set(row
, b
->curs_col
, n
, b
);
820 /* Interpret the 'delete chars' sequence (DCH) */
821 static void interpret_csi_dch(Vt
*t
, int param
[], int pcount
)
823 Buffer
*b
= t
->buffer
;
824 Row
*row
= b
->curs_row
;
825 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
827 if (b
->curs_col
+ n
> b
->cols
)
828 n
= b
->cols
- b
->curs_col
;
830 for (int i
= b
->curs_col
; i
< b
->cols
- n
; i
++)
831 row
->cells
[i
] = row
->cells
[i
+ n
];
833 row_set(row
, b
->cols
- n
, n
, b
);
836 /* Interpret an 'insert line' sequence (IL) */
837 static void interpret_csi_il(Vt
*t
, int param
[], int pcount
)
839 Buffer
*b
= t
->buffer
;
840 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
842 if (b
->curs_row
+ n
>= b
->scroll_bot
) {
843 for (Row
*row
= b
->curs_row
; row
< b
->scroll_bot
; row
++)
844 row_set(row
, 0, b
->cols
, b
);
846 row_roll(b
->curs_row
, b
->scroll_bot
, -n
);
847 for (Row
*row
= b
->curs_row
; row
< b
->curs_row
+ n
; row
++)
848 row_set(row
, 0, b
->cols
, b
);
852 /* Interpret a 'delete line' sequence (DL) */
853 static void interpret_csi_dl(Vt
*t
, int param
[], int pcount
)
855 Buffer
*b
= t
->buffer
;
856 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
858 if (b
->curs_row
+ n
>= b
->scroll_bot
) {
859 for (Row
*row
= b
->curs_row
; row
< b
->scroll_bot
; row
++)
860 row_set(row
, 0, b
->cols
, b
);
862 row_roll(b
->curs_row
, b
->scroll_bot
, n
);
863 for (Row
*row
= b
->scroll_bot
- n
; row
< b
->scroll_bot
; row
++)
864 row_set(row
, 0, b
->cols
, b
);
868 /* Interpret an 'erase characters' (ECH) sequence */
869 static void interpret_csi_ech(Vt
*t
, int param
[], int pcount
)
871 Buffer
*b
= t
->buffer
;
872 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
874 if (b
->curs_col
+ n
> b
->cols
)
875 n
= b
->cols
- b
->curs_col
;
877 row_set(b
->curs_row
, b
->curs_col
, n
, b
);
880 /* Interpret a 'set scrolling region' (DECSTBM) sequence */
881 static void interpret_csi_decstbm(Vt
*t
, int param
[], int pcount
)
883 Buffer
*b
= t
->buffer
;
884 int new_top
, new_bot
;
888 b
->scroll_top
= b
->lines
;
889 b
->scroll_bot
= b
->lines
+ b
->rows
;
892 new_top
= param
[0] - 1;
895 /* clamp to bounds */
898 if (new_top
>= b
->rows
)
899 new_top
= b
->rows
- 1;
902 if (new_bot
>= b
->rows
)
905 /* check for range validity */
906 if (new_top
< new_bot
) {
907 b
->scroll_top
= b
->lines
+ new_top
;
908 b
->scroll_bot
= b
->lines
+ new_bot
;
912 return; /* malformed */
914 b
->curs_row
= b
->scroll_top
;
918 static void interpret_csi_mode(Vt
*t
, int param
[], int pcount
, bool set
)
920 for (int i
= 0; i
< pcount
; i
++) {
922 case 4: /* insert/replace mode */
929 static void interpret_csi_priv_mode(Vt
*t
, int param
[], int pcount
, bool set
)
931 for (int i
= 0; i
< pcount
; i
++) {
933 case 1: /* set application/normal cursor key mode (DECCKM) */
934 t
->curskeymode
= set
;
936 case 6: /* set origin to relative/absolute (DECOM) */
939 case 25: /* make cursor visible/invisible (DECCM) */
942 case 47: /* use alternate/normal screen buffer */
943 t
->buffer
= set
? &t
->buffer_alternate
: &t
->buffer_normal
;
946 case 1000: /* enable/disable normal mouse tracking */
953 static void interpret_csi(Vt
*t
)
955 static int csiparam
[BUFSIZ
];
956 Buffer
*b
= t
->buffer
;
958 const char *p
= t
->ebuf
+ 1;
959 char verb
= t
->ebuf
[t
->elen
- 1];
961 /* parse numeric parameters */
962 for (p
+= (t
->ebuf
[1] == '?'); *p
; p
++) {
963 if (IS_CONTROL(*p
)) {
964 process_nonprinting(t
, *p
);
965 } else if (*p
== ';') {
966 if (param_count
>= (int)sizeof(csiparam
))
967 return; /* too long! */
968 csiparam
[param_count
++] = 0;
969 } else if (isdigit((unsigned char)*p
)) {
970 if (param_count
== 0)
971 csiparam
[param_count
++] = 0;
972 csiparam
[param_count
- 1] *= 10;
973 csiparam
[param_count
- 1] += *p
- '0';
977 if (t
->ebuf
[1] == '?') {
980 case 'l': /* private set/reset mode */
981 interpret_csi_priv_mode(t
, csiparam
, param_count
, verb
== 'h');
987 /* delegate handling depending on command character (verb) */
990 case 'l': /* set/reset mode */
991 interpret_csi_mode(t
, csiparam
, param_count
, verb
== 'h');
993 case 'm': /* set attribute */
994 interpret_csi_sgr(t
, csiparam
, param_count
);
996 case 'J': /* erase display */
997 interpret_csi_ed(t
, csiparam
, param_count
);
1000 case 'f': /* move cursor */
1001 interpret_csi_cup(t
, csiparam
, param_count
);
1013 case '`': /* relative move */
1014 interpret_csi_c(t
, verb
, csiparam
, param_count
);
1016 case 'K': /* erase line */
1017 interpret_csi_el(t
, csiparam
, param_count
);
1019 case '@': /* insert characters */
1020 interpret_csi_ich(t
, csiparam
, param_count
);
1022 case 'P': /* delete characters */
1023 interpret_csi_dch(t
, csiparam
, param_count
);
1025 case 'L': /* insert lines */
1026 interpret_csi_il(t
, csiparam
, param_count
);
1028 case 'M': /* delete lines */
1029 interpret_csi_dl(t
, csiparam
, param_count
);
1031 case 'X': /* erase chars */
1032 interpret_csi_ech(t
, csiparam
, param_count
);
1034 case 'S': /* SU: scroll up */
1035 vt_scroll(t
, param_count
? -csiparam
[0] : -1);
1037 case 'T': /* SD: scroll down */
1038 vt_scroll(t
, param_count
? csiparam
[0] : 1);
1040 case 'Z': /* CBT: cursor backward tabulation */
1041 puttab(t
, param_count
? -csiparam
[0] : -1);
1043 case 'g': /* TBC: tabulation clear */
1044 switch (csiparam
[0]) {
1046 b
->tabs
[b
->curs_col
] = false;
1049 memset(b
->tabs
, 0, sizeof(*b
->tabs
) * b
->maxcols
);
1052 case 'r': /* set scrolling region */
1053 interpret_csi_decstbm(t
, csiparam
, param_count
);
1055 case 's': /* save cursor location */
1058 case 'u': /* restore cursor location */
1061 case 'n': /* query cursor location */
1062 if (param_count
== 1 && csiparam
[0] == 6)
1070 /* Interpret an 'index' (IND) sequence */
1071 static void interpret_csi_ind(Vt
*t
)
1073 Buffer
*b
= t
->buffer
;
1074 if (b
->curs_row
< b
->lines
+ b
->rows
- 1)
1078 /* Interpret a 'reverse index' (RI) sequence */
1079 static void interpret_csi_ri(Vt
*t
)
1081 Buffer
*b
= t
->buffer
;
1082 if (b
->curs_row
> b
->scroll_top
)
1085 row_roll(b
->scroll_top
, b
->scroll_bot
, -1);
1086 row_set(b
->scroll_top
, 0, b
->cols
, b
);
1090 /* Interpret a 'next line' (NEL) sequence */
1091 static void interpret_csi_nel(Vt
*t
)
1093 Buffer
*b
= t
->buffer
;
1094 if (b
->curs_row
< b
->lines
+ b
->rows
- 1) {
1100 /* Interpret a 'select character set' (SCS) sequence */
1101 static void interpret_csi_scs(Vt
*t
)
1103 /* ESC ( sets G0, ESC ) sets G1 */
1104 t
->charsets
[!!(t
->ebuf
[0] == ')')] = (t
->ebuf
[1] == '0');
1105 t
->graphmode
= t
->charsets
[0];
1108 /* Interpret an 'operating system command' (OSC) sequence */
1109 static void interpret_osc(Vt
*t
)
1111 /* ESC ] command ; data BEL
1112 * ESC ] command ; data ESC \\
1113 * Note that BEL or ESC \\ have already been replaced with NUL.
1116 int command
= strtoul(t
->ebuf
+ 1, &data
, 10);
1117 if (data
&& *data
== ';') {
1119 case 0: /* icon name and window title */
1120 case 2: /* window title */
1121 if (t
->title_handler
)
1122 t
->title_handler(t
, data
+1);
1124 case 1: /* icon name */
1128 fprintf(stderr
, "unknown OSC command: %d\n", command
);
1135 static void try_interpret_escape_seq(Vt
*t
)
1137 char lastchar
= t
->ebuf
[t
->elen
- 1];
1143 case '#': /* ignore DECDHL, DECSWL, DECDWL, DECHCP, DECFPP */
1145 if (lastchar
== '8') { /* DECALN */
1146 interpret_csi_ed(t
, (int []){ 2 }, 1);
1155 interpret_csi_scs(t
);
1159 case ']': /* OSC - operating system command */
1160 if (lastchar
== '\a' ||
1161 (lastchar
== '\\' && t
->elen
>= 2 && t
->ebuf
[t
->elen
- 2] == '\e')) {
1162 t
->elen
-= lastchar
== '\a' ? 1 : 2;
1163 t
->ebuf
[t
->elen
] = '\0';
1168 case '[': /* CSI - control sequence introducer */
1169 if (is_valid_csi_ender(lastchar
)) {
1174 case '7': /* DECSC: save cursor and attributes */
1178 case '8': /* DECRC: restore cursor and attributes */
1182 case 'D': /* IND: index */
1183 interpret_csi_ind(t
);
1185 case 'M': /* RI: reverse index */
1186 interpret_csi_ri(t
);
1188 case 'E': /* NEL: next line */
1189 interpret_csi_nel(t
);
1191 case 'H': /* HTS: horizontal tab set */
1192 t
->buffer
->tabs
[t
->buffer
->curs_col
] = true;
1198 if (t
->elen
+ 1 >= sizeof(t
->ebuf
)) {
1201 fprintf(stderr
, "cancelled: \\033");
1202 for (unsigned int i
= 0; i
< t
->elen
; i
++) {
1203 if (isprint(t
->ebuf
[i
])) {
1204 fputc(t
->ebuf
[i
], stderr
);
1206 fprintf(stderr
, "\\%03o", t
->ebuf
[i
]);
1209 fputc('\n', stderr
);
1212 cancel_escape_sequence(t
);
1216 static void puttab(Vt
*t
, int count
)
1218 Buffer
*b
= t
->buffer
;
1219 int direction
= count
>= 0 ? 1 : -1;
1220 int col
= b
->curs_col
+ direction
;
1226 if (col
>= b
->cols
) {
1227 b
->curs_col
= b
->cols
- 1;
1238 static void process_nonprinting(Vt
*t
, wchar_t wc
)
1240 Buffer
*b
= t
->buffer
;
1242 case '\e': /* ESC */
1243 new_escape_sequence(t
);
1245 case '\a': /* BEL */
1250 if (b
->curs_col
> 0)
1262 cursor_line_down(t
);
1264 case '\016': /* SO: shift out, invoke the G1 character set */
1265 t
->graphmode
= t
->charsets
[1];
1267 case '\017': /* SI: shift in, invoke the G0 character set */
1268 t
->graphmode
= t
->charsets
[0];
1273 static void is_utf8_locale(void)
1275 const char *cset
= nl_langinfo(CODESET
);
1277 cset
= "ANSI_X3.4-1968";
1278 is_utf8
= !strcmp(cset
, "UTF-8");
1281 static wchar_t get_vt100_graphic(char c
)
1283 static char vt100_acs
[] = "`afgjklmnopqrstuvwxyz{|}~";
1286 * 5f-7e standard vt100
1287 * 40-5e rxvt extension for extra curses acs chars
1289 static uint16_t const vt100_utf8
[62] = {
1290 0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259a, 0x2603, // 41-47
1291 0, 0, 0, 0, 0, 0, 0, 0, // 48-4f
1292 0, 0, 0, 0, 0, 0, 0, 0, // 50-57
1293 0, 0, 0, 0, 0, 0, 0, 0x0020, // 58-5f
1294 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, // 60-67
1295 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, // 68-6f
1296 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, // 70-77
1297 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e
1301 return vt100_utf8
[c
- 0x41];
1302 else if (strchr(vt100_acs
, c
))
1303 return NCURSES_ACS(c
);
1307 static void put_wc(Vt
*t
, wchar_t wc
)
1311 if (!t
->seen_input
) {
1313 kill(-t
->pid
, SIGWINCH
);
1317 if (t
->elen
+ 1 < sizeof(t
->ebuf
)) {
1318 t
->ebuf
[t
->elen
] = wc
;
1319 t
->ebuf
[++t
->elen
] = '\0';
1320 try_interpret_escape_seq(t
);
1322 cancel_escape_sequence(t
);
1324 } else if (IS_CONTROL(wc
)) {
1325 process_nonprinting(t
, wc
);
1328 if (wc
>= 0x41 && wc
<= 0x7e) {
1329 wchar_t gc
= get_vt100_graphic(wc
);
1334 } else if ((width
= wcwidth(wc
)) < 1) {
1337 Buffer
*b
= t
->buffer
;
1338 Cell blank_cell
= { L
'\0', build_attrs(b
->curattrs
), b
->curfg
, b
->curbg
};
1339 if (width
== 2 && b
->curs_col
== b
->cols
- 1) {
1340 b
->curs_row
->cells
[b
->curs_col
++] = blank_cell
;
1341 b
->curs_row
->dirty
= true;
1344 if (b
->curs_col
>= b
->cols
) {
1346 cursor_line_down(t
);
1350 Cell
*src
= b
->curs_row
->cells
+ b
->curs_col
;
1351 Cell
*dest
= src
+ width
;
1352 size_t len
= b
->cols
- b
->curs_col
- width
;
1353 memmove(dest
, src
, len
* sizeof *dest
);
1356 b
->curs_row
->cells
[b
->curs_col
] = blank_cell
;
1357 b
->curs_row
->cells
[b
->curs_col
++].text
= wc
;
1358 b
->curs_row
->dirty
= true;
1360 b
->curs_row
->cells
[b
->curs_col
++] = blank_cell
;
1364 int vt_process(Vt
*t
)
1367 unsigned int pos
= 0;
1369 memset(&ps
, 0, sizeof(ps
));
1376 res
= read(t
->pty
, t
->rbuf
+ t
->rlen
, sizeof(t
->rbuf
) - t
->rlen
);
1381 while (pos
< t
->rlen
) {
1385 len
= (ssize_t
)mbrtowc(&wc
, t
->rbuf
+ pos
, t
->rlen
- pos
, &ps
);
1388 memmove(t
->rbuf
, t
->rbuf
+ pos
, t
->rlen
);
1397 pos
+= len
? len
: 1;
1402 memmove(t
->rbuf
, t
->rbuf
+ pos
, t
->rlen
);
1406 void vt_default_colors_set(Vt
*t
, attr_t attrs
, short fg
, short bg
)
1408 t
->defattrs
= attrs
;
1414 Vt
*vt_create(int rows
, int cols
, int scroll_buf_size
)
1418 if (rows
<= 0 || cols
<= 0)
1421 t
= calloc(1, sizeof(Vt
));
1426 t
->deffg
= t
->defbg
= -1;
1427 if (!buffer_init(&t
->buffer_normal
, rows
, cols
, scroll_buf_size
) ||
1428 !buffer_init(&t
->buffer_alternate
, rows
, cols
, 0)) {
1432 t
->buffer
= &t
->buffer_normal
;
1436 void vt_resize(Vt
*t
, int rows
, int cols
)
1438 struct winsize ws
= { .ws_row
= rows
, .ws_col
= cols
};
1440 if (rows
<= 0 || cols
<= 0)
1444 buffer_resize(&t
->buffer_normal
, rows
, cols
);
1445 buffer_resize(&t
->buffer_alternate
, rows
, cols
);
1446 clamp_cursor_to_bounds(t
);
1447 ioctl(t
->pty
, TIOCSWINSZ
, &ws
);
1448 kill(-t
->pid
, SIGWINCH
);
1451 void vt_destroy(Vt
*t
)
1455 buffer_free(&t
->buffer_normal
);
1456 buffer_free(&t
->buffer_alternate
);
1461 void vt_dirty(Vt
*t
)
1463 Buffer
*b
= t
->buffer
;
1464 for (Row
*row
= b
->lines
, *end
= row
+ b
->rows
; row
< end
; row
++)
1468 void vt_draw(Vt
*t
, WINDOW
* win
, int srow
, int scol
)
1470 Buffer
*b
= t
->buffer
;
1472 if (srow
!= t
->srow
|| scol
!= t
->scol
) {
1478 for (int i
= 0; i
< b
->rows
; i
++) {
1479 Row
*row
= b
->lines
+ i
;
1484 wmove(win
, srow
+ i
, scol
);
1486 for (int j
= 0; j
< b
->cols
; j
++) {
1487 Cell
*prev_cell
= cell
;
1488 cell
= row
->cells
+ j
;
1489 if (!prev_cell
|| cell
->attr
!= prev_cell
->attr
1490 || cell
->fg
!= prev_cell
->fg
1491 || cell
->bg
!= prev_cell
->bg
) {
1492 if (cell
->attr
== A_NORMAL
)
1493 cell
->attr
= t
->defattrs
;
1495 cell
->fg
= t
->deffg
;
1497 cell
->bg
= t
->defbg
;
1498 wattrset(win
, cell
->attr
<< NCURSES_ATTR_SHIFT
);
1499 wcolor_set(win
, vt_color_get(t
, cell
->fg
, cell
->bg
), NULL
);
1502 if (is_utf8
&& cell
->text
>= 128) {
1503 char buf
[MB_CUR_MAX
+ 1];
1504 size_t len
= wcrtomb(buf
, cell
->text
, NULL
);
1506 waddnstr(win
, buf
, len
);
1507 if (wcwidth(cell
->text
) > 1)
1511 waddch(win
, cell
->text
> ' ' ? cell
->text
: ' ');
1518 if (x
&& x
< b
->cols
- 1)
1519 whline(win
, ' ', b
->cols
- x
);
1524 wmove(win
, srow
+ b
->curs_row
- b
->lines
, scol
+ b
->curs_col
);
1527 void vt_scroll(Vt
*t
, int rows
)
1529 Buffer
*b
= t
->buffer
;
1530 if (!b
->scroll_buf_size
)
1532 if (rows
< 0) { /* scroll back */
1533 if (rows
< -b
->scroll_above
)
1534 rows
= -b
->scroll_above
;
1535 } else { /* scroll forward */
1536 if (rows
> b
->scroll_below
)
1537 rows
= b
->scroll_below
;
1539 fill_scroll_buf(b
, rows
);
1540 b
->scroll_below
-= rows
;
1543 void vt_noscroll(Vt
*t
)
1545 int scroll_below
= t
->buffer
->scroll_below
;
1547 vt_scroll(t
, scroll_below
);
1550 void vt_bell(Vt
*t
, bool bell
)
1555 void vt_togglebell(Vt
*t
)
1560 pid_t
vt_forkpty(Vt
*t
, const char *p
, const char *argv
[], const char *cwd
, const char *env
[], int *to
, int *from
)
1562 int vt2ed
[2], ed2vt
[2];
1564 ws
.ws_row
= t
->buffer
->rows
;
1565 ws
.ws_col
= t
->buffer
->cols
;
1566 ws
.ws_xpixel
= ws
.ws_ypixel
= 0;
1568 if (to
&& pipe(vt2ed
)) {
1572 if (from
&& pipe(ed2vt
)) {
1577 pid_t pid
= forkpty(&t
->pty
, NULL
, NULL
, &ws
);
1585 sigemptyset(&emptyset
);
1586 sigprocmask(SIG_SETMASK
, &emptyset
, NULL
);
1590 dup2(vt2ed
[0], STDIN_FILENO
);
1596 dup2(ed2vt
[1], STDOUT_FILENO
);
1600 int maxfd
= sysconf(_SC_OPEN_MAX
);
1601 for (int fd
= 3; fd
< maxfd
; fd
++)
1602 if (close(fd
) == -1 && errno
== EBADF
)
1605 for (const char **envp
= env
; envp
&& envp
[0]; envp
+= 2)
1606 setenv(envp
[0], envp
[1], 1);
1607 setenv("TERM", vt_term
, 1);
1612 execvp(p
, (char *const *)argv
);
1613 fprintf(stderr
, "\nexecv() failed.\nCommand: '%s'\n", argv
[0]);
1627 return t
->pid
= pid
;
1630 int vt_pty_get(Vt
*t
)
1635 int vt_write(Vt
*t
, const char *buf
, int len
)
1640 int res
= write(t
->pty
, buf
, len
);
1642 if (errno
!= EAGAIN
&& errno
!= EINTR
)
1654 static void send_curs(Vt
*t
)
1656 Buffer
*b
= t
->buffer
;
1658 snprintf(keyseq
, sizeof keyseq
, "\e[%d;%dR", (int)(b
->curs_row
- b
->lines
), b
->curs_col
);
1659 vt_write(t
, keyseq
, strlen(keyseq
));
1662 void vt_keypress(Vt
*t
, int keycode
)
1666 if (keycode
>= 0 && keycode
<= KEY_MAX
&& keytable
[keycode
]) {
1672 char keyseq
[3] = { '\e', (t
->curskeymode
? 'O' : '['), keytable
[keycode
][0] };
1673 vt_write(t
, keyseq
, sizeof keyseq
);
1677 vt_write(t
, keytable
[keycode
], strlen(keytable
[keycode
]));
1679 } else if (keycode
<= UCHAR_MAX
) {
1684 fprintf(stderr
, "unhandled key %#o\n", keycode
);
1689 void vt_mouse(Vt
*t
, int x
, int y
, mmask_t mask
)
1691 #ifdef NCURSES_MOUSE_VERSION
1692 char seq
[6] = { '\e', '[', 'M' }, state
= 0, button
= 0;
1697 if (mask
& (BUTTON1_PRESSED
| BUTTON1_CLICKED
))
1699 else if (mask
& (BUTTON2_PRESSED
| BUTTON2_CLICKED
))
1701 else if (mask
& (BUTTON3_PRESSED
| BUTTON3_CLICKED
))
1703 else if (mask
& (BUTTON1_RELEASED
| BUTTON2_RELEASED
| BUTTON3_RELEASED
))
1706 if (mask
& BUTTON_SHIFT
)
1708 if (mask
& BUTTON_ALT
)
1710 if (mask
& BUTTON_CTRL
)
1713 seq
[3] = 32 + button
+ state
;
1717 vt_write(t
, seq
, sizeof seq
);
1719 if (mask
& (BUTTON1_CLICKED
| BUTTON2_CLICKED
| BUTTON3_CLICKED
)) {
1720 /* send a button release event */
1722 seq
[3] = 32 + button
+ state
;
1723 vt_write(t
, seq
, sizeof seq
);
1725 #endif /* NCURSES_MOUSE_VERSION */
1728 static unsigned int color_hash(short fg
, short bg
)
1734 return fg
* (COLORS
+ 2) + bg
;
1737 short vt_color_get(Vt
*t
, short fg
, short bg
)
1740 fg
= (t
? t
->deffg
: default_fg
);
1742 bg
= (t
? t
->defbg
: default_bg
);
1744 if (!has_default_colors
) {
1746 fg
= (t
&& t
->deffg
!= -1 ? t
->deffg
: default_fg
);
1748 bg
= (t
&& t
->defbg
!= -1 ? t
->defbg
: default_bg
);
1751 if (!color2palette
|| (fg
== -1 && bg
== -1))
1753 unsigned int index
= color_hash(fg
, bg
);
1754 if (color2palette
[index
] == 0) {
1757 if (++color_pair_current
>= color_pairs_max
)
1758 color_pair_current
= color_pairs_reserved
+ 1;
1759 pair_content(color_pair_current
, &oldfg
, &oldbg
);
1760 unsigned int old_index
= color_hash(oldfg
, oldbg
);
1761 if (color2palette
[old_index
] >= 0) {
1762 if (init_pair(color_pair_current
, fg
, bg
) == OK
) {
1763 color2palette
[old_index
] = 0;
1764 color2palette
[index
] = color_pair_current
;
1771 short color_pair
= color2palette
[index
];
1772 return color_pair
>= 0 ? color_pair
: -color_pair
;
1775 short vt_color_reserve(short fg
, short bg
)
1777 if (!color2palette
|| fg
>= COLORS
|| bg
>= COLORS
)
1779 if (!has_default_colors
&& fg
== -1)
1781 if (!has_default_colors
&& bg
== -1)
1783 if (fg
== -1 && bg
== -1)
1785 unsigned int index
= color_hash(fg
, bg
);
1786 if (color2palette
[index
] >= 0) {
1787 if (init_pair(color_pairs_reserved
+ 1, fg
, bg
) == OK
)
1788 color2palette
[index
] = -(++color_pairs_reserved
);
1790 short color_pair
= color2palette
[index
];
1791 return color_pair
>= 0 ? color_pair
: -color_pair
;
1794 static void init_colors(void)
1796 pair_content(0, &default_fg
, &default_bg
);
1797 if (default_fg
== -1)
1798 default_fg
= COLOR_WHITE
;
1799 if (default_bg
== -1)
1800 default_bg
= COLOR_BLACK
;
1801 has_default_colors
= (use_default_colors() == OK
);
1802 color_pairs_max
= MIN(COLOR_PAIRS
, MAX_COLOR_PAIRS
);
1804 color2palette
= calloc((COLORS
+ 2) * (COLORS
+ 2), sizeof(short));
1805 vt_color_reserve(COLOR_WHITE
, COLOR_BLACK
);
1812 char color_suffix
[] = "-256color";
1813 char *term
= getenv("DVTM_TERM");
1815 strncpy(vt_term
, term
, sizeof(vt_term
) - sizeof(color_suffix
));
1817 strncat(vt_term
, color_suffix
, sizeof(color_suffix
) - 1);
1820 void vt_keytable_set(const char * const keytable_overlay
[], int count
)
1822 for (int k
= 0; k
< count
&& k
< KEY_MAX
; k
++) {
1823 const char *keyseq
= keytable_overlay
[k
];
1825 keytable
[k
] = keyseq
;
1829 void vt_shutdown(void)
1831 free(color2palette
);
1834 void vt_title_handler_set(Vt
*t
, vt_title_handler_t handler
)
1836 t
->title_handler
= handler
;
1839 void vt_data_set(Vt
*t
, void *data
)
1844 void *vt_data_get(Vt
*t
)
1849 bool vt_cursor_visible(Vt
*t
)
1851 return t
->buffer
->scroll_below
? false : !t
->curshid
;
1854 pid_t
vt_pid_get(Vt
*t
)
1859 size_t vt_content_get(Vt
*t
, char **buf
) {
1860 Buffer
*b
= t
->buffer
;
1861 int lines
= b
->scroll_above
+ b
->scroll_below
+ b
->rows
+ 1;
1862 size_t size
= lines
* (b
->cols
* MB_CUR_MAX
+ 1);
1864 memset(&ps
, 0, sizeof(ps
));
1866 if (!(*buf
= malloc(size
)))
1871 for (Row
*row
= buffer_row_first(b
); row
; row
= buffer_row_next(b
, row
)) {
1873 char *last_non_space
= s
;
1874 for (int col
= 0; col
< b
->cols
; col
++) {
1875 if (row
->cells
[col
].text
) {
1876 len
= wcrtomb(s
, row
->cells
[col
].text
, &ps
);
1894 int vt_content_start(Vt
*t
) {
1895 return t
->buffer
->scroll_above
;