浏览代码

Merge pull request #5225 from Hinsbart/x_rumble

Windows: Support gamepad vibration using XInput.
Rémi Verschelde 9 年之前
父节点
当前提交
57e1387199
共有 3 个文件被更改,包括 65 次插入6 次删除
  1. 1 1
      main/input_default.cpp
  2. 52 3
      platform/windows/joystick.cpp
  3. 12 2
      platform/windows/joystick.h

+ 1 - 1
main/input_default.cpp

@@ -355,7 +355,7 @@ void InputDefault::start_joy_vibration(int p_device, float p_weak_magnitude, flo
 	vibration.weak_magnitude = p_weak_magnitude;
 	vibration.strong_magnitude = p_strong_magnitude;
 	vibration.duration = p_duration;
-	vibration.timestamp = OS::get_singleton()->get_unix_time();
+	vibration.timestamp = OS::get_singleton()->get_ticks_usec();
 	joy_vibration[p_device] = vibration;
 }
 

+ 52 - 3
platform/windows/joystick.cpp

@@ -37,6 +37,7 @@
 #endif
 
 DWORD WINAPI _xinput_get_state(DWORD dwUserIndex, XINPUT_STATE* pState) { return ERROR_DEVICE_NOT_CONNECTED; }
+DWORD WINAPI _xinput_set_state(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration) { return ERROR_DEVICE_NOT_CONNECTED; }
 
 joystick_windows::joystick_windows() {
 
@@ -50,6 +51,7 @@ joystick_windows::joystick_windows(InputDefault* _input, HWND* hwnd) {
 	dinput = NULL;
 	xinput_dll = NULL;
 	xinput_get_state = NULL;
+	xinput_set_state = NULL;
 
 	load_xinput();
 
@@ -300,6 +302,9 @@ void joystick_windows::probe_joysticks() {
 
 				x_joysticks[i].attached = true;
 				x_joysticks[i].id = id;
+				x_joysticks[i].ff_timestamp = 0;
+				x_joysticks[i].ff_end_timestamp = 0;
+				x_joysticks[i].vibrating = false;
 				attached_joysticks[id] = true;
 				input->joy_connection_changed(id, true, "XInput Gamepad","__XINPUT_DEVICE__");
 			}
@@ -358,6 +363,20 @@ unsigned int joystick_windows::process_joysticks(unsigned int p_last_id) {
 			p_last_id = input->joy_axis(p_last_id, joy.id, JOY_AXIS_5,  axis_correct(joy.state.Gamepad.bRightTrigger, true, true));
 			joy.last_packet = joy.state.dwPacketNumber;
 		}
+		uint64_t timestamp = input->get_joy_vibration_timestamp(joy.id);
+		if (timestamp > joy.ff_timestamp) {
+			Vector2 strength = input->get_joy_vibration_strength(joy.id);
+			float duration = input->get_joy_vibration_duration(joy.id);
+			if (strength.x == 0 && strength.y == 0) {
+				joystick_vibration_stop_xinput(i, timestamp);
+			} else {
+				joystick_vibration_start_xinput(i, strength.x, strength.y, duration, timestamp);
+			}
+		} else if (joy.vibrating && joy.ff_end_timestamp != 0) {
+			uint64_t current_time = OS::get_singleton()->get_ticks_usec();
+			if (current_time >= joy.ff_end_timestamp)
+				joystick_vibration_stop_xinput(i, current_time);
+		}
 	}
 
 	for (int i = 0; i < JOYSTICKS_MAX; i++) {
@@ -401,7 +420,7 @@ unsigned int joystick_windows::process_joysticks(unsigned int p_last_id) {
 			}
 		}
 
-        // on mingw, these constants are not constants
+		// on mingw, these constants are not constants
 		int count = 6;
 		int axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ };
 		int values[] = { js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz };
@@ -500,9 +519,38 @@ InputDefault::JoyAxis joystick_windows::axis_correct(int p_val, bool p_xinput, b
 	return jx;
 }
 
+void joystick_windows::joystick_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
+	xinput_gamepad &joy = x_joysticks[p_device];
+	if (joy.attached) {
+		XINPUT_VIBRATION effect;
+		effect.wLeftMotorSpeed = (65535 * p_strong_magnitude);
+		effect.wRightMotorSpeed = (65535 * p_weak_magnitude);
+		if (xinput_set_state(p_device, &effect) == ERROR_SUCCESS) {
+			joy.ff_timestamp = p_timestamp;
+			joy.ff_end_timestamp = p_duration == 0 ? 0 : p_timestamp + (uint64_t)(p_duration * 1000000.0);
+			joy.vibrating = true;
+		}
+	}
+}
+
+void joystick_windows::joystick_vibration_stop_xinput(int p_device, uint64_t p_timestamp) {
+	xinput_gamepad &joy = x_joysticks[p_device];
+	if (joy.attached) {
+		XINPUT_VIBRATION effect;
+		effect.wLeftMotorSpeed  = 0;
+		effect.wRightMotorSpeed = 0;
+		if (xinput_set_state(p_device, &effect) == ERROR_SUCCESS) {
+			joy.ff_timestamp = p_timestamp;
+			joy.vibrating = false;
+		}
+	}
+}
+
+
 void joystick_windows::load_xinput() {
 
 	xinput_get_state = &_xinput_get_state;
+	xinput_set_state = &_xinput_set_state;
 	xinput_dll = LoadLibrary( "XInput1_4.dll" );
 	if (!xinput_dll) {
 		xinput_dll = LoadLibrary("XInput1_3.dll");
@@ -519,12 +567,13 @@ void joystick_windows::load_xinput() {
 	}
 
 	XInputGetState_t func = (XInputGetState_t)GetProcAddress((HMODULE)xinput_dll, "XInputGetState");
-	if (!func) {
+	XInputSetState_t set_func = (XInputSetState_t)GetProcAddress((HMODULE)xinput_dll, "XInputSetState");
+	if (!func || !set_func) {
 		unload_xinput();
 		return;
 	}
 	xinput_get_state = func;
-	return;
+	xinput_set_state = set_func;
 }
 
 void joystick_windows::unload_xinput() {

+ 12 - 2
platform/windows/joystick.h

@@ -39,8 +39,8 @@
 #define SAFE_RELEASE(x) \
 if(x != NULL)           \
 {                       \
-    x->Release();       \
-    x = NULL;           \
+	x->Release();       \
+	x = NULL;           \
 }
 #endif
 
@@ -96,16 +96,23 @@ private:
 
 		int id;
 		bool attached;
+		bool vibrating;
 		DWORD last_packet;
 		XINPUT_STATE state;
+		uint64_t ff_timestamp;
+		uint64_t ff_end_timestamp;
 
 		xinput_gamepad() {
 			attached = false;
+			vibrating = false;
+			ff_timestamp = 0;
+			ff_end_timestamp = 0;
 			last_packet = 0;
 		}
 	};
 
 	typedef DWORD (WINAPI *XInputGetState_t) (DWORD dwUserIndex, XINPUT_STATE* pState);
+	typedef DWORD (WINAPI *XInputSetState_t) (DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);
 
 	HWND* hWnd;
 	HANDLE xinput_dll;
@@ -132,9 +139,12 @@ private:
 	bool have_device(const GUID &p_guid);
 	bool is_xinput_device(const GUID* p_guid);
 	bool setup_dinput_joystick(const DIDEVICEINSTANCE* instance);
+	void joystick_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
+	void joystick_vibration_stop_xinput(int p_device, uint64_t p_timestamp);
 
 	InputDefault::JoyAxis axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const;
 	XInputGetState_t xinput_get_state;
+	XInputSetState_t xinput_set_state;
 };
 
 #endif