term: enable switching signals using the second argument of term_exec()
[fbpad.git] / fbpad.c
blob319cb2c52ec217ecc6a7ec468773a02ba61c80cb
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) (term_fd(terms[i]))
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))
124 term_hide(terms[idx]);
125 if (save && TERMOPEN(idx) && TERMSNAP(idx))
126 scr_snap(idx);
127 term_save(terms[idx]);
130 /* show=0 (hidden), show=1 (visible), show=2 (load), show=3 (redraw) */
131 static int t_show(int idx, int show)
133 t_conf(idx);
134 term_load(terms[idx], show > 0);
135 if (show == 2) /* redraw if scr_load() fails */
136 show += !TERMOPEN(idx) || !TERMSNAP(idx) || scr_load(idx);
137 if (show > 0)
138 term_redraw(show == 3);
139 if ((show == 2 || show == 3) && TERMOPEN(idx))
140 term_show(terms[idx]);
141 return show;
144 /* switch active terminal; hide oidx and show nidx */
145 static int t_hideshow(int oidx, int save, int nidx, int show)
147 int otag = oidx % NTAGS;
148 int ntag = nidx % NTAGS;
149 int ret;
150 t_hide(oidx, save);
151 if (show && split[otag] && otag == ntag)
152 pad_border(0, BRWID);
153 ret = t_show(nidx, show);
154 if (show && split[ntag])
155 pad_border(BRCLR, BRWID);
156 return ret;
159 /* set cterm() */
160 static void t_set(int n)
162 if (cterm() == n || cmdmode)
163 return;
164 if (taglock && ctag != n % NTAGS)
165 return;
166 if (ctag != n % NTAGS)
167 ltag = ctag;
168 if (ctag == n % NTAGS) {
169 if (split[n % NTAGS])
170 t_hideshow(cterm(), 0, n, 1);
171 else
172 t_hideshow(cterm(), 1, n, 2);
173 } else {
174 int draw = t_hideshow(cterm(), 1, n, 2);
175 if (split[n % NTAGS]) {
176 t_hideshow(n, 0, aterm(n), draw == 2 ? 1 : 2);
177 t_hideshow(aterm(n), 0, n, 1);
180 ctag = n % NTAGS;
181 tops[ctag] = n / NTAGS;
184 static void t_split(int n)
186 split[ctag] = n;
187 t_hideshow(cterm(), 0, aterm(cterm()), 3);
188 t_hideshow(aterm(cterm()), 1, cterm(), 3);
191 static void t_exec(char **args, int swsig)
193 if (!tmain())
194 term_exec(args, swsig);
197 static void listtags(void)
199 /* colors for tags based on their number of terminals */
200 int fg = 0x96cb5c, bg = 0x516f7b;
201 int colors[] = {0x173f4f, fg, 0x68cbc0 | FN_B};
202 int c = 0;
203 int r = pad_rows() - 1;
204 int i;
205 pad_put('T', r, c++, fg | FN_B, bg);
206 pad_put('A', r, c++, fg | FN_B, bg);
207 pad_put('G', r, c++, fg | FN_B, bg);
208 pad_put('S', r, c++, fg | FN_B, bg);
209 pad_put(':', r, c++, fg | FN_B, bg);
210 pad_put(' ', r, c++, fg | FN_B, bg);
211 for (i = 0; i < NTAGS && c + 2 < pad_cols(); i++) {
212 int nt = 0;
213 if (TERMOPEN(i))
214 nt++;
215 if (TERMOPEN(aterm(i)))
216 nt++;
217 pad_put(i == ctag ? '(' : ' ', r, c++, fg, bg);
218 if (TERMSNAP(i))
219 pad_put(tags[i], r, c++, !nt ? bg : colors[nt], colors[0]);
220 else
221 pad_put(tags[i], r, c++, colors[nt], bg);
222 pad_put(i == ctag ? ')' : ' ', r, c++, fg, bg);
224 for (; c < pad_cols(); c++)
225 pad_put(' ', r, c, fg, bg);
228 static void directkey(void)
230 char *shell[32] = SHELL;
231 char *mail[32] = MAIL;
232 char *editor[32] = EDITOR;
233 int c = readchar();
234 if (PASS && locked) {
235 if (c == '\r') {
236 pass[passlen] = '\0';
237 if (!strcmp(PASS, pass))
238 locked = 0;
239 passlen = 0;
240 return;
242 if (isprint(c) && passlen + 1 < sizeof(pass))
243 pass[passlen++] = c;
244 return;
246 if (c == ESC) {
247 switch ((c = readchar())) {
248 case 'c':
249 t_exec(shell, 0);
250 return;
251 case ';':
252 t_exec(shell, 1);
253 return;
254 case 'm':
255 t_exec(mail, 0);
256 return;
257 case 'e':
258 t_exec(editor, 0);
259 return;
260 case 'j':
261 case 'k':
262 t_set(aterm(cterm()));
263 return;
264 case 'o':
265 t_set(tterm(ltag));
266 return;
267 case 'p':
268 listtags();
269 return;
270 case '\t':
271 if (nterm() != cterm())
272 t_set(nterm());
273 return;
274 case CTRLKEY('q'):
275 exitit = 1;
276 return;
277 case 's':
278 term_screenshot();
279 return;
280 case 'y':
281 term_redraw(1);
282 return;
283 case CTRLKEY('l'):
284 locked = 1;
285 passlen = 0;
286 return;
287 case CTRLKEY('o'):
288 taglock = 1 - taglock;
289 return;
290 case ',':
291 term_scrl(pad_rows() / 2);
292 return;
293 case '.':
294 term_scrl(-pad_rows() / 2);
295 return;
296 case '=':
297 t_split(split[ctag] == 1 ? 2 : 1);
298 return;
299 case '-':
300 t_split(0);
301 return;
302 default:
303 if (strchr(tags, c)) {
304 t_set(tterm(strchr(tags, c) - tags));
305 return;
307 if (tmain())
308 term_send(ESC);
311 if (c != -1 && tmain())
312 term_send(c);
315 static void peepterm(int termid)
317 int visible = !hidden && ctag == (termid % NTAGS) && split[ctag];
318 if (termid != cterm())
319 t_hideshow(cterm(), 0, termid, visible);
322 static void peepback(int termid)
324 if (termid != cterm())
325 t_hideshow(termid, 0, cterm(), !hidden);
328 static int pollterms(void)
330 struct pollfd ufds[NTERMS + 1];
331 int term_idx[NTERMS + 1];
332 int i;
333 int n = 1;
334 ufds[0].fd = 0;
335 ufds[0].events = POLLIN;
336 for (i = 0; i < NTERMS; i++) {
337 if (TERMOPEN(i)) {
338 ufds[n].fd = term_fd(terms[i]);
339 ufds[n].events = POLLIN;
340 term_idx[n++] = i;
343 if (poll(ufds, n, 1000) < 1)
344 return 0;
345 if (ufds[0].revents & (POLLFLAGS & ~POLLIN))
346 return 1;
347 if (ufds[0].revents & POLLIN)
348 directkey();
349 for (i = 1; i < n; i++) {
350 if (!(ufds[i].revents & POLLFLAGS))
351 continue;
352 peepterm(term_idx[i]);
353 if (ufds[i].revents & POLLIN) {
354 term_read();
355 } else {
356 scr_free(term_idx[i]);
357 term_end();
358 if (cmdmode)
359 exitit = 1;
361 peepback(term_idx[i]);
363 return 0;
366 static void mainloop(char **args)
368 struct termios oldtermios, termios;
369 tcgetattr(0, &termios);
370 oldtermios = termios;
371 cfmakeraw(&termios);
372 tcsetattr(0, TCSAFLUSH, &termios);
373 term_load(terms[cterm()], 1);
374 term_redraw(1);
375 if (args) {
376 cmdmode = 1;
377 t_exec(args, 0);
379 while (!exitit)
380 if (pollterms())
381 break;
382 tcsetattr(0, 0, &oldtermios);
385 static void signalreceived(int n)
387 if (exitit)
388 return;
389 switch (n) {
390 case SIGUSR1:
391 hidden = 1;
392 t_hide(cterm(), 1);
393 ioctl(0, VT_RELDISP, 1);
394 break;
395 case SIGUSR2:
396 hidden = 0;
397 fb_cmap();
398 if (t_show(cterm(), 2) == 3 && split[ctag]) {
399 t_hideshow(cterm(), 0, aterm(cterm()), 3);
400 t_hideshow(aterm(cterm()), 0, cterm(), 1);
402 break;
403 case SIGCHLD:
404 while (waitpid(-1, NULL, WNOHANG) > 0)
406 break;
410 static void signalsetup(void)
412 struct vt_mode vtm;
413 vtm.mode = VT_PROCESS;
414 vtm.waitv = 0;
415 vtm.relsig = SIGUSR1;
416 vtm.acqsig = SIGUSR2;
417 vtm.frsig = 0;
418 signal(SIGUSR1, signalreceived);
419 signal(SIGUSR2, signalreceived);
420 signal(SIGCHLD, signalreceived);
421 ioctl(0, VT_SETMODE, &vtm);
424 int main(int argc, char **argv)
426 char *hide = "\x1b[2J\x1b[H\x1b[?25l";
427 char *show = "\x1b[?25h";
428 char **args = argv + 1;
429 int i;
430 if (fb_init(getenv("FBDEV"))) {
431 fprintf(stderr, "fbpad: failed to initialize the framebuffer\n");
432 return 1;
434 if (pad_init()) {
435 fprintf(stderr, "fbpad: cannot find fonts\n");
436 return 1;
438 for (i = 0; i < NTERMS; i++)
439 terms[i] = term_make();
440 write(1, hide, strlen(hide));
441 signalsetup();
442 fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
443 while (args[0] && args[0][0] == '-')
444 args++;
445 mainloop(args[0] ? args : NULL);
446 write(1, show, strlen(show));
447 for (i = 0; i < NTERMS; i++)
448 term_free(terms[i]);
449 pad_free();
450 scr_done();
451 fb_free();
452 return 0;