Last active
January 22, 2022 23:08
-
-
Save jstaursky/ee5bf5fd41273717f18292bed099fb1b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// REQUIRES libuv | |
#include <assert.h> | |
#include <iostream> | |
#include <signal.h> | |
#include <stdbool.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <uv.h> | |
#include <string> | |
#include <string.h> | |
#include <atomic> | |
#define create_cursor_mover(anscii) [](uv_buf_t* dst) -> void { \ | |
std::string s{anscii}; \ | |
dst->base = new char[s.length() + 1]; \ | |
dst->len = s.length() + 1; \ | |
memcpy(dst->base, s.c_str(), dst->len); \ | |
} | |
// ascii control codes. | |
#define UP "\033[1A" | |
#define DOWN "\033[1B" | |
#define LEFT "\033[1D" | |
#define RIGHT "\033[1C" | |
auto move_cursor_up = create_cursor_mover (UP); | |
auto move_cursor_down = create_cursor_mover (DOWN); | |
auto move_cursor_left = create_cursor_mover (LEFT); | |
auto move_cursor_right = create_cursor_mover (RIGHT); | |
std::atomic<bool> end(false); | |
uv_loop_t* loop; | |
uv_tty_t tty; | |
uv_tty_t tty_out; | |
uv_signal_t sigterm; | |
uv_write_t write_req; | |
uv_buf_t write_buf = { .base = NULL, .len = 0 }; | |
uint64_t start = uv_hrtime() / 1e9; | |
void stop() | |
{ | |
int stop_ret = uv_read_stop ((uv_stream_t*)&tty); | |
int sigstop_ret = uv_signal_stop (&sigterm); | |
} | |
void writer_cleanup (uv_write_t* req, int status) | |
{ | |
delete[] write_buf.base; | |
write_buf.base = nullptr; | |
write_buf.len = 0; | |
} | |
void reader_preconf (uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) | |
{ | |
buf->base = new char[suggested_size]; | |
buf->len = suggested_size; | |
} | |
void reader_cb (uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) | |
{ | |
if (nread < 0) { | |
end = true; | |
goto cleanup; | |
} | |
for (ssize_t i = 0; i < nread; i++) { | |
switch (buf->base[i]) { | |
case 'h': | |
move_cursor_left (&write_buf); | |
break; | |
case 'j': | |
move_cursor_down (&write_buf); | |
break; | |
case 'k': | |
move_cursor_up (&write_buf); | |
break; | |
case 'l': | |
move_cursor_right (&write_buf); | |
break; | |
default: | |
break; | |
} | |
// ctrl-c | |
if (buf->base[i] == (0xf & 'c')) { | |
end = true; | |
} | |
} | |
// Commit write_buf edits to tty. | |
uv_write (&write_req, (uv_stream_t*)&tty_out, &write_buf, 1, &writer_cleanup); | |
cleanup: | |
if (buf->base && buf->len > 0) | |
delete[] buf->base; | |
if (end.load()) | |
stop(); | |
} | |
void signal_cb (uv_signal_t* sig, int signum) | |
{ | |
stop(); | |
} | |
void walk_and_close_cb (uv_handle_t* handle, void* arg) | |
{ | |
if (!uv_is_closing (handle)) { | |
uv_close (handle, NULL); | |
} | |
} | |
void print_counter (void* arg) | |
{ | |
while(!end.load()) { | |
uint64_t count = (uv_hrtime() / 1e9) - start; | |
printf("%ld\n", count); | |
uv_sleep(1000); | |
} | |
} | |
int main (int argc, char *argv[]) | |
{ | |
loop = uv_default_loop(); | |
uv_tty_init (loop, &tty, fileno (stdin), 0); | |
uv_tty_init (loop, &tty_out, fileno (stdout), 0); | |
uv_signal_init (loop, &sigterm); | |
uv_tty_set_mode (&tty, UV_TTY_MODE_RAW); | |
uv_read_start ((uv_stream_t*)&tty, &reader_preconf, &reader_cb); | |
uv_signal_start (&sigterm, signal_cb, SIGTERM); | |
uv_thread_t tid; | |
uv_thread_create(&tid, print_counter, nullptr); | |
uv_run (loop, UV_RUN_DEFAULT); | |
uv_tty_reset_mode(); | |
uv_walk (loop, &walk_and_close_cb, NULL); | |
uv_run (loop, UV_RUN_DEFAULT); | |
uv_thread_join(&tid); | |
uv_loop_close (loop); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment