Test DS4 emulation

This commit is contained in:
Travis Nickles 2019-04-17 21:29:16 -05:00
parent 372f9aa612
commit dc5e34f1be
8 changed files with 355 additions and 27 deletions

View File

@ -11,15 +11,13 @@ using System.Diagnostics;
using Nefarius.ViGEm.Client;
using Nefarius.ViGEm.Client.Targets;
using Nefarius.ViGEm.Client.Targets.Xbox360;
using Nefarius.ViGEm.Client.Targets.DualShock4;
namespace DS4Windows
{
public class ControlService
{
public ViGEmClient vigemTestClient = null;
private const int inputResolution = 127 - (-128);
private const float reciprocalInputResolution = 1 / (float)inputResolution;
private const int outputResolution = 32767 - (-32768);
public const int DS4_CONTROLLER_COUNT = 4;
public DS4Device[] DS4Controllers = new DS4Device[DS4_CONTROLLER_COUNT];
public Mouse[] touchPad = new Mouse[DS4_CONTROLLER_COUNT];
@ -34,10 +32,12 @@ namespace DS4Windows
bool[] buttonsdown = new bool[4] { false, false, false, false };
bool[] held = new bool[DS4_CONTROLLER_COUNT];
int[] oldmouse = new int[DS4_CONTROLLER_COUNT] { -1, -1, -1, -1 };
public Xbox360Controller[] x360controls = new Xbox360Controller[4] { null, null, null, null };
private Xbox360Report[] x360reports = new Xbox360Report[4] { new Xbox360Report(), new Xbox360Report(),
public OutputDevice[] outputDevices = new OutputDevice[4] { null, null, null, null };
//public Xbox360Controller[] x360controls = new Xbox360Controller[4] { null, null, null, null };
/*private Xbox360Report[] x360reports = new Xbox360Report[4] { new Xbox360Report(), new Xbox360Report(),
new Xbox360Report(), new Xbox360Report()
};
*/
Thread tempThread;
public List<string> affectedDevs = new List<string>()
{
@ -424,14 +424,28 @@ namespace DS4Windows
{
LogDebug("Plugging in X360 Controller #" + (i + 1));
useDInputOnly[i] = false;
x360controls[i] = new Xbox360Controller(vigemTestClient);
DS4OutDevice tempDS4 = new DS4OutDevice(vigemTestClient);
outputDevices[i] = tempDS4;
int devIndex = i;
x360controls[i].FeedbackReceived += (sender, args) =>
tempDS4.cont.FeedbackReceived += (sender, args) =>
{
SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex);
};
x360controls[i].Connect();
tempDS4.Connect();
//x360controls[i] = new Xbox360Controller(vigemTestClient);
/*Xbox360Controller bacon = new Xbox360Controller(vigemTestClient);
Xbox360OutDevice tempXbox = new Xbox360OutDevice(vigemTestClient);
outputDevices[i] = tempXbox;
int devIndex = i;
tempXbox.cont.FeedbackReceived += (sender, args) =>
{
SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex);
};
tempXbox.Connect();
*/
LogDebug("X360 Controller #" + (i + 1) + " connected");
}
else
@ -568,8 +582,10 @@ namespace DS4Windows
}
CurrentState[i].Battery = PreviousState[i].Battery = 0; // Reset for the next connection's initial status change.
x360controls[i]?.Disconnect();
x360controls[i] = null;
//x360controls[i]?.Disconnect();
outputDevices[i]?.Disconnect();
outputDevices[i] = null;
//x360controls[i] = null;
useDInputOnly[i] = true;
DS4Controllers[i] = null;
touchPad[i] = null;
@ -681,14 +697,18 @@ namespace DS4Windows
{
LogDebug("Plugging in X360 Controller #" + (Index + 1));
useDInputOnly[Index] = false;
x360controls[Index] = new Xbox360Controller(vigemTestClient);
//x360controls[Index] = new Xbox360Controller(vigemTestClient);
Xbox360OutDevice tempXbox = new Xbox360OutDevice(vigemTestClient);
outputDevices[Index] = tempXbox;
int devIndex = Index;
x360controls[Index].FeedbackReceived += (sender, args) =>
//x360controls[Index].FeedbackReceived += (sender, args) =>
tempXbox.cont.FeedbackReceived += (sender, args) =>
{
SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex);
};
x360controls[Index].Connect();
//x360controls[Index].Connect();
tempXbox.Connect();
LogDebug("X360 Controller #" + (Index + 1) + " connected");
}
else
@ -723,7 +743,7 @@ namespace DS4Windows
return true;
}
private void testNewReport(ref Xbox360Report xboxreport, DS4State state,
/*private void testNewReport(ref Xbox360Report xboxreport, DS4State state,
int device)
{
Xbox360Buttons tempButtons = 0;
@ -818,8 +838,9 @@ namespace DS4Windows
goto case SASteeringWheelEmulationAxisType.None;
}
}
*/
private short AxisScale(Int32 Value, Boolean Flip)
/*private short AxisScale(Int32 Value, Boolean Flip)
{
unchecked
{
@ -832,6 +853,7 @@ namespace DS4Windows
return (short)(temp * outputResolution + (-32768));
}
}
*/
private void CheckProfileOptions(int ind, DS4Device device, bool startUp=false)
{
@ -1048,8 +1070,8 @@ namespace DS4Windows
{
if (!useDInputOnly[ind])
{
x360controls[ind].Disconnect();
x360controls[ind] = null;
outputDevices[ind].Disconnect();
outputDevices[ind] = null;
useDInputOnly[ind] = true;
LogDebug("X360 Controller #" + (ind + 1) + " unplugged");
}
@ -1059,13 +1081,23 @@ namespace DS4Windows
if (!getDInputOnly(ind))
{
LogDebug("Plugging in X360 Controller #" + (ind + 1));
x360controls[ind] = new Xbox360Controller(vigemTestClient);
Xbox360OutDevice tempXbox = new Xbox360OutDevice(vigemTestClient);
outputDevices[ind] = tempXbox;
tempXbox.cont.FeedbackReceived += (eventsender, args) =>
{
SetDevRumble(device, args.LargeMotor, args.SmallMotor, ind);
};
tempXbox.Connect();
/*x360controls[ind] = new Xbox360Controller(vigemTestClient);
x360controls[ind].FeedbackReceived += (eventsender, args) =>
{
SetDevRumble(device, args.LargeMotor, args.SmallMotor, ind);
};
x360controls[ind].Connect();
*/
useDInputOnly[ind] = false;
LogDebug("X360 Controller #" + (ind + 1) + " connected");
}
@ -1101,8 +1133,10 @@ namespace DS4Windows
CurrentState[ind].Battery = PreviousState[ind].Battery = 0; // Reset for the next connection's initial status change.
if (!useDInputOnly[ind])
{
x360controls[ind].Disconnect();
x360controls[ind] = null;
outputDevices[ind].Disconnect();
outputDevices[ind] = null;
//x360controls[ind].Disconnect();
//x360controls[ind] = null;
LogDebug("X360 Controller # " + (ind + 1) + " unplugged");
}
@ -1241,8 +1275,10 @@ namespace DS4Windows
if (!useDInputOnly[ind])
{
testNewReport(ref x360reports[ind], cState, ind);
x360controls[ind]?.SendReport(x360reports[ind]);
outputDevices[ind]?.ConvertandSendReport(cState, ind);
//testNewReport(ref x360reports[ind], cState, ind);
//x360controls[ind]?.SendReport(x360reports[ind]);
//x360Bus.Parse(cState, processingData[ind].Report, ind);
// We push the translated Xinput state, and simultaneously we
// pull back any possible rumble data coming from Xinput consumers.

View File

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nefarius.ViGEm.Client;
using Nefarius.ViGEm.Client.Targets;
using Nefarius.ViGEm.Client.Targets.DualShock4;
namespace DS4Windows
{
class DS4OutDevice : OutputDevice
{
public DualShock4Controller cont;
private DualShock4Report report;
public DS4OutDevice(ViGEmClient client)
{
cont = new DualShock4Controller(client);
report = new DualShock4Report();
}
public override void ConvertandSendReport(DS4State state, int device)
{
DualShock4Buttons tempButtons = 0;
DualShock4DPadValues tempDPad = DualShock4DPadValues.None;
DualShock4SpecialButtons tempSpecial = 0;
unchecked
{
if (state.Share) tempButtons |= DualShock4Buttons.Share;
if (state.L3) tempButtons |= DualShock4Buttons.ThumbLeft;
if (state.R3) tempButtons |= DualShock4Buttons.ThumbRight;
if (state.Options) tempButtons |= DualShock4Buttons.Options;
if (state.DpadUp && state.DpadRight) tempDPad = DualShock4DPadValues.Northeast;
else if (state.DpadUp) tempDPad = DualShock4DPadValues.North;
else if (state.DpadRight && state.DpadDown) tempDPad = DualShock4DPadValues.Southeast;
else if (state.DpadRight) tempDPad = DualShock4DPadValues.East;
else if (state.DpadDown && state.DpadLeft) tempDPad = DualShock4DPadValues.Southwest;
else if (state.DpadDown) tempDPad = DualShock4DPadValues.South;
else if (state.DpadLeft && state.DpadUp) tempDPad = DualShock4DPadValues.Northwest;
else if (state.DpadLeft) tempDPad = DualShock4DPadValues.West;
/*if (state.DpadUp) tempDPad = (state.DpadRight) ? DualShock4DPadValues.Northeast : DualShock4DPadValues.North;
if (state.DpadRight) tempDPad = (state.DpadDown) ? DualShock4DPadValues.Southeast : DualShock4DPadValues.East;
if (state.DpadDown) tempDPad = (state.DpadLeft) ? DualShock4DPadValues.Southwest : DualShock4DPadValues.South;
if (state.DpadLeft) tempDPad = (state.DpadUp) ? DualShock4DPadValues.Northwest : DualShock4DPadValues.West;
*/
if (state.L1) tempButtons |= DualShock4Buttons.ShoulderLeft;
if (state.R1) tempButtons |= DualShock4Buttons.ShoulderRight;
//if (state.L2Btn) tempButtons |= DualShock4Buttons.TriggerLeft;
//if (state.R2Btn) tempButtons |= DualShock4Buttons.TriggerRight;
if (state.L2 > 0) tempButtons |= DualShock4Buttons.TriggerLeft;
if (state.R2 > 0) tempButtons |= DualShock4Buttons.TriggerRight;
if (state.Triangle) tempButtons |= DualShock4Buttons.Triangle;
if (state.Circle) tempButtons |= DualShock4Buttons.Circle;
if (state.Cross) tempButtons |= DualShock4Buttons.Cross;
if (state.Square) tempButtons |= DualShock4Buttons.Square;
if (state.PS) tempSpecial |= DualShock4SpecialButtons.Ps;
if (state.TouchButton) tempSpecial |= DualShock4SpecialButtons.Touchpad;
//report.SetButtonsFull(tempButtons);
report.Buttons = (ushort)tempButtons;
report.SetDPad(tempDPad);
report.SpecialButtons = (byte)tempSpecial;
}
report.LeftTrigger = state.L2;
report.RightTrigger = state.R2;
report.LeftThumbX = state.LX;
report.LeftThumbY = state.LY;
report.RightThumbX = state.RX;
report.RightThumbY = state.RY;
cont.SendReport(report);
}
public override void Connect() => cont.Connect();
public override void Disconnect() => cont.Disconnect();
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DS4Windows
{
public abstract class OutputDevice
{
public abstract void ConvertandSendReport(DS4State state, int device);
public abstract void Connect();
public abstract void Disconnect();
}
}

View File

@ -335,6 +335,34 @@ namespace DS4Windows
return result;
}
internal static string GetDeviceProperty(string deviceInstanceId,
NativeMethods.DEVPROPKEY prop)
{
string result = string.Empty;
NativeMethods.SP_DEVINFO_DATA deviceInfoData = new NativeMethods.SP_DEVINFO_DATA();
deviceInfoData.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(deviceInfoData);
var dataBuffer = new byte[4096];
ulong propertyType = 0;
var requiredSize = 0;
Guid hidGuid = new Guid();
NativeMethods.HidD_GetHidGuid(ref hidGuid);
IntPtr deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref hidGuid, deviceInstanceId, 0, NativeMethods.DIGCF_PRESENT | NativeMethods.DIGCF_DEVICEINTERFACE);
NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData);
if (NativeMethods.SetupDiGetDeviceProperty(deviceInfoSet, ref deviceInfoData, ref prop, ref propertyType,
dataBuffer, dataBuffer.Length, ref requiredSize, 0))
{
result = dataBuffer.ToUTF16String();
}
if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE)
{
NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet);
}
return result;
}
public static bool IsHidGuardianInstalled()
{
return CheckForSysDevice(@"Root\HidGuardian");
@ -3063,20 +3091,29 @@ namespace DS4Windows
tempDev.setBTPollRate(btPollRate[device]);
if (xinputStatus && xinputPlug)
{
control.x360controls[device] = new Nefarius.ViGEm.Client.Targets.Xbox360Controller(control.vigemTestClient);
Xbox360OutDevice tempXbox = new Xbox360OutDevice(control.vigemTestClient);
control.outputDevices[device] = tempXbox;
tempXbox.cont.FeedbackReceived += (eventsender, args) =>
{
control.SetDevRumble(tempDev, args.LargeMotor, args.SmallMotor, device);
};
tempXbox.Connect();
/*control.x360controls[device] = new Nefarius.ViGEm.Client.Targets.Xbox360Controller(control.vigemTestClient);
control.x360controls[device].FeedbackReceived += (eventsender, args) =>
{
control.SetDevRumble(tempDev, args.LargeMotor, args.SmallMotor, device);
};
control.x360controls[device].Connect();
*/
Global.useDInputOnly[device] = false;
AppLogger.LogToGui("X360 Controller #" + (device + 1) + " connected", false);
}
else if (xinputStatus && !xinputPlug)
{
control.x360controls[device].Disconnect();
control.x360controls[device] = null;
control.outputDevices[device].Disconnect();
control.outputDevices[device] = null;
Global.useDInputOnly[device] = true;
AppLogger.LogToGui("X360 Controller #" + (device + 1) + " unplugged", false);
}

View File

@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nefarius.ViGEm.Client;
using Nefarius.ViGEm.Client.Targets;
using Nefarius.ViGEm.Client.Targets.Xbox360;
namespace DS4Windows
{
public class Xbox360OutDevice : OutputDevice
{
private const int inputResolution = 127 - (-128);
private const float reciprocalInputResolution = 1 / (float)inputResolution;
private const int outputResolution = 32767 - (-32768);
public Xbox360Controller cont;
private Xbox360Report report;
public Xbox360OutDevice(ViGEmClient client)
{
cont = new Xbox360Controller(client);
report = new Xbox360Report();
}
public override void ConvertandSendReport(DS4State state, int device)
{
Xbox360Buttons tempButtons = 0;
unchecked
{
if (state.Share) tempButtons |= Xbox360Buttons.Back;
if (state.L3) tempButtons |= Xbox360Buttons.LeftThumb;
if (state.R3) tempButtons |= Xbox360Buttons.RightThumb;
if (state.Options) tempButtons |= Xbox360Buttons.Start;
if (state.DpadUp) tempButtons |= Xbox360Buttons.Up;
if (state.DpadRight) tempButtons |= Xbox360Buttons.Right;
if (state.DpadDown) tempButtons |= Xbox360Buttons.Down;
if (state.DpadLeft) tempButtons |= Xbox360Buttons.Left;
if (state.L1) tempButtons |= Xbox360Buttons.LeftShoulder;
if (state.R1) tempButtons |= Xbox360Buttons.RightShoulder;
if (state.Triangle) tempButtons |= Xbox360Buttons.Y;
if (state.Circle) tempButtons |= Xbox360Buttons.B;
if (state.Cross) tempButtons |= Xbox360Buttons.A;
if (state.Square) tempButtons |= Xbox360Buttons.X;
if (state.PS) tempButtons |= Xbox360Buttons.Guide;
report.SetButtonsFull(tempButtons);
}
report.LeftTrigger = state.L2;
report.RightTrigger = state.R2;
SASteeringWheelEmulationAxisType steeringWheelMappedAxis = Global.GetSASteeringWheelEmulationAxis(device);
switch (steeringWheelMappedAxis)
{
case SASteeringWheelEmulationAxisType.None:
report.LeftThumbX = AxisScale(state.LX, false);
report.LeftThumbY = AxisScale(state.LY, true);
report.RightThumbX = AxisScale(state.RX, false);
report.RightThumbY = AxisScale(state.RY, true);
break;
case SASteeringWheelEmulationAxisType.LX:
report.LeftThumbX = (short)state.SASteeringWheelEmulationUnit;
report.LeftThumbY = AxisScale(state.LY, true);
report.RightThumbX = AxisScale(state.RX, false);
report.RightThumbY = AxisScale(state.RY, true);
break;
case SASteeringWheelEmulationAxisType.LY:
report.LeftThumbX = AxisScale(state.LX, false);
report.LeftThumbY = (short)state.SASteeringWheelEmulationUnit;
report.RightThumbX = AxisScale(state.RX, false);
report.RightThumbY = AxisScale(state.RY, true);
break;
case SASteeringWheelEmulationAxisType.RX:
report.LeftThumbX = AxisScale(state.LX, false);
report.LeftThumbY = AxisScale(state.LY, true);
report.RightThumbX = (short)state.SASteeringWheelEmulationUnit;
report.RightThumbY = AxisScale(state.RY, true);
break;
case SASteeringWheelEmulationAxisType.RY:
report.LeftThumbX = AxisScale(state.LX, false);
report.LeftThumbY = AxisScale(state.LY, true);
report.RightThumbX = AxisScale(state.RX, false);
report.RightThumbY = (short)state.SASteeringWheelEmulationUnit;
break;
case SASteeringWheelEmulationAxisType.L2R2:
report.LeftTrigger = report.RightTrigger = 0;
if (state.SASteeringWheelEmulationUnit >= 0) report.LeftTrigger = (Byte)state.SASteeringWheelEmulationUnit;
else report.RightTrigger = (Byte)state.SASteeringWheelEmulationUnit;
goto case SASteeringWheelEmulationAxisType.None;
case SASteeringWheelEmulationAxisType.VJoy1X:
case SASteeringWheelEmulationAxisType.VJoy2X:
DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_X);
goto case SASteeringWheelEmulationAxisType.None;
case SASteeringWheelEmulationAxisType.VJoy1Y:
case SASteeringWheelEmulationAxisType.VJoy2Y:
DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Y);
goto case SASteeringWheelEmulationAxisType.None;
case SASteeringWheelEmulationAxisType.VJoy1Z:
case SASteeringWheelEmulationAxisType.VJoy2Z:
DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Z);
goto case SASteeringWheelEmulationAxisType.None;
default:
// Should never come here but just in case use the NONE case as default handler....
goto case SASteeringWheelEmulationAxisType.None;
}
cont.SendReport(report);
}
private short AxisScale(Int32 Value, Boolean Flip)
{
unchecked
{
Value -= 0x80;
//float temp = (Value - (-128)) / (float)inputResolution;
float temp = (Value - (-128)) * reciprocalInputResolution;
if (Flip) temp = (temp - 0.5f) * -1.0f + 0.5f;
return (short)(temp * outputResolution + (-32768));
}
}
public override void Connect() => cont.Connect();
public override void Disconnect() => cont.Disconnect();
}
}

View File

@ -61,12 +61,22 @@ namespace DS4Windows
return deviceInstanceId;
}
private static bool IsRealDS4(HidDevice hDevice)
{
string deviceInstanceId = devicePathToInstanceId(hDevice.DevicePath);
string temp = Global.GetDeviceProperty(deviceInstanceId,
NativeMethods.DEVPKEY_Device_UINumber);
return string.IsNullOrEmpty(temp);
}
// Enumerates ds4 controllers in the system
public static void findControllers()
{
lock (Devices)
{
IEnumerable<HidDevice> hDevices = HidDevices.EnumerateDS4(knownDevices);
hDevices = hDevices.Where(dev => IsRealDS4(dev)).Select(dev => dev);
//hDevices = from dev in hDevices where IsRealDS4(dev) select dev;
// Sort Bluetooth first in case USB is also connected on the same controller.
hDevices = hDevices.OrderBy<HidDevice, ConnectionType>((HidDevice d) => { return DS4Device.HidConnectionType(d); });

View File

@ -138,6 +138,7 @@
<ItemGroup>
<Compile Include="DS4Control\ControlService.cs" />
<Compile Include="DS4Control\DS4LightBar.cs" />
<Compile Include="DS4Control\DS4OutDevice.cs" />
<Compile Include="DS4Control\DS4StateFieldMapping.cs" />
<Compile Include="DS4Control\InputMethods.cs" />
<Compile Include="DS4Control\ITouchpadBehaviour.cs" />
@ -146,9 +147,11 @@
<Compile Include="DS4Control\Mouse.cs" />
<Compile Include="DS4Control\MouseCursor.cs" />
<Compile Include="DS4Control\MouseWheel.cs" />
<Compile Include="DS4Control\OutputDevice.cs" />
<Compile Include="DS4Control\ScpUtil.cs" />
<Compile Include="DS4Control\UdpServer.cs" />
<Compile Include="DS4Control\Util.cs" />
<Compile Include="DS4Control\Xbox360OutDevice.cs" />
<Compile Include="DS4Forms\LanguagePackComboBox.cs">
<SubType>UserControl</SubType>
</Compile>

View File

@ -234,6 +234,9 @@ namespace DS4Windows
internal static DEVPROPKEY DEVPKEY_Device_HardwareIds =
new DEVPROPKEY { fmtid = new Guid(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0), pid = 3 };
internal static DEVPROPKEY DEVPKEY_Device_UINumber =
new DEVPROPKEY { fmtid = new Guid(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0), pid = 18 };
[DllImport("setupapi.dll", EntryPoint = "SetupDiGetDeviceRegistryProperty")]
public static extern bool SetupDiGetDeviceRegistryProperty(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, int propertyVal, ref int propertyRegDataType, byte[] propertyBuffer, int propertyBufferSize, ref int requiredSize);