term: redraw the curser after set/reset mode (SM/RM)
[fbpad.git] / fbpad.c
blob892909cc51a42acbf2a1aaee25cc5ba71d875adc
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 */
42 static int readchar(void)
44 char b;
45 if (read(0, &b, 1) > 0)
46 return (unsigned char) b;
47 return -1;
50 static int cterm(void)
52 return tops[ctag] * NTAGS + ctag;
55 static int altterm(int n)
57 return n < NTAGS ? n + NTAGS : n - NTAGS;
60 static int nextterm(void)
62 int n = (cterm() + 1) % NTERMS;
63 while (n != cterm()) {
64 if (TERMOPEN(n))
65 break;
66 n = (n + 1) % NTERMS;
68 return n;
71 static struct term *mainterm(void)
73 return TERMOPEN(cterm()) ? &terms[cterm()] : NULL;
76 static void switchterm(int oidx, int nidx, int show, int save, int load)
78 if (save && TERMOPEN(oidx) && TERMSNAP(oidx))
79 scr_snap(&terms[oidx]);
80 term_save(&terms[oidx]);
81 term_load(&terms[nidx], show);
82 if (show)
83 term_redraw(load && (!TERMOPEN(nidx) || !TERMSNAP(nidx) ||
84 scr_load(&terms[nidx])));
87 static void showterm(int n)
89 if (cterm() == n || cmdmode)
90 return;
91 if (ctag != n % NTAGS)
92 ltag = ctag;
93 switchterm(cterm(), n, !hidden, !hidden, !hidden);
94 ctag = n % NTAGS;
95 tops[ctag] = n / NTAGS;
98 static void showtag(int n)
100 showterm(tops[n] * NTAGS + n);
103 static void execterm(char **args)
105 if (!mainterm())
106 term_exec(args);
109 static void listtags(void)
111 /* colors for tags based on their number of terminals */
112 int colors[] = {252, FGCOLOR, FGCOLOR | FN_B};
113 int c = 0;
114 int r = pad_rows() - 1;
115 int i;
116 pad_put('T', r, c++, FGCOLOR, BGCOLOR);
117 pad_put('A', r, c++, FGCOLOR, BGCOLOR);
118 pad_put('G', r, c++, FGCOLOR, BGCOLOR);
119 pad_put('S', r, c++, FGCOLOR, BGCOLOR);
120 pad_put(':', r, c++, FGCOLOR, BGCOLOR);
121 pad_put(' ', r, c++, FGCOLOR, BGCOLOR);
122 for (i = 0; i < NTAGS; i++) {
123 int nt = 0;
124 if (TERMOPEN(i))
125 nt++;
126 if (TERMOPEN(altterm(i)))
127 nt++;
128 pad_put(i == ctag ? '(' : ' ', r, c++, FGCOLOR, BGCOLOR);
129 if (TERMSNAP(i))
130 pad_put(tags[i], r, c++, !nt ? BGCOLOR : colors[nt], colors[0]);
131 else
132 pad_put(tags[i], r, c++, colors[nt], BGCOLOR);
133 pad_put(i == ctag ? ')' : ' ', r, c++, FGCOLOR, BGCOLOR);
137 static void directkey(void)
139 char *shell[32] = SHELL;
140 char *mail[32] = MAIL;
141 char *editor[32] = EDITOR;
142 int c = readchar();
143 if (PASS && locked) {
144 if (c == '\r') {
145 pass[passlen] = '\0';
146 if (!strcmp(PASS, pass))
147 locked = 0;
148 passlen = 0;
149 return;
151 if (isprint(c) && passlen + 1 < sizeof(pass))
152 pass[passlen++] = c;
153 return;
155 if (c == ESC) {
156 switch ((c = readchar())) {
157 case 'c':
158 execterm(shell);
159 return;
160 case 'm':
161 execterm(mail);
162 return;
163 case 'e':
164 execterm(editor);
165 return;
166 case 'j':
167 case 'k':
168 showterm(altterm(cterm()));
169 return;
170 case 'o':
171 showtag(ltag);
172 return;
173 case 'p':
174 listtags();
175 return;
176 case '\t':
177 if (nextterm() != cterm())
178 showterm(nextterm());
179 return;
180 case CTRLKEY('q'):
181 exitit = 1;
182 return;
183 case 's':
184 term_screenshot();
185 return;
186 case 'y':
187 term_redraw(1);
188 return;
189 case CTRLKEY('l'):
190 locked = 1;
191 passlen = 0;
192 return;
193 case ',':
194 term_scrl(pad_rows() / 2);
195 return;
196 case '.':
197 term_scrl(-pad_rows() / 2);
198 return;
199 default:
200 if (strchr(tags, c)) {
201 showtag(strchr(tags, c) - tags);
202 return;
204 if (mainterm())
205 term_send(ESC);
208 if (c != -1 && mainterm())
209 term_send(c);
212 static void peepterm(int termid)
214 if (termid != cterm())
215 switchterm(cterm(), termid, 0, 0, 0);
218 static void peepback(int termid)
220 if (termid != cterm())
221 switchterm(termid, cterm(), !hidden, 0, 0);
224 static int pollterms(void)
226 struct pollfd ufds[NTERMS + 1];
227 int term_idx[NTERMS + 1];
228 int i;
229 int n = 1;
230 ufds[0].fd = 0;
231 ufds[0].events = POLLIN;
232 for (i = 0; i < NTERMS; i++) {
233 if (TERMOPEN(i)) {
234 ufds[n].fd = terms[i].fd;
235 ufds[n].events = POLLIN;
236 term_idx[n++] = i;
239 if (poll(ufds, n, 1000) < 1)
240 return 0;
241 if (ufds[0].revents & (POLLFLAGS & ~POLLIN))
242 return 1;
243 if (ufds[0].revents & POLLIN)
244 directkey();
245 for (i = 1; i < n; i++) {
246 if (!(ufds[i].revents & POLLFLAGS))
247 continue;
248 peepterm(term_idx[i]);
249 if (ufds[i].revents & POLLIN) {
250 term_read();
251 } else {
252 scr_free(&terms[term_idx[i]]);
253 term_end();
254 if (cmdmode)
255 exitit = 1;
257 peepback(term_idx[i]);
259 return 0;
262 static void mainloop(char **args)
264 struct termios oldtermios, termios;
265 tcgetattr(0, &termios);
266 oldtermios = termios;
267 cfmakeraw(&termios);
268 tcsetattr(0, TCSAFLUSH, &termios);
269 term_load(&terms[cterm()], 1);
270 term_redraw(1);
271 if (args) {
272 cmdmode = 1;
273 execterm(args);
275 while (!exitit)
276 if (pollterms())
277 break;
278 tcsetattr(0, 0, &oldtermios);
281 static void signalreceived(int n);
282 static void signalregister(void)
284 signal(SIGUSR1, signalreceived);
285 signal(SIGUSR2, signalreceived);
286 signal(SIGCHLD, signalreceived);
289 static void signalreceived(int n)
291 if (exitit)
292 return;
293 /* racy, new signals may arrive before re-registeration */
294 signalregister();
295 switch (n) {
296 case SIGUSR1:
297 hidden = 1;
298 switchterm(cterm(), cterm(), 0, 1, 0);
299 ioctl(0, VT_RELDISP, 1);
300 break;
301 case SIGUSR2:
302 hidden = 0;
303 fb_cmap();
304 switchterm(cterm(), cterm(), 1, 0, 1);
305 break;
306 case SIGCHLD:
307 while (waitpid(-1, NULL, WNOHANG) > 0)
309 break;
313 static void signalsetup(void)
315 struct vt_mode vtm;
316 vtm.mode = VT_PROCESS;
317 vtm.waitv = 0;
318 vtm.relsig = SIGUSR1;
319 vtm.acqsig = SIGUSR2;
320 vtm.frsig = 0;
321 signalregister();
322 ioctl(0, VT_SETMODE, &vtm);
325 int main(int argc, char **argv)
327 char *hide = "\x1b[?25l";
328 char *clear = "\x1b[2J\x1b[H";
329 char *show = "\x1b[?25h";
330 char **args = argv + 1;
331 write(1, clear, strlen(clear));
332 write(1, hide, strlen(hide));
333 if (pad_init())
334 goto failed;
335 signalsetup();
336 fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
337 while (args[0] && args[0][0] == '-')
338 args++;
339 mainloop(args[0] ? args : NULL);
340 pad_free();
341 failed:
342 write(1, show, strlen(show));
343 return 0;