From c0cd8c8142b70c70da4dddc32244da6593cb6bb0 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Fri, 25 Aug 2023 10:39:39 -0400 Subject: [PATCH] gdk: Add SDL_GDKGetDefaultUser, SDL_GetPrefPath implementation --- VisualC-GDK/SDL/SDL.vcxproj | 2 +- VisualC-GDK/SDL/SDL.vcxproj.filters | 6 +- docs/README-gdk.md | 6 ++ include/SDL3/SDL_system.h | 16 +++- src/core/gdk/SDL_gdk.cpp | 20 +++++ src/filesystem/gdk/SDL_sysfilesystem.cpp | 96 ++++++++++++++++++++++ src/filesystem/windows/SDL_sysfilesystem.c | 20 ----- 7 files changed, 141 insertions(+), 25 deletions(-) create mode 100644 src/filesystem/gdk/SDL_sysfilesystem.cpp diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index fd9d84557..f4419c9ee 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -582,7 +582,7 @@ - + diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters index 81dee5cc3..f55ad0342 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj.filters +++ b/VisualC-GDK/SDL/SDL.vcxproj.filters @@ -31,7 +31,7 @@ {377061e4-3856-4f05-b916-0d3b360df0f6} - + {226a6643-1c65-4c7f-92aa-861313d974bb} @@ -916,8 +916,8 @@ file - - filesystem\windows + + filesystem\gdk haptic diff --git a/docs/README-gdk.md b/docs/README-gdk.md index a785e6530..fefe16357 100644 --- a/docs/README-gdk.md +++ b/docs/README-gdk.md @@ -29,6 +29,12 @@ The Windows GDK port supports the full set of Win32 APIs, renderers, controllers * Global task queue callbacks are dispatched during `SDL_PumpEvents` (which is also called internally if using `SDL_PollEvent`). * You can get the handle of the global task queue through `SDL_GDKGetTaskQueue`, if needed. When done with the queue, be sure to use `XTaskQueueCloseHandle` to decrement the reference count (otherwise it will cause a resource leak). +* Single-player games have some additional features available: + * Call `SDL_GDKGetDefaultUser` to get the default XUserHandle pointer. + * `SDL_GetPrefPath` still works, but only for single-player titles. + +These functions mostly wrap around async APIs, and thus should be treated as synchronous alternatives. Also note that the single-player functions return on any OS errors, so be sure to validate the return values! + * What doesn't work: * Compilation with anything other than through the included Visual C++ solution file diff --git a/include/SDL3/SDL_system.h b/include/SDL3/SDL_system.h index b934066f8..98d67c961 100644 --- a/include/SDL3/SDL_system.h +++ b/include/SDL3/SDL_system.h @@ -623,7 +623,8 @@ extern DECLSPEC void SDLCALL SDL_OnApplicationDidChangeStatusBarOrientation(void /* Functions used only by GDK */ #ifdef __GDK__ -typedef struct XTaskQueueObject * XTaskQueueHandle; +typedef struct XTaskQueueObject *XTaskQueueHandle; +typedef struct XUser *XUserHandle; /** * Gets a reference to the global async task queue handle for GDK, @@ -641,6 +642,19 @@ typedef struct XTaskQueueObject * XTaskQueueHandle; */ extern DECLSPEC int SDLCALL SDL_GDKGetTaskQueue(XTaskQueueHandle * outTaskQueue); +/** + * Gets a reference to the default user handle for GDK. + * + * This is effectively a synchronous version of XUserAddAsync, which always + * prefers the default user and allows a sign-in UI. + * + * \param outUserHandle a pointer to be filled in with the default user handle. + * \returns 0 if success, -1 if any error occurs. + * + * \since This function is available since SDL 2.28.0. + */ +extern DECLSPEC int SDLCALL SDL_GDKGetDefaultUser(XUserHandle * outUserHandle); + #endif /* Ends C function definitions when using C++ */ diff --git a/src/core/gdk/SDL_gdk.cpp b/src/core/gdk/SDL_gdk.cpp index d883a3c7b..c2dd14e24 100644 --- a/src/core/gdk/SDL_gdk.cpp +++ b/src/core/gdk/SDL_gdk.cpp @@ -214,3 +214,23 @@ SDL_GDKSuspendComplete() SetEvent(plmSuspendComplete); } } + +extern "C" DECLSPEC int +SDL_GDKGetDefaultUser(XUserHandle *outUserHandle) +{ + XAsyncBlock block = { 0 }; + HRESULT result; + + if (FAILED(result = XUserAddAsync(XUserAddOptions::AddDefaultUserAllowingUI, &block))) { + return WIN_SetErrorFromHRESULT("XUserAddAsync", result); + } + + do { + result = XUserAddResult(&block, outUserHandle); + } while (result == E_PENDING); + if (FAILED(result)) { + return WIN_SetErrorFromHRESULT("XUserAddResult", result); + } + + return 0; +} diff --git a/src/filesystem/gdk/SDL_sysfilesystem.cpp b/src/filesystem/gdk/SDL_sysfilesystem.cpp new file mode 100644 index 000000000..30a2b3489 --- /dev/null +++ b/src/filesystem/gdk/SDL_sysfilesystem.cpp @@ -0,0 +1,96 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + 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" + +#ifdef SDL_FILESYSTEM_XBOX + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* System dependent filesystem routines */ + +#include "../../core/windows/SDL_windows.h" +#include "SDL_hints.h" +#include "SDL_system.h" +#include "SDL_filesystem.h" +#include + +char * +SDL_GetBasePath(void) +{ + return SDL_strdup("G:\\"); +} + +char * +SDL_GetPrefPath(const char *org, const char *app) +{ + XUserHandle user = NULL; + XAsyncBlock block = { 0 }; + char *folderPath; + HRESULT result; + const char *csid = SDL_GetHint("SDL_GDK_SERVICE_CONFIGURATION_ID"); + + if (app == NULL) { + SDL_InvalidParamError("app"); + return NULL; + } + + /* This should be set before calling SDL_GetPrefPath! */ + if (csid == NULL) { + SDL_LogWarn(SDL_LOG_CATEGORY_SYSTEM, "Set SDL_GDK_SERVICE_CONFIGURATION_ID before calling SDL_GetPrefPath!"); + return SDL_strdup("T:\\"); + } + + if (SDL_GDKGetDefaultUser(&user) < 0) { + /* Error already set, just return */ + return NULL; + } + + if (FAILED(result = XGameSaveFilesGetFolderWithUiAsync(user, csid, &block))) { + WIN_SetErrorFromHRESULT("XGameSaveFilesGetFolderWithUiAsync", result); + return NULL; + } + + folderPath = (char*) SDL_malloc(MAX_PATH); + do { + result = XGameSaveFilesGetFolderWithUiResult(&block, MAX_PATH, folderPath); + } while (result == E_PENDING); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT("XGameSaveFilesGetFolderWithUiResult", result); + SDL_free(folderPath); + return NULL; + } + + /* We aren't using 'app' here because the container rules are a lot more + * strict than the NTFS rules, so it will most likely be invalid :( + */ + SDL_strlcat(folderPath, "\\SDLPrefPath\\", MAX_PATH); + if (CreateDirectoryA(folderPath, NULL) == FALSE) { + if (GetLastError() != ERROR_ALREADY_EXISTS) { + WIN_SetError("CreateDirectoryA"); + SDL_free(folderPath); + return NULL; + } + } + return folderPath; +} + +#endif /* SDL_FILESYSTEM_XBOX */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/filesystem/windows/SDL_sysfilesystem.c b/src/filesystem/windows/SDL_sysfilesystem.c index 2dffe4c2a..3027fb00e 100644 --- a/src/filesystem/windows/SDL_sysfilesystem.c +++ b/src/filesystem/windows/SDL_sysfilesystem.c @@ -332,23 +332,3 @@ done: return retval; } #endif /* SDL_FILESYSTEM_WINDOWS */ - -#ifdef SDL_FILESYSTEM_XBOX -char *SDL_GetBasePath(void) -{ - SDL_Unsupported(); - return NULL; -} - -char *SDL_GetPrefPath(const char *org, const char *app) -{ - SDL_Unsupported(); - return NULL; -} - -char *SDL_GetUserFolder(SDL_Folder folder) -{ - SDL_Unsupported(); - return NULL; -} -#endif /* SDL_FILESYSTEM_XBOX */