diff --git a/kilo.c b/kilo.c index 35a492e..28e9175 100644 --- a/kilo.c +++ b/kilo.c @@ -10,11 +10,28 @@ #include /*** defines ***/ + +#define KILO_VERSION "0.0.1" + #define CTRL_KEY(k) ((k) & 0x1f) +enum editorKey { + ARROW_LEFT = 1000, + ARROW_RIGHT, + ARROW_UP, + ARROW_DOWN, + DEL_KEY, + HOME_KEY, + END_KEY, + PAGE_UP, + PAGE_DOWN +}; + + /*** data ***/ struct editorConfig { + int cx, cy; int screenrows; int screencols; struct termios orig_termios; @@ -53,13 +70,53 @@ void enableRawMode() { die("tcsetattr"); } -char editorReadKey() { +int editorReadKey() { int nread; char c; while ((nread = read(STDIN_FILENO, &c, 1)) != 1) { if (nread == -1 && errno != EAGAIN) die("read"); } - return c; + + // Process escape sequences + if (c == '\x1b') { + char seq[3]; + + if (read(STDIN_FILENO, &seq[0], 1) != 1) return '\x1b'; + if (read(STDIN_FILENO, &seq[1], 1) != 1) return '\x1b'; + + if (seq[0] == '[') { + if (seq[1] >= '0' && seq[1] <= '9') { + if (read(STDIN_FILENO, &seq[2], 1) != 1) return '\x1b'; + if (seq[2] == '~') { + switch (seq[1]) { + case '1': return HOME_KEY; + case '3': return DEL_KEY; + case '4': return END_KEY; + case '5': return PAGE_UP; + case '6': return PAGE_DOWN; + case '7': return HOME_KEY; + case '8': return END_KEY; + } + } + } else { + switch (seq[1]) { + case 'A': return ARROW_UP; + case 'B': return ARROW_DOWN; + case 'C': return ARROW_RIGHT; + case 'D': return ARROW_LEFT; + } + } + } else if (seq[0] == '0') { + switch (seq[1]) { + case 'H': return HOME_KEY; + case 'F': return END_KEY; + } + } + + return '\x1b'; + } else { + return c; + } } int getCursorPosition(int *rows, int *cols) { @@ -122,7 +179,21 @@ void abFree(struct abuf *ab) { void editorDrawRows(struct abuf *ab) { int y; for (y = 0; y < E.screenrows; y++) { - abAppend(ab, "~", 1); + if (y == E.screenrows / 3) { + char welcome[80]; + int welcomelen = snprintf(welcome, sizeof(welcome), + "Kilo editor -- version %s", KILO_VERSION); + if (welcomelen > E.screencols) welcomelen = E.screencols; + int padding = (E.screencols - welcomelen) / 2; + if (padding) { + abAppend(ab, "~", 1); + padding--; + } + while(padding--) abAppend(ab, " ", 1); + abAppend(ab, welcome, welcomelen); + } else { + abAppend(ab, "~", 1); + } abAppend(ab, "\x1b[K", 3); // Erase one line if (y < E.screenrows - 1) { @@ -139,7 +210,11 @@ void editorRefreshScreen() { editorDrawRows(&ab); - abAppend(&ab, "\x1b[H", 3); + // Update cursor position + char buf[32]; + snprintf(buf, sizeof(buf), "\x1b[%d;%dH", E.cy +1, E.cx + 1); + abAppend(&ab, buf, strlen(buf)); + abAppend(&ab, "\x1b[?25h", 6); // Show cursor write(STDOUT_FILENO, ab.b, ab.len); @@ -148,8 +223,34 @@ void editorRefreshScreen() { /*** input ***/ +void editorMoveCursor(int key) { + switch (key) { + case ARROW_LEFT: + if (E.cx != 0) { + E.cx--; + } + break; + case ARROW_RIGHT: + if (E.cx != E.screencols -1) { + E.cx++; + } + break; + case ARROW_UP: + if (E.cy != 0) { + E.cy--; + } + break; + case ARROW_DOWN: + if (E.cy != E.screenrows -1) { + E.cy++; + } + break; + } +} + + void editorProcessKeypress() { - char c = editorReadKey(); // Blocking! + int c = editorReadKey(); // Blocking! switch (c) { case CTRL_KEY('q'): @@ -157,12 +258,39 @@ void editorProcessKeypress() { write(STDOUT_FILENO, "\x1b[H", 3); exit(0); break; + + case HOME_KEY: + E.cx = 0; + break; + + case END_KEY: + E.cx = E.screencols -1; + break; + + case PAGE_UP: + case PAGE_DOWN: + { + int times = E.screenrows; + while(times--) + editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN); + } + break; + case ARROW_UP: + case ARROW_DOWN: + case ARROW_LEFT: + case ARROW_RIGHT: + editorMoveCursor(c); + break; + } } /*** init ***/ void initEditor() { + E.cx = 0; + E.cy = 0; + if (getWindowSize(&E.screenrows, &E.screencols) == -1) die("getWindowSize"); }