mirror of
https://github.com/nicbarker/clay.git
synced 2025-09-18 04:26:18 +00:00
[Renderers/termbox2] Termbox2 renderer & examples (#419)
This commit is contained in:
parent
281f961e3d
commit
a9c1f9a8a7
36
examples/termbox2-demo/CMakeLists.txt
Normal file
36
examples/termbox2-demo/CMakeLists.txt
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
cmake_minimum_required(VERSION 3.25)
|
||||||
|
project(clay_examples_termbox2_demo C)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_QUIET FALSE)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
termbox2
|
||||||
|
GIT_REPOSITORY "https://github.com/termbox/termbox2.git"
|
||||||
|
GIT_TAG "9c9281a9a4c971a2be57f8645e828ec99fd555e8"
|
||||||
|
GIT_PROGRESS TRUE
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(termbox2)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
stb
|
||||||
|
GIT_REPOSITORY "https://github.com/nothings/stb.git"
|
||||||
|
GIT_TAG "f58f558c120e9b32c217290b80bad1a0729fbb2c"
|
||||||
|
GIT_PROGRESS TRUE
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(stb)
|
||||||
|
|
||||||
|
add_executable(clay_examples_termbox2_demo main.c)
|
||||||
|
|
||||||
|
target_compile_options(clay_examples_termbox2_demo PUBLIC)
|
||||||
|
target_include_directories(clay_examples_termbox2_demo PUBLIC . PRIVATE ${termbox2_SOURCE_DIR} PRIVATE ${stb_SOURCE_DIR})
|
||||||
|
target_link_libraries(clay_examples_termbox2_demo PRIVATE m) # Used by stb_image.h
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
TARGET clay_examples_termbox2_demo POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/resources
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/resources)
|
789
examples/termbox2-demo/main.c
Normal file
789
examples/termbox2-demo/main.c
Normal file
|
@ -0,0 +1,789 @@
|
||||||
|
/*
|
||||||
|
Unlicense
|
||||||
|
|
||||||
|
Copyright (c) 2025 Mivirl
|
||||||
|
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to <https://unlicense.org/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CLAY_IMPLEMENTATION
|
||||||
|
#include "../../clay.h"
|
||||||
|
#include "../../renderers/termbox2/clay_renderer_termbox2.c"
|
||||||
|
|
||||||
|
#define TB_IMPL
|
||||||
|
#include "termbox2.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||||
|
#include "stb_image_resize2.h"
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// -- Internal state
|
||||||
|
|
||||||
|
// If the program should exit the main render/interaction loop
|
||||||
|
bool end_loop = false;
|
||||||
|
|
||||||
|
// If the debug tools should be displayed
|
||||||
|
bool debugMode = false;
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// -- Clay components
|
||||||
|
|
||||||
|
void component_text_pair(const char *key, const char *value)
|
||||||
|
{
|
||||||
|
size_t keylen = strlen(key);
|
||||||
|
size_t vallen = strlen(value);
|
||||||
|
Clay_String keytext = (Clay_String) {
|
||||||
|
.length = keylen,
|
||||||
|
.chars = key,
|
||||||
|
};
|
||||||
|
Clay_String valtext = (Clay_String) {
|
||||||
|
.length = vallen,
|
||||||
|
.chars = value,
|
||||||
|
};
|
||||||
|
Clay_TextElementConfig *textconfig =
|
||||||
|
CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } });
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = {
|
||||||
|
.width = {
|
||||||
|
.size.minMax = {
|
||||||
|
.min = strlen("Border chars CLAY_TB_IMAGE_MODE_UNICODE_FAST") * Clay_Termbox_Cell_Width(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}) {
|
||||||
|
CLAY_TEXT(keytext, textconfig);
|
||||||
|
CLAY({ .layout = { .sizing = CLAY_SIZING_GROW(1) } }) { }
|
||||||
|
CLAY_TEXT(valtext, textconfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void component_termbox_settings(void)
|
||||||
|
{
|
||||||
|
CLAY({
|
||||||
|
.floating = {
|
||||||
|
.attachTo = CLAY_ATTACH_TO_PARENT,
|
||||||
|
.zIndex = 1,
|
||||||
|
.attachPoints = { CLAY_ATTACH_POINT_CENTER_CENTER, CLAY_ATTACH_POINT_CENTER_TOP },
|
||||||
|
.offset = { 0, 0 }
|
||||||
|
},
|
||||||
|
}) {
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = CLAY_SIZING_FIT(),
|
||||||
|
.padding = {
|
||||||
|
6 * Clay_Termbox_Cell_Width(),
|
||||||
|
6 * Clay_Termbox_Cell_Width(),
|
||||||
|
2 * Clay_Termbox_Cell_Height(),
|
||||||
|
2 * Clay_Termbox_Cell_Height(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.border = {
|
||||||
|
.width = CLAY_BORDER_ALL(1),
|
||||||
|
.color = { 0x00, 0x00, 0x00, 0xff }
|
||||||
|
},
|
||||||
|
.backgroundColor = { 0x7f, 0x00, 0x00, 0x7f }
|
||||||
|
}) {
|
||||||
|
const char *color_mode = NULL;
|
||||||
|
switch (clay_tb_color_mode) {
|
||||||
|
case TB_OUTPUT_NORMAL: {
|
||||||
|
color_mode = "TB_OUTPUT_NORMAL";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_OUTPUT_256: {
|
||||||
|
color_mode = "TB_OUTPUT_256";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_OUTPUT_216: {
|
||||||
|
color_mode = "TB_OUTPUT_216";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_OUTPUT_GRAYSCALE: {
|
||||||
|
color_mode = "TB_OUTPUT_GRAYSCALE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_OUTPUT_TRUECOLOR: {
|
||||||
|
color_mode = "TB_OUTPUT_TRUECOLOR";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_OUTPUT_NOCOLOR: {
|
||||||
|
color_mode = "CLAY_TB_OUTPUT_NOCOLOR";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
color_mode = "INVALID";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char *border_mode = NULL;
|
||||||
|
switch (clay_tb_border_mode) {
|
||||||
|
case CLAY_TB_BORDER_MODE_ROUND: {
|
||||||
|
border_mode = "CLAY_TB_BORDER_MODE_ROUND";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_BORDER_MODE_MINIMUM: {
|
||||||
|
border_mode = "CLAY_TB_BORDER_MODE_MINIMUM";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
border_mode = "INVALID";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char *border_chars = NULL;
|
||||||
|
switch (clay_tb_border_chars) {
|
||||||
|
case CLAY_TB_BORDER_CHARS_ASCII: {
|
||||||
|
border_chars = "CLAY_TB_BORDER_CHARS_ASCII";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_BORDER_CHARS_UNICODE: {
|
||||||
|
border_chars = "CLAY_TB_BORDER_CHARS_UNICODE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_BORDER_CHARS_BLANK: {
|
||||||
|
border_chars = "CLAY_TB_BORDER_CHARS_BLANK";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_BORDER_CHARS_NONE: {
|
||||||
|
border_chars = "CLAY_TB_BORDER_CHARS_NONE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
border_chars = "INVALID";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char *image_mode = NULL;
|
||||||
|
switch (clay_tb_image_mode) {
|
||||||
|
case CLAY_TB_IMAGE_MODE_PLACEHOLDER: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_PLACEHOLDER";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_IMAGE_MODE_BG: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_BG";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_IMAGE_MODE_ASCII_FG: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FG";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_IMAGE_MODE_ASCII_FG_FAST: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FG_FAST";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_IMAGE_MODE_ASCII: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_ASCII";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_IMAGE_MODE_ASCII_FAST: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FAST";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_IMAGE_MODE_UNICODE: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_UNICODE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_IMAGE_MODE_UNICODE_FAST: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_UNICODE_FAST";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
image_mode = "INVALID";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char *transparency = NULL;
|
||||||
|
if (clay_tb_transparency) {
|
||||||
|
transparency = "true";
|
||||||
|
} else {
|
||||||
|
transparency = "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
CLAY({
|
||||||
|
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM },
|
||||||
|
}) {
|
||||||
|
component_text_pair("Color mode", color_mode);
|
||||||
|
component_text_pair("Border mode", border_mode);
|
||||||
|
component_text_pair("Border chars", border_chars);
|
||||||
|
component_text_pair("Image mode", image_mode);
|
||||||
|
component_text_pair("Transparency", transparency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void component_color_palette(void)
|
||||||
|
{
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.childGap = 16,
|
||||||
|
.padding = {
|
||||||
|
2 * Clay_Termbox_Cell_Width(),
|
||||||
|
2 * Clay_Termbox_Cell_Width(),
|
||||||
|
2 * Clay_Termbox_Cell_Height(),
|
||||||
|
2 * Clay_Termbox_Cell_Height(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.border = {
|
||||||
|
.width = CLAY_BORDER_OUTSIDE(2),
|
||||||
|
.color = { 0x00, 0x00, 0x00, 0xff }
|
||||||
|
},
|
||||||
|
.backgroundColor = { 0x7f, 0x7f, 0x7f, 0xff }
|
||||||
|
}) {
|
||||||
|
for (int type = 0; type < 2; ++type) {
|
||||||
|
CLAY({
|
||||||
|
.layout ={
|
||||||
|
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||||
|
.sizing = CLAY_SIZING_FIT(),
|
||||||
|
.childGap = Clay_Termbox_Cell_Height()
|
||||||
|
},
|
||||||
|
}) {
|
||||||
|
for (float ri = 0; ri < 4; ri += 1) {
|
||||||
|
CLAY({
|
||||||
|
.layout ={
|
||||||
|
.sizing = CLAY_SIZING_FIT(),
|
||||||
|
.childGap = Clay_Termbox_Cell_Width()
|
||||||
|
},
|
||||||
|
}) {
|
||||||
|
for (float r = ri * 0x44; r < (ri + 1) * 0x44; r += 0x22) {
|
||||||
|
CLAY({
|
||||||
|
.layout ={
|
||||||
|
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||||
|
.sizing = CLAY_SIZING_FIT(),
|
||||||
|
},
|
||||||
|
}) {
|
||||||
|
for (float g = 0; g < 0xff; g += 0x22) {
|
||||||
|
CLAY({
|
||||||
|
.layout ={
|
||||||
|
.sizing = CLAY_SIZING_FIT(),
|
||||||
|
},
|
||||||
|
}) {
|
||||||
|
for (float b = 0; b < 0xff; b += 0x22) {
|
||||||
|
Clay_Color color = { r, g, b, 0x7f };
|
||||||
|
|
||||||
|
Clay_LayoutConfig layout = (Clay_LayoutConfig) {
|
||||||
|
.sizing = {
|
||||||
|
.width = CLAY_SIZING_FIXED(2 * Clay_Termbox_Cell_Width()),
|
||||||
|
.height = CLAY_SIZING_FIXED(1 * Clay_Termbox_Cell_Height())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (0 == type) {
|
||||||
|
CLAY({
|
||||||
|
.layout = layout,
|
||||||
|
.backgroundColor = color
|
||||||
|
}) {}
|
||||||
|
} else if (1 == type) {
|
||||||
|
CLAY({
|
||||||
|
.layout = layout,
|
||||||
|
}) {
|
||||||
|
CLAY_TEXT(CLAY_STRING("#"), CLAY_TEXT_CONFIG({ .textColor = color }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void component_unicode_text(void)
|
||||||
|
{
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = CLAY_SIZING_FIT(),
|
||||||
|
.padding = {
|
||||||
|
2 * Clay_Termbox_Cell_Width(),
|
||||||
|
2 * Clay_Termbox_Cell_Width(),
|
||||||
|
2 * Clay_Termbox_Cell_Height(),
|
||||||
|
2 * Clay_Termbox_Cell_Height(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.backgroundColor = { 0xcc, 0xbb, 0xaa, 0xff },
|
||||||
|
.border = {
|
||||||
|
// This border should still be displayed in CLAY_TB_BORDER_MODE_ROUND mode
|
||||||
|
.width = {
|
||||||
|
0.75 * Clay_Termbox_Cell_Width(),
|
||||||
|
0.75 * Clay_Termbox_Cell_Width(),
|
||||||
|
0.75 * Clay_Termbox_Cell_Height(),
|
||||||
|
0.75 * Clay_Termbox_Cell_Height(),
|
||||||
|
},
|
||||||
|
.color = { 0x33, 0x33, 0x33, 0xff },
|
||||||
|
},
|
||||||
|
}) {
|
||||||
|
CLAY_TEXT(
|
||||||
|
CLAY_STRING("Non-ascii character tests:\n"
|
||||||
|
"\n"
|
||||||
|
"(from https://www.w3.org/2001/06/utf-8-test/UTF-8-demo.html)\n"
|
||||||
|
" Mathematics and Sciences:\n"
|
||||||
|
" ∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β),\n"
|
||||||
|
" ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (A ⇔ B),\n"
|
||||||
|
" 2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm\n"
|
||||||
|
"\n"
|
||||||
|
" Compact font selection example text:\n"
|
||||||
|
" ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789\n"
|
||||||
|
" abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ\n"
|
||||||
|
" –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд\n"
|
||||||
|
" ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi<>⑀₂ἠḂӥẄɐː⍎אԱა\n"
|
||||||
|
"\n"
|
||||||
|
"(from https://blog.denisbider.com/2015/09/when-monospace-fonts-arent-unicode.html):\n"
|
||||||
|
" aeioucsz\n"
|
||||||
|
" áéíóúčšž\n"
|
||||||
|
" 台北1234\n"
|
||||||
|
" QRS12\n"
|
||||||
|
" アイウ1234\n"
|
||||||
|
"\n"
|
||||||
|
"(from https://stackoverflow.com/a/1644280)\n"
|
||||||
|
" ٩(-̮̮̃-̃)۶ ٩(●̮̮̃•̃)۶ ٩(͡๏̯͡๏)۶ ٩(-̮̮̃•̃)."
|
||||||
|
),
|
||||||
|
CLAY_TEXT_CONFIG({ .textColor = { 0x11, 0x11, 0x11, 0xff } })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void component_keybinds(void)
|
||||||
|
{
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = CLAY_SIZING_FIT(),
|
||||||
|
.padding = {
|
||||||
|
4 * Clay_Termbox_Cell_Width(),
|
||||||
|
4 * Clay_Termbox_Cell_Width(),
|
||||||
|
2 * Clay_Termbox_Cell_Height(),
|
||||||
|
2 * Clay_Termbox_Cell_Height(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.backgroundColor = { 0x00, 0x7f, 0x7f, 0xff }
|
||||||
|
}) {
|
||||||
|
CLAY_TEXT(
|
||||||
|
CLAY_STRING(
|
||||||
|
"Termbox2 renderer test\n"
|
||||||
|
"\n"
|
||||||
|
"Keybinds:\n"
|
||||||
|
" c/C - Cycle through color modes\n"
|
||||||
|
" b/B - Cycle through border modes\n"
|
||||||
|
" h/H - Cycle through border characters\n"
|
||||||
|
" i/I - Cycle through image modes\n"
|
||||||
|
" t/T - Toggle transparency\n"
|
||||||
|
" d/D - Toggle debug mode\n"
|
||||||
|
" q/Q - Quit\n"
|
||||||
|
),
|
||||||
|
CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff }})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void component_image(clay_tb_image *image, int width)
|
||||||
|
{
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = {
|
||||||
|
.width = (0 == width) ? CLAY_SIZING_GROW() : CLAY_SIZING_FIXED(width),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.image = {
|
||||||
|
.imageData = image,
|
||||||
|
},
|
||||||
|
.aspectRatio = { 512.0 / 406.0 }
|
||||||
|
}) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
void component_mouse_data(void)
|
||||||
|
{
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = {
|
||||||
|
.width = CLAY_SIZING_GROW(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}) {
|
||||||
|
Clay_Context* context = Clay_GetCurrentContext();
|
||||||
|
Clay_Vector2 mouse_position = context->pointerInfo.position;
|
||||||
|
|
||||||
|
Clay_LayoutConfig layout = (Clay_LayoutConfig) {
|
||||||
|
.sizing = {
|
||||||
|
.width = CLAY_SIZING_FIXED(2 * Clay_Termbox_Cell_Width()),
|
||||||
|
.height = CLAY_SIZING_FIXED(1 * Clay_Termbox_Cell_Height())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
float v = 255 * mouse_position.x / Clay_Termbox_Width();
|
||||||
|
v = (0 > v) ? 0 : v;
|
||||||
|
v = (255 < v) ? 255 : v;
|
||||||
|
Clay_Color color = (Clay_Color) { v, v, v, 0xff };
|
||||||
|
|
||||||
|
CLAY({
|
||||||
|
.layout = layout,
|
||||||
|
.backgroundColor = color
|
||||||
|
}) {}
|
||||||
|
|
||||||
|
v = 255 * mouse_position.y / Clay_Termbox_Height();
|
||||||
|
v = (0 > v) ? 0 : v;
|
||||||
|
v = (255 < v) ? 255 : v;
|
||||||
|
color = (Clay_Color) { v, v, v, 0xff };
|
||||||
|
|
||||||
|
|
||||||
|
CLAY({
|
||||||
|
.layout = layout,
|
||||||
|
.backgroundColor = color
|
||||||
|
}) {}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void component_bordered_text(void)
|
||||||
|
{
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||||
|
.sizing = {
|
||||||
|
.width = CLAY_SIZING_FIT(450),
|
||||||
|
.height = CLAY_SIZING_FIT(),
|
||||||
|
},
|
||||||
|
.padding = CLAY_PADDING_ALL(32)
|
||||||
|
},
|
||||||
|
.backgroundColor = { 0x24, 0x55, 0x34, 0xff },
|
||||||
|
}) {
|
||||||
|
CLAY({
|
||||||
|
.border = { .width = { 1, 1, 1, 1, 1 }, .color = { 0xaa, 0x00, 0x00, 0xff } },
|
||||||
|
}) {
|
||||||
|
CLAY_TEXT(
|
||||||
|
CLAY_STRING("Test"), CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } }));
|
||||||
|
}
|
||||||
|
CLAY({
|
||||||
|
.border = { .width = { 1, 1, 1, 1, 1 }, .color = { 0x00, 0xaa, 0x00, 0xff } },
|
||||||
|
}) {
|
||||||
|
CLAY_TEXT(CLAY_STRING("of the border width"),
|
||||||
|
CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } }));
|
||||||
|
}
|
||||||
|
CLAY({
|
||||||
|
.border = { .width = { 1, 1, 1, 1, 1 }, .color = { 0x00, 0x00, 0xaa, 0xff } },
|
||||||
|
}) {
|
||||||
|
CLAY_TEXT(CLAY_STRING("and overlap for multiple lines\nof text"),
|
||||||
|
CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } }));
|
||||||
|
}
|
||||||
|
CLAY({
|
||||||
|
.border = { .width = { 1, 1, 1, 1, 1 }, .color = { 0x00, 0x00, 0xaa, 0xff } },
|
||||||
|
}) {
|
||||||
|
CLAY_TEXT(CLAY_STRING("this text\nis long enough\nto display all\n borders\naround it"),
|
||||||
|
CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Clay_RenderCommandArray CreateLayout(clay_tb_image *image1, clay_tb_image *image2)
|
||||||
|
{
|
||||||
|
Clay_BeginLayout();
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = {
|
||||||
|
.width = CLAY_SIZING_GROW(),
|
||||||
|
.height = CLAY_SIZING_GROW()
|
||||||
|
},
|
||||||
|
.childAlignment = {
|
||||||
|
.x = CLAY_ALIGN_X_CENTER,
|
||||||
|
.y = CLAY_ALIGN_Y_CENTER
|
||||||
|
},
|
||||||
|
.childGap = 64
|
||||||
|
},
|
||||||
|
.backgroundColor = { 0x24, 0x24, 0x24, 0xff }
|
||||||
|
}) {
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.childAlignment = {
|
||||||
|
.x = CLAY_ALIGN_X_RIGHT,
|
||||||
|
},
|
||||||
|
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||||
|
.sizing = CLAY_SIZING_FIT(),
|
||||||
|
},
|
||||||
|
}) {
|
||||||
|
component_keybinds();
|
||||||
|
component_unicode_text();
|
||||||
|
}
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||||
|
.childGap = 32,
|
||||||
|
.sizing = CLAY_SIZING_FIT(),
|
||||||
|
},
|
||||||
|
}) {
|
||||||
|
component_termbox_settings();
|
||||||
|
component_image(image1, 150);
|
||||||
|
component_image(image2, 0);
|
||||||
|
component_mouse_data();
|
||||||
|
component_bordered_text();
|
||||||
|
}
|
||||||
|
|
||||||
|
component_color_palette();
|
||||||
|
}
|
||||||
|
return Clay_EndLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// -- Interactive functions
|
||||||
|
|
||||||
|
void handle_clay_errors(Clay_ErrorData errorData)
|
||||||
|
{
|
||||||
|
Clay_Termbox_Close();
|
||||||
|
fprintf(stderr, "%s", errorData.errorText.chars);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Process events received from termbox2 and handle interaction
|
||||||
|
*/
|
||||||
|
void handle_termbox_events(void)
|
||||||
|
{
|
||||||
|
// Wait up to 100ms for an event (key/mouse press, terminal resize) before continuing
|
||||||
|
// If an event is already available, this doesn't wait. Will not wait due to the previous call
|
||||||
|
// to termbox_waitfor_event. Increasing the wait time reduces load without reducing
|
||||||
|
// responsiveness (but will of course prevent other code from running on this thread while it's
|
||||||
|
// waiting)
|
||||||
|
struct tb_event evt;
|
||||||
|
int ms_to_wait = 0;
|
||||||
|
int err = tb_peek_event(&evt, ms_to_wait);
|
||||||
|
|
||||||
|
switch (err) {
|
||||||
|
default:
|
||||||
|
case TB_ERR_NO_EVENT: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_ERR_POLL: {
|
||||||
|
if (EINTR != tb_last_errno()) {
|
||||||
|
Clay_Termbox_Close();
|
||||||
|
fprintf(stderr, "Failed to read event from TTY\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_OK: {
|
||||||
|
switch (evt.type) {
|
||||||
|
case TB_EVENT_RESIZE: {
|
||||||
|
Clay_SetLayoutDimensions((Clay_Dimensions) {
|
||||||
|
Clay_Termbox_Width(),
|
||||||
|
Clay_Termbox_Height()
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_EVENT_KEY: {
|
||||||
|
if (TB_KEY_CTRL_C == evt.key) {
|
||||||
|
end_loop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (evt.ch) {
|
||||||
|
case 'q':
|
||||||
|
case 'Q': {
|
||||||
|
end_loop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'd':
|
||||||
|
case 'D': {
|
||||||
|
debugMode = !debugMode;
|
||||||
|
Clay_SetDebugModeEnabled(debugMode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'c': {
|
||||||
|
int new_mode = clay_tb_color_mode - 1;
|
||||||
|
new_mode = (0 <= new_mode) ? new_mode : TB_OUTPUT_TRUECOLOR;
|
||||||
|
Clay_Termbox_Set_Color_Mode(new_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'C': {
|
||||||
|
int new_mode = (clay_tb_color_mode + 1) % (TB_OUTPUT_TRUECOLOR + 1);
|
||||||
|
Clay_Termbox_Set_Color_Mode(new_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'b': {
|
||||||
|
enum border_mode new_mode = clay_tb_border_mode - 1;
|
||||||
|
new_mode = (CLAY_TB_BORDER_MODE_DEFAULT < new_mode)
|
||||||
|
? new_mode
|
||||||
|
: CLAY_TB_BORDER_MODE_MINIMUM;
|
||||||
|
Clay_Termbox_Set_Border_Mode(new_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'B': {
|
||||||
|
enum border_mode new_mode = (clay_tb_border_mode + 1)
|
||||||
|
% (CLAY_TB_BORDER_MODE_MINIMUM + 1);
|
||||||
|
new_mode = (CLAY_TB_BORDER_MODE_DEFAULT < new_mode)
|
||||||
|
? new_mode
|
||||||
|
: CLAY_TB_BORDER_MODE_ROUND;
|
||||||
|
Clay_Termbox_Set_Border_Mode(new_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'h': {
|
||||||
|
enum border_chars new_chars = clay_tb_border_chars - 1;
|
||||||
|
new_chars = (CLAY_TB_BORDER_CHARS_DEFAULT < new_chars)
|
||||||
|
? new_chars
|
||||||
|
: CLAY_TB_BORDER_CHARS_NONE;
|
||||||
|
Clay_Termbox_Set_Border_Chars(new_chars);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'H': {
|
||||||
|
enum border_chars new_chars
|
||||||
|
= (clay_tb_border_chars + 1) % (CLAY_TB_BORDER_CHARS_NONE + 1);
|
||||||
|
new_chars = (CLAY_TB_BORDER_CHARS_DEFAULT < new_chars)
|
||||||
|
? new_chars
|
||||||
|
: CLAY_TB_BORDER_CHARS_ASCII;
|
||||||
|
Clay_Termbox_Set_Border_Chars(new_chars);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'i': {
|
||||||
|
enum image_mode new_mode = clay_tb_image_mode - 1;
|
||||||
|
new_mode = (CLAY_TB_IMAGE_MODE_DEFAULT < new_mode)
|
||||||
|
? new_mode
|
||||||
|
: CLAY_TB_IMAGE_MODE_UNICODE_FAST;
|
||||||
|
Clay_Termbox_Set_Image_Mode(new_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'I': {
|
||||||
|
enum image_mode new_mode = (clay_tb_image_mode + 1)
|
||||||
|
% (CLAY_TB_IMAGE_MODE_UNICODE_FAST + 1);
|
||||||
|
new_mode = (CLAY_TB_IMAGE_MODE_DEFAULT < new_mode)
|
||||||
|
? new_mode
|
||||||
|
: CLAY_TB_IMAGE_MODE_PLACEHOLDER;
|
||||||
|
Clay_Termbox_Set_Image_Mode(new_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 't':
|
||||||
|
case 'T': {
|
||||||
|
Clay_Termbox_Set_Transparency(!clay_tb_transparency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_EVENT_MOUSE: {
|
||||||
|
Clay_Vector2 mousePosition = {
|
||||||
|
(float)evt.x * Clay_Termbox_Cell_Width(),
|
||||||
|
(float)evt.y * Clay_Termbox_Cell_Height()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mouse release events may not be produced by all terminals, and will
|
||||||
|
// be sent during hover, so can't be used to detect when the mouse has
|
||||||
|
// been released
|
||||||
|
|
||||||
|
switch (evt.key) {
|
||||||
|
case TB_KEY_MOUSE_LEFT: {
|
||||||
|
Clay_SetPointerState(mousePosition, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_KEY_MOUSE_RIGHT: {
|
||||||
|
Clay_SetPointerState(mousePosition, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_KEY_MOUSE_MIDDLE: {
|
||||||
|
Clay_SetPointerState(mousePosition, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_KEY_MOUSE_RELEASE: {
|
||||||
|
Clay_SetPointerState(mousePosition, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_KEY_MOUSE_WHEEL_UP: {
|
||||||
|
Clay_Vector2 scrollDelta = { 0, 1 * Clay_Termbox_Cell_Height() };
|
||||||
|
Clay_UpdateScrollContainers(false, scrollDelta, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_KEY_MOUSE_WHEEL_DOWN: {
|
||||||
|
Clay_Vector2 scrollDelta = { 0, -1 * Clay_Termbox_Cell_Height() };
|
||||||
|
Clay_UpdateScrollContainers(false, scrollDelta, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
clay_tb_image shark_image1 = Clay_Termbox_Image_Load_File("resources/512px-Shark_antwerp_zoo.jpeg");
|
||||||
|
clay_tb_image shark_image2 = Clay_Termbox_Image_Load_File("resources/512px-Shark_antwerp_zoo.jpeg");
|
||||||
|
if (NULL == shark_image1.pixel_data) { exit(1); }
|
||||||
|
if (NULL == shark_image2.pixel_data) { exit(1); }
|
||||||
|
|
||||||
|
int num_elements = 3 * 8192;
|
||||||
|
Clay_SetMaxElementCount(num_elements);
|
||||||
|
|
||||||
|
uint64_t size = Clay_MinMemorySize();
|
||||||
|
void *memory = malloc(size);
|
||||||
|
if (NULL == memory) { exit(1); }
|
||||||
|
|
||||||
|
Clay_Arena clay_arena = Clay_CreateArenaWithCapacityAndMemory(size, memory);
|
||||||
|
|
||||||
|
Clay_Termbox_Initialize(
|
||||||
|
TB_OUTPUT_256, CLAY_TB_BORDER_MODE_DEFAULT, CLAY_TB_BORDER_CHARS_DEFAULT, CLAY_TB_IMAGE_MODE_DEFAULT, false);
|
||||||
|
|
||||||
|
Clay_Initialize(clay_arena, (Clay_Dimensions) { Clay_Termbox_Width(), Clay_Termbox_Height() },
|
||||||
|
(Clay_ErrorHandler) { handle_clay_errors, NULL });
|
||||||
|
|
||||||
|
Clay_SetMeasureTextFunction(Clay_Termbox_MeasureText, NULL);
|
||||||
|
|
||||||
|
// Initial render before waiting for events
|
||||||
|
Clay_RenderCommandArray commands = CreateLayout(&shark_image1, &shark_image2);
|
||||||
|
Clay_Termbox_Render(commands);
|
||||||
|
tb_present();
|
||||||
|
|
||||||
|
while (!end_loop) {
|
||||||
|
// Block until event is available. Optional, but reduces load since this demo is purely
|
||||||
|
// synchronous to user input.
|
||||||
|
Clay_Termbox_Waitfor_Event();
|
||||||
|
|
||||||
|
handle_termbox_events();
|
||||||
|
|
||||||
|
commands = CreateLayout(&shark_image1, &shark_image2);
|
||||||
|
|
||||||
|
tb_clear();
|
||||||
|
Clay_Termbox_Render(commands);
|
||||||
|
tb_present();
|
||||||
|
}
|
||||||
|
|
||||||
|
Clay_Termbox_Close();
|
||||||
|
Clay_Termbox_Image_Free(&shark_image1);
|
||||||
|
Clay_Termbox_Image_Free(&shark_image2);
|
||||||
|
free(memory);
|
||||||
|
return 0;
|
||||||
|
}
|
72
examples/termbox2-demo/readme.md
Normal file
72
examples/termbox2-demo/readme.md
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
# Termbox2 renderer demo
|
||||||
|
|
||||||
|
Terminal-based renderer using [termbox2](https://github.com/termbox/termbox2)
|
||||||
|
|
||||||
|
This demo shows a color palette and a few different components. It allows
|
||||||
|
changing configuration settings for colors, border size rounding mode,
|
||||||
|
characters used for borders, and transparency.
|
||||||
|
|
||||||
|
```
|
||||||
|
Keybinds:
|
||||||
|
c/C - Cycle through color modes
|
||||||
|
b/B - Cycle through border modes
|
||||||
|
h/H - Cycle through border characters
|
||||||
|
i/I - Cycle through image modes
|
||||||
|
t/T - Toggle transparency
|
||||||
|
d/D - Toggle debug mode
|
||||||
|
q/Q - Quit
|
||||||
|
```
|
||||||
|
|
||||||
|
Configuration can be also be overriden by environment variables:
|
||||||
|
- `CLAY_TB_COLOR_MODE`
|
||||||
|
- `NORMAL`
|
||||||
|
- `256`
|
||||||
|
- `216`
|
||||||
|
- `GRAYSCALE`
|
||||||
|
- `TRUECOLOR`
|
||||||
|
- `NOCOLOR`
|
||||||
|
- `CLAY_TB_BORDER_CHARS`
|
||||||
|
- `DEFAULT`
|
||||||
|
- `ASCII`
|
||||||
|
- `UNICODE`
|
||||||
|
- `NONE`
|
||||||
|
- `CLAY_TB_IMAGE_MODE`
|
||||||
|
- `DEFAULT`
|
||||||
|
- `PLACEHOLDER`
|
||||||
|
- `BG`
|
||||||
|
- `ASCII_FG`
|
||||||
|
- `ASCII`
|
||||||
|
- `UNICODE`
|
||||||
|
- `ASCII_FG_FAST`
|
||||||
|
- `ASCII_FAST`
|
||||||
|
- `UNICODE_FAST`
|
||||||
|
- `CLAY_TB_TRANSPARENCY`
|
||||||
|
- `1`
|
||||||
|
- `0`
|
||||||
|
- `CLAY_TB_CELL_PIXELS`
|
||||||
|
- `widthxheight`
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Build the binary with cmake
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run the executable:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./clay_examples_termbox2_demo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Attributions
|
||||||
|
|
||||||
|
Resources used:
|
||||||
|
- `512px-Shark_antwerp_zoo.jpeg`
|
||||||
|
- Retrieved from <https://commons.wikimedia.org/wiki/File:Shark_antwerp_zoo.jpg>
|
||||||
|
- License: [Creative Commons Attribution 3.0 Unported](https://creativecommons.org/licenses/by/3.0/deed.en)
|
||||||
|
- No changes made
|
BIN
examples/termbox2-demo/resources/512px-Shark_antwerp_zoo.jpeg
Normal file
BIN
examples/termbox2-demo/resources/512px-Shark_antwerp_zoo.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
38
examples/termbox2-image-demo/CMakeLists.txt
Normal file
38
examples/termbox2-image-demo/CMakeLists.txt
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
cmake_minimum_required(VERSION 3.25)
|
||||||
|
project(clay_examples_termbox2_image_demo C)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
|
set(CMAKE_C_FLAGS_RELEASE "-O3")
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_QUIET FALSE)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
termbox2
|
||||||
|
GIT_REPOSITORY "https://github.com/termbox/termbox2.git"
|
||||||
|
GIT_TAG "9c9281a9a4c971a2be57f8645e828ec99fd555e8"
|
||||||
|
GIT_PROGRESS TRUE
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(termbox2)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
stb
|
||||||
|
GIT_REPOSITORY "https://github.com/nothings/stb.git"
|
||||||
|
GIT_TAG "f58f558c120e9b32c217290b80bad1a0729fbb2c"
|
||||||
|
GIT_PROGRESS TRUE
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(stb)
|
||||||
|
|
||||||
|
add_executable(clay_examples_termbox2_image_demo main.c)
|
||||||
|
|
||||||
|
target_compile_options(clay_examples_termbox2_image_demo PUBLIC)
|
||||||
|
target_include_directories(clay_examples_termbox2_image_demo PUBLIC . PRIVATE ${termbox2_SOURCE_DIR} PRIVATE ${stb_SOURCE_DIR})
|
||||||
|
target_link_libraries(clay_examples_termbox2_image_demo PRIVATE m) # Used by stb_image.h
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
TARGET clay_examples_termbox2_image_demo POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/resources
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/resources)
|
707
examples/termbox2-image-demo/main.c
Normal file
707
examples/termbox2-image-demo/main.c
Normal file
|
@ -0,0 +1,707 @@
|
||||||
|
/*
|
||||||
|
Unlicense
|
||||||
|
|
||||||
|
Copyright (c) 2025 Mivirl
|
||||||
|
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to <https://unlicense.org/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CLAY_IMPLEMENTATION
|
||||||
|
#include "../../clay.h"
|
||||||
|
#include "../../renderers/termbox2/clay_renderer_termbox2.c"
|
||||||
|
|
||||||
|
#define TB_IMPL
|
||||||
|
#include "termbox2.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||||
|
#include "stb_image_resize2.h"
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// -- Data structures
|
||||||
|
|
||||||
|
struct img_group {
|
||||||
|
clay_tb_image thumbnail;
|
||||||
|
clay_tb_image image;
|
||||||
|
clay_tb_image image_1;
|
||||||
|
clay_tb_image image_2;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
};
|
||||||
|
typedef struct img_group img_group;
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// -- Internal state
|
||||||
|
|
||||||
|
// If the program should exit the main render/interaction loop
|
||||||
|
bool end_loop = false;
|
||||||
|
|
||||||
|
// If the debug tools should be displayed
|
||||||
|
bool debugMode = false;
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// -- Internal utility functions
|
||||||
|
|
||||||
|
img_group img_group_load(const char *filename)
|
||||||
|
{
|
||||||
|
img_group rv;
|
||||||
|
rv.thumbnail = Clay_Termbox_Image_Load_File(filename);
|
||||||
|
rv.image = Clay_Termbox_Image_Load_File(filename);
|
||||||
|
rv.image_1 = Clay_Termbox_Image_Load_File(filename);
|
||||||
|
rv.image_2 = Clay_Termbox_Image_Load_File(filename);
|
||||||
|
if (NULL == rv.thumbnail.pixel_data
|
||||||
|
|| NULL == rv.image.pixel_data
|
||||||
|
|| NULL == rv.image_1.pixel_data
|
||||||
|
|| NULL == rv.image_2.pixel_data) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
rv.width = rv.image.pixel_width;
|
||||||
|
rv.height = rv.image.pixel_height;
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
void img_group_free(img_group *img)
|
||||||
|
{
|
||||||
|
Clay_Termbox_Image_Free(&img->thumbnail);
|
||||||
|
Clay_Termbox_Image_Free(&img->image);
|
||||||
|
Clay_Termbox_Image_Free(&img->image_1);
|
||||||
|
Clay_Termbox_Image_Free(&img->image_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// -- Clay components
|
||||||
|
|
||||||
|
void component_text_pair(const char *key, const char *value)
|
||||||
|
{
|
||||||
|
size_t keylen = strlen(key);
|
||||||
|
size_t vallen = strlen(value);
|
||||||
|
Clay_String keytext = (Clay_String) {
|
||||||
|
.length = keylen,
|
||||||
|
.chars = key,
|
||||||
|
};
|
||||||
|
Clay_String valtext = (Clay_String) {
|
||||||
|
.length = vallen,
|
||||||
|
.chars = value,
|
||||||
|
};
|
||||||
|
Clay_TextElementConfig *textconfig =
|
||||||
|
CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } });
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = {
|
||||||
|
.width = {
|
||||||
|
.size.minMax = {
|
||||||
|
.min = strlen("Border chars CLAY_TB_IMAGE_MODE_UNICODE_FAST") * Clay_Termbox_Cell_Width(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}) {
|
||||||
|
CLAY_TEXT(keytext, textconfig);
|
||||||
|
CLAY({ .layout = { .sizing = CLAY_SIZING_GROW(1) } }) { }
|
||||||
|
CLAY_TEXT(valtext, textconfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void component_termbox_settings(void)
|
||||||
|
{
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = CLAY_SIZING_FIT(),
|
||||||
|
.padding = {
|
||||||
|
6 * Clay_Termbox_Cell_Width(),
|
||||||
|
6 * Clay_Termbox_Cell_Width(),
|
||||||
|
2 * Clay_Termbox_Cell_Height(),
|
||||||
|
2 * Clay_Termbox_Cell_Height(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.border = {
|
||||||
|
.width = CLAY_BORDER_ALL(1),
|
||||||
|
.color = { 0x00, 0x00, 0x00, 0xff }
|
||||||
|
},
|
||||||
|
.backgroundColor = { 0x7f, 0x00, 0x00, 0x7f }
|
||||||
|
}) {
|
||||||
|
const char *color_mode = NULL;
|
||||||
|
switch (clay_tb_color_mode) {
|
||||||
|
case TB_OUTPUT_NORMAL: {
|
||||||
|
color_mode = "TB_OUTPUT_NORMAL";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_OUTPUT_256: {
|
||||||
|
color_mode = "TB_OUTPUT_256";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_OUTPUT_216: {
|
||||||
|
color_mode = "TB_OUTPUT_216";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_OUTPUT_GRAYSCALE: {
|
||||||
|
color_mode = "TB_OUTPUT_GRAYSCALE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_OUTPUT_TRUECOLOR: {
|
||||||
|
color_mode = "TB_OUTPUT_TRUECOLOR";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_OUTPUT_NOCOLOR: {
|
||||||
|
color_mode = "CLAY_TB_OUTPUT_NOCOLOR";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
color_mode = "INVALID";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char *border_mode = NULL;
|
||||||
|
switch (clay_tb_border_mode) {
|
||||||
|
case CLAY_TB_BORDER_MODE_ROUND: {
|
||||||
|
border_mode = "CLAY_TB_BORDER_MODE_ROUND";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_BORDER_MODE_MINIMUM: {
|
||||||
|
border_mode = "CLAY_TB_BORDER_MODE_MINIMUM";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
border_mode = "INVALID";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char *border_chars = NULL;
|
||||||
|
switch (clay_tb_border_chars) {
|
||||||
|
case CLAY_TB_BORDER_CHARS_ASCII: {
|
||||||
|
border_chars = "CLAY_TB_BORDER_CHARS_ASCII";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_BORDER_CHARS_UNICODE: {
|
||||||
|
border_chars = "CLAY_TB_BORDER_CHARS_UNICODE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_BORDER_CHARS_BLANK: {
|
||||||
|
border_chars = "CLAY_TB_BORDER_CHARS_BLANK";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_BORDER_CHARS_NONE: {
|
||||||
|
border_chars = "CLAY_TB_BORDER_CHARS_NONE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
border_chars = "INVALID";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char *image_mode = NULL;
|
||||||
|
switch (clay_tb_image_mode) {
|
||||||
|
case CLAY_TB_IMAGE_MODE_PLACEHOLDER: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_PLACEHOLDER";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_IMAGE_MODE_BG: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_BG";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_IMAGE_MODE_ASCII_FG: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FG";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_IMAGE_MODE_ASCII_FG_FAST: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FG_FAST";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_IMAGE_MODE_ASCII: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_ASCII";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_IMAGE_MODE_ASCII_FAST: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FAST";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_IMAGE_MODE_UNICODE: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_UNICODE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_TB_IMAGE_MODE_UNICODE_FAST: {
|
||||||
|
image_mode = "CLAY_TB_IMAGE_MODE_UNICODE_FAST";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
image_mode = "INVALID";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char *transparency = NULL;
|
||||||
|
if (clay_tb_transparency) {
|
||||||
|
transparency = "true";
|
||||||
|
} else {
|
||||||
|
transparency = "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
CLAY({
|
||||||
|
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM },
|
||||||
|
}) {
|
||||||
|
component_text_pair("Color mode", color_mode);
|
||||||
|
component_text_pair("Border mode", border_mode);
|
||||||
|
component_text_pair("Border chars", border_chars);
|
||||||
|
component_text_pair("Image mode", image_mode);
|
||||||
|
component_text_pair("Transparency", transparency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void component_keybinds(void)
|
||||||
|
{
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = CLAY_SIZING_FIT(),
|
||||||
|
.padding = {
|
||||||
|
4 * Clay_Termbox_Cell_Width(),
|
||||||
|
4 * Clay_Termbox_Cell_Width(),
|
||||||
|
2 * Clay_Termbox_Cell_Height(),
|
||||||
|
2 * Clay_Termbox_Cell_Height(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.backgroundColor = { 0x00, 0x7f, 0x7f, 0xff }
|
||||||
|
}) {
|
||||||
|
CLAY_TEXT(
|
||||||
|
CLAY_STRING(
|
||||||
|
"Termbox2 renderer test\n"
|
||||||
|
"\n"
|
||||||
|
"Keybinds:\n"
|
||||||
|
" up/down arrows - Change selected image\n"
|
||||||
|
" c/C - Cycle through color modes\n"
|
||||||
|
" b/B - Cycle through border modes\n"
|
||||||
|
" h/H - Cycle through border characters\n"
|
||||||
|
" i/I - Cycle through image modes\n"
|
||||||
|
" t/T - Toggle transparency\n"
|
||||||
|
" d/D - Toggle debug mode\n"
|
||||||
|
" q/Q - Quit\n"
|
||||||
|
),
|
||||||
|
CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff }})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void component_image(img_group *img_pair)
|
||||||
|
{
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = {
|
||||||
|
.width = CLAY_SIZING_GROW(),
|
||||||
|
.height = CLAY_SIZING_GROW()
|
||||||
|
},
|
||||||
|
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||||
|
.childAlignment = {
|
||||||
|
.x = CLAY_ALIGN_X_CENTER,
|
||||||
|
.y = CLAY_ALIGN_Y_CENTER
|
||||||
|
},
|
||||||
|
.childGap = 1 * Clay_Termbox_Cell_Height()
|
||||||
|
},
|
||||||
|
.backgroundColor = { 0x24, 0x24, 0x24, 0xff }
|
||||||
|
}) {
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = {
|
||||||
|
.width = CLAY_SIZING_GROW(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.image = {
|
||||||
|
.imageData = &img_pair->image,
|
||||||
|
},
|
||||||
|
.aspectRatio = { (float)img_pair->width / img_pair->height }
|
||||||
|
}) { }
|
||||||
|
component_keybinds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void component_image_small(img_group **img_pairs, int count, int selected_index)
|
||||||
|
{
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = {
|
||||||
|
.width = CLAY_SIZING_PERCENT(0.25),
|
||||||
|
},
|
||||||
|
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||||
|
.childGap = 20,
|
||||||
|
.childAlignment = {
|
||||||
|
.x = CLAY_ALIGN_X_CENTER,
|
||||||
|
.y = CLAY_ALIGN_Y_CENTER
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}) {
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = {
|
||||||
|
.width = CLAY_SIZING_PERCENT(0.7),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.image = {
|
||||||
|
.imageData = &img_pairs[selected_index]->image_1,
|
||||||
|
},
|
||||||
|
.aspectRatio = { (float)img_pairs[selected_index]->width / img_pairs[selected_index]->height }
|
||||||
|
}) { }
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = {
|
||||||
|
.width = CLAY_SIZING_GROW(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.image = {
|
||||||
|
.imageData = &img_pairs[selected_index]->image_2,
|
||||||
|
},
|
||||||
|
.aspectRatio = { (float)img_pairs[selected_index]->width / img_pairs[selected_index]->height }
|
||||||
|
}) { }
|
||||||
|
component_termbox_settings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void component_thumbnails(img_group **img_pairs, int count, int selected_index)
|
||||||
|
{
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = {
|
||||||
|
.width = CLAY_SIZING_PERCENT(0.1),
|
||||||
|
.height = CLAY_SIZING_GROW()
|
||||||
|
},
|
||||||
|
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||||
|
.childGap = 20
|
||||||
|
},
|
||||||
|
.backgroundColor = { 0x42, 0x42, 0x42, 0xff }
|
||||||
|
}) {
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
Clay_BorderElementConfig border;
|
||||||
|
if (i == selected_index) {
|
||||||
|
border = (Clay_BorderElementConfig) {
|
||||||
|
.width =CLAY_BORDER_OUTSIDE(10),
|
||||||
|
.color = { 0x00, 0x30, 0xc0, 0x8f }
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
border = (Clay_BorderElementConfig) {
|
||||||
|
.width = CLAY_BORDER_OUTSIDE(0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = {
|
||||||
|
.width = CLAY_SIZING_GROW(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.border = border,
|
||||||
|
.image = {
|
||||||
|
.imageData = &img_pairs[i]->thumbnail,
|
||||||
|
},
|
||||||
|
.aspectRatio = { (float)img_pairs[i]->width / img_pairs[i]->height }
|
||||||
|
}) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int selected_thumbnail = 0;
|
||||||
|
const int thumbnail_count = 5;
|
||||||
|
Clay_RenderCommandArray CreateLayout(struct img_group **imgs)
|
||||||
|
{
|
||||||
|
Clay_BeginLayout();
|
||||||
|
CLAY({
|
||||||
|
.layout = {
|
||||||
|
.sizing = {
|
||||||
|
.width = CLAY_SIZING_GROW(),
|
||||||
|
.height = CLAY_SIZING_GROW()
|
||||||
|
},
|
||||||
|
.childAlignment = {
|
||||||
|
.x = CLAY_ALIGN_X_LEFT,
|
||||||
|
.y = CLAY_ALIGN_Y_CENTER
|
||||||
|
},
|
||||||
|
.childGap = 64
|
||||||
|
},
|
||||||
|
.backgroundColor = { 0x24, 0x24, 0x24, 0xff }
|
||||||
|
}) {
|
||||||
|
component_thumbnails(imgs, thumbnail_count, selected_thumbnail);
|
||||||
|
component_image_small(imgs, thumbnail_count, selected_thumbnail);
|
||||||
|
component_image(imgs[selected_thumbnail]);
|
||||||
|
}
|
||||||
|
return Clay_EndLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// -- Interactive functions
|
||||||
|
|
||||||
|
void handle_clay_errors(Clay_ErrorData errorData)
|
||||||
|
{
|
||||||
|
Clay_Termbox_Close();
|
||||||
|
fprintf(stderr, "%s", errorData.errorText.chars);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Process events received from termbox2 and handle interaction
|
||||||
|
*/
|
||||||
|
void handle_termbox_events(void)
|
||||||
|
{
|
||||||
|
// Wait up to 100ms for an event (key/mouse press, terminal resize) before continuing
|
||||||
|
// If an event is already available, this doesn't wait. Will not wait due to the previous call
|
||||||
|
// to termbox_waitfor_event. Increasing the wait time reduces load without reducing
|
||||||
|
// responsiveness (but will of course prevent other code from running on this thread while it's
|
||||||
|
// waiting)
|
||||||
|
struct tb_event evt;
|
||||||
|
int ms_to_wait = 0;
|
||||||
|
int err = tb_peek_event(&evt, ms_to_wait);
|
||||||
|
|
||||||
|
switch (err) {
|
||||||
|
default:
|
||||||
|
case TB_ERR_NO_EVENT: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_ERR_POLL: {
|
||||||
|
if (EINTR != tb_last_errno()) {
|
||||||
|
Clay_Termbox_Close();
|
||||||
|
fprintf(stderr, "Failed to read event from TTY\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_OK: {
|
||||||
|
switch (evt.type) {
|
||||||
|
case TB_EVENT_RESIZE: {
|
||||||
|
Clay_SetLayoutDimensions((Clay_Dimensions) {
|
||||||
|
Clay_Termbox_Width(),
|
||||||
|
Clay_Termbox_Height()
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_EVENT_KEY: {
|
||||||
|
if (TB_KEY_CTRL_C == evt.key) {
|
||||||
|
end_loop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (0 != evt.ch) {
|
||||||
|
switch (evt.ch) {
|
||||||
|
case 'q':
|
||||||
|
case 'Q': {
|
||||||
|
end_loop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'd':
|
||||||
|
case 'D': {
|
||||||
|
debugMode = !debugMode;
|
||||||
|
Clay_SetDebugModeEnabled(debugMode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'c': {
|
||||||
|
int new_mode = clay_tb_color_mode - 1;
|
||||||
|
new_mode = (0 <= new_mode) ? new_mode : TB_OUTPUT_TRUECOLOR;
|
||||||
|
Clay_Termbox_Set_Color_Mode(new_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'C': {
|
||||||
|
int new_mode = (clay_tb_color_mode + 1) % (TB_OUTPUT_TRUECOLOR + 1);
|
||||||
|
Clay_Termbox_Set_Color_Mode(new_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'b': {
|
||||||
|
enum border_mode new_mode = clay_tb_border_mode - 1;
|
||||||
|
new_mode = (CLAY_TB_BORDER_MODE_DEFAULT < new_mode)
|
||||||
|
? new_mode
|
||||||
|
: CLAY_TB_BORDER_MODE_MINIMUM;
|
||||||
|
Clay_Termbox_Set_Border_Mode(new_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'B': {
|
||||||
|
enum border_mode new_mode = (clay_tb_border_mode + 1)
|
||||||
|
% (CLAY_TB_BORDER_MODE_MINIMUM + 1);
|
||||||
|
new_mode = (CLAY_TB_BORDER_MODE_DEFAULT < new_mode)
|
||||||
|
? new_mode
|
||||||
|
: CLAY_TB_BORDER_MODE_ROUND;
|
||||||
|
Clay_Termbox_Set_Border_Mode(new_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'h': {
|
||||||
|
enum border_chars new_chars = clay_tb_border_chars - 1;
|
||||||
|
new_chars = (CLAY_TB_BORDER_CHARS_DEFAULT < new_chars)
|
||||||
|
? new_chars
|
||||||
|
: CLAY_TB_BORDER_CHARS_NONE;
|
||||||
|
Clay_Termbox_Set_Border_Chars(new_chars);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'H': {
|
||||||
|
enum border_chars new_chars
|
||||||
|
= (clay_tb_border_chars + 1) % (CLAY_TB_BORDER_CHARS_NONE + 1);
|
||||||
|
new_chars = (CLAY_TB_BORDER_CHARS_DEFAULT < new_chars)
|
||||||
|
? new_chars
|
||||||
|
: CLAY_TB_BORDER_CHARS_ASCII;
|
||||||
|
Clay_Termbox_Set_Border_Chars(new_chars);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'i': {
|
||||||
|
enum image_mode new_mode = clay_tb_image_mode - 1;
|
||||||
|
new_mode = (CLAY_TB_IMAGE_MODE_DEFAULT < new_mode)
|
||||||
|
? new_mode
|
||||||
|
: CLAY_TB_IMAGE_MODE_UNICODE_FAST;
|
||||||
|
Clay_Termbox_Set_Image_Mode(new_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'I': {
|
||||||
|
enum image_mode new_mode = (clay_tb_image_mode + 1)
|
||||||
|
% (CLAY_TB_IMAGE_MODE_UNICODE_FAST + 1);
|
||||||
|
new_mode = (CLAY_TB_IMAGE_MODE_DEFAULT < new_mode)
|
||||||
|
? new_mode
|
||||||
|
: CLAY_TB_IMAGE_MODE_PLACEHOLDER;
|
||||||
|
Clay_Termbox_Set_Image_Mode(new_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 't':
|
||||||
|
case 'T': {
|
||||||
|
Clay_Termbox_Set_Transparency(!clay_tb_transparency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (0 != evt.key) {
|
||||||
|
switch (evt.key) {
|
||||||
|
case TB_KEY_ARROW_UP: {
|
||||||
|
selected_thumbnail = (selected_thumbnail > 0) ? selected_thumbnail - 1 : 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_KEY_ARROW_DOWN: {
|
||||||
|
selected_thumbnail = (selected_thumbnail < thumbnail_count - 1) ? selected_thumbnail + 1 : thumbnail_count - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_EVENT_MOUSE: {
|
||||||
|
Clay_Vector2 mousePosition = {
|
||||||
|
(float)evt.x * Clay_Termbox_Cell_Width(),
|
||||||
|
(float)evt.y * Clay_Termbox_Cell_Height()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mouse release events may not be produced by all terminals, and will
|
||||||
|
// be sent during hover, so can't be used to detect when the mouse has
|
||||||
|
// been released
|
||||||
|
|
||||||
|
switch (evt.key) {
|
||||||
|
case TB_KEY_MOUSE_LEFT: {
|
||||||
|
Clay_SetPointerState(mousePosition, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_KEY_MOUSE_RIGHT: {
|
||||||
|
Clay_SetPointerState(mousePosition, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_KEY_MOUSE_MIDDLE: {
|
||||||
|
Clay_SetPointerState(mousePosition, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_KEY_MOUSE_RELEASE: {
|
||||||
|
Clay_SetPointerState(mousePosition, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_KEY_MOUSE_WHEEL_UP: {
|
||||||
|
Clay_Vector2 scrollDelta = { 0, 1 * Clay_Termbox_Cell_Height() };
|
||||||
|
Clay_UpdateScrollContainers(false, scrollDelta, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TB_KEY_MOUSE_WHEEL_DOWN: {
|
||||||
|
Clay_Vector2 scrollDelta = { 0, -1 * Clay_Termbox_Cell_Height() };
|
||||||
|
Clay_UpdateScrollContainers(false, scrollDelta, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
img_group *imgs[thumbnail_count];
|
||||||
|
img_group img_shark = img_group_load("resources/512px-Shark_antwerp_zoo.jpeg");
|
||||||
|
img_group img_castle = img_group_load("resources/512px-Balmoral_Castle_30_July_2011.jpeg");
|
||||||
|
img_group img_dog = img_group_load("resources/512px-German_Shepherd_(aka_Alsatian_and_Alsatian_Wolf_Dog),_Deutscher_Schäferhund_(Folder_(IV)_22.jpeg");
|
||||||
|
img_group img_rosa = img_group_load("resources/512px-Rosa_Cubana_2018-09-21_1610.jpeg");
|
||||||
|
img_group img_vorderer = img_group_load("resources/512px-Vorderer_Graben_10_Bamberg_20190830_001.jpeg");
|
||||||
|
imgs[0] = &img_shark;
|
||||||
|
imgs[1] = &img_castle;
|
||||||
|
imgs[2] = &img_dog;
|
||||||
|
imgs[3] = &img_rosa;
|
||||||
|
imgs[4] = &img_vorderer;
|
||||||
|
|
||||||
|
int num_elements = 3 * 8192;
|
||||||
|
Clay_SetMaxElementCount(num_elements);
|
||||||
|
|
||||||
|
uint64_t size = Clay_MinMemorySize();
|
||||||
|
void *memory = malloc(size);
|
||||||
|
if (NULL == memory) { exit(1); }
|
||||||
|
|
||||||
|
Clay_Arena clay_arena = Clay_CreateArenaWithCapacityAndMemory(size, memory);
|
||||||
|
|
||||||
|
Clay_Termbox_Initialize(
|
||||||
|
TB_OUTPUT_256, CLAY_TB_BORDER_MODE_DEFAULT, CLAY_TB_BORDER_CHARS_DEFAULT, CLAY_TB_IMAGE_MODE_DEFAULT, false);
|
||||||
|
|
||||||
|
Clay_Initialize(clay_arena, (Clay_Dimensions) { Clay_Termbox_Width(), Clay_Termbox_Height() },
|
||||||
|
(Clay_ErrorHandler) { handle_clay_errors, NULL });
|
||||||
|
|
||||||
|
Clay_SetMeasureTextFunction(Clay_Termbox_MeasureText, NULL);
|
||||||
|
|
||||||
|
// Initial render before waiting for events
|
||||||
|
Clay_RenderCommandArray commands = CreateLayout(imgs);
|
||||||
|
Clay_Termbox_Render(commands);
|
||||||
|
tb_present();
|
||||||
|
|
||||||
|
while (!end_loop) {
|
||||||
|
// Block until event is available. Optional, but reduces load since this demo is purely
|
||||||
|
// synchronous to user input.
|
||||||
|
Clay_Termbox_Waitfor_Event();
|
||||||
|
|
||||||
|
handle_termbox_events();
|
||||||
|
|
||||||
|
commands = CreateLayout(imgs);
|
||||||
|
|
||||||
|
Clay_Termbox_Render(commands);
|
||||||
|
tb_present();
|
||||||
|
}
|
||||||
|
|
||||||
|
Clay_Termbox_Close();
|
||||||
|
img_group_free(&img_shark);
|
||||||
|
img_group_free(&img_castle);
|
||||||
|
img_group_free(&img_dog);
|
||||||
|
img_group_free(&img_rosa);
|
||||||
|
img_group_free(&img_vorderer);
|
||||||
|
free(memory);
|
||||||
|
return 0;
|
||||||
|
}
|
88
examples/termbox2-image-demo/readme.md
Normal file
88
examples/termbox2-image-demo/readme.md
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
# Termbox2 renderer demo
|
||||||
|
|
||||||
|
Terminal-based renderer using [termbox2](https://github.com/termbox/termbox2)
|
||||||
|
|
||||||
|
This demo shows a color palette and a few different components. It allows
|
||||||
|
changing configuration settings for colors, border size rounding mode,
|
||||||
|
characters used for borders, and transparency.
|
||||||
|
|
||||||
|
```
|
||||||
|
Keybinds:
|
||||||
|
c/C - Cycle through color modes
|
||||||
|
b/B - Cycle through border modes
|
||||||
|
h/H - Cycle through border characters
|
||||||
|
i/I - Cycle through image modes
|
||||||
|
t/T - Toggle transparency
|
||||||
|
d/D - Toggle debug mode
|
||||||
|
q/Q - Quit
|
||||||
|
```
|
||||||
|
|
||||||
|
Configuration can be also be overriden by environment variables:
|
||||||
|
- `CLAY_TB_COLOR_MODE`
|
||||||
|
- `NORMAL`
|
||||||
|
- `256`
|
||||||
|
- `216`
|
||||||
|
- `GRAYSCALE`
|
||||||
|
- `TRUECOLOR`
|
||||||
|
- `NOCOLOR`
|
||||||
|
- `CLAY_TB_BORDER_CHARS`
|
||||||
|
- `DEFAULT`
|
||||||
|
- `ASCII`
|
||||||
|
- `UNICODE`
|
||||||
|
- `NONE`
|
||||||
|
- `CLAY_TB_IMAGE_MODE`
|
||||||
|
- `DEFAULT`
|
||||||
|
- `PLACEHOLDER`
|
||||||
|
- `BG`
|
||||||
|
- `ASCII_FG`
|
||||||
|
- `ASCII`
|
||||||
|
- `UNICODE`
|
||||||
|
- `ASCII_FG_FAST`
|
||||||
|
- `ASCII_FAST`
|
||||||
|
- `UNICODE_FAST`
|
||||||
|
- `CLAY_TB_TRANSPARENCY`
|
||||||
|
- `1`
|
||||||
|
- `0`
|
||||||
|
- `CLAY_TB_CELL_PIXELS`
|
||||||
|
- `widthxheight`
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Build the binary with cmake
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run the executable:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./clay_examples_termbox2_demo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Attributions
|
||||||
|
|
||||||
|
Resources used:
|
||||||
|
- `512px-Shark_antwerp_zoo.jpeg`
|
||||||
|
- Retrieved from <https://commons.wikimedia.org/wiki/File:Shark_antwerp_zoo.jpg>
|
||||||
|
- License: [Creative Commons Attribution 3.0 Unported](https://creativecommons.org/licenses/by/3.0/)
|
||||||
|
- No changes made
|
||||||
|
- `512px-Balmoral_Castle_30_July_2011.jpg`
|
||||||
|
- Retrieved from <https://commons.wikimedia.org/wiki/File:Balmoral_Castle_30_July_2011.jpg>
|
||||||
|
- License: [Creative Commons Attribution-ShareAlike 3.0 Unported](https://creativecommons.org/licenses/by-sa/3.0/)
|
||||||
|
- No changes made
|
||||||
|
- `512px-German_Shepherd_(aka_Alsatian_and_Alsatian_Wolf_Dog),_Deutscher_Schäferhund_(Folder_(IV)_22.jpeg`
|
||||||
|
- Retrieved from <https://commons.wikimedia.org/wiki/File:German_Shepherd_(aka_Alsatian_and_Alsatian_Wolf_Dog),_Deutscher_Sch%C3%A4ferhund_(Folder_(IV)_22.JPG>
|
||||||
|
- License: [Creative Commons Attribution-ShareAlike 3.0 Unported](https://creativecommons.org/licenses/by-sa/3.0/)
|
||||||
|
- No changes made
|
||||||
|
- `512px-Rosa_Cubana_2018-09-21_1610.jpeg`
|
||||||
|
- Retrieved from <https://commons.wikimedia.org/wiki/File:Rosa_Cubana_2018-09-21_1610.jpg>
|
||||||
|
- License: [Creative Commons Attribution-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||||
|
- No changes made
|
||||||
|
- `512px-Vorderer_Graben_10_Bamberg_20190830_001.jpeg`
|
||||||
|
- Retrieved from <https://commons.wikimedia.org/wiki/File:Vorderer_Graben_10_Bamberg_20190830_001.jpg>
|
||||||
|
- License: [Creative Commons Attribution-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||||
|
- No changes made
|
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
1776
renderers/termbox2/clay_renderer_termbox2.c
Normal file
1776
renderers/termbox2/clay_renderer_termbox2.c
Normal file
File diff suppressed because it is too large
Load diff
2863
renderers/termbox2/image_character_masks.h
Normal file
2863
renderers/termbox2/image_character_masks.h
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue