From b29ffab9cb7bbe0087f928869437efae9780b5be Mon Sep 17 00:00:00 2001 From: Simon McVittie <smcv@collabora.com> Date: Tue, 4 Feb 2025 19:18:35 +0000 Subject: [PATCH] pressure-vessel: Add STEAM_COMPAT_RUNTIME_SDL2=sdl2-compat After we add sdl2-compat to container runtimes, we will presumably want a reasonably long testing period in which the default is still "classic" SDL2, but users, developers and testers can opt-in to using sdl2-compat as a replacement for it. This mechanism lets us achieve that. This will not be practically useful until soldier and/or sniper includes a copy of sdl2-compat. steamrt/tasks#579, steamrt/tasks#573 Signed-off-by: Simon McVittie <smcv@collabora.com> --- docs/steam-compat-tool-interface.md | 9 +++++++ pressure-vessel/adverb-sdl.c | 38 +++++++++++++++++++++------- pressure-vessel/wrap.1.md | 7 ++++- steam-runtime-tools/steam-internal.h | 1 + steam-runtime-tools/steam.c | 17 ++++++++++++- tests/pressure-vessel/adverb-sdl.c | 22 ++++++++++++++++ tests/utils.c | 4 +++ 7 files changed, 87 insertions(+), 11 deletions(-) diff --git a/docs/steam-compat-tool-interface.md b/docs/steam-compat-tool-interface.md index 7fb910d69..c7017a06d 100644 --- a/docs/steam-compat-tool-interface.md +++ b/docs/steam-compat-tool-interface.md @@ -393,6 +393,14 @@ Some environment variables are set by Steam, including: (For example, a game's Launch Options can be set to `STEAM_COMPAT_RUNTIME_SDL2=1 %command%`) + May also be set to `sdl2-compat` to attempt to use the + [sdl2-compat][] library, if available, in preference to + "classic" SDL2. + This will only work if the container runtime actually contains a + copy of sdl2-compat. + (For example, a game's Launch Options can be set to + `STEAM_COMPAT_RUNTIME_SDL2=sdl2-compat %command%`) + * `STEAM_COMPAT_RUNTIME_SDL3`: May be set to 1 for the same effect as adding `runtime-sdl3` to @@ -654,5 +662,6 @@ unless this is set to the game's top-level directory. [ldlp]: ld-library-path-runtime.md [prctl]: https://manpages.debian.org/unstable/manpages-dev/prctl.2.en.html [scout-on-soldier]: container-runtime.md#steam-linux-runtime-scout-on-soldier +[sdl2-compat]: https://github.com/libsdl-org/sdl2-compat/ [sniper]: container-runtime.md#steam-runtime-3-sniper [soldier]: container-runtime.md#steam-runtime-2-soldier diff --git a/pressure-vessel/adverb-sdl.c b/pressure-vessel/adverb-sdl.c index 72474c4b6..c0d2c2fc4 100644 --- a/pressure-vessel/adverb-sdl.c +++ b/pressure-vessel/adverb-sdl.c @@ -6,16 +6,31 @@ #include "config.h" #include "adverb-sdl.h" +/* + * pv_adverb_set_up_dynamic_sdl: + * @wrapped_command: The command to run and its environment + * @lib_temp_dirs (nullable): A set of per-architecture directories + * using the dynamic string tokens `${PLATFORM}` or `${LIB}` + * @prefix: Where to find SDL, normally `/usr` unless unit-testing + * @overrides: Path to libraries overridden by the #PvRuntime, + * for example `/overrides` + * @dynamic_var: Environment variable name such as `SDL_DYNAMIC_API` + * @library: Path to SDL relative to `${libdir}`, + * for example `sdl2-compat/libSDL2-2.0.so.0` + * @error: Used to report error on failure + */ gboolean pv_adverb_set_up_dynamic_sdl (FlatpakBwrap *wrapped_command, PvPerArchDirs *lib_temp_dirs, const char *prefix, const char *overrides, const char *dynamic_var, - const char *soname, + const char *library, GError **error) { gboolean have_any = FALSE; + /* Only the filename part of @library */ + const char *soname = glnx_basename (library); size_t abi_index; const char *existing_value; @@ -26,7 +41,7 @@ pv_adverb_set_up_dynamic_sdl (FlatpakBwrap *wrapped_command, /* Treat SDL{,3}_DYNAMIC_API from e.g. launch options as being * more important than STEAM_COMPAT_FLAGS=runtime-sdl{2,3} */ g_message ("Not using %s from runtime because %s is already set to \"%s\"", - soname, dynamic_var, existing_value); + library, dynamic_var, existing_value); return TRUE; } @@ -45,8 +60,8 @@ pv_adverb_set_up_dynamic_sdl (FlatpakBwrap *wrapped_command, /* We assume a Debian multiarch layout here: in practice this * is true for all Steam Runtime branches. */ - from_runtime = g_build_filename (prefix, "lib", multiarch_tuple, soname, NULL); - override = g_build_filename (overrides, "lib", multiarch_tuple, soname, NULL); + from_runtime = g_build_filename (prefix, "lib", multiarch_tuple, library, NULL); + override = g_build_filename (overrides, "lib", multiarch_tuple, library, NULL); if (g_file_test (override, G_FILE_TEST_EXISTS)) { @@ -57,7 +72,7 @@ pv_adverb_set_up_dynamic_sdl (FlatpakBwrap *wrapped_command, * in the graphics stack does depend on SDL, we really have * no choice but to use that version. */ g_message ("Using %s %s from graphics stack provider instead of runtime", - multiarch_tuple, soname); + multiarch_tuple, library); target = override; } else if (g_file_test (from_runtime, G_FILE_TEST_EXISTS)) @@ -88,14 +103,14 @@ pv_adverb_set_up_dynamic_sdl (FlatpakBwrap *wrapped_command, g_autofree gchar *value = NULL; value = g_build_filename (lib_temp_dirs->libdl_token_path, soname, NULL); - g_info ("Setting %s=\"%s\" to use runtime's SDL", dynamic_var, soname); + g_info ("Setting %s=\"%s\" to use runtime's %s", dynamic_var, soname, library); flatpak_bwrap_set_env (wrapped_command, dynamic_var, value, TRUE); } else { return glnx_throw (error, "Unable to set %s: %s wasn't available for any architecture", - dynamic_var, soname); + dynamic_var, library); } return TRUE; @@ -111,11 +126,16 @@ pv_adverb_set_up_dynamic_sdls (FlatpakBwrap *wrapped_command, static const struct { const char *dynamic_var; - const char *soname; + const char *library; SrtSteamCompatFlags if_flag; } sdls[] = { + { + "SDL_DYNAMIC_API", + "sdl2-compat/libSDL2-2.0.so.0", + SRT_STEAM_COMPAT_FLAGS_RUNTIME_SDL2_COMPAT + }, { "SDL_DYNAMIC_API", "libSDL2-2.0.so.0", @@ -140,7 +160,7 @@ pv_adverb_set_up_dynamic_sdls (FlatpakBwrap *wrapped_command, prefix, overrides, sdls[i].dynamic_var, - sdls[i].soname, + sdls[i].library, &local_error)) g_warning ("%s", local_error->message); } diff --git a/pressure-vessel/wrap.1.md b/pressure-vessel/wrap.1.md index 920d9d09f..1f16614c0 100644 --- a/pressure-vessel/wrap.1.md +++ b/pressure-vessel/wrap.1.md @@ -1105,7 +1105,7 @@ be made available read/write in the container. </dd> <dt> -`STEAM_COMPAT_RUNTIME_SDL2` (boolean) +`STEAM_COMPAT_RUNTIME_SDL2` (`0`, `1` or `sdl2-compat`) </dt><dd> @@ -1119,6 +1119,11 @@ paths so that both 32- and 64-bit SDL 2 will work. Typically this would be enabled by setting a game's Steam launch options to `STEAM_COMPAT_RUNTIME_SDL2=1 %command%`. +If set to `sdl2-compat`, +and the runtime contains a copy of `sdl2-compat` as a non-default +implementation of SDL, +do the same for that. + </dd> <dt> diff --git a/steam-runtime-tools/steam-internal.h b/steam-runtime-tools/steam-internal.h index 9cdf16149..74877413a 100644 --- a/steam-runtime-tools/steam-internal.h +++ b/steam-runtime-tools/steam-internal.h @@ -80,6 +80,7 @@ typedef enum SRT_STEAM_COMPAT_FLAGS_SYSTEM_TRACING = (1 << 2), SRT_STEAM_COMPAT_FLAGS_RUNTIME_SDL2 = (1 << 3), SRT_STEAM_COMPAT_FLAGS_RUNTIME_SDL3 = (1 << 4), + SRT_STEAM_COMPAT_FLAGS_RUNTIME_SDL2_COMPAT = (1 << 5), SRT_STEAM_COMPAT_FLAGS_NONE = 0 } SrtSteamCompatFlags; diff --git a/steam-runtime-tools/steam.c b/steam-runtime-tools/steam.c index ff9bd7242..c0f2a92a0 100644 --- a/steam-runtime-tools/steam.c +++ b/steam-runtime-tools/steam.c @@ -760,7 +760,6 @@ _srt_steam_get_compat_flags (const char * const *envp) bool_vars[] = { { "STEAM_COMPAT_TRACING", SRT_STEAM_COMPAT_FLAGS_SYSTEM_TRACING, FALSE }, - { "STEAM_COMPAT_RUNTIME_SDL2", SRT_STEAM_COMPAT_FLAGS_RUNTIME_SDL2, FALSE }, { "STEAM_COMPAT_RUNTIME_SDL3", SRT_STEAM_COMPAT_FLAGS_RUNTIME_SDL3, FALSE }, }; SrtSteamCompatFlags ret = SRT_STEAM_COMPAT_FLAGS_NONE; @@ -777,6 +776,22 @@ _srt_steam_get_compat_flags (const char * const *envp) ret |= bool_vars[i].value; } + value = _srt_environ_getenv (envp, "STEAM_COMPAT_RUNTIME_SDL2"); + + if (value != NULL) + { + if (g_strcmp0 (value, "1") == 0) + ret |= SRT_STEAM_COMPAT_FLAGS_RUNTIME_SDL2; + else if (g_strcmp0 (value, "sdl2-compat") == 0) + ret |= SRT_STEAM_COMPAT_FLAGS_RUNTIME_SDL2_COMPAT; + else if (value[0] == '\0' || g_strcmp0 (value, "0") == 0) + ret &= ~(SRT_STEAM_COMPAT_FLAGS_RUNTIME_SDL2 + | SRT_STEAM_COMPAT_FLAGS_RUNTIME_SDL2_COMPAT); + else + g_warning ("Unrecognised value \"%s\" for $STEAM_COMPAT_RUNTIME_SDL2", + value); + } + value = _srt_environ_getenv (envp, "STEAM_COMPAT_FLAGS"); if (value != NULL) diff --git a/tests/pressure-vessel/adverb-sdl.c b/tests/pressure-vessel/adverb-sdl.c index 207f3c4b8..3cbdc5a7e 100644 --- a/tests/pressure-vessel/adverb-sdl.c +++ b/tests/pressure-vessel/adverb-sdl.c @@ -14,6 +14,7 @@ #define SDL_DYNAMIC_API "SDL_DYNAMIC_API" #define SDL2_SONAME "libSDL2-2.0.so.0" +#define SDL2_COMPAT "sdl2-compat/libSDL2-2.0.so.0" #define SDL3_DYNAMIC_API "SDL3_DYNAMIC_API" #define SDL3_SONAME "libSDL3.so.0" @@ -167,6 +168,8 @@ static void test_basic (Fixture *f, gconstpointer context) { + g_autofree gchar *sdl2_compat_target = NULL; + g_autofree gchar *sdl2_symlink = NULL; g_autofree gchar *sdl2_target = NULL; g_autofree gchar *sdl3_target = NULL; @@ -181,6 +184,9 @@ test_basic (Fixture *f, touch (&sdl2_target, f->mock_prefix.path, "lib", pv_multiarch_tuples[PV_PRIMARY_ARCHITECTURE], SDL2_SONAME, NULL); + touch (&sdl2_compat_target, + f->mock_prefix.path, "lib", pv_multiarch_tuples[PV_PRIMARY_ARCHITECTURE], + SDL2_COMPAT, NULL); touch (&sdl3_target, f->mock_prefix.path, "lib", pv_multiarch_tuples[PV_PRIMARY_ARCHITECTURE], SDL3_SONAME, NULL); @@ -226,6 +232,22 @@ test_basic (Fixture *f, f->lib_temp_dirs->libdl_token_path, SDL2_SONAME); assert_env (f->bwrap->envp, SDL3_DYNAMIC_API, f->lib_temp_dirs->libdl_token_path, SDL3_SONAME); + + flatpak_bwrap_unset_env (f->bwrap, SDL_DYNAMIC_API); + sdl2_symlink = g_build_filename (f->lib_temp_dirs->abi_paths[PV_PRIMARY_ARCHITECTURE], + SDL2_SONAME, NULL); + unlink (sdl2_symlink); + + g_test_message ("sdl2-compat flag sets up sdl2-compat..."); + pv_adverb_set_up_dynamic_sdls (f->bwrap, + f->lib_temp_dirs, + f->mock_prefix.path, + f->mock_overrides.path, + SRT_STEAM_COMPAT_FLAGS_RUNTIME_SDL2_COMPAT); + assert_symlink (f->lib_temp_dirs->abi_paths[PV_PRIMARY_ARCHITECTURE], + SDL2_SONAME, sdl2_compat_target, 0); + assert_env (f->bwrap->envp, SDL_DYNAMIC_API, + f->lib_temp_dirs->libdl_token_path, SDL2_SONAME); } static void diff --git a/tests/utils.c b/tests/utils.c index 8fbb2e913..aa51c6602 100644 --- a/tests/utils.c +++ b/tests/utils.c @@ -174,6 +174,10 @@ test_compat_flags (Fixture *f, { { "STEAM_COMPAT_TRACING=", NULL }, SRT_STEAM_COMPAT_FLAGS_NONE }, { { "STEAM_COMPAT_TRACING=0", NULL }, SRT_STEAM_COMPAT_FLAGS_NONE }, { { "STEAM_COMPAT_RUNTIME_SDL2=1", NULL }, SRT_STEAM_COMPAT_FLAGS_RUNTIME_SDL2 }, + { + { "STEAM_COMPAT_RUNTIME_SDL2=sdl2-compat", NULL }, + SRT_STEAM_COMPAT_FLAGS_RUNTIME_SDL2_COMPAT, + }, { { "STEAM_COMPAT_RUNTIME_SDL3=1", NULL }, SRT_STEAM_COMPAT_FLAGS_RUNTIME_SDL3 }, { { NULL }, SRT_STEAM_COMPAT_FLAGS_NONE } }; -- GitLab