fbpad: new tag listing
[fbpad.git] / fbpad.c
blob99a0e4b6e4bd89e9fb616fcdec3e1aa46d201e7b
1 /*
2 * fbpad - a small framebuffer virtual terminal
4 * Copyright (C) 2009-2015 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 <stdio.h>
13 #include <signal.h>
14 #include <string.h>
15 #include <sys/ioctl.h>
16 #include <sys/wait.h>
17 #include <termios.h>
18 #include <unistd.h>
19 #include <linux/vt.h>
20 #include "config.h"
21 #include "fbpad.h"
22 #include "draw.h"
24 #define CTRLKEY(x) ((x) - 96)
25 #define POLLFLAGS (POLLIN | POLLHUP | POLLERR | POLLNVAL)
26 #define NTAGS (sizeof(tags) - 1)
27 #define NTERMS (NTAGS * 2)
28 #define TERMOPEN(i) (terms[i].fd)
29 #define TERMSNAP(i) (strchr(TAGS_SAVED, tags[(i) % NTAGS]))
31 static char tags[] = TAGS;
32 static struct term terms[NTERMS];
33 static int tops[NTAGS]; /* top terms of tags */
34 static int ctag; /* current tag */
35 static int ltag; /* the last tag */
36 static int exitit;
37 static int hidden; /* do not touch the framebuffer */
38 static int locked;
39 static int taglock; /* disable tag switching */
40 static char pass[1024];
41 static int passlen;
42 static int cmdmode; /* execute a command and exit */
43 static int altfont; /* using alternative font set */
45 static int readchar(void)
47 char b;
48 if (read(0, &b, 1) > 0)
49 return (unsigned char) b;
50 return -1;
53 static int cterm(void)
55 return tops[ctag] * NTAGS + ctag;
58 static int altterm(int n)
60 return n < NTAGS ? n + NTAGS : n - NTAGS;
63 static int nextterm(void)
65 int n = (cterm() + 1) % NTERMS;
66 while (n != cterm()) {
67 if (TERMOPEN(n))
68 break;
69 n = (n + 1) % NTERMS;
71 return n;
74 static struct term *mainterm(void)
76 return TERMOPEN(cterm()) ? &terms[cterm()] : NULL;
79 static void switchterm(int oidx, int nidx, int show, int save, int load)
81 if (save && TERMOPEN(oidx) && TERMSNAP(oidx))
82 scr_snap(oidx);
83 term_save(&terms[oidx]);
84 term_load(&terms[nidx], show);
85 if (show)
86 term_redraw(load && (!TERMOPEN(nidx) || !TERMSNAP(nidx) ||
87 scr_load(nidx)));
90 static void showterm(int n)
92 if (cterm() == n || cmdmode)
93 return;
94 if (taglock && ctag != n % NTAGS)
95 return;
96 if (ctag != n % NTAGS)
97 ltag = ctag;
98 switchterm(cterm(), n, !hidden, !hidden, !hidden);
99 ctag = n % NTAGS;
100 tops[ctag] = n / NTAGS;
103 static void showtag(int n)
105 showterm(tops[n] * NTAGS + n);
108 static void execterm(char **args)
110 if (!mainterm())
111 term_exec(args);
114 static void listtags(void)
116 int c = pad_cols() - 1;
117 int r = 1;
118 int i;
119 for (i = 0; i < NTAGS; i++) {
120 int fg = 8, fglow = TERMSNAP(i) ? 218 : 150;
121 int bg = i == ctag ? 193 : 225;
122 int t1 = tops[i] * NTAGS + i;
123 int t2 = (1 - tops[i]) * NTAGS + i;
124 pad_put(tags[i], r + i, c - 1, (TERMOPEN(t1) ? fg : fglow) | FN_B, bg);
125 pad_put(tags[i], r + i, c, (TERMOPEN(t2) ? fg : bg) | FN_B, bg);
129 static void directkey(void)
131 char *shell[32] = SHELL;
132 char *mail[32] = MAIL;
133 char *editor[32] = EDITOR;
134 int c = readchar();
135 if (PASS && locked) {
136 if (c == '\r') {
137 pass[passlen] = '\0';
138 if (!strcmp(PASS, pass))
139 locked = 0;
140 passlen = 0;
141 return;
143 if (isprint(c) && passlen + 1 < sizeof(pass))
144 pass[passlen++] = c;
145 return;
147 if (c == ESC) {
148 switch ((c = readchar())) {
149 case 'c':
150 execterm(shell);
151 return;
152 case 'm':
153 execterm(mail);
154 return;
155 case 'e':
156 execterm(editor);
157 return;
158 case 'j':
159 case 'k':
160 showterm(altterm(cterm()));
161 return;
162 case 'o':
163 showtag(ltag);
164 return;
165 case 'p':
166 listtags();
167 return;
168 case '\t':
169 if (nextterm() != cterm())
170 showterm(nextterm());
171 return;
172 case CTRLKEY('q'):
173 exitit = 1;
174 return;
175 case 's':
176 term_screenshot();
177 return;
178 case 'y':
179 term_redraw(1);
180 return;
181 case 'f':
182 altfont = 1 - altfont;
183 if (altfont)
184 pad_font(FR0, FI0, FB0);
185 else
186 pad_font(FR, FI, FB);
187 term_redraw(1);
188 return;
189 case CTRLKEY('l'):
190 locked = 1;
191 passlen = 0;
192 return;
193 case CTRLKEY('o'):
194 taglock = 1 - taglock;
195 return;
196 case ',':
197 term_scrl(pad_rows() / 2);
198 return;
199 case '.':
200 term_scrl(-pad_rows() / 2);
201 return;
202 default:
203 if (strchr(tags, c)) {
204 showtag(strchr(tags, c) - tags);
205 return;
207 if (mainterm())
208 term_send(ESC);
211 if (c != -1 && mainterm())
212 term_send(c);
215 static void peepterm(int termid)
217 if (termid != cterm())
218 switchterm(cterm(), termid, 0, 0, 0);
221 static void peepback(int termid)
223 if (termid != cterm())
224 switchterm(termid, cterm(), !hidden, 0, 0);
227 static int pollterms(void)
229 struct pollfd ufds[NTERMS + 1];
230 int term_idx[NTERMS + 1];
231 int i;
232 int n = 1;
233 ufds[0].fd = 0;
234 ufds[0].events = POLLIN;
235 for (i = 0; i < NTERMS; i++) {
236 if (TERMOPEN(i)) {
237 ufds[n].fd = terms[i].fd;
238 ufds[n].events = POLLIN;
239 term_idx[n++] = i;
242 if (poll(ufds, n, 1000) < 1)
243 return 0;
244 if (ufds[0].revents & (POLLFLAGS & ~POLLIN))
245 return 1;
246 if (ufds[0].revents & POLLIN)
247 directkey();
248 for (i = 1; i < n; i++) {
249 if (!(ufds[i].revents & POLLFLAGS))
250 continue;
251 peepterm(term_idx[i]);
252 if (ufds[i].revents & POLLIN) {
253 term_read();
254 } else {
255 scr_free(term_idx[i]);
256 term_end();
257 if (cmdmode)
258 exitit = 1;
260 peepback(term_idx[i]);
262 return 0;
265 static void mainloop(char **args)
267 struct termios oldtermios, termios;
268 tcgetattr(0, &termios);
269 oldtermios = termios;
270 cfmakeraw(&termios);
271 tcsetattr(0, TCSAFLUSH, &termios);
272 term_load(&terms[cterm()], 1);
273 term_redraw(1);
274 if (args) {
275 cmdmode = 1;
276 execterm(args);
278 while (!exitit)
279 if (pollterms())
280 break;
281 tcsetattr(0, 0, &oldtermios);
284 static void signalreceived(int n);
285 static void signalregister(void)
287 signal(SIGUSR1, signalreceived);
288 signal(SIGUSR2, signalreceived);
289 signal(SIGCHLD, signalreceived);
292 static void signalreceived(int n)
294 if (exitit)
295 return;
296 /* racy, new signals may arrive before re-registeration */
297 signalregister();
298 switch (n) {
299 case SIGUSR1:
300 hidden = 1;
301 switchterm(cterm(), cterm(), 0, 1, 0);
302 ioctl(0, VT_RELDISP, 1);
303 break;
304 case SIGUSR2:
305 hidden = 0;
306 fb_cmap();
307 switchterm(cterm(), cterm(), 1, 0, 1);
308 break;
309 case SIGCHLD:
310 while (waitpid(-1, NULL, WNOHANG) > 0)
312 break;
316 static void signalsetup(void)
318 struct vt_mode vtm;
319 vtm.mode = VT_PROCESS;
320 vtm.waitv = 0;
321 vtm.relsig = SIGUSR1;
322 vtm.acqsig = SIGUSR2;
323 vtm.frsig = 0;
324 signalregister();
325 ioctl(0, VT_SETMODE, &vtm);
328 int main(int argc, char **argv)
330 char *hide = "\x1b[2J\x1b[H\x1b[?25l";
331 char *show = "\x1b[?25h";
332 char **args = argv + 1;
333 if (fb_init(FBDEV)) {
334 fprintf(stderr, "fbpad: failed to initialize the framebuffer\n");
335 return 1;
337 if (sizeof(fbval_t) != FBM_BPP(fb_mode())) {
338 fprintf(stderr, "fbpad: fbval_t does not match framebuffer depth\n");
339 return 1;
341 if (pad_init()) {
342 fprintf(stderr, "fbpad: cannot find fonts\n");
343 return 1;
345 write(1, hide, strlen(hide));
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 write(1, show, strlen(show));
352 pad_free();
353 scr_done();
354 fb_free();
355 return 0;