// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; using SharpDX.DirectInput; // SharpDX.dll & SharpDX.DirectInput.dll namespace Spludlow.Tetris { /// /// Handle GamePads and JoySticks using SharpDX /// public class TetrisInput : IDisposable { public enum InputCommand { Up, Down, Left, Right, Button0, Button1, Button2 } public static int AutoRepeatRate = 32; // Milliseconds of sleep in joystick loop public static int AutoRepeatDelay = 500 / AutoRepeatRate; // Number of loops before autorepeating public static int CentreMargin = 0x3fff; public static int CenterMin = 0x7fff - CentreMargin; public static int CenterMax = 0x7fff + CentreMargin; private DirectInput DirectInput = null; private Joystick Joystick = null; private DeviceType DeviceType; private Thread Thread = null; public delegate void Move(InputCommand command); public event Move MoveEvent; public TetrisInput() { this.DirectInput = new DirectInput(); } public void Start(int joystickIndex) { DeviceInstance[] deviceInstances = DeviceInstances(this.DirectInput); if (deviceInstances.Length == 0 || (joystickIndex >= deviceInstances.Length)) { Spludlow.Log.Error("Tetris Input, joystick not available: index:" + joystickIndex + ", deviceInstances:" + deviceInstances); return; } this.Joystick = new Joystick(this.DirectInput, deviceInstances[joystickIndex].InstanceGuid); this.DeviceType = deviceInstances[joystickIndex].Type; this.Joystick.Properties.BufferSize = 256; this.Joystick.Acquire(); this.Thread = new Thread(new ThreadStart(this.Run)); this.Thread.Start(); } public void Stop() { if (this.Thread != null) { this.Thread.Abort(); this.Thread.Join(); } this.Thread = null; if (this.Joystick != null) { this.Joystick.Unacquire(); this.Joystick.Dispose(); } this.Joystick = null; } public void Dispose() { this.Stop(); if (this.DirectInput != null) this.DirectInput.Dispose(); } public static string[] DeviceInstancesText() { List lines = new List(); using (DirectInput directInput = new DirectInput()) { foreach (DeviceInstance deviceInstance in DeviceInstances(directInput)) lines.Add(deviceInstance.Type + " : " + deviceInstance.InstanceName + " : " + deviceInstance.InstanceGuid.ToString()); } return lines.ToArray(); } public static DeviceInstance[] DeviceInstances(DirectInput directInput) { List deviceInstances = new List(); DeviceType[] deviceTypes = new DeviceType[] { DeviceType.Gamepad, DeviceType.Joystick }; foreach (DeviceType deviceType in deviceTypes) { foreach (DeviceInstance deviceInstance in directInput.GetDevices(deviceType, DeviceEnumerationFlags.AllDevices)) deviceInstances.Add(deviceInstance); } return deviceInstances.ToArray(); } public void Run() { try { int commandCount = Enum.GetValues(typeof(InputCommand)).Length; bool[] commandsCurrent = new bool[commandCount]; int[] commandRepeatCounts = new int[commandCount]; while (true) { this.Joystick.Poll(); JoystickUpdate[] joystickUpdates = this.Joystick.GetBufferedData(); foreach (JoystickUpdate stick in joystickUpdates) { if (stick.Offset == JoystickOffset.Y) { switch (stick.Value) { case 0: //if (this.DeviceType == DeviceType.Joystick) // Disable up for joysticks, too easy to catch //{ // commandsCurrent[(int)InputCommands.Up] = true; // commandsCurrent[(int)InputCommands.Down] = false; //} break; case 32511: commandsCurrent[(int)InputCommand.Up] = false; commandsCurrent[(int)InputCommand.Down] = false; break; case 65535: commandsCurrent[(int)InputCommand.Up] = false; commandsCurrent[(int)InputCommand.Down] = true; break; default: if (stick.Value > CenterMin && stick.Value < CenterMax) { commandsCurrent[(int)InputCommand.Up] = false; commandsCurrent[(int)InputCommand.Down] = false; } break; } } if (this.DeviceType == DeviceType.Gamepad) { if (stick.Offset == JoystickOffset.Z) { if (stick.Value > 60000) //== 65408) commandsCurrent[(int)InputCommand.Up] = true; else commandsCurrent[(int)InputCommand.Up] = false; } } if (stick.Offset == JoystickOffset.X) { switch (stick.Value) { case 0: commandsCurrent[(int)InputCommand.Left] = true; commandsCurrent[(int)InputCommand.Right] = false; break; case 32511: commandsCurrent[(int)InputCommand.Left] = false; commandsCurrent[(int)InputCommand.Right] = false; break; case 65535: commandsCurrent[(int)InputCommand.Left] = false; commandsCurrent[(int)InputCommand.Right] = true; break; default: if (stick.Value > CenterMin && stick.Value < CenterMax) { commandsCurrent[(int)InputCommand.Left] = false; commandsCurrent[(int)InputCommand.Right] = false; } break; } } if (stick.Offset == JoystickOffset.PointOfViewControllers0) { switch (stick.Value) { case -1: // M commandsCurrent[(int)InputCommand.Up] = false; commandsCurrent[(int)InputCommand.Down] = false; commandsCurrent[(int)InputCommand.Left] = false; commandsCurrent[(int)InputCommand.Right] = false; break; case 0: // U commandsCurrent[(int)InputCommand.Up] = true; commandsCurrent[(int)InputCommand.Down] = false; commandsCurrent[(int)InputCommand.Left] = false; commandsCurrent[(int)InputCommand.Right] = false; break; case 9000: // R commandsCurrent[(int)InputCommand.Up] = false; commandsCurrent[(int)InputCommand.Down] = false; commandsCurrent[(int)InputCommand.Left] = false; commandsCurrent[(int)InputCommand.Right] = true; break; case 18000: // D commandsCurrent[(int)InputCommand.Up] = false; commandsCurrent[(int)InputCommand.Down] = true; commandsCurrent[(int)InputCommand.Left] = false; commandsCurrent[(int)InputCommand.Right] = false; break; case 27000: // L commandsCurrent[(int)InputCommand.Up] = false; commandsCurrent[(int)InputCommand.Down] = false; commandsCurrent[(int)InputCommand.Left] = true; commandsCurrent[(int)InputCommand.Right] = false; break; } } if (stick.Offset == JoystickOffset.Buttons0) commandsCurrent[(int)InputCommand.Button0] = (stick.Value == 128); if (stick.Offset == JoystickOffset.Buttons1) { if (this.DeviceType == DeviceType.Joystick) commandsCurrent[(int)InputCommand.Up] = (stick.Value == 128); // for joystick use CCW for UP(drop) else commandsCurrent[(int)InputCommand.Button1] = (stick.Value == 128); } if (stick.Offset == JoystickOffset.Buttons2) commandsCurrent[(int)InputCommand.Button2] = (stick.Value == 128); } foreach (InputCommand command in Enum.GetValues(typeof(InputCommand))) { if (commandsCurrent[(int)command] == false) { commandRepeatCounts[(int)command] = 0; continue; } bool perform = false; if (commandRepeatCounts[(int)command] == 0) perform = true; if (++commandRepeatCounts[(int)command] > AutoRepeatDelay) perform = true; if (perform == false) continue; this.MoveEvent(command); } System.Threading.Thread.Sleep(AutoRepeatRate); } } catch (Exception ee) { Spludlow.Log.Error("Tetris Input: Error in main loop", ee); this.Stop(); } } } }