1 ////////////////////////////////////////////////////////////////////////////////
2 static void k8t_dbgCSIDump (K8Term
*term
) {
4 for (int f
= 0; f
< term
->escseq
.len
; ++f
) {
5 uint32_t c
= (term
->escseq
.buf
[f
]&0xff);
7 if (c
!= 32 && isprint(c
)) fputc(c
, stderr
);
8 else if (c
== '\n') fprintf(stderr
, "(\\n)");
9 else if (c
== '\r') fprintf(stderr
, "(\\r)");
10 else if (c
== 0x1b) fprintf(stderr
, "(\\e)");
11 else fprintf(stderr
, "(%u)", c
);
17 ////////////////////////////////////////////////////////////////////////////////
18 static void k8t_tmResetAttrs (K8Term
*term
) {
19 term
->c
.attr
.attr
&= ~(K8T_ATTR_REVERSE
|K8T_ATTR_UNDERLINE
|K8T_ATTR_BOLD
);
20 term
->c
.attr
.attr
|= K8T_ATTR_DEFFG
|K8T_ATTR_DEFBG
;
21 term
->c
.attr
.fg
= term
->deffg
;
22 term
->c
.attr
.bg
= term
->defbg
;
26 static void k8t_tmSetAttr (K8Term
*term
, int *attr
, int l
) {
27 for (int f
= 0; f
< l
; ++f
) {
30 k8t_tmResetAttrs(term
);
33 term
->c
.attr
.attr
|= K8T_ATTR_BOLD
;
36 term
->c
.attr
.attr
|= K8T_ATTR_UNDERLINE
;
39 term
->c
.attr
.attr
|= K8T_ATTR_REVERSE
;
42 term
->c
.attr
.attr
&= ~K8T_ATTR_BOLD
;
45 term
->c
.attr
.attr
&= ~K8T_ATTR_UNDERLINE
;
48 term
->c
.attr
.attr
&= ~K8T_ATTR_REVERSE
;
51 if (f
+2 < l
&& attr
[f
+1] == 5) {
53 if (K8T_BETWEEN(attr
[f
], 0, 255)) {
54 term
->c
.attr
.fg
= attr
[f
];
55 term
->c
.attr
.attr
&= ~K8T_ATTR_DEFFG
;
57 fprintf(stderr
, "erresc: bad fgcolor %d\n", attr
[f
]);
60 //fprintf(stderr, "erresc: gfx attr %d unknown\n", attr[f]);
61 term
->c
.attr
.fg
= term
->deffg
;
62 term
->c
.attr
.attr
|= K8T_ATTR_DEFFG
;
66 term
->c
.attr
.fg
= term
->deffg
;
67 term
->c
.attr
.attr
|= K8T_ATTR_DEFFG
;
70 if (f
+2 < l
&& attr
[f
+1] == 5) {
72 if (K8T_BETWEEN(attr
[f
], 0, 255)) {
73 term
->c
.attr
.bg
= attr
[f
];
74 term
->c
.attr
.attr
&= ~K8T_ATTR_DEFBG
;
76 fprintf(stderr
, "erresc: bad bgcolor %d\n", attr
[f
]);
79 fprintf(stderr
, "erresc: gfx attr %d unknown\n", attr
[f
]);
83 term
->c
.attr
.bg
= term
->defbg
;
84 term
->c
.attr
.attr
|= K8T_ATTR_DEFBG
;
87 if (K8T_BETWEEN(attr
[f
], 30, 37)) { term
->c
.attr
.fg
= attr
[f
]-30; term
->c
.attr
.attr
&= ~K8T_ATTR_DEFFG
; }
88 else if (K8T_BETWEEN(attr
[f
], 40, 47)) { term
->c
.attr
.bg
= attr
[f
]-40; term
->c
.attr
.attr
&= ~K8T_ATTR_DEFBG
; }
89 else if (K8T_BETWEEN(attr
[f
], 90, 97)) { term
->c
.attr
.fg
= attr
[f
]-90+8; term
->c
.attr
.attr
&= ~K8T_ATTR_DEFFG
; }
90 else if (K8T_BETWEEN(attr
[f
], 100, 107)) { term
->c
.attr
.bg
= attr
[f
]-100+8; term
->c
.attr
.attr
&= ~K8T_ATTR_DEFBG
; }
91 else { fprintf(stderr
, "erresc: gfx attr %d unknown\n", attr
[f
]); k8t_dbgCSIDump(term
); }
101 // -1: no-wrap mode and tries to wrap
102 static int k8t_tmDoWrap (K8Term
*term
) {
103 if (term
->c
.state
&K8T_CURSOR_WRAPNEXT
) {
104 if (K8T_ISSET(term
, K8T_MODE_WRAP
)) {
105 // always go to first col
106 k8t_tmNewLine(term
, 2); // this will reset K8T_CURSOR_WRAPNEXT
109 k8t_tmCharWrap(term
, term
->c
.y
, 0);
117 ////////////////////////////////////////////////////////////////////////////////
119 static void k8t_tmCSIHandle (K8Term
*term
) {
120 if (term
->dumpescapes
) {
121 fprintf(stderr
, "CSI: ");
122 k8t_dbgCSIDump(term
);
125 switch (term
->escseq
.mode
) {
126 case '@': /* ICH -- Insert <n> blank char */
127 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
128 k8t_tmInsertBlank(term
, term
->escseq
.arg
[0]);
130 case 'A': /* CUU -- Cursor <n> Up */
132 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
133 k8t_tmMoveTo(term
, term
->c
.x
, term
->c
.y
-term
->escseq
.arg
[0]);
135 case 'B': /* CUD -- Cursor <n> Down */
136 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
137 k8t_tmMoveTo(term
, term
->c
.x
, term
->c
.y
+term
->escseq
.arg
[0]);
139 case 'C': /* CUF -- Cursor <n> Forward */
141 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
142 k8t_tmMoveTo(term
, term
->c
.x
+term
->escseq
.arg
[0], term
->c
.y
);
144 case 'D': /* CUB -- Cursor <n> Backward */
145 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
146 k8t_tmMoveTo(term
, term
->c
.x
-term
->escseq
.arg
[0], term
->c
.y
);
148 case 'E': /* CNL -- Cursor <n> Down and first col */
149 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
150 k8t_tmMoveTo(term
, 0, term
->c
.y
+term
->escseq
.arg
[0]);
152 case 'F': /* CPL -- Cursor <n> Up and first col */
153 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
154 k8t_tmMoveTo(term
, 0, term
->c
.y
-term
->escseq
.arg
[0]);
156 case 'G': /* CHA -- Move to <col> */
157 case '`': /* XXX: HPA -- same? */
158 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
159 k8t_tmMoveTo(term
, term
->escseq
.arg
[0]-1, term
->c
.y
);
161 case 'H': /* CUP -- Move to <row> <col> */
162 case 'f': /* XXX: HVP -- same? */
163 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
164 K8T_DEFAULT(term
->escseq
.arg
[1], 1);
165 k8t_tmMoveTo(term
, term
->escseq
.arg
[1]-1, term
->escseq
.arg
[0]-1);
167 /* XXX: (CSI n I) CHT -- Cursor Forward Tabulation <n> tab stops */
168 case 'J': /* ED -- Clear screen */
170 switch (term
->escseq
.arg
[0]) {
172 k8t_tmClearRegion(term
, term
->c
.x
, term
->c
.y
, term
->col
-1, term
->c
.y
);
173 if (term
->c
.y
< term
->row
-1) k8t_tmClearRegion(term
, 0, term
->c
.y
+1, term
->col
-1, term
->row
-1);
176 if (term
->c
.y
> 1) k8t_tmClearRegion(term
, 0, 0, term
->col
-1, term
->c
.y
-1);
177 k8t_tmClearRegion(term
, 0, term
->c
.y
, term
->c
.x
, term
->c
.y
);
180 k8t_tmClearRegion(term
, 0, 0, term
->col
-1, term
->row
-1);
186 case 'K': /* EL -- Clear line */
187 switch (term
->escseq
.arg
[0]) {
189 k8t_tmClearRegion(term
, term
->c
.x
, term
->c
.y
, term
->col
-1, term
->c
.y
);
192 k8t_tmClearRegion(term
, 0, term
->c
.y
, term
->c
.x
, term
->c
.y
);
195 k8t_tmClearRegion(term
, 0, term
->c
.y
, term
->col
-1, term
->c
.y
);
199 case 'S': /* SU -- Scroll <n> line up */
200 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
201 k8t_tmScrollUp(term
, term
->top
, term
->escseq
.arg
[0], 0);
203 case 'T': /* SD -- Scroll <n> line down */
204 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
205 k8t_tmScrollDown(term
, term
->top
, term
->escseq
.arg
[0]);
207 case 'L': /* IL -- Insert <n> blank lines */
208 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
209 k8t_tmInsertBlankLine(term
, term
->escseq
.arg
[0]);
211 case 'l': /* RM -- Reset Mode */
212 if (term
->escseq
.priv
) {
213 switch (term
->escseq
.arg
[0]) {
214 case 1: // 1001 for xterm compatibility
215 DUMP_KEYPAD_SWITCH("1", "OFF");
216 term
->mode
&= ~K8T_MODE_APPKEYPAD
;
217 if (term
->dumpeskeys
) fprintf(stderr
, "*KPMODE: off (1l)\n");
219 case 5: /* DECSCNM -- Remove reverse video */
220 if (K8T_ISSET(term
, K8T_MODE_REVERSE
)) {
221 term
->mode
&= ~K8T_MODE_REVERSE
;
222 k8t_tmFullDirty(term
);
225 case 7: /* autowrap off */
226 if (K8T_ISSET(term
, K8T_MODE_WRAP
)) {
228 term
->mode
&= ~K8T_MODE_WRAP
;
231 case 12: /* att610 -- Stop blinking cursor (IGNORED) */
233 case 20: /* non-standard code? */
234 term
->mode
&= ~K8T_MODE_CRLF
;
236 case 25: /* hide cursor */
237 if ((term
->c
.state
&K8T_CURSOR_HIDE
) == 0) {
238 term
->c
.state
|= K8T_CURSOR_HIDE
;
239 k8t_tmWantRedraw(term
, 0);
242 case 1000: /* disable X11 xterm mouse reporting */
243 term
->mode
&= ~K8T_MODE_MOUSEBTN
;
246 term
->mode
&= ~K8T_MODE_MOUSEMOTION
;
249 term
->mode
&= ~K8T_MODE_FOCUSEVT
;
251 case 1005: /* utf-8 mouse encoding */
252 case 1006: /* sgr mouse encoding */
253 case 1015: /* urxvt mouse encoding */
254 term
->mousemode
= 1000;
256 case 1049: /* = 1047 and 1048 */
259 if (K8T_ISSET(term
, K8T_MODE_ALTSCREEN
)) {
260 k8t_tmClearRegion(term
, 0, 0, term
->col
-1, term
->row
-1);
261 k8t_tmSwapScreen(term
);
263 if (term
->escseq
.arg
[0] != 1049) break;
265 k8t_tmCursor(term
, K8T_CURSOR_LOAD
);
267 case 2004: /* reset bracketed paste mode */
268 term
->mode
&= ~K8T_MODE_BRACPASTE
;
274 switch (term
->escseq
.arg
[0]) {
275 //2: Keyboard Action Mode (AM)
276 //12: Send/receive (SRM)
277 //20: Normal Linefeed (LNM)
279 term
->mode
&= ~K8T_MODE_DISPCTRL
;
282 term
->mode
&= ~K8T_MODE_INSERT
;
289 case 'M': /* DL -- Delete <n> lines */
290 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
291 k8t_tmDeleteLine(term
, term
->escseq
.arg
[0]);
293 case 'X': /* ECH -- Erase <n> char */
294 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
295 k8t_tmClearRegion(term
, term
->c
.x
, term
->c
.y
, term
->c
.x
+ term
->escseq
.arg
[0], term
->c
.y
);
297 case 'P': /* DCH -- Delete <n> char */
298 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
299 k8t_tmDeleteChar(term
, term
->escseq
.arg
[0]);
301 /* XXX: (CSI n Z) CBT -- Cursor Backward Tabulation <n> tab stops */
302 case 'd': /* VPA -- Move to <row> */
303 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
304 k8t_tmMoveTo(term
, term
->c
.x
, term
->escseq
.arg
[0]-1);
306 case 'h': /* SM -- Set terminal mode */
307 if (term
->escseq
.priv
) {
308 switch (term
->escseq
.arg
[0]) {
310 DUMP_KEYPAD_SWITCH("1", "ON");
311 term
->mode
|= K8T_MODE_APPKEYPAD
;
312 if (term
->dumpeskeys
) fprintf(stderr
, "*KPMODE: on (1h)\n");
314 case 5: /* DECSCNM -- Reverve video */
315 if (!K8T_ISSET(term
, K8T_MODE_REVERSE
)) {
316 term
->mode
|= K8T_MODE_REVERSE
;
317 k8t_tmFullDirty(term
);
321 if (!K8T_ISSET(term
, K8T_MODE_WRAP
)) {
322 if (term
->c
.state
&K8T_CURSOR_WRAPNEXT
) k8t_tmMoveTo(term
, term
->c
.x
, term
->c
.y
); // reset 'wrap-next' flag
323 term
->mode
|= K8T_MODE_WRAP
;
327 term
->mode
|= K8T_MODE_CRLF
;
329 case 12: /* att610 -- Start blinking cursor (IGNORED) */
330 /* fallthrough for xterm cvvis = CSI [ ? 12 ; 25 h */
331 if (term
->escseq
.narg
> 1 && term
->escseq
.arg
[1] != 25) break;
333 if ((term
->c
.state
&K8T_CURSOR_HIDE
) != 0) {
334 term
->c
.state
&= ~K8T_CURSOR_HIDE
;
335 k8t_tmWantRedraw(term
, 0);
338 case 1000: /* 1000,1002: enable xterm mouse report */
339 term
->mode
|= K8T_MODE_MOUSEBTN
;
342 term
->mode
|= K8T_MODE_MOUSEMOTION
;
345 term
->mode
|= K8T_MODE_FOCUSEVT
;
347 case 1005: /* utf-8 mouse encoding */
348 case 1006: /* sgr mouse encoding */
349 case 1015: /* urxvt mouse encoding */
350 term
->mousemode
= term
->escseq
.arg
[0];
352 case 1049: /* = 1047 and 1048 */
355 if (K8T_ISSET(term
, K8T_MODE_ALTSCREEN
)) k8t_tmClearRegion(term
, 0, 0, term
->col
-1, term
->row
-1); else k8t_tmSwapScreen(term
);
356 if (term
->escseq
.arg
[0] != 1049) break;
358 k8t_tmCursor(term
, K8T_CURSOR_SAVE
);
360 case 2004: /* set bracketed paste mode */
361 term
->mode
|= K8T_MODE_BRACPASTE
;
363 default: goto unknown
;
366 switch (term
->escseq
.arg
[0]) {
368 term
->mode
|= K8T_MODE_DISPCTRL
;
371 term
->mode
|= K8T_MODE_INSERT
;
378 case 'm': /* SGR -- Terminal attribute (color) */
379 k8t_tmSetAttr(term
, term
->escseq
.arg
, term
->escseq
.narg
);
382 if (!term
->escseq
.priv
) {
383 switch (term
->escseq
.arg
[0]) {
384 case 5: /* Device status report (DSR) */
385 k8t_ttyWriteStr(term
, "\x1b[0n");
387 case 6: { /* cursor position report */
390 sprintf(buf
, "\x1b[%d;%dR", term
->c
.x
+1, term
->c
.y
+1);
391 k8t_ttyWriteStr(term
, buf
);
396 case 'r': /* DECSTBM -- Set Scrolling Region */
397 if (term
->escseq
.priv
&& term
->escseq
.arg
[0] == 1001) {
398 // xterm compatibility
399 DUMP_KEYPAD_SWITCH("1001", "OFF");
400 term
->mode
&= ~K8T_MODE_APPKEYPAD
;
401 if (term
->dumpeskeys
) fprintf(stderr
, "*KPMODE: off (1001r)\n");
402 } else if (term
->escseq
.priv
) {
405 K8T_DEFAULT(term
->escseq
.arg
[0], 1);
406 K8T_DEFAULT(term
->escseq
.arg
[1], term
->row
);
407 k8t_tmSetScrollRegion(term
, term
->escseq
.arg
[0]-1, term
->escseq
.arg
[1]-1);
408 k8t_tmMoveTo(term
, 0, 0);
411 case 's': /* DECSC -- Save cursor position (ANSI.SYS) */
412 if (term
->escseq
.priv
&& term
->escseq
.arg
[0] == 1001) {
413 // xterm compatibility
414 DUMP_KEYPAD_SWITCH("1001", "ON");
415 term
->mode
|= K8T_MODE_APPKEYPAD
;
416 if (term
->dumpeskeys
) fprintf(stderr
, "*KPMODE: on (1001s)\n");
418 k8t_tmCursor(term
, K8T_CURSOR_SAVE
);
421 case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */
422 k8t_tmCursor(term
, K8T_CURSOR_LOAD
);
426 fprintf(stderr
, "erresc: unknown csi ");
427 k8t_dbgCSIDump(term
);
433 static void k8t_tmCSIReset (K8Term
*term
) {
434 memset(&term
->escseq
, 0, sizeof(term
->escseq
));
438 static void k8t_tmCSIParse (K8Term
*term
) {
439 const char *p
= term
->escseq
.buf
;
441 term
->escseq
.narg
= 0;
442 for (int f
= 0; f
< K8T_ESC_ARG_SIZ
; ++f
) term
->escseq
.arg
[f
] = 0;
444 if (*p
== '?') { term
->escseq
.priv
= 1; ++p
; }
445 while (p
< term
->escseq
.buf
+term
->escseq
.len
) {
446 int n
= term
->escseq
.arg
[term
->escseq
.narg
];
448 for (; *p
&& isdigit(*p
); ++p
) n
= n
*10+(p
[0]-'0');
449 term
->escseq
.arg
[term
->escseq
.narg
] = n
;
451 if (*p
== ';' && term
->escseq
.narg
+1 < K8T_ESC_ARG_SIZ
) {
455 term
->escseq
.mode
= *p
;
463 ////////////////////////////////////////////////////////////////////////////////
464 static void k8t_tmPutTab (K8Term
*term
) {
465 int space
= opt_tabsize
-term
->c
.x
%opt_tabsize
;
467 if (space
> 0) k8t_tmMoveTo(term
, term
->c
.x
+space
, term
->c
.y
);
471 ////////////////////////////////////////////////////////////////////////////////
472 // put char to output buffer or process command
474 // return 1 if this was control character
475 // return -1 if this should break esape sequence
476 static int k8t_tmPutCtrl (K8Term
*term
, char ascii
) {
479 if (term
->esc
&K8T_ESC_TITLE
) return 0;
482 case '\t': if (k8t_tmDoWrap(term
) >= 0) k8t_tmPutTab(term
); break;
483 case '\b': k8t_tmDoWrap(term
); k8t_tmMoveTo(term
, term
->c
.x
-1, term
->c
.y
); break;
484 case '\r': k8t_tmDoWrap(term
); k8t_tmMoveTo(term
, 0, term
->c
.y
); break;
485 case '\f': case '\n': case '\v':
487 k8t_tmNewLine(term
, (K8T_ISSET(term
, K8T_MODE_CRLF
) ? 1 : 0)); /* go to first col if the mode is set */
490 if (!(xw
.state
&K8T_WIN_FOCUSED
) && (term
->belltype
&K8T_BELL_URGENT
)) xseturgency(1);
491 if (term
->belltype
&K8T_BELL_AUDIO
) XBell(xw
.dpy
, 100);
493 case 14: term
->charset
= K8T_MODE_GFX1
; break;
494 case 15: term
->charset
= K8T_MODE_GFX0
; break;
495 case 0x18: case 0x1a: res
= -1; break; // do nothing, interrupt current escape sequence
496 case 127: break; // ignore it
497 case '\033': k8t_tmCSIReset(term
); term
->esc
= K8T_ESC_START
; break;
498 //case 0x9b: k8t_tmCSIReset(term); term->esc = K8T_ESC_START|K8T_ESC_CSI; break;
499 default: res
= 0; break;
506 static void k8t_tmPutC (K8Term
*term
, const char *c
) {
508 int ctl
= k8t_tmPutCtrl(term
, ascii
);
510 if (ctl
> 0) return; // control char; should not break escape sequence
512 // control char; should break escape sequence
516 //dlogf("k8t_tmPutC: [%c]\n", c[0]);
517 if (term
->esc
&K8T_ESC_START
) {
518 if (term
->esc
&K8T_ESC_CSI
) {
519 term
->escseq
.buf
[term
->escseq
.len
++] = ascii
;
520 if (K8T_BETWEEN(ascii
, 0x40, 0x7E) || term
->escseq
.len
>= K8T_ESC_BUF_SIZ
) {
522 k8t_tmCSIParse(term
);
523 k8t_tmCSIHandle(term
);
525 } else if (term
->esc
&K8T_ESC_OSC
) {
526 /* TODO: handle other OSC */
530 term
->esc
= K8T_ESC_START
|K8T_ESC_TITLE
;
533 } else if (term
->esc
&K8T_ESC_TITLE
) {
534 int len
= k8t_UTF8Size(c
);
536 if (ascii
== '\a' || term
->titlelen
+len
>= K8T_ESC_TITLE_SIZ
) {
538 term
->title
[term
->titlelen
] = '\0';
539 if (curterm
== term
) fixWindowTitle(term
);
541 } else if (len
> 0) {
542 memcpy(term
->title
+term
->titlelen
, c
, len
);
543 term
->titlelen
+= len
;
544 term
->title
[term
->titlelen
] = '\0';
546 } else if (term
->esc
&K8T_ESC_ALTCHARSET
) {
549 case '0': /* Line drawing crap */
550 term
->mode
|= K8T_MODE_GFX0
;
552 case 'B': /* Back to regular text */
553 term
->mode
&= ~K8T_MODE_GFX0
;
556 fprintf(stderr
, "esc unhandled charset: ESC ( %c\n", ascii
);
557 term
->mode
&= ~K8T_MODE_GFX0
;
560 } else if (term
->esc
&K8T_ESC_ALTG1
) {
563 case '0': /* Line drawing crap */
564 term
->mode
|= K8T_MODE_GFX1
;
566 case 'B': /* Back to regular text */
567 term
->mode
&= ~K8T_MODE_GFX1
;
570 fprintf(stderr
, "esc unhandled charset: ESC ) %c\n", ascii
);
571 term
->mode
&= ~K8T_MODE_GFX1
;
574 } else if (term
->esc
&K8T_ESC_HASH
) {
577 case '8': /* DECALN -- DEC screen alignment test -- fill screen with E's */
578 //tfillscreenwithE();
581 } else if (term
->esc
&K8T_ESC_PERCENT
) {
588 term
->needConv
= (needConversion
? 1 : 0);
593 case '[': term
->esc
|= K8T_ESC_CSI
; break;
594 case ']': term
->esc
|= K8T_ESC_OSC
; break;
595 case '(': term
->esc
|= K8T_ESC_ALTCHARSET
; break;
596 case ')': term
->esc
|= K8T_ESC_ALTG1
; break;
597 case '#': term
->esc
|= K8T_ESC_HASH
; break;
598 case '%': term
->esc
|= K8T_ESC_PERCENT
; break;
599 default: /* other escapes */
601 if (term
->dumpescapes
) {
602 fprintf(stderr
, "ESC: ESC 0x%02X ('%c'; %u)\n", (uint8_t)ascii
, (isprint(ascii
) ? ascii
: '.'), (uint8_t)ascii
);
605 case 'D': /* IND -- Linefeed */
607 if (term
->c
.y
== term
->bot
) k8t_tmScrollUp(term
, term
->top
, 1, 1); else k8t_tmMoveTo(term
, term
->c
.x
, term
->c
.y
+1);
609 case 'E': /* NEL -- Next line */
611 k8t_tmNewLine(term
, 1); /* always go to first col */
613 case 'M': /* RI -- Reverse linefeed */
614 if (term
->c
.state
&K8T_CURSOR_WRAPNEXT
) {
615 k8t_tmCharWrap(term
, term
->c
.y
, 0);
616 term
->c
.state
&= ~K8T_CURSOR_WRAPNEXT
;
617 if (K8T_ISSET(term
, K8T_MODE_WRAP
)) {
618 // we should move to the next line, so just stay where we are
619 k8t_tmMoveTo(term
, term
->c
.x
, term
->c
.y
);
623 if (term
->c
.y
== term
->top
) k8t_tmScrollDown(term
, term
->top
, 1); else k8t_tmMoveTo(term
, term
->c
.x
, term
->c
.y
-1);
625 case 'c': /* RIS -- Reset to inital state */
628 case '=': /* DECPAM -- Application keypad */
629 DUMP_KEYPAD_SWITCH("=", "ON");
630 if (!term
->ignoredecpad
) {
631 term
->mode
|= K8T_MODE_APPKEYPAD
;
632 if (term
->dumpeskeys
) fprintf(stderr
, "*KPMODE: on (^[=)\n");
635 case '>': /* DECPNM -- Normal keypad */
636 DUMP_KEYPAD_SWITCH(">", "OFF");
637 if (!term
->ignoredecpad
) {
638 term
->mode
&= ~K8T_MODE_APPKEYPAD
;
639 if (term
->dumpeskeys
) fprintf(stderr
, "*KPMODE: off (^[>)\n");
642 case '7': /* DECSC -- Save Cursor */
643 /* Save current state (cursor coordinates, attributes, character sets pointed at by G0, G1) */
645 k8t_tmCursor(term
, K8T_CURSOR_SAVE
);
647 case '8': /* DECRC -- Restore Cursor */
649 k8t_tmCursor(term
, K8T_CURSOR_LOAD
);
651 case 'F': /* Cursor to lower left corner of screen */
652 k8t_tmMoveTo(term
, 0, term
->row
-1);
654 case 'Z': /* DEC private identification */
655 k8t_ttyWriteStr(term
, "\x1b[?1;2c");
658 fprintf(stderr
, "erresc: unknown sequence ESC 0x%02X ('%c')\n", (uint8_t)ascii
, isprint(ascii
)?ascii
:'.');
664 //if (term->sel.bx != -1 && K8T_BETWEEN(term->c.y, term->sel.by, term->sel.ey)) term->sel.bx = -1;
666 if (term
->needConv
&& K8T_ISGFX(term
->c
.attr
.attr
)) {
669 k8t_UTF8Decode(&cc
, c
);
670 if (cc
< 32 || cc
>= 127) break; //FIXME: nothing at all?
672 if ((unsigned char)ascii
< 32 || ascii
== 127) break; // seems that this chars are empty too
675 if (k8t_tmDoWrap(term
) < 0) break; // wrapping, but wrap is off, don't want more chars
676 k8t_tmSetChar(term
, c
);
677 if (term
->c
.x
+1 < term
->col
) k8t_tmMoveTo(term
, term
->c
.x
+1, term
->c
.y
); else term
->c
.state
|= K8T_CURSOR_WRAPNEXT
;