From 03f40b1d8e873b57eab68dc66ae9892aa5e39f89 Mon Sep 17 00:00:00 2001 From: Fred Hallock Date: Mon, 18 Dec 2023 01:04:14 -0500 Subject: [PATCH] ui: Support controller peripherals and XMU devices (#1315) * Added XMU Settings to the Input Screen * Added Peripherals to config * Prevent overwriting existing XMUs * Added blockdev.h to try to fix the MacOS build * Fixed some issues that antangelo pointed out * Moved the peripheralType and param vars into the loop * Moved fatx.h and fatx.c to ui\thirdparty\fatx * Added Validation for Peripheral Settings * Fixed some nits that were pointed out * don't pass NULL into xemu_settings_set_string * Changes following Matt's recommendations * Changes to XMU FilePicker * XMU image auto-bind logic refactor * renamed peripheralType to peripheral_type * removed unnecessary calls to g_strdup_printf and g_free * Cleaned up some comments, removed an unnecessary variable * handle overwrite prompt in Windows * Fixed some code format and style inconsistencies * More formatting fixes * Fixed a few memory leaks * qemu_access: check for Read and Write access * Run clang-format * Remove unused xemu_new_xmu declaration * Fix use after free in rebind code --- config_spec.yml | 21 + data/meson.build | 1 + data/xmu_mask.png | Bin 0 -> 9950 bytes ui/meson.build | 2 +- ui/thirdparty/fatx/fatx.c | 51 ++ ui/thirdparty/fatx/fatx.h | 16 + ui/thirdparty/meson.build | 3 + .../noc_file_dialog/noc_file_dialog.h | 3 + ui/xemu-input.c | 258 ++++++++++- ui/xemu-input.h | 19 + ui/xui/gl-helpers.cc | 99 ++-- ui/xui/gl-helpers.hh | 4 +- ui/xui/main-menu.cc | 434 ++++++++++++++---- 13 files changed, 769 insertions(+), 142 deletions(-) create mode 100644 data/xmu_mask.png create mode 100644 ui/thirdparty/fatx/fatx.c create mode 100644 ui/thirdparty/fatx/fatx.h diff --git a/config_spec.yml b/config_spec.yml index cae05c17ec..b858606e68 100644 --- a/config_spec.yml +++ b/config_spec.yml @@ -25,6 +25,27 @@ input: port2: string port3: string port4: string + peripherals: + port1: + peripheral_type_0: integer + peripheral_param_0: string + peripheral_type_1: integer + peripheral_param_1: string + port2: + peripheral_type_0: integer + peripheral_param_0: string + peripheral_type_1: integer + peripheral_param_1: string + port3: + peripheral_type_0: integer + peripheral_param_0: string + peripheral_type_1: integer + peripheral_param_1: string + port4: + peripheral_type_0: integer + peripheral_param_0: string + peripheral_type_1: integer + peripheral_param_1: string gamecontrollerdb_path: string auto_bind: type: bool diff --git a/data/meson.build b/data/meson.build index 89e6123d90..e1a7ebedf4 100644 --- a/data/meson.build +++ b/data/meson.build @@ -1,5 +1,6 @@ pfiles = [ 'controller_mask.png', + 'xmu_mask.png', 'logo_sdf.png', 'xemu_64x64.png', 'abxy.ttf', diff --git a/data/xmu_mask.png b/data/xmu_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..1ff5a7489ed4b333e56fbca03d07cb55d0703e53 GIT binary patch literal 9950 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 = INPUT_DEVICE_SDL_KEYBOARD; new_con->name = "Keyboard"; new_con->bound = -1; + new_con->peripheral_types[0] = PERIPHERAL_NONE; + new_con->peripheral_types[1] = PERIPHERAL_NONE; + new_con->peripherals[0] = NULL; + new_con->peripherals[1] = NULL; sdl_kbd_scancode_map[0] = g_config.input.keyboard_controller_scancode_map.a; sdl_kbd_scancode_map[1] = g_config.input.keyboard_controller_scancode_map.b; @@ -154,6 +184,7 @@ void xemu_input_init(void) char buf[128]; snprintf(buf, sizeof(buf), "Connected '%s' to port %d", new_con->name, port+1); xemu_queue_notification(buf); + xemu_input_rebind_xmu(port); } QTAILQ_INSERT_TAIL(&available_controllers, new_con, entry); @@ -177,6 +208,24 @@ int xemu_input_get_controller_default_bind_port(ControllerState *state, int star return -1; } +void xemu_save_peripheral_settings(int player_index, int peripheral_index, + int peripheral_type, + const char *peripheral_parameter) +{ + int *peripheral_type_ptr = + peripheral_types_settings_map[player_index][peripheral_index]; + const char **peripheral_param_ptr = + peripheral_params_settings_map[player_index][peripheral_index]; + + assert(peripheral_type_ptr); + assert(peripheral_param_ptr); + + *peripheral_type_ptr = peripheral_type; + xemu_settings_set_string( + peripheral_param_ptr, + peripheral_parameter == NULL ? "" : peripheral_parameter); +} + void xemu_input_process_sdl_events(const SDL_Event *event) { if (event->type == SDL_CONTROLLERDEVICEADDED) { @@ -201,6 +250,10 @@ void xemu_input_process_sdl_events(const SDL_Event *event) new_con->sdl_joystick_id = SDL_JoystickInstanceID(new_con->sdl_joystick); new_con->sdl_joystick_guid = SDL_JoystickGetGUID(new_con->sdl_joystick); new_con->bound = -1; + new_con->peripheral_types[0] = PERIPHERAL_NONE; + new_con->peripheral_types[1] = PERIPHERAL_NONE; + new_con->peripherals[0] = NULL; + new_con->peripherals[1] = NULL; char guid_buf[35] = { 0 }; SDL_JoystickGetGUIDString(new_con->sdl_joystick_guid, guid_buf, sizeof(guid_buf)); @@ -254,6 +307,7 @@ void xemu_input_process_sdl_events(const SDL_Event *event) char buf[128]; snprintf(buf, sizeof(buf), "Connected '%s' to port %d", new_con->name, port+1); xemu_queue_notification(buf); + xemu_input_rebind_xmu(port); } } else if (event->type == SDL_CONTROLLERDEVICEREMOVED) { DPRINTF("Controller Removed: %d\n", event->cdevice.which); @@ -286,6 +340,11 @@ void xemu_input_process_sdl_events(const SDL_Event *event) if (iter->sdl_gamecontroller) { SDL_GameControllerClose(iter->sdl_gamecontroller); } + + for (int i = 0; i < 2; i++) { + if (iter->peripherals[i]) + g_free(iter->peripherals[i]); + } free(iter); handled = 1; @@ -380,12 +439,9 @@ void xemu_input_update_sdl_controller_state(ControllerState *state) } const SDL_GameControllerAxis sdl_axis_map[6] = { - SDL_CONTROLLER_AXIS_TRIGGERLEFT, - SDL_CONTROLLER_AXIS_TRIGGERRIGHT, - SDL_CONTROLLER_AXIS_LEFTX, - SDL_CONTROLLER_AXIS_LEFTY, - SDL_CONTROLLER_AXIS_RIGHTX, - SDL_CONTROLLER_AXIS_RIGHTY, + SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, + SDL_CONTROLLER_AXIS_LEFTX, SDL_CONTROLLER_AXIS_LEFTY, + SDL_CONTROLLER_AXIS_RIGHTX, SDL_CONTROLLER_AXIS_RIGHTY, }; for (int i = 0; i < 6; i++) { @@ -429,6 +485,22 @@ void xemu_input_bind(int index, ControllerState *state, int save) if (bound_controllers[index]) { assert(bound_controllers[index]->device != NULL); Error *err = NULL; + + // Unbind any XMUs + for (int i = 0; i < 2; i++) { + if (bound_controllers[index]->peripherals[i]) { + // If this was an XMU, unbind the XMU + if (bound_controllers[index]->peripheral_types[i] == + PERIPHERAL_XMU) + xemu_input_unbind_xmu(index, i); + + // Free up the XmuState and set the peripheral type to none + g_free(bound_controllers[index]->peripherals[i]); + bound_controllers[index]->peripherals[i] = NULL; + bound_controllers[index]->peripheral_types[i] = PERIPHERAL_NONE; + } + } + qdev_unplug((DeviceState *)bound_controllers[index]->device, &err); assert(err == NULL); @@ -460,7 +532,6 @@ void xemu_input_bind(int index, ControllerState *state, int save) bound_controllers[index] = state; bound_controllers[index]->bound = index; - const int port_map[4] = {3, 4, 1, 2}; char *tmp; // Create controller's internal USB hub. @@ -506,6 +577,179 @@ void xemu_input_bind(int index, ControllerState *state, int save) } } +bool xemu_input_bind_xmu(int player_index, int expansion_slot_index, + const char *filename, bool is_rebind) +{ + assert(player_index >= 0 && player_index < 4); + assert(expansion_slot_index >= 0 && expansion_slot_index < 2); + + ControllerState *player = bound_controllers[player_index]; + enum peripheral_type peripheral_type = + player->peripheral_types[expansion_slot_index]; + if (peripheral_type != PERIPHERAL_XMU) + return false; + + XmuState *xmu = (XmuState *)player->peripherals[expansion_slot_index]; + + // Unbind existing XMU + if (xmu->dev != NULL) { + xemu_input_unbind_xmu(player_index, expansion_slot_index); + } + + if (filename == NULL) + return false; + + // Look for any other XMUs that are using this file, and unbind them + for (int player_i = 0; player_i < 4; player_i++) { + ControllerState *state = bound_controllers[player_i]; + if (state != NULL) { + for (int peripheral_i = 0; peripheral_i < 2; peripheral_i++) { + if (state->peripheral_types[peripheral_i] == PERIPHERAL_XMU) { + XmuState *xmu_i = + (XmuState *)state->peripherals[peripheral_i]; + assert(xmu_i); + + if (xmu_i->filename != NULL && + strcmp(xmu_i->filename, filename) == 0) { + char *buf = + g_strdup_printf("This XMU is already mounted on " + "player %d slot %c\r\n", + player_i + 1, 'A' + peripheral_i); + xemu_queue_notification(buf); + g_free(buf); + return false; + } + } + } + } + } + + xmu->filename = g_strdup(filename); + + const int xmu_map[2] = { 2, 3 }; + char *tmp; + + static int id_counter = 0; + tmp = g_strdup_printf("xmu_%d", id_counter++); + + // Add the file as a drive + QDict *qdict1 = qdict_new(); + qdict_put_str(qdict1, "id", tmp); + qdict_put_str(qdict1, "format", "raw"); + qdict_put_str(qdict1, "file", filename); + + QemuOpts *drvopts = + qemu_opts_from_qdict(qemu_find_opts("drive"), qdict1, &error_abort); + + DriveInfo *dinfo = drive_new(drvopts, 0, &error_abort); + assert(dinfo); + + // Create the usb-storage device + QDict *qdict2 = qdict_new(); + + // Specify device driver + qdict_put_str(qdict2, "driver", "usb-storage"); + + // Specify device identifier + qdict_put_str(qdict2, "drive", tmp); + g_free(tmp); + + // Specify index/port + tmp = g_strdup_printf("1.%d.%d", port_map[player_index], + xmu_map[expansion_slot_index]); + qdict_put_str(qdict2, "port", tmp); + g_free(tmp); + + // Create the device + QemuOpts *opts = + qemu_opts_from_qdict(qemu_find_opts("device"), qdict2, &error_abort); + + DeviceState *dev = qdev_device_add(opts, &error_abort); + assert(dev); + + xmu->dev = (void *)dev; + + // Unref for eventual cleanup + qobject_unref(qdict1); + qobject_unref(qdict2); + + if (!is_rebind) { + xemu_save_peripheral_settings(player_index, expansion_slot_index, + peripheral_type, xmu->filename); + } + + return true; +} + +void xemu_input_unbind_xmu(int player_index, int expansion_slot_index) +{ + assert(player_index >= 0 && player_index < 4); + assert(expansion_slot_index >= 0 && expansion_slot_index < 2); + + ControllerState *state = bound_controllers[player_index]; + if (state->peripheral_types[expansion_slot_index] != PERIPHERAL_XMU) + return; + + XmuState *xmu = (XmuState *)state->peripherals[expansion_slot_index]; + if (xmu != NULL) { + if (xmu->dev != NULL) { + qdev_unplug((DeviceState *)xmu->dev, &error_abort); + object_unref(OBJECT(xmu->dev)); + xmu->dev = NULL; + } + + g_free((void *)xmu->filename); + xmu->filename = NULL; + } +} + +void xemu_input_rebind_xmu(int port) +{ + // Try to bind peripherals back to controller + for (int i = 0; i < 2; i++) { + enum peripheral_type peripheral_type = + (enum peripheral_type)(*peripheral_types_settings_map[port][i]); + + // If peripheralType is out of range, change the settings for this + // controller and peripheral port to default + if (peripheral_type < PERIPHERAL_NONE || + peripheral_type >= PERIPHERAL_TYPE_COUNT) { + xemu_save_peripheral_settings(port, i, PERIPHERAL_NONE, NULL); + peripheral_type = PERIPHERAL_NONE; + } + + const char *param = *peripheral_params_settings_map[port][i]; + + if (peripheral_type == PERIPHERAL_XMU) { + if (param != NULL && strlen(param) > 0) { + // This is an XMU and needs to be bound to this controller + if (qemu_access(param, R_OK | W_OK) == 0) { + bound_controllers[port]->peripheral_types[i] = + peripheral_type; + bound_controllers[port]->peripherals[i] = + g_malloc(sizeof(XmuState)); + memset(bound_controllers[port]->peripherals[i], 0, + sizeof(XmuState)); + bool did_bind = xemu_input_bind_xmu(port, i, param, true); + if (did_bind) { + char *buf = + g_strdup_printf("Connected XMU %s to port %d%c", + param, port + 1, 'A' + i); + xemu_queue_notification(buf); + g_free(buf); + } + } else { + char *buf = + g_strdup_printf("Unable to bind XMU at %s to port %d%c", + param, port + 1, 'A' + i); + xemu_queue_error_message(buf); + g_free(buf); + } + } + } + } +} + void xemu_input_set_test_mode(int enabled) { test_mode = enabled; diff --git a/ui/xemu-input.h b/ui/xemu-input.h index 8a8ba6544e..330ae58a7c 100644 --- a/ui/xemu-input.h +++ b/ui/xemu-input.h @@ -26,6 +26,8 @@ #define XEMU_INPUT_H #include +#include + #include "qemu/queue.h" enum controller_state_buttons_mask { @@ -63,6 +65,13 @@ enum controller_input_device_type { INPUT_DEVICE_SDL_GAMECONTROLLER, }; +enum peripheral_type { PERIPHERAL_NONE, PERIPHERAL_XMU, PERIPHERAL_TYPE_COUNT }; + +typedef struct XmuState { + const char *filename; + void *dev; +} XmuState; + typedef struct ControllerState { QTAILQ_ENTRY(ControllerState) entry; @@ -88,6 +97,9 @@ typedef struct ControllerState { SDL_JoystickID sdl_joystick_id; SDL_JoystickGUID sdl_joystick_guid; + enum peripheral_type peripheral_types[2]; + void *peripherals[2]; + int bound; // Which port this input device is bound to void *device; // DeviceState opaque } ControllerState; @@ -109,7 +121,14 @@ void xemu_input_update_sdl_controller_state(ControllerState *state); void xemu_input_update_rumble(ControllerState *state); ControllerState *xemu_input_get_bound(int index); void xemu_input_bind(int index, ControllerState *state, int save); +bool xemu_input_bind_xmu(int player_index, int peripheral_port_index, + const char *filename, bool is_rebind); +void xemu_input_rebind_xmu(int port); +void xemu_input_unbind_xmu(int player_index, int peripheral_port_index); int xemu_input_get_controller_default_bind_port(ControllerState *state, int start); +void xemu_save_peripheral_settings(int player_index, int peripheral_index, + int peripheral_type, + const char *peripheral_parameter); void xemu_input_set_test_mode(int enabled); int xemu_input_get_test_mode(void); diff --git a/ui/xui/gl-helpers.cc b/ui/xui/gl-helpers.cc index 4155ff2097..6c07d087b4 100644 --- a/ui/xui/gl-helpers.cc +++ b/ui/xui/gl-helpers.cc @@ -16,25 +16,24 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . // -#include "common.hh" -#include -#include -#include -#include +#include "ui/xemu-widescreen.h" #include "gl-helpers.hh" -#include "stb_image.h" +#include "common.hh" #include "data/controller_mask.png.h" #include "data/logo_sdf.png.h" -#include "ui/shader/xemu-logo-frag.h" #include "data/xemu_64x64.png.h" +#include "data/xmu_mask.png.h" #include "notifications.hh" -#include "ui/xemu-widescreen.h" +#include "stb_image.h" +#include +#include +#include +#include -Fbo *controller_fbo, - *logo_fbo; -GLuint g_controller_tex, - g_logo_tex, - g_icon_tex; +#include "ui/shader/xemu-logo-frag.h" + +Fbo *controller_fbo, *xmu_fbo, *logo_fbo; +GLuint g_controller_tex, g_logo_tex, g_icon_tex, g_xmu_tex; enum class ShaderType { Blit, @@ -186,9 +185,11 @@ static GLuint Shader(GLenum type, const char *src) glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status != GL_TRUE) { glGetShaderInfoLog(shader, sizeof(err_buf), NULL, err_buf); - fprintf(stderr, "Shader compilation failed: %s\n\n" - "[Shader Source]\n" - "%s\n", err_buf, src); + fprintf(stderr, + "Shader compilation failed: %s\n\n" + "[Shader Source]\n" + "%s\n", + err_buf, src); assert(0); } @@ -222,15 +223,15 @@ void main() { GLuint vert = Shader(GL_VERTEX_SHADER, vert_src); assert(vert != 0); -// const char *image_frag_src = R"( -// #version 150 core -// uniform sampler2D tex; -// in vec2 Texcoord; -// out vec4 out_Color; -// void main() { -// out_Color.rgba = texture(tex, Texcoord); -// } -// )"; + // const char *image_frag_src = R"( + // #version 150 core + // uniform sampler2D tex; + // in vec2 Texcoord; + // out vec4 out_Color; + // void main() { + // out_Color.rgba = texture(tex, Texcoord); + // } + // )"; const char *image_gamma_frag_src = R"( #version 400 core @@ -370,7 +371,7 @@ void RenderDecal(DecalShader *s, float x, float y, float w, float h, glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &th_i); float tw = tw_i, th = th_i; - #define COL(color, c) (float)(((color)>>((c)*8)) & 0xff)/255.0 +#define COL(color, c) (float)(((color) >> ((c)*8)) & 0xff) / 255.0 if (s->flipy_loc >= 0) { glUniform1i(s->flipy_loc, s->flip); } @@ -403,7 +404,7 @@ void RenderDecal(DecalShader *s, float x, float y, float w, float h, if (s->scale_loc >= 0) { glUniform1f(s->scale_loc, s->scale); } - #undef COL +#undef COL glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL); } @@ -412,14 +413,15 @@ struct rect { }; static const struct rect tex_items[] = { - { 0, 148, 467, 364 }, // obj_controller - { 0, 81, 67, 67 }, // obj_lstick - { 0, 14, 67, 67 }, // obj_rstick - { 67, 104, 68, 44 }, // obj_port_socket - { 67, 76, 28, 28 }, // obj_port_lbl_1 - { 67, 48, 28, 28 }, // obj_port_lbl_2 - { 67, 20, 28, 28 }, // obj_port_lbl_3 - { 95, 76, 28, 28 }, // obj_port_lbl_4 + { 0, 148, 467, 364 }, // obj_controller + { 0, 81, 67, 67 }, // obj_lstick + { 0, 14, 67, 67 }, // obj_rstick + { 67, 104, 68, 44 }, // obj_port_socket + { 67, 76, 28, 28 }, // obj_port_lbl_1 + { 67, 48, 28, 28 }, // obj_port_lbl_2 + { 67, 20, 28, 28 }, // obj_port_lbl_3 + { 95, 76, 28, 28 }, // obj_port_lbl_4 + { 0, 0, 512, 512 } // obj_xmu }; enum tex_item_names { @@ -431,6 +433,7 @@ enum tex_item_names { obj_port_lbl_2, obj_port_lbl_3, obj_port_lbl_4, + obj_xmu }; void InitCustomRendering(void) @@ -441,6 +444,9 @@ void InitCustomRendering(void) g_decal_shader = NewDecalShader(ShaderType::Mask); controller_fbo = new Fbo(512, 512); + g_xmu_tex = LoadTextureFromMemory(xmu_mask_data, xmu_mask_size); + xmu_fbo = new Fbo(512, 256); + g_logo_tex = LoadTextureFromMemory(logo_sdf_data, logo_sdf_size); g_logo_shader = NewDecalShader(ShaderType::Logo); logo_fbo = new Fbo(512, 512); @@ -646,6 +652,26 @@ void RenderControllerPort(float frame_x, float frame_y, int i, glUseProgram(0); } +void RenderXmu(float frame_x, float frame_y, uint32_t primary_color, + uint32_t secondary_color) +{ + glUseProgram(g_decal_shader->prog); + glBindVertexArray(g_decal_shader->vao); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, g_xmu_tex); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_ONE, GL_ZERO); + + // Render xmu + RenderDecal(g_decal_shader, frame_x, frame_y, 256, 256, + tex_items[obj_xmu].x, tex_items[obj_xmu].y, + tex_items[obj_xmu].w, tex_items[obj_xmu].h, primary_color, + secondary_color, 0); + + glBindVertexArray(0); + glUseProgram(0); +} + void RenderLogo(uint32_t time) { uint32_t color = 0x62ca13ff; @@ -801,8 +827,7 @@ void SaveScreenshot(GLuint tex, bool flip) time_t t = time(NULL); struct tm *tmp = localtime(&t); if (tmp) { - strftime(fname, sizeof(fname), "xemu-%Y-%m-%d-%H-%M-%S.png", - tmp); + strftime(fname, sizeof(fname), "xemu-%Y-%m-%d-%H-%M-%S.png", tmp); } else { strcpy(fname, "xemu.png"); } diff --git a/ui/xui/gl-helpers.hh b/ui/xui/gl-helpers.hh index 7401540816..82da963e6b 100644 --- a/ui/xui/gl-helpers.hh +++ b/ui/xui/gl-helpers.hh @@ -38,7 +38,7 @@ public: void Restore(); }; -extern Fbo *controller_fbo, *logo_fbo; +extern Fbo *controller_fbo, *xmu_fbo, *logo_fbo; extern GLuint g_icon_tex; void InitCustomRendering(void); @@ -47,6 +47,8 @@ void RenderController(float frame_x, float frame_y, uint32_t primary_color, uint32_t secondary_color, ControllerState *state); void RenderControllerPort(float frame_x, float frame_y, int i, uint32_t port_color); +void RenderXmu(float frame_x, float frame_y, uint32_t primary_color, + uint32_t secondary_color); void RenderFramebuffer(GLint tex, int width, int height, bool flip); void RenderFramebuffer(GLint tex, int width, int height, bool flip, float scale[2]); bool RenderFramebufferToPng(GLuint tex, bool flip, std::vector &png, int max_width = 0, int max_height = 0); diff --git a/ui/xui/main-menu.cc b/ui/xui/main-menu.cc index eb7749a51b..75b88cafb6 100644 --- a/ui/xui/main-menu.cc +++ b/ui/xui/main-menu.cc @@ -40,6 +40,10 @@ #include "../xemu-os-utils.h" #include "../xemu-xbe.h" +#include "../thirdparty/fatx/fatx.h" + +#define DEFAULT_XMU_SIZE 8388608 + MainMenuScene g_main_menu; MainMenuTabView::~MainMenuTabView() {} @@ -86,6 +90,9 @@ void MainMenuInputView::Draw() // Dimensions of controller (rendered at origin) float controller_width = 477.0f; float controller_height = 395.0f; + // Dimensions of XMU + float xmu_x = 0, xmu_x_stride = 256, xmu_y = 0; + float xmu_w = 256, xmu_h = 256; // Setup rendering to fbo for controller and port images controller_fbo->Target(); @@ -120,14 +127,14 @@ void MainMenuInputView::Draw() // uses the texture as a unique ID. Push a new ID now to resolve // the conflict. ImGui::PushID(i); - float x = b_x+i*b_x_stride; - ImGui::PushStyleColor(ImGuiCol_Button, is_selected ? - color_active : - color_inactive); - bool activated = ImGui::ImageButton(id, - ImVec2(b_w*g_viewport_mgr.m_scale,b_h*g_viewport_mgr.m_scale), - ImVec2(x/t_w, (b_y+b_h)/t_h), - ImVec2((x+b_w)/t_w, b_y/t_h), + float x = b_x + i * b_x_stride; + ImGui::PushStyleColor(ImGuiCol_Button, + is_selected ? color_active : color_inactive); + bool activated = ImGui::ImageButton( + id, + ImVec2(b_w * g_viewport_mgr.m_scale, b_h * g_viewport_mgr.m_scale), + ImVec2(x / t_w, (b_y + b_h) / t_h), + ImVec2((x + b_w) / t_w, b_y / t_h), port_padding * g_viewport_mgr.m_scale); ImGui::PopStyleColor(); @@ -193,6 +200,16 @@ void MainMenuInputView::Draw() } if (ImGui::Selectable(selectable_label, is_selected)) { xemu_input_bind(active, iter, 1); + + // FIXME: We want to bind the XMU here, but we can't because we + // just unbound it and we need to wait for Qemu to release the + // file + + // If we previously had no controller connected, we can rebind + // the XMU + if (bound_state == NULL) + xemu_input_rebind_xmu(active); + bound_state = iter; } if (is_selected) { @@ -260,6 +277,168 @@ void MainMenuInputView::Draw() ImGui::PopFont(); ImGui::SetCursorPos(pos); + if (bound_state) { + SectionTitle("Expansion Slots"); + // Begin a 2-column layout to render the expansion slots + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, + g_viewport_mgr.Scale(ImVec2(0, 12))); + ImGui::Columns(2, "mixed", false); + + xmu_fbo->Target(); + id = (ImTextureID)(intptr_t)xmu_fbo->Texture(); + + const char *img_file_filters = ".img Files\0*.img\0All Files\0*.*\0"; + const char *comboLabels[2] = { "###ExpansionSlotA", + "###ExpansionSlotB" }; + for (int i = 0; i < 2; i++) { + // Display a combo box to allow the user to choose the type of + // peripheral they want to use + enum peripheral_type selected_type = + bound_state->peripheral_types[i]; + const char *peripheral_type_names[2] = { "None", "Memory Unit" }; + const char *selected_peripheral_type = + peripheral_type_names[selected_type]; + ImGui::SetNextItemWidth(-FLT_MIN); + if (ImGui::BeginCombo(comboLabels[i], selected_peripheral_type, + ImGuiComboFlags_NoArrowButton)) { + // Handle all available peripheral types + for (int j = 0; j < 2; j++) { + bool is_selected = selected_type == j; + ImGui::PushID(j); + const char *selectable_label = peripheral_type_names[j]; + + if (ImGui::Selectable(selectable_label, is_selected)) { + // Free any existing peripheral + if (bound_state->peripherals[i] != NULL) { + if (bound_state->peripheral_types[i] == + PERIPHERAL_XMU) { + // Another peripheral was already bound. + // Unplugging + xemu_input_unbind_xmu(active, i); + } + + // Free the existing state + g_free((void *)bound_state->peripherals[i]); + bound_state->peripherals[i] = NULL; + } + + // Change the peripheral type to the newly selected type + bound_state->peripheral_types[i] = + (enum peripheral_type)j; + + // Allocate state for the new peripheral + if (j == PERIPHERAL_XMU) { + bound_state->peripherals[i] = + g_malloc(sizeof(XmuState)); + memset(bound_state->peripherals[i], 0, + sizeof(XmuState)); + } + + xemu_save_peripheral_settings( + active, i, bound_state->peripheral_types[i], NULL); + } + + if (is_selected) { + ImGui::SetItemDefaultFocus(); + } + + ImGui::PopID(); + } + + ImGui::EndCombo(); + } + DrawComboChevron(); + + // Set an X offset to center the image button within the column + ImGui::SetCursorPosX( + ImGui::GetCursorPosX() + + (int)((ImGui::GetColumnWidth() - + xmu_w * g_viewport_mgr.m_scale - + 2 * port_padding * g_viewport_mgr.m_scale) / + 2)); + + selected_type = bound_state->peripheral_types[i]; + if (selected_type == PERIPHERAL_XMU) { + float x = xmu_x + i * xmu_x_stride; + float y = xmu_y; + + XmuState *xmu = (XmuState *)bound_state->peripherals[i]; + if (xmu->filename != NULL && strlen(xmu->filename) > 0) { + RenderXmu(x, y, 0x81dc8a00, 0x0f0f0f00); + + } else { + RenderXmu(x, y, 0x1f1f1f00, 0x0f0f0f00); + } + + ImVec2 xmu_display_size; + if (ImGui::GetContentRegionMax().x < + xmu_h * g_viewport_mgr.m_scale) { + xmu_display_size.x = ImGui::GetContentRegionMax().x / 2; + xmu_display_size.y = xmu_display_size.x * xmu_h / xmu_w; + } else { + xmu_display_size = ImVec2(xmu_w * g_viewport_mgr.m_scale, + xmu_h * g_viewport_mgr.m_scale); + } + + ImGui::SetCursorPosX( + ImGui::GetCursorPosX() + + (int)((ImGui::GetColumnWidth() - xmu_display_size.x) / + 2.0)); + + ImGui::Image(id, xmu_display_size, ImVec2(0.5f * i, 1), + ImVec2(0.5f * (i + 1), 0)); + ImVec2 pos = ImGui::GetCursorPos(); + + ImGui::SetCursorPos(pos); + + // Button to generate a new XMU + ImGui::PushID(i); + if (ImGui::Button("New Image", ImVec2(250, 0))) { + int flags = NOC_FILE_DIALOG_SAVE | + NOC_FILE_DIALOG_OVERWRITE_CONFIRMATION; + const char *new_path = PausedFileOpen( + flags, img_file_filters, NULL, "xmu.img"); + + if (new_path) { + if (create_fatx_image(new_path, DEFAULT_XMU_SIZE)) { + // XMU was created successfully. Bind it + xemu_input_bind_xmu(active, i, new_path, false); + } else { + // Show alert message + char *msg = g_strdup_printf( + "Unable to create XMU image at %s", new_path); + xemu_queue_error_message(msg); + g_free(msg); + } + } + } + + const char *xmu_port_path = NULL; + if (xmu->filename == NULL) + xmu_port_path = g_strdup(""); + else + xmu_port_path = g_strdup(xmu->filename); + if (FilePicker("Image", &xmu_port_path, img_file_filters)) { + if (strlen(xmu_port_path) == 0) { + xemu_input_unbind_xmu(active, i); + } else { + xemu_input_bind_xmu(active, i, xmu_port_path, false); + } + } + g_free((void *)xmu_port_path); + + ImGui::PopID(); + } + + ImGui::NextColumn(); + } + + xmu_fbo->Restore(); + + ImGui::PopStyleVar(); // ItemSpacing + ImGui::Columns(1); + } + SectionTitle("Options"); Toggle("Auto-bind controllers", &g_config.input.auto_bind, "Bind newly connected controllers to any open port"); @@ -625,8 +804,9 @@ void MainMenuNetworkView::DrawNatOptions(bool appearing) void MainMenuNetworkView::DrawUdpOptions(bool appearing) { if (appearing) { - strncpy(remote_addr, g_config.net.udp.remote_addr, sizeof(remote_addr)-1); - strncpy(local_addr, g_config.net.udp.bind_addr, sizeof(local_addr)-1); + strncpy(remote_addr, g_config.net.udp.remote_addr, + sizeof(remote_addr) - 1); + strncpy(local_addr, g_config.net.udp.bind_addr, sizeof(local_addr) - 1); } float size_ratio = 0.5; @@ -663,7 +843,9 @@ MainMenuSnapshotsView::~MainMenuSnapshotsView() g_free(m_search_regex); } -bool MainMenuSnapshotsView::BigSnapshotButton(QEMUSnapshotInfo *snapshot, XemuSnapshotData *data, int current_snapshot_binding) +bool MainMenuSnapshotsView::BigSnapshotButton(QEMUSnapshotInfo *snapshot, + XemuSnapshotData *data, + int current_snapshot_binding) { ImGuiStyle &style = ImGui::GetStyle(); ImDrawList *draw_list = ImGui::GetWindowDrawList(); @@ -673,18 +855,27 @@ bool MainMenuSnapshotsView::BigSnapshotButton(QEMUSnapshotInfo *snapshot, XemuSn ImGui::PopFont(); ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, g_viewport_mgr.Scale(ImVec2(5, 5))); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, + g_viewport_mgr.Scale(ImVec2(5, 5))); ImGui::PushFont(g_font_mgr.m_menu_font_medium); ImVec2 ts_title = ImGui::CalcTextSize(snapshot->name); - ImVec2 thumbnail_size = g_viewport_mgr.Scale(ImVec2(XEMU_SNAPSHOT_THUMBNAIL_WIDTH, XEMU_SNAPSHOT_THUMBNAIL_HEIGHT)); + ImVec2 thumbnail_size = g_viewport_mgr.Scale( + ImVec2(XEMU_SNAPSHOT_THUMBNAIL_WIDTH, XEMU_SNAPSHOT_THUMBNAIL_HEIGHT)); ImVec2 thumbnail_pos(style.FramePadding.x, style.FramePadding.y); - ImVec2 name_pos(thumbnail_pos.x + thumbnail_size.x + style.FramePadding.x * 2, thumbnail_pos.y); - ImVec2 title_pos(name_pos.x, name_pos.y + ts_title.y + style.FramePadding.x); - ImVec2 date_pos(name_pos.x, title_pos.y + ts_title.y + style.FramePadding.x); - ImVec2 binding_pos(name_pos.x, date_pos.y + ts_title.y + style.FramePadding.x); - ImVec2 button_size(-FLT_MIN, fmax(thumbnail_size.y + style.FramePadding.y * 2, ts_title.y + ts_sub.y + style.FramePadding.y * 3)); + ImVec2 name_pos(thumbnail_pos.x + thumbnail_size.x + + style.FramePadding.x * 2, + thumbnail_pos.y); + ImVec2 title_pos(name_pos.x, + name_pos.y + ts_title.y + style.FramePadding.x); + ImVec2 date_pos(name_pos.x, + title_pos.y + ts_title.y + style.FramePadding.x); + ImVec2 binding_pos(name_pos.x, + date_pos.y + ts_title.y + style.FramePadding.x); + ImVec2 button_size(-FLT_MIN, + fmax(thumbnail_size.y + style.FramePadding.y * 2, + ts_title.y + ts_sub.y + style.FramePadding.y * 3)); bool load = ImGui::Button("###button", button_size); @@ -699,42 +890,55 @@ bool MainMenuSnapshotsView::BigSnapshotButton(QEMUSnapshotInfo *snapshot, XemuSn int thumbnail_width, thumbnail_height; glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, thumbnail); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &thumbnail_width); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &thumbnail_height); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, + &thumbnail_width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, + &thumbnail_height); // Draw black background behind thumbnail ImVec2 thumbnail_min(p0.x + thumbnail_pos.x, p0.y + thumbnail_pos.y); - ImVec2 thumbnail_max(thumbnail_min.x + thumbnail_size.x, thumbnail_min.y + thumbnail_size.y); + ImVec2 thumbnail_max(thumbnail_min.x + thumbnail_size.x, + thumbnail_min.y + thumbnail_size.y); draw_list->AddRectFilled(thumbnail_min, thumbnail_max, IM_COL32_BLACK); // Draw centered thumbnail image int scaled_width, scaled_height; - ScaleDimensions(thumbnail_width, thumbnail_height, thumbnail_size.x, thumbnail_size.y, &scaled_width, &scaled_height); - ImVec2 img_min = ImVec2(thumbnail_min.x + (thumbnail_size.x - scaled_width) / 2, - thumbnail_min.y + (thumbnail_size.y - scaled_height) / 2); - ImVec2 img_max = ImVec2(img_min.x + scaled_width, img_min.y + scaled_height); + ScaleDimensions(thumbnail_width, thumbnail_height, thumbnail_size.x, + thumbnail_size.y, &scaled_width, &scaled_height); + ImVec2 img_min = + ImVec2(thumbnail_min.x + (thumbnail_size.x - scaled_width) / 2, + thumbnail_min.y + (thumbnail_size.y - scaled_height) / 2); + ImVec2 img_max = + ImVec2(img_min.x + scaled_width, img_min.y + scaled_height); draw_list->AddImage((ImTextureID)(uint64_t)thumbnail, img_min, img_max); // Snapshot title ImGui::PushFont(g_font_mgr.m_menu_font_medium); - draw_list->AddText(ImVec2(p0.x + name_pos.x, p0.y + name_pos.y), IM_COL32(255, 255, 255, 255), snapshot->name); + draw_list->AddText(ImVec2(p0.x + name_pos.x, p0.y + name_pos.y), + IM_COL32(255, 255, 255, 255), snapshot->name); ImGui::PopFont(); // Snapshot XBE title name ImGui::PushFont(g_font_mgr.m_menu_font_small); - const char *title_name = data->xbe_title_name ? data->xbe_title_name : "(Unknown XBE Title Name)"; - draw_list->AddText(ImVec2(p0.x + title_pos.x, p0.y + title_pos.y), IM_COL32(255, 255, 255, 200), title_name); + const char *title_name = data->xbe_title_name ? data->xbe_title_name : + "(Unknown XBE Title Name)"; + draw_list->AddText(ImVec2(p0.x + title_pos.x, p0.y + title_pos.y), + IM_COL32(255, 255, 255, 200), title_name); // Snapshot date - g_autoptr(GDateTime) date = g_date_time_new_from_unix_local(snapshot->date_sec); + g_autoptr(GDateTime) date = + g_date_time_new_from_unix_local(snapshot->date_sec); char *date_buf = g_date_time_format(date, "%Y-%m-%d %H:%M:%S"); - draw_list->AddText(ImVec2(p0.x + date_pos.x, p0.y + date_pos.y), IM_COL32(255, 255, 255, 200), date_buf); + draw_list->AddText(ImVec2(p0.x + date_pos.x, p0.y + date_pos.y), + IM_COL32(255, 255, 255, 200), date_buf); g_free(date_buf); // Snapshot keyboard binding if (current_snapshot_binding != -1) { - char *binding_text = g_strdup_printf("Bound to F%d", current_snapshot_binding + 5); - draw_list->AddText(ImVec2(p0.x + binding_pos.x, p0.y + binding_pos.y), IM_COL32(255, 255, 255, 200), binding_text); + char *binding_text = + g_strdup_printf("Bound to F%d", current_snapshot_binding + 5); + draw_list->AddText(ImVec2(p0.x + binding_pos.x, p0.y + binding_pos.y), + IM_COL32(255, 255, 255, 200), binding_text); g_free(binding_text); } @@ -758,7 +962,7 @@ void MainMenuSnapshotsView::ClearSearch() int MainMenuSnapshotsView::OnSearchTextUpdate(ImGuiInputTextCallbackData *data) { GError *gerr = NULL; - MainMenuSnapshotsView *win = (MainMenuSnapshotsView*)data->UserData; + MainMenuSnapshotsView *win = (MainMenuSnapshotsView *)data->UserData; if (win->m_search_regex) { g_free(win->m_search_regex); @@ -770,7 +974,8 @@ int MainMenuSnapshotsView::OnSearchTextUpdate(ImGuiInputTextCallbackData *data) } char *buf = g_strdup_printf("(.*)%s(.*)", data->Buf); - win->m_search_regex = g_regex_new(buf, (GRegexCompileFlags)0, (GRegexMatchFlags)0, &gerr); + win->m_search_regex = + g_regex_new(buf, (GRegexCompileFlags)0, (GRegexMatchFlags)0, &gerr); g_free(buf); if (gerr) { win->m_search_regex = NULL; @@ -785,14 +990,17 @@ void MainMenuSnapshotsView::Draw() g_snapshot_mgr.Refresh(); SectionTitle("Snapshots"); - Toggle("Filter by current title", &g_config.general.snapshots.filter_current_game, - "Only display snapshots created while running the currently running XBE"); + Toggle("Filter by current title", + &g_config.general.snapshots.filter_current_game, + "Only display snapshots created while running the currently running " + "XBE"); if (g_config.general.snapshots.filter_current_game) { struct xbe *xbe = xemu_get_xbe_info(); if (xbe && xbe->cert) { if (xbe->cert->m_titleid != m_current_title_id) { - char *title_name = g_utf16_to_utf8(xbe->cert->m_title_name, 40, NULL, NULL, NULL); + char *title_name = g_utf16_to_utf8(xbe->cert->m_title_name, 40, + NULL, NULL, NULL); if (title_name) { m_current_title_name = title_name; g_free(title_name); @@ -816,7 +1024,8 @@ void MainMenuSnapshotsView::Draw() bool snapshot_with_create_name_exists = false; for (int i = 0; i < g_snapshot_mgr.m_snapshots_len; ++i) { - if (g_strcmp0(m_search_buf.c_str(), g_snapshot_mgr.m_snapshots[i].name) == 0) { + if (g_strcmp0(m_search_buf.c_str(), + g_snapshot_mgr.m_snapshots[i].name) == 0) { snapshot_with_create_name_exists = true; break; } @@ -828,8 +1037,10 @@ void MainMenuSnapshotsView::Draw() ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1, 0, 0, 1)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1, 0, 0, 1)); } - if (ImGui::Button(snapshot_with_create_name_exists ? "Replace" : "Create", ImVec2(-FLT_MIN, 0))) { - xemu_snapshots_save(m_search_buf.empty() ? NULL : m_search_buf.c_str(), NULL); + if (ImGui::Button(snapshot_with_create_name_exists ? "Replace" : "Create", + ImVec2(-FLT_MIN, 0))) { + xemu_snapshots_save(m_search_buf.empty() ? NULL : m_search_buf.c_str(), + NULL); ClearSearch(); } if (snapshot_with_create_name_exists) { @@ -837,28 +1048,36 @@ void MainMenuSnapshotsView::Draw() } if (snapshot_with_create_name_exists && ImGui::IsItemHovered()) { - ImGui::SetTooltip("A snapshot with the name \"%s\" already exists. This button will overwrite the existing snapshot.", m_search_buf.c_str()); + ImGui::SetTooltip("A snapshot with the name \"%s\" already exists. " + "This button will overwrite the existing snapshot.", + m_search_buf.c_str()); } ImGui::PopFont(); bool at_least_one_snapshot_displayed = false; for (int i = g_snapshot_mgr.m_snapshots_len - 1; i >= 0; i--) { - if (g_config.general.snapshots.filter_current_game && g_snapshot_mgr.m_extra_data[i].xbe_title_name && - m_current_title_name.size() && strcmp(m_current_title_name.c_str(), g_snapshot_mgr.m_extra_data[i].xbe_title_name)) { + if (g_config.general.snapshots.filter_current_game && + g_snapshot_mgr.m_extra_data[i].xbe_title_name && + m_current_title_name.size() && + strcmp(m_current_title_name.c_str(), + g_snapshot_mgr.m_extra_data[i].xbe_title_name)) { continue; } if (m_search_regex) { GMatchInfo *match; bool keep_entry = false; - - g_regex_match(m_search_regex, g_snapshot_mgr.m_snapshots[i].name, (GRegexMatchFlags)0, &match); + + g_regex_match(m_search_regex, g_snapshot_mgr.m_snapshots[i].name, + (GRegexMatchFlags)0, &match); keep_entry |= g_match_info_matches(match); g_match_info_free(match); if (g_snapshot_mgr.m_extra_data[i].xbe_title_name) { - g_regex_match(m_search_regex, g_snapshot_mgr.m_extra_data[i].xbe_title_name, (GRegexMatchFlags)0, &match); + g_regex_match(m_search_regex, + g_snapshot_mgr.m_extra_data[i].xbe_title_name, + (GRegexMatchFlags)0, &match); keep_entry |= g_match_info_matches(match); g_free(match); } @@ -873,7 +1092,8 @@ void MainMenuSnapshotsView::Draw() int current_snapshot_binding = -1; for (int i = 0; i < 4; ++i) { - if (g_strcmp0(*(g_snapshot_shortcut_index_key_map[i]), snapshot->name) == 0) { + if (g_strcmp0(*(g_snapshot_shortcut_index_key_map[i]), + snapshot->name) == 0) { assert(current_snapshot_binding == -1); current_snapshot_binding = i; } @@ -885,13 +1105,14 @@ void MainMenuSnapshotsView::Draw() bool load = BigSnapshotButton(snapshot, data, current_snapshot_binding); // FIXME: Provide context menu control annotation - if (ImGui::IsItemHovered() && ImGui::IsKeyPressed(ImGuiKey_GamepadFaceLeft)) { + if (ImGui::IsItemHovered() && + ImGui::IsKeyPressed(ImGuiKey_GamepadFaceLeft)) { ImGui::SetNextWindowPos(pos); ImGui::OpenPopup("Snapshot Options"); } - + DrawSnapshotContextMenu(snapshot, data, current_snapshot_binding); - + ImGui::PopID(); if (load) { @@ -915,12 +1136,14 @@ void MainMenuSnapshotsView::Draw() } ImVec2 dim = ImGui::CalcTextSize(msg); ImVec2 cur = ImGui::GetCursorPos(); - ImGui::SetCursorPosX(cur.x + (ImGui::GetColumnWidth()-dim.x)/2); + ImGui::SetCursorPosX(cur.x + (ImGui::GetColumnWidth() - dim.x) / 2); ImGui::TextColored(ImVec4(0.94f, 0.94f, 0.94f, 0.70f), "%s", msg); } } -void MainMenuSnapshotsView::DrawSnapshotContextMenu(QEMUSnapshotInfo *snapshot, XemuSnapshotData *data, int current_snapshot_binding) +void MainMenuSnapshotsView::DrawSnapshotContextMenu( + QEMUSnapshotInfo *snapshot, XemuSnapshotData *data, + int current_snapshot_binding) { if (!ImGui::BeginPopupContextItem("Snapshot Options")) { return; @@ -936,9 +1159,12 @@ void MainMenuSnapshotsView::DrawSnapshotContextMenu(QEMUSnapshotInfo *snapshot, if (ImGui::MenuItem(item_name)) { if (current_snapshot_binding >= 0) { - xemu_settings_set_string(g_snapshot_shortcut_index_key_map[current_snapshot_binding], ""); + xemu_settings_set_string(g_snapshot_shortcut_index_key_map + [current_snapshot_binding], + ""); } - xemu_settings_set_string(g_snapshot_shortcut_index_key_map[i], snapshot->name); + xemu_settings_set_string(g_snapshot_shortcut_index_key_map[i], + snapshot->name); current_snapshot_binding = i; ImGui::CloseCurrentPopup(); @@ -949,13 +1175,15 @@ void MainMenuSnapshotsView::DrawSnapshotContextMenu(QEMUSnapshotInfo *snapshot, if (current_snapshot_binding >= 0) { if (ImGui::MenuItem("Unbind")) { - xemu_settings_set_string(g_snapshot_shortcut_index_key_map[current_snapshot_binding], ""); + xemu_settings_set_string( + g_snapshot_shortcut_index_key_map[current_snapshot_binding], + ""); current_snapshot_binding = -1; } } ImGui::EndMenu(); } - + ImGui::Separator(); Error *err = NULL; @@ -982,11 +1210,13 @@ MainMenuSystemView::MainMenuSystemView() : m_dirty(false) void MainMenuSystemView::Draw() { - const char *rom_file_filters = ".bin Files\0*.bin\0.rom Files\0*.rom\0All Files\0*.*\0"; + const char *rom_file_filters = + ".bin Files\0*.bin\0.rom Files\0*.rom\0All Files\0*.*\0"; const char *qcow_file_filters = ".qcow2 Files\0*.qcow2\0All Files\0*.*\0"; if (m_dirty) { - ImGui::TextColored(ImVec4(1,0,0,1), "Application restart required to apply settings"); + ImGui::TextColored(ImVec4(1, 0, 0, 1), + "Application restart required to apply settings"); } if ((int)g_config.sys.avpack == CONFIG_SYS_AVPACK_NONE) { @@ -997,7 +1227,8 @@ void MainMenuSystemView::Draw() if (ChevronCombo( "System Memory", &g_config.sys.mem_limit, - "64 MiB (Default)\0""128 MiB\0", + "64 MiB (Default)\0" + "128 MiB\0", "Increase to 128 MiB for debug or homebrew applications")) { m_dirty = true; } @@ -1030,8 +1261,9 @@ void MainMenuSystemView::Draw() } } -MainMenuAboutView::MainMenuAboutView(): m_config_info_text{NULL} -{} +MainMenuAboutView::MainMenuAboutView() : m_config_info_text{ NULL } +{ +} void MainMenuAboutView::UpdateConfigInfoText() { @@ -1039,20 +1271,21 @@ void MainMenuAboutView::UpdateConfigInfoText() g_free(m_config_info_text); } - gchar *bootrom_checksum = GetFileMD5Checksum(g_config.sys.files.bootrom_path); + gchar *bootrom_checksum = + GetFileMD5Checksum(g_config.sys.files.bootrom_path); if (!bootrom_checksum) { bootrom_checksum = g_strdup("None"); } - gchar *flash_rom_checksum = GetFileMD5Checksum(g_config.sys.files.flashrom_path); + gchar *flash_rom_checksum = + GetFileMD5Checksum(g_config.sys.files.flashrom_path); if (!flash_rom_checksum) { flash_rom_checksum = g_strdup("None"); } - m_config_info_text = g_strdup_printf( - "MCPX Boot ROM MD5 Hash: %s\n" - "Flash ROM (BIOS) MD5 Hash: %s", - bootrom_checksum, flash_rom_checksum); + m_config_info_text = g_strdup_printf("MCPX Boot ROM MD5 Hash: %s\n" + "Flash ROM (BIOS) MD5 Hash: %s", + bootrom_checksum, flash_rom_checksum); g_free(bootrom_checksum); g_free(flash_rom_checksum); } @@ -1061,22 +1294,25 @@ void MainMenuAboutView::Draw() { static const char *build_info_text = NULL; if (build_info_text == NULL) { - build_info_text = g_strdup_printf( - "Version: %s\nBranch: %s\nCommit: %s\nDate: %s", - xemu_version, xemu_branch, xemu_commit, xemu_date); + build_info_text = + g_strdup_printf("Version: %s\nBranch: %s\nCommit: " + "%s\nDate: %s", + xemu_version, xemu_branch, xemu_commit, xemu_date); } static const char *sys_info_text = NULL; if (sys_info_text == NULL) { - const char *gl_shader_version = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION); - const char *gl_version = (const char*)glGetString(GL_VERSION); - const char *gl_renderer = (const char*)glGetString(GL_RENDERER); - const char *gl_vendor = (const char*)glGetString(GL_VENDOR); + const char *gl_shader_version = + (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION); + const char *gl_version = (const char *)glGetString(GL_VERSION); + const char *gl_renderer = (const char *)glGetString(GL_RENDERER); + const char *gl_vendor = (const char *)glGetString(GL_VENDOR); sys_info_text = g_strdup_printf( - "CPU: %s\nOS Platform: %s\nOS Version: %s\nManufacturer: %s\n" + "CPU: %s\nOS Platform: %s\nOS Version: " + "%s\nManufacturer: %s\n" "GPU Model: %s\nDriver: %s\nShader: %s", - xemu_get_cpu_info(), xemu_get_os_platform(), xemu_get_os_info(), gl_vendor, - gl_renderer, gl_version, gl_shader_version); + xemu_get_cpu_info(), xemu_get_os_platform(), xemu_get_os_info(), + gl_vendor, gl_renderer, gl_version, gl_shader_version); } if (m_config_info_text == NULL) { @@ -1121,7 +1357,7 @@ void MainMenuAboutView::Draw() } MainMenuTabButton::MainMenuTabButton(std::string text, std::string icon) -: m_icon(icon), m_text(text) + : m_icon(icon), m_text(text) { } @@ -1134,8 +1370,10 @@ bool MainMenuTabButton::Draw(bool selected) IM_COL32(0, 0, 0, 0); ImGui::PushStyleColor(ImGuiCol_Button, col); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, selected ? col : IM_COL32(32, 32, 32, 255)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, selected ? col : IM_COL32(32, 32, 32, 255)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, + selected ? col : IM_COL32(32, 32, 32, 255)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, + selected ? col : IM_COL32(32, 32, 32, 255)); int p = ImGui::GetTextLineHeight() * 0.5; ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(p, p)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0); @@ -1154,15 +1392,14 @@ bool MainMenuTabButton::Draw(bool selected) } MainMenuScene::MainMenuScene() -: m_animation(0.12, 0.12), - m_general_button("General", ICON_FA_GEARS), - m_input_button("Input", ICON_FA_GAMEPAD), - m_display_button("Display", ICON_FA_TV), - m_audio_button("Audio", ICON_FA_VOLUME_HIGH), - m_network_button("Network", ICON_FA_NETWORK_WIRED), - m_snapshots_button("Snapshots", ICON_FA_CLOCK_ROTATE_LEFT), - m_system_button("System", ICON_FA_MICROCHIP), - m_about_button("About", ICON_FA_CIRCLE_INFO) + : m_animation(0.12, 0.12), m_general_button("General", ICON_FA_GEARS), + m_input_button("Input", ICON_FA_GAMEPAD), + m_display_button("Display", ICON_FA_TV), + m_audio_button("Audio", ICON_FA_VOLUME_HIGH), + m_network_button("Network", ICON_FA_NETWORK_WIRED), + m_snapshots_button("Snapshots", ICON_FA_CLOCK_ROTATE_LEFT), + m_system_button("System", ICON_FA_MICROCHIP), + m_about_button("About", ICON_FA_CIRCLE_INFO) { m_had_focus_last_frame = false; m_focus_view = false; @@ -1249,11 +1486,12 @@ void MainMenuScene::HandleInput() bool focus = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows | ImGuiFocusedFlags_NoPopupHierarchy); - // XXX: Ensure we have focus for two frames. If a user cancels a popup window, we do not want to cancel main + // XXX: Ensure we have focus for two frames. If a user cancels a popup + // window, we do not want to cancel main // window as well. if (nofocus || (focus && m_had_focus_last_frame && - (ImGui::IsKeyDown(ImGuiKey_GamepadFaceRight) - || ImGui::IsKeyDown(ImGuiKey_Escape)))) { + (ImGui::IsKeyDown(ImGuiKey_GamepadFaceRight) || + ImGui::IsKeyDown(ImGuiKey_Escape)))) { Hide(); return; } @@ -1313,9 +1551,10 @@ bool MainMenuScene::Draw() float nav_width = width * 0.3; float content_width = width - nav_width; - ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(26,26,26,255)); + ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(26, 26, 26, 255)); - ImGui::BeginChild("###MainWindowNav", ImVec2(nav_width, -1), true, ImGuiWindowFlags_NavFlattened); + ImGui::BeginChild("###MainWindowNav", ImVec2(nav_width, -1), true, + ImGuiWindowFlags_NavFlattened); bool move_focus_to_tab = false; if (m_current_view_index != m_next_view_index) { @@ -1349,7 +1588,8 @@ bool MainMenuScene::Draw() int s = ImGui::GetTextLineHeight() * 0.75; ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(s, s)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(s, s)); - ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 6*g_viewport_mgr.m_scale); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, + 6 * g_viewport_mgr.m_scale); ImGui::PushID(m_current_view_index); ImGui::BeginChild("###MainWindowContent", ImVec2(content_width, -1), @@ -1364,7 +1604,9 @@ bool MainMenuScene::Draw() ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 128)); ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32_BLACK_TRANS); ImVec2 pos = ImGui::GetCursorPos(); - ImGui::SetCursorPosX(ImGui::GetContentRegionMax().x - style.FramePadding.x * 2.0f - ImGui::GetTextLineHeight()); + ImGui::SetCursorPosX(ImGui::GetContentRegionMax().x - + style.FramePadding.x * 2.0f - + ImGui::GetTextLineHeight()); if (ImGui::Button(ICON_FA_XMARK)) { Hide(); }