Esempio n. 1
0
unsigned int SdlJoystick::getCapabilities()
{
#if SDL_VERSION_ATLEAST(1,3,0)
   if (mHaptic) return SDL_HapticQuery(mHaptic);
#endif
   return 0;
}
Esempio n. 2
0
/**
 * @brief Initializes force feedback for the loaded device.
 */
static void joystick_initHaptic (void)
{
#if SDL_VERSION_ATLEAST(1,3,0)
    if (has_haptic && SDL_JoystickIsHaptic(joystick)) {

        /* Close haptic if already open. */
        if (haptic != NULL) {
            SDL_HapticClose(haptic);
            haptic = NULL;
        }

        /* Try to create haptic device. */
        haptic = SDL_HapticOpenFromJoystick(joystick);
        if (haptic == NULL) {
            WARN("Unable to initialize force feedback: %s", SDL_GetError());
            return;
        }

        /* Check to see what it supports. */
        haptic_query = SDL_HapticQuery(haptic);
        if (!(haptic_query & SDL_HAPTIC_SINE)) {
            SDL_HapticClose(haptic);
            haptic = NULL;
            return;
        }

        DEBUG("   force feedback enabled");
    }
#endif /* SDL_VERSION_ATLEAST(1,3,0) */
}
Esempio n. 3
0
bool CSDLPad::OpenDevice()
{
	m_pSDLDevice = SDL_JoystickOpen(m_deviceNo);
	if (m_pSDLDevice)
	{
		// SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK) should skip the accelerometer when 
		// enumerating joysticks, but it doesn't seem to work.  Manually check for it here 
		// and close it.
		// FIXME: Need a more robust way of detecting this (or get SetHint() to work).
		if (strcmp(SDL_JoystickName(m_pSDLDevice), "Android Accelerometer") == 0)
		{
			SDL_JoystickClose(m_pSDLDevice);
			return false;
		}
		m_connected = true;
#if defined(SDL_USE_HAPTIC_FEEDBACK)
		m_pHapticDevice = SDL_HapticOpenFromJoystick(m_pSDLDevice);
		if (m_pHapticDevice && (SDL_HapticQuery(m_pHapticDevice) & SDL_HAPTIC_LEFTRIGHT))
		{
			m_supportsFeedback = true;
		}
		else
		{
			if (m_pHapticDevice) SDL_HapticClose(m_pHapticDevice);
			m_supportsFeedback = false;
		}
#else
		m_supportsFeedback = false;
#endif
		m_curHapticEffect = -1;
		gEnv->pLog->Log("CSDLPad - Gamepad [%d] supports feedback (Y/N): %s", m_deviceNo, m_supportsFeedback ? "Y" : "N");
	}
	return m_pSDLDevice != NULL;
}
Esempio n. 4
0
/*
 * Displays information about the haptic device.
 */
static void
HapticPrintSupported(SDL_Haptic * haptic)
{
    unsigned int supported;

    supported = SDL_HapticQuery(haptic);
    SDL_Log("   Supported effects [%d effects, %d playing]:\n",
           SDL_HapticNumEffects(haptic), SDL_HapticNumEffectsPlaying(haptic));
    if (supported & SDL_HAPTIC_CONSTANT)
        SDL_Log("      constant\n");
    if (supported & SDL_HAPTIC_SINE)
        SDL_Log("      sine\n");
    /* !!! FIXME: put this back when we have more bits in 2.1 */
    /* if (supported & SDL_HAPTIC_SQUARE)
        SDL_Log("      square\n"); */
    if (supported & SDL_HAPTIC_TRIANGLE)
        SDL_Log("      triangle\n");
    if (supported & SDL_HAPTIC_SAWTOOTHUP)
        SDL_Log("      sawtoothup\n");
    if (supported & SDL_HAPTIC_SAWTOOTHDOWN)
        SDL_Log("      sawtoothdown\n");
    if (supported & SDL_HAPTIC_RAMP)
        SDL_Log("      ramp\n");
    if (supported & SDL_HAPTIC_FRICTION)
        SDL_Log("      friction\n");
    if (supported & SDL_HAPTIC_SPRING)
        SDL_Log("      spring\n");
    if (supported & SDL_HAPTIC_DAMPER)
        SDL_Log("      damper\n");
    if (supported & SDL_HAPTIC_INERTIA)
        SDL_Log("      inertia\n");
    if (supported & SDL_HAPTIC_CUSTOM)
        SDL_Log("      custom\n");
    if (supported & SDL_HAPTIC_LEFTRIGHT)
        SDL_Log("      left/right\n");
    SDL_Log("   Supported capabilities:\n");
    if (supported & SDL_HAPTIC_GAIN)
        SDL_Log("      gain\n");
    if (supported & SDL_HAPTIC_AUTOCENTER)
        SDL_Log("      autocenter\n");
    if (supported & SDL_HAPTIC_STATUS)
        SDL_Log("      status\n");
}
Esempio n. 5
0
bool Joystick::isVibrationSupported()
{
	if (!checkCreateHaptic())
		return false;

	unsigned int features = SDL_HapticQuery(haptic);

	if ((features & SDL_HAPTIC_LEFTRIGHT) != 0)
		return true;

	// Some gamepad drivers only support left/right motors via a custom effect.
	if (isGamepad() && (features & SDL_HAPTIC_CUSTOM) != 0)
		return true;

	// Test for simple sine wave support as a last resort.
	if ((features & SDL_HAPTIC_SINE) != 0)
		return true;

	return false;
}
Esempio n. 6
0
/*
 * Displays information about the haptic device.
 */
static void
HapticPrintSupported(SDL_Haptic * haptic)
{
    unsigned int supported;

    supported = SDL_HapticQuery(haptic);
    printf("   Supported effects [%d effects, %d playing]:\n",
           SDL_HapticNumEffects(haptic), SDL_HapticNumEffectsPlaying(haptic));
    if (supported & SDL_HAPTIC_CONSTANT)
        printf("      constant\n");
    if (supported & SDL_HAPTIC_SINE)
        printf("      sine\n");
    if (supported & SDL_HAPTIC_SQUARE)
        printf("      square\n");
    if (supported & SDL_HAPTIC_TRIANGLE)
        printf("      triangle\n");
    if (supported & SDL_HAPTIC_SAWTOOTHUP)
        printf("      sawtoothup\n");
    if (supported & SDL_HAPTIC_SAWTOOTHDOWN)
        printf("      sawtoothdown\n");
    if (supported & SDL_HAPTIC_RAMP)
        printf("      ramp\n");
    if (supported & SDL_HAPTIC_FRICTION)
        printf("      friction\n");
    if (supported & SDL_HAPTIC_SPRING)
        printf("      spring\n");
    if (supported & SDL_HAPTIC_DAMPER)
        printf("      damper\n");
    if (supported & SDL_HAPTIC_INERTIA)
        printf("      intertia\n");
    if (supported & SDL_HAPTIC_CUSTOM)
        printf("      custom\n");
    printf("   Supported capabilities:\n");
    if (supported & SDL_HAPTIC_GAIN)
        printf("      gain\n");
    if (supported & SDL_HAPTIC_AUTOCENTER)
        printf("      autocenter\n");
    if (supported & SDL_HAPTIC_STATUS)
        printf("      status\n");
}
Esempio n. 7
0
Joystick::Joystick(SDL_Joystick* const joystick, const int sdl_index, const unsigned int index)
	: m_joystick(joystick)
	, m_sdl_index(sdl_index)
	, m_index(index)
{
	// really bad HACKS:
	// to not use SDL for an XInput device
	// too many people on the forums pick the SDL device and ask:
	// "why don't my 360 gamepad triggers/rumble work correctly"
#ifdef _WIN32
	// checking the name is probably good (and hacky) enough
	// but i'll double check with the num of buttons/axes
	std::string lcasename = GetName();
	std::transform(lcasename.begin(), lcasename.end(), lcasename.begin(), tolower);

	if ((std::string::npos != lcasename.find("xbox 360"))
		&& (10 == SDL_JoystickNumButtons(joystick))
		&& (5 == SDL_JoystickNumAxes(joystick))
		&& (1 == SDL_JoystickNumHats(joystick))
		&& (0 == SDL_JoystickNumBalls(joystick))
		)
	{
		// this device won't be used
		return;
	}
#endif

	// get buttons
	for (u8 i = 0; i != SDL_JoystickNumButtons(m_joystick); ++i)
		AddInput(new Button(i, m_joystick));
	
	// get hats
	for (u8 i = 0; i != SDL_JoystickNumHats(m_joystick); ++i)
	{
		// each hat gets 4 input instances associated with it, (up down left right)
		for (u8 d = 0; d != 4; ++d)
			AddInput(new Hat(i, m_joystick, d));
	}

	// get axes
	for (u8 i = 0; i != SDL_JoystickNumAxes(m_joystick); ++i)
	{
		// each axis gets a negative and a positive input instance associated with it
		AddAnalogInputs(new Axis(i, m_joystick, -32768),
			new Axis(i, m_joystick, 32767));
	}

#ifdef USE_SDL_HAPTIC
	// try to get supported ff effects
	m_haptic = SDL_HapticOpenFromJoystick( m_joystick );
	if (m_haptic)
	{
		//SDL_HapticSetGain( m_haptic, 1000 );
		//SDL_HapticSetAutocenter( m_haptic, 0 );

		const unsigned int supported_effects = SDL_HapticQuery( m_haptic );

		// constant effect
		if (supported_effects & SDL_HAPTIC_CONSTANT)
		{
			m_state_out.push_back(EffectIDState());
			AddOutput(new ConstantEffect(m_state_out.back()));
		}

		// ramp effect
		if (supported_effects & SDL_HAPTIC_RAMP)
		{
			m_state_out.push_back(EffectIDState());
			AddOutput(new RampEffect(m_state_out.back()));
		}

		// sine effect
		if (supported_effects & SDL_HAPTIC_SINE)
		{
			m_state_out.push_back(EffectIDState());
			AddOutput(new SineEffect(m_state_out.back()));
		}

		// square effect
		if (supported_effects & SDL_HAPTIC_SQUARE)
		{
			m_state_out.push_back(EffectIDState());
			AddOutput(new SquareEffect(m_state_out.back()));
		}

		// triangle effect
		if (supported_effects & SDL_HAPTIC_TRIANGLE)
		{
			m_state_out.push_back(EffectIDState());
			AddOutput(new TriangleEffect(m_state_out.back()));
		}
	}
#endif

}
Esempio n. 8
0
/**
 * @brief The entry point of this force feedback demo.
 * @param[in] argc Number of arguments.
 * @param[in] argv Array of argc arguments.
 */
int
main(int argc, char **argv)
{
    int i;
    char *name;
    int index;
    SDL_HapticEffect efx[5];
    int id[5];
    int nefx;
    unsigned int supported;

    name = NULL;
    index = -1;
    if (argc > 1) {
        name = argv[1];
        if ((strcmp(name, "--help") == 0) || (strcmp(name, "-h") == 0)) {
            printf("USAGE: %s [device]\n"
                   "If device is a two-digit number it'll use it as an index, otherwise\n"
                   "it'll use it as if it were part of the device's name.\n",
                   argv[0]);
            return 0;
        }

        i = strlen(name);
        if ((i < 3) && isdigit(name[0]) && ((i == 1) || isdigit(name[1]))) {
            index = atoi(name);
            name = NULL;
        }
    }

    /* Initialize the force feedbackness */
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_JOYSTICK |
             SDL_INIT_HAPTIC);
    printf("%d Haptic devices detected.\n", SDL_NumHaptics());
    if (SDL_NumHaptics() > 0) {
        /* We'll just use index or the first force feedback device found */
        if (name == NULL) {
            i = (index != -1) ? index : 0;
        }
        /* Try to find matching device */
        else {
            for (i = 0; i < SDL_NumHaptics(); i++) {
                if (strstr(SDL_HapticName(i), name) != NULL)
                    break;
            }

            if (i >= SDL_NumHaptics()) {
                printf("Unable to find device matching '%s', aborting.\n",
                       name);
                return 1;
            }
        }

        haptic = SDL_HapticOpen(i);
        if (haptic == NULL) {
            printf("Unable to create the haptic device: %s\n",
                   SDL_GetError());
            return 1;
        }
        printf("Device: %s\n", SDL_HapticName(i));
        HapticPrintSupported(haptic);
    } else {
        printf("No Haptic devices found!\n");
        return 1;
    }

    /* We only want force feedback errors. */
    SDL_ClearError();

    /* Create effects. */
    memset(&efx, 0, sizeof(efx));
    nefx = 0;
    supported = SDL_HapticQuery(haptic);

    printf("\nUploading effects\n");
    /* First we'll try a SINE effect. */
    if (supported & SDL_HAPTIC_SINE) {
        printf("   effect %d: Sine Wave\n", nefx);
        efx[nefx].type = SDL_HAPTIC_SINE;
        efx[nefx].periodic.period = 1000;
        efx[nefx].periodic.magnitude = 0x4000;
        efx[nefx].periodic.length = 5000;
        efx[nefx].periodic.attack_length = 1000;
        efx[nefx].periodic.fade_length = 1000;
        id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
        if (id[nefx] < 0) {
            printf("UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
            abort_execution();
        }
        nefx++;
    }
    /* Now we'll try a SAWTOOTHUP */
    if (supported & SDL_HAPTIC_SAWTOOTHUP) {
        printf("   effect %d: Sawtooth Up\n", nefx);
        efx[nefx].type = SDL_HAPTIC_SQUARE;
        efx[nefx].periodic.period = 500;
        efx[nefx].periodic.magnitude = 0x5000;
        efx[nefx].periodic.length = 5000;
        efx[nefx].periodic.attack_length = 1000;
        efx[nefx].periodic.fade_length = 1000;
        id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
        if (id[nefx] < 0) {
            printf("UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
            abort_execution();
        }
        nefx++;
    }
    /* Now the classical constant effect. */
    if (supported & SDL_HAPTIC_CONSTANT) {
        printf("   effect %d: Constant Force\n", nefx);
        efx[nefx].type = SDL_HAPTIC_CONSTANT;
        efx[nefx].constant.direction.type = SDL_HAPTIC_POLAR;
        efx[nefx].constant.direction.dir[0] = 20000;    /* Force comes from the south-west. */
        efx[nefx].constant.length = 5000;
        efx[nefx].constant.level = 0x6000;
        efx[nefx].constant.attack_length = 1000;
        efx[nefx].constant.fade_length = 1000;
        id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
        if (id[nefx] < 0) {
            printf("UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
            abort_execution();
        }
        nefx++;
    }
    /* The cute spring effect. */
    if (supported & SDL_HAPTIC_SPRING) {
        printf("   effect %d: Condition Spring\n", nefx);
        efx[nefx].type = SDL_HAPTIC_SPRING;
        efx[nefx].condition.length = 5000;
        for (i = 0; i < SDL_HapticNumAxes(haptic); i++) {
            efx[nefx].condition.right_sat[i] = 0x7FFF;
            efx[nefx].condition.left_sat[i] = 0x7FFF;
            efx[nefx].condition.right_coeff[i] = 0x2000;
            efx[nefx].condition.left_coeff[i] = 0x2000;
            efx[nefx].condition.center[i] = 0x1000;     /* Displace the center for it to move. */
        }
        id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
        if (id[nefx] < 0) {
            printf("UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
            abort_execution();
        }
        nefx++;
    }
    /* The pretty awesome inertia effect. */
    if (supported & SDL_HAPTIC_INERTIA) {
        printf("   effect %d: Condition Inertia\n", nefx);
        efx[nefx].type = SDL_HAPTIC_SPRING;
        efx[nefx].condition.length = 5000;
        for (i = 0; i < SDL_HapticNumAxes(haptic); i++) {
            efx[nefx].condition.right_sat[i] = 0x7FFF;
            efx[nefx].condition.left_sat[i] = 0x7FFF;
            efx[nefx].condition.right_coeff[i] = 0x2000;
            efx[nefx].condition.left_coeff[i] = 0x2000;
        }
        id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
        if (id[nefx] < 0) {
            printf("UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
            abort_execution();
        }
        nefx++;
    }

    printf
        ("\nNow playing effects for 5 seconds each with 1 second delay between\n");
    for (i = 0; i < nefx; i++) {
        printf("   Playing effect %d\n", i);
        SDL_HapticRunEffect(haptic, id[i], 1);
        SDL_Delay(6000);        /* Effects only have length 5000 */
    }

    /* Quit */
    if (haptic != NULL)
        SDL_HapticClose(haptic);
    SDL_Quit();

    return 0;
}
Esempio n. 9
0
static int
IN_Haptic_Effects_To_Id(int haptic_effect, int effect_volume, int effect_x, int effect_y, int effect_z)
{
	if ((SDL_HapticQuery(joystick_haptic) & SDL_HAPTIC_SINE)==0)
		return -1;

	int hapric_volume = joy_haptic_magnitude->value * effect_volume * 16; // * 128 = 32767 max strength;
	if (hapric_volume > 255)
		hapric_volume = 255;
	else if (hapric_volume < 0)
		hapric_volume = 0;

	switch(haptic_effect) {
	case HAPTIC_EFFECT_MENY:
	case HAPTIC_EFFECT_TRAPCOCK:
	case HAPTIC_EFFECT_STEP:
		/* North */
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 48,
			200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
			100/* Takes 0.1 second to fade away */);
	case HAPTIC_EFFECT_PAIN:
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 700/* 700 ms*/, hapric_volume * 196,
			300/* 0.3 seconds long */, 200/* Takes 0.2 second to get max strength */,
			200/* Takes 0.2 second to fade away */);
	case HAPTIC_EFFECT_BLASTER:
		/* 30 degrees */
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 64,
			200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
			100/* Takes 0.1 second to fade away */);
	case HAPTIC_EFFECT_HYPER_BLASTER:
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 64,
			200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
			100/* Takes 0.1 second to fade away */);
	case HAPTIC_EFFECT_ETFRIFLE:
		/* 60 degrees */
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 64,
			200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
			100/* Takes 0.1 second to fade away */);
	case HAPTIC_EFFECT_TRACKER:
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 64,
			200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
			100/* Takes 0.1 second to fade away */);
	case HAPTIC_EFFECT_MACHINEGUN:
		/* 90 degrees */
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 800/* 800 ms*/, hapric_volume * 88,
			600/* 0.6 seconds long */, 200/* Takes 0.2 second to get max strength */,
			400/* Takes 0.4 second to fade away */);
	case HAPTIC_EFFECT_SHOTGUN:
		/* 120 degrees */
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 700/* 700 ms*/, hapric_volume * 100,
			500/* 0.5 seconds long */, 100/* Takes 0.1 second to get max strength */,
			200/* Takes 0.2 second to fade away */);
	case HAPTIC_EFFECT_SHOTGUN2:
		/* 150 degrees */
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 700/* 700 ms*/, hapric_volume * 96,
			500/* 0.5 seconds long */, 100/* Takes 0.1 second to get max strength */,
			100/* Takes 0.1 second to fade away */);
	case HAPTIC_EFFECT_SSHOTGUN:
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 700/* 700 ms*/, hapric_volume * 96,
			500/* 0.5 seconds long */, 100/* Takes 0.1 second to get max strength */,
			100/* Takes 0.1 second to fade away */);
	case HAPTIC_EFFECT_RAILGUN:
		/* 180 degrees */
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 700/* 700 ms*/, hapric_volume * 64,
			400/* 0.4 seconds long */, 100/* Takes 0.1 second to get max strength */,
			100/* Takes 0.1 second to fade away */);
	case HAPTIC_EFFECT_ROCKETGUN:
		/* 210 degrees */
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 700/* 700 ms*/, hapric_volume * 128,
			400/* 0.4 seconds long */, 300/* Takes 0.3 second to get max strength */,
			100/* Takes 0.1 second to fade away */);
	case HAPTIC_EFFECT_GRENADE:
		/* 240 degrees */
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 64,
			200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
			100/* Takes 0.1 second to fade away */);
	case HAPTIC_EFFECT_BFG:
		/* 270 degrees */
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 800/* 800 ms*/, hapric_volume * 100,
			600/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
			100/* Takes 0.1 second to fade away */);
	case HAPTIC_EFFECT_PALANX:
		/* 300 degrees */
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 64,
			200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
			100/* Takes 0.1 second to fade away */);
	case HAPTIC_EFFECT_IONRIPPER:
		/* 330 degrees */
		return IN_Haptic_Effect_Init(
			effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 64,
			200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
			100/* Takes 0.1 second to fade away */);
	default:
		return -1;
	}
}
Esempio n. 10
0
bool Joystick::setVibration(float left, float right, float duration)
{
	left = std::min(std::max(left, 0.0f), 1.0f);
	right = std::min(std::max(right, 0.0f), 1.0f);

	if (left == 0.0f && right == 0.0f)
		return setVibration();

	if (!checkCreateHaptic())
		return false;

	Uint32 length = SDL_HAPTIC_INFINITY;
	if (duration >= 0.0f)
	{
		float maxduration = std::numeric_limits<Uint32>::max() / 1000.0f;
		length = Uint32(std::min(duration, maxduration) * 1000);
	}

	bool success = false;
	unsigned int features = SDL_HapticQuery(haptic);
	int axes = SDL_HapticNumAxes(haptic);

	if ((features & SDL_HAPTIC_LEFTRIGHT) != 0)
	{
		memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
		vibration.effect.type = SDL_HAPTIC_LEFTRIGHT;

		vibration.effect.leftright.length = length;
		vibration.effect.leftright.large_magnitude = Uint16(left * LOVE_UINT16_MAX);
		vibration.effect.leftright.small_magnitude = Uint16(right * LOVE_UINT16_MAX);

		success = runVibrationEffect();
	}

	// Some gamepad drivers only give support for controlling individual motors
	// through a custom FF effect.
	if (!success && isGamepad() && (features & SDL_HAPTIC_CUSTOM) && axes == 2)
	{
		// NOTE: this may cause issues with drivers which support custom effects
		// but aren't similar to https://github.com/d235j/360Controller .

		// Custom effect data is clamped to 0x7FFF in SDL.
		vibration.data[0] = vibration.data[2] = Uint16(left * 0x7FFF);
		vibration.data[1] = vibration.data[3] = Uint16(right * 0x7FFF);

		memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
		vibration.effect.type = SDL_HAPTIC_CUSTOM;

		vibration.effect.custom.length = length;
		vibration.effect.custom.channels = 2;
		vibration.effect.custom.period = 10;
		vibration.effect.custom.samples = 2;
		vibration.effect.custom.data = vibration.data;

		success = runVibrationEffect();
	}

	// Fall back to a simple sine wave if all else fails. This only supports a
	// single strength value.
	if (!success && (features & SDL_HAPTIC_SINE) != 0)
	{
		memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
		vibration.effect.type = SDL_HAPTIC_SINE;

		vibration.effect.periodic.length = length;
		vibration.effect.periodic.period = 10;

		float strength = std::max(left, right);
		vibration.effect.periodic.magnitude = Sint16(strength * 0x7FFF);

		success = runVibrationEffect();
	}

	if (success)
	{
		vibration.left = left;
		vibration.right = right;

		if (length == SDL_HAPTIC_INFINITY)
			vibration.endtime = SDL_HAPTIC_INFINITY;
		else
			vibration.endtime = SDL_GetTicks() + length;
	}
	else
	{
		vibration.left = vibration.right = 0.0f;
		vibration.endtime = SDL_HAPTIC_INFINITY;
	}

	return success;
}
Esempio n. 11
0
ForceFeedback::ForceFeedback(
	const std::string & device,
	std::ostream & error_output,
	std::ostream & info_output) :
	device_name(device),
	enabled(true),
	lastforce(0),
	haptic(0),
	effect_id(-1)
{
	// Close haptic if already open.
	if (haptic)
	{
		SDL_HapticClose(haptic);
		haptic = NULL;
	}

	// Do we have force feedback devices?
	int haptic_num = SDL_NumHaptics();
	if (haptic_num == 0)
	{
		info_output << "No force feedback devices found." << std::endl;
		return;
	}
	info_output << "Number of force feedback devices: " << haptic_num << std::endl;

	// Try to create haptic device.
	int haptic_id = 0;
	haptic = SDL_HapticOpen(haptic_id);
	if (!haptic)
	{
		error_output << "Failed to initialize force feedback device: " << SDL_GetError();
		return;
	}

	// Check for constant force support.
	unsigned int haptic_query = SDL_HapticQuery(haptic);
	if (!(haptic_query & SDL_HAPTIC_CONSTANT))
	{
		error_output << "Constant force feedback not supported: " << SDL_GetError();
		return;
	}

	// Create the effect.
	memset(&effect, 0, sizeof(SDL_HapticEffect) ); // 0 is safe default
	effect.type = SDL_HAPTIC_CONSTANT;
	effect.constant.direction.type = SDL_HAPTIC_CARTESIAN; // Using cartesian direction encoding.
	effect.constant.direction.dir[0] = 1; // X position
	effect.constant.direction.dir[1] = 0; // Y position
	effect.constant.length = 0xffff;
	effect.constant.delay = 0;
	effect.constant.button = 0;
	effect.constant.interval = 0;
	effect.constant.level = 0;
	effect.constant.attack_length = 0;
	effect.constant.attack_level = 0;
	effect.constant.fade_length = 0;
	effect.constant.fade_level = 0;

	// Upload the effect.
	effect_id = SDL_HapticNewEffect(haptic, &effect);
	if (effect_id == -1)
	{
		error_output << "Failed to initialize force feedback effect: " << SDL_GetError();
		return;
	}

	info_output << "Force feedback enabled." << std::endl;
}
Esempio n. 12
0
bool SDL2FFBDevice::queryDeviceCapabilities()
{
  unsigned int caps;
  bool hasPeriodic = false;
  bool hasCondition = false;

  caps = SDL_HapticQuery(c_haptic);
  if (caps == 0)
    return false;

  if (caps & SDL_HAPTIC_CONSTANT)
    m_availableEffects.push_back(FFBEffectTypes::CONSTANT);

  if (caps & SDL_HAPTIC_SINE) {
    hasPeriodic = true;
    m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::SINE);
  }
  if (caps & SDL_HAPTIC_TRIANGLE) {
    hasPeriodic = true;
    m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::TRIANGLE);
  }
  if (caps & SDL_HAPTIC_SAWTOOTHUP) {
    hasPeriodic = true;
    m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::SAW_UP);
  }
  if (caps & SDL_HAPTIC_SAWTOOTHDOWN) {
    hasPeriodic = true;
    m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::SAW_DOWN);
  }
  if (hasPeriodic)
    m_availableEffects.push_back(FFBEffectTypes::PERIODIC);

  if (caps & SDL_HAPTIC_RAMP)
    m_availableEffects.push_back(FFBEffectTypes::RAMP);

  if (caps & SDL_HAPTIC_SPRING) {
    hasCondition = true;
    m_availableConditionSubtypes.push_back(ConditionSubtypes::SPRING);
  }
  if (caps & SDL_HAPTIC_DAMPER) {
    hasCondition = true;
    m_availableConditionSubtypes.push_back(ConditionSubtypes::DAMPER);
  }
  if (caps & SDL_HAPTIC_INERTIA) {
    hasCondition = true;
    m_availableConditionSubtypes.push_back(ConditionSubtypes::INERTIA);
  }
  if (caps & SDL_HAPTIC_FRICTION) {
    hasCondition = true;
    m_availableConditionSubtypes.push_back(ConditionSubtypes::FRICTION);
  }

  if (hasCondition)
    m_availableEffects.push_back(FFBEffectTypes::CONDITION);

  if (caps & SDL_HAPTIC_GAIN)
    m_adjustableGain = true;

  if (caps & SDL_HAPTIC_AUTOCENTER)
    m_adjustableAC = true;

  return true;
}
Esempio n. 13
0
// First time (lazy) initialization.
void
gfctrlJoyInit(void)
{
#ifndef SDL_JOYSTICK
    gfctrlJoyPresent = GFCTRL_JOY_NONE;

    for (int index = 0; index < GFCTRL_JOY_NUMBER; index++) {
		if (!Joysticks[index]) {
			Joysticks[index] = new jsJoystick(index);
		}
    
		// Don't configure the joystick if it doesn't work
		if (Joysticks[index]->notWorking()) {
			delete Joysticks[index];
			Joysticks[index] = 0;
		} else {
			gfctrlJoyPresent = GFCTRL_JOY_PRESENT;
		}
    }
#else
#if SDL_MAJOR_VERSION >= 2
    memset(&cfx, 0, sizeof(cfx));

    if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC) < 0) {
#else
    if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
#endif
        GfLogError("Couldn't initialize SDL: %s\n", SDL_GetError());
        gfctrlJoyPresent = GFCTRL_JOY_UNTESTED;
	return;
    }
#if SDL_MAJOR_VERSION >= 2
    // Ignore the joystick events, we will poll directly as it is faster
    SDL_JoystickEventState(SDL_IGNORE);
#endif
    gfctrlJoyPresent = SDL_NumJoysticks();
    if (gfctrlJoyPresent > GFCTRL_JOY_NUMBER) gfctrlJoyPresent = GFCTRL_JOY_NUMBER;

    for (int index = 0; index < gfctrlJoyPresent; index++) {
		if (!Joysticks[index]) {
			Joysticks[index] = SDL_JoystickOpen(index);
		}
    
		// Don't configure the joystick if it doesn't work
		if (Joysticks[index] ==  NULL) {
			GfLogError("Couldn't open joystick %d: %s\n", index, SDL_GetError());
#if SDL_MAJOR_VERSION >= 2
		} else {
			cfx_timeout[index] = 0;
			rfx_timeout[index] = 0;
			
			// Find which Haptic device relates to this joystick
			Haptics[index] = SDL_HapticOpenFromJoystick(Joysticks[index]);

			if (!Haptics[index]) {
				GfLogInfo("Joystick %d does not support haptic\n", index);
				break;
#if 0
			} else {
				// add an CF effect on startup
				gfctrlJoyConstantForce(index, 50000, 9000);
#endif
			}

			// Check for Rumble capability
			if (SDL_HapticRumbleSupported(Haptics[index]) == SDL_TRUE) {
				if (SDL_HapticRumbleInit(Haptics[index]) != 0) 
					GfLogError("Couldn't init rumble on joystick %d: %s\n", index, SDL_GetError());
#if 0
				else
					gfctrlJoyRumble(index, 0.5);
#endif
			}
#endif
                }
     }
#endif
}

#if SDL_JOYSTICK
void
gfctrlJoyConstantForce(int index, unsigned int level, int dir)
{
#if SDL_MAJOR_VERSION >= 2
	if (!Haptics[index]) return;

	if ((SDL_HapticQuery(Haptics[index]) & SDL_HAPTIC_CONSTANT) == 0) return;

	cfx[index].type = SDL_HAPTIC_CONSTANT;
	cfx[index].constant.direction.type = SDL_HAPTIC_POLAR;
	cfx[index].constant.direction.dir[0] = dir;
	cfx[index].constant.length = 1000;
	cfx[index].constant.level = level;
	cfx[index].constant.attack_length = 0;
	cfx[index].constant.fade_length = 1000;

#if __WIN32__
	if (SDL_HapticGetEffectStatus(Haptics[index], id[index]) == SDL_TRUE)
#else
	// Linux SDL doesn't support checking status at the moment :-(
	if (cfx_timeout[index] > SDL_GetTicks())
#endif
		SDL_HapticUpdateEffect(Haptics[index], id[index], &cfx[index]);
	else {
		SDL_HapticDestroyEffect(Haptics[index], id[index]);
		id[index] = SDL_HapticNewEffect(Haptics[index], &cfx[index]);
		SDL_HapticRunEffect(Haptics[index], id[index], 1);
	}

	cfx_timeout[index] = SDL_GetTicks() + cfx[index].constant.length;
#endif
}

void
gfctrlJoyRumble(int index, float level)
{
#if SDL_MAJOR_VERSION >= 2
	if (!Haptics[index]) return;

	if (SDL_HapticRumbleSupported(Haptics[index]) != SDL_TRUE) return;

	// we have to stop the rumble before updating
	if (rfx_timeout[index] > SDL_GetTicks()) {
		if (SDL_HapticRumbleStop(Haptics[index]) != 0)
			GfLogError("Failed to stop rumble: %s\n", SDL_GetError() );
	}

	if (SDL_HapticRumblePlay(Haptics[index], level, 100) != 0)
		GfLogError("Failed to play rumble: %s\n", SDL_GetError() );

	rfx_timeout[index] = SDL_GetTicks() + 100;
#endif
}
#endif

// Shutdown time.
void
gfctrlJoyShutdown(void)
{
   if (gfctrlJoyPresent != GFCTRL_JOY_UNTESTED)
#ifndef SDL_JOYSTICK
	
		for (int index = 0; index < GFCTRL_JOY_NUMBER; index++)
			delete Joysticks[index];

	
#else
      for (int index = 0; index < gfctrlJoyPresent; index++) {
			SDL_JoystickClose(Joysticks[index]);
			Joysticks[index] = NULL;
#if SDL_MAJOR_VERSION >= 2
			if (Haptics[index]) {
				SDL_HapticClose(Haptics[index]);
				Haptics[index] = NULL;
			}			
#endif
		}
#endif
      gfctrlJoyPresent = GFCTRL_JOY_UNTESTED;
}

/** Create the joystick control
    @ingroup	ctrl
    @return	pointer on a tCtrlJoyInfo structure
		<br>0 .. if no joystick present
    @note	call GfctrlJoyRelease to free the tCtrlJoyInfo structure
    @see	GfctrlJoyRelease
    @see	tCtrlJoyInfo
*/
tCtrlJoyInfo *
GfctrlJoyCreate(void)
{
    if (gfctrlJoyPresent == GFCTRL_JOY_UNTESTED)
       gfctrlJoyInit();

    tCtrlJoyInfo* joyInfo = (tCtrlJoyInfo *)calloc(1, sizeof(tCtrlJoyInfo));

#if SDL_JOYSTICK
    joyInfoCopy = joyInfo;
#endif

    return joyInfo;
}

/** Release the tCtrlJoyInfo structure
    @ingroup	ctrl
    @param	joyInfo	joystick structure
    @return	none
*/
void
GfctrlJoyRelease(tCtrlJoyInfo *joyInfo)
{
    freez(joyInfo);
}
Esempio n. 14
0
/**
 * @brief The entry point of this force feedback demo.
 * @param[in] argc Number of arguments.
 * @param[in] argv Array of argc arguments.
 */
int
main(int argc, char **argv)
{
    int i;
    char *name;
    int index;
    SDL_HapticEffect efx[9];
    int id[9];
    int nefx;
    unsigned int supported;

    /* Enable standard application logging */
    SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);

    name = NULL;
    index = -1;
    if (argc > 1) {
        name = argv[1];
        if ((strcmp(name, "--help") == 0) || (strcmp(name, "-h") == 0)) {
            SDL_Log("USAGE: %s [device]\n"
                   "If device is a two-digit number it'll use it as an index, otherwise\n"
                   "it'll use it as if it were part of the device's name.\n",
                   argv[0]);
            return 0;
        }

        i = strlen(name);
        if ((i < 3) && isdigit(name[0]) && ((i == 1) || isdigit(name[1]))) {
            index = atoi(name);
            name = NULL;
        }
    }

    /* Initialize the force feedbackness */
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_JOYSTICK |
             SDL_INIT_HAPTIC);
    SDL_Log("%d Haptic devices detected.\n", SDL_NumHaptics());
    if (SDL_NumHaptics() > 0) {
        /* We'll just use index or the first force feedback device found */
        if (name == NULL) {
            i = (index != -1) ? index : 0;
        }
        /* Try to find matching device */
        else {
            for (i = 0; i < SDL_NumHaptics(); i++) {
                if (strstr(SDL_HapticName(i), name) != NULL)
                    break;
            }

            if (i >= SDL_NumHaptics()) {
                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to find device matching '%s', aborting.\n",
                       name);
                return 1;
            }
        }

        haptic = SDL_HapticOpen(i);
        if (haptic == NULL) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create the haptic device: %s\n",
                   SDL_GetError());
            return 1;
        }
        SDL_Log("Device: %s\n", SDL_HapticName(i));
        HapticPrintSupported(haptic);
    } else {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No Haptic devices found!\n");
        return 1;
    }

    /* We only want force feedback errors. */
    SDL_ClearError();

    /* Create effects. */
    memset(&efx, 0, sizeof(efx));
    nefx = 0;
    supported = SDL_HapticQuery(haptic);

    SDL_Log("\nUploading effects\n");
    /* First we'll try a SINE effect. */
    if (supported & SDL_HAPTIC_SINE) {
        SDL_Log("   effect %d: Sine Wave\n", nefx);
        efx[nefx].type = SDL_HAPTIC_SINE;
        efx[nefx].periodic.period = 1000;
        efx[nefx].periodic.magnitude = -0x2000;    /* Negative magnitude and ...                      */
        efx[nefx].periodic.phase = 18000;          /* ... 180 degrees phase shift => cancel eachother */
        efx[nefx].periodic.length = 5000;
        efx[nefx].periodic.attack_length = 1000;
        efx[nefx].periodic.fade_length = 1000;
        id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
        if (id[nefx] < 0) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
            abort_execution();
        }
        nefx++;
    }
    /* Now we'll try a SAWTOOTHUP */
    if (supported & SDL_HAPTIC_SAWTOOTHUP) {
        SDL_Log("   effect %d: Sawtooth Up\n", nefx);
        efx[nefx].type = SDL_HAPTIC_SAWTOOTHUP;
        efx[nefx].periodic.period = 500;
        efx[nefx].periodic.magnitude = 0x5000;
        efx[nefx].periodic.length = 5000;
        efx[nefx].periodic.attack_length = 1000;
        efx[nefx].periodic.fade_length = 1000;
        id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
        if (id[nefx] < 0) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
            abort_execution();
        }
        nefx++;
    }
    
    /* Now the classical constant effect. */
    if (supported & SDL_HAPTIC_CONSTANT) {
        SDL_Log("   effect %d: Constant Force\n", nefx);
        efx[nefx].type = SDL_HAPTIC_CONSTANT;
        efx[nefx].constant.direction.type = SDL_HAPTIC_POLAR;
        efx[nefx].constant.direction.dir[0] = 20000;    /* Force comes from the south-west. */
        efx[nefx].constant.length = 5000;
        efx[nefx].constant.level = 0x6000;
        efx[nefx].constant.attack_length = 1000;
        efx[nefx].constant.fade_length = 1000;
        id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
        if (id[nefx] < 0) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
            abort_execution();
        }
        nefx++;
    }
    
    /* The cute spring effect. */
    if (supported & SDL_HAPTIC_SPRING) {
        SDL_Log("   effect %d: Condition Spring\n", nefx);
        efx[nefx].type = SDL_HAPTIC_SPRING;
        efx[nefx].condition.length = 5000;
        for (i = 0; i < SDL_HapticNumAxes(haptic); i++) {
            efx[nefx].condition.right_sat[i] = 0xFFFF;
            efx[nefx].condition.left_sat[i] = 0xFFFF;
            efx[nefx].condition.right_coeff[i] = 0x2000;
            efx[nefx].condition.left_coeff[i] = 0x2000;
            efx[nefx].condition.center[i] = 0x1000;     /* Displace the center for it to move. */
        }
        id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
        if (id[nefx] < 0) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
            abort_execution();
        }
        nefx++;
    }
    /* The interesting damper effect. */
    if (supported & SDL_HAPTIC_DAMPER) {
        SDL_Log("   effect %d: Condition Damper\n", nefx);
        efx[nefx].type = SDL_HAPTIC_DAMPER;
        efx[nefx].condition.length = 5000;
        for (i = 0; i < SDL_HapticNumAxes(haptic); i++) {
            efx[nefx].condition.right_sat[i] = 0xFFFF;
            efx[nefx].condition.left_sat[i] = 0xFFFF;
            efx[nefx].condition.right_coeff[i] = 0x2000;
            efx[nefx].condition.left_coeff[i] = 0x2000;
        }
        id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
        if (id[nefx] < 0) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
            abort_execution();
        }
        nefx++;
    }
    /* The pretty awesome inertia effect. */
    if (supported & SDL_HAPTIC_INERTIA) {
        SDL_Log("   effect %d: Condition Inertia\n", nefx);
        efx[nefx].type = SDL_HAPTIC_INERTIA;
        efx[nefx].condition.length = 5000;
        for (i = 0; i < SDL_HapticNumAxes(haptic); i++) {
            efx[nefx].condition.right_sat[i] = 0xFFFF;
            efx[nefx].condition.left_sat[i] = 0xFFFF;
            efx[nefx].condition.right_coeff[i] = 0x2000;
            efx[nefx].condition.left_coeff[i] = 0x2000;
            efx[nefx].condition.deadband[i] = 0x1000;    /* 1/16th of axis-range around the center is 'dead'. */
        }
        id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
        if (id[nefx] < 0) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
            abort_execution();
        }
        nefx++;
    }
    /* The hot friction effect. */
    if (supported & SDL_HAPTIC_FRICTION) {
        SDL_Log("   effect %d: Condition Friction\n", nefx);
        efx[nefx].type = SDL_HAPTIC_FRICTION;
        efx[nefx].condition.length = 5000;
        for (i = 0; i < SDL_HapticNumAxes(haptic); i++) {
            efx[nefx].condition.right_sat[i] = 0xFFFF;
            efx[nefx].condition.left_sat[i] = 0xFFFF;
            efx[nefx].condition.right_coeff[i] = 0x2000;
            efx[nefx].condition.left_coeff[i] = 0x2000;
        }
        id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
        if (id[nefx] < 0) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
            abort_execution();
        }
        nefx++;
    }
    
    /* Now we'll try a ramp effect */
    if (supported & SDL_HAPTIC_RAMP) {
        SDL_Log("   effect %d: Ramp\n", nefx);
        efx[nefx].type = SDL_HAPTIC_RAMP;
        efx[nefx].ramp.direction.type = SDL_HAPTIC_CARTESIAN;
        efx[nefx].ramp.direction.dir[0] = 1;     /* Force comes from                 */
        efx[nefx].ramp.direction.dir[1] = -1;    /*                  the north-east. */
        efx[nefx].ramp.length = 5000;
        efx[nefx].ramp.start = 0x4000;
        efx[nefx].ramp.end = -0x4000;
        efx[nefx].ramp.attack_length = 1000;
        efx[nefx].ramp.fade_length = 1000;
        id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
        if (id[nefx] < 0) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
            abort_execution();
        }
        nefx++;
    }

    /* Finally we'll try a left/right effect. */
    if (supported & SDL_HAPTIC_LEFTRIGHT) {
        SDL_Log("   effect %d: Left/Right\n", nefx);
        efx[nefx].type = SDL_HAPTIC_LEFTRIGHT;
        efx[nefx].leftright.length = 5000;
        efx[nefx].leftright.large_magnitude = 0x3000;
        efx[nefx].leftright.small_magnitude = 0xFFFF;
        id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
        if (id[nefx] < 0) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
            abort_execution();
        }
        nefx++;
    }


    SDL_Log
        ("\nNow playing effects for 5 seconds each with 1 second delay between\n");
    for (i = 0; i < nefx; i++) {
        SDL_Log("   Playing effect %d\n", i);
        SDL_HapticRunEffect(haptic, id[i], 1);
        SDL_Delay(6000);        /* Effects only have length 5000 */
    }

    /* Quit */
    if (haptic != NULL)
        SDL_HapticClose(haptic);
    SDL_Quit();

    return 0;
}