Compare commits

..

1 commit

Author SHA1 Message Date
David Khachaturov
488d152508
Update README.md 2020-05-06 13:31:01 +01:00
32 changed files with 3685 additions and 5206 deletions

View file

@ -1,18 +0,0 @@
---
name: Bug Report / Problems
about: PLEASE USE THE ISSUE SEARCH FUNCTION FIRST BEFORE MAKING A NEW ISSUE
title: "[BUG]"
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Controller (please complete the following information):**
- Type: [e.g. Pro/Split Joycons]
- Connection: [e.g. USB/BT]

View file

@ -1,14 +0,0 @@
---
name: Feature Request
about: Suggest an idea for this project
title: "[ENHANCEMENT]"
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.

View file

@ -1,120 +0,0 @@
# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
###############################
# Core EditorConfig Options #
###############################
root = true
# All files
[*]
indent_size = 4
indent_style = space
# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 4
end_of_line = crlf
insert_final_newline = true
charset = utf-8-bom
###############################
# .NET Coding Conventions #
###############################
[*.{cs,vb}]
# Organize usings
dotnet_sort_system_directives_first = true
# this. preferences
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_property = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_event = false:silent
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
dotnet_style_readonly_field = true:suggestion
# Expression-level preferences
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
###############################
# Naming Conventions #
###############################
# Style Definitions
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# Use PascalCase for constant fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
dotnet_naming_symbols.constant_fields.required_modifiers = const
###############################
# C# Coding Conventions #
###############################
[*.cs]
# var preferences
csharp_style_var_for_built_in_types = true:silent
csharp_style_var_when_type_is_apparent = true:silent
csharp_style_var_elsewhere = true:silent
# Expression-bodied members
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
# Null-checking preferences
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
# Expression-level preferences
csharp_prefer_braces = true:silent
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
###############################
# C# Formatting Rules #
###############################
# New line preferences
csharp_new_line_before_open_brace = false
csharp_new_line_before_else = false
csharp_new_line_before_catch = false
csharp_new_line_before_finally = false
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left
# Space preferences
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_around_binary_operators = before_and_after
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
# Wrapping preferences
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true

View file

@ -30,8 +30,8 @@
this.btn_add = new System.Windows.Forms.Button();
this.btn_remove = new System.Windows.Forms.Button();
this.group_props = new System.Windows.Forms.GroupBox();
this.label2 = new System.Windows.Forms.Label();
this.chooseType = new System.Windows.Forms.ComboBox();
this.chk_isLeft = new System.Windows.Forms.CheckBox();
this.chk_isPro = new System.Windows.Forms.CheckBox();
this.btn_applyAndClose = new System.Windows.Forms.Button();
this.btn_apply = new System.Windows.Forms.Button();
this.lbl_all = new System.Windows.Forms.Label();
@ -83,8 +83,8 @@
//
// group_props
//
this.group_props.Controls.Add(this.label2);
this.group_props.Controls.Add(this.chooseType);
this.group_props.Controls.Add(this.chk_isLeft);
this.group_props.Controls.Add(this.chk_isPro);
this.group_props.Location = new System.Drawing.Point(122, 142);
this.group_props.Name = "group_props";
this.group_props.Size = new System.Drawing.Size(150, 81);
@ -92,23 +92,29 @@
this.group_props.TabStop = false;
this.group_props.Text = "Settings";
//
// label2
// chk_isLeft
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(10, 22);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(31, 13);
this.label2.TabIndex = 1;
this.label2.Text = "Type";
this.chk_isLeft.AutoSize = true;
this.chk_isLeft.CheckAlign = System.Drawing.ContentAlignment.MiddleRight;
this.chk_isLeft.Location = new System.Drawing.Point(6, 42);
this.chk_isLeft.Name = "chk_isLeft";
this.chk_isLeft.Size = new System.Drawing.Size(96, 17);
this.chk_isLeft.TabIndex = 1;
this.chk_isLeft.Text = "Left Joycon? ";
this.chk_isLeft.UseVisualStyleBackColor = true;
this.chk_isLeft.CheckedChanged += new System.EventHandler(this.chk_isLeft_CheckedChanged);
//
// chooseType
// chk_isPro
//
this.chooseType.FormattingEnabled = true;
this.chooseType.Location = new System.Drawing.Point(47, 19);
this.chooseType.Name = "chooseType";
this.chooseType.Size = new System.Drawing.Size(97, 21);
this.chooseType.TabIndex = 0;
this.chooseType.SelectedValueChanged += new System.EventHandler(this.chooseType_SelectedValueChanged);
this.chk_isPro.AutoSize = true;
this.chk_isPro.CheckAlign = System.Drawing.ContentAlignment.MiddleRight;
this.chk_isPro.Location = new System.Drawing.Point(6, 19);
this.chk_isPro.Name = "chk_isPro";
this.chk_isPro.Size = new System.Drawing.Size(95, 17);
this.chk_isPro.TabIndex = 0;
this.chk_isPro.Text = "Pro Controller?";
this.chk_isPro.UseVisualStyleBackColor = true;
this.chk_isPro.CheckedChanged += new System.EventHandler(this.chk_isPro_CheckedChanged);
//
// btn_applyAndClose
//
@ -197,10 +203,10 @@
private System.Windows.Forms.Button btn_applyAndClose;
private System.Windows.Forms.Button btn_apply;
private System.Windows.Forms.Label lbl_all;
private System.Windows.Forms.CheckBox chk_isPro;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.CheckBox chk_isLeft;
private System.Windows.Forms.ToolTip tip_device;
private System.Windows.Forms.Button btn_refresh;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.ComboBox chooseType;
}
}

View file

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
@ -14,92 +13,27 @@ using static BetterJoyForCemu.HIDapi;
namespace BetterJoyForCemu {
public partial class _3rdPartyControllers : Form {
public class SController {
public String name;
public ushort product_id;
public ushort vendor_id;
public string serial_number;
public byte type; // 1 is pro, 2 is left joy, 3 is right joy
public SController(String name, ushort vendor_id, ushort product_id, byte type, string serial_number) {
this.product_id = product_id; this.vendor_id = vendor_id; this.type = type;
this.serial_number = serial_number;
this.name = name;
}
public override bool Equals(object obj) {
//Check for null and compare run-time types.
if ((obj == null) || !this.GetType().Equals(obj.GetType())) {
return false;
} else {
SController s = (SController)obj;
return (s.product_id == product_id) && (s.vendor_id == vendor_id) && (s.serial_number == serial_number);
}
}
public override int GetHashCode() {
return Tuple.Create(product_id, vendor_id, serial_number).GetHashCode();
}
public override string ToString() {
return name ?? $"Unidentified Device ({this.product_id})";
}
public string Serialise() {
return String.Format("{0}|{1}|{2}|{3}|{4}", name, vendor_id, product_id, type, serial_number);
}
}
static readonly string path;
static _3rdPartyControllers() {
path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)
+ "\\3rdPartyControllers";
}
public _3rdPartyControllers() {
InitializeComponent();
list_allControllers.HorizontalScrollbar = true; list_customControllers.HorizontalScrollbar = true;
list_allControllers.DisplayMember = "Text";
list_allControllers.ValueMember = "Value";
list_customControllers.DisplayMember = "Text";
list_customControllers.ValueMember = "Value";
chooseType.Items.AddRange(new String[] { "Pro Controller", "Left Joycon", "Right Joycon" });
chooseType.FormattingEnabled = true;
group_props.Controls.Add(chooseType);
group_props.Enabled = false;
if (File.Exists(path)) {
using (StreamReader file = new StreamReader(path)) {
string line = String.Empty;
while ((line = file.ReadLine()) != null && (line != String.Empty)) {
String[] split = line.Split('|');
//won't break existing config file
String serial_number = "";
if (split.Length > 4) {
serial_number = split[4];
}
list_customControllers.Items.Add(new SController(split[0], ushort.Parse(split[1]), ushort.Parse(split[2]), byte.Parse(split[3]), serial_number));
}
}
}
CopyCustomControllers();
RefreshControllerList();
}
public void CopyCustomControllers() {
Program.thirdPartyCons.Clear();
foreach (SController v in list_customControllers.Items) {
Program.thirdPartyCons.Add(v);
}
group_props.Controls.Add(chk_isLeft);
group_props.Controls.Add(chk_isPro);
group_props.Enabled = false;
}
private bool ContainsText(ListBox a, String manu) {
foreach (SController v in a.Items) {
if (v == null)
foreach (var v in a.Items)
{
dynamic d = v as dynamic;
if (d.Text == null)
continue;
if (v.name == null)
continue;
if (v.name.Equals(manu))
if (d.Text.Equals(manu))
return true;
}
return false;
@ -114,29 +48,17 @@ namespace BetterJoyForCemu {
while (ptr != IntPtr.Zero) {
enumerate = (hid_device_info)Marshal.PtrToStructure(ptr, typeof(hid_device_info));
if (enumerate.serial_number == null) {
ptr = enumerate.next;
continue;
}
// TODO: try checking against interface number instead
String name = enumerate.product_string + '(' + enumerate.vendor_id + '-' + enumerate.product_id + '-'+enumerate.serial_number+')';
if (!ContainsText(list_customControllers, name) && !ContainsText(list_allControllers, name)) {
list_allControllers.Items.Add(new SController(name, enumerate.vendor_id, enumerate.product_id, 0, enumerate.serial_number));
// 0 type is undefined
Console.WriteLine("Found controller "+ name);
}
if (!ContainsText(list_customControllers, enumerate.product_string) && !ContainsText(list_allControllers, enumerate.product_string))
list_allControllers.Items.Add(new { Text = enumerate.product_string, Value = enumerate });
ptr = enumerate.next;
}
HIDapi.hid_free_enumeration(top_ptr);
}
private void btn_add_Click(object sender, EventArgs e) {
if (list_allControllers.SelectedItem != null) {
list_customControllers.Items.Add(list_allControllers.SelectedItem);
list_allControllers.Items.Remove(list_allControllers.SelectedItem);
list_allControllers.ClearSelected();
}
}
@ -145,18 +67,20 @@ namespace BetterJoyForCemu {
if (list_customControllers.SelectedItem != null) {
list_allControllers.Items.Add(list_customControllers.SelectedItem);
list_customControllers.Items.Remove(list_customControllers.SelectedItem);
list_customControllers.ClearSelected();
}
}
private void btn_apply_Click(object sender, EventArgs e) {
String sc = "";
foreach (SController v in list_customControllers.Items) {
sc += v.Serialise() + "\r\n";
private void chk_isPro_CheckedChanged(object sender, EventArgs e) {
}
File.WriteAllText(path, sc);
CopyCustomControllers();
private void chk_isLeft_CheckedChanged(object sender, EventArgs e) {
}
private void btn_apply_Click(object sender, EventArgs e) {
}
private void btn_applyAndClose_Click(object sender, EventArgs e) {
@ -174,22 +98,16 @@ namespace BetterJoyForCemu {
private void list_allControllers_SelectedValueChanged(object sender, EventArgs e) {
if (list_allControllers.SelectedItem != null)
tip_device.Show((list_allControllers.SelectedItem as SController).name, list_allControllers);
tip_device.Show((list_allControllers.SelectedItem as dynamic).Text, list_allControllers);
}
private void list_customControllers_SelectedValueChanged(object sender, EventArgs e) {
if (list_customControllers.SelectedItem != null) {
SController v = (list_customControllers.SelectedItem as SController);
tip_device.Show(v.name, list_customControllers);
chooseType.SelectedIndex = v.type - 1;
tip_device.Show((list_customControllers.SelectedItem as dynamic).Text, list_customControllers);
group_props.Enabled = true;
} else {
chooseType.SelectedIndex = -1;
} else
group_props.Enabled = false;
}
}
private void list_customControllers_MouseDown(object sender, MouseEventArgs e) {
if (e.Y > list_customControllers.ItemHeight * list_customControllers.Items.Count)
@ -200,12 +118,5 @@ namespace BetterJoyForCemu {
if (e.Y > list_allControllers.ItemHeight * list_allControllers.Items.Count)
list_allControllers.SelectedItems.Clear();
}
private void chooseType_SelectedValueChanged(object sender, EventArgs e) {
if (list_customControllers.SelectedItem != null) {
SController v = (list_customControllers.SelectedItem as SController);
v.type = (byte)(chooseType.SelectedIndex + 1);
}
}
}
}

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
@ -13,25 +13,17 @@
<!--Whether the Motion Server is enabled or not. Default: true -->
<add key="MotionServer" value="true" />
<!--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: 300 -->
<add key="RumblePeriod" value="300" />
<!--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: 40 and 120 -->
<!--To have "stronger" rumble, try setting the low/high to 160/320-->
<add key="LowFreqRumble" value="40" />
<add key="HighFreqRumble" value="120" />
<!--Don't set above ~1200. Default: 20 and 20 -->
<add key="LowFreqRumble" value="20" />
<add key="HighFreqRumble" value="20" />
<!--Rumble Setting. Turns rumble on or off.-->
<!--On is "true"; off is "false". Default: true -->
<add key="EnableRumble" value="true" />
<!--Enables a input when shaking a controller, only works with DS4 for now, replaces the touchpad input (Button 13 on DirectInput)-->
<!--On is "true"; off is "false". Default: false -->
<add key="EnableShakeInput" value="false" />
<!--How sensitve the shake detection should be. Default: 10-->
<add key="ShakeInputSensitivity" value="10" />
<!--How often should the shake input run in milliseconds. -->
<!--Don't set this lower than 15 -->
<!-- Default: 200 -->
<add key="ShakeInputDelay" value="200" />
<!--Swap A-B buttons; if on, this mimicks the (half of) Xbox layout by the button name, rather than by the physical layout.-->
<!--Also swaps buttons when using "Also use for buttons/axes"-->
<!--On is "true"; off is "false". Default: false -->
@ -41,33 +33,6 @@
<!--On is "true"; off is "false". Default: false -->
<add key="SwapXY" value="false" />
<!-- Allows for calibration of the controller's gyro. Adds a "Calibrate" button.-->
<!-- When "true", click the "Calibrate" button once to get gyro calibrate data.-->
<!-- When enabled, can only calibrate one controller at a time.-->
<!-- Default: false -->
<add key="AllowCalibration" value="false" />
<!-- Allows to emulate the n64 range of joystick (.8, .8) instead of real xbox360 (1, 1). -->
<add key="N64Range" value="true" />
<!-- Default calibration; used for third party controller -->
<add key="acc_sensiti" value="16384,16384,16384"/>
<add key="gyr_sensiti" value="18642,18642,18642"/>
<add key="stick_cal" value="0x780,0x780,0x780,0x830,0x780,0x780"/>
<add key="deadzone" value="200"/>
<add key="stick2_cal" value="0x780,0x780,0x780,0x830,0x780,0x780"/>
<add key="deadzone2" value="200"/>
<!-- Scales the xy coordinates each of the joysticks report. Increase this if you can't push the stick to its maximum. -->
<add key="StickScalingFactor" value="1.00" />
<add key="StickScalingFactor2" value="1.00" />
<!-- Allows use of gyroscope tilting to get full control of the slider values (big triggers)-->
<!-- Works on pro controller and joined joycons (pro controller case - triggers combined, joycons case - separate tilt)-->
<!-- Default: false -->
<add key="GyroAnalogSliders" value="false" />
<!-- Change to -400 to change direction of tilt needed. Positive is ramp up if pointing up-->
<!-- Default: 400 -->
<add key="GyroAnalogSensitivity" value="400" />
<!-- Determines whether or not the program should purge the affected devices list upon exit -->
<!-- Should prevent any more issues of the controller being unusable after the program (even though this can be fixed if you read the README) -->
<!-- Default: true -->
@ -77,41 +42,28 @@
<!-- Default: true -->
<add key="PurgeWhitelist" value="false" />
<!-- Determines whether or not to use HidGuardian (improves compatibility with other programs, like Steam, when set to "false") -->
<!-- When "true", BetterJoy will hide the Pro/Joycons from other programs to prevent glitching out on exit and to prevent DI/XI clashes in certain programs -->
<!-- When "true", BetterJoyForCemu will hide the Pro/Joycons from other programs to prevent glitching out on exit and to prevent DI/XI clashes in certain programs -->
<!-- Default: false -->
<add key="UseHIDG" value="false" />
<add key="UseHIDG" value="true" />
<!-- Determines whether or not to enable (experimental - currently default controller to pro) support for 3rd-party controllers. Adds a "Calibrate" button. -->
<!-- When "true", click "Calibrate" button once to get gyro calibrate data. -->
<!-- When enabled, can only calibrate one controller at a time. -->
<!-- Default: false -->
<add key="NonOriginalController" value="false" />
<!-- The program will keep the HOME button LED ring light on at all times. -->
<!-- Default: true -->
<add key="HomeLEDOn" value="true"/>
<!-- Will use multiple lights to display the current player rather than a single LED-->
<add key="UseIncrementalLights" value="true" />
<!-- Determines whether or not to translate gyro movements into joystick ("joy") or mouse movement ("mouse"), or have no effect ("none") -->
<!-- When "joy_left" or "joy_right", turn gyro movements into respective left/right joystick (mouse-like) movements -->
<!-- When "joy", turn gyro movements into joystick movement (left/right depends on setting) [not yet implemented]-->
<!-- When "mouse", turn gyro movements into mouse movement. Press either stick-button to reset to middle of primary display -->
<!-- Default: none -->
<add key="GyroToJoyOrMouse" value="false"/>
<!-- Whether to use filtered IMU or raw gyro values (the latter is more responsive) -->
<!-- Default: true -->
<add key="UseFilteredIMU" value="true"/>
<!-- Beta value of AHRS. Affects divergence of filter -->
<!-- Default: 0.05 -->
<add key="AHRS_beta" value="0.05"/>
<add key="GyroToJoyOrMouse" value="none"/>
<!-- Sensitivity of gyro-to-mouse movements -->
<!-- Default: 1200; 800 (if using raw values, decrease by a factor of ~15) -->
<add key="GyroMouseSensitivityX" value="1200"/>
<add key="GyroMouseSensitivityY" value="800"/>
<!-- Sensitivity of gyro-to-joystick movements -->
<!-- Default: 40.0; 10.0 (if using raw values, decrease by a factor of ~15: eg 2.6, 0.6) -->
<add key="GyroStickSensitivityX" value="40.0"/>
<add key="GyroStickSensitivityY" value="10.0"/>
<!-- Stick range reduction when gyro-to-joystick is enabled and active; divides range by factor (so 1 is no change; 1.5 is halved range (with deadzone in mind)) -->
<!-- Default: 1.5 -->
<add key="GyroStickReduction" value="1.5"/>
<!-- Gyro Hold/Toggle activation; true will require the mapped button to be continuously held down to keep gyro active -->
<!-- Default: true [hold], false [toggle] -->
<add key="GyroHoldToggle" value="true"/>
<!-- Default: 50 -->
<add key="GyroMouseSensitivity" value="50"/>
<!-- When two joycons are connected, it would take the gyro movement of the right joycon for mouse movement. This swaps that -->
<!-- Default: false -->
<add key="GyroMouseLeftHanded" value="false"/>
@ -121,40 +73,11 @@
<add key="DragToggle" value="false"/>
<!-- Determines whether or not the program will expose detected controllers as Xbox 360 controllers -->
<!-- When "false", BetterJoy is only usable with programs that support UDPServer. -->
<!-- When "false", BetterJoyForCemu is only usable with CEMU. -->
<!-- Default: true -->
<add key="ShowAsXInput" value="true" />
<!-- Have ShowAsXInput as false if using this -->
<!-- Default: false -->
<add key="ShowAsDS4" value="false"/>
<!-- Automatically power off controllers at program exit -->
<!-- Default: false -->
<add key="AutoPowerOff" value="false" />
<!-- Automatically power off controllers after a period of inactivity (in minutes) -->
<!-- Default: 30 (-1 indicates infinite time) -->
<add key="PowerOffInactivity" value="-1" />
<!-- Power off controllers when Capture (left only) or Home (right only or combined) buttons are pressed for a long interval (2s) -->
<!-- Default: true -->
<add key="HomeLongPowerOff" value="true" />
<!-- Double click sticks on joycons (L3, R3) to change joycon orientation -->
<!-- Default: true -->
<add key="ChangeOrientationDoubleClick" value="true" />
<!-- Do not rejoin joycons once split via clicking on their icon -->
<!-- When 'true' allows you to double-click the joycons to split them and then change their orientation to vertical -->
<!-- Default: false -->
<add key="DoNotRejoinJoycons" value="false" />
<!-- Determines what type of debugging information to be printed -->
<!-- None = 0 -->
<!-- All = 1 -->
<!-- Comms = 2 -->
<!-- Threading = 3 -->
<!-- IMU = 4 -->
<!-- Rumble = 5 -->
<!-- Shake Input = 6 -->
<!-- Default: 0 -->
<add key="DebugType" value="0" />
</appSettings>
</configuration>

View file

@ -102,8 +102,8 @@
<Reference Include="JetBrains.Annotations, Version=2020.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
<HintPath>..\packages\JetBrains.Annotations.2020.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
</Reference>
<Reference Include="Nefarius.ViGEm.Client, Version=1.17.178.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nefarius.ViGEm.Client.1.17.178\lib\net452\Nefarius.ViGEm.Client.dll</HintPath>
<Reference Include="Nefarius.ViGEm.Client, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nefarius.ViGEm.Client.1.0.0\lib\net452\Nefarius.ViGEm.Client.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
@ -119,8 +119,8 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="WindowsInput, Version=6.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\WindowsInput.6.3.0\lib\net461\WindowsInput.dll</HintPath>
<Reference Include="WindowsInput, Version=6.1.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\WindowsInput.6.1.1\lib\net461\WindowsInput.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
@ -130,13 +130,9 @@
<Compile Include="3rdPartyControllers.Designer.cs">
<DependentUpon>3rdPartyControllers.cs</DependentUpon>
</Compile>
<Compile Include="Collections\ConcurrentList.cs" />
<Compile Include="Config.cs" />
<Compile Include="Controller\OutputControllerDualShock4.cs" />
<Compile Include="Controller\OutputControllerXbox360.cs" />
<Compile Include="HIDapi.cs" />
<Compile Include="Joycon.cs" />
<Compile Include="MadgwickAHRS.cs" />
<Compile Include="MainForm.cs">
<SubType>Form</SubType>
</Compile>
@ -180,12 +176,6 @@
<None Include="Drivers\HIDGuardian\_drivers\HidGuardian\HidGuardian.sys">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<Content Include="Drivers\ViGEmBusSetup_x64.msi">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Drivers\ViGEmBusSetup_x86.msi">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
@ -238,14 +228,13 @@
<Content Include="Drivers\README.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Icons\snes.png" />
<Content Include="Icons\ultra.png" />
<None Include="Properties\app.manifest" />
<Content Include="Icons\betterjoyforcemu_icon.ico" />
<Content Include="x86\hidapi.dll">
<Content Include="Drivers\ViGEmBus_Setup_1.16.116.exe">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="x64\hidapi.dll">
<Content Include="Icons\snes.png" />
<None Include="Properties\app.manifest" />
<Content Include="Icons\betterjoyforcemu_icon.ico" />
<Content Include="hidapi.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Icons\cross.png" />

View file

@ -1,116 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace BetterJoyForCemu.Collections {
// https://codereview.stackexchange.com/a/125341
public class ConcurrentList<T> : IList<T> {
#region Fields
private IList<T> _internalList;
private readonly object lockObject = new object();
#endregion
#region ctor
public ConcurrentList() {
_internalList = new List<T>();
}
public ConcurrentList(int capacity) {
_internalList = new List<T>(capacity);
}
public ConcurrentList(IEnumerable<T> list) {
_internalList = new List<T>();
foreach (T item in list) {
_internalList.Add(item);
}
}
#endregion
public T this[int index] {
get {
return LockInternalListAndGet(l => l[index]);
}
set {
LockInternalListAndCommand(l => l[index] = value);
}
}
public int Count {
get {
return LockInternalListAndQuery(l => l.Count);
}
}
public bool IsReadOnly => false;
public void Add(T item) {
LockInternalListAndCommand(l => l.Add(item));
}
public void Clear() {
LockInternalListAndCommand(l => l.Clear());
}
public bool Contains(T item) {
return LockInternalListAndQuery(l => l.Contains(item));
}
public void CopyTo(T[] array, int arrayIndex) {
LockInternalListAndCommand(l => l.CopyTo(array, arrayIndex));
}
public IEnumerator<T> GetEnumerator() {
return LockInternalListAndQuery(l => l.GetEnumerator());
}
public int IndexOf(T item) {
return LockInternalListAndQuery(l => l.IndexOf(item));
}
public void Insert(int index, T item) {
LockInternalListAndCommand(l => l.Insert(index, item));
}
public bool Remove(T item) {
return LockInternalListAndQuery(l => l.Remove(item));
}
public void RemoveAt(int index) {
LockInternalListAndCommand(l => l.RemoveAt(index));
}
IEnumerator IEnumerable.GetEnumerator() {
return LockInternalListAndQuery(l => l.GetEnumerator());
}
#region Utilities
protected virtual void LockInternalListAndCommand(Action<IList<T>> action) {
lock (lockObject) {
action(_internalList);
}
}
protected virtual T LockInternalListAndGet(Func<IList<T>, T> func) {
lock (lockObject) {
return func(_internalList);
}
}
protected virtual TObject LockInternalListAndQuery<TObject>(Func<IList<T>, TObject> query) {
lock (lockObject) {
return query(_internalList);
}
}
#endregion
}
}

View file

@ -1,17 +1,16 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterJoyForCemu {
public static class Config { // stores dynamic configuration, including
static readonly string path;
const string PATH = "settings";
static Dictionary<string, string> variables = new Dictionary<string, string>();
const int settingsNum = 11; // currently - ProgressiveScan, StartInTray + special buttons
static Config() {
path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\settings";
}
const int settingsNum = 10; // currently - ProgressiveScan, StartInTray + special buttons
public static string GetDefaultValue(string s) {
switch(s) {
@ -25,36 +24,15 @@ namespace BetterJoyForCemu {
return "0";
}
// Helper function to count how many lines are in a file
// https://www.dotnetperls.com/line-count
static long CountLinesInFile(string f) {
// Zero based count
long count = -1;
using (StreamReader r = new StreamReader(f)) {
string line;
while ((line = r.ReadLine()) != null) {
count++;
}
}
return count;
}
public static void Init(List<KeyValuePair<string, float[]>> caliData) {
foreach (string s in new string[] { "ProgressiveScan", "StartInTray", "capture", "home", "sl_l", "sl_r", "sr_l", "sr_r", "shake", "reset_mouse", "active_gyro" })
foreach (string s in new string[] { "ProgressiveScan", "StartInTray", "capture", "home", "sl_l", "sl_r", "sr_l", "sr_r", "reset_mouse", "active_gyro" })
variables[s] = GetDefaultValue(s);
if (File.Exists(path)) {
// Reset settings file if old settings
if (CountLinesInFile(path) < settingsNum) {
File.Delete(path);
Init(caliData);
return;
}
using (StreamReader file = new StreamReader(path)) {
string line = String.Empty;
if (File.Exists(PATH)) {
int lineNO = 0;
using (StreamReader file = new StreamReader(PATH)) {
string line = String.Empty;
while ((line = file.ReadLine()) != null) {
string[] vs = line.Split();
try {
@ -77,9 +55,17 @@ namespace BetterJoyForCemu {
} catch { }
lineNO++;
}
}
// if old settings
if (lineNO < settingsNum) {
File.Delete(PATH);
Init(caliData);
}
} else {
using (StreamWriter file = new StreamWriter(path)) {
using (StreamWriter file = new StreamWriter(PATH)) {
foreach (string k in variables.Keys)
file.WriteLine(String.Format("{0} {1}", k, variables[k]));
string caliStr = "";
@ -115,7 +101,7 @@ namespace BetterJoyForCemu {
}
public static void SaveCaliData(List<KeyValuePair<string, float[]>> caliData) {
string[] txt = File.ReadAllLines(path);
string[] txt = File.ReadAllLines(PATH);
if (txt.Length < settingsNum + 1) // no custom calibrations yet
Array.Resize(ref txt, txt.Length + 1);
@ -125,18 +111,18 @@ namespace BetterJoyForCemu {
if (i == 0) space = "";
caliStr += space + caliData[i].Key + "," + String.Join(",", caliData[i].Value);
}
txt[settingsNum] = caliStr;
File.WriteAllLines(path, txt);
txt[2] = caliStr;
File.WriteAllLines(PATH, txt);
}
public static void Save() {
string[] txt = File.ReadAllLines(path);
string[] txt = File.ReadAllLines(PATH);
int NO = 0;
foreach (string k in variables.Keys) {
txt[NO] = String.Format("{0} {1}", k, variables[k]);
NO++;
}
File.WriteAllLines(path, txt);
File.WriteAllLines(PATH, txt);
}
}
}

View file

@ -1,173 +0,0 @@
using System;
using Nefarius.ViGEm.Client.Targets;
using Nefarius.ViGEm.Client.Targets.DualShock4;
namespace BetterJoyForCemu.Controller {
public enum DpadDirection {
None,
Northwest,
West,
Southwest,
South,
Southeast,
East,
Northeast,
North,
}
public struct OutputControllerDualShock4InputState {
public bool triangle;
public bool circle;
public bool cross;
public bool square;
public bool trigger_left;
public bool trigger_right;
public bool shoulder_left;
public bool shoulder_right;
public bool options;
public bool share;
public bool ps;
public bool touchpad;
public bool thumb_left;
public bool thumb_right;
public DpadDirection dPad;
public byte thumb_left_x;
public byte thumb_left_y;
public byte thumb_right_x;
public byte thumb_right_y;
public byte trigger_left_value;
public byte trigger_right_value;
public bool IsEqual(OutputControllerDualShock4InputState other) {
bool buttons = triangle == other.triangle
&& circle == other.circle
&& cross == other.cross
&& square == other.square
&& trigger_left == other.trigger_left
&& trigger_right == other.trigger_right
&& shoulder_left == other.shoulder_left
&& shoulder_right == other.shoulder_right
&& options == other.options
&& share == other.share
&& ps == other.ps
&& touchpad == other.touchpad
&& thumb_left == other.thumb_left
&& thumb_right == other.thumb_right
&& dPad == other.dPad;
bool axis = thumb_left_x == other.thumb_left_x
&& thumb_left_y == other.thumb_left_y
&& thumb_right_x == other.thumb_right_x
&& thumb_right_y == other.thumb_right_y;
bool triggers = trigger_left_value == other.trigger_left_value
&& trigger_right_value == other.trigger_right_value;
return buttons && axis && triggers;
}
}
public class OutputControllerDualShock4 {
private IDualShock4Controller controller;
private OutputControllerDualShock4InputState current_state;
public delegate void DualShock4FeedbackReceivedEventHandler(DualShock4FeedbackReceivedEventArgs e);
public event DualShock4FeedbackReceivedEventHandler FeedbackReceived;
public OutputControllerDualShock4() {
controller = Program.emClient.CreateDualShock4Controller();
Init();
}
public OutputControllerDualShock4(ushort vendor_id, ushort product_id) {
controller = Program.emClient.CreateDualShock4Controller(vendor_id, product_id);
Init();
}
private void Init() {
controller.AutoSubmitReport = false;
controller.FeedbackReceived += FeedbackReceivedRcv;
}
private void FeedbackReceivedRcv(object _sender, DualShock4FeedbackReceivedEventArgs e) {
FeedbackReceived(e);
}
public void Connect() {
controller.Connect();
}
public void Disconnect() {
controller.Disconnect();
}
public bool UpdateInput(OutputControllerDualShock4InputState new_state) {
if (current_state.IsEqual(new_state)) {
return false;
}
DoUpdateInput(new_state);
return true;
}
private void DoUpdateInput(OutputControllerDualShock4InputState new_state) {
controller.SetButtonState(DualShock4Button.Triangle, new_state.triangle);
controller.SetButtonState(DualShock4Button.Circle, new_state.circle);
controller.SetButtonState(DualShock4Button.Cross, new_state.cross);
controller.SetButtonState(DualShock4Button.Square, new_state.square);
controller.SetButtonState(DualShock4Button.ShoulderLeft, new_state.shoulder_left);
controller.SetButtonState(DualShock4Button.ShoulderRight, new_state.shoulder_right);
controller.SetButtonState(DualShock4Button.TriggerLeft, new_state.trigger_left);
controller.SetButtonState(DualShock4Button.TriggerRight, new_state.trigger_right);
controller.SetButtonState(DualShock4Button.ThumbLeft, new_state.thumb_left);
controller.SetButtonState(DualShock4Button.ThumbRight, new_state.thumb_right);
controller.SetButtonState(DualShock4Button.Share, new_state.share);
controller.SetButtonState(DualShock4Button.Options, new_state.options);
controller.SetButtonState(DualShock4SpecialButton.Ps, new_state.ps);
controller.SetButtonState(DualShock4SpecialButton.Touchpad, new_state.touchpad);
controller.SetDPadDirection(MapDPadDirection(new_state.dPad));
controller.SetAxisValue(DualShock4Axis.LeftThumbX, new_state.thumb_left_x);
controller.SetAxisValue(DualShock4Axis.LeftThumbY, new_state.thumb_left_y);
controller.SetAxisValue(DualShock4Axis.RightThumbX, new_state.thumb_right_x);
controller.SetAxisValue(DualShock4Axis.RightThumbY, new_state.thumb_right_y);
controller.SetSliderValue(DualShock4Slider.LeftTrigger, new_state.trigger_left_value);
controller.SetSliderValue(DualShock4Slider.RightTrigger, new_state.trigger_right_value);
controller.SubmitReport();
current_state = new_state;
}
private DualShock4DPadDirection MapDPadDirection(DpadDirection dPad) {
switch (dPad) {
case DpadDirection.None: return DualShock4DPadDirection.None;
case DpadDirection.North: return DualShock4DPadDirection.North;
case DpadDirection.Northeast: return DualShock4DPadDirection.Northeast;
case DpadDirection.East: return DualShock4DPadDirection.East;
case DpadDirection.Southeast: return DualShock4DPadDirection.Southeast;
case DpadDirection.South: return DualShock4DPadDirection.South;
case DpadDirection.Southwest: return DualShock4DPadDirection.Southwest;
case DpadDirection.West: return DualShock4DPadDirection.West;
case DpadDirection.Northwest: return DualShock4DPadDirection.Northwest;
default: throw new NotImplementedException();
}
}
}
}

View file

@ -1,151 +0,0 @@
using Nefarius.ViGEm.Client.Targets;
using Nefarius.ViGEm.Client.Targets.Xbox360;
namespace BetterJoyForCemu.Controller {
public struct OutputControllerXbox360InputState {
// buttons
public bool thumb_stick_left;
public bool thumb_stick_right;
public bool y;
public bool x;
public bool b;
public bool a;
public bool start;
public bool back;
public bool guide;
public bool shoulder_left;
public bool shoulder_right;
// dpad
public bool dpad_up;
public bool dpad_right;
public bool dpad_down;
public bool dpad_left;
// axis
public short axis_left_x;
public short axis_left_y;
public short axis_right_x;
public short axis_right_y;
// triggers
public byte trigger_left;
public byte trigger_right;
public bool IsEqual(OutputControllerXbox360InputState other) {
bool buttons = thumb_stick_left == other.thumb_stick_left
&& thumb_stick_right == other.thumb_stick_right
&& y == other.y
&& x == other.x
&& b == other.b
&& a == other.a
&& start == other.start
&& back == other.back
&& guide == other.guide
&& shoulder_left == other.shoulder_left
&& shoulder_right == other.shoulder_right;
bool dpad = dpad_up == other.dpad_up
&& dpad_right == other.dpad_right
&& dpad_down == other.dpad_down
&& dpad_left == other.dpad_left;
bool axis = axis_left_x == other.axis_left_x
&& axis_left_y == other.axis_left_y
&& axis_right_x == other.axis_right_x
&& axis_right_y == other.axis_right_y;
bool triggers = trigger_left == other.trigger_left
&& trigger_right == other.trigger_right;
return buttons && dpad && axis && triggers;
}
}
public class OutputControllerXbox360 {
private IXbox360Controller xbox_controller;
private OutputControllerXbox360InputState current_state;
public delegate void Xbox360FeedbackReceivedEventHandler(Xbox360FeedbackReceivedEventArgs e);
public event Xbox360FeedbackReceivedEventHandler FeedbackReceived;
public OutputControllerXbox360() {
xbox_controller = Program.emClient.CreateXbox360Controller();
Init();
}
public OutputControllerXbox360(ushort vendor_id, ushort product_id) {
xbox_controller = Program.emClient.CreateXbox360Controller(vendor_id, product_id);
Init();
}
private void Init() {
xbox_controller.FeedbackReceived += FeedbackReceivedRcv;
xbox_controller.AutoSubmitReport = false;
}
private void FeedbackReceivedRcv(object _sender, Xbox360FeedbackReceivedEventArgs e) {
if(FeedbackReceived!=null)
FeedbackReceived(e);
}
public bool UpdateInput(OutputControllerXbox360InputState new_state) {
if (current_state.IsEqual(new_state)) {
return false;
}
DoUpdateInput(new_state);
return true;
}
public void Connect() {
xbox_controller.Connect();
DoUpdateInput(new OutputControllerXbox360InputState());
}
public void Disconnect() {
xbox_controller.Disconnect();
}
private void DoUpdateInput(OutputControllerXbox360InputState new_state) {
xbox_controller.SetButtonState(Xbox360Button.LeftThumb, new_state.thumb_stick_left);
xbox_controller.SetButtonState(Xbox360Button.RightThumb, new_state.thumb_stick_right);
xbox_controller.SetButtonState(Xbox360Button.Y, new_state.y);
xbox_controller.SetButtonState(Xbox360Button.X, new_state.x);
xbox_controller.SetButtonState(Xbox360Button.B, new_state.b);
xbox_controller.SetButtonState(Xbox360Button.A, new_state.a);
xbox_controller.SetButtonState(Xbox360Button.Start, new_state.start);
xbox_controller.SetButtonState(Xbox360Button.Back, new_state.back);
xbox_controller.SetButtonState(Xbox360Button.Guide, new_state.guide);
xbox_controller.SetButtonState(Xbox360Button.Up, new_state.dpad_up);
xbox_controller.SetButtonState(Xbox360Button.Right, new_state.dpad_right);
xbox_controller.SetButtonState(Xbox360Button.Down, new_state.dpad_down);
xbox_controller.SetButtonState(Xbox360Button.Left, new_state.dpad_left);
xbox_controller.SetButtonState(Xbox360Button.LeftShoulder, new_state.shoulder_left);
xbox_controller.SetButtonState(Xbox360Button.RightShoulder, new_state.shoulder_right);
xbox_controller.SetAxisValue(Xbox360Axis.LeftThumbX, new_state.axis_left_x);
xbox_controller.SetAxisValue(Xbox360Axis.LeftThumbY, new_state.axis_left_y);
xbox_controller.SetAxisValue(Xbox360Axis.RightThumbX, new_state.axis_right_x);
xbox_controller.SetAxisValue(Xbox360Axis.RightThumbY, new_state.axis_right_y);
xbox_controller.SetSliderValue(Xbox360Slider.LeftTrigger, new_state.trigger_left);
xbox_controller.SetSliderValue(Xbox360Slider.RightTrigger, new_state.trigger_right);
xbox_controller.SubmitReport();
current_state = new_state;
}
}
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because it is too large Load diff

View file

@ -1,162 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// source: https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MadgwickAHRS.cs
namespace BetterJoyForCemu {
/// <summary>
/// MadgwickAHRS class. Implementation of Madgwick's IMU and AHRS algorithms.
/// </summary>
/// <remarks>
/// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms
/// </remarks>
public class MadgwickAHRS {
/// <summary>
/// Gets or sets the sample period.
/// </summary>
public float SamplePeriod { get; set; }
/// <summary>
/// Gets or sets the algorithm gain beta.
/// </summary>
public float Beta { get; set; }
/// <summary>
/// Gets or sets the Quaternion output.
/// </summary>
public float[] Quaternion { get; set; }
public float[] old_pitchYawRoll { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="MadgwickAHRS"/> class.
/// </summary>
/// <param name="samplePeriod">
/// Sample period.
/// </param>
public MadgwickAHRS(float samplePeriod)
: this(samplePeriod, 1f) {
}
/// <summary>
/// Initializes a new instance of the <see cref="MadgwickAHRS"/> class.
/// </summary>
/// <param name="samplePeriod">
/// Sample period.
/// </param>
/// <param name="beta">
/// Algorithm gain beta.
/// </param>
public MadgwickAHRS(float samplePeriod, float beta) {
SamplePeriod = samplePeriod;
Beta = beta;
Quaternion = new float[] { 1f, 0f, 0f, 0f };
old_pitchYawRoll = new float[] { 0f, 0f, 0f };
}
/// <summary>
/// Algorithm IMU update method. Requires only gyroscope and accelerometer data.
/// </summary>
/// <param name="gx">
/// Gyroscope x axis measurement in radians/s.
/// </param>
/// <param name="gy">
/// Gyroscope y axis measurement in radians/s.
/// </param>
/// <param name="gz">
/// Gyroscope z axis measurement in radians/s.
/// </param>
/// <param name="ax">
/// Accelerometer x axis measurement in any calibrated units.
/// </param>
/// <param name="ay">
/// Accelerometer y axis measurement in any calibrated units.
/// </param>
/// <param name="az">
/// Accelerometer z axis measurement in any calibrated units.
/// </param>
/// <remarks>
/// Optimised for minimal arithmetic.
/// Total ±: 45
/// Total *: 85
/// Total /: 3
/// Total sqrt: 3
/// </remarks>
public void Update(float gx, float gy, float gz, float ax, float ay, float az) {
float q1 = Quaternion[0], q2 = Quaternion[1], q3 = Quaternion[2], q4 = Quaternion[3]; // short name local variable for readability
float norm;
float s1, s2, s3, s4;
float qDot1, qDot2, qDot3, qDot4;
// Auxiliary variables to avoid repeated arithmetic
float _2q1 = 2f * q1;
float _2q2 = 2f * q2;
float _2q3 = 2f * q3;
float _2q4 = 2f * q4;
float _4q1 = 4f * q1;
float _4q2 = 4f * q2;
float _4q3 = 4f * q3;
float _8q2 = 8f * q2;
float _8q3 = 8f * q3;
float q1q1 = q1 * q1;
float q2q2 = q2 * q2;
float q3q3 = q3 * q3;
float q4q4 = q4 * q4;
// Normalise accelerometer measurement
norm = (float)Math.Sqrt(ax * ax + ay * ay + az * az);
if (norm == 0f) return; // handle NaN
norm = 1 / norm; // use reciprocal for division
ax *= norm;
ay *= norm;
az *= norm;
// Gradient decent algorithm corrective step
s1 = _4q1 * q3q3 + _2q3 * ax + _4q1 * q2q2 - _2q2 * ay;
s2 = _4q2 * q4q4 - _2q4 * ax + 4f * q1q1 * q2 - _2q1 * ay - _4q2 + _8q2 * q2q2 + _8q2 * q3q3 + _4q2 * az;
s3 = 4f * q1q1 * q3 + _2q1 * ax + _4q3 * q4q4 - _2q4 * ay - _4q3 + _8q3 * q2q2 + _8q3 * q3q3 + _4q3 * az;
s4 = 4f * q2q2 * q4 - _2q2 * ax + 4f * q3q3 * q4 - _2q3 * ay;
norm = 1f / (float)Math.Sqrt(s1 * s1 + s2 * s2 + s3 * s3 + s4 * s4); // normalise step magnitude
s1 *= norm;
s2 *= norm;
s3 *= norm;
s4 *= norm;
// Compute rate of change of quaternion
qDot1 = 0.5f * (-q2 * gx - q3 * gy - q4 * gz) - Beta * s1;
qDot2 = 0.5f * (q1 * gx + q3 * gz - q4 * gy) - Beta * s2;
qDot3 = 0.5f * (q1 * gy - q2 * gz + q4 * gx) - Beta * s3;
qDot4 = 0.5f * (q1 * gz + q2 * gy - q3 * gx) - Beta * s4;
// Integrate to yield quaternion
q1 += qDot1 * SamplePeriod;
q2 += qDot2 * SamplePeriod;
q3 += qDot3 * SamplePeriod;
q4 += qDot4 * SamplePeriod;
norm = 1f / (float)Math.Sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4); // normalise quaternion
Quaternion[0] = q1 * norm;
Quaternion[1] = q2 * norm;
Quaternion[2] = q3 * norm;
Quaternion[3] = q4 * norm;
}
public float[] GetEulerAngles() {
float[] pitchYawRoll = new float[3];
float q0 = Quaternion[0], q1 = Quaternion[1], q2 = Quaternion[2], q3 = Quaternion[3];
float sq1 = q1 * q1, sq2 = q2 * q2, sq3 = q3 * q3;
pitchYawRoll[0] = (float)Math.Asin(2f * (q0 * q2 - q3 * q1)); // Pitch
pitchYawRoll[1] = (float)Math.Atan2(2f * (q0 * q3 + q1 * q2), 1 - 2f * (sq2 + sq3)); // Yaw
pitchYawRoll[2] = (float)Math.Atan2(2f * (q0 * q1 + q2 * q3), 1 - 2f * (sq1 + sq2)); // Roll
float[] returnAngles = new float[6];
Array.Copy(pitchYawRoll, returnAngles, 3);
Array.Copy(old_pitchYawRoll, 0, returnAngles, 3, 3);
old_pitchYawRoll = pitchYawRoll;
return returnAngles;
}
}
}

View file

@ -26,6 +26,7 @@
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
this.console = new System.Windows.Forms.TextBox();
this.console_lbl = new System.Windows.Forms.Label();
this.notifyIcon = new System.Windows.Forms.NotifyIcon(this.components);
this.contextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@ -63,17 +64,26 @@
this.console.Multiline = true;
this.console.Name = "console";
this.console.ReadOnly = true;
this.console.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.console.Size = new System.Drawing.Size(262, 100);
this.console.TabIndex = 2;
//
// console_lbl
//
this.console_lbl.AutoSize = true;
this.console_lbl.Location = new System.Drawing.Point(9, 116);
this.console_lbl.Name = "console_lbl";
this.console_lbl.Size = new System.Drawing.Size(80, 13);
this.console_lbl.TabIndex = 1;
this.console_lbl.Text = "Console Output";
this.console_lbl.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// notifyIcon
//
this.notifyIcon.BalloonTipText = "Double click the tray icon to maximise!";
this.notifyIcon.BalloonTipTitle = "BetterJoy";
this.notifyIcon.BalloonTipText = "Double click the tray icon to maximise";
this.notifyIcon.BalloonTipTitle = "BetterJoyForCemu";
this.notifyIcon.ContextMenuStrip = this.contextMenu;
this.notifyIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon.Icon")));
this.notifyIcon.Text = "BetterJoy";
this.notifyIcon.Text = "BetterJoyForCemu";
this.notifyIcon.Visible = true;
this.notifyIcon.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.notifyIcon_MouseDoubleClick);
//
@ -99,7 +109,7 @@
this.version_lbl.Name = "version_lbl";
this.version_lbl.Size = new System.Drawing.Size(28, 13);
this.version_lbl.TabIndex = 2;
this.version_lbl.Text = "v7.1";
this.version_lbl.Text = "v6.0";
//
// passiveScanBox
//
@ -254,7 +264,7 @@
//
// btn_open3rdP
//
this.btn_open3rdP.Location = new System.Drawing.Point(93, 112);
this.btn_open3rdP.Location = new System.Drawing.Point(188, 112);
this.btn_open3rdP.Name = "btn_open3rdP";
this.btn_open3rdP.Size = new System.Drawing.Size(86, 20);
this.btn_open3rdP.TabIndex = 7;
@ -269,7 +279,7 @@
this.groupBox1.Margin = new System.Windows.Forms.Padding(2);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Padding = new System.Windows.Forms.Padding(2);
this.groupBox1.Size = new System.Drawing.Size(304, 242);
this.groupBox1.Size = new System.Drawing.Size(227, 242);
this.groupBox1.TabIndex = 9;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Config";
@ -287,7 +297,7 @@
this.settingsTable.Name = "settingsTable";
this.settingsTable.RowCount = 1;
this.settingsTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.settingsTable.Size = new System.Drawing.Size(300, 219);
this.settingsTable.Size = new System.Drawing.Size(219, 219);
this.settingsTable.TabIndex = 1;
//
// rightPanel
@ -297,7 +307,7 @@
this.rightPanel.Location = new System.Drawing.Point(289, 1);
this.rightPanel.Margin = new System.Windows.Forms.Padding(2, 2, 12, 2);
this.rightPanel.Name = "rightPanel";
this.rightPanel.Size = new System.Drawing.Size(312, 273);
this.rightPanel.Size = new System.Drawing.Size(231, 273);
this.rightPanel.TabIndex = 11;
this.rightPanel.Visible = false;
//
@ -305,7 +315,7 @@
//
this.settingsApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.settingsApply.Font = new System.Drawing.Font("Microsoft Sans Serif", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.settingsApply.Location = new System.Drawing.Point(246, 252);
this.settingsApply.Location = new System.Drawing.Point(165, 252);
this.settingsApply.Margin = new System.Windows.Forms.Padding(2);
this.settingsApply.Name = "settingsApply";
this.settingsApply.Size = new System.Drawing.Size(61, 21);
@ -326,7 +336,7 @@
//
// btn_reassign_open
//
this.btn_reassign_open.Location = new System.Drawing.Point(12, 112);
this.btn_reassign_open.Location = new System.Drawing.Point(107, 112);
this.btn_reassign_open.Name = "btn_reassign_open";
this.btn_reassign_open.Size = new System.Drawing.Size(75, 20);
this.btn_reassign_open.TabIndex = 13;
@ -340,7 +350,7 @@
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true;
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.ClientSize = new System.Drawing.Size(615, 308);
this.ClientSize = new System.Drawing.Size(284, 261);
this.Controls.Add(this.btn_reassign_open);
this.Controls.Add(this.foldLbl);
this.Controls.Add(this.rightPanel);
@ -351,6 +361,7 @@
this.Controls.Add(this.donationLink);
this.Controls.Add(this.passiveScanBox);
this.Controls.Add(this.version_lbl);
this.Controls.Add(this.console_lbl);
this.Controls.Add(this.console);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
@ -372,7 +383,8 @@
#endregion
public System.Windows.Forms.TextBox console;
public System.Windows.Forms.NotifyIcon notifyIcon;
private System.Windows.Forms.Label console_lbl;
private System.Windows.Forms.NotifyIcon notifyIcon;
private System.Windows.Forms.Label version_lbl;
private System.Windows.Forms.ContextMenuStrip contextMenu;
private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;

View file

@ -15,22 +15,13 @@ using System.Xml.Linq;
namespace BetterJoyForCemu {
public partial class MainForm : Form {
public bool allowCalibration = Boolean.Parse(ConfigurationManager.AppSettings["AllowCalibration"]);
public bool nonOriginal = Boolean.Parse(ConfigurationManager.AppSettings["NonOriginalController"]);
public List<Button> con, loc;
public bool calibrate;
public List<KeyValuePair<string, float[]>> caliData;
private Timer countDown;
private int count;
public List<int> xG, yG, zG, xA, yA, zA;
public bool shakeInputEnabled = Boolean.Parse(ConfigurationManager.AppSettings["EnableShakeInput"]);
public float shakeSesitivity = float.Parse(ConfigurationManager.AppSettings["ShakeInputSensitivity"]);
public float shakeDelay = float.Parse(ConfigurationManager.AppSettings["ShakeInputDelay"]);
public enum NonOriginalController : int {
Disabled = 0,
DefaultCalibration = 1,
ControllerCalibration = 2,
}
public MainForm() {
xG = new List<int>(); yG = new List<int>(); zG = new List<int>();
@ -41,15 +32,18 @@ namespace BetterJoyForCemu {
InitializeComponent();
if (!allowCalibration)
if (!nonOriginal)
AutoCalibrate.Hide();
// Feature not yet implemented - hide
btn_open3rdP.Hide();
con = new List<Button> { con1, con2, con3, con4 };
loc = new List<Button> { loc1, loc2, loc3, loc4 };
//list all options
string[] myConfigs = ConfigurationManager.AppSettings.AllKeys;
Size childSize = new Size(150, 20);
Size childSize = new Size(87, 20);
for (int i = 0; i != myConfigs.Length; i++) {
settingsTable.RowCount++;
settingsTable.Controls.Add(new Label() { Text = myConfigs[i], TextAlign = ContentAlignment.BottomLeft, AutoEllipsis = true, Size = childSize }, 0, i);
@ -70,8 +64,7 @@ namespace BetterJoyForCemu {
private void HideToTray() {
this.WindowState = FormWindowState.Minimized;
notifyIcon.Visible = true;
notifyIcon.BalloonTipText = "Double click the tray icon to maximise!";
notifyIcon.ShowBalloonTip(0);
notifyIcon.ShowBalloonTip(1);
this.ShowInTaskbar = false;
this.Hide();
}
@ -145,9 +138,8 @@ namespace BetterJoyForCemu {
bool toRumble = Boolean.Parse(ConfigurationManager.AppSettings["EnableRumble"]);
bool showAsXInput = Boolean.Parse(ConfigurationManager.AppSettings["ShowAsXInput"]);
bool showAsDS4 = Boolean.Parse(ConfigurationManager.AppSettings["ShowAsDS4"]);
public async void locBtnClickAsync(object sender, EventArgs e) {
public void locBtnClick(object sender, EventArgs e) {
Button bb = sender as Button;
if (bb.Tag.GetType() == typeof(Button)) {
@ -155,15 +147,11 @@ namespace BetterJoyForCemu {
if (button.Tag.GetType() == typeof(Joycon)) {
Joycon v = (Joycon)button.Tag;
v.SetRumble(160.0f, 320.0f, 1.0f);
await Task.Delay(300);
v.SetRumble(160.0f, 320.0f, 0);
v.SetRumble(20.0f, 400.0f, 1.0f, 300);
}
}
}
bool doNotRejoin = Boolean.Parse(ConfigurationManager.AppSettings["DoNotRejoinJoycons"]);
public void conBtnClick(object sender, EventArgs e) {
Button button = sender as Button;
@ -173,7 +161,7 @@ namespace BetterJoyForCemu {
if (v.other == null && !v.isPro) { // needs connecting to other joycon (so messy omg)
bool succ = false;
if (Program.mgr.j.Count == 1 || doNotRejoin) { // when want to have a single joycon in vertical mode
if (Program.mgr.j.Count == 1) { // when want to have a single joycon in vertical mode
v.other = v; // hacky; implement check in Joycon.cs to account for this
succ = true;
} else {
@ -182,15 +170,15 @@ namespace BetterJoyForCemu {
v.other = jc;
jc.other = v;
if (v.out_xbox != null) {
v.out_xbox.Disconnect();
v.out_xbox = null;
}
//Set both Joycon LEDs to the one with the lowest ID
byte led = jc.LED <= v.LED ? jc.LED : v.LED;
jc.LED = led;
v.LED = led;
jc.SetPlayerLED(led);
v.SetPlayerLED(led);
if (v.out_ds4 != null) {
v.out_ds4.Disconnect();
v.out_ds4 = null;
}
v.xin.Disconnect();
v.xin = null;
// setting the other joycon's button image
foreach (Button b in con)
@ -208,8 +196,15 @@ namespace BetterJoyForCemu {
if (b.Tag == v)
b.BackgroundImage = v.isLeft ? Properties.Resources.jc_left : Properties.Resources.jc_right;
} else if (v.other != null && !v.isPro) { // needs disconnecting from other joycon
ReenableViGEm(v);
ReenableViGEm(v.other);
if (v.xin == null) {
ReenableXinput(v);
v.xin.Connect();
}
if (v.other.xin == null) {
ReenableXinput(v.other);
v.other.xin.Connect();
}
button.BackgroundImage = v.isLeft ? Properties.Resources.jc_left_s : Properties.Resources.jc_right_s;
@ -217,6 +212,12 @@ namespace BetterJoyForCemu {
if (b.Tag == v.other)
b.BackgroundImage = v.other.isLeft ? Properties.Resources.jc_left_s : Properties.Resources.jc_right_s;
//Set original Joycon LEDs
v.other.LED = (byte)(0x1 << v.other.PadId);
v.LED = (byte)(0x1 << v.PadId);
v.other.SetPlayerLED(v.other.LED);
v.SetPlayerLED(v.LED);
v.other.other = null;
v.other = null;
}
@ -254,26 +255,16 @@ namespace BetterJoyForCemu {
AppendTextBox("Error writing app settings.\r\n");
}
ConfigurationManager.AppSettings["AutoPowerOff"] = "false"; // Prevent joycons poweroff when applying settings
Application.Restart();
Environment.Exit(0);
}
void ReenableViGEm(Joycon v) {
if (showAsXInput && v.out_xbox == null) {
v.out_xbox = new Controller.OutputControllerXbox360();
void ReenableXinput(Joycon v) {
if (showAsXInput) {
v.xin = Program.emClient.CreateXbox360Controller();
if (toRumble)
v.out_xbox.FeedbackReceived += v.ReceiveRumble;
v.out_xbox.Connect();
}
if (showAsDS4 && v.out_ds4 == null) {
v.out_ds4 = new Controller.OutputControllerDualShock4();
if (toRumble)
v.out_ds4.FeedbackReceived += v.Ds4_FeedbackReceived;
v.out_ds4.Connect();
v.xin.FeedbackReceived += v.ReceiveRumble;
}
}

View file

@ -1,24 +1,24 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using System.Timers;
using System.Windows.Forms;
using BetterJoyForCemu.Collections;
using Nefarius.ViGEm.Client;
using static BetterJoyForCemu._3rdPartyControllers;
using System.Net.NetworkInformation;
using System.Diagnostics;
using static BetterJoyForCemu.HIDapi;
using Nefarius.ViGEm.Client;
using System.Net;
using System.Configuration;
using System.Net.Http;
using System.IO;
using System.Windows.Forms;
using System.ServiceProcess;
namespace BetterJoyForCemu {
public class JoyconManager {
@ -26,13 +26,13 @@ namespace BetterJoyForCemu {
public bool EnableLocalize = false;
private const ushort vendor_id = 0x57e;
private const ushort vendor_id_ = 0x057e;
private const ushort product_l = 0x2006;
private const ushort product_r = 0x2007;
private const ushort product_pro = 0x2009;
private const ushort product_snes = 0x2017;
private const ushort product_n64 = 0x2019;
public ConcurrentList<Joycon> j { get; private set; } // Array of all connected Joy-Cons
public List<Joycon> j; // Array of all connected Joy-Cons
static JoyconManager instance;
public MainForm form;
@ -45,7 +45,7 @@ namespace BetterJoyForCemu {
public void Awake() {
instance = this;
j = new ConcurrentList<Joycon>();
j = new List<Joycon>();
HIDapi.hid_init();
}
@ -64,16 +64,16 @@ namespace BetterJoyForCemu {
void CleanUp() { // removes dropped controllers from list
List<Joycon> rem = new List<Joycon>();
foreach (Joycon joycon in j) {
if (joycon.state == Joycon.state_.DROPPED) {
if (joycon.other != null)
joycon.other.other = null; // The other of the other is the joycon itself
for (int i = 0; i < j.Count; i++) {
Joycon v = j[i];
if (v.state == Joycon.state_.DROPPED) {
if (v.other != null)
v.other.other = null; // The other of the other is the joycon itself
joycon.Detach(true);
rem.Add(joycon);
v.Detach(); rem.Add(v);
foreach (Button b in form.con) {
if (b.Enabled & b.Tag == joycon) {
if (b.Enabled & b.Tag == v) {
b.Invoke(new MethodInvoker(delegate {
b.BackColor = System.Drawing.Color.FromArgb(0x00, System.Drawing.SystemColors.Control);
b.Enabled = false;
@ -83,7 +83,7 @@ namespace BetterJoyForCemu {
}
}
form.AppendTextBox("Removed dropped controller. Can be reconnected.\r\n");
form.AppendTextBox("Removed dropped controller to list. Can be reconnected.\r\n");
}
}
@ -92,34 +92,22 @@ namespace BetterJoyForCemu {
}
void CheckForNewControllersTime(Object source, ElapsedEventArgs e) {
CleanUp();
if (Config.IntValue("ProgressiveScan") == 1) {
CheckForNewControllers();
}
}
private ushort TypeToProdId(byte type) {
switch (type) {
case 1:
return product_pro;
case 2:
return product_l;
case 3:
return product_r;
}
return 0;
}
public void CheckForNewControllers() {
CleanUp();
// move all code for initializing devices here and well as the initial code from Start()
bool isLeft = false;
IntPtr ptr = HIDapi.hid_enumerate(0x0, 0x0);
IntPtr ptr = HIDapi.hid_enumerate(vendor_id, 0x0);
IntPtr top_ptr = ptr;
hid_device_info enumerate; // Add device to list
bool foundNew = false;
while (ptr != IntPtr.Zero) {
SController thirdParty = null;
enumerate = (hid_device_info)Marshal.PtrToStructure(ptr, typeof(hid_device_info));
if (enumerate.serial_number == null) {
@ -128,25 +116,14 @@ namespace BetterJoyForCemu {
continue;
}
if (form.nonOriginal) {
enumerate.product_id = product_pro;
}
bool validController = (enumerate.product_id == product_l || enumerate.product_id == product_r ||
enumerate.product_id == product_pro || enumerate.product_id == product_snes || enumerate.product_id == product_n64) && enumerate.vendor_id == vendor_id;
// check list of custom controllers specified
foreach (SController v in Program.thirdPartyCons) {
if (enumerate.vendor_id == v.vendor_id && enumerate.product_id == v.product_id && enumerate.serial_number == v.serial_number) {
validController = true;
thirdParty = v;
break;
}
}
ushort prod_id = thirdParty == null ? enumerate.product_id : TypeToProdId(thirdParty.type);
if (prod_id == 0) {
ptr = enumerate.next; // controller was not assigned a type, but advance ptr anyway
continue;
}
enumerate.product_id == product_pro || enumerate.product_id == product_snes);
if (validController && !ControllerAlreadyAdded(enumerate.path)) {
switch (prod_id) {
switch (enumerate.product_id) {
case product_l:
isLeft = true;
form.AppendTextBox("Left Joy-Con connected.\r\n"); break;
@ -159,9 +136,6 @@ namespace BetterJoyForCemu {
case product_snes:
isLeft = true;
form.AppendTextBox("SNES controller connected.\r\n"); break;
case product_n64:
isLeft = true;
form.AppendTextBox("N64 controller connected.\r\n"); break;
default:
form.AppendTextBox("Non Joy-Con Nintendo input device skipped.\r\n"); break;
}
@ -196,10 +170,9 @@ namespace BetterJoyForCemu {
break;
}
bool isPro = prod_id == product_pro;
bool isSnes = prod_id == product_snes;
bool is64 = prod_id == product_n64;
j.Add(new Joycon(handle, EnableIMU, EnableLocalize & EnableIMU, 0.05f, isLeft, enumerate.path, enumerate.serial_number, j.Count, isPro, isSnes, is64,thirdParty != null));
bool isPro = enumerate.product_id == product_pro;
bool isSnes = enumerate.product_id == product_snes;
j.Add(new Joycon(handle, EnableIMU, EnableLocalize & EnableIMU, 0.05f, isLeft, enumerate.path, enumerate.serial_number, j.Count, isPro, isSnes));
foundNew = true;
j.Last().form = form;
@ -210,7 +183,7 @@ namespace BetterJoyForCemu {
ii++;
if (!v.Enabled) {
System.Drawing.Bitmap temp;
switch (prod_id) {
switch (enumerate.product_id) {
case (product_l):
temp = Properties.Resources.jc_left_s; break;
case (product_r):
@ -219,8 +192,6 @@ namespace BetterJoyForCemu {
temp = Properties.Resources.pro; break;
case (product_snes):
temp = Properties.Resources.snes; break;
case (product_n64):
temp = Properties.Resources.ultra; break;
default:
temp = Properties.Resources.cross; break;
}
@ -234,7 +205,7 @@ namespace BetterJoyForCemu {
form.loc[ii].Invoke(new MethodInvoker(delegate {
form.loc[ii].Tag = v;
form.loc[ii].Click += new EventHandler(form.locBtnClickAsync);
form.loc[ii].Click += new EventHandler(form.locBtnClick);
}));
break;
@ -243,12 +214,8 @@ namespace BetterJoyForCemu {
}
byte[] mac = new byte[6];
try {
for (int n = 0; n < 6; n++)
mac[n] = byte.Parse(enumerate.serial_number.Substring(n * 2, 2), System.Globalization.NumberStyles.HexNumber);
} catch (Exception e) {
// could not parse mac address
}
j[j.Count - 1].PadMacAddress = new PhysicalAddress(mac);
}
@ -258,37 +225,36 @@ namespace BetterJoyForCemu {
if (foundNew) { // attempt to auto join-up joycons on connection
Joycon temp = null;
foreach (Joycon v in j) {
// Do not attach two controllers if they are either:
// - Not a Joycon
// - Already attached to another Joycon (that isn't itself)
if (v.isPro || (v.other != null && v.other != v)) {
continue;
}
// Otherwise, iterate through and find the Joycon with the lowest
// id that has not been attached already (Does not include self)
if (!v.isPro) {
if (temp == null)
temp = v;
else if (temp.isLeft != v.isLeft && v.other == null) {
temp.other = v;
v.other = temp;
if (temp.out_xbox != null) {
//Set both Joycon LEDs to the one with the lowest ID
byte led = temp.LED <= v.LED ? temp.LED : v.LED;
temp.LED = led;
v.LED = led;
temp.SetPlayerLED(led);
v.SetPlayerLED(led);
if (temp.xin != null) {
try {
temp.out_xbox.Disconnect();
temp.xin.Disconnect();
} catch (Exception e) {
// it wasn't connected in the first place, go figure
}
}
if (temp.out_ds4 != null) {
if (temp.ds4 != null) {
try {
temp.out_ds4.Disconnect();
temp.ds4.Disconnect();
} catch (Exception e) {
// it wasn't connected in the first place, go figure
}
}
temp.out_xbox = null;
temp.out_ds4 = null;
temp.xin = null;
temp.ds4 = null;
foreach (Button b in form.con)
if (b.Tag == v || b.Tag == temp) {
@ -300,47 +266,48 @@ namespace BetterJoyForCemu {
}
}
}
}
HIDapi.hid_free_enumeration(top_ptr);
bool on = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).AppSettings.Settings["HomeLEDOn"].Value.ToLower() == "true";
foreach (Joycon jc in j) { // Connect device straight away
if (jc.state == Joycon.state_.NOT_ATTACHED) {
if (jc.out_xbox != null)
jc.out_xbox.Connect();
if (jc.out_ds4 != null)
jc.out_ds4.Connect();
if (jc.xin != null)
jc.xin.Connect();
if (jc.ds4 != null)
jc.ds4.Connect();
try {
jc.Attach();
} catch (Exception e) {
jc.state = Joycon.state_.DROPPED;
continue;
jc.Attach(leds_: jc.LED);
bool on = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).AppSettings.Settings["HomeLEDOn"].Value.ToLower() == "true";
foreach (Joycon j in Program.mgr.j) {
j.SetHomeLight(on);
}
jc.SetHomeLight(on);
jc.Begin();
if (form.allowCalibration) {
if (form.nonOriginal) {
jc.getActiveData();
}
}
}
}
public void Update() {
for (int i = 0; i < j.Count; ++i)
j[i].Update();
}
public void OnApplicationQuit() {
foreach (Joycon v in j) {
if (Boolean.Parse(ConfigurationManager.AppSettings["AutoPowerOff"]))
v.PowerOff();
v.Detach();
if (v.out_xbox != null) {
v.out_xbox.Disconnect();
if (v.xin != null) {
v.xin.Disconnect();
}
if (v.out_ds4 != null) {
v.out_ds4.Disconnect();
if (v.ds4 != null) {
v.ds4.Disconnect();
}
}
@ -349,23 +316,62 @@ namespace BetterJoyForCemu {
}
}
// Custom timer class because system timers have a limit of 15.6ms
class HighResTimer {
double interval = 0;
double frequency = 0;
Thread thread;
public delegate void ActionDelegate();
ActionDelegate func;
bool run = false;
public HighResTimer(double f, ActionDelegate a) {
frequency = f;
interval = 1.0 / f;
func = a;
}
public void Start() {
run = true;
thread = new Thread(new ThreadStart(Run));
thread.IsBackground = true;
thread.Start();
}
void Run() {
while (run) {
func();
int timeToSleep = (int)(interval * 1000);
Thread.Sleep(timeToSleep);
}
}
public void Stop() {
run = false;
}
}
class Program {
public static PhysicalAddress btMAC = new PhysicalAddress(new byte[] { 0, 0, 0, 0, 0, 0 });
public static UdpServer server;
static double pollsPerSecond = 120.0;
public static ViGEmClient emClient;
private static readonly HttpClient client = new HttpClient();
public static JoyconManager mgr;
static HighResTimer timer;
static string pid;
static MainForm form;
static public bool useHIDG = Boolean.Parse(ConfigurationManager.AppSettings["UseHIDG"]);
public static List<SController> thirdPartyCons = new List<SController>();
private static WindowsInput.Events.Sources.IKeyboardEventSource keyboard;
private static WindowsInput.Events.Sources.IMouseEventSource mouse;
@ -426,10 +432,6 @@ namespace BetterJoyForCemu {
}
}
// a bit hacky
_3rdPartyControllers partyForm = new _3rdPartyControllers();
partyForm.CopyCustomControllers();
mgr = new JoyconManager();
mgr.form = form;
mgr.Awake();
@ -440,6 +442,8 @@ namespace BetterJoyForCemu {
server.form = form;
server.Start(IPAddress.Parse(ConfigurationManager.AppSettings["IP"]), Int32.Parse(ConfigurationManager.AppSettings["Port"]));
timer = new HighResTimer(pollsPerSecond, new HighResTimer.ActionDelegate(mgr.Update));
timer.Start();
// Capture keyboard + mouse events for binding's sake
keyboard = WindowsInput.Capture.Global.KeyboardAsync();
@ -513,43 +517,15 @@ namespace BetterJoyForCemu {
keyboard.Dispose(); mouse.Dispose();
server.Stop();
timer.Stop();
mgr.OnApplicationQuit();
}
private static string appGuid = "1bf709e9-c133-41df-933a-c9ff3f664c7b"; // randomly-generated
static void Main(string[] args) {
// Setting the culturesettings so float gets parsed correctly
CultureInfo.CurrentCulture = new CultureInfo("en-US", false);
// Set the correct DLL for the current OS
SetupDlls();
using (Mutex mutex = new Mutex(false, "Global\\" + appGuid)) {
if (!mutex.WaitOne(0, false)) {
MessageBox.Show("Instance already running.", "BetterJoy");
return;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
form = new MainForm();
Application.Run(form);
}
}
static void SetupDlls() {
string archPath = $"{AppDomain.CurrentDomain.BaseDirectory}{(Environment.Is64BitProcess ? "x64" : "x86")}\\";
string pathVariable = Environment.GetEnvironmentVariable("PATH");
pathVariable = $"{archPath};{pathVariable}";
Environment.SetEnvironmentVariable("PATH", pathVariable);
}
// Helper funtions to set the hidapi dll location acording to the system instruction set.
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetDefaultDllDirectories(int directoryFlags);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern void AddDllDirectory(string lpPathName);
}
}

View file

@ -5,12 +5,12 @@ using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("BetterJoy")]
[assembly: AssemblyTitle("BetterJoyForCemu")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BetterJoy")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyProduct("BetterJoyForCemu")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

View file

@ -139,15 +139,5 @@ namespace BetterJoyForCemu.Properties {
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap ultra {
get {
object obj = ResourceManager.GetObject("ultra", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
}
}

View file

@ -121,9 +121,6 @@
<data name="snes" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\snes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="ultra" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\ultra.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="pro" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\pro.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>

View file

@ -93,15 +93,13 @@ namespace BetterJoyForCemu {
this.btn_sl_r = new BetterJoyForCemu.SplitButton();
this.lbl_sr_r = new System.Windows.Forms.Label();
this.btn_sr_r = new BetterJoyForCemu.SplitButton();
this.btn_close = new System.Windows.Forms.Button();
this.btn_apply = new System.Windows.Forms.Button();
this.btn_close = new Button();
this.btn_apply = new Button();
this.tip_reassign = new System.Windows.Forms.ToolTip(this.components);
this.lbl_reset_mouse = new System.Windows.Forms.Label();
this.btn_reset_mouse = new BetterJoyForCemu.SplitButton();
this.lbl_activate_gyro = new System.Windows.Forms.Label();
this.btn_active_gyro = new BetterJoyForCemu.SplitButton();
this.lbl_shake = new System.Windows.Forms.Label();
this.btn_shake = new BetterJoyForCemu.SplitButton();
this.btn_active_gyro = new SplitButton();
this.SuspendLayout();
//
// btn_capture
@ -214,7 +212,7 @@ namespace BetterJoyForCemu {
//
// btn_close
//
this.btn_close.Location = new System.Drawing.Point(15, 289);
this.btn_close.Location = new System.Drawing.Point(15, 257);
this.btn_close.Name = "btn_close";
this.btn_close.Size = new System.Drawing.Size(75, 23);
this.btn_close.TabIndex = 13;
@ -224,7 +222,7 @@ namespace BetterJoyForCemu {
//
// btn_apply
//
this.btn_apply.Location = new System.Drawing.Point(105, 289);
this.btn_apply.Location = new System.Drawing.Point(105, 257);
this.btn_apply.Name = "btn_apply";
this.btn_apply.Size = new System.Drawing.Size(75, 23);
this.btn_apply.TabIndex = 14;
@ -235,7 +233,7 @@ namespace BetterJoyForCemu {
// lbl_reset_mouse
//
this.lbl_reset_mouse.AutoSize = true;
this.lbl_reset_mouse.Location = new System.Drawing.Point(15, 223);
this.lbl_reset_mouse.Location = new System.Drawing.Point(15, 191);
this.lbl_reset_mouse.Name = "lbl_reset_mouse";
this.lbl_reset_mouse.Size = new System.Drawing.Size(80, 13);
this.lbl_reset_mouse.TabIndex = 16;
@ -244,7 +242,7 @@ namespace BetterJoyForCemu {
//
// btn_reset_mouse
//
this.btn_reset_mouse.Location = new System.Drawing.Point(105, 218);
this.btn_reset_mouse.Location = new System.Drawing.Point(105, 186);
this.btn_reset_mouse.Name = "btn_reset_mouse";
this.btn_reset_mouse.Size = new System.Drawing.Size(75, 23);
this.btn_reset_mouse.TabIndex = 15;
@ -253,7 +251,7 @@ namespace BetterJoyForCemu {
// lbl_activate_gyro
//
this.lbl_activate_gyro.AutoSize = true;
this.lbl_activate_gyro.Location = new System.Drawing.Point(14, 252);
this.lbl_activate_gyro.Location = new System.Drawing.Point(14, 220);
this.lbl_activate_gyro.Name = "lbl_activate_gyro";
this.lbl_activate_gyro.Size = new System.Drawing.Size(71, 13);
this.lbl_activate_gyro.TabIndex = 17;
@ -262,37 +260,17 @@ namespace BetterJoyForCemu {
//
// btn_active_gyro
//
this.btn_active_gyro.Location = new System.Drawing.Point(105, 247);
this.btn_active_gyro.Location = new System.Drawing.Point(105, 215);
this.btn_active_gyro.Name = "btn_active_gyro";
this.btn_active_gyro.Size = new System.Drawing.Size(75, 23);
this.btn_active_gyro.TabIndex = 18;
this.btn_active_gyro.UseVisualStyleBackColor = true;
//
// label1
//
this.lbl_shake.AutoSize = true;
this.lbl_shake.Location = new System.Drawing.Point(15, 191);
this.lbl_shake.Name = "lbl_shake";
this.lbl_shake.Size = new System.Drawing.Size(87, 13);
this.lbl_shake.TabIndex = 20;
this.lbl_shake.Text = "Shake Input";
this.lbl_shake.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// splitButton1
//
this.btn_shake.Location = new System.Drawing.Point(105, 186);
this.btn_shake.Name = "btn_shake";
this.btn_shake.Size = new System.Drawing.Size(75, 23);
this.btn_shake.TabIndex = 19;
this.btn_shake.UseVisualStyleBackColor = true;
//
// Reassign
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(192, 338);
this.Controls.Add(this.lbl_shake);
this.Controls.Add(this.btn_shake);
this.ClientSize = new System.Drawing.Size(192, 292);
this.Controls.Add(this.btn_active_gyro);
this.Controls.Add(this.lbl_activate_gyro);
this.Controls.Add(this.lbl_reset_mouse);
@ -345,7 +323,5 @@ namespace BetterJoyForCemu {
private SplitButton btn_reset_mouse;
private Label lbl_activate_gyro;
private SplitButton btn_active_gyro;
private Label lbl_shake;
private SplitButton btn_shake;
}
}

View file

@ -29,7 +29,7 @@ namespace BetterJoyForCemu {
menu_joy_buttons.ItemClicked += Menu_joy_buttons_ItemClicked;
foreach (SplitButton c in new SplitButton[] { btn_capture, btn_home, btn_sl_l, btn_sl_r, btn_sr_l, btn_sr_r, btn_shake, btn_reset_mouse, btn_active_gyro }) {
foreach (SplitButton c in new SplitButton[] { btn_capture, btn_home, btn_sl_l, btn_sl_r, btn_sr_l, btn_sr_r, btn_reset_mouse , btn_active_gyro}) {
c.Tag = c.Name.Substring(4);
GetPrettyName(c);

View file

@ -1,10 +1,14 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Force.Crc32;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using Force.Crc32;
using System.Configuration;
namespace BetterJoyForCemu {
class UdpServer {
@ -13,11 +17,11 @@ namespace BetterJoyForCemu {
private bool running;
private byte[] recvBuffer = new byte[1024];
IList<Joycon> controllers;
List<Joycon> controllers;
public MainForm form;
public UdpServer(IList<Joycon> p) {
public UdpServer(List<Joycon> p) {
controllers = p;
}
@ -317,57 +321,62 @@ namespace BetterJoyForCemu {
}
}
bool swapAB = Boolean.Parse(ConfigurationManager.AppSettings["SwapAB"]);
bool swapXY = Boolean.Parse(ConfigurationManager.AppSettings["SwapXY"]);
private bool ReportToBuffer(Joycon hidReport, byte[] outputData, ref int outIdx) {
var ds4 = Joycon.MapToDualShock4Input(hidReport);
outputData[outIdx] = 0;
if (ds4.dPad == Controller.DpadDirection.West || ds4.dPad == Controller.DpadDirection.Northwest || ds4.dPad == Controller.DpadDirection.Southwest) outputData[outIdx] |= 0x80;
if (ds4.dPad == Controller.DpadDirection.South || ds4.dPad == Controller.DpadDirection.Southwest || ds4.dPad == Controller.DpadDirection.Southeast) outputData[outIdx] |= 0x40;
if (ds4.dPad == Controller.DpadDirection.East || ds4.dPad == Controller.DpadDirection.Northeast || ds4.dPad == Controller.DpadDirection.Southeast) outputData[outIdx] |= 0x20;
if (ds4.dPad == Controller.DpadDirection.North || ds4.dPad == Controller.DpadDirection.Northwest || ds4.dPad == Controller.DpadDirection.Northeast) outputData[outIdx] |= 0x10;
bool isLeft = hidReport.isLeft;
if (ds4.options) outputData[outIdx] |= 0x08;
if (ds4.thumb_right) outputData[outIdx] |= 0x04;
if (ds4.thumb_left) outputData[outIdx] |= 0x02;
if (ds4.share) outputData[outIdx] |= 0x01;
if (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_LEFT : Joycon.Button.Y)) outputData[outIdx] |= 0x80;
if (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_DOWN : Joycon.Button.B)) outputData[outIdx] |= 0x40;
if (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_RIGHT : Joycon.Button.A)) outputData[outIdx] |= 0x20;
if (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_UP : Joycon.Button.X)) outputData[outIdx] |= 0x10;
if (hidReport.GetButton(Joycon.Button.PLUS)) outputData[outIdx] |= 0x08;
if (hidReport.GetButton(isLeft ? Joycon.Button.STICK2 : Joycon.Button.STICK)) outputData[outIdx] |= 0x04;
if (hidReport.GetButton(isLeft ? Joycon.Button.STICK : Joycon.Button.STICK2)) outputData[outIdx] |= 0x02;
if (hidReport.GetButton(Joycon.Button.MINUS)) outputData[outIdx] |= 0x01;
outputData[++outIdx] = 0;
if (ds4.square) outputData[outIdx] |= 0x80;
if (ds4.cross) outputData[outIdx] |= 0x40;
if (ds4.circle) outputData[outIdx] |= 0x20;
if (ds4.triangle) outputData[outIdx] |= 0x10;
if (hidReport.GetButton(!swapXY ? (isLeft ? Joycon.Button.Y : Joycon.Button.DPAD_LEFT) : (isLeft ? Joycon.Button.X : Joycon.Button.DPAD_UP))) outputData[outIdx] |= 0x80;
if (hidReport.GetButton(!swapAB ? (isLeft ? Joycon.Button.B : Joycon.Button.DPAD_DOWN) : (isLeft ? Joycon.Button.A : Joycon.Button.DPAD_RIGHT))) outputData[outIdx] |= 0x40;
if (hidReport.GetButton(!swapAB ? (isLeft ? Joycon.Button.A : Joycon.Button.DPAD_RIGHT) : (isLeft ? Joycon.Button.B : Joycon.Button.DPAD_DOWN))) outputData[outIdx] |= 0x20;
if (hidReport.GetButton(!swapXY ? (isLeft ? Joycon.Button.X : Joycon.Button.DPAD_UP) : (isLeft ? Joycon.Button.Y : Joycon.Button.DPAD_LEFT))) outputData[outIdx] |= 0x10;
if (ds4.shoulder_right) outputData[outIdx] |= 0x08;
if (ds4.shoulder_left) outputData[outIdx] |= 0x04;
if (ds4.trigger_right_value == Byte.MaxValue) outputData[outIdx] |= 0x02;
if (ds4.trigger_left_value == Byte.MaxValue) outputData[outIdx] |= 0x01;
if (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER2_1 : Joycon.Button.SHOULDER_1)) outputData[outIdx] |= 0x08;
if (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER_1 : Joycon.Button.SHOULDER2_1)) outputData[outIdx] |= 0x04;
if (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER2_2 : Joycon.Button.SHOULDER_2)) outputData[outIdx] |= 0x02;
if (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER_2 : Joycon.Button.SHOULDER2_2)) outputData[outIdx] |= 0x01;
outputData[++outIdx] = ds4.ps ? (byte)1 : (byte)0;
outputData[++outIdx] = ds4.touchpad ? (byte)1 : (byte)0;
outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.HOME)) ? (byte)1 : (byte)0;
outputData[++outIdx] = 0; // no touch pad
outputData[++outIdx] = ds4.thumb_left_x;
outputData[++outIdx] = ds4.thumb_left_y;
outputData[++outIdx] = ds4.thumb_right_x;
outputData[++outIdx] = ds4.thumb_right_y;
float[] leftStick = hidReport.GetStick(); // 127 is 0
outputData[++outIdx] = (byte)(Math.Max(0, Math.Min(255, 127 + leftStick[0] * 127)));
outputData[++outIdx] = (byte)(Math.Max(0, Math.Min(255, 127 + leftStick[1] * 127)));
float[] rightStick = hidReport.GetStick2(); // 127 is 0
outputData[++outIdx] = (byte)(Math.Max(0, Math.Min(255, 127 + rightStick[0] * 127)));
outputData[++outIdx] = (byte)(Math.Max(0, Math.Min(255, 127 + rightStick[1] * 127)));
//we don't have analog buttons so just use the Button enums (which give either 0 or 0xFF)
outputData[++outIdx] = (ds4.dPad == Controller.DpadDirection.West || ds4.dPad == Controller.DpadDirection.Northwest || ds4.dPad == Controller.DpadDirection.Southwest) ? (byte)0xFF : (byte)0;
outputData[++outIdx] = (ds4.dPad == Controller.DpadDirection.South || ds4.dPad == Controller.DpadDirection.Southwest || ds4.dPad == Controller.DpadDirection.Southeast) ? (byte)0xFF : (byte)0;
outputData[++outIdx] = (ds4.dPad == Controller.DpadDirection.East || ds4.dPad == Controller.DpadDirection.Northeast || ds4.dPad == Controller.DpadDirection.Southeast) ? (byte)0xFF : (byte)0;
outputData[++outIdx] = (ds4.dPad == Controller.DpadDirection.North || ds4.dPad == Controller.DpadDirection.Northwest || ds4.dPad == Controller.DpadDirection.Northeast) ? (byte)0xFF : (byte)0; ;
outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_LEFT : Joycon.Button.Y)) ? (byte)0xFF : (byte)0;
outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_DOWN : Joycon.Button.B)) ? (byte)0xFF : (byte)0;
outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_RIGHT : Joycon.Button.A)) ? (byte)0xFF : (byte)0;
outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_UP : Joycon.Button.X)) ? (byte)0xFF : (byte)0;
outputData[++outIdx] = ds4.square ? (byte)0xFF : (byte)0;
outputData[++outIdx] = ds4.cross ? (byte)0xFF : (byte)0;
outputData[++outIdx] = ds4.circle ? (byte)0xFF : (byte)0;
outputData[++outIdx] = ds4.triangle ? (byte)0xFF : (byte)0;
outputData[++outIdx] = (hidReport.GetButton(!swapXY ? (isLeft ? Joycon.Button.Y : Joycon.Button.DPAD_LEFT) : (isLeft ? Joycon.Button.X : Joycon.Button.DPAD_UP))) ? (byte)0xFF : (byte)0;
outputData[++outIdx] = (hidReport.GetButton(!swapAB ? (isLeft ? Joycon.Button.B : Joycon.Button.DPAD_DOWN) : (isLeft ? Joycon.Button.A : Joycon.Button.DPAD_RIGHT))) ? (byte)0xFF : (byte)0;
outputData[++outIdx] = (hidReport.GetButton(!swapAB ? (isLeft ? Joycon.Button.A : Joycon.Button.DPAD_RIGHT) : (isLeft ? Joycon.Button.B : Joycon.Button.DPAD_DOWN))) ? (byte)0xFF : (byte)0;
outputData[++outIdx] = (hidReport.GetButton(!swapXY ? (isLeft ? Joycon.Button.X : Joycon.Button.DPAD_UP) : (isLeft ? Joycon.Button.Y : Joycon.Button.DPAD_LEFT))) ? (byte)0xFF : (byte)0;
outputData[++outIdx] = ds4.shoulder_right ? (byte)0xFF : (byte)0;
outputData[++outIdx] = ds4.shoulder_left ? (byte)0xFF : (byte)0;
outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER2_1 : Joycon.Button.SHOULDER_1)) ? (byte)0xFF : (byte)0;
outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER_1 : Joycon.Button.SHOULDER2_1)) ? (byte)0xFF : (byte)0;
outputData[++outIdx] = ds4.trigger_right_value;
outputData[++outIdx] = ds4.trigger_left_value;
outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER2_2 : Joycon.Button.SHOULDER_2)) ? (byte)0xFF : (byte)0;
outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER_2 : Joycon.Button.SHOULDER2_2)) ? (byte)0xFF : (byte)0;
outIdx++;

BIN
BetterJoyForCemu/hidapi.dll Normal file

Binary file not shown.

View file

@ -2,6 +2,6 @@
<packages>
<package id="Crc32.NET" version="1.2.0" targetFramework="net461" />
<package id="JetBrains.Annotations" version="2020.1.0" targetFramework="net461" />
<package id="Nefarius.ViGEm.Client" version="1.17.178" targetFramework="net461" />
<package id="WindowsInput" version="6.3.0" targetFramework="net461" />
<package id="Nefarius.ViGEm.Client" version="1.0.0" targetFramework="net461" />
<package id="WindowsInput" version="6.1.1" targetFramework="net461" />
</packages>

Binary file not shown.

Binary file not shown.

109
README.md
View file

@ -1,107 +1,4 @@
<p align="center">
<img src="title.png">
</p>
# BetterJoy v6.1 - DS4 Gyro
This is an experimental branch of BetterJoy that uses a custom build of ViGEm to allow the use of DS4 gyro/accel controls. For more info, please see [the following wiki page](https://github.com/Davidobot/BetterJoy/wiki/DS4-Gyro).
# BetterJoy v7.0
Allows the Nintendo Switch Pro Controller, Joycons, and Switch SNES controller to be used with [Cemu](http://cemu.info/) using [Cemuhook](https://sshnuke.net/cemuhook/), [Citra](https://citra-emu.org/), [Dolphin](https://dolphin-emu.org/), [Yuzu](https://yuzu-emu.org/), and system-wide with generic XInput support.
It also allows using the gyro to control your mouse and remap the special buttons (SL, SR, Capture) to key bindings of your choice.
If anyone would like to donate (for whatever reason), [you can do so here](https://www.paypal.me/DavidKhachaturov/5).
#### Personal note
Thank you for using my software and all the constructive feedback I've been getting about it. I started writing this project a while back and have since then learnt a lot more about programming and software development in general. I don't have too much time to work on this project, but I will try to fix bugs when and if they arise. Thank you for your patience in that regard too!
It's been quite a wild ride, with nearly **590k** (!!) official download on GitHub and probably many more through the nightlies. I think this project was responsible for both software jobs I landed so far, so I am quite proud of it.
### Screenshot
![Example](https://user-images.githubusercontent.com/16619943/67919451-bf8e5680-fb76-11e9-995e-7193b87548e1.png)
# Downloads
Go to the [Releases tab](https://github.com/Davidobot/BetterJoy/releases/)!
# How to use
1. Install drivers
1. Read the READMEs (they're there for a reason!)
1. Run *Drivers/ViGEmBus_Setup_1.16.116.exe*
1. Restart your computer
2. Run *BetterJoyForCemu.exe*
1. Run as Administrator if your keyboard/mouse button mappings don't work
3. Connect your controllers.
4. Start Cemu and ensure CemuHook has the controller selected.
1. If using Joycons, CemuHook will detect two controllers - each will give all buttons, but choosing one over the other just chooses preference for which hand to use for gyro controls.
5. Go into *Input Settings*, choose XInput as a source and assign buttons normally.
1. If you don't want to do this for some reason, just have one input profile set up with *Wii U Gamepad* as the controller and enable "Also use for buttons/axes" under *GamePad motion source*. **This is no longer required as of version 3**
2. Turn rumble up to 70-80% if you want rumble.
* As of version 3, you can use the pro controller and Joycons as normal xbox controllers on your PC - try it with Steam!
# More Info
Check out the [wiki](https://github.com/Davidobot/BetterJoy/wiki)! There, you'll find all sorts of goodness such as the changelog, description of app settings, the FAQ and Problems page, and info on how to make BetterJoy work with Steam *better*.
# Connecting and Disconnecting the Controller
## Bluetooth Mode
* Hold down the small button (sync) on the top of the controller for 5 seconds - this puts the controller into broadcasting mode.
* Search for it in your bluetooth settings and pair normally.
* To disconnect the controller - hold the home button (or capture button) down for 2 seconds (or press the sync button). To reconnect - press any button on your controller.
## USB Mode
* Plug the controller into your computer.
## Disconnecting \[Windows 10]
1. Go into "Bluetooth and other devices settings"
1. Under the first category "Mouse, keyboard, & pen", there should be the pro controller.
1. Click on it and a "Remove" button will be revealed.
1. Press the "Remove" button
# Building
## Visual Studio (IDE)
1. If you didn't already, install **Visual Studio Community 2019** via
[the official guide](https://docs.microsoft.com/en-us/visualstudio/install/install-visual-studio?view=vs-2019).
When asked about the workloads, select **.NET Desktop Development**.
2. Get the code project via Git or by using the *Download ZIP* button.
3. Open Visual Studio Community and open the solution file (*BetterJoy.sln*).
4. Open the NuGet manager via *Tools > NuGet Package Manager > Package Manager Settings*.
5. You should have a warning mentioning *restoring your packages*. Click on the **Restore** button.
6. You can now run and build BetterJoy.
## Visual Studio Build Tools (CLI)
1. Download **Visual Studio Build Tools** via
[the official link](https://visualstudio.microsoft.com/it/downloads/#build-tools-for-visual-studio-2019).
2. Install **NuGet** by following
[the official guide](https://docs.microsoft.com/en-us/nuget/install-nuget-client-tools#nugetexe-cli).
You should follow the section for ***nuget.exe***.
Verify that you can run `nuget` from your favourite terminal.
3. Get the code project via Git or by using the *Download ZIP* button.
4. Open a terminal (*cmd*, *PowerShell*, ...) and enter the folder with the source code.
5. Restore the NuGet dependencies by running: `nuget restore`
6. Now build the app with MSBuild:
```
msbuild .\BetterJoy.sln -p:Configuration=CONFIGURATION -p:Platform=PLATFORM -t:Rebuild
```
The available values for **CONFIGURATION** are *Release* and *Debug*.
The available values for **PLATFORM** are *x86* and *x64* (you want the latter 99.99% of the time).
7. You have now built the app. See the next section for locating the binaries.
## Binaries location
The built binaries are located under
*BetterJoyForCemu\bin\PLATFORM\CONFIGURATION*
where `PLATFORM` and `CONFIGURATION` are the one provided at build time.
# Acknowledgements
A massive thanks goes out to [rajkosto](https://github.com/rajkosto/) for putting up with 17 emails and replying very quickly to my silly queries. The UDP server is also mostly taken from his [ScpToolkit](https://github.com/rajkosto/ScpToolkit) repo.
Also I am very grateful to [mfosse](https://github.com/mfosse/JoyCon-Driver) for pointing me in the right direction and to [Looking-Glass](https://github.com/Looking-Glass/JoyconLib) without whom I would not be able to figure anything out. (being honest here - the joycon code is his)
Many thanks to [nefarius](https://github.com/ViGEm/ViGEmBus) for his ViGEm project! Apologies and appreciation go out to [epigramx](https://github.com/epigramx), creator of *WiimoteHook*, for giving me the driver idea and for letting me keep using his installation batch script even though I took it without permission. Thanks go out to [MTCKC](https://github.com/MTCKC/ProconXInput) for inspiration and batch files.
A last thanks goes out to [dekuNukem](https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering) for his documentation, especially on the SPI calibration data and the IMU sensor notes!
Massive *thank you* to **all** code contributors!
Icons (modified): "[Switch Pro Controller](https://thenounproject.com/term/nintendo-switch/930119/)", "[
Switch Detachable Controller Left](https://thenounproject.com/remsing/uploads/?i=930115)", "[Switch Detachable Controller Right](https://thenounproject.com/remsing/uploads/?i=930121)" icons by Chad Remsing from [the Noun Project](http://thenounproject.com/). [Super Nintendo Controller](https://thenounproject.com/themizarkshow/collection/vectogram/?i=193592) icon by Mark Davis from the [the Noun Project](http://thenounproject.com/); icon modified by [Amy Alexander](https://www.linkedin.com/in/-amy-alexander/). [Nintendo 64 Controller](https://thenounproject.com/icon/game-controller-193588/) icon by Mark Davis from the [the Noun Project](http://thenounproject.com/); icon modified by [Gino Moena](https://www.github.com/GinoMoena).
I will merge this branch into master once ViGEm updates to include the gyro API (we're working on it)