Compare commits

..

61 commits

Author SHA1 Message Date
Genesis
b6715638a3
fix null exception when enableRumble setting to false (#1145) 2024-07-19 15:44:46 +01:00
Gino Moena
8890b053c7
Added n64 support (#1138)
* Added n64 support

* Updated icon n64

* fixed Y axis in C buttons

* Added dynamic calibration for n64 joy

* Added option real 64 range

* corrected the stick minimum value for both axis
2024-07-19 15:44:25 +01:00
Fábio Santos
461f5f8f5c
Scale XY coordinates according to new StickScalingFactor configuration option. Closes #630 (#954) 2022-08-04 15:04:23 +01:00
David Khachaturov
5e3d6ac4f4 - misc 2021-11-22 11:23:23 +00:00
BurndiL
96f5620f68
Float Parsing issue fix (#884) 2021-11-21 16:50:43 +00:00
David Khachaturov
95902a517a
Update README.md 2021-11-18 12:12:22 +00:00
cobalt2727
a685abde9c
mention rebooting (#648)
in my experience it tends to not work before a restart
2021-11-18 10:29:06 +00:00
Giovanni Santini
9a9cf60f18
Add building instructions (#851)
* Added building instructions

This can help users to contribute to the project.

* fix: Improve the last part of the building section
2021-11-18 10:28:48 +00:00
IonBlade2K
464eda0a79
- Fix ZL -> L2 and ZR -> R2 not working in many games (implement digital buttons) (#867)
- Remap Pro / combined "Capture" button to PS Share, and Minus -> Touchpad press, as is more logical given the purpose of the Capture button
2021-11-18 10:28:21 +00:00
Mercury233
f29a8aacb5
fix hidapi.dll loading (#816) 2021-11-18 10:27:53 +00:00
empathicqubit
0ccd62fd88
Bump WindowsInput version (#830) 2021-08-22 13:43:44 +01:00
David Khachaturov
50b64b1165 - Add option to use raw gyro (without filter) input for gyro control mapping 2021-04-30 17:05:48 +01:00
David Khachaturov
d0709ac6ff - Implement gyro-to-joystick; still needs testing on joycons 2021-04-29 16:22:55 +01:00
David Khachaturov
4423bcf881 - Add "DoNotRejoinJoycons" setting that allows you to use two joycons (or more) separately while in vertical mode 2021-04-24 19:15:53 +01:00
David Khachaturov
b1fc5267da - Add option to double click sticks to change joycon orientation (split or join) 2021-04-18 10:14:03 +01:00
David Khachaturov
f1996f3864 - Fix application of UseIncrementalLights 2021-04-18 08:56:04 +01:00
David Khachaturov
7444c01adc - fix Hori controllers (thank you bitamind!)
- add default calibration settings (acc/gyro and sticks) that are used for third party controllers automatically
2021-04-17 11:15:38 +01:00
David Khachaturov
e451b95536 Merge branch 'master' of https://github.com/Davidobot/BetterJoy 2021-04-17 10:08:54 +01:00
Casey Fitzpatrick
8d5fd675bd
MotionServer DS4 mapping (#743)
* Fix DS4 button mapping for PS and square buttons

* Use DS4 mapping for UdpServer

Leverage the complex single vs. double joycon vs. pro logic in the
Show as DS4 mapping code so that the MotionServer can properly support
a single joycon with SL and SR buttons available.
2021-03-31 20:37:37 +01:00
Odaem
2ca18ffe19
Fix incorrect mapping to DS4 (#742)
the mapping to ds4 when using combined joycon was faulty; this should be correct but I currently have no way to test it
2021-03-31 20:36:33 +01:00
David Khachaturov
b94203b0ab - fix typo 2021-03-29 09:42:05 +01:00
David Khachaturov
8d14a39ea4 - Update ViGEm 2021-03-29 09:40:15 +01:00
ASleepyCat
19bc9e32c5
Fix 3rdPartyControllers file not being able to be found when launching BetterJoyForCemu from Search/PowerToys Run (#703) 2021-02-23 09:02:08 +00:00
David Khachaturov
b8de5003a4 - Significantly improved gyro-to-mouse accuracy/stability by using a DCM filter. (https://x-io.co.uk/open-source-imu-and-ahrs-algorithms/)
- Gyro Analogue slider accuracy improved as well
2021-02-14 14:52:43 +01:00
ASleepyCat
8c591fd2f2
Fix settings file not being able to be found when launching BetterJoyForCemu from Search/PowerToys Run (#673)
* Explicitly assign settings file path

* Set settings path in Config constructor

* Make settings path readonly
2021-01-22 07:35:44 +01:00
Luca Mazzilli
53a0a272b8
used serial_number to identify and set type to a specific device (#663)
3rd party joycons (tested on joysky "wireless controller for N/SW) reported the same name "wireless gamepad" so i was unable to correcly address the left/right type of joycon. 

By using bluetooth serial number as additional identifier we can target specific device with correct type.

Changes:
- used serial_number as additional condition in "check list of custom controllers specified" block
- SController now incapsulate serial_number and use it for hash, equals, serialize etc
- Read the 5 index of 3rdPartyController file on _3rdPartyControllers
2021-01-13 18:57:09 +01:00
Chris
68f1c476c6
Use incremental lighting to indicate players on Joycons. (#617)
Consolidates LED and other/attached Joycon handling.
2020-11-19 08:54:00 +00:00
Chris
40dce4149c
Do not connect Joycons that are already paired. Fix concurrent modifications on Joycon list with concurrent list implementation. (#612) 2020-11-16 14:46:44 +00:00
David Khachaturov
c0b9240887 - Fix vendor_id/product_id mixup
- Max "calibrate" button non-default
2020-11-15 13:47:59 +00:00
shukenmg
57c1680537
Fix dequeuing (#594) 2020-11-03 16:42:48 +00:00
shukenmg
eda7605739
Expose debugging parameters (#593) 2020-11-03 08:20:42 +00:00
shukenmg
bfdea232cd
Rumble data processing refactor (#591)
* Rumble reimplementation

* Remove white space

* More whitespace

* Remove Update
2020-11-03 08:19:59 +00:00
shukenmg
6f5316d462
Remove unnecessary code (#592) 2020-11-03 08:19:25 +00:00
shukenmg
af3761ab3e
Adds an option to switch between controller calibrated joystick and a predetermined value (#527)
* Fixes an issue some users where having when they enabled Non Original Controller

#524

* Update Joycon.cs

Overwite deadzone 2

* Using enum to switch between

Co-authored-by: Shuken <shukenmg@iuvenisstudios.ga>
Co-authored-by: David Khachaturov <d77777k@gmail.com>
2020-11-02 14:55:23 +00:00
shukenmg
e0e2499f04
New option to calibrate controllers in BetterJoy (#522)
* Inital fix for #521

* Update App.config

* Update App.config

Co-authored-by: Shuken <shukenmg@iuvenisstudios.ga>
2020-11-02 14:52:57 +00:00
german77
f9f1ea4081
Fix accelerometer axis to have correct data for orientation (#563) 2020-10-06 12:33:09 +01:00
shukenmg
36346c1896
Add a mappable shake input
- mappable "shake" input

Co-authored-by: Shuken <shukenmg@iuvenisstudios.ga>
2020-09-22 14:47:26 +01:00
shukenmg
43786c12d9
Automatic switching between 32 and 64-bit hidapi dlls (#542)
- detect bitness of system and change dll appropriately automatically

Co-authored-by: Shuken <shukenmg@iuvenisstudios.ga>
2020-09-22 13:35:54 +01:00
Ceshion
06edcb1be5
🐛 fixes "too many items" when names are null (#543)
* 🐛 avert infinite loop when prod_id not assigned

Without ptr being reassigned in the case of a 0 prod_id, the app would get into an infinite loop on launch, before opening- this should resolve it 😊

* 🐛 fixes "too many items" when names are null

There is an (honestly misleading) OutOfMemoryException with the message "too many items in listbox" that is thrown when there are any items whose ToString() method returns null. This commit (in my maybe-biased opinion) gives a reasonable output in that case instead.
2020-09-22 08:40:10 +01:00
Ceshion
aec0e1d2a8
🐛 avert infinite loop when prod_id not assigned (#539)
Without ptr being reassigned in the case of a 0 prod_id, the app would get into an infinite loop on launch, before opening- this should resolve it 😊
2020-09-21 22:48:46 +01:00
shukenmg
4b30d51b8c
Change the width of the settings menu (#526)
* Change the width of the setings menu

Co-authored-by: Shuken <shukenmg@iuvenisstudios.ga>
2020-09-18 09:01:13 +01:00
shukenmg
c60aa0775f
Fixes #519 (#520)
Make sure to delete the settings file first.
2020-09-18 08:59:58 +01:00
David Khachaturov
99bae0ec2c - Added low battery notification
- Added automatic detection for badly-closed USB connections. Automatically reset the connection in such a case
2020-09-12 09:24:07 +01:00
David Khachaturov
4b181c6aed Merge branch 'master' of https://github.com/Davidobot/BetterJoy 2020-09-10 13:20:39 +01:00
David Khachaturov
2030247716 - Fix program not loading properly for some users 2020-09-10 13:20:35 +01:00
David Khachaturov
3ddd9a7fa0
Update README.md 2020-09-06 17:13:01 +01:00
David Khachaturov
5ea9b05e23
Update README.md 2020-09-06 17:00:42 +01:00
David Khachaturov
a68a2ee424 - Fix rumble (also change default freq values)
- Fix bug on seeing whether a controller is 3rd party
2020-09-06 16:44:49 +01:00
SonicLC
debace5b3a
Fixed button translation error (#425)
Fixed button translation error from OutputControllerDualShock4 to IDualShock4Controller
2020-06-15 19:47:12 +01:00
David Khachaturov
b499db2f0a - Fixed WindowsInput bugs causing the program to close on random key chords. 2020-06-14 10:19:21 +01:00
David Khachaturov
ecfa890c75 - WIP 3rd party controllers 2020-06-12 19:54:13 +01:00
David Khachaturov
e1f3e573fd - WIP Adding support for 3rd party controllers 2020-06-12 16:04:24 +01:00
David Khachaturov
a291f0daf6 - Fixed Subcommands not properly returning.
- all subcommand require a read back from the controller to confirm it has been acknowledged
 - Got rid of the last reminants of "BetterJoyForCemu"
 - Changed GUID to actual GUID of program
2020-06-12 09:26:22 +01:00
David Khachaturov
d894335017 - Added gyro toggle option 2020-06-11 09:27:06 +01:00
David Khachaturov
6d2b4b3918 - Fix LEDs not always being set
- Code clean-up
2020-06-11 09:09:18 +01:00
Robert Borg
f316ade501
avoids some multi threading issues (#418) 2020-06-11 08:03:32 +01:00
David Khachaturov
371346e2a7 Update issue templates 2020-06-09 19:54:25 +01:00
David Khachaturov
9d31a95d90 Update issue templates 2020-06-09 19:53:16 +01:00
David Khachaturov
8af8e581d3 - Changed rumble set_vals to milliseconds 2020-06-09 09:56:35 +01:00
Robert Borg
ff738222c0
Improve fetching subcommand response (#413)
* only send rumble command when rumble data has changed
* don't rerequest data for spi, just wait for response and discard non-related data
2020-06-09 09:26:41 +01:00
David Khachaturov
b0a6a87639
Revert "only send rumble command when rumble data has changed (#408)" (#415)
This reverts commit e9e7cefcdc.
2020-06-09 09:26:23 +01:00
30 changed files with 2714 additions and 1825 deletions

View file

@ -0,0 +1,18 @@
---
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

@ -0,0 +1,14 @@
---
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,212 +1,206 @@
namespace BetterJoyForCemu {
partial class _3rdPartyControllers {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
partial class _3rdPartyControllers {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(_3rdPartyControllers));
this.list_allControllers = new System.Windows.Forms.ListBox();
this.list_customControllers = new System.Windows.Forms.ListBox();
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.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();
this.label1 = new System.Windows.Forms.Label();
this.tip_device = new System.Windows.Forms.ToolTip(this.components);
this.btn_refresh = new System.Windows.Forms.Button();
this.group_props.SuspendLayout();
this.SuspendLayout();
//
// list_allControllers
//
this.list_allControllers.FormattingEnabled = true;
this.list_allControllers.Location = new System.Drawing.Point(12, 27);
this.list_allControllers.Name = "list_allControllers";
this.list_allControllers.Size = new System.Drawing.Size(103, 225);
this.list_allControllers.TabIndex = 0;
this.list_allControllers.SelectedValueChanged += new System.EventHandler(this.list_allControllers_SelectedValueChanged);
this.list_allControllers.MouseDown += new System.Windows.Forms.MouseEventHandler(this.list_allControllers_MouseDown);
//
// list_customControllers
//
this.list_customControllers.FormattingEnabled = true;
this.list_customControllers.Location = new System.Drawing.Point(169, 27);
this.list_customControllers.Name = "list_customControllers";
this.list_customControllers.Size = new System.Drawing.Size(103, 108);
this.list_customControllers.TabIndex = 1;
this.list_customControllers.SelectedValueChanged += new System.EventHandler(this.list_customControllers_SelectedValueChanged);
this.list_customControllers.MouseDown += new System.Windows.Forms.MouseEventHandler(this.list_customControllers_MouseDown);
//
// btn_add
//
this.btn_add.Location = new System.Drawing.Point(121, 27);
this.btn_add.Name = "btn_add";
this.btn_add.Size = new System.Drawing.Size(42, 23);
this.btn_add.TabIndex = 2;
this.btn_add.Text = "->";
this.btn_add.UseVisualStyleBackColor = true;
this.btn_add.Click += new System.EventHandler(this.btn_add_Click);
//
// btn_remove
//
this.btn_remove.Location = new System.Drawing.Point(121, 112);
this.btn_remove.Name = "btn_remove";
this.btn_remove.Size = new System.Drawing.Size(42, 23);
this.btn_remove.TabIndex = 3;
this.btn_remove.Text = "<-";
this.btn_remove.UseVisualStyleBackColor = true;
this.btn_remove.Click += new System.EventHandler(this.btn_remove_Click);
//
// group_props
//
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);
this.group_props.TabIndex = 4;
this.group_props.TabStop = false;
this.group_props.Text = "Settings";
//
// chk_isLeft
//
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);
//
// chk_isPro
//
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
//
this.btn_applyAndClose.Location = new System.Drawing.Point(203, 229);
this.btn_applyAndClose.Name = "btn_applyAndClose";
this.btn_applyAndClose.Size = new System.Drawing.Size(69, 23);
this.btn_applyAndClose.TabIndex = 5;
this.btn_applyAndClose.Text = "Close";
this.btn_applyAndClose.UseVisualStyleBackColor = true;
this.btn_applyAndClose.Click += new System.EventHandler(this.btn_applyAndClose_Click);
//
// btn_apply
//
this.btn_apply.Location = new System.Drawing.Point(121, 229);
this.btn_apply.Name = "btn_apply";
this.btn_apply.Size = new System.Drawing.Size(69, 23);
this.btn_apply.TabIndex = 6;
this.btn_apply.Text = "Apply";
this.btn_apply.UseVisualStyleBackColor = true;
this.btn_apply.Click += new System.EventHandler(this.btn_apply_Click);
//
// lbl_all
//
this.lbl_all.AutoSize = true;
this.lbl_all.Location = new System.Drawing.Point(12, 11);
this.lbl_all.Name = "lbl_all";
this.lbl_all.Size = new System.Drawing.Size(60, 13);
this.lbl_all.TabIndex = 7;
this.lbl_all.Text = "All Devices";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(166, 11);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(91, 13);
this.label1.TabIndex = 8;
this.label1.Text = "Switch Controllers";
//
// btn_refresh
//
this.btn_refresh.Location = new System.Drawing.Point(121, 56);
this.btn_refresh.Name = "btn_refresh";
this.btn_refresh.Size = new System.Drawing.Size(42, 50);
this.btn_refresh.TabIndex = 9;
this.btn_refresh.Text = "Re-\r\nfresh";
this.btn_refresh.UseVisualStyleBackColor = true;
this.btn_refresh.Click += new System.EventHandler(this.btn_refresh_Click);
//
// _3rdPartyControllers
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 261);
this.Controls.Add(this.btn_refresh);
this.Controls.Add(this.label1);
this.Controls.Add(this.lbl_all);
this.Controls.Add(this.btn_apply);
this.Controls.Add(this.btn_applyAndClose);
this.Controls.Add(this.group_props);
this.Controls.Add(this.btn_remove);
this.Controls.Add(this.btn_add);
this.Controls.Add(this.list_customControllers);
this.Controls.Add(this.list_allControllers);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "_3rdPartyControllers";
this.Text = "Add 3rd-Party Controllers";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this._3rdPartyControllers_FormClosing);
this.group_props.ResumeLayout(false);
this.group_props.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(_3rdPartyControllers));
this.list_allControllers = new System.Windows.Forms.ListBox();
this.list_customControllers = new System.Windows.Forms.ListBox();
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.btn_applyAndClose = new System.Windows.Forms.Button();
this.btn_apply = new System.Windows.Forms.Button();
this.lbl_all = new System.Windows.Forms.Label();
this.label1 = new System.Windows.Forms.Label();
this.tip_device = new System.Windows.Forms.ToolTip(this.components);
this.btn_refresh = new System.Windows.Forms.Button();
this.group_props.SuspendLayout();
this.SuspendLayout();
//
// list_allControllers
//
this.list_allControllers.FormattingEnabled = true;
this.list_allControllers.Location = new System.Drawing.Point(12, 27);
this.list_allControllers.Name = "list_allControllers";
this.list_allControllers.Size = new System.Drawing.Size(103, 225);
this.list_allControllers.TabIndex = 0;
this.list_allControllers.SelectedValueChanged += new System.EventHandler(this.list_allControllers_SelectedValueChanged);
this.list_allControllers.MouseDown += new System.Windows.Forms.MouseEventHandler(this.list_allControllers_MouseDown);
//
// list_customControllers
//
this.list_customControllers.FormattingEnabled = true;
this.list_customControllers.Location = new System.Drawing.Point(169, 27);
this.list_customControllers.Name = "list_customControllers";
this.list_customControllers.Size = new System.Drawing.Size(103, 108);
this.list_customControllers.TabIndex = 1;
this.list_customControllers.SelectedValueChanged += new System.EventHandler(this.list_customControllers_SelectedValueChanged);
this.list_customControllers.MouseDown += new System.Windows.Forms.MouseEventHandler(this.list_customControllers_MouseDown);
//
// btn_add
//
this.btn_add.Location = new System.Drawing.Point(121, 27);
this.btn_add.Name = "btn_add";
this.btn_add.Size = new System.Drawing.Size(42, 23);
this.btn_add.TabIndex = 2;
this.btn_add.Text = "->";
this.btn_add.UseVisualStyleBackColor = true;
this.btn_add.Click += new System.EventHandler(this.btn_add_Click);
//
// btn_remove
//
this.btn_remove.Location = new System.Drawing.Point(121, 112);
this.btn_remove.Name = "btn_remove";
this.btn_remove.Size = new System.Drawing.Size(42, 23);
this.btn_remove.TabIndex = 3;
this.btn_remove.Text = "<-";
this.btn_remove.UseVisualStyleBackColor = true;
this.btn_remove.Click += new System.EventHandler(this.btn_remove_Click);
//
// group_props
//
this.group_props.Controls.Add(this.label2);
this.group_props.Controls.Add(this.chooseType);
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);
this.group_props.TabIndex = 4;
this.group_props.TabStop = false;
this.group_props.Text = "Settings";
//
// label2
//
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";
//
// chooseType
//
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);
//
// btn_applyAndClose
//
this.btn_applyAndClose.Location = new System.Drawing.Point(203, 229);
this.btn_applyAndClose.Name = "btn_applyAndClose";
this.btn_applyAndClose.Size = new System.Drawing.Size(69, 23);
this.btn_applyAndClose.TabIndex = 5;
this.btn_applyAndClose.Text = "Close";
this.btn_applyAndClose.UseVisualStyleBackColor = true;
this.btn_applyAndClose.Click += new System.EventHandler(this.btn_applyAndClose_Click);
//
// btn_apply
//
this.btn_apply.Location = new System.Drawing.Point(121, 229);
this.btn_apply.Name = "btn_apply";
this.btn_apply.Size = new System.Drawing.Size(69, 23);
this.btn_apply.TabIndex = 6;
this.btn_apply.Text = "Apply";
this.btn_apply.UseVisualStyleBackColor = true;
this.btn_apply.Click += new System.EventHandler(this.btn_apply_Click);
//
// lbl_all
//
this.lbl_all.AutoSize = true;
this.lbl_all.Location = new System.Drawing.Point(12, 11);
this.lbl_all.Name = "lbl_all";
this.lbl_all.Size = new System.Drawing.Size(60, 13);
this.lbl_all.TabIndex = 7;
this.lbl_all.Text = "All Devices";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(166, 11);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(91, 13);
this.label1.TabIndex = 8;
this.label1.Text = "Switch Controllers";
//
// btn_refresh
//
this.btn_refresh.Location = new System.Drawing.Point(121, 56);
this.btn_refresh.Name = "btn_refresh";
this.btn_refresh.Size = new System.Drawing.Size(42, 50);
this.btn_refresh.TabIndex = 9;
this.btn_refresh.Text = "Re-\r\nfresh";
this.btn_refresh.UseVisualStyleBackColor = true;
this.btn_refresh.Click += new System.EventHandler(this.btn_refresh_Click);
//
// _3rdPartyControllers
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 261);
this.Controls.Add(this.btn_refresh);
this.Controls.Add(this.label1);
this.Controls.Add(this.lbl_all);
this.Controls.Add(this.btn_apply);
this.Controls.Add(this.btn_applyAndClose);
this.Controls.Add(this.group_props);
this.Controls.Add(this.btn_remove);
this.Controls.Add(this.btn_add);
this.Controls.Add(this.list_customControllers);
this.Controls.Add(this.list_allControllers);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "_3rdPartyControllers";
this.Text = "Add 3rd-Party Controllers";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this._3rdPartyControllers_FormClosing);
this.group_props.ResumeLayout(false);
this.group_props.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
}
#endregion
#endregion
private System.Windows.Forms.ListBox list_allControllers;
private System.Windows.Forms.ListBox list_customControllers;
private System.Windows.Forms.Button btn_add;
private System.Windows.Forms.Button btn_remove;
private System.Windows.Forms.GroupBox group_props;
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.ListBox list_allControllers;
private System.Windows.Forms.ListBox list_customControllers;
private System.Windows.Forms.Button btn_add;
private System.Windows.Forms.Button btn_remove;
private System.Windows.Forms.GroupBox group_props;
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.Label label1;
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,6 +3,7 @@ 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;
@ -12,110 +13,199 @@ using System.Windows.Forms;
using static BetterJoyForCemu.HIDapi;
namespace BetterJoyForCemu {
public partial class _3rdPartyControllers : Form {
public _3rdPartyControllers() {
InitializeComponent();
list_allControllers.DisplayMember = "Text";
list_allControllers.ValueMember = "Value";
list_customControllers.DisplayMember = "Text";
list_customControllers.ValueMember = "Value";
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
RefreshControllerList();
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;
}
group_props.Controls.Add(chk_isLeft);
group_props.Controls.Add(chk_isPro);
group_props.Enabled = false;
}
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);
}
}
private bool ContainsText(ListBox a, String manu) {
foreach (var v in a.Items) {
dynamic d = v as dynamic;
if (d.Text == null)
continue;
if (d.Text.Equals(manu))
return true;
}
return false;
}
public override int GetHashCode() {
return Tuple.Create(product_id, vendor_id, serial_number).GetHashCode();
}
private void RefreshControllerList() {
list_allControllers.Items.Clear();
IntPtr ptr = HIDapi.hid_enumerate(0x0, 0x0);
IntPtr top_ptr = ptr;
public override string ToString() {
return name ?? $"Unidentified Device ({this.product_id})";
}
hid_device_info enumerate; // Add device to list
while (ptr != IntPtr.Zero) {
enumerate = (hid_device_info)Marshal.PtrToStructure(ptr, typeof(hid_device_info));
public string Serialise() {
return String.Format("{0}|{1}|{2}|{3}|{4}", name, vendor_id, product_id, type, serial_number);
}
}
if (!ContainsText(list_customControllers, enumerate.product_string) && !ContainsText(list_allControllers, enumerate.product_string))
list_allControllers.Items.Add(new { Text = enumerate.product_string, Value = enumerate });
static readonly string path;
ptr = enumerate.next;
}
}
static _3rdPartyControllers() {
path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)
+ "\\3rdPartyControllers";
}
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();
}
}
public _3rdPartyControllers() {
InitializeComponent();
list_allControllers.HorizontalScrollbar = true; list_customControllers.HorizontalScrollbar = true;
private void btn_remove_Click(object sender, EventArgs e) {
if (list_customControllers.SelectedItem != null) {
list_allControllers.Items.Add(list_customControllers.SelectedItem);
list_customControllers.Items.Remove(list_customControllers.SelectedItem);
list_customControllers.ClearSelected();
}
}
chooseType.Items.AddRange(new String[] { "Pro Controller", "Left Joycon", "Right Joycon" });
private void chk_isPro_CheckedChanged(object sender, EventArgs e) {
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));
}
}
}
private void chk_isLeft_CheckedChanged(object sender, EventArgs e) {
CopyCustomControllers();
RefreshControllerList();
}
}
public void CopyCustomControllers() {
Program.thirdPartyCons.Clear();
foreach (SController v in list_customControllers.Items) {
Program.thirdPartyCons.Add(v);
}
}
private void btn_apply_Click(object sender, EventArgs e) {
private bool ContainsText(ListBox a, String manu) {
foreach (SController v in a.Items) {
if (v == null)
continue;
if (v.name == null)
continue;
if (v.name.Equals(manu))
return true;
}
return false;
}
}
private void RefreshControllerList() {
list_allControllers.Items.Clear();
IntPtr ptr = HIDapi.hid_enumerate(0x0, 0x0);
IntPtr top_ptr = ptr;
private void btn_applyAndClose_Click(object sender, EventArgs e) {
btn_apply_Click(sender, e);
Close();
}
hid_device_info enumerate; // Add device to list
while (ptr != IntPtr.Zero) {
enumerate = (hid_device_info)Marshal.PtrToStructure(ptr, typeof(hid_device_info));
private void _3rdPartyControllers_FormClosing(object sender, FormClosingEventArgs e) {
btn_apply_Click(sender, e);
}
if (enumerate.serial_number == null) {
ptr = enumerate.next;
continue;
}
private void btn_refresh_Click(object sender, EventArgs e) {
RefreshControllerList();
}
// 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);
}
private void list_allControllers_SelectedValueChanged(object sender, EventArgs e) {
if (list_allControllers.SelectedItem != null)
tip_device.Show((list_allControllers.SelectedItem as dynamic).Text, list_allControllers);
}
ptr = enumerate.next;
}
HIDapi.hid_free_enumeration(top_ptr);
}
private void list_customControllers_SelectedValueChanged(object sender, EventArgs e) {
if (list_customControllers.SelectedItem != null) {
tip_device.Show((list_customControllers.SelectedItem as dynamic).Text, list_customControllers);
group_props.Enabled = true;
} else
group_props.Enabled = false;
}
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);
private void list_customControllers_MouseDown(object sender, MouseEventArgs e) {
if (e.Y > list_customControllers.ItemHeight * list_customControllers.Items.Count)
list_customControllers.SelectedItems.Clear();
}
list_allControllers.ClearSelected();
}
}
private void list_allControllers_MouseDown(object sender, MouseEventArgs e) {
if (e.Y > list_allControllers.ItemHeight * list_allControllers.Items.Count)
list_allControllers.SelectedItems.Clear();
}
}
private void btn_remove_Click(object sender, EventArgs e) {
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";
}
File.WriteAllText(path, sc);
CopyCustomControllers();
}
private void btn_applyAndClose_Click(object sender, EventArgs e) {
btn_apply_Click(sender, e);
Close();
}
private void _3rdPartyControllers_FormClosing(object sender, FormClosingEventArgs e) {
btn_apply_Click(sender, e);
}
private void btn_refresh_Click(object sender, EventArgs e) {
RefreshControllerList();
}
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);
}
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;
group_props.Enabled = true;
} else {
chooseType.SelectedIndex = -1;
group_props.Enabled = false;
}
}
private void list_customControllers_MouseDown(object sender, MouseEventArgs e) {
if (e.Y > list_customControllers.ItemHeight * list_customControllers.Items.Count)
list_customControllers.SelectedItems.Clear();
}
private void list_allControllers_MouseDown(object sender, MouseEventArgs e) {
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,7 +1,7 @@
<?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" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
<appSettings>
<!--Motion Server IP: the default is localhost; you can change it to 0.0.0.0 (all interfaces) or a specific LAN IP
@ -13,17 +13,25 @@
<!--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: 20 and 400 -->
<add key="LowFreqRumble" value="20" />
<add key="HighFreqRumble" value="400" />
<!--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" />
<!--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 -->
@ -33,13 +41,32 @@
<!--On is "true"; off is "false". Default: false -->
<add key="SwapXY" value="false" />
<!--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 -->
<!-- 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 -20 to change direction of tilt needed. Positive is ramp up if pointing up-->
<!--Default: 20 -->
<add key="GyroAnalogSensitivity" value="20" />
<!-- 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) -->
@ -50,27 +77,41 @@
<!-- 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", BetterJoyForCemu 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", BetterJoy 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="true" />
<add key="UseHIDG" value="false" />
<!-- 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", turn gyro movements into joystick movement (left/right depends on setting) [not yet implemented]-->
<!-- When "joy_left" or "joy_right", turn gyro movements into respective left/right joystick (mouse-like) movements -->
<!-- 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="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"/>
<!-- Sensitivity of gyro-to-mouse movements -->
<!-- Default: 50 -->
<add key="GyroMouseSensitivity" value="50"/>
<!-- 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"/>
<!-- 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"/>
@ -80,7 +121,7 @@
<add key="DragToggle" value="false"/>
<!-- Determines whether or not the program will expose detected controllers as Xbox 360 controllers -->
<!-- When "false", BetterJoyForCemu is only usable with CEMU. -->
<!-- When "false", BetterJoy is only usable with programs that support UDPServer. -->
<!-- Default: true -->
<add key="ShowAsXInput" value="true" />
<!-- Have ShowAsXInput as false if using this -->
@ -92,9 +133,28 @@
<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="30" />
<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.16.150.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nefarius.ViGEm.Client.1.16.150\lib\net452\Nefarius.ViGEm.Client.dll</HintPath>
<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>
<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.1.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\WindowsInput.6.1.1\lib\net461\WindowsInput.dll</HintPath>
<Reference Include="WindowsInput, Version=6.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\WindowsInput.6.3.0\lib\net461\WindowsInput.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
@ -130,11 +130,13 @@
<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>
@ -178,6 +180,12 @@
<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>
@ -230,13 +238,14 @@
<Content Include="Drivers\README.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Drivers\ViGEmBus_Setup_1.16.116.exe">
<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="hidapi.dll">
<Content Include="x86\hidapi.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="x64\hidapi.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Icons\cross.png" />

View file

@ -0,0 +1,116 @@
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

@ -4,10 +4,14 @@ using System.IO;
namespace BetterJoyForCemu {
public static class Config { // stores dynamic configuration, including
const string PATH = "settings";
static readonly string path;
static Dictionary<string, string> variables = new Dictionary<string, string>();
const int settingsNum = 10; // currently - ProgressiveScan, StartInTray + special buttons
const int settingsNum = 11; // currently - ProgressiveScan, StartInTray + special buttons
static Config() {
path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\settings";
}
public static string GetDefaultValue(string s) {
switch (s) {
@ -21,15 +25,36 @@ 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", "reset_mouse", "active_gyro" })
foreach (string s in new string[] { "ProgressiveScan", "StartInTray", "capture", "home", "sl_l", "sl_r", "sr_l", "sr_r", "shake", "reset_mouse", "active_gyro" })
variables[s] = GetDefaultValue(s);
if (File.Exists(PATH)) {
int lineNO = 0;
using (StreamReader file = new StreamReader(PATH)) {
string line = String.Empty;
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;
int lineNO = 0;
while ((line = file.ReadLine()) != null) {
string[] vs = line.Split();
try {
@ -52,17 +77,9 @@ 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 = "";
@ -98,7 +115,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);
@ -108,18 +125,18 @@ namespace BetterJoyForCemu {
if (i == 0) space = "";
caliStr += space + caliData[i].Key + "," + String.Join(",", caliData[i].Value);
}
txt[2] = caliStr;
File.WriteAllLines(PATH, txt);
txt[settingsNum] = 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

@ -133,7 +133,7 @@ namespace BetterJoyForCemu.Controller {
controller.SetButtonState(DualShock4Button.TriggerRight, new_state.trigger_right);
controller.SetButtonState(DualShock4Button.ThumbLeft, new_state.thumb_left);
controller.SetButtonState(DualShock4Button.ThumbRight, 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);

View file

@ -91,7 +91,8 @@ namespace BetterJoyForCemu.Controller {
}
private void FeedbackReceivedRcv(object _sender, Xbox360FeedbackReceivedEventArgs e) {
FeedbackReceived(e);
if(FeedbackReceived!=null)
FeedbackReceived(e);
}
public bool UpdateInput(OutputControllerXbox360InputState new_state) {

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,162 @@
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

@ -1,394 +1,381 @@
namespace BetterJoyForCemu {
partial class MainForm {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
partial class MainForm {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
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();
this.version_lbl = new System.Windows.Forms.Label();
this.passiveScanBox = new System.Windows.Forms.CheckBox();
this.donationLink = new System.Windows.Forms.LinkLabel();
this.conCntrls = new System.Windows.Forms.GroupBox();
this.loc4 = new System.Windows.Forms.Button();
this.loc3 = new System.Windows.Forms.Button();
this.loc2 = new System.Windows.Forms.Button();
this.loc1 = new System.Windows.Forms.Button();
this.con4 = new System.Windows.Forms.Button();
this.con3 = new System.Windows.Forms.Button();
this.con2 = new System.Windows.Forms.Button();
this.con1 = new System.Windows.Forms.Button();
this.btnTip = new System.Windows.Forms.ToolTip(this.components);
this.foldLbl = new System.Windows.Forms.Label();
this.startInTrayBox = new System.Windows.Forms.CheckBox();
this.btn_open3rdP = new System.Windows.Forms.Button();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.settingsTable = new System.Windows.Forms.TableLayoutPanel();
this.rightPanel = new System.Windows.Forms.Panel();
this.settingsApply = new System.Windows.Forms.Button();
this.AutoCalibrate = new System.Windows.Forms.Button();
this.btn_reassign_open = new System.Windows.Forms.Button();
this.contextMenu.SuspendLayout();
this.conCntrls.SuspendLayout();
this.groupBox1.SuspendLayout();
this.rightPanel.SuspendLayout();
this.SuspendLayout();
//
// console
//
this.console.Location = new System.Drawing.Point(12, 132);
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 = "BetterJoyForCemu";
this.notifyIcon.ContextMenuStrip = this.contextMenu;
this.notifyIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon.Icon")));
this.notifyIcon.Text = "BetterJoyForCemu";
this.notifyIcon.Visible = true;
this.notifyIcon.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.notifyIcon_MouseDoubleClick);
//
// contextMenu
//
this.contextMenu.ImageScalingSize = new System.Drawing.Size(24, 24);
this.contextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
this.console = new System.Windows.Forms.TextBox();
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();
this.version_lbl = new System.Windows.Forms.Label();
this.passiveScanBox = new System.Windows.Forms.CheckBox();
this.donationLink = new System.Windows.Forms.LinkLabel();
this.conCntrls = new System.Windows.Forms.GroupBox();
this.loc4 = new System.Windows.Forms.Button();
this.loc3 = new System.Windows.Forms.Button();
this.loc2 = new System.Windows.Forms.Button();
this.loc1 = new System.Windows.Forms.Button();
this.con4 = new System.Windows.Forms.Button();
this.con3 = new System.Windows.Forms.Button();
this.con2 = new System.Windows.Forms.Button();
this.con1 = new System.Windows.Forms.Button();
this.btnTip = new System.Windows.Forms.ToolTip(this.components);
this.foldLbl = new System.Windows.Forms.Label();
this.startInTrayBox = new System.Windows.Forms.CheckBox();
this.btn_open3rdP = new System.Windows.Forms.Button();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.settingsTable = new System.Windows.Forms.TableLayoutPanel();
this.rightPanel = new System.Windows.Forms.Panel();
this.settingsApply = new System.Windows.Forms.Button();
this.AutoCalibrate = new System.Windows.Forms.Button();
this.btn_reassign_open = new System.Windows.Forms.Button();
this.contextMenu.SuspendLayout();
this.conCntrls.SuspendLayout();
this.groupBox1.SuspendLayout();
this.rightPanel.SuspendLayout();
this.SuspendLayout();
//
// console
//
this.console.Location = new System.Drawing.Point(12, 132);
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;
//
// notifyIcon
//
this.notifyIcon.BalloonTipText = "Double click the tray icon to maximise!";
this.notifyIcon.BalloonTipTitle = "BetterJoy";
this.notifyIcon.ContextMenuStrip = this.contextMenu;
this.notifyIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon.Icon")));
this.notifyIcon.Text = "BetterJoy";
this.notifyIcon.Visible = true;
this.notifyIcon.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.notifyIcon_MouseDoubleClick);
//
// contextMenu
//
this.contextMenu.ImageScalingSize = new System.Drawing.Size(24, 24);
this.contextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.exitToolStripMenuItem});
this.contextMenu.Name = "contextMenu";
this.contextMenu.Size = new System.Drawing.Size(94, 26);
//
// exitToolStripMenuItem
//
this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
this.exitToolStripMenuItem.Size = new System.Drawing.Size(93, 22);
this.exitToolStripMenuItem.Text = "Exit";
this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
//
// version_lbl
//
this.version_lbl.AutoSize = true;
this.version_lbl.Location = new System.Drawing.Point(246, 239);
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 = "v6.3";
//
// passiveScanBox
//
this.passiveScanBox.AutoSize = true;
this.passiveScanBox.Checked = true;
this.passiveScanBox.CheckState = System.Windows.Forms.CheckState.Checked;
this.passiveScanBox.Location = new System.Drawing.Point(12, 238);
this.passiveScanBox.Name = "passiveScanBox";
this.passiveScanBox.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
this.passiveScanBox.Size = new System.Drawing.Size(91, 17);
this.passiveScanBox.TabIndex = 4;
this.passiveScanBox.Text = "Passive Scan";
this.passiveScanBox.UseVisualStyleBackColor = true;
this.passiveScanBox.CheckedChanged += new System.EventHandler(this.passiveScanBox_CheckedChanged);
//
// donationLink
//
this.donationLink.AutoSize = true;
this.donationLink.Location = new System.Drawing.Point(196, 239);
this.donationLink.Name = "donationLink";
this.donationLink.Size = new System.Drawing.Size(42, 13);
this.donationLink.TabIndex = 5;
this.donationLink.TabStop = true;
this.donationLink.Text = "Donate";
this.donationLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked);
//
// conCntrls
//
this.conCntrls.Controls.Add(this.loc4);
this.conCntrls.Controls.Add(this.loc3);
this.conCntrls.Controls.Add(this.loc2);
this.conCntrls.Controls.Add(this.loc1);
this.conCntrls.Controls.Add(this.con4);
this.conCntrls.Controls.Add(this.con3);
this.conCntrls.Controls.Add(this.con2);
this.conCntrls.Controls.Add(this.con1);
this.conCntrls.Location = new System.Drawing.Point(12, 12);
this.conCntrls.Name = "conCntrls";
this.conCntrls.Size = new System.Drawing.Size(262, 100);
this.conCntrls.TabIndex = 0;
this.conCntrls.TabStop = false;
this.conCntrls.Text = "Connected Controllers";
//
// loc4
//
this.loc4.Location = new System.Drawing.Point(198, 80);
this.loc4.Name = "loc4";
this.loc4.Size = new System.Drawing.Size(58, 20);
this.loc4.TabIndex = 7;
this.loc4.Text = "Locate";
this.loc4.UseVisualStyleBackColor = true;
//
// loc3
//
this.loc3.Location = new System.Drawing.Point(134, 80);
this.loc3.Name = "loc3";
this.loc3.Size = new System.Drawing.Size(58, 20);
this.loc3.TabIndex = 6;
this.loc3.Text = "Locate";
this.loc3.UseVisualStyleBackColor = true;
//
// loc2
//
this.loc2.Location = new System.Drawing.Point(70, 80);
this.loc2.Name = "loc2";
this.loc2.Size = new System.Drawing.Size(58, 20);
this.loc2.TabIndex = 5;
this.loc2.Text = "Locate";
this.loc2.UseVisualStyleBackColor = true;
//
// loc1
//
this.loc1.Location = new System.Drawing.Point(6, 80);
this.loc1.Name = "loc1";
this.loc1.Size = new System.Drawing.Size(58, 20);
this.loc1.TabIndex = 4;
this.loc1.Text = "Locate";
this.loc1.UseVisualStyleBackColor = true;
//
// con4
//
this.con4.BackgroundImage = global::BetterJoyForCemu.Properties.Resources.cross;
this.con4.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.con4.Enabled = false;
this.con4.Location = new System.Drawing.Point(198, 20);
this.con4.Name = "con4";
this.con4.Size = new System.Drawing.Size(58, 59);
this.con4.TabIndex = 3;
this.con4.TabStop = false;
this.con4.UseVisualStyleBackColor = true;
//
// con3
//
this.con3.BackgroundImage = global::BetterJoyForCemu.Properties.Resources.cross;
this.con3.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.con3.Enabled = false;
this.con3.Location = new System.Drawing.Point(134, 20);
this.con3.Name = "con3";
this.con3.Size = new System.Drawing.Size(58, 59);
this.con3.TabIndex = 2;
this.con3.TabStop = false;
this.con3.UseVisualStyleBackColor = true;
//
// con2
//
this.con2.BackgroundImage = global::BetterJoyForCemu.Properties.Resources.cross;
this.con2.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.con2.Enabled = false;
this.con2.Location = new System.Drawing.Point(70, 20);
this.con2.Name = "con2";
this.con2.Size = new System.Drawing.Size(58, 59);
this.con2.TabIndex = 1;
this.con2.TabStop = false;
this.con2.UseVisualStyleBackColor = true;
//
// con1
//
this.con1.BackgroundImage = global::BetterJoyForCemu.Properties.Resources.cross;
this.con1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.con1.Enabled = false;
this.con1.Location = new System.Drawing.Point(6, 20);
this.con1.Name = "con1";
this.con1.Size = new System.Drawing.Size(58, 59);
this.con1.TabIndex = 0;
this.con1.TabStop = false;
this.btnTip.SetToolTip(this.con1, "Click on Joycons to join/split them");
this.con1.UseVisualStyleBackColor = true;
//
// foldLbl
//
this.foldLbl.Location = new System.Drawing.Point(274, 20);
this.foldLbl.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0);
this.foldLbl.Name = "foldLbl";
this.foldLbl.Size = new System.Drawing.Size(13, 231);
this.foldLbl.TabIndex = 12;
this.foldLbl.Text = ">";
this.foldLbl.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.btnTip.SetToolTip(this.foldLbl, "Config");
this.foldLbl.Click += new System.EventHandler(this.foldLbl_Click);
//
// startInTrayBox
//
this.startInTrayBox.AutoSize = true;
this.startInTrayBox.Location = new System.Drawing.Point(107, 238);
this.startInTrayBox.Name = "startInTrayBox";
this.startInTrayBox.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
this.startInTrayBox.Size = new System.Drawing.Size(83, 17);
this.startInTrayBox.TabIndex = 6;
this.startInTrayBox.Text = "Start in Tray";
this.startInTrayBox.UseVisualStyleBackColor = true;
this.startInTrayBox.CheckedChanged += new System.EventHandler(this.startInTrayBox_CheckedChanged);
//
// btn_open3rdP
//
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;
this.btn_open3rdP.Text = "Add Controllers";
this.btn_open3rdP.UseVisualStyleBackColor = true;
this.btn_open3rdP.Click += new System.EventHandler(this.btn_open3rdP_Click);
//
// groupBox1
//
this.groupBox1.Controls.Add(this.settingsTable);
this.groupBox1.Location = new System.Drawing.Point(3, 11);
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(227, 242);
this.groupBox1.TabIndex = 9;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Config";
//
// settingsTable
//
this.settingsTable.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
this.contextMenu.Name = "contextMenu";
this.contextMenu.Size = new System.Drawing.Size(94, 26);
//
// exitToolStripMenuItem
//
this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
this.exitToolStripMenuItem.Size = new System.Drawing.Size(93, 22);
this.exitToolStripMenuItem.Text = "Exit";
this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
//
// version_lbl
//
this.version_lbl.AutoSize = true;
this.version_lbl.Location = new System.Drawing.Point(246, 239);
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";
//
// passiveScanBox
//
this.passiveScanBox.AutoSize = true;
this.passiveScanBox.Checked = true;
this.passiveScanBox.CheckState = System.Windows.Forms.CheckState.Checked;
this.passiveScanBox.Location = new System.Drawing.Point(12, 238);
this.passiveScanBox.Name = "passiveScanBox";
this.passiveScanBox.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
this.passiveScanBox.Size = new System.Drawing.Size(91, 17);
this.passiveScanBox.TabIndex = 4;
this.passiveScanBox.Text = "Passive Scan";
this.passiveScanBox.UseVisualStyleBackColor = true;
this.passiveScanBox.CheckedChanged += new System.EventHandler(this.passiveScanBox_CheckedChanged);
//
// donationLink
//
this.donationLink.AutoSize = true;
this.donationLink.Location = new System.Drawing.Point(196, 239);
this.donationLink.Name = "donationLink";
this.donationLink.Size = new System.Drawing.Size(42, 13);
this.donationLink.TabIndex = 5;
this.donationLink.TabStop = true;
this.donationLink.Text = "Donate";
this.donationLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked);
//
// conCntrls
//
this.conCntrls.Controls.Add(this.loc4);
this.conCntrls.Controls.Add(this.loc3);
this.conCntrls.Controls.Add(this.loc2);
this.conCntrls.Controls.Add(this.loc1);
this.conCntrls.Controls.Add(this.con4);
this.conCntrls.Controls.Add(this.con3);
this.conCntrls.Controls.Add(this.con2);
this.conCntrls.Controls.Add(this.con1);
this.conCntrls.Location = new System.Drawing.Point(12, 12);
this.conCntrls.Name = "conCntrls";
this.conCntrls.Size = new System.Drawing.Size(262, 100);
this.conCntrls.TabIndex = 0;
this.conCntrls.TabStop = false;
this.conCntrls.Text = "Connected Controllers";
//
// loc4
//
this.loc4.Location = new System.Drawing.Point(198, 80);
this.loc4.Name = "loc4";
this.loc4.Size = new System.Drawing.Size(58, 20);
this.loc4.TabIndex = 7;
this.loc4.Text = "Locate";
this.loc4.UseVisualStyleBackColor = true;
//
// loc3
//
this.loc3.Location = new System.Drawing.Point(134, 80);
this.loc3.Name = "loc3";
this.loc3.Size = new System.Drawing.Size(58, 20);
this.loc3.TabIndex = 6;
this.loc3.Text = "Locate";
this.loc3.UseVisualStyleBackColor = true;
//
// loc2
//
this.loc2.Location = new System.Drawing.Point(70, 80);
this.loc2.Name = "loc2";
this.loc2.Size = new System.Drawing.Size(58, 20);
this.loc2.TabIndex = 5;
this.loc2.Text = "Locate";
this.loc2.UseVisualStyleBackColor = true;
//
// loc1
//
this.loc1.Location = new System.Drawing.Point(6, 80);
this.loc1.Name = "loc1";
this.loc1.Size = new System.Drawing.Size(58, 20);
this.loc1.TabIndex = 4;
this.loc1.Text = "Locate";
this.loc1.UseVisualStyleBackColor = true;
//
// con4
//
this.con4.BackgroundImage = global::BetterJoyForCemu.Properties.Resources.cross;
this.con4.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.con4.Enabled = false;
this.con4.Location = new System.Drawing.Point(198, 20);
this.con4.Name = "con4";
this.con4.Size = new System.Drawing.Size(58, 59);
this.con4.TabIndex = 3;
this.con4.TabStop = false;
this.con4.UseVisualStyleBackColor = true;
//
// con3
//
this.con3.BackgroundImage = global::BetterJoyForCemu.Properties.Resources.cross;
this.con3.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.con3.Enabled = false;
this.con3.Location = new System.Drawing.Point(134, 20);
this.con3.Name = "con3";
this.con3.Size = new System.Drawing.Size(58, 59);
this.con3.TabIndex = 2;
this.con3.TabStop = false;
this.con3.UseVisualStyleBackColor = true;
//
// con2
//
this.con2.BackgroundImage = global::BetterJoyForCemu.Properties.Resources.cross;
this.con2.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.con2.Enabled = false;
this.con2.Location = new System.Drawing.Point(70, 20);
this.con2.Name = "con2";
this.con2.Size = new System.Drawing.Size(58, 59);
this.con2.TabIndex = 1;
this.con2.TabStop = false;
this.con2.UseVisualStyleBackColor = true;
//
// con1
//
this.con1.BackgroundImage = global::BetterJoyForCemu.Properties.Resources.cross;
this.con1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.con1.Enabled = false;
this.con1.Location = new System.Drawing.Point(6, 20);
this.con1.Name = "con1";
this.con1.Size = new System.Drawing.Size(58, 59);
this.con1.TabIndex = 0;
this.con1.TabStop = false;
this.btnTip.SetToolTip(this.con1, "Click on Joycons to join/split them");
this.con1.UseVisualStyleBackColor = true;
//
// foldLbl
//
this.foldLbl.Location = new System.Drawing.Point(274, 20);
this.foldLbl.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0);
this.foldLbl.Name = "foldLbl";
this.foldLbl.Size = new System.Drawing.Size(13, 231);
this.foldLbl.TabIndex = 12;
this.foldLbl.Text = ">";
this.foldLbl.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.btnTip.SetToolTip(this.foldLbl, "Config");
this.foldLbl.Click += new System.EventHandler(this.foldLbl_Click);
//
// startInTrayBox
//
this.startInTrayBox.AutoSize = true;
this.startInTrayBox.Location = new System.Drawing.Point(107, 238);
this.startInTrayBox.Name = "startInTrayBox";
this.startInTrayBox.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
this.startInTrayBox.Size = new System.Drawing.Size(83, 17);
this.startInTrayBox.TabIndex = 6;
this.startInTrayBox.Text = "Start in Tray";
this.startInTrayBox.UseVisualStyleBackColor = true;
this.startInTrayBox.CheckedChanged += new System.EventHandler(this.startInTrayBox_CheckedChanged);
//
// btn_open3rdP
//
this.btn_open3rdP.Location = new System.Drawing.Point(93, 112);
this.btn_open3rdP.Name = "btn_open3rdP";
this.btn_open3rdP.Size = new System.Drawing.Size(86, 20);
this.btn_open3rdP.TabIndex = 7;
this.btn_open3rdP.Text = "Add Controllers";
this.btn_open3rdP.UseVisualStyleBackColor = true;
this.btn_open3rdP.Click += new System.EventHandler(this.btn_open3rdP_Click);
//
// groupBox1
//
this.groupBox1.Controls.Add(this.settingsTable);
this.groupBox1.Location = new System.Drawing.Point(3, 11);
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.TabIndex = 9;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Config";
//
// settingsTable
//
this.settingsTable.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.settingsTable.AutoScroll = true;
this.settingsTable.ColumnCount = 2;
this.settingsTable.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 58.90411F));
this.settingsTable.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 41.09589F));
this.settingsTable.Location = new System.Drawing.Point(4, 17);
this.settingsTable.Margin = new System.Windows.Forms.Padding(2);
this.settingsTable.Name = "settingsTable";
this.settingsTable.RowCount = 1;
this.settingsTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.settingsTable.Size = new System.Drawing.Size(219, 219);
this.settingsTable.TabIndex = 1;
//
// rightPanel
//
this.rightPanel.Controls.Add(this.settingsApply);
this.rightPanel.Controls.Add(this.groupBox1);
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(231, 273);
this.rightPanel.TabIndex = 11;
this.rightPanel.Visible = false;
//
// settingsApply
//
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(165, 252);
this.settingsApply.Margin = new System.Windows.Forms.Padding(2);
this.settingsApply.Name = "settingsApply";
this.settingsApply.Size = new System.Drawing.Size(61, 21);
this.settingsApply.TabIndex = 10;
this.settingsApply.Text = "Apply";
this.settingsApply.UseVisualStyleBackColor = true;
this.settingsApply.Click += new System.EventHandler(this.settingsApply_Click);
//
// AutoCalibrate
//
this.AutoCalibrate.Location = new System.Drawing.Point(203, 112);
this.AutoCalibrate.Name = "AutoCalibrate";
this.AutoCalibrate.Size = new System.Drawing.Size(71, 20);
this.AutoCalibrate.TabIndex = 8;
this.AutoCalibrate.Text = "Calibrate";
this.AutoCalibrate.UseVisualStyleBackColor = true;
this.AutoCalibrate.Click += new System.EventHandler(this.StartCalibrate);
//
// btn_reassign_open
//
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;
this.btn_reassign_open.Text = "Map Buttons";
this.btn_reassign_open.UseVisualStyleBackColor = true;
this.btn_reassign_open.Click += new System.EventHandler(this.btn_reassign_open_Click);
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true;
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
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);
this.Controls.Add(this.AutoCalibrate);
this.Controls.Add(this.btn_open3rdP);
this.Controls.Add(this.startInTrayBox);
this.Controls.Add(this.conCntrls);
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")));
this.MaximizeBox = false;
this.Name = "MainForm";
this.Text = "BetterJoy";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
this.Load += new System.EventHandler(this.MainForm_Load);
this.Resize += new System.EventHandler(this.MainForm_Resize);
this.contextMenu.ResumeLayout(false);
this.conCntrls.ResumeLayout(false);
this.groupBox1.ResumeLayout(false);
this.rightPanel.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
this.settingsTable.AutoScroll = true;
this.settingsTable.ColumnCount = 2;
this.settingsTable.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 58.90411F));
this.settingsTable.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 41.09589F));
this.settingsTable.Location = new System.Drawing.Point(4, 17);
this.settingsTable.Margin = new System.Windows.Forms.Padding(2);
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.TabIndex = 1;
//
// rightPanel
//
this.rightPanel.Controls.Add(this.settingsApply);
this.rightPanel.Controls.Add(this.groupBox1);
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.TabIndex = 11;
this.rightPanel.Visible = false;
//
// settingsApply
//
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.Margin = new System.Windows.Forms.Padding(2);
this.settingsApply.Name = "settingsApply";
this.settingsApply.Size = new System.Drawing.Size(61, 21);
this.settingsApply.TabIndex = 10;
this.settingsApply.Text = "Apply";
this.settingsApply.UseVisualStyleBackColor = true;
this.settingsApply.Click += new System.EventHandler(this.settingsApply_Click);
//
// AutoCalibrate
//
this.AutoCalibrate.Location = new System.Drawing.Point(203, 112);
this.AutoCalibrate.Name = "AutoCalibrate";
this.AutoCalibrate.Size = new System.Drawing.Size(71, 20);
this.AutoCalibrate.TabIndex = 8;
this.AutoCalibrate.Text = "Calibrate";
this.AutoCalibrate.UseVisualStyleBackColor = true;
this.AutoCalibrate.Click += new System.EventHandler(this.StartCalibrate);
//
// btn_reassign_open
//
this.btn_reassign_open.Location = new System.Drawing.Point(12, 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;
this.btn_reassign_open.Text = "Map Buttons";
this.btn_reassign_open.UseVisualStyleBackColor = true;
this.btn_reassign_open.Click += new System.EventHandler(this.btn_reassign_open_Click);
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
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.Controls.Add(this.btn_reassign_open);
this.Controls.Add(this.foldLbl);
this.Controls.Add(this.rightPanel);
this.Controls.Add(this.AutoCalibrate);
this.Controls.Add(this.btn_open3rdP);
this.Controls.Add(this.startInTrayBox);
this.Controls.Add(this.conCntrls);
this.Controls.Add(this.donationLink);
this.Controls.Add(this.passiveScanBox);
this.Controls.Add(this.version_lbl);
this.Controls.Add(this.console);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.Name = "MainForm";
this.Text = "BetterJoy";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
this.Load += new System.EventHandler(this.MainForm_Load);
this.Resize += new System.EventHandler(this.MainForm_Resize);
this.contextMenu.ResumeLayout(false);
this.conCntrls.ResumeLayout(false);
this.groupBox1.ResumeLayout(false);
this.rightPanel.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
}
#endregion
#endregion
public System.Windows.Forms.TextBox console;
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;
public System.Windows.Forms.TextBox console;
public System.Windows.Forms.NotifyIcon notifyIcon;
private System.Windows.Forms.Label version_lbl;
private System.Windows.Forms.ContextMenuStrip contextMenu;
private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
private System.Windows.Forms.CheckBox passiveScanBox;
private System.Windows.Forms.LinkLabel donationLink;
private System.Windows.Forms.GroupBox conCntrls;
@ -409,6 +396,6 @@
private System.Windows.Forms.Button settingsApply;
private System.Windows.Forms.Label foldLbl;
private System.Windows.Forms.Button AutoCalibrate;
private System.Windows.Forms.Button btn_reassign_open;
}
}
private System.Windows.Forms.Button btn_reassign_open;
}
}

View file

@ -14,423 +14,421 @@ using System.Windows.Forms;
using System.Xml.Linq;
namespace BetterJoyForCemu {
public partial class MainForm : Form {
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 partial class MainForm : Form {
public bool allowCalibration = Boolean.Parse(ConfigurationManager.AppSettings["AllowCalibration"]);
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 MainForm() {
xG = new List<int>(); yG = new List<int>(); zG = new List<int>();
xA = new List<int>(); yA = new List<int>(); zA = new List<int>();
caliData = new List<KeyValuePair<string, float[]>> {
new KeyValuePair<string, float[]>("0", new float[6] {0,0,0,-710,0,0})
};
public enum NonOriginalController : int {
Disabled = 0,
DefaultCalibration = 1,
ControllerCalibration = 2,
}
InitializeComponent();
public MainForm() {
xG = new List<int>(); yG = new List<int>(); zG = new List<int>();
xA = new List<int>(); yA = new List<int>(); zA = new List<int>();
caliData = new List<KeyValuePair<string, float[]>> {
new KeyValuePair<string, float[]>("0", new float[6] {0,0,0,-710,0,0})
};
if (!nonOriginal)
AutoCalibrate.Hide();
InitializeComponent();
// Feature not yet implemented - hide
btn_open3rdP.Hide();
if (!allowCalibration)
AutoCalibrate.Hide();
con = new List<Button> { con1, con2, con3, con4 };
loc = new List<Button> { loc1, loc2, loc3, loc4 };
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(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);
//list all options
string[] myConfigs = ConfigurationManager.AppSettings.AllKeys;
Size childSize = new Size(150, 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);
var value = ConfigurationManager.AppSettings[myConfigs[i]];
Control childControl;
if (value == "true" || value == "false") {
childControl = new CheckBox() { Checked = Boolean.Parse(value), Size = childSize };
} else {
childControl = new TextBox() { Text = value, Size = childSize };
}
var value = ConfigurationManager.AppSettings[myConfigs[i]];
Control childControl;
if (value == "true" || value == "false") {
childControl = new CheckBox() { Checked = Boolean.Parse(value), Size = childSize };
} else {
childControl = new TextBox() { Text = value, Size = childSize };
}
childControl.MouseClick += cbBox_Changed;
settingsTable.Controls.Add(childControl, 1, i);
}
}
childControl.MouseClick += cbBox_Changed;
settingsTable.Controls.Add(childControl, 1, i);
}
}
private void HideToTray() {
this.WindowState = FormWindowState.Minimized;
notifyIcon.Visible = true;
notifyIcon.ShowBalloonTip(1);
this.ShowInTaskbar = false;
this.Hide();
}
private void HideToTray() {
this.WindowState = FormWindowState.Minimized;
notifyIcon.Visible = true;
notifyIcon.BalloonTipText = "Double click the tray icon to maximise!";
notifyIcon.ShowBalloonTip(0);
this.ShowInTaskbar = false;
this.Hide();
}
private void ShowFromTray() {
this.Show();
this.WindowState = FormWindowState.Normal;
this.ShowInTaskbar = true;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.Icon = Properties.Resources.betterjoyforcemu_icon;
notifyIcon.Visible = false;
}
private void ShowFromTray() {
this.Show();
this.WindowState = FormWindowState.Normal;
this.ShowInTaskbar = true;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.Icon = Properties.Resources.betterjoyforcemu_icon;
notifyIcon.Visible = false;
}
private void MainForm_Resize(object sender, EventArgs e) {
if (this.WindowState == FormWindowState.Minimized) {
HideToTray();
}
}
private void MainForm_Resize(object sender, EventArgs e) {
if (this.WindowState == FormWindowState.Minimized) {
HideToTray();
}
}
private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e) {
ShowFromTray();
}
private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e) {
ShowFromTray();
}
private void MainForm_Load(object sender, EventArgs e) {
Config.Init(caliData);
private void MainForm_Load(object sender, EventArgs e) {
Config.Init(caliData);
Program.Start();
Program.Start();
passiveScanBox.Checked = Config.IntValue("ProgressiveScan") == 1;
startInTrayBox.Checked = Config.IntValue("StartInTray") == 1;
passiveScanBox.Checked = Config.IntValue("ProgressiveScan") == 1;
startInTrayBox.Checked = Config.IntValue("StartInTray") == 1;
if (Config.IntValue("StartInTray") == 1) {
HideToTray();
} else {
ShowFromTray();
}
}
if (Config.IntValue("StartInTray") == 1) {
HideToTray();
} else {
ShowFromTray();
}
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e) {
try {
Program.Stop();
Environment.Exit(0);
} catch { }
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e) {
try {
Program.Stop();
Environment.Exit(0);
} catch { }
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e) { // this does not work, for some reason. Fix before release
try {
Program.Stop();
Close();
Environment.Exit(0);
} catch { }
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e) { // this does not work, for some reason. Fix before release
try {
Program.Stop();
Close();
Environment.Exit(0);
} catch { }
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
donationLink.LinkVisited = true;
System.Diagnostics.Process.Start("http://paypal.me/DavidKhachaturov/5");
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
donationLink.LinkVisited = true;
System.Diagnostics.Process.Start("http://paypal.me/DavidKhachaturov/5");
}
private void passiveScanBox_CheckedChanged(object sender, EventArgs e) {
Config.SetValue("ProgressiveScan", passiveScanBox.Checked ? "1" : "0");
Config.Save();
}
private void passiveScanBox_CheckedChanged(object sender, EventArgs e) {
Config.SetValue("ProgressiveScan", passiveScanBox.Checked ? "1" : "0");
Config.Save();
}
public void AppendTextBox(string value) { // https://stackoverflow.com/questions/519233/writing-to-a-textbox-from-another-thread
if (InvokeRequired) {
this.Invoke(new Action<string>(AppendTextBox), new object[] { value });
return;
}
console.AppendText(value);
}
public void AppendTextBox(string value) { // https://stackoverflow.com/questions/519233/writing-to-a-textbox-from-another-thread
if (InvokeRequired) {
this.Invoke(new Action<string>(AppendTextBox), new object[] { value });
return;
}
console.AppendText(value);
}
bool toRumble = Boolean.Parse(ConfigurationManager.AppSettings["EnableRumble"]);
bool showAsXInput = Boolean.Parse(ConfigurationManager.AppSettings["ShowAsXInput"]);
bool showAsDS4 = Boolean.Parse(ConfigurationManager.AppSettings["ShowAsDS4"]);
bool toRumble = Boolean.Parse(ConfigurationManager.AppSettings["EnableRumble"]);
bool showAsXInput = Boolean.Parse(ConfigurationManager.AppSettings["ShowAsXInput"]);
bool showAsDS4 = Boolean.Parse(ConfigurationManager.AppSettings["ShowAsDS4"]);
public void locBtnClick(object sender, EventArgs e) {
Button bb = sender as Button;
public async void locBtnClickAsync(object sender, EventArgs e) {
Button bb = sender as Button;
if (bb.Tag.GetType() == typeof(Button)) {
Button button = bb.Tag as Button;
if (bb.Tag.GetType() == typeof(Button)) {
Button button = bb.Tag as Button;
if (button.Tag.GetType() == typeof(Joycon)) {
Joycon v = (Joycon)button.Tag;
v.SetRumble(20.0f, 400.0f, 1.0f, 300);
}
}
}
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);
}
}
}
public void conBtnClick(object sender, EventArgs e) {
Button button = sender as Button;
bool doNotRejoin = Boolean.Parse(ConfigurationManager.AppSettings["DoNotRejoinJoycons"]);
if (button.Tag.GetType() == typeof(Joycon)) {
Joycon v = (Joycon)button.Tag;
public void conBtnClick(object sender, EventArgs e) {
Button button = sender as Button;
if (v.other == null && !v.isPro) { // needs connecting to other joycon (so messy omg)
bool succ = false;
if (button.Tag.GetType() == typeof(Joycon)) {
Joycon v = (Joycon)button.Tag;
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 {
foreach (Joycon jc in Program.mgr.j) {
if (!jc.isPro && jc.isLeft != v.isLeft && jc != v && jc.other == null) {
v.other = jc;
jc.other = v;
if (v.other == null && !v.isPro) { // needs connecting to other joycon (so messy omg)
bool succ = false;
//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 (Program.mgr.j.Count == 1 || doNotRejoin) { // 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 {
foreach (Joycon jc in Program.mgr.j) {
if (!jc.isPro && jc.isLeft != v.isLeft && jc != v && jc.other == null) {
v.other = jc;
jc.other = v;
if (v.out_xbox != null) {
v.out_xbox.Disconnect();
v.out_xbox = null;
}
if (v.out_xbox != null) {
v.out_xbox.Disconnect();
v.out_xbox = null;
}
if (v.out_ds4 != null) {
v.out_ds4.Disconnect();
v.out_ds4 = null;
}
if (v.out_ds4 != null) {
v.out_ds4.Disconnect();
v.out_ds4 = null;
}
// setting the other joycon's button image
foreach (Button b in con)
if (b.Tag == jc)
b.BackgroundImage = jc.isLeft ? Properties.Resources.jc_left : Properties.Resources.jc_right;
// setting the other joycon's button image
foreach (Button b in con)
if (b.Tag == jc)
b.BackgroundImage = jc.isLeft ? Properties.Resources.jc_left : Properties.Resources.jc_right;
succ = true;
break;
}
}
}
succ = true;
break;
}
}
}
if (succ)
foreach (Button b in con)
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 (succ)
foreach (Button b in con)
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);
button.BackgroundImage = v.isLeft ? Properties.Resources.jc_left_s : Properties.Resources.jc_right_s;
button.BackgroundImage = v.isLeft ? Properties.Resources.jc_left_s : Properties.Resources.jc_right_s;
foreach (Button b in con)
if (b.Tag == v.other)
b.BackgroundImage = v.other.isLeft ? Properties.Resources.jc_left_s : Properties.Resources.jc_right_s;
foreach (Button b in con)
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;
}
}
}
v.other.other = null;
v.other = null;
}
}
}
private void startInTrayBox_CheckedChanged(object sender, EventArgs e) {
Config.SetValue("StartInTray", startInTrayBox.Checked ? "1" : "0");
Config.Save();
}
private void startInTrayBox_CheckedChanged(object sender, EventArgs e) {
Config.SetValue("StartInTray", startInTrayBox.Checked ? "1" : "0");
Config.Save();
}
private void btn_open3rdP_Click(object sender, EventArgs e) {
_3rdPartyControllers partyForm = new _3rdPartyControllers();
partyForm.ShowDialog();
}
private void btn_open3rdP_Click(object sender, EventArgs e) {
_3rdPartyControllers partyForm = new _3rdPartyControllers();
partyForm.ShowDialog();
}
private void settingsApply_Click(object sender, EventArgs e) {
var configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var settings = configFile.AppSettings.Settings;
private void settingsApply_Click(object sender, EventArgs e) {
var configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var settings = configFile.AppSettings.Settings;
for (int row = 0; row < ConfigurationManager.AppSettings.AllKeys.Length; row++) {
var valCtl = settingsTable.GetControlFromPosition(1, row);
var KeyCtl = settingsTable.GetControlFromPosition(0, row).Text;
for (int row = 0; row < ConfigurationManager.AppSettings.AllKeys.Length; row++) {
var valCtl = settingsTable.GetControlFromPosition(1, row);
var KeyCtl = settingsTable.GetControlFromPosition(0, row).Text;
if (valCtl.GetType() == typeof(CheckBox) && settings[KeyCtl] != null) {
settings[KeyCtl].Value = ((CheckBox)valCtl).Checked.ToString().ToLower();
} else if (valCtl.GetType() == typeof(TextBox) && settings[KeyCtl] != null) {
settings[KeyCtl].Value = ((TextBox)valCtl).Text.ToLower();
}
}
if (valCtl.GetType() == typeof(CheckBox) && settings[KeyCtl] != null) {
settings[KeyCtl].Value = ((CheckBox)valCtl).Checked.ToString().ToLower();
} else if (valCtl.GetType() == typeof(TextBox) && settings[KeyCtl] != null) {
settings[KeyCtl].Value = ((TextBox)valCtl).Text.ToLower();
}
}
try {
configFile.Save(ConfigurationSaveMode.Modified);
} catch (ConfigurationErrorsException) {
AppendTextBox("Error writing app settings.\r\n");
}
try {
configFile.Save(ConfigurationSaveMode.Modified);
} catch (ConfigurationErrorsException) {
AppendTextBox("Error writing app settings.\r\n");
}
ConfigurationManager.AppSettings["AutoPowerOff"] = "false"; // Prevent joycons poweroff when applying settings
Application.Restart();
Environment.Exit(0);
}
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 ReenableViGEm(Joycon v) {
if (showAsXInput && v.out_xbox == null) {
v.out_xbox = new Controller.OutputControllerXbox360();
if (toRumble)
v.out_xbox.FeedbackReceived += v.ReceiveRumble;
v.out_xbox.Connect();
}
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 (showAsDS4 && v.out_ds4 == null) {
v.out_ds4 = new Controller.OutputControllerDualShock4();
if (toRumble)
v.out_ds4.FeedbackReceived += v.Ds4_FeedbackReceived;
v.out_ds4.Connect();
}
}
if (toRumble)
v.out_ds4.FeedbackReceived += v.Ds4_FeedbackReceived;
v.out_ds4.Connect();
}
}
private void foldLbl_Click(object sender, EventArgs e) {
rightPanel.Visible = !rightPanel.Visible;
foldLbl.Text = rightPanel.Visible ? "<" : ">";
}
private void foldLbl_Click(object sender, EventArgs e) {
rightPanel.Visible = !rightPanel.Visible;
foldLbl.Text = rightPanel.Visible ? "<" : ">";
}
private void cbBox_Changed(object sender, EventArgs e) {
var coord = settingsTable.GetPositionFromControl(sender as Control);
private void cbBox_Changed(object sender, EventArgs e) {
var coord = settingsTable.GetPositionFromControl(sender as Control);
var valCtl = settingsTable.GetControlFromPosition(coord.Column, coord.Row);
var KeyCtl = settingsTable.GetControlFromPosition(coord.Column - 1, coord.Row).Text;
var valCtl = settingsTable.GetControlFromPosition(coord.Column, coord.Row);
var KeyCtl = settingsTable.GetControlFromPosition(coord.Column - 1, coord.Row).Text;
try {
var configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var settings = configFile.AppSettings.Settings;
if (valCtl.GetType() == typeof(CheckBox) && settings[KeyCtl] != null) {
settings[KeyCtl].Value = ((CheckBox)valCtl).Checked.ToString().ToLower();
} else if (valCtl.GetType() == typeof(TextBox) && settings[KeyCtl] != null) {
settings[KeyCtl].Value = ((TextBox)valCtl).Text.ToLower();
}
try {
var configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var settings = configFile.AppSettings.Settings;
if (valCtl.GetType() == typeof(CheckBox) && settings[KeyCtl] != null) {
settings[KeyCtl].Value = ((CheckBox)valCtl).Checked.ToString().ToLower();
} else if (valCtl.GetType() == typeof(TextBox) && settings[KeyCtl] != null) {
settings[KeyCtl].Value = ((TextBox)valCtl).Text.ToLower();
}
if (KeyCtl == "HomeLEDOn") {
bool on = settings[KeyCtl].Value.ToLower() == "true";
foreach (Joycon j in Program.mgr.j) {
j.SetHomeLight(on);
}
}
if (KeyCtl == "HomeLEDOn") {
bool on = settings[KeyCtl].Value.ToLower() == "true";
foreach (Joycon j in Program.mgr.j) {
j.SetHomeLight(on);
}
}
configFile.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(configFile.AppSettings.SectionInformation.Name);
} catch (ConfigurationErrorsException) {
AppendTextBox("Error writing app settings\r\n");
Trace.WriteLine(String.Format("rw {0}, column {1}, {2}, {3}", coord.Row, coord.Column, sender.GetType(), KeyCtl));
}
}
private void StartCalibrate(object sender, EventArgs e) {
if (Program.mgr.j.Count == 0) {
this.console.Text = "Please connect a single pro controller.";
return;
}
if (Program.mgr.j.Count > 1) {
this.console.Text = "Please calibrate one controller at a time (disconnect others).";
return;
}
this.AutoCalibrate.Enabled = false;
countDown = new Timer();
this.count = 4;
this.CountDown(null, null);
countDown.Tick += new EventHandler(CountDown);
countDown.Interval = 1000;
countDown.Enabled = true;
}
configFile.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(configFile.AppSettings.SectionInformation.Name);
} catch (ConfigurationErrorsException) {
AppendTextBox("Error writing app settings\r\n");
Trace.WriteLine(String.Format("rw {0}, column {1}, {2}, {3}", coord.Row, coord.Column, sender.GetType(), KeyCtl));
}
}
private void StartCalibrate(object sender, EventArgs e) {
if (Program.mgr.j.Count == 0) {
this.console.Text = "Please connect a single pro controller.";
return;
}
if (Program.mgr.j.Count > 1) {
this.console.Text = "Please calibrate one controller at a time (disconnect others).";
return;
}
this.AutoCalibrate.Enabled = false;
countDown = new Timer();
this.count = 4;
this.CountDown(null, null);
countDown.Tick += new EventHandler(CountDown);
countDown.Interval = 1000;
countDown.Enabled = true;
}
private void StartGetData() {
this.xG.Clear(); this.yG.Clear(); this.zG.Clear();
this.xA.Clear(); this.yA.Clear(); this.zA.Clear();
countDown = new Timer();
this.count = 3;
this.calibrate = true;
countDown.Tick += new EventHandler(CalcData);
countDown.Interval = 1000;
countDown.Enabled = true;
}
private void StartGetData() {
this.xG.Clear(); this.yG.Clear(); this.zG.Clear();
this.xA.Clear(); this.yA.Clear(); this.zA.Clear();
countDown = new Timer();
this.count = 3;
this.calibrate = true;
countDown.Tick += new EventHandler(CalcData);
countDown.Interval = 1000;
countDown.Enabled = true;
}
private void btn_reassign_open_Click(object sender, EventArgs e) {
Reassign mapForm = new Reassign();
mapForm.ShowDialog();
}
private void btn_reassign_open_Click(object sender, EventArgs e) {
Reassign mapForm = new Reassign();
mapForm.ShowDialog();
}
private void CountDown(object sender, EventArgs e) {
if (this.count == 0) {
this.console.Text = "Calibrating...";
countDown.Stop();
this.StartGetData();
} else {
this.console.Text = "Plese keep the controller flat." + "\r\n";
this.console.Text += "Calibration will start in " + this.count + " seconds.";
this.count--;
}
}
private void CalcData(object sender, EventArgs e) {
if (this.count == 0) {
countDown.Stop();
this.calibrate = false;
string serNum = Program.mgr.j.First().serial_number;
int serIndex = this.findSer(serNum);
float[] Arr = new float[6] { 0, 0, 0, 0, 0, 0 };
if (serIndex == -1) {
this.caliData.Add(new KeyValuePair<string, float[]>(
serNum,
Arr
));
} else {
Arr = this.caliData[serIndex].Value;
}
Random rnd = new Random();
Arr[0] = (float)quickselect_median(this.xG, rnd.Next);
Arr[1] = (float)quickselect_median(this.yG, rnd.Next);
Arr[2] = (float)quickselect_median(this.zG, rnd.Next);
Arr[3] = (float)quickselect_median(this.xA, rnd.Next);
Arr[4] = (float)quickselect_median(this.yA, rnd.Next);
Arr[5] = (float)quickselect_median(this.zA, rnd.Next) - 4010; //Joycon.cs acc_sen 16384
this.console.Text += "Calibration completed!!!" + "\r\n";
Config.SaveCaliData(this.caliData);
Program.mgr.j.First().getActiveData();
this.AutoCalibrate.Enabled = true;
} else {
this.count--;
}
private void CountDown(object sender, EventArgs e) {
if (this.count == 0) {
this.console.Text = "Calibrating...";
countDown.Stop();
this.StartGetData();
} else {
this.console.Text = "Plese keep the controller flat." + "\r\n";
this.console.Text += "Calibration will start in " + this.count + " seconds.";
this.count--;
}
}
private void CalcData(object sender, EventArgs e) {
if (this.count == 0) {
countDown.Stop();
this.calibrate = false;
string serNum = Program.mgr.j.First().serial_number;
int serIndex = this.findSer(serNum);
float[] Arr = new float[6] { 0, 0, 0, 0, 0, 0 };
if (serIndex == -1) {
this.caliData.Add(new KeyValuePair<string, float[]>(
serNum,
Arr
));
} else {
Arr = this.caliData[serIndex].Value;
}
Random rnd = new Random();
Arr[0] = (float)quickselect_median(this.xG, rnd.Next);
Arr[1] = (float)quickselect_median(this.yG, rnd.Next);
Arr[2] = (float)quickselect_median(this.zG, rnd.Next);
Arr[3] = (float)quickselect_median(this.xA, rnd.Next);
Arr[4] = (float)quickselect_median(this.yA, rnd.Next);
Arr[5] = (float)quickselect_median(this.zA, rnd.Next) - 4010; //Joycon.cs acc_sen 16384
this.console.Text += "Calibration completed!!!" + "\r\n";
Config.SaveCaliData(this.caliData);
Program.mgr.j.First().getActiveData();
this.AutoCalibrate.Enabled = true;
} else {
this.count--;
}
}
private double quickselect_median(List<int> l, Func<int, int> pivot_fn) {
int ll = l.Count;
if (ll % 2 == 1) {
return this.quickselect(l, ll / 2, pivot_fn);
} else {
return 0.5 * (quickselect(l, ll / 2 - 1, pivot_fn) + quickselect(l, ll / 2, pivot_fn));
}
}
}
private double quickselect_median(List<int> l, Func<int, int> pivot_fn) {
int ll = l.Count;
if (ll % 2 == 1) {
return this.quickselect(l, ll / 2, pivot_fn);
} else {
return 0.5 * (quickselect(l, ll / 2 - 1, pivot_fn) + quickselect(l, ll / 2, pivot_fn));
}
}
private int quickselect(List<int> l, int k, Func<int, int> pivot_fn) {
if (l.Count == 1 && k == 0) {
return l[0];
}
int pivot = l[pivot_fn(l.Count)];
List<int> lows = l.Where(x => x < pivot).ToList();
List<int> highs = l.Where(x => x > pivot).ToList();
List<int> pivots = l.Where(x => x == pivot).ToList();
if (k < lows.Count) {
return quickselect(lows, k, pivot_fn);
} else if (k < (lows.Count + pivots.Count)) {
return pivots[0];
} else {
return quickselect(highs, k - lows.Count - pivots.Count, pivot_fn);
}
}
private int quickselect(List<int> l, int k, Func<int, int> pivot_fn) {
if (l.Count == 1 && k == 0) {
return l[0];
}
int pivot = l[pivot_fn(l.Count)];
List<int> lows = l.Where(x => x < pivot).ToList();
List<int> highs = l.Where(x => x > pivot).ToList();
List<int> pivots = l.Where(x => x == pivot).ToList();
if (k < lows.Count) {
return quickselect(lows, k, pivot_fn);
} else if (k < (lows.Count + pivots.Count)) {
return pivots[0];
} else {
return quickselect(highs, k - lows.Count - pivots.Count, pivot_fn);
}
}
public float[] activeCaliData(string serNum) {
for (int i = 0; i < this.caliData.Count; i++) {
if (this.caliData[i].Key == serNum) {
return this.caliData[i].Value;
}
}
return this.caliData[0].Value;
}
public float[] activeCaliData(string serNum) {
for (int i = 0; i < this.caliData.Count; i++) {
if (this.caliData[i].Key == serNum) {
return this.caliData[i].Value;
}
}
return this.caliData[0].Value;
}
private int findSer(string serNum) {
for (int i = 0; i < this.caliData.Count; i++) {
if (this.caliData[i].Key == serNum) {
return i;
}
}
return -1;
}
}
private int findSer(string serNum) {
for (int i = 0; i < this.caliData.Count; i++) {
if (this.caliData[i].Key == serNum) {
return i;
}
}
return -1;
}
}
}

View file

@ -1,7 +1,9 @@
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;
@ -13,7 +15,9 @@ using System.Text;
using System.Threading;
using System.Timers;
using System.Windows.Forms;
using BetterJoyForCemu.Collections;
using Nefarius.ViGEm.Client;
using static BetterJoyForCemu._3rdPartyControllers;
using static BetterJoyForCemu.HIDapi;
namespace BetterJoyForCemu {
@ -22,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 List<Joycon> j; // Array of all connected Joy-Cons
public ConcurrentList<Joycon> j { get; private set; } // Array of all connected Joy-Cons
static JoyconManager instance;
public MainForm form;
@ -41,7 +45,7 @@ namespace BetterJoyForCemu {
public void Awake() {
instance = this;
j = new List<Joycon>();
j = new ConcurrentList<Joycon>();
HIDapi.hid_init();
}
@ -60,16 +64,16 @@ namespace BetterJoyForCemu {
void CleanUp() { // removes dropped controllers from list
List<Joycon> rem = new List<Joycon>();
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
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
v.Detach(true); rem.Add(v);
joycon.Detach(true);
rem.Add(joycon);
foreach (Button b in form.con) {
if (b.Enabled & b.Tag == v) {
if (b.Enabled & b.Tag == joycon) {
b.Invoke(new MethodInvoker(delegate {
b.BackColor = System.Drawing.Color.FromArgb(0x00, System.Drawing.SystemColors.Control);
b.Enabled = false;
@ -88,22 +92,34 @@ namespace BetterJoyForCemu {
}
void CheckForNewControllersTime(Object source, ElapsedEventArgs e) {
CleanUp();
if (Config.IntValue("ProgressiveScan") == 1) {
CheckForNewControllers();
}
}
public void CheckForNewControllers() {
CleanUp();
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() {
// move all code for initializing devices here and well as the initial code from Start()
bool isLeft = false;
IntPtr ptr = HIDapi.hid_enumerate(vendor_id, 0x0);
IntPtr ptr = HIDapi.hid_enumerate(0x0, 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) {
@ -112,14 +128,25 @@ 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;
}
bool validController = (enumerate.product_id == product_l || enumerate.product_id == product_r ||
enumerate.product_id == product_pro || enumerate.product_id == product_snes);
if (validController && !ControllerAlreadyAdded(enumerate.path)) {
switch (enumerate.product_id) {
switch (prod_id) {
case product_l:
isLeft = true;
form.AppendTextBox("Left Joy-Con connected.\r\n"); break;
@ -132,6 +159,9 @@ 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;
}
@ -166,9 +196,10 @@ namespace BetterJoyForCemu {
break;
}
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));
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));
foundNew = true;
j.Last().form = form;
@ -179,7 +210,7 @@ namespace BetterJoyForCemu {
ii++;
if (!v.Enabled) {
System.Drawing.Bitmap temp;
switch (enumerate.product_id) {
switch (prod_id) {
case (product_l):
temp = Properties.Resources.jc_left_s; break;
case (product_r):
@ -188,6 +219,8 @@ 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;
}
@ -201,7 +234,7 @@ namespace BetterJoyForCemu {
form.loc[ii].Invoke(new MethodInvoker(delegate {
form.loc[ii].Tag = v;
form.loc[ii].Click += new EventHandler(form.locBtnClick);
form.loc[ii].Click += new EventHandler(form.locBtnClickAsync);
}));
break;
@ -210,8 +243,12 @@ namespace BetterJoyForCemu {
}
byte[] mac = new byte[6];
for (int n = 0; n < 6; n++)
mac[n] = byte.Parse(enumerate.serial_number.Substring(n * 2, 2), System.Globalization.NumberStyles.HexNumber);
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);
}
@ -221,51 +258,52 @@ namespace BetterJoyForCemu {
if (foundNew) { // attempt to auto join-up joycons on connection
Joycon temp = null;
foreach (Joycon v in j) {
if (!v.isPro) {
if (temp == null)
temp = v;
else if (temp.isLeft != v.isLeft && v.other == null) {
temp.other = v;
v.other = temp;
// 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;
}
//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);
// Otherwise, iterate through and find the Joycon with the lowest
// id that has not been attached already (Does not include self)
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) {
try {
temp.out_xbox.Disconnect();
} catch (Exception e) {
// it wasn't connected in the first place, go figure
}
if (temp.out_xbox != null) {
try {
temp.out_xbox.Disconnect();
} catch (Exception e) {
// it wasn't connected in the first place, go figure
}
if (temp.out_ds4 != null) {
try {
temp.out_ds4.Disconnect();
} catch (Exception e) {
// it wasn't connected in the first place, go figure
}
}
temp.out_xbox = null;
temp.out_ds4 = null;
foreach (Button b in form.con)
if (b.Tag == v || b.Tag == temp) {
Joycon tt = (b.Tag == v) ? v : (b.Tag == temp) ? temp : v;
b.BackgroundImage = tt.isLeft ? Properties.Resources.jc_left : Properties.Resources.jc_right;
}
temp = null; // repeat
}
if (temp.out_ds4 != null) {
try {
temp.out_ds4.Disconnect();
} catch (Exception e) {
// it wasn't connected in the first place, go figure
}
}
temp.out_xbox = null;
temp.out_ds4 = null;
foreach (Button b in form.con)
if (b.Tag == v || b.Tag == temp) {
Joycon tt = (b.Tag == v) ? v : (b.Tag == temp) ? temp : v;
b.BackgroundImage = tt.isLeft ? Properties.Resources.jc_left : Properties.Resources.jc_right;
}
temp = null; // repeat
}
}
}
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)
@ -273,33 +311,29 @@ namespace BetterJoyForCemu {
if (jc.out_ds4 != null)
jc.out_ds4.Connect();
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);
try {
jc.Attach();
} catch (Exception e) {
jc.state = Joycon.state_.DROPPED;
continue;
}
jc.SetHomeLight(on);
jc.Begin();
if (form.nonOriginal) {
if (form.allowCalibration) {
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();
else
v.Detach();
v.Detach();
if (v.out_xbox != null) {
v.out_xbox.Disconnect();
@ -315,62 +349,23 @@ 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;
@ -431,6 +426,10 @@ namespace BetterJoyForCemu {
}
}
// a bit hacky
_3rdPartyControllers partyForm = new _3rdPartyControllers();
partyForm.CopyCustomControllers();
mgr = new JoyconManager();
mgr.form = form;
mgr.Awake();
@ -441,8 +440,6 @@ 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();
@ -516,12 +513,18 @@ namespace BetterJoyForCemu {
keyboard.Dispose(); mouse.Dispose();
server.Stop();
timer.Stop();
mgr.OnApplicationQuit();
}
private static string appGuid = "04450797-3520-462e-a563-107677a483d8"; // randomly-generated
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");
@ -534,5 +537,19 @@ namespace BetterJoyForCemu {
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("BetterJoyForCemu")]
[assembly: AssemblyTitle("BetterJoy")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BetterJoyForCemu")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyProduct("BetterJoy")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

View file

@ -139,5 +139,15 @@ 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,6 +121,9 @@
<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

@ -79,226 +79,248 @@ namespace BetterJoyForCemu {
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Reassign));
this.btn_capture = new BetterJoyForCemu.SplitButton();
this.lbl_capture = new System.Windows.Forms.Label();
this.lbl_home = new System.Windows.Forms.Label();
this.btn_home = new BetterJoyForCemu.SplitButton();
this.lbl_sl_l = new System.Windows.Forms.Label();
this.btn_sl_l = new BetterJoyForCemu.SplitButton();
this.lbl_sr_l = new System.Windows.Forms.Label();
this.btn_sr_l = new BetterJoyForCemu.SplitButton();
this.lbl_sl_r = new System.Windows.Forms.Label();
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 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 SplitButton();
this.SuspendLayout();
//
// btn_capture
//
this.btn_capture.Location = new System.Drawing.Point(105, 12);
this.btn_capture.Name = "btn_capture";
this.btn_capture.Size = new System.Drawing.Size(75, 23);
this.btn_capture.TabIndex = 0;
this.btn_capture.UseVisualStyleBackColor = true;
//
// lbl_capture
//
this.lbl_capture.AutoSize = true;
this.lbl_capture.Location = new System.Drawing.Point(15, 17);
this.lbl_capture.Name = "lbl_capture";
this.lbl_capture.Size = new System.Drawing.Size(44, 13);
this.lbl_capture.TabIndex = 2;
this.lbl_capture.Text = "Capture";
this.lbl_capture.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// lbl_home
//
this.lbl_home.AutoSize = true;
this.lbl_home.Location = new System.Drawing.Point(15, 46);
this.lbl_home.Name = "lbl_home";
this.lbl_home.Size = new System.Drawing.Size(35, 13);
this.lbl_home.TabIndex = 4;
this.lbl_home.Text = "Home";
this.lbl_home.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_home
//
this.btn_home.Location = new System.Drawing.Point(105, 41);
this.btn_home.Name = "btn_home";
this.btn_home.Size = new System.Drawing.Size(75, 23);
this.btn_home.TabIndex = 3;
this.btn_home.UseVisualStyleBackColor = true;
//
// lbl_sl_l
//
this.lbl_sl_l.AutoSize = true;
this.lbl_sl_l.Location = new System.Drawing.Point(15, 75);
this.lbl_sl_l.Name = "lbl_sl_l";
this.lbl_sl_l.Size = new System.Drawing.Size(78, 13);
this.lbl_sl_l.TabIndex = 6;
this.lbl_sl_l.Text = "SL Left Joycon";
this.lbl_sl_l.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_sl_l
//
this.btn_sl_l.Location = new System.Drawing.Point(105, 70);
this.btn_sl_l.Name = "btn_sl_l";
this.btn_sl_l.Size = new System.Drawing.Size(75, 23);
this.btn_sl_l.TabIndex = 5;
this.btn_sl_l.UseVisualStyleBackColor = true;
//
// lbl_sr_l
//
this.lbl_sr_l.AutoSize = true;
this.lbl_sr_l.Location = new System.Drawing.Point(15, 104);
this.lbl_sr_l.Name = "lbl_sr_l";
this.lbl_sr_l.Size = new System.Drawing.Size(80, 13);
this.lbl_sr_l.TabIndex = 8;
this.lbl_sr_l.Text = "SR Left Joycon";
this.lbl_sr_l.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_sr_l
//
this.btn_sr_l.Location = new System.Drawing.Point(105, 99);
this.btn_sr_l.Name = "btn_sr_l";
this.btn_sr_l.Size = new System.Drawing.Size(75, 23);
this.btn_sr_l.TabIndex = 7;
this.btn_sr_l.UseVisualStyleBackColor = true;
//
// lbl_sl_r
//
this.lbl_sl_r.AutoSize = true;
this.lbl_sl_r.Location = new System.Drawing.Point(15, 133);
this.lbl_sl_r.Name = "lbl_sl_r";
this.lbl_sl_r.Size = new System.Drawing.Size(85, 13);
this.lbl_sl_r.TabIndex = 10;
this.lbl_sl_r.Text = "SL Right Joycon";
this.lbl_sl_r.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_sl_r
//
this.btn_sl_r.Location = new System.Drawing.Point(105, 128);
this.btn_sl_r.Name = "btn_sl_r";
this.btn_sl_r.Size = new System.Drawing.Size(75, 23);
this.btn_sl_r.TabIndex = 9;
this.btn_sl_r.UseVisualStyleBackColor = true;
//
// lbl_sr_r
//
this.lbl_sr_r.AutoSize = true;
this.lbl_sr_r.Location = new System.Drawing.Point(15, 162);
this.lbl_sr_r.Name = "lbl_sr_r";
this.lbl_sr_r.Size = new System.Drawing.Size(87, 13);
this.lbl_sr_r.TabIndex = 12;
this.lbl_sr_r.Text = "SR Right Joycon";
this.lbl_sr_r.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_sr_r
//
this.btn_sr_r.Location = new System.Drawing.Point(105, 157);
this.btn_sr_r.Name = "btn_sr_r";
this.btn_sr_r.Size = new System.Drawing.Size(75, 23);
this.btn_sr_r.TabIndex = 11;
this.btn_sr_r.UseVisualStyleBackColor = true;
//
// btn_close
//
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;
this.btn_close.Text = "Okay";
this.btn_close.UseVisualStyleBackColor = true;
this.btn_close.Click += new System.EventHandler(this.btn_close_Click);
//
// btn_apply
//
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;
this.btn_apply.Text = "Apply";
this.btn_apply.UseVisualStyleBackColor = true;
this.btn_apply.Click += new System.EventHandler(this.btn_apply_Click);
//
// lbl_reset_mouse
//
this.lbl_reset_mouse.AutoSize = true;
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;
this.lbl_reset_mouse.Text = "Re-Centre Gyro";
this.lbl_reset_mouse.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_reset_mouse
//
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;
this.btn_reset_mouse.UseVisualStyleBackColor = true;
//
// lbl_activate_gyro
//
this.lbl_activate_gyro.AutoSize = true;
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;
this.lbl_activate_gyro.Text = "Activate Gyro";
this.lbl_activate_gyro.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_active_gyro
//
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;
//
// Reassign
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
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);
this.Controls.Add(this.btn_reset_mouse);
this.Controls.Add(this.btn_apply);
this.Controls.Add(this.btn_close);
this.Controls.Add(this.lbl_sr_r);
this.Controls.Add(this.btn_sr_r);
this.Controls.Add(this.lbl_sl_r);
this.Controls.Add(this.btn_sl_r);
this.Controls.Add(this.lbl_sr_l);
this.Controls.Add(this.btn_sr_l);
this.Controls.Add(this.lbl_sl_l);
this.Controls.Add(this.btn_sl_l);
this.Controls.Add(this.lbl_home);
this.Controls.Add(this.btn_home);
this.Controls.Add(this.lbl_capture);
this.Controls.Add(this.btn_capture);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Reassign";
this.Text = "Map Special Buttons";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Reassign_FormClosing);
this.Load += new System.EventHandler(this.Reassign_Load);
this.ResumeLayout(false);
this.PerformLayout();
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Reassign));
this.btn_capture = new BetterJoyForCemu.SplitButton();
this.lbl_capture = new System.Windows.Forms.Label();
this.lbl_home = new System.Windows.Forms.Label();
this.btn_home = new BetterJoyForCemu.SplitButton();
this.lbl_sl_l = new System.Windows.Forms.Label();
this.btn_sl_l = new BetterJoyForCemu.SplitButton();
this.lbl_sr_l = new System.Windows.Forms.Label();
this.btn_sr_l = new BetterJoyForCemu.SplitButton();
this.lbl_sl_r = new System.Windows.Forms.Label();
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.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.SuspendLayout();
//
// btn_capture
//
this.btn_capture.Location = new System.Drawing.Point(105, 12);
this.btn_capture.Name = "btn_capture";
this.btn_capture.Size = new System.Drawing.Size(75, 23);
this.btn_capture.TabIndex = 0;
this.btn_capture.UseVisualStyleBackColor = true;
//
// lbl_capture
//
this.lbl_capture.AutoSize = true;
this.lbl_capture.Location = new System.Drawing.Point(15, 17);
this.lbl_capture.Name = "lbl_capture";
this.lbl_capture.Size = new System.Drawing.Size(44, 13);
this.lbl_capture.TabIndex = 2;
this.lbl_capture.Text = "Capture";
this.lbl_capture.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// lbl_home
//
this.lbl_home.AutoSize = true;
this.lbl_home.Location = new System.Drawing.Point(15, 46);
this.lbl_home.Name = "lbl_home";
this.lbl_home.Size = new System.Drawing.Size(35, 13);
this.lbl_home.TabIndex = 4;
this.lbl_home.Text = "Home";
this.lbl_home.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_home
//
this.btn_home.Location = new System.Drawing.Point(105, 41);
this.btn_home.Name = "btn_home";
this.btn_home.Size = new System.Drawing.Size(75, 23);
this.btn_home.TabIndex = 3;
this.btn_home.UseVisualStyleBackColor = true;
//
// lbl_sl_l
//
this.lbl_sl_l.AutoSize = true;
this.lbl_sl_l.Location = new System.Drawing.Point(15, 75);
this.lbl_sl_l.Name = "lbl_sl_l";
this.lbl_sl_l.Size = new System.Drawing.Size(78, 13);
this.lbl_sl_l.TabIndex = 6;
this.lbl_sl_l.Text = "SL Left Joycon";
this.lbl_sl_l.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_sl_l
//
this.btn_sl_l.Location = new System.Drawing.Point(105, 70);
this.btn_sl_l.Name = "btn_sl_l";
this.btn_sl_l.Size = new System.Drawing.Size(75, 23);
this.btn_sl_l.TabIndex = 5;
this.btn_sl_l.UseVisualStyleBackColor = true;
//
// lbl_sr_l
//
this.lbl_sr_l.AutoSize = true;
this.lbl_sr_l.Location = new System.Drawing.Point(15, 104);
this.lbl_sr_l.Name = "lbl_sr_l";
this.lbl_sr_l.Size = new System.Drawing.Size(80, 13);
this.lbl_sr_l.TabIndex = 8;
this.lbl_sr_l.Text = "SR Left Joycon";
this.lbl_sr_l.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_sr_l
//
this.btn_sr_l.Location = new System.Drawing.Point(105, 99);
this.btn_sr_l.Name = "btn_sr_l";
this.btn_sr_l.Size = new System.Drawing.Size(75, 23);
this.btn_sr_l.TabIndex = 7;
this.btn_sr_l.UseVisualStyleBackColor = true;
//
// lbl_sl_r
//
this.lbl_sl_r.AutoSize = true;
this.lbl_sl_r.Location = new System.Drawing.Point(15, 133);
this.lbl_sl_r.Name = "lbl_sl_r";
this.lbl_sl_r.Size = new System.Drawing.Size(85, 13);
this.lbl_sl_r.TabIndex = 10;
this.lbl_sl_r.Text = "SL Right Joycon";
this.lbl_sl_r.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_sl_r
//
this.btn_sl_r.Location = new System.Drawing.Point(105, 128);
this.btn_sl_r.Name = "btn_sl_r";
this.btn_sl_r.Size = new System.Drawing.Size(75, 23);
this.btn_sl_r.TabIndex = 9;
this.btn_sl_r.UseVisualStyleBackColor = true;
//
// lbl_sr_r
//
this.lbl_sr_r.AutoSize = true;
this.lbl_sr_r.Location = new System.Drawing.Point(15, 162);
this.lbl_sr_r.Name = "lbl_sr_r";
this.lbl_sr_r.Size = new System.Drawing.Size(87, 13);
this.lbl_sr_r.TabIndex = 12;
this.lbl_sr_r.Text = "SR Right Joycon";
this.lbl_sr_r.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_sr_r
//
this.btn_sr_r.Location = new System.Drawing.Point(105, 157);
this.btn_sr_r.Name = "btn_sr_r";
this.btn_sr_r.Size = new System.Drawing.Size(75, 23);
this.btn_sr_r.TabIndex = 11;
this.btn_sr_r.UseVisualStyleBackColor = true;
//
// btn_close
//
this.btn_close.Location = new System.Drawing.Point(15, 289);
this.btn_close.Name = "btn_close";
this.btn_close.Size = new System.Drawing.Size(75, 23);
this.btn_close.TabIndex = 13;
this.btn_close.Text = "Okay";
this.btn_close.UseVisualStyleBackColor = true;
this.btn_close.Click += new System.EventHandler(this.btn_close_Click);
//
// btn_apply
//
this.btn_apply.Location = new System.Drawing.Point(105, 289);
this.btn_apply.Name = "btn_apply";
this.btn_apply.Size = new System.Drawing.Size(75, 23);
this.btn_apply.TabIndex = 14;
this.btn_apply.Text = "Apply";
this.btn_apply.UseVisualStyleBackColor = true;
this.btn_apply.Click += new System.EventHandler(this.btn_apply_Click);
//
// lbl_reset_mouse
//
this.lbl_reset_mouse.AutoSize = true;
this.lbl_reset_mouse.Location = new System.Drawing.Point(15, 223);
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;
this.lbl_reset_mouse.Text = "Re-Centre Gyro";
this.lbl_reset_mouse.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_reset_mouse
//
this.btn_reset_mouse.Location = new System.Drawing.Point(105, 218);
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;
this.btn_reset_mouse.UseVisualStyleBackColor = true;
//
// lbl_activate_gyro
//
this.lbl_activate_gyro.AutoSize = true;
this.lbl_activate_gyro.Location = new System.Drawing.Point(14, 252);
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;
this.lbl_activate_gyro.Text = "Activate Gyro";
this.lbl_activate_gyro.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_active_gyro
//
this.btn_active_gyro.Location = new System.Drawing.Point(105, 247);
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.Controls.Add(this.btn_active_gyro);
this.Controls.Add(this.lbl_activate_gyro);
this.Controls.Add(this.lbl_reset_mouse);
this.Controls.Add(this.btn_reset_mouse);
this.Controls.Add(this.btn_apply);
this.Controls.Add(this.btn_close);
this.Controls.Add(this.lbl_sr_r);
this.Controls.Add(this.btn_sr_r);
this.Controls.Add(this.lbl_sl_r);
this.Controls.Add(this.btn_sl_r);
this.Controls.Add(this.lbl_sr_l);
this.Controls.Add(this.btn_sr_l);
this.Controls.Add(this.lbl_sl_l);
this.Controls.Add(this.btn_sl_l);
this.Controls.Add(this.lbl_home);
this.Controls.Add(this.btn_home);
this.Controls.Add(this.lbl_capture);
this.Controls.Add(this.btn_capture);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Reassign";
this.Text = "Map Special Buttons";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Reassign_FormClosing);
this.Load += new System.EventHandler(this.Reassign_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
@ -323,5 +345,7 @@ 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

@ -10,123 +10,123 @@ using System.Threading.Tasks;
using System.Windows.Forms;
namespace BetterJoyForCemu {
public partial class Reassign : Form {
private WindowsInput.Events.Sources.IKeyboardEventSource keyboard;
private WindowsInput.Events.Sources.IMouseEventSource mouse;
public partial class Reassign : Form {
private WindowsInput.Events.Sources.IKeyboardEventSource keyboard;
private WindowsInput.Events.Sources.IMouseEventSource mouse;
ContextMenuStrip menu_joy_buttons = new ContextMenuStrip();
ContextMenuStrip menu_joy_buttons = new ContextMenuStrip();
private Control curAssignment;
private Control curAssignment;
public Reassign() {
InitializeComponent();
public Reassign() {
InitializeComponent();
foreach (int i in Enum.GetValues(typeof(Joycon.Button))) {
ToolStripMenuItem temp = new ToolStripMenuItem(Enum.GetName(typeof(Joycon.Button), i));
temp.Tag = i;
menu_joy_buttons.Items.Add(temp);
}
foreach (int i in Enum.GetValues(typeof(Joycon.Button))) {
ToolStripMenuItem temp = new ToolStripMenuItem(Enum.GetName(typeof(Joycon.Button), i));
temp.Tag = i;
menu_joy_buttons.Items.Add(temp);
}
menu_joy_buttons.ItemClicked += Menu_joy_buttons_ItemClicked;
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_reset_mouse, btn_active_gyro }) {
c.Tag = c.Name.Substring(4);
GetPrettyName(c);
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 }) {
c.Tag = c.Name.Substring(4);
GetPrettyName(c);
tip_reassign.SetToolTip(c, "Left-click to detect input.\r\nMiddle-click to clear to default.\r\nRight-click to see more options.");
c.MouseDown += Remap;
c.Menu = menu_joy_buttons;
c.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
}
}
tip_reassign.SetToolTip(c, "Left-click to detect input.\r\nMiddle-click to clear to default.\r\nRight-click to see more options.");
c.MouseDown += Remap;
c.Menu = menu_joy_buttons;
c.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
}
}
private void Menu_joy_buttons_ItemClicked(object sender, ToolStripItemClickedEventArgs e) {
Control c = sender as Control;
private void Menu_joy_buttons_ItemClicked(object sender, ToolStripItemClickedEventArgs e) {
Control c = sender as Control;
ToolStripItem clickedItem = e.ClickedItem;
ToolStripItem clickedItem = e.ClickedItem;
SplitButton caller = (SplitButton)c.Tag;
Config.SetValue((string)caller.Tag, "joy_" + (clickedItem.Tag));
GetPrettyName(caller);
}
SplitButton caller = (SplitButton)c.Tag;
Config.SetValue((string)caller.Tag, "joy_" + (clickedItem.Tag));
GetPrettyName(caller);
}
private void Remap(object sender, MouseEventArgs e) {
SplitButton c = sender as SplitButton;
switch (e.Button) {
case MouseButtons.Left:
c.Text = "...";
curAssignment = c;
break;
case MouseButtons.Middle:
Config.SetValue((string)c.Tag, Config.GetDefaultValue((string)c.Tag));
GetPrettyName(c);
break;
case MouseButtons.Right:
break;
}
}
private void Remap(object sender, MouseEventArgs e) {
SplitButton c = sender as SplitButton;
switch (e.Button) {
case MouseButtons.Left:
c.Text = "...";
curAssignment = c;
break;
case MouseButtons.Middle:
Config.SetValue((string)c.Tag, Config.GetDefaultValue((string)c.Tag));
GetPrettyName(c);
break;
case MouseButtons.Right:
break;
}
}
private void Reassign_Load(object sender, EventArgs e) {
keyboard = WindowsInput.Capture.Global.KeyboardAsync();
keyboard.KeyEvent += Keyboard_KeyEvent;
mouse = WindowsInput.Capture.Global.MouseAsync();
mouse.MouseEvent += Mouse_MouseEvent;
}
private void Reassign_Load(object sender, EventArgs e) {
keyboard = WindowsInput.Capture.Global.KeyboardAsync();
keyboard.KeyEvent += Keyboard_KeyEvent;
mouse = WindowsInput.Capture.Global.MouseAsync();
mouse.MouseEvent += Mouse_MouseEvent;
}
private void Mouse_MouseEvent(object sender, WindowsInput.Events.Sources.EventSourceEventArgs<WindowsInput.Events.Sources.MouseEvent> e) {
if (curAssignment != null && e.Data.ButtonDown != null) {
Config.SetValue((string)curAssignment.Tag, "mse_" + ((int)e.Data.ButtonDown.Button));
AsyncPrettyName(curAssignment);
curAssignment = null;
e.Next_Hook_Enabled = false;
}
}
private void Mouse_MouseEvent(object sender, WindowsInput.Events.Sources.EventSourceEventArgs<WindowsInput.Events.Sources.MouseEvent> e) {
if (curAssignment != null && e.Data.ButtonDown != null) {
Config.SetValue((string)curAssignment.Tag, "mse_" + ((int)e.Data.ButtonDown.Button));
AsyncPrettyName(curAssignment);
curAssignment = null;
e.Next_Hook_Enabled = false;
}
}
private void Keyboard_KeyEvent(object sender, WindowsInput.Events.Sources.EventSourceEventArgs<WindowsInput.Events.Sources.KeyboardEvent> e) {
if (curAssignment != null && e.Data.KeyDown != null) {
Config.SetValue((string)curAssignment.Tag, "key_" + ((int)e.Data.KeyDown.Key));
AsyncPrettyName(curAssignment);
curAssignment = null;
e.Next_Hook_Enabled = false;
}
}
private void Keyboard_KeyEvent(object sender, WindowsInput.Events.Sources.EventSourceEventArgs<WindowsInput.Events.Sources.KeyboardEvent> e) {
if (curAssignment != null && e.Data.KeyDown != null) {
Config.SetValue((string)curAssignment.Tag, "key_" + ((int)e.Data.KeyDown.Key));
AsyncPrettyName(curAssignment);
curAssignment = null;
e.Next_Hook_Enabled = false;
}
}
private void Reassign_FormClosing(object sender, FormClosingEventArgs e) {
keyboard.Dispose();
mouse.Dispose();
}
private void Reassign_FormClosing(object sender, FormClosingEventArgs e) {
keyboard.Dispose();
mouse.Dispose();
}
private void AsyncPrettyName(Control c) {
if (InvokeRequired) {
this.Invoke(new Action<Control>(AsyncPrettyName), new object[] { c });
return;
}
GetPrettyName(c);
}
private void AsyncPrettyName(Control c) {
if (InvokeRequired) {
this.Invoke(new Action<Control>(AsyncPrettyName), new object[] { c });
return;
}
GetPrettyName(c);
}
private void GetPrettyName(Control c) {
string val;
switch (val = Config.Value((string)c.Tag)) {
case "0":
if (c == btn_home)
c.Text = "Guide";
else
c.Text = "";
break;
default:
Type t = val.StartsWith("joy_") ? typeof(Joycon.Button) : (val.StartsWith("key_") ? typeof(WindowsInput.Events.KeyCode) : typeof(WindowsInput.Events.ButtonCode));
c.Text = Enum.GetName(t, Int32.Parse(val.Substring(4)));
break;
}
}
private void GetPrettyName(Control c) {
string val;
switch (val = Config.Value((string)c.Tag)) {
case "0":
if (c == btn_home)
c.Text = "Guide";
else
c.Text = "";
break;
default:
Type t = val.StartsWith("joy_") ? typeof(Joycon.Button) : (val.StartsWith("key_") ? typeof(WindowsInput.Events.KeyCode) : typeof(WindowsInput.Events.ButtonCode));
c.Text = Enum.GetName(t, Int32.Parse(val.Substring(4)));
break;
}
}
private void btn_apply_Click(object sender, EventArgs e) {
Config.Save();
}
private void btn_apply_Click(object sender, EventArgs e) {
Config.Save();
}
private void btn_close_Click(object sender, EventArgs e) {
btn_apply_Click(sender, e);
Close();
}
}
private void btn_close_Click(object sender, EventArgs e) {
btn_apply_Click(sender, e);
Close();
}
}
}

View file

@ -13,11 +13,11 @@ namespace BetterJoyForCemu {
private bool running;
private byte[] recvBuffer = new byte[1024];
List<Joycon> controllers;
IList<Joycon> controllers;
public MainForm form;
public UdpServer(List<Joycon> p) {
public UdpServer(IList<Joycon> p) {
controllers = p;
}
@ -317,62 +317,57 @@ 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;
bool isLeft = hidReport.isLeft;
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;
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;
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;
outputData[++outIdx] = 0;
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.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(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;
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;
outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.HOME)) ? (byte)1 : (byte)0;
outputData[++outIdx] = 0; // no touch pad
outputData[++outIdx] = ds4.ps ? (byte)1 : (byte)0;
outputData[++outIdx] = ds4.touchpad ? (byte)1 : (byte)0;
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)));
outputData[++outIdx] = ds4.thumb_left_x;
outputData[++outIdx] = ds4.thumb_left_y;
outputData[++outIdx] = ds4.thumb_right_x;
outputData[++outIdx] = ds4.thumb_right_y;
//we don't have analog buttons so just use the Button enums (which give either 0 or 0xFF)
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.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(!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.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(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.shoulder_right ? (byte)0xFF : (byte)0;
outputData[++outIdx] = ds4.shoulder_left ? (byte)0xFF : (byte)0;
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;
outputData[++outIdx] = ds4.trigger_right_value;
outputData[++outIdx] = ds4.trigger_left_value;
outIdx++;

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.16.150" targetFramework="net461" />
<package id="WindowsInput" version="6.1.1" targetFramework="net461" />
<package id="Nefarius.ViGEm.Client" version="1.17.178" targetFramework="net461" />
<package id="WindowsInput" version="6.3.0" targetFramework="net461" />
</packages>

Binary file not shown.

Binary file not shown.

View file

@ -2,8 +2,8 @@
<img src="title.png">
</p>
# BetterJoy v6.3
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/), and system-wide with generic XInput support.
# 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.
@ -12,7 +12,7 @@ If anyone would like to donate (for whatever reason), [you can do so here](https
#### 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 110k 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.
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)
@ -24,7 +24,9 @@ Go to the [Releases tab](https://github.com/Davidobot/BetterJoy/releases/)!
1. Install drivers
1. Read the READMEs (they're there for a reason!)
1. Run *Drivers/ViGEmBus_Setup_1.16.116.exe*
2. Run *BetterJoyForCemu.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.
@ -52,6 +54,44 @@ Check out the [wiki](https://github.com/Davidobot/BetterJoy/wiki)! There, you'll
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.
@ -61,5 +101,7 @@ Many thanks to [nefarius](https://github.com/ViGEm/ViGEmBus) for his ViGEm proje
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/).
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).