Files
tim/include/tim.h

423 lines
12 KiB
C

#pragma once
#pragma region include
#include <limits.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
// windows
#define TIM_WINDOWS
#define _CRT_SECURE_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifdef _MSC_VER
// disable integer conversion warnings
#pragma warning(disable:4244)
#endif
#else
// unix
#define TIM_UNIX
#include <poll.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#endif
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;
#if !defined(__cplusplus) && !defined(bool)
# define bool u8
# define false 0
# define true 1
#endif
#ifdef __cplusplus
extern "C" {
#endif
#pragma endregion
#pragma region constants
#define TIM_ENABLE_DBUF 1 // double buffering
#define TIM_MAX_SCOPE 32 // 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
#pragma endregion
#pragma region types
/* first 16 colors from xterm256 supported by any terminal emulator */
enum {
TimColor16_Black = 0x00,
TimColor16_DarkRed = 0x01,
TimColor16_DarkGreen = 0x02,
TimColor16_DarkYellow = 0x03,
TimColor16_DarkBlue = 0x04,
TimColor16_DarkMagenta = 0x05,
TimColor16_DarkCyan = 0x06,
TimColor16_Gray = 0x07,
TimColor16_DarkGray = 0x08,
TimColor16_Red = 0x09,
TimColor16_Green = 0x0a,
TimColor16_Yellow = 0x0b,
TimColor16_Blue = 0x0c,
TimColor16_Magenta = 0x0d,
TimColor16_Cyan = 0x0e,
TimColor16_White = 0x0f,
};
typedef struct TimStyle {
u8 brd; // border
u8 bg; // background
u8 fg; // foreground
} TimStyle;
typedef struct TimCell {
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;
typedef struct TimRect {
i32 x; // x coordinate (left = 0)
i32 y; // y coordinate (top = 0)
i32 w; // width
i32 h; // height
} TimRect;
typedef struct TimText {
i32 size; // size in bytes without terminator
i32 width; // widest line
i32 lines; // number of lines
} TimText;
typedef struct TimLine {
cstr s; // input and parse state
cstr line; // line strings, not terminated
i32 size; // line size in bytes
i32 width; // line width in glyph
} TimLine;
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;
enum {
TimKey_MouseButtonLeft = 1,
TimKey_MouseScrollUp = 4,
TimKey_MouseScrollDown = 5,
TimKey_Backspace = 8,
TimKey_Tab = 9,
TimKey_Enter = 13,
TimKey_Escape = 27,
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,
};
// key code or 32-bit unicode char
typedef i32 TimKey;
typedef struct TimEvent {
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;
typedef struct TimEditState {
bool masked; // if true prints '*' instead of buffer content
i32 cursor; // cursor position (utf8)
i32 length; // string length (utf8)
i32 capacity; // buffer size in bytes
char* s; // zero terminated buffer
} TimEditState;
typedef struct TimScrollItem {
void* data;
void* focus_target; // is assigned to tim->focus
i32 h; // height of the item to know where to draw next item
void (*draw)(bool is_selected, TimRect place, void* data);
} TimScrollItem;
typedef struct TimScrollState {
TimScrollItem* items;
u32 len;
u32 cur;
bool use_mouse_wheel;
} TimScrollState;
typedef struct TimState {
i32 w; // screen width
i32 h; // screen height
i32 frame; // frame counter
TimEvent event; // current event
void* focus; // focused element
i32 loop_stage; // loop stage
bool resized; // screen was resized
i32 scope; // current scope
TimRect scopes[TIM_MAX_SCOPE]; // scope stack
TimCell* cells; // current screen buffer (first or second half of cells_double_buf)
TimCell* cells_double_buf; // pointer to double buffer
char* buf; // final output buffer
i32 buf_size; // position in write buffer
i64 start_us; // render start time
i32 render_us; // elapsed render time
#ifdef TIM_UNIX
struct termios attr; // initial attributes
i32 signal_pipe[2]; // signal fifo pipe
#endif
#ifdef TIM_WINDOWS
SMALL_RECT window; // screen buffer window size
DWORD mode_in; // initial input mode
DWORD mode_out; // initial output mode
UINT cp_in; // initial input code page
UINT cp_out; // initial output code page
#endif
} TimState;
#pragma endregion
#pragma region 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
#pragma endregion
#pragma region general
// global statem initialized by tim_run()
extern TimState* tim;
bool tim_run(f32 fps);
void tim_reset_terminal(void);
i64 tim_time_usec(void);
#pragma endregion
#pragma region scope
// for some stupid reason gcc requires more than 3 levels of macros to concat _i_ with line number
#define _tim_cat2(A, B) A##B
#define _tim_scope_line(L) _tim_cat2(_i_, L)
#define _tim_scope_i _tim_scope_line(__LINE__)
// enter layout scope
#define tim_scope(x, y, w, h) for (\
i32 _tim_scope_i = tim_enter_scope((x), (y), (w), (h)); \
_tim_scope_i;\
_tim_scope_i = tim_exit_scope()\
) /* here goes your { code } */
// convert relative (scoped) to absolute (screen) coordinates
TimRect tim_scope_rect_to_absolute(i32 x, i32 y, i32 w, i32 h);
// enter scope and push coordinates on stack
i32 tim_enter_scope(i32 x, i32 y, i32 w, i32 h);
// exit scope and pop stack
i32 tim_exit_scope(void);
#pragma endregion
#pragma region widgets
// frame
// color: background, frame
void tim_frame(i32 x, i32 y, i32 w, i32 h, TimStyle style);
// text label
// str : text - supports multiple lines
// color: background, text
void tim_label(cstr s, i32 x, i32 y, i32 w, i32 h, TimStyle style);
// button with border - returns true on click
// color: frame, background, text
bool tim_button(cstr txt, i32 x, i32 y, i32 w, i32 h, TimStyle style);
// button without border - returns true on click
// color: frame, background, text
bool tim_button_noborder(cstr txt, i32 x, i32 y, i32 w, i32 h, TimStyle style);
// check box - returns true when clicked
// txt : text label
// state: persistent state, 0 unchecked, -1 semi checked, !0: checked
// color: check, background, text
bool tim_checkbox(cstr txt, i32* state, i32 x, i32 y, i32 w, TimStyle style);
// radio button - return true when clicked
// txt : text label
// state: persistent state, selected if *state == v
// v : value
// color: radio, background, text
bool tim_radiobutton(cstr txt, i32* state, i32 v, i32 x, i32 y, i32 w, TimStyle style);
/// text edit - value in state
/// @param e persistent edit state, use TimEditState_construct() to create new state
/// @param style frame, background, text
/// @return key id or 0
TimKey tim_edit(TimEditState* e, i32 x, i32 y, i32 w, TimStyle style);
/// @param e uninitialized state
/// @param buffer an array
/// @param capacity buffer size in bytes
/// @param initial_content may be NULL
void TimEditState_construct(TimEditState* e, char* buffer, i32 capacity, cstr initial_content);
void TimEditState_insert(TimEditState* e, cstr s);
void TimEditState_delete(TimEditState* e);
/// @param l list of rows to display
/// @param style frame, background, text
TimScrollItem* tim_scroll(TimScrollState* l, i32 x, i32 y, i32 w, i32 h, TimStyle style);
void TimScrollState_selectNext(TimScrollState* l);
void TimScrollState_selectPrev(TimScrollState* l);
#pragma endregion
#pragma region event
// returns true if event was of type and key
bool tim_is_event_key(TimEventType type, TimKey key);
// returns true if event was press of key
static inline bool tim_is_key_press(TimKey key) { return tim_is_event_key(TimEvent_Key, key); }
static inline bool tim_is_mouse_scroll_up() { return tim_is_event_key(TimEvent_Mouse, TimKey_MouseScrollUp); }
static inline bool tim_is_mouse_scroll_down() { return tim_is_event_key(TimEvent_Mouse, TimKey_MouseScrollDown); }
// returns true if mouse event was over r
bool tim_is_mouse_over(TimRect r);
// returns true if event is mouse left-down and over r
bool tim_is_mouse_click_over(TimRect r);
static inline void tim_event_consume(){
tim->event.type = TimEvent_Void;
}
#pragma endregion
#pragma region drawing
// create cell from utf8 code point with fg and bg colors
TimCell tim_cell(cstr s, u8 fg, u8 bg);
// clear cell buffer
void tim_clear_cells(void);
// draw cell at position
void tim_draw_chr(TimCell cell, i32 x, i32 y);
// draw row of cells
void tim_draw_row(TimCell cell, i32 x, i32 y, i32 w);
// draw column of cells
void tim_draw_col(TimCell cell, i32 x, i32 y, i32 h);
// fill lot (area) of cells
void tim_draw_lot(TimCell cell, i32 x, i32 y, i32 w, i32 h);
// draw string to line, tags potential wide characters
void tim_draw_str(cstr s, i32 x, i32 y, i32 w, u8 fg, u8 bg);
// draw box of ascii cell characters
void tim_draw_box(i32 x, i32 y, i32 w, i32 h, u8 fg, u8 bg);
// invert fg and bg colors of line of cells
void tim_draw_invert(i32 x, i32 y, i32 w);
#pragma endregion
#pragma region string
// like strlen, returns 0 on NULL or i32 overflow
i32 tim_ztrlen(cstr s);
// decode one utf8 code point
i32 tim_utf8_to_i32(cstr s);
// number of utf8 code points
i32 tim_utf8_len(cstr s);
// index of utf8 code point at pos
i32 tim_utf8_pos(cstr s, i32 pos);
// true if utf8 code point could be wide
bool tim_utf8_is_wide_perhaps(const u8* s, i32 n);
// bit scan reverse, count leading zeros
i32 tim_bsr8(u8 x);
// scan string for width and lines
TimText tim_scan_str(cstr s);
// iterate through lines, false when end is reached
bool tim_next_line(TimLine* l);
#pragma endregion
#pragma region internal
void tim_write_str(cstr s, i32 size);
void tim_update_screen_size(void);
void tim_init_terminal(void);
void tim_read_event(i32 timeout_ms);
#pragma endregion
#ifdef __cplusplus
}
#endif