Add ImGui screen for Aurora Null backend

Resolves #628
This commit is contained in:
Luke Street
2026-05-06 16:39:28 -06:00
parent c21bce0093
commit 7f0955f022
9 changed files with 160 additions and 69 deletions
+1 -1
-4
View File
@@ -79,15 +79,11 @@ toast heading > row {
toast message {
display: flex;
flex-flow: column;
align-items: start;
justify-content: start;
gap: 8dp;
}
toast message row {
display: flex;
align-items: start;
justify-content: start;
}
toast message row.muted {
+63 -1
View File
@@ -10,7 +10,7 @@
#include "fmt/format.h"
#include "ImGuiConsole.hpp"
#include "dusk/ui/ui.hpp"
#include "ImGuiEngine.hpp"
#include "JSystem/JUtility/JUTGamePad.h"
#include "SDL3/SDL_mouse.h"
#include "dusk/audio/DuskAudioSystem.h"
@@ -20,6 +20,7 @@
#include "dusk/livesplit.h"
#include "dusk/main.h"
#include "dusk/settings.h"
#include "dusk/ui/ui.hpp"
#include "f_pc/f_pc_manager.h"
#include "f_pc/f_pc_name.h"
#include "m_Do/m_Do_controller_pad.h"
@@ -302,6 +303,67 @@ namespace dusk {
UpdateDragScroll();
// Show message when Aurora backend is Null
if (aurora_get_backend() == BACKEND_NULL) {
auto& io = ImGui::GetIO();
ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x, io.DisplaySize.y));
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowBgAlpha(0.65f);
ImGui::Begin("Pre Launch Window", nullptr,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::NewLine();
if (ImGuiEngine::duskLogo) {
const auto& windowSize = ImGui::GetWindowSize();
ImGui::NewLine();
float iconSize = 150.f;
float width = iconSize * 2.5f;
ImGui::SameLine(windowSize.x / 2 - width + (width / 2));
ImGui::Image(ImGuiEngine::duskLogo, ImVec2{width, iconSize});
} else {
ImGui::PushFont(ImGuiEngine::fontExtraLarge);
ImGuiTextCenter("Dusk");
ImGui::PopFont();
}
ImGui::PushFont(ImGuiEngine::fontLarge);
ImGuiTextCenter("Failed to initialize any graphics backend");
const auto& style = ImGui::GetStyle();
const auto retrySize = ImGui::CalcTextSize("Retry (Auto backend)");
const auto quitSize = ImGui::CalcTextSize("Quit");
float buttonsWidth = quitSize.x + style.FramePadding.x * 2.0f;
if constexpr (SupportsProcessRestart) {
buttonsWidth += retrySize.x + style.FramePadding.x * 2.0f + style.ItemSpacing.x;
}
#if DUSK_CAN_OPEN_DATA_FOLDER
const auto openSize = ImGui::CalcTextSize("Open Data Folder");
buttonsWidth += openSize.x + style.FramePadding.x * 2.0f + style.ItemSpacing.x;
#endif
ImGui::NewLine();
ImGui::SetCursorPosX(
ImMax(style.WindowPadding.x, (ImGui::GetWindowSize().x - buttonsWidth) * 0.5f));
if constexpr (SupportsProcessRestart) {
if (ImGui::Button("Retry (Auto backend)")) {
getSettings().backend.graphicsBackend.setValue("auto");
config::Save();
RestartRequested = true;
IsRunning = false;
}
ImGui::SameLine();
}
#if DUSK_CAN_OPEN_DATA_FOLDER
if (ImGui::Button("Open Data Folder")) {
OpenDataFolder();
}
ImGui::SameLine();
#endif
if (ImGui::Button("Quit")) {
IsRunning = false;
}
ImGui::PopFont();
ImGui::End();
}
m_menuGame.windowControllerConfig();
m_menuGame.windowInputViewer();
m_menuGame.drawSpeedrunTimerOverlay();
+24 -1
View File
@@ -2,13 +2,16 @@
#define DUSK_IMGUI_HPP
#include <deque>
#include <filesystem>
#include <string>
#include <string_view>
#include <SDL3/SDL_misc.h>
#include <aurora/aurora.h>
#include "ImGuiMenuGame.hpp"
#include "ImGuiMenuTools.hpp"
#include "dusk/main.h"
#include "imgui.h"
union SDL_Event;
@@ -23,7 +26,7 @@ public:
void PreDraw();
void PostDraw();
static bool CheckMenuViewToggle(ImGuiKey key, bool& active);
static bool CheckMenuViewToggle(ImGuiKey key, bool& active);
void AddToast(std::string_view message, float duration = 3.f);
private:
@@ -72,4 +75,24 @@ float ImGuiScale();
void DuskDebugPad();
#if defined(_WIN32) || \
(defined(__APPLE__) && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_MACCATALYST) || \
(defined(__linux__) && !defined(__ANDROID__))
#define DUSK_CAN_OPEN_DATA_FOLDER 1
namespace fs = std::filesystem;
static void OpenDataFolder() {
const std::string path = fs::absolute(dusk::ConfigPath).generic_string();
#if defined(_WIN32)
const std::string url = std::string("file:///") + path;
#else
const std::string url = std::string("file://") + path;
#endif
(void)SDL_OpenURL(url.c_str());
}
#else
#define DUSK_CAN_OPEN_DATA_FOLDER 0
#endif
#endif // DUSK_IMGUI_HPP
+1 -1
View File
@@ -219,7 +219,7 @@ void ImGuiEngine_AddTextures() {
ImGuiEngine::orgIcon = AddTexture("org-icon.png");
}
if (ImGuiEngine::duskLogo == 0) {
ImGuiEngine::duskLogo = AddTexture("logo.png");
ImGuiEngine::duskLogo = AddTexture("logo-mascot.png");
}
}
} // namespace dusk
-18
View File
@@ -23,24 +23,6 @@
#include <TargetConditionals.h>
#endif
#if defined(_WIN32) || (defined(__APPLE__) && !TARGET_OS_IOS && !TARGET_OS_MACCATALYST) || (defined(__linux__) && !defined(__ANDROID__))
#define DUSK_CAN_OPEN_DATA_FOLDER 1
namespace fs = std::filesystem;
static void OpenDataFolder() {
const std::string path = fs::absolute(dusk::ConfigPath).generic_string();
#if defined(_WIN32)
const std::string url = std::string("file:///") + path;
#else
const std::string url = std::string("file://") + path;
#endif
(void)SDL_OpenURL(url.c_str());
}
#else
#define DUSK_CAN_OPEN_DATA_FOLDER 0
#endif
namespace aurora::gx {
extern bool enableLodBias;
}
+48 -43
View File
@@ -21,53 +21,58 @@ const Rml::String kDocumentSource = R"RML(
</rml>
)RML";
constexpr std::array<std::pair<const char*, const char*>, 3> kAutoSaveLayers{{
{"inner", "res/org-icon-inner.png"},
{"outer", "res/org-icon-outer.png"},
{"center", "res/org-icon-center.png"},
}};
Rml::Element* create_toast(Rml::Element* parent, const Toast& toast) {
if (toast.type == "autosave") {
Rml::Factory::InstanceElementText(parent, R"RML(
<logo>
<img class="inner" src="res/org-icon-inner.png" />
<img class="outer" src="res/org-icon-outer.png" />
<img class="center" src="res/org-icon-center.png" />
</logo>
)RML");
return parent->GetFirstChild();
} else {
auto* elem = append(parent, "toast");
if (!toast.type.empty()) {
elem->SetClass(toast.type, true);
auto* logo = append(parent, "logo");
for (const auto [cls, src] : kAutoSaveLayers) {
auto* img = append(logo, "img");
img->SetClass(cls, true);
img->SetAttribute("src", src);
}
{
auto* heading = append(elem, "heading");
if (toast.title.starts_with("<")) {
heading->SetInnerRML(toast.title);
} else {
auto* span = append(heading, "span");
span->SetInnerRML(toast.title);
}
if (toast.type == "achievement") {
auto* icon = append(heading, "icon");
icon->SetClass("trophy", true);
mDoAud_seStartMenu(kSoundAchievementUnlock);
} else if (toast.type == "controller") {
auto* icon = append(heading, "icon");
icon->SetClass("controller", true);
}
}
{
auto* message = append(elem, "message");
if (toast.content.starts_with("<")) {
message->SetInnerRML(toast.content);
} else {
auto* span = append(message, "span");
span->SetInnerRML(toast.content);
}
}
{
auto* progress = append(elem, "progress");
progress->SetAttribute("value", 1.f);
}
return elem;
return logo;
}
auto* elem = append(parent, "toast");
if (!toast.type.empty()) {
elem->SetClass(toast.type, true);
}
{
auto* heading = append(elem, "heading");
if (toast.title.starts_with("<")) {
heading->SetInnerRML(toast.title);
} else {
auto* span = append(heading, "span");
span->SetInnerRML(toast.title);
}
if (toast.type == "achievement") {
auto* icon = append(heading, "icon");
icon->SetClass("trophy", true);
mDoAud_seStartMenu(kSoundAchievementUnlock);
} else if (toast.type == "controller") {
auto* icon = append(heading, "icon");
icon->SetClass("controller", true);
}
}
{
auto* message = append(elem, "message");
if (toast.content.starts_with("<")) {
message->SetInnerRML(toast.content);
} else {
auto* span = append(message, "span");
span->SetInnerRML(toast.content);
}
}
{
auto* progress = append(elem, "progress");
progress->SetAttribute("value", 1.f);
}
return elem;
}
} // namespace
+8
View File
@@ -117,6 +117,10 @@ const char* connection_state_icon(SDL_JoystickConnectionState state) noexcept {
}
void handle_event(const SDL_Event& event) noexcept {
if (!aurora::rmlui::is_initialized()) {
return;
}
if (event.type == SDL_EVENT_GAMEPAD_ADDED) {
auto* gamepad = SDL_GetGamepadFromID(event.gdevice.which);
if (SDL_GamepadConnected(gamepad)) {
@@ -205,6 +209,10 @@ Document* top_document() noexcept {
}
void update() noexcept {
if (!aurora::rmlui::is_initialized()) {
return;
}
input::update_input();
for (const auto& doc : sDocumentStack) {
doc->update();
+15
View File
@@ -600,6 +600,21 @@ int game_main(int argc, char* argv[]) {
dusk::audio::SetEnableReverb(dusk::getSettings().audio.enableReverb);
dusk::audio::EnableHrtf = dusk::getSettings().audio.enableHrtf;
// Run ImGui UI loop if Aurora couldn't initialize a backend
if (auroraInfo.backend == BACKEND_NULL) {
launchUILoop();
dusk::ShutdownCrashReporting();
dusk::ShutdownFileLogging();
fflush(stdout);
fflush(stderr);
#ifdef DUSK_DISCORD
dusk::discord::shutdown();
#endif
dusk::ui::shutdown();
aurora_shutdown();
return 0;
}
dusk::ui::initialize();
dusk::ui::push_document(std::make_unique<dusk::ui::Overlay>(), true, true);
dusk::ui::push_document(std::make_unique<dusk::ui::MenuBar>(), false);