载入中...
搜索中...
未找到
Pad.cpp
浏览该文件的文档.
1#include "joystick/sdl/Pad.h"
2
3#include "common/Exception.h"
4
5#include <algorithm>
6#include <cstring>
7#include <limits>
8
9#ifndef SDL_TICKS_PASSED
10#define SDL_TICKS_PASSED(A, B) ((Sint32)((B) - (A)) <= 0)
11#endif
12
13namespace eve::joystick::sdl {
14namespace {
15
16std::string hatToString(Uint8 value) {
17 switch (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";
27 default: return "";
28 }
29}
30
31} // namespace
32
33Pad::Pad(int id) : id_(id) {}
34
35Pad::Pad(int id, int joyindex) : id_(id) {
36 open(joyindex);
37}
38
40 close();
41}
42
43bool Pad::open(int deviceindex) {
44 close();
45
46 joyhandle_ = SDL_JoystickOpen(deviceindex);
47 if (!joyhandle_) return false;
48
49 instanceid_ = SDL_JoystickInstanceID(joyhandle_);
50
51 char cstr[33];
52 SDL_JoystickGUID sdlguid = SDL_JoystickGetGUID(joyhandle_);
53 SDL_JoystickGetGUIDString(sdlguid, cstr, static_cast<int>(sizeof(cstr)));
54 guid_ = cstr;
55
56 openGamepad(deviceindex);
57
58 const char* joyname = SDL_JoystickName(joyhandle_);
59 if (!joyname && controller_) joyname = SDL_GameControllerName(controller_);
60 name_ = joyname ? joyname : "";
61
62 return isConnected();
63}
64
65void Pad::close() {
66 if (haptic_) SDL_HapticClose(haptic_);
67 if (controller_) SDL_GameControllerClose(controller_);
68 if (joyhandle_) SDL_JoystickClose(joyhandle_);
69
70 joyhandle_ = nullptr;
71 controller_ = nullptr;
72 haptic_ = nullptr;
73 instanceid_ = -1;
74 vibration_ = Vibration{};
75}
76
77bool Pad::isConnected() const {
78 return joyhandle_ != nullptr && SDL_JoystickGetAttached(joyhandle_);
79}
80
81std::string Pad::getName() const {
82 return name_;
83}
84
85int Pad::getAxisCount() const {
86 return isConnected() ? SDL_JoystickNumAxes(joyhandle_) : 0;
87}
88
90 return isConnected() ? SDL_JoystickNumButtons(joyhandle_) : 0;
91}
92
93int Pad::getHatCount() const {
94 return isConnected() ? SDL_JoystickNumHats(joyhandle_) : 0;
95}
96
97float Pad::getAxis(int axisindex) const {
98 if (!isConnected() || axisindex < 0 || axisindex >= getAxisCount()) return 0.f;
99 return clampAxis(static_cast<float>(SDL_JoystickGetAxis(joyhandle_, axisindex)) / 32768.0f);
100}
101
102std::vector<float> Pad::getAxes() const {
103 std::vector<float> axes;
104 int count = getAxisCount();
105 if (!isConnected() || count <= 0) return 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));
109 return axes;
110}
111
112std::string Pad::getHat(int hatindex) const {
113 if (!isConnected() || hatindex < 0 || hatindex >= getHatCount()) return "";
114 return hatToString(SDL_JoystickGetHat(joyhandle_, hatindex));
115}
116
117bool Pad::isDown(int button) const {
118 return isDown(std::vector<int>{button});
119}
120
121bool Pad::isDown(const std::vector<int>& buttons) const {
122 if (!isConnected()) return false;
123 int numbuttons = getButtonCount();
124 for (int button : buttons) {
125 if (button < 0 || button >= numbuttons) continue;
126 if (SDL_JoystickGetButton(joyhandle_, button) == 1) return true;
127 }
128 return false;
129}
130
131bool Pad::openGamepad(int deviceindex) {
132 if (!SDL_IsGameController(deviceindex)) return false;
133
134 if (controller_) {
135 SDL_GameControllerClose(controller_);
136 controller_ = nullptr;
137 }
138
139 controller_ = SDL_GameControllerOpen(deviceindex);
140 return isGamepad();
141}
142
143bool Pad::isGamepad() const {
144 return controller_ != nullptr;
145}
146
147float Pad::getGamepadAxis(const std::string& axis) const {
148 if (!isConnected() || !isGamepad()) return 0.f;
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);
152 return clampAxis(static_cast<float>(value) / 32768.0f);
153}
154
155bool Pad::isGamepadDown(const std::string& button) const {
156 return isGamepadDown(std::vector<std::string>{button});
157}
158
159bool Pad::isGamepadDown(const std::vector<std::string>& buttons) const {
160 if (!isConnected() || !isGamepad()) return false;
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;
165 }
166 return false;
167}
168
169std::string Pad::getGamepadMappingString() const {
170 char* sdlmapping = nullptr;
171 if (controller_) sdlmapping = SDL_GameControllerMapping(controller_);
172 if (!sdlmapping) {
173 SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(guid_.c_str());
174 sdlmapping = SDL_GameControllerMappingForGUID(sdlguid);
175 }
176 if (!sdlmapping) return "";
177
178 std::string mappingstr(sdlmapping);
179 SDL_free(sdlmapping);
180
181 if (mappingstr.find_last_of(',') != mappingstr.length() - 1) mappingstr += ",";
182 mappingstr += "platform:" + std::string(SDL_GetPlatform());
183 return mappingstr;
184}
185
186void* Pad::getHandle() const {
187 return joyhandle_;
188}
189
190std::string Pad::getGUID() const {
191 return guid_;
192}
193
195 return static_cast<int>(instanceid_);
196}
197
198int Pad::getID() const {
199 return id_;
200}
201
202void Pad::getDeviceInfo(int& vendorID, int& productID, int& productVersion) const {
203#if SDL_VERSION_ATLEAST(2, 0, 6)
204 if (joyhandle_) {
205 vendorID = SDL_JoystickGetVendor(joyhandle_);
206 productID = SDL_JoystickGetProduct(joyhandle_);
207 productVersion = SDL_JoystickGetProductVersion(joyhandle_);
208 return;
209 }
210#endif
211 vendorID = productID = productVersion = 0;
212}
213
214bool Pad::checkCreateHaptic() {
215 if (!isConnected()) return false;
216
217 if (!SDL_WasInit(SDL_INIT_HAPTIC) && SDL_InitSubSystem(SDL_INIT_HAPTIC) < 0) return false;
218
219 if (haptic_ && SDL_HapticIndex(haptic_) != -1) return true;
220
221 if (haptic_) {
222 SDL_HapticClose(haptic_);
223 haptic_ = nullptr;
224 }
225
226 haptic_ = SDL_HapticOpenFromJoystick(joyhandle_);
227 vibration_ = Vibration{};
228 return haptic_ != nullptr;
229}
230
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;
237 return false;
238}
239
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;
244 }
245 SDL_HapticDestroyEffect(haptic_, vibration_.id);
246 vibration_.id = -1;
247 }
248
249 vibration_.id = SDL_HapticNewEffect(haptic_, &vibration_.effect);
250 return vibration_.id != -1 && SDL_HapticRunEffect(haptic_, vibration_.id, 1) == 0;
251}
252
253bool Pad::setVibration(float left, float right, float duration) {
254 left = std::min(std::max(left, 0.0f), 1.0f);
255 right = std::min(std::max(right, 0.0f), 1.0f);
256
257 if (left == 0.0f && right == 0.0f) return setVibration();
258 if (!checkCreateHaptic()) return false;
259
260 Uint32 length = SDL_HAPTIC_INFINITY;
261 if (duration >= 0.0f) {
262 float maxduration = static_cast<float>(std::numeric_limits<Uint32>::max()) / 1000.0f;
263 length = static_cast<Uint32>(std::min(duration, maxduration) * 1000.0f);
264 }
265
266 bool success = false;
267 unsigned int features = SDL_HapticQuery(haptic_);
268 int axes = SDL_HapticNumAxes(haptic_);
269
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();
277 }
278
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();
290 }
291
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;
297 float strength = std::max(left, right);
298 vibration_.effect.periodic.magnitude = static_cast<Sint16>(strength * 0x7FFF);
299 success = runVibrationEffect();
300 }
301
302 if (success) {
303 vibration_.left = left;
304 vibration_.right = right;
305 vibration_.endtime =
306 (length == SDL_HAPTIC_INFINITY) ? SDL_HAPTIC_INFINITY : (SDL_GetTicks() + length);
307 } else {
308 vibration_.left = vibration_.right = 0.0f;
309 vibration_.endtime = SDL_HAPTIC_INFINITY;
310 }
311 return success;
312}
313
315 bool success = true;
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;
319 return success;
320}
321
322void Pad::getVibration(float& left, float& right) {
323 if (vibration_.endtime != SDL_HAPTIC_INFINITY) {
324 if (SDL_TICKS_PASSED(SDL_GetTicks(), vibration_.endtime)) {
325 setVibration();
326 vibration_.endtime = SDL_HAPTIC_INFINITY;
327 }
328 }
329
330 int id = vibration_.id;
331 if (!haptic_ || id == -1 || SDL_HapticGetEffectStatus(haptic_, id) != 1)
332 vibration_.left = vibration_.right = 0.0f;
333
334 left = vibration_.left;
335 right = vibration_.right;
336}
337
338} // namespace eve::joystick::sdl
std::string value
std::string id
#define SDL_TICKS_PASSED(A, B)
Definition Pad.cpp:10
const char * name
Definition RockMesh.cpp:21
void close() override
Definition Pad.cpp:65
int getID() const override
Definition Pad.cpp:198
~Pad() override
Definition Pad.cpp:39
void getVibration(float &left, float &right) override
Definition Pad.cpp:322
bool isGamepad() const override
Definition Pad.cpp:143
float getGamepadAxis(const std::string &axis) const override
Definition Pad.cpp:147
bool isVibrationSupported() override
Definition Pad.cpp:231
bool isConnected() const override
Definition Pad.cpp:77
bool isGamepadDown(const std::string &button) const override
Definition Pad.cpp:155
std::vector< float > getAxes() const override
Definition Pad.cpp:102
int getAxisCount() const override
Definition Pad.cpp:85
std::string getGamepadMappingString() const override
Definition Pad.cpp:169
bool setVibration() override
Definition Pad.cpp:314
std::string getName() const override
Definition Pad.cpp:81
int getHatCount() const override
Definition Pad.cpp:93
bool open(int deviceindex) override
Definition Pad.cpp:43
int getButtonCount() const override
Definition Pad.cpp:89
std::string getHat(int hatindex) const override
Definition Pad.cpp:112
int getInstanceID() const override
Definition Pad.cpp:194
bool openGamepad(int deviceindex) override
Definition Pad.cpp:131
void getDeviceInfo(int &vendorID, int &productID, int &productVersion) const override
Definition Pad.cpp:202
std::string getGUID() const override
Definition Pad.cpp:190
void * getHandle() const override
Definition Pad.cpp:186
bool isDown(int button) const override
Definition Pad.cpp:117
float getAxis(int axisindex) const override
Definition Pad.cpp:97
float clampAxis(float x)
Definition Joystick.cpp:14