QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
SDLJoystick.cc
Go to the documentation of this file.
1#include "SDLJoystick.h"
3
4#include <QtCore/QFile>
5#include <QtCore/QHash>
6#include <QtCore/QTextStream>
7#include <QtCore/QStandardPaths>
8
9#include <SDL3/SDL.h>
10
11QGC_LOGGING_CATEGORY(SDLJoystickLog, "qgc.utilities.sdl.joystick")
12
13namespace SDLJoystick {
14
15// ============================================================================
16// Internal Helpers
17// ============================================================================
18
19static QHash<int, QString> s_virtualJoystickNames;
20
22{
23 const auto loadMappingsFromFile = [](const QString &path, const char *description) {
24 QFile file(path);
25 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
26 return false;
27 }
28
29 qCDebug(SDLJoystickLog) << "Loading gamepad mappings from" << description;
30 int count = 0;
31 QTextStream stream(&file);
32 while (!stream.atEnd()) {
33 const QString line = stream.readLine();
34 if (line.startsWith('#') || line.isEmpty()) {
35 continue;
36 }
37 if (SDL_AddGamepadMapping(qPrintable(line)) == -1) {
38 qCWarning(SDLJoystickLog) << "Couldn't add gamepad mapping:" << SDL_GetError();
39 } else {
40 ++count;
41 }
42 }
43 qCDebug(SDLJoystickLog) << "Loaded" << count << "mappings from" << description;
44 return true;
45 };
46
47 // Load bundled database
48 if (!loadMappingsFromFile(QStringLiteral(":/gamecontrollerdb.txt"), "bundled database")) {
49 qCWarning(SDLJoystickLog) << "Couldn't load bundled gamepad mapping database";
50 }
51
52 // Load user custom mappings
53 const QString userMappingsPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)
54 + QStringLiteral("/gamecontrollerdb.txt");
55 if (loadMappingsFromFile(userMappingsPath, "user config")) {
56 qCInfo(SDLJoystickLog) << "Loaded user gamepad mappings from" << userMappingsPath;
57 }
58}
59
60// ============================================================================
61// Initialization
62// ============================================================================
63
64bool init()
65{
66 qCDebug(SDLJoystickLog) << "Initializing SDL joystick subsystem";
67
68 // Allow joystick events when app is in background (critical for ground stations)
69 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
70
71#ifdef Q_OS_ANDROID
72 // On Android, prevent SDL from trying to load the internal gamepad config file
73 SDL_SetHint(SDL_HINT_GAMECONTROLLERCONFIG_FILE, "");
74#endif
75
76 // Enable Steam controller support
77 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1");
78 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAMDECK, "1");
79
80 // Enable HIDAPI driver for better controller support
81 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI, "1");
82 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
83 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1");
84 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED, "1");
85 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "1");
86 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360, "1");
87 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_WIRELESS, "1");
88
89 // Enable HIDAPI for Nintendo controllers
90 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
91 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
92 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "1");
93
94 // Enable HIDAPI for other controllers
95 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_WII, "1");
96 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_WII_PLAYER_LED, "1");
97 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1");
98 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_8BITDO, "1");
99 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_FLYDIGI, "1");
100 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_LUNA, "1");
101 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STADIA, "1");
102
103 // Enable sensor fusion
104 SDL_SetHint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION, "1");
105
106#ifdef Q_OS_WIN
107 SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "1");
108#endif
109
110#ifdef Q_OS_LINUX
111 SDL_SetHint(SDL_HINT_JOYSTICK_LINUX_DEADZONES, "1");
112 SDL_SetHint(SDL_HINT_JOYSTICK_LINUX_DIGITAL_HATS, "1");
113#endif
114
115 // Enable threaded joystick processing
116 SDL_SetHint(SDL_HINT_JOYSTICK_THREAD, "1");
117
118 // Enable enhanced reports for gyro/accel data
119 SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, "1");
120
121 if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD | SDL_INIT_JOYSTICK)) {
122 qCWarning(SDLJoystickLog) << "Failed to initialize SDL:" << SDL_GetError();
123 return false;
124 }
125
127
128 qCDebug(SDLJoystickLog) << "SDL joystick subsystem initialized successfully";
129 return true;
130}
131
133{
134 qCDebug(SDLJoystickLog) << "Shutting down SDL joystick subsystem";
136 SDL_QuitSubSystem(SDL_INIT_GAMEPAD | SDL_INIT_JOYSTICK);
137}
138
140{
141 return SDL_WasInit(SDL_INIT_JOYSTICK) != 0;
142}
143
144// ============================================================================
145// Event Control
146// ============================================================================
147
149{
150 SDL_PumpEvents();
151 SDL_UpdateJoysticks();
152}
153
154void setJoystickEventsEnabled(bool enabled)
155{
156 SDL_SetJoystickEventsEnabled(enabled);
157}
158
160{
161 return SDL_JoystickEventsEnabled();
162}
163
164void setGamepadEventsEnabled(bool enabled)
165{
166 SDL_SetGamepadEventsEnabled(enabled);
167}
168
170{
171 return SDL_GamepadEventsEnabled();
172}
173
175{
176 SDL_UpdateJoysticks();
177}
178
180{
181 SDL_UpdateGamepads();
182}
183
184// ============================================================================
185// Thread Safety
186// ============================================================================
187
189{
190 SDL_LockJoysticks();
191}
192
194{
195 SDL_UnlockJoysticks();
196}
197
198// ============================================================================
199// Type/String Conversions
200// ============================================================================
201
202QString gamepadTypeToString(int type)
203{
204 const char *str = SDL_GetGamepadStringForType(static_cast<SDL_GamepadType>(type));
205 return str ? QString::fromUtf8(str) : QString();
206}
207
208int gamepadTypeFromString(const QString &str)
209{
210 if (str.isEmpty()) {
211 return SDL_GAMEPAD_TYPE_UNKNOWN;
212 }
213 return SDL_GetGamepadTypeFromString(qPrintable(str));
214}
215
216QString gamepadTypeDisplayName(int type)
217{
218 switch (static_cast<SDL_GamepadType>(type)) {
219 case SDL_GAMEPAD_TYPE_STANDARD:
220 return QStringLiteral("Standard");
221 case SDL_GAMEPAD_TYPE_XBOX360:
222 return QStringLiteral("Xbox 360");
223 case SDL_GAMEPAD_TYPE_XBOXONE:
224 return QStringLiteral("Xbox One");
225 case SDL_GAMEPAD_TYPE_PS3:
226 return QStringLiteral("PlayStation 3");
227 case SDL_GAMEPAD_TYPE_PS4:
228 return QStringLiteral("PlayStation 4");
229 case SDL_GAMEPAD_TYPE_PS5:
230 return QStringLiteral("PlayStation 5");
231 case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO:
232 return QStringLiteral("Nintendo Switch Pro");
233 case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
234 return QStringLiteral("Nintendo Joy-Con (L)");
235 case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
236 return QStringLiteral("Nintendo Joy-Con (R)");
237 case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
238 return QStringLiteral("Nintendo Joy-Con Pair");
239 default:
240 return QStringLiteral("Unknown");
241 }
242}
243
244QString gamepadAxisToString(int axis)
245{
246 const char *str = SDL_GetGamepadStringForAxis(static_cast<SDL_GamepadAxis>(axis));
247 return str ? QString::fromUtf8(str) : QString();
248}
249
250int gamepadAxisFromString(const QString &str)
251{
252 if (str.isEmpty()) {
253 return SDL_GAMEPAD_AXIS_INVALID;
254 }
255 return SDL_GetGamepadAxisFromString(qPrintable(str));
256}
257
258QString gamepadButtonToString(int button)
259{
260 const char *str = SDL_GetGamepadStringForButton(static_cast<SDL_GamepadButton>(button));
261 return str ? QString::fromUtf8(str) : QString();
262}
263
264int gamepadButtonFromString(const QString &str)
265{
266 if (str.isEmpty()) {
267 return SDL_GAMEPAD_BUTTON_INVALID;
268 }
269 return SDL_GetGamepadButtonFromString(qPrintable(str));
270}
271
272QString connectionStateToString(int state)
273{
274 switch (static_cast<SDL_JoystickConnectionState>(state)) {
275 case SDL_JOYSTICK_CONNECTION_INVALID:
276 return QStringLiteral("Invalid");
277 case SDL_JOYSTICK_CONNECTION_UNKNOWN:
278 return QStringLiteral("Unknown");
279 case SDL_JOYSTICK_CONNECTION_WIRED:
280 return QStringLiteral("Wired");
281 case SDL_JOYSTICK_CONNECTION_WIRELESS:
282 return QStringLiteral("Wireless");
283 default:
284 return QString();
285 }
286}
287
288// ============================================================================
289// GUID Utilities
290// ============================================================================
291
292QVariantMap getGUIDInfo(const QString &guid)
293{
294 QVariantMap result;
295 result[QStringLiteral("valid")] = false;
296 result[QStringLiteral("vendor")] = 0;
297 result[QStringLiteral("product")] = 0;
298 result[QStringLiteral("version")] = 0;
299 result[QStringLiteral("crc16")] = 0;
300
301 if (guid.isEmpty()) {
302 return result;
303 }
304
305 SDL_GUID sdlGuid = SDL_StringToGUID(qPrintable(guid));
306 Uint16 vendor = 0, product = 0, version = 0, crc16 = 0;
307 SDL_GetJoystickGUIDInfo(sdlGuid, &vendor, &product, &version, &crc16);
308
309 result[QStringLiteral("valid")] = true;
310 result[QStringLiteral("vendor")] = static_cast<int>(vendor);
311 result[QStringLiteral("product")] = static_cast<int>(product);
312 result[QStringLiteral("version")] = static_cast<int>(version);
313 result[QStringLiteral("crc16")] = static_cast<int>(crc16);
314
315 return result;
316}
317
318QString getMappingForGUID(const QString &guid)
319{
320 if (guid.isEmpty()) {
321 return QString();
322 }
323
324 SDL_GUID sdlGuid = SDL_StringToGUID(qPrintable(guid));
325 char *mapping = SDL_GetGamepadMappingForGUID(sdlGuid);
326 if (mapping) {
327 QString result = QString::fromUtf8(mapping);
328 SDL_free(mapping);
329 return result;
330 }
331 return QString();
332}
333
334// ============================================================================
335// Mapping Management
336// ============================================================================
337
338int addMappingsFromFile(const QString &filePath)
339{
340 if (filePath.isEmpty()) {
341 return -1;
342 }
343
344 int count = SDL_AddGamepadMappingsFromFile(qPrintable(filePath));
345 if (count < 0) {
346 qCDebug(SDLJoystickLog) << "Failed to load mappings from" << filePath << ":" << SDL_GetError();
347 } else {
348 qCDebug(SDLJoystickLog) << "Loaded" << count << "mappings from" << filePath;
349 }
350 return count;
351}
352
353bool addMapping(const QString &mapping)
354{
355 if (mapping.isEmpty()) {
356 return false;
357 }
358
359 int result = SDL_AddGamepadMapping(qPrintable(mapping));
360 if (result == -1) {
361 qCWarning(SDLJoystickLog) << "Failed to add gamepad mapping:" << SDL_GetError();
362 return false;
363 }
364
365 qCDebug(SDLJoystickLog) << "Added gamepad mapping, result:" << result;
366 return true;
367}
368
370{
371 return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)
372 + QStringLiteral("/gamecontrollerdb.txt");
373}
374
375bool addMappingPersistent(const QString &mapping)
376{
377 if (mapping.isEmpty()) {
378 return false;
379 }
380
381 // First add to SDL's runtime mapping database
382 if (!addMapping(mapping)) {
383 return false;
384 }
385
386 // Extract GUID from mapping (format: "GUID,name,mapping...")
387 const int commaPos = mapping.indexOf(',');
388 if (commaPos <= 0) {
389 qCWarning(SDLJoystickLog) << "Invalid mapping format, cannot extract GUID";
390 return false;
391 }
392 const QString guid = mapping.left(commaPos);
393
394 // Now persist to user's config file
395 const QString filePath = userMappingsFilePath();
396 QFile file(filePath);
397
398 // Read existing mappings, filtering out any with the same GUID
399 QStringList existingMappings;
400 if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
401 QTextStream in(&file);
402 while (!in.atEnd()) {
403 const QString line = in.readLine();
404 if (line.isEmpty() || line.startsWith('#')) {
405 existingMappings.append(line);
406 continue;
407 }
408 // Skip if same GUID (we'll add the new mapping)
409 if (!line.startsWith(guid + ',')) {
410 existingMappings.append(line);
411 }
412 }
413 file.close();
414 }
415
416 // Write back with new mapping
417 if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
418 qCWarning(SDLJoystickLog) << "Failed to open user mappings file for writing:" << filePath;
419 return false;
420 }
421
422 QTextStream out(&file);
423 out << "# QGroundControl User Gamepad Mappings\n";
424 out << "# Generated automatically - edit with caution\n\n";
425 for (const QString &line : existingMappings) {
426 if (!line.isEmpty()) {
427 out << line << "\n";
428 }
429 }
430 out << mapping << "\n";
431 file.close();
432
433 qCInfo(SDLJoystickLog) << "Persisted gamepad mapping for GUID:" << guid << "to" << filePath;
434 return true;
435}
436
438{
439 // SDL_ReloadGamepadMappings() reloads built-in mappings only.
440 // We need to re-apply our bundled and user mappings afterwards.
441 // Note: This does NOT call SDL_ReloadGamepadMappings() first because
442 // that would reset to SDL defaults, and loadGamepadMappings() adds
443 // our mappings on top. If we need a full reset, call both explicitly.
445
446 qCDebug(SDLJoystickLog) << "Reloaded gamepad mappings";
447 return true;
448}
449
450// ============================================================================
451// Player Index
452// ============================================================================
453
455{
456 SDL_Gamepad *gamepad = SDL_GetGamepadFromPlayerIndex(playerIndex);
457 if (gamepad) {
458 SDL_JoystickID id = SDL_GetGamepadID(gamepad);
459 return static_cast<int>(id);
460 }
461
462 SDL_Joystick *joystick = SDL_GetJoystickFromPlayerIndex(playerIndex);
463 if (joystick) {
464 SDL_JoystickID id = SDL_GetJoystickID(joystick);
465 return static_cast<int>(id);
466 }
467
468 return -1;
469}
470
471// ============================================================================
472// Pre-Open Device Queries
473// ============================================================================
474
475QString getNameForInstanceId(int instanceId)
476{
477 const char *name = SDL_GetGamepadNameForID(static_cast<SDL_JoystickID>(instanceId));
478 if (name && *name) {
479 return QString::fromUtf8(name);
480 }
481 name = SDL_GetJoystickNameForID(static_cast<SDL_JoystickID>(instanceId));
482 if (name && *name) {
483 return QString::fromUtf8(name);
484 }
485
486 // SDL may report an empty name for freshly-created virtual devices on some platforms.
487 return s_virtualJoystickNames.value(instanceId);
488}
489
490QString getPathForInstanceId(int instanceId)
491{
492 const char *path = SDL_GetGamepadPathForID(static_cast<SDL_JoystickID>(instanceId));
493 if (path) {
494 return QString::fromUtf8(path);
495 }
496 path = SDL_GetJoystickPathForID(static_cast<SDL_JoystickID>(instanceId));
497 return path ? QString::fromUtf8(path) : QString();
498}
499
500QString getGUIDForInstanceId(int instanceId)
501{
502 SDL_GUID guid = SDL_GetJoystickGUIDForID(static_cast<SDL_JoystickID>(instanceId));
503 char guidStr[33];
504 SDL_GUIDToString(guid, guidStr, sizeof(guidStr));
505 return QString::fromUtf8(guidStr);
506}
507
508int getVendorForInstanceId(int instanceId)
509{
510 Uint16 vendor = SDL_GetGamepadVendorForID(static_cast<SDL_JoystickID>(instanceId));
511 if (vendor != 0) {
512 return vendor;
513 }
514 return SDL_GetJoystickVendorForID(static_cast<SDL_JoystickID>(instanceId));
515}
516
517int getProductForInstanceId(int instanceId)
518{
519 Uint16 product = SDL_GetGamepadProductForID(static_cast<SDL_JoystickID>(instanceId));
520 if (product != 0) {
521 return product;
522 }
523 return SDL_GetJoystickProductForID(static_cast<SDL_JoystickID>(instanceId));
524}
525
527{
528 Uint16 version = SDL_GetGamepadProductVersionForID(static_cast<SDL_JoystickID>(instanceId));
529 if (version != 0) {
530 return version;
531 }
532 return SDL_GetJoystickProductVersionForID(static_cast<SDL_JoystickID>(instanceId));
533}
534
535QString getTypeForInstanceId(int instanceId)
536{
537 SDL_GamepadType type = SDL_GetGamepadTypeForID(static_cast<SDL_JoystickID>(instanceId));
538 return gamepadTypeToString(static_cast<int>(type));
539}
540
541QString getRealTypeForInstanceId(int instanceId)
542{
543 SDL_GamepadType type = SDL_GetRealGamepadTypeForID(static_cast<SDL_JoystickID>(instanceId));
544 return gamepadTypeToString(static_cast<int>(type));
545}
546
548{
549 int index = SDL_GetGamepadPlayerIndexForID(static_cast<SDL_JoystickID>(instanceId));
550 if (index >= 0) {
551 return index;
552 }
553 return SDL_GetJoystickPlayerIndexForID(static_cast<SDL_JoystickID>(instanceId));
554}
555
556QString getConnectionStateForInstanceId(int instanceId)
557{
558 // Try gamepad first, then joystick
559 SDL_JoystickConnectionState state = SDL_GetGamepadConnectionState(SDL_GetGamepadFromID(static_cast<SDL_JoystickID>(instanceId)));
560 if (state == SDL_JOYSTICK_CONNECTION_INVALID) {
561 state = SDL_GetJoystickConnectionState(SDL_GetJoystickFromID(static_cast<SDL_JoystickID>(instanceId)));
562 }
563 return connectionStateToString(static_cast<int>(state));
564}
565
566// ============================================================================
567// Virtual Joystick
568// ============================================================================
569
570int createVirtualJoystick(const QString &name, int axisCount, int buttonCount, int hatCount)
571{
572 SDL_VirtualJoystickDesc desc;
573 SDL_INIT_INTERFACE(&desc);
574 desc.type = SDL_JOYSTICK_TYPE_GAMEPAD;
575 desc.naxes = static_cast<Uint16>(axisCount);
576 desc.nbuttons = static_cast<Uint16>(buttonCount);
577 desc.nhats = static_cast<Uint16>(hatCount);
578 const QByteArray utf8Name = name.toUtf8();
579 desc.name = utf8Name.constData();
580
581 SDL_JoystickID instanceId = SDL_AttachVirtualJoystick(&desc);
582 if (instanceId == 0) {
583 qCWarning(SDLJoystickLog) << "Failed to create virtual joystick:" << SDL_GetError();
584 return -1;
585 }
586
587 s_virtualJoystickNames.insert(static_cast<int>(instanceId), name);
588
589 qCDebug(SDLJoystickLog) << "Created virtual joystick" << name << "with instance ID" << instanceId;
590 return static_cast<int>(instanceId);
591}
592
593bool destroyVirtualJoystick(int instanceId)
594{
595 if (instanceId <= 0) {
596 return false;
597 }
598
599 if (!SDL_DetachVirtualJoystick(static_cast<SDL_JoystickID>(instanceId))) {
600 qCWarning(SDLJoystickLog) << "Failed to destroy virtual joystick" << instanceId << ":" << SDL_GetError();
601 return false;
602 }
603
604 s_virtualJoystickNames.remove(instanceId);
605
606 qCDebug(SDLJoystickLog) << "Destroyed virtual joystick" << instanceId;
607 return true;
608}
609
610bool isVirtualJoystick(int instanceId)
611{
612 return SDL_IsJoystickVirtual(static_cast<SDL_JoystickID>(instanceId));
613}
614
615// ============================================================================
616// Gamepad Binding Utilities
617// ============================================================================
618
619void populateBindingResult(QVariantMap &result, const SDL_GamepadBinding *binding)
620{
621 result[QStringLiteral("valid")] = true;
622 result[QStringLiteral("inputType")] = static_cast<int>(binding->input_type);
623 if (binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS) {
624 result[QStringLiteral("inputAxis")] = binding->input.axis.axis;
625 result[QStringLiteral("inputAxisMin")] = binding->input.axis.axis_min;
626 result[QStringLiteral("inputAxisMax")] = binding->input.axis.axis_max;
627 } else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON) {
628 result[QStringLiteral("inputButton")] = binding->input.button;
629 } else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_HAT) {
630 result[QStringLiteral("inputHat")] = binding->input.hat.hat;
631 result[QStringLiteral("inputHatMask")] = binding->input.hat.hat_mask;
632 }
633}
634
635} // namespace SDLJoystick
struct SDL_Gamepad SDL_Gamepad
Definition JoystickSDL.h:14
struct SDL_Joystick SDL_Joystick
Definition JoystickSDL.h:11
#define QGC_LOGGING_CATEGORY(name, categoryStr)
QVariantMap getGUIDInfo(const QString &guid)
Parse GUID info (vendor, product, version, crc16)
void unlockJoysticks()
bool destroyVirtualJoystick(int instanceId)
Destroy a virtual joystick.
QString connectionStateToString(int state)
Connection state to string.
bool gamepadEventsEnabled()
QString getPathForInstanceId(int instanceId)
Get device path for instance ID.
QString userMappingsFilePath()
Get path to user's custom mappings file.
int getVendorForInstanceId(int instanceId)
Get vendor ID for instance ID.
bool reloadMappings()
Reload all gamepad mappings.
void shutdown()
Shutdown SDL joystick/gamepad subsystems.
QString gamepadTypeDisplayName(int type)
bool isVirtualJoystick(int instanceId)
Check if a joystick is virtual.
void updateJoysticks()
Update joystick/gamepad state.
QString getTypeForInstanceId(int instanceId)
Get gamepad type string for instance ID.
QString getConnectionStateForInstanceId(int instanceId)
int addMappingsFromFile(const QString &filePath)
Add gamepad mappings from a file.
bool addMapping(const QString &mapping)
Add a single mapping string.
bool init()
Initialize SDL joystick/gamepad subsystems with QGC-specific hints.
QString getRealTypeForInstanceId(int instanceId)
Get real (underlying) gamepad type string for instance ID.
static QHash< int, QString > s_virtualJoystickNames
int gamepadButtonFromString(const QString &str)
QString getGUIDForInstanceId(int instanceId)
Get GUID string for instance ID.
int getProductVersionForInstanceId(int instanceId)
Get product version for instance ID.
QString getMappingForGUID(const QString &guid)
Get mapping string for a GUID.
QString getNameForInstanceId(int instanceId)
Get device name for instance ID.
int getPlayerIndexForInstanceId(int instanceId)
Get player index for instance ID.
bool isInitialized()
Check if SDL joystick subsystem is initialized.
static void loadGamepadMappings()
void setJoystickEventsEnabled(bool enabled)
Enable/disable joystick event processing.
int createVirtualJoystick(const QString &name, int axisCount, int buttonCount, int hatCount)
Create a virtual joystick.
void setGamepadEventsEnabled(bool enabled)
Enable/disable gamepad event processing.
void populateBindingResult(QVariantMap &result, const SDL_GamepadBinding *binding)
Populate a QVariantMap with binding information from SDL_GamepadBinding.
bool addMappingPersistent(const QString &mapping)
Add a mapping and persist it to user's config file.
void pumpEvents()
Pump SDL events (call periodically)
bool joystickEventsEnabled()
int getProductForInstanceId(int instanceId)
Get product ID for instance ID.
int gamepadTypeFromString(const QString &str)
int getInstanceIdFromPlayerIndex(int playerIndex)
Get instance ID from player index.
QString gamepadButtonToString(int button)
Gamepad button conversions.
QString gamepadTypeToString(int type)
Gamepad type conversions.
int gamepadAxisFromString(const QString &str)
QString gamepadAxisToString(int axis)
Gamepad axis conversions.
void updateGamepads()
void lockJoysticks()
Lock joystick access for thread-safe operations.