Files
tim/src/unix.c
2026-01-09 06:03:59 +05:00

166 lines
5.3 KiB
C
Executable File

#include "tim.h"
#ifdef TIM_UNIX
i64 tim_time_usec(void) {
struct timespec ts = {0};
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
}
void tim_write_str(cstr s, i32 size) {
ssize_t _ = write(STDOUT_FILENO, s, size);
(void)_; // remove unused-result warning
}
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));
(void)_; // remove unused-result warning
}
void tim_update_screen_size(void) {
struct winsize ws = {0};
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != 0) {
printf("ERROR: can't get console buffer size\n");
exit(1);
}
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;
}
}
void tim_init_terminal(void) {
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
tim_write_str(S("\33[?1049h")); // use alternate buffer
tim_write_str(S("\33[?25l")); // hide cursor
tim_write_str(S("\33[?1000h")); // enable mouse
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
if (!err) { //
signal(SIGWINCH, signal_handler); // terminal size changed
}
}
void tim_reset_terminal(void) {
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
tim_write_str(S("\33[?25h")); // show cursor
tim_write_str(S("\33[?1049l")); // exit alternate buffer
}
// parse input stored in e->s
bool parse_input(event* restrict e, i32 n) {
char* s = e->s;
if (n == 1 || s[0] != 27) {
// regular key press
e->type = TimEvent_Key;
e->key = s[0] == 127 ? TimKey_Backspace : tim_utf8_to_i32(s);
return true;
}
if (n >= 9 && !memcmp(s, S("\33[<"))) {
// sgr mouse sequence
e->type = TimEvent_Mouse;
i32 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 = TimKey_MouseButtonLeft;
return true;
}
return false;
}
struct {char s[4]; i32 k;} key_table[] = {
{"[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 (i32 i = 0; i < (i32)ARRAY_SIZE(key_table); i++) {
if (!memcmp(s + 1, key_table[i].s, n - 1)) {
e->type = TimEvent_Key;
e->key = key_table[i].k;
return true;
}
}
}
return false;
}
void tim_read_event(i32 timeout_ms) {
event* e = &tim->event;
struct pollfd pfd[2] = {
{.fd = tim->signal_pipe[0], .events = POLLIN},
{.fd = STDIN_FILENO, .events = POLLIN},
};
while (true) {
memset(e, 0, sizeof(*e));
i32 r = poll(pfd, 2, timeout_ms > 0 ? timeout_ms : -1);
if (r < 0) {
// poll error, EINTR or EAGAIN
continue;
} else if (r == 0) {
// poll timeout
e->type = TimEvent_Draw;
return;
}
if (pfd[0].revents & POLLIN) {
// received signal
i32 sig = 0;
i32 n = read(tim->signal_pipe[0], &sig, sizeof(sig));
if (n > 0 && sig == SIGWINCH) {
// screen size changed
e->type = TimEvent_Draw;
tim_update_screen_size();
return;
}
}
if (pfd[1].revents & POLLIN) {
// received input
i32 n = read(STDIN_FILENO, e->s, sizeof(e->s) - 1);
if (parse_input(e, n)) {
return;
}
}
} // while
}
#endif // TIM_UNIX