🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

SDL 2.0.x game controller events not working.

Started by
0 comments, last by blueshogun96 8 years, 11 months ago

I don't know why this doesn't work, especially when the documentation tells me exactly how it's done. It appears to me that SDL events don't always work; I mean, I've had instances where NONE of the event types would be processesed for a few days, then suddenly start working again. I dunno, what the frell is going on?

This should be rather straight forward, but I guess not. This is the code I use to handle gamepads and such...


#include "KeGamepad.h"
#include <vector>


/* Gamepad handle */
class CKeGamepadHandle
{
public:
	CKeGamepadHandle() : game_controller(NULL), haptic(NULL), joystick_id(0), id(0), connected(No) {}
	~CKeGamepadHandle()
	{
		if( haptic )
			SDL_HapticClose( haptic );

		SDL_GameControllerClose( game_controller );
	}

	SDL_GameController* game_controller;
	SDL_Haptic*			haptic;
	SDL_JoystickID		joystick_id;
	KeGamepadState		state;
	int					id;
	bool				connected;
};

/* Gamepad handles */
std::vector<CKeGamepadHandle> GamepadHandles;


/*
 * Name: KeInitializeGamepads
 * Desc: Initializes the gamepad API for the target platform.
 */
bool KeInitializeGamepads()
{
	int ret = 0;

	/* Initialize SDL2 gamepad API */
	ret = SDL_InitSubSystem( SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC );
	if( ret != 0 )
		DISPDBG( KE_ERROR, "An error has occured initializing SDL controller API!" );

	return ret;
}


/*
 * Name: KeUnintializeGamepads
 * Desc: Uninitializes the above.
 */
void KeUninitializeGamepads()
{
	/* Kill all open gamepad devices */
	GamepadHandles.empty();

	/* Uninitialize the gamepad API */
	SDL_QuitSubSystem( SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC );
}


/*
 * Name: KeOnGamepadAdded
 * Desc: Called when a gamepad has been added/attached.
 */
void KeOnGamepadAdded( int device_id )
{
	CKeGamepadHandle handle;

	/* Attempt to open this device */
	handle.game_controller = SDL_GameControllerOpen( device_id );

	/* Get the joystick instance ID */
	SDL_Joystick* j = SDL_GameControllerGetJoystick( handle.game_controller );
	handle.joystick_id = SDL_JoystickInstanceID(j);
	handle.connected = Yes;
	handle.id = device_id;

	ZeroMemory( &handle.state, sizeof( KeGamepadState ) );

	/* Is this a haptic device? */
	if( SDL_JoystickIsHaptic(j) )
	{
		handle.haptic = SDL_HapticOpenFromJoystick(j);

		/* Is this a rumble supported haptic feature? */
		if( SDL_HapticRumbleSupported( handle.haptic ) )
		{
			/* Can we initialize the rumble feature? */
			if( SDL_HapticRumbleInit( handle.haptic ) )
			{
				/* An error occurred */
				SDL_HapticClose( handle.haptic );
				handle.haptic = 0;

				DISPDBG( KE_WARNING, "Error initializing rumble feature for gamepad! "
					"Device ID:" << device_id <<
					"Joystick ID: " << handle.joystick_id << 
					"Error code:" << SDL_GetError() );
			}
		}
		else
		{
			/* This is not a rumble haptic */
			SDL_HapticClose( handle.haptic );
			handle.haptic = 0;
		}
	}

	/* Add this to the list of handles */
	GamepadHandles.push_back(handle);
}


/* 
 * Name: KeOnGamepadButtonPress
 * Desc: Called when a gamepad button has been pressed
 */
void KeOnGamepadButtonPress( int device_id, void* context )
{
	SDL_ControllerButtonEvent* event = static_cast<SDL_ControllerButtonEvent*>( context );

	/* Search for the specified device and update the button info */

	std::vector<CKeGamepadHandle>::iterator i = GamepadHandles.begin();

	while( i != GamepadHandles.end() )
	{
		if( i->id == device_id )
		{
			i->state.buttons[event->button].pressed = event->state;
			i->state.buttons[event->button].timestamp = event->timestamp;

			return;
		}

		++i;
	}
}


/*
 * Name: KeOnGamepadRemoved
 * Desc: Called when a gamepad has been removed/dettached.
 */
void KeOnGamepadRemoved( int device_id )
{
	/* Search for this ID, if we find it, go ahead and remove it after unintializing it. */

	std::vector<CKeGamepadHandle>::iterator i = GamepadHandles.begin();

	while( i != GamepadHandles.end() )
	{
		if( i->id == device_id )
		{
			GamepadHandles.erase(i);

			return;
		}

		++i;
	}

}


/*
 * Name: KeGetGamepadState
 * Desc: Returns the input state of the specified gamepad
 */
bool KeGetGamepadState( int device_id, KeGamepadState* gamepad )
{
	std::vector<CKeGamepadHandle>::iterator i = GamepadHandles.begin();

	while( i != GamepadHandles.end() )
	{
		if( i->id == device_id )
		{
			memmove( gamepad, &i->state, sizeof( KeGamepadState ) );

			return true;
		}

		++i;
	}

	return false;
}

And this is the event handling loop:


/*
 * Name: KeProcessEvents
 * Desc: Handles system events during the application's lifetime.
 * TODO: Setup callbacks for each event type.
 */
void KeProcessEvents()
{
    //ke_rdtsc();
	SDL_Event   event;
    
    /* Check for any SDL supported events */
    while( SDL_PollEvent( &event ) )
    {
        /* Respond to events accordingly */
        switch( event.type )
        {
            case SDL_QUIT:
				quitting = Yes;
                break;
                
            case SDL_KEYDOWN:
            case SDL_KEYUP:
                KeProcessKeyEvent( &event );
                KeOnKeyboard( KeGetContextPointer(), &event );
                break;
                
            case SDL_MOUSEBUTTONDOWN:
            case SDL_MOUSEBUTTONUP:
                KeProcessMouseEvent( &event );
                KeOnMouse( KeGetContextPointer(), &event );
                break;
                
            case SDL_MOUSEMOTION:
                KeProcessMouseEvent( &event );
                break;
                
            case SDL_CONTROLLERBUTTONDOWN:
            case SDL_CONTROLLERBUTTONUP:
                KeOnGamepad( KeGetContextPointer(), &event );
				KeOnGamepadButtonPress( event.cbutton.which, &event.cbutton );
                break;

			case SDL_CONTROLLERAXISMOTION:
				break;

			case SDL_CONTROLLERDEVICEADDED:
				KeOnGamepadAdded( event.cdevice.which );
				break;

			case SDL_CONTROLLERDEVICEREMOVED:
				KeOnGamepadRemoved( event.cdevice.which );
				break;
        }
    }
    
    /* Update keys */
    KeUpdateKeys();
}

Now, I've inserted breakpoints at every controller event, and the only one that gets triggered is SDL_CONTROLLERDEVICEADDED. When I remove a controller, nothing happens, but when I reattach the same one, I get that message triggered. Button presses and axis movement also doesn't work.

I've also had this problem with mouse input, but then all of a sudden, it started working. Maybe this will happen with my gamepad code too. Anyone else experiencing this?

Shogun.

EDIT: I forgot to mention that I'm using both a Playstation 4 and an Xbox 360 controller to test this on. The SDL2 version I'm using is 2.0.3, and I also tried 2.0.4, same results.

Advertisement

Nevermind, it's my own code causing the problem. The deconstructor in my gamepad handle class gets called after adding it to the vector due to it going out of scope. I should have used a smart pointer instead to delay that. Thanks for reading.

Shogun.

This topic is closed to new replies.

Advertisement