Files
jak-project/game/system/hid/devices/keyboard.cpp

156 lines
6.4 KiB
C++

#include "keyboard.h"
#include "game/system/hid/sdl_util.h"
KeyboardDevice::KeyboardDevice(std::shared_ptr<game_settings::InputSettings> settings) {
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) {
if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) {
const auto key_event = event.key;
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;
return;
}
auto& binds = m_settings->keyboard_binds;
// Binding re-assignment
if (bind_assignment) {
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) {
m_ignore_key_on_keyup = key_event.keysym.sym;
binds.assign_analog_bind(key_event.keysym.sym, bind_assignment.value(),
InputModifiers(key_event.keysym.mod));
} else {
binds.assign_button_bind(key_event.keysym.sym, bind_assignment.value(), false,
InputModifiers(key_event.keysym.mod));
}
} else if (event.type == SDL_KEYUP) {
// modifiers are instead inspected on a KEYUP, however if it's one of the keys
// for triggering the binding assignment, and it's the first time we've seen it -- we ignore
// it
if (!bind_assignment->seen_confirm_up) {
for (const auto& confirm_bind : bind_assignment->keyboard_confirmation_binds) {
if (confirm_bind.sdl_idx == key_event.keysym.sym) {
bind_assignment->seen_confirm_up = true;
return;
}
}
}
// otherwise, set the bind
if (bind_assignment->for_analog) {
binds.assign_analog_bind(key_event.keysym.sym, bind_assignment.value(),
InputModifiers(key_event.keysym.mod));
} else {
binds.assign_button_bind(key_event.keysym.sym, bind_assignment.value(), false,
InputModifiers(key_event.keysym.mod));
}
}
return;
}
// Check for commands
if (event.type == SDL_KEYDOWN &&
commands.keyboard_binds.find(key_event.keysym.sym) != commands.keyboard_binds.end()) {
for (const auto& command : commands.keyboard_binds.at(key_event.keysym.sym)) {
if (command.modifiers.has_necessary_modifiers(key_event.keysym.mod)) {
command.command();
}
}
}
}
}