Understanding XINPUT_STATE

Started by G_G, September 17, 2011, 02:10:38 am

Previous topic - Next topic

G_G

September 17, 2011, 02:10:38 am Last Edit: September 17, 2011, 02:33:31 am by game_guy
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.

AliveDrive

Quote from: Blizzard on September 09, 2011, 02:26:33 am
The permanent solution for your problem would be to stop hanging out with stupid people.

G_G

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)

Blizzard

*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.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

G_G

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.

Blizzard

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. >_>
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

G_G

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

Ryex

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.
I no longer keep up with posts in the forum very well. If you have a question or comment, about my work, or in general I welcome PM's. if you make a post in one of my threads and I don't reply with in a day or two feel free to PM me and point it out to me.<br /><br />DropBox, the best free file syncing service there is.<br />

G_G

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

ForeverZer0

This is pretty freaking awesome. I'm looking forward to you getting this done. ;)
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

G_G

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.