Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/joystick/SDL_steam_virtual_gamepad.c
21928 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
4
5
This software is provided 'as-is', without any express or implied
6
warranty. In no event will the authors be held liable for any damages
7
arising from the use of this software.
8
9
Permission is granted to anyone to use this software for any purpose,
10
including commercial applications, and to alter it and redistribute it
11
freely, subject to the following restrictions:
12
13
1. The origin of this software must not be misrepresented; you must not
14
claim that you wrote the original software. If you use this software
15
in a product, an acknowledgment in the product documentation would be
16
appreciated but is not required.
17
2. Altered source versions must be plainly marked as such, and must not be
18
misrepresented as being the original software.
19
3. This notice may not be removed or altered from any source distribution.
20
*/
21
#include "SDL_internal.h"
22
23
#include "SDL_joystick_c.h"
24
#include "SDL_steam_virtual_gamepad.h"
25
26
#ifdef SDL_PLATFORM_LINUX
27
#include "../core/unix/SDL_appid.h"
28
#endif
29
#ifdef SDL_PLATFORM_WIN32
30
#include "../core/windows/SDL_windows.h"
31
#else
32
#include <sys/types.h>
33
#include <sys/stat.h>
34
#endif
35
36
#define SDL_HINT_STEAM_VIRTUAL_GAMEPAD_INFO_FILE "SteamVirtualGamepadInfo"
37
38
static char *SDL_steam_virtual_gamepad_info_file SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
39
static Uint64 SDL_steam_virtual_gamepad_info_file_mtime SDL_GUARDED_BY(SDL_joystick_lock) = 0;
40
static Uint64 SDL_steam_virtual_gamepad_info_check_time SDL_GUARDED_BY(SDL_joystick_lock) = 0;
41
static SDL_SteamVirtualGamepadInfo **SDL_steam_virtual_gamepad_info SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
42
static int SDL_steam_virtual_gamepad_info_count SDL_GUARDED_BY(SDL_joystick_lock) = 0;
43
44
45
static Uint64 GetFileModificationTime(const char *file)
46
{
47
Uint64 modification_time = 0;
48
49
#ifdef SDL_PLATFORM_WIN32
50
WCHAR *wFile = WIN_UTF8ToStringW(file);
51
if (wFile) {
52
HANDLE hFile = CreateFileW(wFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
53
if (hFile != INVALID_HANDLE_VALUE) {
54
FILETIME last_write_time;
55
if (GetFileTime(hFile, NULL, NULL, &last_write_time)) {
56
modification_time = last_write_time.dwHighDateTime;
57
modification_time <<= 32;
58
modification_time |= last_write_time.dwLowDateTime;
59
}
60
CloseHandle(hFile);
61
}
62
SDL_free(wFile);
63
}
64
#else
65
struct stat sb;
66
67
if (stat(file, &sb) == 0) {
68
modification_time = (Uint64)sb.st_mtime;
69
}
70
#endif
71
return modification_time;
72
}
73
74
static void SDL_FreeSteamVirtualGamepadInfo(void)
75
{
76
int i;
77
78
SDL_AssertJoysticksLocked();
79
80
for (i = 0; i < SDL_steam_virtual_gamepad_info_count; ++i) {
81
SDL_SteamVirtualGamepadInfo *entry = SDL_steam_virtual_gamepad_info[i];
82
if (entry) {
83
SDL_free(entry->name);
84
SDL_free(entry);
85
}
86
}
87
SDL_free(SDL_steam_virtual_gamepad_info);
88
SDL_steam_virtual_gamepad_info = NULL;
89
SDL_steam_virtual_gamepad_info_count = 0;
90
}
91
92
static void AddVirtualGamepadInfo(int slot, SDL_SteamVirtualGamepadInfo *info)
93
{
94
SDL_SteamVirtualGamepadInfo *new_info;
95
96
SDL_AssertJoysticksLocked();
97
98
if (slot < 0) {
99
return;
100
}
101
102
if (slot >= SDL_steam_virtual_gamepad_info_count) {
103
SDL_SteamVirtualGamepadInfo **slots = (SDL_SteamVirtualGamepadInfo **)SDL_realloc(SDL_steam_virtual_gamepad_info, (slot + 1)*sizeof(*SDL_steam_virtual_gamepad_info));
104
if (!slots) {
105
return;
106
}
107
while (SDL_steam_virtual_gamepad_info_count <= slot) {
108
slots[SDL_steam_virtual_gamepad_info_count++] = NULL;
109
}
110
SDL_steam_virtual_gamepad_info = slots;
111
}
112
113
if (SDL_steam_virtual_gamepad_info[slot]) {
114
// We already have this slot info
115
return;
116
}
117
118
new_info = (SDL_SteamVirtualGamepadInfo *)SDL_malloc(sizeof(*new_info));
119
if (!new_info) {
120
return;
121
}
122
SDL_copyp(new_info, info);
123
SDL_steam_virtual_gamepad_info[slot] = new_info;
124
SDL_zerop(info);
125
}
126
127
void SDL_InitSteamVirtualGamepadInfo(void)
128
{
129
const char *file;
130
131
SDL_AssertJoysticksLocked();
132
133
// The file isn't available inside the macOS sandbox
134
if (SDL_GetSandbox() == SDL_SANDBOX_MACOS) {
135
return;
136
}
137
138
file = SDL_GetHint(SDL_HINT_STEAM_VIRTUAL_GAMEPAD_INFO_FILE);
139
if (file && *file) {
140
#ifdef SDL_PLATFORM_LINUX
141
// Older versions of Wine will blacklist the Steam Virtual Gamepad if
142
// it appears to have the real controller's VID/PID, so ignore this.
143
const char *exe = SDL_GetExeName();
144
if (exe && SDL_strcmp(exe, "wine64-preloader") == 0) {
145
SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "Wine launched by Steam, ignoring %s", SDL_HINT_STEAM_VIRTUAL_GAMEPAD_INFO_FILE);
146
return;
147
}
148
#endif
149
SDL_steam_virtual_gamepad_info_file = SDL_strdup(file);
150
}
151
SDL_UpdateSteamVirtualGamepadInfo();
152
}
153
154
bool SDL_SteamVirtualGamepadEnabled(void)
155
{
156
SDL_AssertJoysticksLocked();
157
158
return (SDL_steam_virtual_gamepad_info != NULL);
159
}
160
161
bool SDL_UpdateSteamVirtualGamepadInfo(void)
162
{
163
const int UPDATE_CHECK_INTERVAL_MS = 3000;
164
Uint64 now;
165
Uint64 mtime;
166
char *data, *end, *next, *line, *value;
167
size_t size;
168
int slot, new_slot;
169
SDL_SteamVirtualGamepadInfo info;
170
171
SDL_AssertJoysticksLocked();
172
173
if (!SDL_steam_virtual_gamepad_info_file) {
174
return false;
175
}
176
177
now = SDL_GetTicks();
178
if (SDL_steam_virtual_gamepad_info_check_time &&
179
now < (SDL_steam_virtual_gamepad_info_check_time + UPDATE_CHECK_INTERVAL_MS)) {
180
return false;
181
}
182
SDL_steam_virtual_gamepad_info_check_time = now;
183
184
mtime = GetFileModificationTime(SDL_steam_virtual_gamepad_info_file);
185
if (mtime == 0 || mtime == SDL_steam_virtual_gamepad_info_file_mtime) {
186
return false;
187
}
188
189
data = (char *)SDL_LoadFile(SDL_steam_virtual_gamepad_info_file, &size);
190
if (!data) {
191
return false;
192
}
193
194
SDL_FreeSteamVirtualGamepadInfo();
195
196
slot = -1;
197
SDL_zero(info);
198
199
for (next = data, end = data + size; next < end; ) {
200
while (next < end && (*next == '\0' || *next == '\r' || *next == '\n')) {
201
++next;
202
}
203
204
line = next;
205
206
while (next < end && (*next != '\r' && *next != '\n')) {
207
++next;
208
}
209
*next = '\0';
210
211
if (SDL_sscanf(line, "[slot %d]", &new_slot) == 1) {
212
if (slot >= 0) {
213
AddVirtualGamepadInfo(slot, &info);
214
}
215
slot = new_slot;
216
} else {
217
value = SDL_strchr(line, '=');
218
if (value) {
219
*value++ = '\0';
220
221
if (SDL_strcmp(line, "name") == 0) {
222
SDL_free(info.name);
223
info.name = SDL_strdup(value);
224
} else if (SDL_strcmp(line, "VID") == 0) {
225
info.vendor_id = (Uint16)SDL_strtoul(value, NULL, 0);
226
} else if (SDL_strcmp(line, "PID") == 0) {
227
info.product_id = (Uint16)SDL_strtoul(value, NULL, 0);
228
} else if (SDL_strcmp(line, "type") == 0) {
229
info.type = SDL_GetGamepadTypeFromString(value);
230
} else if (SDL_strcmp(line, "handle") == 0) {
231
info.handle = (Uint64)SDL_strtoull(value, NULL, 0);
232
}
233
}
234
}
235
}
236
if (slot >= 0) {
237
AddVirtualGamepadInfo(slot, &info);
238
}
239
SDL_free(info.name);
240
SDL_free(data);
241
242
SDL_steam_virtual_gamepad_info_file_mtime = mtime;
243
244
return true;
245
}
246
247
const SDL_SteamVirtualGamepadInfo *SDL_GetSteamVirtualGamepadInfo(int slot)
248
{
249
SDL_AssertJoysticksLocked();
250
251
if (slot < 0 || slot >= SDL_steam_virtual_gamepad_info_count) {
252
return NULL;
253
}
254
return SDL_steam_virtual_gamepad_info[slot];
255
}
256
257
void SDL_QuitSteamVirtualGamepadInfo(void)
258
{
259
SDL_AssertJoysticksLocked();
260
261
if (SDL_steam_virtual_gamepad_info_file) {
262
SDL_FreeSteamVirtualGamepadInfo();
263
SDL_free(SDL_steam_virtual_gamepad_info_file);
264
SDL_steam_virtual_gamepad_info_file = NULL;
265
}
266
}
267
268