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