mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-04 08:22:32 +00:00
Mouse polling, CMakeLists, and basic framework
This commit is contained in:
parent
7a95c27b2c
commit
1884b39eb8
@ -724,6 +724,8 @@ set(IMGUI src/imgui/imgui_config.h
|
|||||||
|
|
||||||
set(INPUT src/input/controller.cpp
|
set(INPUT src/input/controller.cpp
|
||||||
src/input/controller.h
|
src/input/controller.h
|
||||||
|
src/input/input_handler.cpp
|
||||||
|
src/input/input_handler.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(EMULATOR src/emulator.cpp
|
set(EMULATOR src/emulator.cpp
|
||||||
|
@ -1,7 +1,383 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
namespace InputHandler {
|
#include "input_handler.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <SDL3/SDL_events.h>
|
||||||
|
#include <SDL3/SDL_timer.h>
|
||||||
|
|
||||||
|
#include "common/config.h"
|
||||||
|
#include "common/io_file.h"
|
||||||
|
#include "common/path_util.h"
|
||||||
|
#include "common/version.h"
|
||||||
|
#include "common/elf_info.h"
|
||||||
|
#include "input/controller.h"
|
||||||
|
|
||||||
|
namespace Input {
|
||||||
|
/*
|
||||||
|
Project structure:
|
||||||
|
n to m connection between inputs and outputs
|
||||||
|
Keyup and keydown events update a dynamic list* of u32 'flags' (what is currently in the list is 'pressed')
|
||||||
|
On every event, after flag updates, we check for every input binding -> controller output pair if all their flags are 'on'
|
||||||
|
If not, disable; if so, enable them.
|
||||||
|
For axes, we gather their data into a struct cumulatively from all inputs,
|
||||||
|
then after we checked all of those, we update them all at once.
|
||||||
|
Wheel inputs generate a timer that doesn't turn off their outputs automatically, but push a userevent to do so.
|
||||||
|
|
||||||
|
What structs are needed?
|
||||||
|
InputBinding(key1, key2, key3)
|
||||||
|
ControllerOutput(button, axis) - we only need a const array of these, and one of the attr-s is always 0
|
||||||
|
BindingConnection(inputBinding (member), controllerOutput (ref to the array element))
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Flags and values for varying purposes
|
||||||
|
// todo: can we change these?
|
||||||
|
int mouse_joystick_binding = 0;
|
||||||
|
float mouse_deadzone_offset = 0.5, mouse_speed = 1, mouse_speed_offset = 0.1250;
|
||||||
|
Uint32 mouse_polling_id = 0;
|
||||||
|
bool mouse_enabled = false, leftjoystick_halfmode = false, rightjoystick_halfmode = false;
|
||||||
|
|
||||||
|
std::string_view getDefaultKeyboardConfig() {
|
||||||
|
static std::string_view default_config =
|
||||||
|
R"(## SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
## SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#This is the default keybinding config
|
||||||
|
#To change per-game configs, modify the CUSAXXXXX.ini files
|
||||||
|
#To change the default config that applies to new games without already existing configs, modify default.ini
|
||||||
|
#If you don't like certain mappings, delete, change or comment them out.
|
||||||
|
#You can add any amount of KBM keybinds to a single controller input,
|
||||||
|
#but you can use each KBM keybind for one controller input.
|
||||||
|
|
||||||
|
#Keybinds used by the emulator (these are unchangeable):
|
||||||
|
#F11 : fullscreen
|
||||||
|
#F10 : FPS counter
|
||||||
|
#F9 : toggle mouse-to-joystick input
|
||||||
|
# (it overwrites everything else to that joystick, so this is required)
|
||||||
|
#F8 : reparse keyboard input(this)
|
||||||
|
|
||||||
|
#This is a mapping for Bloodborne, inspired by other Souls titles on PC.
|
||||||
|
|
||||||
|
#Specifies which joystick the mouse movement controls.
|
||||||
|
mouse_to_joystick = right;
|
||||||
|
|
||||||
|
#Use healing item, change status in inventory
|
||||||
|
triangle = f;
|
||||||
|
#Dodge, back in inventory
|
||||||
|
circle = space;
|
||||||
|
#Interact, select item in inventory
|
||||||
|
cross = e;
|
||||||
|
#Use quick item, remove item in inventory
|
||||||
|
square = r;
|
||||||
|
|
||||||
|
#Emergency extra bullets
|
||||||
|
up = w, lalt;
|
||||||
|
up = mousewheelup;
|
||||||
|
#Change quick item
|
||||||
|
down = s, lalt;
|
||||||
|
down = mousewheeldown;
|
||||||
|
#Change weapon in left hand
|
||||||
|
left = a, lalt;
|
||||||
|
left = mousewheelleft;
|
||||||
|
#Change weapon in right hand
|
||||||
|
right = d, lalt;
|
||||||
|
right = mousewheelright;
|
||||||
|
#Change into 'inventory mode', so you don't have to hold lalt every time you go into menus
|
||||||
|
modkey_toggle = i, lalt;
|
||||||
|
|
||||||
|
#Menu
|
||||||
|
options = escape;
|
||||||
|
#Gestures
|
||||||
|
touchpad = g;
|
||||||
|
|
||||||
|
#Transform
|
||||||
|
l1 = rightbutton, lshift;
|
||||||
|
#Shoot
|
||||||
|
r1 = leftbutton;
|
||||||
|
#Light attack
|
||||||
|
l2 = rightbutton;
|
||||||
|
#Heavy attack
|
||||||
|
r2 = leftbutton, lshift;
|
||||||
|
#Does nothing
|
||||||
|
l3 = x;
|
||||||
|
#Center cam, lock on
|
||||||
|
r3 = q;
|
||||||
|
r3 = middlebutton;
|
||||||
|
|
||||||
|
#Axis mappings
|
||||||
|
#Move
|
||||||
|
axis_left_x_minus = a;
|
||||||
|
axis_left_x_plus = d;
|
||||||
|
axis_left_y_minus = w;
|
||||||
|
axis_left_y_plus = s;
|
||||||
|
#Change to 'walk mode' by holding the following key:
|
||||||
|
leftjoystick_halfmode = lctrl;
|
||||||
|
)";
|
||||||
|
return default_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseInputConfig(const std::string game_id = "") {
|
||||||
|
// Read configuration file of the game, and if it doesn't exist, generate it from default
|
||||||
|
// If that doesn't exist either, generate that from getDefaultConfig() and try again
|
||||||
|
// If even the folder is missing, we start with that.
|
||||||
|
|
||||||
|
// maybe extract this?
|
||||||
|
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "kbmConfig";
|
||||||
|
const auto config_file = config_dir / (game_id + ".ini");
|
||||||
|
const auto default_config_file = config_dir / "default.ini";
|
||||||
|
|
||||||
|
// Ensure the config directory exists
|
||||||
|
if (!std::filesystem::exists(config_dir)) {
|
||||||
|
std::filesystem::create_directories(config_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try loading the game-specific config file
|
||||||
|
if (!std::filesystem::exists(config_file)) {
|
||||||
|
// If game-specific config doesn't exist, check for the default config
|
||||||
|
if (!std::filesystem::exists(default_config_file)) {
|
||||||
|
// If the default config is also missing, create it from getDefaultConfig()
|
||||||
|
const auto default_config = getDefaultKeyboardConfig();
|
||||||
|
std::ofstream default_config_stream(default_config_file);
|
||||||
|
if (default_config_stream) {
|
||||||
|
default_config_stream << default_config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If default config now exists, copy it to the game-specific config file
|
||||||
|
if (std::filesystem::exists(default_config_file) && !game_id.empty()) {
|
||||||
|
std::filesystem::copy(default_config_file, config_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we just called the function to generate the directory and the default .ini
|
||||||
|
if (game_id.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we reset these here so in case the user fucks up or doesn't include this we can fall back to
|
||||||
|
// default
|
||||||
|
mouse_deadzone_offset = 0.5;
|
||||||
|
mouse_speed = 1;
|
||||||
|
mouse_speed_offset = 0.125;
|
||||||
|
//old_button_map.clear();
|
||||||
|
//old_axis_map.clear();
|
||||||
|
//old_key_to_modkey_toggle_map.clear();
|
||||||
|
int lineCount = 0;
|
||||||
|
|
||||||
|
std::ifstream file(config_file);
|
||||||
|
std::string line = "";
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
lineCount++;
|
||||||
|
// strip the ; and whitespace
|
||||||
|
line.erase(std::remove(line.begin(), line.end(), ' '), line.end());
|
||||||
|
if (line[line.length() - 1] == ';') {
|
||||||
|
line = line.substr(0, line.length() - 1);
|
||||||
|
}
|
||||||
|
// Ignore comment lines
|
||||||
|
if (line.empty() || line[0] == '#') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Split the line by '='
|
||||||
|
std::size_t equal_pos = line.find('=');
|
||||||
|
if (equal_pos == std::string::npos) {
|
||||||
|
std::cerr << "Invalid line format at line: " << lineCount << " data: " << line
|
||||||
|
<< std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string before_equals = line.substr(0, equal_pos);
|
||||||
|
std::string after_equals = line.substr(equal_pos + 1);
|
||||||
|
std::size_t comma_pos = after_equals.find(',');
|
||||||
|
|
||||||
|
// new data type construcor here
|
||||||
|
// todo
|
||||||
|
|
||||||
|
// special check for mouse to joystick input
|
||||||
|
if (before_equals == "mouse_to_joystick") {
|
||||||
|
if (after_equals == "left") {
|
||||||
|
mouse_joystick_binding = 1;
|
||||||
|
} else if (after_equals == "right") {
|
||||||
|
mouse_joystick_binding = 2;
|
||||||
|
} else {
|
||||||
|
mouse_joystick_binding = 0; // default to 'none' or invalid
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// mod key toggle
|
||||||
|
if (before_equals == "modkey_toggle") {
|
||||||
|
if (comma_pos != std::string::npos) {
|
||||||
|
// handle key-to-key toggling (separate list?)
|
||||||
|
// todo
|
||||||
|
}
|
||||||
|
std::cerr << "Invalid line format at line: " << lineCount << " data: " << line
|
||||||
|
<< std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// todo
|
||||||
|
/* og parsing
|
||||||
|
// first we parse the binding, and if its wrong, we skip to the next line
|
||||||
|
if (comma_pos != std::string::npos) {
|
||||||
|
// Handle key + modifier
|
||||||
|
std::string key = after_equals.substr(0, comma_pos);
|
||||||
|
std::string mod = after_equals.substr(comma_pos + 1);
|
||||||
|
|
||||||
|
auto key_it = string_to_keyboard_key_map.find(key);
|
||||||
|
auto mod_it = string_to_keyboard_mod_key_map.find(mod);
|
||||||
|
|
||||||
|
if (key_it != string_to_keyboard_key_map.end() &&
|
||||||
|
mod_it != string_to_keyboard_mod_key_map.end()) {
|
||||||
|
binding.key = key_it->second;
|
||||||
|
binding.modifier = mod_it->second;
|
||||||
|
} else if (before_equals == "mouse_movement_params") {
|
||||||
|
// handle mouse movement params
|
||||||
|
float p1 = 0.5, p2 = 1, p3 = 0.125;
|
||||||
|
std::size_t second_comma_pos = after_equals.find(',');
|
||||||
|
try {
|
||||||
|
p1 = std::stof(key);
|
||||||
|
p2 = std::stof(mod.substr(0, second_comma_pos));
|
||||||
|
p3 = std::stof(mod.substr(second_comma_pos + 1));
|
||||||
|
mouse_deadzone_offset = p1;
|
||||||
|
mouse_speed = p2;
|
||||||
|
mouse_speed_offset = p3;
|
||||||
|
} catch (...) {
|
||||||
|
// fallback to default values
|
||||||
|
mouse_deadzone_offset = 0.5;
|
||||||
|
mouse_speed = 1;
|
||||||
|
mouse_speed_offset = 0.125;
|
||||||
|
std::cerr << "Parsing error while parsing kbm inputs at line " << lineCount
|
||||||
|
<< " line data: " << line << "\n";
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Syntax error while parsing kbm inputs at line " << lineCount
|
||||||
|
<< " line data: " << line << "\n";
|
||||||
|
continue; // skip
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Just a key without modifier
|
||||||
|
auto key_it = string_to_keyboard_key_map.find(after_equals);
|
||||||
|
if (key_it != string_to_keyboard_key_map.end()) {
|
||||||
|
binding.key = key_it->second;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Syntax error while parsing kbm inputs at line " << lineCount
|
||||||
|
<< " line data: " << line << "\n";
|
||||||
|
continue; // skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for axis mapping (example: axis_left_x_plus)
|
||||||
|
auto axis_it = string_to_axis_map.find(before_equals);
|
||||||
|
auto button_it = string_to_cbutton_map.find(before_equals);
|
||||||
|
if (axis_it != string_to_axis_map.end()) {
|
||||||
|
old_axis_map[binding] = axis_it->second;
|
||||||
|
} else if (button_it != string_to_cbutton_map.end()) {
|
||||||
|
old_button_map[binding] = button_it->second;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Syntax error while parsing kbm inputs at line " << lineCount
|
||||||
|
<< " line data: " << line << "\n";
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: add an init for this
|
||||||
|
GameController* ControllerOutput::controller = nullptr;
|
||||||
|
void ControllerOutput::setControllerOutputController(GameController* c) {
|
||||||
|
ControllerOutput::controller = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerOutput::update(bool pressed, int axis_direction) {
|
||||||
|
float touchpad_x = 0;
|
||||||
|
Input::Axis axis = Input::Axis::AxisMax;
|
||||||
|
if(button != 0){
|
||||||
|
switch (button) {
|
||||||
|
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2:
|
||||||
|
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2:
|
||||||
|
axis = (button == OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2) ? Input::Axis::TriggerRight
|
||||||
|
: Input::Axis::TriggerLeft;
|
||||||
|
controller->Axis(0, axis, Input::GetAxis(0, 0x80, pressed ? 255 : 0));
|
||||||
|
break;
|
||||||
|
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD:
|
||||||
|
touchpad_x = Config::getBackButtonBehavior() == "left" ? 0.25f
|
||||||
|
: Config::getBackButtonBehavior() == "right" ? 0.75f
|
||||||
|
: 0.5f;
|
||||||
|
controller->SetTouchpadState(0, true, touchpad_x, 0.5f);
|
||||||
|
controller->CheckButton(0, button, pressed);
|
||||||
|
break;
|
||||||
|
default: // is a normal key
|
||||||
|
controller->CheckButton(0, button, pressed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (axis != Axis::AxisMax) {
|
||||||
|
float multiplier = 1.0;
|
||||||
|
switch (axis) {
|
||||||
|
case Input::Axis::LeftX:
|
||||||
|
case Input::Axis::LeftY:
|
||||||
|
multiplier = leftjoystick_halfmode ? 0.5 : 1.0;
|
||||||
|
break;
|
||||||
|
case Input::Axis::RightX:
|
||||||
|
case Input::Axis::RightY:
|
||||||
|
multiplier = rightjoystick_halfmode ? 0.5 : 1.0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int output_value = (pressed ? axis_value : 0) * multiplier;
|
||||||
|
int ax = Input::GetAxis(-0x80, 0x80, output_value);
|
||||||
|
controller->Axis(0, axis, ax);
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Input, "Controller output with no values detected!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateMouse(GameController* controller) {
|
||||||
|
if (!mouse_enabled)
|
||||||
|
return;
|
||||||
|
Axis axis_x, axis_y;
|
||||||
|
switch (mouse_joystick_binding) {
|
||||||
|
case 1:
|
||||||
|
axis_x = Axis::LeftX;
|
||||||
|
axis_y = Axis::LeftY;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
axis_x = Axis::RightX;
|
||||||
|
axis_y = Axis::RightY;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
default:
|
||||||
|
return; // no update needed
|
||||||
|
}
|
||||||
|
|
||||||
|
float d_x = 0, d_y = 0;
|
||||||
|
SDL_GetRelativeMouseState(&d_x, &d_y);
|
||||||
|
|
||||||
|
float output_speed =
|
||||||
|
SDL_clamp((sqrt(d_x * d_x + d_y * d_y) + mouse_speed_offset * 128) * mouse_speed,
|
||||||
|
mouse_deadzone_offset * 128, 128.0);
|
||||||
|
|
||||||
|
float angle = atan2(d_y, d_x);
|
||||||
|
float a_x = cos(angle) * output_speed, a_y = sin(angle) * output_speed;
|
||||||
|
|
||||||
|
if (d_x != 0 && d_y != 0) {
|
||||||
|
controller->Axis(0, axis_x, Input::GetAxis(-0x80, 0x80, a_x));
|
||||||
|
controller->Axis(0, axis_y, Input::GetAxis(-0x80, 0x80, a_y));
|
||||||
|
} else {
|
||||||
|
controller->Axis(0, axis_x, Input::GetAxis(-0x80, 0x80, 0));
|
||||||
|
controller->Axis(0, axis_y, Input::GetAxis(-0x80, 0x80, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint32 mousePolling(void* param, Uint32 id, Uint32 interval) {
|
||||||
|
auto* data = (GameController*)param;
|
||||||
|
updateMouse(data);
|
||||||
|
return interval;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -3,7 +3,284 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace InputHandler {
|
#include "array"
|
||||||
|
#include "map"
|
||||||
|
#include "string"
|
||||||
|
#include "common/types.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;
|
||||||
|
int axis_value;
|
||||||
|
InputBinding(int v, u32 k1 = SDLK_UNKNOWN, u32 k2 = SDLK_UNKNOWN, u32 k3 = SDLK_UNKNOWN) {
|
||||||
|
// we format the keys so comaring 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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
u32 button;
|
||||||
|
Input::Axis axis;
|
||||||
|
int axis_value;
|
||||||
|
bool active;
|
||||||
|
ControllerOutput(const std::string& n, u32 b, Axis a = Axis::AxisMax, int v = 0, bool ac = false) {
|
||||||
|
name = n;
|
||||||
|
button = b;
|
||||||
|
axis = a;
|
||||||
|
axis_value = v;
|
||||||
|
active = ac;
|
||||||
|
}
|
||||||
|
ControllerOutput(const ControllerOutput& o);
|
||||||
|
void update(bool pressed, int axis_direction = 0);
|
||||||
|
};
|
||||||
|
class InputData {
|
||||||
|
InputBinding binding;
|
||||||
|
int axis_value;
|
||||||
|
bool flag;
|
||||||
|
InputData(InputBinding b, int v = 0) : binding(b), axis_value(v), flag(false) {}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// todo
|
||||||
|
// don't forget to change the number too
|
||||||
|
const std::array<ControllerOutput, 4> input_state = {
|
||||||
|
// buttons and axes rolled into one
|
||||||
|
ControllerOutput("up", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP),
|
||||||
|
ControllerOutput("down", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN),
|
||||||
|
// etc.
|
||||||
|
ControllerOutput("axis_left_x_plus", 0, Axis::LeftX, 127),
|
||||||
|
ControllerOutput("axis_left_x_minus", 0, Axis::LeftX, -127),
|
||||||
|
// etc.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
extern std::map<InputData, 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);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -12,6 +12,7 @@
|
|||||||
#include "core/libraries/pad/pad.h"
|
#include "core/libraries/pad/pad.h"
|
||||||
#include "imgui/renderer/imgui_core.h"
|
#include "imgui/renderer/imgui_core.h"
|
||||||
#include "input/controller.h"
|
#include "input/controller.h"
|
||||||
|
#include "input/input_handler.h"
|
||||||
#include "sdl_window.h"
|
#include "sdl_window.h"
|
||||||
#include "video_core/renderdoc.h"
|
#include "video_core/renderdoc.h"
|
||||||
|
|
||||||
@ -76,6 +77,8 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_
|
|||||||
window_info.type = WindowSystemType::Metal;
|
window_info.type = WindowSystemType::Metal;
|
||||||
window_info.render_surface = SDL_Metal_GetLayer(SDL_Metal_CreateView(window));
|
window_info.render_surface = SDL_Metal_GetLayer(SDL_Metal_CreateView(window));
|
||||||
#endif
|
#endif
|
||||||
|
// input handler init-s
|
||||||
|
Input::ControllerOutput::setControllerOutputController(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowSDL::~WindowSDL() = default;
|
WindowSDL::~WindowSDL() = default;
|
||||||
@ -103,9 +106,10 @@ void WindowSDL::waitEvent() {
|
|||||||
is_shown = event.type == SDL_EVENT_WINDOW_EXPOSED;
|
is_shown = event.type == SDL_EVENT_WINDOW_EXPOSED;
|
||||||
onResize();
|
onResize();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_EVENT_KEY_DOWN:
|
case SDL_EVENT_KEY_DOWN:
|
||||||
case SDL_EVENT_KEY_UP:
|
case SDL_EVENT_KEY_UP:
|
||||||
onKeyPress(&event);
|
onKeyboardMouseInput(&event);
|
||||||
break;
|
break;
|
||||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||||
@ -127,6 +131,8 @@ void WindowSDL::waitEvent() {
|
|||||||
|
|
||||||
void WindowSDL::initTimers() {
|
void WindowSDL::initTimers() {
|
||||||
SDL_AddTimer(100, &PollController, controller);
|
SDL_AddTimer(100, &PollController, controller);
|
||||||
|
// todo: add back mouse polling here
|
||||||
|
SDL_AddTimer(33, Input::mousePolling, (void*)controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowSDL::onResize() {
|
void WindowSDL::onResize() {
|
||||||
@ -134,9 +140,18 @@ void WindowSDL::onResize() {
|
|||||||
ImGui::Core::OnResize();
|
ImGui::Core::OnResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowSDL::onKeyPress(const SDL_Event* event) {
|
void WindowSDL::onKeyboardMouseInput(const SDL_Event* event) {
|
||||||
using Libraries::Pad::OrbisPadButtonDataOffset;
|
using Libraries::Pad::OrbisPadButtonDataOffset;
|
||||||
|
|
||||||
|
// get the event's id, if it's keyup or keydown
|
||||||
|
|
||||||
|
// add/remove it from the list
|
||||||
|
|
||||||
|
// update bindings and buttons
|
||||||
|
|
||||||
|
// update axes
|
||||||
|
|
||||||
|
/* og function
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
// Use keys that are more friendly for keyboards without a keypad.
|
// Use keys that are more friendly for keyboards without a keypad.
|
||||||
// Once there are key binding options this won't be necessary.
|
// Once there are key binding options this won't be necessary.
|
||||||
@ -323,6 +338,7 @@ void WindowSDL::onKeyPress(const SDL_Event* event) {
|
|||||||
if (axis != Input::Axis::AxisMax) {
|
if (axis != Input::Axis::AxisMax) {
|
||||||
controller->Axis(0, axis, ax);
|
controller->Axis(0, axis, ax);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowSDL::onGamepadEvent(const SDL_Event* event) {
|
void WindowSDL::onGamepadEvent(const SDL_Event* event) {
|
||||||
|
@ -72,7 +72,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void onResize();
|
void onResize();
|
||||||
void onKeyPress(const SDL_Event* event);
|
void onKeyboardMouseInput(const SDL_Event* event);
|
||||||
void onGamepadEvent(const SDL_Event* event);
|
void onGamepadEvent(const SDL_Event* event);
|
||||||
|
|
||||||
int sdlGamepadToOrbisButton(u8 button);
|
int sdlGamepadToOrbisButton(u8 button);
|
||||||
|
Loading…
Reference in New Issue
Block a user