From e9c7ddb4ef93b72b3632b92ceffb0a8e85b6f72d Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Thu, 20 Oct 2022 19:43:10 +0100 Subject: [PATCH] Use SDL_GameController for gamepad input --- builtin/settingtypes.txt | 43 ++-- src/client/CMakeLists.txt | 6 +- src/client/clientlauncher.cpp | 26 +- src/client/clientlauncher.h | 1 - src/client/game.cpp | 25 +- src/client/input/gamepad_controller.cpp | 216 ++++++++++++++++ src/client/input/gamepad_controller.h | 125 ++++++++++ src/client/{ => input}/inputhandler.cpp | 0 src/client/{ => input}/inputhandler.h | 7 +- src/client/{ => input}/keycode.cpp | 2 +- src/client/{ => input}/keycode.h | 0 src/client/{ => input}/keys.h | 0 src/client/joystick_controller.cpp | 316 ------------------------ src/client/joystick_controller.h | 170 ------------- src/client/renderingengine.cpp | 2 + src/client/renderingengine.h | 2 +- src/defaultsettings.cpp | 5 +- src/gui/guiButtonKey.h | 2 +- src/gui/guiChatConsole.cpp | 2 +- src/gui/guiEngine.cpp | 2 +- src/gui/guiEngine.h | 2 +- src/gui/guiFormSpecMenu.cpp | 8 +- src/gui/guiFormSpecMenu.h | 8 +- src/gui/touchcontrols.h | 2 +- src/gui/touchscreengui.cpp | 0 src/script/lua_api/l_pause_menu.cpp | 2 +- src/unittest/test_keycode.cpp | 2 +- 27 files changed, 410 insertions(+), 566 deletions(-) create mode 100644 src/client/input/gamepad_controller.cpp create mode 100644 src/client/input/gamepad_controller.h rename src/client/{ => input}/inputhandler.cpp (100%) rename src/client/{ => input}/inputhandler.h (98%) rename src/client/{ => input}/keycode.cpp (99%) rename src/client/{ => input}/keycode.h (100%) rename src/client/{ => input}/keys.h (100%) delete mode 100644 src/client/joystick_controller.cpp delete mode 100644 src/client/joystick_controller.h create mode 100644 src/gui/touchscreengui.cpp diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 17ecf5b34..c61eab956 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -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. diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index b11e6ee6f..c9f73988b 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -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 diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 64f2e8f51..23544c927 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -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(); -} - -void ClientLauncher::init_joysticks() -{ - irr::core::array infos; - std::vector 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; + if (g_settings->getBool("enable_joysticks")) { + input->joystick.connectToJoystick(); } - - 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) diff --git a/src/client/clientlauncher.h b/src/client/clientlauncher.h index 21e459db7..f349cec57 100644 --- a/src/client/clientlauncher.h +++ b/src/client/clientlauncher.h @@ -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(); diff --git a/src/client/game.cpp b/src/client/game.cpp index 2c9c4fb77..5ee6ba77b 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -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); diff --git a/src/client/input/gamepad_controller.cpp b/src/client/input/gamepad_controller.cpp new file mode 100644 index 000000000..a325682ae --- /dev/null +++ b/src/client/input/gamepad_controller.cpp @@ -0,0 +1,216 @@ +/* +Minetest +Copyright (C) 2016 est31, +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(button) +#define AXIS(axis) std::make_unique(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 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), + }; +} diff --git a/src/client/input/gamepad_controller.h b/src/client/input/gamepad_controller.h new file mode 100644 index 000000000..cbc63c294 --- /dev/null +++ b/src/client/input/gamepad_controller.h @@ -0,0 +1,125 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2025 rubenwardy + + +#pragma once + +#include "irrlicht.h" +#include "irrlichttypes_bloated.h" +#include "keys.h" +#include +#include +#include + +#include + + +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, KeyType::T>> m_button_bindings; + + s16 m_axes_vals[JA_COUNT]; + + u8 m_joystick_id = 0; + + std::bitset m_keys_down; + std::bitset m_keys_pressed; + + f32 m_internal_time; + + f32 m_past_pressed_time[KeyType::INTERNAL_ENUM_COUNT]; + + std::bitset m_past_keys_pressed; + std::bitset m_keys_released; +}; diff --git a/src/client/inputhandler.cpp b/src/client/input/inputhandler.cpp similarity index 100% rename from src/client/inputhandler.cpp rename to src/client/input/inputhandler.cpp diff --git a/src/client/inputhandler.h b/src/client/input/inputhandler.h similarity index 98% rename from src/client/inputhandler.h rename to src/client/input/inputhandler.h index 85da30ff8..f8702cc5f 100644 --- a/src/client/inputhandler.h +++ b/src/client/input/inputhandler.h @@ -6,7 +6,7 @@ #include "irrlichttypes.h" #include "irr_v2d.h" -#include "joystick_controller.h" +#include "gamepad_controller.h" #include #include #include @@ -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(data)->reloadKeybindings(); } - JoystickController joystick; + GamepadController joystick; + bool isInMenu = false; }; /* diff --git a/src/client/keycode.cpp b/src/client/input/keycode.cpp similarity index 99% rename from src/client/keycode.cpp rename to src/client/input/keycode.cpp index 71e068e30..d49379289 100644 --- a/src/client/keycode.cpp +++ b/src/client/input/keycode.cpp @@ -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" diff --git a/src/client/keycode.h b/src/client/input/keycode.h similarity index 100% rename from src/client/keycode.h rename to src/client/input/keycode.h diff --git a/src/client/keys.h b/src/client/input/keys.h similarity index 100% rename from src/client/keys.h rename to src/client/input/keys.h diff --git a/src/client/joystick_controller.cpp b/src/client/joystick_controller.cpp deleted file mode 100644 index 854bed925..000000000 --- a/src/client/joystick_controller.cpp +++ /dev/null @@ -1,316 +0,0 @@ -// Luanti -// SPDX-License-Identifier: LGPL-2.1-or-later -// Copyright (C) 2016 est31, - -#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 &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 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; -} diff --git a/src/client/joystick_controller.h b/src/client/joystick_controller.h deleted file mode 100644 index d7bf4230e..000000000 --- a/src/client/joystick_controller.h +++ /dev/null @@ -1,170 +0,0 @@ -// Luanti -// SPDX-License-Identifier: LGPL-2.1-or-later -// Copyright (C) 2016 est31, - -#pragma once - -#include -#include "irrlichttypes.h" - -#include "keys.h" -#include -#include - -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 button_keys; - std::vector axis_keys; - JoystickAxisLayout axes[JA_COUNT]; - s16 axes_deadzone; -}; - -class JoystickController { - -public: - JoystickController(); - - void onJoystickConnect(const std::vector &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 m_keys_down; - std::bitset m_keys_pressed; - - f32 m_internal_time; - - f32 m_past_pressed_time[KeyType::INTERNAL_ENUM_COUNT]; - - std::bitset m_past_keys_pressed; - std::bitset m_keys_released; -}; diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index 3b3d114ea..43e3df391 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -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" diff --git a/src/client/renderingengine.h b/src/client/renderingengine.h index 34918ec7a..dcb7427cd 100644 --- a/src/client/renderingengine.h +++ b/src/client/renderingengine.h @@ -8,7 +8,7 @@ #include #include #include -#include "client/inputhandler.h" +#include "client/input/inputhandler.h" #include "debug.h" #include "config.h" #include "client/shader.h" diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 916ce9e51..ef4e8d727 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -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", ""); diff --git a/src/gui/guiButtonKey.h b/src/gui/guiButtonKey.h index 75d0d56ee..a976d9ca4 100644 --- a/src/gui/guiButtonKey.h +++ b/src/gui/guiButtonKey.h @@ -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" diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp index 31df2a944..c830a1f13 100644 --- a/src/gui/guiChatConsole.cpp +++ b/src/gui/guiChatConsole.cpp @@ -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" diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index aef76aec7..f76696a27 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -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, diff --git a/src/gui/guiEngine.h b/src/gui/guiEngine.h index 5c95e9a4c..318dd8e54 100644 --- a/src/gui/guiEngine.h +++ b/src/gui/guiEngine.h @@ -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, diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 4b9601430..7d85702b4 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -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 @@ -25,7 +25,7 @@ #include #include #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) { diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 8dbfcab93..832399f96 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -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 m_focused_element = std::nullopt; - JoystickController *m_joystick; + GamepadController *m_joystick; bool m_show_debug = false; struct parserData { diff --git a/src/gui/touchcontrols.h b/src/gui/touchcontrols.h index 52a3bd6b2..fb51a1f51 100644 --- a/src/gui/touchcontrols.h +++ b/src/gui/touchcontrols.h @@ -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 { diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/script/lua_api/l_pause_menu.cpp b/src/script/lua_api/l_pause_menu.cpp index b1aaae185..ab092776a 100644 --- a/src/script/lua_api/l_pause_menu.cpp +++ b/src/script/lua_api/l_pause_menu.cpp @@ -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" diff --git a/src/unittest/test_keycode.cpp b/src/unittest/test_keycode.cpp index a805ec044..f7a2871a9 100644 --- a/src/unittest/test_keycode.cpp +++ b/src/unittest/test_keycode.cpp @@ -6,7 +6,7 @@ #include #include "exceptions.h" -#include "client/keycode.h" +#include "client/input/keycode.h" class TestKeycode : public TestBase { public: