2 #include "roteprivate.h"
3 #include "inject_csi.h"
6 static void cursor_line_down(RoteTerm *rt) {
9 rt->curpos_dirty = true;
10 if (rt->crow <= rt->pd->scrollbottom) return;
12 /* must scroll the scrolling region up by 1 line, and put cursor on
14 rt->crow = rt->pd->scrollbottom;
16 for (i = rt->pd->scrolltop; i < rt->pd->scrollbottom; i++) {
17 rt->line_dirty[i] = true;
18 memcpy(rt->cells[i], rt->cells[i+1], sizeof(RoteCell) * rt->cols);
21 rt->line_dirty[rt->pd->scrollbottom] = true;
23 /* clear last row of the scrolling region */
24 for (i = 0; i < rt->cols; i++) {
25 rt->cells[rt->pd->scrollbottom][i].ch = 0x20;
26 rt->cells[rt->pd->scrollbottom][i].attr = 0x70;
31 static void cursor_line_up(RoteTerm *rt) {
34 rt->curpos_dirty = true;
35 if (rt->crow >= rt->pd->scrolltop) return;
37 /* must scroll the scrolling region up by 1 line, and put cursor on
39 rt->crow = rt->pd->scrolltop;
41 for (i = rt->pd->scrollbottom; i > rt->pd->scrolltop; i--) {
42 rt->line_dirty[i] = true;
43 memcpy(rt->cells[i], rt->cells[i-1], sizeof(RoteCell) * rt->cols);
46 rt->line_dirty[rt->pd->scrolltop] = true;
48 /* clear first row of the scrolling region */
49 for (i = 0; i < rt->cols; i++) {
50 rt->cells[rt->pd->scrolltop][i].ch = 0x20;
51 rt->cells[rt->pd->scrolltop][i].attr = 0x70;
56 static inline void put_normal_char(RoteTerm *rt, char c) {
57 rt->cells[rt->crow][rt->ccol].ch = c;
58 rt->cells[rt->crow][rt->ccol].attr = rt->curattr;
61 rt->line_dirty[rt->crow] = true;
62 rt->curpos_dirty = true;
64 if (rt->ccol >= rt->cols) {
70 static inline void put_graphmode_char(RoteTerm *rt, char c) {
72 /* do some very pitiful translation to regular ascii chars */
74 case 'j': case 'k': case 'l': case 'm': case 'n': case 't':
75 case 'u': case 'v': case 'w':
83 put_normal_char(rt, nc);
86 static inline void new_escape_sequence(RoteTerm *rt) {
87 rt->pd->escaped = true;
88 rt->pd->esbuf_len = 0;
89 rt->pd->esbuf[0] = '\0';
92 static inline void cancel_escape_sequence(RoteTerm *rt) {
93 rt->pd->escaped = false;
94 rt->pd->esbuf_len = 0;
95 rt->pd->esbuf[0] = '\0';
98 static void handle_control_char(RoteTerm *rt, char c) {
100 case '\r': rt->ccol = 0; break; /* carriage return */
101 case '\n': /* line feed */
102 rt->ccol = 0; cursor_line_down(rt);
103 rt->curpos_dirty = true;
105 case '\b': /* backspace */
106 if (rt->ccol > 0) rt->ccol--;
107 rt->curpos_dirty = true;
110 while (rt->ccol % 8) put_normal_char(rt, ' ');
112 case '\x1B': /* begin escape sequence (aborting previous one if any) */
113 new_escape_sequence(rt);
115 case '\x0E': /* enter graphical character mode */
116 rt->pd->graphmode = true;
118 case '\x0F': /* exit graphical character mode */
119 rt->pd->graphmode = false;
121 case '\x9B': /* CSI character. Equivalent to ESC [ */
122 new_escape_sequence(rt);
123 rt->pd->esbuf[rt->pd->esbuf_len++] = '[';
125 case '\x18': case '\x1A': /* these interrupt escape sequences */
126 cancel_escape_sequence(rt);
128 case '\a': /* bell */
129 /* do nothing for now... maybe a visual bell would be nice? */
133 fprintf(stderr, "Unrecognized control char: %d (^%c)\n", c, c + '@');
139 static inline bool is_valid_csi_ender(char c) {
140 return (c >= 'a' && c <= 'z') ||
141 (c >= 'A' && c <= 'Z') ||
142 c == '@' || c == '`';
145 static void try_interpret_escape_seq(RoteTerm *rt) {
146 char firstchar = rt->pd->esbuf[0];
147 char lastchar = rt->pd->esbuf[rt->pd->esbuf_len-1];
149 if (!firstchar) return; /* too early to do anything */
151 if (rt->pd->handler) {
152 /* call custom handler */
154 fprintf(stderr, "Calling custom handler for ES <%s>.\n", rt->pd->esbuf);
157 int answer = (*(rt->pd->handler))(rt, rt->pd->esbuf);
158 if (answer == ROTE_HANDLERESULT_OK) {
159 /* successfully handled */
161 fprintf(stderr, "Handler returned OK. Done with escape sequence.\n");
164 cancel_escape_sequence(rt);
167 else if (answer == ROTE_HANDLERESULT_NOTYET) {
168 /* handler might handle it when more characters are appended to
169 * it. So for now we don't interpret it */
171 fprintf(stderr, "Handler returned NOTYET. Waiting for more chars.\n");
177 /* If we got here then answer == ROTE_HANDLERESULT_NOWAY */
178 /* handler said it can't handle that escape sequence,
179 * but we can still try handling it ourselves, so
180 * we proceed normally. */
182 fprintf(stderr, "Handler returned NOWAY. Trying our handlers.\n");
186 /* interpret ESC-M as reverse line-feed */
187 if (firstchar == 'M') {
189 cancel_escape_sequence(rt);
193 if (firstchar != '[' && firstchar != ']') {
194 /* unrecognized escape sequence. Let's forget about it. */
196 fprintf(stderr, "Unrecognized ES: <%s>\n", rt->pd->esbuf);
199 cancel_escape_sequence(rt);
203 if (firstchar == '[' && is_valid_csi_ender(lastchar)) {
204 /* we have a csi escape sequence: interpret it */
205 rote_es_interpret_csi(rt);
206 cancel_escape_sequence(rt);
208 else if (firstchar == ']' && lastchar == '\a') {
209 /* we have an xterm escape sequence: interpret it */
211 /* rote_es_interpret_xterm_es(rt); -- TODO!*/
213 fprintf(stderr, "Ignored XTerm ES.\n");
215 cancel_escape_sequence(rt);
218 /* if the escape sequence took up all available space and could
219 * not yet be parsed, abort it */
220 if (rt->pd->esbuf_len + 1 >= ESEQ_BUF_SIZE) cancel_escape_sequence(rt);
223 void rote_vt_inject(RoteTerm *rt, const char *data, int len) {
225 for (i = 0; i < len; i++, data++) {
226 if (*data == 0) continue; /* completely ignore NUL */
227 if (*data >= 1 && *data <= 31) {
228 handle_control_char(rt, *data);
232 if (rt->pd->escaped && rt->pd->esbuf_len < ESEQ_BUF_SIZE) {
233 /* append character to ongoing escape sequence */
234 rt->pd->esbuf[rt->pd->esbuf_len] = *data;
235 rt->pd->esbuf[++rt->pd->esbuf_len] = 0;
237 try_interpret_escape_seq(rt);
239 else if (rt->pd->graphmode)
240 put_graphmode_char(rt, *data);
242 put_normal_char(rt, *data);