Mercurial > repos > blastem
diff nuklear_ui/blastem_nuklear.c @ 2053:3414a4423de1 segacd
Merge from default
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 15 Jan 2022 13:15:21 -0800 |
parents | 380bc5d4a2cf |
children | 237068a25523 |
line wrap: on
line diff
--- a/nuklear_ui/blastem_nuklear.c Sat Jan 05 00:58:08 2019 -0800 +++ b/nuklear_ui/blastem_nuklear.c Sat Jan 15 13:15:21 2022 -0800 @@ -1,9 +1,13 @@ #define NK_IMPLEMENTATION #define NK_SDL_GLES2_IMPLEMENTATION +#define NK_RAWFB_IMPLEMENTATION +#define RAWFB_RGBX_8888 #include <stdlib.h> #include <limits.h> +#include <math.h> #include "blastem_nuklear.h" +#include "nuklear_rawfb.h" #include "font.h" #include "../render.h" #include "../render_sdl.h" @@ -18,6 +22,7 @@ #include "../bindings.h" static struct nk_context *context; +static struct rawfb_context *fb_context; typedef struct { @@ -26,7 +31,8 @@ struct nk_image ui; } ui_image; -static ui_image **ui_images, *controller_360, *controller_ps4, *controller_ps4_6b; +static ui_image **ui_images, *controller_360, *controller_ps4, + *controller_ps4_6b, *controller_wiiu, *controller_gen_6b; static uint32_t num_ui_images, ui_image_storage; typedef void (*view_fun)(struct nk_context *); @@ -45,12 +51,14 @@ } previous_views[num_prev++] = current_view; current_view = new_view; + context->input.selected_widget = 0; } static void pop_view() { if (num_prev) { current_view = previous_views[--num_prev]; + context->input.selected_widget = 0; } } @@ -81,6 +89,15 @@ if (entries) { sort_dir_list(entries, num_entries); } + if (!num_entries) { + //get_dir_list can fail if the user doesn't have permission + //for the current folder, make sure they can still navigate up + free_dir_list(entries, num_entries); + entries = calloc(1, sizeof(dir_entry)); + entries[0].name = strdup(".."); + entries[0].is_dir = 1; + num_entries = 1; + } } if (!got_ext_list) { ext_list = get_extension_list(config, &num_exts); @@ -91,7 +108,8 @@ if (nk_begin(context, "Load ROM", nk_rect(0, 0, width, height), 0)) { nk_layout_row_static(context, height - context->style.font->height * 3, width - 60, 1); int32_t old_selected = selected_entry; - if (nk_group_begin(context, "Select ROM", NK_WINDOW_BORDER | NK_WINDOW_TITLE)) { + char *title = alloc_concat("Select ROM: ", current_path); + if (nk_group_begin(context, title, NK_WINDOW_BORDER | NK_WINDOW_TITLE)) { nk_layout_row_static(context, context->style.font->height - 2, width-100, 1); for (int32_t i = 0; i < num_entries; i++) { @@ -111,6 +129,7 @@ } nk_group_end(context); } + free(title); nk_layout_row_static(context, context->style.font->height * 1.75, width > 600 ? 300 : width / 2, 2); if (nk_button_label(context, "Back")) { pop_view(); @@ -160,8 +179,8 @@ void view_about(struct nk_context *context) { const char *lines[] = { - "BlastEm v0.6.1", - "Copyright 2012-2017 Michael Pavone", + "BlastEm v0.6.3-pre", + "Copyright 2012-2019 Michael Pavone", "", "BlastEm is a high performance open source", "(GPLv3) Genesis/Megadrive emulator", @@ -171,6 +190,8 @@ "Nemesis: Documentation and test ROMs", "Charles MacDonald: Documentation", "Eke-Eke: Documentation", + "Sauraen: YM2612/YM2203 Die Analysis", + "Alexey Khokholov: YM3438 Die Analysis", "Bart Trzynadlowski: Documentation", "KanedaFR: Hosting the best Sega forum", "Titan: Awesome demos and documentation", @@ -293,6 +314,7 @@ free_slot_info(slots); slots = NULL; } else if (current_view == view_play) { + clear_view_stack(); set_content_binding_state(1); } } else { @@ -314,6 +336,7 @@ static int32_t keycode; static const char *set_binding; +static uint8_t bind_click_release, click; char *set_label; void binding_group(struct nk_context *context, char *name, const char **binds, const char **bind_names, uint32_t num_binds, tern_node *binding_lookup) { @@ -332,6 +355,7 @@ if (nk_button_label(context, tern_find_ptr_default(binding_lookup, binds[i], "Not Set"))) { set_binding = binds[i]; set_label = strdup(label); + bind_click_release = 0; keycode = 0; } if (label_alloc) { @@ -429,11 +453,11 @@ }; const char *general_binds[] = { "ui.exit", "ui.save_state", "ui.toggle_fullscreen", "ui.soft_reset", "ui.reload", - "ui.screenshot", "ui.sms_pause", "ui.toggle_keyboard_cpatured", "ui.release_mouse" + "ui.screenshot", "ui.vgm_log", "ui.sms_pause", "ui.toggle_keyboard_cpatured", "ui.release_mouse" }; const char *general_names[] = { "Show Menu", "Quick Save", "Toggle Fullscreen", "Soft Reset", "Reload Media", - "Internal Screenshot", "SMS Pause", "Capture Keyboard", "Release Mouse" + "Internal Screenshot", "Toggle VGM Log", "SMS Pause", "Capture Keyboard", "Release Mouse" }; const char *speed_binds[] = { "ui.next_speed", "ui.prev_speed", @@ -483,7 +507,7 @@ nk_layout_row_static(context, 30, width/2-30, 1); nk_label(context, "Press new key for", NK_TEXT_CENTERED); nk_label(context, set_label, NK_TEXT_CENTERED); - if (nk_button_label(context, "Cancel")) { + if (nk_button_label(context, "Cancel") && bind_click_release) { free(set_label); set_binding = set_label = NULL; } else if (keycode) { @@ -517,6 +541,8 @@ } free(set_label); set_binding = set_label = NULL; + } else if (!click) { + bind_click_release = 1; } nk_end(context); } @@ -578,6 +604,7 @@ conf_names = tern_insert_ptr(conf_names, "ui.vdp_debug_pal", "VDP Debug Palette"); conf_names = tern_insert_ptr(conf_names, "ui.enter_debugger", "Enter CPU Debugger"); conf_names = tern_insert_ptr(conf_names, "ui.screenshot", "Take Screenshot"); + conf_names = tern_insert_ptr(conf_names, "ui.vgm_log", "Toggle VGM Log"); conf_names = tern_insert_ptr(conf_names, "ui.exit", "Show Menu"); conf_names = tern_insert_ptr(conf_names, "ui.save_state", "Quick Save"); conf_names = tern_insert_ptr(conf_names, "ui.set_speed.0", "Set Speed 0"); @@ -962,10 +989,16 @@ static ui_image *select_best_image(controller_info *info) { - if (info->variant != VARIANT_NORMAL) { - return controller_ps4_6b; + if (info->variant != VARIANT_NORMAL || info->type == TYPE_SEGA) { + if (info->type == TYPE_PSX) { + return controller_ps4_6b; + } else { + return controller_gen_6b; + } } else if (info->type == TYPE_PSX) { return controller_ps4; + } else if (info->type == TYPE_NINTENDO) { + return controller_wiiu; } else { return controller_360; } @@ -1054,12 +1087,19 @@ }); } - binding_box(context, bindings, "Right Shoulder", bind_box_left, font->height/2, bind_box_width, - selected_controller_info.variant == VARIANT_6B_BUMPERS ? 1 : 2, - (int[]){ - selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_LEFTSHOULDER : SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, - AXIS | SDL_CONTROLLER_AXIS_TRIGGERLEFT - }); + if (selected_controller_info.variant == VARIANT_NORMAL) { + binding_box(context, bindings, "Right Shoulder", bind_box_left, font->height/2, bind_box_width, 2, (int[]){ + SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, + AXIS | SDL_CONTROLLER_AXIS_TRIGGERRIGHT + }); + } else { + binding_box(context, bindings, "Right Shoulder", bind_box_left, font->height/2, bind_box_width, + selected_controller_info.variant == VARIANT_6B_BUMPERS ? 1 : 2, + (int[]){ + selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_LEFTSHOULDER : AXIS | SDL_CONTROLLER_AXIS_TRIGGERRIGHT, + AXIS | SDL_CONTROLLER_AXIS_TRIGGERLEFT + }); + } binding_box(context, bindings, "Misc Buttons", (render_width() - bind_box_width) / 2, font->height/2, bind_box_width, 3, (int[]){ SDL_CONTROLLER_BUTTON_BACK, @@ -1096,12 +1136,19 @@ dpad_top = img_top; } - binding_box(context, bindings, "Left Shoulder", bind_box_left, font->height/2, bind_box_width, - selected_controller_info.variant == VARIANT_6B_BUMPERS ? 1 : 2, - (int[]){ - selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_LEFTSTICK : SDL_CONTROLLER_BUTTON_LEFTSHOULDER, - SDL_CONTROLLER_BUTTON_RIGHTSTICK - }); + if (selected_controller_info.variant == VARIANT_NORMAL) { + binding_box(context, bindings, "Left Shoulder", bind_box_left, font->height/2, bind_box_width, 2, (int[]){ + SDL_CONTROLLER_BUTTON_LEFTSHOULDER, + AXIS | SDL_CONTROLLER_AXIS_TRIGGERLEFT + }); + } else { + binding_box(context, bindings, "Left Shoulder", bind_box_left, font->height/2, bind_box_width, + selected_controller_info.variant == VARIANT_6B_BUMPERS ? 1 : 2, + (int[]){ + selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_LEFTSTICK : AXIS | SDL_CONTROLLER_AXIS_TRIGGERLEFT, + SDL_CONTROLLER_BUTTON_RIGHTSTICK + }); + } binding_box(context, bindings, "D-pad", dpad_left, dpad_top, bind_box_width, 4, (int[]){ SDL_CONTROLLER_BUTTON_DPAD_UP, @@ -1114,7 +1161,7 @@ def_font->handle.height = orig_height; nk_layout_row_static(context, orig_height + 4, (render_width() - 2*orig_height) / 4, 1); - if (nk_button_label(context, "Back")) { + if (nk_button_label(context, controller_binding_changed ? "Save" : "Back")) { pop_view(); if (controller_binding_changed) { push_view(view_select_binding_dest); @@ -1128,7 +1175,7 @@ static int current_axis; static int button_pressed, last_button; static int hat_moved, hat_value, last_hat, last_hat_value; -static int axis_moved, axis_value, last_axis; +static int axis_moved, axis_value, last_axis, last_axis_value; static char *mapping_string; static size_t mapping_pos; @@ -1147,6 +1194,7 @@ mapping_string[mapping_pos++] = ':'; } +static uint8_t initial_controller_config; #define QUIET_FRAMES 9 static void view_controller_mappings(struct nk_context *context) { @@ -1206,26 +1254,39 @@ last_hat = hat_moved; last_hat_value = hat_value; - } else if (axis_moved >= 0 && abs(axis_value) > 1000 && axis_moved != last_axis) { + } else if (axis_moved >= 0 && abs(axis_value) > 1000 && ( + axis_moved != last_axis || ( + axis_value/abs(axis_value) != last_axis_value/abs(axis_value) && current_button >= SDL_CONTROLLER_BUTTON_DPAD_UP + ) + )) { if (current_button <= SDL_CONTROLLER_BUTTON_B || axis_moved != button_a_axis) { start_mapping(); + if (current_button >= SDL_CONTROLLER_BUTTON_DPAD_UP) { + mapping_string[mapping_pos++] = axis_value >= 0 ? '+' : '-'; + } mapping_string[mapping_pos++] = 'a'; if (axis_moved > 9) { mapping_string[mapping_pos++] = '0' + axis_moved / 10; } mapping_string[mapping_pos++] = '0' + axis_moved % 10; last_axis = axis_moved; + last_axis_value = axis_value; } added_mapping = 1; } } - if (added_mapping) { + while (added_mapping) { quiet = QUIET_FRAMES; if (current_button < SDL_CONTROLLER_BUTTON_MAX) { current_button++; if (current_button == SDL_CONTROLLER_BUTTON_MAX) { current_axis = 0; + if (get_axis_label(&selected_controller_info, current_axis)) { + added_mapping = 0; + } + } else if (get_button_label(&selected_controller_info, current_button)) { + added_mapping = 0; } } else { current_axis++; @@ -1236,8 +1297,13 @@ save_controller_mapping(selected_controller, mapping_string); free(mapping_string); pop_view(); - push_view(view_controller_bindings); - controller_binding_changed = 0; + if (initial_controller_config) { + push_view(view_controller_bindings); + controller_binding_changed = 0; + } + added_mapping = 0; + } else if (get_axis_label(&selected_controller_info, current_axis)) { + added_mapping = 0; } } } @@ -1248,6 +1314,31 @@ } } +static void show_mapping_view(void) +{ + current_button = SDL_CONTROLLER_BUTTON_A; + button_pressed = -1; + last_button = -1; + last_hat = -1; + axis_moved = -1; + last_axis = -1; + last_axis_value = 0; + SDL_Joystick *joy = render_get_joystick(selected_controller); + const char *name = SDL_JoystickName(joy); + size_t namesz = strlen(name); + mapping_string = malloc(512 + namesz); + for (mapping_pos = 0; mapping_pos < namesz; mapping_pos++) + { + char c = name[mapping_pos]; + if (c == ',' || c == '\n' || c == '\r') { + c = ' '; + } + mapping_string[mapping_pos] = c; + } + + push_view(view_controller_mappings); +} + static void view_controller_variant(struct nk_context *context) { uint8_t selected = 0; @@ -1257,58 +1348,56 @@ nk_label(context, "Select the layout that", NK_TEXT_CENTERED); nk_label(context, "best matches your controller", NK_TEXT_CENTERED); nk_label(context, "", NK_TEXT_CENTERED); - if (nk_button_label(context, "4 face buttons")) { - selected_controller_info.variant = VARIANT_NORMAL; - selected = 1; - } - char buffer[512]; - snprintf(buffer, sizeof(buffer), "6 face buttons including %s and %s", - get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), - get_axis_label(&selected_controller_info, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) - ); - if (nk_button_label(context, buffer)) { - selected_controller_info.variant = VARIANT_6B_RIGHT; - selected = 1; - } - snprintf(buffer, sizeof(buffer), "6 face buttons including %s and %s", - get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_LEFTSHOULDER), - get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) - ); - if (nk_button_label(context, buffer)) { - selected_controller_info.variant = VARIANT_6B_BUMPERS; - selected = 1; + if (selected_controller_info.subtype == SUBTYPE_GENESIS) { + if (nk_button_label(context, "3 button")) { + selected_controller_info.variant = VARIANT_3BUTTON; + selected = 1; + } + if (nk_button_label(context, "Standard 6 button")) { + selected_controller_info.variant = VARIANT_6B_BUMPERS; + selected = 1; + } + if (nk_button_label(context, "6 button with 2 shoulder buttons")) { + selected_controller_info.variant = VARIANT_8BUTTON; + selected = 1; + } + } else { + if (nk_button_label(context, "4 face buttons")) { + selected_controller_info.variant = VARIANT_NORMAL; + selected = 1; + } + char buffer[512]; + snprintf(buffer, sizeof(buffer), "6 face buttons including %s and %s", + get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), + get_axis_label(&selected_controller_info, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + ); + if (nk_button_label(context, buffer)) { + selected_controller_info.variant = VARIANT_6B_RIGHT; + selected = 1; + } + snprintf(buffer, sizeof(buffer), "6 face buttons including %s and %s", + get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_LEFTSHOULDER), + get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) + ); + if (nk_button_label(context, buffer)) { + selected_controller_info.variant = VARIANT_6B_BUMPERS; + selected = 1; + } } nk_end(context); } if (selected) { save_controller_info(selected_controller, &selected_controller_info); pop_view(); - SDL_GameController *controller = render_get_controller(selected_controller); - if (controller) { - push_view(view_controller_bindings); - controller_binding_changed = 0; - SDL_GameControllerClose(controller); - } else { - current_button = SDL_CONTROLLER_BUTTON_A; - button_pressed = -1; - last_button = -1; - last_hat = -1; - axis_moved = -1; - last_axis = -1; - SDL_Joystick *joy = render_get_joystick(selected_controller); - const char *name = SDL_JoystickName(joy); - size_t namesz = strlen(name); - mapping_string = malloc(512 + namesz); - for (mapping_pos = 0; mapping_pos < namesz; mapping_pos++) - { - char c = name[mapping_pos]; - if (c == ',' || c == '\n' || c == '\r') { - c = ' '; - } - mapping_string[mapping_pos] = c; + if (initial_controller_config) { + SDL_GameController *controller = render_get_controller(selected_controller); + if (controller) { + push_view(view_controller_bindings); + controller_binding_changed = 0; + SDL_GameControllerClose(controller); + } else { + show_mapping_view(); } - - push_view(view_controller_mappings); } } } @@ -1324,7 +1413,22 @@ selected_controller_info.type = type_id; selected_controller_info.subtype = first_subtype_id + i; pop_view(); - push_view(view_controller_variant); + if (selected_controller_info.subtype == SUBTYPE_SATURN) { + selected_controller_info.variant = VARIANT_6B_BUMPERS; + save_controller_info(selected_controller, &selected_controller_info); + if (initial_controller_config) { + SDL_GameController *controller = render_get_controller(selected_controller); + if (controller) { + push_view(view_controller_bindings); + controller_binding_changed = 0; + SDL_GameControllerClose(controller); + } else { + show_mapping_view(); + } + } + } else { + push_view(view_controller_variant); + } } } nk_group_end(context); @@ -1353,36 +1457,85 @@ void view_controllers(struct nk_context *context) { if (nk_begin(context, "Controllers", nk_rect(0, 0, render_width(), render_height()), NK_WINDOW_NO_SCROLLBAR)) { - int height = (render_width() - 2*context->style.font->height) / MAX_JOYSTICKS; + int height = (render_height() - 2*context->style.font->height) / 5; + int inner_height = height - context->style.window.spacing.y; + const struct nk_user_font *font = context->style.font; + int bindings_width = font->width(font->userdata, font->height, "Bindings", strlen("Bindings")) + context->style.button.padding.x * 2; + int remap_width = font->width(font->userdata, font->height, "Remap", strlen("Remap")) + context->style.button.padding.x * 2; + int change_type_width = font->width(font->userdata, font->height, "Change Type", strlen("Change Type")) + context->style.button.padding.x * 2; + int total = bindings_width + remap_width + change_type_width; + float bindings_ratio = (float)bindings_width / total; + float remap_ratio = (float)remap_width / total; + float change_type_ratio = (float)change_type_width / total; + + + uint8_t found_controller = 0; for (int i = 0; i < MAX_JOYSTICKS; i++) { SDL_Joystick *joy = render_get_joystick(i); if (joy) { + found_controller = 1; controller_info info = get_controller_info(i); ui_image *controller_image = select_best_image(&info); - int image_width = height * controller_image->width / controller_image->height; - nk_layout_row_begin(context, NK_STATIC, height, 2); - nk_layout_row_push(context, image_width); + int image_width = inner_height * controller_image->width / controller_image->height; + nk_layout_space_begin(context, NK_STATIC, height, INT_MAX); + nk_layout_space_push(context, nk_rect(context->style.font->height / 2, 0, image_width, inner_height)); if (info.type == TYPE_UNKNOWN || info.type == TYPE_GENERIC_MAPPING) { nk_label(context, "?", NK_TEXT_CENTERED); } else { nk_image(context, controller_image->ui); } - nk_layout_row_push(context, render_width() - image_width - 2 * context->style.font->height); - if (nk_button_label(context, info.name)) { - selected_controller = i; - selected_controller_info = info; - if (info.type == TYPE_UNKNOWN || info.type == TYPE_GENERIC_MAPPING) { + int button_start = image_width + context->style.font->height; + int button_area_width = render_width() - image_width - 2 * context->style.font->height; + + nk_layout_space_push(context, nk_rect(button_start, 0, button_area_width, inner_height/2)); + nk_label(context, info.name, NK_TEXT_CENTERED); + const struct nk_user_font *font = context->style.font; + if (info.type == TYPE_UNKNOWN || info.type == TYPE_GENERIC_MAPPING) { + int button_width = font->width(font->userdata, font->height, "Configure", strlen("Configure")); + nk_layout_space_push(context, nk_rect(button_start, height/2, button_width, inner_height/2)); + if (nk_button_label(context, "Configure")) { + selected_controller = i; + selected_controller_info = info; + initial_controller_config = 1; push_view(view_controller_type); - } else { + } + } else { + button_area_width -= 2 * context->style.window.spacing.x; + bindings_width = bindings_ratio * button_area_width; + nk_layout_space_push(context, nk_rect(button_start, height/2, bindings_width, inner_height/2)); + if (nk_button_label(context, "Bindings")) { + selected_controller = i; + selected_controller_info = info; push_view(view_controller_bindings); controller_binding_changed = 0; } - + button_start += bindings_width + context->style.window.spacing.x; + remap_width = remap_ratio * button_area_width; + nk_layout_space_push(context, nk_rect(button_start, height/2, remap_width, inner_height/2)); + if (nk_button_label(context, "Remap")) { + selected_controller = i; + selected_controller_info = info; + initial_controller_config = 0; + show_mapping_view(); + } + button_start += remap_width + context->style.window.spacing.x; + change_type_width = change_type_ratio * button_area_width; + nk_layout_space_push(context, nk_rect(button_start, height/2, change_type_width, inner_height/2)); + if (nk_button_label(context, "Change Type")) { + selected_controller = i; + selected_controller_info = info; + initial_controller_config = 0; + push_view(view_controller_type); + } } - nk_layout_row_end(context); + //nk_layout_row_end(context); } } + if (!found_controller) { + nk_layout_row_static(context, context->style.font->height, render_width() - 2 * context->style.font->height, 1); + nk_label(context, "No controllers detected", NK_TEXT_CENTERED); + } nk_layout_row_static(context, context->style.font->height, (render_width() - 2 * context->style.font->height) / 2, 2); nk_label(context, "", NK_TEXT_LEFT); if (nk_button_label(context, "Back")) { @@ -1423,6 +1576,24 @@ } } +void settings_string(struct nk_context *context, char *label, char *path, char *def) +{ + nk_label(context, label, NK_TEXT_LEFT); + char *curstr = tern_find_path_default(config, path, (tern_val){.ptrval = def}, TVAL_PTR).ptrval; + uint32_t len = strlen(curstr); + uint32_t buffer_len = len > 100 ? len + 1 : 101; + char *buffer = malloc(buffer_len); + memcpy(buffer, curstr, len); + memset(buffer+len, 0, buffer_len-len); + nk_edit_string(context, NK_EDIT_SIMPLE, buffer, &len, buffer_len-1, nk_filter_default); + buffer[len] = 0; + if (strcmp(buffer, curstr)) { + config_dirty = 1; + config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(buffer)}, TVAL_PTR); + } + free(buffer); +} + void settings_int_property(struct nk_context *context, char *label, char *name, char *path, int def, int min, int max) { char *curstr = tern_find_path(config, path, TVAL_PTR).ptrval; @@ -1438,6 +1609,21 @@ } } +void settings_float_property(struct nk_context *context, char *label, char *name, char *path, float def, float min, float max, float step) +{ + char *curstr = tern_find_path(config, path, TVAL_PTR).ptrval; + float curval = curstr ? atof(curstr) : def; + nk_label(context, label, NK_TEXT_LEFT); + float val = curval; + nk_property_float(context, name, min, &val, max, step, step); + if (val != curval) { + char buffer[64]; + sprintf(buffer, "%f", val); + config_dirty = 1; + config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(buffer)}, TVAL_PTR); + } +} + typedef struct { char *fragment; char *vertex; @@ -1466,7 +1652,7 @@ if (!dupe) { if (num_progs == prog_storage) { prog_storage = prog_storage ? prog_storage*2 : 4; - progs = realloc(progs, sizeof(progs) * prog_storage); + progs = realloc(progs, sizeof(*progs) * prog_storage); } progs[num_progs].vertex = NULL; progs[num_progs++].fragment = strdup(entries[i].name); @@ -1512,8 +1698,13 @@ progs = NULL; prog_storage = 0; } +#ifdef DATA_PATH + shader_dir = path_append(DATA_PATH, "shaders"); +#else shader_dir = path_append(get_exe_dir(), "shaders"); +#endif entries = get_dir_list(shader_dir, &num_entries); + free(shader_dir); progs = get_shader_progs(entries, num_entries, progs, &num_progs, &prog_storage); *num_out = num_progs; return progs; @@ -1649,13 +1840,24 @@ "128", "64" }; + const char *dac[] = { + "zero_offset", + "linear" + }; + const char *dac_desc[] = { + "Zero Offset", + "Linear" + }; const uint32_t num_rates = sizeof(rates)/sizeof(*rates); const uint32_t num_sizes = sizeof(sizes)/sizeof(*sizes); + const uint32_t num_dacs = sizeof(dac)/sizeof(*dac); static int32_t selected_rate = -1; static int32_t selected_size = -1; - if (selected_rate < 0 || selected_size < 0) { + static int32_t selected_dac = -1; + if (selected_rate < 0 || selected_size < 0 || selected_dac < 0) { selected_rate = find_match(rates, num_rates, "autio\0rate\0", "48000"); selected_size = find_match(sizes, num_sizes, "audio\0buffer\0", "512"); + selected_dac = find_match(dac, num_dacs, "audio\0fm_dac\0", "zero_offset"); } uint32_t width = render_width(); uint32_t height = render_height(); @@ -1668,12 +1870,60 @@ selected_rate = settings_dropdown(context, "Rate in Hz", rates, num_rates, selected_rate, "audio\0rate\0"); selected_size = settings_dropdown(context, "Buffer Samples", sizes, num_sizes, selected_size, "audio\0buffer\0"); settings_int_input(context, "Lowpass Cutoff Hz", "audio\0lowpass_cutoff\0", "3390"); + settings_float_property(context, "Gain (dB)", "Overall", "audio\0gain\0", 0, -30.0f, 30.0f, 0.5f); + settings_float_property(context, "", "FM", "audio\0fm_gain\0", 0, -30.0f, 30.0f, 0.5f); + settings_float_property(context, "", "PSG", "audio\0psg_gain\0", 0, -30.0f, 30.0f, 0.5f); + selected_dac = settings_dropdown_ex(context, "FM DAC", dac, dac_desc, num_dacs, selected_dac, "audio\0fm_dac\0"); if (nk_button_label(context, "Back")) { pop_view(); } nk_end(context); } } +typedef struct { + const char **models; + const char **names; + uint32_t num_models; + uint32_t storage; +} model_foreach_state; +void model_iter(char *key, tern_val val, uint8_t valtype, void *data) +{ + if (valtype != TVAL_NODE) { + return; + } + model_foreach_state *state = data; + if (state->num_models == state->storage) { + state->storage *= 2; + state->models = realloc(state->models, state->storage * sizeof(char *)); + state->names = realloc(state->names, state->storage * sizeof(char *)); + } + char *def = strdup(key); + state->models[state->num_models] = def; + state->names[state->num_models++] = tern_find_ptr_default(val.ptrval, "name", def); +} + +typedef struct { + const char **models; + const char **names; +} models; + +models get_models(uint32_t *num_out) +{ + tern_node *systems = get_systems_config(); + model_foreach_state state = { + .models = calloc(4, sizeof(char *)), + .names = calloc(4, sizeof(char *)), + .num_models = 0, + .storage = 4 + }; + tern_foreach(systems, model_iter, &state); + *num_out = state.num_models; + return (models){ + .models = state.models, + .names = state.names + }; +} + void view_system_settings(struct nk_context *context) { const char *sync_opts[] = { @@ -1696,12 +1946,25 @@ if (selected_region < 0) { selected_region = find_match(region_codes, num_regions, "system\0default_region\0", "U"); } + static const char **model_opts; + static const char **model_names; + static uint32_t num_models; + if (!model_opts) { + models m = get_models(&num_models); + model_opts = m.models; + model_names = m.names; + } + static int32_t selected_model = -1; + if (selected_model < 0) { + selected_model = find_match(model_opts, num_models, "system\0model\0", "md1va3"); + } + const char *formats[] = { "native", "gst" }; const uint32_t num_formats = sizeof(formats)/sizeof(*formats); - int32_t selected_format = -1; + static int32_t selected_format = -1; if (selected_format < 0) { selected_format = find_match(formats, num_formats, "ui\0state_format\0", "native"); } @@ -1715,6 +1978,7 @@ selected_init = find_match(ram_inits, num_inits, "system\0ram_init\0", "zero"); } const char *io_opts_1[] = { + "none", "gamepad2.1", "gamepad3.1", "gamepad6.1", @@ -1723,6 +1987,7 @@ "xband keyboard" }; const char *io_opts_2[] = { + "none", "gamepad2.2", "gamepad3.2", "gamepad6.2", @@ -1743,14 +2008,19 @@ uint32_t desired_width = context->style.font->height * 10; if (nk_begin(context, "System Settings", nk_rect(0, 0, width, height), 0)) { nk_layout_row_static(context, context->style.font->height, desired_width, 2); + + selected_model = settings_dropdown_ex(context, "Model", model_opts, model_names, num_models, selected_model, "system\0model\0"); + 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"); + 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"); + selected_region = settings_dropdown_ex(context, "Default Region", region_codes, regions, num_regions, selected_region, "system\0default_region\0"); selected_sync = settings_dropdown(context, "Sync Source", sync_opts, num_sync_opts, selected_sync, "system\0sync_source\0"); settings_int_property(context, "68000 Clock Divider", "", "clocks\0m68k_divider\0", 7, 1, 53); - settings_toggle(context, "Remember ROM Path", "ui\0remember_path\0", 1); - selected_region = settings_dropdown_ex(context, "Default Region", region_codes, regions, num_regions, selected_region, "system\0default_region\0"); selected_format = settings_dropdown(context, "Save State Format", formats, num_formats, selected_format, "ui\0state_format\0"); selected_init = settings_dropdown(context, "Initial RAM Value", ram_inits, num_inits, selected_init, "system\0ram_init\0"); - 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"); - 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"); + settings_toggle(context, "Remember ROM Path", "ui\0remember_path\0", 1); + settings_toggle(context, "Save config with EXE", "ui\0config_in_exe_dir\0", 0); + settings_string(context, "Game Save Path", "ui\0save_path\0", "$USERDATA/blastem/$ROMNAME"); + if (nk_button_label(context, "Back")) { pop_view(); } @@ -1758,6 +2028,29 @@ } } +void view_confirm_reset(struct nk_context *context) +{ + if (nk_begin(context, "Reset Confirm", nk_rect(0, 0, render_width(), render_height()), 0)) { + uint32_t desired_width = context->style.font->height * 20; + nk_layout_row_static(context, context->style.font->height, desired_width, 1); + nk_label(context, "This will reset all settings and controller", NK_TEXT_LEFT); + nk_label(context, "mappings back to the defaults.", NK_TEXT_LEFT); + nk_label(context, "Are you sure you want to proceed?", NK_TEXT_LEFT); + nk_layout_row_static(context, context->style.font->height * 1.5, desired_width / 2, 2); + if (nk_button_label(context, "Maybe not")) { + pop_view(); + } + if (nk_button_label(context, "Yep, delete it all")) { + delete_custom_config(); + config = load_config(); + delete_controller_info(); + config_dirty = 1; + pop_view(); + } + nk_end(context); + } +} + void view_back(struct nk_context *context) { pop_view(); @@ -1773,6 +2066,7 @@ {"Video", view_video_settings}, {"Audio", view_audio_settings}, {"System", view_system_settings}, + {"Reset to Defaults", view_confirm_reset}, {"Back", view_back} }; @@ -1825,13 +2119,22 @@ if (current_view != view_play) { nk_input_end(context); current_view(context); - nk_sdl_render(NK_ANTI_ALIASING_ON, 512 * 1024, 128 * 1024); + if (fb_context) { + fb_context->fb.pixels = render_get_framebuffer(FRAMEBUFFER_UI, &fb_context->fb.pitch); + nk_rawfb_render(fb_context, nk_rgb(0,0,0), 0); + render_framebuffer_updated(FRAMEBUFFER_UI, render_width()); + } else { +#ifndef DISABLE_OPENGL + nk_sdl_render(NK_ANTI_ALIASING_ON, 512 * 1024, 128 * 1024); +#endif + } nk_input_begin(context); } } void ui_idle_loop(void) { + render_enable_gamepad_events(1); const uint32_t MIN_UI_DELAY = 15; static uint32_t last; while (current_view != view_play) @@ -1848,6 +2151,7 @@ persist_config(config); config_dirty = 0; } + render_enable_gamepad_events(0); } static void handle_event(SDL_Event *event) { @@ -1866,15 +2170,29 @@ axis_moved = event->jaxis.axis; axis_value = event->jaxis.value; } + } else if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == 0) { + click = 1; + } else if (event->type == SDL_MOUSEBUTTONUP && event->button.button == 0) { + click = 0; } nk_sdl_handle_event(event); } static void context_destroyed(void) { - nk_sdl_shutdown(); + if (context) + { + nk_sdl_shutdown(); + context = NULL; + } } +static void fb_resize(void) +{ + nk_rawfb_resize_fb(fb_context, NULL, render_width(), render_height(), 0); +} + +#ifndef DISABLE_OPENGL static struct nk_image load_image_texture(uint32_t *buf, uint32_t width, uint32_t height) { GLuint tex; @@ -1891,11 +2209,29 @@ #endif return nk_image_id((int)tex); } +#endif + +static struct nk_image load_image_rawfb(uint32_t *buf, uint32_t width, uint32_t height) +{ + struct rawfb_image *fbimg = calloc(1, sizeof(struct rawfb_image)); + fbimg->pixels = buf; + fbimg->pitch = width * sizeof(uint32_t); + fbimg->w = width; + fbimg->h = height; + fbimg->format = NK_FONT_ATLAS_RGBA32; + return nk_image_ptr(fbimg); +} static void texture_init(void) { struct nk_font_atlas *atlas; - nk_sdl_font_stash_begin(&atlas); + if (fb_context) { + nk_rawfb_font_stash_begin(fb_context, &atlas); + } else { +#ifndef DISABLE_OPENGL + nk_sdl_font_stash_begin(&atlas); +#endif + } uint32_t font_size; uint8_t *font = default_font(&font_size); if (!font) { @@ -1903,33 +2239,73 @@ } def_font = nk_font_atlas_add_from_memory(atlas, font, font_size, render_height() / 16, NULL); free(font); - nk_sdl_font_stash_end(); + if (fb_context) { + nk_rawfb_font_stash_end(fb_context); + } else { +#ifndef DISABLE_OPENGL + nk_sdl_font_stash_end(); +#endif + } nk_style_set_font(context, &def_font->handle); for (uint32_t i = 0; i < num_ui_images; i++) { - ui_images[i]->ui = load_image_texture(ui_images[i]->image_data, ui_images[i]->width, ui_images[i]->height); +#ifndef DISABLE_OPENGL + if (fb_context) { +#endif + ui_images[i]->ui = load_image_rawfb(ui_images[i]->image_data, ui_images[i]->width, ui_images[i]->height); +#ifndef DISABLE_OPENGL + } else { + ui_images[i]->ui = load_image_texture(ui_images[i]->image_data, ui_images[i]->width, ui_images[i]->height); + } +#endif } } +static void style_init(void) +{ + context->style.checkbox.padding.x = render_height() / 120; + context->style.checkbox.padding.y = render_height() / 120; + context->style.checkbox.border = render_height() / 240; + context->style.checkbox.cursor_normal.type = NK_STYLE_ITEM_COLOR; + context->style.checkbox.cursor_normal.data.color = (struct nk_color){ + .r = 255, .g = 128, .b = 0, .a = 255 + }; + context->style.checkbox.cursor_hover = context->style.checkbox.cursor_normal; + context->style.property.inc_button.text_hover = (struct nk_color){ + .r = 255, .g = 128, .b = 0, .a = 255 + }; + context->style.property.dec_button.text_hover = context->style.property.inc_button.text_hover; + context->style.combo.button.text_hover = context->style.property.inc_button.text_hover; +} + static void context_created(void) { context = nk_sdl_init(render_get_window()); + nk_sdl_device_create(); + style_init(); texture_init(); } void show_pause_menu(void) { - set_content_binding_state(0); - context->style.window.background = nk_rgba(0, 0, 0, 128); - context->style.window.fixed_background = nk_style_item_color(nk_rgba(0, 0, 0, 128)); - current_view = view_pause; - current_system->request_exit(current_system); + if (current_view == view_play) { + set_content_binding_state(0); + context->style.window.background = nk_rgba(0, 0, 0, 128); + context->style.window.fixed_background = nk_style_item_color(nk_rgba(0, 0, 0, 128)); + current_view = view_pause; + context->input.selected_widget = 0; + system_request_exit(current_system, 1); + } else if (current_system && !set_binding) { + clear_view_stack(); + show_play_view(); + } } void show_play_view(void) { set_content_binding_state(1); current_view = view_play; + context->input.selected_widget = 0; } static uint8_t active; @@ -1940,10 +2316,10 @@ uint8_t is_nuklear_available(void) { - if (!render_has_gl()) { + /*if (!render_has_gl()) { //currently no fallback if GL2 unavailable return 0; - } + }*/ char *style = tern_find_path(config, "ui\0style\0", TVAL_PTR).ptrval; if (!style) { return 1; @@ -1993,10 +2369,23 @@ void blastem_nuklear_init(uint8_t file_loaded) { context = nk_sdl_init(render_get_window()); +#ifndef DISABLE_OPENGL + if (render_has_gl()) { + nk_sdl_device_create(); + } else { +#endif + fb_context = nk_rawfb_init(NULL, context, render_width(), render_height(), 0); + render_set_ui_fb_resize_handler(fb_resize); +#ifndef DISABLE_OPENGL + } +#endif + style_init(); controller_360 = load_ui_image("images/360.png"); controller_ps4 = load_ui_image("images/ps4.png"); controller_ps4_6b = load_ui_image("images/ps4_6b.png"); + controller_wiiu = load_ui_image("images/wiiu.png"); + controller_gen_6b = load_ui_image("images/genesis_6b.png"); texture_init();