pad: call fb_val() for border color in pad_border()
[fbpad.git] / fbpad.c
blob4f79d4259b745db8b6f45b17a34a88b22be35721
1 /*
2 * FBPAD FRAMEBUFFER VIRTUAL TERMINAL
4 * Copyright (C) 2009-2021 Ali Gholami Rudi <ali at rudi dot ir>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <ctype.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <poll.h>
22 #include <stdio.h>
23 #include <signal.h>
24 #include <string.h>
25 #include <sys/ioctl.h>
26 #include <sys/wait.h>
27 #include <termios.h>
28 #include <unistd.h>
29 #include <linux/vt.h>
30 #include "conf.h"
31 #include "fbpad.h"
32 #include "draw.h"
34 #define CTRLKEY(x) ((x) - 96)
35 #define POLLFLAGS (POLLIN | POLLHUP | POLLERR | POLLNVAL)
36 #define NTAGS (sizeof(tags) - 1)
37 #define NTERMS (NTAGS * 2)
38 #define TERMOPEN(i) (terms[i].fd)
39 #define TERMSNAP(i) (strchr(TAGS_SAVED, tags[(i) % NTAGS]))
41 static char tags[] = TAGS;
42 static struct term terms[NTERMS];
43 static int tops[NTAGS]; /* top terms of tags */
44 static int split[NTAGS]; /* terms are shown together */
45 static int ctag; /* current tag */
46 static int ltag; /* the last tag */
47 static int exitit;
48 static int hidden; /* do not touch the framebuffer */
49 static int locked;
50 static int taglock; /* disable tag switching */
51 static char pass[1024];
52 static int passlen;
53 static int cmdmode; /* execute a command and exit */
55 static int readchar(void)
57 char b;
58 if (read(0, &b, 1) > 0)
59 return (unsigned char) b;
60 return -1;
63 /* the current terminal */
64 static int cterm(void)
66 return tops[ctag] * NTAGS + ctag;
69 /* the other terminal in the same tag */
70 static int aterm(int n)
72 return n < NTAGS ? n + NTAGS : n - NTAGS;
75 /* the next terminal */
76 static int nterm(void)
78 int n = (cterm() + 1) % NTERMS;
79 while (n != cterm()) {
80 if (TERMOPEN(n))
81 break;
82 n = (n + 1) % NTERMS;
84 return n;
87 static struct term *fterm_main(void)
89 return TERMOPEN(cterm()) ? &terms[cterm()] : NULL;
92 static void fterm_conf(int idx)
94 int hrows = (fb_rows() - 4) / 2;
95 int hcols = (fb_cols() - 4) / 2;
96 int tag = idx % NTAGS;
97 int top = idx < NTAGS;
98 if (split[tag] == 0)
99 pad_conf(0, 0, fb_rows(), fb_cols());
100 if (split[tag] == 1)
101 pad_conf(top ? 1 : hrows + 3, 1, hrows, fb_cols() - 2);
102 if (split[tag] == 2)
103 pad_conf(1, top ? 1 : hcols + 3, fb_rows() - 2, hcols);
106 static void fterm_switch(int oidx, int nidx, int show, int save, int load)
108 int otag = oidx % NTAGS;
109 int ntag = nidx % NTAGS;
110 int bothvisible = otag == ntag && split[otag];
111 if (save && TERMOPEN(oidx) && TERMSNAP(oidx) && !bothvisible)
112 scr_snap(split[otag] ? otag : oidx);
113 term_save(&terms[oidx]);
114 if (show && split[otag] && otag == ntag)
115 pad_border(0);
116 fterm_conf(nidx);
117 term_load(&terms[nidx], show);
118 if (show)
119 term_redraw(load && (load < 0 || !TERMOPEN(nidx) || !TERMSNAP(nidx) ||
120 (!bothvisible && scr_load(split[ntag] ? ntag : nidx))));
121 if (show && split[ntag])
122 pad_border(0xff0000);
125 static void fterm_show(int n)
127 if (cterm() == n || cmdmode)
128 return;
129 if (taglock && ctag != n % NTAGS)
130 return;
131 if (ctag != n % NTAGS)
132 ltag = ctag;
133 if (ctag == n % NTAGS) {
134 if (split[n % NTAGS])
135 fterm_switch(cterm(), n, !hidden, 0, 0);
136 else
137 fterm_switch(cterm(), n, !hidden, !hidden, !hidden);
138 } else {
139 fterm_switch(cterm(), n, !hidden, !hidden, !hidden);
140 if (split[n % NTAGS]) {
141 fterm_switch(n, aterm(n), !hidden, 0, !hidden);
142 fterm_switch(aterm(n), n, !hidden, 0, 0);
145 ctag = n % NTAGS;
146 tops[ctag] = n / NTAGS;
149 static void tag_split(int n)
151 split[ctag] = n;
152 fterm_switch(cterm(), aterm(cterm()), !hidden, 0, -!hidden);
153 fterm_switch(aterm(cterm()), cterm(), !hidden, !hidden, -!hidden);
156 static void tag_show(int n)
158 fterm_show(tops[n] * NTAGS + n);
161 static void fterm_exec(char **args)
163 if (!fterm_main())
164 term_exec(args);
167 static void tag_list(void)
169 /* colors for tags based on their number of terminals */
170 int colors[] = {COLOR7, FGCOLOR, FGCOLOR | FN_B};
171 int c = 0;
172 int r = pad_rows() - 1;
173 int i;
174 pad_put('T', r, c++, FGCOLOR, BGCOLOR);
175 pad_put('A', r, c++, FGCOLOR, BGCOLOR);
176 pad_put('G', r, c++, FGCOLOR, BGCOLOR);
177 pad_put('S', r, c++, FGCOLOR, BGCOLOR);
178 pad_put(':', r, c++, FGCOLOR, BGCOLOR);
179 pad_put(' ', r, c++, FGCOLOR, BGCOLOR);
180 for (i = 0; i < NTAGS && c + 2 < pad_cols(); i++) {
181 int nt = 0;
182 if (TERMOPEN(i))
183 nt++;
184 if (TERMOPEN(aterm(i)))
185 nt++;
186 pad_put(i == ctag ? '(' : ' ', r, c++, FGCOLOR, BGCOLOR);
187 if (TERMSNAP(i))
188 pad_put(tags[i], r, c++, !nt ? BGCOLOR : colors[nt], colors[0]);
189 else
190 pad_put(tags[i], r, c++, colors[nt], BGCOLOR);
191 pad_put(i == ctag ? ')' : ' ', r, c++, FGCOLOR, BGCOLOR);
195 static void directkey(void)
197 char *shell[32] = SHELL;
198 char *mail[32] = MAIL;
199 char *editor[32] = EDITOR;
200 int c = readchar();
201 if (PASS && locked) {
202 if (c == '\r') {
203 pass[passlen] = '\0';
204 if (!strcmp(PASS, pass))
205 locked = 0;
206 passlen = 0;
207 return;
209 if (isprint(c) && passlen + 1 < sizeof(pass))
210 pass[passlen++] = c;
211 return;
213 if (c == ESC) {
214 switch ((c = readchar())) {
215 case 'c':
216 fterm_exec(shell);
217 return;
218 case 'm':
219 fterm_exec(mail);
220 return;
221 case 'e':
222 fterm_exec(editor);
223 return;
224 case 'j':
225 case 'k':
226 fterm_show(aterm(cterm()));
227 return;
228 case 'o':
229 tag_show(ltag);
230 return;
231 case 'p':
232 tag_list();
233 return;
234 case '\t':
235 if (nterm() != cterm())
236 fterm_show(nterm());
237 return;
238 case CTRLKEY('q'):
239 exitit = 1;
240 return;
241 case 's':
242 term_screenshot();
243 return;
244 case 'y':
245 term_redraw(1);
246 return;
247 case CTRLKEY('l'):
248 locked = 1;
249 passlen = 0;
250 return;
251 case CTRLKEY('o'):
252 taglock = 1 - taglock;
253 return;
254 case ',':
255 term_scrl(pad_rows() / 2);
256 return;
257 case '.':
258 term_scrl(-pad_rows() / 2);
259 return;
260 case '=':
261 tag_split(split[ctag] == 1 ? 2 : 1);
262 return;
263 case '-':
264 tag_split(0);
265 return;
266 default:
267 if (strchr(tags, c)) {
268 tag_show(strchr(tags, c) - tags);
269 return;
271 if (fterm_main())
272 term_send(ESC);
275 if (c != -1 && fterm_main())
276 term_send(c);
279 static void peepterm(int termid)
281 int visible = !hidden && ctag == (termid % NTAGS) && split[ctag];
282 if (termid != cterm())
283 fterm_switch(cterm(), termid, visible, 0, 0);
286 static void peepback(int termid)
288 if (termid != cterm())
289 fterm_switch(termid, cterm(), !hidden, 0, 0);
292 static int pollterms(void)
294 struct pollfd ufds[NTERMS + 1];
295 int term_idx[NTERMS + 1];
296 int i;
297 int n = 1;
298 ufds[0].fd = 0;
299 ufds[0].events = POLLIN;
300 for (i = 0; i < NTERMS; i++) {
301 if (TERMOPEN(i)) {
302 ufds[n].fd = terms[i].fd;
303 ufds[n].events = POLLIN;
304 term_idx[n++] = i;
307 if (poll(ufds, n, 1000) < 1)
308 return 0;
309 if (ufds[0].revents & (POLLFLAGS & ~POLLIN))
310 return 1;
311 if (ufds[0].revents & POLLIN)
312 directkey();
313 for (i = 1; i < n; i++) {
314 if (!(ufds[i].revents & POLLFLAGS))
315 continue;
316 peepterm(term_idx[i]);
317 if (ufds[i].revents & POLLIN) {
318 term_read();
319 } else {
320 scr_free(term_idx[i]);
321 term_end();
322 if (cmdmode)
323 exitit = 1;
325 peepback(term_idx[i]);
327 return 0;
330 static void mainloop(char **args)
332 struct termios oldtermios, termios;
333 tcgetattr(0, &termios);
334 oldtermios = termios;
335 cfmakeraw(&termios);
336 tcsetattr(0, TCSAFLUSH, &termios);
337 term_load(&terms[cterm()], 1);
338 term_redraw(1);
339 if (args) {
340 cmdmode = 1;
341 fterm_exec(args);
343 while (!exitit)
344 if (pollterms())
345 break;
346 tcsetattr(0, 0, &oldtermios);
349 static void signalreceived(int n)
351 if (exitit)
352 return;
353 switch (n) {
354 case SIGUSR1:
355 hidden = 1;
356 fterm_switch(cterm(), cterm(), 0, 1, 0);
357 ioctl(0, VT_RELDISP, 1);
358 break;
359 case SIGUSR2:
360 hidden = 0;
361 fb_cmap();
362 fterm_switch(cterm(), cterm(), 1, 0, 1);
363 break;
364 case SIGCHLD:
365 while (waitpid(-1, NULL, WNOHANG) > 0)
367 break;
371 static void signalsetup(void)
373 struct vt_mode vtm;
374 vtm.mode = VT_PROCESS;
375 vtm.waitv = 0;
376 vtm.relsig = SIGUSR1;
377 vtm.acqsig = SIGUSR2;
378 vtm.frsig = 0;
379 signal(SIGUSR1, signalreceived);
380 signal(SIGUSR2, signalreceived);
381 signal(SIGCHLD, signalreceived);
382 ioctl(0, VT_SETMODE, &vtm);
385 int main(int argc, char **argv)
387 char *hide = "\x1b[2J\x1b[H\x1b[?25l";
388 char *show = "\x1b[?25h";
389 char **args = argv + 1;
390 if (fb_init(FBDEV)) {
391 fprintf(stderr, "fbpad: failed to initialize the framebuffer\n");
392 return 1;
394 if (pad_init()) {
395 fprintf(stderr, "fbpad: cannot find fonts\n");
396 return 1;
398 write(1, hide, strlen(hide));
399 signalsetup();
400 fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
401 while (args[0] && args[0][0] == '-')
402 args++;
403 mainloop(args[0] ? args : NULL);
404 write(1, show, strlen(show));
405 pad_free();
406 scr_done();
407 fb_free();
408 return 0;