term: cast characters read from the pty to unsigned char
[fbpad.git] / fbpad.c
blob4c6a508f0682c0cd8f9286a68b3704790e7e4b3b
1 /*
2 * fbpad - a small framebuffer virtual terminal
4 * Copyright (C) 2009-2012 Ali Gholami Rudi <ali at rudi dot ir>
6 * This program is released under the modified BSD license.
7 */
8 #include <ctype.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <poll.h>
12 #include <signal.h>
13 #include <string.h>
14 #include <sys/ioctl.h>
15 #include <sys/wait.h>
16 #include <termios.h>
17 #include <unistd.h>
18 #include <linux/vt.h>
19 #include "config.h"
20 #include "pad.h"
21 #include "term.h"
22 #include "util.h"
23 #include "scrsnap.h"
24 #include "draw.h"
26 #define CTRLKEY(x) ((x) - 96)
27 #define BADPOLLFLAGS (POLLHUP | POLLERR | POLLNVAL)
28 #define NTAGS (sizeof(tags) - 1)
29 #define NTERMS (NTAGS * 2)
30 #define TERMOPEN(i) (terms[i].fd)
31 #define TERMSNAP(i) (strchr(TAGS_SAVED, tags[(i) % NTAGS]))
33 static char tags[] = TAGS;
34 static struct term terms[NTERMS];
35 static int tops[NTAGS]; /* top terms of tags */
36 static int ctag; /* current tag */
37 static int ltag; /* the last tag */
38 static int exitit;
39 static int hidden;
40 static int locked;
41 static char pass[1024];
42 static int passlen;
44 static int readchar(void)
46 char b;
47 if (read(STDIN_FILENO, &b, 1) > 0)
48 return (int) b;
49 return -1;
52 static int cterm(void)
54 return tops[ctag] * NTAGS + ctag;
57 static void term_switch(int oidx, int nidx, int show, int save, int load)
59 int flags = show ? (load ? TERM_REDRAW : TERM_VISIBLE) : TERM_HIDDEN;
60 if (save && TERMOPEN(oidx) && TERMSNAP(oidx))
61 scr_snap(&terms[oidx]);
62 term_save(&terms[oidx]);
63 if (show && load && TERMOPEN(nidx) && TERMSNAP(nidx))
64 flags = scr_load(&terms[nidx]) ? TERM_REDRAW : TERM_VISIBLE;
65 term_load(&terms[nidx], flags);
68 static void showterm(int n)
70 if (cterm() == n)
71 return;
72 if (ctag != n % NTAGS)
73 ltag = ctag;
74 term_switch(cterm(), n, !hidden, !hidden, !hidden);
75 ctag = n % NTAGS;
76 tops[ctag] = n / NTAGS;
79 static void showtag(int n)
81 showterm(tops[n] * NTAGS + n);
84 static struct term *mainterm(void)
86 if (TERMOPEN(cterm()))
87 return &terms[cterm()];
88 return NULL;
91 static void exec_cmd(char *file)
93 if (!mainterm())
94 term_exec(file);
97 static int altterm(int n)
99 return n < NTAGS ? n + NTAGS : n - NTAGS;
102 static void nextterm(void)
104 int n = (cterm() + 1) % NTERMS;
105 while (n != cterm()) {
106 if (TERMOPEN(n)) {
107 showterm(n);
108 break;
110 n = (n + 1) % NTERMS;
114 static void showtags(void)
116 int colors[] = {15, 4, 2};
117 int c = 0;
118 int r = pad_rows() - 1;
119 int i;
120 pad_put('T', r, c++, FGCOLOR, BGCOLOR);
121 pad_put('A', r, c++, FGCOLOR, BGCOLOR);
122 pad_put('G', r, c++, FGCOLOR, BGCOLOR);
123 pad_put('S', r, c++, FGCOLOR, BGCOLOR);
124 pad_put(':', r, c++, FGCOLOR, BGCOLOR);
125 pad_put(' ', r, c++, FGCOLOR, BGCOLOR);
126 for (i = 0; i < NTAGS; i++) {
127 int nt = 0;
128 if (TERMOPEN(i))
129 nt++;
130 if (TERMOPEN(altterm(i)))
131 nt++;
132 pad_put(i == ctag ? '(' : ' ', r, c++, FGCOLOR, BGCOLOR);
133 if (TERMSNAP(i))
134 pad_put(tags[i], r, c++, !nt ? BGCOLOR : colors[nt], 15);
135 else
136 pad_put(tags[i], r, c++, colors[nt], BGCOLOR);
137 pad_put(i == ctag ? ')' : ' ', r, c++, FGCOLOR, BGCOLOR);
141 static void directkey(void)
143 int c = readchar();
144 if (PASS && locked) {
145 if (c == '\r') {
146 pass[passlen] = '\0';
147 if (!strcmp(PASS, pass))
148 locked = 0;
149 passlen = 0;
150 return;
152 if (isprint(c) && passlen + 1 < sizeof(pass))
153 pass[passlen++] = c;
154 return;
156 if (c == ESC) {
157 switch ((c = readchar())) {
158 case 'c':
159 exec_cmd(SHELL);
160 return;
161 case 'm':
162 exec_cmd(MAIL);
163 return;
164 case 'e':
165 exec_cmd(EDITOR);
166 return;
167 case 'j':
168 case 'k':
169 showterm(altterm(cterm()));
170 return;
171 case 'o':
172 showtag(ltag);
173 return;
174 case 'p':
175 showtags();
176 return;
177 case '\t':
178 nextterm();
179 return;
180 case CTRLKEY('q'):
181 exitit = 1;
182 return;
183 case 's':
184 term_screenshot();
185 return;
186 case 'y':
187 term_switch(cterm(), cterm(), 1, 0, 1);
188 return;
189 case CTRLKEY('l'):
190 locked = 1;
191 passlen = 0;
192 return;
193 default:
194 if (strchr(tags, c)) {
195 showtag(strchr(tags, c) - tags);
196 return;
198 if (mainterm())
199 term_send(ESC);
202 if (c != -1 && mainterm())
203 term_send(c);
206 static void temp_switch(int termid)
208 if (termid != cterm())
209 term_switch(cterm(), termid, 0, 0, 0);
212 static void switch_back(int termid)
214 if (termid != cterm())
215 term_switch(termid, cterm(), 1, 0, 0);
218 static int poll_all(void)
220 struct pollfd ufds[NTERMS + 1];
221 int term_idx[NTERMS + 1];
222 int i;
223 int n = 1;
224 ufds[0].fd = STDIN_FILENO;
225 ufds[0].events = POLLIN;
226 for (i = 0; i < NTERMS; i++) {
227 if (TERMOPEN(i)) {
228 ufds[n].fd = terms[i].fd;
229 ufds[n].events = POLLIN;
230 term_idx[n++] = i;
233 if (poll(ufds, n, 1000) < 1)
234 return 0;
235 if (ufds[0].revents & BADPOLLFLAGS)
236 return 1;
237 if (ufds[0].revents & POLLIN)
238 directkey();
239 for (i = 1; i < n; i++) {
240 temp_switch(term_idx[i]);
241 if (ufds[i].revents & POLLIN)
242 term_read();
243 if (ufds[i].revents & BADPOLLFLAGS) {
244 scr_free(&terms[term_idx[i]]);
245 term_end();
247 switch_back(term_idx[i]);
249 return 0;
252 static void mainloop(void)
254 struct termios oldtermios, termios;
255 tcgetattr(STDIN_FILENO, &termios);
256 oldtermios = termios;
257 cfmakeraw(&termios);
258 tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios);
259 term_load(&terms[cterm()], TERM_REDRAW);
260 while (!exitit)
261 if (poll_all())
262 break;
263 tcsetattr(STDIN_FILENO, 0, &oldtermios);
266 static void signalreceived(int n);
267 static void signalregister(void)
269 signal(SIGUSR1, signalreceived);
270 signal(SIGUSR2, signalreceived);
271 signal(SIGCHLD, signalreceived);
274 static void signalreceived(int n)
276 if (exitit)
277 return;
278 /* racy, new signals may arrive before re-registeration */
279 signalregister();
280 switch (n) {
281 case SIGUSR1:
282 hidden = 1;
283 term_switch(cterm(), cterm(), 0, 1, 0);
284 ioctl(STDIN_FILENO, VT_RELDISP, 1);
285 break;
286 case SIGUSR2:
287 hidden = 0;
288 fb_cmap();
289 term_switch(cterm(), cterm(), 1, 0, 1);
290 break;
291 case SIGCHLD:
292 while (waitpid(-1, NULL, WNOHANG) > 0)
294 break;
298 static void setupsignals(void)
300 struct vt_mode vtm;
301 vtm.mode = VT_PROCESS;
302 vtm.waitv = 0;
303 vtm.relsig = SIGUSR1;
304 vtm.acqsig = SIGUSR2;
305 vtm.frsig = 0;
306 signalregister();
307 ioctl(STDIN_FILENO, VT_SETMODE, &vtm);
310 int main(void)
312 char *hide = "\x1b[?25l";
313 char *clear = "\x1b[2J\x1b[H";
314 char *show = "\x1b[?25h";
315 write(STDOUT_FILENO, clear, strlen(clear));
316 write(STDIN_FILENO, hide, strlen(hide));
317 if (pad_init())
318 goto failed;
319 setupsignals();
320 fcntl(STDIN_FILENO, F_SETFL,
321 fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
322 mainloop();
323 pad_free();
324 failed:
325 write(STDIN_FILENO, show, strlen(show));
326 return 0;