Merge branch 'main' of https://github.com/TwilitRealm/dusk into randomizer

This commit is contained in:
gymnast86
2026-04-16 23:45:07 -07:00
26 changed files with 834 additions and 182 deletions
+1 -1
View File
@@ -147,7 +147,7 @@ void dBrightCheck_c::modeMove() {
void dBrightCheck_c::brightCheckWide() {
// Main Canvas
mBrightCheck.Scr->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f);
mBrightCheck.Scr->translate(mDoGph_gInf_c::getMinXF(), 0.0f);
mBrightCheck.Scr->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f);
// Right Square
mBrightCheck.Scr->search(MULTI_CHAR('fuchi_1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
+9 -3
View File
@@ -3782,7 +3782,7 @@ bool dFile_select_c::yesnoWakuAlpahAnm(u8 param_1) {
#if TARGET_PC
void dFile_select_c::fileSelectWide() {
mYnSel.ScrYn->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f);
mYnSel.ScrYn->translate(mDoGph_gInf_c::getMinXF(), 0.0f);
mYnSel.ScrYn->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f);
mYnSel.ScrYn->search(MULTI_CHAR('w_no_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
mYnSel.ScrYn->search(MULTI_CHAR('f_no_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
@@ -3790,7 +3790,7 @@ void dFile_select_c::fileSelectWide() {
mYnSel.ScrYn->search(MULTI_CHAR('f_yes_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
m3mSel.Scr3m->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f);
m3mSel.Scr3m->translate(mDoGph_gInf_c::getMinXF(), 0.0f);
m3mSel.Scr3m->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f);
m3mSel.Scr3m->search(MULTI_CHAR('w_sta'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
m3mSel.Scr3m->search(MULTI_CHAR('f_sta'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
@@ -3800,7 +3800,7 @@ void dFile_select_c::fileSelectWide() {
m3mSel.Scr3m->search(MULTI_CHAR('f_cop_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
fileSel.Scr->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f);
fileSel.Scr->translate(mDoGph_gInf_c::getMinXF(), 0.0f);
fileSel.Scr->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f);
fileSel.Scr->search(MULTI_CHAR('t_for'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
fileSel.Scr->search(MULTI_CHAR('t_for1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
@@ -5590,7 +5590,13 @@ void dFile_select3D_c::createMirrorModel() {
void dFile_select3D_c::toItem3Dpos(f32 param_0, f32 param_1, f32 param_2, cXyz* param_3) {
Mtx adStack_98;
Mtx auStack_c8;
#if TARGET_PC
param_0 =
(2.0f * ((param_0 - mDoGph_gInf_c::getSafeMinXF()) / mDoGph_gInf_c::getSafeWidthF()) -
1.0f);
#else
param_0 = (2.0f * ((param_0 - mDoGph_gInf_c::getMinXF()) / mDoGph_gInf_c::getWidthF()) - 1.0f);
#endif
param_1 = (2.0f * ((param_1 - -100.0f) / FB_HEIGHT_BASE) - 1.0f);
calcViewMtx(adStack_98);
cMtx_inverse(adStack_98, auStack_c8);
+8 -2
View File
@@ -99,7 +99,7 @@ dMenu_Collect2D_c::~dMenu_Collect2D_c() {
void dMenu_Collect2D_c::menuCollectWide() {
// Main Canvas
mpScreen->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f);
mpScreen->translate(mDoGph_gInf_c::getMinXF(), 0.0f);
mpScreen->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f);
// Pieces of Heart
mpScreen->search(MULTI_CHAR('heart_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
@@ -182,7 +182,7 @@ void dMenu_Collect2D_c::_create() {
}
#if TARGET_PC
mpScreenIcon->translate(-mDoGph_gInf_c::getMinXF(), 0.0f);
mpScreenIcon->translate(-mDoGph_gInf_c::getSafeMinXF(), 0.0f);
#endif
dPaneClass_showNullPane(mpScreenIcon);
@@ -2706,8 +2706,14 @@ void dMenu_Collect3D_c::setupItem3D(Mtx param_0) {
void dMenu_Collect3D_c::toItem3Dpos(f32 param_0, f32 param_1, f32 param_2, cXyz* param_3) {
Mtx adStack_98;
Mtx auStack_c8;
#if TARGET_PC
param_0 =
(2.0f * ((param_0 - mDoGph_gInf_c::getSafeMinXF()) / mDoGph_gInf_c::getSafeWidthF()) -
1.0f);
#else
param_0 =
(2.0f * ((param_0 - mDoGph_gInf_c::getMinXF()) / mDoGph_gInf_c::getWidthF()) - 1.0f);
#endif
param_1 = (2.0f * ((param_1 - -100.0f) / FB_HEIGHT_BASE) - 1.0f);
calcViewMtx(adStack_98);
MTXInverse(adStack_98, auStack_c8);
+1 -1
View File
@@ -2782,7 +2782,7 @@ void dMenu_save_c::_draw() {
#if TARGET_PC
void dMenu_save_c::menuSaveWide() {
mSaveSel.Scr->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f);
mSaveSel.Scr->translate(mDoGph_gInf_c::getMinXF(), 0.0f);
mSaveSel.Scr->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f);
mSaveSel.Scr->search(MULTI_CHAR('t_for'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
mSaveSel.Scr->search(MULTI_CHAR('t_for1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
+91
View File
@@ -0,0 +1,91 @@
#include "file_select.hpp"
#include <memory>
#include <SDL3/SDL_dialog.h>
#include <SDL3/SDL_error.h>
#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif
#if defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST
#define USE_IOS_DIALOG 1
#include "ios/FileSelectDialog.h"
#else
#define USE_IOS_DIALOG 0
#endif
namespace dusk {
namespace {
#if USE_IOS_DIALOG
struct IOSDialogCallbackState {
FileCallback callback;
void* userdata;
};
void onIOSDialogFinished(void* userdata, const char* path, const char* error) {
std::unique_ptr<IOSDialogCallbackState> state(static_cast<IOSDialogCallbackState*>(userdata));
if (error != nullptr) {
state->callback(state->userdata, nullptr, error);
return;
}
if (path == nullptr) {
state->callback(state->userdata, nullptr, nullptr);
return;
}
state->callback(state->userdata, path, nullptr);
}
#else
struct SDLDialogCallbackState {
FileCallback callback;
void* userdata;
};
void onSDLDialogFinished(void* userdata, const char* const* filelist, [[maybe_unused]] int filter) {
std::unique_ptr<SDLDialogCallbackState> state(static_cast<SDLDialogCallbackState*>(userdata));
if (filelist == nullptr) {
state->callback(state->userdata, nullptr, SDL_GetError());
return;
}
if (filelist[0] == nullptr) {
state->callback(state->userdata, nullptr, nullptr);
return;
}
state->callback(state->userdata, filelist[0], nullptr);
}
#endif
} // namespace
void ShowFileSelect(FileCallback callback, void* userdata, SDL_Window* window,
const SDL_DialogFileFilter* filters, int nfilters, const char* default_location,
bool allow_many) {
if (callback == nullptr) {
return;
}
#if USE_IOS_DIALOG
auto state = std::make_unique<IOSDialogCallbackState>();
state->callback = callback;
state->userdata = userdata;
Dusk_iOS_ShowFileSelect(&onIOSDialogFinished, state.release(), window, filters, nfilters,
default_location, allow_many);
#else
auto state = std::make_unique<SDLDialogCallbackState>();
state->callback = callback;
state->userdata = userdata;
SDL_ShowOpenFileDialog(&onSDLDialogFinished, state.release(), window, filters, nfilters,
default_location, allow_many);
#endif
}
} // namespace dusk
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <SDL3/SDL_dialog.h>
struct SDL_Window;
namespace dusk {
using FileCallback = void (*)(void* userdata, const char* path, const char* error);
void ShowFileSelect(FileCallback callback, void* userdata, SDL_Window* window,
const SDL_DialogFileFilter* filters, int nfilters, const char* default_location,
bool allow_many);
} // namespace dusk
+151 -21
View File
@@ -3,23 +3,24 @@
#include <numeric>
#include <string_view>
#include <chrono>
#include <thread>
#include "fmt/format.h"
#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui.h"
#include <imgui_internal.h>
#include "fmt/format.h"
#include "ImGuiConsole.hpp"
#include "JSystem/JUtility/JUTGamePad.h"
#include "SDL3/SDL_events.h"
#include "SDL3/SDL_mouse.h"
#include "m_Do/m_Do_controller_pad.h"
#include "m_Do/m_Do_main.h"
#include "aurora/lib/window.hpp"
#include "dusk/audio/DuskAudioSystem.h"
#include "dusk/config.hpp"
#include "dusk/dusk.h"
#include "dusk/main.h"
#include "dusk/settings.h"
#include "dusk/audio/DuskAudioSystem.h"
#include "dusk/dusk.h"
#include "m_Do/m_Do_controller_pad.h"
#include "m_Do/m_Do_main.h"
#include "tracy/Tracy.hpp"
#if _WIN32
@@ -30,6 +31,30 @@
using namespace std::string_literals;
using namespace std::string_view_literals;
namespace {
ImVec2 TouchEventToScreenPos(const SDL_TouchFingerEvent& touch) {
const AuroraWindowSize size = aurora::window::get_window_size();
return ImVec2{
touch.x * static_cast<float>(size.width),
touch.y * static_cast<float>(size.height),
};
}
ImGuiWindow* FindDragScrollWindow(ImGuiWindow* window) {
while (window != nullptr) {
const bool canScrollX = window->ScrollMax.x > 0.0f;
const bool canScrollY = window->ScrollMax.y > 0.0f;
const bool canScrollWithMouse = (window->Flags & (ImGuiWindowFlags_NoScrollWithMouse |
ImGuiWindowFlags_NoMouseInputs)) == 0;
if ((canScrollX || canScrollY) && canScrollWithMouse) {
return window;
}
window = window->ParentWindow;
}
return nullptr;
}
} // namespace
namespace dusk {
float ImGuiScale() { return 1.0f; }
@@ -208,6 +233,51 @@ namespace dusk {
ImGuiConsole::ImGuiConsole() {}
void ImGuiConsole::HandleSDLEvent(const SDL_Event& event) {
if (!IsGameLaunched) {
return;
}
switch (event.type) {
case SDL_EVENT_FINGER_DOWN:
if (!m_touchTapActive) {
m_touchTapActive = true;
m_touchTapMoved = false;
m_touchTapFingerId = event.tfinger.fingerID;
m_touchTapStartPos = TouchEventToScreenPos(event.tfinger);
}
break;
case SDL_EVENT_FINGER_MOTION:
if (m_touchTapActive && m_touchTapFingerId == event.tfinger.fingerID) {
const auto currentPos = TouchEventToScreenPos(event.tfinger);
const auto delta = currentPos - m_touchTapStartPos;
if (ImLengthSqr(delta) > 144.0f) {
m_touchTapMoved = true;
}
}
break;
case SDL_EVENT_FINGER_UP:
if (m_touchTapActive && m_touchTapFingerId == event.tfinger.fingerID) {
const bool shouldToggle =
!m_touchTapMoved && (m_isHidden || !ImGui::GetIO().WantCaptureMouse);
m_touchTapActive = false;
m_touchTapMoved = false;
if (shouldToggle) {
m_isHidden = !m_isHidden;
getSettings().backend.duskMenuOpen.setValue(!m_isHidden);
Save();
}
}
break;
case SDL_EVENT_FINGER_CANCELED:
m_touchTapActive = false;
m_touchTapMoved = false;
break;
default:
break;
}
}
void ImGuiConsole::UpdateSettings() {
getTransientSettings().skipFrameRateLimit = getSettings().game.enableTurboKeybind && ImGui::IsKeyDown(ImGuiKey_Tab);
@@ -237,27 +307,32 @@ namespace dusk {
if (!dusk::IsGameLaunched) {
m_preLaunchWindow.draw();
}
m_isHidden = getSettings().backend.duskMenuOpen;
CheckMenuViewToggle(ImGuiKey_F1, m_isHidden);
m_isHidden = !getSettings().backend.duskMenuOpen;
bool showMenu = !dusk::IsGameLaunched || !CheckMenuViewToggle(ImGuiKey_F1, m_isHidden);
if (dusk::IsGameLaunched) {
if (getSettings().backend.duskMenuOpen != m_isHidden) {
m_isHidden = !getSettings().backend.duskMenuOpen;
getSettings().backend.duskMenuOpen.setValue(m_isHidden);
config::Save();
const bool menuOpen = !m_isHidden;
if (getSettings().backend.duskMenuOpen != menuOpen) {
getSettings().backend.duskMenuOpen.setValue(menuOpen);
Save();
}
}
if ((!dusk::IsGameLaunched || getSettings().backend.duskMenuOpen) && ImGui::BeginMainMenuBar()) {
if (showMenu && ImGui::BeginMainMenuBar()) {
m_menuGame.draw();
m_menuEnhancements.draw();
m_menuRandomizer.draw();
m_menuTools.draw();
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 100.0f * ImGuiScale());
ImGuiIO& io = ImGui::GetIO();
ImGuiStringViewText(fmt::format(FMT_STRING("FPS: {:.2f}\n"), io.Framerate));
const auto fpsLabel =
fmt::format(FMT_STRING("FPS: {:.2f}\n"), ImGui::GetIO().Framerate);
const auto fpsSize =
ImGui::CalcTextSize(fpsLabel.data(), fpsLabel.data() + fpsLabel.size());
ImGui::SetCursorPosX(
ImMax(ImGui::GetCursorPosX(), ImGui::GetWindowWidth() -
ImGui::GetStyle().DisplaySafeAreaPadding.x -
fpsSize.x - ImGui::GetStyle().ItemSpacing.x));
ImGuiStringViewText(fpsLabel);
ImGui::EndMainMenuBar();
}
@@ -268,10 +343,15 @@ namespace dusk {
}
if (dusk::IsGameLaunched && !m_isLaunchInitialized) {
m_toasts.emplace_back("Press F1 to toggle menu"s, 2.5f);
m_toasts.emplace_back(ImGui::GetIO().MouseSource == ImGuiMouseSource_TouchScreen ?
"Tap to toggle menu"s :
"Press F1 to toggle menu"s,
2.5f);
m_isLaunchInitialized = true;
}
UpdateDragScroll();
m_menuGame.windowControllerConfig();
m_menuGame.windowInputViewer();
if (dusk::IsGameLaunched) {
@@ -292,8 +372,7 @@ namespace dusk {
DuskDebugPad(); // temporary, remove later
// Only show cursor when menu or any windows are open
if ((!dusk::IsGameLaunched || getSettings().backend.duskMenuOpen) || ImGui::GetIO().MetricsRenderWindows > 0)
{
if (showMenu || ImGui::GetIO().MetricsRenderWindows > 0) {
ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange;
// Imgui will re-show cursor.
} else {
@@ -309,6 +388,57 @@ namespace dusk {
ShowPipelineProgress();
}
void ImGuiConsole::UpdateDragScroll() {
ImGuiContext& g = *ImGui::GetCurrentContext();
ImGuiIO& io = ImGui::GetIO();
if (io.MouseSource != ImGuiMouseSource_TouchScreen) {
m_dragScrollWindow = nullptr;
return;
}
if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
m_dragScrollWindow = nullptr;
return;
}
if (io.WantTextInput || (g.ActiveId != 0 && g.InputTextState.ID == g.ActiveId)) {
m_dragScrollWindow = nullptr;
return;
}
if (!ImGui::IsMouseDragging(ImGuiMouseButton_Left, io.MouseDragThreshold)) {
return;
}
if (m_dragScrollWindow == nullptr) {
ImGuiWindow* hoveredWindow = nullptr;
ImGuiWindow* hoveredWindowUnderMovingWindow = nullptr;
ImGui::FindHoveredWindowEx(io.MousePos, false, &hoveredWindow,
&hoveredWindowUnderMovingWindow);
m_dragScrollWindow = FindDragScrollWindow(hoveredWindow);
m_dragScrollLastMousePos = io.MousePos;
}
if (m_dragScrollWindow == nullptr) {
return;
}
const auto mouseDelta = io.MousePos - m_dragScrollLastMousePos;
m_dragScrollLastMousePos = io.MousePos;
if (mouseDelta.x != 0.0f && m_dragScrollWindow->ScrollMax.x > 0.0f) {
ImGui::SetScrollX(m_dragScrollWindow,
ImClamp(m_dragScrollWindow->Scroll.x - mouseDelta.x, 0.0f,
m_dragScrollWindow->ScrollMax.x));
}
if (mouseDelta.y != 0.0f && m_dragScrollWindow->ScrollMax.y > 0.0f) {
ImGui::SetScrollY(m_dragScrollWindow,
ImClamp(m_dragScrollWindow->Scroll.y - mouseDelta.y, 0.0f,
m_dragScrollWindow->ScrollMax.y));
}
}
bool ImGuiConsole::CheckMenuViewToggle(ImGuiKey key, bool& active) {
if (ImGui::IsKeyPressed(key)) {
active = !active;
+14 -1
View File
@@ -1,11 +1,13 @@
#ifndef DUSK_IMGUI_HPP
#define DUSK_IMGUI_HPP
#include <aurora/aurora.h>
#include <deque>
#include <string>
#include <string_view>
#include <aurora/aurora.h>
#include <SDL3/SDL_touch.h>
#include "ImGuiFirstRunPreset.hpp"
#include "ImGuiMenuEnhancements.hpp"
#include "ImGuiMenuGame.hpp"
@@ -14,10 +16,14 @@
#include "ImGuiPreLaunchWindow.hpp"
#include "imgui.h"
union SDL_Event;
struct ImGuiWindow;
namespace dusk {
class ImGuiConsole {
public:
ImGuiConsole();
void HandleSDLEvent(const SDL_Event& event);
void UpdateSettings();
void PreDraw();
void PostDraw();
@@ -35,6 +41,12 @@ private:
bool m_isHidden = true;
bool m_isLaunchInitialized = false;
bool m_touchTapActive = false;
bool m_touchTapMoved = false;
SDL_FingerID m_touchTapFingerId = 0;
ImVec2 m_touchTapStartPos = {};
ImGuiWindow* m_dragScrollWindow = nullptr;
ImVec2 m_dragScrollLastMousePos = {};
std::deque<Toast> m_toasts;
ImGuiFirstRunPreset m_firstRunPreset;
@@ -48,6 +60,7 @@ private:
void ShowToasts();
void ShowPipelineProgress();
void UpdateDragScroll();
};
extern ImGuiConsole g_imguiConsole;
+18
View File
@@ -3,12 +3,14 @@
#include <SDL3/SDL_filesystem.h>
#include <SDL3/SDL_pixels.h>
#include <SDL3/SDL_surface.h>
#include <SDL3/SDL_video.h>
#include <aurora/imgui.h>
#include <cmath>
#include <cstring>
#include <fmt/format.h>
#include <string>
#include "aurora/lib/window.hpp"
#include "dusk/logging.h"
#ifdef IMGUI_ENABLE_FREETYPE
@@ -37,6 +39,20 @@ ImTextureID AddTexture(const char* assetName) {
}
return aurora_imgui_add_texture(image.width, image.height, image.data.get());
}
ImVec2 GetDisplaySafeAreaPadding() {
SDL_Window* window = aurora::window::get_sdl_window();
if (window == nullptr) {
return ImVec2(0.0f, 0.0f);
}
SDL_Rect safeRect{};
if (!SDL_GetWindowSafeArea(window, &safeRect)) {
return ImVec2(0.0f, 0.0f);
}
return ImVec2(static_cast<float>(safeRect.x), static_cast<float>(safeRect.y));
}
} // namespace
ImFont* ImGuiEngine::fontNormal;
@@ -75,6 +91,7 @@ void ImGuiEngine_Initialize(float scale) {
ImGuiIO& io = ImGui::GetIO();
io.Fonts->Clear();
io.FontGlobalScale = scale > 0.0f ? 1.0f / scale : 1.0f;
io.ConfigWindowsMoveFromTitleBarOnly = IsMobile;
ImGuiEngine::fontNormal =
CreateFont(std::floor(18.f * scale), GetAssetPath("Inter-Regular.ttf"), "Inter Regular");
@@ -104,6 +121,7 @@ void ImGuiEngine_Initialize(float scale) {
style.PopupRounding = 7.0;
style.TabBorderSize = 1.f;
style.TabRounding = 3.f;
style.DisplaySafeAreaPadding = GetDisplaySafeAreaPadding();
auto* colors = style.Colors;
colors[ImGuiCol_Text] = ImVec4(0.95f, 0.96f, 0.98f, 1.00f);
+6
View File
@@ -25,4 +25,10 @@ struct Image {
uint32_t height;
};
Image GetImage(const std::string& path);
#if (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST) || defined(__ANDROID__)
inline constexpr bool IsMobile = true;
#else
inline constexpr bool IsMobile = false;
#endif
} // namespace dusk
+12 -9
View File
@@ -1,6 +1,7 @@
#include "fmt/format.h"
#include "imgui.h"
#include "ImGuiEngine.hpp"
#include "ImGuiConsole.hpp"
#include "ImGuiMenuGame.hpp"
#include "ImGuiConfig.hpp"
@@ -32,15 +33,17 @@ namespace dusk {
void ImGuiMenuGame::draw() {
if (ImGui::BeginMenu("Game")) {
if (ImGui::BeginMenu("Graphics")) {
if (ImGui::MenuItem("Toggle Fullscreen", hotkeys::TOGGLE_FULLSCREEN)) {
ToggleFullscreen();
}
if (!IsMobile) {
if (ImGui::MenuItem("Toggle Fullscreen", hotkeys::TOGGLE_FULLSCREEN)) {
ToggleFullscreen();
}
if (ImGui::MenuItem("Default Window Size")) {
getSettings().video.enableFullscreen.setValue(false);
VISetWindowFullscreen(false);
VISetWindowSize(FB_WIDTH * 2, FB_HEIGHT * 2);
VICenterWindow();
if (ImGui::MenuItem("Default Window Size")) {
getSettings().video.enableFullscreen.setValue(false);
VISetWindowFullscreen(false);
VISetWindowSize(FB_WIDTH * 2, FB_HEIGHT * 2);
VICenterWindow();
}
}
bool vsync = getSettings().video.enableVsync;
@@ -147,7 +150,7 @@ namespace dusk {
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
}
if (ImGui::MenuItem("Exit")) {
if (!IsMobile && ImGui::MenuItem("Exit")) {
dusk::IsRunning = false;
}
+21 -29
View File
@@ -4,13 +4,13 @@
#include "ImGuiEngine.hpp"
#include "ImGuiPreLaunchWindow.hpp"
#include "../file_select.hpp"
#include "../iso_validate.hpp"
#include "ImGuiConsole.hpp"
#include "dusk/main.h"
#include "dusk/settings.h"
#include "../iso_validate.hpp"
#include <SDL3/SDL_dialog.h>
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_filesystem.h>
#include "aurora/lib/internal.hpp"
@@ -46,30 +46,22 @@ static std::string ShowIsoInvalidError(const iso::ValidationError code) {
}
}
void fileDialogCallback(void* userdata, const char* const* filelist, [[maybe_unused]] int filter) {
void fileDialogCallback(void* userdata, const char* path, const char* error) {
auto* self = static_cast<ImGuiPreLaunchWindow*>(userdata);
self->m_errorString.clear();
if (filelist != nullptr) {
if (filelist[0] == nullptr) {
// Cancelled
self->m_selectedIsoPath.clear();
} else {
const auto path = filelist[0];
const auto ret = iso::validate(path);
if (ret != iso::ValidationError::Success) {
self->m_selectedIsoPath.clear();
self->m_errorString = std::move(ShowIsoInvalidError(ret));
return;
}
self->m_selectedIsoPath = path;
getSettings().backend.isoPath.setValue(path);
config::Save();
}
} else {
// Error occurred
if (error != nullptr) {
self->m_selectedIsoPath.clear();
self->m_errorString = fmt::format("File dialog error: {}", SDL_GetError());
self->m_errorString = fmt::format("File dialog error: {}", error);
return;
}
if (path == nullptr) {
self->m_selectedIsoPath.clear();
return;
}
self->m_selectedIsoPath = path;
getSettings().backend.isoPath.setValue(self->m_selectedIsoPath);
config::Save();
}
ImGuiPreLaunchWindow::ImGuiPreLaunchWindow() = default;
@@ -144,9 +136,9 @@ void ImGuiPreLaunchWindow::drawMainMenu() {
}
if (ImGuiButtonCenter("Select disc image...")) {
SDL_ShowOpenFileDialog(&fileDialogCallback, this, aurora::window::get_sdl_window(),
skGameDiscFileFilters.data(), int(skGameDiscFileFilters.size()),
nullptr, false);
ShowFileSelect(&fileDialogCallback, this, aurora::window::get_sdl_window(),
skGameDiscFileFilters.data(), int(skGameDiscFileFilters.size()), nullptr,
false);
}
} else {
if (ImGuiButtonCenter("Start game")) {
@@ -187,9 +179,9 @@ void ImGuiPreLaunchWindow::drawOptions() {
ImGui::InputText("Game ISO Path", &m_selectedIsoPath, ImGuiInputTextFlags_ReadOnly);
ImGui::SameLine();
if (ImGui::Button("Set")) {
SDL_ShowOpenFileDialog(&fileDialogCallback, this, aurora::window::get_sdl_window(),
skGameDiscFileFilters.data(), int(skGameDiscFileFilters.size()),
nullptr, false);
ShowFileSelect(&fileDialogCallback, this, aurora::window::get_sdl_window(),
skGameDiscFileFilters.data(), int(skGameDiscFileFilters.size()), nullptr,
false);
}
AuroraBackend configuredBackend = BACKEND_AUTO;
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include <stdbool.h>
#include <SDL3/SDL_dialog.h>
struct SDL_Window;
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*IOSFileCallback)(void* userdata, const char* path, const char* error);
void Dusk_iOS_ShowFileSelect(IOSFileCallback callback, void* userdata, SDL_Window* window,
const SDL_DialogFileFilter* filters, int nfilters,
const char* default_location, bool allow_many);
#ifdef __cplusplus
}
#endif
+151
View File
@@ -0,0 +1,151 @@
#include "FileSelectDialog.h"
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
#import <objc/runtime.h>
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_properties.h>
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_video.h>
static void *g_picker_delegate_key = &g_picker_delegate_key;
static void RunOnMainThread(void (^block)(void))
{
if ([NSThread isMainThread]) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
}
static NSError *MakeError(NSString *message)
{
return [NSError errorWithDomain:@"org.twilitrealm.dusk.file-select"
code:1
userInfo:@{NSLocalizedDescriptionKey: message}];
}
static UIViewController *FindTopViewController(UIViewController *controller)
{
UIViewController *current = controller;
while (current.presentedViewController != nil) {
current = current.presentedViewController;
}
return current;
}
static UIViewController *PresenterFromWindow(SDL_Window *window)
{
if (window == nil) {
return nil;
}
const SDL_PropertiesID props = SDL_GetWindowProperties(window);
if (props == 0) {
return nil;
}
UIWindow *uiwindow = (__bridge UIWindow *)SDL_GetPointerProperty(
props, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, NULL);
if (uiwindow == nil || uiwindow.rootViewController == nil) {
return nil;
}
return FindTopViewController(uiwindow.rootViewController);
}
static NSURL *InitialDirectoryURL(const char *default_location)
{
if (default_location == NULL || *default_location == '\0') {
return nil;
}
NSString *path = [NSString stringWithUTF8String:default_location];
NSURL *url = [NSURL fileURLWithPath:path];
if ([path hasSuffix:@"/"]) {
return url;
}
return [url URLByDeletingLastPathComponent];
}
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
@property(nonatomic, assign) IOSFileCallback callback;
@property(nonatomic, assign) void *userdata;
@end
@implementation DocumentPickerDelegate
- (void)finishWithPath:(const char *)path error:(const char *)error {
if (self.callback != NULL) {
self.callback(self.userdata, path, error);
}
}
- (void)documentPicker:(UIDocumentPickerViewController *)controller
didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls
{
NSURL *url = urls.firstObject;
if (url == nil) {
[self finishWithPath:NULL error:NULL];
return;
}
[self finishWithPath:url.path.UTF8String error:NULL];
(void)controller;
}
- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller
{
[self finishWithPath:NULL error:NULL];
(void)controller;
}
@end
void Dusk_iOS_ShowFileSelect(IOSFileCallback callback, void *userdata,
SDL_Window *window,
const SDL_DialogFileFilter *filters, int nfilters,
const char *default_location,
bool allow_many)
{
RunOnMainThread(^{
@autoreleasepool {
UIViewController *presenter = PresenterFromWindow(window);
if (presenter == nil) {
callback(userdata, NULL, "Failed to find an iOS view controller for the file picker.");
return;
}
NSLog(@"[ShowFileSelect] presenting picker from %@", NSStringFromClass([presenter class]));
UIDocumentPickerViewController *picker =
[[UIDocumentPickerViewController alloc]
initForOpeningContentTypes:@[ UTTypeItem ]
asCopy:YES];
picker.allowsMultipleSelection = allow_many ? YES : NO;
picker.shouldShowFileExtensions = YES;
NSURL *directory_url = InitialDirectoryURL(default_location);
if (directory_url != nil) {
picker.directoryURL = directory_url;
}
DocumentPickerDelegate *delegate = [DocumentPickerDelegate new];
delegate.callback = callback;
delegate.userdata = userdata;
picker.delegate = delegate;
objc_setAssociatedObject(picker, g_picker_delegate_key, delegate,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[presenter presentViewController:picker animated:YES completion:nil];
(void)filters;
(void)nfilters;
}
});
}
+87 -2
View File
@@ -47,6 +47,8 @@
#endif
#if TARGET_PC
#include <SDL3/SDL_video.h>
#include "aurora/lib/window.hpp"
#include "d/actor/d_a_horse.h"
#include "dusk/dusk.h"
#include "dusk/endian.h"
@@ -636,8 +638,9 @@ void mDoGph_gInf_c::setTvSize() {
m_invScale = 1.0f / m_scale;
#if TARGET_PC
hudAspectScaleDown = 1.3571428f / mDoGph_gInf_c::getAspect();
hudAspectScaleUp = 1.0f / hudAspectScaleDown;
updateSafeAreaBounds();
hudAspectScaleUp = getSafeWidthF() / FB_WIDTH_BASE;
hudAspectScaleDown = FB_WIDTH_BASE / getSafeWidthF();
#endif
}
@@ -765,6 +768,88 @@ void mDoGph_gInf_c::setWideZoomLightProjection(Mtx& m) {
#if TARGET_PC
f32 mDoGph_gInf_c::hudAspectScaleDown = 1.0f;
f32 mDoGph_gInf_c::hudAspectScaleUp = 1.0f;
f32 mDoGph_gInf_c::m_safeMinXF = 0.0f;
f32 mDoGph_gInf_c::m_safeMinYF = 0.0f;
f32 mDoGph_gInf_c::m_safeMaxXF = FB_WIDTH_BASE;
f32 mDoGph_gInf_c::m_safeMaxYF = FB_HEIGHT_BASE;
f32 mDoGph_gInf_c::m_safeWidthF = FB_WIDTH_BASE;
f32 mDoGph_gInf_c::m_safeHeightF = FB_HEIGHT_BASE;
void mDoGph_gInf_c::updateSafeAreaBounds() {
m_safeMinXF = m_minXF;
m_safeMinYF = m_minYF;
m_safeMaxXF = m_maxXF;
m_safeMaxYF = m_maxYF;
m_safeWidthF = m_widthF;
m_safeHeightF = m_heightF;
SDL_Window* window = aurora::window::get_sdl_window();
if (window == NULL) {
return;
}
const AuroraWindowSize windowSize = aurora::window::get_window_size();
const f32 windowWidth = static_cast<f32>(windowSize.width);
const f32 windowHeight = static_cast<f32>(windowSize.height);
if (windowWidth <= 0.0f || windowHeight <= 0.0f) {
return;
}
SDL_Rect safeRect{};
if (!SDL_GetWindowSafeArea(window, &safeRect)) {
return;
}
if (windowSize.native_fb_width == 0 || windowSize.native_fb_height == 0 ||
windowSize.fb_width == 0 || windowSize.fb_height == 0)
{
return;
}
const f32 nativeScaleX = static_cast<f32>(windowSize.native_fb_width) / windowWidth;
const f32 nativeScaleY = static_cast<f32>(windowSize.native_fb_height) / windowHeight;
const f32 safeLeft = static_cast<f32>(safeRect.x) * nativeScaleX;
const f32 safeTop = static_cast<f32>(safeRect.y) * nativeScaleY;
const f32 safeRight = static_cast<f32>(safeRect.x + safeRect.w) * nativeScaleX;
const f32 safeBottom = static_cast<f32>(safeRect.y + safeRect.h) * nativeScaleY;
const f32 viewportLeft =
(static_cast<f32>(windowSize.native_fb_width) - static_cast<f32>(windowSize.fb_width)) *
0.5f;
const f32 viewportTop =
(static_cast<f32>(windowSize.native_fb_height) - static_cast<f32>(windowSize.fb_height)) *
0.5f;
const f32 viewportRight = viewportLeft + static_cast<f32>(windowSize.fb_width);
const f32 viewportBottom = viewportTop + static_cast<f32>(windowSize.fb_height);
const f32 leftInset = std::max(0.0f, safeLeft - viewportLeft) *
(m_widthF / static_cast<f32>(windowSize.fb_width));
const f32 topInset = std::max(0.0f, safeTop - viewportTop) *
(m_heightF / static_cast<f32>(windowSize.fb_height));
const f32 rightInset = std::max(0.0f, viewportRight - safeRight) *
(m_widthF / static_cast<f32>(windowSize.fb_width));
const f32 bottomInset = std::max(0.0f, viewportBottom - safeBottom) *
(m_heightF / static_cast<f32>(windowSize.fb_height));
const f32 safeMinXF = m_minXF + leftInset;
const f32 safeMinYF = m_minYF + topInset;
const f32 safeMaxXF = m_maxXF - rightInset;
const f32 safeMaxYF = m_maxYF - bottomInset;
const f32 safeWidthF = safeMaxXF - safeMinXF;
const f32 safeHeightF = safeMaxYF - safeMinYF;
if (safeWidthF <= 0.0f || safeHeightF <= 0.0f) {
return;
}
m_safeMinXF = safeMinXF;
m_safeMinYF = safeMinYF;
m_safeMaxXF = safeMaxXF;
m_safeMaxYF = safeMaxYF;
m_safeWidthF = safeWidthF;
m_safeHeightF = safeHeightF;
}
void mDoGph_gInf_c::setWindowSize(AuroraWindowSize const& size) {
JUTVideo::getManager()->setWindowSize(size);
+1 -1
View File
@@ -154,7 +154,7 @@ void mDoLib_project(Vec* src, Vec* dst, JGeometry::TBox2<f32> viewport) {
xSize = FB_WIDTH;
} else {
#if TARGET_PC
xOffset = mDoGph_gInf_c::getMinXF();
xOffset = mDoGph_gInf_c::getSafeMinXF();
xSize = viewport.f.x * mDoGph_gInf_c::hudAspectScaleUp;
#else
xOffset = viewport.i.x;
+12 -6
View File
@@ -138,6 +138,9 @@ bool launchUILoop() {
const AuroraEvent* event = aurora_update();
while (event != nullptr && event->type != AURORA_NONE) {
switch (event->type) {
case AURORA_SDL_EVENT:
dusk::g_imguiConsole.HandleSDLEvent(event->sdl);
break;
case AURORA_WINDOW_RESIZED:
preLaunchUIWindowSize = event->windowSize;
break;
@@ -207,9 +210,9 @@ void main01(void) {
if (preLaunchUIWindowSize.width != 0)
mDoGph_gInf_c::setWindowSize(preLaunchUIWindowSize);
constexpr double kSimStepSeconds = 1.0 / 30.0;
constexpr float kSimStepSeconds = 1.0 / 30.0;
auto previous_time = std::chrono::steady_clock::now();
double accumulator = kSimStepSeconds;
float accumulator = kSimStepSeconds;
do {
// 1. Update Window Events
@@ -218,6 +221,9 @@ void main01(void) {
switch (event->type) {
case AURORA_NONE:
goto eventsDone;
case AURORA_SDL_EVENT:
dusk::g_imguiConsole.HandleSDLEvent(event->sdl);
break;
case AURORA_WINDOW_RESIZED:
mDoGph_gInf_c::setWindowSize(event->windowSize);
break;
@@ -234,7 +240,7 @@ void main01(void) {
eventsDone:;
auto current_time = std::chrono::steady_clock::now();
double frame_seconds = std::chrono::duration<double>(current_time - previous_time).count();
float frame_seconds = std::chrono::duration<float>(current_time - previous_time).count();
previous_time = current_time;
accumulator += frame_seconds;
@@ -248,12 +254,12 @@ void main01(void) {
if (dusk::getSettings().game.enableFrameInterpolation && !dusk::getTransientSettings().skipFrameRateLimit) {
dusk::frame_interp::notify_presentation_frame();
while (accumulator >= kSimStepSeconds) {
if (accumulator >= kSimStepSeconds) {
mDoCPd_c::read();
dusk::gyro::read(kSimStepSeconds);
fapGm_Execute();
mDoAud_Execute();
accumulator -= kSimStepSeconds;
accumulator = 0.0f;
}
dusk::frame_interp::interpolate(static_cast<float>(accumulator / kSimStepSeconds));
{
@@ -261,7 +267,7 @@ void main01(void) {
cAPIGph_Painter();
}
} else {
accumulator = 0.0;
accumulator = 0.0f;
// Game Inputs
mDoCPd_c::read();