From 5c708df504e0a8dc7c49754a1741672791035666 Mon Sep 17 00:00:00 2001 From: Hadeed Ahmad Date: Fri, 25 Aug 2023 23:13:33 +0500 Subject: [PATCH] Add input buffering And refactor a lot of code --- kilo.c | 397 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 260 insertions(+), 137 deletions(-) diff --git a/kilo.c b/kilo.c index 2633caf..e4165b8 100644 --- a/kilo.c +++ b/kilo.c @@ -8,47 +8,64 @@ #include #include -struct abuf { - char *p; - int len; -}; +#define CTRL_KEY(key) ((key) & 0x1f) + +/*****************************************************************************/ struct { struct termios orig_termios; int rows; int cols; int cx, cy; -} E = { .cx = 0, .cy = 0 }; + struct input_buf *ib; +} E; enum keys { - ARROW_UP = 0x100 + 1, + HOME = 0x100 + 1, + DEL, + PG_UP, + PG_DOWN, + END, + ARROW_UP, ARROW_DOWN, ARROW_LEFT, - ARROW_RIGHT + ARROW_RIGHT, + NOP }; -#define ABUF_INIT {NULL, 0} +struct screen_buf; +struct input_buf; -void ab_append(struct abuf *, const char *); -void ab_free(struct abuf *); -void ab_write(struct abuf *); - -void enable_raw_mode(); -void disable_raw_mode(); - -int get_window_size(int *, int *); -int read_cursor_position(int *, int *); +/*****************************************************************************/ void editor_init(); void editor_redraw_screen(); -void editor_clear_screen(); -void editor_draw_rows(struct abuf *); +void editor_draw_rows(struct screen_buf *); void editor_process_key(); int editor_read_key(); +int term_enable_raw(); +void term_disable_raw(); +int term_clear(); +int term_cursor_hidden(bool); +int term_get_win_size(int *, int *); +int term_get_cursor_pos(int *, int *); +int term_set_cursor_pos(int, int); + +struct screen_buf *sb_init(); +void sb_append(struct screen_buf *, const char *); +void sb_free(struct screen_buf *); +void sb_write(struct screen_buf *); + +struct input_buf *ib_init(size_t); +int ib_read(struct input_buf *); +void ib_write(struct input_buf *, int); +bool ib_empty(struct input_buf *); +void ib_free(struct input_buf *); + void die(const char *); -#define CTRL_KEY(key) ((key) & 0x1f) +/*****************************************************************************/ int main() { editor_init(); @@ -61,110 +78,51 @@ int main() { return 0; } +/*****************************************************************************/ + void editor_init() { if (!isatty(STDIN_FILENO)) { printf("kilo only supports a terminal at standard in. Exiting."); exit(1); } - enable_raw_mode(); + if (term_enable_raw() == -1) die("term_enable_raw"); + if (term_get_win_size(&E.rows, &E.cols) == -1) die("term_get_win_size"); - if (get_window_size(&E.rows, &E.cols) == -1) die("get_window_size"); -} - -void enable_raw_mode() { - if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1) die("tcgetattr"); - atexit(disable_raw_mode); - - struct termios raw = E.orig_termios; - cfmakeraw(&raw); - - raw.c_cc[VMIN] = 0; - raw.c_cc[VTIME] = 1; - - if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) die("tcsetattr"); -} - -void disable_raw_mode() { - if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1) - die("tcsetattr"); -} - -int get_window_size(int *rows, int *cols) { - struct winsize ws; - - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col != 0) { - *rows = ws.ws_row; - *cols = ws.ws_col; - - return 0; - } else { - if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12) return -1; - - return read_cursor_position(rows, cols); - } - - return -1; -} - -int read_cursor_position(int *rows, int *cols) { - char buf[32]; - int i = 0; - - if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) return -1; - - if (read(STDIN_FILENO, buf, 1) != 1 && *buf != '\x1b') return -1; - if (read(STDIN_FILENO, buf, 1) != 1 && *buf != '[') return -1; - - do { - if (i == (int) sizeof(buf)) return -1; - if (read(STDIN_FILENO, buf+i, 1) == 0) break; - } while(buf[i++] != 'R'); - buf[i-1] = '\0'; - - if (sscanf(buf, "%d;%d", rows, cols) != 2) return -1; - - return 0; + E.cx = E.cy = 0; + E.ib = ib_init(128); } void editor_redraw_screen() { - struct abuf ab = ABUF_INIT; + if (term_cursor_hidden(true) == -1) die("term_cursor_hidden"); - ab_append(&ab, "\x1b[?25l"); - ab_append(&ab, "\x1b[H"); + struct screen_buf *sb = sb_init(); + editor_draw_rows(sb); + sb_write(sb); + sb_free(sb); - editor_draw_rows(&ab); - - char buf[32]; - snprintf(buf, sizeof(buf), "\x1b[%d;%dH", E.cy + 1, E.cx + 1); - ab_append(&ab, buf); - - ab_append(&ab, "\x1b[?25h"); - - ab_write(&ab); - ab_free(&ab); + if (term_set_cursor_pos(E.cy + 1, E.cx + 1) == -1) die("term_set_cursor_pos"); + if (term_cursor_hidden(false) == -1) die("term_cursor_hidden"); } -void editor_clear_screen() { - write(STDOUT_FILENO, "\x1b[2J", 4); - write(STDOUT_FILENO, "\x1b[H", 3); -} +void editor_draw_rows(struct screen_buf *sb) { + term_set_cursor_pos(1, 1); -void editor_draw_rows(struct abuf *ab) { for (int y = 0; y < E.rows; y++) { - ab_append(ab, "~"); + sb_append(sb, "~"); - ab_append(ab, "\x1b[K"); - if (y < E.rows - 1) ab_append(ab, "\r\n"); + sb_append(sb, "\x1b[K"); // Clear rest of the line + if (y < E.rows - 1) sb_append(sb, "\r\n"); } } + void editor_process_key() { int c = editor_read_key(); switch (c) { case CTRL_KEY('Q'): - editor_clear_screen(); + term_clear(); exit(0); break; @@ -180,63 +138,228 @@ void editor_process_key() { case ARROW_RIGHT: if (E.cx < E.cols - 1) E.cx++; break; - default: - printf("%d\r\n", c); - break; } } int editor_read_key() { + // TODO: There's better ways to do this. + // Somehow make everything go through the input buffer + if (!ib_empty(E.ib)) + return ib_read(E.ib); + char c; while (read(STDIN_FILENO, &c, 1) == 0); if (c == '\x1b') { - char buf[2]; + char buf[8]; - if (read(STDIN_FILENO, buf+0, 1) != 1) return c; - if (read(STDIN_FILENO, buf+1, 1) != 1) return c; - - if (buf[0] == '[') { - switch (buf[1]) { - case 'A': - return ARROW_UP; - break; - case 'B': - return ARROW_DOWN; - break; - case 'C': - return ARROW_RIGHT; - break; - case 'D': - return ARROW_LEFT; - break; + for (size_t i = 0; i < sizeof(buf); i++) { + if (read(STDIN_FILENO, buf+i, 1) == 0) { + buf[i] = '\0'; + break; } } + + char escape_char; + if (sscanf(buf, "[%c~", &escape_char) != EOF) { + switch (escape_char) { + case 'A': return ARROW_UP; + case 'B': return ARROW_DOWN; + case 'C': return ARROW_RIGHT; + case 'D': return ARROW_LEFT; + case 'H': return HOME; + case 'F': return END; + } + } + + int escape_int; + if (sscanf(buf, "[%d~", &escape_int) != EOF) { + switch (escape_int) { + case 1: + case 7: + return HOME; + case 3: + return DEL; + case 4: + case 8: + return END; + case 5: + return PG_UP; + case 6: + return PG_DOWN; + } + } + + return NOP; } return (int) c; } +/*****************************************************************************/ + +int term_enable_raw() { + if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1) return -1; + atexit(term_disable_raw); + + struct termios raw = E.orig_termios; + cfmakeraw(&raw); + + raw.c_cc[VMIN] = 0; + raw.c_cc[VTIME] = 1; + + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) return -1; + + return 0; +} + +void term_disable_raw() { + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1) + die ("term_disable_raw"); +} + +int term_clear() { + if (write(STDOUT_FILENO, "\x1b[2J", 4) != 4) return -1; + if (write(STDOUT_FILENO, "\x1b[H", 3) != 3) return -1; + return 0; + +} + +int term_cursor_hidden(bool hidden) { + if (write(STDOUT_FILENO, (hidden ? "\x1b[?25l" : "\x1b[?25h"), 6) != 6) + return -1; + + return 0; +} + +int term_get_win_size(int *row, int *col) { + struct winsize ws; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col != 0) { + *row = ws.ws_row; + *col = ws.ws_col; + + return 0; + } else { + if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12) return -1; + + return term_get_cursor_pos(row, col); + } +} + +int term_get_cursor_pos(int *row, int *col) { + char buf[32]; + + if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) return -1; + + for (size_t i = 0; i < sizeof(buf); i++) { + if (read(STDIN_FILENO, buf+i, 1) == 0 || buf[i] == 'R') { + buf[i] = '\0'; + break; + } + } + + if (sscanf(buf, "\x1b[%d;%d", row, col) == EOF) + return -1; + + return 0; +} + +int term_set_cursor_pos(int row, int col) { + char buf[16]; + + size_t len = snprintf(buf, sizeof(buf), "\x1b[%d;%dH", row, col); + len = (len >= sizeof(buf) ? sizeof(buf) - 1 : len); + + if (write(STDOUT_FILENO, buf, len) != (ssize_t) len) return -1; + return 0; +} + +/*****************************************************************************/ + +struct screen_buf { + char *s; + size_t size; +}; + +struct screen_buf *sb_init() { + struct screen_buf *sb = (struct screen_buf *) malloc(sizeof(struct screen_buf)); + + sb->s = NULL; + sb->size = 0; + + return sb; +} + +void sb_append(struct screen_buf *sb, const char *s) { + int size = strlen(s); + + sb->s = realloc(sb->s, sb->size + size); + memcpy(sb->s + sb->size, s, size); + + sb->size += size; +} + +void sb_write(struct screen_buf *sb) { + write(STDOUT_FILENO, sb->s, sb->size); +} + +void sb_free(struct screen_buf *sb) { + free(sb->s); + free(sb); +} + + +struct input_buf { + int *carr; // Circular array + int read_i; + int write_i; + size_t capacity; // Actual capacity is one less +}; + +struct input_buf *ib_init(size_t capacity) { + struct input_buf *ib = (struct input_buf *) malloc(sizeof(struct input_buf)); + + ib->carr = (int *) malloc(sizeof(int) * capacity); + ib->write_i = 0; + ib->read_i = 0; + ib->capacity = capacity; + + return ib; +} + +void ib_write(struct input_buf *ib, int key) { + if ((ib->write_i + 1) % (int) ib->capacity == ib->read_i) // Buffer full + die("ib_write"); + + ib->carr[ib->write_i] = key; + ib->write_i = (ib->write_i + 1) % ib->capacity; +} + +int ib_read(struct input_buf *ib) { + if (ib_empty(ib)) die("ib_read"); + + int key = ib->carr[ib->read_i]; + ib->read_i = (ib->read_i + 1) % ib->capacity; + + return key; +} + +bool ib_empty(struct input_buf *ib) { + return ib->write_i == ib->read_i; +} + +void ib_free(struct input_buf *ib) { + free(ib->carr); + free(ib); +} + +/*****************************************************************************/ + void die(const char *s) { - editor_clear_screen(); + term_clear(); perror(s); exit(1); } -void ab_append(struct abuf *ab, const char *s) { - int len = strlen(s); - - ab->p = realloc(ab->p, ab->len + len); - memcpy(ab->p + ab->len, s, len); - - ab->len += len; -} - -void ab_write(struct abuf *ab) { - write(STDOUT_FILENO, ab->p, ab->len); -} - -void ab_free(struct abuf *ab) { - free(ab->p); -}