fbpad: do not use STD*_FILENO macros for standard unix fds
[fbpad.git] / fbpad.c
blob49f97d8aa95e2f5330e1fac51bb0c7707b03aa13
1 /*
2 * fbpad - a small framebuffer virtual terminal
4 * Copyright (C) 2009-2013 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 "fbpad.h"
21 #include "draw.h"
23 #define CTRLKEY(x) ((x) - 96)
24 #define BADPOLLFLAGS (POLLHUP | POLLERR | POLLNVAL)
25 #define NTAGS (sizeof(tags) - 1)
26 #define NTERMS (NTAGS * 2)
27 #define TERMOPEN(i) (terms[i].fd)
28 #define TERMSNAP(i) (strchr(TAGS_SAVED, tags[(i) % NTAGS]))
30 static char tags[] = TAGS;
31 static struct term terms[NTERMS];
32 static int tops[NTAGS]; /* top terms of tags */
33 static int ctag; /* current tag */
34 static int ltag; /* the last tag */
35 static int exitit;
36 static int hidden;
37 static int locked;
38 static char pass[1024];
39 static int passlen;
41 static int readchar(void)
43 char b;
44 if (read(0, &b, 1) > 0)
45 return (int) b;
46 return -1;
49 static int cterm(void)
51 return tops[ctag] * NTAGS + ctag;
54 static void term_switch(int oidx, int nidx, int show, int save, int load)
56 int flags = show ? (load ? TERM_REDRAW : TERM_VISIBLE) : TERM_HIDDEN;
57 if (save && TERMOPEN(oidx) && TERMSNAP(oidx))
58 scr_snap(&terms[oidx]);
59 term_save(&terms[oidx]);
60 if (show && load && TERMOPEN(nidx) && TERMSNAP(nidx))
61 flags = scr_load(&terms[nidx]) ? TERM_REDRAW : TERM_VISIBLE;
62 term_load(&terms[nidx], flags);
65 static void showterm(int n)
67 if (cterm() == n)
68 return;
69 if (ctag != n % NTAGS)
70 ltag = ctag;
71 term_switch(cterm(), n, !hidden, !hidden, !hidden);
72 ctag = n % NTAGS;
73 tops[ctag] = n / NTAGS;
76 static void showtag(int n)
78 showterm(tops[n] * NTAGS + n);
81 static struct term *mainterm(void)
83 if (TERMOPEN(cterm()))
84 return &terms[cterm()];
85 return NULL;
88 static void exec_cmd(char *file)
90 if (!mainterm())
91 term_exec(file);
94 static int altterm(int n)
96 return n < NTAGS ? n + NTAGS : n - NTAGS;
99 static void nextterm(void)
101 int n = (cterm() + 1) % NTERMS;
102 while (n != cterm()) {
103 if (TERMOPEN(n)) {
104 showterm(n);
105 break;
107 n = (n + 1) % NTERMS;
111 static void showtags(void)
113 int colors[] = {15, 4, 2};
114 int c = 0;
115 int r = pad_rows() - 1;
116 int i;
117 pad_put('T', r, c++, FGCOLOR, BGCOLOR);
118 pad_put('A', r, c++, FGCOLOR, BGCOLOR);
119 pad_put('G', r, c++, FGCOLOR, BGCOLOR);
120 pad_put('S', r, c++, FGCOLOR, BGCOLOR);
121 pad_put(':', r, c++, FGCOLOR, BGCOLOR);
122 pad_put(' ', r, c++, FGCOLOR, BGCOLOR);
123 for (i = 0; i < NTAGS; i++) {
124 int nt = 0;
125 if (TERMOPEN(i))
126 nt++;
127 if (TERMOPEN(altterm(i)))
128 nt++;
129 pad_put(i == ctag ? '(' : ' ', r, c++, FGCOLOR, BGCOLOR);
130 if (TERMSNAP(i))
131 pad_put(tags[i], r, c++, !nt ? BGCOLOR : colors[nt], 15);
132 else
133 pad_put(tags[i], r, c++, colors[nt], BGCOLOR);
134 pad_put(i == ctag ? ')' : ' ', r, c++, FGCOLOR, BGCOLOR);
138 static void directkey(void)
140 int c = readchar();
141 if (PASS && locked) {
142 if (c == '\r') {
143 pass[passlen] = '\0';
144 if (!strcmp(PASS, pass))
145 locked = 0;
146 passlen = 0;
147 return;
149 if (isprint(c) && passlen + 1 < sizeof(pass))
150 pass[passlen++] = c;
151 return;
153 if (c == ESC) {
154 switch ((c = readchar())) {
155 case 'c':
156 exec_cmd(SHELL);
157 return;
158 case 'm':
159 exec_cmd(MAIL);
160 return;
161 case 'e':
162 exec_cmd(EDITOR);
163 return;
164 case 'j':
165 case 'k':
166 showterm(altterm(cterm()));
167 return;
168 case 'o':
169 showtag(ltag);
170 return;
171 case 'p':
172 showtags();
173 return;
174 case '\t':
175 nextterm();
176 return;
177 case CTRLKEY('q'):
178 exitit = 1;
179 return;
180 case 's':
181 term_screenshot();
182 return;
183 case 'y':
184 term_switch(cterm(), cterm(), 1, 0, 1);
185 return;
186 case CTRLKEY('l'):
187 locked = 1;
188 passlen = 0;
189 return;
190 default:
191 if (strchr(tags, c)) {
192 showtag(strchr(tags, c) - tags);
193 return;
195 if (mainterm())
196 term_send(ESC);
199 if (c != -1 && mainterm())
200 term_send(c);
203 static void temp_switch(int termid)
205 if (termid != cterm())
206 term_switch(cterm(), termid, 0, 0, 0);
209 static void switch_back(int termid)
211 if (termid != cterm())
212 term_switch(termid, cterm(), 1, 0, 0);
215 static int poll_all(void)
217 struct pollfd ufds[NTERMS + 1];
218 int term_idx[NTERMS + 1];
219 int i;
220 int n = 1;
221 ufds[0].fd = 0;
222 ufds[0].events = POLLIN;
223 for (i = 0; i < NTERMS; i++) {
224 if (TERMOPEN(i)) {
225 ufds[n].fd = terms[i].fd;
226 ufds[n].events = POLLIN;
227 term_idx[n++] = i;
230 if (poll(ufds, n, 1000) < 1)
231 return 0;
232 if (ufds[0].revents & BADPOLLFLAGS)
233 return 1;
234 if (ufds[0].revents & POLLIN)
235 directkey();
236 for (i = 1; i < n; i++) {
237 temp_switch(term_idx[i]);
238 if (ufds[i].revents & POLLIN)
239 term_read();
240 if (ufds[i].revents & BADPOLLFLAGS) {
241 scr_free(&terms[term_idx[i]]);
242 term_end();
244 switch_back(term_idx[i]);
246 return 0;
249 static void mainloop(void)
251 struct termios oldtermios, termios;
252 tcgetattr(0, &termios);
253 oldtermios = termios;
254 cfmakeraw(&termios);
255 tcsetattr(0, TCSAFLUSH, &termios);
256 term_load(&terms[cterm()], TERM_REDRAW);
257 while (!exitit)
258 if (poll_all())
259 break;
260 tcsetattr(0, 0, &oldtermios);
263 static void signalreceived(int n);
264 static void signalregister(void)
266 signal(SIGUSR1, signalreceived);
267 signal(SIGUSR2, signalreceived);
268 signal(SIGCHLD, signalreceived);
271 static void signalreceived(int n)
273 if (exitit)
274 return;
275 /* racy, new signals may arrive before re-registeration */
276 signalregister();
277 switch (n) {
278 case SIGUSR1:
279 hidden = 1;
280 term_switch(cterm(), cterm(), 0, 1, 0);
281 ioctl(0, VT_RELDISP, 1);
282 break;
283 case SIGUSR2:
284 hidden = 0;
285 fb_cmap();
286 term_switch(cterm(), cterm(), 1, 0, 1);
287 break;
288 case SIGCHLD:
289 while (waitpid(-1, NULL, WNOHANG) > 0)
291 break;
295 static void setupsignals(void)
297 struct vt_mode vtm;
298 vtm.mode = VT_PROCESS;
299 vtm.waitv = 0;
300 vtm.relsig = SIGUSR1;
301 vtm.acqsig = SIGUSR2;
302 vtm.frsig = 0;
303 signalregister();
304 ioctl(0, VT_SETMODE, &vtm);
307 int main(void)
309 char *hide = "\x1b[?25l";
310 char *clear = "\x1b[2J\x1b[H";
311 char *show = "\x1b[?25h";
312 write(1, clear, strlen(clear));
313 write(1, hide, strlen(hide));
314 if (pad_init())
315 goto failed;
316 setupsignals();
317 fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
318 mainloop();
319 pad_free();
320 failed:
321 write(1, show, strlen(show));
322 return 0;