TDF - Manual

TDF - Manual

TDF is a small project aiming to make it easy to install, package and safely run Windows games on GNU/Linux. You can think of it as a portable (as in requiring no installation) version of Proton, based on most of the same technology, with better sandboxing.

TDF is based on the following awesome projects:

TDF's main goals are:

Usage

This section explains how to use TDF to install, play and optionally package a game.

Requirements

Basic usage

Configuration variables

The following lists contain all the variables that can be added in vars.conf to configure emulation settings, work around issues, improve performance, etc.

Essential variables (where the game is installed)

game_exe
Specifies the Windows-style path to the game's exe file. If empty, the command prompt will be launched instead.

Example: game_exe='C:\GTAV\PlayGTAV.exe'

game_workingDir
The working directory of the game. By default this is set to the same folder where the game_exe resides. All paths must be Windows-style.

Example:

game_exe='bin\indy.exe'
game_workingDir='C:\Indy'

game_args
Arguments to be passed to the game.

Accepts a string or a bash array.

Examples: ('-iwad' 'doom2.wad') or '-iwad doom2.wad'

SteamGameId
Optional numeric Steam App ID for the game. Identifies a specific game on Steam, enabling game-specific workarounds in the wine-games build (e.g., known configuration tweaks, known fixes). This does NOT require Steam to be installed; it's purely a lookup key for internal game-specific optimizations.

Look up IDs here: https://steamdb.info/

Example: export SteamGameId="1174180"

Sandboxing / Isolation

TDF uses bubblewrap to create a secure sandbox using Linux namespaces. This provides very strong isolation giving games only the bare minimum that they need to work: access to the display, sound, input devices, and the GPU; a potential malware running inside the sandbox won't be able to touch your files, interact with other processes on your computer (or even see them), use the network, use dbus, etc. unless you explicitly allow it.

TDF_ALLOW_HOST_FILESYSTEM
Controls which host filesystem paths are exposed inside the sandbox.

Possible values:

TDF_CUSTOM_MOUNTS
Additional custom mounts in the format "letter:access:hostPath" or "letter".

Example: TDF_CUSTOM_MOUNTS=("D:ro:$HOME/Desktop" "E:rw:/media/games" "F")

TDF_BLOCK_NETWORK
Network access blocking method.

Possible values:

Note that unblocking networking will also allow the sandboxed app to communicate with some services on your computer that use sockets (like CUPS).

TDF_BLOCK_BROWSER
Block Wine from launching the system's native web browser.

Possible values:

TDF_BLOCK_SHM
Block access to /dev/shm (shared memory).

Possible values:

Note: Setting TDF_WINE_SYNC="fsync" requires TDF_BLOCK_SHM=0. Keep in mind that fsync is an obsolete Wine feature and not blocking shared memory is a security concern.

TDF_BLOCK_NATIVE_INPUT
Block direct access to native input devices (/dev/input) for better security.

Possible values:

Note: regardless of this setting, unless udev is misconfigured on your system, this won't allow games to log keypresses to other applications.

Wine

TDF_WINE_PREFERRED_VERSION
TDF comes with 2 different versions of Wine and can also use the one on your system (if installed). This variable lets you choose which one you prefer.

Possible values:

TDF_WINE_ARCH
The architecture of the Wine installation. Can only be set before the initialization is performed, and can't be changed afterwards without deleting the wineprefix.

Possible values:

TDF_WINEMONO
Whether to install Wine Mono in the prefix or not. Mostly useful for launchers and applications based on .NET, games don't usually need this.

Possible values:

TDF_WINEGECKO
Whether to install Wine Gecko in the prefix or not. This provides the equivalent of a Webview, mostly useful for launchers and applications based on IE, games don't usually need this.

Possible values:

TDF_VCREDIST
Whether to install the official Microsoft Visual C++ Redistributables (2015+) or not. This is useful for modern games but unnecessary for older ones.

Possible values:

export WINEDLLOVERRIDES
Some game fixes and mods come in the form of DLLs that override one of Windows' DLL, usually winmm, dinput8, version, d3d9, etc.
Unlike Windows, which happily loads random DLLs from the game's folder, by default Wine prefers to use its own DLLs and overrides need to be specified manually.

This is not a TDF variable, but it's part of Wine. More about this here.

Example:

#load winmm.dll and dinput8 from the game's folder (if available)
export WINEDLLOVERRIDES="winmm,dinput8=n,b"

This can also be used to fix games that complain about outdated drivers on AMD cards or that don't detect the correct amount of VRAM, since they often obtain this information through a DLL called amd_ags_x64.dll:

Example:

#use wine's own fake amd_ags_x64 instead of the game's version
export WINEDLLOVERRIDES="amd_ags_x64=b"

Multiple overrides can be separated by a semicolon ;.

export WINE_PREFER_NATIVE_DLL
Sets the default DLL load order for DLLs that are not being overridden with WINEDLLOVERRIDES or registry entries.

By default, Wine uses the b,n load order for DLLs unless overridden, which may cause issues with some games and mods that override DLLs like winmm, dinput8, version, etc.; if this setting is enabled, it will change the default to n,b, meaning that Wine will try to load DLLs provided by the game first and only use its own as a fallback.

This improves compatibility with mods and cracks, but can cause issues such as outdated driver warnings or straight up crashes, so it is disabled by default.

Possible values:

Notes:

export WINEDEBUG
Enables/disables some Wine debug channels.

By default, TDF sets this to -all to improve performance, but you might want to enable one or more of these for troubleshooting.

Don't add +relay to this variable, as it's controlled by the TDF_WINE_DEBUG_RELAY variable.

Example:

#log loaded DLLs to the terminal and show the pid of the process that generated each message
export WINEDEBUG=+loaddll,+pid

export DRI_PRIME
Sometimes on systems with multiple GPUs, a game might start using the wrong GPU, such as the integrated graphics on your laptop instead of the dedicated card.

By setting a value for DRI_PRIME you can tell the game which graphics card to use.

Example:

#use the second GPU
export DRI_PRIME=1

This is not a TDF variable and you can find more about it here.

TDF_WINE_HIDE_CRASHES
When a Wine application crashes, it normally shows a dialog similar to the "Stopped working" thing on Windows, but depending on the game and configuration, it may be impossible to interact with that window, leaving you stuck. By default, TDF disables this crash window, but it can be enabled for debugging and troubleshooting purposes.

Possible values:

TDF_WINE_AUDIO_DRIVER
Sets the preferred audio driver for Wine. This can be useful if you have crackling audio or if one of the drivers has a lower latency than the others. Default is usually fine.

Possible values:

Note: Choosing a driver that doesn't exist in Wine or is not installed in your system will result in no sound being played.

Note: Wine doesn't natively support PipeWire yet, it uses PulseAudio by default for compatibility if you're using PipeWire.

TDF_WINE_GRAPHICS_DRIVER
Sets the preferred graphics driver for Wine (as in how it outputs, not how it renders 3D graphics). This can be useful if you're messing around with Wayland and X11 and Wine doesn't work properly.

Possible values:

Note: Choosing a driver that doesn't exist in Wine or in your system will result in no graphics being displayed.

Note: This setting is automatically forced to wayland when TDF_HDR is set to 1.

TDF_COREFONTS
Whether to install the Microsoft Corefonts or not. These are fonts like Arial, Comic Sans, etc. that are required by some games such as PC Building Simulator. This is generally harmless, but if some application has font rendering issues, try disabling it.

Possible values:

TDF_WINE_WINVER
Sets the Windows version to emulate.

Possible values:

Note: If using an older Windows version, TDF_WINE_ARCH should also be set accordingly. It usually doesn't break anything, but applications may not expect to see 64-bit versions of legacy systems.

TDF_WINE_THEME
Sets the Wine theme.

Possible values:

Note: It's possible to install msstyles themes, in this case, leave this empty and install them through winecfg.

TDF_WINE_LANGUAGE
By default, TDF will pass the system language to Wine, which may be undesirable for some games and applications that just use the system language instead of showing a language selector. Here's a complete list of locales, obviously not all games will support them.

Example: TDF_WINE_LANGUAGE='it_IT.utf-8'

Limiting CPU cores (and setting CPU topology in general)

As CPUs get more and more cores and threads, problems such as crashes, inconsistent performance and general instability can occur in older games. For this reason, TDF implements several ways to limit which cores can be used.

Note: these settings apply to the game-optimized build only

Before we get into the settings, some terminology:

If a game doesn't support a high number of cores, TDF can limit the number of logical CPUs assigned to it and choose the best ones to maximize performance.

Note: These limits affect the performance of everything running inside Wine, including DXVK and VKD3D. Use them only if absolutely necessary.

Note: if WINE_CPU_TOPOLOGY is set, these settings will have no effect.

TDF_WINE_MAXLOGICALCPUS
The maximum number of logical CPUs that can be assigned to this game.

Possible values:

If the number of logical CPUs exceeds this value, they are assigned intelligently in this way:

Generally speaking, this is the only limit you should set. Let TDF do the rest for you.

Example 1: We're on an Intel Core i7 12900K (8 P-cores with 2 threads each, 8 E-cores with 1 thread each, 24 threads in total), the CPU topology is the following (obtained with lscpu --all --extended):

CPU NODE SOCKET CORE L1d:L1i:L2:L3 ONLINE    MAXMHZ   MINMHZ
  0    0      0    0 0:0:0:0          yes 6700.0000 800.0000
  1    0      0    0 0:0:0:0          yes 6700.0000 800.0000
  2    0      0    1 1:1:1:0          yes 6700.0000 800.0000
  3    0      0    1 1:1:1:0          yes 6700.0000 800.0000
  4    0      0    2 2:2:2:0          yes 6500.0000 800.0000
  5    0      0    2 2:2:2:0          yes 6500.0000 800.0000
  6    0      0    3 3:3:3:0          yes 6500.0000 800.0000
  7    0      0    3 3:3:3:0          yes 6500.0000 800.0000
  8    0      0    4 4:4:4:0          yes 6500.0000 800.0000
  9    0      0    4 4:4:4:0          yes 6500.0000 800.0000
 10    0      0    5 5:5:5:0          yes 6500.0000 800.0000
 11    0      0    5 5:5:5:0          yes 6500.0000 800.0000
 12    0      0    6 6:6:6:0          yes 6500.0000 800.0000
 13    0      0    6 6:6:6:0          yes 6500.0000 800.0000
 14    0      0    7 7:7:7:0          yes 6500.0000 800.0000
 15    0      0    7 7:7:7:0          yes 6500.0000 800.0000
 16    0      0    8 8:8:8:0          yes 3900.0000 800.0000
 17    0      0    9 9:9:8:0          yes 3900.0000 800.0000
 18    0      0   10 10:10:8:0        yes 3900.0000 800.0000
 19    0      0   11 11:11:8:0        yes 3900.0000 800.0000
 20    0      0   12 12:12:9:0        yes 3900.0000 800.0000
 21    0      0   13 13:13:9:0        yes 3900.0000 800.0000
 22    0      0   14 14:14:9:0        yes 3900.0000 800.0000
 23    0      0   15 15:15:9:0        yes 3900.0000 800.0000

If you want to play Colin McRae Dirt (2007), a game that supports 4 cores at most, you'll have to set TDF_WINE_MAXLOGICALCPUS=4, and with this CPU TDF will select logical CPUs 0,2,4,6, because they are the fastest cores available, one thread per core.

If you want to play Lara Croft and the Guardian of Light (2010), a game that supports 12 cores at most, you'll have to set TDF_WINE_MAXLOGICALCPUS=12, and with this CPU TDF will select logical CPUs 0,2,4,6,8,10,12,14,16,17,18,19. The first 8 are the P-cores, one thread per core, the last 4 are E-cores, one thread per core.

If you want to play The Witcher 2 (2010), a game that supports 31 cores at most, you'll have to set TDF_WINE_MAXLOGICALCPUS=31, and with this CPU TDF will not apply any special restriction because it only has 24 logical CPUs.

Example 2: We're on an AMD Ryzen 7 5800X (8 cores with 2 threads each, 16 threads in total), the CPU topology is the following:

CPU NODE SOCKET CORE L1d:L1i:L2:L3 ONLINE    MAXMHZ    MINMHZ
  0    0      0    0 0:0:0:0          yes 4850.1948 2200.0000
  1    0      0    1 1:1:1:0          yes 4850.1948 2200.0000
  2    0      0    2 2:2:2:0          yes 4850.1948 2200.0000
  3    0      0    3 3:3:3:0          yes 4850.1948 2200.0000
  4    0      0    4 4:4:4:0          yes 4850.1948 2200.0000
  5    0      0    5 5:5:5:0          yes 4850.1948 2200.0000
  6    0      0    6 6:6:6:0          yes 4850.1948 2200.0000
  7    0      0    7 7:7:7:0          yes 4850.1948 2200.0000
  8    0      0    0 0:0:0:0          yes 4850.1948 2200.0000
  9    0      0    1 1:1:1:0          yes 4850.1948 2200.0000
 10    0      0    2 2:2:2:0          yes 4850.1948 2200.0000
 11    0      0    3 3:3:3:0          yes 4850.1948 2200.0000
 12    0      0    4 4:4:4:0          yes 4850.1948 2200.0000
 13    0      0    5 5:5:5:0          yes 4850.1948 2200.0000
 14    0      0    6 6:6:6:0          yes 4850.1948 2200.0000
 15    0      0    7 7:7:7:0          yes 4850.1948 2200.0000

(Notice the different interleaving in the CORE column compared to the previous example).

If you want to play Colin McRae Dirt (2007), a game that supports 4 cores at most, you'll have to set TDF_WINE_MAXLOGICALCPUS=4, and with this CPU TDF will select logical CPUs 0,1,2,3, which are simply the first 4 logical CPUs, one per core.

If you want to play Lara Croft and the Guardian of Light (2010), a game that supports 12 cores at most, you'll have to set TDF_WINE_MAXLOGICALCPUS=12, and with this CPU TDF will select logical CPUs 0,1,2,3,4,5,6,7,8,9,10,11. The first 8 are the first logical CPU of each core, one thread per core, the last 4 are the second logical CPU of the first 4 cores.

If you want to play The Witcher 2 (2010), a game that supports 31 cores at most, you'll have to set TDF_WINE_MAXLOGICALCPUS=31, and with this CPU TDF will not apply any special restriction because it only has 16 logical CPUs.

TDF_WINE_NOSMT
Whether to hide the additional logical CPUs on CPUs that support HyperThreading/SMT.

Possible values:

TDF_WINE_NOECORES
Whether to hide E-cores on CPUs like Intel Alder Lake.

Possible values:

Note: for compatibility reasons, these CPUs have no easy way to tell which cores are P-cores and which ones are E-cores, so TDF "guesses" that the E-cores are the ones with a maximum clock that's <75% of that of any other core. This may be improved in the future.

TDF_WINE_PREFER_SAMESOCKET
How to use multiple CPUs.

Possible values:

export WINE_CPU_TOPOLOGY
This is not a TDF variable, but it allows you to set Wine to use specific CPU cores, similar to the /AFFINITY option of the start command in Windows, but more fine grained. This should be used as a last resort or if you want to do dumb things like restrict a game to only use E-cores.

Example:

#limits wine to one CPU core
export WINE_CPU_TOPOLOGY="1:0"

Example:

#limits wine to use the first 4 CPU cores
export WINE_CPU_TOPOLOGY="4:0,1,2,3"

Note: if WINE_CPU_TOPOLOGY is set, the settings above will have no effect.

TDF_WINE_DPI
DPI value for display scaling of Wine applications.

Possible values:

TDF_WINE_SYNC
The synchronization method to be used by Wine (game-optimized build only).

Possible values:

Note: Using fsync requires TDF_BLOCK_SHM=0, which reduces sandbox security.

TDF_WINE_LAA
Enable Large Address Aware (LAA) support for 32-bit applications. Allows 32-bit games to address more than 2GB of memory.

Possible values:

TDF_WINE_HEAP_DELAY_FREE
Enable heap delay-free optimization. Delays freeing heap memory for better compatibility with games with use-after-free bugs and reduces overhead from system calls.

Possible values:

TDF_WINE_SMOKETEST
Whether or not to perform a "smoke test" to make sure that Wine actually works before trying to run the game, that way you can tell if a crash is a Wine problem or a game problem. TDF does this by default but you can disable it if it takes too long at the "Starting Wine" screen.

Possible values:

TDF_WINE_DEBUG_RELAY
Enables the Wine relay feature, which traces all interaction between the application and the rest of the system to a file. Extremely slow but can be useful to debug weird issues and crashes.

Possible values:

TDF_WINE_DEBUG_GSTREAMER
Enables gstreamer debug output (to the terminal). This can be used to debug issues like games not playing videos or crashing when a video is supposed to play.

Possible values:

Graphics

TDF_DXVK
Whether to install DXVK or not, which provides Direct3D 8/9/10/11 to Vulkan translation and DXGI implementation. If this is disabled, WineD3D will be used instead, which may be better for some older games. If VKD3D or D7VK is enabled, DXVK is auto-enabled as a dependency.

Settings for DXVK can be changed by downloading the default configuration file, placing it into the game's folder and editing it.

Possible values:

TDF_DXVK_NVAPI
Enable dxvk-nvapi (Nvidia API layer for DXVK). Provides NVAPI support and some performance optimizations on NVIDIA cards. Requires DXVK.

Possible values:

TDF_VKD3D
Whether to install VKD3D-Proton, which provides Direct3D 12 to Vulkan translation. If this is disabled, Wine's version of VKD3D is used instead, which has very poor game compatibility compared to this version. Requires DXVK.

Possible values:

VKD3D's config can be changed by using its environment variables.

TDF_D7VK
Whether to install D7VK or not, which provides DirectX 3-7 to Vulkan translation. If this is disabled, WineD3D will be used instead. Requires DXVK.

Settings for D7VK can be changed by downloading the default configuration file, placing it into the game's folder and editing it.

Possible values:

TDF_HDR
Whether to expose HDR support to the application or not. HDR must be enabled in the system settings for this to work and an HDR compatible display is required. This setting has no effect if HDR is disabled or unsupported.

Possible values:

Note: HDR only works on Wayland, enabling this setting will force Wine to use Wayland, which may break some games.

TDF_GL_MAXFPS
Enables an FPS limiter for OpenGL applications, based on libstrangle.

Possible values:

export FSR4_UPGRADE
Enable AMD FSR4 on supported GPUs and games. This installs a proprietary AMD DLL into the sandbox.

Possible values:

Desktop integration

TDF_DND
Enable Do Not Disturb mode while the game is running. Works on supported desktop environments (GNOME, KDE, etc.).

Possible values:

TDF_NOSLEEP
Inhibit system sleep while the game is running. Works on supported desktop environments (GNOME, KDE, etc.).

Possible values:

Overlays and other tools

TDF_MANGOHUD
Whether to launch the game with the MangoHud performance overlay or not. If MangoHud is not installed in the system, it has no effect. Note that some games will crash when launched with MangoHud.

Possible values:

Note: If MangoHud is enabled alongside Gamescope, MangoHud is automatically disabled and Gamescope's built-in mangoapp is used instead.

TDF_GAMESCOPE
Whether to enable Gamescope when running the game or not. This is generally not recommended when using the game-optimized version of Wine, since it integrates the fshack patches which make it mostly useless, but it can be useful when using the mainline version for games that change the screen resolution often, require low resolutions, integers scaling, etc. such as KOTOR or WinQuake. If Gamescope is not installed in the system, it has no effect.

Possible values:

TDF_GAMESCOPE_PARAMETERS
The command line arguments used to start Gamescope. You can see a complete list here. Can be a string or a bash array.

By default, TDF sets this variable to -f -r 60 -w $XRES -h $YRES, where XRES and YRES are two read-only variables provided by TDF for convenience that contain the horizontal and vertical resolution of the main display. This default value emulates a virtual screen with the same resolution as the real display, with a refresh rate of 60hz and sets Gamescope to run in fullscreen without any special scaling.

TDF UI settings

TDF_TITLE
The title to show on the title bar of the TDF windows. By default it's set to "Launcher".

TDF_UI_LANGUAGE
The language to use for the TDF user interface. Does not affect Wine or games (see TDF_WINE_LANGUAGE for that).

By default, TDF tries to obtain the language from the OS. If a translation is not available, it will fall back to English.

Currently implemented languages:

TDF_HIDE_GAME_RUNNING_DIALOG
Whether to hide the TDF window that says "Game running". By default, TDF shows it so you can stop the game at any time.

Possible values:

TDF_IGNORE_EXIST_CHECKS
By default, TDF checks whether the executable specified in game_exe actually exists before trying to launch it, but this is not always desirable and can be disabled, which can be useful to run certain commands.

Possible values:

TDF_SHOW_PLAY_TIME
Show playtime after the game closes.

Possible values:

Callbacks

You can optionally define the following functions inside vars.conf and they will be called at specific moments during operation. This can be useful to fix games that have issues with window positioning, focusing, etc. or that have some special requirements. The language is just bash.

NEVER CALL WINE FROM THESE SCRIPTS

If you need to define some variables, do it inside the callback functions, as the configuration is loaded more than once during the initialization process.

Note: callbacks run inside a subshell outside the sandbox; therefore they cannot:

customChecks
This function will be called immediately after the configuration is loaded. It's useful to run some custom checks specific to the game or to change some settings depending on hardware/software configuration. The function returns 0 if the checks succeed, 1 to indicate that they failed and stop TDF. If the function has no return, it will be treated as a success.

Example:

customChecks(){
    if [ "$XDG_SESSION_TYPE" == "x11" ]; then
        return 0
    else
        zenity --error --text="Sorry, this game requires X11"
        return 1
    fi
}

Note that this function is blocking and TDF won't continue the initialization until it has finished running.

onGameStart
This function will be called right before the game is launched.

Example:

onGameStart(){
    echo "Pre-launch: copying mods..."
    cp -r mods/* "data/wineprefix/drive_c/Game/"
}

Note that this function is blocking and the game won't be launched until it has finished running.

onGameEnd
This function will be called when the game's main process finishes running.

Example:

onGameEnd(){
    echo "Post-game: cleaning up temp files..."
    rm -rf "data/home/wine/.cache"
}

Note that this function is blocking and TDF won't close until it has finished running.

whileGameRunning
This function will be called while the game is actively running. It runs in a background subshell and is automatically killed when the game exits. It can run indefinitely or perform periodic tasks.

Example:

whileGameRunning(){
    while true; do
        echo "Game is running..."
        sleep 30
    done
}

Note that the game process may not have been created yet when this function is called, make sure to check the game's process using isProcessRunning

onArchiveStart
This function will be called right before the packaging process begins when using ./run.sh archive (mentioned later). It can be used to remove or move some unnecessary files like DXVK/VKD3D caches, saved games, etc. The function receives the same arguments passed to run.sh and can return 1 to prevent the packaging process from starting if something's wrong.

Example:

onArchiveStart(){
    TEMPDIR="/tmp/requiem$RANDOM"
    mkdir "$TEMPDIR"
    mv "data/wineprefix/drive_c/APTRequiem/vkd3d-proton.cache" "$TEMPDIR"
    mv "data/wineprefix/drive_c/users/wine/AppData/Local/GOG.com" "$TEMPDIR"
}

Note that this function is blocking and the packaging process won't start until it has finished running.

onArchiveEnd
This function will be called at the end of the packaging process when using ./run.sh archive (mentioned later). It can be used to restore files modified or deleted by onArchiveStart. This function receives 0 in input if the packaging process has succeeded, 1 otherwise.
Example:

onArchiveEnd(){
    mv "$TEMPDIR/vkd3d-proton.cache" "data/wineprefix/drive_c/APTRequiem/"
    mv "$TEMPDIR/GOG.com" "data/wineprefix/drive_c/users/wine/AppData/Local/"
    rm -rf "$TEMPDIR"
}

Note that this function is blocking and TDF won't close until it has finished running.

Builtin functions

For convenience, TDF comes with some functions that can be used inside the callback functions mentioned before, in addition to everything provided by bash such as sleep, grep, etc..

waitForWindow exe title [timeout]
Scans windows to find one that matches the specified exe and contains title in the name. If no matching window is found, it will keep trying for timeout seconds (30 if not specified). If exe is an empty string, it will match any process with the specified title, if title is an empty string, it will match any window from the specified process.

The window IDs will be copied to a WINDOWS array, with the first matching window easily accessible with a WINDOW variable.

The function returns 0 if a match was found, 1 if the timeout was reached.

Examples:

Note: this function uses xdotool internally and may not work properly on Wayland, depending on how good the X11 emulation in your display manager is (works on major ones).

focusWindow id
Makes the specified window active and focused, useful to fix games where the taskbar is visible and you have to alt-tab to get rid of it.

The id of the window can be obtained with the waitForWindow function.

Returns 0 if the operation succeeded, 1 if there's no window with the specified id.

Note: this function uses xdotool internally and may not work properly on Wayland, depending on how good the X11 emulation in your display manager is (works on major ones).

maximizeWindow id
Makes the specified window maximized.

The id of the window can be obtained with the waitForWindow function.

Note: this function uses xdotool internally and may not work properly on Wayland, depending on how good the X11 emulation in your display manager is (works on major ones).

restoreWindow id
Makes the specified window not maximized.

The id of the window can be obtained with the waitForWindow function.

Note: this function uses xdotool internally and may not work properly on Wayland, depending on how good the X11 emulation in your display manager is (works on major ones).

minimizeWindow id
Makes the specified window minimized.

The id of the window can be obtained with the waitForWindow function.

Note: this function uses xdotool internally and may not work properly on Wayland, depending on how good the X11 emulation in your display manager is (works on major ones).

activateWindow id
Makes a minimized window active again.

The id of the window can be obtained with the waitForWindow function.

Note: this function uses xdotool internally and may not work properly on Wayland, depending on how good the X11 emulation in your display manager is (works on major ones).

moveWindow id x y
Moves the specified window to the requested position.

The id of the window can be obtained with the waitForWindow function.

Note: this function uses xdotool internally and may not work properly on Wayland, depending on how good the X11 emulation in your display manager is (works on major ones).

resizeWindow id width height
Resizes the specified window to the requested width and height.

The id of the window can be obtained with the waitForWindow function.

Note: this function uses xdotool internally and may not work properly on Wayland, depending on how good the X11 emulation in your display manager is (works on major ones).

makeFullscreen id
Adds the fullscreen attribute to the specified window, essentially making it borderless and maximized. Don't use this unless the game has no other way to go to fullscreen.

The id of the window can be obtained with the waitForWindow function.

Note: this function uses xdotool internally and may not work properly on Wayland, depending on how good the X11 emulation in your display manager is (works on major ones).

removeFullscreen id
Removes the fullscreen attribute to the specified window. Games react differently to this, don't use this if the game has a better way to go to windowed mode.

The id of the window can be obtained with the waitForWindow function.

Note: this function uses xdotool internally and may not work properly on Wayland, depending on how good the X11 emulation in your display manager is (works on major ones).

keepWindowFocused exe title [timeout]
Keeps a game focused automatically, useful for games that have issues when alt-tabbing or when the taskbar is still visible when the game is in fullscreen.

The arguments are the same as the waitForWindow function, since it works in much the same way.

This function is blocking until the window no longer exists.

Example:

whileGameRunning(){
    keepWindowFocused "MassEffect.exe"
}

Note: this function uses xdotool internally and may not work properly on Wayland, depending on how good the X11 emulation in your display manager is (works on major ones).

keepWindowFocusedById id
Keeps a game focused automatically, useful for games that have issues when alt-tabbing or when the taskbar is still visible when the game is in fullscreen.

This is essentially the same as the keepWindowFocused except it takes a window id as input, which you can obtain with the waitForWindow function.

This function is blocking until the window no longer exists. Returns 1 if the specified window was not found in the first place, 0 if the window was found.

Note: this function uses xdotool internally and may not work properly on Wayland, depending on how good the X11 emulation in your display manager is (works on major ones).

pressKey keys [repeats]
Simulates the pressing of the specified key or key combination, which is sent to the currently active and focused window. Useful for games where you have to press alt+enter to enter fullscreen or where you need to press some keys to get rid of a problem.

If a number is added for the repeats parameter, the specified key or key combination will be repeated repeats times, with a delay of 0.5s between each press.

Example:

#workaround for the lag spike when you first press a key in GTA V
whileGameRunning(){
    waitForWindow "GTA5.exe" "Grand Theft Auto V"
    sleep 3
    focusWindow $WINDOW
    sleep 1
    pressKey w
}

Note: this function uses xdotool internally and may not work properly on Wayland, depending on how good the X11 emulation in your display manager is (works on major ones).

setGamma gamma
Sets the gamma for the main screen. gamma can either be a single decimal number (1.0 is the default gamma), or it can be expressed as 3 separate values r:g:b. Useful for games that look too dark on modern displays or when using gamescope since it doesn't support hardware gamma yet.

Returns 0 if the operation succeeded, 1 if an error occurred.

The gamma level is not restored automatically when the game ends, so restoreGamma must be called later.

Note: this function only works on X11.

saveGamma
Stores the current gamma level so it can be restored later with restoreGamma.

Returns 0 if the operation succeeded, 1 if an error occurred.

Note: this function only works on X11.

restoreGamma
Restores the gamma level previously saved with saveGamma. If a gamma level was never saved, it restores the gamma level from when TDF was started.

Returns 0 if the operation succeeded, 1 if an error occurred.

Example:

onGameStart(){
    saveGamma
    setGamma 1.2
}
onGameEnd(){
    restoreGamma
}

Note: this function only works on X11.

defaultGamma
Sets the default 1.0 gamma.

Returns 0 if the operation succeeded, 1 if an error occurred.

Note: this function only works on X11.

resetResolution
Resets the main display to its default screen resolution and gamma. This is useful for when you're not using the game-optimized wine build and an old game crashes without restoring the screen resolution.

Returns 0 if the operation succeeded, 1 if an error occurred.

Note: this function only works on X11.

Example:

TDF_WINE_PREFERRED_VERSION='mainline'
onGameEnd(){
    resetResolution
}

isProcessRunning exe
Determines whether there's a process with a name that contains the value in exe (case sensitive). Not limited to Wine executables.

Returns 1 if a process was found, 0 otherwise.

Example:

while ! isProcessRunning "APlagueTaleRequiem_x64.exe"; do
    sleep 0.5
done

Game collections (multiple games in one TDF instance)

Ideally, you want to have one TDF instance for each game, which keeps them nicely isolated, but some game series like Mass Effect need to import the previous game's data or the game itself has expansions/mods that need to be started with different commands, and therefore need to be installed in the same TDF instance. This is where the confs folder comes in.

Inside the confs folder, you can make as many .conf files as you need, one for each game/mod installed in the TDF instance. These files follow the same syntax as the vars.conf file and each one contains the configuration necessary to launch one game.

TDF will automatically detect the presence of files in the confs folder and show a menu with the list of games in alphabetical order.

When using this mode, the vars.conf file will always be loaded first and its settings will apply to all games, then once the user has chosen one of the games, the specific .conf file will be loaded. If a variable or a callback function is defined both in vars.conf and in one of the .conf file, the latter wins because it's more specific.

Example for Mass Effect Legendary Edition:

Note: the TDF_UI_LANGUAGE variable can only be set in vars.conf, since it's applied before TDF is started, all other variables can be changed at any moment.

By default, when the list of games is displayed, they are shown in alphabetical order. If you want them to appear in a specific order, create a file called _list.txt in the confs folder, with the list of the configuration files in the order in which you want them to appear (filenames only, no extension).

Example:

Mass Effect 1
Mass Effect 2
Mass Effect 3
Mass Effect 1 - Configuration Utility
Mass Effect 2 - Configuration Utility
Mass Effect 3 - Configuration Utility
Command Prompt (for modding)
Windows Explorer (for modding)

Packaging and redistributing a TDF instance

You installed your game(s) in a TDF instance and made sure it works perfectly? Did you test it on different hardware? Different distros? All good? Great! You're ready to package it.

To start the packaging process, open a terminal inside the TDF instance and type ./run.sh archive. This will create an archive containing the whole TDF instance that you can easily redistribute. Users will be able to simply extract these archives and launch run.sh to start the game.

By default, TDF creates a highly compressed .tar.zst archive, which takes a long time to create but can be extracted very quickly.

During the compression process, TDF will show you a progress indicator. At the end of the process, it will tell you the compressed size and the compression ratio.

If you want to customize the way these archives are created, the following short commands can be added after ./run.sh archive:

Generally speaking, the short commands above are the only ones you should need but if you want to customize it further, you can specify the following options after ./run.sh archive instead of the short commands above:

Examples:

#compress using zstd maximum to a file called example.tar.zst in the upper directory
./run.sh archive -o ../example.tar.zst
#compress using xz normal to a file called example.tar.xz in the home directory
./run.sh archive -m xz -p normal -o ~/example.tar.xz
#compress using xz fast to a file with the same name as the current folder and put it in the upper directory
./run.sh archive -m xz -p fast
#compress using zstd maximum to a file called example.tar.zst in the upper directory, split in 5G parts
./run.sh archive -o ../example.tar.zst -s 5G

Note: this feature assumes that tar, xz, gzip and zstd are installed on your system (they probably are).

If you want to transfer a TDF instance from one PC to another using an external drive, it is strongly recommended to use the archive function so that it's just one big file. Never copy a TDF instance to an NTFS, exFAT or a FAT32 partition, it will become unusable.

Updating a TDF instance

TDF is designed to be easy to update. To update a TDF instance from an older version:

It is also possible to downgrade to an older version of TDF in the same way, in case the newer version doesn't work properly.

Note: Loading a wineprefix created with TDF 1.x is not supported. The easiest way to upgrade it is to rename the zzprefix folder from the old instance to wineprefix, make a new instance and launch it at least once, then replace data/wineprefix in the new instance with the renamed folder. Keep in mind that several TDF settings have changed with TDF 2.0, and you will have to redo the vars.conf file as well as any additional config file in the confs directory from scratch.

Using custom versions of Wine, DXVK, etc.

TDF will automatically detect and apply changes to the files in the following folders inside the system folder of a TDF instance:

So for instance, if you need to test a custom version of DXVK, simply replace the DLLs in system/dxvk with your build (making sure to keep the same folder structure); when you launch run.sh, TDF will detect that these DLLs don't match the ones in the wineprefix and reinstall DXVK. The same goes for Wine and the other components in the list above.

The following folders are not monitored for changes as they are very rarely needed:

Should you need to test changes to them, simply disable that component, launch run.sh, then reenable it and launch it again.

Removing unused components

TDF is built to be partially modular, meaning that if your game doesn't need certain components, such as Wine Mono, they can be removed to reduce overhead.

Do not do this unless you know what you're doing.

The following folders can be deleted from the system folder of a TDF instance if they are not needed and disabled in the configuration:

Folders and files not mentioned in this list should not be removed to avoid breaking TDF.

If a component has been removed, TDF will not try to use it, install it or remove it, even if explicitly requested in the config, therefore it is better to remove and disable unnecessary components before the first time initialization of the wineprefix.

Never remove a component that is currently installed in the wineprefix, as this will leave it in an inconsistent state, especially during Wine updates. If that happens, simply restore the removed components and TDF should take care of the problem.

Troubleshooting

This section covers troubleshooting games on Wine in general, with a focus on how to do it with TDF. In general, some good knowledge of Windows and Linux will be very useful here.

If a game doesn't work out of the box, before you even start troubleshooting, check ProtonDB for known issues/fixes for this game. Solutions that work on Proton can easily be adapted to work in TDF.

TDF won't start (Failed to load Wine)

Wine depends on a lot of libraries, and some may be missing. TDF is built exclusively using 64-bit libraries, so multilib is not required. The easiest way to obtain the required libraries is to install Wine on your system and then remove it. * For Arch-based distros: sudo pacman -S wine sudo pacman -R wine * For Debian-based distros: sudo apt-get install wine sudo apt-get remove wine

Game won't install, the installer doesn't start, doesn't work, it freezes or gives an error during the installation

During the installation, I see error messages about .net or being unable to ShellExecute something

Game won't launch (DRM issues like disc not found or requiring a login)

Game won't launch ("Game running" dialog disappears immediately)

Game launcher doesn't work (closes immediately or has garbled graphics)

The game uses the wrong GPU (integrated instead of dedicated)

Mods with DLL files don't work

Game starts with the wrong language with no way to change it

Game detects incorrect amount of VRAM or says that the video driver is out of date

Game crashes/freezes during gameplay, graphical or performance issues

If you find a solution to a problem, always make sure to report it somewhere. If you can't find a solution, report the problem to one of the projects involved, at worst they'll tell you it's not their fault and where to report it. People are generally very friendly in the Linux gaming community.

Ray tracing is not available

Some games only enable ray tracing if they detect and NVIDIA GPU. You can fake it by adding this to the config to pretend that you have a 4090: export DXVK_CONFIG='dxgi.customVendorId = 10de; dxgi.hideAmdGpu = True; dxgi.customDeviceId = 2684; dxgi.customDeviceDesc = "NVIDIA GeForce RTX 4090"'

Sound crackling, cutting in and out, etc.

Videos don't play or game crashes when a video is supposed to play

Window positioning issues (stuck in window mode, partially off screen, flickering, etc.)

Game controller not detected/not working

TDF no longer starts after being copied/moved to an external drive or syncing through cloud to another computer

Building TDF

The TDF build scripts are designed to download the latest version of each component in TDF, build what needs to be compiled from source, and create a template-YYYYMMDD.tar.zst ready to extract and use.

Building with Distrobox and Podman

TDF depends on a lot of dependencies when building and , or who want a reproducible build environment, you can use Distrobox with Podman:

  1. Make sure you have distrobox and podman installed and working properly
  2. Download and extract the preconfigured image based on Debian 13
  3. Load the tdfbuild image: podman load -i tdfbuild.tar
  4. Create the tdfbuild container: distrobox create --image tdfbuild --name tdfbuild
  5. Launch the container: distrobox enter tdfbuild
  6. Clone the TDF repo and enter it: git clone https://github.com/adolfintel/tdf && cd tdf
  7. Build it: TDF_BUILD_AUTOUPDATE=1 ./makeTemplate.sh
  8. Stop the container: distrobox stop tdfbuild

The finished template-YYYYMMDD.tar.zst will be inside the folder where you cloned the TDF repo.

For future builds, just distrobox enter tdfbuild, go to your cloned TDF repo, and run the build script again.

Building natively

If you don't trust my image (understandable) or you just want to experience the pain of building it yourself, TDF can be built on both Arch and Debian, and will help you download dependencies along the way

To build TDF natively:

Every time it encounters a missing dependency, it will stop and you'll have to download it.

The following prebuilt components will be downloaded automatically:

The following components will be built from source:

The first build will take a good 15-60 minutes to download and compile everything, but subsequent builds will be quicker as the download phase will only download updates for the components and the TDF repo itself.

If the build fails (and let's be honest, the first few times it probably will), fix the problem and run ./makeTemplate.sh again, the script will automatically resume from where it left off.

If you don't want to use this caching and resuming system, you can run a clean build using ./makeTemplate.sh clean, this will delete all saved data and redownload everything, then build TDF.

If you're going to build TDF regularly, you can enable automatic updates for the TDF repo using TDF_BUILD_AUTOUPDATE=1 ./makeTemplate.sh (git only).

At the end of the build process, the package will be compressed using a slow but efficient zstd compression and the finished archive will be created as a .tar.zst file.

How secure is TDF?

Wine is an HLE (High Level Emulator) which means that it doesn't emulate an entire system like dosbox does because that would be too slow, instead it provides a way to load Windows exe files, intercept Windows system calls and "convert" them to equivalent Linux system calls. It also provides a ton of libraries and other functionality but that's not relevant here. Without TDF, this means that any random Windows exe that you run in Wine is to all intents and purposes, a regular UNIX process that's running under your user, and therefore it has access to everything you have access to, and malware can easily escape the restrictions put in place by Wine by using Linux system calls directly, it only requires some modest knowledge of how Wine works.

TDF was designed to sandbox Wine itself rather than games running inside it. It provides very strong security compared to a regular installation of Wine, Proton, Lutris, Bottles, and other similar tools, and does it by running Wine inside a sandbox created using bubblewrap, the same technology used to sandbox apps distributed through Flatpak, but configured differently. In other words, TDF assumes that your games are malware and treats them as such, by design.

Out of the box, a game running inside TDF has:

With that being said, I don't encourage you to run malware in it. In order for games to work, TDF needs to allow some access to your system, and this is potential attack surface:

If you need to test malware, use a proper hardened VM, this is for games.

TODOs and future improvements

FAQs

Was this project vibe-coded?

No. TDF predates the generative AI boom, so the code was written by me. Some AI tools were used (specifically a local instance of Qwen 3.6 and pi) to do things like exploring the Wine source code to look for specific settings or behaviors, updating the documentation, sandbox testing, etc. and it was actually quite helpful.

What does TDF mean?

This project started off in 2021 as a "template" that I could use to easily create these self-contained ready-made environments to easily and safely run Windows games, something that things like Lutris couldn't do really well despite having a nice GUI.

Eventually, my friends started calling it "Template Di Frederico", meaning Frederico's Template in Italian (Frederico being a common misspelling of my name, Federico); the temporary name eventually stuck, it got abbreviated to TDF or just "the template", and I couldn't come up with a better name so TDF became the official name in 2023 when I finally decided to write some documentation and release it.

License

All TDF code is distributed under the GNU GPL v3 license, but a built version of TDF will contain components with multiple licenses, including proprietary ones.

Copyright (C) 2021-2026 Federico Dossena

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.