fbpad: do not redraw the whole screen if it was already visible
[fbpad.git] / fbpad.c
blob81f1fc7640388595190c8b847d2312baea342e70
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 POLLFLAGS (POLLIN | 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; /* do not touch the framebuffer */
37 static int locked;
38 static char pass[1024];
39 static int passlen;
40 static int cmdmode; /* execute a command and exit */
41 static int histpos; /* scrolling history */
43 static int readchar(void)
45 char b;
46 if (read(0, &b, 1) > 0)
47 return (int) b;
48 return -1;
51 static int cterm(void)
53 return tops[ctag] * NTAGS + ctag;
56 static int altterm(int n)
58 return n < NTAGS ? n + NTAGS : n - NTAGS;
61 static int nextterm(void)
63 int n = (cterm() + 1) % NTERMS;
64 while (n != cterm()) {
65 if (TERMOPEN(n))
66 break;
67 n = (n + 1) % NTERMS;
69 return n;
72 static struct term *mainterm(void)
74 return TERMOPEN(cterm()) ? &terms[cterm()] : NULL;
77 static void histscrl(int pos)
79 if (pos != histpos)
80 term_hist(pos);
81 histpos = pos;
84 static void switchterm(int oidx, int nidx, int show, int save, int load)
86 if (load && histpos)
87 histscrl(0);
88 if (save && TERMOPEN(oidx) && TERMSNAP(oidx))
89 scr_snap(&terms[oidx]);
90 term_save(&terms[oidx]);
91 term_load(&terms[nidx], show);
92 if (show)
93 term_redraw(load && (!TERMOPEN(nidx) || !TERMSNAP(nidx) ||
94 scr_load(&terms[nidx])));
97 static void showterm(int n)
99 if (cterm() == n || cmdmode)
100 return;
101 if (ctag != n % NTAGS)
102 ltag = ctag;
103 switchterm(cterm(), n, !hidden, !hidden, !hidden);
104 ctag = n % NTAGS;
105 tops[ctag] = n / NTAGS;
108 static void showtag(int n)
110 showterm(tops[n] * NTAGS + n);
113 static void execterm(char **args)
115 if (!mainterm())
116 term_exec(args);
119 static void listtags(void)
121 /* colors for tags based on their number of terminals */
122 int colors[] = {252, FGCOLOR, FGCOLOR | FN_B};
123 int c = 0;
124 int r = pad_rows() - 1;
125 int i;
126 pad_put('T', r, c++, FGCOLOR, BGCOLOR);
127 pad_put('A', r, c++, FGCOLOR, BGCOLOR);
128 pad_put('G', r, c++, FGCOLOR, BGCOLOR);
129 pad_put('S', r, c++, FGCOLOR, BGCOLOR);
130 pad_put(':', r, c++, FGCOLOR, BGCOLOR);
131 pad_put(' ', r, c++, FGCOLOR, BGCOLOR);
132 for (i = 0; i < NTAGS; i++) {
133 int nt = 0;
134 if (TERMOPEN(i))
135 nt++;
136 if (TERMOPEN(altterm(i)))
137 nt++;
138 pad_put(i == ctag ? '(' : ' ', r, c++, FGCOLOR, BGCOLOR);
139 if (TERMSNAP(i))
140 pad_put(tags[i], r, c++, !nt ? BGCOLOR : colors[nt], colors[0]);
141 else
142 pad_put(tags[i], r, c++, colors[nt], BGCOLOR);
143 pad_put(i == ctag ? ')' : ' ', r, c++, FGCOLOR, BGCOLOR);
147 static void directkey(void)
149 char *shell[32] = SHELL;
150 char *mail[32] = MAIL;
151 char *editor[32] = EDITOR;
152 int c = readchar();
153 if (PASS && locked) {
154 if (c == '\r') {
155 pass[passlen] = '\0';
156 if (!strcmp(PASS, pass))
157 locked = 0;
158 passlen = 0;
159 return;
161 if (isprint(c) && passlen + 1 < sizeof(pass))
162 pass[passlen++] = c;
163 return;
165 if (c == ESC) {
166 switch ((c = readchar())) {
167 case 'c':
168 execterm(shell);
169 return;
170 case 'm':
171 execterm(mail);
172 return;
173 case 'e':
174 execterm(editor);
175 return;
176 case 'j':
177 case 'k':
178 showterm(altterm(cterm()));
179 return;
180 case 'o':
181 showtag(ltag);
182 return;
183 case 'p':
184 listtags();
185 return;
186 case '\t':
187 if (nextterm() != cterm())
188 showterm(nextterm());
189 return;
190 case CTRLKEY('q'):
191 exitit = 1;
192 return;
193 case 's':
194 term_screenshot();
195 return;
196 case 'y':
197 term_redraw(1);
198 return;
199 case CTRLKEY('l'):
200 locked = 1;
201 passlen = 0;
202 return;
203 case ',':
204 histscrl(MIN(NHIST, histpos + pad_rows() / 2));
205 return;
206 case '.':
207 histscrl(MAX(0, histpos - pad_rows() / 2));
208 return;
209 default:
210 if (strchr(tags, c)) {
211 showtag(strchr(tags, c) - tags);
212 return;
214 if (mainterm())
215 term_send(ESC);
218 histscrl(0);
219 if (c != -1 && mainterm())
220 term_send(c);
223 static void peepterm(int termid)
225 if (termid != cterm())
226 switchterm(cterm(), termid, 0, 0, 0);
229 static void peepback(int termid)
231 if (termid != cterm())
232 switchterm(termid, cterm(), !hidden, 0, 0);
235 static int pollterms(void)
237 struct pollfd ufds[NTERMS + 1];
238 int term_idx[NTERMS + 1];
239 int i;
240 int n = 1;
241 ufds[0].fd = 0;
242 ufds[0].events = POLLIN;
243 for (i = 0; i < NTERMS; i++) {
244 if (TERMOPEN(i)) {
245 ufds[n].fd = terms[i].fd;
246 ufds[n].events = POLLIN;
247 term_idx[n++] = i;
250 if (poll(ufds, n, 1000) < 1)
251 return 0;
252 if (ufds[0].revents & (POLLFLAGS & ~POLLIN))
253 return 1;
254 if (ufds[0].revents & POLLIN)
255 directkey();
256 for (i = 1; i < n; i++) {
257 if (!(ufds[i].revents & POLLFLAGS))
258 continue;
259 peepterm(term_idx[i]);
260 if (ufds[i].revents & POLLIN) {
261 term_read();
262 } else {
263 scr_free(&terms[term_idx[i]]);
264 term_end();
265 if (cmdmode)
266 exitit = 1;
268 peepback(term_idx[i]);
270 return 0;
273 static void mainloop(char **args)
275 struct termios oldtermios, termios;
276 tcgetattr(0, &termios);
277 oldtermios = termios;
278 cfmakeraw(&termios);
279 tcsetattr(0, TCSAFLUSH, &termios);
280 term_load(&terms[cterm()], 1);
281 term_redraw(1);
282 if (args) {
283 cmdmode = 1;
284 execterm(args);
286 while (!exitit)
287 if (pollterms())
288 break;
289 tcsetattr(0, 0, &oldtermios);
292 static void signalreceived(int n);
293 static void signalregister(void)
295 signal(SIGUSR1, signalreceived);
296 signal(SIGUSR2, signalreceived);
297 signal(SIGCHLD, signalreceived);
300 static void signalreceived(int n)
302 if (exitit)
303 return;
304 /* racy, new signals may arrive before re-registeration */
305 signalregister();
306 switch (n) {
307 case SIGUSR1:
308 hidden = 1;
309 switchterm(cterm(), cterm(), 0, 1, 0);
310 ioctl(0, VT_RELDISP, 1);
311 break;
312 case SIGUSR2:
313 hidden = 0;
314 fb_cmap();
315 switchterm(cterm(), cterm(), 1, 0, 1);
316 break;
317 case SIGCHLD:
318 while (waitpid(-1, NULL, WNOHANG) > 0)
320 break;
324 static void signalsetup(void)
326 struct vt_mode vtm;
327 vtm.mode = VT_PROCESS;
328 vtm.waitv = 0;
329 vtm.relsig = SIGUSR1;
330 vtm.acqsig = SIGUSR2;
331 vtm.frsig = 0;
332 signalregister();
333 ioctl(0, VT_SETMODE, &vtm);
336 int main(int argc, char **argv)
338 char *hide = "\x1b[?25l";
339 char *clear = "\x1b[2J\x1b[H";
340 char *show = "\x1b[?25h";
341 char **args = argv + 1;
342 write(1, clear, strlen(clear));
343 write(1, hide, strlen(hide));
344 if (pad_init())
345 goto failed;
346 signalsetup();
347 fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
348 while (args[0] && args[0][0] == '-')
349 args++;
350 mainloop(args[0] ? args : NULL);
351 pad_free();
352 failed:
353 write(1, show, strlen(show));
354 return 0;