Rearrange code and cleanup

This commit is contained in:
2025-12-30 01:19:50 +00:00
parent 04179d1a4e
commit 235eafb01c
51 changed files with 2608 additions and 2646 deletions
+264
View File
@@ -0,0 +1,264 @@
#include "io/ui.h"
static Queue<char> input_queue;
int get_utf8_seq_len(uint8_t byte) {
if ((byte & 0x80) == 0x00)
return 1;
if ((byte & 0xE0) == 0xC0)
return 2;
if ((byte & 0xF0) == 0xE0)
return 3;
if ((byte & 0xF8) == 0xF0)
return 4;
return 1;
}
int get_next_byte(char *out) {
if (!input_queue.empty()) {
input_queue.pop(*out);
return 1;
}
int n = read(STDIN_FILENO, out, 1);
return (n > 0) ? 1 : 0;
}
void enqueue_bytes(const char *bytes, int len) {
for (int i = 0; i < len; i++)
input_queue.push(bytes[i]);
}
int read_input(char *&buf) {
size_t cap = 16;
buf = (char *)calloc(cap, sizeof(char));
size_t len = 0;
char header;
if (!get_next_byte(&header)) {
free(buf);
return 0;
}
if (header == '\x1b') {
buf[len++] = header;
while (len < 6) {
char next_c;
if (!get_next_byte(&next_c))
break;
buf[len++] = next_c;
}
return len;
}
int seq_len = get_utf8_seq_len((uint8_t)header);
buf[len++] = header;
if (seq_len == 1)
return len;
for (int i = 1; i < seq_len; i++) {
char next_c;
if (!get_next_byte(&next_c)) {
enqueue_bytes(buf, len);
free(buf);
return 0;
}
buf[len++] = next_c;
}
uint_least32_t current_cp, prev_cp;
grapheme_decode_utf8(buf, len, &prev_cp);
uint_least16_t state = 0;
while (true) {
char next_header;
if (!get_next_byte(&next_header))
break;
int next_seq_len = get_utf8_seq_len((uint8_t)next_header);
char temp_seq[5];
temp_seq[0] = next_header;
int temp_len = 1;
bool complete_seq = true;
for (int i = 1; i < next_seq_len; i++) {
char c;
if (!get_next_byte(&c)) {
complete_seq = false;
break;
}
temp_seq[temp_len++] = c;
}
if (!complete_seq) {
enqueue_bytes(temp_seq, temp_len);
break;
}
grapheme_decode_utf8(temp_seq, temp_len, &current_cp);
if (grapheme_is_character_break(prev_cp, current_cp, &state)) {
enqueue_bytes(temp_seq, temp_len);
break;
} else {
if (len + temp_len + 1 >= cap) {
cap *= 2;
buf = (char *)realloc(buf, cap);
}
memcpy(buf + len, temp_seq, temp_len);
len += temp_len;
prev_cp = current_cp;
}
}
buf[len] = '\0';
return len;
}
void capture_mouse(char *buf, KeyEvent *ret) {
uint8_t byte = buf[3];
ret->mouse_modifier = (byte >> 3) & 0x03;
uint8_t aa = (byte >> 5) & 0x03;
uint8_t cc = byte & 0x03;
ret->mouse_x = buf[4] - 33;
ret->mouse_y = buf[5] - 33;
ret->mouse_direction = 4;
if (aa == 1 && cc == 3) {
ret->mouse_state = RELEASE;
ret->mouse_button = NONE_BTN;
} else if (aa == 1) {
ret->mouse_state = PRESS;
ret->mouse_button = cc;
} else if (aa == 2) {
ret->mouse_state = DRAG;
ret->mouse_button = cc;
} else if (aa == 3) {
ret->mouse_button = SCROLL_BTN;
ret->mouse_state = SCROLL;
ret->mouse_direction = cc;
} else {
ret->mouse_state = RELEASE;
ret->mouse_button = NONE_BTN;
}
}
bool read_bracketed_paste(char **out, int *out_len) {
size_t cap = 256, len = 0;
char *buf = (char *)malloc(cap);
if (!buf)
return false;
char window[6] = {0};
size_t wlen = 0;
auto push_byte = [&](char c) {
if (len + 1 >= cap) {
cap *= 2;
buf = (char *)realloc(buf, cap);
}
buf[len++] = c;
};
while (true) {
char c;
if (!get_next_byte(&c)) {
free(buf);
return false;
}
if (wlen < 5) {
window[wlen++] = c;
} else {
memmove(window, window + 1, 4);
window[4] = c;
wlen = 5;
}
if (wlen == 5 && window[0] == '\x1b' && window[1] == '[' &&
window[2] == '2' && window[3] == '0' && window[4] == '1') {
char tilde;
if (!get_next_byte(&tilde)) {
free(buf);
return false;
}
if (tilde == '~')
break;
for (int i = 0; i < 5; i++)
push_byte(window[i]);
push_byte(tilde);
wlen = 0;
continue;
}
if (wlen == 5) {
push_byte(window[0]);
memmove(window, window + 1, 4);
wlen = 4;
}
}
buf[len] = '\0';
*out = buf;
*out_len = (int)len;
return true;
}
KeyEvent read_key() {
KeyEvent ret;
char *buf;
int n = read_input(buf);
if (n <= 0) {
ret.key_type = KEY_NONE;
return ret;
}
if (n >= 6 && buf[0] == '\x1b' && buf[1] == '[' && buf[2] == '2' &&
buf[3] == '0' && buf[4] == '0' && buf[5] == '~') {
char *pbuf = nullptr;
int plen = 0;
if (read_bracketed_paste(&pbuf, &plen)) {
free(buf);
ret.key_type = KEY_PASTE;
ret.c = pbuf;
ret.len = plen;
return ret;
}
} else if (n >= 3 && buf[0] == '\x1b' && buf[1] == '[' && buf[2] == 'M') {
ret.key_type = KEY_MOUSE;
capture_mouse(buf, &ret);
} else if (n >= 2 && buf[0] == '\x1b' && buf[1] == '[') {
ret.key_type = KEY_SPECIAL;
int using_modifiers = buf[3] == ';';
int pos;
if (!using_modifiers) {
pos = 2;
ret.special_modifier = 0;
} else {
pos = 5;
switch (buf[4]) {
case '2':
ret.special_modifier = SHIFT;
break;
case '3':
ret.special_modifier = ALT;
break;
case '5':
ret.special_modifier = CNTRL;
break;
case '7':
ret.special_modifier = CNTRL_ALT;
break;
default:
ret.special_modifier = 0;
break;
}
}
switch (buf[pos]) {
case 'A':
ret.special_key = KEY_UP;
break;
case 'B':
ret.special_key = KEY_DOWN;
break;
case 'C':
ret.special_key = KEY_RIGHT;
break;
case 'D':
ret.special_key = KEY_LEFT;
break;
case '3':
ret.special_key = KEY_DELETE;
break;
default:
ret.special_key = 99;
break;
}
} else if (n > 0) {
ret.key_type = KEY_CHAR;
ret.c = buf;
ret.len = n;
return ret;
}
if (n > 0)
free(buf);
return ret;
}
+1005
View File
File diff suppressed because it is too large Load Diff
+289
View File
@@ -0,0 +1,289 @@
#include "io/ui.h"
uint32_t rows, cols;
bool show_cursor = 0;
std::vector<ScreenCell> screen;
std::vector<ScreenCell> old_screen;
std::mutex screen_mutex;
termios orig_termios;
void disable_raw_mode() {
std::string os = "\x1b[?1049l\x1b[2 q\x1b[?1002l\x1b[?25h\x1b[?2004l";
write(STDOUT_FILENO, os.c_str(), os.size());
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios) == -1) {
perror("tcsetattr");
exit(EXIT_FAILURE);
}
}
void enable_raw_mode() {
if (tcgetattr(STDIN_FILENO, &orig_termios) == -1)
exit(EXIT_FAILURE);
atexit(disable_raw_mode);
struct termios raw = orig_termios;
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_oflag &= ~(OPOST);
raw.c_cflag |= (CS8);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
raw.c_cc[VMIN] = 0;
raw.c_cc[VTIME] = 0;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
exit(EXIT_FAILURE);
std::string os = "\x1b[?1049h\x1b[2 q\x1b[?1002h\x1b[?25l\x1b[?2004h";
write(STDOUT_FILENO, os.c_str(), os.size());
}
Coord start_screen() {
enable_raw_mode();
struct winsize w;
ioctl(0, TIOCGWINSZ, &w);
rows = w.ws_row;
cols = w.ws_col;
screen.assign(rows * cols, {});
old_screen.assign(rows * cols, {});
return {rows, cols};
}
void end_screen() { disable_raw_mode(); }
Coord get_size() { return {rows, cols}; }
void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg,
uint32_t bg, uint8_t flags) {
if (row >= rows || col >= cols)
return;
uint32_t idx = row * cols + col;
std::lock_guard<std::mutex> lock(screen_mutex);
screen[idx].utf8 = utf8 != "" ? utf8 : "";
if (utf8 == "")
return;
screen[idx].width = display_width(utf8.c_str(), utf8.size());
screen[idx].fg = fg;
screen[idx].bg = bg;
screen[idx].flags = flags;
screen[idx].ul_color = 0;
}
void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg,
uint32_t bg, uint8_t flags) {
if (row >= rows || col >= cols)
return;
uint32_t idx = row * cols + col;
std::lock_guard<std::mutex> lock(screen_mutex);
screen[idx].utf8 = utf8 ? utf8 : "";
if (utf8 == nullptr)
return;
screen[idx].width = display_width(utf8, strlen(utf8));
screen[idx].fg = fg;
screen[idx].bg = bg;
screen[idx].flags = flags;
screen[idx].ul_color = 0;
}
void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg,
uint32_t bg, uint8_t flags, uint32_t ul_color) {
if (row >= rows || col >= cols)
return;
uint32_t idx = row * cols + col;
std::lock_guard<std::mutex> lock(screen_mutex);
screen[idx].utf8 = utf8 != "" ? utf8 : "";
if (utf8 == "")
return;
screen[idx].width = display_width(utf8.c_str(), utf8.size());
screen[idx].fg = fg;
screen[idx].bg = bg;
screen[idx].flags = flags;
screen[idx].ul_color = ul_color;
}
void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg,
uint32_t bg, uint8_t flags, uint32_t ul_color) {
if (row >= rows || col >= cols)
return;
uint32_t idx = row * cols + col;
std::lock_guard<std::mutex> lock(screen_mutex);
screen[idx].utf8 = utf8 ? utf8 : "";
if (utf8 == nullptr)
return;
screen[idx].width = display_width(utf8, strlen(utf8));
screen[idx].fg = fg;
screen[idx].bg = bg;
screen[idx].flags = flags;
screen[idx].ul_color = ul_color;
}
inline bool is_empty_cell(const ScreenCell &c) {
return c.utf8.empty() || c.utf8 == " " || c.utf8 == "\x1b";
}
void render() {
static bool first_render = true;
uint32_t current_fg = 0;
uint32_t current_bg = 0;
uint32_t current_ul_color = 0;
bool current_italic = false;
bool current_bold = false;
bool current_underline = false;
std::lock_guard<std::mutex> lock(screen_mutex);
std::string out;
out.reserve(static_cast<size_t>(rows) * static_cast<size_t>(cols) * 4 + 256);
out += "\x1b[s\x1b[?25l";
if (first_render) {
out += "\x1b[2J\x1b[H";
first_render = false;
}
for (uint32_t row = 0; row < rows; ++row) {
int first_change_col = -1;
int last_change_col = -1;
for (uint32_t col = 0; col < cols; ++col) {
uint32_t idx = row * cols + col;
ScreenCell &old_cell = old_screen[idx];
ScreenCell &new_cell = screen[idx];
bool content_changed =
old_cell.utf8 != new_cell.utf8 || old_cell.fg != new_cell.fg ||
old_cell.bg != new_cell.bg || old_cell.flags != new_cell.flags ||
old_cell.ul_color != new_cell.ul_color;
if (content_changed) {
if (first_change_col == -1) {
first_change_col = col;
if (first_change_col > 0) {
for (int back = 1; back <= 3 && first_change_col - back >= 0;
++back) {
ScreenCell &prev_cell =
screen[row * cols + (first_change_col - back)];
if (prev_cell.width > 1) {
first_change_col -= back;
break;
}
}
}
}
last_change_col = col;
}
}
if (first_change_col == -1)
continue;
char buf[64];
snprintf(buf, sizeof(buf), "\x1b[%d;%dH", row + 1, first_change_col + 1);
out.append(buf);
for (int col = first_change_col; col <= last_change_col; ++col) {
int idx = row * cols + col;
ScreenCell &old_cell = old_screen[idx];
ScreenCell &new_cell = screen[idx];
int width = new_cell.width > 0 ? new_cell.width : 1;
bool overlap = false;
if (width > 1) {
for (int i = 1; i < width; ++i) {
int next_col = col + i;
if (next_col >= cols)
break;
const ScreenCell &next = screen[row * cols + next_col];
if (!is_empty_cell(next)) {
overlap = true;
break;
}
}
}
if (current_fg != new_cell.fg) {
if (new_cell.fg) {
char fb[64];
snprintf(fb, sizeof(fb), "\x1b[38;2;%d;%d;%dm",
(new_cell.fg >> 16) & 0xFF, (new_cell.fg >> 8) & 0xFF,
(new_cell.fg >> 0) & 0xFF);
out.append(fb);
} else {
out += "\x1b[39m";
}
current_fg = new_cell.fg;
}
if (current_bg != new_cell.bg) {
if (new_cell.bg) {
char bb[64];
snprintf(bb, sizeof(bb), "\x1b[48;2;%d;%d;%dm",
(new_cell.bg >> 16) & 0xFF, (new_cell.bg >> 8) & 0xFF,
(new_cell.bg >> 0) & 0xFF);
out.append(bb);
} else {
out += "\x1b[49m";
}
current_bg = new_cell.bg;
}
bool italic = (new_cell.flags & CF_ITALIC) != 0;
if (italic != current_italic) {
out += italic ? "\x1b[3m" : "\x1b[23m";
current_italic = italic;
}
bool bold = (new_cell.flags & CF_BOLD) != 0;
if (bold != current_bold) {
out += bold ? "\x1b[1m" : "\x1b[22m";
current_bold = bold;
}
bool underline = (new_cell.flags & CF_UNDERLINE) != 0;
if (underline) {
if (new_cell.ul_color != current_ul_color) {
if (new_cell.ul_color) {
char ubuf[64];
snprintf(ubuf, sizeof(ubuf), "\x1b[58;2;%d;%d;%dm",
(new_cell.ul_color >> 16) & 0xFF,
(new_cell.ul_color >> 8) & 0xFF,
(new_cell.ul_color >> 0) & 0xFF);
out.append(ubuf);
} else {
out += "\x1b[59m";
}
current_ul_color = new_cell.ul_color;
}
}
if (underline != current_underline) {
out += underline ? "\x1b[4m" : "\x1b[24m";
current_underline = underline;
}
if (width > 1 && overlap) {
for (int i = 1; i < width; ++i)
out.push_back(' ');
} else {
if (!new_cell.utf8.empty()) {
if (new_cell.utf8[0] == '\t')
out.append(" ");
else if (new_cell.utf8[0] != '\x1b')
out.append(new_cell.utf8);
} else {
out.push_back(' ');
}
}
old_cell.utf8 = new_cell.utf8;
old_cell.fg = new_cell.fg;
old_cell.bg = new_cell.bg;
old_cell.flags = new_cell.flags;
old_cell.width = new_cell.width;
}
}
out += "\x1b[0m";
out += "\x1b[u";
if (show_cursor)
out += "\x1b[?25h";
const char *ptr = out.data();
size_t remaining = out.size();
while (remaining > 0) {
ssize_t written = write(STDOUT_FILENO, ptr, remaining);
if (written == 0) {
break;
} else if (written == -1) {
if (errno == EINTR)
continue;
exit(EXIT_FAILURE);
break;
} else {
ptr += written;
remaining -= written;
}
}
}
void set_cursor(int row, int col, int type, bool show_cursor_param) {
char buf[32];
int n = snprintf(buf, sizeof(buf), "\x1b[%d;%dH\x1b[%d q", row + 1, col + 1,
type);
show_cursor = show_cursor_param;
write(STDOUT_FILENO, buf, n);
}