Added XInput Support using ViGEm.
- Rumble support - Ability to rebind keys - No longer need to use "Also use for axes/buttons" - System-wide compatability (use your joycons with Steam, or something) - Requires ViGEm driver (provided in release)
This commit is contained in:
parent
90139b3aa4
commit
854aa02c2f
6 changed files with 139 additions and 20 deletions
|
@ -5,6 +5,8 @@ VisualStudioVersion = 15.0.27130.2036
|
|||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BetterJoyForCemu", "BetterJoyForCemu\BetterJoyForCemu.csproj", "{1BF709E9-C133-41DF-933A-C9FF3F664C7B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ViGEmClient", "..\..\..\Downloads\ViGEm-master\ViGEm-master\NET\ViGEmClient\ViGEmClient.csproj", "{AA18EBCF-7E9D-4BC5-8760-E8C6E9A773E5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -27,6 +29,18 @@ Global
|
|||
{1BF709E9-C133-41DF-933A-C9FF3F664C7B}.Release|x64.Build.0 = Release|x64
|
||||
{1BF709E9-C133-41DF-933A-C9FF3F664C7B}.Release|x86.ActiveCfg = Release|x86
|
||||
{1BF709E9-C133-41DF-933A-C9FF3F664C7B}.Release|x86.Build.0 = Release|x86
|
||||
{AA18EBCF-7E9D-4BC5-8760-E8C6E9A773E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AA18EBCF-7E9D-4BC5-8760-E8C6E9A773E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AA18EBCF-7E9D-4BC5-8760-E8C6E9A773E5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{AA18EBCF-7E9D-4BC5-8760-E8C6E9A773E5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{AA18EBCF-7E9D-4BC5-8760-E8C6E9A773E5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{AA18EBCF-7E9D-4BC5-8760-E8C6E9A773E5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{AA18EBCF-7E9D-4BC5-8760-E8C6E9A773E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AA18EBCF-7E9D-4BC5-8760-E8C6E9A773E5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AA18EBCF-7E9D-4BC5-8760-E8C6E9A773E5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{AA18EBCF-7E9D-4BC5-8760-E8C6E9A773E5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{AA18EBCF-7E9D-4BC5-8760-E8C6E9A773E5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{AA18EBCF-7E9D-4BC5-8760-E8C6E9A773E5}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -3,4 +3,23 @@
|
|||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
|
||||
</startup>
|
||||
|
||||
<appSettings>
|
||||
<!--Motion Server IP: the default is localhost; you can change it to 0.0.0.0 (all interfaces) or a specific LAN IP
|
||||
which is **useful if you want to access the server from another computer in a network.** Default: 127.0.0.1-->
|
||||
<add key="IP" value="127.0.0.1" />
|
||||
|
||||
<!--Motion Server port: the default is 26760; if it conflicts with another server set it to anything valid
|
||||
but in that case also change the port in PadTest and cemuhook.ini accordingly. Default: 26760 -->
|
||||
<add key="Port" value="26760" />
|
||||
|
||||
<!--Rumble motor period in millisec. Lower means more granular vibration, higher is more stable.-->
|
||||
<!--The response of rumble does not only depend on this setting and it's always high. Default: 100 -->
|
||||
<add key="RumblePeriod" value="100" />
|
||||
|
||||
<!--The controller's HD rumble settings for the low/high frequency rumble. Change to change the pitch of the rumble.-->
|
||||
<!--Don't set above ~1200. Default: 160 and 320 -->
|
||||
<add key="LowFreqRumble" value="160" />
|
||||
<add key="HighFreqRumble" value="320" />
|
||||
</appSettings>
|
||||
</configuration>
|
|
@ -101,5 +101,11 @@
|
|||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\Downloads\ViGEm-master\ViGEm-master\NET\ViGEmClient\ViGEmClient.csproj">
|
||||
<Project>{aa18ebcf-7e9d-4bc5-8760-e8c6e9a773e5}</Project>
|
||||
<Name>ViGEmClient</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -4,10 +4,14 @@ using System.Diagnostics;
|
|||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Configuration;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Nefarius.ViGEm.Client;
|
||||
using Nefarius.ViGEm.Client.Targets;
|
||||
using Nefarius.ViGEm.Client.Targets.Xbox360;
|
||||
|
||||
namespace BetterJoyForCemu {
|
||||
public class Joycon {
|
||||
float timing = 120.0f;
|
||||
|
@ -197,7 +201,13 @@ namespace BetterJoyForCemu {
|
|||
public PhysicalAddress PadMacAddress = new PhysicalAddress(new byte[] { 01, 02, 03, 04, 05, 06 });
|
||||
public ulong Timestamp = 0;
|
||||
public int packetCounter = 0;
|
||||
//
|
||||
// For XInput
|
||||
public Xbox360Controller xin;
|
||||
Xbox360Report report;
|
||||
|
||||
int rumblePeriod = Int32.Parse(ConfigurationSettings.AppSettings["RumblePeriod"]);
|
||||
int lowFreq = Int32.Parse(ConfigurationSettings.AppSettings["LowFreqRumble"]);
|
||||
int highFreq = Int32.Parse(ConfigurationSettings.AppSettings["HighFreqRumble"]);
|
||||
|
||||
public Joycon(IntPtr handle_, bool imu, bool localize, float alpha, bool left, int id = 0, bool isPro=false, bool usb = false) {
|
||||
handle = handle_;
|
||||
|
@ -210,7 +220,21 @@ namespace BetterJoyForCemu {
|
|||
PadId = id;
|
||||
this.isPro = isPro;
|
||||
isUSB = usb;
|
||||
|
||||
if (isLeft || isPro) {
|
||||
xin = new Xbox360Controller(Program.emClient);
|
||||
xin.FeedbackReceived += ReceiveRumble;
|
||||
report = new Xbox360Report();
|
||||
}
|
||||
}
|
||||
|
||||
public void ReceiveRumble(object sender, Nefarius.ViGEm.Client.Targets.Xbox360.Xbox360FeedbackReceivedEventArgs e) {
|
||||
SetRumble(lowFreq, highFreq, (float) e.LargeMotor / (float) 255, rumblePeriod);
|
||||
|
||||
if (other != null)
|
||||
other.SetRumble(lowFreq, highFreq, (float)e.LargeMotor / (float)255, rumblePeriod);
|
||||
}
|
||||
|
||||
public void DebugPrint(String s, DebugType d) {
|
||||
if (debug_type == DebugType.NONE) return;
|
||||
if (d == DebugType.ALL || d == debug_type || debug_type == DebugType.ALL) {
|
||||
|
@ -343,6 +367,9 @@ namespace BetterJoyForCemu {
|
|||
packetCounter++;
|
||||
if (Program.server != null)
|
||||
Program.server.NewReportIncoming(this);
|
||||
|
||||
if (xin != null)
|
||||
xin.SendReport(report);
|
||||
}
|
||||
|
||||
if (ts_en == raw_buf[1]) {
|
||||
|
@ -457,9 +484,25 @@ namespace BetterJoyForCemu {
|
|||
buttons[(int)Button.STICK2] = ((report_buf[4] & (!isLeft ? 0x08 : 0x04)) != 0);
|
||||
buttons[(int)Button.SHOULDER2_1] = (report_buf[3 + (!isLeft ? 2 : 0)] & 0x40) != 0;
|
||||
buttons[(int)Button.SHOULDER2_2] = (report_buf[3 + (!isLeft ? 2 : 0)] & 0x80) != 0;
|
||||
|
||||
report.SetButtonState(Xbox360Buttons.A, buttons[(int)Button.B]);
|
||||
report.SetButtonState(Xbox360Buttons.B, buttons[(int)Button.A]);
|
||||
report.SetButtonState(Xbox360Buttons.Y, buttons[(int)Button.X]);
|
||||
report.SetButtonState(Xbox360Buttons.X, buttons[(int)Button.Y]);
|
||||
report.SetButtonState(Xbox360Buttons.Up, buttons[(int)Button.DPAD_UP]);
|
||||
report.SetButtonState(Xbox360Buttons.Down, buttons[(int)Button.DPAD_DOWN]);
|
||||
report.SetButtonState(Xbox360Buttons.Left, buttons[(int)Button.DPAD_LEFT]);
|
||||
report.SetButtonState(Xbox360Buttons.Right, buttons[(int)Button.DPAD_RIGHT]);
|
||||
report.SetButtonState(Xbox360Buttons.Back, buttons[(int)Button.MINUS]);
|
||||
report.SetButtonState(Xbox360Buttons.Start, buttons[(int)Button.PLUS]);
|
||||
report.SetButtonState(Xbox360Buttons.Guide, buttons[(int)Button.HOME]);
|
||||
report.SetButtonState(Xbox360Buttons.LeftShoulder, buttons[(int)Button.SHOULDER_1]);
|
||||
report.SetButtonState(Xbox360Buttons.RightShoulder, buttons[(int)Button.SHOULDER2_1]);
|
||||
report.SetButtonState(Xbox360Buttons.LeftThumb, buttons[(int)Button.STICK]);
|
||||
report.SetButtonState(Xbox360Buttons.RightThumb, buttons[(int)Button.STICK2]);
|
||||
}
|
||||
|
||||
if (isLeft && other != null) {
|
||||
if (other != null) {
|
||||
buttons[(int)Button.B] = other.buttons[(int)Button.DPAD_DOWN];
|
||||
buttons[(int)Button.A] = other.buttons[(int)Button.DPAD_RIGHT];
|
||||
buttons[(int)Button.X] = other.buttons[(int)Button.DPAD_UP];
|
||||
|
@ -468,24 +511,35 @@ namespace BetterJoyForCemu {
|
|||
buttons[(int)Button.STICK2] = other.buttons[(int)Button.STICK];
|
||||
buttons[(int)Button.SHOULDER2_1] = other.buttons[(int)Button.SHOULDER_1];
|
||||
buttons[(int)Button.SHOULDER2_2] = other.buttons[(int)Button.SHOULDER_2];
|
||||
}
|
||||
|
||||
if (isLeft && other != null) {
|
||||
buttons[(int)Button.HOME] = other.buttons[(int)Button.HOME];
|
||||
buttons[(int)Button.PLUS] = other.buttons[(int)Button.PLUS];
|
||||
}
|
||||
|
||||
if (!isLeft && other != null) {
|
||||
buttons[(int)Button.B] = other.buttons[(int)Button.DPAD_DOWN];
|
||||
buttons[(int)Button.A] = other.buttons[(int)Button.DPAD_RIGHT];
|
||||
buttons[(int)Button.X] = other.buttons[(int)Button.DPAD_UP];
|
||||
buttons[(int)Button.Y] = other.buttons[(int)Button.DPAD_LEFT];
|
||||
|
||||
buttons[(int)Button.STICK2] = other.buttons[(int)Button.STICK];
|
||||
buttons[(int)Button.SHOULDER2_1] = other.buttons[(int)Button.SHOULDER_1];
|
||||
buttons[(int)Button.SHOULDER2_2] = other.buttons[(int)Button.SHOULDER_2];
|
||||
|
||||
buttons[(int)Button.MINUS] = other.buttons[(int)Button.MINUS];
|
||||
}
|
||||
|
||||
if (!isPro && other != null && xin != null) {
|
||||
report.SetButtonState(Xbox360Buttons.A, buttons[(int)(isLeft ? Button.B : Button.DPAD_DOWN)]);
|
||||
report.SetButtonState(Xbox360Buttons.B, buttons[(int)(isLeft ? Button.A : Button.DPAD_RIGHT)]);
|
||||
report.SetButtonState(Xbox360Buttons.Y, buttons[(int)(isLeft ? Button.X : Button.DPAD_UP)]);
|
||||
report.SetButtonState(Xbox360Buttons.X, buttons[(int)(isLeft ? Button.Y : Button.DPAD_LEFT)]);
|
||||
report.SetButtonState(Xbox360Buttons.Up, buttons[(int)(isLeft ? Button.DPAD_UP : Button.X)]);
|
||||
report.SetButtonState(Xbox360Buttons.Down, buttons[(int)(isLeft ? Button.DPAD_DOWN : Button.B)]);
|
||||
report.SetButtonState(Xbox360Buttons.Left, buttons[(int)(isLeft ? Button.DPAD_LEFT : Button.Y)]);
|
||||
report.SetButtonState(Xbox360Buttons.Right, buttons[(int)(isLeft ? Button.DPAD_RIGHT : Button.A)]);
|
||||
report.SetButtonState(Xbox360Buttons.Back, buttons[(int)Button.MINUS]);
|
||||
report.SetButtonState(Xbox360Buttons.Start, buttons[(int)Button.PLUS]);
|
||||
report.SetButtonState(Xbox360Buttons.Guide, buttons[(int)Button.HOME]);
|
||||
report.SetButtonState(Xbox360Buttons.LeftShoulder, buttons[(int)(isLeft ? Button.SHOULDER_1 : Button.SHOULDER2_1)]);
|
||||
report.SetButtonState(Xbox360Buttons.RightShoulder, buttons[(int)(isLeft ? Button.SHOULDER2_1 : Button.SHOULDER_1)]);
|
||||
report.SetButtonState(Xbox360Buttons.LeftThumb, buttons[(int)(isLeft ? Button.STICK : Button.STICK2)]);
|
||||
report.SetButtonState(Xbox360Buttons.RightThumb, buttons[(int)(isLeft ? Button.STICK2 : Button.STICK)]);
|
||||
}
|
||||
|
||||
lock (buttons_up) {
|
||||
lock (buttons_down) {
|
||||
for (int i = 0; i < buttons.Length; ++i) {
|
||||
|
@ -495,6 +549,16 @@ namespace BetterJoyForCemu {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (xin != null) {
|
||||
report.SetAxis(Xbox360Axes.LeftThumbX, (short)Math.Max(Int16.MinValue, Math.Min(Int16.MaxValue, stick[0] * (stick[0] > 0 ? Int16.MaxValue : -Int16.MinValue))));
|
||||
report.SetAxis(Xbox360Axes.LeftThumbY, (short)Math.Max(Int16.MinValue, Math.Min(Int16.MaxValue, stick[1] * (stick[0] > 0 ? Int16.MaxValue : -Int16.MinValue))));
|
||||
report.SetAxis(Xbox360Axes.RightThumbX, (short)Math.Max(Int16.MinValue, Math.Min(Int16.MaxValue, stick2[0] * (stick[0] > 0 ? Int16.MaxValue : -Int16.MinValue))));
|
||||
report.SetAxis(Xbox360Axes.RightThumbY, (short)Math.Max(Int16.MinValue, Math.Min(Int16.MaxValue, stick2[1] * (stick[0] > 0 ? Int16.MaxValue : -Int16.MinValue))));
|
||||
report.SetAxis(Xbox360Axes.LeftTrigger, (short)(buttons[(int)(isLeft ? Button.SHOULDER_2 : Button.SHOULDER2_2)] ? Int16.MaxValue : 0));
|
||||
report.SetAxis(Xbox360Axes.RightTrigger, (short)(buttons[(int)(isLeft ? Button.SHOULDER2_2 : Button.SHOULDER_2)] ? Int16.MaxValue : 0));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
private void ExtractIMUValues(byte[] report_buf, int n = 0) {
|
||||
|
|
|
@ -8,10 +8,16 @@ using System.Numerics;
|
|||
using System.Threading;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Timers;
|
||||
using static BetterJoyForCemu.HIDapi;
|
||||
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Diagnostics;
|
||||
|
||||
using static BetterJoyForCemu.HIDapi;
|
||||
using Nefarius.ViGEm.Client;
|
||||
using Nefarius.ViGEm.Client.Targets;
|
||||
using System.Net;
|
||||
using System.Configuration;
|
||||
|
||||
namespace BetterJoyForCemu {
|
||||
public class JoyconManager {
|
||||
public bool EnableIMU = true;
|
||||
|
@ -126,6 +132,10 @@ namespace BetterJoyForCemu {
|
|||
public void Start() {
|
||||
for (int i = 0; i < j.Count; ++i) {
|
||||
Joycon jc = j[i];
|
||||
|
||||
if (jc.xin != null)
|
||||
jc.xin.Connect();
|
||||
|
||||
byte LEDs = 0x0;
|
||||
LEDs |= (byte)(0x1 << i);
|
||||
jc.Attach(leds_: LEDs);
|
||||
|
@ -141,6 +151,11 @@ namespace BetterJoyForCemu {
|
|||
public void OnApplicationQuit() {
|
||||
for (int i = 0; i < j.Count; ++i) {
|
||||
j[i].Detach();
|
||||
|
||||
if (j[i].xin != null) {
|
||||
j[i].xin.Disconnect();
|
||||
j[i].xin.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -188,7 +203,11 @@ namespace BetterJoyForCemu {
|
|||
public static UdpServer server;
|
||||
static double pollsPerSecond = 120.0;
|
||||
|
||||
public static ViGEmClient emClient;
|
||||
|
||||
static void Main(string[] args) {
|
||||
emClient = new ViGEmClient(); // Manages emulated XInput
|
||||
|
||||
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) {
|
||||
// Get local BT host MAC
|
||||
if (nic.NetworkInterfaceType != NetworkInterfaceType.FastEthernetFx && nic.NetworkInterfaceType != NetworkInterfaceType.Wireless80211) {
|
||||
|
@ -204,10 +223,7 @@ namespace BetterJoyForCemu {
|
|||
|
||||
server = new UdpServer(mgr.j);
|
||||
|
||||
//updateThread = new Thread(new ThreadStart(mgr.Update));
|
||||
//updateThread.Start();
|
||||
|
||||
server.Start(26760);
|
||||
server.Start(IPAddress.Parse(ConfigurationSettings.AppSettings["IP"]), Int32.Parse(ConfigurationSettings.AppSettings["Port"]));
|
||||
HighResTimer timer = new HighResTimer(pollsPerSecond, new HighResTimer.ActionDelegate(mgr.Update));
|
||||
timer.Start();
|
||||
|
||||
|
|
|
@ -278,7 +278,7 @@ namespace BetterJoyForCemu {
|
|||
}
|
||||
}
|
||||
|
||||
public void Start(int port=26760) {
|
||||
public void Start(IPAddress ip, int port=26760) {
|
||||
if (running) {
|
||||
if (udpSock != null) {
|
||||
udpSock.Close();
|
||||
|
@ -288,7 +288,7 @@ namespace BetterJoyForCemu {
|
|||
}
|
||||
|
||||
udpSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
try { udpSock.Bind(new IPEndPoint(IPAddress.Loopback, port)); } catch (SocketException ex) {
|
||||
try { udpSock.Bind(new IPEndPoint(ip, port)); } catch (SocketException ex) {
|
||||
udpSock.Close();
|
||||
udpSock = null;
|
||||
|
||||
|
@ -301,7 +301,7 @@ namespace BetterJoyForCemu {
|
|||
serverId = BitConverter.ToUInt32(randomBuf, 0);
|
||||
|
||||
running = true;
|
||||
Console.WriteLine("Starting server.");
|
||||
Console.WriteLine("Starting server on {0}:{1}", ip.ToString(), port);
|
||||
StartReceive();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue