9#ifndef SDL_TICKS_PASSED
10#define SDL_TICKS_PASSED(A, B) ((Sint32)((B) - (A)) <= 0)
16std::string hatToString(Uint8
value) {
18 case SDL_HAT_CENTERED:
return "c";
19 case SDL_HAT_UP:
return "u";
20 case SDL_HAT_RIGHT:
return "r";
21 case SDL_HAT_DOWN:
return "d";
22 case SDL_HAT_LEFT:
return "l";
23 case SDL_HAT_RIGHTUP:
return "ru";
24 case SDL_HAT_RIGHTDOWN:
return "rd";
25 case SDL_HAT_LEFTUP:
return "lu";
26 case SDL_HAT_LEFTDOWN:
return "ld";
46 joyhandle_ = SDL_JoystickOpen(deviceindex);
47 if (!joyhandle_)
return false;
49 instanceid_ = SDL_JoystickInstanceID(joyhandle_);
52 SDL_JoystickGUID sdlguid = SDL_JoystickGetGUID(joyhandle_);
53 SDL_JoystickGetGUIDString(sdlguid, cstr,
static_cast<int>(
sizeof(cstr)));
58 const char* joyname = SDL_JoystickName(joyhandle_);
59 if (!joyname && controller_) joyname = SDL_GameControllerName(controller_);
60 name_ = joyname ? joyname :
"";
66 if (haptic_) SDL_HapticClose(haptic_);
67 if (controller_) SDL_GameControllerClose(controller_);
68 if (joyhandle_) SDL_JoystickClose(joyhandle_);
71 controller_ =
nullptr;
74 vibration_ = Vibration{};
78 return joyhandle_ !=
nullptr && SDL_JoystickGetAttached(joyhandle_);
86 return isConnected() ? SDL_JoystickNumAxes(joyhandle_) : 0;
90 return isConnected() ? SDL_JoystickNumButtons(joyhandle_) : 0;
94 return isConnected() ? SDL_JoystickNumHats(joyhandle_) : 0;
99 return clampAxis(
static_cast<float>(SDL_JoystickGetAxis(joyhandle_, axisindex)) / 32768.0f);
103 std::vector<float> axes;
106 axes.reserve(
static_cast<size_t>(
count));
107 for (
int i = 0; i <
count; i++)
108 axes.push_back(
clampAxis(
static_cast<float>(SDL_JoystickGetAxis(joyhandle_, i)) / 32768.0f));
114 return hatToString(SDL_JoystickGetHat(joyhandle_, hatindex));
118 return isDown(std::vector<int>{button});
124 for (
int button : buttons) {
125 if (button < 0 || button >= numbuttons)
continue;
126 if (SDL_JoystickGetButton(joyhandle_, button) == 1)
return true;
132 if (!SDL_IsGameController(deviceindex))
return false;
135 SDL_GameControllerClose(controller_);
136 controller_ =
nullptr;
139 controller_ = SDL_GameControllerOpen(deviceindex);
144 return controller_ !=
nullptr;
149 SDL_GameControllerAxis sdlaxis = SDL_GameControllerGetAxisFromString(axis.c_str());
150 if (sdlaxis == SDL_CONTROLLER_AXIS_INVALID)
return 0.f;
151 Sint16
value = SDL_GameControllerGetAxis(controller_, sdlaxis);
161 for (
const auto&
name : buttons) {
162 SDL_GameControllerButton sdlbutton = SDL_GameControllerGetButtonFromString(
name.c_str());
163 if (sdlbutton == SDL_CONTROLLER_BUTTON_INVALID)
continue;
164 if (SDL_GameControllerGetButton(controller_, sdlbutton) == 1)
return true;
170 char* sdlmapping =
nullptr;
171 if (controller_) sdlmapping = SDL_GameControllerMapping(controller_);
173 SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(guid_.c_str());
174 sdlmapping = SDL_GameControllerMappingForGUID(sdlguid);
176 if (!sdlmapping)
return "";
178 std::string mappingstr(sdlmapping);
179 SDL_free(sdlmapping);
181 if (mappingstr.find_last_of(
',') != mappingstr.length() - 1) mappingstr +=
",";
182 mappingstr +=
"platform:" + std::string(SDL_GetPlatform());
195 return static_cast<int>(instanceid_);
203#if SDL_VERSION_ATLEAST(2, 0, 6)
205 vendorID = SDL_JoystickGetVendor(joyhandle_);
206 productID = SDL_JoystickGetProduct(joyhandle_);
207 productVersion = SDL_JoystickGetProductVersion(joyhandle_);
211 vendorID = productID = productVersion = 0;
214bool Pad::checkCreateHaptic() {
217 if (!SDL_WasInit(SDL_INIT_HAPTIC) && SDL_InitSubSystem(SDL_INIT_HAPTIC) < 0)
return false;
219 if (haptic_ && SDL_HapticIndex(haptic_) != -1)
return true;
222 SDL_HapticClose(haptic_);
226 haptic_ = SDL_HapticOpenFromJoystick(joyhandle_);
227 vibration_ = Vibration{};
228 return haptic_ !=
nullptr;
232 if (!checkCreateHaptic())
return false;
233 unsigned int features = SDL_HapticQuery(haptic_);
234 if ((features & SDL_HAPTIC_LEFTRIGHT) != 0)
return true;
235 if (
isGamepad() && (features & SDL_HAPTIC_CUSTOM) != 0)
return true;
236 if ((features & SDL_HAPTIC_SINE) != 0)
return true;
240bool Pad::runVibrationEffect() {
241 if (vibration_.id != -1) {
242 if (SDL_HapticUpdateEffect(haptic_, vibration_.id, &vibration_.effect) == 0) {
243 if (SDL_HapticRunEffect(haptic_, vibration_.id, 1) == 0)
return true;
245 SDL_HapticDestroyEffect(haptic_, vibration_.id);
249 vibration_.id = SDL_HapticNewEffect(haptic_, &vibration_.effect);
250 return vibration_.id != -1 && SDL_HapticRunEffect(haptic_, vibration_.id, 1) == 0;
254 left = std::min(std::max(
left, 0.0f), 1.0f);
255 right = std::min(std::max(
right, 0.0f), 1.0f);
258 if (!checkCreateHaptic())
return false;
260 Uint32
length = SDL_HAPTIC_INFINITY;
262 float maxduration =
static_cast<float>(std::numeric_limits<Uint32>::max()) / 1000.0f;
263 length =
static_cast<Uint32
>(std::min(
duration, maxduration) * 1000.0f);
266 bool success =
false;
267 unsigned int features = SDL_HapticQuery(haptic_);
268 int axes = SDL_HapticNumAxes(haptic_);
270 if ((features & SDL_HAPTIC_LEFTRIGHT) != 0) {
271 memset(&vibration_.effect, 0,
sizeof(SDL_HapticEffect));
272 vibration_.effect.type = SDL_HAPTIC_LEFTRIGHT;
273 vibration_.effect.leftright.length =
length;
274 vibration_.effect.leftright.large_magnitude =
static_cast<Uint16
>(
left * 0xFFFF);
275 vibration_.effect.leftright.small_magnitude =
static_cast<Uint16
>(
right * 0xFFFF);
276 success = runVibrationEffect();
279 if (!success &&
isGamepad() && (features & SDL_HAPTIC_CUSTOM) && axes == 2) {
280 vibration_.data[0] = vibration_.data[2] =
static_cast<Uint16
>(
left * 0x7FFF);
281 vibration_.data[1] = vibration_.data[3] =
static_cast<Uint16
>(
right * 0x7FFF);
282 memset(&vibration_.effect, 0,
sizeof(SDL_HapticEffect));
283 vibration_.effect.type = SDL_HAPTIC_CUSTOM;
284 vibration_.effect.custom.length =
length;
285 vibration_.effect.custom.channels = 2;
286 vibration_.effect.custom.period = 10;
287 vibration_.effect.custom.samples = 2;
288 vibration_.effect.custom.data = vibration_.data;
289 success = runVibrationEffect();
292 if (!success && (features & SDL_HAPTIC_SINE) != 0) {
293 memset(&vibration_.effect, 0,
sizeof(SDL_HapticEffect));
294 vibration_.effect.type = SDL_HAPTIC_SINE;
295 vibration_.effect.periodic.length =
length;
296 vibration_.effect.periodic.period = 10;
298 vibration_.effect.periodic.magnitude =
static_cast<Sint16
>(
strength * 0x7FFF);
299 success = runVibrationEffect();
303 vibration_.left =
left;
304 vibration_.right =
right;
306 (
length == SDL_HAPTIC_INFINITY) ? SDL_HAPTIC_INFINITY : (SDL_GetTicks() +
length);
308 vibration_.left = vibration_.right = 0.0f;
309 vibration_.endtime = SDL_HAPTIC_INFINITY;
316 if (SDL_WasInit(SDL_INIT_HAPTIC) && haptic_ && SDL_HapticIndex(haptic_) != -1)
317 success = (SDL_HapticStopEffect(haptic_, vibration_.id) == 0);
318 if (success) vibration_.left = vibration_.right = 0.0f;
323 if (vibration_.endtime != SDL_HAPTIC_INFINITY) {
326 vibration_.endtime = SDL_HAPTIC_INFINITY;
330 int id = vibration_.id;
331 if (!haptic_ ||
id == -1 || SDL_HapticGetEffectStatus(haptic_,
id) != 1)
332 vibration_.left = vibration_.right = 0.0f;
334 left = vibration_.left;
335 right = vibration_.right;
#define SDL_TICKS_PASSED(A, B)
void close() override
Closes joystick and gamecontroller handles.
int getID() const override
Engine-side stable pad id assigned at construction.
~Pad() override
Closes SDL handles and stops rumble.
void getVibration(float &left, float &right) override
Reads the last requested left/right rumble strengths.
bool isGamepad() const override
True when a GameController mapping is active for this pad.
float getGamepadAxis(const std::string &axis) const override
Named gamepad axis value (SDL GameController axis string).
bool isVibrationSupported() override
True when haptic/rumble output is available.
bool isConnected() const override
True while the underlying device is still connected.
bool isGamepadDown(const std::string &button) const override
True if the named gamepad button is pressed.
std::vector< float > getAxes() const override
Snapshot of all raw axis values.
int getAxisCount() const override
Number of raw joystick axes.
Pad(int id)
Creates a closed pad with the given engine id.
std::string getGamepadMappingString() const override
Current SDL GameController mapping string for this device.
bool setVibration() override
Starts rumble; duration < 0 means until stopped.
std::string getName() const override
Human-readable device name from the OS/driver.
int getHatCount() const override
Number of hat (D-pad) controls.
bool open(int deviceindex) override
Opens the device by SDL joystick device index.
int getButtonCount() const override
Number of raw joystick buttons.
std::string getHat(int hatindex) const override
Hat direction string for a hat index (see class brief).
int getInstanceID() const override
SDL instance id that stays unique while the device is open.
bool openGamepad(int deviceindex) override
Opens the device as an SDL GameController.
void getDeviceInfo(int &vendorID, int &productID, int &productVersion) const override
USB/HID vendor, product, and version ids when available.
std::string getGUID() const override
Stable GUID string for mapping lookup.
void * getHandle() const override
Native handle (SDL_Joystick* / implementation-defined).
bool isDown(int button) const override
True if the raw button index is currently pressed.
float getAxis(int axisindex) const override
Normalized axis value in [-1, 1] for a raw axis index.
float clampAxis(float x)
Clamps a raw axis sample into [-1, 1].