diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index a29c1ef..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: .NET Build - -on: - workflow_dispatch: - -jobs: - build: - name: Build on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [windows-latest, ubuntu-latest] - include: - - os: windows-latest - vs-version: 'latest' - runtime: 'win-x64' - - os: ubuntu-latest - vs-version: 'latest' - runtime: 'linux-x64' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '8.x' - - - name: Restore dependencies - run: dotnet restore - - - name: Build self-contained application - run: dotnet publish SlidePresenter/ControllerSlidePresenter.csproj --no-restore --configuration Release --self-contained -r ${{ matrix.runtime }} /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: build-${{ matrix.runtime }} - path: | - **/bin/Release/net8.0/${{ matrix.runtime }}/publish/ diff --git a/.idea/.idea.SwitchSlidePresenter/.idea/workspace.xml b/.idea/.idea.SwitchSlidePresenter/.idea/workspace.xml index 41f33f2..6d4b518 100644 --- a/.idea/.idea.SwitchSlidePresenter/.idea/workspace.xml +++ b/.idea/.idea.SwitchSlidePresenter/.idea/workspace.xml @@ -37,15 +37,11 @@ - - { "associatedIndex": 6 } - diff --git a/.run/Publish Linux-x64.run.xml b/.run/Publish Linux-x64.run.xml deleted file mode 100644 index 24fa928..0000000 --- a/.run/Publish Linux-x64.run.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.run/Publish Windows-x64.run.xml b/.run/Publish Windows-x64.run.xml deleted file mode 100644 index 78279af..0000000 --- a/.run/Publish Windows-x64.run.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index e3599a9..0000000 --- a/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Controller Slide Presenter - -A small tool to emulate a Slide Presenter using a Wiimote or a Joy-Con. - -## Controls - -### Wiimote - -- A or Right - Next Slide -- B or Left - Previous Slide - -### Joy-Con - -- A or ZL/ZR - Next Slide -- B or L/R - Previous Slide - -## Platform Support - -### Windows - -For the Joy-Con you just need to connect it via Bluetooth and then run the program. - -For the Wiimote you may need to connect it using Dolphin Emulator and then just run the program. - -**WARNING:** It's possible that Steam tries reading the Joy-Con in the background and keeps it from working. - -### Linux - -**Prerequisite:** You need [xdotool](https://github.com/jordansissel/xdotool) installed in order to redirect the input. - -At the moment there's only Joy-Con support, so connect it via Bluetooth and run the program. - -### MacOS - -At the moment there's no MacOS support as I don't have a computer to test, but feel free to submit a Pull Request adding that feature, the Joy-Con reading should work just fine, only the input redirection is needed. - -## Packages used - -**JoyCon.NET** - ([GitHub](https://github.com/ClusterM/joycon)) ([NuGet](https://www.nuget.org/packages/JoyCon.NET)) - -**WiimoteLib.NetCore** - ([GitHub](https://github.com/BrianPeek/WiimoteLib)) ([NuGet](https://www.nuget.org/packages/WiimoteLib.NetCore)) diff --git a/SlidePresenter/ControllerSelector.cs b/SlidePresenter/ControllerSelector.cs index 31e5dd8..8efff0f 100644 --- a/SlidePresenter/ControllerSelector.cs +++ b/SlidePresenter/ControllerSelector.cs @@ -1,33 +1,11 @@ -using ControllerSlidePresenter.GamepadReader; - -namespace ControllerSlidePresenter; +namespace SwitchSlidePresenter; public static class ControllerSelector { - private static readonly List<(string name, IGamepadReader reader)> Readers = [ -#if JoyCon - ("JoyCon", new JoyConRead()), -#endif -#if Wiimote - ("Wiimote", new WiimoteRead()) -#endif - ]; - public static IGamepadReader? GetReader() { - if (Readers.Count == 1) - return Readers[0].reader; - Console.WriteLine("Write a number to select controller type:"); - for (int i = 0; i < Readers.Count; i++) - Console.WriteLine($"[{i+1}] - {Readers[i].name}"); + Console.WriteLine("[1] - JoyCon"); + Console.WriteLine("[2] - Wiimote"); - int? id = GetReaderIndex(); - if (id == null) - return null; - - return Readers[id.Value].reader; - } - - private static int? GetReaderIndex() { string? line = Console.ReadLine(); if (line == null) { Console.WriteLine("Invalid input."); @@ -37,11 +15,13 @@ public static class ControllerSelector { Console.WriteLine("Invalid number."); return null; } - if (id <= 0 || id >= Readers.Count) { - Console.WriteLine("Invalid number"); - return null; - } - return id - 1; + return GetReader(id); } + + private static IGamepadReader? GetReader(int id) => id switch { + 1 => new JoyConRead(), + 2 => new WiimoteRead(), + _ => null + }; } \ No newline at end of file diff --git a/SlidePresenter/ControllerSlidePresenter.csproj b/SlidePresenter/ControllerSlidePresenter.csproj index 2324e47..66a3144 100644 --- a/SlidePresenter/ControllerSlidePresenter.csproj +++ b/SlidePresenter/ControllerSlidePresenter.csproj @@ -5,31 +5,16 @@ net8.0 enable enable - win-x64;linux-x64 + SwitchSlidePresenter - - $(DefineConstants);OS_WINDOWS - - - $(DefineConstants);OS_LINUX - - - $(DefineConstants);OS_MAC - - - - $(DefineConstants);JoyCon - $(DefineConstants);Wiimote - - - - + + - + diff --git a/SlidePresenter/GamepadReader/IGamepadReader.cs b/SlidePresenter/IGamepadReader.cs similarity index 69% rename from SlidePresenter/GamepadReader/IGamepadReader.cs rename to SlidePresenter/IGamepadReader.cs index f3f0be5..2fbf258 100644 --- a/SlidePresenter/GamepadReader/IGamepadReader.cs +++ b/SlidePresenter/IGamepadReader.cs @@ -1,4 +1,4 @@ -namespace ControllerSlidePresenter.GamepadReader; +namespace SwitchSlidePresenter; public interface IGamepadReader { public event Action NextSlide; diff --git a/SlidePresenter/InputSender/IInputSender.cs b/SlidePresenter/InputSender/IInputSender.cs deleted file mode 100644 index 461f08d..0000000 --- a/SlidePresenter/InputSender/IInputSender.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace ControllerSlidePresenter.InputSender; - -public interface IInputSender { - void NextSlide(); - void PreviousSlide(); -} \ No newline at end of file diff --git a/SlidePresenter/InputSender/LinuxInputSender.cs b/SlidePresenter/InputSender/LinuxInputSender.cs deleted file mode 100644 index 011061c..0000000 --- a/SlidePresenter/InputSender/LinuxInputSender.cs +++ /dev/null @@ -1,32 +0,0 @@ -#if OS_LINUX -using System.Diagnostics; - -namespace ControllerSlidePresenter.InputSender; - -public class LinuxInputSender : IInputSender { - private const string PageUp = "0xff55"; - private const string PageDown = "0xff56"; - - public void NextSlide() { - SendKeys(PageDown); - } - - public void PreviousSlide() { - SendKeys(PageUp); - } - - private static void SendKeys(string keycode) { - Process proc = new() { - StartInfo = { - FileName = "xdotool", - Arguments = $"key {keycode}", - UseShellExecute = false, - RedirectStandardError = false, - RedirectStandardInput = false, - RedirectStandardOutput = false - } - }; - proc.Start(); - } -} -#endif \ No newline at end of file diff --git a/SlidePresenter/InputSender/MacInputSender.cs b/SlidePresenter/InputSender/MacInputSender.cs deleted file mode 100644 index 270704a..0000000 --- a/SlidePresenter/InputSender/MacInputSender.cs +++ /dev/null @@ -1,13 +0,0 @@ -#if OS_MAC -namespace ControllerSlidePresenter.InputSender; - -public class MacInputSender : IInputSender { - public void NextSlide() { - - } - - public void PreviousSlide() { - - } -} -#endif \ No newline at end of file diff --git a/SlidePresenter/InputSender/WindowsInputSender.cs b/SlidePresenter/InputSender/WindowsInputSender.cs deleted file mode 100644 index 3ecb477..0000000 --- a/SlidePresenter/InputSender/WindowsInputSender.cs +++ /dev/null @@ -1,49 +0,0 @@ -#if OS_WINDOWS -using System.Runtime.InteropServices; -using Win32Api; - -namespace ControllerSlidePresenter.InputSender; - -public class WindowsInputSender : IInputSender { - 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 void NextSlide() => SimulateKeyPress(VK_NEXT); - public 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))); - } -} -#endif \ No newline at end of file diff --git a/SlidePresenter/GamepadReader/JoyConRead.cs b/SlidePresenter/JoyConRead.cs similarity index 63% rename from SlidePresenter/GamepadReader/JoyConRead.cs rename to SlidePresenter/JoyConRead.cs index 9459431..09e750f 100644 --- a/SlidePresenter/GamepadReader/JoyConRead.cs +++ b/SlidePresenter/JoyConRead.cs @@ -1,17 +1,15 @@ -#if JoyCon -using System.Text; +using System.Text; using HidSharp; -using HidSharp.Reports; using wtf.cluster.JoyCon; using wtf.cluster.JoyCon.ExtraData; using wtf.cluster.JoyCon.InputData; using wtf.cluster.JoyCon.InputReports; -namespace ControllerSlidePresenter.GamepadReader; +namespace SwitchSlidePresenter; public class JoyConRead : IGamepadReader { - public event Action? NextSlide; - public event Action? PrevSlide; + public event Action NextSlide; + public event Action PrevSlide; public async Task Read() { Console.OutputEncoding = Encoding.UTF8; @@ -64,44 +62,10 @@ public class JoyConRead : IGamepadReader { } private static HidDevice? GetHidDevice() { - return OperatingSystem.IsWindows() - ? GetWindowsHidDevice() - : GetNonWindowsHidDevice(); - } - - private static HidDevice? GetWindowsHidDevice() { DeviceList list = DeviceList.Local; IEnumerable? nintendos = list.GetHidDevices(0x057e); - HidDevice? device = nintendos.FirstOrDefault(); - return device; - } - private static HidDevice? GetNonWindowsHidDevice() { - HidDevice? device = null; - DeviceList list = DeviceList.Local; - - IEnumerable? hidDevices = list.GetHidDevices(); - foreach (HidDevice d in hidDevices) - { - ReportDescriptor? rd = d.GetReportDescriptor(); - if (rd == null) continue; - 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) continue; - - device = d; - break; - } - return device; + return nintendos.FirstOrDefault(); } private Task OnJoyConOnReportReceived(JoyCon _, IJoyConReport input) { @@ -125,5 +89,4 @@ public class JoyConRead : IGamepadReader { private static bool NextPressed(ButtonsSimple input) { return input.ZLorZR || input.Down; } -} -#endif +} \ No newline at end of file diff --git a/SlidePresenter/Linux.cs b/SlidePresenter/Linux.cs deleted file mode 100644 index b13c885..0000000 --- a/SlidePresenter/Linux.cs +++ /dev/null @@ -1,55 +0,0 @@ -#if OS_LINUX -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace ControllerSlidePresenter; - -public static class Linux { - public static bool CanRun() { - if (getuid() != 0) { - Console.WriteLine("On Linux you need tu run as 'sudo'"); - return false; - } - if (!IsXdoToolInstalled()) - return false; - - return true; - } - - private static bool IsXdoToolInstalled() { - string result = RunShellCommand("which xdotool"); - if (!string.IsNullOrEmpty(result)) - return true; - - Console.WriteLine("xdotool is not installed."); - return false; - } - - private static string RunShellCommand(string command) { - try { - ProcessStartInfo procStartInfo = new("bash", "-c \"" + command + "\"") { - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true, - }; - - using Process process = new(); - process.StartInfo = procStartInfo; - process.Start(); - - string result = process.StandardOutput.ReadToEnd(); - process.WaitForExit(); - - return result.Trim(); - } - catch (Exception ex) { - Console.WriteLine("Error: " + ex.Message); - return string.Empty; - } - } - - - [DllImport("libc")] - private static extern uint getuid(); -} -#endif diff --git a/SlidePresenter/Program.cs b/SlidePresenter/Program.cs index 53df152..9a8e12c 100644 --- a/SlidePresenter/Program.cs +++ b/SlidePresenter/Program.cs @@ -1,13 +1,6 @@ -using ControllerSlidePresenter.GamepadReader; - -namespace ControllerSlidePresenter { +namespace SwitchSlidePresenter { internal abstract class Program { private static async Task Main() { -#if OS_LINUX - if (!Linux.CanRun()) - return; -#endif - IGamepadReader? reader = ControllerSelector.GetReader(); if (reader == null) { Console.WriteLine("Invalid Controller Selected."); diff --git a/SlidePresenter/SlideSwitcher.cs b/SlidePresenter/SlideSwitcher.cs index 3cd7c5d..d81adae 100644 --- a/SlidePresenter/SlideSwitcher.cs +++ b/SlidePresenter/SlideSwitcher.cs @@ -1,33 +1,65 @@ -using ControllerSlidePresenter.GamepadReader; -using ControllerSlidePresenter.InputSender; +using System.Runtime.InteropServices; +using Win32Api; -namespace ControllerSlidePresenter; +namespace SwitchSlidePresenter; public class SlideSwitcher : IDisposable { private readonly IGamepadReader? _reader; -#if OS_WINDOWS - private readonly IInputSender _inputSender = new WindowsInputSender(); -#elif OS_MAC - private readonly IInputSender _inputSender = new MacInputSender(); -#elif OS_LINUX - private readonly IInputSender _inputSender = new LinuxInputSender(); -#endif + 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; - if (_reader == null) return; - _reader.NextSlide += NextSlide; _reader.PrevSlide += PreviousSlide; } public void Dispose() { - if (_reader == null) return; _reader.NextSlide -= NextSlide; _reader.PrevSlide -= PreviousSlide; } - private void NextSlide() => _inputSender.NextSlide(); - private void PreviousSlide() => _inputSender.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))); + } } \ No newline at end of file diff --git a/SlidePresenter/GamepadReader/WiimoteRead.cs b/SlidePresenter/WiimoteRead.cs similarity index 87% rename from SlidePresenter/GamepadReader/WiimoteRead.cs rename to SlidePresenter/WiimoteRead.cs index 5918a2e..1a25b90 100644 --- a/SlidePresenter/GamepadReader/WiimoteRead.cs +++ b/SlidePresenter/WiimoteRead.cs @@ -1,11 +1,10 @@ -#if Wiimote -using WiimoteLib.NetCore; +using WiimoteLib.NetCore; -namespace ControllerSlidePresenter.GamepadReader; +namespace SwitchSlidePresenter; public class WiimoteRead : IGamepadReader { - public event Action? NextSlide; - public event Action? PrevSlide; + public event Action NextSlide; + public event Action PrevSlide; public async Task Read() { Wiimote wiimote = new(); @@ -47,5 +46,4 @@ public class WiimoteRead : IGamepadReader { private static bool NextPressed(ButtonState input) { return input.A || input.Right; } -} -#endif +} \ No newline at end of file