diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 57420ec15..e3c43baba 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -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. diff --git a/minetest.conf.example b/minetest.conf.example index 72b3d2046..f477c18c7 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -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. diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index f2aa511b7..93e8d978f 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -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 diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 56beba927..26bbd539d 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -310,22 +310,7 @@ void ClientLauncher::init_input() input = new RealInputHandler(receiver); if (g_settings->getBool("enable_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)) { - 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(); } } diff --git a/src/client/clientlauncher.h b/src/client/clientlauncher.h index dc0794c88..5fb825c06 100644 --- a/src/client/clientlauncher.h +++ b/src/client/clientlauncher.h @@ -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; diff --git a/src/client/game.cpp b/src/client/game.cpp index 849e7702a..d4180b7b7 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -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); diff --git a/src/client/input/gamepad_controller.cpp b/src/client/input/gamepad_controller.cpp new file mode 100644 index 000000000..8cafad8c8 --- /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_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(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/joystick_controller.h b/src/client/input/gamepad_controller.h similarity index 55% rename from src/client/joystick_controller.h rename to src/client/input/gamepad_controller.h index 98486df43..dbc807b6b 100644 --- a/src/client/joystick_controller.h +++ b/src/client/input/gamepad_controller.h @@ -1,6 +1,7 @@ /* 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 @@ -23,8 +24,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "keys.h" #include #include +#include -enum JoystickAxis { +#include + + +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 button_keys; - std::vector 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 &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, KeyType::T>> m_button_bindings; s16 m_axes_vals[JA_COUNT]; diff --git a/src/client/inputhandler.cpp b/src/client/input/inputhandler.cpp similarity index 99% rename from src/client/inputhandler.cpp rename to src/client/input/inputhandler.cpp index 5c91898bc..1f41dd42c 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/input/inputhandler.cpp @@ -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]), 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 f4fae2b0b..9b6f5404e 100644 --- a/src/client/inputhandler.h +++ b/src/client/input/inputhandler.h @@ -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 #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 diff --git a/src/client/keycode.cpp b/src/client/input/keycode.cpp similarity index 100% rename from src/client/keycode.cpp rename to src/client/input/keycode.cpp diff --git a/src/client/keycode.h b/src/client/input/keycode.h similarity index 98% rename from src/client/keycode.h rename to src/client/input/keycode.h index 6bc44bb1d..67e06b3ba 100644 --- a/src/client/keycode.h +++ b/src/client/input/keycode.h @@ -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 +#include "IEventReceiver.h" #include class UnknownKeycode : public BaseException 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 5acb0b751..000000000 --- a/src/client/joystick_controller.cpp +++ /dev/null @@ -1,332 +0,0 @@ -/* -Minetest -Copyright (C) 2016 est31, - -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 &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/renderingengine.cpp b/src/client/renderingengine.cpp index debfe8e21..6d1021907 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -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" diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index d4942a4f5..75ea2445e 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -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", ""); diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp index 243828df0..6f72f14c9 100644 --- a/src/gui/guiChatConsole.cpp +++ b/src/gui/guiChatConsole.cpp @@ -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" diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index ea74f6afa..8c167dd11 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -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, diff --git a/src/gui/guiEngine.h b/src/gui/guiEngine.h index b2b396537..e329bd040 100644 --- a/src/gui/guiEngine.h +++ b/src/gui/guiEngine.h @@ -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, diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 8b66aa4ed..dea6a4a86 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -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 #include @@ -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) { diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 616971bc6..68e7d3156 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -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 m_focused_element = std::nullopt; - JoystickController *m_joystick; + GamepadController *m_joystick; bool m_show_debug = false; struct parserData { diff --git a/src/gui/guiKeyChangeMenu.h b/src/gui/guiKeyChangeMenu.h index 6bb3a9050..3c5537dfe 100644 --- a/src/gui/guiKeyChangeMenu.h +++ b/src/gui/guiKeyChangeMenu.h @@ -24,7 +24,7 @@ #include "irrlichttypes_extrabloated.h" #include "modalMenu.h" #include "gettext.h" -#include "client/keycode.h" +#include "client/input/keycode.h" #include #include diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index 07fe90585..c4b15266e 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchscreengui.cpp @@ -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 diff --git a/src/unittest/test_keycode.cpp b/src/unittest/test_keycode.cpp index 3398626fa..6a412f7b1 100644 --- a/src/unittest/test_keycode.cpp +++ b/src/unittest/test_keycode.cpp @@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "exceptions.h" -#include "client/keycode.h" +#include "client/input/keycode.h" class TestKeycode : public TestBase { public: