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 © 2004 Bruno T. C. de Oliveira
19 Copyright © 2006 Pierre Habouzit
26 #include "roteprivate.h"
27 #include "inject_csi.h"
29 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 rt->cells[rt->pd->scrollbottom][i].ch = 0x20;
50 rt->cells[rt->pd->scrollbottom][i].attr = 0x70;
55 static void cursor_line_up(RoteTerm *rt)
59 rt->curpos_dirty = true;
60 if (rt->crow >= rt->pd->scrolltop) return;
62 /* must scroll the scrolling region up by 1 line, and put cursor on
64 rt->crow = rt->pd->scrolltop;
66 for (i = rt->pd->scrollbottom; i > rt->pd->scrolltop; i--) {
67 rt->line_dirty[i] = true;
68 memcpy(rt->cells[i], rt->cells[i-1], sizeof(RoteCell) * rt->cols);
71 rt->line_dirty[rt->pd->scrolltop] = true;
73 /* clear first row of the scrolling region */
74 for (i = 0; i < rt->cols; i++) {
75 rt->cells[rt->pd->scrolltop][i].ch = 0x20;
76 rt->cells[rt->pd->scrolltop][i].attr = 0x70;
81 static inline void put_normal_char(RoteTerm *rt, char c)
83 if (rt->ccol >= rt->cols) {
91 for(i = rt->cols - 1; i >= rt->ccol+1; i--)
92 rt->cells[rt->crow][i] = rt->cells[rt->crow][i-1];
95 rt->cells[rt->crow][rt->ccol].ch = c;
96 rt->cells[rt->crow][rt->ccol].attr = rt->curattr;
99 rt->line_dirty[rt->crow] = true;
100 rt->curpos_dirty = true;
103 static inline void put_graphmode_char(RoteTerm *rt, char c)
106 /* do some very pitiful translation to regular ascii chars */
108 case 'j': case 'k': case 'l': case 'm': case 'n': case 't':
109 case 'u': case 'v': case 'w':
117 put_normal_char(rt, nc);
120 static inline void new_escape_sequence(RoteTerm *rt)
122 rt->pd->escaped = true;
123 rt->pd->esbuf_len = 0;
124 rt->pd->esbuf[0] = '\0';
127 static inline void cancel_escape_sequence(RoteTerm *rt)
129 rt->pd->escaped = false;
130 rt->pd->esbuf_len = 0;
131 rt->pd->esbuf[0] = '\0';
134 static void handle_control_char(RoteTerm *rt, char c)
137 case '\r': rt->ccol = 0; break; /* carriage return */
138 case '\n': /* line feed */
139 rt->ccol = 0; cursor_line_down(rt);
140 rt->curpos_dirty = true;
142 case '\b': /* backspace */
143 if (rt->ccol > 0) rt->ccol--;
144 rt->curpos_dirty = true;
147 rt->ccol += 8 - (rt->ccol % 8);
148 clamp_cursor_to_bounds(rt);
150 case '\x1B': /* begin escape sequence (aborting previous one if any) */
151 new_escape_sequence(rt);
153 case '\x0E': /* enter graphical character mode */
154 rt->pd->graphmode = true;
156 case '\x0F': /* exit graphical character mode */
157 rt->pd->graphmode = false;
159 case '\x9B': /* CSI character. Equivalent to ESC [ */
160 new_escape_sequence(rt);
161 rt->pd->esbuf[rt->pd->esbuf_len++] = '[';
163 case '\x18': case '\x1A': /* these interrupt escape sequences */
164 cancel_escape_sequence(rt);
166 case '\a': /* bell */
167 /* do nothing for now... maybe a visual bell would be nice? */
171 fprintf(stderr, "Unrecognized control char: %d (^%c)\n", c, c + '@');
177 static inline bool is_valid_csi_ender(char c)
179 return (c >= 'a' && c <= 'z') ||
180 (c >= 'A' && c <= 'Z') ||
181 c == '@' || c == '`';
184 static void try_interpret_escape_seq(RoteTerm *rt)
186 char firstchar = rt->pd->esbuf[0];
187 char lastchar = rt->pd->esbuf[rt->pd->esbuf_len-1];
189 if (!firstchar) return; /* too early to do anything */
191 if (rt->pd->handler) {
192 /* call custom handler */
194 fprintf(stderr, "Calling custom handler for ES <%s>.\n", rt->pd->esbuf);
197 int answer = (*(rt->pd->handler))(rt, rt->pd->esbuf);
198 if (answer == ROTE_HANDLERESULT_OK) {
199 /* successfully handled */
201 fprintf(stderr, "Handler returned OK. Done with escape sequence.\n");
204 cancel_escape_sequence(rt);
207 else if (answer == ROTE_HANDLERESULT_NOTYET) {
208 /* handler might handle it when more characters are appended to
209 * it. So for now we don't interpret it */
211 fprintf(stderr, "Handler returned NOTYET. Waiting for more chars.\n");
217 /* If we got here then answer == ROTE_HANDLERESULT_NOWAY */
218 /* handler said it can't handle that escape sequence,
219 * but we can still try handling it ourselves, so
220 * we proceed normally. */
222 fprintf(stderr, "Handler returned NOWAY. Trying our handlers.\n");
226 /* interpret ESC-M as reverse line-feed */
227 if (firstchar == 'M') {
229 cancel_escape_sequence(rt);
233 if (firstchar != '[' && firstchar != ']') {
234 /* unrecognized escape sequence. Let's forget about it. */
236 fprintf(stderr, "Unrecognized ES: <%s>\n", rt->pd->esbuf);
239 cancel_escape_sequence(rt);
243 if (firstchar == '[' && is_valid_csi_ender(lastchar)) {
244 /* we have a csi escape sequence: interpret it */
245 rote_es_interpret_csi(rt);
246 cancel_escape_sequence(rt);
248 else if (firstchar == ']' && lastchar == '\a') {
249 /* we have an xterm escape sequence: interpret it */
251 /* rote_es_interpret_xterm_es(rt); -- TODO!*/
253 fprintf(stderr, "Ignored XTerm ES.\n");
255 cancel_escape_sequence(rt);
258 /* if the escape sequence took up all available space and could
259 * not yet be parsed, abort it */
260 if (rt->pd->esbuf_len + 1 >= ESEQ_BUF_SIZE) cancel_escape_sequence(rt);
263 void rote_vt_inject(RoteTerm *rt, const char *data, int len)
266 for (i = 0; i < len; i++, data++) {
267 if (*data == 0) continue; /* completely ignore NUL */
268 if (*data >= 1 && *data <= 31) {
269 handle_control_char(rt, *data);
273 if (rt->pd->escaped && rt->pd->esbuf_len < ESEQ_BUF_SIZE) {
274 /* append character to ongoing escape sequence */
275 rt->pd->esbuf[rt->pd->esbuf_len] = *data;
276 rt->pd->esbuf[++rt->pd->esbuf_len] = 0;
278 try_interpret_escape_seq(rt);
280 else if (rt->pd->graphmode)
281 put_graphmode_char(rt, *data);
283 put_normal_char(rt, *data);