#include "prx/libSceVideoOut/include/DisplayWindow.hpp" #include "prx/libSceAgcDriver/Execution/include/AspectFit.hpp" #include "prx/libkernel/AppMetadata/include/AppMetadata.hpp" #include "SDL_vulkan.h" #include #include #ifdef _WIN32 #include "SDL_syswm.h" #include #include #endif namespace { void require(bool condition, const char* reason) { if (!condition) throw std::runtime_error(std::string("DisplayWindow: ") + reason); } #ifdef _WIN32 constexpr UINT_PTR DisplayWindowSubclassId = 0x41505335u; #endif } DisplayWindow::~DisplayWindow() { Destroy(); } void DisplayWindow::Ensure(std::uint32_t sourceWidth, std::uint32_t sourceHeight) { require(sourceWidth != 0 && sourceHeight != 0, "source extent must be non-zero"); if (window == nullptr) create(sourceWidth, sourceHeight); updateAspectRatio(sourceWidth, sourceHeight); } void DisplayWindow::create(std::uint32_t sourceWidth, std::uint32_t sourceHeight) { SDL_Rect usable{}; require(SDL_GetDisplayUsableBounds(0, &usable) == 0, SDL_GetError()); std::uint32_t initialWidth = sourceWidth; std::uint32_t initialHeight = sourceHeight; if (usable.w > 0 && usable.h > 0 && (sourceWidth > static_cast(usable.w) || sourceHeight > static_cast(usable.h))) { const auto fitted = AgcDriver::ComputeContainSize_nid_postfix(sourceWidth, sourceHeight, static_cast(usable.w), static_cast(usable.h), false); initialWidth = fitted.width; initialHeight = fitted.height; } const auto title = GetAppTitle_nid_postfix(); window = SDL_CreateWindow(title.value, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, static_cast(initialWidth), static_cast(initialHeight), SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); require(window != nullptr, SDL_GetError()); SDL_SetWindowMinimumSize(window, static_cast(DisplayWindowMinimumWidth), static_cast(DisplayWindowMinimumHeight)); installSubclass(); } void DisplayWindow::updateAspectRatio(std::uint32_t sourceWidth, std::uint32_t sourceHeight) { aspectWidth = sourceWidth; aspectHeight = sourceHeight; } void DisplayWindow::Destroy() noexcept { if (window == nullptr) return; removeSubclass(); SDL_DestroyWindow(window); window = nullptr; } SDL_Window* DisplayWindow::Handle() const { return window; } void DisplayWindow::DrawableSize(std::uint32_t& width, std::uint32_t& height) const { if (window == nullptr) { width = 0; height = 0; return; } int drawableWidth = 0; int drawableHeight = 0; SDL_Vulkan_GetDrawableSize(window, &drawableWidth, &drawableHeight); width = drawableWidth > 0 ? static_cast(drawableWidth) : 0; height = drawableHeight > 0 ? static_cast(drawableHeight) : 0; } void DisplayWindow::installSubclass() { #ifdef _WIN32 SDL_SysWMinfo info; SDL_VERSION(&info.version); require(SDL_GetWindowWMInfo(window, &info) == SDL_TRUE, SDL_GetError()); require(info.subsystem == SDL_SYSWM_WINDOWS, "unsupported window subsystem"); const auto attached = SetWindowSubclass(info.info.win.window, reinterpret_cast(&DisplayWindow::windowProc), DisplayWindowSubclassId, reinterpret_cast(this)); require(attached != FALSE, "SetWindowSubclass failed"); #endif } void DisplayWindow::removeSubclass() noexcept { #ifdef _WIN32 if (window == nullptr) return; SDL_SysWMinfo info; SDL_VERSION(&info.version); if (SDL_GetWindowWMInfo(window, &info) != SDL_TRUE) return; if (info.subsystem != SDL_SYSWM_WINDOWS) return; RemoveWindowSubclass(info.info.win.window, reinterpret_cast(&DisplayWindow::windowProc), DisplayWindowSubclassId); #endif } void DisplayWindow::applyAspectRatio(std::uintptr_t edge, void* rect) const { #ifdef _WIN32 if (aspectWidth == 0 || aspectHeight == 0) return; auto* bounds = static_cast(rect); const auto currentWidth = static_cast(bounds->right - bounds->left); const auto currentHeight = static_cast(bounds->bottom - bounds->top); if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM) { const auto width = AgcDriver::ComputeWidthForHeight_nid_postfix(aspectWidth, aspectHeight, currentHeight); bounds->right = bounds->left + static_cast(width); return; } const auto height = AgcDriver::ComputeHeightForWidth_nid_postfix(aspectWidth, aspectHeight, currentWidth); if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT) bounds->top = bounds->bottom - static_cast(height); else bounds->bottom = bounds->top + static_cast(height); #else static_cast(edge); static_cast(rect); #endif } std::intptr_t __stdcall DisplayWindow::windowProc(void* hwnd, unsigned int message, std::uintptr_t wParam, std::intptr_t lParam, std::uintptr_t subclassId, std::uintptr_t referenceData) { #ifdef _WIN32 static_cast(subclassId); if (message == WM_SIZING) { reinterpret_cast(referenceData)->applyAspectRatio(wParam, reinterpret_cast(lParam)); } return DefSubclassProc(static_cast(hwnd), message, static_cast(wParam), static_cast(lParam)); #else static_cast(hwnd); static_cast(message); static_cast(wParam); static_cast(lParam); static_cast(subclassId); static_cast(referenceData); return 0; #endif }