flycast/core/ui/settings.cpp

311 lines
9.9 KiB
C++

/*
Copyright 2025 flyinghead
This file is part of Flycast.
Flycast 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 2 of the License, or
(at your option) any later version.
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "settings.h"
#include "gui.h"
#include "IconsFontAwesome6.h"
#include "mainui.h"
#include "log/LogManager.h"
#include "hw/maple/maple_if.h"
#include "imgui_stdlib.h"
#ifdef GDB_SERVER
#include "hw/mem/addrspace.h"
#endif
static void gui_settings_advanced()
{
#if FEAT_SHREC != DYNAREC_NONE
header("CPU Mode");
{
ImGui::Columns(2, "cpu_modes", false);
OptionRadioButton("Dynarec", config::DynarecEnabled, true,
"Use the dynamic recompiler. Recommended in most cases");
ImGui::NextColumn();
OptionRadioButton("Interpreter", config::DynarecEnabled, false,
"Use the interpreter. Very slow but may help in case of a dynarec problem");
ImGui::Columns(1, NULL, false);
OptionSlider("SH4 Clock", config::Sh4Clock, 100, 300,
"Over/Underclock the main SH4 CPU. Default is 200 MHz. Other values may crash, freeze or trigger unexpected nuclear reactions.",
"%d MHz");
}
#ifdef GDB_SERVER
ImGui::Spacing();
header("Virtual memory addresses");
{
void *ram_base, *ram, *vram, *aram;
addrspace::getAddress(&ram_base, &ram, &vram, &aram);
ImGui::Text("Base Address: %p", ram_base);
if (ram == nullptr) {
const ImVec4 gray(0.75f, 0.75f, 0.75f, 1.f);
ImGui::TextColored(gray, "RAM adresses are not available until the emulation is started");
} else {
ImGui::Columns(3, "virtualMemoryAddress", false);
ImGui::Text("RAM: %p", ram);
ImGui::NextColumn();
ImGui::Text("VRAM64: %p", vram);
ImGui::NextColumn();
ImGui::Text("ARAM: %p", aram);
ImGui::Columns(1, nullptr, false);
}
}
ImGui::Spacing();
header("Debugging");
{
OptionCheckbox("Enable GDB", config::GDB, "GDB debugging support, disables Dynarec and dramatically reduces performance when a debugger is connected.");
OptionCheckbox("Wait for connection", config::GDBWaitForConnection, "Start emulation once the debugger is connected.");
#ifndef __ANDROID
OptionCheckbox("Serial Console", config::SerialConsole, "Dump the Dreamcast serial console to stdout");
OptionCheckbox("Serial PTY", config::SerialPTY, "Requires the option \"Serial Console\" to work");
#endif
static int gdbport = config::GDBPort;
if (ImGui::InputInt("GDB port", &gdbport))
{
config::GDBPort = gdbport;
}
const ImGuiStyle& style = ImGui::GetStyle();
ImGui::SameLine(0, style.ItemInnerSpacing.x);
ShowHelpMarker("Default port is 3263");
}
#endif
ImGui::Spacing();
#endif
header("Other");
{
OptionCheckbox("HLE BIOS", config::UseReios, "Force high-level BIOS emulation");
OptionCheckbox("Multi-threaded emulation", config::ThreadedRendering,
"Run the emulated CPU and GPU on different threads");
#if !defined(__ANDROID) && !defined(GDB_SERVER)
OptionCheckbox("Serial Console", config::SerialConsole,
"Dump the Dreamcast serial console to stdout");
#endif
{
DisabledScope scope(game_started);
OptionCheckbox("Dreamcast 32MB RAM Mod", config::RamMod32MB,
"Enables 32MB RAM Mod for Dreamcast. May affect compatibility");
}
OptionCheckbox("Dump Textures", config::DumpTextures,
"Dump all textures into data/texdump/<game id>");
bool logToFile = cfgLoadBool("log", "LogToFile", false);
if (ImGui::Checkbox("Log to File", &logToFile))
cfgSaveBool("log", "LogToFile", logToFile);
ImGui::SameLine();
ShowHelpMarker("Log debug information to flycast.log");
#ifdef SENTRY_UPLOAD
OptionCheckbox("Automatically Report Crashes", config::UploadCrashLogs,
"Automatically upload crash reports to sentry.io to help in troubleshooting. No personal information is included.");
#endif
}
#ifdef USE_LUA
header("Lua Scripting");
{
ImGui::InputText("Lua Filename", &config::LuaFileName.get(), ImGuiInputTextFlags_CharsNoBlank, nullptr, nullptr);
ImGui::SameLine();
ShowHelpMarker("Specify lua filename to use. Should be located in Flycast config folder. Defaults to flycast.lua when empty.");
}
#endif
}
#if !defined(NDEBUG) || defined(DEBUGFAST) || FC_PROFILER
static void gui_debug_tab()
{
header("Logging");
{
LogManager *logManager = LogManager::GetInstance();
for (LogTypes::LOG_TYPE type = LogTypes::AICA; type < LogTypes::NUMBER_OF_LOGS; type = (LogTypes::LOG_TYPE)(type + 1))
{
bool enabled = logManager->IsEnabled(type, logManager->GetLogLevel());
std::string name = std::string(logManager->GetShortName(type)) + " - " + logManager->GetFullName(type);
if (ImGui::Checkbox(name.c_str(), &enabled) && logManager->GetLogLevel() > LogTypes::LWARNING) {
logManager->SetEnable(type, enabled);
cfgSaveBool("log", logManager->GetShortName(type), enabled);
}
}
ImGui::Spacing();
static const char *levels[] = { "Notice", "Error", "Warning", "Info", "Debug" };
if (ImGui::BeginCombo("Log Verbosity", levels[logManager->GetLogLevel() - 1], ImGuiComboFlags_None))
{
for (std::size_t i = 0; i < std::size(levels); i++)
{
bool is_selected = logManager->GetLogLevel() - 1 == (int)i;
if (ImGui::Selectable(levels[i], &is_selected)) {
logManager->SetLogLevel((LogTypes::LOG_LEVELS)(i + 1));
cfgSaveInt("log", "Verbosity", i + 1);
}
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::InputText("Log Server", &config::LogServer.get(), ImGuiInputTextFlags_CharsNoBlank, nullptr, nullptr);
ImGui::SameLine();
ShowHelpMarker("Log to this hostname[:port] with UDP. Default port is 31667.");
}
#if FC_PROFILER
ImGui::Spacing();
header("Profiling");
{
OptionCheckbox("Enable", config::ProfilerEnabled, "Enable the profiler.");
if (!config::ProfilerEnabled)
{
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
}
OptionCheckbox("Display", config::ProfilerDrawToGUI, "Draw the profiler output in an overlay.");
OptionCheckbox("Output to terminal", config::ProfilerOutputTTY, "Write the profiler output to the terminal");
// TODO frame warning time
if (!config::ProfilerEnabled)
{
ImGui::PopItemFlag();
ImGui::PopStyleVar();
}
}
#endif
}
#endif
void gui_display_settings()
{
static bool maple_devices_changed;
fullScreenWindow(false);
ImguiStyleVar _(ImGuiStyleVar_WindowRounding, 0);
ImGui::Begin("Settings", NULL, ImGuiWindowFlags_DragScrolling | ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse);
ImVec2 normal_padding = ImGui::GetStyle().FramePadding;
if (ImGui::Button("Done", ScaledVec2(100, 30)))
{
if (uiUserScaleUpdated)
{
uiUserScaleUpdated = false;
mainui_reinit();
}
if (game_started)
gui_setState(GuiState::Commands);
else
gui_setState(GuiState::Main);
if (maple_devices_changed)
{
maple_devices_changed = false;
if (game_started && settings.platform.isConsole())
{
maple_ReconnectDevices();
reset_vmus();
}
}
SaveSettings();
}
if (game_started)
{
ImGui::SameLine();
ImguiStyleVar _(ImGuiStyleVar_FramePadding, ImVec2(uiScaled(16), normal_padding.y));
if (config::Settings::instance().hasPerGameConfig())
{
if (ImGui::Button("Delete Game Config", ScaledVec2(0, 30)))
{
config::Settings::instance().setPerGameConfig(false);
config::Settings::instance().load(false);
loadGameSpecificSettings();
}
}
else
{
if (ImGui::Button("Make Game Config", ScaledVec2(0, 30)))
config::Settings::instance().setPerGameConfig(true);
}
}
if (ImGui::GetContentRegionAvail().x >= uiScaled(650.f))
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ScaledVec2(16, 6));
else
// low width
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ScaledVec2(4, 6));
if (ImGui::BeginTabBar("settings", ImGuiTabBarFlags_NoTooltip))
{
if (ImGui::BeginTabItem(ICON_FA_TOOLBOX " General"))
{
ImguiStyleVar _(ImGuiStyleVar_FramePadding, normal_padding);
gui_settings_general();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem(ICON_FA_GAMEPAD " Controls"))
{
ImguiStyleVar _(ImGuiStyleVar_FramePadding, normal_padding);
gui_settings_controls(maple_devices_changed);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem(ICON_FA_DISPLAY " Video"))
{
ImguiStyleVar _(ImGuiStyleVar_FramePadding, normal_padding);
gui_settings_video();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem(ICON_FA_MUSIC " Audio"))
{
ImguiStyleVar _(ImGuiStyleVar_FramePadding, normal_padding);
gui_settings_audio();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem(ICON_FA_WIFI " Network"))
{
ImguiStyleVar _(ImGuiStyleVar_FramePadding, normal_padding);
gui_settings_network();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem(ICON_FA_MICROCHIP " Advanced"))
{
ImguiStyleVar _(ImGuiStyleVar_FramePadding, normal_padding);
gui_settings_advanced();
ImGui::EndTabItem();
}
#if !defined(NDEBUG) || defined(DEBUGFAST) || FC_PROFILER
if (ImGui::BeginTabItem(ICON_FA_BUG " Debug"))
{
ImguiStyleVar _(ImGuiStyleVar_FramePadding, normal_padding);
gui_debug_tab();
ImGui::EndTabItem();
}
#endif
if (ImGui::BeginTabItem(ICON_FA_CIRCLE_INFO " About"))
{
ImguiStyleVar _(ImGuiStyleVar_FramePadding, normal_padding);
gui_settings_about();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
ImGui::PopStyleVar();
scrollWhenDraggingOnVoid();
windowDragScroll();
ImGui::End();
}