From f7b4809db8d6238bc1da449397ef07399b7c65b8 Mon Sep 17 00:00:00 2001 From: Timerix Date: Fri, 9 Jan 2026 02:21:50 +0500 Subject: [PATCH] renamed types and constants --- .gitignore | 23 +- example/ask.c | 4 +- example/hello.c | 9 +- example/snek.c | 20 +- makefile | 20 +- test/color.c | 2 +- test/string.c | 2 +- test/test.c | 18 +- tim.h | 726 ++++++++++++++++-------------------------------- 9 files changed, 300 insertions(+), 524 deletions(-) diff --git a/.gitignore b/.gitignore index f0bbb20..4b5ea53 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,21 @@ -out/ -tools/ +# build results +bin/ +obj/ + +# IDE files +.vs/ +.vshistory/ +.editorconfig +*.user +*.vcxproj.filters + +# other files +.old*/ +old/ +tmp/ +temp/ +*.tmp +*.temp +logs/ +log/ +*.log diff --git a/example/ask.c b/example/ask.c index 263ef58..f7ba7ec 100644 --- a/example/ask.c +++ b/example/ask.c @@ -16,7 +16,7 @@ int main(int argc, char** argv) { } // get text properties - struct text msg = scan_str(argv[1]); + TimText_t msg = scan_str(argv[1]); while (tim_run(0)) { // calculate size of message box @@ -41,7 +41,7 @@ int main(int argc, char** argv) { } // return with 1 when q or esc is pressed - if (is_key_press('q') || is_key_press(ESCAPE_KEY)) { + if (is_key_press('q') || is_key_press(TimKey_Escape)) { exit(1); } } diff --git a/example/hello.c b/example/hello.c index b354535..aaeb9f2 100644 --- a/example/hello.c +++ b/example/hello.c @@ -1,5 +1,6 @@ -#include "../tim.h" // one header, no lib -int main(void) { // +#include "../tim.h" + +int main(void) { while (tim_run(0)) { // event loop scope (A, A, 24, 8) { // centered 24x8 scope uint64_t c = 0x0a060f; // three colors @@ -9,7 +10,7 @@ int main(void) { // return 0; // exit on button click if (is_key_press('q')) // ctrl-c is masked return 0; // exit on 'q' press - } // - } // atexit cleanup + } + } //TODO: remove atexit cleanup } diff --git a/example/snek.c b/example/snek.c index 0d5c31a..f5cba8f 100644 --- a/example/snek.c +++ b/example/snek.c @@ -63,12 +63,12 @@ static void game(void) { } // draw - if (tim.event.type == DRAW_EVENT) { + if (tim.event.type == TimEvent_Draw) { // food draw_chr(cell(" ", 0, 0xc5), snek.food.x * 2 + 0, snek.food.y); draw_chr(cell(" ", 0, 0xc5), snek.food.x * 2 + 1, snek.food.y); // snek - struct cell s = cell(" ", 0, 0); + TimCell_t s = cell(" ", 0, 0); for (int i = 0; i < snek.len; i++) { s.bg = (i / 2) % 2 ? 0xe3 : 0xea; int x = snek.body[i].x * 2; @@ -81,13 +81,13 @@ static void game(void) { // user input if (tim.event.type == KEY_EVENT) { int key = tim.event.key; - if ((key == RIGHT_KEY || key == 'd') && snek.look.x != -1) { + if ((key == TimKey_Right || key == 'd') && snek.look.x != -1) { snek.look = (point){{1, 0}}; - } else if ((key == LEFT_KEY || key == 'a') && snek.look.x != 1) { + } else if ((key == TimKey_Left || key == 'a') && snek.look.x != 1) { snek.look = (point){{-1, 0}}; - } else if ((key == DOWN_KEY || key == 's') && snek.look.y != -1) { + } else if ((key == TimKey_Down || key == 's') && snek.look.y != -1) { snek.look = (point){{0, 1}}; - } else if ((key == UP_KEY || key == 'w') && snek.look.y != 1) { + } else if ((key == TimKey_Up || key == 'w') && snek.look.y != 1) { snek.look = (point){{0, -1}}; } } @@ -98,13 +98,13 @@ static void menu(void) { char* lbl = snek.state == OVER ? "GAME OVER" : "SNEK - THE GAME"; char* btn = snek.state == PAUSE ? "Resume" : "Play"; label(lbl, A, 0, A, A, BTN); - if (button(btn, A, 2, 20, 5, BTN) || is_key_press(ENTER_KEY)) { + if (button(btn, A, 2, 20, 5, BTN) || is_key_press(TimKey_Enter)) { if (snek.state != PAUSE) { start(); } snek.state = RUN; } - if (button("Exit", A, 8, 20, 5, BTN) || is_key_press(ESCAPE_KEY)) { + if (button("Exit", A, 8, 20, 5, BTN) || is_key_press(TimKey_Escape)) { exit(0); } } @@ -113,7 +113,7 @@ static void menu(void) { int main(void) { // draw every 10 ms while (tim_run(10)) { - struct cell bg = cell(" ", 0, BG); + TimCell_t bg = cell(" ", 0, BG); draw_lot(bg, 0, 0, tim.w, tim.h); if (snek.state == RUN) { @@ -122,7 +122,7 @@ int main(void) { menu(); } - if (is_key_press(ESCAPE_KEY)) { + if (is_key_press(TimKey_Escape)) { snek.state = PAUSE; } } diff --git a/makefile b/makefile index 35c4dfb..dbf0304 100644 --- a/makefile +++ b/makefile @@ -1,20 +1,20 @@ -all: out/test out/string out/color out/hello out/ask out/snek +all: bin/test bin/string bin/color bin/hello bin/ask bin/snek -out/test: test/test.c out +bin/test: test/test.c bin $(CC) $< -Wall $(CFLAGS) -o $@ -out/string: test/string.c out +bin/string: test/string.c bin $(CC) $< -Wall $(CFLAGS) -o $@ -out/color: test/color.c out +bin/color: test/color.c bin $(CC) $< -Wall $(CFLAGS) -o $@ -out/hello: example/hello.c out +bin/hello: example/hello.c bin $(CC) $< -Wall $(CFLAGS) -o $@ -out/ask: example/ask.c out +bin/ask: example/ask.c bin $(CC) $< -Wall $(CFLAGS) -o $@ -out/snek: example/snek.c out +bin/snek: example/snek.c bin $(CC) $< -Wall $(CFLAGS) -o $@ -out: - mkdir -p out +bin: + mkdir -p bin clean: - rm -rf out + rm -rf bin diff --git a/test/color.c b/test/color.c index e8ab9b5..08d9fa1 100644 --- a/test/color.c +++ b/test/color.c @@ -22,7 +22,7 @@ int main(void) { for (int i = 0; i < 24; i++) { foo(i % 12, i / 12 + 22, i + 232); } - if (is_key_press('q') || is_key_press(ESCAPE_KEY)) { + if (is_key_press('q') || is_key_press(TimKey_Escape)) { exit(1); } } diff --git a/test/string.c b/test/string.c index 09eceeb..7e2a445 100644 --- a/test/string.c +++ b/test/string.c @@ -51,7 +51,7 @@ int main(void) { TEST(scan_str("a").width == 1); TEST(scan_str("äß\no").width == 2); - struct line ln = {.s = "foo\nbar"}; + TimLine_t ln = {.s = "foo\nbar"}; TEST(next_line(&ln) == true); TEST(!memcmp(ln.line, "foo", ln.size)); TEST(next_line(&ln) == true); diff --git a/test/test.c b/test/test.c index 168cb59..9d23f1c 100644 --- a/test/test.c +++ b/test/test.c @@ -1,8 +1,8 @@ #include "../tim.h" -static inline void test_screen(struct event* e) { - static struct event me; - static struct event ke; +static inline void test_screen(TimEvent_t* e) { + static TimEvent_t me; + static TimEvent_t ke; static int render_us; char buf[64]; @@ -37,9 +37,9 @@ static inline void test_screen(struct event* e) { render_us += tim.render_us; sprintf(buf, "%d µs (Ø %d µs)", tim.render_us, render_us / MAX(tim.frame, 1)); label(buf, ~2, ~2, A, A, 0xf); - sprintf(buf, "%d cells (%.0f %%)", tim.w * tim.h, 100.0 * tim.w * tim.h / MAX_CELLS); + sprintf(buf, "%d cells (%.0f %%)", tim.w * tim.h, 100.0 * tim.w * tim.h / TIM_MAX_CELLS); label(buf, ~2, ~1, A, A, 0xf); - sprintf(buf, "%d bytes (%.0f %%)", tim.buf_size, 100.0 * tim.buf_size / MAX_BUF); + sprintf(buf, "%d bytes (%.0f %%)", tim.buf_size, 100.0 * tim.buf_size / TIM_MAX_BUF); label(buf, ~2, ~0, A, A, 0xf); // multi line label @@ -63,8 +63,10 @@ static inline void test_screen(struct event* e) { } // edit - static struct edit ed1 = {.s = "Edit 1"}; - static struct edit ed2 = {0}; + static TimEdit_t ed1; + static TimEdit_t ed2; + edit_init(&ed1, 32, "Edit 1"); + edit_init(&ed2, 32, ""); edit(&ed1, 1, 10, 32, 0xff00ff); sprintf(buf, "cursor: %d length: %d", ed1.cursor, ed1.length); label(buf, 2, 13, A, A, 0xf); @@ -111,7 +113,7 @@ static inline void test_screen(struct event* e) { int main(void) { while (tim_run(1.5)) { test_screen(&tim.event); - if (is_key_press('q') || is_key_press(ESCAPE_KEY)) { + if (is_key_press('q') || is_key_press(TimKey_Escape)) { break; } } diff --git a/tim.h b/tim.h index 331670b..744d87a 100644 --- a/tim.h +++ b/tim.h @@ -1,398 +1,154 @@ -/* about **********************************************************************/ - -// tim.h is a portable library to create simple terminal applications -// Demo video: https://asciinema.org/a/zn3p0dsVCOQOzwY1S9gDfyaxQ - -/* quick start ****************************************************************/ - -// #include "tim.h" // one header, no lib -// int main(void) { // -// while (tim_run(0)) { // event loop -// scope (A, A, 24, 8) { // centered 24x8 scope -// uint64_t c = 0x0a060f; // three colors -// frame(0, 0, ~0, ~0, c); // draw frame for scope -// label("Greetings!", A, 2, A, A, c); // label in top center -// if (button("OK", A, ~1, 8, A, c)) // button in bottom center -// return 0; // exit on button click -// if (is_key_press('q')) // ctrl-c is masked -// return 0; // exit on 'q' press -// } // -// } // atexit cleanup -// } // - -/* layout *********************************************************************/ - -// The terminal's columns (x) and rows (y) are addressed by their coordinates, -// the origin is in the top left corner. -// -// Scopes are the primary layout mechanism. They are used to group and place -// multiple elements. Scopes can be nested. -// -// The root scope is the full terminal screen. The scope macro is constructed -// with a for loop, so statements like break or return inside the scope block -// will probably give you a bad time. -// -// Elements (widget, control, component) are elements of user interaction, such -// as a button or edit box. Most elements take x/y/w/h arguments to control -// placement. All positions are given in relation the element's parent scope. -// -// Automatic (A) width and height are either based on the element's content, or -// take the full available space from parent. -// -// arg | value | placement -// -----|-------|--------------------------------- -// x | n | n columns to left -// x | ~n | n columns to right -// x | A | center horizontally -// y | n | n rows to top -// y | ~n | n rows to bottom -// y | A | center vertically -// w | n | n columns wide -// w | ~n | fit width to n columns to right -// w | A | automatic width -// h | n | n rows high -// h | ~n | fit height n rows to bottom -// h | A | automatic height -// -// The layout automatically adopts to terminal window resize events. - -/* colors *********************************************************************/ - -// Most elements have a uint64 color argument which holds up to eight colors. -// Typically byte 0 is the text color and byte 1 is the background color. -// -// For example 0x08040f encodes three colors. When used with a button the text -// is white (0f), the background is blue (04), and the frame is gray (08). -// -// The terminal should support xterm-256 colors. The TERM variable is ignored. -// The lower 16 colors vary across different terminals, so the upper 240 colors -// should be used if consistency is important. -// -// xterm-256 color chart -// https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg - -/* events *********************************************************************/ - -// tim_run blocks until it observes an event. Mouse and key events are always -// immediately followed by a draw event in order to make changes visible. -// -// Some elements need to consume events, for example edit consumes the key -// event when focused in order to prevent other key handlers on acting on them. -// -// The current event is stored in tim.event. -// -// event | cause -// -------------|----------------------- -// DRAW_EVENT | input, timeout, resize -// KEY_EVENT | key press -// MOUSE_EVENT | mouse click -// VOID_EVENT | consumed event - -/* elements *******************************************************************/ - -// frame (x, y, w, h, color) -// -// Draw an empty frame and fill area. -// -// x/y/w/h see layout documentation -// color background, frame -// -// label (str, x, y, w, h, color) -// -// Draw text label. Automatic width and height are supported. Strings -// exceeding width or height are clipped. -// -// str zero terminated string -// x/y/w/h see layout documentation -// color background, text -// -// button (str, x, y, w, h, color) -> bool -// -// Draw button. Automatic width and height are supported. Strings exceeding -// width or height are clipped. Returns true when clicked. -// -// str zero terminated string -// x/y/w/h see layout documentation -// color frame, background, text -// -// edit (state, x, y, w, color) -> bool -// -// Draw text edit. Output is stored in state.s. -// Receives input events when focused by mouse click or by setting focus manually. -// Escape or return relinquish focus. -// Returns key id if received key input. -// -// state pointer to persistent edit state struct -// x/y/w see layout documentation -// color f rame, background, text -// -// check (str, state, x, y, w, color) -> bool -// -// Draw check box. State determines how the box is checked. [x] when state -// is non-zero, [ ] when state is zero, [-] when state is -1. A mouse click -// toggles the state between one and zero and returns true. -// -// str zero terminated string -// state pointer to persistent state variable -// x/y/w see layout documentation -// color check, background, text -// -// radio (str, state, v, x, y, w, color) -> bool -// -// Draw radio box. If state equals v, the box is selected. Radios are -// grouped through a shared state. Within that group, each v must be unique. -// A mouse click assigns v to state and returns true. -// -// str zero terminated string -// state pointer to persistent state variable -// v unique state value -// x/y/w see layout documentation -// color radio, background, text - -/* functions ******************************************************************/ - -// tim_run (fps) -> bool -// -// Process events and render frame. Blocks until input is received or the -// next frame is due. First call also initializes the terminal. When fps is -// zero the function blocks until input is received. Key and mouse events -// are immediately followed by a draw event, so the actual fps can be -// significantly greater than requested. Always returns true. To reset the -// terminal after a crash, run "reset". -// The Ctrl-C interrupt is masked, so make sure to put an exit condition -// like this at the end of the main loop: -// -// if (is_key_press(ESCAPE_KEY)) -// exit(0); -// -// fps frames per second -// -// is_key_press (key) -> bool -// -// Returns true if key was pressed. -// -// key char literal or one of the KEY constants, see constants -// -// time_us () -> int64 -// -// Returns monotonic clock value in microseconds. Not affected by summer -// time or leap seconds. - -/* useful links ***************************************************************/ - -// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html -// https://learn.microsoft.com/en-us/windows/console/ - -/* bugs ***********************************************************************/ - -// - Double buffering is still new, set ENABLE_DBUF to 0 if you see glitches -// - Double width characters like 彁 are not fully supported. Terminals do not -// handle these consistently and there is no portable way to reliably -// determine character width. The renderer can deal with some of the problems -// caused by this, but results may vary. -// - Decomposed (NFD) UTF-8 is not supported and will cause havoc -// - Zero width code points are not supported -// - Windows cmd.exe resize events may be delayed - -/* compatibility **************************************************************/ - -// emulator | support | remarks -// ------------------|---------|---------------------------------- -// Alacritty | ? | -// cmd.exe | good | resize may lag -// Cool Retro Term | good | wide character spill -// Deepin Terminal | good | wide character spill -// Eterm | abysmal | garbled output -// GNOME Terminal | full | -// GNUstep Terminal | abysmal | garbled output -// iTerm2 | ? | -// kitty | full | -// Konsole | full | -// LXTerminal | full | -// macOS Terminal | ? | -// PuTTY | full | -// QTerminal | good | wide character spill -// rxvt-unicode | full | -// GNU Screen | good | no alternate buffer, double esc -// st | full | -// Terminator | full | -// Terminology | full | -// tmux | good | esc lags -// Windows Terminal | full | -// Xfce Terminal | full | -// XTerm | full | XTerm is law -// Zutty | full | - -/* license ********************************************************************/ - -// MIT License -// -// Copyright (c) MMXXIV Chu'vok -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// 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 or copyright holders 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. - -/* includes *******************************************************************/ - -// unix-like -#if defined __unix__ || defined __unix || defined __APPLE__ || defined __ELF__ -#define TIM_UNIX -#include -#include -#include -#include -#endif - -// windows -#ifdef _WIN32 -#define TIM_WINDOWS -#define _CRT_SECURE_NO_WARNINGS -#define WIN32_LEAN_AND_MEAN -#include -#endif +#pragma once // libc #include #include -#include #include #include #include #include #include -/* workaround *****************************************************************/ +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef float f32; +typedef double f64; +typedef const char* cstr; -#ifdef __cplusplus -#error "C++ is not supported. Sorry." -#endif - -#ifdef _MSC_VER -// disable integer conversion warnings -#pragma warning(disable:4244) +#if !defined(__cplusplus) && !defined(bool) + # define bool u8 + # define false 0 + # define true 1 #endif #ifdef _WIN32 -// fix windows.h name clash, coincidentally they have the same values -#undef KEY_EVENT // 0x0001 -#undef MOUSE_EVENT // 0x0002 + // windows + #define TIM_WINDOWS + #define _CRT_SECURE_NO_WARNINGS + #define WIN32_LEAN_AND_MEAN + #include + + // fix windows.h name clash, coincidentally they have the same values + #undef TimEvent_Key // 0x0001 + #undef TimEvent_Mouse // 0x0002 + + #ifdef _MSC_VER + // disable integer conversion warnings + #pragma warning(disable:4244) + #endif +#else + // unix + #define TIM_UNIX + #include + #include + #include + #include #endif -#ifdef __PCC__ -// Guard to identify dynamic shared objects during global destruction. Not sure -// if this is a good idea. pcc and tcc may require this. -int __dso_handle; +#ifdef __cplusplus +extern "C" { #endif /* constants ******************************************************************/ -#define ENABLE_DBUF 1 // double buffering -#define MAX_SCOPE 20 // max scope nesting -#define MAX_CELLS 0x20000 // size of screen buffer -#define MAX_BUF (MAX_CELLS * 4) // size of output buffer -#define A INT_MAX // auto center / width / height +#define TIM_ENABLE_DBUF 1 // double buffering +#define TIM_MAX_SCOPE 20 // max scope nesting +#define TIM_MAX_CELLS 0x20000 // size of screen buffer +#define TIM_MAX_BUF (TIM_MAX_CELLS * 4) // size of output buffer +#define A INT_MAX // auto center / width / height -// tim.event.type -enum { - DRAW_EVENT, // draw screen - KEY_EVENT, // a key was pressed - MOUSE_EVENT, // mouse button, scroll or move - VOID_EVENT, // set when an event was consumed -}; +typedef enum TimEventType { + TimEvent_Void, // an event was consumed + TimEvent_Draw, // draw screen + TimEvent_Key, // a key was pressed + TimEvent_Mouse, // mouse button, scroll or move +} TimEventType; -// tim.event.key enum { - LEFT_BUTTON = 1, - BACKSPACE_KEY = 8, - TAB_KEY = 9, - ENTER_KEY = 13, - ESCAPE_KEY = 27, - INSERT_KEY = -1, - DELETE_KEY = -2, - HOME_KEY = -3, - END_KEY = -4, - PAGEUP_KEY = -5, - PAGEDOWN_KEY = -6, - UP_KEY = -7, - DOWN_KEY = -8, - LEFT_KEY = -9, - RIGHT_KEY = -10, + TimKey_MouseButtonLeft = 1, + TimKey_Backspace = 8, + TimKey_Tab = 9, + TimKey_Enter = 13, + TimKey_Escape = 27, + /* printable utf8 characters */ + TimKey_Insert = -1, + TimKey_Delete = -2, + TimKey_Home = -3, + TimKey_End = -4, + TimKey_PageUp = -5, + TimKey_PageDown = -6, + TimKey_Up = -7, + TimKey_Down = -8, + TimKey_Left = -9, + TimKey_Right = -10, }; +typedef i32 TimKey; /* types **********************************************************************/ -struct cell { - uint8_t fg; // foreground color - uint8_t bg; // background color - uint8_t wide; // wide or following wide character - uint8_t n; // number of bytes in buf - uint8_t buf[4]; // utf8 code point -}; +typedef struct TimCell_t { + u8 fg; // foreground color + u8 bg; // background color + u8 wide; // wide or following wide character + u8 n; // number of bytes in buf + u8 buf[4]; // utf8 code point +} TimCell_t; -struct rect { +typedef struct TimRect_t { int x; // x coordinate (left = 0) int y; // y coordinate (top = 0) int w; // width int h; // height -}; +} TimRect_t; -struct text { +typedef struct TimText_t { int size; // size in bytes without terminator int width; // widest line int lines; // number of lines -}; +} TimText_t; -struct line { +typedef struct TimLine_t { const char* s; // input and parse state const char* line; // line strings, not terminated int size; // line size in bytes int width; // line width in glyph -}; +} TimLine_t; -struct event { - int type; // event type - int32_t key; // used by KEY_EVENT and MOUSE_EVENT - int x; // used by MOUSE_EVENT - int y; // used by MOUSE_EVENT - char s[32]; // string representation of key -}; +typedef struct TimEvent_t { + TimEventType type; + TimKey key; // used by TimEvent_Key and TimEvent_Mouse + i32 x; // used by TimEvent_Mouse + i32 y; // used by TimEvent_Mouse + char s[32]; // string representation of key, used by TimEvent_Key +} TimEvent_t; -struct edit { +typedef struct TimEdit_t { int cursor; // cursor position (utf8) int length; // string length (utf8) int capacity; // buffer size char* s; // zero terminated buffer -}; +} TimEdit_t; -struct state { +typedef struct TimState_t { int w; // screen width int h; // screen height int frame; // frame counter - struct event event; // current event + TimEvent_t event; // current event void* focus; // focused element int loop_stage; // loop stage bool resized; // screen was resized int scope; // current scope - struct rect scopes[MAX_SCOPE]; // scope stack - struct cell* cells; // screen buffer + TimRect_t scopes[TIM_MAX_SCOPE]; // scope stack + TimCell_t* cells; // screen buffer char* buf; // final output buffer int buf_size; // position in write buffer - int64_t start_us; // render start time + i64 start_us; // render start time int render_us; // elapsed render time #ifdef TIM_UNIX // struct termios attr; // initial attributes @@ -405,34 +161,28 @@ struct state { UINT cp_in; // initial input code page UINT cp_out; // initial output code page #endif -}; +} TimState_t; /* macros *********************************************************************/ #define MAX(a, b) ((a) > (b) ? (a) : (b)) // #define MIN(a, b) ((a) < (b) ? (a) : (b)) // #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) // number of items in array -#define S(s) (""s), (sizeof(s) - 1) // expand to s, sizeof(s) - 1 +#define S(s) ("" s), (sizeof(s) - 1) // expand to s, sizeof(s) - 1 /* global variables ***********************************************************/ +// TODO: remove global variables + // These buffers were part of tim struct but caused the linker to produce very // large binaries. -static struct cell tim_cells[MAX_CELLS << ENABLE_DBUF]; // screen buffer -static char tim_buf[MAX_BUF]; // output buffer +static TimCell_t tim_cells[TIM_MAX_CELLS << TIM_ENABLE_DBUF]; // screen buffer +static char tim_buf[TIM_MAX_BUF]; // output buffer -// global state -#ifdef TIM_EXTERN_STATE -extern struct state tim; -#else -// Intentionally not declared as static to trigger a linker error when used in -// multiple compilation units. If that happens, #define TIM_EXTERN_STATE before -// including this header in all but one compilation unit. -struct state tim = { +TimState_t tim = { .cells = tim_cells, .buf = tim_buf, }; -#endif /* string *********************************************************************/ @@ -447,7 +197,7 @@ static inline int ztrlen(const char* s) { } // bit scan reverse, count leading zeros -static inline int bsr8(uint8_t x) { +static inline int bsr8(u8 x) { #if defined __GNUC__ || defined __clang__ unsigned int b = x; b <<= sizeof(b) * CHAR_BIT - 8; @@ -468,14 +218,14 @@ static inline int bsr8(uint8_t x) { } // decode one utf8 code point -static int32_t utfchr(const char* s) { +static i32 utfchr(const char* s) { s = s ? s : ""; // use bit magic to mask out leading utf8 1s - uint32_t c = s[0] & ((1 << (8 - bsr8(~s[0]))) - 1); + u32 c = s[0] & ((1 << (8 - bsr8(~s[0]))) - 1); for (int i = 1; s[0] && s[i] && i < 4; i++) { c = (c << 6) | (s[i] & 63); } - return (int32_t)c; + return (i32)c; } // number of utf8 code points @@ -500,10 +250,10 @@ static int utfpos(const char* s, int pos) { } // scan string for width and lines -static struct text scan_str(const char* s) { +static TimText_t scan_str(const char* s) { if(s == NULL) s = ""; - struct text t = { + TimText_t t = { .width = 0, .lines = (s[0] != 0), }; @@ -512,7 +262,7 @@ static struct text scan_str(const char* s) { char ch = s[t.size]; int newline = (ch == '\n'); width = newline ? 0 : width; - width += (ch & 192) != 128 && (uint8_t)ch > 31; + width += (ch & 192) != 128 && (u8)ch > 31; t.lines += newline; t.width = MAX(t.width, width); } @@ -520,7 +270,7 @@ static struct text scan_str(const char* s) { } // iterate through lines, false when end is reached -static bool next_line(struct line* l) { +static bool next_line(TimLine_t* l) { if (!l->s || !l->s[0]) { return false; } @@ -529,14 +279,14 @@ static bool next_line(struct line* l) { l->width = 0; for (const char* s = l->s; s[0] && s[0] != '\n'; s++) { l->size += 1; - l->width += (s[0] & 192) != 128 && (uint8_t)s[0] > 31; + l->width += (s[0] & 192) != 128 && (u8)s[0] > 31; } l->s += l->size + !!l->s[l->size]; return true; } // true if utf8 code point could be wide -static bool is_wide_perhaps(const uint8_t* s, int n) { +static bool is_wide_perhaps(const u8* s, int n) { // Character width depends on character, terminal and font. There is no // reliable method, however most frequently used characters are narrow. // Zero with characters are ignored, and hope that user input is benign. @@ -575,7 +325,7 @@ static void update_screen_size(void) { } int w = ws.ws_col; int h = ws.ws_row; - tim.resized = (unsigned)(w * h) <= MAX_CELLS && (w != tim.w || h != tim.h); + tim.resized = (unsigned)(w * h) <= TIM_MAX_CELLS && (w != tim.w || h != tim.h); if (tim.resized) { tim.w = tim.scopes[0].w = w; tim.h = tim.scopes[0].h = h; @@ -610,54 +360,54 @@ static void reset_terminal(void) { } // parse input stored in e->s -static bool parse_input(struct event* restrict e, int n) { +static bool parse_input(event* restrict e, int n) { char* s = e->s; if (n == 1 || s[0] != 27) { // regular key press - e->type = KEY_EVENT; - e->key = s[0] == 127 ? BACKSPACE_KEY : utfchr(s); + e->type = TimEvent_Key; + e->key = s[0] == 127 ? TimKey_Backspace : utfchr(s); return true; } if (n >= 9 && !memcmp(s, S("\33[<"))) { // sgr mouse sequence - e->type = MOUSE_EVENT; + e->type = TimEvent_Mouse; int btn = strtol(s + 3, &s, 10); e->x = strtol(s + 1, &s, 10) - 1; e->y = strtol(s + 1, &s, 10) - 1; if (btn == 0 && s[0] == 'M') { // left button pressed - e->key = LEFT_BUTTON; + e->key = TimKey_MouseButtonLeft; return true; } return false; } static struct {char s[4]; int k;} key_table[] = { - {"[A" , UP_KEY}, // - {"[B" , DOWN_KEY}, // - {"[C" , RIGHT_KEY}, // - {"[D" , LEFT_KEY}, // - {"[2~", INSERT_KEY}, // - {"[4h", INSERT_KEY}, // st - {"[3~", DELETE_KEY}, // - {"[P" , DELETE_KEY}, // st - {"[H" , HOME_KEY}, // - {"[1~", HOME_KEY}, // rxvt, lxterm, putty, tmux, screen - {"[7~", HOME_KEY}, // rxvt - {"[F" , END_KEY}, // - {"[4~", END_KEY}, // rxvt, lxterm, putty, tmux, screen, st - {"[8~", END_KEY}, // rxvt - {"[5~", PAGEUP_KEY}, // - {"[6~", PAGEDOWN_KEY}, // + {"[A" , TimKey_Up}, // + {"[B" , TimKey_Down}, // + {"[C" , TimKey_Right}, // + {"[D" , TimKey_Left}, // + {"[2~", TimKey_Insert}, // + {"[4h", TimKey_Insert}, // st + {"[3~", TimKey_Delete}, // + {"[P" , TimKey_Delete}, // st + {"[H" , TimKey_Home}, // + {"[1~", TimKey_Home}, // rxvt, lxterm, putty, tmux, screen + {"[7~", TimKey_Home}, // rxvt + {"[F" , TimKey_End}, // + {"[4~", TimKey_End}, // rxvt, lxterm, putty, tmux, screen, st + {"[8~", TimKey_End}, // rxvt + {"[5~", TimKey_PageUp}, // + {"[6~", TimKey_PageDown}, // }; if ((n == 3 || n == 4) && s[0] == 27) { // key sequence for (int i = 0; i < (int)ARRAY_SIZE(key_table); i++) { if (!memcmp(s + 1, key_table[i].s, n - 1)) { - e->type = KEY_EVENT; + e->type = TimEvent_Key; e->key = key_table[i].k; return true; } @@ -668,7 +418,7 @@ static bool parse_input(struct event* restrict e, int n) { } static void read_event(int timeout_ms) { - struct event* e = &tim.event; + event* e = &tim.event; struct pollfd pfd[2] = { {.fd = tim.signal_pipe[0], .events = POLLIN}, @@ -684,7 +434,7 @@ static void read_event(int timeout_ms) { continue; } else if (r == 0) { // poll timeout - e->type = DRAW_EVENT; + e->type = TimEvent_Draw; return; } @@ -694,7 +444,7 @@ static void read_event(int timeout_ms) { int n = read(tim.signal_pipe[0], &sig, sizeof(sig)); if (n > 0 && sig == SIGWINCH) { // screen size changed - e->type = DRAW_EVENT; + e->type = TimEvent_Draw; update_screen_size(); return; } @@ -710,7 +460,7 @@ static void read_event(int timeout_ms) { } // while } -static inline int64_t time_us(void) { +static inline i64 time_us(void) { struct timespec ts = {0}; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * 1000000 + ts.tv_nsec / 1000; @@ -740,7 +490,7 @@ static void update_screen_size(void) { } int w = csbi.srWindow.Right - csbi.srWindow.Left + 1; int h = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; - tim.resized = (unsigned)(w * h) <= MAX_CELLS && (w != tim.w || h != tim.h); + tim.resized = (unsigned)(w * h) <= TIM_MAX_CELLS && (w != tim.w || h != tim.h); if (tim.resized) { tim.w = tim.scopes[0].w = w; tim.h = tim.scopes[0].h = h; @@ -755,6 +505,7 @@ static void init_terminal(void) { mode = tim.mode_in; // mode &= ~ENABLE_ECHO_INPUT; // disable echo mode &= ~ENABLE_LINE_INPUT; // disable line buffer + // TODO: enable ctrl-c again mode &= ~ENABLE_PROCESSED_INPUT; // disable ctrl-c mode |= ENABLE_WINDOW_INPUT; // enable resize event mode |= ENABLE_MOUSE_INPUT; // enable mouse event @@ -790,24 +541,24 @@ static void reset_terminal(void) { } static void read_event(int timeout_ms) { - struct event* e = &tim.event; - HANDLE h = GetStdHandle(STD_INPUT_HANDLE); + TimEvent_t* e = &tim.event; + HANDLE h = GetStdHandle(STD_INPUT_HANDLE); - static const int8_t key_table[256] = { - [VK_BACK] = BACKSPACE_KEY, - [VK_TAB] = TAB_KEY, - [VK_RETURN] = ENTER_KEY, - [VK_ESCAPE] = ESCAPE_KEY, - [VK_PRIOR] = PAGEUP_KEY, - [VK_NEXT] = PAGEDOWN_KEY, - [VK_END] = END_KEY, - [VK_HOME] = HOME_KEY, - [VK_LEFT] = LEFT_KEY, - [VK_UP] = UP_KEY, - [VK_RIGHT] = RIGHT_KEY, - [VK_DOWN] = DOWN_KEY, - [VK_INSERT] = INSERT_KEY, - [VK_DELETE] = DELETE_KEY, + static const i8 key_table[256] = { + [VK_BACK] = TimKey_Backspace, + [VK_TAB] = TimKey_Tab, + [VK_RETURN] = TimKey_Enter, + [VK_ESCAPE] = TimKey_Escape, + [VK_PRIOR] = TimKey_PageUp, + [VK_NEXT] = TimKey_PageDown, + [VK_END] = TimKey_End, + [VK_HOME] = TimKey_Home, + [VK_LEFT] = TimKey_Left, + [VK_UP] = TimKey_Up, + [VK_RIGHT] = TimKey_Right, + [VK_DOWN] = TimKey_Down, + [VK_INSERT] = TimKey_Insert, + [VK_DELETE] = TimKey_Delete, }; while (true) { @@ -817,7 +568,7 @@ static void read_event(int timeout_ms) { DWORD r = WaitForSingleObject(h, timeout_ms); if (r == WAIT_TIMEOUT) { - e->type = DRAW_EVENT; + e->type = TimEvent_Draw; update_screen_size(); // workaround, see WINDOW_BUFFER_SIZE_EVENT return; } else if (r != WAIT_OBJECT_0) { @@ -826,7 +577,7 @@ static void read_event(int timeout_ms) { // received input INPUT_RECORD rec = {0}; - DWORD n = 0; + DWORD n = 0; ReadConsoleInput(h, &rec, 1, &n); switch (rec.EventType) { @@ -835,14 +586,14 @@ static void read_event(int timeout_ms) { // only interested in key press continue; } - int key = key_table[(uint8_t)rec.Event.KeyEvent.wVirtualKeyCode]; + int key = key_table[(u8)rec.Event.KeyEvent.wVirtualKeyCode]; WCHAR chr = rec.Event.KeyEvent.uChar.UnicodeChar; if (!key && chr < ' ') { // non printable key continue; } update_screen_size(); // workaround, see WINDOW_BUFFER_SIZE_EVENT - e->type = KEY_EVENT; + e->type = TimEvent_Key; if (key) { e->key = key; return; @@ -862,15 +613,15 @@ static void read_event(int timeout_ms) { continue; } update_screen_size(); // workaround, see WINDOW_BUFFER_SIZE_EVENT - e->type = MOUSE_EVENT; - e->key = LEFT_BUTTON; + e->type = TimEvent_Mouse; + e->key = TimKey_MouseButtonLeft; e->x = rec.Event.MouseEvent.dwMousePosition.X - tim.window.Left; e->y = rec.Event.MouseEvent.dwMousePosition.Y - tim.window.Top; return; } case WINDOW_BUFFER_SIZE_EVENT: - e->type = DRAW_EVENT; + e->type = TimEvent_Draw; // cmd.exe screen buffer and window size are separate, making this // event a bit unreliable. Effectively it is only emitted when the // terminal width changes and not for the height. As a workaround @@ -881,7 +632,7 @@ static void read_event(int timeout_ms) { } // while } -static inline int64_t time_us(void) { +static inline i64 time_us(void) { LARGE_INTEGER ticks = {0}; LARGE_INTEGER freq = {0}; QueryPerformanceCounter(&ticks); @@ -894,32 +645,32 @@ static inline int64_t time_us(void) { /* events *********************************************************************/ // returns true if event was of type and key -static inline bool is_event_key(int type, int32_t key) { +static inline bool is_event_key(TimEventType type, TimKey key) { return tim.event.type == type && tim.event.key == key; } // returns true if event was press of key -static inline bool is_key_press(int32_t key) { - return is_event_key(KEY_EVENT, key); +static inline bool is_key_press(TimKey key) { + return is_event_key(TimEvent_Key, key); } // returns true if mouse event was over r -static inline bool is_mouse_over(struct rect r) { +static inline bool is_mouse_over(TimRect_t r) { int x = tim.event.x; int y = tim.event.y; return x >= r.x && x < r.x + r.w && y >= r.y && y < r.y + r.h; } // returns true if event is mouse left-down and over r -static inline bool is_click_over(struct rect r) { - return is_event_key(MOUSE_EVENT, LEFT_BUTTON) && is_mouse_over(r); +static inline bool is_click_over(TimRect_t r) { + return is_event_key(TimEvent_Mouse, TimKey_MouseButtonLeft) && is_mouse_over(r); } /* drawing ********************************************************************/ // create cell from utf8 code point with fg and bg colors -static inline struct cell cell(const char* s, uint8_t fg, uint8_t bg) { - struct cell c = {.fg = fg, .bg = bg, .n = 1, .buf = {s[0]}}; +static inline TimCell_t cell(const char* s, u8 fg, u8 bg) { + TimCell_t c = {.fg = fg, .bg = bg, .n = 1, .buf = {s[0]}}; while ((s[c.n] & 192) == 128 && c.n < sizeof(c.buf)) { c.buf[c.n] = s[c.n]; c.n += 1; @@ -934,14 +685,14 @@ static void clear_cells(void) { } // draw cell at position -static void draw_chr(struct cell cell, int x, int y) { +static void draw_chr(TimCell_t cell, int x, int y) { if (x >= 0 && x < tim.w && y >= 0 && y < tim.h) { tim.cells[x + y * tim.w] = cell; } } // draw row of cells -static void draw_row(struct cell cell, int x, int y, int w) { +static void draw_row(TimCell_t cell, int x, int y, int w) { if (y >= 0 && y < tim.h && w > 0) { for (int i = MAX(x, 0); i < MIN(x + w, tim.w); i++) { tim.cells[i + y * tim.w] = cell; @@ -950,7 +701,7 @@ static void draw_row(struct cell cell, int x, int y, int w) { } // draw column of cells -static void draw_col(struct cell cell, int x, int y, int h) { +static void draw_col(TimCell_t cell, int x, int y, int h) { if (x >= 0 && x < tim.w && h > 0) { for (int i = MAX(y, 0); i < MIN(y + h, tim.h); i++) { tim.cells[x + i * tim.w] = cell; @@ -959,7 +710,7 @@ static void draw_col(struct cell cell, int x, int y, int h) { } // fill lot (area) of cells -static void draw_lot(struct cell cell, int x, int y, int w, int h) { +static void draw_lot(TimCell_t cell, int x, int y, int w, int h) { if (w > 0 && h > 0) { for (int iy = MAX(y, 0); iy < MIN(y + h, tim.h); iy++) { for (int ix = MAX(x, 0); ix < MIN(x + w, tim.w); ix++) { @@ -971,12 +722,12 @@ static void draw_lot(struct cell cell, int x, int y, int w, int h) { // draw string to line, tags potential wide characters static void draw_str(const char* s, int x, int y, int w, - uint8_t fg, uint8_t bg) { + u8 fg, u8 bg) { if (s && y >= 0 && x < tim.w && y < tim.h ) { int end = MIN(x + w, tim.w); bool wide = false; for (int i = 0; s[i] && x < end; x++) { - struct cell c = cell(&s[i], fg, bg); + TimCell_t c = cell(&s[i], fg, bg); wide = wide || is_wide_perhaps(c.buf, c.n); if (x >= 0) { c.wide = wide; @@ -988,7 +739,7 @@ static void draw_str(const char* s, int x, int y, int w, } // draw box of ascii cell characters -static void draw_box(int x, int y, int w, int h, uint8_t fg, uint8_t bg) { +static void draw_box(int x, int y, int w, int h, u8 fg, u8 bg) { draw_chr(cell("┌", fg, bg), x , y); draw_chr(cell("┐", fg, bg), x + w - 1, y); draw_chr(cell("└", fg, bg), x , y + h - 1); @@ -1004,7 +755,7 @@ static void draw_box(int x, int y, int w, int h, uint8_t fg, uint8_t bg) { static void draw_invert(int x, int y, int w) { if (y >= 0 && y < tim.h && w > 0) { for (int i = MAX(x, 0); i < MIN(x + w, tim.w); i++) { - struct cell c = tim.cells[i + y * tim.w]; + TimCell_t c = tim.cells[i + y * tim.w]; tim.cells[i + y * tim.w].fg = c.bg; tim.cells[i + y * tim.w].bg = c.fg; } @@ -1018,8 +769,8 @@ static void draw_invert(int x, int y, int w) { for (int _i = enter_scope((x), (y), (w), (h)); _i; _i = exit_scope()) // convert relative (scoped) to absolute (screen) coordinates -static struct rect abs_xywh(int x, int y, int w, int h) { - struct rect p = tim.scopes[tim.scope]; // parent scope +static TimRect_t abs_xywh(int x, int y, int w, int h) { + TimRect_t p = tim.scopes[tim.scope]; // parent scope x = (x == A && w == A) ? 0 : x; // special cases y = (y == A && h == A) ? 0 : y; // @@ -1049,15 +800,15 @@ static struct rect abs_xywh(int x, int y, int w, int h) { y += p.y; // anchor y to top } - return (struct rect){x, y, w, h}; + return (TimRect_t){x, y, w, h}; } // enter scope and push coordinates on stack static inline int enter_scope(int x, int y, int w, int h) { - if (tim.scope + 1 >= MAX_SCOPE) { + if (tim.scope + 1 >= TIM_MAX_SCOPE) { return 0; } - struct rect r = abs_xywh(x, y, w, h); + TimRect_t r = abs_xywh(x, y, w, h); tim.scope += 1; tim.scopes[tim.scope] = r; return 1; @@ -1073,9 +824,9 @@ static inline int exit_scope(void) { // frame // color: background, frame -static inline void frame(int x, int y, int w, int h, uint64_t color) { - if (tim.event.type == DRAW_EVENT) { - struct rect r = abs_xywh(x, y, w, h); +static inline void frame(int x, int y, int w, int h, u64 color) { + if (tim.event.type == TimEvent_Draw) { + TimRect_t r = abs_xywh(x, y, w, h); draw_box(r.x, r.y, r.w, r.h, color, color >> 8); } } @@ -1086,15 +837,15 @@ static inline void frame(int x, int y, int w, int h, uint64_t color) { // str : text - supports multiple lines // color: background, text static inline void label(const char* s, int x, int y, int w, int h, - uint64_t color) { - if (tim.event.type == DRAW_EVENT) { - struct text t = scan_str(s); + u64 color) { + if (tim.event.type == TimEvent_Draw) { + TimText_t t = scan_str(s); w = (w == A) ? t.width : w; h = (h == A) ? t.lines : h; - struct rect r = abs_xywh(x, y, w, h); - struct cell c = cell(" ", color, color >> 8); + TimRect_t r = abs_xywh(x, y, w, h); + TimCell_t c = cell(" ", color, color >> 8); draw_lot(c, r.x, r.y, r.w, r.h); - struct line l = {.s = s, .line = ""}; + TimLine_t l = {.s = s, .line = ""}; for (int i = 0; next_line(&l); i++) { draw_str(l.line, r.x, r.y + i, l.width, c.fg, c.bg); } @@ -1106,13 +857,13 @@ static inline void label(const char* s, int x, int y, int w, int h, // button - returns true on click // color: frame, background, text static inline bool button(const char* txt, int x, int y, int w, int h, - uint64_t color) { + u64 color) { int tw = utflen(txt); w = (w == A) ? (tw + 4) : w; h = (h == A) ? 3 : h; - struct rect r = abs_xywh(x, y, w, h); + TimRect_t r = abs_xywh(x, y, w, h); - if (tim.event.type == DRAW_EVENT) { + if (tim.event.type == TimEvent_Draw) { draw_box(r.x, r.y, r.w, r.h, color >> 16, color >> 8); draw_str(txt, r.x + (w - tw) / 2, r.y + h / 2, w, color, color >> 8); } @@ -1121,7 +872,7 @@ static inline bool button(const char* txt, int x, int y, int w, int h, /* edit ***********************************************************************/ -static void edit_insert(struct edit* e, const char* s) { +static void edit_insert(TimEdit_t* e, const char* s) { int dst_size = ztrlen(e->s); int src_size = ztrlen(s); if (dst_size + src_size < e->capacity) { @@ -1135,7 +886,7 @@ static void edit_insert(struct edit* e, const char* s) { } } -static void edit_delete(struct edit* e) { +static void edit_delete(TimEdit_t* e) { int size = ztrlen(e->s); int cur = utfpos(e->s, e->cursor); int len = utfpos(e->s + cur, 1); @@ -1146,43 +897,43 @@ static void edit_delete(struct edit* e) { } /// @return key id or 0 -static int edit_event(struct edit* e, struct rect r) { +static int edit_event(TimEdit_t* e, TimRect_t r) { if (is_click_over(r)) { // take focus tim.focus = e; return 0; } - if (tim.focus != e || tim.event.type != KEY_EVENT) { + if (tim.focus != e || tim.event.type != TimEvent_Key) { // not focused or no key press return 0; } - tim.event.type = VOID_EVENT; // consume event + tim.event.type = TimEvent_Void; // consume event switch (tim.event.key) { - case ESCAPE_KEY: - case ENTER_KEY: + case TimKey_Escape: + case TimKey_Enter: tim.focus = 0; // release focus break; - case DELETE_KEY: + case TimKey_Delete: edit_delete(e); break; - case BACKSPACE_KEY: + case TimKey_Backspace: if (e->cursor > 0) { e->cursor -= 1; edit_delete(e); } break; - case LEFT_KEY: + case TimKey_Left: e->cursor -= (e->cursor > 0); break; - case RIGHT_KEY: + case TimKey_Right: e->cursor += (e->cursor < e->length); break; - case HOME_KEY: + case TimKey_Home: e->cursor = 0; break; - case END_KEY: + case TimKey_End: e->cursor = e->length; break; default: @@ -1195,11 +946,11 @@ static int edit_event(struct edit* e, struct rect r) { return tim.event.key; } -static inline void edit_init(struct edit* e, int capacity, const char* initial_content){ +static inline void edit_init(TimEdit_t* e, int capacity, const char* initial_content){ e->length = utflen(initial_content); e->cursor = utflen(initial_content); e->capacity = capacity; - e->s = malloc(capacity + 1); + e->s = (char*)malloc(capacity + 1); int byte_len = strlen(initial_content); memcpy(e->s, initial_content, byte_len); e->s[byte_len] = 0; @@ -1209,10 +960,10 @@ static inline void edit_init(struct edit* e, int capacity, const char* initial_c /// @param e persistent edit state, use edit_init() to create new state /// @param color frame, background, text /// @return key id or 0 -static inline int edit(struct edit* e, int x, int y, int w, uint64_t color) { - struct rect r = abs_xywh(x, y, w, 3); +static inline int edit(TimEdit_t* e, int x, int y, int w, u64 color) { + TimRect_t r = abs_xywh(x, y, w, 3); - if (tim.event.type == DRAW_EVENT) { + if (tim.event.type == TimEvent_Draw) { draw_box(r.x, r.y, r.w, r.h, color >> 16, color >> 8); if (tim.focus == e) { char* s = e->s + utfpos(e->s, e->cursor - r.w + 4); @@ -1234,11 +985,11 @@ static inline int edit(struct edit* e, int x, int y, int w, uint64_t color) { // state: persistent state, 0 unchecked, -1 semi checked, !0: checked // color: check, background, text static inline bool check(const char* txt, int* state, int x, int y, int w, - uint64_t color) { + u64 color) { w = (w == A) ? utflen(txt) + 4 : w; - struct rect r = abs_xywh(x, y, w, 1); + TimRect_t r = abs_xywh(x, y, w, 1); - if (tim.event.type == DRAW_EVENT) { + if (tim.event.type == TimEvent_Draw) { const char* st = *state == -1 ? "-" : *state ? "x" : " "; draw_str("[ ] ", r.x, r.y, 4, color, color >> 8); draw_str(st, r.x + 1, r.y, 1, color >> 16, color >> 8); @@ -1258,11 +1009,11 @@ static inline bool check(const char* txt, int* state, int x, int y, int w, // v : value // color: radio, background, text static inline bool radio(const char* txt, int* state, int v, int x, int y, - int w, uint64_t color) { + int w, u64 color) { w = (w == A) ? utflen(txt) + 4 : w; - struct rect r = abs_xywh(x, y, w, 1); + TimRect_t r = abs_xywh(x, y, w, 1); - if (tim.event.type == DRAW_EVENT) { + if (tim.event.type == TimEvent_Draw) { const char* st = *state == v ? "o" : " "; draw_str("( ) ", r.x, r.y, 4, color, color >> 8); draw_str(st, r.x + 1, r.y, 1, color >> 16, color >> 8); @@ -1278,7 +1029,7 @@ static inline bool radio(const char* txt, int* state, int v, int x, int y, // write character to output buffer static inline void put_chr(char c) { - if (tim.buf_size + 1 < MAX_BUF) { + if (tim.buf_size + 1 < TIM_MAX_BUF) { tim.buf[tim.buf_size] = c; tim.buf_size += 1; } @@ -1286,7 +1037,7 @@ static inline void put_chr(char c) { // write string to output buffer static inline void put_str(const char* s, int size) { - if (size > 0 && tim.buf_size + size < MAX_BUF) { + if (size > 0 && tim.buf_size + size < TIM_MAX_BUF) { memmove(&tim.buf[tim.buf_size], s, size); tim.buf_size += size; } @@ -1296,7 +1047,7 @@ static inline void put_str(const char* s, int size) { static inline void put_int(int i) { // optimized for small positive values, reduces load by a third char* buf = &tim.buf[tim.buf_size]; - if (tim.buf_size + 11 >= MAX_BUF) { + if (tim.buf_size + 11 >= TIM_MAX_BUF) { // not enough space for 32 bit integer } else if ((unsigned)i < 10) { buf[0] = '0' + i; @@ -1322,20 +1073,20 @@ static void render(void) { bool skip = false; // screen buffers - struct cell* new_cells = tim_cells; - struct cell* old_cells = tim_cells; -#if ENABLE_DBUF - new_cells += (tim.frame & 1) ? MAX_CELLS : 0; - old_cells += (tim.frame & 1) ? 0 : MAX_CELLS; + TimCell_t* new_cells = tim_cells; + TimCell_t* old_cells = tim_cells; +#if TIM_ENABLE_DBUF + new_cells += (tim.frame & 1) ? TIM_MAX_CELLS : 0; + old_cells += (tim.frame & 1) ? 0 : TIM_MAX_CELLS; #endif tim.buf_size = 0; for (int i = 0; i < tim.w * tim.h; i++) { - struct cell c = new_cells[i]; -#if ENABLE_DBUF + TimCell_t c = new_cells[i]; +#if TIM_ENABLE_DBUF // do nothing if cells in look-ahead are identical const int la = 8; // look-ahead - if (!tim.resized && !(i % la) && (i + la < MAX_CELLS) && + if (!tim.resized && !(i % la) && (i + la < TIM_MAX_CELLS) && !memcmp(new_cells + i, old_cells + i, la * sizeof(c))) { skip = true; i = i + la - 1; @@ -1391,7 +1142,7 @@ static void render(void) { /* event loop *****************************************************************/ -static bool tim_run(float fps) { +static bool tim_run(f32 fps) { int timeout = (fps > 0) ? (int)(1000 / fps) : 0; while (true) { @@ -1404,9 +1155,9 @@ static bool tim_run(float fps) { case 1: // process input event tim.start_us = time_us(); - if (tim.event.type != DRAW_EVENT) { + if (tim.event.type != TimEvent_Draw) { // reset focus on mouse click - if (is_event_key(MOUSE_EVENT, LEFT_BUTTON)) { + if (is_event_key(TimEvent_Mouse, TimKey_MouseButtonLeft)) { tim.focus = 0; } tim.loop_stage = 2; @@ -1416,7 +1167,7 @@ static bool tim_run(float fps) { case 2: // process draw event clear_cells(); - tim.event.type = DRAW_EVENT; + tim.event.type = TimEvent_Draw; tim.loop_stage = 3; return true; case 3: @@ -1432,3 +1183,6 @@ static bool tim_run(float fps) { } // while } +#ifdef __cplusplus +} +#endif