Use SDL_GameController for gamepad input

This commit is contained in:
rubenwardy 2022-10-20 19:43:10 +01:00
parent 08284e420d
commit 6ec32dee84
24 changed files with 316 additions and 479 deletions

View File

@ -146,6 +146,25 @@ enable_hotbar_mouse_wheel (Hotbar: Enable mouse wheel for selection) bool true
# Requires: keyboard_mouse
invert_hotbar_mouse_wheel (Hotbar: Invert mouse wheel direction) bool false
[*Gamepads]
# Enable joysticks. Requires a restart to take effect
enable_joysticks (Enable joysticks) bool true
# 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 8000 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
[*Touchscreen]
# Enables touchscreen mode, allowing you to play the game with a touchscreen.
@ -2198,29 +2217,6 @@ enable_remote_media_server (Connect to external media server) bool true
serverlist_file (Serverlist file) string favoriteservers.json
[*Gamepads]
# 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]
# Path to texture directory. All textures are first searched from here.

View File

@ -3171,7 +3171,7 @@
# The dead zone of the joystick
# type: int min: 0 max: 65535
# joystick_deadzone = 2048
# joystick_deadzone = 8000
# The sensitivity of the joystick axes for moving the
# in-game view frustum around.

View File

@ -54,9 +54,9 @@ 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}/joystick_controller.cpp
${CMAKE_CURRENT_SOURCE_DIR}/keycode.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}/localplayer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mapblock_mesh.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mesh.cpp

View File

@ -310,22 +310,7 @@ void ClientLauncher::init_input()
input = new RealInputHandler(receiver);
if (g_settings->getBool("enable_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)) {
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);
} else {
errorstream << "Could not activate joystick support." << std::endl;
}
input->joystick.connectToJoystick();
}
}

View File

@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
#include "irrlichttypes_extrabloated.h"
#include "client/inputhandler.h"
#include "client/input/inputhandler.h"
#include "gameparams.h"
class RenderingEngine;

View File

@ -26,10 +26,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client.h"
#include "client/clientevent.h"
#include "client/gameui.h"
#include "client/inputhandler.h"
#include "client/input/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"
@ -1191,6 +1191,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);
processUserInput(dtime);
// Update camera before player movement to avoid camera lag of one frame
@ -1945,7 +1953,8 @@ void Game::updateStats(RunStats *stats, const FpsControl &draw_times,
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)) {
input->isInMenu = !device->isWindowActive() || isMenuActive() || guienv->hasFocus(gui_chat_console);
if (input->isInMenu) {
if (m_game_focused) {
m_game_focused = false;
infostream << "Game lost focus" << std::endl;
@ -2621,8 +2630,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, -89.5, 89.5);

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_extrabloated.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

@ -1,6 +1,7 @@
/*
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
@ -23,8 +24,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "keys.h"
#include <bitset>
#include <vector>
#include <memory>
enum JoystickAxis {
#include <SDL2/SDL_gamecontroller.h>
enum GamepadAxis
{
JA_SIDEWARD_MOVE,
JA_FORWARD_MOVE,
@ -35,77 +41,29 @@ enum JoystickAxis {
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];
struct GamepadLayout
{
s16 axes_deadzone;
};
class JoystickController {
class GamepadInput
{
public:
JoystickController();
virtual ~GamepadInput() = default;
virtual bool operator()(SDL_GameController *gameController) const = 0;
};
void onJoystickConnect(const std::vector<irr::SJoystickInfo> &joystick_infos);
class GamepadController
{
public:
GamepadController();
void connectToJoystick();
void update(float dtime, bool is_in_gui);
bool handleEvent(const irr::SEvent::SJoystickEvent &ev);
void clear();
@ -126,6 +84,7 @@ public:
{
return m_keys_released[b];
}
void clearWasKeyReleased(GameKeyType b)
{
m_keys_released[b] = false;
@ -135,6 +94,7 @@ public:
{
return m_keys_pressed[b];
}
void clearWasKeyPressed(GameKeyType b)
{
m_keys_pressed[b] = false;
@ -145,22 +105,19 @@ public:
return m_keys_down[b];
}
s16 getAxis(JoystickAxis axis)
{
return m_axes_vals[axis];
}
float getAxisWithoutDead(JoystickAxis axis);
float getMovementDirection();
float getMovementSpeed();
f32 doubling_dtime;
v2f32 getLookRotation();
private:
void setLayoutFromControllerName(const std::string &name);
JoystickLayout m_layout;
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];

View File

@ -201,6 +201,10 @@ bool MyEventReceiver::OnEvent(const SEvent &event)
*/
float RealInputHandler::getMovementSpeed()
{
if (isInMenu) {
return 0.f;
}
bool f = m_receiver->IsKeyDown(keycache.key[KeyType::FORWARD]),
b = m_receiver->IsKeyDown(keycache.key[KeyType::BACKWARD]),
l = m_receiver->IsKeyDown(keycache.key[KeyType::LEFT]),

View File

@ -20,10 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
#include "irrlichttypes_extrabloated.h"
#include "joystick_controller.h"
#include "gamepad_controller.h"
#include <list>
#include "keycode.h"
#include "renderingengine.h"
#include "client/renderingengine.h"
class InputHandler;
@ -197,7 +197,7 @@ public:
keyWasReleased.clear();
}
JoystickController *joystick = nullptr;
GamepadController *joystick = nullptr;
private:
s32 mouse_wheel = 0;
@ -263,8 +263,9 @@ public:
virtual void clear() {}
virtual void releaseAllKeys() {}
JoystickController joystick;
GamepadController joystick;
KeyCache keycache;
bool isInMenu = false;
};
/*
Separated input handler

View File

@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "exceptions.h"
#include "irrlichttypes.h"
#include "Keycodes.h"
#include <IEventReceiver.h>
#include "IEventReceiver.h"
#include <string>
class UnknownKeycode : public BaseException

View File

@ -1,332 +0,0 @@
/*
Minetest
Copyright (C) 2016 est31, <MTest31@outlook.com>
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 "joystick_controller.h"
#include "irrlichttypes_extrabloated.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

@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "renderingengine.h"
#include "render/core.h"
#include "render/factory.h"
#include "inputhandler.h"
#include "client/input/inputhandler.h"
#include "gettext.h"
#include "filesys.h"
#include "../gui/guiSkin.h"

View File

@ -312,12 +312,11 @@ void set_default_settings()
settings->setDefault("autojump", "false");
#endif
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

@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#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

@ -117,7 +117,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

@ -140,7 +140,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

@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiFormSpecMenu.h"
#include "constants.h"
#include "gamedef.h"
#include "client/keycode.h"
#include "client/input/keycode.h"
#include "util/strfnd.h"
#include <IGUIButton.h>
#include <IGUICheckBox.h>
@ -101,7 +101,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,
@ -135,7 +135,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

@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiScrollBar.h"
#include "guiTable.h"
#include "network/networkprotocol.h"
#include "client/joystick_controller.h"
#include "client/input/gamepad_controller.h"
#include "util/string.h"
#include "util/enriched_string.h"
#include "StyleSpec.h"
@ -166,7 +166,7 @@ class GUIFormSpecMenu : public GUIModalMenu
};
public:
GUIFormSpecMenu(JoystickController *joystick,
GUIFormSpecMenu(GamepadController *joystick,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
Client *client,
@ -181,7 +181,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 +385,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

@ -24,7 +24,7 @@
#include "irrlichttypes_extrabloated.h"
#include "modalMenu.h"
#include "gettext.h"
#include "client/keycode.h"
#include "client/input/keycode.h"
#include <string>
#include <vector>

View File

@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h"
#include "settings.h"
#include "client/guiscalingfilter.h"
#include "client/keycode.h"
#include "client/input/keycode.h"
#include "client/renderingengine.h"
#include "util/numeric.h"
#include <ISceneCollisionManager.h>

View File

@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
#include "exceptions.h"
#include "client/keycode.h"
#include "client/input/keycode.h"
class TestKeycode : public TestBase {
public: