term: don't read from a closed term
[fbpad.git] / term.c
blob8b7ca1e310a2fa594f73beea05aea7beb9344fa1
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_NOCURSOR 0x01
17 #define MODE_NOWRAP 0x02
18 #define MODE_ORIGIN 0x04
19 #define MODE_AUTOCR 0x08
20 #define BIT_SET(i, b, val) ((val) ? ((i) | (b)) : ((i) & ~(b)))
21 #define SQRADDR(r, c) (screen + (r) * pad_cols() + (c))
23 static struct term *term;
24 static struct square *screen;
25 static int row, col;
26 static int fg, bg;
27 static int top, bot;
28 static int mode;
29 static int visible;
31 #define MAXLINES (1 << 13)
32 static int dirty[MAXLINES];
33 static int lazy;
35 static void _term_show(int r, int c, int cursor)
37 struct square *sqr = SQRADDR(r, c);
38 int fgcolor = sqr->c ? sqr->fg : fg;
39 int bgcolor = sqr->c ? sqr->bg : bg;
40 if (cursor && !(mode & MODE_NOCURSOR)) {
41 int t = fgcolor;
42 fgcolor = bgcolor;
43 bgcolor = t;
45 if (visible)
46 pad_put(sqr->c, r, c, fgcolor, bgcolor);
49 static void _draw_row(int r)
51 int i;
52 for (i = 0; i < pad_cols(); i++)
53 _term_show(r, i, 0);
56 static void lazy_draw(int sr, int er)
58 int i;
59 if (!visible)
60 return;
61 for (i = sr; i < er; i++) {
62 if (lazy)
63 dirty[i] = 1;
64 else
65 _draw_row(i);
69 static void lazy_drawcols(int r, int sc, int ec)
71 int i;
72 if (!visible)
73 return;
74 if (lazy) {
75 dirty[r] = 1;
76 } else {
77 for (i = sc; i < ec; i++)
78 _term_show(r, i, 0);
82 static void lazy_put(int ch, int r, int c)
84 struct square *sqr = SQRADDR(r, c);
85 sqr->c = ch;
86 sqr->fg = fg;
87 sqr->bg = bg;
88 if (!visible)
89 return;
90 if (lazy)
91 dirty[r] = 1;
92 else
93 _term_show(r, c, 0);
96 static void lazy_cursor(int put)
98 if (!visible)
99 return;
100 if (lazy)
101 dirty[row] = 1;
102 else
103 _term_show(row, col, put);
106 static void lazy_clean()
108 if (visible)
109 memset(dirty, 0, sizeof(*dirty) * MAXLINES);
112 static void lazy_flush()
114 int i;
115 if (!visible)
116 return;
117 _term_show(row, col, 0);
118 for (i = 0; i < pad_rows(); i++)
119 if (dirty[i])
120 _draw_row(i);
121 lazy_clean();
122 _term_show(row, col, 1);
125 static int origin(void)
127 return mode & MODE_ORIGIN;
130 static void setsize(void)
132 struct winsize size;
133 size.ws_col = pad_cols();
134 size.ws_row = pad_rows();
135 size.ws_xpixel = 0;
136 size.ws_ypixel = 0;
137 ioctl(term->fd, TIOCSWINSZ, &size);
140 #define PTYBUFSIZE (1 << 13)
141 static char ptybuf[PTYBUFSIZE];
142 static int ptylen;
143 static int ptycur;
144 static void waitpty(void)
146 struct pollfd ufds[1];
147 ufds[0].fd = term->fd;
148 ufds[0].events = POLLIN;
149 poll(ufds, 1, -1);
152 static int readpty(void)
154 if (ptycur < ptylen)
155 return ptybuf[ptycur++];
156 if (!term->fd)
157 return -1;
158 ptylen = read(term->fd, ptybuf, PTYBUFSIZE);
159 if (ptylen == -1 && errno == EAGAIN) {
160 waitpty();
161 ptylen = read(term->fd, ptybuf, PTYBUFSIZE);
163 if (ptylen > 0) {
164 ptycur = 1;
165 return ptybuf[0];
167 return -1;
170 static void empty_rows(int sr, int er)
172 memset(SQRADDR(sr, 0), 0, (er - sr) * sizeof(*screen) * pad_cols());
175 static void blank_rows(int sr, int er)
177 empty_rows(sr, er);
178 lazy_draw(sr, er);
179 lazy_cursor(1);
182 static void scroll_screen(int sr, int nr, int n)
184 lazy_cursor(0);
185 memmove(SQRADDR(sr + n, 0), SQRADDR(sr, 0),
186 nr * pad_cols() * sizeof(*screen));
187 if (n > 0)
188 empty_rows(sr, sr + n);
189 else
190 empty_rows(sr + nr + n, sr + nr);
191 lazy_draw(MIN(sr, sr + n), MAX(sr + nr, sr + nr + n));
192 lazy_cursor(1);
195 static void insert_lines(int n)
197 int sr = MAX(top, row);
198 int nr = bot - row - n;
199 if (nr > 0)
200 scroll_screen(sr, nr, n);
203 static void delete_lines(int n)
205 int r = MAX(top, row);
206 int sr = r + n;
207 int nr = bot - r - n;
208 if (nr > 0)
209 scroll_screen(sr, nr, -n);
212 static void move_cursor(int r, int c)
214 int t, b;
215 lazy_cursor(0);
216 t = origin() ? top : 0;
217 b = origin() ? bot : pad_rows();
218 row = MAX(t, MIN(r, b - 1));
219 col = MAX(0, MIN(c, pad_cols() - 1));
220 lazy_cursor(1);
223 static void advance(int dr, int dc, int scrl)
225 int r = row + dr;
226 int c = col + dc;
227 if (c >= pad_cols()) {
228 if (!scrl || (mode & MODE_NOWRAP)) {
229 c = pad_cols() - 1;
230 } else {
231 r++;
232 c = 0;
235 if (r >= bot && scrl) {
236 int n = bot - r - 1;
237 int nr = (bot - top) + n;
238 scroll_screen(top + -n, nr, n);
240 if (r < top && scrl) {
241 int n = top - r;
242 int nr = (bot - top) - n;
243 scroll_screen(top, nr, n);
245 r = MIN(bot - 1, MAX(top, r));
246 move_cursor(r, MAX(0, c));
249 void term_send(int c)
251 unsigned char b = (unsigned char) c;
252 if (term->fd)
253 write(term->fd, &b, 1);
256 void term_sendstr(char *s)
258 if (term->fd)
259 write(term->fd, s, strlen(s));
262 static void setmode(int m)
264 if (m == 0) {
265 fg = FGCOLOR;
266 bg = BGCOLOR;
268 if (m == 1)
269 fg = fg | 0x08;
270 if (m == 7) {
271 int t = fg;
272 fg = bg;
273 bg = t;
275 if (m >= 30 && m <= 37)
276 fg = m - 30;
277 if (m >= 40 && m <= 47)
278 bg = m - 40;
281 static void kill_chars(int sc, int ec)
283 int i;
284 for (i = sc; i < ec; i++)
285 lazy_put(' ', row, i);
286 lazy_cursor(1);
289 static void move_chars(int sc, int nc, int n)
291 lazy_cursor(0);
292 memmove(SQRADDR(row, sc + n), SQRADDR(row, sc), nc * sizeof(*screen));
293 if (n > 0)
294 memset(SQRADDR(row, sc), 0, n * sizeof(*screen));
295 else
296 memset(SQRADDR(row, pad_cols() + n), 0, -n * sizeof(*screen));
297 lazy_drawcols(row, MIN(sc, sc + n), pad_cols());
298 lazy_cursor(1);
301 static void delete_chars(int n)
303 int sc = col + n;
304 int nc = pad_cols() - sc;
305 move_chars(sc, nc, -n);
308 static void insert_chars(int n)
310 int nc = pad_cols() - col - n;
311 move_chars(col, nc, n);
314 static void term_blank(void)
316 memset(screen, 0, MAXCHARS * sizeof(*screen));
317 if (visible) {
318 pad_blank(bg);
319 lazy_clean();
323 static void ctlseq(void);
324 void term_read(void)
326 ctlseq();
327 lazy = 1;
328 while (ptycur < ptylen)
329 ctlseq();
330 lazy_flush();
331 lazy = 0;
334 void term_exec(char *cmd)
336 memset(term, 0, sizeof(*term));
337 term->cur.bot = term->sav.bot = pad_rows();
338 term_load(term, visible);
339 if ((term->pid = forkpty(&term->fd, NULL, NULL, NULL)) == -1) {
340 perror("failed to fork");
341 term->fd = 0;
342 return;
344 if (!term->pid) {
345 setenv("TERM", "linux", 1);
346 execlp(cmd, cmd, NULL);
347 exit(1);
349 fcntl(term->fd, F_SETFD, fcntl(term->fd, F_GETFD) | FD_CLOEXEC);
350 fcntl(term->fd, F_SETFL, fcntl(term->fd, F_GETFL) | O_NONBLOCK);
351 setsize();
352 setmode(0);
353 term_blank();
356 static void misc_save(struct term_state *state)
358 state->row = row;
359 state->col = col;
360 state->fg = fg;
361 state->bg = bg;
362 state->top = top;
363 state->bot = bot;
364 state->mode = mode;
367 static void misc_load(struct term_state *state)
369 row = state->row;
370 col = state->col;
371 fg = state->fg;
372 bg = state->bg;
373 top = state->top;
374 bot = state->bot;
375 mode = state->mode;
378 void term_save(struct term *term)
380 visible = 0;
381 misc_save(&term->cur);
384 void term_load(struct term *t, int flags)
386 term = t;
387 misc_load(&term->cur);
388 screen = term->screen;
389 visible = flags;
390 if (flags == TERM_REDRAW) {
391 if (term->fd) {
392 lazy_draw(0, pad_rows());
393 lazy_cursor(1);
394 } else {
395 pad_blank(0);
400 void term_end(void)
402 if (term->fd)
403 close(term->fd);
404 term->fd = 0;
405 row = col = 0;
406 fg = 0;
407 bg = 0;
408 term_blank();
411 void set_region(int t, int b)
413 top = MIN(pad_rows(), MAX(0, t - 1));
414 bot = MIN(pad_rows(), MAX(0, b ? b : pad_rows()));
415 if (origin())
416 move_cursor(top, 0);
419 #include "vt102.c"