kilo.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /*** includes ***/
  2. #include <ctype.h>
  3. #include <stdio.h>
  4. #include <sys/ioctl.h>
  5. #include <errno.h>
  6. #include <string.h>
  7. #include <stdlib.h>
  8. #include <termios.h>
  9. #include <unistd.h>
  10. /*** defines ***/
  11. #define CTRL_KEY(k) ((k) & 0x1f)
  12. #define KILO_VERSION "0.0.1"
  13. enum editorKey {
  14. ARROW_LEFT = 'a',
  15. ARROW_RIGHT = 'd',
  16. ARROW_UP = 'w',
  17. ARROW_DOWN = 's'
  18. };
  19. /*** data ***/
  20. struct editorConfig {
  21. int cx, cy;
  22. int screenrows;
  23. int screencols;
  24. struct termios orig_termios;
  25. };
  26. struct editorConfig E;
  27. /*** terminal ***/
  28. void die(const char *s) {
  29. write(STDOUT_FILENO, "\x1b[2J", 4);
  30. write(STDOUT_FILENO, "\x1b[H", 3);
  31. perror(s);
  32. exit(1);
  33. }
  34. void disableRawMode() {
  35. if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1)
  36. die("tcsetattr");
  37. }
  38. void enableRawMode() {
  39. if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1) die("tcgetattr");
  40. atexit(disableRawMode);
  41. struct termios raw = E.orig_termios;
  42. raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
  43. raw.c_oflag &= ~(OPOST);
  44. raw.c_cflag |= (CS8);
  45. raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
  46. raw.c_cc[VMIN] = 0;
  47. raw.c_cc[VTIME] = 1;
  48. if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) die("tcsetattr");
  49. }
  50. char editorReadKey() {
  51. int nread;
  52. char c;
  53. while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
  54. if (nread == -1 && errno != EAGAIN) die("read");
  55. }
  56. if (c == '\x1b') {
  57. char seq[3];
  58. if (read(STDIN_FILENO, &seq[0], 1) != 1) return '\x1b';
  59. if (read(STDIN_FILENO, &seq[1], 1) != 1) return '\x1b';
  60. if (seq[0] == '[') {
  61. switch (seq[1]) {
  62. case 'A': return ARROW_UP;
  63. case 'B': return ARROW_DOWN;
  64. case 'C': return ARROW_RIGHT;
  65. case 'D': return ARROW_LEFT;
  66. }
  67. }
  68. return '\x1b';
  69. } else {
  70. return c;
  71. }
  72. }
  73. int getWindowSize(int *rows, int *cols) {
  74. struct winsize ws;
  75. if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
  76. return -1;
  77. } else {
  78. *cols = ws.ws_col;
  79. *rows = ws.ws_row;
  80. return 0;
  81. }
  82. }
  83. /*** append buffer ***/
  84. struct abuf {
  85. char *b;
  86. int len;
  87. };
  88. #define ABUF_INIT {NULL, 0}
  89. void abAppend(struct abuf *ab, const char *s, int len) {
  90. char *new = realloc(ab->b, ab->len + len);
  91. if (new == NULL) return;
  92. memcpy(&new[ab->len], s, len);
  93. ab->b = new;
  94. ab->len += len;
  95. }
  96. void abFree(struct abuf *ab) {
  97. free(ab->b);
  98. }
  99. /*** output ***/
  100. void editorDrawRows(struct abuf *ab) {
  101. int y;
  102. for (y = 0; y < E.screenrows; y++) {
  103. if (y == E.screenrows / 3) {
  104. char welcome[80];
  105. int welcomelen = snprintf(welcome, sizeof(welcome),
  106. "Kilo editor -- version %s", KILO_VERSION);
  107. if (welcomelen > E.screencols) welcomelen = E.screencols;
  108. int padding = (E.screencols - welcomelen) / 2;
  109. if (padding) {
  110. abAppend(ab, "~", 1);
  111. padding--;
  112. }
  113. while (padding--) abAppend(ab, " ", 1);
  114. abAppend(ab, welcome, welcomelen);
  115. } else {
  116. abAppend(ab, "~", 1);
  117. }
  118. abAppend(ab, "\x1b[K", 3);
  119. if (y < E.screenrows - 1) {
  120. abAppend(ab, "\r\n", 2);
  121. }
  122. }
  123. }
  124. void editorRefreshScreen() {
  125. struct abuf ab = ABUF_INIT;
  126. abAppend(&ab, "\x1b[?25l", 6);
  127. abAppend(&ab, "\x1b[H", 3);
  128. editorDrawRows(&ab);
  129. char buf[32];
  130. snprintf(buf, sizeof(buf), "\x1b[%d;%dH", E.cy + 1, E.cx + 1);
  131. abAppend(&ab, buf, strlen(buf));
  132. abAppend(&ab, "\x1b[?25h", 6);
  133. write(STDOUT_FILENO, ab.b, ab.len);
  134. abFree(&ab);
  135. }
  136. /*** input ***/
  137. void editorMoveCursor(char key) {
  138. switch (key) {
  139. case ARROW_LEFT:
  140. E.cx--;
  141. break;
  142. case ARROW_RIGHT:
  143. E.cx++;
  144. break;
  145. case ARROW_UP:
  146. E.cy--;
  147. break;
  148. case ARROW_DOWN:
  149. E.cy++;
  150. break;
  151. }
  152. }
  153. void editorProcessKeypress() {
  154. char c = editorReadKey();
  155. switch (c) {
  156. case CTRL_KEY('q'):
  157. exit(0);
  158. break;
  159. case 'w':
  160. case 's':
  161. case 'a':
  162. case 'd':
  163. editorMoveCursor(c);
  164. break;
  165. }
  166. }
  167. /*** init ***/
  168. void initEditor() {
  169. E.cx = 0;
  170. E.cy = 0;
  171. if (getWindowSize(&E.screenrows, &E.screencols) == -1) die("getWindowSize");
  172. }
  173. int main() {
  174. enableRawMode();
  175. initEditor();
  176. while (1) {
  177. editorRefreshScreen();
  178. editorProcessKeypress();
  179. }
  180. return 0;
  181. }