From f13c70d028beac130278b857ca84493841fcc7a1 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Tue, 1 Oct 2024 19:52:10 +0200 Subject: [PATCH 01/63] fix blow noise input (microphone input takes signed values) --- src/frontend/qt_sdl/EmuInstanceAudio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/EmuInstanceAudio.cpp b/src/frontend/qt_sdl/EmuInstanceAudio.cpp index 21b112ea..a9bf1fb7 100644 --- a/src/frontend/qt_sdl/EmuInstanceAudio.cpp +++ b/src/frontend/qt_sdl/EmuInstanceAudio.cpp @@ -321,7 +321,7 @@ void EmuInstance::micProcess() for (int i = 0; i < 735; i++) { - tmp[i] = mic_blow[sample_pos]; + tmp[i] = mic_blow[sample_pos] ^ 0x8000; sample_pos++; if (sample_pos >= sample_len) sample_pos = 0; } From aa443c8846a16f502a6508786f44f2d66c31101b Mon Sep 17 00:00:00 2001 From: RSDuck Date: Sun, 6 Oct 2024 17:33:03 +0200 Subject: [PATCH 02/63] fix #2083 and minor cleanup --- src/frontend/qt_sdl/CLI.cpp | 4 ++-- src/frontend/qt_sdl/CLI.h | 2 -- src/frontend/qt_sdl/main.cpp | 3 +++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/frontend/qt_sdl/CLI.cpp b/src/frontend/qt_sdl/CLI.cpp index 299ce65b..5e352cae 100644 --- a/src/frontend/qt_sdl/CLI.cpp +++ b/src/frontend/qt_sdl/CLI.cpp @@ -96,7 +96,7 @@ CommandLineOptions* ManageArgs(QApplication& melon) } else { - options->errorsToDisplay += "Option -a/--archive-file given, but no archive specified!"; + Log(LogLevel::Error, "Option -a/--archive-file given, but no archive specified!"); } } @@ -108,7 +108,7 @@ CommandLineOptions* ManageArgs(QApplication& melon) } else { - options->errorsToDisplay += "Option -A/--archive-file-gba given, but no archive specified!"; + Log(LogLevel::Error, "Option -A/--archive-file-gba given, but no archive specified!"); } } #endif diff --git a/src/frontend/qt_sdl/CLI.h b/src/frontend/qt_sdl/CLI.h index 4997e6a7..beb120bf 100644 --- a/src/frontend/qt_sdl/CLI.h +++ b/src/frontend/qt_sdl/CLI.h @@ -28,8 +28,6 @@ namespace CLI { struct CommandLineOptions { - QStringList errorsToDisplay = {}; - std::optional dsRomPath; std::optional dsRomArchivePath; std::optional gbaRomPath; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 9a9c93cb..2643104d 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -364,6 +364,9 @@ int main(int argc, char** argv) if (memberSyntaxUsed) printf("Warning: use the a.zip|b.nds format at your own risk!\n"); win->preloadROMs(dsfile, gbafile, options->boot); + + if (options->fullscreen) + ToggleFullscreen(win); } int ret = melon.exec(); From 30441fed24c1f04cb6a7a162e58c3187c7e5cee0 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Sun, 6 Oct 2024 18:59:19 +0200 Subject: [PATCH 03/63] do not restore fullscreen state from saved geometry --- src/frontend/qt_sdl/Window.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 3020defd..0e4f1b06 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -81,6 +81,7 @@ #include "EmuInstance.h" #include "ArchiveUtil.h" #include "CameraManager.h" +#include "Window.h" using namespace melonDS; @@ -666,6 +667,8 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : QByteArray dec = QByteArray::fromBase64(raw, QByteArray::Base64Encoding | QByteArray::AbortOnBase64DecodingErrors); if (!dec.isEmpty()) restoreGeometry(dec); + // if the window was closed in fullscreen do not restore this + setWindowState(windowState() & ~Qt::WindowFullScreen); } show(); From 216b8e045daffa3582978e81c12fdbe74873e246 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Mon, 7 Oct 2024 11:28:58 +0200 Subject: [PATCH 04/63] fix audio interpolation setting range --- src/frontend/qt_sdl/Config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 0a161b6f..3f570302 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -80,7 +80,7 @@ RangeList IntRanges = {"3D.Renderer", {0, renderer3D_Max-1}}, {"Screen.VSyncInterval", {1, 20}}, {"3D.GL.ScaleFactor", {1, 16}}, - {"Audio.Interpolation", {0, 3}}, + {"Audio.Interpolation", {0, 4}}, {"Instance*.Audio.Volume", {0, 256}}, {"Mic.InputType", {0, micInputType_MAX-1}}, {"Instance*.Window*.ScreenRotation", {0, screenRot_MAX-1}}, From fbf753257b74072d2c95c10b2f40c0534959cf8d Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sat, 19 Oct 2024 18:29:05 +0200 Subject: [PATCH 05/63] cmake: Add easy sanitizers option Set -DSANITIZE to a comma-separated list of options to pass to -fsanitize=, like -DSANITIZE=address,undefined --- CMakeLists.txt | 1 + cmake/Sanitizers.cmake | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 cmake/Sanitizers.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 40583949..55bf825f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ include(CMakeDependentOption) include(CheckIPOSupported) include(SetupCCache) +include(Sanitizers) set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") diff --git a/cmake/Sanitizers.cmake b/cmake/Sanitizers.cmake new file mode 100644 index 00000000..9c09da28 --- /dev/null +++ b/cmake/Sanitizers.cmake @@ -0,0 +1,8 @@ +set(SANITIZE "" CACHE STRING "Sanitizers to enable.") + +string(REGEX MATCHALL "[^,]+" ENABLED_SANITIZERS "${SANITIZE}") + +foreach(SANITIZER ${ENABLED_SANITIZERS}) + add_compile_options("-fsanitize=${SANITIZER}") + add_link_options("-fsanitize=${SANITIZER}") +endforeach() \ No newline at end of file From e5501e555f9d1fb6c4b56354e57cbf908a3ff0e7 Mon Sep 17 00:00:00 2001 From: kaitou <52998298+kaitouctr@users.noreply.github.com> Date: Wed, 23 Oct 2024 05:13:55 +1100 Subject: [PATCH 06/63] fix: set default mode to 24 hours (#2166) --- src/RTC.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RTC.cpp b/src/RTC.cpp index fe262644..24e00de7 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -40,7 +40,7 @@ RTC::RTC(melonDS::NDS& nds) : NDS(nds) // indicate the power was off // this will be changed if a previously saved RTC state is loaded - State.StatusReg1 = 0x80; + State.StatusReg1 = 0x80 | (1<<1); } RTC::~RTC() @@ -943,4 +943,4 @@ void RTC::Write(u16 val, bool byte) IO = (IO & 0x0001) | (val & 0xFFFE); } -} \ No newline at end of file +} From b993ec10cd5a3b50f48e8be0e98c4d439f00a597 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 23 Oct 2024 11:57:37 +0200 Subject: [PATCH 07/63] remove "Test" menu item --- src/frontend/qt_sdl/Window.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 0e4f1b06..0280e761 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -623,13 +623,6 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : } } - if (parentWidget() != nullptr) // TEST - { - QMenu* menu = menubar->addMenu("Test"); - - menu->addAction("Test"); - } - actScreenFiltering = menu->addAction("Screen filtering"); actScreenFiltering->setCheckable(true); connect(actScreenFiltering, &QAction::triggered, this, &MainWindow::onChangeScreenFiltering); From d68b58f37e08f16660cdac4c563e404642d0355e Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 23 Oct 2024 14:55:02 +0200 Subject: [PATCH 08/63] fix DSi wifi hang --- src/DSi_NWifi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp index 792bf12d..9827bdbe 100644 --- a/src/DSi_NWifi.cpp +++ b/src/DSi_NWifi.cpp @@ -1445,7 +1445,6 @@ void DSi_NWifi::CheckRX() int rxlen = Platform::Net_RecvPacket(LANBuffer, DSi.UserData); while (rxlen > 0) { - //printf("WMI packet recv %04X %04X %04X\n", *(u16*)&LANBuffer[0], *(u16*)&LANBuffer[2], *(u16*)&LANBuffer[4]); // check destination MAC if (*(u32*)&LANBuffer[0] != 0xFFFFFFFF || *(u16*)&LANBuffer[4] != 0xFFFF) { @@ -1508,6 +1507,7 @@ void DSi_NWifi::CheckRX() Mailbox[8].Write(LANBuffer[14+i]); DrainRXBuffer(); + return; } } From 1428bfb2cfd34dd775b2b04791ee47910484fffe Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 23 Oct 2024 20:33:31 +0200 Subject: [PATCH 09/63] fix one of the cursed bugs --- src/frontend/qt_sdl/EmuInstance.cpp | 19 +++++++++---------- src/frontend/qt_sdl/EmuThread.cpp | 1 - src/frontend/qt_sdl/EmuThread.h | 1 + 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 54bae531..edb6c559 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -1116,6 +1116,9 @@ void EmuInstance::setDateTime() bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGBAArgs&& _gbaargs) noexcept { + // update the console type + consoleType = globalCfg.GetInt("Emu.ConsoleType"); + // Let's get the cart we want to use; // if we wnat to keep the cart, we'll eject it from the existing console first. std::unique_ptr nextndscart; @@ -1149,8 +1152,6 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB } - int consoletype = globalCfg.GetInt("Emu.ConsoleType"); - auto arm9bios = loadARM9BIOS(); if (!arm9bios) return false; @@ -1159,7 +1160,7 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB if (!arm7bios) return false; - auto firmware = loadFirmware(consoletype); + auto firmware = loadFirmware(consoleType); if (!firmware) return false; @@ -1203,7 +1204,7 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB NDSArgs* args = &ndsargs; std::optional dsiargs = std::nullopt; - if (consoletype == 1) + if (consoleType == 1) { ndsargs.GBAROM = nullptr; @@ -1234,19 +1235,19 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB args = &(*dsiargs); } - - if ((!nds) || (consoletype != nds->ConsoleType)) + if ((!nds) || (consoleType != nds->ConsoleType)) { NDS::Current = nullptr; if (nds) delete nds; - if (consoletype == 1) + if (consoleType == 1) nds = new DSi(std::move(dsiargs.value()), this); else nds = new NDS(std::move(ndsargs), this); NDS::Current = nds; nds->Reset(); + //emuThread->updateVideoRenderer(); // not actually needed? } else { @@ -1260,7 +1261,7 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB nds->SPU.SetInterpolation(args->Interpolation); nds->SPU.SetDegrade10Bit(args->BitDepth); - if (consoletype == 1) + if (consoleType == 1) { DSi* dsi = (DSi*)nds; DSiArgs& _dsiargs = *dsiargs; @@ -1282,8 +1283,6 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB void EmuInstance::reset() { - consoleType = globalCfg.GetInt("Emu.ConsoleType"); - updateConsole(Keep {}, Keep {}); if (consoleType == 1) ejectGBACart(); diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 4ce4efda..c6ac80d7 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -664,7 +664,6 @@ void EmuThread::updateRenderer() { if (videoRenderer != lastVideoRenderer) { - printf("creating renderer %d\n", videoRenderer); switch (videoRenderer) { case renderer3D_Software: diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index cd36eb4c..f728788b 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -106,6 +106,7 @@ public: void initContext(); void deinitContext(); void updateVideoSettings() { videoSettingsDirty = true; } + void updateVideoRenderer() { videoSettingsDirty = true; lastVideoRenderer = -1; } int FrontBuffer = 0; QMutex FrontBufferLock; From 82f38f0b7a3f4214d89c4cd877bbe49d1a6d3485 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 24 Oct 2024 00:27:05 +0200 Subject: [PATCH 10/63] start moving ROM/firmware loading to the emuthread to avoid cursed bugs --- src/frontend/qt_sdl/EmuThread.cpp | 51 +++++++++++++++++++++++++++++-- src/frontend/qt_sdl/EmuThread.h | 13 +++++--- src/frontend/qt_sdl/Window.cpp | 47 +++------------------------- 3 files changed, 63 insertions(+), 48 deletions(-) diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index c6ac80d7..9a7ea6cb 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -541,7 +541,8 @@ void EmuThread::handleMessages() break; case msg_EmuStop: - if (msg.stopExternal) emuInstance->nds->Stop(); + if (msg.param.value()) + emuInstance->nds->Stop(); emuStatus = emuStatus_Paused; emuActive = false; @@ -574,6 +575,26 @@ void EmuThread::handleMessages() emuInstance->deinitOpenGL(); useOpenGL = false; break; + + case msg_BootROM: + bootResult = 0; + if (!emuInstance->loadROM(msg.param.value(), true)) + break; + + assert(emuInstance->nds != nullptr); + emuInstance->nds->Start(); + bootResult = 1; + break; + + case msg_BootFirmware: + bootResult = 0; + if (!emuInstance->bootToMenu()) + break; + + assert(emuInstance->nds != nullptr); + emuInstance->nds->Start(); + bootResult = 1; + break; } msgSemaphore.release(); @@ -626,7 +647,7 @@ void EmuThread::emuTogglePause() void EmuThread::emuStop(bool external) { - sendMessage({.type = msg_EmuStop, .stopExternal = external}); + sendMessage({.type = msg_EmuStop, .param = external}); waitMessage(); } @@ -660,6 +681,32 @@ bool EmuThread::emuIsActive() return emuActive; } +int EmuThread::bootROM(QStringList filename) +{ + sendMessage(msg_EmuPause); + sendMessage({.type = msg_BootROM, .param = filename}); + waitMessage(2); + if (!bootResult) + return bootResult; + + sendMessage(msg_EmuRun); + waitMessage(); + return bootResult; +} + +int EmuThread::bootFirmware() +{ + sendMessage(msg_EmuPause); + sendMessage(msg_BootFirmware); + waitMessage(2); + if (!bootResult) + return bootResult; + + sendMessage(msg_EmuRun); + waitMessage(); + return bootResult; +} + void EmuThread::updateRenderer() { if (videoRenderer != lastVideoRenderer) diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index f728788b..612f7920 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -68,15 +68,15 @@ public: msg_InitGL, msg_DeInitGL, + + msg_BootROM, + msg_BootFirmware, }; struct Message { MessageType type; - union - { - bool stopExternal; - }; + QVariant param; }; void sendMessage(Message msg); @@ -100,6 +100,9 @@ public: void emuFrameStep(); void emuReset(); + int bootROM(QStringList filename); + int bootFirmware(); + bool emuIsRunning(); bool emuIsActive(); @@ -153,6 +156,8 @@ private: constexpr static int emuPauseStackPauseThreshold = 1; int emuPauseStack; + int bootResult = 0; + QMutex msgMutex; QSemaphore msgSemaphore; QQueue msgQueue; diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 0280e761..6f2f86a9 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1211,24 +1211,15 @@ void MainWindow::updateCartInserted(bool gba) void MainWindow::onOpenFile() { - emuThread->emuPause(); - if (!verifySetup()) - { - emuThread->emuUnpause(); return; - } QStringList file = pickROM(false); if (file.isEmpty()) - { - emuThread->emuUnpause(); return; - } - - if (!emuInstance->loadROM(file, true)) + + if (!emuThread->bootROM(file)) { - emuThread->emuUnpause(); return; } @@ -1237,10 +1228,6 @@ void MainWindow::onOpenFile() recentFileList.prepend(filename); updateRecentFilesMenu(); - assert(emuInstance->nds != nullptr); - emuInstance->nds->Start(); - emuThread->emuRun(); - updateCartInserted(false); } @@ -1310,24 +1297,15 @@ void MainWindow::onClickRecentFile() QAction *act = (QAction *)sender(); QString filename = act->data().toString(); - emuThread->emuPause(); - if (!verifySetup()) - { - emuThread->emuUnpause(); return; - } const QStringList file = splitArchivePath(filename, true); if (file.isEmpty()) - { - emuThread->emuUnpause(); return; - } - - if (!emuInstance->loadROM(file, true)) + + if (!emuThread->bootROM(file)) { - emuThread->emuUnpause(); return; } @@ -1335,34 +1313,19 @@ void MainWindow::onClickRecentFile() recentFileList.prepend(filename); updateRecentFilesMenu(); - assert(emuInstance->nds != nullptr); - emuInstance->nds->Start(); - emuThread->emuRun(); - updateCartInserted(false); } void MainWindow::onBootFirmware() { - emuThread->emuPause(); - if (!verifySetup()) - { - emuThread->emuUnpause(); return; - } - if (!emuInstance->bootToMenu()) + if (!emuThread->bootFirmware()) { - // TODO: better error reporting? QMessageBox::critical(this, "melonDS", "This firmware is not bootable."); - emuThread->emuUnpause(); return; } - - assert(emuInstance->nds != nullptr); - emuInstance->nds->Start(); - emuThread->emuRun(); } void MainWindow::onInsertCart() From 079341f102f1ded4211196c148df420a5a56344c Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 24 Oct 2024 11:44:21 +0200 Subject: [PATCH 11/63] take this a bit further --- src/frontend/qt_sdl/EmuThread.cpp | 60 ++++++++++++++++++++++++++++--- src/frontend/qt_sdl/EmuThread.h | 10 +++++- src/frontend/qt_sdl/Window.cpp | 49 +++++-------------------- 3 files changed, 73 insertions(+), 46 deletions(-) diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 9a7ea6cb..7c3e1a91 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -595,6 +595,36 @@ void EmuThread::handleMessages() emuInstance->nds->Start(); bootResult = 1; break; + + case msg_InsertCart: + bootResult = 0; + if (!emuInstance->loadROM(msg.param.value(), false)) + break; + + bootResult = 1; + break; + + case msg_EjectCart: + emuInstance->ejectCart(); + break; + + case msg_InsertGBACart: + bootResult = 0; + if (!emuInstance->loadGBAROM(msg.param.value())) + break; + + bootResult = 1; + break; + + case msg_InsertGBAAddon: + bootResult = 0; + emuInstance->loadGBAAddon(msg.param.value()); + bootResult = 1; + break; + + case msg_EjectGBACart: + emuInstance->ejectCart(); + break; } msgSemaphore.release(); @@ -681,11 +711,10 @@ bool EmuThread::emuIsActive() return emuActive; } -int EmuThread::bootROM(QStringList filename) +int EmuThread::bootROM(const QStringList& filename) { - sendMessage(msg_EmuPause); sendMessage({.type = msg_BootROM, .param = filename}); - waitMessage(2); + waitMessage(); if (!bootResult) return bootResult; @@ -696,9 +725,8 @@ int EmuThread::bootROM(QStringList filename) int EmuThread::bootFirmware() { - sendMessage(msg_EmuPause); sendMessage(msg_BootFirmware); - waitMessage(2); + waitMessage(); if (!bootResult) return bootResult; @@ -707,6 +735,28 @@ int EmuThread::bootFirmware() return bootResult; } +int EmuThread::insertCart(const QStringList& filename, bool gba) +{ + MessageType msgtype = gba ? msg_InsertGBACart : msg_InsertCart; + + sendMessage({.type = msgtype, .param = filename}); + waitMessage(); + return bootResult; +} + +void EmuThread::ejectCart(bool gba) +{ + sendMessage(gba ? msg_EjectGBACart : msg_EjectCart); + waitMessage(); +} + +int EmuThread::insertGBAAddon(int type) +{ + sendMessage({.type = msg_InsertGBAAddon, .param = type}); + waitMessage(); + return bootResult; +} + void EmuThread::updateRenderer() { if (videoRenderer != lastVideoRenderer) diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index 612f7920..a21cc511 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -71,6 +71,11 @@ public: msg_BootROM, msg_BootFirmware, + msg_InsertCart, + msg_EjectCart, + msg_InsertGBACart, + msg_InsertGBAAddon, + msg_EjectGBACart, }; struct Message @@ -100,8 +105,11 @@ public: void emuFrameStep(); void emuReset(); - int bootROM(QStringList filename); + int bootROM(const QStringList& filename); int bootFirmware(); + int insertCart(const QStringList& filename, bool gba); + void ejectCart(bool gba); + int insertGBAAddon(int type); bool emuIsRunning(); bool emuIsActive(); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 6f2f86a9..c267eb85 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1330,56 +1330,35 @@ void MainWindow::onBootFirmware() void MainWindow::onInsertCart() { - emuThread->emuPause(); - QStringList file = pickROM(false); if (file.isEmpty()) + return; + + if (!emuThread->insertCart(file, false)) { - emuThread->emuUnpause(); return; } - if (!emuInstance->loadROM(file, false)) - { - emuThread->emuUnpause(); - return; - } - - emuThread->emuUnpause(); - updateCartInserted(false); } void MainWindow::onEjectCart() { - emuThread->emuPause(); - - emuInstance->ejectCart(); - - emuThread->emuUnpause(); - + emuThread->ejectCart(false); updateCartInserted(false); } void MainWindow::onInsertGBACart() { - emuThread->emuPause(); - QStringList file = pickROM(true); if (file.isEmpty()) + return; + + if (!emuThread->insertCart(file, true)) { - emuThread->emuUnpause(); return; } - if (!emuInstance->loadGBAROM(file)) - { - emuThread->emuUnpause(); - return; - } - - emuThread->emuUnpause(); - updateCartInserted(true); } @@ -1388,23 +1367,13 @@ void MainWindow::onInsertGBAAddon() QAction* act = (QAction*)sender(); int type = act->data().toInt(); - emuThread->emuPause(); - - emuInstance->loadGBAAddon(type); - - emuThread->emuUnpause(); - + emuThread->insertGBAAddon(type); updateCartInserted(true); } void MainWindow::onEjectGBACart() { - emuThread->emuPause(); - - emuInstance->ejectGBACart(); - - emuThread->emuUnpause(); - + emuThread->ejectCart(true); updateCartInserted(true); } From 6d3ea6a485930c027488ea0f376a12e77aead4bd Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 24 Oct 2024 13:49:43 +0200 Subject: [PATCH 12/63] fix bug with the GBA addon menu (and make it a proper list so we don't have to hardcode the length all over) --- src/frontend/qt_sdl/Window.cpp | 27 +++++++++++++++------------ src/frontend/qt_sdl/Window.h | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index c267eb85..ecf7f771 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -311,14 +311,17 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : { QMenu* submenu = menu->addMenu("Insert add-on cart"); + QAction* act; - actInsertGBAAddon[0] = submenu->addAction("Memory expansion"); - actInsertGBAAddon[0]->setData(QVariant(GBAAddon_RAMExpansion)); - connect(actInsertGBAAddon[0], &QAction::triggered, this, &MainWindow::onInsertGBAAddon); + act = submenu->addAction("Memory expansion"); + act->setData(QVariant(GBAAddon_RAMExpansion)); + connect(act, &QAction::triggered, this, &MainWindow::onInsertGBAAddon); + actInsertGBAAddon.append(act); - actInsertGBAAddon[1] = submenu->addAction("Rumble Pak"); - actInsertGBAAddon[1]->setData(QVariant(GBAAddon_RumblePak)); - connect(actInsertGBAAddon[1], &QAction::triggered, this, &MainWindow::onInsertGBAAddon); + act = submenu->addAction("Rumble Pak"); + act->setData(QVariant(GBAAddon_RumblePak)); + connect(act, &QAction::triggered, this, &MainWindow::onInsertGBAAddon); + actInsertGBAAddon.append(act); } actEjectGBACart = menu->addAction("Eject cart"); @@ -673,8 +676,8 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : if (globalCfg.GetInt("Emu.ConsoleType") == 1) { actInsertGBACart->setEnabled(false); - for (int i = 0; i < 1; i++) - actInsertGBAAddon[i]->setEnabled(false); + for (auto act : actInsertGBAAddon) + act->setEnabled(false); } for (int i = 0; i < 9; i++) @@ -1700,15 +1703,15 @@ void MainWindow::onEmuSettingsDialogFinished(int res) if (globalCfg.GetInt("Emu.ConsoleType") == 1) { actInsertGBACart->setEnabled(false); - for (int i = 0; i < 1; i++) - actInsertGBAAddon[i]->setEnabled(false); + for (auto act : actInsertGBAAddon) + act->setEnabled(false); actEjectGBACart->setEnabled(false); } else { actInsertGBACart->setEnabled(true); - for (int i = 0; i < 1; i++) - actInsertGBAAddon[i]->setEnabled(true); + for (auto act : actInsertGBAAddon) + act->setEnabled(true); actEjectGBACart->setEnabled(emuInstance->gbaCartInserted()); } diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index 30d97b17..9125f76a 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -269,7 +269,7 @@ public: QAction* actEjectCart; QAction* actCurrentGBACart; QAction* actInsertGBACart; - QAction* actInsertGBAAddon[2]; + QList actInsertGBAAddon; QAction* actEjectGBACart; QAction* actImportSavefile; QAction* actSaveState[9]; From 1666049531c5ebd806ee0c865bd6b3442d8caf58 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 24 Oct 2024 14:00:11 +0200 Subject: [PATCH 13/63] fix shit --- src/frontend/qt_sdl/EmuInstance.cpp | 35 +++++++++++++++++++---------- src/frontend/qt_sdl/EmuInstance.h | 1 + src/frontend/qt_sdl/EmuThread.cpp | 2 +- src/frontend/qt_sdl/Window.cpp | 18 +++++++-------- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index edb6c559..c4c003fb 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -1972,25 +1972,36 @@ bool EmuInstance::gbaCartInserted() return gbaCartType != -1; } +QString EmuInstance::gbaAddonName(int addon) +{ + switch (addon) + { + case GBAAddon_RumblePak: + return "Rumble Pak"; + case GBAAddon_RAMExpansion: + return "Memory expansion"; + } + + return "???"; +} + QString EmuInstance::gbaCartLabel() { if (consoleType == 1) return "none (DSi)"; - switch (gbaCartType) + if (gbaCartType == 0) { - case 0: - { - QString ret = QString::fromStdString(baseGBAROMName); + QString ret = QString::fromStdString(baseGBAROMName); - int maxlen = 32; - if (ret.length() > maxlen) - ret = ret.left(maxlen-6) + "..." + ret.right(3); + int maxlen = 32; + if (ret.length() > maxlen) + ret = ret.left(maxlen-6) + "..." + ret.right(3); - return ret; - } - - case GBAAddon_RAMExpansion: - return "Memory expansion"; + return ret; + } + else if (gbaCartType != -1) + { + return gbaAddonName(gbaCartType); } return "(none)"; diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 04290f56..9e0659e0 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -186,6 +186,7 @@ private: void loadGBAAddon(int type); void ejectGBACart(); bool gbaCartInserted(); + QString gbaAddonName(int addon); QString gbaCartLabel(); void audioInit(); diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 7c3e1a91..c3668fe3 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -623,7 +623,7 @@ void EmuThread::handleMessages() break; case msg_EjectGBACart: - emuInstance->ejectCart(); + emuInstance->ejectGBACart(); break; } diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index ecf7f771..ce900cbb 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -313,15 +313,15 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : QMenu* submenu = menu->addMenu("Insert add-on cart"); QAction* act; - act = submenu->addAction("Memory expansion"); - act->setData(QVariant(GBAAddon_RAMExpansion)); - connect(act, &QAction::triggered, this, &MainWindow::onInsertGBAAddon); - actInsertGBAAddon.append(act); - - act = submenu->addAction("Rumble Pak"); - act->setData(QVariant(GBAAddon_RumblePak)); - connect(act, &QAction::triggered, this, &MainWindow::onInsertGBAAddon); - actInsertGBAAddon.append(act); + int addons[] = {GBAAddon_RAMExpansion, GBAAddon_RumblePak, -1}; + for (int i = 0; addons[i] != -1; i++) + { + int addon = addons[i]; + act = submenu->addAction(emuInstance->gbaAddonName(addon)); + act->setData(QVariant(addon)); + connect(act, &QAction::triggered, this, &MainWindow::onInsertGBAAddon); + actInsertGBAAddon.append(act); + } } actEjectGBACart = menu->addAction("Eject cart"); From 13b4cea171f38461fa798b818ce2d1457c1a5c20 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 24 Oct 2024 16:08:04 +0200 Subject: [PATCH 14/63] move screen layout/etc options to new View menu --- src/frontend/qt_sdl/Window.cpp | 95 +++++++++++++++++----------------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index ce900cbb..f88c1dc7 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -458,53 +458,7 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : } } { - QMenu* menu = menubar->addMenu("Config"); - - actEmuSettings = menu->addAction("Emu settings"); - connect(actEmuSettings, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); - -#ifdef __APPLE__ - actPreferences = menu->addAction("Preferences..."); - connect(actPreferences, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); - actPreferences->setMenuRole(QAction::PreferencesRole); -#endif - - actInputConfig = menu->addAction("Input and hotkeys"); - connect(actInputConfig, &QAction::triggered, this, &MainWindow::onOpenInputConfig); - - actVideoSettings = menu->addAction("Video settings"); - connect(actVideoSettings, &QAction::triggered, this, &MainWindow::onOpenVideoSettings); - - actCameraSettings = menu->addAction("Camera settings"); - connect(actCameraSettings, &QAction::triggered, this, &MainWindow::onOpenCameraSettings); - - actAudioSettings = menu->addAction("Audio settings"); - connect(actAudioSettings, &QAction::triggered, this, &MainWindow::onOpenAudioSettings); - - actMPSettings = menu->addAction("Multiplayer settings"); - connect(actMPSettings, &QAction::triggered, this, &MainWindow::onOpenMPSettings); - - actWifiSettings = menu->addAction("Wifi settings"); - connect(actWifiSettings, &QAction::triggered, this, &MainWindow::onOpenWifiSettings); - - actFirmwareSettings = menu->addAction("Firmware settings"); - connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings); - - actInterfaceSettings = menu->addAction("Interface settings"); - connect(actInterfaceSettings, &QAction::triggered, this, &MainWindow::onOpenInterfaceSettings); - - actPathSettings = menu->addAction("Path settings"); - connect(actPathSettings, &QAction::triggered, this, &MainWindow::onOpenPathSettings); - - { - QMenu* submenu = menu->addMenu("Savestate settings"); - - actSavestateSRAMReloc = submenu->addAction("Separate savefiles"); - actSavestateSRAMReloc->setCheckable(true); - connect(actSavestateSRAMReloc, &QAction::triggered, this, &MainWindow::onChangeSavestateSRAMReloc); - } - - menu->addSeparator(); + QMenu *menu = menubar->addMenu("View"); { QMenu* submenu = menu->addMenu("Screen size"); @@ -633,6 +587,53 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : actShowOSD = menu->addAction("Show OSD"); actShowOSD->setCheckable(true); connect(actShowOSD, &QAction::triggered, this, &MainWindow::onChangeShowOSD); + } + { + QMenu* menu = menubar->addMenu("Config"); + + actEmuSettings = menu->addAction("Emu settings"); + connect(actEmuSettings, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); + +#ifdef __APPLE__ + actPreferences = menu->addAction("Preferences..."); + connect(actPreferences, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); + actPreferences->setMenuRole(QAction::PreferencesRole); +#endif + + actInputConfig = menu->addAction("Input and hotkeys"); + connect(actInputConfig, &QAction::triggered, this, &MainWindow::onOpenInputConfig); + + actVideoSettings = menu->addAction("Video settings"); + connect(actVideoSettings, &QAction::triggered, this, &MainWindow::onOpenVideoSettings); + + actCameraSettings = menu->addAction("Camera settings"); + connect(actCameraSettings, &QAction::triggered, this, &MainWindow::onOpenCameraSettings); + + actAudioSettings = menu->addAction("Audio settings"); + connect(actAudioSettings, &QAction::triggered, this, &MainWindow::onOpenAudioSettings); + + actMPSettings = menu->addAction("Multiplayer settings"); + connect(actMPSettings, &QAction::triggered, this, &MainWindow::onOpenMPSettings); + + actWifiSettings = menu->addAction("Wifi settings"); + connect(actWifiSettings, &QAction::triggered, this, &MainWindow::onOpenWifiSettings); + + actFirmwareSettings = menu->addAction("Firmware settings"); + connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings); + + actInterfaceSettings = menu->addAction("Interface settings"); + connect(actInterfaceSettings, &QAction::triggered, this, &MainWindow::onOpenInterfaceSettings); + + actPathSettings = menu->addAction("Path settings"); + connect(actPathSettings, &QAction::triggered, this, &MainWindow::onOpenPathSettings); + + { + QMenu* submenu = menu->addMenu("Savestate settings"); + + actSavestateSRAMReloc = submenu->addAction("Separate savefiles"); + actSavestateSRAMReloc->setCheckable(true); + connect(actSavestateSRAMReloc, &QAction::triggered, this, &MainWindow::onChangeSavestateSRAMReloc); + } menu->addSeparator(); From 1787235e09d78732204172f68648d84d50bb807d Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 24 Oct 2024 17:20:14 +0200 Subject: [PATCH 15/63] fix more shit now it doesn't shit itself on startup if the BIOS paths are wrong --- src/frontend/qt_sdl/EmuInstance.cpp | 38 +++++++++++++- src/frontend/qt_sdl/EmuInstance.h | 5 +- src/frontend/qt_sdl/EmuThread.cpp | 28 ++-------- src/frontend/qt_sdl/Screen.cpp | 81 +++++++++++++++-------------- src/frontend/qt_sdl/Window.cpp | 3 ++ 5 files changed, 89 insertions(+), 66 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index c4c003fb..402af56b 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -154,6 +154,13 @@ EmuInstance::~EmuInstance() audioDeInit(); inputDeInit(); + + NDS::Current = nullptr; + if (nds) + { + saveRTCData(); + delete nds; + } } @@ -1105,6 +1112,30 @@ void EmuInstance::setBatteryLevels() } } +void EmuInstance::loadRTCData() +{ + auto file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Read); + if (file) + { + RTC::StateData state; + Platform::FileRead(&state, sizeof(state), 1, file); + Platform::CloseFile(file); + nds->RTC.SetState(state); + } +} + +void EmuInstance::saveRTCData() +{ + auto file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Write); + if (file) + { + RTC::StateData state; + nds->RTC.GetState(state); + Platform::FileWrite(&state, sizeof(state), 1, file); + Platform::CloseFile(file); + } +} + void EmuInstance::setDateTime() { QDateTime hosttime = QDateTime::currentDateTime(); @@ -1238,7 +1269,11 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB if ((!nds) || (consoleType != nds->ConsoleType)) { NDS::Current = nullptr; - if (nds) delete nds; + if (nds) + { + saveRTCData(); + delete nds; + } if (consoleType == 1) nds = new DSi(std::move(dsiargs.value()), this); @@ -1247,6 +1282,7 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB NDS::Current = nds; nds->Reset(); + loadRTCData(); //emuThread->updateVideoRenderer(); // not actually needed? } else diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 9e0659e0..742bebba 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -168,7 +168,6 @@ private: std::optional getSDCardArgs(const std::string& key) noexcept; std::optional loadSDCard(const std::string& key) noexcept; void setBatteryLevels(); - void setDateTime(); void reset(); bool bootToMenu(); melonDS::u32 decompressROM(const melonDS::u8* inContent, const melonDS::u32 inSize, std::unique_ptr& outContent); @@ -223,6 +222,10 @@ private: bool hotkeyPressed(int id) { return hotkeyPress & (1<getGlobalConfig(); u32 mainScreenPos[3]; - Platform::FileHandle* file; - emuInstance->updateConsole(nullptr, nullptr); + //emuInstance->updateConsole(nullptr, nullptr); // No carts are inserted when melonDS first boots mainScreenPos[0] = 0; @@ -112,7 +111,7 @@ void EmuThread::run() mainScreenPos[2] = 0; autoScreenSizing = 0; - videoSettingsDirty = false; + //videoSettingsDirty = false; if (emuInstance->usesOpenGL()) { @@ -127,7 +126,8 @@ void EmuThread::run() videoRenderer = 0; } - updateRenderer(); + //updateRenderer(); + videoSettingsDirty = true; u32 nframes = 0; double perfCountsSec = 1.0 / SDL_GetPerformanceFrequency(); @@ -138,15 +138,6 @@ void EmuThread::run() u32 winUpdateCount = 0, winUpdateFreq = 1; u8 dsiVolumeLevel = 0x1F; - file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Read); - if (file) - { - RTC::StateData state; - Platform::FileRead(&state, sizeof(state), 1, file); - Platform::CloseFile(file); - emuInstance->nds->RTC.SetState(state); - } - char melontitle[100]; bool fastforward = false; @@ -453,17 +444,6 @@ void EmuThread::run() handleMessages(); } - - file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Write); - if (file) - { - RTC::StateData state; - emuInstance->nds->RTC.GetState(state); - Platform::FileWrite(&state, sizeof(state), 1, file); - Platform::CloseFile(file); - } - - NDS::Current = nullptr; } void EmuThread::sendMessage(Message msg) diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index 425d99b5..3780de55 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -947,9 +947,6 @@ void ScreenPanelGL::drawScreenGL() { if (!glContext) return; - auto nds = emuInstance->getNDS(); - if (!nds) return; - auto emuThread = emuInstance->getEmuThread(); glContext->MakeCurrent(); @@ -968,50 +965,54 @@ void ScreenPanelGL::drawScreenGL() glViewport(0, 0, w, h); - glUseProgram(screenShaderProgram); - glUniform2f(screenShaderScreenSizeULoc, w / factor, h / factor); + if (emuThread->emuIsActive()) + { + auto nds = emuInstance->getNDS(); - int frontbuf = emuThread->FrontBuffer; - glActiveTexture(GL_TEXTURE0); + glUseProgram(screenShaderProgram); + glUniform2f(screenShaderScreenSizeULoc, w / factor, h / factor); + + int frontbuf = emuThread->FrontBuffer; + glActiveTexture(GL_TEXTURE0); #ifdef OGLRENDERER_ENABLED - if (nds->GPU.GetRenderer3D().Accelerated) - { - // hardware-accelerated render - nds->GPU.GetRenderer3D().BindOutputTexture(frontbuf); - } - else -#endif - { - // regular render - glBindTexture(GL_TEXTURE_2D, screenTexture); - - if (nds->GPU.Framebuffer[frontbuf][0] && nds->GPU.Framebuffer[frontbuf][1]) + if (nds->GPU.GetRenderer3D().Accelerated) { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, nds->GPU.Framebuffer[frontbuf][0].get()); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, nds->GPU.Framebuffer[frontbuf][1].get()); + // hardware-accelerated render + nds->GPU.GetRenderer3D().BindOutputTexture(frontbuf); + } else +#endif + { + // regular render + glBindTexture(GL_TEXTURE_2D, screenTexture); + + if (nds->GPU.Framebuffer[frontbuf][0] && nds->GPU.Framebuffer[frontbuf][1]) + { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, + GL_UNSIGNED_BYTE, nds->GPU.Framebuffer[frontbuf][0].get()); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192 + 2, 256, 192, GL_RGBA, + GL_UNSIGNED_BYTE, nds->GPU.Framebuffer[frontbuf][1].get()); + } } + + screenSettingsLock.lock(); + + GLint filter = this->filter ? GL_LINEAR : GL_NEAREST; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); + + glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); + glBindVertexArray(screenVertexArray); + + for (int i = 0; i < numScreens; i++) + { + glUniformMatrix2x3fv(screenShaderTransformULoc, 1, GL_TRUE, screenMatrix[i]); + glDrawArrays(GL_TRIANGLES, screenKind[i] == 0 ? 0 : 2 * 3, 2 * 3); + } + + screenSettingsLock.unlock(); } - screenSettingsLock.lock(); - - GLint filter = this->filter ? GL_LINEAR : GL_NEAREST; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); - - glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); - glBindVertexArray(screenVertexArray); - - for (int i = 0; i < numScreens; i++) - { - glUniformMatrix2x3fv(screenShaderTransformULoc, 1, GL_TRUE, screenMatrix[i]); - glDrawArrays(GL_TRIANGLES, screenKind[i] == 0 ? 0 : 2*3, 2*3); - } - - screenSettingsLock.unlock(); - osdUpdate(); if (osdEnabled) { diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index f88c1dc7..a19b29ed 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1009,6 +1009,9 @@ bool MainWindow::verifySetup() bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) { + if (file.isEmpty() && gbafile.isEmpty()) + return false; + if (!verifySetup()) { return false; From 3fc065d72d03112ba26b617033537e398e3fbcb2 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 24 Oct 2024 17:48:34 +0200 Subject: [PATCH 16/63] fix ROM preloading to also go through EmuThread --- src/frontend/qt_sdl/Window.cpp | 31 +++++++++++++++---------------- src/frontend/qt_sdl/main.cpp | 4 +--- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index a19b29ed..65e85f1c 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1020,7 +1020,8 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) bool gbaloaded = false; if (!gbafile.isEmpty()) { - if (!emuInstance->loadGBAROM(gbafile)) return false; + if (!emuThread->insertCart(gbafile, true)) + return false; gbaloaded = true; } @@ -1028,33 +1029,31 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) bool ndsloaded = false; if (!file.isEmpty()) { - if (!emuInstance->loadROM(file, true)) return false; + if (boot) + { + if (!emuThread->bootROM(file)) + return false; + } + else + { + if (!emuThread->insertCart(file, false)) + return false; + } recentFileList.removeAll(file.join("|")); recentFileList.prepend(file.join("|")); updateRecentFilesMenu(); ndsloaded = true; } - - if (boot) + else if (boot) { - if (ndsloaded) - { - emuInstance->nds->Start(); - emuThread->emuRun(); - } - else - { - onBootFirmware(); - } + if (!emuThread->bootFirmware()) + return false; } updateCartInserted(false); - if (gbaloaded) - { updateCartInserted(true); - } return true; } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 2643104d..6d18adaf 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -250,10 +250,8 @@ bool MelonApplication::event(QEvent *event) MainWindow* win = inst->getMainWindow(); QFileOpenEvent *openEvent = static_cast(event); - inst->getEmuThread()->emuPause(); const QStringList file = win->splitArchivePath(openEvent->file(), true); - if (!win->preloadROMs(file, {}, true)) - inst->getEmuThread()->emuUnpause(); + win->preloadROMs(file, {}, true); } return QApplication::event(event); From 75e6856af479b81f7066abc97e20d83f781a64c4 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 24 Oct 2024 19:27:45 +0200 Subject: [PATCH 17/63] route savestate stuff through EmuThread --- src/frontend/qt_sdl/EmuThread.cpp | 70 +++++++++++++++++++++++-------- src/frontend/qt_sdl/EmuThread.h | 10 ++++- src/frontend/qt_sdl/Window.cpp | 45 ++++++-------------- 3 files changed, 73 insertions(+), 52 deletions(-) diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 590d10f5..d73cd6ea 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -557,31 +557,31 @@ void EmuThread::handleMessages() break; case msg_BootROM: - bootResult = 0; + msgResult = 0; if (!emuInstance->loadROM(msg.param.value(), true)) break; assert(emuInstance->nds != nullptr); emuInstance->nds->Start(); - bootResult = 1; + msgResult = 1; break; case msg_BootFirmware: - bootResult = 0; + msgResult = 0; if (!emuInstance->bootToMenu()) break; assert(emuInstance->nds != nullptr); emuInstance->nds->Start(); - bootResult = 1; + msgResult = 1; break; case msg_InsertCart: - bootResult = 0; + msgResult = 0; if (!emuInstance->loadROM(msg.param.value(), false)) break; - bootResult = 1; + msgResult = 1; break; case msg_EjectCart: @@ -589,22 +589,35 @@ void EmuThread::handleMessages() break; case msg_InsertGBACart: - bootResult = 0; + msgResult = 0; if (!emuInstance->loadGBAROM(msg.param.value())) break; - bootResult = 1; + msgResult = 1; break; case msg_InsertGBAAddon: - bootResult = 0; + msgResult = 0; emuInstance->loadGBAAddon(msg.param.value()); - bootResult = 1; + msgResult = 1; break; case msg_EjectGBACart: emuInstance->ejectGBACart(); break; + + case msg_SaveState: + msgResult = emuInstance->saveState(msg.param.value().toStdString()); + break; + + case msg_LoadState: + msgResult = emuInstance->loadState(msg.param.value().toStdString()); + break; + + case msg_UndoStateLoad: + emuInstance->undoStateLoad(); + msgResult = 1; + break; } msgSemaphore.release(); @@ -695,24 +708,24 @@ int EmuThread::bootROM(const QStringList& filename) { sendMessage({.type = msg_BootROM, .param = filename}); waitMessage(); - if (!bootResult) - return bootResult; + if (!msgResult) + return msgResult; sendMessage(msg_EmuRun); waitMessage(); - return bootResult; + return msgResult; } int EmuThread::bootFirmware() { sendMessage(msg_BootFirmware); waitMessage(); - if (!bootResult) - return bootResult; + if (!msgResult) + return msgResult; sendMessage(msg_EmuRun); waitMessage(); - return bootResult; + return msgResult; } int EmuThread::insertCart(const QStringList& filename, bool gba) @@ -721,7 +734,7 @@ int EmuThread::insertCart(const QStringList& filename, bool gba) sendMessage({.type = msgtype, .param = filename}); waitMessage(); - return bootResult; + return msgResult; } void EmuThread::ejectCart(bool gba) @@ -734,7 +747,28 @@ int EmuThread::insertGBAAddon(int type) { sendMessage({.type = msg_InsertGBAAddon, .param = type}); waitMessage(); - return bootResult; + return msgResult; +} + +int EmuThread::saveState(const QString& filename) +{ + sendMessage({.type = msg_SaveState, .param = filename}); + waitMessage(); + return msgResult; +} + +int EmuThread::loadState(const QString& filename) +{ + sendMessage({.type = msg_LoadState, .param = filename}); + waitMessage(); + return msgResult; +} + +int EmuThread::undoStateLoad() +{ + sendMessage(msg_UndoStateLoad); + waitMessage(); + return msgResult; } void EmuThread::updateRenderer() diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index a21cc511..52648520 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -76,6 +76,10 @@ public: msg_InsertGBACart, msg_InsertGBAAddon, msg_EjectGBACart, + + msg_LoadState, + msg_SaveState, + msg_UndoStateLoad, }; struct Message @@ -111,6 +115,10 @@ public: void ejectCart(bool gba); int insertGBAAddon(int type); + int saveState(const QString& filename); + int loadState(const QString& filename); + int undoStateLoad(); + bool emuIsRunning(); bool emuIsActive(); @@ -164,7 +172,7 @@ private: constexpr static int emuPauseStackPauseThreshold = 1; int emuPauseStack; - int bootResult = 0; + int msgResult = 0; QMutex msgMutex; QSemaphore msgSemaphore; diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 65e85f1c..cf65fe2c 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1387,30 +1387,23 @@ void MainWindow::onSaveState() { int slot = ((QAction*)sender())->data().toInt(); - emuThread->emuPause(); - - std::string filename; + QString filename; if (slot > 0) { - filename = emuInstance->getSavestateName(slot); + filename = QString::fromStdString(emuInstance->getSavestateName(slot)); } else { // TODO: specific 'last directory' for savestate files? - QString qfilename = QFileDialog::getSaveFileName(this, + filename = QFileDialog::getSaveFileName(this, "Save state", globalCfg.GetQString("LastROMFolder"), "melonDS savestates (*.mln);;Any file (*.*)"); - if (qfilename.isEmpty()) - { - emuThread->emuUnpause(); + if (filename.isEmpty()) return; - } - - filename = qfilename.toStdString(); } - if (emuInstance->saveState(filename)) + if (emuThread->saveState(filename)) { if (slot > 0) emuInstance->osdAddMessage(0, "State saved to slot %d", slot); else emuInstance->osdAddMessage(0, "State saved to file"); @@ -1421,47 +1414,37 @@ void MainWindow::onSaveState() { emuInstance->osdAddMessage(0xFFA0A0, "State save failed"); } - - emuThread->emuUnpause(); } void MainWindow::onLoadState() { int slot = ((QAction*)sender())->data().toInt(); - emuThread->emuPause(); - - std::string filename; + QString filename; if (slot > 0) { - filename = emuInstance->getSavestateName(slot); + filename = QString::fromStdString(emuInstance->getSavestateName(slot)); } else { // TODO: specific 'last directory' for savestate files? - QString qfilename = QFileDialog::getOpenFileName(this, + filename = QFileDialog::getOpenFileName(this, "Load state", globalCfg.GetQString("LastROMFolder"), "melonDS savestates (*.ml*);;Any file (*.*)"); - if (qfilename.isEmpty()) - { - emuThread->emuUnpause(); + if (filename.isEmpty()) return; - } - - filename = qfilename.toStdString(); } - if (!Platform::FileExists(filename)) + if (!Platform::FileExists(filename.toStdString())) { if (slot > 0) emuInstance->osdAddMessage(0xFFA0A0, "State slot %d is empty", slot); else emuInstance->osdAddMessage(0xFFA0A0, "State file does not exist"); - emuThread->emuUnpause(); return; } - if (emuInstance->loadState(filename)) + if (emuThread->loadState(filename)) { if (slot > 0) emuInstance->osdAddMessage(0, "State loaded from slot %d", slot); else emuInstance->osdAddMessage(0, "State loaded from file"); @@ -1472,15 +1455,11 @@ void MainWindow::onLoadState() { emuInstance->osdAddMessage(0xFFA0A0, "State load failed"); } - - emuThread->emuUnpause(); } void MainWindow::onUndoStateLoad() { - emuThread->emuPause(); - emuInstance->undoStateLoad(); - emuThread->emuUnpause(); + emuThread->undoStateLoad(); emuInstance->osdAddMessage(0, "State load undone"); } From 9ebc96d1215260dc10670323bfba82340e86cd90 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Thu, 24 Oct 2024 19:55:41 +0200 Subject: [PATCH 18/63] vcpkg 2024.10.21 --- .github/workflows/build-macos.yml | 2 +- .github/workflows/build-windows.yml | 2 +- cmake/ConfigureVcpkg.cmake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index b2dda12d..47e0f685 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -28,7 +28,7 @@ jobs: - name: Set up vcpkg uses: lukka/run-vcpkg@v11 with: - vcpkgGitCommitId: 3508985146f1b1d248c67ead13f8f54be5b4f5da + vcpkgGitCommitId: 10b7a178346f3f0abef60cecd5130e295afd8da4 - name: Build uses: lukka/run-cmake@v10 with: diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 30dbd2a8..c1cb1856 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -27,7 +27,7 @@ jobs: - name: Set up vcpkg uses: lukka/run-vcpkg@v11 with: - vcpkgGitCommitId: 3508985146f1b1d248c67ead13f8f54be5b4f5da + vcpkgGitCommitId: 10b7a178346f3f0abef60cecd5130e295afd8da4 - name: Configure run: cmake --preset=release-mingw-x86_64 - name: Build diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake index f8c33fd4..c1eb522d 100644 --- a/cmake/ConfigureVcpkg.cmake +++ b/cmake/ConfigureVcpkg.cmake @@ -9,7 +9,7 @@ if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}") endif() FetchContent_Declare(vcpkg GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git" - GIT_TAG 2024.08.23 + GIT_TAG 2024.10.21 SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg") FetchContent_MakeAvailable(vcpkg) endif() From a97463b0ac54d6f5cfd10824aa63a3af9c4b9d72 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Thu, 24 Oct 2024 21:02:41 +0200 Subject: [PATCH 19/63] nix: update inputs --- flake.lock | 12 ++++++------ flake.nix | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/flake.lock b/flake.lock index 9f0fe239..be75f57f 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", "type": "github" }, "original": { @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1725432240, - "narHash": "sha256-+yj+xgsfZaErbfYM3T+QvEE2hU7UuE+Jf0fJCJ8uPS0=", + "lastModified": 1729665710, + "narHash": "sha256-AlcmCXJZPIlO5dmFzV3V2XF6x/OpNWUV8Y/FMPGd8Z4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ad416d066ca1222956472ab7d0555a6946746a80", + "rev": "2768c7d042a37de65bb1b5b3268fc987e534c49d", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 8236ccd3..4b5cd397 100644 --- a/flake.nix +++ b/flake.nix @@ -16,7 +16,7 @@ then sourceInfo.dirtyShortRev else sourceInfo.shortRev; - melonDS = pkgs.stdenv.mkDerivation { + melonDS = pkgs.qt6.qtbase.stdenv.mkDerivation { pname = "melonDS"; version = "0.9.5-${versionSuffix}"; src = ./.; From 287f6642fc78ca84f0e37e70c8c736a662d4884f Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Thu, 24 Oct 2024 22:05:30 +0200 Subject: [PATCH 20/63] Add an About dialog with build info (#2138) add About dialog --- .github/workflows/build-macos.yml | 6 + .github/workflows/build-ubuntu.yml | 10 +- .github/workflows/build-windows.yml | 7 +- flake.nix | 12 +- src/CMakeLists.txt | 11 + src/frontend/qt_sdl/AboutDialog.cpp | 58 ++++++ src/frontend/qt_sdl/AboutDialog.h | 50 +++++ src/frontend/qt_sdl/AboutDialog.ui | 300 ++++++++++++++++++++++++++++ src/frontend/qt_sdl/CMakeLists.txt | 3 + src/frontend/qt_sdl/Window.cpp | 10 + src/frontend/qt_sdl/Window.h | 2 + src/version.h.in | 6 + 12 files changed, 470 insertions(+), 5 deletions(-) create mode 100644 src/frontend/qt_sdl/AboutDialog.cpp create mode 100644 src/frontend/qt_sdl/AboutDialog.h create mode 100644 src/frontend/qt_sdl/AboutDialog.ui diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 47e0f685..f47b3a4a 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -9,6 +9,11 @@ on: branches: - master +env: + MELONDS_GIT_BRANCH: ${{ github.ref }} + MELONDS_GIT_HASH: ${{ github.sha }} + MELONDS_BUILD_PROVIDER: GitHub Actions + jobs: build-macos: strategy: @@ -34,6 +39,7 @@ jobs: with: configurePreset: release-mac-${{ matrix.arch }} buildPreset: release-mac-${{ matrix.arch }} + configurePresetAdditionalArgs: "['-DMELONDS_EMBED_BUILD_INFO=ON']" - name: Compress app bundle shell: bash run: | diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 1bcf2f66..044d01ee 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -8,6 +8,11 @@ on: branches: - master +env: + MELONDS_GIT_BRANCH: ${{ github.ref }} + MELONDS_GIT_HASH: ${{ github.sha }} + MELONDS_BUILD_PROVIDER: GitHub Actions + jobs: build-x86_64: name: x86_64 @@ -23,7 +28,7 @@ jobs: sudo apt install --allow-downgrades cmake ninja-build extra-cmake-modules libpcap0.8-dev libsdl2-dev libenet-dev \ qt6-{base,base-private,multimedia}-dev libqt6svg6-dev libarchive-dev libzstd-dev libfuse2 - name: Configure - run: cmake -B build -G Ninja -DUSE_QT6=ON -DCMAKE_INSTALL_PREFIX=/usr + run: cmake -B build -G Ninja -DUSE_QT6=ON -DCMAKE_INSTALL_PREFIX=/usr -DMELONDS_EMBED_BUILD_INFO=ON - name: Build run: | cmake --build build @@ -74,7 +79,8 @@ jobs: -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config \ -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc-12 \ -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++-12 \ - -DUSE_QT6=ON + -DUSE_QT6=ON \ + -DMELONDS_EMBED_BUILD_INFO=ON - name: Build shell: bash run: | diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index c1cb1856..c3350b4d 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -9,6 +9,11 @@ on: branches: - master +env: + MELONDS_GIT_BRANCH: ${{ github.ref }} + MELONDS_GIT_HASH: ${{ github.sha }} + MELONDS_BUILD_PROVIDER: GitHub Actions + jobs: build: runs-on: windows-latest @@ -29,7 +34,7 @@ jobs: with: vcpkgGitCommitId: 10b7a178346f3f0abef60cecd5130e295afd8da4 - name: Configure - run: cmake --preset=release-mingw-x86_64 + run: cmake --preset=release-mingw-x86_64 -DMELONDS_EMBED_BUILD_INFO=ON - name: Build run: cmake --build --preset=release-mingw-x86_64 - uses: actions/upload-artifact@v4 diff --git a/flake.nix b/flake.nix index 4b5cd397..e898674a 100644 --- a/flake.nix +++ b/flake.nix @@ -12,13 +12,16 @@ inherit (pkgs.lib) cmakeBool optionals makeLibraryPath; inherit (pkgs.stdenv) isLinux isDarwin; - versionSuffix = with self; if sourceInfo?dirtyShortRev + revision = with self; if sourceInfo?dirtyRev + then sourceInfo.dirtyRev + else sourceInfo.rev; + shortRevision = with self; if sourceInfo?dirtyShortRev then sourceInfo.dirtyShortRev else sourceInfo.shortRev; melonDS = pkgs.qt6.qtbase.stdenv.mkDerivation { pname = "melonDS"; - version = "0.9.5-${versionSuffix}"; + version = "0.9.5-${shortRevision}"; src = ./.; nativeBuildInputs = with pkgs; [ @@ -46,8 +49,13 @@ cmakeFlags = [ (cmakeBool "USE_QT6" true) (cmakeBool "USE_SYSTEM_LIBSLIRP" true) + (cmakeBool "MELONDS_EMBED_BUILD_INFO" true) ]; + env.MELONDS_GIT_HASH = revision; + env.MELONDS_GIT_BRANCH = "(unknown)"; + env.MELONDS_BUILD_PROVIDER = "Nix"; + qtWrapperArgs = optionals isLinux [ "--prefix LD_LIBRARY_PATH : ${makeLibraryPath [ pkgs.libpcap pkgs.wayland ]}" ] ++ optionals isDarwin [ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3e177835..1f947d11 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -128,6 +128,17 @@ if (ENABLE_JIT) endif() set(MELONDS_VERSION_SUFFIX "$ENV{MELONDS_VERSION_SUFFIX}" CACHE STRING "Suffix to add to displayed melonDS version") +option(MELONDS_EMBED_BUILD_INFO "Embed detailed build info into the binary" OFF) +set(MELONDS_GIT_BRANCH "$ENV{MELONDS_GIT_BRANCH}" CACHE STRING "The Git branch used for this build") +set(MELONDS_GIT_HASH "$ENV{MELONDS_GIT_HASH}" CACHE STRING "The hash of the Git commit") +set(MELONDS_BUILD_PROVIDER "$ENV{MELONDS_BUILD_PROVIDER}" CACHE STRING "The name of the provider of this build") + +if (MELONDS_EMBED_BUILD_INFO) + target_compile_definitions(core PUBLIC MELONDS_EMBED_BUILD_INFO) + if (NOT MELONDS_GIT_BRANCH OR NOT MELONDS_GIT_HASH OR NOT MELONDS_BUILD_PROVIDER) + message(FATAL_ERROR "When embedding build information, all fields must be filled out. See src/CMakeLists.txt.") + endif() +endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h") target_sources(core PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/version.h") diff --git a/src/frontend/qt_sdl/AboutDialog.cpp b/src/frontend/qt_sdl/AboutDialog.cpp new file mode 100644 index 00000000..ccf39eaa --- /dev/null +++ b/src/frontend/qt_sdl/AboutDialog.cpp @@ -0,0 +1,58 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include "AboutDialog.h" + +#include + +#include "ui_AboutDialog.h" + +#include "version.h" + +AboutDialog::AboutDialog(QWidget *parent) : + QDialog(parent), ui(new Ui::AboutDialog) +{ + ui->setupUi(this); + + ui->lblVersionInfo->setText("Version " MELONDS_VERSION); +#ifdef MELONDS_EMBED_BUILD_INFO + ui->lblBuildInfo->setText( + "Branch: " MELONDS_GIT_BRANCH "\n" + "Commit: " MELONDS_GIT_HASH "\n" + "Built by: " MELONDS_BUILD_PROVIDER + ); +#else + ui->lblBuildInfo->hide(); +#endif + adjustSize(); +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} + +void AboutDialog::openWebsite() +{ + QDesktopServices::openUrl(QUrl(MELONDS_URL)); +} + +void AboutDialog::openGitHub() +{ + QDesktopServices::openUrl(QUrl("https://github.com/melonDS-emu/melonDS"));; +} diff --git a/src/frontend/qt_sdl/AboutDialog.h b/src/frontend/qt_sdl/AboutDialog.h new file mode 100644 index 00000000..eb328f5b --- /dev/null +++ b/src/frontend/qt_sdl/AboutDialog.h @@ -0,0 +1,50 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef MELONDS_ABOUTDIALOG_H +#define MELONDS_ABOUTDIALOG_H + +#include + + +QT_BEGIN_NAMESPACE +namespace Ui +{ + class AboutDialog; +} +QT_END_NAMESPACE + +class AboutDialog : public QDialog +{ +Q_OBJECT + +public: + explicit AboutDialog(QWidget *parent = nullptr); + + ~AboutDialog() override; + +private slots: + static void openWebsite(); + static void openGitHub(); + +private: + Ui::AboutDialog *ui; +}; + + +#endif //MELONDS_ABOUTDIALOG_H diff --git a/src/frontend/qt_sdl/AboutDialog.ui b/src/frontend/qt_sdl/AboutDialog.ui new file mode 100644 index 00000000..89ca7a36 --- /dev/null +++ b/src/frontend/qt_sdl/AboutDialog.ui @@ -0,0 +1,300 @@ + + + AboutDialog + + + + 0 + 0 + 600 + 304 + + + + + 0 + 0 + + + + + 600 + 0 + + + + About melonDS + + + false + + + + 0 + + + QLayout::SizeConstraint::SetFixedSize + + + 12 + + + + + 24 + + + QLayout::SizeConstraint::SetDefaultConstraint + + + + + true + + + + 0 + 0 + + + + + 128 + 128 + + + + + 128 + 128 + + + + + + + Qt::TextFormat::PlainText + + + :/melon-icon + + + true + + + Qt::AlignmentFlag::AlignCenter + + + 0 + + + 0 + + + + + + + 12 + + + + + true + + + + 32 + + + + melonDS + + + + + + + true + + + [VERSION INFO PLACEHOLDER] + + + Qt::TextFormat::MarkdownText + + + + + + + true + + + By Arisotura, the melonDS team <a href="https://github.com/melonDS-emu/melonDS/graphs/contributors">and contributors</a>. + + + + + + + true + + + [EMBEDDED BUILD INFO PLACEHOLDER] + + + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse + + + + + + + false + + + + 10 + + + + <html><head/><body><p>Licensed under the GNU General Public License v3 or any newer version.</p><p>melonDS is intended only for use with software that you own.</p><p>The Nintendo DS and Nintendo DSi systems are the property of Nintendo.<br />melonDS and the melonDS team are not affiliated with or endorsed by Nintendo.</p></body></html> + + + Qt::TextFormat::RichText + + + false + + + + + + + + + + + 12 + + + 12 + + + + + true + + + Qt::Orientation::Horizontal + + + + 40 + 0 + + + + + + + + true + + + Visit the &website + + + true + + + + + + + true + + + View on &GitHub + + + true + + + + + + + true + + + &Close + + + true + + + + + + + + + + + + + btnClose + clicked() + AboutDialog + accept() + + + 586 + 261 + + + 294 + 154 + + + + + btnOpenGitHub + clicked() + AboutDialog + openGitHub() + + + 449 + 243 + + + 299 + 139 + + + + + btnOpenWebsite + clicked() + AboutDialog + openWebsite() + + + 345 + 245 + + + 96 + 275 + + + + + + openWebsite() + openGitHub() + + diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 524fa13d..56f82d89 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -37,6 +37,9 @@ set(SOURCES_QT_SDL QPathInput.h SaveManager.cpp CameraManager.cpp + AboutDialog.cpp + AboutDialog.h + AboutDialog.ui ArchiveUtil.h ArchiveUtil.cpp diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index cf65fe2c..d87c049d 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -82,6 +82,7 @@ #include "ArchiveUtil.h" #include "CameraManager.h" #include "Window.h" +#include "AboutDialog.h" using namespace melonDS; @@ -645,6 +646,15 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : actAudioSync->setCheckable(true); connect(actAudioSync, &QAction::triggered, this, &MainWindow::onChangeAudioSync); } + { + QMenu* menu = menubar->addMenu("Help"); + actAbout = menu->addAction("About..."); + connect(actAbout, &QAction::triggered, this, [&]{ + auto dialog = AboutDialog(this); + dialog.exec(); + }); + } + setMenuBar(menubar); if (localCfg.GetString("Firmware.Username") == "Arisotura") diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index 9125f76a..98298c0b 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -329,6 +329,8 @@ public: QAction* actShowOSD; QAction* actLimitFramerate; QAction* actAudioSync; + + QAction* actAbout; }; void ToggleFullscreen(MainWindow* mainWindow); diff --git a/src/version.h.in b/src/version.h.in index 5aed0d49..9b4cd8ce 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -25,5 +25,11 @@ #define MELONDS_VERSION_SUFFIX "${MELONDS_VERSION_SUFFIX}" #define MELONDS_VERSION MELONDS_VERSION_BASE MELONDS_VERSION_SUFFIX +#ifdef MELONDS_EMBED_BUILD_INFO +#define MELONDS_GIT_BRANCH "${MELONDS_GIT_BRANCH}" +#define MELONDS_GIT_HASH "${MELONDS_GIT_HASH}" +#define MELONDS_BUILD_PROVIDER "${MELONDS_BUILD_PROVIDER}" +#endif + #endif // VERSION_H From 8b6628b07094d658b89a9734f959e4994fae85b2 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 25 Oct 2024 01:15:59 +0200 Subject: [PATCH 21/63] Work around Qt windows11 theme menu bar padding --- src/frontend/qt_sdl/Window.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index d87c049d..2a694879 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -264,6 +264,13 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : setAcceptDrops(true); setFocusPolicy(Qt::ClickFocus); +#ifdef WIN32 + // The "windows11" theme has pretty massive padding around menubar items, this makes Config and Help not fit in a window at 1x screen sizing + // So let's reduce the padding a bit. + if (QApplication::style()->name() == "windows11") + setStyleSheet("QMenuBar::item { padding: 4px 8px; }"); +#endif + QMenuBar* menubar = new QMenuBar(); { QMenu* menu = menubar->addMenu("File"); From 979f1ed6159af0aae2768dc7c4c2711fa551f946 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 25 Oct 2024 10:52:47 +0200 Subject: [PATCH 22/63] same shit with Import Savefile --- src/frontend/qt_sdl/EmuThread.cpp | 28 ++++++++++++++++++++++++++++ src/frontend/qt_sdl/EmuThread.h | 4 ++++ src/frontend/qt_sdl/Window.cpp | 27 ++++++--------------------- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index d73cd6ea..589f11e2 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -618,6 +618,26 @@ void EmuThread::handleMessages() emuInstance->undoStateLoad(); msgResult = 1; break; + + case msg_ImportSavefile: + { + msgResult = 0; + auto f = Platform::OpenFile(msg.param.value().toStdString(), Platform::FileMode::Read); + if (!f) break; + + u32 len = FileLength(f); + + std::unique_ptr data = std::make_unique(len); + Platform::FileRewind(f); + Platform::FileRead(data.get(), len, 1, f); + + assert(emuInstance->nds != nullptr); + emuInstance->nds->SetNDSSave(data.get(), len); + + CloseFile(f); + msgResult = 1; + } + break; } msgSemaphore.release(); @@ -771,6 +791,14 @@ int EmuThread::undoStateLoad() return msgResult; } +int EmuThread::importSavefile(const QString& filename) +{ + sendMessage(msg_EmuReset); + sendMessage({.type = msg_ImportSavefile, .param = filename}); + waitMessage(2); + return msgResult; +} + void EmuThread::updateRenderer() { if (videoRenderer != lastVideoRenderer) diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index 52648520..7a07c42c 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -80,6 +80,8 @@ public: msg_LoadState, msg_SaveState, msg_UndoStateLoad, + + msg_ImportSavefile, }; struct Message @@ -119,6 +121,8 @@ public: int loadState(const QString& filename); int undoStateLoad(); + int importSavefile(const QString& filename); + bool emuIsRunning(); bool emuIsActive(); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 2a694879..08a9474b 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1483,23 +1483,17 @@ void MainWindow::onUndoStateLoad() void MainWindow::onImportSavefile() { - emuThread->emuPause(); QString path = QFileDialog::getOpenFileName(this, "Select savefile", globalCfg.GetQString("LastROMFolder"), "Savefiles (*.sav *.bin *.dsv);;Any file (*.*)"); if (path.isEmpty()) - { - emuThread->emuUnpause(); return; - } - Platform::FileHandle* f = Platform::OpenFile(path.toStdString(), Platform::FileMode::Read); - if (!f) + if (!Platform::FileExists(path.toStdString())) { QMessageBox::critical(this, "melonDS", "Could not open the given savefile."); - emuThread->emuUnpause(); return; } @@ -1510,24 +1504,15 @@ void MainWindow::onImportSavefile() "The emulation will be reset and the current savefile overwritten.", QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) { - emuThread->emuUnpause(); return; } - - emuInstance->reset(); } - u32 len = FileLength(f); - - std::unique_ptr data = std::make_unique(len); - Platform::FileRewind(f); - Platform::FileRead(data.get(), len, 1, f); - - assert(emuInstance->nds != nullptr); - emuInstance->nds->SetNDSSave(data.get(), len); - - CloseFile(f); - emuThread->emuUnpause(); + if (!emuThread->importSavefile(path)) + { + QMessageBox::critical(this, "melonDS", "Could not import the given savefile."); + return; + } } void MainWindow::onQuit() From fc3c7440d1524409700c775baade73be1d6c0fe0 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 25 Oct 2024 12:33:04 +0200 Subject: [PATCH 23/63] fix that crash --- src/frontend/qt_sdl/Window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 08a9474b..47113993 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1800,6 +1800,7 @@ void MainWindow::onUpdateAudioVolume(int vol, int dsisync) void MainWindow::onUpdateAudioSettings() { + if (!emuThread->emuIsActive()) return; assert(emuInstance->nds != nullptr); int interp = globalCfg.GetInt("Audio.Interpolation"); From 1d284f6f1e0afc61431c639b7fafc892d4fffc64 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 25 Oct 2024 16:16:23 +0200 Subject: [PATCH 24/63] as promised, reroute dropEvent() through EmuThread --- src/frontend/qt_sdl/Window.cpp | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 47113993..7c6c0c4c 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -920,20 +920,12 @@ void MainWindow::dropEvent(QDropEvent* event) QList urls = event->mimeData()->urls(); if (urls.count() > 1) return; // not handling more than one file at once - emuThread->emuPause(); - if (!verifySetup()) - { - emuThread->emuUnpause(); return; - } const QStringList file = splitArchivePath(urls.at(0).toLocalFile(), false); if (file.isEmpty()) - { - emuThread->emuUnpause(); return; - } const QString filename = file.last(); const bool romInsideArchive = file.size() > 1; @@ -947,9 +939,8 @@ void MainWindow::dropEvent(QDropEvent* event) if (isNdsRom) { - if (!emuInstance->loadROM(file, true)) + if (!emuThread->bootROM(file)) { - emuThread->emuUnpause(); return; } @@ -958,28 +949,20 @@ void MainWindow::dropEvent(QDropEvent* event) recentFileList.prepend(barredFilename); updateRecentFilesMenu(); - assert(emuInstance->nds != nullptr); - emuInstance->nds->Start(); - emuThread->emuRun(); - updateCartInserted(false); } else if (isGbaRom) { - if (!emuInstance->loadGBAROM(file)) + if (!emuThread->insertCart(file, true)) { - emuThread->emuUnpause(); return; } - emuThread->emuUnpause(); - updateCartInserted(true); } else { QMessageBox::critical(this, "melonDS", "The file could not be recognized as a DS or GBA ROM."); - emuThread->emuUnpause(); return; } } From f375099613e73c69b169d2ef504705c1fbb3c023 Mon Sep 17 00:00:00 2001 From: Citrodata <39993466+Citrodata@users.noreply.github.com> Date: Sat, 26 Oct 2024 23:03:36 +0200 Subject: [PATCH 25/63] Update EmuThread.h (#2171) * Update EmuThread.h Add missing include QVariant. This fixes and error when building on Arch. * Update AboutDialog.cpp --- src/frontend/qt_sdl/AboutDialog.cpp | 1 + src/frontend/qt_sdl/EmuThread.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/frontend/qt_sdl/AboutDialog.cpp b/src/frontend/qt_sdl/AboutDialog.cpp index ccf39eaa..22022de9 100644 --- a/src/frontend/qt_sdl/AboutDialog.cpp +++ b/src/frontend/qt_sdl/AboutDialog.cpp @@ -19,6 +19,7 @@ #include "AboutDialog.h" #include +#include #include "ui_AboutDialog.h" diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index 7a07c42c..c53740c8 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include From 881a740cabbbc5b3e877572de474df03d9dc4dd5 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 01:14:29 +0200 Subject: [PATCH 26/63] start actually implementing multi-window feature, still rough around the edges fix crash when closing main window if sub windows are involved fix OpenGL context handling, still need to fix when changing display type --- src/frontend/qt_sdl/EmuInstance.cpp | 35 +++++++++++++---------------- src/frontend/qt_sdl/EmuInstance.h | 4 ++-- src/frontend/qt_sdl/EmuThread.cpp | 14 ++++++------ src/frontend/qt_sdl/EmuThread.h | 4 ++-- src/frontend/qt_sdl/Screen.cpp | 6 +++++ src/frontend/qt_sdl/Screen.h | 1 + src/frontend/qt_sdl/Window.cpp | 16 +++++++++++-- src/frontend/qt_sdl/Window.h | 2 ++ 8 files changed, 49 insertions(+), 33 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 402af56b..09f6a2bc 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -199,6 +199,10 @@ void EmuInstance::createWindow() numWindows++; emuThread->attachWindow(win); + + // if creating a secondary window, we may need to initialize its OpenGL context here + if (win->hasOpenGL() && (win != topWindow)) + emuThread->initContext(id); } void EmuInstance::deleteWindow(int id, bool close) @@ -208,12 +212,8 @@ void EmuInstance::deleteWindow(int id, bool close) MainWindow* win = windowList[id]; if (!win) return; - if (win->hasOpenGL() && win == mainWindow) - { - // we intentionally don't unpause here - emuThread->emuPause(); - emuThread->deinitContext(); - } + if (win->hasOpenGL()) + emuThread->deinitContext(id); emuThread->detachWindow(win); @@ -226,9 +226,10 @@ void EmuInstance::deleteWindow(int id, bool close) if (close) win->close(); - if ((!mainWindow) && (!deleting)) + if (numWindows == 0) { - // if we closed this instance's main window, delete the instance + // if we closed the last window, delete the instance + // if the main window is closed, Qt will take care of closing any secondary windows deleteEmuInstance(instanceID); } } @@ -292,24 +293,18 @@ bool EmuInstance::usesOpenGL() (globalCfg.GetInt("3D.Renderer") != renderer3D_Software); } -void EmuInstance::initOpenGL() +void EmuInstance::initOpenGL(int win) { - for (int i = 0; i < kMaxWindows; i++) - { - if (windowList[i]) - windowList[i]->initOpenGL(); - } + if (windowList[win]) + windowList[win]->initOpenGL(); setVSyncGL(true); } -void EmuInstance::deinitOpenGL() +void EmuInstance::deinitOpenGL(int win) { - for (int i = 0; i < kMaxWindows; i++) - { - if (windowList[i]) - windowList[i]->deinitOpenGL(); - } + if (windowList[win]) + windowList[win]->deinitOpenGL(); } void EmuInstance::setVSyncGL(bool vsync) diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 742bebba..8022867a 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -103,8 +103,8 @@ public: void emuStop(melonDS::Platform::StopReason reason); bool usesOpenGL(); - void initOpenGL(); - void deinitOpenGL(); + void initOpenGL(int win); + void deinitOpenGL(int win); void setVSyncGL(bool vsync); void makeCurrentGL(); void drawScreenGL(); diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 589f11e2..da28365c 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -115,7 +115,7 @@ void EmuThread::run() if (emuInstance->usesOpenGL()) { - emuInstance->initOpenGL(); + emuInstance->initOpenGL(0); useOpenGL = true; videoRenderer = globalCfg.GetInt("3D.Renderer"); @@ -547,12 +547,12 @@ void EmuThread::handleMessages() break; case msg_InitGL: - emuInstance->initOpenGL(); + emuInstance->initOpenGL(msg.param.value()); useOpenGL = true; break; case msg_DeInitGL: - emuInstance->deinitOpenGL(); + emuInstance->deinitOpenGL(msg.param.value()); useOpenGL = false; break; @@ -650,15 +650,15 @@ void EmuThread::changeWindowTitle(char* title) emit windowTitleChange(QString(title)); } -void EmuThread::initContext() +void EmuThread::initContext(int win) { - sendMessage(msg_InitGL); + sendMessage({.type = msg_InitGL, .param = win}); waitMessage(); } -void EmuThread::deinitContext() +void EmuThread::deinitContext(int win) { - sendMessage(msg_DeInitGL); + sendMessage({.type = msg_DeInitGL, .param = win}); waitMessage(); } diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index c53740c8..57f389af 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -127,8 +127,8 @@ public: bool emuIsRunning(); bool emuIsActive(); - void initContext(); - void deinitContext(); + void initContext(int win); + void deinitContext(int win); void updateVideoSettings() { videoSettingsDirty = true; } void updateVideoRenderer() { videoSettingsDirty = true; lastVideoRenderer = -1; } diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index 3780de55..11a6150a 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -736,6 +736,8 @@ ScreenPanelGL::ScreenPanelGL(QWidget* parent) : ScreenPanel(parent) setAttribute(Qt::WA_KeyCompression, false); setFocusPolicy(Qt::StrongFocus); setMinimumSize(screenGetMinSize()); + + glInited = false; } ScreenPanelGL::~ScreenPanelGL() @@ -781,6 +783,7 @@ void ScreenPanelGL::setSwapInterval(int intv) void ScreenPanelGL::initOpenGL() { if (!glContext) return; + if (glInited) return; glContext->MakeCurrent(); @@ -877,11 +880,13 @@ void ScreenPanelGL::initOpenGL() glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0)); transferLayout(); + glInited = true; } void ScreenPanelGL::deinitOpenGL() { if (!glContext) return; + if (!glInited) return; glDeleteTextures(1, &screenTexture); @@ -906,6 +911,7 @@ void ScreenPanelGL::deinitOpenGL() glContext->DoneCurrent(); lastScreenWidth = lastScreenHeight = -1; + glInited = false; } void ScreenPanelGL::makeCurrentGL() diff --git a/src/frontend/qt_sdl/Screen.h b/src/frontend/qt_sdl/Screen.h index f3662d8d..c17626bd 100644 --- a/src/frontend/qt_sdl/Screen.h +++ b/src/frontend/qt_sdl/Screen.h @@ -197,6 +197,7 @@ private: void setupScreenLayout() override; std::unique_ptr glContext; + bool glInited; GLuint screenVertexBuffer, screenVertexArray; GLuint screenTexture; diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 7c6c0c4c..19c59673 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -588,6 +588,13 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : } } + menu->addSeparator(); + + actNewWindow = menu->addAction("Open new window"); + connect(actNewWindow, &QAction::triggered, this, &MainWindow::onOpenNewWindow); + + menu->addSeparator(); + actScreenFiltering = menu->addAction("Screen filtering"); actScreenFiltering->setCheckable(true); connect(actScreenFiltering, &QAction::triggered, this, &MainWindow::onChangeScreenFiltering); @@ -1950,6 +1957,11 @@ void MainWindow::onChangeIntegerScaling(bool checked) emit screenLayoutChange(); } +void MainWindow::onOpenNewWindow() +{ + emuInstance->createWindow(); +} + void MainWindow::onChangeScreenFiltering(bool checked) { windowCfg.SetBool("ScreenFilter", checked); @@ -2077,7 +2089,7 @@ void MainWindow::onUpdateVideoSettings(bool glchange) if (glchange) { emuThread->emuPause(); - if (hasOGL) emuThread->deinitContext(); + if (hasOGL) emuThread->deinitContext(windowID); delete panel; createScreenPanel(); @@ -2088,7 +2100,7 @@ void MainWindow::onUpdateVideoSettings(bool glchange) if (glchange) { - if (hasOGL) emuThread->initContext(); + if (hasOGL) emuThread->initContext(windowID); emuThread->emuUnpause(); } } diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index 98298c0b..b1391911 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -208,6 +208,7 @@ private slots: void onChangeScreenSizing(QAction* act); void onChangeScreenAspect(QAction* act); void onChangeIntegerScaling(bool checked); + void onOpenNewWindow(); void onChangeScreenFiltering(bool checked); void onChangeShowOSD(bool checked); void onChangeLimitFramerate(bool checked); @@ -325,6 +326,7 @@ public: QAction** actScreenAspectTop; QActionGroup* grpScreenAspectBot; QAction** actScreenAspectBot; + QAction* actNewWindow; QAction* actScreenFiltering; QAction* actShowOSD; QAction* actLimitFramerate; From b2ae4c7dc5490d0e8e2be589b781704487855e77 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 02:07:33 +0200 Subject: [PATCH 27/63] lay base for a window with no menubar --- src/frontend/qt_sdl/EmuThread.cpp | 16 +- src/frontend/qt_sdl/Window.cpp | 889 +++++++++++++++--------------- src/frontend/qt_sdl/Window.h | 8 +- src/frontend/qt_sdl/main.cpp | 2 +- 4 files changed, 478 insertions(+), 437 deletions(-) diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index da28365c..07e03e0f 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -76,11 +76,15 @@ void EmuThread::attachWindow(MainWindow* window) connect(this, SIGNAL(windowEmuStop()), window, SLOT(onEmuStop())); connect(this, SIGNAL(windowEmuPause(bool)), window, SLOT(onEmuPause(bool))); connect(this, SIGNAL(windowEmuReset()), window, SLOT(onEmuReset())); - connect(this, SIGNAL(windowLimitFPSChange()), window->actLimitFramerate, SLOT(trigger())); connect(this, SIGNAL(autoScreenSizingChange(int)), window->panel, SLOT(onAutoScreenSizingChanged(int))); connect(this, SIGNAL(windowFullscreenToggle()), window, SLOT(onFullscreenToggled())); - connect(this, SIGNAL(swapScreensToggle()), window->actScreenSwap, SLOT(trigger())); connect(this, SIGNAL(screenEmphasisToggle()), window, SLOT(onScreenEmphasisToggled())); + + if (window->winHasMenu()) + { + connect(this, SIGNAL(windowLimitFPSChange()), window->actLimitFramerate, SLOT(trigger())); + connect(this, SIGNAL(swapScreensToggle()), window->actScreenSwap, SLOT(trigger())); + } } void EmuThread::detachWindow(MainWindow* window) @@ -91,11 +95,15 @@ void EmuThread::detachWindow(MainWindow* window) disconnect(this, SIGNAL(windowEmuStop()), window, SLOT(onEmuStop())); disconnect(this, SIGNAL(windowEmuPause(bool)), window, SLOT(onEmuPause(bool))); disconnect(this, SIGNAL(windowEmuReset()), window, SLOT(onEmuReset())); - disconnect(this, SIGNAL(windowLimitFPSChange()), window->actLimitFramerate, SLOT(trigger())); disconnect(this, SIGNAL(autoScreenSizingChange(int)), window->panel, SLOT(onAutoScreenSizingChanged(int))); disconnect(this, SIGNAL(windowFullscreenToggle()), window, SLOT(onFullscreenToggled())); - disconnect(this, SIGNAL(swapScreensToggle()), window->actScreenSwap, SLOT(trigger())); disconnect(this, SIGNAL(screenEmphasisToggle()), window, SLOT(onScreenEmphasisToggled())); + + if (window->winHasMenu()) + { + disconnect(this, SIGNAL(windowLimitFPSChange()), window->actLimitFramerate, SLOT(trigger())); + disconnect(this, SIGNAL(swapScreensToggle()), window->actScreenSwap, SLOT(trigger())); + } } void EmuThread::run() diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 19c59673..4a5fa439 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -271,408 +271,416 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : setStyleSheet("QMenuBar::item { padding: 4px 8px; }"); #endif - QMenuBar* menubar = new QMenuBar(); + hasMenu = (!parent); + + if (hasMenu) { - QMenu* menu = menubar->addMenu("File"); - - actOpenROM = menu->addAction("Open ROM..."); - connect(actOpenROM, &QAction::triggered, this, &MainWindow::onOpenFile); - actOpenROM->setShortcut(QKeySequence(QKeySequence::StandardKey::Open)); - - /*actOpenROMArchive = menu->addAction("Open ROM inside archive..."); - connect(actOpenROMArchive, &QAction::triggered, this, &MainWindow::onOpenFileArchive); - actOpenROMArchive->setShortcut(QKeySequence(Qt::Key_O | Qt::CTRL | Qt::SHIFT));*/ - - recentMenu = menu->addMenu("Open recent"); - Config::Array recentROMs = globalCfg.GetArray("RecentROM"); - int numrecent = std::min(kMaxRecentROMs, (int)recentROMs.Size()); - for (int i = 0; i < numrecent; ++i) + QMenuBar * menubar = new QMenuBar(); { - std::string item = recentROMs.GetString(i); - if (!item.empty()) - recentFileList.push_back(QString::fromStdString(item)); - } - updateRecentFilesMenu(); + QMenu * menu = menubar->addMenu("File"); - //actBootFirmware = menu->addAction("Launch DS menu"); - actBootFirmware = menu->addAction("Boot firmware"); - connect(actBootFirmware, &QAction::triggered, this, &MainWindow::onBootFirmware); + actOpenROM = menu->addAction("Open ROM..."); + connect(actOpenROM, &QAction::triggered, this, &MainWindow::onOpenFile); + actOpenROM->setShortcut(QKeySequence(QKeySequence::StandardKey::Open)); - menu->addSeparator(); + /*actOpenROMArchive = menu->addAction("Open ROM inside archive..."); + connect(actOpenROMArchive, &QAction::triggered, this, &MainWindow::onOpenFileArchive); + actOpenROMArchive->setShortcut(QKeySequence(Qt::Key_O | Qt::CTRL | Qt::SHIFT));*/ - actCurrentCart = menu->addAction("DS slot: " + emuInstance->cartLabel()); - actCurrentCart->setEnabled(false); - - actInsertCart = menu->addAction("Insert cart..."); - connect(actInsertCart, &QAction::triggered, this, &MainWindow::onInsertCart); - - actEjectCart = menu->addAction("Eject cart"); - connect(actEjectCart, &QAction::triggered, this, &MainWindow::onEjectCart); - - menu->addSeparator(); - - actCurrentGBACart = menu->addAction("GBA slot: " + emuInstance->gbaCartLabel()); - actCurrentGBACart->setEnabled(false); - - actInsertGBACart = menu->addAction("Insert ROM cart..."); - connect(actInsertGBACart, &QAction::triggered, this, &MainWindow::onInsertGBACart); - - { - QMenu* submenu = menu->addMenu("Insert add-on cart"); - QAction* act; - - int addons[] = {GBAAddon_RAMExpansion, GBAAddon_RumblePak, -1}; - for (int i = 0; addons[i] != -1; i++) + recentMenu = menu->addMenu("Open recent"); + Config::Array recentROMs = globalCfg.GetArray("RecentROM"); + int numrecent = std::min(kMaxRecentROMs, (int) recentROMs.Size()); + for (int i = 0; i < numrecent; ++i) { - int addon = addons[i]; - act = submenu->addAction(emuInstance->gbaAddonName(addon)); - act->setData(QVariant(addon)); - connect(act, &QAction::triggered, this, &MainWindow::onInsertGBAAddon); - actInsertGBAAddon.append(act); + std::string item = recentROMs.GetString(i); + if (!item.empty()) + recentFileList.push_back(QString::fromStdString(item)); } - } + updateRecentFilesMenu(); - actEjectGBACart = menu->addAction("Eject cart"); - connect(actEjectGBACart, &QAction::triggered, this, &MainWindow::onEjectGBACart); - - menu->addSeparator(); - - actImportSavefile = menu->addAction("Import savefile"); - connect(actImportSavefile, &QAction::triggered, this, &MainWindow::onImportSavefile); - - menu->addSeparator(); - - { - QMenu* submenu = menu->addMenu("Save state"); - - for (int i = 1; i < 9; i++) - { - actSaveState[i] = submenu->addAction(QString("%1").arg(i)); - actSaveState[i]->setShortcut(QKeySequence(Qt::ShiftModifier | (Qt::Key_F1+i-1))); - actSaveState[i]->setData(QVariant(i)); - connect(actSaveState[i], &QAction::triggered, this, &MainWindow::onSaveState); - } - - actSaveState[0] = submenu->addAction("File..."); - actSaveState[0]->setShortcut(QKeySequence(Qt::ShiftModifier | Qt::Key_F9)); - actSaveState[0]->setData(QVariant(0)); - connect(actSaveState[0], &QAction::triggered, this, &MainWindow::onSaveState); - } - { - QMenu* submenu = menu->addMenu("Load state"); - - for (int i = 1; i < 9; i++) - { - actLoadState[i] = submenu->addAction(QString("%1").arg(i)); - actLoadState[i]->setShortcut(QKeySequence(Qt::Key_F1+i-1)); - actLoadState[i]->setData(QVariant(i)); - connect(actLoadState[i], &QAction::triggered, this, &MainWindow::onLoadState); - } - - actLoadState[0] = submenu->addAction("File..."); - actLoadState[0]->setShortcut(QKeySequence(Qt::Key_F9)); - actLoadState[0]->setData(QVariant(0)); - connect(actLoadState[0], &QAction::triggered, this, &MainWindow::onLoadState); - } - - actUndoStateLoad = menu->addAction("Undo state load"); - actUndoStateLoad->setShortcut(QKeySequence(Qt::Key_F12)); - connect(actUndoStateLoad, &QAction::triggered, this, &MainWindow::onUndoStateLoad); - - menu->addSeparator(); - actOpenConfig = menu->addAction("Open melonDS directory"); - connect(actOpenConfig, &QAction::triggered, this, [&]() { - QDesktopServices::openUrl(QUrl::fromLocalFile(emuDirectory)); - }); - - menu->addSeparator(); - - actQuit = menu->addAction("Quit"); - connect(actQuit, &QAction::triggered, this, &MainWindow::onQuit); - actQuit->setShortcut(QKeySequence(QKeySequence::StandardKey::Quit)); - } - { - QMenu* menu = menubar->addMenu("System"); - - actPause = menu->addAction("Pause"); - actPause->setCheckable(true); - connect(actPause, &QAction::triggered, this, &MainWindow::onPause); - - actReset = menu->addAction("Reset"); - connect(actReset, &QAction::triggered, this, &MainWindow::onReset); - - actStop = menu->addAction("Stop"); - connect(actStop, &QAction::triggered, this, &MainWindow::onStop); - - actFrameStep = menu->addAction("Frame step"); - connect(actFrameStep, &QAction::triggered, this, &MainWindow::onFrameStep); - - menu->addSeparator(); - - actPowerManagement = menu->addAction("Power management"); - connect(actPowerManagement, &QAction::triggered, this, &MainWindow::onOpenPowerManagement); - - actDateTime = menu->addAction("Date and time"); - connect(actDateTime, &QAction::triggered, this, &MainWindow::onOpenDateTime); - - menu->addSeparator(); - - actEnableCheats = menu->addAction("Enable cheats"); - actEnableCheats->setCheckable(true); - connect(actEnableCheats, &QAction::triggered, this, &MainWindow::onEnableCheats); - - //if (inst == 0) - { - actSetupCheats = menu->addAction("Setup cheat codes"); - actSetupCheats->setMenuRole(QAction::NoRole); - connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats); + //actBootFirmware = menu->addAction("Launch DS menu"); + actBootFirmware = menu->addAction("Boot firmware"); + connect(actBootFirmware, &QAction::triggered, this, &MainWindow::onBootFirmware); menu->addSeparator(); - actROMInfo = menu->addAction("ROM info"); - connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo); - actRAMInfo = menu->addAction("RAM search"); - connect(actRAMInfo, &QAction::triggered, this, &MainWindow::onRAMInfo); + actCurrentCart = menu->addAction("DS slot: " + emuInstance->cartLabel()); + actCurrentCart->setEnabled(false); - actTitleManager = menu->addAction("Manage DSi titles"); - connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager); - } + actInsertCart = menu->addAction("Insert cart..."); + connect(actInsertCart, &QAction::triggered, this, &MainWindow::onInsertCart); + + actEjectCart = menu->addAction("Eject cart"); + connect(actEjectCart, &QAction::triggered, this, &MainWindow::onEjectCart); - { menu->addSeparator(); - QMenu* submenu = menu->addMenu("Multiplayer"); - actMPNewInstance = submenu->addAction("Launch new instance"); - connect(actMPNewInstance, &QAction::triggered, this, &MainWindow::onMPNewInstance); + actCurrentGBACart = menu->addAction("GBA slot: " + emuInstance->gbaCartLabel()); + actCurrentGBACart->setEnabled(false); - submenu->addSeparator(); + actInsertGBACart = menu->addAction("Insert ROM cart..."); + connect(actInsertGBACart, &QAction::triggered, this, &MainWindow::onInsertGBACart); - actLANStartHost = submenu->addAction("Host LAN game"); - connect(actLANStartHost, &QAction::triggered, this, &MainWindow::onLANStartHost); - - actLANStartClient = submenu->addAction("Join LAN game"); - connect(actLANStartClient, &QAction::triggered, this, &MainWindow::onLANStartClient); - - /*submenu->addSeparator(); - - actNPStartHost = submenu->addAction("NETPLAY HOST"); - connect(actNPStartHost, &QAction::triggered, this, &MainWindow::onNPStartHost); - - actNPStartClient = submenu->addAction("NETPLAY CLIENT"); - connect(actNPStartClient, &QAction::triggered, this, &MainWindow::onNPStartClient); - - actNPTest = submenu->addAction("NETPLAY GO"); - connect(actNPTest, &QAction::triggered, this, &MainWindow::onNPTest);*/ - } - } - { - QMenu *menu = menubar->addMenu("View"); - - { - QMenu* submenu = menu->addMenu("Screen size"); - - for (int i = 0; i < 4; i++) { - int data = i+1; - actScreenSize[i] = submenu->addAction(QString("%1x").arg(data)); - actScreenSize[i]->setData(QVariant(data)); - connect(actScreenSize[i], &QAction::triggered, this, &MainWindow::onChangeScreenSize); - } - } - { - QMenu* submenu = menu->addMenu("Screen rotation"); - grpScreenRotation = new QActionGroup(submenu); + QMenu * submenu = menu->addMenu("Insert add-on cart"); + QAction *act; - for (int i = 0; i < screenRot_MAX; i++) - { - int data = i*90; - actScreenRotation[i] = submenu->addAction(QString("%1°").arg(data)); - actScreenRotation[i]->setActionGroup(grpScreenRotation); - actScreenRotation[i]->setData(QVariant(i)); - actScreenRotation[i]->setCheckable(true); - } - - connect(grpScreenRotation, &QActionGroup::triggered, this, &MainWindow::onChangeScreenRotation); - } - { - QMenu* submenu = menu->addMenu("Screen gap"); - grpScreenGap = new QActionGroup(submenu); - - const int screengap[] = {0, 1, 8, 64, 90, 128}; - - for (int i = 0; i < 6; i++) - { - int data = screengap[i]; - actScreenGap[i] = submenu->addAction(QString("%1 px").arg(data)); - actScreenGap[i]->setActionGroup(grpScreenGap); - actScreenGap[i]->setData(QVariant(data)); - actScreenGap[i]->setCheckable(true); - } - - connect(grpScreenGap, &QActionGroup::triggered, this, &MainWindow::onChangeScreenGap); - } - { - QMenu* submenu = menu->addMenu("Screen layout"); - grpScreenLayout = new QActionGroup(submenu); - - const char* screenlayout[] = {"Natural", "Vertical", "Horizontal", "Hybrid"}; - - for (int i = 0; i < screenLayout_MAX; i++) - { - actScreenLayout[i] = submenu->addAction(QString(screenlayout[i])); - actScreenLayout[i]->setActionGroup(grpScreenLayout); - actScreenLayout[i]->setData(QVariant(i)); - actScreenLayout[i]->setCheckable(true); - } - - connect(grpScreenLayout, &QActionGroup::triggered, this, &MainWindow::onChangeScreenLayout); - - submenu->addSeparator(); - - actScreenSwap = submenu->addAction("Swap screens"); - actScreenSwap->setCheckable(true); - connect(actScreenSwap, &QAction::triggered, this, &MainWindow::onChangeScreenSwap); - } - { - QMenu* submenu = menu->addMenu("Screen sizing"); - grpScreenSizing = new QActionGroup(submenu); - - const char* screensizing[] = {"Even", "Emphasize top", "Emphasize bottom", "Auto", "Top only", "Bottom only"}; - - for (int i = 0; i < screenSizing_MAX; i++) - { - actScreenSizing[i] = submenu->addAction(QString(screensizing[i])); - actScreenSizing[i]->setActionGroup(grpScreenSizing); - actScreenSizing[i]->setData(QVariant(i)); - actScreenSizing[i]->setCheckable(true); - } - - connect(grpScreenSizing, &QActionGroup::triggered, this, &MainWindow::onChangeScreenSizing); - - submenu->addSeparator(); - - actIntegerScaling = submenu->addAction("Force integer scaling"); - actIntegerScaling->setCheckable(true); - connect(actIntegerScaling, &QAction::triggered, this, &MainWindow::onChangeIntegerScaling); - } - { - QMenu* submenu = menu->addMenu("Aspect ratio"); - grpScreenAspectTop = new QActionGroup(submenu); - grpScreenAspectBot = new QActionGroup(submenu); - actScreenAspectTop = new QAction*[AspectRatiosNum]; - actScreenAspectBot = new QAction*[AspectRatiosNum]; - - for (int i = 0; i < 2; i++) - { - QActionGroup* group = grpScreenAspectTop; - QAction** actions = actScreenAspectTop; - - if (i == 1) + int addons[] = {GBAAddon_RAMExpansion, GBAAddon_RumblePak, -1}; + for (int i = 0; addons[i] != -1; i++) { - group = grpScreenAspectBot; - submenu->addSeparator(); - actions = actScreenAspectBot; + int addon = addons[i]; + act = submenu->addAction(emuInstance->gbaAddonName(addon)); + act->setData(QVariant(addon)); + connect(act, &QAction::triggered, this, &MainWindow::onInsertGBAAddon); + actInsertGBAAddon.append(act); + } + } + + actEjectGBACart = menu->addAction("Eject cart"); + connect(actEjectGBACart, &QAction::triggered, this, &MainWindow::onEjectGBACart); + + menu->addSeparator(); + + actImportSavefile = menu->addAction("Import savefile"); + connect(actImportSavefile, &QAction::triggered, this, &MainWindow::onImportSavefile); + + menu->addSeparator(); + + { + QMenu * submenu = menu->addMenu("Save state"); + + for (int i = 1; i < 9; i++) + { + actSaveState[i] = submenu->addAction(QString("%1").arg(i)); + actSaveState[i]->setShortcut(QKeySequence(Qt::ShiftModifier | (Qt::Key_F1 + i - 1))); + actSaveState[i]->setData(QVariant(i)); + connect(actSaveState[i], &QAction::triggered, this, &MainWindow::onSaveState); } - for (int j = 0; j < AspectRatiosNum; j++) + actSaveState[0] = submenu->addAction("File..."); + actSaveState[0]->setShortcut(QKeySequence(Qt::ShiftModifier | Qt::Key_F9)); + actSaveState[0]->setData(QVariant(0)); + connect(actSaveState[0], &QAction::triggered, this, &MainWindow::onSaveState); + } + { + QMenu * submenu = menu->addMenu("Load state"); + + for (int i = 1; i < 9; i++) { - auto ratio = aspectRatios[j]; - QString label = QString("%1 %2").arg(i ? "Bottom" : "Top", ratio.label); - actions[j] = submenu->addAction(label); - actions[j]->setActionGroup(group); - actions[j]->setData(QVariant(ratio.id)); - actions[j]->setCheckable(true); + actLoadState[i] = submenu->addAction(QString("%1").arg(i)); + actLoadState[i]->setShortcut(QKeySequence(Qt::Key_F1 + i - 1)); + actLoadState[i]->setData(QVariant(i)); + connect(actLoadState[i], &QAction::triggered, this, &MainWindow::onLoadState); } - connect(group, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspect); + actLoadState[0] = submenu->addAction("File..."); + actLoadState[0]->setShortcut(QKeySequence(Qt::Key_F9)); + actLoadState[0]->setData(QVariant(0)); + connect(actLoadState[0], &QAction::triggered, this, &MainWindow::onLoadState); + } + + actUndoStateLoad = menu->addAction("Undo state load"); + actUndoStateLoad->setShortcut(QKeySequence(Qt::Key_F12)); + connect(actUndoStateLoad, &QAction::triggered, this, &MainWindow::onUndoStateLoad); + + menu->addSeparator(); + actOpenConfig = menu->addAction("Open melonDS directory"); + connect(actOpenConfig, &QAction::triggered, this, [&]() + { + QDesktopServices::openUrl(QUrl::fromLocalFile(emuDirectory)); + }); + + menu->addSeparator(); + + actQuit = menu->addAction("Quit"); + connect(actQuit, &QAction::triggered, this, &MainWindow::onQuit); + actQuit->setShortcut(QKeySequence(QKeySequence::StandardKey::Quit)); + } + { + QMenu * menu = menubar->addMenu("System"); + + actPause = menu->addAction("Pause"); + actPause->setCheckable(true); + connect(actPause, &QAction::triggered, this, &MainWindow::onPause); + + actReset = menu->addAction("Reset"); + connect(actReset, &QAction::triggered, this, &MainWindow::onReset); + + actStop = menu->addAction("Stop"); + connect(actStop, &QAction::triggered, this, &MainWindow::onStop); + + actFrameStep = menu->addAction("Frame step"); + connect(actFrameStep, &QAction::triggered, this, &MainWindow::onFrameStep); + + menu->addSeparator(); + + actPowerManagement = menu->addAction("Power management"); + connect(actPowerManagement, &QAction::triggered, this, &MainWindow::onOpenPowerManagement); + + actDateTime = menu->addAction("Date and time"); + connect(actDateTime, &QAction::triggered, this, &MainWindow::onOpenDateTime); + + menu->addSeparator(); + + actEnableCheats = menu->addAction("Enable cheats"); + actEnableCheats->setCheckable(true); + connect(actEnableCheats, &QAction::triggered, this, &MainWindow::onEnableCheats); + + //if (inst == 0) + { + actSetupCheats = menu->addAction("Setup cheat codes"); + actSetupCheats->setMenuRole(QAction::NoRole); + connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats); + + menu->addSeparator(); + actROMInfo = menu->addAction("ROM info"); + connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo); + + actRAMInfo = menu->addAction("RAM search"); + connect(actRAMInfo, &QAction::triggered, this, &MainWindow::onRAMInfo); + + actTitleManager = menu->addAction("Manage DSi titles"); + connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager); + } + + { + menu->addSeparator(); + QMenu * submenu = menu->addMenu("Multiplayer"); + + actMPNewInstance = submenu->addAction("Launch new instance"); + connect(actMPNewInstance, &QAction::triggered, this, &MainWindow::onMPNewInstance); + + submenu->addSeparator(); + + actLANStartHost = submenu->addAction("Host LAN game"); + connect(actLANStartHost, &QAction::triggered, this, &MainWindow::onLANStartHost); + + actLANStartClient = submenu->addAction("Join LAN game"); + connect(actLANStartClient, &QAction::triggered, this, &MainWindow::onLANStartClient); + + /*submenu->addSeparator(); + + actNPStartHost = submenu->addAction("NETPLAY HOST"); + connect(actNPStartHost, &QAction::triggered, this, &MainWindow::onNPStartHost); + + actNPStartClient = submenu->addAction("NETPLAY CLIENT"); + connect(actNPStartClient, &QAction::triggered, this, &MainWindow::onNPStartClient); + + actNPTest = submenu->addAction("NETPLAY GO"); + connect(actNPTest, &QAction::triggered, this, &MainWindow::onNPTest);*/ } } + { + QMenu * menu = menubar->addMenu("View"); - menu->addSeparator(); + { + QMenu * submenu = menu->addMenu("Screen size"); - actNewWindow = menu->addAction("Open new window"); - connect(actNewWindow, &QAction::triggered, this, &MainWindow::onOpenNewWindow); + for (int i = 0; i < 4; i++) + { + int data = i + 1; + actScreenSize[i] = submenu->addAction(QString("%1x").arg(data)); + actScreenSize[i]->setData(QVariant(data)); + connect(actScreenSize[i], &QAction::triggered, this, &MainWindow::onChangeScreenSize); + } + } + { + QMenu * submenu = menu->addMenu("Screen rotation"); + grpScreenRotation = new QActionGroup(submenu); - menu->addSeparator(); + for (int i = 0; i < screenRot_MAX; i++) + { + int data = i * 90; + actScreenRotation[i] = submenu->addAction(QString("%1°").arg(data)); + actScreenRotation[i]->setActionGroup(grpScreenRotation); + actScreenRotation[i]->setData(QVariant(i)); + actScreenRotation[i]->setCheckable(true); + } - actScreenFiltering = menu->addAction("Screen filtering"); - actScreenFiltering->setCheckable(true); - connect(actScreenFiltering, &QAction::triggered, this, &MainWindow::onChangeScreenFiltering); + connect(grpScreenRotation, &QActionGroup::triggered, this, &MainWindow::onChangeScreenRotation); + } + { + QMenu * submenu = menu->addMenu("Screen gap"); + grpScreenGap = new QActionGroup(submenu); - actShowOSD = menu->addAction("Show OSD"); - actShowOSD->setCheckable(true); - connect(actShowOSD, &QAction::triggered, this, &MainWindow::onChangeShowOSD); - } - { - QMenu* menu = menubar->addMenu("Config"); + const int screengap[] = {0, 1, 8, 64, 90, 128}; - actEmuSettings = menu->addAction("Emu settings"); - connect(actEmuSettings, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); + for (int i = 0; i < 6; i++) + { + int data = screengap[i]; + actScreenGap[i] = submenu->addAction(QString("%1 px").arg(data)); + actScreenGap[i]->setActionGroup(grpScreenGap); + actScreenGap[i]->setData(QVariant(data)); + actScreenGap[i]->setCheckable(true); + } + + connect(grpScreenGap, &QActionGroup::triggered, this, &MainWindow::onChangeScreenGap); + } + { + QMenu * submenu = menu->addMenu("Screen layout"); + grpScreenLayout = new QActionGroup(submenu); + + const char *screenlayout[] = {"Natural", "Vertical", "Horizontal", "Hybrid"}; + + for (int i = 0; i < screenLayout_MAX; i++) + { + actScreenLayout[i] = submenu->addAction(QString(screenlayout[i])); + actScreenLayout[i]->setActionGroup(grpScreenLayout); + actScreenLayout[i]->setData(QVariant(i)); + actScreenLayout[i]->setCheckable(true); + } + + connect(grpScreenLayout, &QActionGroup::triggered, this, &MainWindow::onChangeScreenLayout); + + submenu->addSeparator(); + + actScreenSwap = submenu->addAction("Swap screens"); + actScreenSwap->setCheckable(true); + connect(actScreenSwap, &QAction::triggered, this, &MainWindow::onChangeScreenSwap); + } + { + QMenu * submenu = menu->addMenu("Screen sizing"); + grpScreenSizing = new QActionGroup(submenu); + + const char *screensizing[] = {"Even", "Emphasize top", "Emphasize bottom", "Auto", "Top only", + "Bottom only"}; + + for (int i = 0; i < screenSizing_MAX; i++) + { + actScreenSizing[i] = submenu->addAction(QString(screensizing[i])); + actScreenSizing[i]->setActionGroup(grpScreenSizing); + actScreenSizing[i]->setData(QVariant(i)); + actScreenSizing[i]->setCheckable(true); + } + + connect(grpScreenSizing, &QActionGroup::triggered, this, &MainWindow::onChangeScreenSizing); + + submenu->addSeparator(); + + actIntegerScaling = submenu->addAction("Force integer scaling"); + actIntegerScaling->setCheckable(true); + connect(actIntegerScaling, &QAction::triggered, this, &MainWindow::onChangeIntegerScaling); + } + { + QMenu * submenu = menu->addMenu("Aspect ratio"); + grpScreenAspectTop = new QActionGroup(submenu); + grpScreenAspectBot = new QActionGroup(submenu); + actScreenAspectTop = new QAction *[AspectRatiosNum]; + actScreenAspectBot = new QAction *[AspectRatiosNum]; + + for (int i = 0; i < 2; i++) + { + QActionGroup * group = grpScreenAspectTop; + QAction **actions = actScreenAspectTop; + + if (i == 1) + { + group = grpScreenAspectBot; + submenu->addSeparator(); + actions = actScreenAspectBot; + } + + for (int j = 0; j < AspectRatiosNum; j++) + { + auto ratio = aspectRatios[j]; + QString label = QString("%1 %2").arg(i ? "Bottom" : "Top", ratio.label); + actions[j] = submenu->addAction(label); + actions[j]->setActionGroup(group); + actions[j]->setData(QVariant(ratio.id)); + actions[j]->setCheckable(true); + } + + connect(group, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspect); + } + } + + menu->addSeparator(); + + actNewWindow = menu->addAction("Open new window"); + connect(actNewWindow, &QAction::triggered, this, &MainWindow::onOpenNewWindow); + + menu->addSeparator(); + + actScreenFiltering = menu->addAction("Screen filtering"); + actScreenFiltering->setCheckable(true); + connect(actScreenFiltering, &QAction::triggered, this, &MainWindow::onChangeScreenFiltering); + + actShowOSD = menu->addAction("Show OSD"); + actShowOSD->setCheckable(true); + connect(actShowOSD, &QAction::triggered, this, &MainWindow::onChangeShowOSD); + } + { + QMenu * menu = menubar->addMenu("Config"); + + actEmuSettings = menu->addAction("Emu settings"); + connect(actEmuSettings, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); #ifdef __APPLE__ - actPreferences = menu->addAction("Preferences..."); - connect(actPreferences, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); - actPreferences->setMenuRole(QAction::PreferencesRole); + actPreferences = menu->addAction("Preferences..."); + connect(actPreferences, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); + actPreferences->setMenuRole(QAction::PreferencesRole); #endif - actInputConfig = menu->addAction("Input and hotkeys"); - connect(actInputConfig, &QAction::triggered, this, &MainWindow::onOpenInputConfig); + actInputConfig = menu->addAction("Input and hotkeys"); + connect(actInputConfig, &QAction::triggered, this, &MainWindow::onOpenInputConfig); - actVideoSettings = menu->addAction("Video settings"); - connect(actVideoSettings, &QAction::triggered, this, &MainWindow::onOpenVideoSettings); + actVideoSettings = menu->addAction("Video settings"); + connect(actVideoSettings, &QAction::triggered, this, &MainWindow::onOpenVideoSettings); - actCameraSettings = menu->addAction("Camera settings"); - connect(actCameraSettings, &QAction::triggered, this, &MainWindow::onOpenCameraSettings); + actCameraSettings = menu->addAction("Camera settings"); + connect(actCameraSettings, &QAction::triggered, this, &MainWindow::onOpenCameraSettings); - actAudioSettings = menu->addAction("Audio settings"); - connect(actAudioSettings, &QAction::triggered, this, &MainWindow::onOpenAudioSettings); + actAudioSettings = menu->addAction("Audio settings"); + connect(actAudioSettings, &QAction::triggered, this, &MainWindow::onOpenAudioSettings); - actMPSettings = menu->addAction("Multiplayer settings"); - connect(actMPSettings, &QAction::triggered, this, &MainWindow::onOpenMPSettings); + actMPSettings = menu->addAction("Multiplayer settings"); + connect(actMPSettings, &QAction::triggered, this, &MainWindow::onOpenMPSettings); - actWifiSettings = menu->addAction("Wifi settings"); - connect(actWifiSettings, &QAction::triggered, this, &MainWindow::onOpenWifiSettings); + actWifiSettings = menu->addAction("Wifi settings"); + connect(actWifiSettings, &QAction::triggered, this, &MainWindow::onOpenWifiSettings); - actFirmwareSettings = menu->addAction("Firmware settings"); - connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings); + actFirmwareSettings = menu->addAction("Firmware settings"); + connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings); - actInterfaceSettings = menu->addAction("Interface settings"); - connect(actInterfaceSettings, &QAction::triggered, this, &MainWindow::onOpenInterfaceSettings); + actInterfaceSettings = menu->addAction("Interface settings"); + connect(actInterfaceSettings, &QAction::triggered, this, &MainWindow::onOpenInterfaceSettings); - actPathSettings = menu->addAction("Path settings"); - connect(actPathSettings, &QAction::triggered, this, &MainWindow::onOpenPathSettings); + actPathSettings = menu->addAction("Path settings"); + connect(actPathSettings, &QAction::triggered, this, &MainWindow::onOpenPathSettings); + { + QMenu * submenu = menu->addMenu("Savestate settings"); + + actSavestateSRAMReloc = submenu->addAction("Separate savefiles"); + actSavestateSRAMReloc->setCheckable(true); + connect(actSavestateSRAMReloc, &QAction::triggered, this, &MainWindow::onChangeSavestateSRAMReloc); + } + + menu->addSeparator(); + + actLimitFramerate = menu->addAction("Limit framerate"); + actLimitFramerate->setCheckable(true); + connect(actLimitFramerate, &QAction::triggered, this, &MainWindow::onChangeLimitFramerate); + + actAudioSync = menu->addAction("Audio sync"); + actAudioSync->setCheckable(true); + connect(actAudioSync, &QAction::triggered, this, &MainWindow::onChangeAudioSync); + } { - QMenu* submenu = menu->addMenu("Savestate settings"); - - actSavestateSRAMReloc = submenu->addAction("Separate savefiles"); - actSavestateSRAMReloc->setCheckable(true); - connect(actSavestateSRAMReloc, &QAction::triggered, this, &MainWindow::onChangeSavestateSRAMReloc); + QMenu * menu = menubar->addMenu("Help"); + actAbout = menu->addAction("About..."); + connect(actAbout, &QAction::triggered, this, [&] + { + auto dialog = AboutDialog(this); + dialog.exec(); + }); } - menu->addSeparator(); + setMenuBar(menubar); - actLimitFramerate = menu->addAction("Limit framerate"); - actLimitFramerate->setCheckable(true); - connect(actLimitFramerate, &QAction::triggered, this, &MainWindow::onChangeLimitFramerate); - - actAudioSync = menu->addAction("Audio sync"); - actAudioSync->setCheckable(true); - connect(actAudioSync, &QAction::triggered, this, &MainWindow::onChangeAudioSync); + if (localCfg.GetString("Firmware.Username") == "Arisotura") + actMPNewInstance->setText("Fart"); } - { - QMenu* menu = menubar->addMenu("Help"); - actAbout = menu->addAction("About..."); - connect(actAbout, &QAction::triggered, this, [&]{ - auto dialog = AboutDialog(this); - dialog.exec(); - }); - } - - setMenuBar(menubar); - - if (localCfg.GetString("Firmware.Username") == "Arisotura") - actMPNewInstance->setText("Fart"); #ifdef Q_OS_MAC QPoint screenCenter = screen()->availableGeometry().center(); @@ -695,87 +703,90 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : createScreenPanel(); - actEjectCart->setEnabled(false); - actEjectGBACart->setEnabled(false); - - if (globalCfg.GetInt("Emu.ConsoleType") == 1) + if (hasMenu) { - actInsertGBACart->setEnabled(false); - for (auto act : actInsertGBAAddon) - act->setEnabled(false); - } + actEjectCart->setEnabled(false); + actEjectGBACart->setEnabled(false); - for (int i = 0; i < 9; i++) - { - actSaveState[i]->setEnabled(false); - actLoadState[i]->setEnabled(false); - } - actUndoStateLoad->setEnabled(false); - actImportSavefile->setEnabled(false); - - actPause->setEnabled(false); - actReset->setEnabled(false); - actStop->setEnabled(false); - actFrameStep->setEnabled(false); - - actDateTime->setEnabled(true); - actPowerManagement->setEnabled(false); - - actSetupCheats->setEnabled(false); - actTitleManager->setEnabled(!globalCfg.GetString("DSi.NANDPath").empty()); - - actEnableCheats->setChecked(localCfg.GetBool("EnableCheats")); - - actROMInfo->setEnabled(false); - actRAMInfo->setEnabled(false); - - actSavestateSRAMReloc->setChecked(globalCfg.GetBool("Savestate.RelocSRAM")); - - actScreenRotation[windowCfg.GetInt("ScreenRotation")]->setChecked(true); - - int screenGap = windowCfg.GetInt("ScreenGap"); - for (int i = 0; i < 6; i++) - { - if (actScreenGap[i]->data().toInt() == screenGap) + if (globalCfg.GetInt("Emu.ConsoleType") == 1) { - actScreenGap[i]->setChecked(true); - break; + actInsertGBACart->setEnabled(false); + for (auto act: actInsertGBAAddon) + act->setEnabled(false); } - } - actScreenLayout[windowCfg.GetInt("ScreenLayout")]->setChecked(true); - actScreenSizing[windowCfg.GetInt("ScreenSizing")]->setChecked(true); - actIntegerScaling->setChecked(windowCfg.GetBool("IntegerScaling")); + for (int i = 0; i < 9; i++) + { + actSaveState[i]->setEnabled(false); + actLoadState[i]->setEnabled(false); + } + actUndoStateLoad->setEnabled(false); + actImportSavefile->setEnabled(false); - actScreenSwap->setChecked(windowCfg.GetBool("ScreenSwap")); + actPause->setEnabled(false); + actReset->setEnabled(false); + actStop->setEnabled(false); + actFrameStep->setEnabled(false); - int aspectTop = windowCfg.GetInt("ScreenAspectTop"); - int aspectBot = windowCfg.GetInt("ScreenAspectBot"); - for (int i = 0; i < AspectRatiosNum; i++) - { - if (aspectTop == aspectRatios[i].id) - actScreenAspectTop[i]->setChecked(true); - if (aspectBot == aspectRatios[i].id) - actScreenAspectBot[i]->setChecked(true); - } + actDateTime->setEnabled(true); + actPowerManagement->setEnabled(false); - actScreenFiltering->setChecked(windowCfg.GetBool("ScreenFilter")); - actShowOSD->setChecked(showOSD); + actSetupCheats->setEnabled(false); + actTitleManager->setEnabled(!globalCfg.GetString("DSi.NANDPath").empty()); - actLimitFramerate->setChecked(emuInstance->doLimitFPS); - actAudioSync->setChecked(emuInstance->doAudioSync); + actEnableCheats->setChecked(localCfg.GetBool("EnableCheats")); - if (emuInstance->instanceID > 0) - { - actEmuSettings->setEnabled(false); - actVideoSettings->setEnabled(false); - actMPSettings->setEnabled(false); - actWifiSettings->setEnabled(false); - actInterfaceSettings->setEnabled(false); + actROMInfo->setEnabled(false); + actRAMInfo->setEnabled(false); + + actSavestateSRAMReloc->setChecked(globalCfg.GetBool("Savestate.RelocSRAM")); + + actScreenRotation[windowCfg.GetInt("ScreenRotation")]->setChecked(true); + + int screenGap = windowCfg.GetInt("ScreenGap"); + for (int i = 0; i < 6; i++) + { + if (actScreenGap[i]->data().toInt() == screenGap) + { + actScreenGap[i]->setChecked(true); + break; + } + } + + actScreenLayout[windowCfg.GetInt("ScreenLayout")]->setChecked(true); + actScreenSizing[windowCfg.GetInt("ScreenSizing")]->setChecked(true); + actIntegerScaling->setChecked(windowCfg.GetBool("IntegerScaling")); + + actScreenSwap->setChecked(windowCfg.GetBool("ScreenSwap")); + + int aspectTop = windowCfg.GetInt("ScreenAspectTop"); + int aspectBot = windowCfg.GetInt("ScreenAspectBot"); + for (int i = 0; i < AspectRatiosNum; i++) + { + if (aspectTop == aspectRatios[i].id) + actScreenAspectTop[i]->setChecked(true); + if (aspectBot == aspectRatios[i].id) + actScreenAspectBot[i]->setChecked(true); + } + + actScreenFiltering->setChecked(windowCfg.GetBool("ScreenFilter")); + actShowOSD->setChecked(showOSD); + + actLimitFramerate->setChecked(emuInstance->doLimitFPS); + actAudioSync->setChecked(emuInstance->doAudioSync); + + if (emuInstance->instanceID > 0) + { + actEmuSettings->setEnabled(false); + actVideoSettings->setEnabled(false); + actMPSettings->setEnabled(false); + actWifiSettings->setEnabled(false); + actInterfaceSettings->setEnabled(false); #ifdef __APPLE__ - actPreferences->setEnabled(false); + actPreferences->setEnabled(false); #endif // __APPLE__ + } } QObject::connect(qApp, &QApplication::applicationStateChanged, this, &MainWindow::onAppStateChanged); @@ -786,8 +797,11 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : MainWindow::~MainWindow() { - delete[] actScreenAspectTop; - delete[] actScreenAspectBot; + if (hasMenu) + { + delete[] actScreenAspectTop; + delete[] actScreenAspectBot; + } } void MainWindow::osdAddMessage(unsigned int color, const char* msg) @@ -834,7 +848,8 @@ void MainWindow::createScreenPanel() } setCentralWidget(panel); - actScreenFiltering->setEnabled(hasOGL); + if (hasMenu) + actScreenFiltering->setEnabled(hasOGL); panel->osdSetEnabled(showOSD); connect(this, SIGNAL(screenLayoutChange()), panel, SLOT(onScreenLayoutChanged())); @@ -1635,6 +1650,8 @@ void MainWindow::onNPTest() void MainWindow::updateMPInterface(MPInterfaceType type) { + if (!hasMenu) return; + // MP interface was changed, reflect it in the UI bool enable = (type == MPInterface_Local); @@ -1995,24 +2012,28 @@ void MainWindow::onTitleUpdate(QString title) setWindowTitle(title); } -void ToggleFullscreen(MainWindow* mainWindow) +void MainWindow::toggleFullscreen() { - if (!mainWindow->isFullScreen()) + if (!isFullScreen()) { - mainWindow->showFullScreen(); - mainWindow->menuBar()->setFixedHeight(0); // Don't use hide() as menubar actions stop working + showFullScreen(); + if (hasMenu) + menuBar()->setFixedHeight(0); // Don't use hide() as menubar actions stop working } else { - mainWindow->showNormal(); - int menuBarHeight = mainWindow->menuBar()->sizeHint().height(); - mainWindow->menuBar()->setFixedHeight(menuBarHeight); + showNormal(); + if (hasMenu) + { + int menuBarHeight = menuBar()->sizeHint().height(); + menuBar()->setFixedHeight(menuBarHeight); + } } } void MainWindow::onFullscreenToggled() { - ToggleFullscreen(this); + toggleFullscreen(); } void MainWindow::onScreenEmphasisToggled() @@ -2033,6 +2054,8 @@ void MainWindow::onScreenEmphasisToggled() void MainWindow::onEmuStart() { + if (!hasMenu) return; + for (int i = 1; i < 9; i++) { actSaveState[i]->setEnabled(true); @@ -2056,6 +2079,8 @@ void MainWindow::onEmuStart() void MainWindow::onEmuStop() { + if (!hasMenu) return; + for (int i = 0; i < 9; i++) { actSaveState[i]->setEnabled(false); @@ -2076,11 +2101,15 @@ void MainWindow::onEmuStop() void MainWindow::onEmuPause(bool pause) { + if (!hasMenu) return; + actPause->setChecked(pause); } void MainWindow::onEmuReset() { + if (!hasMenu) return; + actUndoStateLoad->setEnabled(false); } diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index b1391911..32ba5dd2 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -111,6 +111,10 @@ public: EmuInstance* getEmuInstance() { return emuInstance; } Config::Table& getWindowConfig() { return windowCfg; } + bool winHasMenu() { return hasMenu; } + + void toggleFullscreen(); + bool hasOpenGL() { return hasOGL; } GL::Context* getOGLContext(); void initOpenGL(); @@ -263,6 +267,8 @@ private: public: ScreenPanel* panel; + bool hasMenu; + QAction* actOpenROM; QAction* actBootFirmware; QAction* actCurrentCart; @@ -335,6 +341,4 @@ public: QAction* actAbout; }; -void ToggleFullscreen(MainWindow* mainWindow); - #endif // WINDOW_H diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 6d18adaf..e63c8a9b 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -364,7 +364,7 @@ int main(int argc, char** argv) win->preloadROMs(dsfile, gbafile, options->boot); if (options->fullscreen) - ToggleFullscreen(win); + win->toggleFullscreen(); } int ret = melon.exec(); From 4ae4397547f7703d6425cecb61df05be673a9258 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 02:42:27 +0200 Subject: [PATCH 28/63] properly update display type across all windows (remind me to also propagate changes across instances) --- src/frontend/qt_sdl/EmuThread.cpp | 4 ++-- src/frontend/qt_sdl/Window.cpp | 24 +++++++++++++++++++++--- src/frontend/qt_sdl/Window.h | 1 + 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 07e03e0f..1bf22c36 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -70,7 +70,7 @@ EmuThread::EmuThread(EmuInstance* inst, QObject* parent) : QThread(parent) void EmuThread::attachWindow(MainWindow* window) { - connect(this, SIGNAL(windowUpdate()), window->panel, SLOT(repaint())); + //connect(this, SIGNAL(windowUpdate()), window->panel, SLOT(repaint())); connect(this, SIGNAL(windowTitleChange(QString)), window, SLOT(onTitleUpdate(QString))); connect(this, SIGNAL(windowEmuStart()), window, SLOT(onEmuStart())); connect(this, SIGNAL(windowEmuStop()), window, SLOT(onEmuStop())); @@ -89,7 +89,7 @@ void EmuThread::attachWindow(MainWindow* window) void EmuThread::detachWindow(MainWindow* window) { - disconnect(this, SIGNAL(windowUpdate()), window->panel, SLOT(repaint())); + //disconnect(this, SIGNAL(windowUpdate()), window->panel, SLOT(repaint())); disconnect(this, SIGNAL(windowTitleChange(QString)), window, SLOT(onTitleUpdate(QString))); disconnect(this, SIGNAL(windowEmuStart()), window, SLOT(onEmuStart())); disconnect(this, SIGNAL(windowEmuStop()), window, SLOT(onEmuStop())); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 4a5fa439..16e30081 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -701,6 +701,7 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : } show(); + panel = nullptr; createScreenPanel(); if (hasMenu) @@ -827,6 +828,9 @@ void MainWindow::closeEvent(QCloseEvent* event) void MainWindow::createScreenPanel() { + if (panel) delete panel; + panel = nullptr; + hasOGL = globalCfg.GetBool("Screen.UseGL") || (globalCfg.GetInt("3D.Renderer") != renderer3D_Software); @@ -852,6 +856,8 @@ void MainWindow::createScreenPanel() actScreenFiltering->setEnabled(hasOGL); panel->osdSetEnabled(showOSD); + connect(emuThread, SIGNAL(windowUpdate()), panel, SLOT(repaint())); + connect(this, SIGNAL(screenLayoutChange()), panel, SLOT(onScreenLayoutChanged())); emit screenLayoutChange(); } @@ -2115,14 +2121,17 @@ void MainWindow::onEmuReset() void MainWindow::onUpdateVideoSettings(bool glchange) { + MainWindow* parentwin = (MainWindow*)parentWidget(); + if (parentwin) + return parentwin->onUpdateVideoSettings(glchange); + + bool hadOGL = hasOGL; if (glchange) { emuThread->emuPause(); - if (hasOGL) emuThread->deinitContext(windowID); + if (hadOGL) emuThread->deinitContext(windowID); - delete panel; createScreenPanel(); - connect(emuThread, SIGNAL(windowUpdate()), panel, SLOT(repaint())); } emuThread->updateVideoSettings(); @@ -2130,6 +2139,15 @@ void MainWindow::onUpdateVideoSettings(bool glchange) if (glchange) { if (hasOGL) emuThread->initContext(windowID); + + auto childwins = findChildren(Qt::FindDirectChildrenOnly); + for (auto child : childwins) + { + if (hadOGL) emuThread->deinitContext(child->windowID); + child->createScreenPanel(); + if (hasOGL) emuThread->initContext(child->windowID); + } + emuThread->emuUnpause(); } } diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index 32ba5dd2..17575b53 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -110,6 +110,7 @@ public: EmuInstance* getEmuInstance() { return emuInstance; } Config::Table& getWindowConfig() { return windowCfg; } + int getID() { return windowID; } bool winHasMenu() { return hasMenu; } From 2d561a60c88e42f230dc81e72a61469a90a91c42 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 09:16:46 +0100 Subject: [PATCH 29/63] fix Qt5 compatibility (sdffdf) --- src/frontend/qt_sdl/Window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 16e30081..50472300 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -2140,7 +2140,7 @@ void MainWindow::onUpdateVideoSettings(bool glchange) { if (hasOGL) emuThread->initContext(windowID); - auto childwins = findChildren(Qt::FindDirectChildrenOnly); + auto childwins = findChildren(nullptr, Qt::FindDirectChildrenOnly); for (auto child : childwins) { if (hadOGL) emuThread->deinitContext(child->windowID); From 2bf0eb7eadb3f5ee0ce5b532ecf53cac130d0c86 Mon Sep 17 00:00:00 2001 From: Gess1t <39861216+Mrcubix@users.noreply.github.com> Date: Sun, 27 Oct 2024 09:20:51 +0100 Subject: [PATCH 30/63] Handle failure of OpenGL context creation (#2172) --- src/frontend/qt_sdl/Screen.cpp | 12 ++++-------- src/frontend/qt_sdl/Window.cpp | 10 +++++++++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index 11a6150a..ff049120 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -753,10 +753,8 @@ bool ScreenPanelGL::createContext() if (parentwin) { if (windowinfo.has_value()) - { - glContext = parentwin->getOGLContext()->CreateSharedContext(*windowinfo); - glContext->DoneCurrent(); - } + if (glContext = parentwin->getOGLContext()->CreateSharedContext(*windowinfo)) + glContext->DoneCurrent(); } else { @@ -764,10 +762,8 @@ bool ScreenPanelGL::createContext() GL::Context::Version{GL::Context::Profile::Core, 4, 3}, GL::Context::Version{GL::Context::Profile::Core, 3, 2}}; if (windowinfo.has_value()) - { - glContext = GL::Context::Create(*windowinfo, versionsToTry); - glContext->DoneCurrent(); - } + if (glContext = GL::Context::Create(*windowinfo, versionsToTry)) + glContext->DoneCurrent(); } return glContext != nullptr; diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 50472300..3ba9ada5 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -841,7 +841,15 @@ void MainWindow::createScreenPanel() panel = panelGL; - panelGL->createContext(); + // Check that creating the context hasn't failed + if (panelGL->createContext() == false) + { + Log(LogLevel::Error, "Failed to create OpenGL context, falling back to Software Renderer.\n"); + hasOGL = false; + + globalCfg.SetBool("Screen.UseGL", false); + globalCfg.SetInt("3D.Renderer", renderer3D_Software); + } } if (!hasOGL) From 24ca1a5fdbd6f8c2a64966b2417eae678ab95c91 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 10:02:57 +0100 Subject: [PATCH 31/63] lay base for keeping config in sync across multiple instances --- src/frontend/qt_sdl/EmuInstance.cpp | 15 +++++++++ src/frontend/qt_sdl/EmuInstance.h | 3 ++ src/frontend/qt_sdl/Window.cpp | 50 ++++++++++++++++++++--------- src/frontend/qt_sdl/Window.h | 2 ++ src/frontend/qt_sdl/main.cpp | 12 +++++++ src/frontend/qt_sdl/main.h | 7 ++++ 6 files changed, 73 insertions(+), 16 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 09f6a2bc..7fc73670 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -241,6 +241,21 @@ void EmuInstance::deleteAllWindows() } +void EmuInstance::updateConfigInfo(int kind) +{ + switch (kind) + { + case Config_RecentFiles: + for (int i = 0; i < kMaxWindows; i++) + { + if (windowList[i]) + windowList[i]->loadRecentFilesMenu(true); + } + break; + } +} + + void EmuInstance::osdAddMessage(unsigned int color, const char* fmt, ...) { if (fmt == nullptr) diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 8022867a..a7ecddf8 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -21,6 +21,7 @@ #include +#include "main.h" #include "NDS.h" #include "EmuThread.h" #include "Window.h" @@ -91,6 +92,8 @@ public: Config::Table& getGlobalConfig() { return globalCfg; } Config::Table& getLocalConfig() { return localCfg; } + void updateConfigInfo(int kind); + std::string instanceFileSuffix(); void createWindow(); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 3ba9ada5..b44fed7a 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -271,7 +271,8 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : setStyleSheet("QMenuBar::item { padding: 4px 8px; }"); #endif - hasMenu = (!parent); + //hasMenu = (!parent); + hasMenu = true; if (hasMenu) { @@ -288,15 +289,7 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : actOpenROMArchive->setShortcut(QKeySequence(Qt::Key_O | Qt::CTRL | Qt::SHIFT));*/ recentMenu = menu->addMenu("Open recent"); - Config::Array recentROMs = globalCfg.GetArray("RecentROM"); - int numrecent = std::min(kMaxRecentROMs, (int) recentROMs.Size()); - for (int i = 0; i < numrecent; ++i) - { - std::string item = recentROMs.GetString(i); - if (!item.empty()) - recentFileList.push_back(QString::fromStdString(item)); - } - updateRecentFilesMenu(); + loadRecentFilesMenu(true); //actBootFirmware = menu->addAction("Launch DS menu"); actBootFirmware = menu->addAction("Boot firmware"); @@ -1280,12 +1273,23 @@ void MainWindow::onClearRecentFiles() updateRecentFilesMenu(); } -void MainWindow::updateRecentFilesMenu() +void MainWindow::loadRecentFilesMenu(bool loadcfg) { - recentMenu->clear(); + if (loadcfg) + { + recentFileList.clear(); - Config::Array recentroms = globalCfg.GetArray("RecentROM"); - recentroms.Clear(); + Config::Array recentROMs = globalCfg.GetArray("RecentROM"); + int numrecent = std::min(kMaxRecentROMs, (int) recentROMs.Size()); + for (int i = 0; i < numrecent; ++i) + { + std::string item = recentROMs.GetString(i); + if (!item.empty()) + recentFileList.push_back(QString::fromStdString(item)); + } + } + + recentMenu->clear(); for (int i = 0; i < recentFileList.size(); ++i) { @@ -1316,8 +1320,6 @@ void MainWindow::updateRecentFilesMenu() QAction *actRecentFile_i = recentMenu->addAction(QString("%1. %2").arg(i+1).arg(item_display)); actRecentFile_i->setData(item_full); connect(actRecentFile_i, &QAction::triggered, this, &MainWindow::onClickRecentFile); - - recentroms.SetQString(i, recentFileList.at(i)); } while (recentFileList.size() > 10) @@ -1330,8 +1332,24 @@ void MainWindow::updateRecentFilesMenu() if (recentFileList.empty()) actClearRecentList->setEnabled(false); +} + +void MainWindow::updateRecentFilesMenu() +{ + Config::Array recentroms = globalCfg.GetArray("RecentROM"); + recentroms.Clear(); + + for (int i = 0; i < recentFileList.size(); ++i) + { + if (i >= kMaxRecentROMs) break; + + recentroms.SetQString(i, recentFileList.at(i)); + } Config::Save(); + loadRecentFilesMenu(false); + + updateConfigInfoAll(Config_RecentFiles, emuInstance->getInstanceID()); } void MainWindow::onClickRecentFile() diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index 17575b53..a344c1e3 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -134,6 +134,8 @@ public: // called when the MP interface is changed void updateMPInterface(melonDS::MPInterfaceType type); + void loadRecentFilesMenu(bool loadcfg); + protected: void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index e63c8a9b..a4cc965e 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -168,6 +168,18 @@ int numEmuInstances() } +void updateConfigInfoAll(int kind, int sourceinst) +{ + for (int i = 0; i < kMaxEmuInstances; i++) + { + if (i == sourceinst) continue; + if (!emuInstances[i]) continue; + + emuInstances[i]->updateConfigInfo(kind); + } +} + + void pathInit() { // First, check for the portable directory next to the executable. diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 77cdf4ee..b247d1fd 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -31,6 +31,11 @@ #include "ScreenLayout.h" #include "MPInterface.h" +enum +{ + Config_RecentFiles, +}; + class MelonApplication : public QApplication { Q_OBJECT @@ -50,6 +55,8 @@ void deleteEmuInstance(int id); void deleteAllEmuInstances(int first = 0); int numEmuInstances(); +void updateConfigInfoAll(int kind, int sourceinst); + void setMPInterface(melonDS::MPInterfaceType type); #endif // MAIN_H From e6f0d77aa00f490875430c95ab30c4157ace5c05 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 10:17:59 +0100 Subject: [PATCH 32/63] fix freeze when starting new emu instance while using OpenGL --- src/frontend/qt_sdl/EmuInstance.cpp | 2 +- src/frontend/qt_sdl/Screen.cpp | 4 +++- src/frontend/qt_sdl/Window.cpp | 24 ++++++++++++++++-------- src/frontend/qt_sdl/Window.h | 2 +- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 7fc73670..3906b186 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -201,7 +201,7 @@ void EmuInstance::createWindow() emuThread->attachWindow(win); // if creating a secondary window, we may need to initialize its OpenGL context here - if (win->hasOpenGL() && (win != topWindow)) + if (win->hasOpenGL() && (id != 0)) emuThread->initContext(id); } diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index ff049120..37c35a65 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -749,8 +749,10 @@ bool ScreenPanelGL::createContext() // if our parent window is parented to another window, we will // share our OpenGL context with that window + MainWindow* ourwin = (MainWindow*)parentWidget(); MainWindow* parentwin = (MainWindow*)parentWidget()->parentWidget(); - if (parentwin) + //if (parentwin) + if (ourwin->getWindowID() != 0) { if (windowinfo.has_value()) if (glContext = parentwin->getOGLContext()->CreateSharedContext(*windowinfo)) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index b44fed7a..a231a6f8 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -2147,9 +2147,14 @@ void MainWindow::onEmuReset() void MainWindow::onUpdateVideoSettings(bool glchange) { - MainWindow* parentwin = (MainWindow*)parentWidget(); - if (parentwin) - return parentwin->onUpdateVideoSettings(glchange); + if (windowID != 0) + { + MainWindow* parentwin = (MainWindow*)parentWidget(); + if (parentwin) + parentwin->onUpdateVideoSettings(glchange); + + return; + } bool hadOGL = hasOGL; if (glchange) @@ -2166,12 +2171,15 @@ void MainWindow::onUpdateVideoSettings(bool glchange) { if (hasOGL) emuThread->initContext(windowID); - auto childwins = findChildren(nullptr, Qt::FindDirectChildrenOnly); - for (auto child : childwins) + if (windowID == 0) { - if (hadOGL) emuThread->deinitContext(child->windowID); - child->createScreenPanel(); - if (hasOGL) emuThread->initContext(child->windowID); + auto childwins = findChildren(nullptr, Qt::FindDirectChildrenOnly); + for (auto child: childwins) + { + if (hadOGL) emuThread->deinitContext(child->windowID); + child->createScreenPanel(); + if (hasOGL) emuThread->initContext(child->windowID); + } } emuThread->emuUnpause(); diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index a344c1e3..73bb84bb 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -110,7 +110,7 @@ public: EmuInstance* getEmuInstance() { return emuInstance; } Config::Table& getWindowConfig() { return windowCfg; } - int getID() { return windowID; } + int getWindowID() { return windowID; } bool winHasMenu() { return hasMenu; } From e576538268ba06e17324df675b549876ea66e849 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 11:21:30 +0100 Subject: [PATCH 33/63] synchronize pause/unpause across all instances --- src/frontend/qt_sdl/EmuInstance.cpp | 19 ++++++++++++++++--- src/frontend/qt_sdl/EmuInstance.h | 3 ++- src/frontend/qt_sdl/EmuThread.cpp | 18 +++++++++++------- src/frontend/qt_sdl/EmuThread.h | 6 +++--- src/frontend/qt_sdl/Window.cpp | 2 +- src/frontend/qt_sdl/main.cpp | 4 ++-- src/frontend/qt_sdl/main.h | 7 +++++-- 7 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 3906b186..a848fc3e 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -241,11 +241,24 @@ void EmuInstance::deleteAllWindows() } -void EmuInstance::updateConfigInfo(int kind) +void EmuInstance::broadcastCommand(int cmd) { - switch (kind) + broadcastInstanceCommand(cmd, instanceID); +} + +void EmuInstance::handleCommand(int cmd) +{ + switch (cmd) { - case Config_RecentFiles: + case InstCmd_Pause: + emuThread->emuPause(false); + break; + + case InstCmd_Unpause: + emuThread->emuUnpause(false); + break; + + case InstCmd_UpdateRecentFiles: for (int i = 0; i < kMaxWindows; i++) { if (windowList[i]) diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index a7ecddf8..61090496 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -92,7 +92,8 @@ public: Config::Table& getGlobalConfig() { return globalCfg; } Config::Table& getLocalConfig() { return localCfg; } - void updateConfigInfo(int kind); + void broadcastCommand(int cmd); + void handleCommand(int cmd); std::string instanceFileSuffix(); diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 1bf22c36..583ed91c 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -70,7 +70,6 @@ EmuThread::EmuThread(EmuInstance* inst, QObject* parent) : QThread(parent) void EmuThread::attachWindow(MainWindow* window) { - //connect(this, SIGNAL(windowUpdate()), window->panel, SLOT(repaint())); connect(this, SIGNAL(windowTitleChange(QString)), window, SLOT(onTitleUpdate(QString))); connect(this, SIGNAL(windowEmuStart()), window, SLOT(onEmuStart())); connect(this, SIGNAL(windowEmuStop()), window, SLOT(onEmuStop())); @@ -89,7 +88,6 @@ void EmuThread::attachWindow(MainWindow* window) void EmuThread::detachWindow(MainWindow* window) { - //disconnect(this, SIGNAL(windowUpdate()), window->panel, SLOT(repaint())); disconnect(this, SIGNAL(windowTitleChange(QString)), window, SLOT(onTitleUpdate(QString))); disconnect(this, SIGNAL(windowEmuStart()), window, SLOT(onEmuStart())); disconnect(this, SIGNAL(windowEmuStop()), window, SLOT(onEmuStop())); @@ -676,24 +674,30 @@ void EmuThread::emuRun() waitMessage(); } -void EmuThread::emuPause() +void EmuThread::emuPause(bool broadcast) { sendMessage(msg_EmuPause); waitMessage(); + + if (broadcast) + emuInstance->broadcastCommand(InstCmd_Pause); } -void EmuThread::emuUnpause() +void EmuThread::emuUnpause(bool broadcast) { sendMessage(msg_EmuUnpause); waitMessage(); + + if (broadcast) + emuInstance->broadcastCommand(InstCmd_Unpause); } -void EmuThread::emuTogglePause() +void EmuThread::emuTogglePause(bool broadcast) { if (emuStatus == emuStatus_Paused) - emuUnpause(); + emuUnpause(broadcast); else - emuPause(); + emuPause(broadcast); } void EmuThread::emuStop(bool external) diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index 57f389af..19e1a3a6 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -104,9 +104,9 @@ public: // to be called from the UI thread void emuRun(); - void emuPause(); - void emuUnpause(); - void emuTogglePause(); + void emuPause(bool broadcast = true); + void emuUnpause(bool broadcast = true); + void emuTogglePause(bool broadcast = true); void emuStop(bool external); void emuExit(); void emuFrameStep(); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index a231a6f8..442274b5 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1349,7 +1349,7 @@ void MainWindow::updateRecentFilesMenu() Config::Save(); loadRecentFilesMenu(false); - updateConfigInfoAll(Config_RecentFiles, emuInstance->getInstanceID()); + emuInstance->broadcastCommand(InstCmd_UpdateRecentFiles); } void MainWindow::onClickRecentFile() diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index a4cc965e..69485a8e 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -168,14 +168,14 @@ int numEmuInstances() } -void updateConfigInfoAll(int kind, int sourceinst) +void broadcastInstanceCommand(int cmd, int sourceinst) { for (int i = 0; i < kMaxEmuInstances; i++) { if (i == sourceinst) continue; if (!emuInstances[i]) continue; - emuInstances[i]->updateConfigInfo(kind); + emuInstances[i]->handleCommand(cmd); } } diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index b247d1fd..cd5a08f1 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -33,7 +33,10 @@ enum { - Config_RecentFiles, + InstCmd_Pause, + InstCmd_Unpause, + + InstCmd_UpdateRecentFiles, }; class MelonApplication : public QApplication @@ -55,7 +58,7 @@ void deleteEmuInstance(int id); void deleteAllEmuInstances(int first = 0); int numEmuInstances(); -void updateConfigInfoAll(int kind, int sourceinst); +void broadcastInstanceCommand(int cmd, int sourceinst); void setMPInterface(melonDS::MPInterfaceType type); From 6d345cc1eaabf426253b37359664af8d3dcfb0bd Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 13:43:26 +0100 Subject: [PATCH 34/63] correctly propagate video settings changes to all windows --- src/frontend/qt_sdl/EmuInstance.cpp | 10 +++++-- src/frontend/qt_sdl/EmuInstance.h | 4 +-- src/frontend/qt_sdl/Window.cpp | 44 ++++++++++++++++++----------- src/frontend/qt_sdl/Window.h | 1 + src/frontend/qt_sdl/main.cpp | 4 +-- src/frontend/qt_sdl/main.h | 3 +- 6 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index a848fc3e..7efa6509 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -241,12 +241,12 @@ void EmuInstance::deleteAllWindows() } -void EmuInstance::broadcastCommand(int cmd) +void EmuInstance::broadcastCommand(int cmd, QVariant param) { - broadcastInstanceCommand(cmd, instanceID); + broadcastInstanceCommand(cmd, param, instanceID); } -void EmuInstance::handleCommand(int cmd) +void EmuInstance::handleCommand(int cmd, QVariant& param) { switch (cmd) { @@ -265,6 +265,10 @@ void EmuInstance::handleCommand(int cmd) windowList[i]->loadRecentFilesMenu(true); } break; + + /*case InstCmd_UpdateVideoSettings: + mainWindow->updateVideoSettings(param.value()); + break;*/ } } diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 61090496..a79b4f75 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -92,8 +92,8 @@ public: Config::Table& getGlobalConfig() { return globalCfg; } Config::Table& getLocalConfig() { return localCfg; } - void broadcastCommand(int cmd); - void handleCommand(int cmd); + void broadcastCommand(int cmd, QVariant param = QVariant()); + void handleCommand(int cmd, QVariant& param); std::string instanceFileSuffix(); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 442274b5..fef51217 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -2147,14 +2147,11 @@ void MainWindow::onEmuReset() void MainWindow::onUpdateVideoSettings(bool glchange) { - if (windowID != 0) - { - MainWindow* parentwin = (MainWindow*)parentWidget(); - if (parentwin) - parentwin->onUpdateVideoSettings(glchange); - - return; - } + // if we have a parent window: pass the message over to the parent + // the topmost parent takes care of updating all the windows + MainWindow* parentwin = (MainWindow*)parentWidget(); + if (parentwin) + return parentwin->onUpdateVideoSettings(glchange); bool hadOGL = hasOGL; if (glchange) @@ -2170,18 +2167,33 @@ void MainWindow::onUpdateVideoSettings(bool glchange) if (glchange) { if (hasOGL) emuThread->initContext(windowID); + } - if (windowID == 0) + // update any child windows we have + auto childwins = findChildren(nullptr, Qt::FindDirectChildrenOnly); + for (auto child: childwins) + { + // child windows may belong to a different instance + // in that case we need to signal their thread appropriately + auto thread = child->getEmuInstance()->getEmuThread(); + + if (glchange) { - auto childwins = findChildren(nullptr, Qt::FindDirectChildrenOnly); - for (auto child: childwins) - { - if (hadOGL) emuThread->deinitContext(child->windowID); - child->createScreenPanel(); - if (hasOGL) emuThread->initContext(child->windowID); - } + if (hadOGL) thread->deinitContext(child->windowID); + child->createScreenPanel(); } + if (child->getWindowID() == 0) + thread->updateVideoSettings(); + + if (glchange) + { + if (hasOGL) thread->initContext(child->windowID); + } + } + + if (glchange) + { emuThread->emuUnpause(); } } diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index 73bb84bb..64f17841 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -135,6 +135,7 @@ public: void updateMPInterface(melonDS::MPInterfaceType type); void loadRecentFilesMenu(bool loadcfg); + //void updateVideoSettings(bool glchange); protected: void keyPressEvent(QKeyEvent* event) override; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 69485a8e..d940340a 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -168,14 +168,14 @@ int numEmuInstances() } -void broadcastInstanceCommand(int cmd, int sourceinst) +void broadcastInstanceCommand(int cmd, QVariant& param, int sourceinst) { for (int i = 0; i < kMaxEmuInstances; i++) { if (i == sourceinst) continue; if (!emuInstances[i]) continue; - emuInstances[i]->handleCommand(cmd); + emuInstances[i]->handleCommand(cmd, param); } } diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index cd5a08f1..e0d38963 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -37,6 +37,7 @@ enum InstCmd_Unpause, InstCmd_UpdateRecentFiles, + //InstCmd_UpdateVideoSettings, }; class MelonApplication : public QApplication @@ -58,7 +59,7 @@ void deleteEmuInstance(int id); void deleteAllEmuInstances(int first = 0); int numEmuInstances(); -void broadcastInstanceCommand(int cmd, int sourceinst); +void broadcastInstanceCommand(int cmd, QVariant& param, int sourceinst); void setMPInterface(melonDS::MPInterfaceType type); From a61754bb589cdacd45db78dca504dcf840d9f04e Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 13:53:12 +0100 Subject: [PATCH 35/63] fix possible crash when closing window while video settings dialog is open --- src/frontend/qt_sdl/Window.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index fef51217..5115045a 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -2147,6 +2147,8 @@ void MainWindow::onEmuReset() void MainWindow::onUpdateVideoSettings(bool glchange) { + if (!emuInstance) return; + // if we have a parent window: pass the message over to the parent // the topmost parent takes care of updating all the windows MainWindow* parentwin = (MainWindow*)parentWidget(); From f2dce621ceb50f8f617fa4bd252821660ab6ef0e Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 14:24:59 +0100 Subject: [PATCH 36/63] proof all dialogs against use-after-free when closing main window while a dialog is open --- src/frontend/qt_sdl/AudioSettingsDialog.cpp | 6 ++++++ src/frontend/qt_sdl/CameraSettingsDialog.cpp | 6 ++++++ src/frontend/qt_sdl/EmuSettingsDialog.cpp | 7 +++++++ src/frontend/qt_sdl/FirmwareSettingsDialog.cpp | 7 +++++++ .../qt_sdl/InterfaceSettingsDialog.cpp | 7 +++++++ src/frontend/qt_sdl/LANDialog.cpp | 18 ++++++++++++++++++ src/frontend/qt_sdl/MPSettingsDialog.cpp | 7 +++++++ src/frontend/qt_sdl/NetplayDialog.cpp | 12 ++++++++++++ src/frontend/qt_sdl/PathSettingsDialog.cpp | 7 +++++++ src/frontend/qt_sdl/VideoSettingsDialog.cpp | 6 ++++++ src/frontend/qt_sdl/WifiSettingsDialog.cpp | 7 +++++++ 11 files changed, 90 insertions(+) diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp index 37f856ac..5c0c9330 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp +++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp @@ -170,6 +170,12 @@ void AudioSettingsDialog::on_AudioSettingsDialog_accepted() void AudioSettingsDialog::on_AudioSettingsDialog_rejected() { + if (!((MainWindow*)parent())->getEmuInstance()) + { + closeDlg(); + return; + } + auto& cfg = emuInstance->getGlobalConfig(); auto& instcfg = emuInstance->getLocalConfig(); cfg.SetInt("Audio.Interpolation", oldInterp); diff --git a/src/frontend/qt_sdl/CameraSettingsDialog.cpp b/src/frontend/qt_sdl/CameraSettingsDialog.cpp index 39c05cef..63b7a76e 100644 --- a/src/frontend/qt_sdl/CameraSettingsDialog.cpp +++ b/src/frontend/qt_sdl/CameraSettingsDialog.cpp @@ -163,6 +163,12 @@ void CameraSettingsDialog::on_CameraSettingsDialog_accepted() void CameraSettingsDialog::on_CameraSettingsDialog_rejected() { + if (!((MainWindow*)parent())->getEmuInstance()) + { + closeDlg(); + return; + } + for (int i = 0; i < 2; i++) { camManager[i]->stop(); diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp index 7a6c0f40..b37f7118 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -215,6 +215,13 @@ void EmuSettingsDialog::verifyFirmware() void EmuSettingsDialog::done(int r) { + if (!((MainWindow*)parent())->getEmuInstance()) + { + QDialog::done(r); + closeDlg(); + return; + } + needsReset = false; if (r == QDialog::Accepted) diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp index 5d5ecd01..1f71b5b9 100644 --- a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp +++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp @@ -132,6 +132,13 @@ bool FirmwareSettingsDialog::verifyMAC() void FirmwareSettingsDialog::done(int r) { + if (!((MainWindow*)parent())->getEmuInstance()) + { + QDialog::done(r); + closeDlg(); + return; + } + needsReset = false; if (r == QDialog::Accepted) diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp index 2e7e75c9..bd02405e 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp @@ -104,6 +104,13 @@ void InterfaceSettingsDialog::on_pbQuarter_clicked() void InterfaceSettingsDialog::done(int r) { + if (!((MainWindow*)parent())->getEmuInstance()) + { + QDialog::done(r); + closeDlg(); + return; + } + if (r == QDialog::Accepted) { auto& cfg = emuInstance->getGlobalConfig(); diff --git a/src/frontend/qt_sdl/LANDialog.cpp b/src/frontend/qt_sdl/LANDialog.cpp index 32539e3f..bec9c48f 100644 --- a/src/frontend/qt_sdl/LANDialog.cpp +++ b/src/frontend/qt_sdl/LANDialog.cpp @@ -65,6 +65,12 @@ LANStartHostDialog::~LANStartHostDialog() void LANStartHostDialog::done(int r) { + if (!((MainWindow*)parent())->getEmuInstance()) + { + QDialog::done(r); + return; + } + if (r == QDialog::Accepted) { if (ui->txtPlayerName->text().trimmed().isEmpty()) @@ -186,6 +192,12 @@ void LANStartClientDialog::onDirectConnect() void LANStartClientDialog::done(int r) { + if (!((MainWindow*)parent())->getEmuInstance()) + { + QDialog::done(r); + return; + } + if (r == QDialog::Accepted) { if (ui->txtPlayerName->text().trimmed().isEmpty()) @@ -313,6 +325,12 @@ void LANDialog::on_btnLeaveGame_clicked() void LANDialog::done(int r) { + if (!((MainWindow*)parent())->getEmuInstance()) + { + QDialog::done(r); + return; + } + bool showwarning = true; if (lan().GetNumPlayers() < 2) showwarning = false; diff --git a/src/frontend/qt_sdl/MPSettingsDialog.cpp b/src/frontend/qt_sdl/MPSettingsDialog.cpp index e241ba3d..54c35d15 100644 --- a/src/frontend/qt_sdl/MPSettingsDialog.cpp +++ b/src/frontend/qt_sdl/MPSettingsDialog.cpp @@ -59,6 +59,13 @@ MPSettingsDialog::~MPSettingsDialog() void MPSettingsDialog::done(int r) { + if (!((MainWindow*)parent())->getEmuInstance()) + { + QDialog::done(r); + closeDlg(); + return; + } + if (r == QDialog::Accepted) { auto& cfg = emuInstance->getGlobalConfig(); diff --git a/src/frontend/qt_sdl/NetplayDialog.cpp b/src/frontend/qt_sdl/NetplayDialog.cpp index e9ed6022..d7b7cf81 100644 --- a/src/frontend/qt_sdl/NetplayDialog.cpp +++ b/src/frontend/qt_sdl/NetplayDialog.cpp @@ -63,6 +63,12 @@ NetplayStartHostDialog::~NetplayStartHostDialog() void NetplayStartHostDialog::done(int r) { + if (!((MainWindow*)parent())->getEmuInstance()) + { + QDialog::done(r); + return; + } + if (r == QDialog::Accepted) { std::string player = ui->txtPlayerName->text().toStdString(); @@ -94,6 +100,12 @@ NetplayStartClientDialog::~NetplayStartClientDialog() void NetplayStartClientDialog::done(int r) { + if (!((MainWindow*)parent())->getEmuInstance()) + { + QDialog::done(r); + return; + } + if (r == QDialog::Accepted) { std::string player = ui->txtPlayerName->text().toStdString(); diff --git a/src/frontend/qt_sdl/PathSettingsDialog.cpp b/src/frontend/qt_sdl/PathSettingsDialog.cpp index b1bc8301..f3a453d1 100644 --- a/src/frontend/qt_sdl/PathSettingsDialog.cpp +++ b/src/frontend/qt_sdl/PathSettingsDialog.cpp @@ -72,6 +72,13 @@ PathSettingsDialog::~PathSettingsDialog() void PathSettingsDialog::done(int r) { + if (!((MainWindow*)parent())->getEmuInstance()) + { + QDialog::done(r); + closeDlg(); + return; + } + needsReset = false; if (r == QDialog::Accepted) diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.cpp b/src/frontend/qt_sdl/VideoSettingsDialog.cpp index 7eebf5a4..619ecda3 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.cpp +++ b/src/frontend/qt_sdl/VideoSettingsDialog.cpp @@ -121,6 +121,12 @@ void VideoSettingsDialog::on_VideoSettingsDialog_accepted() void VideoSettingsDialog::on_VideoSettingsDialog_rejected() { + if (!((MainWindow*)parent())->getEmuInstance()) + { + closeDlg(); + return; + } + bool old_gl = UsesGL(); auto& cfg = emuInstance->getGlobalConfig(); diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.cpp b/src/frontend/qt_sdl/WifiSettingsDialog.cpp index c3f988b1..e0954c83 100644 --- a/src/frontend/qt_sdl/WifiSettingsDialog.cpp +++ b/src/frontend/qt_sdl/WifiSettingsDialog.cpp @@ -94,6 +94,13 @@ WifiSettingsDialog::~WifiSettingsDialog() void WifiSettingsDialog::done(int r) { + if (!((MainWindow*)parent())->getEmuInstance()) + { + QDialog::done(r); + closeDlg(); + return; + } + needsReset = false; if (r == QDialog::Accepted) From 94955aee81cc06c51db84300f87fb7ee41e62b69 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 14:51:11 +0100 Subject: [PATCH 37/63] fix another OpenGL bug (when closing secondary window) --- src/frontend/qt_sdl/EmuThread.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 583ed91c..3371a03b 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -559,7 +559,8 @@ void EmuThread::handleMessages() case msg_DeInitGL: emuInstance->deinitOpenGL(msg.param.value()); - useOpenGL = false; + if (msg.param.value() == 0) + useOpenGL = false; break; case msg_BootROM: From d79d45a117a219c3d47128f9458004b2560b7db8 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 16:21:09 +0100 Subject: [PATCH 38/63] properly sync up menus between windows of a same instance --- src/frontend/qt_sdl/EmuInstance.cpp | 11 ++++++++++ src/frontend/qt_sdl/EmuInstance.h | 2 ++ src/frontend/qt_sdl/Window.cpp | 34 +++++++++++++++++++++-------- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 7efa6509..1387686e 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -240,6 +240,17 @@ void EmuInstance::deleteAllWindows() deleteWindow(i, true); } +void EmuInstance::doOnAllWindows(std::function func, int exclude) +{ + for (int i = 0; i < kMaxWindows; i++) + { + if (i == exclude) continue; + if (!windowList[i]) continue; + + func(windowList[i]); + } +} + void EmuInstance::broadcastCommand(int cmd, QVariant param) { diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index a79b4f75..ed6ca0df 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -89,6 +89,8 @@ public: MainWindow* getMainWindow() { return mainWindow; } MainWindow* getWindow(int id) { return windowList[id]; } + void doOnAllWindows(std::function func, int exclude = -1); + Config::Table& getGlobalConfig() { return globalCfg; } Config::Table& getLocalConfig() { return localCfg; } diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 5115045a..8f90a47f 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -781,6 +781,9 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : actPreferences->setEnabled(false); #endif // __APPLE__ } + + if (emuThread->emuIsActive()) + onEmuStart(); } QObject::connect(qApp, &QApplication::applicationStateChanged, this, &MainWindow::onAppStateChanged); @@ -1226,21 +1229,34 @@ QStringList MainWindow::pickROM(bool gba) void MainWindow::updateCartInserted(bool gba) { bool inserted; + QString label; if (gba) { - inserted = emuInstance->gbaCartInserted() && (globalCfg.GetInt("Emu.ConsoleType") == 0); - actCurrentGBACart->setText("GBA slot: " + emuInstance->gbaCartLabel()); - actEjectGBACart->setEnabled(inserted); + inserted = emuInstance->gbaCartInserted() && (emuInstance->getConsoleType() == 0); + label = "GBA slot: " + emuInstance->gbaCartLabel(); + + emuInstance->doOnAllWindows([=](MainWindow* win) + { + if (!win->hasMenu) return; + win->actCurrentGBACart->setText(label); + win->actEjectGBACart->setEnabled(inserted); + }); } else { inserted = emuInstance->cartInserted(); - actCurrentCart->setText("DS slot: " + emuInstance->cartLabel()); - actEjectCart->setEnabled(inserted); - actImportSavefile->setEnabled(inserted); - actSetupCheats->setEnabled(inserted); - actROMInfo->setEnabled(inserted); - actRAMInfo->setEnabled(inserted); + label = "DS slot: " + emuInstance->cartLabel(); + + emuInstance->doOnAllWindows([=](MainWindow* win) + { + if (!win->hasMenu) return; + win->actCurrentCart->setText(label); + win->actEjectCart->setEnabled(inserted); + win->actImportSavefile->setEnabled(inserted); + win->actSetupCheats->setEnabled(inserted); + win->actROMInfo->setEnabled(inserted); + win->actRAMInfo->setEnabled(inserted); + }); } } From 238c5525997c49d9c2ad50d51bb53e86f57d58a3 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 16:26:29 +0100 Subject: [PATCH 39/63] limit to 4 windows, and disable 'new window' menu item when that amount is reached --- src/frontend/qt_sdl/EmuInstance.cpp | 14 ++++++++++++++ src/frontend/qt_sdl/EmuInstance.h | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 1387686e..12afb12b 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -203,6 +203,12 @@ void EmuInstance::createWindow() // if creating a secondary window, we may need to initialize its OpenGL context here if (win->hasOpenGL() && (id != 0)) emuThread->initContext(id); + + bool enable = (numWindows < kMaxWindows); + doOnAllWindows([=](MainWindow* win) + { + win->actNewWindow->setEnabled(enable); + }); } void EmuInstance::deleteWindow(int id, bool close) @@ -232,6 +238,14 @@ void EmuInstance::deleteWindow(int id, bool close) // if the main window is closed, Qt will take care of closing any secondary windows deleteEmuInstance(instanceID); } + else + { + bool enable = (numWindows < kMaxWindows); + doOnAllWindows([=](MainWindow* win) + { + win->actNewWindow->setEnabled(enable); + }); + } } void EmuInstance::deleteAllWindows() diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index ed6ca0df..0122b0d9 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -28,7 +28,7 @@ #include "Config.h" #include "SaveManager.h" -const int kMaxWindows = 16; +const int kMaxWindows = 4; enum { @@ -87,6 +87,7 @@ public: melonDS::NDS* getNDS() { return nds; } MainWindow* getMainWindow() { return mainWindow; } + int getNumWindows() { return numWindows; } MainWindow* getWindow(int id) { return windowList[id]; } void doOnAllWindows(std::function func, int exclude = -1); From e42829ea81c3a31706614e95488b27a5a3ca5f39 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 17:21:41 +0100 Subject: [PATCH 40/63] pause emu during file select prompts --- src/frontend/qt_sdl/Window.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 8f90a47f..b09f8354 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1196,6 +1196,8 @@ QString MainWindow::pickFileFromArchive(QString archiveFileName) QStringList MainWindow::pickROM(bool gba) { + emuThread->emuPause(); + const QString console = gba ? "GBA" : "DS"; const QStringList& romexts = gba ? GbaRomExtensions : NdsRomExtensions; @@ -1220,10 +1222,16 @@ QStringList MainWindow::pickROM(bool gba) "All supported files (*" + allROMs + ")" + extraFilters ); - if (filename.isEmpty()) return {}; + if (filename.isEmpty()) + { + emuThread->emuUnpause(); + return {}; + } globalCfg.SetQString("LastROMFolder", QFileInfo(filename).dir().path()); - return splitArchivePath(filename, false); + auto ret = splitArchivePath(filename, false); + emuThread->emuUnpause(); + return ret; } void MainWindow::updateCartInserted(bool gba) @@ -1465,10 +1473,12 @@ void MainWindow::onSaveState() else { // TODO: specific 'last directory' for savestate files? + emuThread->emuPause(); filename = QFileDialog::getSaveFileName(this, "Save state", globalCfg.GetQString("LastROMFolder"), "melonDS savestates (*.mln);;Any file (*.*)"); + emuThread->emuUnpause(); if (filename.isEmpty()) return; } @@ -1498,10 +1508,12 @@ void MainWindow::onLoadState() else { // TODO: specific 'last directory' for savestate files? + emuThread->emuPause(); filename = QFileDialog::getOpenFileName(this, "Load state", globalCfg.GetQString("LastROMFolder"), "melonDS savestates (*.ml*);;Any file (*.*)"); + emuThread->emuUnpause(); if (filename.isEmpty()) return; } From 12b207d915e5437e532f92434cad70047e67e920 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 27 Oct 2024 18:49:17 +0100 Subject: [PATCH 41/63] remember which windows are opened --- src/frontend/qt_sdl/Config.cpp | 2 +- src/frontend/qt_sdl/EmuInstance.cpp | 34 ++++++++++++++++++++++++----- src/frontend/qt_sdl/EmuInstance.h | 3 ++- src/frontend/qt_sdl/Window.cpp | 15 ++++++++++++- src/frontend/qt_sdl/Window.h | 3 +++ 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 3f570302..02be5b65 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -99,7 +99,7 @@ DefaultList DefaultBools = {"3D.Soft.Threaded", true}, {"3D.GL.HiresCoordinates", true}, {"LimitFPS", true}, - {"Window*.ShowOSD", true}, + {"Instance*.Window*.ShowOSD", true}, {"Emu.DirectBoot", true}, {"Instance*.DS.Battery.LevelOkay", true}, {"Instance*.DSi.Battery.Charging", true}, diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 12afb12b..90116692 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -137,6 +137,16 @@ EmuInstance::EmuInstance(int inst) : deleting(false), emuThread->start(); emuThread->emuPause(); + + // if any extra windows were saved as enabled, open them + for (int i = 1; i < kMaxWindows; i++) + { + //Config::Table tbl = localCfg.GetTable("Window"+std::to_string(i), "Window0"); + std::string key = "Window" + std::to_string(i) + ".Enabled"; + bool enable = localCfg.GetBool(key); + if (enable) + createWindow(i); + } } EmuInstance::~EmuInstance() @@ -173,7 +183,7 @@ std::string EmuInstance::instanceFileSuffix() return suffix; } -void EmuInstance::createWindow() +void EmuInstance::createWindow(int id) { if (numWindows >= kMaxWindows) { @@ -181,16 +191,20 @@ void EmuInstance::createWindow() return; } - int id = -1; - for (int i = 0; i < kMaxWindows; i++) + if (id == -1) { - if (windowList[i]) continue; - id = i; - break; + for (int i = 0; i < kMaxWindows; i++) + { + if (windowList[i]) continue; + id = i; + break; + } } if (id == -1) return; + if (windowList[id]) + return; MainWindow* win = new MainWindow(id, this, topWindow); if (!topWindow) topWindow = win; @@ -265,6 +279,14 @@ void EmuInstance::doOnAllWindows(std::function func, int excl } } +void EmuInstance::saveEnabledWindows() +{ + doOnAllWindows([=](MainWindow* win) + { + win->saveEnabled(true); + }); +} + void EmuInstance::broadcastCommand(int cmd, QVariant param) { diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 0122b0d9..658247f0 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -91,6 +91,7 @@ public: MainWindow* getWindow(int id) { return windowList[id]; } void doOnAllWindows(std::function func, int exclude = -1); + void saveEnabledWindows(); Config::Table& getGlobalConfig() { return globalCfg; } Config::Table& getLocalConfig() { return localCfg; } @@ -100,7 +101,7 @@ public: std::string instanceFileSuffix(); - void createWindow(); + void createWindow(int id = -1); void deleteWindow(int id, bool close); void deleteAllWindows(); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index b09f8354..2354fa20 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -234,7 +234,8 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : globalCfg(inst->globalCfg), localCfg(inst->localCfg), windowCfg(localCfg.GetTable("Window"+std::to_string(id), "Window0")), - emuThread(inst->getEmuThread()) + emuThread(inst->getEmuThread()), + enabledSaved(false) { #ifndef _WIN32 if (!parent) @@ -807,8 +808,20 @@ void MainWindow::osdAddMessage(unsigned int color, const char* msg) panel->osdAddMessage(color, msg); } +void MainWindow::saveEnabled(bool enabled) +{ + if (enabledSaved) return; + windowCfg.SetBool("Enabled", enabled); + enabledSaved = true; +} + void MainWindow::closeEvent(QCloseEvent* event) { + if (windowID == 0) + emuInstance->saveEnabledWindows(); + else + saveEnabled(false); + QByteArray geom = saveGeometry(); QByteArray enc = geom.toBase64(QByteArray::Base64Encoding); windowCfg.SetString("Geometry", enc.toStdString()); diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index 64f17841..121115c0 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -114,6 +114,8 @@ public: bool winHasMenu() { return hasMenu; } + void saveEnabled(bool enabled); + void toggleFullscreen(); bool hasOpenGL() { return hasOGL; } @@ -260,6 +262,7 @@ private: bool pausedManually; int windowID; + bool enabledSaved; EmuInstance* emuInstance; EmuThread* emuThread; From b03bceb5c1108f2a14a78b0e5cca5671e0c85ce7 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sun, 27 Oct 2024 21:22:49 +0100 Subject: [PATCH 42/63] flake: shell should also use qt6's stdenv --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index e898674a..8d500c03 100644 --- a/flake.nix +++ b/flake.nix @@ -74,7 +74,7 @@ drv = self.packages.${system}.default; }; devShells = { - default = pkgs.mkShell { + default = pkgs.mkShell.override { stdenv = pkgs.qt6.qtbase.stdenv; } { inputsFrom = [ self.packages.${system}.default ]; }; From 98d969ab15b8be1463386e330e48e64772a1584f Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sun, 27 Oct 2024 21:23:15 +0100 Subject: [PATCH 43/63] only apply windows11 theme workaround to Qt6. Qt5 doesn't have it anywya. --- src/frontend/qt_sdl/Window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 2354fa20..5c599e70 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -265,7 +265,7 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : setAcceptDrops(true); setFocusPolicy(Qt::ClickFocus); -#ifdef WIN32 +#ifdef QT_VERSION_MAJOR == 6 && WIN32 // The "windows11" theme has pretty massive padding around menubar items, this makes Config and Help not fit in a window at 1x screen sizing // So let's reduce the padding a bit. if (QApplication::style()->name() == "windows11") From b60f42b281e4b465ff3cd6f964ab10d238ad31bd Mon Sep 17 00:00:00 2001 From: GalaxyShard <76917584+GalaxyShard@users.noreply.github.com> Date: Sun, 27 Oct 2024 21:06:59 +0000 Subject: [PATCH 44/63] Fix gdb break on start & gdb ports not closing after restarting/crashing (#2167) --- src/ARM.cpp | 1 + src/debug/GdbStub.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/ARM.cpp b/src/ARM.cpp index 4cf70749..b7b703da 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -110,6 +110,7 @@ const u32 ARM::ConditionTable[16] = ARM::ARM(u32 num, bool jit, std::optional gdb, melonDS::NDS& nds) : #ifdef GDBSTUB_ENABLED GdbStub(this, gdb ? (num ? gdb->PortARM7 : gdb->PortARM9) : 0), + BreakOnStartup(gdb ? (num ? gdb->ARM7BreakOnStartup : gdb->ARM9BreakOnStartup) : false), #endif Num(num), // well uh NDS(nds) diff --git a/src/debug/GdbStub.cpp b/src/debug/GdbStub.cpp index 14a8670a..b055794a 100644 --- a/src/debug/GdbStub.cpp +++ b/src/debug/GdbStub.cpp @@ -101,6 +101,15 @@ bool GdbStub::Init() Log(LogLevel::Error, "[GDB] err: can't create a socket fd\n"); goto err; } + { + // Make sure the port can be reused immediately after melonDS stops and/or restarts + int enable = 1; +#ifdef _WIN32 + setsockopt(SockFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&enable, sizeof(enable)); +#else + setsockopt(SockFd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); +#endif + } #ifndef __linux__ SocketSetBlocking(SockFd, false); #endif From 58ab33210af3bbcfdba219990eb56b9716622e04 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Sun, 27 Oct 2024 23:32:05 +0100 Subject: [PATCH 45/63] handle address wrap around in texture cache fixes out of bounds access in Mario 64 also slightly optimise paletted texture conversion --- src/GPU.h | 11 ++++ src/GPU3D_Soft.cpp | 50 ++++++++--------- src/GPU3D_Soft.h | 10 ---- src/GPU3D_Texcache.cpp | 61 ++++++++++----------- src/GPU3D_Texcache.h | 118 ++++++++++++++++++++++++----------------- 5 files changed, 136 insertions(+), 114 deletions(-) diff --git a/src/GPU.h b/src/GPU.h index 26e9d5df..5c373ca8 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -499,6 +499,17 @@ public: OAMDirty |= 1 << (addr / 1024); } + template + inline T ReadVRAMFlat_Texture(u32 addr) const + { + return *(T*)&VRAMFlat_Texture[addr & 0x7FFFF]; + } + template + inline T ReadVRAMFlat_TexPal(u32 addr) const + { + return *(T*)&VRAMFlat_TexPal[addr & 0x1FFFF]; + } + void SetPowerCnt(u32 val) noexcept; void StartFrame() noexcept; diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index 1221ed59..a9d0bd64 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -193,10 +193,10 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s case 1: // A3I5 { vramaddr += ((t * width) + s); - u8 pixel = ReadVRAM_Texture(vramaddr, gpu); + u8 pixel = gpu.ReadVRAMFlat_Texture(vramaddr); texpal <<= 4; - *color = ReadVRAM_TexPal(texpal + ((pixel&0x1F)<<1), gpu); + *color = gpu.ReadVRAMFlat_TexPal(texpal + ((pixel&0x1F)<<1)); *alpha = ((pixel >> 3) & 0x1C) + (pixel >> 6); } break; @@ -204,12 +204,12 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s case 2: // 4-color { vramaddr += (((t * width) + s) >> 2); - u8 pixel = ReadVRAM_Texture(vramaddr, gpu); + u8 pixel = gpu.ReadVRAMFlat_Texture(vramaddr); pixel >>= ((s & 0x3) << 1); pixel &= 0x3; texpal <<= 3; - *color = ReadVRAM_TexPal(texpal + (pixel<<1), gpu); + *color = gpu.ReadVRAMFlat_TexPal(texpal + (pixel<<1)); *alpha = (pixel==0) ? alpha0 : 31; } break; @@ -217,12 +217,12 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s case 3: // 16-color { vramaddr += (((t * width) + s) >> 1); - u8 pixel = ReadVRAM_Texture(vramaddr, gpu); + u8 pixel = gpu.ReadVRAMFlat_Texture(vramaddr); if (s & 0x1) pixel >>= 4; else pixel &= 0xF; texpal <<= 4; - *color = ReadVRAM_TexPal(texpal + (pixel<<1), gpu); + *color = gpu.ReadVRAMFlat_TexPal(texpal + (pixel<<1)); *alpha = (pixel==0) ? alpha0 : 31; } break; @@ -230,10 +230,10 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s case 4: // 256-color { vramaddr += ((t * width) + s); - u8 pixel = ReadVRAM_Texture(vramaddr, gpu); + u8 pixel = gpu.ReadVRAMFlat_Texture(vramaddr); texpal <<= 4; - *color = ReadVRAM_TexPal(texpal + (pixel<<1), gpu); + *color = gpu.ReadVRAMFlat_TexPal(texpal + (pixel<<1)); *alpha = (pixel==0) ? alpha0 : 31; } break; @@ -253,31 +253,31 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s val = 0; else { - val = ReadVRAM_Texture(vramaddr, gpu); + val = gpu.ReadVRAMFlat_Texture(vramaddr); val >>= (2 * (s & 0x3)); } - u16 palinfo = ReadVRAM_Texture(slot1addr, gpu); + u16 palinfo = gpu.ReadVRAMFlat_Texture(slot1addr); u32 paloffset = (palinfo & 0x3FFF) << 2; texpal <<= 4; switch (val & 0x3) { case 0: - *color = ReadVRAM_TexPal(texpal + paloffset, gpu); + *color = gpu.ReadVRAMFlat_TexPal(texpal + paloffset); *alpha = 31; break; case 1: - *color = ReadVRAM_TexPal(texpal + paloffset + 2, gpu); + *color = gpu.ReadVRAMFlat_TexPal(texpal + paloffset + 2); *alpha = 31; break; case 2: if ((palinfo >> 14) == 1) { - u16 color0 = ReadVRAM_TexPal(texpal + paloffset, gpu); - u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2, gpu); + u16 color0 = gpu.ReadVRAMFlat_TexPal(texpal + paloffset); + u16 color1 = gpu.ReadVRAMFlat_TexPal(texpal + paloffset + 2); u32 r0 = color0 & 0x001F; u32 g0 = color0 & 0x03E0; @@ -294,8 +294,8 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s } else if ((palinfo >> 14) == 3) { - u16 color0 = ReadVRAM_TexPal(texpal + paloffset, gpu); - u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2, gpu); + u16 color0 = gpu.ReadVRAMFlat_TexPal(texpal + paloffset); + u16 color1 = gpu.ReadVRAMFlat_TexPal(texpal + paloffset + 2); u32 r0 = color0 & 0x001F; u32 g0 = color0 & 0x03E0; @@ -311,20 +311,20 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s *color = r | g | b; } else - *color = ReadVRAM_TexPal(texpal + paloffset + 4, gpu); + *color = gpu.ReadVRAMFlat_TexPal(texpal + paloffset + 4); *alpha = 31; break; case 3: if ((palinfo >> 14) == 2) { - *color = ReadVRAM_TexPal(texpal + paloffset + 6, gpu); + *color = gpu.ReadVRAMFlat_TexPal(texpal + paloffset + 6); *alpha = 31; } else if ((palinfo >> 14) == 3) { - u16 color0 = ReadVRAM_TexPal(texpal + paloffset, gpu); - u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2, gpu); + u16 color0 = gpu.ReadVRAMFlat_TexPal(texpal + paloffset); + u16 color1 = gpu.ReadVRAMFlat_TexPal(texpal + paloffset + 2); u32 r0 = color0 & 0x001F; u32 g0 = color0 & 0x03E0; @@ -353,10 +353,10 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s case 6: // A5I3 { vramaddr += ((t * width) + s); - u8 pixel = ReadVRAM_Texture(vramaddr, gpu); + u8 pixel = gpu.ReadVRAMFlat_Texture(vramaddr); texpal <<= 4; - *color = ReadVRAM_TexPal(texpal + ((pixel&0x7)<<1), gpu); + *color = gpu.ReadVRAMFlat_TexPal(texpal + ((pixel&0x7)<<1)); *alpha = (pixel >> 3); } break; @@ -364,7 +364,7 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s case 7: // direct color { vramaddr += (((t * width) + s) << 1); - *color = ReadVRAM_Texture(vramaddr, gpu); + *color = gpu.ReadVRAMFlat_Texture(vramaddr); *alpha = (*color & 0x8000) ? 31 : 0; } break; @@ -1659,8 +1659,8 @@ void SoftRenderer::ClearBuffers(const GPU& gpu) { for (int x = 0; x < 256; x++) { - u16 val2 = ReadVRAM_Texture(0x40000 + (yoff << 9) + (xoff << 1), gpu); - u16 val3 = ReadVRAM_Texture(0x60000 + (yoff << 9) + (xoff << 1), gpu); + u16 val2 = gpu.ReadVRAMFlat_Texture(0x40000 + (yoff << 9) + (xoff << 1)); + u16 val3 = gpu.ReadVRAMFlat_Texture(0x60000 + (yoff << 9) + (xoff << 1)); // TODO: confirm color conversion u32 r = (val2 << 1) & 0x3E; if (r) r++; diff --git a/src/GPU3D_Soft.h b/src/GPU3D_Soft.h index 55a698b0..73d02e4f 100644 --- a/src/GPU3D_Soft.h +++ b/src/GPU3D_Soft.h @@ -430,16 +430,6 @@ private: s32 ycoverage, ycov_incr; }; - template - inline T ReadVRAM_Texture(u32 addr, const GPU& gpu) const - { - return *(T*)&gpu.VRAMFlat_Texture[addr & 0x7FFFF]; - } - template - inline T ReadVRAM_TexPal(u32 addr, const GPU& gpu) const - { - return *(T*)&gpu.VRAMFlat_TexPal[addr & 0x1FFFF]; - } u32 AlphaBlend(const GPU3D& gpu3d, u32 srccolor, u32 dstcolor, u32 alpha) const noexcept; struct RendererPolygon diff --git a/src/GPU3D_Texcache.cpp b/src/GPU3D_Texcache.cpp index 196009e6..a6a40a04 100644 --- a/src/GPU3D_Texcache.cpp +++ b/src/GPU3D_Texcache.cpp @@ -75,11 +75,11 @@ inline u32 ConvertRGB5ToRGB6(u16 val) } template -void ConvertBitmapTexture(u32 width, u32 height, u32* output, u8* texData) +void ConvertBitmapTexture(u32 width, u32 height, u32* output, u32 addr, GPU& gpu) { for (u32 i = 0; i < width*height; i++) { - u16 value = *(u16*)&texData[i * 2]; + u16 value = gpu.ReadVRAMFlat_Texture(addr + i * 2); switch (outputFmt) { @@ -96,28 +96,28 @@ void ConvertBitmapTexture(u32 width, u32 height, u32* output, u8* texData) } } -template void ConvertBitmapTexture(u32 width, u32 height, u32* output, u8* texData); +template void ConvertBitmapTexture(u32 width, u32 height, u32* output, u32 addr, GPU& gpu); template -void ConvertCompressedTexture(u32 width, u32 height, u32* output, u8* texData, u8* texAuxData, u16* palData) +void ConvertCompressedTexture(u32 width, u32 height, u32* output, u32 addr, u32 addrAux, u32 palAddr, GPU& gpu) { // we process a whole block at the time for (int y = 0; y < height / 4; y++) { for (int x = 0; x < width / 4; x++) { - u32 data = ((u32*)texData)[x + y * (width / 4)]; - u16 auxData = ((u16*)texAuxData)[x + y * (width / 4)]; + u32 data = gpu.ReadVRAMFlat_Texture(addr + (x + y * (width / 4))*4); + u16 auxData = gpu.ReadVRAMFlat_Texture(addrAux + (x + y * (width / 4))*2); - u32 paletteOffset = auxData & 0x3FFF; - u16 color0 = palData[paletteOffset*2] | 0x8000; - u16 color1 = palData[paletteOffset*2+1] | 0x8000; - u16 color2, color3; + u32 paletteOffset = palAddr + (auxData & 0x3FFF) * 4; + u16 color0 = gpu.ReadVRAMFlat_TexPal(paletteOffset) | 0x8000; + u16 color1 = gpu.ReadVRAMFlat_TexPal(paletteOffset+2) | 0x8000; + u16 color2 = gpu.ReadVRAMFlat_TexPal(paletteOffset+4) | 0x8000; + u16 color3 = gpu.ReadVRAMFlat_TexPal(paletteOffset+6) | 0x8000; switch ((auxData >> 14) & 0x3) { case 0: - color2 = palData[paletteOffset*2+2] | 0x8000; color3 = 0; break; case 1: @@ -137,8 +137,6 @@ void ConvertCompressedTexture(u32 width, u32 height, u32* output, u8* texData, u color3 = 0; break; case 2: - color2 = palData[paletteOffset*2+2] | 0x8000; - color3 = palData[paletteOffset*2+3] | 0x8000; break; case 3: { @@ -179,7 +177,8 @@ void ConvertCompressedTexture(u32 width, u32 height, u32* output, u8* texData, u { for (int i = 0; i < 4; i++) { - u16 color = (packed >> 16 * (data >> 2 * (i + j * 4))) & 0xFFFF; + u32 colorIdx = 16 * ((data >> 2 * (i + j * 4)) & 0x3); + u16 color = (packed >> colorIdx) & 0xFFFF; u32 res; switch (outputFmt) { @@ -197,20 +196,20 @@ void ConvertCompressedTexture(u32 width, u32 height, u32* output, u8* texData, u } } -template void ConvertCompressedTexture(u32, u32, u32*, u8*, u8*, u16*); +template void ConvertCompressedTexture(u32, u32, u32*, u32, u32, u32, GPU&); template -void ConvertAXIYTexture(u32 width, u32 height, u32* output, u8* texData, u16* palData) +void ConvertAXIYTexture(u32 width, u32 height, u32* output, u32 addr, u32 palAddr, GPU& gpu) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - u8 val = texData[x + y * width]; + u8 val = gpu.ReadVRAMFlat_Texture(addr + x + y * width); u32 idx = val & ((1 << Y) - 1); - u16 color = palData[idx]; + u16 color = gpu.ReadVRAMFlat_TexPal(palAddr + idx * 2); u32 alpha = (val >> Y) & ((1 << X) - 1); if (X != 5) alpha = alpha * 4 + alpha / 2; @@ -228,22 +227,24 @@ void ConvertAXIYTexture(u32 width, u32 height, u32* output, u8* texData, u16* pa } } -template void ConvertAXIYTexture(u32, u32, u32*, u8*, u16*); -template void ConvertAXIYTexture(u32, u32, u32*, u8*, u16*); +template void ConvertAXIYTexture(u32, u32, u32*, u32, u32, GPU&); +template void ConvertAXIYTexture(u32, u32, u32*, u32, u32, GPU&); template -void ConvertNColorsTexture(u32 width, u32 height, u32* output, u8* texData, u16* palData, bool color0Transparent) +void ConvertNColorsTexture(u32 width, u32 height, u32* output, u32 addr, u32 palAddr, bool color0Transparent, GPU& gpu) { for (int y = 0; y < height; y++) { - for (int x = 0; x < width / (8 / colorBits); x++) + for (int x = 0; x < width / (16 / colorBits); x++) { - u8 val = texData[x + y * (width / (8 / colorBits))]; + // smallest possible row is 8 pixels with 2bpp => fits in u16 + u16 val = gpu.ReadVRAMFlat_Texture(addr + 2 * (x + y * (width / (16 / colorBits)))); - for (int i = 0; i < 8 / colorBits; i++) + for (int i = 0; i < 16 / colorBits; i++) { - u32 index = (val >> (i * colorBits)) & ((1 << colorBits) - 1); - u16 color = palData[index]; + u32 index = val & ((1 << colorBits) - 1); + val >>= colorBits; + u16 color = gpu.ReadVRAMFlat_TexPal(palAddr + index * 2); bool transparent = color0Transparent && index == 0; u32 res; @@ -256,14 +257,14 @@ void ConvertNColorsTexture(u32 width, u32 height, u32* output, u8* texData, u16* case outputFmt_BGRA8: res = ConvertRGB5ToBGR8(color) | (transparent ? 0 : 0xFF000000); break; } - output[x * (8 / colorBits) + y * width + i] = res; + output[x * (16 / colorBits) + y * width + i] = res; } } } } -template void ConvertNColorsTexture(u32, u32, u32*, u8*, u16*, bool); -template void ConvertNColorsTexture(u32, u32, u32*, u8*, u16*, bool); -template void ConvertNColorsTexture(u32, u32, u32*, u8*, u16*, bool); +template void ConvertNColorsTexture(u32, u32, u32*, u32, u32, bool, GPU&); +template void ConvertNColorsTexture(u32, u32, u32*, u32, u32, bool, GPU&); +template void ConvertNColorsTexture(u32, u32, u32*, u32, u32, bool, GPU&); } \ No newline at end of file diff --git a/src/GPU3D_Texcache.h b/src/GPU3D_Texcache.h index 214c6254..f2cd6416 100644 --- a/src/GPU3D_Texcache.h +++ b/src/GPU3D_Texcache.h @@ -32,13 +32,13 @@ enum }; template -void ConvertBitmapTexture(u32 width, u32 height, u32* output, u8* texData); +void ConvertBitmapTexture(u32 width, u32 height, u32* output, u32 addr, GPU& gpu); template -void ConvertCompressedTexture(u32 width, u32 height, u32* output, u8* texData, u8* texAuxData, u16* palData); +void ConvertCompressedTexture(u32 width, u32 height, u32* output, u32 addr, u32 addrAux, u32 palAddr, GPU& gpu); template -void ConvertAXIYTexture(u32 width, u32 height, u32* output, u8* texData, u16* palData); +void ConvertAXIYTexture(u32 width, u32 height, u32* output, u32 addr, u32 palAddr, GPU& gpu); template -void ConvertNColorsTexture(u32 width, u32 height, u32* output, u8* texData, u16* palData, bool color0Transparent); +void ConvertNColorsTexture(u32 width, u32 height, u32* output, u32 addr, u32 palAddr, bool color0Transparent, GPU& gpu); template class Texcache @@ -48,6 +48,50 @@ public: : TexLoader(texloader) // probably better if this would be a move constructor??? {} + u64 MaskedHash(u8* vram, u32 vramSize, u32 addr, u32 size) + { + u64 hash = 0; + + while (size > 0) + { + u32 pieceSize; + if (addr + size > vramSize) + // wraps around, only do the part inside + pieceSize = vramSize - addr; + else + // fits completely inside + pieceSize = size; + + hash = XXH64(&vram[addr], pieceSize, hash); + + addr += pieceSize; + addr &= (vramSize - 1); + assert(size >= pieceSize); + size -= pieceSize; + } + + return hash; + } + + bool CheckInvalid(u32 start, u32 size, u64 oldHash, u64* dirty, u8* vram, u32 vramSize) + { + u32 startBit = start / VRAMDirtyGranularity; + u32 bitsCount = ((start + size + VRAMDirtyGranularity - 1) / VRAMDirtyGranularity) - startBit; + + u32 startEntry = startBit >> 6; + u64 entriesCount = ((startBit + bitsCount + 0x3F) >> 6) - startEntry; + for (u32 j = startEntry; j < startEntry + entriesCount; j++) + { + if (GetRangedBitMask(j, startBit, bitsCount) & dirty[j & ((vramSize / VRAMDirtyGranularity)-1)]) + { + if (MaskedHash(vram, vramSize, start, size) != oldHash) + return true; + } + } + + return false; + } + bool Update(GPU& gpu) { auto textureDirty = gpu.VRAMDirty_Texture.DeriveState(gpu.VRAMMap_Texture, gpu); @@ -66,40 +110,21 @@ public: { for (u32 i = 0; i < 2; i++) { - u32 startBit = entry.TextureRAMStart[i] / VRAMDirtyGranularity; - u32 bitsCount = ((entry.TextureRAMStart[i] + entry.TextureRAMSize[i] + VRAMDirtyGranularity - 1) / VRAMDirtyGranularity) - startBit; - - u32 startEntry = startBit >> 6; - u64 entriesCount = ((startBit + bitsCount + 0x3F) >> 6) - startEntry; - for (u32 j = startEntry; j < startEntry + entriesCount; j++) - { - if (GetRangedBitMask(j, startBit, bitsCount) & textureDirty.Data[j]) - { - u64 newTexHash = XXH3_64bits(&gpu.VRAMFlat_Texture[entry.TextureRAMStart[i]], entry.TextureRAMSize[i]); - - if (newTexHash != entry.TextureHash[i]) - goto invalidate; - } - } + if (CheckInvalid(entry.TextureRAMStart[i], entry.TextureRAMSize[i], + entry.TextureHash[i], + textureDirty.Data, + gpu.VRAMFlat_Texture, sizeof(gpu.VRAMFlat_Texture))) + goto invalidate; } } if (texPalChanged && entry.TexPalSize > 0) { - u32 startBit = entry.TexPalStart / VRAMDirtyGranularity; - u32 bitsCount = ((entry.TexPalStart + entry.TexPalSize + VRAMDirtyGranularity - 1) / VRAMDirtyGranularity) - startBit; - - u32 startEntry = startBit >> 6; - u64 entriesCount = ((startBit + bitsCount + 0x3F) >> 6) - startEntry; - for (u32 j = startEntry; j < startEntry + entriesCount; j++) - { - if (GetRangedBitMask(j, startBit, bitsCount) & texPalDirty.Data[j]) - { - u64 newPalHash = XXH3_64bits(&gpu.VRAMFlat_TexPal[entry.TexPalStart], entry.TexPalSize); - if (newPalHash != entry.TexPalHash) - goto invalidate; - } - } + if (CheckInvalid(entry.TexPalStart, entry.TexPalSize, + entry.TexPalHash, + texPalDirty.Data, + gpu.VRAMFlat_TexPal, sizeof(gpu.VRAMFlat_TexPal))) + goto invalidate; } it++; @@ -163,17 +188,13 @@ public: { entry.TextureRAMSize[0] = width*height*2; - ConvertBitmapTexture(width, height, DecodingBuffer, &gpu.VRAMFlat_Texture[addr]); + ConvertBitmapTexture(width, height, DecodingBuffer, addr, gpu); } else if (fmt == 5) { - u8* texData = &gpu.VRAMFlat_Texture[addr]; u32 slot1addr = 0x20000 + ((addr & 0x1FFFC) >> 1); if (addr >= 0x40000) slot1addr += 0x10000; - u8* texAuxData = &gpu.VRAMFlat_Texture[slot1addr]; - - u16* palData = (u16*)(gpu.VRAMFlat_TexPal + palBase*16); entry.TextureRAMSize[0] = width*height/16*4; entry.TextureRAMStart[1] = slot1addr; @@ -181,7 +202,7 @@ public: entry.TexPalStart = palBase*16; entry.TexPalSize = 0x10000; - ConvertCompressedTexture(width, height, DecodingBuffer, texData, texAuxData, palData); + ConvertCompressedTexture(width, height, DecodingBuffer, addr, slot1addr, entry.TexPalStart, gpu); } else { @@ -204,30 +225,29 @@ public: entry.TexPalStart = palAddr; entry.TexPalSize = numPalEntries*2; - u8* texData = &gpu.VRAMFlat_Texture[addr]; - u16* palData = (u16*)(gpu.VRAMFlat_TexPal + palAddr); - //assert(entry.TexPalStart+entry.TexPalSize <= 128*1024*1024); bool color0Transparent = texParam & (1 << 29); switch (fmt) { - case 1: ConvertAXIYTexture(width, height, DecodingBuffer, texData, palData); break; - case 6: ConvertAXIYTexture(width, height, DecodingBuffer, texData, palData); break; - case 2: ConvertNColorsTexture(width, height, DecodingBuffer, texData, palData, color0Transparent); break; - case 3: ConvertNColorsTexture(width, height, DecodingBuffer, texData, palData, color0Transparent); break; - case 4: ConvertNColorsTexture(width, height, DecodingBuffer, texData, palData, color0Transparent); break; + case 1: ConvertAXIYTexture(width, height, DecodingBuffer, addr, palAddr, gpu); break; + case 6: ConvertAXIYTexture(width, height, DecodingBuffer, addr, palAddr, gpu); break; + case 2: ConvertNColorsTexture(width, height, DecodingBuffer, addr, palAddr, color0Transparent, gpu); break; + case 3: ConvertNColorsTexture(width, height, DecodingBuffer, addr, palAddr, color0Transparent, gpu); break; + case 4: ConvertNColorsTexture(width, height, DecodingBuffer, addr, palAddr, color0Transparent, gpu); break; } } for (int i = 0; i < 2; i++) { if (entry.TextureRAMSize[i]) - entry.TextureHash[i] = XXH3_64bits(&gpu.VRAMFlat_Texture[entry.TextureRAMStart[i]], entry.TextureRAMSize[i]); + entry.TextureHash[i] = MaskedHash(gpu.VRAMFlat_Texture, sizeof(gpu.VRAMFlat_Texture), + entry.TextureRAMStart[i], entry.TextureRAMSize[i]); } if (entry.TexPalSize) - entry.TexPalHash = XXH3_64bits(&gpu.VRAMFlat_TexPal[entry.TexPalStart], entry.TexPalSize); + entry.TexPalHash = MaskedHash(gpu.VRAMFlat_TexPal, sizeof(gpu.VRAMFlat_TexPal), + entry.TexPalStart, entry.TexPalSize); auto& texArrays = TexArrays[widthLog2][heightLog2]; auto& freeTextures = FreeTextures[widthLog2][heightLog2]; From dfd633899286d44b6c9751564d2d47ab9c5c1415 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Mon, 28 Oct 2024 01:28:52 +0100 Subject: [PATCH 46/63] it shouldn't be ifdef... How did that even compile here? --- src/frontend/qt_sdl/Window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 5c599e70..0af72ef6 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -265,7 +265,7 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : setAcceptDrops(true); setFocusPolicy(Qt::ClickFocus); -#ifdef QT_VERSION_MAJOR == 6 && WIN32 +#if QT_VERSION_MAJOR == 6 && WIN32 // The "windows11" theme has pretty massive padding around menubar items, this makes Config and Help not fit in a window at 1x screen sizing // So let's reduce the padding a bit. if (QApplication::style()->name() == "windows11") From 7a4255b7327dc9ccdfa471b5acbc4ece7184b470 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 29 Oct 2024 14:18:57 +0100 Subject: [PATCH 47/63] fix LDM bugs --- src/ARMInterpreter_LoadStore.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ARMInterpreter_LoadStore.cpp b/src/ARMInterpreter_LoadStore.cpp index cb646df5..f7c24312 100644 --- a/src/ARMInterpreter_LoadStore.cpp +++ b/src/ARMInterpreter_LoadStore.cpp @@ -430,9 +430,9 @@ void A_LDM(ARM* cpu) } } + u32 pc = 0; if (cpu->CurInstr & (1<<15)) { - u32 pc; if (preinc) base += 4; if (first) cpu->DataRead32 (base, &pc); else cpu->DataRead32S(base, &pc); @@ -440,13 +440,8 @@ void A_LDM(ARM* cpu) if (cpu->Num == 1) pc &= ~0x1; - - cpu->JumpTo(pc, cpu->CurInstr & (1<<22)); } - if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15))) - cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true); - if (cpu->CurInstr & (1<<21)) { // post writeback @@ -466,6 +461,12 @@ void A_LDM(ARM* cpu) cpu->R[baseid] = wbbase; } + if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15))) + cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true); + + if (cpu->CurInstr & (1<<15)) + cpu->JumpTo(pc, cpu->CurInstr & (1<<22)); + cpu->AddCycles_CDI(); } From 3877a8e46bf92a9c24eea21c245856c09e87ec7b Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Tue, 29 Oct 2024 20:10:32 -0400 Subject: [PATCH 48/63] Allow `CartGameSolarSensor::LightLevel` to be set explicitly (#2179) * Allow `CartGameSolarSensor::LightLevel` to be set explicitly * Add `CartGameSolarSensor::GetLightLevel` * Update GBACart.cpp --------- Co-authored-by: Kemal Afzal --- src/GBACart.cpp | 5 +++++ src/GBACart.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 4fd42894..a62aca6b 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -582,6 +582,11 @@ int CartGameSolarSensor::SetInput(int num, bool pressed) return -1; } +void CartGameSolarSensor::SetLightLevel(u8 level) noexcept +{ + LightLevel = std::clamp(level, 0, 10); +} + void CartGameSolarSensor::ProcessGPIO() { if (GPIO.data & 4) return; // Boktai chip select diff --git a/src/GBACart.h b/src/GBACart.h index f6fb95dd..726a234d 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -158,6 +158,8 @@ public: void DoSavestate(Savestate* file) override; int SetInput(int num, bool pressed) override; + void SetLightLevel(u8 level) noexcept; + [[nodiscard]] u8 GetLightLevel() const noexcept { return LightLevel; } protected: void ProcessGPIO() override; From 4ba8f330c4d5cd6c25f6acddf2866b40e698d35f Mon Sep 17 00:00:00 2001 From: Jakly <102590697+Jaklyy@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:40:33 -0400 Subject: [PATCH 49/63] fix framerate target not adjusting with vcount writes (#2181) --- src/frontend/qt_sdl/EmuInstance.cpp | 12 ++++++------ src/frontend/qt_sdl/EmuThread.cpp | 21 ++++++++++++--------- src/frontend/qt_sdl/Window.cpp | 6 +++--- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 90116692..e9550f8c 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -93,25 +93,25 @@ EmuInstance::EmuInstance(int inst) : deleting(false), if (val == 0.0) { Platform::Log(Platform::LogLevel::Error, "Target FPS in config invalid\n"); - targetFPS = 1.0 / 60.0; + targetFPS = 60.0; } - else targetFPS = 1.0 / val; + else targetFPS = val; val = globalCfg.GetDouble("FastForwardFPS"); if (val == 0.0) { Platform::Log(Platform::LogLevel::Error, "Fast-Forward FPS in config invalid\n"); - fastForwardFPS = 1.0 / 60.0; + fastForwardFPS = 60.0; } - else fastForwardFPS = 1.0 / val; + else fastForwardFPS = val; val = globalCfg.GetDouble("SlowmoFPS"); if (val == 0.0) { Platform::Log(Platform::LogLevel::Error, "Slow-Mo FPS in config invalid\n"); - slowmoFPS = 1.0 / 60.0; + slowmoFPS = 60.0; } - else slowmoFPS = 1.0 / val; + else slowmoFPS = val; doAudioSync = globalCfg.GetBool("AudioSync"); diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 3371a03b..f4112cd3 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -359,7 +359,7 @@ void EmuThread::run() if (slowmo) emuInstance->curFPS = emuInstance->slowmoFPS; else if (fastforward) emuInstance->curFPS = emuInstance->fastForwardFPS; - else if (!emuInstance->doLimitFPS) emuInstance->curFPS = 1.0 / 1000.0; + else if (!emuInstance->doLimitFPS) emuInstance->curFPS = 1000.0; else emuInstance->curFPS = emuInstance->targetFPS; if (emuInstance->audioDSiVolumeSync && emuInstance->nds->ConsoleType == 1) @@ -378,16 +378,18 @@ void EmuThread::run() if (emuInstance->doAudioSync && !(fastforward || slowmo)) emuInstance->audioSync(); - double frametimeStep = nlines / (60.0 * 263.0); + double frametimeStep = nlines / (emuInstance->curFPS * 263.0); + + if (frametimeStep < 0.001) frametimeStep = 0.001; { double curtime = SDL_GetPerformanceCounter() * perfCountsSec; - frameLimitError += emuInstance->curFPS - (curtime - lastTime); - if (frameLimitError < -emuInstance->curFPS) - frameLimitError = -emuInstance->curFPS; - if (frameLimitError > emuInstance->curFPS) - frameLimitError = emuInstance->curFPS; + frameLimitError += frametimeStep - (curtime - lastTime); + if (frameLimitError < -frametimeStep) + frameLimitError = -frametimeStep; + if (frameLimitError > frametimeStep) + frameLimitError = frametimeStep; if (round(frameLimitError * 1000.0) > 0.0) { @@ -415,10 +417,11 @@ void EmuThread::run() winUpdateFreq = fps / (u32)round(fpstarget); if (winUpdateFreq < 1) winUpdateFreq = 1; - + + double actualfps = (59.8261 * 263.0) / nlines; int inst = emuInstance->instanceID; if (inst == 0) - sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget); + sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, actualfps); else sprintf(melontitle, "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1); changeWindowTitle(melontitle); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 0af72ef6..1d207949 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1942,9 +1942,9 @@ void MainWindow::onOpenInterfaceSettings() void MainWindow::onUpdateInterfaceSettings() { pauseOnLostFocus = globalCfg.GetBool("PauseLostFocus"); - emuInstance->targetFPS = 1.0 / globalCfg.GetDouble("TargetFPS"); - emuInstance->fastForwardFPS = 1.0 / globalCfg.GetDouble("FastForwardFPS"); - emuInstance->slowmoFPS = 1.0 / globalCfg.GetDouble("SlowmoFPS"); + emuInstance->targetFPS = globalCfg.GetDouble("TargetFPS"); + emuInstance->fastForwardFPS = globalCfg.GetDouble("FastForwardFPS"); + emuInstance->slowmoFPS = globalCfg.GetDouble("SlowmoFPS"); panel->setMouseHide(globalCfg.GetBool("MouseHide"), globalCfg.GetInt("MouseHideSeconds")*1000); } From 6dc396741fde1c921ece2d3ca97795469d35081b Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 30 Oct 2024 23:39:17 +0100 Subject: [PATCH 50/63] make sure it doesn't crash if you click the window while nothing is loaded --- src/frontend/qt_sdl/Screen.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index 37c35a65..367d679d 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -222,6 +222,7 @@ void ScreenPanel::resizeEvent(QResizeEvent* event) void ScreenPanel::mousePressEvent(QMouseEvent* event) { event->accept(); + if (!emuInstance->emuIsActive()) { touching = false; return; } if (event->button() != Qt::LeftButton) return; int x = event->pos().x(); @@ -238,6 +239,7 @@ void ScreenPanel::mousePressEvent(QMouseEvent* event) void ScreenPanel::mouseReleaseEvent(QMouseEvent* event) { event->accept(); + if (!emuInstance->emuIsActive()) { touching = false; return; } if (event->button() != Qt::LeftButton) return; if (touching) @@ -254,6 +256,7 @@ void ScreenPanel::mouseMoveEvent(QMouseEvent* event) showCursor(); + if (!emuInstance->emuIsActive()) return; //if (!(event->buttons() & Qt::LeftButton)) return; if (!touching) return; @@ -270,6 +273,7 @@ void ScreenPanel::mouseMoveEvent(QMouseEvent* event) void ScreenPanel::tabletEvent(QTabletEvent* event) { event->accept(); + if (!emuInstance->emuIsActive()) { touching = false; return; } switch(event->type()) { @@ -313,6 +317,7 @@ void ScreenPanel::touchEvent(QTouchEvent* event) #endif event->accept(); + if (!emuInstance->emuIsActive()) { touching = false; return; } switch(event->type()) { From 540ebe7256efa2b9a9605c94bbbe11a5f529d8cc Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 31 Oct 2024 13:57:53 +0100 Subject: [PATCH 51/63] disable 'Enable cheats' until something is loaded, to make it consistent with 'Manage cheats' --- src/frontend/qt_sdl/Window.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 1d207949..430f0d6e 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -726,6 +726,7 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : actDateTime->setEnabled(true); actPowerManagement->setEnabled(false); + actEnableCheats->setEnabled(false); actSetupCheats->setEnabled(false); actTitleManager->setEnabled(!globalCfg.GetString("DSi.NANDPath").empty()); @@ -1274,6 +1275,7 @@ void MainWindow::updateCartInserted(bool gba) win->actCurrentCart->setText(label); win->actEjectCart->setEnabled(inserted); win->actImportSavefile->setEnabled(inserted); + win->actEnableCheats->setEnabled(inserted); win->actSetupCheats->setEnabled(inserted); win->actROMInfo->setEnabled(inserted); win->actRAMInfo->setEnabled(inserted); From 6c6cefad6c4add22cd01ca665301219fd6b2c0b0 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 31 Oct 2024 19:26:41 +0100 Subject: [PATCH 52/63] add splashscreen --- res/melon.qrc | 1 + src/frontend/qt_sdl/OSD_shaders.h | 5 +- src/frontend/qt_sdl/Screen.cpp | 185 +++++++++++++++++++++++++++++- src/frontend/qt_sdl/Screen.h | 16 ++- 4 files changed, 200 insertions(+), 7 deletions(-) diff --git a/res/melon.qrc b/res/melon.qrc index 38915bbf..3c5824d6 100644 --- a/res/melon.qrc +++ b/res/melon.qrc @@ -2,5 +2,6 @@ icon/melon_256x256.png + melon.svg diff --git a/src/frontend/qt_sdl/OSD_shaders.h b/src/frontend/qt_sdl/OSD_shaders.h index a2a6af70..253fcdc5 100644 --- a/src/frontend/qt_sdl/OSD_shaders.h +++ b/src/frontend/qt_sdl/OSD_shaders.h @@ -26,6 +26,7 @@ uniform vec2 uScreenSize; uniform ivec2 uOSDPos; uniform ivec2 uOSDSize; uniform float uScaleFactor; +uniform float uTexScale; in vec2 vPosition; @@ -35,8 +36,8 @@ void main() { vec4 fpos; - vec2 osdpos = (vPosition * vec2(uOSDSize * uScaleFactor)); - fTexcoord = osdpos; + vec2 osdpos = (vPosition * vec2(uOSDSize)); + fTexcoord = osdpos * uTexScale; osdpos += uOSDPos; fpos.xy = ((osdpos * 2.0) / uScreenSize * uScaleFactor) - 1.0; diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index 367d679d..0d05a065 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -46,11 +46,13 @@ #include "main_shaders.h" #include "OSD_shaders.h" #include "font.h" +#include "version.h" using namespace melonDS; const u32 kOSDMargin = 6; +const int kLogoWidth = 192; ScreenPanel::ScreenPanel(QWidget* parent) : QWidget(parent) @@ -80,6 +82,29 @@ ScreenPanel::ScreenPanel(QWidget* parent) : QWidget(parent) loadConfig(); setFilter(mainWindow->getWindowConfig().GetBool("ScreenFilter")); + + splashLogo = QPixmap(":/melon-logo"); + + strncpy(splashText[0].text, "File->Open ROM...", 256); + splashText[0].id = 0x80000000; + splashText[0].color = 0; + splashText[0].rendered = false; + splashText[0].rainbowstart = -1; + + strncpy(splashText[1].text, "to get started", 256); + splashText[1].id = 0x80000001; + splashText[1].color = 0; + splashText[1].rendered = false; + splashText[1].rainbowstart = -1; + + std::string url = MELONDS_URL; + int urlpos = url.find("://"); + urlpos = (urlpos == std::string::npos) ? 0 : urlpos+3; + strncpy(splashText[2].text, url.c_str() + urlpos, 256); + splashText[2].id = 0x80000002; + splashText[2].color = 0; + splashText[2].rendered = false; + splashText[2].rainbowstart = -1; } ScreenPanel::~ScreenPanel() @@ -150,6 +175,8 @@ void ScreenPanel::setupScreenLayout() aspectBot); numScreens = layout.GetScreenTransforms(screenMatrix[0], screenKind); + + calcSplashLayout(); } QSize ScreenPanel::screenGetMinSize(int factor = 1) @@ -483,8 +510,14 @@ void ScreenPanel::osdRenderItem(OSDItem* item) u32 color = item->color; bool rainbow = (color == 0); - u32 ticks = (u32)QDateTime::currentMSecsSinceEpoch(); - u32 rainbowinc = ((text[0] * 17) + (ticks * 13)) % 600; + u32 rainbowinc; + if (item->rainbowstart == -1) + { + u32 ticks = (u32) QDateTime::currentMSecsSinceEpoch(); + rainbowinc = ((text[0] * 17) + (ticks * 13)) % 600; + } + else + rainbowinc = (u32)item->rainbowstart; color |= 0xFF000000; const u32 shadow = 0xE0000000; @@ -582,6 +615,8 @@ void ScreenPanel::osdRenderItem(OSDItem* item) bitmap[(y * w) + x] = shadow; } } + + item->rainbowend = (int)rainbowinc; } void ScreenPanel::osdDeleteItem(OSDItem* item) @@ -603,11 +638,12 @@ void ScreenPanel::osdAddMessage(unsigned int color, const char* text) OSDItem item; - item.id = osdID++; + item.id = (osdID++) & 0x7FFFFFFF; item.timestamp = QDateTime::currentMSecsSinceEpoch(); strncpy(item.text, text, 255); item.text[255] = '\0'; item.color = color; item.rendered = false; + item.rainbowstart = -1; osdItems.push_back(item); @@ -641,6 +677,73 @@ void ScreenPanel::osdUpdate() it++; } + // render splashscreen text items if needed + + int rainbowinc = -1; + bool needrecalc = false; + + for (int i = 0; i < 3; i++) + { + if (!splashText[i].rendered) + { + splashText[i].rainbowstart = rainbowinc; + osdRenderItem(&splashText[i]); + splashText[i].rendered = true; + rainbowinc = splashText[i].rainbowend; + needrecalc = true; + } + } + + osdMutex.unlock(); + + if (needrecalc) + calcSplashLayout(); +} + +void ScreenPanel::calcSplashLayout() +{ + if (!splashText[0].rendered) + return; + + osdMutex.lock(); + + int w = width(); + int h = height(); + + int xlogo = (w - kLogoWidth) / 2; + int ylogo = (h - kLogoWidth) / 2; + + // top text + int totalwidth = splashText[0].bitmap.width() + 6 + splashText[1].bitmap.width(); + if (totalwidth >= w) + { + // stacked vertically + splashPos[0].setX((width() - splashText[0].bitmap.width()) / 2); + splashPos[1].setX((width() - splashText[1].bitmap.width()) / 2); + + int basey = ylogo / 2; + splashPos[0].setY(basey - splashText[0].bitmap.height() - 1); + splashPos[1].setY(basey + 1); + } + else + { + // horizontal + splashPos[0].setX((w - totalwidth) / 2); + splashPos[1].setX(splashPos[0].x() + splashText[0].bitmap.width() + 6); + + int basey = (ylogo - splashText[0].bitmap.height()) / 2; + splashPos[0].setY(basey); + splashPos[1].setY(basey); + } + + // bottom text + splashPos[2].setX((w - splashText[2].bitmap.width()) / 2); + splashPos[2].setY(ylogo + kLogoWidth + ((ylogo - splashText[2].bitmap.height()) / 2)); + + // logo + splashPos[3].setX(xlogo); + splashPos[3].setY(ylogo); + osdMutex.unlock(); } @@ -708,6 +811,20 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) } osdUpdate(); + + if (!emuThread->emuIsActive()) + { + // splashscreen + osdMutex.lock(); + + painter.drawPixmap(QRect(splashPos[3], QSize(kLogoWidth, kLogoWidth)), splashLogo); + + for (int i = 0; i < 3; i++) + painter.drawImage(splashPos[i], splashText[i].bitmap); + + osdMutex.unlock(); + } + if (osdEnabled) { osdMutex.lock(); @@ -862,6 +979,7 @@ void ScreenPanelGL::initOpenGL() osdPosULoc = glGetUniformLocation(osdShader, "uOSDPos"); osdSizeULoc = glGetUniformLocation(osdShader, "uOSDSize"); osdScaleFactorULoc = glGetUniformLocation(osdShader, "uScaleFactor"); + osdTexScaleULoc = glGetUniformLocation(osdShader, "uTexScale"); const float osdvertices[6*2] = { @@ -882,6 +1000,18 @@ void ScreenPanelGL::initOpenGL() glEnableVertexAttribArray(0); // position glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0)); + // splash logo texture + QImage logo = splashLogo.scaled(kLogoWidth*2, kLogoWidth*2).toImage(); + GLuint tex; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, logo.width(), logo.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, logo.bits()); + logoTexture = tex; + transferLayout(); glInited = true; } @@ -908,6 +1038,8 @@ void ScreenPanelGL::deinitOpenGL() glDeleteVertexArrays(1, &osdVertexArray); glDeleteBuffers(1, &osdVertexBuffer); + glDeleteTextures(1, &logoTexture); + glDeleteProgram(osdShader); @@ -1023,6 +1155,52 @@ void ScreenPanelGL::drawScreenGL() } osdUpdate(); + + if (!emuThread->emuIsActive()) + { + // splashscreen + osdMutex.lock(); + + glUseProgram(osdShader); + + glUniform2f(osdScreenSizeULoc, w, h); + glUniform1f(osdScaleFactorULoc, factor); + glUniform1f(osdTexScaleULoc, 2.0); + + glBindBuffer(GL_ARRAY_BUFFER, osdVertexBuffer); + glBindVertexArray(osdVertexArray); + + glActiveTexture(GL_TEXTURE0); + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + glBindTexture(GL_TEXTURE_2D, logoTexture); + glUniform2i(osdPosULoc, splashPos[3].x(), splashPos[3].y()); + glUniform2i(osdSizeULoc, kLogoWidth, kLogoWidth); + glDrawArrays(GL_TRIANGLES, 0, 2*3); + + glUniform1f(osdTexScaleULoc, 1.0); + + for (int i = 0; i < 3; i++) + { + OSDItem& item = splashText[i]; + + if (!osdTextures.count(item.id)) + continue; + + glBindTexture(GL_TEXTURE_2D, osdTextures[item.id]); + glUniform2i(osdPosULoc, splashPos[i].x(), splashPos[i].y()); + glUniform2i(osdSizeULoc, item.bitmap.width(), item.bitmap.height()); + glDrawArrays(GL_TRIANGLES, 0, 2*3); + } + + glDisable(GL_BLEND); + glUseProgram(0); + + osdMutex.unlock(); + } + if (osdEnabled) { osdMutex.lock(); @@ -1033,6 +1211,7 @@ void ScreenPanelGL::drawScreenGL() glUniform2f(osdScreenSizeULoc, w, h); glUniform1f(osdScaleFactorULoc, factor); + glUniform1f(osdTexScaleULoc, 1.0); glBindBuffer(GL_ARRAY_BUFFER, osdVertexBuffer); glBindVertexArray(osdVertexArray); diff --git a/src/frontend/qt_sdl/Screen.h b/src/frontend/qt_sdl/Screen.h index c17626bd..a988815e 100644 --- a/src/frontend/qt_sdl/Screen.h +++ b/src/frontend/qt_sdl/Screen.h @@ -110,6 +110,9 @@ protected: bool rendered; QImage bitmap; + + int rainbowstart; + int rainbowend; }; QMutex osdMutex; @@ -117,6 +120,10 @@ protected: unsigned int osdID; std::deque osdItems; + QPixmap splashLogo; + OSDItem splashText[3]; + QPoint splashPos[4]; + void loadConfig(); virtual void setupScreenLayout(); @@ -141,6 +148,8 @@ protected: virtual void osdDeleteItem(OSDItem* item); void osdUpdate(); + + void calcSplashLayout(); }; @@ -202,7 +211,7 @@ private: GLuint screenVertexBuffer, screenVertexArray; GLuint screenTexture; GLuint screenShaderProgram; - GLuint screenShaderTransformULoc, screenShaderScreenSizeULoc; + GLint screenShaderTransformULoc, screenShaderScreenSizeULoc; QMutex screenSettingsLock; WindowInfo windowInfo; @@ -211,11 +220,14 @@ private: GLuint osdShader; GLint osdScreenSizeULoc, osdPosULoc, osdSizeULoc; - GLfloat osdScaleFactorULoc; + GLint osdScaleFactorULoc; + GLint osdTexScaleULoc; GLuint osdVertexArray; GLuint osdVertexBuffer; std::map osdTextures; + GLuint logoTexture; + void osdRenderItem(OSDItem* item) override; void osdDeleteItem(OSDItem* item) override; }; From 1b8daa046531df238e7343c1cbb2d9cf5129e739 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 31 Oct 2024 20:33:57 +0100 Subject: [PATCH 53/63] fix up cheat toggle --- src/frontend/qt_sdl/EmuInstance.cpp | 2 +- src/frontend/qt_sdl/EmuThread.cpp | 10 ++++++++++ src/frontend/qt_sdl/EmuThread.h | 4 ++++ src/frontend/qt_sdl/Window.cpp | 7 ++++++- 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index e9550f8c..9114a141 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -1161,7 +1161,7 @@ std::optional EmuInstance::loadSDCard(const string& key) noexcept void EmuInstance::enableCheats(bool enable) { cheatsOn = enable; - if (cheatFile) + if (cheatsOn && cheatFile) nds->AREngine.Cheats = cheatFile->GetCodes(); else nds->AREngine.Cheats.clear(); diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index f4112cd3..f4ce8c94 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -648,6 +648,10 @@ void EmuThread::handleMessages() msgResult = 1; } break; + + case msg_EnableCheats: + emuInstance->enableCheats(msg.param.value()); + break; } msgSemaphore.release(); @@ -815,6 +819,12 @@ int EmuThread::importSavefile(const QString& filename) return msgResult; } +void EmuThread::enableCheats(bool enable) +{ + sendMessage({.type = msg_EnableCheats, .param = enable}); + waitMessage(); +} + void EmuThread::updateRenderer() { if (videoRenderer != lastVideoRenderer) diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index 19e1a3a6..18bff6dd 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -83,6 +83,8 @@ public: msg_UndoStateLoad, msg_ImportSavefile, + + msg_EnableCheats, }; struct Message @@ -124,6 +126,8 @@ public: int importSavefile(const QString& filename); + void enableCheats(bool enable); + bool emuIsRunning(); bool emuIsActive(); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 430f0d6e..aed8076a 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1655,7 +1655,12 @@ void MainWindow::onOpenPowerManagement() void MainWindow::onEnableCheats(bool checked) { localCfg.SetBool("EnableCheats", checked); - emuInstance->enableCheats(checked); + emuThread->enableCheats(checked); + + emuInstance->doOnAllWindows([=](MainWindow* win) + { + win->actEnableCheats->setChecked(checked); + }, windowID); } void MainWindow::onSetupCheats() From f3bd58f75ebc87306f9c0bfba9a277f1b85ddbd8 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 31 Oct 2024 22:10:25 +0100 Subject: [PATCH 54/63] do touchscreen input more properly --- src/frontend/qt_sdl/EmuInstance.h | 6 ++++++ src/frontend/qt_sdl/EmuInstanceInput.cpp | 16 ++++++++++++++++ src/frontend/qt_sdl/EmuThread.cpp | 5 +++++ src/frontend/qt_sdl/Screen.cpp | 21 +++++++-------------- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 658247f0..9ab12d93 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -147,6 +147,9 @@ public: int getJoystickID() { return joystickID; } SDL_Joystick* getJoystick() { return joystick; } + void touchScreen(int x, int y); + void releaseScreen(); + private: static int lastSep(const std::string& path); std::string getAssetPath(bool gba, const std::string& configpath, const std::string& ext, const std::string& file); @@ -328,6 +331,9 @@ private: melonDS::u32 inputMask; + bool isTouching; + melonDS::u16 touchX, touchY; + friend class EmuThread; friend class MainWindow; }; diff --git a/src/frontend/qt_sdl/EmuInstanceInput.cpp b/src/frontend/qt_sdl/EmuInstanceInput.cpp index bb06c242..aa1c529f 100644 --- a/src/frontend/qt_sdl/EmuInstanceInput.cpp +++ b/src/frontend/qt_sdl/EmuInstanceInput.cpp @@ -74,6 +74,10 @@ void EmuInstance::inputInit() hotkeyMask = 0; lastHotkeyMask = 0; + isTouching = false; + touchX = 0; + touchY = 0; + joystick = nullptr; controller = nullptr; hasRumble = false; @@ -353,3 +357,15 @@ void EmuInstance::inputProcess() hotkeyRelease = lastHotkeyMask & ~hotkeyMask; lastHotkeyMask = hotkeyMask; } + +void EmuInstance::touchScreen(int x, int y) +{ + touchX = x; + touchY = y; + isTouching = true; +} + +void EmuInstance::releaseScreen() +{ + isTouching = false; +} diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index f4ce8c94..f0207f1a 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -251,6 +251,11 @@ void EmuThread::run() // process input and hotkeys emuInstance->nds->SetKeyMask(emuInstance->inputMask); + if (emuInstance->isTouching) + emuInstance->nds->TouchScreen(emuInstance->touchX, emuInstance->touchY); + else + emuInstance->nds->ReleaseScreen(); + if (emuInstance->hotkeyPressed(HK_Lid)) { bool lid = !emuInstance->nds->IsLidClosed(); diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index 0d05a065..eab5feee 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -258,8 +258,7 @@ void ScreenPanel::mousePressEvent(QMouseEvent* event) if (layout.GetTouchCoords(x, y, false)) { touching = true; - assert(emuInstance->getNDS() != nullptr); - emuInstance->getNDS()->TouchScreen(x, y); + emuInstance->touchScreen(x, y); } } @@ -272,8 +271,7 @@ void ScreenPanel::mouseReleaseEvent(QMouseEvent* event) if (touching) { touching = false; - assert(emuInstance->getNDS() != nullptr); - emuInstance->getNDS()->ReleaseScreen(); + emuInstance->releaseScreen(); } } @@ -292,8 +290,7 @@ void ScreenPanel::mouseMoveEvent(QMouseEvent* event) if (layout.GetTouchCoords(x, y, true)) { - assert(emuInstance->getNDS() != nullptr); - emuInstance->getNDS()->TouchScreen(x, y); + emuInstance->touchScreen(x, y); } } @@ -318,16 +315,14 @@ void ScreenPanel::tabletEvent(QTabletEvent* event) if (layout.GetTouchCoords(x, y, event->type()==QEvent::TabletMove)) { touching = true; - assert(emuInstance->getNDS() != nullptr); - emuInstance->getNDS()->TouchScreen(x, y); + emuInstance->touchScreen(x, y); } } break; case QEvent::TabletRelease: if (touching) { - assert(emuInstance->getNDS() != nullptr); - emuInstance->getNDS()->ReleaseScreen(); + emuInstance->releaseScreen(); touching = false; } break; @@ -365,16 +360,14 @@ void ScreenPanel::touchEvent(QTouchEvent* event) if (layout.GetTouchCoords(x, y, event->type()==QEvent::TouchUpdate)) { touching = true; - assert(emuInstance->getNDS() != nullptr); - emuInstance->getNDS()->TouchScreen(x, y); + emuInstance->touchScreen(x, y); } } break; case QEvent::TouchEnd: if (touching) { - assert(emuInstance->getNDS() != nullptr); - emuInstance->getNDS()->ReleaseScreen(); + emuInstance->releaseScreen(); touching = false; } break; From 9c8f229fed59e0b2f9035d185742c3ab04b921f7 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 31 Oct 2024 22:37:46 +0100 Subject: [PATCH 55/63] misc shit --- src/frontend/qt_sdl/AudioSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/EmuThread.cpp | 8 ++++---- src/frontend/qt_sdl/EmuThread.h | 4 ++-- src/frontend/qt_sdl/Screen.cpp | 10 +++++----- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp index 5c0c9330..5f2aef19 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp +++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp @@ -41,7 +41,7 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui( emuInstance = ((MainWindow*)parent)->getEmuInstance(); auto& cfg = emuInstance->getGlobalConfig(); auto& instcfg = emuInstance->getLocalConfig(); - bool emuActive = emuInstance->getEmuThread()->emuIsActive(); + bool emuActive = emuInstance->emuIsActive(); oldInterp = cfg.GetInt("Audio.Interpolation"); oldBitDepth = cfg.GetInt("Audio.BitDepth"); diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index f0207f1a..7e1b040c 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -319,13 +319,13 @@ void EmuThread::run() if (!useOpenGL) { - FrontBufferLock.lock(); - FrontBuffer = emuInstance->nds->GPU.FrontBuffer; - FrontBufferLock.unlock(); + frontBufferLock.lock(); + frontBuffer = emuInstance->nds->GPU.FrontBuffer; + frontBufferLock.unlock(); } else { - FrontBuffer = emuInstance->nds->GPU.FrontBuffer; + frontBuffer = emuInstance->nds->GPU.FrontBuffer; emuInstance->drawScreenGL(); } diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index 18bff6dd..f28c0604 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -136,8 +136,8 @@ public: void updateVideoSettings() { videoSettingsDirty = true; } void updateVideoRenderer() { videoSettingsDirty = true; lastVideoRenderer = -1; } - int FrontBuffer = 0; - QMutex FrontBufferLock; + int frontBuffer = 0; + QMutex frontBufferLock; signals: void windowUpdate(); diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index eab5feee..3eb37183 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -782,17 +782,17 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) auto nds = emuInstance->getNDS(); assert(nds != nullptr); - emuThread->FrontBufferLock.lock(); - int frontbuf = emuThread->FrontBuffer; + emuThread->frontBufferLock.lock(); + int frontbuf = emuThread->frontBuffer; if (!nds->GPU.Framebuffer[frontbuf][0] || !nds->GPU.Framebuffer[frontbuf][1]) { - emuThread->FrontBufferLock.unlock(); + emuThread->frontBufferLock.unlock(); return; } memcpy(screen[0].scanLine(0), nds->GPU.Framebuffer[frontbuf][0].get(), 256 * 192 * 4); memcpy(screen[1].scanLine(0), nds->GPU.Framebuffer[frontbuf][1].get(), 256 * 192 * 4); - emuThread->FrontBufferLock.unlock(); + emuThread->frontBufferLock.unlock(); QRect screenrc(0, 0, 256, 192); @@ -1106,7 +1106,7 @@ void ScreenPanelGL::drawScreenGL() glUseProgram(screenShaderProgram); glUniform2f(screenShaderScreenSizeULoc, w / factor, h / factor); - int frontbuf = emuThread->FrontBuffer; + int frontbuf = emuThread->frontBuffer; glActiveTexture(GL_TEXTURE0); #ifdef OGLRENDERER_ENABLED From e3e561da3fd2fca66b2d09ba6ed418eb4e0029b1 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 31 Oct 2024 22:51:18 +0100 Subject: [PATCH 56/63] lock framebuffer stuff to prevent conflicts when reiniting the core or changing the renderer --- src/frontend/qt_sdl/EmuInstance.cpp | 2 ++ src/frontend/qt_sdl/EmuInstance.h | 2 ++ src/frontend/qt_sdl/EmuThread.cpp | 2 ++ src/frontend/qt_sdl/Screen.cpp | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 9114a141..f4092b61 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -1340,6 +1340,7 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB args = &(*dsiargs); } + renderLock.lock(); if ((!nds) || (consoleType != nds->ConsoleType)) { NDS::Current = nullptr; @@ -1387,6 +1388,7 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB dsi->EjectGBACart(); } } + renderLock.unlock(); return true; } diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 9ab12d93..33e5fd95 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -150,6 +150,8 @@ public: void touchScreen(int x, int y); void releaseScreen(); + QMutex renderLock; + private: static int lastSep(const std::string& path); std::string getAssetPath(bool gba, const std::string& configpath, const std::string& ext, const std::string& file); diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 7e1b040c..6417fa69 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -231,6 +231,7 @@ void EmuThread::run() // update render settings if needed if (videoSettingsDirty) { + emuInstance->renderLock.lock(); if (useOpenGL) { emuInstance->setVSyncGL(true); @@ -246,6 +247,7 @@ void EmuThread::run() updateRenderer(); videoSettingsDirty = false; + emuInstance->renderLock.unlock(); } // process input and hotkeys diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index 3eb37183..a1048018 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -779,6 +779,7 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) if (emuThread->emuIsActive()) { + emuInstance->renderLock.lock(); auto nds = emuInstance->getNDS(); assert(nds != nullptr); @@ -801,6 +802,7 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) painter.setTransform(screenTrans[i]); painter.drawImage(screenrc, screen[screenKind[i]]); } + emuInstance->renderLock.unlock(); } osdUpdate(); From 09e4400f3cfc7654181a76264203f0c6c5d9315a Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 1 Nov 2024 00:40:09 +0100 Subject: [PATCH 57/63] fix hang when closing an instance that is engaged into local multiplayer --- src/frontend/qt_sdl/EmuInstance.cpp | 2 -- src/frontend/qt_sdl/EmuThread.cpp | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index f4092b61..89d4108b 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -154,8 +154,6 @@ EmuInstance::~EmuInstance() deleting = true; deleteAllWindows(); - MPInterface::Get().End(instanceID); - emuThread->emuExit(); emuThread->wait(); delete emuThread; diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 6417fa69..f767c6db 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -478,7 +478,8 @@ void EmuThread::waitMessage(int num) void EmuThread::waitAllMessages() { if (QThread::currentThread() == this) return; - msgSemaphore.acquire(msgSemaphore.available()); + while (!msgQueue.empty()) + msgSemaphore.acquire(); } void EmuThread::handleMessages() @@ -494,6 +495,7 @@ void EmuThread::handleMessages() emuPauseStack = emuPauseStackRunning; emuInstance->audioDisable(); + MPInterface::Get().End(emuInstance->instanceID); break; case msg_EmuRun: From 78aae252d510711a3d828ce42085350323736af1 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 1 Nov 2024 00:41:55 +0100 Subject: [PATCH 58/63] fix bug where opening a new instance would pause other instances --- src/frontend/qt_sdl/EmuInstance.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 89d4108b..2ca5d09f 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -136,7 +136,6 @@ EmuInstance::EmuInstance(int inst) : deleting(false), createWindow(); emuThread->start(); - emuThread->emuPause(); // if any extra windows were saved as enabled, open them for (int i = 1; i < kMaxWindows; i++) From 58ee191cc8d9b89e7f128b5602105f7efed7b1eb Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 1 Nov 2024 01:31:45 +0100 Subject: [PATCH 59/63] make mic input less shitty (and less dangerous) --- src/frontend/qt_sdl/EmuInstance.cpp | 1 - src/frontend/qt_sdl/EmuInstance.h | 5 +- src/frontend/qt_sdl/EmuInstanceAudio.cpp | 59 ++++++++++++++++++------ 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 2ca5d09f..2ac3f42d 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -140,7 +140,6 @@ EmuInstance::EmuInstance(int inst) : deleting(false), // if any extra windows were saved as enabled, open them for (int i = 1; i < kMaxWindows; i++) { - //Config::Table tbl = localCfg.GetTable("Window"+std::to_string(i), "Window0"); std::string key = "Window" + std::to_string(i) + ".Enabled"; bool enable = localCfg.GetBool(key); if (enable) diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 33e5fd95..a135a52c 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -298,8 +298,9 @@ private: int mpAudioMode; SDL_AudioDeviceID micDevice; - melonDS::s16 micExtBuffer[2048]; + melonDS::s16 micExtBuffer[4096]; melonDS::u32 micExtBufferWritePos; + melonDS::u32 micExtBufferCount; melonDS::u32 micWavLength; melonDS::s16* micWavBuffer; @@ -308,6 +309,8 @@ private: melonDS::u32 micBufferLength; melonDS::u32 micBufferReadPos; + SDL_mutex* micLock; + //int audioInterp; int audioVolume; bool audioDSiVolumeSync; diff --git a/src/frontend/qt_sdl/EmuInstanceAudio.cpp b/src/frontend/qt_sdl/EmuInstanceAudio.cpp index a9bf1fb7..ac6e7d5b 100644 --- a/src/frontend/qt_sdl/EmuInstanceAudio.cpp +++ b/src/frontend/qt_sdl/EmuInstanceAudio.cpp @@ -107,8 +107,12 @@ void EmuInstance::micCallback(void* data, Uint8* stream, int len) s16* input = (s16*)stream; len /= sizeof(s16); + SDL_LockMutex(inst->micLock); int maxlen = sizeof(micExtBuffer) / sizeof(s16); + if ((inst->micExtBufferCount + len) > maxlen) + len = maxlen - inst->micExtBufferCount; + if ((inst->micExtBufferWritePos + len) > maxlen) { u32 len1 = maxlen - inst->micExtBufferWritePos; @@ -121,6 +125,9 @@ void EmuInstance::micCallback(void* data, Uint8* stream, int len) memcpy(&inst->micExtBuffer[inst->micExtBufferWritePos], input, len*sizeof(s16)); inst->micExtBufferWritePos += len; } + + inst->micExtBufferCount += len; + SDL_UnlockMutex(inst->micLock); } void EmuInstance::audioMute() @@ -270,6 +277,8 @@ void EmuInstance::micLoadWav(const std::string& name) void EmuInstance::micProcess() { + SDL_LockMutex(micLock); + int type = micInputType; bool cmd = hotkeyDown(HK_Mic); @@ -278,6 +287,8 @@ void EmuInstance::micProcess() type = micInputType_Silence; } + const int kFrameLen = 735; + switch (type) { case micInputType_Silence: // no mic @@ -289,21 +300,35 @@ void EmuInstance::micProcess() case micInputType_Wav: // WAV if (micBuffer) { - if ((micBufferReadPos + 735) > micBufferLength) - { - s16 tmp[735]; - u32 len1 = micBufferLength - micBufferReadPos; - memcpy(&tmp[0], &micBuffer[micBufferReadPos], len1*sizeof(s16)); - memcpy(&tmp[len1], &micBuffer[0], (735 - len1)*sizeof(s16)); + int len = kFrameLen; + if (micExtBufferCount < len) + len = micExtBufferCount; - nds->MicInputFrame(tmp, 735); - micBufferReadPos = 735 - len1; + s16 tmp[kFrameLen]; + + if ((micBufferReadPos + len) > micBufferLength) + { + u32 part1 = micBufferLength - micBufferReadPos; + memcpy(&tmp[0], &micBuffer[micBufferReadPos], part1*sizeof(s16)); + memcpy(&tmp[part1], &micBuffer[0], (len - part1)*sizeof(s16)); + + micBufferReadPos = len - part1; } else { - nds->MicInputFrame(&micBuffer[micBufferReadPos], 735); - micBufferReadPos += 735; + memcpy(&tmp[0], &micBuffer[micBufferReadPos], len*sizeof(s16)); + + micBufferReadPos += len; } + + if (len < kFrameLen) + { + for (int i = len; i < kFrameLen; i++) + tmp[i] = tmp[len-1]; + } + nds->MicInputFrame(tmp, 735); + + micExtBufferCount -= len; } else { @@ -317,19 +342,21 @@ void EmuInstance::micProcess() int sample_len = sizeof(mic_blow) / sizeof(u16); static int sample_pos = 0; - s16 tmp[735]; + s16 tmp[kFrameLen]; - for (int i = 0; i < 735; i++) + for (int i = 0; i < kFrameLen; i++) { tmp[i] = mic_blow[sample_pos] ^ 0x8000; sample_pos++; if (sample_pos >= sample_len) sample_pos = 0; } - nds->MicInputFrame(tmp, 735); + nds->MicInputFrame(tmp, kFrameLen); } break; } + + SDL_UnlockMutex(micLock); } void EmuInstance::setupMicInputData() @@ -402,12 +429,15 @@ void EmuInstance::audioInit() memset(micExtBuffer, 0, sizeof(micExtBuffer)); micExtBufferWritePos = 0; + micExtBufferCount = 0; micWavBuffer = nullptr; micBuffer = nullptr; micBufferLength = 0; micBufferReadPos = 0; + micLock = SDL_CreateMutex(); + setupMicInputData(); } @@ -425,6 +455,9 @@ void EmuInstance::audioDeInit() if (micWavBuffer) delete[] micWavBuffer; micWavBuffer = nullptr; + + if (micLock) SDL_DestroyMutex(micLock); + micLock = nullptr; } void EmuInstance::audioSync() From 7740634e6a8269d1327fdf3dad4d88de2ac4d330 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 1 Nov 2024 02:19:29 +0100 Subject: [PATCH 60/63] reimplement MP audio mode 2 (active instance only) --- src/frontend/qt_sdl/EmuInstanceAudio.cpp | 15 +++++++++++---- src/frontend/qt_sdl/Screen.cpp | 4 ++++ src/frontend/qt_sdl/Window.cpp | 18 ++++++++++++++++-- src/frontend/qt_sdl/Window.h | 6 ++++++ 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstanceAudio.cpp b/src/frontend/qt_sdl/EmuInstanceAudio.cpp index ac6e7d5b..3b222ba0 100644 --- a/src/frontend/qt_sdl/EmuInstanceAudio.cpp +++ b/src/frontend/qt_sdl/EmuInstanceAudio.cpp @@ -133,6 +133,7 @@ void EmuInstance::micCallback(void* data, Uint8* stream, int len) void EmuInstance::audioMute() { audioMuted = false; + if (numEmuInstances() < 2) return; switch (mpAudioMode) { @@ -141,10 +142,16 @@ void EmuInstance::audioMute() break; case 2: // only currently focused instance - //if (mainWindow != nullptr) - // audioMuted = !mainWindow->isActiveWindow(); - // TODO!! - printf("TODO!! audioMute mode 2\n"); + audioMuted = true; + for (int i = 0; i < kMaxWindows; i++) + { + if (!windowList[i]) continue; + if (windowList[i]->isFocused()) + { + audioMuted = false; + break; + } + } break; } } diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index a1048018..10abe1ce 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -385,6 +385,10 @@ bool ScreenPanel::event(QEvent* event) touchEvent((QTouchEvent*)event); return true; } + else if (event->type() == QEvent::FocusIn) + mainWindow->onFocusIn(); + else if (event->type() == QEvent::FocusOut) + mainWindow->onFocusOut(); return QWidget::event(event); } diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index aed8076a..596a0f5c 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -235,7 +235,8 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : localCfg(inst->localCfg), windowCfg(localCfg.GetTable("Window"+std::to_string(id), "Window0")), emuThread(inst->getEmuThread()), - enabledSaved(false) + enabledSaved(false), + focused(true) { #ifndef _WIN32 if (!parent) @@ -1015,13 +1016,26 @@ void MainWindow::dropEvent(QDropEvent* event) void MainWindow::focusInEvent(QFocusEvent* event) { - emuInstance->audioMute(); + onFocusIn(); } void MainWindow::focusOutEvent(QFocusEvent* event) +{ + onFocusOut(); +} + +void MainWindow::onFocusIn() +{ + focused = true; + if (emuInstance) + emuInstance->audioMute(); +} + +void MainWindow::onFocusOut() { // focusOutEvent is called through the window close event handler // prevent use after free + focused = false; if (emuInstance) emuInstance->audioMute(); } diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index 121115c0..9f652f54 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -131,6 +131,10 @@ public: void onAppStateChanged(Qt::ApplicationState state); + void onFocusIn(); + void onFocusOut(); + bool isFocused() { return focused; } + void osdAddMessage(unsigned int color, const char* msg); // called when the MP interface is changed @@ -264,6 +268,8 @@ private: int windowID; bool enabledSaved; + bool focused; + EmuInstance* emuInstance; EmuThread* emuThread; From a5389286e861ac13ec1f21412b1160d494c2cfbe Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sat, 2 Nov 2024 05:44:36 +0100 Subject: [PATCH 61/63] Make macOS OpenGL deprecation warnings shut up --- src/frontend/qt_sdl/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 56f82d89..7dc4a00c 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -122,6 +122,10 @@ elseif (APPLE) target_sources(melonDS PRIVATE ../duckstation/gl/context_agl.mm ) + set_source_files_properties( + ../duckstation/gl/context_agl.mm + PROPERTIES COMPILE_OPTIONS "-Wno-deprecated-declarations" + ) else() find_package(X11 REQUIRED) find_package(EGL REQUIRED) From d8f1d106f0965e469c84c661e0f80dafd418e0fd Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Tue, 5 Nov 2024 07:58:31 +0100 Subject: [PATCH 62/63] flake: remove workaround no longer needed with Darwin SDK changes also add the Qt tools to the dev shell since they're needed for Qt Designer and such --- flake.lock | 6 +++--- flake.nix | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/flake.lock b/flake.lock index be75f57f..d7dd5bc6 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1729665710, - "narHash": "sha256-AlcmCXJZPIlO5dmFzV3V2XF6x/OpNWUV8Y/FMPGd8Z4=", + "lastModified": 1730531603, + "narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2768c7d042a37de65bb1b5b3268fc987e534c49d", + "rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 8d500c03..a7768e4e 100644 --- a/flake.nix +++ b/flake.nix @@ -19,7 +19,7 @@ then sourceInfo.dirtyShortRev else sourceInfo.shortRev; - melonDS = pkgs.qt6.qtbase.stdenv.mkDerivation { + melonDS = pkgs.stdenv.mkDerivation { pname = "melonDS"; version = "0.9.5-${shortRevision}"; src = ./.; @@ -74,8 +74,11 @@ drv = self.packages.${system}.default; }; devShells = { - default = pkgs.mkShell.override { stdenv = pkgs.qt6.qtbase.stdenv; } { + default = pkgs.mkShell { inputsFrom = [ self.packages.${system}.default ]; + packages = with pkgs; [ + qt6.qttools + ]; }; # Shell for building static melonDS release builds with vcpkg From 5959009ebd600a13578a7be6a6f2baffbcf2b436 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Tue, 5 Nov 2024 17:03:07 +0100 Subject: [PATCH 63/63] Use Qt 6 by default on all platforms and update build instructions (#2187) --- .github/workflows/build-ubuntu.yml | 3 +- BUILD.md | 81 ++++++++++++++++++++++++++++++ CMakePresets.json | 4 -- README.md | 70 +------------------------- cmake/ConfigureVcpkg.cmake | 6 +-- src/frontend/qt_sdl/CMakeLists.txt | 6 +-- 6 files changed, 85 insertions(+), 85 deletions(-) create mode 100644 BUILD.md diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 044d01ee..1104142d 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -28,7 +28,7 @@ jobs: sudo apt install --allow-downgrades cmake ninja-build extra-cmake-modules libpcap0.8-dev libsdl2-dev libenet-dev \ qt6-{base,base-private,multimedia}-dev libqt6svg6-dev libarchive-dev libzstd-dev libfuse2 - name: Configure - run: cmake -B build -G Ninja -DUSE_QT6=ON -DCMAKE_INSTALL_PREFIX=/usr -DMELONDS_EMBED_BUILD_INFO=ON + run: cmake -B build -G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DMELONDS_EMBED_BUILD_INFO=ON - name: Build run: | cmake --build build @@ -79,7 +79,6 @@ jobs: -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config \ -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc-12 \ -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++-12 \ - -DUSE_QT6=ON \ -DMELONDS_EMBED_BUILD_INFO=ON - name: Build shell: bash diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 00000000..51bc4cac --- /dev/null +++ b/BUILD.md @@ -0,0 +1,81 @@ +# Building melonDS + +* [Linux](#linux) +* [Windows](#windows) +* [macOS](#macos) + +## Linux +1. Install dependencies: + * Ubuntu: + * All versions: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev libarchive-dev libenet-dev libzstd-dev` + * 24.04: `sudo apt install qt6-{base,base-private,multimedia,svg}-dev` + * 22.04: `sudo apt install qtbase6-dev qtbase6-private-dev qtmultimedia6-dev libqt6svg6-dev` + * Older versions: `sudo apt install qtbase5-dev qtbase5-private-dev qtmultimedia5-dev libqt5svg5-dev` + Also add `-DUSE_QT6=OFF` to the first CMake command below. + * Fedora: `sudo dnf install gcc-c++ cmake extra-cmake-modules SDL2-devel libarchive-devel enet-devel libzstd-devel qt6-{qtbase,qtmultimedia,qtsvg}-devel wayland-devel` + * Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt6-{base,multimedia,svg} libarchive enet zstd` +2. Download the melonDS repository and prepare: + ```bash + git clone https://github.com/melonDS-emu/melonDS + cd melonDS + ``` +3. Compile: + ```bash + cmake -B build + cmake --build build -j$(nproc --all) + ``` + +## Windows +1. Install [MSYS2](https://www.msys2.org/) +2. Open the MSYS2 terminal from the Start menu: + * For x64 systems (most common), use **MSYS2 UCRT64** + * For ARM64 systems, use **MSYS2 CLANGARM64** +3. Update the packages using `pacman -Syu` and reopen the same terminal if it asks you to +4. Install git and clone the repository + ```bash + pacman -S git + git clone https://github.com/melonDS-emu/melonDS + cd melonDS + ``` +5. Install dependencies: + Replace `` below with `mingw-w64-ucrt-x86_64` on x64 systems, or `mingw-w64-clang-aarch64` on ARM64 systems. + ```bash + pacman -S -{toolchain,cmake,SDL2,libarchive,enet,zstd}` + ``` +6. Install Qt and configure the build directory + * Dynamic builds (with DLLs) + 1. Install Qt: `pacman -S -{qt6-base,qt6-svg,qt6-multimedia,qt6-svg,qt6-tools}` + 2. Set up the build directory with `cmake -B build` + * Static builds (without DLLs, standalone executable) + 1. Install Qt: `pacman -S -qt5-static` + (Note: As of writing, the `qt6-static` package does not work.) + 2. Set up the build directory with `cmake -B build -DBUILD_STATIC=ON -DUSE_QT6=OFF -DCMAKE_PREFIX_PATH=$MSYSTEM_PREFIX/qt5-static` +7. Compile: `cmake --build build` + +If everything went well, melonDS should now be in the `build` folder. For dynamic builds, you may need to run melonDS from the MSYS2 terminal in order for it to find the required DLLs. + +## macOS +1. Install the [Homebrew Package Manager](https://brew.sh) +2. Install dependencies: `brew install git pkg-config cmake sdl2 qt@6 libarchive enet zstd` +3. Download the melonDS repository and prepare: + ```zsh + git clone https://github.com/melonDS-emu/melonDS + cd melonDS + ``` +4. Compile: + ```zsh + cmake -B build -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)" + cmake --build build -j$(sysctl -n hw.logicalcpu) + ``` +If everything went well, melonDS.app should now be in the `build` directory. + +### Self-contained app bundle +If you want an app bundle that can be distributed to other computers without needing to install dependencies through Homebrew, you can additionally run ` +../tools/mac-libs.rb .` after the build is completed, or add `-DMACOS_BUNDLE_LIBS=ON` to the first CMake command. + +## Nix (macOS/Linux) + +melonDS provides a Nix flake with support for both macOS and Linux. The [Nix package manager](https://nixos.org) needs to be installed to use it. + +* To run melonDS, just type `nix run github:melonDS-emu/melonDS`. +* To get a shell for development, clone the melonDS repository and type `nix develop` in its directory. \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json index 2144417b..b506b88a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -27,10 +27,6 @@ "binaryDir": "${sourceDir}/build/release-mingw-x86_64", "generator": "Ninja", "cacheVariables": { - "USE_QT6": { - "type": "BOOL", - "value": "ON" - }, "BUILD_STATIC": { "type": "BOOL", "value": "ON" diff --git a/README.md b/README.md index eb8b1358..2cf42c2d 100644 --- a/README.md +++ b/README.md @@ -32,75 +32,7 @@ DS BIOS dumps from a DSi or 3DS can be used with no compatibility issues. DSi BI As for the rest, the interface should be pretty straightforward. If you have a question, don't hesitate to ask, though! ## How to build - -### Linux -1. Install dependencies: - * Ubuntu 22.04: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qtbase5-dev qtbase5-private-dev qtmultimedia5-dev libqt5svg5-dev libarchive-dev libenet-dev libzstd-dev` - * Older Ubuntu: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default qtbase5-private-dev qtmultimedia5-dev libqt5svg5-dev libarchive-dev libenet-dev libzstd-dev` - * Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt5-base qt5-multimedia qt5-svg libarchive enet zstd` -3. Download the melonDS repository and prepare: - ```bash - git clone https://github.com/melonDS-emu/melonDS - cd melonDS - ``` - -3. Compile: - ```bash - cmake -B build - cmake --build build -j$(nproc --all) - ``` - -### Windows -1. Install [MSYS2](https://www.msys2.org/) -2. Open the **MSYS2 MinGW 64-bit** terminal -3. Update the packages using `pacman -Syu` and reopen the terminal if it asks you to -4. Install git to clone the repository - ```bash - pacman -S git - ``` -5. Download the melonDS repository and prepare: - ```bash - git clone https://github.com/melonDS-emu/melonDS - cd melonDS - ``` -#### Dynamic builds (with DLLs) -5. Install dependencies: `pacman -S mingw-w64-x86_64-{cmake,SDL2,toolchain,qt5-base,qt5-svg,qt5-multimedia,qt5-svg,qt5-tools,libarchive,enet,zstd}` -6. Compile: - ```bash - cmake -B build - cmake --build build - cd build - ../tools/msys-dist.sh - ``` -If everything went well, melonDS and the libraries it needs should now be in the `dist` folder. - -#### Static builds (without DLLs, standalone executable) -5. Install dependencies: `pacman -S mingw-w64-x86_64-{cmake,SDL2,toolchain,qt5-static,libarchive,enet,zstd}` -6. Compile: - ```bash - cmake -B build -DBUILD_STATIC=ON -DCMAKE_PREFIX_PATH=/mingw64/qt5-static - cmake --build build - ``` -If everything went well, melonDS should now be in the `build` folder. - -### macOS -1. Install the [Homebrew Package Manager](https://brew.sh) -2. Install dependencies: `brew install git pkg-config cmake sdl2 qt@6 libarchive enet zstd` -3. Download the melonDS repository and prepare: - ```zsh - git clone https://github.com/melonDS-emu/melonDS - cd melonDS - ``` -4. Compile: - ```zsh - cmake -B build -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)" - cmake --build build -j$(sysctl -n hw.logicalcpu) - ``` -If everything went well, melonDS.app should now be in the `build` directory. - -#### Self-contained app bundle -If you want an app bundle that can be distributed to other computers without needing to install dependencies through Homebrew, you can additionally run ` -../tools/mac-libs.rb .` after the build is completed, or add `-DMACOS_BUNDLE_LIBS=ON` to the first CMake command. +See [BUILD.md](./BUILD.md) for build instructions. ## TODO LIST diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake index c1eb522d..3fb0786f 100644 --- a/cmake/ConfigureVcpkg.cmake +++ b/cmake/ConfigureVcpkg.cmake @@ -19,11 +19,7 @@ set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_SOURCE_DIR}/cmake/overlay-triplets") option(USE_RECOMMENDED_TRIPLETS "Use the recommended triplets that are used for official builds" ON) # Duplicated here because it needs to be set before project() -if (NOT WIN32) - option(USE_QT6 "Build using Qt 6 instead of 5" ON) -else() - option(USE_QT6 "Build using Qt 6 instead of 5" OFF) -endif() +option(USE_QT6 "Use Qt 6 instead of Qt 5" ON) # Since the Linux build pulls in glib anyway, we can just use upstream libslirp if (UNIX AND NOT APPLE) diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 7dc4a00c..54888c49 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -59,11 +59,7 @@ set(SOURCES_QT_SDL NetplayDialog.cpp ) -if (APPLE) - option(USE_QT6 "Build using Qt 6 instead of 5" ON) -else() - option(USE_QT6 "Build using Qt 6 instead of 5" OFF) -endif() +option(USE_QT6 "Use Qt 6 instead of Qt 5" ON) if (USE_QT6) find_package(Qt6 COMPONENTS Core Gui Widgets Network Multimedia OpenGL OpenGLWidgets Svg REQUIRED)