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
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
;
34 static unsigned int mode
;
37 #define MAXLINES (1 << 13)
38 static int dirty
[MAXLINES
];
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
) {
63 pad_put(sqr
->c
, r
, c
, fg
, bg
);
66 static void _draw_row(int r
)
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()))
77 static void lazy_draw(int sr
, int er
)
82 for (i
= sr
; i
< er
; i
++) {
90 static void lazy_drawcols(int r
, int sc
, int ec
)
98 for (i
= sc
; i
< ec
; i
++)
103 static void lazy_put(int ch
, int r
, int c
)
105 struct square
*sqr
= SQRADDR(r
, c
);
117 static void lazy_cursor(int put
)
124 _term_show(row
, col
, put
);
127 static void lazy_clean()
130 memset(dirty
, 0, sizeof(*dirty
) * MAXLINES
);
133 static void lazy_flush()
138 _term_show(row
, col
, 0);
139 for (i
= 0; i
< pad_rows(); i
++)
143 _term_show(row
, col
, 1);
146 static void term_redraw(void)
149 for (i
= 0; i
< pad_rows(); i
++)
151 _term_show(row
, col
, 1);
154 static int origin(void)
156 return mode
& MODE_ORIGIN
;
159 static void setsize(void)
162 size
.ws_col
= pad_cols();
163 size
.ws_row
= pad_rows();
166 ioctl(term
->fd
, TIOCSWINSZ
, &size
);
169 #define PTYBUFSIZE (1 << 13)
170 static char ptybuf
[PTYBUFSIZE
];
173 static void waitpty(void)
175 struct pollfd ufds
[1];
176 ufds
[0].fd
= term
->fd
;
177 ufds
[0].events
= POLLIN
;
181 static int readpty(void)
184 return ptybuf
[ptycur
++];
187 ptylen
= read(term
->fd
, ptybuf
, PTYBUFSIZE
);
188 if (ptylen
== -1 && errno
== EAGAIN
) {
190 ptylen
= read(term
->fd
, ptybuf
, PTYBUFSIZE
);
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
)
211 static void scroll_screen(int sr
, int nr
, int n
)
214 memmove(SQRADDR(sr
+ n
, 0), SQRADDR(sr
, 0),
215 nr
* pad_cols() * sizeof(*screen
));
217 empty_rows(sr
, sr
+ n
);
219 empty_rows(sr
+ nr
+ n
, sr
+ nr
);
220 lazy_draw(MIN(sr
, sr
+ n
), MAX(sr
+ nr
, sr
+ nr
+ n
));
224 static void insert_lines(int n
)
226 int sr
= MAX(top
, row
);
227 int nr
= bot
- row
- n
;
229 scroll_screen(sr
, nr
, n
);
232 static void delete_lines(int n
)
234 int r
= MAX(top
, row
);
236 int nr
= bot
- r
- n
;
238 scroll_screen(sr
, nr
, -n
);
241 static void move_cursor(int r
, int c
)
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));
250 mode
= BIT_SET(mode
, MODE_WRAPREADY
, 0);
253 static void advance(int dr
, int dc
, int scrl
)
257 if (r
>= bot
&& scrl
) {
259 int nr
= (bot
- top
) + n
;
260 scroll_screen(top
+ -n
, nr
, n
);
262 if (r
< top
&& scrl
) {
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
));
272 static void insertchar(int c
)
275 if (mode
& MODE_WRAPREADY
)
277 lazy_put(c
, row
, col
);
278 wrapready
= col
== pad_cols() - 1;
281 mode
= BIT_SET(mode
, MODE_WRAPREADY
, 1);
284 void term_send(int c
)
286 unsigned char b
= (unsigned char) c
;
288 write(term
->fd
, &b
, 1);
291 static void term_sendstr(char *s
)
294 write(term
->fd
, s
, strlen(s
));
297 static void setattr(int m
)
319 fg
= m
> 37 ? FGCOLOR
: m
- 30;
321 bg
= m
> 47 ? BGCOLOR
: m
- 40;
325 static void kill_chars(int sc
, int ec
)
328 for (i
= sc
; i
< ec
; i
++)
329 lazy_put(' ', row
, i
);
333 static void move_chars(int sc
, int nc
, int n
)
336 memmove(SQRADDR(row
, sc
+ n
), SQRADDR(row
, sc
), nc
* sizeof(*screen
));
338 memset(SQRADDR(row
, sc
), 0, n
* sizeof(*screen
));
340 memset(SQRADDR(row
, pad_cols() + n
), 0, -n
* sizeof(*screen
));
341 lazy_drawcols(row
, MIN(sc
, sc
+ n
), pad_cols());
345 static void delete_chars(int 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
));
362 pad_blank(bgcolor());
367 static void ctlseq(void);
371 while (ptycur
< ptylen
) {
372 if (ptylen
- ptycur
> 15)
381 static void term_reset(void)
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");
400 setenv("TERM", "linux", 1);
401 execlp(cmd
, cmd
, NULL
);
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
);
410 static void misc_save(struct term_state
*state
)
419 static void misc_load(struct term_state
*state
)
428 void term_save(struct term
*term
)
431 misc_save(&term
->cur
);
436 void term_load(struct term
*t
, int flags
)
439 misc_load(&term
->cur
);
440 screen
= term
->screen
;
444 if (flags
== TERM_REDRAW
) {
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()));