Do not connect Joycons that are already paired. Fix concurrent modifications on Joycon list with concurrent list implementation. (#612)

This commit is contained in:
Chris 2020-11-16 22:46:44 +08:00 committed by GitHub
parent c0b9240887
commit 40dce4149c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 173 additions and 51 deletions

View file

@ -130,6 +130,7 @@
<Compile Include="3rdPartyControllers.Designer.cs">
<DependentUpon>3rdPartyControllers.cs</DependentUpon>
</Compile>
<Compile Include="Collections\ConcurrentList.cs" />
<Compile Include="Config.cs" />
<Compile Include="Controller\OutputControllerDualShock4.cs" />
<Compile Include="Controller\OutputControllerXbox360.cs" />

View file

@ -0,0 +1,116 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace BetterJoyForCemu.Collections {
// https://codereview.stackexchange.com/a/125341
public class ConcurrentList<T> : IList<T> {
#region Fields
private IList<T> _internalList;
private readonly object lockObject = new object();
#endregion
#region ctor
public ConcurrentList() {
_internalList = new List<T>();
}
public ConcurrentList(int capacity) {
_internalList = new List<T>(capacity);
}
public ConcurrentList(IEnumerable<T> list) {
_internalList = new List<T>();
foreach (T item in list) {
_internalList.Add(item);
}
}
#endregion
public T this[int index] {
get {
return LockInternalListAndGet(l => l[index]);
}
set {
LockInternalListAndCommand(l => l[index] = value);
}
}
public int Count {
get {
return LockInternalListAndQuery(l => l.Count);
}
}
public bool IsReadOnly => false;
public void Add(T item) {
LockInternalListAndCommand(l => l.Add(item));
}
public void Clear() {
LockInternalListAndCommand(l => l.Clear());
}
public bool Contains(T item) {
return LockInternalListAndQuery(l => l.Contains(item));
}
public void CopyTo(T[] array, int arrayIndex) {
LockInternalListAndCommand(l => l.CopyTo(array, arrayIndex));
}
public IEnumerator<T> GetEnumerator() {
return LockInternalListAndQuery(l => l.GetEnumerator());
}
public int IndexOf(T item) {
return LockInternalListAndQuery(l => l.IndexOf(item));
}
public void Insert(int index, T item) {
LockInternalListAndCommand(l => l.Insert(index, item));
}
public bool Remove(T item) {
return LockInternalListAndQuery(l => l.Remove(item));
}
public void RemoveAt(int index) {
LockInternalListAndCommand(l => l.RemoveAt(index));
}
IEnumerator IEnumerable.GetEnumerator() {
return LockInternalListAndQuery(l => l.GetEnumerator());
}
#region Utilities
protected virtual void LockInternalListAndCommand(Action<IList<T>> action) {
lock (lockObject) {
action(_internalList);
}
}
protected virtual T LockInternalListAndGet(Func<IList<T>, T> func) {
lock (lockObject) {
return func(_internalList);
}
}
protected virtual TObject LockInternalListAndQuery<TObject>(Func<IList<T>, TObject> query) {
lock (lockObject) {
return query(_internalList);
}
}
#endregion
}
}

View file

@ -17,7 +17,7 @@ namespace BetterJoyForCemu {
public bool isPro = false;
public bool isSnes = false;
bool isUSB = false;
public Joycon other;
public Joycon other = null;
public bool active_gyro = false;
private long inactivity = Stopwatch.GetTimestamp();

View file

@ -12,8 +12,8 @@ using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Timers;
using System.Web.Configuration;
using System.Windows.Forms;
using BetterJoyForCemu.Collections;
using Nefarius.ViGEm.Client;
using static BetterJoyForCemu._3rdPartyControllers;
using static BetterJoyForCemu.HIDapi;
@ -29,7 +29,7 @@ namespace BetterJoyForCemu {
private const ushort product_pro = 0x2009;
private const ushort product_snes = 0x2017;
public List<Joycon> j; // Array of all connected Joy-Cons
public ConcurrentList<Joycon> j { get; private set; } // Array of all connected Joy-Cons
static JoyconManager instance;
public MainForm form;
@ -42,7 +42,7 @@ namespace BetterJoyForCemu {
public void Awake() {
instance = this;
j = new List<Joycon>();
j = new ConcurrentList<Joycon>();
HIDapi.hid_init();
}
@ -61,16 +61,16 @@ namespace BetterJoyForCemu {
void CleanUp() { // removes dropped controllers from list
List<Joycon> rem = new List<Joycon>();
for (int i = 0; i < j.Count; i++) {
Joycon v = j[i];
if (v.state == Joycon.state_.DROPPED) {
if (v.other != null)
v.other.other = null; // The other of the other is the joycon itself
foreach (Joycon joycon in j) {
if (joycon.state == Joycon.state_.DROPPED) {
if (joycon.other != null)
joycon.other.other = null; // The other of the other is the joycon itself
v.Detach(true); rem.Add(v);
joycon.Detach(true);
rem.Add(joycon);
foreach (Button b in form.con) {
if (b.Enabled & b.Tag == v) {
if (b.Enabled & b.Tag == joycon) {
b.Invoke(new MethodInvoker(delegate {
b.BackColor = System.Drawing.Color.FromArgb(0x00, System.Drawing.SystemColors.Control);
b.Enabled = false;
@ -253,7 +253,15 @@ namespace BetterJoyForCemu {
if (foundNew) { // attempt to auto join-up joycons on connection
Joycon temp = null;
foreach (Joycon v in j) {
if (!v.isPro) {
// Do not attach two controllers if they are either:
// - Not a Joycon
// - Already attached to another Joycon (that isn't itself)
if (v.isPro || (v.other != null && v.other != v)) {
continue;
}
// Otherwise, iterate through and find the Joycon with the lowest
// id that has not been attached already (Does not include self)
if (temp == null)
temp = v;
else if (temp.isLeft != v.isLeft && v.other == null) {
@ -294,10 +302,10 @@ namespace BetterJoyForCemu {
}
}
}
}
HIDapi.hid_free_enumeration(top_ptr);
bool on = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).AppSettings.Settings["HomeLEDOn"].Value.ToLower() == "true";
foreach (Joycon jc in j) { // Connect device straight away
if (jc.state == Joycon.state_.NOT_ATTACHED) {
if (jc.out_xbox != null)
@ -312,10 +320,7 @@ namespace BetterJoyForCemu {
continue;
}
bool on = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).AppSettings.Settings["HomeLEDOn"].Value.ToLower() == "true";
foreach (Joycon j in Program.mgr.j) {
j.SetHomeLight(on);
}
jc.SetHomeLight(on);
jc.Begin();
if (form.allowCalibration) {

View file

@ -17,7 +17,7 @@ namespace BetterJoyForCemu {
public MainForm form;
public UdpServer(List<Joycon> p) {
public UdpServer(IList<Joycon> p) {
controllers = p;
}