@@ -75,7 +75,7 @@ JoypadSDL *JoypadSDL::get_singleton() {
7575Error JoypadSDL::initialize () {
7676 SDL_SetHint (SDL_HINT_JOYSTICK_THREAD, " 1" );
7777 SDL_SetHint (SDL_HINT_NO_SIGNAL_HANDLERS, " 1" );
78- ERR_FAIL_COND_V_MSG (!SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD), FAILED, SDL_GetError ());
78+ ERR_FAIL_COND_V_MSG (!SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC ), FAILED, SDL_GetError ());
7979
8080 // Add Godot's mapping database from memory
8181 int i = 0 ;
@@ -169,6 +169,7 @@ void JoypadSDL::process_events() {
169169 joypads[joy_id].sdl_instance_idx = sdl_event.jdevice .which ;
170170 joypads[joy_id].supports_force_feedback = SDL_GetBooleanProperty (propertiesID, SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, false );
171171 joypads[joy_id].guid = StringName (String (guid));
172+ joypads[joy_id].setup_haptic_effect ();
172173
173174 sdl_instance_id_to_joypad_id.insert (sdl_event.jdevice .which , joy_id);
174175
@@ -280,6 +281,11 @@ void JoypadSDL::close_joypad(int p_pad_idx) {
280281 joypads[p_pad_idx].attached = false ;
281282 sdl_instance_id_to_joypad_id.erase (sdl_instance_idx);
282283
284+ if (joypads[p_pad_idx].haptic ) {
285+ SDL_DestroyHapticEffect (joypads[p_pad_idx].haptic , joypads[p_pad_idx].haptic_effect_id );
286+ SDL_CloseHaptic (joypads[p_pad_idx].haptic );
287+ }
288+
283289 if (SDL_IsGamepad (sdl_instance_idx)) {
284290 SDL_Gamepad *gamepad = SDL_GetGamepadFromID (sdl_instance_idx);
285291 SDL_CloseGamepad (gamepad);
@@ -302,6 +308,204 @@ bool JoypadSDL::Joypad::set_joy_light(const Color &p_color) {
302308 return SDL_SetJoystickLED (get_sdl_joystick (), linear.get_r8 (), linear.get_g8 (), linear.get_b8 ());
303309}
304310
311+ void JoypadSDL::Joypad::constant_force_feedback (float p_force, float p_duration) {
312+ if (!haptic) {
313+ #ifdef DEBUG_ENABLED
314+ print_line (" Input.constant_force_feedback(): not haptic." );
315+ #endif
316+ return ;
317+ }
318+
319+ SDL_HapticEffect effect;
320+ if ((SDL_GetHapticFeatures (haptic) & SDL_HAPTIC_CONSTANT) == 0 ) {
321+ #ifdef DEBUG_ENABLED
322+ print_line (" Input.constant_force_feedback(): unsupported." );
323+ #endif
324+ return ;
325+ }
326+
327+ if (haptic_effect_id) {
328+ SDL_DestroyHapticEffect (haptic, haptic_effect_id);
329+ }
330+
331+ SDL_memset (&effect, 0 , sizeof (SDL_HapticEffect)); // 0 is safe default
332+ effect.type = SDL_HAPTIC_CONSTANT;
333+ effect.constant .direction .type = SDL_HAPTIC_CARTESIAN;
334+ effect.constant .direction .dir [0 ] = 1 ;
335+ effect.constant .direction .dir [1 ] = 0 ;
336+ effect.constant .length = (Uint32)(p_duration * 1000 );
337+ if (effect.constant .length == 0 ) {
338+ effect.constant .length = SDL_HAPTIC_INFINITY;
339+ }
340+ effect.constant .level = SDL_MAX_SINT16 * SDL_clamp (p_force, -1 .0f , 1 .0f );
341+
342+ haptic_effect_id = SDL_CreateHapticEffect (haptic, &effect);
343+ SDL_RunHapticEffect (haptic, haptic_effect_id, 1 );
344+ }
345+
346+ void JoypadSDL::Joypad::spring_force_feedback (float p_cw_coef, float p_ccw_coef, float p_offset, float p_duration) {
347+ if (!haptic) {
348+ #ifdef DEBUG_ENABLED
349+ print_line (" Input.spring_force_feedback(): not haptic." );
350+ #endif
351+ return ;
352+ }
353+
354+ SDL_HapticEffect effect;
355+ if ((SDL_GetHapticFeatures (haptic) & SDL_HAPTIC_SPRING) == 0 ) {
356+ #ifdef DEBUG_ENABLED
357+ print_line (" Input.spring_force_feedback(): unsupported." );
358+ #endif
359+ return ;
360+ }
361+
362+ if (haptic_effect_id) {
363+ SDL_DestroyHapticEffect (haptic, haptic_effect_id);
364+ }
365+
366+ SDL_memset (&effect, 0 , sizeof (SDL_HapticEffect)); // 0 is safe default
367+ effect.type = SDL_HAPTIC_SPRING;
368+ effect.condition .direction .type = SDL_HAPTIC_CARTESIAN;
369+ effect.condition .direction .dir [0 ] = 1 ;
370+ effect.condition .direction .dir [1 ] = 0 ;
371+ effect.condition .right_coeff [0 ] = p_cw_coef;
372+ effect.condition .left_coeff [0 ] = p_ccw_coef;
373+ // TODO: What is "offset"?
374+ effect.condition .length = (Uint32)(p_duration * 1000 );
375+ if (effect.condition .length == 0 ) {
376+ effect.condition .length = SDL_HAPTIC_INFINITY;
377+ }
378+
379+ haptic_effect_id = SDL_CreateHapticEffect (haptic, &effect);
380+ SDL_RunHapticEffect (haptic, haptic_effect_id, 1 );
381+ }
382+
383+ void JoypadSDL::Joypad::friction_force_feedback (float p_cw_coef, float p_ccw_coef, float p_duration) {
384+ if (!haptic) {
385+ #ifdef DEBUG_ENABLED
386+ print_line (" Input.friction_force_feedback(): not haptic." );
387+ #endif
388+ return ;
389+ }
390+
391+ SDL_HapticEffect effect;
392+ if ((SDL_GetHapticFeatures (haptic) & SDL_HAPTIC_FRICTION) == 0 ) {
393+ #ifdef DEBUG_ENABLED
394+ print_line (" Input.friction_force_feedback(): unsupported." );
395+ #endif
396+ return ;
397+ }
398+
399+ if (haptic_effect_id) {
400+ SDL_DestroyHapticEffect (haptic, haptic_effect_id);
401+ }
402+
403+ SDL_memset (&effect, 0 , sizeof (SDL_HapticEffect)); // 0 is safe default
404+ effect.type = SDL_HAPTIC_FRICTION;
405+ effect.condition .direction .type = SDL_HAPTIC_CARTESIAN;
406+ effect.condition .direction .dir [0 ] = 1 ;
407+ effect.condition .direction .dir [1 ] = 0 ;
408+ effect.condition .right_coeff [0 ] = p_cw_coef;
409+ effect.condition .left_coeff [0 ] = p_ccw_coef;
410+ // TODO: What is "offset"?
411+ effect.condition .length = (Uint32)(p_duration * 1000 );
412+ if (effect.condition .length == 0 ) {
413+ effect.condition .length = SDL_HAPTIC_INFINITY;
414+ }
415+
416+ haptic_effect_id = SDL_CreateHapticEffect (haptic, &effect);
417+ SDL_RunHapticEffect (haptic, haptic_effect_id, 1 );
418+ }
419+
420+ void JoypadSDL::Joypad::damper_force_feedback (float p_cw_coef, float p_ccw_coef, float p_offset, float p_duration) {
421+ if (!haptic) {
422+ #ifdef DEBUG_ENABLED
423+ print_line (" Input.damper_force_feedback(): not haptic." );
424+ #endif
425+ return ;
426+ }
427+
428+ SDL_HapticEffect effect;
429+ if ((SDL_GetHapticFeatures (haptic) & SDL_HAPTIC_DAMPER) == 0 ) {
430+ #ifdef DEBUG_ENABLED
431+ print_line (" Input.damper_force_feedback(): unsupported." );
432+ #endif
433+ return ;
434+ }
435+
436+ if (haptic_effect_id) {
437+ SDL_DestroyHapticEffect (haptic, haptic_effect_id);
438+ }
439+
440+ SDL_memset (&effect, 0 , sizeof (SDL_HapticEffect)); // 0 is safe default
441+ effect.type = SDL_HAPTIC_DAMPER;
442+ effect.condition .direction .type = SDL_HAPTIC_CARTESIAN;
443+ effect.condition .direction .dir [0 ] = 1 ;
444+ effect.condition .direction .dir [1 ] = 0 ;
445+ effect.condition .right_coeff [0 ] = p_cw_coef;
446+ effect.condition .left_coeff [0 ] = p_ccw_coef;
447+ // TODO: What is "offset"?
448+ effect.condition .length = (Uint32)(p_duration * 1000 );
449+ if (effect.condition .length == 0 ) {
450+ effect.condition .length = SDL_HAPTIC_INFINITY;
451+ }
452+
453+ haptic_effect_id = SDL_CreateHapticEffect (haptic, &effect);
454+ SDL_RunHapticEffect (haptic, haptic_effect_id, 1 );
455+ }
456+
457+ void JoypadSDL::Joypad::inertia_force_feedback (float p_cw_coef, float p_ccw_coef, float p_offset, float p_duration) {
458+ if (!haptic) {
459+ #ifdef DEBUG_ENABLED
460+ print_line (" Input.inertia_force_feedback(): not haptic." );
461+ #endif
462+ return ;
463+ }
464+
465+ SDL_HapticEffect effect;
466+ if ((SDL_GetHapticFeatures (haptic) & SDL_HAPTIC_INERTIA) == 0 ) {
467+ #ifdef DEBUG_ENABLED
468+ print_line (" Input.inertia_force_feedback(): unsupported." );
469+ #endif
470+ return ;
471+ }
472+
473+ if (haptic_effect_id) {
474+ SDL_DestroyHapticEffect (haptic, haptic_effect_id);
475+ }
476+
477+ SDL_memset (&effect, 0 , sizeof (SDL_HapticEffect)); // 0 is safe default
478+ effect.type = SDL_HAPTIC_INERTIA;
479+ effect.condition .direction .type = SDL_HAPTIC_CARTESIAN;
480+ effect.condition .direction .dir [0 ] = 1 ;
481+ effect.condition .direction .dir [1 ] = 0 ;
482+ effect.condition .right_coeff [0 ] = p_cw_coef;
483+ effect.condition .left_coeff [0 ] = p_ccw_coef;
484+ // TODO: What is "offset"?
485+ effect.condition .length = (Uint32)(p_duration * 1000 );
486+ if (effect.condition .length == 0 ) {
487+ effect.condition .length = SDL_HAPTIC_INFINITY;
488+ }
489+
490+ haptic_effect_id = SDL_CreateHapticEffect (haptic, &effect);
491+ SDL_RunHapticEffect (haptic, haptic_effect_id, 1 );
492+ }
493+
494+ void JoypadSDL::Joypad::stop_force_feedback () {
495+ if (!haptic) {
496+ #ifdef DEBUG_ENABLED
497+ print_line (" Input.stop_force_feedback(): not haptic." );
498+ #endif
499+ return ;
500+ }
501+
502+ if (haptic_effect_id) {
503+ SDL_StopHapticEffect (haptic, haptic_effect_id);
504+ SDL_DestroyHapticEffect (haptic, haptic_effect_id);
505+ haptic_effect_id = 0 ;
506+ }
507+ }
508+
305509SDL_Joystick *JoypadSDL::Joypad::get_sdl_joystick () const {
306510 return SDL_GetJoystickFromID (sdl_instance_idx);
307511}
@@ -310,4 +514,21 @@ SDL_Gamepad *JoypadSDL::Joypad::get_sdl_gamepad() const {
310514 return SDL_GetGamepadFromID (sdl_instance_idx);
311515}
312516
517+ void JoypadSDL::Joypad::setup_haptic_effect () {
518+ haptic = SDL_OpenHapticFromJoystick (get_sdl_joystick ());
519+ if (!haptic) {
520+ return ;
521+ }
522+ SDL_HapticEffect effect;
523+ SDL_memset (&effect, 0 , sizeof (SDL_HapticEffect)); // 0 is safe default
524+ effect.type = SDL_HAPTIC_CONSTANT;
525+ effect.constant .direction .type = SDL_HAPTIC_CARTESIAN;
526+ effect.constant .direction .dir [0 ] = 1 ;
527+ effect.constant .direction .dir [1 ] = 0 ;
528+ effect.constant .length = 0 ;
529+ effect.constant .level = 0 ;
530+
531+ haptic_effect_id = SDL_CreateHapticEffect (haptic, &effect);
532+ }
533+
313534#endif // SDL_ENABLED
0 commit comments