mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-23 14:20:07 +00:00
input: Hopefully make keyboard/mouse handling more consistent (#2807)
The current event-based approach is very difficult to get right, and it depends on no events ever being missed. This changes the keyboard/mouse handling code to a polling-based approach. Other fixes: - an issue where modifier keys were not able to be successfully bound (like Left Shift to `X`) - improves cursor hiding (except when you use the start menu, this seems like an SDL issue, see comment) - Better discarding of kb/mouse inputs when imgui intercepts input - properly swap bindings when an already set key is assigned, even if it crosses the distinction of an analog vs normal button Fixes #2800
This commit is contained in:
parent
c9330a6951
commit
6faa7530f9
@ -42,7 +42,7 @@
|
||||
#include <crt_externs.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <mach-o/dyld.h>
|
||||
#include "mach-o/dyld.h"
|
||||
#endif
|
||||
|
||||
namespace file_util {
|
||||
|
@ -316,7 +316,7 @@ GLDisplay::GLDisplay(SDL_Window* window, SDL_GLContext gl_context, bool is_main)
|
||||
m_display_manager(std::make_shared<DisplayManager>(window)),
|
||||
m_input_manager(std::make_shared<InputManager>()) {
|
||||
m_main = is_main;
|
||||
|
||||
m_display_manager->set_input_manager(m_input_manager);
|
||||
// Register commands
|
||||
m_input_manager->register_command(CommandBinding::Source::KEYBOARD,
|
||||
CommandBinding(SDLK_F12, [&]() {
|
||||
@ -466,13 +466,9 @@ void GLDisplay::process_sdl_events() {
|
||||
ImGui_ImplSDL2_ProcessEvent(&evt);
|
||||
}
|
||||
}
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
{
|
||||
auto p = scoped_prof("sdl-input-monitor");
|
||||
// Ignore relevant inputs from the window if ImGUI is capturing them
|
||||
// On the first frame, this will clear any current inputs in an attempt to reduce unexpected
|
||||
// behaviour
|
||||
m_input_manager->process_sdl_event(evt, io.WantCaptureMouse, io.WantCaptureKeyboard);
|
||||
auto p = scoped_prof("sdl-input-monitor-process-event");
|
||||
m_input_manager->process_sdl_event(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -481,7 +477,27 @@ void GLDisplay::process_sdl_events() {
|
||||
* Main function called to render graphics frames. This is called in a loop.
|
||||
*/
|
||||
void GLDisplay::render() {
|
||||
// Process SDL Events
|
||||
// Before we process the current frames SDL events we for keyboard/mouse button inputs.
|
||||
//
|
||||
// This technically means that keyboard/mouse button inputs will be a frame behind but the
|
||||
// event-based code is buggy and frankly not worth stressing over. Leaving this as a note incase
|
||||
// someone complains. Binding handling is still taken care of by the event code though.
|
||||
{
|
||||
auto p = scoped_prof("sdl-input-monitor-poll-for-kb-mouse");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.WantCaptureKeyboard) {
|
||||
m_input_manager->clear_keyboard_actions();
|
||||
} else {
|
||||
m_input_manager->poll_keyboard_data();
|
||||
}
|
||||
if (io.WantCaptureMouse) {
|
||||
m_input_manager->clear_mouse_actions();
|
||||
} else {
|
||||
m_input_manager->poll_mouse_data();
|
||||
}
|
||||
m_input_manager->finish_polling();
|
||||
}
|
||||
// Now process SDL Events
|
||||
process_sdl_events();
|
||||
// Also process any display related events received from the EE (the game)
|
||||
// this is done here so they run from the perspective of the graphics thread
|
||||
|
@ -66,8 +66,7 @@ GameController::GameController(int sdl_device_id,
|
||||
void GameController::process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment,
|
||||
bool ignore_inputs) {
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment) {
|
||||
if (event.type == SDL_CONTROLLERAXISMOTION && event.caxis.which == m_sdl_instance_id) {
|
||||
// https://wiki.libsdl.org/SDL2/SDL_GameControllerAxis
|
||||
if ((int)event.caxis.axis <= SDL_CONTROLLER_AXIS_INVALID ||
|
||||
|
@ -11,8 +11,7 @@ class GameController : public InputDevice {
|
||||
void process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment,
|
||||
bool ignore_inputs = false) override;
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment) override;
|
||||
void close_device() override;
|
||||
int update_rumble(const u8 low_rumble, const u8 high_rumble);
|
||||
std::string get_name() const { return m_device_name; }
|
||||
|
@ -19,8 +19,7 @@ class InputDevice {
|
||||
virtual void process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment,
|
||||
bool ignore_inputs = false) = 0;
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment) = 0;
|
||||
virtual void close_device() = 0;
|
||||
bool is_loaded() const { return m_loaded; };
|
||||
};
|
||||
|
@ -6,19 +6,95 @@ KeyboardDevice::KeyboardDevice(std::shared_ptr<game_settings::InputSettings> set
|
||||
m_settings = settings;
|
||||
}
|
||||
|
||||
// I don't trust SDL's key repeat stuff, do it myself to avoid bug reports...(or cause more)
|
||||
bool KeyboardDevice::is_action_already_active(const u32 sdl_keycode) {
|
||||
for (const auto& action : m_active_actions) {
|
||||
if (action.sdl_keycode == sdl_keycode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void KeyboardDevice::poll_state(std::shared_ptr<PadData> data) {
|
||||
auto& binds = m_settings->keyboard_binds;
|
||||
const auto keyboard_state = SDL_GetKeyboardState(NULL);
|
||||
const auto keyboard_modifier_state = SDL_GetModState();
|
||||
|
||||
// Iterate binds, see if there are any new actions we need to track
|
||||
// - Normal Buttons
|
||||
for (const auto& [sdl_keycode, bind_list] : binds.buttons) {
|
||||
for (const auto& bind : bind_list) {
|
||||
if (keyboard_state[SDL_GetScancodeFromKey(sdl_keycode)] &&
|
||||
bind.modifiers.has_necessary_modifiers(keyboard_modifier_state) &&
|
||||
!is_action_already_active(sdl_keycode)) {
|
||||
data->button_data.at(bind.pad_data_index) = true; // press the button
|
||||
m_active_actions.push_back(
|
||||
{sdl_keycode, bind, [](std::shared_ptr<PadData> data, InputBinding bind) {
|
||||
data->button_data.at(bind.pad_data_index) = false; // let go of the button
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
// - Analog Buttons
|
||||
for (const auto& [sdl_keycode, bind_list] : binds.button_axii) {
|
||||
for (const auto& bind : bind_list) {
|
||||
if (keyboard_state[SDL_GetScancodeFromKey(sdl_keycode)] &&
|
||||
bind.modifiers.has_necessary_modifiers(keyboard_modifier_state) &&
|
||||
!is_action_already_active(sdl_keycode)) {
|
||||
data->button_data.at(bind.pad_data_index) = true; // press the button
|
||||
m_active_actions.push_back(
|
||||
{sdl_keycode, bind, [](std::shared_ptr<PadData> data, InputBinding bind) {
|
||||
data->button_data.at(bind.pad_data_index) = false; // let go of the button
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
// - Analog Sticks
|
||||
for (const auto& [sdl_keycode, bind_list] : binds.analog_axii) {
|
||||
for (const auto& bind : bind_list) {
|
||||
if (keyboard_state[SDL_GetScancodeFromKey(sdl_keycode)] &&
|
||||
bind.modifiers.has_necessary_modifiers(keyboard_modifier_state) &&
|
||||
!is_action_already_active(sdl_keycode)) {
|
||||
data->analog_data.at(bind.pad_data_index) += bind.minimum_in_range ? -127 : 127;
|
||||
data->update_analog_sim_tracker(false);
|
||||
m_active_actions.push_back(
|
||||
{sdl_keycode, bind, [](std::shared_ptr<PadData> data, InputBinding bind) {
|
||||
data->analog_data.at(bind.pad_data_index) += bind.minimum_in_range ? 127 : -127;
|
||||
data->update_analog_sim_tracker(true);
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any previously tracked actions are now invalidated by the new state of the keyboard
|
||||
// if so, we'll run their revert code and remove them
|
||||
for (auto it = m_active_actions.begin(); it != m_active_actions.end();) {
|
||||
// Modifiers are easy, if the action required one and it's not pressed anymore, evict it
|
||||
// Alternatively, was the primary key released
|
||||
if (!keyboard_state[SDL_GetScancodeFromKey(it->sdl_keycode)] ||
|
||||
!it->binding.modifiers.has_necessary_modifiers(keyboard_modifier_state)) {
|
||||
it->revert_action(data, it->binding);
|
||||
it = m_active_actions.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardDevice::clear_actions(std::shared_ptr<PadData> data) {
|
||||
for (auto it = m_active_actions.begin(); it != m_active_actions.end();) {
|
||||
it->revert_action(data, it->binding);
|
||||
it = m_active_actions.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardDevice::process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment,
|
||||
bool ignore_inputs) {
|
||||
if (ignore_inputs) {
|
||||
return;
|
||||
}
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment) {
|
||||
if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) {
|
||||
const auto key_event = event.key;
|
||||
if (key_event.repeat != 0) {
|
||||
return;
|
||||
}
|
||||
if (m_ignore_key_on_keyup && m_ignore_key_on_keyup.value() == (u32)key_event.keysym.sym &&
|
||||
event.type == SDL_KEYUP) {
|
||||
m_ignore_key_on_keyup = std::nullopt;
|
||||
@ -32,7 +108,6 @@ void KeyboardDevice::process_event(const SDL_Event& event,
|
||||
if (bind_assignment->device_type != InputDeviceType::KEYBOARD) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A normal key down event (a new key was pressed) and it's not a modifier
|
||||
if (event.type == SDL_KEYDOWN && !sdl_util::is_modifier_key(key_event.keysym.sym)) {
|
||||
if (bind_assignment->for_analog) {
|
||||
@ -54,7 +129,7 @@ void KeyboardDevice::process_event(const SDL_Event& event,
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
// otherwise, set the bind
|
||||
if (bind_assignment->for_analog) {
|
||||
binds.assign_analog_bind(key_event.keysym.sym, bind_assignment.value(),
|
||||
@ -64,40 +139,9 @@ void KeyboardDevice::process_event(const SDL_Event& event,
|
||||
InputModifiers(key_event.keysym.mod));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal Buttons
|
||||
if (binds.buttons.find(key_event.keysym.sym) != binds.buttons.end()) {
|
||||
for (const auto& bind : binds.buttons.at(key_event.keysym.sym)) {
|
||||
if (bind.modifiers.has_necessary_modifiers(key_event.keysym.mod)) {
|
||||
data->button_data.at(bind.pad_data_index) = event.type == SDL_KEYDOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Analog Buttons (useless for keyboards, but here for completeness)
|
||||
if (binds.button_axii.find(key_event.keysym.sym) != binds.button_axii.end()) {
|
||||
for (const auto& bind : binds.button_axii.at(key_event.keysym.sym)) {
|
||||
if (bind.modifiers.has_necessary_modifiers(key_event.keysym.mod)) {
|
||||
data->button_data.at(bind.pad_data_index) = event.type == SDL_KEYDOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Analog Sticks simulating
|
||||
if (binds.analog_axii.find(key_event.keysym.sym) != binds.analog_axii.end()) {
|
||||
for (const auto& bind : binds.analog_axii.at(key_event.keysym.sym)) {
|
||||
if (bind.modifiers.has_necessary_modifiers(key_event.keysym.mod)) {
|
||||
int analog_val = bind.minimum_in_range ? 127 : -127;
|
||||
if (event.type == SDL_KEYDOWN) {
|
||||
analog_val = bind.minimum_in_range ? -127 : 127;
|
||||
}
|
||||
data->analog_data.at(bind.pad_data_index) += analog_val;
|
||||
data->update_analog_sim_tracker(event.type != SDL_KEYDOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for commands
|
||||
if (event.type == SDL_KEYDOWN &&
|
||||
commands.keyboard_binds.find(key_event.keysym.sym) != commands.keyboard_binds.end()) {
|
||||
|
@ -2,22 +2,44 @@
|
||||
|
||||
#include "input_device.h"
|
||||
|
||||
// A simple way to store the current state of the keyboard
|
||||
// if we are polling every frame, we need a point of reference to know
|
||||
// how the PadData has to be modified
|
||||
//
|
||||
// For example if the keyboard no longer has "W" pressed is it:
|
||||
// - because it was pressed before and the user let go of it (adjust analog value)
|
||||
// - because it was never pressed at all (noop!)
|
||||
//
|
||||
// Likewise, when modifiers are let go, we can use this to enumerate through to see what has to be
|
||||
// invalidated
|
||||
struct ActiveKeyboardAction {
|
||||
u32 sdl_keycode;
|
||||
InputBinding binding;
|
||||
std::function<void(std::shared_ptr<PadData>, InputBinding bind)> revert_action;
|
||||
};
|
||||
|
||||
class KeyboardDevice : public InputDevice {
|
||||
public:
|
||||
KeyboardDevice(){};
|
||||
KeyboardDevice(std::shared_ptr<game_settings::InputSettings> settings);
|
||||
~KeyboardDevice() {}
|
||||
|
||||
// Polls the entire state of the keyboard to set the PadData
|
||||
void poll_state(std::shared_ptr<PadData> data);
|
||||
void clear_actions(std::shared_ptr<PadData> data);
|
||||
void process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment,
|
||||
bool ignore_inputs = false) override;
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment) override;
|
||||
void close_device() override{
|
||||
// there is nothing to close
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<ActiveKeyboardAction> m_active_actions = {};
|
||||
// When we assign a bind on a keydown, we have to ignore it's keyup event
|
||||
// for analog binds, or it will adjust the position
|
||||
std::optional<u32> m_ignore_key_on_keyup;
|
||||
|
||||
bool is_action_already_active(const u32 sdl_keycode);
|
||||
};
|
||||
|
@ -7,20 +7,109 @@ MouseDevice::MouseDevice(std::shared_ptr<game_settings::InputSettings> settings)
|
||||
enable_relative_mode(m_control_camera);
|
||||
}
|
||||
|
||||
// I don't trust SDL's key repeat stuff, do it myself to avoid bug reports...(or cause more)
|
||||
bool MouseDevice::is_action_already_active(const u32 sdl_code, const bool player_movement) {
|
||||
for (const auto& action : m_active_actions) {
|
||||
if (!player_movement && action.sdl_mouse_button == sdl_code) {
|
||||
return true;
|
||||
} else if (player_movement && action.player_movement) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MouseDevice::poll_state(std::shared_ptr<PadData> data) {
|
||||
auto& binds = m_settings->mouse_binds;
|
||||
const auto mouse_state = SDL_GetMouseState(NULL, NULL);
|
||||
const auto keyboard_modifier_state = SDL_GetModState();
|
||||
|
||||
// Iterate binds, see if there are any new actions we need to track
|
||||
// - Normal Buttons
|
||||
for (const auto& [sdl_code, bind_list] : binds.buttons) {
|
||||
for (const auto& bind : bind_list) {
|
||||
if (mouse_state & SDL_BUTTON(sdl_code) &&
|
||||
bind.modifiers.has_necessary_modifiers(keyboard_modifier_state) &&
|
||||
!is_action_already_active(sdl_code, false)) {
|
||||
data->button_data.at(bind.pad_data_index) = true; // press the button
|
||||
m_active_actions.push_back(
|
||||
{sdl_code, bind, false, [](std::shared_ptr<PadData> data, InputBinding bind) {
|
||||
data->button_data.at(bind.pad_data_index) = false; // let go of the button
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
// - Analog Buttons (useless for keyboards, but here for completeness)
|
||||
for (const auto& [sdl_code, bind_list] : binds.button_axii) {
|
||||
for (const auto& bind : bind_list) {
|
||||
if (mouse_state & SDL_BUTTON(sdl_code) &&
|
||||
bind.modifiers.has_necessary_modifiers(keyboard_modifier_state) &&
|
||||
!is_action_already_active(sdl_code, false)) {
|
||||
data->button_data.at(bind.pad_data_index) = true; // press the button
|
||||
m_active_actions.push_back(
|
||||
{sdl_code, bind, false, [](std::shared_ptr<PadData> data, InputBinding bind) {
|
||||
data->button_data.at(bind.pad_data_index) = false; // let go of the button
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
// No analog stick simulation, not support via the mouse instead we allow for only this:
|
||||
// WoW style mouse movement, if you have both buttons held down, you will move forward
|
||||
if (m_control_movement && !is_action_already_active(0, true) &&
|
||||
(mouse_state & SDL_BUTTON_LMASK && mouse_state & SDL_BUTTON_RMASK)) {
|
||||
data->analog_data.at(1) += -127; // move forward
|
||||
data->update_analog_sim_tracker(false);
|
||||
ActiveMouseAction action;
|
||||
action.player_movement = true;
|
||||
action.revert_action = [](std::shared_ptr<PadData> data, InputBinding bind) {
|
||||
data->analog_data.at(1) += 127; // stop moving forward
|
||||
data->update_analog_sim_tracker(true);
|
||||
};
|
||||
m_active_actions.push_back(action);
|
||||
}
|
||||
|
||||
// Check if any previously tracked actions are now invalidated by the new state of the keyboard
|
||||
// if so, we'll run their revert code and remove them
|
||||
for (auto it = m_active_actions.begin(); it != m_active_actions.end();) {
|
||||
// Modifiers are easy, if the action required one and it's not pressed anymore, evict it
|
||||
// Alternatively, was the primary key released
|
||||
// Alternatively, alternatively the special case'd mouse movement
|
||||
if (it->player_movement) {
|
||||
if (!(mouse_state & SDL_BUTTON_LMASK) || !(mouse_state & SDL_BUTTON_RMASK)) {
|
||||
it->revert_action(data, it->binding);
|
||||
it = m_active_actions.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
} else {
|
||||
if (!(mouse_state & SDL_BUTTON(it->sdl_mouse_button)) ||
|
||||
!it->binding.modifiers.has_necessary_modifiers(keyboard_modifier_state)) {
|
||||
it->revert_action(data, it->binding);
|
||||
it = m_active_actions.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MouseDevice::clear_actions(std::shared_ptr<PadData> data) {
|
||||
for (auto it = m_active_actions.begin(); it != m_active_actions.end();) {
|
||||
it->revert_action(data, it->binding);
|
||||
it = m_active_actions.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void MouseDevice::process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment,
|
||||
bool ignore_inputs) {
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment) {
|
||||
// We still want to keep track of the cursor location even if we aren't using it for inputs
|
||||
// return early
|
||||
if (event.type == SDL_MOUSEMOTION) {
|
||||
// https://wiki.libsdl.org/SDL2/SDL_MouseMotionEvent
|
||||
m_xcoord = event.motion.x;
|
||||
m_ycoord = event.motion.y;
|
||||
if (ignore_inputs) {
|
||||
// We still want to keep track of the cursor location even if we aren't using it for inputs
|
||||
// return early
|
||||
return;
|
||||
}
|
||||
if (m_control_camera) {
|
||||
const auto xadjust = std::clamp(127 + int(float(event.motion.xrel) * m_xsens), 0, 255);
|
||||
const auto yadjust = std::clamp(127 + int(float(event.motion.yrel) * m_ysens), 0, 255);
|
||||
@ -31,7 +120,7 @@ void MouseDevice::process_event(const SDL_Event& event,
|
||||
// Mouse Button Events
|
||||
// https://wiki.libsdl.org/SDL2/SDL_MouseButtonEvent
|
||||
const auto button_event = event.button;
|
||||
// Always update the internal button tracker, this is for GOAL reasons.
|
||||
// Update the internal mouse tracking, this is for GOAL reasons.
|
||||
switch (button_event.button) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
m_button_status.left = event.type == SDL_MOUSEBUTTONDOWN;
|
||||
@ -50,10 +139,6 @@ void MouseDevice::process_event(const SDL_Event& event,
|
||||
break;
|
||||
}
|
||||
|
||||
if (ignore_inputs) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& binds = m_settings->mouse_binds;
|
||||
|
||||
// Binding re-assignment
|
||||
@ -65,49 +150,6 @@ void MouseDevice::process_event(const SDL_Event& event,
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal Buttons
|
||||
if (binds.buttons.find(button_event.button) != binds.buttons.end()) {
|
||||
for (const auto& bind : binds.buttons.at(button_event.button)) {
|
||||
if (bind.modifiers.has_necessary_modifiers(SDL_GetModState())) {
|
||||
data->button_data.at(bind.pad_data_index) = event.type == SDL_MOUSEBUTTONDOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Analog Buttons (useless for mice, but here for completeness)
|
||||
if (binds.button_axii.find(button_event.button) != binds.button_axii.end()) {
|
||||
for (const auto& bind : binds.button_axii.at(button_event.button)) {
|
||||
data->button_data.at(bind.pad_data_index) = event.type == SDL_MOUSEBUTTONDOWN;
|
||||
}
|
||||
}
|
||||
// Analog Sticks simulating
|
||||
if (binds.analog_axii.find(button_event.button) != binds.analog_axii.end()) {
|
||||
for (const auto& bind : binds.analog_axii.at(button_event.button)) {
|
||||
if (bind.modifiers.has_necessary_modifiers(SDL_GetModState())) {
|
||||
int analog_val = event.type == SDL_MOUSEBUTTONDOWN ? 255 : 127;
|
||||
if (event.type == SDL_MOUSEBUTTONDOWN && bind.minimum_in_range) {
|
||||
analog_val = 0;
|
||||
}
|
||||
data->analog_data.at(bind.pad_data_index) = analog_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_control_movement) {
|
||||
// WoW style mouse movement, if you have both buttons held down, you will move forward
|
||||
const auto mouse_state = SDL_GetMouseState(NULL, NULL);
|
||||
if (!m_was_moving_with_mouse && event.type == SDL_MOUSEBUTTONDOWN &&
|
||||
(mouse_state & SDL_BUTTON_LMASK && mouse_state & SDL_BUTTON_RMASK)) {
|
||||
data->analog_data.at(1) += -127;
|
||||
m_was_moving_with_mouse = true;
|
||||
data->update_analog_sim_tracker(false);
|
||||
} else if (m_was_moving_with_mouse && event.type == SDL_MOUSEBUTTONUP &&
|
||||
((mouse_state & SDL_BUTTON_LMASK) == 0 || (mouse_state & SDL_BUTTON_RMASK) == 0)) {
|
||||
data->analog_data.at(1) += 127;
|
||||
m_was_moving_with_mouse = false;
|
||||
data->update_analog_sim_tracker(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for commands
|
||||
if (event.type == SDL_MOUSEBUTTONDOWN &&
|
||||
commands.mouse_binds.find(button_event.button) != commands.mouse_binds.end()) {
|
||||
|
@ -2,6 +2,15 @@
|
||||
|
||||
#include "input_device.h"
|
||||
|
||||
// See ActiveKeyboardAction, same idea slightly different data
|
||||
struct ActiveMouseAction {
|
||||
u32 sdl_mouse_button;
|
||||
InputBinding binding;
|
||||
bool player_movement =
|
||||
false; // A special one for the mouse, no related binding, hard-coded behaviour!
|
||||
std::function<void(std::shared_ptr<PadData>, InputBinding bind)> revert_action;
|
||||
};
|
||||
|
||||
class MouseDevice : public InputDevice {
|
||||
public:
|
||||
struct MouseButtonStatus {
|
||||
@ -16,11 +25,12 @@ class MouseDevice : public InputDevice {
|
||||
MouseDevice(std::shared_ptr<game_settings::InputSettings> settings);
|
||||
~MouseDevice() {}
|
||||
|
||||
void poll_state(std::shared_ptr<PadData> data);
|
||||
void clear_actions(std::shared_ptr<PadData> data);
|
||||
void process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment,
|
||||
bool ignore_inputs = false) override;
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment) override;
|
||||
void close_device() override{
|
||||
// there is nothing to close
|
||||
};
|
||||
@ -31,15 +41,20 @@ class MouseDevice : public InputDevice {
|
||||
std::pair<int, int> get_mouse_pos() const { return {m_xcoord, m_ycoord}; }
|
||||
MouseButtonStatus get_mouse_button_status() const { return m_button_status; }
|
||||
void set_camera_sens(const float xsens, const float ysens);
|
||||
bool is_camera_being_controlled() { return m_control_camera; }
|
||||
|
||||
private:
|
||||
std::vector<ActiveMouseAction> m_active_actions = {};
|
||||
|
||||
// Track the state of mouse for Game reasons
|
||||
int m_xcoord = 0;
|
||||
int m_ycoord = 0;
|
||||
MouseButtonStatus m_button_status;
|
||||
|
||||
bool m_control_camera = false;
|
||||
bool m_control_movement = false;
|
||||
bool m_was_moving_with_mouse = false;
|
||||
float m_xsens = -15.0;
|
||||
float m_ysens = 10.0;
|
||||
|
||||
bool is_action_already_active(const u32 sdl_keycode, const bool player_movement);
|
||||
};
|
||||
|
@ -88,6 +88,14 @@ void DisplayManager::process_sdl_event(const SDL_Event& event) {
|
||||
// framerate we don't handle that
|
||||
update_curr_display_info();
|
||||
break;
|
||||
case SDL_WINDOWEVENT_ENTER:
|
||||
if (m_input_manager && m_input_manager.value()->auto_hiding_cursor()) {
|
||||
m_input_manager.value()->hide_cursor(true);
|
||||
}
|
||||
break;
|
||||
case SDL_WINDOWEVENT_LEAVE:
|
||||
m_input_manager.value()->hide_cursor(false);
|
||||
break;
|
||||
}
|
||||
} else if (event_type == SDL_DISPLAYEVENT) {
|
||||
// https://wiki.libsdl.org/SDL2/SDL_DisplayEventID
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <variant>
|
||||
|
||||
#include "game/settings/settings.h"
|
||||
#include "game/system/hid/input_manager.h"
|
||||
|
||||
#include "third-party/SDL/include/SDL.h"
|
||||
|
||||
@ -89,9 +90,14 @@ class DisplayManager {
|
||||
void enqueue_set_window_display_mode(WindowDisplayMode mode);
|
||||
void enqueue_set_fullscreen_display_id(int display_id);
|
||||
|
||||
void set_input_manager(std::shared_ptr<InputManager> input_manager) {
|
||||
m_input_manager = input_manager;
|
||||
}
|
||||
|
||||
private:
|
||||
SDL_Window* m_window;
|
||||
game_settings::DisplaySettings m_display_settings;
|
||||
std::optional<std::shared_ptr<InputManager>> m_input_manager;
|
||||
|
||||
std::mutex event_queue_mtx;
|
||||
std::queue<EEDisplayEvent> ee_event_queue;
|
||||
|
@ -193,28 +193,11 @@ std::vector<InputBindingInfo> InputBindingGroups::lookup_button_binds(PadData::B
|
||||
return entry;
|
||||
}
|
||||
|
||||
void InputBindingGroups::assign_analog_bind(u32 sdl_idx,
|
||||
void InputBindingGroups::remove_multiple_binds(
|
||||
u32 sdl_idx,
|
||||
InputBindAssignmentMeta& bind_meta,
|
||||
const std::optional<InputModifiers> modifiers) {
|
||||
// Find out if the PS2 input is already bound, if it is we will do a swap so no input is ever left
|
||||
// unmapped
|
||||
const auto current_binds =
|
||||
lookup_analog_binds((PadData::AnalogIndex)bind_meta.pad_idx, bind_meta.for_analog_minimum);
|
||||
if (analog_axii.find(sdl_idx) != analog_axii.end()) {
|
||||
const auto existing_binds = analog_axii.at(sdl_idx);
|
||||
analog_axii[sdl_idx] = {InputBinding((PadData::AnalogIndex)bind_meta.pad_idx,
|
||||
bind_meta.for_analog_minimum, modifiers)};
|
||||
// there already a bind, so swap (as long as it's not the same key)
|
||||
if (!current_binds.empty() && (u32)current_binds.front().sdl_idx != sdl_idx) {
|
||||
analog_axii[current_binds.front().sdl_idx] = existing_binds;
|
||||
}
|
||||
} else {
|
||||
analog_axii[sdl_idx] = {InputBinding((PadData::AnalogIndex)bind_meta.pad_idx,
|
||||
bind_meta.for_analog_minimum, modifiers)};
|
||||
}
|
||||
// The underlying data structures support multiple binds for the same input, but the UX doesn't
|
||||
// so we have to wipe out any shared bindings after an assignment
|
||||
for (auto it = analog_axii.begin(); it != analog_axii.end();) {
|
||||
std::unordered_map<u32, std::vector<InputBinding>>& bind_map) {
|
||||
for (auto it = bind_map.begin(); it != bind_map.end();) {
|
||||
if (it->first == sdl_idx) {
|
||||
it++;
|
||||
continue;
|
||||
@ -225,7 +208,7 @@ void InputBindingGroups::assign_analog_bind(u32 sdl_idx,
|
||||
bind.minimum_in_range != bind_meta.for_analog_minimum) {
|
||||
continue;
|
||||
}
|
||||
it = analog_axii.erase(it);
|
||||
it = bind_map.erase(it);
|
||||
found_match = true;
|
||||
break;
|
||||
}
|
||||
@ -233,19 +216,99 @@ void InputBindingGroups::assign_analog_bind(u32 sdl_idx,
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::pair<InputBinding, bool>> InputBindingGroups::find_button_bind_from_sdl_idx(
|
||||
u32 sdl_idx,
|
||||
const std::optional<InputModifiers> modifiers) {
|
||||
// We need to check that there isn't a shared SDL key on the button group side
|
||||
for (const auto& [sdl_code, binds] : buttons) {
|
||||
if (sdl_code == sdl_idx) {
|
||||
for (const auto bind : binds) {
|
||||
if (!modifiers || bind.modifiers == modifiers.value()) {
|
||||
std::pair<InputBinding, bool> result = {bind, false};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& [sdl_code, binds] : button_axii) {
|
||||
if (sdl_code == sdl_idx) {
|
||||
for (const auto bind : binds) {
|
||||
if (!modifiers || bind.modifiers == modifiers.value()) {
|
||||
std::pair<InputBinding, bool> result = {bind, true};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void InputBindingGroups::assign_analog_bind(u32 sdl_idx,
|
||||
InputBindAssignmentMeta& bind_meta,
|
||||
const std::optional<InputModifiers> modifiers) {
|
||||
// Find out if the PS2 input is already bound, if it is we will do a swap so no input is ever left
|
||||
// unmapped
|
||||
const auto current_analog_binds =
|
||||
lookup_analog_binds((PadData::AnalogIndex)bind_meta.pad_idx, bind_meta.for_analog_minimum);
|
||||
const auto current_button_bind = find_button_bind_from_sdl_idx(sdl_idx, modifiers);
|
||||
if (analog_axii.find(sdl_idx) != analog_axii.end()) {
|
||||
const auto existing_binds = analog_axii.at(sdl_idx);
|
||||
analog_axii[sdl_idx] = {InputBinding((PadData::AnalogIndex)bind_meta.pad_idx,
|
||||
bind_meta.for_analog_minimum, modifiers)};
|
||||
// there already a bind, so swap (as long as it's not the same key)
|
||||
if (!current_analog_binds.empty() && (u32)current_analog_binds.front().sdl_idx != sdl_idx) {
|
||||
analog_axii[current_analog_binds.front().sdl_idx] = existing_binds;
|
||||
}
|
||||
} else if (!current_analog_binds.empty() && current_button_bind.has_value()) {
|
||||
analog_axii[sdl_idx] = {InputBinding((PadData::AnalogIndex)bind_meta.pad_idx,
|
||||
bind_meta.for_analog_minimum, modifiers)};
|
||||
if (current_button_bind->second) {
|
||||
// button_axii
|
||||
button_axii.erase(sdl_idx);
|
||||
button_axii[current_analog_binds.front().sdl_idx] = {current_button_bind->first};
|
||||
} else {
|
||||
// button
|
||||
buttons.erase(sdl_idx);
|
||||
buttons[current_analog_binds.front().sdl_idx] = {current_button_bind->first};
|
||||
}
|
||||
} else {
|
||||
analog_axii[sdl_idx] = {InputBinding((PadData::AnalogIndex)bind_meta.pad_idx,
|
||||
bind_meta.for_analog_minimum, modifiers)};
|
||||
}
|
||||
remove_multiple_binds(sdl_idx, bind_meta, analog_axii);
|
||||
// Invalidate lookup cache
|
||||
m_analog_lookup.clear();
|
||||
m_button_lookup.clear();
|
||||
bind_meta.assigned = true;
|
||||
}
|
||||
|
||||
std::optional<std::pair<InputBinding, bool>> InputBindingGroups::find_analog_bind_from_sdl_idx(
|
||||
u32 sdl_idx,
|
||||
const std::optional<InputModifiers> modifiers) {
|
||||
// We need to check that there isn't a shared SDL key on the button group side
|
||||
for (const auto& [sdl_code, binds] : analog_axii) {
|
||||
if (sdl_code == sdl_idx) {
|
||||
for (const auto bind : binds) {
|
||||
if (!modifiers || bind.modifiers == modifiers.value()) {
|
||||
std::pair<InputBinding, bool> result = {bind, false};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void InputBindingGroups::assign_button_bind(u32 sdl_idx,
|
||||
InputBindAssignmentMeta& bind_meta,
|
||||
const bool analog_button,
|
||||
const std::optional<InputModifiers> modifiers) {
|
||||
// Find out if the PS2 input is already bound, if it is we will do a swap so no input is ever left
|
||||
// unmapped
|
||||
const auto current_binds = lookup_button_binds((PadData::ButtonIndex)bind_meta.pad_idx);
|
||||
const auto current_button_binds = lookup_button_binds((PadData::ButtonIndex)bind_meta.pad_idx);
|
||||
const auto current_analog_bind = find_analog_bind_from_sdl_idx(sdl_idx, modifiers);
|
||||
if (buttons.find(sdl_idx) != buttons.end()) {
|
||||
const auto existing_binds = buttons.at(sdl_idx);
|
||||
if (analog_button) {
|
||||
@ -254,13 +317,21 @@ void InputBindingGroups::assign_button_bind(u32 sdl_idx,
|
||||
buttons[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
|
||||
}
|
||||
// there already a bind, so swap (as long as it's not the same key)
|
||||
if (!current_binds.empty() && current_binds.front().sdl_idx != (s32)sdl_idx) {
|
||||
if (current_binds.front().analog_button) {
|
||||
button_axii[current_binds.front().sdl_idx] = existing_binds;
|
||||
if (!current_button_binds.empty() && current_button_binds.front().sdl_idx != (s32)sdl_idx) {
|
||||
if (current_button_binds.front().analog_button) {
|
||||
button_axii[current_button_binds.front().sdl_idx] = existing_binds;
|
||||
} else {
|
||||
buttons[current_binds.front().sdl_idx] = existing_binds;
|
||||
buttons[current_button_binds.front().sdl_idx] = existing_binds;
|
||||
}
|
||||
}
|
||||
} else if (!current_button_binds.empty() && current_analog_bind.has_value()) {
|
||||
if (analog_button) {
|
||||
button_axii[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
|
||||
} else {
|
||||
buttons[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
|
||||
}
|
||||
analog_axii.erase(sdl_idx);
|
||||
analog_axii[current_button_binds.front().sdl_idx] = {current_analog_bind->first};
|
||||
} else {
|
||||
if (analog_button) {
|
||||
button_axii[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
|
||||
@ -268,47 +339,11 @@ void InputBindingGroups::assign_button_bind(u32 sdl_idx,
|
||||
buttons[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
|
||||
}
|
||||
}
|
||||
// The underlying data structures support multiple binds for the same input, but the UX doesn't
|
||||
// so we have to wipe out any shared bindings after an assignment
|
||||
for (auto it = buttons.begin(); it != buttons.end();) {
|
||||
if (it->first == sdl_idx) {
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
bool found_match = false;
|
||||
for (const auto& bind : it->second) {
|
||||
if (bind.pad_data_index != bind_meta.pad_idx) {
|
||||
continue;
|
||||
}
|
||||
it = buttons.erase(it);
|
||||
found_match = true;
|
||||
break;
|
||||
}
|
||||
if (!found_match) {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
for (auto it = button_axii.begin(); it != button_axii.end();) {
|
||||
if (it->first == sdl_idx) {
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
bool found_match = false;
|
||||
for (const auto& bind : it->second) {
|
||||
if (bind.pad_data_index != bind_meta.pad_idx) {
|
||||
continue;
|
||||
}
|
||||
it = button_axii.erase(it);
|
||||
found_match = true;
|
||||
break;
|
||||
}
|
||||
if (!found_match) {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
remove_multiple_binds(sdl_idx, bind_meta, buttons);
|
||||
remove_multiple_binds(sdl_idx, bind_meta, button_axii);
|
||||
// Invalidate lookup cache
|
||||
m_button_lookup.clear();
|
||||
m_analog_lookup.clear();
|
||||
bind_meta.assigned = true;
|
||||
}
|
||||
|
||||
|
@ -155,6 +155,14 @@ struct InputModifiers {
|
||||
bool need_alt = false;
|
||||
|
||||
bool has_necessary_modifiers(const u16 key_modifiers) const;
|
||||
|
||||
bool operator==(const InputModifiers& other) const {
|
||||
if (need_shift == other.need_shift && need_ctrl == other.need_ctrl &&
|
||||
need_meta == other.need_meta && need_alt == other.need_alt) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void to_json(json& j, const InputModifiers& obj);
|
||||
@ -287,6 +295,17 @@ struct InputBindingGroups {
|
||||
// correspond with the PS2 value. Such as when remapping a key so you can unbind overlapping binds
|
||||
std::unordered_map<BindCacheKey, std::vector<InputBindingInfo>, hash_name> m_analog_lookup;
|
||||
std::unordered_map<BindCacheKey, std::vector<InputBindingInfo>, hash_name> m_button_lookup;
|
||||
// The underlying data structures support multiple binds for the same input, but the UX doesn't
|
||||
// so we have to wipe out any shared bindings after an assignment
|
||||
void remove_multiple_binds(u32 sdl_idx,
|
||||
InputBindAssignmentMeta& bind_meta,
|
||||
std::unordered_map<u32, std::vector<InputBinding>>& bind_map);
|
||||
std::optional<std::pair<InputBinding, bool>> find_button_bind_from_sdl_idx(
|
||||
u32 sdl_idx,
|
||||
const std::optional<InputModifiers> modifiers);
|
||||
std::optional<std::pair<InputBinding, bool>> find_analog_bind_from_sdl_idx(
|
||||
u32 sdl_idx,
|
||||
const std::optional<InputModifiers> modifiers);
|
||||
};
|
||||
|
||||
void to_json(json& j, const InputBindingGroups& obj);
|
||||
@ -306,6 +325,16 @@ extern const InputBindingGroups DEFAULT_MOUSE_BINDS;
|
||||
/// an arbitrary lambda (with no return value) when triggered.
|
||||
///
|
||||
/// An example of these would be taking a screenshot or save-state actions
|
||||
// TODO - there is currently a bad UX if commands overlap with user bindings. For example if "F2"
|
||||
// is for screenshots and the user binds that to "X" it will work, but you're going to take a
|
||||
// screenshot everytime you jump.
|
||||
//
|
||||
// We probably don't want that but fundamentally this is a problem because the commands are
|
||||
// hard-coded and not customizable so even if we prevented such binds -- there would not be a good
|
||||
// user-facing reason why the bind failed to take.
|
||||
//
|
||||
// So there are some potential solutions but this doesn't feel high priority and this was always an
|
||||
// issue.
|
||||
struct CommandBinding {
|
||||
enum Source { CONTROLLER, KEYBOARD, MOUSE };
|
||||
|
||||
|
@ -124,6 +124,8 @@ void InputManager::hide_cursor(const bool hide_cursor) {
|
||||
if (hide_cursor == m_mouse_currently_hidden) {
|
||||
return;
|
||||
}
|
||||
// NOTE - seems like an SDL bug, but the cursor will be visible / locked to the center of the
|
||||
// screen if you use the 'start menu' to exit the window / return to it (atleast in windowed mode)
|
||||
auto ok = SDL_ShowCursor(hide_cursor ? SDL_DISABLE : SDL_ENABLE);
|
||||
if (ok < 0) {
|
||||
sdl_util::log_error("Unable to show/hide mouse cursor");
|
||||
@ -132,9 +134,7 @@ void InputManager::hide_cursor(const bool hide_cursor) {
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::process_sdl_event(const SDL_Event& event,
|
||||
const bool ignore_mouse,
|
||||
const bool ignore_kb) {
|
||||
void InputManager::process_sdl_event(const SDL_Event& event) {
|
||||
// Detect controller connections and disconnects
|
||||
if (sdl_util::is_any_event_type(event.type,
|
||||
{SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEREMOVED})) {
|
||||
@ -142,18 +142,11 @@ void InputManager::process_sdl_event(const SDL_Event& event,
|
||||
refresh_device_list();
|
||||
}
|
||||
|
||||
if (!m_ignored_device_last_frame && (ignore_mouse || ignore_kb)) {
|
||||
clear_inputs();
|
||||
m_ignored_device_last_frame = true;
|
||||
} else if (m_ignored_device_last_frame && !ignore_mouse && !ignore_kb) {
|
||||
m_ignored_device_last_frame = false;
|
||||
}
|
||||
|
||||
if (m_data.find(m_keyboard_and_mouse_port) != m_data.end()) {
|
||||
m_keyboard.process_event(event, m_command_binds, m_data.at(m_keyboard_and_mouse_port),
|
||||
m_waiting_for_bind, ignore_kb || !m_keyboard_enabled);
|
||||
m_waiting_for_bind);
|
||||
m_mouse.process_event(event, m_command_binds, m_data.at(m_keyboard_and_mouse_port),
|
||||
m_waiting_for_bind, ignore_mouse || !m_mouse_enabled);
|
||||
m_waiting_for_bind);
|
||||
}
|
||||
|
||||
// Send event to active controller device
|
||||
@ -168,11 +161,15 @@ void InputManager::process_sdl_event(const SDL_Event& event,
|
||||
// Clear the binding assignment if we got one
|
||||
if (m_waiting_for_bind && m_waiting_for_bind->assigned) {
|
||||
stop_waiting_for_bind();
|
||||
// NOTE - this is a total hack, but it's to prevent immediately re-assigning the "confirmation"
|
||||
// bind if you use a source that is polled
|
||||
// TODO: There's a correct way to do this....figure it out eventually
|
||||
m_skip_polling_for_n_frames = 60;
|
||||
}
|
||||
|
||||
// Adjust mouse cursor visibility
|
||||
if (m_auto_hide_mouse) {
|
||||
if (event.type == SDL_MOUSEMOTION) {
|
||||
if (event.type == SDL_MOUSEMOTION && !m_mouse.is_camera_being_controlled()) {
|
||||
hide_cursor(false);
|
||||
} else if (event.type == SDL_KEYDOWN || event.type == SDL_CONTROLLERBUTTONDOWN) {
|
||||
hide_cursor(true);
|
||||
@ -180,6 +177,45 @@ void InputManager::process_sdl_event(const SDL_Event& event,
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::poll_keyboard_data() {
|
||||
if (m_keyboard_enabled && m_skip_polling_for_n_frames <= 0 && !m_waiting_for_bind) {
|
||||
if (m_data.find(m_keyboard_and_mouse_port) != m_data.end()) {
|
||||
m_keyboard.poll_state(m_data.at(m_keyboard_and_mouse_port));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::clear_keyboard_actions() {
|
||||
if (m_keyboard_enabled) {
|
||||
if (m_data.find(m_keyboard_and_mouse_port) != m_data.end()) {
|
||||
m_keyboard.clear_actions(m_data.at(m_keyboard_and_mouse_port));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::poll_mouse_data() {
|
||||
if (m_mouse_enabled && m_skip_polling_for_n_frames <= 0 && !m_waiting_for_bind) {
|
||||
if (m_data.find(m_keyboard_and_mouse_port) != m_data.end()) {
|
||||
m_mouse.poll_state(m_data.at(m_keyboard_and_mouse_port));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::clear_mouse_actions() {
|
||||
if (m_mouse_enabled && !m_waiting_for_bind) {
|
||||
if (m_data.find(m_keyboard_and_mouse_port) != m_data.end()) {
|
||||
m_mouse.clear_actions(m_data.at(m_keyboard_and_mouse_port));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::finish_polling() {
|
||||
m_skip_polling_for_n_frames--;
|
||||
if (m_skip_polling_for_n_frames < 0) {
|
||||
m_skip_polling_for_n_frames = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::process_ee_events() {
|
||||
const std::lock_guard<std::mutex> lock(m_event_queue_mtx);
|
||||
// Fully process any events from the EE
|
||||
|
@ -46,7 +46,13 @@ class InputManager {
|
||||
~InputManager();
|
||||
|
||||
// Propagate and handle the SDL event, ignored it if it's not relevant
|
||||
void process_sdl_event(const SDL_Event& event, const bool ignore_mouse, const bool ignore_kb);
|
||||
void process_sdl_event(const SDL_Event& event);
|
||||
void poll_keyboard_data();
|
||||
void clear_keyboard_actions();
|
||||
void poll_mouse_data();
|
||||
void clear_mouse_actions();
|
||||
// Any cleanup that should happen after polling has completed for this frame
|
||||
void finish_polling();
|
||||
/// Any event coming from the EE thread that interacts directly with SDL should be enqueued as an
|
||||
/// event so it can be ran from the proper thread context (the graphics thread)
|
||||
void process_ee_events();
|
||||
@ -92,6 +98,8 @@ class InputManager {
|
||||
void stop_waiting_for_bind() { m_waiting_for_bind = std::nullopt; }
|
||||
void set_camera_sens(const float xsens, const float ysens);
|
||||
void reset_input_bindings_to_defaults(const int port, const InputDeviceType device_type);
|
||||
bool auto_hiding_cursor() { return m_auto_hide_mouse || m_mouse.is_camera_being_controlled(); }
|
||||
void hide_cursor(const bool hide_cursor);
|
||||
|
||||
private:
|
||||
std::mutex m_event_queue_mtx;
|
||||
@ -119,10 +127,9 @@ class InputManager {
|
||||
/// Collection of arbitrary commands to run on user actions
|
||||
CommandBindingGroups m_command_binds;
|
||||
|
||||
bool m_ignored_device_last_frame = false;
|
||||
|
||||
bool m_keyboard_enabled = true;
|
||||
bool m_mouse_enabled = false;
|
||||
int m_skip_polling_for_n_frames = 0;
|
||||
bool m_auto_hide_mouse = true;
|
||||
bool m_mouse_currently_hidden = false;
|
||||
bool m_ignore_background_controller_events = false;
|
||||
@ -134,7 +141,6 @@ class InputManager {
|
||||
std::optional<InputBindAssignmentMeta> m_waiting_for_bind = std::nullopt;
|
||||
|
||||
void refresh_device_list();
|
||||
void hide_cursor(const bool hide_cursor);
|
||||
void clear_inputs();
|
||||
|
||||
void ignore_background_controller_events(const bool ignore);
|
||||
|
Loading…
Reference in New Issue
Block a user