Last active
August 27, 2022 17:02
-
-
Save Ryochan7/7329c4355e38ccebe391a844717998ba to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/DS4Windows/DS4Control/ControlService.cs b/DS4Windows/DS4Control/ControlService.cs | |
index b33bec76..8a3e54c2 100644 | |
--- a/DS4Windows/DS4Control/ControlService.cs | |
+++ b/DS4Windows/DS4Control/ControlService.cs | |
@@ -13,6 +13,7 @@ using SharpOSC; | |
using static DS4Windows.Global; | |
using DS4WinWPF.DS4Control; | |
using DS4Windows.DS4Control; | |
+using Nefarius.ViGEm.Client.Targets.DualShock4; | |
namespace DS4Windows | |
{ | |
@@ -971,81 +972,143 @@ namespace DS4Windows | |
tempXbox.cont.FeedbackReceived += p; | |
tempXbox.forceFeedbacksDict.Add(index, p); | |
} | |
+ else if (contType == OutContType.DS4) | |
+ { | |
+ DS4OutDevice tempDS4 = outDevice as DS4OutDevice; | |
+ if (tempDS4.CanUseAwaitOutputBuffer) | |
+ { | |
+ DS4OutDeviceExt.ReceivedOutBufferHandler processOutBuffAction = (DS4OutDeviceExt sender, byte[] reportData) => | |
+ { | |
+ bool useRumble = false; bool useLight = false; | |
+ byte flashOn = 0; byte flashOff = 0; | |
+ DS4Color? color = null; | |
+ if ((reportData[1] & DS4OutDevice.RUMBLE_FEATURE_FLAG) != 0) | |
+ { | |
+ useRumble = true; | |
+ device.setRumble(reportData[4], reportData[5]); | |
+ //SetDevRumble(device, devour[4], devour[5], devIndex); | |
+ } | |
+ | |
+ if ((reportData[1] & DS4OutDevice.LIGHTBAR_FEATURE_FLAG) != 0) | |
+ { | |
+ useLight = true; | |
+ color = new DS4Color(reportData[6], | |
+ reportData[7], | |
+ reportData[8]); | |
+ } | |
+ else | |
+ { | |
+ color = device.LightBarColor; | |
+ } | |
+ | |
+ if ((reportData[1] & DS4OutDevice.FLASH_FEATURE_FLAG) != 0) | |
+ { | |
+ useLight = true; | |
+ flashOn = reportData[9]; | |
+ flashOff = reportData[10]; | |
+ } | |
+ else | |
+ { | |
+ ref DS4LightbarState currentLight = | |
+ ref device.GetLightbarStateRef(); | |
+ | |
+ flashOn = currentLight.LightBarFlashDurationOn; | |
+ flashOff = currentLight.LightBarFlashDurationOff; | |
+ } | |
+ | |
+ if (useLight) | |
+ { | |
+ DS4LightbarState lightState = new DS4LightbarState | |
+ { | |
+ LightBarColor = (DS4Color)color, | |
+ LightBarFlashDurationOn = flashOn, | |
+ LightBarFlashDurationOff = flashOff, | |
+ }; | |
+ device.SetLightbarState(ref lightState); | |
+ } | |
+ }; | |
+ | |
+ DS4OutDeviceExt tempDS4Ext = tempDS4 as DS4OutDeviceExt; | |
+ tempDS4Ext.ReceivedOutBuffer += processOutBuffAction; | |
+ tempDS4Ext.outBufferFeedbacksDict.TryAdd(index, processOutBuffAction); | |
+ tempDS4Ext.StartOutputBufferThread(); | |
+ } | |
+ } | |
//else if (contType == OutContType.DS4) | |
//{ | |
// DS4OutDevice tempDS4 = outDevice as DS4OutDevice; | |
// LightbarSettingInfo deviceLightbarSettingsInfo = Global.LightbarSettingsInfo[devIndex]; | |
- // Nefarius.ViGEm.Client.Targets.DualShock4FeedbackReceivedEventHandler p = (sender, args) => | |
- // { | |
- // bool useRumble = false; bool useLight = false; | |
- // byte largeMotor = args.LargeMotor; | |
- // byte smallMotor = args.SmallMotor; | |
- // //SetDevRumble(device, largeMotor, smallMotor, devIndex); | |
- // DS4Color color = new DS4Color(args.LightbarColor.Red, | |
- // args.LightbarColor.Green, | |
- // args.LightbarColor.Blue); | |
- | |
- // //Console.WriteLine("IN EVENT"); | |
- // //Console.WriteLine("Rumble ({0}, {1}) | Light ({2}, {3}, {4}) {5}", | |
- // // largeMotor, smallMotor, color.red, color.green, color.blue, DateTime.Now.ToString("hh:mm:ss.FFFF")); | |
- | |
- // if (largeMotor != 0 || smallMotor != 0) | |
- // { | |
- // useRumble = true; | |
- // } | |
- | |
- // // Let games to control lightbar only when the mode is Passthru (otherwise DS4Windows controls the light) | |
- // if (deviceLightbarSettingsInfo.Mode == LightbarMode.Passthru && (color.red != 0 || color.green != 0 || color.blue != 0)) | |
- // { | |
- // useLight = true; | |
- // } | |
- | |
- // if (!useRumble && !useLight) | |
- // { | |
- // //Console.WriteLine("Fallback"); | |
- // if (device.LeftHeavySlowRumble != 0 || device.RightLightFastRumble != 0) | |
- // { | |
- // useRumble = true; | |
- // } | |
- // else if (deviceLightbarSettingsInfo.Mode == LightbarMode.Passthru && | |
- // (device.LightBarColor.red != 0 || | |
- // device.LightBarColor.green != 0 || | |
- // device.LightBarColor.blue != 0)) | |
- // { | |
- // useLight = true; | |
- // } | |
- // } | |
- | |
- // if (useRumble) | |
- // { | |
- // //Console.WriteLine("Perform rumble"); | |
- // SetDevRumble(device, largeMotor, smallMotor, devIndex); | |
- // } | |
- | |
- // if (useLight) | |
- // { | |
- // //Console.WriteLine("Change lightbar color"); | |
- // /*DS4HapticState haptics = new DS4HapticState | |
- // { | |
- // LightBarColor = color, | |
- // }; | |
- // device.SetHapticState(ref haptics); | |
- // */ | |
- | |
- // DS4LightbarState lightState = new DS4LightbarState | |
- // { | |
- // LightBarColor = color, | |
- // }; | |
- // device.SetLightbarState(ref lightState); | |
- // } | |
- | |
- // //Console.WriteLine(); | |
- // }; | |
- | |
- // tempDS4.cont.FeedbackReceived += p; | |
- // tempDS4.forceFeedbacksDict.Add(index, p); | |
- //} | |
+ // Nefarius.ViGEm.Client.Targets.DualShock4FeedbackReceivedEventHandler p = (sender, args) => | |
+ // { | |
+ // bool useRumble = false; bool useLight = false; | |
+ // byte largeMotor = args.LargeMotor; | |
+ // byte smallMotor = args.SmallMotor; | |
+ // //SetDevRumble(device, largeMotor, smallMotor, devIndex); | |
+ // DS4Color color = new DS4Color(args.LightbarColor.Red, | |
+ // args.LightbarColor.Green, | |
+ // args.LightbarColor.Blue); | |
+ | |
+ // //Console.WriteLine("IN EVENT"); | |
+ // //Console.WriteLine("Rumble ({0}, {1}) | Light ({2}, {3}, {4}) {5}", | |
+ // // largeMotor, smallMotor, color.red, color.green, color.blue, DateTime.Now.ToString("hh:mm:ss.FFFF")); | |
+ | |
+ // if (largeMotor != 0 || smallMotor != 0) | |
+ // { | |
+ // useRumble = true; | |
+ // } | |
+ | |
+ // // Let games to control lightbar only when the mode is Passthru (otherwise DS4Windows controls the light) | |
+ // if (deviceLightbarSettingsInfo.Mode == LightbarMode.Passthru && (color.red != 0 || color.green != 0 || color.blue != 0)) | |
+ // { | |
+ // useLight = true; | |
+ // } | |
+ | |
+ // if (!useRumble && !useLight) | |
+ // { | |
+ // //Console.WriteLine("Fallback"); | |
+ // if (device.LeftHeavySlowRumble != 0 || device.RightLightFastRumble != 0) | |
+ // { | |
+ // useRumble = true; | |
+ // } | |
+ // else if (deviceLightbarSettingsInfo.Mode == LightbarMode.Passthru && | |
+ // (device.LightBarColor.red != 0 || | |
+ // device.LightBarColor.green != 0 || | |
+ // device.LightBarColor.blue != 0)) | |
+ // { | |
+ // useLight = true; | |
+ // } | |
+ // } | |
+ | |
+ // if (useRumble) | |
+ // { | |
+ // //Console.WriteLine("Perform rumble"); | |
+ // SetDevRumble(device, largeMotor, smallMotor, devIndex); | |
+ // } | |
+ | |
+ // if (useLight) | |
+ // { | |
+ // //Console.WriteLine("Change lightbar color"); | |
+ // /*DS4HapticState haptics = new DS4HapticState | |
+ // { | |
+ // LightBarColor = color, | |
+ // }; | |
+ // device.SetHapticState(ref haptics); | |
+ // */ | |
+ | |
+ // DS4LightbarState lightState = new DS4LightbarState | |
+ // { | |
+ // LightBarColor = color, | |
+ // }; | |
+ // device.SetLightbarState(ref lightState); | |
+ // } | |
+ | |
+ // //Console.WriteLine(); | |
+ // }; | |
+ | |
+ // tempDS4.cont.FeedbackReceived += p; | |
+ // tempDS4.forceFeedbacksDict.Add(index, p); | |
+ //} | |
} | |
public void RemoveOutFeedback(OutContType contType, OutputDevice outDevice, int inIdx) | |
@@ -1057,6 +1120,11 @@ namespace DS4Windows | |
//tempXbox.cont.FeedbackReceived -= tempXbox.forceFeedbackCall; | |
//tempXbox.forceFeedbackCall = null; | |
} | |
+ else if (contType == OutContType.DS4) | |
+ { | |
+ DS4OutDevice tempDS4 = outDevice as DS4OutDevice; | |
+ tempDS4.RemoveFeedback(inIdx); | |
+ } | |
//else if (contType == OutContType.DS4) | |
//{ | |
// DS4OutDevice tempDS4 = outDevice as DS4OutDevice; | |
diff --git a/DS4Windows/DS4Control/DS4OutDevice.cs b/DS4Windows/DS4Control/DS4OutDevice.cs | |
index acbb1187..cb893014 100644 | |
--- a/DS4Windows/DS4Control/DS4OutDevice.cs | |
+++ b/DS4Windows/DS4Control/DS4OutDevice.cs | |
@@ -11,6 +11,10 @@ namespace DS4Windows | |
{ | |
abstract class DS4OutDevice : OutputDevice | |
{ | |
+ internal const byte RUMBLE_FEATURE_FLAG = 0x01; | |
+ internal const byte LIGHTBAR_FEATURE_FLAG = 0x02; | |
+ internal const byte FLASH_FEATURE_FLAG = 0x04; | |
+ | |
public const string devtype = "DS4"; | |
public IDualShock4Controller cont; | |
@@ -18,6 +22,9 @@ namespace DS4Windows | |
public Dictionary<int, DualShock4FeedbackReceivedEventHandler> forceFeedbacksDict = | |
new Dictionary<int, DualShock4FeedbackReceivedEventHandler>(); | |
+ protected bool canUseAwaitOutputBuffer = false; | |
+ public bool CanUseAwaitOutputBuffer => canUseAwaitOutputBuffer; | |
+ | |
public DS4OutDevice(ViGEmClient client) | |
{ | |
cont = client.CreateDualShock4Controller(); | |
@@ -64,5 +71,9 @@ namespace DS4Windows | |
forceFeedbacksDict.Remove(inIdx); | |
} | |
} | |
+ | |
+ public virtual void StartOutputBufferThread() | |
+ { | |
+ } | |
} | |
} | |
diff --git a/DS4Windows/DS4Control/DS4OutDevices/DS4OutDeviceExt.cs b/DS4Windows/DS4Control/DS4OutDevices/DS4OutDeviceExt.cs | |
index bdd43206..a1710f1f 100644 | |
--- a/DS4Windows/DS4Control/DS4OutDevices/DS4OutDeviceExt.cs | |
+++ b/DS4Windows/DS4Control/DS4OutDevices/DS4OutDeviceExt.cs | |
@@ -1,10 +1,11 @@ | |
using Nefarius.ViGEm.Client; | |
+using Nefarius.ViGEm.Client.Targets; | |
using Nefarius.ViGEm.Client.Targets.DualShock4; | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
-using System.Threading.Tasks; | |
+using System.Threading; | |
namespace DS4Windows | |
{ | |
@@ -12,11 +13,26 @@ namespace DS4Windows | |
{ | |
private byte[] rawOutReportEx = new byte[63]; | |
private DS4_REPORT_EX outDS4Report; | |
+ private Thread awaitOutBuffThread; | |
+ public delegate void ReceivedOutBufferHandler(DS4OutDeviceExt sender, | |
+ byte[] reportData); | |
+ | |
+ public event ReceivedOutBufferHandler ReceivedOutBuffer; | |
+ | |
+ public Dictionary<int, ReceivedOutBufferHandler> outBufferFeedbacksDict = | |
+ new Dictionary<int, ReceivedOutBufferHandler>(); | |
+ private CancellationTokenSource tokenSource; | |
public DS4OutDeviceExt(ViGEmClient client) : base(client) | |
{ | |
} | |
+ public DS4OutDeviceExt(ViGEmClient client, bool useAwaitOutputBuffer) : | |
+ this(client) | |
+ { | |
+ canUseAwaitOutputBuffer = useAwaitOutputBuffer; | |
+ } | |
+ | |
public override unsafe void ConvertandSendReport(DS4State state, int device) | |
{ | |
if (!connected) return; | |
@@ -184,5 +200,109 @@ namespace DS4Windows | |
cont.SubmitRawReport(rawOutReportEx); | |
} | |
} | |
+ | |
+ public override void StartOutputBufferThread() | |
+ { | |
+ if (awaitOutBuffThread == null) | |
+ { | |
+ awaitOutBuffThread = new Thread(RunFetchAwaitBufferThread); | |
+ awaitOutBuffThread.Name = "Virtual DS4 Output Buffer Thread"; | |
+ awaitOutBuffThread.Priority = ThreadPriority.Normal; | |
+ awaitOutBuffThread.IsBackground = true; | |
+ awaitOutBuffThread.Start(); | |
+ } | |
+ } | |
+ | |
+ private void RunFetchAwaitBufferThread() | |
+ { | |
+ tokenSource = new CancellationTokenSource(); | |
+ CancellationToken ct = tokenSource.Token; | |
+ | |
+ bool working = true; | |
+ while (working) | |
+ { | |
+ if (!ct.IsCancellationRequested) | |
+ { | |
+ byte[] reportData = null; | |
+ bool reportGrab = false; | |
+ try | |
+ { | |
+ reportData = cont.AwaitRawOutputReport(100, out bool timedOut).ToArray(); | |
+ if (!timedOut) | |
+ { | |
+ reportGrab = true; | |
+ } | |
+ } | |
+ catch (System.ComponentModel.Win32Exception) | |
+ { | |
+ working = false; | |
+ } | |
+ catch (Exception) | |
+ { | |
+ working = false; | |
+ } | |
+ | |
+ if (!working) | |
+ { | |
+ break; | |
+ } | |
+ | |
+ if (reportGrab) | |
+ { | |
+ ReceivedOutBuffer?.Invoke(this, reportData); | |
+ } | |
+ } | |
+ else | |
+ { | |
+ working = false; | |
+ } | |
+ } | |
+ } | |
+ | |
+ public override void RemoveFeedbacks() | |
+ { | |
+ if (!canUseAwaitOutputBuffer) | |
+ { | |
+ base.RemoveFeedbacks(); | |
+ } | |
+ else | |
+ { | |
+ foreach (KeyValuePair<int, ReceivedOutBufferHandler> pair in outBufferFeedbacksDict) | |
+ { | |
+ ReceivedOutBuffer -= pair.Value; | |
+ } | |
+ | |
+ outBufferFeedbacksDict.Clear(); | |
+ } | |
+ } | |
+ | |
+ public override void RemoveFeedback(int inIdx) | |
+ { | |
+ if (!canUseAwaitOutputBuffer) | |
+ { | |
+ base.RemoveFeedback(inIdx); | |
+ } | |
+ else | |
+ { | |
+ if (outBufferFeedbacksDict.TryGetValue(inIdx, out ReceivedOutBufferHandler handler)) | |
+ { | |
+ ReceivedOutBuffer -= handler; | |
+ outBufferFeedbacksDict.Remove(inIdx); | |
+ } | |
+ } | |
+ } | |
+ | |
+ public override void Disconnect() | |
+ { | |
+ // Flag CancellationTokenSource before performing Disconnect. | |
+ // More of a precaution than anything | |
+ tokenSource?.Cancel(); | |
+ base.Disconnect(); | |
+ | |
+ // Call Interrupt on the thread although not going to check thread | |
+ // state | |
+ awaitOutBuffThread?.Interrupt(); | |
+ //awaitOutBuffThread?.Join(); | |
+ } | |
} | |
} | |
diff --git a/DS4Windows/DS4Control/DS4OutDevices/DS4OutDeviceFactory.cs b/DS4Windows/DS4Control/DS4OutDevices/DS4OutDeviceFactory.cs | |
index 036cde4c..8432e4ea 100644 | |
--- a/DS4Windows/DS4Control/DS4OutDevices/DS4OutDeviceFactory.cs | |
+++ b/DS4Windows/DS4Control/DS4OutDevices/DS4OutDeviceFactory.cs | |
@@ -10,14 +10,20 @@ namespace DS4Windows | |
static class DS4OutDeviceFactory | |
{ | |
private static Version extAPIMinVersion = new Version("1.17.333.0"); | |
+ private static Version outBuffAPIMinVersion = new Version("1.19.1738.0"); | |
public static DS4OutDevice CreateDS4Device(ViGEmClient client, | |
Version driverVersion) | |
{ | |
DS4OutDevice result = null; | |
+ //if (outBuffAPIMinVersion.CompareTo(driverVersion) <= 0) | |
+ //{ | |
+ // result = new DS4OutDeviceExt(client, useAwaitOutputBuffer: true); | |
+ //} | |
+ //else if (extAPIMinVersion.CompareTo(driverVersion) <= 0) | |
if (extAPIMinVersion.CompareTo(driverVersion) <= 0) | |
{ | |
- result = new DS4OutDeviceExt(client); | |
+ result = new DS4OutDeviceExt(client, useAwaitOutputBuffer: true); | |
} | |
else | |
{ | |
diff --git a/DS4Windows/DS4Control/OutputSlotManager.cs b/DS4Windows/DS4Control/OutputSlotManager.cs | |
index 3a3a1f8b..5c3209bb 100644 | |
--- a/DS4Windows/DS4Control/OutputSlotManager.cs | |
+++ b/DS4Windows/DS4Control/OutputSlotManager.cs | |
@@ -175,7 +175,10 @@ namespace DS4Windows | |
outputDevices[slot] = null; | |
deviceDict.Remove(slot); | |
revDeviceDict.Remove(outputDevice); | |
+ | |
+ outputDevice.RemoveFeedbacks(); | |
outputDevice.Disconnect(); | |
+ | |
if (inIdx != -1) | |
{ | |
outdevs[inIdx] = null; | |
diff --git a/DS4Windows/DS4Library/DS4Device.cs b/DS4Windows/DS4Library/DS4Device.cs | |
index ac7b0747..2e9aaa2c 100644 | |
--- a/DS4Windows/DS4Library/DS4Device.cs | |
+++ b/DS4Windows/DS4Library/DS4Device.cs | |
@@ -1898,6 +1898,11 @@ namespace DS4Windows | |
currentHap.lightbarState = lightState; | |
} | |
+ public ref DS4LightbarState GetLightbarStateRef() | |
+ { | |
+ return ref currentHap.lightbarState; | |
+ } | |
+ | |
public void SetRumbleState(ref DS4ForceFeedbackState rumbleState) | |
{ | |
currentHap.rumbleState = rumbleState; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment