diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index dc3164e44..d95f8cf41 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -107,6 +107,12 @@ continuous_forward (Continuous forward) bool false # Enable Joysticks enable_joysticks (Enable Joysticks) bool false +# The identifier of the joystick to use +joystick_id (Joystick ID) int 0 + +# The type of joystick +joystick_type (Joystick Type) enum auto auto,generic,xbox + # 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 diff --git a/minetest.conf.example b/minetest.conf.example index 292a645e3..cfc66f168 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -87,6 +87,14 @@ # type: bool # enable_joysticks = false +# The identifier of the joystick to use +# type: int +# joystick_id = 0 + +# The type of joystick +# type: enum values: auto,generic,xbox +# joystick_type = auto + # The time in seconds it takes between repeated events # when holding down a joystick button combination. # type: float @@ -1786,4 +1794,3 @@ server_side_occlusion_culling = true # Print the engine's profiling data in regular intervals (in seconds). 0 = disable. Useful for developers. # type: int # profiler_print_interval = 0 - diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 3ec7be7d9..249f6727a 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -127,10 +127,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) device->setResizable(true); - if (random_input) - input = new RandomInputHandler(); - else - input = new RealInputHandler(device, receiver); + init_input(); smgr = device->getSceneManager(); smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true); @@ -337,6 +334,33 @@ bool ClientLauncher::init_engine() return device != NULL; } +void ClientLauncher::init_input() +{ + if (random_input) + input = new RandomInputHandler(); + else + input = new RealInputHandler(device, 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 (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; + } + } +} + bool ClientLauncher::launch_game(std::string &error_message, bool reconnect_requested, GameParams &game_params, const Settings &cmd_args) @@ -566,25 +590,8 @@ bool ClientLauncher::create_engine_device() device = createDeviceEx(params); - if (device) { - 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 (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]); - } - } else { - errorstream << "Could not activate joystick support." << std::endl; - } - } + if (device) porting::initIrrlicht(device); - } return device != NULL; } diff --git a/src/client/clientlauncher.h b/src/client/clientlauncher.h index b10bbebc9..ab22d7aaa 100644 --- a/src/client/clientlauncher.h +++ b/src/client/clientlauncher.h @@ -91,6 +91,7 @@ public: protected: void init_args(GameParams &game_params, const Settings &cmd_args); bool init_engine(); + void init_input(); bool launch_game(std::string &error_message, bool reconnect_requested, GameParams &game_params, const Settings &cmd_args); diff --git a/src/client/joystick_controller.cpp b/src/client/joystick_controller.cpp index ef8d18ab0..311cd22fb 100644 --- a/src/client/joystick_controller.cpp +++ b/src/client/joystick_controller.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "keys.h" #include "settings.h" #include "gettime.h" +#include "../util/string.h" bool JoystickButtonCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const { @@ -42,7 +43,7 @@ bool JoystickAxisCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const #define JLO_B_PB(A, B, C) jlo.button_keys.push_back(JoystickButtonCmb(A, B, C)) #define JLO_A_PB(A, B, C, D) jlo.axis_keys.push_back(JoystickAxisCmb(A, B, C, D)) -static JoystickLayout create_default_layout() +JoystickLayout create_default_layout() { JoystickLayout jlo; @@ -103,11 +104,59 @@ static JoystickLayout create_default_layout() return jlo; } -static const JoystickLayout default_layout = create_default_layout(); +JoystickLayout create_xbox_layout() +{ + JoystickLayout jlo; + + jlo.axes_dead_border = 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::SPECIAL1, 1 << 2, 1 << 2); // X/blue + JLO_B_PB(KeyType::INVENTORY, 1 << 3, 1 << 3); // Y/yellow + + // Analog Sticks + JLO_B_PB(KeyType::SPECIAL1, 1 << 11, 1 << 11); // left + JLO_B_PB(KeyType::SNEAK, 1 << 12, 1 << 12); // right + + // Triggers + JLO_B_PB(KeyType::MOUSE_L, 1 << 6, 1 << 6); // lt + JLO_B_PB(KeyType::MOUSE_R, 1 << 7, 1 << 7); // rt + JLO_B_PB(KeyType::SCROLL_UP, 1 << 4, 1 << 4); // lb + JLO_B_PB(KeyType::SCROLL_DOWN, 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, 1024); + JLO_A_PB(KeyType::BACKWARD, 1, -1, 1024); + JLO_A_PB(KeyType::LEFT, 0, 1, 1024); + JLO_A_PB(KeyType::RIGHT, 0, -1, 1024); + + return jlo; +} JoystickController::JoystickController() { - m_layout = &default_layout; + m_joystick_id = 0; + doubling_dtime = g_settings->getFloat("repeat_joystick_button_time"); for (size_t i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) { @@ -116,23 +165,54 @@ JoystickController::JoystickController() 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 >= joystick_infos.size()) { + // TODO: auto detection + id = 0; + } + + if (id >= 0 && id < joystick_infos.size()) { + if (layout.empty() || layout == "auto") + setLayoutFromControllerName(joystick_infos[id].Name.c_str()); + else + setLayoutFromControllerName(layout); + } + + m_joystick_id = id; +} + +void JoystickController::setLayoutFromControllerName(std::string name) { + if (lowercase(name).find("xbox") >= 0) { + m_layout = create_xbox_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 = getTimeMs() / 1000.f; std::bitset keys_pressed; // First generate a list of keys pressed - for (size_t i = 0; i < m_layout->button_keys.size(); i++) { - if (m_layout->button_keys[i].isTriggered(ev)) { - keys_pressed.set(m_layout->button_keys[i].key); + for (size_t i = 0; i < m_layout.button_keys.size(); i++) { + if (m_layout.button_keys[i].isTriggered(ev)) { + keys_pressed.set(m_layout.button_keys[i].key); } } - for (size_t i = 0; i < m_layout->axis_keys.size(); i++) { - if (m_layout->axis_keys[i].isTriggered(ev)) { - keys_pressed.set(m_layout->axis_keys[i].key); + for (size_t i = 0; i < m_layout.axis_keys.size(); i++) { + if (m_layout.axis_keys[i].isTriggered(ev)) { + keys_pressed.set(m_layout.axis_keys[i].key); } } @@ -153,7 +233,7 @@ bool JoystickController::handleEvent(const irr::SEvent::SJoystickEvent &ev) } for (size_t i = 0; i < JA_COUNT; i++) { - const JoystickAxisLayout &ax_la = m_layout->axes[i]; + const JoystickAxisLayout &ax_la = m_layout.axes[i]; m_axes_vals[i] = ax_la.invert * ev.Axis[ax_la.axis_id]; } @@ -172,8 +252,8 @@ void JoystickController::clear() s16 JoystickController::getAxisWithoutDead(JoystickAxis axis) { s16 v = m_axes_vals[axis]; - if (((v > 0) && (v < m_layout->axes_dead_border)) || - ((v < 0) && (v > -m_layout->axes_dead_border))) + if (((v > 0) && (v < m_layout.axes_dead_border)) || + ((v < 0) && (v > -m_layout.axes_dead_border))) return 0; return v; } diff --git a/src/client/joystick_controller.h b/src/client/joystick_controller.h index ed0ee4068..867a0c3f2 100644 --- a/src/client/joystick_controller.h +++ b/src/client/joystick_controller.h @@ -98,6 +98,9 @@ class JoystickController { public: JoystickController(); + + void onJoystickConnect(const std::vector &joystick_infos); + bool handleEvent(const irr::SEvent::SJoystickEvent &ev); void clear(); @@ -146,10 +149,14 @@ public: f32 doubling_dtime; private: - const JoystickLayout *m_layout; + void setLayoutFromControllerName(std::string name); + + JoystickLayout m_layout; s16 m_axes_vals[JA_COUNT]; + u8 m_joystick_id; + std::bitset m_pressed_keys; f32 m_internal_time; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 4b50b991b..3f4ce6337 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -203,6 +203,8 @@ void set_default_settings(Settings *settings) settings->setDefault("always_fly_fast", "true"); settings->setDefault("continuous_forward", "false"); settings->setDefault("enable_joysticks", "false"); + settings->setDefault("joystick_id", "0"); + settings->setDefault("joystick_type", ""); settings->setDefault("repeat_joystick_button_time", "0.17"); settings->setDefault("joystick_frustum_sensitivity", "170"); diff --git a/src/game.h b/src/game.h index 19992ce3d..eaedca165 100644 --- a/src/game.h +++ b/src/game.h @@ -171,4 +171,3 @@ void the_game(bool *kill, bool simple_singleplayer_mode); #endif -