чување a661cf7b4e5b0b83ea458becd2b00b7912ce9128
родитељ ebdf20e6e756b87f6d56921e05cf7dcb5efde2a5
Аутор: Страхиња Радић <contact@strahinja.org>
Датум: Fri, 4 Aug 2023 16:47:58 +0200
Update termbox.h; clarify license terms (GPL v3+)
Signed-off-by: Страхиња Радић <contact@strahinja.org>
Diffstat:
измењених датотека: 11, додавања: 622(+), брисања: 345(-)
diff --git a/README b/README
@@ -35,18 +35,13 @@ License
-------
poe - .po file editor
-Copyright (C) 2021, 2022, 2023 Страхиња Радић
+Copyright (C) 2021-2023 Страхиња Радић
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
----
-
-This program includes code from the termbox2 TUI library,
-
- https://github.com/termbox/termbox2
-
-The file LICENSE.termbox applies to the portion of this program which is from
-the termbox2 library. Currently, this includes only the file termbox.h.
+File termbox.h is based on termbox2 TUI library, (c) 2021 termbox developers,
+which is released under the terms of Expat license, see the file
+LICENSE.termbox.
diff --git a/config.def.h b/config.def.h
@@ -1,4 +1,6 @@
-/* See the file LICENSE for copyright and license details. */
+/* This program is licensed under the terms of GNU GPL v3 or (at your option)
+ * any later version.
+ * See the file LICENSE for exact copyright and license details. */
/* Comment out to not create backups */
#define CREATE_BACKUPS
diff --git a/draw.c b/draw.c
@@ -1,4 +1,6 @@
-/* See the file LICENSE for copyright and license details. */
+/* This program is licensed under the terms of GNU GPL v3 or (at your option)
+ * any later version.
+ * See the file LICENSE for exact copyright and license details. */
#define _POSIX_C_SOURCE 200809L
#define _DEFAULT_SOURCE
diff --git a/draw.h b/draw.h
@@ -1,4 +1,6 @@
-/* See the file LICENSE for copyright and license details. */
+/* This program is licensed under the terms of GNU GPL v3 or (at your option)
+ * any later version.
+ * See the file LICENSE for exact copyright and license details. */
#include <stddef.h>
#include <termios.h>
#include <unistd.h>
diff --git a/po.c b/po.c
@@ -1,4 +1,6 @@
-/* See the file LICENSE for copyright and license details. */
+/* This program is licensed under the terms of GNU GPL v3 or (at your option)
+ * any later version.
+ * See the file LICENSE for exact copyright and license details. */
#define _POSIX_C_SOURCE 200809L
#define _DEFAULT_SOURCE
diff --git a/po.h b/po.h
@@ -1,4 +1,6 @@
-/* See the file LICENSE for copyright and license details. */
+/* This program is licensed under the terms of GNU GPL v3 or (at your option)
+ * any later version.
+ * See the file LICENSE for exact copyright and license details. */
#include <stdint.h>
#include <stdlib.h>
diff --git a/poe.1.in b/poe.1.in
@@ -43,13 +43,15 @@ poe \- .po file editor
.SH COPYRIGHT
.
.LP
-poe Copyright \(co 2021, 2022, 2023 Strahinya Radich.
+poe Copyright \(co 2021\-2023 Strahinya Radich.
.br
-This program is licensed under GNU GPL v3 or later. See the file
+This program is licensed under GNU GPL v3 or later.
+See the file
.I LICENSE
-in the poe repository for details. For licensing details about the portion of
-the code taken from the termbox2 library (\fItermbox.h\fR), see the file
-.IR "LICENSE.termbox" .
+in the poe repository for details.
+File \fItermbox.h\fR is based on termbox2 TUI library, (c) 2021 termbox
+developers, which is released under the terms of Expat license, see the file
+.IR LICENSE.termbox .
.
.SH DESCRIPTION
.
diff --git a/poe.c b/poe.c
@@ -1,4 +1,6 @@
-/* See the file LICENSE for copyright and license details. */
+/* This program is licensed under the terms of GNU GPL v3 or (at your option)
+ * any later version.
+ * See the file LICENSE for exact copyright and license details. */
#define _POSIX_C_SOURCE 200809L
#define _DEFAULT_SOURCE
diff --git a/termbox.h b/termbox.h
@@ -1,8 +1,17 @@
+/* This program is licensed under the terms of GNU GPL v3 or (at your option)
+ * any later version.
+ * See the file LICENSE for exact copyright and license details. */
+
/*
+This file is based on the termbox2 TUI library, (c) 2021 termbox developers.
+
+License notice from the original termbox2.h from the termbox2 repository
+follows:
+
MIT License
Copyright (c) 2010-2020 nsf <no.smile.face@gmail.com>
- 2015-2021 Adam Saponara <as@php.net>
+ 2015-2023 Adam Saponara <as@php.net>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -26,30 +35,90 @@ SOFTWARE.
#ifndef __TERMBOX_H
#define __TERMBOX_H
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE
+#endif
+
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <sys/select.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
-#if !TB_OPT_SELECT
-#include <poll.h>
-#else
-#include <sys/select.h>
-#endif
-#include <limits.h>
-#include <signal.h>
#include <termios.h>
#include <unistd.h>
#include <wchar.h>
+#ifdef PATH_MAX
+#define TB_PATH_MAX PATH_MAX
+#else
+#define TB_PATH_MAX 4096
+#endif
+
#ifdef __cplusplus
-extern "C" { // __ffi_strip
+extern "C" {
+#endif
+
+// __ffi_start
+
+#define TB_VERSION_STR "2.4.0-dev"
+
+/* The following compile-time options are supported:
+ *
+ * TB_OPT_ATTR_W: Integer width of fg and bg attributes. Valid values
+ * (assuming system support) are 16, 32, and 64. (See
+ * uintattr_t). 32 or 64 enables output mode
+ * TB_OUTPUT_TRUECOLOR. 64 enables additional style
+ * attributes. (See tb_set_output_mode.) Larger values
+ * consume more memory in exchange for more features.
+ * Defaults to 16.
+ *
+ * TB_OPT_EGC: If set, enable extended grapheme cluster support
+ * (tb_extend_cell, tb_set_cell_ex). Consumes more memory.
+ * Defaults off.
+ *
+ * TB_OPT_PRINTF_BUF: Write buffer size for printf operations. Represents the
+ * largest string that can be sent in one call to tb_print*
+ * and tb_send* functions. Defaults to 4096.
+ *
+ * TB_OPT_READ_BUF: Read buffer size for tty reads. Defaults to 64.
+ *
+ * TB_OPT_TRUECOLOR: Deprecated. Sets TB_OPT_ATTR_W to 32 if not already set.
+ */
+
+#if defined(TB_LIB_OPTS) || 0 // __tb_lib_opts
+// 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
+#undef TB_OPT_READ_BUF
+#define TB_OPT_ATTR_W 64
+#define TB_OPT_EGC
+#endif
+
+// 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
+#define TB_OPT_ATTR_W 32
+#else
+#define TB_OPT_ATTR_W 16
+#endif
#endif
/* ASCII key constants (tb_event.key) */
@@ -102,7 +171,7 @@ extern "C" { // __ffi_strip
#define tb_key_i(i) 0xffff - (i)
/* Terminal-dependent key constants (tb_event.key) and terminfo capabilities */
/* BEGIN codegen h */
-/* Produced by ./codegen.sh on Fri, 18 Feb 2022 13:51:38 +0000 */
+/* Produced by ./codegen.sh on Thu, 13 Jul 2023 05:46:13 +0000 */
#define TB_KEY_F1 (0xffff - 0)
#define TB_KEY_F2 (0xffff - 1)
#define TB_KEY_F3 (0xffff - 2)
@@ -170,12 +239,17 @@ extern "C" { // __ffi_strip
#define TB_CAP_REVERSE 33
#define TB_CAP_ENTER_KEYPAD 34
#define TB_CAP_EXIT_KEYPAD 35
-#define TB_CAP__COUNT 36
+#define TB_CAP_DIM 36
+#define TB_CAP_INVISIBLE 37
+#define TB_CAP__COUNT 38
/* END codegen h */
/* Some hard-coded caps */
#define TB_HARDCAP_ENTER_MOUSE "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h"
#define TB_HARDCAP_EXIT_MOUSE "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l"
+#define TB_HARDCAP_STRIKEOUT "\x1b[9m"
+#define TB_HARDCAP_UNDERLINE_2 "\x1b[21m"
+#define TB_HARDCAP_OVERLINE "\x1b[53m"
/* Colors (numeric) and attributes (bitwise) (tb_cell.fg, tb_cell.bg) */
#define TB_DEFAULT 0x0000
@@ -187,39 +261,74 @@ extern "C" { // __ffi_strip
#define TB_MAGENTA 0x0006
#define TB_CYAN 0x0007
#define TB_WHITE 0x0008
-#define TB_BOLD 0x0100
-#define TB_UNDERLINE 0x0200
-#define TB_REVERSE 0x0400
-#define TB_ITALIC 0x0800
+
+#if TB_OPT_ATTR_W == 16
+#define TB_BOLD 0x0100
+#define TB_UNDERLINE 0x0200
+#define TB_REVERSE 0x0400
+#define TB_ITALIC 0x0800
+#define TB_BLINK 0x1000
+#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_BOLD 0x01000000
+#define TB_UNDERLINE 0x02000000
+#define TB_REVERSE 0x04000000
+#define TB_ITALIC 0x08000000
+#define TB_BLINK 0x10000000
+#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_UNDERLINE TB_UNDERLINE
+#define TB_TRUECOLOR_REVERSE TB_REVERSE
+#define TB_TRUECOLOR_ITALIC TB_ITALIC
+#define TB_TRUECOLOR_BLINK TB_BLINK
+#define TB_TRUECOLOR_BLACK TB_HI_BLACK
+#endif
+
+#if TB_OPT_ATTR_W == 64
+#define TB_STRIKEOUT 0x0000000100000000
+#define TB_UNDERLINE_2 0x0000000200000000
+#define TB_OVERLINE 0x0000000400000000
+#define TB_INVISIBLE 0x0000000800000000
+#endif
/* Event types (tb_event.type) */
-#define TB_EVENT_KEY 1
-#define TB_EVENT_RESIZE 2
-#define TB_EVENT_MOUSE 3
+#define TB_EVENT_KEY 1
+#define TB_EVENT_RESIZE 2
+#define TB_EVENT_MOUSE 3
/* 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
+#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) */
-#define TB_INPUT_CURRENT 0
-#define TB_INPUT_ESC 1
-#define TB_INPUT_ALT 2
-#define TB_INPUT_MOUSE 4
+#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) */
-#define TB_OUTPUT_CURRENT 0
-#define TB_OUTPUT_NORMAL 1
-#define TB_OUTPUT_256 2
-#define TB_OUTPUT_216 3
-#define TB_OUTPUT_GRAYSCALE 4
-#ifdef TB_OPT_TRUECOLOR
+#define TB_OUTPUT_CURRENT 0
+#define TB_OUTPUT_NORMAL 1
+#define TB_OUTPUT_256 2
+#define TB_OUTPUT_216 3
+#define TB_OUTPUT_GRAYSCALE 4
+#if TB_OPT_ATTR_W >= 32
#define TB_OUTPUT_TRUECOLOR 5
#endif
-/* Common function return values unless otherwise noted */
+/* 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.
+ */
#define TB_OK 0
#define TB_ERR -1
#define TB_ERR_NEED_MORE -2
@@ -281,10 +390,12 @@ extern "C" { // __ffi_strip
#define tb_free free
#endif
-#ifdef TB_OPT_TRUECOLOR
+#if TB_OPT_ATTR_W == 64
+typedef uint64_t uintattr_t;
+#elif TB_OPT_ATTR_W == 32
typedef uint32_t uintattr_t;
-#else
-typedef uint16_t uintattr_t; // __ffi_strip
+#else // 16
+typedef uint16_t uintattr_t;
#endif
/* The terminal screen is represented as 2d array of cells. The structure is
@@ -310,8 +421,8 @@ typedef uint16_t uintattr_t; // __ffi_strip
*/
struct tb_cell {
uint32_t ch; /* a Unicode character */
- uintattr_t fg; /* bit-wise foreground attributes */
- uintattr_t bg; /* bit-wise background attributes */
+ uintattr_t fg; /* bitwise foreground attributes */
+ uintattr_t bg; /* bitwise background attributes */
#ifdef TB_OPT_EGC
uint32_t *ech; /* a grapheme cluster of Unicode code points */
size_t nech; /* length in bytes of ech, 0 means use ch instead of ech */
@@ -328,13 +439,13 @@ struct tb_cell {
* 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, h
*
* when TB_EVENT_MOUSE: key (TB_KEY_MOUSE_*), x, y
*/
struct tb_event {
uint8_t type; /* one of TB_EVENT_* constants */
- uint8_t mod; /* bit-wise TB_MOD_* constants */
+ uint8_t mod; /* bitwise TB_MOD_* constants */
uint16_t key; /* one of TB_KEY_* constants */
uint32_t ch; /* a Unicode code point */
int32_t w; /* resize width */
@@ -348,11 +459,11 @@ struct tb_event {
* successful initialization, the library must be finalized using the
* tb_shutdown() function.
*/
-int tb_init();
+int tb_init(void);
int tb_init_file(const char *path);
int tb_init_fd(int ttyfd);
int tb_init_rwfd(int rfd, int wfd);
-int tb_shutdown();
+int tb_shutdown(void);
/* Returns 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
@@ -360,27 +471,37 @@ int tb_shutdown();
* unspecified negative value when called before tb_init() or after
* tb_shutdown().
*/
-int tb_width();
-int tb_height();
+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.
*/
-int tb_clear();
+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. */
-int tb_present();
+int tb_present(void);
+
+/* Clears 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). */
int tb_set_cursor(int cx, int cy);
-int tb_hide_cursor();
+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 diacritical
- * marks). Function 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 code point to cell->ech.
+/* Set cell contents in the internal back buffer at the specified position.
+ *
+ * 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).
+ *
+ * Function tb_extend_cell() is a shortcut for appending 1 code point to
+ * cell->ech.
*/
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,
@@ -388,18 +509,21 @@ int tb_set_cell_ex(int x, int y, uint32_t *ch, size_t nch, uintattr_t fg,
int tb_extend_cell(int x, int y, uint32_t ch);
/* Sets 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.
+ *
* 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). 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.
+ * 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.
*
* If mode is TB_INPUT_CURRENT, the function returns the current input mode.
*
@@ -407,49 +531,102 @@ int tb_extend_cell(int x, int y, uint32_t ch);
*/
int tb_set_input_mode(int mode);
-/* Sets the termbox output mode. Termbox has three output modes:
- * 1. TB_OUTPUT_NORMAL => [1..8]
+/* Sets the termbox output mode. Termbox has multiple output modes:
+ *
+ * 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
- * Colors may be bitwise OR'd with attributes:
- * TB_BOLD, TB_UNDERLINE, TB_REVERSE, TB_ITALIC
+ *
+ * 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
+ *
+ * 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
+ *
+ * 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.
*
* Example usage:
* tb_set_cell(x, y, '@', TB_BLACK | TB_BOLD, TB_RED);
*
- * 2. TB_OUTPUT_256 => [0..255]
- * In this mode you get 256 distinct colors:
- * 0x00 - 0x07: the 8 colors as in TB_OUTPUT_NORMAL
- * 0x08 - 0x0f: TB_<color> | TB_BOLD
- * 0x10 - 0xe7: 216 different colors
- * 0xe8 - 0xff: 24 different shades of grey
+ * 2. TB_OUTPUT_256 => [0..255] + TB_HI_BLACK
*
- * Example usage:
- * tb_set_cell(x, y, '@', 184, 240);
- * tb_set_cell(x, y, '@', 0xb8, 0xf0);
+ * 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
+ * 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.
+ *
+ * Note TB_HI_BLACK must be used for black, as 0x00 represents default.
*
* 3. TB_OUTPUT_216 => [0..216]
- * This mode supports the 3rd range of TB_OUTPUT_256 only, but you don't need
- * to provide an offset.
*
- * 4. TB_OUTPUT_GRAYSCALE => [0..23]
- * This mode supports the 4th range of TB_OUTPUT_256 only, but you dont need
- * to provide an offset.
+ * 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
+ * 0x01..0xd8 (216): 216 different colors
+ *
+ * 4. TB_OUTPUT_GRAYSCALE => [0..24]
+ *
+ * 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
+ * 0x01..0x18 (24): 24 different shades of gray
+ *
+ * 5. TB_OUTPUT_TRUECOLOR => [0x000000..0xffffff] + TB_HI_BLACK
*
- * 5. TB_OUTPUT_TRUECOLOR => [0x000000..0xffffff]
* 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.
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ *
+ * Note, not all terminals support all output modes, especially beyond
+ * 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
+ * advice applies to style attributes.
*/
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 poll(2) call may be
+ * 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.
@@ -494,35 +671,30 @@ int tb_set_func(int fn_type, int (*fn)(struct tb_event *, size_t *));
int tb_utf8_char_length(char c);
int tb_utf8_char_to_unicode(uint32_t *out, const char *c);
int tb_utf8_unicode_to_char(char *out, uint32_t c);
-int tb_last_errno();
+int tb_last_errno(void);
const char *tb_strerror(int err);
-
-struct tb_cell *tb_cell_buffer();
+struct tb_cell *tb_cell_buffer(void);
+int tb_has_truecolor(void);
+int tb_has_egc(void);
+int tb_attr_width(void);
+const char *tb_version(void);
#ifdef __cplusplus
-} // __ffi_strip
+}
#endif
#endif /* __TERMBOX_H */
#ifdef TB_IMPL
-#ifndef TB_OPT_TRUECOLOR
-#define TB_OUTPUT_TRUECOLOR (-1)
-#endif
-
#define if_err_return(rv, expr) \
- if (((rv) = (expr)) != TB_OK) \
- return (rv)
+ if (((rv) = (expr)) != TB_OK) return (rv)
#define if_err_break(rv, expr) \
- if (((rv) = (expr)) != TB_OK) \
- break
+ if (((rv) = (expr)) != TB_OK) break
#define if_ok_return(rv, expr) \
- if (((rv) = (expr)) == TB_OK) \
- return (rv)
+ if (((rv) = (expr)) == TB_OK) return (rv)
#define if_ok_or_need_more_return(rv, expr) \
- if (((rv) = (expr)) == TB_OK || (rv) == TB_ERR_NEED_MORE) \
- return (rv)
+ if (((rv) = (expr)) == TB_OK || (rv) == TB_ERR_NEED_MORE) return (rv)
#define send_literal(rv, a) \
if_err_return((rv), bytebuf_nputs(&global.out, (a), sizeof(a) - 1))
@@ -534,13 +706,11 @@ struct tb_cell *tb_cell_buffer();
#define snprintf_or_return(rv, str, sz, fmt, ...) \
do { \
(rv) = snprintf((str), (sz), (fmt), __VA_ARGS__); \
- if ((rv) < 0 || (rv) >= (int)(sz)) \
- return TB_ERR; \
+ if ((rv) < 0 || (rv) >= (int)(sz)) return TB_ERR; \
} while (0)
#define if_not_init_return() \
- if (!global.initialized) \
- return TB_ERR_NOT_INIT
+ if (!global.initialized) return TB_ERR_NOT_INIT
struct bytebuf_t {
char *buf;
@@ -593,15 +763,15 @@ struct tb_global_t {
int has_orig_tios;
int last_errno;
int initialized;
- int need_resize;
int (*fn_extract_esc_pre)(struct tb_event *, size_t *);
int (*fn_extract_esc_post)(struct tb_event *, size_t *);
+ char errbuf[1024];
};
-struct tb_global_t global = {0};
+static struct tb_global_t global = {0};
/* BEGIN codegen c */
-/* Produced by ./codegen.sh on Fri, 18 Feb 2022 13:51:39 +0000 */
+/* Produced by ./codegen.sh on Thu, 13 Jul 2023 05:46:13 +0000 */
static const int16_t terminfo_cap_indexes[] = {
66, // kf1 (TB_CAP_F1)
@@ -640,6 +810,8 @@ static const int16_t terminfo_cap_indexes[] = {
34, // rev (TB_CAP_REVERSE)
89, // smkx (TB_CAP_ENTER_KEYPAD)
88, // rmkx (TB_CAP_EXIT_KEYPAD)
+ 30, // dim (TB_CAP_DIM)
+ 32, // invis (TB_CAP_INVISIBLE)
};
// xterm
@@ -680,6 +852,8 @@ static const char *xterm_caps[] = {
"\033[7m", // rev (TB_CAP_REVERSE)
"\033[?1h\033=", // smkx (TB_CAP_ENTER_KEYPAD)
"\033[?1l\033>", // rmkx (TB_CAP_EXIT_KEYPAD)
+ "\033[2m", // dim (TB_CAP_DIM)
+ "\033[8m", // invis (TB_CAP_INVISIBLE)
};
// linux
@@ -720,6 +894,8 @@ static const char *linux_caps[] = {
"\033[7m", // rev (TB_CAP_REVERSE)
"", // smkx (TB_CAP_ENTER_KEYPAD)
"", // rmkx (TB_CAP_EXIT_KEYPAD)
+ "\033[2m", // dim (TB_CAP_DIM)
+ "", // invis (TB_CAP_INVISIBLE)
};
// screen
@@ -760,6 +936,8 @@ static const char *screen_caps[] = {
"\033[7m", // rev (TB_CAP_REVERSE)
"\033[?1h\033=", // smkx (TB_CAP_ENTER_KEYPAD)
"\033[?1l\033>", // rmkx (TB_CAP_EXIT_KEYPAD)
+ "\033[2m", // dim (TB_CAP_DIM)
+ "", // invis (TB_CAP_INVISIBLE)
};
// rxvt-256color
@@ -800,46 +978,50 @@ static const char *rxvt_256color_caps[] = {
"\033[7m", // rev (TB_CAP_REVERSE)
"\033=", // smkx (TB_CAP_ENTER_KEYPAD)
"\033>", // rmkx (TB_CAP_EXIT_KEYPAD)
+ "", // dim (TB_CAP_DIM)
+ "", // invis (TB_CAP_INVISIBLE)
};
// rxvt-unicode
static const char *rxvt_unicode_caps[] = {
- "", // kf1 (TB_CAP_F1)
- "", // kf2 (TB_CAP_F2)
- "", // kf3 (TB_CAP_F3)
- "", // kf4 (TB_CAP_F4)
- "", // kf5 (TB_CAP_F5)
- "", // kf6 (TB_CAP_F6)
- "", // kf7 (TB_CAP_F7)
- "", // kf8 (TB_CAP_F8)
- "", // kf9 (TB_CAP_F9)
- "", // kf10 (TB_CAP_F10)
- "", // kf11 (TB_CAP_F11)
- "", // kf12 (TB_CAP_F12)
- "", // kich1 (TB_CAP_INSERT)
- "", // kdch1 (TB_CAP_DELETE)
- "", // khome (TB_CAP_HOME)
- "", // kend (TB_CAP_END)
- "", // kpp (TB_CAP_PGUP)
- "", // knp (TB_CAP_PGDN)
- "", // kcuu1 (TB_CAP_ARROW_UP)
- "", // kcud1 (TB_CAP_ARROW_DOWN)
- "", // kcub1 (TB_CAP_ARROW_LEFT)
- "", // kcuf1 (TB_CAP_ARROW_RIGHT)
- "", // kcbt (TB_CAP_BACK_TAB)
- "", // smcup (TB_CAP_ENTER_CA)
- "", // rmcup (TB_CAP_EXIT_CA)
- "", // cnorm (TB_CAP_SHOW_CURSOR)
- "", // civis (TB_CAP_HIDE_CURSOR)
- "", // clear (TB_CAP_CLEAR_SCREEN)
- "", // sgr0 (TB_CAP_SGR0)
- "", // smul (TB_CAP_UNDERLINE)
- "", // bold (TB_CAP_BOLD)
- "", // blink (TB_CAP_BLINK)
- "", // sitm (TB_CAP_ITALIC)
- "", // rev (TB_CAP_REVERSE)
- "", // smkx (TB_CAP_ENTER_KEYPAD)
- "", // rmkx (TB_CAP_EXIT_KEYPAD)
+ "\033[11~", // kf1 (TB_CAP_F1)
+ "\033[12~", // kf2 (TB_CAP_F2)
+ "\033[13~", // kf3 (TB_CAP_F3)
+ "\033[14~", // kf4 (TB_CAP_F4)
+ "\033[15~", // kf5 (TB_CAP_F5)
+ "\033[17~", // kf6 (TB_CAP_F6)
+ "\033[18~", // kf7 (TB_CAP_F7)
+ "\033[19~", // kf8 (TB_CAP_F8)
+ "\033[20~", // kf9 (TB_CAP_F9)
+ "\033[21~", // kf10 (TB_CAP_F10)
+ "\033[23~", // kf11 (TB_CAP_F11)
+ "\033[24~", // kf12 (TB_CAP_F12)
+ "\033[2~", // kich1 (TB_CAP_INSERT)
+ "\033[3~", // kdch1 (TB_CAP_DELETE)
+ "\033[7~", // khome (TB_CAP_HOME)
+ "\033[8~", // kend (TB_CAP_END)
+ "\033[5~", // kpp (TB_CAP_PGUP)
+ "\033[6~", // knp (TB_CAP_PGDN)
+ "\033[A", // kcuu1 (TB_CAP_ARROW_UP)
+ "\033[B", // kcud1 (TB_CAP_ARROW_DOWN)
+ "\033[D", // kcub1 (TB_CAP_ARROW_LEFT)
+ "\033[C", // kcuf1 (TB_CAP_ARROW_RIGHT)
+ "\033[Z", // kcbt (TB_CAP_BACK_TAB)
+ "\033[?1049h", // smcup (TB_CAP_ENTER_CA)
+ "\033[r\033[?1049l", // rmcup (TB_CAP_EXIT_CA)
+ "\033[?12l\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR)
+ "\033[?25l", // civis (TB_CAP_HIDE_CURSOR)
+ "\033[H\033[2J", // clear (TB_CAP_CLEAR_SCREEN)
+ "\033[m\033(B", // sgr0 (TB_CAP_SGR0)
+ "\033[4m", // smul (TB_CAP_UNDERLINE)
+ "\033[1m", // bold (TB_CAP_BOLD)
+ "\033[5m", // blink (TB_CAP_BLINK)
+ "\033[3m", // sitm (TB_CAP_ITALIC)
+ "\033[7m", // rev (TB_CAP_REVERSE)
+ "\033=", // smkx (TB_CAP_ENTER_KEYPAD)
+ "\033>", // rmkx (TB_CAP_EXIT_KEYPAD)
+ "", // dim (TB_CAP_DIM)
+ "", // invis (TB_CAP_INVISIBLE)
};
// Eterm
@@ -880,6 +1062,8 @@ static const char *eterm_caps[] = {
"\033[7m", // rev (TB_CAP_REVERSE)
"", // smkx (TB_CAP_ENTER_KEYPAD)
"", // rmkx (TB_CAP_EXIT_KEYPAD)
+ "", // dim (TB_CAP_DIM)
+ "", // invis (TB_CAP_INVISIBLE)
};
static struct {
@@ -1284,40 +1468,42 @@ static const unsigned char utf8_length[256] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
static const unsigned char utf8_mask[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
-static int tb_reset();
+static int tb_reset(void);
static int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg,
size_t *out_w, const char *fmt, va_list vl);
-static int init_term_attrs();
-static int init_term_caps();
-static int init_cap_trie();
+static int init_term_attrs(void);
+static int init_term_caps(void);
+static int init_cap_trie(void);
static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod);
static int cap_trie_find(const char *buf, size_t nbuf, struct cap_trie_t **last,
size_t *depth);
static int cap_trie_deinit(struct cap_trie_t *node);
-static int init_resize_handler();
-static int send_init_escape_codes();
-static int send_clear();
-static int update_term_size();
-static int update_term_size_via_esc();
-static int init_cellbuf();
-static int tb_deinit();
-static int load_terminfo();
+static int init_resize_handler(void);
+static int send_init_escape_codes(void);
+static int send_clear(void);
+static int update_term_size(void);
+static int update_term_size_via_esc(void);
+static int init_cellbuf(void);
+static int tb_deinit(void);
+static int load_terminfo(void);
static int load_terminfo_from_path(const char *path, const char *term);
static int read_terminfo_path(const char *path);
-static int parse_terminfo_caps();
-static int load_builtin_caps();
+static int parse_terminfo_caps(void);
+static int load_builtin_caps(void);
static const char *get_terminfo_string(int16_t str_offsets_pos,
- int16_t str_table_pos, int16_t str_table_len, int16_t str_index);
+ int16_t str_offsets_len, int16_t str_table_pos, int16_t str_table_len,
+ int16_t str_index);
static int wait_event(struct tb_event *event, int timeout);
static int extract_event(struct tb_event *event);
static int extract_esc(struct tb_event *event);
static int extract_esc_user(struct tb_event *event, int is_post);
static int extract_esc_cap(struct tb_event *event);
static int extract_esc_mouse(struct tb_event *event);
-static int resize_if_needed();
+static int resize_cellbufs(void);
static void handle_resize(int sig);
static int send_attr(uintattr_t fg, uintattr_t bg);
-static int send_sgr(uintattr_t fg, uintattr_t bg);
+static int send_sgr(uint32_t fg, uint32_t bg, int fg_is_default,
+ int bg_is_default);
static int send_cursor_if(int x, int y);
static int send_char(int x, int y, uint32_t ch);
static int send_cluster(int x, int y, uint32_t *ch, size_t nch);
@@ -1340,7 +1526,7 @@ static int bytebuf_flush(struct bytebuf_t *b, int fd);
static int bytebuf_reserve(struct bytebuf_t *b, size_t sz);
static int bytebuf_free(struct bytebuf_t *b);
-int tb_init() {
+int tb_init(void) {
return tb_init_file("/dev/tty");
}
@@ -1388,26 +1574,24 @@ int tb_init_rwfd(int rfd, int wfd) {
return rv;
}
-int tb_shutdown() {
+int tb_shutdown(void) {
if_not_init_return();
tb_deinit();
return TB_OK;
}
-int tb_width() {
+int tb_width(void) {
if_not_init_return();
return global.width;
}
-int tb_height() {
+int tb_height(void) {
if_not_init_return();
return global.height;
}
-int tb_clear() {
- int rv;
+int tb_clear(void) {
if_not_init_return();
- if_err_return(rv, resize_if_needed());
return cellbuf_clear(&global.back);
}
@@ -1418,11 +1602,10 @@ int tb_set_clear_attrs(uintattr_t fg, uintattr_t bg) {
return TB_OK;
}
-int tb_present() {
+int tb_present(void) {
if_not_init_return();
int rv;
- if_err_return(rv, resize_if_needed());
// TODO Assert global.back.(width,height) == global.front.(width,height)
@@ -1443,7 +1626,8 @@ int tb_present() {
w = wcswidth((wchar_t *)back->ech, back->nech);
else
#endif
- w = wcwidth(back->ch);
+ /* wcwidth() simply returns -1 on overflow of wchar_t */
+ w = wcwidth((wchar_t)back->ch);
}
if (w < 1) {
w = 1;
@@ -1470,7 +1654,8 @@ int tb_present() {
struct tb_cell *front_wide;
if_err_return(rv,
cellbuf_get(&global.front, x + i, y, &front_wide));
- cell_set(front_wide, 0, 1, back->fg, back->bg);
+ if_err_return(rv,
+ cell_set(front_wide, 0, 1, back->fg, back->bg));
}
}
}
@@ -1484,13 +1669,18 @@ int tb_present() {
return TB_OK;
}
+int tb_invalidate(void) {
+ int rv;
+ if_not_init_return();
+ if_err_return(rv, resize_cellbufs());
+ return TB_OK;
+}
+
int tb_set_cursor(int cx, int cy) {
if_not_init_return();
int rv;
- if (cx < 0)
- cx = 0;
- if (cy < 0)
- cy = 0;
+ if (cx < 0) cx = 0;
+ if (cy < 0) cy = 0;
if (global.cursor_x == -1) {
if_err_return(rv,
bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]));
@@ -1501,7 +1691,7 @@ int tb_set_cursor(int cx, int cy) {
return TB_OK;
}
-int tb_hide_cursor() {
+int tb_hide_cursor(void) {
if_not_init_return();
int rv;
if (global.cursor_x >= 0) {
@@ -1585,11 +1775,22 @@ int tb_set_input_mode(int mode) {
int tb_set_output_mode(int mode) {
if_not_init_return();
- if (mode == TB_OUTPUT_CURRENT) {
- return global.output_mode;
+ switch (mode) {
+ case TB_OUTPUT_CURRENT:
+ return global.output_mode;
+ case TB_OUTPUT_NORMAL:
+ case TB_OUTPUT_256:
+ case TB_OUTPUT_216:
+ case TB_OUTPUT_GRAYSCALE:
+#if TB_OPT_ATTR_W >= 32
+ case TB_OUTPUT_TRUECOLOR:
+#endif
+ global.last_fg = ~global.fg;
+ global.last_bg = ~global.bg;
+ global.output_mode = mode;
+ return TB_OK;
}
- global.output_mode = mode;
- return TB_OK;
+ return TB_ERR;
}
int tb_peek_event(struct tb_event *event, int timeout_ms) {
@@ -1625,7 +1826,7 @@ int tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
}
while (*str) {
str += tb_utf8_char_to_unicode(&uni, str);
- w = wcwidth(uni);
+ w = wcwidth((wchar_t)uni);
if (w < 0) {
w = 1;
}
@@ -1691,9 +1892,8 @@ int tb_set_func(int fn_type, int (*fn)(struct tb_event *, size_t *)) {
return TB_ERR;
}
-struct tb_cell *tb_cell_buffer() {
- if (!global.initialized)
- return NULL;
+struct tb_cell *tb_cell_buffer(void) {
+ if (!global.initialized) return NULL;
return global.back.cells;
}
@@ -1753,7 +1953,7 @@ int tb_utf8_unicode_to_char(char *out, uint32_t c) {
return len;
}
-int tb_last_errno() {
+int tb_last_errno(void) {
return global.last_errno;
}
@@ -1795,11 +1995,36 @@ const char *tb_strerror(int err) {
case TB_ERR_RESIZE_POLL:
case TB_ERR_RESIZE_READ:
default:
- return strerror(global.last_errno);
+ strerror_r(global.last_errno, global.errbuf, sizeof(global.errbuf));
+ return (const char *)global.errbuf;
}
}
-static int tb_reset() {
+int tb_has_truecolor(void) {
+#if TB_OPT_ATTR_W >= 32
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+int tb_has_egc(void) {
+#ifdef TB_OPT_EGC
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+int tb_attr_width(void) {
+ return TB_OPT_ATTR_W;
+}
+
+const char *tb_version(void) {
+ return TB_VERSION_STR;
+}
+
+static int tb_reset(void) {
int ttyfd_open = global.ttyfd_open;
memset(&global, 0, sizeof(global));
global.ttyfd = -1;
@@ -1823,7 +2048,7 @@ static int tb_reset() {
return TB_OK;
}
-static int init_term_attrs() {
+static int init_term_attrs(void) {
if (global.ttyfd < 0) {
return TB_OK;
}
@@ -1860,31 +2085,37 @@ int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
return tb_print_ex(x, y, fg, bg, out_w, buf);
}
-static int init_term_caps() {
+static int init_term_caps(void) {
if (load_terminfo() == TB_OK) {
return parse_terminfo_caps();
}
return load_builtin_caps();
}
-static int init_cap_trie() {
+static int init_cap_trie(void) {
int rv, i;
// Add caps from terminfo or built-in
+ //
+ // Collisions are expected as some terminfo entries have dupes. (For
+ // 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.
for (i = 0; i < TB_CAP__COUNT_KEYS; i++) {
- if_err_return(rv, cap_trie_add(global.caps[i], tb_key_i(i), 0));
+ rv = cap_trie_add(global.caps[i], tb_key_i(i), 0);
+ if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv;
}
// Add built-in mod caps
+ //
+ // Collisions are OK here as well. This can happen if global.caps collides
+ // with builtin_mod_caps. It is desirable to give precedence to global.caps
+ // here.
for (i = 0; builtin_mod_caps[i].cap != NULL; i++) {
rv = cap_trie_add(builtin_mod_caps[i].cap, builtin_mod_caps[i].key,
builtin_mod_caps[i].mod);
- // Collisions are OK. This can happen if global.caps collides with
- // builtin_mod_caps. It is desirable to give precedence to global.caps
- // here.
- if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) {
- return rv;
- }
+ if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv;
}
return TB_OK;
@@ -1893,6 +2124,9 @@ static int init_cap_trie() {
static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod) {
struct cap_trie_t *next, *node = &global.cap_trie;
size_t i, j;
+
+ if (!cap || strlen(cap) <= 0) return TB_OK; // Nothing to do for empty caps
+
for (i = 0; cap[i] != '\0'; i++) {
char c = cap[i];
next = NULL;
@@ -1975,7 +2209,7 @@ static int cap_trie_deinit(struct cap_trie_t *node) {
return TB_OK;
}
-static int init_resize_handler() {
+static int init_resize_handler(void) {
if (pipe(global.resize_pipefd) != 0) {
global.last_errno = errno;
return TB_ERR_RESIZE_PIPE;
@@ -1992,7 +2226,7 @@ static int init_resize_handler() {
return TB_OK;
}
-static int send_init_escape_codes() {
+static int send_init_escape_codes(void) {
int rv;
if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_CA]));
if_err_return(rv,
@@ -2002,7 +2236,7 @@ static int send_init_escape_codes() {
return TB_OK;
}
-static int send_clear() {
+static int send_clear(void) {
int rv;
if_err_return(rv, send_attr(global.fg, global.bg));
@@ -2018,8 +2252,8 @@ static int send_clear() {
return TB_OK;
}
-static int update_term_size() {
- int rv;
+static int update_term_size(void) {
+ int rv, ioctl_errno;
if (global.ttyfd < 0) {
return TB_OK;
@@ -2028,22 +2262,22 @@ static int update_term_size() {
struct winsize sz;
memset(&sz, 0, sizeof(sz));
+ // Try ioctl TIOCGWINSZ
if (ioctl(global.ttyfd, TIOCGWINSZ, &sz) == 0) {
global.width = sz.ws_col;
global.height = sz.ws_row;
return TB_OK;
}
+ ioctl_errno = errno;
- // If TB_RESIZE_FALLBACK deinfed, try >cursor(9999,9999), >u7, <u6
- //#ifdef TB_RESIZE_FALLBACK
+ // Try >cursor(9999,9999), >u7, <u6
if_ok_return(rv, update_term_size_via_esc());
- //#endif
- global.last_errno = errno;
+ global.last_errno = ioctl_errno;
return TB_ERR_RESIZE_IOCTL;
}
-static int update_term_size_via_esc() {
+static int update_term_size_via_esc(void) {
#ifndef TB_RESIZE_FALLBACK_MS
#define TB_RESIZE_FALLBACK_MS 1000
#endif
@@ -2055,10 +2289,6 @@ static int update_term_size_via_esc() {
return TB_ERR_RESIZE_WRITE;
}
-#if !TB_OPT_SELECT
- struct pollfd fd = {.fd = global.rfd, .events = POLLIN};
- int poll_rv = poll(&fd, 1, TB_RESIZE_FALLBACK_MS);
-#else
fd_set fds;
FD_ZERO(&fds);
FD_SET(global.rfd, &fds);
@@ -2067,10 +2297,9 @@ static int update_term_size_via_esc() {
timeout.tv_sec = 0;
timeout.tv_usec = TB_RESIZE_FALLBACK_MS * 1000;
- int poll_rv = select(global.rfd + 1, &fds, NULL, NULL, &timeout);
-#endif
+ int select_rv = select(global.rfd + 1, &fds, NULL, NULL, &timeout);
- if (poll_rv != 1) {
+ if (select_rv != 1) {
global.last_errno = errno;
return TB_ERR_RESIZE_POLL;
}
@@ -2093,7 +2322,7 @@ static int update_term_size_via_esc() {
return TB_OK;
}
-static int init_cellbuf() {
+static int init_cellbuf(void) {
int rv;
if_err_return(rv, cellbuf_init(&global.back, global.width, global.height));
if_err_return(rv, cellbuf_init(&global.front, global.width, global.height));
@@ -2102,7 +2331,7 @@ static int init_cellbuf() {
return TB_OK;
}
-static int tb_deinit() {
+static int tb_deinit(void) {
if (global.caps[0] != NULL && global.wfd >= 0) {
bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]);
bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]);
@@ -2123,18 +2352,15 @@ static int tb_deinit() {
}
sigaction(SIGWINCH, &(struct sigaction){.sa_handler = SIG_DFL}, NULL);
- if (global.resize_pipefd[0] >= 0)
- close(global.resize_pipefd[0]);
- if (global.resize_pipefd[1] >= 0)
- close(global.resize_pipefd[1]);
+ if (global.resize_pipefd[0] >= 0) close(global.resize_pipefd[0]);
+ if (global.resize_pipefd[1] >= 0) close(global.resize_pipefd[1]);
cellbuf_free(&global.back);
cellbuf_free(&global.front);
bytebuf_free(&global.in);
bytebuf_free(&global.out);
- if (global.terminfo)
- tb_free(global.terminfo);
+ if (global.terminfo) tb_free(global.terminfo);
cap_trie_deinit(&global.cap_trie);
@@ -2142,9 +2368,9 @@ static int tb_deinit() {
return TB_OK;
}
-static int load_terminfo() {
+static int load_terminfo(void) {
int rv;
- char tmp[PATH_MAX];
+ char tmp[TB_PATH_MAX];
// See terminfo(5) "Fetching Compiled Descriptions" for a description of
// this behavior. Some of these paths are compile-time ncurses options, so
@@ -2204,7 +2430,7 @@ static int load_terminfo() {
static int load_terminfo_from_path(const char *path, const char *term) {
int rv;
- char tmp[PATH_MAX];
+ char tmp[TB_PATH_MAX];
// Look for term at this terminfo location, e.g., <terminfo>/x/xterm
snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term);
@@ -2251,7 +2477,7 @@ static int read_terminfo_path(const char *path) {
return TB_OK;
}
-static int parse_terminfo_caps() {
+static int parse_terminfo_caps(void) {
// See term(5) "LEGACY STORAGE FORMAT" and "EXTENDED STORAGE FORMAT" for a
// description of this behavior.
@@ -2291,8 +2517,8 @@ static int parse_terminfo_caps() {
// Load caps
int i;
for (i = 0; i < TB_CAP__COUNT; i++) {
- const char *cap = get_terminfo_string(pos_str_offsets, pos_str_table,
- header[5], terminfo_cap_indexes[i]);
+ const char *cap = get_terminfo_string(pos_str_offsets, header[4],
+ pos_str_table, header[5], terminfo_cap_indexes[i]);
if (!cap) {
// Something is not right
return TB_ERR;
@@ -2303,7 +2529,7 @@ static int parse_terminfo_caps() {
return TB_OK;
}
-static int load_builtin_caps() {
+static int load_builtin_caps(void) {
int i, j;
const char *term = getenv("TERM");
@@ -2338,16 +2564,30 @@ static int load_builtin_caps() {
}
static const char *get_terminfo_string(int16_t str_offsets_pos,
- int16_t str_table_pos, int16_t str_table_len, int16_t str_index) {
+ int16_t str_offsets_len, int16_t str_table_pos, int16_t str_table_len,
+ int16_t str_index) {
+ const int str_byte_index = (int)str_index * (int)sizeof(int16_t);
+ if (str_byte_index >= (int)str_offsets_len * (int)sizeof(int16_t)) {
+ // An offset beyond the table indicates absent
+ // See `convert_strings` in tinfo `read_entry.c`
+ return "";
+ }
const int16_t *str_offset =
- (int16_t *)(global.terminfo + (int)str_offsets_pos +
- ((int)str_index * (int)sizeof(int16_t)));
- if (*str_offset >= str_table_len) {
- // Invalid string offset
+ (int16_t *)(global.terminfo + (int)str_offsets_pos + str_byte_index);
+ if ((char *)str_offset >= global.terminfo + global.nterminfo) {
+ // str_offset points beyond end of entry
+ // Truncated/corrupt terminfo entry?
return NULL;
}
+ if (*str_offset < 0 || *str_offset >= str_table_len) {
+ // A negative offset indicates absent
+ // An offset beyond the table indicates absent
+ // See `convert_strings` in tinfo `read_entry.c`
+ return "";
+ }
if (((size_t)((int)str_table_pos + (int)*str_offset)) >= global.nterminfo) {
- // Truncated/corrupt terminfo?
+ // string points beyond end of entry
+ // Truncated/corrupt terminfo entry?
return NULL;
}
return (
@@ -2361,22 +2601,12 @@ static int wait_event(struct tb_event *event, int timeout) {
memset(event, 0, sizeof(*event));
if_ok_return(rv, extract_event(event));
-#if !TB_OPT_SELECT
- struct pollfd fds[2] = {
- {.fd = global.rfd, .events = POLLIN},
- {.fd = global.resize_pipefd[0], .events = POLLIN}
- };
-#else
fd_set fds;
struct timeval tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
-#endif
do {
-#if !TB_OPT_SELECT
- int poll_rv = poll(fds, 2, timeout);
-#else
FD_ZERO(&fds);
FD_SET(global.rfd, &fds);
FD_SET(global.resize_pipefd[0], &fds);
@@ -2385,25 +2615,19 @@ static int wait_event(struct tb_event *event, int timeout) {
? global.resize_pipefd[0]
: global.rfd;
- int poll_rv =
+ int select_rv =
select(maxfd + 1, &fds, NULL, NULL, (timeout < 0) ? NULL : &tv);
-#endif
- if (poll_rv < 0) {
+ if (select_rv < 0) {
// Let EINTR/EAGAIN bubble up
global.last_errno = errno;
return TB_ERR_POLL;
- } else if (poll_rv == 0) {
+ } else if (select_rv == 0) {
return TB_ERR_NO_EVENT;
}
-#if !TB_OPT_SELECT
- int tty_has_events = (fds[0].revents & POLLIN);
- int resize_has_events = (fds[1].revents & POLLIN);
-#else
int tty_has_events = (FD_ISSET(global.rfd, &fds));
int resize_has_events = (FD_ISSET(global.resize_pipefd[0], &fds));
-#endif
if (tty_has_events) {
ssize_t read_rv = read(global.rfd, buf, sizeof(buf));
@@ -2418,11 +2642,12 @@ 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
if_err_return(rv, update_term_size());
+ if_err_return(rv, resize_cellbufs());
event->type = TB_EVENT_RESIZE;
event->w = global.width;
event->h = global.height;
- global.need_resize = 1;
return TB_OK;
}
@@ -2656,10 +2881,10 @@ static int extract_esc_mouse(struct tb_event *event) {
} else {
int start = (type == TYPE_1015 ? 2 : 3);
- int n1 = strtoul(&in->buf[start], NULL, 10);
- int n2 =
+ unsigned n1 = strtoul(&in->buf[start], NULL, 10);
+ unsigned n2 =
strtoul(&in->buf[indices[FIRST_SEMICOLON] + 1], NULL, 10);
- int n3 =
+ unsigned n3 =
strtoul(&in->buf[indices[LAST_SEMICOLON] + 1], NULL, 10);
if (type == TYPE_1015) {
@@ -2723,19 +2948,14 @@ static int extract_esc_mouse(struct tb_event *event) {
return ret;
}
-static int resize_if_needed() {
+static int resize_cellbufs(void) {
int rv;
- if (!global.need_resize) {
- return TB_OK;
- }
- if_err_return(rv, update_term_size()); // TODO is this needed?
if_err_return(rv,
cellbuf_resize(&global.back, global.width, global.height));
if_err_return(rv,
cellbuf_resize(&global.front, global.width, global.height));
if_err_return(rv, cellbuf_clear(&global.front));
if_err_return(rv, send_clear());
- global.need_resize = 0;
return TB_OK;
}
@@ -2754,70 +2974,103 @@ static int send_attr(uintattr_t fg, uintattr_t bg) {
if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]));
- uintattr_t cfg, cbg;
+ uint32_t cfg, cbg;
switch (global.output_mode) {
default:
case TB_OUTPUT_NORMAL:
- cfg = fg & 0x0f;
- cbg = bg & 0x0f;
+ // The minus 1 below is because our colors are 1-indexed starting
+ // from black. Black is represented by a 30, 40, 90, or 100 for fg,
+ // bg, bright fg, or bright bg respectively. Red is 31, 41, 91,
+ // 101, etc.
+ cfg = (fg & TB_BRIGHT ? 90 : 30) + (fg & 0x0f) - 1;
+ cbg = (bg & TB_BRIGHT ? 100 : 40) + (bg & 0x0f) - 1;
break;
case TB_OUTPUT_256:
cfg = fg & 0xff;
cbg = bg & 0xff;
+ if (fg & TB_HI_BLACK) cfg = 0;
+ if (bg & TB_HI_BLACK) cbg = 0;
break;
case TB_OUTPUT_216:
cfg = fg & 0xff;
- if (cfg > 215)
- cfg = 7;
cbg = bg & 0xff;
- if (cbg > 215)
- cbg = 0;
- cfg += 0x10;
- cbg += 0x10;
+ if (cfg > 216) cfg = 216;
+ if (cbg > 216) cbg = 216;
+ cfg += 0x0f;
+ cbg += 0x0f;
break;
case TB_OUTPUT_GRAYSCALE:
cfg = fg & 0xff;
- if (cfg > 23)
- cfg = 23;
cbg = bg & 0xff;
- if (cbg > 23)
- cbg = 0;
- cfg += 0xe8;
- cbg += 0xe8;
+ if (cfg > 24) cfg = 24;
+ if (cbg > 24) cbg = 24;
+ cfg += 0xe7;
+ cbg += 0xe7;
break;
+#if TB_OPT_ATTR_W >= 32
case TB_OUTPUT_TRUECOLOR:
- cfg = fg;
- cbg = bg;
+ cfg = fg & 0xffffff;
+ cbg = bg & 0xffffff;
+ if (fg & TB_HI_BLACK) cfg = 0;
+ if (bg & TB_HI_BLACK) cbg = 0;
break;
+#endif
}
- if (global.output_mode != TB_OUTPUT_TRUECOLOR) {
- if (fg & TB_BOLD)
- if_err_return(rv,
- bytebuf_puts(&global.out, global.caps[TB_CAP_BOLD]));
+ if (fg & TB_BOLD)
+ if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BOLD]));
+
+ if (fg & TB_BLINK)
+ if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BLINK]));
+
+ if (fg & TB_UNDERLINE)
+ if_err_return(rv,
+ bytebuf_puts(&global.out, global.caps[TB_CAP_UNDERLINE]));
+
+ if (fg & TB_ITALIC)
+ if_err_return(rv,
+ bytebuf_puts(&global.out, global.caps[TB_CAP_ITALIC]));
+
+ if (fg & TB_DIM)
+ if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_DIM]));
- if (bg & TB_BOLD)
- if_err_return(rv,
- bytebuf_puts(&global.out, global.caps[TB_CAP_BLINK]));
+#if TB_OPT_ATTR_W == 64
+ if (fg & TB_STRIKEOUT)
+ if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_STRIKEOUT));
- if (fg & TB_UNDERLINE)
- if_err_return(rv,
- bytebuf_puts(&global.out, global.caps[TB_CAP_UNDERLINE]));
+ if (fg & TB_UNDERLINE_2)
+ if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_UNDERLINE_2));
- if (fg & TB_ITALIC)
- if_err_return(rv,
- bytebuf_puts(&global.out, global.caps[TB_CAP_ITALIC]));
+ if (fg & TB_OVERLINE)
+ if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_OVERLINE));
- if ((fg & TB_REVERSE) || (bg & TB_REVERSE))
- if_err_return(rv,
- bytebuf_puts(&global.out, global.caps[TB_CAP_REVERSE]));
+ if (fg & TB_INVISIBLE)
+ if_err_return(rv,
+ bytebuf_puts(&global.out, global.caps[TB_CAP_INVISIBLE]));
+#endif
+
+ if ((fg & TB_REVERSE) || (bg & TB_REVERSE))
+ if_err_return(rv,
+ bytebuf_puts(&global.out, global.caps[TB_CAP_REVERSE]));
+
+ int fg_is_default = (fg & 0xff) == 0;
+ int bg_is_default = (bg & 0xff) == 0;
+ if (global.output_mode == TB_OUTPUT_256) {
+ if (fg & TB_HI_BLACK) fg_is_default = 0;
+ if (bg & TB_HI_BLACK) bg_is_default = 0;
+ }
+#if TB_OPT_ATTR_W >= 32
+ if (global.output_mode == TB_OUTPUT_TRUECOLOR) {
+ fg_is_default = ((fg & 0xffffff) == 0) && ((fg & TB_HI_BLACK) == 0);
+ bg_is_default = ((bg & 0xffffff) == 0) && ((bg & TB_HI_BLACK) == 0);
}
+#endif
- if_err_return(rv, send_sgr(cfg, cbg));
+ if_err_return(rv, send_sgr(cfg, cbg, fg_is_default, bg_is_default));
global.last_fg = fg;
global.last_bg = bg;
@@ -2825,13 +3078,12 @@ static int send_attr(uintattr_t fg, uintattr_t bg) {
return TB_OK;
}
-static int send_sgr(uintattr_t fg, uintattr_t bg) {
+static int send_sgr(uint32_t cfg, uint32_t cbg, int fg_is_default,
+ int bg_is_default) {
int rv;
char nbuf[32];
- if (global.output_mode != TB_OUTPUT_TRUECOLOR && fg == TB_DEFAULT &&
- bg == TB_DEFAULT)
- {
+ if (fg_is_default && bg_is_default) {
return TB_OK;
}
@@ -2839,16 +3091,14 @@ static int send_sgr(uintattr_t fg, uintattr_t bg) {
default:
case TB_OUTPUT_NORMAL:
send_literal(rv, "\x1b[");
- if (fg != TB_DEFAULT) {
- send_literal(rv, "3");
- send_num(rv, nbuf, fg - 1);
- if (bg != TB_DEFAULT) {
+ if (!fg_is_default) {
+ send_num(rv, nbuf, cfg);
+ if (!bg_is_default) {
send_literal(rv, ";");
}
}
- if (bg != TB_DEFAULT) {
- send_literal(rv, "4");
- send_num(rv, nbuf, bg - 1);
+ if (!bg_is_default) {
+ send_num(rv, nbuf, cbg);
}
send_literal(rv, "m");
break;
@@ -2857,35 +3107,45 @@ static int send_sgr(uintattr_t fg, uintattr_t bg) {
case TB_OUTPUT_216:
case TB_OUTPUT_GRAYSCALE:
send_literal(rv, "\x1b[");
- if (fg != TB_DEFAULT) {
+ if (!fg_is_default) {
send_literal(rv, "38;5;");
- send_num(rv, nbuf, fg);
- if (bg != TB_DEFAULT) {
+ send_num(rv, nbuf, cfg);
+ if (!bg_is_default) {
send_literal(rv, ";");
}
}
- if (bg != TB_DEFAULT) {
+ if (!bg_is_default) {
send_literal(rv, "48;5;");
- send_num(rv, nbuf, bg);
+ send_num(rv, nbuf, cbg);
}
send_literal(rv, "m");
break;
+#if TB_OPT_ATTR_W >= 32
case TB_OUTPUT_TRUECOLOR:
- send_literal(rv, "\x1b[38;2;");
- send_num(rv, nbuf, (fg >> 16) & 0xff);
- send_literal(rv, ";");
- send_num(rv, nbuf, (fg >> 8) & 0xff);
- send_literal(rv, ";");
- send_num(rv, nbuf, fg & 0xff);
- send_literal(rv, ";48;2;");
- send_num(rv, nbuf, (bg >> 16) & 0xff);
- send_literal(rv, ";");
- send_num(rv, nbuf, (bg >> 8) & 0xff);
- send_literal(rv, ";");
- send_num(rv, nbuf, bg & 0xff);
+ send_literal(rv, "\x1b[");
+ if (!fg_is_default) {
+ send_literal(rv, "38;2;");
+ send_num(rv, nbuf, (cfg >> 16) & 0xff);
+ send_literal(rv, ";");
+ send_num(rv, nbuf, (cfg >> 8) & 0xff);
+ send_literal(rv, ";");
+ send_num(rv, nbuf, cfg & 0xff);
+ if (!bg_is_default) {
+ send_literal(rv, ";");
+ }
+ }
+ if (!bg_is_default) {
+ send_literal(rv, "48;2;");
+ send_num(rv, nbuf, (cbg >> 16) & 0xff);
+ send_literal(rv, ";");
+ send_num(rv, nbuf, (cbg >> 8) & 0xff);
+ send_literal(rv, ";");
+ send_num(rv, nbuf, cbg & 0xff);
+ }
send_literal(rv, "m");
break;
+#endif
}
return TB_OK;
}
@@ -2933,9 +3193,10 @@ static int send_cluster(int x, int y, uint32_t *ch, size_t nch) {
static int convert_num(uint32_t num, char *buf) {
int i, l = 0;
- int ch;
+ char ch;
do {
- buf[l++] = '0' + (num % 10);
+ /* '0' = 48; 48 + num%10 < 58 < MAX_8bitCHAR */
+ buf[l++] = (char)('0' + (num % 10));
num /= 10;
} while (num);
for (i = 0; i < l / 2; i++) {
@@ -2978,7 +3239,8 @@ static int cell_set(struct tb_cell *cell, uint32_t *ch, size_t nch,
if (nch <= 1) {
cell->nech = 0;
} else {
- cell_reserve_ech(cell, nch + 1);
+ int rv;
+ if_err_return(rv, cell_reserve_ech(cell, nch + 1));
memcpy(cell->ech, ch, nch);
cell->ech[nch] = '\0';
cell->nech = nch;
@@ -3041,10 +3303,11 @@ static int cellbuf_free(struct cellbuf_t *c) {
}
static int cellbuf_clear(struct cellbuf_t *c) {
- int i;
+ int rv, i;
uint32_t space = (uint32_t)' ';
for (i = 0; i < c->width * c->height; i++) {
- cell_set(&c->cells[i], &space, 1, global.fg, global.bg);
+ if_err_return(rv,
+ cell_set(&c->cells[i], &space, 1, global.fg, global.bg));
}
return TB_OK;
}
@@ -3096,6 +3359,7 @@ static int cellbuf_resize(struct cellbuf_t *c, int w, int h) {
}
static int bytebuf_puts(struct bytebuf_t *b, const char *str) {
+ if (!str || strlen(str) <= 0) return TB_OK; // Nothing to do for empty caps
return bytebuf_nputs(b, str, (size_t)strlen(str));
}
diff --git a/util.c b/util.c
@@ -1,4 +1,6 @@
-/* See the file LICENSE for copyright and license details. */
+/* This program is licensed under the terms of GNU GPL v3 or (at your option)
+ * any later version.
+ * See the file LICENSE for exact copyright and license details. */
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
diff --git a/version.h.in b/version.h.in
@@ -1,4 +1,6 @@
-/* See the file LICENSE for copyright and license details. */
+/* This program is licensed under the terms of GNU GPL v3 or (at your option)
+ * any later version.
+ * See the file LICENSE for exact copyright and license details. */
#define VERSION "%VERSION%"
#define DATE "%DATE%"