2 * Copyright (c) 2016-2019, 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
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.
27 #include "color-selection.h"
28 #include "file-selection.h"
31 #include "ui-sdl-font.h"
34 #define TICKS_PER_FRAME (1000 / 60)
37 #define M_PI (3.14159265358979323846)
40 /* What clicking does */
41 static enum ui_action
{
62 /* Whether to cycle the state indefinitely (add many vertices, &c) */
63 static uint_fast8_t ui_action_loop
;
65 /* The window we'll be using */
66 static SDL_Window
*sdl_win
;
68 /* The renderer we'll be using */
69 static SDL_Renderer
*sdl_renderer
;
71 /* How to limit the framerate */
72 static Uint32 frame_start_ticks
;
74 /* Whether the scene needs to be drawn again */
75 static uint_fast8_t redraw
;
77 /* Whether a vertex was just dragged (necessitating info redraw) */
78 static uint_fast8_t dragged_selected_vertex
;
80 /* The informative texture */
81 static SDL_Texture
*selected_info
;
83 /* Buffer for event queue */
84 static struct ui_event
*eq_buf
;
86 /* Current max length of event queue */
89 /* Current head of queue */
90 static size_t eq_head
;
92 /* Current tail of queue */
93 static size_t eq_tail
;
95 /* The quiver we'll be using */
96 static struct quiver
*q
;
98 /* Width of drawing area in pixels */
99 static int da_pix_width
;
101 /* Height of drawing area in pixels */
102 static int da_pix_height
;
104 /* The background color */
105 static SDL_Color color_bg
= { .r
= 0xe2, .g
= 0xe2, .b
= 0xe2, .a
= 0xff };
107 /* The normal vertex color */
108 static SDL_Color color_v
= { .r
= 0x82, .g
= 0x82, .b
= 0xb2, .a
= 0xff };
110 /* The vertex color for preview vertices */
111 static SDL_Color color_v_preview
= { .r
= 0x82, .g
= 0x82, .b
= 0xb2, .a
=
114 /* The normal vertex outline color */
115 static SDL_Color color_outline
= { .r
= 0x12, .g
= 0x12, .b
= 0x12, .a
= 0x80 };
117 /* The vertex outline color for preview vertices */
118 static SDL_Color color_outline_preview
= { .r
= 0x12, .g
= 0x12, .b
= 0x12, .a
=
121 /* The selected vertex outline color */
122 static SDL_Color color_outline_sel
= { .r
= 0x42, .g
= 0x42, .b
= 0xe2, .a
=
126 static SDL_Color color_font
= { .r
= 0x12, .g
= 0x12, .b
= 0x12, .a
= 0x80 };
128 /* The normal edge color */
129 static SDL_Color color_e_normal
= { .r
= 0x12, .g
= 0x12, .b
= 0x12, .a
=
132 /* The half edge color */
133 static SDL_Color color_e_half
= { .r
= 0x12, .g
= 0x12, .b
= 0x12, .a
= 0x60 };
135 /* The abnormal edge color */
136 static SDL_Color color_e_abnormal
= { .r
= 0xd0, .g
= 0x12, .b
= 0x12, .a
=
139 /* The selected edge color */
140 static SDL_Color color_e_sel
= { .r
= 0xb2, .g
= 0xb2, .b
= 0xe2, .a
= 0x40 };
142 /* The font for node names, instructions, etc */
143 static TTF_Font
*normal_font
;
145 /* SDL wants a SDL_RWops pointer to deal with in-memory font */
146 static SDL_RWops
*normal_font_rw
;
148 /* The base font size */
149 static uint_fast8_t base_font_size
= 12;
151 /* How much space (pixels) between lower left of screen and bottom text */
152 static unsigned int text_border_padding
= 24;
154 /* Strings to display at bottom of screen */
155 static const char *bottom_string
[] = {
157 [UIA_NONE
] = "[m] Mutate\n[v] Create new vertex\n"
158 "[d] Delete vertex/edge\n[e] Add edge\n"
159 "[h] Add half edge\n[f] Increase vertex fatness\n"
160 "[g] Decrease vertex fatness\n[r] Rename vertex\n"
161 "[c] Change vertex color\n[Shift+key] as [key] "
162 "above, but loop\n[s] Save quiver\n[l] "
163 "Load quiver\n[q] Quit", /* */
164 [UIA_ASK_QUIT
] = "Quit?\n\n[y] Confirm\n[n] Cancel", /* */
165 [UIA_DEC_FATNESS
] = "[Mouse1] Decrease fatness\n[ESC] Cancel", /* */
166 [UIA_DELETE
] = "[Mouse1] Delete vertex/edge\n[ESC] Cancel", /* */
167 [UIA_ENTER_LOAD
] = "[Enter] Load from\n[ESC] Cancel", /* */
168 [UIA_ENTER_RENAME
] = "[Enter] Rename\n[ESC] Cancel", /* */
169 [UIA_ENTER_SAVE
] = "[Enter] Save to\n[ESC] Cancel", /* */
170 [UIA_ENTER_COLOR
] = "[Enter] Use color (rrggbb)\n[ESC] Cancel", /* */
171 [UIA_INC_FATNESS
] = "[Mouse1] Increase fatness\n[ESC] Cancel", /* */
172 [UIA_CHANGE_COLOR
] = "[Mouse1] Change color\n[ESC] Cancel", /* */
173 [UIA_MUTATE
] = "[Mouse1] Mutate at vertex\n[ESC] Cancel", /* */
174 [UIA_NEW_EDGE_1
] = "[Mouse1] Select start\n[ESC] Cancel", /* */
175 [UIA_NEW_EDGE_2
] = "[Mouse1] Select end\n[ESC] Cancel", /* */
176 [UIA_NEW_H_EDGE_1
] = "[Mouse1] Select start\n[ESC] Cancel", /* */
177 [UIA_NEW_H_EDGE_2
] = "[Mouse1] Select end\n[ESC] Cancel", /* */
178 [UIA_NEW_VERTEX
] = "[Mouse1] Create new vertex\n[ESC] Cancel", /* */
179 [UIA_RENAME
] = "[Mouse1] Rename vertex\n[ESC] Cancel", /* */
183 /* The texture containing what to show on the bottom */
184 static SDL_Texture
*bottom_text
[UIA_LEN
];
186 /* The textures containing the names of all vertices - indexed just like q */
187 static SDL_Texture
**vertex_names
;
189 /* The number of elements of vertex_names */
190 static size_t vertex_names_len
;
192 /* Drawing offset x */
195 /* Drawing offset x */
198 /* How wide (in pixels) a fatness 1 node should be */
199 static unsigned int base_node_radius
= 8;
201 /* How much (in pixels) a node should widen for each fatness level */
202 static unsigned int node_radius_per_fatness
= 7;
204 /* How wide (in pixels) the outline should be */
205 static int outline_width
= 2;
207 /* How wide (in pixels) the arrowheads should be */
208 static int arrow_length
= 7;
210 /* How narrow the arrowheads should be */
211 static double arrow_angle
= 6.5 * M_PI
/ 8.0;
213 /* How close to an arrow (in pixels) for it to be selected */
214 static int edge_margin
= 5;
216 /* If we're interacting with a vertex, which one */
217 static size_t selected_vertex
= (size_t) -1;
219 /* If we're interacting with an edge, the i */
220 static size_t selected_edge_i
= (size_t) -1;
222 /* If we're interacting with an edge, the j */
223 static size_t selected_edge_j
= (size_t) -1;
225 /* If we're adding an edge, the last vertex we clicked on */
226 static size_t last_clicked_vertex
= (size_t) -1;
228 /* If we're adding a new vertex, the number to use */
229 static uint16_t next_v_num
= 1;
231 /* Memory for next vertex name */
232 static char next_v_name
[32] = { 'v', '1', 0 };
234 /* x-coordinate of last mouse position */
235 static int last_mouse_x
= -1;
237 /* y-coordinate of last mouse position */
238 static int last_mouse_y
= -1;
241 static char window_title
[2048];
243 /* What name we last loaded/saved */
244 static char current_filename
[2048] = { 'U', 'n', 't', 'i', 't', 'l', 'e', 'd',
247 /* Whether we've edited since last loading/saving */
248 static uint_fast8_t current_file_edited
= 0;
250 /* Maximum length of an input string we'll accept */
251 static size_t max_input_size
= 1 << 10;
253 /* Current position in input */
254 static size_t input_idx
;
256 /* Current length of string held in input */
257 static size_t input_len
;
259 /* If a string is being typed in, what it is (in UTF-8 as per SDL2) */
262 /* Input, with `Filename:' or `New name:' prepended (for rendering) */
263 static char *input_with_prefix
;
265 /* Texture for what user is currently entering */
266 static SDL_Texture
*input_texture
;
268 /* Whether we need to re-render the input */
269 static uint_fast8_t rerender_input_string
;
271 /* To prevent awkward repeats in SDL events, rate-limit `s' and `l' */
272 static uint_fast8_t save_load_delay
;
274 /* If something needs to be freed next frame, but not before */
275 static void *free_this
;
279 gcd(uint_fast8_t x
, uint_fast8_t y
)
299 decode_hex(char *s
, uint32_t *out_argb
)
309 if (strlen(s
) != 6) {
319 } else if ('a' <= *s
&&
321 z
|= (*s
- 'a' + 10);
322 } else if ('A' <= *s
&&
324 z
|= (*s
- 'A' + 10);
337 /* Allocate and print a rational in the way a user expects */
339 pretty_fraction(struct rational
*r
, char **out
)
344 size_t len
= snprintf(0, 0, "%d", (int) r
->p
);
347 ret
= errno
= EOVERFLOW
;
352 if (!(*out
= malloc(len
+ 1))) {
358 sprintf(*out
, "%d", (int) r
->p
);
362 size_t len
= snprintf(0, 0, "%d/%u", (int) r
->p
, (unsigned int) r
->q
);
365 ret
= errno
= EOVERFLOW
;
370 if (!(*out
= malloc(len
+ 1))) {
376 sprintf(*out
, "%d/%u", (int) r
->p
, (unsigned int) r
->q
);
382 /* Render text to texture */
384 render_text(const char *text
, SDL_Texture
**out
)
391 SDL_DestroyTexture(*out
);
395 SDL_Surface
*surf
= 0;
397 if (!(surf
= TTF_RenderUTF8_Blended_Wrapped(normal_font
, text
,
399 fprintf(stderr
, L("TTF_RenderUTF8_Shaded(): %s\n"),
405 if (!(*out
= SDL_CreateTextureFromSurface(sdl_renderer
, surf
))) {
406 fprintf(stderr
, L("SDL_CreateTextureFromSurface(): %s\n"),
413 SDL_FreeSurface(surf
);
425 TTF_CloseFont(normal_font
);
429 if (normal_font_rw
) {
430 SDL_RWclose(normal_font_rw
);
434 if (!(normal_font_rw
= SDL_RWFromConstMem(ttf_memory
,
435 sizeof_ttf_memory()))) {
437 fprintf(stderr
, L("SDL_RWFromConstMem(): %s\n"),
442 if (!(normal_font
= TTF_OpenFontIndexRW(normal_font_rw
, 0,
443 base_font_size
, 0))) {
445 fprintf(stderr
, L("TTF_OpenFont(): %s\n"), TTF_GetError());
449 for (size_t j
= 0; j
< UIA_LEN
; ++j
) {
450 if ((ret
= render_text(bottom_string
[j
], &bottom_text
[j
]))) {
459 TTF_CloseFont(normal_font
);
462 if (normal_font_rw
) {
463 SDL_RWclose(normal_font_rw
);
473 /* Insert str into input at given position */
475 text_input(const char *str
)
477 size_t l
= strlen(str
);
479 if (l
+ input_len
+ 1 >= max_input_size
) {
483 for (size_t k
= input_len
; k
> input_idx
; --k
) {
484 input
[k
+ l
] = input
[k
];
487 /* Special-case for input_idx: 0 prohibits `>=' above */
488 input
[input_idx
+ l
] = input
[input_idx
];
490 for (size_t k
= 0; k
< l
; ++k
) {
491 input
[input_idx
+ k
] = str
[k
];
496 rerender_input_string
= 1;
499 /* Move input cursor left */
501 text_input_left(void)
507 while (input_idx
> 0) {
508 unsigned char c
= input
[input_idx
];
510 if ((c
& 0xc0) == 0x80) {
518 /* Move input cursor right */
520 text_input_right(void)
523 unsigned char c
= input
[input_idx
];
525 if ((c
& 0x80) == 0x0) {
527 } else if ((c
& 0xe0) == 0xc0) {
529 } else if ((c
& 0xf0) == 0xe0) {
531 } else if ((c
& 0xf8) == 0xf0) {
535 if (input_idx
+ adv
<= input_len
) {
550 while (input_idx
> bs
) {
551 unsigned char c
= input
[input_idx
- bs
];
553 if ((c
& 0xc0) == 0x80) {
560 for (size_t k
= input_idx
- bs
; k
<= input_len
- bs
; k
++) {
561 input
[k
] = input
[k
+ bs
];
566 rerender_input_string
= 1;
569 /* Set the title to something like `clav-sdl - foobar.txt*' */
571 update_window_title(void)
573 size_t cutoff
= sizeof (window_title
) / sizeof (window_title
[0]);
574 size_t r
= snprintf(window_title
, cutoff
, "clav-sdl - %s%s",
575 current_filename
, (current_file_edited
? "*" : ""));
578 window_title
[cutoff
- 1] = '\0';
581 SDL_SetWindowTitle(sdl_win
, window_title
);
584 /* Make sure the window title displays as edited */
588 if (current_file_edited
) {
592 current_file_edited
= 1;
593 update_window_title();
596 /* Convert `internal coordinates' to pixel coordinates */
598 internal_to_pixel_xy(int in_x
, int in_y
, int *out_x
, int *out_y
)
600 *out_x
= in_x
+ offset_x
;
601 *out_y
= in_y
+ offset_y
;
604 /* Convert pixel coordinates to `internal coordinates' */
606 pixel_to_internal_xy(int in_x
, int in_y
, int *out_x
, int *out_y
)
608 *out_x
= in_x
- offset_x
;
609 *out_y
= in_y
- offset_y
;
612 /* Set selected_vertex and selected_edge_{i,j} */
614 recalculate_selected_items(int mx
, int my
)
623 pixel_to_internal_xy(mx
, my
, &x
, &y
);
624 size_t last_vertex
= selected_vertex
;
625 size_t last_edge_i
= selected_edge_i
;
626 size_t last_edge_j
= selected_edge_j
;
628 selected_vertex
= (size_t) -1;
629 selected_edge_i
= (size_t) -1;
630 selected_edge_j
= (size_t) -1;
632 if (last_vertex
!= (size_t) -1) {
633 struct vertex
*v
= &(q
->v
[last_vertex
]);
634 int r
= base_node_radius
+ v
->fatness
* node_radius_per_fatness
;
640 selected_vertex
= last_vertex
;
645 for (size_t j
= q
->v_num
; j
> 0; --j
) {
646 struct vertex
*v
= &(q
->v
[j
- 1]);
647 int r
= base_node_radius
+ v
->fatness
* node_radius_per_fatness
;
653 selected_vertex
= j
- 1;
658 for (size_t j
= 1; j
< q
->v_num
; ++j
) {
659 struct vertex
*v1
= &(q
->v
[j
]);
661 for (size_t i
= 0; i
< j
; ++i
) {
662 if (!q
->e
[i
* q
->v_len
+ j
].p
&&
663 !q
->e
[j
* q
->v_len
+ i
].p
) {
667 struct vertex
*v2
= &(q
->v
[i
]);
669 if ((x
- edge_margin
> v1
->x
&&
670 x
- edge_margin
> v2
->x
) ||
671 (x
+ edge_margin
< v1
->x
&&
672 x
+ edge_margin
< v2
->x
) ||
673 (y
- edge_margin
> v1
->y
&&
674 y
- edge_margin
> v2
->y
) ||
675 (y
+ edge_margin
< v1
->y
&&
676 y
+ edge_margin
< v2
->y
)) {
680 if (v1
->x
== v2
->x
) {
681 if (x
+ edge_margin
> v1
->x
&&
682 x
- edge_margin
< v1
->x
) {
687 } else if (v1
->y
== v2
->y
) {
688 if (y
+ edge_margin
> v1
->y
&&
689 y
- edge_margin
< v1
->y
) {
695 double m1
= ((double) (v2
->y
- v1
->y
)) /
696 ((double) (v2
->x
- v1
->x
));
697 double m2
= -1.0 / m1
;
698 double xint
= ((double) y
- (double) v1
->y
+
699 m1
* v1
->x
- m2
* x
) / (m1
- m2
);
700 double yint
= m1
* xint
- m1
* v1
->x
+
703 if ((x
- xint
) * (x
- xint
) + (y
- yint
) * (y
-
705 < edge_margin
* edge_margin
) {
716 if (selected_vertex
!= (size_t) -1 &&
717 (selected_vertex
!= last_vertex
||
718 dragged_selected_vertex
)) {
719 dragged_selected_vertex
= 0;
720 struct vertex
*v
= &(q
->v
[selected_vertex
]);
721 size_t len
= snprintf(0, 0,
722 "Name: %s\nFatness: %d\nPosition: (%d,%d)",
724 (int) v
->fatness
, v
->x
, v
->y
);
727 ret
= errno
= EOVERFLOW
;
732 if (!(s
= malloc(len
+ 1))) {
738 sprintf(s
, "Name: %s\nFatness: %d\nPosition: (%d,%d)",
739 v
->name
, (int) v
->fatness
, v
->x
, v
->y
);
741 if ((ret
= render_text(s
, &selected_info
))) {
744 } else if ((selected_edge_i
!= last_edge_i
||
745 selected_edge_j
!= last_edge_j
) &&
746 selected_edge_i
!= (size_t) -1 &&
747 selected_edge_j
!= (size_t) -1) {
748 struct vertex
*i
= &(q
->v
[selected_edge_i
]);
749 struct vertex
*j
= &(q
->v
[selected_edge_j
]);
750 struct rational
*eij
= &(q
->e
[selected_edge_i
* q
->v_len
+
752 struct rational
*eji
= &(q
->e
[selected_edge_j
* q
->v_len
+
755 if ((ret
= pretty_fraction(eij
, &sij
))) {
759 if ((ret
= pretty_fraction(eji
, &sji
))) {
763 size_t len
= snprintf(0, 0,
764 "%s \u2192 %s: %s\n%s \u2192 %s: %s",
765 i
->name
, j
->name
, sij
,
766 j
->name
, i
->name
, sji
);
769 ret
= errno
= EOVERFLOW
;
774 if (!(s
= malloc(len
+ 1))) {
780 sprintf(s
, "%s \u2192 %s: %s\n%s \u2192 %s: %s", i
->name
,
781 j
->name
, sij
, j
->name
, i
->name
, sji
);
783 if ((ret
= render_text(s
, &selected_info
))) {
796 /* Render vertex names as textures */
798 render_vertex_names(void)
801 for (size_t j
= 0; j
< vertex_names_len
; ++j
) {
802 SDL_DestroyTexture(vertex_names
[j
]);
811 if ((q
->v_num
* sizeof *vertex_names
) / q
->v_num
!=
812 sizeof *vertex_names
) {
815 vertex_names_len
= 0;
820 if (!(vertex_names
= realloc(vertex_names
, q
->v_num
*
821 sizeof(*vertex_names
)))) {
824 perror(L("realloc"));
825 vertex_names_len
= 0;
830 vertex_names_len
= q
->v_num
;
832 for (size_t j
= 0; j
< vertex_names_len
; ++j
) {
836 for (size_t j
= 0; j
< vertex_names_len
; ++j
) {
839 if ((ret
= render_text(q
->v
[j
].name
, &(vertex_names
[j
])))) {
847 /* Get information about the window */
849 react_to_window_resized(void)
851 int old_pix_width
= da_pix_width
;
852 int old_pix_height
= da_pix_height
;
854 SDL_GetWindowSize(sdl_win
, &da_pix_width
, &da_pix_height
);
856 if (old_pix_width
== da_pix_width
&&
857 old_pix_height
== da_pix_height
) {
861 offset_x
+= (da_pix_width
- old_pix_width
) / 2;
862 offset_y
+= (da_pix_height
- old_pix_height
) / 2;
867 eq_pop(struct ui_event
*out
)
869 if (eq_head
== eq_tail
) {
870 *out
= (struct ui_event
) { 0 };
875 memcpy(out
, eq_buf
+ eq_head
, sizeof *out
);
876 eq_buf
[eq_head
] = (struct ui_event
) { 0 };
877 eq_head
= (eq_head
+ 1) % eq_len
;
880 /* Push into queue */
882 eq_push(struct ui_event
*in
)
884 if (((eq_tail
+ 1) % eq_len
) == eq_head
) {
887 if ((eq_len
* sizeof *in
) >= ((size_t) -1) / 2) {
889 "eq_push: Impossibly large buffer\n"));
894 if ((eq_len
* 2) / 2 != eq_len
||
895 (eq_len
* 2 * sizeof *eq_buf
) / (eq_len
* 2) !=
903 if (!(newmem
= realloc(eq_buf
, (eq_len
* 2) *
907 perror(L("realloc"));
912 eq_buf
= (struct ui_event
*) newmem
;
916 memcpy(eq_buf
+ eq_tail
, in
, sizeof *in
);
917 eq_tail
= (eq_tail
+ 1) % eq_len
;
924 ui_init(struct quiver
*i_q
)
929 size_t padding
= strlen("Filename: ");
931 /* No need for overflow check, max_input_size is capped */
932 if (!(input_with_prefix
= malloc(max_input_size
+ padding
))) {
939 strcpy(input_with_prefix
, "Filename: ");
940 input
= input_with_prefix
+ padding
;
944 if (SDL_Init(SDL_INIT_VIDEO
) < 0) {
945 fprintf(stderr
, L("SDL_Init(): %s\n"), SDL_GetError());
950 sdl_win
= SDL_CreateWindow("clav-sdl", SDL_WINDOWPOS_UNDEFINED
,
951 SDL_WINDOWPOS_UNDEFINED
, 1000, 1000,
953 SDL_WINDOW_RESIZABLE
);
956 fprintf(stderr
, L("SDL_CreateWindow(): %s\n"), SDL_GetError());
961 sdl_renderer
= SDL_CreateRenderer(sdl_win
, -1,
962 SDL_RENDERER_ACCELERATED
);
965 fprintf(stderr
, L("SDL_CreateRenderer(): %s\n"),
971 if (TTF_Init() < 0) {
972 fprintf(stderr
, L("TTF_Init(): %s\n"), TTF_GetError());
977 if ((ret
= load_fonts())) {
981 if ((ret
= render_vertex_names())) {
985 if ((ret
= SDL_SetRenderDrawColor(sdl_renderer
, color_bg
.r
, color_bg
.g
,
986 color_bg
.b
, color_bg
.a
))) {
987 fprintf(stderr
, L("SDL_SetRenderDrawColor(): %s\n"),
992 if ((ret
= SDL_RenderClear(sdl_renderer
))) {
993 fprintf(stderr
, L("SDL_RenderClear(): %s\n"), SDL_GetError());
997 update_window_title();
998 SDL_RenderPresent(sdl_renderer
);
999 react_to_window_resized();
1001 /* Set up queue for returning data */
1002 if (!(eq_buf
= calloc(2, sizeof *eq_buf
))) {
1004 perror(L("calloc"));
1016 /* Deal with the fact that the quiver was changed */
1018 ui_respond_quiver_change(void)
1020 return render_vertex_names();
1023 /* Acknowledge a successful load */
1025 ui_respond_successful_load(const char *filename
)
1027 const char *tail
= (const char *) strrchr(filename
, '/');
1035 size_t cutoff
= sizeof(current_filename
) / sizeof(current_filename
[0]);
1036 size_t r
= snprintf(current_filename
, cutoff
, "%s", tail
);
1039 current_filename
[cutoff
- 1] = '\0';
1042 current_file_edited
= 0;
1044 update_window_title();
1049 /* Acknowledge a successful save */
1051 ui_respond_successful_save(const char *filename
)
1053 return ui_respond_successful_load(filename
);
1061 for (size_t j
= 0; j
< vertex_names_len
; ++j
) {
1062 SDL_DestroyTexture(vertex_names
[j
]);
1063 vertex_names
[j
] = 0;
1070 for (size_t j
= 0; j
< UIA_LEN
; ++j
) {
1071 SDL_DestroyTexture(bottom_text
[j
]);
1075 if (selected_info
) {
1076 SDL_DestroyTexture(selected_info
);
1080 if (input_texture
) {
1081 SDL_DestroyTexture(input_texture
);
1086 TTF_CloseFont(normal_font
);
1090 if (normal_font_rw
) {
1091 SDL_RWclose(normal_font_rw
);
1096 SDL_DestroyWindow(sdl_win
);
1102 free(input_with_prefix
);
1103 input_with_prefix
= 0;
1110 /* Record that a frame has been started */
1112 ui_start_frame(void)
1117 Uint32 dummy_format
;
1122 frame_start_ticks
= SDL_GetTicks();
1130 /* Draw the damn thing */
1131 SDL_SetRenderDrawColor(sdl_renderer
, color_bg
.r
, color_bg
.g
, color_bg
.b
,
1133 SDL_RenderClear(sdl_renderer
);
1135 /* Special case for if text input is going on */
1136 if (rerender_input_string
) {
1137 rerender_input_string
= 0;
1139 if ((ret
= render_text(input_with_prefix
, &input_texture
))) {
1144 /* Draw each edge */
1145 for (size_t j
= 0; j
< q
->v_num
; ++j
) {
1146 for (size_t i
= 0; i
< j
; ++i
) {
1147 /* First, determine if we're looking at a half-edge or a full-edge */
1148 int d
= gcd(q
->v
[i
].fatness
, q
->v
[j
].fatness
);
1149 struct rational
*eij
= &(q
->e
[i
* q
->v_len
+ j
]);
1150 struct rational
*eji
= &(q
->e
[j
* q
->v_len
+ i
]);
1162 internal_to_pixel_xy(q
->v
[i
].x
, q
->v
[i
].y
, &cx
, &cy
);
1163 internal_to_pixel_xy(q
->v
[j
].x
, q
->v
[j
].y
, &cx2
, &cy2
);
1165 if (selected_edge_i
== i
&&
1166 selected_edge_j
== j
) {
1167 if ((ret
= SDL_SetRenderDrawColor(sdl_renderer
,
1174 "SDL_RenderDrawColor(): %s\n"),
1179 for (int id
= -edge_margin
; id
< edge_margin
;
1181 for (int jd
= -edge_margin
; jd
<
1182 edge_margin
; ++jd
) {
1183 if ((ret
= SDL_RenderDrawLine(
1189 "SDL_RenderDrawLine(): %s\n"),
1197 /* This is the (eij)/dj = -(eji)/di condition */
1198 if (eij
->p
* q
->v
[i
].fatness
* eji
->q
!= -eji
->p
*
1199 q
->v
[j
].fatness
* eij
->q
) {
1200 ret
= SDL_SetRenderDrawColor(sdl_renderer
,
1204 color_e_abnormal
.a
);
1205 } else if ((int) (abs(eij
->p
) * d
) ==
1206 (int) (q
->v
[j
].fatness
* eij
->q
)) {
1207 ret
= SDL_SetRenderDrawColor(sdl_renderer
,
1212 } else if ((int) (2 * abs(eij
->p
) * d
) ==
1213 (int) (q
->v
[j
].fatness
* eij
->q
)) {
1214 ret
= SDL_SetRenderDrawColor(sdl_renderer
,
1220 ret
= SDL_SetRenderDrawColor(sdl_renderer
,
1224 color_e_abnormal
.a
);
1229 "SDL_SetRenderDrawColor(): %s\n"),
1234 if ((ret
= SDL_RenderDrawLine(sdl_renderer
, cx
, cy
, cx2
,
1236 fprintf(stderr
, L("SDL_RenderDrawLine(): %s\n"),
1242 theta
= (cy2
> cy
) ? M_PI
/ 2.0 : -M_PI
/ 2.0;
1244 theta
= atan2f(cy2
- cy
, cx2
- cx
);
1251 cx
= (cx
+ cx2
) / 2;
1252 cy
= (cy
+ cy2
) / 2;
1253 cx2
= cx
+ arrow_length
* cos(theta
+ arrow_angle
);
1254 cy2
= cy
+ arrow_length
* sin(theta
+ arrow_angle
);
1256 if ((ret
= SDL_RenderDrawLine(sdl_renderer
, cx
, cy
, cx2
,
1258 fprintf(stderr
, L("SDL_RenderDrawLine(): %s\n"),
1263 if ((ret
= SDL_RenderDrawLine(sdl_renderer
, cx
, cy
, cx
+
1264 arrow_length
* cos(theta
-
1267 arrow_length
* sin(theta
-
1270 fprintf(stderr
, L("SDL_RenderDrawLine(): %s\n"),
1277 /* Draw each vertex as a box */
1278 for (size_t j
= 0; j
< q
->v_num
; ++j
) {
1279 struct vertex
*v
= &(q
->v
[j
]);
1283 internal_to_pixel_xy(v
->x
, v
->y
, &cx
, &cy
);
1285 /* Central square */
1286 SDL_SetRenderDrawBlendMode(sdl_renderer
, SDL_BLENDMODE_NONE
);
1287 SDL_SetRenderDrawColor(sdl_renderer
, v
->r
, v
->g
, v
->b
, 0xff);
1288 rho
= base_node_radius
+ node_radius_per_fatness
* v
->fatness
;
1293 SDL_RenderFillRect(sdl_renderer
, &r
);
1296 SDL_SetRenderDrawBlendMode(sdl_renderer
, SDL_BLENDMODE_BLEND
);
1298 if (j
== selected_vertex
||
1299 j
== last_clicked_vertex
) {
1300 SDL_SetRenderDrawColor(sdl_renderer
,
1301 color_outline_sel
.r
,
1302 color_outline_sel
.g
,
1303 color_outline_sel
.b
,
1304 color_outline_sel
.a
);
1306 SDL_SetRenderDrawColor(sdl_renderer
, color_outline
.r
,
1307 color_outline
.g
, color_outline
.b
,
1313 r
.w
= 2 * rho
- outline_width
;
1314 r
.h
= outline_width
;
1315 SDL_RenderFillRect(sdl_renderer
, &r
);
1316 r
.x
= cx
+ rho
- outline_width
;
1318 r
.w
= outline_width
;
1319 r
.h
= 2 * rho
- outline_width
;
1320 SDL_RenderFillRect(sdl_renderer
, &r
);
1321 r
.x
= cx
- rho
+ outline_width
;
1322 r
.y
= cy
+ rho
- outline_width
;
1323 r
.w
= 2 * rho
- outline_width
;
1324 r
.h
= outline_width
;
1325 SDL_RenderFillRect(sdl_renderer
, &r
);
1327 r
.y
= cy
- rho
+ outline_width
;
1328 r
.w
= outline_width
;
1329 r
.h
= 2 * rho
- outline_width
;
1330 SDL_RenderFillRect(sdl_renderer
, &r
);
1333 if (j
>= vertex_names_len
) {
1335 "render_vertex_names() was not called, somehow\n"));
1340 if (SDL_QueryTexture(vertex_names
[j
], &dummy_format
,
1341 &dummy_access
, &tex_w
, &tex_h
)) {
1342 fprintf(stderr
, L("SDL_QueryTexture(): %s\n"),
1348 r
.x
= cx
- tex_w
/ 2;
1349 r
.y
= cy
- tex_h
/ 2;
1352 SDL_RenderCopy(sdl_renderer
, vertex_names
[j
], 0, &r
);
1355 /* If adding a new vertex, draw preview */
1356 if (ui_action
== UIA_NEW_VERTEX
&&
1357 last_mouse_x
!= -1 &&
1358 last_mouse_y
!= -1) {
1359 /* Central square */
1360 SDL_SetRenderDrawBlendMode(sdl_renderer
, SDL_BLENDMODE_NONE
);
1361 SDL_SetRenderDrawColor(sdl_renderer
, color_v_preview
.r
,
1362 color_v_preview
.g
, color_v_preview
.b
,
1364 rho
= base_node_radius
;
1365 r
.x
= last_mouse_x
- rho
;
1366 r
.y
= last_mouse_y
- rho
;
1369 SDL_RenderFillRect(sdl_renderer
, &r
);
1372 SDL_SetRenderDrawBlendMode(sdl_renderer
, SDL_BLENDMODE_BLEND
);
1373 SDL_SetRenderDrawColor(sdl_renderer
, color_outline_preview
.r
,
1374 color_outline_preview
.g
,
1375 color_outline_preview
.b
,
1376 color_outline_preview
.a
);
1377 r
.x
= last_mouse_x
- rho
;
1378 r
.y
= last_mouse_y
- rho
;
1379 r
.w
= 2 * rho
- outline_width
;
1380 r
.h
= outline_width
;
1381 SDL_RenderFillRect(sdl_renderer
, &r
);
1382 r
.x
= last_mouse_x
+ rho
- outline_width
;
1383 r
.y
= last_mouse_y
- rho
;
1384 r
.w
= outline_width
;
1385 r
.h
= 2 * rho
- outline_width
;
1386 SDL_RenderFillRect(sdl_renderer
, &r
);
1387 r
.x
= last_mouse_x
- rho
+ outline_width
;
1388 r
.y
= last_mouse_y
+ rho
- outline_width
;
1389 r
.w
= 2 * rho
- outline_width
;
1390 r
.h
= outline_width
;
1391 SDL_RenderFillRect(sdl_renderer
, &r
);
1392 r
.x
= last_mouse_x
- rho
;
1393 r
.y
= last_mouse_y
- rho
+ outline_width
;
1394 r
.w
= outline_width
;
1395 r
.h
= 2 * rho
- outline_width
;
1396 SDL_RenderFillRect(sdl_renderer
, &r
);
1399 /* If adding a new edge, draw possible */
1400 if ((ui_action
== UIA_NEW_EDGE_2
||
1401 ui_action
== UIA_NEW_H_EDGE_2
) &&
1403 /* last_clicked_vertex != (size_t) -1 && */
1404 last_mouse_x
!= -1 &&
1405 last_mouse_y
!= -1) {
1409 if ((ret
= SDL_SetRenderDrawColor(sdl_renderer
,
1413 color_e_normal
.a
))) {
1414 fprintf(stderr
, L("SDL_RenderDrawLine(): %s\n"),
1419 internal_to_pixel_xy(q
->v
[last_clicked_vertex
].x
,
1420 q
->v
[last_clicked_vertex
].y
, &cx
, &cy
);
1422 if ((ret
= SDL_RenderDrawLine(sdl_renderer
, cx
, cy
,
1423 last_mouse_x
, last_mouse_y
))) {
1424 fprintf(stderr
, L("SDL_RenderDrawLine(): %s\n"),
1431 if (SDL_QueryTexture(bottom_text
[ui_action
], &dummy_format
,
1432 &dummy_access
, &tex_w
, &tex_h
)) {
1433 fprintf(stderr
, L("SDL_QueryTexture(): %s\n"), SDL_GetError());
1438 r
.x
= text_border_padding
;
1439 r
.y
= da_pix_height
- tex_h
- text_border_padding
;
1442 SDL_RenderCopy(sdl_renderer
, bottom_text
[ui_action
], 0, &r
);
1444 /* If something is selected */
1445 if (selected_info
&&
1446 (selected_vertex
!= (size_t) -1 ||
1447 (selected_edge_i
!= (size_t) -1 &&
1448 selected_edge_j
!= (size_t) -1))) {
1449 if (SDL_QueryTexture(selected_info
, &dummy_format
,
1450 &dummy_access
, &tex_w
, &tex_h
)) {
1451 fprintf(stderr
, L("SDL_QueryTexture(): %s\n"),
1457 r
.x
= text_border_padding
;
1458 r
.y
= text_border_padding
;
1461 SDL_RenderCopy(sdl_renderer
, selected_info
, 0, &r
);
1464 /* If user is entering text */
1465 if (ui_action
== UIA_ENTER_SAVE
||
1466 ui_action
== UIA_ENTER_LOAD
||
1467 ui_action
== UIA_ENTER_RENAME
||
1468 ui_action
== UIA_ENTER_COLOR
) {
1469 if (SDL_QueryTexture(bottom_text
[ui_action
], &dummy_format
,
1470 &dummy_access
, &tex_w
, &tex_h
)) {
1471 fprintf(stderr
, L("SDL_QueryTexture(): %s\n"),
1477 r
.x
= text_border_padding
;
1478 r
.y
= da_pix_height
- tex_h
- text_border_padding
;
1480 if (SDL_QueryTexture(input_texture
, &dummy_format
,
1481 &dummy_access
, &tex_w
, &tex_h
)) {
1482 fprintf(stderr
, L("SDL_QueryTexture(): %s\n"),
1488 r
.y
-= (tex_h
+ text_border_padding
);
1491 SDL_RenderCopy(sdl_renderer
, input_texture
, 0, &r
);
1493 /* Now draw a cursor */
1494 char store_idxchar
= input
[input_idx
];
1496 input
[input_idx
] = '\0';
1498 if ((ret
= TTF_SizeUTF8(normal_font
, input_with_prefix
, &tex_w
,
1500 fprintf(stderr
, L("TTF_SizeText(): %s\n"),
1505 input
[input_idx
] = store_idxchar
;
1507 if ((ret
= SDL_SetRenderDrawColor(sdl_renderer
, color_font
.r
,
1508 color_font
.g
, color_font
.b
,
1510 fprintf(stderr
, L("SDL_SetRenderDrawColor(): %s\n"),
1515 if ((ret
= SDL_RenderDrawLine(sdl_renderer
, r
.x
+ tex_w
, r
.y
,
1516 r
.x
+ tex_w
, r
.y
+ tex_h
))) {
1517 fprintf(stderr
, L("SDL_RenderDrawLine(): %s\n"),
1528 /* Draw a frame, possibly sleeping for framelimit */
1530 ui_finish_frame(void)
1533 struct ui_event ui_e
= { 0 };
1534 SDL_Event sdl_e
= { 0 };
1536 uint_fast8_t save_requested
= 0;
1537 uint_fast8_t load_requested
= 0;
1539 uint_fast8_t color_affirmed
= 0;
1546 if (save_load_delay
) {
1550 SDL_RenderPresent(sdl_renderer
);
1552 /* Handle user input */
1553 while (SDL_PollEvent(&sdl_e
) != 0) {
1555 * Redraw should be at least 2, to deal with
1556 * double-buffering. Triple-buffering is occasionally
1557 * used, let's go with 4 to be safe.
1563 switch (sdl_e
.type
) {
1565 ui_e
= (struct ui_event
) { .type
= ET_QUIT
};
1566 ret
= eq_push(&ui_e
);
1569 text_input(sdl_e
.text
.text
);
1573 if (sdl_e
.key
.repeat
) {
1577 k
= sdl_e
.key
.keysym
.sym
;
1578 m
= sdl_e
.key
.keysym
.mod
;
1580 switch (ui_action
) {
1581 case UIA_ENTER_SAVE
:
1582 case UIA_ENTER_LOAD
:
1583 case UIA_ENTER_RENAME
:
1584 case UIA_ENTER_COLOR
:
1586 if (k
== SDLK_ESCAPE
) {
1587 SDL_StopTextInput();
1588 ui_action
= UIA_NONE
;
1589 last_clicked_vertex
= (size_t) -1;
1590 } else if (k
== SDLK_RETURN
||
1591 k
== SDLK_RETURN2
) {
1592 SDL_StopTextInput();
1593 ui_e
= (struct ui_event
) { .str
=
1596 if (ui_action
== UIA_ENTER_SAVE
) {
1597 ui_e
.type
= ET_SAVE
;
1598 } else if (ui_action
==
1600 ui_e
.type
= ET_LOAD
;
1601 } else if (ui_action
==
1603 ui_e
.type
= ET_RENAME
;
1605 last_clicked_vertex
;
1606 } else if (ui_action
==
1608 ui_e
.type
= ET_CHANGE_COLOR
;
1610 last_clicked_vertex
;
1613 if (decode_hex(input
,
1620 last_clicked_vertex
=
1626 ret
= eq_push(&ui_e
);
1628 if (ui_action_loop
) {
1631 ui_action
= UIA_RENAME
;
1632 } else if (ui_action
==
1638 ui_action
= UIA_NONE
;
1641 last_clicked_vertex
= (size_t) -1;
1642 } else if (k
== SDLK_LEFT
) {
1644 } else if (k
== SDLK_RIGHT
) {
1646 } else if (k
== SDLK_BACKSPACE
) {
1654 ui_action
= UIA_ASK_QUIT
;
1655 } else if (k
== SDLK_d
) {
1656 ui_action
= UIA_DELETE
;
1657 ui_action_loop
= (m
& KMOD_SHIFT
);
1658 } else if (k
== SDLK_e
) {
1659 ui_action
= UIA_NEW_EDGE_1
;
1660 ui_action_loop
= (m
& KMOD_SHIFT
);
1661 } else if (k
== SDLK_f
) {
1662 ui_action
= UIA_INC_FATNESS
;
1663 ui_action_loop
= (m
& KMOD_SHIFT
);
1664 } else if (k
== SDLK_g
) {
1665 ui_action
= UIA_DEC_FATNESS
;
1666 ui_action_loop
= (m
& KMOD_SHIFT
);
1667 } else if (k
== SDLK_c
) {
1668 ui_action
= UIA_CHANGE_COLOR
;
1669 ui_action_loop
= (m
& KMOD_SHIFT
);
1670 } else if (k
== SDLK_h
) {
1671 ui_action
= UIA_NEW_H_EDGE_1
;
1672 ui_action_loop
= (m
& KMOD_SHIFT
);
1673 } else if (k
== SDLK_m
) {
1674 ui_action
= UIA_MUTATE
;
1675 ui_action_loop
= (m
& KMOD_SHIFT
);
1676 } else if (k
== SDLK_r
) {
1677 ui_action
= UIA_RENAME
;
1678 ui_action_loop
= (m
& KMOD_SHIFT
);
1679 } else if (k
== SDLK_v
) {
1680 ui_action
= UIA_NEW_VERTEX
;
1681 ui_action_loop
= (m
& KMOD_SHIFT
);
1682 } else if (k
== SDLK_l
&&
1684 /* Don't load - SDL_KEYDOWN repeats */
1686 } else if (k
== SDLK_s
&&
1696 ui_action
= UIA_NONE
;
1697 } else if (k
== SDLK_y
) {
1698 ui_e
= (struct ui_event
) { .type
=
1700 ret
= eq_push(&ui_e
);
1701 ui_action
= UIA_NONE
;
1709 ui_action
= UIA_NONE
;
1716 case SDL_WINDOWEVENT
:
1718 if (sdl_e
.window
.event
== SDL_WINDOWEVENT_RESIZED
||
1719 sdl_e
.window
.event
== SDL_WINDOWEVENT_MAXIMIZED
||
1720 sdl_e
.window
.event
== SDL_WINDOWEVENT_RESTORED
) {
1721 react_to_window_resized();
1722 } else if (sdl_e
.window
.event
==
1723 SDL_WINDOWEVENT_LEAVE
) {
1724 /* This tells the dragging code to not respond */
1730 case SDL_MOUSEMOTION
:
1732 if (sdl_e
.motion
.state
& SDL_BUTTON_LMASK
) {
1733 int x
= sdl_e
.motion
.x
;
1734 int y
= sdl_e
.motion
.y
;
1736 if (last_mouse_x
>= 0 &&
1737 last_mouse_y
>= 0) {
1738 if (selected_vertex
!= (size_t) -1) {
1739 q
->v
[selected_vertex
].x
+= (x
-
1741 q
->v
[selected_vertex
].y
+= (y
-
1743 dragged_selected_vertex
= 1;
1744 recalculate_selected_items(
1749 offset_x
+= (x
- last_mouse_x
);
1750 offset_y
+= (y
- last_mouse_y
);
1754 recalculate_selected_items(sdl_e
.motion
.x
,
1758 last_mouse_x
= sdl_e
.motion
.x
;
1759 last_mouse_y
= sdl_e
.motion
.y
;
1761 case SDL_MOUSEBUTTONUP
:
1763 if ((sdl_e
.button
.state
& SDL_BUTTON_LMASK
) &&
1764 ui_action
!= UIA_NEW_VERTEX
) {
1769 recalculate_selected_items(sdl_e
.button
.x
,
1772 case SDL_MOUSEBUTTONDOWN
:
1774 if (!(sdl_e
.button
.state
& SDL_BUTTON_LMASK
)) {
1778 if (ui_action
!= UIA_NEW_VERTEX
) {
1783 switch (ui_action
) {
1786 if (selected_vertex
== (size_t) -1) {
1790 ui_e
= (struct ui_event
) {
1792 .type
= ET_MUTATE
, .idx_1
=
1795 ret
= eq_push(&ui_e
);
1796 ui_action
= ui_action_loop
? UIA_MUTATE
:
1799 case UIA_CHANGE_COLOR
:
1801 if (selected_vertex
== (size_t) -1) {
1805 ui_e
= (struct ui_event
) {
1807 .type
= ET_CHANGE_COLOR
, .idx_1
=
1808 selected_vertex
, .z
= 0
1810 color_ret
= choose_color(&ui_e
.z
,
1814 if (color_affirmed
) {
1815 ret
= eq_push(&ui_e
);
1818 ui_action
= ui_action_loop
?
1819 UIA_CHANGE_COLOR
: UIA_NONE
;
1821 ui_action
= UIA_ENTER_COLOR
;
1822 last_clicked_vertex
= selected_vertex
;
1826 SDL_StartTextInput();
1828 /* The nul terminator bleeds over into input */
1829 strncpy(input_with_prefix
, " rrggbb: ",
1831 rerender_input_string
= 1;
1835 case UIA_NEW_VERTEX
:
1837 if (selected_vertex
!= (size_t) -1 ||
1838 selected_edge_i
!= (size_t) -1 ||
1839 selected_edge_j
!= (size_t) -1) {
1843 int cx
= sdl_e
.button
.x
- offset_x
;
1844 int cy
= sdl_e
.button
.y
- offset_y
;
1846 sprintf(next_v_name
, "v%zu",
1847 (size_t) next_v_num
);
1849 ui_e
= (struct ui_event
) {
1851 .type
= ET_NEW_VERTEX
, .int_1
= cx
,
1852 .int_2
= cy
, .z
= (color_v
.r
<< 16) |
1857 ret
= eq_push(&ui_e
);
1858 ui_action
= ui_action_loop
? UIA_NEW_VERTEX
:
1861 case UIA_NEW_EDGE_1
:
1862 case UIA_NEW_H_EDGE_1
:
1865 if (selected_vertex
== (size_t) -1) {
1866 if (!ui_action_loop
) {
1867 ui_action
= UIA_NONE
;
1873 last_clicked_vertex
= selected_vertex
;
1875 if (ui_action
== UIA_NEW_EDGE_1
) {
1876 ui_action
= UIA_NEW_EDGE_2
;
1877 } else if (ui_action
== UIA_NEW_H_EDGE_1
) {
1878 ui_action
= UIA_NEW_H_EDGE_2
;
1879 } else if (ui_action
== UIA_RENAME
) {
1880 ui_action
= UIA_ENTER_RENAME
;
1884 SDL_StartTextInput();
1886 /* The nul terminator bleeds over into input */
1887 strncpy(input_with_prefix
, "New name: ",
1889 rerender_input_string
= 1;
1893 case UIA_NEW_EDGE_2
:
1894 case UIA_NEW_H_EDGE_2
:
1896 if (selected_vertex
== (size_t) -1 ||
1897 selected_vertex
== last_clicked_vertex
) {
1898 switch (ui_action
) {
1899 case UIA_NEW_EDGE_2
:
1900 ui_action
= UIA_NEW_EDGE_1
;
1902 case UIA_NEW_H_EDGE_2
:
1903 ui_action
= UIA_NEW_H_EDGE_1
;
1906 ui_action
= UIA_NONE
;
1909 last_clicked_vertex
= (size_t) -1;
1913 ui_e
= (struct ui_event
) {
1915 .type
= ET_NEW_EDGE
, .idx_1
=
1916 last_clicked_vertex
, .idx_2
=
1917 selected_vertex
, .a
= 1, .b
=
1918 (ui_action
== UIA_NEW_EDGE_2
?
1921 ret
= eq_push(&ui_e
);
1923 if (!ui_action_loop
) {
1924 ui_action
= UIA_NONE
;
1925 } else if (ui_action
== UIA_NEW_EDGE_2
) {
1926 ui_action
= UIA_NEW_EDGE_1
;
1928 ui_action
= UIA_NEW_H_EDGE_1
;
1931 last_clicked_vertex
= (size_t) -1;
1935 if (selected_vertex
!= (size_t) -1) {
1936 ui_e
= (struct ui_event
) {
1938 .type
= ET_DELETE_VERTEX
,
1939 .idx_1
= selected_vertex
1941 ret
= eq_push(&ui_e
);
1942 } else if (selected_edge_i
!= (size_t) -1 &&
1943 selected_edge_j
!= (size_t) -1) {
1944 ui_e
= (struct ui_event
) {
1946 .type
= ET_DELETE_EDGE
, .idx_1
=
1951 ret
= eq_push(&ui_e
);
1954 ui_action
= ui_action_loop
? UIA_DELETE
:
1957 case UIA_INC_FATNESS
:
1958 case UIA_DEC_FATNESS
:
1960 if (selected_vertex
== (size_t) -1) {
1964 ui_e
= (struct ui_event
) {
1966 .type
= ET_CHANGE_FATNESS
, .idx_1
=
1967 selected_vertex
, .int_1
=
1972 ret
= eq_push(&ui_e
);
1974 if (!ui_action_loop
) {
1975 ui_action
= UIA_NONE
;
1981 case UIA_ENTER_SAVE
:
1982 case UIA_ENTER_LOAD
:
1983 case UIA_ENTER_RENAME
:
1984 case UIA_ENTER_COLOR
:
1997 if (load_requested
||
1999 save_load_delay
= 30;
2003 if (load_requested
) {
2004 r
= choose_load_file(&f
);
2006 r
= choose_save_file(&f
);
2011 ui_e
= (struct ui_event
) { .str
= f
};
2012 ui_e
.type
= load_requested
? ET_LOAD
: ET_SAVE
;
2014 /* f is freed on next ui_finish_frame */
2016 ret
= eq_push(&ui_e
);
2019 ui_action
= load_requested
? UIA_ENTER_LOAD
:
2024 SDL_StartTextInput();
2026 /* The nul terminator bleeds over into input */
2027 strncpy(input_with_prefix
, "Filename: ", 11);
2028 rerender_input_string
= 1;
2033 now
= SDL_GetTicks();
2035 if (frame_start_ticks
< now
) {
2036 Uint32 elapsed_time
= now
- frame_start_ticks
;
2038 if (elapsed_time
< TICKS_PER_FRAME
) {
2039 SDL_Delay(TICKS_PER_FRAME
- elapsed_time
);
2048 /* Return an event to the main loop */
2050 ui_get_event(struct ui_event
*e
, uint_fast8_t *more
)
2053 *more
= eq_head
!= eq_tail
;
2062 case ET_CHANGE_FATNESS
:
2063 case ET_CHANGE_COLOR
:
2064 case ET_DELETE_EDGE
:
2065 case ET_DELETE_VERTEX
: