♻️ Make termbox2 muliplatform with wasm support.#129
♻️ Make termbox2 muliplatform with wasm support.#129cowboyd wants to merge 2 commits intotermbox:masterfrom
Conversation
WASM and Windows do not have termios support and will require much
different ways of interacting with the terminal.
This extracts a "platform " API that termbox can use so that all
platform specific details can be hidden behind a swappable facade.
The tricky part is that we need to preserve these key properties.
1. `termbox2.h` is a single header file that can be included into any
project as a unit. (no platform specific includes)
2. the individual source `.h` files and `.c` files can be worked upon
using standard IDE tools and language servers to check syntax,
navigate, and resolev symbols.
Ultimately, the consumer experience must be unchanged:
`#define TB_IMPL` + `#include <termbox2.h>` in one translation unit.
The assembled header is functionally identical to the original — same
API, same behavior, same single-file usage.
To accomplish this we re-organize the way that the header file is
built. `termbox2.h` is now created entirely from a template called `termbox2.h.in`
New source file layout:
termbox2.h.in — assembly template with BEGIN/END markers
tb_api.h — public API + implementation internals (#ifdef TB_IMPL)
tb_platform.h — platform interface declarations. Each platform
must implement these functions
termbox2.c — core logic
tb_posix.c — POSIX backend (termios, pipes, signals, terminfo)
tb_wasm.c — WASM backend (delegates to __tb_host_* imports)
assemble.awk — assembles termbox2.h from the above
Every platform now as a chance at the very beginning to allocate a
data structure where all its state will be stored. This state is treated as fully opaque by
termbox and is stored at `global.platform`. It is passed as the first
argument to each platform call. For example, in `tb_posix.c` the
struct looks like:
```c
struct tb_posix {
int ttyfd;
int rfd;
int wfd;
int ttyfd_open;
int resize_pipefd[2];
struct termios orig_tios;
int has_orig_tios;
char *terminfo;
size_t nterminfo;
};
```
The .c files include headers directly and compile standalone. This is
so that LSP/editor support (clangd, jump-to-definition, diagnostics)
works out of the box, but local include directives are stripped during assembly.
`make termbox2.h` reassembles the single-header deliverable. The awk script
inlines headers (stripping include guards), injects .c contents (stripping
preambles), and wraps everything in BEGIN/END markers for traceability.
`make terminfo` regenerates terminfo tables directly in tb_api.h, replacing
content between codegen markers.
|
Ack-ing your PR. Haven't had a chance to review yet. |
|
Cool to see it running in a browser. I still haven't had a chance to review this but haven't forgotten. Last I recall, I had played around with some WASM technologies you mentioned a few months ago, and I was walking through the various approaches people brought up in #123. All of that's been flushed out of memory for me so I'll be coming at this with fresh eyes when I get a chance.
Right now this sounds pretty good to me. |
I pretty much stay by my words in #123 (comment) i.e. I want to see only one file changed in WASM support patch - It's great to see approach taken in this PR in a working state. The proof of concept is undoubtedly successful, altough I haven't tested it myself and I'd nack the architecture which have formed here in this patch, because it's a mess to work with compared to how termbox/termbox2 is used to be used in general. |
|
apart from the usability issues of the multi-step build process, the sheer size of the changes (especially additions) also raises eyebrows. the existing code is 4KLOC, after this PR it's more than double. |
|
@rofl0r It's worth noting that the actual termbox2.h file https://github.com/termbox/termbox2/pull/129/changes#diff-4cc2fac8c0b525fa9fe2f1795c919f9a7f42e6e090f4de0e6c345afeedd25cd4 is 1200L vs 1000L before, so the delta is only about +200 Which is because there is an ifdef where both the wasm, and the posix are included, so in terms of your compiled binary it's pretty much the same. But this is definitely a heavier setup for development. I'm not happy about it, but I'm not sure what the better way would be. From the user's perspective, the experience is still just |


Motivation
WASM and Windows do not have termios support and will require much different ways of interacting with the terminal.
This extracts a "platform" API that termbox can use so that all platform specific details can be hidden behind a swappable facade.
The tricky part is that we need to preserve these key properties of termbox2:
termbox2.his a single header file that can be included into any project as a unit. (no platform specific includes).hfiles and.cfiles can be worked upon using standard IDE tools and language servers to check syntax, navigate, and resolve symbols.Ultimately, the consumer experience must be unchanged:
#define TB_IMPL+#include <termbox2.h>in one translation unit. The assembled header is functionally identical to the original — same API, same behavior, same single-file usage.Approach
To accomplish this we re-organize the way that the header file is built.
termbox2.his now created entirely from a template calledtermbox2.h.inNew source file layout:
Every platform now as a chance at the very beginning to allocate a data structure where all its state will be stored. This state is treated as fully opaque by termbox and is stored at
global.platform. It is passed as the first argument to each platform call. For example, intb_posix.cthe struct looks like:The .c files include headers directly and compile standalone. This is so that LSP/editor support (clangd, jump-to-definition, diagnostics) works out of the box, but local include directives are stripped during assembly.
WASM Support
In order to make wasm actually work, the embedder will still need to provide four more functions:
These have to be provided by the JavaScript runtime and will be different depending on whether this is in the browser, or this is in Node, or Deno.
DX
make termbox2.hbuildstermbox2.hfrom all its component parts.make terminforegenerates terminfo tables directly in tb_api.h, replacing content between codegen markers.