Mercurial > repos > blastem
comparison nuklear_ui/blastem_nuklear.c @ 1692:5dacaef602a7 segacd
Merge from default
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 05 Jan 2019 00:58:08 -0800 |
parents | 357b4951d9b2 |
children | 3f1c8258e20f |
comparison
equal
deleted
inserted
replaced
1504:95b3a1a8b26c | 1692:5dacaef602a7 |
---|---|
1 #define NK_IMPLEMENTATION | |
2 #define NK_SDL_GLES2_IMPLEMENTATION | |
3 | |
4 #include <stdlib.h> | |
5 #include <limits.h> | |
6 #include "blastem_nuklear.h" | |
7 #include "font.h" | |
8 #include "../render.h" | |
9 #include "../render_sdl.h" | |
10 #include "../util.h" | |
11 #include "../paths.h" | |
12 #include "../saves.h" | |
13 #include "../blastem.h" | |
14 #include "../config.h" | |
15 #include "../io.h" | |
16 #include "../png.h" | |
17 #include "../controller_info.h" | |
18 #include "../bindings.h" | |
19 | |
20 static struct nk_context *context; | |
21 | |
22 typedef struct | |
23 { | |
24 uint32_t *image_data; | |
25 uint32_t width, height; | |
26 struct nk_image ui; | |
27 } ui_image; | |
28 | |
29 static ui_image **ui_images, *controller_360, *controller_ps4, *controller_ps4_6b; | |
30 static uint32_t num_ui_images, ui_image_storage; | |
31 | |
32 typedef void (*view_fun)(struct nk_context *); | |
33 static view_fun current_view; | |
34 static view_fun *previous_views; | |
35 static uint32_t view_storage; | |
36 static uint32_t num_prev; | |
37 static struct nk_font *def_font; | |
38 static uint8_t config_dirty; | |
39 | |
40 static void push_view(view_fun new_view) | |
41 { | |
42 if (num_prev == view_storage) { | |
43 view_storage = view_storage ? 2*view_storage : 2; | |
44 previous_views = realloc(previous_views, view_storage*sizeof(view_fun)); | |
45 } | |
46 previous_views[num_prev++] = current_view; | |
47 current_view = new_view; | |
48 } | |
49 | |
50 static void pop_view() | |
51 { | |
52 if (num_prev) { | |
53 current_view = previous_views[--num_prev]; | |
54 } | |
55 } | |
56 | |
57 static void clear_view_stack() | |
58 { | |
59 num_prev = 0; | |
60 } | |
61 | |
62 void view_play(struct nk_context *context) | |
63 { | |
64 | |
65 } | |
66 | |
67 void view_file_browser(struct nk_context *context, uint8_t normal_open) | |
68 { | |
69 static char *current_path; | |
70 static dir_entry *entries; | |
71 static size_t num_entries; | |
72 static int32_t selected_entry = -1; | |
73 static char **ext_list; | |
74 static uint32_t num_exts; | |
75 static uint8_t got_ext_list; | |
76 if (!current_path) { | |
77 get_initial_browse_path(¤t_path); | |
78 } | |
79 if (!entries) { | |
80 entries = get_dir_list(current_path, &num_entries); | |
81 if (entries) { | |
82 sort_dir_list(entries, num_entries); | |
83 } | |
84 } | |
85 if (!got_ext_list) { | |
86 ext_list = get_extension_list(config, &num_exts); | |
87 got_ext_list = 1; | |
88 } | |
89 uint32_t width = render_width(); | |
90 uint32_t height = render_height(); | |
91 if (nk_begin(context, "Load ROM", nk_rect(0, 0, width, height), 0)) { | |
92 nk_layout_row_static(context, height - context->style.font->height * 3, width - 60, 1); | |
93 int32_t old_selected = selected_entry; | |
94 if (nk_group_begin(context, "Select ROM", NK_WINDOW_BORDER | NK_WINDOW_TITLE)) { | |
95 nk_layout_row_static(context, context->style.font->height - 2, width-100, 1); | |
96 for (int32_t i = 0; i < num_entries; i++) | |
97 { | |
98 if (entries[i].name[0] == '.' && entries[i].name[1] != '.') { | |
99 continue; | |
100 } | |
101 if (num_exts && !entries[i].is_dir && !path_matches_extensions(entries[i].name, ext_list, num_exts)) { | |
102 continue; | |
103 } | |
104 int selected = i == selected_entry; | |
105 nk_selectable_label(context, entries[i].name, NK_TEXT_ALIGN_LEFT, &selected); | |
106 if (selected) { | |
107 selected_entry = i; | |
108 } else if (i == selected_entry) { | |
109 selected_entry = -1; | |
110 } | |
111 } | |
112 nk_group_end(context); | |
113 } | |
114 nk_layout_row_static(context, context->style.font->height * 1.75, width > 600 ? 300 : width / 2, 2); | |
115 if (nk_button_label(context, "Back")) { | |
116 pop_view(); | |
117 } | |
118 if (nk_button_label(context, "Open") || (old_selected >= 0 && selected_entry < 0)) { | |
119 if (selected_entry < 0) { | |
120 selected_entry = old_selected; | |
121 } | |
122 char *full_path = path_append(current_path, entries[selected_entry].name); | |
123 if (entries[selected_entry].is_dir) { | |
124 free(current_path); | |
125 current_path = full_path; | |
126 free_dir_list(entries, num_entries); | |
127 entries = NULL; | |
128 } else { | |
129 if(normal_open) { | |
130 if (current_system) { | |
131 current_system->next_rom = full_path; | |
132 current_system->request_exit(current_system); | |
133 } else { | |
134 init_system_with_media(full_path, SYSTEM_UNKNOWN); | |
135 free(full_path); | |
136 } | |
137 } else { | |
138 lockon_media(full_path); | |
139 free(full_path); | |
140 } | |
141 clear_view_stack(); | |
142 show_play_view(); | |
143 } | |
144 selected_entry = -1; | |
145 } | |
146 nk_end(context); | |
147 } | |
148 } | |
149 | |
150 void view_load(struct nk_context *context) | |
151 { | |
152 view_file_browser(context, 1); | |
153 } | |
154 | |
155 void view_lock_on(struct nk_context *context) | |
156 { | |
157 view_file_browser(context, 0); | |
158 } | |
159 | |
160 void view_about(struct nk_context *context) | |
161 { | |
162 const char *lines[] = { | |
163 "BlastEm v0.6.1", | |
164 "Copyright 2012-2017 Michael Pavone", | |
165 "", | |
166 "BlastEm is a high performance open source", | |
167 "(GPLv3) Genesis/Megadrive emulator", | |
168 }; | |
169 const uint32_t NUM_LINES = sizeof(lines)/sizeof(*lines); | |
170 const char *thanks[] = { | |
171 "Nemesis: Documentation and test ROMs", | |
172 "Charles MacDonald: Documentation", | |
173 "Eke-Eke: Documentation", | |
174 "Bart Trzynadlowski: Documentation", | |
175 "KanedaFR: Hosting the best Sega forum", | |
176 "Titan: Awesome demos and documentation", | |
177 "flamewing: BCD info and test ROM", | |
178 "r57shell: Opcode size test ROM", | |
179 "micky: Testing", | |
180 "Sasha: Testing", | |
181 "lol-frank: Testing", | |
182 "Sik: Testing", | |
183 "Tim Lawrence : Testing", | |
184 "ComradeOj: Testing", | |
185 "Vladikcomper: Testing" | |
186 }; | |
187 const uint32_t NUM_THANKS = sizeof(thanks)/sizeof(*thanks); | |
188 uint32_t width = render_width(); | |
189 uint32_t height = render_height(); | |
190 if (nk_begin(context, "About", nk_rect(0, 0, width, height), 0)) { | |
191 nk_layout_row_static(context, context->style.font->height, width-40, 1); | |
192 for (uint32_t i = 0; i < NUM_LINES; i++) | |
193 { | |
194 nk_label(context, lines[i], NK_TEXT_LEFT); | |
195 } | |
196 nk_layout_row_static(context, height - (context->style.font->height * 2 + 20) - (context->style.font->height +4)*NUM_LINES, width-40, 1); | |
197 if (nk_group_begin(context, "Special Thanks", NK_WINDOW_TITLE)) { | |
198 nk_layout_row_static(context, context->style.font->height, width - 80, 1); | |
199 for (uint32_t i = 0; i < NUM_THANKS; i++) | |
200 { | |
201 nk_label(context, thanks[i], NK_TEXT_LEFT); | |
202 } | |
203 nk_group_end(context); | |
204 } | |
205 nk_layout_row_static(context, context->style.font->height * 1.75, width/3, 1); | |
206 if (nk_button_label(context, "Back")) { | |
207 pop_view(); | |
208 } | |
209 nk_end(context); | |
210 } | |
211 } | |
212 | |
213 typedef struct { | |
214 const char *title; | |
215 view_fun next_view; | |
216 } menu_item; | |
217 | |
218 static save_slot_info *slots; | |
219 static uint32_t num_slots, selected_slot; | |
220 | |
221 void view_choose_state(struct nk_context *context, uint8_t is_load) | |
222 { | |
223 uint32_t width = render_width(); | |
224 uint32_t height = render_height(); | |
225 if (nk_begin(context, "Slot Picker", nk_rect(0, 0, width, height), 0)) { | |
226 nk_layout_row_static(context, height - context->style.font->height * 3, width - 60, 1); | |
227 if (nk_group_begin(context, "Select Save Slot", NK_WINDOW_BORDER | NK_WINDOW_TITLE)) { | |
228 nk_layout_row_static(context, context->style.font->height - 2, width-100, 1); | |
229 if (!slots) { | |
230 slots = get_slot_info(current_system, &num_slots); | |
231 } | |
232 for (uint32_t i = 0; i < num_slots; i++) | |
233 { | |
234 int selected = i == selected_slot; | |
235 nk_selectable_label(context, slots[i].desc, NK_TEXT_ALIGN_LEFT, &selected); | |
236 if (selected && (slots[i].modification_time || !is_load)) { | |
237 selected_slot = i; | |
238 } | |
239 } | |
240 nk_group_end(context); | |
241 } | |
242 nk_layout_row_static(context, context->style.font->height * 1.75, width > 600 ? 300 : width / 2, 2); | |
243 if (nk_button_label(context, "Back")) { | |
244 pop_view(); | |
245 } | |
246 if (is_load) { | |
247 if (nk_button_label(context, "Load")) { | |
248 current_system->load_state(current_system, selected_slot); | |
249 show_play_view(); | |
250 } | |
251 } else { | |
252 if (nk_button_label(context, "Save")) { | |
253 current_system->save_state = selected_slot + 1; | |
254 show_play_view(); | |
255 } | |
256 } | |
257 nk_end(context); | |
258 } | |
259 } | |
260 | |
261 void view_save_state(struct nk_context *context) | |
262 { | |
263 view_choose_state(context, 0); | |
264 } | |
265 | |
266 void view_load_state(struct nk_context *context) | |
267 { | |
268 view_choose_state(context, 1); | |
269 } | |
270 | |
271 typedef void (*menu_handler)(uint32_t index); | |
272 | |
273 static void menu(struct nk_context *context, uint32_t num_entries, const menu_item *items, menu_handler handler) | |
274 { | |
275 const uint32_t button_height = context->style.font->height * 1.75; | |
276 const uint32_t ideal_button_width = context->style.font->height * 10; | |
277 const uint32_t button_space = 6; | |
278 | |
279 uint32_t width = render_width(); | |
280 uint32_t height = render_height(); | |
281 uint32_t top = height/2 - (button_height * num_entries)/2; | |
282 uint32_t button_width = width > ideal_button_width ? ideal_button_width : width; | |
283 uint32_t left = width/2 - button_width/2; | |
284 | |
285 nk_layout_space_begin(context, NK_STATIC, top + button_height * num_entries, num_entries); | |
286 for (uint32_t i = 0; i < num_entries; i++) | |
287 { | |
288 nk_layout_space_push(context, nk_rect(left, top + i * button_height, button_width, button_height-button_space)); | |
289 if (nk_button_label(context, items[i].title)) { | |
290 if (items[i].next_view) { | |
291 push_view(items[i].next_view); | |
292 if (current_view == view_save_state || current_view == view_load_state) { | |
293 free_slot_info(slots); | |
294 slots = NULL; | |
295 } else if (current_view == view_play) { | |
296 set_content_binding_state(1); | |
297 } | |
298 } else { | |
299 handler(i); | |
300 } | |
301 } | |
302 } | |
303 nk_layout_space_end(context); | |
304 } | |
305 | |
306 void binding_loop(char *key, tern_val val, uint8_t valtype, void *data) | |
307 { | |
308 if (valtype != TVAL_PTR) { | |
309 return; | |
310 } | |
311 tern_node **binding_lookup = data; | |
312 *binding_lookup = tern_insert_ptr(*binding_lookup, val.ptrval, strdup(key)); | |
313 } | |
314 | |
315 static int32_t keycode; | |
316 static const char *set_binding; | |
317 char *set_label; | |
318 void binding_group(struct nk_context *context, char *name, const char **binds, const char **bind_names, uint32_t num_binds, tern_node *binding_lookup) | |
319 { | |
320 nk_layout_row_static(context, (context->style.font->height + 4)*num_binds+context->style.font->height+30, render_width() - 80, 1); | |
321 if (nk_group_begin(context, name, NK_WINDOW_TITLE)) { | |
322 nk_layout_row_static(context, context->style.font->height, render_width()/2 - 80, 2); | |
323 | |
324 for (int i = 0; i < num_binds; i++) | |
325 { | |
326 char *label_alloc = bind_names ? NULL : path_extension(binds[i]); | |
327 const char *label = label_alloc; | |
328 if (!label) { | |
329 label = bind_names ? bind_names[i] : binds[i]; | |
330 } | |
331 nk_label(context, label, NK_TEXT_LEFT); | |
332 if (nk_button_label(context, tern_find_ptr_default(binding_lookup, binds[i], "Not Set"))) { | |
333 set_binding = binds[i]; | |
334 set_label = strdup(label); | |
335 keycode = 0; | |
336 } | |
337 if (label_alloc) { | |
338 free(label_alloc); | |
339 } | |
340 } | |
341 nk_group_end(context); | |
342 } | |
343 } | |
344 | |
345 static char *get_key_name(int32_t keycode) | |
346 { | |
347 char *name = NULL; | |
348 if (keycode > ' ' && keycode < 0x80) { | |
349 //key corresponds to a printable non-whitespace character | |
350 name = malloc(2); | |
351 name[0] = keycode; | |
352 name[1] = 0; | |
353 } else { | |
354 switch (keycode) | |
355 { | |
356 case RENDERKEY_UP: name = "up"; break; | |
357 case RENDERKEY_DOWN: name = "down"; break; | |
358 case RENDERKEY_LEFT: name = "left"; break; | |
359 case RENDERKEY_RIGHT: name = "right"; break; | |
360 case '\r': name = "enter"; break; | |
361 case ' ': name = "space"; break; | |
362 case '\t': name = "tab"; break; | |
363 case '\b': name = "backspace"; break; | |
364 case RENDERKEY_ESC: name = "esc"; break; | |
365 case RENDERKEY_DEL: name = "delete"; break; | |
366 case RENDERKEY_LSHIFT: name = "lshift"; break; | |
367 case RENDERKEY_RSHIFT: name = "rshift"; break; | |
368 case RENDERKEY_LCTRL: name = "lctrl"; break; | |
369 case RENDERKEY_RCTRL: name = "rctrl"; break; | |
370 case RENDERKEY_LALT: name = "lalt"; break; | |
371 case RENDERKEY_RALT: name = "ralt"; break; | |
372 case RENDERKEY_HOME: name = "home"; break; | |
373 case RENDERKEY_END: name = "end"; break; | |
374 case RENDERKEY_PAGEUP: name = "pageup"; break; | |
375 case RENDERKEY_PAGEDOWN: name = "pagedown"; break; | |
376 case RENDERKEY_F1: name = "f1"; break; | |
377 case RENDERKEY_F2: name = "f2"; break; | |
378 case RENDERKEY_F3: name = "f3"; break; | |
379 case RENDERKEY_F4: name = "f4"; break; | |
380 case RENDERKEY_F5: name = "f5"; break; | |
381 case RENDERKEY_F6: name = "f6"; break; | |
382 case RENDERKEY_F7: name = "f7"; break; | |
383 case RENDERKEY_F8: name = "f8"; break; | |
384 case RENDERKEY_F9: name = "f9"; break; | |
385 case RENDERKEY_F10: name = "f10"; break; | |
386 case RENDERKEY_F11: name = "f11"; break; | |
387 case RENDERKEY_F12: name = "f12"; break; | |
388 case RENDERKEY_SELECT: name = "select"; break; | |
389 case RENDERKEY_PLAY: name = "play"; break; | |
390 case RENDERKEY_SEARCH: name = "search"; break; | |
391 case RENDERKEY_BACK: name = "back"; break; | |
392 case RENDERKEY_NP0: name = "np0"; break; | |
393 case RENDERKEY_NP1: name = "np1"; break; | |
394 case RENDERKEY_NP2: name = "np2"; break; | |
395 case RENDERKEY_NP3: name = "np3"; break; | |
396 case RENDERKEY_NP4: name = "np4"; break; | |
397 case RENDERKEY_NP5: name = "np5"; break; | |
398 case RENDERKEY_NP6: name = "np6"; break; | |
399 case RENDERKEY_NP7: name = "np7"; break; | |
400 case RENDERKEY_NP8: name = "np8"; break; | |
401 case RENDERKEY_NP9: name = "np9"; break; | |
402 case RENDERKEY_NP_DIV: name = "np/"; break; | |
403 case RENDERKEY_NP_MUL: name = "np*"; break; | |
404 case RENDERKEY_NP_MIN: name = "np-"; break; | |
405 case RENDERKEY_NP_PLUS: name = "np+"; break; | |
406 case RENDERKEY_NP_ENTER: name = "npenter"; break; | |
407 case RENDERKEY_NP_STOP: name = "np."; break; | |
408 } | |
409 if (name) { | |
410 name = strdup(name); | |
411 } | |
412 } | |
413 return name; | |
414 } | |
415 | |
416 void view_key_bindings(struct nk_context *context) | |
417 { | |
418 const char *controller1_binds[] = { | |
419 "gamepads.1.up", "gamepads.1.down", "gamepads.1.left", "gamepads.1.right", | |
420 "gamepads.1.a", "gamepads.1.b", "gamepads.1.c", | |
421 "gamepads.1.x", "gamepads.1.y", "gamepads.1.z", | |
422 "gamepads.1.start", "gamepads.1.mode" | |
423 }; | |
424 const char *controller2_binds[] = { | |
425 "gamepads.2.up", "gamepads.2.down", "gamepads.2.left", "gamepads.2.right", | |
426 "gamepads.2.a", "gamepads.2.b", "gamepads.2.c", | |
427 "gamepads.2.x", "gamepads.2.y", "gamepads.2.z", | |
428 "gamepads.2.start", "gamepads.2.mode" | |
429 }; | |
430 const char *general_binds[] = { | |
431 "ui.exit", "ui.save_state", "ui.toggle_fullscreen", "ui.soft_reset", "ui.reload", | |
432 "ui.screenshot", "ui.sms_pause", "ui.toggle_keyboard_cpatured", "ui.release_mouse" | |
433 }; | |
434 const char *general_names[] = { | |
435 "Show Menu", "Quick Save", "Toggle Fullscreen", "Soft Reset", "Reload Media", | |
436 "Internal Screenshot", "SMS Pause", "Capture Keyboard", "Release Mouse" | |
437 }; | |
438 const char *speed_binds[] = { | |
439 "ui.next_speed", "ui.prev_speed", | |
440 "ui.set_speed.0", "ui.set_speed.1", "ui.set_speed.2" ,"ui.set_speed.3", "ui.set_speed.4", | |
441 "ui.set_speed.5", "ui.set_speed.6", "ui.set_speed.7" ,"ui.set_speed.8", "ui.set_speed.9", | |
442 }; | |
443 const char *speed_names[] = { | |
444 "Next", "Previous", | |
445 "Default Speed", "Set Speed 1", "Set Speed 2", "Set Speed 3", "Set Speed 4", | |
446 "Set Speed 5", "Set Speed 6", "Set Speed 7", "Set Speed 8", "Set Speed 9" | |
447 }; | |
448 const char *debug_binds[] = { | |
449 "ui.enter_debugger", "ui.plane_debug", "ui.vram_debug", "ui.cram_debug", | |
450 "ui.compositing_debug", "ui.vdp_debug_mode" | |
451 }; | |
452 const char *debug_names[] = { | |
453 "CPU Debugger", "Plane Debugger", "VRAM Debugger", "CRAM Debugger", | |
454 "Layer Debugger", "Cycle Mode/Pal" | |
455 }; | |
456 const uint32_t NUM_C1_BINDS = sizeof(controller1_binds)/sizeof(*controller1_binds); | |
457 const uint32_t NUM_C2_BINDS = sizeof(controller2_binds)/sizeof(*controller2_binds); | |
458 const uint32_t NUM_SPEED_BINDS = sizeof(speed_binds)/sizeof(*speed_binds); | |
459 const uint32_t NUM_GEN_BINDS = sizeof(general_binds)/sizeof(*general_binds); | |
460 const uint32_t NUM_DBG_BINDS = sizeof(debug_binds)/sizeof(*debug_binds); | |
461 static tern_node *binding_lookup; | |
462 if (!binding_lookup) { | |
463 tern_node *bindings = tern_find_path(config, "bindings\0keys\0", TVAL_NODE).ptrval; | |
464 if (bindings) { | |
465 tern_foreach(bindings, binding_loop, &binding_lookup); | |
466 } | |
467 } | |
468 uint32_t width = render_width(); | |
469 uint32_t height = render_height(); | |
470 if (nk_begin(context, "Keyboard Bindings", nk_rect(0, 0, width, height), 0)) { | |
471 binding_group(context, "Controller 1", controller1_binds, NULL, NUM_C1_BINDS, binding_lookup); | |
472 binding_group(context, "Controller 2", controller2_binds, NULL, NUM_C2_BINDS, binding_lookup); | |
473 binding_group(context, "General", general_binds, general_names, NUM_GEN_BINDS, binding_lookup); | |
474 binding_group(context, "Speed Control", speed_binds, speed_names, NUM_SPEED_BINDS, binding_lookup); | |
475 binding_group(context, "Debug", debug_binds, debug_names, NUM_DBG_BINDS, binding_lookup); | |
476 nk_layout_row_static(context, context->style.font->height * 1.1333, (render_width() - 80) / 2, 1); | |
477 if (nk_button_label(context, "Back")) { | |
478 pop_view(); | |
479 } | |
480 nk_end(context); | |
481 } | |
482 if (set_binding && nk_begin(context, "Set Binding", nk_rect(width/4, height/4, width/2/*width*3/4*/, height/2), NK_WINDOW_TITLE | NK_WINDOW_BORDER)) { | |
483 nk_layout_row_static(context, 30, width/2-30, 1); | |
484 nk_label(context, "Press new key for", NK_TEXT_CENTERED); | |
485 nk_label(context, set_label, NK_TEXT_CENTERED); | |
486 if (nk_button_label(context, "Cancel")) { | |
487 free(set_label); | |
488 set_binding = set_label = NULL; | |
489 } else if (keycode) { | |
490 char *name = get_key_name(keycode); | |
491 if (name) { | |
492 uint32_t prefix_len = strlen("bindings") + strlen("keys") + 2; | |
493 char * old = tern_find_ptr(binding_lookup, set_binding); | |
494 if (old) { | |
495 uint32_t suffix_len = strlen(old) + 1; | |
496 char *old_path = malloc(prefix_len + suffix_len + 1); | |
497 memcpy(old_path, "bindings\0keys\0", prefix_len); | |
498 memcpy(old_path + prefix_len, old, suffix_len); | |
499 old_path[prefix_len + suffix_len] = 0; | |
500 tern_val old_val; | |
501 if (tern_delete_path(&config, old_path, &old_val) == TVAL_PTR) { | |
502 free(old_val.ptrval); | |
503 } | |
504 } | |
505 uint32_t suffix_len = strlen(name) + 1; | |
506 char *path = malloc(prefix_len + suffix_len + 1); | |
507 memcpy(path, "bindings\0keys\0", prefix_len); | |
508 memcpy(path + prefix_len, name, suffix_len); | |
509 path[prefix_len + suffix_len] = 0; | |
510 | |
511 config_dirty = 1; | |
512 config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(set_binding)}, TVAL_PTR); | |
513 free(path); | |
514 free(name); | |
515 tern_free(binding_lookup); | |
516 binding_lookup = NULL; | |
517 } | |
518 free(set_label); | |
519 set_binding = set_label = NULL; | |
520 } | |
521 nk_end(context); | |
522 } | |
523 } | |
524 | |
525 static int selected_controller; | |
526 static controller_info selected_controller_info; | |
527 //#define MIN_BIND_BOX_WIDTH 140 | |
528 #define MAX_BIND_BOX_WIDTH 350 | |
529 | |
530 #define AXIS 0x40000000 | |
531 #define STICKDIR 0x30000000 | |
532 #define LEFTSTICK 0x10000000 | |
533 #define RIGHTSTICK 0x20000000 | |
534 enum { | |
535 UP,DOWN,RIGHT,LEFT,NUM_AXIS_DIRS | |
536 }; | |
537 | |
538 static char * config_ps_names[] = { | |
539 [SDL_CONTROLLER_BUTTON_A] = "cross", | |
540 [SDL_CONTROLLER_BUTTON_B] = "circle", | |
541 [SDL_CONTROLLER_BUTTON_X] = "square", | |
542 [SDL_CONTROLLER_BUTTON_Y] = "triangle", | |
543 [SDL_CONTROLLER_BUTTON_BACK] = "share", | |
544 [SDL_CONTROLLER_BUTTON_START] = "options", | |
545 [SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = "l1", | |
546 [SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = "r1", | |
547 [SDL_CONTROLLER_BUTTON_LEFTSTICK] = "l3", | |
548 [SDL_CONTROLLER_BUTTON_RIGHTSTICK] = "r3", | |
549 }; | |
550 | |
551 typedef struct { | |
552 const char *button_binds[SDL_CONTROLLER_BUTTON_MAX]; | |
553 const char *left_stick[NUM_AXIS_DIRS]; | |
554 const char *right_stick[NUM_AXIS_DIRS]; | |
555 const char *triggers[2]; | |
556 } pad_bind_config; | |
557 | |
558 static const char **current_bind_dest; | |
559 | |
560 const char *translate_binding_option(const char *option) | |
561 { | |
562 static tern_node *conf_names; | |
563 if (!conf_names) { | |
564 conf_names = tern_insert_ptr(conf_names, "gamepads.n.up", "Pad Up"); | |
565 conf_names = tern_insert_ptr(conf_names, "gamepads.n.down", "Pad Down"); | |
566 conf_names = tern_insert_ptr(conf_names, "gamepads.n.left", "Pad Left"); | |
567 conf_names = tern_insert_ptr(conf_names, "gamepads.n.right", "Pad Right"); | |
568 conf_names = tern_insert_ptr(conf_names, "gamepads.n.a", "Pad A"); | |
569 conf_names = tern_insert_ptr(conf_names, "gamepads.n.b", "Pad B"); | |
570 conf_names = tern_insert_ptr(conf_names, "gamepads.n.c", "Pad C"); | |
571 conf_names = tern_insert_ptr(conf_names, "gamepads.n.x", "Pad X"); | |
572 conf_names = tern_insert_ptr(conf_names, "gamepads.n.y", "Pad Y"); | |
573 conf_names = tern_insert_ptr(conf_names, "gamepads.n.z", "Pad Z"); | |
574 conf_names = tern_insert_ptr(conf_names, "gamepads.n.start", "Pad Start"); | |
575 conf_names = tern_insert_ptr(conf_names, "gamepads.n.mode", "Pad Mode"); | |
576 conf_names = tern_insert_ptr(conf_names, "ui.release_mouse", "Release Mouse"); | |
577 conf_names = tern_insert_ptr(conf_names, "ui.vdp_debug_mode", "VDP Debug Mode"); | |
578 conf_names = tern_insert_ptr(conf_names, "ui.vdp_debug_pal", "VDP Debug Palette"); | |
579 conf_names = tern_insert_ptr(conf_names, "ui.enter_debugger", "Enter CPU Debugger"); | |
580 conf_names = tern_insert_ptr(conf_names, "ui.screenshot", "Take Screenshot"); | |
581 conf_names = tern_insert_ptr(conf_names, "ui.exit", "Show Menu"); | |
582 conf_names = tern_insert_ptr(conf_names, "ui.save_state", "Quick Save"); | |
583 conf_names = tern_insert_ptr(conf_names, "ui.set_speed.0", "Set Speed 0"); | |
584 conf_names = tern_insert_ptr(conf_names, "ui.set_speed.1", "Set Speed 1"); | |
585 conf_names = tern_insert_ptr(conf_names, "ui.set_speed.2", "Set Speed 2"); | |
586 conf_names = tern_insert_ptr(conf_names, "ui.set_speed.3", "Set Speed 3"); | |
587 conf_names = tern_insert_ptr(conf_names, "ui.set_speed.4", "Set Speed 4"); | |
588 conf_names = tern_insert_ptr(conf_names, "ui.set_speed.5", "Set Speed 5"); | |
589 conf_names = tern_insert_ptr(conf_names, "ui.set_speed.6", "Set Speed 6"); | |
590 conf_names = tern_insert_ptr(conf_names, "ui.set_speed.7", "Set Speed 7"); | |
591 conf_names = tern_insert_ptr(conf_names, "ui.set_speed.8", "Set Speed 8"); | |
592 conf_names = tern_insert_ptr(conf_names, "ui.set_speed.9", "Set Speed 9"); | |
593 conf_names = tern_insert_ptr(conf_names, "ui.next_speed", "Next Speed"); | |
594 conf_names = tern_insert_ptr(conf_names, "ui.prev_speed", "Prev. Speed"); | |
595 conf_names = tern_insert_ptr(conf_names, "ui.toggle_fullscreen", "Toggle Fullscreen"); | |
596 conf_names = tern_insert_ptr(conf_names, "ui.soft_reset", "Soft Reset"); | |
597 conf_names = tern_insert_ptr(conf_names, "ui.reload", "Reload ROM"); | |
598 conf_names = tern_insert_ptr(conf_names, "ui.sms_pause", "SMS Pause"); | |
599 conf_names = tern_insert_ptr(conf_names, "ui.toggle_keyboard_captured", "Toggle Keyboard Capture"); | |
600 } | |
601 return tern_find_ptr_default(conf_names, option, (void *)option); | |
602 } | |
603 | |
604 static uint8_t controller_binding_changed; | |
605 static void bind_option_group(struct nk_context *context, char *name, const char **options, uint32_t num_options) | |
606 { | |
607 float margin = context->style.font->height * 2; | |
608 nk_layout_row_static(context, (context->style.font->height + 3) * ((num_options + 2) / 3) + context->style.font->height*2.1, render_width() - margin, 1); | |
609 if (nk_group_begin(context, name, NK_WINDOW_TITLE|NK_WINDOW_NO_SCROLLBAR)) { | |
610 nk_layout_row_static(context, context->style.font->height, (render_width() - margin - context->style.font->height) / 3, 3); | |
611 for (int i = 0; i < num_options; i++) | |
612 { | |
613 if (nk_button_label(context, translate_binding_option(options[i]))) { | |
614 *current_bind_dest = options[i]; | |
615 controller_binding_changed = 1; | |
616 pop_view(); | |
617 } | |
618 } | |
619 nk_group_end(context); | |
620 } | |
621 } | |
622 | |
623 static void view_button_binding(struct nk_context *context) | |
624 { | |
625 static const char *pad_opts[] = { | |
626 "gamepads.n.up", | |
627 "gamepads.n.down", | |
628 "gamepads.n.left", | |
629 "gamepads.n.right", | |
630 "gamepads.n.a", | |
631 "gamepads.n.b", | |
632 "gamepads.n.c", | |
633 "gamepads.n.x", | |
634 "gamepads.n.y", | |
635 "gamepads.n.z", | |
636 "gamepads.n.start", | |
637 "gamepads.n.mode" | |
638 }; | |
639 static const char *system_buttons[] = { | |
640 "ui.soft_reset", | |
641 "ui.reload", | |
642 "ui.sms_pause" | |
643 }; | |
644 static const char *emu_control[] = { | |
645 "ui.save_state", | |
646 "ui.exit", | |
647 "ui.toggle_fullscreen", | |
648 "ui.screenshot", | |
649 "ui.release_mouse", | |
650 "ui.toggle_keyboard_captured" | |
651 }; | |
652 static const char *debugger[] = { | |
653 "ui.vdp_debug_mode", | |
654 "ui.vdp_debug_pal", | |
655 "ui.enter_debugger" | |
656 }; | |
657 static const char *speeds[] = { | |
658 "ui.next_speed", | |
659 "ui.prev_speed", | |
660 "ui.set_speed.0", | |
661 "ui.set_speed.1", | |
662 "ui.set_speed.2", | |
663 "ui.set_speed.3", | |
664 "ui.set_speed.4", | |
665 "ui.set_speed.5", | |
666 "ui.set_speed.6", | |
667 "ui.set_speed.7", | |
668 "ui.set_speed.8", | |
669 "ui.set_speed.9" | |
670 }; | |
671 | |
672 if (nk_begin(context, "Button Binding", nk_rect(0, 0, render_width(), render_height()), 0)) { | |
673 bind_option_group(context, "Controller Buttons", pad_opts, sizeof(pad_opts)/sizeof(*pad_opts)); | |
674 bind_option_group(context, "System Buttons", system_buttons, sizeof(system_buttons)/sizeof(*system_buttons)); | |
675 bind_option_group(context, "Emulator Control", emu_control, sizeof(emu_control)/sizeof(*emu_control)); | |
676 bind_option_group(context, "Debugging", debugger, sizeof(debugger)/sizeof(*debugger)); | |
677 bind_option_group(context, "Speed Control", speeds, sizeof(speeds)/sizeof(*speeds)); | |
678 | |
679 nk_layout_row_static(context, context->style.font->height, (render_width() - 80)/4, 1); | |
680 if (nk_button_label(context, "Back")) { | |
681 pop_view(); | |
682 } | |
683 nk_end(context); | |
684 } | |
685 } | |
686 | |
687 static void binding_box(struct nk_context *context, pad_bind_config *bindings, char *name, float x, float y, float width, int num_binds, int *binds) | |
688 { | |
689 const struct nk_user_font *font = context->style.font; | |
690 float row_height = font->height * 2; | |
691 | |
692 char const **labels = calloc(sizeof(char *), num_binds); | |
693 char const ***conf_vals = calloc(sizeof(char *), num_binds); | |
694 float max_width = 0.0f; | |
695 | |
696 int skipped = 0; | |
697 for (int i = 0; i < num_binds; i++) | |
698 { | |
699 if (binds[i] & AXIS) { | |
700 labels[i] = get_axis_label(&selected_controller_info, binds[i] & ~AXIS); | |
701 conf_vals[i] = &bindings->triggers[(binds[i] & ~AXIS) - SDL_CONTROLLER_AXIS_TRIGGERLEFT]; | |
702 } else if (binds[i] & STICKDIR) { | |
703 static char const * dirs[] = {"Up", "Down", "Right", "Left"}; | |
704 labels[i] = dirs[binds[i] & 3]; | |
705 conf_vals[i] = &(binds[i] & LEFTSTICK ? bindings->left_stick : bindings->right_stick)[binds[i] & 3]; | |
706 } else { | |
707 labels[i] = get_button_label(&selected_controller_info, binds[i]); | |
708 conf_vals[i] = &bindings->button_binds[binds[i]]; | |
709 } | |
710 if (!labels[i]) { | |
711 skipped++; | |
712 continue; | |
713 } | |
714 float lb_width = font->width(font->userdata, font->height, labels[i], strlen(labels[i])); | |
715 max_width = max_width < lb_width ? lb_width : max_width; | |
716 } | |
717 nk_layout_space_push(context, nk_rect(x, y, width, (num_binds - skipped) * (row_height + 4) + 4)); | |
718 nk_group_begin(context, name, NK_WINDOW_BORDER | NK_WINDOW_NO_SCROLLBAR); | |
719 | |
720 float widths[] = {max_width + 3, width - (max_width + 6)}; | |
721 nk_layout_row(context, NK_STATIC, row_height, 2, widths); | |
722 for (int i = 0; i < num_binds; i++) | |
723 { | |
724 if (!labels[i]) { | |
725 continue; | |
726 } | |
727 nk_label(context, labels[i], NK_TEXT_LEFT); | |
728 const char *name = *conf_vals[i] ? translate_binding_option(*conf_vals[i]) : "None"; | |
729 if (nk_button_label(context, name)) { | |
730 current_bind_dest = conf_vals[i]; | |
731 push_view(view_button_binding); | |
732 } | |
733 } | |
734 free(labels); | |
735 free(conf_vals); | |
736 nk_group_end(context); | |
737 } | |
738 | |
739 static void button_iter(char *key, tern_val val, uint8_t valtype, void *data) | |
740 { | |
741 pad_bind_config *bindings = data; | |
742 if (valtype != TVAL_PTR) { | |
743 return; | |
744 } | |
745 int button = render_lookup_button(key); | |
746 if (button != SDL_CONTROLLER_BUTTON_INVALID) { | |
747 bindings->button_binds[button] = val.ptrval; | |
748 } | |
749 } | |
750 | |
751 static void axis_iter(char *key, tern_val val, uint8_t valtype, void *data) | |
752 { | |
753 pad_bind_config *bindings = data; | |
754 if (valtype != TVAL_PTR) { | |
755 return; | |
756 } | |
757 int axis; | |
758 uint8_t is_negative = 0; | |
759 char *period = strchr(key, '.'); | |
760 if (period) { | |
761 char *tmp = malloc(period-key + 1); | |
762 memcpy(tmp, key, period-key); | |
763 tmp[period-key] = 0; | |
764 axis = render_lookup_axis(tmp); | |
765 free(tmp); | |
766 is_negative = strcmp(period+1, "negative") == 0; | |
767 } else { | |
768 axis = render_lookup_axis(key); | |
769 } | |
770 switch (axis) | |
771 { | |
772 case SDL_CONTROLLER_AXIS_LEFTX: | |
773 case SDL_CONTROLLER_AXIS_LEFTY: | |
774 bindings->left_stick[(axis - SDL_CONTROLLER_AXIS_LEFTX) * 2 + is_negative] = val.ptrval; | |
775 break; | |
776 case SDL_CONTROLLER_AXIS_RIGHTX: | |
777 case SDL_CONTROLLER_AXIS_RIGHTY: | |
778 bindings->right_stick[(axis - SDL_CONTROLLER_AXIS_RIGHTX) * 2 + is_negative] = val.ptrval; | |
779 break; | |
780 case SDL_CONTROLLER_AXIS_TRIGGERLEFT: | |
781 case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: | |
782 bindings->triggers[axis-SDL_CONTROLLER_AXIS_TRIGGERLEFT] = val.ptrval; | |
783 break; | |
784 } | |
785 } | |
786 | |
787 enum { | |
788 SIMILAR_CONTROLLERS, | |
789 IDENTICAL_CONTROLLERS, | |
790 BY_INDEX, | |
791 DEFAULT, | |
792 NUM_DEST_TYPES | |
793 }; | |
794 | |
795 //it would be cleaner to generate this algorithmically for 4th and up, | |
796 //but BlastEm only supports 8 controllers currently so it's not worth the effort | |
797 static const char *by_index_names[] = { | |
798 "Use for 1st controller", | |
799 "Use for 2nd controller", | |
800 "Use for 3rd controller", | |
801 "Use for 4th controller", | |
802 "Use for 5th controller", | |
803 "Use for 6th controller", | |
804 "Use for 7th controller", | |
805 "Use for 8th controller", | |
806 }; | |
807 | |
808 static void save_stick_binds(char *axes_key, size_t axes_key_size, const char **bindings, char *prefix) | |
809 { | |
810 for (int i = 0; i < NUM_AXIS_DIRS; i++) | |
811 { | |
812 char axis = (i / 2) ? 'x' : 'y'; | |
813 char *suffix = (i % 2) ? ".negative" : ".positive"; | |
814 size_t prefix_len = strlen(prefix), suffix_len = strlen(suffix); | |
815 size_t full_key_size = axes_key_size + prefix_len + 1 + suffix_len + 2; | |
816 char *full_key = malloc(full_key_size); | |
817 memcpy(full_key, axes_key, axes_key_size); | |
818 memcpy(full_key + axes_key_size, prefix, prefix_len); | |
819 full_key[axes_key_size+prefix_len] = axis; | |
820 memcpy(full_key + axes_key_size + prefix_len + 1, suffix, suffix_len +1); | |
821 full_key[axes_key_size + prefix_len + 1 + suffix_len + 1] = 0; | |
822 | |
823 if (bindings[i]) { | |
824 tern_insert_path(config, full_key, (tern_val){.ptrval = strdup(bindings[i])}, TVAL_PTR); | |
825 } else { | |
826 tern_val prev_val; | |
827 uint8_t prev_type = tern_delete_path(&config, full_key, &prev_val); | |
828 if (prev_type == TVAL_PTR) { | |
829 free(prev_val.ptrval); | |
830 } | |
831 } | |
832 | |
833 free(full_key); | |
834 } | |
835 } | |
836 | |
837 static pad_bind_config *bindings; | |
838 static void handle_dest_clicked(uint32_t dest) | |
839 { | |
840 char key_buf[12]; | |
841 char *key; | |
842 switch (dest) | |
843 { | |
844 case SIMILAR_CONTROLLERS: | |
845 key = make_controller_type_key(&selected_controller_info); | |
846 break; | |
847 case IDENTICAL_CONTROLLERS: | |
848 key = render_joystick_type_id(selected_controller); | |
849 break; | |
850 case BY_INDEX: | |
851 snprintf(key_buf, sizeof(key_buf), "%d", selected_controller); | |
852 key = key_buf; | |
853 break; | |
854 default: | |
855 key = "default"; | |
856 break; | |
857 } | |
858 static const char base_path[] = "bindings\0pads"; | |
859 size_t pad_key_size = sizeof(base_path) + strlen(key) + 1; | |
860 char *pad_key = malloc(pad_key_size); | |
861 memcpy(pad_key, base_path, sizeof(base_path)); | |
862 strcpy(pad_key + sizeof(base_path), key); | |
863 static const char dpad_base[] = "dpads\0""0"; | |
864 size_t dpad_key_size = pad_key_size + sizeof(dpad_base); | |
865 char *dpad_key = malloc(dpad_key_size); | |
866 memcpy(dpad_key, pad_key, pad_key_size); | |
867 memcpy(dpad_key + pad_key_size, dpad_base, sizeof(dpad_base)); | |
868 static const char button_base[] = "buttons"; | |
869 size_t button_key_size = pad_key_size + sizeof(button_base); | |
870 char *button_key = malloc(button_key_size); | |
871 memcpy(button_key, pad_key, pad_key_size); | |
872 memcpy(button_key + pad_key_size, button_base, sizeof(button_base)); | |
873 | |
874 char *final_key; | |
875 for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++) | |
876 { | |
877 char *base; | |
878 const char *suffix; | |
879 size_t base_key_len; | |
880 if ( i < SDL_CONTROLLER_BUTTON_DPAD_UP) { | |
881 suffix = SDL_GameControllerGetStringForButton(i); | |
882 base_key_len = button_key_size; | |
883 base = button_key; | |
884 | |
885 | |
886 } else { | |
887 static const char *dir_keys[] = {"up", "down", "left", "right"}; | |
888 suffix = dir_keys[i - SDL_CONTROLLER_BUTTON_DPAD_UP]; | |
889 base = dpad_key; | |
890 base_key_len = dpad_key_size; | |
891 } | |
892 size_t suffix_len = strlen(suffix); | |
893 final_key = malloc(base_key_len + suffix_len + 2); | |
894 memcpy(final_key, base, base_key_len); | |
895 memcpy(final_key + base_key_len, suffix, suffix_len + 1); | |
896 final_key[base_key_len + suffix_len + 1] = 0; | |
897 if (bindings->button_binds[i]) { | |
898 tern_insert_path(config, final_key, (tern_val){.ptrval = strdup(bindings->button_binds[i])}, TVAL_PTR); | |
899 } else { | |
900 tern_val prev_val; | |
901 uint8_t prev_type = tern_delete_path(&config, final_key, &prev_val); | |
902 if (prev_type == TVAL_PTR) { | |
903 free(prev_val.ptrval); | |
904 } | |
905 } | |
906 free(final_key); | |
907 } | |
908 free(button_key); | |
909 free(dpad_key); | |
910 | |
911 static const char axes_base[] = "axes"; | |
912 size_t axes_key_size = pad_key_size + sizeof(axes_base); | |
913 char *axes_key = malloc(axes_key_size); | |
914 memcpy(axes_key, pad_key, pad_key_size); | |
915 memcpy(axes_key + pad_key_size, axes_base, sizeof(axes_base)); | |
916 | |
917 save_stick_binds(axes_key, axes_key_size,bindings->left_stick, "left"); | |
918 save_stick_binds(axes_key, axes_key_size,bindings->right_stick, "right"); | |
919 for (int i = SDL_CONTROLLER_AXIS_TRIGGERLEFT; i < SDL_CONTROLLER_AXIS_MAX; i++) | |
920 { | |
921 const char *suffix = SDL_GameControllerGetStringForAxis(i); | |
922 size_t suffix_len = strlen(suffix); | |
923 final_key = malloc(axes_key_size + suffix_len + 2); | |
924 memcpy(final_key, axes_key, axes_key_size); | |
925 memcpy(final_key + axes_key_size, suffix, suffix_len + 1); | |
926 final_key[axes_key_size + suffix_len + 1] = 0; | |
927 if (bindings->triggers[i - SDL_CONTROLLER_AXIS_TRIGGERLEFT]) { | |
928 tern_insert_path(config, final_key, (tern_val){.ptrval = strdup(bindings->triggers[i - SDL_CONTROLLER_AXIS_TRIGGERLEFT])}, TVAL_PTR); | |
929 } else { | |
930 tern_val prev_val; | |
931 uint8_t prev_type = tern_delete_path(&config, final_key, &prev_val); | |
932 if (prev_type == TVAL_PTR) { | |
933 free(prev_val.ptrval); | |
934 } | |
935 } | |
936 free(final_key); | |
937 } | |
938 free(axes_key); | |
939 | |
940 free(pad_key); | |
941 if (dest == SIMILAR_CONTROLLERS) { | |
942 free(key); | |
943 } | |
944 pop_view(); | |
945 config_dirty = 1; | |
946 } | |
947 | |
948 void view_select_binding_dest(struct nk_context *context) | |
949 { | |
950 static menu_item options[NUM_DEST_TYPES]; | |
951 options[IDENTICAL_CONTROLLERS].title = "Use for identical controllers"; | |
952 options[DEFAULT].title = "Use as default"; | |
953 options[BY_INDEX].title = by_index_names[selected_controller]; | |
954 options[SIMILAR_CONTROLLERS].title = make_human_readable_type_name(&selected_controller_info); | |
955 | |
956 if (nk_begin(context, "Select Binding Dest", nk_rect(0, 0, render_width(), render_height()), NK_WINDOW_NO_SCROLLBAR)) { | |
957 menu(context, NUM_DEST_TYPES, options, handle_dest_clicked); | |
958 nk_end(context); | |
959 } | |
960 free((char *)options[SIMILAR_CONTROLLERS].title); | |
961 } | |
962 | |
963 static ui_image *select_best_image(controller_info *info) | |
964 { | |
965 if (info->variant != VARIANT_NORMAL) { | |
966 return controller_ps4_6b; | |
967 } else if (info->type == TYPE_PSX) { | |
968 return controller_ps4; | |
969 } else { | |
970 return controller_360; | |
971 } | |
972 } | |
973 | |
974 void view_controller_bindings(struct nk_context *context) | |
975 { | |
976 if (nk_begin(context, "Controller Bindings", nk_rect(0, 0, render_width(), render_height()), NK_WINDOW_NO_SCROLLBAR)) { | |
977 if (!bindings) { | |
978 bindings = calloc(1, sizeof(*bindings)); | |
979 tern_node *pad = get_binding_node_for_pad(selected_controller); | |
980 if (pad) { | |
981 tern_foreach(tern_find_node(pad, "buttons"), button_iter, bindings); | |
982 tern_foreach(tern_find_node(pad, "axes"), axis_iter, bindings); | |
983 tern_node *dpad = tern_find_path(pad, "dpads\0" "0\0", TVAL_NODE).ptrval; | |
984 const char *dir_keys[] = {"up", "down", "right", "left"}; | |
985 const int button_idx[] = {SDL_CONTROLLER_BUTTON_DPAD_UP, SDL_CONTROLLER_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, SDL_CONTROLLER_BUTTON_DPAD_LEFT}; | |
986 for (int i = 0; i < NUM_AXIS_DIRS; i++) | |
987 { | |
988 bindings->button_binds[button_idx[i]] = tern_find_ptr(dpad, dir_keys[i]); | |
989 } | |
990 } | |
991 } | |
992 | |
993 float orig_height = def_font->handle.height; | |
994 def_font->handle.height *= 0.5f; | |
995 | |
996 uint32_t avail_height = render_height() - 2 * orig_height; | |
997 float desired_width = render_width() * 0.5f, desired_height = avail_height * 0.5f; | |
998 ui_image *controller_image = select_best_image(&selected_controller_info); | |
999 | |
1000 float controller_ratio = (float)controller_image->width / (float)controller_image->height; | |
1001 | |
1002 const struct nk_user_font *font = context->style.font; | |
1003 int MIN_BIND_BOX_WIDTH = font->width(font->userdata, font->height, "Right", strlen("Right")) | |
1004 + def_font->handle.width(font->userdata, font->height, "Internal Screenshot", strlen("Internal Screenshot")); | |
1005 | |
1006 if (render_width() - desired_width < 2.5f*MIN_BIND_BOX_WIDTH) { | |
1007 desired_width = render_width() - 2.5f*MIN_BIND_BOX_WIDTH; | |
1008 } | |
1009 | |
1010 if (desired_width / desired_height > controller_ratio) { | |
1011 desired_width = desired_height * controller_ratio; | |
1012 } else { | |
1013 desired_height = desired_width / controller_ratio; | |
1014 } | |
1015 float img_left = render_width() / 2.0f - desired_width / 2.0f; | |
1016 float img_top = avail_height / 2.0f - desired_height / 2.0f; | |
1017 float img_right = img_left + desired_width; | |
1018 float img_bot = img_top + desired_height; | |
1019 nk_layout_space_begin(context, NK_STATIC, avail_height, INT_MAX); | |
1020 nk_layout_space_push(context, nk_rect(img_left, img_top, desired_width, desired_height)); | |
1021 nk_image(context, controller_image->ui); | |
1022 | |
1023 float bind_box_width = (render_width() - img_right) * 0.8f; | |
1024 if (bind_box_width < MIN_BIND_BOX_WIDTH) { | |
1025 bind_box_width = render_width() - img_right; | |
1026 if (bind_box_width > MIN_BIND_BOX_WIDTH) { | |
1027 bind_box_width = MIN_BIND_BOX_WIDTH; | |
1028 } | |
1029 } else if (bind_box_width > MAX_BIND_BOX_WIDTH) { | |
1030 bind_box_width = MAX_BIND_BOX_WIDTH; | |
1031 } | |
1032 float bind_box_left; | |
1033 if (bind_box_width >= (render_width() - img_right)) { | |
1034 bind_box_left = img_right; | |
1035 } else { | |
1036 bind_box_left = img_right + (render_width() - img_right) / 2.0f - bind_box_width / 2.0f; | |
1037 } | |
1038 | |
1039 if (selected_controller_info.variant == VARIANT_NORMAL) { | |
1040 binding_box(context, bindings, "Action Buttons", bind_box_left, img_top, bind_box_width, 4, (int[]){ | |
1041 SDL_CONTROLLER_BUTTON_A, | |
1042 SDL_CONTROLLER_BUTTON_B, | |
1043 SDL_CONTROLLER_BUTTON_X, | |
1044 SDL_CONTROLLER_BUTTON_Y | |
1045 }); | |
1046 } else { | |
1047 binding_box(context, bindings, "Action Buttons", bind_box_left, img_top, bind_box_width, 6, (int[]){ | |
1048 SDL_CONTROLLER_BUTTON_A, | |
1049 SDL_CONTROLLER_BUTTON_B, | |
1050 selected_controller_info.variant == VARIANT_6B_RIGHT ? AXIS | SDL_CONTROLLER_AXIS_TRIGGERRIGHT : SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, | |
1051 SDL_CONTROLLER_BUTTON_X, | |
1052 SDL_CONTROLLER_BUTTON_Y, | |
1053 selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_RIGHTSHOULDER : SDL_CONTROLLER_BUTTON_LEFTSHOULDER, | |
1054 }); | |
1055 } | |
1056 | |
1057 binding_box(context, bindings, "Right Shoulder", bind_box_left, font->height/2, bind_box_width, | |
1058 selected_controller_info.variant == VARIANT_6B_BUMPERS ? 1 : 2, | |
1059 (int[]){ | |
1060 selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_LEFTSHOULDER : SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, | |
1061 AXIS | SDL_CONTROLLER_AXIS_TRIGGERLEFT | |
1062 }); | |
1063 | |
1064 binding_box(context, bindings, "Misc Buttons", (render_width() - bind_box_width) / 2, font->height/2, bind_box_width, 3, (int[]){ | |
1065 SDL_CONTROLLER_BUTTON_BACK, | |
1066 SDL_CONTROLLER_BUTTON_GUIDE, | |
1067 SDL_CONTROLLER_BUTTON_START | |
1068 }); | |
1069 | |
1070 if (selected_controller_info.variant == VARIANT_NORMAL) | |
1071 { | |
1072 binding_box(context, bindings, "Right Stick", img_right - desired_width/3, img_bot, bind_box_width, 5, (int[]){ | |
1073 RIGHTSTICK | UP, | |
1074 RIGHTSTICK | DOWN, | |
1075 RIGHTSTICK | LEFT, | |
1076 RIGHTSTICK | RIGHT, | |
1077 SDL_CONTROLLER_BUTTON_RIGHTSTICK | |
1078 }); | |
1079 } | |
1080 | |
1081 bind_box_left -= img_right; | |
1082 float dpad_left, dpad_top; | |
1083 if (selected_controller_info.variant == VARIANT_NORMAL) | |
1084 { | |
1085 binding_box(context, bindings, "Left Stick", bind_box_left, img_top, bind_box_width, 5, (int[]){ | |
1086 LEFTSTICK | UP, | |
1087 LEFTSTICK | DOWN, | |
1088 LEFTSTICK | LEFT, | |
1089 LEFTSTICK | RIGHT, | |
1090 SDL_CONTROLLER_BUTTON_LEFTSTICK | |
1091 }); | |
1092 dpad_left = img_left - desired_width/6; | |
1093 dpad_top = img_bot + font->height * 1.5; | |
1094 } else { | |
1095 dpad_left = bind_box_left; | |
1096 dpad_top = img_top; | |
1097 } | |
1098 | |
1099 binding_box(context, bindings, "Left Shoulder", bind_box_left, font->height/2, bind_box_width, | |
1100 selected_controller_info.variant == VARIANT_6B_BUMPERS ? 1 : 2, | |
1101 (int[]){ | |
1102 selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_LEFTSTICK : SDL_CONTROLLER_BUTTON_LEFTSHOULDER, | |
1103 SDL_CONTROLLER_BUTTON_RIGHTSTICK | |
1104 }); | |
1105 | |
1106 binding_box(context, bindings, "D-pad", dpad_left, dpad_top, bind_box_width, 4, (int[]){ | |
1107 SDL_CONTROLLER_BUTTON_DPAD_UP, | |
1108 SDL_CONTROLLER_BUTTON_DPAD_DOWN, | |
1109 SDL_CONTROLLER_BUTTON_DPAD_LEFT, | |
1110 SDL_CONTROLLER_BUTTON_DPAD_RIGHT | |
1111 }); | |
1112 | |
1113 nk_layout_space_end(context); | |
1114 | |
1115 def_font->handle.height = orig_height; | |
1116 nk_layout_row_static(context, orig_height + 4, (render_width() - 2*orig_height) / 4, 1); | |
1117 if (nk_button_label(context, "Back")) { | |
1118 pop_view(); | |
1119 if (controller_binding_changed) { | |
1120 push_view(view_select_binding_dest); | |
1121 } | |
1122 } | |
1123 nk_end(context); | |
1124 } | |
1125 } | |
1126 | |
1127 static int current_button; | |
1128 static int current_axis; | |
1129 static int button_pressed, last_button; | |
1130 static int hat_moved, hat_value, last_hat, last_hat_value; | |
1131 static int axis_moved, axis_value, last_axis; | |
1132 static char *mapping_string; | |
1133 static size_t mapping_pos; | |
1134 | |
1135 static void start_mapping(void) | |
1136 { | |
1137 const char *name; | |
1138 mapping_string[mapping_pos++] = ','; | |
1139 if (current_button != SDL_CONTROLLER_BUTTON_MAX) { | |
1140 name = SDL_GameControllerGetStringForButton(current_button); | |
1141 } else { | |
1142 name = SDL_GameControllerGetStringForAxis(current_axis); | |
1143 } | |
1144 size_t namesz = strlen(name); | |
1145 memcpy(mapping_string + mapping_pos, name, namesz); | |
1146 mapping_pos += namesz; | |
1147 mapping_string[mapping_pos++] = ':'; | |
1148 } | |
1149 | |
1150 #define QUIET_FRAMES 9 | |
1151 static void view_controller_mappings(struct nk_context *context) | |
1152 { | |
1153 char buffer[512]; | |
1154 static int quiet, button_a = -1, button_a_axis = -1; | |
1155 uint8_t added_mapping = 0; | |
1156 if (nk_begin(context, "Controllers", nk_rect(0, 0, render_width(), render_height()), NK_WINDOW_NO_SCROLLBAR)) { | |
1157 | |
1158 nk_layout_space_begin(context, NK_STATIC, render_height() - context->style.font->height, 3); | |
1159 | |
1160 if (current_button < SDL_CONTROLLER_BUTTON_MAX) { | |
1161 snprintf(buffer, sizeof(buffer), "Press Button %s", get_button_label(&selected_controller_info, current_button)); | |
1162 } else { | |
1163 snprintf(buffer, sizeof(buffer), "Move Axis %s", get_axis_label(&selected_controller_info, current_axis)); | |
1164 } | |
1165 | |
1166 float height = context->style.font->height * 1.25; | |
1167 float top = render_height()/2 - 1.5 * height; | |
1168 float width = render_width() - context->style.font->height; | |
1169 | |
1170 nk_layout_space_push(context, nk_rect(0, top, width, height)); | |
1171 nk_label(context, buffer, NK_TEXT_CENTERED); | |
1172 if (current_button > SDL_CONTROLLER_BUTTON_B) { | |
1173 nk_layout_space_push(context, nk_rect(0, top + height, width, height)); | |
1174 nk_label(context, "OR", NK_TEXT_CENTERED); | |
1175 | |
1176 nk_layout_space_push(context, nk_rect(0, top + 2.0 * height, width, height)); | |
1177 snprintf(buffer, sizeof(buffer), "Press Button %s to skip", get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_A)); | |
1178 nk_label(context, buffer, NK_TEXT_CENTERED); | |
1179 } | |
1180 | |
1181 nk_layout_space_end(context); | |
1182 if (quiet) { | |
1183 --quiet; | |
1184 } else { | |
1185 if (button_pressed >= 0 && button_pressed != last_button) { | |
1186 if (current_button <= SDL_CONTROLLER_BUTTON_B || button_pressed != button_a) { | |
1187 start_mapping(); | |
1188 mapping_string[mapping_pos++] = 'b'; | |
1189 if (button_pressed > 9) { | |
1190 mapping_string[mapping_pos++] = '0' + button_pressed / 10; | |
1191 } | |
1192 mapping_string[mapping_pos++] = '0' + button_pressed % 10; | |
1193 last_button = button_pressed; | |
1194 if (current_button == SDL_CONTROLLER_BUTTON_A) { | |
1195 button_a = button_pressed; | |
1196 } | |
1197 } | |
1198 added_mapping = 1; | |
1199 } else if (hat_moved >= 0 && hat_value && (hat_moved != last_hat || hat_value != last_hat_value)) { | |
1200 start_mapping(); | |
1201 mapping_string[mapping_pos++] = 'h'; | |
1202 mapping_string[mapping_pos++] = '0' + hat_moved; | |
1203 mapping_string[mapping_pos++] = '.'; | |
1204 mapping_string[mapping_pos++] = '0' + hat_value; | |
1205 added_mapping = 1; | |
1206 | |
1207 last_hat = hat_moved; | |
1208 last_hat_value = hat_value; | |
1209 } else if (axis_moved >= 0 && abs(axis_value) > 1000 && axis_moved != last_axis) { | |
1210 if (current_button <= SDL_CONTROLLER_BUTTON_B || axis_moved != button_a_axis) { | |
1211 start_mapping(); | |
1212 mapping_string[mapping_pos++] = 'a'; | |
1213 if (axis_moved > 9) { | |
1214 mapping_string[mapping_pos++] = '0' + axis_moved / 10; | |
1215 } | |
1216 mapping_string[mapping_pos++] = '0' + axis_moved % 10; | |
1217 last_axis = axis_moved; | |
1218 } | |
1219 added_mapping = 1; | |
1220 } | |
1221 } | |
1222 | |
1223 if (added_mapping) { | |
1224 quiet = QUIET_FRAMES; | |
1225 if (current_button < SDL_CONTROLLER_BUTTON_MAX) { | |
1226 current_button++; | |
1227 if (current_button == SDL_CONTROLLER_BUTTON_MAX) { | |
1228 current_axis = 0; | |
1229 } | |
1230 } else { | |
1231 current_axis++; | |
1232 if (current_axis == SDL_CONTROLLER_AXIS_MAX) { | |
1233 button_a = -1; | |
1234 button_a_axis = -1; | |
1235 mapping_string[mapping_pos] = 0; | |
1236 save_controller_mapping(selected_controller, mapping_string); | |
1237 free(mapping_string); | |
1238 pop_view(); | |
1239 push_view(view_controller_bindings); | |
1240 controller_binding_changed = 0; | |
1241 } | |
1242 } | |
1243 } | |
1244 button_pressed = -1; | |
1245 hat_moved = -1; | |
1246 axis_moved = -1; | |
1247 nk_end(context); | |
1248 } | |
1249 } | |
1250 | |
1251 static void view_controller_variant(struct nk_context *context) | |
1252 { | |
1253 uint8_t selected = 0; | |
1254 if (nk_begin(context, "Controller Type", nk_rect(0, 0, render_width(), render_height()), 0)) { | |
1255 nk_layout_row_static(context, context->style.font->height*1.25, render_width() - context->style.font->height * 2, 1); | |
1256 nk_label(context, "", NK_TEXT_CENTERED); | |
1257 nk_label(context, "Select the layout that", NK_TEXT_CENTERED); | |
1258 nk_label(context, "best matches your controller", NK_TEXT_CENTERED); | |
1259 nk_label(context, "", NK_TEXT_CENTERED); | |
1260 if (nk_button_label(context, "4 face buttons")) { | |
1261 selected_controller_info.variant = VARIANT_NORMAL; | |
1262 selected = 1; | |
1263 } | |
1264 char buffer[512]; | |
1265 snprintf(buffer, sizeof(buffer), "6 face buttons including %s and %s", | |
1266 get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), | |
1267 get_axis_label(&selected_controller_info, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) | |
1268 ); | |
1269 if (nk_button_label(context, buffer)) { | |
1270 selected_controller_info.variant = VARIANT_6B_RIGHT; | |
1271 selected = 1; | |
1272 } | |
1273 snprintf(buffer, sizeof(buffer), "6 face buttons including %s and %s", | |
1274 get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_LEFTSHOULDER), | |
1275 get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) | |
1276 ); | |
1277 if (nk_button_label(context, buffer)) { | |
1278 selected_controller_info.variant = VARIANT_6B_BUMPERS; | |
1279 selected = 1; | |
1280 } | |
1281 nk_end(context); | |
1282 } | |
1283 if (selected) { | |
1284 save_controller_info(selected_controller, &selected_controller_info); | |
1285 pop_view(); | |
1286 SDL_GameController *controller = render_get_controller(selected_controller); | |
1287 if (controller) { | |
1288 push_view(view_controller_bindings); | |
1289 controller_binding_changed = 0; | |
1290 SDL_GameControllerClose(controller); | |
1291 } else { | |
1292 current_button = SDL_CONTROLLER_BUTTON_A; | |
1293 button_pressed = -1; | |
1294 last_button = -1; | |
1295 last_hat = -1; | |
1296 axis_moved = -1; | |
1297 last_axis = -1; | |
1298 SDL_Joystick *joy = render_get_joystick(selected_controller); | |
1299 const char *name = SDL_JoystickName(joy); | |
1300 size_t namesz = strlen(name); | |
1301 mapping_string = malloc(512 + namesz); | |
1302 for (mapping_pos = 0; mapping_pos < namesz; mapping_pos++) | |
1303 { | |
1304 char c = name[mapping_pos]; | |
1305 if (c == ',' || c == '\n' || c == '\r') { | |
1306 c = ' '; | |
1307 } | |
1308 mapping_string[mapping_pos] = c; | |
1309 } | |
1310 | |
1311 push_view(view_controller_mappings); | |
1312 } | |
1313 } | |
1314 } | |
1315 | |
1316 static void controller_type_group(struct nk_context *context, char *name, int type_id, int first_subtype_id, const char **types, uint32_t num_types) | |
1317 { | |
1318 nk_layout_row_static(context, (context->style.font->height + 3) * num_types + context->style.font->height, render_width() - 80, 1); | |
1319 if (nk_group_begin(context, name, NK_WINDOW_TITLE)) { | |
1320 nk_layout_row_static(context, context->style.font->height, render_width()/2 - 80, 2); | |
1321 for (int i = 0; i < num_types; i++) | |
1322 { | |
1323 if (nk_button_label(context, types[i])) { | |
1324 selected_controller_info.type = type_id; | |
1325 selected_controller_info.subtype = first_subtype_id + i; | |
1326 pop_view(); | |
1327 push_view(view_controller_variant); | |
1328 } | |
1329 } | |
1330 nk_group_end(context); | |
1331 } | |
1332 } | |
1333 | |
1334 void view_controller_type(struct nk_context *context) | |
1335 { | |
1336 if (nk_begin(context, "Controller Type", nk_rect(0, 0, render_width(), render_height()), 0)) { | |
1337 controller_type_group(context, "Xbox", TYPE_XBOX, SUBTYPE_XBOX, (const char *[]){ | |
1338 "Original", "Xbox 360", "Xbox One" | |
1339 }, 3); | |
1340 controller_type_group(context, "Playstation", TYPE_PSX, SUBTYPE_PS3, (const char *[]){ | |
1341 "PS3", "PS4" | |
1342 }, 2); | |
1343 controller_type_group(context, "Sega", TYPE_SEGA, SUBTYPE_GENESIS, (const char *[]){ | |
1344 "Genesis", "Saturn" | |
1345 }, 2); | |
1346 controller_type_group(context, "Nintendo", TYPE_NINTENDO, SUBTYPE_WIIU, (const char *[]){ | |
1347 "WiiU", "Switch" | |
1348 }, 2); | |
1349 nk_end(context); | |
1350 } | |
1351 } | |
1352 | |
1353 void view_controllers(struct nk_context *context) | |
1354 { | |
1355 if (nk_begin(context, "Controllers", nk_rect(0, 0, render_width(), render_height()), NK_WINDOW_NO_SCROLLBAR)) { | |
1356 int height = (render_width() - 2*context->style.font->height) / MAX_JOYSTICKS; | |
1357 for (int i = 0; i < MAX_JOYSTICKS; i++) | |
1358 { | |
1359 SDL_Joystick *joy = render_get_joystick(i); | |
1360 if (joy) { | |
1361 controller_info info = get_controller_info(i); | |
1362 ui_image *controller_image = select_best_image(&info); | |
1363 int image_width = height * controller_image->width / controller_image->height; | |
1364 nk_layout_row_begin(context, NK_STATIC, height, 2); | |
1365 nk_layout_row_push(context, image_width); | |
1366 if (info.type == TYPE_UNKNOWN || info.type == TYPE_GENERIC_MAPPING) { | |
1367 nk_label(context, "?", NK_TEXT_CENTERED); | |
1368 } else { | |
1369 nk_image(context, controller_image->ui); | |
1370 } | |
1371 nk_layout_row_push(context, render_width() - image_width - 2 * context->style.font->height); | |
1372 if (nk_button_label(context, info.name)) { | |
1373 selected_controller = i; | |
1374 selected_controller_info = info; | |
1375 if (info.type == TYPE_UNKNOWN || info.type == TYPE_GENERIC_MAPPING) { | |
1376 push_view(view_controller_type); | |
1377 } else { | |
1378 push_view(view_controller_bindings); | |
1379 controller_binding_changed = 0; | |
1380 } | |
1381 | |
1382 } | |
1383 nk_layout_row_end(context); | |
1384 } | |
1385 } | |
1386 nk_layout_row_static(context, context->style.font->height, (render_width() - 2 * context->style.font->height) / 2, 2); | |
1387 nk_label(context, "", NK_TEXT_LEFT); | |
1388 if (nk_button_label(context, "Back")) { | |
1389 pop_view(); | |
1390 } | |
1391 nk_end(context); | |
1392 } | |
1393 } | |
1394 | |
1395 void settings_toggle(struct nk_context *context, char *label, char *path, uint8_t def) | |
1396 { | |
1397 uint8_t curval = !strcmp("on", tern_find_path_default(config, path, (tern_val){.ptrval = def ? "on": "off"}, TVAL_PTR).ptrval); | |
1398 nk_label(context, label, NK_TEXT_LEFT); | |
1399 uint8_t newval = nk_check_label(context, "", curval); | |
1400 if (newval != curval) { | |
1401 config_dirty = 1; | |
1402 config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(newval ? "on" : "off")}, TVAL_PTR); | |
1403 } | |
1404 } | |
1405 | |
1406 void settings_int_input(struct nk_context *context, char *label, char *path, char *def) | |
1407 { | |
1408 char buffer[12]; | |
1409 nk_label(context, label, NK_TEXT_LEFT); | |
1410 uint32_t curval; | |
1411 char *curstr = tern_find_path_default(config, path, (tern_val){.ptrval = def}, TVAL_PTR).ptrval; | |
1412 uint32_t len = strlen(curstr); | |
1413 if (len > 11) { | |
1414 len = 11; | |
1415 } | |
1416 memcpy(buffer, curstr, len); | |
1417 memset(buffer+len, 0, sizeof(buffer)-len); | |
1418 nk_edit_string(context, NK_EDIT_SIMPLE, buffer, &len, sizeof(buffer)-1, nk_filter_decimal); | |
1419 buffer[len] = 0; | |
1420 if (strcmp(buffer, curstr)) { | |
1421 config_dirty = 1; | |
1422 config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(buffer)}, TVAL_PTR); | |
1423 } | |
1424 } | |
1425 | |
1426 void settings_int_property(struct nk_context *context, char *label, char *name, char *path, int def, int min, int max) | |
1427 { | |
1428 char *curstr = tern_find_path(config, path, TVAL_PTR).ptrval; | |
1429 int curval = curstr ? atoi(curstr) : def; | |
1430 nk_label(context, label, NK_TEXT_LEFT); | |
1431 int val = curval; | |
1432 nk_property_int(context, name, min, &val, max, 1, 1.0f); | |
1433 if (val != curval) { | |
1434 char buffer[12]; | |
1435 sprintf(buffer, "%d", val); | |
1436 config_dirty = 1; | |
1437 config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(buffer)}, TVAL_PTR); | |
1438 } | |
1439 } | |
1440 | |
1441 typedef struct { | |
1442 char *fragment; | |
1443 char *vertex; | |
1444 } shader_prog; | |
1445 | |
1446 shader_prog *get_shader_progs(dir_entry *entries, size_t num_entries, shader_prog *progs, uint32_t *num_existing, uint32_t *storage) | |
1447 { | |
1448 uint32_t num_progs = *num_existing; | |
1449 uint32_t prog_storage = *storage; | |
1450 uint32_t starting = num_progs; | |
1451 | |
1452 for (uint32_t i = 0; i < num_entries; i++) { | |
1453 if (entries[i].is_dir) { | |
1454 continue; | |
1455 } | |
1456 char *no_ext = basename_no_extension(entries[i].name); | |
1457 uint32_t len = strlen(no_ext); | |
1458 if (no_ext[len-1] == 'f' && no_ext[len-2] == '.') { | |
1459 uint8_t dupe = 0;; | |
1460 for (uint32_t j = 0; j < starting; j++) { | |
1461 if (!strcmp(entries[i].name, progs[j].fragment)) { | |
1462 dupe = 1; | |
1463 break; | |
1464 } | |
1465 } | |
1466 if (!dupe) { | |
1467 if (num_progs == prog_storage) { | |
1468 prog_storage = prog_storage ? prog_storage*2 : 4; | |
1469 progs = realloc(progs, sizeof(progs) * prog_storage); | |
1470 } | |
1471 progs[num_progs].vertex = NULL; | |
1472 progs[num_progs++].fragment = strdup(entries[i].name); | |
1473 } | |
1474 } | |
1475 free(no_ext); | |
1476 } | |
1477 | |
1478 for (uint32_t i = 0; i < num_entries; i++) { | |
1479 if (entries[i].is_dir) { | |
1480 continue; | |
1481 } | |
1482 char *no_ext = basename_no_extension(entries[i].name); | |
1483 uint32_t len = strlen(no_ext); | |
1484 if (no_ext[len-1] == 'v' && no_ext[len-2] == '.') { | |
1485 for (uint32_t j = 0; j < num_progs; j++) { | |
1486 if (!strncmp(no_ext, progs[j].fragment, len-1) && progs[j].fragment[len-1] == 'f' && progs[j].fragment[len] == '.') { | |
1487 progs[j].vertex = strdup(entries[i].name); | |
1488 } | |
1489 } | |
1490 } | |
1491 free(no_ext); | |
1492 } | |
1493 free_dir_list(entries, num_entries); | |
1494 *num_existing = num_progs; | |
1495 *storage = prog_storage; | |
1496 return progs; | |
1497 } | |
1498 | |
1499 shader_prog *get_shader_list(uint32_t *num_out) | |
1500 { | |
1501 char *shader_dir = path_append(get_config_dir(), "shaders"); | |
1502 size_t num_entries; | |
1503 dir_entry *entries = get_dir_list(shader_dir, &num_entries); | |
1504 free(shader_dir); | |
1505 shader_prog *progs; | |
1506 uint32_t num_progs = 0, prog_storage; | |
1507 if (num_entries) { | |
1508 progs = calloc(num_entries, sizeof(shader_prog)); | |
1509 prog_storage = num_entries; | |
1510 progs = get_shader_progs(entries, num_entries, progs, &num_progs, &prog_storage); | |
1511 } else { | |
1512 progs = NULL; | |
1513 prog_storage = 0; | |
1514 } | |
1515 shader_dir = path_append(get_exe_dir(), "shaders"); | |
1516 entries = get_dir_list(shader_dir, &num_entries); | |
1517 progs = get_shader_progs(entries, num_entries, progs, &num_progs, &prog_storage); | |
1518 *num_out = num_progs; | |
1519 return progs; | |
1520 } | |
1521 | |
1522 int32_t find_match(const char **options, uint32_t num_options, char *path, char *def) | |
1523 { | |
1524 char *setting = tern_find_path_default(config, path, (tern_val){.ptrval = def}, TVAL_PTR).ptrval; | |
1525 int32_t selected = -1; | |
1526 for (uint32_t i = 0; i < num_options; i++) | |
1527 { | |
1528 if (!strcmp(setting, options[i])) { | |
1529 selected = i; | |
1530 break; | |
1531 } | |
1532 } | |
1533 if (selected == -1) { | |
1534 for (uint32_t i = 0; i < num_options; i++) | |
1535 { | |
1536 if (!strcmp(def, options[i])) { | |
1537 selected = i; | |
1538 break; | |
1539 } | |
1540 } | |
1541 } | |
1542 return selected; | |
1543 } | |
1544 | |
1545 int32_t settings_dropdown_ex(struct nk_context *context, char *label, const char **options, const char **opt_display, uint32_t num_options, int32_t current, char *path) | |
1546 { | |
1547 nk_label(context, label, NK_TEXT_LEFT); | |
1548 int32_t next = nk_combo(context, opt_display, num_options, current, 30, nk_vec2(300, 300)); | |
1549 if (next != current) { | |
1550 config_dirty = 1; | |
1551 config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(options[next])}, TVAL_PTR); | |
1552 } | |
1553 return next; | |
1554 } | |
1555 | |
1556 int32_t settings_dropdown(struct nk_context *context, char *label, const char **options, uint32_t num_options, int32_t current, char *path) | |
1557 { | |
1558 return settings_dropdown_ex(context, label, options, options, num_options, current, path); | |
1559 } | |
1560 | |
1561 void view_video_settings(struct nk_context *context) | |
1562 { | |
1563 const char *vsync_opts[] = {"on", "off", "tear"}; | |
1564 const char *vsync_opt_names[] = { | |
1565 "On", | |
1566 "Off", | |
1567 "On, tear if late" | |
1568 }; | |
1569 const uint32_t num_vsync_opts = sizeof(vsync_opts)/sizeof(*vsync_opts); | |
1570 static shader_prog *progs; | |
1571 static char **prog_names; | |
1572 static uint32_t num_progs; | |
1573 static uint32_t selected_prog; | |
1574 static int32_t selected_vsync = -1; | |
1575 if (selected_vsync < 0) { | |
1576 selected_vsync = find_match(vsync_opts, num_vsync_opts, "video\0vsync\0", "off"); | |
1577 } | |
1578 if(!progs) { | |
1579 progs = get_shader_list(&num_progs); | |
1580 prog_names = calloc(num_progs, sizeof(char*)); | |
1581 for (uint32_t i = 0; i < num_progs; i++) | |
1582 { | |
1583 prog_names[i] = basename_no_extension(progs[i].fragment);; | |
1584 uint32_t len = strlen(prog_names[i]); | |
1585 if (len > 2) { | |
1586 prog_names[i][len-2] = 0; | |
1587 } | |
1588 if (!progs[i].vertex) { | |
1589 progs[i].vertex = strdup("default.v.glsl"); | |
1590 } | |
1591 if (!strcmp( | |
1592 progs[i].fragment, | |
1593 tern_find_path_default(config, "video\0fragment_shader\0", (tern_val){.ptrval = "default.f.glsl"}, TVAL_PTR).ptrval | |
1594 )) { | |
1595 selected_prog = i; | |
1596 } | |
1597 } | |
1598 } | |
1599 uint32_t width = render_width(); | |
1600 uint32_t height = render_height(); | |
1601 uint32_t desired_width = context->style.font->height * 10; | |
1602 if (desired_width > width) { | |
1603 desired_width = width; | |
1604 } | |
1605 if (nk_begin(context, "Video Settings", nk_rect(0, 0, width, height), 0)) { | |
1606 nk_layout_row_static(context, context->style.font->height, desired_width, 2); | |
1607 settings_toggle(context, "Fullscreen", "video\0fullscreen\0", 0); | |
1608 settings_toggle(context, "Open GL", "video\0gl\0", 1); | |
1609 settings_toggle(context, "Scanlines", "video\0scanlines\0", 0); | |
1610 selected_vsync = settings_dropdown_ex(context, "VSync", vsync_opts, vsync_opt_names, num_vsync_opts, selected_vsync, "video\0vsync\0"); | |
1611 settings_int_input(context, "Windowed Width", "video\0width\0", "640"); | |
1612 nk_label(context, "Shader", NK_TEXT_LEFT); | |
1613 uint32_t next_selected = nk_combo(context, (const char **)prog_names, num_progs, selected_prog, context->style.font->height, nk_vec2(desired_width, desired_width)); | |
1614 if (next_selected != selected_prog) { | |
1615 selected_prog = next_selected; | |
1616 config_dirty = 1; | |
1617 config = tern_insert_path(config, "video\0fragment_shader\0", (tern_val){.ptrval = strdup(progs[next_selected].fragment)}, TVAL_PTR); | |
1618 config = tern_insert_path(config, "video\0vertex_shader\0", (tern_val){.ptrval = strdup(progs[next_selected].vertex)}, TVAL_PTR); | |
1619 } | |
1620 settings_int_property(context, "NTSC Overscan", "Top", "video\0ntsc\0overscan\0top\0", 2, 0, 32); | |
1621 settings_int_property(context, "", "Bottom", "video\0ntsc\0overscan\0bottom\0", 17, 0, 32); | |
1622 settings_int_property(context, "", "Left", "video\0ntsc\0overscan\0left\0", 13, 0, 32); | |
1623 settings_int_property(context, "", "Right", "video\0ntsc\0overscan\0right\0", 14, 0, 32); | |
1624 settings_int_property(context, "PAL Overscan", "Top", "video\0pal\0overscan\0top\0", 2, 0, 32); | |
1625 settings_int_property(context, "", "Bottom", "video\0pal\0overscan\0bottom\0", 17, 0, 32); | |
1626 settings_int_property(context, "", "Left", "video\0pal\0overscan\0left\0", 13, 0, 32); | |
1627 settings_int_property(context, "", "Right", "video\0pal\0overscan\0right\0", 14, 0, 32); | |
1628 | |
1629 if (nk_button_label(context, "Back")) { | |
1630 pop_view(); | |
1631 } | |
1632 nk_end(context); | |
1633 } | |
1634 } | |
1635 | |
1636 void view_audio_settings(struct nk_context *context) | |
1637 { | |
1638 const char *rates[] = { | |
1639 "192000", | |
1640 "96000", | |
1641 "48000", | |
1642 "44100", | |
1643 "22050" | |
1644 }; | |
1645 const char *sizes[] = { | |
1646 "1024", | |
1647 "512", | |
1648 "256", | |
1649 "128", | |
1650 "64" | |
1651 }; | |
1652 const uint32_t num_rates = sizeof(rates)/sizeof(*rates); | |
1653 const uint32_t num_sizes = sizeof(sizes)/sizeof(*sizes); | |
1654 static int32_t selected_rate = -1; | |
1655 static int32_t selected_size = -1; | |
1656 if (selected_rate < 0 || selected_size < 0) { | |
1657 selected_rate = find_match(rates, num_rates, "autio\0rate\0", "48000"); | |
1658 selected_size = find_match(sizes, num_sizes, "audio\0buffer\0", "512"); | |
1659 } | |
1660 uint32_t width = render_width(); | |
1661 uint32_t height = render_height(); | |
1662 uint32_t desired_width = context->style.font->height * 10; | |
1663 if (desired_width > width) { | |
1664 desired_width = width; | |
1665 } | |
1666 if (nk_begin(context, "Audio Settings", nk_rect(0, 0, width, height), 0)) { | |
1667 nk_layout_row_static(context, context->style.font->height , desired_width, 2); | |
1668 selected_rate = settings_dropdown(context, "Rate in Hz", rates, num_rates, selected_rate, "audio\0rate\0"); | |
1669 selected_size = settings_dropdown(context, "Buffer Samples", sizes, num_sizes, selected_size, "audio\0buffer\0"); | |
1670 settings_int_input(context, "Lowpass Cutoff Hz", "audio\0lowpass_cutoff\0", "3390"); | |
1671 if (nk_button_label(context, "Back")) { | |
1672 pop_view(); | |
1673 } | |
1674 nk_end(context); | |
1675 } | |
1676 } | |
1677 void view_system_settings(struct nk_context *context) | |
1678 { | |
1679 const char *sync_opts[] = { | |
1680 "video", | |
1681 "audio" | |
1682 }; | |
1683 const uint32_t num_sync_opts = sizeof(sync_opts)/sizeof(*sync_opts); | |
1684 static int32_t selected_sync = -1; | |
1685 if (selected_sync < 0) { | |
1686 selected_sync = find_match(sync_opts, num_sync_opts, "system\0sync_source\0", "video"); | |
1687 } | |
1688 const char *regions[] = { | |
1689 "J - Japan", | |
1690 "U - Americas", | |
1691 "E - Europe" | |
1692 }; | |
1693 const char *region_codes[] = {"J", "U", "E"}; | |
1694 const uint32_t num_regions = sizeof(regions)/sizeof(*regions); | |
1695 static int32_t selected_region = -1; | |
1696 if (selected_region < 0) { | |
1697 selected_region = find_match(region_codes, num_regions, "system\0default_region\0", "U"); | |
1698 } | |
1699 const char *formats[] = { | |
1700 "native", | |
1701 "gst" | |
1702 }; | |
1703 const uint32_t num_formats = sizeof(formats)/sizeof(*formats); | |
1704 int32_t selected_format = -1; | |
1705 if (selected_format < 0) { | |
1706 selected_format = find_match(formats, num_formats, "ui\0state_format\0", "native"); | |
1707 } | |
1708 const char *ram_inits[] = { | |
1709 "zero", | |
1710 "random" | |
1711 }; | |
1712 const uint32_t num_inits = sizeof(ram_inits)/sizeof(*ram_inits); | |
1713 static int32_t selected_init = -1; | |
1714 if (selected_init < 0) { | |
1715 selected_init = find_match(ram_inits, num_inits, "system\0ram_init\0", "zero"); | |
1716 } | |
1717 const char *io_opts_1[] = { | |
1718 "gamepad2.1", | |
1719 "gamepad3.1", | |
1720 "gamepad6.1", | |
1721 "mouse.1", | |
1722 "saturn keyboard", | |
1723 "xband keyboard" | |
1724 }; | |
1725 const char *io_opts_2[] = { | |
1726 "gamepad2.2", | |
1727 "gamepad3.2", | |
1728 "gamepad6.2", | |
1729 "mouse.1", | |
1730 "saturn keyboard", | |
1731 "xband keyboard" | |
1732 }; | |
1733 static int32_t selected_io_1 = -1; | |
1734 static int32_t selected_io_2 = -1; | |
1735 const uint32_t num_io = sizeof(io_opts_1)/sizeof(*io_opts_1); | |
1736 if (selected_io_1 < 0 || selected_io_2 < 0) { | |
1737 selected_io_1 = find_match(io_opts_1, num_io, "io\0devices\0""1\0", "gamepad6.1"); | |
1738 selected_io_2 = find_match(io_opts_2, num_io, "io\0devices\0""2\0", "gamepad6.2"); | |
1739 } | |
1740 | |
1741 uint32_t width = render_width(); | |
1742 uint32_t height = render_height(); | |
1743 uint32_t desired_width = context->style.font->height * 10; | |
1744 if (nk_begin(context, "System Settings", nk_rect(0, 0, width, height), 0)) { | |
1745 nk_layout_row_static(context, context->style.font->height, desired_width, 2); | |
1746 selected_sync = settings_dropdown(context, "Sync Source", sync_opts, num_sync_opts, selected_sync, "system\0sync_source\0"); | |
1747 settings_int_property(context, "68000 Clock Divider", "", "clocks\0m68k_divider\0", 7, 1, 53); | |
1748 settings_toggle(context, "Remember ROM Path", "ui\0remember_path\0", 1); | |
1749 selected_region = settings_dropdown_ex(context, "Default Region", region_codes, regions, num_regions, selected_region, "system\0default_region\0"); | |
1750 selected_format = settings_dropdown(context, "Save State Format", formats, num_formats, selected_format, "ui\0state_format\0"); | |
1751 selected_init = settings_dropdown(context, "Initial RAM Value", ram_inits, num_inits, selected_init, "system\0ram_init\0"); | |
1752 selected_io_1 = settings_dropdown_ex(context, "IO Port 1 Device", io_opts_1, device_type_names, num_io, selected_io_1, "io\0devices\0""1\0"); | |
1753 selected_io_2 = settings_dropdown_ex(context, "IO Port 2 Device", io_opts_2, device_type_names, num_io, selected_io_2, "io\0devices\0""2\0"); | |
1754 if (nk_button_label(context, "Back")) { | |
1755 pop_view(); | |
1756 } | |
1757 nk_end(context); | |
1758 } | |
1759 } | |
1760 | |
1761 void view_back(struct nk_context *context) | |
1762 { | |
1763 pop_view(); | |
1764 pop_view(); | |
1765 current_view(context); | |
1766 } | |
1767 | |
1768 void view_settings(struct nk_context *context) | |
1769 { | |
1770 static menu_item items[] = { | |
1771 {"Key Bindings", view_key_bindings}, | |
1772 {"Controllers", view_controllers}, | |
1773 {"Video", view_video_settings}, | |
1774 {"Audio", view_audio_settings}, | |
1775 {"System", view_system_settings}, | |
1776 {"Back", view_back} | |
1777 }; | |
1778 | |
1779 if (nk_begin(context, "Settings Menu", nk_rect(0, 0, render_width(), render_height()), 0)) { | |
1780 menu(context, sizeof(items)/sizeof(*items), items, NULL); | |
1781 nk_end(context); | |
1782 } | |
1783 } | |
1784 | |
1785 void exit_handler(uint32_t index) | |
1786 { | |
1787 exit(0); | |
1788 } | |
1789 | |
1790 void view_pause(struct nk_context *context) | |
1791 { | |
1792 static menu_item items[] = { | |
1793 {"Resume", view_play}, | |
1794 {"Load ROM", view_load}, | |
1795 {"Lock On", view_lock_on}, | |
1796 {"Save State", view_save_state}, | |
1797 {"Load State", view_load_state}, | |
1798 {"Settings", view_settings}, | |
1799 {"Exit", NULL} | |
1800 }; | |
1801 | |
1802 if (nk_begin(context, "Main Menu", nk_rect(0, 0, render_width(), render_height()), 0)) { | |
1803 menu(context, sizeof(items)/sizeof(*items), items, exit_handler); | |
1804 nk_end(context); | |
1805 } | |
1806 } | |
1807 | |
1808 void view_menu(struct nk_context *context) | |
1809 { | |
1810 static menu_item items[] = { | |
1811 {"Load ROM", view_load}, | |
1812 {"Settings", view_settings}, | |
1813 {"About", view_about}, | |
1814 {"Exit", NULL} | |
1815 }; | |
1816 | |
1817 if (nk_begin(context, "Main Menu", nk_rect(0, 0, render_width(), render_height()), 0)) { | |
1818 menu(context, sizeof(items)/sizeof(*items), items, exit_handler); | |
1819 nk_end(context); | |
1820 } | |
1821 } | |
1822 | |
1823 void blastem_nuklear_render(void) | |
1824 { | |
1825 if (current_view != view_play) { | |
1826 nk_input_end(context); | |
1827 current_view(context); | |
1828 nk_sdl_render(NK_ANTI_ALIASING_ON, 512 * 1024, 128 * 1024); | |
1829 nk_input_begin(context); | |
1830 } | |
1831 } | |
1832 | |
1833 void ui_idle_loop(void) | |
1834 { | |
1835 const uint32_t MIN_UI_DELAY = 15; | |
1836 static uint32_t last; | |
1837 while (current_view != view_play) | |
1838 { | |
1839 uint32_t current = render_elapsed_ms(); | |
1840 if ((current - last) < MIN_UI_DELAY) { | |
1841 render_sleep_ms(MIN_UI_DELAY - (current - last) - 1); | |
1842 } | |
1843 last = current; | |
1844 render_update_display(); | |
1845 } | |
1846 if (config_dirty) { | |
1847 apply_updated_config(); | |
1848 persist_config(config); | |
1849 config_dirty = 0; | |
1850 } | |
1851 } | |
1852 static void handle_event(SDL_Event *event) | |
1853 { | |
1854 if (event->type == SDL_KEYDOWN) { | |
1855 keycode = event->key.keysym.sym; | |
1856 } | |
1857 else if (event->type == SDL_JOYBUTTONDOWN) { | |
1858 button_pressed = event->jbutton.button; | |
1859 } | |
1860 else if (event->type == SDL_JOYHATMOTION) { | |
1861 hat_moved = event->jhat.hat; | |
1862 hat_value = event->jhat.value; | |
1863 } | |
1864 else if (event->type == SDL_JOYAXISMOTION) { | |
1865 if (event->jaxis.axis == axis_moved || abs(event->jaxis.value) > abs(axis_value) || abs(event->jaxis.value) > 1000) { | |
1866 axis_moved = event->jaxis.axis; | |
1867 axis_value = event->jaxis.value; | |
1868 } | |
1869 } | |
1870 nk_sdl_handle_event(event); | |
1871 } | |
1872 | |
1873 static void context_destroyed(void) | |
1874 { | |
1875 nk_sdl_shutdown(); | |
1876 } | |
1877 | |
1878 static struct nk_image load_image_texture(uint32_t *buf, uint32_t width, uint32_t height) | |
1879 { | |
1880 GLuint tex; | |
1881 glGenTextures(1, &tex); | |
1882 glBindTexture(GL_TEXTURE_2D, tex); | |
1883 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
1884 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
1885 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
1886 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
1887 #ifdef USE_GLES | |
1888 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); | |
1889 #else | |
1890 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, buf); | |
1891 #endif | |
1892 return nk_image_id((int)tex); | |
1893 } | |
1894 | |
1895 static void texture_init(void) | |
1896 { | |
1897 struct nk_font_atlas *atlas; | |
1898 nk_sdl_font_stash_begin(&atlas); | |
1899 uint32_t font_size; | |
1900 uint8_t *font = default_font(&font_size); | |
1901 if (!font) { | |
1902 fatal_error("Failed to find default font path\n"); | |
1903 } | |
1904 def_font = nk_font_atlas_add_from_memory(atlas, font, font_size, render_height() / 16, NULL); | |
1905 free(font); | |
1906 nk_sdl_font_stash_end(); | |
1907 nk_style_set_font(context, &def_font->handle); | |
1908 for (uint32_t i = 0; i < num_ui_images; i++) | |
1909 { | |
1910 ui_images[i]->ui = load_image_texture(ui_images[i]->image_data, ui_images[i]->width, ui_images[i]->height); | |
1911 } | |
1912 } | |
1913 | |
1914 static void context_created(void) | |
1915 { | |
1916 context = nk_sdl_init(render_get_window()); | |
1917 texture_init(); | |
1918 } | |
1919 | |
1920 void show_pause_menu(void) | |
1921 { | |
1922 set_content_binding_state(0); | |
1923 context->style.window.background = nk_rgba(0, 0, 0, 128); | |
1924 context->style.window.fixed_background = nk_style_item_color(nk_rgba(0, 0, 0, 128)); | |
1925 current_view = view_pause; | |
1926 current_system->request_exit(current_system); | |
1927 } | |
1928 | |
1929 void show_play_view(void) | |
1930 { | |
1931 set_content_binding_state(1); | |
1932 current_view = view_play; | |
1933 } | |
1934 | |
1935 static uint8_t active; | |
1936 uint8_t is_nuklear_active(void) | |
1937 { | |
1938 return active; | |
1939 } | |
1940 | |
1941 uint8_t is_nuklear_available(void) | |
1942 { | |
1943 if (!render_has_gl()) { | |
1944 //currently no fallback if GL2 unavailable | |
1945 return 0; | |
1946 } | |
1947 char *style = tern_find_path(config, "ui\0style\0", TVAL_PTR).ptrval; | |
1948 if (!style) { | |
1949 return 1; | |
1950 } | |
1951 return strcmp(style, "rom") != 0; | |
1952 } | |
1953 | |
1954 static void persist_config_exit(void) | |
1955 { | |
1956 if (config_dirty) { | |
1957 persist_config(config); | |
1958 } | |
1959 } | |
1960 | |
1961 ui_image *load_ui_image(char *name) | |
1962 { | |
1963 uint32_t buf_size; | |
1964 uint8_t *buf = (uint8_t *)read_bundled_file(name, &buf_size); | |
1965 if (buf) { | |
1966 num_ui_images++; | |
1967 if (num_ui_images > ui_image_storage) { | |
1968 ui_image_storage = (ui_image_storage + 1) * 2; | |
1969 ui_images = realloc(ui_images, ui_image_storage * sizeof(*ui_images)); | |
1970 } | |
1971 ui_image *this_image = ui_images[num_ui_images-1] = calloc(1, sizeof(ui_image)); | |
1972 this_image->image_data = load_png(buf, buf_size, &this_image->width, &this_image->height); | |
1973 #ifdef USE_GLES | |
1974 uint32_t *cur = this_image->image_data; | |
1975 for (int i = 0; i < this_image->width*this_image->height; i++, cur++) | |
1976 { | |
1977 uint32_t pixel = *cur; | |
1978 *cur = (pixel & 0xFF00FF00) | (pixel << 16 & 0xFF0000) | (pixel >> 16 & 0xFF); | |
1979 } | |
1980 #endif | |
1981 free(buf); | |
1982 if (!this_image->image_data) { | |
1983 num_ui_images--; | |
1984 free(this_image); | |
1985 return NULL; | |
1986 } | |
1987 return this_image; | |
1988 } else { | |
1989 return NULL; | |
1990 } | |
1991 } | |
1992 | |
1993 void blastem_nuklear_init(uint8_t file_loaded) | |
1994 { | |
1995 context = nk_sdl_init(render_get_window()); | |
1996 | |
1997 controller_360 = load_ui_image("images/360.png"); | |
1998 controller_ps4 = load_ui_image("images/ps4.png"); | |
1999 controller_ps4_6b = load_ui_image("images/ps4_6b.png"); | |
2000 | |
2001 texture_init(); | |
2002 | |
2003 if (file_loaded) { | |
2004 current_view = view_play; | |
2005 } else { | |
2006 current_view = view_menu; | |
2007 set_content_binding_state(0); | |
2008 } | |
2009 render_set_ui_render_fun(blastem_nuklear_render); | |
2010 render_set_event_handler(handle_event); | |
2011 render_set_gl_context_handlers(context_destroyed, context_created); | |
2012 | |
2013 atexit(persist_config_exit); | |
2014 | |
2015 active = 1; | |
2016 ui_idle_loop(); | |
2017 } |