Add Zickert's B2 quivers
[clav.git] / ui-sdl.c
blobdb478141921f2aade1e2376c5d119c957823b870
1 /*
2 * Copyright (c) 2017, 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 #ifndef M_PI
35 #define M_PI (3.14159265358979323846)
36 #endif
38 /* What clicking does */
39 static enum ui_action {
40 UIA_NONE = 0,
41 UIA_ASK_QUIT,
42 UIA_DEC_FATNESS,
43 UIA_DELETE,
44 UIA_ENTER_LOAD,
45 UIA_ENTER_RENAME,
46 UIA_ENTER_SAVE,
47 UIA_INC_FATNESS,
48 UIA_MUTATE,
49 UIA_NEW_EDGE_1,
50 UIA_NEW_EDGE_2,
51 UIA_NEW_H_EDGE_1,
52 UIA_NEW_H_EDGE_2,
53 UIA_NEW_VERTEX,
54 UIA_RENAME,
55 UIA_LEN
56 } ui_action;
58 /* The window we'll be using */
59 static SDL_Window *sdl_win;
61 /* The renderer we'll be using */
62 static SDL_Renderer *sdl_renderer;
64 /* How to limit the framerate */
65 static Uint32 frame_start_ticks;
67 /* Whether the scene needs to be drawn again */
68 static uint_fast8_t redraw;
70 /* Whether a vertex was just dragged (necessitating info redraw) */
71 static uint_fast8_t dragged_selected_vertex;
73 /* The informative texture */
74 static SDL_Texture *selected_info;
76 /* Buffer for event queue */
77 static struct ui_event *eq_buf;
79 /* Current max length of event queue */
80 static size_t eq_len;
82 /* Current head of queue */
83 static size_t eq_head;
85 /* Current tail of queue */
86 static size_t eq_tail;
88 /* The quiver we'll be using */
89 static struct quiver *q;
91 /* Width of drawing area in pixels */
92 static int da_pix_width;
94 /* Height of drawing area in pixels */
95 static int da_pix_height;
97 /* The background color */
98 static SDL_Color color_bg = { .r = 0xe2, .g = 0xe2, .b = 0xe2, .a = 0xff };
100 /* The normal vertex color */
101 static SDL_Color color_v = { .r = 0x82, .g = 0x82, .b = 0xb2, .a = 0xff };
103 /* The vertex color for preview vertices */
104 static SDL_Color color_v_preview = { .r = 0x82, .g = 0x82, .b = 0xb2, .a =
105 0x40 };
107 /* The normal vertex outline color */
108 static SDL_Color color_outline = { .r = 0x12, .g = 0x12, .b = 0x12, .a = 0x80 };
110 /* The vertex outline color for preview vertices */
111 static SDL_Color color_outline_preview = { .r = 0x12, .g = 0x12, .b = 0x12, .a =
112 0x20 };
114 /* The selected vertex outline color */
115 static SDL_Color color_outline_sel = { .r = 0x42, .g = 0x42, .b = 0xe2, .a =
116 0x80 };
118 /* The font color */
119 static SDL_Color color_font = { .r = 0x12, .g = 0x12, .b = 0x12, .a = 0x80 };
121 /* The normal edge color */
122 static SDL_Color color_e_normal = { .r = 0x12, .g = 0x12, .b = 0x12, .a =
123 0xff };
125 /* The half edge color */
126 static SDL_Color color_e_half = { .r = 0x12, .g = 0x12, .b = 0x12, .a = 0x60 };
128 /* The abnormal edge color */
129 static SDL_Color color_e_abnormal = { .r = 0xd0, .g = 0x12, .b = 0x12, .a =
130 0xf0 };
132 /* The selected edge color */
133 static SDL_Color color_e_sel = { .r = 0xb2, .g = 0xb2, .b = 0xe2, .a = 0x40 };
135 /* The font for node names, instructions, etc */
136 static TTF_Font *normal_font;
138 /* The base font size */
139 static uint_fast8_t base_font_size = 12;
141 /* How much space (pixels) between lower left of screen and bottom text */
142 static unsigned int text_border_padding = 24;
144 /* Strings to display at bottom of screen */
145 static const char *bottom_string[] = {
146 /* */
147 [UIA_NONE] = "[m] Mutate\n[v] Create new vertex\n"
148 "[d] Delete vertex/edge\n[e] Add edge\n"
149 "[h] Add half edge\n[f] Increase vertex fatness\n"
150 "[g] Decrease vertex fatness\n[r] Rename vertex\n"
151 "[s] Save quiver\n[l] Load quiver\n[q] Quit", /* */
152 [UIA_ASK_QUIT] = "Quit?\n\n[y] Confirm\n[n] Cancel", /* */
153 [UIA_DEC_FATNESS] = "[Mouse1] Decrease fatness\n[ESC] Cancel", /* */
154 [UIA_DELETE] = "[Mouse1] Delete vertex/edge\n[ESC] Cancel", /* */
155 [UIA_ENTER_LOAD] = "[Enter] Load from\n[ESC] Cancel", /* */
156 [UIA_ENTER_RENAME] = "[Enter] Rename\n[ESC] Cancel", /* */
157 [UIA_ENTER_SAVE] = "[Enter] Save to\n[ESC] Cancel", /* */
158 [UIA_INC_FATNESS] = "[Mouse1] Increase fatness\n[ESC] Cancel", /* */
159 [UIA_MUTATE] = "[Mouse1] Mutate at vertex\n[ESC] Cancel", /* */
160 [UIA_NEW_EDGE_1] = "[Mouse1] Select start\n[ESC] Cancel", /* */
161 [UIA_NEW_EDGE_2] = "[Mouse1] Select end\n[ESC] Cancel", /* */
162 [UIA_NEW_H_EDGE_1] = "[Mouse1] Select start\n[ESC] Cancel", /* */
163 [UIA_NEW_H_EDGE_2] = "[Mouse1] Select end\n[ESC] Cancel", /* */
164 [UIA_NEW_VERTEX] = "[Mouse1] Create new vertex\n[ESC] Cancel", /* */
165 [UIA_RENAME] = "[Mouse1] Rename vertex\n[ESC] Cancel", /* */
166 [UIA_LEN] = "" /* */
169 /* The texture containing what to show on the bottom */
170 static SDL_Texture *bottom_text[UIA_LEN];
172 /* The textures containing the names of all vertices - indexed just like q */
173 static SDL_Texture **vertex_names;
175 /* The number of elements of vertex_names */
176 static size_t vertex_names_len;
178 /* Drawing offset x */
179 static int offset_x;
181 /* Drawing offset x */
182 static int offset_y;
184 /* How wide (in pixels) a fatness 1 node should be */
185 static unsigned int base_node_radius = 8;
187 /* How much (in pixels) a node should widen for each fatness level */
188 static unsigned int node_radius_per_fatness = 7;
190 /* How wide (in pixels) the outline should be */
191 static int outline_width = 2;
193 /* How wide (in pixels) the arrowheads should be */
194 static int arrow_length = 7;
196 /* How narrow the arrowheads should be */
197 static double arrow_angle = 6.5 * M_PI / 8.0;
199 /* How close to an arrow (in pixels) for it to be selected */
200 static int edge_margin = 5;
202 /* If we're interacting with a vertex, which one */
203 static size_t selected_vertex = (size_t) -1;
205 /* If we're interacting with an edge, the i */
206 static size_t selected_edge_i = (size_t) -1;
208 /* If we're interacting with an edge, the j */
209 static size_t selected_edge_j = (size_t) -1;
211 /* If we're adding an edge, the last vertex we clicked on */
212 static size_t last_clicked_vertex = (size_t) -1;
214 /* x-coordinate of last mouse position */
215 static int last_mouse_x = -1;
217 /* y-coordinate of last mouse position */
218 static int last_mouse_y = -1;
220 /* Maximum length of an input string we'll accept */
221 static size_t max_input_size = 1 << 10;
223 /* Current position in input */
224 static size_t input_idx;
226 /* Current length of string held in input */
227 static size_t input_len;
229 /* If a string is being typed in, what it is (in UTF-8 as per SDL2) */
230 static char *input;
232 /* Input, with `Filename:' or `New name:' prepended (for rendering) */
233 static char *input_with_prefix;
235 /* Texture for what user is currently entering */
236 static SDL_Texture *input_texture;
238 /* Whether we need to re-render the input */
239 static uint_fast8_t rerender_input_string;
241 /* To prevent awkward repeats in SDL events, rate-limit `s' and `l' */
242 static uint_fast8_t save_load_delay;
244 /* If something needs to be freed next frame, but not before */
245 static void *free_this;
247 /* GCD */
248 static int gcd(uint_fast8_t x, uint_fast8_t y)
250 uint_fast8_t r = 0;
252 if (!x &&
253 !y) {
254 return 1;
257 while (y) {
258 r = x % y;
259 x = y;
260 y = r;
263 return x;
266 /* Allocate and print a rational in the way a user expects */
267 static int pretty_fraction(struct rational *r, char **out)
269 int ret = 0;
271 if (r->q == 1) {
272 size_t len = snprintf(0, 0, "%d", (int) r->p);
274 if (!(*out = malloc(len + 1))) {
275 ret = errno;
276 perror(L("malloc"));
277 goto done;
280 sprintf(*out, "%d", (int) r->p);
281 goto done;
284 size_t len = snprintf(0, 0, "%d/%u", (int) r->p, (unsigned int) r->q);
286 if (!(*out = malloc(len + 1))) {
287 ret = errno;
288 perror(L("malloc"));
289 goto done;
292 sprintf(*out, "%d/%u", (int) r->p, (unsigned int) r->q);
293 done:
295 return ret;
298 /* Render text to texture */
299 static int render_text(const char *text, SDL_Texture **out)
301 if (!out) {
302 return 0;
305 if (*out) {
306 SDL_DestroyTexture(*out);
309 int ret = 0;
310 SDL_Surface *surf = 0;
312 if (!(surf = TTF_RenderUTF8_Blended_Wrapped(normal_font, text,
313 color_font, 800))) {
314 fprintf(stderr, L("TTF_RenderUTF8_Shaded(): %s\n"),
315 TTF_GetError());
316 ret = ENOMEDIUM;
317 goto done;
320 if (!(*out = SDL_CreateTextureFromSurface(sdl_renderer, surf))) {
321 fprintf(stderr, L("SDL_CreateTextureFromSurface(): %s\n"),
322 SDL_GetError());
323 ret = ENOMEDIUM;
324 goto done;
327 done:
328 SDL_FreeSurface(surf);
330 return ret;
333 /* Load fonts */
334 static int load_fonts(void)
336 int ret = 0;
338 if (normal_font) {
339 TTF_CloseFont(normal_font);
340 normal_font = 0;
343 if (!(normal_font = TTF_OpenFont(FONT_PATH, base_font_size))) {
344 ret = ENOMEDIUM;
345 fprintf(stderr, L("TTF_OpenFont(): %s\n"), TTF_GetError());
346 goto done;
349 for (size_t j = 0; j < UIA_LEN; ++j) {
350 if ((ret = render_text(bottom_string[j], &bottom_text[j]))) {
351 goto done;
355 done:
357 if (ret) {
358 if (normal_font) {
359 TTF_CloseFont(normal_font);
362 normal_font = 0;
365 return ret;
368 /* Insert str into input at given position */
369 static void text_input(const char *str)
371 size_t l = strlen(str);
373 if (l + input_len + 1 >= max_input_size) {
374 return;
377 for (size_t k = input_len; k > input_idx; --k) {
378 input[k + l] = input[k];
381 /* Special-case for input_idx: 0 prohibits `>=' above */
382 input[input_idx + l] = input[input_idx];
384 for (size_t k = 0; k < l; ++k) {
385 input[input_idx + k] = str[k];
388 input_idx += l;
389 input_len += l;
390 rerender_input_string = 1;
393 /* Move input cursor left */
394 static void text_input_left(void)
396 if (input_idx > 0) {
397 input_idx--;
400 while (input_idx > 0) {
401 unsigned char c = input[input_idx];
403 if ((c & 0xc0) == 0x80) {
404 input_idx--;
405 } else {
406 break;
411 /* Move input cursor right */
412 static void text_input_right(void)
414 size_t adv = 1;
415 unsigned char c = input[input_idx];
417 if ((c & 0x80) == 0x0) {
418 adv = 1;
419 } else if ((c & 0xe0) == 0xc0) {
420 adv = 2;
421 } else if ((c & 0xf0) == 0xe0) {
422 adv = 3;
423 } else if ((c & 0xf8) == 0xf0) {
424 adv = 4;
427 if (input_idx + adv <= input_len) {
428 input_idx += adv;
432 /* Backspace */
433 static void text_input_bs(void)
435 if (!input_idx) {
436 return;
439 size_t bs = 1;
441 while (input_idx > bs) {
442 unsigned char c = input[input_idx - bs];
444 if ((c & 0xc0) == 0x80) {
445 bs++;
446 } else {
447 break;
451 for (size_t k = input_idx - bs; k <= input_len - bs; k++) {
452 input[k] = input[k + bs];
455 input_len -= bs;
456 input_idx -= bs;
457 rerender_input_string = 1;
460 /* Convert `internal coordinates' to pixel coordinates */
461 static void internal_to_pixel_xy(int in_x, int in_y, int *out_x, int *out_y)
463 *out_x = in_x + offset_x;
464 *out_y = in_y + offset_y;
467 /* Convert pixel coordinates to `internal coordinates' */
468 static void pixel_to_internal_xy(int in_x, int in_y, int *out_x, int *out_y)
470 *out_x = in_x - offset_x;
471 *out_y = in_y - offset_y;
474 /* Set selected_vertex and selected_edge_{i,j} */
475 static int recalculate_selected_items(int mx, int my)
477 int x = 0;
478 int y = 0;
479 int ret = 0;
480 char *s = 0;
481 char *sij = 0;
482 char *sji = 0;
484 pixel_to_internal_xy(mx, my, &x, &y);
485 size_t last_vertex = selected_vertex;
486 size_t last_edge_i = selected_edge_i;
487 size_t last_edge_j = selected_edge_j;
489 selected_vertex = (size_t) -1;
490 selected_edge_i = (size_t) -1;
491 selected_edge_j = (size_t) -1;
493 if (last_vertex != (size_t) -1) {
494 struct vertex *v = &(q->v[last_vertex]);
495 int r = base_node_radius + v->fatness * node_radius_per_fatness;
497 if (x > v->x - r &&
498 x < v->x + r &&
499 y > v->y - r &&
500 y < v->y + r) {
501 selected_vertex = last_vertex;
502 goto compute_str;
506 for (size_t j = q->v_num; j > 0; --j) {
507 struct vertex *v = &(q->v[j - 1]);
508 int r = base_node_radius + v->fatness * node_radius_per_fatness;
510 if (x > v->x - r &&
511 x < v->x + r &&
512 y > v->y - r &&
513 y < v->y + r) {
514 selected_vertex = j - 1;
515 goto compute_str;
519 for (size_t j = 1; j < q->v_num; ++j) {
520 struct vertex *v1 = &(q->v[j]);
522 for (size_t i = 0; i < j; ++i) {
523 if (!q->e[i * q->v_len + j].p &&
524 !q->e[j * q->v_len + i].p) {
525 continue;
528 struct vertex *v2 = &(q->v[i]);
530 if ((x - edge_margin > v1->x &&
531 x - edge_margin > v2->x) ||
532 (x + edge_margin < v1->x &&
533 x + edge_margin < v2->x) ||
534 (y - edge_margin > v1->y &&
535 y - edge_margin > v2->y) ||
536 (y + edge_margin < v1->y &&
537 y + edge_margin < v2->y)) {
538 continue;
541 if (v1->x == v2->x) {
542 if (x + edge_margin > v1->x &&
543 x - edge_margin < v1->x) {
544 selected_edge_i = i;
545 selected_edge_j = j;
546 goto compute_str;
548 } else if (v1->y == v2->y) {
549 if (y + edge_margin > v1->y &&
550 y - edge_margin < v1->y) {
551 selected_edge_i = i;
552 selected_edge_j = j;
553 goto compute_str;
555 } else {
556 double m1 = ((double) (v2->y - v1->y)) /
557 ((double) (v2->x - v1->x));
558 double m2 = -1.0 / m1;
559 double xint = ((double) y - (double) v1->y +
560 m1 * v1->x - m2 * x) / (m1 - m2);
561 double yint = m1 * xint - m1 * v1->x +
562 (double) v1->y;
564 if ((x - xint) * (x - xint) + (y - yint) * (y -
565 yint)
566 < edge_margin * edge_margin) {
567 selected_edge_i = i;
568 selected_edge_j = j;
569 goto compute_str;
575 compute_str:
577 if (selected_vertex != (size_t) -1 &&
578 (selected_vertex != last_vertex ||
579 dragged_selected_vertex)) {
580 dragged_selected_vertex = 0;
581 struct vertex *v = &(q->v[selected_vertex]);
582 size_t len = snprintf(0, 0,
583 "Name: %s\nFatness: %d\nPosition: (%d,%d)",
584 v->name,
585 (int) v->fatness, v->x, v->y);
587 if (!(s = malloc(len + 1))) {
588 ret = errno;
589 perror(L("malloc"));
590 goto done;
593 sprintf(s, "Name: %s\nFatness: %d\nPosition: (%d,%d)",
594 v->name, (int) v->fatness, v->x, v->y);
596 if ((ret = render_text(s, &selected_info))) {
597 goto done;
599 } else if ((selected_edge_i != last_edge_i ||
600 selected_edge_j != last_edge_j) &&
601 selected_edge_i != (size_t) -1 &&
602 selected_edge_j != (size_t) -1) {
603 struct vertex *i = &(q->v[selected_edge_i]);
604 struct vertex *j = &(q->v[selected_edge_j]);
605 struct rational *eij = &(q->e[selected_edge_i * q->v_len +
606 selected_edge_j]);
607 struct rational *eji = &(q->e[selected_edge_j * q->v_len +
608 selected_edge_i]);
610 if ((ret = pretty_fraction(eij, &sij))) {
611 goto done;
614 if ((ret = pretty_fraction(eji, &sji))) {
615 goto done;
618 size_t len = snprintf(0, 0,
619 "%s \u2192 %s: %s\n%s \u2192 %s: %s",
620 i->name, j->name, sij,
621 j->name, i->name, sji);
623 if (!(s = malloc(len + 1))) {
624 ret = errno;
625 perror(L("malloc"));
626 goto done;
629 sprintf(s, "%s \u2192 %s: %s\n%s \u2192 %s: %s", i->name,
630 j->name, sij, j->name, i->name, sji);
632 if ((ret = render_text(s, &selected_info))) {
633 goto done;
637 done:
638 free(s);
639 free(sij);
640 free(sji);
642 return ret;
645 /* Render vertex names as textures */
646 static int render_vertex_names(void)
648 if (vertex_names) {
649 for (size_t j = 0; j < vertex_names_len; ++j) {
650 SDL_DestroyTexture(vertex_names[j]);
651 vertex_names[j] = 0;
655 if (!q->v_num) {
656 return 0;
659 if (!(vertex_names = realloc(vertex_names, q->v_num *
660 sizeof(*vertex_names)))) {
661 int sv_err = errno;
663 perror(L("realloc()"));
664 vertex_names_len = 0;
666 return sv_err;
669 vertex_names_len = q->v_num;
671 for (size_t j = 0; j < vertex_names_len; ++j) {
672 vertex_names[j] = 0;
675 for (size_t j = 0; j < vertex_names_len; ++j) {
676 int ret = 0;
678 if ((ret = render_text(q->v[j].name, &(vertex_names[j])))) {
679 return ret;
683 return 0;
686 /* Get information about the window */
687 static void react_to_window_resized(void)
689 int old_pix_width = da_pix_width;
690 int old_pix_height = da_pix_height;
692 SDL_GetWindowSize(sdl_win, &da_pix_width, &da_pix_height);
694 if (old_pix_width == da_pix_width &&
695 old_pix_height == da_pix_height) {
696 return;
699 offset_x += (da_pix_width - old_pix_width) / 2;
700 offset_y += (da_pix_height - old_pix_height) / 2;
703 /* Pop from queue */
704 static void eq_pop(struct ui_event *out)
706 if (eq_head == eq_tail) {
707 *out = (struct ui_event) { 0 };
709 return;
712 memcpy(out, eq_buf + eq_head, sizeof *out);
713 eq_buf[eq_head] = (struct ui_event) { 0 };
714 eq_head = (eq_head + 1) % eq_len;
717 /* Push into queue */
718 static int eq_push(struct ui_event *in)
720 if (((eq_tail + 1) % eq_len) == eq_head) {
721 void *newmem;
723 if ((eq_len * sizeof *in) >= ((size_t) -1) / 2) {
724 fprintf(stderr, L(
725 "eq_push: Impossibly large buffer\n"));
727 return ENOMEM;
730 if (!(newmem = realloc(eq_buf, (eq_len * 2) *
731 sizeof *eq_buf))) {
732 int sv_err = errno;
734 perror(L("realloc"));
736 return sv_err;
739 eq_buf = (struct ui_event *) newmem;
740 eq_len *= 2;
743 memcpy(eq_buf + eq_tail, in, sizeof *in);
744 eq_tail = (eq_tail + 1) % eq_len;
746 return 0;
749 /* Initialize SDL */
750 int ui_init(struct quiver *i_q)
752 int ret;
754 q = i_q;
755 size_t padding = strlen("Filename: ");
757 if (!(input_with_prefix = malloc(max_input_size + padding))) {
758 ret = errno;
759 perror(L("malloc"));
761 return ret;
764 strcpy(input_with_prefix, "Filename: ");
765 input = input_with_prefix + padding;
766 input_idx = 0;
767 input_len = 0;
769 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
770 fprintf(stderr, L("SDL_Init(): %s\n"), SDL_GetError());
772 return ENOMEDIUM;
775 sdl_win = SDL_CreateWindow("Cluster Algebra Visualizer",
776 SDL_WINDOWPOS_UNDEFINED,
777 SDL_WINDOWPOS_UNDEFINED, 1000, 1000,
778 SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
780 if (!sdl_win) {
781 fprintf(stderr, L("SDL_CreateWindow(): %s\n"), SDL_GetError());
783 return ENOMEDIUM;
786 sdl_renderer = SDL_CreateRenderer(sdl_win, -1,
787 SDL_RENDERER_ACCELERATED);
789 if (!sdl_renderer) {
790 fprintf(stderr, L("SDL_CreateRenderer(): %s\n"),
791 SDL_GetError());
793 return ENOMEDIUM;
796 if (TTF_Init() < 0) {
797 fprintf(stderr, L("TTF_Init(): %s\n"), TTF_GetError());
799 return ENOMEDIUM;
802 if ((ret = load_fonts())) {
803 goto done;
806 if ((ret = render_vertex_names())) {
807 goto done;
810 if ((ret = SDL_SetRenderDrawColor(sdl_renderer, color_bg.r, color_bg.g,
811 color_bg.b, color_bg.a))) {
812 fprintf(stderr, L("SDL_SetRenderDrawColor(): %s\n"),
813 SDL_GetError());
814 goto done;
817 if ((ret = SDL_RenderClear(sdl_renderer))) {
818 fprintf(stderr, L("SDL_RenderClear(): %s\n"), SDL_GetError());
819 goto done;
822 SDL_RenderPresent(sdl_renderer);
823 react_to_window_resized();
825 /* Set up queue for returning data */
826 if (!(eq_buf = calloc(2, sizeof *eq_buf))) {
827 ret = errno;
828 perror(L("malloc"));
829 goto done;
832 eq_len = 2;
833 eq_head = 0;
834 eq_tail = 0;
835 done:
837 return ret;
840 /* Deal with the fact that the quiver was changed */
841 int ui_respond_quiver_change(void)
843 return render_vertex_names();
846 /* Tear down SDL */
847 int ui_teardown(void)
849 if (vertex_names) {
850 for (size_t j = 0; j < vertex_names_len; ++j) {
851 SDL_DestroyTexture(vertex_names[j]);
852 vertex_names[j] = 0;
855 free(vertex_names);
856 vertex_names = 0;
859 for (size_t j = 0; j < UIA_LEN; ++j) {
860 SDL_DestroyTexture(bottom_text[j]);
861 bottom_text[j] = 0;
864 if (selected_info) {
865 SDL_DestroyTexture(selected_info);
866 selected_info = 0;
869 if (input_texture) {
870 SDL_DestroyTexture(input_texture);
871 input_texture = 0;
874 if (normal_font) {
875 TTF_CloseFont(normal_font);
876 normal_font = 0;
879 if (sdl_win) {
880 SDL_DestroyWindow(sdl_win);
881 sdl_win = 0;
884 free(eq_buf);
885 eq_buf = 0;
886 free(input_with_prefix);
887 input_with_prefix = 0;
888 input = 0;
889 SDL_Quit();
891 return 0;
894 /* Record that a frame has been started */
895 int ui_start_frame(void)
897 int ret = 0;
898 int rho = 0;
899 SDL_Rect r = { 0 };
900 Uint32 dummy_format;
901 int dummy_access;
902 int tex_w;
903 int tex_h;
905 frame_start_ticks = SDL_GetTicks();
907 if (!redraw) {
908 goto done;
911 redraw = 0;
913 /* Draw the damn thing */
914 SDL_SetRenderDrawColor(sdl_renderer, color_bg.r, color_bg.g, color_bg.b,
915 color_bg.a);
916 SDL_RenderClear(sdl_renderer);
918 /* Special case for if text input is going on */
919 if (rerender_input_string) {
920 rerender_input_string = 0;
922 if ((ret = render_text(input_with_prefix, &input_texture))) {
923 goto done;
927 /* Draw each edge */
928 for (size_t j = 0; j < q->v_num; ++j) {
929 for (size_t i = 0; i < j; ++i) {
930 /* First, determine if we're looking at a half-edge or a full-edge */
931 int d = gcd(q->v[i].fatness, q->v[j].fatness);
932 struct rational *eij = &(q->e[i * q->v_len + j]);
933 struct rational *eji = &(q->e[j * q->v_len + i]);
934 int cx = 0;
935 int cy = 0;
936 int cx2 = 0;
937 int cy2 = 0;
938 double theta = 0.0;
940 if (!eij->p &&
941 !eji->p) {
942 continue;
945 internal_to_pixel_xy(q->v[i].x, q->v[i].y, &cx, &cy);
946 internal_to_pixel_xy(q->v[j].x, q->v[j].y, &cx2, &cy2);
948 if (selected_edge_i == i &&
949 selected_edge_j == j) {
950 if ((ret = SDL_SetRenderDrawColor(sdl_renderer,
951 color_e_sel.r,
952 color_e_sel.g,
953 color_e_sel.b,
954 color_e_sel.a)))
956 fprintf(stderr, L(
957 "SDL_RenderDrawColor(): %s\n"),
958 SDL_GetError());
959 goto done;
962 for (int id = -edge_margin; id < edge_margin;
963 ++id) {
964 for (int jd = -edge_margin; jd <
965 edge_margin; ++jd) {
966 if ((ret = SDL_RenderDrawLine(
967 sdl_renderer, cx +
968 id, cy + jd,
969 cx2 + id, cy2 +
970 jd))) {
971 fprintf(stderr, L(
972 "SDL_RenderDrawLine(): %s\n"),
973 SDL_GetError());
974 goto done;
980 /* This is the (eij)/dj = -(eji)/di condition */
981 if (eij->p * q->v[i].fatness * eji->q != -eji->p *
982 q->v[j].fatness * eij->q) {
983 ret = SDL_SetRenderDrawColor(sdl_renderer,
984 color_e_abnormal.r,
985 color_e_abnormal.g,
986 color_e_abnormal.b,
987 color_e_abnormal.a);
988 } else if (abs(eij->p) * d == q->v[j].fatness *
989 eij->q) {
990 ret = SDL_SetRenderDrawColor(sdl_renderer,
991 color_e_normal.r,
992 color_e_normal.g,
993 color_e_normal.b,
994 color_e_normal.a);
995 } else if (2 * abs(eij->p) * d == q->v[j].fatness *
996 eij->q) {
997 ret = SDL_SetRenderDrawColor(sdl_renderer,
998 color_e_half.r,
999 color_e_half.g,
1000 color_e_half.b,
1001 color_e_half.a);
1002 } else {
1003 ret = SDL_SetRenderDrawColor(sdl_renderer,
1004 color_e_abnormal.r,
1005 color_e_abnormal.g,
1006 color_e_abnormal.b,
1007 color_e_abnormal.a);
1010 if (ret) {
1011 fprintf(stderr, L(
1012 "SDL_SetRenderDrawColor(): %s\n"),
1013 SDL_GetError());
1014 goto done;
1017 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy, cx2,
1018 cy2))) {
1019 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1020 SDL_GetError());
1021 goto done;
1024 if (cx == cx2) {
1025 theta = (cy2 > cy) ? M_PI / 2.0 : -M_PI / 2.0;
1026 } else {
1027 theta = atan2f(cy2 - cy, cx2 - cx);
1030 if ((eij->p < 0)) {
1031 theta += M_PI;
1034 cx = (cx + cx2) / 2;
1035 cy = (cy + cy2) / 2;
1036 cx2 = cx + arrow_length * cos(theta + arrow_angle);
1037 cy2 = cy + arrow_length * sin(theta + arrow_angle);
1039 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy, cx2,
1040 cy2))) {
1041 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1042 SDL_GetError());
1043 goto done;
1046 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy, cx +
1047 arrow_length * cos(theta -
1048 arrow_angle),
1049 cy +
1050 arrow_length * sin(theta -
1051 arrow_angle))))
1053 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1054 SDL_GetError());
1055 goto done;
1060 /* Draw each vertex as a box */
1061 for (size_t j = 0; j < q->v_num; ++j) {
1062 struct vertex *v = &(q->v[j]);
1063 int cx = 0;
1064 int cy = 0;
1066 internal_to_pixel_xy(v->x, v->y, &cx, &cy);
1068 /* Central square */
1069 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_NONE);
1070 SDL_SetRenderDrawColor(sdl_renderer, color_v.r, color_v.g,
1071 color_v.b, color_v.a);
1072 rho = base_node_radius + node_radius_per_fatness * v->fatness;
1073 r.x = cx - rho;
1074 r.y = cy - rho;
1075 r.w = 2 * rho;
1076 r.h = 2 * rho;
1077 SDL_RenderFillRect(sdl_renderer, &r);
1079 /* Outline */
1080 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
1082 if (j == selected_vertex ||
1083 j == last_clicked_vertex) {
1084 SDL_SetRenderDrawColor(sdl_renderer,
1085 color_outline_sel.r,
1086 color_outline_sel.g,
1087 color_outline_sel.b,
1088 color_outline_sel.a);
1089 } else {
1090 SDL_SetRenderDrawColor(sdl_renderer, color_outline.r,
1091 color_outline.g, color_outline.b,
1092 color_outline.a);
1095 r.x = cx - rho;
1096 r.y = cy - rho;
1097 r.w = 2 * rho - outline_width;
1098 r.h = outline_width;
1099 SDL_RenderFillRect(sdl_renderer, &r);
1100 r.x = cx + rho - outline_width;
1101 r.y = cy - rho;
1102 r.w = outline_width;
1103 r.h = 2 * rho - outline_width;
1104 SDL_RenderFillRect(sdl_renderer, &r);
1105 r.x = cx - rho + outline_width;
1106 r.y = cy + rho - outline_width;
1107 r.w = 2 * rho - outline_width;
1108 r.h = outline_width;
1109 SDL_RenderFillRect(sdl_renderer, &r);
1110 r.x = cx - rho;
1111 r.y = cy - rho + outline_width;
1112 r.w = outline_width;
1113 r.h = 2 * rho - outline_width;
1114 SDL_RenderFillRect(sdl_renderer, &r);
1116 /* Text */
1117 if (j >= vertex_names_len) {
1118 fprintf(stderr, L(
1119 "render_vertex_names() was not called, somehow\n"));
1120 ret = EINVAL;
1121 goto done;
1124 if (SDL_QueryTexture(vertex_names[j], &dummy_format,
1125 &dummy_access, &tex_w, &tex_h)) {
1126 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1127 SDL_GetError());
1128 ret = ENOMEDIUM;
1129 goto done;
1132 r.x = cx - tex_w / 2;
1133 r.y = cy - tex_h / 2;
1134 r.w = tex_w;
1135 r.h = tex_h;
1136 SDL_RenderCopy(sdl_renderer, vertex_names[j], 0, &r);
1139 /* If adding a new vertex, draw preview */
1140 if (ui_action == UIA_NEW_VERTEX &&
1141 last_mouse_x != -1 &&
1142 last_mouse_y != -1) {
1143 /* Central square */
1144 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_NONE);
1145 SDL_SetRenderDrawColor(sdl_renderer, color_v_preview.r,
1146 color_v_preview.g, color_v_preview.b,
1147 color_v_preview.a);
1148 rho = base_node_radius;
1149 r.x = last_mouse_x - rho;
1150 r.y = last_mouse_y - rho;
1151 r.w = 2 * rho;
1152 r.h = 2 * rho;
1153 SDL_RenderFillRect(sdl_renderer, &r);
1155 /* Outline */
1156 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
1157 SDL_SetRenderDrawColor(sdl_renderer, color_outline_preview.r,
1158 color_outline_preview.g,
1159 color_outline_preview.b,
1160 color_outline_preview.a);
1161 r.x = last_mouse_x - rho;
1162 r.y = last_mouse_y - rho;
1163 r.w = 2 * rho - outline_width;
1164 r.h = outline_width;
1165 SDL_RenderFillRect(sdl_renderer, &r);
1166 r.x = last_mouse_x + rho - outline_width;
1167 r.y = last_mouse_y - rho;
1168 r.w = outline_width;
1169 r.h = 2 * rho - outline_width;
1170 SDL_RenderFillRect(sdl_renderer, &r);
1171 r.x = last_mouse_x - rho + outline_width;
1172 r.y = last_mouse_y + rho - outline_width;
1173 r.w = 2 * rho - outline_width;
1174 r.h = outline_width;
1175 SDL_RenderFillRect(sdl_renderer, &r);
1176 r.x = last_mouse_x - rho;
1177 r.y = last_mouse_y - rho + outline_width;
1178 r.w = outline_width;
1179 r.h = 2 * rho - outline_width;
1180 SDL_RenderFillRect(sdl_renderer, &r);
1183 /* If adding a new edge, draw possible */
1184 if ((ui_action == UIA_NEW_EDGE_2 ||
1185 ui_action == UIA_NEW_H_EDGE_2) &&
1187 /* last_clicked_vertex != (size_t) -1 && */
1188 last_mouse_x != -1 &&
1189 last_mouse_y != -1) {
1190 int cx = 0;
1191 int cy = 0;
1193 if ((ret = SDL_SetRenderDrawColor(sdl_renderer,
1194 color_e_normal.r,
1195 color_e_normal.g,
1196 color_e_normal.b,
1197 color_e_normal.a))) {
1198 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1199 SDL_GetError());
1200 goto done;
1203 internal_to_pixel_xy(q->v[last_clicked_vertex].x,
1204 q->v[last_clicked_vertex].y, &cx, &cy);
1206 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy,
1207 last_mouse_x, last_mouse_y))) {
1208 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1209 SDL_GetError());
1210 goto done;
1214 /* Bottom text */
1215 if (SDL_QueryTexture(bottom_text[ui_action], &dummy_format,
1216 &dummy_access, &tex_w, &tex_h)) {
1217 fprintf(stderr, L("SDL_QueryTexture(): %s\n"), SDL_GetError());
1218 ret = ENOMEDIUM;
1219 goto done;
1222 r.x = text_border_padding;
1223 r.y = da_pix_height - tex_h - text_border_padding;
1224 r.w = tex_w;
1225 r.h = tex_h;
1226 SDL_RenderCopy(sdl_renderer, bottom_text[ui_action], 0, &r);
1228 /* If something is selected */
1229 if (selected_info &&
1230 (selected_vertex != (size_t) -1 ||
1231 (selected_edge_i != (size_t) -1 &&
1232 selected_edge_j != (size_t) -1))) {
1233 if (SDL_QueryTexture(selected_info, &dummy_format,
1234 &dummy_access, &tex_w, &tex_h)) {
1235 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1236 SDL_GetError());
1237 ret = ENOMEDIUM;
1238 goto done;
1241 r.x = text_border_padding;
1242 r.y = text_border_padding;
1243 r.w = tex_w;
1244 r.h = tex_h;
1245 SDL_RenderCopy(sdl_renderer, selected_info, 0, &r);
1248 /* If user is entering text */
1249 if (ui_action == UIA_ENTER_SAVE ||
1250 ui_action == UIA_ENTER_LOAD ||
1251 ui_action == UIA_ENTER_RENAME) {
1252 if (SDL_QueryTexture(bottom_text[ui_action], &dummy_format,
1253 &dummy_access, &tex_w, &tex_h)) {
1254 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1255 SDL_GetError());
1256 ret = ENOMEDIUM;
1257 goto done;
1260 r.x = text_border_padding;
1261 r.y = da_pix_height - tex_h - text_border_padding;
1263 if (SDL_QueryTexture(input_texture, &dummy_format,
1264 &dummy_access, &tex_w, &tex_h)) {
1265 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1266 SDL_GetError());
1267 ret = ENOMEDIUM;
1268 goto done;
1271 r.y -= (tex_h + text_border_padding);
1272 r.w = tex_w;
1273 r.h = tex_h;
1274 SDL_RenderCopy(sdl_renderer, input_texture, 0, &r);
1276 /* Now draw a cursor */
1277 char store_idxchar = input[input_idx];
1279 input[input_idx] = '\0';
1281 if ((ret = TTF_SizeUTF8(normal_font, input_with_prefix, &tex_w,
1282 &tex_h))) {
1283 fprintf(stderr, L("TTF_SizeText(): %s\n"),
1284 TTF_GetError());
1285 goto done;
1288 input[input_idx] = store_idxchar;
1290 if ((ret = SDL_SetRenderDrawColor(sdl_renderer, color_font.r,
1291 color_font.g, color_font.b,
1292 color_font.a))) {
1293 fprintf(stderr, L("SDL_SetRenderDrawColor(): %s\n"),
1294 SDL_GetError());
1295 goto done;
1298 if ((ret = SDL_RenderDrawLine(sdl_renderer, r.x + tex_w, r.y,
1299 r.x + tex_w, r.y + tex_h))) {
1300 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1301 SDL_GetError());
1302 goto done;
1306 done:
1308 return ret;
1311 /* Draw a frame, possibly sleeping for framelimit */
1312 int ui_finish_frame(void)
1314 int ret = 0;
1315 struct ui_event ui_e = { 0 };
1316 SDL_Event sdl_e = { 0 };
1317 Uint32 now = 0;
1318 uint_fast8_t save_requested = 0;
1319 uint_fast8_t load_requested = 0;
1321 if (free_this) {
1322 free(free_this);
1323 free_this = 0;
1326 if (save_load_delay) {
1327 save_load_delay--;
1330 SDL_RenderPresent(sdl_renderer);
1332 /* Handle user input */
1333 while (SDL_PollEvent(&sdl_e) != 0) {
1334 redraw = 1;
1335 SDL_Keycode k = 0;
1337 switch (sdl_e.type) {
1338 case SDL_QUIT:
1339 ui_e = (struct ui_event) { .type = ET_QUIT };
1340 ret = eq_push(&ui_e);
1341 break;
1342 case SDL_TEXTINPUT:
1343 text_input(sdl_e.text.text);
1344 break;
1345 case SDL_KEYUP:
1347 if (sdl_e.key.repeat) {
1348 break;
1351 k = sdl_e.key.keysym.sym;
1353 switch (ui_action) {
1354 case UIA_ENTER_SAVE:
1355 case UIA_ENTER_LOAD:
1356 case UIA_ENTER_RENAME:
1358 if (k == SDLK_ESCAPE) {
1359 SDL_StopTextInput();
1360 ui_action = UIA_NONE;
1361 last_clicked_vertex = (size_t) -1;
1362 } else if (k == SDLK_RETURN ||
1363 k == SDLK_RETURN2) {
1364 SDL_StopTextInput();
1365 ui_e = (struct ui_event) { .str =
1366 input };
1368 if (ui_action == UIA_ENTER_SAVE) {
1369 ui_e.type = ET_SAVE;
1370 } else if (ui_action ==
1371 UIA_ENTER_LOAD) {
1372 ui_e.type = ET_LOAD;
1373 } else if (ui_action ==
1374 UIA_ENTER_RENAME) {
1375 ui_e.type = ET_RENAME;
1376 ui_e.idx_1 =
1377 last_clicked_vertex;
1380 ret = eq_push(&ui_e);
1381 ui_action = UIA_NONE;
1382 last_clicked_vertex = (size_t) -1;
1383 } else if (k == SDLK_LEFT) {
1384 text_input_left();
1385 } else if (k == SDLK_RIGHT) {
1386 text_input_right();
1387 } else if (k == SDLK_BACKSPACE) {
1388 text_input_bs();
1391 break;
1392 case UIA_NONE:
1394 if (k == SDLK_q) {
1395 ui_action = UIA_ASK_QUIT;
1396 } else if (k == SDLK_d) {
1397 ui_action = UIA_DELETE;
1398 } else if (k == SDLK_e) {
1399 ui_action = UIA_NEW_EDGE_1;
1400 } else if (k == SDLK_f) {
1401 ui_action = UIA_INC_FATNESS;
1402 } else if (k == SDLK_g) {
1403 ui_action = UIA_DEC_FATNESS;
1404 } else if (k == SDLK_h) {
1405 ui_action = UIA_NEW_H_EDGE_1;
1406 } else if (k == SDLK_m) {
1407 ui_action = UIA_MUTATE;
1408 } else if (k == SDLK_r) {
1409 ui_action = UIA_RENAME;
1410 } else if (k == SDLK_v) {
1411 ui_action = UIA_NEW_VERTEX;
1412 } else if (k == SDLK_l &&
1413 !save_load_delay) {
1414 /* Don't load - SDL_KEYUP repeats */
1415 load_requested = 1;
1416 } else if (k == SDLK_s &&
1417 !save_load_delay) {
1418 save_requested = 1;
1421 break;
1422 case UIA_ASK_QUIT:
1424 if (k == SDLK_n ||
1425 k == SDLK_ESCAPE) {
1426 ui_action = UIA_NONE;
1427 } else if (k == SDLK_y) {
1428 ui_e = (struct ui_event) { .type =
1429 ET_QUIT };
1430 ret = eq_push(&ui_e);
1431 ui_action = UIA_NONE;
1434 break;
1435 default:
1437 if (k == SDLK_q ||
1438 k == SDLK_ESCAPE) {
1439 ui_action = UIA_NONE;
1442 break;
1445 break;
1446 case SDL_WINDOWEVENT:
1448 if (sdl_e.window.event == SDL_WINDOWEVENT_RESIZED ||
1449 sdl_e.window.event == SDL_WINDOWEVENT_MAXIMIZED ||
1450 sdl_e.window.event == SDL_WINDOWEVENT_RESTORED) {
1451 react_to_window_resized();
1452 } else if (sdl_e.window.event ==
1453 SDL_WINDOWEVENT_LEAVE) {
1454 /* This tells the dragging code to not respond */
1455 last_mouse_x = -1;
1456 last_mouse_y = -1;
1459 break;
1460 case SDL_MOUSEMOTION:
1462 if (sdl_e.motion.state & SDL_BUTTON_LMASK) {
1463 int x = sdl_e.motion.x;
1464 int y = sdl_e.motion.y;
1466 if (last_mouse_x >= 0 &&
1467 last_mouse_y >= 0) {
1468 if (selected_vertex != (size_t) -1) {
1469 q->v[selected_vertex].x += (x -
1470 last_mouse_x);
1471 q->v[selected_vertex].y += (y -
1472 last_mouse_y);
1473 dragged_selected_vertex = 1;
1474 recalculate_selected_items(
1475 sdl_e.motion.x,
1476 sdl_e.motion.y);
1477 } else {
1478 offset_x += (x - last_mouse_x);
1479 offset_y += (y - last_mouse_y);
1482 } else {
1483 recalculate_selected_items(sdl_e.motion.x,
1484 sdl_e.motion.y);
1487 last_mouse_x = sdl_e.motion.x;
1488 last_mouse_y = sdl_e.motion.y;
1489 break;
1490 case SDL_MOUSEBUTTONUP:
1492 if ((sdl_e.button.state & SDL_BUTTON_LMASK) &&
1493 ui_action != UIA_NEW_VERTEX) {
1494 last_mouse_x = -1;
1495 last_mouse_y = -1;
1498 recalculate_selected_items(sdl_e.button.x,
1499 sdl_e.button.y);
1500 break;
1501 case SDL_MOUSEBUTTONDOWN:
1503 if (!(sdl_e.button.state & SDL_BUTTON_LMASK)) {
1504 break;
1507 if (ui_action != UIA_NEW_VERTEX) {
1508 last_mouse_x = -1;
1509 last_mouse_y = -1;
1512 switch (ui_action) {
1513 case UIA_MUTATE:
1515 if (selected_vertex == (size_t) -1) {
1516 break;
1519 ui_e = (struct ui_event) {
1520 /* */
1521 .type = ET_MUTATE, .idx_1 =
1522 selected_vertex
1524 ret = eq_push(&ui_e);
1525 ui_action = UIA_NONE;
1526 break;
1527 case UIA_NEW_VERTEX:
1529 if (selected_vertex != (size_t) -1 ||
1530 selected_edge_i != (size_t) -1 ||
1531 selected_edge_j != (size_t) -1) {
1532 break;
1535 int cx = sdl_e.button.x - offset_x;
1536 int cy = sdl_e.button.y - offset_y;
1538 ui_e = (struct ui_event) {
1539 /* */
1540 .type = ET_NEW_VERTEX, .int_1 = cx,
1541 .int_2 = cy
1543 ret = eq_push(&ui_e);
1544 ui_action = UIA_NONE;
1545 break;
1546 case UIA_NEW_EDGE_1:
1547 case UIA_NEW_H_EDGE_1:
1548 case UIA_RENAME:
1550 if (selected_vertex == (size_t) -1) {
1551 ui_action = UIA_NONE;
1552 break;
1555 last_clicked_vertex = selected_vertex;
1557 if (ui_action == UIA_NEW_EDGE_1) {
1558 ui_action = UIA_NEW_EDGE_2;
1559 } else if (ui_action == UIA_NEW_H_EDGE_1) {
1560 ui_action = UIA_NEW_H_EDGE_2;
1561 } else if (ui_action == UIA_RENAME) {
1562 ui_action = UIA_ENTER_RENAME;
1563 input_len = 0;
1564 input_idx = 0;
1565 input[0] = '\0';
1566 SDL_StartTextInput();
1568 /* Intentionally not copying null terminator */
1569 strncpy(input_with_prefix, "New name: ",
1570 10);
1571 rerender_input_string = 1;
1574 break;
1575 case UIA_NEW_EDGE_2:
1576 case UIA_NEW_H_EDGE_2:
1578 if (selected_vertex == (size_t) -1 ||
1579 selected_vertex == last_clicked_vertex) {
1580 ui_action = UIA_NONE;
1581 last_clicked_vertex = (size_t) -1;
1582 break;
1585 ui_e = (struct ui_event) {
1586 /* */
1587 .type = ET_NEW_EDGE, .idx_1 =
1588 last_clicked_vertex, .idx_2 =
1589 selected_vertex, .a = 1, .b =
1590 (ui_action == UIA_NEW_EDGE_2 ?
1591 1 : 2)
1593 ret = eq_push(&ui_e);
1594 ui_action = UIA_NONE;
1595 last_clicked_vertex = (size_t) -1;
1596 break;
1597 case UIA_DELETE:
1599 if (selected_vertex != (size_t) -1) {
1600 ui_e = (struct ui_event) {
1601 /* */
1602 .type = ET_DELETE_VERTEX,
1603 .idx_1 = selected_vertex
1605 ret = eq_push(&ui_e);
1606 } else if (selected_edge_i != (size_t) -1 &&
1607 selected_edge_j != (size_t) -1) {
1608 ui_e = (struct ui_event) {
1609 /* */
1610 .type = ET_DELETE_EDGE, .idx_1 =
1611 selected_edge_i,
1612 .idx_2 =
1613 selected_edge_j
1615 ret = eq_push(&ui_e);
1618 ui_action = UIA_NONE;
1619 break;
1620 case UIA_INC_FATNESS:
1621 case UIA_DEC_FATNESS:
1623 if (selected_vertex == (size_t) -1) {
1624 break;
1627 ui_e = (struct ui_event) {
1628 /* */
1629 .type = ET_CHANGE_FATNESS, .idx_1 =
1630 selected_vertex, .int_1 =
1631 (ui_action ==
1632 UIA_INC_FATNESS
1633 ? 1 : -1)
1635 ret = eq_push(&ui_e);
1636 ui_action = UIA_NONE;
1637 break;
1638 case UIA_NONE:
1639 case UIA_ASK_QUIT:
1640 case UIA_ENTER_SAVE:
1641 case UIA_ENTER_LOAD:
1642 case UIA_ENTER_RENAME:
1643 case UIA_LEN:
1644 break;
1647 break;
1650 if (ret) {
1651 goto done;
1655 if (load_requested ||
1656 save_requested) {
1657 save_load_delay = 30;
1658 char *f = 0;
1659 int r = 0;
1661 if (load_requested) {
1662 r = choose_load_file(&f);
1663 } else {
1664 r = choose_save_file(&f);
1667 if (!r) {
1668 if (f) {
1669 ui_e = (struct ui_event) { .str = f };
1670 ui_e.type = load_requested ? ET_LOAD : ET_SAVE;
1672 /* f is freed on next ui_finish_frame */
1673 free_this = f;
1674 ret = eq_push(&ui_e);
1676 } else {
1677 ui_action = load_requested ? UIA_ENTER_LOAD :
1678 UIA_ENTER_SAVE;
1679 input_idx = 0;
1680 input_len = 0;
1681 input[0] = '\0';
1682 SDL_StartTextInput();
1684 /* Intentionally not copying null terminator */
1685 strncpy(input_with_prefix, "Filename: ", 10);
1686 rerender_input_string = 1;
1690 /* framelimit */
1691 now = SDL_GetTicks();
1693 if (frame_start_ticks < now) {
1694 Uint32 elapsed_time = now - frame_start_ticks;
1696 if (elapsed_time < TICKS_PER_FRAME) {
1697 SDL_Delay(TICKS_PER_FRAME - elapsed_time);
1701 done:
1703 return ret;
1706 /* Return an event to the main loop */
1707 int ui_get_event(struct ui_event *e, uint_fast8_t *more)
1709 eq_pop(e);
1710 *more = eq_head != eq_tail;
1712 return 0;