Skip to content

Commit f7dc8b6

Browse files
committed
Set additional Proton env vars
Set various environment variables automatically to ensure DXVK, vkd3d-proton, nvapi and GStreamer are properly configured. This should improve compatibility in scenarios in which an application launched through Steam worked properly, but didn't work when launched through Protontricks. The environment variables are set fairly conservatively: env var is only set if the Proton installation appears to support it and if the user hasn't already set it themselves. The environment variables are usually set by the `proton` launcher script provided by Proton installations and used by Steam. However, it can't be used for a variety of reasons: * The command-line interface is not stable and can change from version to version * While written in Python, it isn't suitable for programmatic usage due to relying on global variables
1 parent caeeae7 commit f7dc8b6

4 files changed

Lines changed: 207 additions & 28 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77
## [1.12.1] - 2025-03-08
8+
### Added
9+
- Additional Proton related environment variables are set by Protontricks when applicable, improving compatibility
10+
811
### Fixed
912
- Fix missing app icons for games installed using newer Steam client
1013
- Fix spurious "unknown file arch" Winetricks warnings (newer Winetricks required)

src/protontricks/data/scripts/wine_launch.sh

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,6 @@ WINESERVER_ENV_VARS_TO_COPY=(
4242
WINEESYNC WINEFSYNC
4343
)
4444

45-
PROTON_DEFAULT_DLL_OVERRIDES=(
46-
beclient beclient_x64
47-
dxgi d3d9 d3d10core d3d11 d3d12 d3d12core
48-
nvapi nvapi64 nvofapi64 nvcuda
49-
)
50-
5145
if [[ -n "$PROTONTRICKS_BACKGROUND_WINESERVER"
5246
&& "$0" = "@@script_path@@"
5347
]]; then
@@ -143,28 +137,6 @@ if [[ -z "$PROTONTRICKS_FIRST_START" ]]; then
143137
fi
144138
fi
145139

146-
dll_overrides="${WINEDLLOVERRIDES:-}"
147-
148-
# Set default Proton Wine DLL overrides while ignoring any custom overrides
149-
# the user might have set
150-
for override_name in "${PROTON_DEFAULT_DLL_OVERRIDES[@]}"; do
151-
already_set=0
152-
if [[ "$dll_overrides" =~ ^"${override_name}"=|\;"${override_name}"= ]]; then
153-
already_set=1
154-
fi
155-
156-
if [[ "$already_set" = "0" ]]; then
157-
dll_overrides="${dll_overrides};${override_name}=n"
158-
fi
159-
160-
# Strip ';' from beginning
161-
if [[ "$dll_overrides" =~ ^\; ]]; then
162-
dll_overrides=${dll_overrides:1}
163-
fi
164-
done
165-
166-
export WINEDLLOVERRIDES="${dll_overrides}"
167-
168140
export PROTONTRICKS_FIRST_START=1
169141
fi
170142

src/protontricks/util.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import itertools
12
import locale
23
import logging
34
import os
@@ -308,6 +309,109 @@ def _get_fixed_locale_env():
308309
return fixed_env
309310

310311

312+
DLL_OVERRIDES = {
313+
"beclient": "b,n",
314+
"beclient_x64": "b,n",
315+
316+
# DXVK
317+
"dxgi": "n",
318+
"d3d9": "n",
319+
"d3d10core": "n",
320+
"d3d11": "n",
321+
322+
# vkd3d-proton
323+
"d3d12": "n",
324+
"d3d12core": "n",
325+
326+
# nvapi
327+
"nvapi": "n",
328+
"nvapi64": "n",
329+
"nvofapi64": "n",
330+
"nvcuda": "b"
331+
}
332+
333+
WINE_DEFAULT_ENV_VARS = {
334+
"WINE_LARGE_ADDRESS_AWARE": "1",
335+
"DXVK_ENABLE_NVAPI": "1",
336+
}
337+
338+
def _get_proton_env(proton_app, steam_app, orig_env):
339+
"""
340+
Return a dict of various Proton related environment variables.
341+
These are mainly required to configure DXVK, vkd3d-proton and GStreamer
342+
properly.
343+
344+
When using Proton proper, these are usually set by the `proton` launcher
345+
script. The script is not designed for programmatic usage, so we'll have to
346+
set them manually instead.
347+
348+
Some environment variables are not supported depending on the Python
349+
version. We will err on the side of caution and check if preconditions are
350+
met before attempting to set any environment variables; for example, Proton
351+
installation must contain GStreamer directory in order to set GStreamer env
352+
vars.
353+
"""
354+
new_env = {}
355+
356+
dist_path = proton_app.proton_dist_path
357+
358+
# 1.Configure WINEDLLOVERRIDES
359+
available_dlls = set([
360+
file_.stem for file_
361+
in itertools.chain(
362+
(dist_path / "lib64").glob("**/*.dll"),
363+
(dist_path / "lib").glob("**/*.dll")
364+
)
365+
])
366+
367+
dll_overrides = {
368+
value.split("=")[0]: value.split("=")[1]
369+
for value in orig_env.get("WINEDLLOVERRIDES", "").split(";")
370+
if "=" in value
371+
}
372+
373+
logger.debug(
374+
"Following Wine DLL overrides already set: %s",
375+
dll_overrides
376+
)
377+
378+
# Set DLL overrides if the corresponding DLL file exists *and* if the user
379+
# hasn't declared something else
380+
for name, setting in DLL_OVERRIDES.items():
381+
is_available = name in available_dlls
382+
is_set = name in dll_overrides
383+
384+
if is_available and not is_set:
385+
logger.debug("Setting Wine DLL override: %s=%s", name, setting)
386+
dll_overrides[name] = setting
387+
388+
new_env["WINEDLLOVERRIDES"] = ";".join(
389+
f"{name}={setting}" for name, setting in dll_overrides.items()
390+
)
391+
392+
# 2. Configure GStreamer
393+
is_gstreamer_available = (dist_path / "lib/gstreamer-1.0").is_dir()
394+
395+
if is_gstreamer_available:
396+
logger.debug("Setting GStreamer environment variables")
397+
398+
new_env["GST_PLUGIN_SYSTEM_PATH_1_0"] = (
399+
f"{dist_path / "lib64/gstreamer-1.0"}"
400+
f":{dist_path / "lib/gstreamer-1.0"}"
401+
)
402+
new_env["WINE_GST_REGISTRY_DIR"] = str(
403+
steam_app.prefix_path.parent / "gstreamer-1.0"
404+
)
405+
406+
# 3. Enable various env vars unless already set by user
407+
for name, value in WINE_DEFAULT_ENV_VARS.items():
408+
if name not in orig_env:
409+
logger.debug("Setting default env var: %s=%s", name, value)
410+
new_env[name] = value
411+
412+
return new_env
413+
414+
311415
def _start_process(args, wait=False, **kwargs):
312416
"""Start a new process and return a Popen instance
313417
"""
@@ -396,6 +500,14 @@ def run_command(
396500
# Fix the locale for Steam Deck, if necessary
397501
wine_environ.update(_get_fixed_locale_env())
398502

503+
# Set various Proton related environment variables
504+
wine_environ.update(
505+
_get_proton_env(
506+
proton_app=proton_app, steam_app=steam_app,
507+
orig_env=wine_environ
508+
)
509+
)
510+
399511
wine_bin_dir = None
400512
wine_environ["PROTONTRICKS_STEAM_RUNTIME"] = "off"
401513
if use_steam_runtime:

tests/test_util.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,98 @@ def test_locale_fixed_on_steam_deck(
210210
assert command.env["LC_TIME"] == "en_US.UTF-8"
211211
assert command.env["LC_NUMERIC"] == "en_US.UTF-8"
212212

213+
def test_winedlloverrides_defaults_are_set(
214+
self, steam_app_factory, default_proton, command_mock, caplog):
215+
"""
216+
Test that Protontricks will automatically set WINEDLLOVERRIDES
217+
while skipping any DLLs that user has already configured
218+
"""
219+
dxvk_lib_path = \
220+
default_proton.proton_dist_path / "lib" / "wine" / "dxvk"
221+
dxvk_lib_path.mkdir(parents=True)
222+
223+
(dxvk_lib_path / "dxgi.dll").touch()
224+
(dxvk_lib_path / "d3d9.dll").touch()
225+
(dxvk_lib_path / "d3d11.dll").touch()
226+
227+
steam_app = steam_app_factory(name="Fake game", appid=10)
228+
run_command(
229+
winetricks_path=Path("/usr/bin/winetricks"),
230+
proton_app=default_proton,
231+
steam_app=steam_app,
232+
command=["/bin/env"],
233+
env={
234+
"WINEDLLOVERRIDES": "fakelibrary=b;dxgi=b"
235+
}
236+
)
237+
238+
command = command_mock.commands[-1]
239+
240+
# User-provided environment variables are not overridden
241+
assert "dxgi=b" in command.env["WINEDLLOVERRIDES"]
242+
assert "fakelibrary=b" in command.env["WINEDLLOVERRIDES"]
243+
244+
# DXVK overrides are set if the corresponding DLL files exist in the
245+
# Proton installation
246+
assert "d3d9=n" in command.env["WINEDLLOVERRIDES"]
247+
assert "d3d11=n" in command.env["WINEDLLOVERRIDES"]
248+
249+
assert "d3d10core" not in command.env["WINEDLLOVERRIDES"]
250+
251+
def test_gstreamer_env_is_set(
252+
self, steam_app_factory, default_proton, command_mock):
253+
"""
254+
Test that Protontricks will automatically set GStreamer related
255+
environment variables if GStreamer appears to be installed for Proton
256+
"""
257+
(default_proton.proton_dist_path / "lib/gstreamer-1.0").mkdir(
258+
parents=True
259+
)
260+
261+
steam_app = steam_app_factory(name="Fake game", appid=10)
262+
263+
run_command(
264+
winetricks_path=Path("/usr/bin/winetricks"),
265+
proton_app=default_proton,
266+
steam_app=steam_app,
267+
command=["/bin/env"],
268+
)
269+
270+
command = command_mock.commands[-1]
271+
272+
assert str(default_proton.proton_dist_path / "lib/gstreamer-1.0") \
273+
in command.env["GST_PLUGIN_SYSTEM_PATH_1_0"]
274+
assert str(steam_app.prefix_path.parent / "gstreamer-1.0") \
275+
in command.env["WINE_GST_REGISTRY_DIR"]
276+
277+
def test_default_proton_env_vars_set(
278+
self, steam_app_factory, default_proton, command_mock):
279+
"""
280+
Test that Protontricks will automatically set various Proton related
281+
environment variables, unless they're already set by the user
282+
"""
283+
steam_app = steam_app_factory(name="Fake game", appid=10)
284+
285+
run_command(
286+
winetricks_path=Path("/usr/bin/winetricks"),
287+
proton_app=default_proton,
288+
steam_app=steam_app,
289+
command=["/bin/env"],
290+
env={
291+
"WINE_LARGE_ADDRESS_AWARE": "2"
292+
}
293+
)
294+
295+
command = command_mock.commands[-1]
296+
297+
# Default env var is set
298+
assert command.env["DXVK_ENABLE_NVAPI"] == "1"
299+
300+
# User-set env var is not overridden
301+
assert command.env["WINE_LARGE_ADDRESS_AWARE"] == "2"
302+
303+
304+
213305
def test_bwrap_launcher_crash_detected(
214306
self, default_new_proton, steam_app_factory, command_mock):
215307
"""

0 commit comments

Comments
 (0)