From 3ca76c674cd70710deb266caf498bcfbbac2e745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 22 Oct 2026 13:40:07 +0200 Subject: [PATCH] Outputs: Say the menus are on the device, instead of showing black When no screen on the stack shows anything on an output (we're in the menus), the background screen draws "Menus are on your device" there. UI drawn to an output gets the primary's DPI scale, over the whole output. Co-Authored-By: Claude Opus 5.5 (1M context) --- Common/UI/ScreenManager.cpp | 23 ++++++++++++++++++++++- UI/MiscScreens.cpp | 14 ++++++++++++++ UI/MiscScreens.h | 1 + 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Common/UI/ScreenManager.cpp b/Common/UI/ScreenManager.cpp index 38246c348a..1f994a57a8 100644 --- a/Common/UI/ScreenManager.cpp +++ b/Common/UI/ScreenManager.cpp @@ -2,6 +2,8 @@ #include "Common/System/System.h" #include "Common/Data/Collections/TinySet.h" #include "Common/UI/ScreenManager.h" +#include "Common/UI/Context.h" +#include "Common/Render/DrawBuffer.h" #include "Common/UI/Root.h" #include "Common/UI/View.h" #include "Common/UI/ScrollView.h" @@ -278,7 +280,11 @@ ScreenRenderFlags ScreenManager::Render(std::function afterPreRender) { afterPreRender(); - // Secondary outputs. The topmost screen with something for one draws it, otherwise it's left cleared. + // Secondary outputs. The topmost screen with something for one draws it, then the background screen. + UIContext *ui = getUIContext(); + if (!outputs_.empty()) { + ui->BeginFrame(); + } for (const Output &output : outputs_) { Draw::Swapchain *swapchain = output.display.swapchain; if (!swapchain) { @@ -289,11 +295,26 @@ ScreenRenderFlags ScreenManager::Render(std::function afterPreRender) { draw_->GetFramebufferDimensions(swapchain, &w, &h); draw_->SetViewport(Draw::Viewport{0.0f, 0.0f, (float)w, (float)h, 0.0f, 1.0f}); draw_->SetScissorRect(0, 0, w, h); + + // UI drawn there gets the primary's DPI scale, over the whole output. + const float dpWidth = w * g_display.dpi_scale_x; + const float dpHeight = h * g_display.dpi_scale_y; + const Bounds primaryBounds = ui->GetBounds(); + ui->SetBounds(Bounds(0, 0, dpWidth, dpHeight)); + ui->Draw()->PushDrawMatrix(ComputeOrthoMatrix(dpWidth, dpHeight, draw_->GetDeviceCaps().coordConvention)); + bool drawn = false; for (auto iter = stack_.rbegin(); iter != stack_.rend(); ++iter) { if (iter->screen->renderOutput(output.display)) { + drawn = true; break; } } + if (!drawn && backgroundScreen_) { + backgroundScreen_->renderOutput(output.display); + } + ui->Flush(); + ui->Draw()->PopDrawMatrix(); + ui->SetBounds(primaryBounds); } // Now, start the final render pass. This is now the ONLY place where binding the backbuffer is allowed. diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index dede720f6d..3c2e9cc6ab 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -97,6 +97,20 @@ void HandleCommonMessages(UIMessage message, const char *value, ScreenManager *m } } +// Nothing on the stack shows anything there, so we're in the menus. +bool BackgroundScreen::renderOutput(const OutputDisplay &output) { + if (output.role != OutputRole::Game) { + return false; + } + UIContext &dc = *screenManager()->getUIContext(); + auto di = GetI18NCategory(I18NCat::DIALOG); + const Bounds &bounds = dc.GetBounds(); + dc.Begin(); + dc.DrawText(di->T("Menus are on your device"), bounds.centerX(), bounds.centerY(), 0xFF505050, ALIGN_CENTER); + dc.Flush(); + return true; +} + ScreenRenderFlags BackgroundScreen::render(ScreenRenderMode mode) { UIContext *uiContext = screenManager()->getUIContext(); diff --git a/UI/MiscScreens.h b/UI/MiscScreens.h index 9e9fc8cd0d..ceb8b35ae8 100644 --- a/UI/MiscScreens.h +++ b/UI/MiscScreens.h @@ -37,6 +37,7 @@ inline void NoOpVoidBool(bool) {} class BackgroundScreen : public UIScreen { public: ScreenRenderFlags render(ScreenRenderMode mode) override; + bool renderOutput(const OutputDisplay &output) override; void sendMessage(UIMessage message, const char *value) override; private: