window: implemented monitor switching

This commit is contained in:
Hyper 2024-12-16 19:05:40 +00:00
parent 74e6df96d4
commit f547c7ca6d
8 changed files with 224 additions and 95 deletions

View file

@ -29,6 +29,11 @@ SWA_API void Game_PlaySound(const char* pName)
}
}
SWA_API void Window_SetDisplay(int displayIndex)
{
Window::SetDisplay(displayIndex);
}
SWA_API void Window_SetFullscreen(bool isEnabled)
{
Window::SetFullscreen(isEnabled);

View file

@ -1,4 +1,5 @@
#pragma once
SWA_API void Game_PlaySound(const char* pName);
SWA_API void Window_SetDisplay(int displayIndex);
SWA_API void Window_SetFullscreen(bool isEnabled);

View file

@ -390,6 +390,8 @@ enum GuestTextureAddress
D3DTADDRESS_BORDER = 6
};
extern bool g_needsResize;
extern std::unique_ptr<GuestTexture> LoadTexture(const uint8_t* data, size_t dataSize, RenderComponentMapping componentMapping = RenderComponentMapping());
extern void VideoConfigValueChangedCallback(class IConfigDef* config);

View file

@ -50,6 +50,18 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
{ ELanguage::English, "This option is not available at this location." }
}
},
{
"Options_Desc_NotAvailableWindowed",
{
{ ELanguage::English, "This option is not available in windowed mode." }
}
},
{
"Options_Desc_NotAvailableHardware",
{
{ ELanguage::English, "This option is not available with your current hardware configuration." }
}
},
{
"Options_Desc_NotAvailableMSAA",
{

View file

@ -424,7 +424,7 @@ static bool DrawCategories()
template<typename T>
static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* config,
bool isAccessible, std::string* inaccessibleReason = nullptr,
T valueMin = T(0), T valueCenter = T(0.5), T valueMax = T(1))
T valueMin = T(0), T valueCenter = T(0.5), T valueMax = T(1), bool isSlider = true)
{
auto drawList = ImGui::GetForegroundDrawList();
auto clipRectMin = drawList->GetClipRectMin();
@ -522,6 +522,10 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
// TODO: check if value was changed?
VideoConfigValueChangedCallback(config);
// TODO: check if value was changed?
if (config->Callback)
config->Callback(config);
Game_PlaySound("sys_worldmap_decide");
}
}
@ -568,44 +572,47 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
drawList->AddRectFilledMultiColor(min, max, IM_COL32(0, 0, 0, 13 * alpha), IM_COL32(0, 0, 0, 0), IM_COL32(0, 0, 0, 55 * alpha), IM_COL32(0, 0, 0, 6 * alpha));
drawList->AddRectFilledMultiColor(min, max, IM_COL32(0, 130, 0, 13 * alpha), IM_COL32(0, 130, 0, 111 * alpha), IM_COL32(0, 130, 0, 0), IM_COL32(0, 130, 0, 55 * alpha));
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, int32_t>)
if (isSlider)
{
// Inner container of slider
const uint32_t innerColor0 = IM_COL32(0, 65, 0, 255 * alpha);
const uint32_t innerColor1 = IM_COL32(0, 32, 0, 255 * alpha);
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, int32_t>)
{
// Inner container of slider
const uint32_t innerColor0 = IM_COL32(0, 65, 0, 255 * alpha);
const uint32_t innerColor1 = IM_COL32(0, 32, 0, 255 * alpha);
float xPadding = Scale(6.0f);
float yPadding = Scale(3.0f);
float xPadding = Scale(6.0f);
float yPadding = Scale(3.0f);
drawList->AddRectFilledMultiColor
(
{ min.x + xPadding, min.y + yPadding },
{ max.x - xPadding, max.y - yPadding },
innerColor0,
innerColor0,
innerColor1,
innerColor1
);
drawList->AddRectFilledMultiColor
(
{ min.x + xPadding, min.y + yPadding },
{ max.x - xPadding, max.y - yPadding },
innerColor0,
innerColor0,
innerColor1,
innerColor1
);
// The actual slider
const uint32_t sliderColor0 = IM_COL32(57, 241, 0, 255 * alpha);
const uint32_t sliderColor1 = IM_COL32(2, 106, 0, 255 * alpha);
// The actual slider
const uint32_t sliderColor0 = IM_COL32(57, 241, 0, 255 * alpha);
const uint32_t sliderColor1 = IM_COL32(2, 106, 0, 255 * alpha);
xPadding += Scale(1.0f);
yPadding += Scale(1.0f);
xPadding += Scale(1.0f);
yPadding += Scale(1.0f);
ImVec2 sliderMin = { min.x + xPadding, min.y + yPadding };
ImVec2 sliderMax = { max.x - xPadding, max.y - yPadding };
float factor;
ImVec2 sliderMin = { min.x + xPadding, min.y + yPadding };
ImVec2 sliderMax = { max.x - xPadding, max.y - yPadding };
float factor;
if (config->Value <= valueCenter)
factor = float(config->Value - valueMin) / (valueCenter - valueMin) * 0.5f;
else
factor = 0.5f + float(config->Value - valueCenter) / (valueMax - valueCenter) * 0.5f;
sliderMax.x = sliderMin.x + (sliderMax.x - sliderMin.x) * factor;
if (config->Value <= valueCenter)
factor = float(config->Value - valueMin) / (valueCenter - valueMin) * 0.5f;
else
factor = 0.5f + float(config->Value - valueCenter) / (valueMax - valueCenter) * 0.5f;
drawList->AddRectFilledMultiColor(sliderMin, sliderMax, sliderColor0, sliderColor0, sliderColor1, sliderColor1);
sliderMax.x = sliderMin.x + (sliderMax.x - sliderMin.x) * factor;
drawList->AddRectFilledMultiColor(sliderMin, sliderMax, sliderColor0, sliderColor0, sliderColor1, sliderColor1);
}
}
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
@ -732,7 +739,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
config->Value = std::clamp(config->Value, valueMin, valueMax);
}
if (config->Callback)
if ((increment || decrement) && config->Callback)
config->Callback(config);
}
@ -743,7 +750,10 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
}
else if constexpr (std::is_same_v<T, int32_t>)
{
valueText = config->Value >= valueMax ? Localise("Options_Value_Max") : fmt::format("{}", config->Value);
valueText = fmt::format("{}", config->Value);
if (isSlider && config->Value >= valueMax)
valueText = Localise("Options_Value_Max");
}
else
{
@ -798,45 +808,61 @@ static void DrawConfigOptions()
// TODO: Don't use raw numbers here!
switch (g_categoryIndex)
{
case 0: // SYSTEM
DrawConfigOption(rowCount++, yOffset, &Config::Language, !OptionsMenu::s_isPause, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::Hints, !isStage, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::ControlTutorial, !isStage, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::AchievementNotifications, true);
DrawConfigOption(rowCount++, yOffset, &Config::TimeOfDayTransition, true);
break;
case 1: // INPUT
DrawConfigOption(rowCount++, yOffset, &Config::InvertCameraX, true);
DrawConfigOption(rowCount++, yOffset, &Config::InvertCameraY, true);
DrawConfigOption(rowCount++, yOffset, &Config::AllowBackgroundInput, true);
DrawConfigOption(rowCount++, yOffset, &Config::AllowDPadMovement, true);
DrawConfigOption(rowCount++, yOffset, &Config::ControllerIcons, true);
break;
case 2: // AUDIO
DrawConfigOption(rowCount++, yOffset, &Config::MusicVolume, true);
DrawConfigOption(rowCount++, yOffset, &Config::EffectsVolume, true);
DrawConfigOption(rowCount++, yOffset, &Config::VoiceLanguage, OptionsMenu::s_pauseMenuType == SWA::eMenuType_WorldMap, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::Subtitles, true);
DrawConfigOption(rowCount++, yOffset, &Config::MusicAttenuation, AudioPatches::CanAttenuate(), &Localise("Options_Desc_OSNotSupported"));
DrawConfigOption(rowCount++, yOffset, &Config::BattleTheme, true);
break;
case 3: // VIDEO
// TODO: expose WindowWidth/WindowHeight as WindowSize.
DrawConfigOption(rowCount++, yOffset, &Config::AspectRatio, true);
DrawConfigOption(rowCount++, yOffset, &Config::ResolutionScale, true, nullptr, 0.25f, 1.0f, 2.0f);
DrawConfigOption(rowCount++, yOffset, &Config::Fullscreen, true);
DrawConfigOption(rowCount++, yOffset, &Config::VSync, true);
DrawConfigOption(rowCount++, yOffset, &Config::FPS, true, nullptr, 15, 120, 240);
DrawConfigOption(rowCount++, yOffset, &Config::Brightness, true);
DrawConfigOption(rowCount++, yOffset, &Config::AntiAliasing, true);
DrawConfigOption(rowCount++, yOffset, &Config::TransparencyAntiAliasing, Config::AntiAliasing != EAntiAliasing::None, &Localise("Options_Desc_NotAvailableMSAA"));
DrawConfigOption(rowCount++, yOffset, &Config::ShadowResolution, true);
DrawConfigOption(rowCount++, yOffset, &Config::GITextureFiltering, true);
DrawConfigOption(rowCount++, yOffset, &Config::MotionBlur, true);
DrawConfigOption(rowCount++, yOffset, &Config::XboxColorCorrection, true);
DrawConfigOption(rowCount++, yOffset, &Config::MovieScaleMode, true);
DrawConfigOption(rowCount++, yOffset, &Config::UIScaleMode, true);
break;
case 0: // SYSTEM
DrawConfigOption(rowCount++, yOffset, &Config::Language, !OptionsMenu::s_isPause, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::Hints, !isStage, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::ControlTutorial, !isStage, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::AchievementNotifications, true);
DrawConfigOption(rowCount++, yOffset, &Config::TimeOfDayTransition, true);
break;
case 1: // INPUT
DrawConfigOption(rowCount++, yOffset, &Config::InvertCameraX, true);
DrawConfigOption(rowCount++, yOffset, &Config::InvertCameraY, true);
DrawConfigOption(rowCount++, yOffset, &Config::AllowBackgroundInput, true);
DrawConfigOption(rowCount++, yOffset, &Config::AllowDPadMovement, true);
DrawConfigOption(rowCount++, yOffset, &Config::ControllerIcons, true);
break;
case 2: // AUDIO
DrawConfigOption(rowCount++, yOffset, &Config::MusicVolume, true);
DrawConfigOption(rowCount++, yOffset, &Config::EffectsVolume, true);
DrawConfigOption(rowCount++, yOffset, &Config::VoiceLanguage, OptionsMenu::s_pauseMenuType == SWA::eMenuType_WorldMap, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::Subtitles, true);
DrawConfigOption(rowCount++, yOffset, &Config::MusicAttenuation, AudioPatches::CanAttenuate(), &Localise("Options_Desc_OSNotSupported"));
DrawConfigOption(rowCount++, yOffset, &Config::BattleTheme, true);
break;
case 3: // VIDEO
{
// TODO: expose WindowWidth/WindowHeight as WindowSize.
auto displayCount = Window::GetDisplayCount();
auto canChangeMonitor = Config::Fullscreen && displayCount > 1;
auto monitorReason = &Localise("Options_Desc_NotAvailableWindowed");
if (Config::Fullscreen && displayCount <= 1)
monitorReason = &Localise("Options_Desc_NotAvailableHardware");
DrawConfigOption(rowCount++, yOffset, &Config::Monitor, canChangeMonitor, monitorReason, 0, 0, displayCount - 1, false);
DrawConfigOption(rowCount++, yOffset, &Config::AspectRatio, true);
DrawConfigOption(rowCount++, yOffset, &Config::ResolutionScale, true, nullptr, 0.25f, 1.0f, 2.0f);
DrawConfigOption(rowCount++, yOffset, &Config::Fullscreen, true);
DrawConfigOption(rowCount++, yOffset, &Config::VSync, true);
DrawConfigOption(rowCount++, yOffset, &Config::FPS, true, nullptr, 15, 120, 240);
DrawConfigOption(rowCount++, yOffset, &Config::Brightness, true);
DrawConfigOption(rowCount++, yOffset, &Config::AntiAliasing, true);
DrawConfigOption(rowCount++, yOffset, &Config::TransparencyAntiAliasing, Config::AntiAliasing != EAntiAliasing::None, &Localise("Options_Desc_NotAvailableMSAA"));
DrawConfigOption(rowCount++, yOffset, &Config::ShadowResolution, true);
DrawConfigOption(rowCount++, yOffset, &Config::GITextureFiltering, true);
DrawConfigOption(rowCount++, yOffset, &Config::MotionBlur, true);
DrawConfigOption(rowCount++, yOffset, &Config::XboxColorCorrection, true);
DrawConfigOption(rowCount++, yOffset, &Config::MovieScaleMode, true);
DrawConfigOption(rowCount++, yOffset, &Config::UIScaleMode, true);
break;
}
}
auto inputState = SWA::CInputState::GetInstance();
@ -948,7 +974,7 @@ static void DrawInfoPanel()
auto clipRectMin = drawList->GetClipRectMin();
auto clipRectMax = drawList->GetClipRectMax();
ImVec2 thumbnailMax = { clipRectMin.x + ScaleX(343.0f), clipRectMin.y + ScaleY(198.0f) };
ImVec2 thumbnailMax = { clipRectMax.x, clipRectMin.y + Scale(198.0f) };
if (g_selectedItem)
{

View file

@ -3,6 +3,7 @@
#include <user/config.h>
#include <SDL_syswm.h>
#include <app.h>
#include <gpu/video.h>
bool m_isFullscreenKeyReleased = true;
bool m_isResizing = false;
@ -33,8 +34,14 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
Config::Fullscreen = Window::SetFullscreen(!Window::IsFullscreen());
if (!Config::Fullscreen)
if (Config::Fullscreen)
{
Config::Monitor = Window::GetDisplay();
}
else
{
Config::WindowState = Window::SetMaximised(Config::WindowState == EWindowState::Maximised);
}
// Block holding ALT+ENTER spamming window changes.
m_isFullscreenKeyReleased = false;
@ -45,7 +52,7 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
// Restore original window dimensions on F2.
case SDLK_F2:
Config::Fullscreen = Window::SetFullscreen(false);
Window::SetDimensions(DEFAULT_WIDTH, DEFAULT_HEIGHT);
Window::ResetDimensions();
break;
// Recentre window on F3.
@ -118,11 +125,9 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
}
case SDL_USER_EVILSONIC:
{
Window::s_isIconNight = event->user.code;
Window::SetIcon(Window::s_isIconNight);
break;
}
}
if (!Window::IsFullscreen())
@ -158,23 +163,15 @@ void Window::Init()
s_x = s_y = SDL_WINDOWPOS_CENTERED;
if (!IsPositionValid())
{
s_x = SDL_WINDOWPOS_CENTERED;
s_y = SDL_WINDOWPOS_CENTERED;
s_width = DEFAULT_WIDTH;
s_height = DEFAULT_HEIGHT;
Config::WindowX = s_x;
Config::WindowY = s_y;
Config::WindowWidth = s_width;
Config::WindowHeight = s_height;
}
Window::ResetDimensions();
s_pWindow = SDL_CreateWindow("SWA", s_x, s_y, s_width, s_height, GetWindowFlags());
if (IsFullscreen())
SDL_ShowCursor(SDL_DISABLE);
SetDisplay(Config::Monitor);
SetIcon();
SetTitle();
SDL_SetWindowMinimumSize(s_pWindow, 640, 480);
@ -192,7 +189,7 @@ void Window::Init()
void Window::Update()
{
if (!Window::IsFullscreen() && !Window::IsMaximised())
if (!Window::IsFullscreen() && !Window::IsMaximised() && !s_isChangingDisplay)
{
Config::WindowX = Window::s_x;
Config::WindowY = Window::s_y;
@ -205,4 +202,7 @@ void Window::Update()
SetTitle();
m_isResizing = false;
}
if (g_needsResize)
s_isChangingDisplay = false;
}

View file

@ -29,6 +29,7 @@ public:
static inline bool s_isFocused;
static inline bool s_isIconNight;
static inline bool s_isFullscreenCursorVisible;
static inline bool s_isChangingDisplay;
static SDL_Surface* GetIconSurface(void* pIconBmp, size_t iconSize)
{
@ -107,7 +108,9 @@ public:
{
SDL_SetWindowFullscreen(s_pWindow, 0);
SDL_ShowCursor(SDL_ENABLE);
SetIcon(Window::s_isIconNight);
SetDimensions(Config::WindowWidth, Config::WindowHeight, Config::WindowX, Config::WindowY);
}
return isEnabled;
@ -172,6 +175,19 @@ public:
SDL_MoveEvent(s_pWindow, x, y);
}
static void ResetDimensions()
{
s_x = SDL_WINDOWPOS_CENTERED;
s_y = SDL_WINDOWPOS_CENTERED;
s_width = DEFAULT_WIDTH;
s_height = DEFAULT_HEIGHT;
Config::WindowX = s_x;
Config::WindowY = s_y;
Config::WindowWidth = s_width;
Config::WindowHeight = s_height;
}
static uint32_t GetWindowFlags()
{
uint32_t flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE;
@ -185,19 +201,78 @@ public:
return flags;
}
static bool IsPositionValid()
static int GetDisplayCount()
{
auto displayCount = SDL_GetNumVideoDisplays();
auto result = SDL_GetNumVideoDisplays();
if (displayCount <= 0)
if (result < 0)
{
LOGF_ERROR("Failed to validate window position: {}", SDL_GetError());
return false;
LOGF_ERROR("Failed to get display count: {}", SDL_GetError());
return 1;
}
return result;
}
static int GetDisplay()
{
auto displayCount = GetDisplayCount();
for (int i = 0; i < displayCount; i++)
{
SDL_Rect bounds;
if (SDL_GetDisplayBounds(i, &bounds) == 0)
{
auto x = s_x;
auto y = s_y;
if (x == SDL_WINDOWPOS_CENTERED)
x = bounds.w / 2 - s_width / 2;
if (y == SDL_WINDOWPOS_CENTERED)
y = bounds.h / 2 - s_height / 2;
if (x >= bounds.x && x < bounds.x + bounds.w &&
y >= bounds.y && y < bounds.y + bounds.h)
{
return i;
}
}
}
return 0;
}
static void SetDisplay(int displayIndex)
{
if (!IsFullscreen())
return;
s_isChangingDisplay = true;
SDL_Rect bounds;
if (SDL_GetDisplayBounds(displayIndex, &bounds) == 0)
{
SetFullscreen(false);
SetDimensions(bounds.w, bounds.h, bounds.x, bounds.y);
SetFullscreen(true);
}
else
{
ResetDimensions();
}
}
static bool IsPositionValid()
{
auto displayCount = GetDisplayCount();
for (int i = 0; i < displayCount; i++)
{
SDL_Rect bounds;
if (SDL_GetDisplayBounds(i, &bounds) == 0)
{
auto x = s_x;

View file

@ -34,7 +34,14 @@ public:
CONFIG_DEFINE("Video", int32_t, WindowWidth, 1280);
CONFIG_DEFINE("Video", int32_t, WindowHeight, 720);
CONFIG_DEFINE_ENUM("Video", EWindowState, WindowState, EWindowState::Normal);
CONFIG_DEFINE_LOCALISED("Video", size_t, Monitor, 0);
CONFIG_DEFINE_CALLBACK("Video", int32_t, Monitor, 0,
{
def->Locale = &g_Monitor_locale;
Window_SetDisplay(def->Value);
});
CONFIG_DEFINE_ENUM_LOCALISED("Video", EAspectRatio, AspectRatio, EAspectRatio::Auto);
CONFIG_DEFINE_CALLBACK("Video", float, ResolutionScale, 1.0f,
@ -48,6 +55,7 @@ public:
def->Locale = &g_Fullscreen_locale;
Window_SetFullscreen(def->Value);
Window_SetDisplay(Monitor);
});
CONFIG_DEFINE_LOCALISED("Video", bool, VSync, true);