shadPS4/src/input/input_handler.h
2024-11-13 17:09:00 +01:00

290 lines
9.1 KiB
C++

// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "array"
#include "map"
#include "unordered_set"
#include "string"
#include "common/types.h"
#include "common/logging/log.h"
#include "core/libraries/pad/pad.h"
#include "input/controller.h"
#include "SDL3/SDL_events.h"
#include "SDL3/SDL_timer.h"
// +1 and +2 is taken
#define SDL_MOUSE_WHEEL_UP SDL_EVENT_MOUSE_WHEEL + 3
#define SDL_MOUSE_WHEEL_DOWN SDL_EVENT_MOUSE_WHEEL + 4
#define SDL_MOUSE_WHEEL_LEFT SDL_EVENT_MOUSE_WHEEL + 5
#define SDL_MOUSE_WHEEL_RIGHT SDL_EVENT_MOUSE_WHEEL + 7
#define LEFTJOYSTICK_HALFMODE 0x00010000
#define RIGHTJOYSTICK_HALFMODE 0x00020000
namespace Input {
using Input::Axis;
using Libraries::Pad::OrbisPadButtonDataOffset;
struct AxisMapping {
Axis axis;
int value; // Value to set for key press (+127 or -127 for movement)
};
// i strongly suggest you collapse these maps
const std::map<std::string, u32> string_to_cbutton_map = {
{"triangle", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE},
{"circle", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE},
{"cross", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS},
{"square", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE},
{"l1", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L1},
{"l2", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2},
{"r1", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1},
{"r2", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2},
{"l3", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3},
{"r3", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3},
{"options", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS},
{"touchpad", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD},
{"up", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP},
{"down", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN},
{"left", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT},
{"right", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT},
{"leftjoystick_halfmode", LEFTJOYSTICK_HALFMODE},
{"rightjoystick_halfmode", RIGHTJOYSTICK_HALFMODE},
};
const std::map<std::string, AxisMapping> string_to_axis_map = {
{"axis_left_x_plus", {Input::Axis::LeftX, 127}},
{"axis_left_x_minus", {Input::Axis::LeftX, -127}},
{"axis_left_y_plus", {Input::Axis::LeftY, 127}},
{"axis_left_y_minus", {Input::Axis::LeftY, -127}},
{"axis_right_x_plus", {Input::Axis::RightX, 127}},
{"axis_right_x_minus", {Input::Axis::RightX, -127}},
{"axis_right_y_plus", {Input::Axis::RightY, 127}},
{"axis_right_y_minus", {Input::Axis::RightY, -127}},
};
const std::map<std::string, u32> string_to_keyboard_key_map = {
{"a", SDLK_A},
{"b", SDLK_B},
{"c", SDLK_C},
{"d", SDLK_D},
{"e", SDLK_E},
{"f", SDLK_F},
{"g", SDLK_G},
{"h", SDLK_H},
{"i", SDLK_I},
{"j", SDLK_J},
{"k", SDLK_K},
{"l", SDLK_L},
{"m", SDLK_M},
{"n", SDLK_N},
{"o", SDLK_O},
{"p", SDLK_P},
{"q", SDLK_Q},
{"r", SDLK_R},
{"s", SDLK_S},
{"t", SDLK_T},
{"u", SDLK_U},
{"v", SDLK_V},
{"w", SDLK_W},
{"x", SDLK_X},
{"y", SDLK_Y},
{"z", SDLK_Z},
{"0", SDLK_0},
{"1", SDLK_1},
{"2", SDLK_2},
{"3", SDLK_3},
{"4", SDLK_4},
{"5", SDLK_5},
{"6", SDLK_6},
{"7", SDLK_7},
{"8", SDLK_8},
{"9", SDLK_9},
{"kp0", SDLK_KP_0},
{"kp1", SDLK_KP_1},
{"kp2", SDLK_KP_2},
{"kp3", SDLK_KP_3},
{"kp4", SDLK_KP_4},
{"kp5", SDLK_KP_5},
{"kp6", SDLK_KP_6},
{"kp7", SDLK_KP_7},
{"kp8", SDLK_KP_8},
{"kp9", SDLK_KP_9},
{"comma", SDLK_COMMA},
{"period", SDLK_PERIOD},
{"question", SDLK_QUESTION},
{"semicolon", SDLK_SEMICOLON},
{"minus", SDLK_MINUS},
{"underscore", SDLK_UNDERSCORE},
{"lparenthesis", SDLK_LEFTPAREN},
{"rparenthesis", SDLK_RIGHTPAREN},
{"lbracket", SDLK_LEFTBRACKET},
{"rbracket", SDLK_RIGHTBRACKET},
{"lbrace", SDLK_LEFTBRACE},
{"rbrace", SDLK_RIGHTBRACE},
{"backslash", SDLK_BACKSLASH},
{"dash", SDLK_SLASH},
{"enter", SDLK_RETURN},
{"space", SDLK_SPACE},
{"tab", SDLK_TAB},
{"backspace", SDLK_BACKSPACE},
{"escape", SDLK_ESCAPE},
{"left", SDLK_LEFT},
{"right", SDLK_RIGHT},
{"up", SDLK_UP},
{"down", SDLK_DOWN},
{"lctrl", SDLK_LCTRL},
{"rctrl", SDLK_RCTRL},
{"lshift", SDLK_LSHIFT},
{"rshift", SDLK_RSHIFT},
{"lalt", SDLK_LALT},
{"ralt", SDLK_RALT},
{"lmeta", SDLK_LGUI},
{"rmeta", SDLK_RGUI},
{"lwin", SDLK_LGUI},
{"rwin", SDLK_RGUI},
{"home", SDLK_HOME},
{"end", SDLK_END},
{"pgup", SDLK_PAGEUP},
{"pgdown", SDLK_PAGEDOWN},
{"leftbutton", SDL_BUTTON_LEFT},
{"rightbutton", SDL_BUTTON_RIGHT},
{"middlebutton", SDL_BUTTON_MIDDLE},
{"sidebuttonback", SDL_BUTTON_X1},
{"sidebuttonforward", SDL_BUTTON_X2},
{"mousewheelup", SDL_MOUSE_WHEEL_UP},
{"mousewheeldown", SDL_MOUSE_WHEEL_DOWN},
{"mousewheelleft", SDL_MOUSE_WHEEL_LEFT},
{"mousewheelright", SDL_MOUSE_WHEEL_RIGHT},
{"kpperiod", SDLK_KP_PERIOD},
{"kpcomma", SDLK_KP_COMMA},
{"kpdivide", SDLK_KP_DIVIDE},
{"kpmultiply", SDLK_KP_MULTIPLY},
{"kpminus", SDLK_KP_MINUS},
{"kpplus", SDLK_KP_PLUS},
{"kpenter", SDLK_KP_ENTER},
{"kpequals", SDLK_KP_EQUALS},
{"capslock", SDLK_CAPSLOCK},
};
// i wrapped it in a function so I can collapse it
std::string_view getDefaultKeyboardConfig();
void parseInputConfig(const std::string game_id);
class InputBinding {
public:
u32 key1, key2, key3;
InputBinding(u32 k1 = SDLK_UNKNOWN, u32 k2 = SDLK_UNKNOWN, u32 k3 = SDLK_UNKNOWN) {
// we format the keys so comparing them will be very fast, because we will only have to compare 3 sorted elements,
// where the only possible duplicate item is 0
// duplicate entries get changed to one original, one null
if(k1 == k2 && k1 != SDLK_UNKNOWN) { k2 = 0; }
if(k1 == k3 && k1 != SDLK_UNKNOWN) { k3 = 0; }
if(k3 == k2 && k2 != SDLK_UNKNOWN) { k2 = 0; }
// this sorts them
if (k1 <= k2 && k1 <= k3) {
key1 = k1;
if (k2 <= k3) { key2 = k2; key3 = k3; }
else { key2 = k3; key3 = k2; }
} else if (k2 <= k1 && k2 <= k3) {
key1 = k2;
if (k1 <= k3) { key2 = k1; key3 = k3; }
else { key2 = k3; key3 = k1; }
} else {
key1 = k3;
if (k1 <= k2) { key2 = k1; key3 = k2; }
else { key2 = k2; key3 = k1; }
}
}
// copy ctor
InputBinding(const InputBinding& o) : key1(o.key1), key2(o.key2), key3(o.key3) {}
inline bool operator==(const InputBinding& o) {
// 0 = SDLK_UNKNOWN aka unused slot
return (key3 == o.key3 || key3 == 0 || o.key3 == 0) &&
(key2 == o.key2 || key2 == 0 || o.key2 == 0) &&
(key1 == o.key1 || key1 == 0 || o.key1 == 0);
// it is already very fast,
// but reverse order makes it check the actual keys first instead of possible 0-s,
// potenially skipping the later expressions of the three-way AND
}
inline bool isEmpty() {
return key1 == 0 && key2 == 0 && key3 == 0;
}
// returns a u32 based on the event type (keyboard, mouse buttons, or wheel)
static u32 getInputIDFromEvent(const SDL_Event& e) {
switch(e.type) {
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
return e.key.key;
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP:
return (u32)e.button.button;
default:
// todo: add the rest (wheel)
return 0;
}
}
};
class ControllerOutput {
static GameController* controller;
public:
static void setControllerOutputController(GameController* c);
u32 button;
Axis axis;
ControllerOutput(const u32 b, Axis a = Axis::AxisMax) {
button = b;
axis = a;
}
ControllerOutput(const ControllerOutput& o) : button(o.button), axis(o.axis) {}
inline bool operator==(const ControllerOutput& o) const { // fucking consts everywhere
return button == o.button && axis == o.axis;
}
inline bool operator!=(const ControllerOutput& o) const {
return button != o.button || axis != o.axis;
}
void update(bool pressed, int axis_direction = 0);
};
class BindingConnection {
public:
InputBinding binding;
ControllerOutput* output;
int axis_value;
BindingConnection(InputBinding b, ControllerOutput* out, int a_v = 0) {
binding = b;
axis_value = 0;
// todo: check if out is in the allowed array
output = out;
}
};
// todo
//extern ControllerOutput output_array[];
//extern std::map<BindingConnection, ControllerOutput&> new_binding_map;
extern u32 pressed_keys[];
// Check if the 3 key input is currently active.
bool checkForInputDown(InputBinding i);
// Add/remove the input that generated the event to/from the held keys container.
void updatePressedKeys(u32 button, bool is_pressed);
void updateMouse(GameController* controller);
// Polls the mouse for changes, and simulates joystick movement from it.
Uint32 mousePolling(void* param, Uint32 id, Uint32 interval);
}