Correct invisible text while renaming vertices
[clav.git] / ui-sdl.c
blobdca9d4b3627a96ce67d5de5f24450db7942bc2f7
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_RENAME,
44 UIA_ENTER_SAVE,
45 UIA_INC_FATNESS,
46 UIA_MUTATE,
47 UIA_NEW_EDGE_1,
48 UIA_NEW_EDGE_2,
49 UIA_NEW_H_EDGE_1,
50 UIA_NEW_H_EDGE_2,
51 UIA_NEW_VERTEX,
52 UIA_RENAME,
53 UIA_LEN
54 } ui_action;
56 /* The window we'll be using */
57 static SDL_Window *sdl_win;
59 /* The renderer we'll be using */
60 static SDL_Renderer *sdl_renderer;
62 /* How to limit the framerate */
63 static Uint32 frame_start_ticks;
65 /* Whether the scene needs to be drawn again */
66 static uint_fast8_t redraw;
68 /* The informative texture */
69 static SDL_Texture *selected_info;
71 /* Buffer for event queue */
72 static struct ui_event *eq_buf;
74 /* Current max length of event queue */
75 static size_t eq_len;
77 /* Current head of queue */
78 static size_t eq_head;
80 /* Current tail of queue */
81 static size_t eq_tail;
83 /* The quiver we'll be using */
84 static struct quiver *q;
86 /* Width of drawing area in pixels */
87 static int da_pix_width;
89 /* Height of drawing area in pixels */
90 static int da_pix_height;
92 /* The background color */
93 static SDL_Color color_bg = { .r = 0xe2, .g = 0xe2, .b = 0xe2, .a = 0xff };
95 /* The normal vertex color */
96 static SDL_Color color_v = { .r = 0x82, .g = 0x82, .b = 0xb2, .a = 0xff };
98 /* The vertex color for preview vertices */
99 static SDL_Color color_v_preview = { .r = 0x82, .g = 0x82, .b = 0xb2, .a =
100 0x40 };
102 /* The normal vertex outline color */
103 static SDL_Color color_outline = { .r = 0x12, .g = 0x12, .b = 0x12, .a = 0x80 };
105 /* The vertex outline color for preview vertices */
106 static SDL_Color color_outline_preview = { .r = 0x12, .g = 0x12, .b = 0x12, .a =
107 0x20 };
109 /* The selected vertex outline color */
110 static SDL_Color color_outline_sel = { .r = 0x42, .g = 0x42, .b = 0xe2, .a =
111 0x80 };
113 /* The font color */
114 static SDL_Color color_font = { .r = 0x12, .g = 0x12, .b = 0x12, .a = 0x80 };
116 /* The normal edge color */
117 static SDL_Color color_e_normal = { .r = 0x12, .g = 0x12, .b = 0x12, .a =
118 0xff };
120 /* The half edge color */
121 static SDL_Color color_e_half = { .r = 0x12, .g = 0x12, .b = 0x12, .a = 0x60 };
123 /* The abnormal edge color */
124 static SDL_Color color_e_abnormal = { .r = 0xd0, .g = 0x12, .b = 0x12, .a =
125 0xf0 };
127 /* The selected edge color */
128 static SDL_Color color_e_sel = { .r = 0xb2, .g = 0xb2, .b = 0xe2, .a = 0x40 };
130 /* The font for node names, instructions, etc */
131 static TTF_Font *normal_font;
133 /* The base font size */
134 static uint_fast8_t base_font_size = 12;
136 /* How much space (pixels) between lower left of screen and bottom text */
137 static unsigned int text_border_padding = 24;
139 /* Strings to display at bottom of screen */
140 static const char *bottom_string[] = {
141 /* */
142 [UIA_NONE] = "[m] Mutate\n[v] Create new vertex\n"
143 "[d] Delete vertex/edge\n[e] Add edge\n"
144 "[h] Add half edge\n[f] Increase vertex fatness\n"
145 "[g] Decrease vertex fatness\n[r] Rename vertex\n"
146 "[s] Save quiver\n[l] Load quiver\n[q] Quit", /* */
147 [UIA_ASK_QUIT] = "Quit?\n\n[y] Confirm\n[n] Cancel", /* */
148 [UIA_DEC_FATNESS] = "[Mouse1] Decrease fatness\n[ESC] Cancel", /* */
149 [UIA_DELETE] = "[Mouse1] Delete vertex/edge\n[ESC] Cancel", /* */
150 [UIA_ENTER_LOAD] = "[Enter] Load from\n[ESC] Cancel", /* */
151 [UIA_ENTER_RENAME] = "[Enter] Rename\n[ESC] Cancel", /* */
152 [UIA_ENTER_SAVE] = "[Enter] Save to\n[ESC] Cancel", /* */
153 [UIA_INC_FATNESS] = "[Mouse1] Increase fatness\n[ESC] Cancel", /* */
154 [UIA_MUTATE] = "[Mouse1] Mutate at vertex\n[ESC] Cancel", /* */
155 [UIA_NEW_EDGE_1] = "[Mouse1] Select start\n[ESC] Cancel", /* */
156 [UIA_NEW_EDGE_2] = "[Mouse1] Select end\n[ESC] Cancel", /* */
157 [UIA_NEW_H_EDGE_1] = "[Mouse1] Select start\n[ESC] Cancel", /* */
158 [UIA_NEW_H_EDGE_2] = "[Mouse1] Select end\n[ESC] Cancel", /* */
159 [UIA_NEW_VERTEX] = "[Mouse1] Create new vertex\n[ESC] Cancel", /* */
160 [UIA_RENAME] = "[Mouse1] Rename vertex\n[ESC] Cancel", /* */
161 [UIA_LEN] = "" /* */
164 /* The texture containing what to show on the bottom */
165 static SDL_Texture *bottom_text[UIA_LEN];
167 /* The textures containing the names of all vertices - indexed just like q */
168 static SDL_Texture **vertex_names;
170 /* The number of elements of vertex_names */
171 static size_t vertex_names_len;
173 /* Drawing offset x */
174 static int offset_x;
176 /* Drawing offset x */
177 static int offset_y;
179 /* How wide (in pixels) a fatness 1 node should be */
180 static unsigned int base_node_radius = 8;
182 /* How much (in pixels) a node should widen for each fatness level */
183 static unsigned int node_radius_per_fatness = 7;
185 /* How wide (in pixels) the outline should be */
186 static int outline_width = 2;
188 /* How wide (in pixels) the arrowheads should be */
189 static int arrow_length = 7;
191 /* How narrow the arrowheads should be */
192 static double arrow_angle = 6.5 * M_PI / 8.0;
194 /* How close to an arrow (in pixels) for it to be selected */
195 static int edge_margin = 5;
197 /* If we're interacting with a vertex, which one */
198 static size_t selected_vertex = (size_t) -1;
200 /* If we're interacting with an edge, the i */
201 static size_t selected_edge_i = (size_t) -1;
203 /* If we're interacting with an edge, the j */
204 static size_t selected_edge_j = (size_t) -1;
206 /* If we're adding an edge, the last vertex we clicked on */
207 static size_t last_clicked_vertex = (size_t) -1;
209 /* x-coordinate of last mouse position */
210 static int last_mouse_x = -1;
212 /* y-coordinate of last mouse position */
213 static int last_mouse_y = -1;
215 /* Maximum length of an input string we'll accept */
216 static size_t max_input_size = 1 << 10;
218 /* Current position in input */
219 static size_t input_idx;
221 /* Current length of string held in input */
222 static size_t input_len;
224 /* If a string is being typed in, what it is (in UTF-8 as per SDL2) */
225 static char *input;
227 /* Input, with `Filename:' or `New name:' prepended (for rendering) */
228 static char *input_with_prefix;
230 /* Texture for what user is currently entering */
231 static SDL_Texture *input_texture;
233 /* Whether we need to re-render the input */
234 static uint_fast8_t rerender_input_string;
236 /* To prevent awkward repeats in SDL events, rate-limit `s' and `l' */
237 static uint_fast8_t save_load_delay;
239 /* If something needs to be freed next frame, but not before */
240 static void *free_this;
242 /* sine tables */
243 static double precalc_sins[TRIG_PRECALC_NUM];
245 /* cosine tables */
246 static double precalc_coss[TRIG_PRECALC_NUM];
248 /* Precalculate sines and cosines */
249 static void precalc_trig(void)
251 precalc_coss[0] = 0.0;
252 precalc_sins[0] = 1.0;
254 for (size_t j = 1; j < TRIG_PRECALC_NUM - 1; ++j) {
255 double theta = (M_PI * j) / (2 * (TRIG_PRECALC_NUM - 1));
257 precalc_sins[j] = sin(theta);
258 precalc_coss[j] = cos(theta);
261 precalc_coss[TRIG_PRECALC_NUM - 1] = 0.0;
262 precalc_sins[TRIG_PRECALC_NUM - 1] = 1.0;
265 /* GCD */
266 static int gcd(uint_fast8_t x, uint_fast8_t y)
268 uint_fast8_t r = 0;
270 if (!x &&
271 !y) {
272 return 1;
275 while (y) {
276 r = x % y;
277 x = y;
278 y = r;
281 return x;
284 /* Allocate and print a rational in the way a user expects */
285 static int pretty_fraction(struct rational *r, char **out)
287 int ret = 0;
289 if (r->q == 1) {
290 size_t len = snprintf(0, 0, "%d", (int) r->p);
292 if (!(*out = malloc(len + 1))) {
293 ret = errno;
294 perror(L("malloc"));
295 goto done;
298 sprintf(*out, "%d", (int) r->p);
299 goto done;
302 size_t len = snprintf(0, 0, "%d/%u", (int) r->p, (unsigned int) r->q);
304 if (!(*out = malloc(len + 1))) {
305 ret = errno;
306 perror(L("malloc"));
307 goto done;
310 sprintf(*out, "%d/%u", (int) r->p, (unsigned int) r->q);
311 done:
313 return ret;
316 /* Render text to texture */
317 static int render_text(const char *text, SDL_Texture **out)
319 if (!out) {
320 return 0;
323 if (*out) {
324 SDL_DestroyTexture(*out);
327 int ret = 0;
328 SDL_Surface *surf = 0;
330 if (!(surf = TTF_RenderUTF8_Blended_Wrapped(normal_font, text,
331 color_font, 800))) {
332 fprintf(stderr, L("TTF_RenderUTF8_Shaded(): %s\n"),
333 TTF_GetError());
334 ret = ENOMEDIUM;
335 goto done;
338 if (!(*out = SDL_CreateTextureFromSurface(sdl_renderer, surf))) {
339 fprintf(stderr, L("SDL_CreateTextureFromSurface(): %s\n"),
340 SDL_GetError());
341 ret = ENOMEDIUM;
342 goto done;
345 done:
346 SDL_FreeSurface(surf);
348 return ret;
351 /* Load fonts */
352 static int load_fonts(void)
354 int ret = 0;
356 if (normal_font) {
357 TTF_CloseFont(normal_font);
358 normal_font = 0;
361 if (!(normal_font = TTF_OpenFont(FONT_PATH, base_font_size))) {
362 ret = ENOMEDIUM;
363 fprintf(stderr, L("TTF_OpenFont(): %s\n"), TTF_GetError());
364 goto done;
367 for (size_t j = 0; j < UIA_LEN; ++j) {
368 if ((ret = render_text(bottom_string[j], &bottom_text[j]))) {
369 goto done;
373 done:
375 if (ret) {
376 if (normal_font) {
377 TTF_CloseFont(normal_font);
380 normal_font = 0;
383 return ret;
386 /* Insert str into input at given position */
387 static void text_input(const char *str)
389 size_t l = strlen(str);
391 if (l + input_len + 1 >= max_input_size) {
392 return;
395 for (size_t k = input_len; k > input_idx; --k) {
396 input[k + l] = input[k];
399 /* Special-case for input_idx: 0 prohibits `>=' above */
400 input[input_idx + l] = input[input_idx];
402 for (size_t k = 0; k < l; ++k) {
403 input[input_idx + k] = str[k];
406 input_idx += l;
407 input_len += l;
408 rerender_input_string = 1;
411 /* Move input cursor left */
412 static void text_input_left(void)
414 if (input_idx > 0) {
415 input_idx--;
418 while (input_idx > 0) {
419 unsigned char c = input[input_idx];
421 if ((c & 0xc0) == 0x80) {
422 input_idx--;
423 } else {
424 break;
429 /* Move input cursor right */
430 static void text_input_right(void)
432 size_t adv = 1;
433 unsigned char c = input[input_idx];
435 if ((c & 0x80) == 0x0) {
436 adv = 1;
437 } else if ((c & 0xe0) == 0xc0) {
438 adv = 2;
439 } else if ((c & 0xf0) == 0xe0) {
440 adv = 3;
441 } else if ((c & 0xf8) == 0xf0) {
442 adv = 4;
445 if (input_idx + adv <= input_len) {
446 input_idx += adv;
450 /* Backspace */
451 static void text_input_bs(void)
453 if (!input_idx) {
454 return;
457 size_t bs = 1;
459 while (input_idx > bs) {
460 unsigned char c = input[input_idx - bs];
462 if ((c & 0xc0) == 0x80) {
463 bs++;
464 } else {
465 break;
469 for (size_t k = input_idx - bs; k <= input_len - bs; k++) {
470 input[k] = input[k + bs];
473 input_len -= bs;
474 input_idx -= bs;
475 rerender_input_string = 1;
478 /* Convert `internal coordinates' to pixel coordinates */
479 static void internal_to_pixel_xy(int in_x, int in_y, int *out_x, int *out_y)
481 *out_x = in_x + offset_x;
482 *out_y = in_y + offset_y;
485 /* Convert pixel coordinates to `internal coordinates' */
486 static void pixel_to_internal_xy(int in_x, int in_y, int *out_x, int *out_y)
488 *out_x = in_x - offset_x;
489 *out_y = in_y - offset_y;
492 /* Set selected_vertex and selected_edge_{i,j} */
493 static int recalculate_selected_items(int mx, int my)
495 int x = 0;
496 int y = 0;
497 int ret = 0;
498 char *s = 0;
499 char *sij = 0;
500 char *sji = 0;
502 pixel_to_internal_xy(mx, my, &x, &y);
503 size_t last_vertex = selected_vertex;
504 size_t last_edge_i = selected_edge_i;
505 size_t last_edge_j = selected_edge_j;
507 selected_vertex = (size_t) -1;
508 selected_edge_i = (size_t) -1;
509 selected_edge_j = (size_t) -1;
511 for (size_t j = q->v_num; j > 0; --j) {
512 struct vertex *v = &(q->v[j - 1]);
513 int r = base_node_radius + v->fatness * node_radius_per_fatness;
515 if (x > v->x - r &&
516 x < v->x + r &&
517 y > v->y - r &&
518 y < v->y + r) {
519 selected_vertex = j - 1;
520 goto compute_str;
524 for (size_t j = 1; j < q->v_num; ++j) {
525 struct vertex *v1 = &(q->v[j]);
527 for (size_t i = 0; i < j; ++i) {
528 if (!q->e[i * q->v_len + j].p &&
529 !q->e[j * q->v_len + i].p) {
530 continue;
533 struct vertex *v2 = &(q->v[i]);
535 if ((x - edge_margin > v1->x &&
536 x - edge_margin > v2->x) ||
537 (x + edge_margin < v1->x &&
538 x + edge_margin < v2->x) ||
539 (y - edge_margin > v1->y &&
540 y - edge_margin > v2->y) ||
541 (y + edge_margin < v1->y &&
542 y + edge_margin < v2->y)) {
543 continue;
546 if (v1->x == v2->x) {
547 if (x + edge_margin > v1->x &&
548 x - edge_margin < v1->x) {
549 selected_edge_i = i;
550 selected_edge_j = j;
551 goto compute_str;
553 } else if (v1->y == v2->y) {
554 if (y + edge_margin > v1->y &&
555 y - edge_margin < v1->y) {
556 selected_edge_i = i;
557 selected_edge_j = j;
558 goto compute_str;
560 } else {
561 double m1 = ((double) (v2->y - v1->y)) /
562 ((double) (v2->x - v1->x));
563 double m2 = -1.0 / m1;
564 double xint = ((double) y - (double) v1->y +
565 m1 * v1->x - m2 * x) / (m1 - m2);
566 double yint = m1 * xint - m1 * v1->x +
567 (double) v1->y;
569 if ((x - xint) * (x - xint) + (y - yint) * (y -
570 yint)
571 < edge_margin * edge_margin) {
572 selected_edge_i = i;
573 selected_edge_j = j;
574 goto compute_str;
580 compute_str:
582 if (selected_vertex != last_vertex &&
583 selected_vertex != (size_t) -1) {
584 struct vertex *v = &(q->v[selected_vertex]);
585 size_t len = snprintf(0, 0,
586 "Name: %s\nFatness: %d\nPosition: (%d,%d)",
587 v->name,
588 (int) v->fatness, v->x, v->y);
590 if (!(s = malloc(len + 1))) {
591 ret = errno;
592 perror(L("malloc"));
593 goto done;
596 sprintf(s, "Name: %s\nFatness: %d\nPosition: (%d,%d)",
597 v->name, (int) v->fatness, v->x, v->y);
599 if ((ret = render_text(s, &selected_info))) {
600 goto done;
602 } else if ((selected_edge_i != last_edge_i ||
603 selected_edge_j != last_edge_j) &&
604 selected_edge_i != (size_t) -1 &&
605 selected_edge_j != (size_t) -1) {
606 struct vertex *i = &(q->v[selected_edge_i]);
607 struct vertex *j = &(q->v[selected_edge_j]);
608 struct rational *eij = &(q->e[selected_edge_i * q->v_len +
609 selected_edge_j]);
610 struct rational *eji = &(q->e[selected_edge_j * q->v_len +
611 selected_edge_i]);
613 if ((ret = pretty_fraction(eij, &sij))) {
614 goto done;
617 if ((ret = pretty_fraction(eji, &sji))) {
618 goto done;
621 size_t len = snprintf(0, 0,
622 "%s \u2192 %s: %s\n%s \u2192 %s: %s",
623 i->name, j->name, sij,
624 j->name, i->name, sji);
626 if (!(s = malloc(len + 1))) {
627 ret = errno;
628 perror(L("malloc"));
629 goto done;
632 sprintf(s, "%s \u2192 %s: %s\n%s \u2192 %s: %s", i->name,
633 j->name, sij, j->name, i->name, sji);
635 if ((ret = render_text(s, &selected_info))) {
636 goto done;
640 done:
641 free(s);
642 free(sij);
643 free(sji);
645 return ret;
648 /* Render vertex names as textures */
649 static int render_vertex_names(void)
651 if (vertex_names) {
652 for (size_t j = 0; j < vertex_names_len; ++j) {
653 SDL_DestroyTexture(vertex_names[j]);
654 vertex_names[j] = 0;
658 if (!q->v_num) {
659 return 0;
662 if (!(vertex_names = realloc(vertex_names, q->v_num *
663 sizeof(*vertex_names)))) {
664 int sv_err = errno;
666 perror(L("realloc()"));
667 vertex_names_len = 0;
669 return sv_err;
672 vertex_names_len = q->v_num;
674 for (size_t j = 0; j < vertex_names_len; ++j) {
675 vertex_names[j] = 0;
678 for (size_t j = 0; j < vertex_names_len; ++j) {
679 int ret = 0;
681 if ((ret = render_text(q->v[j].name, &(vertex_names[j])))) {
682 return ret;
686 return 0;
689 /* Get information about the window */
690 static void react_to_window_resized(void)
692 int old_pix_width = da_pix_width;
693 int old_pix_height = da_pix_height;
695 SDL_GetWindowSize(sdl_win, &da_pix_width, &da_pix_height);
697 if (old_pix_width == da_pix_width &&
698 old_pix_height == da_pix_height) {
699 return;
702 offset_x += (da_pix_width - old_pix_width) / 2;
703 offset_y += (da_pix_height - old_pix_height) / 2;
706 /* Pop from queue */
707 static void eq_pop(struct ui_event *out)
709 if (eq_head == eq_tail) {
710 *out = (struct ui_event) { 0 };
712 return;
715 memcpy(out, eq_buf + eq_head, sizeof *out);
716 eq_buf[eq_head] = (struct ui_event) { 0 };
717 eq_head = (eq_head + 1) % eq_len;
720 /* Push into queue */
721 static int eq_push(struct ui_event *in)
723 if (((eq_tail + 1) % eq_len) == eq_head) {
724 void *newmem;
726 if ((eq_len * sizeof *in) >= ((size_t) -1) / 2) {
727 fprintf(stderr, L(
728 "eq_push: Impossibly large buffer\n"));
730 return ENOMEM;
733 if (!(newmem = realloc(eq_buf, (eq_len * 2) *
734 sizeof *eq_buf))) {
735 int sv_err = errno;
737 perror(L("realloc"));
739 return sv_err;
742 eq_buf = (struct ui_event *) newmem;
743 eq_len *= 2;
746 memcpy(eq_buf + eq_tail, in, sizeof *in);
747 eq_tail = (eq_tail + 1) % eq_len;
749 return 0;
752 /* Initialize SDL */
753 int ui_init(struct quiver *i_q)
755 int ret;
757 q = i_q;
758 size_t padding = strlen("Filename: ");
760 if (!(input_with_prefix = malloc(max_input_size + padding))) {
761 ret = errno;
762 perror(L("malloc"));
764 return ret;
767 strcpy(input_with_prefix, "Filename: ");
768 input = input_with_prefix + padding;
769 input_idx = 0;
770 input_len = 0;
772 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
773 fprintf(stderr, L("SDL_Init(): %s\n"), SDL_GetError());
775 return ENOMEDIUM;
778 sdl_win = SDL_CreateWindow("Cluster Algebra Visualizer",
779 SDL_WINDOWPOS_UNDEFINED,
780 SDL_WINDOWPOS_UNDEFINED, 1000, 1000,
781 SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
783 if (!sdl_win) {
784 fprintf(stderr, L("SDL_CreateWindow(): %s\n"), SDL_GetError());
786 return ENOMEDIUM;
789 sdl_renderer = SDL_CreateRenderer(sdl_win, -1,
790 SDL_RENDERER_ACCELERATED);
792 if (!sdl_renderer) {
793 fprintf(stderr, L("SDL_CreateRenderer(): %s\n"),
794 SDL_GetError());
796 return ENOMEDIUM;
799 if (TTF_Init() < 0) {
800 fprintf(stderr, L("TTF_Init(): %s\n"), TTF_GetError());
802 return ENOMEDIUM;
805 if ((ret = load_fonts())) {
806 goto done;
809 if ((ret = render_vertex_names())) {
810 goto done;
813 if ((ret = SDL_SetRenderDrawColor(sdl_renderer, color_bg.r, color_bg.g,
814 color_bg.b, color_bg.a))) {
815 fprintf(stderr, L("SDL_SetRenderDrawColor(): %s\n"),
816 SDL_GetError());
817 goto done;
820 if ((ret = SDL_RenderClear(sdl_renderer))) {
821 fprintf(stderr, L("SDL_RenderClear(): %s\n"), SDL_GetError());
822 goto done;
825 SDL_RenderPresent(sdl_renderer);
826 react_to_window_resized();
828 /* Set up queue for returning data */
829 if (!(eq_buf = calloc(2, sizeof *eq_buf))) {
830 ret = errno;
831 perror(L("malloc"));
832 goto done;
835 eq_len = 2;
836 eq_head = 0;
837 eq_tail = 0;
839 /* Sines and cosines for drawing circles */
840 precalc_trig();
841 done:
843 return ret;
846 /* Deal with the fact that the quiver was changed */
847 int ui_respond_quiver_change(void)
849 return render_vertex_names();
852 /* Tear down SDL */
853 int ui_teardown(void)
855 if (vertex_names) {
856 for (size_t j = 0; j < vertex_names_len; ++j) {
857 SDL_DestroyTexture(vertex_names[j]);
858 vertex_names[j] = 0;
862 for (size_t j = 0; j < UIA_LEN; ++j) {
863 SDL_DestroyTexture(bottom_text[j]);
864 bottom_text[j] = 0;
867 if (selected_info) {
868 SDL_DestroyTexture(selected_info);
869 selected_info = 0;
872 if (input_texture) {
873 SDL_DestroyTexture(input_texture);
876 if (normal_font) {
877 TTF_CloseFont(normal_font);
878 normal_font = 0;
881 if (sdl_win) {
882 SDL_DestroyWindow(sdl_win);
885 free(input_with_prefix);
886 input = 0;
887 SDL_Quit();
889 return 0;
892 /* Record that a frame has been started */
893 int ui_start_frame(void)
895 int ret = 0;
896 int rho = 0;
897 SDL_Rect r = { 0 };
898 Uint32 dummy_format;
899 int dummy_access;
900 int tex_w;
901 int tex_h;
903 frame_start_ticks = SDL_GetTicks();
905 if (!redraw) {
906 goto done;
909 redraw = 0;
911 /* Draw the damn thing */
912 SDL_SetRenderDrawColor(sdl_renderer, color_bg.r, color_bg.g, color_bg.b,
913 color_bg.a);
914 SDL_RenderClear(sdl_renderer);
916 /* Special case for if text input is going on */
917 if (rerender_input_string) {
918 rerender_input_string = 0;
920 if ((ret = render_text(input_with_prefix, &input_texture))) {
921 goto done;
925 /* Draw each edge */
926 for (size_t j = 0; j < q->v_num; ++j) {
927 for (size_t i = 0; i < j; ++i) {
928 /* First, determine if we're looking at a half-edge or a full-edge */
929 int d = gcd(q->v[i].fatness, q->v[j].fatness);
930 struct rational *eij = &(q->e[i * q->v_len + j]);
931 struct rational *eji = &(q->e[j * q->v_len + i]);
932 int cx = 0;
933 int cy = 0;
934 int cx2 = 0;
935 int cy2 = 0;
936 double theta = 0.0;
938 if (!eij->p &&
939 !eji->p) {
940 continue;
943 internal_to_pixel_xy(q->v[i].x, q->v[i].y, &cx, &cy);
944 internal_to_pixel_xy(q->v[j].x, q->v[j].y, &cx2, &cy2);
946 if (selected_edge_i == i &&
947 selected_edge_j == j) {
948 if ((ret = SDL_SetRenderDrawColor(sdl_renderer,
949 color_e_sel.r,
950 color_e_sel.g,
951 color_e_sel.b,
952 color_e_sel.a)))
954 fprintf(stderr, L(
955 "SDL_RenderDrawColor(): %s\n"),
956 SDL_GetError());
957 goto done;
960 for (int id = -edge_margin; id < edge_margin;
961 ++id) {
962 for (int jd = -edge_margin; jd <
963 edge_margin; ++jd) {
964 if ((ret = SDL_RenderDrawLine(
965 sdl_renderer, cx +
966 id, cy + jd,
967 cx2 + id, cy2 +
968 jd))) {
969 fprintf(stderr, L(
970 "SDL_RenderDrawLine(): %s\n"),
971 SDL_GetError());
972 goto done;
978 /* This is the (eij)/dj = -(eji)/di condition */
979 if (eij->p * q->v[i].fatness * eji->q != -eji->p *
980 q->v[j].fatness * eij->q) {
981 ret = SDL_SetRenderDrawColor(sdl_renderer,
982 color_e_abnormal.r,
983 color_e_abnormal.g,
984 color_e_abnormal.b,
985 color_e_abnormal.a);
986 } else if (abs(eij->p) * d == q->v[j].fatness *
987 eij->q) {
988 ret = SDL_SetRenderDrawColor(sdl_renderer,
989 color_e_normal.r,
990 color_e_normal.g,
991 color_e_normal.b,
992 color_e_normal.a);
993 } else if (2 * abs(eij->p) * d == q->v[j].fatness *
994 eij->q) {
995 ret = SDL_SetRenderDrawColor(sdl_renderer,
996 color_e_half.r,
997 color_e_half.g,
998 color_e_half.b,
999 color_e_half.a);
1000 } else {
1001 ret = SDL_SetRenderDrawColor(sdl_renderer,
1002 color_e_abnormal.r,
1003 color_e_abnormal.g,
1004 color_e_abnormal.b,
1005 color_e_abnormal.a);
1008 if (ret) {
1009 fprintf(stderr, L(
1010 "SDL_SetRenderDrawColor(): %s\n"),
1011 SDL_GetError());
1012 goto done;
1015 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy, cx2,
1016 cy2))) {
1017 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1018 SDL_GetError());
1019 goto done;
1022 if (cx == cx2) {
1023 theta = (cy2 > cy) ? M_PI / 2.0 : -M_PI / 2.0;
1024 } else {
1025 theta = atan2f(cy2 - cy, cx2 - cx);
1028 if ((eij->p < 0)) {
1029 theta += M_PI;
1032 cx = (cx + cx2) / 2;
1033 cy = (cy + cy2) / 2;
1034 cx2 = cx + arrow_length * cos(theta + arrow_angle);
1035 cy2 = cy + arrow_length * sin(theta + arrow_angle);
1037 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy, cx2,
1038 cy2))) {
1039 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1040 SDL_GetError());
1041 goto done;
1044 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy, cx +
1045 arrow_length * cos(theta -
1046 arrow_angle),
1047 cy +
1048 arrow_length * sin(theta -
1049 arrow_angle))))
1051 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1052 SDL_GetError());
1053 goto done;
1058 /* Draw each vertex as a box */
1059 for (size_t j = 0; j < q->v_num; ++j) {
1060 struct vertex *v = &(q->v[j]);
1061 int cx = 0;
1062 int cy = 0;
1064 internal_to_pixel_xy(v->x, v->y, &cx, &cy);
1066 /* Central square */
1067 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_NONE);
1068 SDL_SetRenderDrawColor(sdl_renderer, color_v.r, color_v.g,
1069 color_v.b, color_v.a);
1070 rho = base_node_radius + node_radius_per_fatness * v->fatness;
1071 r.x = cx - rho;
1072 r.y = cy - rho;
1073 r.w = 2 * rho;
1074 r.h = 2 * rho;
1075 SDL_RenderFillRect(sdl_renderer, &r);
1077 /* Outline */
1078 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
1080 if (j == selected_vertex ||
1081 j == last_clicked_vertex) {
1082 SDL_SetRenderDrawColor(sdl_renderer,
1083 color_outline_sel.r,
1084 color_outline_sel.g,
1085 color_outline_sel.b,
1086 color_outline_sel.a);
1087 } else {
1088 SDL_SetRenderDrawColor(sdl_renderer, color_outline.r,
1089 color_outline.g, color_outline.b,
1090 color_outline.a);
1093 r.x = cx - rho;
1094 r.y = cy - rho;
1095 r.w = 2 * rho - outline_width;
1096 r.h = outline_width;
1097 SDL_RenderFillRect(sdl_renderer, &r);
1098 r.x = cx + rho - outline_width;
1099 r.y = cy - rho;
1100 r.w = outline_width;
1101 r.h = 2 * rho - outline_width;
1102 SDL_RenderFillRect(sdl_renderer, &r);
1103 r.x = cx - rho + outline_width;
1104 r.y = cy + rho - outline_width;
1105 r.w = 2 * rho - outline_width;
1106 r.h = outline_width;
1107 SDL_RenderFillRect(sdl_renderer, &r);
1108 r.x = cx - rho;
1109 r.y = cy - rho + outline_width;
1110 r.w = outline_width;
1111 r.h = 2 * rho - outline_width;
1112 SDL_RenderFillRect(sdl_renderer, &r);
1114 /* Text */
1115 if (j >= vertex_names_len) {
1116 fprintf(stderr, L(
1117 "render_vertex_names() was not called, somehow\n"));
1118 ret = EINVAL;
1119 goto done;
1122 if (SDL_QueryTexture(vertex_names[j], &dummy_format,
1123 &dummy_access, &tex_w, &tex_h)) {
1124 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1125 SDL_GetError());
1126 ret = ENOMEDIUM;
1127 goto done;
1130 r.x = cx - tex_w / 2;
1131 r.y = cy - tex_h / 2;
1132 r.w = tex_w;
1133 r.h = tex_h;
1134 SDL_RenderCopy(sdl_renderer, vertex_names[j], 0, &r);
1137 /* If adding a new vertex, draw preview */
1138 if (ui_action == UIA_NEW_VERTEX &&
1139 last_mouse_x != -1 &&
1140 last_mouse_y != -1) {
1141 /* Central square */
1142 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_NONE);
1143 SDL_SetRenderDrawColor(sdl_renderer, color_v_preview.r,
1144 color_v_preview.g, color_v_preview.b,
1145 color_v_preview.a);
1146 rho = base_node_radius;
1147 r.x = last_mouse_x - rho;
1148 r.y = last_mouse_y - rho;
1149 r.w = 2 * rho;
1150 r.h = 2 * rho;
1151 SDL_RenderFillRect(sdl_renderer, &r);
1153 /* Outline */
1154 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
1155 SDL_SetRenderDrawColor(sdl_renderer, color_outline_preview.r,
1156 color_outline_preview.g,
1157 color_outline_preview.b,
1158 color_outline_preview.a);
1159 r.x = last_mouse_x - rho;
1160 r.y = last_mouse_y - rho;
1161 r.w = 2 * rho - outline_width;
1162 r.h = outline_width;
1163 SDL_RenderFillRect(sdl_renderer, &r);
1164 r.x = last_mouse_x + rho - outline_width;
1165 r.y = last_mouse_y - rho;
1166 r.w = outline_width;
1167 r.h = 2 * rho - outline_width;
1168 SDL_RenderFillRect(sdl_renderer, &r);
1169 r.x = last_mouse_x - rho + outline_width;
1170 r.y = last_mouse_y + rho - outline_width;
1171 r.w = 2 * rho - outline_width;
1172 r.h = outline_width;
1173 SDL_RenderFillRect(sdl_renderer, &r);
1174 r.x = last_mouse_x - rho;
1175 r.y = last_mouse_y - rho + outline_width;
1176 r.w = outline_width;
1177 r.h = 2 * rho - outline_width;
1178 SDL_RenderFillRect(sdl_renderer, &r);
1181 /* If adding a new edge, draw possible */
1182 if ((ui_action == UIA_NEW_EDGE_2 ||
1183 ui_action == UIA_NEW_H_EDGE_2) &&
1185 /* last_clicked_vertex != (size_t) -1 && */
1186 last_mouse_x != -1 &&
1187 last_mouse_y != -1) {
1188 int cx = 0;
1189 int cy = 0;
1191 if ((ret = SDL_SetRenderDrawColor(sdl_renderer,
1192 color_e_normal.r,
1193 color_e_normal.g,
1194 color_e_normal.b,
1195 color_e_normal.a))) {
1196 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1197 SDL_GetError());
1198 goto done;
1201 internal_to_pixel_xy(q->v[last_clicked_vertex].x,
1202 q->v[last_clicked_vertex].y, &cx, &cy);
1204 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy,
1205 last_mouse_x, last_mouse_y))) {
1206 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1207 SDL_GetError());
1208 goto done;
1212 /* Bottom text */
1213 if (SDL_QueryTexture(bottom_text[ui_action], &dummy_format,
1214 &dummy_access, &tex_w, &tex_h)) {
1215 fprintf(stderr, L("SDL_QueryTexture(): %s\n"), SDL_GetError());
1216 ret = ENOMEDIUM;
1217 goto done;
1220 r.x = text_border_padding;
1221 r.y = da_pix_height - tex_h - text_border_padding;
1222 r.w = tex_w;
1223 r.h = tex_h;
1224 SDL_RenderCopy(sdl_renderer, bottom_text[ui_action], 0, &r);
1226 /* If something is selected */
1227 if (selected_info &&
1228 (selected_vertex != (size_t) -1 ||
1229 (selected_edge_i != (size_t) -1 &&
1230 selected_edge_j != (size_t) -1))) {
1231 if (SDL_QueryTexture(selected_info, &dummy_format,
1232 &dummy_access, &tex_w, &tex_h)) {
1233 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1234 SDL_GetError());
1235 ret = ENOMEDIUM;
1236 goto done;
1239 r.x = text_border_padding;
1240 r.y = text_border_padding;
1241 r.w = tex_w;
1242 r.h = tex_h;
1243 SDL_RenderCopy(sdl_renderer, selected_info, 0, &r);
1246 /* If user is entering text */
1247 if (ui_action == UIA_ENTER_SAVE ||
1248 ui_action == UIA_ENTER_LOAD ||
1249 ui_action == UIA_ENTER_RENAME) {
1250 if (SDL_QueryTexture(bottom_text[ui_action], &dummy_format,
1251 &dummy_access, &tex_w, &tex_h)) {
1252 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1253 SDL_GetError());
1254 ret = ENOMEDIUM;
1255 goto done;
1258 r.x = text_border_padding;
1259 r.y = da_pix_height - tex_h - text_border_padding;
1261 if (SDL_QueryTexture(input_texture, &dummy_format,
1262 &dummy_access, &tex_w, &tex_h)) {
1263 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1264 SDL_GetError());
1265 ret = ENOMEDIUM;
1266 goto done;
1269 r.y -= (tex_h + text_border_padding);
1270 r.w = tex_w;
1271 r.h = tex_h;
1272 SDL_RenderCopy(sdl_renderer, input_texture, 0, &r);
1274 /* Now draw a cursor */
1275 char store_idxchar = input[input_idx];
1277 input[input_idx] = '\0';
1279 if ((ret = TTF_SizeUTF8(normal_font, input_with_prefix, &tex_w,
1280 &tex_h))) {
1281 fprintf(stderr, L("TTF_SizeText(): %s\n"),
1282 TTF_GetError());
1283 goto done;
1286 input[input_idx] = store_idxchar;
1288 if ((ret = SDL_SetRenderDrawColor(sdl_renderer, color_font.r,
1289 color_font.g, color_font.b,
1290 color_font.a))) {
1291 fprintf(stderr, L("SDL_SetRenderDrawColor(): %s\n"),
1292 SDL_GetError());
1293 goto done;
1296 if ((ret = SDL_RenderDrawLine(sdl_renderer, r.x + tex_w, r.y,
1297 r.x + tex_w, r.y + tex_h))) {
1298 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1299 SDL_GetError());
1300 goto done;
1304 done:
1306 return ret;
1309 /* Draw a frame, possibly sleeping for framelimit */
1310 int ui_finish_frame(void)
1312 int ret = 0;
1313 struct ui_event ui_e = { 0 };
1314 SDL_Event sdl_e = { 0 };
1315 Uint32 now = 0;
1316 uint_fast8_t save_requested = 0;
1317 uint_fast8_t load_requested = 0;
1319 if (free_this) {
1320 free(free_this);
1321 free_this = 0;
1324 if (save_load_delay) {
1325 save_load_delay--;
1328 SDL_RenderPresent(sdl_renderer);
1330 /* Handle user input */
1331 while (SDL_PollEvent(&sdl_e) != 0) {
1332 redraw = 1;
1333 SDL_Keycode k = 0;
1335 switch (sdl_e.type) {
1336 case SDL_QUIT:
1337 ui_e = (struct ui_event) { .type = ET_QUIT };
1338 ret = eq_push(&ui_e);
1339 break;
1340 case SDL_TEXTINPUT:
1341 text_input(sdl_e.text.text);
1342 break;
1343 case SDL_KEYUP:
1345 if (sdl_e.key.repeat) {
1346 break;
1349 k = sdl_e.key.keysym.sym;
1351 switch (ui_action) {
1352 case UIA_ENTER_SAVE:
1353 case UIA_ENTER_LOAD:
1354 case UIA_ENTER_RENAME:
1356 if (k == SDLK_ESCAPE) {
1357 SDL_StopTextInput();
1358 ui_action = UIA_NONE;
1359 last_clicked_vertex = (size_t) -1;
1360 } else if (k == SDLK_RETURN ||
1361 k == SDLK_RETURN2) {
1362 SDL_StopTextInput();
1363 ui_e = (struct ui_event) { .str =
1364 input };
1366 if (ui_action == UIA_ENTER_SAVE) {
1367 ui_e.type = ET_SAVE;
1368 } else if (ui_action ==
1369 UIA_ENTER_LOAD) {
1370 ui_e.type = ET_LOAD;
1371 } else if (ui_action ==
1372 UIA_ENTER_RENAME) {
1373 ui_e.type = ET_RENAME;
1374 ui_e.idx_1 =
1375 last_clicked_vertex;
1378 ret = eq_push(&ui_e);
1379 ui_action = UIA_NONE;
1380 last_clicked_vertex = (size_t) -1;
1381 } else if (k == SDLK_LEFT) {
1382 text_input_left();
1383 } else if (k == SDLK_RIGHT) {
1384 text_input_right();
1385 } else if (k == SDLK_BACKSPACE) {
1386 text_input_bs();
1389 break;
1390 case UIA_NONE:
1392 if (k == SDLK_q) {
1393 ui_action = UIA_ASK_QUIT;
1394 } else if (k == SDLK_d) {
1395 ui_action = UIA_DELETE;
1396 } else if (k == SDLK_e) {
1397 ui_action = UIA_NEW_EDGE_1;
1398 } else if (k == SDLK_f) {
1399 ui_action = UIA_INC_FATNESS;
1400 } else if (k == SDLK_g) {
1401 ui_action = UIA_DEC_FATNESS;
1402 } else if (k == SDLK_h) {
1403 ui_action = UIA_NEW_H_EDGE_1;
1404 } else if (k == SDLK_m) {
1405 ui_action = UIA_MUTATE;
1406 } else if (k == SDLK_r) {
1407 ui_action = UIA_RENAME;
1408 } else if (k == SDLK_v) {
1409 ui_action = UIA_NEW_VERTEX;
1410 } else if (k == SDLK_l &&
1411 !save_load_delay) {
1412 /* Don't load - SDL_KEYUP repeats */
1413 load_requested = 1;
1414 } else if (k == SDLK_s &&
1415 !save_load_delay) {
1416 save_requested = 1;
1419 break;
1420 case UIA_ASK_QUIT:
1422 if (k == SDLK_n ||
1423 k == SDLK_ESCAPE) {
1424 ui_action = UIA_NONE;
1425 } else if (k == SDLK_y) {
1426 ui_e = (struct ui_event) { .type =
1427 ET_QUIT };
1428 ret = eq_push(&ui_e);
1429 ui_action = UIA_NONE;
1432 break;
1433 default:
1435 if (k == SDLK_q ||
1436 k == SDLK_ESCAPE) {
1437 ui_action = UIA_NONE;
1440 break;
1443 break;
1444 case SDL_WINDOWEVENT:
1446 if (sdl_e.window.event == SDL_WINDOWEVENT_RESIZED ||
1447 sdl_e.window.event == SDL_WINDOWEVENT_MAXIMIZED ||
1448 sdl_e.window.event == SDL_WINDOWEVENT_RESTORED) {
1449 react_to_window_resized();
1450 } else if (sdl_e.window.event ==
1451 SDL_WINDOWEVENT_LEAVE) {
1452 /* This tells the dragging code to not respond */
1453 last_mouse_x = -1;
1454 last_mouse_y = -1;
1457 break;
1458 case SDL_MOUSEMOTION:
1460 if (sdl_e.motion.state & SDL_BUTTON_LMASK) {
1461 int x = sdl_e.motion.x;
1462 int y = sdl_e.motion.y;
1464 if (last_mouse_x >= 0 &&
1465 last_mouse_y >= 0) {
1466 if (selected_vertex != (size_t) -1) {
1467 q->v[selected_vertex].x += (x -
1468 last_mouse_x);
1469 q->v[selected_vertex].y += (y -
1470 last_mouse_y);
1471 } else {
1472 offset_x += (x - last_mouse_x);
1473 offset_y += (y - last_mouse_y);
1476 } else {
1477 recalculate_selected_items(sdl_e.motion.x,
1478 sdl_e.motion.y);
1481 last_mouse_x = sdl_e.motion.x;
1482 last_mouse_y = sdl_e.motion.y;
1483 break;
1484 case SDL_MOUSEBUTTONUP:
1486 if ((sdl_e.button.state & SDL_BUTTON_LMASK) &&
1487 ui_action != UIA_NEW_VERTEX) {
1488 last_mouse_x = -1;
1489 last_mouse_y = -1;
1492 recalculate_selected_items(sdl_e.button.x,
1493 sdl_e.button.y);
1494 break;
1495 case SDL_MOUSEBUTTONDOWN:
1497 if (!(sdl_e.button.state & SDL_BUTTON_LMASK)) {
1498 break;
1501 if (ui_action != UIA_NEW_VERTEX) {
1502 last_mouse_x = -1;
1503 last_mouse_y = -1;
1506 switch (ui_action) {
1507 case UIA_MUTATE:
1509 if (selected_vertex == (size_t) -1) {
1510 break;
1513 ui_e = (struct ui_event) {
1514 /* */
1515 .type = ET_MUTATE, .idx_1 =
1516 selected_vertex
1518 ret = eq_push(&ui_e);
1519 ui_action = UIA_NONE;
1520 break;
1521 case UIA_NEW_VERTEX:
1523 if (selected_vertex != (size_t) -1 ||
1524 selected_edge_i != (size_t) -1 ||
1525 selected_edge_j != (size_t) -1) {
1526 break;
1529 int cx = sdl_e.button.x - offset_x;
1530 int cy = sdl_e.button.y - offset_y;
1532 ui_e = (struct ui_event) {
1533 /* */
1534 .type = ET_NEW_VERTEX, .int_1 = cx,
1535 .int_2 = cy
1537 ret = eq_push(&ui_e);
1538 ui_action = UIA_NONE;
1539 break;
1540 case UIA_NEW_EDGE_1:
1541 case UIA_NEW_H_EDGE_1:
1542 case UIA_RENAME:
1544 if (selected_vertex == (size_t) -1) {
1545 ui_action = UIA_NONE;
1546 break;
1549 last_clicked_vertex = selected_vertex;
1551 if (ui_action == UIA_NEW_EDGE_1) {
1552 ui_action = UIA_NEW_EDGE_2;
1553 } else if (ui_action == UIA_NEW_H_EDGE_1) {
1554 ui_action = UIA_NEW_H_EDGE_2;
1555 } else if (ui_action == UIA_RENAME) {
1556 ui_action = UIA_ENTER_RENAME;
1557 input_len = 0;
1558 input_idx = 0;
1559 input[0] = '\0';
1560 SDL_StartTextInput();
1562 /* Intentionally not copying null terminator */
1563 strncpy(input_with_prefix, "New name: ",
1564 10);
1565 rerender_input_string = 1;
1568 break;
1569 case UIA_NEW_EDGE_2:
1570 case UIA_NEW_H_EDGE_2:
1572 if (selected_vertex == (size_t) -1 ||
1573 selected_vertex == last_clicked_vertex) {
1574 ui_action = UIA_NONE;
1575 last_clicked_vertex = (size_t) -1;
1576 break;
1579 ui_e = (struct ui_event) {
1580 /* */
1581 .type = ET_NEW_EDGE, .idx_1 =
1582 last_clicked_vertex, .idx_2 =
1583 selected_vertex, .a = 1, .b =
1584 (ui_action == UIA_NEW_EDGE_2 ?
1585 1 : 2)
1587 ret = eq_push(&ui_e);
1588 ui_action = UIA_NONE;
1589 last_clicked_vertex = (size_t) -1;
1590 break;
1591 case UIA_DELETE:
1593 if (selected_vertex != (size_t) -1) {
1594 ui_e = (struct ui_event) {
1595 /* */
1596 .type = ET_DELETE_VERTEX,
1597 .idx_1 = selected_vertex
1599 ret = eq_push(&ui_e);
1600 } else if (selected_edge_i != (size_t) -1 &&
1601 selected_edge_j != (size_t) -1) {
1602 ui_e = (struct ui_event) {
1603 /* */
1604 .type = ET_DELETE_EDGE, .idx_1 =
1605 selected_edge_i,
1606 .idx_2 =
1607 selected_edge_j
1609 ret = eq_push(&ui_e);
1612 ui_action = UIA_NONE;
1613 break;
1614 case UIA_INC_FATNESS:
1615 case UIA_DEC_FATNESS:
1617 if (selected_vertex == (size_t) -1) {
1618 break;
1621 ui_e = (struct ui_event) {
1622 /* */
1623 .type = ET_CHANGE_FATNESS, .idx_1 =
1624 selected_vertex, .int_1 =
1625 (ui_action ==
1626 UIA_INC_FATNESS
1627 ? 1 : -1)
1629 ret = eq_push(&ui_e);
1630 ui_action = UIA_NONE;
1631 break;
1632 case UIA_NONE:
1633 case UIA_ASK_QUIT:
1634 case UIA_ENTER_SAVE:
1635 case UIA_ENTER_LOAD:
1636 case UIA_ENTER_RENAME:
1637 case UIA_LEN:
1638 break;
1641 break;
1644 if (ret) {
1645 goto done;
1649 if (load_requested ||
1650 save_requested) {
1651 save_load_delay = 30;
1652 char *f = 0;
1653 int r = 0;
1655 if (load_requested) {
1656 r = choose_load_file(&f);
1657 } else {
1658 r = choose_save_file(&f);
1661 if (!r) {
1662 if (f) {
1663 ui_e = (struct ui_event) { .str = f };
1664 ui_e.type = load_requested ? ET_LOAD : ET_SAVE;
1666 /* f is freed on next ui_finish_frame */
1667 free_this = f;
1668 ret = eq_push(&ui_e);
1670 } else {
1671 ui_action = load_requested ? UIA_ENTER_LOAD :
1672 UIA_ENTER_SAVE;
1673 input_idx = 0;
1674 input_len = 0;
1675 input[0] = '\0';
1676 SDL_StartTextInput();
1678 /* Intentionally not copying null terminator */
1679 strncpy(input_with_prefix, "Filename: ", 10);
1680 rerender_input_string = 1;
1684 /* framelimit */
1685 now = SDL_GetTicks();
1687 if (frame_start_ticks < now) {
1688 Uint32 elapsed_time = now - frame_start_ticks;
1690 if (elapsed_time < TICKS_PER_FRAME) {
1691 SDL_Delay(TICKS_PER_FRAME - elapsed_time);
1695 done:
1697 return ret;
1700 /* Return an event to the main loop */
1701 int ui_get_event(struct ui_event *e, uint_fast8_t *more)
1703 eq_pop(e);
1704 *more = eq_head != eq_tail;
1706 return 0;