README: replace ft2tf with fbpad_mkfn
[fbpad.git] / fbpad.c
blobedeab505cd4f5a5edb81df1bdf23bcfbad0d456e
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 */
44 static int readchar(void)
46 char b;
47 if (read(0, &b, 1) > 0)
48 return (unsigned char) b;
49 return -1;
52 static int cterm(void)
54 return tops[ctag] * NTAGS + ctag;
57 static int altterm(int n)
59 return n < NTAGS ? n + NTAGS : n - NTAGS;
62 static int nextterm(void)
64 int n = (cterm() + 1) % NTERMS;
65 while (n != cterm()) {
66 if (TERMOPEN(n))
67 break;
68 n = (n + 1) % NTERMS;
70 return n;
73 static struct term *mainterm(void)
75 return TERMOPEN(cterm()) ? &terms[cterm()] : NULL;
78 static void switchterm(int oidx, int nidx, int show, int save, int load)
80 if (save && TERMOPEN(oidx) && TERMSNAP(oidx))
81 scr_snap(oidx);
82 term_save(&terms[oidx]);
83 term_load(&terms[nidx], show);
84 if (show)
85 term_redraw(load && (!TERMOPEN(nidx) || !TERMSNAP(nidx) ||
86 scr_load(nidx)));
89 static void showterm(int n)
91 if (cterm() == n || cmdmode)
92 return;
93 if (taglock && ctag != n % NTAGS)
94 return;
95 if (ctag != n % NTAGS)
96 ltag = ctag;
97 switchterm(cterm(), n, !hidden, !hidden, !hidden);
98 ctag = n % NTAGS;
99 tops[ctag] = n / NTAGS;
102 static void showtag(int n)
104 showterm(tops[n] * NTAGS + n);
107 static void execterm(char **args)
109 if (!mainterm())
110 term_exec(args);
113 static void listtags(void)
115 /* colors for tags based on their number of terminals */
116 int colors[] = {252, FGCOLOR, FGCOLOR | FN_B};
117 int c = 0;
118 int r = pad_rows() - 1;
119 int i;
120 pad_put('T', r, c++, FGCOLOR, BGCOLOR);
121 pad_put('A', r, c++, FGCOLOR, BGCOLOR);
122 pad_put('G', r, c++, FGCOLOR, BGCOLOR);
123 pad_put('S', r, c++, FGCOLOR, BGCOLOR);
124 pad_put(':', r, c++, FGCOLOR, BGCOLOR);
125 pad_put(' ', r, c++, FGCOLOR, BGCOLOR);
126 for (i = 0; i < NTAGS; i++) {
127 int nt = 0;
128 if (TERMOPEN(i))
129 nt++;
130 if (TERMOPEN(altterm(i)))
131 nt++;
132 pad_put(i == ctag ? '(' : ' ', r, c++, FGCOLOR, BGCOLOR);
133 if (TERMSNAP(i))
134 pad_put(tags[i], r, c++, !nt ? BGCOLOR : colors[nt], colors[0]);
135 else
136 pad_put(tags[i], r, c++, colors[nt], BGCOLOR);
137 pad_put(i == ctag ? ')' : ' ', r, c++, FGCOLOR, BGCOLOR);
141 static void directkey(void)
143 char *shell[32] = SHELL;
144 char *mail[32] = MAIL;
145 char *editor[32] = EDITOR;
146 int c = readchar();
147 if (PASS && locked) {
148 if (c == '\r') {
149 pass[passlen] = '\0';
150 if (!strcmp(PASS, pass))
151 locked = 0;
152 passlen = 0;
153 return;
155 if (isprint(c) && passlen + 1 < sizeof(pass))
156 pass[passlen++] = c;
157 return;
159 if (c == ESC) {
160 switch ((c = readchar())) {
161 case 'c':
162 execterm(shell);
163 return;
164 case 'm':
165 execterm(mail);
166 return;
167 case 'e':
168 execterm(editor);
169 return;
170 case 'j':
171 case 'k':
172 showterm(altterm(cterm()));
173 return;
174 case 'o':
175 showtag(ltag);
176 return;
177 case 'p':
178 listtags();
179 return;
180 case '\t':
181 if (nextterm() != cterm())
182 showterm(nextterm());
183 return;
184 case CTRLKEY('q'):
185 exitit = 1;
186 return;
187 case 's':
188 term_screenshot();
189 return;
190 case 'y':
191 term_redraw(1);
192 return;
193 case CTRLKEY('l'):
194 locked = 1;
195 passlen = 0;
196 return;
197 case CTRLKEY('o'):
198 taglock = 1 - taglock;
199 return;
200 case ',':
201 term_scrl(pad_rows() / 2);
202 return;
203 case '.':
204 term_scrl(-pad_rows() / 2);
205 return;
206 default:
207 if (strchr(tags, c)) {
208 showtag(strchr(tags, c) - tags);
209 return;
211 if (mainterm())
212 term_send(ESC);
215 if (c != -1 && mainterm())
216 term_send(c);
219 static void peepterm(int termid)
221 if (termid != cterm())
222 switchterm(cterm(), termid, 0, 0, 0);
225 static void peepback(int termid)
227 if (termid != cterm())
228 switchterm(termid, cterm(), !hidden, 0, 0);
231 static int pollterms(void)
233 struct pollfd ufds[NTERMS + 1];
234 int term_idx[NTERMS + 1];
235 int i;
236 int n = 1;
237 ufds[0].fd = 0;
238 ufds[0].events = POLLIN;
239 for (i = 0; i < NTERMS; i++) {
240 if (TERMOPEN(i)) {
241 ufds[n].fd = terms[i].fd;
242 ufds[n].events = POLLIN;
243 term_idx[n++] = i;
246 if (poll(ufds, n, 1000) < 1)
247 return 0;
248 if (ufds[0].revents & (POLLFLAGS & ~POLLIN))
249 return 1;
250 if (ufds[0].revents & POLLIN)
251 directkey();
252 for (i = 1; i < n; i++) {
253 if (!(ufds[i].revents & POLLFLAGS))
254 continue;
255 peepterm(term_idx[i]);
256 if (ufds[i].revents & POLLIN) {
257 term_read();
258 } else {
259 scr_free(term_idx[i]);
260 term_end();
261 if (cmdmode)
262 exitit = 1;
264 peepback(term_idx[i]);
266 return 0;
269 static void mainloop(char **args)
271 struct termios oldtermios, termios;
272 tcgetattr(0, &termios);
273 oldtermios = termios;
274 cfmakeraw(&termios);
275 tcsetattr(0, TCSAFLUSH, &termios);
276 term_load(&terms[cterm()], 1);
277 term_redraw(1);
278 if (args) {
279 cmdmode = 1;
280 execterm(args);
282 while (!exitit)
283 if (pollterms())
284 break;
285 tcsetattr(0, 0, &oldtermios);
288 static void signalreceived(int n);
289 static void signalregister(void)
291 signal(SIGUSR1, signalreceived);
292 signal(SIGUSR2, signalreceived);
293 signal(SIGCHLD, signalreceived);
296 static void signalreceived(int n)
298 if (exitit)
299 return;
300 /* racy, new signals may arrive before re-registeration */
301 signalregister();
302 switch (n) {
303 case SIGUSR1:
304 hidden = 1;
305 switchterm(cterm(), cterm(), 0, 1, 0);
306 ioctl(0, VT_RELDISP, 1);
307 break;
308 case SIGUSR2:
309 hidden = 0;
310 fb_cmap();
311 switchterm(cterm(), cterm(), 1, 0, 1);
312 break;
313 case SIGCHLD:
314 while (waitpid(-1, NULL, WNOHANG) > 0)
316 break;
320 static void signalsetup(void)
322 struct vt_mode vtm;
323 vtm.mode = VT_PROCESS;
324 vtm.waitv = 0;
325 vtm.relsig = SIGUSR1;
326 vtm.acqsig = SIGUSR2;
327 vtm.frsig = 0;
328 signalregister();
329 ioctl(0, VT_SETMODE, &vtm);
332 int main(int argc, char **argv)
334 char *hide = "\x1b[2J\x1b[H\x1b[?25l";
335 char *show = "\x1b[?25h";
336 char **args = argv + 1;
337 if (fb_init(FBDEV)) {
338 fprintf(stderr, "fbpad: failed to initialize the framebuffer\n");
339 return 1;
341 if (sizeof(fbval_t) != FBM_BPP(fb_mode())) {
342 fprintf(stderr, "fbpad: fbval_t does not match framebuffer depth\n");
343 return 1;
345 if (pad_init()) {
346 fprintf(stderr, "fbpad: cannot find fonts\n");
347 return 1;
349 write(1, hide, strlen(hide));
350 signalsetup();
351 fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
352 while (args[0] && args[0][0] == '-')
353 args++;
354 mainloop(args[0] ? args : NULL);
355 write(1, show, strlen(show));
356 pad_free();
357 scr_done();
358 fb_free();
359 return 0;