Path: blob/master/thirdparty/sdl/joystick/SDL_steam_virtual_gamepad.c
21928 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_LINUX26#include "../core/unix/SDL_appid.h"27#endif28#ifdef SDL_PLATFORM_WIN3229#include "../core/windows/SDL_windows.h"30#else31#include <sys/types.h>32#include <sys/stat.h>33#endif3435#define SDL_HINT_STEAM_VIRTUAL_GAMEPAD_INFO_FILE "SteamVirtualGamepadInfo"3637static char *SDL_steam_virtual_gamepad_info_file SDL_GUARDED_BY(SDL_joystick_lock) = NULL;38static Uint64 SDL_steam_virtual_gamepad_info_file_mtime SDL_GUARDED_BY(SDL_joystick_lock) = 0;39static Uint64 SDL_steam_virtual_gamepad_info_check_time SDL_GUARDED_BY(SDL_joystick_lock) = 0;40static SDL_SteamVirtualGamepadInfo **SDL_steam_virtual_gamepad_info SDL_GUARDED_BY(SDL_joystick_lock) = NULL;41static int SDL_steam_virtual_gamepad_info_count SDL_GUARDED_BY(SDL_joystick_lock) = 0;424344static Uint64 GetFileModificationTime(const char *file)45{46Uint64 modification_time = 0;4748#ifdef SDL_PLATFORM_WIN3249WCHAR *wFile = WIN_UTF8ToStringW(file);50if (wFile) {51HANDLE hFile = CreateFileW(wFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);52if (hFile != INVALID_HANDLE_VALUE) {53FILETIME last_write_time;54if (GetFileTime(hFile, NULL, NULL, &last_write_time)) {55modification_time = last_write_time.dwHighDateTime;56modification_time <<= 32;57modification_time |= last_write_time.dwLowDateTime;58}59CloseHandle(hFile);60}61SDL_free(wFile);62}63#else64struct stat sb;6566if (stat(file, &sb) == 0) {67modification_time = (Uint64)sb.st_mtime;68}69#endif70return modification_time;71}7273static void SDL_FreeSteamVirtualGamepadInfo(void)74{75int i;7677SDL_AssertJoysticksLocked();7879for (i = 0; i < SDL_steam_virtual_gamepad_info_count; ++i) {80SDL_SteamVirtualGamepadInfo *entry = SDL_steam_virtual_gamepad_info[i];81if (entry) {82SDL_free(entry->name);83SDL_free(entry);84}85}86SDL_free(SDL_steam_virtual_gamepad_info);87SDL_steam_virtual_gamepad_info = NULL;88SDL_steam_virtual_gamepad_info_count = 0;89}9091static void AddVirtualGamepadInfo(int slot, SDL_SteamVirtualGamepadInfo *info)92{93SDL_SteamVirtualGamepadInfo *new_info;9495SDL_AssertJoysticksLocked();9697if (slot < 0) {98return;99}100101if (slot >= SDL_steam_virtual_gamepad_info_count) {102SDL_SteamVirtualGamepadInfo **slots = (SDL_SteamVirtualGamepadInfo **)SDL_realloc(SDL_steam_virtual_gamepad_info, (slot + 1)*sizeof(*SDL_steam_virtual_gamepad_info));103if (!slots) {104return;105}106while (SDL_steam_virtual_gamepad_info_count <= slot) {107slots[SDL_steam_virtual_gamepad_info_count++] = NULL;108}109SDL_steam_virtual_gamepad_info = slots;110}111112if (SDL_steam_virtual_gamepad_info[slot]) {113// We already have this slot info114return;115}116117new_info = (SDL_SteamVirtualGamepadInfo *)SDL_malloc(sizeof(*new_info));118if (!new_info) {119return;120}121SDL_copyp(new_info, info);122SDL_steam_virtual_gamepad_info[slot] = new_info;123SDL_zerop(info);124}125126void SDL_InitSteamVirtualGamepadInfo(void)127{128const char *file;129130SDL_AssertJoysticksLocked();131132// The file isn't available inside the macOS sandbox133if (SDL_GetSandbox() == SDL_SANDBOX_MACOS) {134return;135}136137file = SDL_GetHint(SDL_HINT_STEAM_VIRTUAL_GAMEPAD_INFO_FILE);138if (file && *file) {139#ifdef SDL_PLATFORM_LINUX140// Older versions of Wine will blacklist the Steam Virtual Gamepad if141// it appears to have the real controller's VID/PID, so ignore this.142const char *exe = SDL_GetExeName();143if (exe && SDL_strcmp(exe, "wine64-preloader") == 0) {144SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "Wine launched by Steam, ignoring %s", SDL_HINT_STEAM_VIRTUAL_GAMEPAD_INFO_FILE);145return;146}147#endif148SDL_steam_virtual_gamepad_info_file = SDL_strdup(file);149}150SDL_UpdateSteamVirtualGamepadInfo();151}152153bool SDL_SteamVirtualGamepadEnabled(void)154{155SDL_AssertJoysticksLocked();156157return (SDL_steam_virtual_gamepad_info != NULL);158}159160bool SDL_UpdateSteamVirtualGamepadInfo(void)161{162const int UPDATE_CHECK_INTERVAL_MS = 3000;163Uint64 now;164Uint64 mtime;165char *data, *end, *next, *line, *value;166size_t size;167int slot, new_slot;168SDL_SteamVirtualGamepadInfo info;169170SDL_AssertJoysticksLocked();171172if (!SDL_steam_virtual_gamepad_info_file) {173return false;174}175176now = SDL_GetTicks();177if (SDL_steam_virtual_gamepad_info_check_time &&178now < (SDL_steam_virtual_gamepad_info_check_time + UPDATE_CHECK_INTERVAL_MS)) {179return false;180}181SDL_steam_virtual_gamepad_info_check_time = now;182183mtime = GetFileModificationTime(SDL_steam_virtual_gamepad_info_file);184if (mtime == 0 || mtime == SDL_steam_virtual_gamepad_info_file_mtime) {185return false;186}187188data = (char *)SDL_LoadFile(SDL_steam_virtual_gamepad_info_file, &size);189if (!data) {190return false;191}192193SDL_FreeSteamVirtualGamepadInfo();194195slot = -1;196SDL_zero(info);197198for (next = data, end = data + size; next < end; ) {199while (next < end && (*next == '\0' || *next == '\r' || *next == '\n')) {200++next;201}202203line = next;204205while (next < end && (*next != '\r' && *next != '\n')) {206++next;207}208*next = '\0';209210if (SDL_sscanf(line, "[slot %d]", &new_slot) == 1) {211if (slot >= 0) {212AddVirtualGamepadInfo(slot, &info);213}214slot = new_slot;215} else {216value = SDL_strchr(line, '=');217if (value) {218*value++ = '\0';219220if (SDL_strcmp(line, "name") == 0) {221SDL_free(info.name);222info.name = SDL_strdup(value);223} else if (SDL_strcmp(line, "VID") == 0) {224info.vendor_id = (Uint16)SDL_strtoul(value, NULL, 0);225} else if (SDL_strcmp(line, "PID") == 0) {226info.product_id = (Uint16)SDL_strtoul(value, NULL, 0);227} else if (SDL_strcmp(line, "type") == 0) {228info.type = SDL_GetGamepadTypeFromString(value);229} else if (SDL_strcmp(line, "handle") == 0) {230info.handle = (Uint64)SDL_strtoull(value, NULL, 0);231}232}233}234}235if (slot >= 0) {236AddVirtualGamepadInfo(slot, &info);237}238SDL_free(info.name);239SDL_free(data);240241SDL_steam_virtual_gamepad_info_file_mtime = mtime;242243return true;244}245246const SDL_SteamVirtualGamepadInfo *SDL_GetSteamVirtualGamepadInfo(int slot)247{248SDL_AssertJoysticksLocked();249250if (slot < 0 || slot >= SDL_steam_virtual_gamepad_info_count) {251return NULL;252}253return SDL_steam_virtual_gamepad_info[slot];254}255256void SDL_QuitSteamVirtualGamepadInfo(void)257{258SDL_AssertJoysticksLocked();259260if (SDL_steam_virtual_gamepad_info_file) {261SDL_FreeSteamVirtualGamepadInfo();262SDL_free(SDL_steam_virtual_gamepad_info_file);263SDL_steam_virtual_gamepad_info_file = NULL;264}265}266267268