mirror of
https://github.com/PCSX2/pcsx2.git
synced 2025-12-16 04:08:48 +00:00
BPM: Add Network and HDD settings page
Signed-off-by: SternXD <stern@sidestore.io>
This commit is contained in:
parent
5445cb516a
commit
5fe5148e86
@ -22,6 +22,12 @@
|
||||
#include "USB/USB.h"
|
||||
#include "VMManager.h"
|
||||
#include "ps2/BiosTools.h"
|
||||
#include "DEV9/ATA/HddCreate.h"
|
||||
#include "DEV9/pcap_io.h"
|
||||
#include "DEV9/sockets.h"
|
||||
#ifdef _WIN32
|
||||
#include "DEV9/Win32/tap.h"
|
||||
#endif
|
||||
|
||||
#include "common/Console.h"
|
||||
#include "common/Error.h"
|
||||
@ -48,9 +54,11 @@
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@ -173,6 +181,7 @@ using ImGuiFullscreen::OpenInfoMessageDialog;
|
||||
using ImGuiFullscreen::OpenInputStringDialog;
|
||||
using ImGuiFullscreen::PopPrimaryColor;
|
||||
using ImGuiFullscreen::PushPrimaryColor;
|
||||
using ImGuiFullscreen::InputFilterType;
|
||||
using ImGuiFullscreen::QueueResetFocus;
|
||||
using ImGuiFullscreen::ResetFocusHere;
|
||||
using ImGuiFullscreen::RightAlignNavButtons;
|
||||
@ -184,6 +193,176 @@ using ImGuiFullscreen::WantsToCloseMenu;
|
||||
|
||||
namespace FullscreenUI
|
||||
{
|
||||
|
||||
class HddCreateInProgress : public HddCreate
|
||||
{
|
||||
private:
|
||||
std::string m_dialogId;
|
||||
std::atomic_bool m_completed{false};
|
||||
std::atomic_bool m_success{false};
|
||||
int m_reqMiB = 0;
|
||||
std::atomic_bool m_dialogClosed{false}; // Check if dialog was already closed
|
||||
|
||||
static std::vector<std::shared_ptr<HddCreateInProgress>> s_activeOperations;
|
||||
static std::mutex s_operationsMutex;
|
||||
static std::atomic_int s_nextOperationId;
|
||||
|
||||
public:
|
||||
HddCreateInProgress(const std::string& dialogId)
|
||||
: m_dialogId(dialogId)
|
||||
{
|
||||
}
|
||||
|
||||
~HddCreateInProgress()
|
||||
{
|
||||
SafeCloseDialog();
|
||||
}
|
||||
|
||||
void SafeCloseDialog()
|
||||
{
|
||||
bool expected = false;
|
||||
if (m_dialogClosed.compare_exchange_strong(expected, true))
|
||||
{
|
||||
ImGuiFullscreen::CloseProgressDialog(m_dialogId.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static bool StartCreation(const std::string& filePath, int sizeInGB, bool use48BitLBA)
|
||||
{
|
||||
if (filePath.empty() || sizeInGB <= 0)
|
||||
return false;
|
||||
|
||||
std::string dialogId = fmt::format("hdd_create_{}", s_nextOperationId.fetch_add(1, std::memory_order_relaxed));
|
||||
|
||||
std::shared_ptr<HddCreateInProgress> instance = std::make_shared<HddCreateInProgress>(dialogId);
|
||||
|
||||
// Convert GB to bytes
|
||||
const u64 sizeBytes = static_cast<u64>(sizeInGB) * static_cast<u64>(_1gb);
|
||||
|
||||
// Make sure the file doesn't already exist (or delete it if it does)
|
||||
if (FileSystem::FileExists(filePath.c_str()))
|
||||
{
|
||||
if (!FileSystem::DeleteFilePath(filePath.c_str()))
|
||||
{
|
||||
Host::RunOnCPUThread([filePath]() {
|
||||
ShowToast(
|
||||
fmt::format("{} HDD Creation Failed", ICON_FA_TRIANGLE_EXCLAMATION),
|
||||
fmt::format("Failed to delete existing HDD image file '{}'. Please check file permissions and try again.", Path::GetFileName(filePath)),
|
||||
5.0f);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the creation parameters
|
||||
instance->filePath = filePath;
|
||||
instance->neededSize = sizeBytes;
|
||||
|
||||
// Register the operation
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(s_operationsMutex);
|
||||
s_activeOperations.push_back(instance);
|
||||
}
|
||||
|
||||
// Start the HDD creation
|
||||
std::thread([instance = std::move(instance)]() {
|
||||
instance->Start();
|
||||
|
||||
if (!instance->errored)
|
||||
Host::RunOnCPUThread([size_gb = static_cast<int>(instance->neededSize / static_cast<u64>(_1gb))]() {
|
||||
ShowToast(
|
||||
ICON_FA_CIRCLE_CHECK,
|
||||
fmt::format("HDD image ({} GB) created successfully.", size_gb),
|
||||
3.0f);
|
||||
});
|
||||
else
|
||||
Host::RunOnCPUThread([]() {
|
||||
ShowToast(
|
||||
ICON_FA_TRIANGLE_EXCLAMATION,
|
||||
"Failed to create HDD image.",
|
||||
3.0f);
|
||||
});
|
||||
|
||||
std::lock_guard<std::mutex> lock(s_operationsMutex);
|
||||
for (auto it = s_activeOperations.begin(); it != s_activeOperations.end(); ++it)
|
||||
{
|
||||
if (it->get() == instance.get())
|
||||
{
|
||||
s_activeOperations.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}).detach();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void CancelAllOperations()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(s_operationsMutex);
|
||||
for (auto& operation : s_activeOperations)
|
||||
{
|
||||
operation->SetCanceled();
|
||||
operation->SafeCloseDialog();
|
||||
}
|
||||
s_activeOperations.clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void Init() override
|
||||
{
|
||||
m_reqMiB = static_cast<int>((neededSize + ((1024 * 1024) - 1)) / (1024 * 1024));
|
||||
const std::string message = fmt::format("{} Creating HDD Image\n{} / {} MiB", ICON_FA_HARD_DRIVE, 0, m_reqMiB);
|
||||
ImGuiFullscreen::OpenProgressDialog(m_dialogId.c_str(), message, 0, m_reqMiB, 0);
|
||||
}
|
||||
|
||||
virtual void SetFileProgress(u64 currentSize) override
|
||||
{
|
||||
const int writtenMiB = static_cast<int>((currentSize + ((1024 * 1024) - 1)) / (1024 * 1024));
|
||||
const std::string message = fmt::format("{} Creating HDD Image\n{} / {} MiB", ICON_FA_HARD_DRIVE, writtenMiB, m_reqMiB);
|
||||
ImGuiFullscreen::UpdateProgressDialog(m_dialogId.c_str(), message, 0, m_reqMiB, writtenMiB);
|
||||
}
|
||||
|
||||
virtual void SetError() override
|
||||
{
|
||||
SafeCloseDialog();
|
||||
HddCreate::SetError();
|
||||
}
|
||||
|
||||
virtual void Cleanup() override
|
||||
{
|
||||
SafeCloseDialog();
|
||||
m_success.store(!errored, std::memory_order_release);
|
||||
m_completed.store(true, std::memory_order_release);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::shared_ptr<HddCreateInProgress>> HddCreateInProgress::s_activeOperations;
|
||||
std::mutex HddCreateInProgress::s_operationsMutex;
|
||||
std::atomic_int HddCreateInProgress::s_nextOperationId{0};
|
||||
|
||||
bool CreateHardDriveWithProgress(const std::string& filePath, int sizeInGB, bool use48BitLBA)
|
||||
{
|
||||
// Validate size limits based on the LBA mode set
|
||||
const int min_size = use48BitLBA ? 100 : 40;
|
||||
const int max_size = use48BitLBA ? 2000 : 120;
|
||||
|
||||
if (sizeInGB < min_size || sizeInGB > max_size)
|
||||
{
|
||||
Host::RunOnCPUThread([min_size, max_size]() {
|
||||
ShowToast(std::string(), fmt::format("Invalid HDD size. Size must be between {} and {} GB.", min_size, max_size).c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return HddCreateInProgress::StartCreation(filePath, sizeInGB, use48BitLBA);
|
||||
}
|
||||
|
||||
void CancelAllHddOperations()
|
||||
{
|
||||
HddCreateInProgress::CancelAllOperations();
|
||||
}
|
||||
|
||||
enum class MainWindowType
|
||||
{
|
||||
None,
|
||||
@ -214,6 +393,7 @@ namespace FullscreenUI
|
||||
Graphics,
|
||||
Audio,
|
||||
MemoryCard,
|
||||
NetworkHDD,
|
||||
Folders,
|
||||
Achievements,
|
||||
Controller,
|
||||
@ -232,6 +412,16 @@ namespace FullscreenUI
|
||||
Count
|
||||
};
|
||||
|
||||
enum class IPAddressType
|
||||
{
|
||||
PS2IP,
|
||||
SubnetMask,
|
||||
Gateway,
|
||||
DNS1,
|
||||
DNS2,
|
||||
Other
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Main
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -340,6 +530,7 @@ namespace FullscreenUI
|
||||
static void DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_advanced_settings);
|
||||
static void DrawAudioSettingsPage();
|
||||
static void DrawMemoryCardSettingsPage();
|
||||
static void DrawNetworkHDDSettingsPage();
|
||||
static void DrawFoldersSettingsPage();
|
||||
static void DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& settings_lock);
|
||||
static void DrawControllerSettingsPage();
|
||||
@ -395,6 +586,10 @@ namespace FullscreenUI
|
||||
static void DrawStringListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
|
||||
const char* default_value, SettingInfo::GetOptionsCallback options_callback, bool enabled = true,
|
||||
float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
|
||||
static void DrawIPAddressSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
|
||||
const char* default_value, bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT,
|
||||
std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font,
|
||||
IPAddressType ip_type = IPAddressType::Other);
|
||||
static void DrawFloatListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
|
||||
float default_value, const char* const* options, const float* option_values, size_t option_count, bool translate_options,
|
||||
bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font,
|
||||
@ -965,6 +1160,7 @@ void FullscreenUI::Shutdown(bool clear_state)
|
||||
{
|
||||
if (clear_state)
|
||||
{
|
||||
CancelAllHddOperations();
|
||||
CloseSaveStateSelector();
|
||||
s_cover_image_map.clear();
|
||||
s_game_list_sorted_entries = {};
|
||||
@ -2897,6 +3093,91 @@ void FullscreenUI::DrawPathSetting(SettingsInterface* bsi, const char* title, co
|
||||
}
|
||||
}
|
||||
|
||||
void FullscreenUI::DrawIPAddressSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section,
|
||||
const char* key, const char* default_value, bool enabled, float height, std::pair<ImFont*, float> font, std::pair<ImFont*, float> summary_font, IPAddressType ip_type)
|
||||
{
|
||||
const bool game_settings = IsEditingGameSettings(bsi);
|
||||
const std::optional<SmallString> value(
|
||||
bsi->GetOptionalSmallStringValue(section, key, game_settings ? std::nullopt : std::optional<const char*>(default_value)));
|
||||
|
||||
const SmallString value_text = value.has_value() ? value.value() : SmallString(FSUI_VSTR("Use Global Setting"));
|
||||
|
||||
static std::array<int, 4> ip_octets = {0, 0, 0, 0};
|
||||
|
||||
if (MenuButtonWithValue(title, summary, value_text.c_str(), enabled, height, font, summary_font))
|
||||
{
|
||||
const std::string current_ip = value.has_value() ? std::string(value->c_str()) : std::string(default_value);
|
||||
std::istringstream iss(current_ip);
|
||||
std::string segment;
|
||||
int i = 0;
|
||||
while (std::getline(iss, segment, '.') && i < 4)
|
||||
{
|
||||
ip_octets[i] = std::clamp(std::atoi(segment.c_str()), 0, 255);
|
||||
i++;
|
||||
}
|
||||
for (; i < 4; i++)
|
||||
ip_octets[i] = 0;
|
||||
|
||||
char ip_str[16];
|
||||
std::snprintf(ip_str, sizeof(ip_str), "%d.%d.%d.%d", ip_octets[0], ip_octets[1], ip_octets[2], ip_octets[3]);
|
||||
|
||||
const char* message;
|
||||
switch (ip_type)
|
||||
{
|
||||
case IPAddressType::DNS1:
|
||||
case IPAddressType::DNS2:
|
||||
message = FSUI_CSTR("Enter the DNS server address");
|
||||
break;
|
||||
case IPAddressType::Gateway:
|
||||
message = FSUI_CSTR("Enter the Gateway address");
|
||||
break;
|
||||
case IPAddressType::SubnetMask:
|
||||
message = FSUI_CSTR("Enter the Subnet Mask");
|
||||
break;
|
||||
case IPAddressType::PS2IP:
|
||||
message = FSUI_CSTR("Enter the PS2 IP address");
|
||||
break;
|
||||
case IPAddressType::Other:
|
||||
default:
|
||||
message = FSUI_CSTR("Enter the IP address");
|
||||
break;
|
||||
}
|
||||
|
||||
ImGuiFullscreen::CloseInputDialog();
|
||||
|
||||
std::string ip_str_value(ip_str);
|
||||
|
||||
ImGuiFullscreen::OpenInputStringDialog(
|
||||
title,
|
||||
message,
|
||||
"",
|
||||
std::string(FSUI_ICONSTR(ICON_FA_CHECK, "OK")),
|
||||
[bsi, section, key, default_value](std::string text) {
|
||||
// Validate and clean up the IP address
|
||||
std::array<int, 4> new_octets = {0, 0, 0, 0};
|
||||
std::istringstream iss(text);
|
||||
std::string segment;
|
||||
int i = 0;
|
||||
while (std::getline(iss, segment, '.') && i < 4)
|
||||
{
|
||||
new_octets[i] = std::clamp(std::atoi(segment.c_str()), 0, 255);
|
||||
i++;
|
||||
}
|
||||
|
||||
char ip_str[16];
|
||||
std::snprintf(ip_str, sizeof(ip_str), "%d.%d.%d.%d", new_octets[0], new_octets[1], new_octets[2], new_octets[3]);
|
||||
|
||||
if (IsEditingGameSettings(bsi) && strcmp(ip_str, default_value) == 0)
|
||||
bsi->DeleteValue(section, key);
|
||||
else
|
||||
bsi->SetStringValue(section, key, ip_str);
|
||||
SetSettingsChanged(bsi);
|
||||
},
|
||||
ip_str_value,
|
||||
ImGuiFullscreen::InputFilterType::IPAddress);
|
||||
}
|
||||
}
|
||||
|
||||
void FullscreenUI::StartAutomaticBinding(u32 port)
|
||||
{
|
||||
// messy because the enumeration has to happen on the input thread
|
||||
@ -3127,6 +3408,7 @@ void FullscreenUI::DrawSettingsWindow()
|
||||
ICON_PF_PICTURE,
|
||||
ICON_PF_SOUND,
|
||||
ICON_PF_MEMORY_CARD,
|
||||
ICON_FA_NETWORK_WIRED,
|
||||
ICON_FA_FOLDER_OPEN,
|
||||
ICON_FA_TROPHY,
|
||||
ICON_PF_GAMEPAD_ALT,
|
||||
@ -3150,6 +3432,7 @@ void FullscreenUI::DrawSettingsWindow()
|
||||
SettingsPage::Graphics,
|
||||
SettingsPage::Audio,
|
||||
SettingsPage::MemoryCard,
|
||||
SettingsPage::NetworkHDD,
|
||||
SettingsPage::Folders,
|
||||
SettingsPage::Achievements,
|
||||
SettingsPage::Controller,
|
||||
@ -3174,6 +3457,7 @@ void FullscreenUI::DrawSettingsWindow()
|
||||
FSUI_NSTR("Graphics Settings"),
|
||||
FSUI_NSTR("Audio Settings"),
|
||||
FSUI_NSTR("Memory Card Settings"),
|
||||
FSUI_NSTR("Network & HDD Settings"),
|
||||
FSUI_NSTR("Folder Settings"),
|
||||
FSUI_NSTR("Achievements Settings"),
|
||||
FSUI_NSTR("Controller Settings"),
|
||||
@ -3305,6 +3589,10 @@ void FullscreenUI::DrawSettingsWindow()
|
||||
DrawMemoryCardSettingsPage();
|
||||
break;
|
||||
|
||||
case SettingsPage::NetworkHDD:
|
||||
DrawNetworkHDDSettingsPage();
|
||||
break;
|
||||
|
||||
case SettingsPage::Folders:
|
||||
DrawFoldersSettingsPage();
|
||||
break;
|
||||
@ -4672,6 +4960,540 @@ void FullscreenUI::DrawMemoryCardSettingsPage()
|
||||
}
|
||||
}
|
||||
|
||||
EndMenuButtons();
|
||||
}
|
||||
|
||||
void FullscreenUI::DrawNetworkHDDSettingsPage()
|
||||
{
|
||||
|
||||
static constexpr const char* dns_options[] = {
|
||||
FSUI_NSTR("Manual"),
|
||||
FSUI_NSTR("Auto"),
|
||||
FSUI_NSTR("Internal"),
|
||||
};
|
||||
|
||||
static constexpr const char* dns_values[] = {
|
||||
"Manual",
|
||||
"Auto",
|
||||
"Internal",
|
||||
};
|
||||
|
||||
SettingsInterface* bsi = GetEditingSettingsInterface();
|
||||
|
||||
BeginMenuButtons();
|
||||
|
||||
MenuHeading(FSUI_CSTR("Network Adapter"));
|
||||
|
||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_NETWORK_WIRED, "Enable Network Adapter"),
|
||||
FSUI_CSTR("Enables the network adapter for online functionality and LAN play."), "DEV9/Eth", "EthEnable", false);
|
||||
|
||||
const bool network_enabled = GetEffectiveBoolSetting(bsi, "DEV9/Eth", "EthEnable", false);
|
||||
|
||||
const std::string current_api = bsi->GetStringValue("DEV9/Eth", "EthApi", "Unset");
|
||||
|
||||
static std::vector<std::vector<AdapterEntry>> adapter_lists;
|
||||
static std::vector<Pcsx2Config::DEV9Options::NetApi> api_types;
|
||||
static std::vector<std::string> api_display_names;
|
||||
static bool adapters_loaded = false;
|
||||
|
||||
if (!adapters_loaded && network_enabled)
|
||||
{
|
||||
adapter_lists.clear();
|
||||
api_types.clear();
|
||||
api_display_names.clear();
|
||||
|
||||
adapter_lists.emplace_back();
|
||||
api_types.emplace_back(Pcsx2Config::DEV9Options::NetApi::Unset);
|
||||
api_display_names.emplace_back("Unset");
|
||||
|
||||
std::vector<AdapterEntry> pcap_adapters = PCAPAdapter::GetAdapters();
|
||||
if (!pcap_adapters.empty())
|
||||
{
|
||||
std::vector<AdapterEntry> pcap_bridged_adapters;
|
||||
std::vector<AdapterEntry> pcap_switched_adapters;
|
||||
std::set<std::string> seen_bridged_guids;
|
||||
std::set<std::string> seen_switched_guids;
|
||||
|
||||
for (const auto& adapter : pcap_adapters)
|
||||
{
|
||||
if (adapter.type == Pcsx2Config::DEV9Options::NetApi::PCAP_Bridged)
|
||||
{
|
||||
if (seen_bridged_guids.find(adapter.guid) == seen_bridged_guids.end())
|
||||
{
|
||||
seen_bridged_guids.insert(adapter.guid);
|
||||
pcap_bridged_adapters.push_back(adapter);
|
||||
}
|
||||
}
|
||||
else if (adapter.type == Pcsx2Config::DEV9Options::NetApi::PCAP_Switched)
|
||||
{
|
||||
if (seen_switched_guids.find(adapter.guid) == seen_switched_guids.end())
|
||||
{
|
||||
seen_switched_guids.insert(adapter.guid);
|
||||
pcap_switched_adapters.push_back(adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort adapters alphabetically by name
|
||||
std::sort(pcap_bridged_adapters.begin(), pcap_bridged_adapters.end(),
|
||||
[](const AdapterEntry& a, const AdapterEntry& b) { return a.name < b.name; });
|
||||
std::sort(pcap_switched_adapters.begin(), pcap_switched_adapters.end(),
|
||||
[](const AdapterEntry& a, const AdapterEntry& b) { return a.name < b.name; });
|
||||
|
||||
if (!pcap_bridged_adapters.empty())
|
||||
{
|
||||
adapter_lists.emplace_back(pcap_bridged_adapters);
|
||||
api_types.emplace_back(Pcsx2Config::DEV9Options::NetApi::PCAP_Bridged);
|
||||
api_display_names.emplace_back("PCAP Bridged");
|
||||
}
|
||||
|
||||
if (!pcap_switched_adapters.empty())
|
||||
{
|
||||
adapter_lists.emplace_back(pcap_switched_adapters);
|
||||
api_types.emplace_back(Pcsx2Config::DEV9Options::NetApi::PCAP_Switched);
|
||||
api_display_names.emplace_back("PCAP Switched");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::vector<AdapterEntry> tap_adapters = TAPAdapter::GetAdapters();
|
||||
if (!tap_adapters.empty())
|
||||
{
|
||||
// Sort adapters alphabetically by name
|
||||
std::sort(tap_adapters.begin(), tap_adapters.end(),
|
||||
[](const AdapterEntry& a, const AdapterEntry& b) { return a.name < b.name; });
|
||||
|
||||
adapter_lists.emplace_back(tap_adapters);
|
||||
api_types.emplace_back(Pcsx2Config::DEV9Options::NetApi::TAP);
|
||||
api_display_names.emplace_back("TAP");
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<AdapterEntry> socket_adapters = SocketAdapter::GetAdapters();
|
||||
if (!socket_adapters.empty())
|
||||
{
|
||||
// Sort adapters alphabetically by name
|
||||
std::sort(socket_adapters.begin(), socket_adapters.end(),
|
||||
[](const AdapterEntry& a, const AdapterEntry& b) { return a.name < b.name; });
|
||||
|
||||
adapter_lists.emplace_back(socket_adapters);
|
||||
api_types.emplace_back(Pcsx2Config::DEV9Options::NetApi::Sockets);
|
||||
api_display_names.emplace_back("Sockets");
|
||||
}
|
||||
|
||||
adapters_loaded = true;
|
||||
}
|
||||
|
||||
size_t current_api_index = 0;
|
||||
for (size_t i = 0; i < api_types.size(); i++)
|
||||
{
|
||||
if (current_api == Pcsx2Config::DEV9Options::NetApiNames[static_cast<int>(api_types[i])])
|
||||
{
|
||||
current_api_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (MenuButtonWithValue(FSUI_ICONSTR(ICON_FA_PLUG, "Ethernet Device Type"),
|
||||
FSUI_CSTR("Determines the simulated Ethernet adapter type."),
|
||||
current_api_index < api_display_names.size() ? api_display_names[current_api_index].c_str() : "Unset",
|
||||
network_enabled))
|
||||
{
|
||||
ImGuiFullscreen::ChoiceDialogOptions options;
|
||||
|
||||
for (size_t i = 0; i < api_display_names.size(); i++)
|
||||
{
|
||||
options.emplace_back(api_display_names[i], i == current_api_index);
|
||||
}
|
||||
|
||||
std::vector<Pcsx2Config::DEV9Options::NetApi> current_api_types = api_types;
|
||||
std::vector<std::vector<AdapterEntry>> current_adapter_lists = adapter_lists;
|
||||
|
||||
OpenChoiceDialog(FSUI_ICONSTR(ICON_FA_PLUG, "Ethernet Device Type"), false, std::move(options),
|
||||
[bsi, current_api_types, current_adapter_lists](s32 index, const std::string& title, bool checked) {
|
||||
if (index < 0 || index >= static_cast<s32>(current_api_types.size()))
|
||||
return;
|
||||
|
||||
auto lock = Host::GetSettingsLock();
|
||||
const std::string selected_api = Pcsx2Config::DEV9Options::NetApiNames[static_cast<int>(current_api_types[index])];
|
||||
const std::string previous_api = bsi->GetStringValue("DEV9/Eth", "EthApi", "Unset");
|
||||
const std::string previous_device = bsi->GetStringValue("DEV9/Eth", "EthDevice", "");
|
||||
|
||||
bsi->SetStringValue("DEV9/Eth", "EthApi", selected_api.c_str());
|
||||
|
||||
std::string new_device = "";
|
||||
if (index < static_cast<s32>(current_adapter_lists.size()))
|
||||
{
|
||||
const auto& new_adapter_list = current_adapter_lists[index];
|
||||
|
||||
// Try to find the same GUID in the new adapter list
|
||||
if (!previous_device.empty())
|
||||
{
|
||||
for (const auto& adapter : new_adapter_list)
|
||||
{
|
||||
if (adapter.guid == previous_device)
|
||||
{
|
||||
new_device = adapter.guid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no matching device found, use the first available device
|
||||
if (new_device.empty() && !new_adapter_list.empty())
|
||||
{
|
||||
new_device = new_adapter_list[0].guid;
|
||||
}
|
||||
}
|
||||
|
||||
bsi->SetStringValue("DEV9/Eth", "EthDevice", new_device.c_str());
|
||||
SetSettingsChanged(bsi);
|
||||
|
||||
CloseChoiceDialog();
|
||||
});
|
||||
}
|
||||
|
||||
const std::string current_device = bsi->GetStringValue("DEV9/Eth", "EthDevice", "");
|
||||
const bool show_device_setting = (current_api_index > 0 && current_api_index < api_types.size());
|
||||
|
||||
std::string device_display = "";
|
||||
if (show_device_setting && !current_device.empty())
|
||||
{
|
||||
if (current_api_index < adapter_lists.size())
|
||||
{
|
||||
const auto& adapter_list = adapter_lists[current_api_index];
|
||||
for (const auto& adapter : adapter_list)
|
||||
{
|
||||
if (adapter.guid == current_device)
|
||||
{
|
||||
device_display = adapter.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (device_display.empty())
|
||||
device_display = current_device;
|
||||
}
|
||||
else if (show_device_setting && current_device.empty())
|
||||
{
|
||||
device_display = "Not Selected";
|
||||
}
|
||||
|
||||
if (MenuButtonWithValue(FSUI_ICONSTR(ICON_FA_ETHERNET, "Ethernet Device"),
|
||||
FSUI_CSTR("Network adapter to use for PS2 network emulation."),
|
||||
device_display.c_str(),
|
||||
network_enabled && show_device_setting))
|
||||
{
|
||||
ImGuiFullscreen::ChoiceDialogOptions options;
|
||||
|
||||
if (current_api_index > 0 && current_api_index < adapter_lists.size())
|
||||
{
|
||||
const auto& adapter_list = adapter_lists[current_api_index];
|
||||
for (size_t i = 0; i < adapter_list.size(); i++)
|
||||
{
|
||||
const auto& adapter = adapter_list[i];
|
||||
options.emplace_back(adapter.name, adapter.guid == current_device);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.empty())
|
||||
{
|
||||
options.emplace_back("No adapters found", false);
|
||||
}
|
||||
|
||||
std::vector<AdapterEntry> current_adapter_list;
|
||||
if (current_api_index > 0 && current_api_index < adapter_lists.size())
|
||||
{
|
||||
current_adapter_list = adapter_lists[current_api_index];
|
||||
}
|
||||
|
||||
std::string current_api_choice = bsi->GetStringValue("DEV9/Eth", "EthApi", "Unset");
|
||||
|
||||
OpenChoiceDialog(FSUI_ICONSTR(ICON_FA_ETHERNET, "Ethernet Device"), false, std::move(options),
|
||||
[bsi, current_adapter_list, current_api_choice](s32 index, const std::string& title, bool checked) {
|
||||
if (index < 0 || title == "No adapters found")
|
||||
return;
|
||||
|
||||
if (index < static_cast<s32>(current_adapter_list.size()))
|
||||
{
|
||||
const auto& selected_adapter = current_adapter_list[index];
|
||||
|
||||
auto lock = Host::GetSettingsLock();
|
||||
bsi->SetStringValue("DEV9/Eth", "EthApi", current_api_choice.c_str());
|
||||
bsi->SetStringValue("DEV9/Eth", "EthDevice", selected_adapter.guid.c_str());
|
||||
SetSettingsChanged(bsi);
|
||||
}
|
||||
|
||||
CloseChoiceDialog();
|
||||
});
|
||||
}
|
||||
|
||||
AdapterOptions adapter_options = AdapterOptions::None;
|
||||
const std::string final_api = bsi->GetStringValue("DEV9/Eth", "EthApi", "Unset");
|
||||
if (final_api == "PCAP Bridged" || final_api == "PCAP Switched")
|
||||
adapter_options = PCAPAdapter::GetAdapterOptions();
|
||||
#ifdef _WIN32
|
||||
else if (final_api == "TAP")
|
||||
adapter_options = TAPAdapter::GetAdapterOptions();
|
||||
#endif
|
||||
else if (final_api == "Sockets")
|
||||
adapter_options = SocketAdapter::GetAdapterOptions();
|
||||
|
||||
const bool dhcp_can_be_disabled = (adapter_options & AdapterOptions::DHCP_ForcedOn) == AdapterOptions::None;
|
||||
|
||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_SHIELD_HALVED, "Intercept DHCP"),
|
||||
FSUI_CSTR("When enabled, DHCP packets will be intercepted and replaced with internal responses."), "DEV9/Eth", "InterceptDHCP", false, network_enabled && dhcp_can_be_disabled);
|
||||
|
||||
MenuHeading(FSUI_CSTR("Network Configuration"));
|
||||
|
||||
const bool intercept_dhcp = GetEffectiveBoolSetting(bsi, "DEV9/Eth", "InterceptDHCP", false);
|
||||
const bool dhcp_forced_on = (adapter_options & AdapterOptions::DHCP_ForcedOn) == AdapterOptions::DHCP_ForcedOn;
|
||||
const bool ip_settings_enabled = network_enabled && (intercept_dhcp || dhcp_forced_on);
|
||||
|
||||
const bool ip_can_be_edited = (adapter_options & AdapterOptions::DHCP_OverrideIP) == AdapterOptions::None;
|
||||
const bool subnet_can_be_edited = (adapter_options & AdapterOptions::DHCP_OverideSubnet) == AdapterOptions::None;
|
||||
const bool gateway_can_be_edited = (adapter_options & AdapterOptions::DHCP_OverideGateway) == AdapterOptions::None;
|
||||
|
||||
DrawIPAddressSetting(bsi, FSUI_ICONSTR(ICON_FA_NETWORK_WIRED, "Address"),
|
||||
FSUI_CSTR("IP address for the PS2 virtual network adapter."), "DEV9/Eth", "PS2IP", "0.0.0.0",
|
||||
ip_settings_enabled && ip_can_be_edited, LAYOUT_MENU_BUTTON_HEIGHT, g_large_font, g_medium_font, IPAddressType::PS2IP);
|
||||
|
||||
const bool mask_auto = GetEffectiveBoolSetting(bsi, "DEV9/Eth", "AutoMask", true);
|
||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_WAND_MAGIC, "Auto Subnet Mask"),
|
||||
FSUI_CSTR("Automatically determine the subnet mask based on the IP address class."),
|
||||
"DEV9/Eth", "AutoMask", true, ip_settings_enabled && subnet_can_be_edited);
|
||||
DrawIPAddressSetting(bsi, FSUI_ICONSTR(ICON_FA_NETWORK_WIRED, "Subnet Mask"),
|
||||
FSUI_CSTR("Subnet mask for the PS2 virtual network adapter."), "DEV9/Eth", "Mask", "0.0.0.0",
|
||||
ip_settings_enabled && subnet_can_be_edited && !mask_auto, LAYOUT_MENU_BUTTON_HEIGHT, g_large_font, g_medium_font, IPAddressType::SubnetMask);
|
||||
|
||||
const bool gateway_auto = GetEffectiveBoolSetting(bsi, "DEV9/Eth", "AutoGateway", true);
|
||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_WAND_MAGIC, "Auto Gateway"),
|
||||
FSUI_CSTR("Automatically determine the gateway address based on the IP address."),
|
||||
"DEV9/Eth", "AutoGateway", true, ip_settings_enabled && gateway_can_be_edited);
|
||||
DrawIPAddressSetting(bsi, FSUI_ICONSTR(ICON_FA_NETWORK_WIRED, "Gateway Address"),
|
||||
FSUI_CSTR("Gateway address for the PS2 virtual network adapter."), "DEV9/Eth", "Gateway", "0.0.0.0",
|
||||
ip_settings_enabled && gateway_can_be_edited && !gateway_auto, LAYOUT_MENU_BUTTON_HEIGHT, g_large_font, g_medium_font, IPAddressType::Gateway);
|
||||
|
||||
// DNS Configuration
|
||||
const std::string dns1_mode = bsi->GetStringValue("DEV9/Eth", "ModeDNS1", "Auto");
|
||||
const std::string dns2_mode = bsi->GetStringValue("DEV9/Eth", "ModeDNS2", "Auto");
|
||||
const bool dns1_editable = dns1_mode == "Manual" && ip_settings_enabled;
|
||||
const bool dns2_editable = dns2_mode == "Manual" && ip_settings_enabled;
|
||||
|
||||
DrawStringListSetting(bsi, FSUI_ICONSTR(ICON_FA_SERVER, "DNS1 Mode"),
|
||||
FSUI_CSTR("Determines how primary DNS requests are handled."), "DEV9/Eth", "ModeDNS1", "Auto",
|
||||
dns_options, dns_values, std::size(dns_options), true, ip_settings_enabled);
|
||||
|
||||
DrawIPAddressSetting(bsi, FSUI_ICONSTR(ICON_FA_SERVER, "DNS1 Address"),
|
||||
FSUI_CSTR("Primary DNS server address for the PS2 virtual network adapter."), "DEV9/Eth", "DNS1", "0.0.0.0",
|
||||
dns1_editable, LAYOUT_MENU_BUTTON_HEIGHT, g_large_font, g_medium_font, IPAddressType::DNS1);
|
||||
|
||||
DrawStringListSetting(bsi, FSUI_ICONSTR(ICON_FA_SERVER, "DNS2 Mode"),
|
||||
FSUI_CSTR("Determines how secondary DNS requests are handled."), "DEV9/Eth", "ModeDNS2", "Auto",
|
||||
dns_options, dns_values, std::size(dns_options), true, ip_settings_enabled);
|
||||
|
||||
DrawIPAddressSetting(bsi, FSUI_ICONSTR(ICON_FA_SERVER, "DNS2 Address"),
|
||||
FSUI_CSTR("Secondary DNS server address for the PS2 virtual network adapter."), "DEV9/Eth", "DNS2", "0.0.0.0",
|
||||
dns2_editable, LAYOUT_MENU_BUTTON_HEIGHT, g_large_font, g_medium_font, IPAddressType::DNS2);
|
||||
|
||||
MenuHeading(FSUI_CSTR("Internal HDD"));
|
||||
|
||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_FLOPPY_DISK, "Enable HDD"),
|
||||
FSUI_CSTR("Enables the internal Hard Disk Drive for expanded storage."), "DEV9/Hdd", "HddEnable", false);
|
||||
|
||||
const bool hdd_enabled = GetEffectiveBoolSetting(bsi, "DEV9/Hdd", "HddEnable", false);
|
||||
|
||||
const SmallString hdd_selection = GetEditingSettingsInterface()->GetSmallStringValue("DEV9/Hdd", "HddFile", "");
|
||||
const std::string current_display = hdd_selection.empty() ? std::string(FSUI_CSTR("None")) : std::string(Path::GetFileName(hdd_selection.c_str()));
|
||||
if (MenuButtonWithValue(FSUI_ICONSTR(ICON_FA_HARD_DRIVE, "HDD Image Selection"),
|
||||
FSUI_CSTR("Changes the HDD image used for PS2 internal storage."),
|
||||
current_display.c_str(), hdd_enabled))
|
||||
{
|
||||
ImGuiFullscreen::ChoiceDialogOptions choices;
|
||||
choices.emplace_back(FSUI_STR("None"), hdd_selection.empty());
|
||||
|
||||
std::vector<std::string> values;
|
||||
values.push_back("");
|
||||
|
||||
FileSystem::FindResultsArray results;
|
||||
FileSystem::FindFiles(EmuFolders::DataRoot.c_str(), "*.raw", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES, &results);
|
||||
for (const FILESYSTEM_FIND_DATA& fd : results)
|
||||
{
|
||||
const std::string full_path = fd.FileName;
|
||||
const std::string filename = std::string(Path::GetFileName(full_path));
|
||||
|
||||
// Get file size and determine LBA mode
|
||||
const s64 file_size = FileSystem::GetPathFileSize(full_path.c_str());
|
||||
if (file_size > 0)
|
||||
{
|
||||
const int size_gb = static_cast<int>(file_size / _1gb);
|
||||
const bool uses_lba48 = (file_size > static_cast<s64>(120) * _1gb);
|
||||
const std::string lba_mode = uses_lba48 ? "LBA48" : "LBA28";
|
||||
|
||||
choices.emplace_back(fmt::format("{} ({} GB, {})", filename, size_gb, lba_mode),
|
||||
hdd_selection == full_path);
|
||||
values.emplace_back(full_path);
|
||||
}
|
||||
}
|
||||
|
||||
choices.emplace_back(FSUI_STR("Browse..."), false);
|
||||
values.emplace_back("__browse__");
|
||||
|
||||
choices.emplace_back(FSUI_STR("Create New..."), false);
|
||||
values.emplace_back("__create__");
|
||||
|
||||
OpenChoiceDialog(FSUI_CSTR("HDD Image Selection"), false, std::move(choices),
|
||||
[game_settings = IsEditingGameSettings(bsi), values = std::move(values)](s32 index, const std::string& title, bool checked) {
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
if (values[index] == "__browse__")
|
||||
{
|
||||
CloseChoiceDialog();
|
||||
|
||||
OpenFileSelector(FSUI_ICONSTR(ICON_FA_HARD_DRIVE, "Select HDD Image File"), false,
|
||||
[game_settings](const std::string& path) {
|
||||
if (path.empty())
|
||||
return;
|
||||
|
||||
auto lock = Host::GetSettingsLock();
|
||||
SettingsInterface* bsi = GetEditingSettingsInterface(game_settings);
|
||||
bsi->SetStringValue("DEV9/Hdd", "HddFile", path.c_str());
|
||||
SetSettingsChanged(bsi);
|
||||
ShowToast(std::string(), fmt::format(FSUI_FSTR("Selected HDD image: {}"), Path::GetFileName(path)));
|
||||
}, {"*.raw", "*"}, EmuFolders::DataRoot);
|
||||
}
|
||||
else if (values[index] == "__create__")
|
||||
{
|
||||
CloseChoiceDialog();
|
||||
|
||||
std::vector<std::pair<std::string, int>> size_options = {
|
||||
{"40 GB (Recommended)", 40},
|
||||
{"80 GB", 80},
|
||||
{"120 GB (Max LBA28)", 120},
|
||||
{"200 GB", 200},
|
||||
{"Custom...", -1}
|
||||
};
|
||||
|
||||
ImGuiFullscreen::ChoiceDialogOptions size_choices;
|
||||
std::vector<int> size_values;
|
||||
for (const auto& [label, size] : size_options)
|
||||
{
|
||||
size_choices.emplace_back(label, false);
|
||||
size_values.push_back(size);
|
||||
}
|
||||
|
||||
OpenChoiceDialog(FSUI_ICONSTR(ICON_FA_PLUS, "Select HDD Size"), false, std::move(size_choices),
|
||||
[game_settings, size_values = std::move(size_values)](s32 size_index, const std::string& size_title, bool size_checked) {
|
||||
if (size_index < 0)
|
||||
return;
|
||||
|
||||
if (size_values[size_index] == -1)
|
||||
{
|
||||
CloseChoiceDialog();
|
||||
|
||||
OpenInputStringDialog(
|
||||
FSUI_ICONSTR(ICON_FA_PEN_TO_SQUARE, "Custom HDD Size"),
|
||||
FSUI_STR("Enter custom HDD size in gigabytes (40–2000):"),
|
||||
std::string(),
|
||||
FSUI_ICONSTR(ICON_FA_CHECK, "Create"),
|
||||
[game_settings](std::string input) {
|
||||
if (input.empty())
|
||||
return;
|
||||
|
||||
std::optional<int> custom_size_opt = StringUtil::FromChars<int>(input);
|
||||
if (!custom_size_opt.has_value())
|
||||
{
|
||||
ShowToast(std::string(), FSUI_STR("Invalid size. Please enter a number between 40 and 2000."));
|
||||
return;
|
||||
}
|
||||
int custom_size_gb = custom_size_opt.value();
|
||||
|
||||
if (custom_size_gb < 40 || custom_size_gb > 2000)
|
||||
{
|
||||
ShowToast(std::string(), FSUI_STR("HDD size must be between 40 GB and 2000 GB."));
|
||||
return;
|
||||
}
|
||||
|
||||
const bool lba48 = (custom_size_gb > 120);
|
||||
const std::string filename = fmt::format("DEV9hdd_{}GB_{}.raw", custom_size_gb, lba48 ? "LBA48" : "LBA28");
|
||||
const std::string filepath = Path::Combine(EmuFolders::DataRoot, filename);
|
||||
|
||||
if (FileSystem::FileExists(filepath.c_str()))
|
||||
{
|
||||
OpenConfirmMessageDialog(
|
||||
FSUI_ICONSTR(ICON_FA_TRIANGLE_EXCLAMATION, "File Already Exists"),
|
||||
fmt::format(FSUI_FSTR("HDD image '{}' already exists. Do you want to overwrite it?"), filename),
|
||||
[filepath, custom_size_gb, lba48, game_settings](bool confirmed) {
|
||||
if (confirmed)
|
||||
{
|
||||
auto lock = Host::GetSettingsLock();
|
||||
SettingsInterface* bsi = GetEditingSettingsInterface(game_settings);
|
||||
bsi->SetStringValue("DEV9/Hdd", "HddFile", filepath.c_str());
|
||||
SetSettingsChanged(bsi);
|
||||
FullscreenUI::CreateHardDriveWithProgress(filepath, custom_size_gb, lba48);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto lock = Host::GetSettingsLock();
|
||||
SettingsInterface* bsi = GetEditingSettingsInterface(game_settings);
|
||||
bsi->SetStringValue("DEV9/Hdd", "HddFile", filepath.c_str());
|
||||
SetSettingsChanged(bsi);
|
||||
FullscreenUI::CreateHardDriveWithProgress(filepath, custom_size_gb, lba48);
|
||||
}
|
||||
},
|
||||
"40",
|
||||
InputFilterType::Numeric);
|
||||
return;
|
||||
}
|
||||
|
||||
const int size_gb = size_values[size_index];
|
||||
const bool lba48 = (size_gb > 120);
|
||||
|
||||
const std::string filename = fmt::format("DEV9hdd_{}GB_{}.raw", size_gb, lba48 ? "LBA48" : "LBA28");
|
||||
const std::string filepath = Path::Combine(EmuFolders::DataRoot, filename);
|
||||
|
||||
if (FileSystem::FileExists(filepath.c_str()))
|
||||
{
|
||||
OpenConfirmMessageDialog(
|
||||
FSUI_ICONSTR(ICON_FA_TRIANGLE_EXCLAMATION, "File Already Exists"),
|
||||
fmt::format(FSUI_FSTR("HDD image '{}' already exists. Do you want to overwrite it?"), filename),
|
||||
[filepath, size_gb, lba48, game_settings](bool confirmed) {
|
||||
if (confirmed)
|
||||
{
|
||||
auto lock = Host::GetSettingsLock();
|
||||
SettingsInterface* bsi = GetEditingSettingsInterface(game_settings);
|
||||
bsi->SetStringValue("DEV9/Hdd", "HddFile", filepath.c_str());
|
||||
SetSettingsChanged(bsi);
|
||||
FullscreenUI::CreateHardDriveWithProgress(filepath, size_gb, lba48);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto lock = Host::GetSettingsLock();
|
||||
SettingsInterface* bsi = GetEditingSettingsInterface(game_settings);
|
||||
bsi->SetStringValue("DEV9/Hdd", "HddFile", filepath.c_str());
|
||||
SetSettingsChanged(bsi);
|
||||
FullscreenUI::CreateHardDriveWithProgress(filepath, size_gb, lba48);
|
||||
}
|
||||
|
||||
CloseChoiceDialog();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto lock = Host::GetSettingsLock();
|
||||
SettingsInterface* bsi = GetEditingSettingsInterface(game_settings);
|
||||
bsi->SetStringValue("DEV9/Hdd", "HddFile", values[index].c_str());
|
||||
SetSettingsChanged(bsi);
|
||||
CloseChoiceDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
EndMenuButtons();
|
||||
}
|
||||
|
||||
@ -40,6 +40,9 @@ namespace FullscreenUI
|
||||
void Render();
|
||||
void InvalidateCoverCache();
|
||||
TinyString TimeToPrintableString(time_t t);
|
||||
|
||||
bool CreateHardDriveWithProgress(const std::string& filePath, int sizeInGB, bool use48BitLBA = true);
|
||||
void CancelAllHddOperations();
|
||||
} // namespace FullscreenUI
|
||||
|
||||
// Host UI triggers from Big Picture mode.
|
||||
|
||||
@ -121,6 +121,7 @@ namespace ImGuiFullscreen
|
||||
static std::string s_input_dialog_text;
|
||||
static std::string s_input_dialog_ok_text;
|
||||
static InputStringDialogCallback s_input_dialog_callback;
|
||||
static InputFilterType s_input_dialog_filter_type = InputFilterType::None;
|
||||
|
||||
static bool s_message_dialog_open = false;
|
||||
static std::string s_message_dialog_title;
|
||||
@ -2492,14 +2493,17 @@ bool ImGuiFullscreen::IsInputDialogOpen()
|
||||
}
|
||||
|
||||
void ImGuiFullscreen::OpenInputStringDialog(
|
||||
std::string title, std::string message, std::string caption, std::string ok_button_text, InputStringDialogCallback callback)
|
||||
std::string title, std::string message, std::string caption, std::string ok_button_text, InputStringDialogCallback callback,
|
||||
std::string default_value, InputFilterType filter_type)
|
||||
{
|
||||
s_input_dialog_open = true;
|
||||
s_input_dialog_title = std::move(title);
|
||||
s_input_dialog_message = std::move(message);
|
||||
s_input_dialog_caption = std::move(caption);
|
||||
s_input_dialog_ok_text = std::move(ok_button_text);
|
||||
s_input_dialog_text = std::move(default_value);
|
||||
s_input_dialog_callback = std::move(callback);
|
||||
s_input_dialog_filter_type = filter_type;
|
||||
QueueResetFocus(FocusResetType::PopupOpened);
|
||||
}
|
||||
|
||||
@ -2520,10 +2524,11 @@ void ImGuiFullscreen::DrawInputDialog()
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, UIBackgroundColor);
|
||||
|
||||
bool is_open = true;
|
||||
if (ImGui::BeginPopupModal(s_input_dialog_title.c_str(), &is_open,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
|
||||
{
|
||||
ResetFocusHere();
|
||||
ImGui::TextWrapped("%s", s_input_dialog_message.c_str());
|
||||
@ -2542,7 +2547,40 @@ void ImGuiFullscreen::DrawInputDialog()
|
||||
{
|
||||
ImGui::SetNextItemWidth(ImGui::GetCurrentWindow()->WorkRect.GetWidth());
|
||||
}
|
||||
ImGui::InputText("##input", &s_input_dialog_text);
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
|
||||
|
||||
static auto input_callback = [](ImGuiInputTextCallbackData* data) -> int {
|
||||
InputFilterType* filter_type = static_cast<InputFilterType*>(data->UserData);
|
||||
|
||||
if (data->EventFlag == ImGuiInputTextFlags_CallbackCharFilter) {
|
||||
char c = static_cast<char>(data->EventChar);
|
||||
|
||||
if (*filter_type == InputFilterType::Numeric) {
|
||||
if (!std::isdigit(c)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (*filter_type == InputFilterType::IPAddress) {
|
||||
if (!std::isdigit(c) && c != '.') {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_None;
|
||||
if (s_input_dialog_filter_type != InputFilterType::None)
|
||||
flags |= ImGuiInputTextFlags_CallbackCharFilter;
|
||||
|
||||
if (s_focus_reset_queued != FocusResetType::None)
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
|
||||
ImGui::InputText("##input", &s_input_dialog_text, flags,
|
||||
(s_input_dialog_filter_type != InputFilterType::None) ? input_callback : nullptr,
|
||||
(s_input_dialog_filter_type != InputFilterType::None) ? static_cast<void*>(&s_input_dialog_filter_type) : nullptr);
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
|
||||
|
||||
@ -2574,7 +2612,7 @@ void ImGuiFullscreen::DrawInputDialog()
|
||||
else
|
||||
GetInputDialogHelpText(s_fullscreen_footer_text);
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopStyleColor(4);
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopFont();
|
||||
}
|
||||
@ -2591,6 +2629,7 @@ void ImGuiFullscreen::CloseInputDialog()
|
||||
s_input_dialog_ok_text = {};
|
||||
s_input_dialog_text = {};
|
||||
s_input_dialog_callback = {};
|
||||
s_input_dialog_filter_type = InputFilterType::None;
|
||||
}
|
||||
|
||||
bool ImGuiFullscreen::IsMessageBoxDialogOpen()
|
||||
@ -2831,61 +2870,56 @@ void ImGuiFullscreen::DrawBackgroundProgressDialogs(ImVec2& position, float spac
|
||||
if (s_background_progress_dialogs.empty())
|
||||
return;
|
||||
|
||||
const float window_width = LayoutScale(500.0f);
|
||||
const float window_height = LayoutScale(75.0f);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, UIPrimaryDarkColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, UISecondaryStrongColor);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, LayoutScale(4.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, LayoutScale(1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(10.0f, 10.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, LayoutScale(10.0f, 10.0f));
|
||||
ImGui::PushFont(g_medium_font.first, g_medium_font.second);
|
||||
|
||||
ImDrawList* dl = ImGui::GetForegroundDrawList();
|
||||
|
||||
for (const BackgroundProgressDialogData& data : s_background_progress_dialogs)
|
||||
{
|
||||
const float window_pos_x = position.x;
|
||||
const float window_pos_y = position.y - ((s_notification_vertical_direction < 0.0f) ? window_height : 0.0f);
|
||||
const std::string popup_id = fmt::format("##background_progress_dialog_{}", data.id);
|
||||
ImGui::SetNextWindowSize(LayoutScale(600.0f, 0.0f));
|
||||
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
||||
ImGui::OpenPopup(popup_id.c_str());
|
||||
|
||||
dl->AddRectFilled(ImVec2(window_pos_x, window_pos_y), ImVec2(window_pos_x + window_width, window_pos_y + window_height),
|
||||
IM_COL32(0x11, 0x11, 0x11, 200), LayoutScale(10.0f));
|
||||
ImGui::PushFont(g_large_font.first, g_large_font.second);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(20.0f, 20.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, UIBackgroundColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, UISecondaryColor);
|
||||
|
||||
ImVec2 pos(window_pos_x + LayoutScale(10.0f), window_pos_y + LayoutScale(10.0f));
|
||||
dl->AddText(g_medium_font.first, g_medium_font.second, pos, IM_COL32(255, 255, 255, 255), data.message.c_str(), nullptr, 0.0f);
|
||||
pos.y += g_medium_font.second + LayoutScale(10.0f);
|
||||
bool is_open = true;
|
||||
const u32 flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar;
|
||||
|
||||
const ImVec2 box_end(pos.x + window_width - LayoutScale(10.0f * 2.0f), pos.y + LayoutScale(25.0f));
|
||||
dl->AddRectFilled(pos, box_end, ImGui::GetColorU32(UIPrimaryDarkColor));
|
||||
|
||||
if (data.min != data.max)
|
||||
if (ImGui::BeginPopupModal(popup_id.c_str(), &is_open, flags))
|
||||
{
|
||||
const float fraction = static_cast<float>(data.value - data.min) / static_cast<float>(data.max - data.min);
|
||||
dl->AddRectFilled(pos, ImVec2(pos.x + fraction * (box_end.x - pos.x), box_end.y), ImGui::GetColorU32(UISecondaryColor));
|
||||
BeginMenuButtons();
|
||||
ResetFocusHere();
|
||||
|
||||
const std::string text(fmt::format("{}%", static_cast<int>(std::round(fraction * 100.0f))));
|
||||
const ImVec2 text_size(ImGui::CalcTextSize(text.c_str()));
|
||||
const ImVec2 text_pos(
|
||||
pos.x + ((box_end.x - pos.x) / 2.0f) - (text_size.x / 2.0f), pos.y + ((box_end.y - pos.y) / 2.0f) - (text_size.y / 2.0f));
|
||||
dl->AddText(g_medium_font.first, g_medium_font.second, text_pos, ImGui::GetColorU32(UIPrimaryTextColor), text.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// indeterminate, so draw a scrolling bar
|
||||
const float bar_width = LayoutScale(30.0f);
|
||||
const float fraction = std::fmod(ImGui::GetTime(), 2.0f) * 0.5f;
|
||||
const ImVec2 bar_start(pos.x + ImLerp(0.0f, box_end.x, fraction) - bar_width, pos.y);
|
||||
const ImVec2 bar_end(std::min(bar_start.x + bar_width, box_end.x), pos.y + LayoutScale(25.0f));
|
||||
dl->AddRectFilled(ImClamp(bar_start, pos, box_end), ImClamp(bar_end, pos, box_end), ImGui::GetColorU32(UISecondaryColor));
|
||||
ImGui::TextWrapped("%s", data.message.c_str());
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(20.0f));
|
||||
|
||||
if (data.min != data.max)
|
||||
{
|
||||
const float progress = static_cast<float>(data.value - data.min) / static_cast<float>(data.max - data.min);
|
||||
ImGui::ProgressBar(progress, ImVec2(-1.0f, LayoutScale(30.0f)));
|
||||
}
|
||||
else
|
||||
{
|
||||
const float fraction = std::fmod(ImGui::GetTime(), 2.0f) * 0.5f;
|
||||
ImGui::ProgressBar(fraction, ImVec2(-1.0f, LayoutScale(30.0f)));
|
||||
}
|
||||
|
||||
EndMenuButtons();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
position.y += s_notification_vertical_direction * (window_height + spacing);
|
||||
ImGui::PopStyleColor(5);
|
||||
ImGui::PopStyleVar(4);
|
||||
ImGui::PopFont();
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::PopFont();
|
||||
ImGui::PopStyleVar(4);
|
||||
ImGui::PopStyleColor(2);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -257,8 +257,16 @@ namespace ImGuiFullscreen
|
||||
|
||||
using InputStringDialogCallback = std::function<void(std::string text)>;
|
||||
bool IsInputDialogOpen();
|
||||
enum class InputFilterType : u8
|
||||
{
|
||||
None,
|
||||
Numeric,
|
||||
IPAddress
|
||||
};
|
||||
|
||||
void OpenInputStringDialog(
|
||||
std::string title, std::string message, std::string caption, std::string ok_button_text, InputStringDialogCallback callback);
|
||||
std::string title, std::string message, std::string caption, std::string ok_button_text, InputStringDialogCallback callback,
|
||||
std::string default_value = std::string(), InputFilterType filter_type = InputFilterType::None);
|
||||
void CloseInputDialog();
|
||||
|
||||
using ConfirmMessageDialogCallback = std::function<void(bool)>;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user