📜 ⬆️ ⬇️

Making a Windows Phone Joystick

A little background.


image One evening, a son came up to me and said that he wanted to play Mario. In the summer at his grandmother's cottage, he loved to “wriggle out” in rainy weather. And it was raining outside. Without hesitation, I downloaded him the first emulator of an 8-bit set-top box and the game. However, it turned out that the pleasure of playing the keyboard is not at all the same. Going to buy a joystick was too late. And then I thought that you can do without it. At hand we had an old Nokia Lumia, its size and shape roughly coincided with our needs. It was decided to write a joystick. The son went to draw a design on a piece of paper in the cell, and dad went to make coffee and think about how to implement this idea with the least amount of time.

I decided to take the path of least (from my point of view) resistance. The emulator prefix in the settings you need to specify the pressed buttons, then our application must press the buttons. Button presses can be emulated with the good old WINAPI.

The ultimate idea was a client-server application. The client (phone) when you click on a button sends a request to the server, which, in turn, depending on what came to emulate pressing or releasing the keyboard button. Communication is carried out through sockets. It seems simple. We start to do.

Server part


Put on the textbox form with the same name textBox. In it we will show what comes from the phone.
')
image

Getting started with sockets.

First of all we connect them:

using System.Net; using System.Net.Sockets; 

We get a socket and a buffer into which everything will come:

 public partial class ServerForm : Form { private Socket _serverSocket, _clientSocket; private byte[] _buffer; 

We write the function that runs our server.

 private void StartServer() { try { _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _serverSocket.Bind(new IPEndPoint(IPAddress.Any, 3333)); _serverSocket.Listen(0); _serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null); } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } 

And accordingly we start it at the very beginning.

  public ServerForm() { InitializeComponent(); StartServer(); } 

We connect the work with the keyboard and write a couple of functions: one presses the key, the other releases.

 [ DllImport("user32.dll")] private static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); private const int KEYEVENTF_EXTENDEDKEY = 1; private const int KEYEVENTF_KEYUP = 2; public static void KeyDown(Keys vKey) { keybd_event((byte)vKey, 0, KEYEVENTF_EXTENDEDKEY, 0); } public static void KeyUp(Keys vKey) { keybd_event((byte)vKey, 0, KEYEVENTF_KEYUP, 0); } 

We try to get something and, if everything is in order, then we add in the textBox what we have received and press (release) the button.

 private void ReceiveCallback(IAsyncResult AR) { try { int received = _clientSocket.EndReceive(AR); Array.Resize(ref _buffer, received); string text = Encoding.ASCII.GetString(_buffer); //     AppendToTextBox(text); // ------------------ Array.Resize(ref _buffer, _clientSocket.ReceiveBufferSize); _clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null); } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } 

Function to emulate pressing and releasing a button depending on what came:

 private void AppendToTextBox(string text) { MethodInvoker invoker = new MethodInvoker(delegate { string text_before = text; string exitW = text; // if (text == "a") { KeyUp(Keys.D); KeyDown(Keys.A); textBox.Text += text + " "; } // if (text == "a1" ) { KeyUp(Keys.A); textBox.Text += text + " "; } }); this.Invoke(invoker); } 

In the process of testing, it was discovered that the “forward” button can stick for unknown reasons. In this case, the player reflexively presses "back", trying to slow down. In order to have such an opportunity when you press the "back" we just in case raise the "forward" button.

  if (text == "a") { KeyUp(Keys.D); KeyDown(Keys.A); textBox.Text += text + " "; } 

The reason for sticking a button is not clear.

Client part


I wanted to play Mario more and more, so it was decided to set up the connection and make the connection on the same screen.
At the top, there are fields for entering the ip address and port of the connection button and a textblock showing the status. As the control buttons were taken standard AppBarButton, which the son himself and placed, in accordance with its own design. According to his idea, it was decided to abandon the standard background. Design is complete. It remains to be done to make it all work.

image

We cut sockets:

 using Windows.Networking.Sockets; using Windows.Networking; using Windows.Storage.Streams; 

Open a new socket:

  StreamSocket clientSocket = new StreamSocket(); 

Trying to connect:

 private async void btnConnect_Click(object sender, RoutedEventArgs e) { HostName host = new HostName(textBoxIP.Text); string port = textBoxPort.Text; if (connected) { StatusText.Text = " "; return; } try { StatusText.Text = "  ..."; await clientSocket.ConnectAsync(host, port); connected = true; StatusText.Text = " " + Environment.NewLine; } catch (Exception exception) { if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) { throw; } StatusText.Text = "   : "; closing = true; clientSocket.Dispose(); clientSocket = null; } } 

We send data to the server:

 private async void sendkey(string key) { if (!connected) { StatusText.Text = " "; return; } try { StatusText.Text = "   ..."; DataWriter writer = new DataWriter(clientSocket.OutputStream); writer.WriteString(key); await writer.StoreAsync(); StatusText.Text = " " + Environment.NewLine; writer.DetachStream(); writer.Dispose(); } catch (Exception exception) { if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) { throw; } StatusText.Text = "    "; closing = true; clientSocket.Dispose(); clientSocket = null; connected = false; } } 

Suddenly there was a problem. Click events occur at the button not at the moment of its pressing, but at the moment of its release after pressing. After a short digging in msdn, it was decided to use GotFocus as a click, and Click as a release button, later the PointerReleased event was added for the same purpose (this allowed to release one button while the other was pressed). When the button is released, the focus still remains on it, in order to avoid this, we pass it to any other element, in this case it is a button that does not participate in the control named btnConnect:

 //   // private void gotFocusUp(object sender, RoutedEventArgs e) { sendkey("w"); } private void lostFocusUp(object sender, RoutedEventArgs e) { sendkey("w1"); btnConnect.Focus(FocusState.Programmatic); } private void lostFocusUp(object sender, PointerRoutedEventArgs e) { sendkey("w1"); btnConnect.Focus(FocusState.Programmatic); } 

Result


image

Pros: Mario runs and jumps, rescues the princess. The son is pleased.

Future plans:

1. Make a server search on the phone.
2. Deal with sticking (although comrades say sticking adds realism, and not at all a bug, but a feature).
3. Redraw the design.
4. Add the ability to play together
5. Brush the code. (I don’t like the number of if in the server part and the fact that each button has its own almost identical events in the client)

Link for those who are interested in gitHub.

Source: https://habr.com/ru/post/274271/


All Articles