fbpad: pass FBDEV environment variable to fb_init()
[fbpad.git] / fbpad.c
blob8769109dd235ca061b2de9b79987380a5c18e2e3
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 <stdlib.h>
24 #include <signal.h>
25 #include <string.h>
26 #include <sys/ioctl.h>
27 #include <sys/wait.h>
28 #include <termios.h>
29 #include <unistd.h>
30 #include <linux/vt.h>
31 #include "conf.h"
32 #include "fbpad.h"
33 #include "draw.h"
35 #define CTRLKEY(x) ((x) - 96)
36 #define POLLFLAGS (POLLIN | POLLHUP | POLLERR | POLLNVAL)
37 #define NTAGS (sizeof(tags) - 1)
38 #define NTERMS (NTAGS * 2)
39 #define TERMOPEN(i) (terms[i].fd)
40 #define TERMSNAP(i) (strchr(TAGS_SAVED, tags[(i) % NTAGS]))
42 static char tags[] = TAGS;
43 static struct term terms[NTERMS];
44 static int tops[NTAGS]; /* top terms of tags */
45 static int split[NTAGS]; /* terms are shown together */
46 static int ctag; /* current tag */
47 static int ltag; /* the last tag */
48 static int exitit;
49 static int hidden; /* do not touch the framebuffer */
50 static int locked;
51 static int taglock; /* disable tag switching */
52 static char pass[1024];
53 static int passlen;
54 static int cmdmode; /* execute a command and exit */
56 static int readchar(void)
58 char b;
59 if (read(0, &b, 1) > 0)
60 return (unsigned char) b;
61 return -1;
64 /* the current terminal */
65 static int cterm(void)
67 return tops[ctag] * NTAGS + ctag;
70 /* tag's active terminal */
71 static int tterm(int n)
73 return tops[n] * NTAGS + n;
76 /* the other terminal in the same tag */
77 static int aterm(int n)
79 return n < NTAGS ? n + NTAGS : n - NTAGS;
82 /* the next terminal */
83 static int nterm(void)
85 int n = (cterm() + 1) % NTERMS;
86 while (n != cterm()) {
87 if (TERMOPEN(n))
88 break;
89 n = (n + 1) % NTERMS;
91 return n;
94 /* term struct of cterm() */
95 static struct term *tmain(void)
97 return TERMOPEN(cterm()) ? &terms[cterm()] : NULL;
100 #define BRWID 2
101 #define BRCLR 0xff0000
103 static void t_conf(int idx)
105 int h1 = fb_rows() / 2 / pad_crows() * pad_crows();
106 int h2 = fb_rows() - h1 - 4 * BRWID;
107 int w1 = fb_cols() / 2 / pad_ccols() * pad_ccols();
108 int w2 = fb_cols() - w1 - 4 * BRWID;
109 int tag = idx % NTAGS;
110 int top = idx < NTAGS;
111 if (split[tag] == 0)
112 pad_conf(0, 0, fb_rows(), fb_cols());
113 if (split[tag] == 1)
114 pad_conf(top ? BRWID : h1 + 3 * BRWID, BRWID,
115 top ? h1 : h2, fb_cols() - 2 * BRWID);
116 if (split[tag] == 2)
117 pad_conf(BRWID, top ? BRWID : w1 + 3 * BRWID,
118 fb_rows() - 2 * BRWID, top ? w1 : w2);
121 static void t_hide(int idx, int save)
123 if (save && TERMOPEN(idx) && TERMSNAP(idx))
124 scr_snap(idx);
125 term_save(&terms[idx]);
128 /* show=0 (hidden), show=1 (visible), show=2 (load), show=3 (redraw) */
129 static int t_show(int idx, int show)
131 t_conf(idx);
132 term_load(&terms[idx], show > 0);
133 if (show == 2) /* redraw if scr_load() fails */
134 show += !TERMOPEN(idx) || !TERMSNAP(idx) || scr_load(idx);
135 if (show > 0)
136 term_redraw(show == 3);
137 return show;
140 /* switch active terminal; hide oidx and show nidx */
141 static int t_hideshow(int oidx, int save, int nidx, int show)
143 int otag = oidx % NTAGS;
144 int ntag = nidx % NTAGS;
145 int ret;
146 t_hide(oidx, save);
147 if (show && split[otag] && otag == ntag)
148 pad_border(0, BRWID);
149 ret = t_show(nidx, show);
150 if (show && split[ntag])
151 pad_border(BRCLR, BRWID);
152 return ret;
155 /* set cterm() */
156 static void t_set(int n)
158 if (cterm() == n || cmdmode)
159 return;
160 if (taglock && ctag != n % NTAGS)
161 return;
162 if (ctag != n % NTAGS)
163 ltag = ctag;
164 if (ctag == n % NTAGS) {
165 if (split[n % NTAGS])
166 t_hideshow(cterm(), 0, n, 1);
167 else
168 t_hideshow(cterm(), 1, n, 2);
169 } else {
170 int draw = t_hideshow(cterm(), 1, n, 2);
171 if (split[n % NTAGS]) {
172 t_hideshow(n, 0, aterm(n), draw == 2 ? 1 : 2);
173 t_hideshow(aterm(n), 0, n, 1);
176 ctag = n % NTAGS;
177 tops[ctag] = n / NTAGS;
180 static void t_split(int n)
182 split[ctag] = n;
183 t_hideshow(cterm(), 0, aterm(cterm()), 3);
184 t_hideshow(aterm(cterm()), 1, cterm(), 3);
187 static void t_exec(char **args)
189 if (!tmain())
190 term_exec(args);
193 static void listtags(void)
195 /* colors for tags based on their number of terminals */
196 int fg = 0x96cb5c, bg = 0x516f7b;
197 int colors[] = {0x173f4f, fg, 0x68cbc0 | FN_B};
198 int c = 0;
199 int r = pad_rows() - 1;
200 int i;
201 pad_put('T', r, c++, fg | FN_B, bg);
202 pad_put('A', r, c++, fg | FN_B, bg);
203 pad_put('G', r, c++, fg | FN_B, bg);
204 pad_put('S', r, c++, fg | FN_B, bg);
205 pad_put(':', r, c++, fg | FN_B, bg);
206 pad_put(' ', r, c++, fg | FN_B, bg);
207 for (i = 0; i < NTAGS && c + 2 < pad_cols(); i++) {
208 int nt = 0;
209 if (TERMOPEN(i))
210 nt++;
211 if (TERMOPEN(aterm(i)))
212 nt++;
213 pad_put(i == ctag ? '(' : ' ', r, c++, fg, bg);
214 if (TERMSNAP(i))
215 pad_put(tags[i], r, c++, !nt ? bg : colors[nt], colors[0]);
216 else
217 pad_put(tags[i], r, c++, colors[nt], bg);
218 pad_put(i == ctag ? ')' : ' ', r, c++, fg, bg);
220 for (; c < pad_cols(); c++)
221 pad_put(' ', r, c, fg, bg);
224 static void directkey(void)
226 char *shell[32] = SHELL;
227 char *mail[32] = MAIL;
228 char *editor[32] = EDITOR;
229 int c = readchar();
230 if (PASS && locked) {
231 if (c == '\r') {
232 pass[passlen] = '\0';
233 if (!strcmp(PASS, pass))
234 locked = 0;
235 passlen = 0;
236 return;
238 if (isprint(c) && passlen + 1 < sizeof(pass))
239 pass[passlen++] = c;
240 return;
242 if (c == ESC) {
243 switch ((c = readchar())) {
244 case 'c':
245 t_exec(shell);
246 return;
247 case 'm':
248 t_exec(mail);
249 return;
250 case 'e':
251 t_exec(editor);
252 return;
253 case 'j':
254 case 'k':
255 t_set(aterm(cterm()));
256 return;
257 case 'o':
258 t_set(tterm(ltag));
259 return;
260 case 'p':
261 listtags();
262 return;
263 case '\t':
264 if (nterm() != cterm())
265 t_set(nterm());
266 return;
267 case CTRLKEY('q'):
268 exitit = 1;
269 return;
270 case 's':
271 term_screenshot();
272 return;
273 case 'y':
274 term_redraw(1);
275 return;
276 case CTRLKEY('l'):
277 locked = 1;
278 passlen = 0;
279 return;
280 case CTRLKEY('o'):
281 taglock = 1 - taglock;
282 return;
283 case ',':
284 term_scrl(pad_rows() / 2);
285 return;
286 case '.':
287 term_scrl(-pad_rows() / 2);
288 return;
289 case '=':
290 t_split(split[ctag] == 1 ? 2 : 1);
291 return;
292 case '-':
293 t_split(0);
294 return;
295 default:
296 if (strchr(tags, c)) {
297 t_set(tterm(strchr(tags, c) - tags));
298 return;
300 if (tmain())
301 term_send(ESC);
304 if (c != -1 && tmain())
305 term_send(c);
308 static void peepterm(int termid)
310 int visible = !hidden && ctag == (termid % NTAGS) && split[ctag];
311 if (termid != cterm())
312 t_hideshow(cterm(), 0, termid, visible);
315 static void peepback(int termid)
317 if (termid != cterm())
318 t_hideshow(termid, 0, cterm(), !hidden);
321 static int pollterms(void)
323 struct pollfd ufds[NTERMS + 1];
324 int term_idx[NTERMS + 1];
325 int i;
326 int n = 1;
327 ufds[0].fd = 0;
328 ufds[0].events = POLLIN;
329 for (i = 0; i < NTERMS; i++) {
330 if (TERMOPEN(i)) {
331 ufds[n].fd = terms[i].fd;
332 ufds[n].events = POLLIN;
333 term_idx[n++] = i;
336 if (poll(ufds, n, 1000) < 1)
337 return 0;
338 if (ufds[0].revents & (POLLFLAGS & ~POLLIN))
339 return 1;
340 if (ufds[0].revents & POLLIN)
341 directkey();
342 for (i = 1; i < n; i++) {
343 if (!(ufds[i].revents & POLLFLAGS))
344 continue;
345 peepterm(term_idx[i]);
346 if (ufds[i].revents & POLLIN) {
347 term_read();
348 } else {
349 scr_free(term_idx[i]);
350 term_end();
351 if (cmdmode)
352 exitit = 1;
354 peepback(term_idx[i]);
356 return 0;
359 static void mainloop(char **args)
361 struct termios oldtermios, termios;
362 tcgetattr(0, &termios);
363 oldtermios = termios;
364 cfmakeraw(&termios);
365 tcsetattr(0, TCSAFLUSH, &termios);
366 term_load(&terms[cterm()], 1);
367 term_redraw(1);
368 if (args) {
369 cmdmode = 1;
370 t_exec(args);
372 while (!exitit)
373 if (pollterms())
374 break;
375 tcsetattr(0, 0, &oldtermios);
378 static void signalreceived(int n)
380 if (exitit)
381 return;
382 switch (n) {
383 case SIGUSR1:
384 hidden = 1;
385 t_hide(cterm(), 1);
386 ioctl(0, VT_RELDISP, 1);
387 break;
388 case SIGUSR2:
389 hidden = 0;
390 fb_cmap();
391 if (t_show(cterm(), 2) == 3 && split[ctag]) {
392 t_hideshow(cterm(), 0, aterm(cterm()), 3);
393 t_hideshow(aterm(cterm()), 0, cterm(), 1);
395 break;
396 case SIGCHLD:
397 while (waitpid(-1, NULL, WNOHANG) > 0)
399 break;
403 static void signalsetup(void)
405 struct vt_mode vtm;
406 vtm.mode = VT_PROCESS;
407 vtm.waitv = 0;
408 vtm.relsig = SIGUSR1;
409 vtm.acqsig = SIGUSR2;
410 vtm.frsig = 0;
411 signal(SIGUSR1, signalreceived);
412 signal(SIGUSR2, signalreceived);
413 signal(SIGCHLD, signalreceived);
414 ioctl(0, VT_SETMODE, &vtm);
417 int main(int argc, char **argv)
419 char *hide = "\x1b[2J\x1b[H\x1b[?25l";
420 char *show = "\x1b[?25h";
421 char **args = argv + 1;
422 if (fb_init(getenv("FBDEV"))) {
423 fprintf(stderr, "fbpad: failed to initialize the framebuffer\n");
424 return 1;
426 if (pad_init()) {
427 fprintf(stderr, "fbpad: cannot find fonts\n");
428 return 1;
430 write(1, hide, strlen(hide));
431 signalsetup();
432 fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
433 while (args[0] && args[0][0] == '-')
434 args++;
435 mainloop(args[0] ? args : NULL);
436 write(1, show, strlen(show));
437 pad_free();
438 scr_done();
439 fb_free();
440 return 0;