feat: Added wiimote support and cleaned up the project

This commit is contained in:
Gerard Gascón 2024-06-15 01:47:00 +02:00
parent 36574bdf46
commit baf15cdb0f
25 changed files with 263 additions and 69 deletions

View file

@ -0,0 +1 @@
ControllerSlidePresenter

View file

@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwitchSlidePresenter", "SlidePresenter\SwitchSlidePresenter.csproj", "{F9832F2F-C7CA-4725-8F70-0FF3F76C0A5D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Win32Api", "Win32Api\Win32Api.csproj", "{16DC83C8-381A-4CA7-9E45-2A9CBC7BF48A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F9832F2F-C7CA-4725-8F70-0FF3F76C0A5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F9832F2F-C7CA-4725-8F70-0FF3F76C0A5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F9832F2F-C7CA-4725-8F70-0FF3F76C0A5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F9832F2F-C7CA-4725-8F70-0FF3F76C0A5D}.Release|Any CPU.Build.0 = Release|Any CPU
{16DC83C8-381A-4CA7-9E45-2A9CBC7BF48A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{16DC83C8-381A-4CA7-9E45-2A9CBC7BF48A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{16DC83C8-381A-4CA7-9E45-2A9CBC7BF48A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{16DC83C8-381A-4CA7-9E45-2A9CBC7BF48A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View file

@ -1,10 +0,0 @@
namespace SwitchSlidePresenter {
class Program {
private static async Task Main() {
IGamepadReader reader = new JoyConRead();
SlideSwitcher switcher = new(reader);
await reader.Read();
switcher.Dispose();
}
}
}

View file

@ -0,0 +1,27 @@
namespace SwitchSlidePresenter;
public static class ControllerSelector {
public static IGamepadReader? GetReader() {
Console.WriteLine("Write a number to select controller type:");
Console.WriteLine("[1] - JoyCon");
Console.WriteLine("[2] - Wiimote");
string? line = Console.ReadLine();
if (line == null) {
Console.WriteLine("Invalid input.");
return null;
}
if (!int.TryParse(line, out int id)) {
Console.WriteLine("Invalid number.");
return null;
}
return GetReader(id);
}
private static IGamepadReader? GetReader(int id) => id switch {
1 => new JoyConRead(),
2 => new WiimoteRead(),
_ => null
};
}

View file

@ -1,12 +1,9 @@
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.InputData;
using wtf.cluster.JoyCon.InputReports;
using wtf.cluster.JoyCon.Rumble;
namespace SwitchSlidePresenter;

14
SlidePresenter/Program.cs Normal file
View file

@ -0,0 +1,14 @@
namespace SwitchSlidePresenter {
internal abstract class Program {
private static async Task Main() {
IGamepadReader? reader = ControllerSelector.GetReader();
if (reader == null) {
Console.WriteLine("Invalid Controller Selected.");
return;
}
SlideSwitcher switcher = new(reader);
await reader.Read();
switcher.Dispose();
}
}
}

View file

@ -0,0 +1,65 @@
using System.Runtime.InteropServices;
using Win32Api;
namespace SwitchSlidePresenter;
public class SlideSwitcher : IDisposable {
private readonly IGamepadReader? _reader;
private const uint INPUT_KEYBOARD = 1;
private const ushort VK_NEXT = 0x22;
private const ushort VK_PRIOR = 0x21;
private const uint KEYEVENTF_KEYDOWN = 0x0000;
private const uint KEYEVENTF_KEYUP = 0x0002;
public SlideSwitcher(IGamepadReader? reader) {
_reader = reader;
_reader.NextSlide += NextSlide;
_reader.PrevSlide += PreviousSlide;
}
public void Dispose() {
_reader.NextSlide -= NextSlide;
_reader.PrevSlide -= PreviousSlide;
}
private static void NextSlide() {
SimulateKeyPress(VK_NEXT);
}
private static void PreviousSlide() {
SimulateKeyPress(VK_PRIOR);
}
private static void SimulateKeyPress(ushort keyCode) {
Input[] inputs = new Input[2];
inputs[0] = new Input {
type = INPUT_KEYBOARD,
u = new InputUnion {
ki = new KeyboardInput {
wVk = keyCode,
wScan = 0,
dwFlags = KEYEVENTF_KEYDOWN,
time = 0,
dwExtraInfo = IntPtr.Zero
}
}
};
inputs[1] = new Input {
type = INPUT_KEYBOARD,
u = new InputUnion {
ki = new KeyboardInput {
wVk = keyCode,
wScan = 0,
dwFlags = KEYEVENTF_KEYUP,
time = 0,
dwExtraInfo = IntPtr.Zero
}
}
};
Win32Api.Win32Api.SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(Input)));
}
}

View file

@ -9,6 +9,11 @@
<ItemGroup>
<PackageReference Include="JoyCon.NET" Version="1.0.1" />
<PackageReference Include="WiimoteLib.NetCore" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Win32Api\Win32Api.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,58 @@
using WiimoteLib.NetCore;
namespace SwitchSlidePresenter;
public class WiimoteRead : IGamepadReader {
public event Action NextSlide;
public event Action PrevSlide;
private const int RetryDelay = 1000;
public async Task Read() {
Wiimote wiimote = new();
while (string.IsNullOrEmpty(wiimote.HIDDevicePath)) {
wiimote.Connect();
if (string.IsNullOrEmpty(wiimote.HIDDevicePath)) {
Console.WriteLine("Wiimote connection failed, trying again...");
await Task.Delay(RetryDelay);
} else {
Console.WriteLine("Wiimote ready for presenting!");
}
}
ButtonState previousState = wiimote.WiimoteState.ButtonState;
while (true) {
if (PreviousPressed(wiimote.WiimoteState.ButtonState, previousState)) {
PrevSlide?.Invoke();
}
if (NextPressed(wiimote.WiimoteState.ButtonState, previousState)) {
NextSlide?.Invoke();
}
previousState = wiimote.WiimoteState.ButtonState;
await Task.Yield();
if (!Console.KeyAvailable || Console.ReadKey().Key != ConsoleKey.Enter)
continue;
wiimote.Disconnect();
Console.WriteLine();
Console.WriteLine("Stopped.");
break;
}
}
private static bool PreviousPressed(ButtonState input) {
return input.B || input.Left;
}
private static bool NextPressed(ButtonState input) {
return input.A || input.Right;
}
private static bool PreviousPressed(ButtonState input, ButtonState previousState) {
return PreviousPressed(input) && !PreviousPressed(previousState);
}
private static bool NextPressed(ButtonState input, ButtonState previousState) {
return NextPressed(input) && !NextPressed(previousState);
}
}

View file

@ -1,26 +0,0 @@
namespace SwitchSlidePresenter;
public class SlideSwitcher : IDisposable {
private readonly IGamepadReader _reader;
public SlideSwitcher(IGamepadReader reader) {
_reader = reader;
_reader.NextSlide += NextSlide;
_reader.PrevSlide += PreviousSlide;
}
public void Dispose() {
_reader.NextSlide -= NextSlide;
_reader.PrevSlide -= PreviousSlide;
}
private void NextSlide() {
IntPtr handle = Win32Api.GetForegroundWindow();
Win32Api.PostMessage(handle, Win32Api.WM_KEYDOWN, Win32Api.VK_NEXT, 0);
}
private void PreviousSlide() {
IntPtr handle = Win32Api.GetForegroundWindow();
Win32Api.PostMessage(handle, Win32Api.WM_KEYDOWN, Win32Api.VK_PRIOR, 0);
}
}

View file

@ -1,16 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwitchSlidePresenter", "SwitchSlidePresenter.csproj", "{50CECFA5-3AC2-4F08-82F2-ED8C82993360}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{50CECFA5-3AC2-4F08-82F2-ED8C82993360}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{50CECFA5-3AC2-4F08-82F2-ED8C82993360}.Debug|Any CPU.Build.0 = Debug|Any CPU
{50CECFA5-3AC2-4F08-82F2-ED8C82993360}.Release|Any CPU.ActiveCfg = Release|Any CPU
{50CECFA5-3AC2-4F08-82F2-ED8C82993360}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View file

@ -1,14 +0,0 @@
using System.Runtime.InteropServices;
namespace SwitchSlidePresenter;
public static class Win32Api {
public const UInt32 WM_KEYDOWN = 0x0100;
public const int VK_NEXT = 0x22;
public const int VK_PRIOR = 0x21;
[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
}

10
Win32Api/HardwareInput.cs Normal file
View file

@ -0,0 +1,10 @@
using System.Runtime.InteropServices;
namespace Win32Api;
[StructLayout(LayoutKind.Sequential)]
public struct HardwareInput {
public uint uMsg;
public ushort wParamL;
public ushort wParamH;
}

9
Win32Api/Input.cs Normal file
View file

@ -0,0 +1,9 @@
using System.Runtime.InteropServices;
namespace Win32Api;
[StructLayout(LayoutKind.Sequential)]
public struct Input {
public uint type;
public InputUnion u;
}

10
Win32Api/InputUnion.cs Normal file
View file

@ -0,0 +1,10 @@
using System.Runtime.InteropServices;
namespace Win32Api;
[StructLayout(LayoutKind.Explicit)]
public struct InputUnion {
[FieldOffset(0)] public MouseInput mi;
[FieldOffset(0)] public KeyboardInput ki;
[FieldOffset(0)] public HardwareInput hi;
}

12
Win32Api/KeyboardInput.cs Normal file
View file

@ -0,0 +1,12 @@
using System.Runtime.InteropServices;
namespace Win32Api;
[StructLayout(LayoutKind.Sequential)]
public struct KeyboardInput {
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}

13
Win32Api/MouseInput.cs Normal file
View file

@ -0,0 +1,13 @@
using System.Runtime.InteropServices;
namespace Win32Api;
[StructLayout(LayoutKind.Sequential)]
public struct MouseInput {
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}

8
Win32Api/Win32Api.cs Normal file
View file

@ -0,0 +1,8 @@
using System.Runtime.InteropServices;
namespace Win32Api;
public static class Win32Api {
[DllImport("user32.dll", SetLastError = true)]
public static extern uint SendInput(uint nInputs, Input[] pInputs, int cbSize);
}

9
Win32Api/Win32Api.csproj Normal file
View file

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>