1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

Use SDL_GameController for gamepad input

This commit is contained in:
rubenwardy 2022-10-20 19:43:10 +01:00
parent fa0c09d202
commit e9c7ddb4ef
27 changed files with 410 additions and 566 deletions

View file

@ -468,6 +468,25 @@ fixed_virtual_joystick (Fixed virtual joystick) bool false
# Requires: touchscreen
virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool false
[*Gamepads] [client]
# Enable joysticks. Requires a restart to take effect
enable_joysticks (Enable joysticks) bool false
# The identifier of the joystick to use
joystick_id (Joystick ID) int 0 0 255
# The time in seconds it takes between repeated events
# when holding down a joystick button combination.
repeat_joystick_button_time (Joystick button repetition interval) float 0.17 0.001
# The dead zone of the joystick
joystick_deadzone (Joystick dead zone) int 2048 0 65535
# The sensitivity of the joystick axes for moving the
# in-game view frustum around.
joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170.0 0.001
[Graphics and Audio] [client]
[*Graphics]
@ -2498,30 +2517,6 @@ enable_remote_media_server (Connect to external media server) [client] bool true
# Multiplayer Tab.
serverlist_file (Serverlist file) [client] string favoriteservers.json
[*Gamepads] [client]
# Enable joysticks. Requires a restart to take effect
enable_joysticks (Enable joysticks) bool false
# The identifier of the joystick to use
joystick_id (Joystick ID) int 0 0 255
# The type of joystick
joystick_type (Joystick type) enum auto auto,generic,xbox,dragonrise_gamecube
# The time in seconds it takes between repeated events
# when holding down a joystick button combination.
repeat_joystick_button_time (Joystick button repetition interval) float 0.17 0.001
# The dead zone of the joystick
joystick_deadzone (Joystick dead zone) int 2048 0 65535
# The sensitivity of the joystick axes for moving the
# in-game view frustum around.
joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170.0 0.001
[*Hide: Temporary Settings] [common]
# Path to texture directory. All textures are first searched from here.

View file

@ -55,10 +55,10 @@ set(client_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/guiscalingfilter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/hud.cpp
${CMAKE_CURRENT_SOURCE_DIR}/imagefilters.cpp
${CMAKE_CURRENT_SOURCE_DIR}/inputhandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/input/inputhandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/input/gamepad_controller.cpp
${CMAKE_CURRENT_SOURCE_DIR}/input/keycode.cpp
${CMAKE_CURRENT_SOURCE_DIR}/item_visuals_manager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/joystick_controller.cpp
${CMAKE_CURRENT_SOURCE_DIR}/keycode.cpp
${CMAKE_CURRENT_SOURCE_DIR}/localplayer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mapblock_mesh.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mesh.cpp

View file

@ -11,7 +11,7 @@
#include "player.h"
#include "chat.h"
#include "gettext.h"
#include "inputhandler.h"
#include "input/inputhandler.h"
#include "profiler.h"
#include "gui/guiEngine.h"
#include "fontengine.h"
@ -296,29 +296,9 @@ void ClientLauncher::init_input()
else
input = new RealInputHandler(receiver);
if (g_settings->getBool("enable_joysticks"))
init_joysticks();
if (g_settings->getBool("enable_joysticks")) {
input->joystick.connectToJoystick();
}
void ClientLauncher::init_joysticks()
{
irr::core::array<irr::SJoystickInfo> infos;
std::vector<irr::SJoystickInfo> joystick_infos;
// Make sure this is called maximum once per
// irrlicht device, otherwise it will give you
// multiple events for the same joystick.
if (!m_rendering_engine->get_raw_device()->activateJoysticks(infos)) {
errorstream << "Could not activate joystick support." << std::endl;
return;
}
infostream << "Joystick support enabled" << std::endl;
joystick_infos.reserve(infos.size());
for (u32 i = 0; i < infos.size(); i++) {
joystick_infos.push_back(infos[i]);
}
input->joystick.onJoystickConnect(joystick_infos);
}
void ClientLauncher::setting_changed_callback(const std::string &name, void *data)

View file

@ -26,7 +26,6 @@ private:
void init_args(GameStartData &start_data, const Settings &cmd_args);
bool init_engine();
void init_input();
void init_joysticks();
static void setting_changed_callback(const std::string &name, void *data);
void config_guienv();

View file

@ -10,11 +10,11 @@
#include "client.h"
#include "client/clientevent.h"
#include "client/gameui.h"
#include "client/input/inputhandler.h"
#include "client/game_formspec.h"
#include "client/inputhandler.h"
#include "client/texturepaths.h"
#include "client/keys.h"
#include "client/joystick_controller.h"
#include "client/input/keys.h"
#include "client/input/gamepad_controller.h"
#include "client/mapblock_mesh.h"
#include "client/sound.h"
#include "clientmap.h"
@ -1066,6 +1066,14 @@ void Game::run()
m_game_ui->clearInfoText();
input->joystick.update(dtime, isMenuActive());
if (isMenuActive()) {
v2f32 look = input->joystick.getLookRotation();
auto cursor = device->getCursorControl();
cursor->setPosition(cursor->getPosition() + v2s32(-1000 * dtime * look.X, 1000 * dtime * look.Y));
}
updateProfilers(stats, draw_times, dtime);
// Update camera offset once before doing anything.
@ -1851,7 +1859,7 @@ void Game::updateStats(RunStats *stats, const FpsControl &draw_times,
/****************************************************************************
Input handling
****************************************************************************/
*********************************************i*******************************/
void Game::processUserInput(f32 dtime)
{
@ -1865,7 +1873,8 @@ void Game::processUserInput(f32 dtime)
}
// Reset input if window not active or some menu is active
if (!device->isWindowActive() || isMenuActive() || guienv->hasFocus(gui_chat_console.get())) {
input->isInMenu = !device->isWindowActive() || isMenuActive() || guienv->hasFocus(gui_chat_console.get());
if (input->isInMenu) {
if (m_game_focused) {
m_game_focused = false;
infostream << "Game lost focus" << std::endl;
@ -2502,8 +2511,10 @@ void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)
if (m_cache_enable_joysticks) {
f32 sens_scale = getSensitivityScaleFactor();
f32 c = m_cache_joystick_frustum_sensitivity * dtime * sens_scale;
cam->camera_yaw -= input->joystick.getAxisWithoutDead(JA_FRUSTUM_HORIZONTAL) * c;
cam->camera_pitch += input->joystick.getAxisWithoutDead(JA_FRUSTUM_VERTICAL) * c;
auto look_rotation = input->joystick.getLookRotation() * c;
cam->camera_yaw += look_rotation.X;
cam->camera_pitch += look_rotation.Y;
}
cam->camera_pitch = rangelim(cam->camera_pitch, -90, 90);

View file

@ -0,0 +1,216 @@
/*
Minetest
Copyright (C) 2016 est31, <MTest31@outlook.com>
Copyright (C) 2022 rubenwardy
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gamepad_controller.h"
#include "irrlichttypes_bloated.h"
#include "keys.h"
#include "settings.h"
#include "porting.h"
#include "util/string.h"
#include "util/numeric.h"
class GamepadInputButton : public GamepadInput
{
SDL_GameControllerButton button;
public:
explicit GamepadInputButton(SDL_GameControllerButton button): button(button) {}
~GamepadInputButton() override = default;
bool operator()(SDL_GameController *gameController) const override {
return SDL_GameControllerGetButton(gameController, button);
};
};
class GamepadInputAxis : public GamepadInput
{
SDL_GameControllerAxis axis;
public:
explicit GamepadInputAxis(SDL_GameControllerAxis axis): axis(axis) {}
~GamepadInputAxis() override = default;
bool operator()(SDL_GameController *gameController) const override {
return SDL_GameControllerGetAxis(gameController, axis) > 9000;
};
};
#define BTN(button) std::make_unique<GamepadInputButton>(button)
#define AXIS(axis) std::make_unique<GamepadInputAxis>(axis)
GamepadController::GamepadController() :
doubling_dtime(std::max(g_settings->getFloat("repeat_joystick_button_time"), 0.001f))
{
for (float &i : m_past_pressed_time) {
i = 0;
}
clear();
m_layout.axes_deadzone = g_settings->getU16("joystick_deadzone");
m_button_bindings.emplace_back( BTN(SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_BACK), KeyType::ESC );
m_button_bindings.emplace_back( BTN(SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_START), KeyType::ESC );
m_button_bindings.emplace_back( BTN(SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_A), KeyType::JUMP );
m_button_bindings.emplace_back( BTN(SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_B), KeyType::ESC );
m_button_bindings.emplace_back( BTN(SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_X), KeyType::AUX1 );
m_button_bindings.emplace_back( BTN(SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_Y), KeyType::INVENTORY );
m_button_bindings.emplace_back( BTN(SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSTICK), KeyType::AUX1 );
m_button_bindings.emplace_back( BTN(SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSTICK), KeyType::SNEAK );
m_button_bindings.emplace_back( AXIS(SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERRIGHT), KeyType::DIG );
m_button_bindings.emplace_back( AXIS(SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERLEFT), KeyType::PLACE );
m_button_bindings.emplace_back( BTN(SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSHOULDER), KeyType::HOTBAR_PREV );
m_button_bindings.emplace_back( BTN(SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), KeyType::HOTBAR_NEXT );
m_button_bindings.emplace_back( BTN(SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_UP), KeyType::ZOOM );
m_button_bindings.emplace_back( BTN(SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_LEFT), KeyType::DROP );
m_button_bindings.emplace_back( BTN(SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_RIGHT), KeyType::SCREENSHOT );
m_button_bindings.emplace_back( BTN(SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_DOWN), KeyType::FREEMOVE );
}
void GamepadController::connectToJoystick()
{
s32 id = g_settings->getS32("joystick_id");
int joystick_count = SDL_NumJoysticks();
if (id < 0 || id >= joystick_count) {
id = 0;
for (int joystick_id = 0; joystick_id < joystick_count; joystick_id++) {
if (SDL_IsGameController(joystick_id)) {
id = joystick_id;
break;
}
}
}
m_joystick_id = id;
if (gameController)
SDL_GameControllerClose(gameController);
gameController = SDL_GameControllerOpen(m_joystick_id);
}
void GamepadController::update(float dtime, bool is_in_gui)
{
if (!gameController)
return;
m_internal_time = porting::getTimeMs() / 1000.f;
std::bitset<KeyType::INTERNAL_ENUM_COUNT> keys_pressed;
for (const auto &pair : m_button_bindings) {
if ((*pair.first)(gameController)) {
keys_pressed.set(pair.second);
}
}
// Then update the values
for (size_t i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) {
if (keys_pressed[i]) {
if (!m_past_keys_pressed[i] &&
m_past_pressed_time[i] < m_internal_time - doubling_dtime) {
m_past_keys_pressed[i] = true;
m_past_pressed_time[i] = m_internal_time;
}
} else if (m_keys_down[i]) {
m_keys_released[i] = true;
}
if (keys_pressed[i] && !(m_keys_down[i]))
m_keys_pressed[i] = true;
m_keys_down[i] = keys_pressed[i];
}
}
bool GamepadController::handleEvent(const irr::SEvent::SJoystickEvent &ev)
{
return true;
}
void GamepadController::clear()
{
m_keys_pressed.reset();
m_keys_down.reset();
m_past_keys_pressed.reset();
m_keys_released.reset();
memset(m_axes_vals, 0, sizeof(m_axes_vals));
}
float GamepadController::getAxisWithoutDead(GamepadAxis axis)
{
SDL_GameControllerAxis sdlAxis;
switch (axis) {
case JA_SIDEWARD_MOVE:
sdlAxis = SDL_CONTROLLER_AXIS_LEFTX;
break;
case JA_FORWARD_MOVE:
sdlAxis = SDL_CONTROLLER_AXIS_LEFTY;
break;
case JA_FRUSTUM_HORIZONTAL:
sdlAxis = SDL_CONTROLLER_AXIS_RIGHTX;
break;
case JA_FRUSTUM_VERTICAL:
sdlAxis = SDL_CONTROLLER_AXIS_RIGHTY;
break;
case JA_COUNT:
break;
}
s16 v = SDL_GameControllerGetAxis(gameController, sdlAxis);
if (abs(v) < m_layout.axes_deadzone)
return 0.0f;
v += (v < 0 ? m_layout.axes_deadzone : -m_layout.axes_deadzone);
return (float)v / ((float)(INT16_MAX - m_layout.axes_deadzone));
}
float GamepadController::getMovementDirection()
{
return atan2(getAxisWithoutDead(JA_SIDEWARD_MOVE), -getAxisWithoutDead(JA_FORWARD_MOVE));
}
float GamepadController::getMovementSpeed()
{
float speed = sqrt(pow(getAxisWithoutDead(JA_FORWARD_MOVE), 2) + pow(getAxisWithoutDead(JA_SIDEWARD_MOVE), 2));
if (speed > 1.0f)
speed = 1.0f;
return speed;
}
v2f32 GamepadController::getLookRotation()
{
return {
-getAxisWithoutDead(JA_FRUSTUM_HORIZONTAL),
getAxisWithoutDead(JA_FRUSTUM_VERTICAL),
};
}

View file

@ -0,0 +1,125 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2025 rubenwardy <rw@rubenwardy.com>
#pragma once
#include "irrlicht.h"
#include "irrlichttypes_bloated.h"
#include "keys.h"
#include <bitset>
#include <vector>
#include <memory>
#include <SDL2/SDL_gamecontroller.h>
enum GamepadAxis
{
JA_SIDEWARD_MOVE,
JA_FORWARD_MOVE,
JA_FRUSTUM_HORIZONTAL,
JA_FRUSTUM_VERTICAL,
// To know the count of enum values
JA_COUNT,
};
struct GamepadLayout
{
s16 axes_deadzone;
};
class GamepadInput
{
public:
virtual ~GamepadInput() = default;
virtual bool operator()(SDL_GameController *gameController) const = 0;
};
class GamepadController
{
public:
GamepadController();
void connectToJoystick();
void update(float dtime, bool is_in_gui);
bool handleEvent(const irr::SEvent::SJoystickEvent &ev);
void clear();
void releaseAllKeys()
{
m_keys_released |= m_keys_down;
m_keys_down.reset();
}
bool wasKeyDown(GameKeyType b)
{
bool r = m_past_keys_pressed[b];
m_past_keys_pressed[b] = false;
return r;
}
bool wasKeyReleased(GameKeyType b)
{
return m_keys_released[b];
}
void clearWasKeyReleased(GameKeyType b)
{
m_keys_released[b] = false;
}
bool wasKeyPressed(GameKeyType b)
{
return m_keys_pressed[b];
}
void clearWasKeyPressed(GameKeyType b)
{
m_keys_pressed[b] = false;
}
bool isKeyDown(GameKeyType b)
{
return m_keys_down[b];
}
float getMovementDirection();
float getMovementSpeed();
v2f32 getLookRotation();
inline u8 getJoystickId() {
return m_joystick_id;
}
private:
float getAxisWithoutDead(GamepadAxis axis);
f32 doubling_dtime;
SDL_GameController *gameController = nullptr;
GamepadLayout m_layout;
std::vector<std::pair<std::unique_ptr<GamepadInput>, KeyType::T>> m_button_bindings;
s16 m_axes_vals[JA_COUNT];
u8 m_joystick_id = 0;
std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_keys_down;
std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_keys_pressed;
f32 m_internal_time;
f32 m_past_pressed_time[KeyType::INTERNAL_ENUM_COUNT];
std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_past_keys_pressed;
std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_keys_released;
};

View file

@ -6,7 +6,7 @@
#include "irrlichttypes.h"
#include "irr_v2d.h"
#include "joystick_controller.h"
#include "gamepad_controller.h"
#include <array>
#include <list>
#include <set>
@ -84,7 +84,7 @@ public:
keyWasReleased.reset();
}
JoystickController *joystick = nullptr;
GamepadController *joystick = nullptr;
PointerType getLastPointerType() { return last_pointer_type; }
@ -177,7 +177,8 @@ public:
static_cast<InputHandler *>(data)->reloadKeybindings();
}
JoystickController joystick;
GamepadController joystick;
bool isInMenu = false;
};
/*

View file

@ -6,7 +6,7 @@
#include "settings.h"
#include "log.h"
#include "debug.h"
#include "renderingengine.h"
#include "../renderingengine.h"
#include "util/hex.h"
#include "util/string.h"
#include "util/basic_macros.h"

View file

@ -1,316 +0,0 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2016 est31, <MTest31@outlook.com>
#include "joystick_controller.h"
#include "keys.h"
#include "settings.h"
#include "gettime.h"
#include "porting.h"
#include "util/string.h"
#include "util/numeric.h"
bool JoystickButtonCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const
{
u32 buttons = ev.ButtonStates;
buttons &= filter_mask;
return buttons == compare_mask;
}
bool JoystickAxisCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const
{
s16 ax_val = ev.Axis[axis_to_compare];
return (ax_val * direction < -thresh);
}
// spares many characters
#define JLO_B_PB(A, B, C) jlo.button_keys.emplace_back(A, B, C)
#define JLO_A_PB(A, B, C, D) jlo.axis_keys.emplace_back(A, B, C, D)
JoystickLayout create_default_layout()
{
JoystickLayout jlo;
jlo.axes_deadzone = g_settings->getU16("joystick_deadzone");
const JoystickAxisLayout axes[JA_COUNT] = {
{0, 1}, // JA_SIDEWARD_MOVE
{1, 1}, // JA_FORWARD_MOVE
{3, 1}, // JA_FRUSTUM_HORIZONTAL
{4, 1}, // JA_FRUSTUM_VERTICAL
};
memcpy(jlo.axes, axes, sizeof(jlo.axes));
u32 sb = 1 << 7; // START button mask
u32 fb = 1 << 3; // FOUR button mask
u32 bm = sb | fb; // Mask for Both Modifiers
// The back button means "ESC".
JLO_B_PB(KeyType::ESC, 1 << 6, 1 << 6);
// The start button counts as modifier as well as use key.
// JLO_B_PB(KeyType::USE, sb, sb));
// Accessible without start modifier button pressed
// regardless whether four is pressed or not
JLO_B_PB(KeyType::SNEAK, sb | 1 << 2, 1 << 2);
// Accessible without four modifier button pressed
// regardless whether start is pressed or not
JLO_B_PB(KeyType::DIG, fb | 1 << 4, 1 << 4);
JLO_B_PB(KeyType::PLACE, fb | 1 << 5, 1 << 5);
// Accessible without any modifier pressed
JLO_B_PB(KeyType::JUMP, bm | 1 << 0, 1 << 0);
JLO_B_PB(KeyType::AUX1, bm | 1 << 1, 1 << 1);
// Accessible with start button not pressed, but four pressed
// TODO find usage for button 0
JLO_B_PB(KeyType::DROP, bm | 1 << 1, fb | 1 << 1);
JLO_B_PB(KeyType::HOTBAR_PREV, bm | 1 << 4, fb | 1 << 4);
JLO_B_PB(KeyType::HOTBAR_NEXT, bm | 1 << 5, fb | 1 << 5);
// Accessible with start button and four pressed
// TODO find usage for buttons 0, 1 and 4, 5
// Now about the buttons simulated by the axes
// Movement buttons, important for vessels
JLO_A_PB(KeyType::FORWARD, 1, 1, jlo.axes_deadzone);
JLO_A_PB(KeyType::BACKWARD, 1, -1, jlo.axes_deadzone);
JLO_A_PB(KeyType::LEFT, 0, 1, jlo.axes_deadzone);
JLO_A_PB(KeyType::RIGHT, 0, -1, jlo.axes_deadzone);
// Scroll buttons
JLO_A_PB(KeyType::HOTBAR_PREV, 2, -1, jlo.axes_deadzone);
JLO_A_PB(KeyType::HOTBAR_NEXT, 5, -1, jlo.axes_deadzone);
return jlo;
}
JoystickLayout create_xbox_layout()
{
JoystickLayout jlo;
jlo.axes_deadzone = 7000;
const JoystickAxisLayout axes[JA_COUNT] = {
{0, 1}, // JA_SIDEWARD_MOVE
{1, 1}, // JA_FORWARD_MOVE
{2, 1}, // JA_FRUSTUM_HORIZONTAL
{3, 1}, // JA_FRUSTUM_VERTICAL
};
memcpy(jlo.axes, axes, sizeof(jlo.axes));
// The back button means "ESC".
JLO_B_PB(KeyType::ESC, 1 << 8, 1 << 8); // back
JLO_B_PB(KeyType::ESC, 1 << 9, 1 << 9); // start
// 4 Buttons
JLO_B_PB(KeyType::JUMP, 1 << 0, 1 << 0); // A/green
JLO_B_PB(KeyType::ESC, 1 << 1, 1 << 1); // B/red
JLO_B_PB(KeyType::AUX1, 1 << 2, 1 << 2); // X/blue
JLO_B_PB(KeyType::INVENTORY, 1 << 3, 1 << 3); // Y/yellow
// Analog Sticks
JLO_B_PB(KeyType::AUX1, 1 << 11, 1 << 11); // left
JLO_B_PB(KeyType::SNEAK, 1 << 12, 1 << 12); // right
// Triggers
JLO_B_PB(KeyType::DIG, 1 << 6, 1 << 6); // lt
JLO_B_PB(KeyType::PLACE, 1 << 7, 1 << 7); // rt
JLO_B_PB(KeyType::HOTBAR_PREV, 1 << 4, 1 << 4); // lb
JLO_B_PB(KeyType::HOTBAR_NEXT, 1 << 5, 1 << 5); // rb
// D-PAD
JLO_B_PB(KeyType::ZOOM, 1 << 15, 1 << 15); // up
JLO_B_PB(KeyType::DROP, 1 << 13, 1 << 13); // left
JLO_B_PB(KeyType::SCREENSHOT, 1 << 14, 1 << 14); // right
JLO_B_PB(KeyType::FREEMOVE, 1 << 16, 1 << 16); // down
// Movement buttons, important for vessels
JLO_A_PB(KeyType::FORWARD, 1, 1, jlo.axes_deadzone);
JLO_A_PB(KeyType::BACKWARD, 1, -1, jlo.axes_deadzone);
JLO_A_PB(KeyType::LEFT, 0, 1, jlo.axes_deadzone);
JLO_A_PB(KeyType::RIGHT, 0, -1, jlo.axes_deadzone);
return jlo;
}
JoystickLayout create_dragonrise_gamecube_layout()
{
JoystickLayout jlo;
jlo.axes_deadzone = 7000;
const JoystickAxisLayout axes[JA_COUNT] = {
// Control Stick
{0, 1}, // JA_SIDEWARD_MOVE
{1, 1}, // JA_FORWARD_MOVE
// C-Stick
{3, 1}, // JA_FRUSTUM_HORIZONTAL
{4, 1}, // JA_FRUSTUM_VERTICAL
};
memcpy(jlo.axes, axes, sizeof(jlo.axes));
// The center button
JLO_B_PB(KeyType::ESC, 1 << 9, 1 << 9); // Start/Pause Button
// Front right buttons
JLO_B_PB(KeyType::JUMP, 1 << 2, 1 << 2); // A Button
JLO_B_PB(KeyType::SNEAK, 1 << 3, 1 << 3); // B Button
JLO_B_PB(KeyType::DROP, 1 << 0, 1 << 0); // Y Button
JLO_B_PB(KeyType::AUX1, 1 << 1, 1 << 1); // X Button
// Triggers
JLO_B_PB(KeyType::DIG, 1 << 4, 1 << 4); // L Trigger
JLO_B_PB(KeyType::PLACE, 1 << 5, 1 << 5); // R Trigger
JLO_B_PB(KeyType::INVENTORY, 1 << 6, 1 << 6); // Z Button
// D-Pad
JLO_A_PB(KeyType::HOTBAR_PREV, 5, 1, jlo.axes_deadzone); // left
JLO_A_PB(KeyType::HOTBAR_NEXT, 5, -1, jlo.axes_deadzone); // right
// Axis are hard to actuate independently, best to leave up and down unused.
//JLO_A_PB(0, 6, 1, jlo.axes_deadzone); // up
//JLO_A_PB(0, 6, -1, jlo.axes_deadzone); // down
// Movements tied to Control Stick, important for vessels
JLO_A_PB(KeyType::LEFT, 0, 1, jlo.axes_deadzone);
JLO_A_PB(KeyType::RIGHT, 0, -1, jlo.axes_deadzone);
JLO_A_PB(KeyType::FORWARD, 1, 1, jlo.axes_deadzone);
JLO_A_PB(KeyType::BACKWARD, 1, -1, jlo.axes_deadzone);
return jlo;
}
JoystickController::JoystickController()
{
doubling_dtime = std::max(g_settings->getFloat("repeat_joystick_button_time"), 0.001f);
for (float &i : m_past_pressed_time) {
i = 0;
}
m_layout.axes_deadzone = 0;
clear();
}
void JoystickController::onJoystickConnect(const std::vector<irr::SJoystickInfo> &joystick_infos)
{
s32 id = g_settings->getS32("joystick_id");
std::string layout = g_settings->get("joystick_type");
if (id < 0 || id >= (s32)joystick_infos.size()) {
// TODO: auto detection
id = 0;
}
if (id >= 0 && id < (s32)joystick_infos.size()) {
if (layout.empty() || layout == "auto")
setLayoutFromControllerName(joystick_infos[id].Name.c_str());
else
setLayoutFromControllerName(layout);
}
// Irrlicht restriction.
m_joystick_id = rangelim(id, 0, UINT8_MAX);
}
void JoystickController::setLayoutFromControllerName(const std::string &name)
{
if (lowercase(name).find("xbox") != std::string::npos) {
m_layout = create_xbox_layout();
} else if (lowercase(name).find("dragonrise_gamecube") != std::string::npos) {
m_layout = create_dragonrise_gamecube_layout();
} else {
m_layout = create_default_layout();
}
}
bool JoystickController::handleEvent(const irr::SEvent::SJoystickEvent &ev)
{
if (ev.Joystick != m_joystick_id)
return false;
m_internal_time = porting::getTimeMs() / 1000.f;
std::bitset<KeyType::INTERNAL_ENUM_COUNT> keys_pressed;
// First generate a list of keys pressed
for (const auto &button_key : m_layout.button_keys) {
if (button_key.isTriggered(ev)) {
keys_pressed.set(button_key.key);
}
}
for (const auto &axis_key : m_layout.axis_keys) {
if (axis_key.isTriggered(ev)) {
keys_pressed.set(axis_key.key);
}
}
// Then update the values
for (size_t i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) {
if (keys_pressed[i]) {
if (!m_past_keys_pressed[i] &&
m_past_pressed_time[i] < m_internal_time - doubling_dtime) {
m_past_keys_pressed[i] = true;
m_past_pressed_time[i] = m_internal_time;
}
} else if (m_keys_down[i]) {
m_keys_released[i] = true;
}
if (keys_pressed[i] && !(m_keys_down[i]))
m_keys_pressed[i] = true;
m_keys_down[i] = keys_pressed[i];
}
for (size_t i = 0; i < JA_COUNT; i++) {
const JoystickAxisLayout &ax_la = m_layout.axes[i];
m_axes_vals[i] = ax_la.invert * ev.Axis[ax_la.axis_id];
}
return true;
}
void JoystickController::clear()
{
m_keys_pressed.reset();
m_keys_down.reset();
m_past_keys_pressed.reset();
m_keys_released.reset();
memset(m_axes_vals, 0, sizeof(m_axes_vals));
}
float JoystickController::getAxisWithoutDead(JoystickAxis axis)
{
s16 v = m_axes_vals[axis];
if (abs(v) < m_layout.axes_deadzone)
return 0.0f;
v += (v < 0 ? m_layout.axes_deadzone : -m_layout.axes_deadzone);
return (float)v / ((float)(INT16_MAX - m_layout.axes_deadzone));
}
float JoystickController::getMovementDirection()
{
return std::atan2(getAxisWithoutDead(JA_SIDEWARD_MOVE),
-getAxisWithoutDead(JA_FORWARD_MOVE));
}
float JoystickController::getMovementSpeed()
{
float speed = std::sqrt(std::pow(getAxisWithoutDead(JA_FORWARD_MOVE), 2) +
std::pow(getAxisWithoutDead(JA_SIDEWARD_MOVE), 2));
if (speed > 1.0f)
speed = 1.0f;
return speed;
}

View file

@ -1,170 +0,0 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2016 est31, <MTest31@outlook.com>
#pragma once
#include <IEventReceiver.h>
#include "irrlichttypes.h"
#include "keys.h"
#include <bitset>
#include <vector>
enum JoystickAxis {
JA_SIDEWARD_MOVE,
JA_FORWARD_MOVE,
JA_FRUSTUM_HORIZONTAL,
JA_FRUSTUM_VERTICAL,
// To know the count of enum values
JA_COUNT,
};
struct JoystickAxisLayout {
u16 axis_id;
// -1 if to invert, 1 if to keep it.
int invert;
};
struct JoystickCombination {
virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const=0;
GameKeyType key;
};
struct JoystickButtonCmb : public JoystickCombination {
JoystickButtonCmb() = default;
JoystickButtonCmb(GameKeyType key, u32 filter_mask, u32 compare_mask) :
filter_mask(filter_mask),
compare_mask(compare_mask)
{
this->key = key;
}
virtual ~JoystickButtonCmb() = default;
virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const;
u32 filter_mask;
u32 compare_mask;
};
struct JoystickAxisCmb : public JoystickCombination {
JoystickAxisCmb() = default;
JoystickAxisCmb(GameKeyType key, u16 axis_to_compare, int direction, s16 thresh) :
axis_to_compare(axis_to_compare),
direction(direction),
thresh(thresh)
{
this->key = key;
}
virtual ~JoystickAxisCmb() = default;
bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const override;
u16 axis_to_compare;
// if -1, thresh must be smaller than the axis value in order to trigger
// if 1, thresh must be bigger than the axis value in order to trigger
int direction;
s16 thresh;
};
struct JoystickLayout {
std::vector<JoystickButtonCmb> button_keys;
std::vector<JoystickAxisCmb> axis_keys;
JoystickAxisLayout axes[JA_COUNT];
s16 axes_deadzone;
};
class JoystickController {
public:
JoystickController();
void onJoystickConnect(const std::vector<irr::SJoystickInfo> &joystick_infos);
bool handleEvent(const irr::SEvent::SJoystickEvent &ev);
void clear();
void releaseAllKeys()
{
m_keys_released |= m_keys_down;
m_keys_down.reset();
}
bool wasKeyDown(GameKeyType b)
{
bool r = m_past_keys_pressed[b];
m_past_keys_pressed[b] = false;
return r;
}
bool wasKeyReleased(GameKeyType b)
{
return m_keys_released[b];
}
void clearWasKeyReleased(GameKeyType b)
{
m_keys_released[b] = false;
}
bool wasKeyPressed(GameKeyType b)
{
return m_keys_pressed[b];
}
void clearWasKeyPressed(GameKeyType b)
{
m_keys_pressed[b] = false;
}
bool isKeyDown(GameKeyType b)
{
return m_keys_down[b];
}
s16 getAxis(JoystickAxis axis)
{
return m_axes_vals[axis];
}
float getAxisWithoutDead(JoystickAxis axis);
float getMovementDirection();
float getMovementSpeed();
u8 getJoystickId() const
{
return m_joystick_id;
}
f32 doubling_dtime;
private:
void setLayoutFromControllerName(const std::string &name);
JoystickLayout m_layout;
s16 m_axes_vals[JA_COUNT];
u8 m_joystick_id = 0;
std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_keys_down;
std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_keys_pressed;
f32 m_internal_time;
f32 m_past_pressed_time[KeyType::INTERNAL_ENUM_COUNT];
std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_past_keys_pressed;
std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_keys_released;
};

View file

@ -20,6 +20,8 @@
#include "renderingengine.h"
#include "render/core.h"
#include "render/factory.h"
#include "input/inputhandler.h"
#include "gettext.h"
#include "filesys.h"
#include "irrlicht_changes/static_text.h"
#include "irr_ptr.h"

View file

@ -8,7 +8,7 @@
#include <vector>
#include <memory>
#include <string>
#include "client/inputhandler.h"
#include "client/input/inputhandler.h"
#include "debug.h"
#include "config.h"
#include "client/shader.h"

View file

@ -370,12 +370,11 @@ void set_default_settings()
settings->setDefault("toggle_aux1_key", "false");
settings->setDefault("autojump", bool_to_cstr(has_touch));
settings->setDefault("continuous_forward", "false");
settings->setDefault("enable_joysticks", "false");
settings->setDefault("enable_joysticks", "true");
settings->setDefault("joystick_id", "0");
settings->setDefault("joystick_type", "auto");
settings->setDefault("repeat_joystick_button_time", "0.17");
settings->setDefault("joystick_frustum_sensitivity", "170");
settings->setDefault("joystick_deadzone", "2048");
settings->setDefault("joystick_deadzone", "8000");
// Main menu
settings->setDefault("main_menu_path", "");

View file

@ -4,7 +4,7 @@
#pragma once
#include "guiButton.h"
#include "client/keycode.h"
#include "client/input/keycode.h"
#include "util/string.h"
#include "gettext.h"

View file

@ -7,7 +7,7 @@
#include "client/client.h"
#include "debug.h"
#include "gettime.h"
#include "client/keycode.h"
#include "client/input/keycode.h"
#include "settings.h"
#include "porting.h"
#include "client/texturesource.h"

View file

@ -105,7 +105,7 @@ void MenuMusicFetcher::addThePaths(const std::string &name,
/******************************************************************************/
/** GUIEngine */
/******************************************************************************/
GUIEngine::GUIEngine(JoystickController *joystick,
GUIEngine::GUIEngine(GamepadController *joystick,
gui::IGUIElement *parent,
RenderingEngine *rendering_engine,
IMenuManager *menumgr,

View file

@ -125,7 +125,7 @@ public:
* @param smgr scene manager to add scene elements to
* @param data struct to transfer data to main game handling
*/
GUIEngine(JoystickController *joystick,
GUIEngine(GamepadController *joystick,
gui::IGUIElement *parent,
RenderingEngine *rendering_engine,
IMenuManager *menumgr,

View file

@ -13,7 +13,7 @@
#include "EGUIElementTypes.h"
#include "constants.h"
#include "gamedef.h"
#include "client/keycode.h"
#include "client/input/keycode.h"
#include "gui/guiTable.h"
#include "util/strfnd.h"
#include <IGUIButton.h>
@ -25,7 +25,7 @@
#include <IGUIImage.h>
#include <IAnimatedMeshSceneNode.h>
#include "client/renderingengine.h"
#include "client/joystick_controller.h"
#include "client/input/gamepad_controller.h"
#include "log.h"
#include "drawItemStack.h"
#include "filesys.h"
@ -90,7 +90,7 @@ inline u32 clamp_u8(s32 value)
return (u32) MYMIN(MYMAX(value, 0), 255);
}
GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick,
GUIFormSpecMenu::GUIFormSpecMenu(GamepadController *joystick,
gui::IGUIElement *parent, s32 id, IMenuManager *menumgr,
Client *client, gui::IGUIEnvironment *guienv, ISimpleTextureSource *tsrc,
ISoundManager *sound_manager, IFormSource *fsrc, TextDest *tdst,
@ -123,7 +123,7 @@ GUIFormSpecMenu::~GUIFormSpecMenu()
}
void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client,
gui::IGUIEnvironment *guienv, JoystickController *joystick, IFormSource *fs_src,
gui::IGUIEnvironment *guienv, GamepadController *joystick, IFormSource *fs_src,
TextDest *txt_dest, const std::string &formspecPrepend, ISoundManager *sound_manager)
{
if (cur_formspec && cur_formspec->getReferenceCount() == 1) {

View file

@ -17,6 +17,8 @@
#include "guiInventoryList.h"
#include "guiScrollBar.h"
#include "guiTable.h"
#include "network/networkprotocol.h"
#include "client/input/gamepad_controller.h"
#include "util/string.h"
#include "util/enriched_string.h"
#include "StyleSpec.h"
@ -152,7 +154,7 @@ class GUIFormSpecMenu : public GUIModalMenu
};
public:
GUIFormSpecMenu(JoystickController *joystick,
GUIFormSpecMenu(GamepadController *joystick,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
Client *client,
@ -167,7 +169,7 @@ public:
~GUIFormSpecMenu();
static void create(GUIFormSpecMenu *&cur_formspec, Client *client,
gui::IGUIEnvironment *guienv, JoystickController *joystick, IFormSource *fs_src,
gui::IGUIEnvironment *guienv, GamepadController *joystick, IFormSource *fs_src,
TextDest *txt_dest, const std::string &formspecPrepend,
ISoundManager *sound_manager);
@ -385,7 +387,7 @@ private:
std::string m_last_formname;
u16 m_formspec_version = 1;
std::optional<std::string> m_focused_element = std::nullopt;
JoystickController *m_joystick;
GamepadController *m_joystick;
bool m_show_debug = false;
struct parserData {

View file

@ -16,7 +16,7 @@
#include "itemdef.h"
#include "touchscreenlayout.h"
#include "util/basic_macros.h"
#include "client/keycode.h"
#include "client/input/keycode.h"
namespace irr
{

View file

View file

@ -3,7 +3,7 @@
// Copyright (C) 2025 grorp
#include "l_pause_menu.h"
#include "client/keycode.h"
#include "client/input/keycode.h"
#include "gui/mainmenumanager.h"
#include "lua_api/l_internal.h"
#include "client/client.h"

View file

@ -6,7 +6,7 @@
#include <string>
#include "exceptions.h"
#include "client/keycode.h"
#include "client/input/keycode.h"
class TestKeycode : public TestBase {
public: