Add Joystick type detection and Xbox controller support
authorrubenwardy <rubenwardy@gmail.com>
Sun, 2 Apr 2017 22:00:34 +0000 (23:00 +0100)
committerAuke Kok <sofar+github@foo-projects.org>
Fri, 7 Apr 2017 03:58:52 +0000 (20:58 -0700)
* Add joystick type detection (with joystick_type setting to override it)
* Fix multiple joysticks from interfering with each other by only reading from one (add joystick_id setting)
* Add support for Xbox controllers

builtin/settingtypes.txt
minetest.conf.example
src/client/clientlauncher.cpp
src/client/clientlauncher.h
src/client/joystick_controller.cpp
src/client/joystick_controller.h
src/defaultsettings.cpp
src/game.h

index dc3164e440dc175eefac4ad7685e1f4a2f6e3c7b..d95f8cf416e7a282af5bb1e5f3b1d597244e0224 100644 (file)
@@ -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
index 292a645e3d1f4c15bf1f9f9dacf5f1251e7849b5..cfc66f168b6da6917216e9a5b2d7501f34fe192d 100644 (file)
 #    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
-
index 3ec7be7d99f81a0786d2f83816a41c07e713469d..249f6727a23478ee898d22382ba9a9a88277f9d2 100644 (file)
@@ -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<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 (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<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 (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;
 }
index b10bbebc91f4d358b6a636cfde6e30b6542e4059..ab22d7aaa180082d7eab8ebf981d96c0e5ce081f 100644 (file)
@@ -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);
index ef8d18ab05616346e1467f1195918194b66a733b..311cd22fb3803a368ac9d4410c019949603897dc 100644 (file)
@@ -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<irr::SJoystickInfo> &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<KeyType::INTERNAL_ENUM_COUNT> 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;
 }
index ed0ee4068296fc602d1d16fa4efdf5c2e44bd5d7..867a0c3f2a68cf12c39ab575d17c55ae473b4122 100644 (file)
@@ -98,6 +98,9 @@ class JoystickController {
 
 public:
        JoystickController();
+
+       void onJoystickConnect(const std::vector<irr::SJoystickInfo> &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<KeyType::INTERNAL_ENUM_COUNT> m_pressed_keys;
 
        f32 m_internal_time;
index 4b50b991b8098dd3b5bb0d3683c51a5cc21fb409..3f4ce6337ac37b1c13a442d877a1dc981a4a8bab 100644 (file)
@@ -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");
 
index 19992ce3da7c3b95ad75dcfe55043918cbd9c725..eaedca165d3831b958bcfb3aa05114bdfa3a21d6 100644 (file)
@@ -171,4 +171,3 @@ void the_game(bool *kill,
                bool simple_singleplayer_mode);
 
 #endif
-