]> git.lizzy.rs Git - nothing.git/blob - src/ui/console.c
Remove TODO(#354)
[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/history.h"
15 #include "ui/log.h"
16
17 #define FONT_WIDTH_SCALE 3.0f
18 #define FONT_HEIGHT_SCALE 3.0f
19
20 #define LOG_CAPACITY 10
21 #define HISTORY_CAPACITY 20
22 #define PROMPT_HEIGHT (FONT_HEIGHT_SCALE * FONT_CHAR_HEIGHT)
23 #define LOG_HEIGHT (FONT_HEIGHT_SCALE * FONT_CHAR_HEIGHT * LOG_CAPACITY)
24
25 #define CONSOLE_HEIGHT (LOG_HEIGHT + PROMPT_HEIGHT)
26
27 #define SLIDE_DOWN_TIME 0.4f
28
29 #define CONSOLE_ALPHA (0.80f)
30 #define CONSOLE_BACKGROUND (color(0.20f, 0.20f, 0.20f, CONSOLE_ALPHA))
31 #define CONSOLE_FOREGROUND (color(0.80f, 0.80f, 0.80f, CONSOLE_ALPHA))
32 #define CONSOLE_ERROR (color(0.80f, 0.50f, 0.50f, CONSOLE_ALPHA))
33
34 #define CONSOLE_EVAL_RESULT_SIZE 256
35
36 struct Console
37 {
38     Lt *lt;
39     Gc *gc;
40     struct Scope scope;
41     Edit_field *edit_field;
42     Log *log;
43     Level *level;
44     History *history;
45     float a;
46     char *eval_result;
47 };
48
49 /* TODO(#355): Console does not support Emacs keybindings */
50 /* TODO(#356): Console does not support autocompletion */
51 /* TODO(#357): Console does not show the state of the GC of the script */
52 /* TODO(#358): Console does not support copy, cut, paste operations */
53
54 static struct EvalResult rect_apply_force(void *param, Gc *gc, struct Scope *scope, struct Expr args)
55 {
56     assert(gc);
57     assert(scope);
58     assert(param);
59
60     /* TODO(#401): rect_apply_force doesn't sanitize it's input */
61
62     Level *level = (Level*) param;
63     const char *rect_id = CAR(args).atom->str;
64     struct Expr vector_force_expr = CAR(CDR(args));
65     const float force_x = (float) CAR(vector_force_expr).atom->num;
66     const float force_y = (float) CDR(vector_force_expr).atom->num;
67
68     print_expr_as_sexpr(stdout, args); printf("\n");
69
70     Rigid_rect *rigid_rect = level_rigid_rect(level, rect_id);
71     if (rigid_rect != NULL) {
72         printf("Found rect `%s`\n", rect_id);
73         printf("Applying force (%f, %f)\n", force_x, force_y);
74         rigid_rect_apply_force(rigid_rect, vec(force_x, force_y));
75     } else {
76         fprintf(stderr, "Couldn't find rigid_rect `%s`", rect_id);
77     }
78
79     return eval_success(NIL(gc));
80 }
81
82 Console *create_console(Level *level,
83                         const Sprite_font *font)
84 {
85     Lt *lt = create_lt();
86
87     if (lt == NULL) {
88         return NULL;
89     }
90
91     Console *console = PUSH_LT(lt, malloc(sizeof(Console)), free);
92     if (console == NULL) {
93         throw_error(ERROR_TYPE_LIBC);
94         RETURN_LT(lt, NULL);
95     }
96     console->lt = lt;
97
98     console->gc = PUSH_LT(lt, create_gc(), destroy_gc);
99     if (console->gc == NULL) {
100         RETURN_LT(lt, NULL);
101     }
102
103     console->scope.expr = CONS(console->gc,
104                                NIL(console->gc),
105                                NIL(console->gc));
106     set_scope_value(
107         console->gc,
108         &console->scope,
109         SYMBOL(console->gc, "rect-apply-force"),
110         NATIVE(console->gc, rect_apply_force, level));
111
112     console->edit_field = PUSH_LT(
113         lt,
114         create_edit_field(
115             font,
116             vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
117             CONSOLE_FOREGROUND),
118         destroy_edit_field);
119     if (console->edit_field == NULL) {
120         RETURN_LT(lt, NULL);
121     }
122
123     console->log = PUSH_LT(
124         lt,
125         create_log(
126             font,
127             vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
128             LOG_CAPACITY),
129         destroy_log);
130
131     console->level = level;
132     console->a = 0;
133
134     console->eval_result = PUSH_LT(
135         lt,
136         malloc(sizeof(char) * CONSOLE_EVAL_RESULT_SIZE),
137         free);
138     if (console->eval_result == NULL) {
139         RETURN_LT(lt, NULL);
140     }
141     memset(console->eval_result, 0, sizeof(char) * CONSOLE_EVAL_RESULT_SIZE);
142
143     console->history = PUSH_LT(
144         lt,
145         create_history(HISTORY_CAPACITY),
146         destroy_history);
147     if (console->history == NULL) {
148         RETURN_LT(lt, NULL);
149     }
150
151     return console;
152 }
153
154 void destroy_console(Console *console)
155 {
156     assert(console);
157     RETURN_LT0(console->lt);
158 }
159
160 int console_handle_event(Console *console,
161                          const SDL_Event *event)
162 {
163     switch(event->type) {
164     case SDL_KEYDOWN:
165         switch(event->key.keysym.sym) {
166         case SDLK_RETURN: {
167             const char *source_code = edit_field_as_text(console->edit_field);
168
169             /* TODO(#387): console pushes empty strings to the history */
170             if (history_push(console->history, source_code) < 0) {
171                 return -1;
172             }
173
174             if (log_push_line(console->log, source_code, CONSOLE_FOREGROUND) < 0) {
175                 return -1;
176             }
177
178             struct ParseResult parse_result = read_expr_from_string(console->gc,
179                                                                     source_code);
180
181             if (parse_result.is_error) {
182                 if (log_push_line(console->log, parse_result.error_message, CONSOLE_ERROR)) {
183                     return -1;
184                 }
185
186                 edit_field_clean(console->edit_field);
187
188                 return 0;
189             }
190
191             struct EvalResult eval_result = eval(
192                 console->gc,
193                 &console->scope,
194                 parse_result.expr);
195
196             if (expr_as_sexpr(
197                     eval_result.expr,
198                     console->eval_result,
199                     CONSOLE_EVAL_RESULT_SIZE) < 0) {
200                 return -1;
201             }
202
203             if (log_push_line(console->log,
204                               console->eval_result,
205                               eval_result.is_error ?
206                               CONSOLE_ERROR :
207                               CONSOLE_FOREGROUND)) {
208                 return -1;
209             }
210
211             gc_collect(console->gc, console->scope.expr);
212             edit_field_clean(console->edit_field);
213         } return 0;
214
215         case SDLK_UP: {
216             edit_field_replace(
217                 console->edit_field,
218                 history_current(console->history));
219             history_prev(console->history);
220         } return 0;
221
222         case SDLK_DOWN: {
223             edit_field_replace(
224                 console->edit_field,
225                 history_current(console->history));
226             history_next(console->history);
227         } return 0;
228         }
229         break;
230     }
231
232     return edit_field_handle_event(console->edit_field, event);
233 }
234
235 int console_render(const Console *console,
236                    SDL_Renderer *renderer)
237 {
238     /* TODO(#364): console doesn't have any padding around the edit fields */
239     SDL_Rect view_port;
240     SDL_RenderGetViewport(renderer, &view_port);
241
242     const float e = console->a * (2 - console->a);
243     const float y = -(1.0f - e) * CONSOLE_HEIGHT;
244
245     if (fill_rect(renderer,
246                   rect(0.0f, y,
247                        (float) view_port.w,
248                        CONSOLE_HEIGHT),
249                   CONSOLE_BACKGROUND) < 0) {
250         return -1;
251     }
252
253     if (log_render(console->log,
254                    renderer,
255                    vec(0.0f, y)) < 0) {
256         return -1;
257     }
258
259     if (edit_field_render(console->edit_field,
260                           renderer,
261                           vec(0.0f, y + LOG_HEIGHT)) < 0) {
262         return -1;
263     }
264
265     return 0;
266 }
267
268 int console_update(Console *console, float delta_time)
269 {
270     assert(console);
271
272     /* TODO(#366): console slide down animation doesn't have any easing */
273
274     if (console->a < 1.0f) {
275         console->a += 1.0f / SLIDE_DOWN_TIME * delta_time;
276
277         if (console->a > 1.0f) {
278             console->a = 1.0f;
279         }
280     }
281
282     return 0;
283 }
284
285 void console_slide_down(Console *console)
286 {
287     assert(console);
288     console->a = 0.0f;
289 }