From 8e4e4b1cae1bc301a177bbbffbca55f3c6cba8c3 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sun, 21 Jan 2024 14:04:56 -0500 Subject: [PATCH] Split recomp config handling into separate file, changed "inputs" RmlUi variable to be a struct with an "array" member and named fields for each input --- CMakeLists.txt | 1 + assets/config_menu.rml | 12 +---- include/recomp_config.h | 17 +++++++ include/recomp_input.h | 53 +++++++++++++++++--- include/rt64_layer.h | 1 + src/game/config.cpp | 59 ++++++++++++++++++++++ src/game/controls.cpp | 103 +++++++------------------------------- src/main/main.cpp | 3 +- src/recomp/rt64_layer.cpp | 32 +++++++++++- src/ui/ui_config.cpp | 87 ++++++++++++++++++++++++-------- ultramodern/config.hpp | 3 +- ultramodern/events.cpp | 21 +++++--- 12 files changed, 258 insertions(+), 134 deletions(-) create mode 100644 include/recomp_config.h create mode 100644 src/game/config.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cd89d7..0a17f46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,6 +121,7 @@ set (SOURCES ${CMAKE_SOURCE_DIR}/src/game/input.cpp ${CMAKE_SOURCE_DIR}/src/game/controls.cpp + ${CMAKE_SOURCE_DIR}/src/game/config.cpp ${CMAKE_SOURCE_DIR}/src/game/quicksaving.cpp ${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp diff --git a/assets/config_menu.rml b/assets/config_menu.rml index e5e60b7..a157178 100644 --- a/assets/config_menu.rml +++ b/assets/config_menu.rml @@ -133,7 +133,7 @@
diff --git a/include/recomp_config.h b/include/recomp_config.h new file mode 100644 index 0000000..a3caaee --- /dev/null +++ b/include/recomp_config.h @@ -0,0 +1,17 @@ +#ifndef __RECOMP_CONFIG_H__ +#define __RECOMP_CONFIG_H__ + +#include +#include "../ultramodern/config.hpp" + +namespace recomp { + constexpr std::u8string_view program_id = u8"ZeldaRecomp"; + + void load_config(); + void save_config(); + + void reset_input_bindings(); + void reset_graphics_options(); +}; + +#endif \ No newline at end of file diff --git a/include/recomp_input.h b/include/recomp_input.h index 0eb1488..e64cd05 100644 --- a/include/recomp_input.h +++ b/include/recomp_input.h @@ -7,8 +7,50 @@ #include #include #include +#include namespace recomp { + // x-macros to build input enums and arrays. + // First parameter is the enum name, second parameter is the bit field for the input (or 0 if there is no associated one), third is the readable name. + #define DEFINE_N64_BUTTON_INPUTS() \ + DEFINE_INPUT(A, 0x8000, "[A Button]") \ + DEFINE_INPUT(B, 0x4000, "[B Button]") \ + DEFINE_INPUT(Z, 0x2000, "[Z Button]") \ + DEFINE_INPUT(START, 0x1000, "[Start Button]") \ + DEFINE_INPUT(DPAD_UP, 0x0800, "[Dpad Up]") \ + DEFINE_INPUT(DPAD_DOWN, 0x0400, "[Dpad Down]") \ + DEFINE_INPUT(DPAD_LEFT, 0x0200, "[Dpad Left]") \ + DEFINE_INPUT(DPAD_RIGHT, 0x0100, "[Dpad Right]") \ + DEFINE_INPUT(L, 0x0020, "[L Button]") \ + DEFINE_INPUT(R, 0x0010, "[R Button]") \ + DEFINE_INPUT(C_UP, 0x0008, "[C Up]") \ + DEFINE_INPUT(C_DOWN, 0x0004, "[C Down]") \ + DEFINE_INPUT(C_LEFT, 0x0002, "[C Left]") \ + DEFINE_INPUT(C_RIGHT, 0x0001, "[C Right]") + + #define DEFINE_N64_AXIS_INPUTS() \ + DEFINE_INPUT(X_AXIS_NEG, 0, "[Analog Left]") \ + DEFINE_INPUT(X_AXIS_POS, 0, "[Analog Right]") \ + DEFINE_INPUT(Y_AXIS_NEG, 0, "[Analog Down]") \ + DEFINE_INPUT(Y_AXIS_POS, 0, "[Analog Up]") \ + + #define DEFINE_ALL_INPUTS() \ + DEFINE_N64_BUTTON_INPUTS() \ + DEFINE_N64_AXIS_INPUTS() + + // Enum containing every recomp input. + #define DEFINE_INPUT(name, value, readable) name, + enum class GameInput { + DEFINE_ALL_INPUTS() + + COUNT, + N64_BUTTON_START = A, + N64_BUTTON_COUNT = C_RIGHT - N64_BUTTON_START + 1, + N64_AXIS_START = X_AXIS_NEG, + N64_AXIS_COUNT = Y_AXIS_POS - N64_AXIS_START + 1, + }; + #undef DEFINE_INPUT + struct InputField { uint32_t input_type; int32_t input_id; @@ -61,13 +103,12 @@ namespace recomp { constexpr size_t bindings_per_input = 2; - // Loads the user's saved controller mapping if one exists, loads the default mappings if no saved mapping exists. - void init_control_mappings(); size_t get_num_inputs(); - const std::string& get_input_name(size_t input_index); - const std::string& get_input_enum_name(size_t input_index); - InputField& get_input_binding(size_t input_index, size_t binding_index, InputDevice device); - void set_input_binding(size_t input_index, size_t binding_index, InputDevice device, InputField value); + const std::string& get_input_name(GameInput input); + const std::string& get_input_enum_name(GameInput input); + GameInput get_input_from_enum_name(const std::string_view name); + InputField& get_input_binding(GameInput input, size_t binding_index, InputDevice device); + void set_input_binding(GameInput input, size_t binding_index, InputDevice device, InputField value); void get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out); void handle_events(); diff --git a/include/rt64_layer.h b/include/rt64_layer.h index 3840c12..a27c094 100644 --- a/include/rt64_layer.h +++ b/include/rt64_layer.h @@ -18,6 +18,7 @@ void RT64SendDL(uint8_t* rdram, const OSTask* task); void RT64UpdateScreen(uint32_t vi_origin); void RT64ChangeWindow(); void RT64Shutdown(); +RT64::UserConfiguration::Antialiasing RT64MaxMSAA(); void set_rt64_hooks(); diff --git a/src/game/config.cpp b/src/game/config.cpp new file mode 100644 index 0000000..739d051 --- /dev/null +++ b/src/game/config.cpp @@ -0,0 +1,59 @@ +#include "recomp_config.h" +#include "recomp_input.h" +#include "../../ultramodern/config.hpp" + +void recomp::reset_input_bindings() { + auto assign_mapping = [](recomp::InputDevice device, recomp::GameInput input, const std::vector& value) { + for (size_t binding_index = 0; binding_index < std::min(value.size(), recomp::bindings_per_input); binding_index++) { + recomp::set_input_binding(input, binding_index, device, value[binding_index]); + } + }; + + auto assign_all_mappings = [&](recomp::InputDevice device, const recomp::DefaultN64Mappings& values) { + assign_mapping(device, recomp::GameInput::A, values.a); + assign_mapping(device, recomp::GameInput::B, values.b); + assign_mapping(device, recomp::GameInput::Z, values.z); + assign_mapping(device, recomp::GameInput::START, values.start); + assign_mapping(device, recomp::GameInput::DPAD_UP, values.dpad_up); + assign_mapping(device, recomp::GameInput::DPAD_DOWN, values.dpad_down); + assign_mapping(device, recomp::GameInput::DPAD_LEFT, values.dpad_left); + assign_mapping(device, recomp::GameInput::DPAD_RIGHT, values.dpad_right); + assign_mapping(device, recomp::GameInput::L, values.l); + assign_mapping(device, recomp::GameInput::R, values.r); + assign_mapping(device, recomp::GameInput::C_UP, values.c_up); + assign_mapping(device, recomp::GameInput::C_DOWN, values.c_down); + assign_mapping(device, recomp::GameInput::C_LEFT, values.c_left); + assign_mapping(device, recomp::GameInput::C_RIGHT, values.c_right); + + assign_mapping(device, recomp::GameInput::X_AXIS_NEG, values.analog_left); + assign_mapping(device, recomp::GameInput::X_AXIS_POS, values.analog_right); + assign_mapping(device, recomp::GameInput::Y_AXIS_NEG, values.analog_down); + assign_mapping(device, recomp::GameInput::Y_AXIS_POS, values.analog_up); + }; + + assign_all_mappings(recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings); + assign_all_mappings(recomp::InputDevice::Controller, recomp::default_n64_controller_mappings); +} + +void recomp::reset_graphics_options() { + ultramodern::GraphicsConfig new_config{}; + new_config.res_option = ultramodern::Resolution::Auto; + new_config.wm_option = ultramodern::WindowMode::Fullscreen; + new_config.ar_option = RT64::UserConfiguration::AspectRatio::Expand; + new_config.msaa_option = RT64::UserConfiguration::Antialiasing::MSAA4X; + new_config.rr_option = RT64::UserConfiguration::RefreshRate::Original; + new_config.rr_manual_value = 60; + ultramodern::set_graphics_config(new_config); +} + +void recomp::load_config() { + // TODO load from a file if one exists. + recomp::reset_input_bindings(); + + // TODO load from a file if one exists. + recomp::reset_graphics_options(); +} + +void recomp::save_config() { + +} diff --git a/src/game/controls.cpp b/src/game/controls.cpp index d0e2ef6..265310a 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -5,50 +5,9 @@ #include "../ultramodern/ultramodern.hpp" #include "../patches/input.h" -// x-macros to build input enums and arrays. -// First parameter is the enum name, second parameter is the bit field for the input (or 0 if there is no associated one), third is the readable name. -#define DEFINE_N64_BUTTON_INPUTS() \ - DEFINE_INPUT(A, 0x8000, "[A Button]") \ - DEFINE_INPUT(B, 0x4000, "[B Button]") \ - DEFINE_INPUT(Z, 0x2000, "[Z Button]") \ - DEFINE_INPUT(START, 0x1000, "[Start Button]") \ - DEFINE_INPUT(DPAD_UP, 0x0800, "[Dpad Up]") \ - DEFINE_INPUT(DPAD_DOWN, 0x0400, "[Dpad Down]") \ - DEFINE_INPUT(DPAD_LEFT, 0x0200, "[Dpad Left]") \ - DEFINE_INPUT(DPAD_RIGHT, 0x0100, "[Dpad Right]") \ - DEFINE_INPUT(L, 0x0020, "[L Button]") \ - DEFINE_INPUT(R, 0x0010, "[R Button]") \ - DEFINE_INPUT(C_UP, 0x0008, "[C Up]") \ - DEFINE_INPUT(C_DOWN, 0x0004, "[C Down]") \ - DEFINE_INPUT(C_LEFT, 0x0002, "[C Left]") \ - DEFINE_INPUT(C_RIGHT, 0x0001, "[C Right]") - -#define DEFINE_N64_AXIS_INPUTS() \ - DEFINE_INPUT(X_AXIS_NEG, 0, "[Analog Left]") \ - DEFINE_INPUT(X_AXIS_POS, 0, "[Analog Right]") \ - DEFINE_INPUT(Y_AXIS_NEG, 0, "[Analog Down]") \ - DEFINE_INPUT(Y_AXIS_POS, 0, "[Analog Up]") \ - -#define DEFINE_ALL_INPUTS() \ - DEFINE_N64_BUTTON_INPUTS() \ - DEFINE_N64_AXIS_INPUTS() - -// Make the input enum. -#define DEFINE_INPUT(name, value, readable) name, -enum class GameInput { - DEFINE_ALL_INPUTS() - - COUNT, - N64_BUTTON_START = A, - N64_BUTTON_COUNT = C_RIGHT - N64_BUTTON_START + 1, - N64_AXIS_START = X_AXIS_NEG, - N64_AXIS_COUNT = Y_AXIS_POS - N64_AXIS_START + 1, -}; -#undef DEFINE_INPUT - // Arrays that hold the mappings for every input for keyboard and controller respectively. using input_mapping = std::array; -using input_mapping_array = std::array; +using input_mapping_array = std::array(recomp::GameInput::COUNT)>; static input_mapping_array keyboard_input_mappings{}; static input_mapping_array controller_input_mappings{}; @@ -73,57 +32,31 @@ static const std::vector input_enum_names = { }; #undef DEFINE_INPUT -void recomp::init_control_mappings() { - // TODO load from a file if one exists. - - auto assign_mapping = [](input_mapping_array& mapping, GameInput input, const std::vector& value) { - input_mapping& cur_mapping = mapping.at((size_t)input); - std::copy_n(value.begin(), std::min(value.size(), cur_mapping.size()), cur_mapping.begin()); - }; - - auto assign_all_mappings = [&](input_mapping_array& mapping, const recomp::DefaultN64Mappings& values) { - assign_mapping(mapping, GameInput::A, values.a); - assign_mapping(mapping, GameInput::A, values.a); - assign_mapping(mapping, GameInput::B, values.b); - assign_mapping(mapping, GameInput::Z, values.z); - assign_mapping(mapping, GameInput::START, values.start); - assign_mapping(mapping, GameInput::DPAD_UP, values.dpad_up); - assign_mapping(mapping, GameInput::DPAD_DOWN, values.dpad_down); - assign_mapping(mapping, GameInput::DPAD_LEFT, values.dpad_left); - assign_mapping(mapping, GameInput::DPAD_RIGHT, values.dpad_right); - assign_mapping(mapping, GameInput::L, values.l); - assign_mapping(mapping, GameInput::R, values.r); - assign_mapping(mapping, GameInput::C_UP, values.c_up); - assign_mapping(mapping, GameInput::C_DOWN, values.c_down); - assign_mapping(mapping, GameInput::C_LEFT, values.c_left); - assign_mapping(mapping, GameInput::C_RIGHT, values.c_right); - - assign_mapping(mapping, GameInput::X_AXIS_NEG, values.analog_left); - assign_mapping(mapping, GameInput::X_AXIS_POS, values.analog_right); - assign_mapping(mapping, GameInput::Y_AXIS_NEG, values.analog_down); - assign_mapping(mapping, GameInput::Y_AXIS_POS, values.analog_up); - }; - - assign_all_mappings(keyboard_input_mappings, recomp::default_n64_keyboard_mappings); - assign_all_mappings(controller_input_mappings, recomp::default_n64_controller_mappings); -} - size_t recomp::get_num_inputs() { return (size_t)GameInput::COUNT; } -const std::string& recomp::get_input_name(size_t input_index) { - return input_names.at(input_index); +const std::string& recomp::get_input_name(GameInput input) { + return input_names.at(static_cast(input)); } -const std::string& recomp::get_input_enum_name(size_t input_index) { - return input_enum_names.at(input_index); +const std::string& recomp::get_input_enum_name(GameInput input) { + return input_enum_names.at(static_cast(input)); +} + +recomp::GameInput recomp::get_input_from_enum_name(const std::string_view enum_name) { + auto find_it = std::find(input_enum_names.begin(), input_enum_names.end(), enum_name); + if (find_it == input_enum_names.end()) { + return recomp::GameInput::COUNT; + } + + return static_cast(find_it - input_enum_names.begin()); } // Due to an RmlUi limitation this can't be const. Ideally it would return a const reference or even just a straight up copy. -recomp::InputField& recomp::get_input_binding(size_t input_index, size_t binding_index, recomp::InputDevice device) { +recomp::InputField& recomp::get_input_binding(GameInput input, size_t binding_index, recomp::InputDevice device) { input_mapping_array& device_mappings = (device == recomp::InputDevice::Controller) ? controller_input_mappings : keyboard_input_mappings; - input_mapping& cur_input_mapping = device_mappings.at(input_index); + input_mapping& cur_input_mapping = device_mappings.at(static_cast(input)); if (binding_index < cur_input_mapping.size()) { return cur_input_mapping[binding_index]; @@ -134,9 +67,9 @@ recomp::InputField& recomp::get_input_binding(size_t input_index, size_t binding } } -void recomp::set_input_binding(size_t input_index, size_t binding_index, recomp::InputDevice device, recomp::InputField value) { +void recomp::set_input_binding(recomp::GameInput input, size_t binding_index, recomp::InputDevice device, recomp::InputField value) { input_mapping_array& device_mappings = (device == recomp::InputDevice::Controller) ? controller_input_mappings : keyboard_input_mappings; - input_mapping& cur_input_mapping = device_mappings.at(input_index); + input_mapping& cur_input_mapping = device_mappings.at(static_cast(input)); if (binding_index < cur_input_mapping.size()) { cur_input_mapping[binding_index] = value; diff --git a/src/main/main.cpp b/src/main/main.cpp index daa88a0..1f25c59 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -19,6 +19,7 @@ #include "recomp_ui.h" #include "recomp_input.h" +#include "recomp_config.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -234,7 +235,7 @@ int main(int argc, char** argv) { reset_audio(48000); init(); - recomp::init_control_mappings(); + recomp::load_config(); ultramodern::gfx_callbacks_t gfx_callbacks{ .create_gfx = create_gfx, diff --git a/src/recomp/rt64_layer.cpp b/src/recomp/rt64_layer.cpp index 962af8f..da8eaa7 100644 --- a/src/recomp/rt64_layer.cpp +++ b/src/recomp/rt64_layer.cpp @@ -108,6 +108,8 @@ unsigned int VI_Y_SCALE_REG = 0; unsigned int SP_STATUS_REG = 0; unsigned int RDRAM_SIZE = 0x800000; +static RT64::UserConfiguration::Antialiasing device_max_msaa = RT64::UserConfiguration::Antialiasing::None; + #define GET_FUNC(lib, name) \ name = (decltype(name))GetProcAddress(lib, #name) @@ -115,6 +117,19 @@ void dummy_check_interrupts() { } +RT64::UserConfiguration::Antialiasing compute_max_supported_aa(RT64::RenderSampleCounts bits) { + if (bits & RT64::RenderSampleCount::Bits::COUNT_2) { + if (bits & RT64::RenderSampleCount::Bits::COUNT_4) { + if (bits & RT64::RenderSampleCount::Bits::COUNT_8) { + return RT64::UserConfiguration::Antialiasing::MSAA8X; + } + return RT64::UserConfiguration::Antialiasing::MSAA4X; + } + return RT64::UserConfiguration::Antialiasing::MSAA2X; + }; + return RT64::UserConfiguration::Antialiasing::None; +} + RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHandle window_handle) { set_rt64_hooks(); // Dynamic loading @@ -170,14 +185,23 @@ RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHan gfx_info.RDRAM_SIZE = &RDRAM_SIZE; #if defined(_WIN32) - return InitiateGFXWindows(gfx_info, window_handle.window, window_handle.thread_id); + RT64::Application* ret = InitiateGFXWindows(gfx_info, window_handle.window, window_handle.thread_id); #elif defined(__ANDROID__) static_assert(false && "Unimplemented"); #elif defined(__linux__) - return InitiateGFXLinux(gfx_info, window_handle.window, window_handle.display); + RT64::Application* ret = InitiateGFXLinux(gfx_info, window_handle.window, window_handle.display); #else static_assert(false && "Unimplemented"); #endif + + // Before configuring multisampling, make sure the device actually supports it for the formats we'll use. If it doesn't, turn off antialiasing in the configuration. + RT64::RenderSampleCounts color_sample_counts = ret->device->getSampleCountsSupported(RT64::RenderFormat::R8G8B8A8_UNORM); + RT64::RenderSampleCounts depth_sample_counts = ret->device->getSampleCountsSupported(RT64::RenderFormat::D32_FLOAT); + RT64::RenderSampleCounts common_sample_counts = color_sample_counts & depth_sample_counts; + + device_max_msaa = compute_max_supported_aa(common_sample_counts); + + return ret; } void RT64SendDL(uint8_t* rdram, const OSTask* task) { @@ -235,3 +259,7 @@ void RT64UpdateConfig(RT64::Application* application, const ultramodern::Graphic application->updateMultisampling(); } } + +RT64::UserConfiguration::Antialiasing RT64MaxMSAA() { + return device_max_msaa; +} diff --git a/src/ui/ui_config.cpp b/src/ui/ui_config.cpp index 75b2768..892193f 100644 --- a/src/ui/ui_config.cpp +++ b/src/ui/ui_config.cpp @@ -1,10 +1,10 @@ #include "recomp_ui.h" #include "recomp_input.h" +#include "recomp_config.h" #include "../../ultramodern/config.hpp" #include "../../ultramodern/ultramodern.hpp" #include "RmlUi/Core.h" -ultramodern::GraphicsConfig cur_options; ultramodern::GraphicsConfig new_options; Rml::DataModelHandle graphics_model_handle; Rml::DataModelHandle controls_model_handle; @@ -61,7 +61,7 @@ static int focused_input_index = -1; constexpr recomp::InputDevice cur_device = recomp::InputDevice::Controller; void recomp::finish_scanning_input(recomp::InputField scanned_field) { - recomp::set_input_binding(scanned_input_index, scanned_binding_index, cur_device, scanned_field); + recomp::set_input_binding(static_cast(scanned_input_index), scanned_binding_index, cur_device, scanned_field); scanned_input_index = -1; scanned_binding_index = -1; controls_model_handle.DirtyVariable("inputs"); @@ -69,6 +69,25 @@ void recomp::finish_scanning_input(recomp::InputField scanned_field) { controls_model_handle.DirtyVariable("active_binding_slot"); } +// Counts down every frame while positive until it reaches 0, then saves the graphics config file. +// This prevents a graphics config that would cause the game to crash from +static std::atomic_int save_graphics_config_frame_timer; + +void queue_saving_graphics_config() { + save_graphics_config_frame_timer = 5; +} + +void close_config_menu() { + recomp::save_config(); + + if (ultramodern::is_game_started()) { + recomp::set_current_menu(recomp::Menu::None); + } + else { + recomp::set_current_menu(recomp::Menu::Launcher); + } +} + class ConfigMenu : public recomp::MenuController { public: ConfigMenu() { @@ -83,20 +102,14 @@ public: void register_events(recomp::UiEventListenerInstancer& listener) override { recomp::register_event(listener, "apply_options", [](const std::string& param, Rml::Event& event) { - cur_options = new_options; graphics_model_handle.DirtyVariable("options_changed"); - update_graphics_config(new_options); + ultramodern::set_graphics_config(new_options); }); recomp::register_event(listener, "config_keydown", [](const std::string& param, Rml::Event& event) { if (event.GetId() == Rml::EventId::Keydown) { if (event.GetParameter("key_identifier", Rml::Input::KeyIdentifier::KI_UNKNOWN) == Rml::Input::KeyIdentifier::KI_ESCAPE) { - if (ultramodern::is_game_started()) { - recomp::set_current_menu(recomp::Menu::None); - } - else { - recomp::set_current_menu(recomp::Menu::Launcher); - } + close_config_menu(); } } }); @@ -107,6 +120,12 @@ public: throw std::runtime_error("Failed to make RmlUi data model for the graphics config menu"); } + { + using namespace std::chrono_literals; + std::this_thread::sleep_for(50ms); + } + new_options = ultramodern::get_graphics_config(); + bind_option(constructor, "res_option", &new_options.res_option); bind_option(constructor, "wm_option", &new_options.wm_option); bind_option(constructor, "ar_option", &new_options.ar_option); @@ -123,7 +142,7 @@ public: constructor.BindFunc("options_changed", [](Rml::Variant& out) { - out = (cur_options != new_options); + out = (ultramodern::get_graphics_config() != new_options); }); graphics_model_handle = constructor.GetModelHandle(); @@ -138,11 +157,11 @@ public: constructor.BindFunc("input_count", [](Rml::Variant& out) { out = recomp::get_num_inputs(); } ); constructor.RegisterTransformFunc("get_input_name", [](const Rml::VariantList& inputs) { - return Rml::Variant{recomp::get_input_name(inputs.at(0).Get())}; + return Rml::Variant{recomp::get_input_name(static_cast(inputs.at(0).Get()))}; }); constructor.RegisterTransformFunc("get_input_enum_name", [](const Rml::VariantList& inputs) { - return Rml::Variant{recomp::get_input_enum_name(inputs.at(0).Get())}; + return Rml::Variant{recomp::get_input_enum_name(static_cast(inputs.at(0).Get()))}; }); constructor.BindEventCallback("set_input_binding", @@ -156,9 +175,9 @@ public: constructor.BindEventCallback("clear_input_bindings", [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { - size_t input_index = inputs.at(0).Get(); + recomp::GameInput input = static_cast(inputs.at(0).Get()); for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) { - recomp::set_input_binding(input_index, binding_index, cur_device, recomp::InputField{}); + recomp::set_input_binding(input, binding_index, cur_device, recomp::InputField{}); } model_handle.DirtyVariable("inputs"); }); @@ -193,16 +212,16 @@ public: virtual int Size(void* ptr) override { return recomp::bindings_per_input; } virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override { - uintptr_t input_index = (uintptr_t)ptr; - return Rml::DataVariable{&input_field_definition_instance, & recomp::get_input_binding(input_index, address.index, recomp::InputDevice::Controller)}; + recomp::GameInput input = static_cast((uintptr_t)ptr); + return Rml::DataVariable{&input_field_definition_instance, &recomp::get_input_binding(input, address.index, recomp::InputDevice::Controller)}; } }; - // Static instance of the InputField array variable definition to a fixed pointer to return to RmlUi. + // Static instance of the InputField array variable definition to have a fixed pointer to return to RmlUi. static BindingContainerVariableDefinition binding_container_var_instance{}; // Rml variable definition for an array of an array of InputField values (e.g. all the bindings for all inputs). - struct InputContainerVariableDefinition : public Rml::VariableDefinition { - InputContainerVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Array) {} + struct BindingArrayContainerVariableDefinition : public Rml::VariableDefinition { + BindingArrayContainerVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Array) {} virtual bool Get(void* ptr, Rml::Variant& variant) override { return false; } virtual bool Set(void* ptr, const Rml::Variant& variant) override { return false; } @@ -214,6 +233,30 @@ public: } }; + // Static instance of the BindingArrayContainerVariableDefinition variable definition to have a fixed pointer to return to RmlUi. + static BindingArrayContainerVariableDefinition binding_array_var_instance{}; + + struct InputContainerVariableDefinition : public Rml::VariableDefinition { + InputContainerVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Struct) {} + + virtual bool Get(void* ptr, Rml::Variant& variant) override { return true; } + virtual bool Set(void* ptr, const Rml::Variant& variant) override { return false; } + + virtual int Size(void* ptr) override { return recomp::get_num_inputs(); } + virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override { + if (address.name == "array") { + return Rml::DataVariable(&binding_array_var_instance, nullptr); + } + else { + recomp::GameInput input = recomp::get_input_from_enum_name(address.name); + if (input != recomp::GameInput::COUNT) { + return Rml::DataVariable(&binding_container_var_instance, (void*)(uintptr_t)input); + } + } + return Rml::DataVariable{}; + } + }; + // Dummy type to associate with the variable definition. struct InputContainer {}; constructor.RegisterCustomDataVariableDefinition(Rml::MakeUnique()); @@ -227,7 +270,7 @@ public: out = "NONE"; } else { - out = recomp::get_input_enum_name(focused_input_index); + out = recomp::get_input_enum_name(static_cast(focused_input_index)); } }); @@ -236,7 +279,7 @@ public: out = "NONE"; } else { - out = recomp::get_input_enum_name(scanned_input_index); + out = recomp::get_input_enum_name(static_cast(scanned_input_index)); } }); diff --git a/ultramodern/config.hpp b/ultramodern/config.hpp index 0f5694a..ba6d135 100644 --- a/ultramodern/config.hpp +++ b/ultramodern/config.hpp @@ -27,7 +27,8 @@ namespace ultramodern { auto operator<=>(const GraphicsConfig& rhs) const = default; }; - void update_graphics_config(const GraphicsConfig& config); + void set_graphics_config(const GraphicsConfig& config); + const GraphicsConfig& get_graphics_config(); }; #endif diff --git a/ultramodern/events.cpp b/ultramodern/events.cpp index a926534..03ea2ac 100644 --- a/ultramodern/events.cpp +++ b/ultramodern/events.cpp @@ -28,7 +28,6 @@ struct SwapBuffersAction { }; struct UpdateConfigAction { - ultramodern::GraphicsConfig config; }; using Action = std::variant; @@ -257,8 +256,15 @@ void task_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_rea } } -void ultramodern::update_graphics_config(const ultramodern::GraphicsConfig& config) { - events_context.action_queue.enqueue(UpdateConfigAction{ config }); +static std::atomic cur_config{}; + +void ultramodern::set_graphics_config(const ultramodern::GraphicsConfig& config) { + cur_config = config; + events_context.action_queue.enqueue(UpdateConfigAction{}); +} + +const ultramodern::GraphicsConfig& ultramodern::get_graphics_config() { + return cur_config; } void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_ready, ultramodern::WindowHandle window_handle) { @@ -267,7 +273,7 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read ultramodern::set_native_thread_name("Gfx Thread"); ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Normal); - ultramodern::GraphicsConfig cur_config{}; + ultramodern::GraphicsConfig old_config; RT64::Application* application = RT64Init(rom, rdram, window_handle); @@ -300,9 +306,10 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read RT64UpdateScreen(swap_action->origin); } else if (const auto* config_action = std::get_if(&action)) { - if (cur_config != config_action->config) { - RT64UpdateConfig(application, cur_config, config_action->config); - cur_config = config_action->config; + ultramodern::GraphicsConfig new_config = cur_config; + if (old_config != new_config) { + RT64UpdateConfig(application, old_config, new_config); + old_config = new_config; } } }