– gdk: Add SDL_GDKGetDefaultUser, SDL_GetPrefPath implementation

This commit is contained in:
Ethan Lee 2023-08-14 13:32:02 -04:00
parent 6ff48dddcb
commit 24fcb61470
7 changed files with 186 additions and 21 deletions

View file

@ -563,7 +563,7 @@
<ClCompile Include="..\..\src\events\SDL_touch.c" />
<ClCompile Include="..\..\src\events\SDL_windowevents.c" />
<ClCompile Include="..\..\src\file\SDL_rwops.c" />
<ClCompile Include="..\..\src\filesystem\windows\SDL_sysfilesystem.c" />
<ClCompile Include="..\..\src\filesystem\gdk\SDL_sysfilesystem.cpp" />
<ClCompile Include="..\..\src\haptic\dummy\SDL_syshaptic.c" />
<ClCompile Include="..\..\src\haptic\SDL_haptic.c" />
<ClCompile Include="..\..\src\haptic\windows\SDL_dinputhaptic.c" />

View file

@ -31,7 +31,7 @@
<Filter Include="filesystem">
<UniqueIdentifier>{377061e4-3856-4f05-b916-0d3b360df0f6}</UniqueIdentifier>
</Filter>
<Filter Include="filesystem\windows">
<Filter Include="filesystem\gdk">
<UniqueIdentifier>{226a6643-1c65-4c7f-92aa-861313d974bb}</UniqueIdentifier>
</Filter>
<Filter Include="haptic">
@ -925,8 +925,8 @@
<ClCompile Include="..\..\src\file\SDL_rwops.c">
<Filter>file</Filter>
</ClCompile>
<ClCompile Include="..\..\src\filesystem\windows\SDL_sysfilesystem.c">
<Filter>filesystem\windows</Filter>
<ClCompile Include="..\..\src\filesystem\gdk\SDL_sysfilesystem.cpp">
<Filter>filesystem\gdk</Filter>
</ClCompile>
<ClCompile Include="..\..\src\haptic\SDL_haptic.c">
<Filter>haptic</Filter>

View file

@ -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

View file

@ -594,6 +594,7 @@ extern DECLSPEC void SDLCALL SDL_OnApplicationDidChangeStatusBarOrientation(void
/* Functions used only by GDK */
#if defined(__GDK__)
typedef struct XTaskQueueObject *XTaskQueueHandle;
typedef struct XUser *XUserHandle;
/**
* Gets a reference to the global async task queue handle for GDK,
@ -610,6 +611,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++ */

View file

@ -218,3 +218,24 @@ 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;
}

View file

@ -0,0 +1,140 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#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 <XGameSaveFiles.h>
char *
SDL_GetBasePath(void)
{
/* NOTE: This function is a UTF8 version of the Win32 SDL_GetBasePath()!
* The GDK actually _recommends_ the 'A' functions over the 'W' functions :o
*/
DWORD buflen = 128;
CHAR *path = NULL;
DWORD len = 0;
int i;
while (SDL_TRUE) {
void *ptr = SDL_realloc(path, buflen * sizeof(CHAR));
if (ptr == NULL) {
SDL_free(path);
SDL_OutOfMemory();
return NULL;
}
path = (CHAR *)ptr;
len = GetModuleFileNameA(NULL, path, buflen);
/* if it truncated, then len >= buflen - 1 */
/* if there was enough room (or failure), len < buflen - 1 */
if (len < buflen - 1) {
break;
}
/* buffer too small? Try again. */
buflen *= 2;
}
if (len == 0) {
SDL_free(path);
WIN_SetError("Couldn't locate our .exe");
return NULL;
}
for (i = len - 1; i > 0; i--) {
if (path[i] == '\\') {
break;
}
}
SDL_assert(i > 0); /* Should have been an absolute path. */
path[i + 1] = '\0'; /* chop off filename. */
return path;
}
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: */

View file

@ -170,20 +170,4 @@ char *SDL_GetPrefPath(const char *org, const char *app)
#endif /* SDL_FILESYSTEM_WINDOWS */
#ifdef SDL_FILESYSTEM_XBOX
#include "SDL_filesystem.h"
#include "SDL_error.h"
char *SDL_GetBasePath(void)
{
SDL_Unsupported();
return NULL;
}
char *SDL_GetPrefPath(const char *org, const char *app)
{
SDL_Unsupported();
return NULL;
}
#endif /* SDL_FILESYSTEM_XBOX */
/* vi: set ts=4 sw=4 expandtab: */