term: stop showing the history after term_hist(0)
[fbpad.git] / fbpad.c
blob4868c82d636cd0bddd113a28c5a80f3d8201ecd9
1 /*
2 * fbpad - a small framebuffer virtual terminal
4 * Copyright (C) 2009-2013 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 <signal.h>
13 #include <string.h>
14 #include <sys/ioctl.h>
15 #include <sys/wait.h>
16 #include <termios.h>
17 #include <unistd.h>
18 #include <linux/vt.h>
19 #include "config.h"
20 #include "fbpad.h"
21 #include "draw.h"
23 #define CTRLKEY(x) ((x) - 96)
24 #define POLLFLAGS (POLLIN | POLLHUP | POLLERR | POLLNVAL)
25 #define NTAGS (sizeof(tags) - 1)
26 #define NTERMS (NTAGS * 2)
27 #define TERMOPEN(i) (terms[i].fd)
28 #define TERMSNAP(i) (strchr(TAGS_SAVED, tags[(i) % NTAGS]))
30 static char tags[] = TAGS;
31 static struct term terms[NTERMS];
32 static int tops[NTAGS]; /* top terms of tags */
33 static int ctag; /* current tag */
34 static int ltag; /* the last tag */
35 static int exitit;
36 static int hidden; /* do not touch the framebuffer */
37 static int locked;
38 static char pass[1024];
39 static int passlen;
40 static int cmdmode; /* execute a command and exit */
41 static int histpos; /* scrolling history */
43 static int readchar(void)
45 char b;
46 if (read(0, &b, 1) > 0)
47 return (int) b;
48 return -1;
51 static int cterm(void)
53 return tops[ctag] * NTAGS + ctag;
56 static void term_switch(int oidx, int nidx, int show, int save, int load)
58 int flags = show ? (load ? TERM_REDRAW : TERM_VISIBLE) : TERM_HIDDEN;
59 if (save && TERMOPEN(oidx) && TERMSNAP(oidx))
60 scr_snap(&terms[oidx]);
61 term_save(&terms[oidx]);
62 if (show && load && TERMOPEN(nidx) && TERMSNAP(nidx))
63 flags = scr_load(&terms[nidx]) ? TERM_REDRAW : TERM_VISIBLE;
64 term_load(&terms[nidx], flags);
65 if (show && load)
66 histpos = 0;
69 static void showterm(int n)
71 if (cterm() == n || cmdmode)
72 return;
73 if (ctag != n % NTAGS)
74 ltag = ctag;
75 term_switch(cterm(), n, !hidden, !hidden, !hidden);
76 ctag = n % NTAGS;
77 tops[ctag] = n / NTAGS;
80 static void showtag(int n)
82 showterm(tops[n] * NTAGS + n);
85 static struct term *mainterm(void)
87 if (TERMOPEN(cterm()))
88 return &terms[cterm()];
89 return NULL;
92 static void exec_cmd(char **args)
94 if (!mainterm())
95 term_exec(args);
98 static int altterm(int n)
100 return n < NTAGS ? n + NTAGS : n - NTAGS;
103 static void nextterm(void)
105 int n = (cterm() + 1) % NTERMS;
106 while (n != cterm()) {
107 if (TERMOPEN(n)) {
108 showterm(n);
109 break;
111 n = (n + 1) % NTERMS;
115 static void showtags(void)
117 int colors[] = {15, 4, 2};
118 int c = 0;
119 int r = pad_rows() - 1;
120 int i;
121 pad_put('T', r, c++, FGCOLOR, BGCOLOR);
122 pad_put('A', r, c++, FGCOLOR, BGCOLOR);
123 pad_put('G', r, c++, FGCOLOR, BGCOLOR);
124 pad_put('S', r, c++, FGCOLOR, BGCOLOR);
125 pad_put(':', r, c++, FGCOLOR, BGCOLOR);
126 pad_put(' ', r, c++, FGCOLOR, BGCOLOR);
127 for (i = 0; i < NTAGS; i++) {
128 int nt = 0;
129 if (TERMOPEN(i))
130 nt++;
131 if (TERMOPEN(altterm(i)))
132 nt++;
133 pad_put(i == ctag ? '(' : ' ', r, c++, FGCOLOR, BGCOLOR);
134 if (TERMSNAP(i))
135 pad_put(tags[i], r, c++, !nt ? BGCOLOR : colors[nt], 15);
136 else
137 pad_put(tags[i], r, c++, colors[nt], BGCOLOR);
138 pad_put(i == ctag ? ')' : ' ', r, c++, FGCOLOR, BGCOLOR);
142 static void directkey(void)
144 char *shell[32] = SHELL;
145 char *mail[32] = MAIL;
146 char *editor[32] = EDITOR;
147 int c = readchar();
148 if (PASS && locked) {
149 if (c == '\r') {
150 pass[passlen] = '\0';
151 if (!strcmp(PASS, pass))
152 locked = 0;
153 passlen = 0;
154 return;
156 if (isprint(c) && passlen + 1 < sizeof(pass))
157 pass[passlen++] = c;
158 return;
160 if (c == ESC) {
161 switch ((c = readchar())) {
162 case 'c':
163 exec_cmd(shell);
164 return;
165 case 'm':
166 exec_cmd(mail);
167 return;
168 case 'e':
169 exec_cmd(editor);
170 return;
171 case 'j':
172 case 'k':
173 showterm(altterm(cterm()));
174 return;
175 case 'o':
176 showtag(ltag);
177 return;
178 case 'p':
179 showtags();
180 return;
181 case '\t':
182 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_switch(cterm(), cterm(), 1, 0, 1);
192 return;
193 case CTRLKEY('l'):
194 locked = 1;
195 passlen = 0;
196 return;
197 case ',':
198 histpos = MIN(NHIST, histpos + pad_rows() / 2);
199 term_hist(histpos);
200 return;
201 case '.':
202 histpos = MAX(0, histpos - pad_rows() / 2);
203 term_hist(histpos);
204 return;
205 default:
206 if (strchr(tags, c)) {
207 showtag(strchr(tags, c) - tags);
208 return;
210 if (mainterm())
211 term_send(ESC);
214 histpos = 0;
215 if (c != -1 && mainterm())
216 term_send(c);
219 static void temp_switch(int termid)
221 if (termid != cterm())
222 term_switch(cterm(), termid, 0, 0, 0);
225 static void switch_back(int termid)
227 if (termid != cterm())
228 term_switch(termid, cterm(), !hidden, 0, 0);
231 static int poll_all(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 temp_switch(term_idx[i]);
256 if (ufds[i].revents & POLLIN) {
257 term_read();
258 } else {
259 scr_free(&terms[term_idx[i]]);
260 term_end();
261 if (cmdmode)
262 exitit = 1;
264 switch_back(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()], TERM_REDRAW);
277 if (args) {
278 cmdmode = 1;
279 exec_cmd(args);
281 while (!exitit)
282 if (poll_all())
283 break;
284 tcsetattr(0, 0, &oldtermios);
287 static void signalreceived(int n);
288 static void signalregister(void)
290 signal(SIGUSR1, signalreceived);
291 signal(SIGUSR2, signalreceived);
292 signal(SIGCHLD, signalreceived);
295 static void signalreceived(int n)
297 if (exitit)
298 return;
299 /* racy, new signals may arrive before re-registeration */
300 signalregister();
301 switch (n) {
302 case SIGUSR1:
303 hidden = 1;
304 term_switch(cterm(), cterm(), 0, 1, 0);
305 ioctl(0, VT_RELDISP, 1);
306 break;
307 case SIGUSR2:
308 hidden = 0;
309 fb_cmap();
310 term_switch(cterm(), cterm(), 1, 0, 1);
311 break;
312 case SIGCHLD:
313 while (waitpid(-1, NULL, WNOHANG) > 0)
315 break;
319 static void setupsignals(void)
321 struct vt_mode vtm;
322 vtm.mode = VT_PROCESS;
323 vtm.waitv = 0;
324 vtm.relsig = SIGUSR1;
325 vtm.acqsig = SIGUSR2;
326 vtm.frsig = 0;
327 signalregister();
328 ioctl(0, VT_SETMODE, &vtm);
331 int main(int argc, char **argv)
333 char *hide = "\x1b[?25l";
334 char *clear = "\x1b[2J\x1b[H";
335 char *show = "\x1b[?25h";
336 char **args = argv + 1;
337 write(1, clear, strlen(clear));
338 write(1, hide, strlen(hide));
339 if (pad_init())
340 goto failed;
341 setupsignals();
342 fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
343 while (args[0] && args[0][0] == '-')
344 args++;
345 mainloop(args[0] ? args : NULL);
346 pad_free();
347 failed:
348 write(1, show, strlen(show));
349 return 0;