Compare commits
No commits in common. "master" and "0.2" have entirely different histories.
18 changed files with 155 additions and 563 deletions
41
.github/workflows/build.yml
vendored
41
.github/workflows/build.yml
vendored
|
|
@ -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/
|
|
||||||
144
.idea/.idea.SwitchSlidePresenter/.idea/workspace.xml
generated
144
.idea/.idea.SwitchSlidePresenter/.idea/workspace.xml
generated
|
|
@ -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">{
|
|
||||||
"lastFilter": {
|
|
||||||
"state": "OPEN",
|
|
||||||
"assignee": "GerardGascon"
|
|
||||||
}
|
|
||||||
}</component>
|
|
||||||
<component name="GithubPullRequestsUISettings">{
|
|
||||||
"selectedUrlAndAccountId": {
|
|
||||||
"url": "https://github.com/GerardGascon/Switch-Slide-Presenter.git",
|
|
||||||
"accountId": "0af66c52-cbb7-4844-ad24-01b5c5b9bee8"
|
|
||||||
}
|
|
||||||
}</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">{
|
|
||||||
"associatedIndex": 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">{
|
|
||||||
"keyToString": {
|
|
||||||
".NET Project.SwitchSlidePresenter.executor": "Run",
|
|
||||||
"Publish to folder.Publish ControllerSlidePresenter to folder.executor": "Run",
|
|
||||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
||||||
"git-widget-placeholder": "master",
|
|
||||||
"node.js.detected.package.eslint": "true",
|
|
||||||
"node.js.detected.package.tslint": "true",
|
|
||||||
"node.js.selected.package.eslint": "(autodetect)",
|
|
||||||
"node.js.selected.package.tslint": "(autodetect)",
|
|
||||||
"nodejs_package_manager_path": "npm",
|
|
||||||
"vue.rearranger.settings.migration": "true"
|
|
||||||
},
|
|
||||||
"keyToStringList": {
|
|
||||||
"rider.external.source.directories": [
|
|
||||||
"C:\\Users\\ggasc\\AppData\\Roaming\\JetBrains\\Rider2024.1\\resharper-host\\DecompilerCache",
|
|
||||||
"C:\\Users\\ggasc\\AppData\\Roaming\\JetBrains\\Rider2024.1\\resharper-host\\SourcesCache",
|
|
||||||
"C:\\Users\\ggasc\\AppData\\Local\\Symbols\\src"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}</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>
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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>
|
|
||||||
41
README.md
41
README.md
|
|
@ -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))
|
|
||||||
|
|
@ -1,33 +1,11 @@
|
||||||
using ControllerSlidePresenter.GamepadReader;
|
namespace SwitchSlidePresenter;
|
||||||
|
|
||||||
namespace ControllerSlidePresenter;
|
|
||||||
|
|
||||||
public static class ControllerSelector {
|
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() {
|
public static IGamepadReader? GetReader() {
|
||||||
if (Readers.Count == 1)
|
|
||||||
return Readers[0].reader;
|
|
||||||
|
|
||||||
Console.WriteLine("Write a number to select controller type:");
|
Console.WriteLine("Write a number to select controller type:");
|
||||||
for (int i = 0; i < Readers.Count; i++)
|
Console.WriteLine("[1] - JoyCon");
|
||||||
Console.WriteLine($"[{i+1}] - {Readers[i].name}");
|
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();
|
string? line = Console.ReadLine();
|
||||||
if (line == null) {
|
if (line == null) {
|
||||||
Console.WriteLine("Invalid input.");
|
Console.WriteLine("Invalid input.");
|
||||||
|
|
@ -37,11 +15,13 @@ public static class ControllerSelector {
|
||||||
Console.WriteLine("Invalid number.");
|
Console.WriteLine("Invalid number.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (id <= 0 || id >= Readers.Count) {
|
|
||||||
Console.WriteLine("Invalid number");
|
return GetReader(id);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return id - 1;
|
private static IGamepadReader? GetReader(int id) => id switch {
|
||||||
}
|
1 => new JoyConRead(),
|
||||||
|
2 => new WiimoteRead(),
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -5,31 +5,16 @@
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<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>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="JoyCon.NET" Version="1.0.1" Condition="$(DefineConstants.Contains(JoyCon))" />
|
<PackageReference Include="JoyCon.NET" Version="1.0.1" />
|
||||||
<PackageReference Include="WiimoteLib.NetCore" Version="1.0.0" Condition="$(DefineConstants.Contains(Wiimote))" />
|
<PackageReference Include="WiimoteLib.NetCore" Version="1.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Win32Api\Win32Api.csproj" Condition="$([MSBuild]::IsOSPlatform('Windows'))"/>
|
<ProjectReference Include="..\Win32Api\Win32Api.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
namespace ControllerSlidePresenter.GamepadReader;
|
namespace SwitchSlidePresenter;
|
||||||
|
|
||||||
public interface IGamepadReader {
|
public interface IGamepadReader {
|
||||||
public event Action NextSlide;
|
public event Action NextSlide;
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
namespace ControllerSlidePresenter.InputSender;
|
|
||||||
|
|
||||||
public interface IInputSender {
|
|
||||||
void NextSlide();
|
|
||||||
void PreviousSlide();
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
#if OS_MAC
|
|
||||||
namespace ControllerSlidePresenter.InputSender;
|
|
||||||
|
|
||||||
public class MacInputSender : IInputSender {
|
|
||||||
public void NextSlide() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PreviousSlide() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,24 +1,22 @@
|
||||||
#if JoyCon
|
using System.Text;
|
||||||
using System.Text;
|
|
||||||
using HidSharp;
|
using HidSharp;
|
||||||
using HidSharp.Reports;
|
|
||||||
using wtf.cluster.JoyCon;
|
using wtf.cluster.JoyCon;
|
||||||
using wtf.cluster.JoyCon.ExtraData;
|
using wtf.cluster.JoyCon.ExtraData;
|
||||||
using wtf.cluster.JoyCon.InputData;
|
using wtf.cluster.JoyCon.InputData;
|
||||||
using wtf.cluster.JoyCon.InputReports;
|
using wtf.cluster.JoyCon.InputReports;
|
||||||
|
|
||||||
namespace ControllerSlidePresenter.GamepadReader;
|
namespace SwitchSlidePresenter;
|
||||||
|
|
||||||
public class JoyConRead : IGamepadReader {
|
public class JoyConRead : IGamepadReader {
|
||||||
public event Action? NextSlide;
|
public event Action NextSlide;
|
||||||
public event Action? PrevSlide;
|
public event Action PrevSlide;
|
||||||
|
|
||||||
public async Task Read() {
|
public async Task Read() {
|
||||||
Console.OutputEncoding = Encoding.UTF8;
|
Console.OutputEncoding = Encoding.UTF8;
|
||||||
|
|
||||||
HidDevice? device = GetHidDevice();
|
HidDevice? device = GetHidDevice();
|
||||||
if (device == null) {
|
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.WriteLine("Press any key to exit program.");
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
return;
|
return;
|
||||||
|
|
@ -41,14 +39,11 @@ public class JoyConRead : IGamepadReader {
|
||||||
|
|
||||||
Console.WriteLine("JoyCon ready for presenting.");
|
Console.WriteLine("JoyCon ready for presenting.");
|
||||||
Console.WriteLine("Press Enter to exit program.");
|
Console.WriteLine("Press Enter to exit program.");
|
||||||
while (Console.ReadKey().Key != ConsoleKey.Enter) {
|
while (Console.ReadKey().Key != ConsoleKey.Enter) { }
|
||||||
await Task.Yield();
|
|
||||||
}
|
|
||||||
joycon.Stop();
|
joycon.Stop();
|
||||||
|
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("Stopped.");
|
Console.WriteLine("Stopped.");
|
||||||
await Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task LogDeviceInfo(JoyCon joycon) {
|
private static async Task LogDeviceInfo(JoyCon joycon) {
|
||||||
|
|
@ -64,43 +59,37 @@ public class JoyConRead : IGamepadReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HidDevice? GetHidDevice() {
|
private static HidDevice? GetHidDevice() {
|
||||||
return OperatingSystem.IsWindows()
|
|
||||||
? GetWindowsHidDevice()
|
|
||||||
: GetNonWindowsHidDevice();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static HidDevice? GetWindowsHidDevice() {
|
|
||||||
DeviceList list = DeviceList.Local;
|
DeviceList list = DeviceList.Local;
|
||||||
IEnumerable<HidDevice>? nintendos = list.GetHidDevices(0x057e);
|
|
||||||
HidDevice? device = nintendos.FirstOrDefault();
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static HidDevice? GetNonWindowsHidDevice() {
|
|
||||||
HidDevice? device = null;
|
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;
|
|
||||||
|
|
||||||
|
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;
|
device = d;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,4 +115,3 @@ public class JoyConRead : IGamepadReader {
|
||||||
return input.ZLorZR || input.Down;
|
return input.ZLorZR || input.Down;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,13 +1,6 @@
|
||||||
using ControllerSlidePresenter.GamepadReader;
|
namespace SwitchSlidePresenter {
|
||||||
|
|
||||||
namespace ControllerSlidePresenter {
|
|
||||||
internal abstract class Program {
|
internal abstract class Program {
|
||||||
private static async Task Main() {
|
private static async Task Main() {
|
||||||
#if OS_LINUX
|
|
||||||
if (!Linux.CanRun())
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
IGamepadReader? reader = ControllerSelector.GetReader();
|
IGamepadReader? reader = ControllerSelector.GetReader();
|
||||||
if (reader == null) {
|
if (reader == null) {
|
||||||
Console.WriteLine("Invalid Controller Selected.");
|
Console.WriteLine("Invalid Controller Selected.");
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,65 @@
|
||||||
using ControllerSlidePresenter.GamepadReader;
|
using System.Runtime.InteropServices;
|
||||||
using ControllerSlidePresenter.InputSender;
|
using Win32Api;
|
||||||
|
|
||||||
namespace ControllerSlidePresenter;
|
namespace SwitchSlidePresenter;
|
||||||
|
|
||||||
public class SlideSwitcher : IDisposable {
|
public class SlideSwitcher : IDisposable {
|
||||||
private readonly IGamepadReader? _reader;
|
private readonly IGamepadReader? _reader;
|
||||||
|
|
||||||
#if OS_WINDOWS
|
private const uint INPUT_KEYBOARD = 1;
|
||||||
private readonly IInputSender _inputSender = new WindowsInputSender();
|
private const ushort VK_NEXT = 0x22;
|
||||||
#elif OS_MAC
|
private const ushort VK_PRIOR = 0x21;
|
||||||
private readonly IInputSender _inputSender = new MacInputSender();
|
private const uint KEYEVENTF_KEYDOWN = 0x0000;
|
||||||
#elif OS_LINUX
|
private const uint KEYEVENTF_KEYUP = 0x0002;
|
||||||
private readonly IInputSender _inputSender = new LinuxInputSender();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public SlideSwitcher(IGamepadReader? reader) {
|
public SlideSwitcher(IGamepadReader? reader) {
|
||||||
_reader = reader;
|
_reader = reader;
|
||||||
if (_reader == null) return;
|
|
||||||
|
|
||||||
_reader.NextSlide += NextSlide;
|
_reader.NextSlide += NextSlide;
|
||||||
_reader.PrevSlide += PreviousSlide;
|
_reader.PrevSlide += PreviousSlide;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
if (_reader == null) return;
|
|
||||||
_reader.NextSlide -= NextSlide;
|
_reader.NextSlide -= NextSlide;
|
||||||
_reader.PrevSlide -= PreviousSlide;
|
_reader.PrevSlide -= PreviousSlide;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NextSlide() => _inputSender.NextSlide();
|
private static void NextSlide() {
|
||||||
private void PreviousSlide() => _inputSender.PreviousSlide();
|
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)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
58
SlidePresenter/WiimoteRead.cs
Normal file
58
SlidePresenter/WiimoteRead.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue