term: save font attr to handle clear attr ops
[fbpad.git] / term.c
blob9aaeb838d6136f6096d9f85d207d41a1b65fec87
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)
25 #define BIT_SET(i, b, val) ((val) ? ((i) | (b)) : ((i) & ~(b)))
26 #define SQRADDR(r, c) (screen + (r) * pad_cols() + (c))
28 static struct term *term;
29 static struct square *screen;
30 static int row, col;
31 static int fg, bg;
32 static int top, bot;
33 static unsigned int mode;
34 static int visible;
36 #define MAXLINES (1 << 13)
37 static int dirty[MAXLINES];
38 static int lazy;
40 static int fgcolor(void)
42 int c = mode & ATTR_REV ? bg : fg;
43 return mode & ATTR_BOLD ? c | 0x08 : c;
46 static int bgcolor(void)
48 return mode & ATTR_REV ? fg : bg;
51 static void _term_show(int r, int c, int cursor)
53 struct square *sqr = SQRADDR(r, c);
54 int fg = sqr->c ? sqr->fg : fgcolor();
55 int bg = sqr->c ? sqr->bg : bgcolor();
56 if (cursor && mode & MODE_CURSOR) {
57 int t = fg;
58 fg = bg;
59 bg = t;
61 if (visible)
62 pad_put(sqr->c, r, c, fg, bg);
65 static void _draw_row(int r)
67 int i;
68 pad_blankrow(r, bgcolor());
69 for (i = 0; i < pad_cols(); i++) {
70 struct square *s = SQRADDR(r, i);
71 if (s->c && (s->c != ' ' || s->bg != bgcolor()))
72 _term_show(r, i, 0);
76 static void lazy_draw(int sr, int er)
78 int i;
79 if (!visible)
80 return;
81 for (i = sr; i < er; i++) {
82 if (lazy)
83 dirty[i] = 1;
84 else
85 _draw_row(i);
89 static void lazy_drawcols(int r, int sc, int ec)
91 int i;
92 if (!visible)
93 return;
94 if (lazy) {
95 dirty[r] = 1;
96 } else {
97 for (i = sc; i < ec; i++)
98 _term_show(r, i, 0);
102 static void lazy_put(int ch, int r, int c)
104 struct square *sqr = SQRADDR(r, c);
105 sqr->c = ch;
106 sqr->fg = fgcolor();
107 sqr->bg = bgcolor();
108 if (!visible)
109 return;
110 if (lazy)
111 dirty[r] = 1;
112 else
113 _term_show(r, c, 0);
116 static void lazy_cursor(int put)
118 if (!visible)
119 return;
120 if (lazy)
121 dirty[row] = 1;
122 else
123 _term_show(row, col, put);
126 static void lazy_clean()
128 if (visible)
129 memset(dirty, 0, sizeof(*dirty) * MAXLINES);
132 static void lazy_flush()
134 int i;
135 if (!visible)
136 return;
137 _term_show(row, col, 0);
138 for (i = 0; i < pad_rows(); i++)
139 if (dirty[i])
140 _draw_row(i);
141 lazy_clean();
142 _term_show(row, col, 1);
145 static void term_redraw(void)
147 int i;
148 for (i = 0; i < pad_rows(); i++)
149 _draw_row(i);
150 _term_show(row, col, 1);
153 static int origin(void)
155 return mode & MODE_ORIGIN;
158 static void setsize(void)
160 struct winsize size;
161 size.ws_col = pad_cols();
162 size.ws_row = pad_rows();
163 size.ws_xpixel = 0;
164 size.ws_ypixel = 0;
165 ioctl(term->fd, TIOCSWINSZ, &size);
168 #define PTYBUFSIZE (1 << 13)
169 static char ptybuf[PTYBUFSIZE];
170 static int ptylen;
171 static int ptycur;
172 static void waitpty(void)
174 struct pollfd ufds[1];
175 ufds[0].fd = term->fd;
176 ufds[0].events = POLLIN;
177 poll(ufds, 1, -1);
180 static int readpty(void)
182 if (ptycur < ptylen)
183 return ptybuf[ptycur++];
184 if (!term->fd)
185 return -1;
186 ptylen = read(term->fd, ptybuf, PTYBUFSIZE);
187 if (ptylen == -1 && errno == EAGAIN) {
188 waitpty();
189 ptylen = read(term->fd, ptybuf, PTYBUFSIZE);
191 if (ptylen > 0) {
192 ptycur = 1;
193 return ptybuf[0];
195 return -1;
198 static void empty_rows(int sr, int er)
200 memset(SQRADDR(sr, 0), 0, (er - sr) * sizeof(*screen) * pad_cols());
203 static void blank_rows(int sr, int er)
205 empty_rows(sr, er);
206 lazy_draw(sr, er);
207 lazy_cursor(1);
210 static void scroll_screen(int sr, int nr, int n)
212 lazy_cursor(0);
213 memmove(SQRADDR(sr + n, 0), SQRADDR(sr, 0),
214 nr * pad_cols() * sizeof(*screen));
215 if (n > 0)
216 empty_rows(sr, sr + n);
217 else
218 empty_rows(sr + nr + n, sr + nr);
219 lazy_draw(MIN(sr, sr + n), MAX(sr + nr, sr + nr + n));
220 lazy_cursor(1);
223 static void insert_lines(int n)
225 int sr = MAX(top, row);
226 int nr = bot - row - n;
227 if (nr > 0)
228 scroll_screen(sr, nr, n);
231 static void delete_lines(int n)
233 int r = MAX(top, row);
234 int sr = r + n;
235 int nr = bot - r - n;
236 if (nr > 0)
237 scroll_screen(sr, nr, -n);
240 static void move_cursor(int r, int c)
242 int t, b;
243 lazy_cursor(0);
244 t = origin() ? top : 0;
245 b = origin() ? bot : pad_rows();
246 row = MAX(t, MIN(r, b - 1));
247 col = MAX(0, MIN(c, pad_cols() - 1));
248 lazy_cursor(1);
251 static void advance(int dr, int dc, int scrl)
253 int r = row + dr;
254 int c = col + dc;
255 if (c >= pad_cols()) {
256 if (scrl && mode & MODE_WRAP) {
257 r++;
258 c = 0;
259 } else {
260 c = pad_cols() - 1;
263 if (r >= bot && scrl) {
264 int n = bot - r - 1;
265 int nr = (bot - top) + n;
266 scroll_screen(top + -n, nr, n);
268 if (r < top && scrl) {
269 int n = top - r;
270 int nr = (bot - top) - n;
271 scroll_screen(top, nr, n);
273 r = MIN(bot - 1, MAX(top, r));
274 move_cursor(r, MAX(0, c));
277 void term_send(int c)
279 unsigned char b = (unsigned char) c;
280 if (term->fd)
281 write(term->fd, &b, 1);
284 static void term_sendstr(char *s)
286 if (term->fd)
287 write(term->fd, s, strlen(s));
290 static void setattr(int m)
292 switch (m) {
293 case 0:
294 fg = FGCOLOR;
295 bg = BGCOLOR;
296 mode &= ~ATTR_ALL;
297 break;
298 case 1:
299 mode |= ATTR_BOLD;
300 break;
301 case 7:
302 mode |= ATTR_REV;
303 break;
304 case 22:
305 mode &= ~ATTR_BOLD;
306 break;
307 case 27:
308 mode &= ~ATTR_REV;
309 break;
310 default:
311 if ((m / 10) == 3)
312 fg = m > 37 ? FGCOLOR : m - 30;
313 if ((m / 10) == 4)
314 bg = m > 47 ? BGCOLOR : m - 40;
318 static void kill_chars(int sc, int ec)
320 int i;
321 for (i = sc; i < ec; i++)
322 lazy_put(' ', row, i);
323 lazy_cursor(1);
326 static void move_chars(int sc, int nc, int n)
328 lazy_cursor(0);
329 memmove(SQRADDR(row, sc + n), SQRADDR(row, sc), nc * sizeof(*screen));
330 if (n > 0)
331 memset(SQRADDR(row, sc), 0, n * sizeof(*screen));
332 else
333 memset(SQRADDR(row, pad_cols() + n), 0, -n * sizeof(*screen));
334 lazy_drawcols(row, MIN(sc, sc + n), pad_cols());
335 lazy_cursor(1);
338 static void delete_chars(int n)
340 int sc = col + n;
341 int nc = pad_cols() - sc;
342 move_chars(sc, nc, -n);
345 static void insert_chars(int n)
347 int nc = pad_cols() - col - n;
348 move_chars(col, nc, n);
351 static void term_blank(void)
353 memset(screen, 0, MAXCHARS * sizeof(*screen));
354 if (visible) {
355 pad_blank(bgcolor());
356 lazy_clean();
360 static void ctlseq(void);
361 void term_read(void)
363 ctlseq();
364 lazy = 1;
365 while (ptycur < ptylen)
366 ctlseq();
367 lazy_flush();
368 lazy = 0;
371 void term_exec(char *cmd)
373 memset(term, 0, sizeof(*term));
374 term->cur.bot = term->sav.bot = pad_rows();
375 term->cur.mode = MODE_DEFAULT;
376 term_load(term, visible);
377 if ((term->pid = forkpty(&term->fd, NULL, NULL, NULL)) == -1) {
378 perror("failed to fork");
379 term->fd = 0;
380 return;
382 if (!term->pid) {
383 setenv("TERM", "linux", 1);
384 execlp(cmd, cmd, NULL);
385 exit(1);
387 fcntl(term->fd, F_SETFD, fcntl(term->fd, F_GETFD) | FD_CLOEXEC);
388 fcntl(term->fd, F_SETFL, fcntl(term->fd, F_GETFL) | O_NONBLOCK);
389 setsize();
390 setattr(0);
391 term_blank();
394 static void misc_save(struct term_state *state)
396 state->row = row;
397 state->col = col;
398 state->fg = fg;
399 state->bg = bg;
400 state->top = top;
401 state->bot = bot;
402 state->mode = mode;
405 static void misc_load(struct term_state *state)
407 row = state->row;
408 col = state->col;
409 fg = state->fg;
410 bg = state->bg;
411 top = state->top;
412 bot = state->bot;
413 mode = state->mode;
416 void term_save(struct term *term)
418 visible = 0;
419 misc_save(&term->cur);
422 void term_load(struct term *t, int flags)
424 term = t;
425 misc_load(&term->cur);
426 screen = term->screen;
427 visible = flags;
428 if (flags == TERM_REDRAW) {
429 if (term->fd)
430 term_redraw();
431 else
432 pad_blank(0);
436 void term_end(void)
438 if (term->fd)
439 close(term->fd);
440 term->fd = 0;
441 row = col = 0;
442 fg = 0;
443 bg = 0;
444 term_blank();
447 static void set_region(int t, int b)
449 top = MIN(pad_rows(), MAX(0, t - 1));
450 bot = MIN(pad_rows(), MAX(0, b ? b : pad_rows()));
451 if (origin())
452 move_cursor(top, 0);
455 #include "vt102.c"