diff --git a/Makefile b/Makefile index 5ab6f56..d8b498d 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ -CCFLAGS := -Wall -Wextra -Wpedantic -Werror -std=c99 -D_DEFAULT_SOURCE +CFLAGS := -Wall -Wextra -Wpedantic -Werror -std=c99 kilo: kilo.c - $(CC) $(CCFLAGS) kilo.c -o kilo + $(CC) $(CFLAGS) kilo.c -o kilo clean: rm -f ./kilo diff --git a/kilo.c b/kilo.c index e4165b8..c05c5ba 100644 --- a/kilo.c +++ b/kilo.c @@ -1,6 +1,11 @@ +#define _BSD_SOURCE +#define _DEFAULT_SOURCE +#define _GNU_SOURCE + #include #include #include +#include #include #include #include @@ -12,12 +17,17 @@ /*****************************************************************************/ +struct screen_buf; +struct input_buf; + +typedef uint16_t KEY; + struct { struct termios orig_termios; - int rows; - int cols; - int cx, cy; struct input_buf *ib; + int screenrows, screencols; + int cx, cy; + bool running; } E; enum keys { @@ -33,16 +43,24 @@ enum keys { NOP }; -struct screen_buf; -struct input_buf; - /*****************************************************************************/ void editor_init(); void editor_redraw_screen(); void editor_draw_rows(struct screen_buf *); void editor_process_key(); -int editor_read_key(); +void editor_free(); + +struct screen_buf *sb_init(); +void sb_append(struct screen_buf *, const char *); +void sb_write(struct screen_buf *); +void sb_free(struct screen_buf *); + +struct input_buf *ib_init(size_t); +KEY ib_read(struct input_buf *); +void ib_write(struct input_buf *, KEY); +bool ib_empty(struct input_buf *); +void ib_free(struct input_buf *); int term_enable_raw(); void term_disable_raw(); @@ -51,17 +69,8 @@ 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); +KEY term_read_key(); -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 *); @@ -70,11 +79,12 @@ void die(const char *); int main() { editor_init(); - while (1) { + while (E.running) { editor_redraw_screen(); editor_process_key(); } + editor_free(); return 0; } @@ -87,10 +97,12 @@ void editor_init() { } 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 (term_get_win_size(&E.screenrows, &E.screencols) == -1) die("term_get_win_size"); E.cx = E.cy = 0; E.ib = ib_init(128); + E.running = true; } void editor_redraw_screen() { @@ -108,92 +120,133 @@ void editor_redraw_screen() { void editor_draw_rows(struct screen_buf *sb) { term_set_cursor_pos(1, 1); - for (int y = 0; y < E.rows; y++) { + for (int y = 0; y < E.screenrows; y++) { sb_append(sb, "~"); sb_append(sb, "\x1b[K"); // Clear rest of the line - if (y < E.rows - 1) sb_append(sb, "\r\n"); + if (y < E.screenrows - 1) sb_append(sb, "\r\n"); } } void editor_process_key() { - int c = editor_read_key(); + KEY c = ib_read(E.ib); switch (c) { - case CTRL_KEY('Q'): - term_clear(); - exit(0); - break; - case ARROW_LEFT: if (E.cx > 0) E.cx--; break; case ARROW_DOWN: - if (E.cy < E.rows - 1) E.cy++; + if (E.cy < E.screenrows - 1) E.cy++; break; case ARROW_UP: if (E.cy > 0) E.cy--; break; case ARROW_RIGHT: - if (E.cx < E.cols - 1) E.cx++; + if (E.cx < E.screencols - 1) E.cx++; + break; + + case HOME: + E.cx = 0; + break; + case END: + E.cx = E.screencols - 1; + break; + + case PG_UP: + case PG_DOWN: + for (int i = 0; i < E.screenrows; i++) + ib_write(E.ib, (c == PG_UP ? ARROW_UP : ARROW_DOWN)); + break; + + case CTRL_KEY('Q'): + E.running = false; 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); +void editor_free() { + term_clear(); + ib_free(E.ib); +} - char c; - while (read(STDIN_FILENO, &c, 1) == 0); +/*****************************************************************************/ - if (c == '\x1b') { - char buf[8]; +struct screen_buf { + char *s; + size_t size; +}; - for (size_t i = 0; i < sizeof(buf); i++) { - if (read(STDIN_FILENO, buf+i, 1) == 0) { - buf[i] = '\0'; - break; - } - } +struct screen_buf *sb_init() { + struct screen_buf *sb = (struct screen_buf *) malloc(sizeof(struct screen_buf)); - 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; - } - } + sb->s = NULL; + sb->size = 0; - 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 sb; +} - return NOP; - } +void sb_append(struct screen_buf *sb, const char *s) { + int size = strlen(s); - return (int) c; + 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 { + KEY *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 = (KEY *) malloc(sizeof(KEY) * capacity); + ib->write_i = 0; + ib->read_i = 0; + ib->capacity = capacity; + + return ib; +} + +void ib_write(struct input_buf *ib, KEY 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; +} + +KEY ib_read(struct input_buf *ib) { + if (ib_empty(ib)) ib_write(ib, term_read_key()); + + KEY 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); } /*****************************************************************************/ @@ -206,7 +259,7 @@ int term_enable_raw() { cfmakeraw(&raw); raw.c_cc[VMIN] = 0; - raw.c_cc[VTIME] = 1; + raw.c_cc[VTIME] = 10; if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) return -1; @@ -275,83 +328,52 @@ int term_set_cursor_pos(int row, int col) { return 0; } -/*****************************************************************************/ +KEY term_read_key() { + char c; + while (read(STDIN_FILENO, &c, 1) == 0); -struct screen_buf { - char *s; - size_t size; -}; + if (c == '\x1b') { + char buf[8] = { '\0' }; -struct screen_buf *sb_init() { - struct screen_buf *sb = (struct screen_buf *) malloc(sizeof(struct screen_buf)); + // Inefficient but I like the way it looks + for (size_t i = 0; i < sizeof(buf); i++) { + if (read(STDIN_FILENO, buf+i, 1) == 0) break; - sb->s = NULL; - sb->size = 0; + 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; + } + } - return sb; -} + 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; + } + } + } -void sb_append(struct screen_buf *sb, const char *s) { - int size = strlen(s); + return NOP; + } - 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); + return (KEY) c; } /*****************************************************************************/