Skip to content

Commit 5e988f6

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 d01901e commit 5e988f6

File tree

3 files changed

+207
-0
lines changed

3 files changed

+207
-0
lines changed

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+
- Improve compatibility by setting additional Proton related environment variables when applicable
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/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)