/****** Stop haptic force-feedback on joypad ******/ void DMG_GamePad::stop_rumble() { if((jstick != NULL) && (rumble != NULL) && (is_rumbling == true)) { SDL_HapticRumbleStop(rumble); is_rumbling = false; } }
int JoyHapticRumble(int pad, uint32_t low, uint32_t high) { #if 0 float mag; if (g.PadState[pad].haptic) { /* Stop the effect if it was playing. */ SDL_HapticRumbleStop(g.PadState[pad].haptic); mag = ((high * 2 + low) / 6) / 127.5; //printf("low: %d high: %d mag: %f\n", low, high, mag); if (SDL_HapticRumblePlay(g.PadState[pad].haptic, mag, 500) != 0) { printf("\nFailed to play rumble: %s\n", SDL_GetError()); return 1; } } #endif return 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; /* Enable standard application logging */ SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); name = NULL; index = -1; if (argc > 1) { size_t l; 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; } l = SDL_strlen(name); if ((l < 3) && SDL_isdigit(name[0]) && ((l == 1) || SDL_isdigit(name[1]))) { index = SDL_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)); } else { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No Haptic devices found!\n"); return 1; } /* We only want force feedback errors. */ SDL_ClearError(); if (SDL_HapticRumbleSupported(haptic) == SDL_FALSE) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Rumble not supported!\n"); return 1; } if (SDL_HapticRumbleInit(haptic) != 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize rumble: %s\n", SDL_GetError()); return 1; } SDL_Log("Playing 2 second rumble at 0.5 magnitude.\n"); if (SDL_HapticRumblePlay(haptic, 0.5, 5000) != 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to play rumble: %s\n", SDL_GetError() ); return 1; } SDL_Delay(2000); SDL_Log("Stopping rumble.\n"); SDL_HapticRumbleStop(haptic); SDL_Delay(2000); SDL_Log("Playing 2 second rumble at 0.3 magnitude.\n"); if (SDL_HapticRumblePlay(haptic, 0.3f, 5000) != 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to play rumble: %s\n", SDL_GetError() ); return 1; } SDL_Delay(2000); /* Quit */ if (haptic != NULL) SDL_HapticClose(haptic); SDL_Quit(); return 0; }
void I_StopGamepadVibration(void) { if (haptic) SDL_HapticRumbleStop(haptic); }
void C4GamePadOpener::StopRumble() { if (SDL_HapticRumbleSupported(haptic)) SDL_HapticRumbleStop(haptic); }
void LibretroRunner::commandIn( Command command, QVariant data, qint64 timeStamp ) { // Command is not relayed to children automatically switch( command ) { case Command::Play: { // Make sure we're only connecting LibretroCore to this node if it's a command that only shows up when // emulation is active (in other words, never during loading) if( !connectedToCore ) { connectedToCore = true; qDebug() << "LibretroRunner will now emit signals from LibretroCore"; connect( &libretroCore, &LibretroCore::dataOut, this, &LibretroRunner::dataOut ); connect( &libretroCore, &LibretroCore::commandOut, this, &LibretroRunner::commandOut ); } qCDebug( phxCore ) << command; libretroCore.state = State::Playing; emit commandOut( Command::Play, QVariant(), nodeCurrentTime() ); break; } case Command::Pause: { if( !connectedToCore ) { connectedToCore = true; qDebug() << "LibretroRunner will now emit signals from LibretroCore"; connect( &libretroCore, &LibretroCore::dataOut, this, &LibretroRunner::dataOut ); connect( &libretroCore, &LibretroCore::commandOut, this, &LibretroRunner::commandOut ); } qCDebug( phxCore ) << command; libretroCore.state = State::Paused; emit commandOut( Command::Pause, QVariant(), nodeCurrentTime() ); break; } case Command::Stop: { if( !connectedToCore ) { connectedToCore = true; qDebug() << "LibretroRunner will now emit signals from LibretroCore"; connect( &libretroCore, &LibretroCore::dataOut, this, &LibretroRunner::dataOut ); connect( &libretroCore, &LibretroCore::commandOut, this, &LibretroRunner::commandOut ); } qCDebug( phxCore ) << command; libretroCore.state = State::Unloading; emit commandOut( Command::Unload, QVariant(), nodeCurrentTime() ); // Write SRAM qCInfo( phxCore ) << "=======Saving game...======="; LibretroCoreStoreSaveData(); qCInfo( phxCore ) << "============================"; // Unload core { // symbols.retro_api_version is reasonably expected to be defined if the core is loaded if( libretroCore.symbols.retro_api_version ) { libretroCore.symbols.retro_unload_game(); libretroCore.symbols.retro_deinit(); libretroCore.symbols.clear(); libretroCore.coreFile.unload(); qCDebug( phxCore ) << "Unloaded core successfully"; } else { qCCritical( phxCore ) << "stop() called on an unloaded core!"; } } // Unload game (if we've read its contents into a buffer) { libretroCore.gameData.clear(); } // Disconnect LibretroCore from the rest of the pipeline disconnect( &libretroCore, &LibretroCore::dataOut, this, &LibretroRunner::dataOut ); disconnect( &libretroCore, &LibretroCore::commandOut, this, &LibretroRunner::commandOut ); connectedToCore = false; // Delete the FBO { if( libretroCore.fbo ) { delete libretroCore.fbo; } libretroCore.fbo = nullptr; } // Reset video mode to 2D (will be set to 3D if the next core asks for it) libretroCore.videoFormat.videoMode = SOFTWARERENDER; libretroCore.state = State::Stopped; emit commandOut( Command::Stop, QVariant(), nodeCurrentTime() ); break; } case Command::Heartbeat: { // Drop any heartbeats from too far in the past if( nodeCurrentTime() - timeStamp > 50 ) { return; } emit commandOut( command, data, timeStamp ); if( libretroCore.state == State::Playing ) { // If in 3D mode, lock the mutex before emulating then activate our context and FBO // This is because we're not sure exactly when the core will render to the texture. So, we'll just lock the // mutex for the *entire* frame to be safe and not just from the start of the frame until the video callback // In 2D mode it's simpler: We know that the data will come in a buffer which we can quickly copy within // the video callback. if( libretroCore.videoFormat.videoMode == HARDWARERENDER ) { libretroCore.videoMutex.lock(); //qDebug() << "LibretroRunner lock"; libretroCore.context->makeCurrent( libretroCore.surface ); libretroCore.fbo->bind(); } // Invoke libretro core libretroCore.symbols.retro_run(); // Update rumble state // TODO: Apply per-controller for( GamepadState &gamepad : libretroCore.gamepads ) { if( gamepad.instanceID == -1 || !gamepad.haptic ) { //qDebug() << gamepad.instanceID << ( !gamepad.haptic ) << ( gamepad.hapticID < 0 ); continue; } else if( libretroCore.fallbackRumbleCurrentStrength[ gamepad.instanceID ] != gamepad.fallbackRumbleRequestedStrength ) { //qDebug() << "from" << core.fallbackRumbleCurrentStrength[ gamepad.instanceID ] << "to" << gamepad.fallbackRumbleRequestedStrength; libretroCore.fallbackRumbleCurrentStrength[ gamepad.instanceID ] = gamepad.fallbackRumbleRequestedStrength; SDL_HapticRumbleStop( gamepad.haptic ); if( SDL_HapticRumblePlay( gamepad.haptic, libretroCore.fallbackRumbleCurrentStrength[ gamepad.instanceID ], SDL_HAPTIC_INFINITY ) != 0 ) { qWarning() << gamepad.friendlyName << SDL_GetError(); qWarning().nospace() << gamepad.friendlyName << ": SDL_HapticRumblePlay( " << gamepad.haptic << ", " << libretroCore.fallbackRumbleCurrentStrength << ", SDL_HAPTIC_INFINITY ) != 0, rumble not available"; qWarning() << "SDL:" << SDL_GetError(); } // Reset the requested strength // Implicitly reset by incoming Gamepads overwriting the value set by us with the default of 0.0 // gamepad.fallbackRumbleRequestedStrength = 0.0; } } if( libretroCore.videoFormat.videoMode == HARDWARERENDER ) { libretroCore.context->makeCurrent( libretroCore.surface ); libretroCore.context->functions()->glFlush(); libretroCore.context->doneCurrent(); //qDebug() << "LibretroRunner unlock"; libretroCore.videoMutex.unlock(); } // Flush stderr, some cores may still write to it despite having RETRO_LOG fflush( stderr ); } break; } case Command::SetWindowGeometry: { emit commandOut( command, data, timeStamp ); libretroCore.windowGeometry = data.toRect(); emit commandOut( command, data, timeStamp ); break; } case Command::SetAspectRatioMode: { libretroCore.aspectMode = data.toInt(); emit commandOut( command, data, timeStamp ); break; } case Command::SetLibretroVariable: { LibretroVariable var = data.value<LibretroVariable>(); libretroCore.variables.insert( var.key(), var ); libretroCore.variablesAreDirty = true; emit commandOut( command, data, timeStamp ); break; } case Command::AddController: { if( !connectedToCore ) { connectedToCore = true; qDebug() << "LibretroRunner will now emit signals from LibretroCore"; connect( &libretroCore, &LibretroCore::dataOut, this, &LibretroRunner::dataOut ); connect( &libretroCore, &LibretroCore::commandOut, this, &LibretroRunner::commandOut ); } GamepadState gamepad = data.value<GamepadState>(); int instanceID = gamepad.instanceID; libretroCore.fallbackRumbleCurrentStrength[ instanceID ] = 0.0; emit commandOut( command, data, timeStamp ); break; } case Command::RemoveController: { if( !connectedToCore ) { connectedToCore = true; qDebug() << "LibretroRunner will now emit signals from LibretroCore"; connect( &libretroCore, &LibretroCore::dataOut, this, &LibretroRunner::dataOut ); connect( &libretroCore, &LibretroCore::commandOut, this, &LibretroRunner::commandOut ); } GamepadState gamepad = data.value<GamepadState>(); int instanceID = gamepad.instanceID; libretroCore.gamepads.remove( instanceID ); emit commandOut( command, data, timeStamp ); break; } default: { emit commandOut( command, data, timeStamp ); break; } } }
// 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); }
/** * @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)); } else { printf("No Haptic devices found!\n"); return 1; } /* We only want force feedback errors. */ SDL_ClearError(); if (SDL_HapticRumbleSupported(haptic) == SDL_FALSE) { printf("\nRumble not supported!\n"); return 1; } if (SDL_HapticRumbleInit(haptic) != 0) { printf("\nFailed to initialize rumble: %s\n", SDL_GetError()); return 1; } printf("Playing 2 second rumble at 0.5 magnitude.\n"); if (SDL_HapticRumblePlay(haptic, 0.5, 5000) != 0) { printf("\nFailed to play rumble: %s\n", SDL_GetError() ); return 1; } SDL_Delay(2000); printf("Stopping rumble.\n"); SDL_HapticRumbleStop(haptic); SDL_Delay(2000); printf("Playing 2 second rumble at 0.3 magnitude.\n"); if (SDL_HapticRumblePlay(haptic, 0.3, 5000) != 0) { printf("\nFailed to play rumble: %s\n", SDL_GetError() ); return 1; } SDL_Delay(2000); /* Quit */ if (haptic != NULL) SDL_HapticClose(haptic); SDL_Quit(); return 0; }