ee93e031f5733ede6287e509a31b548305a2010a
[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         tcsetattr(ttyfd, TCSAFLUSH, &saved_term);
60         close(ttyfd);
61         ttyfd = -1;
62 }
63
64 void term_getsize(int *width, int *height)
65 {
66         *width = term_width;
67         *height = term_height;
68 }
69
70 void term_resize_func(void (*func)(int, int))
71 {
72         cb_resized = func;
73 }
74
75
76 static char termbuf[1024];
77 static int termbuf_len;
78
79 void term_send(const char *s, int size)
80 {
81         if(size >= sizeof termbuf) {
82                 /* too large, just flush the buffer and write directly to the tty */
83                 term_flush();
84                 write(ttyfd, s, size);
85         } else {
86                 if(size >= sizeof termbuf - termbuf_len) {
87                         term_flush();
88                 }
89                 memcpy(termbuf + termbuf_len, s, size);
90                 termbuf_len += size;
91         }
92 }
93
94 void term_puts(const char *s)
95 {
96         term_send(s, strlen(s));
97 }
98
99 void term_printf(const char *fmt, ...)
100 {
101         static char *buf;
102         static long bufsz;
103         va_list ap;
104         long len;
105
106         if(!buf) {
107                 bufsz = 512;
108                 if(!(buf = malloc(bufsz))) {
109                         return;
110                 }
111         }
112
113         for(;;) {
114                 va_start(ap, fmt);
115                 len = vsnprintf(buf, bufsz, fmt, ap);
116                 va_end(ap);
117
118                 if(len < bufsz) break;
119                 if(len < 0) {
120                         void *tmp;
121                         long n = bufsz << 1;
122                         if(!(tmp = realloc(buf, n))) {
123                                 break;  /* if realloc fails, will result in truncated output */
124                         }
125                 }
126         }
127
128         term_send(buf, len);
129 }
130
131 void term_flush(void)
132 {
133         if(termbuf_len > 0) {
134                 write(ttyfd, termbuf, termbuf_len);
135                 termbuf_len = 0;
136         }
137 }
138
139 void term_clear(void)
140 {
141         term_puts("\033[2J");
142 }
143
144 int term_getchar(void)
145 {
146         int res;
147         char c;
148         while((res = read(ttyfd, &c, 1)) < 0 && errno == EINTR);
149         if(res <= 0) return -1;
150         return c;
151 }
152
153
154 static void sighandler(int s)
155 {
156         struct winsize winsz;
157
158         signal(s, sighandler);
159
160         switch(s) {
161         case SIGWINCH:
162                 ioctl(1, TIOCGWINSZ, &winsz);
163                 term_width = winsz.ws_col;
164                 term_height = winsz.ws_row;
165                 /* redraw */
166                 break;
167
168         default:
169                 break;
170         }
171 }