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
29 #include <sys/ioctl.h>
33 #define MAX_CSI_ES_PARAMS 32
34 #define ESEQ_BUF_SIZE 128 /* size of escape sequence buffer */
36 /* Terminal private data */
37 struct RoteTermPrivate_ {
38 unsigned escaped : 1; /* whether we are currently reading an
41 unsigned graphmode : 1; /* whether terminal is in graphical
42 * character mode or not */
44 int scrolltop, scrollbottom; /* current scrolling region of terminal */
45 int saved_x, saved_y; /* saved cursor position */
47 char esbuf[ESEQ_BUF_SIZE]; /* 0-terminated string. Does NOT include
48 * the initial escape (\x1B) character. */
49 int esbuf_len; /* length of buffer. The following property
50 * is always kept: esbuf[esbuf_len] == '\0' */
53 static void clamp_cursor_to_bounds(RoteTerm *rt)
56 rt->curpos_dirty = true;
60 rt->curpos_dirty = true;
64 if (rt->crow >= rt->rows) {
65 rt->curpos_dirty = true;
66 rt->crow = rt->rows - 1;
68 if (rt->ccol >= rt->cols) {
69 rt->curpos_dirty = true;
70 rt->ccol = rt->cols - 1;
74 static void cursor_line_down(RoteTerm *rt)
79 rt->curpos_dirty = true;
80 if (rt->crow <= rt->pd->scrollbottom)
83 /* must scroll the scrolling region up by 1 line, and put cursor on
85 rt->crow = rt->pd->scrollbottom;
87 for (i = rt->pd->scrolltop; i < rt->pd->scrollbottom; i++) {
88 rt->line_dirty[i] = true;
89 memcpy(rt->cells[i], rt->cells[i+1], sizeof(RoteCell) * rt->cols);
92 rt->line_dirty[rt->pd->scrollbottom] = true;
94 /* clear last row of the scrolling region */
95 for (i = 0; i < rt->cols; i++) {
96 rt->cells[rt->pd->scrollbottom][i].s[0] = 0x20;
97 rt->cells[rt->pd->scrollbottom][i].len = 1;
98 rt->cells[rt->pd->scrollbottom][i].attr = 0x70;
102 static void cursor_line_up(RoteTerm *rt)
107 rt->curpos_dirty = true;
108 if (rt->crow >= rt->pd->scrolltop)
111 /* must scroll the scrolling region up by 1 line, and put cursor on
112 * first line of it */
113 rt->crow = rt->pd->scrolltop;
115 for (i = rt->pd->scrollbottom; i > rt->pd->scrolltop; i--) {
116 rt->line_dirty[i] = true;
117 memcpy(rt->cells[i], rt->cells[i-1], sizeof(RoteCell) * rt->cols);
120 rt->line_dirty[rt->pd->scrolltop] = true;
122 /* clear first row of the scrolling region */
123 for (i = 0; i < rt->cols; i++) {
124 rt->cells[rt->pd->scrolltop][i].s[0] = 0x20;
125 rt->cells[rt->pd->scrolltop][i].len = 1;
126 rt->cells[rt->pd->scrolltop][i].attr = 0x70;
130 static void put_normal_char(RoteTerm *rt, const char *s, int len)
132 if (rt->ccol >= rt->cols) {
134 cursor_line_down(rt);
140 for (i = rt->cols - 1; i >= rt->ccol+1; i--) {
141 rt->cells[rt->crow][i] = rt->cells[rt->crow][i-1];
145 memcpy(rt->cells[rt->crow][rt->ccol].s, s, len);
146 rt->cells[rt->crow][rt->ccol].len = len;
147 rt->cells[rt->crow][rt->ccol].attr = rt->curattr;
150 rt->line_dirty[rt->crow] = true;
151 rt->curpos_dirty = true;
154 static void put_graphmode_char(RoteTerm *rt, int c)
157 /* do some very pitiful translation to regular ascii chars */
159 case 'j': case 'k': case 'l': case 'm': case 'n': case 't':
160 case 'u': case 'v': case 'w':
168 put_normal_char(rt, &nc, 1);
171 static void new_escape_sequence(RoteTerm *rt)
173 rt->pd->escaped = true;
174 rt->pd->esbuf_len = 0;
175 rt->pd->esbuf[0] = '\0';
178 static void cancel_escape_sequence(RoteTerm *rt)
180 rt->pd->escaped = false;
181 rt->pd->esbuf_len = 0;
182 rt->pd->esbuf[0] = '\0';
185 static void handle_control_char(RoteTerm *rt, int c)
188 case '\r': /* carriage return */
192 case '\n': /* line feed */
194 cursor_line_down(rt);
195 rt->curpos_dirty = true;
198 case '\b': /* backspace */
201 rt->curpos_dirty = true;
205 rt->ccol = (rt->ccol + 8) & ~7;
206 clamp_cursor_to_bounds(rt);
209 case '\x1b': /* begin escape sequence (aborting previous one if any) */
210 new_escape_sequence(rt);
213 case '\x0e': /* enter graphical character mode */
214 rt->pd->graphmode = true;
217 case '\x0f': /* exit graphical character mode */
218 rt->pd->graphmode = false;
221 case '\x9b': /* CSI character. Equivalent to ESC [ */
222 new_escape_sequence(rt);
223 rt->pd->esbuf[rt->pd->esbuf_len++] = '[';
227 case '\x1a': /* these interrupt escape sequences */
228 cancel_escape_sequence(rt);
231 case '\a': /* bell */
232 /* do nothing for now... maybe a visual bell would be nice? */
237 static bool is_valid_csi_ender(int c)
239 return (c >= 'a' && c <= 'z')
240 || (c >= 'A' && c <= 'Z')
241 || (c == '@' || c == '`');
244 static void rote_es_interpret_csi(RoteTerm *rt);
246 static void try_interpret_escape_seq(RoteTerm *rt)
248 char firstchar = rt->pd->esbuf[0];
249 char lastchar = rt->pd->esbuf[rt->pd->esbuf_len-1];
252 return; /* too early to do anything */
254 /* interpret ESC-M as reverse line-feed */
255 if (firstchar == 'M') {
257 cancel_escape_sequence(rt);
261 if (firstchar != '[' && firstchar != ']') {
262 /* unrecognized escape sequence. Let's forget about it. */
263 cancel_escape_sequence(rt);
267 if (firstchar == '[' && is_valid_csi_ender(lastchar)) {
268 /* we have a csi escape sequence: interpret it */
269 rote_es_interpret_csi(rt);
270 cancel_escape_sequence(rt);
271 } else if (firstchar == ']' && lastchar == '\a') {
272 /* we have an xterm escape sequence: interpret it */
274 /* rote_es_interpret_xterm_es(rt); -- TODO!*/
275 cancel_escape_sequence(rt);
278 /* if the escape sequence took up all available space and could
279 * not yet be parsed, abort it */
280 if (rt->pd->esbuf_len + 1 >= ESEQ_BUF_SIZE)
281 cancel_escape_sequence(rt);
284 int rote_vt_inject(RoteTerm *rt, const char *data, int len)
288 for (pos = 0; pos < len; pos++) {
289 if ((unsigned char)data[pos] <= 31) {
290 handle_control_char(rt, data[pos]);
294 if (rt->pd->escaped && rt->pd->esbuf_len < ESEQ_BUF_SIZE) {
295 /* append character to ongoing escape sequence */
296 rt->pd->esbuf[rt->pd->esbuf_len] = data[pos];
297 rt->pd->esbuf[++rt->pd->esbuf_len] = 0;
299 try_interpret_escape_seq(rt);
301 if (rt->pd->graphmode) {
302 put_graphmode_char(rt, data[pos]);
304 static int8_t const lens[5] = { 1, -1, 2, 3, 4 };
305 int bsf = __builtin_clz(~((unsigned char)data[pos] << 24));
307 if (pos + lens[bsf] > len)
310 put_normal_char(rt, data + pos, lens[bsf]);
311 pos += lens[bsf] - 1;
318 /****************************************************************************/
320 /****************************************************************************/
322 /* interprets a 'set attribute' (SGR) CSI escape sequence */
323 static void interpret_csi_SGR(RoteTerm *rt, int param[], int pcount)
328 /* special case: reset attributes */
333 for (i = 0; i < pcount; i++) {
335 // From http://vt100.net/docs/vt510-rm/SGR table 5-16
336 // 0 All attributes off
342 // 10 The ASCII character set is the current 7-bit
343 // display character set (default) - SCO Console only.
344 // 11 Map Hex 00-7F of the PC character set codes
345 // to the current 7-bit display character set
346 // - SCO Console only.
347 // 12 Map Hex 80-FF of the current character set to
348 // the current 7-bit display character set - SCO
353 // 27 Negative image off
354 // 28 Invisible image off
356 if (param[i] == 0) rt->curattr = 0x70;
357 else if (param[i] == 1 || param[i] == 2 || param[i] == 4) /* set bold */
358 ROTE_ATTR_MOD_BOLD(rt->curattr,1);
359 else if (param[i] == 5) /* set blink */
360 ROTE_ATTR_MOD_BLINK(rt->curattr,1);
361 else if (param[i] == 7 || param[i] == 27) { /* reverse video */
362 int fg = ROTE_ATTR_FG(rt->curattr);
363 int bg = ROTE_ATTR_BG(rt->curattr);
364 ROTE_ATTR_MOD_FG(rt->curattr, bg);
365 ROTE_ATTR_MOD_BG(rt->curattr, fg);
367 else if (param[i] == 8) rt->curattr = 0x0; /* invisible */
368 else if (param[i] == 22 || param[i] == 24) /* bold off */
369 ROTE_ATTR_MOD_BOLD(rt->curattr,0);
370 else if (param[i] == 25) /* blink off */
371 ROTE_ATTR_MOD_BLINK(rt->curattr,0);
372 else if (param[i] == 28) /* invisible off */
374 else if (param[i] >= 30 && param[i] <= 37) /* set fg */
375 ROTE_ATTR_MOD_FG(rt->curattr, param[i] - 30);
376 else if (param[i] >= 40 && param[i] <= 47) /* set bg */
377 ROTE_ATTR_MOD_BG(rt->curattr, param[i] - 40);
378 else if (param[i] == 39) /* reset foreground to default */
379 ROTE_ATTR_MOD_FG(rt->curattr, 7);
380 else if (param[i] == 49) /* reset background to default */
381 ROTE_ATTR_MOD_BG(rt->curattr, 0);
385 /* interprets an 'erase display' (ED) escape sequence */
386 static void interpret_csi_ED(RoteTerm *rt, int param[], int pcount)
389 int start_row, start_col, end_row, end_col;
392 if (pcount && param[0] == 2) {
395 end_row = rt->rows - 1;
396 end_col = rt->cols - 1;
398 if (pcount && param[0] == 1) {
404 start_row = rt->crow;
405 start_col = rt->ccol;
406 end_row = rt->rows - 1;
407 end_col = rt->cols - 1;
411 for (r = start_row; r <= end_row; r++) {
412 rt->line_dirty[r] = true;
414 for (c = (r == start_row ? start_col : 0);
415 c <= (r == end_row ? end_col : rt->cols - 1);
418 rt->cells[r][c].s[0] = 0x20;
419 rt->cells[r][c].len = 1;
420 rt->cells[r][c].attr = rt->curattr;
425 /* interprets a 'move cursor' (CUP) escape sequence */
426 static void interpret_csi_CUP(RoteTerm *rt, int param[], int pcount)
430 rt->crow = rt->ccol = 0;
434 return; /* malformed */
437 rt->crow = param[0] - 1; /* convert from 1-based to 0-based */
438 rt->ccol = param[1] - 1; /* convert from 1-based to 0-based */
440 rt->curpos_dirty = true;
442 clamp_cursor_to_bounds(rt);
445 /* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL,
446 * CPL, CHA, HPR, VPA, VPR, HPA */
447 static void interpret_csi_C(RoteTerm *rt, char verb, int param[], int pcount)
449 int n = (pcount && param[0] > 0) ? param[0] : 1;
452 case 'A': rt->crow -= n; break;
453 case 'B': case 'e': rt->crow += n; break;
454 case 'C': case 'a': rt->ccol += n; break;
455 case 'D': rt->ccol -= n; break;
456 case 'E': rt->crow += n; rt->ccol = 0; break;
457 case 'F': rt->crow -= n; rt->ccol = 0; break;
458 case 'G': case '`': rt->ccol = param[0] - 1; break;
459 case 'd': rt->crow = param[0] - 1; break;
462 rt->curpos_dirty = true;
463 clamp_cursor_to_bounds(rt);
466 /* Interpret the 'erase line' escape sequence */
467 static void interpret_csi_EL(RoteTerm *rt, int param[], int pcount)
469 int erase_start, erase_end, i;
470 int cmd = pcount ? param[0] : 0;
473 case 1: erase_start = 0; erase_end = rt->ccol; break;
474 case 2: erase_start = 0; erase_end = rt->cols - 1; break;
475 default: erase_start = rt->ccol; erase_end = rt->cols - 1; break;
478 for (i = erase_start; i <= erase_end; i++) {
479 rt->cells[rt->crow][i].s[0] = 0x20;
480 rt->cells[rt->crow][i].len = 1;
481 rt->cells[rt->crow][i].attr = rt->curattr;
484 rt->line_dirty[rt->crow] = true;
487 /* Interpret the 'insert blanks' sequence (ICH) */
488 static void interpret_csi_ICH(RoteTerm *rt, int param[], int pcount)
490 int n = (pcount && param[0] > 0) ? param[0] : 1;
493 for (i = rt->cols - 1; i >= rt->ccol + n; i--) {
494 rt->cells[rt->crow][i] = rt->cells[rt->crow][i - n];
497 for (i = rt->ccol; i < rt->ccol + n; i++) {
498 rt->cells[rt->crow][i].s[0] = 0x20;
499 rt->cells[rt->crow][i].len = 1;
500 rt->cells[rt->crow][i].attr = rt->curattr;
503 rt->line_dirty[rt->crow] = true;
506 /* Interpret the 'delete chars' sequence (DCH) */
507 static void interpret_csi_DCH(RoteTerm *rt, int param[], int pcount)
509 int n = (pcount && param[0] > 0) ? param[0] : 1;
512 for (i = rt->ccol; i < rt->cols; i++) {
513 if (i + n < rt->cols) {
514 rt->cells[rt->crow][i] = rt->cells[rt->crow][i + n];
516 rt->cells[rt->crow][i].s[0] = 0x20;
517 rt->cells[rt->crow][i].len = 1;
518 rt->cells[rt->crow][i].attr = rt->curattr;
522 rt->line_dirty[rt->crow] = true;
525 /* Interpret an 'insert line' sequence (IL) */
526 static void interpret_csi_IL(RoteTerm *rt, int param[], int pcount)
528 int n = (pcount && param[0] > 0) ? param[0] : 1;
531 for (i = rt->pd->scrollbottom; i >= rt->crow + n; i--) {
532 memcpy(rt->cells[i], rt->cells[i - n], sizeof(RoteCell) * rt->cols);
535 for (i = rt->crow; i < rt->crow + n && i <= rt->pd->scrollbottom; i++) {
536 rt->line_dirty[i] = true;
537 for (j = 0; j < rt->cols; j++) {
538 rt->cells[i][j].s[0] = 0x20;
539 rt->cells[i][j].len = 1;
540 rt->cells[i][j].attr = rt->curattr;
546 /* Interpret a 'delete line' sequence (DL) */
547 static void interpret_csi_DL(RoteTerm *rt, int param[], int pcount)
549 int n = (pcount && param[0] > 0) ? param[0] : 1;
552 for (i = rt->crow; i <= rt->pd->scrollbottom; i++) {
553 rt->line_dirty[i] = true;
554 if (i + n <= rt->pd->scrollbottom) {
555 memcpy(rt->cells[i], rt->cells[i+n], sizeof(RoteCell) * rt->cols);
557 for (j = 0; j < rt->cols; j++) {
558 rt->cells[i][j].s[0] = 0x20;
559 rt->cells[i][j].len = 1;
560 rt->cells[i][j].attr = rt->curattr;
566 /* Interpret an 'erase characters' (ECH) sequence */
567 static void interpret_csi_ECH(RoteTerm *rt, int param[], int pcount)
569 int n = (pcount && param[0] > 0) ? param[0] : 1;
572 for (i = rt->ccol; i < rt->ccol + n && i < rt->cols; i++) {
573 rt->cells[rt->crow][i].s[0] = 0x20;
574 rt->cells[rt->crow][i].len = 1;
575 rt->cells[rt->crow][i].attr = rt->curattr;
578 rt->line_dirty[rt->crow] = true;
581 /* Interpret a 'set scrolling region' (DECSTBM) sequence */
582 static void interpret_csi_DECSTBM(RoteTerm *rt, int param[], int pcount)
584 int newtop, newbottom;
588 newbottom = rt->rows - 1;
591 return; /* malformed */
594 newtop = param[0] - 1;
595 newbottom = param[1] - 1;
597 /* clamp to bounds */
600 if (newtop >= rt->rows)
601 newtop = rt->rows - 1;
604 if (newbottom >= rt->rows)
605 newbottom = rt->rows - 1;
607 /* check for range validity */
608 if (newtop > newbottom)
610 rt->pd->scrolltop = newtop;
611 rt->pd->scrollbottom = newbottom;
615 interpret_csi_SAVECUR(RoteTerm *rt,
616 int param[] __attribute__((unused)),
617 int pcount __attribute__((unused)))
619 rt->pd->saved_x = rt->ccol;
620 rt->pd->saved_y = rt->crow;
624 interpret_csi_RESTORECUR(RoteTerm *rt,
625 int param[] __attribute__((unused)),
626 int pcount __attribute__((unused)))
628 rt->ccol = rt->pd->saved_x;
629 rt->crow = rt->pd->saved_y;
630 rt->curpos_dirty = true;
633 static void rote_es_interpret_csi(RoteTerm *rt)
635 static int csiparam[MAX_CSI_ES_PARAMS];
637 const char *p = rt->pd->esbuf + 1;
638 char verb = rt->pd->esbuf[rt->pd->esbuf_len - 1];
640 if (!strncmp(rt->pd->esbuf, "[?", 2)) { /* private-mode CSI, ignore */
644 /* parse numeric parameters */
645 while (isdigit((unsigned char)*p) || *p == ';') {
647 if (param_count >= MAX_CSI_ES_PARAMS) return; /* too long! */
648 csiparam[param_count++] = 0;
650 if (param_count == 0) csiparam[param_count++] = 0;
651 csiparam[param_count - 1] *= 10;
652 csiparam[param_count - 1] += *p - '0';
658 /* delegate handling depending on command character (verb) */
661 if (param_count == 1 && csiparam[0] == 4) /* insert mode */
665 if (param_count == 1 && csiparam[0] == 4) /* replace mode */
668 case 'm': /* it's a 'set attribute' sequence */
669 interpret_csi_SGR(rt, csiparam, param_count); break;
670 case 'J': /* it's an 'erase display' sequence */
671 interpret_csi_ED(rt, csiparam, param_count); break;
672 case 'H': case 'f': /* it's a 'move cursor' sequence */
673 interpret_csi_CUP(rt, csiparam, param_count); break;
674 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
675 case 'e': case 'a': case 'd': case '`':
676 /* it is a 'relative move' */
677 interpret_csi_C(rt, verb, csiparam, param_count); break;
678 case 'K': /* erase line */
679 interpret_csi_EL(rt, csiparam, param_count); break;
680 case '@': /* insert characters */
681 interpret_csi_ICH(rt, csiparam, param_count); break;
682 case 'P': /* delete characters */
683 interpret_csi_DCH(rt, csiparam, param_count); break;
684 case 'L': /* insert lines */
685 interpret_csi_IL(rt, csiparam, param_count); break;
686 case 'M': /* delete lines */
687 interpret_csi_DL(rt, csiparam, param_count); break;
688 case 'X': /* erase chars */
689 interpret_csi_ECH(rt, csiparam, param_count); break;
690 case 'r': /* set scrolling region */
691 interpret_csi_DECSTBM(rt, csiparam, param_count); break;
692 case 's': /* save cursor location */
693 interpret_csi_SAVECUR(rt, csiparam, param_count); break;
694 case 'u': /* restore cursor location */
695 interpret_csi_RESTORECUR(rt, csiparam, param_count); break;
701 RoteTerm *rote_vt_create(int rows, int cols)
706 if (rows <= 0 || cols <= 0)
709 rt = (RoteTerm*)calloc(sizeof(RoteTerm), 1);
713 /* record dimensions */
717 /* default mode is replace */
720 /* create the cell matrix */
721 rt->cells = (RoteCell**)calloc(sizeof(RoteCell*), rt->rows);
722 for (i = 0; i < rt->rows; i++) {
723 rt->cells[i] = (RoteCell*)calloc(sizeof(RoteCell), rt->cols);
726 /* allocate dirtiness array */
727 rt->line_dirty = (bool*)calloc(sizeof(bool), rt->rows);
729 /* initialization of other public fields */
730 rt->crow = rt->ccol = 0;
731 rt->curattr = 0x70; /* white text over black background */
733 /* allocate private data */
734 rt->pd = (RoteTermPrivate*)calloc(sizeof(RoteTermPrivate), 1);
736 rt->pty = -1; /* no pty for now */
738 /* initial scrolling area is the whole window */
739 rt->pd->scrolltop = 0;
740 rt->pd->scrollbottom = rt->rows - 1;
745 void rote_vt_destroy(RoteTerm *rt)
752 free(rt->line_dirty);
753 for (i = 0; i < rt->rows; i++) {
760 static void cur_set_attr(WINDOW *win, uint8_t attr)
762 unsigned cp = ROTE_ATTR_BG(attr) * 8 + 7 - ROTE_ATTR_FG(attr);
763 wattrset(win, cp ? COLOR_PAIR(cp) : A_NORMAL);
765 if (ROTE_ATTR_BOLD(attr))
766 wattron(win, A_BOLD);
767 if (ROTE_ATTR_BLINK(attr))
768 wattron(win, A_BLINK);
771 static unsigned ensure_printable(unsigned ch)
773 return ch >= 32 ? ch : 32;
776 void rote_vt_draw(RoteTerm *rt, WINDOW *win, int srow, int scol)
780 for (i = 0; i < rt->rows; i++) {
781 wmove(win, srow + i, scol);
782 for (j = 0; j < rt->cols; j++) {
783 cur_set_attr(win, rt->cells[i][j].attr);
784 if (rt->cells[i][j].len && rt->cells[i][j].s[0] >= ' ') {
785 waddnstr(win, rt->cells[i][j].s, rt->cells[i][j].len);
792 wmove(win, srow + rt->crow, scol + rt->ccol);
795 /******************************************************/
797 pid_t rote_vt_forkpty(RoteTerm *rt, const char *path, const char *argv[])
802 ws.ws_row = rt->rows;
803 ws.ws_col = rt->cols;
804 ws.ws_xpixel = ws.ws_ypixel = 0;
806 pid = forkpty(&rt->pty, NULL, NULL, &ws);
813 setenv("TERM", "linux", 1);
814 execv(path, (char *const*)argv);
815 fprintf(stderr, "\nexecv() failed.\nCommand: '%s'\n", argv[0]);
819 return rt->childpid = pid;
822 int rote_vt_read(RoteTerm *rt, char *buf, int buflen)
829 return read(rt->pty, buf, buflen);
832 int rote_vt_write(RoteTerm *rt, const char *data, int len)
842 res = write(rt->pty, data, len);
844 if (errno == EINTR || errno == EAGAIN)
851 static const char *keytable[KEY_MAX+1];
853 static void keytable_init()
855 memset(keytable, 0, KEY_MAX+1 * sizeof(const char*));
857 keytable['\n'] = "\r";
858 keytable[KEY_UP] = "\e[A";
859 keytable[KEY_DOWN] = "\e[B";
860 keytable[KEY_RIGHT] = "\e[C";
861 keytable[KEY_LEFT] = "\e[D";
862 keytable[KEY_BACKSPACE] = "\b";
863 keytable[KEY_HOME] = "\e[1~";
864 keytable[KEY_IC] = "\e[2~";
865 keytable[KEY_DC] = "\e[3~";
866 keytable[KEY_END] = "\e[4~";
867 keytable[KEY_PPAGE] = "\e[5~";
868 keytable[KEY_NPAGE] = "\e[6~";
869 keytable[KEY_SUSPEND] = "\x1A"; /* Ctrl+Z gets mapped to this */
870 keytable[KEY_F(1)] = "\e[[A";
871 keytable[KEY_F(2)] = "\e[[B";
872 keytable[KEY_F(3)] = "\e[[C";
873 keytable[KEY_F(4)] = "\e[[D";
874 keytable[KEY_F(5)] = "\e[[E";
875 keytable[KEY_F(6)] = "\e[17~";
876 keytable[KEY_F(7)] = "\e[18~";
877 keytable[KEY_F(8)] = "\e[19~";
878 keytable[KEY_F(9)] = "\e[20~";
879 keytable[KEY_F(10)] = "\e[21~";
882 void rote_vt_keypress(RoteTerm *rt, int keycode)
884 char c = (char)keycode;
888 if (keytable['\n'] == NULL)
891 if (keycode >= 0 && keycode < KEY_MAX && keytable[keycode]) {
892 buf = keytable[keycode];
893 len = strlen(keytable[keycode]);
900 int res = rote_vt_write(rt, buf, len);