draw: fix framebuffer mmap() size
[fbpad.git] / term.c
blob4a525ff5317a73d2d1b5d44d73b38c5f634a9d2f
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 <sys/ioctl.h>
11 #include <unistd.h>
12 #include "config.h"
13 #include "pad.h"
14 #include "util.h"
15 #include "term.h"
17 #define MODE_CURSOR 0x01
18 #define MODE_WRAP 0x02
19 #define MODE_ORIGIN 0x04
20 #define MODE_AUTOCR 0x08
21 #define MODE_DEFAULT (MODE_CURSOR | MODE_WRAP)
22 #define ATTR_BOLD 0x10
23 #define ATTR_REV 0x20
24 #define ATTR_ALL (ATTR_BOLD | ATTR_REV)
25 #define MODE_WRAPREADY 0x80
27 #define BIT_SET(i, b, val) ((val) ? ((i) | (b)) : ((i) & ~(b)))
28 #define OFFSET(r, c) ((r) * pad_cols() + (c))
30 static struct term *term;
31 static unsigned int *screen;
32 static unsigned char *fgs, *bgs;
33 static int row, col;
34 static unsigned char fg, bg;
35 static int top, bot;
36 static unsigned int mode;
37 static int visible;
39 #define MAXLINES (1 << 13)
40 static int dirty[MAXLINES];
41 static int lazy;
43 static unsigned char fgcolor(void)
45 int c = mode & ATTR_REV ? bg : fg;
46 return mode & ATTR_BOLD ? c | 0x08 : c;
49 static unsigned char bgcolor(void)
51 return mode & ATTR_REV ? fg : bg;
54 static void _term_show(int r, int c, int cursor)
56 int i = OFFSET(r, c);
57 unsigned char fg = screen[i] ? fgs[i] : fgcolor();
58 unsigned char bg = screen[i] ? bgs[i] : bgcolor();
59 if (cursor && mode & MODE_CURSOR) {
60 int t = fg;
61 fg = bg;
62 bg = t;
64 if (visible)
65 pad_put(screen[i], r, c, fg, bg);
68 static void _draw_row(int r)
70 int i;
71 pad_blankrow(r, bgcolor());
72 for (i = 0; i < pad_cols(); i++) {
73 unsigned int c = screen[OFFSET(r, i)];
74 if (c && (c != ' ' || bgs[OFFSET(r, i)] != bgcolor()))
75 _term_show(r, i, 0);
79 static void lazy_draw(int sr, int er)
81 int i;
82 if (!visible)
83 return;
84 for (i = sr; i < er; i++) {
85 if (lazy)
86 dirty[i] = 1;
87 else
88 _draw_row(i);
92 static void lazy_drawcols(int r, int sc, int ec)
94 int i;
95 if (!visible)
96 return;
97 if (lazy) {
98 dirty[r] = 1;
99 } else {
100 for (i = sc; i < ec; i++)
101 _term_show(r, i, 0);
105 static void lazy_put(int ch, int r, int c)
107 int i = OFFSET(r, c);
108 screen[i] = ch;
109 fgs[i] = fgcolor();
110 bgs[i] = bgcolor();
111 if (!visible)
112 return;
113 if (lazy)
114 dirty[r] = 1;
115 else
116 _term_show(r, c, 0);
119 static void lazy_cursor(int put)
121 if (!visible)
122 return;
123 if (lazy)
124 dirty[row] = 1;
125 else
126 _term_show(row, col, put);
129 static void lazy_clean(void)
131 if (visible)
132 memset(dirty, 0, sizeof(*dirty) * MAXLINES);
135 static void lazy_flush(void)
137 int i;
138 if (!visible)
139 return;
140 _term_show(row, col, 0);
141 for (i = 0; i < pad_rows(); i++)
142 if (dirty[i])
143 _draw_row(i);
144 lazy_clean();
145 _term_show(row, col, 1);
148 static void term_redraw(void)
150 int i;
151 for (i = 0; i < pad_rows(); i++)
152 _draw_row(i);
153 _term_show(row, col, 1);
156 static int origin(void)
158 return mode & MODE_ORIGIN;
161 #define PTYBUFSIZE (1 << 13)
162 static char ptybuf[PTYBUFSIZE];
163 static int ptylen;
164 static int ptycur;
165 static void waitpty(void)
167 struct pollfd ufds[1];
168 ufds[0].fd = term->fd;
169 ufds[0].events = POLLIN;
170 poll(ufds, 1, -1);
173 static int readpty(void)
175 if (ptycur < ptylen)
176 return ptybuf[ptycur++];
177 if (!term->fd)
178 return -1;
179 ptylen = read(term->fd, ptybuf, PTYBUFSIZE);
180 if (ptylen == -1 && errno == EAGAIN) {
181 waitpty();
182 ptylen = read(term->fd, ptybuf, PTYBUFSIZE);
184 if (ptylen > 0) {
185 ptycur = 1;
186 return ptybuf[0];
188 return -1;
191 static void screen_move(int dst, int src, int n)
193 memmove(screen + dst, screen + src, n * sizeof(*screen));
194 memmove(fgs + dst, fgs + src, n);
195 memmove(bgs + dst, bgs + src, n);
198 static void screen_reset(int i, int n)
200 memset(screen + i, 0, n * sizeof(*screen));
201 memset(fgs + i, fg, n);
202 memset(bgs + i, bg, n);
205 static void empty_rows(int sr, int er)
207 screen_reset(OFFSET(sr, 0), (er - sr) * pad_cols());
210 static void blank_rows(int sr, int er)
212 empty_rows(sr, er);
213 lazy_draw(sr, er);
214 lazy_cursor(1);
217 static void scroll_screen(int sr, int nr, int n)
219 lazy_cursor(0);
220 screen_move(OFFSET(sr + n, 0), OFFSET(sr, 0), nr * pad_cols());
221 if (n > 0)
222 empty_rows(sr, sr + n);
223 else
224 empty_rows(sr + nr + n, sr + nr);
225 lazy_draw(MIN(sr, sr + n), MAX(sr + nr, sr + nr + n));
226 lazy_cursor(1);
229 static void insert_lines(int n)
231 int sr = MAX(top, row);
232 int nr = bot - row - n;
233 if (nr > 0)
234 scroll_screen(sr, nr, n);
237 static void delete_lines(int n)
239 int r = MAX(top, row);
240 int sr = r + n;
241 int nr = bot - r - n;
242 if (nr > 0)
243 scroll_screen(sr, nr, -n);
246 static void move_cursor(int r, int c)
248 int t, b;
249 lazy_cursor(0);
250 t = origin() ? top : 0;
251 b = origin() ? bot : pad_rows();
252 row = MAX(t, MIN(r, b - 1));
253 col = MAX(0, MIN(c, pad_cols() - 1));
254 lazy_cursor(1);
255 mode = BIT_SET(mode, MODE_WRAPREADY, 0);
258 static void advance(int dr, int dc, int scrl)
260 int r = row + dr;
261 int c = col + dc;
262 if (r >= bot && scrl) {
263 int n = bot - r - 1;
264 int nr = (bot - top) + n;
265 scroll_screen(top + -n, nr, n);
267 if (r < top && scrl) {
268 int n = top - r;
269 int nr = (bot - top) - n;
270 scroll_screen(top, nr, n);
272 r = MIN(bot - 1, MAX(top, r));
273 c = MIN(pad_cols() - 1, MAX(0, c));
274 move_cursor(r, c);
277 static void insertchar(int c)
279 int wrapready;
280 if (mode & MODE_WRAPREADY)
281 advance(1, -col, 1);
282 lazy_put(c, row, col);
283 wrapready = col == pad_cols() - 1;
284 advance(0, 1, 1);
285 if (wrapready)
286 mode = BIT_SET(mode, MODE_WRAPREADY, 1);
289 void term_send(int c)
291 unsigned char b = (unsigned char) c;
292 if (term->fd)
293 write(term->fd, &b, 1);
296 static void term_sendstr(char *s)
298 if (term->fd)
299 write(term->fd, s, strlen(s));
302 static void setattr(int m)
304 switch (m) {
305 case 0:
306 fg = FGCOLOR;
307 bg = BGCOLOR;
308 mode &= ~ATTR_ALL;
309 break;
310 case 1:
311 mode |= ATTR_BOLD;
312 break;
313 case 7:
314 mode |= ATTR_REV;
315 break;
316 case 22:
317 mode &= ~ATTR_BOLD;
318 break;
319 case 27:
320 mode &= ~ATTR_REV;
321 break;
322 default:
323 if ((m / 10) == 3)
324 fg = m > 37 ? FGCOLOR : m - 30;
325 if ((m / 10) == 4)
326 bg = m > 47 ? BGCOLOR : m - 40;
330 static void kill_chars(int sc, int ec)
332 int i;
333 for (i = sc; i < ec; i++)
334 lazy_put(' ', row, i);
335 lazy_cursor(1);
338 static void move_chars(int sc, int nc, int n)
340 lazy_cursor(0);
341 screen_move(OFFSET(row, sc + n), OFFSET(row, sc), nc);
342 if (n > 0)
343 screen_reset(OFFSET(row, sc), n);
344 else
345 screen_reset(OFFSET(row, pad_cols() + n), -n);
346 lazy_drawcols(row, MIN(sc, sc + n), pad_cols());
347 lazy_cursor(1);
350 static void delete_chars(int n)
352 int sc = col + n;
353 int nc = pad_cols() - sc;
354 move_chars(sc, nc, -n);
357 static void insert_chars(int n)
359 int nc = pad_cols() - col - n;
360 move_chars(col, nc, n);
363 static void term_blank(void)
365 screen_reset(0, pad_rows() * pad_cols());
366 if (visible) {
367 pad_blank(bgcolor());
368 lazy_clean();
372 static void ctlseq(void);
373 void term_read(void)
375 ctlseq();
376 while (ptycur < ptylen) {
377 if (ptylen - ptycur > 15)
378 lazy = 1;
379 ctlseq();
381 if (lazy)
382 lazy_flush();
383 lazy = 0;
386 static void term_reset(void)
388 row = col = 0;
389 top = 0;
390 bot = pad_rows();
391 mode = MODE_DEFAULT;
392 setattr(0);
393 term_blank();
396 static int _openpty(int *master, int *slave)
398 int unlock = 0;
399 int ptyno = 0;
400 char name[20];
401 if ((*master = open("/dev/ptmx", O_RDWR)) == -1)
402 return -1;
403 if (ioctl(*master, TIOCSPTLCK, &unlock) == -1)
404 return -1;
405 if (ioctl(*master, TIOCGPTN, &ptyno) == -1)
406 return -1;
407 sprintf(name, "/dev/pts/%d", ptyno);
408 *slave = open(name, O_RDWR | O_NOCTTY);
409 return 0;
412 static void _login(int fd)
414 static char env[] = "TERM=linux";
415 struct winsize winp;
416 winp.ws_col = pad_cols();
417 winp.ws_row = pad_rows();
418 winp.ws_xpixel = 0;
419 winp.ws_ypixel = 0;
420 setsid();
421 ioctl(fd, TIOCSCTTY, NULL);
422 ioctl(fd, TIOCSWINSZ, &winp);
423 dup2(fd, 0);
424 dup2(fd, 1);
425 dup2(fd, 2);
426 if (fd > 2)
427 close(fd);
428 putenv(env);
431 void term_exec(char *cmd)
433 int master, slave;
434 memset(term, 0, sizeof(*term));
435 if (_openpty(&master, &slave) == -1)
436 return;
437 if ((term->pid = fork()) == -1)
438 return;
439 if (!term->pid) {
440 _login(slave);
441 close(master);
442 execlp(cmd, cmd, NULL);
443 exit(1);
445 close(slave);
446 term->fd = master;
447 fcntl(term->fd, F_SETFD, fcntl(term->fd, F_GETFD) | FD_CLOEXEC);
448 fcntl(term->fd, F_SETFL, fcntl(term->fd, F_GETFL) | O_NONBLOCK);
449 term_reset();
452 static void misc_save(struct term_state *state)
454 state->row = row;
455 state->col = col;
456 state->fg = fg;
457 state->bg = bg;
458 state->mode = mode;
461 static void misc_load(struct term_state *state)
463 row = state->row;
464 col = state->col;
465 fg = state->fg;
466 bg = state->bg;
467 mode = state->mode;
470 void term_save(struct term *term)
472 visible = 0;
473 misc_save(&term->cur);
474 term->top = top;
475 term->bot = bot;
478 void term_load(struct term *t, int flags)
480 term = t;
481 misc_load(&term->cur);
482 screen = term->screen;
483 fgs = term->fgs;
484 bgs = term->bgs;
485 visible = flags;
486 top = term->top;
487 bot = term->bot;
488 if (flags == TERM_REDRAW) {
489 if (term->fd)
490 term_redraw();
491 else
492 pad_blank(0);
496 void term_end(void)
498 if (term->fd)
499 close(term->fd);
500 memset(term, 0, sizeof(*term));
501 term_load(term, visible ? TERM_REDRAW : TERM_HIDDEN);
504 static void set_region(int t, int b)
506 top = MIN(pad_rows(), MAX(0, t - 1));
507 bot = MIN(pad_rows(), MAX(0, b ? b : pad_rows()));
508 if (origin())
509 move_cursor(top, 0);
512 void term_screenshot(void)
514 FILE *fp = fopen(SCREENSHOT, "w");
515 int i, j;
516 for (i = 0; i < pad_rows(); i++) {
517 for (j = 0; j < pad_cols(); j++) {
518 int c = screen[OFFSET(i, j)];
519 fputc(c ? c : ' ', fp);
521 fputc('\n', fp);
523 fclose(fp);
526 #include "vt102.c"