|
| 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