About the new frontend in development ...

Technical discussion for those interested in Supermodel development and Model 3 reverse engineering. Prospective contributors welcome. Not for end-user support.
Forum rules
Keep it classy!
  • No ROM requests or links.
  • Do not ask to be a play tester.
  • Do not ask about release dates.
  • No drama!
segaduck
Posts: 14
Joined: Fri Nov 17, 2023 2:49 am

About the new frontend in development ...

Post by segaduck »

Hi everyone,

It's the new frontend i am developing right now.
I've studied C# from the beginning and try my best to develop it as possible. I know i am in a very slow pace, wish you guys will like it when I finish.
Here is what I did so far. Enjoy.

https://www.youtube.com/watch?v=OGzriGmIZ6E
segaduck
Posts: 14
Joined: Fri Nov 17, 2023 2:49 am

Re: About the new frontend in development ...

Post by segaduck »

SuerpM3UI_0121.jpg
SuerpM3UI_0121.jpg (225.73 KiB) Viewed 3114 times
ToBul
Posts: 4
Joined: Sat Jan 20, 2024 1:27 pm

Re: About the new frontend in development ...

Post by ToBul »

This looks really nice. Beautiful, simple, sleek aesthetic.
Supermodel has needed a good UI for a while now.
Great work.
You must have done some sort of coding before ?
aka rokfpoewrkcpoqwkcp
User avatar
Masked Ninja
Posts: 9
Joined: Sat Nov 11, 2023 9:48 pm

Re: About the new frontend in development ...

Post by Masked Ninja »

It looks great, please make sure you add all the commands.
Thank you so much... :D
segaduck
Posts: 14
Joined: Fri Nov 17, 2023 2:49 am

Re: About the new frontend in development ...

Post by segaduck »

I will implement all config parameters such as -true-hz- of the latest version Supermodel for sure.
What I am struggling right now is the keys/buttons/joystick mapping UI, and wish I can find a best way to finish it ASAP.
Viking245
Posts: 3
Joined: Thu Nov 09, 2023 12:59 pm

Re: About the new frontend in development ...

Post by Viking245 »

segaduck wrote: Mon Jan 22, 2024 9:06 am What I am struggling right now is the keys/buttons/joystick mapping UI, and wish I can find a best way to finish it ASAP.
I've used SharpDX (and SharpDX.XInput and SharpDX.DirectInput) in the past to capture inputs. Though now 'archived', it may still be useful or lead you in the right direction.

If you'd like any help with the project, feel free to DM me. You can keep 100% of the credit for the project. Makes no matter to me. :D (I did try to DM you, but 'lurkers' aren't allowed. ;) )
segaduck
Posts: 14
Joined: Fri Nov 17, 2023 2:49 am

Re: About the new frontend in development ...

Post by segaduck »

Viking245 wrote: Mon Jan 22, 2024 2:13 pm
segaduck wrote: Mon Jan 22, 2024 9:06 am What I am struggling right now is the keys/buttons/joystick mapping UI, and wish I can find a best way to finish it ASAP.
I've used SharpDX (and SharpDX.XInput and SharpDX.DirectInput) in the past to capture inputs. Though now 'archived', it may still be useful or lead you in the right direction.

If you'd like any help with the project, feel free to DM me. You can keep 100% of the credit for the project. Makes no matter to me. :D (I did try to DM you, but 'lurkers' aren't allowed. ;) )
Hi, Viking245,

Nice to meet you here. It is very welcome to invite you to join the new frontend project's development, and I believe we can finish the project sooner with your great help.

I've tried SharpDX.XInput in the development and get the inputs successfully. What I am confused is how to map the naming in SharpDX.Xinput or Dinput to the Suerpmodel's naming. For example, in Supermodel "JOY1_BUTTON1" to SharpDX's names, especially there are more than only 1 joystick connected.

I am thinking if you can develop a C# class with method which will return the user's input name once a time and I will implement other UI and Key mapping stuff for each game. Or you can give me other suggestion will be great.
Thank you.

Following are the source codes that I've written for Joystick input testing UI.
======================================================================================
using System;
using System.Data;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Timer = System.Windows.Forms.Timer;
using System.Drawing.Drawing2D;
using SharpDX.XInput;
using SharpDX.DirectInput;
using SharpDX.Mathematics.Interop;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.TextBox;

namespace SuperM3UI
{
public partial class FormControlTest : Form
{
private Timer updateTimer;
private Controller controller;
int JStickPos;
int JStickPos_pre;

int shiftX = 120;
int shiftY = 120;
int shiftUR = 84;
int ballPosX = 220;
int ballPosY = 170;
Bitmap myBitmap;

Rectangle expansionRectangle;


public Image sourceImage;

public FormControlTest()
{
InitializeComponent();

// Initialize Timer
updateTimer = new Timer();
updateTimer.Interval = 16; // every16ms, around 60fps
updateTimer.Tick += UpdateTimer_Tick;

// Intialize JStick Position on UI
JStickPos = 5;
JStickPos_pre = 5;
sourceImage = Image.FromFile(@"C:\Development\SuperM3UI\SuperM3UI\icons\sticks-ball-mid.png");
myBitmap = new Bitmap(@"C:\Development\SuperM3UI\SuperM3UI\icons\sticks-ball-mid.png");

// Initialize Xbox controller
controller = new Controller(UserIndex.One);

if (!controller.IsConnected)
{
MessageBox.Show("Xbox Controller is not connected。");
}
}

private void FormControlTest_Load(object sender, EventArgs e)
{
updateTimer.Start();
this.Location = new System.Drawing.Point(this.Location.X + 110, this.Location.Y + 65);

}

private void UpdateTimer_Tick(object sender, EventArgs e)
{
if (controller.IsConnected)
{
State st = controller.GetState();
//lb_PosX.Text = "Parent's X/Y =" + this.Parent.Location.X.ToString() + "//" + this.Parent.Location.Y.ToString();
//lb_PosY.Text = "It's X/Y =" + this.Location.X.ToString() + "//" + this.Location.Y.ToString();

// Checked buttons / joystick 's input
Boolean A_Btn_Now_pressed = ((st.Gamepad.Buttons & GamepadButtonFlags.A) != 0);
if (A_Btn_Now_pressed) { picBox_GreenBtnPress.Visible = true; } else { picBox_GreenBtnPress.Visible = false; }
//labelButton_A.Text = $"A Button: {A_Btn_Now_pressed}";

Boolean B_Btn_Now_pressed = ((st.Gamepad.Buttons & GamepadButtonFlags.B) != 0);
if (B_Btn_Now_pressed) { picBox_BlueBtnPress.Visible = true; } else { picBox_BlueBtnPress.Visible = false; }
//labelButton_B.Text = $"B Button: {(st.Gamepad.Buttons & GamepadButtonFlags.B) != 0}";

Boolean X_Btn_Now_pressed = ((st.Gamepad.Buttons & GamepadButtonFlags.X) != 0);
if (X_Btn_Now_pressed) { picBox_PinkBtnPress.Visible = true; } else { picBox_PinkBtnPress.Visible = false; }
//labelButton_X.Text = $"X Button: {(st.Gamepad.Buttons & GamepadButtonFlags.X) != 0}";

Boolean Y_Btn_Now_pressed = ((st.Gamepad.Buttons & GamepadButtonFlags.Y) != 0);
if (Y_Btn_Now_pressed) { picBox_RedBtnPress.Visible = true; } else { picBox_RedBtnPress.Visible = false; }
//labelButton_Y.Text = $"Y Button: {(st.Gamepad.Buttons & GamepadButtonFlags.Y) != 0}";


if ((st.Gamepad.Buttons & GamepadButtonFlags.DPadUp) != 0)
{
JStickPos = 8;
if ((st.Gamepad.Buttons & GamepadButtonFlags.DPadUp) != 0 && (st.Gamepad.Buttons & GamepadButtonFlags.DPadRight) != 0) JStickPos = 9;
if ((st.Gamepad.Buttons & GamepadButtonFlags.DPadUp) != 0 && (st.Gamepad.Buttons & GamepadButtonFlags.DPadLeft) != 0) JStickPos = 7;
}

if ((st.Gamepad.Buttons & GamepadButtonFlags.DPadDown) != 0)
{
JStickPos = 2;
if ((st.Gamepad.Buttons & GamepadButtonFlags.DPadDown) != 0 && (st.Gamepad.Buttons & GamepadButtonFlags.DPadRight) != 0) JStickPos = 3;
if ((st.Gamepad.Buttons & GamepadButtonFlags.DPadDown) != 0 && (st.Gamepad.Buttons & GamepadButtonFlags.DPadLeft) != 0) JStickPos = 1;
}

if ((st.Gamepad.Buttons & GamepadButtonFlags.DPadRight) != 0)
{
JStickPos = 6;
if ((st.Gamepad.Buttons & GamepadButtonFlags.DPadRight) != 0 && (st.Gamepad.Buttons & GamepadButtonFlags.DPadUp) != 0) JStickPos = 9;
if ((st.Gamepad.Buttons & GamepadButtonFlags.DPadRight) != 0 && (st.Gamepad.Buttons & GamepadButtonFlags.DPadDown) != 0) JStickPos = 3;
}

if ((st.Gamepad.Buttons & GamepadButtonFlags.DPadLeft) != 0)
{
JStickPos = 4;
if ((st.Gamepad.Buttons & GamepadButtonFlags.DPadLeft) != 0 && (st.Gamepad.Buttons & GamepadButtonFlags.DPadUp) != 0) JStickPos = 7;
if ((st.Gamepad.Buttons & GamepadButtonFlags.DPadLeft) != 0 && (st.Gamepad.Buttons & GamepadButtonFlags.DPadDown) != 0) JStickPos = 1;
}

if (((st.Gamepad.Buttons & GamepadButtonFlags.DPadLeft) == 0) && ((st.Gamepad.Buttons & GamepadButtonFlags.DPadRight) == 0)
&& ((st.Gamepad.Buttons & GamepadButtonFlags.DPadUp) == 0) && ((st.Gamepad.Buttons & GamepadButtonFlags.DPadDown) == 0))
{
JStickPos = 5;
}
if (JStickPos_pre != JStickPos) panel1.Invalidate();
JStickPos_pre = JStickPos;
}
}

private void FormControlTest_FormClosed(object sender, FormClosedEventArgs e)
{
updateTimer.Stop();
}

private void rjButton2_Click(object sender, EventArgs e)
{
this.Close();
}

private void panel1_Paint(object sender, PaintEventArgs e)
{
Graphics graphics = e.Graphics;
expansionRectangle = new Rectangle(ballPosX, ballPosY, myBitmap.Width * 14 / 10, myBitmap.Height * 14 / 10);


// Draw a portion of the source image.
if (JStickPos == 1)
{
expansionRectangle = new Rectangle(ballPosX - shiftUR, ballPosY + shiftUR, myBitmap.Width * 14 / 10, myBitmap.Height * 14 / 10);
graphics.DrawImage(myBitmap, expansionRectangle);
}
if (JStickPos == 2)
{
expansionRectangle = new Rectangle(ballPosX, ballPosY + shiftY, myBitmap.Width * 14 / 10, myBitmap.Height * 14 / 10);
graphics.DrawImage(myBitmap, expansionRectangle);
}
if (JStickPos == 3)
{
expansionRectangle = new Rectangle(ballPosX + shiftUR, ballPosY + shiftUR, myBitmap.Width * 14 / 10, myBitmap.Height * 14 / 10);
graphics.DrawImage(myBitmap, expansionRectangle);
}
if (JStickPos == 4)
{
expansionRectangle = new Rectangle(ballPosX - shiftX, ballPosY, myBitmap.Width * 14 / 10, myBitmap.Height * 14 / 10);
graphics.DrawImage(myBitmap, expansionRectangle);
}
if (JStickPos == 5)
{
expansionRectangle = new Rectangle(ballPosX, ballPosY, myBitmap.Width * 14 / 10, myBitmap.Height * 14 / 10);
graphics.DrawImage(myBitmap, expansionRectangle);
}
if (JStickPos == 6)
{
expansionRectangle = new Rectangle(ballPosX + shiftX, ballPosY, myBitmap.Width * 14 / 10, myBitmap.Height * 14 / 10);
graphics.DrawImage(myBitmap, expansionRectangle);
}
if (JStickPos == 7)
{
expansionRectangle = new Rectangle(ballPosX - shiftUR, ballPosY - shiftUR, myBitmap.Width * 14 / 10, myBitmap.Height * 14 / 10);
graphics.DrawImage(myBitmap, expansionRectangle);
}
if (JStickPos == 8)
{
expansionRectangle = new Rectangle(ballPosX, ballPosY - shiftY, myBitmap.Width * 14 / 10, myBitmap.Height * 14 / 10);
graphics.DrawImage(myBitmap, expansionRectangle);
}
if (JStickPos == 9)
{
expansionRectangle = new Rectangle(ballPosX + shiftUR, ballPosY - shiftUR, myBitmap.Width * 14 / 10, myBitmap.Height * 14 / 10);
graphics.DrawImage(myBitmap, expansionRectangle);
}
}
}
}
======================================================================================
segaduck
Posts: 14
Joined: Fri Nov 17, 2023 2:49 am

Re: About the new frontend in development ...

Post by segaduck »

The video clip of my testing implement of Joystick input testing.

https://youtu.be/YXV8efEmFfk

However, it's a quite old version codes which I used C# Winforms and I am learning and using WPF to develop the new codes for much better outlook of the UI.
Viking245
Posts: 3
Joined: Thu Nov 09, 2023 12:59 pm

Re: About the new frontend in development ...

Post by Viking245 »

segaduck wrote: Tue Jan 23, 2024 5:41 pm However, it's a quite old version codes which I used C# Winforms and I am learning and using WPF to develop the new codes for much better outlook of the UI.
WPF will definitely look WAY better than WinForms. ;) Which Framework are you using?

What I am confused is how to map the naming in SharpDX.Xinput or Dinput to the Suerpmodel's naming.
Say you ask the user to select an input for 'Player 1 - Coin'. This can trigger a Method that has a Do/While loop to wait for a controller Button press (or time out). The Method captures the input and returns the transposed value. i.e. they want to use the Back Buton. (truncated method)

Edit: moved 'State st =' to inside do loop

Code: Select all

public string GetControllerInput()
{
	TimeSpan timeout = TimeSpan.FromSeconds(5);
	var stopwatch = Stopwatch.StartNew();
	
	//State st = controller.GetState();

	do
	{
		//needs to be inside do loop
		State st = controller.GetState();
		
		if ((st.Gamepad.Buttons & GamepadButtonFlags.Back) != 0)
		{
			return "KEY_1,JOY1_BUTTON7";
		}
		
		//... all other possible inputs
	}
	while (stopwatch.Elapsed < timeout);
		
	return string.Empty;
}	
To get Controller #2, #3 etc., probably need to declare all [possible] controllers.

Code: Select all

Controller controller1 = new Controller(UserIndex.One);
Controller controller2 = new Controller(UserIndex.Two);
Controller controller3 = new Controller(UserIndex.Three);
//.... etc.
Then in the above method, account for the 'possibilities'.

Code: Select all

State st1 = controller1.GetState();
State st2 = controller2.GetState();
State st3 = controller3.GetState();

//...
if ((st1.Gamepad.Buttons & GamepadButtonFlags.Back) != 0)
{
	return "KEY_1,JOY1_BUTTON7";
}

if ((st2.Gamepad.Buttons & GamepadButtonFlags.Back) != 0)
{
	return "KEY_1,JOY2_BUTTON7";
}
//...
Then.. however you plan to 'ask the question', followed up with

Code: Select all

string InputCoin1 = GetControllerInput();
if (InputCoin1 == string.Empty)
{
	//do something to indicate there's no 'InputCoin1'
}
Hope that helps. Or at least gives you some momentum. This is just one way of doing it, as I'm sure there's at least 50 others. ;)
segaduck
Posts: 14
Joined: Fri Nov 17, 2023 2:49 am

Re: About the new frontend in development ...

Post by segaduck »

Viking245 wrote: Wed Jan 24, 2024 1:45 pm
segaduck wrote: Tue Jan 23, 2024 5:41 pm However, it's a quite old version codes which I used C# Winforms and I am learning and using WPF to develop the new codes for much better outlook of the UI.
WPF will definitely look WAY better than WinForms. ;) Which Framework are you using?

I used the standard WPF for Microsoft in the beginning, but I changed to use Avalonia with MVVM community toolkit and rewrite everything now.

What I am confused is how to map the naming in SharpDX.Xinput or Dinput to the Suerpmodel's naming.
Say you ask the user to select an input for 'Player 1 - Coin'. This can trigger a Method that has a Do/While loop to wait for a controller Button press (or time out). The Method captures the input and returns the transposed value. i.e. they want to use the Back Buton. (truncated method)

Edit: moved 'State st =' to inside do loop

Code: Select all

public string GetControllerInput()
{
	TimeSpan timeout = TimeSpan.FromSeconds(5);
	var stopwatch = Stopwatch.StartNew();
	
	//State st = controller.GetState();

	do
	{
		//needs to be inside do loop
		State st = controller.GetState();
		
		if ((st.Gamepad.Buttons & GamepadButtonFlags.Back) != 0)
		{
			return "KEY_1,JOY1_BUTTON7";
		}
		
		//... all other possible inputs
	}
	while (stopwatch.Elapsed < timeout);
		
	return string.Empty;
}	
To get Controller #2, #3 etc., probably need to declare all [possible] controllers.

Code: Select all

Controller controller1 = new Controller(UserIndex.One);
Controller controller2 = new Controller(UserIndex.Two);
Controller controller3 = new Controller(UserIndex.Three);
//.... etc.
Then in the above method, account for the 'possibilities'.

Code: Select all

State st1 = controller1.GetState();
State st2 = controller2.GetState();
State st3 = controller3.GetState();

//...
if ((st1.Gamepad.Buttons & GamepadButtonFlags.Back) != 0)
{
	return "KEY_1,JOY1_BUTTON7";
}

if ((st2.Gamepad.Buttons & GamepadButtonFlags.Back) != 0)
{
	return "KEY_1,JOY2_BUTTON7";
}
//...
Then.. however you plan to 'ask the question', followed up with

Code: Select all

string InputCoin1 = GetControllerInput();
if (InputCoin1 == string.Empty)
{
	//do something to indicate there's no 'InputCoin1'
}
Hope that helps. Or at least gives you some momentum. This is just one way of doing it, as I'm sure there's at least 50 others. ;)
Thanks for your great help and wonderful advice. It is very useful information for me.
However, I am so busy in working recently. I may restart to write new codes in Feb.
Keep in touch !!!
Post Reply