Compare commits
10 Commits
7efbae3f5f
...
18cca24bef
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18cca24bef | ||
|
|
f2bc79352f | ||
|
|
6be36b20ed | ||
|
|
944b9892f9 | ||
|
|
7e0150b303 | ||
|
|
0c1ccbd177 | ||
|
|
8299059dd4 | ||
|
|
cb9d09aca0 | ||
|
|
8cc51b8223 | ||
|
|
9a9d24cb62 |
4
.github/workflows/Test_Build.yml
vendored
@@ -35,7 +35,9 @@ jobs:
|
|||||||
- name: Clone and compile 3ds-examples
|
- name: Clone and compile 3ds-examples
|
||||||
run: |
|
run: |
|
||||||
git clone --recursive https://github.com/devkitPro/3ds-examples tests/3ds-examples
|
git clone --recursive https://github.com/devkitPro/3ds-examples tests/3ds-examples
|
||||||
make -C tests/3ds-examples
|
# The devkitpro docker image is outdated and cannot build 3ds-examples at the moment
|
||||||
|
# TODO: Reenable this when it's updated again
|
||||||
|
# make -C tests/3ds-examples
|
||||||
|
|
||||||
- name: Upload binaries
|
- name: Upload binaries
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
3
.gitignore
vendored
@@ -16,7 +16,8 @@ ReleaseWithClangCL/
|
|||||||
Enabled/
|
Enabled/
|
||||||
Disabled/
|
Disabled/
|
||||||
build/
|
build/
|
||||||
|
build_uwp/
|
||||||
|
build_fsui/
|
||||||
.vs/
|
.vs/
|
||||||
.vscode/*.log
|
.vscode/*.log
|
||||||
.cache/
|
.cache/
|
||||||
|
|||||||
3
.gitmodules
vendored
@@ -80,3 +80,6 @@
|
|||||||
[submodule "third_party/xbyak"]
|
[submodule "third_party/xbyak"]
|
||||||
path = third_party/xbyak
|
path = third_party/xbyak
|
||||||
url = https://github.com/Panda3DS-emu/xbyak
|
url = https://github.com/Panda3DS-emu/xbyak
|
||||||
|
[submodule "third_party/fsui"]
|
||||||
|
path = third_party/fsui
|
||||||
|
url = https://git.nanodata.cloud/moonpower/fsui-lib.git
|
||||||
|
|||||||
@@ -176,7 +176,6 @@ include_directories(${FMT_INCLUDE_DIR})
|
|||||||
include_directories(third_party/boost/)
|
include_directories(third_party/boost/)
|
||||||
include_directories(third_party/elfio/)
|
include_directories(third_party/elfio/)
|
||||||
include_directories(third_party/hips/include/)
|
include_directories(third_party/hips/include/)
|
||||||
include_directories(third_party/imgui/)
|
|
||||||
include_directories(third_party/dynarmic/src)
|
include_directories(third_party/dynarmic/src)
|
||||||
include_directories(third_party/cityhash/include)
|
include_directories(third_party/cityhash/include)
|
||||||
include_directories(third_party/result/include)
|
include_directories(third_party/result/include)
|
||||||
@@ -186,6 +185,13 @@ include_directories(third_party/stb)
|
|||||||
include_directories(third_party/opengl)
|
include_directories(third_party/opengl)
|
||||||
include_directories(third_party/miniaudio)
|
include_directories(third_party/miniaudio)
|
||||||
include_directories(third_party/mio/single_include)
|
include_directories(third_party/mio/single_include)
|
||||||
|
if(IMGUI_FRONTEND)
|
||||||
|
include_directories(third_party/fsui/include)
|
||||||
|
include_directories(third_party/fsui/third_party/imgui)
|
||||||
|
include_directories(third_party/fsui/third_party/imgui/backends)
|
||||||
|
else()
|
||||||
|
include_directories(third_party/imgui/)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_compile_definitions(NOMINMAX) # Make windows.h not define min/max macros because third-party deps don't like it
|
add_compile_definitions(NOMINMAX) # Make windows.h not define min/max macros because third-party deps don't like it
|
||||||
add_compile_definitions(WIN32_LEAN_AND_MEAN) # Make windows.h not include literally everything
|
add_compile_definitions(WIN32_LEAN_AND_MEAN) # Make windows.h not include literally everything
|
||||||
@@ -202,20 +208,35 @@ endif()
|
|||||||
|
|
||||||
if (NOT ANDROID)
|
if (NOT ANDROID)
|
||||||
if (UWP_BUILD)
|
if (UWP_BUILD)
|
||||||
|
if (NOT TARGET SDL2::SDL2)
|
||||||
|
add_library(SDL2::SDL2 SHARED IMPORTED GLOBAL)
|
||||||
|
set_target_properties(SDL2::SDL2 PROPERTIES
|
||||||
|
IMPORTED_IMPLIB "${MINGW_UWP_DIR}/deps/lib/SDL2.lib"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${MINGW_UWP_DIR}/deps/include;${MINGW_UWP_DIR}/deps/include/SDL2"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
target_include_directories(AlberCore PUBLIC
|
target_include_directories(AlberCore PUBLIC
|
||||||
"${MINGW_UWP_DIR}/deps/include"
|
"${MINGW_UWP_DIR}/deps/include"
|
||||||
"${MINGW_UWP_DIR}/deps/include/SDL2"
|
"${MINGW_UWP_DIR}/deps/include/SDL2"
|
||||||
)
|
)
|
||||||
target_link_libraries(AlberCore PUBLIC "${MINGW_UWP_DIR}/deps/lib/SDL2.lib")
|
target_link_libraries(AlberCore PUBLIC SDL2::SDL2)
|
||||||
elseif (USE_SYSTEM_SDL2)
|
elseif (USE_SYSTEM_SDL2)
|
||||||
find_package(SDL2 CONFIG REQUIRED)
|
find_package(SDL2 CONFIG REQUIRED)
|
||||||
target_link_libraries(AlberCore PUBLIC SDL2::SDL2)
|
target_link_libraries(AlberCore PUBLIC SDL2::SDL2)
|
||||||
else()
|
else()
|
||||||
set(SDL_STATIC ON CACHE BOOL "" FORCE)
|
set(SDL_STATIC ON CACHE BOOL "" FORCE)
|
||||||
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
|
if(IMGUI_FRONTEND)
|
||||||
|
set(SDL_SHARED ON CACHE BOOL "" FORCE)
|
||||||
|
else()
|
||||||
|
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
|
||||||
|
endif()
|
||||||
set(SDL_TEST OFF CACHE BOOL "" FORCE)
|
set(SDL_TEST OFF CACHE BOOL "" FORCE)
|
||||||
add_subdirectory(third_party/SDL2)
|
add_subdirectory(third_party/SDL2)
|
||||||
target_link_libraries(AlberCore PUBLIC SDL2-static)
|
if(IMGUI_FRONTEND)
|
||||||
|
target_link_libraries(AlberCore PUBLIC SDL2::SDL2)
|
||||||
|
else()
|
||||||
|
target_link_libraries(AlberCore PUBLIC SDL2-static)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -228,6 +249,16 @@ include_directories(third_party/duckstation)
|
|||||||
include_directories(third_party/host_memory/include)
|
include_directories(third_party/host_memory/include)
|
||||||
|
|
||||||
add_subdirectory(third_party/cmrc)
|
add_subdirectory(third_party/cmrc)
|
||||||
|
add_subdirectory(third_party/glad)
|
||||||
|
if(IMGUI_FRONTEND)
|
||||||
|
set(FSUI_BUILD_SAMPLES OFF CACHE BOOL "" FORCE)
|
||||||
|
set(FSUI_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
|
||||||
|
set(FSUI_PLATFORM_BACKEND SDL2 CACHE STRING "" FORCE)
|
||||||
|
add_subdirectory(third_party/fsui)
|
||||||
|
if(TARGET fsui-renderer-opengl)
|
||||||
|
target_compile_definitions(fsui-renderer-opengl PRIVATE FSUI_USE_LEGACY_GLAD=1)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/third_party/boost")
|
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/third_party/boost")
|
||||||
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/third_party/boost")
|
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/third_party/boost")
|
||||||
@@ -243,8 +274,6 @@ if(ANDROID)
|
|||||||
target_link_libraries(AlberCore PRIVATE EGL log)
|
target_link_libraries(AlberCore PRIVATE EGL log)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(third_party/glad)
|
|
||||||
|
|
||||||
# Cryptopp doesn't support compiling under clang-cl, so we have to include it as a prebuilt MSVC static library
|
# Cryptopp doesn't support compiling under clang-cl, so we have to include it as a prebuilt MSVC static library
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC)
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC)
|
||||||
add_subdirectory(third_party/cryptoppwin)
|
add_subdirectory(third_party/cryptoppwin)
|
||||||
@@ -500,18 +529,21 @@ cmrc_add_resource_library(
|
|||||||
"src/core/services/fonts/SharedFontReplacement.bin"
|
"src/core/services/fonts/SharedFontReplacement.bin"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
|
set(THIRD_PARTY_SOURCE_FILES third_party/cityhash/cityhash.cpp
|
||||||
third_party/imgui/imgui_draw.cpp
|
|
||||||
third_party/imgui/imgui_tables.cpp
|
|
||||||
third_party/imgui/imgui_widgets.cpp
|
|
||||||
third_party/imgui/imgui_demo.cpp
|
|
||||||
|
|
||||||
third_party/cityhash/cityhash.cpp
|
|
||||||
third_party/xxhash/xxhash.c
|
third_party/xxhash/xxhash.c
|
||||||
|
|
||||||
third_party/host_memory/host_memory.cpp
|
third_party/host_memory/host_memory.cpp
|
||||||
third_party/host_memory/virtual_buffer.cpp
|
third_party/host_memory/virtual_buffer.cpp
|
||||||
)
|
)
|
||||||
|
if(NOT IMGUI_FRONTEND)
|
||||||
|
list(APPEND THIRD_PARTY_SOURCE_FILES
|
||||||
|
third_party/imgui/imgui.cpp
|
||||||
|
third_party/imgui/imgui_draw.cpp
|
||||||
|
third_party/imgui/imgui_tables.cpp
|
||||||
|
third_party/imgui/imgui_widgets.cpp
|
||||||
|
third_party/imgui/imgui_demo.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(ENABLE_LUAJIT AND NOT ANDROID)
|
if(ENABLE_LUAJIT AND NOT ANDROID)
|
||||||
# Build luv and libuv for Lua TCP server usage if we're not on Android
|
# Build luv and libuv for Lua TCP server usage if we're not on Android
|
||||||
@@ -900,11 +932,11 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
|
|||||||
if(IMGUI_FRONTEND)
|
if(IMGUI_FRONTEND)
|
||||||
list(APPEND FRONTEND_SOURCE_FILES
|
list(APPEND FRONTEND_SOURCE_FILES
|
||||||
src/panda_sdl/imgui_layer.cpp
|
src/panda_sdl/imgui_layer.cpp
|
||||||
third_party/imgui/backends/imgui_impl_sdl.cpp
|
src/panda_sdl/panda_fsui.cpp
|
||||||
third_party/imgui/backends/imgui_impl_opengl3.cpp
|
|
||||||
)
|
)
|
||||||
list(APPEND FRONTEND_HEADER_FILES
|
list(APPEND FRONTEND_HEADER_FILES
|
||||||
"include/panda_sdl/imgui_layer.hpp"
|
"include/panda_sdl/imgui_layer.hpp"
|
||||||
|
"include/panda_sdl/panda_fsui.hpp"
|
||||||
)
|
)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
list(APPEND FRONTEND_LIBRARIES imm32)
|
list(APPEND FRONTEND_LIBRARIES imm32)
|
||||||
@@ -916,6 +948,10 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
|
|||||||
if(FRONTEND_LIBRARIES)
|
if(FRONTEND_LIBRARIES)
|
||||||
target_link_libraries(Alber PRIVATE ${FRONTEND_LIBRARIES})
|
target_link_libraries(Alber PRIVATE ${FRONTEND_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
if(IMGUI_FRONTEND)
|
||||||
|
target_link_libraries(Alber PRIVATE FSUI::backend-sdl FSUI::donor)
|
||||||
|
target_compile_definitions(Alber PRIVATE PANDA3DS_FSUI_ICON_DIR="${PROJECT_SOURCE_DIR}/docs/img")
|
||||||
|
endif()
|
||||||
target_sources(Alber PRIVATE ${FRONTEND_SOURCE_FILES} ${FRONTEND_HEADER_FILES} ${GL_CONTEXT_SOURCE_FILES} ${APP_RESOURCES})
|
target_sources(Alber PRIVATE ${FRONTEND_SOURCE_FILES} ${FRONTEND_HEADER_FILES} ${GL_CONTEXT_SOURCE_FILES} ${APP_RESOURCES})
|
||||||
elseif(BUILD_HYDRA_CORE)
|
elseif(BUILD_HYDRA_CORE)
|
||||||
target_compile_definitions(AlberCore PRIVATE PANDA3DS_HYDRA_CORE=1)
|
target_compile_definitions(AlberCore PRIVATE PANDA3DS_HYDRA_CORE=1)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "audio/dsp_core.hpp"
|
#include "audio/dsp_core.hpp"
|
||||||
#include "frontend_settings.hpp"
|
#include "frontend_settings.hpp"
|
||||||
@@ -108,6 +109,21 @@ struct EmulatorConfig {
|
|||||||
std::filesystem::path defaultRomPath = "";
|
std::filesystem::path defaultRomPath = "";
|
||||||
std::filesystem::path filePath;
|
std::filesystem::path filePath;
|
||||||
|
|
||||||
|
static constexpr size_t maxRecentGames = 8;
|
||||||
|
std::vector<std::filesystem::path> recentlyPlayed;
|
||||||
|
std::vector<std::filesystem::path> fsuiGameListPaths;
|
||||||
|
std::vector<std::filesystem::path> fsuiGameListRecursivePaths;
|
||||||
|
std::filesystem::path fsuiCoversPath = "";
|
||||||
|
int fsuiDefaultGameView = 0;
|
||||||
|
int fsuiGameSort = 0;
|
||||||
|
bool fsuiGameSortReverse = false;
|
||||||
|
std::string fsuiTheme = "Dark";
|
||||||
|
std::string fsuiPromptIconPack = "Auto";
|
||||||
|
std::filesystem::path fsuiBackgroundImagePath = "";
|
||||||
|
bool fsuiShowInputsOverlay = false;
|
||||||
|
bool fsuiShowSettingsOverlay = false;
|
||||||
|
bool fsuiShowPerformanceOverlay = false;
|
||||||
|
|
||||||
// Frontend window settings
|
// Frontend window settings
|
||||||
struct WindowSettings {
|
struct WindowSettings {
|
||||||
static constexpr int defaultX = 200;
|
static constexpr int defaultX = 200;
|
||||||
@@ -132,6 +148,8 @@ struct EmulatorConfig {
|
|||||||
void load();
|
void load();
|
||||||
void save();
|
void save();
|
||||||
|
|
||||||
|
void addToRecentGames(const std::filesystem::path& path);
|
||||||
|
|
||||||
static LanguageCodes languageCodeFromString(std::string inString);
|
static LanguageCodes languageCodeFromString(std::string inString);
|
||||||
static const char* languageCodeToString(LanguageCodes code);
|
static const char* languageCodeToString(LanguageCodes code);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ class Emulator {
|
|||||||
ServiceManager& getServiceManager() { return kernel.getServiceManager(); }
|
ServiceManager& getServiceManager() { return kernel.getServiceManager(); }
|
||||||
LuaManager& getLua() { return lua; }
|
LuaManager& getLua() { return lua; }
|
||||||
AudioDeviceInterface& getAudioDevice() { return audioDevice; }
|
AudioDeviceInterface& getAudioDevice() { return audioDevice; }
|
||||||
|
const std::optional<std::filesystem::path>& getROMPath() const { return romPath; }
|
||||||
|
|
||||||
RendererType getRendererType() const { return config.rendererType; }
|
RendererType getRendererType() const { return config.rendererType; }
|
||||||
Renderer* getRenderer() { return gpu.getRenderer(); }
|
Renderer* getRenderer() { return gpu.getRenderer(); }
|
||||||
|
|||||||
@@ -28,11 +28,17 @@ struct FrontendSettings {
|
|||||||
WindowIcon icon = WindowIcon::Rpog;
|
WindowIcon icon = WindowIcon::Rpog;
|
||||||
std::string language = "en";
|
std::string language = "en";
|
||||||
bool showImGuiDebugPanel = true;
|
bool showImGuiDebugPanel = true;
|
||||||
|
bool enableFullscreenUI = false;
|
||||||
|
#ifdef IMGUI_FRONTEND
|
||||||
|
bool stretchImGuiOutputToWindow = true;
|
||||||
|
#else
|
||||||
bool stretchImGuiOutputToWindow = false;
|
bool stretchImGuiOutputToWindow = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
static Theme themeFromString(std::string inString);
|
static Theme themeFromString(std::string inString);
|
||||||
static const char* themeToString(Theme theme);
|
static const char* themeToString(Theme theme);
|
||||||
|
|
||||||
static WindowIcon iconFromString(std::string inString);
|
static WindowIcon iconFromString(std::string inString);
|
||||||
static const char* iconToString(WindowIcon icon);
|
static const char* iconToString(WindowIcon icon);
|
||||||
|
static bool defaultFullscreenUIEnabled();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ class MainWindow : public QMainWindow {
|
|||||||
std::vector<EmulatorMessage> messageQueue;
|
std::vector<EmulatorMessage> messageQueue;
|
||||||
|
|
||||||
QMenuBar* menuBar = nullptr;
|
QMenuBar* menuBar = nullptr;
|
||||||
|
QMenu* recentsMenu = nullptr;
|
||||||
InputMappings keyboardMappings;
|
InputMappings keyboardMappings;
|
||||||
ScreenWidget* screen;
|
ScreenWidget* screen;
|
||||||
AboutWindow* aboutWindow;
|
AboutWindow* aboutWindow;
|
||||||
@@ -123,6 +124,8 @@ class MainWindow : public QMainWindow {
|
|||||||
void emuThreadMainLoop();
|
void emuThreadMainLoop();
|
||||||
void selectLuaFile();
|
void selectLuaFile();
|
||||||
void selectROM();
|
void selectROM();
|
||||||
|
void loadROMFromPath(const std::filesystem::path& path);
|
||||||
|
void updateRecentsMenu();
|
||||||
void dumpDspFirmware();
|
void dumpDspFirmware();
|
||||||
void dumpRomFS();
|
void dumpRomFS();
|
||||||
void showAboutMenu();
|
void showAboutMenu();
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ class FrontendSDL {
|
|||||||
bool keyboardAnalogY = false;
|
bool keyboardAnalogY = false;
|
||||||
bool emuPaused = false;
|
bool emuPaused = false;
|
||||||
bool returnToSelector = false;
|
bool returnToSelector = false;
|
||||||
|
#ifdef IMGUI_FRONTEND
|
||||||
|
bool controllerStartHeld = false;
|
||||||
|
bool controllerSelectHeld = false;
|
||||||
|
bool controllerPauseComboArmed = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupControllerSensors(SDL_GameController* controller);
|
void setupControllerSensors(SDL_GameController* controller);
|
||||||
|
|||||||
@@ -4,10 +4,17 @@
|
|||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "emulator.hpp"
|
#include "emulator.hpp"
|
||||||
|
#include "fsui/backend_sdl.hpp"
|
||||||
|
#include "fsui/fsui.hpp"
|
||||||
|
#include "panda_sdl/panda_fsui.hpp"
|
||||||
|
|
||||||
|
struct ImFont;
|
||||||
|
|
||||||
class ImGuiLayer {
|
class ImGuiLayer {
|
||||||
public:
|
public:
|
||||||
@@ -27,15 +34,27 @@ class ImGuiLayer {
|
|||||||
void setPauseCallback(std::function<void(bool)> callback) { onPauseChange = std::move(callback); }
|
void setPauseCallback(std::function<void(bool)> callback) { onPauseChange = std::move(callback); }
|
||||||
void setVsyncCallback(std::function<void(bool)> callback) { onVsyncChange = std::move(callback); }
|
void setVsyncCallback(std::function<void(bool)> callback) { onVsyncChange = std::move(callback); }
|
||||||
void setExitToSelectorCallback(std::function<void()> callback) { onExitToSelector = std::move(callback); }
|
void setExitToSelectorCallback(std::function<void()> callback) { onExitToSelector = std::move(callback); }
|
||||||
|
void showPauseMenuFromController();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool useFullscreenUI();
|
||||||
void drawDebugPanel();
|
void drawDebugPanel();
|
||||||
void drawPausePanel();
|
void drawClassicPausePanel();
|
||||||
void drawSettingsPanel();
|
void drawClassicSettingsPanel();
|
||||||
|
void drawSettingsGeneralSection(bool& reloadSettings);
|
||||||
|
void drawSettingsWindowSection(bool& reloadSettings);
|
||||||
|
void drawSettingsUISection(bool& reloadSettings);
|
||||||
|
void drawSettingsGraphicsSection(bool& reloadSettings);
|
||||||
|
void drawSettingsAudioSection(bool& reloadSettings);
|
||||||
|
void drawSettingsBatterySection(bool& reloadSettings);
|
||||||
|
void drawSettingsSDSection(bool& reloadSettings);
|
||||||
|
|
||||||
SDL_Window* window = nullptr;
|
SDL_Window* window = nullptr;
|
||||||
SDL_GLContext glContext = nullptr;
|
SDL_GLContext glContext = nullptr;
|
||||||
Emulator& emu;
|
Emulator& emu;
|
||||||
|
fsui::FontStack fontStack;
|
||||||
|
fsui::SdlImGuiBackend imguiBackend;
|
||||||
|
PandaFsuiAdapter fsuiAdapter;
|
||||||
|
|
||||||
bool showDebug = true;
|
bool showDebug = true;
|
||||||
bool showPauseMenu = false;
|
bool showPauseMenu = false;
|
||||||
@@ -43,6 +62,7 @@ class ImGuiLayer {
|
|||||||
bool isPaused = false;
|
bool isPaused = false;
|
||||||
bool captureKeyboard = false;
|
bool captureKeyboard = false;
|
||||||
bool captureMouse = false;
|
bool captureMouse = false;
|
||||||
|
bool fullscreenSelectorMode = false;
|
||||||
|
|
||||||
std::function<void(bool)> onPauseChange;
|
std::function<void(bool)> onPauseChange;
|
||||||
std::function<void(bool)> onVsyncChange;
|
std::function<void(bool)> onVsyncChange;
|
||||||
|
|||||||
95
include/panda_sdl/panda_fsui.hpp
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef IMGUI_FRONTEND
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "fsui/fsui.hpp"
|
||||||
|
|
||||||
|
class Emulator;
|
||||||
|
|
||||||
|
class PandaFsuiAdapter {
|
||||||
|
public:
|
||||||
|
enum class ClassicUiRequest {
|
||||||
|
None,
|
||||||
|
Return,
|
||||||
|
OpenSettings,
|
||||||
|
};
|
||||||
|
|
||||||
|
PandaFsuiAdapter(SDL_Window* window, Emulator& emu);
|
||||||
|
|
||||||
|
bool initialize(const fsui::FontStack& fonts);
|
||||||
|
void shutdown(bool clear_state);
|
||||||
|
void render();
|
||||||
|
void consumeCommands();
|
||||||
|
|
||||||
|
void setSelectorMode(bool selector_mode);
|
||||||
|
bool isSelectorMode() const;
|
||||||
|
bool hasActiveWindow() const;
|
||||||
|
void openPauseMenu();
|
||||||
|
void returnToPreviousWindow();
|
||||||
|
void returnToMainWindow();
|
||||||
|
void switchToSettings();
|
||||||
|
|
||||||
|
std::optional<std::filesystem::path> consumeLaunchPath();
|
||||||
|
bool consumeCloseSelector();
|
||||||
|
ClassicUiRequest consumeClassicUiRequest();
|
||||||
|
|
||||||
|
void setPauseCallback(std::function<void(bool)> callback);
|
||||||
|
void setVsyncCallback(std::function<void(bool)> callback);
|
||||||
|
void setExitToSelectorCallback(std::function<void()> callback);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ParsedMetadata;
|
||||||
|
struct LanguageOption;
|
||||||
|
|
||||||
|
void syncUiStateFromConfig();
|
||||||
|
void persistUiState(bool reload);
|
||||||
|
void seedGameListPathsIfNeeded();
|
||||||
|
std::vector<std::filesystem::path> currentScanRoots() const;
|
||||||
|
bool hasSupportedExtension(const std::filesystem::path& path) const;
|
||||||
|
std::vector<fsui::GameEntry> buildGameList();
|
||||||
|
std::vector<fsui::SettingsPageDescriptor> buildSettingsPages(fsui::SettingsScope scope);
|
||||||
|
fsui::CurrentGameInfo buildCurrentGameInfo();
|
||||||
|
std::vector<fsui::MenuItemDescriptor> buildLandingItems();
|
||||||
|
std::vector<fsui::MenuItemDescriptor> buildStartItems();
|
||||||
|
std::vector<fsui::MenuItemDescriptor> buildExitItems();
|
||||||
|
std::vector<fsui::MenuItemDescriptor> buildPauseItems();
|
||||||
|
std::vector<fsui::MenuItemDescriptor> buildGameLaunchOptions(const fsui::GameEntry& entry);
|
||||||
|
|
||||||
|
std::optional<ParsedMetadata> readMetadataForPath(const std::filesystem::path& path) const;
|
||||||
|
fsui::GameEntry buildGameListEntry(const std::filesystem::directory_entry& entry) const;
|
||||||
|
std::filesystem::path defaultCoverDirectory() const;
|
||||||
|
std::filesystem::path findCoverPath(const std::filesystem::path& rom_path, const std::string& title_id) const;
|
||||||
|
std::string currentGameTitle() const;
|
||||||
|
std::string currentGameSubtitle() const;
|
||||||
|
std::string formatPercent(float value) const;
|
||||||
|
std::string formatInteger(int value) const;
|
||||||
|
std::string formatTitleId(std::uint64_t program_id) const;
|
||||||
|
std::vector<fsui::OverlayTextLine> buildPerformanceOverlayLines() const;
|
||||||
|
std::vector<fsui::OverlayTextLine> buildSettingsOverlayLines() const;
|
||||||
|
std::vector<fsui::InputOverlayDeviceState> buildInputOverlayDevices() const;
|
||||||
|
void openFileAndLaunch();
|
||||||
|
void requestLaunchPath(const std::filesystem::path& path);
|
||||||
|
void requestClassicUi(bool open_settings);
|
||||||
|
void openUnsupportedPrompt(std::string title, std::string message) const;
|
||||||
|
|
||||||
|
SDL_Window* window = nullptr;
|
||||||
|
Emulator& emu;
|
||||||
|
fsui::UiContext fsuiContext;
|
||||||
|
fsui::UiState uiState;
|
||||||
|
std::vector<fsui::GameEntry> cachedGameList;
|
||||||
|
std::optional<std::filesystem::path> pendingLaunchPath;
|
||||||
|
bool closeSelectorRequested = false;
|
||||||
|
ClassicUiRequest pendingClassicUiRequest = ClassicUiRequest::None;
|
||||||
|
std::function<void(bool)> onPauseChange;
|
||||||
|
std::function<void(bool)> onVsyncChange;
|
||||||
|
std::function<void()> onExitToSelector;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
115
src/config.cpp
@@ -15,7 +15,10 @@
|
|||||||
// We are legally allowed, as per the author's wish, to use the above code without any licensing restrictions
|
// We are legally allowed, as per the author's wish, to use the above code without any licensing restrictions
|
||||||
// However we still want to follow the license as closely as possible and offer the proper attributions.
|
// However we still want to follow the license as closely as possible and offer the proper attributions.
|
||||||
|
|
||||||
EmulatorConfig::EmulatorConfig(const std::filesystem::path& path) : filePath(path) { load(); }
|
EmulatorConfig::EmulatorConfig(const std::filesystem::path& path) : filePath(path) {
|
||||||
|
frontendSettings.enableFullscreenUI = FrontendSettings::defaultFullscreenUIEnabled();
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
void EmulatorConfig::load() {
|
void EmulatorConfig::load() {
|
||||||
const std::filesystem::path& path = filePath;
|
const std::filesystem::path& path = filePath;
|
||||||
@@ -50,6 +53,48 @@ void EmulatorConfig::load() {
|
|||||||
circlePadProEnabled = toml::find_or<toml::boolean>(general, "EnableCirclePadPro", true);
|
circlePadProEnabled = toml::find_or<toml::boolean>(general, "EnableCirclePadPro", true);
|
||||||
fastmemEnabled = toml::find_or<toml::boolean>(general, "EnableFastmem", enableFastmemDefault);
|
fastmemEnabled = toml::find_or<toml::boolean>(general, "EnableFastmem", enableFastmemDefault);
|
||||||
systemLanguage = languageCodeFromString(toml::find_or<std::string>(general, "SystemLanguage", "en"));
|
systemLanguage = languageCodeFromString(toml::find_or<std::string>(general, "SystemLanguage", "en"));
|
||||||
|
|
||||||
|
// Load recent games list
|
||||||
|
if (general.contains("RecentGames") && general.at("RecentGames").is_array()) {
|
||||||
|
const auto& recentsArray = general.at("RecentGames").as_array();
|
||||||
|
recentlyPlayed.clear();
|
||||||
|
|
||||||
|
for (const auto& item : recentsArray) {
|
||||||
|
if (item.is_string()) {
|
||||||
|
std::filesystem::path gamePath = toml::get<std::string>(item);
|
||||||
|
|
||||||
|
recentlyPlayed.push_back(gamePath);
|
||||||
|
if (recentlyPlayed.size() >= maxRecentGames) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.contains("GameList")) {
|
||||||
|
auto gameListResult = toml::expect<toml::value>(data.at("GameList"));
|
||||||
|
if (gameListResult.is_ok()) {
|
||||||
|
auto gameList = gameListResult.unwrap();
|
||||||
|
|
||||||
|
fsuiGameListPaths.clear();
|
||||||
|
if (gameList.contains("Paths") && gameList.at("Paths").is_array()) {
|
||||||
|
for (const auto& item : gameList.at("Paths").as_array()) {
|
||||||
|
if (item.is_string()) {
|
||||||
|
fsuiGameListPaths.emplace_back(toml::get<std::string>(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fsuiGameListRecursivePaths.clear();
|
||||||
|
if (gameList.contains("RecursivePaths") && gameList.at("RecursivePaths").is_array()) {
|
||||||
|
for (const auto& item : gameList.at("RecursivePaths").as_array()) {
|
||||||
|
if (item.is_string()) {
|
||||||
|
fsuiGameListRecursivePaths.emplace_back(toml::get<std::string>(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +198,30 @@ void EmulatorConfig::load() {
|
|||||||
frontendSettings.icon = FrontendSettings::iconFromString(toml::find_or<std::string>(ui, "WindowIcon", "rpog"));
|
frontendSettings.icon = FrontendSettings::iconFromString(toml::find_or<std::string>(ui, "WindowIcon", "rpog"));
|
||||||
frontendSettings.language = toml::find_or<std::string>(ui, "Language", "en");
|
frontendSettings.language = toml::find_or<std::string>(ui, "Language", "en");
|
||||||
frontendSettings.showImGuiDebugPanel = toml::find_or<toml::boolean>(ui, "ShowImGuiDebugPanel", true);
|
frontendSettings.showImGuiDebugPanel = toml::find_or<toml::boolean>(ui, "ShowImGuiDebugPanel", true);
|
||||||
|
frontendSettings.enableFullscreenUI =
|
||||||
|
toml::find_or<toml::boolean>(ui, "EnableFullscreenUI", FrontendSettings::defaultFullscreenUIEnabled());
|
||||||
|
fsuiTheme = toml::find_or<std::string>(ui, "FullscreenUITheme", "Dark");
|
||||||
|
fsuiPromptIconPack = toml::find_or<std::string>(ui, "FullscreenUIPromptIcons", "Auto");
|
||||||
|
fsuiBackgroundImagePath = toml::find_or<std::string>(ui, "FullscreenUIBackgroundImage", "");
|
||||||
|
fsuiDefaultGameView = static_cast<int>(toml::find_or<toml::integer>(ui, "DefaultFullscreenUIGameView", 0));
|
||||||
|
fsuiGameSort = static_cast<int>(toml::find_or<toml::integer>(ui, "FullscreenUIGameSort", 0));
|
||||||
|
fsuiGameSortReverse = toml::find_or<toml::boolean>(ui, "FullscreenUIGameSortReverse", false);
|
||||||
|
fsuiShowInputsOverlay = toml::find_or<toml::boolean>(ui, "FullscreenUIShowInputs", false);
|
||||||
|
fsuiShowSettingsOverlay = toml::find_or<toml::boolean>(ui, "FullscreenUIShowSettings", false);
|
||||||
|
fsuiShowPerformanceOverlay = toml::find_or<toml::boolean>(ui, "FullscreenUIShowPerformance", false);
|
||||||
|
#ifdef IMGUI_FRONTEND
|
||||||
|
frontendSettings.stretchImGuiOutputToWindow = toml::find_or<toml::boolean>(ui, "StretchImGuiOutputToWindow", true);
|
||||||
|
#else
|
||||||
frontendSettings.stretchImGuiOutputToWindow = toml::find_or<toml::boolean>(ui, "StretchImGuiOutputToWindow", false);
|
frontendSettings.stretchImGuiOutputToWindow = toml::find_or<toml::boolean>(ui, "StretchImGuiOutputToWindow", false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.contains("Folders")) {
|
||||||
|
auto foldersResult = toml::expect<toml::value>(data.at("Folders"));
|
||||||
|
if (foldersResult.is_ok()) {
|
||||||
|
auto folders = foldersResult.unwrap();
|
||||||
|
fsuiCoversPath = toml::find_or<std::string>(folders, "Covers", "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,6 +253,24 @@ void EmulatorConfig::save() {
|
|||||||
data["General"]["EnableCirclePadPro"] = circlePadProEnabled;
|
data["General"]["EnableCirclePadPro"] = circlePadProEnabled;
|
||||||
data["General"]["EnableFastmem"] = fastmemEnabled;
|
data["General"]["EnableFastmem"] = fastmemEnabled;
|
||||||
|
|
||||||
|
toml::array recentsArray;
|
||||||
|
for (const auto& gamePath : recentlyPlayed) {
|
||||||
|
recentsArray.push_back(gamePath.string());
|
||||||
|
}
|
||||||
|
data["General"]["RecentGames"] = recentsArray;
|
||||||
|
|
||||||
|
toml::array pathsArray;
|
||||||
|
for (const auto& path : fsuiGameListPaths) {
|
||||||
|
pathsArray.push_back(path.string());
|
||||||
|
}
|
||||||
|
data["GameList"]["Paths"] = pathsArray;
|
||||||
|
|
||||||
|
toml::array recursivePathsArray;
|
||||||
|
for (const auto& path : fsuiGameListRecursivePaths) {
|
||||||
|
recursivePathsArray.push_back(path.string());
|
||||||
|
}
|
||||||
|
data["GameList"]["RecursivePaths"] = recursivePathsArray;
|
||||||
|
|
||||||
data["Window"]["AppVersionOnWindow"] = windowSettings.showAppVersion;
|
data["Window"]["AppVersionOnWindow"] = windowSettings.showAppVersion;
|
||||||
data["Window"]["RememberWindowPosition"] = windowSettings.rememberPosition;
|
data["Window"]["RememberWindowPosition"] = windowSettings.rememberPosition;
|
||||||
data["Window"]["WindowPosX"] = windowSettings.x;
|
data["Window"]["WindowPosX"] = windowSettings.x;
|
||||||
@@ -223,7 +309,18 @@ void EmulatorConfig::save() {
|
|||||||
data["UI"]["WindowIcon"] = std::string(FrontendSettings::iconToString(frontendSettings.icon));
|
data["UI"]["WindowIcon"] = std::string(FrontendSettings::iconToString(frontendSettings.icon));
|
||||||
data["UI"]["Language"] = frontendSettings.language;
|
data["UI"]["Language"] = frontendSettings.language;
|
||||||
data["UI"]["ShowImGuiDebugPanel"] = frontendSettings.showImGuiDebugPanel;
|
data["UI"]["ShowImGuiDebugPanel"] = frontendSettings.showImGuiDebugPanel;
|
||||||
|
data["UI"]["EnableFullscreenUI"] = frontendSettings.enableFullscreenUI;
|
||||||
|
data["UI"]["FullscreenUITheme"] = fsuiTheme;
|
||||||
|
data["UI"]["FullscreenUIPromptIcons"] = fsuiPromptIconPack;
|
||||||
|
data["UI"]["FullscreenUIBackgroundImage"] = fsuiBackgroundImagePath.string();
|
||||||
|
data["UI"]["DefaultFullscreenUIGameView"] = fsuiDefaultGameView;
|
||||||
|
data["UI"]["FullscreenUIGameSort"] = fsuiGameSort;
|
||||||
|
data["UI"]["FullscreenUIGameSortReverse"] = fsuiGameSortReverse;
|
||||||
|
data["UI"]["FullscreenUIShowInputs"] = fsuiShowInputsOverlay;
|
||||||
|
data["UI"]["FullscreenUIShowSettings"] = fsuiShowSettingsOverlay;
|
||||||
|
data["UI"]["FullscreenUIShowPerformance"] = fsuiShowPerformanceOverlay;
|
||||||
data["UI"]["StretchImGuiOutputToWindow"] = frontendSettings.stretchImGuiOutputToWindow;
|
data["UI"]["StretchImGuiOutputToWindow"] = frontendSettings.stretchImGuiOutputToWindow;
|
||||||
|
data["Folders"]["Covers"] = fsuiCoversPath.string();
|
||||||
|
|
||||||
std::ofstream file(path, std::ios::out);
|
std::ofstream file(path, std::ios::out);
|
||||||
file << data;
|
file << data;
|
||||||
@@ -281,4 +378,18 @@ const char* EmulatorConfig::languageCodeToString(LanguageCodes code) {
|
|||||||
} else {
|
} else {
|
||||||
return codes[static_cast<u32>(code)];
|
return codes[static_cast<u32>(code)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void EmulatorConfig::addToRecentGames(const std::filesystem::path& path) {
|
||||||
|
// Remove path if it's already in the list
|
||||||
|
auto it = std::find(recentlyPlayed.begin(), recentlyPlayed.end(), path);
|
||||||
|
if (it != recentlyPlayed.end()) {
|
||||||
|
recentlyPlayed.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
recentlyPlayed.insert(recentlyPlayed.begin(), path);
|
||||||
|
|
||||||
|
// Limit how many games can be saved
|
||||||
|
if (recentlyPlayed.size() > maxRecentGames) {
|
||||||
|
recentlyPlayed.resize(maxRecentGames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#if defined(__WINRT__) && !defined(__ANDROID__)
|
||||||
|
#include <SDL.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
// Frontend setting serialization/deserialization functions
|
// Frontend setting serialization/deserialization functions
|
||||||
|
|
||||||
FrontendSettings::Theme FrontendSettings::themeFromString(std::string inString) {
|
FrontendSettings::Theme FrontendSettings::themeFromString(std::string inString) {
|
||||||
@@ -62,4 +66,14 @@ const char* FrontendSettings::iconToString(WindowIcon icon) {
|
|||||||
case WindowIcon::Rpog:
|
case WindowIcon::Rpog:
|
||||||
default: return "rpog";
|
default: return "rpog";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FrontendSettings::defaultFullscreenUIEnabled() {
|
||||||
|
#if defined(__WINRT__) && !defined(__ANDROID__)
|
||||||
|
return SDL_WinRTGetDeviceFamily() == SDL_WINRT_DEVICEFAMILY_XBOX;
|
||||||
|
#elif defined(__XBOXONE__) || defined(__XBOXSERIES__)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,6 +53,11 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
|
|||||||
|
|
||||||
// Create and bind actions for them
|
// Create and bind actions for them
|
||||||
auto loadGameAction = fileMenu->addAction(tr("Load game"));
|
auto loadGameAction = fileMenu->addAction(tr("Load game"));
|
||||||
|
|
||||||
|
recentsMenu = fileMenu->addMenu(tr("Recents"));
|
||||||
|
updateRecentsMenu();
|
||||||
|
|
||||||
|
fileMenu->addSeparator();
|
||||||
auto loadLuaAction = fileMenu->addAction(tr("Load Lua script"));
|
auto loadLuaAction = fileMenu->addAction(tr("Load Lua script"));
|
||||||
auto openAppFolderAction = fileMenu->addAction(tr("Open Panda3DS folder"));
|
auto openAppFolderAction = fileMenu->addAction(tr("Open Panda3DS folder"));
|
||||||
|
|
||||||
@@ -140,6 +145,10 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
|
|||||||
if (!emu->loadROM(romPath)) {
|
if (!emu->loadROM(romPath)) {
|
||||||
// For some reason just .c_str() doesn't show the proper path
|
// For some reason just .c_str() doesn't show the proper path
|
||||||
Helpers::warn("Failed to load ROM file: %s", romPath.string().c_str());
|
Helpers::warn("Failed to load ROM file: %s", romPath.string().c_str());
|
||||||
|
} else {
|
||||||
|
emu->getConfig().addToRecentGames(romPath);
|
||||||
|
emu->getConfig().save();
|
||||||
|
updateRecentsMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,11 +249,48 @@ void MainWindow::selectROM() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!path.isEmpty()) {
|
if (!path.isEmpty()) {
|
||||||
std::filesystem::path* p = new std::filesystem::path(path.toStdU16String());
|
loadROMFromPath(std::filesystem::path(path.toStdU16String()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EmulatorMessage message{.type = MessageType::LoadROM};
|
void MainWindow::loadROMFromPath(const std::filesystem::path& path) {
|
||||||
message.path.p = p;
|
std::filesystem::path* p = new std::filesystem::path(path);
|
||||||
sendMessage(message);
|
|
||||||
|
EmulatorMessage message{.type = MessageType::LoadROM};
|
||||||
|
message.path.p = p;
|
||||||
|
sendMessage(message);
|
||||||
|
|
||||||
|
emu->getConfig().addToRecentGames(path);
|
||||||
|
emu->getConfig().save();
|
||||||
|
updateRecentsMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateRecentsMenu() {
|
||||||
|
recentsMenu->clear();
|
||||||
|
const auto& recentGames = emu->getConfig().recentlyPlayed;
|
||||||
|
|
||||||
|
if (recentGames.empty()) {
|
||||||
|
// Add a disabled "No recent games" item
|
||||||
|
QAction* noRecentsAction = recentsMenu->addAction(tr("No recent games"));
|
||||||
|
noRecentsAction->setEnabled(false);
|
||||||
|
} else {
|
||||||
|
for (const auto& gamePath : recentGames) {
|
||||||
|
QString displayName = QString::fromStdU16String(gamePath.filename().u16string());
|
||||||
|
QAction* action = recentsMenu->addAction(displayName);
|
||||||
|
|
||||||
|
// Store the full path in the action's data, set tooltip to show full path
|
||||||
|
action->setData(QString::fromStdU16String(gamePath.u16string()));
|
||||||
|
action->setToolTip(QString::fromStdU16String(gamePath.u16string()));
|
||||||
|
connect(action, &QAction::triggered, this, [this, gamePath]() { loadROMFromPath(gamePath); });
|
||||||
|
}
|
||||||
|
|
||||||
|
recentsMenu->addSeparator();
|
||||||
|
QAction* clearAction = recentsMenu->addAction(tr("Clear recent games"));
|
||||||
|
connect(clearAction, &QAction::triggered, this, [this]() {
|
||||||
|
emu->getConfig().recentlyPlayed.clear();
|
||||||
|
emu->getConfig().save();
|
||||||
|
updateRecentsMenu();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ FrontendSDL::FrontendSDL(SDL_Window* existingWindow, SDL_GLContext existingConte
|
|||||||
FrontendSDL::ImGuiWindowContext FrontendSDL::createImGuiWindowContext(const EmulatorConfig& bootConfig, const char* windowTitle) {
|
FrontendSDL::ImGuiWindowContext FrontendSDL::createImGuiWindowContext(const EmulatorConfig& bootConfig, const char* windowTitle) {
|
||||||
int windowX = SDL_WINDOWPOS_CENTERED;
|
int windowX = SDL_WINDOWPOS_CENTERED;
|
||||||
int windowY = SDL_WINDOWPOS_CENTERED;
|
int windowY = SDL_WINDOWPOS_CENTERED;
|
||||||
int windowW = 400;
|
int windowW = 640;
|
||||||
int windowH = 480;
|
int windowH = 360;
|
||||||
if (bootConfig.windowSettings.rememberPosition) {
|
if (bootConfig.windowSettings.rememberPosition) {
|
||||||
windowX = bootConfig.windowSettings.x;
|
windowX = bootConfig.windowSettings.x;
|
||||||
windowY = bootConfig.windowSettings.y;
|
windowY = bootConfig.windowSettings.y;
|
||||||
@@ -115,8 +115,13 @@ void FrontendSDL::initialize(SDL_Window* existingWindow, SDL_GLContext existingC
|
|||||||
} else {
|
} else {
|
||||||
windowX = SDL_WINDOWPOS_CENTERED;
|
windowX = SDL_WINDOWPOS_CENTERED;
|
||||||
windowY = SDL_WINDOWPOS_CENTERED;
|
windowY = SDL_WINDOWPOS_CENTERED;
|
||||||
|
#ifdef IMGUI_FRONTEND
|
||||||
|
windowWidth = 640;
|
||||||
|
windowHeight = 360;
|
||||||
|
#else
|
||||||
windowWidth = 400;
|
windowWidth = 400;
|
||||||
windowHeight = 480;
|
windowHeight = 480;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize output size and screen layout
|
// Initialize output size and screen layout
|
||||||
@@ -293,7 +298,20 @@ void FrontendSDL::initialize(SDL_Window* existingWindow, SDL_GLContext existingC
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FrontendSDL::loadROM(const std::filesystem::path& path) { return emu.loadROM(path); }
|
bool FrontendSDL::loadROM(const std::filesystem::path& path) {
|
||||||
|
const bool loaded = emu.loadROM(path);
|
||||||
|
if (loaded) {
|
||||||
|
emuPaused = false;
|
||||||
|
#ifdef IMGUI_FRONTEND
|
||||||
|
if (imgui) {
|
||||||
|
imgui->setPaused(false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
emu.getConfig().addToRecentGames(path);
|
||||||
|
emu.getConfig().save();
|
||||||
|
}
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::filesystem::path> FrontendSDL::selectGame() {
|
std::optional<std::filesystem::path> FrontendSDL::selectGame() {
|
||||||
#ifdef IMGUI_FRONTEND
|
#ifdef IMGUI_FRONTEND
|
||||||
@@ -309,22 +327,32 @@ void FrontendSDL::run() {
|
|||||||
keyboardAnalogX = false;
|
keyboardAnalogX = false;
|
||||||
keyboardAnalogY = false;
|
keyboardAnalogY = false;
|
||||||
holdingRightClick = false;
|
holdingRightClick = false;
|
||||||
|
#ifdef IMGUI_FRONTEND
|
||||||
|
int lastDrawableW = -1;
|
||||||
|
int lastDrawableH = -1;
|
||||||
|
controllerStartHeld = false;
|
||||||
|
controllerSelectHeld = false;
|
||||||
|
controllerPauseComboArmed = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
while (programRunning) {
|
while (programRunning) {
|
||||||
#ifdef IMGUI_FRONTEND
|
#ifdef IMGUI_FRONTEND
|
||||||
const auto& cfg = emu.getConfig();
|
const auto& cfg = emu.getConfig();
|
||||||
if (cfg.frontendSettings.stretchImGuiOutputToWindow) {
|
int drawableW = 0;
|
||||||
int drawableW = 0;
|
int drawableH = 0;
|
||||||
int drawableH = 0;
|
SDL_GL_GetDrawableSize(window, &drawableW, &drawableH);
|
||||||
SDL_GL_GetDrawableSize(window, &drawableW, &drawableH);
|
if (drawableW > 0 && drawableH > 0 && (drawableW != lastDrawableW || drawableH != lastDrawableH)) {
|
||||||
if (drawableW > 0 && drawableH > 0) {
|
lastDrawableW = drawableW;
|
||||||
|
lastDrawableH = drawableH;
|
||||||
|
if (cfg.frontendSettings.stretchImGuiOutputToWindow) {
|
||||||
windowWidth = u32(drawableW);
|
windowWidth = u32(drawableW);
|
||||||
windowHeight = u32(drawableH);
|
windowHeight = u32(drawableH);
|
||||||
emu.setOutputSize(windowWidth, windowHeight);
|
emu.setOutputSize(windowWidth, windowHeight);
|
||||||
|
}
|
||||||
|
if (cfg.frontendSettings.stretchImGuiOutputToWindow) {
|
||||||
ScreenLayout::calculateCoordinates(
|
ScreenLayout::calculateCoordinates(
|
||||||
screenCoordinates, windowWidth, windowHeight, cfg.topScreenSize, cfg.screenLayout
|
screenCoordinates, windowWidth, windowHeight, cfg.topScreenSize, cfg.screenLayout
|
||||||
);
|
);
|
||||||
glViewport(0, 0, drawableW, drawableH);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -511,6 +539,24 @@ void FrontendSDL::run() {
|
|||||||
case SDL_CONTROLLER_BUTTON_START: key = Keys::Start; break;
|
case SDL_CONTROLLER_BUTTON_START: key = Keys::Start; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef IMGUI_FRONTEND
|
||||||
|
if (event.cbutton.button == SDL_CONTROLLER_BUTTON_START) {
|
||||||
|
controllerStartHeld = event.cbutton.state == SDL_PRESSED;
|
||||||
|
}
|
||||||
|
if (event.cbutton.button == SDL_CONTROLLER_BUTTON_BACK) {
|
||||||
|
controllerSelectHeld = event.cbutton.state == SDL_PRESSED;
|
||||||
|
}
|
||||||
|
if (event.cbutton.state == SDL_PRESSED && controllerStartHeld && controllerSelectHeld && controllerPauseComboArmed) {
|
||||||
|
controllerPauseComboArmed = false;
|
||||||
|
if (imgui) {
|
||||||
|
imgui->showPauseMenuFromController();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (event.cbutton.state == SDL_RELEASED && (!controllerStartHeld && !controllerSelectHeld)) {
|
||||||
|
controllerPauseComboArmed = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (key != 0) {
|
if (key != 0) {
|
||||||
if (event.cbutton.state == SDL_PRESSED) {
|
if (event.cbutton.state == SDL_PRESSED) {
|
||||||
hid.pressKey(key);
|
hid.pressKey(key);
|
||||||
@@ -684,7 +730,9 @@ void FrontendSDL::run() {
|
|||||||
// TODO: Should this be uncommented?
|
// TODO: Should this be uncommented?
|
||||||
// kernel.evalReschedule();
|
// kernel.evalReschedule();
|
||||||
|
|
||||||
|
#ifndef IMGUI_FRONTEND
|
||||||
SDL_GL_SwapWindow(window);
|
SDL_GL_SwapWindow(window);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef IMGUI_FRONTEND
|
#ifdef IMGUI_FRONTEND
|
||||||
@@ -767,4 +815,4 @@ void FrontendSDL::togglePaused() {
|
|||||||
setPaused(!emuPaused);
|
setPaused(!emuPaused);
|
||||||
}
|
}
|
||||||
|
|
||||||
FrontendSDL::~FrontendSDL() = default;
|
FrontendSDL::~FrontendSDL() = default;
|
||||||
|
|||||||
1488
src/panda_sdl/panda_fsui.cpp
Normal file
2
third_party/discord-rpc
vendored
1
third_party/fsui
vendored
Submodule
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 19 KiB |