mirror of
https://github.com/cemu-project/DS4Windows.git
synced 2025-01-23 05:31:12 +01:00
SA steering wheel emulation can now feed VJoy virtual joystick device (analog axies there). This leaves all original X360/DS4 analog axies for other purposes. Useful in games which support mapping controls to more than one controller (for example EurotruckSimulator/AmericalTruckSimulator).
This commit is contained in:
parent
4678e14408
commit
5b49a8ed47
@ -1650,7 +1650,7 @@ namespace DS4Windows
|
||||
}
|
||||
}
|
||||
|
||||
if (getSASteeringWheelEmulationAxis(device) != DS4Controls.None)
|
||||
if (getSASteeringWheelEmulationAxis(device) != SASteeringWheelEmulationAxisType.None)
|
||||
MappedState.SASteeringWheelEmulationUnit = Mapping.Scale360degreeGyroAxis(device, eState, ctrl);
|
||||
|
||||
calculateFinalMouseMovement(ref tempMouseDeltaX, ref tempMouseDeltaY,
|
||||
@ -4132,15 +4132,24 @@ namespace DS4Windows
|
||||
|
||||
// Scale input to a raw x360 16bit output scale, except if output axis of steering wheel emulation is L2+R2 trigger axis.
|
||||
// L2+R2 triggers use independent 8bit values, so use -255..0..+255 scaled values (therefore L2+R2 Trigger axis supports only 360 turn range)
|
||||
if (Global.getSASteeringWheelEmulationAxis(device) != DS4Controls.L2)
|
||||
switch(Global.getSASteeringWheelEmulationAxis(device))
|
||||
{
|
||||
return (((result - maxRangeLeft) * (32767 - (-32768))) / (maxRangeRight - maxRangeLeft)) + (-32768); // Stick axis with configurable range
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Convert.ToInt32(Math.Round(result / (1.0 * C_WHEEL_ANGLE_PRECISION))); // Trigger axis with fixed 360 range
|
||||
if (result < 0) result = -181 - result;
|
||||
return (((result - (-180)) * (255 - (-255))) / (180 - (-180))) + (-255);
|
||||
case SASteeringWheelEmulationAxisType.LX:
|
||||
case SASteeringWheelEmulationAxisType.LY:
|
||||
case SASteeringWheelEmulationAxisType.RX:
|
||||
case SASteeringWheelEmulationAxisType.RY:
|
||||
// DS4 Stick axis values with configurable range
|
||||
return (((result - maxRangeLeft) * (32767 - (-32768))) / (maxRangeRight - maxRangeLeft)) + (-32768);
|
||||
|
||||
case SASteeringWheelEmulationAxisType.L2R2:
|
||||
// DS4 Trigger axis values with fixed 360 range
|
||||
result = Convert.ToInt32(Math.Round(result / (1.0 * C_WHEEL_ANGLE_PRECISION)));
|
||||
if (result < 0) result = -181 - result;
|
||||
return (((result - (-180)) * (255 - (-255))) / (180 - (-180))) + (-255);
|
||||
|
||||
default:
|
||||
// VJoy axis values with configurable range
|
||||
return (((result - maxRangeLeft) * (32767 - (-0))) / (maxRangeRight - maxRangeLeft)) + (-0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ namespace DS4Windows
|
||||
public enum DS4Controls : byte { None, LXNeg, LXPos, LYNeg, LYPos, RXNeg, RXPos, RYNeg, RYPos, L1, L2, L3, R1, R2, R3, Square, Triangle, Circle, Cross, DpadUp, DpadRight, DpadDown, DpadLeft, PS, TouchLeft, TouchUpper, TouchMulti, TouchRight, Share, Options, GyroXPos, GyroXNeg, GyroZPos, GyroZNeg, SwipeLeft, SwipeRight, SwipeUp, SwipeDown };
|
||||
public enum X360Controls : byte { None, LXNeg, LXPos, LYNeg, LYPos, RXNeg, RXPos, RYNeg, RYPos, LB, LT, LS, RB, RT, RS, X, Y, B, A, DpadUp, DpadRight, DpadDown, DpadLeft, Guide, Back, Start, LeftMouse, RightMouse, MiddleMouse, FourthMouse, FifthMouse, WUP, WDOWN, MouseUp, MouseDown, MouseLeft, MouseRight, Unbound };
|
||||
|
||||
public enum SASteeringWheelEmulationAxisType: byte { None = 0, LX, LY, RX, RY, L2R2, VJoy1X, VJoy1Y, VJoy1Z, VJoy2X, VJoy2Y, VJoy2Z };
|
||||
|
||||
public class DS4ControlSettings
|
||||
{
|
||||
public DS4Controls control;
|
||||
@ -718,8 +720,8 @@ namespace DS4Windows
|
||||
m_Config.SetSaTriggerCond(index, text);
|
||||
}
|
||||
|
||||
public static DS4Controls[] SASteeringWheelEmulationAxis => m_Config.sASteeringWheelEmulationAxis;
|
||||
public static DS4Controls getSASteeringWheelEmulationAxis(int index)
|
||||
public static SASteeringWheelEmulationAxisType[] SASteeringWheelEmulationAxis => m_Config.sASteeringWheelEmulationAxis;
|
||||
public static SASteeringWheelEmulationAxisType getSASteeringWheelEmulationAxis(int index)
|
||||
{
|
||||
return m_Config.sASteeringWheelEmulationAxis[index];
|
||||
}
|
||||
@ -1519,7 +1521,7 @@ namespace DS4Windows
|
||||
public bool[] useSAforMouse = new bool[5] { false, false, false, false, false };
|
||||
public string[] sATriggers = new string[5] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty };
|
||||
public bool[] sATriggerCond = new bool[5] { true, true, true, true, true };
|
||||
public DS4Controls[] sASteeringWheelEmulationAxis = new DS4Controls[5] { DS4Controls.None, DS4Controls.None, DS4Controls.None, DS4Controls.None, DS4Controls.None };
|
||||
public SASteeringWheelEmulationAxisType[] sASteeringWheelEmulationAxis = new SASteeringWheelEmulationAxisType[5] { SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None };
|
||||
public int[] sASteeringWheelEmulationRange = new int[5] { 360, 360, 360, 360, 360 };
|
||||
public int[][] touchDisInvertTriggers = new int[5][] { new int[1] { -1 }, new int[1] { -1 }, new int[1] { -1 },
|
||||
new int[1] { -1 }, new int[1] { -1 } };
|
||||
@ -2645,8 +2647,8 @@ namespace DS4Windows
|
||||
try { Item = m_Xdoc.SelectSingleNode("/" + rootname + "/SATriggerCond"); sATriggerCond[device] = SaTriggerCondValue(Item.InnerText); }
|
||||
catch { sATriggerCond[device] = true; missingSetting = true; }
|
||||
|
||||
try { Item = m_Xdoc.SelectSingleNode("/" + rootname + "/SASteeringWheelEmulationAxis"); DS4Controls.TryParse(Item.InnerText, out sASteeringWheelEmulationAxis[device]); }
|
||||
catch { sASteeringWheelEmulationAxis[device] = DS4Controls.None; missingSetting = true; }
|
||||
try { Item = m_Xdoc.SelectSingleNode("/" + rootname + "/SASteeringWheelEmulationAxis"); SASteeringWheelEmulationAxisType.TryParse(Item.InnerText, out sASteeringWheelEmulationAxis[device]); }
|
||||
catch { sASteeringWheelEmulationAxis[device] = SASteeringWheelEmulationAxisType.None; missingSetting = true; }
|
||||
|
||||
try { Item = m_Xdoc.SelectSingleNode("/" + rootname + "/SASteeringWheelEmulationRange"); int.TryParse(Item.InnerText, out sASteeringWheelEmulationRange[device]); }
|
||||
catch { sASteeringWheelEmulationRange[device] = 360; missingSetting = true; }
|
||||
@ -3942,7 +3944,7 @@ namespace DS4Windows
|
||||
useSAforMouse[device] = false;
|
||||
sATriggers[device] = string.Empty;
|
||||
sATriggerCond[device] = true;
|
||||
sASteeringWheelEmulationAxis[device] = DS4Controls.None;
|
||||
sASteeringWheelEmulationAxis[device] = SASteeringWheelEmulationAxisType.None;
|
||||
sASteeringWheelEmulationRange[device] = 360;
|
||||
touchDisInvertTriggers[device] = new int[1] { -1 };
|
||||
lsCurve[device] = rsCurve[device] = 0;
|
||||
|
@ -129,38 +129,73 @@ namespace DS4Windows
|
||||
|
||||
if (state.PS) Output[11] |= (Byte)(1 << 2); // Guide
|
||||
|
||||
DS4Controls steeringWheelMappedAxis = Global.getSASteeringWheelEmulationAxis(device);
|
||||
|
||||
if (steeringWheelMappedAxis == DS4Controls.L2)
|
||||
{
|
||||
Output[12] = Output[13] = 0;
|
||||
if (state.SASteeringWheelEmulationUnit >= 0)
|
||||
Output[12] = (Byte)state.SASteeringWheelEmulationUnit;
|
||||
else
|
||||
Output[13] = (Byte)state.SASteeringWheelEmulationUnit;
|
||||
}
|
||||
else
|
||||
{
|
||||
Output[12] = state.L2; // Left Trigger
|
||||
Output[13] = state.R2; // Right Trigger
|
||||
}
|
||||
|
||||
SASteeringWheelEmulationAxisType steeringWheelMappedAxis = Global.getSASteeringWheelEmulationAxis(device);
|
||||
Int32 ThumbLX;
|
||||
Int32 ThumbLY;
|
||||
Int32 ThumbRX;
|
||||
Int32 ThumbRY;
|
||||
|
||||
if (steeringWheelMappedAxis == DS4Controls.LXPos) ThumbLX = state.SASteeringWheelEmulationUnit;
|
||||
else ThumbLX = Scale(state.LX, false);
|
||||
Output[12] = state.L2; // Left Trigger
|
||||
Output[13] = state.R2; // Right Trigger
|
||||
|
||||
if (steeringWheelMappedAxis == DS4Controls.LYPos) ThumbLY = state.SASteeringWheelEmulationUnit;
|
||||
else ThumbLY = Scale(state.LY, true);
|
||||
switch(steeringWheelMappedAxis)
|
||||
{
|
||||
case SASteeringWheelEmulationAxisType.LX:
|
||||
ThumbLX = state.SASteeringWheelEmulationUnit;
|
||||
ThumbLY = Scale(state.LY, true);
|
||||
ThumbRX = Scale(state.RX, false);
|
||||
ThumbRY = Scale(state.RY, true);
|
||||
break;
|
||||
|
||||
if (steeringWheelMappedAxis == DS4Controls.RXPos) ThumbRX = state.SASteeringWheelEmulationUnit;
|
||||
else ThumbRX = Scale(state.RX, false);
|
||||
case SASteeringWheelEmulationAxisType.LY:
|
||||
ThumbLX = Scale(state.LX, false);
|
||||
ThumbLY = state.SASteeringWheelEmulationUnit;
|
||||
ThumbRX = Scale(state.RX, false);
|
||||
ThumbRY = Scale(state.RY, true);
|
||||
break;
|
||||
|
||||
if (steeringWheelMappedAxis == DS4Controls.RYPos) ThumbRY = state.SASteeringWheelEmulationUnit;
|
||||
else ThumbRY = Scale(state.RY, true);
|
||||
case SASteeringWheelEmulationAxisType.RX:
|
||||
ThumbLX = Scale(state.LX, false);
|
||||
ThumbLY = Scale(state.LY, true);
|
||||
ThumbRX = state.SASteeringWheelEmulationUnit;
|
||||
ThumbRY = Scale(state.RY, true);
|
||||
break;
|
||||
|
||||
case SASteeringWheelEmulationAxisType.RY:
|
||||
ThumbLX = Scale(state.LX, false);
|
||||
ThumbLY = Scale(state.LY, true);
|
||||
ThumbRX = Scale(state.RX, false);
|
||||
ThumbRY = state.SASteeringWheelEmulationUnit;
|
||||
break;
|
||||
|
||||
case SASteeringWheelEmulationAxisType.L2R2:
|
||||
Output[12] = Output[13] = 0;
|
||||
if (state.SASteeringWheelEmulationUnit >= 0) Output[12] = (Byte)state.SASteeringWheelEmulationUnit;
|
||||
else Output[13] = (Byte)state.SASteeringWheelEmulationUnit;
|
||||
goto default; // Usually GOTO is not a good idea but in switch-case statements it is sometimes pretty handy and acceptable way to fall through case options
|
||||
|
||||
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 default;
|
||||
|
||||
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 default;
|
||||
|
||||
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 default;
|
||||
|
||||
default:
|
||||
ThumbLX = Scale(state.LX, false);
|
||||
ThumbLY = Scale(state.LY, true);
|
||||
ThumbRX = Scale(state.RX, false);
|
||||
ThumbRY = Scale(state.RY, true);
|
||||
break;
|
||||
}
|
||||
|
||||
Output[14] = (Byte)((ThumbLX >> 0) & 0xFF); // LX
|
||||
Output[15] = (Byte)((ThumbLX >> 8) & 0xFF);
|
||||
|
8
DS4Windows/DS4Forms/Options.Designer.cs
generated
8
DS4Windows/DS4Forms/Options.Designer.cs
generated
@ -1936,7 +1936,13 @@
|
||||
resources.GetString("cBSteeringWheelEmulationAxis.Items2"),
|
||||
resources.GetString("cBSteeringWheelEmulationAxis.Items3"),
|
||||
resources.GetString("cBSteeringWheelEmulationAxis.Items4"),
|
||||
resources.GetString("cBSteeringWheelEmulationAxis.Items5")});
|
||||
resources.GetString("cBSteeringWheelEmulationAxis.Items5"),
|
||||
resources.GetString("cBSteeringWheelEmulationAxis.Items6"),
|
||||
resources.GetString("cBSteeringWheelEmulationAxis.Items7"),
|
||||
resources.GetString("cBSteeringWheelEmulationAxis.Items8"),
|
||||
resources.GetString("cBSteeringWheelEmulationAxis.Items9"),
|
||||
resources.GetString("cBSteeringWheelEmulationAxis.Items10"),
|
||||
resources.GetString("cBSteeringWheelEmulationAxis.Items11")});
|
||||
resources.ApplyResources(this.cBSteeringWheelEmulationAxis, "cBSteeringWheelEmulationAxis");
|
||||
this.cBSteeringWheelEmulationAxis.Name = "cBSteeringWheelEmulationAxis";
|
||||
this.cBSteeringWheelEmulationAxis.SelectedIndexChanged += new System.EventHandler(this.cBSteeringWheelEmulationAxis_SelectedIndexChanged);
|
||||
|
@ -719,16 +719,7 @@ namespace DS4Windows
|
||||
cBGyroMouseXAxis.SelectedIndex = GyroMouseHorizontalAxis[device];
|
||||
triggerCondAndCombo.SelectedIndex = SATriggerCond[device] ? 0 : 1;
|
||||
|
||||
switch (getSASteeringWheelEmulationAxis(device))
|
||||
{
|
||||
case DS4Controls.None: cBSteeringWheelEmulationAxis.SelectedIndex = 0; break;
|
||||
case DS4Controls.LXPos: cBSteeringWheelEmulationAxis.SelectedIndex = 1; break;
|
||||
case DS4Controls.LYPos: cBSteeringWheelEmulationAxis.SelectedIndex = 2; break;
|
||||
case DS4Controls.RXPos: cBSteeringWheelEmulationAxis.SelectedIndex = 3; break;
|
||||
case DS4Controls.RYPos: cBSteeringWheelEmulationAxis.SelectedIndex = 4; break;
|
||||
case DS4Controls.L2:
|
||||
case DS4Controls.R2: cBSteeringWheelEmulationAxis.SelectedIndex = 5; break;
|
||||
}
|
||||
cBSteeringWheelEmulationAxis.SelectedIndex = (int) getSASteeringWheelEmulationAxis(device);
|
||||
|
||||
int idxSASteeringWheelEmulationRange = cBSteeringWheelEmulationRange.Items.IndexOf(getSASteeringWheelEmulationRange(device).ToString());
|
||||
if (idxSASteeringWheelEmulationRange >= 0) cBSteeringWheelEmulationRange.SelectedIndex = idxSASteeringWheelEmulationRange;
|
||||
@ -3020,15 +3011,8 @@ namespace DS4Windows
|
||||
{
|
||||
if (loading == false)
|
||||
{
|
||||
switch (cBSteeringWheelEmulationAxis.SelectedIndex)
|
||||
{
|
||||
case 0: SASteeringWheelEmulationAxis[device] = DS4Controls.None; break; // Gyro SA steering wheel emulation disabled
|
||||
case 1: SASteeringWheelEmulationAxis[device] = DS4Controls.LXPos; break; // Left stick X axis
|
||||
case 2: SASteeringWheelEmulationAxis[device] = DS4Controls.LYPos; break; // Left stick Y axis
|
||||
case 3: SASteeringWheelEmulationAxis[device] = DS4Controls.RXPos; break; // Right stick X axis
|
||||
case 4: SASteeringWheelEmulationAxis[device] = DS4Controls.RYPos; break; // Right stick Y axis
|
||||
case 5: SASteeringWheelEmulationAxis[device] = DS4Controls.L2; break; // Left+Right trigger axis (max range -255..0 as left trigger and 0..+255 as right trigger)
|
||||
}
|
||||
if (cBSteeringWheelEmulationAxis.SelectedIndex >= 0) SASteeringWheelEmulationAxis[device] = (SASteeringWheelEmulationAxisType) ((byte) cBSteeringWheelEmulationAxis.SelectedIndex);
|
||||
else SASteeringWheelEmulationAxis[device] = SASteeringWheelEmulationAxisType.None;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4108,6 +4108,24 @@ with profile</value>
|
||||
<data name="cBSteeringWheelEmulationAxis.Items5" xml:space="preserve">
|
||||
<value>Trigger L+R Axis</value>
|
||||
</data>
|
||||
<data name="cBSteeringWheelEmulationAxis.Items6" xml:space="preserve">
|
||||
<value>VJoy1 X-Axis</value>
|
||||
</data>
|
||||
<data name="cBSteeringWheelEmulationAxis.Items7" xml:space="preserve">
|
||||
<value>VJoy1 Y-Axis</value>
|
||||
</data>
|
||||
<data name="cBSteeringWheelEmulationAxis.Items8" xml:space="preserve">
|
||||
<value>VJoy1 Z-Axis</value>
|
||||
</data>
|
||||
<data name="cBSteeringWheelEmulationAxis.Items9" xml:space="preserve">
|
||||
<value>VJoy2 X-Axis</value>
|
||||
</data>
|
||||
<data name="cBSteeringWheelEmulationAxis.Items10" xml:space="preserve">
|
||||
<value>VJoy2 Y-Axis</value>
|
||||
</data>
|
||||
<data name="cBSteeringWheelEmulationAxis.Items11" xml:space="preserve">
|
||||
<value>VJoy2 Z-Axis</value>
|
||||
</data>
|
||||
<data name="cBSteeringWheelEmulationAxis.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>107, 119</value>
|
||||
</data>
|
||||
@ -9140,7 +9158,7 @@ with profile</value>
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>102</value>
|
||||
<value>25</value>
|
||||
</metadata>
|
||||
<data name="$this.AutoScaleDimensions" type="System.Drawing.SizeF, System.Drawing">
|
||||
<value>96, 96</value>
|
||||
@ -9569,7 +9587,7 @@ with profile</value>
|
||||
<value>advColorDialog</value>
|
||||
</data>
|
||||
<data name=">>advColorDialog.Type" xml:space="preserve">
|
||||
<value>DS4Windows.AdvancedColorDialog, DS4Windows, Version=1.5.17.0, Culture=neutral, PublicKeyToken=null</value>
|
||||
<value>DS4Windows.AdvancedColorDialog, DS4Windows, Version=1.6.0.0, Culture=neutral, PublicKeyToken=null</value>
|
||||
</data>
|
||||
<data name=">>$this.Name" xml:space="preserve">
|
||||
<value>Options</value>
|
||||
|
@ -231,6 +231,7 @@
|
||||
<Compile Include="HidLibrary\NativeMethods.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Settings.cs" />
|
||||
<Compile Include="VJoyFeeder\vJoyFeeder.cs" />
|
||||
<EmbeddedResource Include="DS4Forms\DS4Form.ar.resx">
|
||||
<DependentUpon>DS4Form.cs</DependentUpon>
|
||||
<SubType>Designer</SubType>
|
||||
@ -621,6 +622,7 @@
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="DS4Forms\Options.resx">
|
||||
<DependentUpon>Options.cs</DependentUpon>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="DS4Forms\Options.ro-RO.resx">
|
||||
<DependentUpon>Options.cs</DependentUpon>
|
||||
@ -1044,7 +1046,9 @@
|
||||
<EmbeddedResource Include="Properties\Resources.cs.resx" />
|
||||
<EmbeddedResource Include="Properties\Resources.de-DE.resx" />
|
||||
<EmbeddedResource Include="Properties\Resources.el.resx" />
|
||||
<EmbeddedResource Include="Properties\Resources.es.resx" />
|
||||
<EmbeddedResource Include="Properties\Resources.es.resx">
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.fi.resx" />
|
||||
<EmbeddedResource Include="Properties\Resources.fr-FR.resx" />
|
||||
<EmbeddedResource Include="Properties\Resources.he.resx" />
|
||||
@ -1172,6 +1176,7 @@
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>rem Copy compiled l18n assemblies to alt folder
|
||||
|
@ -99,7 +99,7 @@ namespace DS4Windows
|
||||
|
||||
//if (mutex.WaitOne(TimeSpan.Zero, true))
|
||||
//{
|
||||
createControlService();
|
||||
createControlService();
|
||||
//rootHub = new ControlService();
|
||||
Application.EnableVisualStyles();
|
||||
ds4form = new DS4Form(args);
|
||||
|
701
DS4Windows/VJoyFeeder/vJoyFeeder.cs
Normal file
701
DS4Windows/VJoyFeeder/vJoyFeeder.cs
Normal file
@ -0,0 +1,701 @@
|
||||
// VJoy C# interface file taken from an excellent Shaul's virtual joystick driver project.
|
||||
// Licensed to public domain as is (http://vjoystick.sourceforge.net/site/index.php/forum/5-Discussion/104-what-is-the-usage-license-for-this-software).
|
||||
// http://vjoystick.sourceforge.net/site/
|
||||
// https://github.com/shauleiz/vJoy/tree/master/apps/common/vJoyInterfaceCS
|
||||
//
|
||||
// This module is a feeder for VJoy virtual joystick driver. DS4Windows can optionally re-map and feed buttons and analog axis values from DS4 Controller to VJoy device.
|
||||
// At first this may seem silly because DS4Windows can already to re-mapping by using a virtual X360 Controller driver, so why feed VJoy virtual driver also?
|
||||
// Sometimes X360 driver may run out of analog axis options, so for example "SA motion sensor steering wheel emulation" in DS4Windows would reserve a thumbstick X or Y
|
||||
// axis for SA steering wheel emulation usage. That thumbstick axis would be unavailable for "normal" thumbstick usage after this re-mapping.
|
||||
// The problem can be solved by configuring DS4Windows to re-map SA steering wheel emulation axis to VJoy axis, so all analog axies in DS4 controller are still available for normal usage.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DS4Windows.VJoyFeeder
|
||||
{
|
||||
[Flags]
|
||||
public enum HID_USAGES
|
||||
{
|
||||
HID_USAGE_X = 0x30,
|
||||
HID_USAGE_Y = 0x31,
|
||||
HID_USAGE_Z = 0x32,
|
||||
HID_USAGE_RX = 0x33,
|
||||
HID_USAGE_RY = 0x34,
|
||||
HID_USAGE_RZ = 0x35,
|
||||
HID_USAGE_SL0 = 0x36,
|
||||
HID_USAGE_SL1 = 0x37,
|
||||
HID_USAGE_WHL = 0x38,
|
||||
HID_USAGE_POV = 0x39,
|
||||
}
|
||||
|
||||
public enum VjdStat /* Declares an enumeration data type called BOOLEAN */
|
||||
{
|
||||
VJD_STAT_OWN, // The vJoy Device is owned by this application.
|
||||
VJD_STAT_FREE, // The vJoy Device is NOT owned by any application (including this one).
|
||||
VJD_STAT_BUSY, // The vJoy Device is owned by another application. It cannot be acquired by this application.
|
||||
VJD_STAT_MISS, // The vJoy Device is missing. It either does not exist or the driver is down.
|
||||
VJD_STAT_UNKN // Unknown
|
||||
};
|
||||
|
||||
|
||||
// FFB Declarations
|
||||
|
||||
// HID Descriptor definitions - FFB Report IDs
|
||||
|
||||
public enum FFBPType // FFB Packet Type
|
||||
{
|
||||
// Write
|
||||
PT_EFFREP = 0x01, // Usage Set Effect Report
|
||||
PT_ENVREP = 0x02, // Usage Set Envelope Report
|
||||
PT_CONDREP = 0x03, // Usage Set Condition Report
|
||||
PT_PRIDREP = 0x04, // Usage Set Periodic Report
|
||||
PT_CONSTREP = 0x05, // Usage Set Constant Force Report
|
||||
PT_RAMPREP = 0x06, // Usage Set Ramp Force Report
|
||||
PT_CSTMREP = 0x07, // Usage Custom Force Data Report
|
||||
PT_SMPLREP = 0x08, // Usage Download Force Sample
|
||||
PT_EFOPREP = 0x0A, // Usage Effect Operation Report
|
||||
PT_BLKFRREP = 0x0B, // Usage PID Block Free Report
|
||||
PT_CTRLREP = 0x0C, // Usage PID Device Control
|
||||
PT_GAINREP = 0x0D, // Usage Device Gain Report
|
||||
PT_SETCREP = 0x0E, // Usage Set Custom Force Report
|
||||
|
||||
// Feature
|
||||
PT_NEWEFREP = 0x01 + 0x10, // Usage Create New Effect Report
|
||||
PT_BLKLDREP = 0x02 + 0x10, // Usage Block Load Report
|
||||
PT_POOLREP = 0x03 + 0x10, // Usage PID Pool Report
|
||||
};
|
||||
|
||||
public enum FFBEType // FFB Effect Type
|
||||
{
|
||||
|
||||
// Effect Type
|
||||
ET_NONE = 0, // No Force
|
||||
ET_CONST = 1, // Constant Force
|
||||
ET_RAMP = 2, // Ramp
|
||||
ET_SQR = 3, // Square
|
||||
ET_SINE = 4, // Sine
|
||||
ET_TRNGL = 5, // Triangle
|
||||
ET_STUP = 6, // Sawtooth Up
|
||||
ET_STDN = 7, // Sawtooth Down
|
||||
ET_SPRNG = 8, // Spring
|
||||
ET_DMPR = 9, // Damper
|
||||
ET_INRT = 10, // Inertia
|
||||
ET_FRCTN = 11, // Friction
|
||||
ET_CSTM = 12, // Custom Force Data
|
||||
};
|
||||
|
||||
public enum FFB_CTRL
|
||||
{
|
||||
CTRL_ENACT = 1, // Enable all device actuators.
|
||||
CTRL_DISACT = 2, // Disable all the device actuators.
|
||||
CTRL_STOPALL = 3, // Stop All Effects Issues a stop on every running effect.
|
||||
CTRL_DEVRST = 4, // Device Reset– Clears any device paused condition, enables all actuators and clears all effects from memory.
|
||||
CTRL_DEVPAUSE = 5, // Device Pause– The all effects on the device are paused at the current time step.
|
||||
CTRL_DEVCONT = 6, // Device Continue– The all effects that running when the device was paused are restarted from their last time step.
|
||||
};
|
||||
|
||||
public enum FFBOP
|
||||
{
|
||||
EFF_START = 1, // EFFECT START
|
||||
EFF_SOLO = 2, // EFFECT SOLO START
|
||||
EFF_STOP = 3, // EFFECT STOP
|
||||
};
|
||||
|
||||
//namespace vJoyInterfaceWrap
|
||||
//{
|
||||
public class vJoy
|
||||
{
|
||||
|
||||
/***************************************************/
|
||||
/*********** Various declarations ******************/
|
||||
/***************************************************/
|
||||
private static RemovalCbFunc UserRemCB;
|
||||
private static WrapRemovalCbFunc wrf;
|
||||
private static GCHandle hRemUserData;
|
||||
|
||||
|
||||
private static FfbCbFunc UserFfbCB;
|
||||
private static WrapFfbCbFunc wf;
|
||||
private static GCHandle hFfbUserData;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct JoystickState
|
||||
{
|
||||
public byte bDevice;
|
||||
public Int32 Throttle;
|
||||
public Int32 Rudder;
|
||||
public Int32 Aileron;
|
||||
public Int32 AxisX;
|
||||
public Int32 AxisY;
|
||||
public Int32 AxisZ;
|
||||
public Int32 AxisXRot;
|
||||
public Int32 AxisYRot;
|
||||
public Int32 AxisZRot;
|
||||
public Int32 Slider;
|
||||
public Int32 Dial;
|
||||
public Int32 Wheel;
|
||||
public Int32 AxisVX;
|
||||
public Int32 AxisVY;
|
||||
public Int32 AxisVZ;
|
||||
public Int32 AxisVBRX;
|
||||
public Int32 AxisVBRY;
|
||||
public Int32 AxisVBRZ;
|
||||
public UInt32 Buttons;
|
||||
public UInt32 bHats; // Lower 4 bits: HAT switch or 16-bit of continuous HAT switch
|
||||
public UInt32 bHatsEx1; // Lower 4 bits: HAT switch or 16-bit of continuous HAT switch
|
||||
public UInt32 bHatsEx2; // Lower 4 bits: HAT switch or 16-bit of continuous HAT switch
|
||||
public UInt32 bHatsEx3; // Lower 4 bits: HAT switch or 16-bit of continuous HAT switch
|
||||
public UInt32 ButtonsEx1;
|
||||
public UInt32 ButtonsEx2;
|
||||
public UInt32 ButtonsEx3;
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct FFB_DATA
|
||||
{
|
||||
private UInt32 size;
|
||||
private UInt32 cmd;
|
||||
private IntPtr data;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct FFB_EFF_CONSTANT
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public Byte EffectBlockIndex;
|
||||
[FieldOffset(4)]
|
||||
public Int16 Magnitude;
|
||||
}
|
||||
|
||||
[System.Obsolete("use FFB_EFF_REPORT")]
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct FFB_EFF_CONST
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public Byte EffectBlockIndex;
|
||||
[FieldOffset(4)]
|
||||
public FFBEType EffectType;
|
||||
[FieldOffset(8)]
|
||||
public UInt16 Duration;// Value in milliseconds. 0xFFFF means infinite
|
||||
[FieldOffset(10)]
|
||||
public UInt16 TrigerRpt;
|
||||
[FieldOffset(12)]
|
||||
public UInt16 SamplePrd;
|
||||
[FieldOffset(14)]
|
||||
public Byte Gain;
|
||||
[FieldOffset(15)]
|
||||
public Byte TrigerBtn;
|
||||
[FieldOffset(16)]
|
||||
public bool Polar; // How to interpret force direction Polar (0-360°) or Cartesian (X,Y)
|
||||
[FieldOffset(20)]
|
||||
public Byte Direction; // Polar direction: (0x00-0xFF correspond to 0-360°)
|
||||
[FieldOffset(20)]
|
||||
public Byte DirX; // X direction: Positive values are To the right of the center (X); Negative are Two's complement
|
||||
[FieldOffset(21)]
|
||||
public Byte DirY; // Y direction: Positive values are below the center (Y); Negative are Two's complement
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct FFB_EFF_REPORT
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public Byte EffectBlockIndex;
|
||||
[FieldOffset(4)]
|
||||
public FFBEType EffectType;
|
||||
[FieldOffset(8)]
|
||||
public UInt16 Duration;// Value in milliseconds. 0xFFFF means infinite
|
||||
[FieldOffset(10)]
|
||||
public UInt16 TrigerRpt;
|
||||
[FieldOffset(12)]
|
||||
public UInt16 SamplePrd;
|
||||
[FieldOffset(14)]
|
||||
public Byte Gain;
|
||||
[FieldOffset(15)]
|
||||
public Byte TrigerBtn;
|
||||
[FieldOffset(16)]
|
||||
public bool Polar; // How to interpret force direction Polar (0-360°) or Cartesian (X,Y)
|
||||
[FieldOffset(20)]
|
||||
public Byte Direction; // Polar direction: (0x00-0xFF correspond to 0-360°)
|
||||
[FieldOffset(20)]
|
||||
public Byte DirX; // X direction: Positive values are To the right of the center (X); Negative are Two's complement
|
||||
[FieldOffset(21)]
|
||||
public Byte DirY; // Y direction: Positive values are below the center (Y); Negative are Two's complement
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct FFB_EFF_OP
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public Byte EffectBlockIndex;
|
||||
[FieldOffset(4)]
|
||||
public FFBOP EffectOp;
|
||||
[FieldOffset(8)]
|
||||
public Byte LoopCount;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct FFB_EFF_COND
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public Byte EffectBlockIndex;
|
||||
[FieldOffset(4)]
|
||||
public bool isY;
|
||||
[FieldOffset(8)]
|
||||
public Int16 CenterPointOffset; // CP Offset: Range 0x800x7F (10000 10000)
|
||||
[FieldOffset(12)]
|
||||
public Int16 PosCoeff; // Positive Coefficient: Range 0x800x7F (10000 10000)
|
||||
[FieldOffset(16)]
|
||||
public Int16 NegCoeff; // Negative Coefficient: Range 0x800x7F (10000 10000)
|
||||
[FieldOffset(20)]
|
||||
public UInt32 PosSatur; // Positive Saturation: Range 0x000xFF (0 – 10000)
|
||||
[FieldOffset(24)]
|
||||
public UInt32 NegSatur; // Negative Saturation: Range 0x000xFF (0 – 10000)
|
||||
[FieldOffset(28)]
|
||||
public Int32 DeadBand; // Dead Band: : Range 0x000xFF (0 – 10000)
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct FFB_EFF_ENVLP
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public Byte EffectBlockIndex;
|
||||
[FieldOffset(4)]
|
||||
public UInt16 AttackLevel;
|
||||
[FieldOffset(8)]
|
||||
public UInt16 FadeLevel;
|
||||
[FieldOffset(12)]
|
||||
public UInt32 AttackTime;
|
||||
[FieldOffset(16)]
|
||||
public UInt32 FadeTime;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct FFB_EFF_PERIOD
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public Byte EffectBlockIndex;
|
||||
[FieldOffset(4)]
|
||||
public UInt32 Magnitude;
|
||||
[FieldOffset(8)]
|
||||
public Int16 Offset;
|
||||
[FieldOffset(12)]
|
||||
public UInt32 Phase;
|
||||
[FieldOffset(16)]
|
||||
public UInt32 Period;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct FFB_EFF_RAMP
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public Byte EffectBlockIndex;
|
||||
[FieldOffset(4)]
|
||||
public Int16 Start; // The Normalized magnitude at the start of the effect
|
||||
[FieldOffset(8)]
|
||||
public Int16 End; // The Normalized magnitude at the end of the effect
|
||||
}
|
||||
|
||||
|
||||
/***************************************************/
|
||||
/***** Import from file vJoyInterface.dll (C) ******/
|
||||
/***************************************************/
|
||||
|
||||
///// General driver data
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "GetvJoyVersion")]
|
||||
private static extern short _GetvJoyVersion();
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "vJoyEnabled")]
|
||||
private static extern bool _vJoyEnabled();
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "GetvJoyProductString")]
|
||||
private static extern IntPtr _GetvJoyProductString();
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "GetvJoyManufacturerString")]
|
||||
private static extern IntPtr _GetvJoyManufacturerString();
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "GetvJoySerialNumberString")]
|
||||
private static extern IntPtr _GetvJoySerialNumberString();
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "DriverMatch")]
|
||||
private static extern bool _DriverMatch(ref UInt32 DllVer, ref UInt32 DrvVer);
|
||||
|
||||
///// vJoy Device properties
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "GetVJDButtonNumber")]
|
||||
private static extern int _GetVJDButtonNumber(UInt32 rID);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "GetVJDDiscPovNumber")]
|
||||
private static extern int _GetVJDDiscPovNumber(UInt32 rID);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "GetVJDContPovNumber")]
|
||||
private static extern int _GetVJDContPovNumber(UInt32 rID);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "GetVJDAxisExist")]
|
||||
private static extern UInt32 _GetVJDAxisExist(UInt32 rID, UInt32 Axis);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "GetVJDAxisMax")]
|
||||
private static extern bool _GetVJDAxisMax(UInt32 rID, UInt32 Axis, ref long Max);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "GetVJDAxisMin")]
|
||||
private static extern bool _GetVJDAxisMin(UInt32 rID, UInt32 Axis, ref long Min);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "isVJDExists")]
|
||||
private static extern bool _isVJDExists(UInt32 rID);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "GetOwnerPid")]
|
||||
private static extern int _GetOwnerPid(UInt32 rID);
|
||||
|
||||
///// Write access to vJoy Device - Basic
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "AcquireVJD")]
|
||||
private static extern bool _AcquireVJD(UInt32 rID);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "RelinquishVJD")]
|
||||
private static extern void _RelinquishVJD(UInt32 rID);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "UpdateVJD")]
|
||||
private static extern bool _UpdateVJD(UInt32 rID, ref JoystickState pData);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "GetVJDStatus")]
|
||||
private static extern int _GetVJDStatus(UInt32 rID);
|
||||
|
||||
|
||||
//// Reset functions
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "ResetVJD")]
|
||||
private static extern bool _ResetVJD(UInt32 rID);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "ResetAll")]
|
||||
private static extern bool _ResetAll();
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "ResetButtons")]
|
||||
private static extern bool _ResetButtons(UInt32 rID);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "ResetPovs")]
|
||||
private static extern bool _ResetPovs(UInt32 rID);
|
||||
|
||||
////// Write data
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "SetAxis")]
|
||||
private static extern bool _SetAxis(Int32 Value, UInt32 rID, HID_USAGES Axis);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "SetBtn")]
|
||||
private static extern bool _SetBtn(bool Value, UInt32 rID, Byte nBtn);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "SetDiscPov")]
|
||||
private static extern bool _SetDiscPov(Int32 Value, UInt32 rID, uint nPov);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "SetContPov")]
|
||||
private static extern bool _SetContPov(Int32 Value, UInt32 rID, uint nPov);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "RegisterRemovalCB", CallingConvention = CallingConvention.Cdecl)]
|
||||
private extern static void _RegisterRemovalCB(WrapRemovalCbFunc cb, IntPtr data);
|
||||
|
||||
public delegate void RemovalCbFunc(bool complete, bool First, object userData);
|
||||
public delegate void WrapRemovalCbFunc(bool complete, bool First, IntPtr userData);
|
||||
|
||||
public static void WrapperRemCB(bool complete, bool First, IntPtr userData)
|
||||
{
|
||||
|
||||
object obj = null;
|
||||
|
||||
if (userData != IntPtr.Zero)
|
||||
{
|
||||
// Convert userData from pointer to object
|
||||
GCHandle handle2 = (GCHandle)userData;
|
||||
obj = handle2.Target as object;
|
||||
}
|
||||
|
||||
// Call user-defined CB function
|
||||
UserRemCB(complete, First, obj);
|
||||
}
|
||||
|
||||
// Force Feedback (FFB)
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "FfbRegisterGenCB", CallingConvention = CallingConvention.Cdecl)]
|
||||
private extern static void _FfbRegisterGenCB(WrapFfbCbFunc cb, IntPtr data);
|
||||
|
||||
public delegate void FfbCbFunc(IntPtr data, object userData);
|
||||
public delegate void WrapFfbCbFunc(IntPtr data, IntPtr userData);
|
||||
|
||||
public static void WrapperFfbCB(IntPtr data, IntPtr userData)
|
||||
{
|
||||
|
||||
object obj = null;
|
||||
|
||||
if (userData != IntPtr.Zero)
|
||||
{
|
||||
// Convert userData from pointer to object
|
||||
GCHandle handle2 = (GCHandle)userData;
|
||||
obj = handle2.Target as object;
|
||||
}
|
||||
|
||||
// Call user-defined CB function
|
||||
UserFfbCB(data, obj);
|
||||
}
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "FfbStart")]
|
||||
private static extern bool _FfbStart(UInt32 rID);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "FfbStop")]
|
||||
private static extern bool _FfbStop(UInt32 rID);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "IsDeviceFfb")]
|
||||
private static extern bool _IsDeviceFfb(UInt32 rID);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "IsDeviceFfbEffect")]
|
||||
private static extern bool _IsDeviceFfbEffect(UInt32 rID, UInt32 Effect);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_DeviceID")]
|
||||
private static extern UInt32 _Ffb_h_DeviceID(IntPtr Packet, ref int DeviceID);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_Type")]
|
||||
private static extern UInt32 _Ffb_h_Type(IntPtr Packet, ref FFBPType Type);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_Packet")]
|
||||
private static extern UInt32 _Ffb_h_Packet(IntPtr Packet, ref UInt32 Type, ref Int32 DataSize, ref IntPtr Data);
|
||||
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_EBI")]
|
||||
private static extern UInt32 _Ffb_h_EBI(IntPtr Packet, ref Int32 Index);
|
||||
|
||||
#pragma warning disable 618
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_Eff_Const")]
|
||||
private static extern UInt32 _Ffb_h_Eff_Const(IntPtr Packet, ref FFB_EFF_CONST Effect);
|
||||
#pragma warning restore 618
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_Eff_Report")]
|
||||
private static extern UInt32 _Ffb_h_Eff_Report(IntPtr Packet, ref FFB_EFF_REPORT Effect);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_DevCtrl")]
|
||||
private static extern UInt32 _Ffb_h_DevCtrl(IntPtr Packet, ref FFB_CTRL Control);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_EffOp")]
|
||||
private static extern UInt32 _Ffb_h_EffOp(IntPtr Packet, ref FFB_EFF_OP Operation);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_DevGain")]
|
||||
private static extern UInt32 _Ffb_h_DevGain(IntPtr Packet, ref Byte Gain);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_Eff_Cond")]
|
||||
private static extern UInt32 _Ffb_h_Eff_Cond(IntPtr Packet, ref FFB_EFF_COND Condition);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_Eff_Envlp")]
|
||||
private static extern UInt32 _Ffb_h_Eff_Envlp(IntPtr Packet, ref FFB_EFF_ENVLP Envelope);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_Eff_Period")]
|
||||
private static extern UInt32 _Ffb_h_Eff_Period(IntPtr Packet, ref FFB_EFF_PERIOD Effect);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_EffNew")]
|
||||
private static extern UInt32 _Ffb_h_EffNew(IntPtr Packet, ref FFBEType Effect);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_Eff_Ramp")]
|
||||
private static extern UInt32 _Ffb_h_Eff_Ramp(IntPtr Packet, ref FFB_EFF_RAMP RampEffect);
|
||||
|
||||
[DllImport("vJoyInterface.dll", EntryPoint = "Ffb_h_Eff_Constant")]
|
||||
private static extern UInt32 _Ffb_h_Eff_Constant(IntPtr Packet, ref FFB_EFF_CONSTANT ConstantEffect);
|
||||
|
||||
/***************************************************/
|
||||
/********** Export functions (C#) ******************/
|
||||
/***************************************************/
|
||||
|
||||
///// General driver data
|
||||
public short GetvJoyVersion() { return _GetvJoyVersion(); }
|
||||
public bool vJoyEnabled() { return _vJoyEnabled(); }
|
||||
public string GetvJoyProductString() { return Marshal.PtrToStringAuto(_GetvJoyProductString()); }
|
||||
public string GetvJoyManufacturerString() { return Marshal.PtrToStringAuto(_GetvJoyManufacturerString()); }
|
||||
public string GetvJoySerialNumberString() { return Marshal.PtrToStringAuto(_GetvJoySerialNumberString()); }
|
||||
public bool DriverMatch(ref UInt32 DllVer, ref UInt32 DrvVer) { return _DriverMatch(ref DllVer, ref DrvVer); }
|
||||
|
||||
///// vJoy Device properties
|
||||
public int GetVJDButtonNumber(uint rID) { return _GetVJDButtonNumber(rID); }
|
||||
public int GetVJDDiscPovNumber(uint rID) { return _GetVJDDiscPovNumber(rID); }
|
||||
public int GetVJDContPovNumber(uint rID) { return _GetVJDContPovNumber(rID); }
|
||||
public bool GetVJDAxisExist(UInt32 rID, HID_USAGES Axis)
|
||||
{
|
||||
UInt32 res = _GetVJDAxisExist(rID, (uint)Axis);
|
||||
if (res == 1)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
public bool GetVJDAxisMax(UInt32 rID, HID_USAGES Axis, ref long Max) { return _GetVJDAxisMax(rID, (uint)Axis, ref Max); }
|
||||
public bool GetVJDAxisMin(UInt32 rID, HID_USAGES Axis, ref long Min) { return _GetVJDAxisMin(rID, (uint)Axis, ref Min); }
|
||||
public bool isVJDExists(UInt32 rID) { return _isVJDExists(rID); }
|
||||
public int GetOwnerPid(UInt32 rID) { return _GetOwnerPid(rID); }
|
||||
|
||||
///// Write access to vJoy Device - Basic
|
||||
public bool AcquireVJD(UInt32 rID) { return _AcquireVJD(rID); }
|
||||
public void RelinquishVJD(uint rID) { _RelinquishVJD(rID); }
|
||||
public bool UpdateVJD(UInt32 rID, ref JoystickState pData) { return _UpdateVJD(rID, ref pData); }
|
||||
public VjdStat GetVJDStatus(UInt32 rID) { return (VjdStat)_GetVJDStatus(rID); }
|
||||
|
||||
//// Reset functions
|
||||
public bool ResetVJD(UInt32 rID) { return _ResetVJD(rID); }
|
||||
public bool ResetAll() { return _ResetAll(); }
|
||||
public bool ResetButtons(UInt32 rID) { return _ResetButtons(rID); }
|
||||
public bool ResetPovs(UInt32 rID) { return _ResetPovs(rID); }
|
||||
|
||||
////// Write data
|
||||
public bool SetAxis(Int32 Value, UInt32 rID, HID_USAGES Axis) { return _SetAxis(Value, rID, Axis); }
|
||||
public bool SetBtn(bool Value, UInt32 rID, uint nBtn) { return _SetBtn(Value, rID, (Byte)nBtn); }
|
||||
public bool SetDiscPov(Int32 Value, UInt32 rID, uint nPov) { return _SetDiscPov(Value, rID, nPov); }
|
||||
public bool SetContPov(Int32 Value, UInt32 rID, uint nPov) { return _SetContPov(Value, rID, nPov); }
|
||||
|
||||
// Register CB function that takes a C# object as userdata
|
||||
public void RegisterRemovalCB(RemovalCbFunc cb, object data)
|
||||
{
|
||||
// Free existing GCHandle (if exists)
|
||||
if (hRemUserData.IsAllocated && hRemUserData.Target != null)
|
||||
hRemUserData.Free();
|
||||
|
||||
// Convert object to pointer
|
||||
hRemUserData = GCHandle.Alloc(data);
|
||||
|
||||
// Apply the user-defined CB function
|
||||
UserRemCB = new RemovalCbFunc(cb);
|
||||
wrf = new WrapRemovalCbFunc(WrapperRemCB);
|
||||
|
||||
_RegisterRemovalCB(wrf, (IntPtr)hRemUserData);
|
||||
}
|
||||
|
||||
// Register CB function that takes a pointer as userdata
|
||||
public void RegisterRemovalCB(WrapRemovalCbFunc cb, IntPtr data)
|
||||
{
|
||||
wrf = new WrapRemovalCbFunc(cb);
|
||||
_RegisterRemovalCB(wrf, data);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//// Force Feedback (FFB)
|
||||
|
||||
// Register CB function that takes a C# object as userdata
|
||||
public void FfbRegisterGenCB(FfbCbFunc cb, object data)
|
||||
{
|
||||
// Free existing GCHandle (if exists)
|
||||
if (hFfbUserData.IsAllocated && hFfbUserData.Target != null)
|
||||
hFfbUserData.Free();
|
||||
|
||||
// Convert object to pointer
|
||||
hFfbUserData = GCHandle.Alloc(data);
|
||||
|
||||
// Apply the user-defined CB function
|
||||
UserFfbCB = new FfbCbFunc(cb);
|
||||
wf = new WrapFfbCbFunc(WrapperFfbCB);
|
||||
|
||||
_FfbRegisterGenCB(wf, (IntPtr)hFfbUserData);
|
||||
}
|
||||
|
||||
// Register CB function that takes a pointer as userdata
|
||||
public void FfbRegisterGenCB(WrapFfbCbFunc cb, IntPtr data)
|
||||
{
|
||||
wf = new WrapFfbCbFunc(cb);
|
||||
_FfbRegisterGenCB(wf, data);
|
||||
}
|
||||
|
||||
[Obsolete("you can remove the function from your code")]
|
||||
public bool FfbStart(UInt32 rID) { return _FfbStart(rID); }
|
||||
[Obsolete("you can remove the function from your code")]
|
||||
public bool FfbStop(UInt32 rID) { return _FfbStop(rID); }
|
||||
public bool IsDeviceFfb(UInt32 rID) { return _IsDeviceFfb(rID); }
|
||||
public bool IsDeviceFfbEffect(UInt32 rID, UInt32 Effect) { return _IsDeviceFfbEffect(rID, Effect); }
|
||||
public UInt32 Ffb_h_DeviceID(IntPtr Packet, ref int DeviceID) { return _Ffb_h_DeviceID(Packet, ref DeviceID); }
|
||||
public UInt32 Ffb_h_Type(IntPtr Packet, ref FFBPType Type) { return _Ffb_h_Type(Packet, ref Type); }
|
||||
public UInt32 Ffb_h_Packet(IntPtr Packet, ref UInt32 Type, ref Int32 DataSize, ref Byte[] Data)
|
||||
{
|
||||
IntPtr buf = IntPtr.Zero;
|
||||
UInt32 res = _Ffb_h_Packet(Packet, ref Type, ref DataSize, ref buf);
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
DataSize -= 8;
|
||||
Data = new byte[DataSize];
|
||||
Marshal.Copy(buf, Data, 0, DataSize);
|
||||
return res;
|
||||
}
|
||||
public UInt32 Ffb_h_EBI(IntPtr Packet, ref Int32 Index) { return _Ffb_h_EBI(Packet, ref Index); }
|
||||
[Obsolete("use Ffb_h_Eff_Report instead")]
|
||||
public UInt32 Ffb_h_Eff_Const(IntPtr Packet, ref FFB_EFF_CONST Effect) { return _Ffb_h_Eff_Const(Packet, ref Effect); }
|
||||
public UInt32 Ffb_h_Eff_Report(IntPtr Packet, ref FFB_EFF_REPORT Effect) { return _Ffb_h_Eff_Report(Packet, ref Effect); }
|
||||
public UInt32 Ffb_h_DevCtrl(IntPtr Packet, ref FFB_CTRL Control) { return _Ffb_h_DevCtrl(Packet, ref Control); }
|
||||
public UInt32 Ffb_h_EffOp(IntPtr Packet, ref FFB_EFF_OP Operation) { return _Ffb_h_EffOp(Packet, ref Operation); }
|
||||
public UInt32 Ffb_h_DevGain(IntPtr Packet, ref Byte Gain) { return _Ffb_h_DevGain(Packet, ref Gain); }
|
||||
public UInt32 Ffb_h_Eff_Cond(IntPtr Packet, ref FFB_EFF_COND Condition) { return _Ffb_h_Eff_Cond(Packet, ref Condition); }
|
||||
public UInt32 Ffb_h_Eff_Envlp(IntPtr Packet, ref FFB_EFF_ENVLP Envelope) { return _Ffb_h_Eff_Envlp(Packet, ref Envelope); }
|
||||
public UInt32 Ffb_h_Eff_Period(IntPtr Packet, ref FFB_EFF_PERIOD Effect) { return _Ffb_h_Eff_Period(Packet, ref Effect); }
|
||||
public UInt32 Ffb_h_EffNew(IntPtr Packet, ref FFBEType Effect) { return _Ffb_h_EffNew(Packet, ref Effect); }
|
||||
public UInt32 Ffb_h_Eff_Ramp(IntPtr Packet, ref FFB_EFF_RAMP RampEffect) { return _Ffb_h_Eff_Ramp(Packet, ref RampEffect); }
|
||||
public UInt32 Ffb_h_Eff_Constant(IntPtr Packet, ref FFB_EFF_CONSTANT ConstantEffect) { return _Ffb_h_Eff_Constant(Packet, ref ConstantEffect); }
|
||||
}
|
||||
//}
|
||||
|
||||
public class vJoyFeeder
|
||||
{
|
||||
private static readonly object vJoyLocker = new object();
|
||||
|
||||
static bool vJoyInitialized = false;
|
||||
static bool vJoyAvailable = false;
|
||||
static vJoy vJoyObj = null;
|
||||
|
||||
vJoyFeeder()
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
~vJoyFeeder()
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public static void InitializeVJoyDevice(uint vJoyID, HID_USAGES axis)
|
||||
{
|
||||
lock (vJoyLocker)
|
||||
{
|
||||
if (vJoyInitialized) return;
|
||||
|
||||
vJoyInitialized = true;
|
||||
AppLogger.LogToGui("Initializing VJoy virtual joystick driver via vJoyInterface.dll interface", false);
|
||||
|
||||
if (vJoyObj == null) vJoyObj = new vJoy();
|
||||
|
||||
if (vJoyObj.vJoyEnabled() && vJoyObj.GetVJDAxisExist(vJoyID, axis))
|
||||
{
|
||||
AppLogger.LogToGui("Connection to VJoy virtual joystick established", false);
|
||||
AppLogger.LogToGui($"VJoy driver. Vendor={vJoyObj.GetvJoyManufacturerString()} Product={vJoyObj.GetvJoyProductString()} Version={vJoyObj.GetvJoySerialNumberString()} Device#={vJoyID} Axis={axis}", false);
|
||||
|
||||
// Test if DLL matches the driver
|
||||
UInt32 DllVer = 0, DrvVer = 0;
|
||||
if (!vJoyObj.DriverMatch(ref DllVer, ref DrvVer))
|
||||
AppLogger.LogToGui("WARNING. VJoy version of Driver {DrvVer}) does not match interface DLL Version {DllVer}. This may lead to unexpected problems or crashes. Update VJoy driver and vJoyInterface.dll", false);
|
||||
|
||||
VjdStat status = vJoyObj.GetVJDStatus(vJoyID);
|
||||
if ((status == VjdStat.VJD_STAT_OWN) || ((status == VjdStat.VJD_STAT_FREE) && (!vJoyObj.AcquireVJD(vJoyID))))
|
||||
{
|
||||
vJoyAvailable = false;
|
||||
AppLogger.LogToGui("ERROR. Failed to acquire vJoy device# {vJoyID}. Use another VJoy device or make sure there are no other VJoy feeder apps using the same device", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
//vJoyObj.GetVJDAxisMax(vJoyID, axis, ref vJoyAxisMaxValue);
|
||||
//AppLogger.LogToGui($"VJoy axis {axis} max value={vJoyAxisMaxValue}", false);
|
||||
vJoyObj.ResetVJD(vJoyID);
|
||||
vJoyAvailable = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vJoyAvailable = false;
|
||||
AppLogger.LogToGui($"ERROR. VJoy device# {vJoyID} or {axis} axis not available. Check vJoy driver installation and configuration", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Feed axis value to VJoy virtual joystic driver (DS4Windows sixaxis (SA) motion sensor steering wheel emulation feature can optionally feed VJoy analog axis instead of ScpVBus x360 axis
|
||||
public static void FeedAxisValue(int value, uint vJoyID, HID_USAGES axis)
|
||||
{
|
||||
if (!vJoyInitialized)
|
||||
InitializeVJoyDevice(vJoyID, axis);
|
||||
|
||||
if (vJoyAvailable)
|
||||
vJoyObj.SetAxis(value, vJoyID, axis);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user