From f3fdf01c47c0aae2c51a8731e986a51967d8bb7f Mon Sep 17 00:00:00 2001 From: Glenn Van Loon Date: Wed, 28 Jan 2026 22:34:23 +0100 Subject: [PATCH 1/2] Build and install libstrangle --- src/Dockerfile | 40 ++++++++++++++++++++++++++++++++++++++++ src/libstrangle.patch | 12 ++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/libstrangle.patch diff --git a/src/Dockerfile b/src/Dockerfile index b4974432..5ee0a2e3 100644 --- a/src/Dockerfile +++ b/src/Dockerfile @@ -18,6 +18,33 @@ WORKDIR /usr/src ADD https://github.com/quietvoid/runfromprocess-rs.git#${RUNFROMPROCESS_VERSION} . RUN cargo build --target x86_64-pc-windows-gnu --release +FROM debian:${DEBIAN_VERSION}-slim AS build-libstrangle + +ARG LIBSTRANGLE_VERSION=0273e318e3b0cc759155db8729ad74266b74cb9b + +# Install prerequisites +# - ca-certificates and git to fetch libstrangle source code +# - build-essential for gcc compiler and make +# - g++-multilib, gcc-multilib, libx11-dev, mesa-common-dev required by libstrangle +RUN DEBIAN_FRONTEND="noninteractive" apt-get update \ + && DEBIAN_FRONTEND="noninteractive" apt-get install --no-install-recommends -y \ + build-essential \ + ca-certificates \ + g++-multilib \ + gcc-multilib \ + git \ + libx11-dev \ + mesa-common-dev \ + && rm -rf /var/lib/apt/lists/* + +# Build libstrangle +# Apply patch to specific commit to fix compilation errors +WORKDIR /usr/libstrangle +ADD https://gitlab.com/torkel104/libstrangle.git#${LIBSTRANGLE_VERSION} . +COPY libstrangle.patch . +RUN git apply libstrangle.patch \ + && make + FROM debian:${DEBIAN_VERSION}-slim AS wine-base # Wine @@ -106,5 +133,18 @@ COPY --chmod=755 update_zwift.sh /bin/update_zwift.sh COPY --chmod=755 run_zwift.sh /bin/run_zwift.sh COPY --chmod=755 zwift-auth.sh /bin/zwift-auth COPY --from=build-runfromprocess /usr/src/target/x86_64-pc-windows-gnu/release/runfromprocess-rs.exe /bin/runfromprocess-rs.exe +COPY --chmod=644 --from=build-libstrangle /usr/libstrangle/build/libstrangle.conf /etc/ld.so.conf.d/libstrangle.conf +COPY --chmod=755 --from=build-libstrangle /usr/libstrangle/build/libstrangle32.so /usr/local/lib/libstrangle/lib32/libstrangle.so +COPY --chmod=755 --from=build-libstrangle /usr/libstrangle/build/libstrangle64.so /usr/local/lib/libstrangle/lib64/libstrangle.so +COPY --chmod=755 --from=build-libstrangle /usr/libstrangle/build/libstrangle32_nodlsym.so /usr/local/lib/libstrangle/lib32/libstrangle_nodlsym.so +COPY --chmod=755 --from=build-libstrangle /usr/libstrangle/build/libstrangle64_nodlsym.so /usr/local/lib/libstrangle/lib64/libstrangle_nodlsym.so +COPY --chmod=755 --from=build-libstrangle /usr/libstrangle/build/libstrangle_vk32.so /usr/local/lib/libstrangle/lib32/libstrangle_vk.so +COPY --chmod=755 --from=build-libstrangle /usr/libstrangle/build/libstrangle_vk64.so /usr/local/lib/libstrangle/lib64/libstrangle_vk.so +COPY --chmod=644 --from=build-libstrangle /usr/libstrangle/src/vulkan/libstrangle_vk.json /usr/local/share/vulkan/implicit_layer.d/libstrangle_vk.json +COPY --chmod=755 --from=build-libstrangle /usr/libstrangle/src/strangle.sh /usr/local/bin/strangle +COPY --chmod=755 --from=build-libstrangle /usr/libstrangle/src/stranglevk.sh /usr/local/bin/stranglevk + +# create symlinks for libstrangle +RUN ldconfig ENTRYPOINT ["entrypoint"] diff --git a/src/libstrangle.patch b/src/libstrangle.patch new file mode 100644 index 00000000..f6f8b0db --- /dev/null +++ b/src/libstrangle.patch @@ -0,0 +1,12 @@ +diff --git i/src/vulkan/overlay.cpp w/src/vulkan/overlay.cpp +index 39c93cd..5441d8c 100644 +--- i/src/vulkan/overlay.cpp ++++ w/src/vulkan/overlay.cpp +@@ -23,6 +23,7 @@ + */ + + #include ++#include + #include + #include + From cf814c063dbe7199f920cc09ec5c952edc2f10fb Mon Sep 17 00:00:00 2001 From: Glenn Van Loon Date: Wed, 28 Jan 2026 22:53:56 +0100 Subject: [PATCH 2/2] Use libstrangle to limit fps --- docs/configuration/options.md | 14 ++++++++++++++ src/Dockerfile | 2 +- src/run_zwift.sh | 22 ++++++++++++++++++---- src/zwift.sh | 6 ++++++ 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index 806a7d0d..604c1106 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -76,6 +76,7 @@ These environment variables can be used to alter the execution of the zwift bash | [`ZWIFT_SCREENSHOTS_DIR`](#zwift_screenshots_dir) | | Set the screenshots directory location | | [`ZWIFT_OVERRIDE_GRAPHICS`](#zwift_override_graphics) | `0` | If set to `1`, override the zwift graphics profiles | | [`ZWIFT_OVERRIDE_RESOLUTION`](#zwift_override_resolution) | | If set, change the game resolution | +| [`ZWIFT_FPS_LIMIT`](#zwift_fps_limit) | | If set, limit the fps to the requested value | | [`ZWIFT_FG`](#zwift_fg) | `0` | If set to `1`, run the container in the foreground | | [`ZWIFT_NO_GAMEMODE`](#zwift_no_gamemode) | `0` | If set to `1`, don't run game mode | | [`WINE_EXPERIMENTAL_WAYLAND`](#wine_experimental_wayland) | `0` | If set to `1`, use native Wayland | @@ -569,6 +570,19 @@ Set this option to a value to change the Zwift game resolution. For details on h --- +### `ZWIFT_FPS_LIMIT` + +If set, limit the Zwift game fps to the requested value. + +| Item | Description | +|:------------------|:-----------------------------| +| Allowed values | number | +| Default value | | +| Commandline usage | `ZWIFT_FPS_LIMIT="60" zwift` | +| Config file usage | `ZWIFT_FPS_LIMIT="60"` | + +--- + ### `ZWIFT_FG` If set to `1`, launch the container in the foreground instead of the background. Use this option if you want to see the output diff --git a/src/Dockerfile b/src/Dockerfile index 5ee0a2e3..231f1897 100644 --- a/src/Dockerfile +++ b/src/Dockerfile @@ -144,7 +144,7 @@ COPY --chmod=644 --from=build-libstrangle /usr/libstrangle/src/vulkan/libstrangl COPY --chmod=755 --from=build-libstrangle /usr/libstrangle/src/strangle.sh /usr/local/bin/strangle COPY --chmod=755 --from=build-libstrangle /usr/libstrangle/src/stranglevk.sh /usr/local/bin/stranglevk -# create symlinks for libstrangle +# Configure libstrangle shared libraries RUN ldconfig ENTRYPOINT ["entrypoint"] diff --git a/src/run_zwift.sh b/src/run_zwift.sh index bf0b794f..270634c3 100755 --- a/src/run_zwift.sh +++ b/src/run_zwift.sh @@ -26,6 +26,7 @@ readonly CONTAINER_TOOL="${CONTAINER_TOOL:?}" readonly ZWIFT_USERNAME="${ZWIFT_USERNAME:-}" readonly ZWIFT_PASSWORD="${ZWIFT_PASSWORD:-}" readonly ZWIFT_OVERRIDE_RESOLUTION="${ZWIFT_OVERRIDE_RESOLUTION:-}" +readonly ZWIFT_FPS_LIMIT="${ZWIFT_FPS_LIMIT:-}" readonly ZWIFT_NO_GAMEMODE="${ZWIFT_NO_GAMEMODE:-0}" readonly WINE_USER_HOME="/home/user/.wine/drive_c/users/user" @@ -168,17 +169,30 @@ fi msgbox ok "Zwift launcher started using wine" msgbox info "Starting Zwift using wine" -declare -a zwift_cmd +declare -a launcher_cmd +launcher_cmd=() +declare -a wine_extra_args +wine_extra_args=() +declare -a wine_cmd +wine_cmd=(/bin/runfromprocess-rs.exe "${launcher_pid}" ZwiftApp.exe "${zwift_args[@]}") if [[ ${ZWIFT_NO_GAMEMODE} -eq 1 ]]; then msgbox info "Not using gamemode" - zwift_cmd=(wine start /exec /bin/runfromprocess-rs.exe "${launcher_pid}" ZwiftApp.exe "${zwift_args[@]}") + wine_extra_args+=(start /exec) else msgbox info "Using gamemode" - zwift_cmd=(/usr/games/gamemoderun wine /bin/runfromprocess-rs.exe "${launcher_pid}" ZwiftApp.exe "${zwift_args[@]}") + launcher_cmd+=(/usr/games/gamemoderun) fi -if ! "${zwift_cmd[@]}" || ! wait_until_wine_task_started ZwiftApp.exe; then +if [[ -n ${ZWIFT_FPS_LIMIT} ]]; then + msgbox info "Limiting Zwift fps to ${ZWIFT_FPS_LIMIT}" + launcher_cmd+=(strangle "${ZWIFT_FPS_LIMIT}") +fi + +declare -a launch_zwift_cmd +launch_zwift_cmd=("${launcher_cmd[@]}" wine "${wine_extra_args[@]}" "${wine_cmd[@]}") + +if ! "${launch_zwift_cmd[@]}" || ! wait_until_wine_task_started ZwiftApp.exe; then msgbox error "Failed to start Zwift using wine!" exit 1 fi diff --git a/src/zwift.sh b/src/zwift.sh index 9b45ab04..361e8bf2 100755 --- a/src/zwift.sh +++ b/src/zwift.sh @@ -174,6 +174,7 @@ readonly ZWIFT_LOG_DIR="${ZWIFT_LOG_DIR:-}" readonly ZWIFT_SCREENSHOTS_DIR="${ZWIFT_SCREENSHOTS_DIR:-}" readonly ZWIFT_OVERRIDE_GRAPHICS="${ZWIFT_OVERRIDE_GRAPHICS:-0}" readonly ZWIFT_OVERRIDE_RESOLUTION="${ZWIFT_OVERRIDE_RESOLUTION:-}" +readonly ZWIFT_FPS_LIMIT="${ZWIFT_FPS_LIMIT:-}" readonly ZWIFT_FG="${ZWIFT_FG:-0}" readonly ZWIFT_NO_GAMEMODE="${ZWIFT_NO_GAMEMODE:-0}" readonly WINE_EXPERIMENTAL_WAYLAND="${WINE_EXPERIMENTAL_WAYLAND:-0}" @@ -469,6 +470,11 @@ if [[ -n ${ZWIFT_OVERRIDE_RESOLUTION} ]]; then container_env_vars+=(ZWIFT_OVERRIDE_RESOLUTION="${ZWIFT_OVERRIDE_RESOLUTION}") fi +# If frame rate limiting is requested, pass environment variables to container +if [[ -n ${ZWIFT_FPS_LIMIT} ]]; then + container_env_vars+=(ZWIFT_FPS_LIMIT="${ZWIFT_FPS_LIMIT}") +fi + # Check if gamemode should be enabled if [[ ${ZWIFT_NO_GAMEMODE} -eq 1 ]]; then container_env_vars+=(ZWIFT_NO_GAMEMODE="1")