switch ui/input to be using recompfrontend library (unfinished)

This commit is contained in:
thecozies
2025-09-09 13:50:53 -05:00
parent 1b2b40bc6e
commit 8c46312948
120 changed files with 141 additions and 20669 deletions
+18 -42
View File
@@ -2,64 +2,40 @@
#define __BANJO_CONFIG_H__
#include <filesystem>
#include <string>
#include <string_view>
#include "ultramodern/config.hpp"
#include "recomp_input.h"
namespace banjo {
constexpr std::u8string_view program_id = u8"BanjoRecompiled";
constexpr std::string_view program_name = "Banjo: Recompiled";
inline const std::u8string program_id = u8"BanjoRecompiled";
inline const std::string program_name = "Banjo: Recompiled";
namespace configkeys {
namespace general {
inline const std::string camera_invert_mode = "camera_invert_mode";
inline const std::string analog_cam_mode = "analog_cam_mode";
inline const std::string analog_camera_invert_mode = "analog_camera_invert_mode";
}
namespace sound {
inline const std::string bgm_volume = "bgm_volume";
}
}
// TODO: Move loading configs to the runtime once we have a way to allow per-project customization.
void load_config();
void save_config();
bool read_json_with_backups(const std::filesystem::path& path, nlohmann::json& json_out);
bool save_json_with_backups(const std::filesystem::path& path, const nlohmann::json& json_data);
void init_config();
void reset_input_bindings();
void reset_cont_input_bindings(int profile_index);
void reset_kb_input_bindings(int profile_index);
void reset_single_input_binding(int profile_index, recomp::InputDevice device, recomp::GameInput input);
std::filesystem::path get_app_folder_path();
bool get_debug_mode_enabled();
void set_debug_mode_enabled(bool enabled);
enum class CameraInvertMode {
InvertNone,
InvertX,
InvertY,
InvertBoth,
OptionCount
InvertBoth
};
NLOHMANN_JSON_SERIALIZE_ENUM(banjo::CameraInvertMode, {
{banjo::CameraInvertMode::InvertNone, "InvertNone"},
{banjo::CameraInvertMode::InvertX, "InvertX"},
{banjo::CameraInvertMode::InvertY, "InvertY"},
{banjo::CameraInvertMode::InvertBoth, "InvertBoth"}
});
CameraInvertMode get_camera_invert_mode();
void set_camera_invert_mode(CameraInvertMode mode);
CameraInvertMode get_analog_camera_invert_mode();
void set_analog_camera_invert_mode(CameraInvertMode mode);
enum class AnalogCamMode {
On,
Off,
OptionCount
};
NLOHMANN_JSON_SERIALIZE_ENUM(banjo::AnalogCamMode, {
{banjo::AnalogCamMode::On, "On"},
{banjo::AnalogCamMode::Off, "Off"}
});
AnalogCamMode get_analog_cam_mode();
void set_analog_cam_mode(AnalogCamMode mode);
bool get_analog_cam_mode();
void open_quit_game_prompt();
};
-4
View File
@@ -2,10 +2,6 @@
#define __BANJO_SOUND_H__
namespace banjo {
void reset_sound_settings();
void set_main_volume(int volume);
int get_main_volume();
void set_bgm_volume(int volume);
int get_bgm_volume();
}
-317
View File
@@ -1,317 +0,0 @@
#ifndef __RECOMP_INPUT_H__
#define __RECOMP_INPUT_H__
#include <cstdint>
#include <variant>
#include <vector>
#include <type_traits>
#include <span>
#include <string>
#include <string_view>
#include "ultramodern/input.hpp"
#include "json/json.hpp"
#include "SDL.h"
#include "chrono"
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.
// TODO refactor this to allow projects to rename these, or get rid of the readable name and leave that up to individual projects to map.
#define DEFINE_N64_BUTTON_INPUTS() \
DEFINE_INPUT(A, 0x8000, "A") \
DEFINE_INPUT(B, 0x4000, "B") \
DEFINE_INPUT(Z, 0x2000, "Z") \
DEFINE_INPUT(START, 0x1000, "Start") \
DEFINE_INPUT(L, 0x0020, "L") \
DEFINE_INPUT(R, 0x0010, "R") \
DEFINE_INPUT(C_UP, 0x0008, "C Up") \
DEFINE_INPUT(C_LEFT, 0x0002, "C Left") \
DEFINE_INPUT(C_DOWN, 0x0004, "C Down") \
DEFINE_INPUT(C_RIGHT, 0x0001, "C Right") \
DEFINE_INPUT(DPAD_UP, 0x0800, "D Pad Down") \
DEFINE_INPUT(DPAD_RIGHT, 0x0100, "D-Pad Down") \
DEFINE_INPUT(DPAD_DOWN, 0x0400, "D-Pad Down") \
DEFINE_INPUT(DPAD_LEFT, 0x0200, "D-Pad Down")
#define DEFINE_N64_AXIS_INPUTS() \
DEFINE_INPUT(Y_AXIS_POS, 0, "Up") \
DEFINE_INPUT(Y_AXIS_NEG, 0, "Down") \
DEFINE_INPUT(X_AXIS_NEG, 0, "Left") \
DEFINE_INPUT(X_AXIS_POS, 0, "Right") \
#define DEFINE_RECOMP_UI_INPUTS() \
DEFINE_INPUT(TOGGLE_MENU, 0, "Toggle Menu") \
DEFINE_INPUT(ACCEPT_MENU, 0, "Accept (Menu)") \
DEFINE_INPUT(BACK_MENU, 0, "Back (Menu)") \
DEFINE_INPUT(APPLY_MENU, 0, "Apply (Menu)") \
DEFINE_INPUT(TAB_LEFT_MENU, 0, "Tab Left (Menu)") \
DEFINE_INPUT(TAB_RIGHT_MENU, 0, "Tab Right (Menu)")
#define DEFINE_ALL_INPUTS() \
DEFINE_N64_BUTTON_INPUTS() \
DEFINE_N64_AXIS_INPUTS() \
DEFINE_RECOMP_UI_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
// What type of source an input comes from (SDL_Scancode, SDL_GameControllerButton, SDL_GameControllerAxis, SDL_BUTTON, etc.)
enum class InputType {
None = 0, // Using zero for None ensures that default initialized InputFields are unbound.
Keyboard,
Mouse,
ControllerDigital,
ControllerAnalog // Axis input_id values are the SDL value + 1
};
// A single input. Combines the source of the input (see InputType) and a specific key/button/axis.
struct InputField {
InputType input_type;
// Represents a single source input. e.g. A keyboard's shift key, or a controller's R trigger
int32_t input_id;
std::string to_string() const;
auto operator<=>(const InputField& rhs) const = default;
};
void poll_inputs();
float get_input_analog(int controller_num, const InputField& field);
float get_input_analog(int controller_num, const std::span<const recomp::InputField> fields);
bool get_input_digital(int controller_num, const InputField& field);
bool get_input_digital(int controller_num, const std::span<const recomp::InputField> fields);
void get_gyro_deltas(float* x, float* y);
void get_mouse_deltas(float* x, float* y);
void get_right_analog(int controller_num, float* x, float* y);
enum class InputDevice {
Controller,
Keyboard,
COUNT
};
NLOHMANN_JSON_SERIALIZE_ENUM(recomp::InputDevice, {
{ recomp::InputDevice::Controller, "Controller" },
{ recomp::InputDevice::Keyboard, "Keyboard" },
});
void config_menu_set_cont_or_kb(bool cont_interacted);
struct DefaultN64Mappings {
std::vector<InputField> a;
std::vector<InputField> b;
std::vector<InputField> l;
std::vector<InputField> r;
std::vector<InputField> z;
std::vector<InputField> start;
std::vector<InputField> c_left;
std::vector<InputField> c_right;
std::vector<InputField> c_up;
std::vector<InputField> c_down;
std::vector<InputField> dpad_left;
std::vector<InputField> dpad_right;
std::vector<InputField> dpad_up;
std::vector<InputField> dpad_down;
std::vector<InputField> analog_left;
std::vector<InputField> analog_right;
std::vector<InputField> analog_up;
std::vector<InputField> analog_down;
std::vector<InputField> toggle_menu;
std::vector<InputField> accept_menu;
std::vector<InputField> back_menu;
std::vector<InputField> apply_menu;
std::vector<InputField> tab_left_menu;
std::vector<InputField> tab_right_menu;
};
inline const std::vector<InputField>& get_default_mapping_for_input(const DefaultN64Mappings& defaults, const GameInput input) {
static const std::vector<InputField> empty_input_field{};
switch (input) {
case GameInput::A: return defaults.a;
case GameInput::B: return defaults.b;
case GameInput::L: return defaults.l;
case GameInput::R: return defaults.r;
case GameInput::Z: return defaults.z;
case GameInput::START: return defaults.start;
case GameInput::C_LEFT: return defaults.c_left;
case GameInput::C_RIGHT: return defaults.c_right;
case GameInput::C_UP: return defaults.c_up;
case GameInput::C_DOWN: return defaults.c_down;
case GameInput::DPAD_LEFT: return defaults.dpad_left;
case GameInput::DPAD_RIGHT: return defaults.dpad_right;
case GameInput::DPAD_UP: return defaults.dpad_up;
case GameInput::DPAD_DOWN: return defaults.dpad_down;
case GameInput::X_AXIS_NEG: return defaults.analog_left;
case GameInput::X_AXIS_POS: return defaults.analog_right;
case GameInput::Y_AXIS_POS: return defaults.analog_up;
case GameInput::Y_AXIS_NEG: return defaults.analog_down;
case GameInput::TOGGLE_MENU: return defaults.toggle_menu;
case GameInput::ACCEPT_MENU: return defaults.accept_menu;
case GameInput::BACK_MENU: return defaults.back_menu;
case GameInput::APPLY_MENU: return defaults.apply_menu;
case GameInput::TAB_LEFT_MENU: return defaults.tab_left_menu;
case GameInput::TAB_RIGHT_MENU: return defaults.tab_right_menu;
default: return empty_input_field;
}
}
extern const DefaultN64Mappings default_n64_keyboard_mappings;
extern const DefaultN64Mappings default_n64_controller_mappings;
constexpr size_t bindings_per_input = 2;
size_t get_num_inputs();
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(int profile_index, GameInput input, size_t binding_index);
void set_input_binding(int profile_index, GameInput input, size_t binding_index, InputField value);
void clear_input_binding(int profile_index, GameInput input);
void reset_input_binding(int profile_index, InputDevice device, GameInput input);
void reset_profile_bindings(int profile_index, recomp::InputDevice device);
int add_input_profile(const std::string &key, const std::string &name, InputDevice device, bool custom);
int get_input_profile_by_key(const std::string &key);
const std::string &get_input_profile_key(int profile_index);
const std::string &get_input_profile_name(int profile_index);
InputDevice get_input_profile_device(int profile_index);
bool is_input_profile_custom(int profile_index);
int get_input_profile_count();
const std::vector<int> get_indices_for_custom_profiles(InputDevice device);
void set_input_profile_for_player(int player_index, int profile_index, InputDevice device);
int get_input_profile_for_player(int player_index, InputDevice device);
struct ControllerGUID {
uint64_t hash;
std::string serial;
int vendor{};
int product{};
int version{};
int crc16{};
};
int add_controller(ControllerGUID guid, int profile_index);
const ControllerGUID &get_controller_guid(int controller_index);
int get_controller_profile_index(int controller_index);
int get_controller_by_guid(ControllerGUID guid);
int get_controller_count();
ControllerGUID get_guid_from_sdl_controller(SDL_GameController* game_controller);
int get_controller_profile_index_from_sdl_controller(SDL_GameController* game_controller);
std::string get_string_from_controller_guid(ControllerGUID guid);
void initialize_input_bindings();
int get_sp_controller_profile_index();
int get_sp_keyboard_profile_index();
int get_mp_keyboard_profile_index(int player_index);
bool get_n64_input(int player_index, uint16_t* buttons_out, float* x_out, float* y_out);
void set_rumble(int player_index, bool);
void update_rumble();
void handle_events();
ultramodern::input::connected_device_info_t get_connected_device_info(int controller_num);
// Rumble strength ranges from 0 to 100.
int get_rumble_strength();
void set_rumble_strength(int strength);
// Gyro and mouse sensitivities range from 0 to 100.
int get_gyro_sensitivity();
int get_mouse_sensitivity();
int get_joystick_deadzone();
void set_gyro_sensitivity(int strength);
void set_mouse_sensitivity(int strength);
void set_joystick_deadzone(int strength);
void apply_joystick_deadzone(float x_in, float y_in, float* x_out, float* y_out);
void set_right_analog_suppressed(bool suppressed);
enum class BackgroundInputMode {
On,
Off,
OptionCount
};
NLOHMANN_JSON_SERIALIZE_ENUM(recomp::BackgroundInputMode, {
{recomp::BackgroundInputMode::On, "On"},
{recomp::BackgroundInputMode::Off, "Off"}
});
BackgroundInputMode get_background_input_mode();
void set_background_input_mode(BackgroundInputMode mode);
bool get_single_controller_mode();
void set_single_controller_mode(bool single_controller);
bool game_input_disabled();
bool all_input_disabled();
}
namespace recompinput {
struct BindingState {
bool active = false;
// Designates when binding has been cancelled or completed and the event queue should be purged/ignored.
bool skip_events = false;
int player_index = -1;
recomp::GameInput game_input = recomp::GameInput::COUNT;
int binding_index = -1;
recomp::InputField new_binding = {};
recomp::InputDevice device = recomp::InputDevice::COUNT;
};
void start_scanning_for_binding(int player_index, recomp::GameInput game_input, int binding_index, recomp::InputDevice device);
void stop_scanning_for_binding();
bool is_binding();
BindingState& get_binding_state();
int get_num_players();
struct AssignedPlayer {
SDL_GameController* controller = nullptr;
bool is_assigned = false;
bool keyboard_enabled = false;
std::chrono::high_resolution_clock::duration last_button_press_timestamp = std::chrono::high_resolution_clock::duration::zero();
AssignedPlayer() : controller(nullptr), is_assigned(false), keyboard_enabled(false), last_button_press_timestamp(std::chrono::high_resolution_clock::duration::zero()) {};
};
constexpr size_t temp_max_players = 4;
struct PlayerAssignmentState {
bool is_assigning = false;
int player_index = 0;
std::array<AssignedPlayer, temp_max_players> temp_assigned_players;
PlayerAssignmentState() : is_assigning(false), player_index(0), temp_assigned_players{} {};
};
AssignedPlayer& get_assigned_player(int player_index, bool temp_player = false);
bool get_player_is_assigned(int player_index);
recomp::InputDevice get_assigned_player_input_device(int player_index);
void start_player_assignment(void);
void stop_player_assignment(void);
void stop_player_assignment_and_close_modal(void);
void commit_player_assignment(void);
bool is_player_assignment_active();
std::chrono::steady_clock::duration get_player_time_since_last_button_press(int player_index);
bool does_player_have_controller(int player_index);
}
#endif
-175
View File
@@ -1,175 +0,0 @@
#ifndef __RECOMP_UI__
#define __RECOMP_UI__
#include <memory>
#include <string>
#include <string_view>
#include <list>
// TODO move this file into src/ui
#include "SDL.h"
#include "RmlUi/Core.h"
#include "recomp_input.h"
#include "../src/ui/util/hsv.h"
#include "../src/ui/util/bem.h"
#include "../src/ui/elements/ui_button.h"
#include "../src/ui/elements/ui_theme.h"
#include "../src/ui/elements/ui_types.h"
#include "../src/ui/core/ui_context.h"
namespace Rml {
class ElementDocument;
class EventListenerInstancer;
class Context;
class Event;
}
namespace recompui {
class UiEventListenerInstancer;
// TODO remove this once the UI has been ported over to the new system.
class MenuController {
public:
virtual ~MenuController() {}
virtual void load_document() = 0;
virtual void register_events(UiEventListenerInstancer& listener) = 0;
virtual void make_bindings(Rml::Context* context) = 0;
};
std::unique_ptr<MenuController> create_launcher_menu();
std::unique_ptr<MenuController> create_config_menu();
using event_handler_t = void(const std::string& param, Rml::Event&);
void queue_event(const SDL_Event& event);
bool try_deque_event(SDL_Event& out);
std::unique_ptr<UiEventListenerInstancer> make_event_listener_instancer();
void register_event(UiEventListenerInstancer& listener, const std::string& name, event_handler_t* handler);
void show_context(ContextId context, std::string_view param);
void hide_context(ContextId context);
void hide_all_contexts();
bool is_context_shown(ContextId context);
bool is_context_capturing_input();
bool is_context_capturing_mouse();
bool is_any_context_shown();
ContextId try_close_current_context();
ContextId get_launcher_context_id();
ContextId get_config_context_id();
ContextId get_config_sub_menu_context_id();
enum class ConfigTabId {
General,
Controls,
Graphics,
Sound,
Mods,
Debug,
};
void set_config_tab(ConfigTabId tab);
int config_tab_to_index(ConfigTabId tab);
Rml::ElementTabSet* get_config_tabset();
Rml::Element* get_mod_tab();
void set_config_tabset_mod_nav();
void focus_mod_configure_button();
void init_styling(const std::filesystem::path& rcss_file);
void init_prompt_context();
void open_choice_prompt(
const std::string& header_text,
const std::string& content_text,
const std::string& confirm_label_text,
const std::string& cancel_label_text,
std::function<void()> confirm_action,
std::function<void()> cancel_action,
ButtonStyle confirm_variant = ButtonStyle::Success,
ButtonStyle cancel_variant = ButtonStyle::Danger,
bool focus_on_cancel = true,
const std::string& return_element_id = ""
);
void open_info_prompt(
const std::string& header_text,
const std::string& content_text,
const std::string& okay_label_text,
std::function<void()> okay_action,
ButtonStyle okay_variant = ButtonStyle::Danger,
const std::string& return_element_id = ""
);
void open_notification(
const std::string& header_text,
const std::string& content_text,
const std::string& return_element_id = ""
);
void close_prompt();
bool is_prompt_open();
void update_mod_list(bool scan_mods = true);
void process_game_started();
void apply_color_hack();
void get_window_size(int& width, int& height);
void set_cursor_visible(bool visible);
void update_supported_options();
void toggle_fullscreen();
bool get_cont_active(void);
void set_cont_active(bool active);
void activate_mouse();
void message_box(const char* msg);
void set_render_hooks();
Rml::ElementPtr create_custom_element(Rml::Element* parent, std::string tag);
Rml::ElementDocument* load_document(const std::filesystem::path& path);
Rml::ElementDocument* create_empty_document();
Rml::Element* get_child_by_tag(Rml::Element* parent, const std::string& tag);
void queue_image_from_bytes_rgba32(const std::string &src, const std::vector<char> &bytes, uint32_t width, uint32_t height);
void queue_image_from_bytes_file(const std::string &src, const std::vector<char> &bytes);
void release_image(const std::string &src);
void drop_files(const std::list<std::filesystem::path> &file_list);
void report_removed_element(Rml::Element* element);
namespace menu_action_mapping {
struct key_map {
// End result menu action
const MenuAction action;
// Mapped input from controller
const recomp::GameInput input;
// SDL key code from controller -> keyboard
const int sdl;
// RML key identifier passed to rmlui
const Rml::Input::KeyIdentifier rml;
};
static const key_map accept = { MenuAction::Accept, recomp::GameInput::ACCEPT_MENU, SDLK_RETURN, Rml::Input::KI_RETURN };
static const key_map apply = { MenuAction::Apply, recomp::GameInput::APPLY_MENU, SDLK_f, Rml::Input::KI_F };
static const key_map back = { MenuAction::Back, recomp::GameInput::BACK_MENU, SDLK_F15, Rml::Input::KI_F15 };
static const key_map toggle = { MenuAction::Toggle, recomp::GameInput::TOGGLE_MENU, SDLK_ESCAPE, Rml::Input::KI_ESCAPE };
static const key_map tab_left = { MenuAction::TabLeft, recomp::GameInput::TAB_LEFT_MENU, SDLK_F16, Rml::Input::KI_F16 };
static const key_map tab_right = { MenuAction::TabRight, recomp::GameInput::TAB_RIGHT_MENU, SDLK_F17, Rml::Input::KI_F17 };
static const std::unordered_map<Rml::Input::KeyIdentifier, key_map> rml_key_to_action {
{ accept.rml, accept },
{ apply.rml, apply },
{ back.rml, back },
{ toggle.rml, toggle },
{ tab_left.rml, tab_left },
{ tab_right.rml, tab_right }
};
MenuAction menu_action_from_rml_key(const Rml::Input::KeyIdentifier& key);
};
// Constant that represents an input that doesn't have a promptfont icon associated with it.
extern const std::string unknown_input;
}
#endif