filesystem: Implement SDL_GetExeName() for all platforms.

(cherry picked from commit d7ba3efe6b)
This commit is contained in:
Ryan C. Gordon
2026-05-27 15:41:51 -04:00
parent 11e13dc4a5
commit b8601dc8a7
10 changed files with 113 additions and 13 deletions

View File

@@ -266,7 +266,9 @@ extern "C" {
anything calling it without an extremely good reason. */
extern SDL_NORETURN void SDL_ExitProcess(int exitcode);
// Get just the process's binary name, no path. Calculates and caches the string on first call. String lives until SDL_Quit(). This is not a public API right now!
// Get just the process's binary name, no path. NULL if it doesn't make sense for a platform.
// Can be something not a file, like a package ID on Android. Meant to be human-readable, not appended to a path, etc.
// Calculates and caches the string on first call. String lives until SDL_Quit(). This is not a public API right now!
extern const char *SDL_GetExeName(void);
#ifdef HAVE_LIBC

View File

@@ -2848,6 +2848,43 @@ int SDL_GetAndroidSDKVersion(void)
return sdk_version;
}
char *SDL_GetAndroidPackageName(void)
{
// this doesn't currently cache this, because it's only used by SDL_GetExeName, which _does_ cache it.
struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(SDL_FUNCTION);
JNIEnv *env = Android_JNI_GetEnv();
if (!LocalReferenceHolder_Init(&refs, env)) {
LocalReferenceHolder_Cleanup(&refs);
return NULL;
}
// context = SDLActivity.getContext();
jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
if (!context) {
SDL_SetError("Couldn't get Android context!");
LocalReferenceHolder_Cleanup(&refs);
return NULL;
}
// fileObj = context.getFilesDir();
jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), "getPackageName", "()Ljava/lang/String;");
jstring jstr = (jstring)(*env)->CallObjectMethod(env, context, mid);
if (Android_JNI_ExceptionOccurred(false)) {
LocalReferenceHolder_Cleanup(&refs);
return NULL;
}
const char *cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
char *retval = cstr ? SDL_strdup(cstr) : NULL;
(*env)->ReleaseStringUTFChars(env, jstr, cstr);
LocalReferenceHolder_Cleanup(&refs);
return retval;
}
bool SDL_IsAndroidTablet(void)
{
JNIEnv *env = Android_JNI_GetEnv();

View File

@@ -153,6 +153,8 @@ int SDL_GetAndroidSDKVersion(void);
bool SDL_IsAndroidTablet(void);
bool SDL_IsAndroidTV(void);
char *SDL_GetAndroidPackageName(void); // this is a SDL_malloc'd string the caller will own.
// File Dialogs
bool Android_JNI_OpenFileDialog(SDL_DialogFileCallback callback, void *userdata,
const SDL_DialogFileFilter *filters, int nfilters, bool forwrite,

View File

@@ -26,8 +26,7 @@
// System dependent filesystem routines
#include "../SDL_sysfilesystem.h"
#include <unistd.h>
#include "../../core/android/SDL_android.h"
char *SDL_SYS_GetBasePath(void)
{
@@ -36,7 +35,7 @@ char *SDL_SYS_GetBasePath(void)
char *SDL_SYS_GetExeName(void)
{
return NULL; // !!! FIXME: probably just use the Linux path?
return SDL_GetAndroidPackageName();
}
char *SDL_SYS_GetPrefPath(const char *org, const char *app)

View File

@@ -47,7 +47,7 @@ char *SDL_SYS_GetBasePath(void)
} else if (SDL_strcasecmp(baseType, "parent") == 0) {
base = [[[bundle bundlePath] stringByDeletingLastPathComponent] fileSystemRepresentation];
} else {
// this returns the exedir for non-bundled and the resourceDir for bundled apps
// this returns the exedir for non-bundled and the resourceDir for bundled apps
base = [[bundle resourcePath] fileSystemRepresentation];
}
@@ -65,8 +65,24 @@ char *SDL_SYS_GetBasePath(void)
char *SDL_SYS_GetExeName(void)
{
SDL_Unsupported(); // !!! FIXME
return NULL;
@autoreleasepool {
NSBundle *bundle = [NSBundle mainBundle];
const char *name = [[[bundle infoDictionary] objectForKey:@"CFBundleIdentifier"] UTF8String];
if (!name) {
name = [[[bundle infoDictionary] objectForKey:@"CFBundleDisplayName"] UTF8String];
if (!name) {
name = [[[bundle infoDictionary] objectForKey:@"CFBundleName"] UTF8String];
if (!name) {
name = [[[bundle infoDictionary] objectForKey:@"CFBundleExecutable"] UTF8String];
if (!name) {
name = [[[NSProcessInfo processInfo] processName] UTF8String]; // oh well.
}
}
}
}
return name ? SDL_strdup(name) : NULL;
}
}
char *SDL_SYS_GetPrefPath(const char *org, const char *app)

View File

@@ -44,6 +44,7 @@ char *SDL_SYS_GetBasePath(void)
return NULL;
}
// !!! FIXME: if find_path promises an absolute path, can we dump this and just do SDL_strrchr(name, '/')?
BEntry entry(name, true);
BPath path;
status_t rc = entry.GetPath(&path); // (path) now has binary's path.
@@ -66,8 +67,12 @@ char *SDL_SYS_GetBasePath(void)
char *SDL_SYS_GetExeName(void)
{
SDL_Unsupported(); // !!! FIXME: Move most of SDL_SYS_GetBasePath to a separate function and reuse it here.
return NULL;
char name[MAXPATHLEN];
if (find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, name, sizeof(name)) != B_OK) {
return NULL;
}
char *ptr = SDL_strrchr(name, '/');
return SDL_strdup(ptr ? ptr + 1 : name);
}
char *SDL_SYS_GetPrefPath(const char *org, const char *app)

View File

@@ -21,6 +21,7 @@
#include "SDL_internal.h"
extern void NGAGE_GetAppPath(char *path);
extern void NGAGE_GetExeName(char *path);
char *SDL_SYS_GetBasePath(void)
{
@@ -32,8 +33,9 @@ char *SDL_SYS_GetBasePath(void)
char *SDL_SYS_GetExeName(void)
{
SDL_Unsupported(); // !!! FIXME: see code in NGAGE_GetAppPath
return NULL;
char exe_name[512];
NGAGE_GetExeName(exe_name);
return SDL_strdup(exe_name);
}
char *SDL_SYS_GetPrefPath(const char *org, const char *app)

View File

@@ -63,6 +63,23 @@ void NGAGE_GetAppPath(char *path)
}
}
void NGAGE_GetExeName(char *path)
{
TBuf<512> aPath;
TFileName fullExePath = RProcess().FileName();
TParsePtrC parser(fullExePath);
aPath.Copy(parser.NameAndExt());
TBuf8<512> utf8Path; // Temporary buffer for UTF-8 data.
CnvUtfConverter::ConvertFromUnicodeToUtf8(utf8Path, aPath);
// Copy UTF-8 data to the provided char* buffer.
strncpy(path, (const char *)utf8Path.Ptr(), utf8Path.Length());
path[utf8Path.Length()] = '\0';
}
#ifdef __cplusplus
}
#endif

View File

@@ -154,8 +154,25 @@ char *SDL_SYS_GetBasePath(void)
char *SDL_SYS_GetExeName(void)
{
SDL_Unsupported(); // !!! FIXME: see code in SDL_SYS_GetBasePath.
return NULL;
_kernel_swi_regs regs;
_kernel_oserror *error;
char *canon, *ptr, *retval;
error = _kernel_swi(OS_GetEnv, &regs, &regs);
if (error) {
return NULL;
}
canon = canonicalisePath((const char *)regs.r[0], "Run$Path");
if (!canon) {
return NULL;
}
// find filename.
ptr = SDL_strrchr(canon, '.');
retval = SDL_strdup(ptr ? ptr + 1 : canon);
SDL_free(canon);
return retval;
}
char *SDL_SYS_GetPrefPath(const char *org, const char *app)

View File

@@ -94,6 +94,9 @@ int main(int argc, char *argv[])
char *curdir;
const char *base_path;
/* this will be SDL's best guess at the human-readable exe name (or bundle id, or whatever) by default. */
SDL_Log("Default app name: '%s'", SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING));
/* Initialize test framework */
state = SDLTest_CommonCreateState(argv, 0);
if (!state) {