Hopefully fix compilation on *BSD.
[librote.git] / inject.c
blob08c1a914170c407d94a85dc54501090f35e44af8
1 /*
2 LICENSE INFORMATION:
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
22 #include "rote.h"
23 #include "utf8.h"
24 #include "roteprivate.h"
25 #include "inject_csi.h"
26 #include <string.h>
27 #include <stdbool.h>
28 #include <stdio.h>
30 static void cursor_line_down(RoteTerm *rt) {
31 int i;
32 rt->crow++;
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
37 * last line of it */
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) {
55 int i;
56 rt->crow--;
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
61 * first line of it */
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) {
79 #ifdef USE_UTF8
80 const ssize_t width = wcwidth (c);
81 if (width < 0) return;
82 if (rt->ccol + width > rt->cols) {
83 #else
84 if (rt->ccol >= rt->cols) {
85 #endif
86 rt->ccol = 0;
87 cursor_line_down(rt);
90 if (rt->insert) {
91 int i;
93 for(i = rt->cols - 1; i >= rt->ccol+1; i--)
94 rt->cells[rt->crow][i] = rt->cells[rt->crow][i-1];
97 #ifdef USE_UTF8
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);
101 int i;
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;
106 if (i == 0) {
107 rt->cells[rt->crow][rt->ccol + i].ch = c;
108 } else {
109 rt->cells[rt->crow][rt->ccol + i].ch = 0x20;
111 rt->cells[rt->crow][rt->ccol + i].attr = rt->curattr;
113 rt->ccol += width;
114 #else
115 # ifdef USE_NCURSES
116 /* translate from Code Page 437 to ACS_* values */
117 char_t ch = (unsigned char)c;
118 if(ch >= 128){
119 switch(ch){
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;
131 /* --- */
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;
136 /* --- */
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;
147 # endif
148 rt->cells[rt->crow][rt->ccol].ch = c;
149 rt->cells[rt->crow][rt->ccol].attr = rt->curattr;
150 rt->ccol++;
151 #endif
152 rt->line_dirty[rt->crow] = true;
153 rt->curpos_dirty = true;
156 static inline void put_graphmode_char(RoteTerm *rt, char_t c) {
157 char_t nc;
158 /* do some very pitiful translation to regular ascii chars */
159 switch (c) {
160 case 'j': case 'k': case 'l': case 'm': case 'n': case 't':
161 case 'u': case 'v': case 'w':
162 nc = '+'; break;
163 case 'x':
164 nc = '|'; break;
165 default:
166 nc = '%';
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) {
185 switch (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;
190 break;
191 case '\b': /* backspace */
192 if (rt->ccol > 0) rt->ccol--;
193 rt->curpos_dirty = true;
194 break;
195 case '\t': /* tab */
196 rt->ccol += 8 - (rt->ccol % 8);
197 clamp_cursor_to_bounds(rt);
198 break;
199 case '\x1B': /* begin escape sequence (aborting previous one if any) */
200 new_escape_sequence(rt);
201 break;
202 case '\x0E': /* enter graphical character mode */
203 rt->pd->graphmode = true;
204 break;
205 case '\x0F': /* exit graphical character mode */
206 rt->pd->graphmode = false;
207 break;
208 case '\x9B': /* CSI character. Equivalent to ESC [ */
209 new_escape_sequence(rt);
210 rt->pd->esbuf[rt->pd->esbuf_len++] = '[';
211 break;
212 case '\x18': case '\x1A': /* these interrupt escape sequences */
213 cancel_escape_sequence(rt);
214 break;
215 case '\a': /* bell */
216 /* do nothing for now... maybe a visual bell would be nice? */
217 break;
218 #ifdef DEBUG
219 default:
220 fprintf(stderr, "Unrecognized control char: %d (^%c)\n", c, c + '@');
221 break;
222 #endif
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 */
240 #ifdef DEBUG
241 fprintf(stderr, "Calling custom handler for ES <%s>.\n", rt->pd->esbuf);
242 #endif
244 int answer = (*(rt->pd->handler))(rt, rt->pd->esbuf);
245 if (answer == ROTE_HANDLERESULT_OK) {
246 /* successfully handled */
247 #ifdef DEBUG
248 fprintf(stderr, "Handler returned OK. Done with escape sequence.\n");
249 #endif
251 cancel_escape_sequence(rt);
252 return;
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 */
257 #ifdef DEBUG
258 fprintf(stderr, "Handler returned NOTYET. Waiting for more chars.\n");
259 #endif
261 return;
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. */
268 #ifdef DEBUG
269 fprintf(stderr, "Handler returned NOWAY. Trying our handlers.\n");
270 #endif
273 /* interpret ESC-M as reverse line-feed */
274 if (firstchar == 'M') {
275 cursor_line_up(rt);
276 cancel_escape_sequence(rt);
277 return;
280 if (firstchar != '[' && firstchar != ']') {
281 /* unrecognized escape sequence. Let's forget about it. */
282 #ifdef DEBUG
283 fprintf(stderr, "Unrecognized ES: <%s>\n", rt->pd->esbuf);
284 #endif
286 cancel_escape_sequence(rt);
287 return;
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!*/
299 #ifdef DEBUG
300 fprintf(stderr, "Ignored XTerm ES.\n");
301 #endif
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) {
311 int i;
312 for (i = 0; i < len; i++, data++) {
313 #ifdef USE_UTF8
314 wchar_t wc;
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;
334 } else {
335 continue;
337 } else {
338 const unsigned char c = *data;
339 if (c < 0x80) {
340 rt->pd->utf8_index = 0;
341 rt->pd->utf8_length = 1;
342 mbc[0] = c;
343 mbc_length = 1;
344 wc = c;
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;
349 continue;
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;
354 continue;
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;
359 continue;
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;
364 continue;
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;
369 continue;
370 } else {
371 // Invalid utf8 sequence, how should I do?
372 rt->pd->utf8_index = 0;
373 rt->pd->utf8_length = 1;
374 mbc[0] = c;
375 mbc_length = 1;
376 wc = c;
380 if (wc == 0) continue; /* completely ignore NUL */
382 if (wc >= 1 && wc <= 31) {
383 handle_control_char(rt, wc);
384 continue;
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);
393 } else {
394 cancel_escape_sequence(rt);
395 break;
397 } else if (rt->pd->graphmode)
398 put_graphmode_char(rt, wc);
399 else
400 put_normal_char(rt, wc);
401 #else
402 if (*data == 0) continue; /* completely ignore NUL */
403 if (*data >= 1 && *data <= 31) {
404 handle_control_char(rt, *data);
405 continue;
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);
417 else
418 put_normal_char(rt, *data);
419 #endif