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.

TDF is based on the following awesome projects:

TDF's main goals are are:

If you're happy with Lutris, you probably don't need TDF.

Usage

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

Requirements

Basic usage

Here's a video showing how to install a game from GOG that requires no additional configuration: Basic usage - Installing a game from GOG

You can find more video examples at the end of this document.

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

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

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

game_args
Arguments to be passed to the game.

Example: game_args='-iwad doom2.wad -file mymod.wad'

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'

TDF variables

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

TDF_DETAILED_PROGRESS
Whether to show the details of what's happening above the progress bar in the TDF window.

Possible values:

TDF_MULTIPLE_INSTANCES
What to do if the user tries to launch run.sh while it's already running.

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_HIDE_GAME_RUNNING_DIALOG
Whether to hide the TDF window that says "Game running". By default, TDF shows it so you can know if the process has stalled.

Possible values:

TDF_SHOW_PLAY_TIME
Whether to show a message when you close the game that tells you how long you've been playing.

Possible values:

TDF_DND
Enables Do Not Disturb mode (on supported DEs) while the game is running.

Possible values:

Note: some games launch a separate process and terminate immediately. This setting won't work on these games.

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:

Wine variables

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_HIDE_CRASHES
When a Wine application crashes, it normally shows a window similar to the "Stopped working" dialog 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 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_WINE_DPI
DPI value for display scaling of Wine applications.

Possible values:

TDF_WINE_KILL_BEFORE
Whether to kill wine before launching the game. Not recommended.

Possible values:

TDF_WINE_KILL_AFTER
Whether to kill wine after the game ends. Not recommended.

Possible values:

TDF_START_ARGS
Optional arguments to pass to Wine's start command. More info. Mostly useful to set CPU affinity for old games (in this regard, see also TDF_WINE_MAXLOGICALCPUS).

Example: TDF_START_ARGS='/AFFINITY 1'

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'

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

Possible values:

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

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_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. This can be used to debug issues like games not playing videos or crashing when a video is supposed to play.

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 we 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_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:

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, 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 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, or restore the default Wine settings using unset WINEDEBUG.

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

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 each 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
For systems with multiple CPUs only, how to use them.

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.

DXVK and VKD3D variables

TDF_DXVK
Whether to install DXVK or not, which provides DirextX 9-11 emulation through Vulkan. If this is disabled, WineD3D will be used instead, which is better for some older games.

Settings for DXVK can be changed by editing the dxvk.conf file that will be created inside zzprefix.

Possible values:

TDF_DXVK_ASYNC
If enabled, TDF will use the gplasync version of DXVK instead of the regular version. This is only useful for systems that don't support the Vulkan VK_EXT_graphics_pipeline_library (GPL) extension because either the GPU or the driver is too old.

Possible values:

TDF_DXVK_NVAPI
Whether to install DXVK-nvapi or not, which provides nvapi support for nVidia GPUs. Requires TDF_DXVK to be set to 1.

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 is HDR is disabled or unsupported.

Possible values:

Note: HDR is not supported by X11, you must be using Wayland for this to work.

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

Possible values:

VKD3D's config can be changed by using its environment variables. By default, doesn't set this variable, meaning that VKD3D will automatically enable ray tracing on supported cards. Older versions of TDF (before November 2023) set this to dxr11 to enable ray tracing.

Sandboxing variables

TDF_BLOCK_NETWORK
Whether to block network access to Wine. By default, TDF blocks network access entirely to prevent undesirable data collection, but it can be unblocked if you trust the game you're running or it needs to go online.

Possible values:

TDF_BLOCK_BROWSER
Whether to block Wine from opening the system's native web browser or not. By default, TDF will block these requests to prevent undesirable data collection, but it can be unblocked if you trust the game you're running or it needs to open some web pages.

Possible values:

Note that allowing access to the native web browser can be abused to bypass TDF_BLOCK_NETWORK, this behavior has been noticed in several game repack installers.

TDF_BLOCK_ZDRIVE
Wine normally exposes a Z drive to applications, with full access to the Linux file system, which can be abused by games to collect data or by malware to modify files outside the TDF instance, but it can also be useful when installing games, since you can access mounted drives, your Downloads folder, etc. or if you're running applications.

Possible values:

Note that while this can help protect against Windows malware, malware designed to attack Wine or Linux can very easily bypass this restriction. Never run untrusted software inside TDF, use a VM instead.

TDF_BLOCK_EXTERNAL_DRIVES
Wine normally exposes external drives to applications, giving a letter to each drive. This can be undesirable for the same reasons as exposing the Z drive and TDF blocks this by default.

Possible values:

TDF_PROTECT_DOSDEVICES
Prevents Wine from automatically handling all drive mappings for better security. Can cause the winedevice service to hang when external drives are connected.

Possible values:

Note: this option is effectively useless when using TDF's own Wine builds, since dbus is disabled and no mappings will be automatically created.

TDF_BLOCK_SYMLINKS_IN_CDRIVE
When this option is enabled, TDF will scan the C drive on startup and remove all symlinks. This can be useful in case Wine creats some symlinks outside the TDF instance during an update, or if you created a symlink during the installation and forgot to remove it, which can be dangerous.

Possible values:

Note that Wine's symlinks to your home directory, My Documents, Desktop, etc. as well as the creation of shortcuts (.desktop files) on your desktop and start menu will always be blocked regardless of settings; TDF is not designed to let applications "integrate" with the system, quite the opposite.

TDF_FAKE_HOMEDIR
When this option is enabled, Wine will not see your home directory, but a zzhome folder will be created in the TDF instance. This can be used to improve security, but it's mostly useful for games that require special settings in ~/.driconf, such as KOTOR.

Possible values:

Note that the zzhome folder and all data inside it will be automatically deleted when this option is disabled.

Gamescope variables

TDF_GAMESCOPE
Whether to enable Gamescope when running the game or not. This is generally not recommended when using the games version of Wine, since it integrates the fshack patches which makes 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:

Note that Gamescope currently only works properly on AMD GPUs and getting it to work properly on Intel and nVidia cards requires additional configuration.

libstrangle variables

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

Possible values:

TDF_GAMESCOPE_PARAMETERS
The command line arguments used to start Gamescope. You can see a complete list here.

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 conveninece that contain the horizontal and vertical resolution of the main display. This default value emulates a virutal 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.

Miscellaneous variables

TDF_GAMEMODE
Whether to launch the game using Feral Gamemode or not, which can improve performance especially on weaker or mobile systems. If Gamemode is not installed in the system, it has no effect.

Possible values:

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:

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_VCREDIST
Whether to install the Microsoft Visual C++ Redistributable (2015+) or not. This is useful for modern games but unnecessary for older ones.

Possible values:

TDF_REAPER
Whether to launch Wine using reaper or not. This will improve detection of when a game is running or not, even if the main exe just launches a separate process and terminates immediately.

Possible values:

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.

export SteamGameId
The games-optimized build contains some game fixes from Proton that are activated by setting this variable with the game's Steam appid.

Example:

#fix black screen in GOW:Ragnarok
export SteamGameId=2322010

You can find the appid for a specific game by searching for it on Steam and copying it from the URL, for instance: https://store.steampowered.com/app/2322010/God_of_War_Ragnark/, the appid is 2322010.

Always copy the appid for the main game, not for a DLC.

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.

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.

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(){
    setGamma 1.2
}

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(){
    restoreGamma
}

Notes:

whileGameRunning
This function will be called right before the game is running, and will continue running in parallel to the game in a subshell.

Example:

whileGameRunning(){
    #workaround for the little black bar at the top of the screen
    waitForWindow "APlagueTaleRequiem_x64.exe"
    sleep 3
    focusWindow $WINDOW
    pressKey alt+enter 2
}

Notes:

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 zzprefix/drive_c/APTRequiem/vkd3d-proton.cache "$TEMPDIR"
    mv zzprefix/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" zzprefix/drive_c/APTRequiem/
    mv "$TEMPDIR/tmp/requiem/GOG.com" zzprefix/drive_c/users/wine/AppData/Local/
    rm -rf "$TEMPDIR"
}

Note that this function is blocking and TDF won't quit 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 an 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:

if isProcessRunning "explorer.exe"; then
    wineserver -k -w
fi

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 that confs folder comes in.

Inside the conf 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 conf 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 (assuming you have the rights to do it). 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 introduces some problems.

Using custom versions of Wine, DXVK, etc.

TDF will automatically detect and apply changes to the files in the following folders inside the system 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 other components in the list above.

Note: your custom files will only be used if that component is actually being used. For instance, if your GPU supports the VK_EXT_graphics_pipeline_library, then DXVK-gplasync will never be used unless you explicitly set TDF_DXVK_ASYNC=1 or switch to an older driver.

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:

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 or install it even if explicitly requested in the config. If a component is removed after it has been installed in the wineprefix, it will not be removed even if explicitly requested in the config.
For these reasons, it's better to remove 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, TDF is built exclusively using 64-bit libraries, so multilib is not required. The easiest way to obtain these is to simply install Wine on your system and then removing 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.

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.)

TDF says the wineprefix is already running but the game is not

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.

It is strongly recommended to use an Arch-based distro to build TDF.

To build TDF:

The following dependencies must be installed on your system:

The following components will be downloaded:

The following components will be built from source:

The first build will take a good 30-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 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 ~310MB.

Important security notice

While TDF provides some additional security compared to a standard installation of Wine or Proton, it is important to understand that Wine is simply not designed for security, quite the opposite, it's designed to seamlessly integrate Windows stuff into Linux.

Wine is an HLE (High Level Emulator) which means that, to put it simply, it doesn't emulate an entire system like dosbox does because it 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.

What this means is that a process running inside Wine is, to all intents and purposes, a regular UNIX process that's running under your user, and therefore has access to everything you have access to, and malicious software can easily escape the restrictions put in place by Wine or TDF by using Linux system calls directly, it only requires some modest knowledge of assembly.

This is not really a problem if you're just running games, the chances of games containing such a sophisticated malware are virtually zero, but it's important to understand that TDF is not a safe way to run malware or other software downloaded from dubious sources, it can easily escape the sandboxing and damage the real system. Always use a well isolated VM to test or reverse engineer malware.

To put it short: if you're worried about telemetry and data collection in games, you just don't want games to put files all over your system or you just want to package games, TDF is good; if you're going to run GTAV_Installer.exe (2.9MB) downloaded from SkidEmpressReloadedLegitCracks69.ru it is very much not.

TODOs and future improvements

Videos

Important: some cracked copies of games that I have in my Steam/GOG/EGS library are used in these videos. These cracks have been used to circumvent DRM issues or incompatibilities with modern systems. TDF does not endorse piracy and I will not provide cracked copies of games.

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-2025 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.