diff options
author | Wilhem Barbier <nounoursheureux@openmailbox.org> | 2016-06-15 07:25:35 +0200 |
---|---|---|
committer | RĂ©mi Verschelde <remi@verschelde.fr> | 2016-06-15 07:25:35 +0200 |
commit | f665200df7bd6d1660f6dd8255ad66f5c675770d (patch) | |
tree | 8686ad14a09013631c5a5853ff7a77c0bca6d64d | |
parent | 333de40180d7d8d6890cceaa1bc3d46efa2b5083 (diff) |
Add joystick vibration support on Linux (#5043)
-rw-r--r-- | core/os/input.cpp | 4 | ||||
-rw-r--r-- | core/os/input.h | 5 | ||||
-rw-r--r-- | doc/base/classes.xml | 37 | ||||
-rw-r--r-- | main/input_default.cpp | 47 | ||||
-rw-r--r-- | main/input_default.h | 17 | ||||
-rw-r--r-- | platform/x11/joystick_linux.cpp | 74 | ||||
-rw-r--r-- | platform/x11/joystick_linux.h | 7 |
7 files changed, 189 insertions, 2 deletions
diff --git a/core/os/input.cpp b/core/os/input.cpp index a766ef87fc..005a248aac 100644 --- a/core/os/input.cpp +++ b/core/os/input.cpp @@ -59,6 +59,10 @@ void Input::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_joy_axis","device","axis"),&Input::get_joy_axis); ObjectTypeDB::bind_method(_MD("get_joy_name","device"),&Input::get_joy_name); ObjectTypeDB::bind_method(_MD("get_joy_guid","device"),&Input::get_joy_guid); + ObjectTypeDB::bind_method(_MD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength); + ObjectTypeDB::bind_method(_MD("get_joy_vibration_duration", "device"), &Input::get_joy_vibration_duration); + ObjectTypeDB::bind_method(_MD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration); + ObjectTypeDB::bind_method(_MD("stop_joy_vibration", "device"), &Input::stop_joy_vibration); ObjectTypeDB::bind_method(_MD("get_accelerometer"),&Input::get_accelerometer); ObjectTypeDB::bind_method(_MD("get_magnetometer"),&Input::get_magnetometer); //ObjectTypeDB::bind_method(_MD("get_mouse_pos"),&Input::get_mouse_pos); - this is not the function you want diff --git a/core/os/input.h b/core/os/input.h index 46edb30aa1..6364d597b0 100644 --- a/core/os/input.h +++ b/core/os/input.h @@ -67,6 +67,11 @@ public: virtual void remove_joy_mapping(String p_guid)=0; virtual bool is_joy_known(int p_device)=0; virtual String get_joy_guid(int p_device) const=0; + virtual Vector2 get_joy_vibration_strength(int p_device)=0; + virtual float get_joy_vibration_duration(int p_device)=0; + virtual uint64_t get_joy_vibration_timestamp(int p_device)=0; + virtual void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration)=0; + virtual void stop_joy_vibration(int p_device)=0; virtual Point2 get_mouse_pos() const=0; virtual Point2 get_mouse_speed() const=0; diff --git a/doc/base/classes.xml b/doc/base/classes.xml index 0d8f7eecdd..71e972bac6 100644 --- a/doc/base/classes.xml +++ b/doc/base/classes.xml @@ -15788,6 +15788,23 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) Returns a SDL2 compatible device guid on platforms that use gamepad remapping. Returns "Default Gamepad" otherwise. </description> </method> + <method name="get_joy_vibration_strength"> + <return type="Vector2"> + </return> + <argument index="0" name="device" type="int"> + </argument> + <description> + Returns the strength of the joystick vibration: x is the strength of the weak motor, and y is the strength of the strong motor. + </description> + </method> + <method name="get_joy_vibration_duration"> + <return type="float"> + </return> + <argument index="0" name="device" type="int"> + </argument> + <description> + Returns the duration of the current vibration effect in seconds. + </description> <method name="get_accelerometer"> <return type="Vector3"> </return> @@ -15830,6 +15847,26 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) Return the mouse mode. See the constants for more information. </description> </method> + <method name="start_joy_vibration"> + <argument index="0" name="device" type="int"> + </argument> + <argument index="1" name="weak_magnitude" type="float"> + </argument> + <argument index="2" name="strong_magnitude" type="float"> + </argument> + <argument index="3" name="duration" type="float"> + </argument> + <description> + Starts to vibrate the joystick. Joysticks usually come with two rumble motors, a strong and a weak one. weak_magnitude is the strength of the weak motor (between 0 and 1) and strong_magnitude is the strength of the strong motor (between 0 and 1). duration is the duration of the effect in seconds (a duration of 0 will play the vibration indefinitely). + </description> + </method> + <method name="stop_joy_vibration"> + <argument index="0" name="device" type="int"> + </argument> + <description> + Stops the vibration of the joystick. + </description> + </method> <method name="warp_mouse_pos"> <argument index="0" name="to" type="Vector2"> </argument> diff --git a/main/input_default.cpp b/main/input_default.cpp index e36306727d..a6f14ae1f5 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -137,6 +137,30 @@ String InputDefault::get_joy_name(int p_idx) { return joy_names[p_idx].name; }; +Vector2 InputDefault::get_joy_vibration_strength(int p_device) { + if (joy_vibration.has(p_device)) { + return Vector2(joy_vibration[p_device].weak_magnitude, joy_vibration[p_device].strong_magnitude); + } else { + return Vector2(0, 0); + } +} + +uint64_t InputDefault::get_joy_vibration_timestamp(int p_device) { + if (joy_vibration.has(p_device)) { + return joy_vibration[p_device].timestamp; + } else { + return 0; + } +} + +float InputDefault::get_joy_vibration_duration(int p_device) { + if (joy_vibration.has(p_device)) { + return joy_vibration[p_device].duration; + } else { + return 0.f; + } +} + static String _hex_str(uint8_t p_byte) { static const char* dict = "0123456789abcdef"; @@ -294,6 +318,29 @@ void InputDefault::set_joy_axis(int p_device,int p_axis,float p_value) { _joy_axis[c]=p_value; } +void InputDefault::start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration) { + _THREAD_SAFE_METHOD_ + if (p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) { + return; + } + VibrationInfo vibration; + vibration.weak_magnitude = p_weak_magnitude; + vibration.strong_magnitude = p_strong_magnitude; + vibration.duration = p_duration; + vibration.timestamp = OS::get_singleton()->get_unix_time(); + joy_vibration[p_device] = vibration; +} + +void InputDefault::stop_joy_vibration(int p_device) { + _THREAD_SAFE_METHOD_ + VibrationInfo vibration; + vibration.weak_magnitude = 0; + vibration.strong_magnitude = 0; + vibration.duration = 0; + vibration.timestamp = OS::get_singleton()->get_unix_time(); + joy_vibration[p_device] = vibration; +} + void InputDefault::set_accelerometer(const Vector3& p_accel) { _THREAD_SAFE_METHOD_ diff --git a/main/input_default.h b/main/input_default.h index 8f6a430436..01b813f3ca 100644 --- a/main/input_default.h +++ b/main/input_default.h @@ -3,6 +3,7 @@ #include "os/input.h" + class InputDefault : public Input { OBJ_TYPE( InputDefault, Input ); @@ -19,6 +20,16 @@ class InputDefault : public Input { MainLoop *main_loop; bool emulate_touch; + + struct VibrationInfo { + float weak_magnitude; + float strong_magnitude; + float duration; // Duration in seconds + uint64_t timestamp; + }; + + Map<int, VibrationInfo> joy_vibration; + struct SpeedTrack { uint64_t last_tick; @@ -129,6 +140,9 @@ public: virtual float get_joy_axis(int p_device,int p_axis); String get_joy_name(int p_idx); + virtual Vector2 get_joy_vibration_strength(int p_device); + virtual float get_joy_vibration_duration(int p_device); + virtual uint64_t get_joy_vibration_timestamp(int p_device); void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = ""); void parse_joystick_mapping(String p_mapping, bool p_update_existing); @@ -147,6 +161,9 @@ public: void set_magnetometer(const Vector3& p_magnetometer); void set_joy_axis(int p_device,int p_axis,float p_value); + virtual void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration); + virtual void stop_joy_vibration(int p_device); + void set_main_loop(MainLoop *main_loop); void set_mouse_pos(const Point2& p_posf); diff --git a/platform/x11/joystick_linux.cpp b/platform/x11/joystick_linux.cpp index 2793cc5734..5ce0219df7 100644 --- a/platform/x11/joystick_linux.cpp +++ b/platform/x11/joystick_linux.cpp @@ -316,13 +316,21 @@ void joystick_linux::setup_joystick_properties(int p_id) { } } } -} + joy->force_feedback = false; + joy->ff_effect_timestamp = 0; + unsigned long ffbit[NBITS(FF_CNT)]; + if (ioctl(joy->fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) != -1) { + if (test_bit(FF_RUMBLE, ffbit)) { + joy->force_feedback = true; + } + } +} void joystick_linux::open_joystick(const char *p_path) { int joy_num = get_free_joy_slot(); - int fd = open(p_path, O_RDONLY | O_NONBLOCK); + int fd = open(p_path, O_RDWR | O_NONBLOCK); if (fd != -1 && joy_num != -1) { unsigned long evbit[NBITS(EV_MAX)] = { 0 }; @@ -392,6 +400,55 @@ void joystick_linux::open_joystick(const char *p_path) { } } +void joystick_linux::joystick_vibration_start(int p_id, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) +{ + Joystick& joy = joysticks[p_id]; + if (!joy.force_feedback || joy.fd == -1 || p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) { + return; + } + if (joy.ff_effect_id != -1) { + joystick_vibration_stop(p_id, p_timestamp); + } + + struct ff_effect effect; + effect.type = FF_RUMBLE; + effect.id = -1; + effect.u.rumble.weak_magnitude = floor(p_weak_magnitude * (float)0xffff); + effect.u.rumble.strong_magnitude = floor(p_strong_magnitude * (float)0xffff); + effect.replay.length = floor(p_duration * 1000); + effect.replay.delay = 0; + + if (ioctl(joy.fd, EVIOCSFF, &effect) < 0) { + return; + } + + struct input_event play; + play.type = EV_FF; + play.code = effect.id; + play.value = 1; + write(joy.fd, (const void*)&play, sizeof(play)); + + joy.ff_effect_id = effect.id; + joy.ff_effect_timestamp = p_timestamp; +} + +void joystick_linux::joystick_vibration_stop(int p_id, uint64_t p_timestamp) +{ + Joystick& joy = joysticks[p_id]; + if (!joy.force_feedback || joy.fd == -1 || joy.ff_effect_id == -1) { + return; + } + + struct input_event stop; + stop.type = EV_FF; + stop.code = joy.ff_effect_id; + stop.value = 0; + write(joy.fd, (const void*)&stop, sizeof(stop)); + + joy.ff_effect_id = -1; + joy.ff_effect_timestamp = p_timestamp; +} + InputDefault::JoyAxis joystick_linux::axis_correct(const input_absinfo *p_abs, int p_value) const { int min = p_abs->minimum; @@ -485,6 +542,19 @@ uint32_t joystick_linux::process_joysticks(uint32_t p_event_id) { if (len == 0 || (len < 0 && errno != EAGAIN)) { close_joystick(i); }; + + if (joy->force_feedback) { + uint64_t timestamp = input->get_joy_vibration_timestamp(i); + if (timestamp > joy->ff_effect_timestamp) { + Vector2 strength = input->get_joy_vibration_strength(i); + float duration = input->get_joy_vibration_duration(i); + if (strength.x == 0 && strength.y == 0) { + joystick_vibration_stop(i, timestamp); + } else { + joystick_vibration_start(i, strength.x, strength.y, duration, timestamp); + } + } + } } joy_mutex->unlock(); return p_event_id; diff --git a/platform/x11/joystick_linux.h b/platform/x11/joystick_linux.h index 4f0533721b..7ea2664ebb 100644 --- a/platform/x11/joystick_linux.h +++ b/platform/x11/joystick_linux.h @@ -61,6 +61,10 @@ private: String devpath; input_absinfo *abs_info[MAX_ABS]; + bool force_feedback; + int ff_effect_id; + uint64_t ff_effect_timestamp; + Joystick(); ~Joystick(); void reset(); @@ -88,6 +92,9 @@ private: void run_joystick_thread(); void open_joystick(const char* path); + void joystick_vibration_start(int p_id, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp); + void joystick_vibration_stop(int p_id, uint64_t p_timestamp); + InputDefault::JoyAxis axis_correct(const input_absinfo *abs, int value) const; }; |