term: enter lazy mode when >15 chars queued
[fbpad.git] / term.c
blob5b0447772d7c3a1d153f133f195f4ac6ac303383
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 SQRADDR(r, c) (screen + (r) * pad_cols() + (c))
29 static struct term *term;
30 static struct square *screen;
31 static int row, col;
32 static int fg, bg;
33 static int top, bot;
34 static unsigned int mode;
35 static int visible;
37 #define MAXLINES (1 << 13)
38 static int dirty[MAXLINES];
39 static int lazy;
41 static int fgcolor(void)
43 int c = mode & ATTR_REV ? bg : fg;
44 return mode & ATTR_BOLD ? c | 0x08 : c;
47 static int bgcolor(void)
49 return mode & ATTR_REV ? fg : bg;
52 static void _term_show(int r, int c, int cursor)
54 struct square *sqr = SQRADDR(r, c);
55 int fg = sqr->c ? sqr->fg : fgcolor();
56 int bg = sqr->c ? sqr->bg : bgcolor();
57 if (cursor && mode & MODE_CURSOR) {
58 int t = fg;
59 fg = bg;
60 bg = t;
62 if (visible)
63 pad_put(sqr->c, r, c, fg, bg);
66 static void _draw_row(int r)
68 int i;
69 pad_blankrow(r, bgcolor());
70 for (i = 0; i < pad_cols(); i++) {
71 struct square *s = SQRADDR(r, i);
72 if (s->c && (s->c != ' ' || s->bg != bgcolor()))
73 _term_show(r, i, 0);
77 static void lazy_draw(int sr, int er)
79 int i;
80 if (!visible)
81 return;
82 for (i = sr; i < er; i++) {
83 if (lazy)
84 dirty[i] = 1;
85 else
86 _draw_row(i);
90 static void lazy_drawcols(int r, int sc, int ec)
92 int i;
93 if (!visible)
94 return;
95 if (lazy) {
96 dirty[r] = 1;
97 } else {
98 for (i = sc; i < ec; i++)
99 _term_show(r, i, 0);
103 static void lazy_put(int ch, int r, int c)
105 struct square *sqr = SQRADDR(r, c);
106 sqr->c = ch;
107 sqr->fg = fgcolor();
108 sqr->bg = bgcolor();
109 if (!visible)
110 return;
111 if (lazy)
112 dirty[r] = 1;
113 else
114 _term_show(r, c, 0);
117 static void lazy_cursor(int put)
119 if (!visible)
120 return;
121 if (lazy)
122 dirty[row] = 1;
123 else
124 _term_show(row, col, put);
127 static void lazy_clean()
129 if (visible)
130 memset(dirty, 0, sizeof(*dirty) * MAXLINES);
133 static void lazy_flush()
135 int i;
136 if (!visible)
137 return;
138 _term_show(row, col, 0);
139 for (i = 0; i < pad_rows(); i++)
140 if (dirty[i])
141 _draw_row(i);
142 lazy_clean();
143 _term_show(row, col, 1);
146 static void term_redraw(void)
148 int i;
149 for (i = 0; i < pad_rows(); i++)
150 _draw_row(i);
151 _term_show(row, col, 1);
154 static int origin(void)
156 return mode & MODE_ORIGIN;
159 static void setsize(void)
161 struct winsize size;
162 size.ws_col = pad_cols();
163 size.ws_row = pad_rows();
164 size.ws_xpixel = 0;
165 size.ws_ypixel = 0;
166 ioctl(term->fd, TIOCSWINSZ, &size);
169 #define PTYBUFSIZE (1 << 13)
170 static char ptybuf[PTYBUFSIZE];
171 static int ptylen;
172 static int ptycur;
173 static void waitpty(void)
175 struct pollfd ufds[1];
176 ufds[0].fd = term->fd;
177 ufds[0].events = POLLIN;
178 poll(ufds, 1, -1);
181 static int readpty(void)
183 if (ptycur < ptylen)
184 return ptybuf[ptycur++];
185 if (!term->fd)
186 return -1;
187 ptylen = read(term->fd, ptybuf, PTYBUFSIZE);
188 if (ptylen == -1 && errno == EAGAIN) {
189 waitpty();
190 ptylen = read(term->fd, ptybuf, PTYBUFSIZE);
192 if (ptylen > 0) {
193 ptycur = 1;
194 return ptybuf[0];
196 return -1;
199 static void empty_rows(int sr, int er)
201 memset(SQRADDR(sr, 0), 0, (er - sr) * sizeof(*screen) * pad_cols());
204 static void blank_rows(int sr, int er)
206 empty_rows(sr, er);
207 lazy_draw(sr, er);
208 lazy_cursor(1);
211 static void scroll_screen(int sr, int nr, int n)
213 lazy_cursor(0);
214 memmove(SQRADDR(sr + n, 0), SQRADDR(sr, 0),
215 nr * pad_cols() * sizeof(*screen));
216 if (n > 0)
217 empty_rows(sr, sr + n);
218 else
219 empty_rows(sr + nr + n, sr + nr);
220 lazy_draw(MIN(sr, sr + n), MAX(sr + nr, sr + nr + n));
221 lazy_cursor(1);
224 static void insert_lines(int n)
226 int sr = MAX(top, row);
227 int nr = bot - row - n;
228 if (nr > 0)
229 scroll_screen(sr, nr, n);
232 static void delete_lines(int n)
234 int r = MAX(top, row);
235 int sr = r + n;
236 int nr = bot - r - n;
237 if (nr > 0)
238 scroll_screen(sr, nr, -n);
241 static void move_cursor(int r, int c)
243 int t, b;
244 lazy_cursor(0);
245 t = origin() ? top : 0;
246 b = origin() ? bot : pad_rows();
247 row = MAX(t, MIN(r, b - 1));
248 col = MAX(0, MIN(c, pad_cols() - 1));
249 lazy_cursor(1);
250 mode = BIT_SET(mode, MODE_WRAPREADY, 0);
253 static void advance(int dr, int dc, int scrl)
255 int r = row + dr;
256 int c = col + dc;
257 if (r >= bot && scrl) {
258 int n = bot - r - 1;
259 int nr = (bot - top) + n;
260 scroll_screen(top + -n, nr, n);
262 if (r < top && scrl) {
263 int n = top - r;
264 int nr = (bot - top) - n;
265 scroll_screen(top, nr, n);
267 r = MIN(bot - 1, MAX(top, r));
268 c = MIN(pad_cols() - 1, MAX(0, c));
269 move_cursor(r, c);
272 static void insertchar(int c)
274 int wrapready;
275 if (mode & MODE_WRAPREADY)
276 advance(1, -col, 1);
277 lazy_put(c, row, col);
278 wrapready = col == pad_cols() - 1;
279 advance(0, 1, 1);
280 if (wrapready)
281 mode = BIT_SET(mode, MODE_WRAPREADY, 1);
284 void term_send(int c)
286 unsigned char b = (unsigned char) c;
287 if (term->fd)
288 write(term->fd, &b, 1);
291 static void term_sendstr(char *s)
293 if (term->fd)
294 write(term->fd, s, strlen(s));
297 static void setattr(int m)
299 switch (m) {
300 case 0:
301 fg = FGCOLOR;
302 bg = BGCOLOR;
303 mode &= ~ATTR_ALL;
304 break;
305 case 1:
306 mode |= ATTR_BOLD;
307 break;
308 case 7:
309 mode |= ATTR_REV;
310 break;
311 case 22:
312 mode &= ~ATTR_BOLD;
313 break;
314 case 27:
315 mode &= ~ATTR_REV;
316 break;
317 default:
318 if ((m / 10) == 3)
319 fg = m > 37 ? FGCOLOR : m - 30;
320 if ((m / 10) == 4)
321 bg = m > 47 ? BGCOLOR : m - 40;
325 static void kill_chars(int sc, int ec)
327 int i;
328 for (i = sc; i < ec; i++)
329 lazy_put(' ', row, i);
330 lazy_cursor(1);
333 static void move_chars(int sc, int nc, int n)
335 lazy_cursor(0);
336 memmove(SQRADDR(row, sc + n), SQRADDR(row, sc), nc * sizeof(*screen));
337 if (n > 0)
338 memset(SQRADDR(row, sc), 0, n * sizeof(*screen));
339 else
340 memset(SQRADDR(row, pad_cols() + n), 0, -n * sizeof(*screen));
341 lazy_drawcols(row, MIN(sc, sc + n), pad_cols());
342 lazy_cursor(1);
345 static void delete_chars(int n)
347 int sc = col + n;
348 int nc = pad_cols() - sc;
349 move_chars(sc, nc, -n);
352 static void insert_chars(int n)
354 int nc = pad_cols() - col - n;
355 move_chars(col, nc, n);
358 static void term_blank(void)
360 memset(screen, 0, MAXCHARS * sizeof(*screen));
361 if (visible) {
362 pad_blank(bgcolor());
363 lazy_clean();
367 static void ctlseq(void);
368 void term_read(void)
370 ctlseq();
371 while (ptycur < ptylen) {
372 if (ptylen - ptycur > 15)
373 lazy = 1;
374 ctlseq();
376 if (lazy)
377 lazy_flush();
378 lazy = 0;
381 static void term_reset(void)
383 row = col = 0;
384 top = 0;
385 bot = pad_rows();
386 mode = MODE_DEFAULT;
387 setattr(0);
388 term_blank();
391 void term_exec(char *cmd)
393 memset(term, 0, sizeof(*term));
394 if ((term->pid = forkpty(&term->fd, NULL, NULL, NULL)) == -1) {
395 perror("failed to fork");
396 term->fd = 0;
397 return;
399 if (!term->pid) {
400 setenv("TERM", "linux", 1);
401 execlp(cmd, cmd, NULL);
402 exit(1);
404 fcntl(term->fd, F_SETFD, fcntl(term->fd, F_GETFD) | FD_CLOEXEC);
405 fcntl(term->fd, F_SETFL, fcntl(term->fd, F_GETFL) | O_NONBLOCK);
406 setsize();
407 term_reset();
410 static void misc_save(struct term_state *state)
412 state->row = row;
413 state->col = col;
414 state->fg = fg;
415 state->bg = bg;
416 state->mode = mode;
419 static void misc_load(struct term_state *state)
421 row = state->row;
422 col = state->col;
423 fg = state->fg;
424 bg = state->bg;
425 mode = state->mode;
428 void term_save(struct term *term)
430 visible = 0;
431 misc_save(&term->cur);
432 term->top = top;
433 term->bot = bot;
436 void term_load(struct term *t, int flags)
438 term = t;
439 misc_load(&term->cur);
440 screen = term->screen;
441 visible = flags;
442 top = term->top;
443 bot = term->bot;
444 if (flags == TERM_REDRAW) {
445 if (term->fd)
446 term_redraw();
447 else
448 pad_blank(0);
452 void term_end(void)
454 if (term->fd)
455 close(term->fd);
456 memset(term, 0, sizeof(*term));
457 term_load(term, visible ? TERM_REDRAW : TERM_HIDDEN);
460 static void set_region(int t, int b)
462 top = MIN(pad_rows(), MAX(0, t - 1));
463 bot = MIN(pad_rows(), MAX(0, b ? b : pad_rows()));
464 if (origin())
465 move_cursor(top, 0);
468 #include "vt102.c"