1 #include <stdio.h>
2 #include "console/console.h"
3 #include "console/console_type.h"
4 #include "game/gui/menu_background.h"
5 #include "game/utils/settings.h"
6 #include "video/video.h"
7
8 #define HISTORY_MAX 100
9 #define BUFFER_INC(b) (((b) + 1) % sizeof(con->output))
10 #define BUFFER_DEC(b) (((b) + sizeof(con->output) - 1) % sizeof(con->output))
11
12 #define CURSOR_STR "\x7f"
13
14 // Console State
15 console *con = NULL;
16
17 // defined in console_cmd.c
18 void console_init_cmd();
19
make_argv(char * p,char ** argv)20 int make_argv(char *p, char **argv) {
21 // split line into argv, warning: does not handle quoted strings
22 int argc = 0;
23 while(isspace(*p)) { ++p; }
24 while(*p) {
25 if(argv != NULL) { argv[argc] = p; }
26 while(*p && !isspace(*p)) { ++p; }
27 if(argv != NULL && *p) { *p++ = '\0'; }
28 while(isspace(*p)) { ++p; }
29 ++argc;
30 }
31 return argc;
32 }
33
console_add_history(const char * input,unsigned int len)34 void console_add_history(const char *input, unsigned int len) {
35 iterator it;
36 list_iter_begin(&con->history, &it);
37
38 char *input2 = iter_next(&it);
39 if(input2 != NULL) {
40 if(strcmp(input, input2) != 0) {
41 if(list_size(&con->history) == HISTORY_MAX) {
42 list_iter_end(&con->history, &it);
43 list_delete(&con->history, &it);
44 }
45 list_prepend(&con->history, input, len);
46 }
47 } else if(input2 == NULL) {
48 list_prepend(&con->history, input, len);
49 }
50 con->histpos = -1;
51 }
52
console_handle_line(game_state * gs)53 void console_handle_line(game_state *gs) {
54 if(con->input[0] == '\0') {
55 console_output_addline(">");
56 } else {
57 char input_copy[sizeof(con->input)];
58 memcpy(input_copy, con->input, sizeof(con->input));
59 int argc = make_argv(con->input, NULL);
60 if(argc > 0) {
61 char *argv[argc];
62 void *val = 0;
63 unsigned int len;
64 make_argv(con->input, argv);
65 if(!hashmap_sget(&con->cmds, argv[0], &val, &len)) {
66 command *cmd = val;
67 int err = cmd->func(gs, argc, argv);
68 if(err == 0)
69 {
70 console_output_add("> ");
71 console_output_add(argv[0]);
72 console_output_addline(" SUCCESS");
73 } else {
74 char buf[12];
75 sprintf(buf, "%d", err);
76 console_output_add("> ");
77 console_output_add(argv[0]);
78 console_output_add(" ERROR:");
79 console_output_addline(buf);
80 }
81 console_add_history(input_copy, sizeof(input_copy));
82 } else {
83 console_output_add("> ");
84 console_output_add(argv[0]);
85 console_output_addline(" NOT RECOGNIZED");
86 }
87 } else {
88 console_output_addline(">");
89 }
90 }
91 }
92
console_output_scroll_to_end()93 void console_output_scroll_to_end() {
94 // iterate the output buffer backward to count up to 16 lines or 480 chars, whichever comes first
95 unsigned int lines = 0;
96 unsigned int si;
97 if(con->output_overflowing) {
98 si = BUFFER_DEC(con->output_head);
99 si = BUFFER_DEC(si);
100 } else if(con->output_tail == con->output_head) {
101 si = con->output_tail;
102 } else {
103 si = BUFFER_DEC(con->output_tail);
104 }
105
106 for(;
107 si != con->output_head;
108 si = BUFFER_DEC(si)) {
109
110 if(con->output[si] == '\n') {
111 lines++;
112
113 if(lines >= 16) {
114 si = BUFFER_INC(si);
115 break;
116 }
117 }
118 }
119
120 con->output_pos = si;
121 }
122
console_output_scroll_up(unsigned int lines)123 void console_output_scroll_up(unsigned int lines) {
124 unsigned int l = 0;
125 if(con->output_pos != con->output_head) {
126 con->output_pos = BUFFER_DEC(con->output_pos);
127 }
128 while(con->output_pos != con->output_head) {
129 con->output_pos = BUFFER_DEC(con->output_pos);
130 if(con->output[con->output_pos] == '\n') {
131 l++;
132 if(l == lines){
133 con->output_pos = BUFFER_INC(con->output_pos);
134 break;
135 }
136 }
137 }
138 }
139
console_output_scroll_down(unsigned int lines)140 void console_output_scroll_down(unsigned int lines) {
141 unsigned int l = 0;
142 while(con->output_pos != con->output_tail) {
143 con->output_pos = BUFFER_INC(con->output_pos);
144 if(con->output[con->output_pos] == '\n') {
145 l++;
146 if(l == lines){
147 con->output_pos = BUFFER_INC(con->output_pos);
148 break;
149 }
150 }
151 }
152 }
153
console_output_add(const char * text)154 void console_output_add(const char *text) {
155 size_t len = strlen(text);
156 for(size_t i = 0;i < len;++i) {
157 char c = text[i];
158 con->output[con->output_tail] = c;
159 con->output_tail = BUFFER_INC(con->output_tail);
160 if(con->output_tail == con->output_head) {
161 // buffer is overflowing
162 con->output_head = BUFFER_INC(con->output_head);
163 con->output_overflowing = 1;
164 }
165 }
166
167 console_output_scroll_to_end();
168 }
console_output_addline(const char * text)169 void console_output_addline(const char *text) {
170 console_output_add(text);
171 console_output_add("\n");
172 }
173
console_output_render()174 void console_output_render() {
175 int x = 0;
176 int y = 0;
177 unsigned int lines = 0;
178 const color textcolor = color_create(121, 121, 121, 255);
179 for(unsigned int i = con->output_pos;
180 i != con->output_tail && lines < 15;
181 i = BUFFER_INC(i)) {
182
183 char c = con->output[i];
184 if(c == '\n') {
185 x = 0;
186 y += font_small.h;
187 lines++;
188 } else {
189 // TODO add word wrapping?
190 font_render_char(&font_small, c, x, y+con->ypos-100, textcolor);
191 x += font_small.w;
192 }
193 }
194 }
195
console_init()196 int console_init() {
197 if(con != NULL) return 1;
198 con = malloc(sizeof(console));
199 con->isopen = 0;
200 con->ownsinput = 0;
201 con->ypos = 0;
202 con->ticks = 0;
203 con->dir = 0;
204 con->input[0] = '\0';
205 con->output[0] = '\0';
206 con->output_head = 0;
207 con->output_tail = 0;
208 con->output_pos = 0;
209 con->output_overflowing = 0;
210 con->histpos = -1;
211 con->histpos_changed = 0;
212 list_create(&con->history);
213 hashmap_create(&con->cmds, 8);
214 menu_background_create(&con->background, 322, 101);
215
216 console_init_cmd();
217
218 // Print the header
219 for(int i=0;i<37;i++) {
220 console_output_add(CURSOR_STR);
221 }
222 console_output_addline("");
223 console_output_addline(CURSOR_STR " " CURSOR_STR "\n"
224 CURSOR_STR " OpenOMF Debug Console Cheat Sheet " CURSOR_STR "\n"
225 CURSOR_STR " " CURSOR_STR "\n"
226 CURSOR_STR " PageUp - Scroll Up " CURSOR_STR "\n"
227 CURSOR_STR " PageDn - Scroll Down " CURSOR_STR "\n"
228 CURSOR_STR " Up - Reissue Previous Command " CURSOR_STR "\n"
229 CURSOR_STR " Down - Reissue Next Command " CURSOR_STR "\n"
230 CURSOR_STR " Enter - Execute Current Command " CURSOR_STR "\n"
231 CURSOR_STR " --------------------------------- " CURSOR_STR "\n"
232 CURSOR_STR " Type in Help to explore more " CURSOR_STR "\n"
233 CURSOR_STR " --------------------------------- " CURSOR_STR "\n"
234 CURSOR_STR " " CURSOR_STR);
235 for(int i=0;i<37;i++) {
236 console_output_add(CURSOR_STR);
237 }
238 console_output_addline("\n");
239
240 return 0;
241 }
242
console_close()243 void console_close() {
244 surface_free(&con->background);
245 list_free(&con->history);
246 hashmap_free(&con->cmds);
247 free(con);
248 }
249
console_event(game_state * gs,SDL_Event * e)250 void console_event(game_state *gs, SDL_Event *e) {
251 if (e->type == SDL_TEXTINPUT) {
252 size_t len = strlen(con->input);
253 if (strlen(e->text.text) == 1) {
254 // make sure it is not a unicode sequence
255 unsigned char c = e->text.text[0];
256 if (c >= 32 && c <= 126) {
257 // only allow ASCII through
258 if (len < sizeof(con->input)-1) {
259 con->input[len+1] = '\0';
260 con->input[len] = c;
261 }
262 }
263 }
264 } else if (e->type == SDL_KEYDOWN) {
265 size_t len = strlen(con->input);
266 unsigned char scancode = e->key.keysym.scancode;
267 /*if ((code >= SDLK_a && code <= SDLK_z) || (code >= SDLK_0 && code <= SDLK_9) || code == SDLK_SPACE || code == SDLK) {*/
268 // SDLK_UP and SDLK_DOWN does not work here
269 if(scancode == SDL_SCANCODE_UP) {
270 if(con->histpos < HISTORY_MAX && con->histpos < (signed int)(list_size(&con->history)-1)) {
271 con->histpos++;
272 con->histpos_changed = 1;
273 }
274 } else if(scancode == SDL_SCANCODE_DOWN) {
275 if(con->histpos > -1) {
276 con->histpos--;
277 con->histpos_changed = 1;
278 }
279 } else if(scancode == SDL_SCANCODE_LEFT) {
280 // TODO move cursor to the left
281 } else if(scancode == SDL_SCANCODE_RIGHT) {
282 // TODO move cursor to the right
283 } else if (scancode == SDL_SCANCODE_BACKSPACE || scancode == SDL_SCANCODE_DELETE) {
284 if (len > 0) {
285 con->input[len-1] = '\0';
286 }
287 } else if (scancode == SDL_SCANCODE_RETURN || scancode == SDL_SCANCODE_KP_ENTER) {
288 // send the input somewhere and clear the input line
289 console_handle_line(gs);
290 con->input[0] = '\0';
291 } else if(scancode == SDL_SCANCODE_PAGEUP) {
292 console_output_scroll_up(1);
293 } else if(scancode == SDL_SCANCODE_PAGEDOWN) {
294 console_output_scroll_down(1);
295 }
296 }
297 }
298
console_render()299 void console_render() {
300 if (con->ypos > 0) {
301 if(con->histpos != -1 && con->histpos_changed) {
302 char *input = list_get(&con->history, con->histpos);
303 memcpy(con->input, input, sizeof(con->input));
304 con->histpos_changed = 0;
305 }
306 if(con->histpos == -1 && con->histpos_changed) {
307 con->input[0] = '\0';
308 con->histpos_changed = 0;
309 }
310 video_render_sprite(&con->background, -1, con->ypos - 101, BLEND_ALPHA, 0);
311 int t = con->ticks / 2;
312 // input line
313 font_render(&font_small, con->input, 0 , con->ypos - 7, color_create(121, 121, 121, 255));
314 //cursor
315 font_render(&font_small, CURSOR_STR, strlen(con->input)*font_small.w , con->ypos - 7, color_create(121 - t, 121 - t, 121 - t, 255));
316 console_output_render();
317 }
318 }
319
console_tick()320 void console_tick() {
321 if (con->isopen && con->ypos < 100) {
322 con->ypos+=4;
323 if(settings_get()->video.instant_console) { con->ypos = 100; }
324 } else if (!con->isopen && con->ypos > 0) {
325 con->ypos-=4;
326 if(settings_get()->video.instant_console) { con->ypos = 0; }
327 }
328 if(!con->dir) {
329 con->ticks++;
330 } else {
331 con->ticks--;
332 }
333 if(con->ticks > 120) {
334 con->dir = 1;
335 }
336 if(con->ticks == 0) {
337 con->dir = 0;
338 }
339 }
340
console_add_cmd(const char * name,command_func func,const char * doc)341 void console_add_cmd(const char *name, command_func func, const char *doc) {
342 command c;
343 c.func = func;
344 c.doc = doc;
345 hashmap_sput(&con->cmds, name, &c, sizeof(command));
346 }
347
console_remove_cmd(const char * name)348 void console_remove_cmd(const char *name) {
349 hashmap_sdel(&con->cmds, name);
350 }
351
console_window_is_open()352 int console_window_is_open() {
353 return con->isopen;
354 }
355
console_window_open()356 void console_window_open() {
357 if (!SDL_IsTextInputActive()) {
358 SDL_StartTextInput();
359 con->ownsinput = 1;
360 } else {
361 con->ownsinput = 0;
362 }
363 con->isopen = 1;
364 }
365
console_window_close()366 void console_window_close() {
367 if (con->ownsinput) {
368 SDL_StopTextInput();
369 }
370 con->isopen = 0;
371 }
372