Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Qt: Add config window controls #655

Merged
merged 13 commits into from
Dec 1, 2024
6 changes: 5 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ set(SOURCE_FILES src/emulator.cpp src/io_file.cpp src/config.cpp
src/core/memory.cpp src/renderer.cpp src/core/renderer_null/renderer_null.cpp
src/http_server.cpp src/stb_image_write.c src/core/cheats.cpp src/core/action_replay.cpp
src/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp src/miniaudio.cpp src/renderdoc.cpp
src/frontend_settings.cpp
)
set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp)
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
Expand Down Expand Up @@ -361,7 +362,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
include/PICA/pica_vert_config.hpp include/sdl_sensors.hpp include/PICA/draw_acceleration.hpp include/renderdoc.hpp
include/align.hpp include/audio/aac_decoder.hpp include/PICA/pica_simd.hpp include/services/fonts.hpp
include/audio/audio_interpolation.hpp include/audio/hle_mixer.hpp include/audio/dsp_simd.hpp
include/services/dsp_firmware_db.hpp
include/services/dsp_firmware_db.hpp include/frontend_settings.hpp
)

cmrc_add_resource_library(
Expand Down Expand Up @@ -695,6 +696,9 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
PREFIX "/"
FILES
docs/img/rsob_icon.png docs/img/rstarstruck_icon.png docs/img/rpog_icon.png docs/img/rsyn_icon.png
docs/img/settings_icon.png docs/img/display_icon.png docs/img/speaker_icon.png
docs/img/sparkling_icon.png docs/img/battery_icon.png docs/img/sdcard_icon.png
docs/img/rnap_icon.png docs/img/rcow_icon.png
)
else()
set(FRONTEND_SOURCE_FILES src/panda_sdl/main.cpp src/panda_sdl/frontend_sdl.cpp src/panda_sdl/mappings.cpp)
Expand Down
Binary file added docs/img/battery_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/display_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/rcow_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/rnap_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/sdcard_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/settings_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/sparkling_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/speaker_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion include/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "audio/dsp_core.hpp"
#include "renderer.hpp"
#include "frontend_settings.hpp"

struct AudioDeviceConfig {
float volumeRaw = 1.0f;
Expand Down Expand Up @@ -86,8 +87,9 @@ struct EmulatorConfig {

WindowSettings windowSettings;
AudioDeviceConfig audioDeviceConfig;
FrontendSettings frontendSettings;

EmulatorConfig(const std::filesystem::path& path);
void load();
void save();
};
};
2 changes: 2 additions & 0 deletions include/discord_rpc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ namespace Discord {
void init();
void update(RPCStatus status, const std::string& title);
void stop();

bool running() const { return enabled; }
};
} // namespace Discord

Expand Down
3 changes: 3 additions & 0 deletions include/emulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ class Emulator {
void setOutputSize(u32 width, u32 height) { gpu.setOutputSize(width, height); }
void deinitGraphicsContext() { gpu.deinitGraphicsContext(); }

// Reloads some settings that require special handling, such as audio enable
void reloadSettings();

EmulatorConfig& getConfig() { return config; }
Cheats& getCheats() { return cheats; }
ServiceManager& getServiceManager() { return kernel.getServiceManager(); }
Expand Down
32 changes: 32 additions & 0 deletions include/frontend_settings.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once
#include <string>

// Some UI settings that aren't fully frontend-dependent. Note: Not all frontends will support the same settings.
// Note: Any enums should ideally be ordered in the same order we want to show them in UI dropdown menus, so that we can cast indices to enums
// directly.
struct FrontendSettings {
enum class Theme : int {
System = 0,
Light = 1,
Dark = 2,
GreetingsCat = 3,
Cream = 4,
};

// Different panda-themed window icons
enum class WindowIcon : int {
Rpog = 0,
Rsyn = 1,
Rnap = 2,
Rcow = 3,
};

Theme theme = Theme::Dark;
WindowIcon icon = WindowIcon::Rpog;

static Theme themeFromString(std::string inString);
static const char* themeToString(Theme theme);

static WindowIcon iconFromString(std::string inString);
static const char* iconToString(WindowIcon icon);
};
48 changes: 37 additions & 11 deletions include/panda_qt/config_window.hpp
Original file line number Diff line number Diff line change
@@ -1,30 +1,56 @@
#pragma once

#include <QApplication>
#include <QCheckBox>
#include <QComboBox>
#include <QDialog>
#include <QListWidget>
#include <QPalette>
#include <QStackedWidget>
#include <QTextEdit>
#include <QWidget>
#include <QtWidgets>
#include <array>
#include <functional>
#include <utility>

#include "emulator.hpp"
#include "frontend_settings.hpp"

class ConfigWindow : public QDialog {
Q_OBJECT

private:
enum class Theme : int {
System = 0,
Light = 1,
Dark = 2,
GreetingsCat = 3,
Cream = 4,
};
using ConfigCallback = std::function<void()>;
using IconCallback = std::function<void(const QString&)>;

using Theme = FrontendSettings::Theme;
using WindowIcon = FrontendSettings::WindowIcon;

QTextEdit* helpText = nullptr;
QListWidget* widgetList = nullptr;
QStackedWidget* widgetContainer = nullptr;

static constexpr size_t settingWidgetCount = 6;
std::array<QString, settingWidgetCount> helpTexts;

Theme currentTheme;
QComboBox* themeSelect = nullptr;
// The config class holds a copy of the emulator config which it edits and sends
// over to the emulator in a thread-safe manner
EmulatorConfig config;

void setTheme(Theme theme);
ConfigCallback updateConfig;
IconCallback updateIcon;

void addWidget(QWidget* widget, QString title, QString icon, QString helpText);
void setTheme(FrontendSettings::Theme theme);
void setIcon(FrontendSettings::WindowIcon icon);

public:
ConfigWindow(QWidget* parent = nullptr);
ConfigWindow(ConfigCallback configCallback, IconCallback iconCallback, const EmulatorConfig& config, QWidget* parent = nullptr);
~ConfigWindow();

EmulatorConfig& getConfig() { return config; }

private:
Emulator* emu;
};
1 change: 1 addition & 0 deletions include/panda_qt/main_window.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class MainWindow : public QMainWindow {
ReleaseTouchscreen,
ReloadUbershader,
SetScreenSize,
UpdateConfig,
};

// Tagged union representing our message queue messages
Expand Down
4 changes: 4 additions & 0 deletions include/renderdoc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ namespace Renderdoc {
// Sets output directory for captures
void setOutputDir(const std::string& path, const std::string& prefix);

// Returns whether Renderdoc has been loaded
bool isLoaded();

// Returns whether we've compiled with Renderdoc support
static constexpr bool isSupported() { return true; }
} // namespace Renderdoc
Expand All @@ -34,6 +37,7 @@ namespace Renderdoc {
static void triggerCapture() { Helpers::panic("Tried to trigger a Renderdoc capture while support for renderdoc is disabled"); }
static void setOutputDir(const std::string& path, const std::string& prefix) {}
static constexpr bool isSupported() { return false; }
static constexpr bool isLoaded() { return false; }
} // namespace Renderdoc
#endif

Expand Down
13 changes: 13 additions & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,16 @@ void EmulatorConfig::load() {
sdWriteProtected = toml::find_or<toml::boolean>(sd, "WriteProtectVirtualSD", false);
}
}

if (data.contains("UI")) {
auto uiResult = toml::expect<toml::value>(data.at("UI"));
if (uiResult.is_ok()) {
auto ui = uiResult.unwrap();

frontendSettings.theme = FrontendSettings::themeFromString(toml::find_or<std::string>(ui, "Theme", "dark"));
frontendSettings.icon = FrontendSettings::iconFromString(toml::find_or<std::string>(ui, "WindowIcon", "rpog"));
}
}
}

void EmulatorConfig::save() {
Expand Down Expand Up @@ -186,6 +196,9 @@ void EmulatorConfig::save() {
data["SD"]["UseVirtualSD"] = sdCardInserted;
data["SD"]["WriteProtectVirtualSD"] = sdWriteProtected;

data["UI"]["Theme"] = std::string(FrontendSettings::themeToString(frontendSettings.theme));
data["UI"]["WindowIcon"] = std::string(FrontendSettings::iconToString(frontendSettings.icon));

std::ofstream file(path, std::ios::out);
file << data;
file.close();
Expand Down
21 changes: 21 additions & 0 deletions src/emulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -444,3 +444,24 @@ void Emulator::loadRenderdoc() {
Renderdoc::loadRenderdoc();
Renderdoc::setOutputDir(capturePath, "");
}

void Emulator::reloadSettings() {
setAudioEnabled(config.audioEnabled);

if (Renderdoc::isSupported() && config.enableRenderdoc && !Renderdoc::isLoaded()) {
loadRenderdoc();
}

#ifdef PANDA3DS_ENABLE_DISCORD_RPC
// Reload RPC setting if we're compiling with RPC support

if (discordRpc.running() != config.discordRpcEnabled) {
if (config.discordRpcEnabled) {
discordRpc.init();
updateDiscord();
} else {
discordRpc.stop();
}
}
#endif
}
64 changes: 64 additions & 0 deletions src/frontend_settings.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include "frontend_settings.hpp"

#include <algorithm>
#include <cctype>
#include <unordered_map>

// Frontend setting serialization/deserialization functions

FrontendSettings::Theme FrontendSettings::themeFromString(std::string inString) {
// Transform to lower-case to make the setting case-insensitive
std::transform(inString.begin(), inString.end(), inString.begin(), [](unsigned char c) { return std::tolower(c); });

static const std::unordered_map<std::string, Theme> map = {
{"system", Theme::System}, {"light", Theme::Light}, {"dark", Theme::Dark}, {"greetingscat", Theme::GreetingsCat}, {"cream", Theme::Cream},
};

if (auto search = map.find(inString); search != map.end()) {
return search->second;
}

// Default to dark theme
return Theme::Dark;
}

const char* FrontendSettings::themeToString(Theme theme) {
switch (theme) {
case Theme::System: return "system";
case Theme::Light: return "light";
case Theme::GreetingsCat: return "greetingscat";
case Theme::Cream: return "cream";

case Theme::Dark:
default: return "dark";
}
}

FrontendSettings::WindowIcon FrontendSettings::iconFromString(std::string inString) { // Transform to lower-case to make the setting case-insensitive
std::transform(inString.begin(), inString.end(), inString.begin(), [](unsigned char c) { return std::tolower(c); });

static const std::unordered_map<std::string, WindowIcon> map = {
{"rpog", WindowIcon::Rpog},
{"rsyn", WindowIcon::Rsyn},
{"rcow", WindowIcon::Rcow},
{"rnap", WindowIcon::Rnap},
};

if (auto search = map.find(inString); search != map.end()) {
return search->second;
}

// Default to the icon rpog icon
return WindowIcon::Rpog;
}

const char* FrontendSettings::iconToString(WindowIcon icon) {
switch (icon) {
case WindowIcon::Rsyn: return "rsyn";
case WindowIcon::Rcow: return "rcow";
case WindowIcon::Rnap: return "rnap";

case WindowIcon::Rpog:
default: return "rpog";
}
}
Loading
Loading