3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU Lesser General Public
5 License (LGPL) as published by the Free Software Foundation.
7 Please refer to the COPYING file for more information.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 Copyright © 2004 Bruno T. C. de Oliveira
19 Copyright © 2006 Pierre Habouzit
20 Copyright © 2008 Marc Andre Tanner
33 #include <sys/ioctl.h>
34 #include <sys/types.h>
39 #elif defined(__FreeBSD__)
41 #elif defined(__OpenBSD__) || defined(__NetBSD__)
46 #ifndef NCURSES_ATTR_SHIFT
47 # define NCURSES_ATTR_SHIFT 8
50 #define IS_CONTROL(ch) !((ch) & 0xffffff60UL)
52 static int has_default
, is_utf8
;
56 C0_SOH
, C0_STX
, C0_ETX
, C0_EOT
, C0_ENQ
, C0_ACK
, C0_BEL
,
57 C0_BS
, C0_HT
, C0_LF
, C0_VT
, C0_FF
, C0_CR
, C0_SO
, C0_SI
,
58 C0_DLE
, C0_DC1
, C0_DC2
, D0_DC3
, C0_DC4
, C0_NAK
, C0_SYN
, C0_ETB
,
59 C0_CAN
, C0_EM
, C0_SUB
, C0_ESC
, C0_IS4
, C0_IS3
, C0_IS2
, C0_IS1
,
64 C1_41
, C1_BPH
, C1_NBH
, C1_44
, C1_NEL
, C1_SSA
, C1_ESA
,
65 C1_HTS
, C1_HTJ
, C1_VTS
, C1_PLD
, C1_PLU
, C1_RI
, C1_SS2
, C1_SS3
,
66 C1_DCS
, C1_PU1
, C1_PU2
, C1_STS
, C1_CCH
, C1_MW
, C1_SPA
, C1_EPA
,
67 C1_SOS
, C1_59
, C1_SCI
, C1_CSI
, CS_ST
, C1_OSC
, C1_PM
, C1_APC
,
72 CSI_CUU
, CSI_CUD
, CSI_CUF
, CSI_CUB
, CSI_CNL
, CSI_CPL
, CSI_CHA
,
73 CSI_CUP
, CSI_CHT
, CSI_ED
, CSI_EL
, CSI_IL
, CSI_DL
, CSI_EF
, CSI_EA
,
74 CSI_DCH
, CSI_SEE
, CSI_CPR
, CSI_SU
, CSI_SD
, CSI_NP
, CSI_PP
, CSI_CTC
,
75 CSI_ECH
, CSI_CVT
, CSI_CBT
, CSI_SRS
, CSI_PTX
, CSI_SDS
, CSI_SIMD
, CSI_5F
,
76 CSI_HPA
, CSI_HPR
, CSI_REP
, CSI_DA
, CSI_VPA
, CSI_VPR
, CSI_HVP
, CSI_TBC
,
77 CSI_SM
, CSI_MC
, CSI_HPB
, CSI_VPB
, CSI_RM
, CSI_SGR
, CSI_DSR
, CSI_DAQ
,
78 CSI_70
, CSI_71
, CSI_72
, CSI_73
, CSI_74
, CSI_75
, CSI_76
, CSI_77
,
79 CSI_78
, CSI_79
, CSI_7A
, CSI_7B
, CSI_7C
, CSI_7D
, CSI_7E
, CSI_7F
87 unsigned seen_input
: 1;
90 unsigned graphmode
: 1;
92 unsigned curskeymode
: 1;
98 /* scrollback buffer */
99 struct t_row_t
*scroll_buf
;
105 struct t_row_t
*lines
;
106 struct t_row_t
*scroll_top
;
107 struct t_row_t
*scroll_bot
;
110 struct t_row_t
*curs_row
;
111 int curs_col
, curs_srow
, curs_scol
;
113 /* buffers and parsing state */
119 /* custom escape sequence handler */
120 madtty_handler_t handler
;
124 typedef struct t_row_t
{
130 static char const * const keytable
[KEY_MAX
+1] = {
132 /* for the arrow keys the CSI / SS3 sequences are not stored here
133 * because they depend on the current cursor terminal mode
139 [KEY_BACKSPACE
] = "\177",
140 [KEY_HOME
] = "\e[1~",
144 [KEY_PPAGE
] = "\e[5~",
145 [KEY_NPAGE
] = "\e[6~",
146 [KEY_SUSPEND
] = "\x1A", /* Ctrl+Z gets mapped to this */
147 [KEY_F(1)] = "\e[11~",
148 [KEY_F(2)] = "\e[12~",
149 [KEY_F(3)] = "\e[13~",
150 [KEY_F(4)] = "\e[14~",
151 [KEY_F(5)] = "\e[15~",
152 [KEY_F(6)] = "\e[17~",
153 [KEY_F(7)] = "\e[18~",
154 [KEY_F(8)] = "\e[19~",
155 [KEY_F(9)] = "\e[20~",
156 [KEY_F(10)] = "\e[21~",
157 [KEY_F(11)] = "\e[23~",
158 [KEY_F(12)] = "\e[24~",
159 [KEY_F(13)] = "\e[25~",
160 [KEY_F(14)] = "\e[26~",
161 [KEY_F(15)] = "\e[28~",
162 [KEY_F(16)] = "\e[29~",
163 [KEY_F(17)] = "\e[31~",
164 [KEY_F(18)] = "\e[32~",
165 [KEY_F(19)] = "\e[33~",
166 [KEY_F(20)] = "\e[34~",
169 static void t_row_set(t_row_t
*row
, int start
, int len
, uint16_t attr
)
172 wmemset(row
->text
+ start
, 0, len
);
173 for (int i
= start
; i
< len
+ start
; i
++) {
178 static void t_row_roll(t_row_t
*start
, t_row_t
*end
, int count
)
187 t_row_t
*buf
= alloca(count
* sizeof(t_row_t
));
189 memcpy(buf
, start
, count
* sizeof(t_row_t
));
190 memmove(start
, start
+ count
, (n
- count
) * sizeof(t_row_t
));
191 memcpy(end
- count
, buf
, count
* sizeof(t_row_t
));
192 for (t_row_t
*row
= start
; row
< end
; row
++) {
198 static void clamp_cursor_to_bounds(madtty_t
*t
)
200 if (t
->curs_row
< t
->lines
) {
201 t
->curs_row
= t
->lines
;
203 if (t
->curs_row
>= t
->lines
+ t
->rows
) {
204 t
->curs_row
= t
->lines
+ t
->rows
- 1;
207 if (t
->curs_col
< 0) {
210 if (t
->curs_col
>= t
->cols
) {
211 t
->curs_col
= t
->cols
- 1;
215 static void fill_scroll_buf(madtty_t
*t
, int s
)
217 /* work in screenfuls */
218 int ssz
= t
->scroll_bot
- t
->scroll_top
;
220 fill_scroll_buf(t
, ssz
);
221 fill_scroll_buf(t
, s
-ssz
);
225 fill_scroll_buf(t
, -ssz
);
226 fill_scroll_buf(t
, s
+ ssz
);
230 t
->scroll_buf_len
+= s
;
231 if (t
->scroll_buf_len
>= t
->scroll_buf_sz
)
232 t
->scroll_buf_len
= t
->scroll_buf_sz
;
235 for (int i
= 0; i
< s
; i
++) {
236 struct t_row_t tmp
= t
->scroll_top
[i
];
237 t
->scroll_top
[i
] = t
->scroll_buf
[t
->scroll_buf_ptr
];
238 t
->scroll_buf
[t
->scroll_buf_ptr
] = tmp
;
241 if (t
->scroll_buf_ptr
== t
->scroll_buf_sz
)
242 t
->scroll_buf_ptr
= 0;
245 t_row_roll(t
->scroll_top
, t
->scroll_bot
, s
);
247 for (int i
= (-s
)-1; i
>= 0; i
--) {
249 if (t
->scroll_buf_ptr
== -1)
250 t
->scroll_buf_ptr
= t
->scroll_buf_sz
- 1;
252 struct t_row_t tmp
= t
->scroll_top
[i
];
253 t
->scroll_top
[i
] = t
->scroll_buf
[t
->scroll_buf_ptr
];
254 t
->scroll_buf
[t
->scroll_buf_ptr
] = tmp
;
255 t
->scroll_top
[i
].dirty
= true;
260 static void cursor_line_down(madtty_t
*t
)
263 if (t
->curs_row
< t
->scroll_bot
)
268 t
->curs_row
= t
->scroll_bot
- 1;
269 fill_scroll_buf(t
, 1);
270 t_row_set(t
->curs_row
, 0, t
->cols
, 0);
273 __attribute__((const))
274 static uint16_t build_attrs(unsigned curattrs
)
276 return ((curattrs
& ~A_COLOR
) | COLOR_PAIR(curattrs
& 0xff))
277 >> NCURSES_ATTR_SHIFT
;
280 static void new_escape_sequence(madtty_t
*t
)
287 static void cancel_escape_sequence(madtty_t
*t
)
294 static bool is_valid_csi_ender(int c
)
296 return (c
>= 'a' && c
<= 'z')
297 || (c
>= 'A' && c
<= 'Z')
298 || (c
== '@' || c
== '`');
301 /* interprets a 'set attribute' (SGR) CSI escape sequence */
302 static void interpret_csi_SGR(madtty_t
*t
, int param
[], int pcount
)
307 /* special case: reset attributes */
308 t
->curattrs
= A_NORMAL
;
312 for (i
= 0; i
< pcount
; i
++) {
314 #define CASE(x, op) case x: op; break
315 CASE(0, t
->curattrs
= A_NORMAL
);
316 CASE(1, t
->curattrs
|= A_BOLD
);
317 CASE(4, t
->curattrs
|= A_UNDERLINE
);
318 CASE(5, t
->curattrs
|= A_BLINK
);
319 CASE(6, t
->curattrs
|= A_BLINK
);
320 CASE(7, t
->curattrs
|= A_REVERSE
);
321 CASE(8, t
->curattrs
|= A_INVIS
);
322 CASE(22, t
->curattrs
&= ~A_BOLD
);
323 CASE(24, t
->curattrs
&= ~A_UNDERLINE
);
324 CASE(25, t
->curattrs
&= ~A_BLINK
);
325 CASE(27, t
->curattrs
&= ~A_REVERSE
);
326 CASE(28, t
->curattrs
&= ~A_INVIS
);
328 case 30 ... 37: /* fg */
330 t
->curattrs
&= ~0xf0;
331 t
->curattrs
|= (param
[i
] + 1 - 30) << 4;
334 t
->curattrs
|= (7 - (param
[i
] - 30)) << 3;
339 t
->curattrs
&= has_default
? ~0xf0 : ~070;
342 case 40 ... 47: /* bg */
344 t
->curattrs
&= ~0x0f;
345 t
->curattrs
|= (param
[i
] + 1 - 40);
348 t
->curattrs
|= (param
[i
] - 40);
353 t
->curattrs
&= has_default
? ~0x0f : ~007;
362 /* interprets an 'erase display' (ED) escape sequence */
363 static void interpret_csi_ED(madtty_t
*t
, int param
[], int pcount
)
365 t_row_t
*row
, *start
, *end
;
366 attr_t attr
= build_attrs(t
->curattrs
);
369 if (pcount
&& param
[0] == 2) {
371 end
= t
->lines
+ t
->rows
;
373 if (pcount
&& param
[0] == 1) {
376 t_row_set(t
->curs_row
, 0, t
->curs_col
+ 1, attr
);
378 t_row_set(t
->curs_row
, t
->curs_col
,
379 t
->cols
- t
->curs_col
, attr
);
380 start
= t
->curs_row
+ 1;
381 end
= t
->lines
+ t
->rows
;
384 for (row
= start
; row
< end
; row
++) {
385 t_row_set(row
, 0, t
->cols
, attr
);
389 /* interprets a 'move cursor' (CUP) escape sequence */
390 static void interpret_csi_CUP(madtty_t
*t
, int param
[], int pcount
)
394 t
->curs_row
= t
->lines
;
399 return; /* malformed */
402 t
->curs_row
= t
->lines
+ param
[0] - 1;
403 t
->curs_col
= param
[1] - 1;
405 clamp_cursor_to_bounds(t
);
408 /* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL,
409 * CPL, CHA, HPR, VPA, VPR, HPA */
411 interpret_csi_C(madtty_t
*t
, char verb
, int param
[], int pcount
)
413 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
416 case 'A': t
->curs_row
-= n
; break;
417 case 'B': case 'e': t
->curs_row
+= n
; break;
418 case 'C': case 'a': t
->curs_col
+= n
; break;
419 case 'D': t
->curs_col
-= n
; break;
420 case 'E': t
->curs_row
+= n
; t
->curs_col
= 0; break;
421 case 'F': t
->curs_row
-= n
; t
->curs_col
= 0; break;
422 case 'G': case '`': t
->curs_col
= param
[0] - 1; break;
423 case 'd': t
->curs_row
= t
->lines
+ param
[0] - 1; break;
426 clamp_cursor_to_bounds(t
);
429 /* Interpret the 'erase line' escape sequence */
430 static void interpret_csi_EL(madtty_t
*t
, int param
[], int pcount
)
432 attr_t attr
= build_attrs(t
->curattrs
);
434 switch (pcount
? param
[0] : 0) {
436 t_row_set(t
->curs_row
, 0, t
->curs_col
+ 1, attr
);
439 t_row_set(t
->curs_row
, 0, t
->cols
, attr
);
442 t_row_set(t
->curs_row
, t
->curs_col
, t
->cols
- t
->curs_col
,
448 /* Interpret the 'insert blanks' sequence (ICH) */
449 static void interpret_csi_ICH(madtty_t
*t
, int param
[], int pcount
)
451 t_row_t
*row
= t
->curs_row
;
452 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
455 if (t
->curs_col
+ n
> t
->cols
) {
456 n
= t
->cols
- t
->curs_col
;
459 for (i
= t
->cols
- 1; i
>= t
->curs_col
+ n
; i
--) {
460 row
->text
[i
] = row
->text
[i
- n
];
461 row
->attr
[i
] = row
->attr
[i
- n
];
464 t_row_set(row
, t
->curs_col
, n
, build_attrs(t
->curattrs
));
467 /* Interpret the 'delete chars' sequence (DCH) */
468 static void interpret_csi_DCH(madtty_t
*t
, int param
[], int pcount
)
470 t_row_t
*row
= t
->curs_row
;
471 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
474 if (t
->curs_col
+ n
> t
->cols
) {
475 n
= t
->cols
- t
->curs_col
;
478 for (i
= t
->curs_col
; i
< t
->cols
- n
; i
++) {
479 row
->text
[i
] = row
->text
[i
+ n
];
480 row
->attr
[i
] = row
->attr
[i
+ n
];
483 t_row_set(row
, t
->cols
- n
, n
, build_attrs(t
->curattrs
));
486 /* Interpret a 'scroll reverse' (SR) */
487 static void interpret_csi_SR(madtty_t
*t
)
489 t_row_roll(t
->scroll_top
, t
->scroll_bot
, -1);
490 t_row_set(t
->scroll_top
, 0, t
->cols
, build_attrs(t
->curattrs
));
493 /* Interpret an 'insert line' sequence (IL) */
494 static void interpret_csi_IL(madtty_t
*t
, int param
[], int pcount
)
496 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
498 if (t
->curs_row
+ n
>= t
->scroll_bot
) {
499 for (t_row_t
*row
= t
->curs_row
; row
< t
->scroll_bot
; row
++) {
500 t_row_set(row
, 0, t
->cols
, build_attrs(t
->curattrs
));
503 t_row_roll(t
->curs_row
, t
->scroll_bot
, -n
);
504 for (t_row_t
*row
= t
->curs_row
; row
< t
->curs_row
+ n
; row
++) {
505 t_row_set(row
, 0, t
->cols
, build_attrs(t
->curattrs
));
510 /* Interpret a 'delete line' sequence (DL) */
511 static void interpret_csi_DL(madtty_t
*t
, int param
[], int pcount
)
513 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
515 if (t
->curs_row
+ n
>= t
->scroll_bot
) {
516 for (t_row_t
*row
= t
->curs_row
; row
< t
->scroll_bot
; row
++) {
517 t_row_set(row
, 0, t
->cols
, build_attrs(t
->curattrs
));
520 t_row_roll(t
->curs_row
, t
->scroll_bot
, n
);
521 for (t_row_t
*row
= t
->scroll_bot
- n
; row
< t
->scroll_bot
; row
++) {
522 t_row_set(row
, 0, t
->cols
, build_attrs(t
->curattrs
));
527 /* Interpret an 'erase characters' (ECH) sequence */
528 static void interpret_csi_ECH(madtty_t
*t
, int param
[], int pcount
)
530 int n
= (pcount
&& param
[0] > 0) ? param
[0] : 1;
532 if (t
->curs_col
+ n
< t
->cols
) {
533 n
= t
->cols
- t
->curs_col
;
535 t_row_set(t
->curs_row
, t
->curs_col
, n
, build_attrs(t
->curattrs
));
538 /* Interpret a 'set scrolling region' (DECSTBM) sequence */
539 static void interpret_csi_DECSTBM(madtty_t
*t
, int param
[], int pcount
)
541 int new_top
, new_bot
;
545 t
->scroll_top
= t
->lines
;
546 t
->scroll_bot
= t
->lines
+ t
->rows
;
549 return; /* malformed */
552 new_top
= param
[0] - 1;
555 /* clamp to bounds */
558 if (new_top
>= t
->rows
)
559 new_top
= t
->rows
- 1;
562 if (new_bot
>= t
->rows
)
565 /* check for range validity */
566 if (new_top
< new_bot
) {
567 t
->scroll_top
= t
->lines
+ new_top
;
568 t
->scroll_bot
= t
->lines
+ new_bot
;
574 static void es_interpret_csi(madtty_t
*t
)
576 static int csiparam
[BUFSIZ
];
578 const char *p
= t
->ebuf
+ 1;
579 char verb
= t
->ebuf
[t
->elen
- 1];
581 p
+= t
->ebuf
[1] == '?'; /* CSI private mode */
583 /* parse numeric parameters */
584 while (isdigit((unsigned char)*p
) || *p
== ';') {
586 if (param_count
>= (int)sizeof(csiparam
))
587 return; /* too long! */
588 csiparam
[param_count
++] = 0;
590 if (param_count
== 0) csiparam
[param_count
++] = 0;
591 csiparam
[param_count
- 1] *= 10;
592 csiparam
[param_count
- 1] += *p
- '0';
598 if (t
->ebuf
[1] == '?') {
601 if (csiparam
[0] == 25)
603 if (csiparam
[0] == 1) /* DECCKM: reset ANSI cursor (normal) key mode */
608 if (csiparam
[0] == 25)
610 if (csiparam
[0] == 1) /* DECCKM: set ANSI cursor (application) key mode */
616 /* delegate handling depending on command character (verb) */
619 if (param_count
== 1 && csiparam
[0] == 4) /* insert mode */
623 if (param_count
== 1 && csiparam
[0] == 4) /* replace mode */
626 case 'm': /* it's a 'set attribute' sequence */
627 interpret_csi_SGR(t
, csiparam
, param_count
); break;
628 case 'J': /* it's an 'erase display' sequence */
629 interpret_csi_ED(t
, csiparam
, param_count
); break;
630 case 'H': case 'f': /* it's a 'move cursor' sequence */
631 interpret_csi_CUP(t
, csiparam
, param_count
); break;
632 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
633 case 'e': case 'a': case 'd': case '`':
634 /* it is a 'relative move' */
635 interpret_csi_C(t
, verb
, csiparam
, param_count
); break;
636 case 'K': /* erase line */
637 interpret_csi_EL(t
, csiparam
, param_count
); break;
638 case '@': /* insert characters */
639 interpret_csi_ICH(t
, csiparam
, param_count
); break;
640 case 'P': /* delete characters */
641 interpret_csi_DCH(t
, csiparam
, param_count
); break;
642 case 'L': /* insert lines */
643 interpret_csi_IL(t
, csiparam
, param_count
); break;
644 case 'M': /* delete lines */
645 interpret_csi_DL(t
, csiparam
, param_count
); break;
646 case 'X': /* erase chars */
647 interpret_csi_ECH(t
, csiparam
, param_count
); break;
648 case 'r': /* set scrolling region */
649 interpret_csi_DECSTBM(t
, csiparam
, param_count
); break;
650 case 's': /* save cursor location */
651 t
->curs_srow
= t
->curs_row
- t
->lines
;
652 t
->curs_scol
= t
->curs_col
;
654 case 'u': /* restore cursor location */
655 t
->curs_row
= t
->lines
+ t
->curs_srow
;
656 t
->curs_col
= t
->curs_scol
;
657 clamp_cursor_to_bounds(t
);
664 static void try_interpret_escape_seq(madtty_t
*t
)
666 char lastchar
= t
->ebuf
[t
->elen
-1];
670 switch((*(t
->handler
))(t
, t
->ebuf
)){
671 case MADTTY_HANDLER_OK
:
673 case MADTTY_HANDLER_NOTYET
:
680 cancel_escape_sequence(t
);
689 case ']': /* xterm thing */
690 if (lastchar
== '\a')
698 if (is_valid_csi_ender(lastchar
)) {
700 cancel_escape_sequence(t
);
706 if (t
->elen
+ 1 >= (int)sizeof(t
->ebuf
)) {
709 fprintf(stderr
, "cancelled: \\033");
710 for (int i
= 0; i
< (int)t
->elen
; i
++) {
711 if (isprint(t
->ebuf
[i
])) {
712 fputc(t
->ebuf
[i
], stderr
);
714 fprintf(stderr
, "\\%03o", t
->ebuf
[i
]);
719 cancel_escape_sequence(t
);
723 static void madtty_process_nonprinting(madtty_t
*t
, wchar_t wc
)
727 new_escape_sequence(t
);
731 /* do nothing for now... maybe a visual bell would be nice? */
739 case C0_HT
: /* tab */
740 t
->curs_col
= (t
->curs_col
+ 8) & ~7;
741 if (t
->curs_col
>= t
->cols
)
742 t
->curs_col
= t
->cols
- 1;
755 case C0_SO
: /* shift out - acs */
758 case C0_SI
: /* shift in - acs */
759 t
->graphmode
= false;
764 static void is_utf8_locale(void)
766 const char *cset
= nl_langinfo(CODESET
) ?: "ANSI_X3.4-1968";
767 is_utf8
= !strcmp(cset
, "UTF-8");
770 // vt100 special graphics and line drawing
771 // 5f-7e standard vt100
772 // 40-5e rxvt extension for extra curses acs chars
773 static uint16_t const vt100_utf8
[62] = { // 41 .. 7e
774 0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259a, 0x2603, // 41-47 hi mr. snowman!
775 0, 0, 0, 0, 0, 0, 0, 0, // 48-4f
776 0, 0, 0, 0, 0, 0, 0, 0, // 50-57
777 0, 0, 0, 0, 0, 0, 0, 0x0020, // 58-5f
778 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, // 60-67
779 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, // 68-6f
780 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, // 70-77
781 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e
784 static uint32_t vt100
[62];
786 void madtty_init_vt100_graphics(void)
788 vt100
['l' - 0x41] = ACS_ULCORNER
;
789 vt100
['m' - 0x41] = ACS_LLCORNER
;
790 vt100
['k' - 0x41] = ACS_URCORNER
;
791 vt100
['j' - 0x41] = ACS_LRCORNER
;
792 vt100
['u' - 0x41] = ACS_RTEE
;
793 vt100
['t' - 0x41] = ACS_LTEE
;
794 vt100
['v' - 0x41] = ACS_TTEE
;
795 vt100
['w' - 0x41] = ACS_BTEE
;
796 vt100
['q' - 0x41] = ACS_HLINE
;
797 vt100
['x' - 0x41] = ACS_VLINE
;
798 vt100
['n' - 0x41] = ACS_PLUS
;
799 vt100
['o' - 0x41] = ACS_S1
;
800 vt100
['s' - 0x41] = ACS_S9
;
801 vt100
['`' - 0x41] = ACS_DIAMOND
;
802 vt100
['a' - 0x41] = ACS_CKBOARD
;
803 vt100
['f' - 0x41] = ACS_DEGREE
;
804 vt100
['g' - 0x41] = ACS_PLMINUS
;
805 vt100
['~' - 0x41] = ACS_BULLET
;
806 #if 0 /* out of bounds */
807 vt100
[',' - 0x41] = ACS_LARROW
;
808 vt100
['+' - 0x41] = ACS_RARROW
;
809 vt100
['.' - 0x41] = ACS_DARROW
;
810 vt100
['-' - 0x41] = ACS_UARROW
;
811 vt100
['0' - 0x41] = ACS_BLOCK
;
813 vt100
['h' - 0x41] = ACS_BOARD
;
814 vt100
['i' - 0x41] = ACS_LANTERN
;
815 /* these defaults were invented for ncurses */
816 vt100
['p' - 0x41] = ACS_S3
;
817 vt100
['r' - 0x41] = ACS_S7
;
818 vt100
['y' - 0x41] = ACS_LEQUAL
;
819 vt100
['z' - 0x41] = ACS_GEQUAL
;
820 vt100
['{' - 0x41] = ACS_PI
;
821 vt100
['|' - 0x41] = ACS_NEQUAL
;
822 vt100
['}' - 0x41] = ACS_STERLING
;
826 static void madtty_putc(madtty_t
*t
, wchar_t wc
)
830 if (!t
->seen_input
) {
832 kill(-t
->childpid
, SIGWINCH
);
836 assert (t
->elen
+ 1 < (int)sizeof(t
->ebuf
));
837 t
->ebuf
[t
->elen
] = wc
;
838 t
->ebuf
[++t
->elen
] = '\0';
839 try_interpret_escape_seq(t
);
840 } else if (IS_CONTROL(wc
)) {
841 madtty_process_nonprinting(t
, wc
);
846 if (wc
>= 0x41 && wc
<= 0x7e) {
847 wchar_t gc
= is_utf8
? vt100_utf8
[wc
- 0x41] : vt100
[wc
- 0x41];
853 width
= wcwidth(wc
) ?: 1;
856 if (width
== 2 && t
->curs_col
== t
->cols
- 1) {
859 tmp
->text
[t
->curs_col
] = 0;
860 tmp
->attr
[t
->curs_col
] = build_attrs(t
->curattrs
);
864 if (t
->curs_col
>= t
->cols
) {
873 wmemmove(tmp
->text
+ t
->curs_col
+ width
, tmp
->text
+ t
->curs_col
,
874 (t
->cols
- t
->curs_col
- width
));
875 memmove(tmp
->attr
+ t
->curs_col
+ width
, tmp
->attr
+ t
->curs_col
,
876 (t
->cols
- t
->curs_col
- width
) * sizeof(tmp
->attr
[0]));
879 tmp
->text
[t
->curs_col
] = wc
;
880 tmp
->attr
[t
->curs_col
] = build_attrs(t
->curattrs
);
883 tmp
->text
[t
->curs_col
] = 0;
884 tmp
->attr
[t
->curs_col
] = build_attrs(t
->curattrs
);
890 int madtty_process(madtty_t
*t
)
899 res
= read(t
->pty
, t
->rbuf
+ t
->rlen
, sizeof(t
->rbuf
) - t
->rlen
);
904 while (pos
< t
->rlen
) {
908 len
= (ssize_t
)mbrtowc(&wc
, t
->rbuf
+ pos
, t
->rlen
- pos
, &t
->ps
);
911 memmove(t
->rbuf
, t
->rbuf
+ pos
, t
->rlen
);
920 pos
+= len
? len
: 1;
925 memmove(t
->rbuf
, t
->rbuf
+ pos
, t
->rlen
);
929 madtty_t
*madtty_create(int rows
, int cols
)
934 if (rows
<= 0 || cols
<= 0)
937 t
= (madtty_t
*)calloc(sizeof(madtty_t
), 1);
941 /* record dimensions */
945 /* default mode is replace */
948 /* create the cell matrix */
949 t
->lines
= (t_row_t
*)calloc(sizeof(t_row_t
), t
->rows
);
950 for (i
= 0; i
< t
->rows
; i
++) {
951 t
->lines
[i
].text
= (wchar_t *)calloc(sizeof(wchar_t), t
->cols
);
952 t
->lines
[i
].attr
= (uint16_t *)calloc(sizeof(uint16_t), t
->cols
);
955 t
->pty
= -1; /* no pty for now */
957 /* initialization of other public fields */
958 t
->curs_row
= t
->lines
;
960 t
->curattrs
= A_NORMAL
; /* white text over black background */
962 /* initial scrolling area is the whole window */
963 t
->scroll_top
= t
->lines
;
964 t
->scroll_bot
= t
->lines
+ t
->rows
;
966 /* scrollback buffer */
967 t
->scroll_buf_sz
= 1000;
968 t
->scroll_buf
= calloc(sizeof(t_row_t
), t
->scroll_buf_sz
);
969 for (i
= 0; i
< t
->scroll_buf_sz
; i
++) {
970 t
->scroll_buf
[i
].text
= calloc(sizeof(wchar_t), t
->cols
);
971 t
->scroll_buf
[i
].attr
= calloc(sizeof(uint16_t), t
->cols
);
973 t
->scroll_buf_ptr
= t
->scroll_buf_len
= 0;
974 t
->scroll_amount
= 0;
978 void madtty_resize(madtty_t
*t
, int rows
, int cols
)
980 struct winsize ws
= { .ws_row
= rows
, .ws_col
= cols
};
981 t_row_t
*lines
= t
->lines
;
983 if (rows
<= 0 || cols
<= 0)
988 if (t
->rows
!= rows
) {
989 if (t
->curs_row
> lines
+rows
) {
990 /* scroll up instead of simply chopping off bottom */
991 fill_scroll_buf(t
, t
->rows
- rows
);
993 while (t
->rows
> rows
) {
994 free(lines
[t
->rows
- 1].text
);
995 free(lines
[t
->rows
- 1].attr
);
999 lines
= realloc(lines
, sizeof(t_row_t
) * rows
);
1002 if (t
->cols
!= cols
) {
1003 for (int row
= 0; row
< t
->rows
; row
++) {
1004 lines
[row
].text
= realloc(lines
[row
].text
, sizeof(wchar_t) * cols
);
1005 lines
[row
].attr
= realloc(lines
[row
].attr
, sizeof(uint16_t) * cols
);
1007 t_row_set(lines
+ row
, t
->cols
, cols
- t
->cols
, 0);
1009 lines
[row
].dirty
= true;
1011 t_row_t
*sbuf
= t
->scroll_buf
;
1012 for (int row
= 0; row
< t
->scroll_buf_sz
; row
++) {
1013 sbuf
[row
].text
= realloc(sbuf
[row
].text
, sizeof(wchar_t) * cols
);
1014 sbuf
[row
].attr
= realloc(sbuf
[row
].attr
, sizeof(uint16_t) * cols
);
1016 t_row_set(sbuf
+ row
, t
->cols
, cols
- t
->cols
, 0);
1021 while (t
->rows
< rows
) {
1022 lines
[t
->rows
].text
= (wchar_t *)calloc(sizeof(wchar_t), cols
);
1023 lines
[t
->rows
].attr
= (uint16_t *)calloc(sizeof(uint16_t), cols
);
1024 t_row_set(lines
+ t
->rows
, 0, t
->cols
, 0);
1028 t
->curs_row
+= lines
- t
->lines
;
1029 t
->scroll_top
= lines
;
1030 t
->scroll_bot
= lines
+ rows
;
1032 clamp_cursor_to_bounds(t
);
1033 ioctl(t
->pty
, TIOCSWINSZ
, &ws
);
1034 kill(-t
->childpid
, SIGWINCH
);
1037 void madtty_destroy(madtty_t
*t
)
1043 for (i
= 0; i
< t
->rows
; i
++) {
1044 free(t
->lines
[i
].text
);
1045 free(t
->lines
[i
].attr
);
1048 for (i
= 0; i
< t
->scroll_buf_sz
; i
++) {
1049 free(t
->scroll_buf
[i
].text
);
1050 free(t
->scroll_buf
[i
].attr
);
1052 free(t
->scroll_buf
);
1056 void madtty_dirty(madtty_t
*t
)
1058 for (int i
= 0; i
< t
->rows
; i
++)
1059 t
->lines
[i
].dirty
= true;
1062 void madtty_draw(madtty_t
*t
, WINDOW
*win
, int srow
, int scol
)
1065 for (int i
= 0; i
< t
->rows
; i
++) {
1066 t_row_t
*row
= t
->lines
+ i
;
1072 wmove(win
, srow
+ i
, scol
);
1073 for (int j
= 0; j
< t
->cols
; j
++) {
1074 if (!j
|| row
->attr
[j
] != row
->attr
[j
- 1])
1075 wattrset(win
, (attr_t
)row
->attr
[j
] << NCURSES_ATTR_SHIFT
);
1076 if (is_utf8
&& row
->text
[j
] >= 128) {
1077 char buf
[MB_CUR_MAX
+ 1];
1080 len
= wcrtomb(buf
, row
->text
[j
], NULL
);
1081 waddnstr(win
, buf
, len
);
1082 if (wcwidth(row
->text
[j
]) > 1)
1085 waddch(win
, row
->text
[j
] > ' ' ? row
->text
[j
] : ' ');
1091 wmove(win
, srow
+ t
->curs_row
- t
->lines
, scol
+ t
->curs_col
);
1092 curs_set(madtty_cursor(t
));
1095 void madtty_scroll(madtty_t
*t
, int rows
)
1099 if (rows
< -t
->scroll_buf_len
)
1100 rows
= -t
->scroll_buf_len
;
1102 /* scroll forward */
1103 if (rows
> t
->scroll_amount
)
1104 rows
= t
->scroll_amount
;
1106 fill_scroll_buf(t
, rows
);
1107 t
->scroll_amount
-= rows
;
1110 void madtty_noscroll(madtty_t
*t
)
1112 if (t
->scroll_amount
)
1113 madtty_scroll(t
, t
->scroll_amount
);
1116 /******************************************************/
1118 pid_t
madtty_forkpty(madtty_t
*t
, const char *p
, const char *argv
[], const char *env
[], int *pty
)
1122 const char **envp
= env
;
1124 ws
.ws_row
= t
->rows
;
1125 ws
.ws_col
= t
->cols
;
1126 ws
.ws_xpixel
= ws
.ws_ypixel
= 0;
1128 pid
= forkpty(&t
->pty
, NULL
, NULL
, &ws
);
1134 while (envp
&& envp
[0]) {
1135 setenv(envp
[0], envp
[1], 1);
1138 setenv("TERM", "rxvt", 1);
1139 execv(p
, (char *const*)argv
);
1140 fprintf(stderr
, "\nexecv() failed.\nCommand: '%s'\n", argv
[0]);
1146 return t
->childpid
= pid
;
1149 int madtty_getpty(madtty_t
*t
)
1154 static void term_write(madtty_t
*t
, const char *buf
, int len
)
1157 int res
= write(t
->pty
, buf
, len
);
1158 if (res
< 0 && errno
!= EAGAIN
&& errno
!= EINTR
)
1166 void madtty_keypress(madtty_t
*t
, int keycode
)
1168 char c
= (char)keycode
;
1172 if (keycode
>= 0 && keycode
< KEY_MAX
&& keytable
[keycode
]) {
1178 char keyseq
[3] = { '\e', (t
->curskeymode
? 'O' : '['), keytable
[keycode
][0] };
1179 term_write(t
, keyseq
, 3);
1183 term_write(t
, keytable
[keycode
], strlen(keytable
[keycode
]));
1186 term_write(t
, &c
, 1);
1189 void madtty_keypress_sequence(madtty_t
*t
, const char *seq
)
1191 int key
, len
= strlen(seq
);
1192 /* check for function keys from putty, this makes the
1193 * keypad work but it's probably not the right way to
1194 * do it. the sequence we look for is \eO + a character
1195 * representing the number.
1197 if(len
== 3 && seq
[0] == '\e' && seq
[1] == 'O') {
1199 if(key
>= '0' && key
<= '9')
1200 madtty_keypress(t
, key
);
1202 term_write(t
, seq
, len
);
1205 void madtty_init_colors(void)
1207 if (COLOR_PAIRS
> 64) {
1208 use_default_colors();
1211 for (int bg
= -1; bg
< 8; bg
++) {
1212 for (int fg
= -1; fg
< 8; fg
++) {
1213 init_pair((fg
+ 1) * 16 + bg
+ 1, fg
, bg
);
1217 int use_default
= use_default_colors() == OK
;
1218 for (int bg
= 0; bg
< 8; bg
++) {
1219 for (int fg
= 0; fg
< 8; fg
++) {
1221 init_pair((7 - fg
) * 8 + bg
,
1222 fg
== COLOR_WHITE
? -1 : fg
,
1223 bg
== COLOR_BLACK
? -1 : bg
);
1225 init_pair((7 - fg
) * 8 + bg
, fg
, bg
);
1232 int madtty_color_pair(int fg
, int bg
)
1239 return COLOR_PAIR((fg
+ 1) * 16 + bg
+ 1);
1245 return COLOR_PAIR((7 - fg
) * 8 + bg
);
1248 void madtty_set_handler(madtty_t
*t
, madtty_handler_t handler
)
1250 t
->handler
= handler
;
1253 void madtty_set_data(madtty_t
*t
, void *data
)
1258 void *madtty_get_data(madtty_t
*t
)
1263 unsigned madtty_cursor(madtty_t
*t
)
1265 return t
->scroll_amount
? 0 : !t
->curshid
;