Stop hardcoding important quivers: move them to data/
[clav.git] / ui-sdl.c
blob8d658298e377e37a6ba138445b46ccc2d4fd0438
1 /*
2 * Copyright (c) 2016, S. Gilles <sgilles@math.umd.edu>
4 * Permission to use, copy, modify, and/or distribute this software
5 * for any purpose with or without fee is hereby granted, provided
6 * that the above copyright notice and this permission notice appear
7 * in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <errno.h>
19 #include <math.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
24 #include <SDL.h>
25 #include <SDL_ttf.h>
27 #include "file-selection.h"
28 #include "macros.h"
29 #include "quiver.h"
30 #include "ui.h"
32 #define TICKS_PER_FRAME (1000 / 60)
34 #define TRIG_PRECALC_NUM 4
36 /* What clicking does */
37 static enum ui_action {
38 UIA_NONE = 0,
39 UIA_ASK_QUIT,
40 UIA_DEC_FATNESS,
41 UIA_DELETE,
42 UIA_ENTER_LOAD,
43 UIA_ENTER_SAVE,
44 UIA_INC_FATNESS,
45 UIA_MUTATE,
46 UIA_NEW_EDGE_1,
47 UIA_NEW_EDGE_2,
48 UIA_NEW_H_EDGE_1,
49 UIA_NEW_H_EDGE_2,
50 UIA_NEW_VERTEX,
51 UIA_LEN
52 } ui_action;
54 /* The window we'll be using */
55 static SDL_Window *sdl_win;
57 /* The renderer we'll be using */
58 static SDL_Renderer *sdl_renderer;
60 /* How to limit the framerate */
61 static Uint32 frame_start_ticks;
63 /* Whether the scene needs to be drawn again */
64 static uint_fast8_t redraw;
66 /* The informative texture */
67 static SDL_Texture *selected_info;
69 /* Buffer for event queue */
70 static struct ui_event *eq_buf;
72 /* Current max length of event queue */
73 static size_t eq_len;
75 /* Current head of queue */
76 static size_t eq_head;
78 /* Current tail of queue */
79 static size_t eq_tail;
81 /* The quiver we'll be using */
82 static struct quiver *q;
84 /* Width of drawing area in pixels */
85 static int da_pix_width;
87 /* Height of drawing area in pixels */
88 static int da_pix_height;
90 /* The background color */
91 static SDL_Color color_bg = { .r = 0xe2, .g = 0xe2, .b = 0xe2, .a = 0xff };
93 /* The normal vertex color */
94 static SDL_Color color_v = { .r = 0x82, .g = 0x82, .b = 0xb2, .a = 0xff };
96 /* The vertex color for preview vertices */
97 static SDL_Color color_v_preview = { .r = 0x82, .g = 0x82, .b = 0xb2, .a =
98 0x40 };
100 /* The normal vertex outline color */
101 static SDL_Color color_outline = { .r = 0x12, .g = 0x12, .b = 0x12, .a = 0x80 };
103 /* The vertex outline color for preview vertices */
104 static SDL_Color color_outline_preview = { .r = 0x12, .g = 0x12, .b = 0x12, .a =
105 0x20 };
107 /* The selected vertex outline color */
108 static SDL_Color color_outline_sel = { .r = 0x42, .g = 0x42, .b = 0xe2, .a =
109 0x80 };
111 /* The font color */
112 static SDL_Color color_font = { .r = 0x12, .g = 0x12, .b = 0x12, .a = 0x80 };
114 /* The normal edge color */
115 static SDL_Color color_e_normal = { .r = 0x12, .g = 0x12, .b = 0x12, .a =
116 0xff };
118 /* The half edge color */
119 static SDL_Color color_e_half = { .r = 0x12, .g = 0x12, .b = 0x12, .a = 0x60 };
121 /* The abnormal edge color */
122 static SDL_Color color_e_abnormal = { .r = 0xd0, .g = 0x12, .b = 0x12, .a =
123 0xf0 };
125 /* The selected edge color */
126 static SDL_Color color_e_sel = { .r = 0xb2, .g = 0xb2, .b = 0xe2, .a = 0x40 };
128 /* The font for node names, instructions, etc */
129 static TTF_Font *normal_font;
131 /* The base font size */
132 static uint_fast8_t base_font_size = 12;
134 /* How much space (pixels) between lower left of screen and bottom text */
135 static unsigned int text_border_padding = 24;
137 /* Strings to display at bottom of screen */
138 static const char *bottom_string[] = {
139 /* */
140 [UIA_NONE] = "[m] Mutate\n[v] Create new vertex\n"
141 "[d] Delete vertex/edge\n[e] Add edge\n"
142 "[h] Add half edge\n[f] Increase vertex fatness\n"
143 "[g] Decrease vertex fatness\n[s] Save quiver\n"
144 "[l] Load quiver\n[q] Quit", /* */
145 [UIA_NEW_VERTEX] = "[Mouse1] Create new vertex\n[ESC] Cancel", /* */
146 [UIA_ASK_QUIT] = "Quit?\n\n[y] Confirm\n[n] Cancel", /* */
147 [UIA_DEC_FATNESS] = "[Mouse1] Decrease fatness\n[ESC] Cancel", /* */
148 [UIA_DELETE] = "[Mouse1] Delete vertex/edge\n[ESC] Cancel", /* */
149 [UIA_ENTER_LOAD] = "[Enter] Load from\n[ESC] Cancel", /* */
150 [UIA_ENTER_SAVE] = "[Enter] Save to\n[ESC] Cancel", /* */
151 [UIA_INC_FATNESS] = "[Mouse1] Increase fatness\n[ESC] Cancel", /* */
152 [UIA_MUTATE] = "[Mouse1] Mutate at vertex\n[ESC] Cancel", /* */
153 [UIA_NEW_EDGE_1] = "[Mouse1] Select start\n[ESC] Cancel", /* */
154 [UIA_NEW_EDGE_2] = "[Mouse1] Select end\n[ESC] Cancel", /* */
155 [UIA_NEW_H_EDGE_1] = "[Mouse1] Select start\n[ESC] Cancel", /* */
156 [UIA_NEW_H_EDGE_2] = "[Mouse1] Select end\n[ESC] Cancel", /* */
157 [UIA_LEN] = "" /* */
160 /* The texture containing what to show on the bottom */
161 static SDL_Texture *bottom_text[UIA_LEN];
163 /* The textures containing the names of all vertices - indexed just like q */
164 static SDL_Texture **vertex_names;
166 /* The number of elements of vertex_names */
167 static size_t vertex_names_len;
169 /* Drawing offset x */
170 static int offset_x;
172 /* Drawing offset x */
173 static int offset_y;
175 /* How wide (in pixels) a fatness 1 node should be */
176 static unsigned int base_node_radius = 8;
178 /* How much (in pixels) a node should widen for each fatness level */
179 static unsigned int node_radius_per_fatness = 7;
181 /* How wide (in pixels) the outline should be */
182 static int outline_width = 2;
184 /* How wide (in pixels) the arrowheads should be */
185 static int arrow_length = 7;
187 /* How narrow the arrowheads should be */
188 static double arrow_angle = 6.5 * M_PI / 8.0;
190 /* How close to an arrow (in pixels) for it to be selected */
191 static int edge_margin = 5;
193 /* If we're interacting with a vertex, which one */
194 static size_t selected_vertex = (size_t) -1;
196 /* If we're interacting with an edge, the i */
197 static size_t selected_edge_i = (size_t) -1;
199 /* If we're interacting with an edge, the j */
200 static size_t selected_edge_j = (size_t) -1;
202 /* If we're adding an edge, the last vertex we clicked on */
203 static size_t last_clicked_vertex = (size_t) -1;
205 /* x-coordinate of last mouse position */
206 static int last_mouse_x = -1;
208 /* y-coordinate of last mouse position */
209 static int last_mouse_y = -1;
211 /* Maximum length of an input string we'll accept */
212 static size_t max_input_size = 1 << 12;
214 /* Current position in input */
215 static size_t input_idx;
217 /* Current length of string held in input */
218 static size_t input_len;
220 /* If a string is being typed in, what it is (in UTF-8 as per SDL2) */
221 static char *input;
223 /* Input, but with `Filename: ' prepended to it (for rendering) */
224 static char *input_with_prefix;
226 /* Texture for what user is currently entering */
227 static SDL_Texture *input_texture;
229 /* Whether we need to re-render the input */
230 static uint_fast8_t rerender_input_string;
232 /* To prevent awkward repeats in SDL events, rate-limit `s' and `l' */
233 static uint_fast8_t save_load_delay;
235 /* If something needs to be freed next frame, but not before */
236 static void *free_this;
238 /* sine tables */
239 static double precalc_sins[TRIG_PRECALC_NUM];
241 /* cosine tables */
242 static double precalc_coss[TRIG_PRECALC_NUM];
244 /* Precalculate sines and cosines */
245 static void precalc_trig(void)
247 precalc_coss[0] = 0.0;
248 precalc_sins[0] = 1.0;
250 for (size_t j = 1; j < TRIG_PRECALC_NUM - 1; ++j) {
251 double theta = (M_PI * j) / (2 * (TRIG_PRECALC_NUM - 1));
253 precalc_sins[j] = sin(theta);
254 precalc_coss[j] = cos(theta);
257 precalc_coss[TRIG_PRECALC_NUM - 1] = 0.0;
258 precalc_sins[TRIG_PRECALC_NUM - 1] = 1.0;
261 /* GCD */
262 static int gcd(uint_fast8_t x, uint_fast8_t y)
264 uint_fast8_t r = 0;
266 if (!x &&
267 !y) {
268 return 1;
271 while (y) {
272 r = x % y;
273 x = y;
274 y = r;
277 return x;
280 /* Allocate and print a rational in the way a user expects */
281 static int pretty_fraction(struct rational *r, char **out)
283 int ret = 0;
285 if (r->q == 1) {
286 size_t len = snprintf(0, 0, "%d", (int) r->p);
288 if (!(*out = malloc(len + 1))) {
289 ret = errno;
290 perror(L("malloc"));
291 goto done;
294 sprintf(*out, "%d", (int) r->p);
295 goto done;
298 size_t len = snprintf(0, 0, "%d/%u", (int) r->p, (unsigned int) r->q);
300 if (!(*out = malloc(len + 1))) {
301 ret = errno;
302 perror(L("malloc"));
303 goto done;
306 sprintf(*out, "%d/%u", (int) r->p, (unsigned int) r->q);
307 done:
309 return ret;
312 /* Render text to texture */
313 static int render_text(const char *text, SDL_Texture **out)
315 if (!out) {
316 return 0;
319 if (*out) {
320 SDL_DestroyTexture(*out);
323 int ret = 0;
324 SDL_Surface *surf = 0;
326 if (!(surf = TTF_RenderUTF8_Blended_Wrapped(normal_font, text,
327 color_font, 800))) {
328 fprintf(stderr, L("TTF_RenderUTF8_Shaded(): %s\n"),
329 TTF_GetError());
330 ret = ENOMEDIUM;
331 goto done;
334 if (!(*out = SDL_CreateTextureFromSurface(sdl_renderer, surf))) {
335 fprintf(stderr, L("SDL_CreateTextureFromSurface(): %s\n"),
336 SDL_GetError());
337 ret = ENOMEDIUM;
338 goto done;
341 done:
342 SDL_FreeSurface(surf);
344 return ret;
347 /* Load fonts */
348 static int load_fonts(void)
350 int ret = 0;
352 if (normal_font) {
353 TTF_CloseFont(normal_font);
354 normal_font = 0;
357 if (!(normal_font = TTF_OpenFont(FONT_PATH, base_font_size))) {
358 ret = ENOMEDIUM;
359 fprintf(stderr, L("TTF_OpenFont(): %s\n"), TTF_GetError());
360 goto done;
363 for (size_t j = 0; j < UIA_LEN; ++j) {
364 if ((ret = render_text(bottom_string[j], &bottom_text[j]))) {
365 goto done;
369 done:
371 if (ret) {
372 if (normal_font) {
373 TTF_CloseFont(normal_font);
376 normal_font = 0;
379 return ret;
382 /* Insert str into input at given position */
383 static void text_input(const char *str)
385 size_t l = strlen(str);
387 if (l + input_len >= max_input_size) {
388 return;
391 for (size_t k = input_len; k > input_idx; --k) {
392 input[k + l] = input[k];
395 /* Special-case for input_idx: 0 prohibits `>=' above */
396 input[input_idx + l] = input[input_idx];
398 for (size_t k = 0; k < l; ++k) {
399 input[input_idx + k] = str[k];
402 input_idx += l;
403 input_len += l;
404 rerender_input_string = 1;
407 /* Move input cursor left */
408 static void text_input_left(void)
410 if (input_idx > 0) {
411 input_idx--;
414 while (input_idx > 0) {
415 unsigned char c = input[input_idx];
417 if ((c & 0xc0) == 0x80) {
418 input_idx--;
419 } else {
420 break;
425 /* Move input cursor right */
426 static void text_input_right(void)
428 size_t adv = 1;
429 unsigned char c = input[input_idx];
431 if ((c & 0x80) == 0x0) {
432 adv = 1;
433 } else if ((c & 0xe0) == 0xc0) {
434 adv = 2;
435 } else if ((c & 0xf0) == 0xe0) {
436 adv = 3;
437 } else if ((c & 0xf8) == 0xf0) {
438 adv = 4;
441 if (input_idx + adv <= input_len) {
442 input_idx += adv;
446 /* Backspace */
447 static void text_input_bs(void)
449 if (!input_idx) {
450 return;
453 size_t bs = 1;
455 while (input_idx > bs) {
456 unsigned char c = input[input_idx - bs];
458 if ((c & 0xc0) == 0x80) {
459 bs++;
460 } else {
461 break;
465 for (size_t k = input_idx - bs; k <= input_len - bs; k++) {
466 input[k] = input[k + bs];
469 input_len -= bs;
470 input_idx -= bs;
471 rerender_input_string = 1;
474 /* Convert `internal coordinates' to pixel coordinates */
475 static void internal_to_pixel_xy(int in_x, int in_y, int *out_x, int *out_y)
477 *out_x = in_x + offset_x;
478 *out_y = in_y + offset_y;
481 /* Convert pixel coordinates to `internal coordinates' */
482 static void pixel_to_internal_xy(int in_x, int in_y, int *out_x, int *out_y)
484 *out_x = in_x - offset_x;
485 *out_y = in_y - offset_y;
488 /* Set selected_vertex and selected_edge_{i,j} */
489 static int recalculate_selected_items(int mx, int my)
491 int x = 0;
492 int y = 0;
493 int ret = 0;
494 char *s = 0;
495 char *sij = 0;
496 char *sji = 0;
498 pixel_to_internal_xy(mx, my, &x, &y);
499 size_t last_vertex = selected_vertex;
500 size_t last_edge_i = selected_edge_i;
501 size_t last_edge_j = selected_edge_j;
503 selected_vertex = (size_t) -1;
504 selected_edge_i = (size_t) -1;
505 selected_edge_j = (size_t) -1;
507 for (size_t j = q->v_num; j > 0; --j) {
508 struct vertex *v = &(q->v[j - 1]);
509 int r = base_node_radius + v->fatness * node_radius_per_fatness;
511 if (x > v->x - r &&
512 x < v->x + r &&
513 y > v->y - r &&
514 y < v->y + r) {
515 selected_vertex = j - 1;
516 goto compute_str;
520 for (size_t j = 1; j < q->v_num; ++j) {
521 struct vertex *v1 = &(q->v[j]);
523 for (size_t i = 0; i < j; ++i) {
524 if (!q->e[i * q->v_len + j].p &&
525 !q->e[j * q->v_len + i].p) {
526 continue;
529 struct vertex *v2 = &(q->v[i]);
531 if ((x - edge_margin > v1->x &&
532 x - edge_margin > v2->x) ||
533 (x + edge_margin < v1->x &&
534 x + edge_margin < v2->x) ||
535 (y - edge_margin > v1->y &&
536 y - edge_margin > v2->y) ||
537 (y + edge_margin < v1->y &&
538 y + edge_margin < v2->y)) {
539 continue;
542 if (v1->x == v2->x) {
543 if (x + edge_margin > v1->x &&
544 x - edge_margin < v1->x) {
545 selected_edge_i = i;
546 selected_edge_j = j;
547 goto compute_str;
549 } else if (v1->y == v2->y) {
550 if (y + edge_margin > v1->y &&
551 y - edge_margin < v1->y) {
552 selected_edge_i = i;
553 selected_edge_j = j;
554 goto compute_str;
556 } else {
557 double m1 = ((double) (v2->y - v1->y)) /
558 ((double) (v2->x - v1->x));
559 double m2 = -1.0 / m1;
560 double xint = ((double) y - (double) v1->y +
561 m1 * v1->x - m2 * x) / (m1 - m2);
562 double yint = m1 * xint - m1 * v1->x +
563 (double) v1->y;
565 if ((x - xint) * (x - xint) + (y - yint) * (y -
566 yint)
567 < edge_margin * edge_margin) {
568 selected_edge_i = i;
569 selected_edge_j = j;
570 goto compute_str;
576 compute_str:
578 if (selected_vertex != last_vertex &&
579 selected_vertex != (size_t) -1) {
580 struct vertex *v = &(q->v[selected_vertex]);
581 size_t len = snprintf(0, 0,
582 "Name: %s\nFatness: %d\nPosition: (%d,%d)",
583 v->name,
584 (int) v->fatness, v->x, v->y);
586 if (!(s = malloc(len + 1))) {
587 ret = errno;
588 perror(L("malloc"));
589 goto done;
592 sprintf(s, "Name: %s\nFatness: %d\nPosition: (%d,%d)",
593 v->name, (int) v->fatness, v->x, v->y);
595 if ((ret = render_text(s, &selected_info))) {
596 goto done;
598 } else if ((selected_edge_i != last_edge_i ||
599 selected_edge_j != last_edge_j) &&
600 selected_edge_i != (size_t) -1 &&
601 selected_edge_j != (size_t) -1) {
602 struct vertex *i = &(q->v[selected_edge_i]);
603 struct vertex *j = &(q->v[selected_edge_j]);
604 struct rational *eij = &(q->e[selected_edge_i * q->v_len +
605 selected_edge_j]);
606 struct rational *eji = &(q->e[selected_edge_j * q->v_len +
607 selected_edge_i]);
609 if ((ret = pretty_fraction(eij, &sij))) {
610 goto done;
613 if ((ret = pretty_fraction(eji, &sji))) {
614 goto done;
617 size_t len = snprintf(0, 0,
618 "%s \u2192 %s: %s\n%s \u2192 %s: %s",
619 i->name, j->name, sij,
620 j->name, i->name, sji);
622 if (!(s = malloc(len + 1))) {
623 ret = errno;
624 perror(L("malloc"));
625 goto done;
628 sprintf(s, "%s \u2192 %s: %s\n%s \u2192 %s: %s", i->name,
629 j->name, sij, j->name, i->name, sji);
631 if ((ret = render_text(s, &selected_info))) {
632 goto done;
636 done:
637 free(s);
638 free(sij);
639 free(sji);
641 return ret;
644 /* Render vertex names as textures */
645 static int render_vertex_names(void)
647 if (vertex_names) {
648 for (size_t j = 0; j < vertex_names_len; ++j) {
649 SDL_DestroyTexture(vertex_names[j]);
650 vertex_names[j] = 0;
654 if (!q->v_num) {
655 return 0;
658 if (!(vertex_names = realloc(vertex_names, q->v_num *
659 sizeof(*vertex_names)))) {
660 int sv_err = errno;
662 perror(L("realloc()"));
663 vertex_names_len = 0;
665 return sv_err;
668 vertex_names_len = q->v_num;
670 for (size_t j = 0; j < vertex_names_len; ++j) {
671 vertex_names[j] = 0;
674 for (size_t j = 0; j < vertex_names_len; ++j) {
675 int ret = 0;
677 if ((ret = render_text(q->v[j].name, &(vertex_names[j])))) {
678 return ret;
682 return 0;
685 /* Get information about the window */
686 static void react_to_window_resized(void)
688 int old_pix_width = da_pix_width;
689 int old_pix_height = da_pix_height;
691 SDL_GetWindowSize(sdl_win, &da_pix_width, &da_pix_height);
693 if (old_pix_width == da_pix_width &&
694 old_pix_height == da_pix_height) {
695 return;
698 offset_x += (da_pix_width - old_pix_width) / 2;
699 offset_y += (da_pix_height - old_pix_height) / 2;
702 /* Pop from queue */
703 static void eq_pop(struct ui_event *out)
705 if (eq_head == eq_tail) {
706 *out = (struct ui_event) { 0 };
708 return;
711 memcpy(out, eq_buf + eq_head, sizeof *out);
712 eq_buf[eq_head] = (struct ui_event) { 0 };
713 eq_head = (eq_head + 1) % eq_len;
716 /* Push into queue */
717 static int eq_push(struct ui_event *in)
719 if (((eq_tail + 1) % eq_len) == eq_head) {
720 void *newmem;
722 if ((eq_len * sizeof *in) >= ((size_t) -1) / 2) {
723 fprintf(stderr, L(
724 "eq_push: Impossibly large buffer\n"));
726 return ENOMEM;
729 if (!(newmem = realloc(eq_buf, (eq_len * 2) *
730 sizeof *eq_buf))) {
731 int sv_err = errno;
733 perror(L("realloc"));
735 return sv_err;
738 eq_buf = (struct ui_event *) newmem;
739 eq_len *= 2;
742 memcpy(eq_buf + eq_tail, in, sizeof *in);
743 eq_tail = (eq_tail + 1) % eq_len;
745 return 0;
748 /* Initialize SDL */
749 int ui_init(struct quiver *i_q)
751 int ret;
753 q = i_q;
754 size_t padding = strlen("Filename: ");
756 if (!(input_with_prefix = malloc(max_input_size + padding))) {
757 ret = errno;
758 perror(L("malloc"));
760 return ret;
763 strcpy(input_with_prefix, "Filename: ");
764 input = input_with_prefix + padding;
765 input_idx = 0;
766 input_len = 0;
768 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
769 fprintf(stderr, L("SDL_Init(): %s\n"), SDL_GetError());
771 return ENOMEDIUM;
774 sdl_win = SDL_CreateWindow("Cluster Algebra Visualizer",
775 SDL_WINDOWPOS_UNDEFINED,
776 SDL_WINDOWPOS_UNDEFINED, 1000, 1000,
777 SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
779 if (!sdl_win) {
780 fprintf(stderr, L("SDL_CreateWindow(): %s\n"), SDL_GetError());
782 return ENOMEDIUM;
785 sdl_renderer = SDL_CreateRenderer(sdl_win, -1,
786 SDL_RENDERER_ACCELERATED);
788 if (!sdl_renderer) {
789 fprintf(stderr, L("SDL_CreateRenderer(): %s\n"),
790 SDL_GetError());
792 return ENOMEDIUM;
795 if (TTF_Init() < 0) {
796 fprintf(stderr, L("TTF_Init(): %s\n"), TTF_GetError());
798 return ENOMEDIUM;
801 if ((ret = load_fonts())) {
802 goto done;
805 if ((ret = render_vertex_names())) {
806 goto done;
809 if ((ret = SDL_SetRenderDrawColor(sdl_renderer, color_bg.r, color_bg.g,
810 color_bg.b, color_bg.a))) {
811 fprintf(stderr, L("SDL_SetRenderDrawColor(): %s\n"),
812 SDL_GetError());
813 goto done;
816 if ((ret = SDL_RenderClear(sdl_renderer))) {
817 fprintf(stderr, L("SDL_RenderClear(): %s\n"), SDL_GetError());
818 goto done;
821 SDL_RenderPresent(sdl_renderer);
822 react_to_window_resized();
824 /* Set up queue for returning data */
825 if (!(eq_buf = calloc(2, sizeof *eq_buf))) {
826 ret = errno;
827 perror(L("malloc"));
828 goto done;
831 eq_len = 2;
832 eq_head = 0;
833 eq_tail = 0;
835 /* Sines and cosines for drawing circles */
836 precalc_trig();
837 done:
839 return ret;
842 /* Deal with the fact that the quiver was changed */
843 int ui_respond_quiver_change(void)
845 return render_vertex_names();
848 /* Tear down SDL */
849 int ui_teardown(void)
851 if (vertex_names) {
852 for (size_t j = 0; j < vertex_names_len; ++j) {
853 SDL_DestroyTexture(vertex_names[j]);
854 vertex_names[j] = 0;
858 for (size_t j = 0; j < UIA_LEN; ++j) {
859 SDL_DestroyTexture(bottom_text[j]);
860 bottom_text[j] = 0;
863 if (selected_info) {
864 SDL_DestroyTexture(selected_info);
865 selected_info = 0;
868 if (input_texture) {
869 SDL_DestroyTexture(input_texture);
872 if (normal_font) {
873 TTF_CloseFont(normal_font);
874 normal_font = 0;
877 if (sdl_win) {
878 SDL_DestroyWindow(sdl_win);
881 free(input_with_prefix);
882 input = 0;
883 SDL_Quit();
885 return 0;
888 /* Record that a frame has been started */
889 int ui_start_frame(void)
891 int ret = 0;
892 int rho = 0;
893 SDL_Rect r = { 0 };
894 Uint32 dummy_format;
895 int dummy_access;
896 int tex_w;
897 int tex_h;
899 frame_start_ticks = SDL_GetTicks();
901 if (!redraw) {
902 goto done;
905 redraw = 0;
907 /* Draw the damn thing */
908 SDL_SetRenderDrawColor(sdl_renderer, color_bg.r, color_bg.g, color_bg.b,
909 color_bg.a);
910 SDL_RenderClear(sdl_renderer);
912 /* Special case for if text input is going on */
913 if (rerender_input_string) {
914 rerender_input_string = 0;
916 if ((ret = render_text(input_with_prefix, &input_texture))) {
917 goto done;
921 /* Draw each edge */
922 for (size_t j = 0; j < q->v_num; ++j) {
923 for (size_t i = 0; i < j; ++i) {
924 /* First, determine if we're looking at a half-edge or a full-edge */
925 int d = gcd(q->v[i].fatness, q->v[j].fatness);
926 struct rational *eij = &(q->e[i * q->v_len + j]);
927 struct rational *eji = &(q->e[j * q->v_len + i]);
928 int cx = 0;
929 int cy = 0;
930 int cx2 = 0;
931 int cy2 = 0;
932 double theta = 0.0;
934 if (!eij->p &&
935 !eji->p) {
936 continue;
939 internal_to_pixel_xy(q->v[i].x, q->v[i].y, &cx, &cy);
940 internal_to_pixel_xy(q->v[j].x, q->v[j].y, &cx2, &cy2);
942 if (selected_edge_i == i &&
943 selected_edge_j == j) {
944 if ((ret = SDL_SetRenderDrawColor(sdl_renderer,
945 color_e_sel.r,
946 color_e_sel.g,
947 color_e_sel.b,
948 color_e_sel.a)))
950 fprintf(stderr, L(
951 "SDL_RenderDrawColor(): %s\n"),
952 SDL_GetError());
953 goto done;
956 for (int id = -edge_margin; id < edge_margin;
957 ++id) {
958 for (int jd = -edge_margin; jd <
959 edge_margin; ++jd) {
960 if ((ret = SDL_RenderDrawLine(
961 sdl_renderer, cx +
962 id, cy + jd,
963 cx2 + id, cy2 +
964 jd))) {
965 fprintf(stderr, L(
966 "SDL_RenderDrawLine(): %s\n"),
967 SDL_GetError());
968 goto done;
974 /* This is the (eij)/dj = -(eji)/di condition */
975 if (eij->p * q->v[i].fatness * eji->q != -eji->p *
976 q->v[j].fatness * eij->q) {
977 ret = SDL_SetRenderDrawColor(sdl_renderer,
978 color_e_abnormal.r,
979 color_e_abnormal.g,
980 color_e_abnormal.b,
981 color_e_abnormal.a);
982 } else if (abs(eij->p) * d == q->v[j].fatness *
983 eij->q) {
984 ret = SDL_SetRenderDrawColor(sdl_renderer,
985 color_e_normal.r,
986 color_e_normal.g,
987 color_e_normal.b,
988 color_e_normal.a);
989 } else if (2 * abs(eij->p) * d == q->v[j].fatness *
990 eij->q) {
991 ret = SDL_SetRenderDrawColor(sdl_renderer,
992 color_e_half.r,
993 color_e_half.g,
994 color_e_half.b,
995 color_e_half.a);
996 } else {
997 ret = SDL_SetRenderDrawColor(sdl_renderer,
998 color_e_abnormal.r,
999 color_e_abnormal.g,
1000 color_e_abnormal.b,
1001 color_e_abnormal.a);
1004 if (ret) {
1005 fprintf(stderr, L(
1006 "SDL_SetRenderDrawColor(): %s\n"),
1007 SDL_GetError());
1008 goto done;
1011 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy, cx2,
1012 cy2))) {
1013 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1014 SDL_GetError());
1015 goto done;
1018 if (cx == cx2) {
1019 theta = (cy2 > cy) ? M_PI / 2.0 : -M_PI / 2.0;
1020 } else {
1021 theta = atan2f(cy2 - cy, cx2 - cx);
1024 if ((eij->p < 0)) {
1025 theta += M_PI;
1028 cx = (cx + cx2) / 2;
1029 cy = (cy + cy2) / 2;
1030 cx2 = cx + arrow_length * cos(theta + arrow_angle);
1031 cy2 = cy + arrow_length * sin(theta + arrow_angle);
1033 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy, cx2,
1034 cy2))) {
1035 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1036 SDL_GetError());
1037 goto done;
1040 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy, cx +
1041 arrow_length * cos(theta -
1042 arrow_angle),
1043 cy +
1044 arrow_length * sin(theta -
1045 arrow_angle))))
1047 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1048 SDL_GetError());
1049 goto done;
1054 /* Draw each vertex as a box */
1055 for (size_t j = 0; j < q->v_num; ++j) {
1056 struct vertex *v = &(q->v[j]);
1057 int cx = 0;
1058 int cy = 0;
1060 internal_to_pixel_xy(v->x, v->y, &cx, &cy);
1062 /* Central square */
1063 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_NONE);
1064 SDL_SetRenderDrawColor(sdl_renderer, color_v.r, color_v.g,
1065 color_v.b, color_v.a);
1066 rho = base_node_radius + node_radius_per_fatness * v->fatness;
1067 r.x = cx - rho;
1068 r.y = cy - rho;
1069 r.w = 2 * rho;
1070 r.h = 2 * rho;
1071 SDL_RenderFillRect(sdl_renderer, &r);
1073 /* Outline */
1074 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
1076 if (j == selected_vertex) {
1077 SDL_SetRenderDrawColor(sdl_renderer,
1078 color_outline_sel.r,
1079 color_outline_sel.g,
1080 color_outline_sel.b,
1081 color_outline_sel.a);
1082 } else {
1083 SDL_SetRenderDrawColor(sdl_renderer, color_outline.r,
1084 color_outline.g, color_outline.b,
1085 color_outline.a);
1088 r.x = cx - rho;
1089 r.y = cy - rho;
1090 r.w = 2 * rho - outline_width;
1091 r.h = outline_width;
1092 SDL_RenderFillRect(sdl_renderer, &r);
1093 r.x = cx + rho - outline_width;
1094 r.y = cy - rho;
1095 r.w = outline_width;
1096 r.h = 2 * rho - outline_width;
1097 SDL_RenderFillRect(sdl_renderer, &r);
1098 r.x = cx - rho + outline_width;
1099 r.y = cy + rho - outline_width;
1100 r.w = 2 * rho - outline_width;
1101 r.h = outline_width;
1102 SDL_RenderFillRect(sdl_renderer, &r);
1103 r.x = cx - rho;
1104 r.y = cy - rho + outline_width;
1105 r.w = outline_width;
1106 r.h = 2 * rho - outline_width;
1107 SDL_RenderFillRect(sdl_renderer, &r);
1109 /* Text */
1110 if (j >= vertex_names_len) {
1111 fprintf(stderr, L(
1112 "render_vertex_names() was not called, somehow\n"));
1113 ret = EINVAL;
1114 goto done;
1117 if (SDL_QueryTexture(vertex_names[j], &dummy_format,
1118 &dummy_access, &tex_w, &tex_h)) {
1119 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1120 SDL_GetError());
1121 ret = ENOMEDIUM;
1122 goto done;
1125 r.x = cx - tex_w / 2;
1126 r.y = cy - tex_h / 2;
1127 r.w = tex_w;
1128 r.h = tex_h;
1129 SDL_RenderCopy(sdl_renderer, vertex_names[j], 0, &r);
1132 /* If adding a new vertex, draw preview */
1133 if (ui_action == UIA_NEW_VERTEX &&
1134 last_mouse_x != -1 &&
1135 last_mouse_y != -1) {
1136 /* Central square */
1137 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_NONE);
1138 SDL_SetRenderDrawColor(sdl_renderer, color_v_preview.r,
1139 color_v_preview.g, color_v_preview.b,
1140 color_v_preview.a);
1141 rho = base_node_radius;
1142 r.x = last_mouse_x - rho;
1143 r.y = last_mouse_y - rho;
1144 r.w = 2 * rho;
1145 r.h = 2 * rho;
1146 SDL_RenderFillRect(sdl_renderer, &r);
1148 /* Outline */
1149 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
1150 SDL_SetRenderDrawColor(sdl_renderer, color_outline_preview.r,
1151 color_outline_preview.g,
1152 color_outline_preview.b,
1153 color_outline_preview.a);
1154 r.x = last_mouse_x - rho;
1155 r.y = last_mouse_y - rho;
1156 r.w = 2 * rho - outline_width;
1157 r.h = outline_width;
1158 SDL_RenderFillRect(sdl_renderer, &r);
1159 r.x = last_mouse_x + rho - outline_width;
1160 r.y = last_mouse_y - rho;
1161 r.w = outline_width;
1162 r.h = 2 * rho - outline_width;
1163 SDL_RenderFillRect(sdl_renderer, &r);
1164 r.x = last_mouse_x - rho + outline_width;
1165 r.y = last_mouse_y + rho - outline_width;
1166 r.w = 2 * rho - outline_width;
1167 r.h = outline_width;
1168 SDL_RenderFillRect(sdl_renderer, &r);
1169 r.x = last_mouse_x - rho;
1170 r.y = last_mouse_y - rho + outline_width;
1171 r.w = outline_width;
1172 r.h = 2 * rho - outline_width;
1173 SDL_RenderFillRect(sdl_renderer, &r);
1176 /* If adding a new edge, draw possible */
1177 if ((ui_action == UIA_NEW_EDGE_2 ||
1178 ui_action == UIA_NEW_H_EDGE_2) &&
1180 /* last_clicked_vertex != (size_t) -1 && */
1181 last_mouse_x != -1 &&
1182 last_mouse_y != -1) {
1183 int cx = 0;
1184 int cy = 0;
1186 if ((ret = SDL_SetRenderDrawColor(sdl_renderer,
1187 color_e_normal.r,
1188 color_e_normal.g,
1189 color_e_normal.b,
1190 color_e_normal.a))) {
1191 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1192 SDL_GetError());
1193 goto done;
1196 internal_to_pixel_xy(q->v[last_clicked_vertex].x,
1197 q->v[last_clicked_vertex].y, &cx, &cy);
1199 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy,
1200 last_mouse_x, last_mouse_y))) {
1201 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1202 SDL_GetError());
1203 goto done;
1207 /* Bottom text */
1208 if (SDL_QueryTexture(bottom_text[ui_action], &dummy_format,
1209 &dummy_access, &tex_w, &tex_h)) {
1210 fprintf(stderr, L("SDL_QueryTexture(): %s\n"), SDL_GetError());
1211 ret = ENOMEDIUM;
1212 goto done;
1215 r.x = text_border_padding;
1216 r.y = da_pix_height - tex_h - text_border_padding;
1217 r.w = tex_w;
1218 r.h = tex_h;
1219 SDL_RenderCopy(sdl_renderer, bottom_text[ui_action], 0, &r);
1221 /* If something is selected */
1222 if (selected_info &&
1223 (selected_vertex != (size_t) -1 ||
1224 (selected_edge_i != (size_t) -1 &&
1225 selected_edge_j != (size_t) -1))) {
1226 if (SDL_QueryTexture(selected_info, &dummy_format,
1227 &dummy_access, &tex_w, &tex_h)) {
1228 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1229 SDL_GetError());
1230 ret = ENOMEDIUM;
1231 goto done;
1234 r.x = text_border_padding;
1235 r.y = text_border_padding;
1236 r.w = tex_w;
1237 r.h = tex_h;
1238 SDL_RenderCopy(sdl_renderer, selected_info, 0, &r);
1241 /* If user is entering text */
1242 if (ui_action == UIA_ENTER_SAVE ||
1243 ui_action == UIA_ENTER_LOAD) {
1244 if (SDL_QueryTexture(bottom_text[ui_action], &dummy_format,
1245 &dummy_access, &tex_w, &tex_h)) {
1246 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1247 SDL_GetError());
1248 ret = ENOMEDIUM;
1249 goto done;
1252 r.x = text_border_padding;
1253 r.y = da_pix_height - tex_h - text_border_padding;
1255 if (SDL_QueryTexture(input_texture, &dummy_format,
1256 &dummy_access, &tex_w, &tex_h)) {
1257 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1258 SDL_GetError());
1259 ret = ENOMEDIUM;
1260 goto done;
1263 r.y -= (tex_h + text_border_padding);
1264 r.w = tex_w;
1265 r.h = tex_h;
1266 SDL_RenderCopy(sdl_renderer, input_texture, 0, &r);
1268 /* Now draw a cursor */
1269 char store_idxchar = input[input_idx];
1271 input[input_idx] = '\0';
1273 if ((ret = TTF_SizeUTF8(normal_font, input_with_prefix, &tex_w,
1274 &tex_h))) {
1275 fprintf(stderr, L("TTF_SizeText(): %s\n"),
1276 TTF_GetError());
1277 goto done;
1280 input[input_idx] = store_idxchar;
1282 if ((ret = SDL_SetRenderDrawColor(sdl_renderer, color_font.r,
1283 color_font.g, color_font.b,
1284 color_font.a))) {
1285 fprintf(stderr, L("SDL_SetRenderDrawColor(): %s\n"),
1286 SDL_GetError());
1287 goto done;
1290 if ((ret = SDL_RenderDrawLine(sdl_renderer, r.x + tex_w, r.y,
1291 r.x + tex_w, r.y + tex_h))) {
1292 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1293 SDL_GetError());
1294 goto done;
1298 done:
1300 return ret;
1303 /* Draw a frame, possibly sleeping for framelimit */
1304 int ui_finish_frame(void)
1306 int ret = 0;
1307 struct ui_event ui_e = { 0 };
1308 SDL_Event sdl_e = { 0 };
1309 Uint32 now = 0;
1310 uint_fast8_t save_requested = 0;
1311 uint_fast8_t load_requested = 0;
1313 if (free_this) {
1314 free(free_this);
1315 free_this = 0;
1318 if (save_load_delay) {
1319 save_load_delay--;
1322 SDL_RenderPresent(sdl_renderer);
1324 /* Handle user input */
1325 while (SDL_PollEvent(&sdl_e) != 0) {
1326 redraw = 1;
1327 SDL_Keycode k = 0;
1329 switch (sdl_e.type) {
1330 case SDL_QUIT:
1331 ui_e = (struct ui_event) { .type = ET_QUIT };
1332 ret = eq_push(&ui_e);
1333 break;
1334 case SDL_TEXTINPUT:
1335 text_input(sdl_e.text.text);
1336 break;
1337 case SDL_KEYDOWN:
1339 if (sdl_e.key.repeat) {
1340 break;
1343 k = sdl_e.key.keysym.sym;
1345 switch (ui_action) {
1346 case UIA_ENTER_SAVE:
1347 case UIA_ENTER_LOAD:
1349 if (k == SDLK_ESCAPE) {
1350 SDL_StopTextInput();
1351 ui_action = UIA_NONE;
1352 } else if (k == SDLK_RETURN ||
1353 k == SDLK_RETURN2) {
1354 SDL_StopTextInput();
1355 ui_e = (struct ui_event) { .str =
1356 input };
1357 ui_e.type = (ui_action ==
1358 UIA_ENTER_SAVE) ? ET_SAVE :
1359 ET_LOAD;
1360 ret = eq_push(&ui_e);
1361 ui_action = UIA_NONE;
1362 } else if (k == SDLK_LEFT) {
1363 text_input_left();
1364 } else if (k == SDLK_RIGHT) {
1365 text_input_right();
1366 } else if (k == SDLK_BACKSPACE) {
1367 text_input_bs();
1370 break;
1371 case UIA_NONE:
1373 if (k == SDLK_q) {
1374 ui_action = UIA_ASK_QUIT;
1375 } else if (k == SDLK_m) {
1376 ui_action = UIA_MUTATE;
1377 } else if (k == SDLK_v) {
1378 ui_action = UIA_NEW_VERTEX;
1379 } else if (k == SDLK_d) {
1380 ui_action = UIA_DELETE;
1381 } else if (k == SDLK_e) {
1382 ui_action = UIA_NEW_EDGE_1;
1383 } else if (k == SDLK_h) {
1384 ui_action = UIA_NEW_H_EDGE_1;
1385 } else if (k == SDLK_f) {
1386 ui_action = UIA_INC_FATNESS;
1387 } else if (k == SDLK_g) {
1388 ui_action = UIA_DEC_FATNESS;
1389 } else if (k == SDLK_l &&
1390 !save_load_delay) {
1391 /* Don't load - SDL_KEYUP repeats */
1392 load_requested = 1;
1393 } else if (k == SDLK_s &&
1394 !save_load_delay) {
1395 save_requested = 1;
1398 break;
1399 case UIA_ASK_QUIT:
1401 if (k == SDLK_n ||
1402 k == SDLK_ESCAPE) {
1403 ui_action = UIA_NONE;
1404 } else if (k == SDLK_y) {
1405 ui_e = (struct ui_event) { .type =
1406 ET_QUIT };
1407 ret = eq_push(&ui_e);
1408 ui_action = UIA_NONE;
1411 break;
1412 default:
1414 if (k == SDLK_q ||
1415 k == SDLK_ESCAPE) {
1416 ui_action = UIA_NONE;
1419 break;
1422 break;
1423 case SDL_WINDOWEVENT:
1425 if (sdl_e.window.event == SDL_WINDOWEVENT_RESIZED ||
1426 sdl_e.window.event == SDL_WINDOWEVENT_MAXIMIZED ||
1427 sdl_e.window.event == SDL_WINDOWEVENT_RESTORED) {
1428 react_to_window_resized();
1429 } else if (sdl_e.window.event ==
1430 SDL_WINDOWEVENT_LEAVE) {
1431 /* This tells the dragging code to not respond */
1432 last_mouse_x = -1;
1433 last_mouse_y = -1;
1436 break;
1437 case SDL_MOUSEMOTION:
1439 if (sdl_e.motion.state & SDL_BUTTON_LMASK) {
1440 int x = sdl_e.motion.x;
1441 int y = sdl_e.motion.y;
1443 if (last_mouse_x >= 0 &&
1444 last_mouse_y >= 0) {
1445 if (selected_vertex != (size_t) -1) {
1446 q->v[selected_vertex].x += (x -
1447 last_mouse_x);
1448 q->v[selected_vertex].y += (y -
1449 last_mouse_y);
1450 } else {
1451 offset_x += (x - last_mouse_x);
1452 offset_y += (y - last_mouse_y);
1455 } else {
1456 recalculate_selected_items(sdl_e.motion.x,
1457 sdl_e.motion.y);
1460 last_mouse_x = sdl_e.motion.x;
1461 last_mouse_y = sdl_e.motion.y;
1462 break;
1463 case SDL_MOUSEBUTTONUP:
1465 if ((sdl_e.button.state & SDL_BUTTON_LMASK) &&
1466 ui_action != UIA_NEW_VERTEX) {
1467 last_mouse_x = -1;
1468 last_mouse_y = -1;
1471 recalculate_selected_items(sdl_e.button.x,
1472 sdl_e.button.y);
1473 break;
1474 case SDL_MOUSEBUTTONDOWN:
1476 if (!(sdl_e.button.state & SDL_BUTTON_LMASK)) {
1477 break;
1480 if (ui_action != UIA_NEW_VERTEX) {
1481 last_mouse_x = -1;
1482 last_mouse_y = -1;
1485 switch (ui_action) {
1486 case UIA_MUTATE:
1488 if (selected_vertex == (size_t) -1) {
1489 break;
1492 ui_e = (struct ui_event) {
1493 /* */
1494 .type = ET_MUTATE, .idx_1 =
1495 selected_vertex
1497 ret = eq_push(&ui_e);
1498 ui_action = UIA_NONE;
1499 break;
1500 case UIA_NEW_VERTEX:
1502 if (selected_vertex != (size_t) -1 ||
1503 selected_edge_i != (size_t) -1 ||
1504 selected_edge_j != (size_t) -1) {
1505 break;
1508 int cx = sdl_e.button.x - offset_x;
1509 int cy = sdl_e.button.y - offset_y;
1511 ui_e = (struct ui_event) {
1512 /* */
1513 .type = ET_NEW_VERTEX, .int_1 = cx,
1514 .int_2 = cy
1516 ret = eq_push(&ui_e);
1517 ui_action = UIA_NONE;
1518 break;
1519 case UIA_NEW_EDGE_1:
1520 case UIA_NEW_H_EDGE_1:
1522 if (selected_vertex == (size_t) -1) {
1523 ui_action = UIA_NONE;
1524 break;
1527 last_clicked_vertex = selected_vertex;
1528 ui_action = (ui_action == UIA_NEW_EDGE_1 ?
1529 UIA_NEW_EDGE_2 : UIA_NEW_H_EDGE_2);
1530 break;
1531 case UIA_NEW_EDGE_2:
1532 case UIA_NEW_H_EDGE_2:
1534 if (selected_vertex == (size_t) -1 ||
1535 selected_vertex == last_clicked_vertex) {
1536 ui_action = UIA_NONE;
1537 break;
1540 ui_e = (struct ui_event) {
1541 /* */
1542 .type = ET_NEW_EDGE, .idx_1 =
1543 last_clicked_vertex, .idx_2 =
1544 selected_vertex, .a = 1, .b =
1545 (ui_action == UIA_NEW_EDGE_2 ?
1546 1 : 2)
1548 ret = eq_push(&ui_e);
1549 ui_action = UIA_NONE;
1550 break;
1551 case UIA_DELETE:
1553 if (selected_vertex != (size_t) -1) {
1554 ui_e = (struct ui_event) {
1555 /* */
1556 .type = ET_DELETE_VERTEX,
1557 .idx_1 = selected_vertex
1559 ret = eq_push(&ui_e);
1560 } else if (selected_edge_i != (size_t) -1 &&
1561 selected_edge_j != (size_t) -1) {
1562 ui_e = (struct ui_event) {
1563 /* */
1564 .type = ET_DELETE_EDGE, .idx_1 =
1565 selected_edge_i,
1566 .idx_2 =
1567 selected_edge_j
1569 ret = eq_push(&ui_e);
1572 ui_action = UIA_NONE;
1573 break;
1574 case UIA_INC_FATNESS:
1575 case UIA_DEC_FATNESS:
1577 if (selected_vertex == (size_t) -1) {
1578 break;
1581 ui_e = (struct ui_event) {
1582 /* */
1583 .type = ET_CHANGE_FATNESS, .idx_1 =
1584 selected_vertex, .int_1 =
1585 (ui_action ==
1586 UIA_INC_FATNESS
1587 ? 1 : -1)
1589 ret = eq_push(&ui_e);
1590 ui_action = UIA_NONE;
1591 break;
1592 case UIA_NONE:
1593 case UIA_ASK_QUIT:
1594 case UIA_ENTER_SAVE:
1595 case UIA_ENTER_LOAD:
1596 case UIA_LEN:
1597 break;
1600 break;
1603 if (ret) {
1604 goto done;
1608 if (load_requested ||
1609 save_requested) {
1610 save_load_delay = 30;
1611 rerender_input_string = 1;
1612 char *f = 0;
1613 int r = 0;
1615 if (load_requested) {
1616 r = choose_load_file(&f);
1617 } else {
1618 r = choose_save_file(&f);
1621 if (!r) {
1622 if (f) {
1623 ui_e = (struct ui_event) { .str = f };
1624 ui_e.type = load_requested ? ET_LOAD : ET_SAVE;
1626 /* f is freed on next ui_finish_frame */
1627 free_this = f;
1628 ret = eq_push(&ui_e);
1630 } else {
1631 ui_action = load_requested ? UIA_ENTER_LOAD :
1632 UIA_ENTER_SAVE;
1633 SDL_StartTextInput();
1634 input_idx = 0;
1635 input_len = 0;
1636 input[0] = '\0';
1640 /* framelimit */
1641 now = SDL_GetTicks();
1643 if (frame_start_ticks < now) {
1644 Uint32 elapsed_time = now - frame_start_ticks;
1646 if (elapsed_time < TICKS_PER_FRAME) {
1647 SDL_Delay(TICKS_PER_FRAME - elapsed_time);
1651 done:
1653 return ret;
1656 /* Return an event to the main loop */
1657 int ui_get_event(struct ui_event *e, uint_fast8_t *more)
1659 eq_pop(e);
1660 *more = eq_head != eq_tail;
1662 return 0;