| /* |
| Simple DirectMedia Layer |
| Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org> |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely, subject to the following restrictions: |
| |
| 1. The origin of this software must not be misrepresented; you must not |
| claim that you wrote the original software. If you use this software |
| in a product, an acknowledgment in the product documentation would be |
| appreciated but is not required. |
| 2. Altered source versions must be plainly marked as such, and must not be |
| misrepresented as being the original software. |
| 3. This notice may not be removed or altered from any source distribution. |
| */ |
| #include "SDL_internal.h" |
| |
| #include "SDL_joystick_c.h" |
| #include "SDL_steam_virtual_gamepad.h" |
| |
| #ifdef SDL_PLATFORM_WIN32 |
| #include "../core/windows/SDL_windows.h" |
| #else |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #endif |
| |
| #define SDL_HINT_STEAM_VIRTUAL_GAMEPAD_INFO_FILE "SteamVirtualGamepadInfo" |
| |
| static char *SDL_steam_virtual_gamepad_info_file SDL_GUARDED_BY(SDL_joystick_lock) = NULL; |
| static Uint64 SDL_steam_virtual_gamepad_info_file_mtime SDL_GUARDED_BY(SDL_joystick_lock) = 0; |
| static Uint64 SDL_steam_virtual_gamepad_info_check_time SDL_GUARDED_BY(SDL_joystick_lock) = 0; |
| static SDL_SteamVirtualGamepadInfo **SDL_steam_virtual_gamepad_info SDL_GUARDED_BY(SDL_joystick_lock) = NULL; |
| static int SDL_steam_virtual_gamepad_info_count SDL_GUARDED_BY(SDL_joystick_lock) = 0; |
| |
| |
| static Uint64 GetFileModificationTime(const char *file) |
| { |
| Uint64 modification_time = 0; |
| |
| #ifdef SDL_PLATFORM_WIN32 |
| WCHAR *wFile = WIN_UTF8ToStringW(file); |
| if (wFile) { |
| HANDLE hFile = CreateFileW(wFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); |
| if (hFile != INVALID_HANDLE_VALUE) { |
| FILETIME last_write_time; |
| if (GetFileTime(hFile, NULL, NULL, &last_write_time)) { |
| modification_time = last_write_time.dwHighDateTime; |
| modification_time <<= 32; |
| modification_time |= last_write_time.dwLowDateTime; |
| } |
| CloseHandle(hFile); |
| } |
| SDL_free(wFile); |
| } |
| #else |
| struct stat sb; |
| |
| if (stat(file, &sb) == 0) { |
| modification_time = (Uint64)sb.st_mtime; |
| } |
| #endif |
| return modification_time; |
| } |
| |
| static void SDL_FreeSteamVirtualGamepadInfo(void) |
| { |
| int i; |
| |
| SDL_AssertJoysticksLocked(); |
| |
| for (i = 0; i < SDL_steam_virtual_gamepad_info_count; ++i) { |
| SDL_SteamVirtualGamepadInfo *entry = SDL_steam_virtual_gamepad_info[i]; |
| if (entry) { |
| SDL_free(entry->name); |
| SDL_free(entry); |
| } |
| } |
| SDL_free(SDL_steam_virtual_gamepad_info); |
| SDL_steam_virtual_gamepad_info = NULL; |
| SDL_steam_virtual_gamepad_info_count = 0; |
| } |
| |
| static void AddVirtualGamepadInfo(int slot, SDL_SteamVirtualGamepadInfo *info) |
| { |
| SDL_SteamVirtualGamepadInfo *new_info; |
| |
| SDL_AssertJoysticksLocked(); |
| |
| if (slot < 0) { |
| return; |
| } |
| |
| if (slot >= SDL_steam_virtual_gamepad_info_count) { |
| SDL_SteamVirtualGamepadInfo **slots = (SDL_SteamVirtualGamepadInfo **)SDL_realloc(SDL_steam_virtual_gamepad_info, (slot + 1)*sizeof(*SDL_steam_virtual_gamepad_info)); |
| if (!slots) { |
| return; |
| } |
| while (SDL_steam_virtual_gamepad_info_count <= slot) { |
| slots[SDL_steam_virtual_gamepad_info_count++] = NULL; |
| } |
| SDL_steam_virtual_gamepad_info = slots; |
| } |
| |
| if (SDL_steam_virtual_gamepad_info[slot]) { |
| /* We already have this slot info */ |
| return; |
| } |
| |
| new_info = (SDL_SteamVirtualGamepadInfo *)SDL_malloc(sizeof(*new_info)); |
| if (!new_info) { |
| return; |
| } |
| SDL_copyp(new_info, info); |
| SDL_steam_virtual_gamepad_info[slot] = new_info; |
| SDL_zerop(info); |
| } |
| |
| void SDL_InitSteamVirtualGamepadInfo(void) |
| { |
| const char *file; |
| |
| SDL_AssertJoysticksLocked(); |
| |
| file = SDL_GetHint(SDL_HINT_STEAM_VIRTUAL_GAMEPAD_INFO_FILE); |
| if (file && *file) { |
| SDL_steam_virtual_gamepad_info_file = SDL_strdup(file); |
| } |
| SDL_UpdateSteamVirtualGamepadInfo(); |
| } |
| |
| SDL_bool SDL_SteamVirtualGamepadEnabled(void) |
| { |
| SDL_AssertJoysticksLocked(); |
| |
| return (SDL_steam_virtual_gamepad_info != NULL); |
| } |
| |
| SDL_bool SDL_UpdateSteamVirtualGamepadInfo(void) |
| { |
| const int UPDATE_CHECK_INTERVAL_MS = 3000; |
| Uint64 now; |
| Uint64 mtime; |
| char *data, *end, *next, *line, *value; |
| size_t size; |
| int slot, new_slot; |
| SDL_SteamVirtualGamepadInfo info; |
| |
| SDL_AssertJoysticksLocked(); |
| |
| if (!SDL_steam_virtual_gamepad_info_file) { |
| return SDL_FALSE; |
| } |
| |
| now = SDL_GetTicks(); |
| if (SDL_steam_virtual_gamepad_info_check_time && |
| now < (SDL_steam_virtual_gamepad_info_check_time + UPDATE_CHECK_INTERVAL_MS)) { |
| return SDL_FALSE; |
| } |
| SDL_steam_virtual_gamepad_info_check_time = now; |
| |
| mtime = GetFileModificationTime(SDL_steam_virtual_gamepad_info_file); |
| if (mtime == 0 || mtime == SDL_steam_virtual_gamepad_info_file_mtime) { |
| return SDL_FALSE; |
| } |
| |
| data = (char *)SDL_LoadFile(SDL_steam_virtual_gamepad_info_file, &size); |
| if (!data) { |
| return SDL_FALSE; |
| } |
| |
| SDL_FreeSteamVirtualGamepadInfo(); |
| |
| slot = -1; |
| SDL_zero(info); |
| |
| for (next = data, end = data + size; next < end; ) { |
| while (next < end && (*next == '\0' || *next == '\r' || *next == '\n')) { |
| ++next; |
| } |
| |
| line = next; |
| |
| while (next < end && (*next != '\r' && *next != '\n')) { |
| ++next; |
| } |
| *next = '\0'; |
| |
| if (SDL_sscanf(line, "[slot %d]", &new_slot) == 1) { |
| if (slot >= 0) { |
| AddVirtualGamepadInfo(slot, &info); |
| } |
| slot = new_slot; |
| } else { |
| value = SDL_strchr(line, '='); |
| if (value) { |
| *value++ = '\0'; |
| |
| if (SDL_strcmp(line, "name") == 0) { |
| SDL_free(info.name); |
| info.name = SDL_strdup(value); |
| } else if (SDL_strcmp(line, "VID") == 0) { |
| info.vendor_id = (Uint16)SDL_strtoul(value, NULL, 0); |
| } else if (SDL_strcmp(line, "PID") == 0) { |
| info.product_id = (Uint16)SDL_strtoul(value, NULL, 0); |
| } else if (SDL_strcmp(line, "type") == 0) { |
| info.type = SDL_GetGamepadTypeFromString(value); |
| } else if (SDL_strcmp(line, "handle") == 0) { |
| info.handle = SDL_strtoull(value, NULL, 0); |
| } |
| } |
| } |
| } |
| if (slot >= 0) { |
| AddVirtualGamepadInfo(slot, &info); |
| } |
| SDL_free(info.name); |
| SDL_free(data); |
| |
| SDL_steam_virtual_gamepad_info_file_mtime = mtime; |
| |
| return SDL_TRUE; |
| } |
| |
| const SDL_SteamVirtualGamepadInfo *SDL_GetSteamVirtualGamepadInfo(int slot) |
| { |
| SDL_AssertJoysticksLocked(); |
| |
| if (slot < 0 || slot >= SDL_steam_virtual_gamepad_info_count) { |
| return NULL; |
| } |
| return SDL_steam_virtual_gamepad_info[slot]; |
| } |
| |
| void SDL_QuitSteamVirtualGamepadInfo(void) |
| { |
| SDL_AssertJoysticksLocked(); |
| |
| if (SDL_steam_virtual_gamepad_info_file) { |
| SDL_FreeSteamVirtualGamepadInfo(); |
| SDL_free(SDL_steam_virtual_gamepad_info_file); |
| SDL_steam_virtual_gamepad_info_file = NULL; |
| } |
| } |