Implement controller rumble support in Gamepad (#1739)
* Add SDL rumble * Fix rumble * whitespace fixins real (#2) * whitespacing real.. please! * nativecffi rumble thing fix lol * Remove trailing whitespace. * Use SDL's argument names and order. * Standardize formatting. * Make `SDLGamepad` fully static again, for simplicity. Also, consistently use `find()` instead of array access, to avoid accidentally creating entries. Also also, consistently use guard clauses instead of indenting. * Make another guard clause. * Update CFFI function signature. * Use `clamp()` instead of `if` statements. * Include required header for `std::clamp()`. * Revert "Use `clamp()` instead of `if` statements." `std::clamp()` was not available until C++17, and we'd like to continue supporting older versions. This reverts commit715a270f79. * Revert "Include required header for `std::clamp()`." This reverts commitf47aebf640. * Tidy up. * Document `Gamepad.rumble()`'s arguments. * Don't limit rumble duration. SDL apparently supports the full Uint32 range, so there's no reason for Lime to restrict it. * Fix whitespace. * Add rumble support in HTML5 (experimental). --------- Co-authored-by: Cameron Taylor <cameron.taylor.ninja@gmail.com> Co-authored-by: player-03 <player3.14@gmail.com>
This commit is contained in:
@@ -12,6 +12,7 @@ namespace lime {
|
|||||||
static void AddMapping (const char* content);
|
static void AddMapping (const char* content);
|
||||||
static const char* GetDeviceGUID (int id);
|
static const char* GetDeviceGUID (int id);
|
||||||
static const char* GetDeviceName (int id);
|
static const char* GetDeviceName (int id);
|
||||||
|
static void Rumble (int id, double lowFrequencyRumble, double highFrequencyRumble, int duration);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1765,6 +1765,20 @@ namespace lime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void lime_gamepad_rumble (int id, double lowFrequencyRumble, double highFrequencyRumble, int duration) {
|
||||||
|
|
||||||
|
Gamepad::Rumble (id, lowFrequencyRumble, highFrequencyRumble, duration);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HL_PRIM void HL_NAME(hl_gamepad_rumble) (int id, double lowFrequencyRumble, double highFrequencyRumble, int duration) {
|
||||||
|
|
||||||
|
Gamepad::Rumble (id, lowFrequencyRumble, highFrequencyRumble, duration);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
value lime_gzip_compress (value buffer, value bytes) {
|
value lime_gzip_compress (value buffer, value bytes) {
|
||||||
|
|
||||||
#ifdef LIME_ZLIB
|
#ifdef LIME_ZLIB
|
||||||
@@ -4062,6 +4076,7 @@ namespace lime {
|
|||||||
DEFINE_PRIME2v (lime_gamepad_event_manager_register);
|
DEFINE_PRIME2v (lime_gamepad_event_manager_register);
|
||||||
DEFINE_PRIME1 (lime_gamepad_get_device_guid);
|
DEFINE_PRIME1 (lime_gamepad_get_device_guid);
|
||||||
DEFINE_PRIME1 (lime_gamepad_get_device_name);
|
DEFINE_PRIME1 (lime_gamepad_get_device_name);
|
||||||
|
DEFINE_PRIME4v (lime_gamepad_rumble);
|
||||||
DEFINE_PRIME2 (lime_gzip_compress);
|
DEFINE_PRIME2 (lime_gzip_compress);
|
||||||
DEFINE_PRIME2 (lime_gzip_decompress);
|
DEFINE_PRIME2 (lime_gzip_decompress);
|
||||||
DEFINE_PRIME2v (lime_haptic_vibrate);
|
DEFINE_PRIME2v (lime_haptic_vibrate);
|
||||||
@@ -4255,6 +4270,7 @@ namespace lime {
|
|||||||
DEFINE_HL_PRIM (_VOID, hl_gamepad_event_manager_register, _FUN(_VOID, _NO_ARG) _TGAMEPAD_EVENT);
|
DEFINE_HL_PRIM (_VOID, hl_gamepad_event_manager_register, _FUN(_VOID, _NO_ARG) _TGAMEPAD_EVENT);
|
||||||
DEFINE_HL_PRIM (_BYTES, hl_gamepad_get_device_guid, _I32);
|
DEFINE_HL_PRIM (_BYTES, hl_gamepad_get_device_guid, _I32);
|
||||||
DEFINE_HL_PRIM (_BYTES, hl_gamepad_get_device_name, _I32);
|
DEFINE_HL_PRIM (_BYTES, hl_gamepad_get_device_name, _I32);
|
||||||
|
DEFINE_HL_PRIM (_VOID, hl_gamepad_rumble, _I32 _I32 _F64 _F64);
|
||||||
DEFINE_HL_PRIM (_TBYTES, hl_gzip_compress, _TBYTES _TBYTES);
|
DEFINE_HL_PRIM (_TBYTES, hl_gzip_compress, _TBYTES _TBYTES);
|
||||||
DEFINE_HL_PRIM (_TBYTES, hl_gzip_decompress, _TBYTES _TBYTES);
|
DEFINE_HL_PRIM (_TBYTES, hl_gzip_decompress, _TBYTES _TBYTES);
|
||||||
DEFINE_HL_PRIM (_VOID, hl_haptic_vibrate, _I32 _I32);
|
DEFINE_HL_PRIM (_VOID, hl_haptic_vibrate, _I32 _I32);
|
||||||
|
|||||||
@@ -4,48 +4,40 @@
|
|||||||
namespace lime {
|
namespace lime {
|
||||||
|
|
||||||
|
|
||||||
std::map<int, SDL_GameController*> gameControllers = std::map<int, SDL_GameController*> ();
|
std::map<int, SDL_GameController*> gameControllers;
|
||||||
std::map<int, int> gameControllerIDs = std::map<int, int> ();
|
std::map<int, int> gameControllerIDs;
|
||||||
|
|
||||||
|
|
||||||
bool SDLGamepad::Connect (int deviceID) {
|
bool SDLGamepad::Connect (int deviceID) {
|
||||||
|
|
||||||
if (SDL_IsGameController (deviceID)) {
|
if (!SDL_IsGameController (deviceID))
|
||||||
|
return false;
|
||||||
|
|
||||||
SDL_GameController *gameController = SDL_GameControllerOpen (deviceID);
|
SDL_GameController *gameController = SDL_GameControllerOpen (deviceID);
|
||||||
|
if (gameController == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (gameController) {
|
SDL_Joystick *joystick = SDL_GameControllerGetJoystick (gameController);
|
||||||
|
int id = SDL_JoystickInstanceID (joystick);
|
||||||
|
|
||||||
SDL_Joystick *joystick = SDL_GameControllerGetJoystick (gameController);
|
gameControllers[id] = gameController;
|
||||||
int id = SDL_JoystickInstanceID (joystick);
|
gameControllerIDs[deviceID] = id;
|
||||||
|
|
||||||
gameControllers[id] = gameController;
|
return true;
|
||||||
gameControllerIDs[deviceID] = id;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SDLGamepad::Disconnect (int id) {
|
bool SDLGamepad::Disconnect (int id) {
|
||||||
|
|
||||||
if (gameControllers.find (id) != gameControllers.end ()) {
|
auto it = gameControllers.find (id);
|
||||||
|
if (it == gameControllers.end ())
|
||||||
|
return false;
|
||||||
|
|
||||||
SDL_GameController *gameController = gameControllers[id];
|
SDL_GameControllerClose (it->second);
|
||||||
SDL_GameControllerClose (gameController);
|
gameControllers.erase (id);
|
||||||
gameControllers.erase (id);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,26 +58,51 @@ namespace lime {
|
|||||||
|
|
||||||
const char* Gamepad::GetDeviceGUID (int id) {
|
const char* Gamepad::GetDeviceGUID (int id) {
|
||||||
|
|
||||||
SDL_Joystick* joystick = SDL_GameControllerGetJoystick (gameControllers[id]);
|
auto it = gameControllers.find (id);
|
||||||
|
if (it == gameControllers.end ())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
if (joystick) {
|
SDL_Joystick* joystick = SDL_GameControllerGetJoystick (it->second);
|
||||||
|
if (joystick == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
char* guid = new char[64];
|
char* guid = new char[64];
|
||||||
SDL_JoystickGetGUIDString (SDL_JoystickGetGUID (joystick), guid, 64);
|
SDL_JoystickGetGUIDString (SDL_JoystickGetGUID (joystick), guid, 64);
|
||||||
return guid;
|
return guid;
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const char* Gamepad::GetDeviceName (int id) {
|
const char* Gamepad::GetDeviceName (int id) {
|
||||||
|
|
||||||
return SDL_GameControllerName (gameControllers[id]);
|
auto it = gameControllers.find (id);
|
||||||
|
if (it == gameControllers.end ())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return SDL_GameControllerName (it->second);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
void Gamepad::Rumble (int id, double lowFrequencyRumble, double highFrequencyRumble, int duration) {
|
||||||
|
|
||||||
|
auto it = gameControllers.find (id);
|
||||||
|
if (it == gameControllers.end ())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (highFrequencyRumble < 0.0f)
|
||||||
|
highFrequencyRumble = 0.0f;
|
||||||
|
else if (highFrequencyRumble > 1.0f)
|
||||||
|
highFrequencyRumble = 1.0f;
|
||||||
|
|
||||||
|
if (lowFrequencyRumble < 0.0f)
|
||||||
|
lowFrequencyRumble = 0.0f;
|
||||||
|
else if (lowFrequencyRumble > 1.0f)
|
||||||
|
lowFrequencyRumble = 1.0f;
|
||||||
|
|
||||||
|
SDL_GameControllerRumble (it->second, lowFrequencyRumble * 0xFFFF, highFrequencyRumble * 0xFFFF, duration);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -161,6 +161,8 @@ class NativeCFFI
|
|||||||
|
|
||||||
@:cffi private static function lime_gamepad_get_device_name(id:Int):Dynamic;
|
@:cffi private static function lime_gamepad_get_device_name(id:Int):Dynamic;
|
||||||
|
|
||||||
|
@:cffi private static function lime_gamepad_rumble(id:Int, lowFrequencyRumble:Float, highFrequencyRumble:Float, duration:Int):Void;
|
||||||
|
|
||||||
@:cffi private static function lime_gamepad_event_manager_register(callback:Dynamic, eventObject:Dynamic):Void;
|
@:cffi private static function lime_gamepad_event_manager_register(callback:Dynamic, eventObject:Dynamic):Void;
|
||||||
|
|
||||||
@:cffi private static function lime_gzip_compress(data:Dynamic, bytes:Dynamic):Dynamic;
|
@:cffi private static function lime_gzip_compress(data:Dynamic, bytes:Dynamic):Dynamic;
|
||||||
@@ -458,6 +460,8 @@ class NativeCFFI
|
|||||||
false));
|
false));
|
||||||
private static var lime_gamepad_get_device_name = new cpp.Callable<Int->cpp.Object>(cpp.Prime._loadPrime("lime", "lime_gamepad_get_device_name", "io",
|
private static var lime_gamepad_get_device_name = new cpp.Callable<Int->cpp.Object>(cpp.Prime._loadPrime("lime", "lime_gamepad_get_device_name", "io",
|
||||||
false));
|
false));
|
||||||
|
private static var lime_gamepad_rumble = new cpp.Callable<Int->Float->Float->Int->cpp.Void>(cpp.Prime._loadPrime("lime", "lime_gamepad_rumble", "iddiv",
|
||||||
|
false));
|
||||||
private static var lime_gamepad_event_manager_register = new cpp.Callable<cpp.Object->cpp.Object->cpp.Void>(cpp.Prime._loadPrime("lime",
|
private static var lime_gamepad_event_manager_register = new cpp.Callable<cpp.Object->cpp.Object->cpp.Void>(cpp.Prime._loadPrime("lime",
|
||||||
"lime_gamepad_event_manager_register", "oov", false));
|
"lime_gamepad_event_manager_register", "oov", false));
|
||||||
private static var lime_gzip_compress = new cpp.Callable<cpp.Object->cpp.Object->cpp.Object>(cpp.Prime._loadPrime("lime", "lime_gzip_compress", "ooo",
|
private static var lime_gzip_compress = new cpp.Callable<cpp.Object->cpp.Object->cpp.Object>(cpp.Prime._loadPrime("lime", "lime_gzip_compress", "ooo",
|
||||||
@@ -693,6 +697,7 @@ class NativeCFFI
|
|||||||
private static var lime_gamepad_add_mappings = CFFI.load("lime", "lime_gamepad_add_mappings", 1);
|
private static var lime_gamepad_add_mappings = CFFI.load("lime", "lime_gamepad_add_mappings", 1);
|
||||||
private static var lime_gamepad_get_device_guid = CFFI.load("lime", "lime_gamepad_get_device_guid", 1);
|
private static var lime_gamepad_get_device_guid = CFFI.load("lime", "lime_gamepad_get_device_guid", 1);
|
||||||
private static var lime_gamepad_get_device_name = CFFI.load("lime", "lime_gamepad_get_device_name", 1);
|
private static var lime_gamepad_get_device_name = CFFI.load("lime", "lime_gamepad_get_device_name", 1);
|
||||||
|
private static var lime_gamepad_rumble = CFFI.load("lime", "lime_gamepad_rumble", 4);
|
||||||
private static var lime_gamepad_event_manager_register = CFFI.load("lime", "lime_gamepad_event_manager_register", 2);
|
private static var lime_gamepad_event_manager_register = CFFI.load("lime", "lime_gamepad_event_manager_register", 2);
|
||||||
private static var lime_gzip_compress = CFFI.load("lime", "lime_gzip_compress", 2);
|
private static var lime_gzip_compress = CFFI.load("lime", "lime_gzip_compress", 2);
|
||||||
private static var lime_gzip_decompress = CFFI.load("lime", "lime_gzip_decompress", 2);
|
private static var lime_gzip_decompress = CFFI.load("lime", "lime_gzip_decompress", 2);
|
||||||
@@ -1036,6 +1041,8 @@ class NativeCFFI
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@:hlNative("lime", "hl_gamepad_rumble") private static function lime_gamepad_rumble(id:Int, lowFrequencyRumble:Float, highFrequencyRumble:Float, duration:Int):Void {}
|
||||||
|
|
||||||
@:hlNative("lime", "hl_gamepad_event_manager_register") private static function lime_gamepad_event_manager_register(callback:Void->Void,
|
@:hlNative("lime", "hl_gamepad_event_manager_register") private static function lime_gamepad_event_manager_register(callback:Void->Void,
|
||||||
eventObject:GamepadEventInfo):Void {}
|
eventObject:GamepadEventInfo):Void {}
|
||||||
|
|
||||||
|
|||||||
@@ -24,10 +24,19 @@ class Gamepad
|
|||||||
public var onButtonUp = new Event<GamepadButton->Void>();
|
public var onButtonUp = new Event<GamepadButton->Void>();
|
||||||
public var onDisconnect = new Event<Void->Void>();
|
public var onDisconnect = new Event<Void->Void>();
|
||||||
|
|
||||||
|
#if (js && html5)
|
||||||
|
private var __jsGamepad:js.html.Gamepad;
|
||||||
|
#end
|
||||||
|
|
||||||
public function new(id:Int)
|
public function new(id:Int)
|
||||||
{
|
{
|
||||||
this.id = id;
|
this.id = id;
|
||||||
connected = true;
|
connected = true;
|
||||||
|
|
||||||
|
#if (js && html5)
|
||||||
|
var devices = Joystick.__getDeviceData();
|
||||||
|
__jsGamepad = devices[this.id];
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function addMappings(mappings:Array<String>):Void
|
public static function addMappings(mappings:Array<String>):Void
|
||||||
@@ -43,6 +52,41 @@ class Gamepad
|
|||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@param lowFrequencyRumble The intensity of the low frequency (strong)
|
||||||
|
rumble motor, from 0 to 1.
|
||||||
|
@param highFrequencyRumble The intensity of the high frequency (weak)
|
||||||
|
rumble motor, from 0 to 1. Will be ignored in situations where only one
|
||||||
|
motor is available.
|
||||||
|
@param duration The duration of the rumble effect, in milliseconds.
|
||||||
|
**/
|
||||||
|
public inline function rumble(lowFrequencyRumble:Float, highFrequencyRumble:Float, duration:Int):Void
|
||||||
|
{
|
||||||
|
#if (lime_cffi && !macro)
|
||||||
|
NativeCFFI.lime_gamepad_rumble(this.id, lowFrequencyRumble, highFrequencyRumble, duration);
|
||||||
|
#elseif (js && html5)
|
||||||
|
var actuator:Dynamic = (untyped __jsGamepad.vibrationActuator) ? untyped __jsGamepad.vibrationActuator
|
||||||
|
: (untyped __jsGamepad.hapticActuators) ? untyped __jsGamepad.hapticActuators[0]
|
||||||
|
: null;
|
||||||
|
if (actuator == null) return;
|
||||||
|
|
||||||
|
if (untyped actuator.playEffect)
|
||||||
|
{
|
||||||
|
untyped actuator.playEffect("dual-rumble", {
|
||||||
|
duration: duration,
|
||||||
|
strongMagnitude: lowFrequencyRumble,
|
||||||
|
weakMagnitude: highFrequencyRumble
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (untyped actuator.pulse)
|
||||||
|
{
|
||||||
|
untyped actuator.pulse(lowFrequencyRumble, duration);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
@:noCompletion private static function __connect(id:Int):Void
|
@:noCompletion private static function __connect(id:Int):Void
|
||||||
{
|
{
|
||||||
if (!devices.exists(id))
|
if (!devices.exists(id))
|
||||||
@@ -67,8 +111,7 @@ class Gamepad
|
|||||||
#if (lime_cffi && !macro)
|
#if (lime_cffi && !macro)
|
||||||
return CFFI.stringValue(NativeCFFI.lime_gamepad_get_device_guid(this.id));
|
return CFFI.stringValue(NativeCFFI.lime_gamepad_get_device_guid(this.id));
|
||||||
#elseif (js && html5)
|
#elseif (js && html5)
|
||||||
var devices = Joystick.__getDeviceData();
|
return __jsGamepad.id;
|
||||||
return devices[this.id].id;
|
|
||||||
#else
|
#else
|
||||||
return null;
|
return null;
|
||||||
#end
|
#end
|
||||||
@@ -79,8 +122,7 @@ class Gamepad
|
|||||||
#if (lime_cffi && !macro)
|
#if (lime_cffi && !macro)
|
||||||
return CFFI.stringValue(NativeCFFI.lime_gamepad_get_device_name(this.id));
|
return CFFI.stringValue(NativeCFFI.lime_gamepad_get_device_name(this.id));
|
||||||
#elseif (js && html5)
|
#elseif (js && html5)
|
||||||
var devices = Joystick.__getDeviceData();
|
return __jsGamepad.id;
|
||||||
return devices[this.id].id;
|
|
||||||
#else
|
#else
|
||||||
return null;
|
return null;
|
||||||
#end
|
#end
|
||||||
|
|||||||
@@ -52,11 +52,11 @@ class Joystick
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if (js && html5)
|
#if (js && html5)
|
||||||
@:noCompletion private static function __getDeviceData():Array<Dynamic>
|
@:noCompletion private static function __getDeviceData():Array<js.html.Gamepad>
|
||||||
{
|
{
|
||||||
var res:Dynamic = null;
|
var res:Array<js.html.Gamepad> = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
res = (untyped navigator.getGamepads) ? untyped navigator.getGamepads() : (untyped navigator.webkitGetGamepads) ? untyped navigator.webkitGetGamepads() : null;
|
res = (untyped navigator.getGamepads) ? untyped navigator.getGamepads() : (untyped navigator.webkitGetGamepads) ? untyped navigator.webkitGetGamepads() : null;
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ class Joystick
|
|||||||
// if something went wrong, treat it the same as when navigator.getGamepads doesn't exist
|
// if something went wrong, treat it the same as when navigator.getGamepads doesn't exist
|
||||||
// we probably don't have permission to use this feature
|
// we probably don't have permission to use this feature
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
|||||||
Reference in New Issue
Block a user