Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions DS4Windows/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,19 @@ private void CheckOptions(ArgumentParser parser)
exitApp = true;
Current.Shutdown();
}
else if (parser.Validatedriver)
{
// Read-only smoke test of the Native Mode driver-validation
// gate. Runs before the ControlService or any window exists, so
// no controller is opened or released, no elevation is
// requested, and no attach can occur. Exit code: 0 pass,
// non-zero fail.
int validationExitCode =
DS4Windows.NativeModeDriverValidationCommand.Run();
runShutdown = false;
exitApp = true;
Current.Shutdown(validationExitCode);
}
else if (parser.ReenableDevice)
{
DS4Windows.DS4Devices.reEnableDevice(parser.DeviceInstanceId);
Expand Down Expand Up @@ -748,6 +761,9 @@ private void CleanShutdown()
{
if (rootHub != null)
{
// Application exit must synchronously tear down the child;
// otherwise usbip-win2 keeps the virtual pad attached.
rootHub.StopNativeModeForShutdown();
Task.Run(() =>
{
if (rootHub.running)
Expand Down
9 changes: 9 additions & 0 deletions DS4Windows/ArgumentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class ArgumentParser
private bool mini;
private bool stop;
private bool driverinstall;
private bool validatedriver;
private bool reenableDevice;
private string deviceInstanceId;
private bool runtask;
Expand All @@ -39,6 +40,7 @@ public class ArgumentParser
public bool Mini { get => mini; }
public bool Stop { get => stop; }
public bool Driverinstall { get => driverinstall; }
public bool Validatedriver { get => validatedriver; }
public bool ReenableDevice { get => reenableDevice; }
public bool Runtask { get => runtask; }
public bool Command { get => command; }
Expand All @@ -63,6 +65,13 @@ public void Parse(string[] args)
driverinstall = true;
break;

// Read-only Native Mode driver-validation smoke test. Prints
// a diagnostic report and exits without starting Native Mode.
case "validatedriver":
case "-validatedriver":
validatedriver = true;
break;

case "re-enabledevice":
case "-re-enabledevice":
reenableDevice = true;
Expand Down
842 changes: 839 additions & 3 deletions DS4Windows/DS4Control/ControlService.cs

Large diffs are not rendered by default.

165 changes: 165 additions & 0 deletions DS4Windows/DS4Control/ControlServiceDeviceOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ public bool Enabled

public class DualSenseControllerOptions : ControllerOptionsStore
{
public const int DEFAULT_NATIVE_MODE_SPEAKER_VOLUME = 50;

public const string XML_ELEMENT_NAME = "DualSenseSupportSettings";

public enum LEDBarMode : ushort
Expand All @@ -229,6 +231,28 @@ public enum MuteLEDMode : ushort
Pulse
}

public enum HapticsMode : ushort
{
Off,
SystemAudio,
RumbleToHaptics,
Mix,
}

public enum AudioOutputRoute : ushort
{
Auto, // headphone jack when plugged in, speaker otherwise
Headphone,
Speaker,
}

public enum AudioLatencyMode : ushort
{
Smooth, // maximum buffering; survives congested links
Balanced,
LowLatency, // minimum buffering; needs a clean link
}

private LEDBarMode ledMode = LEDBarMode.MultipleControllers;
public LEDBarMode LedMode
{
Expand All @@ -255,6 +279,147 @@ public MuteLEDMode MuteLedMode
}
public event EventHandler MuteLedModeChanged;

// Bluetooth audio-haptics streaming settings. Fires a single combined
// change event; the device applies live-safe fields or restarts the
// capture pipeline as needed.
private HapticsMode btHapticsMode = HapticsMode.Off;
public HapticsMode BTHapticsMode
{
get => btHapticsMode;
set
{
if (btHapticsMode == value) return;
btHapticsMode = value;
BTHapticsOptionChanged?.Invoke(this, EventArgs.Empty);
}
}

private double btHapticsGain = 3.0;
public double BTHapticsGain
{
get => btHapticsGain;
set
{
value = Math.Clamp(value, 0.1, 10.0);
if (btHapticsGain == value) return;
btHapticsGain = value;
BTHapticsOptionChanged?.Invoke(this, EventArgs.Empty);
}
}

private int btHapticsLowPassHz = 350;
public int BTHapticsLowPassHz
{
get => btHapticsLowPassHz;
set
{
value = Math.Clamp(value, 40, 1000);
if (btHapticsLowPassHz == value) return;
btHapticsLowPassHz = value;
BTHapticsOptionChanged?.Invoke(this, EventArgs.Empty);
}
}

private string btHapticsAudioDeviceId = string.Empty;
public string BTHapticsAudioDeviceId
{
get => btHapticsAudioDeviceId;
set
{
value ??= string.Empty;
if (btHapticsAudioDeviceId == value) return;
btHapticsAudioDeviceId = value;
BTHapticsOptionChanged?.Invoke(this, EventArgs.Empty);
}
}

// Bluetooth listening-audio (headphone jack / speaker) settings.
private bool btAudioEnabled = false;
public bool BTAudioEnabled
{
get => btAudioEnabled;
set
{
if (btAudioEnabled == value) return;
btAudioEnabled = value;
BTHapticsOptionChanged?.Invoke(this, EventArgs.Empty);
}
}

private AudioOutputRoute btAudioRoute = AudioOutputRoute.Auto;
public AudioOutputRoute BTAudioRoute
{
get => btAudioRoute;
set
{
if (btAudioRoute == value) return;
btAudioRoute = value;
BTHapticsOptionChanged?.Invoke(this, EventArgs.Empty);
}
}

private int btAudioVolume = 50;
public int BTAudioVolume
{
get => btAudioVolume;
set
{
value = Math.Clamp(value, 0, 100);
if (btAudioVolume == value) return;
btAudioVolume = value;
BTHapticsOptionChanged?.Invoke(this, EventArgs.Empty);
}
}

private AudioLatencyMode btAudioLatency = AudioLatencyMode.Smooth;
public AudioLatencyMode BTAudioLatency
{
get => btAudioLatency;
set
{
if (btAudioLatency == value) return;
btAudioLatency = value;
BTHapticsOptionChanged?.Invoke(this, EventArgs.Empty);
}
}

public event EventHandler BTHapticsOptionChanged;

private bool nativeModeSpeakerAudio = true;
public bool NativeModeSpeakerAudio
{
get => nativeModeSpeakerAudio;
set
{
if (nativeModeSpeakerAudio == value) return;
nativeModeSpeakerAudio = value;
}
}

private int nativeModeSpeakerVolume =
DEFAULT_NATIVE_MODE_SPEAKER_VOLUME;
public int NativeModeSpeakerVolume
{
get => nativeModeSpeakerVolume;
set
{
value = Math.Clamp(value, 0, 100);
if (nativeModeSpeakerVolume == value) return;
nativeModeSpeakerVolume = value;
}
}

private AudioOutputRoute nativeModeRoute = AudioOutputRoute.Auto;
public AudioOutputRoute NativeModeRoute
{
get => nativeModeRoute;
set
{
if (nativeModeRoute == value) return;
nativeModeRoute = value;
}
}

public DualSenseControllerOptions(InputDeviceType deviceType) :
base(deviceType)
{
Expand Down
10 changes: 10 additions & 0 deletions DS4Windows/DS4Control/DTOXml/AppSettingsDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,12 @@ public string CustomSteamFolder
get; set;
} = string.Empty;

[XmlElement("UsbipExePath")]
public string UsbipExePath
{
get; set;
} = BackingStore.DEFAULT_USBIP_EXE_PATH;

[XmlIgnore]
public bool AutoProfileRevertDefaultProfile
{
Expand Down Expand Up @@ -876,6 +882,7 @@ public void MapFrom(BackingStore source)
};
UseCustomSteamFolder = source.useCustomSteamFolder;
CustomSteamFolder = source.customSteamFolder;
UsbipExePath = source.usbipExePath;
AutoProfileRevertDefaultProfile = source.autoProfileRevertDefaultProfile;
AutoProfileSwitchNotifyChoice = source.autoProfileSwitchNotifyChoice;
AbsRegionDisplay = source.absDisplayEDID;
Expand Down Expand Up @@ -980,6 +987,9 @@ public void MapTo(BackingStore destination)

destination.useCustomSteamFolder = UseCustomSteamFolder;
destination.customSteamFolder = CustomSteamFolder;
destination.usbipExePath = string.IsNullOrWhiteSpace(UsbipExePath)
? BackingStore.DEFAULT_USBIP_EXE_PATH
: UsbipExePath.Trim();
destination.autoProfileRevertDefaultProfile = AutoProfileRevertDefaultProfile;
destination.autoProfileSwitchNotifyChoice = AutoProfileSwitchNotifyChoice;
if (!string.IsNullOrEmpty(AbsRegionDisplay))
Expand Down
88 changes: 88 additions & 0 deletions DS4Windows/DS4Control/DTOXml/DualSenseControllerOptsDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,104 @@ public MuteLEDMode MuteLedMode
get; set;
}

[XmlElement("BTHapticsMode")]
public HapticsMode BTHapticsMode
{
get; set;
} = HapticsMode.Off;

[XmlElement("BTHapticsGain")]
public double BTHapticsGain
{
get; set;
} = 3.0;

[XmlElement("BTHapticsLowPassHz")]
public int BTHapticsLowPassHz
{
get; set;
} = 350;

[XmlElement("BTHapticsAudioDeviceId")]
public string BTHapticsAudioDeviceId
{
get; set;
} = string.Empty;

[XmlElement("BTAudioEnabled")]
public bool BTAudioEnabled
{
get; set;
} = false;

[XmlElement("BTAudioRoute")]
public AudioOutputRoute BTAudioRoute
{
get; set;
} = AudioOutputRoute.Auto;

[XmlElement("BTAudioVolume")]
public int BTAudioVolume
{
get; set;
} = 50;

[XmlElement("BTAudioLatency")]
public AudioLatencyMode BTAudioLatency
{
get; set;
} = AudioLatencyMode.Smooth;

[XmlElement("NativeModeSpeakerAudio")]
public bool NativeModeSpeakerAudio
{
get; set;
} = true;

[XmlElement("NativeModeSpeakerVolume")]
public int NativeModeSpeakerVolume
{
get; set;
} = DualSenseControllerOptions.DEFAULT_NATIVE_MODE_SPEAKER_VOLUME;

[XmlElement("NativeModeRoute")]
public AudioOutputRoute NativeModeRoute
{
get; set;
} = AudioOutputRoute.Auto;

public void MapFrom(DualSenseControllerOptions source)
{
LEDMode = source.LedMode;
MuteLedMode = source.MuteLedMode;
BTHapticsMode = source.BTHapticsMode;
BTHapticsGain = source.BTHapticsGain;
BTHapticsLowPassHz = source.BTHapticsLowPassHz;
BTHapticsAudioDeviceId = source.BTHapticsAudioDeviceId;
BTAudioEnabled = source.BTAudioEnabled;
BTAudioRoute = source.BTAudioRoute;
BTAudioVolume = source.BTAudioVolume;
BTAudioLatency = source.BTAudioLatency;
NativeModeSpeakerAudio = source.NativeModeSpeakerAudio;
NativeModeSpeakerVolume = source.NativeModeSpeakerVolume;
NativeModeRoute = source.NativeModeRoute;
}

public void MapTo(DualSenseControllerOptions destination)
{
destination.LedMode = LEDMode;
destination.MuteLedMode = MuteLedMode;
destination.BTHapticsMode = BTHapticsMode;
destination.BTHapticsGain = BTHapticsGain;
destination.BTHapticsLowPassHz = BTHapticsLowPassHz;
destination.BTHapticsAudioDeviceId = BTHapticsAudioDeviceId;
destination.BTAudioEnabled = BTAudioEnabled;
destination.BTAudioRoute = BTAudioRoute;
destination.BTAudioVolume = BTAudioVolume;
destination.BTAudioLatency = BTAudioLatency;
destination.NativeModeSpeakerAudio = NativeModeSpeakerAudio;
destination.NativeModeSpeakerVolume = NativeModeSpeakerVolume;
destination.NativeModeRoute = NativeModeRoute;
}
}
}
Loading