Add a bunch of overflow checks around malloc()
[clav.git] / ui-sdl.c
blobb447a1c40f8673b0f023b8e4931f535a5e360ffd
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 /* Window title */
221 static char window_title[2048];
223 /* What name we last loaded/saved */
224 static char current_filename[2048] = { 'U', 'n', 't', 'i', 't', 'l', 'e', 'd',
225 0 };
227 /* Whether we've edited since last loading/saving */
228 static uint_fast8_t current_file_edited = 0;
230 /* Maximum length of an input string we'll accept */
231 static size_t max_input_size = 1 << 10;
233 /* Current position in input */
234 static size_t input_idx;
236 /* Current length of string held in input */
237 static size_t input_len;
239 /* If a string is being typed in, what it is (in UTF-8 as per SDL2) */
240 static char *input;
242 /* Input, with `Filename:' or `New name:' prepended (for rendering) */
243 static char *input_with_prefix;
245 /* Texture for what user is currently entering */
246 static SDL_Texture *input_texture;
248 /* Whether we need to re-render the input */
249 static uint_fast8_t rerender_input_string;
251 /* To prevent awkward repeats in SDL events, rate-limit `s' and `l' */
252 static uint_fast8_t save_load_delay;
254 /* If something needs to be freed next frame, but not before */
255 static void *free_this;
257 /* GCD */
258 static int gcd(uint_fast8_t x, uint_fast8_t y)
260 uint_fast8_t r = 0;
262 if (!x &&
263 !y) {
264 return 1;
267 while (y) {
268 r = x % y;
269 x = y;
270 y = r;
273 return x;
276 /* Allocate and print a rational in the way a user expects */
277 static int pretty_fraction(struct rational *r, char **out)
279 int ret = 0;
281 if (r->q == 1) {
282 size_t len = snprintf(0, 0, "%d", (int) r->p);
284 if (len + 1 < len) {
285 ret = errno = EOVERFLOW;
286 perror(L(""));
287 goto done;
290 if (!(*out = malloc(len + 1))) {
291 ret = errno;
292 perror(L("malloc"));
293 goto done;
296 sprintf(*out, "%d", (int) r->p);
297 goto done;
300 size_t len = snprintf(0, 0, "%d/%u", (int) r->p, (unsigned int) r->q);
302 if (len + 1 < len) {
303 ret = errno = EOVERFLOW;
304 perror(L(""));
305 goto done;
308 if (!(*out = malloc(len + 1))) {
309 ret = errno;
310 perror(L("malloc"));
311 goto done;
314 sprintf(*out, "%d/%u", (int) r->p, (unsigned int) r->q);
315 done:
317 return ret;
320 /* Render text to texture */
321 static int render_text(const char *text, SDL_Texture **out)
323 if (!out) {
324 return 0;
327 if (*out) {
328 SDL_DestroyTexture(*out);
331 int ret = 0;
332 SDL_Surface *surf = 0;
334 if (!(surf = TTF_RenderUTF8_Blended_Wrapped(normal_font, text,
335 color_font, 800))) {
336 fprintf(stderr, L("TTF_RenderUTF8_Shaded(): %s\n"),
337 TTF_GetError());
338 ret = ENOMEDIUM;
339 goto done;
342 if (!(*out = SDL_CreateTextureFromSurface(sdl_renderer, surf))) {
343 fprintf(stderr, L("SDL_CreateTextureFromSurface(): %s\n"),
344 SDL_GetError());
345 ret = ENOMEDIUM;
346 goto done;
349 done:
350 SDL_FreeSurface(surf);
352 return ret;
355 /* Load fonts */
356 static int load_fonts(void)
358 int ret = 0;
360 if (normal_font) {
361 TTF_CloseFont(normal_font);
362 normal_font = 0;
365 if (!(normal_font = TTF_OpenFont(FONT_PATH, base_font_size))) {
366 ret = ENOMEDIUM;
367 fprintf(stderr, L("TTF_OpenFont(): %s\n"), TTF_GetError());
368 goto done;
371 for (size_t j = 0; j < UIA_LEN; ++j) {
372 if ((ret = render_text(bottom_string[j], &bottom_text[j]))) {
373 goto done;
377 done:
379 if (ret) {
380 if (normal_font) {
381 TTF_CloseFont(normal_font);
384 normal_font = 0;
387 return ret;
390 /* Insert str into input at given position */
391 static void text_input(const char *str)
393 size_t l = strlen(str);
395 if (l + input_len + 1 >= max_input_size) {
396 return;
399 for (size_t k = input_len; k > input_idx; --k) {
400 input[k + l] = input[k];
403 /* Special-case for input_idx: 0 prohibits `>=' above */
404 input[input_idx + l] = input[input_idx];
406 for (size_t k = 0; k < l; ++k) {
407 input[input_idx + k] = str[k];
410 input_idx += l;
411 input_len += l;
412 rerender_input_string = 1;
415 /* Move input cursor left */
416 static void text_input_left(void)
418 if (input_idx > 0) {
419 input_idx--;
422 while (input_idx > 0) {
423 unsigned char c = input[input_idx];
425 if ((c & 0xc0) == 0x80) {
426 input_idx--;
427 } else {
428 break;
433 /* Move input cursor right */
434 static void text_input_right(void)
436 size_t adv = 1;
437 unsigned char c = input[input_idx];
439 if ((c & 0x80) == 0x0) {
440 adv = 1;
441 } else if ((c & 0xe0) == 0xc0) {
442 adv = 2;
443 } else if ((c & 0xf0) == 0xe0) {
444 adv = 3;
445 } else if ((c & 0xf8) == 0xf0) {
446 adv = 4;
449 if (input_idx + adv <= input_len) {
450 input_idx += adv;
454 /* Backspace */
455 static void text_input_bs(void)
457 if (!input_idx) {
458 return;
461 size_t bs = 1;
463 while (input_idx > bs) {
464 unsigned char c = input[input_idx - bs];
466 if ((c & 0xc0) == 0x80) {
467 bs++;
468 } else {
469 break;
473 for (size_t k = input_idx - bs; k <= input_len - bs; k++) {
474 input[k] = input[k + bs];
477 input_len -= bs;
478 input_idx -= bs;
479 rerender_input_string = 1;
482 /* Set the title to something like `clav-sdl - foobar.txt*' */
483 static void update_window_title(void)
485 size_t cutoff = sizeof (window_title) / sizeof (window_title[0]);
486 size_t r = snprintf(window_title, cutoff, "clav-sdl - %s%s",
487 current_filename, (current_file_edited ? "*" : ""));
489 if (r >= cutoff) {
490 window_title[cutoff - 1] = '\0';
493 SDL_SetWindowTitle(sdl_win, window_title);
496 /* Make sure the window title displays as edited */
497 static void mark_as_edited(void)
499 if (current_file_edited) {
500 return;
503 current_file_edited = 1;
504 update_window_title();
507 /* Convert `internal coordinates' to pixel coordinates */
508 static void internal_to_pixel_xy(int in_x, int in_y, int *out_x, int *out_y)
510 *out_x = in_x + offset_x;
511 *out_y = in_y + offset_y;
514 /* Convert pixel coordinates to `internal coordinates' */
515 static void pixel_to_internal_xy(int in_x, int in_y, int *out_x, int *out_y)
517 *out_x = in_x - offset_x;
518 *out_y = in_y - offset_y;
521 /* Set selected_vertex and selected_edge_{i,j} */
522 static int recalculate_selected_items(int mx, int my)
524 int x = 0;
525 int y = 0;
526 int ret = 0;
527 char *s = 0;
528 char *sij = 0;
529 char *sji = 0;
531 pixel_to_internal_xy(mx, my, &x, &y);
532 size_t last_vertex = selected_vertex;
533 size_t last_edge_i = selected_edge_i;
534 size_t last_edge_j = selected_edge_j;
536 selected_vertex = (size_t) -1;
537 selected_edge_i = (size_t) -1;
538 selected_edge_j = (size_t) -1;
540 if (last_vertex != (size_t) -1) {
541 struct vertex *v = &(q->v[last_vertex]);
542 int r = base_node_radius + v->fatness * node_radius_per_fatness;
544 if (x > v->x - r &&
545 x < v->x + r &&
546 y > v->y - r &&
547 y < v->y + r) {
548 selected_vertex = last_vertex;
549 goto compute_str;
553 for (size_t j = q->v_num; j > 0; --j) {
554 struct vertex *v = &(q->v[j - 1]);
555 int r = base_node_radius + v->fatness * node_radius_per_fatness;
557 if (x > v->x - r &&
558 x < v->x + r &&
559 y > v->y - r &&
560 y < v->y + r) {
561 selected_vertex = j - 1;
562 goto compute_str;
566 for (size_t j = 1; j < q->v_num; ++j) {
567 struct vertex *v1 = &(q->v[j]);
569 for (size_t i = 0; i < j; ++i) {
570 if (!q->e[i * q->v_len + j].p &&
571 !q->e[j * q->v_len + i].p) {
572 continue;
575 struct vertex *v2 = &(q->v[i]);
577 if ((x - edge_margin > v1->x &&
578 x - edge_margin > v2->x) ||
579 (x + edge_margin < v1->x &&
580 x + edge_margin < v2->x) ||
581 (y - edge_margin > v1->y &&
582 y - edge_margin > v2->y) ||
583 (y + edge_margin < v1->y &&
584 y + edge_margin < v2->y)) {
585 continue;
588 if (v1->x == v2->x) {
589 if (x + edge_margin > v1->x &&
590 x - edge_margin < v1->x) {
591 selected_edge_i = i;
592 selected_edge_j = j;
593 goto compute_str;
595 } else if (v1->y == v2->y) {
596 if (y + edge_margin > v1->y &&
597 y - edge_margin < v1->y) {
598 selected_edge_i = i;
599 selected_edge_j = j;
600 goto compute_str;
602 } else {
603 double m1 = ((double) (v2->y - v1->y)) /
604 ((double) (v2->x - v1->x));
605 double m2 = -1.0 / m1;
606 double xint = ((double) y - (double) v1->y +
607 m1 * v1->x - m2 * x) / (m1 - m2);
608 double yint = m1 * xint - m1 * v1->x +
609 (double) v1->y;
611 if ((x - xint) * (x - xint) + (y - yint) * (y -
612 yint)
613 < edge_margin * edge_margin) {
614 selected_edge_i = i;
615 selected_edge_j = j;
616 goto compute_str;
622 compute_str:
624 if (selected_vertex != (size_t) -1 &&
625 (selected_vertex != last_vertex ||
626 dragged_selected_vertex)) {
627 dragged_selected_vertex = 0;
628 struct vertex *v = &(q->v[selected_vertex]);
629 size_t len = snprintf(0, 0,
630 "Name: %s\nFatness: %d\nPosition: (%d,%d)",
631 v->name,
632 (int) v->fatness, v->x, v->y);
634 if (len + 1 < len) {
635 ret = errno = EOVERFLOW;
636 perror(L(""));
637 goto done;
640 if (!(s = malloc(len + 1))) {
641 ret = errno;
642 perror(L("malloc"));
643 goto done;
646 sprintf(s, "Name: %s\nFatness: %d\nPosition: (%d,%d)",
647 v->name, (int) v->fatness, v->x, v->y);
649 if ((ret = render_text(s, &selected_info))) {
650 goto done;
652 } else if ((selected_edge_i != last_edge_i ||
653 selected_edge_j != last_edge_j) &&
654 selected_edge_i != (size_t) -1 &&
655 selected_edge_j != (size_t) -1) {
656 struct vertex *i = &(q->v[selected_edge_i]);
657 struct vertex *j = &(q->v[selected_edge_j]);
658 struct rational *eij = &(q->e[selected_edge_i * q->v_len +
659 selected_edge_j]);
660 struct rational *eji = &(q->e[selected_edge_j * q->v_len +
661 selected_edge_i]);
663 if ((ret = pretty_fraction(eij, &sij))) {
664 goto done;
667 if ((ret = pretty_fraction(eji, &sji))) {
668 goto done;
671 size_t len = snprintf(0, 0,
672 "%s \u2192 %s: %s\n%s \u2192 %s: %s",
673 i->name, j->name, sij,
674 j->name, i->name, sji);
676 if (len + 1 < len) {
677 ret = errno = EOVERFLOW;
678 perror(L(""));
679 goto done;
682 if (!(s = malloc(len + 1))) {
683 ret = errno;
684 perror(L("malloc"));
685 goto done;
688 sprintf(s, "%s \u2192 %s: %s\n%s \u2192 %s: %s", i->name,
689 j->name, sij, j->name, i->name, sji);
691 if ((ret = render_text(s, &selected_info))) {
692 goto done;
696 done:
697 free(s);
698 free(sij);
699 free(sji);
701 return ret;
704 /* Render vertex names as textures */
705 static int render_vertex_names(void)
707 if (vertex_names) {
708 for (size_t j = 0; j < vertex_names_len; ++j) {
709 SDL_DestroyTexture(vertex_names[j]);
710 vertex_names[j] = 0;
714 if (!q->v_num) {
715 return 0;
718 if ((q->v_num * sizeof *vertex_names) / q->v_num !=
719 sizeof *vertex_names) {
720 errno = EOVERFLOW;
721 perror(L(""));
722 vertex_names_len = 0;
724 return EOVERFLOW;
727 if (!(vertex_names = realloc(vertex_names, q->v_num *
728 sizeof(*vertex_names)))) {
729 int sv_err = errno;
731 perror(L("realloc"));
732 vertex_names_len = 0;
734 return sv_err;
737 vertex_names_len = q->v_num;
739 for (size_t j = 0; j < vertex_names_len; ++j) {
740 vertex_names[j] = 0;
743 for (size_t j = 0; j < vertex_names_len; ++j) {
744 int ret = 0;
746 if ((ret = render_text(q->v[j].name, &(vertex_names[j])))) {
747 return ret;
751 return 0;
754 /* Get information about the window */
755 static void react_to_window_resized(void)
757 int old_pix_width = da_pix_width;
758 int old_pix_height = da_pix_height;
760 SDL_GetWindowSize(sdl_win, &da_pix_width, &da_pix_height);
762 if (old_pix_width == da_pix_width &&
763 old_pix_height == da_pix_height) {
764 return;
767 offset_x += (da_pix_width - old_pix_width) / 2;
768 offset_y += (da_pix_height - old_pix_height) / 2;
771 /* Pop from queue */
772 static void eq_pop(struct ui_event *out)
774 if (eq_head == eq_tail) {
775 *out = (struct ui_event) { 0 };
777 return;
780 memcpy(out, eq_buf + eq_head, sizeof *out);
781 eq_buf[eq_head] = (struct ui_event) { 0 };
782 eq_head = (eq_head + 1) % eq_len;
785 /* Push into queue */
786 static int eq_push(struct ui_event *in)
788 if (((eq_tail + 1) % eq_len) == eq_head) {
789 void *newmem;
791 if ((eq_len * sizeof *in) >= ((size_t) -1) / 2) {
792 fprintf(stderr, L(
793 "eq_push: Impossibly large buffer\n"));
795 return ENOMEM;
798 if ((eq_len * 2) / 2 != eq_len ||
799 (eq_len * 2 * sizeof *eq_buf) / (eq_len * 2) !=
800 sizeof *eq_buf) {
801 errno = EOVERFLOW;
802 perror(L(""));
804 return EOVERFLOW;
807 if (!(newmem = realloc(eq_buf, (eq_len * 2) *
808 sizeof *eq_buf))) {
809 int sv_err = errno;
811 perror(L("realloc"));
813 return sv_err;
816 eq_buf = (struct ui_event *) newmem;
817 eq_len *= 2;
820 memcpy(eq_buf + eq_tail, in, sizeof *in);
821 eq_tail = (eq_tail + 1) % eq_len;
823 return 0;
826 /* Initialize SDL */
827 int ui_init(struct quiver *i_q)
829 int ret;
831 q = i_q;
832 size_t padding = strlen("Filename: ");
834 /* No need for overflow check, max_input_size is capped */
835 if (!(input_with_prefix = malloc(max_input_size + padding))) {
836 ret = errno;
837 perror(L("malloc"));
839 return ret;
842 strcpy(input_with_prefix, "Filename: ");
843 input = input_with_prefix + padding;
844 input_idx = 0;
845 input_len = 0;
847 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
848 fprintf(stderr, L("SDL_Init(): %s\n"), SDL_GetError());
850 return ENOMEDIUM;
853 sdl_win = SDL_CreateWindow("clav-sdl", SDL_WINDOWPOS_UNDEFINED,
854 SDL_WINDOWPOS_UNDEFINED, 1000, 1000,
855 SDL_WINDOW_SHOWN |
856 SDL_WINDOW_RESIZABLE);
858 if (!sdl_win) {
859 fprintf(stderr, L("SDL_CreateWindow(): %s\n"), SDL_GetError());
861 return ENOMEDIUM;
864 sdl_renderer = SDL_CreateRenderer(sdl_win, -1,
865 SDL_RENDERER_ACCELERATED);
867 if (!sdl_renderer) {
868 fprintf(stderr, L("SDL_CreateRenderer(): %s\n"),
869 SDL_GetError());
871 return ENOMEDIUM;
874 if (TTF_Init() < 0) {
875 fprintf(stderr, L("TTF_Init(): %s\n"), TTF_GetError());
877 return ENOMEDIUM;
880 if ((ret = load_fonts())) {
881 goto done;
884 if ((ret = render_vertex_names())) {
885 goto done;
888 if ((ret = SDL_SetRenderDrawColor(sdl_renderer, color_bg.r, color_bg.g,
889 color_bg.b, color_bg.a))) {
890 fprintf(stderr, L("SDL_SetRenderDrawColor(): %s\n"),
891 SDL_GetError());
892 goto done;
895 if ((ret = SDL_RenderClear(sdl_renderer))) {
896 fprintf(stderr, L("SDL_RenderClear(): %s\n"), SDL_GetError());
897 goto done;
900 update_window_title();
901 SDL_RenderPresent(sdl_renderer);
902 react_to_window_resized();
904 /* Set up queue for returning data */
905 if (!(eq_buf = calloc(2, sizeof *eq_buf))) {
906 ret = errno;
907 perror(L("calloc"));
908 goto done;
911 eq_len = 2;
912 eq_head = 0;
913 eq_tail = 0;
914 done:
916 return ret;
919 /* Deal with the fact that the quiver was changed */
920 int ui_respond_quiver_change(void)
922 return render_vertex_names();
925 /* Acknowledge a successful load */
926 int ui_respond_successful_load(const char *filename)
928 const char *tail = (const char *) strrchr(filename, '/');
930 if (!tail) {
931 tail = filename;
932 } else {
933 tail++;
936 size_t cutoff = sizeof(current_filename) / sizeof(current_filename[0]);
937 size_t r = snprintf(current_filename, cutoff, "%s", tail);
939 if (r >= cutoff) {
940 current_filename[cutoff - 1] = '\0';
943 current_file_edited = 0;
944 update_window_title();
946 return 0;
949 /* Acknowledge a successful save */
950 int ui_respond_successful_save(const char *filename)
952 return ui_respond_successful_load(filename);
955 /* Tear down SDL */
956 int ui_teardown(void)
958 if (vertex_names) {
959 for (size_t j = 0; j < vertex_names_len; ++j) {
960 SDL_DestroyTexture(vertex_names[j]);
961 vertex_names[j] = 0;
964 free(vertex_names);
965 vertex_names = 0;
968 for (size_t j = 0; j < UIA_LEN; ++j) {
969 SDL_DestroyTexture(bottom_text[j]);
970 bottom_text[j] = 0;
973 if (selected_info) {
974 SDL_DestroyTexture(selected_info);
975 selected_info = 0;
978 if (input_texture) {
979 SDL_DestroyTexture(input_texture);
980 input_texture = 0;
983 if (normal_font) {
984 TTF_CloseFont(normal_font);
985 normal_font = 0;
988 if (sdl_win) {
989 SDL_DestroyWindow(sdl_win);
990 sdl_win = 0;
993 free(eq_buf);
994 eq_buf = 0;
995 free(input_with_prefix);
996 input_with_prefix = 0;
997 input = 0;
998 SDL_Quit();
1000 return 0;
1003 /* Record that a frame has been started */
1004 int ui_start_frame(void)
1006 int ret = 0;
1007 int rho = 0;
1008 SDL_Rect r = { 0 };
1009 Uint32 dummy_format;
1010 int dummy_access;
1011 int tex_w;
1012 int tex_h;
1014 frame_start_ticks = SDL_GetTicks();
1016 if (!redraw) {
1017 goto done;
1020 redraw = 0;
1022 /* Draw the damn thing */
1023 SDL_SetRenderDrawColor(sdl_renderer, color_bg.r, color_bg.g, color_bg.b,
1024 color_bg.a);
1025 SDL_RenderClear(sdl_renderer);
1027 /* Special case for if text input is going on */
1028 if (rerender_input_string) {
1029 rerender_input_string = 0;
1031 if ((ret = render_text(input_with_prefix, &input_texture))) {
1032 goto done;
1036 /* Draw each edge */
1037 for (size_t j = 0; j < q->v_num; ++j) {
1038 for (size_t i = 0; i < j; ++i) {
1039 /* First, determine if we're looking at a half-edge or a full-edge */
1040 int d = gcd(q->v[i].fatness, q->v[j].fatness);
1041 struct rational *eij = &(q->e[i * q->v_len + j]);
1042 struct rational *eji = &(q->e[j * q->v_len + i]);
1043 int cx = 0;
1044 int cy = 0;
1045 int cx2 = 0;
1046 int cy2 = 0;
1047 double theta = 0.0;
1049 if (!eij->p &&
1050 !eji->p) {
1051 continue;
1054 internal_to_pixel_xy(q->v[i].x, q->v[i].y, &cx, &cy);
1055 internal_to_pixel_xy(q->v[j].x, q->v[j].y, &cx2, &cy2);
1057 if (selected_edge_i == i &&
1058 selected_edge_j == j) {
1059 if ((ret = SDL_SetRenderDrawColor(sdl_renderer,
1060 color_e_sel.r,
1061 color_e_sel.g,
1062 color_e_sel.b,
1063 color_e_sel.a)))
1065 fprintf(stderr, L(
1066 "SDL_RenderDrawColor(): %s\n"),
1067 SDL_GetError());
1068 goto done;
1071 for (int id = -edge_margin; id < edge_margin;
1072 ++id) {
1073 for (int jd = -edge_margin; jd <
1074 edge_margin; ++jd) {
1075 if ((ret = SDL_RenderDrawLine(
1076 sdl_renderer, cx +
1077 id, cy + jd,
1078 cx2 + id, cy2 +
1079 jd))) {
1080 fprintf(stderr, L(
1081 "SDL_RenderDrawLine(): %s\n"),
1082 SDL_GetError());
1083 goto done;
1089 /* This is the (eij)/dj = -(eji)/di condition */
1090 if (eij->p * q->v[i].fatness * eji->q != -eji->p *
1091 q->v[j].fatness * eij->q) {
1092 ret = SDL_SetRenderDrawColor(sdl_renderer,
1093 color_e_abnormal.r,
1094 color_e_abnormal.g,
1095 color_e_abnormal.b,
1096 color_e_abnormal.a);
1097 } else if (abs(eij->p) * d == q->v[j].fatness *
1098 eij->q) {
1099 ret = SDL_SetRenderDrawColor(sdl_renderer,
1100 color_e_normal.r,
1101 color_e_normal.g,
1102 color_e_normal.b,
1103 color_e_normal.a);
1104 } else if (2 * abs(eij->p) * d == q->v[j].fatness *
1105 eij->q) {
1106 ret = SDL_SetRenderDrawColor(sdl_renderer,
1107 color_e_half.r,
1108 color_e_half.g,
1109 color_e_half.b,
1110 color_e_half.a);
1111 } else {
1112 ret = SDL_SetRenderDrawColor(sdl_renderer,
1113 color_e_abnormal.r,
1114 color_e_abnormal.g,
1115 color_e_abnormal.b,
1116 color_e_abnormal.a);
1119 if (ret) {
1120 fprintf(stderr, L(
1121 "SDL_SetRenderDrawColor(): %s\n"),
1122 SDL_GetError());
1123 goto done;
1126 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy, cx2,
1127 cy2))) {
1128 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1129 SDL_GetError());
1130 goto done;
1133 if (cx == cx2) {
1134 theta = (cy2 > cy) ? M_PI / 2.0 : -M_PI / 2.0;
1135 } else {
1136 theta = atan2f(cy2 - cy, cx2 - cx);
1139 if ((eij->p < 0)) {
1140 theta += M_PI;
1143 cx = (cx + cx2) / 2;
1144 cy = (cy + cy2) / 2;
1145 cx2 = cx + arrow_length * cos(theta + arrow_angle);
1146 cy2 = cy + arrow_length * sin(theta + arrow_angle);
1148 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy, cx2,
1149 cy2))) {
1150 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1151 SDL_GetError());
1152 goto done;
1155 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy, cx +
1156 arrow_length * cos(theta -
1157 arrow_angle),
1158 cy +
1159 arrow_length * sin(theta -
1160 arrow_angle))))
1162 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1163 SDL_GetError());
1164 goto done;
1169 /* Draw each vertex as a box */
1170 for (size_t j = 0; j < q->v_num; ++j) {
1171 struct vertex *v = &(q->v[j]);
1172 int cx = 0;
1173 int cy = 0;
1175 internal_to_pixel_xy(v->x, v->y, &cx, &cy);
1177 /* Central square */
1178 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_NONE);
1179 SDL_SetRenderDrawColor(sdl_renderer, color_v.r, color_v.g,
1180 color_v.b, color_v.a);
1181 rho = base_node_radius + node_radius_per_fatness * v->fatness;
1182 r.x = cx - rho;
1183 r.y = cy - rho;
1184 r.w = 2 * rho;
1185 r.h = 2 * rho;
1186 SDL_RenderFillRect(sdl_renderer, &r);
1188 /* Outline */
1189 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
1191 if (j == selected_vertex ||
1192 j == last_clicked_vertex) {
1193 SDL_SetRenderDrawColor(sdl_renderer,
1194 color_outline_sel.r,
1195 color_outline_sel.g,
1196 color_outline_sel.b,
1197 color_outline_sel.a);
1198 } else {
1199 SDL_SetRenderDrawColor(sdl_renderer, color_outline.r,
1200 color_outline.g, color_outline.b,
1201 color_outline.a);
1204 r.x = cx - rho;
1205 r.y = cy - rho;
1206 r.w = 2 * rho - outline_width;
1207 r.h = outline_width;
1208 SDL_RenderFillRect(sdl_renderer, &r);
1209 r.x = cx + rho - outline_width;
1210 r.y = cy - rho;
1211 r.w = outline_width;
1212 r.h = 2 * rho - outline_width;
1213 SDL_RenderFillRect(sdl_renderer, &r);
1214 r.x = cx - rho + outline_width;
1215 r.y = cy + rho - outline_width;
1216 r.w = 2 * rho - outline_width;
1217 r.h = outline_width;
1218 SDL_RenderFillRect(sdl_renderer, &r);
1219 r.x = cx - rho;
1220 r.y = cy - rho + outline_width;
1221 r.w = outline_width;
1222 r.h = 2 * rho - outline_width;
1223 SDL_RenderFillRect(sdl_renderer, &r);
1225 /* Text */
1226 if (j >= vertex_names_len) {
1227 fprintf(stderr, L(
1228 "render_vertex_names() was not called, somehow\n"));
1229 ret = EINVAL;
1230 goto done;
1233 if (SDL_QueryTexture(vertex_names[j], &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 = cx - tex_w / 2;
1242 r.y = cy - tex_h / 2;
1243 r.w = tex_w;
1244 r.h = tex_h;
1245 SDL_RenderCopy(sdl_renderer, vertex_names[j], 0, &r);
1248 /* If adding a new vertex, draw preview */
1249 if (ui_action == UIA_NEW_VERTEX &&
1250 last_mouse_x != -1 &&
1251 last_mouse_y != -1) {
1252 /* Central square */
1253 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_NONE);
1254 SDL_SetRenderDrawColor(sdl_renderer, color_v_preview.r,
1255 color_v_preview.g, color_v_preview.b,
1256 color_v_preview.a);
1257 rho = base_node_radius;
1258 r.x = last_mouse_x - rho;
1259 r.y = last_mouse_y - rho;
1260 r.w = 2 * rho;
1261 r.h = 2 * rho;
1262 SDL_RenderFillRect(sdl_renderer, &r);
1264 /* Outline */
1265 SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
1266 SDL_SetRenderDrawColor(sdl_renderer, color_outline_preview.r,
1267 color_outline_preview.g,
1268 color_outline_preview.b,
1269 color_outline_preview.a);
1270 r.x = last_mouse_x - rho;
1271 r.y = last_mouse_y - rho;
1272 r.w = 2 * rho - outline_width;
1273 r.h = outline_width;
1274 SDL_RenderFillRect(sdl_renderer, &r);
1275 r.x = last_mouse_x + rho - outline_width;
1276 r.y = last_mouse_y - rho;
1277 r.w = outline_width;
1278 r.h = 2 * rho - outline_width;
1279 SDL_RenderFillRect(sdl_renderer, &r);
1280 r.x = last_mouse_x - rho + outline_width;
1281 r.y = last_mouse_y + rho - outline_width;
1282 r.w = 2 * rho - outline_width;
1283 r.h = outline_width;
1284 SDL_RenderFillRect(sdl_renderer, &r);
1285 r.x = last_mouse_x - rho;
1286 r.y = last_mouse_y - rho + outline_width;
1287 r.w = outline_width;
1288 r.h = 2 * rho - outline_width;
1289 SDL_RenderFillRect(sdl_renderer, &r);
1292 /* If adding a new edge, draw possible */
1293 if ((ui_action == UIA_NEW_EDGE_2 ||
1294 ui_action == UIA_NEW_H_EDGE_2) &&
1296 /* last_clicked_vertex != (size_t) -1 && */
1297 last_mouse_x != -1 &&
1298 last_mouse_y != -1) {
1299 int cx = 0;
1300 int cy = 0;
1302 if ((ret = SDL_SetRenderDrawColor(sdl_renderer,
1303 color_e_normal.r,
1304 color_e_normal.g,
1305 color_e_normal.b,
1306 color_e_normal.a))) {
1307 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1308 SDL_GetError());
1309 goto done;
1312 internal_to_pixel_xy(q->v[last_clicked_vertex].x,
1313 q->v[last_clicked_vertex].y, &cx, &cy);
1315 if ((ret = SDL_RenderDrawLine(sdl_renderer, cx, cy,
1316 last_mouse_x, last_mouse_y))) {
1317 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1318 SDL_GetError());
1319 goto done;
1323 /* Bottom text */
1324 if (SDL_QueryTexture(bottom_text[ui_action], &dummy_format,
1325 &dummy_access, &tex_w, &tex_h)) {
1326 fprintf(stderr, L("SDL_QueryTexture(): %s\n"), SDL_GetError());
1327 ret = ENOMEDIUM;
1328 goto done;
1331 r.x = text_border_padding;
1332 r.y = da_pix_height - tex_h - text_border_padding;
1333 r.w = tex_w;
1334 r.h = tex_h;
1335 SDL_RenderCopy(sdl_renderer, bottom_text[ui_action], 0, &r);
1337 /* If something is selected */
1338 if (selected_info &&
1339 (selected_vertex != (size_t) -1 ||
1340 (selected_edge_i != (size_t) -1 &&
1341 selected_edge_j != (size_t) -1))) {
1342 if (SDL_QueryTexture(selected_info, &dummy_format,
1343 &dummy_access, &tex_w, &tex_h)) {
1344 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1345 SDL_GetError());
1346 ret = ENOMEDIUM;
1347 goto done;
1350 r.x = text_border_padding;
1351 r.y = text_border_padding;
1352 r.w = tex_w;
1353 r.h = tex_h;
1354 SDL_RenderCopy(sdl_renderer, selected_info, 0, &r);
1357 /* If user is entering text */
1358 if (ui_action == UIA_ENTER_SAVE ||
1359 ui_action == UIA_ENTER_LOAD ||
1360 ui_action == UIA_ENTER_RENAME) {
1361 if (SDL_QueryTexture(bottom_text[ui_action], &dummy_format,
1362 &dummy_access, &tex_w, &tex_h)) {
1363 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1364 SDL_GetError());
1365 ret = ENOMEDIUM;
1366 goto done;
1369 r.x = text_border_padding;
1370 r.y = da_pix_height - tex_h - text_border_padding;
1372 if (SDL_QueryTexture(input_texture, &dummy_format,
1373 &dummy_access, &tex_w, &tex_h)) {
1374 fprintf(stderr, L("SDL_QueryTexture(): %s\n"),
1375 SDL_GetError());
1376 ret = ENOMEDIUM;
1377 goto done;
1380 r.y -= (tex_h + text_border_padding);
1381 r.w = tex_w;
1382 r.h = tex_h;
1383 SDL_RenderCopy(sdl_renderer, input_texture, 0, &r);
1385 /* Now draw a cursor */
1386 char store_idxchar = input[input_idx];
1388 input[input_idx] = '\0';
1390 if ((ret = TTF_SizeUTF8(normal_font, input_with_prefix, &tex_w,
1391 &tex_h))) {
1392 fprintf(stderr, L("TTF_SizeText(): %s\n"),
1393 TTF_GetError());
1394 goto done;
1397 input[input_idx] = store_idxchar;
1399 if ((ret = SDL_SetRenderDrawColor(sdl_renderer, color_font.r,
1400 color_font.g, color_font.b,
1401 color_font.a))) {
1402 fprintf(stderr, L("SDL_SetRenderDrawColor(): %s\n"),
1403 SDL_GetError());
1404 goto done;
1407 if ((ret = SDL_RenderDrawLine(sdl_renderer, r.x + tex_w, r.y,
1408 r.x + tex_w, r.y + tex_h))) {
1409 fprintf(stderr, L("SDL_RenderDrawLine(): %s\n"),
1410 SDL_GetError());
1411 goto done;
1415 done:
1417 return ret;
1420 /* Draw a frame, possibly sleeping for framelimit */
1421 int ui_finish_frame(void)
1423 int ret = 0;
1424 struct ui_event ui_e = { 0 };
1425 SDL_Event sdl_e = { 0 };
1426 Uint32 now = 0;
1427 uint_fast8_t save_requested = 0;
1428 uint_fast8_t load_requested = 0;
1430 if (free_this) {
1431 free(free_this);
1432 free_this = 0;
1435 if (save_load_delay) {
1436 save_load_delay--;
1439 SDL_RenderPresent(sdl_renderer);
1441 /* Handle user input */
1442 while (SDL_PollEvent(&sdl_e) != 0) {
1443 redraw = 1;
1444 SDL_Keycode k = 0;
1446 switch (sdl_e.type) {
1447 case SDL_QUIT:
1448 ui_e = (struct ui_event) { .type = ET_QUIT };
1449 ret = eq_push(&ui_e);
1450 break;
1451 case SDL_TEXTINPUT:
1452 text_input(sdl_e.text.text);
1453 break;
1454 case SDL_KEYUP:
1456 if (sdl_e.key.repeat) {
1457 break;
1460 k = sdl_e.key.keysym.sym;
1462 switch (ui_action) {
1463 case UIA_ENTER_SAVE:
1464 case UIA_ENTER_LOAD:
1465 case UIA_ENTER_RENAME:
1467 if (k == SDLK_ESCAPE) {
1468 SDL_StopTextInput();
1469 ui_action = UIA_NONE;
1470 last_clicked_vertex = (size_t) -1;
1471 } else if (k == SDLK_RETURN ||
1472 k == SDLK_RETURN2) {
1473 SDL_StopTextInput();
1474 ui_e = (struct ui_event) { .str =
1475 input };
1477 if (ui_action == UIA_ENTER_SAVE) {
1478 ui_e.type = ET_SAVE;
1479 } else if (ui_action ==
1480 UIA_ENTER_LOAD) {
1481 ui_e.type = ET_LOAD;
1482 } else if (ui_action ==
1483 UIA_ENTER_RENAME) {
1484 ui_e.type = ET_RENAME;
1485 ui_e.idx_1 =
1486 last_clicked_vertex;
1489 ret = eq_push(&ui_e);
1490 ui_action = UIA_NONE;
1491 last_clicked_vertex = (size_t) -1;
1492 } else if (k == SDLK_LEFT) {
1493 text_input_left();
1494 } else if (k == SDLK_RIGHT) {
1495 text_input_right();
1496 } else if (k == SDLK_BACKSPACE) {
1497 text_input_bs();
1500 break;
1501 case UIA_NONE:
1503 if (k == SDLK_q) {
1504 ui_action = UIA_ASK_QUIT;
1505 } else if (k == SDLK_d) {
1506 ui_action = UIA_DELETE;
1507 } else if (k == SDLK_e) {
1508 ui_action = UIA_NEW_EDGE_1;
1509 } else if (k == SDLK_f) {
1510 ui_action = UIA_INC_FATNESS;
1511 } else if (k == SDLK_g) {
1512 ui_action = UIA_DEC_FATNESS;
1513 } else if (k == SDLK_h) {
1514 ui_action = UIA_NEW_H_EDGE_1;
1515 } else if (k == SDLK_m) {
1516 ui_action = UIA_MUTATE;
1517 } else if (k == SDLK_r) {
1518 ui_action = UIA_RENAME;
1519 } else if (k == SDLK_v) {
1520 ui_action = UIA_NEW_VERTEX;
1521 } else if (k == SDLK_l &&
1522 !save_load_delay) {
1523 /* Don't load - SDL_KEYUP repeats */
1524 load_requested = 1;
1525 } else if (k == SDLK_s &&
1526 !save_load_delay) {
1527 save_requested = 1;
1530 break;
1531 case UIA_ASK_QUIT:
1533 if (k == SDLK_n ||
1534 k == SDLK_ESCAPE) {
1535 ui_action = UIA_NONE;
1536 } else if (k == SDLK_y) {
1537 ui_e = (struct ui_event) { .type =
1538 ET_QUIT };
1539 ret = eq_push(&ui_e);
1540 ui_action = UIA_NONE;
1543 break;
1544 default:
1546 if (k == SDLK_q ||
1547 k == SDLK_ESCAPE) {
1548 ui_action = UIA_NONE;
1551 break;
1554 break;
1555 case SDL_WINDOWEVENT:
1557 if (sdl_e.window.event == SDL_WINDOWEVENT_RESIZED ||
1558 sdl_e.window.event == SDL_WINDOWEVENT_MAXIMIZED ||
1559 sdl_e.window.event == SDL_WINDOWEVENT_RESTORED) {
1560 react_to_window_resized();
1561 } else if (sdl_e.window.event ==
1562 SDL_WINDOWEVENT_LEAVE) {
1563 /* This tells the dragging code to not respond */
1564 last_mouse_x = -1;
1565 last_mouse_y = -1;
1568 break;
1569 case SDL_MOUSEMOTION:
1571 if (sdl_e.motion.state & SDL_BUTTON_LMASK) {
1572 int x = sdl_e.motion.x;
1573 int y = sdl_e.motion.y;
1575 if (last_mouse_x >= 0 &&
1576 last_mouse_y >= 0) {
1577 if (selected_vertex != (size_t) -1) {
1578 q->v[selected_vertex].x += (x -
1579 last_mouse_x);
1580 q->v[selected_vertex].y += (y -
1581 last_mouse_y);
1582 dragged_selected_vertex = 1;
1583 recalculate_selected_items(
1584 sdl_e.motion.x,
1585 sdl_e.motion.y);
1586 mark_as_edited();
1587 } else {
1588 offset_x += (x - last_mouse_x);
1589 offset_y += (y - last_mouse_y);
1592 } else {
1593 recalculate_selected_items(sdl_e.motion.x,
1594 sdl_e.motion.y);
1597 last_mouse_x = sdl_e.motion.x;
1598 last_mouse_y = sdl_e.motion.y;
1599 break;
1600 case SDL_MOUSEBUTTONUP:
1602 if ((sdl_e.button.state & SDL_BUTTON_LMASK) &&
1603 ui_action != UIA_NEW_VERTEX) {
1604 last_mouse_x = -1;
1605 last_mouse_y = -1;
1608 recalculate_selected_items(sdl_e.button.x,
1609 sdl_e.button.y);
1610 break;
1611 case SDL_MOUSEBUTTONDOWN:
1613 if (!(sdl_e.button.state & SDL_BUTTON_LMASK)) {
1614 break;
1617 if (ui_action != UIA_NEW_VERTEX) {
1618 last_mouse_x = -1;
1619 last_mouse_y = -1;
1622 switch (ui_action) {
1623 case UIA_MUTATE:
1625 if (selected_vertex == (size_t) -1) {
1626 break;
1629 ui_e = (struct ui_event) {
1630 /* */
1631 .type = ET_MUTATE, .idx_1 =
1632 selected_vertex
1634 ret = eq_push(&ui_e);
1635 ui_action = UIA_NONE;
1636 break;
1637 case UIA_NEW_VERTEX:
1639 if (selected_vertex != (size_t) -1 ||
1640 selected_edge_i != (size_t) -1 ||
1641 selected_edge_j != (size_t) -1) {
1642 break;
1645 int cx = sdl_e.button.x - offset_x;
1646 int cy = sdl_e.button.y - offset_y;
1648 ui_e = (struct ui_event) {
1649 /* */
1650 .type = ET_NEW_VERTEX, .int_1 = cx,
1651 .int_2 = cy
1653 ret = eq_push(&ui_e);
1654 ui_action = UIA_NONE;
1655 break;
1656 case UIA_NEW_EDGE_1:
1657 case UIA_NEW_H_EDGE_1:
1658 case UIA_RENAME:
1660 if (selected_vertex == (size_t) -1) {
1661 ui_action = UIA_NONE;
1662 break;
1665 last_clicked_vertex = selected_vertex;
1667 if (ui_action == UIA_NEW_EDGE_1) {
1668 ui_action = UIA_NEW_EDGE_2;
1669 } else if (ui_action == UIA_NEW_H_EDGE_1) {
1670 ui_action = UIA_NEW_H_EDGE_2;
1671 } else if (ui_action == UIA_RENAME) {
1672 ui_action = UIA_ENTER_RENAME;
1673 input_len = 0;
1674 input_idx = 0;
1675 input[0] = '\0';
1676 SDL_StartTextInput();
1678 /* Intentionally not copying null terminator */
1679 strncpy(input_with_prefix, "New name: ",
1680 10);
1681 rerender_input_string = 1;
1684 break;
1685 case UIA_NEW_EDGE_2:
1686 case UIA_NEW_H_EDGE_2:
1688 if (selected_vertex == (size_t) -1 ||
1689 selected_vertex == last_clicked_vertex) {
1690 ui_action = UIA_NONE;
1691 last_clicked_vertex = (size_t) -1;
1692 break;
1695 ui_e = (struct ui_event) {
1696 /* */
1697 .type = ET_NEW_EDGE, .idx_1 =
1698 last_clicked_vertex, .idx_2 =
1699 selected_vertex, .a = 1, .b =
1700 (ui_action == UIA_NEW_EDGE_2 ?
1701 1 : 2)
1703 ret = eq_push(&ui_e);
1704 ui_action = UIA_NONE;
1705 last_clicked_vertex = (size_t) -1;
1706 break;
1707 case UIA_DELETE:
1709 if (selected_vertex != (size_t) -1) {
1710 ui_e = (struct ui_event) {
1711 /* */
1712 .type = ET_DELETE_VERTEX,
1713 .idx_1 = selected_vertex
1715 ret = eq_push(&ui_e);
1716 } else if (selected_edge_i != (size_t) -1 &&
1717 selected_edge_j != (size_t) -1) {
1718 ui_e = (struct ui_event) {
1719 /* */
1720 .type = ET_DELETE_EDGE, .idx_1 =
1721 selected_edge_i,
1722 .idx_2 =
1723 selected_edge_j
1725 ret = eq_push(&ui_e);
1728 ui_action = UIA_NONE;
1729 break;
1730 case UIA_INC_FATNESS:
1731 case UIA_DEC_FATNESS:
1733 if (selected_vertex == (size_t) -1) {
1734 break;
1737 ui_e = (struct ui_event) {
1738 /* */
1739 .type = ET_CHANGE_FATNESS, .idx_1 =
1740 selected_vertex, .int_1 =
1741 (ui_action ==
1742 UIA_INC_FATNESS
1743 ? 1 : -1)
1745 ret = eq_push(&ui_e);
1746 ui_action = UIA_NONE;
1747 break;
1748 case UIA_NONE:
1749 case UIA_ASK_QUIT:
1750 case UIA_ENTER_SAVE:
1751 case UIA_ENTER_LOAD:
1752 case UIA_ENTER_RENAME:
1753 case UIA_LEN:
1754 break;
1757 break;
1760 if (ret) {
1761 goto done;
1765 if (load_requested ||
1766 save_requested) {
1767 save_load_delay = 30;
1768 char *f = 0;
1769 int r = 0;
1771 if (load_requested) {
1772 r = choose_load_file(&f);
1773 } else {
1774 r = choose_save_file(&f);
1777 if (!r) {
1778 if (f) {
1779 ui_e = (struct ui_event) { .str = f };
1780 ui_e.type = load_requested ? ET_LOAD : ET_SAVE;
1782 /* f is freed on next ui_finish_frame */
1783 free_this = f;
1784 ret = eq_push(&ui_e);
1786 } else {
1787 ui_action = load_requested ? UIA_ENTER_LOAD :
1788 UIA_ENTER_SAVE;
1789 input_idx = 0;
1790 input_len = 0;
1791 input[0] = '\0';
1792 SDL_StartTextInput();
1794 /* Intentionally not copying null terminator */
1795 strncpy(input_with_prefix, "Filename: ", 10);
1796 rerender_input_string = 1;
1800 /* framelimit */
1801 now = SDL_GetTicks();
1803 if (frame_start_ticks < now) {
1804 Uint32 elapsed_time = now - frame_start_ticks;
1806 if (elapsed_time < TICKS_PER_FRAME) {
1807 SDL_Delay(TICKS_PER_FRAME - elapsed_time);
1811 done:
1813 return ret;
1816 /* Return an event to the main loop */
1817 int ui_get_event(struct ui_event *e, uint_fast8_t *more)
1819 eq_pop(e);
1820 *more = eq_head != eq_tail;
1822 if (e) {
1823 switch (e->type) {
1824 case ET_NONE:
1825 case ET_LOAD:
1826 case ET_SAVE:
1827 case ET_QUIT:
1828 break;
1829 case ET_CHANGE_FATNESS:
1830 case ET_DELETE_EDGE:
1831 case ET_DELETE_VERTEX:
1832 case ET_MUTATE:
1833 case ET_NEW_EDGE:
1834 case ET_NEW_VERTEX:
1835 case ET_RENAME:
1836 mark_as_edited();
1837 break;
1841 return 0;