mirror of
https://github.com/ayufan/steam-deck-tools.git
synced 2025-12-06 07:12:01 +01:00
SteamController: Add initial DS4 support (with Gyro, Accel, Trackpads and Haptics)
This commit is contained in:
parent
862d7afec5
commit
70237ad9d4
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
## 0.6.x
|
||||
|
||||
- SteamController: Add initial `DS4` support (with Gyro, Accel, Trackpads and Haptics)
|
||||
- PowerControl: Install custom resolutions (EDID) (experimental feature)
|
||||
- SteamController: Add `X360: No Touchpads` profile
|
||||
- All: Show `Missing RTSS` button to install RTSS
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ namespace SteamController
|
|||
|
||||
public Devices.SteamController Steam { get; private set; }
|
||||
public Devices.Xbox360Controller X360 { get; private set; }
|
||||
public Devices.DS4Controller DS4 { get; private set; }
|
||||
public Devices.KeyboardController Keyboard { get; private set; }
|
||||
public Devices.MouseController Mouse { get; private set; }
|
||||
|
||||
|
|
@ -25,11 +26,12 @@ namespace SteamController
|
|||
public bool GameProcessRunning { get; set; }
|
||||
public bool RTSSInForeground { get; set; }
|
||||
public bool SteamUsesX360Controller { get; set; }
|
||||
public bool SteamUsesDS4Controller { get; set; }
|
||||
public bool SteamUsesSteamInput { get; set; }
|
||||
|
||||
public bool IsActive
|
||||
{
|
||||
get { return RTSSInForeground || GameProcessRunning || SteamUsesX360Controller || SteamUsesSteamInput; }
|
||||
get { return RTSSInForeground || GameProcessRunning || SteamUsesX360Controller || SteamUsesDS4Controller || SteamUsesSteamInput; }
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
|
@ -37,6 +39,7 @@ namespace SteamController
|
|||
string reason = "state";
|
||||
if (GameProcessRunning) reason += " game";
|
||||
if (SteamUsesX360Controller) reason += " steamX360";
|
||||
if (SteamUsesDS4Controller) reason += " steamDS4";
|
||||
if (SteamUsesSteamInput) reason += " steamInput";
|
||||
if (RTSSInForeground) reason += " rtss";
|
||||
return reason;
|
||||
|
|
@ -78,6 +81,7 @@ namespace SteamController
|
|||
{
|
||||
Steam = new Devices.SteamController();
|
||||
X360 = new Devices.Xbox360Controller();
|
||||
DS4 = new Devices.DS4Controller();
|
||||
Keyboard = new Devices.KeyboardController();
|
||||
Mouse = new Devices.MouseController();
|
||||
|
||||
|
|
@ -92,6 +96,7 @@ namespace SteamController
|
|||
|
||||
using (Steam) { }
|
||||
using (X360) { }
|
||||
using (DS4) { }
|
||||
using (Keyboard) { }
|
||||
using (Mouse) { }
|
||||
}
|
||||
|
|
@ -99,6 +104,7 @@ namespace SteamController
|
|||
public void Tick()
|
||||
{
|
||||
X360.Tick();
|
||||
DS4.Tick();
|
||||
|
||||
foreach (var manager in Managers)
|
||||
{
|
||||
|
|
@ -111,6 +117,7 @@ namespace SteamController
|
|||
{
|
||||
Steam.BeforeUpdate();
|
||||
X360.BeforeUpdate();
|
||||
DS4.BeforeUpdate();
|
||||
Keyboard.BeforeUpdate();
|
||||
Mouse.BeforeUpdate();
|
||||
|
||||
|
|
@ -131,6 +138,7 @@ namespace SteamController
|
|||
{
|
||||
Steam.Update();
|
||||
X360.Update();
|
||||
DS4.Update();
|
||||
Keyboard.Update();
|
||||
Mouse.Update();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ namespace SteamController
|
|||
items.Add("[LM]");
|
||||
|
||||
items.Add(X360.Connected ? "[X360]" : X360.Valid ? "[no-X360]" : "[inv-X360]");
|
||||
items.Add(DS4.Connected ? "[DS4]" : DS4.Valid ? "[no-DS4]" : "[inv-DS4]");
|
||||
items.Add(KeyboardMouseValid ? "[KM]" : "[inv-KM]");
|
||||
|
||||
foreach (var button in Steam.AllButtons)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ namespace SteamController
|
|||
var nextReset = stopwatch.Elapsed.Add(UpdateResetInterval);
|
||||
|
||||
X360.Start();
|
||||
DS4.Start();
|
||||
|
||||
while (threadRunning)
|
||||
{
|
||||
|
|
@ -58,6 +59,7 @@ namespace SteamController
|
|||
}
|
||||
|
||||
X360.Stop();
|
||||
DS4.Stop();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
|
|
|
|||
|
|
@ -30,7 +30,10 @@ namespace SteamController
|
|||
new Profiles.Predefined.SteamProfile() { Name = "Steam", Visible = false },
|
||||
new Profiles.Predefined.SteamWithShorcutsProfile() { Name = "Steam with Shortcuts", Visible = false },
|
||||
new Profiles.Predefined.X360HapticProfile() { Name = "X360", EmulateTouchPads = true },
|
||||
new Profiles.Predefined.X360HapticProfile() { Name = "X360: No Touchpads", EmulateTouchPads = false }
|
||||
new Profiles.Predefined.X360HapticProfile() { Name = "X360: No Touchpads", EmulateTouchPads = false },
|
||||
#if DEBUG
|
||||
new Profiles.Predefined.DS4HapticProfile() { Name = "DS4" },
|
||||
#endif
|
||||
},
|
||||
Managers = {
|
||||
new Managers.ProcessManager(),
|
||||
|
|
@ -274,8 +277,12 @@ namespace SteamController
|
|||
Devices.Xbox360Controller.VendorID,
|
||||
Devices.Xbox360Controller.ProductID
|
||||
);
|
||||
var blacklistedDS4Controller = Helpers.SteamConfiguration.IsControllerBlacklisted(
|
||||
Devices.DS4Controller.VendorID,
|
||||
Devices.DS4Controller.ProductID
|
||||
);
|
||||
|
||||
if (blacklistedSteamController is null || blacklistedX360Controller is null)
|
||||
if (blacklistedSteamController is null || blacklistedX360Controller is null || blacklistedDS4Controller is null)
|
||||
{
|
||||
// Appears that Steam is not installed
|
||||
if (always)
|
||||
|
|
@ -291,11 +298,12 @@ namespace SteamController
|
|||
page.Caption = TitleWithVersion;
|
||||
page.AllowCancel = true;
|
||||
|
||||
var useX360Controller = page.RadioButtons.Add("Use &X360 Controller with Steam (preferred)");
|
||||
useX360Controller.Text += "\n- Will always use X360 controller.";
|
||||
useX360Controller.Checked = Settings.Default.EnableSteamDetection == true &&
|
||||
var useXInputController = page.RadioButtons.Add("Use &X360/DS4 Controller with Steam (preferred)");
|
||||
useXInputController.Text += "\n- Will always use X360 or DS4 controller.";
|
||||
useXInputController.Checked = Settings.Default.EnableSteamDetection == true &&
|
||||
blacklistedSteamController == true &&
|
||||
blacklistedX360Controller == false;
|
||||
blacklistedX360Controller == false &&
|
||||
blacklistedDS4Controller == false;
|
||||
|
||||
var useSteamInput = page.RadioButtons.Add("Use &Steam Input with Steam (requires configuration)");
|
||||
useSteamInput.Text += "\n- Will try to use Steam controls.";
|
||||
|
|
@ -303,13 +311,14 @@ namespace SteamController
|
|||
useSteamInput.Text += "\n- Click Help for more information.";
|
||||
useSteamInput.Checked = Settings.Default.EnableSteamDetection == true &&
|
||||
blacklistedSteamController == false &&
|
||||
blacklistedX360Controller == true;
|
||||
blacklistedX360Controller == true &&
|
||||
blacklistedDS4Controller == true;
|
||||
|
||||
var ignoreSteam = page.RadioButtons.Add("&Ignore Steam (only if you know why you need it)");
|
||||
ignoreSteam.Text += "\n- Will revert all previously made changes.";
|
||||
ignoreSteam.Checked = Settings.Default.EnableSteamDetection == false;
|
||||
|
||||
bool valid = ignoreSteam.Checked || useX360Controller.Checked || useSteamInput.Checked;
|
||||
bool valid = ignoreSteam.Checked || useXInputController.Checked || useSteamInput.Checked;
|
||||
|
||||
// If everything is OK, on subsequent runs nothing to configure
|
||||
if (valid && !always)
|
||||
|
|
@ -352,16 +361,21 @@ namespace SteamController
|
|||
var steamControllerUpdate = Helpers.SteamConfiguration.UpdateControllerBlacklist(
|
||||
Devices.SteamController.VendorID,
|
||||
Devices.SteamController.ProductID,
|
||||
useX360Controller.Checked
|
||||
useXInputController.Checked
|
||||
);
|
||||
var x360ControllerUpdate = Helpers.SteamConfiguration.UpdateControllerBlacklist(
|
||||
Devices.Xbox360Controller.VendorID,
|
||||
Devices.Xbox360Controller.ProductID,
|
||||
useSteamInput.Checked
|
||||
);
|
||||
Settings.Default.EnableSteamDetection = useSteamInput.Checked || useX360Controller.Checked;
|
||||
var ds4ControllerUpdate = Helpers.SteamConfiguration.UpdateControllerBlacklist(
|
||||
Devices.DS4Controller.VendorID,
|
||||
Devices.DS4Controller.ProductID,
|
||||
useSteamInput.Checked
|
||||
);
|
||||
Settings.Default.EnableSteamDetection = useSteamInput.Checked || useXInputController.Checked;
|
||||
|
||||
if (steamControllerUpdate && x360ControllerUpdate)
|
||||
if (steamControllerUpdate && x360ControllerUpdate && ds4ControllerUpdate)
|
||||
{
|
||||
notifyIcon.ShowBalloonTip(
|
||||
3000, TitleWithVersion,
|
||||
|
|
@ -399,7 +413,11 @@ namespace SteamController
|
|||
{
|
||||
Desktop = ProfilesSettings.DesktopPanelSettings.Default,
|
||||
X360 = ProfilesSettings.X360BackPanelSettings.Default,
|
||||
X360Haptic = ProfilesSettings.X360HapticSettings.Default,
|
||||
X360Haptic = ProfilesSettings.HapticSettings.X360,
|
||||
#if DEBUG
|
||||
DS4 = ProfilesSettings.DS4BackPanelSettings.Default,
|
||||
DS4Haptic = ProfilesSettings.HapticSettings.DS4,
|
||||
#endif
|
||||
Application = Settings.Default,
|
||||
DEBUG = SettingsDebug.Default
|
||||
}
|
||||
|
|
|
|||
304
SteamController/Devices/DS4Controller.cs
Normal file
304
SteamController/Devices/DS4Controller.cs
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
using System.Diagnostics;
|
||||
using Nefarius.ViGEm.Client;
|
||||
using Nefarius.ViGEm.Client.Exceptions;
|
||||
using Nefarius.ViGEm.Client.Targets;
|
||||
using Nefarius.ViGEm.Client.Targets.DualShock4;
|
||||
using Nefarius.ViGEm.Client.Targets.Xbox360;
|
||||
using static CommonHelpers.Log;
|
||||
|
||||
namespace SteamController.Devices
|
||||
{
|
||||
public partial class DS4Controller : IDisposable
|
||||
{
|
||||
public readonly TimeSpan FeedbackTimeout = TimeSpan.FromMilliseconds(1000);
|
||||
public const ushort VendorID = 0x054C;
|
||||
public const ushort ProductID = 0x05C4;
|
||||
|
||||
private const int REPORT_SIZE = 63;
|
||||
private const int TIMESTAMP_HZ = 800;
|
||||
private const int TIMESTAMP_INCREMENT = 188;
|
||||
|
||||
private ViGEmClient? client;
|
||||
private IDualShock4Controller? device;
|
||||
private bool isConnected;
|
||||
private byte[] reportBytes = new byte[REPORT_SIZE];
|
||||
private bool submitReport;
|
||||
private int submittedReports = 0;
|
||||
private Stopwatch stopwatch = new Stopwatch();
|
||||
|
||||
public DS4Controller()
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
using (client) { }
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
lock (this) { Fail(); }
|
||||
}
|
||||
|
||||
internal bool Tick()
|
||||
{
|
||||
if (this.device is not null)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
var client = new ViGEmClient();
|
||||
var device = client.CreateDualShock4Controller();
|
||||
device.AutoSubmitReport = false;
|
||||
device.FeedbackReceived += DS4_FeedbackReceived;
|
||||
this.device = device;
|
||||
this.client = client;
|
||||
return true;
|
||||
}
|
||||
catch (VigemBusNotFoundException)
|
||||
{
|
||||
// ViGem is not installed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void Fail()
|
||||
{
|
||||
var client = this.client;
|
||||
|
||||
// unset current device
|
||||
this.isConnected = false;
|
||||
this.client = null;
|
||||
this.device = null;
|
||||
this.stopwatch.Stop();
|
||||
|
||||
try { using (client) { } }
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
private void PrepareReport()
|
||||
{
|
||||
var oldReportBytes = reportBytes;
|
||||
|
||||
reportBytes = new byte[63];
|
||||
submitReport = false;
|
||||
|
||||
Counter.Set(reportBytes, (byte)((submittedReports << 2) & 0xFF));
|
||||
BatteryLevel.Set(reportBytes, 255);
|
||||
Timestamp.Set(reportBytes, (ushort)(stopwatch.ElapsedMilliseconds * TIMESTAMP_HZ * TIMESTAMP_INCREMENT / 1000));
|
||||
|
||||
DPadReleased.Set(reportBytes);
|
||||
LeftThumbX.SetScaled(reportBytes, 0);
|
||||
LeftThumbY.SetScaled(reportBytes, 0);
|
||||
RightThumbX.SetScaled(reportBytes, 0);
|
||||
RightThumbY.SetScaled(reportBytes, 0);
|
||||
GyroX.Set(reportBytes, 0);
|
||||
GyroY.Set(reportBytes, 0);
|
||||
GyroZ.Set(reportBytes, 0);
|
||||
AccelX.Set(reportBytes, 0);
|
||||
AccelY.Set(reportBytes, 0);
|
||||
AccelZ.Set(reportBytes, 0);
|
||||
|
||||
// Copy trackpad packets
|
||||
reportBytes[32] = (byte)((reportBytes[32] + 1) & 0x3);
|
||||
Array.Copy(oldReportBytes, 33, reportBytes, 33, 9);
|
||||
Array.Copy(oldReportBytes, 33, reportBytes, 42, 9 * 2);
|
||||
}
|
||||
|
||||
internal void BeforeUpdate()
|
||||
{
|
||||
device?.ResetReport();
|
||||
|
||||
if (!isConnected)
|
||||
{
|
||||
FeedbackLargeMotor = null;
|
||||
FeedbackSmallMotor = null;
|
||||
FeedbackReceived = null;
|
||||
LightbarColor = null;
|
||||
}
|
||||
|
||||
PrepareReport();
|
||||
Connected = false;
|
||||
}
|
||||
|
||||
private void SetConnected(bool wantsConnected)
|
||||
{
|
||||
if (wantsConnected == isConnected)
|
||||
return;
|
||||
|
||||
if (wantsConnected)
|
||||
{
|
||||
try
|
||||
{
|
||||
device?.Connect();
|
||||
stopwatch.Restart();
|
||||
TraceLine("Connected DS4 Controller.");
|
||||
}
|
||||
catch (System.ComponentModel.Win32Exception e)
|
||||
{
|
||||
// This is expected exception (as sometimes device will fail to connect)
|
||||
// ERROR_SUCCESS, which likely means COM did not succeed
|
||||
if (e.NativeErrorCode == 0)
|
||||
DebugException("DS4", "ConnectExpected", e);
|
||||
else
|
||||
TraceException("DS4", "ConnectExpected", e);
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TraceException("DS4", "Connect", e);
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
device?.Disconnect();
|
||||
stopwatch.Stop();
|
||||
TraceLine("Disconnected DS4 Controller.");
|
||||
}
|
||||
catch (VigemTargetNotPluggedInException)
|
||||
{
|
||||
// everything fine
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TraceException("DS4", "Disconnect", e);
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
isConnected = wantsConnected;
|
||||
}
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
if (device is not null && Connected != isConnected)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
SetConnected(Connected);
|
||||
}
|
||||
}
|
||||
|
||||
if (isConnected && submitReport)
|
||||
{
|
||||
try
|
||||
{
|
||||
device?.SubmitRawReport(reportBytes);
|
||||
submittedReports++;
|
||||
}
|
||||
catch (VigemInvalidTargetException)
|
||||
{
|
||||
// Device was lost
|
||||
lock (this) { Fail(); }
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TraceException("DS4", "SubmitReport", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (FeedbackReceived is not null && FeedbackReceived.Value.Add(FeedbackTimeout) < DateTime.Now)
|
||||
{
|
||||
FeedbackLargeMotor = null;
|
||||
FeedbackSmallMotor = null;
|
||||
FeedbackReceived = null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Valid
|
||||
{
|
||||
get { return device is not null; }
|
||||
}
|
||||
|
||||
public bool Connected { get; set; }
|
||||
public byte? FeedbackLargeMotor { get; private set; }
|
||||
public byte? FeedbackSmallMotor { get; private set; }
|
||||
public LightbarColor? LightbarColor { get; private set; }
|
||||
public DateTime? FeedbackReceived { get; private set; }
|
||||
|
||||
public bool this[DualShock4Button button]
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
button.Set(reportBytes, value);
|
||||
submitReport = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool this[DualShock4DPadDirection button]
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
button.Set(reportBytes);
|
||||
submitReport = true;
|
||||
}
|
||||
}
|
||||
|
||||
public short this[DualShock4Axis axis]
|
||||
{
|
||||
set
|
||||
{
|
||||
axis.SetScaled(reportBytes, value);
|
||||
submitReport = true;
|
||||
}
|
||||
}
|
||||
|
||||
public short this[DualShock4Slider slider]
|
||||
{
|
||||
set
|
||||
{
|
||||
slider.Set(reportBytes, value);
|
||||
submitReport = true;
|
||||
}
|
||||
}
|
||||
|
||||
public short this[DualShock4Sensor sensor]
|
||||
{
|
||||
set
|
||||
{
|
||||
sensor.Set(reportBytes, value);
|
||||
submitReport = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Point? this[DualShock4Finger finger]
|
||||
{
|
||||
set
|
||||
{
|
||||
finger.Set(reportBytes, value);
|
||||
submitReport = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Overwrite(DualShock4Button button, bool value, int minPressedTime = 0)
|
||||
{
|
||||
button.Set(reportBytes, value);
|
||||
submitReport = true;
|
||||
}
|
||||
|
||||
public void ResetFeedback()
|
||||
{
|
||||
FeedbackReceived = null;
|
||||
}
|
||||
|
||||
private void DS4_FeedbackReceived(object sender, DualShock4FeedbackReceivedEventArgs e)
|
||||
{
|
||||
FeedbackLargeMotor = e.LargeMotor;
|
||||
FeedbackSmallMotor = e.SmallMotor;
|
||||
LightbarColor = e.LightbarColor;
|
||||
FeedbackReceived = DateTime.Now;
|
||||
}
|
||||
}
|
||||
}
|
||||
212
SteamController/Devices/DS4ControllerActions.cs
Normal file
212
SteamController/Devices/DS4ControllerActions.cs
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
using Nefarius.ViGEm.Client;
|
||||
using Nefarius.ViGEm.Client.Exceptions;
|
||||
using Nefarius.ViGEm.Client.Targets;
|
||||
using Nefarius.ViGEm.Client.Targets.DualShock4;
|
||||
using static CommonHelpers.Log;
|
||||
|
||||
namespace SteamController.Devices
|
||||
{
|
||||
public partial class DS4Controller
|
||||
{
|
||||
public struct DualShock4Axis
|
||||
{
|
||||
public int Offset { get; }
|
||||
public bool Invert { get; }
|
||||
|
||||
public DualShock4Axis(int offset, bool invert)
|
||||
{
|
||||
Offset = offset;
|
||||
Invert = invert;
|
||||
}
|
||||
|
||||
internal void Set(byte[] report, byte value)
|
||||
{
|
||||
report[Offset] = value;
|
||||
}
|
||||
|
||||
internal void SetScaled(byte[] report, short value)
|
||||
{
|
||||
var valuef = value / 256;
|
||||
if (Invert)
|
||||
valuef = -valuef;
|
||||
Set(report, (byte)(valuef + sbyte.MinValue));
|
||||
}
|
||||
}
|
||||
|
||||
public struct DualShock4Button
|
||||
{
|
||||
public int Offset { get; }
|
||||
public int Bit { get; }
|
||||
public byte Mask { get => (byte)(1 << Bit); }
|
||||
|
||||
public DualShock4Button(int offset, int bit)
|
||||
{
|
||||
Offset = offset;
|
||||
Bit = bit;
|
||||
}
|
||||
|
||||
internal void Set(byte[] report, bool value)
|
||||
{
|
||||
report[Offset] = (byte)((report[Offset] & ~Mask) | (value ? Mask : 0));
|
||||
}
|
||||
}
|
||||
|
||||
public struct DualShock4DPadDirection
|
||||
{
|
||||
public int Offset { get; }
|
||||
public int Value { get; }
|
||||
public int Mask { get; }
|
||||
|
||||
public DualShock4DPadDirection(int offset, int value, int mask)
|
||||
{
|
||||
Offset = offset;
|
||||
Value = value;
|
||||
Mask = mask;
|
||||
}
|
||||
|
||||
internal void Set(byte[] report)
|
||||
{
|
||||
report[Offset] = (byte)((report[Offset] & ~Mask) | Value);
|
||||
}
|
||||
}
|
||||
|
||||
public struct DualShock4Slider
|
||||
{
|
||||
public int Offset { get; }
|
||||
|
||||
public DualShock4Slider(int offset)
|
||||
{
|
||||
Offset = offset;
|
||||
}
|
||||
|
||||
internal void Set(byte[] report, byte value)
|
||||
{
|
||||
report[Offset] = value;
|
||||
}
|
||||
|
||||
internal void Set(byte[] report, short value)
|
||||
{
|
||||
int result = Math.Clamp(value, (short)0, short.MaxValue) * byte.MaxValue / short.MaxValue;
|
||||
Set(report, (byte)result);
|
||||
}
|
||||
}
|
||||
|
||||
public struct DualShock4Sensor
|
||||
{
|
||||
public int Offset { get; }
|
||||
public bool Invert { get; }
|
||||
|
||||
public DualShock4Sensor(int offset, bool invert)
|
||||
{
|
||||
Offset = offset;
|
||||
Invert = invert;
|
||||
}
|
||||
|
||||
internal void Set(byte[] report, short value)
|
||||
{
|
||||
if (Invert)
|
||||
value = (short)Math.Clamp(-value, short.MinValue, short.MaxValue);
|
||||
|
||||
BitConverter.GetBytes(value).CopyTo(report, Offset);
|
||||
}
|
||||
|
||||
internal void Set(byte[] report, ushort value)
|
||||
{
|
||||
BitConverter.GetBytes(value).CopyTo(report, Offset);
|
||||
}
|
||||
}
|
||||
|
||||
public struct DualShock4Finger
|
||||
{
|
||||
public const int MaxX = 1920;
|
||||
public const int MaxY = 942;
|
||||
|
||||
public int Index { get; }
|
||||
private int Offset { get => 34 + Index * 4; }
|
||||
|
||||
public DualShock4Finger(int index)
|
||||
{
|
||||
Index = index;
|
||||
}
|
||||
|
||||
internal void Set(byte[] report, Point? data)
|
||||
{
|
||||
uint currentValue = BitConverter.ToUInt32(report, Offset);
|
||||
|
||||
// copy report ID
|
||||
uint calculatedValue = (byte)(((currentValue & 0x7F) + 1) & 0x7F);
|
||||
|
||||
if (data.HasValue)
|
||||
{
|
||||
// store coordinates into report
|
||||
int x = Math.Clamp(data.Value.X, 0, MaxX);
|
||||
int y = Math.Clamp(data.Value.Y, 0, MaxY);
|
||||
calculatedValue |= (uint)((x & 0x7FF) << 8);
|
||||
calculatedValue |= (uint)((y & 0x7FF) << 20);
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy existing coordinates
|
||||
calculatedValue |= 0x80;
|
||||
calculatedValue |= (uint)(currentValue & 0xFFFFFF00);
|
||||
}
|
||||
|
||||
// compare position and key status
|
||||
if ((currentValue & 0xFFFFFF80) == (calculatedValue & 0xFFFFFF80))
|
||||
return;
|
||||
|
||||
// increment packet number
|
||||
report[33] = (byte)(report[33] + 1);
|
||||
BitConverter.GetBytes(calculatedValue).CopyTo(report, Offset);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly static DualShock4Axis LeftThumbX = new DualShock4Axis(0, false);
|
||||
public readonly static DualShock4Axis LeftThumbY = new DualShock4Axis(1, true);
|
||||
public readonly static DualShock4Axis RightThumbX = new DualShock4Axis(2, false);
|
||||
public readonly static DualShock4Axis RightThumbY = new DualShock4Axis(3, true);
|
||||
|
||||
public static DualShock4Slider LeftTrigger = new DualShock4Slider(7);
|
||||
public static DualShock4Slider RightTrigger = new DualShock4Slider(8);
|
||||
|
||||
public static DualShock4Button ThumbRight = new DualShock4Button(5, 7);
|
||||
public static DualShock4Button ThumbLeft = new DualShock4Button(5, 6);
|
||||
public static DualShock4Button Options = new DualShock4Button(5, 5);
|
||||
public static DualShock4Button Share = new DualShock4Button(5, 4);
|
||||
public static DualShock4Button TriggerRight = new DualShock4Button(5, 3);
|
||||
public static DualShock4Button TriggerLeft = new DualShock4Button(5, 2);
|
||||
public static DualShock4Button ShoulderRight = new DualShock4Button(5, 1);
|
||||
public static DualShock4Button ShoulderLeft = new DualShock4Button(5, 0);
|
||||
public static DualShock4Button Triangle = new DualShock4Button(4, 7);
|
||||
public static DualShock4Button Circle = new DualShock4Button(4, 6);
|
||||
public static DualShock4Button Cross = new DualShock4Button(4, 5);
|
||||
public static DualShock4Button Square = new DualShock4Button(4, 4);
|
||||
|
||||
public static DualShock4Button TPadClick = new DualShock4Button(6, 1);
|
||||
public static DualShock4Button PS = new DualShock4Button(6, 0);
|
||||
|
||||
private static DualShock4Sensor Timestamp = new DualShock4Sensor(9, false);
|
||||
private static DualShock4Slider BatteryLevel = new DualShock4Slider(11);
|
||||
private static DualShock4Slider Counter = new DualShock4Slider(6);
|
||||
|
||||
public static DualShock4Sensor GyroX = new DualShock4Sensor(12, false);
|
||||
public static DualShock4Sensor GyroY = new DualShock4Sensor(14, true);
|
||||
public static DualShock4Sensor GyroZ = new DualShock4Sensor(16, false);
|
||||
public static DualShock4Sensor AccelX = new DualShock4Sensor(18, false);
|
||||
public static DualShock4Sensor AccelY = new DualShock4Sensor(20, true);
|
||||
public static DualShock4Sensor AccelZ = new DualShock4Sensor(22, false);
|
||||
|
||||
public static DualShock4DPadDirection DPadReleased = new DualShock4DPadDirection(4, 8, 15);
|
||||
public static DualShock4DPadDirection DPadNorthwest = new DualShock4DPadDirection(4, 7, 15);
|
||||
public static DualShock4DPadDirection DPadWest = new DualShock4DPadDirection(4, 6, 15);
|
||||
public static DualShock4DPadDirection DPadSouthwest = new DualShock4DPadDirection(4, 5, 15);
|
||||
public static DualShock4DPadDirection DPadSouth = new DualShock4DPadDirection(4, 4, 15);
|
||||
public static DualShock4DPadDirection DPadSoutheast = new DualShock4DPadDirection(4, 3, 15);
|
||||
public static DualShock4DPadDirection DPadEast = new DualShock4DPadDirection(4, 2, 15);
|
||||
public static DualShock4DPadDirection DPadNortheast = new DualShock4DPadDirection(4, 1, 15);
|
||||
public static DualShock4DPadDirection DPadNorth = new DualShock4DPadDirection(4, 0, 15);
|
||||
|
||||
public static DualShock4Finger LeftFinger = new DualShock4Finger(0);
|
||||
public static DualShock4Finger RightFinger = new DualShock4Finger(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ namespace SteamController.Managers
|
|||
{
|
||||
context.State.SteamUsesSteamInput = false;
|
||||
context.State.SteamUsesX360Controller = false;
|
||||
context.State.SteamUsesDS4Controller = false;
|
||||
lastState = null;
|
||||
stateChanged = 0;
|
||||
return;
|
||||
|
|
@ -58,11 +59,17 @@ namespace SteamController.Managers
|
|||
Devices.Xbox360Controller.VendorID,
|
||||
Devices.Xbox360Controller.ProductID
|
||||
) != true;
|
||||
|
||||
context.State.SteamUsesDS4Controller = Helpers.SteamConfiguration.IsControllerBlacklisted(
|
||||
Devices.DS4Controller.VendorID,
|
||||
Devices.DS4Controller.ProductID
|
||||
) != true;
|
||||
}
|
||||
else
|
||||
{
|
||||
context.State.SteamUsesSteamInput = false;
|
||||
context.State.SteamUsesX360Controller = false;
|
||||
context.State.SteamUsesDS4Controller = false;
|
||||
}
|
||||
|
||||
lastState = usesController;
|
||||
|
|
@ -70,11 +77,12 @@ namespace SteamController.Managers
|
|||
|
||||
#if DEBUG
|
||||
CommonHelpers.Log.TraceLine(
|
||||
"SteamManager: uses={0}, isRunning={1}, usesSteamInput={2}, usesX360={3}",
|
||||
"SteamManager: uses={0}, isRunning={1}, usesSteamInput={2}, usesX360={3}, usesDS4={4}",
|
||||
usesController,
|
||||
SteamConfiguration.IsRunning,
|
||||
context.State.SteamUsesSteamInput,
|
||||
context.State.SteamUsesX360Controller
|
||||
context.State.SteamUsesX360Controller,
|
||||
context.State.SteamUsesDS4Controller
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
29
SteamController/Profiles/Predefined/DS4HapticProfile.cs
Normal file
29
SteamController/Profiles/Predefined/DS4HapticProfile.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using SteamController.ProfilesSettings;
|
||||
using HapticPad = SteamController.Devices.SteamController.HapticPad;
|
||||
|
||||
namespace SteamController.Profiles.Predefined
|
||||
{
|
||||
public class DS4HapticProfile : DS4Profile
|
||||
{
|
||||
private ProfilesSettings.HapticSettings HapticSettings
|
||||
{
|
||||
get { return ProfilesSettings.HapticSettings.DS4; }
|
||||
}
|
||||
|
||||
public override Status Run(Context context)
|
||||
{
|
||||
if (base.Run(context).IsDone)
|
||||
return Status.Done;
|
||||
|
||||
if (HapticSettings.GetHapticIntensity(context.DS4.FeedbackLargeMotor, HapticSettings.LeftIntensity, out var leftIntensity))
|
||||
context.Steam.SendHaptic(HapticPad.Right, HapticSettings.HapticStyle, leftIntensity);
|
||||
|
||||
if (HapticSettings.GetHapticIntensity(context.DS4.FeedbackSmallMotor, HapticSettings.RightIntensity, out var rightIntensity))
|
||||
context.Steam.SendHaptic(HapticPad.Left, HapticSettings.HapticStyle, rightIntensity);
|
||||
|
||||
context.DS4.ResetFeedback();
|
||||
|
||||
return Status.Continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
106
SteamController/Profiles/Predefined/DS4Profile.cs
Normal file
106
SteamController/Profiles/Predefined/DS4Profile.cs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
using Nefarius.ViGEm.Client.Targets.Xbox360;
|
||||
using SteamController.Devices;
|
||||
using SteamController.ProfilesSettings;
|
||||
|
||||
namespace SteamController.Profiles.Predefined
|
||||
{
|
||||
public class DS4Profile : Default.BackPanelShortcutsProfile
|
||||
{
|
||||
public override bool Selected(Context context)
|
||||
{
|
||||
return context.Enabled && context.DS4.Valid && context.KeyboardMouseValid && !context.State.SteamUsesSteamInput;
|
||||
}
|
||||
|
||||
internal override ProfilesSettings.BackPanelSettings BackPanelSettings
|
||||
{
|
||||
get { return ProfilesSettings.DS4BackPanelSettings.Default; }
|
||||
}
|
||||
|
||||
public override Status Run(Context context)
|
||||
{
|
||||
context.Steam.LizardButtons = false;
|
||||
context.Steam.LizardMouse = false;
|
||||
context.DS4.Connected = true;
|
||||
|
||||
// Controls
|
||||
context.DS4.Overwrite(DS4Controller.PS, context.Steam.BtnSteam.Pressed(), 100);
|
||||
context.DS4[DS4Controller.Share] = context.Steam.BtnMenu;
|
||||
context.DS4[DS4Controller.Options] = context.Steam.BtnOptions;
|
||||
|
||||
if (base.Run(context).IsDone)
|
||||
{
|
||||
return Status.Done;
|
||||
}
|
||||
|
||||
// DPad
|
||||
if (context.Steam.BtnDpadUp && context.Steam.BtnDpadLeft)
|
||||
context.DS4[DS4Controller.DPadNorthwest] = true;
|
||||
else if (context.Steam.BtnDpadUp && context.Steam.BtnDpadRight)
|
||||
context.DS4[DS4Controller.DPadNortheast] = true;
|
||||
else if (context.Steam.BtnDpadDown && context.Steam.BtnDpadLeft)
|
||||
context.DS4[DS4Controller.DPadSouthwest] = true;
|
||||
else if (context.Steam.BtnDpadDown && context.Steam.BtnDpadRight)
|
||||
context.DS4[DS4Controller.DPadSoutheast] = true;
|
||||
else if (context.Steam.BtnDpadUp)
|
||||
context.DS4[DS4Controller.DPadNorth] = true;
|
||||
else if (context.Steam.BtnDpadDown)
|
||||
context.DS4[DS4Controller.DPadSouth] = true;
|
||||
else if (context.Steam.BtnDpadLeft)
|
||||
context.DS4[DS4Controller.DPadWest] = true;
|
||||
else if (context.Steam.BtnDpadRight)
|
||||
context.DS4[DS4Controller.DPadEast] = true;
|
||||
|
||||
// Buttons
|
||||
context.DS4[DS4Controller.Cross] = context.Steam.BtnA;
|
||||
context.DS4[DS4Controller.Circle] = context.Steam.BtnB;
|
||||
context.DS4[DS4Controller.Square] = context.Steam.BtnX;
|
||||
context.DS4[DS4Controller.Triangle] = context.Steam.BtnY;
|
||||
|
||||
// Sticks
|
||||
context.DS4[DS4Controller.LeftThumbX] = context.Steam.LeftThumbX;
|
||||
context.DS4[DS4Controller.LeftThumbY] = context.Steam.LeftThumbY;
|
||||
context.DS4[DS4Controller.RightThumbX] = context.Steam.RightThumbX;
|
||||
context.DS4[DS4Controller.RightThumbY] = context.Steam.RightThumbY;
|
||||
context.DS4[DS4Controller.ThumbLeft] = context.Steam.BtnLeftStickPress;
|
||||
context.DS4[DS4Controller.ThumbRight] = context.Steam.BtnRightStickPress;
|
||||
|
||||
// Triggers
|
||||
context.DS4[DS4Controller.LeftTrigger] = context.Steam.LeftTrigger;
|
||||
context.DS4[DS4Controller.RightTrigger] = context.Steam.RightTrigger;
|
||||
context.DS4[DS4Controller.TriggerLeft] = context.Steam.LeftTrigger > short.MaxValue * 3 / 4;
|
||||
context.DS4[DS4Controller.TriggerRight] = context.Steam.RightTrigger > short.MaxValue * 3 / 4;
|
||||
context.DS4[DS4Controller.ShoulderLeft] = context.Steam.BtnL1;
|
||||
context.DS4[DS4Controller.ShoulderRight] = context.Steam.BtnR1;
|
||||
|
||||
// Accel & Gyro
|
||||
context.DS4[DS4Controller.GyroX] = context.Steam.GyroPitch;
|
||||
context.DS4[DS4Controller.GyroY] = context.Steam.GyroRoll;
|
||||
context.DS4[DS4Controller.GyroZ] = context.Steam.GyroYaw;
|
||||
context.DS4[DS4Controller.AccelX] = context.Steam.AccelX;
|
||||
context.DS4[DS4Controller.AccelY] = context.Steam.AccelY;
|
||||
context.DS4[DS4Controller.AccelZ] = context.Steam.AccelZ;
|
||||
|
||||
// Trackpad
|
||||
context.DS4[DS4Controller.TPadClick] = context.Steam.BtnLPadPress || context.Steam.BtnRPadPress;
|
||||
context.DS4[DS4Controller.LeftFinger] = GetTPadPoint(context.Steam.LPadX, context.Steam.LPadY);
|
||||
context.DS4[DS4Controller.RightFinger] = GetTPadPoint(context.Steam.RPadX, context.Steam.RPadY);
|
||||
|
||||
return Status.Continue;
|
||||
}
|
||||
|
||||
private Point? GetTPadPoint(SteamAxis x, SteamAxis y)
|
||||
{
|
||||
if (x || y)
|
||||
{
|
||||
return new Point(
|
||||
(int)x.GetDeltaValue(0, 1920, DeltaValueMode.Absolute),
|
||||
(int)y.GetDeltaValue(943 + 488, -488, DeltaValueMode.Absolute)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,9 +5,9 @@ namespace SteamController.Profiles.Predefined
|
|||
{
|
||||
public class X360HapticProfile : X360Profile
|
||||
{
|
||||
private ProfilesSettings.X360HapticSettings HapticSettings
|
||||
private ProfilesSettings.HapticSettings HapticSettings
|
||||
{
|
||||
get { return ProfilesSettings.X360HapticSettings.Default; }
|
||||
get { return ProfilesSettings.HapticSettings.X360; }
|
||||
}
|
||||
|
||||
public override Status Run(Context context)
|
||||
|
|
@ -15,26 +15,15 @@ namespace SteamController.Profiles.Predefined
|
|||
if (base.Run(context).IsDone)
|
||||
return Status.Done;
|
||||
|
||||
if (GetHapticIntensity(context.X360.FeedbackLargeMotor, HapticSettings.LeftIntensity, out var leftIntensity))
|
||||
if (HapticSettings.GetHapticIntensity(context.X360.FeedbackLargeMotor, HapticSettings.LeftIntensity, out var leftIntensity))
|
||||
context.Steam.SendHaptic(HapticPad.Right, HapticSettings.HapticStyle, leftIntensity);
|
||||
|
||||
if (GetHapticIntensity(context.X360.FeedbackSmallMotor, HapticSettings.RightIntensity, out var rightIntensity))
|
||||
if (HapticSettings.GetHapticIntensity(context.X360.FeedbackSmallMotor, HapticSettings.RightIntensity, out var rightIntensity))
|
||||
context.Steam.SendHaptic(HapticPad.Left, HapticSettings.HapticStyle, rightIntensity);
|
||||
|
||||
context.X360.ResetFeedback();
|
||||
|
||||
return Status.Continue;
|
||||
}
|
||||
|
||||
private bool GetHapticIntensity(byte? input, sbyte maxIntensity, out sbyte output)
|
||||
{
|
||||
output = default;
|
||||
if (input is null || input.Value == 0)
|
||||
return false;
|
||||
|
||||
int value = X360HapticSettings.MinIntensity + (maxIntensity - X360HapticSettings.MinIntensity) * input.Value / 255;
|
||||
output = (sbyte)value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
SteamController/ProfilesSettings/DS4BackPanelSettings.cs
Normal file
16
SteamController/ProfilesSettings/DS4BackPanelSettings.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using System.ComponentModel;
|
||||
using System.Configuration;
|
||||
|
||||
namespace SteamController.ProfilesSettings
|
||||
{
|
||||
internal class DS4BackPanelSettings : BackPanelSettings
|
||||
{
|
||||
private const String MappingsDescription = @"Shortcuts are to be changed in future release.";
|
||||
|
||||
public static DS4BackPanelSettings Default { get; } = new DS4BackPanelSettings();
|
||||
|
||||
public DS4BackPanelSettings() : base("DS4BackPanelSettings")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,17 +4,29 @@ using System.Configuration;
|
|||
namespace SteamController.ProfilesSettings
|
||||
{
|
||||
[Category("Settings")]
|
||||
internal sealed class X360HapticSettings : CommonHelpers.BaseSettings
|
||||
internal sealed class HapticSettings : CommonHelpers.BaseSettings
|
||||
{
|
||||
public const sbyte MinIntensity = -2;
|
||||
public const sbyte MaxIntensity = 10;
|
||||
|
||||
public static X360HapticSettings Default = new X360HapticSettings();
|
||||
public static HapticSettings X360 = new HapticSettings("X360HapticSettings");
|
||||
public static HapticSettings DS4 = new HapticSettings("DS4HapticSettings");
|
||||
|
||||
public X360HapticSettings() : base("X360HapticSettings")
|
||||
public HapticSettings(string name) : base(name)
|
||||
{
|
||||
}
|
||||
|
||||
public static bool GetHapticIntensity(byte? input, sbyte maxIntensity, out sbyte output)
|
||||
{
|
||||
output = default;
|
||||
if (input is null || input.Value == 0)
|
||||
return false;
|
||||
|
||||
int value = MinIntensity + (maxIntensity - MinIntensity) * input.Value / 255;
|
||||
output = (sbyte)value;
|
||||
return true;
|
||||
}
|
||||
|
||||
public Devices.SteamController.HapticStyle HapticStyle
|
||||
{
|
||||
get { return Get<Devices.SteamController.HapticStyle>("HapticStyle", Devices.SteamController.HapticStyle.Weak); }
|
||||
|
|
|
|||
Loading…
Reference in a new issue