diff --git a/termbox2.h b/termbox2.h index 4864940..39cfb1f 100644 --- a/termbox2.h +++ b/termbox2.h @@ -23,8 +23,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef __TERMBOX_H -#define __TERMBOX_H +#ifndef TERMBOX_H_INCL +#define TERMBOX_H_INCL #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE @@ -51,6 +51,7 @@ SOFTWARE. #include #include #include +#include #ifdef PATH_MAX #define TB_PATH_MAX PATH_MAX @@ -90,7 +91,7 @@ extern "C" { */ #if defined(TB_LIB_OPTS) || 0 // __tb_lib_opts -// Ensure consistent compile-time options when using as a shared library +/* Ensure consistent compile-time options when using as a shared library */ #undef TB_OPT_ATTR_W #undef TB_OPT_EGC #undef TB_OPT_PRINTF_BUF @@ -99,22 +100,22 @@ extern "C" { #define TB_OPT_EGC #endif -// Ensure sane TB_OPT_ATTR_W (16, 32, or 64) +/* Ensure sane `TB_OPT_ATTR_W` (16, 32, or 64) */ #if defined TB_OPT_ATTR_W && TB_OPT_ATTR_W == 16 #elif defined TB_OPT_ATTR_W && TB_OPT_ATTR_W == 32 #elif defined TB_OPT_ATTR_W && TB_OPT_ATTR_W == 64 #else #undef TB_OPT_ATTR_W -#if defined TB_OPT_TRUECOLOR // Back-compat for old flag +#if defined TB_OPT_TRUECOLOR // Deprecated. Back-compat for old flag. #define TB_OPT_ATTR_W 32 #else #define TB_OPT_ATTR_W 16 #endif #endif -/* ASCII key constants (tb_event.key) */ +/* ASCII key constants (`tb_event.key`) */ #define TB_KEY_CTRL_TILDE 0x00 -#define TB_KEY_CTRL_2 0x00 /* clash with 'CTRL_TILDE' */ +#define TB_KEY_CTRL_2 0x00 // clash with `CTRL_TILDE` #define TB_KEY_CTRL_A 0x01 #define TB_KEY_CTRL_B 0x02 #define TB_KEY_CTRL_C 0x03 @@ -123,14 +124,14 @@ extern "C" { #define TB_KEY_CTRL_F 0x06 #define TB_KEY_CTRL_G 0x07 #define TB_KEY_BACKSPACE 0x08 -#define TB_KEY_CTRL_H 0x08 /* clash with 'CTRL_BACKSPACE' */ +#define TB_KEY_CTRL_H 0x08 // clash with `CTRL_BACKSPACE` #define TB_KEY_TAB 0x09 -#define TB_KEY_CTRL_I 0x09 /* clash with 'TAB' */ +#define TB_KEY_CTRL_I 0x09 // clash with `TAB` #define TB_KEY_CTRL_J 0x0a #define TB_KEY_CTRL_K 0x0b #define TB_KEY_CTRL_L 0x0c #define TB_KEY_ENTER 0x0d -#define TB_KEY_CTRL_M 0x0d /* clash with 'ENTER' */ +#define TB_KEY_CTRL_M 0x0d // clash with `ENTER` #define TB_KEY_CTRL_N 0x0e #define TB_KEY_CTRL_O 0x0f #define TB_KEY_CTRL_P 0x10 @@ -145,24 +146,24 @@ extern "C" { #define TB_KEY_CTRL_Y 0x19 #define TB_KEY_CTRL_Z 0x1a #define TB_KEY_ESC 0x1b -#define TB_KEY_CTRL_LSQ_BRACKET 0x1b /* clash with 'ESC' */ -#define TB_KEY_CTRL_3 0x1b /* clash with 'ESC' */ +#define TB_KEY_CTRL_LSQ_BRACKET 0x1b // clash with 'ESC' +#define TB_KEY_CTRL_3 0x1b // clash with 'ESC' #define TB_KEY_CTRL_4 0x1c -#define TB_KEY_CTRL_BACKSLASH 0x1c /* clash with 'CTRL_4' */ +#define TB_KEY_CTRL_BACKSLASH 0x1c // clash with 'CTRL_4' #define TB_KEY_CTRL_5 0x1d -#define TB_KEY_CTRL_RSQ_BRACKET 0x1d /* clash with 'CTRL_5' */ +#define TB_KEY_CTRL_RSQ_BRACKET 0x1d // clash with 'CTRL_5' #define TB_KEY_CTRL_6 0x1e #define TB_KEY_CTRL_7 0x1f -#define TB_KEY_CTRL_SLASH 0x1f /* clash with 'CTRL_7' */ -#define TB_KEY_CTRL_UNDERSCORE 0x1f /* clash with 'CTRL_7' */ +#define TB_KEY_CTRL_SLASH 0x1f // clash with 'CTRL_7' +#define TB_KEY_CTRL_UNDERSCORE 0x1f // clash with 'CTRL_7' #define TB_KEY_SPACE 0x20 #define TB_KEY_BACKSPACE2 0x7f -#define TB_KEY_CTRL_8 0x7f /* clash with 'BACKSPACE2' */ +#define TB_KEY_CTRL_8 0x7f // clash with 'BACKSPACE2' #define tb_key_i(i) 0xffff - (i) -/* Terminal-dependent key constants (tb_event.key) and terminfo capabilities */ +/* Terminal-dependent key constants (`tb_event.key`) and terminfo caps */ /* BEGIN codegen h */ -/* Produced by ./codegen.sh on Thu, 13 Jul 2023 05:46:13 +0000 */ +/* Produced by ./codegen.sh on Tue, 03 Sep 2024 04:17:47 +0000 */ #define TB_KEY_F1 (0xffff - 0) #define TB_KEY_F2 (0xffff - 1) #define TB_KEY_F3 (0xffff - 2) @@ -242,7 +243,7 @@ extern "C" { #define TB_HARDCAP_UNDERLINE_2 "\x1b[21m" #define TB_HARDCAP_OVERLINE "\x1b[53m" -/* Colors (numeric) and attributes (bitwise) (tb_cell.fg, tb_cell.bg) */ +/* Colors (numeric) and attributes (bitwise) (`tb_cell.fg`, `tb_cell.bg`) */ #define TB_DEFAULT 0x0000 #define TB_BLACK 0x0001 #define TB_RED 0x0002 @@ -262,8 +263,9 @@ extern "C" { #define TB_HI_BLACK 0x2000 #define TB_BRIGHT 0x4000 #define TB_DIM 0x8000 -#define TB_256_BLACK TB_HI_BLACK // TB_256_BLACK is deprecated -#else // 32 or 64 +#define TB_256_BLACK TB_HI_BLACK // `TB_256_BLACK` is deprecated +#else +// `TB_OPT_ATTR_W` is 32 or 64 #define TB_BOLD 0x01000000 #define TB_UNDERLINE 0x02000000 #define TB_REVERSE 0x04000000 @@ -272,7 +274,7 @@ extern "C" { #define TB_HI_BLACK 0x20000000 #define TB_BRIGHT 0x40000000 #define TB_DIM 0x80000000 -#define TB_TRUECOLOR_BOLD TB_BOLD // TB_TRUECOLOR_* is deprecated +#define TB_TRUECOLOR_BOLD TB_BOLD // `TB_TRUECOLOR_*` is deprecated #define TB_TRUECOLOR_UNDERLINE TB_UNDERLINE #define TB_TRUECOLOR_REVERSE TB_REVERSE #define TB_TRUECOLOR_ITALIC TB_ITALIC @@ -287,24 +289,24 @@ extern "C" { #define TB_INVISIBLE 0x0000000800000000 #endif -/* Event types (tb_event.type) */ +/* Event types (`tb_event.type`) */ #define TB_EVENT_KEY 1 #define TB_EVENT_RESIZE 2 #define TB_EVENT_MOUSE 3 -/* Key modifiers (bitwise) (tb_event.mod) */ +/* Key modifiers (bitwise) (`tb_event.mod`) */ #define TB_MOD_ALT 1 #define TB_MOD_CTRL 2 #define TB_MOD_SHIFT 4 #define TB_MOD_MOTION 8 -/* Input modes (bitwise) (tb_set_input_mode) */ +/* Input modes (bitwise) (`tb_set_input_mode`) */ #define TB_INPUT_CURRENT 0 #define TB_INPUT_ESC 1 #define TB_INPUT_ALT 2 #define TB_INPUT_MOUSE 4 -/* Output modes (tb_set_output_mode) */ +/* Output modes (`tb_set_output_mode`) */ #define TB_OUTPUT_CURRENT 0 #define TB_OUTPUT_NORMAL 1 #define TB_OUTPUT_256 2 @@ -316,9 +318,9 @@ extern "C" { /* Common function return values unless otherwise noted. * - * Library behavior is undefined after receiving TB_ERR_MEM. Callers may - * attempt reinitializing by freeing memory, invoking tb_shutdown, then - * tb_init. + * Library behavior is undefined after receiving `TB_ERR_MEM`. Callers may + * attempt reinitializing by freeing memory, invoking `tb_shutdown`, then + * `tb_init`. */ #define TB_OK 0 #define TB_ERR -1 @@ -347,12 +349,12 @@ extern "C" { #define TB_ERR_SELECT TB_ERR_POLL #define TB_ERR_RESIZE_SELECT TB_ERR_RESIZE_POLL -/* Function types to be used with tb_set_func() */ +/* Deprecated. Function types to be used with `tb_set_func`. */ #define TB_FUNC_EXTRACT_PRE 0 #define TB_FUNC_EXTRACT_POST 1 -/* Define this to set the size of the buffer used in tb_printf() - * and tb_sendf() +/* Define this to set the size of the buffer used in `tb_printf` + * and `tb_sendf` */ #ifndef TB_OPT_PRINTF_BUF #define TB_OPT_PRINTF_BUF 4096 @@ -389,35 +391,39 @@ typedef uint32_t uintattr_t; typedef uint16_t uintattr_t; #endif -/* The terminal screen is represented as 2d array of cells. The structure is - * optimized for dealing with single-width (wcwidth()==1) Unicode codepoints, +/* A cell in a 2d grid representing the terminal screen. + * + * The terminal screen is represented as 2d array of cells. The structure is + * optimized for dealing with single-width (`wcwidth==1`) Unicode codepoints, * however some support for grapheme clusters (e.g., combining diacritical - * marks) and wide codepoints (e.g., Hiragana) is provided through ech, nech, - * cech via tb_set_cell_ex(). ech is only valid when nech>0, otherwise ch is - * used. + * marks) and wide codepoints (e.g., Hiragana) is provided through `ech`, + * `nech`, and `cech` via `tb_set_cell_ex`. `ech` is only valid when `nech>0`, + * otherwise `ch` is used. * - * For non-single-width codepoints, given N=wcwidth(ch)/wcswidth(ech): + * For non-single-width codepoints, given `N=wcwidth(ch)/wcswidth(ech)`: * - * when N==0: termbox forces a single-width cell. Callers should avoid this - * if aiming to render text accurately. + * when `N==0`: termbox forces a single-width cell. Callers should avoid this + * if aiming to render text accurately. Callers may use + * `tb_set_cell_ex` or `tb_print*` to render `N==0` combining + * characters. * - * when N>1: termbox zeroes out the following N-1 cells and skips sending - * them to the tty. So, e.g., if the caller sets x=0,y=0 to an N==2 - * codepoint, the caller's next set should be at x=2,y=0. Anything - * set at x=1,y=0 will be ignored. If there are not enough columns - * remaining on the line to render N width, spaces are sent - * instead. + * when `N>1`: termbox zeroes out the following `N-1` cells and skips sending + * them to the tty. So, e.g., if the caller sets `x=0,y=0` to an + * `N==2` codepoint, the caller's next set should be at `x=2,y=0`. + * Anything set at `x=1,y=0` will be ignored. If there are not + * enough columns remaining on the line to render `N` width, spaces + * are sent instead. * - * See tb_present() for implementation. + * See `tb_present` for implementation. */ struct tb_cell { - uint32_t ch; /* a Unicode codepoint */ - uintattr_t fg; /* bitwise foreground attributes */ - uintattr_t bg; /* bitwise background attributes */ + uint32_t ch; // a Unicode codepoint + uintattr_t fg; // bitwise foreground attributes + uintattr_t bg; // bitwise background attributes #ifdef TB_OPT_EGC - uint32_t *ech; /* a grapheme cluster of Unicode codepoints, 0-terminated */ - size_t nech; /* num elements in ech, 0 means use ch instead of ech */ - size_t cech; /* num elements allocated for ech */ + uint32_t *ech; // a grapheme cluster of Unicode codepoints, 0-terminated + size_t nech; // num elements in ech, 0 means use ch instead of ech + size_t cech; // num elements allocated for ech #endif }; @@ -425,30 +431,29 @@ struct tb_cell { * * Given the event type, the following fields are relevant: * - * when TB_EVENT_KEY: (key XOR ch, one will be zero), mod. Note there is - * overlap between TB_MOD_CTRL and TB_KEY_CTRL_*. - * TB_MOD_CTRL and TB_MOD_SHIFT are only set as - * modifiers to TB_KEY_ARROW_*. + * when `TB_EVENT_KEY`: `key` xor `ch` (one will be zero) and `mod`. Note + * there is overlap between `TB_MOD_CTRL` and + * `TB_KEY_CTRL_*`. `TB_MOD_CTRL` and `TB_MOD_SHIFT` are + * only set as modifiers to `TB_KEY_ARROW_*`. * - * when TB_EVENT_RESIZE: w, h + * when `TB_EVENT_RESIZE`: `w` and `h` * - * when TB_EVENT_MOUSE: key (TB_KEY_MOUSE_*), x, y + * when `TB_EVENT_MOUSE`: `key` (`TB_KEY_MOUSE_*`), `x`, and `y` */ struct tb_event { - uint8_t type; /* one of TB_EVENT_* constants */ - uint8_t mod; /* bitwise TB_MOD_* constants */ - uint16_t key; /* one of TB_KEY_* constants */ - uint32_t ch; /* a Unicode codepoint */ - int32_t w; /* resize width */ - int32_t h; /* resize height */ - int32_t x; /* mouse x */ - int32_t y; /* mouse y */ + uint8_t type; // one of `TB_EVENT_*` constants + uint8_t mod; // bitwise `TB_MOD_*` constants + uint16_t key; // one of `TB_KEY_*` constants + uint32_t ch; // a Unicode codepoint + int32_t w; // resize width + int32_t h; // resize height + int32_t x; // mouse x + int32_t y; // mouse y }; -/* Initializes the termbox library. This function should be called before any - * other functions. tb_init() is equivalent to tb_init_file("/dev/tty"). After - * successful initialization, the library must be finalized using the - * tb_shutdown() function. +/* Initialize the termbox library. This function should be called before any + * other functions. `tb_init` is equivalent to `tb_init_file("/dev/tty")`. After + * successful initialization, the library must be finalized using `tb_shutdown`. */ int tb_init(void); int tb_init_file(const char *path); @@ -456,184 +461,197 @@ int tb_init_fd(int ttyfd); int tb_init_rwfd(int rfd, int wfd); int tb_shutdown(void); -/* Returns the size of the internal back buffer (which is the same as terminal's +/* Return the size of the internal back buffer (which is the same as terminal's * window size in rows and columns). The internal buffer can be resized after - * tb_clear() or tb_present() function calls. Both dimensions have an - * unspecified negative value when called before tb_init() or after - * tb_shutdown(). + * `tb_clear` or `tb_present` calls. Both dimensions have an unspecified + * negative value when called before `tb_init` or after `tb_shutdown`. */ int tb_width(void); int tb_height(void); -/* Clears the internal back buffer using TB_DEFAULT color or the - * color/attributes set by tb_set_clear_attrs() function. +/* Clear the internal back buffer using `TB_DEFAULT` or the attributes set by + * `tb_set_clear_attrs`. */ int tb_clear(void); int tb_set_clear_attrs(uintattr_t fg, uintattr_t bg); -/* Synchronizes the internal back buffer with the terminal by writing to tty. */ +/* Synchronize the internal back buffer with the terminal by writing to tty. */ int tb_present(void); -/* Clears the internal front buffer effectively forcing a complete re-render of +/* Clear the internal front buffer effectively forcing a complete re-render of * the back buffer to the tty. It is not necessary to call this under normal * circumstances. */ int tb_invalidate(void); -/* Sets the position of the cursor. Upper-left character is (0, 0). */ +/* Set the position of the cursor. Upper-left cell is (0, 0). */ int tb_set_cursor(int cx, int cy); int tb_hide_cursor(void); /* Set cell contents in the internal back buffer at the specified position. * - * Use tb_set_cell_ex() for rendering grapheme clusters (e.g., combining + * Use `tb_set_cell_ex` for rendering grapheme clusters (e.g., combining * diacritical marks). * - * Function tb_set_cell(x, y, ch, fg, bg) is equivalent to - * tb_set_cell_ex(x, y, &ch, 1, fg, bg). + * Calling `tb_set_cell(x, y, ch, fg, bg)` is equivalent to + * `tb_set_cell_ex(x, y, &ch, 1, fg, bg)`. + * + * `tb_extend_cell` is a shortcut for appending 1 codepoint to `tb_cell.ech`. * - * Function tb_extend_cell() is a shortcut for appending 1 codepoint to - * cell->ech. + * Non-printable (`iswprint(3)`) codepoints are replaced with `U+FFFD` at render + * time. */ int tb_set_cell(int x, int y, uint32_t ch, uintattr_t fg, uintattr_t bg); int tb_set_cell_ex(int x, int y, uint32_t *ch, size_t nch, uintattr_t fg, uintattr_t bg); int tb_extend_cell(int x, int y, uint32_t ch); -/* Sets the input mode. Termbox has two input modes: +/* Set the input mode. Termbox has two input modes: * - * 1. TB_INPUT_ESC - * When escape (\x1b) is in the buffer and there's no match for an escape - * sequence, a key event for TB_KEY_ESC is returned. + * 1. `TB_INPUT_ESC` + * When escape (`\x1b`) is in the buffer and there's no match for an escape + * sequence, a key event for `TB_KEY_ESC` is returned. * - * 2. TB_INPUT_ALT - * When escape (\x1b) is in the buffer and there's no match for an escape - * sequence, the next keyboard event is returned with a TB_MOD_ALT modifier. + * 2. `TB_INPUT_ALT` + * When escape (`\x1b`) is in the buffer and there's no match for an escape + * sequence, the next keyboard event is returned with a `TB_MOD_ALT` + * modifier. * - * You can also apply TB_INPUT_MOUSE via bitwise OR operation to either of the - * modes (e.g., TB_INPUT_ESC | TB_INPUT_MOUSE) to receive TB_EVENT_MOUSE events. - * If none of the main two modes were set, but the mouse mode was, TB_INPUT_ESC - * mode is used. If for some reason you've decided to use - * (TB_INPUT_ESC | TB_INPUT_ALT) combination, it will behave as if only - * TB_INPUT_ESC was selected. + * You can also apply `TB_INPUT_MOUSE` via bitwise OR operation to either of the + * modes (e.g., `TB_INPUT_ESC | TB_INPUT_MOUSE`) to receive `TB_EVENT_MOUSE` + * events. If none of the main two modes were set, but the mouse mode was, + * `TB_INPUT_ESC` is used. If for some reason you've decided to use + * `TB_INPUT_ESC | TB_INPUT_ALT`, it will behave as if only `TB_INPUT_ESC` was + * selected. * - * If mode is TB_INPUT_CURRENT, the function returns the current input mode. + * If mode is `TB_INPUT_CURRENT`, return the current input mode. * - * The default input mode is TB_INPUT_ESC. + * The default input mode is `TB_INPUT_ESC`. */ int tb_set_input_mode(int mode); -/* Sets the termbox output mode. Termbox has multiple output modes: +/* Set the output mode. Termbox has multiple output modes: * - * 1. TB_OUTPUT_NORMAL => [0..8] + * 1. `TB_OUTPUT_NORMAL` => [0..8] * * This mode provides 8 different colors: - * TB_BLACK, TB_RED, TB_GREEN, TB_YELLOW, - * TB_BLUE, TB_MAGENTA, TB_CYAN, TB_WHITE + * `TB_BLACK`, `TB_RED`, `TB_GREEN`, `TB_YELLOW`, + * `TB_BLUE`, `TB_MAGENTA`, `TB_CYAN`, `TB_WHITE` * - * Plus TB_DEFAULT which skips sending a color code (i.e., uses the + * Plus `TB_DEFAULT` which skips sending a color code (i.e., uses the * terminal's default color). * - * Colors (including TB_DEFAULT) may be bitwise OR'd with attributes: - * TB_BOLD, TB_UNDERLINE, TB_REVERSE, TB_ITALIC, TB_BLINK, TB_BRIGHT, - * TB_DIM + * Colors (including `TB_DEFAULT`) may be bitwise OR'd with attributes: + * `TB_BOLD`, `TB_UNDERLINE`, `TB_REVERSE`, `TB_ITALIC`, `TB_BLINK`, + * `TB_BRIGHT`, `TB_DIM` * * The following style attributes are also available if compiled with - * TB_OPT_ATTR_W set to 64: - * TB_STRIKEOUT, TB_UNDERLINE_2, TB_OVERLINE, TB_INVISIBLE + * `TB_OPT_ATTR_W` set to 64: + * `TB_STRIKEOUT`, `TB_UNDERLINE_2`, `TB_OVERLINE`, `TB_INVISIBLE` * - * As in all modes, the value 0 is interpreted as TB_DEFAULT for + * As in all modes, the value 0 is interpreted as `TB_DEFAULT` for * convenience. * - * Some notes: TB_REVERSE can be applied as either fg or bg attributes for - * the same effect. TB_BRIGHT can be applied to either fg or bg. The rest of - * the attributes apply to fg only and are ignored as bg attributes. + * Some notes: `TB_REVERSE` and `TB_BRIGHT` can be applied as either `fg` or + * `bg` attributes for the same effect. The rest of the attributes apply to + * `fg` only and are ignored as `bg` attributes. * - * Example usage: - * tb_set_cell(x, y, '@', TB_BLACK | TB_BOLD, TB_RED); + * Example usage: `tb_set_cell(x, y, '@', TB_BLACK | TB_BOLD, TB_RED)` * - * 2. TB_OUTPUT_256 => [0..255] + TB_HI_BLACK + * 2. `TB_OUTPUT_256` => [0..255] + `TB_HI_BLACK` * * In this mode you get 256 distinct colors (plus default): - * 0x00 (1): TB_DEFAULT - * TB_HI_BLACK (1): TB_BLACK in TB_OUTPUT_NORMAL - * 0x01..0x07 (7): the next 7 colors as in TB_OUTPUT_NORMAL + * 0x00 (1): `TB_DEFAULT` + * `TB_HI_BLACK` (1): `TB_BLACK` in `TB_OUTPUT_NORMAL` + * 0x01..0x07 (7): the next 7 colors as in `TB_OUTPUT_NORMAL` * 0x08..0x0f (8): bright versions of the above * 0x10..0xe7 (216): 216 different colors * 0xe8..0xff (24): 24 different shades of gray * - * All TB_* style attributes except TB_BRIGHT may be bitwise OR'd as in - * TB_OUTPUT_NORMAL. + * All `TB_*` style attributes except `TB_BRIGHT` may be bitwise OR'd as in + * `TB_OUTPUT_NORMAL`. * - * Note TB_HI_BLACK must be used for black, as 0x00 represents default. + * Note `TB_HI_BLACK` must be used for black, as 0x00 represents default. * - * 3. TB_OUTPUT_216 => [0..216] + * 3. `TB_OUTPUT_216` => [0..216] * - * This mode supports the 216-color range of TB_OUTPUT_256 only, but you + * This mode supports the 216-color range of `TB_OUTPUT_256` only, but you * don't need to provide an offset: - * 0x00 (1): TB_DEFAULT + * 0x00 (1): `TB_DEFAULT` * 0x01..0xd8 (216): 216 different colors * - * 4. TB_OUTPUT_GRAYSCALE => [0..24] + * 4. `TB_OUTPUT_GRAYSCALE` => [0..24] * - * This mode supports the 24-color range of TB_OUTPUT_256 only, but you + * This mode supports the 24-color range of `TB_OUTPUT_256` only, but you * don't need to provide an offset: - * 0x00 (1): TB_DEFAULT + * 0x00 (1): `TB_DEFAULT` * 0x01..0x18 (24): 24 different shades of gray * - * 5. TB_OUTPUT_TRUECOLOR => [0x000000..0xffffff] + TB_HI_BLACK + * 5. `TB_OUTPUT_TRUECOLOR` => [0x000000..0xffffff] + `TB_HI_BLACK` * * This mode provides 24-bit color on supported terminals. The format is * 0xRRGGBB. * - * All TB_* style attributes except TB_BRIGHT may be bitwise OR'd as in - * TB_OUTPUT_NORMAL. + * All `TB_*` style attributes except `TB_BRIGHT` may be bitwise OR'd as in + * `TB_OUTPUT_NORMAL`. * - * Note TB_HI_BLACK must be used for black, as 0x000000 represents default. - * - * If mode is TB_OUTPUT_CURRENT, the function returns the current output mode. - * - * The default output mode is TB_OUTPUT_NORMAL. + * Note `TB_HI_BLACK` must be used for black, as 0x000000 represents default. * * To use the terminal default color (i.e., to not send an escape code), pass - * TB_DEFAULT. For convenience, the value 0 is interpreted as TB_DEFAULT in + * `TB_DEFAULT`. For convenience, the value 0 is interpreted as `TB_DEFAULT` in * all modes. * * Note, cell attributes persist after switching output modes. Any translation - * between, for example, TB_OUTPUT_NORMAL's TB_RED and TB_OUTPUT_TRUECOLOR's - * 0xff0000 must be performed by the caller. Also note that cells previously - * rendered in one mode may persist unchanged until the front buffer is cleared - * (such as after a resize event) at which point it will be re-interpreted and - * flushed according to the current mode. Callers may invoke tb_invalidate if - * it is desirable to immediately re-interpret and flush the entire screen - * according to the current mode. + * between, for example, `TB_OUTPUT_NORMAL`'s `TB_RED` and + * `TB_OUTPUT_TRUECOLOR`'s 0xff0000 must be performed by the caller. Also note + * that cells previously rendered in one mode may persist unchanged until the + * front buffer is cleared (such as after a resize event) at which point it will + * be re-interpreted and flushed according to the current mode. Callers may + * invoke `tb_invalidate` if it is desirable to immediately re-interpret and + * flush the entire screen according to the current mode. * * Note, not all terminals support all output modes, especially beyond - * TB_OUTPUT_NORMAL. There is also no very reliable way to determine color + * `TB_OUTPUT_NORMAL`. There is also no very reliable way to determine color * support dynamically. If portability is desired, callers are recommended to - * use TB_OUTPUT_NORMAL or make output mode end-user configurable. The same + * use `TB_OUTPUT_NORMAL` or make output mode end-user configurable. The same * advice applies to style attributes. + * + * If mode is `TB_OUTPUT_CURRENT`, return the current output mode. + * + * The default output mode is `TB_OUTPUT_NORMAL`. */ int tb_set_output_mode(int mode); -/* Wait for an event up to timeout_ms milliseconds and fill the event structure - * with it. If no event is available within the timeout period, TB_ERR_NO_EVENT - * is returned. On a resize event, the underlying select(2) call may be - * interrupted, yielding a return code of TB_ERR_POLL. In this case, you may - * check errno via tb_last_errno(). If it's EINTR, you can safely ignore that - * and call tb_peek_event() again. +/* Wait for an event up to `timeout_ms` milliseconds and populate `event` with + * it. If no event is available within the timeout period, `TB_ERR_NO_EVENT` + * is returned. On a resize event, the underlying `select(2)` call may be + * interrupted, yielding a return code of `TB_ERR_POLL`. In this case, you may + * check `errno` via `tb_last_errno`. If it's `EINTR`, you may elect to ignore + * that and call `tb_peek_event` again. */ int tb_peek_event(struct tb_event *event, int timeout_ms); -/* Same as tb_peek_event except no timeout. */ +/* Same as `tb_peek_event` except no timeout. */ int tb_poll_event(struct tb_event *event); -/* Internal termbox FDs that can be used with poll() / select(). Must call - * tb_poll_event() / tb_peek_event() if activity is detected. */ +/* Internal termbox fds that can be used with `poll(2)`, `select(2)`, etc. + * externally. Callers must invoke `tb_poll_event` or `tb_peek_event` if + * fds become readable. */ int tb_get_fds(int *ttyfd, int *resizefd); -/* Print and printf functions. Specify param out_w to determine width of printed - * string. Incomplete trailing UTF-8 byte sequences are replaced with U+FFFD. - * For finer control, use tb_set_cell(). +/* Print and printf functions. Specify param `out_w` to determine width of + * printed string. Strings are interpreted as UTF-8. + * + * Non-printable characters (`iswprint(3)`) and truncated UTF-8 byte sequences + * are replaced with U+FFFD. + * + * Newlines (`\n`) are supported with the caveat that `out_w` will return the + * width of the string as if it were on a single line. + * + * If the starting coordinate is out of bounds, `TB_ERR_OUT_OF_BOUNDS` is + * returned. If the starting coordinate is in bounds, but goes out of bounds, + * then the out-of-bounds portions of the string are ignored. + * + * For finer control, use `tb_set_cell`. */ int tb_print(int x, int y, uintattr_t fg, uintattr_t bg, const char *str); int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char *fmt, ...); @@ -646,14 +664,14 @@ int tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w, int tb_send(const char *buf, size_t nbuf); int tb_sendf(const char *fmt, ...); -/* Set custom functions. fn_type is one of TB_FUNC_* constants, fn is a - * compatible function pointer, or NULL to clear. +/* Deprecated. Set custom callbacks. `fn_type` is one of `TB_FUNC_*` constants, + * `fn` is a compatible function pointer, or NULL to clear. * - * TB_FUNC_EXTRACT_PRE: + * `TB_FUNC_EXTRACT_PRE`: * If specified, invoke this function BEFORE termbox tries to extract any * escape sequences from the input buffer. * - * TB_FUNC_EXTRACT_POST: + * `TB_FUNC_EXTRACT_POST`: * If specified, invoke this function AFTER termbox tries (and fails) to * extract any escape sequences from the input buffer. */ @@ -683,17 +701,35 @@ int tb_utf8_unicode_to_char(char *out, uint32_t c); /* Library utility functions */ int tb_last_errno(void); const char *tb_strerror(int err); -struct tb_cell *tb_cell_buffer(void); +struct tb_cell *tb_cell_buffer(void); // Deprecated int tb_has_truecolor(void); int tb_has_egc(void); int tb_attr_width(void); const char *tb_version(void); +/* Deprecation notice! + * + * The following will be removed in version 3.x (ABI version 3): + * + * TB_256_BLACK (use TB_HI_BLACK) + * TB_OPT_TRUECOLOR (use TB_OPT_ATTR_W) + * TB_TRUECOLOR_BOLD (use TB_BOLD) + * TB_TRUECOLOR_UNDERLINE (use TB_UNDERLINE) + * TB_TRUECOLOR_REVERSE (use TB_REVERSE) + * TB_TRUECOLOR_ITALIC (use TB_ITALICe) + * TB_TRUECOLOR_BLINK (use TB_BLINK) + * TB_TRUECOLOR_BLACK (use TB_HI_BLACK) + * tb_cell_buffer + * tb_set_func + * TB_FUNC_EXTRACT_PRE + * TB_FUNC_EXTRACT_POST + */ + #ifdef __cplusplus } #endif -#endif /* __TERMBOX_H */ +#endif // TERMBOX_H_INCL #ifdef TB_IMPL @@ -781,7 +817,7 @@ struct tb_global_t { static struct tb_global_t global = {0}; /* BEGIN codegen c */ -/* Produced by ./codegen.sh on Thu, 13 Jul 2023 05:46:13 +0000 */ +/* Produced by ./codegen.sh on Tue, 03 Sep 2024 04:17:48 +0000 */ static const int16_t terminfo_cap_indexes[] = { 66, // kf1 (TB_CAP_F1) @@ -1528,6 +1564,7 @@ static int cellbuf_init(struct cellbuf_t *c, int w, int h); static int cellbuf_free(struct cellbuf_t *c); static int cellbuf_clear(struct cellbuf_t *c); static int cellbuf_get(struct cellbuf_t *c, int x, int y, struct tb_cell **out); +static int cellbuf_in_bounds(struct cellbuf_t *c, int x, int y); static int cellbuf_resize(struct cellbuf_t *c, int w, int h); static int bytebuf_puts(struct bytebuf_t *b, const char *str); static int bytebuf_nputs(struct bytebuf_t *b, const char *str, size_t nstr); @@ -1541,9 +1578,7 @@ int tb_init(void) { } int tb_init_file(const char *path) { - if (global.initialized) { - return TB_ERR_INIT_ALREADY; - } + if (global.initialized) return TB_ERR_INIT_ALREADY; int ttyfd = open(path, O_RDWR); if (ttyfd < 0) { global.last_errno = errno; @@ -1617,7 +1652,7 @@ int tb_present(void) { int rv; - // TODO Assert global.back.(width,height) == global.front.(width,height) + // TODO: Assert global.back.(width,height) == global.front.(width,height) global.last_x = -1; global.last_y = -1; @@ -1636,18 +1671,17 @@ int tb_present(void) { w = wcswidth((wchar_t *)back->ech, back->nech); else #endif - /* wcwidth() simply returns -1 on overflow of wchar_t */ + // wcwidth simply returns -1 on overflow of wchar_t w = wcwidth((wchar_t)back->ch); } - if (w < 1) { - w = 1; - } + if (w < 1) w = 1; if (cell_cmp(back, front) != 0) { cell_copy(front, back); send_attr(back->fg, back->bg); if (w > 1 && x >= global.front.width - (w - 1)) { + // Not enough room for wide char, send spaces for (i = x; i < global.front.width; i++) { send_char(i, y, ' '); } @@ -1660,12 +1694,20 @@ int tb_present(void) { #endif send_char(x, y, back->ch); } + + // When wcwidth>1, we need to advance the cursor by more + // than 1, thereby skipping some cells. Set these skipped + // cells to an invalid codepoint in the front buffer, so + // that if this cell is later replaced by a wcwidth==1 char, + // we'll get a cell_cmp diff for the skipped cells and + // properly re-render. for (i = 1; i < w; i++) { struct tb_cell *front_wide; + uint32_t invalid = -1; if_err_return(rv, cellbuf_get(&global.front, x + i, y, &front_wide)); if_err_return(rv, - cell_set(front_wide, 0, 1, back->fg, back->bg)); + cell_set(front_wide, &invalid, 1, -1, -1)); } } } @@ -1827,14 +1869,21 @@ int tb_print(int x, int y, uintattr_t fg, uintattr_t bg, const char *str) { int tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w, const char *str) { - int rv; + int rv, w, ix; uint32_t uni; - int w, ix = x; - if (out_w) { - *out_w = 0; + + if_not_init_return(); + + if (!cellbuf_in_bounds(&global.back, x, y)) { + return TB_ERR_OUT_OF_BOUNDS; } + + ix = x; + if (out_w) *out_w = 0; + while (*str) { rv = tb_utf8_char_to_unicode(&uni, str); + if (rv < 0) { uni = 0xfffd; // replace invalid UTF-8 char with U+FFFD str += rv * -1; @@ -1843,18 +1892,32 @@ int tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w, } else { break; // shouldn't get here } + + if (uni == '\n') { // TODO: \r, \t, \v, \f, etc? + x = ix; + y += 1; + continue; + } else if (!iswprint((wint_t)uni)) { + uni = 0xfffd; // replace non-printable with U+FFFD + } + w = wcwidth((wchar_t)uni); - if (w < 0) w = 1; - if (w == 0 && x > ix) { - if_err_return(rv, tb_extend_cell(x - 1, y, uni)); + if (w < 0) { + return TB_ERR; // shouldn't happen if iswprint + } else if (w == 0) { // combining character + if (cellbuf_in_bounds(&global.back, x - 1, y)) { + if_err_return(rv, tb_extend_cell(x - 1, y, uni)); + } } else { - if_err_return(rv, tb_set_cell(x, y, uni, fg, bg)); + if (cellbuf_in_bounds(&global.back, x, y)) { + if_err_return(rv, tb_set_cell(x, y, uni, fg, bg)); + } } + x += w; - if (out_w) { - *out_w += w; - } + if (out_w) *out_w += w; } + return TB_OK; } @@ -2117,7 +2180,7 @@ static int init_cap_trie(void) { // example, att605-pc collides on TB_CAP_F4 and TB_CAP_DELETE.) First cap // in TB_CAP_* index order will win. // - // TODO Reorder TB_CAP_* so more critical caps come first. + // TODO: Reorder TB_CAP_* so more critical caps come first. for (i = 0; i < TB_CAP__COUNT_KEYS; i++) { rv = cap_trie_add(global.caps[i], tb_key_i(i), 0); if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv; @@ -2658,7 +2721,7 @@ static int wait_event(struct tb_event *event, int timeout) { if (resize_has_events) { int ignore = 0; read(global.resize_pipefd[0], &ignore, sizeof(ignore)); - // TODO Harden against errors encountered mid-resize + // TODO: Harden against errors encountered mid-resize if_err_return(rv, update_term_size()); if_err_return(rv, resize_cellbufs()); event->type = TB_EVENT_RESIZE; @@ -2787,7 +2850,7 @@ static int extract_esc_mouse(struct tb_event *event) { enum type { TYPE_VT200 = 0, TYPE_1006, TYPE_1015, TYPE_MAX }; - char *cmp[TYPE_MAX] = {// + const char *cmp[TYPE_MAX] = {// // X10 mouse encoding, the simplest one // \x1b [ M Cb Cx Cy [TYPE_VT200] = "\x1b[M", @@ -3197,13 +3260,10 @@ static int send_cluster(int x, int y, uint32_t *ch, size_t nch) { int i; for (i = 0; i < (int)nch; i++) { uint32_t ch32 = *(ch + i); - int chu8_len; - if (ch32 == 0) { // replace null with space (from termbox 19dbee5) - chu8_len = 1; - chu8[0] = ' '; - } else { - chu8_len = tb_utf8_unicode_to_char(chu8, ch32); + if (!iswprint((wint_t)ch32)) { + ch32 = 0xfffd; // replace non-printable codepoints with U+FFFD } + int chu8_len = tb_utf8_unicode_to_char(chu8, ch32); if_err_return(rv, bytebuf_nputs(&global.out, chu8, (size_t)chu8_len)); } @@ -3214,7 +3274,6 @@ static int convert_num(uint32_t num, char *buf) { int i, l = 0; char ch; do { - /* '0' = 48; 48 + num%10 < 58 < MAX_8bitCHAR */ buf[l++] = (char)('0' + (num % 10)); num /= 10; } while (num); @@ -3333,7 +3392,7 @@ static int cellbuf_clear(struct cellbuf_t *c) { static int cellbuf_get(struct cellbuf_t *c, int x, int y, struct tb_cell **out) { - if (x < 0 || x >= c->width || y < 0 || y >= c->height) { + if (!cellbuf_in_bounds(c, x, y)) { *out = NULL; return TB_ERR_OUT_OF_BOUNDS; } @@ -3341,6 +3400,13 @@ static int cellbuf_get(struct cellbuf_t *c, int x, int y, return TB_OK; } +static int cellbuf_in_bounds(struct cellbuf_t *c, int x, int y) { + if (x < 0 || x >= c->width || y < 0 || y >= c->height) { + return 0; + } + return 1; +} + static int cellbuf_resize(struct cellbuf_t *c, int w, int h) { int rv; @@ -3445,4 +3511,4 @@ static int bytebuf_free(struct bytebuf_t *b) { return TB_OK; } -#endif /* TB_IMPL */ +#endif // TB_IMPL