Skip to content

Instantly share code, notes, and snippets.

@Ryochan7
Last active March 4, 2021 00:20
Show Gist options
  • Select an option

  • Save Ryochan7/736cbd9c488e6b6409fa4757b08f0aaf to your computer and use it in GitHub Desktop.

Select an option

Save Ryochan7/736cbd9c488e6b6409fa4757b08f0aaf to your computer and use it in GitHub Desktop.

Revisions

  1. Ryochan7 renamed this gist Mar 4, 2021. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions mergejoycondraft8.diff → mergejoycondraft9.diff
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    diff --git a/DS4Windows/DS4Control/ControlService.cs b/DS4Windows/DS4Control/ControlService.cs
    index a47dfb0b..c9ce0311 100644
    index a47dfb0b..86a9d578 100644
    --- a/DS4Windows/DS4Control/ControlService.cs
    +++ b/DS4Windows/DS4Control/ControlService.cs
    @@ -10,6 +10,7 @@ using System.Windows.Threading;
    @@ -293,7 +293,7 @@ index a47dfb0b..c9ce0311 100644
    + if ((device.DeviceType == InputDevices.InputDeviceType.JoyConL ||
    + device.DeviceType == InputDevices.InputDeviceType.JoyConR) && device.PerformStateMerge)
    + {
    + if (tempPrimaryJoyDev == device &&
    + if (device.PrimaryDevice &&
    + tempSecondaryJoyDev != null)
    + {
    + InputDevices.JoyConDevice currentJoyDev = device as InputDevices.JoyConDevice;
  2. Ryochan7 renamed this gist Mar 4, 2021. 1 changed file with 16 additions and 2 deletions.
    18 changes: 16 additions & 2 deletions mergejoycondraft7.diff → mergejoycondraft8.diff
    Original file line number Diff line number Diff line change
    @@ -819,7 +819,7 @@ index 484e7312..a00eaac2 100644
    }
    }
    diff --git a/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs b/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    index 92325d77..596d1845 100644
    index 92325d77..ef5bfd56 100644
    --- a/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    +++ b/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    @@ -215,6 +215,37 @@ namespace DS4Windows.InputDevices
    @@ -860,7 +860,7 @@ index 92325d77..596d1845 100644
    public override event ReportHandler<EventArgs> Report = null;
    public override event EventHandler<EventArgs> Removal = null;
    public override event EventHandler BatteryChanged;
    @@ -1288,5 +1319,80 @@ namespace DS4Windows.InputDevices
    @@ -1288,5 +1319,94 @@ namespace DS4Windows.InputDevices
    enableHomeLED = nativeOptionsStore.EnableHomeLED;
    }
    }
    @@ -917,6 +917,13 @@ index 92325d77..596d1845 100644
    + dState.DpadLeft = cState.DpadLeft;
    + dState.DpadRight = cState.DpadRight;
    + dState.Share = cState.Share;
    + if (primaryDevice)
    + {
    + dState.elapsedTime = cState.elapsedTime;
    + dState.totalMicroSec = cState.totalMicroSec;
    + dState.ReportTimeStamp = cState.ReportTimeStamp;
    + }
    +
    + if (outputMapGyro) dState.Motion = cState.Motion;
    + //dState.Motion = cState.Motion;
    + }
    @@ -934,6 +941,13 @@ index 92325d77..596d1845 100644
    + dState.Square = cState.Square;
    + dState.PS = cState.PS;
    + dState.Options = cState.Options;
    + if (primaryDevice)
    + {
    + dState.elapsedTime = cState.elapsedTime;
    + dState.totalMicroSec = cState.totalMicroSec;
    + dState.ReportTimeStamp = cState.ReportTimeStamp;
    + }
    +
    + if (outputMapGyro) dState.Motion = cState.Motion;
    + //dState.Motion = cState.Motion;
    + }
  3. Ryochan7 renamed this gist Mar 3, 2021. 1 changed file with 121 additions and 18 deletions.
    139 changes: 121 additions & 18 deletions mergejoycondraft6.diff → mergejoycondraft7.diff
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    diff --git a/DS4Windows/DS4Control/ControlService.cs b/DS4Windows/DS4Control/ControlService.cs
    index a47dfb0b..b06352cf 100644
    index a47dfb0b..c9ce0311 100644
    --- a/DS4Windows/DS4Control/ControlService.cs
    +++ b/DS4Windows/DS4Control/ControlService.cs
    @@ -10,6 +10,7 @@ using System.Windows.Threading;
    @@ -283,7 +283,7 @@ index a47dfb0b..b06352cf 100644
    for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext() && loopControllers;)
    {
    DS4Device device = devEnum.Current;
    @@ -1328,10 +1462,48 @@ namespace DS4Windows
    @@ -1328,10 +1462,49 @@ namespace DS4Windows

    Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); });
    task.Start();
    @@ -309,7 +309,8 @@ index a47dfb0b..b06352cf 100644
    + tempSecondaryJoyDev = null;
    + tempPrimaryJoyDev = null;
    + }
    + else if (tempPrimaryJoyDev != null)
    + else if (!device.PrimaryDevice &&
    + tempPrimaryJoyDev != null)
    + {
    + InputDevices.JoyConDevice currentJoyDev = device as InputDevices.JoyConDevice;
    + tempPrimaryJoyDev.JointDevice = currentJoyDev;
    @@ -333,7 +334,7 @@ index a47dfb0b..b06352cf 100644
    device.LoadStoreSettings();
    device.CheckControllerNumDeviceSettings(numControllers);

    @@ -1368,7 +1540,28 @@ namespace DS4Windows
    @@ -1368,7 +1541,28 @@ namespace DS4Windows
    if (!getDInputOnly(Index) && device.isSynced())
    {
    //useDInputOnly[Index] = false;
    @@ -363,7 +364,7 @@ index a47dfb0b..b06352cf 100644
    }
    else
    {
    @@ -1376,7 +1569,25 @@ namespace DS4Windows
    @@ -1376,7 +1570,25 @@ namespace DS4Windows
    Global.activeOutDevType[Index] = OutContType.None;
    }

    @@ -390,7 +391,7 @@ index a47dfb0b..b06352cf 100644
    CheckProfileOptions(Index, device);
    }

    @@ -1386,7 +1597,7 @@ namespace DS4Windows
    @@ -1386,7 +1598,7 @@ namespace DS4Windows
    this.On_Report(sender, e, tempIdx);
    };

    @@ -399,7 +400,7 @@ index a47dfb0b..b06352cf 100644
    {
    DS4Device.ReportHandler<EventArgs> tempEvnt = (sender, args) =>
    {
    @@ -1766,6 +1977,15 @@ namespace DS4Windows
    @@ -1766,6 +1978,15 @@ namespace DS4Windows
    {
    UnplugOutDev(ind, device);
    }
    @@ -415,7 +416,7 @@ index a47dfb0b..b06352cf 100644

    // Use Task to reset device synth state and commit it
    Task.Run(() =>
    @@ -1873,8 +2093,18 @@ namespace DS4Windows
    @@ -1873,8 +2094,18 @@ namespace DS4Windows
    }
    }

    @@ -425,7 +426,7 @@ index a47dfb0b..b06352cf 100644
    + if (!device.PerformStateMerge)
    + {
    + cState = CurrentState[ind];
    + device.getCurrentState(cState);
    + device.getRawCurrentState(cState);
    + }
    + else
    + {
    @@ -436,7 +437,7 @@ index a47dfb0b..b06352cf 100644
    DS4State pState = device.getPreviousStateRef();
    //device.getPreviousState(PreviousState[ind]);
    //DS4State pState = PreviousState[ind];
    @@ -1912,6 +2142,12 @@ namespace DS4Windows
    @@ -1912,6 +2143,12 @@ namespace DS4Windows
    // */
    //}

    @@ -449,6 +450,18 @@ index a47dfb0b..b06352cf 100644
    if (getEnableTouchToggle(ind))
    CheckForTouchToggle(ind, cState, pState);

    @@ -1983,6 +2220,11 @@ namespace DS4Windows

    // Update the GUI/whatever.
    DS4LightBar.updateLightBar(device, ind);
    +
    + if (device.PerformStateMerge)
    + {
    + device.PreserveMergedStateData();
    + }
    }
    }

    diff --git a/DS4Windows/DS4Control/DS4OutDevice.cs b/DS4Windows/DS4Control/DS4OutDevice.cs
    index 8dab686a..9b1248a1 100644
    --- a/DS4Windows/DS4Control/DS4OutDevice.cs
    @@ -487,12 +500,15 @@ index ada31e46..154f650e 100644
    }
    }
    diff --git a/DS4Windows/DS4Control/ScpUtil.cs b/DS4Windows/DS4Control/ScpUtil.cs
    index 97380f13..695c0a56 100644
    index 97380f13..c90404f6 100644
    --- a/DS4Windows/DS4Control/ScpUtil.cs
    +++ b/DS4Windows/DS4Control/ScpUtil.cs
    @@ -5083,8 +5083,12 @@ namespace DS4Windows
    @@ -5081,10 +5081,14 @@ namespace DS4Windows

    // Only change xinput devices under certain conditions. Avoid
    // performing this upon program startup before loading devices.
    if (xinputChange)
    - if (xinputChange)
    + if (xinputChange && device < ControlService.CURRENT_DS4_CONTROLLER_LIMIT)
    {
    - CheckOldDevicestatus(device, control, oldContType,
    - out xinputPlug, out xinputStatus);
    @@ -694,20 +710,27 @@ index c8e4163f..42807ed5 100644
    public event CustomColorHandler RequestColorPicker;

    diff --git a/DS4Windows/DS4Library/DS4Device.cs b/DS4Windows/DS4Library/DS4Device.cs
    index 484e7312..5148a30e 100644
    index 484e7312..a00eaac2 100644
    --- a/DS4Windows/DS4Library/DS4Device.cs
    +++ b/DS4Windows/DS4Library/DS4Device.cs
    @@ -578,6 +578,42 @@ namespace DS4Windows
    @@ -578,6 +578,49 @@ namespace DS4Windows
    protected event EventHandler DeviceSlotNumberChanged;
    protected byte deviceSlotMask = 0x00;

    + protected DS4State jointState = new DS4State();
    + protected DS4State jointPreviousState = new DS4State();
    + public DS4State JointState
    + {
    + get => jointState;
    + set => jointState = value;
    + }
    +
    + public DS4State JointPreviousState
    + {
    + get => jointPreviousState;
    + set => jointPreviousState = value;
    + }
    +
    + protected bool performStateMerge;
    + public bool PerformStateMerge
    + {
    @@ -740,7 +763,52 @@ index 484e7312..5148a30e 100644
    public DS4Device(HidDevice hidDevice, string disName, VidPidFeatureSet featureSet = VidPidFeatureSet.DefaultDS4)
    {
    hDevice = hidDevice;
    @@ -2055,5 +2091,9 @@ namespace DS4Windows
    @@ -1904,26 +1947,40 @@ namespace DS4Windows
    return pState.Clone();
    }

    - public void getCurrentState(DS4State state)
    + public void getRawCurrentState(DS4State state)
    {
    cState.CopyTo(state);
    }

    - public void getPreviousState(DS4State state)
    + public void getRawPreviousState(DS4State state)
    {
    pState.CopyTo(state);
    }

    - public DS4State getCurrentStateRef()
    + public virtual DS4State getCurrentStateRef()
    + {
    + return cState;
    + }
    +
    + public virtual DS4State getPreviousStateRef()
    + {
    + return pState;
    + }
    +
    + public DS4State GetRawCurrentStateRef()
    {
    return cState;
    }

    - public DS4State getPreviousStateRef()
    + public DS4State GetRawPreviousStateRef()
    {
    return pState;
    }

    + public virtual void PreserveMergedStateData()
    + {
    + }
    +
    public bool isDS4Idle()
    {
    if (cState.Square || cState.Cross || cState.Circle || cState.Triangle)
    @@ -2055,5 +2112,9 @@ namespace DS4Windows
    PrepareOutputFeaturesByte();
    }
    }
    @@ -751,7 +819,7 @@ index 484e7312..5148a30e 100644
    }
    }
    diff --git a/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs b/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    index 92325d77..1551063e 100644
    index 92325d77..596d1845 100644
    --- a/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    +++ b/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    @@ -215,6 +215,37 @@ namespace DS4Windows.InputDevices
    @@ -792,11 +860,46 @@ index 92325d77..1551063e 100644
    public override event ReportHandler<EventArgs> Report = null;
    public override event EventHandler<EventArgs> Removal = null;
    public override event EventHandler BatteryChanged;
    @@ -1288,5 +1319,45 @@ namespace DS4Windows.InputDevices
    @@ -1288,5 +1319,80 @@ namespace DS4Windows.InputDevices
    enableHomeLED = nativeOptionsStore.EnableHomeLED;
    }
    }
    +
    + public override DS4State getCurrentStateRef()
    + {
    + DS4State tempState = null;
    + if (!performStateMerge)
    + {
    + tempState = cState;
    + }
    + else
    + {
    + tempState = jointState;
    + }
    +
    + return tempState;
    + }
    +
    + public override DS4State getPreviousStateRef()
    + {
    + DS4State tempState = null;
    + if (!performStateMerge)
    + {
    + tempState = pState;
    + }
    + else
    + {
    + tempState = jointPreviousState;
    + }
    +
    + return tempState;
    + }
    +
    + public override void PreserveMergedStateData()
    + {
    + jointState.CopyTo(jointPreviousState);
    + }
    +
    + public override void MergeStateData(DS4State dState)
    + {
    + using (WriteLocker locker = new WriteLocker(lockSlim))
  4. Ryochan7 renamed this gist Mar 3, 2021. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions mergejoycondraft5.diff → mergejoycondraft6.diff
    Original file line number Diff line number Diff line change
    @@ -751,7 +751,7 @@ index 484e7312..5148a30e 100644
    }
    }
    diff --git a/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs b/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    index 92325d77..c21309d2 100644
    index 92325d77..1551063e 100644
    --- a/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    +++ b/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    @@ -215,6 +215,37 @@ namespace DS4Windows.InputDevices
    @@ -814,7 +814,7 @@ index 92325d77..c21309d2 100644
    + dState.DpadLeft = cState.DpadLeft;
    + dState.DpadRight = cState.DpadRight;
    + dState.Share = cState.Share;
    + if (primaryDevice) dState.Motion = cState.Motion;
    + if (outputMapGyro) dState.Motion = cState.Motion;
    + //dState.Motion = cState.Motion;
    + }
    + else if (DeviceType == InputDeviceType.JoyConR)
    @@ -831,7 +831,7 @@ index 92325d77..c21309d2 100644
    + dState.Square = cState.Square;
    + dState.PS = cState.PS;
    + dState.Options = cState.Options;
    + if (primaryDevice) dState.Motion = cState.Motion;
    + if (outputMapGyro) dState.Motion = cState.Motion;
    + //dState.Motion = cState.Motion;
    + }
    + }
  5. Ryochan7 renamed this gist Mar 3, 2021. 1 changed file with 142 additions and 72 deletions.
    214 changes: 142 additions & 72 deletions mergejoycondraft4.diff → mergejoycondraft5.diff
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    diff --git a/DS4Windows/DS4Control/ControlService.cs b/DS4Windows/DS4Control/ControlService.cs
    index a47dfb0b..1109b080 100644
    index a47dfb0b..b06352cf 100644
    --- a/DS4Windows/DS4Control/ControlService.cs
    +++ b/DS4Windows/DS4Control/ControlService.cs
    @@ -10,6 +10,7 @@ using System.Windows.Threading;
    @@ -10,7 +10,30 @@ index a47dfb0b..1109b080 100644

    namespace DS4Windows
    {
    @@ -623,7 +624,7 @@ namespace DS4Windows
    @@ -256,6 +257,22 @@ namespace DS4Windows
    DualSenseControllerOptions dSOpts = tempDSDev.NativeOptionsStore;
    dSOpts.LedModeChanged += (sender, e) => { tempDSDev.CheckControllerNumDeviceSettings(activeControllers); };
    }
    + else if (device.DeviceType == InputDevices.InputDeviceType.JoyConL ||
    + device.DeviceType == InputDevices.InputDeviceType.JoyConR)
    + {
    + InputDevices.JoyConDevice tempJoyDev = device as InputDevices.JoyConDevice;
    + tempJoyDev.PerformStateMerge = true;
    + if (device.DeviceType == InputDevices.InputDeviceType.JoyConL)
    + {
    + tempJoyDev.PrimaryDevice = true;
    + tempJoyDev.OutputMapGyro = false;
    + }
    + else
    + {
    + tempJoyDev.PrimaryDevice = false;
    + tempJoyDev.OutputMapGyro = true;
    + }
    + }
    }

    public bool CheckForSupportedDevice(HidDevice device, VidPidInfo metaInfo)
    @@ -623,7 +640,7 @@ namespace DS4Windows
    return temp;
    }

    @@ -19,7 +42,7 @@ index a47dfb0b..1109b080 100644
    OutputDevice outDevice, DS4Device device)
    {
    int devIndex = index;
    @@ -638,7 +639,7 @@ namespace DS4Windows
    @@ -638,7 +655,7 @@ namespace DS4Windows
    SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex);
    };
    tempXbox.cont.FeedbackReceived += p;
    @@ -28,7 +51,7 @@ index a47dfb0b..1109b080 100644
    }
    else if (contType == OutContType.DS4)
    {
    @@ -717,13 +718,15 @@ namespace DS4Windows
    @@ -717,13 +734,15 @@ namespace DS4Windows
    }
    }

    @@ -47,7 +70,7 @@ index a47dfb0b..1109b080 100644
    }
    else if (contType == OutContType.DS4)
    {
    @@ -790,7 +793,17 @@ namespace DS4Windows
    @@ -790,7 +809,17 @@ namespace DS4Windows

    // Enable ViGem feedback callback handler only if lightbar/rumble data output is enabled (if those are disabled then no point enabling ViGem callback handler call)
    if (Global.EnableOutputDataToDS4[index])
    @@ -65,7 +88,7 @@ index a47dfb0b..1109b080 100644

    outputslotMan.DeferredPlugin(tempXbox, index, outputDevices, contType);
    //slotDevice.CurrentInputBound = OutSlotDevice.InputBound.Bound;
    @@ -810,8 +823,19 @@ namespace DS4Windows
    @@ -810,8 +839,19 @@ namespace DS4Windows

    // Enable ViGem feedback callback handler only if lightbar/rumble data output is enabled (if those are disabled then no point enabling ViGem callback handler call)
    if (Global.EnableOutputDataToDS4[index])
    @@ -85,7 +108,7 @@ index a47dfb0b..1109b080 100644
    outputslotMan.EventDispatcher.BeginInvoke((Action)(() =>
    {
    outputDevices[index] = tempXbox;
    @@ -838,8 +862,19 @@ namespace DS4Windows
    @@ -838,8 +878,19 @@ namespace DS4Windows

    // Enable ViGem feedback callback handler only if DS4 lightbar/rumble data output is enabled (if those are disabled then no point enabling ViGem callback handler call)
    if (Global.EnableOutputDataToDS4[index])
    @@ -106,7 +129,7 @@ index a47dfb0b..1109b080 100644
    outputslotMan.DeferredPlugin(tempDS4, index, outputDevices, contType);
    //slotDevice.CurrentInputBound = OutSlotDevice.InputBound.Bound;

    @@ -858,8 +893,19 @@ namespace DS4Windows
    @@ -858,8 +909,19 @@ namespace DS4Windows

    // Enable ViGem feedback callback handler only if lightbar/rumble data output is enabled (if those are disabled then no point enabling ViGem callback handler call)
    if (Global.EnableOutputDataToDS4[index])
    @@ -126,7 +149,7 @@ index a47dfb0b..1109b080 100644
    outputslotMan.EventDispatcher.BeginInvoke((Action)(() =>
    {
    outputDevices[index] = tempDS4;
    @@ -912,7 +958,8 @@ namespace DS4Windows
    @@ -912,7 +974,8 @@ namespace DS4Windows
    {
    slotDevice.CurrentInputBound = OutSlotDevice.InputBound.Unbound;
    dev.ResetState();
    @@ -136,20 +159,22 @@ index a47dfb0b..1109b080 100644
    }
    //dev.Disconnect();
    //LogDebug(tempType + " Controller # " + (index + 1) + " unplugged");
    @@ -974,6 +1021,7 @@ namespace DS4Windows
    @@ -974,6 +1037,7 @@ namespace DS4Windows

    //for (int i = 0, devCount = devices.Count(); i < devCount; i++)
    int i = 0;
    + InputDevices.JoyConDevice tempPrimaryJoyDev = null;
    for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext() && loopControllers; i++)
    {
    DS4Device device = devEnum.Current;
    @@ -987,6 +1035,32 @@ namespace DS4Windows
    device.CurrentExclusiveStatus = DS4Device.ExclusiveStatus.HidGuardAffected;
    }
    @@ -990,10 +1054,34 @@ namespace DS4Windows
    Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); });
    task.Start();

    + if (device.DeviceType == InputDevices.InputDeviceType.JoyConL ||
    + device.DeviceType == InputDevices.InputDeviceType.JoyConR)
    + PostDS4DeviceInit(device);
    +
    + if ((device.DeviceType == InputDevices.InputDeviceType.JoyConL ||
    + device.DeviceType == InputDevices.InputDeviceType.JoyConR) && device.PerformStateMerge)
    + {
    + if (tempPrimaryJoyDev == null)
    + {
    @@ -158,26 +183,27 @@ index a47dfb0b..1109b080 100644
    + else
    + {
    + InputDevices.JoyConDevice currentJoyDev = device as InputDevices.JoyConDevice;
    + tempPrimaryJoyDev.PerformStateMerge = true;
    + tempPrimaryJoyDev.JointDevice = currentJoyDev;
    + currentJoyDev.PerformStateMerge = true;
    + currentJoyDev.JointDevice = tempPrimaryJoyDev;
    + currentJoyDev.PrimaryDevice = false;
    +
    + tempPrimaryJoyDev.JointState = currentJoyDev.JointState;
    + InputDevices.JoyConDevice parentJoy = tempPrimaryJoyDev;
    +
    + InputDevices.JoyConDevice parentJoy = tempPrimaryJoyDev;
    + tempPrimaryJoyDev.Removal += (sender, args) => { currentJoyDev.JointDevice = null; };
    + currentJoyDev.Removal += (sender, args) => { parentJoy.JointDevice = null; };
    +
    + tempPrimaryJoyDev = null;
    + }
    + }
    +
    Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); });
    task.Start();
    DS4Controllers[i] = device;
    device.DeviceSlotNumber = i;
    Global.LoadControllerConfigs(device);
    - PostDS4DeviceInit(device);
    device.LoadStoreSettings();
    device.CheckControllerNumDeviceSettings(numControllers);

    @@ -1030,7 +1104,28 @@ namespace DS4Windows
    @@ -1030,7 +1118,28 @@ namespace DS4Windows
    if (!getDInputOnly(i) && device.isSynced())
    {
    //useDInputOnly[i] = false;
    @@ -186,9 +212,9 @@ index a47dfb0b..1109b080 100644
    + {
    + PluginOutDev(i, device);
    + }
    + else
    + else if (device.JointDeviceSlotNumber != DS4Device.DEFAULT_JOINT_SLOT_NUMBER)
    + {
    + int otherIdx = (device as InputDevices.JoyConDevice).JointDevice.DeviceSlotNumber;
    + int otherIdx = device.JointDeviceSlotNumber;
    + OutputDevice tempOutDev = outputDevices[otherIdx];
    + if (tempOutDev != null)
    + {
    @@ -207,25 +233,34 @@ index a47dfb0b..1109b080 100644
    }
    else
    {
    @@ -1038,7 +1133,16 @@ namespace DS4Windows
    @@ -1038,7 +1147,25 @@ namespace DS4Windows
    Global.activeOutDevType[i] = OutContType.None;
    }

    - TouchPadOn(i, device);
    + if (!device.PrimaryDevice && device.JointDeviceSlotNumber != DS4Device.DEFAULT_JOINT_SLOT_NUMBER)
    + if (device.PrimaryDevice && device.OutputMapGyro)
    + {
    + int otherIdx = device.JointDeviceSlotNumber;
    + TouchPadOn(otherIdx, device);
    + TouchPadOn(i, device);
    + }
    + else if (device.PrimaryDevice && (device.DeviceType == InputDevices.InputDeviceType.JoyConR))
    + else if (device.JointDeviceSlotNumber != DS4Device.DEFAULT_JOINT_SLOT_NUMBER)
    + {
    + TouchPadOn(i, device);
    + int otherIdx = device.JointDeviceSlotNumber;
    + DS4Device tempDev = DS4Controllers[otherIdx];
    + if (tempDev != null)
    + {
    + int mappedIdx = tempDev.PrimaryDevice ? otherIdx : i;
    + DS4Device gyroDev = device.OutputMapGyro ? device : (tempDev.OutputMapGyro ? tempDev : null);
    + if (gyroDev != null)
    + {
    + TouchPadOn(mappedIdx, gyroDev);
    + }
    + }
    + }
    +
    CheckProfileOptions(i, device, true);
    }

    @@ -1048,7 +1152,7 @@ namespace DS4Windows
    @@ -1048,7 +1175,7 @@ namespace DS4Windows
    this.On_Report(sender, e, tempIdx);
    };

    @@ -234,7 +269,7 @@ index a47dfb0b..1109b080 100644
    {
    DS4Device.ReportHandler<EventArgs> tempEvnt = (sender, args) =>
    {
    @@ -1287,6 +1391,13 @@ namespace DS4Windows
    @@ -1287,6 +1414,13 @@ namespace DS4Windows
    activeControllers = numControllers;
    //foreach (DS4Device device in devices)
    //for (int i = 0, devlen = devices.Count(); i < devlen; i++)
    @@ -248,24 +283,25 @@ index a47dfb0b..1109b080 100644
    for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext() && loopControllers;)
    {
    DS4Device device = devEnum.Current;
    @@ -1326,6 +1437,45 @@ namespace DS4Windows
    device.CurrentExclusiveStatus = DS4Device.ExclusiveStatus.HidGuardAffected;
    }
    @@ -1328,10 +1462,48 @@ namespace DS4Windows

    + if (device.DeviceType == InputDevices.InputDeviceType.JoyConL ||
    + device.DeviceType == InputDevices.InputDeviceType.JoyConR)
    Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); });
    task.Start();
    +
    + PostDS4DeviceInit(device);
    +
    + if ((device.DeviceType == InputDevices.InputDeviceType.JoyConL ||
    + device.DeviceType == InputDevices.InputDeviceType.JoyConR) && device.PerformStateMerge)
    + {
    + if (tempPrimaryJoyDev == device &&
    + tempSecondaryJoyDev != null)
    + {
    + InputDevices.JoyConDevice currentJoyDev = device as InputDevices.JoyConDevice;
    + tempSecondaryJoyDev.PerformStateMerge = true;
    + tempSecondaryJoyDev.JointDevice = currentJoyDev;
    + currentJoyDev.PerformStateMerge = true;
    + currentJoyDev.JointDevice = tempSecondaryJoyDev;
    + currentJoyDev.PrimaryDevice = true;
    +
    + tempSecondaryJoyDev.JointState = currentJoyDev.JointState;
    +
    + InputDevices.JoyConDevice secondaryJoy = tempSecondaryJoyDev;
    + secondaryJoy.Removal += (sender, args) => { currentJoyDev.JointDevice = null; };
    + currentJoyDev.Removal += (sender, args) => { secondaryJoy.JointDevice = null; };
    @@ -276,13 +312,11 @@ index a47dfb0b..1109b080 100644
    + else if (tempPrimaryJoyDev != null)
    + {
    + InputDevices.JoyConDevice currentJoyDev = device as InputDevices.JoyConDevice;
    + tempPrimaryJoyDev.PerformStateMerge = true;
    + tempPrimaryJoyDev.JointDevice = currentJoyDev;
    + currentJoyDev.PerformStateMerge = true;
    + currentJoyDev.JointDevice = tempPrimaryJoyDev;
    + currentJoyDev.PrimaryDevice = false;
    +
    + tempPrimaryJoyDev.JointState = currentJoyDev.JointState;
    +
    + InputDevices.JoyConDevice parentJoy = tempPrimaryJoyDev;
    + tempPrimaryJoyDev.Removal += (sender, args) => { currentJoyDev.JointDevice = null; };
    + currentJoyDev.Removal += (sender, args) => { parentJoy.JointDevice = null; };
    @@ -291,10 +325,15 @@ index a47dfb0b..1109b080 100644
    + }
    + }
    +
    Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); });
    task.Start();
    +
    DS4Controllers[Index] = device;
    @@ -1368,7 +1518,28 @@ namespace DS4Windows
    device.DeviceSlotNumber = Index;
    Global.LoadControllerConfigs(device);
    - PostDS4DeviceInit(device);
    device.LoadStoreSettings();
    device.CheckControllerNumDeviceSettings(numControllers);

    @@ -1368,7 +1540,28 @@ namespace DS4Windows
    if (!getDInputOnly(Index) && device.isSynced())
    {
    //useDInputOnly[Index] = false;
    @@ -324,25 +363,34 @@ index a47dfb0b..1109b080 100644
    }
    else
    {
    @@ -1376,7 +1547,16 @@ namespace DS4Windows
    @@ -1376,7 +1569,25 @@ namespace DS4Windows
    Global.activeOutDevType[Index] = OutContType.None;
    }

    - TouchPadOn(Index, device);
    + if (!device.PrimaryDevice && device.JointDeviceSlotNumber != DS4Device.DEFAULT_JOINT_SLOT_NUMBER)
    + if (device.PrimaryDevice && device.OutputMapGyro)
    + {
    + int otherIdx = device.JointDeviceSlotNumber;
    + TouchPadOn(otherIdx, device);
    + TouchPadOn(Index, device);
    + }
    + else if (device.PrimaryDevice && (device.DeviceType == InputDevices.InputDeviceType.JoyConR))
    + else if (device.JointDeviceSlotNumber != DS4Device.DEFAULT_JOINT_SLOT_NUMBER)
    + {
    + TouchPadOn(Index, device);
    + int otherIdx = device.JointDeviceSlotNumber;
    + DS4Device tempDev = DS4Controllers[otherIdx];
    + if (tempDev != null)
    + {
    + int mappedIdx = tempDev.PrimaryDevice ? otherIdx : Index;
    + DS4Device gyroDev = device.OutputMapGyro ? device : (tempDev.OutputMapGyro ? tempDev : null);
    + if (gyroDev != null)
    + {
    + TouchPadOn(mappedIdx, gyroDev);
    + }
    + }
    + }
    +
    CheckProfileOptions(Index, device);
    }

    @@ -1386,7 +1566,7 @@ namespace DS4Windows
    @@ -1386,7 +1597,7 @@ namespace DS4Windows
    this.On_Report(sender, e, tempIdx);
    };

    @@ -351,7 +399,7 @@ index a47dfb0b..1109b080 100644
    {
    DS4Device.ReportHandler<EventArgs> tempEvnt = (sender, args) =>
    {
    @@ -1766,6 +1946,15 @@ namespace DS4Windows
    @@ -1766,6 +1977,15 @@ namespace DS4Windows
    {
    UnplugOutDev(ind, device);
    }
    @@ -367,7 +415,7 @@ index a47dfb0b..1109b080 100644

    // Use Task to reset device synth state and commit it
    Task.Run(() =>
    @@ -1873,8 +2062,18 @@ namespace DS4Windows
    @@ -1873,8 +2093,18 @@ namespace DS4Windows
    }
    }

    @@ -381,14 +429,14 @@ index a47dfb0b..1109b080 100644
    + }
    + else
    + {
    + cState = (device as InputDevices.JoyConDevice).JointState;
    + cState = device.JointState;
    + device.MergeStateData(cState);
    + }
    +
    DS4State pState = device.getPreviousStateRef();
    //device.getPreviousState(PreviousState[ind]);
    //DS4State pState = PreviousState[ind];
    @@ -1912,6 +2111,12 @@ namespace DS4Windows
    @@ -1912,6 +2142,12 @@ namespace DS4Windows
    // */
    //}

    @@ -617,6 +665,18 @@ index 5c09b3e0..0064b2de 100644
    Tag="{Binding DevIndex, Mode=OneTime}" Click="ProfEditSBtn_Click" HorizontalAlignment="Center">
    <xctk:SplitButton.DropDownContent>
    <Button x:Name="newProfBtn" Content="{lex:Loc NewProfile}" MinWidth="120" Tag="{Binding DevIndex, Mode=OneTime}" Click="NewProfBtn_Click" />
    diff --git a/DS4Windows/DS4Forms/MainWindow.xaml.cs b/DS4Windows/DS4Forms/MainWindow.xaml.cs
    index a5a1ed26..d1f9e7fd 100644
    --- a/DS4Windows/DS4Forms/MainWindow.xaml.cs
    +++ b/DS4Windows/DS4Forms/MainWindow.xaml.cs
    @@ -903,6 +903,7 @@ Suspend support not enabled.", true);
    {
    Image img = sender as Image;
    int tag = Convert.ToInt32(img.Tag);
    + conLvViewModel.CurrentIndex = tag;
    CompositeDeviceModel item = conLvViewModel.CurrentItem;
    //CompositeDeviceModel item = conLvViewModel.ControllerDict[tag];
    if (item != null)
    diff --git a/DS4Windows/DS4Forms/ViewModels/ControllerListViewModel.cs b/DS4Windows/DS4Forms/ViewModels/ControllerListViewModel.cs
    index c8e4163f..42807ed5 100644
    --- a/DS4Windows/DS4Forms/ViewModels/ControllerListViewModel.cs
    @@ -634,17 +694,25 @@ index c8e4163f..42807ed5 100644
    public event CustomColorHandler RequestColorPicker;

    diff --git a/DS4Windows/DS4Library/DS4Device.cs b/DS4Windows/DS4Library/DS4Device.cs
    index 484e7312..8d01dfe9 100644
    index 484e7312..5148a30e 100644
    --- a/DS4Windows/DS4Library/DS4Device.cs
    +++ b/DS4Windows/DS4Library/DS4Device.cs
    @@ -578,6 +578,27 @@ namespace DS4Windows
    @@ -578,6 +578,42 @@ namespace DS4Windows
    protected event EventHandler DeviceSlotNumberChanged;
    protected byte deviceSlotMask = 0x00;

    + protected DS4State jointState = new DS4State();
    + public DS4State JointState
    + {
    + get => jointState;
    + set => jointState = value;
    + }
    +
    + protected bool performStateMerge;
    + public bool PerformStateMerge
    + {
    + get => performStateMerge; set => performStateMerge = value;
    + get => performStateMerge;
    + set => performStateMerge = value;
    + }
    +
    + protected bool primaryDevice = true;
    @@ -661,11 +729,18 @@ index 484e7312..8d01dfe9 100644
    + get => jointDeviceSlotNumber;
    + set => jointDeviceSlotNumber = value;
    + }
    +
    + protected bool outputMapGyro = true;
    + public bool OutputMapGyro
    + {
    + get => outputMapGyro;
    + set => outputMapGyro = value;
    + }
    +
    public DS4Device(HidDevice hidDevice, string disName, VidPidFeatureSet featureSet = VidPidFeatureSet.DefaultDS4)
    {
    hDevice = hidDevice;
    @@ -2055,5 +2076,9 @@ namespace DS4Windows
    @@ -2055,5 +2091,9 @@ namespace DS4Windows
    PrepareOutputFeaturesByte();
    }
    }
    @@ -676,19 +751,13 @@ index 484e7312..8d01dfe9 100644
    }
    }
    diff --git a/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs b/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    index 92325d77..8563cde7 100644
    index 92325d77..c21309d2 100644
    --- a/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    +++ b/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    @@ -214,6 +214,42 @@ namespace DS4Windows.InputDevices
    public bool EnableHomeLED { get => enableHomeLED; set => enableHomeLED = value; }
    @@ -215,6 +215,37 @@ namespace DS4Windows.InputDevices

    private JoyConControllerOptions nativeOptionsStore;
    + private DS4State jointState = new DS4State();
    + public DS4State JointState
    + {
    + get => jointState; set => jointState = value;
    + }
    +

    + private ReaderWriterLockSlim lockSlim = new ReaderWriterLockSlim();
    + private JoyConDevice jointDevice;
    + public JoyConDevice JointDevice
    @@ -719,10 +788,11 @@ index 92325d77..8563cde7 100644
    + return result;
    + }
    + }
    +
    public override event ReportHandler<EventArgs> Report = null;
    public override event EventHandler<EventArgs> Removal = null;
    @@ -1288,5 +1324,45 @@ namespace DS4Windows.InputDevices
    public override event EventHandler BatteryChanged;
    @@ -1288,5 +1319,45 @@ namespace DS4Windows.InputDevices
    enableHomeLED = nativeOptionsStore.EnableHomeLED;
    }
    }
  6. Ryochan7 renamed this gist Mar 2, 2021. 1 changed file with 146 additions and 77 deletions.
    223 changes: 146 additions & 77 deletions mergejoycondraft3.diff → mergejoycondraft4.diff
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    diff --git a/DS4Windows/DS4Control/ControlService.cs b/DS4Windows/DS4Control/ControlService.cs
    index a47dfb0b..1540527d 100644
    index a47dfb0b..1109b080 100644
    --- a/DS4Windows/DS4Control/ControlService.cs
    +++ b/DS4Windows/DS4Control/ControlService.cs
    @@ -10,6 +10,7 @@ using System.Windows.Threading;
    @@ -140,47 +140,49 @@ index a47dfb0b..1540527d 100644

    //for (int i = 0, devCount = devices.Count(); i < devCount; i++)
    int i = 0;
    + InputDevices.JoyConDevice tempJoyDev = null;
    + InputDevices.JoyConDevice tempPrimaryJoyDev = null;
    for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext() && loopControllers; i++)
    {
    DS4Device device = devEnum.Current;
    @@ -987,6 +1035,30 @@ namespace DS4Windows
    @@ -987,6 +1035,32 @@ namespace DS4Windows
    device.CurrentExclusiveStatus = DS4Device.ExclusiveStatus.HidGuardAffected;
    }

    + if (device.DeviceType == InputDevices.InputDeviceType.JoyConL ||
    + device.DeviceType == InputDevices.InputDeviceType.JoyConR)
    + {
    + if (tempJoyDev == null)
    + if (tempPrimaryJoyDev == null)
    + {
    + tempJoyDev = device as InputDevices.JoyConDevice;
    + tempPrimaryJoyDev = device as InputDevices.JoyConDevice;
    + }
    + else
    + {
    + InputDevices.JoyConDevice currentJoyDev = device as InputDevices.JoyConDevice;
    + tempJoyDev.PerformStateMerge = true;
    + tempJoyDev.JointDevice = currentJoyDev;
    + tempPrimaryJoyDev.PerformStateMerge = true;
    + tempPrimaryJoyDev.JointDevice = currentJoyDev;
    + currentJoyDev.PerformStateMerge = true;
    + currentJoyDev.JointDevice = tempJoyDev;
    + currentJoyDev.JointDevice = tempPrimaryJoyDev;
    + currentJoyDev.PrimaryDevice = false;
    +
    + tempJoyDev.JointState = currentJoyDev.JointState;
    + InputDevices.JoyConDevice parentJoy = tempJoyDev;
    + tempPrimaryJoyDev.JointState = currentJoyDev.JointState;
    + InputDevices.JoyConDevice parentJoy = tempPrimaryJoyDev;
    +
    + tempPrimaryJoyDev.Removal += (sender, args) => { currentJoyDev.JointDevice = null; };
    + currentJoyDev.Removal += (sender, args) => { parentJoy.JointDevice = null; };
    +
    + tempJoyDev = null;
    + tempPrimaryJoyDev = null;
    + }
    + }
    +
    Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); });
    task.Start();

    @@ -1030,7 +1102,28 @@ namespace DS4Windows
    @@ -1030,7 +1104,28 @@ namespace DS4Windows
    if (!getDInputOnly(i) && device.isSynced())
    {
    //useDInputOnly[i] = false;
    - PluginOutDev(i, device);
    + if (!device.PerformStateMerge)
    + if (device.PrimaryDevice)
    + {
    + PluginOutDev(i, device);
    + }
    @@ -205,67 +207,94 @@ index a47dfb0b..1540527d 100644
    }
    else
    {
    @@ -1038,7 +1131,11 @@ namespace DS4Windows
    @@ -1038,7 +1133,16 @@ namespace DS4Windows
    Global.activeOutDevType[i] = OutContType.None;
    }

    - TouchPadOn(i, device);
    + if (device.PrimaryDevice)
    + if (!device.PrimaryDevice && device.JointDeviceSlotNumber != DS4Device.DEFAULT_JOINT_SLOT_NUMBER)
    + {
    + int otherIdx = device.JointDeviceSlotNumber;
    + TouchPadOn(otherIdx, device);
    + }
    + else if (device.PrimaryDevice && (device.DeviceType == InputDevices.InputDeviceType.JoyConR))
    + {
    + TouchPadOn(i, device);
    + }
    +
    CheckProfileOptions(i, device, true);
    }

    @@ -1048,7 +1145,7 @@ namespace DS4Windows
    @@ -1048,7 +1152,7 @@ namespace DS4Windows
    this.On_Report(sender, e, tempIdx);
    };

    - if (_udpServer != null && i < UdpServer.NUMBER_SLOTS)
    + if (_udpServer != null && i < UdpServer.NUMBER_SLOTS && device.PerformStateMerge)
    + if (_udpServer != null && i < UdpServer.NUMBER_SLOTS && device.PrimaryDevice)
    {
    DS4Device.ReportHandler<EventArgs> tempEvnt = (sender, args) =>
    {
    @@ -1287,6 +1384,10 @@ namespace DS4Windows
    @@ -1287,6 +1391,13 @@ namespace DS4Windows
    activeControllers = numControllers;
    //foreach (DS4Device device in devices)
    //for (int i = 0, devlen = devices.Count(); i < devlen; i++)
    + InputDevices.JoyConDevice tempJoyDev = devices.Where(d =>
    + InputDevices.JoyConDevice tempPrimaryJoyDev = devices.Where(d =>
    + (d.DeviceType == InputDevices.InputDeviceType.JoyConL || d.DeviceType == InputDevices.InputDeviceType.JoyConR)
    + && d.PrimaryDevice && d.JointDeviceSlotNumber == -1).FirstOrDefault() as InputDevices.JoyConDevice;
    + InputDevices.JoyConDevice tempSecondaryJoyDev = devices.Where(d =>
    + (d.DeviceType == InputDevices.InputDeviceType.JoyConL || d.DeviceType == InputDevices.InputDeviceType.JoyConR)
    + && !d.PrimaryDevice && d.JointDeviceSlotNumber == -1).FirstOrDefault() as InputDevices.JoyConDevice;
    +
    for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext() && loopControllers;)
    {
    DS4Device device = devEnum.Current;
    @@ -1326,6 +1427,26 @@ namespace DS4Windows
    @@ -1326,6 +1437,45 @@ namespace DS4Windows
    device.CurrentExclusiveStatus = DS4Device.ExclusiveStatus.HidGuardAffected;
    }

    + if (device.DeviceType == InputDevices.InputDeviceType.JoyConL ||
    + device.DeviceType == InputDevices.InputDeviceType.JoyConR)
    + {
    + if (tempJoyDev != null)
    + if (tempPrimaryJoyDev == device &&
    + tempSecondaryJoyDev != null)
    + {
    + InputDevices.JoyConDevice currentJoyDev = device as InputDevices.JoyConDevice;
    + tempSecondaryJoyDev.PerformStateMerge = true;
    + tempSecondaryJoyDev.JointDevice = currentJoyDev;
    + currentJoyDev.PerformStateMerge = true;
    + currentJoyDev.JointDevice = tempSecondaryJoyDev;
    + currentJoyDev.PrimaryDevice = true;
    +
    + tempSecondaryJoyDev.JointState = currentJoyDev.JointState;
    + InputDevices.JoyConDevice secondaryJoy = tempSecondaryJoyDev;
    + secondaryJoy.Removal += (sender, args) => { currentJoyDev.JointDevice = null; };
    + currentJoyDev.Removal += (sender, args) => { secondaryJoy.JointDevice = null; };
    +
    + tempSecondaryJoyDev = null;
    + tempPrimaryJoyDev = null;
    + }
    + else if (tempPrimaryJoyDev != null)
    + {
    + InputDevices.JoyConDevice currentJoyDev = device as InputDevices.JoyConDevice;
    + tempJoyDev.PerformStateMerge = true;
    + tempJoyDev.JointDevice = currentJoyDev;
    + tempPrimaryJoyDev.PerformStateMerge = true;
    + tempPrimaryJoyDev.JointDevice = currentJoyDev;
    + currentJoyDev.PerformStateMerge = true;
    + currentJoyDev.JointDevice = tempJoyDev;
    + currentJoyDev.JointDevice = tempPrimaryJoyDev;
    + currentJoyDev.PrimaryDevice = false;
    +
    + tempJoyDev.JointState = currentJoyDev.JointState;
    + InputDevices.JoyConDevice parentJoy = tempJoyDev;
    + tempPrimaryJoyDev.JointState = currentJoyDev.JointState;
    + InputDevices.JoyConDevice parentJoy = tempPrimaryJoyDev;
    + tempPrimaryJoyDev.Removal += (sender, args) => { currentJoyDev.JointDevice = null; };
    + currentJoyDev.Removal += (sender, args) => { parentJoy.JointDevice = null; };
    +
    + tempJoyDev = null;
    + tempPrimaryJoyDev = null;
    + }
    + }
    +
    Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); });
    task.Start();
    DS4Controllers[Index] = device;
    @@ -1368,7 +1489,23 @@ namespace DS4Windows
    @@ -1368,7 +1518,28 @@ namespace DS4Windows
    if (!getDInputOnly(Index) && device.isSynced())
    {
    //useDInputOnly[Index] = false;
    @@ -277,11 +306,16 @@ index a47dfb0b..1540527d 100644
    + else
    + {
    + int otherIdx = (device as InputDevices.JoyConDevice).JointDevice.DeviceSlotNumber;
    + Xbox360OutDevice tempXbox = outputDevices[otherIdx] as Xbox360OutDevice;
    + if (tempXbox != null)
    + OutputDevice tempOutDev = outputDevices[otherIdx];
    + if (tempOutDev != null)
    + {
    + EstablishOutFeedback(Index, OutContType.X360, tempXbox, device);
    + outputDevices[Index] = tempXbox;
    + OutContType tempConType = activeOutDevType[otherIdx];
    + EstablishOutFeedback(Index, tempConType, tempOutDev, device);
    + outputDevices[Index] = tempOutDev;
    + Global.activeOutDevType[Index] = tempConType;
    +
    + //useDInputOnly[i] = false;
    + //Global.activeOutDevType[i] = OutContType.X360;
    + }
    +
    + //useDInputOnly[Index] = true;
    @@ -290,20 +324,34 @@ index a47dfb0b..1540527d 100644
    }
    else
    {
    @@ -1376,7 +1513,11 @@ namespace DS4Windows
    @@ -1376,7 +1547,16 @@ namespace DS4Windows
    Global.activeOutDevType[Index] = OutContType.None;
    }

    - TouchPadOn(Index, device);
    + if (device.PrimaryDevice)
    + if (!device.PrimaryDevice && device.JointDeviceSlotNumber != DS4Device.DEFAULT_JOINT_SLOT_NUMBER)
    + {
    + int otherIdx = device.JointDeviceSlotNumber;
    + TouchPadOn(otherIdx, device);
    + }
    + else if (device.PrimaryDevice && (device.DeviceType == InputDevices.InputDeviceType.JoyConR))
    + {
    + TouchPadOn(Index, device);
    + }
    +
    CheckProfileOptions(Index, device);
    }

    @@ -1766,6 +1907,15 @@ namespace DS4Windows
    @@ -1386,7 +1566,7 @@ namespace DS4Windows
    this.On_Report(sender, e, tempIdx);
    };

    - if (_udpServer != null && Index < UdpServer.NUMBER_SLOTS)
    + if (_udpServer != null && Index < UdpServer.NUMBER_SLOTS && device.PrimaryDevice)
    {
    DS4Device.ReportHandler<EventArgs> tempEvnt = (sender, args) =>
    {
    @@ -1766,6 +1946,15 @@ namespace DS4Windows
    {
    UnplugOutDev(ind, device);
    }
    @@ -319,7 +367,7 @@ index a47dfb0b..1540527d 100644

    // Use Task to reset device synth state and commit it
    Task.Run(() =>
    @@ -1873,8 +2023,18 @@ namespace DS4Windows
    @@ -1873,8 +2062,18 @@ namespace DS4Windows
    }
    }

    @@ -340,7 +388,7 @@ index a47dfb0b..1540527d 100644
    DS4State pState = device.getPreviousStateRef();
    //device.getPreviousState(PreviousState[ind]);
    //DS4State pState = PreviousState[ind];
    @@ -1912,6 +2072,12 @@ namespace DS4Windows
    @@ -1912,6 +2111,12 @@ namespace DS4Windows
    // */
    //}

    @@ -391,7 +439,7 @@ index ada31e46..154f650e 100644
    }
    }
    diff --git a/DS4Windows/DS4Control/ScpUtil.cs b/DS4Windows/DS4Control/ScpUtil.cs
    index 97380f13..138c87ac 100644
    index 97380f13..695c0a56 100644
    --- a/DS4Windows/DS4Control/ScpUtil.cs
    +++ b/DS4Windows/DS4Control/ScpUtil.cs
    @@ -5083,8 +5083,12 @@ namespace DS4Windows
    @@ -409,7 +457,7 @@ index 97380f13..138c87ac 100644
    }

    try
    @@ -7062,26 +7066,61 @@ namespace DS4Windows
    @@ -7062,25 +7066,27 @@ namespace DS4Windows
    {
    tempDev.setIdleTimeout(idleDisconnectTimeout[device]);
    tempDev.setBTPollRate(btPollRate[device]);
    @@ -443,46 +491,14 @@ index 97380f13..138c87ac 100644
    - control.PluginOutDev(device, tempDev);
    - //Global.useDInputOnly[device] = false;
    -
    }
    - }
    - else if (xinputStatus && !xinputPlug)
    + /*else if (xinputStatus && !tempDev.PrimaryDevice)
    {
    - {
    - //Global.activeOutDevType[device] = OutContType.None;
    - control.UnplugOutDev(device, tempDev);
    + OutputDevice tempOutDev = control.outputDevices[device];
    + if (xinputPlug)
    + {
    + if (tempOutDev != null)
    + {
    + tempOutDev.RemoveFeedback(device);
    + control.outputDevices[device] = null;
    + Global.activeOutDevType[device] = OutContType.None;
    + }
    +
    + int tempJointDeviceSlot = tempDev.JointDeviceSlotNumber;
    + tempOutDev = tempJointDeviceSlot != -1 ? control.outputDevices[tempDev.JointDeviceSlotNumber] : null;
    + if (tempOutDev != null && outputDevType[device] == Global.activeOutDevType[tempJointDeviceSlot])
    + {
    + OutContType tempContType = outputDevType[device];
    + control.EstablishOutFeedback(device, tempContType, tempOutDev, tempDev);
    + control.outputDevices[device] = tempOutDev;
    + Global.activeOutDevType[device] = outputDevType[device];
    + }
    + }
    + else
    + {
    + if (tempOutDev != null)
    + {
    + tempOutDev.RemoveFeedback(device);
    + control.outputDevices[device] = null;
    + Global.activeOutDevType[device] = OutContType.None;
    + }
    + }
    }
    + */

    tempDev.RumbleAutostopTime = rumbleAutostopTime[device];
    tempDev.setRumble(0, 0);
    diff --git a/DS4Windows/DS4Control/Xbox360OutDevice.cs b/DS4Windows/DS4Control/Xbox360OutDevice.cs
    index 6c4df1fc..6f736e78 100644
    --- a/DS4Windows/DS4Control/Xbox360OutDevice.cs
    @@ -565,11 +581,63 @@ index 6c4df1fc..6f736e78 100644
    + }
    }
    }
    diff --git a/DS4Windows/DS4Forms/MainWindow.xaml b/DS4Windows/DS4Forms/MainWindow.xaml
    index 5c09b3e0..0064b2de 100644
    --- a/DS4Windows/DS4Forms/MainWindow.xaml
    +++ b/DS4Windows/DS4Forms/MainWindow.xaml
    @@ -95,11 +95,11 @@
    </DataTemplate>
    </GridViewColumn.CellTemplate>
    </GridViewColumn>
    - <GridViewColumn x:Name="LinkProfColumn" Header="Link Profile/ID" Width="100" >
    + <GridViewColumn x:Name="LinkProfColumn" Header="Link Profile/ID" Width="100">
    <GridViewColumn.CellTemplate>
    <DataTemplate>
    <StackPanel Width="{Binding ElementName=LinkProfColumn, Path=Width}">
    - <CheckBox IsChecked="{Binding LinkedProfile}" HorizontalAlignment="Center" />
    + <CheckBox IsChecked="{Binding LinkedProfile}" HorizontalAlignment="Center" IsEnabled="{Binding PrimaryDevice}" />
    </StackPanel>
    </DataTemplate>
    </GridViewColumn.CellTemplate>
    @@ -109,7 +109,7 @@
    <DataTemplate>
    <StackPanel Width="{Binding Path=Width, ElementName=selectProfileColumn, Mode=OneWay}" HorizontalAlignment="Center">
    <ComboBox x:Name="selectProfCombo" Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=OneWay}"
    - ItemsSource="{Binding ProfileListCol}" SelectedIndex="{Binding SelectedIndex}" Height="Auto" Tag="{Binding DevIndex, Mode=OneTime}" Margin="0"
    + ItemsSource="{Binding ProfileListCol}" SelectedIndex="{Binding SelectedIndex}" IsEnabled="{Binding PrimaryDevice}" Height="Auto" Tag="{Binding DevIndex, Mode=OneTime}" Margin="0"
    SelectionChanged="SelectProfCombo_SelectionChanged" KeyDown="SelectProfCombo_KeyDown">
    <ComboBox.ItemTemplate>
    <DataTemplate>
    @@ -125,7 +125,7 @@
    <GridViewColumn.CellTemplate>
    <DataTemplate>
    <StackPanel Width="{Binding ElementName=editColumn, Path=Width}">
    - <xctk:SplitButton x:Name="ProfEditSBtn" Content="{lex:Loc Edit}" Height="20" MinWidth="60"
    + <xctk:SplitButton x:Name="ProfEditSBtn" Content="{lex:Loc Edit}" Height="20" MinWidth="60" IsEnabled="{Binding PrimaryDevice}"
    Tag="{Binding DevIndex, Mode=OneTime}" Click="ProfEditSBtn_Click" HorizontalAlignment="Center">
    <xctk:SplitButton.DropDownContent>
    <Button x:Name="newProfBtn" Content="{lex:Loc NewProfile}" MinWidth="120" Tag="{Binding DevIndex, Mode=OneTime}" Click="NewProfBtn_Click" />
    diff --git a/DS4Windows/DS4Forms/ViewModels/ControllerListViewModel.cs b/DS4Windows/DS4Forms/ViewModels/ControllerListViewModel.cs
    index c8e4163f..42807ed5 100644
    --- a/DS4Windows/DS4Forms/ViewModels/ControllerListViewModel.cs
    +++ b/DS4Windows/DS4Forms/ViewModels/ControllerListViewModel.cs
    @@ -339,6 +339,11 @@ namespace DS4WinWPF.DS4Forms.ViewModels
    }
    }

    + public bool PrimaryDevice
    + {
    + get => device.PrimaryDevice;
    + }
    +
    public delegate void CustomColorHandler(CompositeDeviceModel sender);
    public event CustomColorHandler RequestColorPicker;

    diff --git a/DS4Windows/DS4Library/DS4Device.cs b/DS4Windows/DS4Library/DS4Device.cs
    index 484e7312..e022c6b1 100644
    index 484e7312..8d01dfe9 100644
    --- a/DS4Windows/DS4Library/DS4Device.cs
    +++ b/DS4Windows/DS4Library/DS4Device.cs
    @@ -578,6 +578,26 @@ namespace DS4Windows
    @@ -578,6 +578,27 @@ namespace DS4Windows
    protected event EventHandler DeviceSlotNumberChanged;
    protected byte deviceSlotMask = 0x00;

    @@ -586,7 +654,8 @@ index 484e7312..e022c6b1 100644
    + set => primaryDevice = value;
    + }
    +
    + protected int jointDeviceSlotNumber = -1;
    + public const int DEFAULT_JOINT_SLOT_NUMBER = -1;
    + protected int jointDeviceSlotNumber = DEFAULT_JOINT_SLOT_NUMBER;
    + public virtual int JointDeviceSlotNumber
    + {
    + get => jointDeviceSlotNumber;
    @@ -596,7 +665,7 @@ index 484e7312..e022c6b1 100644
    public DS4Device(HidDevice hidDevice, string disName, VidPidFeatureSet featureSet = VidPidFeatureSet.DefaultDS4)
    {
    hDevice = hidDevice;
    @@ -2055,5 +2075,9 @@ namespace DS4Windows
    @@ -2055,5 +2076,9 @@ namespace DS4Windows
    PrepareOutputFeaturesByte();
    }
    }
  7. Ryochan7 revised this gist Mar 1, 2021. 2 changed files with 701 additions and 173 deletions.
    173 changes: 0 additions & 173 deletions mergejoycondraft1.diff
    Original file line number Diff line number Diff line change
    @@ -1,173 +0,0 @@
    diff --git a/DS4Windows/DS4Control/ControlService.cs b/DS4Windows/DS4Control/ControlService.cs
    index a47dfb0b..b7aec6c8 100644
    --- a/DS4Windows/DS4Control/ControlService.cs
    +++ b/DS4Windows/DS4Control/ControlService.cs
    @@ -31,6 +31,8 @@ namespace DS4Windows
    public bool running = false;
    public bool loopControllers = true;
    public bool inServiceTask = false;
    + private ReaderWriterLockSlim[] inputMergeLock = new ReaderWriterLockSlim[MAX_DS4_CONTROLLER_COUNT];
    + private DS4State[] tempInputMappedState = new DS4State[MAX_DS4_CONTROLLER_COUNT];
    private DS4State[] MappedState = new DS4State[MAX_DS4_CONTROLLER_COUNT];
    private DS4State[] CurrentState = new DS4State[MAX_DS4_CONTROLLER_COUNT];
    private DS4State[] PreviousState = new DS4State[MAX_DS4_CONTROLLER_COUNT];
    @@ -206,6 +208,8 @@ namespace DS4Windows
    CurrentState[i] = new DS4State();
    TempState[i] = new DS4State();
    PreviousState[i] = new DS4State();
    + tempInputMappedState[i] = new DS4State();
    + inputMergeLock[i] = new ReaderWriterLockSlim();
    ExposedState[i] = new DS4StateExposed(CurrentState[i]);

    int tempDev = i;
    @@ -631,11 +635,22 @@ namespace DS4Windows
    if (contType == OutContType.X360)
    {
    Xbox360OutDevice tempXbox = outDevice as Xbox360OutDevice;
    + using (WriteLocker locker = new WriteLocker(tempXbox.feedbackDevColLock))
    + {
    + tempXbox.receiveFeedbackDevices.Add(device);
    + }
    +
    Nefarius.ViGEm.Client.Targets.Xbox360FeedbackReceivedEventHandler p = (sender, args) =>
    {
    //Console.WriteLine("Rumble ({0}, {1}) {2}",
    // args.LargeMotor, args.SmallMotor, DateTime.Now.ToString("hh:mm:ss.FFFF"));
    - SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex);
    + using (ReadLocker locker = new ReadLocker(tempXbox.feedbackDevColLock))
    + {
    + foreach (DS4Device tmpdevice in tempXbox.receiveFeedbackDevices)
    + {
    + SetDevRumble(tmpdevice, args.LargeMotor, args.SmallMotor, tmpdevice.DeviceSlotNumber);
    + }
    + }
    };
    tempXbox.cont.FeedbackReceived += p;
    tempXbox.forceFeedbackCall = p;
    @@ -1030,7 +1045,15 @@ namespace DS4Windows
    if (!getDInputOnly(i) && device.isSynced())
    {
    //useDInputOnly[i] = false;
    - PluginOutDev(i, device);
    + if (i == 0)
    + {
    + PluginOutDev(i, device);
    + }
    + else
    + {
    + Xbox360OutDevice tempXbox = outputDevices[0] as Xbox360OutDevice;
    + tempXbox.receiveFeedbackDevices.Add(device);
    + }
    }
    else
    {
    @@ -1368,7 +1391,18 @@ namespace DS4Windows
    if (!getDInputOnly(Index) && device.isSynced())
    {
    //useDInputOnly[Index] = false;
    - PluginOutDev(Index, device);
    + if (Index == 0)
    + {
    + PluginOutDev(Index, device);
    + }
    + else
    + {
    + Xbox360OutDevice tempXbox = outputDevices[0] as Xbox360OutDevice;
    + using (WriteLocker locker = new WriteLocker(tempXbox.feedbackDevColLock))
    + {
    + tempXbox.receiveFeedbackDevices.Add(device);
    + }
    + }
    }
    else
    {
    @@ -1921,8 +1955,24 @@ namespace DS4Windows
    containsCustomAction(ind) || containsCustomExtras(ind) ||
    getProfileActionCount(ind) > 0))
    {
    - DS4State tempMapState = MappedState[ind];
    - Mapping.MapCustom(ind, cState, tempMapState, ExposedState[ind], touchPad[ind], this);
    + DS4State tempMapState;
    +
    + if (device.DeviceType == InputDevices.InputDeviceType.JoyConL ||
    + device.DeviceType == InputDevices.InputDeviceType.JoyConR)
    + {
    + using (WriteLocker locker = new WriteLocker(inputMergeLock[0]))
    + {
    + tempMapState = MappedState[0];
    + DS4State tempInputState = tempInputMappedState[0];
    + MergeStateData(device, cState, tempInputState);
    + Mapping.MapCustom(ind, tempInputState, tempMapState, ExposedState[ind], touchPad[ind], this);
    + }
    + }
    + else
    + {
    + tempMapState = MappedState[ind];
    + Mapping.MapCustom(ind, cState, tempMapState, ExposedState[ind], touchPad[ind], this);
    + }

    // Copy current Touchpad and Gyro data
    tempMapState.Motion = cState.Motion;
    @@ -2204,5 +2254,41 @@ namespace DS4Windows
    {
    return TempState[ind];
    }
    +
    + public void MergeStateData(DS4Device inputDevice, DS4State cState,
    + DS4State dState)
    + {
    + if (inputDevice.DeviceType == InputDevices.InputDeviceType.JoyConL)
    + {
    + dState.LX = cState.LX;
    + dState.LY = cState.LY;
    + dState.L1 = cState.L1;
    + dState.L2 = cState.L2;
    + dState.L3 = cState.L3;
    + dState.L2Btn = cState.L2Btn;
    + dState.DpadUp = cState.DpadUp;
    + dState.DpadDown = cState.DpadDown;
    + dState.DpadLeft = cState.DpadLeft;
    + dState.DpadRight = cState.DpadRight;
    + dState.Share = cState.Share;
    + dState.Motion = cState.Motion;
    + }
    + else if (inputDevice.DeviceType == InputDevices.InputDeviceType.JoyConR)
    + {
    + dState.RX = cState.RX;
    + dState.RY = cState.RY;
    + dState.R1 = cState.R1;
    + dState.R2 = cState.R2;
    + dState.R3 = cState.R3;
    + dState.R2Btn = cState.R2Btn;
    + dState.Cross = cState.Cross;
    + dState.Circle = cState.Circle;
    + dState.Triangle = cState.Triangle;
    + dState.Square = cState.Square;
    + dState.PS = cState.PS;
    + dState.Options = cState.Options;
    + dState.Motion = cState.Motion;
    + }
    + }
    }
    }
    diff --git a/DS4Windows/DS4Control/Xbox360OutDevice.cs b/DS4Windows/DS4Control/Xbox360OutDevice.cs
    index 6c4df1fc..b5d0acab 100644
    --- a/DS4Windows/DS4Control/Xbox360OutDevice.cs
    +++ b/DS4Windows/DS4Control/Xbox360OutDevice.cs
    @@ -2,6 +2,7 @@
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    +using System.Threading;
    using System.Threading.Tasks;
    using Nefarius.ViGEm.Client;
    using Nefarius.ViGEm.Client.Targets;
    @@ -20,6 +21,8 @@ namespace DS4Windows

    public IXbox360Controller cont;
    public Xbox360FeedbackReceivedEventHandler forceFeedbackCall;
    + public List<DS4Device> receiveFeedbackDevices = new List<DS4Device>();
    + public ReaderWriterLockSlim feedbackDevColLock = new ReaderWriterLockSlim();

    public Xbox360OutDevice(ViGEmClient client)
    {
    701 changes: 701 additions & 0 deletions mergejoycondraft3.diff
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,701 @@
    diff --git a/DS4Windows/DS4Control/ControlService.cs b/DS4Windows/DS4Control/ControlService.cs
    index a47dfb0b..1540527d 100644
    --- a/DS4Windows/DS4Control/ControlService.cs
    +++ b/DS4Windows/DS4Control/ControlService.cs
    @@ -10,6 +10,7 @@ using System.Windows.Threading;
    using DS4WinWPF.DS4Control;
    using Microsoft.Win32;
    using Sensorit.Base;
    +using System.Linq;

    namespace DS4Windows
    {
    @@ -623,7 +624,7 @@ namespace DS4Windows
    return temp;
    }

    - private void EstablishOutFeedback(int index, OutContType contType,
    + public void EstablishOutFeedback(int index, OutContType contType,
    OutputDevice outDevice, DS4Device device)
    {
    int devIndex = index;
    @@ -638,7 +639,7 @@ namespace DS4Windows
    SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex);
    };
    tempXbox.cont.FeedbackReceived += p;
    - tempXbox.forceFeedbackCall = p;
    + tempXbox.forceFeedbackCall.Add(index, p);
    }
    else if (contType == OutContType.DS4)
    {
    @@ -717,13 +718,15 @@ namespace DS4Windows
    }
    }

    - public void RemoveOutFeedback(OutContType contType, OutputDevice outDevice)
    + public void RemoveOutFeedback(OutContType contType, OutputDevice outDevice, int inIdx)
    {
    if (contType == OutContType.X360)
    {
    Xbox360OutDevice tempXbox = outDevice as Xbox360OutDevice;
    - tempXbox.cont.FeedbackReceived -= tempXbox.forceFeedbackCall;
    - tempXbox.forceFeedbackCall = null;
    + tempXbox.cont.FeedbackReceived -= tempXbox.forceFeedbackCall[inIdx];
    + tempXbox.forceFeedbackCall.Remove(inIdx);
    + //tempXbox.cont.FeedbackReceived -= tempXbox.forceFeedbackCall;
    + //tempXbox.forceFeedbackCall = null;
    }
    else if (contType == OutContType.DS4)
    {
    @@ -790,7 +793,17 @@ namespace DS4Windows

    // Enable ViGem feedback callback handler only if lightbar/rumble data output is enabled (if those are disabled then no point enabling ViGem callback handler call)
    if (Global.EnableOutputDataToDS4[index])
    + {
    EstablishOutFeedback(index, OutContType.X360, tempXbox, device);
    + if (device.JointDeviceSlotNumber != -1)
    + {
    + DS4Device tempDS4Device = DS4Controllers[device.JointDeviceSlotNumber];
    + if (tempDS4Device != null)
    + {
    + EstablishOutFeedback(device.JointDeviceSlotNumber, OutContType.X360, tempXbox, tempDS4Device);
    + }
    + }
    + }

    outputslotMan.DeferredPlugin(tempXbox, index, outputDevices, contType);
    //slotDevice.CurrentInputBound = OutSlotDevice.InputBound.Bound;
    @@ -810,8 +823,19 @@ namespace DS4Windows

    // Enable ViGem feedback callback handler only if lightbar/rumble data output is enabled (if those are disabled then no point enabling ViGem callback handler call)
    if (Global.EnableOutputDataToDS4[index])
    + {
    EstablishOutFeedback(index, OutContType.X360, tempXbox, device);

    + if (device.JointDeviceSlotNumber != -1)
    + {
    + DS4Device tempDS4Device = DS4Controllers[device.JointDeviceSlotNumber];
    + if (tempDS4Device != null)
    + {
    + EstablishOutFeedback(device.JointDeviceSlotNumber, OutContType.X360, tempXbox, tempDS4Device);
    + }
    + }
    + }
    +
    outputslotMan.EventDispatcher.BeginInvoke((Action)(() =>
    {
    outputDevices[index] = tempXbox;
    @@ -838,8 +862,19 @@ namespace DS4Windows

    // Enable ViGem feedback callback handler only if DS4 lightbar/rumble data output is enabled (if those are disabled then no point enabling ViGem callback handler call)
    if (Global.EnableOutputDataToDS4[index])
    + {
    EstablishOutFeedback(index, OutContType.DS4, tempDS4, device);
    -
    +
    + if (device.JointDeviceSlotNumber != -1)
    + {
    + DS4Device tempDS4Device = DS4Controllers[device.JointDeviceSlotNumber];
    + if (tempDS4Device != null)
    + {
    + EstablishOutFeedback(device.JointDeviceSlotNumber, OutContType.DS4, tempDS4, tempDS4Device);
    + }
    + }
    + }
    +
    outputslotMan.DeferredPlugin(tempDS4, index, outputDevices, contType);
    //slotDevice.CurrentInputBound = OutSlotDevice.InputBound.Bound;

    @@ -858,8 +893,19 @@ namespace DS4Windows

    // Enable ViGem feedback callback handler only if lightbar/rumble data output is enabled (if those are disabled then no point enabling ViGem callback handler call)
    if (Global.EnableOutputDataToDS4[index])
    + {
    EstablishOutFeedback(index, OutContType.DS4, tempDS4, device);

    + if (device.JointDeviceSlotNumber != -1)
    + {
    + DS4Device tempDS4Device = DS4Controllers[device.JointDeviceSlotNumber];
    + if (tempDS4Device != null)
    + {
    + EstablishOutFeedback(device.JointDeviceSlotNumber, OutContType.DS4, tempDS4, tempDS4Device);
    + }
    + }
    + }
    +
    outputslotMan.EventDispatcher.BeginInvoke((Action)(() =>
    {
    outputDevices[index] = tempDS4;
    @@ -912,7 +958,8 @@ namespace DS4Windows
    {
    slotDevice.CurrentInputBound = OutSlotDevice.InputBound.Unbound;
    dev.ResetState();
    - RemoveOutFeedback(currentType, dev);
    + dev.RemoveFeedbacks();
    + //RemoveOutFeedback(currentType, dev, index);
    }
    //dev.Disconnect();
    //LogDebug(tempType + " Controller # " + (index + 1) + " unplugged");
    @@ -974,6 +1021,7 @@ namespace DS4Windows

    //for (int i = 0, devCount = devices.Count(); i < devCount; i++)
    int i = 0;
    + InputDevices.JoyConDevice tempJoyDev = null;
    for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext() && loopControllers; i++)
    {
    DS4Device device = devEnum.Current;
    @@ -987,6 +1035,30 @@ namespace DS4Windows
    device.CurrentExclusiveStatus = DS4Device.ExclusiveStatus.HidGuardAffected;
    }

    + if (device.DeviceType == InputDevices.InputDeviceType.JoyConL ||
    + device.DeviceType == InputDevices.InputDeviceType.JoyConR)
    + {
    + if (tempJoyDev == null)
    + {
    + tempJoyDev = device as InputDevices.JoyConDevice;
    + }
    + else
    + {
    + InputDevices.JoyConDevice currentJoyDev = device as InputDevices.JoyConDevice;
    + tempJoyDev.PerformStateMerge = true;
    + tempJoyDev.JointDevice = currentJoyDev;
    + currentJoyDev.PerformStateMerge = true;
    + currentJoyDev.JointDevice = tempJoyDev;
    + currentJoyDev.PrimaryDevice = false;
    +
    + tempJoyDev.JointState = currentJoyDev.JointState;
    + InputDevices.JoyConDevice parentJoy = tempJoyDev;
    + currentJoyDev.Removal += (sender, args) => { parentJoy.JointDevice = null; };
    +
    + tempJoyDev = null;
    + }
    + }
    +
    Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); });
    task.Start();

    @@ -1030,7 +1102,28 @@ namespace DS4Windows
    if (!getDInputOnly(i) && device.isSynced())
    {
    //useDInputOnly[i] = false;
    - PluginOutDev(i, device);
    + if (!device.PerformStateMerge)
    + {
    + PluginOutDev(i, device);
    + }
    + else
    + {
    + int otherIdx = (device as InputDevices.JoyConDevice).JointDevice.DeviceSlotNumber;
    + OutputDevice tempOutDev = outputDevices[otherIdx];
    + if (tempOutDev != null)
    + {
    + OutContType tempConType = activeOutDevType[otherIdx];
    + EstablishOutFeedback(i, tempConType, tempOutDev, device);
    + outputDevices[i] = tempOutDev;
    + Global.activeOutDevType[i] = tempConType;
    +
    + //useDInputOnly[i] = false;
    + //Global.activeOutDevType[i] = OutContType.X360;
    + }
    +
    + //useDInputOnly[i] = true;
    + //Global.activeOutDevType[i] = OutContType.None;
    + }
    }
    else
    {
    @@ -1038,7 +1131,11 @@ namespace DS4Windows
    Global.activeOutDevType[i] = OutContType.None;
    }

    - TouchPadOn(i, device);
    + if (device.PrimaryDevice)
    + {
    + TouchPadOn(i, device);
    + }
    +
    CheckProfileOptions(i, device, true);
    }

    @@ -1048,7 +1145,7 @@ namespace DS4Windows
    this.On_Report(sender, e, tempIdx);
    };

    - if (_udpServer != null && i < UdpServer.NUMBER_SLOTS)
    + if (_udpServer != null && i < UdpServer.NUMBER_SLOTS && device.PerformStateMerge)
    {
    DS4Device.ReportHandler<EventArgs> tempEvnt = (sender, args) =>
    {
    @@ -1287,6 +1384,10 @@ namespace DS4Windows
    activeControllers = numControllers;
    //foreach (DS4Device device in devices)
    //for (int i = 0, devlen = devices.Count(); i < devlen; i++)
    + InputDevices.JoyConDevice tempJoyDev = devices.Where(d =>
    + (d.DeviceType == InputDevices.InputDeviceType.JoyConL || d.DeviceType == InputDevices.InputDeviceType.JoyConR)
    + && d.PrimaryDevice && d.JointDeviceSlotNumber == -1).FirstOrDefault() as InputDevices.JoyConDevice;
    +
    for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext() && loopControllers;)
    {
    DS4Device device = devEnum.Current;
    @@ -1326,6 +1427,26 @@ namespace DS4Windows
    device.CurrentExclusiveStatus = DS4Device.ExclusiveStatus.HidGuardAffected;
    }

    + if (device.DeviceType == InputDevices.InputDeviceType.JoyConL ||
    + device.DeviceType == InputDevices.InputDeviceType.JoyConR)
    + {
    + if (tempJoyDev != null)
    + {
    + InputDevices.JoyConDevice currentJoyDev = device as InputDevices.JoyConDevice;
    + tempJoyDev.PerformStateMerge = true;
    + tempJoyDev.JointDevice = currentJoyDev;
    + currentJoyDev.PerformStateMerge = true;
    + currentJoyDev.JointDevice = tempJoyDev;
    + currentJoyDev.PrimaryDevice = false;
    +
    + tempJoyDev.JointState = currentJoyDev.JointState;
    + InputDevices.JoyConDevice parentJoy = tempJoyDev;
    + currentJoyDev.Removal += (sender, args) => { parentJoy.JointDevice = null; };
    +
    + tempJoyDev = null;
    + }
    + }
    +
    Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); });
    task.Start();
    DS4Controllers[Index] = device;
    @@ -1368,7 +1489,23 @@ namespace DS4Windows
    if (!getDInputOnly(Index) && device.isSynced())
    {
    //useDInputOnly[Index] = false;
    - PluginOutDev(Index, device);
    + if (device.PrimaryDevice)
    + {
    + PluginOutDev(Index, device);
    + }
    + else
    + {
    + int otherIdx = (device as InputDevices.JoyConDevice).JointDevice.DeviceSlotNumber;
    + Xbox360OutDevice tempXbox = outputDevices[otherIdx] as Xbox360OutDevice;
    + if (tempXbox != null)
    + {
    + EstablishOutFeedback(Index, OutContType.X360, tempXbox, device);
    + outputDevices[Index] = tempXbox;
    + }
    +
    + //useDInputOnly[Index] = true;
    + //Global.activeOutDevType[Index] = OutContType.None;
    + }
    }
    else
    {
    @@ -1376,7 +1513,11 @@ namespace DS4Windows
    Global.activeOutDevType[Index] = OutContType.None;
    }

    - TouchPadOn(Index, device);
    + if (device.PrimaryDevice)
    + {
    + TouchPadOn(Index, device);
    + }
    +
    CheckProfileOptions(Index, device);
    }

    @@ -1766,6 +1907,15 @@ namespace DS4Windows
    {
    UnplugOutDev(ind, device);
    }
    + else if (!device.PrimaryDevice)
    + {
    + OutputDevice outDev = outputDevices[ind];
    + if (outDev != null)
    + {
    + outDev.RemoveFeedback(ind);
    + outputDevices[ind] = null;
    + }
    + }

    // Use Task to reset device synth state and commit it
    Task.Run(() =>
    @@ -1873,8 +2023,18 @@ namespace DS4Windows
    }
    }

    - device.getCurrentState(CurrentState[ind]);
    - DS4State cState = CurrentState[ind];
    + DS4State cState;
    + if (!device.PerformStateMerge)
    + {
    + cState = CurrentState[ind];
    + device.getCurrentState(cState);
    + }
    + else
    + {
    + cState = (device as InputDevices.JoyConDevice).JointState;
    + device.MergeStateData(cState);
    + }
    +
    DS4State pState = device.getPreviousStateRef();
    //device.getPreviousState(PreviousState[ind]);
    //DS4State pState = PreviousState[ind];
    @@ -1912,6 +2072,12 @@ namespace DS4Windows
    // */
    //}

    + if (!device.PrimaryDevice)
    + {
    + // Skip mapping routine if part of a joined device
    + return;
    + }
    +
    if (getEnableTouchToggle(ind))
    CheckForTouchToggle(ind, cState, pState);

    diff --git a/DS4Windows/DS4Control/DS4OutDevice.cs b/DS4Windows/DS4Control/DS4OutDevice.cs
    index 8dab686a..9b1248a1 100644
    --- a/DS4Windows/DS4Control/DS4OutDevice.cs
    +++ b/DS4Windows/DS4Control/DS4OutDevice.cs
    @@ -42,5 +42,18 @@ namespace DS4Windows
    cont = null;
    }
    public override string GetDeviceType() => devtype;
    +
    + public override void RemoveFeedbacks()
    + {
    + if (forceFeedbackCall != null)
    + {
    + cont.FeedbackReceived -= forceFeedbackCall;
    + forceFeedbackCall = null;
    + }
    + }
    +
    + public override void RemoveFeedback(int inIdx)
    + {
    + }
    }
    }
    diff --git a/DS4Windows/DS4Control/OutputDevice.cs b/DS4Windows/DS4Control/OutputDevice.cs
    index ada31e46..154f650e 100644
    --- a/DS4Windows/DS4Control/OutputDevice.cs
    +++ b/DS4Windows/DS4Control/OutputDevice.cs
    @@ -11,5 +11,9 @@ namespace DS4Windows
    public abstract void Disconnect();
    public abstract void ResetState(bool submit=true);
    public abstract string GetDeviceType();
    +
    + public abstract void RemoveFeedbacks();
    +
    + public abstract void RemoveFeedback(int inIdx);
    }
    }
    diff --git a/DS4Windows/DS4Control/ScpUtil.cs b/DS4Windows/DS4Control/ScpUtil.cs
    index 97380f13..138c87ac 100644
    --- a/DS4Windows/DS4Control/ScpUtil.cs
    +++ b/DS4Windows/DS4Control/ScpUtil.cs
    @@ -5083,8 +5083,12 @@ namespace DS4Windows
    // performing this upon program startup before loading devices.
    if (xinputChange)
    {
    - CheckOldDevicestatus(device, control, oldContType,
    - out xinputPlug, out xinputStatus);
    + DS4Device tempDev = control.DS4Controllers[device];
    + if (tempDev != null)
    + {
    + CheckOldDevicestatus(device, control, oldContType,
    + out xinputPlug, out xinputStatus);
    + }
    }

    try
    @@ -7062,26 +7066,61 @@ namespace DS4Windows
    {
    tempDev.setIdleTimeout(idleDisconnectTimeout[device]);
    tempDev.setBTPollRate(btPollRate[device]);
    - if (xinputStatus && xinputPlug)
    + if (xinputStatus && tempDev.PrimaryDevice)
    {
    - OutputDevice tempOutDev = control.outputDevices[device];
    - if (tempOutDev != null)
    + if (xinputPlug)
    + {
    + OutputDevice tempOutDev = control.outputDevices[device];
    + if (tempOutDev != null)
    + {
    + tempOutDev = null;
    + //Global.activeOutDevType[device] = OutContType.None;
    + control.UnplugOutDev(device, tempDev);
    + }
    +
    + OutContType tempContType = outputDevType[device];
    + control.PluginOutDev(device, tempDev);
    + //Global.useDInputOnly[device] = false;
    + }
    + else
    {
    - tempOutDev = null;
    //Global.activeOutDevType[device] = OutContType.None;
    control.UnplugOutDev(device, tempDev);
    }
    -
    - OutContType tempContType = outputDevType[device];
    - control.PluginOutDev(device, tempDev);
    - //Global.useDInputOnly[device] = false;
    -
    }
    - else if (xinputStatus && !xinputPlug)
    + /*else if (xinputStatus && !tempDev.PrimaryDevice)
    {
    - //Global.activeOutDevType[device] = OutContType.None;
    - control.UnplugOutDev(device, tempDev);
    + OutputDevice tempOutDev = control.outputDevices[device];
    + if (xinputPlug)
    + {
    + if (tempOutDev != null)
    + {
    + tempOutDev.RemoveFeedback(device);
    + control.outputDevices[device] = null;
    + Global.activeOutDevType[device] = OutContType.None;
    + }
    +
    + int tempJointDeviceSlot = tempDev.JointDeviceSlotNumber;
    + tempOutDev = tempJointDeviceSlot != -1 ? control.outputDevices[tempDev.JointDeviceSlotNumber] : null;
    + if (tempOutDev != null && outputDevType[device] == Global.activeOutDevType[tempJointDeviceSlot])
    + {
    + OutContType tempContType = outputDevType[device];
    + control.EstablishOutFeedback(device, tempContType, tempOutDev, tempDev);
    + control.outputDevices[device] = tempOutDev;
    + Global.activeOutDevType[device] = outputDevType[device];
    + }
    + }
    + else
    + {
    + if (tempOutDev != null)
    + {
    + tempOutDev.RemoveFeedback(device);
    + control.outputDevices[device] = null;
    + Global.activeOutDevType[device] = OutContType.None;
    + }
    + }
    }
    + */

    tempDev.RumbleAutostopTime = rumbleAutostopTime[device];
    tempDev.setRumble(0, 0);
    diff --git a/DS4Windows/DS4Control/Xbox360OutDevice.cs b/DS4Windows/DS4Control/Xbox360OutDevice.cs
    index 6c4df1fc..6f736e78 100644
    --- a/DS4Windows/DS4Control/Xbox360OutDevice.cs
    +++ b/DS4Windows/DS4Control/Xbox360OutDevice.cs
    @@ -2,6 +2,7 @@
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    +using System.Threading;
    using System.Threading.Tasks;
    using Nefarius.ViGEm.Client;
    using Nefarius.ViGEm.Client.Targets;
    @@ -19,7 +20,10 @@ namespace DS4Windows
    public const string devType = "X360";

    public IXbox360Controller cont;
    - public Xbox360FeedbackReceivedEventHandler forceFeedbackCall;
    + // Input index, Xbox360FeedbackReceivedEventHandler instance
    + public Dictionary<int, Xbox360FeedbackReceivedEventHandler> forceFeedbackCall =
    + new Dictionary<int, Xbox360FeedbackReceivedEventHandler>();
    + private ReaderWriterLockSlim lockSlim = new ReaderWriterLockSlim();

    public Xbox360OutDevice(ViGEmClient client)
    {
    @@ -32,6 +36,7 @@ namespace DS4Windows
    if (!connected) return;

    //cont.ResetReport();
    +
    ushort tempButtons = 0;

    unchecked
    @@ -125,6 +130,7 @@ namespace DS4Windows
    }

    cont.SubmitReport();
    +
    }

    private short AxisScale(Int32 Value, Boolean Flip)
    @@ -152,8 +158,13 @@ namespace DS4Windows
    {
    if (forceFeedbackCall != null)
    {
    - cont.FeedbackReceived -= forceFeedbackCall;
    - forceFeedbackCall = null;
    + foreach(KeyValuePair<int, Xbox360FeedbackReceivedEventHandler> pair in forceFeedbackCall)
    + {
    + cont.FeedbackReceived -= pair.Value;
    + }
    +
    + //forceFeedbackCall = null;
    + forceFeedbackCall.Clear();
    }

    connected = false;
    @@ -170,5 +181,25 @@ namespace DS4Windows
    cont.SubmitReport();
    }
    }
    +
    + public override void RemoveFeedbacks()
    + {
    + foreach (KeyValuePair<int, Xbox360FeedbackReceivedEventHandler> pair in forceFeedbackCall)
    + {
    + cont.FeedbackReceived -= pair.Value;
    + }
    +
    + //forceFeedbackCall = null;
    + forceFeedbackCall.Clear();
    + }
    +
    + public override void RemoveFeedback(int inIdx)
    + {
    + if (forceFeedbackCall.TryGetValue(inIdx, out Xbox360FeedbackReceivedEventHandler handler))
    + {
    + cont.FeedbackReceived -= handler;
    + forceFeedbackCall.Remove(inIdx);
    + }
    + }
    }
    }
    diff --git a/DS4Windows/DS4Library/DS4Device.cs b/DS4Windows/DS4Library/DS4Device.cs
    index 484e7312..e022c6b1 100644
    --- a/DS4Windows/DS4Library/DS4Device.cs
    +++ b/DS4Windows/DS4Library/DS4Device.cs
    @@ -578,6 +578,26 @@ namespace DS4Windows
    protected event EventHandler DeviceSlotNumberChanged;
    protected byte deviceSlotMask = 0x00;

    + protected bool performStateMerge;
    + public bool PerformStateMerge
    + {
    + get => performStateMerge; set => performStateMerge = value;
    + }
    +
    + protected bool primaryDevice = true;
    + public bool PrimaryDevice
    + {
    + get => primaryDevice;
    + set => primaryDevice = value;
    + }
    +
    + protected int jointDeviceSlotNumber = -1;
    + public virtual int JointDeviceSlotNumber
    + {
    + get => jointDeviceSlotNumber;
    + set => jointDeviceSlotNumber = value;
    + }
    +
    public DS4Device(HidDevice hidDevice, string disName, VidPidFeatureSet featureSet = VidPidFeatureSet.DefaultDS4)
    {
    hDevice = hidDevice;
    @@ -2055,5 +2075,9 @@ namespace DS4Windows
    PrepareOutputFeaturesByte();
    }
    }
    +
    + public virtual void MergeStateData(DS4State dState)
    + {
    + }
    }
    }
    diff --git a/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs b/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    index 92325d77..8563cde7 100644
    --- a/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    +++ b/DS4Windows/DS4Library/InputDevices/JoyConDevice.cs
    @@ -214,6 +214,42 @@ namespace DS4Windows.InputDevices
    public bool EnableHomeLED { get => enableHomeLED; set => enableHomeLED = value; }

    private JoyConControllerOptions nativeOptionsStore;
    + private DS4State jointState = new DS4State();
    + public DS4State JointState
    + {
    + get => jointState; set => jointState = value;
    + }
    +
    + private ReaderWriterLockSlim lockSlim = new ReaderWriterLockSlim();
    + private JoyConDevice jointDevice;
    + public JoyConDevice JointDevice
    + {
    + get => jointDevice;
    + set
    + {
    + jointDevice = value;
    + if (jointDevice == null)
    + {
    + }
    + else
    + {
    + }
    + }
    + }
    +
    + public override int JointDeviceSlotNumber
    + {
    + get
    + {
    + int result = -1;
    + if (jointDevice != null)
    + {
    + result = jointDevice.deviceSlotNumber;
    + }
    +
    + return result;
    + }
    + }

    public override event ReportHandler<EventArgs> Report = null;
    public override event EventHandler<EventArgs> Removal = null;
    @@ -1288,5 +1324,45 @@ namespace DS4Windows.InputDevices
    enableHomeLED = nativeOptionsStore.EnableHomeLED;
    }
    }
    +
    + public override void MergeStateData(DS4State dState)
    + {
    + using (WriteLocker locker = new WriteLocker(lockSlim))
    + {
    + if (DeviceType == InputDeviceType.JoyConL)
    + {
    + dState.LX = cState.LX;
    + dState.LY = cState.LY;
    + dState.L1 = cState.L1;
    + dState.L2 = cState.L2;
    + dState.L3 = cState.L3;
    + dState.L2Btn = cState.L2Btn;
    + dState.DpadUp = cState.DpadUp;
    + dState.DpadDown = cState.DpadDown;
    + dState.DpadLeft = cState.DpadLeft;
    + dState.DpadRight = cState.DpadRight;
    + dState.Share = cState.Share;
    + if (primaryDevice) dState.Motion = cState.Motion;
    + //dState.Motion = cState.Motion;
    + }
    + else if (DeviceType == InputDeviceType.JoyConR)
    + {
    + dState.RX = cState.RX;
    + dState.RY = cState.RY;
    + dState.R1 = cState.R1;
    + dState.R2 = cState.R2;
    + dState.R3 = cState.R3;
    + dState.R2Btn = cState.R2Btn;
    + dState.Cross = cState.Cross;
    + dState.Circle = cState.Circle;
    + dState.Triangle = cState.Triangle;
    + dState.Square = cState.Square;
    + dState.PS = cState.PS;
    + dState.Options = cState.Options;
    + if (primaryDevice) dState.Motion = cState.Motion;
    + //dState.Motion = cState.Motion;
    + }
    + }
    + }
    }
    }
  8. Ryochan7 created this gist Feb 28, 2021.
    173 changes: 173 additions & 0 deletions mergejoycondraft1.diff
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,173 @@
    diff --git a/DS4Windows/DS4Control/ControlService.cs b/DS4Windows/DS4Control/ControlService.cs
    index a47dfb0b..b7aec6c8 100644
    --- a/DS4Windows/DS4Control/ControlService.cs
    +++ b/DS4Windows/DS4Control/ControlService.cs
    @@ -31,6 +31,8 @@ namespace DS4Windows
    public bool running = false;
    public bool loopControllers = true;
    public bool inServiceTask = false;
    + private ReaderWriterLockSlim[] inputMergeLock = new ReaderWriterLockSlim[MAX_DS4_CONTROLLER_COUNT];
    + private DS4State[] tempInputMappedState = new DS4State[MAX_DS4_CONTROLLER_COUNT];
    private DS4State[] MappedState = new DS4State[MAX_DS4_CONTROLLER_COUNT];
    private DS4State[] CurrentState = new DS4State[MAX_DS4_CONTROLLER_COUNT];
    private DS4State[] PreviousState = new DS4State[MAX_DS4_CONTROLLER_COUNT];
    @@ -206,6 +208,8 @@ namespace DS4Windows
    CurrentState[i] = new DS4State();
    TempState[i] = new DS4State();
    PreviousState[i] = new DS4State();
    + tempInputMappedState[i] = new DS4State();
    + inputMergeLock[i] = new ReaderWriterLockSlim();
    ExposedState[i] = new DS4StateExposed(CurrentState[i]);

    int tempDev = i;
    @@ -631,11 +635,22 @@ namespace DS4Windows
    if (contType == OutContType.X360)
    {
    Xbox360OutDevice tempXbox = outDevice as Xbox360OutDevice;
    + using (WriteLocker locker = new WriteLocker(tempXbox.feedbackDevColLock))
    + {
    + tempXbox.receiveFeedbackDevices.Add(device);
    + }
    +
    Nefarius.ViGEm.Client.Targets.Xbox360FeedbackReceivedEventHandler p = (sender, args) =>
    {
    //Console.WriteLine("Rumble ({0}, {1}) {2}",
    // args.LargeMotor, args.SmallMotor, DateTime.Now.ToString("hh:mm:ss.FFFF"));
    - SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex);
    + using (ReadLocker locker = new ReadLocker(tempXbox.feedbackDevColLock))
    + {
    + foreach (DS4Device tmpdevice in tempXbox.receiveFeedbackDevices)
    + {
    + SetDevRumble(tmpdevice, args.LargeMotor, args.SmallMotor, tmpdevice.DeviceSlotNumber);
    + }
    + }
    };
    tempXbox.cont.FeedbackReceived += p;
    tempXbox.forceFeedbackCall = p;
    @@ -1030,7 +1045,15 @@ namespace DS4Windows
    if (!getDInputOnly(i) && device.isSynced())
    {
    //useDInputOnly[i] = false;
    - PluginOutDev(i, device);
    + if (i == 0)
    + {
    + PluginOutDev(i, device);
    + }
    + else
    + {
    + Xbox360OutDevice tempXbox = outputDevices[0] as Xbox360OutDevice;
    + tempXbox.receiveFeedbackDevices.Add(device);
    + }
    }
    else
    {
    @@ -1368,7 +1391,18 @@ namespace DS4Windows
    if (!getDInputOnly(Index) && device.isSynced())
    {
    //useDInputOnly[Index] = false;
    - PluginOutDev(Index, device);
    + if (Index == 0)
    + {
    + PluginOutDev(Index, device);
    + }
    + else
    + {
    + Xbox360OutDevice tempXbox = outputDevices[0] as Xbox360OutDevice;
    + using (WriteLocker locker = new WriteLocker(tempXbox.feedbackDevColLock))
    + {
    + tempXbox.receiveFeedbackDevices.Add(device);
    + }
    + }
    }
    else
    {
    @@ -1921,8 +1955,24 @@ namespace DS4Windows
    containsCustomAction(ind) || containsCustomExtras(ind) ||
    getProfileActionCount(ind) > 0))
    {
    - DS4State tempMapState = MappedState[ind];
    - Mapping.MapCustom(ind, cState, tempMapState, ExposedState[ind], touchPad[ind], this);
    + DS4State tempMapState;
    +
    + if (device.DeviceType == InputDevices.InputDeviceType.JoyConL ||
    + device.DeviceType == InputDevices.InputDeviceType.JoyConR)
    + {
    + using (WriteLocker locker = new WriteLocker(inputMergeLock[0]))
    + {
    + tempMapState = MappedState[0];
    + DS4State tempInputState = tempInputMappedState[0];
    + MergeStateData(device, cState, tempInputState);
    + Mapping.MapCustom(ind, tempInputState, tempMapState, ExposedState[ind], touchPad[ind], this);
    + }
    + }
    + else
    + {
    + tempMapState = MappedState[ind];
    + Mapping.MapCustom(ind, cState, tempMapState, ExposedState[ind], touchPad[ind], this);
    + }

    // Copy current Touchpad and Gyro data
    tempMapState.Motion = cState.Motion;
    @@ -2204,5 +2254,41 @@ namespace DS4Windows
    {
    return TempState[ind];
    }
    +
    + public void MergeStateData(DS4Device inputDevice, DS4State cState,
    + DS4State dState)
    + {
    + if (inputDevice.DeviceType == InputDevices.InputDeviceType.JoyConL)
    + {
    + dState.LX = cState.LX;
    + dState.LY = cState.LY;
    + dState.L1 = cState.L1;
    + dState.L2 = cState.L2;
    + dState.L3 = cState.L3;
    + dState.L2Btn = cState.L2Btn;
    + dState.DpadUp = cState.DpadUp;
    + dState.DpadDown = cState.DpadDown;
    + dState.DpadLeft = cState.DpadLeft;
    + dState.DpadRight = cState.DpadRight;
    + dState.Share = cState.Share;
    + dState.Motion = cState.Motion;
    + }
    + else if (inputDevice.DeviceType == InputDevices.InputDeviceType.JoyConR)
    + {
    + dState.RX = cState.RX;
    + dState.RY = cState.RY;
    + dState.R1 = cState.R1;
    + dState.R2 = cState.R2;
    + dState.R3 = cState.R3;
    + dState.R2Btn = cState.R2Btn;
    + dState.Cross = cState.Cross;
    + dState.Circle = cState.Circle;
    + dState.Triangle = cState.Triangle;
    + dState.Square = cState.Square;
    + dState.PS = cState.PS;
    + dState.Options = cState.Options;
    + dState.Motion = cState.Motion;
    + }
    + }
    }
    }
    diff --git a/DS4Windows/DS4Control/Xbox360OutDevice.cs b/DS4Windows/DS4Control/Xbox360OutDevice.cs
    index 6c4df1fc..b5d0acab 100644
    --- a/DS4Windows/DS4Control/Xbox360OutDevice.cs
    +++ b/DS4Windows/DS4Control/Xbox360OutDevice.cs
    @@ -2,6 +2,7 @@
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    +using System.Threading;
    using System.Threading.Tasks;
    using Nefarius.ViGEm.Client;
    using Nefarius.ViGEm.Client.Targets;
    @@ -20,6 +21,8 @@ namespace DS4Windows

    public IXbox360Controller cont;
    public Xbox360FeedbackReceivedEventHandler forceFeedbackCall;
    + public List<DS4Device> receiveFeedbackDevices = new List<DS4Device>();
    + public ReaderWriterLockSlim feedbackDevColLock = new ReaderWriterLockSlim();

    public Xbox360OutDevice(ViGEmClient client)
    {