Fix crash with event watchers + handle SDL loop better + shut down more appropriately

This commit is contained in:
irisz64
2025-05-27 15:30:36 +02:00
parent ab44f6d980
commit 4f596f5856
11 changed files with 71 additions and 104 deletions

View File

@@ -18,7 +18,6 @@ public:
void TogglePause() const noexcept;
void Reset() const noexcept;
void Stop() const noexcept;
void requestInterruption() { interruptionRequested = true; }
bool interruptionRequested = false, isRunning = false, parallelRDPInitialized = false;
std::shared_ptr<n64::Core> core;

View File

@@ -149,6 +149,9 @@ namespace gui {
ImGui::NewFrame();
}
inline void EndFrame() {
inline void Cleanup() {
ImGui_ImplVulkan_Shutdown();
ImGui_ImplSDL3_Shutdown();
ImGui::DestroyContext();
}
}

View File

@@ -7,18 +7,26 @@
namespace gui {
struct PopupWindow {
PopupWindow(const std::string& title, std::function<void()>&& func = nullptr, bool opened = false) : title(title), exec(func), opened(opened) {}
void setFunc(std::function<void()>&& func) {
exec = func;
}
void setFunc(std::function<void()>&& func) { exec = func; }
void setOpened(bool v) { opened = v; }
void setOnClose(std::function<void()>&& func) { onClose = func; }
void setOnOpen(std::function<void()>&& func) { onOpen = func; }
bool render() {
if(!opened)
return false;
if(!opened) {
if(onClose)
onClose();
return false;
}
if (!ImGui::IsPopupOpen(title.c_str()))
{
if(onOpen)
onOpen();
ImGui::OpenPopup(title.c_str());
}
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
@@ -27,6 +35,7 @@ struct PopupWindow {
{
if(exec)
exec();
ImGui::EndPopup();
return true;
@@ -36,6 +45,8 @@ struct PopupWindow {
}
private:
std::function<void()> exec;
std::function<void()> onClose;
std::function<void()> onOpen;
std::string title;
bool opened = false;
};

View File

@@ -14,10 +14,18 @@ InputSettings::InputSettings(nlohmann::json &settings) : settings(settings) {
devices.addItem({"Keyboard/Mouse"});
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
}
void InputSettings::RegisterEventWatchers() {
SDL_AddEventWatch(QueryDevices, this);
SDL_AddEventWatch(PollGamepad, this);
}
void InputSettings::UnregisterEventWatchers() {
SDL_RemoveEventWatch(QueryDevices, this);
SDL_RemoveEventWatch(PollGamepad, this);
}
bool InputSettings::render() {
if(devices.render()) {
auto currentlySelectedDevice = devices.getCurrentlySelected();

View File

@@ -59,4 +59,6 @@ public:
bool getModified() { return modified; }
nlohmann::json &settings;
std::array<SDL_Keycode, 18> GetMappedKeys();
void RegisterEventWatchers();
void UnregisterEventWatchers();
};

View File

@@ -22,14 +22,6 @@ KaizenGui::KaizenGui() noexcept : window("Kaizen", 800, 600), core(std::make_sha
core->Stop();
}
});
emuExitFunc = [&]() {
quit = true;
if (emuThread.isRunning) {
emuThread.requestInterruption();
while (emuThread.isRunning) {}
}
};
about.setFunc([&]() {
ImGui::Text("Kaizen is a Nintendo 64 emulator that strives");
@@ -63,7 +55,10 @@ KaizenGui::KaizenGui() noexcept : window("Kaizen", 800, 600), core(std::make_sha
Util::panic("Error: {}", NFD::GetError());
LoadROM(path.get());
}},
{"Exit", std::move(emuExitFunc)}
{"Exit", [&]() {
quit = true;
emuThread.Stop();
}}
}
});
@@ -85,6 +80,10 @@ KaizenGui::KaizenGui() noexcept : window("Kaizen", 800, 600), core(std::make_sha
}});
}
KaizenGui::~KaizenGui() {
gui::Cleanup();
}
void KaizenGui::RenderUI() {
gui::StartFrame();
statusBar.render();
@@ -114,17 +113,22 @@ void KaizenGui::LoadROM(const std::string &path) noexcept {
Util::RPC::GetInstance().Update(Util::RPC::Playing, gameNameDB);
}
int KaizenGui::run() {
void KaizenGui::run() {
while(!quit) {
if(vulkanWidget.wsiPlatform->quitRequested) {
emuExitFunc();
emuThread.run();
SDL_Event e;
while (SDL_PollEvent(&e)) {
ImGui_ImplSDL3_ProcessEvent(&e);
switch(e.type) {
case SDL_EVENT_QUIT:
quit = true;
emuThread.Stop();
break;
}
}
RenderUI();
emuThread.run();
}
return 0;
}
void KaizenGui::LoadTAS(const std::string &path) const noexcept {

View File

@@ -11,6 +11,7 @@ class KaizenGui final {
gui::NativeWindow window;
public:
explicit KaizenGui() noexcept;
~KaizenGui();
double fpsCounter = -1.0;
gui::MenuBar<true> menuBar;
gui::MenuItem actionPause{"Pause", nullptr, false}, actionStop{"Stop", nullptr, false}, actionReset{"Reset", nullptr, false};
@@ -21,7 +22,7 @@ public:
gui::PopupWindow about{"About Kaizen"};
gui::StatusBar statusBar{};
int run();
void run();
void LoadTAS(const std::string &path) const noexcept;
void LoadROM(const std::string &path) noexcept;
private:

View File

@@ -9,80 +9,4 @@ RenderWidget::RenderWidget(const std::shared_ptr<n64::Core> &core, SDL_Window* w
wsiPlatform = std::make_shared<SDLWSIPlatform>(core, window);
windowInfo = std::make_shared<SDLParallelRdpWindowInfo>(window);
core->parallel.Init(wsiPlatform, windowInfo, core->cpu->GetMem().GetRDRAMPtr());
}
void SDLWSIPlatform::poll_input() {
SDL_Event e;
while (SDL_PollEvent(&e)) {
ImGui_ImplSDL3_ProcessEvent(&e);
switch (e.type) {
case SDL_EVENT_QUIT:
quitRequested = true;
break;
case SDL_EVENT_GAMEPAD_ADDED:
{
const auto index = e.gdevice.which;
gamepad = SDL_OpenGamepad(index);
Util::info("Controller found!");
const auto serial = SDL_GetGamepadSerial(gamepad);
const auto name = SDL_GetGamepadName(gamepad);
const auto path = SDL_GetGamepadPath(gamepad);
Util::info("\tName: {}", name ? name : "Not available");
Util::info("\tSerial: {}", serial ? serial : "Not available");
Util::info("\tPath: {}", path ? path : "Not available");
gamepadConnected = true;
}
break;
case SDL_EVENT_GAMEPAD_REMOVED:
{
gamepadConnected = false;
SDL_CloseGamepad(gamepad);
}
break;
}
}
if (gamepadConnected) {
n64::PIF &pif = core->cpu->GetMem().mmio.si.pif;
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::Z,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
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));
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 /= float(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 /= float(std::abs(SDL_JOYSTICK_AXIS_MIN));
} else {
yclamped /= SDL_JOYSTICK_AXIS_MAX;
}
yclamped *= 86;
pif.UpdateAxis(0, n64::Controller::Axis::Y, -yclamped);
pif.UpdateAxis(0, n64::Controller::Axis::X, xclamped);
}
}
}

View File

@@ -25,6 +25,10 @@ private:
class SDLWSIPlatform final : public Vulkan::WSIPlatform {
public:
explicit SDLWSIPlatform(const std::shared_ptr<n64::Core> &core, SDL_Window* window) : window(window), core(core) {}
~SDLWSIPlatform() {
if(gamepadConnected)
SDL_CloseGamepad(gamepad);
}
std::vector<const char *> get_instance_extensions() override {
auto vec = std::vector<const char *>();
@@ -44,7 +48,9 @@ public:
return surface;
}
void destroy_surface(VkInstance, VkSurfaceKHR) override {}
void destroy_surface(VkInstance instance, VkSurfaceKHR surface) override {
SDL_Vulkan_DestroySurface(instance, surface, nullptr);
}
uint32_t get_surface_width() override { return 640; }
@@ -52,7 +58,7 @@ public:
bool alive(Vulkan::WSI &) override { return true; }
void poll_input() override;
void poll_input() override {}
void poll_input_async(Granite::InputTrackerHandler *handler) override {}
void event_frame_tick(double frame, double elapsed) override {}
@@ -63,7 +69,6 @@ public:
SDL_Window* window{};
VkSurfaceKHR surface;
bool quitRequested = false;
private:
std::shared_ptr<n64::Core> core;
SDL_Gamepad *gamepad{};

View File

@@ -52,6 +52,14 @@ SettingsWindow::SettingsWindow() : settings{JSONOpenOrCreate("resources/settings
apply.setEnabled(false);
popup.setOnOpen([&]() {
inputSettings.RegisterEventWatchers();
});
popup.setOnClose([&]() {
inputSettings.UnregisterEventWatchers();
});
popup.setFunc([&]() {
tabs.render();

View File

@@ -13,5 +13,7 @@ int main(int argc, char **argv) {
return -1;
}
return kaizenGui.run();
kaizenGui.run();
return 0;
}