alloc big buffers on heap

This commit is contained in:
2026-01-09 05:42:08 +05:00
parent 01df4abcfb
commit bd23c66607
14 changed files with 245 additions and 230 deletions

View File

@@ -1,7 +1,7 @@
#include "tim.h"
i32 main(void) {
while (tim_run(0)) { // event loop
while (tim_run(0)) { // init state and start event loop
tim_scope(A, A, 24, 8) { // centered 24x8 scope
u64 c = 0x0a060f; // three colors
tim_frame(0, 0, ~0, ~0, c); // draw frame for scope
@@ -11,5 +11,5 @@ i32 main(void) {
if (tim_is_key_press('q')) // ctrl-c is masked
return 0; // exit on 'q' press
}
} //TODO: remove atexit cleanup
} // atexit() cleanup
}

View File

@@ -33,8 +33,8 @@ static struct {
static void start(void) {
memset(snek.body, -1, sizeof(snek.body));
snek.len = 2;
snek.body[0] = (point){{1, tim.h / 2}};
snek.food = (point){{tim.w / 8, tim.h / 2}};
snek.body[0] = (point){{1, tim->h / 2}};
snek.food = (point){{tim->w / 8, tim->h / 2}};
snek.look = (point){{1, 0}};
}
@@ -53,19 +53,19 @@ static void game(void) {
crash |= snek.body[0].xy == snek.body[i].xy;
}
// border crash
crash |= snek.body[0].x < 0 || snek.body[0].x >= tim.w / 2 ||
snek.body[0].y < 0 || snek.body[0].y >= tim.h;
crash |= snek.body[0].x < 0 || snek.body[0].x >= tim->w / 2 ||
snek.body[0].y < 0 || snek.body[0].y >= tim->h;
snek.state = crash ? OVER : snek.state;
// food
if (snek.food.xy == snek.body[0].xy) {
snek.len = MIN(snek.len + 2, (i32)ARRAY_SIZE(snek.body));
snek.food.x = rand() % (tim.w / 2 - 2) + 1;
snek.food.y = rand() % (tim.h - 2) + 1;
snek.food.x = rand() % (tim->w / 2 - 2) + 1;
snek.food.y = rand() % (tim->h - 2) + 1;
}
}
// draw
if (tim.event.type == TimEvent_Draw) {
if (tim->event.type == TimEvent_Draw) {
// food
tim_draw_chr(tim_cell(" ", 0, 0xc5), snek.food.x * 2 + 0, snek.food.y);
tim_draw_chr(tim_cell(" ", 0, 0xc5), snek.food.x * 2 + 1, snek.food.y);
@@ -81,8 +81,8 @@ static void game(void) {
}
// user input
if (tim.event.type == KEY_EVENT) {
i32 key = tim.event.key;
if (tim->event.type == KEY_EVENT) {
i32 key = tim->event.key;
if ((key == TimKey_Right || key == 'd') && snek.look.x != -1) {
snek.look = (point){{1, 0}};
} else if ((key == TimKey_Left || key == 'a') && snek.look.x != 1) {
@@ -119,7 +119,7 @@ i32 main(void) {
// draw every 10 ms
while (tim_run(60)) {
TimCell bg = tim_cell(" ", 0, BG);
tim_draw_lot(bg, 0, 0, tim.w, tim.h);
tim_draw_lot(bg, 0, 0, tim->w, tim->h);
if (snek.state == RUN) {
game();

View File

@@ -181,8 +181,8 @@ typedef struct TimState {
#pragma region general
// TODO: remove global variables
extern TimState tim;
// global statem initialized by tim_run()
extern TimState* tim;
bool tim_run(f32 fps);
@@ -343,8 +343,6 @@ void tim_init_terminal(void);
void tim_read_event(i32 timeout_ms);
void tim_render(void);
#pragma endregion

View File

@@ -1,6 +1,6 @@
## about
tim.h is a portable library to create simple terminal applications
tim->h is a portable library to create simple terminal applications
Demo video: https://asciinema.org/a/zn3p0dsVCOQOzwY1S9gDfyaxQ
## quick start
@@ -66,7 +66,7 @@ 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.
The current event is stored in tim->event.
event | cause
-------------|-----------------------

View File

@@ -10,52 +10,52 @@ TimCell tim_cell(cstr s, u8 fg, u8 bg) {
}
void tim_clear_cells(void) {
size_t size = sizeof(tim.cells[0]) * tim.w * tim.h;
memset(tim.cells, 0, size);
size_t size = sizeof(tim->cells[0]) * tim->w * tim->h;
memset(tim->cells, 0, size);
}
void tim_draw_chr(TimCell cell, i32 x, i32 y) {
if (x >= 0 && x < tim.w && y >= 0 && y < tim.h) {
tim.cells[x + y * tim.w] = cell;
if (x >= 0 && x < tim->w && y >= 0 && y < tim->h) {
tim->cells[x + y * tim->w] = cell;
}
}
void tim_draw_row(TimCell cell, i32 x, i32 y, i32 w) {
if (y >= 0 && y < tim.h && w > 0) {
for (i32 i = MAX(x, 0); i < MIN(x + w, tim.w); i++) {
tim.cells[i + y * tim.w] = cell;
if (y >= 0 && y < tim->h && w > 0) {
for (i32 i = MAX(x, 0); i < MIN(x + w, tim->w); i++) {
tim->cells[i + y * tim->w] = cell;
}
}
}
void tim_draw_col(TimCell cell, i32 x, i32 y, i32 h) {
if (x >= 0 && x < tim.w && h > 0) {
for (i32 i = MAX(y, 0); i < MIN(y + h, tim.h); i++) {
tim.cells[x + i * tim.w] = cell;
if (x >= 0 && x < tim->w && h > 0) {
for (i32 i = MAX(y, 0); i < MIN(y + h, tim->h); i++) {
tim->cells[x + i * tim->w] = cell;
}
}
}
void tim_draw_lot(TimCell cell, i32 x, i32 y, i32 w, i32 h) {
if (w > 0 && h > 0) {
for (i32 iy = MAX(y, 0); iy < MIN(y + h, tim.h); iy++) {
for (i32 ix = MAX(x, 0); ix < MIN(x + w, tim.w); ix++) {
tim.cells[ix + iy * tim.w] = cell;
for (i32 iy = MAX(y, 0); iy < MIN(y + h, tim->h); iy++) {
for (i32 ix = MAX(x, 0); ix < MIN(x + w, tim->w); ix++) {
tim->cells[ix + iy * tim->w] = cell;
}
}
}
}
void tim_draw_str(cstr s, i32 x, i32 y, i32 w, u8 fg, u8 bg) {
if (s && y >= 0 && x < tim.w && y < tim.h ) {
i32 end = MIN(x + w, tim.w);
if (s && y >= 0 && x < tim->w && y < tim->h ) {
i32 end = MIN(x + w, tim->w);
bool wide = false;
for (i32 i = 0; s[i] && x < end; x++) {
TimCell c = tim_cell(&s[i], fg, bg);
wide = wide || tim_utf8_is_wide_perhaps(c.buf, c.n);
if (x >= 0) {
c.wide = wide;
tim.cells[x + y * tim.w] = c;
tim->cells[x + y * tim->w] = c;
}
i += c.n;
}
@@ -75,11 +75,11 @@ void tim_draw_box(i32 x, i32 y, i32 w, i32 h, u8 fg, u8 bg) {
}
void tim_draw_invert(i32 x, i32 y, i32 w) {
if (y >= 0 && y < tim.h && w > 0) {
for (i32 i = MAX(x, 0); i < MIN(x + w, tim.w); i++) {
TimCell 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;
if (y >= 0 && y < tim->h && w > 0) {
for (i32 i = MAX(x, 0); i < MIN(x + w, tim->w); i++) {
TimCell 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;
}
}
}

View File

@@ -39,20 +39,20 @@ void edit_delete(TimEditState* e) {
static i32 edit_event(TimEditState* e, TimRect r) {
if (tim_is_mouse_click_over(r)) {
// take focus
tim.focus = e;
tim->focus = e;
return 0;
}
if (tim.focus != e || tim.event.type != TimEvent_Key) {
if (tim->focus != e || tim->event.type != TimEvent_Key) {
// not focused or no key press
return 0;
}
tim.event.type = TimEvent_Void; // consume event
tim->event.type = TimEvent_Void; // consume event
switch (tim.event.key) {
switch (tim->event.key) {
case TimKey_Escape:
case TimKey_Enter:
tim.focus = 0; // release focus
tim->focus = 0; // release focus
break;
case TimKey_Delete:
edit_delete(e);
@@ -76,21 +76,21 @@ static i32 edit_event(TimEditState* e, TimRect r) {
e->cursor = e->length;
break;
default:
if (tim.event.key >= ' ') {
edit_insert(e, tim.event.s);
if (tim->event.key >= ' ') {
edit_insert(e, tim->event.s);
}
break;
}
return tim.event.key;
return tim->event.key;
}
i32 edit(TimEditState* e, i32 x, i32 y, i32 w, u64 color) {
TimRect r = tim_scope_rect_to_absolute(x, y, w, 3);
if (tim.event.type == TimEvent_Draw) {
if (tim->event.type == TimEvent_Draw) {
tim_draw_box(r.x, r.y, r.w, r.h, color >> 16, color >> 8);
if (tim.focus == e) {
if (tim->focus == e) {
char* s = e->s + tim_utf8_pos(e->s, e->cursor - r.w + 4);
i32 cur = MIN(r.w - 4, e->cursor);
tim_draw_str(s, r.x + 2, r.y + 1, r.w - 3, color, color >> 8);

View File

@@ -1,7 +1,7 @@
#include "tim.h"
bool tim_is_event_key(TimEventType type, TimKey key) {
return tim.event.type == type && tim.event.key == key;
return tim->event.type == type && tim->event.key == key;
}
bool tim_is_key_press(TimKey key) {
@@ -9,8 +9,8 @@ bool tim_is_key_press(TimKey key) {
}
bool tim_is_mouse_over(TimRect r) {
i32 x = tim.event.x;
i32 y = tim.event.y;
i32 x = tim->event.x;
i32 y = tim->event.y;
return x >= r.x && x < r.x + r.w && y >= r.y && y < r.y + r.h;
}

View File

@@ -1,19 +1,37 @@
#include "tim.h"
// TODO: remove global variables
static TimCell _tim_cells[TIM_MAX_CELLS << TIM_ENABLE_DBUF];
static char _tim_buf[TIM_MAX_BUF];
TimState tim = {
.cells = _tim_cells,
.cells_double_buf = _tim_cells,
.buf = _tim_buf,
};
static void tim_render(void);
TimState* tim = NULL;
static void tim_init(void){
tim = (TimState*)malloc(sizeof(TimState));
memset(tim, 0, sizeof(TimState));
size_t cdb_size = (TIM_MAX_CELLS << TIM_ENABLE_DBUF);
tim->cells_double_buf = (TimCell*)malloc(cdb_size * sizeof(TimCell));
tim->cells = tim->cells_double_buf;
tim->buf = (char*)malloc(TIM_MAX_BUF);
}
static void tim_deinit(void){
if(!tim)
return;
free(tim->cells_double_buf);
free(tim->buf);
free(tim);
tim = NULL;
}
bool tim_run(f32 fps) {
if(tim == NULL){
tim_init();
atexit(tim_deinit);
}
i32 timeout = (fps > 0) ? (i32)(1000 / fps) : 0;
while (true) {
switch (tim.loop_stage) {
switch (tim->loop_stage) {
case 0:
// runs only once
tim_init_terminal();
@@ -21,31 +39,144 @@ bool tim_run(f32 fps) {
// fallthru
case 1:
// process input event
tim.start_us = tim_time_usec();
if (tim.event.type != TimEvent_Draw) {
tim->start_us = tim_time_usec();
if (tim->event.type != TimEvent_Draw) {
// reset focus on mouse click
if (tim_is_event_key(TimEvent_Mouse, TimKey_MouseButtonLeft)) {
tim.focus = 0;
tim->focus = 0;
}
tim.loop_stage = 2;
tim->loop_stage = 2;
return true;
}
// fallthru
case 2:
// process draw event
tim_clear_cells();
tim.event.type = TimEvent_Draw;
tim.loop_stage = 3;
tim->event.type = TimEvent_Draw;
tim->loop_stage = 3;
return true;
case 3:
// render screen and wait for next event
tim_render();
tim.render_us = tim_time_usec() - tim.start_us;
tim->render_us = tim_time_usec() - tim->start_us;
tim_read_event(timeout); // blocks
// fallthru
default:
tim.loop_stage = 1;
tim->loop_stage = 1;
break;
}
} // while
}
// write character to output buffer
static void tim_put_chr(char c) {
if (tim->buf_size + 1 < TIM_MAX_BUF) {
tim->buf[tim->buf_size] = c;
tim->buf_size += 1;
}
}
// write string to output buffer
static void tim_put_str(cstr s, i32 size) {
if (size > 0 && tim->buf_size + size < TIM_MAX_BUF) {
memmove(&tim->buf[tim->buf_size], s, size);
tim->buf_size += size;
}
}
// write integer as decimal string to output buffer
static void tim_put_int(i32 i) {
// optimized for small positive values, reduces load by a third
char* buf = &tim->buf[tim->buf_size];
if (tim->buf_size + 11 >= TIM_MAX_BUF) {
// not enough space for 32 bit integer
} else if ((u32)i < 10) {
buf[0] = '0' + i;
tim->buf_size += 1;
} else if ((u32)i < 100) {
buf[0] = '0' + i / 10;
buf[1] = '0' + i % 10;
tim->buf_size += 2;
} else if ((u32)i < 1000) {
buf[0] = '0' + i / 100;
buf[1] = '0' + (i / 10) % 10;
buf[2] = '0' + i % 10;
tim->buf_size += 3;
} else {
tim->buf_size += sprintf(buf, "%d", i);
}
}
static void tim_render(void) {
i32 fg = -1;
i32 bg = -1;
bool wide = false;
bool skip = false;
// screen buffers
TimCell* new_cells = tim->cells_double_buf;
TimCell* old_cells = tim->cells_double_buf;
#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 (i32 i = 0; i < tim->w * tim->h; i++) {
TimCell c = new_cells[i];
#if TIM_ENABLE_DBUF
// do nothing if cells in look-ahead are identical
const i32 la = 8; // look-ahead
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;
continue;
}
#endif
// Set cursor position after a new line, after a string containing wide
// characters or after skipping identical cells.
bool new_line = i % tim->w == 0;
bool wide_spill = wide && (c.n == 0 || c.buf[0] == ' ');
bool wide_flank = wide && !wide_spill && !c.wide;
if (new_line || wide_flank || skip) {
tim_put_str(S("\33["));
tim_put_int((i / tim->w) + 1);
tim_put_chr(';');
tim_put_int((i % tim->w) + 1);
tim_put_chr('H');
}
wide = c.wide || wide_spill;
skip = false;
// change foreground color
if (c.fg != fg) {
fg = c.fg;
tim_put_str(S("\33[38;5;"));
tim_put_int(fg);
tim_put_chr('m');
}
// change background color
if (c.bg != bg) {
bg = c.bg;
tim_put_str(S("\33[48;5;"));
tim_put_int(bg);
tim_put_chr('m');
}
// write character
if (c.n) {
tim_put_str((char*)c.buf, c.n);
} else {
tim_put_chr(' ');
}
}
// duration depends on connection and terminal rendering speed
tim_write_str(tim->buf, tim->buf_size);
tim->resized = false;
tim->frame += 1; // frame counter
tim->cells = old_cells; // swap buffer
}

View File

@@ -1,114 +0,0 @@
#include "tim.h"
// write character to output buffer
static void tim_put_chr(char c) {
if (tim.buf_size + 1 < TIM_MAX_BUF) {
tim.buf[tim.buf_size] = c;
tim.buf_size += 1;
}
}
// write string to output buffer
static void tim_put_str(cstr s, i32 size) {
if (size > 0 && tim.buf_size + size < TIM_MAX_BUF) {
memmove(&tim.buf[tim.buf_size], s, size);
tim.buf_size += size;
}
}
// write integer as decimal string to output buffer
static void tim_put_int(i32 i) {
// optimized for small positive values, reduces load by a third
char* buf = &tim.buf[tim.buf_size];
if (tim.buf_size + 11 >= TIM_MAX_BUF) {
// not enough space for 32 bit integer
} else if ((u32)i < 10) {
buf[0] = '0' + i;
tim.buf_size += 1;
} else if ((u32)i < 100) {
buf[0] = '0' + i / 10;
buf[1] = '0' + i % 10;
tim.buf_size += 2;
} else if ((u32)i < 1000) {
buf[0] = '0' + i / 100;
buf[1] = '0' + (i / 10) % 10;
buf[2] = '0' + i % 10;
tim.buf_size += 3;
} else {
tim.buf_size += sprintf(buf, "%d", i);
}
}
void tim_render(void) {
i32 fg = -1;
i32 bg = -1;
bool wide = false;
bool skip = false;
// screen buffers
TimCell* new_cells = tim.cells_double_buf;
TimCell* old_cells = tim.cells_double_buf;
#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 (i32 i = 0; i < tim.w * tim.h; i++) {
TimCell c = new_cells[i];
#if TIM_ENABLE_DBUF
// do nothing if cells in look-ahead are identical
const i32 la = 8; // look-ahead
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;
continue;
}
#endif
// Set cursor position after a new line, after a string containing wide
// characters or after skipping identical cells.
bool new_line = i % tim.w == 0;
bool wide_spill = wide && (c.n == 0 || c.buf[0] == ' ');
bool wide_flank = wide && !wide_spill && !c.wide;
if (new_line || wide_flank || skip) {
tim_put_str(S("\33["));
tim_put_int((i / tim.w) + 1);
tim_put_chr(';');
tim_put_int((i % tim.w) + 1);
tim_put_chr('H');
}
wide = c.wide || wide_spill;
skip = false;
// change foreground color
if (c.fg != fg) {
fg = c.fg;
tim_put_str(S("\33[38;5;"));
tim_put_int(fg);
tim_put_chr('m');
}
// change background color
if (c.bg != bg) {
bg = c.bg;
tim_put_str(S("\33[48;5;"));
tim_put_int(bg);
tim_put_chr('m');
}
// write character
if (c.n) {
tim_put_str((char*)c.buf, c.n);
} else {
tim_put_chr(' ');
}
}
// duration depends on connection and terminal rendering speed
tim_write_str(tim.buf, tim.buf_size);
tim.resized = false;
tim.frame += 1; // frame counter
tim.cells = old_cells; // swap buffer
}

View File

@@ -1,7 +1,7 @@
#include "tim.h"
TimRect tim_scope_rect_to_absolute(i32 x, i32 y, i32 w, i32 h) {
TimRect p = tim.scopes[tim.scope]; // parent scope
TimRect p = tim->scopes[tim->scope]; // parent scope
x = (x == A && w == A) ? 0 : x; // special cases
y = (y == A && h == A) ? 0 : y; //
@@ -35,16 +35,16 @@ TimRect tim_scope_rect_to_absolute(i32 x, i32 y, i32 w, i32 h) {
}
i32 tim_enter_scope(i32 x, i32 y, i32 w, i32 h) {
if (tim.scope + 1 >= TIM_MAX_SCOPE) {
if (tim->scope + 1 >= TIM_MAX_SCOPE) {
return 0;
}
TimRect r = tim_scope_rect_to_absolute(x, y, w, h);
tim.scope += 1;
tim.scopes[tim.scope] = r;
tim->scope += 1;
tim->scopes[tim->scope] = r;
return 1;
}
i32 tim_exit_scope(void) {
tim.scope -= (tim.scope > 0);
tim->scope -= (tim->scope > 0);
return 0;
}

View File

@@ -15,7 +15,7 @@ void tim_write_str(cstr s, i32 size) {
void signal_handler(i32 signal) {
// signals are written into a fifo pipe and read by event loop
ssize_t _ = write(tim.signal_pipe[1], &signal, sizeof(signal));
ssize_t _ = write(tim->signal_pipe[1], &signal, sizeof(signal));
(void)_; // remove unused-result warning
}
@@ -27,16 +27,16 @@ void tim_update_screen_size(void) {
}
i32 w = ws.ws_col;
i32 h = ws.ws_row;
tim.resized = (u32)(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;
tim->resized = (u32)(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;
}
}
void tim_init_terminal(void) {
tcgetattr(STDOUT_FILENO, &tim.attr); // store attributes
struct termios attr = tim.attr; //
tcgetattr(STDOUT_FILENO, &tim->attr); // store attributes
struct termios attr = tim->attr; //
cfmakeraw(&attr); // configure raw mode
tcsetattr(STDOUT_FILENO, TCSADRAIN, &attr); // set new attributes
tim_write_str(S("\33[?2004l")); // reset bracketed paste mode
@@ -46,14 +46,14 @@ void tim_init_terminal(void) {
tim_write_str(S("\33[?1002h")); // enable button events
tim_write_str(S("\33[?1006h")); // use mouse sgr protocol
tim_update_screen_size(); // get terminal size
i32 err = pipe(tim.signal_pipe); // create signal pipe
i32 err = pipe(tim->signal_pipe); // create signal pipe
if (!err) { //
signal(SIGWINCH, signal_handler); // terminal size changed
}
}
void tim_reset_terminal(void) {
tcsetattr(STDOUT_FILENO, TCSADRAIN, &tim.attr); // restore attributes
tcsetattr(STDOUT_FILENO, TCSADRAIN, &tim->attr); // restore attributes
tim_write_str(S("\33[?1000l")); // disable mouse
tim_write_str(S("\33[?1002l")); // disable mouse
tim_write_str(S("\33[m")); // reset colors
@@ -120,10 +120,10 @@ bool parse_input(event* restrict e, i32 n) {
}
void tim_read_event(i32 timeout_ms) {
event* e = &tim.event;
event* e = &tim->event;
struct pollfd pfd[2] = {
{.fd = tim.signal_pipe[0], .events = POLLIN},
{.fd = tim->signal_pipe[0], .events = POLLIN},
{.fd = STDIN_FILENO, .events = POLLIN},
};
@@ -143,7 +143,7 @@ void tim_read_event(i32 timeout_ms) {
if (pfd[0].revents & POLLIN) {
// received signal
i32 sig = 0;
i32 n = read(tim.signal_pipe[0], &sig, sizeof(sig));
i32 n = read(tim->signal_pipe[0], &sig, sizeof(sig));
if (n > 0 && sig == SIGWINCH) {
// screen size changed
e->type = TimEvent_Draw;

View File

@@ -1,14 +1,14 @@
#include "tim.h"
void tim_frame(i32 x, i32 y, i32 w, i32 h, u64 color) {
if (tim.event.type == TimEvent_Draw) {
if (tim->event.type == TimEvent_Draw) {
TimRect r = tim_scope_rect_to_absolute(x, y, w, h);
tim_draw_box(r.x, r.y, r.w, r.h, color, color >> 8);
}
}
void label(cstr s, i32 x, i32 y, i32 w, i32 h, u64 color) {
if (tim.event.type == TimEvent_Draw) {
if (tim->event.type == TimEvent_Draw) {
TimText t = tim_scan_str(s);
w = (w == A) ? t.width : w;
h = (h == A) ? t.lines : h;
@@ -28,7 +28,7 @@ bool button(cstr txt, i32 x, i32 y, i32 w, i32 h, u64 color) {
h = (h == A) ? 3 : h;
TimRect r = tim_scope_rect_to_absolute(x, y, w, h);
if (tim.event.type == TimEvent_Draw) {
if (tim->event.type == TimEvent_Draw) {
tim_draw_box(r.x, r.y, r.w, r.h, color >> 16, color >> 8);
tim_draw_str(txt, r.x + (w - tw) / 2, r.y + h / 2, w, color, color >> 8);
}
@@ -39,7 +39,7 @@ bool check(cstr txt, i32* state, i32 x, i32 y, i32 w, u64 color) {
w = (w == A) ? tim_utf8_len(txt) + 4 : w;
TimRect r = tim_scope_rect_to_absolute(x, y, w, 1);
if (tim.event.type == TimEvent_Draw) {
if (tim->event.type == TimEvent_Draw) {
cstr st = *state == -1 ? "-" : *state ? "x" : " ";
tim_draw_str("[ ] ", r.x, r.y, 4, color, color >> 8);
tim_draw_str(st, r.x + 1, r.y, 1, color >> 16, color >> 8);
@@ -56,7 +56,7 @@ bool radio(cstr txt, i32* state, i32 v, i32 x, i32 y, i32 w, u64 color) {
w = (w == A) ? tim_utf8_len(txt) + 4 : w;
TimRect r = tim_scope_rect_to_absolute(x, y, w, 1);
if (tim.event.type == TimEvent_Draw) {
if (tim->event.type == TimEvent_Draw) {
cstr st = *state == v ? "o" : " ";
tim_draw_str("( ) ", r.x, r.y, 4, color, color >> 8);
tim_draw_str(st, r.x + 1, r.y, 1, color >> 16, color >> 8);

View File

@@ -25,19 +25,19 @@ void tim_update_screen_size(void) {
}
i32 w = csbi.srWindow.Right - csbi.srWindow.Left + 1;
i32 h = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
tim.resized = (u32)(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;
tim.window = csbi.srWindow;
tim->resized = (u32)(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;
tim->window = csbi.srWindow;
}
}
void tim_init_terminal(void) {
DWORD mode = 0;
HANDLE hin = GetStdHandle(STD_INPUT_HANDLE);
GetConsoleMode(hin, &tim.mode_in); // get current input mode
mode = tim.mode_in; //
GetConsoleMode(hin, &tim->mode_in); // get current input mode
mode = tim->mode_in; //
mode &= ~ENABLE_ECHO_INPUT; // disable echo
mode &= ~ENABLE_LINE_INPUT; // disable line buffer
// TODO: enable ctrl-c again
@@ -49,14 +49,14 @@ void tim_init_terminal(void) {
SetConsoleMode(hin, mode); // set input mode
//
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE); //
GetConsoleMode(hout, &tim.mode_out); // get current output mode
mode = tim.mode_out; //
GetConsoleMode(hout, &tim->mode_out); // get current output mode
mode = tim->mode_out; //
mode |= ENABLE_PROCESSED_OUTPUT; // enable ascii sequences
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; // enable vt sequences
SetConsoleMode(hout, mode); // set output mode
//
tim.cp_in = GetConsoleCP(); // get current code page
tim.cp_out = GetConsoleOutputCP(); //
tim->cp_in = GetConsoleCP(); // get current code page
tim->cp_out = GetConsoleOutputCP(); //
SetConsoleCP(CP_UTF8); // set utf8 in/out code page
SetConsoleOutputCP(CP_UTF8); //
tim_write_str(S("\33[?1049h")); // use alternate buffer
@@ -69,14 +69,14 @@ void tim_reset_terminal(void) {
tim_write_str(S("\33[?1049l")); // exit alternate buffer
HANDLE hin = GetStdHandle(STD_INPUT_HANDLE); //
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE); //
SetConsoleMode(hin, tim.mode_in); // set original mode
SetConsoleMode(hout, tim.mode_out); //
SetConsoleCP(tim.cp_in); // set original code page
SetConsoleOutputCP(tim.cp_out); //
SetConsoleMode(hin, tim->mode_in); // set original mode
SetConsoleMode(hout, tim->mode_out); //
SetConsoleCP(tim->cp_in); // set original code page
SetConsoleOutputCP(tim->cp_out); //
}
void tim_read_event(i32 timeout_ms) {
TimEvent* e = &tim.event;
TimEvent* e = &tim->event;
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
const i8 key_table[256] = {
@@ -150,8 +150,8 @@ void tim_read_event(i32 timeout_ms) {
tim_update_screen_size(); // workaround, see WINDOW_BUFFER_SIZE_EVENT
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;
e->x = rec.Event.MouseEvent.dwMousePosition.X - tim->window.Left;
e->y = rec.Event.MouseEvent.dwMousePosition.Y - tim->window.Top;
return;
}

View File

@@ -21,9 +21,9 @@ static inline void test_screen(TimEvent* e) {
label("|", A, ~0, A, A, 0xf);
// some information
sprintf(buf, "screen: %dx%d", tim.w, tim.h);
sprintf(buf, "screen: %dx%d", tim->w, tim->h);
label(buf, 2, 0, A, A, 0xf);
sprintf(buf, "frame : [%c] %d", ": "[tim.frame & 1], tim.frame);
sprintf(buf, "frame : [%c] %d", ": "[tim->frame & 1], tim->frame);
label(buf, 2, 1, A, A, 0xf);
sprintf(buf, "key : [%d] %s", ke.key, ke.s + (ke.key < 32));
label(buf, 2, 2, A, A, 0xf);
@@ -34,12 +34,12 @@ static inline void test_screen(TimEvent* e) {
label(buf, 2, 4, A, A, 0xf);
// lower right
render_us += tim.render_us;
sprintf(buf, "%d µs (Ø %d µs)", tim.render_us, render_us / MAX(tim.frame, 1));
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 / TIM_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 / TIM_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
@@ -112,7 +112,7 @@ static inline void test_screen(TimEvent* e) {
i32 main(void) {
while (tim_run(1.5)) {
test_screen(&tim.event);
test_screen(&tim->event);
if (tim_is_key_press('q') || tim_is_key_press(TimKey_Escape)) {
break;
}