Implemented rebinding inputs from UI (controller only so far) and built minimal UI for doing so
This commit is contained in:
parent
457bc878e9
commit
6ebc7256eb
|
@ -123,7 +123,14 @@
|
||||||
<div>Controls</div>
|
<div>Controls</div>
|
||||||
<div class="tab__indicator"></div>
|
<div class="tab__indicator"></div>
|
||||||
</tab>
|
</tab>
|
||||||
<panel class="config" >
|
<panel class="config" data-model="controls_model">
|
||||||
|
<form class="config__form" style="display:block">
|
||||||
|
<div id="input_row" data-for="input_bindings, i : inputs" style="display:block">
|
||||||
|
<label class="config-option__title" style="display:inline-block;width:250dp">{{get_input_name(i)}}</label>
|
||||||
|
<button data-for="cur_binding, j : input_bindings" data-event-click="set_input_binding(i,j)" class="button button--secondary label-md" style="font-family:promptfont;font-size:40dp;display:inline-block;height:50dp;width:150dp;padding:0dp;text-align:center">{{cur_binding}}</button>
|
||||||
|
<button data-event-click="clear_input_bindings(i)" class="button button--secondary label-md" style="display:inline-block;height:50dp;width:150dp;padding:0dp;text-align:center">Delete</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</panel>
|
</panel>
|
||||||
<tab class="tab">
|
<tab class="tab">
|
||||||
<div>Sound</div>
|
<div>Sound</div>
|
||||||
|
|
|
@ -6,11 +6,14 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace recomp {
|
namespace recomp {
|
||||||
struct InputField {
|
struct InputField {
|
||||||
uint32_t device_type;
|
uint32_t input_type;
|
||||||
int32_t input_id;
|
int32_t input_id;
|
||||||
|
std::string to_string() const;
|
||||||
|
auto operator<=>(const InputField& rhs) const = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
void poll_inputs();
|
void poll_inputs();
|
||||||
|
@ -19,6 +22,16 @@ namespace recomp {
|
||||||
bool get_input_digital(const InputField& field);
|
bool get_input_digital(const InputField& field);
|
||||||
bool get_input_digital(const std::span<const recomp::InputField> fields);
|
bool get_input_digital(const std::span<const recomp::InputField> fields);
|
||||||
|
|
||||||
|
enum class InputDevice {
|
||||||
|
Controller,
|
||||||
|
Keyboard,
|
||||||
|
COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
void start_scanning_input(InputDevice device);
|
||||||
|
void finish_scanning_input(InputField scanned_field);
|
||||||
|
InputField get_scanned_input();
|
||||||
|
|
||||||
struct DefaultN64Mappings {
|
struct DefaultN64Mappings {
|
||||||
std::vector<InputField> a;
|
std::vector<InputField> a;
|
||||||
std::vector<InputField> b;
|
std::vector<InputField> b;
|
||||||
|
@ -46,13 +59,20 @@ namespace recomp {
|
||||||
extern const DefaultN64Mappings default_n64_keyboard_mappings;
|
extern const DefaultN64Mappings default_n64_keyboard_mappings;
|
||||||
extern const DefaultN64Mappings default_n64_controller_mappings;
|
extern const DefaultN64Mappings default_n64_controller_mappings;
|
||||||
|
|
||||||
|
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.
|
// Loads the user's saved controller mapping if one exists, loads the default mappings if no saved mapping exists.
|
||||||
void init_control_mappings();
|
void init_control_mappings();
|
||||||
|
size_t get_num_inputs();
|
||||||
|
const std::vector<std::string>& get_input_names();
|
||||||
|
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);
|
||||||
|
|
||||||
void get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out);
|
void get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out);
|
||||||
void handle_events();
|
void handle_events();
|
||||||
|
|
||||||
bool game_input_disabled();
|
bool game_input_disabled();
|
||||||
|
bool all_input_disabled();
|
||||||
|
|
||||||
// TODO move these
|
// TODO move these
|
||||||
void quicksave_save();
|
void quicksave_save();
|
||||||
|
|
|
@ -5,34 +5,38 @@
|
||||||
#include "../ultramodern/ultramodern.hpp"
|
#include "../ultramodern/ultramodern.hpp"
|
||||||
#include "../patches/input.h"
|
#include "../patches/input.h"
|
||||||
|
|
||||||
// x-macros to build input enums and arrays. First parameter is the name, second parameter is the bit field for the input (or 0 if there is no associated one)
|
// 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 DEFINE_N64_BUTTON_INPUTS() \
|
||||||
DEFINE_INPUT(A, 0x8000) \
|
DEFINE_INPUT(A, 0x8000, "[A Button]") \
|
||||||
DEFINE_INPUT(B, 0x4000) \
|
DEFINE_INPUT(B, 0x4000, "[B Button]") \
|
||||||
DEFINE_INPUT(Z, 0x2000) \
|
DEFINE_INPUT(Z, 0x2000, "[Z Button]") \
|
||||||
DEFINE_INPUT(START, 0x1000) \
|
DEFINE_INPUT(START, 0x1000, "[Start Button]") \
|
||||||
DEFINE_INPUT(DPAD_UP, 0x0800) \
|
DEFINE_INPUT(DPAD_UP, 0x0800, "[Dpad Up]") \
|
||||||
DEFINE_INPUT(DPAD_DOWN, 0x0400) \
|
DEFINE_INPUT(DPAD_DOWN, 0x0400, "[Dpad Down]") \
|
||||||
DEFINE_INPUT(DPAD_LEFT, 0x0200) \
|
DEFINE_INPUT(DPAD_LEFT, 0x0200, "[Dpad Left]") \
|
||||||
DEFINE_INPUT(DPAD_RIGHT, 0x0100) \
|
DEFINE_INPUT(DPAD_RIGHT, 0x0100, "[Dpad Right]") \
|
||||||
DEFINE_INPUT(L, 0x0020) \
|
DEFINE_INPUT(L, 0x0020, "[L Button]") \
|
||||||
DEFINE_INPUT(R, 0x0010) \
|
DEFINE_INPUT(R, 0x0010, "[R Button]") \
|
||||||
DEFINE_INPUT(C_UP, 0x0008) \
|
DEFINE_INPUT(C_UP, 0x0008, "[C Up]") \
|
||||||
DEFINE_INPUT(C_DOWN, 0x0004) \
|
DEFINE_INPUT(C_DOWN, 0x0004, "[C Down]") \
|
||||||
DEFINE_INPUT(C_LEFT, 0x0002) \
|
DEFINE_INPUT(C_LEFT, 0x0002, "[C Left]") \
|
||||||
DEFINE_INPUT(C_RIGHT, 0x0001)
|
DEFINE_INPUT(C_RIGHT, 0x0001, "[C Right]")
|
||||||
|
|
||||||
#define DEFINE_N64_AXIS_INPUTS() \
|
#define DEFINE_N64_AXIS_INPUTS() \
|
||||||
DEFINE_INPUT(X_AXIS_NEG, 0) \
|
DEFINE_INPUT(X_AXIS_NEG, 0, "[Analog Left]") \
|
||||||
DEFINE_INPUT(X_AXIS_POS, 0) \
|
DEFINE_INPUT(X_AXIS_POS, 0, "[Analog Right]") \
|
||||||
DEFINE_INPUT(Y_AXIS_NEG, 0) \
|
DEFINE_INPUT(Y_AXIS_NEG, 0, "[Analog Down]") \
|
||||||
DEFINE_INPUT(Y_AXIS_POS, 0) \
|
DEFINE_INPUT(Y_AXIS_POS, 0, "[Analog Up]") \
|
||||||
|
|
||||||
|
#define DEFINE_ALL_INPUTS() \
|
||||||
|
DEFINE_N64_BUTTON_INPUTS() \
|
||||||
|
DEFINE_N64_AXIS_INPUTS()
|
||||||
|
|
||||||
// Make the input enum.
|
// Make the input enum.
|
||||||
#define DEFINE_INPUT(name, value) name,
|
#define DEFINE_INPUT(name, value, readable) name,
|
||||||
enum class GameInput {
|
enum class GameInput {
|
||||||
DEFINE_N64_BUTTON_INPUTS()
|
DEFINE_ALL_INPUTS()
|
||||||
DEFINE_N64_AXIS_INPUTS()
|
|
||||||
|
|
||||||
COUNT,
|
COUNT,
|
||||||
N64_BUTTON_START = A,
|
N64_BUTTON_START = A,
|
||||||
|
@ -43,22 +47,30 @@ enum class GameInput {
|
||||||
#undef DEFINE_INPUT
|
#undef DEFINE_INPUT
|
||||||
|
|
||||||
// Arrays that hold the mappings for every input for keyboard and controller respectively.
|
// Arrays that hold the mappings for every input for keyboard and controller respectively.
|
||||||
using input_mapping_array = std::array<std::vector<recomp::InputField>, (size_t)GameInput::COUNT>;
|
using input_mapping = std::array<recomp::InputField, recomp::bindings_per_input>;
|
||||||
static std::array<std::vector<recomp::InputField>, (size_t)GameInput::COUNT> keyboard_input_mappings{};
|
using input_mapping_array = std::array<input_mapping, (size_t)GameInput::COUNT>;
|
||||||
static std::array<std::vector<recomp::InputField>, (size_t)GameInput::COUNT> controller_input_mappings{};
|
static input_mapping_array keyboard_input_mappings{};
|
||||||
|
static input_mapping_array controller_input_mappings{};
|
||||||
|
|
||||||
// Make the button value array, which maps a button index to its bit field.
|
// Make the button value array, which maps a button index to its bit field.
|
||||||
#define DEFINE_INPUT(name, value) uint16_t(value##u),
|
#define DEFINE_INPUT(name, value, readable) uint16_t(value##u),
|
||||||
static const std::array n64_button_values = {
|
static const std::array n64_button_values = {
|
||||||
DEFINE_N64_BUTTON_INPUTS()
|
DEFINE_N64_BUTTON_INPUTS()
|
||||||
};
|
};
|
||||||
#undef DEFINE_INPUT
|
#undef DEFINE_INPUT
|
||||||
|
|
||||||
|
// Make the input name array.
|
||||||
|
#define DEFINE_INPUT(name, value, readable) readable,
|
||||||
|
static const std::vector<std::string> input_names = {
|
||||||
|
DEFINE_ALL_INPUTS()
|
||||||
|
};
|
||||||
|
|
||||||
void recomp::init_control_mappings() {
|
void recomp::init_control_mappings() {
|
||||||
// TODO load from a file if one exists.
|
// TODO load from a file if one exists.
|
||||||
|
|
||||||
auto assign_mapping = [](input_mapping_array& mapping, GameInput input, const std::vector<recomp::InputField>& value) {
|
auto assign_mapping = [](input_mapping_array& mapping, GameInput input, const std::vector<recomp::InputField>& value) {
|
||||||
mapping[(size_t)input] = 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) {
|
auto assign_all_mappings = [&](input_mapping_array& mapping, const recomp::DefaultN64Mappings& values) {
|
||||||
|
@ -88,6 +100,37 @@ void recomp::init_control_mappings() {
|
||||||
assign_all_mappings(controller_input_mappings, recomp::default_n64_controller_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::vector<std::string>& recomp::get_input_names() {
|
||||||
|
return input_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (binding_index < cur_input_mapping.size()) {
|
||||||
|
return cur_input_mapping[binding_index];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
static recomp::InputField dummy_field = {};
|
||||||
|
return dummy_field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void recomp::set_input_binding(size_t input_index, 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);
|
||||||
|
|
||||||
|
if (binding_index < cur_input_mapping.size()) {
|
||||||
|
cur_input_mapping[binding_index] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void recomp::get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out) {
|
void recomp::get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out) {
|
||||||
uint16_t cur_buttons = 0;
|
uint16_t cur_buttons = 0;
|
||||||
float cur_x = 0.0f;
|
float cur_x = 0.0f;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
#include "rt64_layer.h"
|
#include "rt64_layer.h"
|
||||||
|
|
||||||
|
constexpr float axis_threshold = 0.5f;
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
const Uint8* keys = nullptr;
|
const Uint8* keys = nullptr;
|
||||||
|
@ -16,21 +17,57 @@ static struct {
|
||||||
std::vector<SDL_GameController*> cur_controllers{};
|
std::vector<SDL_GameController*> cur_controllers{};
|
||||||
} InputState;
|
} InputState;
|
||||||
|
|
||||||
|
std::atomic<recomp::InputDevice> scanning_device = recomp::InputDevice::COUNT;
|
||||||
|
std::atomic<recomp::InputField> scanned_input;
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
void set_scanned_input(recomp::InputField value) {
|
||||||
|
scanning_device.store(recomp::InputDevice::COUNT);
|
||||||
|
scanned_input.store(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
recomp::InputField recomp::get_scanned_input() {
|
||||||
|
recomp::InputField ret = scanned_input.load();
|
||||||
|
scanned_input.store({});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void recomp::start_scanning_input(recomp::InputDevice device) {
|
||||||
|
scanned_input.store({});
|
||||||
|
scanning_device.store(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
void queue_if_enabled(SDL_Event* event) {
|
||||||
|
if (!recomp::all_input_disabled()) {
|
||||||
|
recomp::queue_event(*event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
case SDL_EventType::SDL_KEYDOWN:
|
case SDL_EventType::SDL_KEYDOWN:
|
||||||
{
|
{
|
||||||
SDL_KeyboardEvent* keyevent = (SDL_KeyboardEvent*)event;
|
SDL_KeyboardEvent* keyevent = &event->key;
|
||||||
|
|
||||||
if (keyevent->keysym.scancode == SDL_Scancode::SDL_SCANCODE_RETURN && (keyevent->keysym.mod & SDL_Keymod::KMOD_ALT)) {
|
if (keyevent->keysym.scancode == SDL_Scancode::SDL_SCANCODE_RETURN && (keyevent->keysym.mod & SDL_Keymod::KMOD_ALT)) {
|
||||||
RT64ChangeWindow();
|
RT64ChangeWindow();
|
||||||
}
|
}
|
||||||
|
if (scanning_device == recomp::InputDevice::Keyboard) {
|
||||||
|
set_scanned_input({(uint32_t)InputType::Keyboard, keyevent->keysym.scancode});
|
||||||
}
|
}
|
||||||
recomp::queue_event(*event);
|
}
|
||||||
|
queue_if_enabled(event);
|
||||||
break;
|
break;
|
||||||
case SDL_EventType::SDL_CONTROLLERDEVICEADDED:
|
case SDL_EventType::SDL_CONTROLLERDEVICEADDED:
|
||||||
{
|
{
|
||||||
SDL_ControllerDeviceEvent* controller_event = (SDL_ControllerDeviceEvent*)event;
|
SDL_ControllerDeviceEvent* controller_event = &event->cdevice;
|
||||||
SDL_GameController* controller = SDL_GameControllerOpen(controller_event->which);
|
SDL_GameController* controller = SDL_GameControllerOpen(controller_event->which);
|
||||||
printf("Controller added: %d\n", controller_event->which);
|
printf("Controller added: %d\n", controller_event->which);
|
||||||
if (controller != nullptr) {
|
if (controller != nullptr) {
|
||||||
|
@ -41,7 +78,7 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||||
break;
|
break;
|
||||||
case SDL_EventType::SDL_CONTROLLERDEVICEREMOVED:
|
case SDL_EventType::SDL_CONTROLLERDEVICEREMOVED:
|
||||||
{
|
{
|
||||||
SDL_ControllerDeviceEvent* controller_event = (SDL_ControllerDeviceEvent*)event;
|
SDL_ControllerDeviceEvent* controller_event = &event->cdevice;
|
||||||
printf("Controller removed: %d\n", controller_event->which);
|
printf("Controller removed: %d\n", controller_event->which);
|
||||||
std::erase(InputState.controller_ids, controller_event->which);
|
std::erase(InputState.controller_ids, controller_event->which);
|
||||||
}
|
}
|
||||||
|
@ -51,13 +88,33 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||||
return true;
|
return true;
|
||||||
case SDL_EventType::SDL_MOUSEWHEEL:
|
case SDL_EventType::SDL_MOUSEWHEEL:
|
||||||
{
|
{
|
||||||
SDL_MouseWheelEvent* wheel_event = (SDL_MouseWheelEvent*)event;
|
SDL_MouseWheelEvent* wheel_event = &event->wheel;
|
||||||
InputState.mouse_wheel_pos.fetch_add(wheel_event->y * (wheel_event->direction == SDL_MOUSEWHEEL_FLIPPED ? -1 : 1));
|
InputState.mouse_wheel_pos.fetch_add(wheel_event->y * (wheel_event->direction == SDL_MOUSEWHEEL_FLIPPED ? -1 : 1));
|
||||||
}
|
}
|
||||||
recomp::queue_event(*event);
|
queue_if_enabled(event);
|
||||||
|
break;
|
||||||
|
case SDL_EventType::SDL_CONTROLLERBUTTONDOWN:
|
||||||
|
if (scanning_device == recomp::InputDevice::Controller) {
|
||||||
|
SDL_ControllerButtonEvent* button_event = &event->cbutton;
|
||||||
|
set_scanned_input({(uint32_t)InputType::ControllerDigital, button_event->button});
|
||||||
|
}
|
||||||
|
queue_if_enabled(event);
|
||||||
|
break;
|
||||||
|
case SDL_EventType::SDL_CONTROLLERAXISMOTION:
|
||||||
|
if (scanning_device == recomp::InputDevice::Controller) {
|
||||||
|
SDL_ControllerAxisEvent* axis_event = &event->caxis;
|
||||||
|
float axis_value = axis_event->value * (1/32768.0f);
|
||||||
|
if (axis_value > axis_threshold) {
|
||||||
|
set_scanned_input({(uint32_t)InputType::ControllerAnalog, axis_event->axis + 1});
|
||||||
|
}
|
||||||
|
else if (axis_value < -axis_threshold) {
|
||||||
|
set_scanned_input({(uint32_t)InputType::ControllerAnalog, -axis_event->axis - 1});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queue_if_enabled(event);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
recomp::queue_event(*event);
|
queue_if_enabled(event);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -71,13 +128,6 @@ void recomp::handle_events() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class DeviceType {
|
|
||||||
Keyboard,
|
|
||||||
Mouse,
|
|
||||||
ControllerDigital,
|
|
||||||
ControllerAnalog // Axis input_id values are the SDL value + 1
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_SOUTH = SDL_CONTROLLER_BUTTON_A;
|
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_SOUTH = SDL_CONTROLLER_BUTTON_A;
|
||||||
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_EAST = SDL_CONTROLLER_BUTTON_B;
|
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_EAST = SDL_CONTROLLER_BUTTON_B;
|
||||||
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_WEST = SDL_CONTROLLER_BUTTON_X;
|
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_WEST = SDL_CONTROLLER_BUTTON_X;
|
||||||
|
@ -85,117 +135,117 @@ constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_NORTH = SDL_CONTROLLER_
|
||||||
|
|
||||||
const recomp::DefaultN64Mappings recomp::default_n64_keyboard_mappings = {
|
const recomp::DefaultN64Mappings recomp::default_n64_keyboard_mappings = {
|
||||||
.a = {
|
.a = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_SPACE}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_SPACE}
|
||||||
},
|
},
|
||||||
.b = {
|
.b = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_LSHIFT}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_LSHIFT}
|
||||||
},
|
},
|
||||||
.l = {
|
.l = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_E}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_E}
|
||||||
},
|
},
|
||||||
.r = {
|
.r = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_R}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_R}
|
||||||
},
|
},
|
||||||
.z = {
|
.z = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_Q}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_Q}
|
||||||
},
|
},
|
||||||
.start = {
|
.start = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_RETURN}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_RETURN}
|
||||||
},
|
},
|
||||||
.c_left = {
|
.c_left = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_LEFT}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_LEFT}
|
||||||
},
|
},
|
||||||
.c_right = {
|
.c_right = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_RIGHT}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_RIGHT}
|
||||||
},
|
},
|
||||||
.c_up = {
|
.c_up = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_UP}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_UP}
|
||||||
},
|
},
|
||||||
.c_down = {
|
.c_down = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_DOWN}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_DOWN}
|
||||||
},
|
},
|
||||||
.dpad_left = {
|
.dpad_left = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_J}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_J}
|
||||||
},
|
},
|
||||||
.dpad_right = {
|
.dpad_right = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_L}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_L}
|
||||||
},
|
},
|
||||||
.dpad_up = {
|
.dpad_up = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_I}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_I}
|
||||||
},
|
},
|
||||||
.dpad_down = {
|
.dpad_down = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_K}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_K}
|
||||||
},
|
},
|
||||||
.analog_left = {
|
.analog_left = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_A}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_A}
|
||||||
},
|
},
|
||||||
.analog_right = {
|
.analog_right = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_D}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_D}
|
||||||
},
|
},
|
||||||
.analog_up = {
|
.analog_up = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_W}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_W}
|
||||||
},
|
},
|
||||||
.analog_down = {
|
.analog_down = {
|
||||||
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_S}
|
{.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_S}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const recomp::DefaultN64Mappings recomp::default_n64_controller_mappings = {
|
const recomp::DefaultN64Mappings recomp::default_n64_controller_mappings = {
|
||||||
.a = {
|
.a = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_SOUTH},
|
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_SOUTH},
|
||||||
},
|
},
|
||||||
.b = {
|
.b = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_EAST},
|
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_EAST},
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_WEST},
|
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_WEST},
|
||||||
},
|
},
|
||||||
.l = {
|
.l = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
|
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
|
||||||
},
|
},
|
||||||
.r = {
|
.r = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
|
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_TRIGGERRIGHT + 1},
|
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_TRIGGERRIGHT + 1},
|
||||||
},
|
},
|
||||||
.z = {
|
.z = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_TRIGGERLEFT + 1},
|
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_TRIGGERLEFT + 1},
|
||||||
},
|
},
|
||||||
.start = {
|
.start = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_START},
|
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_START},
|
||||||
},
|
},
|
||||||
.c_left = {
|
.c_left = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_RIGHTX + 1)},
|
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_RIGHTX + 1)},
|
||||||
},
|
},
|
||||||
.c_right = {
|
.c_right = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_RIGHTX + 1},
|
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_RIGHTX + 1},
|
||||||
},
|
},
|
||||||
.c_up = {
|
.c_up = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_RIGHTY + 1)},
|
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_RIGHTY + 1)},
|
||||||
},
|
},
|
||||||
.c_down = {
|
.c_down = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_RIGHTY + 1},
|
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_RIGHTY + 1},
|
||||||
},
|
},
|
||||||
.dpad_left = {
|
.dpad_left = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_LEFT},
|
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_LEFT},
|
||||||
},
|
},
|
||||||
.dpad_right = {
|
.dpad_right = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
|
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
|
||||||
},
|
},
|
||||||
.dpad_up = {
|
.dpad_up = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_UP},
|
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_UP},
|
||||||
},
|
},
|
||||||
.dpad_down = {
|
.dpad_down = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_DOWN},
|
{.input_type = (uint32_t)InputType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_DOWN},
|
||||||
},
|
},
|
||||||
.analog_left = {
|
.analog_left = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_LEFTX + 1)},
|
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_LEFTX + 1)},
|
||||||
},
|
},
|
||||||
.analog_right = {
|
.analog_right = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_LEFTX + 1},
|
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_LEFTX + 1},
|
||||||
},
|
},
|
||||||
.analog_up = {
|
.analog_up = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_LEFTY + 1)},
|
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_LEFTY + 1)},
|
||||||
},
|
},
|
||||||
.analog_down = {
|
.analog_down = {
|
||||||
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_LEFTY + 1},
|
{.input_type = (uint32_t)InputType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_LEFTY + 1},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -264,27 +314,21 @@ float controller_axis_state(int32_t input_id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
float recomp::get_input_analog(const recomp::InputField& field) {
|
float recomp::get_input_analog(const recomp::InputField& field) {
|
||||||
switch ((DeviceType)field.device_type) {
|
switch ((InputType)field.input_type) {
|
||||||
case DeviceType::Keyboard:
|
case InputType::Keyboard:
|
||||||
{
|
|
||||||
if (InputState.keys && field.input_id >= 0 && field.input_id < InputState.numkeys) {
|
if (InputState.keys && field.input_id >= 0 && field.input_id < InputState.numkeys) {
|
||||||
return InputState.keys[field.input_id] ? 1.0f : 0.0f;
|
return InputState.keys[field.input_id] ? 1.0f : 0.0f;
|
||||||
}
|
}
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
case InputType::ControllerDigital:
|
||||||
case DeviceType::ControllerDigital:
|
|
||||||
{
|
|
||||||
return controller_button_state(field.input_id) ? 1.0f : 0.0f;
|
return controller_button_state(field.input_id) ? 1.0f : 0.0f;
|
||||||
}
|
case InputType::ControllerAnalog:
|
||||||
case DeviceType::ControllerAnalog:
|
|
||||||
{
|
|
||||||
return controller_axis_state(field.input_id);
|
return controller_axis_state(field.input_id);
|
||||||
}
|
case InputType::Mouse:
|
||||||
case DeviceType::Mouse:
|
|
||||||
{
|
|
||||||
// TODO mouse support
|
// TODO mouse support
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
case InputType::None:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,28 +341,22 @@ float recomp::get_input_analog(const std::span<const recomp::InputField> fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool recomp::get_input_digital(const recomp::InputField& field) {
|
bool recomp::get_input_digital(const recomp::InputField& field) {
|
||||||
switch ((DeviceType)field.device_type) {
|
switch ((InputType)field.input_type) {
|
||||||
case DeviceType::Keyboard:
|
case InputType::Keyboard:
|
||||||
{
|
|
||||||
if (InputState.keys && field.input_id >= 0 && field.input_id < InputState.numkeys) {
|
if (InputState.keys && field.input_id >= 0 && field.input_id < InputState.numkeys) {
|
||||||
return InputState.keys[field.input_id] != 0;
|
return InputState.keys[field.input_id] != 0;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
case InputType::ControllerDigital:
|
||||||
case DeviceType::ControllerDigital:
|
|
||||||
{
|
|
||||||
return controller_button_state(field.input_id);
|
return controller_button_state(field.input_id);
|
||||||
}
|
case InputType::ControllerAnalog:
|
||||||
case DeviceType::ControllerAnalog:
|
|
||||||
{
|
|
||||||
// TODO adjustable threshold
|
// TODO adjustable threshold
|
||||||
return controller_axis_state(field.input_id) >= 0.5f;
|
return controller_axis_state(field.input_id) >= axis_threshold;
|
||||||
}
|
case InputType::Mouse:
|
||||||
case DeviceType::Mouse:
|
|
||||||
{
|
|
||||||
// TODO mouse support
|
// TODO mouse support
|
||||||
return false;
|
return false;
|
||||||
}
|
case InputType::None:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,3 +372,88 @@ bool recomp::game_input_disabled() {
|
||||||
// Disable input if any menu is open.
|
// Disable input if any menu is open.
|
||||||
return recomp::get_current_menu() != recomp::Menu::None;
|
return recomp::get_current_menu() != recomp::Menu::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool recomp::all_input_disabled() {
|
||||||
|
// Disable all input if an input is being polled.
|
||||||
|
return scanning_device != recomp::InputDevice::COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string controller_button_to_string(SDL_GameControllerButton button) {
|
||||||
|
switch (button) {
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_A:
|
||||||
|
return "\u21A7";
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_B:
|
||||||
|
return "\u21A6";
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_X:
|
||||||
|
return "\u21A4";
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_Y:
|
||||||
|
return "\u21A5";
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_BACK:
|
||||||
|
return "\u21FA";
|
||||||
|
// case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_GUIDE:
|
||||||
|
// return "";
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_START:
|
||||||
|
return "\u21FB";
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSTICK:
|
||||||
|
return "\u21BA";
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSTICK:
|
||||||
|
return "\u21BB";
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
|
||||||
|
return "\u2198";
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
|
||||||
|
return "\u2199";
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_UP:
|
||||||
|
return "\u219F";
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_DOWN:
|
||||||
|
return "\u21A1";
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_LEFT:
|
||||||
|
return "\u219E";
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
|
||||||
|
return "\u21A0";
|
||||||
|
// case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_MISC1:
|
||||||
|
// return "";
|
||||||
|
// case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE1:
|
||||||
|
// return "";
|
||||||
|
// case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE2:
|
||||||
|
// return "";
|
||||||
|
// case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE3:
|
||||||
|
// return "";
|
||||||
|
// case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE4:
|
||||||
|
// return "";
|
||||||
|
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_TOUCHPAD:
|
||||||
|
return "\u21E7";
|
||||||
|
}
|
||||||
|
return "Button " + std::to_string(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string controller_axis_to_string(int axis) {
|
||||||
|
bool positive = axis > 0;
|
||||||
|
SDL_GameControllerAxis actual_axis = SDL_GameControllerAxis(abs(axis) - 1);
|
||||||
|
switch (actual_axis) {
|
||||||
|
case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTX:
|
||||||
|
return positive ? "\u21C0" : "\u21BC";
|
||||||
|
case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTY:
|
||||||
|
return positive ? "\u21C2" : "\u21BE";
|
||||||
|
case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX:
|
||||||
|
return positive ? "\u21C1" : "\u21BD";
|
||||||
|
case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY:
|
||||||
|
return positive ? "\u21C3" : "\u21BF";
|
||||||
|
case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERLEFT:
|
||||||
|
return positive ? "\u219A" : "\u21DC";
|
||||||
|
case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
|
||||||
|
return positive ? "\u219B" : "\u21DD";
|
||||||
|
}
|
||||||
|
return "Axis " + std::to_string(actual_axis) + (positive ? '+' : '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string recomp::InputField::to_string() const {
|
||||||
|
switch ((InputType)input_type) {
|
||||||
|
case InputType::None:
|
||||||
|
return "";
|
||||||
|
case InputType::ControllerDigital:
|
||||||
|
return controller_button_to_string((SDL_GameControllerButton)input_id);
|
||||||
|
case InputType::ControllerAnalog:
|
||||||
|
return controller_axis_to_string(input_id);
|
||||||
|
}
|
||||||
|
return std::to_string(input_type) + "," + std::to_string(input_id);
|
||||||
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ ultramodern::gfx_callbacks_t::gfx_data_t create_gfx() {
|
||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
|
|
||||||
ultramodern::WindowHandle create_window(ultramodern::gfx_callbacks_t::gfx_data_t) {
|
ultramodern::WindowHandle create_window(ultramodern::gfx_callbacks_t::gfx_data_t) {
|
||||||
window = SDL_CreateWindow("Recomp", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_RESIZABLE );
|
window = SDL_CreateWindow("Zelda 64: Recompiled", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_RESIZABLE );
|
||||||
|
|
||||||
if (window == nullptr) {
|
if (window == nullptr) {
|
||||||
exit_error("Failed to create window: %s\n", SDL_GetError());
|
exit_error("Failed to create window: %s\n", SDL_GetError());
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
#include "recomp_ui.h"
|
#include "recomp_ui.h"
|
||||||
|
#include "recomp_input.h"
|
||||||
#include "../../ultramodern/config.hpp"
|
#include "../../ultramodern/config.hpp"
|
||||||
#include "../../ultramodern/ultramodern.hpp"
|
#include "../../ultramodern/ultramodern.hpp"
|
||||||
#include "RmlUi/Core.h"
|
#include "RmlUi/Core.h"
|
||||||
|
|
||||||
ultramodern::GraphicsConfig cur_options;
|
ultramodern::GraphicsConfig cur_options;
|
||||||
ultramodern::GraphicsConfig new_options;
|
ultramodern::GraphicsConfig new_options;
|
||||||
Rml::DataModelHandle options_handle;
|
Rml::DataModelHandle graphics_model_handle;
|
||||||
|
Rml::DataModelHandle controls_model_handle;
|
||||||
|
// True if controller config menu is open, false if keyboard config menu is open, undefined otherwise
|
||||||
|
bool configuring_controller = false;
|
||||||
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::Resolution, {
|
NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::Resolution, {
|
||||||
{ultramodern::Resolution::Original, "Original"},
|
{ultramodern::Resolution::Original, "Original"},
|
||||||
|
@ -46,10 +50,20 @@ template <typename T>
|
||||||
void bind_option(Rml::DataModelConstructor& constructor, const std::string& name, T* option) {
|
void bind_option(Rml::DataModelConstructor& constructor, const std::string& name, T* option) {
|
||||||
constructor.BindFunc(name,
|
constructor.BindFunc(name,
|
||||||
[option](Rml::Variant& out) { get_option(*option, out); },
|
[option](Rml::Variant& out) { get_option(*option, out); },
|
||||||
[option](const Rml::Variant& in) { set_option(*option, in); options_handle.DirtyVariable("options_changed"); }
|
[option](const Rml::Variant& in) { set_option(*option, in); graphics_model_handle.DirtyVariable("options_changed"); }
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static size_t scanned_binding_index;
|
||||||
|
static size_t scanned_input_index;
|
||||||
|
|
||||||
|
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);
|
||||||
|
controls_model_handle.DirtyVariable("inputs");
|
||||||
|
}
|
||||||
|
|
||||||
class ConfigMenu : public recomp::MenuController {
|
class ConfigMenu : public recomp::MenuController {
|
||||||
public:
|
public:
|
||||||
ConfigMenu() {
|
ConfigMenu() {
|
||||||
|
@ -65,9 +79,18 @@ public:
|
||||||
recomp::register_event(listener, "apply_options",
|
recomp::register_event(listener, "apply_options",
|
||||||
[](const std::string& param, Rml::Event& event) {
|
[](const std::string& param, Rml::Event& event) {
|
||||||
cur_options = new_options;
|
cur_options = new_options;
|
||||||
options_handle.DirtyVariable("options_changed");
|
graphics_model_handle.DirtyVariable("options_changed");
|
||||||
update_graphics_config(new_options);
|
update_graphics_config(new_options);
|
||||||
});
|
});
|
||||||
|
recomp::register_event(listener, "rebind_input_bindings",
|
||||||
|
[](const std::string& param, Rml::Event& event) {
|
||||||
|
});
|
||||||
|
recomp::register_event(listener, "clear_input_bindings",
|
||||||
|
[](const std::string& param, Rml::Event& event) {
|
||||||
|
});
|
||||||
|
recomp::register_event(listener, "add_input_binding",
|
||||||
|
[](const std::string& param, Rml::Event& event) {
|
||||||
|
});
|
||||||
recomp::register_event(listener, "config_keydown",
|
recomp::register_event(listener, "config_keydown",
|
||||||
[](const std::string& param, Rml::Event& event) {
|
[](const std::string& param, Rml::Event& event) {
|
||||||
if (event.GetId() == Rml::EventId::Keydown) {
|
if (event.GetId() == Rml::EventId::Keydown) {
|
||||||
|
@ -82,10 +105,10 @@ public:
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
void make_bindings(Rml::Context* context) override {
|
void make_graphics_bindings(Rml::Context* context) {
|
||||||
Rml::DataModelConstructor constructor = context->CreateDataModel("graphics_model");
|
Rml::DataModelConstructor constructor = context->CreateDataModel("graphics_model");
|
||||||
if (!constructor) {
|
if (!constructor) {
|
||||||
throw std::runtime_error("Failed to make RmlUi data model for the config menu");
|
throw std::runtime_error("Failed to make RmlUi data model for the graphics config menu");
|
||||||
}
|
}
|
||||||
|
|
||||||
bind_option(constructor, "res_option", &new_options.res_option);
|
bind_option(constructor, "res_option", &new_options.res_option);
|
||||||
|
@ -99,7 +122,7 @@ public:
|
||||||
},
|
},
|
||||||
[](const Rml::Variant& in) {
|
[](const Rml::Variant& in) {
|
||||||
new_options.rr_manual_value = in.Get<int>();
|
new_options.rr_manual_value = in.Get<int>();
|
||||||
options_handle.DirtyVariable("options_changed");
|
graphics_model_handle.DirtyVariable("options_changed");
|
||||||
});
|
});
|
||||||
|
|
||||||
constructor.BindFunc("options_changed",
|
constructor.BindFunc("options_changed",
|
||||||
|
@ -107,7 +130,91 @@ public:
|
||||||
out = (cur_options != new_options);
|
out = (cur_options != new_options);
|
||||||
});
|
});
|
||||||
|
|
||||||
options_handle = constructor.GetModelHandle();
|
graphics_model_handle = constructor.GetModelHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_controls_bindings(Rml::Context* context) {
|
||||||
|
Rml::DataModelConstructor constructor = context->CreateDataModel("controls_model");
|
||||||
|
if (!constructor) {
|
||||||
|
throw std::runtime_error("Failed to make RmlUi data model for the controls config menu");
|
||||||
|
}
|
||||||
|
|
||||||
|
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_names().at(inputs.at(0).Get<size_t>())};
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor.BindEventCallback("set_input_binding",
|
||||||
|
[](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) {
|
||||||
|
scanned_input_index = inputs.at(0).Get<size_t>();
|
||||||
|
scanned_binding_index = inputs.at(1).Get<size_t>();
|
||||||
|
recomp::start_scanning_input(cur_device);
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor.BindEventCallback("clear_input_bindings",
|
||||||
|
[](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) {
|
||||||
|
size_t input_index = inputs.at(0).Get<size_t>();
|
||||||
|
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{});
|
||||||
|
}
|
||||||
|
model_handle.DirtyVariable("inputs");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rml variable definition for an individual InputField.
|
||||||
|
struct InputFieldVariableDefinition : public Rml::VariableDefinition {
|
||||||
|
InputFieldVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Scalar) {}
|
||||||
|
|
||||||
|
virtual bool Get(void* ptr, Rml::Variant& variant) override { variant = reinterpret_cast<recomp::InputField*>(ptr)->to_string(); return true; }
|
||||||
|
virtual bool Set(void* ptr, const Rml::Variant& variant) override { return false; }
|
||||||
|
};
|
||||||
|
// Static instance of the InputField variable definition to have a pointer to return to RmlUi.
|
||||||
|
static InputFieldVariableDefinition input_field_definition_instance{};
|
||||||
|
|
||||||
|
// Rml variable definition for an array of InputField values (e.g. all the bindings for a single input).
|
||||||
|
struct BindingContainerVariableDefinition : public Rml::VariableDefinition {
|
||||||
|
BindingContainerVariableDefinition() : 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; }
|
||||||
|
|
||||||
|
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)};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Static instance of the InputField array variable definition to 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) {}
|
||||||
|
|
||||||
|
virtual bool Get(void* ptr, Rml::Variant& variant) override { return false; }
|
||||||
|
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 {
|
||||||
|
// Encode the input index as the pointer to avoid needing to do any allocations.
|
||||||
|
return Rml::DataVariable(&binding_container_var_instance, (void*)(uintptr_t)address.index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dummy type to associate with the variable definition.
|
||||||
|
struct InputContainer {};
|
||||||
|
constructor.RegisterCustomDataVariableDefinition<InputContainer>(Rml::MakeUnique<InputContainerVariableDefinition>());
|
||||||
|
|
||||||
|
// Dummy instance of the dummy type to bind to the variable.
|
||||||
|
static InputContainer dummy_container;
|
||||||
|
constructor.Bind("inputs", &dummy_container);
|
||||||
|
|
||||||
|
controls_model_handle = constructor.GetModelHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_bindings(Rml::Context* context) override {
|
||||||
|
make_graphics_bindings(context);
|
||||||
|
make_controls_bindings(context);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
#include "recomp_ui.h"
|
#include "recomp_ui.h"
|
||||||
|
#include "recomp_input.h"
|
||||||
|
|
||||||
#include "concurrentqueue.h"
|
#include "concurrentqueue.h"
|
||||||
|
|
||||||
|
@ -677,6 +678,7 @@ struct {
|
||||||
else {
|
else {
|
||||||
current_document = nullptr;
|
current_document = nullptr;
|
||||||
}
|
}
|
||||||
|
prev_focused = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void swap_config_menu(recomp::ConfigSubmenu submenu) {
|
void swap_config_menu(recomp::ConfigSubmenu submenu) {
|
||||||
|
@ -686,6 +688,7 @@ struct {
|
||||||
Rml::ElementTabSet* config_tabset = rmlui_dynamic_cast<Rml::ElementTabSet*>(config_tabset_base);
|
Rml::ElementTabSet* config_tabset = rmlui_dynamic_cast<Rml::ElementTabSet*>(config_tabset_base);
|
||||||
if (config_tabset != nullptr) {
|
if (config_tabset != nullptr) {
|
||||||
config_tabset->SetActiveTab(static_cast<int>(submenu));
|
config_tabset->SetActiveTab(static_cast<int>(submenu));
|
||||||
|
prev_focused = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -714,6 +717,8 @@ struct {
|
||||||
for (auto& [menu, controller]: menus) {
|
for (auto& [menu, controller]: menus) {
|
||||||
documents.emplace(menu, controller->load_document(context));
|
documents.emplace(menu, controller->load_document(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prev_focused = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void make_event_listeners() {
|
void make_event_listeners() {
|
||||||
|
@ -917,6 +922,11 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recomp::InputField scanned_field = recomp::get_scanned_input();
|
||||||
|
if (scanned_field != recomp::InputField{}) {
|
||||||
|
recomp::finish_scanning_input(scanned_field);
|
||||||
|
}
|
||||||
|
|
||||||
UIContext.rml.update_focus(mouse_moved);
|
UIContext.rml.update_focus(mouse_moved);
|
||||||
|
|
||||||
if (cur_menu != recomp::Menu::None) {
|
if (cur_menu != recomp::Menu::None) {
|
||||||
|
|
Loading…
Reference in New Issue