2 * Copyright © 2004 Bruno T. C. de Oliveira
3 * Copyright © 2006 Pierre Habouzit
4 * Copyright © 2008-2016 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__) || defined(__DragonFly__)
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 MIN(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 LENGTH(arr) (sizeof(arr) / sizeof((arr)[0]))
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];
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 vt_urgent_handler_t urgent_handler
; /* hook which is called upon bell */
196 void *data
; /* user supplied data */
199 static const char *keytable
[KEY_MAX
+1] = {
202 /* for the arrow keys the CSI / SS3 sequences are not stored here
203 * because they depend on the current cursor terminal mode
210 [KEY_SUP
] = "\e[1;2A",
213 [KEY_SDOWN
] = "\e[1;2B",
215 [KEY_SRIGHT
] = "\e[1;2C",
216 [KEY_SLEFT
] = "\e[1;2D",
217 [KEY_BACKSPACE
] = "\177",
220 [KEY_PPAGE
] = "\e[5~",
221 [KEY_NPAGE
] = "\e[6~",
222 [KEY_HOME
] = "\e[7~",
225 [KEY_SUSPEND
] = "\x1A", /* Ctrl+Z gets mapped to this */
226 [KEY_F(1)] = "\e[11~",
227 [KEY_F(2)] = "\e[12~",
228 [KEY_F(3)] = "\e[13~",
229 [KEY_F(4)] = "\e[14~",
230 [KEY_F(5)] = "\e[15~",
231 [KEY_F(6)] = "\e[17~",
232 [KEY_F(7)] = "\e[18~",
233 [KEY_F(8)] = "\e[19~",
234 [KEY_F(9)] = "\e[20~",
235 [KEY_F(10)] = "\e[21~",
236 [KEY_F(11)] = "\e[23~",
237 [KEY_F(12)] = "\e[24~",
238 [KEY_F(13)] = "\e[23~",
239 [KEY_F(14)] = "\e[24~",
240 [KEY_F(15)] = "\e[25~",
241 [KEY_F(16)] = "\e[26~",
242 [KEY_F(17)] = "\e[28~",
243 [KEY_F(18)] = "\e[29~",
244 [KEY_F(19)] = "\e[31~",
245 [KEY_F(20)] = "\e[32~",
246 [KEY_F(21)] = "\e[33~",
247 [KEY_F(22)] = "\e[34~",
254 static void puttab(Vt
*t
, int count
);
255 static void process_nonprinting(Vt
*t
, wchar_t wc
);
256 static void send_curs(Vt
*t
);
258 __attribute__ ((const))
259 static attr_t
build_attrs(attr_t curattrs
)
261 return ((curattrs
& ~A_COLOR
) | COLOR_PAIR(curattrs
& 0xff))
262 >> NCURSES_ATTR_SHIFT
;
265 static void row_set(Row
*row
, int start
, int len
, Buffer
*t
)
269 .attr
= t
? build_attrs(t
->curattrs
) : 0,
270 .fg
= t
? t
->curfg
: -1,
271 .bg
= t
? t
->curbg
: -1,
274 for (int i
= start
; i
< len
+ start
; i
++)
275 row
->cells
[i
] = cell
;
279 static void row_roll(Row
*start
, Row
*end
, int count
)
288 char buf
[count
* sizeof(Row
)];
289 memcpy(buf
, start
, count
* sizeof(Row
));
290 memmove(start
, start
+ count
, (n
- count
) * sizeof(Row
));
291 memcpy(end
- count
, buf
, count
* sizeof(Row
));
292 for (Row
*row
= start
; row
< end
; row
++)
297 static void buffer_clear(Buffer
*b
)
306 for (int i
= 0; i
< b
->rows
; i
++) {
307 Row
*row
= b
->lines
+ i
;
308 for (int j
= 0; j
< b
->cols
; j
++) {
309 row
->cells
[j
] = cell
;
315 static void buffer_free(Buffer
*b
)
317 for (int i
= 0; i
< b
->rows
; i
++)
318 free(b
->lines
[i
].cells
);
320 for (int i
= 0; i
< b
->scroll_size
; i
++)
321 free(b
->scroll_buf
[i
].cells
);
326 static void buffer_scroll(Buffer
*b
, int s
)
328 /* work in screenfuls */
329 int ssz
= b
->scroll_bot
- b
->scroll_top
;
331 buffer_scroll(b
, ssz
);
332 buffer_scroll(b
, s
- ssz
);
336 buffer_scroll(b
, -ssz
);
337 buffer_scroll(b
, s
+ ssz
);
341 b
->scroll_above
+= s
;
342 if (b
->scroll_above
>= b
->scroll_size
)
343 b
->scroll_above
= b
->scroll_size
;
345 if (s
> 0 && b
->scroll_size
) {
346 for (int i
= 0; i
< s
; i
++) {
347 Row tmp
= b
->scroll_top
[i
];
348 b
->scroll_top
[i
] = b
->scroll_buf
[b
->scroll_index
];
349 b
->scroll_buf
[b
->scroll_index
] = tmp
;
352 if (b
->scroll_index
== b
->scroll_size
)
356 row_roll(b
->scroll_top
, b
->scroll_bot
, s
);
357 if (s
< 0 && b
->scroll_size
) {
358 for (int i
= (-s
) - 1; i
>= 0; i
--) {
360 if (b
->scroll_index
== -1)
361 b
->scroll_index
= b
->scroll_size
- 1;
363 Row tmp
= b
->scroll_top
[i
];
364 b
->scroll_top
[i
] = b
->scroll_buf
[b
->scroll_index
];
365 b
->scroll_buf
[b
->scroll_index
] = tmp
;
366 b
->scroll_top
[i
].dirty
= true;
371 static void buffer_resize(Buffer
*b
, int rows
, int cols
)
373 Row
*lines
= b
->lines
;
375 if (b
->rows
!= rows
) {
376 if (b
->curs_row
>= lines
+ rows
) {
377 /* scroll up instead of simply chopping off bottom */
378 buffer_scroll(b
, (b
->curs_row
- b
->lines
) - rows
+ 1);
380 while (b
->rows
> rows
) {
381 free(lines
[b
->rows
- 1].cells
);
385 lines
= realloc(lines
, sizeof(Row
) * rows
);
388 if (b
->maxcols
< cols
) {
389 for (int row
= 0; row
< b
->rows
; row
++) {
390 lines
[row
].cells
= realloc(lines
[row
].cells
, sizeof(Cell
) * cols
);
392 row_set(lines
+ row
, b
->cols
, cols
- b
->cols
, NULL
);
393 lines
[row
].dirty
= true;
395 Row
*sbuf
= b
->scroll_buf
;
396 for (int row
= 0; row
< b
->scroll_size
; row
++) {
397 sbuf
[row
].cells
= realloc(sbuf
[row
].cells
, sizeof(Cell
) * cols
);
399 row_set(sbuf
+ row
, b
->cols
, cols
- b
->cols
, NULL
);
401 b
->tabs
= realloc(b
->tabs
, sizeof(*b
->tabs
) * cols
);
402 for (int col
= b
->cols
; col
< cols
; col
++)
403 b
->tabs
[col
] = !(col
& 7);
406 } else if (b
->cols
!= cols
) {
407 for (int row
= 0; row
< b
->rows
; row
++)
408 lines
[row
].dirty
= true;
413 if (b
->rows
< rows
) {
414 while (b
->rows
< rows
) {
415 lines
[b
->rows
].cells
= calloc(b
->maxcols
, sizeof(Cell
));
416 row_set(lines
+ b
->rows
, 0, b
->maxcols
, b
);
420 /* prepare for backfill */
421 if (b
->curs_row
>= b
->scroll_bot
- 1) {
422 deltarows
= b
->lines
+ rows
- b
->curs_row
- 1;
423 if (deltarows
> b
->scroll_above
)
424 deltarows
= b
->scroll_above
;
428 b
->curs_row
+= lines
- b
->lines
;
429 b
->scroll_top
= lines
;
430 b
->scroll_bot
= lines
+ rows
;
433 /* perform backfill */
435 buffer_scroll(b
, -deltarows
);
436 b
->curs_row
+= deltarows
;
440 static bool buffer_init(Buffer
*b
, int rows
, int cols
, int scroll_size
)
442 b
->curattrs
= A_NORMAL
; /* white text over black background */
443 b
->curfg
= b
->curbg
= -1;
446 if (scroll_size
&& !(b
->scroll_buf
= calloc(scroll_size
, sizeof(Row
))))
448 b
->scroll_size
= scroll_size
;
449 buffer_resize(b
, rows
, cols
);
453 static void buffer_boundry(Buffer
*b
, Row
**bs
, Row
**be
, Row
**as
, Row
**ae
) {
465 if (b
->scroll_above
) {
467 *bs
= &b
->scroll_buf
[(b
->scroll_index
- b
->scroll_above
+ b
->scroll_size
) % b
->scroll_size
];
469 *be
= &b
->scroll_buf
[(b
->scroll_index
-1 + b
->scroll_size
) % b
->scroll_size
];
471 if (b
->scroll_below
) {
473 *as
= &b
->scroll_buf
[b
->scroll_index
];
475 *ae
= &b
->scroll_buf
[(b
->scroll_index
+ b
->scroll_below
-1) % b
->scroll_size
];
479 static Row
*buffer_row_first(Buffer
*b
) {
481 if (!b
->scroll_size
|| !b
->scroll_above
)
483 buffer_boundry(b
, &bstart
, NULL
, NULL
, NULL
);
487 static Row
*buffer_row_last(Buffer
*b
) {
489 if (!b
->scroll_size
|| !b
->scroll_below
)
490 return b
->lines
+ b
->rows
- 1;
491 buffer_boundry(b
, NULL
, NULL
, NULL
, &aend
);
495 static Row
*buffer_row_next(Buffer
*b
, Row
*row
)
497 Row
*before_start
, *before_end
, *after_start
, *after_end
;
498 Row
*first
= b
->lines
, *last
= b
->lines
+ b
->rows
- 1;
503 buffer_boundry(b
, &before_start
, &before_end
, &after_start
, &after_end
);
505 if (row
>= first
&& row
< last
)
509 if (row
== before_end
)
511 if (row
== after_end
)
513 if (row
== &b
->scroll_buf
[b
->scroll_size
- 1])
514 return b
->scroll_buf
;
518 static Row
*buffer_row_prev(Buffer
*b
, Row
*row
)
520 Row
*before_start
, *before_end
, *after_start
, *after_end
;
521 Row
*first
= b
->lines
, *last
= b
->lines
+ b
->rows
- 1;
526 buffer_boundry(b
, &before_start
, &before_end
, &after_start
, &after_end
);
528 if (row
> first
&& row
<= last
)
532 if (row
== before_start
)
534 if (row
== after_start
)
536 if (row
== b
->scroll_buf
)
537 return &b
->scroll_buf
[b
->scroll_size
- 1];
541 static void cursor_clamp(Vt
*t
)
543 Buffer
*b
= t
->buffer
;
544 Row
*lines
= t
->relposmode
? b
->scroll_top
: b
->lines
;
545 int rows
= t
->relposmode
? b
->scroll_bot
- b
->scroll_top
: b
->rows
;
547 if (b
->curs_row
< lines
)
549 if (b
->curs_row
>= lines
+ rows
)
550 b
->curs_row
= lines
+ rows
- 1;
553 if (b
->curs_col
>= b
->cols
)
554 b
->curs_col
= b
->cols
- 1;
557 static void cursor_line_down(Vt
*t
)
559 Buffer
*b
= t
->buffer
;
560 row_set(b
->curs_row
, b
->cols
, b
->maxcols
- b
->cols
, NULL
);
562 if (b
->curs_row
< b
->scroll_bot
)
567 b
->curs_row
= b
->scroll_bot
- 1;
569 row_set(b
->curs_row
, 0, b
->cols
, b
);
572 static void cursor_save(Vt
*t
)
574 Buffer
*b
= t
->buffer
;
575 b
->curs_srow
= b
->curs_row
- b
->lines
;
576 b
->curs_scol
= b
->curs_col
;
579 static void cursor_restore(Vt
*t
)
581 Buffer
*b
= t
->buffer
;
582 b
->curs_row
= b
->lines
+ b
->curs_srow
;
583 b
->curs_col
= b
->curs_scol
;
587 static void attributes_save(Vt
*t
)
589 Buffer
*b
= t
->buffer
;
590 b
->savattrs
= b
->curattrs
;
593 t
->savgraphmode
= t
->graphmode
;
596 static void attributes_restore(Vt
*t
)
598 Buffer
*b
= t
->buffer
;
599 b
->curattrs
= b
->savattrs
;
602 t
->graphmode
= t
->savgraphmode
;
605 static void new_escape_sequence(Vt
*t
)
612 static void cancel_escape_sequence(Vt
*t
)
619 static bool is_valid_csi_ender(int c
)
621 return (c
>= 'a' && c
<= 'z')
622 || (c
>= 'A' && c
<= 'Z')
623 || (c
== '@' || c
== '`');
626 /* interprets a 'set attribute' (SGR) CSI escape sequence */
627 static void interpret_csi_sgr(Vt
*t
, int param
[], int pcount
)
629 Buffer
*b
= t
->buffer
;
631 /* special case: reset attributes */
632 b
->curattrs
= A_NORMAL
;
633 b
->curfg
= b
->curbg
= -1;
637 for (int i
= 0; i
< pcount
; i
++) {
640 b
->curattrs
= A_NORMAL
;
641 b
->curfg
= b
->curbg
= -1;
644 b
->curattrs
|= A_BOLD
;
647 b
->curattrs
|= 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
;
667 b
->curattrs
&= ~(A_BOLD
| A_DIM
);
671 b
->curattrs
&= ~A_ITALIC
;
675 b
->curattrs
&= ~A_UNDERLINE
;
678 b
->curattrs
&= ~A_BLINK
;
681 b
->curattrs
&= ~A_REVERSE
;
684 b
->curattrs
&= ~A_INVIS
;
686 case 30 ... 37: /* fg */
687 b
->curfg
= param
[i
] - 30;
690 if ((i
+ 2) < pcount
&& param
[i
+ 1] == 5) {
691 b
->curfg
= param
[i
+ 2];
698 case 40 ... 47: /* bg */
699 b
->curbg
= param
[i
] - 40;
702 if ((i
+ 2) < pcount
&& param
[i
+ 1] == 5) {
703 b
->curbg
= param
[i
+ 2];
710 case 90 ... 97: /* hi fg */
711 b
->curfg
= param
[i
] - 82;
713 case 100 ... 107: /* hi bg */
714 b
->curbg
= param
[i
] - 92;
722 /* interprets an 'erase display' (ED) escape sequence */
723 static void interpret_csi_ed(Vt
*t
, int param
[], int pcount
)
725 Row
*row
, *start
, *end
;
726 Buffer
*b
= t
->buffer
;
729 b
->curattrs
= A_NORMAL
;
730 b
->curfg
= b
->curbg
= -1;
732 if (pcount
&& param
[0] == 2) {
734 end
= b
->lines
+ b
->rows
;
735 } else if (pcount
&& param
[0] == 1) {
738 row_set(b
->curs_row
, 0, b
->curs_col
+ 1, b
);
740 row_set(b
->curs_row
, b
->curs_col
, b
->cols
- b
->curs_col
, b
);
741 start
= b
->curs_row
+ 1;
742 end
= b
->lines
+ b
->rows
;
745 for (row
= start
; row
< end
; row
++)
746 row_set(row
, 0, b
->cols
, b
);
748 attributes_restore(t
);
751 /* interprets a 'move cursor' (CUP) escape sequence */
752 static void interpret_csi_cup(Vt
*t
, int param
[], int pcount
)
754 Buffer
*b
= t
->buffer
;
755 Row
*lines
= t
->relposmode
? b
->scroll_top
: b
->lines
;
760 } else if (pcount
== 1) {
761 b
->curs_row
= lines
+ param
[0] - 1;
764 b
->curs_row
= lines
+ param
[0] - 1;
765 b
->curs_col
= param
[1] - 1;
771 /* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL,
772 * CPL, CHA, HPR, VPA, VPR, HPA */
773 static void interpret_csi_c(Vt
*t
, char verb
, int param
[], int pcount
)
775 Buffer
*b
= t
->buffer
;
776 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
806 b
->curs_row
= b
->lines
+ n
- 1;
813 /* Interpret the 'erase line' escape sequence */
814 static void interpret_csi_el(Vt
*t
, int param
[], int pcount
)
816 Buffer
*b
= t
->buffer
;
817 switch (pcount
? param
[0] : 0) {
819 row_set(b
->curs_row
, 0, b
->curs_col
+ 1, b
);
822 row_set(b
->curs_row
, 0, b
->cols
, b
);
825 row_set(b
->curs_row
, b
->curs_col
, b
->cols
- b
->curs_col
, b
);
830 /* Interpret the 'insert blanks' sequence (ICH) */
831 static void interpret_csi_ich(Vt
*t
, int param
[], int pcount
)
833 Buffer
*b
= t
->buffer
;
834 Row
*row
= b
->curs_row
;
835 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
837 if (b
->curs_col
+ n
> b
->cols
)
838 n
= b
->cols
- b
->curs_col
;
840 for (int i
= b
->cols
- 1; i
>= b
->curs_col
+ n
; i
--)
841 row
->cells
[i
] = row
->cells
[i
- n
];
843 row_set(row
, b
->curs_col
, n
, b
);
846 /* Interpret the 'delete chars' sequence (DCH) */
847 static void interpret_csi_dch(Vt
*t
, int param
[], int pcount
)
849 Buffer
*b
= t
->buffer
;
850 Row
*row
= b
->curs_row
;
851 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
853 if (b
->curs_col
+ n
> b
->cols
)
854 n
= b
->cols
- b
->curs_col
;
856 for (int i
= b
->curs_col
; i
< b
->cols
- n
; i
++)
857 row
->cells
[i
] = row
->cells
[i
+ n
];
859 row_set(row
, b
->cols
- n
, n
, b
);
862 /* Interpret an 'insert line' sequence (IL) */
863 static void interpret_csi_il(Vt
*t
, int param
[], int pcount
)
865 Buffer
*b
= t
->buffer
;
866 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
868 if (b
->curs_row
+ n
>= b
->scroll_bot
) {
869 for (Row
*row
= b
->curs_row
; row
< b
->scroll_bot
; row
++)
870 row_set(row
, 0, b
->cols
, b
);
872 row_roll(b
->curs_row
, b
->scroll_bot
, -n
);
873 for (Row
*row
= b
->curs_row
; row
< b
->curs_row
+ n
; row
++)
874 row_set(row
, 0, b
->cols
, b
);
878 /* Interpret a 'delete line' sequence (DL) */
879 static void interpret_csi_dl(Vt
*t
, int param
[], int pcount
)
881 Buffer
*b
= t
->buffer
;
882 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
884 if (b
->curs_row
+ n
>= b
->scroll_bot
) {
885 for (Row
*row
= b
->curs_row
; row
< b
->scroll_bot
; row
++)
886 row_set(row
, 0, b
->cols
, b
);
888 row_roll(b
->curs_row
, b
->scroll_bot
, n
);
889 for (Row
*row
= b
->scroll_bot
- n
; row
< b
->scroll_bot
; row
++)
890 row_set(row
, 0, b
->cols
, b
);
894 /* Interpret an 'erase characters' (ECH) sequence */
895 static void interpret_csi_ech(Vt
*t
, int param
[], int pcount
)
897 Buffer
*b
= t
->buffer
;
898 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
900 if (b
->curs_col
+ n
> b
->cols
)
901 n
= b
->cols
- b
->curs_col
;
903 row_set(b
->curs_row
, b
->curs_col
, n
, b
);
906 /* Interpret a 'set scrolling region' (DECSTBM) sequence */
907 static void interpret_csi_decstbm(Vt
*t
, int param
[], int pcount
)
909 Buffer
*b
= t
->buffer
;
910 int new_top
, new_bot
;
914 b
->scroll_top
= b
->lines
;
915 b
->scroll_bot
= b
->lines
+ b
->rows
;
918 new_top
= param
[0] - 1;
921 /* clamp to bounds */
924 if (new_top
>= b
->rows
)
925 new_top
= b
->rows
- 1;
928 if (new_bot
>= b
->rows
)
931 /* check for range validity */
932 if (new_top
< new_bot
) {
933 b
->scroll_top
= b
->lines
+ new_top
;
934 b
->scroll_bot
= b
->lines
+ new_bot
;
938 return; /* malformed */
940 b
->curs_row
= b
->scroll_top
;
944 static void interpret_csi_mode(Vt
*t
, int param
[], int pcount
, bool set
)
946 for (int i
= 0; i
< pcount
; i
++) {
948 case 4: /* insert/replace mode */
955 static void interpret_csi_priv_mode(Vt
*t
, int param
[], int pcount
, bool set
)
957 for (int i
= 0; i
< pcount
; i
++) {
959 case 1: /* set application/normal cursor key mode (DECCKM) */
960 t
->curskeymode
= set
;
962 case 6: /* set origin to relative/absolute (DECOM) */
965 case 25: /* make cursor visible/invisible (DECCM) */
968 case 1049: /* combine 1047 + 1048 */
969 case 47: /* use alternate/normal screen buffer */
972 buffer_clear(&t
->buffer_alternate
);
973 t
->buffer
= set
? &t
->buffer_alternate
: &t
->buffer_normal
;
975 if (param
[i
] != 1049)
978 case 1048: /* save/restore cursor */
984 case 1000: /* enable/disable normal mouse tracking */
991 static void interpret_csi(Vt
*t
)
993 Buffer
*b
= t
->buffer
;
995 unsigned int param_count
= 0;
996 const char *p
= t
->ebuf
+ 1;
997 char verb
= t
->ebuf
[t
->elen
- 1];
999 /* parse numeric parameters */
1000 for (p
+= (t
->ebuf
[1] == '?'); *p
; p
++) {
1001 if (IS_CONTROL(*p
)) {
1002 process_nonprinting(t
, *p
);
1003 } else if (*p
== ';') {
1004 if (param_count
>= LENGTH(csiparam
))
1005 return; /* too long! */
1006 csiparam
[param_count
++] = 0;
1007 } else if (isdigit((unsigned char)*p
)) {
1008 if (param_count
== 0)
1009 csiparam
[param_count
++] = 0;
1010 csiparam
[param_count
- 1] *= 10;
1011 csiparam
[param_count
- 1] += *p
- '0';
1015 if (t
->ebuf
[1] == '?') {
1018 case 'l': /* private set/reset mode */
1019 interpret_csi_priv_mode(t
, csiparam
, param_count
, verb
== 'h');
1025 /* delegate handling depending on command character (verb) */
1028 case 'l': /* set/reset mode */
1029 interpret_csi_mode(t
, csiparam
, param_count
, verb
== 'h');
1031 case 'm': /* set attribute */
1032 interpret_csi_sgr(t
, csiparam
, param_count
);
1034 case 'J': /* erase display */
1035 interpret_csi_ed(t
, csiparam
, param_count
);
1038 case 'f': /* move cursor */
1039 interpret_csi_cup(t
, csiparam
, param_count
);
1051 case '`': /* relative move */
1052 interpret_csi_c(t
, verb
, csiparam
, param_count
);
1054 case 'K': /* erase line */
1055 interpret_csi_el(t
, csiparam
, param_count
);
1057 case '@': /* insert characters */
1058 interpret_csi_ich(t
, csiparam
, param_count
);
1060 case 'P': /* delete characters */
1061 interpret_csi_dch(t
, csiparam
, param_count
);
1063 case 'L': /* insert lines */
1064 interpret_csi_il(t
, csiparam
, param_count
);
1066 case 'M': /* delete lines */
1067 interpret_csi_dl(t
, csiparam
, param_count
);
1069 case 'X': /* erase chars */
1070 interpret_csi_ech(t
, csiparam
, param_count
);
1072 case 'S': /* SU: scroll up */
1073 vt_scroll(t
, param_count
? -csiparam
[0] : -1);
1075 case 'T': /* SD: scroll down */
1076 vt_scroll(t
, param_count
? csiparam
[0] : 1);
1078 case 'Z': /* CBT: cursor backward tabulation */
1079 puttab(t
, param_count
? -csiparam
[0] : -1);
1081 case 'g': /* TBC: tabulation clear */
1082 switch (param_count
? csiparam
[0] : 0) {
1084 b
->tabs
[b
->curs_col
] = false;
1087 memset(b
->tabs
, 0, sizeof(*b
->tabs
) * b
->maxcols
);
1091 case 'r': /* set scrolling region */
1092 interpret_csi_decstbm(t
, csiparam
, param_count
);
1094 case 's': /* save cursor location */
1097 case 'u': /* restore cursor location */
1100 case 'n': /* query cursor location */
1101 if (param_count
== 1 && csiparam
[0] == 6)
1109 /* Interpret an 'index' (IND) sequence */
1110 static void interpret_csi_ind(Vt
*t
)
1112 Buffer
*b
= t
->buffer
;
1113 if (b
->curs_row
< b
->lines
+ b
->rows
- 1)
1117 /* Interpret a 'reverse index' (RI) sequence */
1118 static void interpret_csi_ri(Vt
*t
)
1120 Buffer
*b
= t
->buffer
;
1121 if (b
->curs_row
> b
->scroll_top
)
1124 row_roll(b
->scroll_top
, b
->scroll_bot
, -1);
1125 row_set(b
->scroll_top
, 0, b
->cols
, b
);
1129 /* Interpret a 'next line' (NEL) sequence */
1130 static void interpret_csi_nel(Vt
*t
)
1132 Buffer
*b
= t
->buffer
;
1133 if (b
->curs_row
< b
->lines
+ b
->rows
- 1) {
1139 /* Interpret a 'select character set' (SCS) sequence */
1140 static void interpret_csi_scs(Vt
*t
)
1142 /* ESC ( sets G0, ESC ) sets G1 */
1143 t
->charsets
[!!(t
->ebuf
[0] == ')')] = (t
->ebuf
[1] == '0');
1144 t
->graphmode
= t
->charsets
[0];
1147 /* Interpret an 'operating system command' (OSC) sequence */
1148 static void interpret_osc(Vt
*t
)
1150 /* ESC ] command ; data BEL
1151 * ESC ] command ; data ESC \\
1152 * Note that BEL or ESC \\ have already been replaced with NUL.
1155 int command
= strtoul(t
->ebuf
+ 1, &data
, 10);
1156 if (data
&& *data
== ';') {
1158 case 0: /* icon name and window title */
1159 case 2: /* window title */
1160 if (t
->title_handler
)
1161 t
->title_handler(t
, data
+1);
1163 case 1: /* icon name */
1167 fprintf(stderr
, "unknown OSC command: %d\n", command
);
1174 static void try_interpret_escape_seq(Vt
*t
)
1176 char lastchar
= t
->ebuf
[t
->elen
- 1];
1182 case '#': /* ignore DECDHL, DECSWL, DECDWL, DECHCP, DECFPP */
1184 if (lastchar
== '8') { /* DECALN */
1185 interpret_csi_ed(t
, (int []){ 2 }, 1);
1194 interpret_csi_scs(t
);
1198 case ']': /* OSC - operating system command */
1199 if (lastchar
== '\a' ||
1200 (lastchar
== '\\' && t
->elen
>= 2 && t
->ebuf
[t
->elen
- 2] == '\e')) {
1201 t
->elen
-= lastchar
== '\a' ? 1 : 2;
1202 t
->ebuf
[t
->elen
] = '\0';
1207 case '[': /* CSI - control sequence introducer */
1208 if (is_valid_csi_ender(lastchar
)) {
1213 case '7': /* DECSC: save cursor and attributes */
1217 case '8': /* DECRC: restore cursor and attributes */
1218 attributes_restore(t
);
1221 case 'D': /* IND: index */
1222 interpret_csi_ind(t
);
1224 case 'M': /* RI: reverse index */
1225 interpret_csi_ri(t
);
1227 case 'E': /* NEL: next line */
1228 interpret_csi_nel(t
);
1230 case 'H': /* HTS: horizontal tab set */
1231 t
->buffer
->tabs
[t
->buffer
->curs_col
] = true;
1237 if (t
->elen
+ 1 >= sizeof(t
->ebuf
)) {
1240 fprintf(stderr
, "cancelled: \\033");
1241 for (unsigned int i
= 0; i
< t
->elen
; i
++) {
1242 if (isprint(t
->ebuf
[i
])) {
1243 fputc(t
->ebuf
[i
], stderr
);
1245 fprintf(stderr
, "\\%03o", t
->ebuf
[i
]);
1248 fputc('\n', stderr
);
1251 cancel_escape_sequence(t
);
1255 static void puttab(Vt
*t
, int count
)
1257 Buffer
*b
= t
->buffer
;
1258 int direction
= count
>= 0 ? 1 : -1;
1259 for (int col
= b
->curs_col
+ direction
; count
; col
+= direction
) {
1264 if (col
>= b
->cols
) {
1265 b
->curs_col
= b
->cols
- 1;
1275 static void process_nonprinting(Vt
*t
, wchar_t wc
)
1277 Buffer
*b
= t
->buffer
;
1279 case '\e': /* ESC */
1280 new_escape_sequence(t
);
1282 case '\a': /* BEL */
1283 if (t
->urgent_handler
)
1284 t
->urgent_handler(t
);
1287 if (b
->curs_col
> 0)
1299 cursor_line_down(t
);
1301 case '\016': /* SO: shift out, invoke the G1 character set */
1302 t
->graphmode
= t
->charsets
[1];
1304 case '\017': /* SI: shift in, invoke the G0 character set */
1305 t
->graphmode
= t
->charsets
[0];
1310 static void is_utf8_locale(void)
1312 const char *cset
= nl_langinfo(CODESET
);
1314 cset
= "ANSI_X3.4-1968";
1315 is_utf8
= !strcmp(cset
, "UTF-8");
1318 static wchar_t get_vt100_graphic(char c
)
1320 static char vt100_acs
[] = "`afgjklmnopqrstuvwxyz{|}~";
1323 * 5f-7e standard vt100
1324 * 40-5e rxvt extension for extra curses acs chars
1326 static uint16_t const vt100_utf8
[62] = {
1327 0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259a, 0x2603, // 41-47
1328 0, 0, 0, 0, 0, 0, 0, 0, // 48-4f
1329 0, 0, 0, 0, 0, 0, 0, 0, // 50-57
1330 0, 0, 0, 0, 0, 0, 0, 0x0020, // 58-5f
1331 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, // 60-67
1332 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, // 68-6f
1333 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, // 70-77
1334 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e
1338 return vt100_utf8
[c
- 0x41];
1339 else if (strchr(vt100_acs
, c
))
1340 return NCURSES_ACS(c
);
1344 static void put_wc(Vt
*t
, wchar_t wc
)
1348 if (!t
->seen_input
) {
1350 kill(-t
->pid
, SIGWINCH
);
1354 if (t
->elen
+ 1 < sizeof(t
->ebuf
)) {
1355 t
->ebuf
[t
->elen
] = wc
;
1356 t
->ebuf
[++t
->elen
] = '\0';
1357 try_interpret_escape_seq(t
);
1359 cancel_escape_sequence(t
);
1361 } else if (IS_CONTROL(wc
)) {
1362 process_nonprinting(t
, wc
);
1365 if (wc
>= 0x41 && wc
<= 0x7e) {
1366 wchar_t gc
= get_vt100_graphic(wc
);
1371 } else if ((width
= wcwidth(wc
)) < 1) {
1374 Buffer
*b
= t
->buffer
;
1375 Cell blank_cell
= { L
'\0', build_attrs(b
->curattrs
), b
->curfg
, b
->curbg
};
1376 if (width
== 2 && b
->curs_col
== b
->cols
- 1) {
1377 b
->curs_row
->cells
[b
->curs_col
++] = blank_cell
;
1378 b
->curs_row
->dirty
= true;
1381 if (b
->curs_col
>= b
->cols
) {
1383 cursor_line_down(t
);
1387 Cell
*src
= b
->curs_row
->cells
+ b
->curs_col
;
1388 Cell
*dest
= src
+ width
;
1389 size_t len
= b
->cols
- b
->curs_col
- width
;
1390 memmove(dest
, src
, len
* sizeof *dest
);
1393 b
->curs_row
->cells
[b
->curs_col
] = blank_cell
;
1394 b
->curs_row
->cells
[b
->curs_col
++].text
= wc
;
1395 b
->curs_row
->dirty
= true;
1397 b
->curs_row
->cells
[b
->curs_col
++] = blank_cell
;
1401 int vt_process(Vt
*t
)
1404 unsigned int pos
= 0;
1406 memset(&ps
, 0, sizeof(ps
));
1413 res
= read(t
->pty
, t
->rbuf
+ t
->rlen
, sizeof(t
->rbuf
) - t
->rlen
);
1418 while (pos
< t
->rlen
) {
1422 len
= (ssize_t
)mbrtowc(&wc
, t
->rbuf
+ pos
, t
->rlen
- pos
, &ps
);
1425 memmove(t
->rbuf
, t
->rbuf
+ pos
, t
->rlen
);
1434 pos
+= len
? len
: 1;
1439 memmove(t
->rbuf
, t
->rbuf
+ pos
, t
->rlen
);
1443 void vt_default_colors_set(Vt
*t
, attr_t attrs
, short fg
, short bg
)
1445 t
->defattrs
= attrs
;
1450 Vt
*vt_create(int rows
, int cols
, int scroll_size
)
1452 if (rows
<= 0 || cols
<= 0)
1455 Vt
*t
= calloc(1, sizeof(Vt
));
1460 t
->deffg
= t
->defbg
= -1;
1461 t
->buffer
= &t
->buffer_normal
;
1463 if (!buffer_init(&t
->buffer_normal
, rows
, cols
, scroll_size
) ||
1464 !buffer_init(&t
->buffer_alternate
, rows
, cols
, 0)) {
1472 void vt_resize(Vt
*t
, int rows
, int cols
)
1474 struct winsize ws
= { .ws_row
= rows
, .ws_col
= cols
};
1476 if (rows
<= 0 || cols
<= 0)
1480 buffer_resize(&t
->buffer_normal
, rows
, cols
);
1481 buffer_resize(&t
->buffer_alternate
, rows
, cols
);
1483 ioctl(t
->pty
, TIOCSWINSZ
, &ws
);
1484 kill(-t
->pid
, SIGWINCH
);
1487 void vt_destroy(Vt
*t
)
1491 buffer_free(&t
->buffer_normal
);
1492 buffer_free(&t
->buffer_alternate
);
1497 void vt_dirty(Vt
*t
)
1499 Buffer
*b
= t
->buffer
;
1500 for (Row
*row
= b
->lines
, *end
= row
+ b
->rows
; row
< end
; row
++)
1504 void vt_draw(Vt
*t
, WINDOW
*win
, int srow
, int scol
)
1506 Buffer
*b
= t
->buffer
;
1508 if (srow
!= t
->srow
|| scol
!= t
->scol
) {
1514 for (int i
= 0; i
< b
->rows
; i
++) {
1515 Row
*row
= b
->lines
+ i
;
1520 wmove(win
, srow
+ i
, scol
);
1522 for (int j
= 0; j
< b
->cols
; j
++) {
1523 Cell
*prev_cell
= cell
;
1524 cell
= row
->cells
+ j
;
1525 if (!prev_cell
|| cell
->attr
!= prev_cell
->attr
1526 || cell
->fg
!= prev_cell
->fg
1527 || cell
->bg
!= prev_cell
->bg
) {
1528 if (cell
->attr
== A_NORMAL
)
1529 cell
->attr
= t
->defattrs
;
1531 cell
->fg
= t
->deffg
;
1533 cell
->bg
= t
->defbg
;
1534 wattrset(win
, cell
->attr
<< NCURSES_ATTR_SHIFT
);
1535 wcolor_set(win
, vt_color_get(t
, cell
->fg
, cell
->bg
), NULL
);
1538 if (is_utf8
&& cell
->text
>= 128) {
1539 char buf
[MB_CUR_MAX
+ 1];
1540 size_t len
= wcrtomb(buf
, cell
->text
, NULL
);
1542 waddnstr(win
, buf
, len
);
1543 if (wcwidth(cell
->text
) > 1)
1547 waddch(win
, cell
->text
> ' ' ? cell
->text
: ' ');
1554 if (x
&& x
< b
->cols
- 1)
1555 whline(win
, ' ', b
->cols
- x
);
1560 wmove(win
, srow
+ b
->curs_row
- b
->lines
, scol
+ b
->curs_col
);
1563 void vt_scroll(Vt
*t
, int rows
)
1565 Buffer
*b
= t
->buffer
;
1566 if (!b
->scroll_size
)
1568 if (rows
< 0) { /* scroll back */
1569 if (rows
< -b
->scroll_above
)
1570 rows
= -b
->scroll_above
;
1571 } else { /* scroll forward */
1572 if (rows
> b
->scroll_below
)
1573 rows
= b
->scroll_below
;
1575 buffer_scroll(b
, rows
);
1576 b
->scroll_below
-= rows
;
1579 void vt_noscroll(Vt
*t
)
1581 int scroll_below
= t
->buffer
->scroll_below
;
1583 vt_scroll(t
, scroll_below
);
1586 pid_t
vt_forkpty(Vt
*t
, const char *p
, const char *argv
[], const char *cwd
, const char *env
[], int *to
, int *from
)
1588 int vt2ed
[2], ed2vt
[2];
1590 ws
.ws_row
= t
->buffer
->rows
;
1591 ws
.ws_col
= t
->buffer
->cols
;
1592 ws
.ws_xpixel
= ws
.ws_ypixel
= 0;
1594 if (to
&& pipe(vt2ed
)) {
1598 if (from
&& pipe(ed2vt
)) {
1603 pid_t pid
= forkpty(&t
->pty
, NULL
, NULL
, &ws
);
1611 sigemptyset(&emptyset
);
1612 sigprocmask(SIG_SETMASK
, &emptyset
, NULL
);
1616 dup2(vt2ed
[0], STDIN_FILENO
);
1622 dup2(ed2vt
[1], STDOUT_FILENO
);
1626 int maxfd
= sysconf(_SC_OPEN_MAX
);
1627 for (int fd
= 3; fd
< maxfd
; fd
++)
1628 if (close(fd
) == -1 && errno
== EBADF
)
1631 for (const char **envp
= env
; envp
&& envp
[0]; envp
+= 2)
1632 setenv(envp
[0], envp
[1], 1);
1633 setenv("TERM", vt_term
, 1);
1636 int err
= chdir(cwd
);
1638 fprintf(stderr
, "\nchdir() failed. ");
1644 struct sigaction sa
;
1645 memset(&sa
, 0, sizeof sa
);
1647 sigemptyset(&sa
.sa_mask
);
1648 sa
.sa_handler
= SIG_DFL
;
1649 sigaction(SIGPIPE
, &sa
, NULL
);
1651 execvp(p
, (char *const *)argv
);
1652 fprintf(stderr
, "\nexecv() failed.\nCommand: '%s'\n", argv
[0]);
1666 return t
->pid
= pid
;
1669 int vt_pty_get(Vt
*t
)
1674 ssize_t
vt_write(Vt
*t
, const char *buf
, size_t len
)
1679 ssize_t res
= write(t
->pty
, buf
, len
);
1681 if (errno
!= EAGAIN
&& errno
!= EINTR
)
1692 static void send_curs(Vt
*t
)
1694 Buffer
*b
= t
->buffer
;
1696 snprintf(keyseq
, sizeof keyseq
, "\e[%d;%dR", (int)(b
->curs_row
- b
->lines
), b
->curs_col
);
1697 vt_write(t
, keyseq
, strlen(keyseq
));
1700 void vt_keypress(Vt
*t
, int keycode
)
1704 if (keycode
>= 0 && keycode
<= KEY_MAX
&& keytable
[keycode
]) {
1710 char keyseq
[3] = { '\e', (t
->curskeymode
? 'O' : '['), keytable
[keycode
][0] };
1711 vt_write(t
, keyseq
, sizeof keyseq
);
1715 vt_write(t
, keytable
[keycode
], strlen(keytable
[keycode
]));
1717 } else if (keycode
<= UCHAR_MAX
) {
1722 fprintf(stderr
, "unhandled key %#o\n", keycode
);
1727 void vt_mouse(Vt
*t
, int x
, int y
, mmask_t mask
)
1729 #ifdef NCURSES_MOUSE_VERSION
1730 char seq
[6] = { '\e', '[', 'M' }, state
= 0, button
= 0;
1735 if (mask
& (BUTTON1_PRESSED
| BUTTON1_CLICKED
))
1737 else if (mask
& (BUTTON2_PRESSED
| BUTTON2_CLICKED
))
1739 else if (mask
& (BUTTON3_PRESSED
| BUTTON3_CLICKED
))
1741 else if (mask
& (BUTTON1_RELEASED
| BUTTON2_RELEASED
| BUTTON3_RELEASED
))
1744 if (mask
& BUTTON_SHIFT
)
1746 if (mask
& BUTTON_ALT
)
1748 if (mask
& BUTTON_CTRL
)
1751 seq
[3] = 32 + button
+ state
;
1755 vt_write(t
, seq
, sizeof seq
);
1757 if (mask
& (BUTTON1_CLICKED
| BUTTON2_CLICKED
| BUTTON3_CLICKED
)) {
1758 /* send a button release event */
1760 seq
[3] = 32 + button
+ state
;
1761 vt_write(t
, seq
, sizeof seq
);
1763 #endif /* NCURSES_MOUSE_VERSION */
1766 static unsigned int color_hash(short fg
, short bg
)
1772 return fg
* (COLORS
+ 2) + bg
;
1775 short vt_color_get(Vt
*t
, short fg
, short bg
)
1778 fg
= (t
? t
->deffg
: default_fg
);
1780 bg
= (t
? t
->defbg
: default_bg
);
1782 if (!has_default_colors
) {
1784 fg
= (t
&& t
->deffg
!= -1 ? t
->deffg
: default_fg
);
1786 bg
= (t
&& t
->defbg
!= -1 ? t
->defbg
: default_bg
);
1789 if (!color2palette
|| (fg
== -1 && bg
== -1))
1791 unsigned int index
= color_hash(fg
, bg
);
1792 if (color2palette
[index
] == 0) {
1795 if (++color_pair_current
>= color_pairs_max
)
1796 color_pair_current
= color_pairs_reserved
+ 1;
1797 pair_content(color_pair_current
, &oldfg
, &oldbg
);
1798 unsigned int old_index
= color_hash(oldfg
, oldbg
);
1799 if (color2palette
[old_index
] >= 0) {
1800 if (init_pair(color_pair_current
, fg
, bg
) == OK
) {
1801 color2palette
[old_index
] = 0;
1802 color2palette
[index
] = color_pair_current
;
1809 short color_pair
= color2palette
[index
];
1810 return color_pair
>= 0 ? color_pair
: -color_pair
;
1813 short vt_color_reserve(short fg
, short bg
)
1815 if (!color2palette
|| fg
>= COLORS
|| bg
>= COLORS
)
1817 if (!has_default_colors
&& fg
== -1)
1819 if (!has_default_colors
&& bg
== -1)
1821 if (fg
== -1 && bg
== -1)
1823 unsigned int index
= color_hash(fg
, bg
);
1824 if (color2palette
[index
] >= 0) {
1825 if (init_pair(color_pairs_reserved
+ 1, fg
, bg
) == OK
)
1826 color2palette
[index
] = -(++color_pairs_reserved
);
1828 short color_pair
= color2palette
[index
];
1829 return color_pair
>= 0 ? color_pair
: -color_pair
;
1832 static void init_colors(void)
1834 pair_content(0, &default_fg
, &default_bg
);
1835 if (default_fg
== -1)
1836 default_fg
= COLOR_WHITE
;
1837 if (default_bg
== -1)
1838 default_bg
= COLOR_BLACK
;
1839 has_default_colors
= (use_default_colors() == OK
);
1840 color_pairs_max
= MIN(MAX_COLOR_PAIRS
, SHRT_MAX
);
1842 color2palette
= calloc((COLORS
+ 2) * (COLORS
+ 2), sizeof(short));
1844 * XXX: On undefined color-pairs NetBSD curses pair_content() set fg
1845 * and bg to default colors while ncurses set them respectively to
1846 * 0 and 0. Initialize all color-pairs in order to have consistent
1847 * behaviour despite the implementation used.
1849 for (short i
= 1; i
< color_pairs_max
; i
++)
1851 vt_color_reserve(COLOR_WHITE
, COLOR_BLACK
);
1858 char *term
= getenv("DVTM_TERM");
1861 snprintf(vt_term
, sizeof vt_term
, "%s%s", term
, COLORS
>= 256 ? "-256color" : "");
1864 void vt_keytable_set(const char * const keytable_overlay
[], int count
)
1866 for (int k
= 0; k
< count
&& k
< KEY_MAX
; k
++) {
1867 const char *keyseq
= keytable_overlay
[k
];
1869 keytable
[k
] = keyseq
;
1873 void vt_shutdown(void)
1875 free(color2palette
);
1878 void vt_title_handler_set(Vt
*t
, vt_title_handler_t handler
)
1880 t
->title_handler
= handler
;
1883 void vt_urgent_handler_set(Vt
*t
, vt_urgent_handler_t handler
)
1885 t
->urgent_handler
= handler
;
1888 void vt_data_set(Vt
*t
, void *data
)
1893 void *vt_data_get(Vt
*t
)
1898 bool vt_cursor_visible(Vt
*t
)
1900 return t
->buffer
->scroll_below
? false : !t
->curshid
;
1903 pid_t
vt_pid_get(Vt
*t
)
1908 size_t vt_content_get(Vt
*t
, char **buf
, bool colored
)
1910 Buffer
*b
= t
->buffer
;
1911 int lines
= b
->scroll_above
+ b
->scroll_below
+ b
->rows
+ 1;
1912 size_t size
= lines
* ((b
->cols
+ 1) * ((colored
? 64 : 0) + MB_CUR_MAX
));
1914 memset(&ps
, 0, sizeof(ps
));
1916 if (!(*buf
= malloc(size
)))
1920 Cell
*prev_cell
= NULL
;
1922 for (Row
*row
= buffer_row_first(b
); row
; row
= buffer_row_next(b
, row
)) {
1924 char *last_non_space
= s
;
1925 for (int col
= 0; col
< b
->cols
; col
++) {
1926 Cell
*cell
= row
->cells
+ col
;
1929 if (!prev_cell
|| cell
->attr
!= prev_cell
->attr
) {
1930 attr_t attr
= cell
->attr
<< NCURSES_ATTR_SHIFT
;
1931 esclen
= sprintf(s
, "\033[0%s%s%s%s%s%sm",
1932 attr
& A_BOLD
? ";1" : "",
1933 attr
& A_DIM
? ";2" : "",
1934 attr
& A_UNDERLINE
? ";4" : "",
1935 attr
& A_BLINK
? ";5" : "",
1936 attr
& A_REVERSE
? ";7" : "",
1937 attr
& A_INVIS
? ";8" : "");
1941 if (!prev_cell
|| cell
->fg
!= prev_cell
->fg
|| cell
->attr
!= prev_cell
->attr
) {
1943 esclen
= sprintf(s
, "\033[39m");
1945 esclen
= sprintf(s
, "\033[38;5;%dm", cell
->fg
);
1949 if (!prev_cell
|| cell
->bg
!= prev_cell
->bg
|| cell
->attr
!= prev_cell
->attr
) {
1951 esclen
= sprintf(s
, "\033[49m");
1953 esclen
= sprintf(s
, "\033[48;5;%dm", cell
->bg
);
1960 len
= wcrtomb(s
, cell
->text
, &ps
);
1978 int vt_content_start(Vt
*t
)
1980 return t
->buffer
->scroll_above
;