Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 81 additions & 30 deletions assets/config_menu/controls.rml
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,83 @@
</head>
<body>
<form class="config__form" data-attr-cur-input="cur_input_row" data-attr-cur-binding-slot="active_binding_slot">
<div class="config__header">
<div class="config__header-left">
<button
class="toggle"
id="cont_kb_toggle"
data-class-toggle--checked="input_device_is_keyboard"
onclick="toggle_input_device"
style="nav-down: #input_row_button_0_0; nav-up: #tab_controls"
>
<div class="toggle__border" />
<div class="toggle__floater" />
<div class="toggle__icons">
<div class="toggle__icon toggle__icon--left"><div>␼</div></div>
<div class="toggle__icon toggle__icon--right"><div>␽</div></div>
</div>
</button>
</div>
<div>
<button
class="button button--warning"
style="nav-down:#input_row_button_0_0"
data-event-click="reset_input_bindings_to_defaults"
>
<div class="button__label">Reset to defaults</div>
</button>
</div>
<!-- Port selector -->
<div class="config__header" style="justify-content: center; gap: 8dp; margin-bottom: 4dp;">
<button
class="button"
data-class-button--success="selected_port == 0"
data-event-click="select_port(0)"
data-attr-style="port_0_style"
>
<div class="button__label">P1</div>
</button>
<button
class="button"
data-class-button--success="selected_port == 1"
data-event-click="select_port(1)"
data-attr-style="port_1_style"
>
<div class="button__label">P2</div>
</button>
<button
class="button"
data-class-button--success="selected_port == 2"
data-event-click="select_port(2)"
data-attr-style="port_2_style"
>
<div class="button__label">P3</div>
</button>
<button
class="button"
data-class-button--success="selected_port == 3"
data-event-click="select_port(3)"
data-attr-style="port_3_style"
>
<div class="button__label">P4</div>
</button>
</div>
<!-- Port mode selector -->
<div class="config__header" style="justify-content: center; gap: 8dp; margin-bottom: 4dp; align-items: center;">
<button
class="button"
data-class-button--error="port_mode == 'Off'"
data-event-click="set_port_mode('Off')"
data-attr-style="port_mode == 'Off' ? 'min-width: 64dp; opacity: 1.0;' : 'min-width: 64dp; opacity: 0.5;'"
>
<div class="button__label">Off</div>
</button>
<button
class="button"
data-class-button--success="port_mode == 'Keyboard'"
data-event-click="set_port_mode('Keyboard')"
data-attr-style="port_mode == 'Keyboard' ? 'min-width: 80dp; opacity: 1.0;' : 'min-width: 80dp; opacity: 0.5;'"
>
<div class="button__label">Keyboard</div>
</button>
<button
class="button"
data-class-button--success="port_mode == 'Controller'"
data-event-click="set_port_mode('Controller')"
data-attr-style="port_mode == 'Controller' ? 'min-width: 80dp; opacity: 1.0;' : 'min-width: 80dp; opacity: 0.5;'"
>
<div class="button__label">Controller</div>
</button>
<select
class="config-option-dropdown__select"
id="port_controller_select"
data-value="port_controller_index"
onchange="port_controller_changed"
style="min-width: 300dp; margin-left: 8dp; flex: 0 1 auto; height: auto; padding: 23dp;"
>
<option data-for="name, i : connected_controller_names" data-attr-value="i">{{name}}</option>
</select>
<button
class="button button--warning"
style="nav-down:#input_row_button_0_0; margin-left: 8dp;"
data-event-click="reset_input_bindings_to_defaults"
>
<div class="button__label">Reset to defaults</div>
</button>
</div>
<div class="config__wrapper input-config">
<div class="input-config__horizontal-split">
Expand All @@ -41,7 +92,7 @@
data-for="input_bindings, i : inputs.array"
data-event-mouseover="set_input_row_focus(i)"
data-class-control-option--active="get_input_enum_name(i)==cur_input_row"
data-if="!input_device_is_keyboard || (get_input_enum_name(i) != 'TOGGLE_MENU' && get_input_enum_name(i) != 'ACCEPT_MENU' && get_input_enum_name(i) != 'APPLY_MENU')"
data-if="(!is_multiplayer_port || get_input_enum_name(i) == 'A' || get_input_enum_name(i) == 'B' || get_input_enum_name(i) == 'START' || get_input_enum_name(i) == 'X_AXIS_NEG' || get_input_enum_name(i) == 'X_AXIS_POS' || get_input_enum_name(i) == 'Y_AXIS_NEG' || get_input_enum_name(i) == 'Y_AXIS_POS') && (!input_device_is_keyboard || (get_input_enum_name(i) != 'TOGGLE_MENU' && get_input_enum_name(i) != 'ACCEPT_MENU' && get_input_enum_name(i) != 'APPLY_MENU'))"
>
<label
class="control-option__label"
Expand All @@ -55,7 +106,7 @@
data-event-click="set_input_binding(i,j)"
class="prompt-font control-option__binding"
data-attr-bind-slot="j"
data-attr-style="i == 0 ? 'nav-up:#cont_kb_toggle' : 'nav-up:auto'"
data-attr-style="i == 0 ? 'nav-up:#tab_controls' : 'nav-up:auto'"
>
<div class="control-option__binding-recording">
<div class="control-option__binding-circle" />
Expand All @@ -72,7 +123,7 @@
data-event-focus="set_input_row_focus(i)"
data-event-click="clear_input_bindings(i)"
class="icon-button icon-button--error"
data-attr-style="i == 0 ? 'nav-up:#cont_kb_toggle' : 'nav-up:auto'"
data-attr-style="i == 0 ? 'nav-up:#tab_controls' : 'nav-up:auto'"
>
<svg src="icons/Trash.svg" />
</button>
Expand All @@ -82,7 +133,7 @@
data-event-focus="set_input_row_focus(i)"
data-event-click="reset_single_input_binding_to_default(i)"
class="icon-button icon-button--error"
data-attr-style="i == 0 ? 'nav-up:#cont_kb_toggle' : 'nav-up:auto'"
data-attr-style="i == 0 ? 'nav-up:#tab_controls' : 'nav-up:auto'"
>
<svg src="icons/Reset.svg" />
</button>
Expand Down
33 changes: 32 additions & 1 deletion include/recomp_input.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ namespace recomp {
float get_input_analog(const std::span<const recomp::InputField> fields);
bool get_input_digital(const InputField& field);
bool get_input_digital(const std::span<const recomp::InputField> fields);
// Per-port versions: for controller inputs, read only from the port's assigned gamepad
float get_input_analog_port(const InputField& field, int port);
float get_input_analog_port(const std::span<const recomp::InputField> fields, int port);
bool get_input_digital_port(const InputField& field, int port);
bool get_input_digital_port(const std::span<const recomp::InputField> fields, int port);
void get_gyro_deltas(float* x, float* y);
void get_mouse_deltas(float* x, float* y);
void get_right_analog(float* x, float* y);
Expand All @@ -84,6 +89,25 @@ namespace recomp {
COUNT
};

enum class ControllerPortMode {
Off,
Keyboard,
Controller,
OptionCount
};

NLOHMANN_JSON_SERIALIZE_ENUM(recomp::ControllerPortMode, {
{recomp::ControllerPortMode::Off, "Off"},
{recomp::ControllerPortMode::Keyboard, "Keyboard"},
{recomp::ControllerPortMode::Controller, "Controller"}
});

constexpr int max_ports = 4;

ControllerPortMode get_port_mode(int port);
void set_port_mode(int port, ControllerPortMode mode);
int get_port_count();

void start_scanning_input(InputDevice device);
void stop_scanning_input();
void finish_scanning_input(InputField scanned_field);
Expand Down Expand Up @@ -150,6 +174,7 @@ namespace recomp {

extern const DefaultN64Mappings default_n64_keyboard_mappings;
extern const DefaultN64Mappings default_n64_controller_mappings;
const DefaultN64Mappings& get_default_keyboard_mappings_for_port(int port);

constexpr size_t bindings_per_input = 2;

Expand All @@ -158,15 +183,21 @@ namespace recomp {
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);
InputField& get_input_binding(GameInput input, size_t binding_index, InputDevice device, int port);
void set_input_binding(GameInput input, size_t binding_index, InputDevice device, InputField value);
void set_input_binding(GameInput input, size_t binding_index, InputDevice device, InputField value, int port);

bool get_n64_input(int controller_num, uint16_t* buttons_out, float* x_out, float* y_out);
void set_rumble(int controller_num, bool);
void update_rumble();
void handle_events();

ultramodern::input::connected_device_info_t get_connected_device_info(int controller_num);

std::string get_port_controller_name(int port);
std::vector<std::string> get_connected_controller_names();
int get_port_controller_index(int port);
void assign_controller_to_port(int port, int controller_index);

// Rumble strength ranges from 0 to 100.
int get_rumble_strength();
void set_rumble_strength(int strength);
Expand Down
4 changes: 4 additions & 0 deletions include/zelda_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ namespace zelda64 {
void save_config();

void reset_input_bindings();
void reset_input_bindings(int port);
void reset_cont_input_bindings();
void reset_cont_input_bindings(int port);
void reset_kb_input_bindings();
void reset_kb_input_bindings(int port);
void reset_single_input_binding(recomp::InputDevice device, recomp::GameInput input);
void reset_single_input_binding(recomp::InputDevice device, recomp::GameInput input, int port);

std::filesystem::path get_app_folder_path();

Expand Down
Loading
Loading