term: ignore movements beyond the scrolling region
[fbpad.git] / fbpad.c
blob8ca6180e21b88c759dc67d4f0246212c85ce3dee
1 /*
2 * FBPAD FRAMEBUFFER VIRTUAL TERMINAL
4 * Copyright (C) 2009-2018 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 <signal.h>
24 #include <string.h>
25 #include <sys/ioctl.h>
26 #include <sys/wait.h>
27 #include <termios.h>
28 #include <unistd.h>
29 #include <linux/vt.h>
30 #include "conf.h"
31 #include "fbpad.h"
32 #include "draw.h"
34 #define CTRLKEY(x) ((x) - 96)
35 #define POLLFLAGS (POLLIN | POLLHUP | POLLERR | POLLNVAL)
36 #define NTAGS (sizeof(tags) - 1)
37 #define NTERMS (NTAGS * 2)
38 #define TERMOPEN(i) (terms[i].fd)
39 #define TERMSNAP(i) (strchr(TAGS_SAVED, tags[(i) % NTAGS]))
41 static char tags[] = TAGS;
42 static struct term terms[NTERMS];
43 static int tops[NTAGS]; /* top terms of tags */
44 static int ctag; /* current tag */
45 static int ltag; /* the last tag */
46 static int exitit;
47 static int hidden; /* do not touch the framebuffer */
48 static int locked;
49 static int taglock; /* disable tag switching */
50 static char pass[1024];
51 static int passlen;
52 static int cmdmode; /* execute a command and exit */
54 static int readchar(void)
56 char b;
57 if (read(0, &b, 1) > 0)
58 return (unsigned char) b;
59 return -1;
62 static int cterm(void)
64 return tops[ctag] * NTAGS + ctag;
67 static int altterm(int n)
69 return n < NTAGS ? n + NTAGS : n - NTAGS;
72 static int nextterm(void)
74 int n = (cterm() + 1) % NTERMS;
75 while (n != cterm()) {
76 if (TERMOPEN(n))
77 break;
78 n = (n + 1) % NTERMS;
80 return n;
83 static struct term *mainterm(void)
85 return TERMOPEN(cterm()) ? &terms[cterm()] : NULL;
88 static void switchterm(int oidx, int nidx, int show, int save, int load)
90 if (save && TERMOPEN(oidx) && TERMSNAP(oidx))
91 scr_snap(oidx);
92 term_save(&terms[oidx]);
93 term_load(&terms[nidx], show);
94 if (show)
95 term_redraw(load && (!TERMOPEN(nidx) || !TERMSNAP(nidx) ||
96 scr_load(nidx)));
99 static void showterm(int n)
101 if (cterm() == n || cmdmode)
102 return;
103 if (taglock && ctag != n % NTAGS)
104 return;
105 if (ctag != n % NTAGS)
106 ltag = ctag;
107 switchterm(cterm(), n, !hidden, !hidden, !hidden);
108 ctag = n % NTAGS;
109 tops[ctag] = n / NTAGS;
112 static void showtag(int n)
114 showterm(tops[n] * NTAGS + n);
117 static void execterm(char **args)
119 if (!mainterm())
120 term_exec(args);
123 static void listtags(void)
125 /* colors for tags based on their number of terminals */
126 int colors[] = {COLOR7, FGCOLOR, FGCOLOR | FN_B};
127 int c = 0;
128 int r = pad_rows() - 1;
129 int i;
130 pad_put('T', r, c++, FGCOLOR, BGCOLOR);
131 pad_put('A', r, c++, FGCOLOR, BGCOLOR);
132 pad_put('G', r, c++, FGCOLOR, BGCOLOR);
133 pad_put('S', r, c++, FGCOLOR, BGCOLOR);
134 pad_put(':', r, c++, FGCOLOR, BGCOLOR);
135 pad_put(' ', r, c++, FGCOLOR, BGCOLOR);
136 for (i = 0; i < NTAGS; i++) {
137 int nt = 0;
138 if (TERMOPEN(i))
139 nt++;
140 if (TERMOPEN(altterm(i)))
141 nt++;
142 pad_put(i == ctag ? '(' : ' ', r, c++, FGCOLOR, BGCOLOR);
143 if (TERMSNAP(i))
144 pad_put(tags[i], r, c++, !nt ? BGCOLOR : colors[nt], colors[0]);
145 else
146 pad_put(tags[i], r, c++, colors[nt], BGCOLOR);
147 pad_put(i == ctag ? ')' : ' ', r, c++, FGCOLOR, BGCOLOR);
151 static void directkey(void)
153 char *shell[32] = SHELL;
154 char *mail[32] = MAIL;
155 char *editor[32] = EDITOR;
156 int c = readchar();
157 if (PASS && locked) {
158 if (c == '\r') {
159 pass[passlen] = '\0';
160 if (!strcmp(PASS, pass))
161 locked = 0;
162 passlen = 0;
163 return;
165 if (isprint(c) && passlen + 1 < sizeof(pass))
166 pass[passlen++] = c;
167 return;
169 if (c == ESC) {
170 switch ((c = readchar())) {
171 case 'c':
172 execterm(shell);
173 return;
174 case 'm':
175 execterm(mail);
176 return;
177 case 'e':
178 execterm(editor);
179 return;
180 case 'j':
181 case 'k':
182 showterm(altterm(cterm()));
183 return;
184 case 'o':
185 showtag(ltag);
186 return;
187 case 'p':
188 listtags();
189 return;
190 case '\t':
191 if (nextterm() != cterm())
192 showterm(nextterm());
193 return;
194 case CTRLKEY('q'):
195 exitit = 1;
196 return;
197 case 's':
198 term_screenshot();
199 return;
200 case 'y':
201 term_redraw(1);
202 return;
203 case CTRLKEY('l'):
204 locked = 1;
205 passlen = 0;
206 return;
207 case CTRLKEY('o'):
208 taglock = 1 - taglock;
209 return;
210 case ',':
211 term_scrl(pad_rows() / 2);
212 return;
213 case '.':
214 term_scrl(-pad_rows() / 2);
215 return;
216 default:
217 if (strchr(tags, c)) {
218 showtag(strchr(tags, c) - tags);
219 return;
221 if (mainterm())
222 term_send(ESC);
225 if (c != -1 && mainterm())
226 term_send(c);
229 static void peepterm(int termid)
231 if (termid != cterm())
232 switchterm(cterm(), termid, 0, 0, 0);
235 static void peepback(int termid)
237 if (termid != cterm())
238 switchterm(termid, cterm(), !hidden, 0, 0);
241 static int pollterms(void)
243 struct pollfd ufds[NTERMS + 1];
244 int term_idx[NTERMS + 1];
245 int i;
246 int n = 1;
247 ufds[0].fd = 0;
248 ufds[0].events = POLLIN;
249 for (i = 0; i < NTERMS; i++) {
250 if (TERMOPEN(i)) {
251 ufds[n].fd = terms[i].fd;
252 ufds[n].events = POLLIN;
253 term_idx[n++] = i;
256 if (poll(ufds, n, 1000) < 1)
257 return 0;
258 if (ufds[0].revents & (POLLFLAGS & ~POLLIN))
259 return 1;
260 if (ufds[0].revents & POLLIN)
261 directkey();
262 for (i = 1; i < n; i++) {
263 if (!(ufds[i].revents & POLLFLAGS))
264 continue;
265 peepterm(term_idx[i]);
266 if (ufds[i].revents & POLLIN) {
267 term_read();
268 } else {
269 scr_free(term_idx[i]);
270 term_end();
271 if (cmdmode)
272 exitit = 1;
274 peepback(term_idx[i]);
276 return 0;
279 static void mainloop(char **args)
281 struct termios oldtermios, termios;
282 tcgetattr(0, &termios);
283 oldtermios = termios;
284 cfmakeraw(&termios);
285 tcsetattr(0, TCSAFLUSH, &termios);
286 term_load(&terms[cterm()], 1);
287 term_redraw(1);
288 if (args) {
289 cmdmode = 1;
290 execterm(args);
292 while (!exitit)
293 if (pollterms())
294 break;
295 tcsetattr(0, 0, &oldtermios);
298 static void signalreceived(int n)
300 if (exitit)
301 return;
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 signal(SIGUSR1, signalreceived);
329 signal(SIGUSR2, signalreceived);
330 signal(SIGCHLD, signalreceived);
331 ioctl(0, VT_SETMODE, &vtm);
334 int main(int argc, char **argv)
336 char *hide = "\x1b[2J\x1b[H\x1b[?25l";
337 char *show = "\x1b[?25h";
338 char **args = argv + 1;
339 if (fb_init(FBDEV)) {
340 fprintf(stderr, "fbpad: failed to initialize the framebuffer\n");
341 return 1;
343 if (sizeof(fbval_t) != FBM_BPP(fb_mode())) {
344 fprintf(stderr, "fbpad: fbval_t does not match framebuffer depth\n");
345 return 1;
347 if (pad_init()) {
348 fprintf(stderr, "fbpad: cannot find fonts\n");
349 return 1;
351 write(1, hide, strlen(hide));
352 signalsetup();
353 fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
354 while (args[0] && args[0][0] == '-')
355 args++;
356 mainloop(args[0] ? args : NULL);
357 write(1, show, strlen(show));
358 pad_free();
359 scr_done();
360 fb_free();
361 return 0;