From ba39535acf0f34b6a1244e8f6b2c7b37b9d93508 Mon Sep 17 00:00:00 2001 From: iris Date: Fri, 8 May 2026 14:27:37 +0200 Subject: [PATCH] first work on remappable inputs --- CMakeLists.txt | 5 +- external/imgui/imconfig.h | 1 + src/backend/core/mmio/PIF.hpp | 413 +++++---- src/frontend/EmuThread.cpp | 73 +- src/frontend/EmuThread.hpp | 24 +- src/frontend/Gamepad.hpp | 80 ++ src/frontend/ImGuiImpl/GUI.hpp | 232 +++-- src/frontend/ImGuiImpl/ProgressIndicators.hpp | 57 +- src/frontend/ImGuiImpl/SettingsTab.hpp | 8 +- src/frontend/ImGuiImpl/StatusBar.hpp | 33 +- src/frontend/KaizenGui.cpp | 809 +++++++++--------- src/frontend/KaizenGui.hpp | 77 +- src/frontend/NativeWindow.hpp | 28 - src/frontend/RenderWidget.cpp | 12 +- src/frontend/RenderWidget.hpp | 89 +- src/frontend/Settings/AudioSettings.cpp | 50 +- src/frontend/Settings/AudioSettings.hpp | 10 +- src/frontend/Settings/CPUSettings.hpp | 6 +- src/frontend/Settings/GeneralSettings.cpp | 50 +- src/frontend/Settings/GeneralSettings.hpp | 11 +- src/frontend/SettingsWindow.cpp | 66 +- src/frontend/SettingsWindow.hpp | 34 +- src/frontend/Window.hpp | 23 + src/frontend/main.cpp | 20 +- src/utils/Options.cpp | 18 +- src/utils/Options.hpp | 33 +- 26 files changed, 1224 insertions(+), 1038 deletions(-) create mode 100644 src/frontend/Gamepad.hpp delete mode 100644 src/frontend/NativeWindow.hpp create mode 100644 src/frontend/Window.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e25eb1..f06c4db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,11 +171,12 @@ add_executable(kaizen src/frontend/Settings/CPUSettings.cpp src/frontend/Settings/AudioSettings.hpp src/frontend/Settings/AudioSettings.cpp - src/frontend/NativeWindow.hpp + src/frontend/Window.hpp src/utils/Options.cpp src/utils/File.cpp src/frontend/Debugger.hpp - src/frontend/Debugger.cpp) + src/frontend/Debugger.cpp + src/frontend/Gamepad.hpp) if (WIN32) diff --git a/external/imgui/imconfig.h b/external/imgui/imconfig.h index 0d843be..a1bd2c0 100644 --- a/external/imgui/imconfig.h +++ b/external/imgui/imconfig.h @@ -13,6 +13,7 @@ //----------------------------------------------------------------------------- #pragma once +#define IMGUI_IMPL_VULKAN_USE_VOLK //---- Define assertion handler. Defaults to calling assert(). // - If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. diff --git a/src/backend/core/mmio/PIF.hpp b/src/backend/core/mmio/PIF.hpp index c7505fe..7adb6c0 100644 --- a/src/backend/core/mmio/PIF.hpp +++ b/src/backend/core/mmio/PIF.hpp @@ -14,220 +14,265 @@ namespace n64 { enum AccessoryType : u8 { ACCESSORY_NONE, ACCESSORY_MEMPACK, ACCESSORY_RUMBLE_PACK }; struct Controller { - union { - struct { - union { - u8 byte1; + union { struct { - bool dpRight : 1; - bool dpLeft : 1; - bool dpDown : 1; - bool dpUp : 1; - bool start : 1; - bool z : 1; - bool b : 1; - bool a : 1; - }; - }; - union { - u8 byte2; - struct { - bool cRight : 1; - bool cLeft : 1; - bool cDown : 1; - bool cUp : 1; - bool r : 1; - bool l : 1; - bool zero : 1; - bool joyReset : 1; - }; - }; + union { + u8 byte1; + struct { + bool dpRight : 1; + bool dpLeft : 1; + bool dpDown : 1; + bool dpUp : 1; + bool start : 1; + bool z : 1; + bool b : 1; + bool a : 1; + }; + }; + union { + u8 byte2; + struct { + bool cRight : 1; + bool cLeft : 1; + bool cDown : 1; + bool cUp : 1; + bool r : 1; + bool l : 1; + bool zero : 1; + bool joyReset : 1; + }; + }; - s8 joyX; - s8 joyY; + s8 joyX; + s8 joyY; + }; + + u32 raw; }; + Controller &operator=(const Controller &other) { + byte1 = other.byte1; + byte2 = other.byte2; + joyX = other.joyX; + joyY = other.joyY; - u32 raw; - }; - Controller &operator=(const Controller &other) { - byte1 = other.byte1; - byte2 = other.byte2; - joyX = other.joyX; - joyY = other.joyY; - - return *this; - } - - enum Key { A, B, Z, Start, DUp, DDown, DLeft, DRight, CUp, CDown, CLeft, CRight, LT, RT }; - - enum Axis { X, Y }; - - Controller() = default; - void UpdateButton(Key k, bool state) { - switch (k) { - case A: - a = state; - break; - case B: - b = state; - break; - case Z: - z = state; - break; - case Start: - start = state; - break; - case DUp: - dpUp = state; - break; - case DDown: - dpDown = state; - break; - case DLeft: - dpLeft = state; - break; - case DRight: - dpRight = state; - break; - case CUp: - cUp = state; - break; - case CDown: - cDown = state; - break; - case CLeft: - cLeft = state; - break; - case CRight: - cRight = state; - break; - case LT: - l = state; - break; - case RT: - r = state; - break; + return *this; } - } - void UpdateAxis(Axis a, s8 state) { - switch (a) { - case X: - joyX = state; - break; - case Y: - joyY = state; - break; + enum Key { A, B, Z, Start, DUp, DDown, DLeft, DRight, CUp, CDown, CLeft, CRight, LT, RT }; + + enum Axis { X, Y }; + + Controller() = default; + void UpdateButton(Key k, bool state) { + switch (k) { + case A: + a = state; + break; + case B: + b = state; + break; + case Z: + z = state; + break; + case Start: + start = state; + break; + case DUp: + dpUp = state; + break; + case DDown: + dpDown = state; + break; + case DLeft: + dpLeft = state; + break; + case DRight: + dpRight = state; + break; + case CUp: + cUp = state; + break; + case CDown: + cDown = state; + break; + case CLeft: + cLeft = state; + break; + case CRight: + cRight = state; + break; + case LT: + l = state; + break; + case RT: + r = state; + break; + } } - } - Controller &operator=(u32 v) { - joyY = v & 0xff; - joyX = v >> 8; - byte2 = v >> 16; - byte1 = v >> 24; + void UpdateAxis(Axis a, s8 state) { + switch (a) { + case X: + joyX = state; + break; + case Y: + joyY = state; + break; + } + } - return *this; - } + Controller &operator=(u32 v) { + joyY = v & 0xff; + joyX = v >> 8; + byte2 = v >> 16; + byte1 = v >> 24; + + return *this; + } + + static const char *as_c_str(Axis a) { + switch (a) { + case X: + return "X-Axis"; + case Y: + return "Y-Axis"; + break; + } + } + + static const char *as_c_str(Key k) { + switch (k) { + case A: + return "A"; + case B: + return "B"; + case Z: + return "Z"; + case Start: + return "Start"; + case DUp: + return "Up"; + case DDown: + return "Down"; + case DLeft: + return "Left"; + case DRight: + return "Right"; + case CUp: + return "C-Up"; + case CDown: + return "C-Down"; + case CLeft: + return "C-Left"; + case CRight: + return "C-Right"; + case LT: + return "Left Trigger"; + case RT: + return "Right Trigger"; + break; + } + } }; static_assert(sizeof(Controller) == 4); enum JoybusType : u8 { - JOYBUS_NONE, - JOYBUS_CONTROLLER, - JOYBUS_DANCEPAD, - JOYBUS_VRU, - JOYBUS_MOUSE, - JOYBUS_RANDNET_KEYBOARD, - JOYBUS_DENSHA_DE_GO, - JOYBUS_4KB_EEPROM, - JOYBUS_16KB_EEPROM + JOYBUS_NONE, + JOYBUS_CONTROLLER, + JOYBUS_DANCEPAD, + JOYBUS_VRU, + JOYBUS_MOUSE, + JOYBUS_RANDNET_KEYBOARD, + JOYBUS_DENSHA_DE_GO, + JOYBUS_4KB_EEPROM, + JOYBUS_16KB_EEPROM }; struct JoybusDevice { - JoybusType type{}; - AccessoryType accessoryType{}; - Controller controller{}; + JoybusType type{}; + AccessoryType accessoryType{}; + Controller controller{}; - JoybusDevice() = default; + JoybusDevice() = default; }; // https://github.com/ares-emulator/ares/blob/master/ares/n64/cic/cic.cpp // https://github.com/ares-emulator/ares/blob/master/LICENSE constexpr u32 cicSeeds[] = { - 0x0, - 0x00043F3F, // CIC_NUS_6101 - 0x00043F3F, // CIC_NUS_7102 - 0x00043F3F, // CIC_NUS_6102_7101 - 0x00047878, // CIC_NUS_6103_7103 - 0x00049191, // CIC_NUS_6105_7105 - 0x00048585, // CIC_NUS_6106_7106 + 0x0, + 0x00043F3F, // CIC_NUS_6101 + 0x00043F3F, // CIC_NUS_7102 + 0x00043F3F, // CIC_NUS_6102_7101 + 0x00047878, // CIC_NUS_6103_7103 + 0x00049191, // CIC_NUS_6105_7105 + 0x00048585, // CIC_NUS_6106_7106 }; enum CICType { - UNKNOWN_CIC_TYPE, - CIC_NUS_6101, - CIC_NUS_7102, - CIC_NUS_6102_7101, - CIC_NUS_6103_7103, - CIC_NUS_6105_7105, - CIC_NUS_6106_7106 + UNKNOWN_CIC_TYPE, + CIC_NUS_6101, + CIC_NUS_7102, + CIC_NUS_6102_7101, + CIC_NUS_6103_7103, + CIC_NUS_6105_7105, + CIC_NUS_6106_7106 }; struct PIF { - void Reset(); - void MaybeLoadMempak(); - void LoadEeprom(SaveType, const std::string &); - void ProcessCommands(); - void InitDevices(SaveType); - void CICChallenge(); - void Execute() const; - void HLE(bool pal, CICType cicType) const; - bool ReadButtons(u8 *); - void ControllerID(u8 *) const; - void MempakRead(const u8 *, u8 *); - void MempakWrite(u8 *, u8 *); - void EepromRead(const u8 *, u8 *) const; - void EepromWrite(const u8 *, u8 *); - void UpdateButton(int index, Controller::Key k, bool state) { - joybusDevices[index].controller.UpdateButton(k, state); - } - - void UpdateAxis(int index, Controller::Axis a, s8 state) { joybusDevices[index].controller.UpdateAxis(a, state); } - - bool mempakOpen = false; - std::array bootrom{}; - std::array ram{}; - int channel = 0; - std::array joybusDevices{}; - mio::mmap_sink mempak, eeprom; - std::string mempakPath{}, eepromPath{}; - size_t eepromSize{}; - MupenMovie movie; - - [[nodiscard]] FORCE_INLINE u8 Read(u32 addr) const { - addr &= 0x7FF; - if (addr < 0x7c0) - return bootrom[addr]; - return ram[addr & PIF_RAM_DSIZE]; - } - - FORCE_INLINE void Write(u32 addr, const u8 val) { - addr &= 0x7FF; - if (addr < 0x7c0) - return; - ram[addr & PIF_RAM_DSIZE] = val; - } - - [[nodiscard]] FORCE_INLINE AccessoryType GetAccessoryType() const { - if (channel >= 4 || joybusDevices[channel].type != JOYBUS_CONTROLLER) { - return ACCESSORY_NONE; + void Reset(); + void MaybeLoadMempak(); + void LoadEeprom(SaveType, const std::string &); + void ProcessCommands(); + void InitDevices(SaveType); + void CICChallenge(); + void Execute() const; + void HLE(bool pal, CICType cicType) const; + bool ReadButtons(u8 *); + void ControllerID(u8 *) const; + void MempakRead(const u8 *, u8 *); + void MempakWrite(u8 *, u8 *); + void EepromRead(const u8 *, u8 *) const; + void EepromWrite(const u8 *, u8 *); + void UpdateButton(int index, Controller::Key k, bool state) { + joybusDevices[index].controller.UpdateButton(k, state); } - return joybusDevices[channel].accessoryType; - } -private: - void ConfigureJoyBusFrame(); + void UpdateAxis(int index, Controller::Axis a, s8 state) { joybusDevices[index].controller.UpdateAxis(a, state); } + + bool mempakOpen = false; + std::array bootrom{}; + std::array ram{}; + int channel = 0; + std::array joybusDevices{}; + mio::mmap_sink mempak, eeprom; + std::string mempakPath{}, eepromPath{}; + size_t eepromSize{}; + MupenMovie movie; + + [[nodiscard]] FORCE_INLINE u8 Read(u32 addr) const { + addr &= 0x7FF; + if (addr < 0x7c0) + return bootrom[addr]; + return ram[addr & PIF_RAM_DSIZE]; + } + + FORCE_INLINE void Write(u32 addr, const u8 val) { + addr &= 0x7FF; + if (addr < 0x7c0) + return; + ram[addr & PIF_RAM_DSIZE] = val; + } + + [[nodiscard]] FORCE_INLINE AccessoryType GetAccessoryType() const { + if (channel >= 4 || joybusDevices[channel].type != JOYBUS_CONTROLLER) { + return ACCESSORY_NONE; + } + + return joybusDevices[channel].accessoryType; + } + + private: + void ConfigureJoyBusFrame(); }; } // namespace n64 diff --git a/src/frontend/EmuThread.cpp b/src/frontend/EmuThread.cpp index 0a83ce7..c6ca99f 100644 --- a/src/frontend/EmuThread.cpp +++ b/src/frontend/EmuThread.cpp @@ -1,53 +1,50 @@ #include -#include +#include #include EmuThread::EmuThread(double &fps, SettingsWindow &settings) noexcept : settings(settings), fps(fps) {} void EmuThread::run() const noexcept { - n64::Core& core = n64::Core::GetInstance(); - if(!core.romLoaded) return; - - auto lastSample = std::chrono::high_resolution_clock::now(); - auto avgFps = 16.667; - auto sampledFps = 0; - static bool oneSecondPassed = false; + n64::Core &core = n64::Core::GetInstance(); + if (!core.romLoaded) + return; - fps = 1000.0 / avgFps; + auto lastSample = std::chrono::high_resolution_clock::now(); + auto avgFps = 16.667; + auto sampledFps = 0; + static bool oneSecondPassed = false; - const auto startFrameTime = std::chrono::high_resolution_clock::now(); - if (!core.pause) { - core.Run(settings.getVolumeL(), settings.getVolumeR()); - } - - const auto endFrameTime = std::chrono::high_resolution_clock::now(); - using namespace std::chrono_literals; - const auto frameTimeMs = std::chrono::duration(endFrameTime - startFrameTime) / 1ms; - avgFps += frameTimeMs; - - sampledFps++; - - if (const auto elapsedSinceLastSample = std::chrono::duration(endFrameTime - lastSample) / 1s; - elapsedSinceLastSample >= 1.0) { - if (!oneSecondPassed) { - oneSecondPassed = true; - return; - } - avgFps /= sampledFps; fps = 1000.0 / avgFps; - } + + const auto startFrameTime = std::chrono::high_resolution_clock::now(); + if (!core.pause) { + core.Run(settings.getVolumeL(), settings.getVolumeR()); + } + + const auto endFrameTime = std::chrono::high_resolution_clock::now(); + using namespace std::chrono_literals; + const auto frameTimeMs = std::chrono::duration(endFrameTime - startFrameTime) / 1ms; + avgFps += frameTimeMs; + + sampledFps++; + + if (const auto elapsedSinceLastSample = std::chrono::duration(endFrameTime - lastSample) / 1s; + elapsedSinceLastSample >= 1.0) { + if (!oneSecondPassed) { + oneSecondPassed = true; + return; + } + avgFps /= sampledFps; + fps = 1000.0 / avgFps; + } } -void EmuThread::TogglePause() const noexcept { - n64::Core::GetInstance().TogglePause(); -} +void EmuThread::TogglePause() const noexcept { n64::Core::GetInstance().TogglePause(); } -void EmuThread::Reset() const noexcept { - n64::Core::GetInstance().Reset(); -} +void EmuThread::Reset() const noexcept { n64::Core::GetInstance().Reset(); } void EmuThread::Stop() const noexcept { - n64::Core& core = n64::Core::GetInstance(); - core.Stop(); - core.rom = {}; + n64::Core &core = n64::Core::GetInstance(); + core.Stop(); + core.rom = {}; } diff --git a/src/frontend/EmuThread.hpp b/src/frontend/EmuThread.hpp index 6172867..79d6cb3 100644 --- a/src/frontend/EmuThread.hpp +++ b/src/frontend/EmuThread.hpp @@ -1,23 +1,23 @@ #pragma once #include #include -#include namespace n64 { struct Core; } class EmuThread final { - bool started = false; -public: - explicit EmuThread(double &, SettingsWindow &) noexcept; - ~EmuThread() = default; - void run() const noexcept; - void TogglePause() const noexcept; - void Reset() const noexcept; - void Stop() const noexcept; + bool started = false; - bool interruptionRequested = false, parallelRDPInitialized = false; - SettingsWindow &settings; - double& fps; + public: + explicit EmuThread(double &, SettingsWindow &) noexcept; + ~EmuThread() = default; + void run() const noexcept; + void TogglePause() const noexcept; + void Reset() const noexcept; + void Stop() const noexcept; + + bool interruptionRequested = false, parallelRDPInitialized = false; + SettingsWindow &settings; + double &fps; }; diff --git a/src/frontend/Gamepad.hpp b/src/frontend/Gamepad.hpp new file mode 100644 index 0000000..1cee4ad --- /dev/null +++ b/src/frontend/Gamepad.hpp @@ -0,0 +1,80 @@ +#pragma once +#include +#include +#include +#include + +struct Gamepad { + private: + struct MapEntry { + bool isAxis; + int sdlVal; + std::variant emuVal; + + const char *as_c_str() { + if (std::holds_alternative(emuVal)) { + switch (std::get(emuVal)) { + case n64::Controller::A: + return "A"; + case n64::Controller::B: + return "B"; + case n64::Controller::Z: + return "Z"; + case n64::Controller::Start: + return "Start"; + case n64::Controller::DUp: + return "DUp"; + case n64::Controller::DDown: + return "DDown"; + case n64::Controller::DLeft: + return "DLeft"; + case n64::Controller::DRight: + return "DRight"; + case n64::Controller::CUp: + return "CUp"; + case n64::Controller::CDown: + return "CDown"; + case n64::Controller::CLeft: + return "CLeft"; + case n64::Controller::CRight: + return "CRight"; + case n64::Controller::LT: + return "LT"; + case n64::Controller::RT: + return "RT"; + } + } + + switch (std::get(emuVal)) { + case n64::Controller::X: + return "X"; + case n64::Controller::Y: + return "Y"; + } + } + } entries[14] = { + {false, SDL_GAMEPAD_BUTTON_SOUTH, n64::Controller::A}, + {false, SDL_GAMEPAD_BUTTON_WEST, n64::Controller::B}, + {false, SDL_GAMEPAD_BUTTON_START, n64::Controller::Start}, + {false, SDL_GAMEPAD_BUTTON_DPAD_UP, n64::Controller::DUp}, + {false, SDL_GAMEPAD_BUTTON_DPAD_DOWN, n64::Controller::DDown}, + {false, SDL_GAMEPAD_BUTTON_DPAD_LEFT, n64::Controller::DLeft}, + {false, SDL_GAMEPAD_BUTTON_DPAD_RIGHT, n64::Controller::DRight}, + {false, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, n64::Controller::LT}, + {false, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, n64::Controller::RT}, + {false, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, n64::Controller::Z}, + {true, SDL_GAMEPAD_AXIS_RIGHTX, n64::Controller::X}, + {true, SDL_GAMEPAD_AXIS_RIGHTY, n64::Controller::Y}, + {true, SDL_GAMEPAD_AXIS_LEFTX, n64::Controller::X}, + {true, SDL_GAMEPAD_AXIS_LEFTY, n64::Controller::Y}, + }; + + public: + void SetValue(const MapEntry &); + void Serialize() { + auto &options = Options::GetInstance(); + for(const auto& entry : entries) { + options.SetValue(, const std::string &field, const T &value) + } + } +}; diff --git a/src/frontend/ImGuiImpl/GUI.hpp b/src/frontend/ImGuiImpl/GUI.hpp index 293a36b..45a9fdd 100644 --- a/src/frontend/ImGuiImpl/GUI.hpp +++ b/src/frontend/ImGuiImpl/GUI.hpp @@ -1,10 +1,8 @@ #pragma once -#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES #include #include #include #include -#include namespace gui { static VkAllocationCallbacks *g_Allocator = NULL; @@ -20,138 +18,138 @@ static ImGui_ImplVulkanH_Window g_MainWindowData; static uint32_t g_MinImageCount = 2; static void CheckVkResult(VkResult err) { - if (err == VK_SUCCESS) - return; + if (err == VK_SUCCESS) + return; - if (err < VK_SUCCESS) - panic("[vulkan] VkResult = {}", (int)err); + if (err < VK_SUCCESS) + panic("[vulkan] VkResult = {}", (int)err); - warn("[vulkan] VkResult = {}", (int)err); + warn("[vulkan] VkResult = {}", (int)err); } inline void Initialize(const std::shared_ptr &wsi, SDL_Window *nativeWindow) { - VkResult err; - // Setup Dear ImGui context - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO &io = ImGui::GetIO(); - (void)io; - io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; - // io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - // io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + VkResult err; + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO &io = ImGui::GetIO(); + (void)io; + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + // io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + // io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls - // Setup Dear ImGui style - ImGui::StyleColorsDark(); - // ImGui::StyleColorsClassic(); + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + // ImGui::StyleColorsClassic(); - g_Instance = wsi->get_context().get_instance(); - g_PhysicalDevice = wsi->get_device().get_physical_device(); - g_Device = wsi->get_device().get_device(); - g_QueueFamily = wsi->get_context().get_queue_info().family_indices[Vulkan::QUEUE_INDEX_GRAPHICS]; - g_Queue = wsi->get_context().get_queue_info().queues[Vulkan::QUEUE_INDEX_GRAPHICS]; - g_PipelineCache = nullptr; - g_DescriptorPool = nullptr; - g_Allocator = nullptr; - g_MinImageCount = 2; + g_Instance = wsi->get_context().get_instance(); + g_PhysicalDevice = wsi->get_device().get_physical_device(); + g_Device = wsi->get_device().get_device(); + g_QueueFamily = wsi->get_context().get_queue_info().family_indices[Vulkan::QUEUE_INDEX_GRAPHICS]; + g_Queue = wsi->get_context().get_queue_info().queues[Vulkan::QUEUE_INDEX_GRAPHICS]; + g_PipelineCache = nullptr; + g_DescriptorPool = nullptr; + g_Allocator = nullptr; + g_MinImageCount = 2; - // Create Descriptor Pool - { - VkDescriptorPoolSize pool_sizes[] = {{VK_DESCRIPTOR_TYPE_SAMPLER, 1000}, - {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000}, - {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000}, - {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000}, - {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000}, - {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000}, - {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000}, - {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000}, - {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000}, - {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000}, - {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000}}; - VkDescriptorPoolCreateInfo pool_info = {}; - pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; - pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes); - pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); - pool_info.pPoolSizes = pool_sizes; - err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); - CheckVkResult(err); - } + // Create Descriptor Pool + { + VkDescriptorPoolSize pool_sizes[] = {{VK_DESCRIPTOR_TYPE_SAMPLER, 1000}, + {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000}, + {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000}, + {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000}, + {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000}, + {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000}, + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000}, + {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000}, + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000}, + {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000}, + {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000}}; + VkDescriptorPoolCreateInfo pool_info = {}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; + pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes); + pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); + pool_info.pPoolSizes = pool_sizes; + err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); + CheckVkResult(err); + } - // Create the Render Pass - VkRenderPass renderPass; - { - VkAttachmentDescription attachment = {}; - attachment.format = wsi->get_device().get_swapchain_view().get_format(); - attachment.samples = VK_SAMPLE_COUNT_1_BIT; - attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - VkAttachmentReference color_attachment = {}; - color_attachment.attachment = 0; - color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_attachment; - VkSubpassDependency dependency = {}; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - VkRenderPassCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - info.attachmentCount = 1; - info.pAttachments = &attachment; - info.subpassCount = 1; - info.pSubpasses = &subpass; - info.dependencyCount = 1; - info.pDependencies = &dependency; - err = vkCreateRenderPass(g_Device, &info, g_Allocator, &renderPass); - CheckVkResult(err); - } - // Setup Platform/Renderer backends - ImGui_ImplSDL3_InitForVulkan(nativeWindow); - ImGui_ImplVulkan_InitInfo init_info = {}; - init_info.Instance = g_Instance; - init_info.PhysicalDevice = g_PhysicalDevice; - init_info.Device = g_Device; - init_info.QueueFamily = g_QueueFamily; - init_info.Queue = g_Queue; - init_info.PipelineCache = g_PipelineCache; - init_info.DescriptorPool = g_DescriptorPool; - init_info.Allocator = g_Allocator; - init_info.MinImageCount = g_MinImageCount; - init_info.ImageCount = 2; - init_info.CheckVkResultFn = CheckVkResult; - init_info.PipelineInfoMain.RenderPass = renderPass; - init_info.ApiVersion = VK_API_VERSION_1_3; + // Create the Render Pass + VkRenderPass renderPass; + { + VkAttachmentDescription attachment = {}; + attachment.format = wsi->get_device().get_swapchain_view().get_format(); + attachment.samples = VK_SAMPLE_COUNT_1_BIT; + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + VkAttachmentReference color_attachment = {}; + color_attachment.attachment = 0; + color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment; + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + VkRenderPassCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + info.attachmentCount = 1; + info.pAttachments = &attachment; + info.subpassCount = 1; + info.pSubpasses = &subpass; + info.dependencyCount = 1; + info.pDependencies = &dependency; + err = vkCreateRenderPass(g_Device, &info, g_Allocator, &renderPass); + CheckVkResult(err); + } + // Setup Platform/Renderer backends + ImGui_ImplSDL3_InitForVulkan(nativeWindow); + ImGui_ImplVulkan_InitInfo init_info = {}; + init_info.Instance = g_Instance; + init_info.PhysicalDevice = g_PhysicalDevice; + init_info.Device = g_Device; + init_info.QueueFamily = g_QueueFamily; + init_info.Queue = g_Queue; + init_info.PipelineCache = g_PipelineCache; + init_info.DescriptorPool = g_DescriptorPool; + init_info.Allocator = g_Allocator; + init_info.MinImageCount = g_MinImageCount; + init_info.ImageCount = 2; + init_info.CheckVkResultFn = CheckVkResult; + init_info.PipelineInfoMain.RenderPass = renderPass; + init_info.ApiVersion = VK_API_VERSION_1_3; - ImGui_ImplVulkan_LoadFunctions( - VK_API_VERSION_1_3, - [](const char *function_name, void *vulkan_instance) { - return vkGetInstanceProcAddr((reinterpret_cast(vulkan_instance)), function_name); - }, - g_Instance); + ImGui_ImplVulkan_LoadFunctions( + VK_API_VERSION_1_3, + [](const char *function_name, void *vulkan_instance) { + return vkGetInstanceProcAddr((reinterpret_cast(vulkan_instance)), function_name); + }, + g_Instance); - if (!ImGui_ImplVulkan_Init(&init_info)) - panic("Failed to initialize ImGui!"); + if (!ImGui_ImplVulkan_Init(&init_info)) + panic("Failed to initialize ImGui!"); } inline void StartFrame() { - ImGui_ImplVulkan_NewFrame(); - ImGui_ImplSDL3_NewFrame(); - ImGui::NewFrame(); + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); } inline void Cleanup() { - ImGui_ImplVulkan_Shutdown(); - ImGui_ImplSDL3_Shutdown(); - ImGui::DestroyContext(); + ImGui_ImplVulkan_Shutdown(); + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); } } // namespace gui diff --git a/src/frontend/ImGuiImpl/ProgressIndicators.hpp b/src/frontend/ImGuiImpl/ProgressIndicators.hpp index fcd7b74..05a0f7e 100644 --- a/src/frontend/ImGuiImpl/ProgressIndicators.hpp +++ b/src/frontend/ImGuiImpl/ProgressIndicators.hpp @@ -1,43 +1,44 @@ #pragma once #include #include +#include namespace ImGui { -inline bool Spinner(const char* label, const float radius, const int thickness, const ImU32& color) { - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; +inline bool Spinner(const char *label, const float radius, const int thickness, const ImU32 &color) { + ImGuiWindow *window = GetCurrentWindow(); + if (window->SkipItems) + return false; - const ImGuiContext & g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); + const ImGuiContext &g = *GImGui; + const ImGuiStyle &style = g.Style; + const ImGuiID id = window->GetID(label); - const ImVec2 pos = window->DC.CursorPos; - const ImVec2 size(radius*2, (radius + style.FramePadding.y)*2); + const ImVec2 pos = window->DC.CursorPos; + const ImVec2 size(radius * 2, (radius + style.FramePadding.y) * 2); - const ImRect bb(pos, ImVec2(pos.x + size.x, pos.y + size.y)); - ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(bb, id)) - return false; + const ImRect bb(pos, ImVec2(pos.x + size.x, pos.y + size.y)); + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(bb, id)) + return false; - // Render - window->DrawList->PathClear(); + // Render + window->DrawList->PathClear(); - constexpr int num_segments = 30; - const int start = abs(ImSin(g.Time*1.8f)*(num_segments-5)); + constexpr int num_segments = 30; + const int start = std::abs(ImSin(g.Time * 1.8f) * (num_segments - 5)); - const float a_min = IM_PI * 2.0f * static_cast(start) / static_cast(num_segments); - constexpr float a_max = IM_PI*2.0f * (static_cast(num_segments) -3) / static_cast(num_segments); + const float a_min = IM_PI * 2.0f * static_cast(start) / static_cast(num_segments); + constexpr float a_max = IM_PI * 2.0f * (static_cast(num_segments) - 3) / static_cast(num_segments); - const auto centre = ImVec2(pos.x+radius, pos.y+radius+style.FramePadding.y); + const auto centre = ImVec2(pos.x + radius, pos.y + radius + style.FramePadding.y); - for (int i = 0; i < num_segments; i++) { - const float a = a_min + static_cast(i) / static_cast(num_segments) * (a_max - a_min); - window->DrawList->PathLineTo(ImVec2(centre.x + ImCos(a+g.Time*8) * radius, - centre.y + ImSin(a+g.Time*8) * radius)); - } + for (int i = 0; i < num_segments; i++) { + const float a = a_min + static_cast(i) / static_cast(num_segments) * (a_max - a_min); + window->DrawList->PathLineTo( + ImVec2(centre.x + ImCos(a + g.Time * 8) * radius, centre.y + ImSin(a + g.Time * 8) * radius)); + } - window->DrawList->PathStroke(color, false, thickness); - return true; + window->DrawList->PathStroke(color, false, thickness); + return true; } -} \ No newline at end of file +} // namespace ImGui diff --git a/src/frontend/ImGuiImpl/SettingsTab.hpp b/src/frontend/ImGuiImpl/SettingsTab.hpp index de57919..d9fef82 100644 --- a/src/frontend/ImGuiImpl/SettingsTab.hpp +++ b/src/frontend/ImGuiImpl/SettingsTab.hpp @@ -1,8 +1,8 @@ #pragma once struct SettingsTab { - virtual ~SettingsTab() = default; - virtual void render() = 0; + virtual ~SettingsTab() = default; + virtual void render() = 0; - bool modified = false; -}; \ No newline at end of file + bool modified = false; +}; diff --git a/src/frontend/ImGuiImpl/StatusBar.hpp b/src/frontend/ImGuiImpl/StatusBar.hpp index 0a3dbf3..23fc881 100644 --- a/src/frontend/ImGuiImpl/StatusBar.hpp +++ b/src/frontend/ImGuiImpl/StatusBar.hpp @@ -3,19 +3,22 @@ #include namespace ImGui { -inline bool BeginMainStatusBar() -{ - ImGuiContext& g = *GetCurrentContext(); - ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); +inline bool BeginMainStatusBar() { + ImGuiContext &g = *GetCurrentContext(); + ImGuiViewportP *viewport = (ImGuiViewportP *)(void *)GetMainViewport(); // Notify of viewport change so GetFrameHeight() can be accurate in case of DPI change SetCurrentViewport(NULL, viewport); - // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. + // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be + // visible on a TV set. // FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea? - // FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings. - g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; + // FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV + // calibration in OS settings. + g.NextWindowData.MenuBarOffsetMinVal = ImVec2( + g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); + ImGuiWindowFlags window_flags = + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; float height = GetFrameHeight(); bool is_open = BeginViewportSideBar("##MainStatusBar", viewport, ImGuiDir_Down, height, window_flags); g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); @@ -27,16 +30,18 @@ inline bool BeginMainStatusBar() return is_open; } -inline void EndMainStatusBar() -{ +inline void EndMainStatusBar() { EndMenuBar(); - // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window + // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus + // to the previous window // FIXME: With this strategy we won't be able to restore a NULL focus. - ImGuiContext& g = *GImGui; + ImGuiContext &g = *GImGui; if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest) - FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL, ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild); + FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL, + ImGuiFocusRequestFlags_UnlessBelowModal | + ImGuiFocusRequestFlags_RestoreFocusedChild); End(); } -} \ No newline at end of file +} // namespace ImGui diff --git a/src/frontend/KaizenGui.cpp b/src/frontend/KaizenGui.cpp index dd4f4d8..5bc778d 100644 --- a/src/frontend/KaizenGui.cpp +++ b/src/frontend/KaizenGui.cpp @@ -5,456 +5,485 @@ #include #include -KaizenGui::KaizenGui() noexcept : window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()), emuThread(fpsCounter, settingsWindow) { - gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle()); - SDL_InitSubSystem(SDL_INIT_GAMEPAD); +KaizenGui::KaizenGui() noexcept : + window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()), + emuThread(fpsCounter, settingsWindow) { + gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle()); + SDL_InitSubSystem(SDL_INIT_GAMEPAD); - SDL_AddGamepadMapping(gamecontrollerdb_str); + SDL_AddGamepadMapping(gamecontrollerdb_str); } KaizenGui::~KaizenGui() { - gui::Cleanup(); - SDL_Quit(); + gui::Cleanup(); + SDL_Quit(); } void KaizenGui::QueryDevices(const SDL_Event &event) { - switch (event.type) { - case SDL_EVENT_GAMEPAD_ADDED: - if (!gamepad) { - const auto index = event.gdevice.which; + switch (event.type) { + case SDL_EVENT_GAMEPAD_ADDED: + if (!gamepad) { + const auto index = event.gdevice.which; - gamepad = SDL_OpenGamepad(index); - info("Found controller!"); - info("Name: {}", SDL_GetGamepadName(gamepad)); - info("Vendor: {}", SDL_GetGamepadVendor(gamepad)); + gamepad = SDL_OpenGamepad(index); + info("Found controller!"); + info("Name: {}", SDL_GetGamepadName(gamepad)); + info("Vendor: {}", SDL_GetGamepadVendor(gamepad)); + } + break; + case SDL_EVENT_GAMEPAD_REMOVED: + if (gamepad) + SDL_CloseGamepad(gamepad); + break; + default: + break; } - break; - case SDL_EVENT_GAMEPAD_REMOVED: - if (gamepad) - SDL_CloseGamepad(gamepad); - break; - default: break; - } } void KaizenGui::HandleInput(const SDL_Event &event) { - const n64::Core& core = n64::Core::GetInstance(); - n64::PIF &pif = n64::Core::GetMem().mmio.si.pif; - switch(event.type) { + const n64::Core &core = n64::Core::GetInstance(); + n64::PIF &pif = n64::Core::GetMem().mmio.si.pif; + switch (event.type) { case SDL_EVENT_GAMEPAD_AXIS_MOTION: - if(!gamepad) + if (!gamepad) + break; + { + pif.UpdateButton(0, n64::Controller::Key::Z, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX || + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX); + pif.UpdateButton(0, n64::Controller::Key::CUp, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127); + pif.UpdateButton(0, n64::Controller::Key::CDown, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127); + pif.UpdateButton(0, n64::Controller::Key::CLeft, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127); + pif.UpdateButton(0, n64::Controller::Key::CRight, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127); + + float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); + if (xclamped < 0) { + xclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MAX)); + } else { + xclamped /= SDL_JOYSTICK_AXIS_MAX; + } + + xclamped *= 86; + + float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); + if (yclamped < 0) { + yclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MIN)); + } else { + yclamped /= SDL_JOYSTICK_AXIS_MAX; + } + + yclamped *= 86; + + pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast(-yclamped)); + pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast(xclamped)); + } break; - { - pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX); - pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127); - pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127); - pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127); - pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127); - - float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); - if (xclamped < 0) { - xclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MAX)); - } else { - xclamped /= SDL_JOYSTICK_AXIS_MAX; - } - - xclamped *= 86; - - float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); - if (yclamped < 0) { - yclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MIN)); - } else { - yclamped /= SDL_JOYSTICK_AXIS_MAX; - } - - yclamped *= 86; - - pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast(-yclamped)); - pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast( xclamped)); - } - break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: - if(!gamepad) + if (!gamepad) + break; + + pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH)); + pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST)); + pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START)); + pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP)); + pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); + pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT)); + pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); + pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)); + pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)); break; - - pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH)); - pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST)); - pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START)); - pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP)); - pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); - pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT)); - pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); - pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)); - pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)); - break; case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: - { - const auto keys = SDL_GetKeyboardState(nullptr); - if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) { - fileDialogOpen = true; + { + const auto keys = SDL_GetKeyboardState(nullptr); + if ((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) { + fileDialogOpen = true; + } + + fastForward = keys[SDL_SCANCODE_SPACE]; + if (!unlockFramerate) + core.parallel.SetFramerateUnlocked(fastForward); + + if (core.romLoaded) { + if (keys[SDL_SCANCODE_P]) { + emuThread.TogglePause(); + } + + if (keys[SDL_SCANCODE_R]) { + emuThread.Reset(); + } + + if (keys[SDL_SCANCODE_Q]) { + emuThread.Stop(); + } + } + + if (gamepad) + break; + + pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]); + pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]); + pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]); + pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]); + pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]); + pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]); + pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]); + pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]); + pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]); + pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]); + pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]); + pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]); + pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]); + pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]); + + float x = 0, y = 0; + + if (keys[SDL_SCANCODE_UP]) + y = 86; + if (keys[SDL_SCANCODE_DOWN]) + y = -86; + if (keys[SDL_SCANCODE_LEFT]) + x = -86; + if (keys[SDL_SCANCODE_RIGHT]) + x = 86; + + pif.UpdateAxis(0, n64::Controller::Axis::X, x); + pif.UpdateAxis(0, n64::Controller::Axis::Y, y); } - - fastForward = keys[SDL_SCANCODE_SPACE]; - if(!unlockFramerate) - core.parallel.SetFramerateUnlocked(fastForward); - - if(core.romLoaded) { - if(keys[SDL_SCANCODE_P]) { - emuThread.TogglePause(); - } - - if(keys[SDL_SCANCODE_R]) { - emuThread.Reset(); - } - - if(keys[SDL_SCANCODE_Q]) { - emuThread.Stop(); - } - } - - if(gamepad) - break; - - pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]); - pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]); - pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]); - pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]); - pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]); - pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]); - pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]); - pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]); - pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]); - pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]); - pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]); - pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]); - pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]); - pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]); - - float x = 0, y = 0; - - if (keys[SDL_SCANCODE_UP]) y = 86; - if (keys[SDL_SCANCODE_DOWN]) y = -86; - if (keys[SDL_SCANCODE_LEFT]) x = -86; - if (keys[SDL_SCANCODE_RIGHT]) x = 86; - - pif.UpdateAxis(0, n64::Controller::Axis::X, x); - pif.UpdateAxis(0, n64::Controller::Axis::Y, y); - } - break; - default: break; - } + break; + default: + break; + } } std::pair, std::optional> RenderErrorMessageDetails() { - auto lastPC = Util::Error::GetLastPC(); - if(lastPC.has_value()) { - ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str()); - } + auto lastPC = Util::Error::GetLastPC(); + if (lastPC.has_value()) { + ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str()); + } - auto memoryAccess = Util::Error::GetMemoryAccess(); - if(memoryAccess.has_value()) { - const auto [is_write, size, address, written_val] = memoryAccess.value(); - ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", - static_cast(size), address, - is_write ? std::format(" (value = 0x{:X})", written_val) : "") - .c_str()); - } + auto memoryAccess = Util::Error::GetMemoryAccess(); + if (memoryAccess.has_value()) { + const auto [is_write, size, address, written_val] = memoryAccess.value(); + ImGui::Text("%s", + std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", static_cast(size), + address, is_write ? std::format(" (value = 0x{:X})", written_val) : "") + .c_str()); + } - return {lastPC, memoryAccess}; + return {lastPC, memoryAccess}; } void KaizenGui::RenderUI() { - n64::Core& core = n64::Core::GetInstance(); - gui::StartFrame(); + n64::Core &core = n64::Core::GetInstance(); + gui::StartFrame(); - if(ImGui::BeginMainMenuBar()) { - if(ImGui::BeginMenu("File")) { - if(ImGui::MenuItem("Open", "Ctrl-O")) { - fileDialogOpen = true; - } - if(ImGui::MenuItem("Exit")) { - quit = true; - emuThread.Stop(); - } - ImGui::EndMenu(); + if (ImGui::BeginMainMenuBar()) { + if (ImGui::BeginMenu("File")) { + if (ImGui::MenuItem("Open", "Ctrl-O")) { + fileDialogOpen = true; + } + if (ImGui::MenuItem("Exit")) { + quit = true; + emuThread.Stop(); + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Emulation")) { + ImGui::BeginDisabled(!core.romLoaded); + + if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) { + emuThread.TogglePause(); + } + + if (ImGui::MenuItem("Reset", "R")) { + emuThread.Reset(); + } + + if (ImGui::MenuItem("Stop", "Q")) { + emuThread.Stop(); + core.romLoaded = false; + } + + if (ImGui::Checkbox("Unlock framerate", &unlockFramerate)) { + core.parallel.SetFramerateUnlocked(unlockFramerate); + } + + if (ImGui::MenuItem("Open Debugger")) { + debugger.Open(); + } + + ImGui::EndDisabled(); + + if (ImGui::MenuItem("Options")) { + settingsWindow.isOpen = true; + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Help")) { + if (ImGui::MenuItem("About")) { + aboutOpen = true; + } + + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); } - if(ImGui::BeginMenu("Emulation")) { - ImGui::BeginDisabled(!core.romLoaded); - - if(ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) { - emuThread.TogglePause(); - } - - if(ImGui::MenuItem("Reset", "R")) { - emuThread.Reset(); - } - - if(ImGui::MenuItem("Stop", "Q")) { - emuThread.Stop(); - core.romLoaded = false; - } - - if(ImGui::Checkbox("Unlock framerate", &unlockFramerate)) { - core.parallel.SetFramerateUnlocked(unlockFramerate); - } - if(ImGui::MenuItem("Open Debugger")) { - debugger.Open(); - } - - ImGui::EndDisabled(); - - if(ImGui::MenuItem("Options")) { - settingsWindow.isOpen = true; - } - ImGui::EndMenu(); + if (!Util::Error::IsHandled()) { + ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str()); } - if(ImGui::BeginMenu("Help")) { - if(ImGui::MenuItem("About")) { - aboutOpen = true; - } - - ImGui::EndMenu(); + + if (settingsWindow.isOpen) { + ImGui::OpenPopup("Settings", ImGuiPopupFlags_None); } - ImGui::EndMainMenuBar(); - } - if(!Util::Error::IsHandled()) { - ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str()); - } - - if(settingsWindow.isOpen) { - ImGui::OpenPopup("Settings", ImGuiPopupFlags_None); - } - - if(aboutOpen) { - ImGui::OpenPopup("About Kaizen"); - } - - settingsWindow.render(); - debugger.render(); - - const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); - ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - - if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::Text("Kaizen is a Nintendo 64 emulator that strives"); - ImGui::Text("to offer a friendly user experience and compatibility."); - ImGui::Text("Kaizen is licensed under the BSD 3-clause license."); - ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd."); - ImGui::Separator(); - ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR); - ImGui::Separator(); - if(ImGui::Button("OK")) { - aboutOpen = false; - ImGui::CloseCurrentPopup(); + if (aboutOpen) { + ImGui::OpenPopup("About Kaizen"); } - - ImGui::EndPopup(); - } - ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + settingsWindow.render(); + debugger.render(); - if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { - emuThread.TogglePause(); - switch(Util::Error::GetSeverity().as_enum) { - case Util::Error::Severity::WARN: { - ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5); - ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1); - ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str()); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str()); - RenderErrorMessageDetails(); + const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) { - const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); - const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); - const bool chooseAnother = ImGui::Button("Choose another ROM"); - if(ignore || stop || chooseAnother) { - Util::Error::SetHandled(); + if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("Kaizen is a Nintendo 64 emulator that strives"); + ImGui::Text("to offer a friendly user experience and compatibility."); + ImGui::Text("Kaizen is licensed under the BSD 3-clause license."); + ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd."); + ImGui::Separator(); + ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR); + ImGui::Separator(); + if (ImGui::Button("OK")) { + aboutOpen = false; ImGui::CloseCurrentPopup(); - } - - if(ignore) { - emuThread.TogglePause(); - } - - if(stop || chooseAnother) { - emuThread.Stop(); - } - - if(chooseAnother) { - fileDialogOpen = true; - } - break; } - if(ImGui::Button("OK")) - ImGui::CloseCurrentPopup(); - } break; - case Util::Error::Severity::UNRECOVERABLE: { - emuThread.Stop(); - ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); - ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); - ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped..."); - ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); - RenderErrorMessageDetails(); - if(ImGui::Button("OK")) - ImGui::CloseCurrentPopup(); - } break; - case Util::Error::Severity::NON_FATAL: { - ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); - ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); - ImGui::Text("An error has occurred!"); - ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); - auto [lastPC, memoryAccess] = RenderErrorMessageDetails(); - - const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); - const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); - const bool chooseAnother = ImGui::Button("Choose another ROM"); - const bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false; - if(ignore || stop || chooseAnother || openInDebugger) { - Util::Error::SetHandled(); - ImGui::CloseCurrentPopup(); - } - - if(ignore) { - emuThread.TogglePause(); - } - - if(stop || chooseAnother) { - emuThread.Stop(); - } - - if(chooseAnother) { - fileDialogOpen = true; - } - - if(openInDebugger) { - if(!n64::Core::GetInstance().breakpoints.contains(lastPC.value())) - n64::Core::GetInstance().ToggleBreakpoint(lastPC.value()); - - debugger.Open(); - emuThread.Reset(); - } - } break; - default: break; + ImGui::EndPopup(); } - - ImGui::EndPopup(); - } - - if(ImGui::BeginMainStatusBar()) { - ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate); - ImGui::EndMainStatusBar(); - } - if (shouldDisplaySpinner) { - ImGui::SetNextWindowPos({static_cast(width) * 0.5f, static_cast(height) * 0.5f}, 0, ImVec2(0.5f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { + emuThread.TogglePause(); + switch (Util::Error::GetSeverity().as_enum) { + case Util::Error::Severity::WARN: + { + ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5); + ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1); + ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str()); + RenderErrorMessageDetails(); - ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive])); - ImGui::SameLine(); + if (n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) { + const bool ignore = ImGui::Button("Try continuing"); + ImGui::SameLine(); + const bool stop = ImGui::Button("Stop emulation"); + ImGui::SameLine(); + const bool chooseAnother = ImGui::Button("Choose another ROM"); + if (ignore || stop || chooseAnother) { + Util::Error::SetHandled(); + ImGui::CloseCurrentPopup(); + } - ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f); - ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str()); - ImGui::PopFont(); + if (ignore) { + emuThread.TogglePause(); + } - ImGui::End(); + if (stop || chooseAnother) { + emuThread.Stop(); + } - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); - } + if (chooseAnother) { + fileDialogOpen = true; + } + break; + } - ImGui::Render(); - if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); - } + if (ImGui::Button("OK")) + ImGui::CloseCurrentPopup(); + } + break; + case Util::Error::Severity::UNRECOVERABLE: + { + emuThread.Stop(); + ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); + ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); + ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped..."); + ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); + RenderErrorMessageDetails(); + if (ImGui::Button("OK")) + ImGui::CloseCurrentPopup(); + } + break; + case Util::Error::Severity::NON_FATAL: + { + ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); + ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); + ImGui::Text("An error has occurred!"); + ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); + auto [lastPC, memoryAccess] = RenderErrorMessageDetails(); - if(fileDialogOpen) { - fileDialogOpen = false; - constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; - SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) { - auto kaizen = static_cast(userdata); - - if (!filelist) { - panic("An error occured: {}", SDL_GetError()); - } + const bool ignore = ImGui::Button("Try continuing"); + ImGui::SameLine(); + const bool stop = ImGui::Button("Stop emulation"); + ImGui::SameLine(); + const bool chooseAnother = ImGui::Button("Choose another ROM"); + const bool openInDebugger = + lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false; + if (ignore || stop || chooseAnother || openInDebugger) { + Util::Error::SetHandled(); + ImGui::CloseCurrentPopup(); + } - if (!*filelist) { - warn("The user did not select any file."); - warn("Most likely, the dialog was canceled."); + if (ignore) { + emuThread.TogglePause(); + } + + if (stop || chooseAnother) { + emuThread.Stop(); + } + + if (chooseAnother) { + fileDialogOpen = true; + } + + if (openInDebugger) { + if (!n64::Core::GetInstance().breakpoints.contains(lastPC.value())) + n64::Core::GetInstance().ToggleBreakpoint(lastPC.value()); + + debugger.Open(); + emuThread.Reset(); + } + } + break; + default: + break; + } + + ImGui::EndPopup(); + } + + if (ImGui::BeginMainStatusBar()) { + ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate); + ImGui::EndMainStatusBar(); + } + + if (shouldDisplaySpinner) { + ImGui::SetNextWindowPos({static_cast(width) * 0.5f, static_cast(height) * 0.5f}, 0, + ImVec2(0.5f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + + ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + + ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive])); + ImGui::SameLine(); + + ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f); + ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str()); + ImGui::PopFont(); + + ImGui::End(); + + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + } + + ImGui::Render(); + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + + if (fileDialogOpen) { + fileDialogOpen = false; + constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, + {"Nintendo 64 executable", "n64;z64;v64"}, + {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; + SDL_ShowOpenFileDialog( + [](void *userdata, const char *const *filelist, int) { + auto kaizen = static_cast(userdata); + + if (!filelist) { + panic("An error occured: {}", SDL_GetError()); + } + + if (!*filelist) { + warn("The user did not select any file."); + warn("Most likely, the dialog was canceled."); + return; + } + + kaizen->fileToLoad = *filelist; + kaizen->shouldDisplaySpinner = true; + + std::thread fileWorker(&KaizenGui::FileWorker, kaizen); + fileWorker.detach(); + }, + this, window.getHandle(), filters, 3, nullptr, false); + } + + if (minimized) return; - } - kaizen->fileToLoad = *filelist; - kaizen->shouldDisplaySpinner = true; + if (core.romLoaded) { + core.parallel.UpdateScreen(); + return; + } - std::thread fileWorker(&KaizenGui::FileWorker, kaizen); - fileWorker.detach(); - }, this, window.getHandle(), filters, 3, nullptr, false); - } - - if(minimized) - return; - - if(core.romLoaded) { - core.parallel.UpdateScreen(); - return; - } - - core.parallel.UpdateScreen(); + core.parallel.UpdateScreen(); } void KaizenGui::LoadROM(const std::string &path) noexcept { - n64::Core& core = n64::Core::GetInstance(); - core.LoadROM(path); - const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB; - SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str()); + n64::Core &core = n64::Core::GetInstance(); + core.LoadROM(path); + const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB; + SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str()); } void KaizenGui::run() { - while(!quit) { - SDL_Event e; - while (SDL_PollEvent(&e)) { - ImGui_ImplSDL3_ProcessEvent(&e); - switch(e.type) { - case SDL_EVENT_QUIT: - quit = true; - emuThread.Stop(); - break; - case SDL_EVENT_WINDOW_MINIMIZED: - minimized = true; - break; - case SDL_EVENT_WINDOW_RESTORED: - minimized = false; - break; - default: - } - QueryDevices(e); - HandleInput(e); + while (!quit) { + SDL_Event e; + while (SDL_PollEvent(&e)) { + ImGui_ImplSDL3_ProcessEvent(&e); + switch (e.type) { + case SDL_EVENT_QUIT: + quit = true; + emuThread.Stop(); + break; + case SDL_EVENT_WINDOW_MINIMIZED: + minimized = true; + break; + case SDL_EVENT_WINDOW_RESTORED: + minimized = false; + break; + default: + } + QueryDevices(e); + HandleInput(e); + } + + SDL_GetWindowSize(window.getHandle(), &width, &height); + + emuThread.run(); + RenderUI(); } - - SDL_GetWindowSize(window.getHandle(), &width, &height); - - emuThread.run(); - RenderUI(); - } } -void KaizenGui::LoadTAS(const std::string &path) noexcept { - n64::Core::GetInstance().LoadTAS(fs::path(path)); -} \ No newline at end of file +void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); } diff --git a/src/frontend/KaizenGui.hpp b/src/frontend/KaizenGui.hpp index d55a78d..bce517e 100644 --- a/src/frontend/KaizenGui.hpp +++ b/src/frontend/KaizenGui.hpp @@ -1,49 +1,52 @@ #pragma once #include -#include +#include #include #include #include +#include class KaizenGui final { - gui::NativeWindow window; -public: - explicit KaizenGui() noexcept; - ~KaizenGui(); - - double fpsCounter = -1.0; - bool fastForward = false; - bool unlockFramerate = false; - bool minimized = false; + gui::Window window; - SettingsWindow settingsWindow; - RenderWidget vulkanWidget; - EmuThread emuThread; - Debugger debugger; + public: + explicit KaizenGui() noexcept; + ~KaizenGui(); - SDL_Gamepad* gamepad = nullptr; - - void run(); - static void LoadTAS(const std::string &path) noexcept; - void LoadROM(const std::string &path) noexcept; -private: - int width{}, height{}; - bool aboutOpen = false; - bool fileDialogOpen = false; - bool quit = false; - bool shouldDisplaySpinner = false; - std::string fileToLoad = ""; - void RenderUI(); - void HandleInput(const SDL_Event &event); - void QueryDevices(const SDL_Event &event); + double fpsCounter = -1.0; + bool fastForward = false; + bool unlockFramerate = false; + bool minimized = false; - [[noreturn]] void FileWorker() { - while (true) { - if (!fileToLoad.empty()) { - LoadROM(fileToLoad); - shouldDisplaySpinner = false; - fileToLoad = ""; - } + SettingsWindow settingsWindow; + RenderWidget vulkanWidget; + EmuThread emuThread; + Debugger debugger; + + SDL_Gamepad *gamepad = nullptr; + + void run(); + static void LoadTAS(const std::string &path) noexcept; + void LoadROM(const std::string &path) noexcept; + + private: + int width{}, height{}; + bool aboutOpen = false; + bool fileDialogOpen = false; + bool quit = false; + bool shouldDisplaySpinner = false; + std::string fileToLoad = ""; + void RenderUI(); + void HandleInput(const SDL_Event &event); + void QueryDevices(const SDL_Event &event); + + [[noreturn]] void FileWorker() { + while (true) { + if (!fileToLoad.empty()) { + LoadROM(fileToLoad); + shouldDisplaySpinner = false; + fileToLoad = ""; + } + } } - } }; diff --git a/src/frontend/NativeWindow.hpp b/src/frontend/NativeWindow.hpp deleted file mode 100644 index 279afcf..0000000 --- a/src/frontend/NativeWindow.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -namespace gui { -struct NativeWindow { - NativeWindow(const std::string& title, int w, int h, int posX = SDL_WINDOWPOS_CENTERED, int posY = SDL_WINDOWPOS_CENTERED) { - SDL_Init(SDL_INIT_VIDEO); - float scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); - window = SDL_CreateWindow(title.c_str(), w * scale, h * scale, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY); - - if(volkInitialize() != VK_SUCCESS) { - panic("Failed to initialize Volk!"); - } - } - - ~NativeWindow() { - SDL_DestroyWindow(window); - } - - SDL_Window* getHandle() { return window; } -private: - SDL_Window* window; -}; -} \ No newline at end of file diff --git a/src/frontend/RenderWidget.cpp b/src/frontend/RenderWidget.cpp index c3a88df..38635f0 100644 --- a/src/frontend/RenderWidget.cpp +++ b/src/frontend/RenderWidget.cpp @@ -4,9 +4,9 @@ #include #include -RenderWidget::RenderWidget(SDL_Window* window) { - wsiPlatform = std::make_shared(window); - windowInfo = std::make_shared(window); - n64::Core& core = n64::Core::GetInstance(); - core.parallel.Init(wsiPlatform, windowInfo, core.GetMem().GetRDRAMPtr()); -} \ No newline at end of file +RenderWidget::RenderWidget(SDL_Window *window) { + wsiPlatform = std::make_shared(window); + windowInfo = std::make_shared(window); + n64::Core &core = n64::Core::GetInstance(); + core.parallel.Init(wsiPlatform, windowInfo, core.GetMem().GetRDRAMPtr()); +} diff --git a/src/frontend/RenderWidget.hpp b/src/frontend/RenderWidget.hpp index af3ff6a..aa45cf2 100644 --- a/src/frontend/RenderWidget.hpp +++ b/src/frontend/RenderWidget.hpp @@ -10,70 +10,71 @@ struct Core; } class SDLParallelRdpWindowInfo final : public ParallelRDP::WindowInfo { -public: - explicit SDLParallelRdpWindowInfo(SDL_Window* window) : window(window) {} - CoordinatePair get_window_size() override { - int w,h; - SDL_GetWindowSizeInPixels(window, &w, &h); - return CoordinatePair{static_cast(w), static_cast(h)}; - } + public: + explicit SDLParallelRdpWindowInfo(SDL_Window *window) : window(window) {} + CoordinatePair get_window_size() override { + int w, h; + SDL_GetWindowSizeInPixels(window, &w, &h); + return CoordinatePair{static_cast(w), static_cast(h)}; + } -private: - SDL_Window* window{}; + private: + SDL_Window *window{}; }; class SDLWSIPlatform final : public Vulkan::WSIPlatform { -public: - explicit SDLWSIPlatform(SDL_Window* window) : window(window) {} - ~SDLWSIPlatform() = default; + public: + explicit SDLWSIPlatform(SDL_Window *window) : window(window) {} + ~SDLWSIPlatform() = default; - std::vector get_instance_extensions() override { - auto vec = std::vector(); - u32 extCount; - const auto &extensions = SDL_Vulkan_GetInstanceExtensions(&extCount); - vec.resize(extCount); + std::vector get_instance_extensions() override { + auto vec = std::vector(); + u32 extCount; + const auto &extensions = SDL_Vulkan_GetInstanceExtensions(&extCount); + vec.resize(extCount); - for (u32 i = 0; i < extCount; i++) { - vec[i] = extensions[i]; + for (u32 i = 0; i < extCount; i++) { + vec[i] = extensions[i]; + } + + return vec; } - return vec; - } + VkSurfaceKHR create_surface(VkInstance instance, VkPhysicalDevice pDevice) override { + SDL_Vulkan_CreateSurface(window, instance, nullptr, &surface); + return surface; + } - VkSurfaceKHR create_surface(VkInstance instance, VkPhysicalDevice pDevice) override { - SDL_Vulkan_CreateSurface(window, instance, nullptr, &surface); - return surface; - } + void destroy_surface(VkInstance instance, VkSurfaceKHR surface) override { + SDL_Vulkan_DestroySurface(instance, surface, nullptr); + } - void destroy_surface(VkInstance instance, VkSurfaceKHR surface) override { - SDL_Vulkan_DestroySurface(instance, surface, nullptr); - } + uint32_t get_surface_width() override { return 640; } - uint32_t get_surface_width() override { return 640; } + uint32_t get_surface_height() override { return 480; } - uint32_t get_surface_height() override { return 480; } + bool alive(Vulkan::WSI &) override { return true; } - bool alive(Vulkan::WSI &) override { return true; } + void poll_input() override {} + void poll_input_async(Granite::InputTrackerHandler *handler) override {} - void poll_input() override {} - void poll_input_async(Granite::InputTrackerHandler *handler) override {} + void event_frame_tick(double frame, double elapsed) override {} - void event_frame_tick(double frame, double elapsed) override {} + const VkApplicationInfo *get_application_info() override { return &appInfo; } - const VkApplicationInfo *get_application_info() override { return &appInfo; } + VkApplicationInfo appInfo{.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .apiVersion = VK_API_VERSION_1_3}; - VkApplicationInfo appInfo{.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .apiVersion = VK_API_VERSION_1_3}; + SDL_Window *window{}; + VkSurfaceKHR surface; - SDL_Window* window{}; - VkSurfaceKHR surface; -private: - bool gamepadConnected = false; + private: + bool gamepadConnected = false; }; class RenderWidget final { -public: - explicit RenderWidget(SDL_Window*); + public: + explicit RenderWidget(SDL_Window *); - std::shared_ptr windowInfo; - std::shared_ptr wsiPlatform; + std::shared_ptr windowInfo; + std::shared_ptr wsiPlatform; }; diff --git a/src/frontend/Settings/AudioSettings.cpp b/src/frontend/Settings/AudioSettings.cpp index 2bb380f..853d7cb 100644 --- a/src/frontend/Settings/AudioSettings.cpp +++ b/src/frontend/Settings/AudioSettings.cpp @@ -3,36 +3,36 @@ #include AudioSettings::AudioSettings() { - lockChannels = Options::GetInstance().GetValue("audio", "lock"); - volumeL = Options::GetInstance().GetValue("audio", "volumeL") * 100; - volumeR = Options::GetInstance().GetValue("audio", "volumeR") * 100; + lockChannels = Options::GetInstance().GetValue("audio", "lock"); + volumeL = Options::GetInstance().GetValue("audio", "volumeL") * 100; + volumeR = Options::GetInstance().GetValue("audio", "volumeR") * 100; } void AudioSettings::render() { - if(ImGui::Checkbox("Lock channels:", &lockChannels)) { - Options::GetInstance().SetValue("audio", "lock", lockChannels); - if(lockChannels) { - volumeR = volumeL; - Options::GetInstance().SetValue("audio", "volumeR", volumeR / 100.f); + if (ImGui::Checkbox("Lock channels:", &lockChannels)) { + Options::GetInstance().SetValue("audio", "lock", lockChannels); + if (lockChannels) { + volumeR = volumeL; + Options::GetInstance().SetValue("audio", "volumeR", volumeR / 100.f); + } + + modified = true; } - modified = true; - } + if (ImGui::SliderFloat("Volume L", &volumeL, 0.f, 100.f, "%.2f")) { + Options::GetInstance().SetValue("audio", "volumeL", volumeL / 100.f); + if (lockChannels) { + volumeR = volumeL; + Options::GetInstance().SetValue("audio", "volumeR", volumeR / 100.f); + } - if(ImGui::SliderFloat("Volume L", &volumeL, 0.f, 100.f, "%.2f")) { - Options::GetInstance().SetValue("audio", "volumeL", volumeL / 100.f); - if (lockChannels) { - volumeR = volumeL; - Options::GetInstance().SetValue("audio", "volumeR", volumeR / 100.f); + modified = true; } - modified = true; - } - - ImGui::BeginDisabled(lockChannels); - if(ImGui::SliderFloat("Volume R", &volumeR, 0.f, 100.f, "%.2f")) { - Options::GetInstance().SetValue("audio", "volumeR", volumeR / 100.f); - modified = true; - } - ImGui::EndDisabled(); -} \ No newline at end of file + ImGui::BeginDisabled(lockChannels); + if (ImGui::SliderFloat("Volume R", &volumeR, 0.f, 100.f, "%.2f")) { + Options::GetInstance().SetValue("audio", "volumeR", volumeR / 100.f); + modified = true; + } + ImGui::EndDisabled(); +} diff --git a/src/frontend/Settings/AudioSettings.hpp b/src/frontend/Settings/AudioSettings.hpp index 5929199..48fb17b 100644 --- a/src/frontend/Settings/AudioSettings.hpp +++ b/src/frontend/Settings/AudioSettings.hpp @@ -2,10 +2,10 @@ #include struct AudioSettings final : SettingsTab { - bool lockChannels = false; - float volumeL{}; - float volumeR{}; + bool lockChannels = false; + float volumeL{}; + float volumeR{}; - explicit AudioSettings(); - void render() override; + explicit AudioSettings(); + void render() override; }; diff --git a/src/frontend/Settings/CPUSettings.hpp b/src/frontend/Settings/CPUSettings.hpp index 11fd921..168c47f 100644 --- a/src/frontend/Settings/CPUSettings.hpp +++ b/src/frontend/Settings/CPUSettings.hpp @@ -2,7 +2,7 @@ #include struct CPUSettings final : SettingsTab { - int selectedCpuTypeIndex = 0; - void render() override; - explicit CPUSettings(); + int selectedCpuTypeIndex = 0; + void render() override; + explicit CPUSettings(); }; diff --git a/src/frontend/Settings/GeneralSettings.cpp b/src/frontend/Settings/GeneralSettings.cpp index 9fc83af..3fa699f 100644 --- a/src/frontend/Settings/GeneralSettings.cpp +++ b/src/frontend/Settings/GeneralSettings.cpp @@ -3,33 +3,35 @@ #include #include -GeneralSettings::GeneralSettings(gui::NativeWindow& window) : window(window) { - savesPath = Options::GetInstance().GetValue("general", "savePath"); +GeneralSettings::GeneralSettings(gui::Window &window) : window(window) { + savesPath = Options::GetInstance().GetValue("general", "savePath"); } void GeneralSettings::render() { - if(ImGui::Button("Pick...")) { - SDL_ShowOpenFolderDialog([](void *userdata, const char * const *filelist, int _) { - auto* general = static_cast(userdata); + if (ImGui::Button("Pick...")) { + SDL_ShowOpenFolderDialog( + [](void *userdata, const char *const *filelist, int _) { + auto *general = static_cast(userdata); - if (!filelist) { - panic("An error occurred: {}", SDL_GetError()); - } + if (!filelist) { + panic("An error occurred: {}", SDL_GetError()); + } - if (!*filelist) { - warn("The user did not select any file."); - warn("Most likely, the dialog was canceled."); - general->modified = false; - return; - } + if (!*filelist) { + warn("The user did not select any file."); + warn("Most likely, the dialog was canceled."); + general->modified = false; + return; + } - general->savesPath = fs::absolute(*filelist).string(); - Options::GetInstance().SetValue("general", "savePath", general->savesPath); - general->modified = true; - }, this, window.getHandle(), nullptr, false); - } - ImGui::SameLine(); - ImGui::BeginDisabled(); - ImGui::InputText("Save Path", const_cast(savesPath.c_str()), savesPath.length()); - ImGui::EndDisabled(); -} \ No newline at end of file + general->savesPath = fs::absolute(*filelist).string(); + Options::GetInstance().SetValue("general", "savePath", general->savesPath); + general->modified = true; + }, + this, window.getHandle(), nullptr, false); + } + ImGui::SameLine(); + ImGui::BeginDisabled(); + ImGui::InputText("Save Path", const_cast(savesPath.c_str()), savesPath.length()); + ImGui::EndDisabled(); +} diff --git a/src/frontend/Settings/GeneralSettings.hpp b/src/frontend/Settings/GeneralSettings.hpp index 436cc79..c7ddba3 100644 --- a/src/frontend/Settings/GeneralSettings.hpp +++ b/src/frontend/Settings/GeneralSettings.hpp @@ -1,11 +1,12 @@ #pragma once #include -#include +#include struct GeneralSettings final : SettingsTab { void render() override; - explicit GeneralSettings(gui::NativeWindow&); -private: - gui::NativeWindow& window; + explicit GeneralSettings(gui::Window &); + + private: + gui::Window &window; std::string savesPath; -}; \ No newline at end of file +}; diff --git a/src/frontend/SettingsWindow.cpp b/src/frontend/SettingsWindow.cpp index da92f06..208549f 100644 --- a/src/frontend/SettingsWindow.cpp +++ b/src/frontend/SettingsWindow.cpp @@ -4,45 +4,45 @@ #include bool SettingsWindow::render() { - const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); - ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - if(!ImGui::BeginPopupModal("Settings", &isOpen, ImGuiWindowFlags_AlwaysAutoResize)) - return false; - - if(!ImGui::BeginTabBar("SettingsTabBar")) - return false; + if (!ImGui::BeginPopupModal("Settings", &isOpen, ImGuiWindowFlags_AlwaysAutoResize)) + return false; - for (auto& [name, tab] : tabs) { - if (ImGui::BeginTabItem(name.c_str())) { - tab->render(); - if (tab->modified && !applyEnabled) - applyEnabled = true; + if (!ImGui::BeginTabBar("SettingsTabBar")) + return false; - ImGui::EndTabItem(); + for (auto &[name, tab] : tabs) { + if (ImGui::BeginTabItem(name.c_str())) { + tab->render(); + if (tab->modified && !applyEnabled) + applyEnabled = true; + + ImGui::EndTabItem(); + } } - } - ImGui::EndTabBar(); + ImGui::EndTabBar(); - ImGui::BeginDisabled(!applyEnabled); - if(ImGui::Button("Apply")) { - applyEnabled = false; - Options::GetInstance().Apply(); + ImGui::BeginDisabled(!applyEnabled); + if (ImGui::Button("Apply")) { + applyEnabled = false; + Options::GetInstance().Apply(); - for (const auto &tab : tabs | std::views::values) { - tab->modified = false; + for (const auto &tab : tabs | std::views::values) { + tab->modified = false; + } } - } - ImGui::EndDisabled(); - - ImGui::SameLine(); - - if(ImGui::Button("Cancel")) { - isOpen = false; - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); + ImGui::EndDisabled(); - return true; -} \ No newline at end of file + ImGui::SameLine(); + + if (ImGui::Button("Cancel")) { + isOpen = false; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + + return true; +} diff --git a/src/frontend/SettingsWindow.hpp b/src/frontend/SettingsWindow.hpp index dfa7b41..8ac05ad 100644 --- a/src/frontend/SettingsWindow.hpp +++ b/src/frontend/SettingsWindow.hpp @@ -2,25 +2,25 @@ #include #include #include -#include +#include #include class SettingsWindow final { - gui::NativeWindow& window; - GeneralSettings generalSettings; - CPUSettings cpuSettings; - AudioSettings audioSettings; - bool applyEnabled = false; + GeneralSettings generalSettings; + CPUSettings cpuSettings; + AudioSettings audioSettings; + bool applyEnabled = false; - std::vector> tabs = { - { "General", &generalSettings }, - { "CPU", &cpuSettings }, - { "Audio", &audioSettings }, - }; -public: - bool isOpen = false; - bool render(); - explicit SettingsWindow(gui::NativeWindow& window) : window(window), generalSettings(window) {} - [[nodiscard]] float getVolumeL() const { return audioSettings.volumeL / 100.f; } - [[nodiscard]] float getVolumeR() const { return audioSettings.volumeR / 100.f; } + std::vector> tabs = { + {"General", &generalSettings}, + {"CPU", &cpuSettings}, + {"Audio", &audioSettings}, + }; + + public: + bool isOpen = false; + bool render(); + explicit SettingsWindow(gui::Window &window) : generalSettings(window) {} + [[nodiscard]] float getVolumeL() const { return audioSettings.volumeL / 100.f; } + [[nodiscard]] float getVolumeR() const { return audioSettings.volumeR / 100.f; } }; diff --git a/src/frontend/Window.hpp b/src/frontend/Window.hpp new file mode 100644 index 0000000..b9fb647 --- /dev/null +++ b/src/frontend/Window.hpp @@ -0,0 +1,23 @@ +#pragma once +#include +#include +#include + +namespace gui { +struct Window { + Window(const std::string &title, int w, int h, int posX = SDL_WINDOWPOS_CENTERED, + int posY = SDL_WINDOWPOS_CENTERED) { + SDL_Init(SDL_INIT_VIDEO); + float scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + window = SDL_CreateWindow(title.c_str(), w * scale, h * scale, + SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY); + } + + ~Window() { SDL_DestroyWindow(window); } + + SDL_Window *getHandle() { return window; } + + private: + SDL_Window *window; +}; +} // namespace gui diff --git a/src/frontend/main.cpp b/src/frontend/main.cpp index 6583399..87939da 100644 --- a/src/frontend/main.cpp +++ b/src/frontend/main.cpp @@ -2,16 +2,18 @@ #include int main(const int argc, char **argv) { - KaizenGui kaizenGui; - cflags::cflags flags; - flags.add_string_callback('\0', "rom", [&kaizenGui](const std::string& v) { kaizenGui.LoadROM(v); }, "Rom to launch from command-line"); - flags.add_string_callback('\0', "movie", [](const std::string& v) { KaizenGui::LoadTAS(v); }, "Mupen Movie to replay"); + KaizenGui kaizenGui; + cflags::cflags flags; + flags.add_string_callback( + '\0', "rom", [&kaizenGui](const std::string &v) { kaizenGui.LoadROM(v); }, "Rom to launch from command-line"); + flags.add_string_callback( + '\0', "movie", [](const std::string &v) { KaizenGui::LoadTAS(v); }, "Mupen Movie to replay"); - if(!flags.parse(argc, argv)) { - return -1; - } + if (!flags.parse(argc, argv)) { + return -1; + } - kaizenGui.run(); + kaizenGui.run(); - return 0; + return 0; } diff --git a/src/utils/Options.cpp b/src/utils/Options.cpp index 8c02bc1..2b3e14d 100644 --- a/src/utils/Options.cpp +++ b/src/utils/Options.cpp @@ -10,22 +10,32 @@ void Options::SetValue(const std::string &key, const std::string &field, structure[key][field] = std::format("{:.2f}", value); } +template <> +void Options::SetValue(const std::string &key, const std::string &field, const int &value) { + structure[key][field] = std::format("{}", value); +} + template <> void Options::SetValue(const std::string &key, const std::string &field, const bool &value) { structure[key][field] = value ? "true" : "false"; } -template<> +template <> std::string Options::GetValue(const std::string &key, const std::string &field) { return structure[key][field]; } -template<> +template <> float Options::GetValue(const std::string &key, const std::string &field) { return std::stof(structure[key][field]); } -template<> +template <> +int Options::GetValue(const std::string &key, const std::string &field) { + return std::stoi(structure[key][field]); +} + +template <> bool Options::GetValue(const std::string &key, const std::string &field) { return structure[key][field] == "true" ? true : false; -} \ No newline at end of file +} diff --git a/src/utils/Options.hpp b/src/utils/Options.hpp index 8450a2b..f03bbbe 100644 --- a/src/utils/Options.hpp +++ b/src/utils/Options.hpp @@ -10,19 +10,33 @@ namespace fs = std::filesystem; struct Options { Options() : file{"resources/options.ini"} { auto fileExists = fs::exists("resources/options.ini"); - if(fileExists) { + if (fileExists) { file.read(structure); return; } - structure["general"]["savePath"] = "saves"; - fs::create_directory("saves"); + if (!fs::exists("saves")) + fs::create_directory("saves"); + + structure["cpu"]["type"] = "interpreter"; + structure["audio"]["lock"] = "true"; structure["audio"]["volumeL"] = "0.5"; structure["audio"]["volumeR"] = "0.5"; - structure["audio"]["lock"] = "true"; - structure["cpu"]["type"] = "interpreter"; + structure["general"]["savePath"] = "saves"; + structure["gamepad"]["A"] = "SDL_GAMEPAD_BUTTON_SOUTH"; + structure["gamepad"]["B"] = "SDL_GAMEPAD_BUTTON_WEST"; + structure["gamepad"]["Start"] = "SDL_GAMEPAD_BUTTON_START"; + structure["gamepad"]["DUp"] = "SDL_GAMEPAD_BUTTON_DPAD_UP"; + structure["gamepad"]["DDown"] = "SDL_GAMEPAD_BUTTON_DPAD_DOWN"; + structure["gamepad"]["DLeft"] = "SDL_GAMEPAD_BUTTON_DPAD_LEFT"; + structure["gamepad"]["DRight"] = "SDL_GAMEPAD_BUTTON_DPAD_RIGHT"; + structure["gamepad"]["LT"] = "SDL_GAMEPAD_BUTTON_LEFT_SHOULDER"; + structure["gamepad"]["RT"] = "SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER"; + structure["gamepad"]["Z"] = "SDL_GAMEPAD_AXIS_LEFT_TRIGGER"; + structure["gamepad"]["X"] = "SDL_GAMEPAD_AXIS_RIGHTX"; + structure["gamepad"]["Y"] = "SDL_GAMEPAD_AXIS_RIGHTY"; - if(!file.generate(structure)) + if (!file.generate(structure)) panic("Couldn't generate settings' INI!"); } @@ -37,10 +51,11 @@ struct Options { T GetValue(const std::string &key, const std::string &field); void Apply() { - if(!file.write(structure)) + if (!file.write(structure)) panic("Could not modify options on disk!"); } -private: + + private: mINI::INIFile file; mINI::INIStructure structure; -}; \ No newline at end of file +};