]> git.lizzy.rs Git - nothing.git/blob - src/ui/console.c
(#359) Print parsing errors to the console
[nothing.git] / src / ui / console.c
1 #include <assert.h>
2
3 #include "game/level.h"
4 #include "game/level/player/rigid_rect.h"
5 #include "script/gc.h"
6 #include "script/interpreter.h"
7 #include "script/parser.h"
8 #include "script/scope.h"
9 #include "sdl/renderer.h"
10 #include "system/error.h"
11 #include "system/lt.h"
12 #include "ui/console.h"
13 #include "ui/edit_field.h"
14 #include "ui/log.h"
15
16 #define FONT_WIDTH_SCALE 3.0f
17 #define FONT_HEIGHT_SCALE 3.0f
18
19 #define LOG_CAPACITY 10
20 #define PROMPT_HEIGHT (FONT_HEIGHT_SCALE * FONT_CHAR_HEIGHT)
21 #define LOG_HEIGHT (FONT_HEIGHT_SCALE * FONT_CHAR_HEIGHT * LOG_CAPACITY)
22
23 #define CONSOLE_HEIGHT (LOG_HEIGHT + PROMPT_HEIGHT)
24
25 #define SLIDE_DOWN_TIME 0.1f
26 #define SLIDE_DOWN_SPEED (CONSOLE_HEIGHT / SLIDE_DOWN_TIME)
27
28 #define CONSOLE_BACKGROUND (color(0.20f, 0.20f, 0.20f, 1.0f))
29 #define CONSOLE_FOREGROUND (color(0.80f, 0.80f, 0.80f, 1.0f))
30 #define CONSOLE_ERROR (color(0.80f, 0.50f, 0.50f, 1.0f))
31
32 struct Console
33 {
34     Lt *lt;
35     Gc *gc;
36     struct Scope scope;
37     Edit_field *edit_field;
38     Log *log;
39     Level *level;
40     float y;
41 };
42
43 /* TODO(#354): Console does not allow to travel the history by pressing up and down */
44 /* TODO(#355): Console does not support Emacs keybindings */
45 /* TODO(#356): Console does not support autocompletion */
46 /* TODO(#357): Console does not show the state of the GC of the script */
47 /* TODO(#358): Console does not support copy, cut, paste operations */
48
49 static struct EvalResult rect_apply_force(void *param, Gc *gc, struct Scope *scope, struct Expr args)
50 {
51     assert(gc);
52     assert(scope);
53     assert(param);
54
55     Level *level = (Level*) param;
56     const char *rect_id = CAR(args).atom->str;
57     struct Expr vector_force_expr = CAR(CDR(args));
58     const float force_x = (float) CAR(vector_force_expr).atom->num;
59     const float force_y = (float) CDR(vector_force_expr).atom->num;
60
61     print_expr_as_sexpr(args); printf("\n");
62
63     Rigid_rect *rigid_rect = level_rigid_rect(level, rect_id);
64     if (rigid_rect != NULL) {
65         printf("Found rect `%s`\n", rect_id);
66         printf("Applying force (%f, %f)\n", force_x, force_y);
67         rigid_rect_apply_force(rigid_rect, vec(force_x, force_y));
68     } else {
69         fprintf(stderr, "Couldn't find rigid_rect `%s`", rect_id);
70     }
71
72     return eval_success(NIL(gc));
73 }
74
75 Console *create_console(Level *level,
76                         const Sprite_font *font)
77 {
78     Lt *lt = create_lt();
79
80     if (lt == NULL) {
81         return NULL;
82     }
83
84     Console *console = PUSH_LT(lt, malloc(sizeof(Console)), free);
85     if (console == NULL) {
86         throw_error(ERROR_TYPE_LIBC);
87         RETURN_LT(lt, NULL);
88     }
89     console->lt = lt;
90
91     console->gc = PUSH_LT(lt, create_gc(), destroy_gc);
92     if (console->gc == NULL) {
93         RETURN_LT(lt, NULL);
94     }
95
96     console->scope.expr = CONS(console->gc,
97                                NIL(console->gc),
98                                NIL(console->gc));
99     set_scope_value(
100         console->gc,
101         &console->scope,
102         SYMBOL(console->gc, "rect-apply-force"),
103         NATIVE(console->gc, rect_apply_force, level));
104
105     console->edit_field = PUSH_LT(
106         lt,
107         create_edit_field(
108             font,
109             vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
110             CONSOLE_FOREGROUND),
111         destroy_edit_field);
112     if (console->edit_field == NULL) {
113         RETURN_LT(lt, NULL);
114     }
115
116     console->log = PUSH_LT(
117         lt,
118         create_log(
119             font,
120             vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
121             LOG_CAPACITY),
122         destroy_log);
123
124     console->level = level;
125     console->y = -CONSOLE_HEIGHT;
126
127     return console;
128 }
129
130 void destroy_console(Console *console)
131 {
132     assert(console);
133     RETURN_LT0(console->lt);
134 }
135
136 int console_handle_event(Console *console,
137                          const SDL_Event *event)
138 {
139     switch(event->type) {
140     case SDL_KEYDOWN:
141         switch(event->key.keysym.sym) {
142         case SDLK_RETURN: {
143             const char *source_code = edit_field_as_text(console->edit_field);
144             struct ParseResult parse_result = read_expr_from_string(console->gc,
145                                                                     source_code);
146             if (parse_result.is_error) {
147                 if (log_push_line(console->log, source_code, CONSOLE_ERROR) < 0) {
148                     return -1;
149                 }
150
151                 if (log_push_line(console->log, parse_result.error_message, CONSOLE_ERROR)) {
152                     return -1;
153                 }
154
155                 edit_field_clean(console->edit_field);
156
157                 return 0;
158             }
159
160             struct EvalResult eval_result = eval(
161                 console->gc,
162                 &console->scope,
163                 parse_result.expr);
164
165             if (eval_result.is_error) {
166                 /* TODO(#360): Console doesn't report any eval errors visually */
167                 printf("Error:\t");
168                 print_expr_as_sexpr(eval_result.expr);
169                 printf("\n");
170             }
171
172             gc_collect(console->gc, console->scope.expr);
173
174             if (log_push_line(console->log,
175                               edit_field_as_text(console->edit_field),
176                               CONSOLE_FOREGROUND) < 0) {
177                 return -1;
178             }
179             edit_field_clean(console->edit_field);
180
181         } return 0;
182         }
183         break;
184     }
185
186     return edit_field_handle_event(console->edit_field, event);
187 }
188
189 int console_render(const Console *console,
190                    SDL_Renderer *renderer)
191 {
192     /* TODO(#364): console doesn't have any padding around the edit fields */
193     SDL_Rect view_port;
194     SDL_RenderGetViewport(renderer, &view_port);
195
196     if (fill_rect(renderer,
197                   rect(0.0f, console->y,
198                        (float) view_port.w,
199                        CONSOLE_HEIGHT),
200                   CONSOLE_BACKGROUND) < 0) {
201         return -1;
202     }
203
204     if (log_render(console->log,
205                    renderer,
206                    vec(0.0f, console->y)) < 0) {
207         return -1;
208     }
209
210     if (edit_field_render(console->edit_field,
211                           renderer,
212                           vec(0.0f, console->y + LOG_HEIGHT)) < 0) {
213         return -1;
214     }
215
216     return 0;
217 }
218
219 int console_update(Console *console, float delta_time)
220 {
221     assert(console);
222
223     /* TODO(#366): console slide down animation doesn't have any easing */
224
225     if (console->y < 0.0f) {
226         console->y += SLIDE_DOWN_SPEED * delta_time;
227
228         if (console->y > 0.0f) {
229             console->y = 0.0f;
230         }
231     }
232
233     return 0;
234 }
235
236 void console_slide_down(Console *console)
237 {
238     assert(console);
239     console->y = -CONSOLE_HEIGHT;
240 }