Skip to content

Instantly share code, notes, and snippets.

@Ryochan7
Last active August 27, 2022 17:02
Show Gist options
  • Save Ryochan7/7329c4355e38ccebe391a844717998ba to your computer and use it in GitHub Desktop.
Save Ryochan7/7329c4355e38ccebe391a844717998ba to your computer and use it in GitHub Desktop.
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