config: added 'a' tag
[fbpad.git] / term.c
blob78b028fa9ba13438a14627dae22596796c4c83bf
1 #include <ctype.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <poll.h>
5 #include <pty.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <sys/types.h>
10 #include <unistd.h>
11 #include "config.h"
12 #include "pad.h"
13 #include "util.h"
14 #include "term.h"
16 #define MODE_CURSOR 0x01
17 #define MODE_WRAP 0x02
18 #define MODE_ORIGIN 0x04
19 #define MODE_AUTOCR 0x08
20 #define MODE_DEFAULT (MODE_CURSOR | MODE_WRAP)
21 #define ATTR_BOLD 0x10
22 #define ATTR_REV 0x20
23 #define ATTR_ALL (ATTR_BOLD | ATTR_REV)
24 #define MODE_WRAPREADY 0x80
26 #define BIT_SET(i, b, val) ((val) ? ((i) | (b)) : ((i) & ~(b)))
27 #define OFFSET(r, c) ((r) * pad_cols() + (c))
29 static struct term *term;
30 static unsigned int *screen;
31 static unsigned char *fgs, *bgs;
32 static int row, col;
33 static unsigned char fg, bg;
34 static int top, bot;
35 static unsigned int mode;
36 static int visible;
38 #define MAXLINES (1 << 13)
39 static int dirty[MAXLINES];
40 static int lazy;
42 static unsigned char fgcolor(void)
44 int c = mode & ATTR_REV ? bg : fg;
45 return mode & ATTR_BOLD ? c | 0x08 : c;
48 static unsigned char bgcolor(void)
50 return mode & ATTR_REV ? fg : bg;
53 static void _term_show(int r, int c, int cursor)
55 int i = OFFSET(r, c);
56 unsigned char fg = screen[i] ? fgs[i] : fgcolor();
57 unsigned char bg = screen[i] ? bgs[i] : bgcolor();
58 if (cursor && mode & MODE_CURSOR) {
59 int t = fg;
60 fg = bg;
61 bg = t;
63 if (visible)
64 pad_put(screen[i], r, c, fg, bg);
67 static void _draw_row(int r)
69 int i;
70 pad_blankrow(r, bgcolor());
71 for (i = 0; i < pad_cols(); i++) {
72 unsigned int c = screen[OFFSET(r, i)];
73 if (c && (c != ' ' || bgs[OFFSET(r, i)] != bgcolor()))
74 _term_show(r, i, 0);
78 static void lazy_draw(int sr, int er)
80 int i;
81 if (!visible)
82 return;
83 for (i = sr; i < er; i++) {
84 if (lazy)
85 dirty[i] = 1;
86 else
87 _draw_row(i);
91 static void lazy_drawcols(int r, int sc, int ec)
93 int i;
94 if (!visible)
95 return;
96 if (lazy) {
97 dirty[r] = 1;
98 } else {
99 for (i = sc; i < ec; i++)
100 _term_show(r, i, 0);
104 static void lazy_put(int ch, int r, int c)
106 int i = OFFSET(r, c);
107 screen[i] = ch;
108 fgs[i] = fgcolor();
109 bgs[i] = bgcolor();
110 if (!visible)
111 return;
112 if (lazy)
113 dirty[r] = 1;
114 else
115 _term_show(r, c, 0);
118 static void lazy_cursor(int put)
120 if (!visible)
121 return;
122 if (lazy)
123 dirty[row] = 1;
124 else
125 _term_show(row, col, put);
128 static void lazy_clean()
130 if (visible)
131 memset(dirty, 0, sizeof(*dirty) * MAXLINES);
134 static void lazy_flush()
136 int i;
137 if (!visible)
138 return;
139 _term_show(row, col, 0);
140 for (i = 0; i < pad_rows(); i++)
141 if (dirty[i])
142 _draw_row(i);
143 lazy_clean();
144 _term_show(row, col, 1);
147 static void term_redraw(void)
149 int i;
150 for (i = 0; i < pad_rows(); i++)
151 _draw_row(i);
152 _term_show(row, col, 1);
155 static int origin(void)
157 return mode & MODE_ORIGIN;
160 static void setsize(void)
162 struct winsize size;
163 size.ws_col = pad_cols();
164 size.ws_row = pad_rows();
165 size.ws_xpixel = 0;
166 size.ws_ypixel = 0;
167 ioctl(term->fd, TIOCSWINSZ, &size);
170 #define PTYBUFSIZE (1 << 13)
171 static char ptybuf[PTYBUFSIZE];
172 static int ptylen;
173 static int ptycur;
174 static void waitpty(void)
176 struct pollfd ufds[1];
177 ufds[0].fd = term->fd;
178 ufds[0].events = POLLIN;
179 poll(ufds, 1, -1);
182 static int readpty(void)
184 if (ptycur < ptylen)
185 return ptybuf[ptycur++];
186 if (!term->fd)
187 return -1;
188 ptylen = read(term->fd, ptybuf, PTYBUFSIZE);
189 if (ptylen == -1 && errno == EAGAIN) {
190 waitpty();
191 ptylen = read(term->fd, ptybuf, PTYBUFSIZE);
193 if (ptylen > 0) {
194 ptycur = 1;
195 return ptybuf[0];
197 return -1;
200 static void screen_move(int dst, int src, int n)
202 memmove(screen + dst, screen + src, n * sizeof(*screen));
203 memmove(fgs + dst, fgs + src, n);
204 memmove(bgs + dst, bgs + src, n);
207 static void screen_reset(int i, int n)
209 memset(screen + i, 0, n * sizeof(*screen));
210 memset(fgs + i, fg, n);
211 memset(bgs + i, bg, n);
214 static void empty_rows(int sr, int er)
216 screen_reset(OFFSET(sr, 0), (er - sr) * pad_cols());
219 static void blank_rows(int sr, int er)
221 empty_rows(sr, er);
222 lazy_draw(sr, er);
223 lazy_cursor(1);
226 static void scroll_screen(int sr, int nr, int n)
228 lazy_cursor(0);
229 screen_move(OFFSET(sr + n, 0), OFFSET(sr, 0), nr * pad_cols());
230 if (n > 0)
231 empty_rows(sr, sr + n);
232 else
233 empty_rows(sr + nr + n, sr + nr);
234 lazy_draw(MIN(sr, sr + n), MAX(sr + nr, sr + nr + n));
235 lazy_cursor(1);
238 static void insert_lines(int n)
240 int sr = MAX(top, row);
241 int nr = bot - row - n;
242 if (nr > 0)
243 scroll_screen(sr, nr, n);
246 static void delete_lines(int n)
248 int r = MAX(top, row);
249 int sr = r + n;
250 int nr = bot - r - n;
251 if (nr > 0)
252 scroll_screen(sr, nr, -n);
255 static void move_cursor(int r, int c)
257 int t, b;
258 lazy_cursor(0);
259 t = origin() ? top : 0;
260 b = origin() ? bot : pad_rows();
261 row = MAX(t, MIN(r, b - 1));
262 col = MAX(0, MIN(c, pad_cols() - 1));
263 lazy_cursor(1);
264 mode = BIT_SET(mode, MODE_WRAPREADY, 0);
267 static void advance(int dr, int dc, int scrl)
269 int r = row + dr;
270 int c = col + dc;
271 if (r >= bot && scrl) {
272 int n = bot - r - 1;
273 int nr = (bot - top) + n;
274 scroll_screen(top + -n, nr, n);
276 if (r < top && scrl) {
277 int n = top - r;
278 int nr = (bot - top) - n;
279 scroll_screen(top, nr, n);
281 r = MIN(bot - 1, MAX(top, r));
282 c = MIN(pad_cols() - 1, MAX(0, c));
283 move_cursor(r, c);
286 static void insertchar(int c)
288 int wrapready;
289 if (mode & MODE_WRAPREADY)
290 advance(1, -col, 1);
291 lazy_put(c, row, col);
292 wrapready = col == pad_cols() - 1;
293 advance(0, 1, 1);
294 if (wrapready)
295 mode = BIT_SET(mode, MODE_WRAPREADY, 1);
298 void term_send(int c)
300 unsigned char b = (unsigned char) c;
301 if (term->fd)
302 write(term->fd, &b, 1);
305 static void term_sendstr(char *s)
307 if (term->fd)
308 write(term->fd, s, strlen(s));
311 static void setattr(int m)
313 switch (m) {
314 case 0:
315 fg = FGCOLOR;
316 bg = BGCOLOR;
317 mode &= ~ATTR_ALL;
318 break;
319 case 1:
320 mode |= ATTR_BOLD;
321 break;
322 case 7:
323 mode |= ATTR_REV;
324 break;
325 case 22:
326 mode &= ~ATTR_BOLD;
327 break;
328 case 27:
329 mode &= ~ATTR_REV;
330 break;
331 default:
332 if ((m / 10) == 3)
333 fg = m > 37 ? FGCOLOR : m - 30;
334 if ((m / 10) == 4)
335 bg = m > 47 ? BGCOLOR : m - 40;
339 static void kill_chars(int sc, int ec)
341 int i;
342 for (i = sc; i < ec; i++)
343 lazy_put(' ', row, i);
344 lazy_cursor(1);
347 static void move_chars(int sc, int nc, int n)
349 lazy_cursor(0);
350 screen_move(OFFSET(row, sc + n), OFFSET(row, sc), nc);
351 if (n > 0)
352 screen_reset(OFFSET(row, sc), n);
353 else
354 screen_reset(OFFSET(row, pad_cols() + n), -n);
355 lazy_drawcols(row, MIN(sc, sc + n), pad_cols());
356 lazy_cursor(1);
359 static void delete_chars(int n)
361 int sc = col + n;
362 int nc = pad_cols() - sc;
363 move_chars(sc, nc, -n);
366 static void insert_chars(int n)
368 int nc = pad_cols() - col - n;
369 move_chars(col, nc, n);
372 static void term_blank(void)
374 screen_reset(0, pad_rows() * pad_cols());
375 if (visible) {
376 pad_blank(bgcolor());
377 lazy_clean();
381 static void ctlseq(void);
382 void term_read(void)
384 ctlseq();
385 while (ptycur < ptylen) {
386 if (ptylen - ptycur > 15)
387 lazy = 1;
388 ctlseq();
390 if (lazy)
391 lazy_flush();
392 lazy = 0;
395 static void term_reset(void)
397 row = col = 0;
398 top = 0;
399 bot = pad_rows();
400 mode = MODE_DEFAULT;
401 setattr(0);
402 term_blank();
405 void term_exec(char *cmd)
407 memset(term, 0, sizeof(*term));
408 if ((term->pid = forkpty(&term->fd, NULL, NULL, NULL)) == -1) {
409 perror("failed to fork");
410 term->fd = 0;
411 return;
413 if (!term->pid) {
414 setenv("TERM", "linux", 1);
415 execlp(cmd, cmd, NULL);
416 exit(1);
418 fcntl(term->fd, F_SETFD, fcntl(term->fd, F_GETFD) | FD_CLOEXEC);
419 fcntl(term->fd, F_SETFL, fcntl(term->fd, F_GETFL) | O_NONBLOCK);
420 setsize();
421 term_reset();
424 static void misc_save(struct term_state *state)
426 state->row = row;
427 state->col = col;
428 state->fg = fg;
429 state->bg = bg;
430 state->mode = mode;
433 static void misc_load(struct term_state *state)
435 row = state->row;
436 col = state->col;
437 fg = state->fg;
438 bg = state->bg;
439 mode = state->mode;
442 void term_save(struct term *term)
444 visible = 0;
445 misc_save(&term->cur);
446 term->top = top;
447 term->bot = bot;
450 void term_load(struct term *t, int flags)
452 term = t;
453 misc_load(&term->cur);
454 screen = term->screen;
455 fgs = term->fgs;
456 bgs = term->bgs;
457 visible = flags;
458 top = term->top;
459 bot = term->bot;
460 if (flags == TERM_REDRAW) {
461 if (term->fd)
462 term_redraw();
463 else
464 pad_blank(0);
468 void term_end(void)
470 if (term->fd)
471 close(term->fd);
472 memset(term, 0, sizeof(*term));
473 term_load(term, visible ? TERM_REDRAW : TERM_HIDDEN);
476 static void set_region(int t, int b)
478 top = MIN(pad_rows(), MAX(0, t - 1));
479 bot = MIN(pad_rows(), MAX(0, b ? b : pad_rows()));
480 if (origin())
481 move_cursor(top, 0);
484 void term_screenshot(void)
486 FILE *fp = fopen(SCREENSHOT, "w");
487 int i, j;
488 for (i = 0; i < pad_rows(); i++) {
489 for (j = 0; j < pad_cols(); j++) {
490 int c = screen[OFFSET(i, j)];
491 fputc(c ? c : ' ', fp);
493 fputc('\n', fp);
495 fclose(fp);
498 #include "vt102.c"