Compare commits

..

No commits in common. "master" and "0.2" have entirely different histories.
master ... 0.2

18 changed files with 155 additions and 563 deletions

View file

@ -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/

View file

@ -1,144 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoGeneratedRunConfigurationManager">
<projectFile>SwitchSlidePresenter.csproj</projectFile>
</component>
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="756a98cd-4cfe-4a7d-9110-f232a435843b" name="Changes" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="DpaMonitoringSettings">
<option name="autoShow" value="false" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GitHubPullRequestSearchHistory">{
&quot;lastFilter&quot;: {
&quot;state&quot;: &quot;OPEN&quot;,
&quot;assignee&quot;: &quot;GerardGascon&quot;
}
}</component>
<component name="GithubPullRequestsUISettings">{
&quot;selectedUrlAndAccountId&quot;: {
&quot;url&quot;: &quot;https://github.com/GerardGascon/Switch-Slide-Presenter.git&quot;,
&quot;accountId&quot;: &quot;0af66c52-cbb7-4844-ad24-01b5c5b9bee8&quot;
}
}</component>
<component name="HighlightingSettingsPerFile">
<setting file="file://$USER_HOME$/AppData/Local/Symbols/src/dotnet/runtime/bf5e279d9239bfef5bb1b8d6212f1b971c434606/src/coreclr/System.Private.CoreLib/src/System/Threading/Monitor.CoreCLR.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/045ef3cb636f4aaa894e8cefcc5e4b367e00/c1/d22d509a/Wiimote.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/SlidePresenter/Program.cs" root0="FORCE_HIGHLIGHTING" />
</component>
<component name="MetaFilesCheckinStateConfiguration" checkMetaFiles="true" />
<component name="ProblemsViewState">
<option name="selectedTabId" value="Toolset" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 6
}</component>
<component name="ProjectId" id="2htCW7BWCLo4IJC4dn1HIEGkg1I" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true">
<OptionsSetting value="false" id="Update" />
<ConfirmationsSetting value="2" id="Add" />
</component>
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;.NET Project.SwitchSlidePresenter.executor&quot;: &quot;Run&quot;,
&quot;Publish to folder.Publish ControllerSlidePresenter to folder.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
},
&quot;keyToStringList&quot;: {
&quot;rider.external.source.directories&quot;: [
&quot;C:\\Users\\ggasc\\AppData\\Roaming\\JetBrains\\Rider2024.1\\resharper-host\\DecompilerCache&quot;,
&quot;C:\\Users\\ggasc\\AppData\\Roaming\\JetBrains\\Rider2024.1\\resharper-host\\SourcesCache&quot;,
&quot;C:\\Users\\ggasc\\AppData\\Local\\Symbols\\src&quot;
]
}
}</component>
<component name="RunManager" selected="Publish to folder.Publish ControllerSlidePresenter to folder">
<configuration name="Publish ControllerSlidePresenter to folder" type="DotNetFolderPublish" factoryName="Publish to folder">
<riderPublish configuration="Release" delete_existing_files="true" platform="Any CPU" runtime="Portable" target_folder="$PROJECT_DIR$/SlidePresenter/bin/Release/net8.0/publish" target_framework="net8.0" uuid_high="-467478054054508763" uuid_low="-8110965388391740835" />
<method v="2" />
</configuration>
<configuration name="ControllerSlidePresenter" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/SlidePresenter/bin/Debug/net8.0/ControllerSlidePresenter.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/SlidePresenter/bin/Debug/net8.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/SlidePresenter/ControllerSlidePresenter.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net8.0" />
<method v="2">
<option name="Build" />
</method>
</configuration>
<list>
<item itemvalue=".NET Project.ControllerSlidePresenter" />
<item itemvalue="Publish to folder.Publish ControllerSlidePresenter to folder" />
</list>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="756a98cd-4cfe-4a7d-9110-f232a435843b" name="Changes" comment="" />
<created>1718404015037</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1718404015037</updated>
<workItem from="1718404016227" duration="5433000" />
</task>
<task id="LOCAL-00001" summary="feat: Added wiimote support and cleaned up the project">
<option name="closed" value="true" />
<created>1718408820801</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1718408820801</updated>
</task>
<task id="LOCAL-00002" summary="rename: switch slide project to controller slide">
<option name="closed" value="true" />
<created>1718408956240</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1718408956240</updated>
</task>
<option name="localTasksCounter" value="3" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="UnityCheckinConfiguration" checkUnsavedScenes="true" />
<component name="UnityProjectConfiguration" hasMinimizedUI="false" />
<component name="VcsManagerConfiguration">
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
<MESSAGE value="feat: Added wiimote support and cleaned up the project" />
<MESSAGE value="rename: switch slide project to controller slide" />
<option name="LAST_COMMIT_MESSAGE" value="rename: switch slide project to controller slide" />
</component>
</project>

View file

@ -1,6 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Publish Linux-x64" type="DotNetFolderPublish" factoryName="Publish to folder">
<riderPublish configuration="Release" platform="Any CPU" runtime="linux-x64" self_contained="true" target_folder="$PROJECT_DIR$/SlidePresenter/bin/Release/net8.0/linux-x64/publish" target_framework="net8.0" uuid_high="-467478054054508763" uuid_low="-8110965388391740835" />
<method v="2" />
</configuration>
</component>

View file

@ -1,6 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Publish Windows-x64" type="DotNetFolderPublish" factoryName="Publish to folder">
<riderPublish configuration="Release" include_native_libs_for_self_extract="true" platform="Any CPU" produce_single_file="true" runtime="win-x64" self_contained="true" target_folder="$PROJECT_DIR$/SlidePresenter/bin/Release/net8.0/win-x64/publish" target_framework="net8.0" uuid_high="-467478054054508763" uuid_low="-8110965388391740835" />
<method v="2" />
</configuration>
</component>

View file

@ -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))

View file

@ -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
};
}

View file

@ -5,31 +5,16 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
<RootNamespace>SwitchSlidePresenter</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Windows'))">
<DefineConstants>$(DefineConstants);OS_WINDOWS</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Linux'))">
<DefineConstants>$(DefineConstants);OS_LINUX</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('OSX'))">
<DefineConstants>$(DefineConstants);OS_MAC</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<DefineConstants>$(DefineConstants);JoyCon</DefineConstants>
<DefineConstants>$(DefineConstants);Wiimote</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JoyCon.NET" Version="1.0.1" Condition="$(DefineConstants.Contains(JoyCon))" />
<PackageReference Include="WiimoteLib.NetCore" Version="1.0.0" Condition="$(DefineConstants.Contains(Wiimote))" />
<PackageReference Include="JoyCon.NET" Version="1.0.1" />
<PackageReference Include="WiimoteLib.NetCore" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Win32Api\Win32Api.csproj" Condition="$([MSBuild]::IsOSPlatform('Windows'))"/>
<ProjectReference Include="..\Win32Api\Win32Api.csproj" />
</ItemGroup>
</Project>

View file

@ -1,51 +0,0 @@
#if Wiimote
using WiimoteLib.NetCore;
namespace ControllerSlidePresenter.GamepadReader;
public class WiimoteRead : IGamepadReader {
public event Action? NextSlide;
public event Action? PrevSlide;
public async Task Read() {
Wiimote wiimote = new();
wiimote.Connect();
if (string.IsNullOrEmpty(wiimote.HIDDevicePath)) {
Console.WriteLine("No controller. Please connect Wiimote via Bluetooth.");
Console.WriteLine("Press any key to exit program.");
Console.ReadKey();
return;
}
wiimote.WiimoteChanged += WiimoteChanged;
Console.WriteLine("Wiimote ready for presenting.");
Console.WriteLine("Press Enter to exit program.");
while (Console.ReadKey().Key != ConsoleKey.Enter) {
await Task.Yield();
}
wiimote.Disconnect();
Console.WriteLine();
Console.WriteLine("Stopped.");
await Task.CompletedTask;
}
private void WiimoteChanged(object? sender, WiimoteChangedEventArgs e) {
if (PreviousPressed(e.WiimoteState.ButtonState)) {
PrevSlide?.Invoke();
}
if (NextPressed(e.WiimoteState.ButtonState)) {
NextSlide?.Invoke();
}
}
private static bool PreviousPressed(ButtonState input) {
return input.B || input.Left;
}
private static bool NextPressed(ButtonState input) {
return input.A || input.Right;
}
}
#endif

View file

@ -1,4 +1,4 @@
namespace ControllerSlidePresenter.GamepadReader;
namespace SwitchSlidePresenter;
public interface IGamepadReader {
public event Action NextSlide;

View file

@ -1,6 +0,0 @@
namespace ControllerSlidePresenter.InputSender;
public interface IInputSender {
void NextSlide();
void PreviousSlide();
}

View file

@ -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

View file

@ -1,13 +0,0 @@
#if OS_MAC
namespace ControllerSlidePresenter.InputSender;
public class MacInputSender : IInputSender {
public void NextSlide() {
}
public void PreviousSlide() {
}
}
#endif

View file

@ -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

View file

@ -1,24 +1,22 @@
#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;
HidDevice? device = GetHidDevice();
if (device == null) {
Console.WriteLine("No controller. Please connect Joy-Con via Bluetooth.");
Console.WriteLine("No controller. Please connect Joy-Con or Pro controller via Bluetooth.");
Console.WriteLine("Press any key to exit program.");
Console.ReadKey();
return;
@ -41,14 +39,11 @@ public class JoyConRead : IGamepadReader {
Console.WriteLine("JoyCon ready for presenting.");
Console.WriteLine("Press Enter to exit program.");
while (Console.ReadKey().Key != ConsoleKey.Enter) {
await Task.Yield();
}
while (Console.ReadKey().Key != ConsoleKey.Enter) { }
joycon.Stop();
Console.WriteLine();
Console.WriteLine("Stopped.");
await Task.CompletedTask;
}
private static async Task LogDeviceInfo(JoyCon joycon) {
@ -64,42 +59,36 @@ public class JoyConRead : IGamepadReader {
}
private static HidDevice? GetHidDevice() {
return OperatingSystem.IsWindows()
? GetWindowsHidDevice()
: GetNonWindowsHidDevice();
}
private static HidDevice? GetWindowsHidDevice() {
DeviceList list = DeviceList.Local;
IEnumerable<HidDevice>? nintendos = list.GetHidDevices(0x057e);
HidDevice? device = nintendos.FirstOrDefault();
return device;
}
private static HidDevice? GetNonWindowsHidDevice() {
HidDevice? device = null;
DeviceList list = DeviceList.Local;
IEnumerable<HidDevice>? 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;
if (OperatingSystem.IsWindows()) {
var nintendos = list.GetHidDevices(0x057e);
device = nintendos.FirstOrDefault();
} else {
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;
}
}
}
}
return device;
}
@ -125,5 +114,4 @@ public class JoyConRead : IGamepadReader {
private static bool NextPressed(ButtonsSimple input) {
return input.ZLorZR || input.Down;
}
}
#endif
}

View file

@ -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

View file

@ -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.");

View file

@ -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)));
}
}

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);
}
}