166 lines
5.3 KiB
C
Executable File
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
|