Chaos Project

Game Development => General Discussion => Topic started by: G_G on September 17, 2011, 02:10:38 am

Title: Understanding XINPUT_STATE
Post by: G_G on September 17, 2011, 02:10:38 am
Using this code in Ruby I've managed to get the XINPUT_STATE while an Xbox 360 controller is plugged in. I've done several tests while moving the left and right analog sticks and pressing several buttons.
require "Win32API"
module GamePad
 @set = Win32API.new("xinput1_3", "XInputSetState", "IP", "V")
 def self.test
   @get = Win32API.new("xinput1_3", "XInputGetState", "IP", "L")
   state = "\0" * 256
   @get.call(0, state)
   p state.unpack("C*")
 end
end
GamePad.test
gets
exit


After unpacking the pointer, I discovered that the first slot in the array seems to be the Left analog stick. I've also discovered that the Start and Back button share the 5th slot. Start's value is 16 while Back's value is 32. Hold both of them together the value becomes 48. The 6th slot are the left and right bumpers. Left being 1 and Right being 2. In fact I'm positive all buttons besides the Start and Select are on the 6th slot. So I've managed to successfully use the GetState but now I want to use the SetState function to be able to use the Vibration control. And since I need to pass an actual Vibration structure as a parameter I'm completely lost.

As far as my experimenting is going, it seems to be going well. My overall goal is to see how well I can get the xbox controller to work with Ruby.

Also discovered the directional pad is in the 5 slot. It seems the XINPUT_STATE only has 16 values. It doesn't seem to be generating anything past that.
Title: Re: Understanding XINPUT_STATE
Post by: AliveDrive on September 17, 2011, 02:34:29 am
Dude! Baller.
Title: Re: Understanding XINPUT_STATE
Post by: G_G on September 17, 2011, 03:09:57 am
So I created a dynamic library. Here's the main file.
#include "stdafx.h"
#include <cstdlib>
#include <ctime>
#include <ctype.h>
#include <math.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "XboxC.h"

CXBOXController* controller;
extern "C" _declspec (dllexport) BOOL Vibrate(int player, long left, long right)
{
controller = new CXBOXController(player);
controller->Vibrate(left, right);
   return true;
}

extern "C" _declspec (dllexport) BOOL Connected(int player)
{
controller = new CXBOXController(player);
return controller->IsConnected();
}


Then I have my CXBOXController header and class files with simple functions to handle XInput and its structures, making it easy to use with Ruby. In order to actually use this you need the DirectX SDK installed since it has the XInput.dll. Oh and this ruby snippet set off the vibration in my controller.
module GamePad
  @vibrate = Win32API.new("RGSSXbox.dll", "Vibrate", "ILL", "V")
  def self.vibrate(left, right)
    @vibrate.call(1, left, right)
  end
end
GamePad.vibrate(65355, 65355)
Title: Re: Understanding XINPUT_STATE
Post by: Blizzard on September 17, 2011, 03:19:45 am
*scratches head* Why don't you look up on the Internet how XInput works and it used? I've implemented XInput in C++ 2 months ago and it took me a day with the proper documentation I found online.
Title: Re: Understanding XINPUT_STATE
Post by: G_G on September 17, 2011, 03:22:45 am
I have been. I was trying to implement it into Ruby using Win32API calls. Which really wasn't going to work in the long run so I realized I needed to create my own library to basically manage things between XInput and Ruby using exported functions. And the fact C++ is finally making some sense to me. Its sill confusing to me in every aspect but bits and pieces are coming.
Title: Re: Understanding XINPUT_STATE
Post by: Blizzard on September 17, 2011, 03:28:29 am
That sounds great. :) Keep working. Though, it'd be a lot more useful if you would work on the Python portion of ARC right now as I have most of the C++ code done already. >_>
Title: Re: Understanding XINPUT_STATE
Post by: G_G on September 17, 2011, 03:32:23 am
Quote from: Blizzard on September 17, 2011, 03:28:29 am
That sounds great. :) Keep working. Though, it'd be a lot more useful if you would work on the Python portion of ARC right now as I have most of the C++ code done already. >_>


Always ruining my fun...:V I'll do some python work tomorrow and have Ryex set me on some tasks. I'm probably the most useless member right now. xD
Title: Re: Understanding XINPUT_STATE
Post by: Ryex on September 17, 2011, 04:32:11 am
Quote from: game_guy on September 17, 2011, 03:32:23 am
I'll do some python work tomorrow and have Ryex set me on some tasks. I'm probably the most useless member right now. xD


you said it not me. I've actually been wanting to say that but didn't want to be a hypocrite. after all I basically didn't work on it for 4 months.
Title: Re: Understanding XINPUT_STATE
Post by: G_G on September 18, 2011, 02:52:47 am
So I was messing around with this even more. I managed to get Input.press? done and Input.trigger? done for gamepad input. The trigger was tricky but I did finally get it. However, I'm curious if the way I did it is efficient so if someone could maybe over look this? Basically XInput doesn't have a trigger. It just tells you if the button is being pressed or not. What I basically did was check to see if the button has been pressed. It then won't update until its been released.

Here's the variables I have.
module GamePad
  @vibrate = Win32API.new("RGSSXbox.dll", "Vibrate", "ILL", "V")
  @connected = Win32API.new("RGSSXbox.dll", "Connected", "I", "L")
  @pressed = Win32API.new("RGSSXbox.dll", "Pressed", "II", "L")
  @duration = [0, 0, 0, 0]
  @triggered = {}
end


Here's the trigger code.
  def self.trigger?(player, button)
    return false if @triggered[button] == true
    press = @pressed.call(player, button)
    if press > 0
      @triggered[button] = true
      return true
    end
    return false
  end


Then here's the update method.
  def self.update
    @duration.each_index{|i|
      if @duration[i] >= 0
        @duration[i] -= 1
        self.stop(i + 1) if @duration[i] <= 0
      end}
    @triggered.each_key {|button|
      if @triggered[button] == true
        trigger = @pressed.call(1, button)
        @triggered[button] = false if trigger == 0
      end}
  end


But all in all, this works. :naughty:
if GamePad.trigger?(GamePad::A)
  # insert naughty stuff here
end


Here's the full GamePad module + a modified Input module.
Spoiler: ShowHide
module GamePad
  DPAD_UP         = 0x00000001
  DPAD_DOWN       = 0x00000002
  DPAD_LEFT       = 0x00000004
  DPAD_RIGHT      = 0x00000008
  START           = 0x00000010
  BACK            = 0x00000020
  LEFT_THUMB      = 0x00000040
  RIGHT_THUMB     = 0x00000080
  LEFT_SHOULDER   = 0x0100
  RIGHT_SHOULDER  = 0x0200
  A               = 0x1000
  B               = 0x2000
  X               = 0x4000
  Y               = 0x8000
  Keys = {
    Input::C => GamePad::A,
    Input::B => GamePad::B,
    Input::DOWN => GamePad::DPAD_DOWN,
  }
  @vibrate = Win32API.new("RGSSXbox.dll", "Vibrate", "ILL", "V")
  @connected = Win32API.new("RGSSXbox.dll", "Connected", "I", "L")
  @pressed = Win32API.new("RGSSXbox.dll", "Pressed", "II", "L")
  @duration = [0, 0, 0, 0]
  @triggered = {}
  def self.vibrate(player, left, right, duration = 0)
    player = player.clamp(1, 4)
    @vibrate.call(player, left, right)
    @duration[player - 1] = duration * 40 if duration > 0
  end
  def self.stop(player)
    player = player.clamp(1, 4)
    @vibrate.call(player, 0, 0)
  end
  def self.update
    @duration.each_index{|i|
      if @duration[i] >= 0
        @duration[i] -= 1
        self.stop(i + 1) if @duration[i] <= 0
      end}
    @triggered.each_key {|button|
      if @triggered[button] == true
        trigger = @pressed.call(1, button)
        @triggered[button] = false if trigger == 0
      end}
  end
  def self.pressed?(player, button)
    bool = @pressed.call(player, button)
    if bool == 0
      return false
    else
      return true
    end
  end
  def self.trigger?(player, button)
    return false if @triggered[button] == true
    press = @pressed.call(player, button)
    if press > 0
      @triggered[button] = true
      return true
    end
    return false
  end
end
module Input
  class << self
    alias gg_upd_input_gamepad_lat update
    alias gg_upd_input_dir4_lat dir4
    alias gg_upd_input_trigger_lat trigger?
  end
  def self.trigger?(button)
    if GamePad::Keys[button] != nil
      return true if GamePad.trigger?(1, GamePad::Keys[button])
    end
    return gg_upd_input_trigger_lat(button)
  end
  def self.update
    gg_upd_input_gamepad_lat
    GamePad.update
  end
  def self.dir4
    if GamePad.pressed?(1, GamePad::DPAD_DOWN)
      return 2
    elsif GamePad.pressed?(1, GamePad::DPAD_LEFT)
      return 4
    elsif GamePad.pressed?(1, GamePad::DPAD_RIGHT)
      return 6
    elsif GamePad.pressed?(1, GamePad::DPAD_UP)
      return 8
    end
    return gg_upd_input_dir4_lat
  end
end
Title: Re: Understanding XINPUT_STATE
Post by: ForeverZer0 on September 18, 2011, 03:20:08 am
This is pretty freaking awesome. I'm looking forward to you getting this done. ;)
Title: Re: Understanding XINPUT_STATE
Post by: G_G on September 18, 2011, 03:30:46 am
Thanks! Oh and I just got multi-button presses figured out too. First I fixed a few mistakes I had with the button checking. But you can check multiple buttons like so.
if GamePad.trigger?(GamePad::A + GamePad::B)
  #code here
end


It'll only execute if A and B are both pressed. Working in GamePad.repeat? right now. After that its analog sticks time. Been having trouble with those.