2 * FBPAD FRAMEBUFFER VIRTUAL TERMINAL
4 * Copyright (C) 2009-2024 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.
26 #include <sys/ioctl.h>
35 #define CTRLKEY(x) ((x) - 96)
36 #define POLLFLAGS (POLLIN | POLLHUP | POLLERR | POLLNVAL)
37 #define NTAGS (sizeof(tags) - 1)
38 #define NTERMS (NTAGS * 2)
39 #define TERMOPEN(i) (term_fd(terms[i]))
40 #define TERMSNAP(i) (strchr(TAGS_SAVED, tags[(i) % NTAGS]))
42 static char tags
[] = TAGS
;
43 static struct term
*terms
[NTERMS
];
44 static int tops
[NTAGS
]; /* top terms of tags */
45 static int split
[NTAGS
]; /* terms are shown together */
46 static int ctag
; /* current tag */
47 static int ltag
; /* the last tag */
49 static int hidden
; /* do not touch the framebuffer */
51 static int taglock
; /* disable tag switching */
52 static char pass
[1024];
54 static int cmdmode
; /* execute a command and exit */
56 static int readchar(void)
59 if (read(0, &b
, 1) > 0)
60 return (unsigned char) b
;
64 /* the current terminal */
65 static int cterm(void)
67 return tops
[ctag
] * NTAGS
+ ctag
;
70 /* tag's active terminal */
71 static int tterm(int n
)
73 return tops
[n
] * NTAGS
+ n
;
76 /* the other terminal in the same tag */
77 static int aterm(int n
)
79 return n
< NTAGS
? n
+ NTAGS
: n
- NTAGS
;
82 /* the next terminal */
83 static int nterm(void)
85 int n
= (cterm() + 1) % NTERMS
;
86 while (n
!= cterm()) {
94 /* term struct of cterm() */
95 static struct term
*tmain(void)
97 return TERMOPEN(cterm()) ? terms
[cterm()] : NULL
;
101 #define BRCLR 0xff0000
103 static void t_conf(int idx
)
105 int h1
= fb_rows() / 2 / pad_crows() * pad_crows();
106 int h2
= fb_rows() - h1
- 4 * BRWID
;
107 int w1
= fb_cols() / 2 / pad_ccols() * pad_ccols();
108 int w2
= fb_cols() - w1
- 4 * BRWID
;
109 int tag
= idx
% NTAGS
;
110 int top
= idx
< NTAGS
;
112 pad_conf(0, 0, fb_rows(), fb_cols());
114 pad_conf(top
? BRWID
: h1
+ 3 * BRWID
, BRWID
,
115 top
? h1
: h2
, fb_cols() - 2 * BRWID
);
117 pad_conf(BRWID
, top
? BRWID
: w1
+ 3 * BRWID
,
118 fb_rows() - 2 * BRWID
, top
? w1
: w2
);
121 static void t_hide(int idx
, int save
)
123 if (save
&& TERMOPEN(idx
))
124 term_hide(terms
[idx
]);
125 if (save
&& TERMOPEN(idx
) && TERMSNAP(idx
))
127 term_save(terms
[idx
]);
130 /* show=0 (hidden), show=1 (visible), show=2 (load), show=3 (redraw) */
131 static int t_show(int idx
, int show
)
134 term_load(terms
[idx
], show
> 0);
135 if (show
== 2) /* redraw if scr_load() fails */
136 show
+= !TERMOPEN(idx
) || !TERMSNAP(idx
) || scr_load(idx
);
138 term_redraw(show
== 3);
139 if ((show
== 2 || show
== 3) && TERMOPEN(idx
))
140 term_show(terms
[idx
]);
144 /* switch active terminal; hide oidx and show nidx */
145 static int t_hideshow(int oidx
, int save
, int nidx
, int show
)
147 int otag
= oidx
% NTAGS
;
148 int ntag
= nidx
% NTAGS
;
151 if (show
&& split
[otag
] && otag
== ntag
)
152 pad_border(0, BRWID
);
153 ret
= t_show(nidx
, show
);
154 if (show
&& split
[ntag
])
155 pad_border(BRCLR
, BRWID
);
160 static void t_set(int n
)
162 if (cterm() == n
|| cmdmode
)
164 if (taglock
&& ctag
!= n
% NTAGS
)
166 if (ctag
!= n
% NTAGS
)
168 if (ctag
== n
% NTAGS
) {
169 if (split
[n
% NTAGS
])
170 t_hideshow(cterm(), 0, n
, 1);
172 t_hideshow(cterm(), 1, n
, 2);
174 int draw
= t_hideshow(cterm(), 1, n
, 2);
175 if (split
[n
% NTAGS
]) {
176 t_hideshow(n
, 0, aterm(n
), draw
== 2 ? 1 : 2);
177 t_hideshow(aterm(n
), 0, n
, 1);
181 tops
[ctag
] = n
/ NTAGS
;
184 static void t_split(int n
)
187 t_hideshow(cterm(), 0, aterm(cterm()), 3);
188 t_hideshow(aterm(cterm()), 1, cterm(), 3);
191 static void t_exec(char **args
, int swsig
)
194 term_exec(args
, swsig
);
197 static void listtags(void)
199 /* colors for tags based on their number of terminals */
200 int fg
= 0x96cb5c, bg
= 0x516f7b;
201 int colors
[] = {0x173f4f, fg
, 0x68cbc0 | FN_B
};
203 int r
= pad_rows() - 1;
205 pad_put('T', r
, c
++, fg
| FN_B
, bg
);
206 pad_put('A', r
, c
++, fg
| FN_B
, bg
);
207 pad_put('G', r
, c
++, fg
| FN_B
, bg
);
208 pad_put('S', r
, c
++, fg
| FN_B
, bg
);
209 pad_put(':', r
, c
++, fg
| FN_B
, bg
);
210 pad_put(' ', r
, c
++, fg
| FN_B
, bg
);
211 for (i
= 0; i
< NTAGS
&& c
+ 2 < pad_cols(); i
++) {
215 if (TERMOPEN(aterm(i
)))
217 pad_put(i
== ctag
? '(' : ' ', r
, c
++, fg
, bg
);
219 pad_put(tags
[i
], r
, c
++, !nt
? bg
: colors
[nt
], colors
[0]);
221 pad_put(tags
[i
], r
, c
++, colors
[nt
], bg
);
222 pad_put(i
== ctag
? ')' : ' ', r
, c
++, fg
, bg
);
224 for (; c
< pad_cols(); c
++)
225 pad_put(' ', r
, c
, fg
, bg
);
228 static void directkey(void)
230 char *shell
[32] = SHELL
;
231 char *mail
[32] = MAIL
;
232 char *editor
[32] = EDITOR
;
234 if (PASS
&& locked
) {
236 pass
[passlen
] = '\0';
237 if (!strcmp(PASS
, pass
))
242 if (isprint(c
) && passlen
+ 1 < sizeof(pass
))
247 switch ((c
= readchar())) {
262 t_set(aterm(cterm()));
271 if (nterm() != cterm())
278 term_screenshot(SCRSHOT
);
284 if (!term_colors(CLRFILE
))
292 taglock
= 1 - taglock
;
295 term_scrl(pad_rows() / 2);
298 term_scrl(-pad_rows() / 2);
301 t_split(split
[ctag
] == 1 ? 2 : 1);
307 if (strchr(tags
, c
)) {
308 t_set(tterm(strchr(tags
, c
) - tags
));
315 if (c
!= -1 && tmain())
319 static void peepterm(int termid
)
321 int visible
= !hidden
&& ctag
== (termid
% NTAGS
) && split
[ctag
];
322 if (termid
!= cterm())
323 t_hideshow(cterm(), 0, termid
, visible
);
326 static void peepback(int termid
)
328 if (termid
!= cterm())
329 t_hideshow(termid
, 0, cterm(), !hidden
);
332 static int pollterms(void)
334 struct pollfd ufds
[NTERMS
+ 1];
335 int term_idx
[NTERMS
+ 1];
339 ufds
[0].events
= POLLIN
;
340 for (i
= 0; i
< NTERMS
; i
++) {
342 ufds
[n
].fd
= term_fd(terms
[i
]);
343 ufds
[n
].events
= POLLIN
;
347 if (poll(ufds
, n
, 1000) < 1)
349 if (ufds
[0].revents
& (POLLFLAGS
& ~POLLIN
))
351 if (ufds
[0].revents
& POLLIN
)
353 for (i
= 1; i
< n
; i
++) {
354 if (!(ufds
[i
].revents
& POLLFLAGS
))
356 peepterm(term_idx
[i
]);
357 if (ufds
[i
].revents
& POLLIN
) {
360 scr_free(term_idx
[i
]);
365 peepback(term_idx
[i
]);
370 static void mainloop(char **args
)
372 struct termios oldtermios
, termios
;
373 tcgetattr(0, &termios
);
374 oldtermios
= termios
;
376 tcsetattr(0, TCSAFLUSH
, &termios
);
377 term_load(terms
[cterm()], 1);
386 tcsetattr(0, 0, &oldtermios
);
389 static void signalreceived(int n
)
397 ioctl(0, VT_RELDISP
, 1);
402 if (t_show(cterm(), 2) == 3 && split
[ctag
]) {
403 t_hideshow(cterm(), 0, aterm(cterm()), 3);
404 t_hideshow(aterm(cterm()), 0, cterm(), 1);
408 while (waitpid(-1, NULL
, WNOHANG
) > 0)
414 static void signalsetup(void)
417 vtm
.mode
= VT_PROCESS
;
419 vtm
.relsig
= SIGUSR1
;
420 vtm
.acqsig
= SIGUSR2
;
422 signal(SIGUSR1
, signalreceived
);
423 signal(SIGUSR2
, signalreceived
);
424 signal(SIGCHLD
, signalreceived
);
425 ioctl(0, VT_SETMODE
, &vtm
);
428 int main(int argc
, char **argv
)
430 char *hide
= "\x1b[2J\x1b[H\x1b[?25l";
431 char *show
= "\x1b[?25h";
432 char **args
= argv
+ 1;
434 if (fb_init(getenv("FBDEV"))) {
435 fprintf(stderr
, "fbpad: failed to initialize the framebuffer\n");
439 fprintf(stderr
, "fbpad: cannot find fonts\n");
442 for (i
= 0; i
< NTERMS
; i
++)
443 terms
[i
] = term_make();
444 write(1, hide
, strlen(hide
));
446 fcntl(0, F_SETFL
, fcntl(0, F_GETFL
) | O_NONBLOCK
);
447 while (args
[0] && args
[0][0] == '-')
449 mainloop(args
[0] ? args
: NULL
);
450 write(1, show
, strlen(show
));
451 for (i
= 0; i
< NTERMS
; i
++)