Skip to content

Commit 26ac687

Browse files
committed
Add TUI paneling example
1 parent bf03b2e commit 26ac687

File tree

3 files changed

+179
-1
lines changed

3 files changed

+179
-1
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,14 @@ include libc/testlib/testlib.mk
137137
include tool/viz/lib/vizlib.mk
138138
include third_party/lua/lua.mk
139139
include third_party/quickjs/quickjs.mk
140-
include examples/examples.mk
141140
include third_party/lz4cli/lz4cli.mk
142141
include tool/build/lib/buildlib.mk
143142
include third_party/chibicc/chibicc.mk
144143
include third_party/chibicc/test/test.mk
145144
include tool/build/emucrt/emucrt.mk
146145
include tool/build/emubin/emubin.mk
147146
include tool/build/build.mk
147+
include examples/examples.mk
148148
include tool/decode/lib/decodelib.mk
149149
include tool/decode/decode.mk
150150
include tool/hash/hash.mk

examples/examples.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ EXAMPLES_DIRECTDEPS = \
7474
THIRD_PARTY_STB \
7575
THIRD_PARTY_XED \
7676
THIRD_PARTY_ZLIB \
77+
TOOL_BUILD_LIB \
7778
TOOL_VIZ_LIB
7879

7980
EXAMPLES_DEPS := \

examples/panels.c

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#if 0
2+
/*─────────────────────────────────────────────────────────────────╗
3+
│ To the extent possible under law, Justine Tunney has waived │
4+
│ all copyright and related or neighboring rights to this file, │
5+
│ as it is written in the following disclaimers: │
6+
│ • http://unlicense.org/ │
7+
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
8+
╚─────────────────────────────────────────────────────────────────*/
9+
#endif
10+
#include "libc/calls/calls.h"
11+
#include "libc/calls/ioctl.h"
12+
#include "libc/calls/struct/sigaction.h"
13+
#include "libc/calls/struct/termios.h"
14+
#include "libc/calls/struct/winsize.h"
15+
#include "libc/log/check.h"
16+
#include "libc/log/gdb.h"
17+
#include "libc/log/log.h"
18+
#include "libc/macros.internal.h"
19+
#include "libc/runtime/gc.internal.h"
20+
#include "libc/runtime/runtime.h"
21+
#include "libc/str/str.h"
22+
#include "libc/sysv/consts/sa.h"
23+
#include "libc/sysv/consts/sig.h"
24+
#include "libc/sysv/consts/termios.h"
25+
#include "libc/x/x.h"
26+
#include "tool/build/lib/panel.h"
27+
28+
/**
29+
* @fileoverview Cosmopolitan Paneling Demo.
30+
*
31+
* This is useful for creating terminal user interfaces. We take the
32+
* simplest approach possible. The main thing we abstract is like,
33+
* truncating the lines that overflow within a panel. In order to do
34+
* that, we abstract the ANSI parsing and the implementation is able to
35+
* tell how many cells wide each UNICODE character is.
36+
*
37+
* There are smarter ways for Cosmopolitan to do this. For example, it'd
38+
* be great to have automatic flex boxes. It'd also be nice to be able
39+
* to use dynamic programming for low bandwidth display updates, like
40+
* Emacs does, but that's less of an issue these days and can actually
41+
* make things slower, since for heavy workloads like printing videos,
42+
* having ANSI codes bouncing around the display actually goes slower.
43+
*
44+
* Beyond basic paneling, a message box widget is also provided, which
45+
* makes it easier to do modal dialogs.
46+
*/
47+
48+
struct Panels {
49+
union {
50+
struct {
51+
struct Panel left;
52+
struct Panel right;
53+
};
54+
struct Panel p[2];
55+
};
56+
} pan;
57+
58+
long tyn;
59+
long txn;
60+
char key[8];
61+
bool shutdown;
62+
bool invalidated;
63+
struct termios oldterm;
64+
65+
void OnShutdown(int sig) {
66+
shutdown = true;
67+
}
68+
69+
void OnInvalidate(int sig) {
70+
invalidated = true;
71+
}
72+
73+
void GetTtySize(void) {
74+
struct winsize wsize;
75+
wsize.ws_row = tyn;
76+
wsize.ws_col = txn;
77+
getttysize(1, &wsize);
78+
tyn = wsize.ws_row;
79+
txn = wsize.ws_col;
80+
}
81+
82+
int Write(const char *s) {
83+
return write(1, s, strlen(s));
84+
}
85+
86+
void Setup(void) {
87+
CHECK_NE(-1, ioctl(1, TCGETS, &oldterm));
88+
}
89+
90+
void Enter(void) {
91+
struct termios term;
92+
memcpy(&term, &oldterm, sizeof(term));
93+
term.c_cc[VMIN] = 1;
94+
term.c_cc[VTIME] = 1;
95+
term.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON);
96+
term.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL);
97+
term.c_cflag &= ~(CSIZE | PARENB);
98+
term.c_cflag |= CS8;
99+
term.c_iflag |= IUTF8;
100+
CHECK_NE(-1, ioctl(1, TCSETS, &term));
101+
Write("\e[?25l");
102+
}
103+
104+
void Leave(void) {
105+
Write(gc(xasprintf("\e[?25h\e[%d;%dH\e[S\r\n", tyn, txn)));
106+
ioctl(1, TCSETS, &oldterm);
107+
}
108+
109+
void Clear(void) {
110+
long i, j;
111+
for (i = 0; i < ARRAYLEN(pan.p); ++i) {
112+
for (j = 0; j < pan.p[i].n; ++j) {
113+
free(pan.p[i].lines[j].p);
114+
}
115+
free(pan.p[i].lines);
116+
pan.p[i].lines = 0;
117+
pan.p[i].n = 0;
118+
}
119+
}
120+
121+
void Layout(void) {
122+
long i, j;
123+
i = txn >> 1;
124+
pan.left.top = 0;
125+
pan.left.left = 0;
126+
pan.left.bottom = tyn;
127+
pan.left.right = i;
128+
pan.right.top = 0;
129+
pan.right.left = i + 1;
130+
pan.right.bottom = tyn;
131+
pan.right.right = txn;
132+
pan.left.n = pan.left.bottom - pan.left.top;
133+
pan.left.lines = xcalloc(pan.left.n, sizeof(*pan.left.lines));
134+
pan.right.n = pan.right.bottom - pan.right.top;
135+
pan.right.lines = xcalloc(pan.right.n, sizeof(*pan.right.lines));
136+
}
137+
138+
void Append(struct Panel *p, int i, const char *s) {
139+
if (i >= p->n) return;
140+
AppendStr(p->lines + i, s);
141+
}
142+
143+
void Draw(void) {
144+
Append(&pan.left, 0, gc(xasprintf("you typed %`'s", key)));
145+
Append(&pan.left, ((tyn + 1) >> 1) + 0, "hello left 1 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷");
146+
Append(&pan.left, ((tyn + 1) >> 1) + 1, "hello left 2 (╯°□°)╯");
147+
Append(&pan.right, ((tyn + 1) >> 1) + 0, "hello right 1");
148+
Append(&pan.right, ((tyn + 1) >> 1) + 1, "hello right 2");
149+
PrintPanels(1, ARRAYLEN(pan.p), pan.p, tyn, txn);
150+
}
151+
152+
int main(int argc, char *argv[]) {
153+
struct sigaction sa[2] = {
154+
{.sa_handler = OnShutdown},
155+
{.sa_handler = OnInvalidate, .sa_flags = SA_RESTART},
156+
};
157+
showcrashreports();
158+
Setup();
159+
Enter();
160+
GetTtySize();
161+
sigaction(SIGINT, &sa[0], 0);
162+
sigaction(SIGCONT, &sa[1], 0);
163+
sigaction(SIGWINCH, &sa[1], 0);
164+
atexit(Leave);
165+
do {
166+
Clear();
167+
Layout();
168+
Draw();
169+
if (invalidated) {
170+
Enter();
171+
GetTtySize();
172+
invalidated = false;
173+
} else {
174+
readansi(0, key, sizeof(key));
175+
}
176+
} while (!shutdown);
177+
}

0 commit comments

Comments
 (0)