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 (c) 2004 Bruno T. C. de Oliveira
24 #include "roteprivate.h"
25 #include "inject_csi.h"
30 static void cursor_line_down(RoteTerm
*rt
) {
33 rt
->curpos_dirty
= true;
34 if (rt
->crow
<= rt
->pd
->scrollbottom
) return;
36 /* must scroll the scrolling region up by 1 line, and put cursor on
38 rt
->crow
= rt
->pd
->scrollbottom
;
40 for (i
= rt
->pd
->scrolltop
; i
< rt
->pd
->scrollbottom
; i
++) {
41 rt
->line_dirty
[i
] = true;
42 memcpy(rt
->cells
[i
], rt
->cells
[i
+1], sizeof(RoteCell
) * rt
->cols
);
45 rt
->line_dirty
[rt
->pd
->scrollbottom
] = true;
47 /* clear last row of the scrolling region */
48 for (i
= 0; i
< rt
->cols
; i
++) {
49 clear_cell(&rt
->cells
[rt
->pd
->scrollbottom
][i
]);
54 static void cursor_line_up(RoteTerm
*rt
) {
57 rt
->curpos_dirty
= true;
58 if (rt
->crow
>= rt
->pd
->scrolltop
) return;
60 /* must scroll the scrolling region up by 1 line, and put cursor on
62 rt
->crow
= rt
->pd
->scrolltop
;
64 for (i
= rt
->pd
->scrollbottom
; i
> rt
->pd
->scrolltop
; i
--) {
65 rt
->line_dirty
[i
] = true;
66 memcpy(rt
->cells
[i
], rt
->cells
[i
-1], sizeof(RoteCell
) * rt
->cols
);
69 rt
->line_dirty
[rt
->pd
->scrolltop
] = true;
71 /* clear first row of the scrolling region */
72 for (i
= 0; i
< rt
->cols
; i
++) {
73 clear_cell(&rt
->cells
[rt
->pd
->scrolltop
][i
]);
78 static inline void put_normal_char(RoteTerm
*rt
, char_t c
) {
80 const ssize_t width
= wcwidth (c
);
81 if (width
< 0) return;
82 if (rt
->ccol
+ width
> rt
->cols
) {
84 if (rt
->ccol
>= rt
->cols
) {
93 for(i
= rt
->cols
- 1; i
>= rt
->ccol
+1; i
--)
94 rt
->cells
[rt
->crow
][i
] = rt
->cells
[rt
->crow
][i
-1];
98 rote_utf8_erase_character(rt
, rt
->crow
, rt
->ccol
);
99 if (width
> 1) rote_utf8_erase_character(rt
, rt
->crow
+ width
- 1, rt
->ccol
);
102 for (i
= 0; i
< width
; ++i
) {
103 rt
->cells
[rt
->crow
][rt
->ccol
+ i
].fcount
= width
;
104 rt
->cells
[rt
->crow
][rt
->ccol
+ i
].empty
= false;
105 rt
->cells
[rt
->crow
][rt
->ccol
+ i
].findex
= i
;
107 rt
->cells
[rt
->crow
][rt
->ccol
+ i
].ch
= c
;
109 rt
->cells
[rt
->crow
][rt
->ccol
+ i
].ch
= 0x20;
111 rt
->cells
[rt
->crow
][rt
->ccol
+ i
].attr
= rt
->curattr
;
116 /* translate from Code Page 437 to ACS_* values */
117 char_t ch
= (unsigned char)c
;
120 case 218: c
= ACS_ULCORNER
; break;
121 case 192: c
= ACS_LLCORNER
; break;
122 case 191: c
= ACS_URCORNER
; break;
123 case 217: c
= ACS_LRCORNER
; break;
124 case 195: c
= ACS_LTEE
; break;
125 case 180: c
= ACS_RTEE
; break;
126 case 193: c
= ACS_BTEE
; break;
127 case 194: c
= ACS_TTEE
; break;
128 case 196: c
= ACS_HLINE
; break;
129 case 179: c
= ACS_VLINE
; break;
130 case 197: c
= ACS_PLUS
; break;
132 case 177: c
= ACS_CKBOARD
; break;
133 case 248: c
= ACS_DEGREE
; break;
134 case 241: c
= ACS_PLMINUS
; break;
135 case 254: c
= ACS_BULLET
; break;
137 case 176: c
= ACS_BOARD
; break;
138 case 206: c
= ACS_LANTERN
; break;
139 case 219: c
= ACS_BLOCK
; break;
140 case 243: c
= ACS_LEQUAL
; break;
141 case 242: c
= ACS_GEQUAL
; break;
142 case 227: c
= ACS_PI
; break;
143 case 216: c
= ACS_NEQUAL
; break;
144 case 156: c
= ACS_STERLING
; break;
148 rt
->cells
[rt
->crow
][rt
->ccol
].ch
= c
;
149 rt
->cells
[rt
->crow
][rt
->ccol
].attr
= rt
->curattr
;
152 rt
->line_dirty
[rt
->crow
] = true;
153 rt
->curpos_dirty
= true;
156 static inline void put_graphmode_char(RoteTerm
*rt
, char_t c
) {
158 /* do some very pitiful translation to regular ascii chars */
160 case 'j': case 'k': case 'l': case 'm': case 'n': case 't':
161 case 'u': case 'v': case 'w':
169 put_normal_char(rt
, nc
);
172 static inline 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 inline void cancel_escape_sequence(RoteTerm
*rt
) {
179 rt
->pd
->escaped
= false;
180 rt
->pd
->esbuf_len
= 0;
181 rt
->pd
->esbuf
[0] = '\0';
184 static void handle_control_char(RoteTerm
*rt
, char c
) {
186 case '\r': rt
->ccol
= 0; break; /* carriage return */
187 case '\n': /* line feed */
188 rt
->ccol
= 0; cursor_line_down(rt
);
189 rt
->curpos_dirty
= true;
191 case '\b': /* backspace */
192 if (rt
->ccol
> 0) rt
->ccol
--;
193 rt
->curpos_dirty
= true;
196 rt
->ccol
+= 8 - (rt
->ccol
% 8);
197 clamp_cursor_to_bounds(rt
);
199 case '\x1B': /* begin escape sequence (aborting previous one if any) */
200 new_escape_sequence(rt
);
202 case '\x0E': /* enter graphical character mode */
203 rt
->pd
->graphmode
= true;
205 case '\x0F': /* exit graphical character mode */
206 rt
->pd
->graphmode
= false;
208 case '\x9B': /* CSI character. Equivalent to ESC [ */
209 new_escape_sequence(rt
);
210 rt
->pd
->esbuf
[rt
->pd
->esbuf_len
++] = '[';
212 case '\x18': case '\x1A': /* these interrupt escape sequences */
213 cancel_escape_sequence(rt
);
215 case '\a': /* bell */
216 /* do nothing for now... maybe a visual bell would be nice? */
220 fprintf(stderr
, "Unrecognized control char: %d (^%c)\n", c
, c
+ '@');
226 static inline bool is_valid_csi_ender(char c
) {
227 return (c
>= 'a' && c
<= 'z') ||
228 (c
>= 'A' && c
<= 'Z') ||
229 c
== '@' || c
== '`';
232 static void try_interpret_escape_seq(RoteTerm
*rt
) {
233 char firstchar
= rt
->pd
->esbuf
[0];
234 char lastchar
= rt
->pd
->esbuf
[rt
->pd
->esbuf_len
-1];
236 if (!firstchar
) return; /* too early to do anything */
238 if (rt
->pd
->handler
) {
239 /* call custom handler */
241 fprintf(stderr
, "Calling custom handler for ES <%s>.\n", rt
->pd
->esbuf
);
244 int answer
= (*(rt
->pd
->handler
))(rt
, rt
->pd
->esbuf
);
245 if (answer
== ROTE_HANDLERESULT_OK
) {
246 /* successfully handled */
248 fprintf(stderr
, "Handler returned OK. Done with escape sequence.\n");
251 cancel_escape_sequence(rt
);
254 else if (answer
== ROTE_HANDLERESULT_NOTYET
) {
255 /* handler might handle it when more characters are appended to
256 * it. So for now we don't interpret it */
258 fprintf(stderr
, "Handler returned NOTYET. Waiting for more chars.\n");
264 /* If we got here then answer == ROTE_HANDLERESULT_NOWAY */
265 /* handler said it can't handle that escape sequence,
266 * but we can still try handling it ourselves, so
267 * we proceed normally. */
269 fprintf(stderr
, "Handler returned NOWAY. Trying our handlers.\n");
273 /* interpret ESC-M as reverse line-feed */
274 if (firstchar
== 'M') {
276 cancel_escape_sequence(rt
);
280 if (firstchar
!= '[' && firstchar
!= ']') {
281 /* unrecognized escape sequence. Let's forget about it. */
283 fprintf(stderr
, "Unrecognized ES: <%s>\n", rt
->pd
->esbuf
);
286 cancel_escape_sequence(rt
);
290 if (firstchar
== '[' && is_valid_csi_ender(lastchar
)) {
291 /* we have a csi escape sequence: interpret it */
292 rote_es_interpret_csi(rt
);
293 cancel_escape_sequence(rt
);
295 else if (firstchar
== ']' && lastchar
== '\a') {
296 /* we have an xterm escape sequence: interpret it */
298 /* rote_es_interpret_xterm_es(rt); -- TODO!*/
300 fprintf(stderr
, "Ignored XTerm ES.\n");
302 cancel_escape_sequence(rt
);
305 /* if the escape sequence took up all available space and could
306 * not yet be parsed, abort it */
307 if (rt
->pd
->esbuf_len
+ 1 >= ESEQ_BUF_SIZE
) cancel_escape_sequence(rt
);
310 void rote_vt_inject(RoteTerm
*rt
, const char *data
, int len
) {
312 for (i
= 0; i
< len
; i
++, data
++) {
315 unsigned char mbc
[6];
316 size_t mbc_length
= 0;
318 if (rt
->pd
->utf8_index
> 0) {
319 rt
->pd
->utf8_buffer
[rt
->pd
->utf8_index
] = ((unsigned char) *data
) & 0x3f;
320 ++rt
->pd
->utf8_index
;
321 if (rt
->pd
->utf8_index
>= 6) {
322 memcpy (mbc
, rt
->pd
->utf8_buffer
, sizeof (char) * 6);
323 mbc_length
= rt
->pd
->utf8_length
;
324 wc
= /*(rt->pd->utf8_buffer[0] << 30) | (rt->pd->utf8_buffer[1] << 24) | */
325 (rt
->pd
->utf8_buffer
[2] << 18) | (rt
->pd
->utf8_buffer
[3] << 12) |
326 (rt
->pd
->utf8_buffer
[4] << 6) | (rt
->pd
->utf8_buffer
[5] << 0);
327 rt
->pd
->utf8_index
= 0;
328 rt
->pd
->utf8_buffer
[0] = 0;
329 rt
->pd
->utf8_buffer
[1] = 0;
330 rt
->pd
->utf8_buffer
[2] = 0;
331 rt
->pd
->utf8_buffer
[3] = 0;
332 rt
->pd
->utf8_buffer
[4] = 0;
333 rt
->pd
->utf8_buffer
[5] = 0;
338 const unsigned char c
= *data
;
340 rt
->pd
->utf8_index
= 0;
341 rt
->pd
->utf8_length
= 1;
345 } else if (0xc0 <= c
&& c
< 0xe0) {
346 rt
->pd
->utf8_buffer
[4] = c
& 0x1f;
347 rt
->pd
->utf8_index
= 5;
348 rt
->pd
->utf8_length
= 2;
350 } else if (0xe0 <= c
&& c
< 0xf0) {
351 rt
->pd
->utf8_buffer
[3] = c
& 0x0f;
352 rt
->pd
->utf8_index
= 4;
353 rt
->pd
->utf8_length
= 3;
355 } else if (0xf0 <= c
&& c
< 0xf8) {
356 rt
->pd
->utf8_buffer
[2] = c
& 0x07;
357 rt
->pd
->utf8_index
= 3;
358 rt
->pd
->utf8_length
= 4;
360 } else if (0xf8 <= c
&& c
< 0xfc) {
361 rt
->pd
->utf8_buffer
[1] = c
& 0x03;
362 rt
->pd
->utf8_index
= 2;
363 rt
->pd
->utf8_length
= 5;
365 } else if (0xfc <= c
&& c
< 0xfe) {
366 rt
->pd
->utf8_buffer
[0] = c
& 0x01;
367 rt
->pd
->utf8_index
= 1;
368 rt
->pd
->utf8_length
= 6;
371 // Invalid utf8 sequence, how should I do?
372 rt
->pd
->utf8_index
= 0;
373 rt
->pd
->utf8_length
= 1;
380 if (wc
== 0) continue; /* completely ignore NUL */
382 if (wc
>= 1 && wc
<= 31) {
383 handle_control_char(rt
, wc
);
387 if (rt
->pd
->escaped
) {
388 if (rt
->pd
->esbuf_len
+ mbc_length
< ESEQ_BUF_SIZE
) {
389 memcpy(&rt
->pd
->esbuf
[rt
->pd
->esbuf_len
], mbc
, sizeof (char) * mbc_length
);
390 rt
->pd
->esbuf
[rt
->pd
->esbuf_len
+ mbc_length
] = 0;
391 rt
->pd
->esbuf_len
+= mbc_length
;
392 try_interpret_escape_seq(rt
);
394 cancel_escape_sequence(rt
);
397 } else if (rt
->pd
->graphmode
)
398 put_graphmode_char(rt
, wc
);
400 put_normal_char(rt
, wc
);
402 if (*data
== 0) continue; /* completely ignore NUL */
403 if (*data
>= 1 && *data
<= 31) {
404 handle_control_char(rt
, *data
);
408 if (rt
->pd
->escaped
&& rt
->pd
->esbuf_len
< ESEQ_BUF_SIZE
) {
409 /* append character to ongoing escape sequence */
410 rt
->pd
->esbuf
[rt
->pd
->esbuf_len
] = *data
;
411 rt
->pd
->esbuf
[++rt
->pd
->esbuf_len
] = 0;
413 try_interpret_escape_seq(rt
);
415 else if (rt
->pd
->graphmode
)
416 put_graphmode_char(rt
, *data
);
418 put_normal_char(rt
, *data
);