diff --git a/docs/steam-compat-tool-interface.md b/docs/steam-compat-tool-interface.md index 7fb910d69725725b9ca15fb6efe8a1e199dfad23..c7017a06d22f1f9b613049c88c71d4f75b9137f6 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 72474c4b687f7f801f00a588ce952cc8fd72e38d..c0d2c2fc4b24f4840cbbb176b68285353119ed18 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 920d9d09ff6d92adefe0abeeff35f1bedc9692da..1f16614c0fa0446d317b68de776270c0fcae92e7 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 9cdf16149d4001ec065faa9d05e41ef13a9c1373..74877413a4e0c3cf5f3b27ca3617815cd5b4ef3e 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 ff9bd7242922124dadb844d3679410c84db266ba..c0f2a92a0da016b8a8f91900ab42ee1906b2500e 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 207f3c4b877ff37c9d00a225ab2e6210b91ee9ad..3cbdc5a7e8d655702a4b51f2abef6f220746cad4 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 8fbb2e9138b5e9fdd02dfff0c6aca8901fe6d713..aa51c66020a4ad111391d59dfffaea8df6ed33f8 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 } };