387 lines
10 KiB
C
387 lines
10 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>
|
|
|
|
// 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 <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_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 {
|
|
i32 cursor; // cursor position (utf8)
|
|
i32 length; // string length (utf8)
|
|
i32 capacity; // buffer size in bytes
|
|
char* s; // zero terminated buffer
|
|
} TimEditState;
|
|
|
|
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
|
|
|
|
// TODO: create enum TimColor and struct TimStyle
|
|
|
|
// 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 - returns true on click
|
|
// color: frame, background, text
|
|
bool tim_button(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_init() to create new state
|
|
/// @param color frame, background, text
|
|
/// @return key id or 0
|
|
TimKey tim_edit(TimEditState* e, i32 x, i32 y, i32 w, TimStyle style);
|
|
|
|
void TimEditState_init(TimEditState* e, i32 capacity, cstr initial_content);
|
|
|
|
void TimEditState_insert(TimEditState* e, cstr s);
|
|
|
|
void TimEditState_delete(TimEditState* e);
|
|
|
|
#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
|
|
bool tim_is_key_press(TimKey key);
|
|
// 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);
|
|
|
|
#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
|