2023-11-24 22:10:21 +00:00
|
|
|
#include <atomic>
|
|
|
|
|
|
|
|
#include "../ultramodern/ultramodern.hpp"
|
|
|
|
#include "recomp.h"
|
|
|
|
#include "recomp_input.h"
|
|
|
|
#include "recomp_ui.h"
|
|
|
|
#include "SDL.h"
|
|
|
|
|
2023-12-13 07:06:56 +00:00
|
|
|
|
|
|
|
static struct {
|
|
|
|
const Uint8* keys = nullptr;
|
|
|
|
int numkeys = 0;
|
|
|
|
std::atomic_int32_t mouse_wheel_pos = 0;
|
|
|
|
std::vector<SDL_JoystickID> controller_ids{};
|
|
|
|
std::vector<SDL_GameController*> cur_controllers{};
|
|
|
|
} InputState;
|
2023-11-24 22:10:21 +00:00
|
|
|
|
|
|
|
bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
|
|
|
switch (event->type) {
|
|
|
|
//case SDL_EventType::SDL_KEYUP:
|
|
|
|
//case SDL_EventType::SDL_KEYDOWN:
|
|
|
|
// {
|
|
|
|
// const Uint8* key_states = SDL_GetKeyboardState(nullptr);
|
|
|
|
// int new_button = 0;
|
|
|
|
|
|
|
|
// for (const auto& mapping : keyboard_button_map) {
|
|
|
|
// if (key_states[mapping.first]) {
|
|
|
|
// new_button |= mapping.second;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// button = new_button;
|
|
|
|
|
|
|
|
// stick_x = (100.0f / 100.0f) * (key_states[SDL_Scancode::SDL_SCANCODE_D] - key_states[SDL_Scancode::SDL_SCANCODE_A]);
|
|
|
|
// stick_y = (100.0f / 100.0f) * (key_states[SDL_Scancode::SDL_SCANCODE_W] - key_states[SDL_Scancode::SDL_SCANCODE_S]);
|
|
|
|
// }
|
|
|
|
// break;
|
|
|
|
case SDL_EventType::SDL_CONTROLLERDEVICEADDED:
|
|
|
|
{
|
|
|
|
SDL_ControllerDeviceEvent* controller_event = (SDL_ControllerDeviceEvent*)event;
|
|
|
|
SDL_GameController* controller = SDL_GameControllerOpen(controller_event->which);
|
|
|
|
printf("Controller added: %d\n", controller_event->which);
|
|
|
|
if (controller != nullptr) {
|
|
|
|
printf(" Instance ID: %d\n", SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller)));
|
2023-12-13 07:06:56 +00:00
|
|
|
InputState.controller_ids.push_back(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller)));
|
2023-11-24 22:10:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_EventType::SDL_CONTROLLERDEVICEREMOVED:
|
|
|
|
{
|
|
|
|
SDL_ControllerDeviceEvent* controller_event = (SDL_ControllerDeviceEvent*)event;
|
|
|
|
printf("Controller removed: %d\n", controller_event->which);
|
2023-12-13 07:06:56 +00:00
|
|
|
std::erase(InputState.controller_ids, controller_event->which);
|
2023-11-24 22:10:21 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_EventType::SDL_QUIT:
|
|
|
|
ultramodern::quit();
|
|
|
|
return true;
|
|
|
|
case SDL_EventType::SDL_MOUSEWHEEL:
|
|
|
|
{
|
|
|
|
SDL_MouseWheelEvent* wheel_event = (SDL_MouseWheelEvent*)event;
|
2023-12-13 07:06:56 +00:00
|
|
|
InputState.mouse_wheel_pos.fetch_add(wheel_event->y * (wheel_event->direction == SDL_MOUSEWHEEL_FLIPPED ? -1 : 1));
|
2023-11-24 22:10:21 +00:00
|
|
|
}
|
|
|
|
recomp::queue_event(*event);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
recomp::queue_event(*event);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void recomp::handle_events() {
|
|
|
|
SDL_Event cur_event;
|
|
|
|
static bool exited = false;
|
|
|
|
while (SDL_PollEvent(&cur_event) && !exited) {
|
|
|
|
exited = sdl_event_filter(nullptr, &cur_event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-13 07:06:56 +00:00
|
|
|
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_EAST = SDL_CONTROLLER_BUTTON_B;
|
|
|
|
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_WEST = SDL_CONTROLLER_BUTTON_X;
|
|
|
|
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_NORTH = SDL_CONTROLLER_BUTTON_Y;
|
|
|
|
|
|
|
|
const recomp::DefaultN64Mappings recomp::default_n64_mappings = {
|
|
|
|
.a = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_SOUTH},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_SPACE}
|
|
|
|
},
|
|
|
|
.b = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_EAST},
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_WEST},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_LSHIFT}
|
|
|
|
},
|
|
|
|
.l = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_E}
|
|
|
|
},
|
|
|
|
.r = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_R}
|
|
|
|
},
|
|
|
|
.z = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_TRIGGERLEFT + 1},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_Q}
|
|
|
|
},
|
|
|
|
.start = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_START},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_RETURN}
|
|
|
|
},
|
|
|
|
.c_left = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_RIGHTX + 1)},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_LEFT}
|
|
|
|
},
|
|
|
|
.c_right = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_RIGHTX + 1},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_RIGHT}
|
|
|
|
},
|
|
|
|
.c_up = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_RIGHTY + 1)},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_UP}
|
|
|
|
},
|
|
|
|
.c_down = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_RIGHTY + 1},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_DOWN}
|
|
|
|
},
|
|
|
|
.dpad_left = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_LEFT},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_J}
|
|
|
|
},
|
|
|
|
.dpad_right = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_L}
|
|
|
|
},
|
|
|
|
.dpad_up = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_UP},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_I}
|
|
|
|
},
|
|
|
|
.dpad_down = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_DOWN},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_K}
|
|
|
|
},
|
|
|
|
.analog_left = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_LEFTX + 1)},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_A}
|
|
|
|
},
|
|
|
|
.analog_right = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_LEFTX + 1},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_D}
|
|
|
|
},
|
|
|
|
.analog_up = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_LEFTY + 1)},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_W}
|
|
|
|
},
|
|
|
|
.analog_down = {
|
|
|
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_LEFTY + 1},
|
|
|
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_S}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
void recomp::poll_inputs() {
|
|
|
|
InputState.keys = SDL_GetKeyboardState(&InputState.numkeys);
|
|
|
|
|
|
|
|
InputState.cur_controllers.clear();
|
|
|
|
|
|
|
|
for (SDL_JoystickID id : InputState.controller_ids) {
|
|
|
|
SDL_GameController* controller = SDL_GameControllerFromInstanceID(id);
|
|
|
|
if (controller != nullptr) {
|
|
|
|
InputState.cur_controllers.push_back(controller);
|
2023-11-24 22:10:21 +00:00
|
|
|
}
|
|
|
|
}
|
2023-12-13 07:06:56 +00:00
|
|
|
}
|
2023-11-24 22:10:21 +00:00
|
|
|
|
2023-12-13 07:06:56 +00:00
|
|
|
bool controller_button_state(int32_t input_id) {
|
|
|
|
if (input_id >= 0 && input_id < SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_MAX) {
|
|
|
|
SDL_GameControllerButton button = (SDL_GameControllerButton)input_id;
|
|
|
|
bool ret = false;
|
2023-11-24 22:10:21 +00:00
|
|
|
|
2023-12-13 07:06:56 +00:00
|
|
|
for (const auto& controller : InputState.cur_controllers) {
|
|
|
|
ret |= SDL_GameControllerGetButton(controller, button);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2023-11-24 22:10:21 +00:00
|
|
|
}
|
2023-12-13 07:06:56 +00:00
|
|
|
return false;
|
|
|
|
}
|
2023-11-24 22:10:21 +00:00
|
|
|
|
2023-12-13 07:06:56 +00:00
|
|
|
float controller_axis_state(int32_t input_id) {
|
|
|
|
if (abs(input_id) - 1 < SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_MAX) {
|
|
|
|
SDL_GameControllerAxis axis = (SDL_GameControllerAxis)(abs(input_id) - 1);
|
|
|
|
bool negative_range = input_id < 0;
|
|
|
|
float ret = 0.0f;
|
|
|
|
|
|
|
|
for (const auto& controller : InputState.cur_controllers) {
|
|
|
|
float cur_val = SDL_GameControllerGetAxis(controller, axis) * (1/32768.0f);
|
|
|
|
if (negative_range) {
|
|
|
|
cur_val = -cur_val;
|
2023-11-24 22:10:21 +00:00
|
|
|
}
|
2023-12-13 07:06:56 +00:00
|
|
|
ret += std::clamp(cur_val, 0.0f, 1.0f);
|
2023-11-24 22:10:21 +00:00
|
|
|
}
|
|
|
|
|
2023-12-13 07:06:56 +00:00
|
|
|
return std::clamp(ret, 0.0f, 1.0f);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
float recomp::get_input_analog(const recomp::InputField& field) {
|
|
|
|
switch ((DeviceType)field.device_type) {
|
|
|
|
case DeviceType::Keyboard:
|
|
|
|
{
|
|
|
|
if (InputState.keys && field.input_id >= 0 && field.input_id < InputState.numkeys) {
|
|
|
|
return InputState.keys[field.input_id] ? 1.0f : 0.0f;
|
|
|
|
}
|
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
case DeviceType::ControllerDigital:
|
|
|
|
{
|
|
|
|
return controller_button_state(field.input_id) ? 1.0f : 0.0f;
|
|
|
|
}
|
|
|
|
case DeviceType::ControllerAnalog:
|
|
|
|
{
|
|
|
|
return controller_axis_state(field.input_id);
|
2023-11-24 22:10:21 +00:00
|
|
|
}
|
2023-12-13 07:06:56 +00:00
|
|
|
case DeviceType::Mouse:
|
|
|
|
{
|
|
|
|
// TODO mouse support
|
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-11-24 22:10:21 +00:00
|
|
|
|
2023-12-13 07:06:56 +00:00
|
|
|
float recomp::get_input_analog(const std::span<const recomp::InputField> fields) {
|
|
|
|
float ret = 0.0f;
|
|
|
|
for (const auto& field : fields) {
|
|
|
|
ret += get_input_analog(field);
|
|
|
|
}
|
|
|
|
return std::clamp(ret, 0.0f, 1.0f);
|
2023-11-24 22:10:21 +00:00
|
|
|
}
|
|
|
|
|
2023-12-13 07:06:56 +00:00
|
|
|
bool recomp::get_input_digital(const recomp::InputField& field) {
|
|
|
|
switch ((DeviceType)field.device_type) {
|
|
|
|
case DeviceType::Keyboard:
|
|
|
|
{
|
|
|
|
if (InputState.keys && field.input_id >= 0 && field.input_id < InputState.numkeys) {
|
|
|
|
return InputState.keys[field.input_id] != 0;
|
2023-11-24 22:10:21 +00:00
|
|
|
}
|
2023-12-13 07:06:56 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
case DeviceType::ControllerDigital:
|
|
|
|
{
|
|
|
|
return controller_button_state(field.input_id);
|
|
|
|
}
|
|
|
|
case DeviceType::ControllerAnalog:
|
|
|
|
{
|
|
|
|
// TODO adjustable threshold
|
|
|
|
return controller_axis_state(field.input_id) >= 0.5f;
|
|
|
|
}
|
|
|
|
case DeviceType::Mouse:
|
|
|
|
{
|
|
|
|
// TODO mouse support
|
|
|
|
return false;
|
2023-11-24 22:10:21 +00:00
|
|
|
}
|
2023-12-13 07:06:56 +00:00
|
|
|
}
|
|
|
|
}
|
2023-11-24 22:10:21 +00:00
|
|
|
|
2023-12-13 07:06:56 +00:00
|
|
|
bool recomp::get_input_digital(const std::span<const recomp::InputField> fields) {
|
|
|
|
bool ret = 0;
|
|
|
|
for (const auto& field : fields) {
|
|
|
|
ret |= get_input_digital(field);
|
|
|
|
}
|
|
|
|
return ret;
|
2023-11-24 22:10:21 +00:00
|
|
|
}
|