jak-project/game/system/hid/input_manager.cpp

550 lines
22 KiB
C++
Raw Normal View History

#include "input_manager.h"
#include <atomic>
#include <cmath>
#include "input_manager.h"
#include "sdl_util.h"
#include "common/global_profiler/GlobalProfiler.h"
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "common/util/FileUtil.h"
#include "game/graphics/pipelines/opengl.h"
#include "game/runtime.h"
#include "third-party/imgui/imgui.h"
InputManager::InputManager()
// Load user settings
: m_settings(std::make_shared<game_settings::InputSettings>(game_settings::InputSettings())) {
prof().instant_event("ROOT");
{
auto p = scoped_prof("input_manager::init");
m_settings->load_settings();
{
auto p = scoped_prof("input_manager::init::sdl_init_subsystem");
// initializing the controllers on startup can sometimes take a very long time
// so we isolate that to here instead
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0) {
sdl_util::log_error(
"Could not initialize SDL Controller support, controllers will not work!");
}
}
// Update to latest controller DB file
std::string mapping_path =
(file_util::get_jak_project_dir() / "game" / "assets" / "sdl_controller_db.txt").string();
if (file_util::file_exists(mapping_path)) {
SDL_GameControllerAddMappingsFromFile(mapping_path.c_str());
} else {
lg::error("Could not find SDL Controller DB at path `{}`", mapping_path);
}
// Initialize atleast 2 ports, because that's normal for Jak
// more will be allocated if more controllers are found
m_data[0] = std::make_shared<PadData>();
m_data[1] = std::make_shared<PadData>();
m_keyboard = KeyboardDevice(m_settings);
m_mouse = MouseDevice(m_settings);
if (m_data.find(m_keyboard_and_mouse_port) == m_data.end()) {
m_data[m_keyboard_and_mouse_port] = std::make_shared<PadData>();
}
m_command_binds = CommandBindingGroups();
refresh_device_list();
ignore_background_controller_events(false);
hide_cursor(m_auto_hide_mouse);
}
}
InputManager::~InputManager() {
prof().instant_event("ROOT");
{
auto p = scoped_prof("input_manager::destroy");
for (auto& device : m_available_controllers) {
device->close_device();
}
m_settings->save_settings();
}
}
void InputManager::refresh_device_list() {
prof().instant_event("ROOT");
{
auto p = scoped_prof("input_manager::refresh_device_list");
m_available_controllers.clear();
m_controller_port_mapping.clear();
// Enumerate devices
// TODO - if this was done on a separate thread, there would be no hitch in the game thread
// but of course, that presents other synchronization challenges.
const auto num_joysticks = SDL_NumJoysticks();
if (num_joysticks > 0) {
for (int i = 0; i < num_joysticks; i++) {
if (!SDL_IsGameController(i)) {
lg::error("Controller with device id {} is not avaiable via the GameController API", i);
continue;
}
auto controller = std::make_shared<GameController>(i, m_settings);
if (!controller->is_loaded()) {
lg::error("Unable to successfully connect to GameController with id {}, skipping", i);
continue;
}
m_available_controllers.push_back(controller);
// By default, controller port mapping is on a first-come-first-served basis
//
// However, we will use previously saved controller port mappings to take precedence
// For example, if you previous set your PS5 controller to be port 0, then even
// if another controller is detected first, the PS5 controller should be assigned as
// expected.
if (m_settings->controller_port_mapping.find(controller->get_guid()) !=
m_settings->controller_port_mapping.end()) {
// Though it's possible for a user to assign multiple controllers to the same port, so the
// last one wins
m_controller_port_mapping[m_settings->controller_port_mapping.at(
controller->get_guid())] = i;
} else {
m_controller_port_mapping[m_available_controllers.size() - 1] = i;
m_settings->controller_port_mapping[controller->get_guid()] =
m_available_controllers.size() - 1;
}
// Allocate a PadData if this is a new port
if (m_data.find(i) == m_data.end()) {
m_data[i] = std::make_shared<PadData>();
}
}
// If the controller that was last selected to be port 0 is around, prioritize it
if (!m_settings->last_selected_controller_guid.empty()) {
for (size_t i = 0; i < m_available_controllers.size(); i++) {
const auto& controller_guid = m_available_controllers.at(i)->get_guid();
if (controller_guid == m_settings->last_selected_controller_guid) {
m_controller_port_mapping[0] = i;
m_settings->controller_port_mapping[controller_guid] = 0;
break;
}
}
}
}
if (m_available_controllers.empty()) {
lg::warn(
"No active game controllers could be found or loaded successfully - inputs will not "
"work!");
game: disable keyboard input by default, give users a way to enable it via the imgui menu (#3295) It was narrowed down recently that a lot of people have issues with the controller input because of Steam Input working as intended. Steam Input can be configured to replicate controller inputs as keyboard inputs (for example, pressing X on your controller presses Enter on the keyboard). This results in the problem of "jumping pauses the game" and similar issues. This is a consequence of the intended behaviour of the game listening to all input sources at the same time. Since the vast majority of players are using controllers over keyboards, it makes sense to disable the keyboard input by default to solve this problem. However that makes things awkward for users that want to use the keyboard (how do they enable the setting). The solution is a new imgui option in the settings menu: ![Screenshot 2024-01-07 141224](https://github.com/open-goal/jak-project/assets/13153231/6f9ffa2d-be7a-433d-b698-15b70210e97e) **Known issue that I don't care about** -- in Jak 1's menu code, since the flags are controlled by pointers to values instead of a lambda like in jak 2, the menu won't update live with the imgui option. This has no functional impact and I don't care enough to fix it. I also made the pc-settings.gc file persist on first load if the file wasn't found. Hopefully this helps diagnose the support issues related to the black screen. # Why not just ignore the keyboard inputs for a period of time? This won't work, the keyboard is polled every frame. Therefore if you hold down the X button on your controller, steam is continuously signaling that `Enter` is held down on the keyboard. Yes it would be possible to completely disable the keyboard while the controller is being used, but this defeats the purpose of creating an input system that allows multiple input sources at the same time. With an explicit option, not only can the user decide the behaviour they want (do they want the keyboard ignored or simultaneously listened to) but we avoid breaking strange edge-cases in usage leading to never ending complexity: - ie. imagine steam input sends events to the mouse, well you can't disable the mouse while using the keyboard because most times people are using mouse and keyboard - ie. a user that wants to hold a direction with the keyboard and press buttons on the controller in tandem (something i frequently do while TAS'ing, to move in a perfect straight line)
2024-02-23 18:19:07 -05:00
m_settings->keyboard_temp_enabled = true;
} else {
lg::info("Found {} controllers", m_available_controllers.size());
game: disable keyboard input by default, give users a way to enable it via the imgui menu (#3295) It was narrowed down recently that a lot of people have issues with the controller input because of Steam Input working as intended. Steam Input can be configured to replicate controller inputs as keyboard inputs (for example, pressing X on your controller presses Enter on the keyboard). This results in the problem of "jumping pauses the game" and similar issues. This is a consequence of the intended behaviour of the game listening to all input sources at the same time. Since the vast majority of players are using controllers over keyboards, it makes sense to disable the keyboard input by default to solve this problem. However that makes things awkward for users that want to use the keyboard (how do they enable the setting). The solution is a new imgui option in the settings menu: ![Screenshot 2024-01-07 141224](https://github.com/open-goal/jak-project/assets/13153231/6f9ffa2d-be7a-433d-b698-15b70210e97e) **Known issue that I don't care about** -- in Jak 1's menu code, since the flags are controlled by pointers to values instead of a lambda like in jak 2, the menu won't update live with the imgui option. This has no functional impact and I don't care enough to fix it. I also made the pc-settings.gc file persist on first load if the file wasn't found. Hopefully this helps diagnose the support issues related to the black screen. # Why not just ignore the keyboard inputs for a period of time? This won't work, the keyboard is polled every frame. Therefore if you hold down the X button on your controller, steam is continuously signaling that `Enter` is held down on the keyboard. Yes it would be possible to completely disable the keyboard while the controller is being used, but this defeats the purpose of creating an input system that allows multiple input sources at the same time. With an explicit option, not only can the user decide the behaviour they want (do they want the keyboard ignored or simultaneously listened to) but we avoid breaking strange edge-cases in usage leading to never ending complexity: - ie. imagine steam input sends events to the mouse, well you can't disable the mouse while using the keyboard because most times people are using mouse and keyboard - ie. a user that wants to hold a direction with the keyboard and press buttons on the controller in tandem (something i frequently do while TAS'ing, to move in a perfect straight line)
2024-02-23 18:19:07 -05:00
m_settings->keyboard_temp_enabled = false;
}
}
}
void InputManager::enqueue_ignore_background_controller_events(const bool ignore) {
const std::lock_guard<std::mutex> lock(m_event_queue_mtx);
ee_event_queue.push({EEInputEventType::IGNORE_BACKGROUND_CONTROLLER_EVENTS, ignore, {}, {}, {}});
}
void InputManager::ignore_background_controller_events(const bool ignore) {
m_ignore_background_controller_events = ignore;
// TODO - ignoring return value (atleast log it)
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, ignore ? "0" : "1");
}
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");
} else {
m_mouse_currently_hidden = hide_cursor;
}
}
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})) {
lg::info("Controller added or removed. refreshing controller device list");
refresh_device_list();
}
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);
m_mouse.process_event(event, m_command_binds, m_data.at(m_keyboard_and_mouse_port),
m_waiting_for_bind);
}
// Send event to active controller device
// This goes last so it takes precedence
for (const auto& [port, controller_idx] : m_controller_port_mapping) {
if (m_data.find(port) != m_data.end() && (int)m_available_controllers.size() > controller_idx) {
m_available_controllers.at(controller_idx)
->process_event(event, m_command_binds, m_data.at(port), m_waiting_for_bind);
}
}
// 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 && !m_mouse.is_camera_being_controlled()) {
hide_cursor(false);
} else if (event.type == SDL_KEYDOWN || event.type == SDL_CONTROLLERBUTTONDOWN) {
hide_cursor(true);
}
}
}
void InputManager::poll_keyboard_data() {
game: disable keyboard input by default, give users a way to enable it via the imgui menu (#3295) It was narrowed down recently that a lot of people have issues with the controller input because of Steam Input working as intended. Steam Input can be configured to replicate controller inputs as keyboard inputs (for example, pressing X on your controller presses Enter on the keyboard). This results in the problem of "jumping pauses the game" and similar issues. This is a consequence of the intended behaviour of the game listening to all input sources at the same time. Since the vast majority of players are using controllers over keyboards, it makes sense to disable the keyboard input by default to solve this problem. However that makes things awkward for users that want to use the keyboard (how do they enable the setting). The solution is a new imgui option in the settings menu: ![Screenshot 2024-01-07 141224](https://github.com/open-goal/jak-project/assets/13153231/6f9ffa2d-be7a-433d-b698-15b70210e97e) **Known issue that I don't care about** -- in Jak 1's menu code, since the flags are controlled by pointers to values instead of a lambda like in jak 2, the menu won't update live with the imgui option. This has no functional impact and I don't care enough to fix it. I also made the pc-settings.gc file persist on first load if the file wasn't found. Hopefully this helps diagnose the support issues related to the black screen. # Why not just ignore the keyboard inputs for a period of time? This won't work, the keyboard is polled every frame. Therefore if you hold down the X button on your controller, steam is continuously signaling that `Enter` is held down on the keyboard. Yes it would be possible to completely disable the keyboard while the controller is being used, but this defeats the purpose of creating an input system that allows multiple input sources at the same time. With an explicit option, not only can the user decide the behaviour they want (do they want the keyboard ignored or simultaneously listened to) but we avoid breaking strange edge-cases in usage leading to never ending complexity: - ie. imagine steam input sends events to the mouse, well you can't disable the mouse while using the keyboard because most times people are using mouse and keyboard - ie. a user that wants to hold a direction with the keyboard and press buttons on the controller in tandem (something i frequently do while TAS'ing, to move in a perfect straight line)
2024-02-23 18:19:07 -05:00
if (is_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() {
game: disable keyboard input by default, give users a way to enable it via the imgui menu (#3295) It was narrowed down recently that a lot of people have issues with the controller input because of Steam Input working as intended. Steam Input can be configured to replicate controller inputs as keyboard inputs (for example, pressing X on your controller presses Enter on the keyboard). This results in the problem of "jumping pauses the game" and similar issues. This is a consequence of the intended behaviour of the game listening to all input sources at the same time. Since the vast majority of players are using controllers over keyboards, it makes sense to disable the keyboard input by default to solve this problem. However that makes things awkward for users that want to use the keyboard (how do they enable the setting). The solution is a new imgui option in the settings menu: ![Screenshot 2024-01-07 141224](https://github.com/open-goal/jak-project/assets/13153231/6f9ffa2d-be7a-433d-b698-15b70210e97e) **Known issue that I don't care about** -- in Jak 1's menu code, since the flags are controlled by pointers to values instead of a lambda like in jak 2, the menu won't update live with the imgui option. This has no functional impact and I don't care enough to fix it. I also made the pc-settings.gc file persist on first load if the file wasn't found. Hopefully this helps diagnose the support issues related to the black screen. # Why not just ignore the keyboard inputs for a period of time? This won't work, the keyboard is polled every frame. Therefore if you hold down the X button on your controller, steam is continuously signaling that `Enter` is held down on the keyboard. Yes it would be possible to completely disable the keyboard while the controller is being used, but this defeats the purpose of creating an input system that allows multiple input sources at the same time. With an explicit option, not only can the user decide the behaviour they want (do they want the keyboard ignored or simultaneously listened to) but we avoid breaking strange edge-cases in usage leading to never ending complexity: - ie. imagine steam input sends events to the mouse, well you can't disable the mouse while using the keyboard because most times people are using mouse and keyboard - ie. a user that wants to hold a direction with the keyboard and press buttons on the controller in tandem (something i frequently do while TAS'ing, to move in a perfect straight line)
2024-02-23 18:19:07 -05:00
if (is_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
while (!ee_event_queue.empty()) {
const auto& evt = ee_event_queue.front();
switch (evt.type) {
case EEInputEventType::IGNORE_BACKGROUND_CONTROLLER_EVENTS:
ignore_background_controller_events(std::get<bool>(evt.param1));
break;
case EEInputEventType::UPDATE_RUMBLE:
update_rumble(std::get<int>(evt.param1), std::get<u8>(evt.param2),
std::get<u8>(evt.param3));
break;
case EEInputEventType::SET_CONTROLLER_LED:
set_controller_led(std::get<int>(evt.param1), std::get<u8>(evt.param2),
std::get<u8>(evt.param3), std::get<u8>(evt.param4));
break;
case EEInputEventType::UPDATE_MOUSE_OPTIONS:
update_mouse_options(std::get<bool>(evt.param1), std::get<bool>(evt.param2),
std::get<bool>(evt.param3));
break;
case EEInputEventType::SET_AUTO_HIDE_MOUSE:
set_auto_hide_mouse(std::get<bool>(evt.param1));
break;
}
ee_event_queue.pop();
}
}
void InputManager::register_command(const CommandBinding::Source source,
const CommandBinding bind) {
switch (source) {
case CommandBinding::Source::CONTROLLER:
if (m_command_binds.controller_binds.find(bind.host_key) ==
m_command_binds.controller_binds.end()) {
m_command_binds.controller_binds[bind.host_key] = {};
}
m_command_binds.controller_binds[bind.host_key].push_back(bind);
break;
case CommandBinding::Source::KEYBOARD:
if (m_command_binds.keyboard_binds.find(bind.host_key) ==
m_command_binds.keyboard_binds.end()) {
m_command_binds.keyboard_binds[bind.host_key] = {};
}
m_command_binds.keyboard_binds[bind.host_key].push_back(bind);
break;
case CommandBinding::Source::MOUSE:
if (m_command_binds.mouse_binds.find(bind.host_key) == m_command_binds.mouse_binds.end()) {
m_command_binds.mouse_binds[bind.host_key] = {};
}
m_command_binds.mouse_binds[bind.host_key].push_back(bind);
break;
}
}
std::optional<std::shared_ptr<PadData>> InputManager::get_current_data(const int port) const {
if (m_data.find(port) == m_data.end()) {
return {};
}
return m_data.at(port);
}
std::string InputManager::get_controller_name(const int controller_id) {
if ((size_t)controller_id >= m_available_controllers.size()) {
return "";
}
return m_available_controllers.at(controller_id)->get_name();
}
std::string InputManager::get_current_bind(const int port,
const InputDeviceType device_type,
const bool buttons,
const int input_idx,
const bool analog_for_minimum) {
std::vector<InputBindingInfo> binding_info;
switch (device_type) {
case InputDeviceType::CONTROLLER:
if (m_controller_port_mapping.find(port) != m_controller_port_mapping.end() &&
m_controller_port_mapping.at(port) < (int)m_available_controllers.size() &&
m_settings->controller_binds.find(
m_available_controllers.at(m_controller_port_mapping.at(port))->get_guid()) !=
m_settings->controller_binds.end()) {
binding_info =
m_settings->controller_binds
.at(m_available_controllers.at(m_controller_port_mapping.at(port))->get_guid())
.lookup_button_binds((PadData::ButtonIndex)input_idx);
}
break;
case InputDeviceType::KEYBOARD:
if (!buttons) {
binding_info = m_settings->keyboard_binds.lookup_analog_binds(
(PadData::AnalogIndex)input_idx, analog_for_minimum);
} else {
binding_info =
m_settings->keyboard_binds.lookup_button_binds((PadData::ButtonIndex)input_idx);
}
break;
case InputDeviceType::MOUSE:
binding_info = m_settings->mouse_binds.lookup_button_binds((PadData::ButtonIndex)input_idx);
break;
}
if (binding_info.empty()) {
return "";
}
return binding_info.front().host_name;
}
int InputManager::get_controller_index(const int port) {
if (m_controller_port_mapping.find(port) == m_controller_port_mapping.end()) {
return 0;
}
return m_controller_port_mapping.at(port);
}
void InputManager::set_controller_for_port(const int controller_id, const int port) {
if (controller_id < (int)m_available_controllers.size()) {
// Reset inputs as this device won't be able to be read from again!
clear_inputs();
auto& controller = m_available_controllers.at(controller_id);
m_controller_port_mapping[port] = controller_id;
m_settings->controller_port_mapping[controller->get_guid()] = port;
// NOTE - only tracking port 0 for now
if (port == 0) {
m_settings->last_selected_controller_guid = controller->get_guid();
}
m_settings->save_settings();
}
}
bool InputManager::controller_has_led(const int port) {
if (m_controller_port_mapping.find(port) == m_controller_port_mapping.end()) {
return false;
}
const auto id = m_controller_port_mapping.at(port);
if (id >= (int)m_available_controllers.size()) {
return false;
}
return m_available_controllers.at(id)->has_led();
}
bool InputManager::controller_has_rumble(const int port) {
if (m_controller_port_mapping.find(port) == m_controller_port_mapping.end()) {
return false;
}
const auto id = m_controller_port_mapping.at(port);
if (id >= (int)m_available_controllers.size()) {
return false;
}
return m_available_controllers.at(id)->has_rumble();
}
void InputManager::enqueue_set_controller_led(const int port,
const u8 red,
const u8 green,
const u8 blue) {
const std::lock_guard<std::mutex> lock(m_event_queue_mtx);
ee_event_queue.push({EEInputEventType::SET_CONTROLLER_LED, port, red, green, blue});
}
void InputManager::set_controller_led(const int port, const u8 red, const u8 green, const u8 blue) {
if (m_controller_port_mapping.find(port) == m_controller_port_mapping.end()) {
return;
}
const auto id = m_controller_port_mapping.at(port);
if (id >= (int)m_available_controllers.size()) {
return;
}
m_available_controllers.at(id)->set_led(red, green, blue);
}
void InputManager::enqueue_update_rumble(const int port,
const u8 low_intensity,
const u8 high_intensity) {
const std::lock_guard<std::mutex> lock(m_event_queue_mtx);
ee_event_queue.push({EEInputEventType::UPDATE_RUMBLE, port, low_intensity, high_intensity, {}});
}
int InputManager::update_rumble(int port, u8 low_intensity, u8 high_intensity) {
if (m_controller_port_mapping.find(port) == m_controller_port_mapping.end()) {
return 0;
}
return m_available_controllers.at(m_controller_port_mapping.at(port))
->update_rumble(low_intensity, high_intensity);
}
void InputManager::enable_keyboard(const bool enabled) {
game: disable keyboard input by default, give users a way to enable it via the imgui menu (#3295) It was narrowed down recently that a lot of people have issues with the controller input because of Steam Input working as intended. Steam Input can be configured to replicate controller inputs as keyboard inputs (for example, pressing X on your controller presses Enter on the keyboard). This results in the problem of "jumping pauses the game" and similar issues. This is a consequence of the intended behaviour of the game listening to all input sources at the same time. Since the vast majority of players are using controllers over keyboards, it makes sense to disable the keyboard input by default to solve this problem. However that makes things awkward for users that want to use the keyboard (how do they enable the setting). The solution is a new imgui option in the settings menu: ![Screenshot 2024-01-07 141224](https://github.com/open-goal/jak-project/assets/13153231/6f9ffa2d-be7a-433d-b698-15b70210e97e) **Known issue that I don't care about** -- in Jak 1's menu code, since the flags are controlled by pointers to values instead of a lambda like in jak 2, the menu won't update live with the imgui option. This has no functional impact and I don't care enough to fix it. I also made the pc-settings.gc file persist on first load if the file wasn't found. Hopefully this helps diagnose the support issues related to the black screen. # Why not just ignore the keyboard inputs for a period of time? This won't work, the keyboard is polled every frame. Therefore if you hold down the X button on your controller, steam is continuously signaling that `Enter` is held down on the keyboard. Yes it would be possible to completely disable the keyboard while the controller is being used, but this defeats the purpose of creating an input system that allows multiple input sources at the same time. With an explicit option, not only can the user decide the behaviour they want (do they want the keyboard ignored or simultaneously listened to) but we avoid breaking strange edge-cases in usage leading to never ending complexity: - ie. imagine steam input sends events to the mouse, well you can't disable the mouse while using the keyboard because most times people are using mouse and keyboard - ie. a user that wants to hold a direction with the keyboard and press buttons on the controller in tandem (something i frequently do while TAS'ing, to move in a perfect straight line)
2024-02-23 18:19:07 -05:00
m_settings->keyboard_enabled = enabled;
if (!m_settings->keyboard_enabled) {
// Reset inputs as this device won't be able to be read from again!
clear_inputs();
}
}
void InputManager::enqueue_update_mouse_options(const bool enabled,
const bool control_camera,
const bool control_movement) {
const std::lock_guard<std::mutex> lock(m_event_queue_mtx);
ee_event_queue.push(
{EEInputEventType::UPDATE_MOUSE_OPTIONS, enabled, control_camera, control_movement, {}});
}
void InputManager::update_mouse_options(const bool enabled,
const bool control_camera,
const bool control_movement) {
m_mouse_enabled = enabled;
if (!m_mouse_enabled) {
// Reset inputs as this device won't be able to be read from again!
clear_inputs();
}
// Switch relevant flags over related to the mouse
m_mouse.enable_camera_control(enabled && control_camera);
m_mouse.enable_movement_control(enabled && control_movement);
}
void InputManager::set_wait_for_bind(const InputDeviceType device_type,
const bool for_analog,
const bool for_minimum_analog,
const int input_idx) {
m_waiting_for_bind = InputBindAssignmentMeta();
m_waiting_for_bind->device_type = device_type;
m_waiting_for_bind->pad_idx = input_idx;
m_waiting_for_bind->for_analog = for_analog;
m_waiting_for_bind->for_analog_minimum = for_minimum_analog;
m_waiting_for_bind->seen_keyboard_confirm_up = false;
m_waiting_for_bind->keyboard_confirmation_binds =
m_settings->keyboard_binds.lookup_button_binds(PadData::CROSS);
m_waiting_for_bind->seen_controller_confirm_neutral = false;
if (m_controller_port_mapping.find(0) != m_controller_port_mapping.end() &&
m_controller_port_mapping.at(0) < (int)m_available_controllers.size() &&
m_settings->controller_binds.find(
m_available_controllers.at(m_controller_port_mapping.at(0))->get_guid()) !=
m_settings->controller_binds.end()) {
m_waiting_for_bind->controller_confirmation_binds =
m_settings->controller_binds
.at(m_available_controllers.at(m_controller_port_mapping.at(0))->get_guid())
.lookup_button_binds(PadData::CROSS);
}
if (g_game_version == GameVersion::Jak1) {
auto keyboard_circle_binds = m_settings->keyboard_binds.lookup_button_binds(PadData::CIRCLE);
m_waiting_for_bind->keyboard_confirmation_binds.insert(
m_waiting_for_bind->keyboard_confirmation_binds.end(), keyboard_circle_binds.begin(),
keyboard_circle_binds.end());
if (m_controller_port_mapping.find(0) != m_controller_port_mapping.end() &&
m_controller_port_mapping.at(0) < (int)m_available_controllers.size() &&
m_settings->controller_binds.find(
m_available_controllers.at(m_controller_port_mapping.at(0))->get_guid()) !=
m_settings->controller_binds.end()) {
auto controller_circle_binds =
m_settings->controller_binds
.at(m_available_controllers.at(m_controller_port_mapping.at(0))->get_guid())
.lookup_button_binds(PadData::CIRCLE);
m_waiting_for_bind->controller_confirmation_binds.insert(
m_waiting_for_bind->controller_confirmation_binds.end(), controller_circle_binds.begin(),
controller_circle_binds.end());
}
}
}
void InputManager::set_camera_sens(const float xsens, const float ysens) {
m_mouse.set_camera_sens(xsens, ysens);
}
void InputManager::reset_input_bindings_to_defaults(const int port,
const InputDeviceType device_type) {
switch (device_type) {
case InputDeviceType::CONTROLLER:
if (m_controller_port_mapping.find(port) != m_controller_port_mapping.end() &&
m_controller_port_mapping.at(port) < (int)m_available_controllers.size() &&
m_settings->controller_binds.find(
m_available_controllers.at(m_controller_port_mapping.at(port))->get_guid()) !=
m_settings->controller_binds.end()) {
m_settings->controller_binds
.at(m_available_controllers.at(m_controller_port_mapping.at(port))->get_guid())
.set_bindings(DEFAULT_CONTROLLER_BINDS);
}
break;
case InputDeviceType::KEYBOARD:
m_settings->keyboard_binds.set_bindings(DEFAULT_KEYBOARD_BINDS);
break;
case InputDeviceType::MOUSE:
m_settings->mouse_binds.set_bindings(DEFAULT_MOUSE_BINDS);
break;
}
}
void InputManager::enqueue_set_auto_hide_mouse(const bool auto_hide_mouse) {
const std::lock_guard<std::mutex> lock(m_event_queue_mtx);
ee_event_queue.push({EEInputEventType::SET_AUTO_HIDE_MOUSE, auto_hide_mouse, {}, {}, {}});
}
void InputManager::set_auto_hide_mouse(const bool auto_hide_mouse) {
m_auto_hide_mouse = auto_hide_mouse;
if (!auto_hide_mouse) {
hide_cursor(false);
}
}
void InputManager::clear_inputs() {
// Reset inputs as this device won't be able to be read from again!
for (auto& [port, data] : m_data) {
data->clear();
}
}