rename the project into madtty.
[apps/madtty.git] / inject.c
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.
6
7 Please refer to the COPYING file for more information.
8
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.
13
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
17
18 Copyright (c) 2004 Bruno T. C. de Oliveira
19 */
20
21
22 #include "rote.h"
23 #include "roteprivate.h"
24 #include "inject_csi.h"
25 #include <string.h>
26 #include <stdio.h>
27
28 static void cursor_line_down(RoteTerm *rt) {
29    int i;
30    rt->crow++;
31    rt->curpos_dirty = true;
32    if (rt->crow <= rt->pd->scrollbottom) return;
33
34    /* must scroll the scrolling region up by 1 line, and put cursor on 
35     * last line of it */
36    rt->crow = rt->pd->scrollbottom;
37    
38    for (i = rt->pd->scrolltop; i < rt->pd->scrollbottom; i++) {
39       rt->line_dirty[i] = true;
40       memcpy(rt->cells[i], rt->cells[i+1], sizeof(RoteCell) * rt->cols);
41    }
42       
43    rt->line_dirty[rt->pd->scrollbottom] = true;
44
45    /* clear last row of the scrolling region */
46    for (i = 0; i < rt->cols; i++) {
47       rt->cells[rt->pd->scrollbottom][i].ch = 0x20;
48       rt->cells[rt->pd->scrollbottom][i].attr = 0x70;
49    }
50
51 }
52
53 static void cursor_line_up(RoteTerm *rt) {
54    int i;
55    rt->crow--;
56    rt->curpos_dirty = true;
57    if (rt->crow >= rt->pd->scrolltop) return;
58
59    /* must scroll the scrolling region up by 1 line, and put cursor on 
60     * first line of it */
61    rt->crow = rt->pd->scrolltop;
62    
63    for (i = rt->pd->scrollbottom; i > rt->pd->scrolltop; i--) {
64       rt->line_dirty[i] = true;
65       memcpy(rt->cells[i], rt->cells[i-1], sizeof(RoteCell) * rt->cols);
66    }
67       
68    rt->line_dirty[rt->pd->scrolltop] = true;
69
70    /* clear first row of the scrolling region */
71    for (i = 0; i < rt->cols; i++) {
72       rt->cells[rt->pd->scrolltop][i].ch = 0x20;
73       rt->cells[rt->pd->scrolltop][i].attr = 0x70;
74    }
75
76 }
77
78 static inline void put_normal_char(RoteTerm *rt, char c) {
79    if (rt->ccol >= rt->cols) {
80       rt->ccol = 0;
81       cursor_line_down(rt);
82    }
83
84    if (rt->insert) {
85        int i;
86
87        for(i = rt->cols - 1; i >= rt->ccol+1; i--)
88            rt->cells[rt->crow][i] = rt->cells[rt->crow][i-1];
89    }
90
91    rt->cells[rt->crow][rt->ccol].ch = c;
92    rt->cells[rt->crow][rt->ccol].attr = rt->curattr;
93    rt->ccol++;
94
95    rt->line_dirty[rt->crow] = true;
96    rt->curpos_dirty = true;
97 }
98
99 static inline void put_graphmode_char(RoteTerm *rt, char c) {
100    char nc;
101    /* do some very pitiful translation to regular ascii chars */
102    switch (c) {
103       case 'j': case 'k': case 'l': case 'm': case 'n': case 't': 
104                                     case 'u': case 'v': case 'w':
105          nc = '+'; break;
106       case 'x':
107          nc = '|'; break;
108       default:
109          nc = '%';
110    }
111
112    put_normal_char(rt, nc);
113 }
114
115 static inline void new_escape_sequence(RoteTerm *rt) {
116    rt->pd->escaped = true;
117    rt->pd->esbuf_len = 0;
118    rt->pd->esbuf[0] = '\0';
119 }
120
121 static inline void cancel_escape_sequence(RoteTerm *rt) {
122    rt->pd->escaped = false;
123    rt->pd->esbuf_len = 0;
124    rt->pd->esbuf[0] = '\0';
125 }
126
127 static void handle_control_char(RoteTerm *rt, char c) {
128    switch (c) {
129       case '\r': rt->ccol = 0; break; /* carriage return */
130       case '\n':  /* line feed */
131          rt->ccol = 0; cursor_line_down(rt);
132          rt->curpos_dirty = true;
133          break;
134       case '\b': /* backspace */
135          if (rt->ccol > 0) rt->ccol--;
136          rt->curpos_dirty = true;
137          break;
138       case '\t': /* tab */
139          rt->ccol += 8 - (rt->ccol % 8);
140          clamp_cursor_to_bounds(rt);
141          break;
142       case '\x1B': /* begin escape sequence (aborting previous one if any) */
143          new_escape_sequence(rt);
144          break;
145       case '\x0E': /* enter graphical character mode */
146          rt->pd->graphmode = true;
147          break;
148       case '\x0F': /* exit graphical character mode */
149          rt->pd->graphmode = false;
150          break;
151       case '\x9B': /* CSI character. Equivalent to ESC [ */
152          new_escape_sequence(rt);
153          rt->pd->esbuf[rt->pd->esbuf_len++] = '[';
154          break;
155       case '\x18': case '\x1A': /* these interrupt escape sequences */
156          cancel_escape_sequence(rt);
157          break;
158       case '\a': /* bell */
159          /* do nothing for now... maybe a visual bell would be nice? */
160          break;
161       #ifdef DEBUG
162       default:
163          fprintf(stderr, "Unrecognized control char: %d (^%c)\n", c, c + '@');
164          break;
165       #endif
166    }
167 }
168
169 static inline bool is_valid_csi_ender(char c) {
170    return (c >= 'a' && c <= 'z') ||
171           (c >= 'A' && c <= 'Z') ||
172           c == '@' || c == '`';
173 }
174
175 static void try_interpret_escape_seq(RoteTerm *rt) {
176    char firstchar = rt->pd->esbuf[0];
177    char lastchar  = rt->pd->esbuf[rt->pd->esbuf_len-1];
178
179    if (!firstchar) return;  /* too early to do anything */
180
181    if (rt->pd->handler) {
182       /* call custom handler */
183       #ifdef DEBUG
184       fprintf(stderr, "Calling custom handler for ES <%s>.\n", rt->pd->esbuf);
185       #endif
186
187       int answer = (*(rt->pd->handler))(rt, rt->pd->esbuf);
188       if (answer == ROTE_HANDLERESULT_OK) {
189          /* successfully handled */
190          #ifdef DEBUG
191          fprintf(stderr, "Handler returned OK. Done with escape sequence.\n");
192          #endif
193
194          cancel_escape_sequence(rt);
195          return;
196       }
197       else if (answer == ROTE_HANDLERESULT_NOTYET) {
198          /* handler might handle it when more characters are appended to 
199           * it. So for now we don't interpret it */
200          #ifdef DEBUG
201          fprintf(stderr, "Handler returned NOTYET. Waiting for more chars.\n");
202          #endif
203
204          return;
205       }
206    
207       /* If we got here then answer == ROTE_HANDLERESULT_NOWAY */
208       /* handler said it can't handle that escape sequence,
209        * but we can still try handling it ourselves, so 
210        * we proceed normally. */
211       #ifdef DEBUG
212       fprintf(stderr, "Handler returned NOWAY. Trying our handlers.\n");
213       #endif
214    }
215
216    /* interpret ESC-M as reverse line-feed */
217    if (firstchar == 'M') {
218       cursor_line_up(rt);
219       cancel_escape_sequence(rt);
220       return;
221    }
222
223    if (firstchar != '[' && firstchar != ']') {
224       /* unrecognized escape sequence. Let's forget about it. */
225       #ifdef DEBUG
226       fprintf(stderr, "Unrecognized ES: <%s>\n", rt->pd->esbuf);
227       #endif
228
229       cancel_escape_sequence(rt);
230       return;
231    }
232
233    if (firstchar == '[' && is_valid_csi_ender(lastchar)) {
234       /* we have a csi escape sequence: interpret it */
235       rote_es_interpret_csi(rt);
236       cancel_escape_sequence(rt);
237    }
238    else if (firstchar == ']' && lastchar == '\a') {
239       /* we have an xterm escape sequence: interpret it */
240
241       /* rote_es_interpret_xterm_es(rt);     -- TODO!*/
242       #ifdef DEBUG
243       fprintf(stderr, "Ignored XTerm ES.\n");
244       #endif
245       cancel_escape_sequence(rt);
246    }
247
248    /* if the escape sequence took up all available space and could
249     * not yet be parsed, abort it */
250    if (rt->pd->esbuf_len + 1 >= ESEQ_BUF_SIZE) cancel_escape_sequence(rt);
251 }
252    
253 void rote_vt_inject(RoteTerm *rt, const char *data, int len) {
254    int i;
255    for (i = 0; i < len; i++, data++) {
256       if (*data == 0) continue;  /* completely ignore NUL */
257       if (*data >= 1 && *data <= 31) {
258          handle_control_char(rt, *data);
259          continue;
260       }
261
262       if (rt->pd->escaped && rt->pd->esbuf_len < ESEQ_BUF_SIZE) {
263          /* append character to ongoing escape sequence */
264          rt->pd->esbuf[rt->pd->esbuf_len] = *data;
265          rt->pd->esbuf[++rt->pd->esbuf_len] = 0;
266
267          try_interpret_escape_seq(rt);
268       }
269       else if (rt->pd->graphmode)
270          put_graphmode_char(rt, *data);
271       else
272          put_normal_char(rt, *data);
273    }
274 }
275