Просмотр исходного кода

Add basic Ubuntu Touch functions #12543

This adds support for:
* System theme
* Sandbox detection
* Device form factor detection

Many things aren't properly supported yet, but changes and upgrades will happen on the Ubuntu Touch side, so SDL should automatically support more Ubuntu Touch features as time goes.
Semphriss 22 часов назад
Родитель
Сommit
e52dbf4442

+ 1 - 0
CMakeLists.txt

@@ -2142,6 +2142,7 @@ elseif(UNIX AND NOT (APPLE OR RISCOS OR HAIKU OR CYGWIN))
       "${SDL3_SOURCE_DIR}/src/core/linux/SDL_evdev_capabilities.c"
       "${SDL3_SOURCE_DIR}/src/core/linux/SDL_evdev_capabilities.h"
       "${SDL3_SOURCE_DIR}/src/core/linux/SDL_threadprio.c"
+      "${SDL3_SOURCE_DIR}/src/core/linux/SDL_ubuntu_touch.c"
     )
 
     # src/core/unix/*.c is included in a generic if(UNIX) section, elsewhere.

+ 32 - 0
docs/README-ubuntu-touch.md

@@ -0,0 +1,32 @@
+# Ubuntu Touch / Lomiri
+
+Ubuntu Touch being similar to Ubuntu desktop, most features should be supported
+out-of-the-box with SDL.
+
+## Developing apps
+
+Ubuntu Touch apps are developed using [Clickable](https://clickable-ut.dev/).
+
+Clickable provides an SDL template. It is highly recommended to use the template
+as a starting point for both new and existing apps.
+
+## Considerations
+
+Ubuntu Touch is similar to the desktop version of Ubuntu, but presents some
+differences in behavior. Developers should be wary of the following:
+
+### SDL_GetPrefPath
+
+The only allowed writable folder is `~/.local/share/<appname>/`.
+`SDL_GetPrefPath` ignores its arguments and will always return that path on
+Ubuntu Touch. No changes are needed in apps.
+
+### Video driver
+
+Currently, [a bug](https://github.com/libsdl-org/SDL/issues/12247) forces SDL to
+use the Wayland driver on Ubuntu Touch. No changes are needed in apps.
+
+### Extra functions
+
+SDL provides `SDL_IsUbuntuTouch()` to differentiate between Ubuntu Touch and
+regular Unix, which can help if certain platform-specific tweaks are needed.

+ 55 - 1
include/SDL3/SDL_system.h

@@ -725,7 +725,8 @@ typedef enum SDL_Sandbox
     SDL_SANDBOX_UNKNOWN_CONTAINER,
     SDL_SANDBOX_FLATPAK,
     SDL_SANDBOX_SNAP,
-    SDL_SANDBOX_MACOS
+    SDL_SANDBOX_MACOS,
+    SDL_SANDBOX_LOMIRI
 } SDL_Sandbox;
 
 /**
@@ -903,6 +904,59 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetGDKDefaultUser(XUserHandle *outUserHandl
 
 #endif
 
+/*
+ * Functions used only with Ubuntu Touch
+ */
+#ifdef SDL_PLATFORM_LINUX
+
+/**
+ * Detect whether the current platform is Ubuntu Touch.
+ *
+ * \returns true if the platform is Ubuntu Touch; false otherwise.
+ *
+ * \since This function is available since SDL 3.6.0.
+ */
+extern SDL_DECLSPEC bool SDLCALL SDL_IsUbuntuTouch(void);
+
+/**
+ * The ID of the application on Ubuntu Touch, as reported in the manifest.
+ *
+ * This is often called the "App Name"; the human-readable name for an app is
+ * called the "App Title".
+ *
+ * This string is needed by some low-level OS features to operate properly.
+ *
+ * \since This macro is available since SDL 3.6.0.
+ *
+ * \sa SDL_IsUbuntuTouch
+ */
+#define SDL_PROP_GLOBAL_SYSTEM_UBUNTU_TOUCH_APPID_STRING "SDL.system.ubuntu_touch.appid"
+
+/**
+ * The identifier for the specific hook which launched the current executable,
+ * as reported in the manifest.
+ *
+ * This is relevant for application packages that ship multiple applications
+ * with their desktop files; they will have the same app ID but will differ by
+ * their hook.
+ *
+ * \since This macro is available since SDL 3.6.0.
+ *
+ * \sa SDL_IsUbuntuTouch
+ */
+#define SDL_PROP_GLOBAL_SYSTEM_UBUNTU_TOUCH_HOOK_STRING "SDL.system.ubuntu_touch.hook"
+
+/**
+ * The version of the application on Ubuntu Touch, as reported in the manifest.
+ *
+ * \since This macro is available since SDL 3.6.0.
+ *
+ * \sa SDL_IsUbuntuTouch
+ */
+#define SDL_PROP_GLOBAL_SYSTEM_UBUNTU_TOUCH_APP_VERSION_STRING "SDL.system.ubuntu_touch.app_version"
+
+#endif
+
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
 }

+ 16 - 0
src/SDL.c

@@ -169,6 +169,16 @@ const char *SDL_GetAppMetadataProperty(const char *name)
         value = SDL_GetStringProperty(SDL_GetGlobalProperties(), name, NULL);
     }
     if (!value || !*value) {
+#ifdef SDL_PLATFORM_LINUX
+        if (SDL_IsUbuntuTouch()) {
+            if (SDL_strcmp(name, SDL_PROP_APP_METADATA_IDENTIFIER_STRING) == 0) {
+                value = SDL_GetStringProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_SYSTEM_UBUNTU_TOUCH_APPID_STRING, NULL);
+            } else if (SDL_strcmp(name, SDL_PROP_APP_METADATA_VERSION_STRING) == 0) {
+                value = SDL_GetStringProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_SYSTEM_UBUNTU_TOUCH_APP_VERSION_STRING, NULL);
+            }
+        }
+#endif
+
         if (SDL_strcmp(name, SDL_PROP_APP_METADATA_NAME_STRING) == 0) {
             value = SDL_GetExeName();
             if (!value) {
@@ -915,6 +925,12 @@ static SDL_Sandbox SDL_DetectSandbox(void)
         return SDL_SANDBOX_SNAP;
     }
 
+    /* Ubuntu Touch also supports Snap; check for classic sandboxing only if
+     * Snap hasn't been detected. */
+    if (SDL_getenv("LOMIRI_APPLICATION_ISOLATION") || SDL_getenv("CLICKABLE_DESKTOP_MODE")) {
+        return SDL_SANDBOX_LOMIRI;
+    }
+
     if (access("/run/host/container-manager", F_OK) == 0) {
         return SDL_SANDBOX_UNKNOWN_CONTAINER;
     }

+ 9 - 0
src/SDL_properties.c

@@ -22,6 +22,7 @@
 
 #include "SDL_hints_c.h"
 #include "SDL_properties_c.h"
+#include "core/linux/SDL_ubuntu_touch.h"
 
 
 typedef struct
@@ -146,6 +147,14 @@ SDL_PropertiesID SDL_GetGlobalProperties(void)
         props = SDL_CreateProperties();
 
         // Set global platform properties
+#ifdef SDL_PLATFORM_LINUX
+        if (SDL_IsUbuntuTouch()) {
+            if (!SDL_SetupUbuntuTouchGlobalProperties(props)) {
+                SDL_DestroyProperties(props);
+                return 0;
+            }
+        }
+#endif
 
         if (!SDL_CompareAndSwapAtomicU32(&SDL_global_properties, 0, props)) {
             // Somebody else created global properties before us, just use those

+ 9 - 0
src/core/SDL_core_unsupported.c

@@ -195,3 +195,12 @@ Sint32 JNI_OnLoad(JavaVM *vm, void *reserved)
     return 0x00010004; // JNI_VERSION_1_4
 }
 #endif
+
+#ifndef SDL_PLATFORM_LINUX
+
+bool SDL_IsUbuntuTouch(void)
+{
+    return false;
+}
+
+#endif

+ 4 - 0
src/core/SDL_core_unsupported.h

@@ -65,3 +65,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_IsChromebook(void);
 extern SDL_DECLSPEC bool SDLCALL SDL_IsDeXMode(void);
 extern SDL_DECLSPEC Sint32 SDLCALL JNI_OnLoad(JavaVM *vm, void *reserved);
 #endif /* !SDL_PLATFORM_ANDROID */
+
+#if !defined(SDL_PLATFORM_LINUX)
+extern SDL_DECLSPEC bool SDLCALL SDL_IsUbuntuTouch(void);
+#endif /* !SDL_PLATFORM_LINUX */

+ 49 - 0
src/core/linux/SDL_system_theme.c

@@ -24,6 +24,7 @@
 #include "SDL_system_theme.h"
 #include "../../video/SDL_sysvideo.h"
 
+#include <stdio.h>
 #include <unistd.h>
 
 #define PORTAL_DESTINATION "org.freedesktop.portal.Desktop"
@@ -150,7 +151,55 @@ incorrect_type:
     return true;
 }
 
+SDL_SystemTheme UbuntuTouch_GetSystemTheme(void)
+{
+    SDL_SystemTheme theme = SDL_SYSTEM_THEME_UNKNOWN;
+    FILE *config_file = NULL;
+    char *line = NULL;
+    size_t line_alloc = 0;
+    ssize_t line_size = 0;
+    bool is_in_general_category = false;
+
+    // "Lomiri": Ubuntu Touch 20.04+
+    // "Ubuntu": Ubuntu Touch 16.04
+    config_file = fopen("/home/phablet/.config/lomiri-ui-toolkit/theme.ini", "r");
+    if (!config_file) {
+        config_file = fopen("/home/phablet/.config/ubuntu-ui-toolkit/theme.ini", "r");
+        if (!config_file) {
+            return SDL_SYSTEM_THEME_UNKNOWN;
+        }
+    }
+
+    while ((line_size = getline(&line, &line_alloc, config_file)) != -1) {
+        if (line_size >= 1 && line[0] == '[') {
+            is_in_general_category = SDL_strcmp(line, "[General]\n") == 0;
+        } else if (is_in_general_category && SDL_strncmp(line, "theme=", 6) == 0) {
+            if (SDL_strcmp(line, "theme=Lomiri.Components.Themes.SuruDark\n") == 0 ||
+                SDL_strcmp(line, "theme=Ubuntu.Components.Themes.SuruDark\n") == 0) {
+                theme = SDL_SYSTEM_THEME_DARK;
+            } else if (SDL_strcmp(line, "theme=Lomiri.Components.Themes.Ambiance\n") == 0 ||
+                       SDL_strcmp(line, "theme=Ubuntu.Components.Themes.Ambiance\n") == 0) {
+                theme = SDL_SYSTEM_THEME_LIGHT;
+            } else {
+                theme = SDL_SYSTEM_THEME_UNKNOWN;
+            }
+        }
+    }
+
+    free(line); // This should NOT be SDL_free()
+
+    fclose(config_file);
+
+    return theme;
+}
+
 SDL_SystemTheme SDL_SystemTheme_Get(void)
 {
+    if (system_theme_data.theme == SDL_SYSTEM_THEME_UNKNOWN) {
+        // TODO: Use inotify to watch for changes, so that the config file
+        // doesn't need to be checked each time.
+        return UbuntuTouch_GetSystemTheme();
+    }
+
     return system_theme_data.theme;
 }

+ 90 - 0
src/core/linux/SDL_ubuntu_touch.c

@@ -0,0 +1,90 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2026 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_PLATFORM_LINUX
+#include <stdio.h>
+#include <unistd.h>
+
+bool SDL_IsUbuntuTouch(void)
+{
+    static bool checked = false;
+    static bool is_ubuntu_touch = false;
+
+    if (!checked) {
+        const char *xdg_session_desktop = SDL_getenv("XDG_SESSION_DESKTOP");
+        if (xdg_session_desktop && SDL_strcmp(xdg_session_desktop, "ubuntu-touch") == 0) {
+            is_ubuntu_touch = true;
+        }
+        checked = true;
+    }
+    return is_ubuntu_touch;
+}
+
+bool SDL_SetupUbuntuTouchGlobalProperties(SDL_PropertiesID props)
+{
+    if (!SDL_IsUbuntuTouch()) {
+        return true;
+    }
+
+    // Format is <appname>_<hookname>_<version>, like myapp.myname_myapp_1.0.0.
+    // None of those are allowed to have underscores.
+    const char *app_id = SDL_getenv("APP_ID");
+    if (!app_id) {
+        SDL_SetError("Missing APP_ID");
+        return false;
+    }
+
+    char *buffer = SDL_strdup(app_id);
+    if (!buffer) {
+        return false;
+    }
+
+    char *it = SDL_strchr(buffer, '_');
+    if (!*it) {
+        SDL_SetError("Malformed APP_ID");
+        SDL_free(buffer);
+        return NULL;
+    }
+
+    char *it2 = SDL_strchr(it + 1, '_');
+    if (!*it2) {
+        SDL_SetError("Malformed APP_ID");
+        SDL_free(buffer);
+        return NULL;
+    }
+
+    *it++ = '\0';
+    *it2++ = '\0';
+
+    if (!SDL_SetStringProperty(props, SDL_PROP_GLOBAL_SYSTEM_UBUNTU_TOUCH_APPID_STRING, buffer) ||
+        !SDL_SetStringProperty(props, SDL_PROP_GLOBAL_SYSTEM_UBUNTU_TOUCH_HOOK_STRING, it) ||
+        !SDL_SetStringProperty(props, SDL_PROP_GLOBAL_SYSTEM_UBUNTU_TOUCH_APP_VERSION_STRING, it2)) {
+        SDL_free(buffer);
+        return false;
+    }
+
+    SDL_free(buffer);
+
+    return true;
+}
+
+#endif

+ 27 - 0
src/core/linux/SDL_ubuntu_touch.h

@@ -0,0 +1,27 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2026 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_PLATFORM_LINUX
+
+bool SDL_SetupUbuntuTouchGlobalProperties(SDL_PropertiesID props);
+
+#endif

+ 1 - 0
src/dynapi/SDL_dynapi.exports

@@ -1300,3 +1300,4 @@ _SDL_ShowNotification
 _SDL_RemoveNotification
 _SDL_GetDeviceFormFactor
 _SDL_GetDeviceFormFactorName
+_SDL_IsUbuntuTouch

+ 1 - 0
src/dynapi/SDL_dynapi.sym

@@ -1301,6 +1301,7 @@ SDL3_0.0.0 {
     SDL_RemoveNotification;
     SDL_GetDeviceFormFactor;
     SDL_GetDeviceFormFactorName;
+    SDL_IsUbuntuTouch;
     # extra symbols go here (don't modify this line)
   local: *;
 };

+ 1 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -1327,3 +1327,4 @@
 #define SDL_RemoveNotification SDL_RemoveNotification_REAL
 #define SDL_GetDeviceFormFactor SDL_GetDeviceFormFactor_REAL
 #define SDL_GetDeviceFormFactorName SDL_GetDeviceFormFactorName_REAL
+#define SDL_IsUbuntuTouch SDL_IsUbuntuTouch_REAL

+ 1 - 0
src/dynapi/SDL_dynapi_procs.h

@@ -1335,3 +1335,4 @@ SDL_DYNAPI_PROC(SDL_NotificationID,SDL_ShowNotification,(const char *a,const cha
 SDL_DYNAPI_PROC(bool,SDL_RemoveNotification,(SDL_NotificationID a),(a),return)
 SDL_DYNAPI_PROC(SDL_FormFactor,SDL_GetDeviceFormFactor,(void),(),return)
 SDL_DYNAPI_PROC(const char*,SDL_GetDeviceFormFactorName,(SDL_FormFactor a),(a),return)
+SDL_DYNAPI_PROC(bool,SDL_IsUbuntuTouch,(void),(),return)

+ 21 - 0
src/filesystem/unix/SDL_sysfilesystem.c

@@ -285,6 +285,27 @@ char *SDL_SYS_GetPrefPath(const char *org, const char *app)
     char *result = NULL;
     char *ptr = NULL;
 
+#ifdef SDL_PLATFORM_LINUX
+    if (SDL_IsUbuntuTouch()) {
+        // On Ubuntu Touch, the only allowed data folder is:
+        //     ~/.local/share/<app id>/
+
+        SDL_PropertiesID props = SDL_GetGlobalProperties();
+        if (!props) {
+            return NULL;
+        }
+
+        const char *appid = SDL_GetStringProperty(props, SDL_PROP_GLOBAL_SYSTEM_UBUNTU_TOUCH_APPID_STRING, NULL);
+        if (!appid) {
+            SDL_SetError("Ubuntu Touch App ID missing from global properties");
+            return NULL;
+        }
+
+        app = appid;
+        org = "";
+    }
+#endif
+
     if (!envr) {
         // You end up with "$HOME/.local/share/Game Name 2"
         envr = SDL_getenv("HOME");

+ 6 - 0
src/video/SDL_video.c

@@ -657,6 +657,12 @@ bool SDL_VideoInit(const char *driver_name)
 
     // Select the proper video driver
     video = NULL;
+#ifdef SDL_PLATFORM_LINUX
+    // https://github.com/libsdl-org/SDL/issues/12247
+    if (SDL_IsUbuntuTouch()) {
+        driver_name = "wayland";
+    }
+#endif
     if (!driver_name) {
         driver_name = SDL_GetHint(SDL_HINT_VIDEO_DRIVER);
     }

+ 1 - 0
test/testsymbols.c

@@ -53,6 +53,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_SetiOSEventPump(void);
 #if !defined(SDL_PLATFORM_LINUX)
 extern SDL_DECLSPEC void SDLCALL SDL_SetLinuxThreadPriority(void);
 extern SDL_DECLSPEC void SDLCALL SDL_SetLinuxThreadPriorityAndPolicy(void);
+extern SDL_DECLSPEC bool SDLCALL SDL_IsUbuntuTouch(void);
 #endif
 
 #if !(defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK))