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.
30 #include <sys/ioctl.h>
31 #include <sys/types.h>
34 #if defined(__linux__) || defined(__CYGWIN__)
36 #elif defined(__FreeBSD__)
38 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
41 #if defined(__CYGWIN__) || defined(_AIX)
48 # include "forkpty-aix.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 #define IS_CONTROL(ch) !((ch) & 0xffffff60UL)
76 #define MIN(x, y) ((x) < (y) ? (x) : (y))
77 #define sstrlen(str) (sizeof(str) - 1)
79 #define COPYMODE_ATTR A_REVERSE
80 static bool is_utf8
, has_default_colors
;
81 static short color_pairs_reserved
, color_pairs_max
, color_pair_current
;
82 static short *color2palette
, default_fg
, default_bg
;
83 static char vt_term
[32] = "dvtm";
100 void (*callback
)(void *data
);
125 int scroll_amount_above
;
126 int scroll_amount_below
;
127 int rows
, cols
, maxcols
;
128 unsigned curattrs
, savattrs
;
129 int curs_col
, curs_srow
, curs_scol
;
130 short curfg
, curbg
, savfg
, savbg
;
134 Buffer buffer_normal
;
135 Buffer buffer_alternate
;
143 unsigned seen_input
:1;
147 unsigned curskeymode
:1;
149 unsigned copymode_selecting
:1;
151 unsigned relposmode
:1;
152 unsigned mousetrack
:1;
153 unsigned graphmode
:1;
154 unsigned savgraphmode
:1;
157 int copymode_curs_srow
, copymode_curs_scol
;
158 Row
*copymode_sel_start_row
;
159 int copymode_sel_start_col
;
160 int copymode_cmd_multiplier
;
162 /* buffers and parsing state */
165 unsigned int rlen
, elen
;
167 /* xterm style window title */
170 vt_event_handler_t event_handler
;
172 /* custom escape sequence handler */
173 vt_escseq_handler_t escseq_handler
;
177 static const char *keytable
[KEY_MAX
+1] = {
179 /* for the arrow keys the CSI / SS3 sequences are not stored here
180 * because they depend on the current cursor terminal mode
187 [KEY_SUP
] = "\e[1;2A",
190 [KEY_SDOWN
] = "\e[1;2B",
192 [KEY_SRIGHT
] = "\e[1;2C",
193 [KEY_SLEFT
] = "\e[1;2D",
194 [KEY_BACKSPACE
] = "\177",
197 [KEY_PPAGE
] = "\e[5~",
198 [KEY_NPAGE
] = "\e[6~",
199 [KEY_HOME
] = "\e[7~",
202 [KEY_SUSPEND
] = "\x1A", /* Ctrl+Z gets mapped to this */
203 [KEY_F(1)] = "\e[11~",
204 [KEY_F(2)] = "\e[12~",
205 [KEY_F(3)] = "\e[13~",
206 [KEY_F(4)] = "\e[14~",
207 [KEY_F(5)] = "\e[15~",
208 [KEY_F(6)] = "\e[17~",
209 [KEY_F(7)] = "\e[18~",
210 [KEY_F(8)] = "\e[19~",
211 [KEY_F(9)] = "\e[20~",
212 [KEY_F(10)] = "\e[21~",
213 [KEY_F(11)] = "\e[23~",
214 [KEY_F(12)] = "\e[24~",
215 [KEY_F(13)] = "\e[23~",
216 [KEY_F(14)] = "\e[24~",
217 [KEY_F(15)] = "\e[25~",
218 [KEY_F(16)] = "\e[26~",
219 [KEY_F(17)] = "\e[28~",
220 [KEY_F(18)] = "\e[29~",
221 [KEY_F(19)] = "\e[31~",
222 [KEY_F(20)] = "\e[32~",
223 [KEY_F(21)] = "\e[33~",
224 [KEY_F(22)] = "\e[34~",
231 static void puttab(Vt
*t
, int count
);
232 static void process_nonprinting(Vt
*t
, wchar_t wc
);
233 static void send_curs(Vt
*t
);
234 static void cmdline_hide_callback(void *t
);
235 static void cmdline_free(Cmdline
*c
);
237 static int xwcwidth(wchar_t c
) {
244 __attribute__ ((const))
245 static uint16_t build_attrs(unsigned curattrs
)
247 return ((curattrs
& ~A_COLOR
) | COLOR_PAIR(curattrs
& 0xff))
248 >> NCURSES_ATTR_SHIFT
;
251 static void row_set(Row
*row
, int start
, int len
, Buffer
*t
)
255 .attr
= t
? build_attrs(t
->curattrs
) : 0,
256 .fg
= t
? t
->curfg
: -1,
257 .bg
= t
? t
->curbg
: -1,
260 for (int i
= start
; i
< len
+ start
; i
++)
261 row
->cells
[i
] = cell
;
265 static void row_roll(Row
*start
, Row
*end
, int count
)
274 Row
*buf
= alloca(count
* sizeof(Row
));
276 memcpy(buf
, start
, count
* sizeof(Row
));
277 memmove(start
, start
+ count
, (n
- count
) * sizeof(Row
));
278 memcpy(end
- count
, buf
, count
* sizeof(Row
));
279 for (Row
*row
= start
; row
< end
; row
++)
284 static void clamp_cursor_to_bounds(Vt
*t
)
286 Buffer
*b
= t
->buffer
;
287 Row
*lines
= t
->relposmode
? b
->scroll_top
: b
->lines
;
288 int rows
= t
->relposmode
? b
->scroll_bot
- b
->scroll_top
: b
->rows
;
290 if (b
->curs_row
< lines
)
292 if (b
->curs_row
>= lines
+ rows
)
293 b
->curs_row
= lines
+ rows
- 1;
296 if (b
->curs_col
>= b
->cols
)
297 b
->curs_col
= b
->cols
- 1;
300 static void save_curs(Vt
*t
)
302 Buffer
*b
= t
->buffer
;
303 b
->curs_srow
= b
->curs_row
- b
->lines
;
304 b
->curs_scol
= b
->curs_col
;
307 static void restore_curs(Vt
*t
)
309 Buffer
*b
= t
->buffer
;
310 b
->curs_row
= b
->lines
+ b
->curs_srow
;
311 b
->curs_col
= b
->curs_scol
;
312 clamp_cursor_to_bounds(t
);
315 static void save_attrs(Vt
*t
)
317 Buffer
*b
= t
->buffer
;
318 b
->savattrs
= b
->curattrs
;
321 t
->savgraphmode
= t
->graphmode
;
324 static void restore_attrs(Vt
*t
)
326 Buffer
*b
= t
->buffer
;
327 b
->curattrs
= b
->savattrs
;
330 t
->graphmode
= t
->savgraphmode
;
333 static void fill_scroll_buf(Buffer
*t
, int s
)
335 /* work in screenfuls */
336 int ssz
= t
->scroll_bot
- t
->scroll_top
;
338 fill_scroll_buf(t
, ssz
);
339 fill_scroll_buf(t
, s
- ssz
);
343 fill_scroll_buf(t
, -ssz
);
344 fill_scroll_buf(t
, s
+ ssz
);
348 t
->scroll_amount_above
+= s
;
349 if (t
->scroll_amount_above
>= t
->scroll_buf_size
)
350 t
->scroll_amount_above
= t
->scroll_buf_size
;
352 if (s
> 0 && t
->scroll_buf_size
) {
353 for (int i
= 0; i
< s
; i
++) {
354 Row tmp
= t
->scroll_top
[i
];
355 t
->scroll_top
[i
] = t
->scroll_buf
[t
->scroll_buf_ptr
];
356 t
->scroll_buf
[t
->scroll_buf_ptr
] = tmp
;
359 if (t
->scroll_buf_ptr
== t
->scroll_buf_size
)
360 t
->scroll_buf_ptr
= 0;
363 row_roll(t
->scroll_top
, t
->scroll_bot
, s
);
364 if (s
< 0 && t
->scroll_buf_size
) {
365 for (int i
= (-s
) - 1; i
>= 0; i
--) {
367 if (t
->scroll_buf_ptr
== -1)
368 t
->scroll_buf_ptr
= t
->scroll_buf_size
- 1;
370 Row tmp
= t
->scroll_top
[i
];
371 t
->scroll_top
[i
] = t
->scroll_buf
[t
->scroll_buf_ptr
];
372 t
->scroll_buf
[t
->scroll_buf_ptr
] = tmp
;
373 t
->scroll_top
[i
].dirty
= true;
378 static void cursor_line_down(Vt
*t
)
380 Buffer
*b
= t
->buffer
;
381 row_set(b
->curs_row
, b
->cols
, b
->maxcols
- b
->cols
, NULL
);
383 if (b
->curs_row
< b
->scroll_bot
)
388 b
->curs_row
= b
->scroll_bot
- 1;
389 fill_scroll_buf(b
, 1);
390 row_set(b
->curs_row
, 0, b
->cols
, b
);
393 static void new_escape_sequence(Vt
*t
)
400 static void cancel_escape_sequence(Vt
*t
)
407 static bool is_valid_csi_ender(int c
)
409 return (c
>= 'a' && c
<= 'z')
410 || (c
>= 'A' && c
<= 'Z')
411 || (c
== '@' || c
== '`');
414 /* interprets a 'set attribute' (SGR) CSI escape sequence */
415 static void interpret_csi_sgr(Vt
*t
, int param
[], int pcount
)
417 Buffer
*b
= t
->buffer
;
419 /* special case: reset attributes */
420 b
->curattrs
= A_NORMAL
;
421 b
->curfg
= b
->curbg
= -1;
425 for (int i
= 0; i
< pcount
; i
++) {
428 b
->curattrs
= A_NORMAL
;
429 b
->curfg
= b
->curbg
= -1;
432 b
->curattrs
|= A_BOLD
;
435 b
->curattrs
|= A_UNDERLINE
;
438 b
->curattrs
|= A_BLINK
;
441 b
->curattrs
|= A_REVERSE
;
444 b
->curattrs
|= A_INVIS
;
447 b
->curattrs
&= ~A_BOLD
;
450 b
->curattrs
&= ~A_UNDERLINE
;
453 b
->curattrs
&= ~A_BLINK
;
456 b
->curattrs
&= ~A_REVERSE
;
459 b
->curattrs
&= ~A_INVIS
;
461 case 30 ... 37: /* fg */
462 b
->curfg
= param
[i
] - 30;
465 if ((i
+ 2) < pcount
&& param
[i
+ 1] == 5) {
466 b
->curfg
= param
[i
+ 2];
473 case 40 ... 47: /* bg */
474 b
->curbg
= param
[i
] - 40;
477 if ((i
+ 2) < pcount
&& param
[i
+ 1] == 5) {
478 b
->curbg
= param
[i
+ 2];
485 case 90 ... 97: /* hi fg */
486 b
->curfg
= param
[i
] - 82;
488 case 100 ... 107: /* hi bg */
489 b
->curbg
= param
[i
] - 92;
497 /* interprets an 'erase display' (ED) escape sequence */
498 static void interpret_csi_ed(Vt
*t
, int param
[], int pcount
)
500 Row
*row
, *start
, *end
;
501 Buffer
*b
= t
->buffer
;
504 b
->curattrs
= A_NORMAL
;
505 b
->curfg
= b
->curbg
= -1;
507 if (pcount
&& param
[0] == 2) {
509 end
= b
->lines
+ b
->rows
;
510 } else if (pcount
&& param
[0] == 1) {
513 row_set(b
->curs_row
, 0, b
->curs_col
+ 1, b
);
515 row_set(b
->curs_row
, b
->curs_col
, b
->cols
- b
->curs_col
, b
);
516 start
= b
->curs_row
+ 1;
517 end
= b
->lines
+ b
->rows
;
520 for (row
= start
; row
< end
; row
++)
521 row_set(row
, 0, b
->cols
, b
);
526 /* interprets a 'move cursor' (CUP) escape sequence */
527 static void interpret_csi_cup(Vt
*t
, int param
[], int pcount
)
529 Buffer
*b
= t
->buffer
;
530 Row
*lines
= t
->relposmode
? b
->scroll_top
: b
->lines
;
535 } else if (pcount
== 1) {
536 b
->curs_row
= lines
+ param
[0] - 1;
539 b
->curs_row
= lines
+ param
[0] - 1;
540 b
->curs_col
= param
[1] - 1;
543 clamp_cursor_to_bounds(t
);
546 /* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL,
547 * CPL, CHA, HPR, VPA, VPR, HPA */
548 static void interpret_csi_c(Vt
*t
, char verb
, int param
[], int pcount
)
550 Buffer
*b
= t
->buffer
;
551 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
578 b
->curs_col
= param
[0] - 1;
581 b
->curs_row
= b
->lines
+ param
[0] - 1;
585 clamp_cursor_to_bounds(t
);
588 /* Interpret the 'erase line' escape sequence */
589 static void interpret_csi_el(Vt
*t
, int param
[], int pcount
)
591 Buffer
*b
= t
->buffer
;
592 switch (pcount
? param
[0] : 0) {
594 row_set(b
->curs_row
, 0, b
->curs_col
+ 1, b
);
597 row_set(b
->curs_row
, 0, b
->cols
, b
);
600 row_set(b
->curs_row
, b
->curs_col
, b
->cols
- b
->curs_col
, b
);
605 /* Interpret the 'insert blanks' sequence (ICH) */
606 static void interpret_csi_ich(Vt
*t
, int param
[], int pcount
)
608 Buffer
*b
= t
->buffer
;
609 Row
*row
= b
->curs_row
;
610 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
612 if (b
->curs_col
+ n
> b
->cols
)
613 n
= b
->cols
- b
->curs_col
;
615 for (int i
= b
->cols
- 1; i
>= b
->curs_col
+ n
; i
--)
616 row
->cells
[i
] = row
->cells
[i
- n
];
618 row_set(row
, b
->curs_col
, n
, b
);
621 /* Interpret the 'delete chars' sequence (DCH) */
622 static void interpret_csi_dch(Vt
*t
, int param
[], int pcount
)
624 Buffer
*b
= t
->buffer
;
625 Row
*row
= b
->curs_row
;
626 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
628 if (b
->curs_col
+ n
> b
->cols
)
629 n
= b
->cols
- b
->curs_col
;
631 for (int i
= b
->curs_col
; i
< b
->cols
- n
; i
++)
632 row
->cells
[i
] = row
->cells
[i
+ n
];
634 row_set(row
, b
->cols
- n
, n
, b
);
637 /* Interpret an 'insert line' sequence (IL) */
638 static void interpret_csi_il(Vt
*t
, int param
[], int pcount
)
640 Buffer
*b
= t
->buffer
;
641 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
643 if (b
->curs_row
+ n
>= b
->scroll_bot
) {
644 for (Row
*row
= b
->curs_row
; row
< b
->scroll_bot
; row
++)
645 row_set(row
, 0, b
->cols
, b
);
647 row_roll(b
->curs_row
, b
->scroll_bot
, -n
);
648 for (Row
*row
= b
->curs_row
; row
< b
->curs_row
+ n
; row
++)
649 row_set(row
, 0, b
->cols
, b
);
653 /* Interpret a 'delete line' sequence (DL) */
654 static void interpret_csi_dl(Vt
*t
, int param
[], int pcount
)
656 Buffer
*b
= t
->buffer
;
657 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
659 if (b
->curs_row
+ n
>= b
->scroll_bot
) {
660 for (Row
*row
= b
->curs_row
; row
< b
->scroll_bot
; row
++)
661 row_set(row
, 0, b
->cols
, b
);
663 row_roll(b
->curs_row
, b
->scroll_bot
, n
);
664 for (Row
*row
= b
->scroll_bot
- n
; row
< b
->scroll_bot
; row
++)
665 row_set(row
, 0, b
->cols
, b
);
669 /* Interpret an 'erase characters' (ECH) sequence */
670 static void interpret_csi_ech(Vt
*t
, int param
[], int pcount
)
672 Buffer
*b
= t
->buffer
;
673 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
675 if (b
->curs_col
+ n
> b
->cols
)
676 n
= b
->cols
- b
->curs_col
;
678 row_set(b
->curs_row
, b
->curs_col
, n
, b
);
681 /* Interpret a 'set scrolling region' (DECSTBM) sequence */
682 static void interpret_csi_decstbm(Vt
*t
, int param
[], int pcount
)
684 Buffer
*b
= t
->buffer
;
685 int new_top
, new_bot
;
689 b
->scroll_top
= b
->lines
;
690 b
->scroll_bot
= b
->lines
+ b
->rows
;
693 new_top
= param
[0] - 1;
696 /* clamp to bounds */
699 if (new_top
>= b
->rows
)
700 new_top
= b
->rows
- 1;
703 if (new_bot
>= b
->rows
)
706 /* check for range validity */
707 if (new_top
< new_bot
) {
708 b
->scroll_top
= b
->lines
+ new_top
;
709 b
->scroll_bot
= b
->lines
+ new_bot
;
713 return; /* malformed */
715 b
->curs_row
= b
->scroll_top
;
719 static void interpret_csi_mode(Vt
*t
, int param
[], int pcount
, bool set
)
721 for (int i
= 0; i
< pcount
; i
++) {
723 case 4: /* insert/replace mode */
730 static void interpret_csi_priv_mode(Vt
*t
, int param
[], int pcount
, bool set
)
732 for (int i
= 0; i
< pcount
; i
++) {
734 case 1: /* set application/normal cursor key mode (DECCKM) */
735 t
->curskeymode
= set
;
737 case 6: /* set origin to relative/absolute (DECOM) */
740 case 25: /* make cursor visible/invisible (DECCM) */
743 case 47: /* use alternate/normal screen buffer */
744 vt_copymode_leave(t
);
745 t
->buffer
= set
? &t
->buffer_alternate
: &t
->buffer_normal
;
748 case 1000: /* enable/disable normal mouse tracking */
755 static void interpret_csi(Vt
*t
)
757 static int csiparam
[BUFSIZ
];
758 Buffer
*b
= t
->buffer
;
760 const char *p
= t
->ebuf
+ 1;
761 char verb
= t
->ebuf
[t
->elen
- 1];
763 /* parse numeric parameters */
764 for (p
+= (t
->ebuf
[1] == '?'); *p
; p
++) {
765 if (IS_CONTROL(*p
)) {
766 process_nonprinting(t
, *p
);
767 } else if (*p
== ';') {
768 if (param_count
>= (int)sizeof(csiparam
))
769 return; /* too long! */
770 csiparam
[param_count
++] = 0;
771 } else if (isdigit((unsigned char)*p
)) {
772 if (param_count
== 0)
773 csiparam
[param_count
++] = 0;
774 csiparam
[param_count
- 1] *= 10;
775 csiparam
[param_count
- 1] += *p
- '0';
779 if (t
->ebuf
[1] == '?') {
782 case 'l': /* private set/reset mode */
783 interpret_csi_priv_mode(t
, csiparam
, param_count
, verb
== 'h');
789 /* delegate handling depending on command character (verb) */
792 case 'l': /* set/reset mode */
793 interpret_csi_mode(t
, csiparam
, param_count
, verb
== 'h');
795 case 'm': /* set attribute */
796 interpret_csi_sgr(t
, csiparam
, param_count
);
798 case 'J': /* erase display */
799 interpret_csi_ed(t
, csiparam
, param_count
);
802 case 'f': /* move cursor */
803 interpret_csi_cup(t
, csiparam
, param_count
);
815 case '`': /* relative move */
816 interpret_csi_c(t
, verb
, csiparam
, param_count
);
818 case 'K': /* erase line */
819 interpret_csi_el(t
, csiparam
, param_count
);
821 case '@': /* insert characters */
822 interpret_csi_ich(t
, csiparam
, param_count
);
824 case 'P': /* delete characters */
825 interpret_csi_dch(t
, csiparam
, param_count
);
827 case 'L': /* insert lines */
828 interpret_csi_il(t
, csiparam
, param_count
);
830 case 'M': /* delete lines */
831 interpret_csi_dl(t
, csiparam
, param_count
);
833 case 'X': /* erase chars */
834 interpret_csi_ech(t
, csiparam
, param_count
);
836 case 'S': /* SU: scroll up */
837 vt_scroll(t
, param_count
? -csiparam
[0] : -1);
839 case 'T': /* SD: scroll down */
840 vt_scroll(t
, param_count
? csiparam
[0] : 1);
842 case 'Z': /* CBT: cursor backward tabulation */
843 puttab(t
, param_count
? -csiparam
[0] : -1);
845 case 'g': /* TBC: tabulation clear */
846 switch (csiparam
[0]) {
848 b
->tabs
[b
->curs_col
] = false;
851 memset(b
->tabs
, 0, sizeof(*b
->tabs
) * b
->maxcols
);
854 case 'r': /* set scrolling region */
855 interpret_csi_decstbm(t
, csiparam
, param_count
);
857 case 's': /* save cursor location */
860 case 'u': /* restore cursor location */
863 case 'n': /* query cursor location */
864 if (param_count
== 1 && csiparam
[0] == 6)
872 /* Interpret an 'index' (IND) sequence */
873 static void interpret_csi_ind(Vt
*t
)
875 Buffer
*b
= t
->buffer
;
876 if (b
->curs_row
< b
->lines
+ b
->rows
- 1)
880 /* Interpret a 'reverse index' (RI) sequence */
881 static void interpret_csi_ri(Vt
*t
)
883 Buffer
*b
= t
->buffer
;
884 if (b
->curs_row
> b
->scroll_top
)
887 row_roll(b
->scroll_top
, b
->scroll_bot
, -1);
888 row_set(b
->scroll_top
, 0, b
->cols
, b
);
892 /* Interpret a 'next line' (NEL) sequence */
893 static void interpret_csi_nel(Vt
*t
)
895 Buffer
*b
= t
->buffer
;
896 if (b
->curs_row
< b
->lines
+ b
->rows
- 1) {
902 /* Interpret a 'select character set' (SCS) sequence */
903 static void interpret_csi_scs(Vt
*t
)
905 /* ESC ( sets G0, ESC ) sets G1 */
906 t
->charsets
[!!(t
->ebuf
[0] == ')')] = (t
->ebuf
[1] == '0');
907 t
->graphmode
= t
->charsets
[0];
910 /* Interpret xterm specific escape sequences */
911 static void interpret_esc_xterm(Vt
*t
)
913 /* ESC]n;dataBEL -- the ESC is not part of t->ebuf */
916 switch (t
->ebuf
[1]) {
919 t
->ebuf
[t
->elen
- 1] = '\0';
920 if (t
->elen
> sstrlen("]n;\a"))
921 title
= t
->ebuf
+ sstrlen("]n;");
923 if (t
->event_handler
)
924 t
->event_handler(t
, VT_EVENT_TITLE
, title
);
928 static void try_interpret_escape_seq(Vt
*t
)
930 char lastchar
= t
->ebuf
[t
->elen
- 1];
935 if (t
->escseq_handler
) {
936 switch ((*(t
->escseq_handler
)) (t
, t
->ebuf
)) {
937 case VT_ESCSEQ_HANDLER_OK
:
938 cancel_escape_sequence(t
);
940 case VT_ESCSEQ_HANDLER_NOTYET
:
941 if (t
->elen
+ 1 >= sizeof(t
->ebuf
))
948 case '#': /* ignore DECDHL, DECSWL, DECDWL, DECHCP, DECFPP */
950 if (lastchar
== '8') { /* DECALN */
951 interpret_csi_ed(t
, (int []){ 2 }, 1);
960 interpret_csi_scs(t
);
964 case ']': /* xterm thing */
965 if (lastchar
== '\a' ||
966 (lastchar
== '\\' && t
->elen
>= 2 && t
->ebuf
[t
->elen
- 2] == '\e')) {
967 interpret_esc_xterm(t
);
972 if (is_valid_csi_ender(lastchar
)) {
977 case '7': /* DECSC: save cursor and attributes */
981 case '8': /* DECRC: restore cursor and attributes */
985 case 'D': /* IND: index */
986 interpret_csi_ind(t
);
988 case 'M': /* RI: reverse index */
991 case 'E': /* NEL: next line */
992 interpret_csi_nel(t
);
994 case 'H': /* HTS: horizontal tab set */
995 t
->buffer
->tabs
[t
->buffer
->curs_col
] = true;
1001 if (t
->elen
+ 1 >= sizeof(t
->ebuf
)) {
1004 fprintf(stderr
, "cancelled: \\033");
1005 for (unsigned int i
= 0; i
< t
->elen
; i
++) {
1006 if (isprint(t
->ebuf
[i
])) {
1007 fputc(t
->ebuf
[i
], stderr
);
1009 fprintf(stderr
, "\\%03o", t
->ebuf
[i
]);
1012 fputc('\n', stderr
);
1015 cancel_escape_sequence(t
);
1019 static void puttab(Vt
*t
, int count
)
1021 Buffer
*b
= t
->buffer
;
1022 int direction
= count
>= 0 ? 1 : -1;
1023 int col
= b
->curs_col
+ direction
;
1029 if (col
>= b
->cols
) {
1030 b
->curs_col
= b
->cols
- 1;
1041 static void process_nonprinting(Vt
*t
, wchar_t wc
)
1043 Buffer
*b
= t
->buffer
;
1045 case '\e': /* ESC */
1046 new_escape_sequence(t
);
1048 case '\a': /* BEL */
1053 if (b
->curs_col
> 0)
1065 cursor_line_down(t
);
1067 case '\016': /* SO: shift out, invoke the G1 character set */
1068 t
->graphmode
= t
->charsets
[1];
1070 case '\017': /* SI: shift in, invoke the G0 character set */
1071 t
->graphmode
= t
->charsets
[0];
1076 static void is_utf8_locale(void)
1078 const char *cset
= nl_langinfo(CODESET
);
1080 cset
= "ANSI_X3.4-1968";
1081 is_utf8
= !strcmp(cset
, "UTF-8");
1084 static wchar_t get_vt100_graphic(char c
)
1086 static char vt100_acs
[] = "`afgjklmnopqrstuvwxyz{|}~";
1089 * 5f-7e standard vt100
1090 * 40-5e rxvt extension for extra curses acs chars
1092 static uint16_t const vt100_utf8
[62] = {
1093 0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259a, 0x2603, // 41-47
1094 0, 0, 0, 0, 0, 0, 0, 0, // 48-4f
1095 0, 0, 0, 0, 0, 0, 0, 0, // 50-57
1096 0, 0, 0, 0, 0, 0, 0, 0x0020, // 58-5f
1097 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, // 60-67
1098 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, // 68-6f
1099 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, // 70-77
1100 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e
1104 return vt100_utf8
[c
- 0x41];
1105 else if (strchr(vt100_acs
, c
))
1106 return NCURSES_ACS(c
);
1110 static void put_wc(Vt
*t
, wchar_t wc
)
1114 if (!t
->seen_input
) {
1116 kill(-t
->childpid
, SIGWINCH
);
1120 if (t
->elen
+ 1 < sizeof(t
->ebuf
)) {
1121 t
->ebuf
[t
->elen
] = wc
;
1122 t
->ebuf
[++t
->elen
] = '\0';
1123 try_interpret_escape_seq(t
);
1125 cancel_escape_sequence(t
);
1127 } else if (IS_CONTROL(wc
)) {
1128 process_nonprinting(t
, wc
);
1131 if (wc
>= 0x41 && wc
<= 0x7e) {
1132 wchar_t gc
= get_vt100_graphic(wc
);
1137 } else if ((width
= wcwidth(wc
)) < 1) {
1140 Buffer
*b
= t
->buffer
;
1141 Cell blank_cell
= { L
'\0', build_attrs(b
->curattrs
), b
->curfg
, b
->curbg
};
1142 if (width
== 2 && b
->curs_col
== b
->cols
- 1) {
1143 b
->curs_row
->cells
[b
->curs_col
++] = blank_cell
;
1144 b
->curs_row
->dirty
= true;
1147 if (b
->curs_col
>= b
->cols
) {
1149 cursor_line_down(t
);
1153 Cell
*src
= b
->curs_row
->cells
+ b
->curs_col
;
1154 Cell
*dest
= src
+ width
;
1155 size_t len
= b
->cols
- b
->curs_col
- width
;
1156 memmove(dest
, src
, len
* sizeof *dest
);
1159 b
->curs_row
->cells
[b
->curs_col
] = blank_cell
;
1160 b
->curs_row
->cells
[b
->curs_col
++].text
= wc
;
1161 b
->curs_row
->dirty
= true;
1163 b
->curs_row
->cells
[b
->curs_col
++] = blank_cell
;
1167 int vt_process(Vt
*t
)
1170 unsigned int pos
= 0;
1172 memset(&ps
, 0, sizeof(ps
));
1179 res
= read(t
->pty
, t
->rbuf
+ t
->rlen
, sizeof(t
->rbuf
) - t
->rlen
);
1184 while (pos
< t
->rlen
) {
1188 len
= (ssize_t
)mbrtowc(&wc
, t
->rbuf
+ pos
, t
->rlen
- pos
, &ps
);
1191 memmove(t
->rbuf
, t
->rbuf
+ pos
, t
->rlen
);
1200 pos
+= len
? len
: 1;
1205 memmove(t
->rbuf
, t
->rbuf
+ pos
, t
->rlen
);
1209 void vt_set_default_colors(Vt
*t
, unsigned attrs
, short fg
, short bg
)
1211 t
->defattrs
= attrs
;
1216 static void buffer_free(Buffer
*t
)
1218 for (int i
= 0; i
< t
->rows
; i
++)
1219 free(t
->lines
[i
].cells
);
1221 for (int i
= 0; i
< t
->scroll_buf_size
; i
++)
1222 free(t
->scroll_buf
[i
].cells
);
1223 free(t
->scroll_buf
);
1227 static bool buffer_init(Buffer
*t
, int rows
, int cols
, int scroll_buf_size
)
1229 Row
*lines
, *scroll_buf
;
1230 t
->lines
= lines
= calloc(rows
, sizeof(Row
));
1233 t
->curattrs
= A_NORMAL
; /* white text over black background */
1234 t
->curfg
= t
->curbg
= -1;
1235 for (Row
*row
= lines
, *end
= lines
+ rows
; row
< end
; row
++) {
1236 row
->cells
= malloc(cols
* sizeof(Cell
));
1238 t
->rows
= row
- lines
;
1241 row_set(row
, 0, cols
, NULL
);
1244 if (scroll_buf_size
< 0)
1245 scroll_buf_size
= 0;
1246 t
->scroll_buf
= scroll_buf
= calloc(scroll_buf_size
, sizeof(Row
));
1247 if (!scroll_buf
&& scroll_buf_size
)
1249 for (Row
*row
= scroll_buf
, *end
= scroll_buf
+ scroll_buf_size
; row
< end
; row
++) {
1250 row
->cells
= calloc(cols
, sizeof(Cell
));
1252 t
->scroll_buf_size
= row
- scroll_buf
;
1256 t
->tabs
= calloc(cols
, sizeof(*t
->tabs
));
1259 for (int col
= 8; col
< cols
; col
+= 8)
1260 t
->tabs
[col
] = true;
1261 t
->curs_row
= lines
;
1263 /* initial scrolling area is the whole window */
1264 t
->scroll_top
= lines
;
1265 t
->scroll_bot
= lines
+ rows
;
1266 t
->scroll_buf_size
= scroll_buf_size
;
1267 t
->maxcols
= t
->cols
= cols
;
1275 Vt
*vt_create(int rows
, int cols
, int scroll_buf_size
)
1279 if (rows
<= 0 || cols
<= 0)
1282 t
= calloc(1, sizeof(Vt
));
1287 t
->deffg
= t
->defbg
= -1;
1288 if (!buffer_init(&t
->buffer_normal
, rows
, cols
, scroll_buf_size
) ||
1289 !buffer_init(&t
->buffer_alternate
, rows
, cols
, 0)) {
1293 t
->buffer
= &t
->buffer_normal
;
1294 t
->copymode_cmd_multiplier
= 0;
1298 static void buffer_resize(Buffer
*t
, int rows
, int cols
)
1300 Row
*lines
= t
->lines
;
1302 if (t
->rows
!= rows
) {
1303 if (t
->curs_row
> lines
+ rows
) {
1304 /* scroll up instead of simply chopping off bottom */
1305 fill_scroll_buf(t
, (t
->curs_row
- t
->lines
) - rows
+ 1);
1307 while (t
->rows
> rows
) {
1308 free(lines
[t
->rows
- 1].cells
);
1312 lines
= realloc(lines
, sizeof(Row
) * rows
);
1315 if (t
->maxcols
< cols
) {
1316 for (int row
= 0; row
< t
->rows
; row
++) {
1317 lines
[row
].cells
= realloc(lines
[row
].cells
, sizeof(Cell
) * cols
);
1319 row_set(lines
+ row
, t
->cols
, cols
- t
->cols
, NULL
);
1320 lines
[row
].dirty
= true;
1322 Row
*sbuf
= t
->scroll_buf
;
1323 for (int row
= 0; row
< t
->scroll_buf_size
; row
++) {
1324 sbuf
[row
].cells
= realloc(sbuf
[row
].cells
, sizeof(Cell
) * cols
);
1326 row_set(sbuf
+ row
, t
->cols
, cols
- t
->cols
, NULL
);
1328 t
->tabs
= realloc(t
->tabs
, sizeof(*t
->tabs
) * cols
);
1329 for (int col
= t
->cols
; col
< cols
; col
++)
1330 t
->tabs
[col
] = !(col
& 7);
1333 } else if (t
->cols
!= cols
) {
1334 for (int row
= 0; row
< t
->rows
; row
++)
1335 lines
[row
].dirty
= true;
1340 if (t
->rows
< rows
) {
1341 while (t
->rows
< rows
) {
1342 lines
[t
->rows
].cells
= calloc(t
->maxcols
, sizeof(Cell
));
1343 row_set(lines
+ t
->rows
, 0, t
->maxcols
, t
);
1347 /* prepare for backfill */
1348 if (t
->curs_row
>= t
->scroll_bot
- 1) {
1349 deltarows
= t
->lines
+ rows
- t
->curs_row
- 1;
1350 if (deltarows
> t
->scroll_amount_above
)
1351 deltarows
= t
->scroll_amount_above
;
1355 t
->curs_row
+= lines
- t
->lines
;
1356 t
->scroll_top
= lines
;
1357 t
->scroll_bot
= lines
+ rows
;
1360 /* perform backfill */
1361 if (deltarows
> 0) {
1362 fill_scroll_buf(t
, -deltarows
);
1363 t
->curs_row
+= deltarows
;
1367 void vt_resize(Vt
*t
, int rows
, int cols
)
1369 struct winsize ws
= { .ws_row
= rows
, .ws_col
= cols
};
1371 if (rows
<= 0 || cols
<= 0)
1376 vt_copymode_leave(t
);
1377 buffer_resize(&t
->buffer_normal
, rows
, cols
);
1378 buffer_resize(&t
->buffer_alternate
, rows
, cols
);
1379 clamp_cursor_to_bounds(t
);
1380 ioctl(t
->pty
, TIOCSWINSZ
, &ws
);
1381 kill(-t
->childpid
, SIGWINCH
);
1384 void vt_destroy(Vt
*t
)
1388 buffer_free(&t
->buffer_normal
);
1389 buffer_free(&t
->buffer_alternate
);
1390 cmdline_free(t
->cmdline
);
1395 void vt_dirty(Vt
*t
)
1397 Buffer
*b
= t
->buffer
;
1398 for (Row
*row
= b
->lines
, *end
= row
+ b
->rows
; row
< end
; row
++)
1402 static void copymode_get_selection_boundry(Vt
*t
, Row
**start_row
, int *start_col
, Row
**end_row
, int *end_col
, bool clip
) {
1403 Buffer
*b
= t
->buffer
;
1404 if (t
->copymode_sel_start_row
>= b
->lines
&& t
->copymode_sel_start_row
< b
->lines
+ b
->rows
) {
1405 /* within the current page */
1406 if (b
->curs_row
>= t
->copymode_sel_start_row
) {
1407 *start_row
= t
->copymode_sel_start_row
;
1408 *end_row
= b
->curs_row
;
1409 *start_col
= t
->copymode_sel_start_col
;
1410 *end_col
= b
->curs_col
;
1412 *start_row
= b
->curs_row
;
1413 *end_row
= t
->copymode_sel_start_row
;
1414 *start_col
= b
->curs_col
;
1415 *end_col
= t
->copymode_sel_start_col
;
1417 if (b
->curs_col
< *start_col
&& *start_row
== *end_row
) {
1418 *start_col
= b
->curs_col
;
1419 *end_col
= t
->copymode_sel_start_col
;
1422 /* part of the scrollback buffer is also selected */
1423 if (t
->copymode_sel_start_row
< b
->lines
) {
1424 /* above the current page */
1426 *start_row
= b
->lines
;
1429 int copied_lines
= b
->lines
- t
->copymode_sel_start_row
;
1430 *start_row
= &b
->scroll_buf
1431 [(b
->scroll_buf_ptr
- copied_lines
+ b
->scroll_buf_size
) % b
->scroll_buf_size
];
1432 *start_col
= t
->copymode_sel_start_col
;
1434 *end_row
= b
->curs_row
;
1435 *end_col
= b
->curs_col
;
1437 /* below the current page */
1438 *start_row
= b
->curs_row
;
1439 *start_col
= b
->curs_col
;
1441 *end_row
= b
->lines
+ b
->rows
;
1442 *end_col
= b
->cols
- 1;
1444 int copied_lines
= t
->copymode_sel_start_row
-(b
->lines
+ b
->rows
);
1445 *end_row
= &b
->scroll_buf
1446 [(b
->scroll_buf_ptr
+ copied_lines
) % b
->scroll_buf_size
];
1447 *end_col
= t
->copymode_sel_start_col
;
1453 void vt_draw(Vt
*t
, WINDOW
* win
, int srow
, int scol
)
1455 Buffer
*b
= t
->buffer
;
1457 Row
*sel_row_start
, *sel_row_end
;
1458 int sel_col_start
, sel_col_end
;
1460 copymode_get_selection_boundry(t
, &sel_row_start
, &sel_col_start
, &sel_row_end
, &sel_col_end
, true);
1462 for (int i
= 0; i
< b
->rows
; i
++) {
1463 Row
*row
= b
->lines
+ i
;
1468 wmove(win
, srow
+ i
, scol
);
1470 for (int j
= 0; j
< b
->cols
; j
++) {
1471 Cell
*prev_cell
= cell
;
1472 cell
= row
->cells
+ j
;
1473 if (!prev_cell
|| cell
->attr
!= prev_cell
->attr
1474 || cell
->fg
!= prev_cell
->fg
1475 || cell
->bg
!= prev_cell
->bg
) {
1476 if (cell
->attr
== A_NORMAL
)
1477 cell
->attr
= t
->defattrs
;
1479 cell
->fg
= t
->deffg
;
1481 cell
->bg
= t
->defbg
;
1482 wattrset(win
, (attr_t
) cell
->attr
<< NCURSES_ATTR_SHIFT
);
1483 wcolor_set(win
, vt_color_get(t
, cell
->fg
, cell
->bg
), NULL
);
1486 if (t
->copymode_selecting
&& ((row
> sel_row_start
&& row
< sel_row_end
) ||
1487 (row
== sel_row_start
&& j
>= sel_col_start
&& (row
!= sel_row_end
|| j
<= sel_col_end
)) ||
1488 (row
== sel_row_end
&& j
<= sel_col_end
&& (row
!= sel_row_start
|| j
>= sel_col_start
)))) {
1489 wattrset(win
, (attr_t
) ((cell
->attr
<< NCURSES_ATTR_SHIFT
)|COPYMODE_ATTR
));
1492 wattrset(win
, (attr_t
) cell
->attr
<< NCURSES_ATTR_SHIFT
);
1493 wcolor_set(win
, vt_color_get(t
, cell
->fg
, cell
->bg
), NULL
);
1497 if (is_utf8
&& cell
->text
>= 128) {
1498 char buf
[MB_CUR_MAX
+ 1];
1499 size_t len
= wcrtomb(buf
, cell
->text
, NULL
);
1501 waddnstr(win
, buf
, len
);
1502 if (wcwidth(cell
->text
) > 1)
1506 waddch(win
, cell
->text
> ' ' ? cell
->text
: ' ');
1513 if (x
&& x
< b
->cols
- 1)
1514 whline(win
, ' ', b
->cols
- x
);
1519 wmove(win
, srow
+ b
->curs_row
- b
->lines
, scol
+ b
->curs_col
);
1521 if (t
->cmdline
&& t
->cmdline
->state
) {
1522 wattrset(win
, t
->defattrs
<< NCURSES_ATTR_SHIFT
);
1523 mvwaddch(win
, srow
+ b
->rows
- 1, 0, t
->cmdline
->prefix
);
1524 whline(win
, ' ', b
->cols
- 1);
1525 if (t
->cmdline
->state
== CMDLINE_ACTIVE
) {
1526 waddnwstr(win
, t
->cmdline
->display
, b
->cols
- 1);
1527 wmove(win
, srow
+ b
->rows
- 1, 1 + t
->cmdline
->cursor_pos
);
1529 wmove(win
, srow
+ b
->rows
- 1, 1);
1533 void vt_scroll(Vt
*t
, int rows
)
1535 Buffer
*b
= t
->buffer
;
1536 if (!b
->scroll_buf_size
)
1538 if (rows
< 0) { /* scroll back */
1539 if (rows
< -b
->scroll_amount_above
)
1540 rows
= -b
->scroll_amount_above
;
1541 } else { /* scroll forward */
1542 if (rows
> b
->scroll_amount_below
)
1543 rows
= b
->scroll_amount_below
;
1545 fill_scroll_buf(b
, rows
);
1546 b
->scroll_amount_below
-= rows
;
1547 if (t
->copymode_selecting
)
1548 t
->copymode_sel_start_row
-= rows
;
1551 void vt_noscroll(Vt
*t
)
1553 int scroll_amount_below
= t
->buffer
->scroll_amount_below
;
1554 if (scroll_amount_below
)
1555 vt_scroll(t
, scroll_amount_below
);
1558 void vt_bell(Vt
*t
, bool bell
)
1563 void vt_togglebell(Vt
*t
)
1568 pid_t
vt_forkpty(Vt
*t
, const char *p
, const char *argv
[], const char *cwd
, const char *env
[], int *pty
)
1572 const char **envp
= env
;
1575 ws
.ws_row
= t
->buffer
->rows
;
1576 ws
.ws_col
= t
->buffer
->cols
;
1577 ws
.ws_xpixel
= ws
.ws_ypixel
= 0;
1579 pid
= forkpty(&t
->pty
, NULL
, NULL
, &ws
);
1586 maxfd
= sysconf(_SC_OPEN_MAX
);
1587 for (fd
= 3; fd
< maxfd
; fd
++)
1588 if (close(fd
) == -1 && errno
== EBADF
)
1591 while (envp
&& envp
[0]) {
1592 setenv(envp
[0], envp
[1], 1);
1595 setenv("TERM", vt_term
, 1);
1598 execv(p
, (char *const *)argv
);
1599 fprintf(stderr
, "\nexecv() failed.\nCommand: '%s'\n", argv
[0]);
1605 return t
->childpid
= pid
;
1608 int vt_getpty(Vt
*t
)
1613 int vt_write(Vt
*t
, const char *buf
, int len
)
1618 int res
= write(t
->pty
, buf
, len
);
1620 if (errno
!= EAGAIN
&& errno
!= EINTR
)
1632 static void send_curs(Vt
*t
)
1634 Buffer
*b
= t
->buffer
;
1636 snprintf(keyseq
, sizeof keyseq
, "\e[%d;%dR", (int)(b
->curs_row
- b
->lines
), b
->curs_col
);
1637 vt_write(t
, keyseq
, strlen(keyseq
));
1640 void vt_keypress(Vt
*t
, int keycode
)
1644 if (keycode
>= 0 && keycode
<= KEY_MAX
&& keytable
[keycode
]) {
1650 char keyseq
[3] = { '\e', (t
->curskeymode
? 'O' : '['), keytable
[keycode
][0] };
1651 vt_write(t
, keyseq
, sizeof keyseq
);
1655 vt_write(t
, keytable
[keycode
], strlen(keytable
[keycode
]));
1657 } else if (keycode
<= UCHAR_MAX
) {
1662 fprintf(stderr
, "unhandled key %#o\n", keycode
);
1667 static Row
*buffer_next_row(Buffer
*t
, Row
*row
, int direction
)
1669 bool has_scroll_buf
= t
->scroll_buf_size
> 0;
1670 Row
*before_start_row
, *before_end_row
, *after_start_row
, *after_end_row
;
1671 Row
*first_row
= t
->lines
;
1672 Row
*last_row
= t
->lines
+ t
->rows
- 1;
1674 if (has_scroll_buf
) {
1675 before_end_row
= &t
->scroll_buf
1676 [(t
->scroll_buf_ptr
- 1 + t
->scroll_buf_size
) % t
->scroll_buf_size
];
1677 before_start_row
= &t
->scroll_buf
1678 [(t
->scroll_buf_ptr
- t
->scroll_amount_above
+ t
->scroll_buf_size
) % t
->scroll_buf_size
];
1679 after_start_row
= &t
->scroll_buf
[t
->scroll_buf_ptr
];
1680 after_end_row
= &t
->scroll_buf
1681 [(t
->scroll_buf_ptr
+ t
->scroll_amount_below
- 1) % t
->scroll_buf_size
];
1684 if (direction
> 0) {
1685 if (row
>= first_row
&& row
< last_row
)
1687 if (row
== last_row
) {
1688 if (has_scroll_buf
) {
1689 if (t
->scroll_amount_below
)
1690 return after_start_row
;
1691 else if (t
->scroll_amount_above
)
1692 return before_start_row
;
1696 if (row
== before_end_row
)
1698 if (row
== after_end_row
)
1699 return t
->scroll_amount_above
? before_start_row
: first_row
;
1700 if (row
== &t
->scroll_buf
[t
->scroll_buf_size
- 1])
1701 return t
->scroll_buf
;
1704 if (row
> first_row
&& row
<= last_row
)
1706 if (row
== first_row
) {
1707 if (has_scroll_buf
) {
1708 if (t
->scroll_amount_above
)
1709 return before_end_row
;
1710 else if (t
->scroll_amount_below
)
1711 return after_end_row
;
1715 if (row
== before_start_row
)
1716 return t
->scroll_amount_below
? after_end_row
: last_row
;
1717 if (row
== after_start_row
)
1719 if (row
== t
->scroll_buf
)
1720 return &t
->scroll_buf
[t
->scroll_buf_size
- 1];
1725 static void row_show(Vt
*t
, Row
*r
)
1727 Buffer
*b
= t
->buffer
;
1728 int below
= b
->scroll_amount_below
;
1729 int above
= b
->scroll_amount_above
;
1730 int ptr
= b
->scroll_buf_ptr
;
1731 int size
= b
->scroll_buf_size
;
1732 int row
= r
- b
->scroll_buf
;
1735 if (b
->lines
<= r
&& r
< b
->lines
+ b
->rows
) {
1744 if (row
- ptr
+ size
< below
)
1745 scroll
= row
- ptr
+ size
+ 1;
1746 else if (ptr
- row
<= above
)
1749 if (row
- ptr
< below
)
1750 scroll
= row
- ptr
+ 1;
1751 else if (ptr
- row
+ size
<= above
)
1752 scroll
= row
- ptr
- size
;
1756 vt_scroll(t
, scroll
);
1757 b
->curs_row
= b
->lines
+ (scroll
> 0 ? b
->rows
- 1 : 0);
1761 static void copymode_search(Vt
*t
, int direction
)
1763 wchar_t *searchbuf
= t
->cmdline
? t
->cmdline
->buf
: NULL
;
1764 if (!searchbuf
|| *searchbuf
== '\0')
1767 Buffer
*b
= t
->buffer
;
1768 /* avoid match at current cursor position */
1769 Row
*start_row
= b
->curs_row
;
1770 int start_col
= b
->curs_col
+ direction
;
1771 if (start_col
>= b
->cols
) {
1773 start_row
= buffer_next_row(b
, start_row
, 1);
1774 } else if (start_col
< 0) {
1775 start_col
= b
->cols
- 1;
1776 start_row
= buffer_next_row(b
, start_row
, -1);
1779 Row
*row
= start_row
, *matched_row
= NULL
;
1780 int matched_col
= 0;
1781 int len
= wcslen(searchbuf
) - 1;
1782 int s_start
= direction
> 0 ? 0 : len
;
1783 int s_end
= direction
> 0 ? len
: 0;
1787 int col
= direction
> 0 ? 0 : b
->cols
- 1;
1788 if (row
== start_row
)
1790 for (; col
>= 0 && col
< b
->cols
; col
+= direction
) {
1791 if (searchbuf
[s
] == row
->cells
[col
].text
) {
1797 b
->curs_col
= matched_col
;
1799 row_show(t
, matched_row
);
1803 int width
= wcwidth(searchbuf
[s
]);
1806 else if (width
>= 1)
1808 col
+= direction
> 0 ? width
: -width
;
1809 } else if (searchbuf
[s_start
] == row
->cells
[col
].text
) {
1810 s
= s_start
+ direction
;
1818 if ((row
= buffer_next_row(b
, row
, direction
)) == start_row
)
1823 static void cmdline_hide_callback(void *t
)
1825 Buffer
*b
= ((Vt
*)t
)->buffer
;
1826 b
->lines
[b
->rows
- 1].dirty
= true;
1829 static void cmdline_show(Cmdline
*c
, char prefix
, int width
, void (*callback
)(void *t
), void *data
)
1833 c
->callback
= callback
;
1834 memset(&c
->ps
, 0, sizeof(mbstate_t));
1837 c
->buf
= calloc(c
->size
, sizeof(wchar_t));
1838 c
->cursor
= c
->end
= c
->display
= c
->buf
;
1842 c
->state
= CMDLINE_INIT
;
1845 c
->width
= width
- (prefix
? 1 : 0);
1848 static void cmdline_hide(Cmdline
*c
)
1852 c
->state
= CMDLINE_INACTIVE
;
1853 c
->callback(c
->data
);
1856 static void cmdline_adjust_cursor_pos(Cmdline
*c
)
1859 for (wchar_t *cur
= c
->display
; cur
< c
->cursor
; cur
++)
1860 pos
+= xwcwidth(*cur
);
1861 int extraspace
= pos
- c
->width
+ 1;
1862 if (extraspace
> 0) {
1863 for (w
= 0; w
< extraspace
; w
+= xwcwidth(*c
->display
++));
1864 c
->cursor_pos
= pos
- w
;
1866 c
->cursor_pos
= pos
;
1869 static void cmdline_keypress(Cmdline
*c
, int keycode
)
1871 if (keycode
!= KEY_UP
&& c
->state
== CMDLINE_INIT
) {
1873 c
->display
= c
->cursor
= c
->end
= c
->buf
;
1876 char keychar
= (char)keycode
;
1880 size_t n
= (c
->end
- c
->cursor
) * sizeof(wchar_t);
1883 if (c
->cursor
== c
->end
) /* n == 0 */
1885 memmove(c
->cursor
, c
->cursor
+ 1, n
- sizeof(wchar_t));
1888 cmdline_adjust_cursor_pos(c
);
1891 if (c
->cursor
== c
->buf
)
1893 memmove(c
->cursor
- 1, c
->cursor
, n
);
1894 if (c
->end
> c
->buf
)
1898 if (c
->cursor
> c
->buf
)
1900 if (c
->cursor_pos
== 0) {
1901 c
->display
-= c
->width
/ 2;
1902 if (c
->display
< c
->buf
)
1903 c
->display
= c
->buf
;
1905 cmdline_adjust_cursor_pos(c
);
1913 c
->display
= c
->cursor
= c
->buf
;
1919 wchar_t *disp
= c
->end
;
1920 while (disp
>= c
->buf
) {
1921 int tmp
= xwcwidth(*disp
);
1922 if (width
+ tmp
> c
->width
- 1)
1927 c
->display
= disp
>= c
->buf
? disp
+ 1: c
->buf
;
1928 c
->cursor_pos
= (width
< c
->width
- 1) ? width
: c
->width
- 1;
1930 case 1 ... 26: /* CTRL('a') ... CTRL('z') */
1931 if (keycode
!= '\n')
1933 copymode_search(c
->data
, c
->prefix
== '/' ? 1 : -1);
1938 len
= (ssize_t
)mbrtowc(&wc
, &keychar
, 1, &c
->ps
);
1944 width
= xwcwidth(wc
);
1946 if (c
->end
- c
->buf
>= c
->size
- 2) {
1948 wchar_t *buf
= realloc(c
->buf
, c
->size
* sizeof(wchar_t));
1951 ptrdiff_t diff
= buf
- c
->buf
;
1958 if (c
->cursor
< c
->end
)
1959 memmove(c
->cursor
+ 1, c
->cursor
, n
);
1961 *(++c
->end
) = L
'\0';
1963 if (c
->cursor_pos
== c
->width
- 1) {
1964 if (c
->cursor
< c
->end
)
1966 if (c
->cursor
!= c
->end
) {
1967 c
->display
+= c
->width
/ 2;
1968 if (c
->display
> c
->end
)
1969 c
->display
= c
->buf
;
1971 cmdline_adjust_cursor_pos(c
);
1974 width
= xwcwidth(*c
->cursor
);
1975 c
->cursor_pos
+= width
;
1976 if (c
->cursor_pos
> c
->width
- 1)
1977 c
->cursor_pos
= c
->width
- 1;
1978 if (c
->cursor
< c
->end
)
1983 c
->state
= CMDLINE_ACTIVE
;
1986 static void cmdline_free(Cmdline
*c
)
1994 void vt_copymode_keypress(Vt
*t
, int keycode
)
1996 Buffer
*b
= t
->buffer
;
1997 Row
*start_row
, *end_row
;
1998 int direction
, col
, start_col
, end_col
, delta
, scroll_page
= b
->rows
/ 2;
1999 char *copybuf
, keychar
= (char)keycode
;
2005 if (t
->cmdline
&& t
->cmdline
->state
) {
2006 cmdline_keypress(t
->cmdline
, keycode
);
2010 t
->copymode_cmd_multiplier
= (t
->copymode_cmd_multiplier
* 10) + (keychar
- '0');
2013 delta
= b
->curs_row
- b
->lines
;
2014 if (delta
> scroll_page
)
2015 b
->curs_row
-= scroll_page
;
2017 b
->curs_row
= b
->lines
;
2018 vt_scroll(t
, delta
- scroll_page
);
2022 delta
= b
->rows
- (b
->curs_row
- b
->lines
);
2023 if (delta
> scroll_page
)
2024 b
->curs_row
+= scroll_page
;
2026 b
->curs_row
= b
->lines
+ b
->rows
- 1;
2027 vt_scroll(t
, scroll_page
- delta
);
2031 if (b
->scroll_amount_above
)
2032 vt_scroll(t
, -b
->scroll_amount_above
);
2035 b
->curs_row
= b
->lines
;
2038 b
->curs_row
= b
->lines
+ (b
->rows
/ 2);
2044 b
->curs_row
= b
->lines
+ b
->rows
- 1;
2052 start_col
= b
->cols
- 1;
2053 for (int i
= 0; i
< b
->cols
; i
++)
2054 if (b
->curs_row
->cells
[i
].text
)
2056 b
->curs_col
= start_col
;
2061 t
->cmdline
= calloc(1, sizeof(Cmdline
));
2062 cmdline_show(t
->cmdline
, keycode
, b
->cols
, cmdline_hide_callback
, t
);
2066 copymode_search(t
, keycode
== 'n' ? 1 : -1);
2069 t
->copymode_selecting
= true;
2070 t
->copymode_sel_start_row
= b
->curs_row
;
2071 t
->copymode_sel_start_col
= b
->curs_col
;
2074 if (!t
->copymode_selecting
) {
2076 t
->copymode_sel_start_row
= b
->curs_row
+
2077 (t
->copymode_cmd_multiplier
? t
->copymode_cmd_multiplier
- 1 : 0);
2078 if (t
->copymode_sel_start_row
>= b
->lines
+ b
->rows
)
2079 t
->copymode_sel_start_row
= b
->lines
+ b
->rows
- 1;
2080 t
->copymode_sel_start_col
= b
->cols
- 1;
2083 copymode_get_selection_boundry(t
, &start_row
, &start_col
, &end_row
, &end_col
, false);
2084 int line_count
= t
->copymode_sel_start_row
> b
->curs_row
?
2085 t
->copymode_sel_start_row
- b
->curs_row
:
2086 b
->curs_row
- t
->copymode_sel_start_row
;
2087 copybuf
= calloc(1, (line_count
+ 1) * b
->cols
* MB_CUR_MAX
+ 1);
2092 memset(&ps
, 0, sizeof(ps
));
2093 Row
*row
= start_row
;
2095 char *last_non_space
= s
;
2096 int j
= (row
== start_row
) ? start_col
: 0;
2097 int col
= (row
== end_row
) ? end_col
: b
->cols
- 1;
2098 for (size_t len
= 0; j
<= col
; j
++) {
2099 if (row
->cells
[j
].text
) {
2100 len
= wcrtomb(s
, row
->cells
[j
].text
, &ps
);
2118 row
= buffer_next_row(b
, row
, 1);
2121 if (t
->event_handler
)
2122 t
->event_handler(t
, VT_EVENT_COPY_TEXT
, copybuf
);
2127 vt_copymode_leave(t
);
2130 for (int c
= 0; c
< (t
->copymode_cmd_multiplier
? t
->copymode_cmd_multiplier
: 1); c
++) {
2137 direction
= (keycode
== 'w' || keycode
== 'W') ? 1 : -1;
2138 start_col
= (direction
> 0) ? 0 : b
->cols
- 1;
2139 end_col
= (direction
> 0) ? b
->cols
- 1 : 0;
2144 if (b
->curs_row
->cells
[col
].text
== ' ') {
2155 while (b
->curs_row
->cells
[col
].text
== ' ') {
2156 if (col
== end_col
) {
2157 b
->curs_row
+= direction
;
2164 b
->curs_row
+= direction
;
2167 if (b
->curs_row
< b
->lines
) {
2168 b
->curs_row
= b
->lines
;
2169 if (b
->scroll_amount_above
)
2175 if (b
->curs_row
>= b
->lines
+ b
->rows
) {
2176 b
->curs_row
= b
->lines
+ b
->rows
- 1;
2177 if (b
->scroll_amount_below
)
2189 if (b
->curs_row
== b
->lines
)
2196 if (b
->curs_row
== b
->lines
+ b
->rows
- 1)
2203 width
= wcwidth(b
->curs_row
->cells
[b
->curs_col
].text
);
2204 b
->curs_col
+= width
< 1 ? 1 : width
;
2205 if (b
->curs_col
>= b
->cols
) {
2206 b
->curs_col
= b
->cols
- 1;
2207 t
->copymode_cmd_multiplier
= 0;
2213 if (b
->curs_col
>= 2 && !b
->curs_row
->cells
[b
->curs_col
-1].text
)
2214 width
= wcwidth(b
->curs_row
->cells
[b
->curs_col
-2].text
);
2215 b
->curs_col
-= width
< 1 ? 1 : width
;
2216 if (b
->curs_col
< 0) {
2218 t
->copymode_cmd_multiplier
= 0;
2226 if (t
->copymode_selecting
)
2228 t
->copymode_cmd_multiplier
= 0;
2231 void vt_mouse(Vt
*t
, int x
, int y
, mmask_t mask
)
2233 #ifdef NCURSES_MOUSE_VERSION
2234 char seq
[6] = { '\e', '[', 'M' }, state
= 0, button
= 0;
2239 if (mask
& (BUTTON1_PRESSED
| BUTTON1_CLICKED
))
2241 else if (mask
& (BUTTON2_PRESSED
| BUTTON2_CLICKED
))
2243 else if (mask
& (BUTTON3_PRESSED
| BUTTON3_CLICKED
))
2245 else if (mask
& (BUTTON1_RELEASED
| BUTTON2_RELEASED
| BUTTON3_RELEASED
))
2248 if (mask
& BUTTON_SHIFT
)
2250 if (mask
& BUTTON_ALT
)
2252 if (mask
& BUTTON_CTRL
)
2255 seq
[3] = 32 + button
+ state
;
2259 vt_write(t
, seq
, sizeof seq
);
2261 if (mask
& (BUTTON1_CLICKED
| BUTTON2_CLICKED
| BUTTON3_CLICKED
)) {
2262 /* send a button release event */
2264 seq
[3] = 32 + button
+ state
;
2265 vt_write(t
, seq
, sizeof seq
);
2267 #endif /* NCURSES_MOUSE_VERSION */
2270 static unsigned int color_hash(short fg
, short bg
)
2276 return fg
* (COLORS
+ 2) + bg
;
2279 short vt_color_get(Vt
*t
, short fg
, short bg
)
2282 fg
= (t
? t
->deffg
: default_fg
);
2284 bg
= (t
? t
->defbg
: default_bg
);
2286 if (!has_default_colors
) {
2288 fg
= (t
&& t
->deffg
!= -1 ? t
->deffg
: default_fg
);
2290 bg
= (t
&& t
->defbg
!= -1 ? t
->defbg
: default_bg
);
2293 if (!color2palette
|| (fg
== -1 && bg
== -1))
2295 unsigned int index
= color_hash(fg
, bg
);
2296 if (color2palette
[index
] == 0) {
2299 if (++color_pair_current
>= color_pairs_max
)
2300 color_pair_current
= color_pairs_reserved
+ 1;
2301 pair_content(color_pair_current
, &oldfg
, &oldbg
);
2302 unsigned int old_index
= color_hash(oldfg
, oldbg
);
2303 if (color2palette
[old_index
] >= 0) {
2304 if (init_pair(color_pair_current
, fg
, bg
) == OK
) {
2305 color2palette
[old_index
] = 0;
2306 color2palette
[index
] = color_pair_current
;
2313 short color_pair
= color2palette
[index
];
2314 return color_pair
>= 0 ? color_pair
: -color_pair
;
2317 short vt_color_reserve(short fg
, short bg
)
2319 if (!color2palette
|| fg
>= COLORS
|| bg
>= COLORS
)
2321 if (!has_default_colors
&& fg
== -1)
2323 if (!has_default_colors
&& bg
== -1)
2325 if (fg
== -1 && bg
== -1)
2327 unsigned int index
= color_hash(fg
, bg
);
2328 if (color2palette
[index
] >= 0) {
2329 if (init_pair(++color_pairs_reserved
, fg
, bg
) == OK
)
2330 color2palette
[index
] = -color_pairs_reserved
;
2332 short color_pair
= color2palette
[index
];
2333 return color_pair
>= 0 ? color_pair
: -color_pair
;
2336 static void init_colors(void)
2338 pair_content(0, &default_fg
, &default_bg
);
2339 if (default_fg
== -1)
2340 default_fg
= COLOR_WHITE
;
2341 if (default_bg
== -1)
2342 default_bg
= COLOR_BLACK
;
2343 has_default_colors
= (use_default_colors() == OK
);
2344 color_pairs_max
= MIN(COLOR_PAIRS
, MAX_COLOR_PAIRS
);
2346 color2palette
= calloc((COLORS
+ 2) * (COLORS
+ 2), sizeof(short));
2347 vt_color_reserve(COLOR_WHITE
, COLOR_BLACK
);
2354 char color_suffix
[] = "-256color";
2355 char *term
= getenv("DVTM_TERM");
2357 strncpy(vt_term
, term
, sizeof(vt_term
) - sizeof(color_suffix
));
2359 strncat(vt_term
, color_suffix
, sizeof(color_suffix
) - 1);
2362 void vt_set_keytable(const char * const keytable_overlay
[], int count
)
2364 for (int k
= 0; k
< count
&& k
< KEY_MAX
; k
++) {
2365 const char *keyseq
= keytable_overlay
[k
];
2367 keytable
[k
] = keyseq
;
2371 void vt_shutdown(void)
2373 free(color2palette
);
2376 void vt_set_escseq_handler(Vt
*t
, vt_escseq_handler_t handler
)
2378 t
->escseq_handler
= handler
;
2381 void vt_set_event_handler(Vt
*t
, vt_event_handler_t handler
)
2383 t
->event_handler
= handler
;
2386 void vt_set_data(Vt
*t
, void *data
)
2391 void *vt_get_data(Vt
*t
)
2396 unsigned vt_cursor(Vt
*t
)
2400 return t
->buffer
->scroll_amount_below
? 0 : !t
->curshid
;
2403 unsigned vt_copymode(Vt
*t
)
2408 void vt_copymode_enter(Vt
*t
)
2412 Buffer
*b
= t
->buffer
;
2413 t
->copymode_curs_srow
= b
->curs_row
- b
->lines
;
2414 t
->copymode_curs_scol
= b
->curs_col
;
2418 void vt_copymode_leave(Vt
*t
)
2422 Buffer
*b
= t
->buffer
;
2423 t
->copymode
= false;
2424 t
->copymode_selecting
= false;
2425 t
->copymode_sel_start_row
= b
->lines
;
2426 t
->copymode_sel_start_col
= 0;
2427 t
->copymode_cmd_multiplier
= 0;
2428 b
->curs_row
= b
->lines
+ t
->copymode_curs_srow
;
2429 b
->curs_col
= t
->copymode_curs_scol
;
2430 cmdline_hide(t
->cmdline
);