diff --git a/BetterJoyForCemu/App.config b/BetterJoyForCemu/App.config index 3efa29a..625fd5b 100644 --- a/BetterJoyForCemu/App.config +++ b/BetterJoyForCemu/App.config @@ -19,7 +19,7 @@ - + diff --git a/BetterJoyForCemu/Config.cs b/BetterJoyForCemu/Config.cs index ad401a1..6892a8b 100644 --- a/BetterJoyForCemu/Config.cs +++ b/BetterJoyForCemu/Config.cs @@ -32,7 +32,7 @@ namespace BetterJoyForCemu { } public static bool Value(string key) { - if (!variables.TryGetValue(key, out bool temp)) { + if (!variables.ContainsKey("ProgressiveScan")) { return false; } return variables[key]; diff --git a/BetterJoyForCemu/Joycon.cs b/BetterJoyForCemu/Joycon.cs index 125cee8..ccb6d8f 100644 --- a/BetterJoyForCemu/Joycon.cs +++ b/BetterJoyForCemu/Joycon.cs @@ -14,8 +14,9 @@ using Nefarius.ViGEm.Client.Targets.Xbox360; namespace BetterJoyForCemu { public class Joycon { - float timing = 60.0f; + float timing = 120.0f; + public string path = String.Empty; public bool isPro = false; bool isUSB = false; public Joycon other; @@ -73,8 +74,7 @@ namespace BetterJoyForCemu { private float[] stick = { 0, 0 }; private float[] stick2 = { 0, 0 }; - private - IntPtr handle; + private IntPtr handle; byte[] default_buf = { 0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40 }; @@ -219,7 +219,7 @@ namespace BetterJoyForCemu { public byte LED = 0x0; - public Joycon(IntPtr handle_, bool imu, bool localize, float alpha, bool left, int id = 0, bool isPro=false, bool usb = false) { + public Joycon(IntPtr handle_, bool imu, bool localize, float alpha, bool left, string path, int id = 0, bool isPro=false, bool usb = false) { handle = handle_; imu_enabled = imu; do_localize = localize; @@ -231,6 +231,8 @@ namespace BetterJoyForCemu { this.isPro = isPro; isUSB = usb; + this.path = path; + connection = isUSB ? 0x01 : 0x02; if (showAsXInput) { @@ -295,7 +297,7 @@ namespace BetterJoyForCemu { Subcommand(0x03, new byte[] { 0x3f }, 1, false); a = Enumerable.Repeat((byte)0, 64).ToArray(); - form.console.Text += "Using USB.\r\n"; + form.AppendTextBox("Using USB.\r\n"); a[0] = 0x80; a[1] = 0x01; @@ -354,6 +356,10 @@ namespace BetterJoyForCemu { public void Detach() { stop_polling = true; + if (xin != null) { + xin.Disconnect(); xin.Dispose(); + } + if (state > state_.NO_JOYCONS) { Subcommand(0x40, new byte[] { 0x0 }, 1); //Subcommand(0x48, new byte[] { 0x0 }, 1); // Would turn off rumble? @@ -375,11 +381,8 @@ namespace BetterJoyForCemu { if (handle == IntPtr.Zero) return -2; HIDapi.hid_set_nonblocking(handle, 0); byte[] raw_buf = new byte[report_len]; - int ret = HIDapi.hid_read(handle, raw_buf, new UIntPtr(report_len)); - if (ret > 0) { - if (!isPro) - SendRumble(rumble_obj.GetData()); // make rumble better when on bluetooth - + int ret = 0; + while ((ret = HIDapi.hid_read(handle, raw_buf, new UIntPtr(report_len))) > 0) { // Process packets as soon as they come for (int n = 0; n < 3; n++) { ExtractIMUValues(raw_buf, n); @@ -400,7 +403,7 @@ namespace BetterJoyForCemu { } if (ts_en == raw_buf[1]) { - form.console.Text += "Duplicate timestamp enqueued.\r\n"; + form.AppendTextBox("Duplicate timestamp enqueued.\r\n"); DebugPrint(string.Format("Duplicate timestamp enqueued. TS: {0:X2}", ts_en), DebugType.THREADING); } ts_en = raw_buf[1]; @@ -413,17 +416,17 @@ namespace BetterJoyForCemu { private void Poll() { int attempts = 0; while (!stop_polling & state > state_.NO_JOYCONS) { - if (isPro) - SendRumble(rumble_obj.GetData()); // Needed for USB to not time out + SendRumble(rumble_obj.GetData()); // Needed for EVERYTHING to not time out. Never remove pls int a = ReceiveRaw(); if (a > 0) { - state = state_.IMU_DATA_OK; + state = state_.IMU_DATA_OK; attempts = 0; - } else if (attempts > 1000) { + } else if (attempts > 240) { state = state_.DROPPED; - //form.console.Text += "Dropped\r\n"; - DebugPrint("Connection lost. Is the Joy-Con connected?", DebugType.ALL); + form.AppendTextBox("Dropped.\r\n"); + + DebugPrint("Connection lost. Is the Joy-Con connected?", DebugType.ALL); break; } else { DebugPrint("Pause 5ms", DebugType.THREADING); @@ -431,7 +434,6 @@ namespace BetterJoyForCemu { } ++attempts; } - DebugPrint("End poll loop.", DebugType.THREADING); } public void Update() { @@ -449,7 +451,7 @@ namespace BetterJoyForCemu { public float[] otherStick = { 0, 0 }; - bool swapButtons = Boolean.Parse(ConfigurationSettings.AppSettings["SwapButtons"]); + bool swapButtons = Boolean.Parse(ConfigurationManager.AppSettings["SwapButtons"]); private int ProcessButtonsAndStick(byte[] report_buf) { if (report_buf[0] == 0x00) return -1; @@ -655,8 +657,10 @@ namespace BetterJoyForCemu { PollThreadObj.IsBackground = true; PollThreadObj.Start(); - form.console.Text += "Starting poll thread.\r\n"; - } + form.AppendTextBox("Starting poll thread.\r\n"); + } else { + form.AppendTextBox("Poll cannot start.\r\n"); + } } public void Recenter() { @@ -721,13 +725,13 @@ namespace BetterJoyForCemu { bool found = false; for (int i = 0; i < 9; ++i) { if (buf_[i] != 0xff) { - form.console.Text += "Using user stick calibration data.\r\n"; + form.AppendTextBox("Using user stick calibration data.\r\n"); found = true; break; } } if (!found) { - form.console.Text += "Using factory stick calibration data.\r\n"; + form.AppendTextBox("Using factory stick calibration data.\r\n"); buf_ = ReadSPI(0x60, (isLeft ? (byte)0x3d : (byte)0x46), 9); // get user calibration data if possible } stick_cal[isLeft ? 0 : 2] = (UInt16)((buf_[1] << 8) & 0xF00 | buf_[0]); // X Axis Max above center @@ -744,13 +748,13 @@ namespace BetterJoyForCemu { found = false; for (int i = 0; i < 9; ++i) { if (buf_[i] != 0xff) { - form.console.Text += "Using user stick calibration data.\r\n"; + form.AppendTextBox("Using user stick calibration data.\r\n"); found = true; break; } } if (!found) { - form.console.Text += "Using factory stick calibration data.\r\n"; + form.AppendTextBox("Using factory stick calibration data.\r\n"); buf_ = ReadSPI(0x60, (!isLeft ? (byte)0x3d : (byte)0x46), 9); // get user calibration data if possible } stick2_cal[!isLeft ? 0 : 2] = (UInt16)((buf_[1] << 8) & 0xF00 | buf_[0]); // X Axis Max above center diff --git a/BetterJoyForCemu/MainForm.cs b/BetterJoyForCemu/MainForm.cs index 1231d09..2978ee1 100644 --- a/BetterJoyForCemu/MainForm.cs +++ b/BetterJoyForCemu/MainForm.cs @@ -63,5 +63,13 @@ namespace BetterJoyForCemu { private void passiveScanBox_CheckedChanged(object sender, EventArgs e) { Config.Save("ProgressiveScan", passiveScanBox.Checked); } + + public void AppendTextBox(string value) { // https://stackoverflow.com/questions/519233/writing-to-a-textbox-from-another-thread + if (InvokeRequired) { + this.Invoke(new Action(AppendTextBox), new object[] { value }); + return; + } + console.AppendText(value); + } } } diff --git a/BetterJoyForCemu/Program.cs b/BetterJoyForCemu/Program.cs index 30a854b..2d1246e 100644 --- a/BetterJoyForCemu/Program.cs +++ b/BetterJoyForCemu/Program.cs @@ -39,7 +39,9 @@ namespace BetterJoyForCemu { public MainForm form; - bool useHIDG = Boolean.Parse(ConfigurationSettings.AppSettings["UseHIDG"]); + System.Timers.Timer controllerCheck; + + bool useHIDG = Boolean.Parse(ConfigurationManager.AppSettings["UseHIDG"]); public static JoyconManager Instance { get { return instance; } @@ -47,39 +49,70 @@ namespace BetterJoyForCemu { public void Awake() { instance = this; - int i = 0; - j = new List(); - bool isLeft = false; HIDapi.hid_init(); + } - IntPtr ptr = HIDapi.hid_enumerate(vendor_id, 0x0); - IntPtr top_ptr = ptr; + public void Start() { + controllerCheck = new System.Timers.Timer(2000); // check every 2 seconds + controllerCheck.Elapsed += CheckForNewControllersTime; + controllerCheck.Start(); + } - if (ptr == IntPtr.Zero) { - ptr = HIDapi.hid_enumerate(vendor_id_, 0x0); - if (ptr == IntPtr.Zero) { - HIDapi.hid_free_enumeration(ptr); - form.console.Text += "No Joy-Cons found!\r\n"; + bool ControllerAlreadyAdded(string path) { + foreach (Joycon v in j) + if (v.path == path) + return true; + return false; + } + + void CleanUp() { // removes dropped controllers from list + List rem = new List(); + for (int i = 0; i < j.Count; i++) { + Joycon v = j[i]; + if (v.state == Joycon.state_.DROPPED) { + v.Detach(); rem.Add(v); + form.AppendTextBox("Removed dropped controller to list. Can be reconnected.\r\n"); } } - hid_device_info enumerate; + foreach (Joycon v in rem) + j.Remove(v); + } + + void CheckForNewControllersTime(Object source, ElapsedEventArgs e) { + if (Config.Value("ProgressiveScan")) { + CheckForNewControllers(); + } + } + + public void CheckForNewControllers() { + CleanUp(); + + // move all code for initializing devices here and well as the initial code from Start() + bool isLeft = false; + IntPtr ptr = HIDapi.hid_enumerate(vendor_id, 0x0); + IntPtr top_ptr = ptr; + + bool foundNew = false; + + hid_device_info enumerate; // Add device to list while (ptr != IntPtr.Zero) { enumerate = (hid_device_info)Marshal.PtrToStructure(ptr, typeof(hid_device_info)); - if (enumerate.product_id == product_l || enumerate.product_id == product_r || enumerate.product_id == product_pro) { - if (enumerate.product_id == product_l) { - isLeft = true; - form.console.Text += "Left Joy-Con connected.\r\n"; - } else if (enumerate.product_id == product_r) { - isLeft = false; - form.console.Text += "Right Joy-Con connected.\r\n"; - } else if (enumerate.product_id == product_pro) { - isLeft = true; - form.console.Text += "Pro controller connected.\r\n"; - } else { - form.console.Text += "Non Joy-Con input device skipped.\r\n"; + if ((enumerate.product_id == product_l || enumerate.product_id == product_r || enumerate.product_id == product_pro) && !ControllerAlreadyAdded(enumerate.path)) { + switch (enumerate.product_id) { + case product_l: + isLeft = true; + form.AppendTextBox("Left Joy-Con connected.\r\n"); break; + case product_r: + isLeft = false; + form.AppendTextBox("Right Joy-Con connected.\r\n"); break; + case product_pro: + isLeft = true; + form.AppendTextBox("Pro controller connected.\r\n"); break; + default: + form.AppendTextBox("Non Joy-Con Nintendo input device skipped.\r\n"); break; } // Add controller to block-list for HidGuardian @@ -98,21 +131,26 @@ namespace BetterJoyForCemu { try { var response = (HttpWebResponse)request.GetResponse(); var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd(); - } catch (Exception e) { - form.console.Text += "Unable to add controller to block-list.\r\n"; + } catch { + form.AppendTextBox("Unable to add controller to block-list.\r\n"); } + } else { // Remove affected devices from list + try { + HttpWebResponse r1 = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/affected/purge/").GetResponse(); + } catch { } } // -------------------- // IntPtr handle = HIDapi.hid_open_path(enumerate.path); try { HIDapi.hid_set_nonblocking(handle, 1); - } catch (Exception e) { - form.console.Text += "Unable to open path to device - are you using the correct (64 vs 32-bit) version for your PC?\r\n"; + } catch { + form.AppendTextBox("Unable to open path to device - are you using the correct (64 vs 32-bit) version for your PC?\r\n"); break; } - j.Add(new Joycon(handle, EnableIMU, EnableLocalize & EnableIMU, 0.05f, isLeft, j.Count, enumerate.product_id == product_pro, enumerate.serial_number == "000000000001")); + j.Add(new Joycon(handle, EnableIMU, EnableLocalize & EnableIMU, 0.05f, isLeft, enumerate.path, j.Count, enumerate.product_id == product_pro, enumerate.serial_number == "000000000001")); + foundNew = true; j.Last().form = form; @@ -120,15 +158,14 @@ namespace BetterJoyForCemu { for (int n = 0; n < 6; n++) mac[n] = byte.Parse(enumerate.serial_number.Substring(n * 2, 2), System.Globalization.NumberStyles.HexNumber); j[j.Count - 1].PadMacAddress = new PhysicalAddress(mac); - - ++i; } + ptr = enumerate.next; } int found = 0; int minPadID = 10; - foreach (Joycon v in j) { + foreach (Joycon v in j) { // current system is designed for a maximum of two joycons connected to the PC if (!v.isPro) { found++; minPadID = Math.Min(v.PadId, minPadID); @@ -138,12 +175,12 @@ namespace BetterJoyForCemu { } if (found == 2) { - form.console.Text += "Both joycons successfully found.\r\n"; + form.AppendTextBox("Both joycons successfully found.\r\n"); Joycon temp = null; foreach (Joycon v in j) { if (!v.isPro) { v.LED = (byte)(0x1 << minPadID); - + if (temp == null) temp = v; else { @@ -155,23 +192,18 @@ namespace BetterJoyForCemu { } } } // Join up the two joycons - } else if (found != 0) - form.console.Text += "Only one joycon found. Using in single joycone mode.\r\n"; + } HIDapi.hid_free_enumeration(top_ptr); - } - public void Start() { - for (int i = 0; i < j.Count; ++i) { - Joycon jc = j[i]; + foreach (Joycon jc in j) { // Connect device straight away + if (jc.state == Joycon.state_.NOT_ATTACHED) { + if (jc.xin != null) + jc.xin.Connect(); - if (jc.xin != null) - jc.xin.Connect(); - - //byte LEDs = 0x0; - // LEDs |= (byte)(0x1 << jc.PadId); - jc.Attach(leds_: jc.LED); - jc.Begin(); + jc.Attach(leds_: jc.LED); + jc.Begin(); + } } } @@ -189,6 +221,9 @@ namespace BetterJoyForCemu { v.xin.Dispose(); } } + + controllerCheck.Stop(); + HIDapi.hid_exit(); } } @@ -234,7 +269,7 @@ namespace BetterJoyForCemu { class Program { public static PhysicalAddress btMAC = new PhysicalAddress(new byte[] { 0, 0, 0, 0, 0, 0 }); public static UdpServer server; - static double pollsPerSecond = 60.0; + static double pollsPerSecond = 120.0; public static ViGEmClient emClient; @@ -299,6 +334,7 @@ namespace BetterJoyForCemu { mgr = new JoyconManager(); mgr.form = form; mgr.Awake(); + mgr.CheckForNewControllers(); mgr.Start(); server = new UdpServer(mgr.j);