From 746587ebbd720dc8d26ff76b48cd993ea23c0c4b Mon Sep 17 00:00:00 2001 From: mika-n Date: Sat, 6 Apr 2019 15:04:21 +0300 Subject: [PATCH 1/3] Brute force timer fix to the "stuck rumble motor" bug in ViGem virtual gamepad driver. This rumble autostop timer should be removed when a signed ViGem driver has a fix to this issue. Existing autostop timer is 2 secs, so this assumes that game keeps on updating a rumble values at least once in 2 secs. If rumble-0 event is lost or game doesn't send a new rumble value within 2 secs then rumble motor is automagically stopped (=assumed it is stuck). Usually games keep on sending rumble values as long an effect needs it, so this autostop timer works reasonable good in most games. --- DS4Windows/DS4Control/ControlService.cs | 12 ++++++------ DS4Windows/DS4Control/ScpUtil.cs | 4 ++-- DS4Windows/DS4Library/DS4Device.cs | 14 ++++++++++++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/DS4Windows/DS4Control/ControlService.cs b/DS4Windows/DS4Control/ControlService.cs index b6aeeb1..fd5f350 100644 --- a/DS4Windows/DS4Control/ControlService.cs +++ b/DS4Windows/DS4Control/ControlService.cs @@ -426,11 +426,11 @@ namespace DS4Windows useDInputOnly[i] = false; x360controls[i] = new Xbox360Controller(vigemTestClient); int devIndex = i; - /*x360controls[i].FeedbackReceived += (sender, args) => + x360controls[i].FeedbackReceived += (sender, args) => { SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex); }; - */ + x360controls[i].Connect(); LogDebug("X360 Controller #" + (i + 1) + " connected"); } @@ -683,11 +683,11 @@ namespace DS4Windows useDInputOnly[Index] = false; x360controls[Index] = new Xbox360Controller(vigemTestClient); int devIndex = Index; - /*x360controls[Index].FeedbackReceived += (sender, args) => + x360controls[Index].FeedbackReceived += (sender, args) => { SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex); }; - */ + x360controls[Index].Connect(); LogDebug("X360 Controller #" + (Index + 1) + " connected"); } @@ -1060,11 +1060,11 @@ namespace DS4Windows { LogDebug("Plugging in X360 Controller #" + (ind + 1)); x360controls[ind] = new Xbox360Controller(vigemTestClient); - /*x360controls[ind].FeedbackReceived += (eventsender, args) => + x360controls[ind].FeedbackReceived += (eventsender, args) => { SetDevRumble(device, args.LargeMotor, args.SmallMotor, ind); }; - */ + x360controls[ind].Connect(); useDInputOnly[ind] = false; LogDebug("X360 Controller #" + (ind + 1) + " connected"); diff --git a/DS4Windows/DS4Control/ScpUtil.cs b/DS4Windows/DS4Control/ScpUtil.cs index 048eb76..0519925 100644 --- a/DS4Windows/DS4Control/ScpUtil.cs +++ b/DS4Windows/DS4Control/ScpUtil.cs @@ -3064,11 +3064,11 @@ namespace DS4Windows if (xinputStatus && xinputPlug) { control.x360controls[device] = new Nefarius.ViGEm.Client.Targets.Xbox360Controller(control.vigemTestClient); - /*control.x360controls[device].FeedbackReceived += (eventsender, args) => + control.x360controls[device].FeedbackReceived += (eventsender, args) => { control.SetDevRumble(tempDev, args.LargeMotor, args.SmallMotor, device); }; - */ + control.x360controls[device].Connect(); Global.useDInputOnly[device] = false; AppLogger.LogToGui("X360 Controller #" + (device + 1) + " connected", false); diff --git a/DS4Windows/DS4Library/DS4Device.cs b/DS4Windows/DS4Library/DS4Device.cs index e78bb6c..a3c0765 100644 --- a/DS4Windows/DS4Library/DS4Device.cs +++ b/DS4Windows/DS4Library/DS4Device.cs @@ -611,6 +611,8 @@ namespace DS4Windows } } + private readonly Stopwatch rumbleAutostopTimer = new Stopwatch(); // Autostop timer to stop rumble motors if those are stuck in a rumble state + private byte outputPendCount = 0; private readonly Stopwatch standbySw = new Stopwatch(); private unsafe void performDs4Output() @@ -1210,6 +1212,13 @@ namespace DS4Windows } } + if (rumbleAutostopTimer.IsRunning) + { + // Workaround to a bug in ViGem driver. Force stop potentially stuck rumble motor on the next output report if there haven't been new rumble events within X seconds + if (rumbleAutostopTimer.ElapsedMilliseconds >= 2000L) + setRumble(0, 0); + } + if (synchronous) { if (output || haptime) @@ -1399,6 +1408,11 @@ namespace DS4Windows testRumble.RumbleMotorStrengthRightLightFast = rightLightFastMotor; testRumble.RumbleMotorStrengthLeftHeavySlow = leftHeavySlowMotor; testRumble.RumbleMotorsExplicitlyOff = rightLightFastMotor == 0 && leftHeavySlowMotor == 0; + + if (testRumble.RumbleMotorsExplicitlyOff) + rumbleAutostopTimer.Reset(); // Stop an autostop timer because ViGem driver sent properly a zero rumble notification + else + rumbleAutostopTimer.Restart(); // Start an autostop timer to stop potentially stuck rumble motor because of lost rumble notification events from ViGem driver } private void MergeStates() From 1e82db0f9e600e1d0a76ad058a9260d226ffaae4 Mon Sep 17 00:00:00 2001 From: mika-n Date: Sun, 7 Apr 2019 14:33:42 +0300 Subject: [PATCH 2/3] Allow LoadProfile action key to be used without unload trigger in a loadProfile special action definition and without temp profile lockdown. This way loadProfile special actions can be linked to load unlimited number of profiles without unloading (=untrigger) the current temp profile first. --- DS4Windows/DS4Control/Mapping.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/DS4Windows/DS4Control/Mapping.cs b/DS4Windows/DS4Control/Mapping.cs index 5632ba4..2e5253f 100644 --- a/DS4Windows/DS4Control/Mapping.cs +++ b/DS4Windows/DS4Control/Mapping.cs @@ -1896,11 +1896,15 @@ namespace DS4Windows { actionFound = true; - if (!actionDone[index].dev[device] && !useTempProfile[device]) + if (!actionDone[index].dev[device] && (!useTempProfile[device] || untriggeraction[device] == null || untriggeraction[device].typeID != SpecialAction.ActionTypeId.Profile) ) { actionDone[index].dev[device] = true; - untriggeraction[device] = action; - untriggerindex[device] = index; + // If Loadprofile special action doesn't have unload trigger then don't set untrigger status. This way the new loaded profile allows yet another loadProfile action key events) + if (action.uTrigger.Count > 0) + { + untriggeraction[device] = action; + untriggerindex[device] = index; + } //foreach (DS4Controls dc in action.trigger) for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) { From 88e3e0e3a2716b17f3b21eaafdf6523afdeac4aa Mon Sep 17 00:00:00 2001 From: mika-n Date: Sun, 7 Apr 2019 16:01:41 +0300 Subject: [PATCH 3/3] Allow saving and editing loadProfile special action without unload trigger definition (ie. don't crash when editing specAction without unload trigger and allow saving an empty list of unload trigger keys for loadProfile action type). --- DS4Windows/DS4Forms/SpecActions.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/DS4Windows/DS4Forms/SpecActions.cs b/DS4Windows/DS4Forms/SpecActions.cs index da4c6f4..c9ce18d 100644 --- a/DS4Windows/DS4Forms/SpecActions.cs +++ b/DS4Windows/DS4Forms/SpecActions.cs @@ -92,13 +92,16 @@ namespace DS4Windows case "Profile": cBActions.SelectedIndex = 3; cBProfiles.Text = act.details; - foreach (string s in act.ucontrols.Split('/')) - foreach (ListViewItem lvi in lVUnloadTrigger.Items) - if (lvi.Text == s) - { - lvi.Checked = true; - break; - } + if (act.ucontrols != null) + { + foreach (string s in act.ucontrols.Split('/')) + foreach (ListViewItem lvi in lVUnloadTrigger.Items) + if (lvi.Text == s) + { + lvi.Checked = true; + break; + } + } break; case "Key": cBActions.SelectedIndex = 4; @@ -261,7 +264,7 @@ namespace DS4Windows } break; case 3: - if (cBProfiles.SelectedIndex > 0 && ucontrols.Count > 0) + if (cBProfiles.SelectedIndex > 0 /*&& ucontrols.Count > 0*/) { action = Properties.Resources.LoadProfile.Replace("*profile*", cBProfiles.Text); actRe = true;