updated docs
[rlserver.git] / vt100.c
blob5667019e4441ce18216584a7d6df8289d8684e3d
1 #include <stdio.h>
2 #include <ctype.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include "log.h"
7 #include "vt100.h"
11 vt100* init_term (int width, int height)
13 vt100 *term = malloc(sizeof(vt100) + 3 * width * height);
14 if (term == NULL)
16 write_log (LOG_ERROR, "unable to allocate memory to create terminal %dx%d", width, height);
17 return NULL;
20 term->wid = width;
21 term->hgt = height;
22 term->cx = term->cy = 0;
23 term->cmode = CM_NORM;
24 term->att = 0;
25 term->col = 7;
26 term->esc_len = 0;
27 term->prev_char = ' ';
29 term->screen = ((uint8_t*)term) + sizeof(vt100);
30 term->colour = term->screen + width * height;
31 term->attrib = term->colour + width * height;
32 memset (term->screen, ' ', width * height);
33 memset (term->attrib, 0, width * height);
34 memset (term->colour, 0, width * height);
36 return term;
41 vt100* resize_term (vt100 *term, int width, int height)
43 // XXX
44 return term;
49 // we can't use strtol() because the string doesn't have to be 0-terminated
50 static int get_num (const uint8_t *data, int len, int *pos)
52 int i, n = 0;
55 for (i = 0; i < len; i++)
57 if (!isdigit(data[i])) break;
58 n = n * 10 + data[i] - '0';
61 *pos = i;
62 return n;
67 static void process_attr (vt100 *term, int attr)
69 // attribute
70 if (attr <= 10) term->att = attr;
72 // foreground colour
73 if (attr >= 30 && attr <= 37) term->col = (term->col & 0xF0) | (attr - 30);
75 // background colour
76 if (attr >= 40 && attr <= 47) term->col = (term->col & 0x0F) | ((attr - 40) << 4);
81 static void erase_screen (vt100 *term, int n1)
83 memset (term->screen, ' ', term->wid * term->hgt);
84 memset (term->attrib, 0, term->wid * term->hgt);
85 memset (term->colour, 0, term->wid * term->hgt);
86 if (n1 != 2) term->cx = term->cy = 0;
91 static void erase_line (vt100 *term, int n1)
93 int ofs = term->wid * term->cy;
94 int l = term->wid;
96 // [ 0 K or [ K - to the end of line
97 if (n1 <= 0)
99 ofs += term->cx;
100 l -= term->cx;
103 // [ 1 K - to the beginning of line
104 if (n1 == 1)
106 l = term->cx;
109 memset (&term->screen[ofs], ' ', l);
110 memset (&term->attrib[ofs], 0, l);
111 memset (&term->colour[ofs], 0, l);
116 static void erase_characters (vt100 *term, int n1)
118 int ofs = term->wid * term->cy + term->cx;
119 int l = term->wid - term->cx;
121 if (n1 < l) l = n1;
122 memset (&term->screen[ofs], ' ', l);
123 memset (&term->attrib[ofs], 0, l);
124 memset (&term->colour[ofs], 0, l);
129 // process single non-esc character
130 static void process_char (vt100 *term, char c)
132 if (c == '\r')
134 term->cx = 0;
136 else if (c == '\n')
138 // XXX no scrolling
139 if (term->cy < term->hgt - 1) term->cy++;
141 // backspace
142 else if (c == 8)
144 if (term->cx > 0) term->cx--;
145 return;
147 else
149 int ofs = term->cy * term->wid + term->cx;
150 term->screen[ofs] = c;
151 term->attrib[ofs] = term->att;
152 term->colour[ofs] = term->col;
153 if (++term->cx >= term->wid)
155 term->cx = 0;
156 // XXX no scrolling
157 if (term->cy < term->hgt - 1) term->cy++;
160 term->prev_char = c;
166 #define MAX_ARGS 3
168 static int parse_dec (vt100 *term, int args, int arg[MAX_ARGS])
170 int i;
173 switch (term->esc[term->esc_len - 1])
175 // [ Y ; X H - goto Y,X
176 case 'H':
177 case 'f':
178 if (args == 0)
180 term->cx = term->cy = 0;
181 break;
184 if (args != 2) return -1;
186 if (arg[0] != 0) arg[0]--;
187 if (arg[0] >= 0 && arg[0] < term->hgt) term->cy = arg[0];
188 if (arg[1] != 0) arg[1]--;
189 if (arg[1] >= 0 && arg[1] < term->wid) term->cx = arg[1];
190 break;
192 // [ nn J or [ J - clear screen
193 case 'J':
194 if (args == 0) arg[0] = 0;
195 erase_screen (term, arg[0]);
196 break;
198 // [ nn K or [ K - erase line
199 case 'K':
200 if (args == 0) arg[0] = 0;
201 erase_line (term, arg[0]);
202 break;
204 // [ N1 m or [ N1 ; N2 m or [ N1 ; N2 ; N3 m - set colour and/or attribute (SGR mode)
205 case 'm':
206 if (args == 0) args = 1, arg[0] = 0;
208 for (i = 0; i < args; i++)
210 process_attr (term, arg[i]);
212 break;
214 // [ nn @ or [ @ - insert blank characters
215 case '@':
216 if (args == 0) arg[0] = 1;
217 for (i = 0; i < arg[0]; i++)
219 process_char (term, ' ');
221 break;
223 case 'b':
224 if (args != 1) return -1;
225 for (i = 0; i < arg[0]; i++)
227 process_char (term, term->prev_char);
229 break;
231 // [ nn A or [ A - move cursor up
232 case 'A':
233 if (args == 0 || arg[0] < 1) arg[0] = 1;
234 term->cy -= arg[0];
235 if (term->cy < 0) term->cy = 0;
236 break;
238 // [ nn B or [ B - move cursor down
239 case 'B':
240 if (args == 0 || arg[0] < 1) arg[0] = 1;
241 term->cy += arg[0];
242 if (term->cy >= term->hgt) term->cy = term->hgt - 1;
243 break;
245 // [ nn C or [ C - move cursor right
246 case 'C':
247 if (args == 0 || arg[0] < 1) arg[0] = 1;
248 term->cx += arg[0];
249 if (term->cx >= term->wid) term->cx = term->wid - 1;
250 break;
252 // [ nn D or [ D - move cursor left
253 case 'D':
254 if (args == 0 || arg[0] < 1) arg[0] = 1;
255 term->cx -= arg[0];
256 if (term->cx < 0) term->cx = 0;
257 break;
259 // [ nn X - delete characters
260 case 'X':
261 if (args != 1) return -1;
263 if (arg[0] == 0) arg[0] = 1;
264 erase_characters (term, arg[0]);
265 break;
267 // [ nn d - move to specified row
268 case 'd':
269 if (args != 1) return -1;
271 if (arg[0] != 0) arg[0]--;
272 term->cy = arg[0];
273 if (term->cy >= term->hgt) term->cy = term->hgt - 1;
274 break;
276 // [ nn G - move to specified column
277 case 'G':
278 if (args != 1) return -1;
280 if (arg[0] != 0) arg[0]--;
281 term->cx = arg[0];
282 if (term->cx >= term->wid) term->cx = term->wid - 1;
283 break;
285 default:
286 return -1;
289 return 0;
294 static int parse_dec_priv (vt100 *term, int args, int arg[MAX_ARGS])
296 switch (term->esc[term->esc_len - 1])
298 case 'h':
299 if (args == 0) return -1;
300 if (arg[0] == 25) term->cmode = CM_NORM;
301 break;
303 case 'l':
304 if (args == 0) return -1;
305 if (arg[0] == 25) term->cmode = CM_HIDE;
306 break;
308 default:
309 return -1;
312 return 0;
317 static int parse_esc (vt100 *term)
319 int arg[MAX_ARGS];
320 int p = 2, args = 0;
323 // XXX something unknown - skip
324 if (!isalnum(term->esc[2]))
326 if (term->esc[2] == '@' || term->esc[2] == '?') p++;
327 else return -1;
330 // parse numbers
331 while (args < MAX_ARGS)
333 int np;
335 arg[args] = get_num(&term->esc[p], term->esc_len - p, &np);
336 if (np == 0) break;
338 p += 1 + np;
339 args++;
340 if (term->esc[p - 1] != ';') break;
343 // DEC command
344 if (term->esc[1] == '[')
346 // DEC private mode command
347 if (term->esc[2] == '?') return parse_dec_priv(term, args, arg);
349 return parse_dec(term, args, arg);
352 return -1;
357 static void process_esc (vt100 *term)
359 char c = term->esc[term->esc_len - 1];
361 // sequence is not complete yet
362 if (!isalpha(c) && c != '@' && c != 033) return;
363 if (term->esc_len > 2) parse_esc (term);
365 term->esc_len = 0;
370 void term_process (vt100 *term, const char *data, int len)
372 int i, j;
375 for (i = 0; i < len; i++)
377 // inside esc seqence
378 if (term->esc_len)
380 term->esc[term->esc_len++] = data[i];
381 process_esc (term);
383 // XXX sequence is too long, just dump it to the terminal
384 if (term->esc_len >= VT100_ESC_BUF)
386 for (j = 0; j < VT100_ESC_BUF; j++)
388 process_char (term, term->esc[j]);
392 continue;
395 // esc
396 if (data[i] == 033) term->esc[0] = 0x33, term->esc_len = 1;
397 // a normal character
398 else process_char (term, data[i]);
404 #define BUFSZ 1280
406 static void add_buf (char *buf, int *pos, const char *data, int len, int fd)
408 int l, max;
411 while (len > 0)
413 max = BUFSZ - *pos;
414 l = (len < max) ? len : max;
415 memcpy (&buf[*pos], data, l);
416 *pos = *pos + l;
417 len -= l;
418 data += l;
420 if (*pos == BUFSZ)
422 write (fd, buf, BUFSZ);
423 *pos = 0;
430 void term_copy_data (vt100 *term, int fd)
432 char buf[BUFSZ] = "\033[H\033[2J";
433 int i, pos = 7, a, c, cf, cb;
434 char b2[80];
435 int p2;
438 // initial colours and attribute
439 a = term->attrib[0];
440 c = term->colour[0];
441 cf = (c & 0x0F);
442 cb = (c >> 4);
443 pos += sprintf(&buf[pos], "\033[0;%dm\033[%d;%dm", a, cb + 40, cf + 30);
446 for (i = 0; i < term->wid * term->hgt; i++)
448 int cf2, cb2;
449 p2 = 0;
451 // change attribute
452 if (a != term->attrib[i])
454 a = term->attrib[i];
455 p2 += sprintf(&b2[p2], "\033[0;%dm", a);
457 // force colour change since colours are reset
458 c = cf = cb = -1;
461 // change colour
462 if (c != term->colour[i])
464 c = term->colour[i];
465 cf2 = (c & 0x0F);
466 cb2 = (c >> 4);
468 if (cf2 != cf && cb2 != cb) p2 += sprintf(&b2[p2], "\033[%d;%dm", cb2 + 40, cf2 + 30);
469 else if (cf2 != cf) p2 += sprintf(&b2[p2], "\033[%dm", cf2 + 30);
470 else p2 += sprintf(&b2[p2], "\033[%dm", cb2 + 40);
472 cb = cb2;
473 cf = cf2;
476 add_buf (buf, &pos, b2, p2, fd);
478 add_buf (buf, &pos, (char*)&term->screen[i], 1, fd);
481 p2 = sprintf(b2, "\033[%d;%dH", term->cy + 1, term->cx + 1);
482 add_buf (buf, &pos, b2, p2, fd);
484 // cursor state
485 if (term->cmode == CM_HIDE) p2 = sprintf(b2, "\033[?25l");
486 else p2 = sprintf(b2, "\033[?25h");
487 add_buf (buf, &pos, b2, p2, fd);
489 if (pos > 0) write (fd, buf, pos);