]> git.lizzy.rs Git - nothing.git/blob - src/ui/console.c
(#813) Implement player_layer_render
[nothing.git] / src / ui / console.c
1 #include "system/stacktrace.h"
2
3 #include "ebisp/gc.h"
4 #include "ebisp/interpreter.h"
5 #include "ebisp/parser.h"
6 #include "ebisp/scope.h"
7 #include "ebisp/std.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"
19
20 #define FONT_WIDTH_SCALE 3.0f
21 #define FONT_HEIGHT_SCALE 3.0f
22
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)
27
28 #define CONSOLE_HEIGHT (CONSOLE_LOG_HEIGHT + PROMPT_HEIGHT)
29
30 #define SLIDE_DOWN_TIME 0.4f
31
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))
36
37 #define CONSOLE_EVAL_RESULT_SIZE 256
38
39 struct Console
40 {
41     Lt *lt;
42     Gc *gc;
43     struct Scope scope;
44     Edit_field *edit_field;
45     Console_Log *console_log;
46     History *history;
47     float a;
48     char *eval_result;
49 };
50
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 */
55
56 Console *create_console(Broadcast *broadcast,
57                         const Sprite_font *font)
58 {
59     Lt *lt = create_lt();
60
61
62     Console *console = PUSH_LT(lt, nth_calloc(1, sizeof(Console)), free);
63     if (console == NULL) {
64         RETURN_LT(lt, NULL);
65     }
66     console->lt = lt;
67
68     console->gc = PUSH_LT(lt, create_gc(), destroy_gc);
69     if (console->gc == NULL) {
70         RETURN_LT(lt, NULL);
71     }
72
73     console->scope.expr = CONS(console->gc,
74                                NIL(console->gc),
75                                NIL(console->gc));
76
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);
81
82     console->edit_field = PUSH_LT(
83         lt,
84         create_edit_field(
85             vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
86             CONSOLE_FOREGROUND),
87         destroy_edit_field);
88     if (console->edit_field == NULL) {
89         RETURN_LT(lt, NULL);
90     }
91
92     console->console_log = PUSH_LT(
93         lt,
94         create_console_log(
95             font,
96             vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
97             CONSOLE_LOG_CAPACITY),
98         destroy_console_log);
99
100     console->a = 0;
101
102     console->eval_result = PUSH_LT(
103         lt,
104         nth_calloc(1, sizeof(char) * CONSOLE_EVAL_RESULT_SIZE),
105         free);
106     if (console->eval_result == NULL) {
107         RETURN_LT(lt, NULL);
108     }
109     memset(console->eval_result, 0, sizeof(char) * CONSOLE_EVAL_RESULT_SIZE);
110
111     console->history = PUSH_LT(
112         lt,
113         create_history(HISTORY_CAPACITY),
114         destroy_history);
115     if (console->history == NULL) {
116         RETURN_LT(lt, NULL);
117     }
118
119     return console;
120 }
121
122 void destroy_console(Console *console)
123 {
124     trace_assert(console);
125     RETURN_LT0(console->lt);
126 }
127
128 static int console_eval_input(Console *console)
129 {
130     const char *source_code = edit_field_as_text(console->edit_field);
131
132     /* TODO(#387): console pushes empty strings to the history */
133     if (history_push(console->history, source_code) < 0) {
134         return -1;
135     }
136
137     if (console_log_push_line(console->console_log, source_code, CONSOLE_FOREGROUND) < 0) {
138         return -1;
139     }
140
141     while (*source_code != 0) {
142         struct ParseResult parse_result = read_expr_from_string(console->gc,
143                                                                 source_code);
144
145         if (parse_result.is_error) {
146             if (console_log_push_line(console->console_log, parse_result.error_message, CONSOLE_ERROR)) {
147                 return -1;
148             }
149
150             edit_field_clean(console->edit_field);
151
152             return 0;
153         }
154
155         struct EvalResult eval_result = eval(
156             console->gc,
157             &console->scope,
158             parse_result.expr);
159
160         if (expr_as_sexpr(
161                 eval_result.expr,
162                 console->eval_result,
163                 CONSOLE_EVAL_RESULT_SIZE) < 0) {
164             return -1;
165         }
166
167         if (console_log_push_line(console->console_log,
168                           console->eval_result,
169                           eval_result.is_error ?
170                           CONSOLE_ERROR :
171                           CONSOLE_FOREGROUND)) {
172             return -1;
173         }
174
175         source_code = next_token(parse_result.end).begin;
176     }
177
178     gc_collect(console->gc, console->scope.expr);
179     edit_field_clean(console->edit_field);
180
181     return 0;
182 }
183
184 int console_handle_event(Console *console,
185                          const SDL_Event *event)
186 {
187     switch(event->type) {
188     case SDL_KEYDOWN: {
189         switch(event->key.keysym.sym) {
190         case SDLK_RETURN:
191             return console_eval_input(console);
192
193         case SDLK_UP:
194             edit_field_replace(
195                 console->edit_field,
196                 history_current(console->history));
197             history_prev(console->history);
198             return 0;
199
200         case SDLK_DOWN:
201             edit_field_replace(
202                 console->edit_field,
203                 history_current(console->history));
204             history_next(console->history);
205             return 0;
206         }
207
208         return edit_field_keyboard(console->edit_field, &event->key);
209     } break;
210
211     case SDL_TEXTINPUT:
212         return edit_field_text_input(console->edit_field, &event->text);
213
214     default: {}
215     }
216
217     return 0;
218 }
219
220 int console_render(const Console *console,
221                    Camera *camera,
222                    SDL_Renderer *renderer)
223 {
224     /* TODO(#364): console doesn't have any padding around the edit fields */
225     SDL_Rect view_port;
226     SDL_RenderGetViewport(renderer, &view_port);
227
228     const float e = console->a * (2 - console->a);
229     const float y = -(1.0f - e) * CONSOLE_HEIGHT;
230
231     if (fill_rect(renderer,
232                   rect(0.0f, y,
233                        (float) view_port.w,
234                        CONSOLE_HEIGHT),
235                   CONSOLE_BACKGROUND) < 0) {
236         return -1;
237     }
238
239     if (console_log_render(console->console_log,
240                    renderer,
241                    vec(0.0f, y)) < 0) {
242         return -1;
243     }
244
245     if (edit_field_render(console->edit_field,
246                           camera,
247                           vec(0.0f, y + CONSOLE_LOG_HEIGHT)) < 0) {
248         return -1;
249     }
250
251     return 0;
252 }
253
254 int console_update(Console *console, float delta_time)
255 {
256     trace_assert(console);
257
258     if (console->a < 1.0f) {
259         console->a += 1.0f / SLIDE_DOWN_TIME * delta_time;
260
261         if (console->a > 1.0f) {
262             console->a = 1.0f;
263         }
264     }
265
266     return 0;
267 }
268
269 void console_slide_down(Console *console)
270 {
271     trace_assert(console);
272     console->a = 0.0f;
273 }