16 StartupStage stage(
"joystick: SDL subsystem init + device enumeration");
17 if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0)
18 throw eve::Exception(
"Could not initialize SDL joystick subsystem (%s)", SDL_GetError());
20 for (
int i = 0; i < SDL_NumJoysticks(); i++)
addJoystick(i);
22 SDL_JoystickEventState(SDL_ENABLE);
23 SDL_GameControllerEventState(SDL_ENABLE);
29 for (
auto* stick : joysticks_) {
34 activeSticks_.clear();
36 if (SDL_WasInit(SDL_INIT_HAPTIC) != 0) SDL_QuitSubSystem(SDL_INIT_HAPTIC);
37 SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
41 if (joyindex < 0 ||
static_cast<size_t>(joyindex) >= activeSticks_.size())
return nullptr;
42 return activeSticks_[
static_cast<size_t>(joyindex)];
46 for (
int i = 0; i < static_cast<int>(activeSticks_.size()); i++) {
47 if (activeSticks_[
static_cast<size_t>(i)] ==
pad)
return i;
53 return static_cast<int>(activeSticks_.size());
57 for (
auto* stick : activeSticks_) {
58 if (stick->getInstanceID() == instanceid)
return stick;
64 if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks())
return nullptr;
66 std::string guidstr = getDeviceGUID(deviceindex);
70 for (
auto* stick : joysticks_) {
71 if (!stick->isConnected() && stick->getGUID() == guidstr) {
79 pad =
new Pad(
static_cast<int>(joysticks_.size()));
80 joysticks_.push_back(
pad);
85 if (!
pad->open(deviceindex))
return nullptr;
87 for (
auto*
active : activeSticks_) {
88 if (
pad->getHandle() ==
active->getHandle()) {
91 joysticks_.remove(
pad);
98 if (
pad->isGamepad()) recentGamepadGUIDs_[
pad->getGUID()] =
true;
100 activeSticks_.push_back(
pad);
106 auto it = std::find(activeSticks_.begin(), activeSticks_.end(),
pad);
107 if (it != activeSticks_.end()) {
109 activeSticks_.erase(it);
113void Joystick::checkGamepads(
const std::string& guid)
const {
114 for (
int d_index = 0; d_index < SDL_NumJoysticks(); d_index++) {
115 if (!SDL_IsGameController(d_index))
continue;
116 if (guid.compare(getDeviceGUID(d_index)) != 0)
continue;
118 for (
auto* stick : activeSticks_) {
119 if (guid.compare(stick->getGUID()) != 0)
continue;
121 SDL_GameController* controller = SDL_GameControllerOpen(d_index);
122 if (!controller)
continue;
124 SDL_Joystick* sdlstick = SDL_GameControllerGetJoystick(controller);
125 bool open_gamepad = (sdlstick ==
static_cast<SDL_Joystick*
>(stick->getHandle()));
126 SDL_GameControllerClose(controller);
128 if (open_gamepad) stick->openGamepad(d_index);
133std::string Joystick::getDeviceGUID(
int deviceindex)
const {
134 if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks())
return "";
136 char guidstr[33] = {
'\0'};
137 SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(deviceindex);
138 SDL_JoystickGetGUIDString(guid, guidstr,
sizeof(guidstr));
143 std::stringstream ss(mappings);
145 bool success =
false;
147 while (std::getline(ss, mapping)) {
148 if (mapping.empty())
continue;
149 if (mapping[0] ==
'#')
continue;
151 size_t pstartpos = mapping.find(
"platform:");
152 if (pstartpos != std::string::npos) {
153 pstartpos += strlen(
"platform:");
154 size_t pendpos = mapping.find_first_of(
',', pstartpos);
155 std::string platform = mapping.substr(pstartpos, pendpos - pstartpos);
156 if (platform.compare(SDL_GetPlatform()) != 0) {
160 pstartpos -= strlen(
"platform:");
161 mapping.erase(pstartpos, pendpos - pstartpos + 1);
164 if (SDL_GameControllerAddMapping(mapping.c_str()) != -1) {
166 std::string guid = mapping.substr(0, mapping.find_first_of(
','));
167 recentGamepadGUIDs_[guid] =
true;
172 if (!success && !mappings.empty())
throw eve::Exception(
"Invalid gamepad mappings.");
176 SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(guid.c_str());
177 char* sdlmapping = SDL_GameControllerMappingForGUID(sdlguid);
178 if (!sdlmapping)
return "";
180 std::string mapping(sdlmapping);
181 SDL_free(sdlmapping);
183 if (mapping.find_last_of(
',') != mapping.length() - 1) mapping +=
",";
184 mapping +=
"platform:" + std::string(SDL_GetPlatform());
189 std::string mappings;
190 for (
const auto&
g : recentGamepadGUIDs_) {
191 SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(
g.first.c_str());
192 char* sdlmapping = SDL_GameControllerMappingForGUID(sdlguid);
193 if (!sdlmapping)
continue;
195 std::string mapping = sdlmapping;
196 SDL_free(sdlmapping);
198 if (mapping.find_last_of(
',') != mapping.length() - 1) mapping +=
",";
199 mapping +=
"platform:" + std::string(SDL_GetPlatform()) +
",\n";
EVENGINE_API_FOUNDATION public API.
One connected (or previously connected) joystick / gamepad device. Gamepad axis/button names follow S...
int getJoystickCount() const override
Number of currently tracked pads.
Joystick()
Initializes SDL joystick subsystems used by this backend.
std::string saveGamepadMappings() override
Serializes the in-memory GameController mapping database.
void removeJoystick(eve::joystick::Pad *pad) override
Removes and destroys a previously opened pad.
std::string getGamepadMappingString(const std::string &guid) const override
Mapping string for a device GUID, or empty if none.
Pad * addJoystick(int deviceindex) override
Open a device by SDL device index; returns null on failure.
void loadGamepadMappings(const std::string &mappings) override
Load SDL GameController mapping database (newline-separated).
int getIndex(const eve::joystick::Pad *pad) override
Dense index of pad, or -1 if not tracked.
~Joystick() override
Closes all tracked pads.
Pad * getJoystick(int joyindex) override
Pad at a dense joyindex in [0, getJoystickCount()).
Pad * getJoystickFromID(int instanceid) override
Looks up a pad by SDL instance id; null if unknown.
SDL joystick/gamepad backend (GameController + optional haptic rumble).
void bindJoystickEventSinkOwner(eve::joystick::Joystick *owner) noexcept
Bind the live Joystick module into the SDL event sink. @ownership Borrowed; the Joystick module owns ...