vt: factor out buffer handling
[dvtm.git] / vt.c
blob18425cd39a6791fbac4fd31f7c79502132ccfbc5
1 /*
2 * Copyright © 2004 Bruno T. C. de Oliveira
3 * Copyright © 2006 Pierre Habouzit
4 * Copyright © 2008-2012 Marc Andre 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.
19 #define _GNU_SOURCE
20 #include <ctype.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <langinfo.h>
24 #include <signal.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29 #include <sys/types.h>
30 #include <termios.h>
31 #include <wchar.h>
32 #if defined(__linux__) || defined(__CYGWIN__)
33 # include <pty.h>
34 #elif defined(__FreeBSD__)
35 # include <libutil.h>
36 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
37 # include <util.h>
38 #endif
39 #if defined(__CYGWIN__) || defined(_AIX)
40 # include <alloca.h>
41 #endif
43 #include "vt.h"
45 #ifdef _AIX
46 # include "forkpty-aix.c"
47 #endif
49 #ifndef NCURSES_ATTR_SHIFT
50 # define NCURSES_ATTR_SHIFT 8
51 #endif
53 #ifndef NCURSES_ACS
54 # ifdef PDCURSES
55 # define NCURSES_ACS(c) (acs_map[(unsigned char)(c)])
56 # else /* BSD curses */
57 # define NCURSES_ACS(c) (_acs_map[(unsigned char)(c)])
58 # endif
59 #endif
61 #ifdef NCURSES_VERSION
62 # ifndef NCURSES_EXT_COLORS
63 # define NCURSES_EXT_COLORS 0
64 # endif
65 # if !NCURSES_EXT_COLORS
66 # define MAX_COLOR_PAIRS 256
67 # endif
68 #endif
69 #ifndef MAX_COLOR_PAIRS
70 # define MAX_COLOR_PAIRS COLOR_PAIRS
71 #endif
73 #define IS_CONTROL(ch) !((ch) & 0xffffff60UL)
74 #define MIN(x, y) ((x) < (y) ? (x) : (y))
75 #define sstrlen(str) (sizeof(str) - 1)
77 static bool is_utf8, has_default_colors;
78 static short color_pairs_reserved, color_pairs_max, color_pair_current;
79 static short *color2palette, default_fg, default_bg;
81 enum {
82 C0_NUL = 0x00,
83 C0_SOH, C0_STX, C0_ETX, C0_EOT, C0_ENQ, C0_ACK, C0_BEL,
84 C0_BS, C0_HT, C0_LF, C0_VT, C0_FF, C0_CR, C0_SO, C0_SI,
85 C0_DLE, C0_DC1, C0_DC2, D0_DC3, C0_DC4, C0_NAK, C0_SYN, C0_ETB,
86 C0_CAN, C0_EM, C0_SUB, C0_ESC, C0_IS4, C0_IS3, C0_IS2, C0_IS1,
89 enum {
90 C1_40 = 0x40,
91 C1_41, C1_BPH, C1_NBH, C1_44, C1_NEL, C1_SSA, C1_ESA,
92 C1_HTS, C1_HTJ, C1_VTS, C1_PLD, C1_PLU, C1_RI, C1_SS2, C1_SS3,
93 C1_DCS, C1_PU1, C1_PU2, C1_STS, C1_CCH, C1_MW, C1_SPA, C1_EPA,
94 C1_SOS, C1_59, C1_SCI, C1_CSI, CS_ST, C1_OSC, C1_PM, C1_APC,
97 enum {
98 CSI_ICH = 0x40,
99 CSI_CUU, CSI_CUD, CSI_CUF, CSI_CUB, CSI_CNL, CSI_CPL, CSI_CHA,
100 CSI_CUP, CSI_CHT, CSI_ED, CSI_EL, CSI_IL, CSI_DL, CSI_EF, CSI_EA,
101 CSI_DCH, CSI_SEE, CSI_CPR, CSI_SU, CSI_SD, CSI_NP, CSI_PP, CSI_CTC,
102 CSI_ECH, CSI_CVT, CSI_CBT, CSI_SRS, CSI_PTX, CSI_SDS, CSI_SIMD, CSI_5F,
103 CSI_HPA, CSI_HPR, CSI_REP, CSI_DA, CSI_VPA, CSI_VPR, CSI_HVP, CSI_TBC,
104 CSI_SM, CSI_MC, CSI_HPB, CSI_VPB, CSI_RM, CSI_SGR, CSI_DSR, CSI_DAQ,
105 CSI_70, CSI_71, CSI_72, CSI_73, CSI_74, CSI_75, CSI_76, CSI_77,
106 CSI_78, CSI_79, CSI_7A, CSI_7B, CSI_7C, CSI_7D, CSI_7E, CSI_7F
109 typedef struct {
110 wchar_t text;
111 uint16_t attr;
112 short fg;
113 short bg;
114 } Cell;
116 typedef struct {
117 Cell *cells;
118 unsigned dirty:1;
119 } Row;
121 typedef struct {
122 Row *lines;
123 Row *curs_row;
124 Row *scroll_buf;
125 Row *scroll_top;
126 Row *scroll_bot;
127 int scroll_buf_sz;
128 int scroll_buf_ptr;
129 int scroll_buf_len;
130 int scroll_amount;
131 int rows, cols, maxcols;
132 unsigned curattrs, savattrs;
133 int curs_col, curs_srow, curs_scol;
134 short curfg, curbg, savfg, savbg;
135 } Buffer;
137 struct Vt {
138 Buffer buffer_normal;
139 Buffer *buffer;
140 unsigned defattrs;
141 short deffg, defbg;
142 int pty;
143 pid_t childpid;
145 /* flags */
146 unsigned seen_input:1;
147 unsigned insert:1;
148 unsigned escaped:1;
149 unsigned curshid:1;
150 unsigned curskeymode:1;
151 unsigned bell:1;
152 unsigned relposmode:1;
153 unsigned mousetrack:1;
154 unsigned graphmode:1;
155 bool charsets[2];
157 /* buffers and parsing state */
158 mbstate_t ps;
159 char rbuf[BUFSIZ];
160 char ebuf[BUFSIZ];
161 unsigned int rlen, elen;
163 /* xterm style window title */
164 char title[256];
166 vt_event_handler_t event_handler;
168 /* custom escape sequence handler */
169 vt_escseq_handler_t escseq_handler;
170 void *data;
173 static char const * const keytable[KEY_MAX+1] = {
174 ['\n'] = "\r",
175 /* for the arrow keys the CSI / SS3 sequences are not stored here
176 * because they depend on the current cursor terminal mode
178 [KEY_UP] = "A",
179 [KEY_DOWN] = "B",
180 [KEY_RIGHT] = "C",
181 [KEY_LEFT] = "D",
182 #ifdef KEY_SUP
183 [KEY_SUP] = "\e[1;2A",
184 #endif
185 #ifdef KEY_SDOWN
186 [KEY_SDOWN] = "\e[1;2B",
187 #endif
188 [KEY_SRIGHT] = "\e[1;2C",
189 [KEY_SLEFT] = "\e[1;2D",
190 [KEY_BACKSPACE] = "\177",
191 [KEY_IC] = "\e[2~",
192 [KEY_DC] = "\e[3~",
193 [KEY_PPAGE] = "\e[5~",
194 [KEY_NPAGE] = "\e[6~",
195 [KEY_HOME] = "\e[7~",
196 [KEY_END] = "\e[8~",
197 [KEY_SUSPEND] = "\x1A", /* Ctrl+Z gets mapped to this */
198 [KEY_F(1)] = "\e[11~",
199 [KEY_F(2)] = "\e[12~",
200 [KEY_F(3)] = "\e[13~",
201 [KEY_F(4)] = "\e[14~",
202 [KEY_F(5)] = "\e[15~",
203 [KEY_F(6)] = "\e[17~",
204 [KEY_F(7)] = "\e[18~",
205 [KEY_F(8)] = "\e[19~",
206 [KEY_F(9)] = "\e[20~",
207 [KEY_F(10)] = "\e[21~",
208 [KEY_F(11)] = "\e[23~",
209 [KEY_F(12)] = "\e[24~",
210 [KEY_F(13)] = "\e[25~",
211 [KEY_F(14)] = "\e[26~",
212 [KEY_F(15)] = "\e[28~",
213 [KEY_F(16)] = "\e[29~",
214 [KEY_F(17)] = "\e[31~",
215 [KEY_F(18)] = "\e[32~",
216 [KEY_F(19)] = "\e[33~",
217 [KEY_F(20)] = "\e[34~",
218 [KEY_RESIZE] = "",
221 static void process_nonprinting(Vt *t, wchar_t wc);
222 static void send_curs(Vt *t);
224 __attribute__ ((const))
225 static uint16_t build_attrs(unsigned curattrs)
227 return ((curattrs & ~A_COLOR) | COLOR_PAIR(curattrs & 0xff))
228 >> NCURSES_ATTR_SHIFT;
231 static void row_set(Row *row, int start, int len, Buffer *t)
233 Cell cell = {
234 .text = L'\0',
235 .attr = t ? build_attrs(t->curattrs) : 0,
236 .fg = t ? t->curfg : -1,
237 .bg = t ? t->curbg : -1,
240 for (int i = start; i < len + start; i++)
241 row->cells[i] = cell;
242 row->dirty = true;
245 static void row_roll(Row *start, Row *end, int count)
247 int n = end - start;
249 count %= n;
250 if (count < 0)
251 count += n;
253 if (count) {
254 Row *buf = alloca(count * sizeof(Row));
256 memcpy(buf, start, count * sizeof(Row));
257 memmove(start, start + count, (n - count) * sizeof(Row));
258 memcpy(end - count, buf, count * sizeof(Row));
259 for (Row *row = start; row < end; row++)
260 row->dirty = true;
264 static void clamp_cursor_to_bounds(Vt *vt)
266 Buffer *t = vt->buffer;
267 Row *lines = vt->relposmode ? t->scroll_top : t->lines;
268 int rows = vt->relposmode ? t->scroll_bot - t->scroll_top : t->rows;
270 if (t->curs_row < lines)
271 t->curs_row = lines;
272 if (t->curs_row >= lines + rows)
273 t->curs_row = lines + rows - 1;
274 if (t->curs_col < 0)
275 t->curs_col = 0;
276 if (t->curs_col >= t->cols)
277 t->curs_col = t->cols - 1;
280 static void save_curs(Vt *vt)
282 Buffer *t = vt->buffer;
283 t->curs_srow = t->curs_row - t->lines;
284 t->curs_scol = t->curs_col;
287 static void restore_curs(Vt *vt)
289 Buffer *t = vt->buffer;
290 t->curs_row = t->lines + t->curs_srow;
291 t->curs_col = t->curs_scol;
292 clamp_cursor_to_bounds(vt);
295 static void save_attrs(Vt *vt)
297 Buffer *t = vt->buffer;
298 t->savattrs = t->curattrs;
299 t->savfg = t->curfg;
300 t->savbg = t->curbg;
303 static void restore_attrs(Vt *vt)
305 Buffer *t = vt->buffer;
306 t->curattrs = t->savattrs;
307 t->curfg = t->savfg;
308 t->curbg = t->savbg;
311 static void fill_scroll_buf(Buffer *t, int s)
313 /* work in screenfuls */
314 int ssz = t->scroll_bot - t->scroll_top;
315 if (s > ssz) {
316 fill_scroll_buf(t, ssz);
317 fill_scroll_buf(t, s - ssz);
318 return;
320 if (s < -ssz) {
321 fill_scroll_buf(t, -ssz);
322 fill_scroll_buf(t, s + ssz);
323 return;
326 t->scroll_buf_len += s;
327 if (t->scroll_buf_len >= t->scroll_buf_sz)
328 t->scroll_buf_len = t->scroll_buf_sz;
330 if (s > 0 && t->scroll_buf_sz) {
331 for (int i = 0; i < s; i++) {
332 Row tmp = t->scroll_top[i];
333 t->scroll_top[i] = t->scroll_buf[t->scroll_buf_ptr];
334 t->scroll_buf[t->scroll_buf_ptr] = tmp;
336 t->scroll_buf_ptr++;
337 if (t->scroll_buf_ptr == t->scroll_buf_sz)
338 t->scroll_buf_ptr = 0;
341 row_roll(t->scroll_top, t->scroll_bot, s);
342 if (s < 0 && t->scroll_buf_sz) {
343 for (int i = (-s) - 1; i >= 0; i--) {
344 t->scroll_buf_ptr--;
345 if (t->scroll_buf_ptr == -1)
346 t->scroll_buf_ptr = t->scroll_buf_sz - 1;
348 Row tmp = t->scroll_top[i];
349 t->scroll_top[i] = t->scroll_buf[t->scroll_buf_ptr];
350 t->scroll_buf[t->scroll_buf_ptr] = tmp;
351 t->scroll_top[i].dirty = true;
356 static void cursor_line_down(Vt *vt)
358 Buffer *t = vt->buffer;
359 row_set(t->curs_row, t->cols, t->maxcols - t->cols, 0);
360 t->curs_row++;
361 if (t->curs_row < t->scroll_bot)
362 return;
364 vt_noscroll(vt);
366 t->curs_row = t->scroll_bot - 1;
367 fill_scroll_buf(t, 1);
368 row_set(t->curs_row, 0, t->cols, t);
371 static void new_escape_sequence(Vt *t)
373 t->escaped = true;
374 t->elen = 0;
375 t->ebuf[0] = '\0';
378 static void cancel_escape_sequence(Vt *t)
380 t->escaped = false;
381 t->elen = 0;
382 t->ebuf[0] = '\0';
385 static bool is_valid_csi_ender(int c)
387 return (c >= 'a' && c <= 'z')
388 || (c >= 'A' && c <= 'Z')
389 || (c == '@' || c == '`');
392 /* interprets a 'set attribute' (SGR) CSI escape sequence */
393 static void interpret_csi_SGR(Vt *vt, int param[], int pcount)
395 Buffer *t = vt->buffer;
396 if (pcount == 0) {
397 /* special case: reset attributes */
398 t->curattrs = A_NORMAL;
399 t->curfg = t->curbg = -1;
400 return;
403 for (int i = 0; i < pcount; i++) {
404 switch (param[i]) {
405 case 0:
406 t->curattrs = A_NORMAL;
407 t->curfg = t->curbg = -1;
408 break;
409 case 1:
410 t->curattrs |= A_BOLD;
411 break;
412 case 4:
413 t->curattrs |= A_UNDERLINE;
414 break;
415 case 5:
416 t->curattrs |= A_BLINK;
417 break;
418 case 7:
419 t->curattrs |= A_REVERSE;
420 break;
421 case 8:
422 t->curattrs |= A_INVIS;
423 break;
424 case 22:
425 t->curattrs &= ~A_BOLD;
426 break;
427 case 24:
428 t->curattrs &= ~A_UNDERLINE;
429 break;
430 case 25:
431 t->curattrs &= ~A_BLINK;
432 break;
433 case 27:
434 t->curattrs &= ~A_REVERSE;
435 break;
436 case 28:
437 t->curattrs &= ~A_INVIS;
438 break;
439 case 30 ... 37: /* fg */
440 t->curfg = param[i] - 30;
441 break;
442 case 38:
443 if ((i + 2) < pcount && param[i + 1] == 5) {
444 t->curfg = param[i + 2];
445 i += 2;
447 break;
448 case 39:
449 t->curfg = -1;
450 break;
451 case 40 ... 47: /* bg */
452 t->curbg = param[i] - 40;
453 break;
454 case 48:
455 if ((i + 2) < pcount && param[i + 1] == 5) {
456 t->curbg = param[i + 2];
457 i += 2;
459 break;
460 case 49:
461 t->curbg = -1;
462 break;
463 case 90 ... 97: /* hi fg */
464 t->curfg = param[i] - 82;
465 break;
466 case 100 ... 107: /* hi bg */
467 t->curbg = param[i] - 92;
468 break;
469 default:
470 break;
475 /* interprets an 'erase display' (ED) escape sequence */
476 static void interpret_csi_ED(Vt *vt, int param[], int pcount)
478 Row *row, *start, *end;
479 Buffer *t = vt->buffer;
481 save_attrs(vt);
482 t->curattrs = A_NORMAL;
483 t->curfg = t->curbg = -1;
485 if (pcount && param[0] == 2) {
486 start = t->lines;
487 end = t->lines + t->rows;
488 } else if (pcount && param[0] == 1) {
489 start = t->lines;
490 end = t->curs_row;
491 row_set(t->curs_row, 0, t->curs_col + 1, t);
492 } else {
493 row_set(t->curs_row, t->curs_col, t->cols - t->curs_col, t);
494 start = t->curs_row + 1;
495 end = t->lines + t->rows;
498 for (row = start; row < end; row++)
499 row_set(row, 0, t->cols, t);
501 restore_attrs(vt);
504 /* interprets a 'move cursor' (CUP) escape sequence */
505 static void interpret_csi_CUP(Vt *vt, int param[], int pcount)
507 Buffer *t = vt->buffer;
508 Row *lines = vt->relposmode ? t->scroll_top : t->lines;
510 if (pcount == 0) {
511 t->curs_row = lines;
512 t->curs_col = 0;
513 } else if (pcount == 1) {
514 t->curs_row = lines + param[0] - 1;
515 t->curs_col = 0;
516 } else {
517 t->curs_row = lines + param[0] - 1;
518 t->curs_col = param[1] - 1;
521 clamp_cursor_to_bounds(vt);
524 /* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL,
525 * CPL, CHA, HPR, VPA, VPR, HPA */
526 static void interpret_csi_C(Vt *vt, char verb, int param[], int pcount)
528 Buffer *t = vt->buffer;
529 int n = (pcount && param[0] > 0) ? param[0] : 1;
531 switch (verb) {
532 case 'A':
533 t->curs_row -= n;
534 break;
535 case 'B':
536 case 'e':
537 t->curs_row += n;
538 break;
539 case 'C':
540 case 'a':
541 t->curs_col += n;
542 break;
543 case 'D':
544 t->curs_col -= n;
545 break;
546 case 'E':
547 t->curs_row += n;
548 t->curs_col = 0;
549 break;
550 case 'F':
551 t->curs_row -= n;
552 t->curs_col = 0;
553 break;
554 case 'G':
555 case '`':
556 t->curs_col = param[0] - 1;
557 break;
558 case 'd':
559 t->curs_row = t->lines + param[0] - 1;
560 break;
563 clamp_cursor_to_bounds(vt);
566 /* Interpret the 'erase line' escape sequence */
567 static void interpret_csi_EL(Vt *vt, int param[], int pcount)
569 Buffer *t = vt->buffer;
570 switch (pcount ? param[0] : 0) {
571 case 1:
572 row_set(t->curs_row, 0, t->curs_col + 1, t);
573 break;
574 case 2:
575 row_set(t->curs_row, 0, t->cols, t);
576 break;
577 default:
578 row_set(t->curs_row, t->curs_col, t->cols - t->curs_col, t);
579 break;
583 /* Interpret the 'insert blanks' sequence (ICH) */
584 static void interpret_csi_ICH(Vt *vt, int param[], int pcount)
586 Buffer *t = vt->buffer;
587 Row *row = t->curs_row;
588 int n = (pcount && param[0] > 0) ? param[0] : 1;
590 if (t->curs_col + n > t->cols)
591 n = t->cols - t->curs_col;
593 for (int i = t->cols - 1; i >= t->curs_col + n; i--)
594 row->cells[i] = row->cells[i - n];
596 row_set(row, t->curs_col, n, t);
599 /* Interpret the 'delete chars' sequence (DCH) */
600 static void interpret_csi_DCH(Vt *vt, int param[], int pcount)
602 Buffer *t = vt->buffer;
603 Row *row = t->curs_row;
604 int n = (pcount && param[0] > 0) ? param[0] : 1;
606 if (t->curs_col + n > t->cols)
607 n = t->cols - t->curs_col;
609 for (int i = t->curs_col; i < t->cols - n; i++)
610 row->cells[i] = row->cells[i + n];
612 row_set(row, t->cols - n, n, t);
615 /* Interpret an 'insert line' sequence (IL) */
616 static void interpret_csi_IL(Vt *vt, int param[], int pcount)
618 Buffer *t = vt->buffer;
619 int n = (pcount && param[0] > 0) ? param[0] : 1;
621 if (t->curs_row + n >= t->scroll_bot) {
622 for (Row *row = t->curs_row; row < t->scroll_bot; row++)
623 row_set(row, 0, t->cols, t);
624 } else {
625 row_roll(t->curs_row, t->scroll_bot, -n);
626 for (Row *row = t->curs_row; row < t->curs_row + n; row++)
627 row_set(row, 0, t->cols, t);
631 /* Interpret a 'delete line' sequence (DL) */
632 static void interpret_csi_DL(Vt *vt, int param[], int pcount)
634 Buffer *t = vt->buffer;
635 int n = (pcount && param[0] > 0) ? param[0] : 1;
637 if (t->curs_row + n >= t->scroll_bot) {
638 for (Row *row = t->curs_row; row < t->scroll_bot; row++)
639 row_set(row, 0, t->cols, t);
640 } else {
641 row_roll(t->curs_row, t->scroll_bot, n);
642 for (Row *row = t->scroll_bot - n; row < t->scroll_bot; row++)
643 row_set(row, 0, t->cols, t);
647 /* Interpret an 'erase characters' (ECH) sequence */
648 static void interpret_csi_ECH(Vt *vt, int param[], int pcount)
650 Buffer *t = vt->buffer;
651 int n = (pcount && param[0] > 0) ? param[0] : 1;
653 if (t->curs_col + n > t->cols)
654 n = t->cols - t->curs_col;
656 row_set(t->curs_row, t->curs_col, n, t);
659 /* Interpret a 'set scrolling region' (DECSTBM) sequence */
660 static void interpret_csi_DECSTBM(Vt *vt, int param[], int pcount)
662 Buffer *t = vt->buffer;
663 int new_top, new_bot;
665 switch (pcount) {
666 case 0:
667 t->scroll_top = t->lines;
668 t->scroll_bot = t->lines + t->rows;
669 break;
670 case 2:
671 new_top = param[0] - 1;
672 new_bot = param[1];
674 /* clamp to bounds */
675 if (new_top < 0)
676 new_top = 0;
677 if (new_top >= t->rows)
678 new_top = t->rows - 1;
679 if (new_bot < 0)
680 new_bot = 0;
681 if (new_bot >= t->rows)
682 new_bot = t->rows;
684 /* check for range validity */
685 if (new_top < new_bot) {
686 t->scroll_top = t->lines + new_top;
687 t->scroll_bot = t->lines + new_bot;
689 break;
690 default:
691 return; /* malformed */
695 static void es_interpret_csi(Vt *t)
697 static int csiparam[BUFSIZ];
698 int param_count = 0;
699 const char *p = t->ebuf + 1;
700 char verb = t->ebuf[t->elen - 1];
702 /* parse numeric parameters */
703 for (p += (t->ebuf[1] == '?'); *p; p++) {
704 if (IS_CONTROL(*p)) {
705 process_nonprinting(t, *p);
706 } else if (*p == ';') {
707 if (param_count >= (int)sizeof(csiparam))
708 return; /* too long! */
709 csiparam[param_count++] = 0;
710 } else if (isdigit((unsigned char)*p)) {
711 if (param_count == 0)
712 csiparam[param_count++] = 0;
713 csiparam[param_count - 1] *= 10;
714 csiparam[param_count - 1] += *p - '0';
718 if (t->ebuf[1] == '?') {
719 if (verb == 'h') { /* DEC Private Mode Set (DECSET) */
720 switch (csiparam[0]) {
721 case 1: /* set ANSI cursor (application) key mode (DECCKM) */
722 t->curskeymode = true;
723 break;
724 case 6: /* set origin to relative (DECOM) */
725 t->relposmode = true;
726 break;
727 case 25: /* make cursor visible (DECCM) */
728 t->curshid = false;
729 break;
730 case 47: /* use alternate screen buffer */
731 t->buffer->curattrs = A_NORMAL;
732 t->buffer->curfg = t->buffer->curbg = -1;
733 break;
734 case 1000: /* enable normal mouse tracking */
735 t->mousetrack = true;
736 break;
738 } else if (verb == 'l') { /* DEC Private Mode Reset (DECRST) */
739 switch (csiparam[0]) {
740 case 1: /* reset ANSI cursor (normal) key mode (DECCKM) */
741 t->curskeymode = false;
742 break;
743 case 6: /* set origin to absolute (DECOM) */
744 t->relposmode = false;
745 break;
746 case 25: /* make cursor visible (DECCM) */
747 t->curshid = true;
748 break;
749 case 47: /* use normal screen buffer */
750 t->buffer->curattrs = A_NORMAL;
751 t->buffer->curfg = t->buffer->curbg = -1;
752 break;
753 case 1000: /* disable normal mouse tracking */
754 t->mousetrack = false;
755 break;
760 /* delegate handling depending on command character (verb) */
761 switch (verb) {
762 case 'h':
763 if (param_count == 1 && csiparam[0] == 4) /* insert mode */
764 t->insert = true;
765 break;
766 case 'l':
767 if (param_count == 1 && csiparam[0] == 4) /* replace mode */
768 t->insert = false;
769 break;
770 case 'm': /* it's a 'set attribute' sequence */
771 interpret_csi_SGR(t, csiparam, param_count);
772 break;
773 case 'J': /* it's an 'erase display' sequence */
774 interpret_csi_ED(t, csiparam, param_count);
775 break;
776 case 'H':
777 case 'f': /* it's a 'move cursor' sequence */
778 interpret_csi_CUP(t, csiparam, param_count);
779 break;
780 case 'A':
781 case 'B':
782 case 'C':
783 case 'D':
784 case 'E':
785 case 'F':
786 case 'G':
787 case 'e':
788 case 'a':
789 case 'd':
790 case '`':
791 /* it is a 'relative move' */
792 interpret_csi_C(t, verb, csiparam, param_count);
793 break;
794 case 'K': /* erase line */
795 interpret_csi_EL(t, csiparam, param_count);
796 break;
797 case '@': /* insert characters */
798 interpret_csi_ICH(t, csiparam, param_count);
799 break;
800 case 'P': /* delete characters */
801 interpret_csi_DCH(t, csiparam, param_count);
802 break;
803 case 'L': /* insert lines */
804 interpret_csi_IL(t, csiparam, param_count);
805 break;
806 case 'M': /* delete lines */
807 interpret_csi_DL(t, csiparam, param_count);
808 break;
809 case 'X': /* erase chars */
810 interpret_csi_ECH(t, csiparam, param_count);
811 break;
812 case 'r': /* set scrolling region */
813 interpret_csi_DECSTBM(t, csiparam, param_count);
814 break;
815 case 's': /* save cursor location */
816 save_curs(t);
817 break;
818 case 'u': /* restore cursor location */
819 restore_curs(t);
820 break;
821 case 'n': /* query cursor location */
822 if (param_count == 1 && csiparam[0] == 6)
823 send_curs(t);
824 break;
825 default:
826 break;
830 /* Interpret an 'index' (IND) sequence */
831 static void interpret_esc_IND(Vt *vt)
833 Buffer *t = vt->buffer;
834 if (t->curs_row < t->lines + t->rows - 1)
835 t->curs_row++;
838 /* Interpret a 'reverse index' (RI) sequence */
839 static void interpret_esc_RI(Vt *vt)
841 Buffer *t = vt->buffer;
842 if (t->curs_row > t->lines)
843 t->curs_row--;
844 else {
845 row_roll(t->scroll_top, t->scroll_bot, -1);
846 row_set(t->scroll_top, 0, t->cols, t);
850 /* Interpret a 'next line' (NEL) sequence */
851 static void interpret_esc_NEL(Vt *vt)
853 Buffer *t = vt->buffer;
854 if (t->curs_row < t->lines + t->rows - 1) {
855 t->curs_row++;
856 t->curs_col = 0;
860 /* Interpret a 'select character set' (SCS) sequence */
861 static void interpret_esc_SCS(Vt *t)
863 /* ESC ( sets G0, ESC ) sets G1 */
864 t->charsets[! !(t->ebuf[0] == ')')] = (t->ebuf[1] == '0');
865 t->graphmode = t->charsets[0];
868 /* Interpret xterm specific escape sequences */
869 static void interpret_esc_xterm(Vt *t)
871 /* ESC]n;dataBEL -- the ESC is not part of t->ebuf */
872 char *title = NULL;
874 switch (t->ebuf[1]) {
875 case '0':
876 case '2':
877 t->ebuf[t->elen - 1] = '\0';
878 if (t->elen > sstrlen("]n;\a"))
879 title = t->ebuf + sstrlen("]n;");
881 if (t->event_handler)
882 t->event_handler(t, VT_EVENT_TITLE, title);
886 static void try_interpret_escape_seq(Vt *t)
888 char lastchar = t->ebuf[t->elen - 1];
890 if (!*t->ebuf)
891 return;
893 if (t->escseq_handler) {
894 switch ((*(t->escseq_handler)) (t, t->ebuf)) {
895 case VT_ESCSEQ_HANDLER_OK:
896 cancel_escape_sequence(t);
897 return;
898 case VT_ESCSEQ_HANDLER_NOTYET:
899 if (t->elen + 1 >= sizeof(t->ebuf))
900 goto cancel;
901 return;
905 switch (*t->ebuf) {
906 case '#': /* ignore DECDHL, DECSWL, DECDWL, DECHCP, DECALN, DECFPP */
907 if (t->elen == 2) {
908 cancel_escape_sequence(t);
909 return;
911 break;
912 case '(':
913 case ')':
914 if (t->elen == 2) {
915 interpret_esc_SCS(t);
916 cancel_escape_sequence(t);
917 return;
919 break;
920 case ']': /* xterm thing */
921 if (lastchar == '\a' ||
922 (lastchar == '\\' && t->elen >= 2 && t->ebuf[t->elen - 2] == '\e')) {
923 interpret_esc_xterm(t);
924 cancel_escape_sequence(t);
925 return;
927 break;
928 case '[':
929 if (is_valid_csi_ender(lastchar)) {
930 es_interpret_csi(t);
931 cancel_escape_sequence(t);
932 return;
934 break;
935 case '7': /* DECSC: save cursor and attributes */
936 save_attrs(t);
937 save_curs(t);
938 cancel_escape_sequence(t);
939 return;
940 case '8': /* DECRC: restore cursor and attributes */
941 restore_attrs(t);
942 restore_curs(t);
943 cancel_escape_sequence(t);
944 return;
945 case 'D': /* IND: index */
946 interpret_esc_IND(t);
947 cancel_escape_sequence(t);
948 return;
949 case 'M': /* RI: reverse index */
950 interpret_esc_RI(t);
951 cancel_escape_sequence(t);
952 return;
953 case 'E': /* NEL: next line */
954 interpret_esc_NEL(t);
955 cancel_escape_sequence(t);
956 return;
957 default:
958 goto cancel;
961 if (t->elen + 1 >= sizeof(t->ebuf)) {
962 cancel:
963 #ifndef NDEBUG
964 fprintf(stderr, "cancelled: \\033");
965 for (unsigned int i = 0; i < t->elen; i++) {
966 if (isprint(t->ebuf[i])) {
967 fputc(t->ebuf[i], stderr);
968 } else {
969 fprintf(stderr, "\\%03o", t->ebuf[i]);
972 fputc('\n', stderr);
973 #endif
974 cancel_escape_sequence(t);
978 static void process_nonprinting(Vt *vt, wchar_t wc)
980 Buffer *t = vt->buffer;
981 switch (wc) {
982 case C0_ESC:
983 new_escape_sequence(vt);
984 break;
985 case C0_BEL:
986 if (vt->bell)
987 beep();
988 break;
989 case C0_BS:
990 if (t->curs_col > 0)
991 t->curs_col--;
992 break;
993 case C0_HT: /* tab */
994 t->curs_col = (t->curs_col + 8) & ~7;
995 if (t->curs_col >= t->cols)
996 t->curs_col = t->cols - 1;
997 break;
998 case C0_CR:
999 t->curs_col = 0;
1000 break;
1001 case C0_VT:
1002 case C0_FF:
1003 case C0_LF:
1004 cursor_line_down(vt);
1005 break;
1006 case C0_SO: /* shift out, invoke the G1 character set */
1007 vt->graphmode = vt->charsets[1];
1008 break;
1009 case C0_SI: /* shift in, invoke the G0 character set */
1010 vt->graphmode = vt->charsets[0];
1011 break;
1015 static void is_utf8_locale(void)
1017 const char *cset = nl_langinfo(CODESET) ? : "ANSI_X3.4-1968";
1018 is_utf8 = !strcmp(cset, "UTF-8");
1021 static wchar_t get_vt100_graphic(char c)
1023 static char vt100_acs[] = "`afgjklmnopqrstuvwxyz{|}~";
1026 * 5f-7e standard vt100
1027 * 40-5e rxvt extension for extra curses acs chars
1029 static uint16_t const vt100_utf8[62] = {
1030 0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259a, 0x2603, // 41-47
1031 0, 0, 0, 0, 0, 0, 0, 0, // 48-4f
1032 0, 0, 0, 0, 0, 0, 0, 0, // 50-57
1033 0, 0, 0, 0, 0, 0, 0, 0x0020, // 58-5f
1034 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, // 60-67
1035 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, // 68-6f
1036 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, // 70-77
1037 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e
1040 if (is_utf8)
1041 return vt100_utf8[c - 0x41];
1042 else if (strchr(vt100_acs, c))
1043 return NCURSES_ACS(c);
1044 return '\0';
1047 static void put_wc(Vt *t, wchar_t wc)
1049 int width = 0;
1051 if (!t->seen_input) {
1052 t->seen_input = 1;
1053 kill(-t->childpid, SIGWINCH);
1056 if (t->escaped) {
1057 if (t->elen + 1 < sizeof(t->ebuf)) {
1058 t->ebuf[t->elen] = wc;
1059 t->ebuf[++t->elen] = '\0';
1060 try_interpret_escape_seq(t);
1061 } else {
1062 cancel_escape_sequence(t);
1064 } else if (IS_CONTROL(wc)) {
1065 process_nonprinting(t, wc);
1066 } else {
1067 if (t->graphmode) {
1068 if (wc >= 0x41 && wc <= 0x7e) {
1069 wchar_t gc = get_vt100_graphic(wc);
1070 if (gc)
1071 wc = gc;
1073 width = 1;
1074 } else if ((width = wcwidth(wc)) < 1) {
1075 width = 1;
1077 Buffer *b = t->buffer;
1078 Cell blank_cell = { L'\0', build_attrs(b->curattrs), b->curfg, b->curbg };
1079 if (width == 2 && b->curs_col == b->cols - 1) {
1080 b->curs_row->cells[b->curs_col++] = blank_cell;
1081 b->curs_row->dirty = true;
1084 if (b->curs_col >= b->cols) {
1085 b->curs_col = 0;
1086 cursor_line_down(t);
1089 if (t->insert) {
1090 Cell *src = b->curs_row->cells + b->curs_col;
1091 Cell *dest = src + width;
1092 size_t len = b->cols - b->curs_col - width;
1093 memmove(dest, src, len);
1096 b->curs_row->cells[b->curs_col] = blank_cell;
1097 b->curs_row->cells[b->curs_col++].text = wc;
1098 b->curs_row->dirty = true;
1099 if (width == 2)
1100 b->curs_row->cells[b->curs_col++] = blank_cell;
1104 int vt_process(Vt *t)
1106 int res;
1107 unsigned int pos = 0;
1109 if (t->pty < 0) {
1110 errno = EINVAL;
1111 return -1;
1114 res = read(t->pty, t->rbuf + t->rlen, sizeof(t->rbuf) - t->rlen);
1115 if (res < 0)
1116 return -1;
1118 t->rlen += res;
1119 while (pos < t->rlen) {
1120 wchar_t wc;
1121 ssize_t len;
1123 len = (ssize_t)mbrtowc(&wc, t->rbuf + pos, t->rlen - pos, &t->ps);
1124 if (len == -2) {
1125 t->rlen -= pos;
1126 memmove(t->rbuf, t->rbuf + pos, t->rlen);
1127 return 0;
1130 if (len == -1) {
1131 len = 1;
1132 wc = t->rbuf[pos];
1135 pos += len ? len : 1;
1136 put_wc(t, wc);
1139 t->rlen -= pos;
1140 memmove(t->rbuf, t->rbuf + pos, t->rlen);
1141 return 0;
1144 void vt_set_default_colors(Vt *t, unsigned attrs, short fg, short bg)
1146 t->defattrs = attrs;
1147 t->deffg = fg;
1148 t->defbg = bg;
1151 void buffer_init(Buffer *t, int rows, int cols, int scroll_buf_sz)
1153 Row *lines, *scroll_buf;
1154 t->rows = rows;
1155 t->maxcols = t->cols = cols;
1156 t->curattrs = A_NORMAL; /* white text over black background */
1157 t->curfg = t->curbg = -1;
1158 t->lines = lines = calloc(t->rows, sizeof(Row));
1159 for (Row *row = lines, *end = lines + rows; row < end; row++)
1160 row->cells = calloc(cols, sizeof(Cell));
1161 t->curs_row = lines;
1162 t->curs_col = 0;
1163 /* initial scrolling area is the whole window */
1164 t->scroll_top = lines;
1165 t->scroll_bot = lines + rows;
1166 if (scroll_buf_sz < 0)
1167 scroll_buf_sz = 0;
1168 t->scroll_buf_sz = scroll_buf_sz;
1169 t->scroll_buf = scroll_buf = calloc(scroll_buf_sz, sizeof(Row));
1170 for (Row *row = scroll_buf, *end = scroll_buf + scroll_buf_sz; row < end; row++)
1171 row->cells = calloc(cols, sizeof(Cell));
1174 Vt *vt_create(int rows, int cols, int scroll_buf_sz)
1176 Vt *t;
1178 if (rows <= 0 || cols <= 0)
1179 return NULL;
1181 t = calloc(1, sizeof(Vt));
1182 if (!t)
1183 return NULL;
1185 t->pty = -1;
1186 t->deffg = t->defbg = -1;
1187 buffer_init(&t->buffer_normal, rows, cols, scroll_buf_sz);
1188 t->buffer = &t->buffer_normal;
1189 return t;
1192 void buffer_resize(Buffer *t, int rows, int cols)
1194 Row *lines = t->lines;
1196 if (t->rows != rows) {
1197 if (t->curs_row > lines + rows) {
1198 /* scroll up instead of simply chopping off bottom */
1199 fill_scroll_buf(t, (t->curs_row - t->lines) - rows + 1);
1201 while (t->rows > rows) {
1202 free(lines[t->rows - 1].cells);
1203 t->rows--;
1206 lines = realloc(lines, sizeof(Row) * rows);
1209 if (t->maxcols < cols) {
1210 for (int row = 0; row < t->rows; row++) {
1211 lines[row].cells = realloc(lines[row].cells, sizeof(Cell) * cols);
1212 if (t->cols < cols)
1213 row_set(lines + row, t->cols, cols - t->cols, 0);
1214 lines[row].dirty = true;
1216 Row *sbuf = t->scroll_buf;
1217 for (int row = 0; row < t->scroll_buf_sz; row++) {
1218 sbuf[row].cells = realloc(sbuf[row].cells, sizeof(Cell) * cols);
1219 if (t->cols < cols)
1220 row_set(sbuf + row, t->cols, cols - t->cols, 0);
1222 t->maxcols = cols;
1223 t->cols = cols;
1224 } else if (t->cols != cols) {
1225 for (int row = 0; row < t->rows; row++)
1226 lines[row].dirty = true;
1227 t->cols = cols;
1230 int deltarows = 0;
1231 if (t->rows < rows) {
1232 while (t->rows < rows) {
1233 lines[t->rows].cells = calloc(t->maxcols, sizeof(Cell));
1234 row_set(lines + t->rows, 0, t->maxcols, t);
1235 t->rows++;
1238 /* prepare for backfill */
1239 if (t->curs_row >= t->scroll_bot - 1) {
1240 deltarows = t->lines + rows - t->curs_row - 1;
1241 if (deltarows > t->scroll_buf_len)
1242 deltarows = t->scroll_buf_len;
1246 t->curs_row += lines - t->lines;
1247 t->scroll_top = lines;
1248 t->scroll_bot = lines + rows;
1249 t->lines = lines;
1251 /* perform backfill */
1252 if (deltarows > 0) {
1253 fill_scroll_buf(t, -deltarows);
1254 t->curs_row += deltarows;
1258 void vt_resize(Vt *t, int rows, int cols)
1260 struct winsize ws = {.ws_row = rows,.ws_col = cols };
1262 if (rows <= 0 || cols <= 0)
1263 return;
1265 vt_noscroll(t);
1266 buffer_resize(t->buffer, rows, cols);
1267 clamp_cursor_to_bounds(t);
1268 ioctl(t->pty, TIOCSWINSZ, &ws);
1269 kill(-t->childpid, SIGWINCH);
1272 void buffer_free(Buffer *t)
1274 for (int i = 0; i < t->rows; i++)
1275 free(t->lines[i].cells);
1276 free(t->lines);
1277 for (int i = 0; i < t->scroll_buf_sz; i++)
1278 free(t->scroll_buf[i].cells);
1279 free(t->scroll_buf);
1282 void vt_destroy(Vt *t)
1284 if (!t)
1285 return;
1286 buffer_free(&t->buffer_normal);
1287 free(t);
1290 void vt_dirty(Vt *vt)
1292 Buffer *t = vt->buffer;
1293 for (Row *row = t->lines, *end = row + t->rows; row < end; row++)
1294 row->dirty = true;
1297 void vt_draw(Vt *vt, WINDOW * win, int srow, int scol)
1299 Buffer *t = vt->buffer;
1300 curs_set(0);
1301 for (int i = 0; i < t->rows; i++) {
1302 Row *row = t->lines + i;
1304 if (!row->dirty)
1305 continue;
1307 wmove(win, srow + i, scol);
1308 Cell *cell = NULL;
1309 for (int j = 0; j < t->cols; j++) {
1310 Cell *prev_cell = cell;
1311 cell = row->cells + j;
1312 if (!prev_cell || cell->attr != prev_cell->attr
1313 || cell->fg != prev_cell->fg
1314 || cell->bg != prev_cell->bg) {
1315 if (cell->attr == A_NORMAL)
1316 cell->attr = vt->defattrs;
1317 if (cell->fg == -1)
1318 cell->fg = vt->deffg;
1319 if (cell->bg == -1)
1320 cell->bg = vt->defbg;
1321 wattrset(win, (attr_t) cell->attr << NCURSES_ATTR_SHIFT);
1322 wcolor_set(win, vt_color_get(vt, cell->fg, cell->bg), NULL);
1324 if (is_utf8 && cell->text >= 128) {
1325 char buf[MB_CUR_MAX + 1];
1326 int len = wcrtomb(buf, cell->text, NULL);
1327 waddnstr(win, buf, len);
1328 if (wcwidth(cell->text) > 1)
1329 j++;
1330 } else {
1331 waddch(win, cell->text > ' ' ? cell->text : ' ');
1334 row->dirty = false;
1337 wmove(win, srow + t->curs_row - t->lines, scol + t->curs_col);
1338 curs_set(vt_cursor(vt));
1341 void vt_scroll(Vt *vt, int rows)
1343 Buffer *t = vt->buffer;
1344 if (rows < 0) { /* scroll back */
1345 if (rows < -t->scroll_buf_len)
1346 rows = -t->scroll_buf_len;
1347 } else { /* scroll forward */
1348 if (rows > t->scroll_amount)
1349 rows = t->scroll_amount;
1351 fill_scroll_buf(t, rows);
1352 t->scroll_amount -= rows;
1355 void vt_noscroll(Vt *t)
1357 int scroll_amount = t->buffer->scroll_amount;
1358 if (scroll_amount)
1359 vt_scroll(t, scroll_amount);
1362 void vt_bell(Vt *t, bool bell)
1364 t->bell = bell;
1367 void vt_togglebell(Vt *t)
1369 t->bell = !t->bell;
1372 pid_t vt_forkpty(Vt *t, const char *p, const char *argv[], const char *env[], int *pty)
1374 struct winsize ws;
1375 pid_t pid;
1376 const char **envp = env;
1377 int fd, maxfd;
1379 ws.ws_row = t->buffer->rows;
1380 ws.ws_col = t->buffer->cols;
1381 ws.ws_xpixel = ws.ws_ypixel = 0;
1383 pid = forkpty(&t->pty, NULL, NULL, &ws);
1384 if (pid < 0)
1385 return -1;
1387 if (pid == 0) {
1388 setsid();
1390 maxfd = sysconf(_SC_OPEN_MAX);
1391 for (fd = 3; fd < maxfd; fd++)
1392 if (close(fd) == -1 && errno == EBADF)
1393 break;
1395 while (envp && envp[0]) {
1396 setenv(envp[0], envp[1], 1);
1397 envp += 2;
1399 setenv("TERM", COLORS >= 256 ? "rxvt-256color" : "rxvt", 1);
1400 execv(p, (char *const *)argv);
1401 fprintf(stderr, "\nexecv() failed.\nCommand: '%s'\n", argv[0]);
1402 exit(1);
1405 if (pty)
1406 *pty = t->pty;
1407 return t->childpid = pid;
1410 int vt_getpty(Vt *t)
1412 return t->pty;
1415 int vt_write(Vt *t, const char *buf, int len)
1417 int ret = len;
1419 while (len > 0) {
1420 int res = write(t->pty, buf, len);
1421 if (res < 0 && errno != EAGAIN && errno != EINTR)
1422 return -1;
1424 buf += res;
1425 len -= res;
1428 return ret;
1431 static void send_curs(Vt *vt)
1433 Buffer *t = vt->buffer;
1434 char keyseq[16];
1435 sprintf(keyseq, "\e[%d;%dR", (int)(t->curs_row - t->lines), t->curs_col);
1436 vt_write(vt, keyseq, strlen(keyseq));
1439 void vt_keypress(Vt *t, int keycode)
1441 char c = (char)keycode;
1443 vt_noscroll(t);
1445 if (keycode >= 0 && keycode < KEY_MAX && keytable[keycode]) {
1446 switch (keycode) {
1447 case KEY_UP:
1448 case KEY_DOWN:
1449 case KEY_RIGHT:
1450 case KEY_LEFT: {
1451 char keyseq[3] = { '\e', (t->curskeymode ? 'O' : '['), keytable[keycode][0] };
1452 vt_write(t, keyseq, sizeof keyseq);
1453 break;
1455 default:
1456 vt_write(t, keytable[keycode], strlen(keytable[keycode]));
1458 } else {
1459 vt_write(t, &c, 1);
1463 void vt_mouse(Vt *t, int x, int y, mmask_t mask)
1465 #ifdef NCURSES_MOUSE_VERSION
1466 char seq[6] = { '\e', '[', 'M' }, state = 0, button = 0;
1468 if (!t->mousetrack)
1469 return;
1471 if (mask & (BUTTON1_PRESSED | BUTTON1_CLICKED))
1472 button = 0;
1473 else if (mask & (BUTTON2_PRESSED | BUTTON2_CLICKED))
1474 button = 1;
1475 else if (mask & (BUTTON3_PRESSED | BUTTON3_CLICKED))
1476 button = 2;
1477 else if (mask & (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED))
1478 button = 3;
1480 if (mask & BUTTON_SHIFT)
1481 state |= 4;
1482 if (mask & BUTTON_ALT)
1483 state |= 8;
1484 if (mask & BUTTON_CTRL)
1485 state |= 16;
1487 seq[3] = 32 + button + state;
1488 seq[4] = 32 + x;
1489 seq[5] = 32 + y;
1491 vt_write(t, seq, sizeof seq);
1493 if (mask & (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED)) {
1494 /* send a button release event */
1495 button = 3;
1496 seq[3] = 32 + button + state;
1497 vt_write(t, seq, sizeof seq);
1499 #endif /* NCURSES_MOUSE_VERSION */
1502 static unsigned int color_hash(short fg, short bg)
1504 if (fg == -1)
1505 fg = COLORS;
1506 if (bg == -1)
1507 bg = COLORS + 1;
1508 return fg * (COLORS + 2) + bg;
1511 short vt_color_get(Vt *t, short fg, short bg)
1513 if (fg >= COLORS)
1514 fg = (t ? t->deffg : default_fg);
1515 if (bg >= COLORS)
1516 bg = (t ? t->defbg : default_bg);
1518 if (!has_default_colors) {
1519 if (fg == -1)
1520 fg = (t && t->deffg != -1 ? t->deffg : default_fg);
1521 if (bg == -1)
1522 bg = (t && t->defbg != -1 ? t->defbg : default_bg);
1525 if (!color2palette || (fg == -1 && bg == -1))
1526 return 0;
1527 unsigned int index = color_hash(fg, bg);
1528 if (color2palette[index] == 0) {
1529 short oldfg, oldbg;
1530 for (;;) {
1531 if (++color_pair_current >= color_pairs_max)
1532 color_pair_current = color_pairs_reserved + 1;
1533 pair_content(color_pair_current, &oldfg, &oldbg);
1534 unsigned int old_index = color_hash(oldfg, oldbg);
1535 if (color2palette[old_index] >= 0) {
1536 if (init_pair(color_pair_current, fg, bg) == OK) {
1537 color2palette[old_index] = 0;
1538 color2palette[index] = color_pair_current;
1540 break;
1545 short color_pair = color2palette[index];
1546 return color_pair >= 0 ? color_pair : -color_pair;
1549 short vt_color_reserve(short fg, short bg)
1551 if (!color2palette || fg >= COLORS || bg >= COLORS)
1552 return 0;
1553 if (!has_default_colors && fg == -1)
1554 fg = default_fg;
1555 if (!has_default_colors && bg == -1)
1556 bg = default_bg;
1557 if (fg == -1 && bg == -1)
1558 return 0;
1559 unsigned int index = color_hash(fg, bg);
1560 if (color2palette[index] >= 0) {
1561 if (init_pair(++color_pairs_reserved, fg, bg) == OK)
1562 color2palette[index] = -color_pairs_reserved;
1564 short color_pair = color2palette[index];
1565 return color_pair >= 0 ? color_pair : -color_pair;
1568 static void init_colors(void)
1570 pair_content(0, &default_fg, &default_bg);
1571 if (default_fg == -1)
1572 default_fg = COLOR_WHITE;
1573 if (default_bg == -1)
1574 default_bg = COLOR_BLACK;
1575 has_default_colors = (use_default_colors() == OK);
1576 color_pairs_max = MIN(COLOR_PAIRS, MAX_COLOR_PAIRS);
1577 color2palette = calloc((COLORS + 2) * (COLORS + 2), sizeof(short));
1578 vt_color_reserve(COLOR_WHITE, COLOR_BLACK);
1581 void vt_init(void)
1583 init_colors();
1584 is_utf8_locale();
1587 void vt_shutdown(void)
1589 free(color2palette);
1592 void vt_set_escseq_handler(Vt *t, vt_escseq_handler_t handler)
1594 t->escseq_handler = handler;
1597 void vt_set_event_handler(Vt *t, vt_event_handler_t handler)
1599 t->event_handler = handler;
1602 void vt_set_data(Vt *t, void *data)
1604 t->data = data;
1607 void *vt_get_data(Vt *t)
1609 return t->data;
1612 unsigned vt_cursor(Vt *t)
1614 return t->buffer->scroll_amount ? 0 : !t->curshid;