2 * FBPAD FRAMEBUFFER VIRTUAL TERMINAL
4 * Copyright (C) 2009-2021 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.
25 #include <sys/ioctl.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 split
[NTAGS
]; /* terms are shown together */
45 static int ctag
; /* current tag */
46 static int ltag
; /* the last tag */
48 static int hidden
; /* do not touch the framebuffer */
50 static int taglock
; /* disable tag switching */
51 static char pass
[1024];
53 static int cmdmode
; /* execute a command and exit */
55 static int readchar(void)
58 if (read(0, &b
, 1) > 0)
59 return (unsigned char) b
;
63 /* the current terminal */
64 static int cterm(void)
66 return tops
[ctag
] * NTAGS
+ ctag
;
69 /* the other terminal in the same tag */
70 static int aterm(int n
)
72 return n
< NTAGS
? n
+ NTAGS
: n
- NTAGS
;
75 /* the next terminal */
76 static int nterm(void)
78 int n
= (cterm() + 1) % NTERMS
;
79 while (n
!= cterm()) {
87 static struct term
*fterm_main(void)
89 return TERMOPEN(cterm()) ? &terms
[cterm()] : NULL
;
93 #define BRCLR 0xff0000
95 static void fterm_conf(int idx
)
97 int h1
= fb_rows() / 2 / pad_crows() * pad_crows();
98 int h2
= fb_rows() - h1
- 4 * BRWID
;
99 int w1
= fb_cols() / 2 / pad_ccols() * pad_ccols();
100 int w2
= fb_cols() - w1
- 4 * BRWID
;
101 int tag
= idx
% NTAGS
;
102 int top
= idx
< NTAGS
;
104 pad_conf(0, 0, fb_rows(), fb_cols());
106 pad_conf(top
? BRWID
: h1
+ 3 * BRWID
, BRWID
,
107 top
? h1
: h2
, fb_cols() - 2 * BRWID
);
109 pad_conf(BRWID
, top
? BRWID
: w1
+ 3 * BRWID
,
110 fb_rows() - 2 * BRWID
, top
? w1
: w2
);
113 static void fterm_switch(int oidx
, int nidx
, int show
, int save
, int load
)
115 int otag
= oidx
% NTAGS
;
116 int ntag
= nidx
% NTAGS
;
117 int bothvisible
= otag
== ntag
&& split
[otag
];
118 if (save
&& TERMOPEN(oidx
) && TERMSNAP(oidx
) && !bothvisible
)
119 scr_snap(split
[otag
] ? otag
: oidx
);
120 term_save(&terms
[oidx
]);
121 if (show
&& split
[otag
] && otag
== ntag
)
122 pad_border(0, BRWID
);
124 term_load(&terms
[nidx
], show
);
126 term_redraw(load
&& (load
< 0 || !TERMOPEN(nidx
) || !TERMSNAP(nidx
) ||
127 (!bothvisible
&& scr_load(split
[ntag
] ? ntag
: nidx
))));
128 if (show
&& split
[ntag
])
129 pad_border(BRCLR
, BRWID
);
132 static void fterm_show(int n
)
134 if (cterm() == n
|| cmdmode
)
136 if (taglock
&& ctag
!= n
% NTAGS
)
138 if (ctag
!= n
% NTAGS
)
140 if (ctag
== n
% NTAGS
) {
141 if (split
[n
% NTAGS
])
142 fterm_switch(cterm(), n
, !hidden
, 0, 0);
144 fterm_switch(cterm(), n
, !hidden
, !hidden
, !hidden
);
146 fterm_switch(cterm(), n
, !hidden
, !hidden
, !hidden
);
147 if (split
[n
% NTAGS
]) {
148 fterm_switch(n
, aterm(n
), !hidden
, 0, !hidden
);
149 fterm_switch(aterm(n
), n
, !hidden
, 0, 0);
153 tops
[ctag
] = n
/ NTAGS
;
156 static void tag_split(int n
)
159 fterm_switch(cterm(), aterm(cterm()), !hidden
, 0, -!hidden
);
160 fterm_switch(aterm(cterm()), cterm(), !hidden
, !hidden
, -!hidden
);
163 static void tag_show(int n
)
165 fterm_show(tops
[n
] * NTAGS
+ n
);
168 static void fterm_exec(char **args
)
174 static void tag_list(void)
176 /* colors for tags based on their number of terminals */
177 int fg
= 0x96cb5c, bg
= 0x516f7b;
178 int colors
[] = {0x173f4f, fg
, 0x68cbc0 | FN_B
};
180 int r
= pad_rows() - 1;
182 pad_put('T', r
, c
++, fg
| FN_B
, bg
);
183 pad_put('A', r
, c
++, fg
| FN_B
, bg
);
184 pad_put('G', r
, c
++, fg
| FN_B
, bg
);
185 pad_put('S', r
, c
++, fg
| FN_B
, bg
);
186 pad_put(':', r
, c
++, fg
| FN_B
, bg
);
187 pad_put(' ', r
, c
++, fg
| FN_B
, bg
);
188 for (i
= 0; i
< NTAGS
&& c
+ 2 < pad_cols(); i
++) {
192 if (TERMOPEN(aterm(i
)))
194 pad_put(i
== ctag
? '(' : ' ', r
, c
++, fg
, bg
);
196 pad_put(tags
[i
], r
, c
++, !nt
? bg
: colors
[nt
], colors
[0]);
198 pad_put(tags
[i
], r
, c
++, colors
[nt
], bg
);
199 pad_put(i
== ctag
? ')' : ' ', r
, c
++, fg
, bg
);
201 for (; c
< pad_cols(); c
++)
202 pad_put(' ', r
, c
, fg
, bg
);
205 static void directkey(void)
207 char *shell
[32] = SHELL
;
208 char *mail
[32] = MAIL
;
209 char *editor
[32] = EDITOR
;
211 if (PASS
&& locked
) {
213 pass
[passlen
] = '\0';
214 if (!strcmp(PASS
, pass
))
219 if (isprint(c
) && passlen
+ 1 < sizeof(pass
))
224 switch ((c
= readchar())) {
236 fterm_show(aterm(cterm()));
245 if (nterm() != cterm())
262 taglock
= 1 - taglock
;
265 term_scrl(pad_rows() / 2);
268 term_scrl(-pad_rows() / 2);
271 tag_split(split
[ctag
] == 1 ? 2 : 1);
277 if (strchr(tags
, c
)) {
278 tag_show(strchr(tags
, c
) - tags
);
285 if (c
!= -1 && fterm_main())
289 static void peepterm(int termid
)
291 int visible
= !hidden
&& ctag
== (termid
% NTAGS
) && split
[ctag
];
292 if (termid
!= cterm())
293 fterm_switch(cterm(), termid
, visible
, 0, 0);
296 static void peepback(int termid
)
298 if (termid
!= cterm())
299 fterm_switch(termid
, cterm(), !hidden
, 0, 0);
302 static int pollterms(void)
304 struct pollfd ufds
[NTERMS
+ 1];
305 int term_idx
[NTERMS
+ 1];
309 ufds
[0].events
= POLLIN
;
310 for (i
= 0; i
< NTERMS
; i
++) {
312 ufds
[n
].fd
= terms
[i
].fd
;
313 ufds
[n
].events
= POLLIN
;
317 if (poll(ufds
, n
, 1000) < 1)
319 if (ufds
[0].revents
& (POLLFLAGS
& ~POLLIN
))
321 if (ufds
[0].revents
& POLLIN
)
323 for (i
= 1; i
< n
; i
++) {
324 if (!(ufds
[i
].revents
& POLLFLAGS
))
326 peepterm(term_idx
[i
]);
327 if (ufds
[i
].revents
& POLLIN
) {
330 scr_free(term_idx
[i
]);
335 peepback(term_idx
[i
]);
340 static void mainloop(char **args
)
342 struct termios oldtermios
, termios
;
343 tcgetattr(0, &termios
);
344 oldtermios
= termios
;
346 tcsetattr(0, TCSAFLUSH
, &termios
);
347 term_load(&terms
[cterm()], 1);
356 tcsetattr(0, 0, &oldtermios
);
359 static void signalreceived(int n
)
366 fterm_switch(cterm(), cterm(), 0, 1, 0);
367 ioctl(0, VT_RELDISP
, 1);
372 fterm_switch(cterm(), cterm(), 1, 0, 1);
375 while (waitpid(-1, NULL
, WNOHANG
) > 0)
381 static void signalsetup(void)
384 vtm
.mode
= VT_PROCESS
;
386 vtm
.relsig
= SIGUSR1
;
387 vtm
.acqsig
= SIGUSR2
;
389 signal(SIGUSR1
, signalreceived
);
390 signal(SIGUSR2
, signalreceived
);
391 signal(SIGCHLD
, signalreceived
);
392 ioctl(0, VT_SETMODE
, &vtm
);
395 int main(int argc
, char **argv
)
397 char *hide
= "\x1b[2J\x1b[H\x1b[?25l";
398 char *show
= "\x1b[?25h";
399 char **args
= argv
+ 1;
400 if (fb_init(FBDEV
)) {
401 fprintf(stderr
, "fbpad: failed to initialize the framebuffer\n");
405 fprintf(stderr
, "fbpad: cannot find fonts\n");
408 write(1, hide
, strlen(hide
));
410 fcntl(0, F_SETFL
, fcntl(0, F_GETFL
) | O_NONBLOCK
);
411 while (args
[0] && args
[0][0] == '-')
413 mainloop(args
[0] ? args
: NULL
);
414 write(1, show
, strlen(show
));