From e10e1f74d566688645c60c6941085f99fcb0825a Mon Sep 17 00:00:00 2001 From: David Kanevskyy <156260369+korrectional@users.noreply.github.com> Date: Sun, 17 May 2026 09:25:12 -0400 Subject: [PATCH 01/10] Feature - remember window position (#1238) * added .zed/ to gitignore (editor configs) * remember window position upon closing * Save window location on SDL_EVENT_WINDOW_MOVED or SDL_EVENT_WINDOW_RESIZED * Fix code format mistakes * Also persist window width/height * Undo change to input::handle_event * Undo aurora submodule change --------- Co-authored-by: Luke Street --- .gitignore | 1 + include/dusk/settings.h | 4 ++++ src/dusk/settings.cpp | 11 +++++++++++ src/dusk/ui/ui.cpp | 14 ++++++++++++++ src/m_Do/m_Do_main.cpp | 8 ++++---- 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 43b2e4a1c3..c43499954e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # IDE folders .idea/ .vs/ +.zed/ # Caches __pycache__ diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 43e565ed5e..c652d71789 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -72,6 +72,10 @@ struct UserSettings { ConfigVar lockAspectRatio; ConfigVar enableFpsOverlay; ConfigVar fpsOverlayCorner; + ConfigVar windowPositionX; + ConfigVar windowPositionY; + ConfigVar windowWidth; + ConfigVar windowHeight; } video; struct { diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index dbe27db8aa..f8ca1b418a 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -1,5 +1,8 @@ #include "dusk/settings.h" #include "dusk/config.hpp" +#include "dusk/dusk.h" + +#include namespace dusk { @@ -10,6 +13,10 @@ UserSettings g_userSettings = { .lockAspectRatio {"video.lockAspectRatio", false}, .enableFpsOverlay {"game.enableFpsOverlay", false}, .fpsOverlayCorner {"game.fpsOverlayCorner", 0}, + .windowPositionX {"video.windowPositionX", SDL_WINDOWPOS_UNDEFINED}, + .windowPositionY {"video.windowPositionY", SDL_WINDOWPOS_UNDEFINED}, + .windowWidth {"video.windowWidth", defaultWindowWidth * 2}, + .windowHeight {"video.windowHeight", defaultWindowHeight * 2}, }, .audio = { @@ -177,6 +184,10 @@ void registerSettings() { Register(g_userSettings.video.lockAspectRatio); Register(g_userSettings.video.enableFpsOverlay); Register(g_userSettings.video.fpsOverlayCorner); + Register(g_userSettings.video.windowPositionX); + Register(g_userSettings.video.windowPositionY); + Register(g_userSettings.video.windowWidth); + Register(g_userSettings.video.windowHeight); // Audio Register(g_userSettings.audio.masterVolume); diff --git a/src/dusk/ui/ui.cpp b/src/dusk/ui/ui.cpp index fc2d5d6691..89a174b000 100644 --- a/src/dusk/ui/ui.cpp +++ b/src/dusk/ui/ui.cpp @@ -11,7 +11,9 @@ #include #include "aurora/lib/window.hpp" +#include "dusk/dusk.h" #include "dusk/io.hpp" +#include "dusk/config.hpp" #include "input.hpp" #include "prelaunch.hpp" #include "window.hpp" @@ -169,6 +171,18 @@ void handle_event(const SDL_Event& event) noexcept { }); } sConnectedGamepads.erase(event.gdevice.which); + } else if (event.type == SDL_EVENT_WINDOW_MOVED || event.type == SDL_EVENT_WINDOW_RESIZED) { + int x, y; + if (SDL_GetWindowPosition(aurora::window::get_sdl_window(), &x, &y)) { + getSettings().video.windowPositionX.setValue(x); + getSettings().video.windowPositionY.setValue(y); + } + int width, height; + if (SDL_GetWindowSize(aurora::window::get_sdl_window(), &width, &height)) { + getSettings().video.windowWidth.setValue(width); + getSettings().video.windowHeight.setValue(height); + } + config::Save(); } input::handle_event(event); } diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index b892a1f706..56ff865bee 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -551,10 +551,10 @@ int game_main(int argc, char* argv[]) { config.cachePath = reinterpret_cast(cachePathString.c_str()); config.vsync = dusk::getSettings().video.enableVsync; config.startFullscreen = dusk::getSettings().video.enableFullscreen; - config.windowPosX = -1; - config.windowPosY = -1; - config.windowWidth = defaultWindowWidth * 2; - config.windowHeight = defaultWindowHeight * 2; + config.windowPosX = dusk::getSettings().video.windowPositionX; + config.windowPosY = dusk::getSettings().video.windowPositionY; + config.windowWidth = dusk::getSettings().video.windowWidth; + config.windowHeight = dusk::getSettings().video.windowHeight; config.desiredBackend = ResolveDesiredBackend(parsed_arg_options); config.logCallback = &aurora_log_callback; config.logLevel = startupLogLevel; From db6a1835d2d24cbfeb5026920c763ca7cf8dc3e2 Mon Sep 17 00:00:00 2001 From: Krutonium <3945538+Krutonium@users.noreply.github.com> Date: Sun, 17 May 2026 09:26:50 -0400 Subject: [PATCH 02/10] Flake: Update GitHub revision and hash for aurora-src (#1237) * Flake: Update GitHub revision and hash for aurora-src Fixes Build Error due to Aurora being out of date in the flake. * No Required Aurora Hash --- flake.nix | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/flake.nix b/flake.nix index edba703a3d..29c99b10a5 100644 --- a/flake.nix +++ b/flake.nix @@ -15,12 +15,6 @@ # Dependencies that are not packaged in nixpkgs (used by the Linux package build): buildSources = pkgs: { - aurora-src = pkgs.fetchFromGitHub { - owner = "encounter"; - repo = "aurora"; - rev = "63606a43265a3bc18dafd500ab4d7a2108f109e6"; - hash = "sha256-xBvnAwGwNzav67Ac6oUz7RqDUwqgL2bsME3OOMn8Tqw="; - }; dawn-src = pkgs.fetchzip { url = "https://github.com/encounter/dawn-build/releases/download/v20260423.175430/dawn-linux-x86_64.tar.gz"; hash = "sha256-HXfKTLHtMPwupnFnaflCARtXVPuS/0PoCePXidjE5xs="; @@ -59,9 +53,6 @@ name = "dusklight"; src = ./.; postUnpack = '' - mkdir -p $sourceRoot/extern/aurora - cp -r ${srcs.aurora-src}/. $sourceRoot/extern/aurora/ - chmod -R u+w $sourceRoot/extern/aurora sed -i '/add_subdirectory(tests)/d' $sourceRoot/extern/aurora/CMakeLists.txt ''; # Remove last line to re-enable tests @@ -225,4 +216,4 @@ default = mkDevShell (pkgsFor system); }); }; -} \ No newline at end of file +} From a773e5489efbbfc5c882862ebe48a1bf48f74a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFs?= <49660929+SailorSnoW@users.noreply.github.com> Date: Sun, 17 May 2026 15:27:27 +0200 Subject: [PATCH 03/10] Fix unresponsive R trigger in dusklight menu (#1424) --- src/dusk/ui/input.cpp | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/dusk/ui/input.cpp b/src/dusk/ui/input.cpp index 02a15e9a0f..2628aeb682 100644 --- a/src/dusk/ui/input.cpp +++ b/src/dusk/ui/input.cpp @@ -454,10 +454,18 @@ bool touch_moved_too_far( return delta.SquaredMagnitude() > threshold * threshold; } -void dispatch_menu_key(Rml::Context& context) noexcept { +void emit_key_press(Rml::Context& context, Rml::Input::KeyIdentifier key) noexcept { context.ProcessMouseLeave(); - context.ProcessKeyDown(Rml::Input::KI_F1, 0); - context.ProcessKeyUp(Rml::Input::KI_F1, 0); + context.ProcessKeyDown(key, 0); +} + +void emit_key_tap(Rml::Context& context, Rml::Input::KeyIdentifier key) noexcept { + emit_key_press(context, key); + context.ProcessKeyUp(key, 0); +} + +void dispatch_menu_key(Rml::Context& context) noexcept { + emit_key_tap(context, Rml::Input::KI_F1); } bool handle_touch_menu_tap(Rml::Context& context, const SDL_Event& event) noexcept { @@ -627,7 +635,9 @@ void process_axis_direction( if (repeat->held) { if (released) { - if (!repeat->pending) { + if (repeat->pending) { + emit_key_tap(context, repeat->key); + } else { context.ProcessKeyUp(repeat->key, 0); } set_pad_button_held(port, heldPadButton, false); @@ -658,8 +668,7 @@ void process_axis_direction( } begin_gamepad_key(*repeat, key); - context.ProcessMouseLeave(); - context.ProcessKeyDown(key, 0); + emit_key_press(context, key); } } // namespace @@ -747,8 +756,7 @@ void handle_event(const SDL_Event& event) noexcept { } } if (!deferred) { - context->ProcessMouseLeave(); - context->ProcessKeyDown(key, 0); + emit_key_press(*context, key); } } } else { @@ -760,7 +768,9 @@ void handle_event(const SDL_Event& event) noexcept { if (repeat != nullptr) { *repeat = {}; } - if (!wasPending) { + if (wasPending) { + emit_key_tap(*context, key); + } else { context->ProcessKeyUp(key, 0); } } @@ -787,8 +797,7 @@ void update_input() noexcept { repeat.pressedAt = now; repeat.nextRepeatAt = repeat.repeatable ? now + kGamepadRepeatInitialDelay : 0.0; - context->ProcessMouseLeave(); - context->ProcessKeyDown(repeat.key, 0); + emit_key_press(*context, repeat.key); continue; } From eed81e9ecc22037a4f1aacce0edbf7d38ecb573a Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 17 May 2026 09:33:26 -0400 Subject: [PATCH 04/10] Update aurora --- extern/aurora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/aurora b/extern/aurora index 40913d532e..f93b9e5bc2 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 40913d532e5859a68e56b88d8aaec6bff1b88a2e +Subproject commit f93b9e5bc20850198538ccd3abc69ab2b128ecf7 From 6faa4a3330864e644c4ee10617f6b9462c0becf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFs?= <49660929+SailorSnoW@users.noreply.github.com> Date: Sun, 17 May 2026 15:34:18 +0200 Subject: [PATCH 05/10] Fix mButtonText buffer overflow on long localized action labels (#1491) --- src/d/d_meter_button.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/d/d_meter_button.cpp b/src/d/d_meter_button.cpp index e55b42bc1a..b39a234056 100644 --- a/src/d/d_meter_button.cpp +++ b/src/d/d_meter_button.cpp @@ -18,6 +18,9 @@ #include "d/d_pane_class.h" #include "dusk/frame_interpolation.h" #include +#if TARGET_PC +#include "dusk/string.hpp" +#endif #if VERSION == VERSION_GCN_JPN #define STR_BUF_LEN 528 @@ -2927,6 +2930,12 @@ bool dMeterButton_c::isClose() { } void dMeterButton_c::setString(char* i_string, u8 i_button, u8 param_2, u8 param_3) { +#if TARGET_PC + char* i_string_full = i_string; + char i_string_buf[sizeof(mButtonText[0])]; + dusk::SafeStringCopyTruncate(i_string_buf, i_string); + i_string = i_string_buf; +#endif if (strcmp(mButtonText[param_2], i_string) != 0 || field_0x4be[param_2] != i_button) { if (param_2 == 0 && strcmp(mButtonText[1], i_string) == 0 && ((i_button == BUTTON_A_e && field_0x4be[1] == BUTTON_A_e) || @@ -3022,6 +3031,10 @@ void dMeterButton_c::setString(char* i_string, u8 i_button, u8 param_2, u8 param strcpy(mButtonText[param_2], i_string); +#if TARGET_PC + i_string = i_string_full; +#endif + if (param_2 == 0) { if (param_3 != 0) { field_0x4d9 = param_2; From cd4b29f8a12eb01d94020e15163bfa9a9b461bf3 Mon Sep 17 00:00:00 2001 From: Olivia!! Date: Sun, 17 May 2026 15:38:24 +0200 Subject: [PATCH 06/10] Update controller bindings menu. (#1461) * Add a button to reset to default controls, and hides the digital L and R binds except on advanced menu. * Rename Controller mentions to Device + Extra menu sounds * Shouldn't add a column here * Changes mentions of controller to device in accordance with #1479 --------- Co-authored-by: MelonSpeedruns --- src/dusk/ui/controller_config.cpp | 117 ++++++++++++++++++------------ src/dusk/ui/overlay.cpp | 4 +- src/dusk/ui/settings.cpp | 12 +-- src/dusk/ui/ui.cpp | 4 +- src/dusk/ui/ui.hpp | 4 + 5 files changed, 83 insertions(+), 58 deletions(-) diff --git a/src/dusk/ui/controller_config.cpp b/src/dusk/ui/controller_config.cpp index aef911f996..43b913b6a7 100644 --- a/src/dusk/ui/controller_config.cpp +++ b/src/dusk/ui/controller_config.cpp @@ -37,7 +37,7 @@ Rml::String current_controller_name(int port) { Rml::String controller_index_name(u32 index) { const char* name = PADGetNameForControllerIndex(index); if (name == nullptr) { - return fmt::format("Controller {}", index + 1); + return fmt::format("Device {}", index + 1); } return name; } @@ -124,7 +124,7 @@ Rml::String native_axis_name(const PADAxisMapping& mapping, SDL_Gamepad* gamepad return native_button_name(gamepad, static_cast(mapping.nativeButton)); } - return "Not bound"; + return "Not Bound"; } bool is_dpad_button(PADButton button) { @@ -162,7 +162,7 @@ bool keyboard_escape_pressed() { Rml::String keyboard_key_name(s32 scancode) { if (scancode == PAD_KEY_INVALID) { - return "Not bound"; + return "Not Bound"; } switch (scancode) { case PAD_KEY_MOUSE_LEFT: @@ -303,7 +303,7 @@ void ControllerConfigWindow::build_port_tab(Rml::Element* content, int port) { }); }; - addPageButton(Page::Controller, "Controller", [port] { return current_controller_name(port); }, [] { return false; }); + addPageButton(Page::Controller, "Device", [port] { return current_controller_name(port); }, [] { return false; }); addPageButton(Page::Buttons, "Buttons", [] { return Rml::String(">"); }, [] { return false; }); addPageButton(Page::Triggers, "Triggers", [] { return Rml::String(">"); }, [] { return false; }); addPageButton(Page::Sticks, "Sticks", [] { return Rml::String(">"); }, [] { return false; }); @@ -349,7 +349,14 @@ void ControllerConfigWindow::build_port_tab(Rml::Element* content, int port) { rightPane, [](Pane& pane) { pane.add_text("Treat analog trigger movement as digital L and R button input."); }); - + leftPane.register_control(leftPane.add_button("Restore Default Controls").on_pressed([this, port] { + mDoAud_seStartMenu(kSoundClick); + PADRestoreDefaultMapping(port); + }), + rightPane, [](Pane& pane) { + pane.clear(); + pane.add_text("Restores all binding configurations for the currently selected device to their defaults."); + }); render_page(rightPane, port, mPage); } @@ -365,7 +372,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { [port] { return PADGetIndexForPort(port) < 0 && !keyboard_active(port); }, }) .on_pressed([this, port] { - mDoAud_seStartMenu(kSoundItemChange); + mDoAud_seStartMenu(kSoundClick); cancel_pending_binding(); PADClearPort(port); PADSetKeyboardActive(static_cast(port), FALSE); @@ -378,7 +385,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { .isSelected = [port] { return keyboard_active(port); }, }) .on_pressed([this, port] { - mDoAud_seStartMenu(kSoundItemChange); + mDoAud_seStartMenu(kSoundClick); cancel_pending_binding(); PADClearPort(port); PADSetKeyboardActive(static_cast(port), TRUE); @@ -388,7 +395,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { const u32 controllerCount = PADCount(); if (controllerCount == 0) { - pane.add_text("No controllers detected"); + pane.add_text("No Device Detected"); break; } @@ -400,7 +407,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { [port, i] { return PADGetIndexForPort(port) == static_cast(i); }, }) .on_pressed([this, port, i] { - mDoAud_seStartMenu(kSoundItemChange); + mDoAud_seStartMenu(kSoundClick); cancel_pending_binding(); PADSetKeyboardActive(static_cast(port), FALSE); PADSetPortForIndex(i, port); @@ -425,17 +432,18 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { PADKeyButtonBinding* bindings = PADGetKeyButtonBindings(static_cast(port), &count); if (bindings == nullptr) { - return Rml::String("Not bound"); + return Rml::String("Not Bound"); } for (u32 i = 0; i < PAD_BUTTON_COUNT; ++i) { if (bindings[i].padButton == button) { return keyboard_key_name(bindings[i].scancode); } } - return Rml::String("Not bound"); + return Rml::String("Not Bound"); }, }) .on_pressed([this, port, button] { + mDoAud_seStartMenu(kSoundClick); cancel_pending_binding(); mPendingPort = port; mPendingBindingArmed = false; @@ -462,7 +470,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { u32 buttonCount = 0; PADButtonMapping* mappings = PADGetButtonMappings(port, &buttonCount); if (mappings == nullptr) { - pane.add_text("No controller selected"); + pane.add_text("No Device Selected"); break; } @@ -486,6 +494,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { }, }) .on_pressed([this, port, &mapping] { + mDoAud_seStartMenu(kSoundClick); cancel_pending_binding(); mPendingPort = port; mPendingBindingArmed = false; @@ -512,6 +521,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { }, }) .on_pressed([this, port, &mapping] { + mDoAud_seStartMenu(kSoundClick); cancel_pending_binding(); mPendingPort = port; mPendingBindingArmed = false; @@ -535,17 +545,18 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { PADKeyButtonBinding* bindings = PADGetKeyButtonBindings(static_cast(port), &count); if (bindings == nullptr) { - return Rml::String("Not bound"); + return Rml::String("Not Bound"); } for (u32 i = 0; i < PAD_BUTTON_COUNT; ++i) { if (bindings[i].padButton == button) { return keyboard_key_name(bindings[i].scancode); } } - return Rml::String("Not bound"); + return Rml::String("Not Bound"); }, }) .on_pressed([this, port, button] { + mDoAud_seStartMenu(kSoundClick); cancel_pending_binding(); mPendingPort = port; mPendingBindingArmed = false; @@ -566,17 +577,18 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { PADKeyAxisBinding* bindings = PADGetKeyAxisBindings(static_cast(port), &count); if (bindings == nullptr) { - return Rml::String("Not bound"); + return Rml::String("Not Bound"); } for (u32 i = 0; i < PAD_AXIS_COUNT; ++i) { if (bindings[i].padAxis == axis) { return keyboard_key_name(bindings[i].scancode); } } - return Rml::String("Not bound"); + return Rml::String("Not Bound"); }, }) .on_pressed([this, port, axis] { + mDoAud_seStartMenu(kSoundClick); cancel_pending_binding(); mPendingPort = port; mPendingBindingArmed = false; @@ -599,7 +611,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { u32 buttonCount = 0; PADButtonMapping* buttons = PADGetButtonMappings(port, &buttonCount); if (axes == nullptr && buttons == nullptr) { - pane.add_text("No controller selected"); + pane.add_text("No Device Selected"); break; } @@ -623,6 +635,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { }, }) .on_pressed([this, port, &mapping] { + mDoAud_seStartMenu(kSoundClick); cancel_pending_binding(); mPendingPort = port; mPendingBindingArmed = false; @@ -631,30 +644,33 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { } } - pane.add_section("Digital"); - if (buttons != nullptr) { - for (u32 i = 0; i < buttonCount; ++i) { - PADButtonMapping& mapping = buttons[i]; - if (mapping.padButton != PAD_TRIGGER_L && mapping.padButton != PAD_TRIGGER_R) { - continue; + if (getSettings().backend.enableAdvancedSettings) { + pane.add_section("Digital"); + if (buttons != nullptr) { + for (u32 i = 0; i < buttonCount; ++i) { + PADButtonMapping& mapping = buttons[i]; + if (mapping.padButton != PAD_TRIGGER_L && mapping.padButton != PAD_TRIGGER_R) { + continue; + } + pane.add_select_button({ + .key = PADGetButtonName(mapping.padButton), + .getValue = + [this, &mapping, gamepad] { + if (mPendingButtonMapping == &mapping) { + return pending_button_label(); + } + return native_button_name( + gamepad, mapping.nativeButton); + }, + }) + .on_pressed([this, port, &mapping] { + mDoAud_seStartMenu(kSoundClick); + cancel_pending_binding(); + mPendingPort = port; + mPendingBindingArmed = false; + mPendingButtonMapping = &mapping; + }); } - pane.add_select_button({ - .key = PADGetButtonName(mapping.padButton), - .getValue = - [this, &mapping, gamepad] { - if (mPendingButtonMapping == &mapping) { - return pending_button_label(); - } - return native_button_name( - gamepad, mapping.nativeButton); - }, - }) - .on_pressed([this, port, &mapping] { - cancel_pending_binding(); - mPendingPort = port; - mPendingBindingArmed = false; - mPendingButtonMapping = &mapping; - }); } } @@ -706,17 +722,18 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { PADKeyAxisBinding* bindings = PADGetKeyAxisBindings(static_cast(port), &count); if (bindings == nullptr) { - return Rml::String("Not bound"); + return Rml::String("Not Bound"); } for (u32 i = 0; i < PAD_AXIS_COUNT; ++i) { if (bindings[i].padAxis == axis) { return keyboard_key_name(bindings[i].scancode); } } - return Rml::String("Not bound"); + return Rml::String("Not Bound"); }, }) .on_pressed([this, port, axis] { + mDoAud_seStartMenu(kSoundClick); cancel_pending_binding(); mPendingPort = port; mPendingBindingArmed = false; @@ -741,7 +758,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { u32 axisCount = 0; PADAxisMapping* axes = PADGetAxisMappings(port, &axisCount); if (axes == nullptr) { - pane.add_text("No controller selected"); + pane.add_text("No Device Selected"); break; } @@ -762,6 +779,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { }, }) .on_pressed([this, port, &mapping] { + mDoAud_seStartMenu(kSoundClick); cancel_pending_binding(); mPendingPort = port; mPendingBindingArmed = false; @@ -907,6 +925,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { }, }) .on_pressed([this, port, actionBind] { + mDoAud_seStartMenu(kSoundClick); cancel_pending_binding(); mPendingPort = port; mPendingBindingArmed = false; @@ -926,7 +945,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { u32 buttonCount = 0; PADButtonMapping* mappings = PADGetButtonMappings(port, &buttonCount); if (mappings == nullptr) { - pane.add_text("No controller selected"); + pane.add_text("No Device Selected"); break; } @@ -950,6 +969,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) { }, }) .on_pressed([this, port, actionBind] { + mDoAud_seStartMenu(kSoundClick); cancel_pending_binding(); mPendingPort = port; mPendingBindingArmed = false; @@ -1058,6 +1078,7 @@ void ControllerConfigWindow::poll_pending_binding() { } void ControllerConfigWindow::finish_pending_binding(int completedPort) { + mDoAud_seStartMenu(kSoundBindingChanged); mPendingButtonMapping = nullptr; mPendingAxisMapping = nullptr; mPendingActionBinding = nullptr; @@ -1110,11 +1131,11 @@ bool ControllerConfigWindow::pending_input_neutral() const { } Rml::String ControllerConfigWindow::pending_button_label() const { - return mPendingBindingArmed ? "Press a button..." : "Waiting..."; + return mPendingBindingArmed ? "Press a Key or Button..." : "Waiting..."; } Rml::String ControllerConfigWindow::pending_axis_label() const { - return mPendingBindingArmed ? "Move axis or press a button..." : "Waiting..."; + return mPendingBindingArmed ? "Move Axis or press a Key or Button..." : "Waiting..."; } void ControllerConfigWindow::cancel_pending_binding() { @@ -1143,7 +1164,7 @@ void ControllerConfigWindow::finish_pending_key_binding() { } Rml::String ControllerConfigWindow::pending_key_label() const { - return mPendingBindingArmed ? "Press a key or mouse button..." : "Waiting..."; + return mPendingBindingArmed ? "Press a Key or Mouse Button..." : "Waiting..."; } void ControllerConfigWindow::stop_rumble_test() { @@ -1159,7 +1180,7 @@ void ControllerConfigWindow::stop_rumble_test() { Rml::String native_button_name(SDL_Gamepad* gamepad, u32 buttonUntyped) { if (buttonUntyped == PAD_NATIVE_BUTTON_INVALID) { - return "Not bound"; + return "Not Bound"; } auto button = static_cast(buttonUntyped); diff --git a/src/dusk/ui/overlay.cpp b/src/dusk/ui/overlay.cpp index 2ec47e6215..69c262720e 100644 --- a/src/dusk/ui/overlay.cpp +++ b/src/dusk/ui/overlay.cpp @@ -103,13 +103,13 @@ Rml::Element* create_controller_warning(Rml::Element* parent) { auto* heading = append(elem, "heading"); auto* title = append(heading, "span"); - title->SetInnerRML("No controller assigned"); + title->SetInnerRML("No Device Assigned"); auto* icon = append(heading, "icon"); icon->SetClass("warning", true); auto* message = append(elem, "message"); auto* content = append(message, "span"); - content->SetInnerRML("Configure controller port 1 in Settings."); + content->SetInnerRML("Configure Port 1 in Settings."); return elem; } diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index f12cb11ade..eb0df9fa6f 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -829,18 +829,18 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { }); }; - leftPane.add_section("Controller"); - leftPane.register_control(leftPane.add_button("Configure Controller").on_pressed([this] { + leftPane.add_section("Inputs"); + leftPane.register_control(leftPane.add_button("Configure Inputs").on_pressed([this] { push(std::make_unique(mPrelaunch)); }), rightPane, [](Pane& pane) { pane.clear(); - pane.add_text("Open controller binding configuration."); + pane.add_text("Open input binding configuration."); }); config_bool_select(leftPane, rightPane, getSettings().game.allowBackgroundInput, { - .key = "Allow Background Input", - .helpText = "Allow controller input even when the game window is not focused.", + .key = "Allow Background Inputs", + .helpText = "Allow inputs even when the game window is not focused.", .onChange = [](bool value) { aurora_set_background_input(value); }, }); @@ -1247,7 +1247,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { }); pane.add_button( { - .text = "Controller", + .text = "Missing Device", .isSelected = [] { return getSettings().game.enableControllerToasts.getValue(); }, }) diff --git a/src/dusk/ui/ui.cpp b/src/dusk/ui/ui.cpp index 89a174b000..90c1453948 100644 --- a/src/dusk/ui/ui.cpp +++ b/src/dusk/ui/ui.cpp @@ -132,7 +132,7 @@ void handle_event(const SDL_Event& event) noexcept { if (getSettings().game.enableControllerToasts) { const char* name = SDL_GetGamepadName(gamepad); Rml::String content = fmt::format("{}", name ? name : "[Unknown]"); - Rml::String title = "Controller connected"; + Rml::String title = "Device Connected"; if (const char* icon = connection_state_icon(SDL_GetGamepadConnectionState(gamepad))) { title = fmt::format( "{} &#x{};", title, @@ -165,7 +165,7 @@ void handle_event(const SDL_Event& event) noexcept { const char* name = SDL_GetGamepadNameForID(event.gdevice.which); push_toast({ .type = "controller", - .title = "Controller disconnected", + .title = "Device Disconnected", .content = name ? name : "[Unknown]", .duration = std::chrono::seconds(4), }); diff --git a/src/dusk/ui/ui.hpp b/src/dusk/ui/ui.hpp index 4a27ac7aac..cbfe3dcc9d 100644 --- a/src/dusk/ui/ui.hpp +++ b/src/dusk/ui/ui.hpp @@ -26,6 +26,8 @@ struct Toast { constexpr u32 kSoundClick = Z2SE_SY_CURSOR_OK; // "Play" button clicked/pressed constexpr u32 kSoundPlay = Z2SE_SY_ITEM_COMBINE_ON; +// Input binding changed +constexpr u32 kSoundBindingChanged = Z2SE_SY_ITEM_SET_X; // Menu button pressed (open/close menu bar or hide/show the active window) constexpr u32 kSoundMenuOpen = Z2SE_SY_MENU_SUB_IN; @@ -49,6 +51,8 @@ constexpr u32 kSoundItemDisable = Z2SE_SUBJ_VIEW_OUT; // Achievement unlocked constexpr u32 kSoundAchievementUnlock = Z2SE_NAVI_FLY; +// Warning prompt +constexpr u32 kSoundWarning = Z2SE_SY_COW_GET_IN; struct Insets { float top = 0.0f; From c710ea5b383c4de000de02d0a0957b6673bd238c Mon Sep 17 00:00:00 2001 From: doop <56421834+dooplecks@users.noreply.github.com> Date: Sun, 17 May 2026 09:44:29 -0400 Subject: [PATCH 07/10] Use 10% steps for bloom brightness setting (#1423) --- src/dusk/ui/graphics_tuner.cpp | 2 +- src/dusk/ui/graphics_tuner.hpp | 1 + src/dusk/ui/settings.cpp | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dusk/ui/graphics_tuner.cpp b/src/dusk/ui/graphics_tuner.cpp index 440d9f312a..4b70bab592 100644 --- a/src/dusk/ui/graphics_tuner.cpp +++ b/src/dusk/ui/graphics_tuner.cpp @@ -211,7 +211,7 @@ GraphicsTuner::GraphicsTuner(GraphicsTunerProps props, bool prelaunch) SteppedCarousel::Props{ .min = mValueMin, .max = mValueMax, - .step = 1, + .step = props.step, .getValue = [this] { return get_value(mOption); }, .onChange = [this](int value) { set_value(mOption, value); }, .formatValue = diff --git a/src/dusk/ui/graphics_tuner.hpp b/src/dusk/ui/graphics_tuner.hpp index 254aada22b..36932bcbbb 100644 --- a/src/dusk/ui/graphics_tuner.hpp +++ b/src/dusk/ui/graphics_tuner.hpp @@ -55,6 +55,7 @@ struct GraphicsTunerProps { int valueMin = 0; int valueMax = 0; int defaultValue = 0; + int step = 1; }; class GraphicsTuner : public Document { diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index eb0df9fa6f..cdd662d767 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -793,6 +793,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { .valueMin = 0, .valueMax = 100, .defaultValue = 100, + .step = 10, }, mPrelaunch); leftPane.add_section("Rendering"); From b91a58feab46ec3245f0857ae89782c799a5097d Mon Sep 17 00:00:00 2001 From: Olivia!! Date: Sun, 17 May 2026 15:45:08 +0200 Subject: [PATCH 08/10] Adds option to unbind using controller only (#1212) * Adds option to unbind using controller only * Adds a new cheat to let you transform from the start of the game, without Midna or the Shadow Crystal * Revert "Adds a new cheat to let you transform from the start of the game, without Midna or the Shadow Crystal" This reverts commit 51ed7367295a1cfc60be0adf2d16dcb84f2c0821. * Do not allow unbinding the A or B buttons to prevent softlocks when no other input sources are available (e.g. playing on a TV) * change 'and' to '&&' in accordance with code standards --- src/dusk/ui/controller_config.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/dusk/ui/controller_config.cpp b/src/dusk/ui/controller_config.cpp index 43b913b6a7..a0b2b6baa9 100644 --- a/src/dusk/ui/controller_config.cpp +++ b/src/dusk/ui/controller_config.cpp @@ -1033,6 +1033,12 @@ void ControllerConfigWindow::poll_pending_binding() { const s32 nativeButton = PADGetNativeButtonPressed(mPendingPort); if (nativeButton != -1) { const int completedPort = mPendingPort; + if (mPendingButtonMapping->nativeButton == static_cast(nativeButton) && + (mPendingButtonMapping->padButton != PAD_BUTTON_A && + mPendingButtonMapping->padButton != PAD_BUTTON_B)) { + unmap_pending_binding(); + return; + } mPendingButtonMapping->nativeButton = static_cast(nativeButton); finish_pending_binding(completedPort); } @@ -1043,6 +1049,10 @@ void ControllerConfigWindow::poll_pending_binding() { const PADSignedNativeAxis nativeAxis = PADGetNativeAxisPulled(mPendingPort); if (nativeAxis.nativeAxis != -1) { const int completedPort = mPendingPort; + if (mPendingAxisMapping->nativeAxis.nativeAxis == nativeAxis.nativeAxis) { + unmap_pending_binding(); + return; + } mPendingAxisMapping->nativeAxis = nativeAxis; mPendingAxisMapping->nativeButton = -1; finish_pending_binding(completedPort); @@ -1069,6 +1079,10 @@ void ControllerConfigWindow::poll_pending_binding() { if (button != -1) { const int completedPort = mPendingPort; + if (mPendingActionBinding->getValue() == button) { + unmap_pending_binding(); + return; + } mPendingActionBinding->setValue(button); config::Save(); finish_pending_binding(completedPort); From a499877c37c95c9629f7c6e90b790e3bec645e90 Mon Sep 17 00:00:00 2001 From: SrBananaMan <86680367+SrBananaMan@users.noreply.github.com> Date: Sun, 17 May 2026 08:51:15 -0500 Subject: [PATCH 09/10] Add an enable texture replacements option in the video tab (#1517) Co-authored-by: Luke Street --- include/dusk/settings.h | 1 + src/dusk/settings.cpp | 2 ++ src/dusk/ui/settings.cpp | 6 ++++++ src/m_Do/m_Do_main.cpp | 2 +- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/dusk/settings.h b/include/dusk/settings.h index c652d71789..6d281b6f1a 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -127,6 +127,7 @@ struct UserSettings { ConfigVar bloomMode; ConfigVar bloomMultiplier; ConfigVar disableWaterRefraction; + ConfigVar enableTextureReplacements; ConfigVar enableFrameInterpolation; ConfigVar internalResolutionScale; ConfigVar shadowResolutionMultiplier; diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index f8ca1b418a..8eeb762f9c 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -65,6 +65,7 @@ UserSettings g_userSettings = { .bloomMode {"game.bloomMode", BloomMode::Dusk}, .bloomMultiplier {"game.bloomMultiplier", 1.0f}, .disableWaterRefraction {"game.disableWaterRefraction", false}, + .enableTextureReplacements {"game.enableTextureReplacements", true}, .enableFrameInterpolation {"game.enableFrameInterpolation", false}, .internalResolutionScale {"game.internalResolutionScale", 0}, .shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1}, @@ -229,6 +230,7 @@ void registerSettings() { Register(g_userSettings.game.bloomMode); Register(g_userSettings.game.bloomMultiplier); Register(g_userSettings.game.disableWaterRefraction); + Register(g_userSettings.game.enableTextureReplacements); Register(g_userSettings.game.internalResolutionScale); Register(g_userSettings.game.shadowResolutionMultiplier); Register(g_userSettings.game.enableDepthOfField); diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index cdd662d767..79082f75ef 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -797,6 +797,12 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { }, mPrelaunch); leftPane.add_section("Rendering"); + config_bool_select(leftPane, rightPane, getSettings().game.enableTextureReplacements, + { + .key = "Use Texture Pack", + .helpText = "Enable installed texture replacements.", + .onChange = [](bool value) { aurora_set_texture_replacements_enabled(value); }, + }); config_bool_select(leftPane, rightPane, getSettings().game.enableFrameInterpolation, { .key = "Unlock Framerate", diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 56ff865bee..8ed3e38430 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -563,7 +563,7 @@ int game_main(int argc, char* argv[]) { config.allowJoystickBackgroundEvents = dusk::getSettings().game.allowBackgroundInput; config.pauseOnFocusLost = dusk::getSettings().game.pauseOnFocusLost; config.imGuiInitCallback = &aurora_imgui_init_callback; - config.allowTextureReplacements = true; + config.allowTextureReplacements = dusk::getSettings().game.enableTextureReplacements; config.allowTextureDumps = false; auroraInfo = aurora_initialize(argc, argv, &config); } From 31dd069879820fb91f7e94ac723c94a269c8de20 Mon Sep 17 00:00:00 2001 From: Giorgio Mendieta <31053658+GiorgioMendieta@users.noreply.github.com> Date: Sun, 17 May 2026 15:51:38 +0200 Subject: [PATCH 10/10] docs: Improve building.md file (#1530) * docs: Improve building.md file - Fix broken XCode link - Add MacOS running instructions - Alphabetize linux packages * fix: collapse packages sections for readability * fix: collapse packages sections for readability * fix: add --dvd flag to specify iso path * fix: Remove incorrect info about game.iso --- docs/building.md | 236 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 183 insertions(+), 53 deletions(-) diff --git a/docs/building.md b/docs/building.md index 1001c42788..9f7879ab48 100644 --- a/docs/building.md +++ b/docs/building.md @@ -1,50 +1,164 @@ -### Building -#### Prerequisites +# Building Dusklight + +## Dependencies + +The following dependencies are required: + * [CMake 3.25+](https://cmake.org) - * Windows: Install `CMake Tools` in Visual Studio - * macOS: `brew install cmake` * [Python 3+](https://python.org) - * Windows: [Microsoft Store](https://go.microsoft.com/fwlink?linkID=2082640) - * Verify it's added to `%PATH%` by typing `python` in `cmd`. - * macOS: `brew install python@3` -* **[Windows]** [Visual Studio 2026 Community](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx) - * Select `C++ Development` and verify the following packages are included: - * `Windows 11 SDK` - * `CMake Tools` - * `C++ Clang Compiler` - * `C++ Clang-cl` -* **[macOS]** [Xcode 16.4+](https://developer.apple.com/xcode/download/) -* **[Linux]** Actively tested on Ubuntu 24.04, Arch Linux & derivatives. - * Ubuntu 24.04+ packages - ``` - build-essential curl git ninja-build clang lld zlib1g-dev libcurl4-openssl-dev \ - libglu1-mesa-dev libdbus-1-dev libvulkan-dev libxi-dev libxrandr-dev libasound2-dev libpulse-dev \ - libudev-dev libpng-dev libncurses5-dev cmake libx11-xcb-dev python3 python-is-python3 \ - libclang-dev libfreetype-dev libxinerama-dev libxcursor-dev python3-markupsafe libgtk-3-dev \ - libxss-dev libxtst-dev - ``` - * Arch Linux packages - ``` - base-devel cmake ninja llvm vulkan-headers python python-markupsafe clang lld alsa-lib libpulse libxrandr freetype2 - ``` - * Fedora packages - ``` - cmake vulkan-headers ninja-build clang-devel llvm-devel libpng-devel - ``` - * It's also important that you install the developer tools and libraries - ``` - sudo dnf groupinstall "Development Tools" "Development Libraries" - ``` -#### Setup -Clone and initialize the Dusklight repository + +### Windows + +* Install [CMake 3.25+](https://cmake.org) by searching `CMake Tools` in Visual Studio +* Install Python 3 from the [Microsoft Store](https://go.microsoft.com/fwlink?linkID=2082640) and verify it's added to `%PATH%` by typing `python` in `cmd`. + +Recommended IDEs: + +* [Visual Studio 2026 Community](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx). During installation: + * Select `C++ Development` and verify the following packages are included: + * `Windows 11 SDK` + * `CMake Tools` + * `C++ Clang Compiler` + * `C++ Clang-cl` + +### macOS + +* Make sure [Homebrew](https://brew.sh) is installed +* Install [CMake 3.25+](https://cmake.org) + ```sh -git clone --recursive https://github.com/TwilitRealm/dusklight.git -cd dusklight -git pull -git submodule update --init --recursive +brew install cmake ``` -#### Building +* Install Python 3 + +```sh +brew install python@3 +``` + +Recommended IDEs: + +* [Xcode 16.4 or later](https://developer.apple.com/xcode/) +* [Visual Studio Code](https://code.visualstudio.com/download/) +* [CLion](https://www.jetbrains.com/clion/) + +### Linux + +Actively tested on Ubuntu 24.04, Arch Linux & derivatives. + +**Ubuntu 24.04+ packages** + +
+Click to expand + +* Run the following command to install the required dependencies: + +```sh +sudo apt update && sudo apt install -y \ + build-essential \ + clang \ + cmake \ + curl \ + git \ + libasound2-dev \ + libclang-dev \ + libcurl4-openssl-dev \ + libdbus-1-dev \ + libfreetype-dev \ + libglu1-mesa-dev \ + libgtk-3-dev \ + libncurses5-dev \ + libpng-dev \ + libpulse-dev \ + libudev-dev \ + libvulkan-dev \ + libx11-xcb-dev \ + libxcursor-dev \ + libxi-dev \ + libxinerama-dev \ + libxrandr-dev \ + libxss-dev \ + libxtst-dev \ + lld \ + ninja-build \ + python-is-python3 \ + python3 \ + python3-markupsafe \ + zlib1g-dev +``` + +
+
+ +**Arch Linux packages** + +
+Click to expand + +* Run the following command to install the required dependencies: + +```sh +sudo pacman -S --needed \ + alsa-lib \ + base-devel \ + clang \ + cmake \ + freetype2 \ + libpulse \ + libxrandr \ + lld \ + llvm \ + ninja \ + python \ + python-markupsafe \ + vulkan-headers +``` + +
+
+ +**Fedora packages** + +
+Click to expand + +* Run the following command to install the required dependencies: + +```sh +sudo dnf install -y \ + clang-devel \ + cmake \ + libpng-devel \ + llvm-devel \ + ninja-build \ + vulkan-headers +``` + +* It's also important that you install the developer tools and libraries + +```sh +sudo dnf groupinstall \ + "Development Libraries" "Development Tools" +``` + +
+
+ +Recommended IDEs: + +* [CLion](https://www.jetbrains.com/clion/) +* [Visual Studio Code](https://code.visualstudio.com/download/) + +## Building + +* Clone and initialize the Dusklight repository: + +```sh +git clone --recursive https://github.com/TwilitRealm/dusklight.git +git pull +cd dusklight +git submodule update --init --recursive +``` **CLion (Windows / macOS / Linux)** @@ -64,7 +178,8 @@ cmake --build --preset macos-default-relwithdebinfo ``` Alternate presets available: -- `macos-default-debug`: Clang, Debug + +* `macos-default-debug`: Clang, Debug **ninja (Linux)** @@ -74,9 +189,10 @@ cmake --build --preset linux-default-relwithdebinfo ``` Alternate presets available: -- `linux-default-debug`: GCC, Debug -- `linux-clang-relwithdebinfo`: Clang, RelWithDebInfo -- `linux-clang-debug`: Clang, Debug + +* `linux-default-debug`: GCC, Debug +* `linux-clang-relwithdebinfo`: Clang, RelWithDebInfo +* `linux-clang-debug`: Clang, Debug **ninja (Windows)** @@ -86,13 +202,27 @@ cmake --build --preset windows-msvc-relwithdebinfo ``` Alternate presets available: -- `windows-msvc-debug`: MSVC, Debug -- `windows-clang-relwithdebinfo`: Clang-cl, RelWithDebInfo -- `windows-clang-debug`: Clang-cl, Debug -#### Running -Pass the disc image as a positional argument. Supported formats: ISO (GCM), RVZ, WIA, WBFS, CISO, GCZ +* `windows-msvc-debug`: MSVC, Debug +* `windows-clang-relwithdebinfo`: Clang-cl, RelWithDebInfo +* `windows-clang-debug`: Clang-cl, Debug + +## Running + +**Windows / Linux** + +* Pass the disc image as a positional argument using the `--dvd` flag. Supported formats are: ISO (GCM), RVZ, WIA, WBFS, CISO, GCZ + ```sh -build/{preset}/dusklight/path/to/game.rvz +build/{preset}/dusklight --dvd /path/to/game.iso +``` + +**macOS** + +macOS builds an `.app` bundle which contains the executable and all necessary resources. + +* Pass the disc image as a positional argument using the `--dvd` flag. Supported formats are: ISO (GCM), RVZ, WIA, WBFS, CISO, GCZ + +```sh +build/{preset}/Dusklight.app/Contents/MacOS/Dusklight --dvd /path/to/game.iso ``` -If no path is specified, Dusklight defaults to `game.iso` in the current working directory.