diff --git a/JoyConRead.cs b/JoyConRead.cs new file mode 100644 index 0000000..f94fbee --- /dev/null +++ b/JoyConRead.cs @@ -0,0 +1,111 @@ +using System.Text; +using HidSharp; +using wtf.cluster.JoyCon; +using wtf.cluster.JoyCon.Calibration; +using wtf.cluster.JoyCon.ExtraData; +using wtf.cluster.JoyCon.HomeLed; +using wtf.cluster.JoyCon.InputReports; +using wtf.cluster.JoyCon.Rumble; + +namespace SwitchSlidePresenter; + +public class JoyConRead { + public static async Task Read() { + Console.OutputEncoding = Encoding.UTF8; + + HidDevice? device = null; + // Get a list of all HID devices + DeviceList list = DeviceList.Local; + if (OperatingSystem.IsWindows()) { + // Get all devices developed by Nintendo by vendor ID + var nintendos = list.GetHidDevices(0x057e); + // Get the first Nintendo controller + device = nintendos.FirstOrDefault(); + // It works fine for Windows, but... + } else { + // Linux has more complicated HID device management, we can't get device's vendor ID, + // so let's filter devices by their report descriptor + // It should work in Windows too, so it's more multiplatform solution + var hidDevices = list.GetHidDevices(); + foreach (var d in hidDevices) { + var rd = d.GetReportDescriptor(); + if (rd != null) { + if ( + rd.OutputReports.Count() == 4 + && rd.OutputReports.Count(r => r.ReportID == 0x01) == 1 + && rd.OutputReports.Count(r => r.ReportID == 0x10) == 1 + && rd.OutputReports.Count(r => r.ReportID == 0x11) == 1 + && rd.OutputReports.Count(r => r.ReportID == 0x12) == 1 + && rd.InputReports.Count() == 6 + && rd.InputReports.Count(r => r.ReportID == 0x21) == 1 + && rd.InputReports.Count(r => r.ReportID == 0x30) == 1 + && rd.InputReports.Count(r => r.ReportID == 0x31) == 1 + && rd.InputReports.Count(r => r.ReportID == 0x32) == 1 + && rd.InputReports.Count(r => r.ReportID == 0x33) == 1 + && rd.InputReports.Count(r => r.ReportID == 0x3F) == 1 + ) { + device = d; + break; + } + } + } + } + if (device == null) { + Console.WriteLine("No controller. Please connect Joy-Con or Pro controller via Bluetooth."); + return; + } + // Create a new JoyCon instance based on the HID device + JoyCon joycon = new(device); + // Start controller polling + joycon.Start(); + + // First of all, we need to set format of the input reports, + // most of the time you will need to use InputReportMode.Full mode + await joycon.SetInputReportModeAsync(JoyCon.InputReportType.Simple); + + // Get some information about the controller + DeviceInfo deviceInfo = await joycon.GetDeviceInfoAsync(); + Console.WriteLine( + $"Type: {deviceInfo.ControllerType}, Firmware: {deviceInfo.FirmwareVersionMajor}.{deviceInfo.FirmwareVersionMinor}"); + var serial = await joycon.GetSerialNumberAsync(); + Console.WriteLine($"Serial number: {serial ?? ""}"); + ControllerColors? colors = await joycon.GetColorsAsync(); + if (colors != null) { + Console.WriteLine($"Body color: {colors.BodyColor}, buttons color: {colors.ButtonsColor}"); + } else { + Console.WriteLine("Colors not specified, seems like the controller is grey."); + } + + // Save the current cursor position + (var cX, var cY) = (Console.CursorLeft, Console.CursorTop); + + // Subscribe to the input reports + joycon.ReportReceived += (sender, input) => { + Console.SetCursorPosition(cX, cY); + // Check the type of the input report, most likely it will be InputWithImu + if (input is InputSimple j) { + // Some base data from the controller + Console.WriteLine( + $"Buttons: ({j.Buttons}) "); + } else { + Console.WriteLine($"Invalid input report type: {input.GetType()}"); + } + return Task.CompletedTask; + }; + + // Error handling + joycon.StoppedOnError += (_, ex) => { + Console.WriteLine(); + Console.WriteLine($"Critical error: {ex.Message}"); + Console.WriteLine("Controller polling stopped."); + Environment.Exit(1); + return Task.CompletedTask; + }; + + Console.ReadKey(); + joycon.Stop(); + + Console.WriteLine(); + Console.WriteLine("Stopped."); + } +} \ No newline at end of file diff --git a/Program.cs b/Program.cs index 7795ee7..f3df943 100644 --- a/Program.cs +++ b/Program.cs @@ -10,7 +10,7 @@ using wtf.cluster.JoyCon.Rumble; namespace SwitchSlidePresenter { class Program { static async Task Main(string[] args) { - await Tutorial.Execute(); + await JoyConRead.Read(); } } } \ No newline at end of file diff --git a/SlideSwitchPress.cs b/SlideSwitchPress.cs new file mode 100644 index 0000000..eec63e7 --- /dev/null +++ b/SlideSwitchPress.cs @@ -0,0 +1,7 @@ +namespace SwitchSlidePresenter; + +public enum SlideSwitchPress { + None, + Previous, + Next +} \ No newline at end of file diff --git a/Tutorial.cs b/Tutorial.cs deleted file mode 100644 index d7dd9e2..0000000 --- a/Tutorial.cs +++ /dev/null @@ -1,184 +0,0 @@ -using HidSharp; -using System.Text; -using wtf.cluster.JoyCon; -using wtf.cluster.JoyCon.Calibration; -using wtf.cluster.JoyCon.ExtraData; -using wtf.cluster.JoyCon.HomeLed; -using wtf.cluster.JoyCon.InputReports; -using wtf.cluster.JoyCon.Rumble; - -namespace SwitchSlidePresenter { - public class Tutorial { - public static async Task Execute() { - Console.OutputEncoding = Encoding.UTF8; - - HidDevice? device = null; - // Get a list of all HID devices - DeviceList list = DeviceList.Local; - if (OperatingSystem.IsWindows()) { - // Get all devices developed by Nintendo by vendor ID - var nintendos = list.GetHidDevices(0x057e); - // Get the first Nintendo controller - device = nintendos.FirstOrDefault(); - // It works fine for Windows, but... - } else { - // Linux has more complicated HID device management, we can't get device's vendor ID, - // so let's filter devices by their report descriptor - // It should work in Windows too, so it's more multiplatform solution - var hidDevices = list.GetHidDevices(); - foreach (var d in hidDevices) { - var rd = d.GetReportDescriptor(); - if (rd != null) { - if ( - rd.OutputReports.Count() == 4 - && rd.OutputReports.Count(r => r.ReportID == 0x01) == 1 - && rd.OutputReports.Count(r => r.ReportID == 0x10) == 1 - && rd.OutputReports.Count(r => r.ReportID == 0x11) == 1 - && rd.OutputReports.Count(r => r.ReportID == 0x12) == 1 - && rd.InputReports.Count() == 6 - && rd.InputReports.Count(r => r.ReportID == 0x21) == 1 - && rd.InputReports.Count(r => r.ReportID == 0x30) == 1 - && rd.InputReports.Count(r => r.ReportID == 0x31) == 1 - && rd.InputReports.Count(r => r.ReportID == 0x32) == 1 - && rd.InputReports.Count(r => r.ReportID == 0x33) == 1 - && rd.InputReports.Count(r => r.ReportID == 0x3F) == 1 - ) { - device = d; - break; - } - } - } - } - if (device == null) { - Console.WriteLine("No controller. Please connect Joy-Con or Pro controller via Bluetooth."); - return; - } - // Create a new JoyCon instance based on the HID device - var joycon = new JoyCon(device); - // Start controller polling - joycon.Start(); - - // First of all, we need to set format of the input reports, - // most of the time you will need to use InputReportMode.Full mode - await joycon.SetInputReportModeAsync(JoyCon.InputReportType.Full); - - // Get some information about the controller - DeviceInfo deviceInfo = await joycon.GetDeviceInfoAsync(); - Console.WriteLine( - $"Type: {deviceInfo.ControllerType}, Firmware: {deviceInfo.FirmwareVersionMajor}.{deviceInfo.FirmwareVersionMinor}"); - var serial = await joycon.GetSerialNumberAsync(); - Console.WriteLine($"Serial number: {serial ?? ""}"); - ControllerColors? colors = await joycon.GetColorsAsync(); - if (colors != null) { - Console.WriteLine($"Body color: {colors.BodyColor}, buttons color: {colors.ButtonsColor}"); - } else { - Console.WriteLine("Colors not specified, seems like the controller is grey."); - } - - // Enable IMU (accelerometer and gyroscope) - await joycon.EnableImuAsync(true); - // Enable rumble feature (it's enabled by default, actually) - await joycon.EnableRumbleAsync(true); - // You can control LEDs on the controller - await joycon.SetPlayerLedsAsync(JoyCon.LedState.Off, JoyCon.LedState.On, JoyCon.LedState.Off, - JoyCon.LedState.Blinking); - - // Get factory calibration data - CalibrationData facCal = await joycon.GetFactoryCalibrationAsync(); - // Get user calibration data - CalibrationData userCal = await joycon.GetUserCalibrationAsync(); - // Combine both calibrations, e.g. user calibration has higher priority - CalibrationData calibration = facCal + userCal; - // Get some parameters for the sticks - StickParametersSet sticksParameters = await joycon.GetStickParametersAsync(); - - // Home LED dimming pattern demo. Useless, but fun. - await joycon.SetHomeLedDimmingPatternAsync(new HomeLedDimmingPattern { - StepDurationBase = 4, // base duration is 40ms - StartLedBrightness = 0, // 0%, off - FullCyclesNumber = 0, // infinite - HomeLedDimmingSteps = { - new HomeLedDimmingStep { - LedBrightness = 0x0F, // 100% - TransitionDuration = 2, // base * 2 = 40ms * 2 = 80ms - PauseDuration = 4, // base * 4 = 40ms * 4 = 160ms - }, - new HomeLedDimmingStep { - LedBrightness = 0x00, // LED off - TransitionDuration = 2, // base * 2 = 40ms * 2 = 80ms - PauseDuration = 4, // base * 4 = 40ms * 4 = 160ms - }, - new HomeLedDimmingStep { LedBrightness = 0x0F, TransitionDuration = 2, PauseDuration = 4 }, - new HomeLedDimmingStep { LedBrightness = 0x00, TransitionDuration = 2, PauseDuration = 4 }, - new HomeLedDimmingStep { LedBrightness = 0x0F, TransitionDuration = 2, PauseDuration = 4 }, - new HomeLedDimmingStep { LedBrightness = 0x00, TransitionDuration = 2, PauseDuration = 4 }, - new HomeLedDimmingStep { - LedBrightness = 0x08, // 50% - TransitionDuration = 8, // base * 8 = 40ms * 8 = 320ms - PauseDuration = 4, // base * 8 = 40ms * 8 = 320ms - }, - new HomeLedDimmingStep { - LedBrightness = 0x00, // LED off - TransitionDuration = 8, // base * 8 = 40ms * 8 = 320ms - PauseDuration = 15, // base * 15 = 40ms * 15 = 600ms - } - }, - }); - - // Save the current cursor position - (var cX, var cY) = (Console.CursorLeft, Console.CursorTop); - - // Subscribe to the input reports - joycon.ReportReceived += (sender, input) => { - Console.SetCursorPosition(cX, cY); - // Check the type of the input report, most likely it will be InputWithImu - if (input is InputFullWithImu j) { - // Some base data from the controller - Console.WriteLine( - $"LeftStick: ({j.LeftStick}), RightStick: ({j.RightStick}), Buttons: ({j.Buttons}), Battery: {j.Battery}, Charging: {j.Charging} "); - // But we need calibrated data - StickPositionCalibrated leftStickCalibrated = - j.LeftStick.GetCalibrated(calibration.LeftStickCalibration!, - sticksParameters.LeftStickParameters.DeadZone); - StickPositionCalibrated rightStickCalibrated = - j.RightStick.GetCalibrated(calibration.RightStickCalibration!, - sticksParameters.RightStickParameters.DeadZone); - Console.WriteLine( - $"Calibrated LeftStick: (({leftStickCalibrated}), RightStick: ({rightStickCalibrated})) "); - // And accelerometer and gyroscope data - ImuFrameCalibrated calibratedImu = j.Imu.Frames[0].GetCalibrated(calibration.ImuCalibration!); - Console.WriteLine($"Calibrated IMU: ({calibratedImu}) "); - } else { - Console.WriteLine($"Invalid input report type: {input.GetType()}"); - } - return Task.CompletedTask; - }; - - // Error handling - joycon.StoppedOnError += new JoyCon.StoppedOnErrorHandler((_, ex) => { - Console.WriteLine(); - Console.WriteLine($"Critical error: {ex.Message}"); - Console.WriteLine("Controller polling stopped."); - Environment.Exit(1); - return Task.CompletedTask; - }); - - // Periodically send rumble command to the controller - var rumbleTimer = new Timer(async _ => { - try { - await joycon.WriteRumble(new RumbleSet(new RumbleData(40.9, 1), new RumbleData(65, 1))); - } - catch (Exception e) { - Console.WriteLine($"Rumble error: {e.Message}"); - } - }, null, 0, 1000); - - Console.ReadKey(); - await rumbleTimer.DisposeAsync(); - joycon.Stop(); - - Console.WriteLine(); - Console.WriteLine("Stopped."); - } - } -} \ No newline at end of file