├── .clang-format ├── .gitattributes ├── .github └── workflows │ └── termbox_test.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── codegen.sh ├── demo ├── example.d ├── example.go ├── example.nim ├── example.php ├── example.py ├── example.rb ├── example.rs ├── example.zig ├── keyboard.c └── keyboard.gif ├── termbox2.h └── tests ├── Dockerfile ├── run.sh ├── test_64bit ├── expected.ansi └── test.php ├── test_basic ├── expected.ansi └── test.php ├── test_color_8bit ├── expected.ansi └── test.php ├── test_color_true ├── expected.ansi └── test.php ├── test_egc ├── expected.ansi └── test.php ├── test_error ├── expected.ansi └── test.php ├── test_ffi.php ├── test_invalid_term ├── expected.ansi └── test.php ├── test_invalid_utf8 ├── expected.ansi └── test.php ├── test_invalidate ├── expected.ansi └── test.php ├── test_large_input ├── expected.ansi └── test.php ├── test_mod ├── expected.ansi └── test.php ├── test_non_printable ├── expected.ansi └── test.php ├── test_non_spacing_mark ├── expected.ansi └── test.php ├── test_output_mode ├── expected.ansi └── test.php ├── test_prepend.php ├── test_print ├── expected.ansi └── test.php ├── test_resize ├── expected.ansi └── test.php └── test_wcwidth ├── expected.ansi └── test.php /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | ColumnLimit: 80 4 | AlignAfterOpenBracket: DontAlign 5 | AllowAllArgumentsOnNextLine: false 6 | AlignConsecutiveMacros: AcrossEmptyLinesAndComments 7 | AlignArrayOfStructures: Left 8 | AllowShortFunctionsOnASingleLine: None 9 | AllowShortBlocksOnASingleLine: Always 10 | AllowShortIfStatementsOnASingleLine: WithoutElse 11 | BreakBeforeBraces: Custom 12 | BraceWrapping: 13 | AfterControlStatement: MultiLine 14 | IndentCaseLabels: true 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ansi binary 2 | -------------------------------------------------------------------------------- /.github/workflows/termbox_test.yml: -------------------------------------------------------------------------------- 1 | name: termbox_test 2 | on: [push, pull_request] 3 | jobs: 4 | termbox_test_job: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - run: make clean test 9 | - run: CFLAGS='-UTB_LIB_OPTS' make clean test # non-egc, non-truecolor 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | termbox2.o 2 | libtermbox2.a 3 | libtermbox2.so* 4 | termbox2.ffi.h 5 | termbox2.ffi.macro 6 | termbox2.h.lib 7 | demo/keyboard 8 | tests/**/observed.ansi 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 termbox developers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | prefix?=/usr/local 2 | 3 | termbox_cflags:=-std=c99 -Wall -Wextra -pedantic -Wno-unused-result -g -O0 -D_XOPEN_SOURCE -D_DEFAULT_SOURCE $(CFLAGS) 4 | termbox_demos:=$(patsubst demo/%.c,demo/%,$(wildcard demo/*.c)) 5 | termbox_h:=termbox2.h 6 | termbox_h_lib:=termbox2.h.lib 7 | termbox_ffi_h:=termbox2.ffi.h 8 | termbox_ffi_macro:=termbox2.ffi.macro 9 | termbox_o:=termbox2.o 10 | termbox_so_version_abi:=2 11 | termbox_so_version_minor_patch:=0.0 12 | termbox_so:=libtermbox2.so 13 | termbox_so_x:=$(termbox_so).$(termbox_so_version_abi) 14 | termbox_so_x_y_z:=$(termbox_so_x).$(termbox_so_version_minor_patch) 15 | termbox_ld_soname:=soname 16 | termbox_a:=libtermbox2.a 17 | 18 | DOCKER?=docker 19 | 20 | ifeq ($(shell $(CC) -dumpmachine | grep -q apple && echo 1), 1) 21 | termbox_so:=libtermbox2.dylib 22 | termbox_so_x:=libtermbox2.$(termbox_so_version_abi).dylib 23 | termbox_so_x_y_z:=libtermbox2.$(termbox_so_version_abi).$(termbox_so_version_minor_patch).dylib 24 | termbox_ld_soname:=install_name 25 | endif 26 | 27 | all: $(termbox_demos) $(termbox_so) $(termbox_so_x) $(termbox_a) 28 | 29 | $(termbox_demos): %: %.c 30 | $(CC) -DTB_IMPL -DTB_LIB_OPTS $(termbox_cflags) $^ -o $@ 31 | 32 | $(termbox_o): $(termbox_h) 33 | $(CC) -DTB_IMPL -DTB_LIB_OPTS -fPIC -xc -c $(termbox_cflags) $(termbox_h) -o $@ 34 | 35 | $(termbox_so_x_y_z): $(termbox_o) 36 | $(CC) -shared -Wl,-$(termbox_ld_soname),$(termbox_so_x) $(termbox_o) -o $@ 37 | 38 | $(termbox_so_x): $(termbox_so_x_y_z) 39 | ln -sf $(termbox_so_x_y_z) $@ 40 | 41 | $(termbox_so): $(termbox_so_x_y_z) 42 | ln -sf $(termbox_so_x_y_z) $@ 43 | 44 | $(termbox_a): $(termbox_o) 45 | $(AR) rcs $@ $(termbox_o) 46 | 47 | $(termbox_ffi_h): $(termbox_h) 48 | awk '/__ffi_start/{p=1} p==1 || /TERMBOX_H_INCL/{print}' $^ | $(CC) -DTB_LIB_OPTS $(termbox_cflags) -P -E - >$@ 49 | 50 | $(termbox_ffi_macro): $(termbox_h) 51 | awk '/__ffi_start/{p=1} p==1 || /TERMBOX_H_INCL/{print}' $^ | $(CC) -DTB_LIB_OPTS $(termbox_cflags) -P -E -dM - >$@ 52 | 53 | $(termbox_h_lib): $(termbox_h) 54 | sed 's|0 // __tb_lib_opts|1 // __tb_lib_opts|' $(termbox_h) >$@ 55 | 56 | terminfo: 57 | awk -vg=0 'g==0{print} /BEGIN codegen h/{g=1; system("./codegen.sh h")} /END codegen h/{g=0; print} g==1{next}' termbox2.h >termbox2.h.tmp && mv -vf termbox2.h.tmp termbox2.h 58 | awk -vg=0 'g==0{print} /BEGIN codegen c/{g=1; system("./codegen.sh c")} /END codegen c/{g=0; print} g==1{next}' termbox2.h >termbox2.h.tmp && mv -vf termbox2.h.tmp termbox2.h 59 | 60 | format: 61 | clang-format -i termbox2.h 62 | 63 | test: $(termbox_so) $(termbox_ffi_h) $(termbox_ffi_macro) 64 | $(DOCKER) build -f tests/Dockerfile --build-arg=cflags="$(termbox_cflags)" . 65 | 66 | test_local: $(termbox_so) $(termbox_ffi_h) $(termbox_ffi_macro) 67 | ./tests/run.sh 68 | 69 | install: 70 | $(MAKE) install_h 71 | 72 | lib: 73 | $(MAKE) $(termbox_h_lib) 74 | $(MAKE) $(termbox_a) 75 | $(MAKE) $(termbox_so) 76 | 77 | install_lib: 78 | $(MAKE) install_h_lib 79 | $(MAKE) install_a 80 | $(MAKE) install_so 81 | 82 | install_h: $(termbox_h) 83 | install -d $(DESTDIR)$(prefix)/include 84 | install -p -m 644 $(termbox_h) $(DESTDIR)$(prefix)/include/$(termbox_h) 85 | 86 | install_h_lib: $(termbox_h_lib) 87 | install -d $(DESTDIR)$(prefix)/include 88 | install -p -m 644 $(termbox_h_lib) $(DESTDIR)$(prefix)/include/$(termbox_h) 89 | 90 | install_a: $(termbox_a) 91 | install -d $(DESTDIR)$(prefix)/lib 92 | install -p -m 644 $(termbox_a) $(DESTDIR)$(prefix)/lib/$(termbox_a) 93 | 94 | install_so: $(termbox_so_x_y_z) 95 | install -d $(DESTDIR)$(prefix)/lib 96 | install -p -m 755 $(termbox_so_x_y_z) $(DESTDIR)$(prefix)/lib/$(termbox_so_x_y_z) 97 | ln -sf $(termbox_so_x_y_z) $(DESTDIR)$(prefix)/lib/$(termbox_so_x) 98 | ln -sf $(termbox_so_x_y_z) $(DESTDIR)$(prefix)/lib/$(termbox_so) 99 | 100 | clean: 101 | rm -f $(termbox_demos) $(termbox_o) $(termbox_a) $(termbox_so) $(termbox_so_x) $(termbox_so_x_y_z) $(termbox_ffi_h) $(termbox_ffi_macro) $(termbox_h_lib) tests/**/observed.ansi 102 | 103 | .PHONY: all lib terminfo format test test_local install install_lib install_h install_h_lib install_a install_so clean 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # termbox2 2 | 3 | termbox2 is a terminal rendering library for creating TUIs. It is a 4 | [suckless](https://suckless.org) alternative to the ubiquitous 5 | [ncurses](https://invisible-island.net/ncurses/) library. It ships with built-in 6 | support for popular terminals and can also fallback to terminfo if present. 7 | Compared to the [original termbox](https://github.com/termbox/termbox), it 8 | retains a simple API and no dependencies beyond libc, and adds stricter error 9 | checking, more efficient escape sequence parsing, opt-in support for 32-bit 10 | color, extended grapheme clusters, code gen for built-in escape sequences, a 11 | test suite, and more. 12 | 13 | termbox2 is organized as a single file header library, though it is possible to 14 | compile it as a stand-alone shared or static library. 15 | 16 | ![keyboard demo](demo/keyboard.gif) 17 | 18 | ### Synopsis 19 | 20 | ```c 21 | #define TB_IMPL 22 | #include "termbox2.h" 23 | 24 | int main(int argc, char **argv) { 25 | struct tb_event ev; 26 | int y = 0; 27 | 28 | tb_init(); 29 | 30 | tb_printf(0, y++, TB_GREEN, 0, "hello from termbox"); 31 | tb_printf(0, y++, 0, 0, "width=%d height=%d", tb_width(), tb_height()); 32 | tb_printf(0, y++, 0, 0, "press any key..."); 33 | tb_present(); 34 | 35 | tb_poll_event(&ev); 36 | 37 | y++; 38 | tb_printf(0, y++, 0, 0, "event type=%d key=%d ch=%c", ev.type, ev.key, ev.ch); 39 | tb_printf(0, y++, 0, 0, "press any key to quit..."); 40 | tb_present(); 41 | 42 | tb_poll_event(&ev); 43 | tb_shutdown(); 44 | 45 | return 0; 46 | } 47 | ``` 48 | 49 | ### API 50 | 51 | The basic API is pretty self-explanatory. Consult the header file itself for the 52 | complete API and documentation. 53 | 54 | ```c 55 | int tb_init(); 56 | int tb_shutdown(); 57 | 58 | int tb_width(); 59 | int tb_height(); 60 | 61 | int tb_clear(); 62 | int tb_present(); 63 | 64 | int tb_set_cursor(int cx, int cy); 65 | int tb_hide_cursor(); 66 | 67 | int tb_set_cell(int x, int y, uint32_t ch, uintattr_t fg, uintattr_t bg); 68 | 69 | int tb_peek_event(struct tb_event *event, int timeout_ms); 70 | int tb_poll_event(struct tb_event *event); 71 | 72 | int tb_print(int x, int y, uintattr_t fg, uintattr_t bg, const char *str); 73 | int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char *fmt, ...); 74 | ``` 75 | 76 | ### How to use termbox2 77 | 78 | As mentioned above, there are two options: 79 | 80 | 1. Copy (or `git submodule`) `termbox2.h` into your C project. As normal, 81 | include the header file wherever you want to use `tb_*` functions, but also 82 | be sure to `#define TB_IMPL` in exactly one of your source files. (This is a 83 | common pattern for single file header libraries.) Ensure that feature test 84 | macros `_DEFAULT_SOURCE` and `_XOPEN_SOURCE` are defined (either by defining 85 | them via compiler flags or including `termbox2.h` first[^1]). 86 | 2. Build termbox2 as a library (either `make libtermbox2.so` or 87 | `make libtermbox2.a`) and link as normal. Make sure the compile-time options 88 | remain the same for libtermbox2 and your application. (Alternatively, build 89 | with `make lib` and use `termbox2.h.lib` instead of `termbox2.h`. This will 90 | guarantee that the same `TB_LIB_OPTS`-gated compile-time options are used in 91 | the library and the header file.) 92 | 93 | ### Language bindings 94 | 95 | Basic FFI or ABI-compatible examples in the languages below are in the `demo/` 96 | directory. (Feel free to submit PRs for other languages.) 97 | 98 | * D 99 | * Go 100 | * Nim 101 | * PHP 102 | * Python 103 | * Ruby 104 | * Rust 105 | * Zig 106 | 107 | Other wrapper libraries: 108 | 109 | * [termbox.cr (Crystal)](https://github.com/thmisch/termbox.cr) 110 | * [termbox2.cr (Crystal)](https://github.com/homonoidian/termbox2.cr) 111 | * [Termbox.pm (Perl)](https://github.com/sanko/Termbox.pm) 112 | * [termbox2-hs (Haskell)](https://github.com/gatlin/termbox2-hs) 113 | * [termbox2-zig (Zig)](https://sr.ht/~kolunmi/termbox2-zig) 114 | * [termbox2-node (JavaScript)](https://github.com/RauliL/termbox2-node) 115 | * [letloop's termbox2 (Chez Scheme)](https://github.com/letloop/letloop/) 116 | * [odin-termbox2 (Odin)](https://github.com/sudokit/odin-termbox2) 117 | 118 | ### Examples 119 | 120 | * [mle](https://github.com/adsr/mle) - flexible terminal-based text editor 121 | * [ictree](https://github.com/NikitaIvanovV/ictree) - like tree but interactive 122 | * [lavat](https://github.com/AngelJumbo/lavat) - lava lamp for the terminal 123 | * [termbox-tetris](https://github.com/zacharygraber/termbox-tetris) - Tetris clone 124 | * [dvd-screensaver](https://github.com/yamin-shihab/dvd-screensaver) - a terminal screensaver 125 | * [matrix-tui](https://github.com/git-bruh/matrix-tui) - Matrix client 126 | * [Vgmi](https://github.com/RealMelkor/Vgmi) - Gemini client 127 | * [poe](https://sr.ht/~strahinja/poe/) - `.po` file editor 128 | * [xtxf](https://github.com/charlesrocket/xtxf) - 2D matrix screensaver 129 | 130 | [^1]: See https://github.com/termbox/termbox2/pull/75#issuecomment-2252242269 131 | -------------------------------------------------------------------------------- /codegen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -uo pipefail 3 | 4 | read -r -d '' builtin_terms <<'EOD' 5 | xterm 6 | linux 7 | screen tmux 8 | rxvt-256color 9 | rxvt-unicode rxvt 10 | Eterm 11 | EOD 12 | 13 | read -r -d '' terminfo_keys <<'EOD' 14 | kf1 F1 15 | kf2 F2 16 | kf3 F3 17 | kf4 F4 18 | kf5 F5 19 | kf6 F6 20 | kf7 F7 21 | kf8 F8 22 | kf9 F9 23 | kf10 F10 24 | kf11 F11 25 | kf12 F12 26 | kich1 INSERT 27 | kdch1 DELETE 28 | khome HOME 29 | kend END 30 | kpp PGUP 31 | knp PGDN 32 | kcuu1 ARROW_UP 33 | kcud1 ARROW_DOWN 34 | kcub1 ARROW_LEFT 35 | kcuf1 ARROW_RIGHT 36 | kcbt BACK_TAB 37 | EOD 38 | 39 | read -r -d '' terminfo_funcs <<'EOD' 40 | smcup ENTER_CA 41 | rmcup EXIT_CA 42 | cnorm SHOW_CURSOR 43 | civis HIDE_CURSOR 44 | clear CLEAR_SCREEN 45 | sgr0 SGR0 46 | smul UNDERLINE 47 | bold BOLD 48 | blink BLINK 49 | sitm ITALIC 50 | rev REVERSE 51 | smkx ENTER_KEYPAD 52 | rmkx EXIT_KEYPAD 53 | dim DIM 54 | invis INVISIBLE 55 | EOD 56 | 57 | read -r -d '' extra_keys <<'EOD' 58 | MOUSE_LEFT 59 | MOUSE_RIGHT 60 | MOUSE_MIDDLE 61 | MOUSE_RELEASE 62 | MOUSE_WHEEL_UP 63 | MOUSE_WHEEL_DOWN 64 | EOD 65 | 66 | main() { 67 | local IFS=$'\n' 68 | local codegen_type=$1 69 | 70 | # codegen terminfo_cap_indexes 71 | # codegen #define TB_CAP_* 72 | # codegen #define TB_KEY_* 73 | c_cap_indexes='static const int16_t terminfo_cap_indexes[] = {'$'\n' 74 | c_cap_defines='' 75 | c_cap_num=0 76 | c_key_defines='' 77 | c_key_num=0 78 | for terminfo_cap_tuple in $terminfo_keys; do 79 | string_name=$(awk '{print $1}' <<<"$terminfo_cap_tuple") 80 | define_name=$(awk '{print $2}' <<<"$terminfo_cap_tuple") 81 | string_index=$(terminfo_string_index $string_name) 82 | c_cap_indexes+=" $string_index, // $string_name (TB_CAP_${define_name})"$'\n' 83 | c_cap_defines+="#define TB_CAP_${define_name} $c_cap_num"$'\n' 84 | c_key_defines+="#define TB_KEY_${define_name} (0xffff - $c_key_num)"$'\n' 85 | let c_cap_num+=1 86 | let c_key_num+=1 87 | done 88 | c_cap_defines+="#define TB_CAP__COUNT_KEYS $c_cap_num"$'\n' 89 | for extra_keys_tuple in $extra_keys; do 90 | define_name=$(awk '{print $1}' <<<"$extra_keys_tuple") 91 | c_key_defines+="#define TB_KEY_${define_name} (0xffff - $c_key_num)"$'\n' 92 | let c_key_num+=1 93 | done 94 | for terminfo_cap_tuple in $terminfo_funcs; do 95 | string_name=$(awk '{print $1}' <<<"$terminfo_cap_tuple") 96 | define_name=$(awk '{print $2}' <<<"$terminfo_cap_tuple") 97 | string_index=$(terminfo_string_index $string_name) 98 | c_cap_indexes+=" $string_index, // $string_name (TB_CAP_${define_name})"$'\n' 99 | c_cap_defines+="#define TB_CAP_${define_name} $c_cap_num"$'\n' 100 | let c_cap_num+=1 101 | done 102 | c_cap_defines+="#define TB_CAP__COUNT $c_cap_num"$'\n' 103 | c_cap_indexes+='};' 104 | 105 | c_codegen_comment="/* Produced by $0 on $(date -uR) */" 106 | 107 | if [ "$codegen_type" == "c" ]; then 108 | echo "$c_codegen_comment" 109 | echo 110 | echo "$c_cap_indexes" 111 | echo 112 | 113 | # codegen built-in _keys and _funcs as _caps 114 | c_term_name_cap='static struct {'$'\n' 115 | c_term_name_cap+=' const char *name;'$'\n' 116 | c_term_name_cap+=' const char **caps;'$'\n' 117 | c_term_name_cap+=' const char *alias;'$'\n' 118 | c_term_name_cap+='} builtin_terms[] = {'$'\n' 119 | for builtin_terms_tuple in $builtin_terms; do 120 | term_name=$(awk '{print $1}' <<<"$builtin_terms_tuple") 121 | term_alias=$(awk '{print $2}' <<<"$builtin_terms_tuple") 122 | c_term_name=$(tr -d '\n' <<<$term_name | tr -c 'A-Za-z0-9' '_' | tr 'A-Z' 'a-z') 123 | 124 | c_term_caps="static const char *${c_term_name}_caps[] = {"$'\n' 125 | for terminfo_cap_tuple in $terminfo_keys $terminfo_funcs; do 126 | string_name=$(awk '{print $1}' <<<"$terminfo_cap_tuple") 127 | define_name=$(awk '{print $2}' <<<"$terminfo_cap_tuple") 128 | c_string_literal=$(terminfo_string_literal $term_name $string_name) 129 | c_term_caps+=" ${c_string_literal}, // $string_name (TB_CAP_${define_name})"$'\n' 130 | done 131 | c_term_caps+='};' 132 | 133 | echo "// $term_name" 134 | echo "$c_term_caps" 135 | echo 136 | 137 | c_term_name_cap+=" { \"${term_name}\", ${c_term_name}_caps, \"${term_alias}\" },"$'\n' 138 | done 139 | c_term_name_cap+=' { NULL, NULL, NULL },'$'\n' 140 | c_term_name_cap+='};'$'\n' 141 | 142 | echo "$c_term_name_cap" 143 | 144 | elif [ "$codegen_type" == "h" ]; then 145 | 146 | echo "$c_codegen_comment" 147 | echo -n "$c_key_defines" 148 | echo 149 | echo -n "$c_cap_defines" 150 | 151 | fi 152 | } 153 | 154 | terminfo_string_index() { 155 | local string_name=$1 156 | infocmp -E | grep -w $string_name | awk '{print $2}' | sed 's|:$||g' 157 | } 158 | 159 | terminfo_string_literal() { 160 | local term_name=$1 161 | local string_name=$2 162 | local var_name=$(infocmp -E $term_name | grep -w $string_name | awk '{print $5}' | sed 's|,$||g') 163 | if [ "$var_name" == "ABSENT_STRING" ]; then 164 | echo '""' 165 | return 0 166 | fi 167 | local c_string_literal=$(infocmp -E $term_name | grep -w "${var_name}" | grep ' = ' | awk '{print $NF}' | sed 's|;$||g') 168 | if [ -z "$c_string_literal" ]; then 169 | echo '""' 170 | return 0 171 | fi 172 | echo $c_string_literal 173 | } 174 | 175 | main "$@" 176 | -------------------------------------------------------------------------------- /demo/example.d: -------------------------------------------------------------------------------- 1 | // rdmd ../libtermbox.a example.d 2 | 3 | struct tb_event { 4 | ubyte type; 5 | ubyte mod; 6 | ushort key; 7 | uint ch; 8 | int w; 9 | int h; 10 | int x; 11 | int y; 12 | }; 13 | 14 | extern (C) int tb_init(); 15 | extern (C) int tb_shutdown(); 16 | extern (C) int tb_present(); 17 | extern (C) int tb_poll_event(tb_event *event); 18 | extern (C) int tb_printf(int x, int y, uint fg, uint bg, const char *fmt, ...); 19 | 20 | void main() { 21 | tb_event ev; 22 | int y = 0; 23 | 24 | tb_init(); 25 | 26 | tb_printf(0, y++, 0x02 | 0x0100, 0x00, "hello from d"); 27 | tb_printf(0, y++, 0x03, 0x00, "press any key"); 28 | 29 | tb_present(); 30 | tb_poll_event(&ev); 31 | 32 | tb_printf(0, y++, 0x04, 0x00, 33 | "event: type=%d mod=%d key=%d ch=%d w=%d h=%d x=%d y=%d", 34 | ev.type, 35 | ev.mod, 36 | ev.key, 37 | ev.ch, 38 | ev.w, 39 | ev.h, 40 | ev.x, 41 | ev.y); 42 | tb_printf(0, y++, 0x05, 0x00, "press any key to quit"); 43 | 44 | tb_present(); 45 | tb_poll_event(&ev); 46 | 47 | tb_shutdown(); 48 | } 49 | -------------------------------------------------------------------------------- /demo/example.go: -------------------------------------------------------------------------------- 1 | // go run example.go 2 | 3 | package main 4 | 5 | /* 6 | #cgo LDFLAGS: -L.. -Wl,-rpath,.. -ltermbox2 7 | #include 8 | typedef struct tb_event_s { 9 | uint8_t _type; 10 | uint8_t mod; 11 | uint16_t key; 12 | uint32_t ch; 13 | int32_t w; 14 | int32_t h; 15 | int32_t x; 16 | int32_t y; 17 | } tb_event; 18 | int tb_init(); 19 | int tb_shutdown(); 20 | int tb_present(); 21 | int tb_poll_event(tb_event *event); 22 | int tb_print(int x, int y, uint32_t fg, uint32_t bg, const char *str); 23 | */ 24 | import "C" 25 | import "fmt" 26 | 27 | func main() { 28 | ev := C.tb_event{} 29 | 30 | C.tb_init() 31 | 32 | y := 0 33 | C.tb_print(C.int(0), C.int(y), C.uint(0x02 | 0x0100), C.uint(0x00), C.CString("hello from go")) 34 | y += 1 35 | C.tb_print(C.int(0), C.int(y), C.uint(0x03), C.uint(0x00), C.CString("press any key")) 36 | y += 1 37 | C.tb_present() 38 | C.tb_poll_event(&ev) 39 | 40 | C.tb_print(C.int(0), C.int(y), C.uint(0x04), C.uint(0x00), C.CString(fmt.Sprintf( 41 | "event: type=%d mod=%d key=%d ch=%d w=%d h=%d x=%d y=%d", 42 | ev._type, 43 | ev.mod, 44 | ev.key, 45 | ev.ch, 46 | ev.w, 47 | ev.h, 48 | ev.x, 49 | ev.y))) 50 | y += 1 51 | C.tb_present() 52 | 53 | C.tb_print(C.int(0), C.int(y), C.uint(0x05), C.uint(0x00), C.CString("press any key to quit")) 54 | y += 1 55 | C.tb_present() 56 | C.tb_poll_event(&ev) 57 | 58 | C.tb_shutdown() 59 | } 60 | -------------------------------------------------------------------------------- /demo/example.nim: -------------------------------------------------------------------------------- 1 | # nim c -r example.nim 2 | const libtermbox2 = "../libtermbox2.so" 3 | 4 | type Event = object 5 | etype {.importc :"type".}: uint8 6 | emod {.importc :"mod".}: uint8 7 | key: uint16 8 | ch: uint32 9 | w, h, x, y: int32 10 | 11 | proc tb_init(): cint {.importc, dynlib: libtermbox2.} 12 | proc tb_shutdown(): cint {.importc, dynlib: libtermbox2.} 13 | proc tb_present(): cint {.importc, dynlib: libtermbox2.} 14 | proc tb_poll_event(ev: ptr Event): cint {.importc, dynlib: libtermbox2.} 15 | proc tb_printf(x: cint, y: cint, fg: uint32, bg: uint32, fmt: cstring): cint {.importc, dynlib: libtermbox2, varargs.} 16 | 17 | var ev: Event 18 | 19 | discard tb_init() 20 | 21 | var y: int = 0; 22 | discard tb_printf(0.cint, y.cint, 0x0102.uint32, 0x00.uint32, "hello from nim") 23 | y += 1 24 | discard tb_printf(0.cint, y.cint, 0x03.uint32, 0x00.uint32, "press any key") 25 | y += 1 26 | discard tb_present() 27 | 28 | discard tb_poll_event(ev.addr) 29 | 30 | discard tb_printf(0.cint, y.cint, 0x04.uint32, 0x00.uint32, 31 | "event: type=%d mod=%d key=%d ch=%d w=%d h=%d x=%d y=%d", 32 | ev.etype, 33 | ev.emod, 34 | ev.key, 35 | ev.ch, 36 | ev.w, 37 | ev.h, 38 | ev.x, 39 | ev.y) 40 | y += 1 41 | discard tb_present() 42 | 43 | discard tb_printf(0.cint, y.cint, 0x05.uint32, 0x00.uint32, "press any key to quit") 44 | y += 1 45 | discard tb_present() 46 | discard tb_poll_event(ev.addr) 47 | 48 | discard tb_shutdown() 49 | -------------------------------------------------------------------------------- /demo/example.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | new('struct tb_event'); 25 | 26 | $termbox->tb_init(); 27 | 28 | $y = 0; 29 | $termbox->tb_printf(0, $y++, 0x02 | 0x0100, 0x00, 'hello from php'); 30 | $termbox->tb_printf(0, $y++, 0x03, 0x00, 'press any key'); 31 | $termbox->tb_present(); 32 | $termbox->tb_poll_event(FFI::addr($ev)); 33 | 34 | $termbox->tb_printf(0, $y++, 0x04, 0x00, 35 | 'event: type=%d mod=%d key=%d ch=%d w=%d h=%d x=%d y=%d', 36 | $ev->type, 37 | $ev->mod, 38 | $ev->key, 39 | $ev->ch, 40 | $ev->w, 41 | $ev->h, 42 | $ev->x, 43 | $ev->y); 44 | $termbox->tb_present(); 45 | 46 | $termbox->tb_printf(0, $y++, 0x05, 0x00, 'press any key to quit'); 47 | $termbox->tb_present(); 48 | $termbox->tb_poll_event(FFI::addr($ev)); 49 | 50 | $termbox->tb_shutdown(); 51 | -------------------------------------------------------------------------------- /demo/example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import cffi # requires python3-cffi 3 | 4 | ffi = cffi.FFI() 5 | 6 | ffi.cdef(""" 7 | struct tb_event { 8 | uint8_t type; 9 | uint8_t mod; 10 | uint16_t key; 11 | uint32_t ch; 12 | int32_t w; 13 | int32_t h; 14 | int32_t x; 15 | int32_t y; 16 | }; 17 | int tb_init(); 18 | int tb_shutdown(); 19 | int tb_present(); 20 | int tb_poll_event(struct tb_event *event); 21 | int tb_printf(int x, int y, uint32_t fg, uint32_t bg, const char *fmt, ...); 22 | """) 23 | termbox = ffi.dlopen("../libtermbox2.so") 24 | ev = ffi.new("struct tb_event *") 25 | 26 | termbox.tb_init() 27 | 28 | y = 0 29 | termbox.tb_printf(0, y, 0x02 | 0x0100, 0x00, b"hello from python"); y += 1 30 | termbox.tb_printf(0, y, 0x03, 0x00, b"press any key"); y += 1 31 | termbox.tb_present() 32 | termbox.tb_poll_event(ev); 33 | 34 | termbox.tb_printf(0, y, 0x04, 0x00, 35 | b"event: type=%d mod=%d key=%d ch=%d w=%d h=%d x=%d y=%d", 36 | ffi.cast("int", ev.type), 37 | ffi.cast("int", ev.mod), 38 | ffi.cast("int", ev.key), 39 | ffi.cast("int", ev.ch), 40 | ffi.cast("int", ev.w), 41 | ffi.cast("int", ev.h), 42 | ffi.cast("int", ev.x), 43 | ffi.cast("int", ev.y)); y += 1 44 | termbox.tb_present() 45 | 46 | termbox.tb_printf(0, y, 0x05, 0x00, b"press any key to quit"); y += 1 47 | termbox.tb_present() 48 | termbox.tb_poll_event(ev); 49 | 50 | termbox.tb_shutdown() 51 | -------------------------------------------------------------------------------- /demo/example.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'ffi' 3 | 4 | module Termbox 5 | extend FFI::Library 6 | ffi_lib '../libtermbox2.so' 7 | class Event < FFI::Struct 8 | layout :type, :uint8, 9 | :mod, :uint8, 10 | :key, :uint16, 11 | :ch, :uint32, 12 | :w, :int32, 13 | :h, :int32, 14 | :x, :int32, 15 | :y, :int32 16 | end 17 | attach_function :tb_init, [], :int 18 | attach_function :tb_shutdown, [], :int 19 | attach_function :tb_present, [], :int 20 | attach_function :tb_poll_event, [Event], :int 21 | attach_function :tb_printf, [:int, :int, :int, :int, :string, :varargs], :int 22 | end 23 | 24 | ev = Termbox::Event.new 25 | 26 | Termbox::tb_init 27 | 28 | y = -1 29 | Termbox::tb_printf(0, y += 1, 0x02 | 0x0100, 0x00, "hello from ruby") 30 | Termbox::tb_printf(0, y += 1, 0x03, 0x00, "press any key") 31 | Termbox::tb_present 32 | Termbox::tb_poll_event(ev) 33 | 34 | Termbox::tb_printf(0, y += 1, 0x04, 0x00, 35 | "event: type=%d mod=%d key=%d ch=%d w=%d h=%d x=%d y=%d", 36 | :int, ev[:type], 37 | :int, ev[:mod], 38 | :int, ev[:key], 39 | :int, ev[:ch], 40 | :int, ev[:w], 41 | :int, ev[:h], 42 | :int, ev[:x], 43 | :int, ev[:y]) 44 | Termbox::tb_present 45 | 46 | Termbox::tb_printf(0, y += 1, 0x05, 0x00, "press any key to quit") 47 | Termbox::tb_present 48 | Termbox::tb_poll_event(ev) 49 | 50 | Termbox::tb_shutdown 51 | -------------------------------------------------------------------------------- /demo/example.rs: -------------------------------------------------------------------------------- 1 | // rustc example.rs -L.. -ltermbox2 && LD_LIBRARY_PATH=.. ./example 2 | 3 | use std::os::raw::c_uint; 4 | 5 | #[repr(C)] 6 | #[derive(Default)] 7 | struct TbEvent { 8 | type_: u8, 9 | mod_: u8, 10 | key: u16, 11 | ch: u32, 12 | w: i32, 13 | h: i32, 14 | x: i32, 15 | y: i32, 16 | } 17 | 18 | extern "C" { 19 | fn tb_init() -> i32; 20 | fn tb_shutdown() -> i32; 21 | fn tb_printf(x: i32, y: i32, fg: u32, bg: u32, fmt: *const u8, ...) -> i32; 22 | fn tb_present() -> i32; 23 | fn tb_poll_event(event: *mut TbEvent) -> i32; 24 | } 25 | 26 | fn main() { 27 | unsafe { 28 | main_unsafe(); 29 | } 30 | } 31 | 32 | unsafe fn main_unsafe() { 33 | let mut ev = TbEvent::default(); 34 | 35 | tb_init(); 36 | 37 | let mut y = 0; 38 | tb_printf(0, y, (0x0002 | 0x0100) as u32, 0, "hello world from rust\0".as_ptr() as *const u8); 39 | y += 1; 40 | tb_printf(0, y, 3, 0, "press any key\0".as_ptr() as *const u8); 41 | y += 1; 42 | tb_present(); 43 | tb_poll_event(&mut ev); 44 | 45 | tb_printf(0, y, 4, 0, 46 | "event: type=%d mod=%d key=%d ch=%d w=%d h=%d x=%d y=%d\0".as_ptr() as *const u8, 47 | ev.type_ as c_uint, 48 | ev.mod_ as c_uint, 49 | ev.key as c_uint, 50 | ev.ch, 51 | ev.w, 52 | ev.h, 53 | ev.x, 54 | ev.y, 55 | ); 56 | y += 1; 57 | tb_present(); 58 | 59 | tb_printf(0, y, 5, 0, "press any key to quit\0".as_ptr() as *const u8); 60 | tb_present(); 61 | tb_poll_event(&mut ev); 62 | 63 | tb_shutdown(); 64 | } 65 | -------------------------------------------------------------------------------- /demo/example.zig: -------------------------------------------------------------------------------- 1 | // zig run -I.. -I/usr/include -I/usr/include/x86_64-linux-gnu/ -rpath .. -L.. -ltermbox2 -lc example.zig 2 | // (Is there a way to avoid the explicit system library paths?) 3 | 4 | const c = @cImport(@cInclude("termbox2.h")); 5 | const std = @import("std"); 6 | 7 | pub fn main() void { 8 | _ = c.tb_init(); 9 | var ev = c.tb_event { 10 | .type = 0, 11 | .mod = 0, 12 | .key = 0, 13 | .ch = 0, 14 | .w = 0, 15 | .h = 0, 16 | .x = 0, 17 | .y = 0, 18 | }; 19 | 20 | var y: i32 = 0; 21 | _ = c.tb_printf(0, y, 0x02 | 0x0100, 0x00, "hello from zig"); 22 | y += 1; 23 | _ = c.tb_printf(0, y, 0x03, 0x00, "press any key"); 24 | y += 1; 25 | _ = c.tb_present(); 26 | _ = c.tb_poll_event(&ev); 27 | 28 | _ = c.tb_printf(0, y, 0x04, 0x00, 29 | "event: type=%d mod=%d key=%d ch=%d w=%d h=%d x=%d y=%d", 30 | ev.type, 31 | ev.mod, 32 | ev.key, 33 | ev.ch, 34 | ev.w, 35 | ev.h, 36 | ev.x, 37 | ev.y); 38 | y += 1; 39 | _ = c.tb_present(); 40 | 41 | _ = c.tb_printf(0, y, 0x05, 0x00, "press any key to quit"); 42 | y += 1; 43 | _ = c.tb_present(); 44 | _ = c.tb_poll_event(&ev); 45 | 46 | _ = c.tb_shutdown(); 47 | } 48 | -------------------------------------------------------------------------------- /demo/keyboard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../termbox2.h" 7 | 8 | struct key { 9 | unsigned char x; 10 | unsigned char y; 11 | uint32_t ch; 12 | }; 13 | 14 | #define STOP {0,0,0} 15 | struct key K_ESC[] = {{1,1,'E'},{2,1,'S'},{3,1,'C'},STOP}; 16 | struct key K_F1[] = {{6,1,'F'},{7,1,'1'},STOP}; 17 | struct key K_F2[] = {{9,1,'F'},{10,1,'2'},STOP}; 18 | struct key K_F3[] = {{12,1,'F'},{13,1,'3'},STOP}; 19 | struct key K_F4[] = {{15,1,'F'},{16,1,'4'},STOP}; 20 | struct key K_F5[] = {{19,1,'F'},{20,1,'5'},STOP}; 21 | struct key K_F6[] = {{22,1,'F'},{23,1,'6'},STOP}; 22 | struct key K_F7[] = {{25,1,'F'},{26,1,'7'},STOP}; 23 | struct key K_F8[] = {{28,1,'F'},{29,1,'8'},STOP}; 24 | struct key K_F9[] = {{33,1,'F'},{34,1,'9'},STOP}; 25 | struct key K_F10[] = {{36,1,'F'},{37,1,'1'},{38,1,'0'},STOP}; 26 | struct key K_F11[] = {{40,1,'F'},{41,1,'1'},{42,1,'1'},STOP}; 27 | struct key K_F12[] = {{44,1,'F'},{45,1,'1'},{46,1,'2'},STOP}; 28 | struct key K_PRN[] = {{50,1,'P'},{51,1,'R'},{52,1,'N'},STOP}; 29 | struct key K_SCR[] = {{54,1,'S'},{55,1,'C'},{56,1,'R'},STOP}; 30 | struct key K_BRK[] = {{58,1,'B'},{59,1,'R'},{60,1,'K'},STOP}; 31 | struct key K_LED1[] = {{66,1,'-'},STOP}; 32 | struct key K_LED2[] = {{70,1,'-'},STOP}; 33 | struct key K_LED3[] = {{74,1,'-'},STOP}; 34 | 35 | struct key K_TILDE[] = {{1,4,'`'},STOP}; 36 | struct key K_TILDE_SHIFT[] = {{1,4,'~'},STOP}; 37 | struct key K_1[] = {{4,4,'1'},STOP}; 38 | struct key K_1_SHIFT[] = {{4,4,'!'},STOP}; 39 | struct key K_2[] = {{7,4,'2'},STOP}; 40 | struct key K_2_SHIFT[] = {{7,4,'@'},STOP}; 41 | struct key K_3[] = {{10,4,'3'},STOP}; 42 | struct key K_3_SHIFT[] = {{10,4,'#'},STOP}; 43 | struct key K_4[] = {{13,4,'4'},STOP}; 44 | struct key K_4_SHIFT[] = {{13,4,'$'},STOP}; 45 | struct key K_5[] = {{16,4,'5'},STOP}; 46 | struct key K_5_SHIFT[] = {{16,4,'%'},STOP}; 47 | struct key K_6[] = {{19,4,'6'},STOP}; 48 | struct key K_6_SHIFT[] = {{19,4,'^'},STOP}; 49 | struct key K_7[] = {{22,4,'7'},STOP}; 50 | struct key K_7_SHIFT[] = {{22,4,'&'},STOP}; 51 | struct key K_8[] = {{25,4,'8'},STOP}; 52 | struct key K_8_SHIFT[] = {{25,4,'*'},STOP}; 53 | struct key K_9[] = {{28,4,'9'},STOP}; 54 | struct key K_9_SHIFT[] = {{28,4,'('},STOP}; 55 | struct key K_0[] = {{31,4,'0'},STOP}; 56 | struct key K_0_SHIFT[] = {{31,4,')'},STOP}; 57 | struct key K_MINUS[] = {{34,4,'-'},STOP}; 58 | struct key K_MINUS_SHIFT[] = {{34,4,'_'},STOP}; 59 | struct key K_EQUALS[] = {{37,4,'='},STOP}; 60 | struct key K_EQUALS_SHIFT[] = {{37,4,'+'},STOP}; 61 | struct key K_BACKSLASH[] = {{40,4,'\\'},STOP}; 62 | struct key K_BACKSLASH_SHIFT[] = {{40,4,'|'},STOP}; 63 | struct key K_BACKSPACE[] = {{44,4,0x2190},{45,4,0x2500},{46,4,0x2500},STOP}; 64 | struct key K_INS[] = {{50,4,'I'},{51,4,'N'},{52,4,'S'},STOP}; 65 | struct key K_HOM[] = {{54,4,'H'},{55,4,'O'},{56,4,'M'},STOP}; 66 | struct key K_PGU[] = {{58,4,'P'},{59,4,'G'},{60,4,'U'},STOP}; 67 | struct key K_K_NUMLOCK[] = {{65,4,'N'},STOP}; 68 | struct key K_K_SLASH[] = {{68,4,'/'},STOP}; 69 | struct key K_K_STAR[] = {{71,4,'*'},STOP}; 70 | struct key K_K_MINUS[] = {{74,4,'-'},STOP}; 71 | 72 | struct key K_TAB[] = {{1,6,'T'},{2,6,'A'},{3,6,'B'},STOP}; 73 | struct key K_q[] = {{6,6,'q'},STOP}; 74 | struct key K_Q[] = {{6,6,'Q'},STOP}; 75 | struct key K_w[] = {{9,6,'w'},STOP}; 76 | struct key K_W[] = {{9,6,'W'},STOP}; 77 | struct key K_e[] = {{12,6,'e'},STOP}; 78 | struct key K_E[] = {{12,6,'E'},STOP}; 79 | struct key K_r[] = {{15,6,'r'},STOP}; 80 | struct key K_R[] = {{15,6,'R'},STOP}; 81 | struct key K_t[] = {{18,6,'t'},STOP}; 82 | struct key K_T[] = {{18,6,'T'},STOP}; 83 | struct key K_y[] = {{21,6,'y'},STOP}; 84 | struct key K_Y[] = {{21,6,'Y'},STOP}; 85 | struct key K_u[] = {{24,6,'u'},STOP}; 86 | struct key K_U[] = {{24,6,'U'},STOP}; 87 | struct key K_i[] = {{27,6,'i'},STOP}; 88 | struct key K_I[] = {{27,6,'I'},STOP}; 89 | struct key K_o[] = {{30,6,'o'},STOP}; 90 | struct key K_O[] = {{30,6,'O'},STOP}; 91 | struct key K_p[] = {{33,6,'p'},STOP}; 92 | struct key K_P[] = {{33,6,'P'},STOP}; 93 | struct key K_LSQB[] = {{36,6,'['},STOP}; 94 | struct key K_LCUB[] = {{36,6,'{'},STOP}; 95 | struct key K_RSQB[] = {{39,6,']'},STOP}; 96 | struct key K_RCUB[] = {{39,6,'}'},STOP}; 97 | struct key K_ENTER[] = { 98 | {43,6,0x2591},{44,6,0x2591},{45,6,0x2591},{46,6,0x2591}, 99 | {43,7,0x2591},{44,7,0x2591},{45,7,0x21B5},{46,7,0x2591}, 100 | {41,8,0x2591},{42,8,0x2591},{43,8,0x2591},{44,8,0x2591}, 101 | {45,8,0x2591},{46,8,0x2591},STOP 102 | }; 103 | struct key K_DEL[] = {{50,6,'D'},{51,6,'E'},{52,6,'L'},STOP}; 104 | struct key K_END[] = {{54,6,'E'},{55,6,'N'},{56,6,'D'},STOP}; 105 | struct key K_PGD[] = {{58,6,'P'},{59,6,'G'},{60,6,'D'},STOP}; 106 | struct key K_K_7[] = {{65,6,'7'},STOP}; 107 | struct key K_K_8[] = {{68,6,'8'},STOP}; 108 | struct key K_K_9[] = {{71,6,'9'},STOP}; 109 | struct key K_K_PLUS[] = {{74,6,' '},{74,7,'+'},{74,8,' '},STOP}; 110 | 111 | struct key K_CAPS[] = {{1,8,'C'},{2,8,'A'},{3,8,'P'},{4,8,'S'},STOP}; 112 | struct key K_a[] = {{7,8,'a'},STOP}; 113 | struct key K_A[] = {{7,8,'A'},STOP}; 114 | struct key K_s[] = {{10,8,'s'},STOP}; 115 | struct key K_S[] = {{10,8,'S'},STOP}; 116 | struct key K_d[] = {{13,8,'d'},STOP}; 117 | struct key K_D[] = {{13,8,'D'},STOP}; 118 | struct key K_f[] = {{16,8,'f'},STOP}; 119 | struct key K_F[] = {{16,8,'F'},STOP}; 120 | struct key K_g[] = {{19,8,'g'},STOP}; 121 | struct key K_G[] = {{19,8,'G'},STOP}; 122 | struct key K_h[] = {{22,8,'h'},STOP}; 123 | struct key K_H[] = {{22,8,'H'},STOP}; 124 | struct key K_j[] = {{25,8,'j'},STOP}; 125 | struct key K_J[] = {{25,8,'J'},STOP}; 126 | struct key K_k[] = {{28,8,'k'},STOP}; 127 | struct key K_K[] = {{28,8,'K'},STOP}; 128 | struct key K_l[] = {{31,8,'l'},STOP}; 129 | struct key K_L[] = {{31,8,'L'},STOP}; 130 | struct key K_SEMICOLON[] = {{34,8,';'},STOP}; 131 | struct key K_PARENTHESIS[] = {{34,8,':'},STOP}; 132 | struct key K_QUOTE[] = {{37,8,'\''},STOP}; 133 | struct key K_DOUBLEQUOTE[] = {{37,8,'"'},STOP}; 134 | struct key K_K_4[] = {{65,8,'4'},STOP}; 135 | struct key K_K_5[] = {{68,8,'5'},STOP}; 136 | struct key K_K_6[] = {{71,8,'6'},STOP}; 137 | 138 | struct key K_LSHIFT[] = {{1,10,'S'},{2,10,'H'},{3,10,'I'},{4,10,'F'},{5,10,'T'},STOP}; 139 | struct key K_z[] = {{9,10,'z'},STOP}; 140 | struct key K_Z[] = {{9,10,'Z'},STOP}; 141 | struct key K_x[] = {{12,10,'x'},STOP}; 142 | struct key K_X[] = {{12,10,'X'},STOP}; 143 | struct key K_c[] = {{15,10,'c'},STOP}; 144 | struct key K_C[] = {{15,10,'C'},STOP}; 145 | struct key K_v[] = {{18,10,'v'},STOP}; 146 | struct key K_V[] = {{18,10,'V'},STOP}; 147 | struct key K_b[] = {{21,10,'b'},STOP}; 148 | struct key K_B[] = {{21,10,'B'},STOP}; 149 | struct key K_n[] = {{24,10,'n'},STOP}; 150 | struct key K_N[] = {{24,10,'N'},STOP}; 151 | struct key K_m[] = {{27,10,'m'},STOP}; 152 | struct key K_M[] = {{27,10,'M'},STOP}; 153 | struct key K_COMMA[] = {{30,10,','},STOP}; 154 | struct key K_LANB[] = {{30,10,'<'},STOP}; 155 | struct key K_PERIOD[] = {{33,10,'.'},STOP}; 156 | struct key K_RANB[] = {{33,10,'>'},STOP}; 157 | struct key K_SLASH[] = {{36,10,'/'},STOP}; 158 | struct key K_QUESTION[] = {{36,10,'?'},STOP}; 159 | struct key K_RSHIFT[] = {{42,10,'S'},{43,10,'H'},{44,10,'I'},{45,10,'F'},{46,10,'T'},STOP}; 160 | struct key K_ARROW_UP[] = {{54,10,'('},{55,10,0x2191},{56,10,')'},STOP}; 161 | struct key K_K_1[] = {{65,10,'1'},STOP}; 162 | struct key K_K_2[] = {{68,10,'2'},STOP}; 163 | struct key K_K_3[] = {{71,10,'3'},STOP}; 164 | struct key K_K_ENTER[] = {{74,10,0x2591},{74,11,0x2591},{74,12,0x2591},STOP}; 165 | 166 | struct key K_LCTRL[] = {{1,12,'C'},{2,12,'T'},{3,12,'R'},{4,12,'L'},STOP}; 167 | struct key K_LWIN[] = {{6,12,'W'},{7,12,'I'},{8,12,'N'},STOP}; 168 | struct key K_LALT[] = {{10,12,'A'},{11,12,'L'},{12,12,'T'},STOP}; 169 | struct key K_SPACE[] = { 170 | {14,12,' '},{15,12,' '},{16,12,' '},{17,12,' '},{18,12,' '}, 171 | {19,12,'S'},{20,12,'P'},{21,12,'A'},{22,12,'C'},{23,12,'E'}, 172 | {24,12,' '},{25,12,' '},{26,12,' '},{27,12,' '},{28,12,' '}, 173 | STOP 174 | }; 175 | struct key K_RALT[] = {{30,12,'A'},{31,12,'L'},{32,12,'T'},STOP}; 176 | struct key K_RWIN[] = {{34,12,'W'},{35,12,'I'},{36,12,'N'},STOP}; 177 | struct key K_RPROP[] = {{38,12,'P'},{39,12,'R'},{40,12,'O'},{41,12,'P'},STOP}; 178 | struct key K_RCTRL[] = {{43,12,'C'},{44,12,'T'},{45,12,'R'},{46,12,'L'},STOP}; 179 | struct key K_ARROW_LEFT[] = {{50,12,'('},{51,12,0x2190},{52,12,')'},STOP}; 180 | struct key K_ARROW_DOWN[] = {{54,12,'('},{55,12,0x2193},{56,12,')'},STOP}; 181 | struct key K_ARROW_RIGHT[] = {{58,12,'('},{59,12,0x2192},{60,12,')'},STOP}; 182 | struct key K_K_0[] = {{65,12,' '},{66,12,'0'},{67,12,' '},{68,12,' '},STOP}; 183 | struct key K_K_PERIOD[] = {{71,12,'.'},STOP}; 184 | 185 | struct combo { 186 | struct key *keys[6]; 187 | }; 188 | 189 | struct combo combos[] = { 190 | {{K_TILDE, K_2, K_LCTRL, K_RCTRL, 0}}, 191 | {{K_A, K_LCTRL, K_RCTRL, 0}}, 192 | {{K_B, K_LCTRL, K_RCTRL, 0}}, 193 | {{K_C, K_LCTRL, K_RCTRL, 0}}, 194 | {{K_D, K_LCTRL, K_RCTRL, 0}}, 195 | {{K_E, K_LCTRL, K_RCTRL, 0}}, 196 | {{K_F, K_LCTRL, K_RCTRL, 0}}, 197 | {{K_G, K_LCTRL, K_RCTRL, 0}}, 198 | {{K_H, K_BACKSPACE, K_LCTRL, K_RCTRL, 0}}, 199 | {{K_I, K_TAB, K_LCTRL, K_RCTRL, 0}}, 200 | {{K_J, K_LCTRL, K_RCTRL, 0}}, 201 | {{K_K, K_LCTRL, K_RCTRL, 0}}, 202 | {{K_L, K_LCTRL, K_RCTRL, 0}}, 203 | {{K_M, K_ENTER, K_K_ENTER, K_LCTRL, K_RCTRL, 0}}, 204 | {{K_N, K_LCTRL, K_RCTRL, 0}}, 205 | {{K_O, K_LCTRL, K_RCTRL, 0}}, 206 | {{K_P, K_LCTRL, K_RCTRL, 0}}, 207 | {{K_Q, K_LCTRL, K_RCTRL, 0}}, 208 | {{K_R, K_LCTRL, K_RCTRL, 0}}, 209 | {{K_S, K_LCTRL, K_RCTRL, 0}}, 210 | {{K_T, K_LCTRL, K_RCTRL, 0}}, 211 | {{K_U, K_LCTRL, K_RCTRL, 0}}, 212 | {{K_V, K_LCTRL, K_RCTRL, 0}}, 213 | {{K_W, K_LCTRL, K_RCTRL, 0}}, 214 | {{K_X, K_LCTRL, K_RCTRL, 0}}, 215 | {{K_Y, K_LCTRL, K_RCTRL, 0}}, 216 | {{K_Z, K_LCTRL, K_RCTRL, 0}}, 217 | {{K_LSQB, K_ESC, K_3, K_LCTRL, K_RCTRL, 0}}, 218 | {{K_4, K_BACKSLASH, K_LCTRL, K_RCTRL, 0}}, 219 | {{K_RSQB, K_5, K_LCTRL, K_RCTRL, 0}}, 220 | {{K_6, K_LCTRL, K_RCTRL, 0}}, 221 | {{K_7, K_SLASH, K_MINUS_SHIFT, K_LCTRL, K_RCTRL, 0}}, 222 | {{K_SPACE,0}}, 223 | {{K_1_SHIFT,K_LSHIFT,K_RSHIFT,0}}, 224 | {{K_DOUBLEQUOTE,K_LSHIFT,K_RSHIFT,0}}, 225 | {{K_3_SHIFT,K_LSHIFT,K_RSHIFT,0}}, 226 | {{K_4_SHIFT,K_LSHIFT,K_RSHIFT,0}}, 227 | {{K_5_SHIFT,K_LSHIFT,K_RSHIFT,0}}, 228 | {{K_7_SHIFT,K_LSHIFT,K_RSHIFT,0}}, 229 | {{K_QUOTE,0}}, 230 | {{K_9_SHIFT,K_LSHIFT,K_RSHIFT,0}}, 231 | {{K_0_SHIFT,K_LSHIFT,K_RSHIFT,0}}, 232 | {{K_8_SHIFT,K_K_STAR,K_LSHIFT,K_RSHIFT,0}}, 233 | {{K_EQUALS_SHIFT,K_K_PLUS,K_LSHIFT,K_RSHIFT,0}}, 234 | {{K_COMMA,0}}, 235 | {{K_MINUS,K_K_MINUS,0}}, 236 | {{K_PERIOD,K_K_PERIOD,0}}, 237 | {{K_SLASH,K_K_SLASH,0}}, 238 | {{K_0,K_K_0,0}}, 239 | {{K_1,K_K_1,0}}, 240 | {{K_2,K_K_2,0}}, 241 | {{K_3,K_K_3,0}}, 242 | {{K_4,K_K_4,0}}, 243 | {{K_5,K_K_5,0}}, 244 | {{K_6,K_K_6,0}}, 245 | {{K_7,K_K_7,0}}, 246 | {{K_8,K_K_8,0}}, 247 | {{K_9,K_K_9,0}}, 248 | {{K_PARENTHESIS,K_LSHIFT,K_RSHIFT,0}}, 249 | {{K_SEMICOLON,0}}, 250 | {{K_LANB,K_LSHIFT,K_RSHIFT,0}}, 251 | {{K_EQUALS,0}}, 252 | {{K_RANB,K_LSHIFT,K_RSHIFT,0}}, 253 | {{K_QUESTION,K_LSHIFT,K_RSHIFT,0}}, 254 | {{K_2_SHIFT,K_LSHIFT,K_RSHIFT,0}}, 255 | {{K_A,K_LSHIFT,K_RSHIFT,0}}, 256 | {{K_B,K_LSHIFT,K_RSHIFT,0}}, 257 | {{K_C,K_LSHIFT,K_RSHIFT,0}}, 258 | {{K_D,K_LSHIFT,K_RSHIFT,0}}, 259 | {{K_E,K_LSHIFT,K_RSHIFT,0}}, 260 | {{K_F,K_LSHIFT,K_RSHIFT,0}}, 261 | {{K_G,K_LSHIFT,K_RSHIFT,0}}, 262 | {{K_H,K_LSHIFT,K_RSHIFT,0}}, 263 | {{K_I,K_LSHIFT,K_RSHIFT,0}}, 264 | {{K_J,K_LSHIFT,K_RSHIFT,0}}, 265 | {{K_K,K_LSHIFT,K_RSHIFT,0}}, 266 | {{K_L,K_LSHIFT,K_RSHIFT,0}}, 267 | {{K_M,K_LSHIFT,K_RSHIFT,0}}, 268 | {{K_N,K_LSHIFT,K_RSHIFT,0}}, 269 | {{K_O,K_LSHIFT,K_RSHIFT,0}}, 270 | {{K_P,K_LSHIFT,K_RSHIFT,0}}, 271 | {{K_Q,K_LSHIFT,K_RSHIFT,0}}, 272 | {{K_R,K_LSHIFT,K_RSHIFT,0}}, 273 | {{K_S,K_LSHIFT,K_RSHIFT,0}}, 274 | {{K_T,K_LSHIFT,K_RSHIFT,0}}, 275 | {{K_U,K_LSHIFT,K_RSHIFT,0}}, 276 | {{K_V,K_LSHIFT,K_RSHIFT,0}}, 277 | {{K_W,K_LSHIFT,K_RSHIFT,0}}, 278 | {{K_X,K_LSHIFT,K_RSHIFT,0}}, 279 | {{K_Y,K_LSHIFT,K_RSHIFT,0}}, 280 | {{K_Z,K_LSHIFT,K_RSHIFT,0}}, 281 | {{K_LSQB,0}}, 282 | {{K_BACKSLASH,0}}, 283 | {{K_RSQB,0}}, 284 | {{K_6_SHIFT,K_LSHIFT,K_RSHIFT,0}}, 285 | {{K_MINUS_SHIFT,K_LSHIFT,K_RSHIFT,0}}, 286 | {{K_TILDE,0}}, 287 | {{K_a,0}}, 288 | {{K_b,0}}, 289 | {{K_c,0}}, 290 | {{K_d,0}}, 291 | {{K_e,0}}, 292 | {{K_f,0}}, 293 | {{K_g,0}}, 294 | {{K_h,0}}, 295 | {{K_i,0}}, 296 | {{K_j,0}}, 297 | {{K_k,0}}, 298 | {{K_l,0}}, 299 | {{K_m,0}}, 300 | {{K_n,0}}, 301 | {{K_o,0}}, 302 | {{K_p,0}}, 303 | {{K_q,0}}, 304 | {{K_r,0}}, 305 | {{K_s,0}}, 306 | {{K_t,0}}, 307 | {{K_u,0}}, 308 | {{K_v,0}}, 309 | {{K_w,0}}, 310 | {{K_x,0}}, 311 | {{K_y,0}}, 312 | {{K_z,0}}, 313 | {{K_LCUB,K_LSHIFT,K_RSHIFT,0}}, 314 | {{K_BACKSLASH_SHIFT,K_LSHIFT,K_RSHIFT,0}}, 315 | {{K_RCUB,K_LSHIFT,K_RSHIFT,0}}, 316 | {{K_TILDE_SHIFT,K_LSHIFT,K_RSHIFT,0}}, 317 | {{K_8, K_BACKSPACE, K_LCTRL, K_RCTRL, 0}} 318 | }; 319 | 320 | struct combo func_combos[] = { 321 | {{K_F1,0}}, 322 | {{K_F2,0}}, 323 | {{K_F3,0}}, 324 | {{K_F4,0}}, 325 | {{K_F5,0}}, 326 | {{K_F6,0}}, 327 | {{K_F7,0}}, 328 | {{K_F8,0}}, 329 | {{K_F9,0}}, 330 | {{K_F10,0}}, 331 | {{K_F11,0}}, 332 | {{K_F12,0}}, 333 | {{K_INS,0}}, 334 | {{K_DEL,0}}, 335 | {{K_HOM,0}}, 336 | {{K_END,0}}, 337 | {{K_PGU,0}}, 338 | {{K_PGD,0}}, 339 | {{K_ARROW_UP,0}}, 340 | {{K_ARROW_DOWN,0}}, 341 | {{K_ARROW_LEFT,0}}, 342 | {{K_ARROW_RIGHT,0}} 343 | }; 344 | 345 | void print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg) 346 | { 347 | while (*str) { 348 | uint32_t uni; 349 | str += tb_utf8_char_to_unicode(&uni, str); 350 | tb_set_cell(x, y, uni, fg, bg); 351 | x++; 352 | } 353 | } 354 | 355 | void printf_tb(int x, int y, uint16_t fg, uint16_t bg, const char *fmt, ...) 356 | { 357 | char buf[4096]; 358 | va_list vl; 359 | va_start(vl, fmt); 360 | vsnprintf(buf, sizeof(buf), fmt, vl); 361 | va_end(vl); 362 | print_tb(buf, x, y, fg, bg); 363 | } 364 | 365 | void draw_key(struct key *k, uint16_t fg, uint16_t bg) 366 | { 367 | while (k->x) { 368 | tb_set_cell(k->x+2, k->y+4, k->ch, fg, bg); 369 | k++; 370 | } 371 | } 372 | 373 | void draw_keyboard(void) 374 | { 375 | int i; 376 | tb_set_cell(0, 0, 0x250C, TB_WHITE, TB_DEFAULT); 377 | tb_set_cell(79, 0, 0x2510, TB_WHITE, TB_DEFAULT); 378 | tb_set_cell(0, 23, 0x2514, TB_WHITE, TB_DEFAULT); 379 | tb_set_cell(79, 23, 0x2518, TB_WHITE, TB_DEFAULT); 380 | 381 | for (i = 1; i < 79; ++i) { 382 | tb_set_cell(i, 0, 0x2500, TB_WHITE, TB_DEFAULT); 383 | tb_set_cell(i, 23, 0x2500, TB_WHITE, TB_DEFAULT); 384 | tb_set_cell(i, 17, 0x2500, TB_WHITE, TB_DEFAULT); 385 | tb_set_cell(i, 4, 0x2500, TB_WHITE, TB_DEFAULT); 386 | } 387 | for (i = 1; i < 23; ++i) { 388 | tb_set_cell(0, i, 0x2502, TB_WHITE, TB_DEFAULT); 389 | tb_set_cell(79, i, 0x2502, TB_WHITE, TB_DEFAULT); 390 | } 391 | tb_set_cell(0, 17, 0x251C, TB_WHITE, TB_DEFAULT); 392 | tb_set_cell(79, 17, 0x2524, TB_WHITE, TB_DEFAULT); 393 | tb_set_cell(0, 4, 0x251C, TB_WHITE, TB_DEFAULT); 394 | tb_set_cell(79, 4, 0x2524, TB_WHITE, TB_DEFAULT); 395 | for (i = 5; i < 17; ++i) { 396 | tb_set_cell(1, i, 0x2588, TB_YELLOW, TB_YELLOW); 397 | tb_set_cell(78, i, 0x2588, TB_YELLOW, TB_YELLOW); 398 | } 399 | 400 | draw_key(K_ESC, TB_WHITE, TB_BLUE); 401 | draw_key(K_F1, TB_WHITE, TB_BLUE); 402 | draw_key(K_F2, TB_WHITE, TB_BLUE); 403 | draw_key(K_F3, TB_WHITE, TB_BLUE); 404 | draw_key(K_F4, TB_WHITE, TB_BLUE); 405 | draw_key(K_F5, TB_WHITE, TB_BLUE); 406 | draw_key(K_F6, TB_WHITE, TB_BLUE); 407 | draw_key(K_F7, TB_WHITE, TB_BLUE); 408 | draw_key(K_F8, TB_WHITE, TB_BLUE); 409 | draw_key(K_F9, TB_WHITE, TB_BLUE); 410 | draw_key(K_F10, TB_WHITE, TB_BLUE); 411 | draw_key(K_F11, TB_WHITE, TB_BLUE); 412 | draw_key(K_F12, TB_WHITE, TB_BLUE); 413 | draw_key(K_PRN, TB_WHITE, TB_BLUE); 414 | draw_key(K_SCR, TB_WHITE, TB_BLUE); 415 | draw_key(K_BRK, TB_WHITE, TB_BLUE); 416 | draw_key(K_LED1, TB_WHITE, TB_BLUE); 417 | draw_key(K_LED2, TB_WHITE, TB_BLUE); 418 | draw_key(K_LED3, TB_WHITE, TB_BLUE); 419 | 420 | draw_key(K_TILDE, TB_WHITE, TB_BLUE); 421 | draw_key(K_1, TB_WHITE, TB_BLUE); 422 | draw_key(K_2, TB_WHITE, TB_BLUE); 423 | draw_key(K_3, TB_WHITE, TB_BLUE); 424 | draw_key(K_4, TB_WHITE, TB_BLUE); 425 | draw_key(K_5, TB_WHITE, TB_BLUE); 426 | draw_key(K_6, TB_WHITE, TB_BLUE); 427 | draw_key(K_7, TB_WHITE, TB_BLUE); 428 | draw_key(K_8, TB_WHITE, TB_BLUE); 429 | draw_key(K_9, TB_WHITE, TB_BLUE); 430 | draw_key(K_0, TB_WHITE, TB_BLUE); 431 | draw_key(K_MINUS, TB_WHITE, TB_BLUE); 432 | draw_key(K_EQUALS, TB_WHITE, TB_BLUE); 433 | draw_key(K_BACKSLASH, TB_WHITE, TB_BLUE); 434 | draw_key(K_BACKSPACE, TB_WHITE, TB_BLUE); 435 | draw_key(K_INS, TB_WHITE, TB_BLUE); 436 | draw_key(K_HOM, TB_WHITE, TB_BLUE); 437 | draw_key(K_PGU, TB_WHITE, TB_BLUE); 438 | draw_key(K_K_NUMLOCK, TB_WHITE, TB_BLUE); 439 | draw_key(K_K_SLASH, TB_WHITE, TB_BLUE); 440 | draw_key(K_K_STAR, TB_WHITE, TB_BLUE); 441 | draw_key(K_K_MINUS, TB_WHITE, TB_BLUE); 442 | 443 | draw_key(K_TAB, TB_WHITE, TB_BLUE); 444 | draw_key(K_q, TB_WHITE, TB_BLUE); 445 | draw_key(K_w, TB_WHITE, TB_BLUE); 446 | draw_key(K_e, TB_WHITE, TB_BLUE); 447 | draw_key(K_r, TB_WHITE, TB_BLUE); 448 | draw_key(K_t, TB_WHITE, TB_BLUE); 449 | draw_key(K_y, TB_WHITE, TB_BLUE); 450 | draw_key(K_u, TB_WHITE, TB_BLUE); 451 | draw_key(K_i, TB_WHITE, TB_BLUE); 452 | draw_key(K_o, TB_WHITE, TB_BLUE); 453 | draw_key(K_p, TB_WHITE, TB_BLUE); 454 | draw_key(K_LSQB, TB_WHITE, TB_BLUE); 455 | draw_key(K_RSQB, TB_WHITE, TB_BLUE); 456 | draw_key(K_ENTER, TB_WHITE, TB_BLUE); 457 | draw_key(K_DEL, TB_WHITE, TB_BLUE); 458 | draw_key(K_END, TB_WHITE, TB_BLUE); 459 | draw_key(K_PGD, TB_WHITE, TB_BLUE); 460 | draw_key(K_K_7, TB_WHITE, TB_BLUE); 461 | draw_key(K_K_8, TB_WHITE, TB_BLUE); 462 | draw_key(K_K_9, TB_WHITE, TB_BLUE); 463 | draw_key(K_K_PLUS, TB_WHITE, TB_BLUE); 464 | 465 | draw_key(K_CAPS, TB_WHITE, TB_BLUE); 466 | draw_key(K_a, TB_WHITE, TB_BLUE); 467 | draw_key(K_s, TB_WHITE, TB_BLUE); 468 | draw_key(K_d, TB_WHITE, TB_BLUE); 469 | draw_key(K_f, TB_WHITE, TB_BLUE); 470 | draw_key(K_g, TB_WHITE, TB_BLUE); 471 | draw_key(K_h, TB_WHITE, TB_BLUE); 472 | draw_key(K_j, TB_WHITE, TB_BLUE); 473 | draw_key(K_k, TB_WHITE, TB_BLUE); 474 | draw_key(K_l, TB_WHITE, TB_BLUE); 475 | draw_key(K_SEMICOLON, TB_WHITE, TB_BLUE); 476 | draw_key(K_QUOTE, TB_WHITE, TB_BLUE); 477 | draw_key(K_K_4, TB_WHITE, TB_BLUE); 478 | draw_key(K_K_5, TB_WHITE, TB_BLUE); 479 | draw_key(K_K_6, TB_WHITE, TB_BLUE); 480 | 481 | draw_key(K_LSHIFT, TB_WHITE, TB_BLUE); 482 | draw_key(K_z, TB_WHITE, TB_BLUE); 483 | draw_key(K_x, TB_WHITE, TB_BLUE); 484 | draw_key(K_c, TB_WHITE, TB_BLUE); 485 | draw_key(K_v, TB_WHITE, TB_BLUE); 486 | draw_key(K_b, TB_WHITE, TB_BLUE); 487 | draw_key(K_n, TB_WHITE, TB_BLUE); 488 | draw_key(K_m, TB_WHITE, TB_BLUE); 489 | draw_key(K_COMMA, TB_WHITE, TB_BLUE); 490 | draw_key(K_PERIOD, TB_WHITE, TB_BLUE); 491 | draw_key(K_SLASH, TB_WHITE, TB_BLUE); 492 | draw_key(K_RSHIFT, TB_WHITE, TB_BLUE); 493 | draw_key(K_ARROW_UP, TB_WHITE, TB_BLUE); 494 | draw_key(K_K_1, TB_WHITE, TB_BLUE); 495 | draw_key(K_K_2, TB_WHITE, TB_BLUE); 496 | draw_key(K_K_3, TB_WHITE, TB_BLUE); 497 | draw_key(K_K_ENTER, TB_WHITE, TB_BLUE); 498 | 499 | draw_key(K_LCTRL, TB_WHITE, TB_BLUE); 500 | draw_key(K_LWIN, TB_WHITE, TB_BLUE); 501 | draw_key(K_LALT, TB_WHITE, TB_BLUE); 502 | draw_key(K_SPACE, TB_WHITE, TB_BLUE); 503 | draw_key(K_RCTRL, TB_WHITE, TB_BLUE); 504 | draw_key(K_RPROP, TB_WHITE, TB_BLUE); 505 | draw_key(K_RWIN, TB_WHITE, TB_BLUE); 506 | draw_key(K_RALT, TB_WHITE, TB_BLUE); 507 | draw_key(K_ARROW_LEFT, TB_WHITE, TB_BLUE); 508 | draw_key(K_ARROW_DOWN, TB_WHITE, TB_BLUE); 509 | draw_key(K_ARROW_RIGHT, TB_WHITE, TB_BLUE); 510 | draw_key(K_K_0, TB_WHITE, TB_BLUE); 511 | draw_key(K_K_PERIOD, TB_WHITE, TB_BLUE); 512 | 513 | printf_tb(33, 1, (uint16_t)(TB_MAGENTA | TB_BOLD), TB_DEFAULT, "Keyboard demo!"); 514 | printf_tb(21, 2, TB_MAGENTA, TB_DEFAULT, "(press CTRL+X and then CTRL+Q to exit)"); 515 | printf_tb(15, 3, TB_MAGENTA, TB_DEFAULT, "(press CTRL+X and then CTRL+C to change input mode)"); 516 | 517 | int inputmode = tb_set_input_mode(0); 518 | char inputmode_str[64]; 519 | 520 | if (inputmode & TB_INPUT_ESC) 521 | sprintf(inputmode_str, "TB_INPUT_ESC"); 522 | if (inputmode & TB_INPUT_ALT) 523 | sprintf(inputmode_str, "TB_INPUT_ALT"); 524 | if (inputmode & TB_INPUT_MOUSE) 525 | sprintf(inputmode_str + 12, " | TB_INPUT_MOUSE"); 526 | 527 | printf_tb(3, 18, TB_WHITE, TB_DEFAULT, "Input mode: %s", inputmode_str); 528 | } 529 | 530 | const char *funckeymap(int k) 531 | { 532 | static const char *fcmap[] = { 533 | "CTRL+2, CTRL+~", 534 | "CTRL+A", 535 | "CTRL+B", 536 | "CTRL+C", 537 | "CTRL+D", 538 | "CTRL+E", 539 | "CTRL+F", 540 | "CTRL+G", 541 | "CTRL+H, BACKSPACE", 542 | "CTRL+I, TAB", 543 | "CTRL+J", 544 | "CTRL+K", 545 | "CTRL+L", 546 | "CTRL+M, ENTER", 547 | "CTRL+N", 548 | "CTRL+O", 549 | "CTRL+P", 550 | "CTRL+Q", 551 | "CTRL+R", 552 | "CTRL+S", 553 | "CTRL+T", 554 | "CTRL+U", 555 | "CTRL+V", 556 | "CTRL+W", 557 | "CTRL+X", 558 | "CTRL+Y", 559 | "CTRL+Z", 560 | "CTRL+3, ESC, CTRL+[", 561 | "CTRL+4, CTRL+\\", 562 | "CTRL+5, CTRL+]", 563 | "CTRL+6", 564 | "CTRL+7, CTRL+/, CTRL+_", 565 | "SPACE" 566 | }; 567 | static const char *fkmap[] = { 568 | "F1", 569 | "F2", 570 | "F3", 571 | "F4", 572 | "F5", 573 | "F6", 574 | "F7", 575 | "F8", 576 | "F9", 577 | "F10", 578 | "F11", 579 | "F12", 580 | "INSERT", 581 | "DELETE", 582 | "HOME", 583 | "END", 584 | "PGUP", 585 | "PGDN", 586 | "ARROW UP", 587 | "ARROW DOWN", 588 | "ARROW LEFT", 589 | "ARROW RIGHT" 590 | }; 591 | 592 | if (k == TB_KEY_CTRL_8) 593 | return "CTRL+8, BACKSPACE 2"; /* 0x7F */ 594 | else if (k >= TB_KEY_ARROW_RIGHT && k <= 0xFFFF) 595 | return fkmap[0xFFFF-k]; 596 | else if (k <= TB_KEY_SPACE) 597 | return fcmap[k]; 598 | return "UNKNOWN"; 599 | } 600 | 601 | void pretty_print_press(struct tb_event *ev) 602 | { 603 | char buf[7]; 604 | buf[tb_utf8_unicode_to_char(buf, ev->ch)] = '\0'; 605 | printf_tb(3, 19, TB_WHITE , TB_DEFAULT, "Key: "); 606 | printf_tb(8, 19, TB_YELLOW, TB_DEFAULT, "decimal: %d", ev->key); 607 | printf_tb(8, 20, TB_GREEN , TB_DEFAULT, "hex: 0x%X", ev->key); 608 | printf_tb(8, 21, TB_CYAN , TB_DEFAULT, "octal: 0%o", ev->key); 609 | printf_tb(8, 22, TB_RED , TB_DEFAULT, "string: %s", funckeymap(ev->key)); 610 | 611 | printf_tb(54, 19, TB_WHITE , TB_DEFAULT, "Char: "); 612 | printf_tb(60, 19, TB_YELLOW, TB_DEFAULT, "decimal: %d", ev->ch); 613 | printf_tb(60, 20, TB_GREEN , TB_DEFAULT, "hex: 0x%X", ev->ch); 614 | printf_tb(60, 21, TB_CYAN , TB_DEFAULT, "octal: 0%o", ev->ch); 615 | printf_tb(60, 22, TB_RED , TB_DEFAULT, "string: %s", buf); 616 | 617 | printf_tb(54, 18, TB_WHITE, TB_DEFAULT, "Modifier: %c%c%c%c", 618 | (ev->mod & TB_MOD_CTRL) ? 'C' : ' ', 619 | (ev->mod & TB_MOD_ALT) ? 'A' : ' ', 620 | (ev->mod & TB_MOD_SHIFT) ? 'S' : ' ', 621 | (ev->mod & TB_MOD_MOTION) ? 'M' : ' '); 622 | 623 | } 624 | 625 | void pretty_print_resize(struct tb_event *ev) 626 | { 627 | printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Resize event: %d x %d", ev->w, ev->h); 628 | } 629 | 630 | int counter = 0; 631 | 632 | void pretty_print_mouse(struct tb_event *ev) { 633 | printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Mouse event: %d x %d %c", ev->x, ev->y, (ev->mod & TB_MOD_MOTION) ? '*' : ' '); 634 | char *btn = ""; 635 | switch (ev->key) { 636 | case TB_KEY_MOUSE_LEFT: 637 | btn = "MouseLeft: %d"; 638 | break; 639 | case TB_KEY_MOUSE_MIDDLE: 640 | btn = "MouseMiddle: %d"; 641 | break; 642 | case TB_KEY_MOUSE_RIGHT: 643 | btn = "MouseRight: %d"; 644 | break; 645 | case TB_KEY_MOUSE_WHEEL_UP: 646 | btn = "MouseWheelUp: %d"; 647 | break; 648 | case TB_KEY_MOUSE_WHEEL_DOWN: 649 | btn = "MouseWheelDown: %d"; 650 | break; 651 | case TB_KEY_MOUSE_RELEASE: 652 | btn = "MouseRelease: %d"; 653 | } 654 | counter++; 655 | printf_tb(43, 19, TB_WHITE, TB_DEFAULT, "Key: "); 656 | printf_tb(48, 19, TB_YELLOW, TB_DEFAULT, btn, counter); 657 | } 658 | 659 | void dispatch_press(struct tb_event *ev) 660 | { 661 | if (ev->mod & TB_MOD_ALT) { 662 | draw_key(K_LALT, TB_WHITE, TB_RED); 663 | draw_key(K_RALT, TB_WHITE, TB_RED); 664 | } 665 | if (ev->mod & TB_MOD_CTRL) { 666 | draw_key(K_LCTRL, TB_WHITE, TB_RED); 667 | draw_key(K_RCTRL, TB_WHITE, TB_RED); 668 | } 669 | if (ev->mod & TB_MOD_SHIFT) { 670 | draw_key(K_LSHIFT, TB_WHITE, TB_RED); 671 | draw_key(K_RSHIFT, TB_WHITE, TB_RED); 672 | } 673 | 674 | struct combo *k = 0; 675 | if (ev->key >= TB_KEY_ARROW_RIGHT) 676 | k = &func_combos[0xFFFF-ev->key]; 677 | else if (ev->ch < 128) { 678 | if (ev->ch == 0 && ev->key < 128) 679 | k = &combos[ev->key]; 680 | else 681 | k = &combos[ev->ch]; 682 | } 683 | if (!k) 684 | return; 685 | 686 | struct key **keys = k->keys; 687 | while (*keys) { 688 | draw_key(*keys, TB_WHITE, TB_RED); 689 | keys++; 690 | } 691 | } 692 | 693 | int main(int argc, char **argv) 694 | { 695 | (void) argc; (void) argv; 696 | int ret; 697 | 698 | setlocale(LC_ALL, ""); 699 | 700 | ret = tb_init(); 701 | if (ret) { 702 | fprintf(stderr, "tb_init() failed with error code %d\n", ret); 703 | return 1; 704 | } 705 | 706 | tb_set_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE); 707 | struct tb_event ev; 708 | 709 | tb_clear(); 710 | draw_keyboard(); 711 | tb_present(); 712 | int inputmode = 0; 713 | int ctrlxpressed = 0; 714 | 715 | while (1) { 716 | ret = tb_poll_event(&ev); 717 | 718 | if (ret != TB_OK) { 719 | if (ret == TB_ERR_POLL && tb_last_errno() == EINTR) { 720 | /* poll was interrupted, maybe by a SIGWINCH; try again */ 721 | continue; 722 | } 723 | /* some other error occurred; bail */ 724 | break; 725 | } 726 | 727 | switch (ev.type) { 728 | case TB_EVENT_KEY: 729 | if (ev.key == TB_KEY_CTRL_Q && ctrlxpressed) { 730 | tb_shutdown(); 731 | return 0; 732 | } 733 | if (ev.key == TB_KEY_CTRL_C && ctrlxpressed) { 734 | static int chmap[] = { 735 | TB_INPUT_ESC | TB_INPUT_MOUSE, /* 101 */ 736 | TB_INPUT_ALT | TB_INPUT_MOUSE, /* 110 */ 737 | TB_INPUT_ESC, /* 001 */ 738 | TB_INPUT_ALT, /* 010 */ 739 | }; 740 | inputmode++; 741 | if (inputmode >= 4) { 742 | inputmode = 0; 743 | } 744 | tb_set_input_mode(chmap[inputmode]); 745 | } 746 | if (ev.key == TB_KEY_CTRL_X) 747 | ctrlxpressed = 1; 748 | else 749 | ctrlxpressed = 0; 750 | 751 | tb_clear(); 752 | draw_keyboard(); 753 | dispatch_press(&ev); 754 | pretty_print_press(&ev); 755 | tb_present(); 756 | break; 757 | case TB_EVENT_RESIZE: 758 | tb_clear(); 759 | draw_keyboard(); 760 | pretty_print_resize(&ev); 761 | tb_present(); 762 | break; 763 | case TB_EVENT_MOUSE: 764 | tb_clear(); 765 | draw_keyboard(); 766 | pretty_print_mouse(&ev); 767 | tb_present(); 768 | break; 769 | default: 770 | break; 771 | } 772 | } 773 | tb_shutdown(); 774 | return 0; 775 | } 776 | -------------------------------------------------------------------------------- /demo/keyboard.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/termbox/termbox2/8ee9dc17e1ca61c630f91db0aa7f81fa29a32040/demo/keyboard.gif -------------------------------------------------------------------------------- /tests/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:trixie-slim 2 | ARG cflags="" 3 | RUN export DEBIAN_FRONTEND=noninteractive \ 4 | && apt-get -y update >/dev/null \ 5 | && apt-get -y install lsb-release apt-transport-https ca-certificates wget >/dev/null \ 6 | && wget -qO /usr/share/keyrings/sury-php.gpg 'https://packages.sury.org/php/apt.gpg' \ 7 | && echo "deb [signed-by=/usr/share/keyrings/sury-php.gpg] https://packages.sury.org/php/ bookworm main" | \ 8 | tee /etc/apt/sources.list.d/php.list \ 9 | && apt-get -y update >/dev/null \ 10 | && apt-get -y install make gcc php8.4-cli xvfb xterm xvkbd locales locales-all >/dev/null 11 | ENV LC_ALL=en_US.UTF-8 \ 12 | LANG=en_US.UTF-8 \ 13 | LANGUAGE=en_US.UTF-8 14 | COPY . /termbox 15 | WORKDIR /termbox 16 | RUN CFLAGS="${cflags}" make clean test_local 17 | -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -uo pipefail 3 | 4 | main() { 5 | local timeout_s=5 6 | local x_display=':1000' 7 | local xvfb_pipe='/tmp/.X11-unix/X1000' 8 | local this_dir="$(cd $(dirname "${BASH_SOURCE[0]}") &>/dev/null && pwd)/" 9 | local xterm_geom='80x24+0+0' 10 | local xterm_bg='grey3' 11 | local xterm_fg='grey93' 12 | local single_test_name=${TERMBOX2_TEST_NAME:-} 13 | local main_ec=0 14 | 15 | check_deps 16 | 17 | # loop through each 'test_*' dir 18 | for test_php in $(find . -type f -wholename '*/test_*/test.php' | sort); do 19 | local test_dir=$(dirname $test_php) 20 | local test_name=$(basename $test_dir) 21 | 22 | # check if we are running a specific test (TERMBOX2_TEST_NAME) 23 | if [ -n "$single_test_name" -a "$single_test_name" != "$test_name" ]; then 24 | continue 25 | fi 26 | 27 | # begin 28 | echo -e "\x1b[1m$test_name\x1b[0m: BEGIN" 29 | 30 | # make log file 31 | local test_log_cmd=$(mktemp '/tmp/tb_test.XXXXXXXXXX') 32 | local test_log_xterm="${test_log_cmd}.xterm" 33 | local test_log_php="${test_log_cmd}.php" 34 | 35 | # run Xvfb (headless X server) 36 | echo -n ' waiting for Xvfb slot...' 37 | while test -e $xvfb_pipe; do echo -n .; sleep 0.1; done 38 | echo 39 | Xvfb -screen 0 800x600x24 $x_display &>/dev/null & 40 | local xvfb_pid=$! 41 | 42 | # wait for Xvfb to come up 43 | echo -n ' starting Xvfb...' 44 | local max_wait=30 45 | while ! test -e $xvfb_pipe; do 46 | echo -n . 47 | max_wait=$((max_wait-1)) 48 | [ "$max_wait" -le 0 ] && break 49 | sleep 1 50 | done 51 | echo 52 | 53 | # run test_bin in xterm in Xvfb 54 | echo ' running test in xterm' 55 | xterm -display $x_display \ 56 | -u8 -geometry $xterm_geom -bg $xterm_bg -fg $xterm_fg \ 57 | -xrm 'xterm*metaSendsEscape:true' \ 58 | -xrm 'xterm*translations:#override\nShift Home:print-immediate()' \ 59 | -xrm 'xterm*printOptsImmediate:1' \ 60 | -xrm 'xterm*printModeImmediate:2' \ 61 | -xrm "xterm*printFileImmediate:$test_log_xterm" \ 62 | -e "php -d auto_prepend_file=$this_dir/test_prepend.php $test_php $test_log_cmd &>$test_log_php" \ 63 | &>/dev/null & 64 | local xterm_pid=$! 65 | 66 | # tail test_log_cmd until we see 'screencap' 67 | local test_log_cursor=0 68 | local test_log_size=0 69 | local test_end_ts=$(($(date +%s) + $timeout_s)) 70 | local test_skipped=0 71 | while true; do 72 | test_log_size=$(stat -c %s $test_log_cmd 2>/dev/null) 73 | [ -z "$test_log_size" ] && break # stat failed or deleted 74 | [ "$test_log_size" -lt "$test_log_cursor" ] && break # truncated 75 | local test_log_content=$(tail -c "+$test_log_cursor" $test_log_cmd | \ 76 | head -c "$((test_log_size-test_log_cursor))") 77 | test_log_cursor=$test_log_size 78 | echo -n "$test_log_content" 79 | grep -q 'skip' <<<"$test_log_content" && test_skipped=1 && break 80 | grep -q 'screencap' <<<"$test_log_content" && break 81 | sleep 0.1 82 | if [ "$(date +%s)" -ge "$test_end_ts" ]; then 83 | echo -e '\n timeout' 84 | break 85 | fi 86 | done 87 | echo 88 | 89 | if [ "$test_skipped" -ne 1 ]; then 90 | # take screencap 91 | # xwd -root -display $x_display -out $test_dir/observed.xwd # graphical 92 | rm -f $test_dir/observed.* 93 | DISPLAY=$x_display xvkbd -window xterm -text '\S\[Home]' &>/dev/null # ansi 94 | local test_log_xterm_count=$(ls -1 ${test_log_xterm}* 2>/dev/null | wc -l) 95 | [ "$test_log_xterm_count" -eq 1 ] && cp ${test_log_xterm}* $test_dir/observed.ansi 96 | 97 | # diff screencap 98 | # convert $test_dir/expected.xwd $test_dir/observed.gif 99 | diff $test_dir/expected.ansi $test_dir/observed.ansi &>/dev/null 100 | diff_ec=$? 101 | fi 102 | 103 | # print result 104 | if [ "$test_skipped" -eq 1 ]; then 105 | echo -e "\x1b[1m$test_name\x1b[0m: \x1b[33mSKIP\x1b[0m" 106 | elif [ "$diff_ec" -eq 0 ]; then 107 | echo -e "\x1b[1m$test_name\x1b[0m: \x1b[32mOK\x1b[0m" 108 | else 109 | echo 110 | echo -e ' \x1b[31mdiff!\x1b[0m' 111 | # compare $test_dir/expected.gif $test_dir/observed.gif $test_dir/diff.gif 112 | # xz -c $test_dir/diff.gif | base64 113 | 114 | echo -e '\n [xterm expected]' 115 | cat $test_dir/expected.ansi 2>/dev/null | sed 's/^/ /'; echo -e '\x1b[0m' 116 | 117 | echo -e '\n [xterm observed]' 118 | cat $test_dir/observed.ansi 2>/dev/null | sed 's/^/ /'; echo -e '\x1b[0m' 119 | 120 | echo -e '\n [xterm observed.ansi.b64]' 121 | base64 $test_dir/observed.ansi 2>/dev/null | sed 's/^/ /' 122 | 123 | if [ -s "$test_log_php" ]; then 124 | echo -e '\n [php log]' 125 | cat $test_log_php | sed 's/^/ /' 126 | fi 127 | 128 | echo -e "\x1b[1m$test_name\x1b[0m: \x1b[31mERR\x1b[0m" 129 | main_ec=1 130 | fi 131 | echo 132 | 133 | # clean up 134 | kill $xterm_pid &>/dev/null 135 | kill $xvfb_pid &>/dev/null 136 | rm -f $test_log_cmd $test_log_xterm $test_log_php 137 | done 138 | 139 | return $main_ec 140 | } 141 | 142 | check_deps() { 143 | for dep in Xvfb xterm xvkbd; do 144 | if ! command -v $dep &>/dev/null; then 145 | echo "Missing dependency: $dep" 146 | exit 1 147 | fi 148 | done 149 | } 150 | 151 | main 152 | -------------------------------------------------------------------------------- /tests/test_64bit/expected.ansi: -------------------------------------------------------------------------------- 1 | #5attr=TB_STRIKEOUT 2 | #5attr=TB_INVISIBLE 3 | #5attr=TB_UNDERLINE_2 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_64bit/test.php: -------------------------------------------------------------------------------- 1 | ffi->tb_attr_width() !== 64) { 5 | // This will only work with 64-bit attrs 6 | $test->skip(); 7 | } 8 | 9 | $test->ffi->tb_init(); 10 | 11 | $attrs = [ 12 | 'TB_STRIKEOUT', 13 | // 'TB_OVERLINE', // Not supported by xterm 14 | 'TB_INVISIBLE', 15 | 'TB_UNDERLINE_2', 16 | ]; 17 | 18 | $y = 0; 19 | foreach ($attrs as $attr) { 20 | $test->ffi->tb_printf(0, $y++, $test->defines[$attr], 0, 'attr=%s', $attr); 21 | } 22 | 23 | $test->ffi->tb_present(); 24 | 25 | $test->screencap(); 26 | -------------------------------------------------------------------------------- /tests/test_basic/expected.ansi: -------------------------------------------------------------------------------- 1 | #5has_version=y 2 | #5width=80 3 | #5height=24 4 | #5attr=TB_BOLD 5 | #5attr=TB_UNDERLINE 6 | #5attr=TB_ITALIC 7 | #5attr=TB_REVERSE 8 | #5attr=TB_BRIGHT 9 | #5attr=TB_DIM 10 | #5event rv=0 type=1 mod=2 key=1 ch=0 w=0 h=0 x=0 y=0 11 | #5out_w=50 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_basic/test.php: -------------------------------------------------------------------------------- 1 | ffi->tb_init(); 5 | 6 | $w = $test->ffi->tb_width(); 7 | $h = $test->ffi->tb_height(); 8 | 9 | $bg = $test->defines['TB_BLACK']; 10 | $red = $test->defines['TB_RED']; 11 | $green = $test->defines['TB_GREEN']; 12 | $blue = $test->defines['TB_BLUE']; 13 | 14 | $y = 0; 15 | $version_str = $test->ffi->tb_version(); 16 | $has_version = is_string($version_str) && strlen($version_str) > 0; 17 | $test->ffi->tb_printf(0, $y++, 0, 0, "has_version=%s", $has_version ? 'y' : 'n'); 18 | $test->ffi->tb_printf(0, $y++, $red, $bg, "width=%d", $w); 19 | $test->ffi->tb_printf(0, $y++, $green, $bg, "height=%d", $h); 20 | foreach (['TB_BOLD', 'TB_UNDERLINE', 'TB_ITALIC', 'TB_REVERSE', 'TB_BRIGHT', 'TB_DIM'] as $attr) { 21 | $test->ffi->tb_printf(0, $y++, $blue | $test->defines[$attr], $bg, "attr=%s", $attr); 22 | } 23 | 24 | $test->xvkbd('\Ca'); // Ctrl-A 25 | 26 | $event = $test->ffi->new('struct tb_event'); 27 | $rv = $test->ffi->tb_peek_event(FFI::addr($event), 1000); 28 | 29 | $out_w = $test->ffi->new('size_t'); 30 | $test->ffi->tb_printf_ex(0, $y++, $blue, $bg, FFI::addr($out_w), "event rv=%d type=%d mod=%d key=%d ch=%d w=%d h=%d x=%d y=%d", 31 | $rv, 32 | $event->type, 33 | $event->mod, 34 | $event->key, 35 | $event->ch, 36 | $event->w, 37 | $event->h, 38 | $event->x, 39 | $event->y 40 | ); 41 | 42 | $test->ffi->tb_printf(0, $y++, 0, 0, "out_w=%d", $out_w->cdata); 43 | 44 | $test->ffi->tb_present(); 45 | 46 | $test->screencap(); 47 | -------------------------------------------------------------------------------- /tests/test_color_8bit/expected.ansi: -------------------------------------------------------------------------------- 1 | #5TB_OUTPUT_NORMAL 2 | #5▀▀▀▀▀▀▀▀▀ 3 | #5fg=def|ital bg=6 4 | #5fg=0x0|ital bg=6 5 | #5TB_OUTPUT_256 6 | #5▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀[0;38:5:16m▀[0;38:5:17m▀[0;38:5:18m▀[0;38:5:19m▀[0;38:5:20m▀[0;38:5:21m▀[0;38:5:22m▀[0;38:5:23m▀[0;38:5:24m▀[0;38:5:25m▀[0;38:5:26m▀[0;38:5:27m▀[0;38:5:28m▀[0;38:5:29m▀[0;38:5:30m▀[0;38:5:31m▀[0;38:5:32m▀[0;38:5:33m▀[0;38:5:34m▀[0;38:5:35m▀[0;38:5:36m▀[0;38:5:37m▀[0;38:5:38m▀[0;38:5:39m▀[0;38:5:40m▀[0;38:5:41m▀[0;38:5:42m▀[0;38:5:43m▀[0;38:5:44m▀[0;38:5:45m▀[0;38:5:46m▀[0;38:5:47m▀[0;38:5:48m▀[0;38:5:49m▀[0;38:5:50m▀[0;38:5:51m▀[0;38:5:52m▀[0;38:5:53m▀[0;38:5:54m▀[0;38:5:55m▀[0;38:5:56m▀[0;38:5:57m▀[0;38:5:58m▀[0;38:5:59m▀[0;38:5:60m▀[0;38:5:61m▀[0;38:5:62m▀[0;38:5:63m▀[0;38:5:64m▀[0;38:5:65m▀[0;38:5:66m▀[0;38:5:67m▀[0;38:5:68m▀[0;38:5:69m▀[0;38:5:70m▀[0;38:5:71m▀[0;38:5:72m▀[0;38:5:73m▀[0;38:5:74m▀[0;38:5:75m▀[0;38:5:76m▀[0;38:5:77m▀[0;38:5:78m▀ 7 | #5[0;38:5:79m▀[0;38:5:80m▀[0;38:5:81m▀[0;38:5:82m▀[0;38:5:83m▀[0;38:5:84m▀[0;38:5:85m▀[0;38:5:86m▀[0;38:5:87m▀[0;38:5:88m▀[0;38:5:89m▀[0;38:5:90m▀[0;38:5:91m▀[0;38:5:92m▀[0;38:5:93m▀[0;38:5:94m▀[0;38:5:95m▀[0;38:5:96m▀[0;38:5:97m▀[0;38:5:98m▀[0;38:5:99m▀[0;38:5:100m▀[0;38:5:101m▀[0;38:5:102m▀[0;38:5:103m▀[0;38:5:104m▀[0;38:5:105m▀[0;38:5:106m▀[0;38:5:107m▀[0;38:5:108m▀[0;38:5:109m▀[0;38:5:110m▀[0;38:5:111m▀[0;38:5:112m▀[0;38:5:113m▀[0;38:5:114m▀[0;38:5:115m▀[0;38:5:116m▀[0;38:5:117m▀[0;38:5:118m▀[0;38:5:119m▀[0;38:5:120m▀[0;38:5:121m▀[0;38:5:122m▀[0;38:5:123m▀[0;38:5:124m▀[0;38:5:125m▀[0;38:5:126m▀[0;38:5:127m▀[0;38:5:128m▀[0;38:5:129m▀[0;38:5:130m▀[0;38:5:131m▀[0;38:5:132m▀[0;38:5:133m▀[0;38:5:134m▀[0;38:5:135m▀[0;38:5:136m▀[0;38:5:137m▀[0;38:5:138m▀[0;38:5:139m▀[0;38:5:140m▀[0;38:5:141m▀[0;38:5:142m▀[0;38:5:143m▀[0;38:5:144m▀[0;38:5:145m▀[0;38:5:146m▀[0;38:5:147m▀[0;38:5:148m▀[0;38:5:149m▀[0;38:5:150m▀[0;38:5:151m▀[0;38:5:152m▀[0;38:5:153m▀[0;38:5:154m▀[0;38:5:155m▀[0;38:5:156m▀[0;38:5:157m▀[0;38:5:158m▀ 8 | #5[0;38:5:159m▀[0;38:5:160m▀[0;38:5:161m▀[0;38:5:162m▀[0;38:5:163m▀[0;38:5:164m▀[0;38:5:165m▀[0;38:5:166m▀[0;38:5:167m▀[0;38:5:168m▀[0;38:5:169m▀[0;38:5:170m▀[0;38:5:171m▀[0;38:5:172m▀[0;38:5:173m▀[0;38:5:174m▀[0;38:5:175m▀[0;38:5:176m▀[0;38:5:177m▀[0;38:5:178m▀[0;38:5:179m▀[0;38:5:180m▀[0;38:5:181m▀[0;38:5:182m▀[0;38:5:183m▀[0;38:5:184m▀[0;38:5:185m▀[0;38:5:186m▀[0;38:5:187m▀[0;38:5:188m▀[0;38:5:189m▀[0;38:5:190m▀[0;38:5:191m▀[0;38:5:192m▀[0;38:5:193m▀[0;38:5:194m▀[0;38:5:195m▀[0;38:5:196m▀[0;38:5:197m▀[0;38:5:198m▀[0;38:5:199m▀[0;38:5:200m▀[0;38:5:201m▀[0;38:5:202m▀[0;38:5:203m▀[0;38:5:204m▀[0;38:5:205m▀[0;38:5:206m▀[0;38:5:207m▀[0;38:5:208m▀[0;38:5:209m▀[0;38:5:210m▀[0;38:5:211m▀[0;38:5:212m▀[0;38:5:213m▀[0;38:5:214m▀[0;38:5:215m▀[0;38:5:216m▀[0;38:5:217m▀[0;38:5:218m▀[0;38:5:219m▀[0;38:5:220m▀[0;38:5:221m▀[0;38:5:222m▀[0;38:5:223m▀[0;38:5:224m▀[0;38:5:225m▀[0;38:5:226m▀[0;38:5:227m▀[0;38:5:228m▀[0;38:5:229m▀[0;38:5:230m▀[0;38:5:231m▀[0;38:5:232m▀[0;38:5:233m▀[0;38:5:234m▀[0;38:5:235m▀[0;38:5:236m▀[0;38:5:237m▀[0;38:5:238m▀ 9 | #5[0;38:5:239m▀[0;38:5:240m▀[0;38:5:241m▀[0;38:5:242m▀[0;38:5:243m▀[0;38:5:244m▀[0;38:5:245m▀[0;38:5:246m▀[0;38:5:247m▀[0;38:5:248m▀[0;38:5:249m▀[0;38:5:250m▀[0;38:5:251m▀[0;38:5:252m▀[0;38:5:253m▀[0;38:5:254m▀[0;38:5:255m▀ 10 | #5fg=def|ital bg=6 11 | #5fg=0x0|ital bg=6 12 | #5TB_OUTPUT_216 13 | #5▀[0;38:5:16m▀[0;38:5:17m▀[0;38:5:18m▀[0;38:5:19m▀[0;38:5:20m▀[0;38:5:21m▀[0;38:5:22m▀[0;38:5:23m▀[0;38:5:24m▀[0;38:5:25m▀[0;38:5:26m▀[0;38:5:27m▀[0;38:5:28m▀[0;38:5:29m▀[0;38:5:30m▀[0;38:5:31m▀[0;38:5:32m▀[0;38:5:33m▀[0;38:5:34m▀[0;38:5:35m▀[0;38:5:36m▀[0;38:5:37m▀[0;38:5:38m▀[0;38:5:39m▀[0;38:5:40m▀[0;38:5:41m▀[0;38:5:42m▀[0;38:5:43m▀[0;38:5:44m▀[0;38:5:45m▀[0;38:5:46m▀[0;38:5:47m▀[0;38:5:48m▀[0;38:5:49m▀[0;38:5:50m▀[0;38:5:51m▀[0;38:5:52m▀[0;38:5:53m▀[0;38:5:54m▀[0;38:5:55m▀[0;38:5:56m▀[0;38:5:57m▀[0;38:5:58m▀[0;38:5:59m▀[0;38:5:60m▀[0;38:5:61m▀[0;38:5:62m▀[0;38:5:63m▀[0;38:5:64m▀[0;38:5:65m▀[0;38:5:66m▀[0;38:5:67m▀[0;38:5:68m▀[0;38:5:69m▀[0;38:5:70m▀[0;38:5:71m▀[0;38:5:72m▀[0;38:5:73m▀[0;38:5:74m▀[0;38:5:75m▀[0;38:5:76m▀[0;38:5:77m▀[0;38:5:78m▀[0;38:5:79m▀[0;38:5:80m▀[0;38:5:81m▀[0;38:5:82m▀[0;38:5:83m▀[0;38:5:84m▀[0;38:5:85m▀[0;38:5:86m▀[0;38:5:87m▀[0;38:5:88m▀[0;38:5:89m▀[0;38:5:90m▀[0;38:5:91m▀[0;38:5:92m▀[0;38:5:93m▀[0;38:5:94m▀ 14 | #5[0;38:5:95m▀[0;38:5:96m▀[0;38:5:97m▀[0;38:5:98m▀[0;38:5:99m▀[0;38:5:100m▀[0;38:5:101m▀[0;38:5:102m▀[0;38:5:103m▀[0;38:5:104m▀[0;38:5:105m▀[0;38:5:106m▀[0;38:5:107m▀[0;38:5:108m▀[0;38:5:109m▀[0;38:5:110m▀[0;38:5:111m▀[0;38:5:112m▀[0;38:5:113m▀[0;38:5:114m▀[0;38:5:115m▀[0;38:5:116m▀[0;38:5:117m▀[0;38:5:118m▀[0;38:5:119m▀[0;38:5:120m▀[0;38:5:121m▀[0;38:5:122m▀[0;38:5:123m▀[0;38:5:124m▀[0;38:5:125m▀[0;38:5:126m▀[0;38:5:127m▀[0;38:5:128m▀[0;38:5:129m▀[0;38:5:130m▀[0;38:5:131m▀[0;38:5:132m▀[0;38:5:133m▀[0;38:5:134m▀[0;38:5:135m▀[0;38:5:136m▀[0;38:5:137m▀[0;38:5:138m▀[0;38:5:139m▀[0;38:5:140m▀[0;38:5:141m▀[0;38:5:142m▀[0;38:5:143m▀[0;38:5:144m▀[0;38:5:145m▀[0;38:5:146m▀[0;38:5:147m▀[0;38:5:148m▀[0;38:5:149m▀[0;38:5:150m▀[0;38:5:151m▀[0;38:5:152m▀[0;38:5:153m▀[0;38:5:154m▀[0;38:5:155m▀[0;38:5:156m▀[0;38:5:157m▀[0;38:5:158m▀[0;38:5:159m▀[0;38:5:160m▀[0;38:5:161m▀[0;38:5:162m▀[0;38:5:163m▀[0;38:5:164m▀[0;38:5:165m▀[0;38:5:166m▀[0;38:5:167m▀[0;38:5:168m▀[0;38:5:169m▀[0;38:5:170m▀[0;38:5:171m▀[0;38:5:172m▀[0;38:5:173m▀[0;38:5:174m▀ 15 | #5[0;38:5:175m▀[0;38:5:176m▀[0;38:5:177m▀[0;38:5:178m▀[0;38:5:179m▀[0;38:5:180m▀[0;38:5:181m▀[0;38:5:182m▀[0;38:5:183m▀[0;38:5:184m▀[0;38:5:185m▀[0;38:5:186m▀[0;38:5:187m▀[0;38:5:188m▀[0;38:5:189m▀[0;38:5:190m▀[0;38:5:191m▀[0;38:5:192m▀[0;38:5:193m▀[0;38:5:194m▀[0;38:5:195m▀[0;38:5:196m▀[0;38:5:197m▀[0;38:5:198m▀[0;38:5:199m▀[0;38:5:200m▀[0;38:5:201m▀[0;38:5:202m▀[0;38:5:203m▀[0;38:5:204m▀[0;38:5:205m▀[0;38:5:206m▀[0;38:5:207m▀[0;38:5:208m▀[0;38:5:209m▀[0;38:5:210m▀[0;38:5:211m▀[0;38:5:212m▀[0;38:5:213m▀[0;38:5:214m▀[0;38:5:215m▀[0;38:5:216m▀[0;38:5:217m▀[0;38:5:218m▀[0;38:5:219m▀[0;38:5:220m▀[0;38:5:221m▀[0;38:5:222m▀[0;38:5:223m▀[0;38:5:224m▀[0;38:5:225m▀[0;38:5:226m▀[0;38:5:227m▀[0;38:5:228m▀[0;38:5:229m▀[0;38:5:230m▀[0;38:5:231m▀ 16 | #5[0;3;48:5:21mf[0;3;48:5:21mg[0;3;48:5:21m=[0;3;48:5:21md[0;3;48:5:21me[0;3;48:5:21mf[0;3;48:5:21m|[0;3;48:5:21mi[0;3;48:5:21mt[0;3;48:5:21ma[0;3;48:5:21ml[0;3;48:5:21m [0;3;48:5:21mb[0;3;48:5:21mg[0;3;48:5:21m=[0;3;48:5:21m6 17 | #5[0;3;48:5:21mf[0;3;48:5:21mg[0;3;48:5:21m=[0;3;48:5:21m0[0;3;48:5:21mx[0;3;48:5:21m0[0;3;48:5:21m|[0;3;48:5:21mi[0;3;48:5:21mt[0;3;48:5:21ma[0;3;48:5:21ml[0;3;48:5:21m [0;3;48:5:21mb[0;3;48:5:21mg[0;3;48:5:21m=[0;3;48:5:21m6 18 | #5TB_OUTPUT_GRAYSCALE 19 | #5▀[0;38:5:232m▀[0;38:5:233m▀[0;38:5:234m▀[0;38:5:235m▀[0;38:5:236m▀[0;38:5:237m▀[0;38:5:238m▀[0;38:5:239m▀[0;38:5:240m▀[0;38:5:241m▀[0;38:5:242m▀[0;38:5:243m▀[0;38:5:244m▀[0;38:5:245m▀[0;38:5:246m▀[0;38:5:247m▀[0;38:5:248m▀[0;38:5:249m▀[0;38:5:250m▀[0;38:5:251m▀[0;38:5:252m▀[0;38:5:253m▀[0;38:5:254m▀[0;38:5:255m▀ 20 | #5[0;3;48:5:237mf[0;3;48:5:237mg[0;3;48:5:237m=[0;3;48:5:237md[0;3;48:5:237me[0;3;48:5:237mf[0;3;48:5:237m|[0;3;48:5:237mi[0;3;48:5:237mt[0;3;48:5:237ma[0;3;48:5:237ml[0;3;48:5:237m [0;3;48:5:237mb[0;3;48:5:237mg[0;3;48:5:237m=[0;3;48:5:237m6 21 | #5[0;3;48:5:237mf[0;3;48:5:237mg[0;3;48:5:237m=[0;3;48:5:237m0[0;3;48:5:237mx[0;3;48:5:237m0[0;3;48:5:237m|[0;3;48:5:237mi[0;3;48:5:237mt[0;3;48:5:237ma[0;3;48:5:237ml[0;3;48:5:237m [0;3;48:5:237mb[0;3;48:5:237mg[0;3;48:5:237m=[0;3;48:5:237m6 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_color_8bit/test.php: -------------------------------------------------------------------------------- 1 | ffi->tb_init(); 5 | 6 | $w = $test->ffi->tb_width(); 7 | $h = $test->ffi->tb_height(); 8 | 9 | $x = 0; 10 | $y = 0; 11 | 12 | function test_mode($test, string $mode, int $n, int $w, int &$x, int &$y): void { 13 | $attr_default = $test->defines['TB_DEFAULT']; 14 | $attr_italic = $test->defines['TB_ITALIC']; 15 | $attr_reverse = $test->defines['TB_REVERSE']; 16 | 17 | $test->ffi->tb_set_output_mode($test->defines[$mode]); 18 | $test->ffi->tb_print($x = 0, $y, $attr_default, $attr_default, $mode); 19 | 20 | $y++; 21 | for ($fg = 0; $fg <= $n; $fg++) { 22 | $s = "\xe2\x96\x80"; 23 | $slen = 1; // mb_strlen 24 | if ($x + $slen > $w) { 25 | $x = 0; 26 | $y++; 27 | } 28 | if ($mode === 'TB_OUTPUT_256') { 29 | if ($fg === 0) { 30 | $cfg = $attr_default; 31 | } else if ($fg === 1) { 32 | $cfg = $test->defines['TB_HI_BLACK']; 33 | } else { 34 | $cfg = $fg - 1; 35 | } 36 | } else { 37 | $cfg = $fg; 38 | } 39 | $test->ffi->tb_print($x, $y, $cfg, $attr_default, $s); 40 | $x += $slen; 41 | } 42 | 43 | $y++; 44 | $test->ffi->tb_print($x = 0, $y++, $attr_default | $attr_italic, 6, "fg=def|ital bg=6"); 45 | $test->ffi->tb_print($x = 0, $y++, 0 | $attr_italic, 6, "fg=0x0|ital bg=6"); 46 | 47 | $test->ffi->tb_present(); 48 | } 49 | 50 | // TB_OUTPUT_NORMAL 51 | test_mode($test, 'TB_OUTPUT_NORMAL', 8, $w, $x, $y); 52 | test_mode($test, 'TB_OUTPUT_256', 256, $w, $x, $y); 53 | test_mode($test, 'TB_OUTPUT_216', 216, $w, $x, $y); 54 | test_mode($test, 'TB_OUTPUT_GRAYSCALE', 24, $w, $x, $y); 55 | 56 | $test->screencap(); 57 | -------------------------------------------------------------------------------- /tests/test_color_true/expected.ansi: -------------------------------------------------------------------------------- 1 | #5[0;38:2::250:235:215ma[0;38:2::250:235:215mn[0;38:2::250:235:215mt[0;38:2::250:235:215mi[0;38:2::250:235:215mq[0;38:2::250:235:215mu[0;38:2::250:235:215me[0;38:2::250:235:215mw[0;38:2::250:235:215mh[0;38:2::250:235:215mi[0;38:2::250:235:215mt[0;38:2::250:235:215me[0;38:2::250:235:215m,[0;38:2::250:235:215m [0;38:2::127:255:212ma[0;38:2::127:255:212mq[0;38:2::127:255:212mu[0;38:2::127:255:212ma[0;38:2::127:255:212mm[0;38:2::127:255:212ma[0;38:2::127:255:212mr[0;38:2::127:255:212mi[0;38:2::127:255:212mn[0;38:2::127:255:212me[0;38:2::127:255:212m,[0;38:2::127:255:212m [0;38:2::245:245:220mb[0;38:2::245:245:220me[0;38:2::245:245:220mi[0;38:2::245:245:220mg[0;38:2::245:245:220me[0;38:2::245:245:220m,[0;38:2::245:245:220m [0;38:2::0:0:0mb[0;38:2::0:0:0ml[0;38:2::0:0:0ma[0;38:2::0:0:0mc[0;38:2::0:0:0mk[0;38:2::0:0:0m,[0;38:2::0:0:0m [0;38:2::0:0:255mb[0;38:2::0:0:255ml[0;38:2::0:0:255mu[0;38:2::0:0:255me[0;38:2::0:0:255m,[0;38:2::0:0:255m [0;38:2::165:42:42mb[0;38:2::165:42:42mr[0;38:2::165:42:42mo[0;38:2::165:42:42mw[0;38:2::165:42:42mn[0;38:2::165:42:42m,[0;38:2::165:42:42m [0;38:2::95:158:160mc[0;38:2::95:158:160ma[0;38:2::95:158:160md[0;38:2::95:158:160me[0;38:2::95:158:160mt[0;38:2::95:158:160mb[0;38:2::95:158:160ml[0;38:2::95:158:160mu[0;38:2::95:158:160me[0;38:2::95:158:160m,[0;38:2::95:158:160m [0;38:2::210:105:30mc[0;38:2::210:105:30mh[0;38:2::210:105:30mo[0;38:2::210:105:30mc[0;38:2::210:105:30mo[0;38:2::210:105:30ml[0;38:2::210:105:30ma[0;38:2::210:105:30mt[0;38:2::210:105:30me[0;38:2::210:105:30m,[0;38:2::210:105:30m  2 | #5[0;38:2::100:149:237mc[0;38:2::100:149:237mo[0;38:2::100:149:237mr[0;38:2::100:149:237mn[0;38:2::100:149:237mf[0;38:2::100:149:237ml[0;38:2::100:149:237mo[0;38:2::100:149:237mw[0;38:2::100:149:237me[0;38:2::100:149:237mr[0;38:2::100:149:237mb[0;38:2::100:149:237ml[0;38:2::100:149:237mu[0;38:2::100:149:237me[0;38:2::100:149:237m,[0;38:2::100:149:237m [0;38:2::220:20:60mc[0;38:2::220:20:60mr[0;38:2::220:20:60mi[0;38:2::220:20:60mm[0;38:2::220:20:60ms[0;38:2::220:20:60mo[0;38:2::220:20:60mn[0;38:2::220:20:60m,[0;38:2::220:20:60m [0;38:2::0:0:139md[0;38:2::0:0:139ma[0;38:2::0:0:139mr[0;38:2::0:0:139mk[0;38:2::0:0:139mb[0;38:2::0:0:139ml[0;38:2::0:0:139mu[0;38:2::0:0:139me[0;38:2::0:0:139m,[0;38:2::0:0:139m [0;38:2::184:134:11md[0;38:2::184:134:11ma[0;38:2::184:134:11mr[0;38:2::184:134:11mk[0;38:2::184:134:11mg[0;38:2::184:134:11mo[0;38:2::184:134:11ml[0;38:2::184:134:11md[0;38:2::184:134:11me[0;38:2::184:134:11mn[0;38:2::184:134:11mr[0;38:2::184:134:11mo[0;38:2::184:134:11md[0;38:2::184:134:11m,[0;38:2::184:134:11m [0;38:2::0:100:0md[0;38:2::0:100:0ma[0;38:2::0:100:0mr[0;38:2::0:100:0mk[0;38:2::0:100:0mg[0;38:2::0:100:0mr[0;38:2::0:100:0me[0;38:2::0:100:0me[0;38:2::0:100:0mn[0;38:2::0:100:0m,[0;38:2::0:100:0m [0;38:2::189:183:107md[0;38:2::189:183:107ma[0;38:2::189:183:107mr[0;38:2::189:183:107mk[0;38:2::189:183:107mk[0;38:2::189:183:107mh[0;38:2::189:183:107ma[0;38:2::189:183:107mk[0;38:2::189:183:107mi[0;38:2::189:183:107m,[0;38:2::189:183:107m  3 | #5[0;38:2::85:107:47md[0;38:2::85:107:47ma[0;38:2::85:107:47mr[0;38:2::85:107:47mk[0;38:2::85:107:47mo[0;38:2::85:107:47ml[0;38:2::85:107:47mi[0;38:2::85:107:47mv[0;38:2::85:107:47me[0;38:2::85:107:47mg[0;38:2::85:107:47mr[0;38:2::85:107:47me[0;38:2::85:107:47me[0;38:2::85:107:47mn[0;38:2::85:107:47m,[0;38:2::85:107:47m [0;38:2::153:50:204md[0;38:2::153:50:204ma[0;38:2::153:50:204mr[0;38:2::153:50:204mk[0;38:2::153:50:204mo[0;38:2::153:50:204mr[0;38:2::153:50:204mc[0;38:2::153:50:204mh[0;38:2::153:50:204mi[0;38:2::153:50:204md[0;38:2::153:50:204m,[0;38:2::153:50:204m [0;38:2::233:150:122md[0;38:2::233:150:122ma[0;38:2::233:150:122mr[0;38:2::233:150:122mk[0;38:2::233:150:122ms[0;38:2::233:150:122ma[0;38:2::233:150:122ml[0;38:2::233:150:122mm[0;38:2::233:150:122mo[0;38:2::233:150:122mn[0;38:2::233:150:122m,[0;38:2::233:150:122m [0;38:2::72:61:139md[0;38:2::72:61:139ma[0;38:2::72:61:139mr[0;38:2::72:61:139mk[0;38:2::72:61:139ms[0;38:2::72:61:139ml[0;38:2::72:61:139ma[0;38:2::72:61:139mt[0;38:2::72:61:139me[0;38:2::72:61:139mb[0;38:2::72:61:139ml[0;38:2::72:61:139mu[0;38:2::72:61:139me[0;38:2::72:61:139m,[0;38:2::72:61:139m [0;38:2::0:206:209md[0;38:2::0:206:209ma[0;38:2::0:206:209mr[0;38:2::0:206:209mk[0;38:2::0:206:209mt[0;38:2::0:206:209mu[0;38:2::0:206:209mr[0;38:2::0:206:209mq[0;38:2::0:206:209mu[0;38:2::0:206:209mo[0;38:2::0:206:209mi[0;38:2::0:206:209ms[0;38:2::0:206:209me[0;38:2::0:206:209m,[0;38:2::0:206:209m [0;38:2::255:20:147md[0;38:2::255:20:147me[0;38:2::255:20:147me[0;38:2::255:20:147mp[0;38:2::255:20:147mp[0;38:2::255:20:147mi[0;38:2::255:20:147mn[0;38:2::255:20:147mk[0;38:2::255:20:147m,[0;38:2::255:20:147m  4 | #5[0;38:2::105:105:105md[0;38:2::105:105:105mi[0;38:2::105:105:105mm[0;38:2::105:105:105mg[0;38:2::105:105:105mr[0;38:2::105:105:105ma[0;38:2::105:105:105my[0;38:2::105:105:105m,[0;38:2::105:105:105m [0;38:2::30:144:255md[0;38:2::30:144:255mo[0;38:2::30:144:255md[0;38:2::30:144:255mg[0;38:2::30:144:255me[0;38:2::30:144:255mr[0;38:2::30:144:255mb[0;38:2::30:144:255ml[0;38:2::30:144:255mu[0;38:2::30:144:255me[0;38:2::30:144:255m,[0;38:2::30:144:255m [0;38:2::255:250:240mf[0;38:2::255:250:240ml[0;38:2::255:250:240mo[0;38:2::255:250:240mr[0;38:2::255:250:240ma[0;38:2::255:250:240ml[0;38:2::255:250:240mw[0;38:2::255:250:240mh[0;38:2::255:250:240mi[0;38:2::255:250:240mt[0;38:2::255:250:240me[0;38:2::255:250:240m,[0;38:2::255:250:240m [0;38:2::255:0:255mf[0;38:2::255:0:255mu[0;38:2::255:0:255mc[0;38:2::255:0:255mh[0;38:2::255:0:255ms[0;38:2::255:0:255mi[0;38:2::255:0:255ma[0;38:2::255:0:255m,[0;38:2::255:0:255m [0;38:2::248:248:255mg[0;38:2::248:248:255mh[0;38:2::248:248:255mo[0;38:2::248:248:255ms[0;38:2::248:248:255mt[0;38:2::248:248:255mw[0;38:2::248:248:255mh[0;38:2::248:248:255mi[0;38:2::248:248:255mt[0;38:2::248:248:255me[0;38:2::248:248:255m,[0;38:2::248:248:255m [0;38:2::218:165:32mg[0;38:2::218:165:32mo[0;38:2::218:165:32ml[0;38:2::218:165:32md[0;38:2::218:165:32me[0;38:2::218:165:32mn[0;38:2::218:165:32mr[0;38:2::218:165:32mo[0;38:2::218:165:32md[0;38:2::218:165:32m,[0;38:2::218:165:32m [0;38:2::0:128:0mg[0;38:2::0:128:0mr[0;38:2::0:128:0me[0;38:2::0:128:0me[0;38:2::0:128:0mn[0;38:2::0:128:0m,[0;38:2::0:128:0m [0;38:2::128:128:128mg[0;38:2::128:128:128mr[0;38:2::128:128:128me[0;38:2::128:128:128my[0;38:2::128:128:128m,[0;38:2::128:128:128m  5 | #5[0;38:2::255:105:180mh[0;38:2::255:105:180mo[0;38:2::255:105:180mt[0;38:2::255:105:180mp[0;38:2::255:105:180mi[0;38:2::255:105:180mn[0;38:2::255:105:180mk[0;38:2::255:105:180m,[0;38:2::255:105:180m [0;38:2::75:0:130mi[0;38:2::75:0:130mn[0;38:2::75:0:130md[0;38:2::75:0:130mi[0;38:2::75:0:130mg[0;38:2::75:0:130mo[0;38:2::75:0:130m,[0;38:2::75:0:130m [0;38:2::240:230:140mk[0;38:2::240:230:140mh[0;38:2::240:230:140ma[0;38:2::240:230:140mk[0;38:2::240:230:140mi[0;38:2::240:230:140m,[0;38:2::240:230:140m [0;38:2::255:240:245ml[0;38:2::255:240:245ma[0;38:2::255:240:245mv[0;38:2::255:240:245me[0;38:2::255:240:245mn[0;38:2::255:240:245md[0;38:2::255:240:245me[0;38:2::255:240:245mr[0;38:2::255:240:245mb[0;38:2::255:240:245ml[0;38:2::255:240:245mu[0;38:2::255:240:245ms[0;38:2::255:240:245mh[0;38:2::255:240:245m,[0;38:2::255:240:245m [0;38:2::255:250:205ml[0;38:2::255:250:205me[0;38:2::255:250:205mm[0;38:2::255:250:205mo[0;38:2::255:250:205mn[0;38:2::255:250:205mc[0;38:2::255:250:205mh[0;38:2::255:250:205mi[0;38:2::255:250:205mf[0;38:2::255:250:205mf[0;38:2::255:250:205mo[0;38:2::255:250:205mn[0;38:2::255:250:205m,[0;38:2::255:250:205m [0;38:2::240:128:128ml[0;38:2::240:128:128mi[0;38:2::240:128:128mg[0;38:2::240:128:128mh[0;38:2::240:128:128mt[0;38:2::240:128:128mc[0;38:2::240:128:128mo[0;38:2::240:128:128mr[0;38:2::240:128:128ma[0;38:2::240:128:128ml[0;38:2::240:128:128m,[0;38:2::240:128:128m  6 | #5[0;38:2::250:250:210ml[0;38:2::250:250:210mi[0;38:2::250:250:210mg[0;38:2::250:250:210mh[0;38:2::250:250:210mt[0;38:2::250:250:210mg[0;38:2::250:250:210mo[0;38:2::250:250:210ml[0;38:2::250:250:210md[0;38:2::250:250:210me[0;38:2::250:250:210mn[0;38:2::250:250:210mr[0;38:2::250:250:210mo[0;38:2::250:250:210md[0;38:2::250:250:210my[0;38:2::250:250:210me[0;38:2::250:250:210ml[0;38:2::250:250:210ml[0;38:2::250:250:210mo[0;38:2::250:250:210mw[0;38:2::250:250:210m,[0;38:2::250:250:210m [0;38:2::144:238:144ml[0;38:2::144:238:144mi[0;38:2::144:238:144mg[0;38:2::144:238:144mh[0;38:2::144:238:144mt[0;38:2::144:238:144mg[0;38:2::144:238:144mr[0;38:2::144:238:144me[0;38:2::144:238:144me[0;38:2::144:238:144mn[0;38:2::144:238:144m,[0;38:2::144:238:144m [0;38:2::255:182:193ml[0;38:2::255:182:193mi[0;38:2::255:182:193mg[0;38:2::255:182:193mh[0;38:2::255:182:193mt[0;38:2::255:182:193mp[0;38:2::255:182:193mi[0;38:2::255:182:193mn[0;38:2::255:182:193mk[0;38:2::255:182:193m,[0;38:2::255:182:193m [0;38:2::32:178:170ml[0;38:2::32:178:170mi[0;38:2::32:178:170mg[0;38:2::32:178:170mh[0;38:2::32:178:170mt[0;38:2::32:178:170ms[0;38:2::32:178:170me[0;38:2::32:178:170ma[0;38:2::32:178:170mg[0;38:2::32:178:170mr[0;38:2::32:178:170me[0;38:2::32:178:170me[0;38:2::32:178:170mn[0;38:2::32:178:170m,[0;38:2::32:178:170m [0;38:2::119:136:153ml[0;38:2::119:136:153mi[0;38:2::119:136:153mg[0;38:2::119:136:153mh[0;38:2::119:136:153mt[0;38:2::119:136:153ms[0;38:2::119:136:153ml[0;38:2::119:136:153ma[0;38:2::119:136:153mt[0;38:2::119:136:153me[0;38:2::119:136:153mg[0;38:2::119:136:153mr[0;38:2::119:136:153me[0;38:2::119:136:153my[0;38:2::119:136:153m,[0;38:2::119:136:153m  7 | #5[0;38:2::255:255:224ml[0;38:2::255:255:224mi[0;38:2::255:255:224mg[0;38:2::255:255:224mh[0;38:2::255:255:224mt[0;38:2::255:255:224my[0;38:2::255:255:224me[0;38:2::255:255:224ml[0;38:2::255:255:224ml[0;38:2::255:255:224mo[0;38:2::255:255:224mw[0;38:2::255:255:224m,[0;38:2::255:255:224m [0;38:2::50:205:50ml[0;38:2::50:205:50mi[0;38:2::50:205:50mm[0;38:2::50:205:50me[0;38:2::50:205:50mg[0;38:2::50:205:50mr[0;38:2::50:205:50me[0;38:2::50:205:50me[0;38:2::50:205:50mn[0;38:2::50:205:50m,[0;38:2::50:205:50m [0;38:2::255:0:255mm[0;38:2::255:0:255ma[0;38:2::255:0:255mg[0;38:2::255:0:255me[0;38:2::255:0:255mn[0;38:2::255:0:255mt[0;38:2::255:0:255ma[0;38:2::255:0:255m,[0;38:2::255:0:255m [0;38:2::102:205:170mm[0;38:2::102:205:170me[0;38:2::102:205:170md[0;38:2::102:205:170mi[0;38:2::102:205:170mu[0;38:2::102:205:170mm[0;38:2::102:205:170ma[0;38:2::102:205:170mq[0;38:2::102:205:170mu[0;38:2::102:205:170ma[0;38:2::102:205:170mm[0;38:2::102:205:170ma[0;38:2::102:205:170mr[0;38:2::102:205:170mi[0;38:2::102:205:170mn[0;38:2::102:205:170me[0;38:2::102:205:170m,[0;38:2::102:205:170m [0;38:2::186:85:211mm[0;38:2::186:85:211me[0;38:2::186:85:211md[0;38:2::186:85:211mi[0;38:2::186:85:211mu[0;38:2::186:85:211mm[0;38:2::186:85:211mo[0;38:2::186:85:211mr[0;38:2::186:85:211mc[0;38:2::186:85:211mh[0;38:2::186:85:211mi[0;38:2::186:85:211md[0;38:2::186:85:211m,[0;38:2::186:85:211m  8 | #5[0;38:2::60:179:113mm[0;38:2::60:179:113me[0;38:2::60:179:113md[0;38:2::60:179:113mi[0;38:2::60:179:113mu[0;38:2::60:179:113mm[0;38:2::60:179:113ms[0;38:2::60:179:113me[0;38:2::60:179:113ma[0;38:2::60:179:113mg[0;38:2::60:179:113mr[0;38:2::60:179:113me[0;38:2::60:179:113me[0;38:2::60:179:113mn[0;38:2::60:179:113m,[0;38:2::60:179:113m [0;38:2::0:250:154mm[0;38:2::0:250:154me[0;38:2::0:250:154md[0;38:2::0:250:154mi[0;38:2::0:250:154mu[0;38:2::0:250:154mm[0;38:2::0:250:154ms[0;38:2::0:250:154mp[0;38:2::0:250:154mr[0;38:2::0:250:154mi[0;38:2::0:250:154mn[0;38:2::0:250:154mg[0;38:2::0:250:154mg[0;38:2::0:250:154mr[0;38:2::0:250:154me[0;38:2::0:250:154me[0;38:2::0:250:154mn[0;38:2::0:250:154m,[0;38:2::0:250:154m [0;38:2::199:21:133mm[0;38:2::199:21:133me[0;38:2::199:21:133md[0;38:2::199:21:133mi[0;38:2::199:21:133mu[0;38:2::199:21:133mm[0;38:2::199:21:133mv[0;38:2::199:21:133mi[0;38:2::199:21:133mo[0;38:2::199:21:133ml[0;38:2::199:21:133me[0;38:2::199:21:133mt[0;38:2::199:21:133mr[0;38:2::199:21:133me[0;38:2::199:21:133md[0;38:2::199:21:133m,[0;38:2::199:21:133m [0;38:2::245:255:250mm[0;38:2::245:255:250mi[0;38:2::245:255:250mn[0;38:2::245:255:250mt[0;38:2::245:255:250mc[0;38:2::245:255:250mr[0;38:2::245:255:250me[0;38:2::245:255:250ma[0;38:2::245:255:250mm[0;38:2::245:255:250m,[0;38:2::245:255:250m [0;38:2::255:228:181mm[0;38:2::255:228:181mo[0;38:2::255:228:181mc[0;38:2::255:228:181mc[0;38:2::255:228:181ma[0;38:2::255:228:181ms[0;38:2::255:228:181mi[0;38:2::255:228:181mn[0;38:2::255:228:181m,[0;38:2::255:228:181m [0;38:2::0:0:128mn[0;38:2::0:0:128ma[0;38:2::0:0:128mv[0;38:2::0:0:128my[0;38:2::0:0:128m,[0;38:2::0:0:128m  9 | #5[0;38:2::128:128:0mo[0;38:2::128:128:0ml[0;38:2::128:128:0mi[0;38:2::128:128:0mv[0;38:2::128:128:0me[0;38:2::128:128:0m,[0;38:2::128:128:0m [0;38:2::255:165:0mo[0;38:2::255:165:0mr[0;38:2::255:165:0ma[0;38:2::255:165:0mn[0;38:2::255:165:0mg[0;38:2::255:165:0me[0;38:2::255:165:0m,[0;38:2::255:165:0m [0;38:2::218:112:214mo[0;38:2::218:112:214mr[0;38:2::218:112:214mc[0;38:2::218:112:214mh[0;38:2::218:112:214mi[0;38:2::218:112:214md[0;38:2::218:112:214m,[0;38:2::218:112:214m [0;38:2::152:251:152mp[0;38:2::152:251:152ma[0;38:2::152:251:152ml[0;38:2::152:251:152me[0;38:2::152:251:152mg[0;38:2::152:251:152mr[0;38:2::152:251:152me[0;38:2::152:251:152me[0;38:2::152:251:152mn[0;38:2::152:251:152m,[0;38:2::152:251:152m [0;38:2::219:112:147mp[0;38:2::219:112:147ma[0;38:2::219:112:147ml[0;38:2::219:112:147me[0;38:2::219:112:147mv[0;38:2::219:112:147mi[0;38:2::219:112:147mo[0;38:2::219:112:147ml[0;38:2::219:112:147me[0;38:2::219:112:147mt[0;38:2::219:112:147mr[0;38:2::219:112:147me[0;38:2::219:112:147md[0;38:2::219:112:147m,[0;38:2::219:112:147m [0;38:2::255:218:185mp[0;38:2::255:218:185me[0;38:2::255:218:185ma[0;38:2::255:218:185mc[0;38:2::255:218:185mh[0;38:2::255:218:185mp[0;38:2::255:218:185mu[0;38:2::255:218:185mf[0;38:2::255:218:185mf[0;38:2::255:218:185m,[0;38:2::255:218:185m [0;38:2::255:192:203mp[0;38:2::255:192:203mi[0;38:2::255:192:203mn[0;38:2::255:192:203mk[0;38:2::255:192:203m,[0;38:2::255:192:203m [0;38:2::176:224:230mp[0;38:2::176:224:230mo[0;38:2::176:224:230mw[0;38:2::176:224:230md[0;38:2::176:224:230me[0;38:2::176:224:230mr[0;38:2::176:224:230mb[0;38:2::176:224:230ml[0;38:2::176:224:230mu[0;38:2::176:224:230me[0;38:2::176:224:230m,[0;38:2::176:224:230m  10 | #5[0;38:2::255:0:0mr[0;38:2::255:0:0me[0;38:2::255:0:0md[0;38:2::255:0:0m,[0;38:2::255:0:0m [0;38:2::65:105:225mr[0;38:2::65:105:225mo[0;38:2::65:105:225my[0;38:2::65:105:225ma[0;38:2::65:105:225ml[0;38:2::65:105:225mb[0;38:2::65:105:225ml[0;38:2::65:105:225mu[0;38:2::65:105:225me[0;38:2::65:105:225m,[0;38:2::65:105:225m [0;38:2::250:128:114ms[0;38:2::250:128:114ma[0;38:2::250:128:114ml[0;38:2::250:128:114mm[0;38:2::250:128:114mo[0;38:2::250:128:114mn[0;38:2::250:128:114m,[0;38:2::250:128:114m [0;38:2::46:139:87ms[0;38:2::46:139:87me[0;38:2::46:139:87ma[0;38:2::46:139:87mg[0;38:2::46:139:87mr[0;38:2::46:139:87me[0;38:2::46:139:87me[0;38:2::46:139:87mn[0;38:2::46:139:87m,[0;38:2::46:139:87m [0;38:2::160:82:45ms[0;38:2::160:82:45mi[0;38:2::160:82:45me[0;38:2::160:82:45mn[0;38:2::160:82:45mn[0;38:2::160:82:45ma[0;38:2::160:82:45m,[0;38:2::160:82:45m [0;38:2::135:206:235ms[0;38:2::135:206:235mk[0;38:2::135:206:235my[0;38:2::135:206:235mb[0;38:2::135:206:235ml[0;38:2::135:206:235mu[0;38:2::135:206:235me[0;38:2::135:206:235m,[0;38:2::135:206:235m [0;38:2::112:128:144ms[0;38:2::112:128:144ml[0;38:2::112:128:144ma[0;38:2::112:128:144mt[0;38:2::112:128:144me[0;38:2::112:128:144mg[0;38:2::112:128:144mr[0;38:2::112:128:144me[0;38:2::112:128:144my[0;38:2::112:128:144m,[0;38:2::112:128:144m [0;38:2::0:255:127ms[0;38:2::0:255:127mp[0;38:2::0:255:127mr[0;38:2::0:255:127mi[0;38:2::0:255:127mn[0;38:2::0:255:127mg[0;38:2::0:255:127mg[0;38:2::0:255:127mr[0;38:2::0:255:127me[0;38:2::0:255:127me[0;38:2::0:255:127mn[0;38:2::0:255:127m,[0;38:2::0:255:127m [0;38:2::210:180:140mt[0;38:2::210:180:140ma[0;38:2::210:180:140mn[0;38:2::210:180:140m,[0;38:2::210:180:140m  11 | #5[0;38:2::216:191:216mt[0;38:2::216:191:216mh[0;38:2::216:191:216mi[0;38:2::216:191:216ms[0;38:2::216:191:216mt[0;38:2::216:191:216ml[0;38:2::216:191:216me[0;38:2::216:191:216m,[0;38:2::216:191:216m [0;38:2::64:224:208mt[0;38:2::64:224:208mu[0;38:2::64:224:208mr[0;38:2::64:224:208mq[0;38:2::64:224:208mu[0;38:2::64:224:208mo[0;38:2::64:224:208mi[0;38:2::64:224:208ms[0;38:2::64:224:208me[0;38:2::64:224:208m,[0;38:2::64:224:208m [0;38:2::245:222:179mw[0;38:2::245:222:179mh[0;38:2::245:222:179me[0;38:2::245:222:179ma[0;38:2::245:222:179mt[0;38:2::245:222:179m,[0;38:2::245:222:179m [0;38:2::245:245:245mw[0;38:2::245:245:245mh[0;38:2::245:245:245mi[0;38:2::245:245:245mt[0;38:2::245:245:245me[0;38:2::245:245:245ms[0;38:2::245:245:245mm[0;38:2::245:245:245mo[0;38:2::245:245:245mk[0;38:2::245:245:245me[0;38:2::245:245:245m,[0;38:2::245:245:245m [0;38:2::154:205:50my[0;38:2::154:205:50me[0;38:2::154:205:50ml[0;38:2::154:205:50ml[0;38:2::154:205:50mo[0;38:2::154:205:50mw[0;38:2::154:205:50mg[0;38:2::154:205:50mr[0;38:2::154:205:50me[0;38:2::154:205:50me[0;38:2::154:205:50mn[0;38:2::154:205:50m,[0;38:2::154:205:50m [0;38:2::0:255:0mg[0;38:2::0:255:0mr[0;38:2::0:255:0me[0;38:2::0:255:0me[0;38:2::0:255:0mn[0;38:2::0:255:0m [0;38:2::0:255:0mo[0;38:2::0:255:0mn[0;38:2::0:255:0m [0;38:2::0:255:0md[0;38:2::0:255:0me[0;38:2::0:255:0mf[0;38:2::0:255:0ma[0;38:2::0:255:0mu[0;38:2::0:255:0ml[0;38:2::0:255:0mt[0;38:2::0:255:0m,[0;38:2::0:255:0m  12 | #5[0;38:2::0:0:0mb[0;38:2::0:0:0ml[0;38:2::0:0:0ma[0;38:2::0:0:0mc[0;38:2::0:0:0mk[0;38:2::0:0:0m [0;38:2::0:0:0mo[0;38:2::0:0:0mn[0;38:2::0:0:0m [0;38:2::0:0:0md[0;38:2::0:0:0me[0;38:2::0:0:0mf[0;38:2::0:0:0ma[0;38:2::0:0:0mu[0;38:2::0:0:0ml[0;38:2::0:0:0mt[0;38:2::0:0:0m,[0;38:2::0:0:0m [0;48:2::0:255:0md[0;48:2::0:255:0me[0;48:2::0:255:0mf[0;48:2::0:255:0ma[0;48:2::0:255:0mu[0;48:2::0:255:0ml[0;48:2::0:255:0mt[0;48:2::0:255:0m [0;48:2::0:255:0mo[0;48:2::0:255:0mn[0;48:2::0:255:0m [0;48:2::0:255:0mg[0;48:2::0:255:0mr[0;48:2::0:255:0me[0;48:2::0:255:0me[0;48:2::0:255:0mn[0;48:2::0:255:0m,[0;48:2::0:255:0m [0;48:2::0:0:0md[0;48:2::0:0:0me[0;48:2::0:0:0mf[0;48:2::0:0:0ma[0;48:2::0:0:0mu[0;48:2::0:0:0ml[0;48:2::0:0:0mt[0;48:2::0:0:0m [0;48:2::0:0:0mo[0;48:2::0:0:0mn[0;48:2::0:0:0m [0;48:2::0:0:0mb[0;48:2::0:0:0ml[0;48:2::0:0:0ma[0;48:2::0:0:0mc[0;48:2::0:0:0mk[0;48:2::0:0:0m,[0;48:2::0:0:0m default on default, 13 | #5[0;38:2::0:0:0;48:2::0:0:0mb[0;38:2::0:0:0;48:2::0:0:0ml[0;38:2::0:0:0;48:2::0:0:0ma[0;38:2::0:0:0;48:2::0:0:0mc[0;38:2::0:0:0;48:2::0:0:0mk[0;38:2::0:0:0;48:2::0:0:0m [0;38:2::0:0:0;48:2::0:0:0mo[0;38:2::0:0:0;48:2::0:0:0mn[0;38:2::0:0:0;48:2::0:0:0m [0;38:2::0:0:0;48:2::0:0:0mb[0;38:2::0:0:0;48:2::0:0:0ml[0;38:2::0:0:0;48:2::0:0:0ma[0;38:2::0:0:0;48:2::0:0:0mc[0;38:2::0:0:0;48:2::0:0:0mk[0;38:2::0:0:0;48:2::0:0:0m [0;38:2::0:0:0;48:2::0:0:0mw[0;38:2::0:0:0;48:2::0:0:0m/[0;38:2::0:0:0;48:2::0:0:0m [0;38:2::0:0:0;48:2::0:0:0mi[0;38:2::0:0:0;48:2::0:0:0mg[0;38:2::0:0:0;48:2::0:0:0mn[0;38:2::0:0:0;48:2::0:0:0mo[0;38:2::0:0:0;48:2::0:0:0mr[0;38:2::0:0:0;48:2::0:0:0me[0;38:2::0:0:0;48:2::0:0:0md[0;38:2::0:0:0;48:2::0:0:0m [0;38:2::0:0:0;48:2::0:0:0mb[0;38:2::0:0:0;48:2::0:0:0mi[0;38:2::0:0:0;48:2::0:0:0mt[0;38:2::0:0:0;48:2::0:0:0ms[0;38:2::0:0:0;48:2::0:0:0m,[0;38:2::0:0:0;48:2::0:0:0m italic on default,  14 | #5[0;1;38:2::128:128:128my[0;1;38:2::128:128:128me[0;1;38:2::128:128:128ms[0;1;38:2::128:128:128m [0;1;38:2::128:128:128mb[0;1;38:2::128:128:128mo[0;1;38:2::128:128:128ml[0;1;38:2::128:128:128md[0;1;38:2::128:128:128m [0;1;38:2::128:128:128m([0;1;38:2::128:128:128m#[0;1;38:2::128:128:128m1[0;1;38:2::128:128:128m8[0;1;38:2::128:128:128m0[0;1;38:2::128:128:128m8[0;1;38:2::128:128:128m0[0;1;38:2::128:128:128m8[0;1;38:2::128:128:128m0[0;1;38:2::128:128:128m) 15 | #5[0;4;38:2::128:128:128my[0;4;38:2::128:128:128me[0;4;38:2::128:128:128ms[0;4;38:2::128:128:128m [0;4;38:2::128:128:128mu[0;4;38:2::128:128:128mn[0;4;38:2::128:128:128md[0;4;38:2::128:128:128me[0;4;38:2::128:128:128mr[0;4;38:2::128:128:128ml[0;4;38:2::128:128:128mi[0;4;38:2::128:128:128mn[0;4;38:2::128:128:128me[0;4;38:2::128:128:128m [0;4;38:2::128:128:128m([0;4;38:2::128:128:128m#[0;4;38:2::128:128:128m2[0;4;38:2::128:128:128m8[0;4;38:2::128:128:128m0[0;4;38:2::128:128:128m8[0;4;38:2::128:128:128m0[0;4;38:2::128:128:128m8[0;4;38:2::128:128:128m0[0;4;38:2::128:128:128m) 16 | #5[0;3;38:2::128:128:128my[0;3;38:2::128:128:128me[0;3;38:2::128:128:128ms[0;3;38:2::128:128:128m [0;3;38:2::128:128:128mi[0;3;38:2::128:128:128mt[0;3;38:2::128:128:128ma[0;3;38:2::128:128:128ml[0;3;38:2::128:128:128mi[0;3;38:2::128:128:128mc[0;3;38:2::128:128:128m [0;3;38:2::128:128:128m([0;3;38:2::128:128:128m#[0;3;38:2::128:128:128m8[0;3;38:2::128:128:128m8[0;3;38:2::128:128:128m0[0;3;38:2::128:128:128m8[0;3;38:2::128:128:128m0[0;3;38:2::128:128:128m8[0;3;38:2::128:128:128m0[0;3;38:2::128:128:128m) 17 | #5[0;38:2::0:0:0;48:2::255:255:255m#[0;38:2::0:0:0;48:2::255:255:255m0[0;38:2::0:0:0;48:2::255:255:255m0[0;38:2::0:0:0;48:2::255:255:255m0[0;38:2::0:0:0;48:2::255:255:255m0[0;38:2::0:0:0;48:2::255:255:255m0[0;38:2::0:0:0;48:2::255:255:255m0[0;38:2::0:0:0;48:2::255:255:255m [0;38:2::0:0:0;48:2::255:255:255mo[0;38:2::0:0:0;48:2::255:255:255mn[0;38:2::0:0:0;48:2::255:255:255m [0;38:2::0:0:0;48:2::255:255:255m#[0;38:2::0:0:0;48:2::255:255:255mf[0;38:2::0:0:0;48:2::255:255:255mf[0;38:2::0:0:0;48:2::255:255:255mf[0;38:2::0:0:0;48:2::255:255:255mf[0;38:2::0:0:0;48:2::255:255:255mf[0;38:2::0:0:0;48:2::255:255:255mf[0;38:2::0:0:0;48:2::255:255:255m [0;38:2::0:0:255;48:2::255:255:0m#[0;38:2::0:0:255;48:2::255:255:0m0[0;38:2::0:0:255;48:2::255:255:0m0[0;38:2::0:0:255;48:2::255:255:0m0[0;38:2::0:0:255;48:2::255:255:0m0[0;38:2::0:0:255;48:2::255:255:0mf[0;38:2::0:0:255;48:2::255:255:0mf[0;38:2::0:0:255;48:2::255:255:0m [0;38:2::0:0:255;48:2::255:255:0mo[0;38:2::0:0:255;48:2::255:255:0mn[0;38:2::0:0:255;48:2::255:255:0m [0;38:2::0:0:255;48:2::255:255:0m#[0;38:2::0:0:255;48:2::255:255:0mf[0;38:2::0:0:255;48:2::255:255:0mf[0;38:2::0:0:255;48:2::255:255:0mf[0;38:2::0:0:255;48:2::255:255:0mf[0;38:2::0:0:255;48:2::255:255:0m0[0;38:2::0:0:255;48:2::255:255:0m0[0;38:2::0:0:255;48:2::255:255:0m [0;38:2::0:255:0;48:2::255:0:255m#[0;38:2::0:255:0;48:2::255:0:255m0[0;38:2::0:255:0;48:2::255:0:255m0[0;38:2::0:255:0;48:2::255:0:255mf[0;38:2::0:255:0;48:2::255:0:255mf[0;38:2::0:255:0;48:2::255:0:255m0[0;38:2::0:255:0;48:2::255:0:255m0[0;38:2::0:255:0;48:2::255:0:255m [0;38:2::0:255:0;48:2::255:0:255mo[0;38:2::0:255:0;48:2::255:0:255mn[0;38:2::0:255:0;48:2::255:0:255m [0;38:2::0:255:0;48:2::255:0:255m#[0;38:2::0:255:0;48:2::255:0:255mf[0;38:2::0:255:0;48:2::255:0:255mf[0;38:2::0:255:0;48:2::255:0:255m0[0;38:2::0:255:0;48:2::255:0:255m0[0;38:2::0:255:0;48:2::255:0:255mf[0;38:2::0:255:0;48:2::255:0:255mf[0;38:2::0:255:0;48:2::255:0:255m [0;38:2::0:255:255;48:2::255:0:0m#[0;38:2::0:255:255;48:2::255:0:0m0[0;38:2::0:255:255;48:2::255:0:0m0[0;38:2::0:255:255;48:2::255:0:0mf[0;38:2::0:255:255;48:2::255:0:0mf[0;38:2::0:255:255;48:2::255:0:0mf[0;38:2::0:255:255;48:2::255:0:0mf[0;38:2::0:255:255;48:2::255:0:0m [0;38:2::0:255:255;48:2::255:0:0mo[0;38:2::0:255:255;48:2::255:0:0mn[0;38:2::0:255:255;48:2::255:0:0m [0;38:2::0:255:255;48:2::255:0:0m#[0;38:2::0:255:255;48:2::255:0:0mf[0;38:2::0:255:255;48:2::255:0:0mf[0;38:2::0:255:255;48:2::255:0:0m0[0;38:2::0:255:255;48:2::255:0:0m0[0;38:2::0:255:255;48:2::255:0:0m0[0;38:2::0:255:255;48:2::255:0:0m0[0;38:2::0:255:255;48:2::255:0:0m  18 | #5[0;38:2::255:0:0;48:2::0:255:255m#[0;38:2::255:0:0;48:2::0:255:255mf[0;38:2::255:0:0;48:2::0:255:255mf[0;38:2::255:0:0;48:2::0:255:255m0[0;38:2::255:0:0;48:2::0:255:255m0[0;38:2::255:0:0;48:2::0:255:255m0[0;38:2::255:0:0;48:2::0:255:255m0[0;38:2::255:0:0;48:2::0:255:255m [0;38:2::255:0:0;48:2::0:255:255mo[0;38:2::255:0:0;48:2::0:255:255mn[0;38:2::255:0:0;48:2::0:255:255m [0;38:2::255:0:0;48:2::0:255:255m#[0;38:2::255:0:0;48:2::0:255:255m0[0;38:2::255:0:0;48:2::0:255:255m0[0;38:2::255:0:0;48:2::0:255:255mf[0;38:2::255:0:0;48:2::0:255:255mf[0;38:2::255:0:0;48:2::0:255:255mf[0;38:2::255:0:0;48:2::0:255:255mf[0;38:2::255:0:0;48:2::0:255:255m [0;38:2::255:0:255;48:2::0:255:0m#[0;38:2::255:0:255;48:2::0:255:0mf[0;38:2::255:0:255;48:2::0:255:0mf[0;38:2::255:0:255;48:2::0:255:0m0[0;38:2::255:0:255;48:2::0:255:0m0[0;38:2::255:0:255;48:2::0:255:0mf[0;38:2::255:0:255;48:2::0:255:0mf[0;38:2::255:0:255;48:2::0:255:0m [0;38:2::255:0:255;48:2::0:255:0mo[0;38:2::255:0:255;48:2::0:255:0mn[0;38:2::255:0:255;48:2::0:255:0m [0;38:2::255:0:255;48:2::0:255:0m#[0;38:2::255:0:255;48:2::0:255:0m0[0;38:2::255:0:255;48:2::0:255:0m0[0;38:2::255:0:255;48:2::0:255:0mf[0;38:2::255:0:255;48:2::0:255:0mf[0;38:2::255:0:255;48:2::0:255:0m0[0;38:2::255:0:255;48:2::0:255:0m0[0;38:2::255:0:255;48:2::0:255:0m [0;38:2::255:255:0;48:2::0:0:255m#[0;38:2::255:255:0;48:2::0:0:255mf[0;38:2::255:255:0;48:2::0:0:255mf[0;38:2::255:255:0;48:2::0:0:255mf[0;38:2::255:255:0;48:2::0:0:255mf[0;38:2::255:255:0;48:2::0:0:255m0[0;38:2::255:255:0;48:2::0:0:255m0[0;38:2::255:255:0;48:2::0:0:255m [0;38:2::255:255:0;48:2::0:0:255mo[0;38:2::255:255:0;48:2::0:0:255mn[0;38:2::255:255:0;48:2::0:0:255m [0;38:2::255:255:0;48:2::0:0:255m#[0;38:2::255:255:0;48:2::0:0:255m0[0;38:2::255:255:0;48:2::0:0:255m0[0;38:2::255:255:0;48:2::0:0:255m0[0;38:2::255:255:0;48:2::0:0:255m0[0;38:2::255:255:0;48:2::0:0:255mf[0;38:2::255:255:0;48:2::0:0:255mf[0;38:2::255:255:0;48:2::0:0:255m [0;38:2::255:255:255;48:2::0:0:0m#[0;38:2::255:255:255;48:2::0:0:0mf[0;38:2::255:255:255;48:2::0:0:0mf[0;38:2::255:255:255;48:2::0:0:0mf[0;38:2::255:255:255;48:2::0:0:0mf[0;38:2::255:255:255;48:2::0:0:0mf[0;38:2::255:255:255;48:2::0:0:0mf[0;38:2::255:255:255;48:2::0:0:0m [0;38:2::255:255:255;48:2::0:0:0mo[0;38:2::255:255:255;48:2::0:0:0mn[0;38:2::255:255:255;48:2::0:0:0m [0;38:2::255:255:255;48:2::0:0:0m#[0;38:2::255:255:255;48:2::0:0:0m0[0;38:2::255:255:255;48:2::0:0:0m0[0;38:2::255:255:255;48:2::0:0:0m0[0;38:2::255:255:255;48:2::0:0:0m0[0;38:2::255:255:255;48:2::0:0:0m0[0;38:2::255:255:255;48:2::0:0:0m0[0;38:2::255:255:255;48:2::0:0:0m  19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_color_true/test.php: -------------------------------------------------------------------------------- 1 | ffi->tb_has_truecolor()) { 5 | // This will only work with truecolor support 6 | $test->skip(); 7 | } 8 | 9 | $attr_bold = $test->defines['TB_BOLD']; 10 | $attr_underline = $test->defines['TB_UNDERLINE']; 11 | $attr_italic = $test->defines['TB_ITALIC']; 12 | $attr_default = $test->defines['TB_DEFAULT']; 13 | $true_black = $test->defines['TB_HI_BLACK']; 14 | 15 | $css_colors = [ 16 | 'antiquewhite' => [ 0xfaebd7, $attr_default ], 17 | 'aquamarine' => [ 0x7fffd4, $attr_default ], 18 | 'beige' => [ 0xf5f5dc, $attr_default ], 19 | 'black' => [ $true_black, $attr_default ], 20 | 'blue' => [ 0x0000ff, $attr_default ], 21 | 'brown' => [ 0xa52a2a, $attr_default ], 22 | 'cadetblue' => [ 0x5f9ea0, $attr_default ], 23 | 'chocolate' => [ 0xd2691e, $attr_default ], 24 | 'cornflowerblue' => [ 0x6495ed, $attr_default ], 25 | 'crimson' => [ 0xdc143c, $attr_default ], 26 | 'darkblue' => [ 0x00008b, $attr_default ], 27 | 'darkgoldenrod' => [ 0xb8860b, $attr_default ], 28 | 'darkgreen' => [ 0x006400, $attr_default ], 29 | 'darkkhaki' => [ 0xbdb76b, $attr_default ], 30 | 'darkolivegreen' => [ 0x556b2f, $attr_default ], 31 | 'darkorchid' => [ 0x9932cc, $attr_default ], 32 | 'darksalmon' => [ 0xe9967a, $attr_default ], 33 | 'darkslateblue' => [ 0x483d8b, $attr_default ], 34 | 'darkturquoise' => [ 0x00ced1, $attr_default ], 35 | 'deeppink' => [ 0xff1493, $attr_default ], 36 | 'dimgray' => [ 0x696969, $attr_default ], 37 | 'dodgerblue' => [ 0x1e90ff, $attr_default ], 38 | 'floralwhite' => [ 0xfffaf0, $attr_default ], 39 | 'fuchsia' => [ 0xff00ff, $attr_default ], 40 | 'ghostwhite' => [ 0xf8f8ff, $attr_default ], 41 | 'goldenrod' => [ 0xdaa520, $attr_default ], 42 | 'green' => [ 0x008000, $attr_default ], 43 | 'grey' => [ 0x808080, $attr_default ], 44 | 'hotpink' => [ 0xff69b4, $attr_default ], 45 | 'indigo' => [ 0x4b0082, $attr_default ], 46 | 'khaki' => [ 0xf0e68c, $attr_default ], 47 | 'lavenderblush' => [ 0xfff0f5, $attr_default ], 48 | 'lemonchiffon' => [ 0xfffacd, $attr_default ], 49 | 'lightcoral' => [ 0xf08080, $attr_default ], 50 | 'lightgoldenrodyellow' => [ 0xfafad2, $attr_default ], 51 | 'lightgreen' => [ 0x90ee90, $attr_default ], 52 | 'lightpink' => [ 0xffb6c1, $attr_default ], 53 | 'lightseagreen' => [ 0x20b2aa, $attr_default ], 54 | 'lightslategrey' => [ 0x778899, $attr_default ], 55 | 'lightyellow' => [ 0xffffe0, $attr_default ], 56 | 'limegreen' => [ 0x32cd32, $attr_default ], 57 | 'magenta' => [ 0xff00ff, $attr_default ], 58 | 'mediumaquamarine' => [ 0x66cdaa, $attr_default ], 59 | 'mediumorchid' => [ 0xba55d3, $attr_default ], 60 | 'mediumseagreen' => [ 0x3cb371, $attr_default ], 61 | 'mediumspringgreen' => [ 0x00fa9a, $attr_default ], 62 | 'mediumvioletred' => [ 0xc71585, $attr_default ], 63 | 'mintcream' => [ 0xf5fffa, $attr_default ], 64 | 'moccasin' => [ 0xffe4b5, $attr_default ], 65 | 'navy' => [ 0x000080, $attr_default ], 66 | 'olive' => [ 0x808000, $attr_default ], 67 | 'orange' => [ 0xffa500, $attr_default ], 68 | 'orchid' => [ 0xda70d6, $attr_default ], 69 | 'palegreen' => [ 0x98fb98, $attr_default ], 70 | 'palevioletred' => [ 0xdb7093, $attr_default ], 71 | 'peachpuff' => [ 0xffdab9, $attr_default ], 72 | 'pink' => [ 0xffc0cb, $attr_default ], 73 | 'powderblue' => [ 0xb0e0e6, $attr_default ], 74 | 'red' => [ 0xff0000, $attr_default ], 75 | 'royalblue' => [ 0x4169e1, $attr_default ], 76 | 'salmon' => [ 0xfa8072, $attr_default ], 77 | 'seagreen' => [ 0x2e8b57, $attr_default ], 78 | 'sienna' => [ 0xa0522d, $attr_default ], 79 | 'skyblue' => [ 0x87ceeb, $attr_default ], 80 | 'slategrey' => [ 0x708090, $attr_default ], 81 | 'springgreen' => [ 0x00ff7f, $attr_default ], 82 | 'tan' => [ 0xd2b48c, $attr_default ], 83 | 'thistle' => [ 0xd8bfd8, $attr_default ], 84 | 'turquoise' => [ 0x40e0d0, $attr_default ], 85 | 'wheat' => [ 0xf5deb3, $attr_default ], 86 | 'whitesmoke' => [ 0xf5f5f5, $attr_default ], 87 | 'yellowgreen' => [ 0x9acd32, $attr_default ], 88 | 'green on default' => [ 0x00ff00, $attr_default ], 89 | 'black on default' => [ $true_black, $attr_default ], 90 | 'default on green' => [ $attr_default, 0x00ff00 ], 91 | 'default on black' => [ $attr_default, $true_black ], 92 | 'default on default' => [ $attr_default, $attr_default ], 93 | 'black on black w/ ignored bits' => [ 0x123456 | $true_black, 0x789abc | $true_black ], 94 | 'italic on default' => [ $attr_default | $attr_italic, $attr_default ], 95 | ]; 96 | 97 | $test->ffi->tb_init(); 98 | 99 | $w = $test->ffi->tb_width(); 100 | $h = $test->ffi->tb_height(); 101 | 102 | $test->ffi->tb_set_output_mode($test->defines['TB_OUTPUT_TRUECOLOR']); 103 | 104 | // Test true colors 105 | $x = 0; 106 | $y = 0; 107 | foreach ($css_colors as $name => [$fg, $bg]) { 108 | $s = "{$name}, "; 109 | $slen = strlen($s); 110 | if ($x + $slen > $w) { 111 | $x = 0; 112 | $y++; 113 | } 114 | $test->ffi->tb_print($x, $y, $fg, $bg, $s); 115 | $x += $slen; 116 | } 117 | 118 | // Test bold, underline, italic in true-color mode 119 | $x = 0; 120 | $color = 0x808080 | $attr_bold; 121 | $test->ffi->tb_printf($x, ++$y, $color, $attr_default, 'yes bold (#%06x)', $color); 122 | $color = 0x808080 | $attr_underline; 123 | $test->ffi->tb_printf($x, ++$y, $color, $attr_default, 'yes underline (#%06x)', $color); 124 | $color = 0x808080 | $attr_italic; 125 | $test->ffi->tb_printf($x, ++$y, $color, $attr_default, 'yes italic (#%06x)', $color); 126 | 127 | // Test fg/bg together 128 | $x = 0; 129 | $y += 1; 130 | for ($r = 0x00; $r <= 0xff; $r += 0xff) { 131 | for ($g = 0x00; $g <= 0xff; $g += 0xff) { 132 | for ($b = 0x00; $b <= 0xff; $b += 0xff) { 133 | $fg = ($r << 16) + ($g << 8) + $b; 134 | $bg = ((0xff - $r) << 16) + ((0xff - $g) << 8) + (0xff - $b); 135 | $str = sprintf('#%06x on #%06x ', $fg, $bg); 136 | $slen = strlen($str); 137 | if ($x + $slen > $w) { 138 | $x = 0; 139 | $y++; 140 | } 141 | if ($fg == 0) $fg = $true_black; 142 | if ($bg == 0) $bg = $true_black; 143 | $test->ffi->tb_print($x, $y, $fg, $bg, $str); 144 | $x += $slen; 145 | } 146 | } 147 | } 148 | 149 | $test->ffi->tb_present(); 150 | 151 | $test->screencap(); 152 | -------------------------------------------------------------------------------- /tests/test_egc/expected.ansi: -------------------------------------------------------------------------------- 1 | #5aёb 2 | #5cёd 3 | #5I̤ͯ 4 | #5I̤ͯ 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_egc/test.php: -------------------------------------------------------------------------------- 1 | ffi->tb_has_egc()) { 5 | // This will only work with extended grapheme cluster support 6 | $test->skip(); 7 | } 8 | 9 | $test->ffi->tb_init(); 10 | 11 | $ech = $test->ffi->new('uint32_t[2]'); 12 | $test->ffi->tb_utf8_char_to_unicode(FFI::addr($ech[0]), "\xd0\xb5"); // cyrillic small letter ie 13 | $test->ffi->tb_utf8_char_to_unicode(FFI::addr($ech[1]), "\xcc\x88"); // combining diaeresis 14 | 15 | // Set EGC using tb_set_cell_ex 16 | $y = 0; 17 | $test->ffi->tb_set_cell(0, $y, ord('a'), 0, 0); 18 | $test->ffi->tb_set_cell_ex(1, $y, $test->ffi->cast('uint32_t *', FFI::addr($ech)), 2, 0, 0); 19 | $test->ffi->tb_set_cell(2, $y, ord('b'), 0, 0); 20 | 21 | // Same effect with tb_extend_cell 22 | ++$y; 23 | $test->ffi->tb_set_cell(0, $y, ord('c'), 0, 0); 24 | $test->ffi->tb_set_cell(1, $y, $ech[0], 0, 0); 25 | $test->ffi->tb_extend_cell(1, $y, $ech[1]); 26 | $test->ffi->tb_set_cell(2, $y, ord('d'), 0, 0); 27 | 28 | // Another example with 2 combining characters 29 | ++$y; 30 | $test->ffi->tb_set_cell(0, $y, 0xff29, 0, 0); // fullwidth latin capital letter i 31 | $test->ffi->tb_extend_cell(0, $y, 0x0324); // combining diaeresis below 32 | $test->ffi->tb_extend_cell(0, $y, 0x036f); // combining latin small letter x 33 | 34 | // Same as above via tb_print (should be equivalent) 35 | ++$y; 36 | $test->ffi->tb_print(0, $y, 0, 0, "\xef\xbc\xa9\xcc\xa4\xcd\xaf"); 37 | 38 | $test->ffi->tb_present(); 39 | $test->screencap(); 40 | -------------------------------------------------------------------------------- /tests/test_error/expected.ansi: -------------------------------------------------------------------------------- 1 | #5oob err=-9 errmsg=Out of bounds 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_error/test.php: -------------------------------------------------------------------------------- 1 | ffi->tb_init(); 5 | 6 | $w = $test->ffi->tb_width(); 7 | $h = $test->ffi->tb_height(); 8 | 9 | // try to set a cell out of bounds 10 | $err = $test->ffi->tb_set_cell(-1, -1, 'x', 0, 0); 11 | $errmsg = $test->ffi->tb_strerror($err); 12 | 13 | $test->ffi->tb_printf(0, 0, 0, 0, "oob err=%d errmsg=%s", $err, $errmsg); 14 | 15 | $test->ffi->tb_present(); 16 | 17 | $test->screencap(); 18 | -------------------------------------------------------------------------------- /tests/test_ffi.php: -------------------------------------------------------------------------------- 1 | ffi->tb_init(); 7 | $test->ffi->tb_shutdown(); 8 | 9 | file_put_contents("/dev/tty", "no_segfault\n"); 10 | 11 | $test->screencap(); 12 | -------------------------------------------------------------------------------- /tests/test_invalid_utf8/expected.ansi: -------------------------------------------------------------------------------- 1 | #5foo# 2 | #5# 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_invalid_utf8/test.php: -------------------------------------------------------------------------------- 1 | ffi->tb_init(); 5 | 6 | $y = 0; 7 | $test->ffi->tb_print_ex(0, $y++, 0, 0, NULL, "foo\xc2\x00password"); // stop at NULL 8 | $test->ffi->tb_set_cell(0, $y++, 0xffff, 0, 0); // invalid codepoint 9 | 10 | $test->ffi->tb_present(); 11 | 12 | $test->screencap(); 13 | -------------------------------------------------------------------------------- /tests/test_invalidate/expected.ansi: -------------------------------------------------------------------------------- 1 | #5[0;38:5:238mc[0;38:5:238my[0;38:5:238ma[0;38:5:238mn[0;38:5:238m [0;38:5:238m([0;38:5:238mt[0;38:5:238mh[0;38:5:238me[0;38:5:238mn[0;38:5:238m [0;38:5:238mg[0;38:5:238mr[0;38:5:238ma[0;38:5:238my[0;38:5:238m [0;38:5:238ma[0;38:5:238mf[0;38:5:238mt[0;38:5:238me[0;38:5:238mr[0;38:5:238m [0;38:5:238mm[0;38:5:238mo[0;38:5:238md[0;38:5:238me[0;38:5:238m [0;38:5:238ms[0;38:5:238mw[0;38:5:238mi[0;38:5:238mt[0;38:5:238mc[0;38:5:238mh[0;38:5:238m [0;38:5:238ma[0;38:5:238mn[0;38:5:238md[0;38:5:238m [0;38:5:238mi[0;38:5:238mn[0;38:5:238mv[0;38:5:238ma[0;38:5:238ml[0;38:5:238mi[0;38:5:238md[0;38:5:238ma[0;38:5:238mt[0;38:5:238me[0;38:5:238m) 2 | #5[0;38:5:238mg[0;38:5:238mr[0;38:5:238ma[0;38:5:238my 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_invalidate/test.php: -------------------------------------------------------------------------------- 1 | ffi->tb_init(); 5 | 6 | $y = 0; 7 | $fg = 7; 8 | 9 | $test->ffi->tb_printf(0, $y++, $fg, 0, "cyan (then gray after mode switch and invalidate)"); 10 | $test->ffi->tb_present(); 11 | 12 | $test->ffi->tb_set_output_mode($test->defines['TB_OUTPUT_GRAYSCALE']); 13 | $test->ffi->tb_invalidate(); 14 | 15 | $test->ffi->tb_printf(0, $y++, $fg, 0, "gray"); 16 | $test->ffi->tb_present(); 17 | 18 | $test->screencap(); 19 | -------------------------------------------------------------------------------- /tests/test_large_input/expected.ansi: -------------------------------------------------------------------------------- 1 | #5up_arrow_count=22 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_large_input/test.php: -------------------------------------------------------------------------------- 1 | memfd_create('ttyin', 0); 10 | $ttyout = $libc->memfd_create('ttyout', 0); 11 | $test->ffi->tb_init_rwfd($ttyin, $ttyout); 12 | 13 | // we're going to send a bunch of up-arrow escape sequences to termbox via our 14 | // fake tty, and then ensure that termbox emits the expected up-arrow events 15 | // via `tb_peek_event`. we will send enough data to fill up the read buffer 16 | // (TB_OPT_READ_BUF). 17 | $up_arrow = "\x1bOA"; 18 | $read_buf_size = $test->defines['TB_OPT_READ_BUF']; 19 | $num_up_arrows = (int)ceil($read_buf_size / strlen($up_arrow)); 20 | $input_data = str_repeat($up_arrow, $num_up_arrows); 21 | $fttyin = fopen("php://fd/$ttyin", 'w'); 22 | $nbytes = fwrite($fttyin, $input_data); 23 | fseek($fttyin, strlen($input_data) * -1, SEEK_CUR); 24 | 25 | // count how many up arrow events termbox emits 26 | $up_arrow_count = 0; 27 | $test->ffi->tb_set_input_mode($test->defines['TB_INPUT_ALT']); 28 | $event = $test->ffi->new('struct tb_event'); 29 | do { 30 | $rv = $test->ffi->tb_peek_event(FFI::addr($event), 1000); 31 | if ($rv == 0 && $event->key === $test->defines['TB_KEY_ARROW_UP']) { 32 | $up_arrow_count += 1; 33 | } 34 | } while ($rv == 0); 35 | 36 | // close fake termbox setup 37 | fclose($fttyin); 38 | $libc->close($ttyin); 39 | $libc->close($ttyout); 40 | $test->ffi->tb_shutdown(); 41 | 42 | // display up_arrow_count 43 | $test->ffi->tb_init(); 44 | $test->ffi->tb_printf(0, 0, 0, 0, "up_arrow_count=%d", $up_arrow_count); 45 | $test->ffi->tb_present(); 46 | $test->screencap(); 47 | -------------------------------------------------------------------------------- /tests/test_mod/expected.ansi: -------------------------------------------------------------------------------- 1 | #5event=1,4,65517,0 2 | #5event=1,1,65517,0 3 | #5event=1,5,65517,0 4 | #5event=1,2,65517,0 5 | #5event=1,6,65517,0 6 | #5event=1,3,65517,0 7 | #5event=1,7,65517,0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_mod/test.php: -------------------------------------------------------------------------------- 1 | memfd_create('ttyin', 0); 10 | $ttyout = $libc->memfd_create('ttyout', 0); 11 | $test->ffi->tb_init_rwfd($ttyin, $ttyout); 12 | $input_data = 13 | "\x1b[1;2A" . // TB_KEY_ARROW_UP, TB_MOD_SHIFT 14 | "\x1b[1;3A" . // TB_KEY_ARROW_UP, TB_MOD_ALT 15 | "\x1b[1;4A" . // TB_KEY_ARROW_UP, TB_MOD_ALT | TB_MOD_SHIFT 16 | "\x1b[1;5A" . // TB_KEY_ARROW_UP, TB_MOD_CTRL 17 | "\x1b[1;6A" . // TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_SHIFT 18 | "\x1b[1;7A" . // TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT 19 | "\x1b[1;8A" ; // TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT 20 | $fttyin = fopen("php://fd/$ttyin", 'w'); 21 | $nbytes = fwrite($fttyin, $input_data); 22 | fseek($fttyin, strlen($input_data) * -1, SEEK_CUR); 23 | 24 | // record events that termbox emits 25 | $events = []; 26 | $test->ffi->tb_set_input_mode($test->defines['TB_INPUT_ALT']); 27 | $e = $test->ffi->new('struct tb_event'); 28 | do { 29 | $rv = $test->ffi->tb_peek_event(FFI::addr($e), 1000); 30 | if ($rv == 0) { 31 | $events[] = [ $e->type, $e->mod, $e->key, $e->ch ]; 32 | } 33 | } while ($rv == 0); 34 | 35 | // close fake termbox setup 36 | fclose($fttyin); 37 | $libc->close($ttyin); 38 | $libc->close($ttyout); 39 | $test->ffi->tb_shutdown(); 40 | 41 | // display events 42 | $test->ffi->tb_init(); 43 | $y = 0; 44 | foreach ($events as $e) { 45 | $test->ffi->tb_printf(0, $y++, 0, 0, "event=%s", implode(',', $e)); 46 | } 47 | $test->ffi->tb_present(); 48 | $test->screencap(); 49 | -------------------------------------------------------------------------------- /tests/test_non_printable/expected.ansi: -------------------------------------------------------------------------------- 1 | #50x00 # 2 | #50x01 # 3 | #50x08 # 4 | #50x09 # 5 | #50x0a # 6 | #50x1f # 7 | #50x7f # 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_non_printable/test.php: -------------------------------------------------------------------------------- 1 | ffi->tb_init(); 5 | 6 | $codepoints = [ 7 | 0x00, // NULL 8 | 0x01, // control code 9 | 0x08, // backspace 10 | 0x09, // tab 11 | 0x0a, // newline 12 | 0x1f, // control code 13 | 0x7f, // delete 14 | ]; 15 | 16 | $y = 0; 17 | foreach ($codepoints as $ch) { 18 | $test->ffi->tb_printf(0, $y, 0, 0, "0x%02x ", $ch); 19 | $test->ffi->tb_set_cell(5, $y, $ch, 0, 0); 20 | $y += 1; 21 | } 22 | 23 | $test->ffi->tb_present(); 24 | 25 | $test->screencap(); 26 | -------------------------------------------------------------------------------- /tests/test_non_spacing_mark/expected.ansi: -------------------------------------------------------------------------------- 1 | #5STARGΛ̊TE SG-1 2 | #5a = v̇ = r̈, a⃑ ⊥ b⃑ 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_non_spacing_mark/test.php: -------------------------------------------------------------------------------- 1 | ffi->tb_has_egc()) { 5 | // This will only work with extended grapheme cluster support 6 | $test->skip(); 7 | } 8 | 9 | $test->ffi->tb_init(); 10 | 11 | $test->ffi->tb_print(0, 0, 0, 0, "STARG\xce\x9b\xcc\x8aTE SG-1"); 12 | $test->ffi->tb_print(0, 1, 0, 0, "a = v\xcc\x87 = r\xcc\x88, a\xe2\x83\x91 \xe2\x8a\xa5 b\xe2\x83\x91"); 13 | 14 | 15 | $test->ffi->tb_present(); 16 | $test->screencap(); 17 | -------------------------------------------------------------------------------- /tests/test_output_mode/expected.ansi: -------------------------------------------------------------------------------- 1 | #5cyan (even after mode switch) 2 | #5[0;38:5:238mg[0;38:5:238mr[0;38:5:238ma[0;38:5:238my 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_output_mode/test.php: -------------------------------------------------------------------------------- 1 | ffi->tb_init(); 5 | 6 | $y = 0; 7 | $fg = 7; 8 | 9 | $test->ffi->tb_printf(0, $y++, $fg, 0, "cyan (even after mode switch)"); 10 | $test->ffi->tb_present(); 11 | 12 | $test->ffi->tb_set_output_mode($test->defines['TB_OUTPUT_GRAYSCALE']); 13 | 14 | $test->ffi->tb_printf(0, $y++, $fg, 0, "gray"); 15 | $test->ffi->tb_present(); 16 | 17 | $test->screencap(); 18 | -------------------------------------------------------------------------------- /tests/test_prepend.php: -------------------------------------------------------------------------------- 1 | ffi = $this->makeFfi(); 12 | $this->test_log = $GLOBALS['argv'][1] ?? ''; 13 | } 14 | 15 | private function makeFfi(): object { 16 | // Make FFI 17 | [ $ffi, $defines ] = require __DIR__ . '/test_ffi.php'; 18 | $this->defines = $defines; 19 | 20 | // Return wrapper that logs FFI calls 21 | return new class($ffi, $this) { 22 | private FFI $ffi; 23 | private object $test; 24 | private bool $quiet; 25 | public function __construct($ffi, $test) { 26 | $this->ffi = $ffi; 27 | $this->test = $test; 28 | $this->quiet = false; 29 | } 30 | public function quiet($on_off) { 31 | $this->quiet = $on_off; 32 | } 33 | public function __call(string $name, array $args) { 34 | if (!$this->quiet) { 35 | $this->test->log("ffi $name " . json_encode($args)); 36 | } 37 | return $this->ffi->$name(...$args); 38 | } 39 | }; 40 | } 41 | 42 | public function xvkbd(string $xvkbd_cmd): int { 43 | $this->log("xvkbd $xvkbd_cmd"); 44 | $cmd = sprintf( 45 | "DISPLAY=:1000 xvkbd -remote-display :1000 -window xterm -text %s", 46 | escapeshellarg($xvkbd_cmd) 47 | ); 48 | $sh_cmd = sprintf( 49 | 'sh -c %s >/dev/null 2>&1', 50 | escapeshellarg($cmd) 51 | ); 52 | $output = []; 53 | $exit_code = 1; 54 | exec($sh_cmd, $output, $exit_code); 55 | return $exit_code; 56 | } 57 | 58 | public function log(string $str): void { 59 | if (!$this->test_log) { 60 | return; 61 | } 62 | $lines = explode("\n", $str); 63 | foreach ($lines as $line) { 64 | file_put_contents($this->test_log, " $line\n", FILE_APPEND); 65 | } 66 | } 67 | 68 | public function screencap(): void { 69 | $this->log('screencap'); 70 | sleep(PHP_INT_MAX); 71 | } 72 | 73 | public function skip(): void { 74 | $this->log('skip'); 75 | sleep(PHP_INT_MAX); 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /tests/test_print/expected.ansi: -------------------------------------------------------------------------------- 1 | #5 line1 2 | #5 line2 3 | #5 line3 4 | #5escape=[#] 5 | #5tab=[#] 6 | #5oob_rv1=-9 7 | #5oob_rv2=-9 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | #5 01234 21 | #5 01234 22 | #5 01234 23 | #5 01234 24 | #5 01234 25 | -------------------------------------------------------------------------------- /tests/test_print/test.php: -------------------------------------------------------------------------------- 1 | ffi->tb_init(); 5 | 6 | $w = $test->ffi->tb_width(); 7 | $h = $test->ffi->tb_height(); 8 | 9 | $y = 0; 10 | $test->ffi->tb_print(1, $y++, 0, 0, "line1\nline2\nline3"); 11 | $y += 2; 12 | 13 | $test->ffi->tb_print(0, $y++, 0, 0, "escape=[\x1b]"); 14 | $test->ffi->tb_print(0, $y++, 0, 0, "tab=[\t]"); 15 | 16 | $oob_rv1 = $test->ffi->tb_print($w, $h, 0, 0, "oob1"); 17 | $oob_rv2 = $test->ffi->tb_print(-1, -1, 0, 0, "oob2"); 18 | $test->ffi->tb_printf(0, $y++, 0, 0, "oob_rv1=%d", $oob_rv1); 19 | $test->ffi->tb_printf(0, $y++, 0, 0, "oob_rv2=%d", $oob_rv2); 20 | 21 | $test->ffi->tb_print($w - 5, $h - 5, 0, 0, str_repeat("0123456789\n", 10)); 22 | 23 | $test->ffi->tb_present(); 24 | 25 | $test->screencap(); 26 | -------------------------------------------------------------------------------- /tests/test_resize/expected.ansi: -------------------------------------------------------------------------------- 1 | #5event rv=0 type=2 ow=80 oh=24 w=80 h=24 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_resize/test.php: -------------------------------------------------------------------------------- 1 | ffi->tb_init(); 9 | 10 | $w = $test->ffi->tb_width(); 11 | $h = $test->ffi->tb_height(); 12 | 13 | $libc->raise(SIGWINCH); 14 | 15 | $event = $test->ffi->new('struct tb_event'); 16 | $rv = $test->ffi->tb_peek_event(FFI::addr($event), 1000); 17 | 18 | $test->ffi->tb_printf(0, 0, 0, 0, "event rv=%d type=%d ow=%d oh=%d w=%d h=%d", 19 | $rv, 20 | $event->type, 21 | $w, 22 | $h, 23 | $event->w, 24 | $event->h, 25 | ); 26 | 27 | $test->ffi->tb_present(); 28 | 29 | $test->screencap(); 30 | -------------------------------------------------------------------------------- /tests/test_wcwidth/expected.ansi: -------------------------------------------------------------------------------- 1 | #5mismatch=0 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test_wcwidth/test.php: -------------------------------------------------------------------------------- 1 | ffi->tb_init(); 8 | 9 | $mismatch = 0; 10 | 11 | $test->ffi->quiet(true); 12 | for ($c = 0; $c <= 0x10ffff; $c++) { 13 | if ($test->ffi->tb_wcwidth($c) !== $libc->wcwidth($c)) { 14 | ++$mismatch; 15 | } 16 | if ((bool)$test->ffi->tb_iswprint($c) !== (bool)$libc->iswprint($c)) { 17 | ++$mismatch; 18 | } 19 | } 20 | $test->ffi->quiet(false); 21 | 22 | $test->ffi->tb_printf(0, 0, 0, 0, "mismatch=%d", $mismatch); 23 | 24 | $test->ffi->tb_present(); 25 | 26 | $test->screencap(); 27 | --------------------------------------------------------------------------------