Skip to content

Commit 1b21648

Browse files
author
Semphris
committed
Add basic Ubuntu Touch functions
This adds support for: - System theme - Sandbox detection - Platform detection - Device form factor detection
1 parent efe122b commit 1b21648

11 files changed

+244
-1
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1790,6 +1790,7 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
17901790
sdl_sources(
17911791
"${SDL3_SOURCE_DIR}/src/core/linux/SDL_evdev_capabilities.c"
17921792
"${SDL3_SOURCE_DIR}/src/core/linux/SDL_threadprio.c"
1793+
"${SDL3_SOURCE_DIR}/src/core/linux/SDL_ubuntu_touch.c"
17931794
)
17941795

17951796
# src/core/unix/*.c is included in a generic if(UNIX) section, elsewhere.

docs/README-ubuntu-touch.md

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Ubuntu Touch / Lomiri
2+
3+
Ubuntu Touch being similar to Ubuntu desktop, most features should be supported
4+
out-of-the-box with SDL.
5+
6+
## Developing apps
7+
8+
Ubuntu Touch apps are developed using [Clickable](https://clickable-ut.dev/).
9+
10+
Clickable provides an SDL template. It is highly recommended to use the template
11+
as a starting point for both new and existing apps.
12+
13+
## Considerations
14+
15+
Ubuntu Touch is similar to the desktop version of Ubuntu, but presents some
16+
differences in behavior. Developers should be wary of the following:
17+
18+
### SDL_GetPrefPath
19+
20+
The only allowed writable folder is `~/.local/share/<appname>/`, where
21+
`<appname>` is the identifier string for the app. It can be found in the
22+
`manifest.json` file under the key `"name"`, and usually looks like
23+
`appname.yourname`.
24+
25+
A future version of SDL may change how `SDL_GetPrefPath` operates on Ubuntu
26+
Touch to make it ignore its arguments and always choose the correct writable
27+
folder. For future-proof-ness, using `SDL_GetPrefPath("", "<appname>")` as a
28+
writable directory is a safe choice.
29+
30+
### Video driver
31+
32+
Currently, [a bug](https://github.com/libsdl-org/SDL/issues/12247) forces SDL to
33+
use the Wayland driver on Ubuntu Touch. No changes are needed in apps, but
34+
forcing the X11 driver won't work.
35+
36+
### Extra functions
37+
38+
Some extra functions are provided to help development with Ubuntu Touch. A full
39+
list can be found in the documentation; some of these are documented here:
40+
- `SDL_IsUbuntuTouch()` to differentiate between Ubuntu Touch and regular Unix.
41+
- `SDL_GetUbuntuTouchFormFactor()` to obtain what kind of device the app is
42+
being ran on.

include/SDL3/SDL_system.h

+45-1
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,8 @@ typedef enum SDL_Sandbox
631631
SDL_SANDBOX_UNKNOWN_CONTAINER,
632632
SDL_SANDBOX_FLATPAK,
633633
SDL_SANDBOX_SNAP,
634-
SDL_SANDBOX_MACOS
634+
SDL_SANDBOX_MACOS,
635+
SDL_SANDBOX_LOMIRI
635636
} SDL_Sandbox;
636637

637638
/**
@@ -809,6 +810,49 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetGDKDefaultUser(XUserHandle *outUserHandl
809810

810811
#endif
811812

813+
/**
814+
* The possible form factors for an Ubuntu Touch device.
815+
*
816+
* \since This enum is available since SDL 3.4.0.
817+
*
818+
* \sa SDL_GetUbuntuTouchFormFactor
819+
*/
820+
typedef enum SDL_UTFormFactor {
821+
SDL_UTFORMFACTOR_UNKNOWN,
822+
SDL_UTFORMFACTOR_PHONE,
823+
SDL_UTFORMFACTOR_TABLET,
824+
SDL_UTFORMFACTOR_LAPTOP,
825+
SDL_UTFORMFACTOR_DESKTOP
826+
} SDL_UTFormFactor;
827+
828+
/*
829+
* Functions used only with Ubuntu Touch
830+
*/
831+
#ifdef SDL_PLATFORM_LINUX
832+
833+
/**
834+
* Detect whether the current platform is Ubuntu Touch.
835+
*
836+
* \returns true if the platform is Ubuntu Touch; false otherwise.
837+
*
838+
* \since This function is available since SDL 3.4.0.
839+
*/
840+
extern SDL_DECLSPEC bool SDLCALL SDL_IsUbuntuTouch(void);
841+
842+
/**
843+
* Get the form factor of the current device.
844+
*
845+
* \returns the form factor of the current device, or SDL_UTFORMFACTOR_UNKNOWN
846+
* on error or if the current platform isn't Ubuntu Touch.
847+
*
848+
* \since This function is available since SDL 3.4.0.
849+
*
850+
* \sa SDL_UTFormFactor
851+
*/
852+
extern SDL_DECLSPEC SDL_UTFormFactor SDLCALL SDL_GetUbuntuTouchFormFactor(void);
853+
854+
#endif
855+
812856
/* Ends C function definitions when using C++ */
813857
#ifdef __cplusplus
814858
}

src/SDL.c

+11
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,11 @@ bool SDL_IsTablet(void)
774774
#elif defined(SDL_PLATFORM_IOS)
775775
extern bool SDL_IsIPad(void);
776776
return SDL_IsIPad();
777+
#elif defined(SDL_PLATFORM_LINUX)
778+
if (SDL_IsUbuntuTouch()) {
779+
return SDL_GetUbuntuTouchFormFactor() == SDL_UTFORMFACTOR_TABLET;
780+
}
781+
return false;
777782
#else
778783
return false;
779784
#endif
@@ -804,6 +809,12 @@ static SDL_Sandbox SDL_DetectSandbox(void)
804809
return SDL_SANDBOX_SNAP;
805810
}
806811

812+
/* Ubuntu Touch also supports Snap; check for classic sandboxing only if
813+
* Snap hasn't been detected. */
814+
if (SDL_getenv("LOMIRI_APPLICATION_ISOLATION") || SDL_getenv("CLICKABLE_DESKTOP_MODE")) {
815+
return SDL_SANDBOX_LOMIRI;
816+
}
817+
807818
if (access("/run/host/container-manager", F_OK) == 0) {
808819
return SDL_SANDBOX_UNKNOWN_CONTAINER;
809820
}

src/core/SDL_core_unsupported.c

+16
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,19 @@ Sint32 JNI_OnLoad(void *vm, void *reserved)
211211
return -1; // JNI_ERR
212212
}
213213
#endif
214+
215+
#ifndef SDL_PLATFORM_LINUX
216+
217+
SDL_DECLSPEC bool SDLCALL SDL_IsUbuntuTouch(void);
218+
bool SDL_IsUbuntuTouch(void)
219+
{
220+
return false;
221+
}
222+
223+
SDL_DECLSPEC SDL_UTFormFactor SDLCALL SDL_GetUbuntuTouchFormFactor(void);
224+
SDL_UTFormFactor SDL_GetUbuntuTouchFormFactor(void)
225+
{
226+
return SDL_UTFORMFACTOR_UNKNOWN;
227+
}
228+
229+
#endif

src/core/linux/SDL_system_theme.c

+49
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "SDL_system_theme.h"
2525
#include "../../video/SDL_sysvideo.h"
2626

27+
#include <stdio.h>
2728
#include <unistd.h>
2829

2930
#define PORTAL_DESTINATION "org.freedesktop.portal.Desktop"
@@ -150,7 +151,55 @@ bool SDL_SystemTheme_Init(void)
150151
return true;
151152
}
152153

154+
SDL_SystemTheme UbuntuTouch_GetSystemTheme(void)
155+
{
156+
SDL_SystemTheme theme = SDL_SYSTEM_THEME_UNKNOWN;
157+
FILE *config_file = NULL;
158+
char *line = NULL;
159+
size_t line_alloc = 0;
160+
ssize_t line_size = 0;
161+
bool is_in_general_category = false;
162+
163+
// "Lomiri": Ubuntu Touch 20.04+
164+
// "Ubuntu": Ubuntu Touch 16.04
165+
config_file = fopen("/home/phablet/.config/lomiri-ui-toolkit/theme.ini", "r");
166+
if (!config_file) {
167+
config_file = fopen("/home/phablet/.config/ubuntu-ui-toolkit/theme.ini", "r");
168+
if (!config_file) {
169+
return SDL_SYSTEM_THEME_UNKNOWN;
170+
}
171+
}
172+
173+
while ((line_size = getline(&line, &line_alloc, config_file)) != -1) {
174+
if (line_size >= 1 && line[0] == '[') {
175+
is_in_general_category = SDL_strcmp(line, "[General]\n") == 0;
176+
} else if (is_in_general_category && SDL_strncmp(line, "theme=", 6) == 0) {
177+
if (SDL_strcmp(line, "theme=Lomiri.Components.Themes.SuruDark\n") == 0 ||
178+
SDL_strcmp(line, "theme=Ubuntu.Components.Themes.SuruDark\n") == 0) {
179+
theme = SDL_SYSTEM_THEME_DARK;
180+
} else if (SDL_strcmp(line, "theme=Lomiri.Components.Themes.Ambiance\n") == 0 ||
181+
SDL_strcmp(line, "theme=Ubuntu.Components.Themes.Ambiance\n") == 0) {
182+
theme = SDL_SYSTEM_THEME_LIGHT;
183+
} else {
184+
theme = SDL_SYSTEM_THEME_UNKNOWN;
185+
}
186+
}
187+
188+
free(line); // This should NOT be SDL_free()
189+
}
190+
191+
fclose(config_file);
192+
193+
return theme;
194+
}
195+
153196
SDL_SystemTheme SDL_SystemTheme_Get(void)
154197
{
198+
if (system_theme_data.theme == SDL_SYSTEM_THEME_UNKNOWN) {
199+
// TODO: Use inotify to watch for changes, so that the config file
200+
// doesn't need to be checked each time.
201+
return UbuntuTouch_GetSystemTheme();
202+
}
203+
155204
return system_theme_data.theme;
156205
}

src/core/linux/SDL_ubuntu_touch.c

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
Simple DirectMedia Layer
3+
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
4+
5+
This software is provided 'as-is', without any express or implied
6+
warranty. In no event will the authors be held liable for any damages
7+
arising from the use of this software.
8+
9+
Permission is granted to anyone to use this software for any purpose,
10+
including commercial applications, and to alter it and redistribute it
11+
freely, subject to the following restrictions:
12+
13+
1. The origin of this software must not be misrepresented; you must not
14+
claim that you wrote the original software. If you use this software
15+
in a product, an acknowledgment in the product documentation would be
16+
appreciated but is not required.
17+
2. Altered source versions must be plainly marked as such, and must not be
18+
misrepresented as being the original software.
19+
3. This notice may not be removed or altered from any source distribution.
20+
*/
21+
#include "SDL_internal.h"
22+
23+
#ifdef SDL_PLATFORM_LINUX
24+
#include <stdio.h>
25+
#include <unistd.h>
26+
27+
bool SDL_IsUbuntuTouch(void)
28+
{
29+
return access("/etc/ubuntu-touch-session.d/", F_OK) == 0;
30+
}
31+
32+
// https://docs.ubports.com/en/latest/porting/configure_test_fix/Display.html#form-factor
33+
SDL_UTFormFactor SDL_GetUbuntuTouchFormFactor(void)
34+
{
35+
SDL_UTFormFactor form_factor = SDL_UTFORMFACTOR_UNKNOWN;
36+
FILE *config_file = NULL;
37+
char *line = NULL;
38+
size_t line_alloc = 0;
39+
ssize_t line_size = 0;
40+
41+
config_file = fopen("/etc/ubuntu-touch-session.d/android.conf", "r");
42+
if (!config_file) {
43+
return form_factor;
44+
}
45+
46+
while ((line_size = getline(&line, &line_alloc, config_file)) != -1) {
47+
if (SDL_strncmp(line, "FORM_FACTOR=", 12) == 0) {
48+
if (SDL_strcmp(line, "FORM_FACTOR=handset\n")) {
49+
form_factor = SDL_UTFORMFACTOR_PHONE;
50+
} else if (SDL_strcmp(line, "FORM_FACTOR=tablet\n")) {
51+
form_factor = SDL_UTFORMFACTOR_TABLET;
52+
} else if (SDL_strcmp(line, "FORM_FACTOR=laptop\n")) {
53+
form_factor = SDL_UTFORMFACTOR_LAPTOP;
54+
} else if (SDL_strcmp(line, "FORM_FACTOR=desktop\n")) {
55+
form_factor = SDL_UTFORMFACTOR_DESKTOP;
56+
} else {
57+
form_factor = SDL_UTFORMFACTOR_UNKNOWN;
58+
}
59+
}
60+
61+
free(line); // This should NOT be SDL_free()
62+
}
63+
64+
fclose(config_file);
65+
66+
return form_factor;
67+
}
68+
#endif

src/dynapi/SDL_dynapi.sym

+2
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,8 @@ SDL3_0.0.0 {
12421242
SDL_SetGPURenderStateFragmentUniforms;
12431243
SDL_SetRenderGPUState;
12441244
SDL_DestroyGPURenderState;
1245+
SDL_IsUbuntuTouch;
1246+
SDL_GetUbuntuTouchFormFactor;
12451247
# extra symbols go here (don't modify this line)
12461248
local: *;
12471249
};

src/dynapi/SDL_dynapi_overrides.h

+2
Original file line numberDiff line numberDiff line change
@@ -1267,3 +1267,5 @@
12671267
#define SDL_SetGPURenderStateFragmentUniforms SDL_SetGPURenderStateFragmentUniforms_REAL
12681268
#define SDL_SetRenderGPUState SDL_SetRenderGPUState_REAL
12691269
#define SDL_DestroyGPURenderState SDL_DestroyGPURenderState_REAL
1270+
#define SDL_IsUbuntuTouch SDL_IsUbuntuTouch_REAL
1271+
#define SDL_GetUbuntuTouchFormFactor SDL_GetUbuntuTouchFormFactor_REAL

src/dynapi/SDL_dynapi_procs.h

+2
Original file line numberDiff line numberDiff line change
@@ -1275,3 +1275,5 @@ SDL_DYNAPI_PROC(SDL_GPURenderState*,SDL_CreateGPURenderState,(SDL_Renderer *a,SD
12751275
SDL_DYNAPI_PROC(bool,SDL_SetGPURenderStateFragmentUniforms,(SDL_GPURenderState *a,Uint32 b,const void *c,Uint32 d),(a,b,c,d),return)
12761276
SDL_DYNAPI_PROC(bool,SDL_SetRenderGPUState,(SDL_Renderer *a,SDL_GPURenderState *b),(a,b),return)
12771277
SDL_DYNAPI_PROC(void,SDL_DestroyGPURenderState,(SDL_GPURenderState *a),(a),)
1278+
SDL_DYNAPI_PROC(bool,SDL_IsUbuntuTouch,(void),(),return)
1279+
SDL_DYNAPI_PROC(SDL_UTFormFactor,SDL_GetUbuntuTouchFormFactor,(void),(),return)

src/video/SDL_video.c

+6
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,12 @@ bool SDL_VideoInit(const char *driver_name)
647647
if (!driver_name) {
648648
driver_name = SDL_GetHint(SDL_HINT_VIDEO_DRIVER);
649649
}
650+
#ifdef SDL_PLATFORM_LINUX
651+
// https://github.com/libsdl-org/SDL/issues/12247
652+
if (SDL_IsUbuntuTouch()) {
653+
driver_name = "wayland";
654+
}
655+
#endif
650656
if (driver_name && *driver_name != 0) {
651657
const char *driver_attempt = driver_name;
652658
while (driver_attempt && *driver_attempt != 0 && !video) {

0 commit comments

Comments
 (0)