initial commit
[erebus] / erebus / src / console.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <ctype.h>
6 #include <drawtext.h>
7 #include "console.h"
8
9 static void set_cursor(int x);
10
11 static int visible;
12 static int nlines = 16;
13 static int ncolumns = 80;
14
15 #define INPUTQ_SIZE 64
16 static int inpq_front, inpq_back;
17 static int keybuf[INPUTQ_SIZE];
18
19 static const char *prompt = "> ";
20 static char input[512]; /* current input line */
21 static int input_size;
22 static int cursor;
23 static int input_win;   /* first char to show in the input "window" */
24
25 /* input history */
26 static char **hist;
27 static int max_hist_lines = 64;
28 static int hist_next;
29 static int cur_hist_item = -1;
30
31 /* console output */
32 static char **output;
33 static int max_output_lines = 128;
34 static int output_next;
35 static int echo = 1;
36
37 static void (*cmd_handler)(const char*, void*);
38 static void *cmd_handler_cls;
39
40 static int font_size;
41 static struct dtx_font *font;
42
43
44 void con_set_font(struct dtx_font *fnt, int sz)
45 {
46         font = fnt;
47         font_size = sz;
48 }
49
50 int con_set_history_size(int hsz)
51 {
52         char **tmp;
53
54         if(hsz < 0) return -1;
55
56         if(hsz < hist_next) {
57                 memmove(hist, hist + hist_next - hsz, hsz);
58                 hist_next = 0;
59         }
60
61         if(!(tmp = realloc(hist, hsz * sizeof *hist))) {
62                 fprintf(stderr, "failed to change console history to %d: %s\n", hsz, strerror(errno));
63                 return -1;
64         }
65         hist = tmp;
66         max_hist_lines = hsz;
67         return 0;
68 }
69
70 int con_set_output_buffer_size(int hsz)
71 {
72         char **tmp;
73
74         if(hsz < 0) return -1;
75
76         if(hsz < output_next) {
77                 memmove(output, output + output_next - hsz, hsz);
78                 output_next = 0;
79         }
80
81         if(!(tmp = realloc(output, hsz * sizeof *output))) {
82                 fprintf(stderr, "failed to change console outputory to %d: %s\n", hsz, strerror(errno));
83                 return -1;
84         }
85         output = tmp;
86         max_output_lines = hsz;
87         return 0;
88 }
89
90 void con_set_command_func(void (*func)(const char*, void*), void *cls)
91 {
92         cmd_handler = func;
93         cmd_handler_cls = cls;
94 }
95
96 void con_set_echo(int e)
97 {
98         echo = e;
99 }
100
101 int con_get_echo(void)
102 {
103         return echo;
104 }
105
106 void con_show(void)
107 {
108         visible = 1;
109 }
110
111 void con_hide(void)
112 {
113         visible = 0;
114 }
115
116 void con_set_visible(int v)
117 {
118         visible = v;
119 }
120
121 int con_is_visible(void)
122 {
123         return visible;
124 }
125
126 void con_set_size(int lines, int columns)
127 {
128         nlines = lines;
129         ncolumns = columns;
130 }
131
132 int con_get_size_lines(void)
133 {
134         return nlines;
135 }
136
137 int con_get_size_columns(void)
138 {
139         return ncolumns;
140 }
141
142 int con_update(void)
143 {
144         int must_redraw = 0;
145         char *str;
146
147         while(inpq_front != inpq_back) {
148                 int c = keybuf[inpq_front];
149                 inpq_front = (inpq_front + 1) % INPUTQ_SIZE;
150
151                 switch(c) {
152                 case '\n':
153                 case '\r':
154                         if(echo) {
155                                 con_puts(input);
156                                 con_putchar('\n');
157                         }
158                         if(input_size > 0 && cmd_handler) {
159                                 cmd_handler(input, cmd_handler_cls);
160                         }
161
162                         /* append the input string into the history buffer */
163                         if(!(str = malloc(strlen(input) + 1))) {
164                                 fprintf(stderr, "failed to add input to history buffer: %s\n", strerror(errno));
165                                 continue;
166                         }
167                         if(hist[hist_next]) free(hist[hist_next]);
168                         hist[hist_next] = str;
169                         hist_next = (hist_next + 1) & (max_hist_lines - 1);
170                         cur_hist_item = -1;
171
172                         set_cursor(0);
173                         must_redraw = 1;
174                         break;
175
176                 case '\t':
177                         /* TODO completion */
178                         break;
179
180                 case '\b':
181                         if(input_size > 0) {
182                                 if(cursor == input_size) {
183                                         input_size--;
184                                         set_cursor(cursor - 1);
185                                         must_redraw = 1;
186                                 }
187                         } else if(cursor > 0) {
188                                 memmove(input + cursor, input + cursor + 1, input_size - cursor - 1);
189                                 input_size--;
190                                 set_cursor(cursor - 1);
191                                 must_redraw = 1;
192                         }
193                         break;
194
195                 case CON_KEY_UP:
196                         break;  /* TODO */
197                 case CON_KEY_DOWN:
198                         break;  /* TODO */
199
200                 case CON_KEY_LEFT:
201                         if(cursor > 0) {
202                                 set_cursor(cursor - 1);
203                                 must_redraw = 1;
204                         }
205                         break;
206
207                 case CON_KEY_RIGHT:
208                         if(cursor < input_size) {
209                                 set_cursor(cursor + 1);
210                                 must_redraw = 1;
211                         }
212                         break;
213
214                 case CON_KEY_HOME:
215                         set_cursor(0);
216                         must_redraw = 1;
217                         break;
218
219                 case CON_KEY_END:
220                         set_cursor(input_size);
221                         must_redraw = 1;
222                         break;
223
224                 case CON_KEY_PGUP:
225                 case CON_KEY_PGDOWN:
226                         /* TODO scroll output buffer */
227                         break;
228
229                 default:
230                         if((c < 256 && isprint(c))) {
231                                 if(cursor == input_size) {
232                                         input[input_size++] = c;
233                                 } else {
234                                         memmove(input + cursor + 1, input + cursor, input_size - cursor);
235                                         input[cursor] = c;
236                                 }
237                                 set_cursor(cursor + 1);
238                                 must_redraw = 1;
239                         }
240                 }
241         }
242         return must_redraw;
243 }
244
245 static void set_cursor(int x)
246 {
247         int max_chars;
248
249         if(x < 0 || x > input_size) {
250                 return;
251         }
252         cursor = x;
253
254         max_chars = ncolumns - strlen(prompt) - 1;
255
256         if(cursor < input_win) {
257                 input_win = cursor;
258         } else if(cursor > input_win + max_chars) {
259                 input_win = cursor - max_chars;
260                 if(input_win < 0) input_win = 0;
261         }
262 }