Capture pressed buttons on video game control reliably

8

I want to use an XBox control to control a program. It's not a game, it's just a Windows Forms application. I do not want to use XNA because I do not want to force the redistributables to download when I use my application on another machine.

I'm doing some experimenting with DirectX, using some libraries that encapsulate it. From the documentation, it seems that there is no push button event.

So I'm looking for an alternative. I tried to use a timer (with class System.Windows.Forms.Timer ), in which event I read the state of the control this way:

private void timer_tick(object sender, EventArgs e)
{
    State s = controller.GetState();
    stateLabel.Text = s.Gamepad.Buttons == GamepadButtonFlags.A ? "A" : "";
}

Problem: Between two runs of this method, I have no way of knowing if the user hit the button twice, or if he has been holding the button all the time. Even using the packet number information sent by the control does not help because a packet is sent at the slightest contact with the analog directional switches or the triggers on the control's shoulders.

Does anyone know of any more reliable way to do this?

    
asked by anonymous 23.03.2014 / 20:45

1 answer

5

Instead of implementing this in a timer, why not use another thread, and continuously check the state of the control, firing an event on the form when a button is pressed and when it is released.

Form Code:

public partial class FormWithExternalEvents : Form
{
    private GamepadEvents gpe;

    public FormWithExternalEvents()
    {
        InitializeComponent();
    }

    protected override void OnLoad(EventArgs e)
    {
        gpe = new GamepadEvents();
        gpe.JoystickButtonDown += b =>
        {
            if (this.InvokeRequired) this.Invoke((GamepadButtonEvent) gpe_JoystickButtonDown);
            else gpe_JoystickButtonDown(b);
        };
        gpe.JoystickButtonUp += b =>
        {
            if (this.InvokeRequired) this.Invoke((GamepadButtonEvent)gpe_JoystickButtonUp);
            else gpe_JoystickButtonUp(b);
        };
        var thread = new Thread(gpe.Run);
        thread.Start();

        base.OnLoad(e);
    }

    void gpe_JoystickButtonUp(SharpDX.XInput.GamepadButtonFlags button)
    {
        throw new NotImplementedException();
    }

    void gpe_JoystickButtonDown(SharpDX.XInput.GamepadButtonFlags button)
    {
        throw new NotImplementedException();
    }
}

Code of the class that will be running on another thread:

public delegate void GamepadButtonEvent(GamepadButtonFlags button);

public class GamepadEvents
{
    public event GamepadButtonEvent JoystickButtonUp;
    public event GamepadButtonEvent JoystickButtonDown;

    private bool[] buttonsBools = new bool[32];

    public void Run()
    {
        while (true)
        {
            var controller = new Controller();
            State s = controller.GetState();

            ButtonSignal(0, GamepadButtonFlags.DPadUp, (s.Gamepad.Buttons & GamepadButtonFlags.DPadUp) != 0);
            ButtonSignal(1, GamepadButtonFlags.DPadDown, (s.Gamepad.Buttons & GamepadButtonFlags.DPadDown) != 0);
            ButtonSignal(2, GamepadButtonFlags.DPadLeft, (s.Gamepad.Buttons & GamepadButtonFlags.DPadLeft) != 0);
            ButtonSignal(3, GamepadButtonFlags.DPadRight, (s.Gamepad.Buttons & GamepadButtonFlags.DPadRight) != 0);
            ButtonSignal(4, GamepadButtonFlags.Start, (s.Gamepad.Buttons & GamepadButtonFlags.Start) != 0);
            ButtonSignal(5, GamepadButtonFlags.Back, (s.Gamepad.Buttons & GamepadButtonFlags.Back) != 0);
            ButtonSignal(6, GamepadButtonFlags.LeftThumb, (s.Gamepad.Buttons & GamepadButtonFlags.LeftThumb) != 0);
            ButtonSignal(7, GamepadButtonFlags.RightThumb, (s.Gamepad.Buttons & GamepadButtonFlags.RightThumb) != 0);
            ButtonSignal(8, GamepadButtonFlags.LeftShoulder, (s.Gamepad.Buttons & GamepadButtonFlags.LeftShoulder) != 0);
            ButtonSignal(9, GamepadButtonFlags.RightShoulder, (s.Gamepad.Buttons & GamepadButtonFlags.RightShoulder) != 0);
            ButtonSignal(10, GamepadButtonFlags.A, (s.Gamepad.Buttons & GamepadButtonFlags.A) != 0);
            ButtonSignal(11, GamepadButtonFlags.B, (s.Gamepad.Buttons & GamepadButtonFlags.B) != 0);
            ButtonSignal(12, GamepadButtonFlags.X, (s.Gamepad.Buttons & GamepadButtonFlags.X) != 0);
            ButtonSignal(13, GamepadButtonFlags.Y, (s.Gamepad.Buttons & GamepadButtonFlags.Y) != 0);

            Thread.Sleep(1);
        }
    }

    private void ButtonSignal(int btnIdx, GamepadButtonFlags gamepadButton, bool pressed)
    {
        bool wasPressed = buttonsBools[btnIdx];
        buttonsBools[btnIdx] = pressed;

        if (wasPressed && !pressed)
            this.JoystickButtonUp(gamepadButton);

        if (!wasPressed && pressed)
            this.JoystickButtonDown(gamepadButton);
    }
}
    
24.03.2014 / 18:41