Path: blob/master/thirdparty/sdl/joystick/SDL_steam_virtual_gamepad.c
9902 views
/*1Simple DirectMedia Layer2Copyright (C) 1997-2025 Sam Lantinga <[email protected]>34This software is provided 'as-is', without any express or implied5warranty. In no event will the authors be held liable for any damages6arising from the use of this software.78Permission is granted to anyone to use this software for any purpose,9including commercial applications, and to alter it and redistribute it10freely, subject to the following restrictions:11121. The origin of this software must not be misrepresented; you must not13claim that you wrote the original software. If you use this software14in a product, an acknowledgment in the product documentation would be15appreciated but is not required.162. Altered source versions must be plainly marked as such, and must not be17misrepresented as being the original software.183. This notice may not be removed or altered from any source distribution.19*/20#include "SDL_internal.h"2122#include "SDL_joystick_c.h"23#include "SDL_steam_virtual_gamepad.h"2425#ifdef SDL_PLATFORM_WIN3226#include "../core/windows/SDL_windows.h"27#else28#include <sys/types.h>29#include <sys/stat.h>30#endif3132#define SDL_HINT_STEAM_VIRTUAL_GAMEPAD_INFO_FILE "SteamVirtualGamepadInfo"3334static char *SDL_steam_virtual_gamepad_info_file SDL_GUARDED_BY(SDL_joystick_lock) = NULL;35static Uint64 SDL_steam_virtual_gamepad_info_file_mtime SDL_GUARDED_BY(SDL_joystick_lock) = 0;36static Uint64 SDL_steam_virtual_gamepad_info_check_time SDL_GUARDED_BY(SDL_joystick_lock) = 0;37static SDL_SteamVirtualGamepadInfo **SDL_steam_virtual_gamepad_info SDL_GUARDED_BY(SDL_joystick_lock) = NULL;38static int SDL_steam_virtual_gamepad_info_count SDL_GUARDED_BY(SDL_joystick_lock) = 0;394041static Uint64 GetFileModificationTime(const char *file)42{43Uint64 modification_time = 0;4445#ifdef SDL_PLATFORM_WIN3246WCHAR *wFile = WIN_UTF8ToStringW(file);47if (wFile) {48HANDLE hFile = CreateFileW(wFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);49if (hFile != INVALID_HANDLE_VALUE) {50FILETIME last_write_time;51if (GetFileTime(hFile, NULL, NULL, &last_write_time)) {52modification_time = last_write_time.dwHighDateTime;53modification_time <<= 32;54modification_time |= last_write_time.dwLowDateTime;55}56CloseHandle(hFile);57}58SDL_free(wFile);59}60#else61struct stat sb;6263if (stat(file, &sb) == 0) {64modification_time = (Uint64)sb.st_mtime;65}66#endif67return modification_time;68}6970static void SDL_FreeSteamVirtualGamepadInfo(void)71{72int i;7374SDL_AssertJoysticksLocked();7576for (i = 0; i < SDL_steam_virtual_gamepad_info_count; ++i) {77SDL_SteamVirtualGamepadInfo *entry = SDL_steam_virtual_gamepad_info[i];78if (entry) {79SDL_free(entry->name);80SDL_free(entry);81}82}83SDL_free(SDL_steam_virtual_gamepad_info);84SDL_steam_virtual_gamepad_info = NULL;85SDL_steam_virtual_gamepad_info_count = 0;86}8788static void AddVirtualGamepadInfo(int slot, SDL_SteamVirtualGamepadInfo *info)89{90SDL_SteamVirtualGamepadInfo *new_info;9192SDL_AssertJoysticksLocked();9394if (slot < 0) {95return;96}9798if (slot >= SDL_steam_virtual_gamepad_info_count) {99SDL_SteamVirtualGamepadInfo **slots = (SDL_SteamVirtualGamepadInfo **)SDL_realloc(SDL_steam_virtual_gamepad_info, (slot + 1)*sizeof(*SDL_steam_virtual_gamepad_info));100if (!slots) {101return;102}103while (SDL_steam_virtual_gamepad_info_count <= slot) {104slots[SDL_steam_virtual_gamepad_info_count++] = NULL;105}106SDL_steam_virtual_gamepad_info = slots;107}108109if (SDL_steam_virtual_gamepad_info[slot]) {110// We already have this slot info111return;112}113114new_info = (SDL_SteamVirtualGamepadInfo *)SDL_malloc(sizeof(*new_info));115if (!new_info) {116return;117}118SDL_copyp(new_info, info);119SDL_steam_virtual_gamepad_info[slot] = new_info;120SDL_zerop(info);121}122123void SDL_InitSteamVirtualGamepadInfo(void)124{125const char *file;126127SDL_AssertJoysticksLocked();128129// The file isn't available inside the macOS sandbox130if (SDL_GetSandbox() == SDL_SANDBOX_MACOS) {131return;132}133134file = SDL_GetHint(SDL_HINT_STEAM_VIRTUAL_GAMEPAD_INFO_FILE);135if (file && *file) {136SDL_steam_virtual_gamepad_info_file = SDL_strdup(file);137}138SDL_UpdateSteamVirtualGamepadInfo();139}140141bool SDL_SteamVirtualGamepadEnabled(void)142{143SDL_AssertJoysticksLocked();144145return (SDL_steam_virtual_gamepad_info != NULL);146}147148bool SDL_UpdateSteamVirtualGamepadInfo(void)149{150const int UPDATE_CHECK_INTERVAL_MS = 3000;151Uint64 now;152Uint64 mtime;153char *data, *end, *next, *line, *value;154size_t size;155int slot, new_slot;156SDL_SteamVirtualGamepadInfo info;157158SDL_AssertJoysticksLocked();159160if (!SDL_steam_virtual_gamepad_info_file) {161return false;162}163164now = SDL_GetTicks();165if (SDL_steam_virtual_gamepad_info_check_time &&166now < (SDL_steam_virtual_gamepad_info_check_time + UPDATE_CHECK_INTERVAL_MS)) {167return false;168}169SDL_steam_virtual_gamepad_info_check_time = now;170171mtime = GetFileModificationTime(SDL_steam_virtual_gamepad_info_file);172if (mtime == 0 || mtime == SDL_steam_virtual_gamepad_info_file_mtime) {173return false;174}175176data = (char *)SDL_LoadFile(SDL_steam_virtual_gamepad_info_file, &size);177if (!data) {178return false;179}180181SDL_FreeSteamVirtualGamepadInfo();182183slot = -1;184SDL_zero(info);185186for (next = data, end = data + size; next < end; ) {187while (next < end && (*next == '\0' || *next == '\r' || *next == '\n')) {188++next;189}190191line = next;192193while (next < end && (*next != '\r' && *next != '\n')) {194++next;195}196*next = '\0';197198if (SDL_sscanf(line, "[slot %d]", &new_slot) == 1) {199if (slot >= 0) {200AddVirtualGamepadInfo(slot, &info);201}202slot = new_slot;203} else {204value = SDL_strchr(line, '=');205if (value) {206*value++ = '\0';207208if (SDL_strcmp(line, "name") == 0) {209SDL_free(info.name);210info.name = SDL_strdup(value);211} else if (SDL_strcmp(line, "VID") == 0) {212info.vendor_id = (Uint16)SDL_strtoul(value, NULL, 0);213} else if (SDL_strcmp(line, "PID") == 0) {214info.product_id = (Uint16)SDL_strtoul(value, NULL, 0);215} else if (SDL_strcmp(line, "type") == 0) {216info.type = SDL_GetGamepadTypeFromString(value);217} else if (SDL_strcmp(line, "handle") == 0) {218info.handle = (Uint64)SDL_strtoull(value, NULL, 0);219}220}221}222}223if (slot >= 0) {224AddVirtualGamepadInfo(slot, &info);225}226SDL_free(info.name);227SDL_free(data);228229SDL_steam_virtual_gamepad_info_file_mtime = mtime;230231return true;232}233234const SDL_SteamVirtualGamepadInfo *SDL_GetSteamVirtualGamepadInfo(int slot)235{236SDL_AssertJoysticksLocked();237238if (slot < 0 || slot >= SDL_steam_virtual_gamepad_info_count) {239return NULL;240}241return SDL_steam_virtual_gamepad_info[slot];242}243244void SDL_QuitSteamVirtualGamepadInfo(void)245{246SDL_AssertJoysticksLocked();247248if (SDL_steam_virtual_gamepad_info_file) {249SDL_FreeSteamVirtualGamepadInfo();250SDL_free(SDL_steam_virtual_gamepad_info_file);251SDL_steam_virtual_gamepad_info_file = NULL;252}253}254255256