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
23 #include "roteprivate.h"
24 #include "inject_csi.h"
27 static void cursor_line_down(RoteTerm
*rt
) {
30 rt
->curpos_dirty
= true;
31 if (rt
->crow
<= rt
->pd
->scrollbottom
) return;
33 /* must scroll the scrolling region up by 1 line, and put cursor on
35 rt
->crow
= rt
->pd
->scrollbottom
;
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
);
42 rt
->line_dirty
[rt
->pd
->scrollbottom
] = true;
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;
52 static void cursor_line_up(RoteTerm
*rt
) {
55 rt
->curpos_dirty
= true;
56 if (rt
->crow
>= rt
->pd
->scrolltop
) return;
58 /* must scroll the scrolling region up by 1 line, and put cursor on
60 rt
->crow
= rt
->pd
->scrolltop
;
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
);
67 rt
->line_dirty
[rt
->pd
->scrolltop
] = true;
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;
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
;
82 rt
->line_dirty
[rt
->crow
] = true;
83 rt
->curpos_dirty
= true;
85 if (rt
->ccol
>= rt
->cols
) {
91 static inline void put_graphmode_char(RoteTerm
*rt
, char c
) {
93 /* do some very pitiful translation to regular ascii chars */
95 case 'j': case 'k': case 'l': case 'm': case 'n': case 't':
96 case 'u': case 'v': case 'w':
104 put_normal_char(rt
, nc
);
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';
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';
119 static void handle_control_char(RoteTerm
*rt
, char 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;
126 case '\b': /* backspace */
127 if (rt
->ccol
> 0) rt
->ccol
--;
128 rt
->curpos_dirty
= true;
131 while (rt
->ccol
% 8) put_normal_char(rt
, ' ');
133 case '\x1B': /* begin escape sequence (aborting previous one if any) */
134 new_escape_sequence(rt
);
136 case '\x0E': /* enter graphical character mode */
137 rt
->pd
->graphmode
= true;
139 case '\x0F': /* exit graphical character mode */
140 rt
->pd
->graphmode
= false;
142 case '\x9B': /* CSI character. Equivalent to ESC [ */
143 new_escape_sequence(rt
);
144 rt
->pd
->esbuf
[rt
->pd
->esbuf_len
++] = '[';
146 case '\x18': case '\x1A': /* these interrupt escape sequences */
147 cancel_escape_sequence(rt
);
149 case '\a': /* bell */
150 /* do nothing for now... maybe a visual bell would be nice? */
154 fprintf(stderr
, "Unrecognized control char: %d (^%c)\n", c
, c
+ '@');
160 static inline bool is_valid_csi_ender(char c
) {
161 return (c
>= 'a' && c
<= 'z') ||
162 (c
>= 'A' && c
<= 'Z') ||
163 c
== '@' || c
== '`';
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];
170 if (!firstchar
) return; /* too early to do anything */
172 if (rt
->pd
->handler
) {
173 /* call custom handler */
175 fprintf(stderr
, "Calling custom handler for ES <%s>.\n", rt
->pd
->esbuf
);
178 int answer
= (*(rt
->pd
->handler
))(rt
, rt
->pd
->esbuf
);
179 if (answer
== ROTE_HANDLERESULT_OK
) {
180 /* successfully handled */
182 fprintf(stderr
, "Handler returned OK. Done with escape sequence.\n");
185 cancel_escape_sequence(rt
);
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 */
192 fprintf(stderr
, "Handler returned NOTYET. Waiting for more chars.\n");
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. */
203 fprintf(stderr
, "Handler returned NOWAY. Trying our handlers.\n");
207 /* interpret ESC-M as reverse line-feed */
208 if (firstchar
== 'M') {
210 cancel_escape_sequence(rt
);
214 if (firstchar
!= '[' && firstchar
!= ']') {
215 /* unrecognized escape sequence. Let's forget about it. */
217 fprintf(stderr
, "Unrecognized ES: <%s>\n", rt
->pd
->esbuf
);
220 cancel_escape_sequence(rt
);
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
);
229 else if (firstchar
== ']' && lastchar
== '\a') {
230 /* we have an xterm escape sequence: interpret it */
232 /* rote_es_interpret_xterm_es(rt); -- TODO!*/
234 fprintf(stderr
, "Ignored XTerm ES.\n");
236 cancel_escape_sequence(rt
);
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
);
244 void rote_vt_inject(RoteTerm
*rt
, const char *data
, int len
) {
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
);
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;
258 try_interpret_escape_seq(rt
);
260 else if (rt
->pd
->graphmode
)
261 put_graphmode_char(rt
, *data
);
263 put_normal_char(rt
, *data
);