feat: add default exe Wine and prefix settings #190

Manually merged
CastroFidel merged 1 commits from scripts-default into main 2026-06-03 17:32:40 +00:00
6 changed files with 232 additions and 50 deletions

No files matched your search

@@ -1190,6 +1190,10 @@ check_user_conf () {
# export DXVK_HUD=full
# export GALLIUM_HUD=fps
export PW_GPU_USE="disabled"
# Optional defaults for executables without matching db values:
# export PW_DEFAULT_WINE_USE="WINE_LG"
# export PW_DEFAULT_PREFIX_NAME="DEFAULT"
# export PW_DEFAULT_VULKAN_USE="2"
EOF
else
sed -i \
@@ -1903,6 +1907,10 @@ check_dirs_and_files_in_pfx () {
}
pw_init_db () {
[[ -n "${PW_DEFAULT_WINE_USE:-}" ]] && export PW_WINE_USE="${PW_DEFAULT_WINE_USE}"
[[ -n "${PW_DEFAULT_PREFIX_NAME:-}" ]] && export PW_PREFIX_NAME="${PW_DEFAULT_PREFIX_NAME}"
[[ -n "${PW_DEFAULT_VULKAN_USE:-}" ]] && export PW_VULKAN_USE="${PW_DEFAULT_VULKAN_USE}"
if echo "$LSPCI_VGA" | grep -i -q 'nvidia.*rtx' ; then
export PW_USE_NVAPI_AND_DLSS="1"
export PW_USE_RAY_TRACING="1"
@@ -2228,15 +2236,18 @@ edit_user_conf_from_gui () {
elif [[ $proxy_mod_db =~ ^(Enabled|Enable)$ ]] ; then
proxy_mod_db=enabled
fi
proxy_mod_db=$(echo "$proxy_mod_db" | sed 's/\\/\\\\\\\\/g; s/"/\\"/g')
proxy_mod_db_escaped=$(printf '%s\n' "$proxy_mod_db" | sed 's/[\/&]/\\&/g')
if ! grep "export ${mod_db}=" "${USER_CONF}" &>/dev/null
then echo "export ${mod_db}=\"$proxy_mod_db\"" >> "${USER_CONF}"
elif grep "export ${mod_db}=" "${USER_CONF}" | grep -v "#" &>/dev/null
then
if [[ "$(grep "export ${mod_db}=" "${USER_CONF}" | grep -v "#")" != "export ${mod_db}=\"$proxy_mod_db\"" ]]
then sed -ri "s/^export ${mod_db}=.*/export ${mod_db}=\"$proxy_mod_db\"/" "${USER_CONF}"
then sed -ri "s|^export ${mod_db}=.*|export ${mod_db}=\"${proxy_mod_db_escaped}\"|" "${USER_CONF}"
fi
elif [[ "$(grep "export ${mod_db}=" "${USER_CONF}")" != "export ${mod_db}=$proxy_mod_db" ]]
then sed -ri "s/.*export ${mod_db}=.*/export ${mod_db}=\"$proxy_mod_db\"/g" "${USER_CONF}"
then sed -ri "s|.*export ${mod_db}=.*|export ${mod_db}=\"${proxy_mod_db_escaped}\"|g" "${USER_CONF}"
fi
done
return 0
@@ -396,6 +396,10 @@ Usage examples:
done < <(grep -E '^export ' "$USER_CONF" 2>/dev/null | sed -E 's/[[:space:]]*#.*$//' | sed '/^[[:space:]]*$/d')
fi
[[ -n "${PW_DEFAULT_WINE_USE:-}" && -z "${all_vars[PW_WINE_USE]+x}" ]] && all_vars["PW_WINE_USE"]="$PW_DEFAULT_WINE_USE"
[[ -n "${PW_DEFAULT_PREFIX_NAME:-}" && -z "${all_vars[PW_PREFIX_NAME]+x}" ]] && all_vars["PW_PREFIX_NAME"]="$PW_DEFAULT_PREFIX_NAME"
[[ -n "${PW_DEFAULT_VULKAN_USE:-}" && -z "${all_vars[PW_VULKAN_USE]+x}" ]] && all_vars["PW_VULKAN_USE"]="$PW_DEFAULT_VULKAN_USE"
while IFS='=' read -r key val; do
key="${key#export }"
val="${val#\"}"
+7 -32
View File
@@ -2,9 +2,7 @@
import os
import re
import shutil
import subprocess
from pathlib import Path
from typing import cast, TYPE_CHECKING
from PySide6.QtCore import Qt, QObject, QEvent, QProcess, QTimer, QUrl
@@ -46,12 +44,13 @@ from portprotonqt.logger import get_logger
from portprotonqt.preloader import Preloader
from portprotonqt.settings_manager import (
ADVANCED_SETTING_KEYS,
get_available_prefix_options,
get_available_wine_options,
get_advanced_settings,
get_toggle_settings,
read_lg_dist_versions_from_var,
)
from portprotonqt.theme_manager import ThemeManager
from portprotonqt.version_utils import include_pinned_prefixes, version_sort_key
from portprotonqt.virtual_keyboard import VirtualKeyboard
logger = get_logger(__name__)
@@ -142,42 +141,18 @@ class ExeSettingsDialog(DraggableDialog, MangoHudSettingsMixin, GamescopeSetting
self.lg_dist_aliases = {}
self.prefix_options = []
if self.portproton_path:
dist_dir = os.path.join(self.portproton_path, "data", 'dist')
if os.path.exists(dist_dir):
self.dist_options = sorted(
[f for f in os.listdir(dist_dir) if os.path.isdir(os.path.join(dist_dir, f))],
key=version_sort_key
)
scripts_path = get_portproton_scripts_path()
if scripts_path:
var_path = os.path.join(scripts_path, "var")
self.lg_dist_aliases = read_lg_dist_versions_from_var(var_path)
for version in self.lg_dist_aliases.values():
if version not in self.dist_options:
self.dist_options.append(version)
from portprotonqt.steam_api import get_steam_proton_versions
from portprotonqt.steam_api.utils import _is_steam_proton_dir
for version in get_steam_proton_versions():
if version not in self.dist_options:
self.dist_options.append(version)
if self.game_source == "steam":
self.dist_options = [
version for version in self.dist_options
if _is_steam_proton_dir(
Path(version) if os.path.isabs(version) else Path(dist_dir, version)
)
]
self.dist_options.sort(key=version_sort_key)
system_wine_label = "" if self.game_source == "steam" else _('System WINE')
self.dist_options = get_available_wine_options(
self.portproton_path, system_wine_label, self.game_source == "steam"
)
prefixes_dir = os.path.join(self.portproton_path, 'prefixes')
prefixes = []
if os.path.exists(prefixes_dir):
_normalize_prefix_directories(prefixes_dir)
prefixes = [f for f in os.listdir(prefixes_dir) if os.path.isdir(os.path.join(prefixes_dir, f))]
self.prefix_options = include_pinned_prefixes(prefixes)
if self.game_source != "steam" and shutil.which('wine'):
if _('System WINE') not in self.dist_options:
self.dist_options.append(_('System WINE'))
self.prefix_options = get_available_prefix_options(self.portproton_path)
self.current_settings = {}
self.value_widgets = {}
+150 -14
View File
@@ -59,7 +59,7 @@ from portprotonqt.cli import (
)
from portprotonqt.tray_manager import restart_application_process
from portprotonqt.version_utils import include_pinned_prefixes, version_sort_key
from portprotonqt.version_utils import version_sort_key
from portprotonqt.localization import _, get_metadata_language, read_metadata_translations
from portprotonqt.downloader import Downloader
from portprotonqt.tray_manager import TrayManager
@@ -72,6 +72,7 @@ from portprotonqt.scripts_utils.prefix_backup import BACKUP_EXTENSION, is_legacy
from portprotonqt.tabs.control_hints import MainWindowControlHintsMixin
from portprotonqt.tabs.system_tab import MainWindowSystemTabMixin
from portprotonqt.tabs.workers import MainWindowWorkersMixin
from portprotonqt.settings_manager import get_available_prefix_options, get_available_wine_options
from PySide6.QtWidgets import (QLineEdit, QMainWindow, QWidget, QVBoxLayout, QLabel, QHBoxLayout, QStackedWidget, QComboBox,
QDialog, QFormLayout, QMessageBox, QApplication, QPushButton, QProgressBar, QCheckBox, QSizePolicy, QGridLayout, QScrollArea, QScroller, QSlider, QFrame)
@@ -168,6 +169,48 @@ class MainWindow(MainWindowControlHintsMixin, MainWindowSystemTabMixin, MainWind
logger.warning("Failed to read LG versions from %s: %s", var_path, exc)
return versions
def _get_default_exe_wine_options(self) -> list[str]:
if not self.portproton_location:
return []
return get_available_wine_options(self.portproton_location, "USE_SYSTEM_WINE")
def _get_var_default_setting(self, key: str, fallback: str) -> str:
scripts_path = get_portproton_scripts_path()
if not scripts_path:
return fallback
var_path = os.path.join(scripts_path, "var")
if not os.path.exists(var_path):
return fallback
try:
with open(var_path, encoding="utf-8") as var_file:
for line in var_file:
match = re.match(rf'^\s*check_variables\s+{key}\s+["\']?([^"\']+)', line)
if match:
return match.group(1)
except OSError as exc:
logger.warning("Failed to read default %s from %s: %s", key, var_path, exc)
return fallback
def _get_default_exe_prefix_options(self) -> list[str]:
if not self.portproton_location:
return []
prefixes_path = os.path.join(self.portproton_location, "data", "prefixes")
if os.path.exists(prefixes_path):
self._normalize_prefix_directories(prefixes_path)
return get_available_prefix_options(self.portproton_location)
def _set_combo_current_data(self, combo: QComboBox, value: str) -> None:
for index in range(combo.count()):
if combo.itemData(index) == value:
combo.setCurrentIndex(index)
return
if value:
combo.addItem(value, value)
combo.setCurrentIndex(combo.count() - 1)
def __init__(self, app_name: str, version: str, launch_exe: str | None = None, resolution: tuple[int, int] | None = None, show_system_tab: bool = False):
super().__init__()
self.theme_manager = ThemeManager()
@@ -1840,11 +1883,7 @@ class MainWindow(MainWindowControlHintsMixin, MainWindowSystemTabMixin, MainWind
self.wineCombo.setCurrentIndex(0)
formLayout.addRow(self.wineTitleLabel, self.wineCombo)
prefixes = [
d for d in os.listdir(prefixes_path)
if os.path.isdir(os.path.join(prefixes_path, d))
] if os.path.exists(prefixes_path) else []
self.prefixes = include_pinned_prefixes(prefixes)
self.prefixes = get_available_prefix_options(self.portproton_location)
self.prefixCombo = QComboBox()
self.prefixCombo.view().window().setWindowFlags(
Qt.WindowType.Popup | Qt.WindowType.FramelessWindowHint
@@ -2196,12 +2235,7 @@ class MainWindow(MainWindowControlHintsMixin, MainWindowSystemTabMixin, MainWind
if os.path.exists(prefixes_path):
self._normalize_prefix_directories(prefixes_path)
# Update the prefixes list with sorting
prefixes = [
d for d in os.listdir(prefixes_path)
if os.path.isdir(os.path.join(prefixes_path, d))
] if os.path.exists(prefixes_path) else []
self.prefixes = include_pinned_prefixes(prefixes)
self.prefixes = get_available_prefix_options(self.portproton_location)
self.prefixCombo.clear()
self.prefixCombo.addItems(self.prefixes)
if normalized_current_prefix:
@@ -2689,7 +2723,99 @@ class MainWindow(MainWindowControlHintsMixin, MainWindowSystemTabMixin, MainWind
downloadForm.addRow(economy_mode_layout)
downloadForm.addRow(self.downloadMirrorTitle, self.downloadMirrorCombo)
# 4. Hardware Settings Section
# 4. Default Exe Settings Section
exeDefaultsFrame, exeDefaultsForm = create_section(_("Default Exe Settings"), self.theme)
exeDefaultsForm.setRowWrapPolicy(QFormLayout.RowWrapPolicy.DontWrapRows)
scrollLayout.addWidget(exeDefaultsFrame)
self.defaultExeWineCombo = QComboBox()
self.defaultExeWineCombo.view().window().setWindowFlags(
Qt.WindowType.Popup | Qt.WindowType.FramelessWindowHint
)
self.defaultExeWineCombo.view().window().setAttribute(
Qt.WidgetAttribute.WA_TranslucentBackground
)
self.defaultExeWineCombo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self.defaultExeWineCombo.setStyleSheet(self.theme.COMBOBOX_STYLE + self.theme.SCROLL_STYLE)
self.defaultExeWineCombo.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
default_wine_from_var = self._get_var_default_setting("PW_WINE_USE", "PROTON_LG")
self.defaultExeWineCombo.addItem(default_wine_from_var, "")
for wine_version in self._get_default_exe_wine_options():
if wine_version == default_wine_from_var:
continue
display_name = _("System WINE") if wine_version == "USE_SYSTEM_WINE" else wine_version
self.defaultExeWineCombo.addItem(display_name, wine_version)
self._set_combo_current_data(
self.defaultExeWineCombo, get_user_conf_setting('PW_DEFAULT_WINE_USE') or ""
)
self.defaultExeWineTitle = QLabel(_("Default WINE/Proton:"))
self.defaultExeWineTitle.setStyleSheet(self.theme.SETTINGS_TITLE_STYLE)
self.defaultExeWineTitle.setFocusPolicy(Qt.FocusPolicy.NoFocus)
exeDefaultsForm.addRow(self.defaultExeWineTitle, self.defaultExeWineCombo)
self.defaultExePrefixCombo = QComboBox()
self.defaultExePrefixCombo.view().window().setWindowFlags(
Qt.WindowType.Popup | Qt.WindowType.FramelessWindowHint
)
self.defaultExePrefixCombo.view().window().setAttribute(
Qt.WidgetAttribute.WA_TranslucentBackground
)
self.defaultExePrefixCombo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self.defaultExePrefixCombo.setStyleSheet(self.theme.COMBOBOX_STYLE + self.theme.SCROLL_STYLE)
self.defaultExePrefixCombo.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
default_prefix_from_var = self._get_var_default_setting("PW_PREFIX_NAME", "DEFAULT")
self.defaultExePrefixCombo.addItem(default_prefix_from_var, "")
for prefix_name in self._get_default_exe_prefix_options():
if prefix_name != default_prefix_from_var:
self.defaultExePrefixCombo.addItem(prefix_name)
self.defaultExePrefixCombo.setEditable(True)
self.defaultExePrefixCombo.setInsertPolicy(QComboBox.InsertPolicy.NoInsert)
prefix_line_edit = self.defaultExePrefixCombo.lineEdit()
if prefix_line_edit is not None:
prefix_line_edit.setPlaceholderText(_("Enter prefix name"))
default_prefix = get_user_conf_setting('PW_DEFAULT_PREFIX_NAME')
if default_prefix:
self.defaultExePrefixCombo.setCurrentText(default_prefix)
self.defaultExePrefixTitle = QLabel(_("Default prefix:"))
self.defaultExePrefixTitle.setStyleSheet(self.theme.SETTINGS_TITLE_STYLE)
self.defaultExePrefixTitle.setFocusPolicy(Qt.FocusPolicy.NoFocus)
exeDefaultsForm.addRow(self.defaultExePrefixTitle, self.defaultExePrefixCombo)
self.defaultExeVulkanCombo = QComboBox()
self.defaultExeVulkanCombo.view().window().setWindowFlags(
Qt.WindowType.Popup | Qt.WindowType.FramelessWindowHint
)
self.defaultExeVulkanCombo.view().window().setAttribute(
Qt.WidgetAttribute.WA_TranslucentBackground
)
self.defaultExeVulkanCombo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self.defaultExeVulkanCombo.setStyleSheet(self.theme.COMBOBOX_STYLE + self.theme.SCROLL_STYLE)
self.defaultExeVulkanCombo.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
default_vulkan_from_var = self._get_var_default_setting("PW_VULKAN_USE", "6")
vulkan_options = [
(_("Newest"), "6"),
(_("Stable"), "2"),
("Sarek", "1"),
("WINED3D - OpenGL", "0"),
]
for vulkan_name, vulkan_value in vulkan_options:
if vulkan_value == default_vulkan_from_var:
self.defaultExeVulkanCombo.addItem(vulkan_name, "")
break
if self.defaultExeVulkanCombo.count() == 0:
self.defaultExeVulkanCombo.addItem(default_vulkan_from_var, "")
for vulkan_name, vulkan_value in vulkan_options:
if vulkan_value != default_vulkan_from_var:
self.defaultExeVulkanCombo.addItem(vulkan_name, vulkan_value)
self._set_combo_current_data(
self.defaultExeVulkanCombo, get_user_conf_setting('PW_DEFAULT_VULKAN_USE') or ""
)
self.defaultExeVulkanTitle = QLabel(_("Default 3D API:"))
self.defaultExeVulkanTitle.setStyleSheet(self.theme.SETTINGS_TITLE_STYLE)
self.defaultExeVulkanTitle.setFocusPolicy(Qt.FocusPolicy.NoFocus)
exeDefaultsForm.addRow(self.defaultExeVulkanTitle, self.defaultExeVulkanCombo)
# 5. Hardware Settings Section
hwFrame, hwForm = create_section(_("Hardware Settings"), self.theme)
hwForm.setRowWrapPolicy(QFormLayout.RowWrapPolicy.DontWrapRows)
scrollLayout.addWidget(hwFrame)
@@ -2723,7 +2849,7 @@ class MainWindow(MainWindowControlHintsMixin, MainWindowSystemTabMixin, MainWind
self.gpuTitle.setFocusPolicy(Qt.FocusPolicy.NoFocus)
hwForm.addRow(self.gpuTitle, self.gpuCombo)
# 5. Proxy Settings Section
# 6. Proxy Settings Section
proxyFrame, proxyForm = create_section(_("Proxy Settings"), self.theme)
proxyForm.setRowWrapPolicy(QFormLayout.RowWrapPolicy.DontWrapRows)
scrollLayout.addWidget(proxyFrame)
@@ -2923,6 +3049,16 @@ class MainWindow(MainWindowControlHintsMixin, MainWindowSystemTabMixin, MainWind
else:
ui_config.get_disable_runtime_download()
default_wine = self.defaultExeWineCombo.currentData()
set_user_conf_setting('PW_DEFAULT_WINE_USE', str(default_wine or ""))
default_prefix = self.defaultExePrefixCombo.currentText().strip()
if default_prefix == self.defaultExePrefixCombo.itemText(0):
default_prefix = ""
default_prefix = re.sub(r"[ \t]", "_", default_prefix).upper() if default_prefix else ""
set_user_conf_setting('PW_DEFAULT_PREFIX_NAME', default_prefix)
default_vulkan = self.defaultExeVulkanCombo.currentData()
set_user_conf_setting('PW_DEFAULT_VULKAN_USE', str(default_vulkan or ""))
set_user_conf_setting('MIRROR', self.downloadMirrorCombo.currentText())
# Save GPU selection to user.conf (only if the combo box exists)
+2 -2
View File
@@ -932,12 +932,12 @@ def get_user_conf_setting(variable_name):
clean_lines = []
for line in result.stdout.splitlines():
stripped_line = line.strip()
stripped_line = re.sub(r'\x1b\[[0-9;]*[A-Za-z]', '', line).strip()
if not stripped_line:
continue
if any(char in stripped_line for char in "█░▄▀╔╗╚╝║"):
continue
if stripped_line.startswith(("Warning:", "Info:")):
if re.match(r'^\[?[0-9;]*m?\s*(Info|Warning|Error|OK):', stripped_line):
continue
if "must use subscript when assigning associative array" in stripped_line:
continue
+56
View File
@@ -1,7 +1,11 @@
import os
import shutil
import subprocess
from pathlib import Path
from portprotonqt.logger import get_logger
from portprotonqt.version_utils import include_pinned_prefixes
from portprotonqt.version_utils import version_sort_key
logger = get_logger(__name__)
LG_WINE_ALIASES = (
@@ -40,6 +44,58 @@ def resolve_lg_wine_alias(wine_version: str, env_vars: dict[str, str]) -> str:
return wine_version
def get_available_wine_options(
portproton_path: str,
system_wine_label: str = "",
steam_only: bool = False
) -> list[str]:
dist_dir = os.path.join(portproton_path, "data", "dist")
options = []
if os.path.exists(dist_dir):
options = sorted(
[f for f in os.listdir(dist_dir) if os.path.isdir(os.path.join(dist_dir, f))],
key=version_sort_key
)
from portprotonqt.config import get_portproton_scripts_path
scripts_path = get_portproton_scripts_path()
if scripts_path:
var_path = os.path.join(scripts_path, "var")
for version in read_lg_dist_versions_from_var(var_path).values():
if version not in options:
options.append(version)
from portprotonqt.steam_api import get_steam_proton_versions
from portprotonqt.steam_api.utils import _is_steam_proton_dir
for version in get_steam_proton_versions():
if version not in options:
options.append(version)
if steam_only:
options = [
version for version in options
if _is_steam_proton_dir(
Path(version) if os.path.isabs(version) else Path(dist_dir, version)
)
]
elif system_wine_label and shutil.which('wine') and system_wine_label not in options:
options.append(system_wine_label)
options.sort(key=version_sort_key)
return options
def get_available_prefix_options(portproton_path: str) -> list[str]:
prefixes_dir = os.path.join(portproton_path, "data", "prefixes")
prefixes = []
if os.path.exists(prefixes_dir):
prefixes = [
name for name in os.listdir(prefixes_dir)
if os.path.isdir(os.path.join(prefixes_dir, name))
]
return include_pinned_prefixes(prefixes)
def get_available_locale_options() -> list[str]:
"""Get locale options based on locales available in the system."""
target_locales = ("ru_RU", "en_US", "zh_CN", "ja_JP", "ko_KR")