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";
103 /* Buffer holding the current terminal window content (as an array) as well
104 * as the scroll back buffer content (as a circular/ring buffer).
106 * If new content is added to terminal the view port slides down and the
107 * previously top most line is moved into the scroll back buffer at postion
108 * scroll_index. This index will eventually wrap around and thus overwrite
111 * In the scenerio below a scroll up has been performed. That is 'scroll_above'
112 * lines still lie above the current view port. Further scrolling up will show
113 * them. Similarly 'scroll_below' is the amount of lines below the current
116 * The function buffer_boundary sets the row pointers to the start/end range
117 * of the section delimiting the region before/after the viewport. The functions
118 * buffer_row_{first,last} return the first/last logical row. And
119 * buffer_row_{next,prev} allows to iterate over the logical lines in either
124 * scroll_buf->+----------------+-----+
127 * current terminal content | viewport | | | |
129 * +----------------+-----+\ | | | s > scroll_above
130 * ^ | | i | \ | | i | c |
131 * | | | n | \ | | n | r |
132 * | | v | \ | | v | o |
133 * r | | i | \ | | i | l /
134 * o | viewport | s | >|<- scroll_index | s | l \
135 * w | | i | / | | i | |
136 * s | | b | / | after | b | s > scroll_below
137 * | | l | / | viewport | l | i |
138 * v | | e | / | | e | z /
139 * +----------------+-----+/ | unused | | e
140 * <- maxcols -> | scroll back | |
141 * <- cols -> | buffer | | |
144 * roll_buf + scroll_size->+----------------+-----+
149 Row
*lines
; /* array of Row pointers of size 'rows' */
150 Row
*curs_row
; /* row on which the cursor currently resides */
151 Row
*scroll_buf
; /* a ring buffer holding the scroll back content */
152 Row
*scroll_top
; /* row in lines where scrolling region starts */
153 Row
*scroll_bot
; /* row in lines where scrolling region ends */
154 bool *tabs
; /* a boolean flag for each column whether it is a tab */
155 int scroll_size
; /* maximal capacity of scroll back buffer (in lines) */
156 int scroll_index
; /* current index into the ring buffer */
157 int scroll_above
; /* number of lines above current viewport */
158 int scroll_below
; /* number of lines below current viewport */
159 int rows
, cols
; /* current dimension of buffer */
160 int maxcols
; /* allocated cells (maximal cols over time) */
161 attr_t curattrs
, savattrs
; /* current and saved attributes for cells */
162 int curs_col
; /* current cursor column (zero based) */
163 int curs_srow
, curs_scol
; /* saved cursor row/colmn (zero based) */
164 short curfg
, curbg
; /* current fore and background colors */
165 short savfg
, savbg
; /* saved colors */
169 Buffer buffer_normal
; /* normal screen buffer */
170 Buffer buffer_alternate
; /* alternate screen buffer */
171 Buffer
*buffer
; /* currently active buffer (one of the above) */
172 attr_t defattrs
; /* attributes to use for normal/empty cells */
173 short deffg
, defbg
; /* colors to use for back normal/empty cells (white/black) */
174 int pty
; /* master side pty file descriptor */
175 pid_t pid
; /* process id of the process running in this vt */
177 unsigned seen_input
:1;
181 unsigned curskeymode
:1;
183 unsigned relposmode
:1;
184 unsigned mousetrack
:1;
185 unsigned graphmode
:1;
186 unsigned savgraphmode
:1;
188 /* buffers and parsing state */
191 unsigned int rlen
, elen
;
192 int srow
, scol
; /* last known offset to display start row, start column */
193 char title
[256]; /* xterm style window title */
194 vt_title_handler_t title_handler
; /* hook which is called when title changes */
195 void *data
; /* user supplied data */
198 static const char *keytable
[KEY_MAX
+1] = {
200 /* for the arrow keys the CSI / SS3 sequences are not stored here
201 * because they depend on the current cursor terminal mode
208 [KEY_SUP
] = "\e[1;2A",
211 [KEY_SDOWN
] = "\e[1;2B",
213 [KEY_SRIGHT
] = "\e[1;2C",
214 [KEY_SLEFT
] = "\e[1;2D",
215 [KEY_BACKSPACE
] = "\177",
218 [KEY_PPAGE
] = "\e[5~",
219 [KEY_NPAGE
] = "\e[6~",
220 [KEY_HOME
] = "\e[7~",
223 [KEY_SUSPEND
] = "\x1A", /* Ctrl+Z gets mapped to this */
224 [KEY_F(1)] = "\e[11~",
225 [KEY_F(2)] = "\e[12~",
226 [KEY_F(3)] = "\e[13~",
227 [KEY_F(4)] = "\e[14~",
228 [KEY_F(5)] = "\e[15~",
229 [KEY_F(6)] = "\e[17~",
230 [KEY_F(7)] = "\e[18~",
231 [KEY_F(8)] = "\e[19~",
232 [KEY_F(9)] = "\e[20~",
233 [KEY_F(10)] = "\e[21~",
234 [KEY_F(11)] = "\e[23~",
235 [KEY_F(12)] = "\e[24~",
236 [KEY_F(13)] = "\e[23~",
237 [KEY_F(14)] = "\e[24~",
238 [KEY_F(15)] = "\e[25~",
239 [KEY_F(16)] = "\e[26~",
240 [KEY_F(17)] = "\e[28~",
241 [KEY_F(18)] = "\e[29~",
242 [KEY_F(19)] = "\e[31~",
243 [KEY_F(20)] = "\e[32~",
244 [KEY_F(21)] = "\e[33~",
245 [KEY_F(22)] = "\e[34~",
252 static void puttab(Vt
*t
, int count
);
253 static void process_nonprinting(Vt
*t
, wchar_t wc
);
254 static void send_curs(Vt
*t
);
256 __attribute__ ((const))
257 static attr_t
build_attrs(attr_t curattrs
)
259 return ((curattrs
& ~A_COLOR
) | COLOR_PAIR(curattrs
& 0xff))
260 >> NCURSES_ATTR_SHIFT
;
263 static void row_set(Row
*row
, int start
, int len
, Buffer
*t
)
267 .attr
= t
? build_attrs(t
->curattrs
) : 0,
268 .fg
= t
? t
->curfg
: -1,
269 .bg
= t
? t
->curbg
: -1,
272 for (int i
= start
; i
< len
+ start
; i
++)
273 row
->cells
[i
] = cell
;
277 static void row_roll(Row
*start
, Row
*end
, int count
)
286 char buf
[count
* sizeof(Row
)];
287 memcpy(buf
, start
, count
* sizeof(Row
));
288 memmove(start
, start
+ count
, (n
- count
) * sizeof(Row
));
289 memcpy(end
- count
, buf
, count
* sizeof(Row
));
290 for (Row
*row
= start
; row
< end
; row
++)
295 static void buffer_free(Buffer
*b
)
297 for (int i
= 0; i
< b
->rows
; i
++)
298 free(b
->lines
[i
].cells
);
300 for (int i
= 0; i
< b
->scroll_size
; i
++)
301 free(b
->scroll_buf
[i
].cells
);
306 static void buffer_scroll(Buffer
*b
, int s
)
308 /* work in screenfuls */
309 int ssz
= b
->scroll_bot
- b
->scroll_top
;
311 buffer_scroll(b
, ssz
);
312 buffer_scroll(b
, s
- ssz
);
316 buffer_scroll(b
, -ssz
);
317 buffer_scroll(b
, s
+ ssz
);
321 b
->scroll_above
+= s
;
322 if (b
->scroll_above
>= b
->scroll_size
)
323 b
->scroll_above
= b
->scroll_size
;
325 if (s
> 0 && b
->scroll_size
) {
326 for (int i
= 0; i
< s
; i
++) {
327 Row tmp
= b
->scroll_top
[i
];
328 b
->scroll_top
[i
] = b
->scroll_buf
[b
->scroll_index
];
329 b
->scroll_buf
[b
->scroll_index
] = tmp
;
332 if (b
->scroll_index
== b
->scroll_size
)
336 row_roll(b
->scroll_top
, b
->scroll_bot
, s
);
337 if (s
< 0 && b
->scroll_size
) {
338 for (int i
= (-s
) - 1; i
>= 0; i
--) {
340 if (b
->scroll_index
== -1)
341 b
->scroll_index
= b
->scroll_size
- 1;
343 Row tmp
= b
->scroll_top
[i
];
344 b
->scroll_top
[i
] = b
->scroll_buf
[b
->scroll_index
];
345 b
->scroll_buf
[b
->scroll_index
] = tmp
;
346 b
->scroll_top
[i
].dirty
= true;
351 static void buffer_resize(Buffer
*b
, int rows
, int cols
)
353 Row
*lines
= b
->lines
;
355 if (b
->rows
!= rows
) {
356 if (b
->curs_row
>= lines
+ rows
) {
357 /* scroll up instead of simply chopping off bottom */
358 buffer_scroll(b
, (b
->curs_row
- b
->lines
) - rows
+ 1);
360 while (b
->rows
> rows
) {
361 free(lines
[b
->rows
- 1].cells
);
365 lines
= realloc(lines
, sizeof(Row
) * rows
);
368 if (b
->maxcols
< cols
) {
369 for (int row
= 0; row
< b
->rows
; row
++) {
370 lines
[row
].cells
= realloc(lines
[row
].cells
, sizeof(Cell
) * cols
);
372 row_set(lines
+ row
, b
->cols
, cols
- b
->cols
, NULL
);
373 lines
[row
].dirty
= true;
375 Row
*sbuf
= b
->scroll_buf
;
376 for (int row
= 0; row
< b
->scroll_size
; row
++) {
377 sbuf
[row
].cells
= realloc(sbuf
[row
].cells
, sizeof(Cell
) * cols
);
379 row_set(sbuf
+ row
, b
->cols
, cols
- b
->cols
, NULL
);
381 b
->tabs
= realloc(b
->tabs
, sizeof(*b
->tabs
) * cols
);
382 for (int col
= b
->cols
; col
< cols
; col
++)
383 b
->tabs
[col
] = !(col
& 7);
386 } else if (b
->cols
!= cols
) {
387 for (int row
= 0; row
< b
->rows
; row
++)
388 lines
[row
].dirty
= true;
393 if (b
->rows
< rows
) {
394 while (b
->rows
< rows
) {
395 lines
[b
->rows
].cells
= calloc(b
->maxcols
, sizeof(Cell
));
396 row_set(lines
+ b
->rows
, 0, b
->maxcols
, b
);
400 /* prepare for backfill */
401 if (b
->curs_row
>= b
->scroll_bot
- 1) {
402 deltarows
= b
->lines
+ rows
- b
->curs_row
- 1;
403 if (deltarows
> b
->scroll_above
)
404 deltarows
= b
->scroll_above
;
408 b
->curs_row
+= lines
- b
->lines
;
409 b
->scroll_top
= lines
;
410 b
->scroll_bot
= lines
+ rows
;
413 /* perform backfill */
415 buffer_scroll(b
, -deltarows
);
416 b
->curs_row
+= deltarows
;
420 static bool buffer_init(Buffer
*b
, int rows
, int cols
, int scroll_size
)
422 b
->curattrs
= A_NORMAL
; /* white text over black background */
423 b
->curfg
= b
->curbg
= -1;
426 if (scroll_size
&& !(b
->scroll_buf
= calloc(scroll_size
, sizeof(Row
))))
428 b
->scroll_size
= scroll_size
;
429 buffer_resize(b
, rows
, cols
);
433 static void buffer_boundry(Buffer
*b
, Row
**bs
, Row
**be
, Row
**as
, Row
**ae
) {
445 if (b
->scroll_above
) {
447 *bs
= &b
->scroll_buf
[(b
->scroll_index
- b
->scroll_above
+ b
->scroll_size
) % b
->scroll_size
];
449 *be
= &b
->scroll_buf
[(b
->scroll_index
-1 + b
->scroll_size
) % b
->scroll_size
];
451 if (b
->scroll_below
) {
453 *as
= &b
->scroll_buf
[b
->scroll_index
];
455 *ae
= &b
->scroll_buf
[(b
->scroll_index
+ b
->scroll_below
-1) % b
->scroll_size
];
459 static Row
*buffer_row_first(Buffer
*b
) {
461 if (!b
->scroll_size
|| !b
->scroll_above
)
463 buffer_boundry(b
, &bstart
, NULL
, NULL
, NULL
);
467 static Row
*buffer_row_last(Buffer
*b
) {
469 if (!b
->scroll_size
|| !b
->scroll_below
)
470 return b
->lines
+ b
->rows
- 1;
471 buffer_boundry(b
, NULL
, NULL
, NULL
, &aend
);
475 static Row
*buffer_row_next(Buffer
*b
, Row
*row
)
477 Row
*before_start
, *before_end
, *after_start
, *after_end
;
478 Row
*first
= b
->lines
, *last
= b
->lines
+ b
->rows
- 1;
483 buffer_boundry(b
, &before_start
, &before_end
, &after_start
, &after_end
);
485 if (row
>= first
&& row
< last
)
489 if (row
== before_end
)
491 if (row
== after_end
)
493 if (row
== &b
->scroll_buf
[b
->scroll_size
- 1])
494 return b
->scroll_buf
;
498 static Row
*buffer_row_prev(Buffer
*b
, Row
*row
)
500 Row
*before_start
, *before_end
, *after_start
, *after_end
;
501 Row
*first
= b
->lines
, *last
= b
->lines
+ b
->rows
- 1;
506 buffer_boundry(b
, &before_start
, &before_end
, &after_start
, &after_end
);
508 if (row
> first
&& row
<= last
)
512 if (row
== before_start
)
514 if (row
== after_start
)
516 if (row
== b
->scroll_buf
)
517 return &b
->scroll_buf
[b
->scroll_size
- 1];
521 static void cursor_clamp(Vt
*t
)
523 Buffer
*b
= t
->buffer
;
524 Row
*lines
= t
->relposmode
? b
->scroll_top
: b
->lines
;
525 int rows
= t
->relposmode
? b
->scroll_bot
- b
->scroll_top
: b
->rows
;
527 if (b
->curs_row
< lines
)
529 if (b
->curs_row
>= lines
+ rows
)
530 b
->curs_row
= lines
+ rows
- 1;
533 if (b
->curs_col
>= b
->cols
)
534 b
->curs_col
= b
->cols
- 1;
537 static void cursor_line_down(Vt
*t
)
539 Buffer
*b
= t
->buffer
;
540 row_set(b
->curs_row
, b
->cols
, b
->maxcols
- b
->cols
, NULL
);
542 if (b
->curs_row
< b
->scroll_bot
)
547 b
->curs_row
= b
->scroll_bot
- 1;
549 row_set(b
->curs_row
, 0, b
->cols
, b
);
552 static void cursor_save(Vt
*t
)
554 Buffer
*b
= t
->buffer
;
555 b
->curs_srow
= b
->curs_row
- b
->lines
;
556 b
->curs_scol
= b
->curs_col
;
559 static void cursor_restore(Vt
*t
)
561 Buffer
*b
= t
->buffer
;
562 b
->curs_row
= b
->lines
+ b
->curs_srow
;
563 b
->curs_col
= b
->curs_scol
;
567 static void attributes_save(Vt
*t
)
569 Buffer
*b
= t
->buffer
;
570 b
->savattrs
= b
->curattrs
;
573 t
->savgraphmode
= t
->graphmode
;
576 static void attributes_restore(Vt
*t
)
578 Buffer
*b
= t
->buffer
;
579 b
->curattrs
= b
->savattrs
;
582 t
->graphmode
= t
->savgraphmode
;
585 static void new_escape_sequence(Vt
*t
)
592 static void cancel_escape_sequence(Vt
*t
)
599 static bool is_valid_csi_ender(int c
)
601 return (c
>= 'a' && c
<= 'z')
602 || (c
>= 'A' && c
<= 'Z')
603 || (c
== '@' || c
== '`');
606 /* interprets a 'set attribute' (SGR) CSI escape sequence */
607 static void interpret_csi_sgr(Vt
*t
, int param
[], int pcount
)
609 Buffer
*b
= t
->buffer
;
611 /* special case: reset attributes */
612 b
->curattrs
= A_NORMAL
;
613 b
->curfg
= b
->curbg
= -1;
617 for (int i
= 0; i
< pcount
; i
++) {
620 b
->curattrs
= A_NORMAL
;
621 b
->curfg
= b
->curbg
= -1;
624 b
->curattrs
|= A_BOLD
;
627 b
->curattrs
|= A_DIM
;
631 b
->curattrs
|= A_ITALIC
;
635 b
->curattrs
|= A_UNDERLINE
;
638 b
->curattrs
|= A_BLINK
;
641 b
->curattrs
|= A_REVERSE
;
644 b
->curattrs
|= A_INVIS
;
647 b
->curattrs
&= ~(A_BOLD
| A_DIM
);
651 b
->curattrs
&= ~A_ITALIC
;
655 b
->curattrs
&= ~A_UNDERLINE
;
658 b
->curattrs
&= ~A_BLINK
;
661 b
->curattrs
&= ~A_REVERSE
;
664 b
->curattrs
&= ~A_INVIS
;
666 case 30 ... 37: /* fg */
667 b
->curfg
= param
[i
] - 30;
670 if ((i
+ 2) < pcount
&& param
[i
+ 1] == 5) {
671 b
->curfg
= param
[i
+ 2];
678 case 40 ... 47: /* bg */
679 b
->curbg
= param
[i
] - 40;
682 if ((i
+ 2) < pcount
&& param
[i
+ 1] == 5) {
683 b
->curbg
= param
[i
+ 2];
690 case 90 ... 97: /* hi fg */
691 b
->curfg
= param
[i
] - 82;
693 case 100 ... 107: /* hi bg */
694 b
->curbg
= param
[i
] - 92;
702 /* interprets an 'erase display' (ED) escape sequence */
703 static void interpret_csi_ed(Vt
*t
, int param
[], int pcount
)
705 Row
*row
, *start
, *end
;
706 Buffer
*b
= t
->buffer
;
709 b
->curattrs
= A_NORMAL
;
710 b
->curfg
= b
->curbg
= -1;
712 if (pcount
&& param
[0] == 2) {
714 end
= b
->lines
+ b
->rows
;
715 } else if (pcount
&& param
[0] == 1) {
718 row_set(b
->curs_row
, 0, b
->curs_col
+ 1, b
);
720 row_set(b
->curs_row
, b
->curs_col
, b
->cols
- b
->curs_col
, b
);
721 start
= b
->curs_row
+ 1;
722 end
= b
->lines
+ b
->rows
;
725 for (row
= start
; row
< end
; row
++)
726 row_set(row
, 0, b
->cols
, b
);
728 attributes_restore(t
);
731 /* interprets a 'move cursor' (CUP) escape sequence */
732 static void interpret_csi_cup(Vt
*t
, int param
[], int pcount
)
734 Buffer
*b
= t
->buffer
;
735 Row
*lines
= t
->relposmode
? b
->scroll_top
: b
->lines
;
740 } else if (pcount
== 1) {
741 b
->curs_row
= lines
+ param
[0] - 1;
744 b
->curs_row
= lines
+ param
[0] - 1;
745 b
->curs_col
= param
[1] - 1;
751 /* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL,
752 * CPL, CHA, HPR, VPA, VPR, HPA */
753 static void interpret_csi_c(Vt
*t
, char verb
, int param
[], int pcount
)
755 Buffer
*b
= t
->buffer
;
756 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
783 b
->curs_col
= param
[0] - 1;
786 b
->curs_row
= b
->lines
+ param
[0] - 1;
793 /* Interpret the 'erase line' escape sequence */
794 static void interpret_csi_el(Vt
*t
, int param
[], int pcount
)
796 Buffer
*b
= t
->buffer
;
797 switch (pcount
? param
[0] : 0) {
799 row_set(b
->curs_row
, 0, b
->curs_col
+ 1, b
);
802 row_set(b
->curs_row
, 0, b
->cols
, b
);
805 row_set(b
->curs_row
, b
->curs_col
, b
->cols
- b
->curs_col
, b
);
810 /* Interpret the 'insert blanks' sequence (ICH) */
811 static void interpret_csi_ich(Vt
*t
, int param
[], int pcount
)
813 Buffer
*b
= t
->buffer
;
814 Row
*row
= b
->curs_row
;
815 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
817 if (b
->curs_col
+ n
> b
->cols
)
818 n
= b
->cols
- b
->curs_col
;
820 for (int i
= b
->cols
- 1; i
>= b
->curs_col
+ n
; i
--)
821 row
->cells
[i
] = row
->cells
[i
- n
];
823 row_set(row
, b
->curs_col
, n
, b
);
826 /* Interpret the 'delete chars' sequence (DCH) */
827 static void interpret_csi_dch(Vt
*t
, int param
[], int pcount
)
829 Buffer
*b
= t
->buffer
;
830 Row
*row
= b
->curs_row
;
831 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
833 if (b
->curs_col
+ n
> b
->cols
)
834 n
= b
->cols
- b
->curs_col
;
836 for (int i
= b
->curs_col
; i
< b
->cols
- n
; i
++)
837 row
->cells
[i
] = row
->cells
[i
+ n
];
839 row_set(row
, b
->cols
- n
, n
, b
);
842 /* Interpret an 'insert line' sequence (IL) */
843 static void interpret_csi_il(Vt
*t
, int param
[], int pcount
)
845 Buffer
*b
= t
->buffer
;
846 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
848 if (b
->curs_row
+ n
>= b
->scroll_bot
) {
849 for (Row
*row
= b
->curs_row
; row
< b
->scroll_bot
; row
++)
850 row_set(row
, 0, b
->cols
, b
);
852 row_roll(b
->curs_row
, b
->scroll_bot
, -n
);
853 for (Row
*row
= b
->curs_row
; row
< b
->curs_row
+ n
; row
++)
854 row_set(row
, 0, b
->cols
, b
);
858 /* Interpret a 'delete line' sequence (DL) */
859 static void interpret_csi_dl(Vt
*t
, int param
[], int pcount
)
861 Buffer
*b
= t
->buffer
;
862 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
864 if (b
->curs_row
+ n
>= b
->scroll_bot
) {
865 for (Row
*row
= b
->curs_row
; row
< b
->scroll_bot
; row
++)
866 row_set(row
, 0, b
->cols
, b
);
868 row_roll(b
->curs_row
, b
->scroll_bot
, n
);
869 for (Row
*row
= b
->scroll_bot
- n
; row
< b
->scroll_bot
; row
++)
870 row_set(row
, 0, b
->cols
, b
);
874 /* Interpret an 'erase characters' (ECH) sequence */
875 static void interpret_csi_ech(Vt
*t
, int param
[], int pcount
)
877 Buffer
*b
= t
->buffer
;
878 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
880 if (b
->curs_col
+ n
> b
->cols
)
881 n
= b
->cols
- b
->curs_col
;
883 row_set(b
->curs_row
, b
->curs_col
, n
, b
);
886 /* Interpret a 'set scrolling region' (DECSTBM) sequence */
887 static void interpret_csi_decstbm(Vt
*t
, int param
[], int pcount
)
889 Buffer
*b
= t
->buffer
;
890 int new_top
, new_bot
;
894 b
->scroll_top
= b
->lines
;
895 b
->scroll_bot
= b
->lines
+ b
->rows
;
898 new_top
= param
[0] - 1;
901 /* clamp to bounds */
904 if (new_top
>= b
->rows
)
905 new_top
= b
->rows
- 1;
908 if (new_bot
>= b
->rows
)
911 /* check for range validity */
912 if (new_top
< new_bot
) {
913 b
->scroll_top
= b
->lines
+ new_top
;
914 b
->scroll_bot
= b
->lines
+ new_bot
;
918 return; /* malformed */
920 b
->curs_row
= b
->scroll_top
;
924 static void interpret_csi_mode(Vt
*t
, int param
[], int pcount
, bool set
)
926 for (int i
= 0; i
< pcount
; i
++) {
928 case 4: /* insert/replace mode */
935 static void interpret_csi_priv_mode(Vt
*t
, int param
[], int pcount
, bool set
)
937 for (int i
= 0; i
< pcount
; i
++) {
939 case 1: /* set application/normal cursor key mode (DECCKM) */
940 t
->curskeymode
= set
;
942 case 6: /* set origin to relative/absolute (DECOM) */
945 case 25: /* make cursor visible/invisible (DECCM) */
948 case 47: /* use alternate/normal screen buffer */
949 t
->buffer
= set
? &t
->buffer_alternate
: &t
->buffer_normal
;
952 case 1000: /* enable/disable normal mouse tracking */
959 static void interpret_csi(Vt
*t
)
961 static int csiparam
[BUFSIZ
];
962 Buffer
*b
= t
->buffer
;
964 const char *p
= t
->ebuf
+ 1;
965 char verb
= t
->ebuf
[t
->elen
- 1];
967 /* parse numeric parameters */
968 for (p
+= (t
->ebuf
[1] == '?'); *p
; p
++) {
969 if (IS_CONTROL(*p
)) {
970 process_nonprinting(t
, *p
);
971 } else if (*p
== ';') {
972 if (param_count
>= (int)sizeof(csiparam
))
973 return; /* too long! */
974 csiparam
[param_count
++] = 0;
975 } else if (isdigit((unsigned char)*p
)) {
976 if (param_count
== 0)
977 csiparam
[param_count
++] = 0;
978 csiparam
[param_count
- 1] *= 10;
979 csiparam
[param_count
- 1] += *p
- '0';
983 if (t
->ebuf
[1] == '?') {
986 case 'l': /* private set/reset mode */
987 interpret_csi_priv_mode(t
, csiparam
, param_count
, verb
== 'h');
993 /* delegate handling depending on command character (verb) */
996 case 'l': /* set/reset mode */
997 interpret_csi_mode(t
, csiparam
, param_count
, verb
== 'h');
999 case 'm': /* set attribute */
1000 interpret_csi_sgr(t
, csiparam
, param_count
);
1002 case 'J': /* erase display */
1003 interpret_csi_ed(t
, csiparam
, param_count
);
1006 case 'f': /* move cursor */
1007 interpret_csi_cup(t
, csiparam
, param_count
);
1019 case '`': /* relative move */
1020 interpret_csi_c(t
, verb
, csiparam
, param_count
);
1022 case 'K': /* erase line */
1023 interpret_csi_el(t
, csiparam
, param_count
);
1025 case '@': /* insert characters */
1026 interpret_csi_ich(t
, csiparam
, param_count
);
1028 case 'P': /* delete characters */
1029 interpret_csi_dch(t
, csiparam
, param_count
);
1031 case 'L': /* insert lines */
1032 interpret_csi_il(t
, csiparam
, param_count
);
1034 case 'M': /* delete lines */
1035 interpret_csi_dl(t
, csiparam
, param_count
);
1037 case 'X': /* erase chars */
1038 interpret_csi_ech(t
, csiparam
, param_count
);
1040 case 'S': /* SU: scroll up */
1041 vt_scroll(t
, param_count
? -csiparam
[0] : -1);
1043 case 'T': /* SD: scroll down */
1044 vt_scroll(t
, param_count
? csiparam
[0] : 1);
1046 case 'Z': /* CBT: cursor backward tabulation */
1047 puttab(t
, param_count
? -csiparam
[0] : -1);
1049 case 'g': /* TBC: tabulation clear */
1050 switch (csiparam
[0]) {
1052 b
->tabs
[b
->curs_col
] = false;
1055 memset(b
->tabs
, 0, sizeof(*b
->tabs
) * b
->maxcols
);
1058 case 'r': /* set scrolling region */
1059 interpret_csi_decstbm(t
, csiparam
, param_count
);
1061 case 's': /* save cursor location */
1064 case 'u': /* restore cursor location */
1067 case 'n': /* query cursor location */
1068 if (param_count
== 1 && csiparam
[0] == 6)
1076 /* Interpret an 'index' (IND) sequence */
1077 static void interpret_csi_ind(Vt
*t
)
1079 Buffer
*b
= t
->buffer
;
1080 if (b
->curs_row
< b
->lines
+ b
->rows
- 1)
1084 /* Interpret a 'reverse index' (RI) sequence */
1085 static void interpret_csi_ri(Vt
*t
)
1087 Buffer
*b
= t
->buffer
;
1088 if (b
->curs_row
> b
->scroll_top
)
1091 row_roll(b
->scroll_top
, b
->scroll_bot
, -1);
1092 row_set(b
->scroll_top
, 0, b
->cols
, b
);
1096 /* Interpret a 'next line' (NEL) sequence */
1097 static void interpret_csi_nel(Vt
*t
)
1099 Buffer
*b
= t
->buffer
;
1100 if (b
->curs_row
< b
->lines
+ b
->rows
- 1) {
1106 /* Interpret a 'select character set' (SCS) sequence */
1107 static void interpret_csi_scs(Vt
*t
)
1109 /* ESC ( sets G0, ESC ) sets G1 */
1110 t
->charsets
[!!(t
->ebuf
[0] == ')')] = (t
->ebuf
[1] == '0');
1111 t
->graphmode
= t
->charsets
[0];
1114 /* Interpret an 'operating system command' (OSC) sequence */
1115 static void interpret_osc(Vt
*t
)
1117 /* ESC ] command ; data BEL
1118 * ESC ] command ; data ESC \\
1119 * Note that BEL or ESC \\ have already been replaced with NUL.
1122 int command
= strtoul(t
->ebuf
+ 1, &data
, 10);
1123 if (data
&& *data
== ';') {
1125 case 0: /* icon name and window title */
1126 case 2: /* window title */
1127 if (t
->title_handler
)
1128 t
->title_handler(t
, data
+1);
1130 case 1: /* icon name */
1134 fprintf(stderr
, "unknown OSC command: %d\n", command
);
1141 static void try_interpret_escape_seq(Vt
*t
)
1143 char lastchar
= t
->ebuf
[t
->elen
- 1];
1149 case '#': /* ignore DECDHL, DECSWL, DECDWL, DECHCP, DECFPP */
1151 if (lastchar
== '8') { /* DECALN */
1152 interpret_csi_ed(t
, (int []){ 2 }, 1);
1161 interpret_csi_scs(t
);
1165 case ']': /* OSC - operating system command */
1166 if (lastchar
== '\a' ||
1167 (lastchar
== '\\' && t
->elen
>= 2 && t
->ebuf
[t
->elen
- 2] == '\e')) {
1168 t
->elen
-= lastchar
== '\a' ? 1 : 2;
1169 t
->ebuf
[t
->elen
] = '\0';
1174 case '[': /* CSI - control sequence introducer */
1175 if (is_valid_csi_ender(lastchar
)) {
1180 case '7': /* DECSC: save cursor and attributes */
1184 case '8': /* DECRC: restore cursor and attributes */
1185 attributes_restore(t
);
1188 case 'D': /* IND: index */
1189 interpret_csi_ind(t
);
1191 case 'M': /* RI: reverse index */
1192 interpret_csi_ri(t
);
1194 case 'E': /* NEL: next line */
1195 interpret_csi_nel(t
);
1197 case 'H': /* HTS: horizontal tab set */
1198 t
->buffer
->tabs
[t
->buffer
->curs_col
] = true;
1204 if (t
->elen
+ 1 >= sizeof(t
->ebuf
)) {
1207 fprintf(stderr
, "cancelled: \\033");
1208 for (unsigned int i
= 0; i
< t
->elen
; i
++) {
1209 if (isprint(t
->ebuf
[i
])) {
1210 fputc(t
->ebuf
[i
], stderr
);
1212 fprintf(stderr
, "\\%03o", t
->ebuf
[i
]);
1215 fputc('\n', stderr
);
1218 cancel_escape_sequence(t
);
1222 static void puttab(Vt
*t
, int count
)
1224 Buffer
*b
= t
->buffer
;
1225 int direction
= count
>= 0 ? 1 : -1;
1226 for (int col
= b
->curs_col
+ direction
; count
; col
+= direction
) {
1231 if (col
>= b
->cols
) {
1232 b
->curs_col
= b
->cols
- 1;
1242 static void process_nonprinting(Vt
*t
, wchar_t wc
)
1244 Buffer
*b
= t
->buffer
;
1246 case '\e': /* ESC */
1247 new_escape_sequence(t
);
1249 case '\a': /* BEL */
1254 if (b
->curs_col
> 0)
1266 cursor_line_down(t
);
1268 case '\016': /* SO: shift out, invoke the G1 character set */
1269 t
->graphmode
= t
->charsets
[1];
1271 case '\017': /* SI: shift in, invoke the G0 character set */
1272 t
->graphmode
= t
->charsets
[0];
1277 static void is_utf8_locale(void)
1279 const char *cset
= nl_langinfo(CODESET
);
1281 cset
= "ANSI_X3.4-1968";
1282 is_utf8
= !strcmp(cset
, "UTF-8");
1285 static wchar_t get_vt100_graphic(char c
)
1287 static char vt100_acs
[] = "`afgjklmnopqrstuvwxyz{|}~";
1290 * 5f-7e standard vt100
1291 * 40-5e rxvt extension for extra curses acs chars
1293 static uint16_t const vt100_utf8
[62] = {
1294 0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259a, 0x2603, // 41-47
1295 0, 0, 0, 0, 0, 0, 0, 0, // 48-4f
1296 0, 0, 0, 0, 0, 0, 0, 0, // 50-57
1297 0, 0, 0, 0, 0, 0, 0, 0x0020, // 58-5f
1298 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, // 60-67
1299 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, // 68-6f
1300 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, // 70-77
1301 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e
1305 return vt100_utf8
[c
- 0x41];
1306 else if (strchr(vt100_acs
, c
))
1307 return NCURSES_ACS(c
);
1311 static void put_wc(Vt
*t
, wchar_t wc
)
1315 if (!t
->seen_input
) {
1317 kill(-t
->pid
, SIGWINCH
);
1321 if (t
->elen
+ 1 < sizeof(t
->ebuf
)) {
1322 t
->ebuf
[t
->elen
] = wc
;
1323 t
->ebuf
[++t
->elen
] = '\0';
1324 try_interpret_escape_seq(t
);
1326 cancel_escape_sequence(t
);
1328 } else if (IS_CONTROL(wc
)) {
1329 process_nonprinting(t
, wc
);
1332 if (wc
>= 0x41 && wc
<= 0x7e) {
1333 wchar_t gc
= get_vt100_graphic(wc
);
1338 } else if ((width
= wcwidth(wc
)) < 1) {
1341 Buffer
*b
= t
->buffer
;
1342 Cell blank_cell
= { L
'\0', build_attrs(b
->curattrs
), b
->curfg
, b
->curbg
};
1343 if (width
== 2 && b
->curs_col
== b
->cols
- 1) {
1344 b
->curs_row
->cells
[b
->curs_col
++] = blank_cell
;
1345 b
->curs_row
->dirty
= true;
1348 if (b
->curs_col
>= b
->cols
) {
1350 cursor_line_down(t
);
1354 Cell
*src
= b
->curs_row
->cells
+ b
->curs_col
;
1355 Cell
*dest
= src
+ width
;
1356 size_t len
= b
->cols
- b
->curs_col
- width
;
1357 memmove(dest
, src
, len
* sizeof *dest
);
1360 b
->curs_row
->cells
[b
->curs_col
] = blank_cell
;
1361 b
->curs_row
->cells
[b
->curs_col
++].text
= wc
;
1362 b
->curs_row
->dirty
= true;
1364 b
->curs_row
->cells
[b
->curs_col
++] = blank_cell
;
1368 int vt_process(Vt
*t
)
1371 unsigned int pos
= 0;
1373 memset(&ps
, 0, sizeof(ps
));
1380 res
= read(t
->pty
, t
->rbuf
+ t
->rlen
, sizeof(t
->rbuf
) - t
->rlen
);
1385 while (pos
< t
->rlen
) {
1389 len
= (ssize_t
)mbrtowc(&wc
, t
->rbuf
+ pos
, t
->rlen
- pos
, &ps
);
1392 memmove(t
->rbuf
, t
->rbuf
+ pos
, t
->rlen
);
1401 pos
+= len
? len
: 1;
1406 memmove(t
->rbuf
, t
->rbuf
+ pos
, t
->rlen
);
1410 void vt_default_colors_set(Vt
*t
, attr_t attrs
, short fg
, short bg
)
1412 t
->defattrs
= attrs
;
1417 Vt
*vt_create(int rows
, int cols
, int scroll_size
)
1421 if (rows
<= 0 || cols
<= 0)
1424 t
= calloc(1, sizeof(Vt
));
1429 t
->deffg
= t
->defbg
= -1;
1430 if (!buffer_init(&t
->buffer_normal
, rows
, cols
, scroll_size
) ||
1431 !buffer_init(&t
->buffer_alternate
, rows
, cols
, 0)) {
1435 t
->buffer
= &t
->buffer_normal
;
1439 void vt_resize(Vt
*t
, int rows
, int cols
)
1441 struct winsize ws
= { .ws_row
= rows
, .ws_col
= cols
};
1443 if (rows
<= 0 || cols
<= 0)
1447 buffer_resize(&t
->buffer_normal
, rows
, cols
);
1448 buffer_resize(&t
->buffer_alternate
, rows
, cols
);
1450 ioctl(t
->pty
, TIOCSWINSZ
, &ws
);
1451 kill(-t
->pid
, SIGWINCH
);
1454 void vt_destroy(Vt
*t
)
1458 buffer_free(&t
->buffer_normal
);
1459 buffer_free(&t
->buffer_alternate
);
1464 void vt_dirty(Vt
*t
)
1466 Buffer
*b
= t
->buffer
;
1467 for (Row
*row
= b
->lines
, *end
= row
+ b
->rows
; row
< end
; row
++)
1471 void vt_draw(Vt
*t
, WINDOW
* win
, int srow
, int scol
)
1473 Buffer
*b
= t
->buffer
;
1475 if (srow
!= t
->srow
|| scol
!= t
->scol
) {
1481 for (int i
= 0; i
< b
->rows
; i
++) {
1482 Row
*row
= b
->lines
+ i
;
1487 wmove(win
, srow
+ i
, scol
);
1489 for (int j
= 0; j
< b
->cols
; j
++) {
1490 Cell
*prev_cell
= cell
;
1491 cell
= row
->cells
+ j
;
1492 if (!prev_cell
|| cell
->attr
!= prev_cell
->attr
1493 || cell
->fg
!= prev_cell
->fg
1494 || cell
->bg
!= prev_cell
->bg
) {
1495 if (cell
->attr
== A_NORMAL
)
1496 cell
->attr
= t
->defattrs
;
1498 cell
->fg
= t
->deffg
;
1500 cell
->bg
= t
->defbg
;
1501 wattrset(win
, cell
->attr
<< NCURSES_ATTR_SHIFT
);
1502 wcolor_set(win
, vt_color_get(t
, cell
->fg
, cell
->bg
), NULL
);
1505 if (is_utf8
&& cell
->text
>= 128) {
1506 char buf
[MB_CUR_MAX
+ 1];
1507 size_t len
= wcrtomb(buf
, cell
->text
, NULL
);
1509 waddnstr(win
, buf
, len
);
1510 if (wcwidth(cell
->text
) > 1)
1514 waddch(win
, cell
->text
> ' ' ? cell
->text
: ' ');
1521 if (x
&& x
< b
->cols
- 1)
1522 whline(win
, ' ', b
->cols
- x
);
1527 wmove(win
, srow
+ b
->curs_row
- b
->lines
, scol
+ b
->curs_col
);
1530 void vt_scroll(Vt
*t
, int rows
)
1532 Buffer
*b
= t
->buffer
;
1533 if (!b
->scroll_size
)
1535 if (rows
< 0) { /* scroll back */
1536 if (rows
< -b
->scroll_above
)
1537 rows
= -b
->scroll_above
;
1538 } else { /* scroll forward */
1539 if (rows
> b
->scroll_below
)
1540 rows
= b
->scroll_below
;
1542 buffer_scroll(b
, rows
);
1543 b
->scroll_below
-= rows
;
1546 void vt_noscroll(Vt
*t
)
1548 int scroll_below
= t
->buffer
->scroll_below
;
1550 vt_scroll(t
, scroll_below
);
1553 void vt_bell(Vt
*t
, bool bell
)
1558 void vt_togglebell(Vt
*t
)
1563 pid_t
vt_forkpty(Vt
*t
, const char *p
, const char *argv
[], const char *cwd
, const char *env
[], int *to
, int *from
)
1565 int vt2ed
[2], ed2vt
[2];
1567 ws
.ws_row
= t
->buffer
->rows
;
1568 ws
.ws_col
= t
->buffer
->cols
;
1569 ws
.ws_xpixel
= ws
.ws_ypixel
= 0;
1571 if (to
&& pipe(vt2ed
)) {
1575 if (from
&& pipe(ed2vt
)) {
1580 pid_t pid
= forkpty(&t
->pty
, NULL
, NULL
, &ws
);
1588 sigemptyset(&emptyset
);
1589 sigprocmask(SIG_SETMASK
, &emptyset
, NULL
);
1593 dup2(vt2ed
[0], STDIN_FILENO
);
1599 dup2(ed2vt
[1], STDOUT_FILENO
);
1603 int maxfd
= sysconf(_SC_OPEN_MAX
);
1604 for (int fd
= 3; fd
< maxfd
; fd
++)
1605 if (close(fd
) == -1 && errno
== EBADF
)
1608 for (const char **envp
= env
; envp
&& envp
[0]; envp
+= 2)
1609 setenv(envp
[0], envp
[1], 1);
1610 setenv("TERM", vt_term
, 1);
1615 execvp(p
, (char *const *)argv
);
1616 fprintf(stderr
, "\nexecv() failed.\nCommand: '%s'\n", argv
[0]);
1630 return t
->pid
= pid
;
1633 int vt_pty_get(Vt
*t
)
1638 ssize_t
vt_write(Vt
*t
, const char *buf
, size_t len
)
1643 ssize_t res
= write(t
->pty
, buf
, len
);
1645 if (errno
!= EAGAIN
&& errno
!= EINTR
)
1656 static void send_curs(Vt
*t
)
1658 Buffer
*b
= t
->buffer
;
1660 snprintf(keyseq
, sizeof keyseq
, "\e[%d;%dR", (int)(b
->curs_row
- b
->lines
), b
->curs_col
);
1661 vt_write(t
, keyseq
, strlen(keyseq
));
1664 void vt_keypress(Vt
*t
, int keycode
)
1668 if (keycode
>= 0 && keycode
<= KEY_MAX
&& keytable
[keycode
]) {
1674 char keyseq
[3] = { '\e', (t
->curskeymode
? 'O' : '['), keytable
[keycode
][0] };
1675 vt_write(t
, keyseq
, sizeof keyseq
);
1679 vt_write(t
, keytable
[keycode
], strlen(keytable
[keycode
]));
1681 } else if (keycode
<= UCHAR_MAX
) {
1686 fprintf(stderr
, "unhandled key %#o\n", keycode
);
1691 void vt_mouse(Vt
*t
, int x
, int y
, mmask_t mask
)
1693 #ifdef NCURSES_MOUSE_VERSION
1694 char seq
[6] = { '\e', '[', 'M' }, state
= 0, button
= 0;
1699 if (mask
& (BUTTON1_PRESSED
| BUTTON1_CLICKED
))
1701 else if (mask
& (BUTTON2_PRESSED
| BUTTON2_CLICKED
))
1703 else if (mask
& (BUTTON3_PRESSED
| BUTTON3_CLICKED
))
1705 else if (mask
& (BUTTON1_RELEASED
| BUTTON2_RELEASED
| BUTTON3_RELEASED
))
1708 if (mask
& BUTTON_SHIFT
)
1710 if (mask
& BUTTON_ALT
)
1712 if (mask
& BUTTON_CTRL
)
1715 seq
[3] = 32 + button
+ state
;
1719 vt_write(t
, seq
, sizeof seq
);
1721 if (mask
& (BUTTON1_CLICKED
| BUTTON2_CLICKED
| BUTTON3_CLICKED
)) {
1722 /* send a button release event */
1724 seq
[3] = 32 + button
+ state
;
1725 vt_write(t
, seq
, sizeof seq
);
1727 #endif /* NCURSES_MOUSE_VERSION */
1730 static unsigned int color_hash(short fg
, short bg
)
1736 return fg
* (COLORS
+ 2) + bg
;
1739 short vt_color_get(Vt
*t
, short fg
, short bg
)
1742 fg
= (t
? t
->deffg
: default_fg
);
1744 bg
= (t
? t
->defbg
: default_bg
);
1746 if (!has_default_colors
) {
1748 fg
= (t
&& t
->deffg
!= -1 ? t
->deffg
: default_fg
);
1750 bg
= (t
&& t
->defbg
!= -1 ? t
->defbg
: default_bg
);
1753 if (!color2palette
|| (fg
== -1 && bg
== -1))
1755 unsigned int index
= color_hash(fg
, bg
);
1756 if (color2palette
[index
] == 0) {
1759 if (++color_pair_current
>= color_pairs_max
)
1760 color_pair_current
= color_pairs_reserved
+ 1;
1761 pair_content(color_pair_current
, &oldfg
, &oldbg
);
1762 unsigned int old_index
= color_hash(oldfg
, oldbg
);
1763 if (color2palette
[old_index
] >= 0) {
1764 if (init_pair(color_pair_current
, fg
, bg
) == OK
) {
1765 color2palette
[old_index
] = 0;
1766 color2palette
[index
] = color_pair_current
;
1773 short color_pair
= color2palette
[index
];
1774 return color_pair
>= 0 ? color_pair
: -color_pair
;
1777 short vt_color_reserve(short fg
, short bg
)
1779 if (!color2palette
|| fg
>= COLORS
|| bg
>= COLORS
)
1781 if (!has_default_colors
&& fg
== -1)
1783 if (!has_default_colors
&& bg
== -1)
1785 if (fg
== -1 && bg
== -1)
1787 unsigned int index
= color_hash(fg
, bg
);
1788 if (color2palette
[index
] >= 0) {
1789 if (init_pair(color_pairs_reserved
+ 1, fg
, bg
) == OK
)
1790 color2palette
[index
] = -(++color_pairs_reserved
);
1792 short color_pair
= color2palette
[index
];
1793 return color_pair
>= 0 ? color_pair
: -color_pair
;
1796 static void init_colors(void)
1798 pair_content(0, &default_fg
, &default_bg
);
1799 if (default_fg
== -1)
1800 default_fg
= COLOR_WHITE
;
1801 if (default_bg
== -1)
1802 default_bg
= COLOR_BLACK
;
1803 has_default_colors
= (use_default_colors() == OK
);
1804 color_pairs_max
= MIN(COLOR_PAIRS
, MAX_COLOR_PAIRS
);
1806 color2palette
= calloc((COLORS
+ 2) * (COLORS
+ 2), sizeof(short));
1807 vt_color_reserve(COLOR_WHITE
, COLOR_BLACK
);
1814 char color_suffix
[] = "-256color";
1815 char *term
= getenv("DVTM_TERM");
1817 strncpy(vt_term
, term
, sizeof(vt_term
) - sizeof(color_suffix
));
1819 strncat(vt_term
, color_suffix
, sizeof(color_suffix
) - 1);
1822 void vt_keytable_set(const char * const keytable_overlay
[], int count
)
1824 for (int k
= 0; k
< count
&& k
< KEY_MAX
; k
++) {
1825 const char *keyseq
= keytable_overlay
[k
];
1827 keytable
[k
] = keyseq
;
1831 void vt_shutdown(void)
1833 free(color2palette
);
1836 void vt_title_handler_set(Vt
*t
, vt_title_handler_t handler
)
1838 t
->title_handler
= handler
;
1841 void vt_data_set(Vt
*t
, void *data
)
1846 void *vt_data_get(Vt
*t
)
1851 bool vt_cursor_visible(Vt
*t
)
1853 return t
->buffer
->scroll_below
? false : !t
->curshid
;
1856 pid_t
vt_pid_get(Vt
*t
)
1861 size_t vt_content_get(Vt
*t
, char **buf
) {
1862 Buffer
*b
= t
->buffer
;
1863 int lines
= b
->scroll_above
+ b
->scroll_below
+ b
->rows
+ 1;
1864 size_t size
= lines
* (b
->cols
* MB_CUR_MAX
+ 1);
1866 memset(&ps
, 0, sizeof(ps
));
1868 if (!(*buf
= malloc(size
)))
1873 for (Row
*row
= buffer_row_first(b
); row
; row
= buffer_row_next(b
, row
)) {
1875 char *last_non_space
= s
;
1876 for (int col
= 0; col
< b
->cols
; col
++) {
1877 if (row
->cells
[col
].text
) {
1878 len
= wcrtomb(s
, row
->cells
[col
].text
, &ps
);
1896 int vt_content_start(Vt
*t
) {
1897 return t
->buffer
->scroll_above
;