don't crash with no buffers, and clear the screen on exit
[visor] / visor / src / term.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <signal.h>
5 #include <errno.h>
6 #include <stdarg.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <termios.h>
10 #include <sys/ioctl.h>
11 #include "term.h"
12
13 static void sighandler(int s);
14
15 static int term_width, term_height;
16 static int ttyfd = -1;
17 static int selfpipe[2];
18 static struct termios saved_term;
19
20 static void (*cb_resized)(int, int);
21
22
23 int term_init(const char *ttypath)
24 {
25         struct termios term;
26         struct winsize winsz;
27
28         if((ttyfd = open(ttypath ? ttypath : "/dev/tty", O_RDWR)) == -1) {
29                 perror("failed to open /dev/tty");
30                 return -1;
31         }
32         if(tcgetattr(ttyfd, &term) == -1) {
33                 perror("failed to get terminal attr");
34                 return -1;
35         }
36         saved_term = term;
37         term.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
38         term.c_oflag &= ~OPOST;
39         term.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
40         term.c_cflag = (term.c_cflag & ~(CSIZE | PARENB)) | CS8;
41
42         if(tcsetattr(ttyfd, TCSAFLUSH, &term) == -1) {
43                 perror("failed to change terminal attributes");
44                 return -1;
45         }
46
47         ioctl(1, TIOCGWINSZ, &winsz);
48         term_width = winsz.ws_col;
49         term_height = winsz.ws_row;
50
51         pipe(selfpipe);
52
53         signal(SIGWINCH, sighandler);
54         return 0;
55 }
56
57 void term_cleanup(void)
58 {
59         term_clear();
60         term_setcursor(0, 0);
61         term_flush();
62         tcsetattr(ttyfd, TCSAFLUSH, &saved_term);
63         close(ttyfd);
64         ttyfd = -1;
65 }
66
67 void term_reset(void)
68 {
69         term_puts("\033c");
70         term_flush();
71 }
72
73 void term_getsize(int *width, int *height)
74 {
75         *width = term_width;
76         *height = term_height;
77 }
78
79 void term_resize_func(void (*func)(int, int))
80 {
81         cb_resized = func;
82 }
83
84
85 static char termbuf[1024];
86 static int termbuf_len;
87
88 void term_send(const char *s, int size)
89 {
90         if(size >= sizeof termbuf) {
91                 /* too large, just flush the buffer and write directly to the tty */
92                 term_flush();
93                 write(ttyfd, s, size);
94         } else {
95                 if(size >= sizeof termbuf - termbuf_len) {
96                         term_flush();
97                 }
98                 memcpy(termbuf + termbuf_len, s, size);
99                 termbuf_len += size;
100         }
101 }
102
103 void term_putchar(char c)
104 {
105         term_send(&c, 1);
106 }
107
108 void term_puts(const char *s)
109 {
110         term_send(s, strlen(s));
111 }
112
113 void term_printf(const char *fmt, ...)
114 {
115         static char *buf;
116         static long bufsz;
117         va_list ap;
118         long len;
119
120         if(!buf) {
121                 bufsz = 512;
122                 if(!(buf = malloc(bufsz))) {
123                         return;
124                 }
125         }
126
127         for(;;) {
128                 va_start(ap, fmt);
129                 len = vsnprintf(buf, bufsz, fmt, ap);
130                 va_end(ap);
131
132                 if(len < bufsz) break;
133                 if(len < 0) {
134                         void *tmp;
135                         long n = bufsz << 1;
136                         if(!(tmp = realloc(buf, n))) {
137                                 break;  /* if realloc fails, will result in truncated output */
138                         }
139                 }
140         }
141
142         term_send(buf, len);
143 }
144
145 void term_flush(void)
146 {
147         if(termbuf_len > 0) {
148                 write(ttyfd, termbuf, termbuf_len);
149                 termbuf_len = 0;
150         }
151 }
152
153 void term_clear(void)
154 {
155         term_puts("\033[2J");
156 }
157
158 void term_cursor(int show)
159 {
160         term_printf("\033[?25%c", show ? 'h' : 'l');
161 }
162
163 void term_setcursor(int row, int col)
164 {
165         term_printf("\033[%d;%dH", row + 1, col + 1);
166 }
167
168 int term_getchar(void)
169 {
170         int res;
171         char c;
172         while((res = read(ttyfd, &c, 1)) < 0 && errno == EINTR);
173         if(res <= 0) return -1;
174         return c;
175 }
176
177
178 static void sighandler(int s)
179 {
180         struct winsize winsz;
181
182         signal(s, sighandler);
183
184         switch(s) {
185         case SIGWINCH:
186                 ioctl(1, TIOCGWINSZ, &winsz);
187                 term_width = winsz.ws_col;
188                 term_height = winsz.ws_row;
189                 /* redraw */
190                 break;
191
192         default:
193                 break;
194         }
195 }