1 #include "system/stacktrace.h"
4 #include "ebisp/interpreter.h"
5 #include "ebisp/parser.h"
6 #include "ebisp/scope.h"
8 #include "game/level.h"
9 #include "sdl/renderer.h"
10 #include "system/log.h"
11 #include "system/log_script.h"
12 #include "system/lt.h"
13 #include "system/nth_alloc.h"
14 #include "ui/console.h"
15 #include "ui/console_log.h"
16 #include "ui/edit_field.h"
17 #include "ui/history.h"
18 #include "broadcast.h"
20 #define FONT_WIDTH_SCALE 3.0f
21 #define FONT_HEIGHT_SCALE 3.0f
23 #define CONSOLE_LOG_CAPACITY 10
24 #define HISTORY_CAPACITY 20
25 #define PROMPT_HEIGHT (FONT_HEIGHT_SCALE * FONT_CHAR_HEIGHT)
26 #define CONSOLE_LOG_HEIGHT (FONT_HEIGHT_SCALE * FONT_CHAR_HEIGHT * CONSOLE_LOG_CAPACITY)
28 #define CONSOLE_HEIGHT (CONSOLE_LOG_HEIGHT + PROMPT_HEIGHT)
30 #define SLIDE_DOWN_TIME 0.4f
32 #define CONSOLE_ALPHA (0.80f)
33 #define CONSOLE_BACKGROUND (rgba(0.20f, 0.20f, 0.20f, CONSOLE_ALPHA))
34 #define CONSOLE_FOREGROUND (rgba(0.80f, 0.80f, 0.80f, CONSOLE_ALPHA))
35 #define CONSOLE_ERROR (rgba(0.80f, 0.50f, 0.50f, CONSOLE_ALPHA))
37 #define CONSOLE_EVAL_RESULT_SIZE 256
44 Edit_field *edit_field;
45 Console_Log *console_log;
51 /* TODO(#355): Console does not support Emacs keybindings */
52 /* TODO(#356): Console does not support autocompletion */
53 /* TODO(#357): Console does not show the state of the GC of the script */
54 /* TODO(#358): Console does not support copy, cut, paste operations */
56 Console *create_console(Broadcast *broadcast,
57 const Sprite_font *font)
62 Console *console = PUSH_LT(lt, nth_calloc(1, sizeof(Console)), free);
63 if (console == NULL) {
68 console->gc = PUSH_LT(lt, create_gc(), destroy_gc);
69 if (console->gc == NULL) {
73 console->scope.expr = CONS(console->gc,
77 load_std_library(console->gc, &console->scope);
78 load_log_library(console->gc, &console->scope);
79 /* TODO(#669): how to report EvalResult error from create_console? */
80 broadcast_load_library(broadcast, console->gc, &console->scope);
82 console->edit_field = PUSH_LT(
85 vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
88 if (console->edit_field == NULL) {
92 console->console_log = PUSH_LT(
96 vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
97 CONSOLE_LOG_CAPACITY),
102 console->eval_result = PUSH_LT(
104 nth_calloc(1, sizeof(char) * CONSOLE_EVAL_RESULT_SIZE),
106 if (console->eval_result == NULL) {
109 memset(console->eval_result, 0, sizeof(char) * CONSOLE_EVAL_RESULT_SIZE);
111 console->history = PUSH_LT(
113 create_history(HISTORY_CAPACITY),
115 if (console->history == NULL) {
122 void destroy_console(Console *console)
124 trace_assert(console);
125 RETURN_LT0(console->lt);
128 static int console_eval_input(Console *console)
130 const char *source_code = edit_field_as_text(console->edit_field);
132 /* TODO(#387): console pushes empty strings to the history */
133 if (history_push(console->history, source_code) < 0) {
137 if (console_log_push_line(console->console_log, source_code, CONSOLE_FOREGROUND) < 0) {
141 while (*source_code != 0) {
142 struct ParseResult parse_result = read_expr_from_string(console->gc,
145 if (parse_result.is_error) {
146 if (console_log_push_line(console->console_log, parse_result.error_message, CONSOLE_ERROR)) {
150 edit_field_clean(console->edit_field);
155 struct EvalResult eval_result = eval(
162 console->eval_result,
163 CONSOLE_EVAL_RESULT_SIZE) < 0) {
167 if (console_log_push_line(console->console_log,
168 console->eval_result,
169 eval_result.is_error ?
171 CONSOLE_FOREGROUND)) {
175 source_code = next_token(parse_result.end).begin;
178 gc_collect(console->gc, console->scope.expr);
179 edit_field_clean(console->edit_field);
184 int console_handle_event(Console *console,
185 const SDL_Event *event)
187 switch(event->type) {
189 switch(event->key.keysym.sym) {
191 return console_eval_input(console);
196 history_current(console->history));
197 history_prev(console->history);
203 history_current(console->history));
204 history_next(console->history);
208 return edit_field_keyboard(console->edit_field, &event->key);
212 return edit_field_text_input(console->edit_field, &event->text);
220 int console_render(const Console *console,
222 SDL_Renderer *renderer)
224 /* TODO(#364): console doesn't have any padding around the edit fields */
226 SDL_RenderGetViewport(renderer, &view_port);
228 const float e = console->a * (2 - console->a);
229 const float y = -(1.0f - e) * CONSOLE_HEIGHT;
231 if (fill_rect(renderer,
235 CONSOLE_BACKGROUND) < 0) {
239 if (console_log_render(console->console_log,
245 if (edit_field_render(console->edit_field,
247 vec(0.0f, y + CONSOLE_LOG_HEIGHT)) < 0) {
254 int console_update(Console *console, float delta_time)
256 trace_assert(console);
258 if (console->a < 1.0f) {
259 console->a += 1.0f / SLIDE_DOWN_TIME * delta_time;
261 if (console->a > 1.0f) {
269 void console_slide_down(Console *console)
271 trace_assert(console);