1
0
Fork 0
mirror of synced 2024-06-29 11:40:37 +12:00

Merge pull request #189 from Dummerle/change_some_settings

Change some settings
This commit is contained in:
Dummerle 2022-03-28 21:12:34 +02:00 committed by GitHub
commit ab385f334d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 1101 additions and 747 deletions

View file

@ -1,3 +1,3 @@
__version__ = "1.8.7" __version__ = "1.8.8"
code_name = "Stellula Kakopo" code_name = "Stellula Kakopo"

View file

@ -7,8 +7,9 @@ import sys
import time import time
import traceback import traceback
from argparse import Namespace from argparse import Namespace
from datetime import datetime
from PyQt5.QtCore import Qt, QThreadPool, QSettings, QTranslator from PyQt5.QtCore import Qt, QThreadPool, QSettings, QTranslator, QTimer
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMessageBox from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMessageBox
from requests import HTTPError from requests import HTTPError
@ -159,6 +160,19 @@ class App(QApplication):
self.launch_dialog.login() self.launch_dialog.login()
dt_exp = datetime.fromisoformat(self.core.lgd.userdata['expires_at'][:-1])
dt_now = datetime.utcnow()
td = abs(dt_exp - dt_now)
self.timer = QTimer()
self.timer.timeout.connect(self.re_login)
self.timer.start(int(td.total_seconds() - 60))
def re_login(self):
logger.info("Session expires shortly. Renew session")
self.core.login()
self.timer.stop()
self.timer.deleteLater()
def show_mainwindow(self): def show_mainwindow(self):
if self.window_launched: if self.window_launched:
self.mainwindow.show() self.mainwindow.show()

View file

@ -2,7 +2,7 @@ import os
import platform import platform
from logging import getLogger from logging import getLogger
from PyQt5.QtCore import Qt, pyqtSignal, QRunnable, QObject, QThreadPool from PyQt5.QtCore import Qt, pyqtSignal, QRunnable, QObject, QThreadPool, QSettings
from PyQt5.QtWidgets import QDialog, QApplication from PyQt5.QtWidgets import QDialog, QApplication
from legendary.core import LegendaryCore from legendary.core import LegendaryCore
from requests.exceptions import ConnectionError, HTTPError from requests.exceptions import ConnectionError, HTTPError
@ -40,9 +40,10 @@ class ApiRequestWorker(QRunnable):
self.signals = LaunchDialogSignals() self.signals = LaunchDialogSignals()
self.setAutoDelete(True) self.setAutoDelete(True)
self.core = LegendaryCoreSingleton() self.core = LegendaryCoreSingleton()
self.settings = QSettings()
def run(self) -> None: def run(self) -> None:
if platform.system() == "Darwin" or "Mac" in self.core.get_installed_platforms(): if self.settings.value("mac_meta", platform.system() == "Darwin", bool):
try: try:
result = self.core.get_game_and_dlc_list(True, "Mac") result = self.core.get_game_and_dlc_list(True, "Mac")
except HTTPError: except HTTPError:
@ -50,9 +51,13 @@ class ApiRequestWorker(QRunnable):
self.signals.result.emit(result, "mac") self.signals.result.emit(result, "mac")
else: else:
self.signals.result.emit(([], {}), "mac") self.signals.result.emit(([], {}), "mac")
try:
result = self.core.get_game_and_dlc_list(True, "Win32") if self.settings.value("win32_meta", False, bool):
except HTTPError: try:
result = self.core.get_game_and_dlc_list(True, "Win32")
except HTTPError:
result = [], {}
else:
result = [], {} result = [], {}
self.signals.result.emit(result, "32bit") self.signals.result.emit(result, "32bit")

View file

@ -27,7 +27,7 @@ class BrowserLogin(QWidget, Ui_BrowserLogin):
self.core = core self.core = core
self.sid_edit = IndicatorLineEdit( self.sid_edit = IndicatorLineEdit(
ph_text=self.tr("Insert SID here"), edit_func=self.text_changed, parent=self placeholder=self.tr("Insert SID here"), edit_func=self.text_changed, parent=self
) )
self.link_text.setText(self.login_url) self.link_text.setText(self.login_url)
self.copy_button.setIcon(icon("mdi.content-copy", "fa.copy")) self.copy_button.setIcon(icon("mdi.content-copy", "fa.copy"))

View file

@ -43,8 +43,8 @@ class ConsoleWindow(QWidget):
def log(self, text: str, end: str = "\n"): def log(self, text: str, end: str = "\n"):
self.console.log(text + end) self.console.log(text + end)
def error(self, text): def error(self, text, end: str = "\n"):
self.console.error(text) self.console.error(text + end)
class Console(QPlainTextEdit): class Console(QPlainTextEdit):
@ -55,11 +55,13 @@ class Console(QPlainTextEdit):
self._cursor_output = self.textCursor() self._cursor_output = self.textCursor()
def log(self, text): def log(self, text):
self._cursor_output.insertText(text) html = f"<p style=\"color:#999;white-space:pre\">{text}</p>"
self._cursor_output.insertHtml(html)
self.scroll_to_last_line() self.scroll_to_last_line()
def error(self, text): def error(self, text):
self._cursor_output.insertHtml(f'<font color="Red">{text}</font>') html = f"<p style=\"color:#eee;white-space:pre\">{text}</p>"
self._cursor_output.insertHtml(html)
self.scroll_to_last_line() self.scroll_to_last_line()
def scroll_to_last_line(self): def scroll_to_last_line(self):

View file

@ -1,5 +1,4 @@
import os import os
import time
from logging import getLogger from logging import getLogger
from PyQt5.QtCore import Qt, QSettings, QTimer, QSize from PyQt5.QtCore import Qt, QSettings, QTimer, QSize

View file

@ -3,6 +3,7 @@ import webbrowser
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QMessageBox, QLabel, QPushButton from PyQt5.QtWidgets import QWidget, QVBoxLayout, QMessageBox, QLabel, QPushButton
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton
from rare.utils.utils import icon
class MiniWidget(QWidget): class MiniWidget(QWidget):
@ -18,7 +19,7 @@ class MiniWidget(QWidget):
self.layout.addWidget(QLabel(self.tr("Logged in as {}").format(username))) self.layout.addWidget(QLabel(self.tr("Logged in as {}").format(username)))
self.open_browser = QPushButton(self.tr("Account settings")) self.open_browser = QPushButton(icon("fa.external-link"), self.tr("Account settings"))
self.open_browser.clicked.connect( self.open_browser.clicked.connect(
lambda: webbrowser.open( lambda: webbrowser.open(
"https://www.epicgames.com/account/personal?productName=epicgames" "https://www.epicgames.com/account/personal?productName=epicgames"

View file

@ -18,7 +18,7 @@ class GameInfoTabs(SideTabWidget):
self.info = GameInfo(self, game_utils) self.info = GameInfo(self, game_utils)
self.addTab(self.info, self.tr("Information")) self.addTab(self.info, self.tr("Information"))
self.settings = GameSettings(self.core, self) self.settings = GameSettings(self)
self.addTab(self.settings, self.tr("Settings")) self.addTab(self.settings, self.tr("Settings"))
self.dlc_list = dlcs self.dlc_list = dlcs
@ -30,12 +30,12 @@ class GameInfoTabs(SideTabWidget):
def update_game(self, app_name: str): def update_game(self, app_name: str):
self.setCurrentIndex(1) self.setCurrentIndex(1)
self.info.update_game(app_name) self.info.update_game(app_name)
self.settings.update_game(app_name) self.settings.load_settings(app_name)
# DLC Tab: Disable if no dlcs available # DLC Tab: Disable if no dlcs available
if ( if (
len(self.dlc_list.get(self.core.get_game(app_name).catalog_item_id, [])) len(self.dlc_list.get(self.core.get_game(app_name).catalog_item_id, []))
== 0 == 0
): ):
self.setTabEnabled(3, False) self.setTabEnabled(3, False)
else: else:

View file

@ -1,75 +1,29 @@
import os import os
import platform import platform
from logging import getLogger from logging import getLogger
from pathlib import Path
from typing import Tuple
from PyQt5.QtCore import QSettings, QThreadPool, Qt from PyQt5.QtCore import Qt, QThreadPool
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import QSizePolicy, QPushButton, QLabel, QFileDialog, QMessageBox
QWidget, from legendary.models.game import Game, InstalledGame
QFileDialog,
QMessageBox,
QLabel,
QPushButton,
QSizePolicy
)
from legendary.core import LegendaryCore
from legendary.models.game import InstalledGame, Game
from rare.components.tabs.settings.linux import LinuxSettings from rare.components.tabs.settings import DefaultGameSettings
from rare.components.tabs.settings.widgets.wrapper import WrapperSettings from rare.components.tabs.settings.widgets.pre_launch import PreLaunchSettings
from rare.ui.components.tabs.games.game_info.game_settings import Ui_GameSettings
from rare.components.tabs.settings.widgets.env_vars import EnvVars
from rare.utils import config_helper from rare.utils import config_helper
from rare.utils.extra_widgets import PathEdit from rare.utils.extra_widgets import PathEdit
from rare.utils.utils import WineResolver, get_raw_save_path from rare.utils.utils import icon, WineResolver, get_raw_save_path
from rare.utils.utils import icon
logger = getLogger("GameSettings") logger = getLogger("GameSettings")
def find_proton_wrappers(): class GameSettings(DefaultGameSettings):
possible_proton_wrappers = []
compatibilitytools_dirs = [
os.path.expanduser("~/.steam/steam/steamapps/common"),
"/usr/share/steam/compatibilitytools.d",
os.path.expanduser("~/.steam/compatibilitytools.d"),
os.path.expanduser("~/.steam/root/compatibilitytools.d"),
]
for c in compatibilitytools_dirs:
if os.path.exists(c):
for i in os.listdir(c):
proton = os.path.join(c, i, "proton")
compatibilitytool = os.path.join(c, i, "compatibilitytool.vdf")
toolmanifest = os.path.join(c, i, "toolmanifest.vdf")
if os.path.exists(proton) and (
os.path.exists(compatibilitytool) or os.path.exists(toolmanifest)
):
wrapper = f'"{proton}" run'
possible_proton_wrappers.append(wrapper)
if not possible_proton_wrappers:
logger.warning("Unable to find any Proton version")
return possible_proton_wrappers
class GameSettings(QWidget, Ui_GameSettings):
game: Game game: Game
igame: InstalledGame igame: InstalledGame
# variable to no update when changing game def __init__(self, parent=None):
change = False super(GameSettings, self).__init__(False, parent)
self.pre_launch_settings = PreLaunchSettings()
def __init__(self, core: LegendaryCore, parent):
super(GameSettings, self).__init__(parent=parent)
self.setupUi(self)
self.core = core
self.settings = QSettings()
self.wrapper_settings = WrapperSettings()
self.launch_settings_group.layout().addRow( self.launch_settings_group.layout().addRow(
QLabel("Wrapper"), self.wrapper_settings QLabel(self.tr("Pre launch command")), self.pre_launch_settings
) )
self.cloud_save_path_edit = PathEdit( self.cloud_save_path_edit = PathEdit(
@ -103,49 +57,15 @@ class GameSettings(QWidget, Ui_GameSettings):
f"{self.game.app_name}/auto_sync_cloud", self.cloud_sync.isChecked() f"{self.game.app_name}/auto_sync_cloud", self.cloud_sync.isChecked()
) )
) )
self.override_exe_edit.textChanged.connect(
lambda text: self.save_line_edit("override_exe", text)
)
self.launch_params.textChanged.connect( self.launch_params.textChanged.connect(
lambda x: self.save_line_edit("start_params", x) lambda x: self.save_line_edit("start_params", x)
) )
if platform.system() != "Windows":
self.possible_proton_wrappers = find_proton_wrappers()
self.proton_wrapper.addItems(self.possible_proton_wrappers)
self.proton_wrapper.currentIndexChanged.connect(self.change_proton)
self.proton_prefix = PathEdit(
file_type=QFileDialog.DirectoryOnly,
edit_func=self.proton_prefix_edit,
save_func=self.proton_prefix_save,
placeholder=self.tr("Please select path for proton prefix")
)
self.proton_prefix_layout.addWidget(self.proton_prefix)
self.linux_settings = LinuxAppSettings()
# FIXME: Remove the spacerItem and margins from the linux settings
# FIXME: This should be handled differently at soem point in the future
self.linux_settings.layout().setContentsMargins(0, 0, 0, 0)
for item in [
self.linux_settings.layout().itemAt(idx)
for idx in range(self.linux_settings.layout().count())
]:
if item.spacerItem():
self.linux_settings.layout().removeItem(item)
del item
# FIXME: End of FIXME
self.linux_settings_layout.addWidget(self.linux_settings)
self.linux_settings_layout.setAlignment(Qt.AlignTop)
else:
self.linux_settings_widget.setVisible(False)
self.game_settings_layout.setAlignment(Qt.AlignTop) self.game_settings_layout.setAlignment(Qt.AlignTop)
self.linux_settings.mangohud.set_wrapper_activated.connect(
lambda active: self.wrapper_settings.add_wrapper("mangohud")
if active else self.wrapper_settings.delete_wrapper("mangohud"))
self.env_vars = EnvVars(self)
self.game_settings_contents_layout.addWidget(self.env_vars)
def compute_save_path(self): def compute_save_path(self):
if ( if (
self.core.is_installed(self.game.app_name) self.core.is_installed(self.game.app_name)
@ -155,12 +75,17 @@ class GameSettings(QWidget, Ui_GameSettings):
new_path = self.core.get_save_path(self.game.app_name) new_path = self.core.get_save_path(self.game.app_name)
except Exception as e: except Exception as e:
logger.warning(str(e)) logger.warning(str(e))
resolver = WineResolver(
get_raw_save_path(self.game), self.game.app_name
)
if not resolver.wine_env.get("WINEPREFIX"):
self.cloud_save_path_edit.setText("")
QMessageBox.warning(self, "Warning", "No wine prefix selected. Please set it in settings")
return
self.cloud_save_path_edit.setText(self.tr("Loading")) self.cloud_save_path_edit.setText(self.tr("Loading"))
self.cloud_save_path_edit.setDisabled(True) self.cloud_save_path_edit.setDisabled(True)
self.compute_save_path_button.setDisabled(True) self.compute_save_path_button.setDisabled(True)
resolver = WineResolver(
get_raw_save_path(self.game), self.game.app_name, self.core
)
app_name = self.game.app_name[:] app_name = self.game.app_name[:]
resolver.signals.result_ready.connect( resolver.signals.result_ready.connect(
lambda x: self.wine_resolver_finished(x, app_name) lambda x: self.wine_resolver_finished(x, app_name)
@ -228,61 +153,9 @@ class GameSettings(QWidget, Ui_GameSettings):
config_helper.remove_option(self.game.app_name, option) config_helper.remove_option(self.game.app_name, option)
config_helper.save_config() config_helper.save_config()
def change_proton(self, i): def load_settings(self, app_name):
if self.change:
# First combo box entry: Don't use Proton
if i == 0:
self.wrapper_settings.delete_wrapper("proton")
config_helper.remove_option(self.game.app_name, "no_wine")
config_helper.remove_option(f"{self.game.app_name}.env", "STEAM_COMPAT_DATA_PATH")
config_helper.remove_option(f"{self.game.app_name}.env", "STEAM_COMPAT_CLIENT_INSTALL_PATH")
self.proton_prefix.setEnabled(False)
# lk: TODO: This has to be fixed properly.
# lk: It happens because of the widget update. Mask it for now behind disabling the save button
self.linux_settings.wine_groupbox.setEnabled(True)
else:
self.proton_prefix.setEnabled(True)
self.linux_settings.wine_groupbox.setEnabled(False)
wrapper = self.possible_proton_wrappers[i - 1]
self.wrapper_settings.add_wrapper(wrapper)
config_helper.add_option(self.game.app_name, "no_wine", "true")
config_helper.add_option(
f"{self.game.app_name}.env",
"STEAM_COMPAT_DATA_PATH",
os.path.expanduser("~/.proton"),
)
config_helper.add_option(
f"{self.game.app_name}.env",
"STEAM_COMPAT_CLIENT_INSTALL_PATH",
str(Path.home().joinpath(".steam", "steam"))
)
self.proton_prefix.setText(os.path.expanduser("~/.proton"))
# Don't use Wine
self.linux_settings.wine_exec.setText("")
self.linux_settings.wine_prefix.setText("")
config_helper.save_config()
def proton_prefix_edit(self, text: str) -> Tuple[bool, str, str]:
if not text:
text = os.path.expanduser("~/.proton")
return True, text, ""
parent = os.path.dirname(text)
return os.path.exists(parent), text, PathEdit.reasons.dir_not_exist
def proton_prefix_save(self, text: str):
config_helper.add_option(
f"{self.game.app_name}.env", "STEAM_COMPAT_DATA_PATH", text
)
config_helper.save_config()
def update_game(self, app_name: str):
self.change = False self.change = False
super(GameSettings, self).load_settings(app_name)
self.game = self.core.get_game(app_name) self.game = self.core.get_game(app_name)
self.igame = self.core.get_installed_game(self.game.app_name) self.igame = self.core.get_installed_game(self.game.app_name)
if self.igame: if self.igame:
@ -314,34 +187,12 @@ class GameSettings(QWidget, Ui_GameSettings):
self.skip_update.setCurrentIndex(0) self.skip_update.setCurrentIndex(0)
self.title.setTitle(self.game.app_title) self.title.setTitle(self.game.app_title)
self.wrapper_settings.load_settings(app_name)
if platform.system() != "Windows": if platform.system() != "Windows":
self.linux_settings.update_game(app_name)
if self.igame and self.igame.platform == "Mac": if self.igame and self.igame.platform == "Mac":
self.linux_settings_widget.setVisible(False) self.linux_settings_widget.setVisible(False)
else: else:
self.linux_settings_widget.setVisible(True) self.linux_settings_widget.setVisible(True)
proton = self.wrapper_settings.wrappers.get("proton", None)
if proton:
proton = proton.text.replace('"', "")
self.proton_prefix.setEnabled(True)
self.proton_wrapper.setCurrentText(
f'"{proton.replace(" run", "")}" run'
)
proton_prefix = self.core.lgd.config.get(
f"{app_name}.env",
"STEAM_COMPAT_DATA_PATH",
fallback=Path.home().joinpath(".proton"),
)
self.proton_prefix.setText(proton_prefix)
self.linux_settings.wine_groupbox.setEnabled(False)
else:
self.proton_wrapper.setCurrentIndex(0)
self.proton_prefix.setEnabled(False)
self.linux_settings.wine_groupbox.setEnabled(True)
if not self.game.supports_cloud_saves: if not self.game.supports_cloud_saves:
self.cloud_group.setEnabled(False) self.cloud_group.setEnabled(False)
self.cloud_save_path_edit.setText("") self.cloud_save_path_edit.setText("")
@ -362,21 +213,7 @@ class GameSettings(QWidget, Ui_GameSettings):
self.override_exe_edit.setText( self.override_exe_edit.setText(
self.core.lgd.config.get(self.game.app_name, "override_exe", fallback="") self.core.lgd.config.get(self.game.app_name, "override_exe", fallback="")
) )
self.pre_launch_settings.load_settings(app_name)
self.change = True self.change = True
self.env_vars.update_game(app_name)
class LinuxAppSettings(LinuxSettings):
def __init__(self):
super(LinuxAppSettings, self).__init__()
def update_game(self, app_name):
self.name = app_name
self.wine_prefix.setText(self.load_prefix())
self.wine_exec.setText(self.load_setting(self.name, "wine_executable"))
self.dxvk.load_settings(self.name)
self.mangohud.load_settings(self.name)

View file

@ -2,18 +2,18 @@ import datetime
import os import os
import platform import platform
import shutil import shutil
import webbrowser
from dataclasses import dataclass from dataclasses import dataclass
from logging import getLogger from logging import getLogger
from PyQt5.QtCore import QObject, QSettings, QProcess, QProcessEnvironment, pyqtSignal from PyQt5.QtCore import QObject, QSettings, QProcess, QProcessEnvironment, pyqtSignal, QUrl
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import QMessageBox, QPushButton from PyQt5.QtWidgets import QMessageBox, QPushButton
from legendary.models.game import LaunchParameters, InstalledGame
from legendary.models.game import LaunchParameters
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
from rare.components.dialogs.uninstall_dialog import UninstallDialog from rare.components.dialogs.uninstall_dialog import UninstallDialog
from rare.components.extra.console import ConsoleWindow from rare.components.extra.console import ConsoleWindow
from rare.components.tabs.games import CloudSaveUtils from rare.components.tabs.games import CloudSaveUtils
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
from rare.utils import legendary_utils from rare.utils import legendary_utils
from rare.utils.meta import RareGameMeta from rare.utils.meta import RareGameMeta
@ -97,6 +97,7 @@ class GameUtils(QObject):
except ValueError: except ValueError:
logger.info("Cancel startup") logger.info("Cancel startup")
self.sync_finished(app_name) self.sync_finished(app_name)
return
except AssertionError: except AssertionError:
dont_sync_after_finish = True dont_sync_after_finish = True
else: else:
@ -167,162 +168,32 @@ class GameUtils(QObject):
) )
return return
process = GameProcess(app_name) def _launch_real():
process.setProcessChannelMode(GameProcess.MergedChannels) process = self._get_process(app_name, env)
self.console.log("\n"*2)
if game.third_party_store != "Origin":
self._launch_game(igame, process, offline, skip_update_check, ask_always_sync)
else:
self._launch_origin(app_name, process)
if game.third_party_store != "Origin": env = self.core.get_app_environment(app_name, wine_pfx=wine_pfx)
if not offline: pre_cmd, wait = self.core.get_pre_launch_command(app_name)
if not skip_update_check and not self.core.is_noupdate_game(app_name): if pre_cmd:
# check updates pre_cmd = pre_cmd.split()
try: pre_proc = self._launch_pre_command(env)
latest = self.core.get_asset( self.console.log("\n"*2)
app_name, igame.platform, update=False pre_proc.start(pre_cmd[0], pre_cmd[1:])
) if wait:
except ValueError: pre_proc.finished.connect(_launch_real)
self.finished.emit(app_name, self.tr("Metadata doesn't exist"))
return
else:
if latest.build_version != igame.version:
self.finished.emit(app_name, self.tr("Please update game"))
return
params: LaunchParameters = self.core.get_launch_parameters(
app_name=app_name, offline=offline, wine_bin=wine_bin, wine_pfx=wine_pfx
)
full_params = list()
if os.environ.get("container") == "flatpak":
full_params.extend(["flatpak-spawn", "--host"])
full_params.extend(params.launch_command)
full_params.append(
os.path.join(params.game_directory, params.game_executable)
)
full_params.extend(params.game_parameters)
full_params.extend(params.egl_parameters)
full_params.extend(params.user_parameters)
process.setWorkingDirectory(params.working_directory)
environment = QProcessEnvironment()
full_env = os.environ.copy()
full_env.update(params.environment)
for env, value in full_env.items():
environment.insert(env, value)
if platform.system() != "Windows":
# wine prefixes
for env in ["STEAM_COMPAT_DATA_PATH", "WINEPREFIX"]:
if val := full_env.get(env):
if not os.path.exists(val):
try:
os.makedirs(val)
except PermissionError as e:
logger.error(str(e))
QMessageBox.warning(
None,
"Error",
self.tr(
"Error while launching {}. No permission to create {} for {}"
).format(game.app_title, val, env),
)
process.deleteLater()
return
# check wine executable
if shutil.which(full_params[0]) is None:
# wine binary does not exist
QMessageBox.warning(
None,
"Warning",
self.tr(
"'{}' does not exist. Please change it in Settings"
).format(full_params[0]),
)
process.deleteLater()
return
if shutil.which(full_params[0]) is None:
QMessageBox.warning(None, "Warning", self.tr("'{}' does not exist").format(full_params[0]))
return return
_launch_real()
process.setProcessEnvironment(environment)
process.game_finished.connect(self.game_finished)
running_game = RunningGameModel(
process=process, app_name=app_name, always_ask_sync=ask_always_sync
)
process.start(full_params[0], full_params[1:])
self.game_launched.emit(app_name)
self.signals.set_discord_rpc.emit(app_name)
logger.info(f"{game.app_title} launched")
self.running_games[game.app_name] = running_game
else:
origin_uri = self.core.get_origin_uri(game.app_name, self.args.offline)
logger.info("Launch Origin Game: ")
if platform.system() == "Windows":
webbrowser.open(origin_uri)
self.finished.emit(app_name, "")
return
wine_pfx = self.core.lgd.config.get(
game.app_name, "wine_prefix", fallback=os.path.expanduser("~/.wine")
)
if not wine_bin:
wine_bin = self.core.lgd.config.get(
game.app_name, "wine_executable", fallback="/usr/bin/wine"
)
if shutil.which(wine_bin) is None:
# wine binary does not exist
QMessageBox.warning(
None,
"Warning",
self.tr(
"Wine executable '{}' does not exist. Please change it in Settings"
).format(wine_bin),
)
process.deleteLater()
return
env = self.core.get_app_environment(game.app_name, wine_pfx=wine_pfx)
if not env.get("WINEPREFIX") and not os.path.exists("/usr/bin/wine"):
logger.error(
f"In order to launch Origin correctly you must specify the wine binary and prefix "
f"to use in the configuration file or command line. See the README for details."
)
self.finished.emit(
app_name,
self.tr("No wine executable selected. Please set it in settings"),
)
return
environment = QProcessEnvironment()
for e in env:
environment.insert(e, env[e])
process.setProcessEnvironment(environment)
process.finished.connect(lambda x: self.game_finished(x, game.app_name))
process.start(wine_bin, [origin_uri])
if QSettings().value("show_console", False, bool):
self.console.show()
process.readyReadStandardOutput.connect(
lambda: self.console.log(
str(process.readAllStandardOutput().data(), "utf-8", "ignore")
)
)
process.readyReadStandardError.connect(
lambda: self.console.error(
str(process.readAllStandardError().data(), "utf-8", "ignore")
)
)
def game_finished(self, exit_code, app_name): def game_finished(self, exit_code, app_name):
logger.info(f"Game exited with exit code: {exit_code}") logger.info(f"Game exited with exit code: {exit_code}")
self.console.log(f"Game exited with code: {exit_code}")
self.signals.set_discord_rpc.emit("") self.signals.set_discord_rpc.emit("")
is_origin = self.core.get_game(app_name).third_party_store == "Origin" is_origin = self.core.get_game(app_name).third_party_store == "Origin"
if exit_code == 53 and is_origin: if exit_code == 1 and is_origin:
msg_box = QMessageBox() msg_box = QMessageBox()
msg_box.setText( msg_box.setText(
self.tr( self.tr(
@ -334,7 +205,8 @@ class GameUtils(QObject):
resp = msg_box.exec() resp = msg_box.exec()
# click install button # click install button
if resp == 0: if resp == 0:
webbrowser.open("https://www.dm.origin.com/download") QDesktopServices.openUrl(QUrl("https://www.dm.origin.com/download"))
return
if exit_code != 0: if exit_code != 0:
QMessageBox.warning( QMessageBox.warning(
None, None,
@ -351,16 +223,14 @@ class GameUtils(QObject):
self.running_games.pop(app_name) self.running_games.pop(app_name)
self.finished.emit(app_name, "") self.finished.emit(app_name, "")
if QSettings().value("show_console", False, bool):
self.console.log(f"Game exited with code: {exit_code}")
if self.core.get_game(app_name).supports_cloud_saves: if self.core.get_game(app_name).supports_cloud_saves:
if exit_code != 0: if exit_code != 0:
r = QMessageBox.question( r = QMessageBox.question(
None, None,
"Question", "Question",
self.tr( self.tr(
"Game exited with code {}, which is not a normal code. It could be caused by a crash. Do you want to sync cloud saves" "Game exited with code {}, which is not a normal code. "
"It could be caused by a crash. Do you want to sync cloud saves"
).format(exit_code), ).format(exit_code),
buttons=QMessageBox.Yes | QMessageBox.No, buttons=QMessageBox.Yes | QMessageBox.No,
defaultButton=QMessageBox.Yes, defaultButton=QMessageBox.Yes,
@ -369,6 +239,145 @@ class GameUtils(QObject):
return return
self.cloud_save_utils.game_finished(app_name, game.always_ask_sync) self.cloud_save_utils.game_finished(app_name, game.always_ask_sync)
def _launch_pre_command(self, env: dict):
proc = QProcess()
environment = QProcessEnvironment()
for e in env:
environment.insert(e, env[e])
proc.setProcessEnvironment(environment)
proc.readyReadStandardOutput.connect(
lambda: self.console.log(
str(proc.readAllStandardOutput().data(), "utf-8", "ignore")
)
)
proc.readyReadStandardError.connect(
lambda: self.console.error(
str(proc.readAllStandardError().data(), "utf-8", "ignore")
)
)
return proc
def _get_process(self, app_name, env):
process = GameProcess(app_name)
environment = QProcessEnvironment()
for e in env:
environment.insert(e, env[e])
process.setProcessEnvironment(environment)
process.readyReadStandardOutput.connect(
lambda: self.console.log(
str(process.readAllStandardOutput().data(), "utf-8", "ignore")
)
)
process.readyReadStandardError.connect(
lambda: self.console.error(
str(process.readAllStandardError().data(), "utf-8", "ignore")
)
)
process.finished.connect(lambda x: self.game_finished(x, app_name))
process.stateChanged.connect(
lambda state: self.console.show()
if (state == QProcess.Running
and QSettings().value("show_console", False, bool))
else None
)
return process
def _launch_origin(self, app_name, process: QProcess):
origin_uri = self.core.get_origin_uri(app_name, self.args.offline)
logger.info("Launch Origin Game: ")
if platform.system() == "Windows":
QDesktopServices.openUrl(QUrl(origin_uri))
self.finished.emit(app_name, "")
return
command = self.core.get_app_launch_command(app_name)
if not os.path.exists(command[0]) and shutil.which(command[0]) is None:
# wine binary does not exist
QMessageBox.warning(
None, "Warning",
self.tr(
"'{}' does not exist. Please change it in Settings"
).format(command[0]),
)
process.deleteLater()
return
command.append(origin_uri)
process.start(command[0], command[1:])
def _launch_game(self, igame: InstalledGame, process: QProcess, offline: bool,
skip_update_check: bool, ask_always_sync: bool):
if not offline: # skip for update
if not skip_update_check and not self.core.is_noupdate_game(igame.app_name):
# check updates
try:
latest = self.core.get_asset(
igame.app_name, igame.platform, update=False
)
except ValueError:
self.finished.emit(igame.app_name, self.tr("Metadata doesn't exist"))
return
else:
if latest.build_version != igame.version:
self.finished.emit(igame.app_name, self.tr("Please update game"))
return
params: LaunchParameters = self.core.get_launch_parameters(
app_name=igame.app_name, offline=offline
)
full_params = list()
if os.environ.get("container") == "flatpak":
full_params.extend(["flatpak-spawn", "--host"])
full_params.extend(params.launch_command)
full_params.append(
os.path.join(params.game_directory, params.game_executable)
)
full_params.extend(params.game_parameters)
full_params.extend(params.egl_parameters)
full_params.extend(params.user_parameters)
process.setWorkingDirectory(params.working_directory)
if platform.system() != "Windows":
# wine prefixes
for env in ["STEAM_COMPAT_DATA_PATH", "WINEPREFIX"]:
if val := process.processEnvironment().value(env, ""):
if not os.path.exists(val):
try:
os.makedirs(val)
except PermissionError as e:
logger.error(str(e))
QMessageBox.warning(
None,
"Error",
self.tr(
"Error while launching {}. No permission to create {} for {}"
).format(igame.title, val, env),
)
process.deleteLater()
return
# check wine executable
if shutil.which(full_params[0]) is None:
QMessageBox.warning(None, "Warning", self.tr("'{}' does not exist").format(full_params[0]))
return
running_game = RunningGameModel(
process=process, app_name=igame.app_name, always_ask_sync=ask_always_sync
)
process.start(full_params[0], full_params[1:])
self.game_launched.emit(igame.app_name)
self.signals.set_discord_rpc.emit(igame.app_name)
logger.info(f"{igame.title} launched")
self.running_games[igame.app_name] = running_game
def sync_finished(self, app_name): def sync_finished(self, app_name):
if app_name in self.launch_queue.keys(): if app_name in self.launch_queue.keys():
self.cloud_save_finished.emit(app_name) self.cloud_save_finished.emit(app_name)

View file

@ -28,7 +28,7 @@ class GameListHeadBar(QWidget):
self.filter = QComboBox() self.filter = QComboBox()
self.filter.addItems( self.filter.addItems(
[ [
self.tr("All"), self.tr("All games"),
self.tr("Installed only"), self.tr("Installed only"),
self.tr("Offline Games"), self.tr("Offline Games"),
]) ])

View file

@ -48,7 +48,7 @@ class EGLSyncGroup(QGroupBox, Ui_EGLSyncGroup):
if not self.core.egl.programdata_path: if not self.core.egl.programdata_path:
self.egl_path_info.setText(self.tr("Updating...")) self.egl_path_info.setText(self.tr("Updating..."))
wine_resolver = WineResolver( wine_resolver = WineResolver(
PathSpec.egl_programdata, "default", self.core PathSpec.egl_programdata, "default"
) )
wine_resolver.signals.result_ready.connect(self.wine_resolver_cb) wine_resolver.signals.result_ready.connect(self.wine_resolver_cb)
self.thread_pool.start(wine_resolver) self.thread_pool.start(wine_resolver)

View file

@ -87,7 +87,7 @@ class ImportGroup(QGroupBox, Ui_ImportGroup):
self.path_edit_layout.addWidget(self.path_edit) self.path_edit_layout.addWidget(self.path_edit)
self.app_name = IndicatorLineEdit( self.app_name = IndicatorLineEdit(
ph_text=self.tr("Use in case the app name was not found automatically"), placeholder=self.tr("Use in case the app name was not found automatically"),
completer=AppNameCompleter( completer=AppNameCompleter(
app_names=[ app_names=[
(i.app_name, i.app_title) for i in self.api_results.game_list (i.app_name, i.app_title) for i in self.api_results.game_list

View file

@ -3,22 +3,21 @@ import platform
from rare.utils.extra_widgets import SideTabWidget from rare.utils.extra_widgets import SideTabWidget
from .about import About from .about import About
from .legendary import LegendarySettings from .legendary import LegendarySettings
from .linux import LinuxSettings from rare.components.tabs.settings.widgets.linux import LinuxSettings
from .rare import RareSettings from .rare import RareSettings
from .default_game_settings import DefaultGameSettings
class SettingsTab(SideTabWidget): class SettingsTab(SideTabWidget):
def __init__(self, parent=None): def __init__(self, parent=None):
super(SettingsTab, self).__init__(parent=parent) super(SettingsTab, self).__init__(parent=parent)
about_tab = 2 about_tab = 3
self.rare_settings = RareSettings() self.rare_settings = RareSettings()
self.addTab(self.rare_settings, "Rare") self.addTab(self.rare_settings, "Rare")
self.addTab(LegendarySettings(), "Legendary") self.addTab(LegendarySettings(), "Legendary")
if platform.system() != "Windows": self.addTab(DefaultGameSettings(True, self), self.tr("Games"), self.tr("Default game settings"))
self.addTab(LinuxSettings(), "Linux")
about_tab = 3
self.about = About() self.about = About()
self.addTab(self.about, "About", "About") self.addTab(self.about, "About", "About")

View file

@ -0,0 +1,127 @@
import platform
from logging import getLogger
from PyQt5.QtCore import QSettings, Qt
from PyQt5.QtWidgets import (
QWidget,
QLabel
)
from rare.components.tabs.settings.widgets.env_vars import EnvVars
from rare.components.tabs.settings.widgets.linux import LinuxSettings
from rare.components.tabs.settings.widgets.proton import ProtonSettings
from rare.components.tabs.settings.widgets.wrapper import WrapperSettings
from rare.shared import LegendaryCoreSingleton
from rare.ui.components.tabs.games.game_info.game_settings import Ui_GameSettings
from rare.utils import config_helper
logger = getLogger("GameSettings")
class DefaultGameSettings(QWidget, Ui_GameSettings):
# variable to no update when changing game
change = False
app_name: str
def __init__(self, is_default, parent=None):
super(DefaultGameSettings, self).__init__(parent=parent)
self.setupUi(self)
self.core = LegendaryCoreSingleton()
self.settings = QSettings()
self.wrapper_settings = WrapperSettings()
self.launch_settings_group.layout().addRow(
QLabel("Wrapper"), self.wrapper_settings
)
if platform.system() != "Windows":
self.linux_settings = LinuxAppSettings()
self.proton_settings = ProtonSettings(self.linux_settings, self.wrapper_settings)
self.game_settings_layout.replaceWidget(self.proton_placeholder, self.proton_settings)
self.proton_placeholder.deleteLater()
# FIXME: Remove the spacerItem and margins from the linux settings
# FIXME: This should be handled differently at soem point in the future
self.linux_settings.layout().setContentsMargins(0, 0, 0, 0)
for item in [
self.linux_settings.layout().itemAt(idx)
for idx in range(self.linux_settings.layout().count())
]:
if item.spacerItem():
self.linux_settings.layout().removeItem(item)
del item
# FIXME: End of FIXME
self.linux_settings_layout.addWidget(self.linux_settings)
self.linux_settings_layout.setAlignment(Qt.AlignTop)
self.game_settings_layout.setAlignment(Qt.AlignTop)
self.linux_settings.mangohud.set_wrapper_activated.connect(
lambda active: self.wrapper_settings.add_wrapper("mangohud")
if active else self.wrapper_settings.delete_wrapper("mangohud"))
else:
self.linux_settings_widget.setVisible(False)
self.env_vars = EnvVars(self)
self.game_settings_layout.addWidget(self.env_vars)
if is_default:
for i in range(4): # remove some entries which are not supported by default
self.launch_settings_layout.removeRow(0)
self.cloud_group.deleteLater()
self.load_settings("default")
def save_line_edit(self, option, value):
if value:
config_helper.add_option(self.game.app_name, option, value)
else:
config_helper.remove_option(self.game.app_name, option)
config_helper.save_config()
if option == "wine_prefix":
if self.game.supports_cloud_saves:
self.compute_save_path()
def update_combobox(self, i, option):
if self.change:
# remove section
if i:
if i == 1:
config_helper.add_option(self.game.app_name, option, "true")
if i == 2:
config_helper.add_option(self.game.app_name, option, "false")
else:
config_helper.remove_option(self.game.app_name, option)
config_helper.save_config()
def load_settings(self, app_name):
self.app_name = app_name
self.wrapper_settings.load_settings(app_name)
if platform.system() != "Windows":
self.linux_settings.update_game(app_name)
proton = self.wrapper_settings.wrappers.get("proton", "")
if proton:
proton = proton.text
self.proton_settings.load_settings(app_name, proton)
if proton:
self.linux_settings.wine_groupbox.setEnabled(False)
else:
self.linux_settings.wine_groupbox.setEnabled(True)
self.env_vars.update_game(app_name)
class LinuxAppSettings(LinuxSettings):
def __init__(self):
super(LinuxAppSettings, self).__init__()
def update_game(self, app_name):
self.name = app_name
self.wine_prefix.setText(self.load_prefix())
self.wine_exec.setText(self.load_setting(self.name, "wine_executable"))
self.dxvk.load_settings(self.name)
self.mangohud.load_settings(self.name)

View file

@ -1,13 +1,14 @@
import platform
import re import re
from logging import getLogger from logging import getLogger
from typing import Tuple from typing import Tuple
from PyQt5.QtCore import Qt, QRunnable, QObject, pyqtSignal, QThreadPool from PyQt5.QtCore import Qt, QRunnable, QObject, pyqtSignal, QThreadPool, QSettings
from PyQt5.QtWidgets import QSizePolicy, QWidget, QFileDialog, QMessageBox from PyQt5.QtWidgets import QSizePolicy, QWidget, QFileDialog, QMessageBox
from rare.shared import LegendaryCoreSingleton
from rare.components.tabs.settings.widgets.eos import EosWidget from rare.components.tabs.settings.widgets.eos import EosWidget
from rare.components.tabs.settings.widgets.ubisoft_activation import UbiActivationHelper from rare.components.tabs.settings.widgets.ubisoft_activation import UbiActivationHelper
from rare.shared import LegendaryCoreSingleton
from rare.ui.components.tabs.settings.legendary import Ui_LegendarySettings from rare.ui.components.tabs.settings.legendary import Ui_LegendarySettings
from rare.utils.extra_widgets import PathEdit, IndicatorLineEdit from rare.utils.extra_widgets import PathEdit, IndicatorLineEdit
from rare.utils.utils import get_size from rare.utils.utils import get_size
@ -38,6 +39,7 @@ class LegendarySettings(QWidget, Ui_LegendarySettings):
def __init__(self, parent=None): def __init__(self, parent=None):
super(LegendarySettings, self).__init__(parent=parent) super(LegendarySettings, self).__init__(parent=parent)
self.setupUi(self) self.setupUi(self)
self.settings = QSettings()
self.core = LegendaryCoreSingleton() self.core = LegendaryCoreSingleton()
@ -89,6 +91,12 @@ class LegendarySettings(QWidget, Ui_LegendarySettings):
self.eos_widget = EosWidget() self.eos_widget = EosWidget()
self.left_layout.insertWidget(3, self.eos_widget, alignment=Qt.AlignTop) self.left_layout.insertWidget(3, self.eos_widget, alignment=Qt.AlignTop)
self.win32_cb.setChecked(self.settings.value("win32_meta", False, bool))
self.win32_cb.stateChanged.connect(lambda: self.settings.setValue("win32_meta", self.win32_cb.isChecked()))
self.mac_cb.setChecked(self.settings.value("mac_meta", platform.system() == "Darwin", bool))
self.mac_cb.stateChanged.connect(lambda: self.settings.setValue("mac_meta", self.mac_cb.isChecked()))
self.refresh_game_meta_btn.clicked.connect(self.refresh_game_meta) self.refresh_game_meta_btn.clicked.connect(self.refresh_game_meta)
def refresh_game_meta(self): def refresh_game_meta(self):

View file

@ -1,4 +1,5 @@
import os import os
import shutil
from logging import getLogger from logging import getLogger
from PyQt5.QtWidgets import QFileDialog, QWidget from PyQt5.QtWidgets import QFileDialog, QWidget
@ -37,6 +38,7 @@ class LinuxSettings(QWidget, Ui_LinuxSettings):
self.load_setting(self.name, "wine_executable"), self.load_setting(self.name, "wine_executable"),
file_type=QFileDialog.ExistingFile, file_type=QFileDialog.ExistingFile,
name_filter="Wine executable (wine wine64)", name_filter="Wine executable (wine wine64)",
edit_func=lambda text: (os.path.exists(text) or not text, text, PathEdit.reasons.dir_not_exist),
save_func=lambda text: self.save_setting( save_func=lambda text: self.save_setting(
text, section=self.name, setting="wine_executable" text, section=self.name, setting="wine_executable"
), ),
@ -46,9 +48,15 @@ class LinuxSettings(QWidget, Ui_LinuxSettings):
# dxvk # dxvk
self.dxvk = DxvkSettings() self.dxvk = DxvkSettings()
self.overlay_layout.addWidget(self.dxvk) self.overlay_layout.addWidget(self.dxvk)
self.dxvk.load_settings(self.name)
self.mangohud = MangoHudSettings() self.mangohud = MangoHudSettings()
self.overlay_layout.addWidget(self.mangohud) self.overlay_layout.addWidget(self.mangohud)
self.mangohud.load_settings(self.name)
if not shutil.which("mangohud"):
self.mangohud.setDisabled(True)
self.mangohud.setToolTip(self.tr("Mangohud is not installed or not in path"))
def load_prefix(self) -> str: def load_prefix(self) -> str:
return self.load_setting( return self.load_setting(

View file

@ -0,0 +1,61 @@
import os
import shutil
from typing import Tuple
from PyQt5.QtWidgets import QHBoxLayout, QCheckBox, QFileDialog
from rare.shared import LegendaryCoreSingleton
from rare.utils import config_helper
from rare.utils.extra_widgets import IndicatorLineEdit, PathEdit
class PreLaunchSettings(QHBoxLayout):
app_name: str
def __init__(self):
super(PreLaunchSettings, self).__init__()
self.core = LegendaryCoreSingleton()
self.edit = PathEdit(
path="",
placeholder=self.tr("Path to script"),
file_type=QFileDialog.ExistingFile,
edit_func=self.edit_command,
save_func=self.save_pre_launch_command,
)
self.layout().addWidget(self.edit)
self.wait_check = QCheckBox(self.tr("Wait for finish"))
self.layout().addWidget(self.wait_check)
self.wait_check.stateChanged.connect(self.save_wait_finish)
def edit_command(self, text: str) -> Tuple[bool, str, str]:
if not text.strip():
return True, text, ""
if not os.path.isfile(text.split()[0]) and not shutil.which(text.split()[0]):
return False, text, IndicatorLineEdit.reasons.file_not_exist
else:
return True, text, ""
def save_pre_launch_command(self, text):
if text:
config_helper.add_option(self.app_name, "pre_launch_command", text)
self.wait_check.setDisabled(False)
else:
config_helper.remove_option(self.app_name, "pre_launch_command")
self.wait_check.setDisabled(True)
config_helper.remove_option(self.app_name, "pre_launch_wait")
def save_wait_finish(self):
config_helper.add_option(self.app_name, "pre_launch_wait", str(self.wait_check.isChecked()).lower())
def load_settings(self, app_name):
self.app_name = app_name
command = self.core.lgd.config.get(app_name, "pre_launch_command", fallback="")
self.edit.setText(command)
wait = self.core.lgd.config.getboolean(app_name, "pre_launch_wait", fallback=False)
self.wait_check.setChecked(wait)
self.wait_check.setEnabled(bool(command))

View file

@ -0,0 +1,141 @@
import os
from logging import getLogger
from pathlib import Path
from typing import Tuple
from PyQt5.QtWidgets import QGroupBox, QFileDialog
from rare.components.tabs.settings import LinuxSettings
from .wrapper import WrapperSettings
from rare.ui.components.tabs.settings.proton import Ui_ProtonSettings
from rare.utils import config_helper
from rare.utils.extra_widgets import PathEdit
from rare.shared import LegendaryCoreSingleton
logger = getLogger("Proton")
def find_proton_combos():
possible_proton_combos = []
compatibilitytools_dirs = [
os.path.expanduser("~/.steam/steam/steamapps/common"),
"/usr/share/steam/compatibilitytools.d",
os.path.expanduser("~/.steam/compatibilitytools.d"),
os.path.expanduser("~/.steam/root/compatibilitytools.d"),
]
for c in compatibilitytools_dirs:
if os.path.exists(c):
for i in os.listdir(c):
proton = os.path.join(c, i, "proton")
compatibilitytool = os.path.join(c, i, "compatibilitytool.vdf")
toolmanifest = os.path.join(c, i, "toolmanifest.vdf")
if os.path.exists(proton) and (
os.path.exists(compatibilitytool) or os.path.exists(toolmanifest)
):
wrapper = f'"{proton}" run'
possible_proton_combos.append(wrapper)
if not possible_proton_combos:
logger.warning("Unable to find any Proton version")
return possible_proton_combos
class ProtonSettings(QGroupBox, Ui_ProtonSettings):
app_name: str
changeable = True
def __init__(self, linux_settings: LinuxSettings, wrapper_settings: WrapperSettings):
super(ProtonSettings, self).__init__()
self.setupUi(self)
self._linux_settings = linux_settings
self._wrapper_settings = wrapper_settings
self.core = LegendaryCoreSingleton()
self.possible_proton_combos = find_proton_combos()
self.proton_combo.addItems(self.possible_proton_combos)
self.proton_combo.currentIndexChanged.connect(self.change_proton)
self.proton_prefix = PathEdit(
file_type=QFileDialog.DirectoryOnly,
edit_func=self.proton_prefix_edit,
save_func=self.proton_prefix_save,
placeholder=self.tr("Please select path for proton prefix")
)
self.layout().replaceWidget(self.placeholder_prefix_edit, self.proton_prefix)
self.placeholder_prefix_edit.deleteLater()
def change_proton(self, i):
if not self.changeable:
return
# First combo box entry: Don't use Proton
if i == 0:
self._wrapper_settings.delete_wrapper("proton")
config_helper.remove_option(self.app_name, "no_wine")
config_helper.remove_option(f"{self.app_name}.env", "STEAM_COMPAT_DATA_PATH")
config_helper.remove_option(f"{self.app_name}.env", "STEAM_COMPAT_CLIENT_INSTALL_PATH")
self.proton_prefix.setEnabled(False)
# lk: TODO: This has to be fixed properly.
# lk: It happens because of the widget update. Mask it for now behind disabling the save button
self._linux_settings.wine_groupbox.setEnabled(True)
else:
self.proton_prefix.setEnabled(True)
self._linux_settings.wine_groupbox.setEnabled(False)
wrapper = self.possible_proton_combos[i - 1]
self._wrapper_settings.add_wrapper(wrapper)
config_helper.add_option(self.app_name, "no_wine", "true")
config_helper.add_option(
f"{self.app_name}.env",
"STEAM_COMPAT_DATA_PATH",
os.path.expanduser("~/.proton"),
)
config_helper.add_option(
f"{self.app_name}.env",
"STEAM_COMPAT_CLIENT_INSTALL_PATH",
str(Path.home().joinpath(".steam", "steam"))
)
self.proton_prefix.setText(os.path.expanduser("~/.proton"))
# Don't use Wine
self._linux_settings.wine_exec.setText("")
self._linux_settings.wine_prefix.setText("")
config_helper.save_config()
def proton_prefix_edit(self, text: str) -> Tuple[bool, str, str]:
if not text:
text = os.path.expanduser("~/.proton")
return True, text, ""
parent_dir = os.path.dirname(text)
return os.path.exists(parent_dir), text, PathEdit.reasons.dir_not_exist
def proton_prefix_save(self, text: str):
if not self.changeable:
return
config_helper.add_option(
f"{self.app_name}.env", "STEAM_COMPAT_DATA_PATH", text
)
config_helper.save_config()
def load_settings(self, app_name: str, proton: str):
self.changeable = False
self.app_name = app_name
proton = proton.replace('"', "")
self.proton_prefix.setEnabled(bool(proton))
if proton:
print(proton)
self.proton_combo.setCurrentText(
f'"{proton.replace(" run", "")}" run'
)
else:
self.proton_combo.setCurrentIndex(0)
proton_prefix = self.core.lgd.config.get(
f"{app_name}.env",
"STEAM_COMPAT_DATA_PATH",
fallback=str(Path.home().joinpath(".proton")),
)
self.proton_prefix.setText(proton_prefix)
self.changeable = True

View file

@ -166,6 +166,9 @@ class WrapperSettings(QWidget, Ui_WrapperSettings):
self.widget_stack.setCurrentIndex(0) self.widget_stack.setCurrentIndex(0)
if widget := self.wrappers.get(show_text, None):
widget.deleteLater()
widget = WrapperWidget(text, show_text, self.scroll_content) widget = WrapperWidget(text, show_text, self.scroll_content)
self.scroll_content.layout().addWidget(widget) self.scroll_content.layout().addWidget(widget)
self.adjust_scrollarea( self.adjust_scrollarea(
@ -173,7 +176,6 @@ class WrapperSettings(QWidget, Ui_WrapperSettings):
self.wrapper_scroll.horizontalScrollBar().maximum() self.wrapper_scroll.horizontalScrollBar().maximum()
) )
widget.delete_wrapper.connect(self.delete_wrapper) widget.delete_wrapper.connect(self.delete_wrapper)
self.wrappers[show_text] = widget self.wrappers[show_text] = widget
if not from_load: if not from_load:

View file

@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_GameSettings(object): class Ui_GameSettings(object):
def setupUi(self, GameSettings): def setupUi(self, GameSettings):
GameSettings.setObjectName("GameSettings") GameSettings.setObjectName("GameSettings")
GameSettings.resize(545, 348) GameSettings.resize(558, 357)
self.game_settings_layout = QtWidgets.QVBoxLayout(GameSettings) self.game_settings_layout = QtWidgets.QVBoxLayout(GameSettings)
self.game_settings_layout.setObjectName("game_settings_layout") self.game_settings_layout.setObjectName("game_settings_layout")
self.launch_settings_group = QtWidgets.QGroupBox(GameSettings) self.launch_settings_group = QtWidgets.QGroupBox(GameSettings)
@ -82,30 +82,12 @@ class Ui_GameSettings(object):
self.cloud_sync.setObjectName("cloud_sync") self.cloud_sync.setObjectName("cloud_sync")
self.cloud_layout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.cloud_sync) self.cloud_layout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.cloud_sync)
self.game_settings_layout.addWidget(self.cloud_group) self.game_settings_layout.addWidget(self.cloud_group)
self.proton_group = QtWidgets.QGroupBox(GameSettings) self.proton_placeholder = QtWidgets.QWidget(GameSettings)
self.proton_group.setObjectName("proton_group") self.proton_placeholder.setObjectName("proton_placeholder")
self.proton_layout = QtWidgets.QFormLayout(self.proton_group) self.proton_layout = QtWidgets.QFormLayout(self.proton_placeholder)
self.proton_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.proton_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.proton_layout.setObjectName("proton_layout") self.proton_layout.setObjectName("proton_layout")
self.proton_wrapper_label = QtWidgets.QLabel(self.proton_group) self.game_settings_layout.addWidget(self.proton_placeholder)
self.proton_wrapper_label.setObjectName("proton_wrapper_label")
self.proton_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.proton_wrapper_label)
self.proton_wrapper = QtWidgets.QComboBox(self.proton_group)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.proton_wrapper.sizePolicy().hasHeightForWidth())
self.proton_wrapper.setSizePolicy(sizePolicy)
self.proton_wrapper.setObjectName("proton_wrapper")
self.proton_wrapper.addItem("")
self.proton_layout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.proton_wrapper)
self.proton_prefix_label = QtWidgets.QLabel(self.proton_group)
self.proton_prefix_label.setObjectName("proton_prefix_label")
self.proton_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.proton_prefix_label)
self.proton_prefix_layout = QtWidgets.QVBoxLayout()
self.proton_prefix_layout.setObjectName("proton_prefix_layout")
self.proton_layout.setLayout(2, QtWidgets.QFormLayout.FieldRole, self.proton_prefix_layout)
self.game_settings_layout.addWidget(self.proton_group)
self.linux_settings_widget = QtWidgets.QWidget(GameSettings) self.linux_settings_widget = QtWidgets.QWidget(GameSettings)
self.linux_settings_widget.setObjectName("linux_settings_widget") self.linux_settings_widget.setObjectName("linux_settings_widget")
self.linux_settings_layout = QtWidgets.QVBoxLayout(self.linux_settings_widget) self.linux_settings_layout = QtWidgets.QVBoxLayout(self.linux_settings_widget)
@ -134,10 +116,6 @@ class Ui_GameSettings(object):
self.override_exe_edit.setPlaceholderText(_translate("GameSettings", "Relative path to launch executable")) self.override_exe_edit.setPlaceholderText(_translate("GameSettings", "Relative path to launch executable"))
self.cloud_group.setTitle(_translate("GameSettings", "Cloud Saves")) self.cloud_group.setTitle(_translate("GameSettings", "Cloud Saves"))
self.cloud_sync_label.setText(_translate("GameSettings", "Sync with cloud")) self.cloud_sync_label.setText(_translate("GameSettings", "Sync with cloud"))
self.proton_group.setTitle(_translate("GameSettings", "Proton Settings"))
self.proton_wrapper_label.setText(_translate("GameSettings", "Proton"))
self.proton_wrapper.setItemText(0, _translate("GameSettings", "Don\'t use Proton"))
self.proton_prefix_label.setText(_translate("GameSettings", "Prefix"))
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>545</width> <width>558</width>
<height>348</height> <height>357</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -157,46 +157,11 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QGroupBox" name="proton_group"> <widget class="QWidget" name="proton_placeholder" native="true">
<property name="title">
<string>Proton Settings</string>
</property>
<layout class="QFormLayout" name="proton_layout"> <layout class="QFormLayout" name="proton_layout">
<property name="labelAlignment"> <property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
<item row="1" column="0">
<widget class="QLabel" name="proton_wrapper_label">
<property name="text">
<string>Proton</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="proton_wrapper">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Don't use Proton</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="proton_prefix_label">
<property name="text">
<string>Prefix</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QVBoxLayout" name="proton_prefix_layout"/>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View file

@ -14,10 +14,52 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_LegendarySettings(object): class Ui_LegendarySettings(object):
def setupUi(self, LegendarySettings): def setupUi(self, LegendarySettings):
LegendarySettings.setObjectName("LegendarySettings") LegendarySettings.setObjectName("LegendarySettings")
LegendarySettings.resize(552, 312) LegendarySettings.resize(654, 498)
LegendarySettings.setWindowTitle("LegendarySettings") LegendarySettings.setWindowTitle("LegendarySettings")
self.legendary_layout = QtWidgets.QHBoxLayout(LegendarySettings) self.gridLayout = QtWidgets.QGridLayout(LegendarySettings)
self.legendary_layout.setObjectName("legendary_layout") self.gridLayout.setObjectName("gridLayout")
self.right_layout = QtWidgets.QVBoxLayout()
self.right_layout.setObjectName("right_layout")
self.locale_group = QtWidgets.QGroupBox(LegendarySettings)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.locale_group.sizePolicy().hasHeightForWidth())
self.locale_group.setSizePolicy(sizePolicy)
self.locale_group.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.locale_group.setObjectName("locale_group")
self.locale_layout = QtWidgets.QVBoxLayout(self.locale_group)
self.locale_layout.setObjectName("locale_layout")
self.right_layout.addWidget(self.locale_group, 0, QtCore.Qt.AlignTop)
self.cleanup_group = QtWidgets.QGroupBox(LegendarySettings)
self.cleanup_group.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.cleanup_group.setObjectName("cleanup_group")
self.cleanup_layout = QtWidgets.QVBoxLayout(self.cleanup_group)
self.cleanup_layout.setObjectName("cleanup_layout")
self.clean_keep_manifests_button = QtWidgets.QPushButton(self.cleanup_group)
self.clean_keep_manifests_button.setObjectName("clean_keep_manifests_button")
self.cleanup_layout.addWidget(self.clean_keep_manifests_button)
self.clean_button = QtWidgets.QPushButton(self.cleanup_group)
self.clean_button.setObjectName("clean_button")
self.cleanup_layout.addWidget(self.clean_button)
self.right_layout.addWidget(self.cleanup_group, 0, QtCore.Qt.AlignTop)
self.meta_group = QtWidgets.QGroupBox(LegendarySettings)
self.meta_group.setObjectName("meta_group")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.meta_group)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.win32_cb = QtWidgets.QCheckBox(self.meta_group)
self.win32_cb.setObjectName("win32_cb")
self.verticalLayout_2.addWidget(self.win32_cb)
self.mac_cb = QtWidgets.QCheckBox(self.meta_group)
self.mac_cb.setObjectName("mac_cb")
self.verticalLayout_2.addWidget(self.mac_cb)
self.refresh_game_meta_btn = QtWidgets.QPushButton(self.meta_group)
self.refresh_game_meta_btn.setObjectName("refresh_game_meta_btn")
self.verticalLayout_2.addWidget(self.refresh_game_meta_btn)
self.right_layout.addWidget(self.meta_group)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.right_layout.addItem(spacerItem)
self.gridLayout.addLayout(self.right_layout, 0, 1, 1, 1)
self.left_layout = QtWidgets.QVBoxLayout() self.left_layout = QtWidgets.QVBoxLayout()
self.left_layout.setObjectName("left_layout") self.left_layout.setObjectName("left_layout")
self.install_dir_group = QtWidgets.QGroupBox(LegendarySettings) self.install_dir_group = QtWidgets.QGroupBox(LegendarySettings)
@ -104,46 +146,23 @@ class Ui_LegendarySettings(object):
self.verticalLayout = QtWidgets.QVBoxLayout(self.ubisoft_gb) self.verticalLayout = QtWidgets.QVBoxLayout(self.ubisoft_gb)
self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout.setObjectName("verticalLayout")
self.left_layout.addWidget(self.ubisoft_gb, 0, QtCore.Qt.AlignTop) self.left_layout.addWidget(self.ubisoft_gb, 0, QtCore.Qt.AlignTop)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.left_layout.addItem(spacerItem)
self.legendary_layout.addLayout(self.left_layout)
self.right_layout = QtWidgets.QVBoxLayout()
self.right_layout.setObjectName("right_layout")
self.locale_group = QtWidgets.QGroupBox(LegendarySettings)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.locale_group.sizePolicy().hasHeightForWidth())
self.locale_group.setSizePolicy(sizePolicy)
self.locale_group.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.locale_group.setObjectName("locale_group")
self.locale_layout = QtWidgets.QVBoxLayout(self.locale_group)
self.locale_layout.setObjectName("locale_layout")
self.right_layout.addWidget(self.locale_group, 0, QtCore.Qt.AlignTop)
self.cleanup_group = QtWidgets.QGroupBox(LegendarySettings)
self.cleanup_group.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.cleanup_group.setObjectName("cleanup_group")
self.cleanup_layout = QtWidgets.QVBoxLayout(self.cleanup_group)
self.cleanup_layout.setObjectName("cleanup_layout")
self.clean_keep_manifests_button = QtWidgets.QPushButton(self.cleanup_group)
self.clean_keep_manifests_button.setObjectName("clean_keep_manifests_button")
self.cleanup_layout.addWidget(self.clean_keep_manifests_button)
self.clean_button = QtWidgets.QPushButton(self.cleanup_group)
self.clean_button.setObjectName("clean_button")
self.cleanup_layout.addWidget(self.clean_button)
self.refresh_game_meta_btn = QtWidgets.QPushButton(self.cleanup_group)
self.refresh_game_meta_btn.setObjectName("refresh_game_meta_btn")
self.cleanup_layout.addWidget(self.refresh_game_meta_btn)
self.right_layout.addWidget(self.cleanup_group, 0, QtCore.Qt.AlignTop)
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.right_layout.addItem(spacerItem1) self.left_layout.addItem(spacerItem1)
self.legendary_layout.addLayout(self.right_layout) self.gridLayout.addLayout(self.left_layout, 0, 0, 1, 1)
self.retranslateUi(LegendarySettings) self.retranslateUi(LegendarySettings)
QtCore.QMetaObject.connectSlotsByName(LegendarySettings) QtCore.QMetaObject.connectSlotsByName(LegendarySettings)
def retranslateUi(self, LegendarySettings): def retranslateUi(self, LegendarySettings):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
self.locale_group.setTitle(_translate("LegendarySettings", "Locale"))
self.cleanup_group.setTitle(_translate("LegendarySettings", "Cleanup"))
self.clean_keep_manifests_button.setText(_translate("LegendarySettings", "Clean, but keep manifests"))
self.clean_button.setText(_translate("LegendarySettings", "Remove everything"))
self.meta_group.setTitle(_translate("LegendarySettings", "Game metadata"))
self.win32_cb.setText(_translate("LegendarySettings", "Load 32 bit data"))
self.mac_cb.setText(_translate("LegendarySettings", "Load MacOS data"))
self.refresh_game_meta_btn.setText(_translate("LegendarySettings", "Refresh game meta"))
self.install_dir_group.setTitle(_translate("LegendarySettings", "Default Installation Directory")) self.install_dir_group.setTitle(_translate("LegendarySettings", "Default Installation Directory"))
self.download_group.setTitle(_translate("LegendarySettings", "Download Settings")) self.download_group.setTitle(_translate("LegendarySettings", "Download Settings"))
self.max_workers_label.setText(_translate("LegendarySettings", "Max Workers")) self.max_workers_label.setText(_translate("LegendarySettings", "Max Workers"))
@ -155,11 +174,6 @@ class Ui_LegendarySettings(object):
self.preferred_cdn_line.setPlaceholderText(_translate("LegendarySettings", "Default")) self.preferred_cdn_line.setPlaceholderText(_translate("LegendarySettings", "Default"))
self.disable_https_label.setText(_translate("LegendarySettings", "Disable HTTPS")) self.disable_https_label.setText(_translate("LegendarySettings", "Disable HTTPS"))
self.ubisoft_gb.setTitle(_translate("LegendarySettings", "Link Ubisoft Games")) self.ubisoft_gb.setTitle(_translate("LegendarySettings", "Link Ubisoft Games"))
self.locale_group.setTitle(_translate("LegendarySettings", "Locale"))
self.cleanup_group.setTitle(_translate("LegendarySettings", "Cleanup"))
self.clean_keep_manifests_button.setText(_translate("LegendarySettings", "Clean, but keep manifests"))
self.clean_button.setText(_translate("LegendarySettings", "Remove everything"))
self.refresh_game_meta_btn.setText(_translate("LegendarySettings", "Refresh game meta"))
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -1,266 +1,323 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>LegendarySettings</class> <class>LegendarySettings</class>
<widget class="QWidget" name="LegendarySettings"> <widget class="QWidget" name="LegendarySettings">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>552</width> <width>552</width>
<height>312</height> <height>312</height>
</rect> </rect>
</property>
<property name="windowTitle">
<string notr="true">LegendarySettings</string>
</property>
<layout class="QHBoxLayout" name="legendary_layout">
<item>
<layout class="QVBoxLayout" name="left_layout">
<item alignment="Qt::AlignTop">
<widget class="QGroupBox" name="install_dir_group">
<property name="title">
<string>Default Installation Directory</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<layout class="QVBoxLayout" name="install_dir_layout"/>
</widget>
</item>
<item alignment="Qt::AlignTop">
<widget class="QGroupBox" name="download_group">
<property name="title">
<string>Download Settings</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<layout class="QFormLayout" name="download_layout">
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
<item row="0" column="0"> <property name="windowTitle">
<widget class="QLabel" name="max_workers_label"> <string notr="true">LegendarySettings</string>
<property name="sizePolicy"> </property>
<sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <layout class="QHBoxLayout" name="legendary_layout">
<horstretch>0</horstretch> <item>
<verstretch>0</verstretch> <widget class="QPushButton" name="clean_keep_manifests_button">
</sizepolicy> <property name="text">
</property> <string>Clean, but keep manifests</string>
<property name="text"> </property>
<string>Max Workers</string> </widget>
</property> </item>
</widget> <item>
</item> <widget class="QPushButton" name="clean_button">
<item row="0" column="1"> <property name="text">
<layout class="QHBoxLayout" name="max_workers_layout"> <string>Remove everything</string>
<item> </property>
<widget class="QSpinBox" name="max_worker_spin"> </widget>
<property name="sizePolicy"> </item>
<sizepolicy hsizetype="Maximum" vsizetype="Fixed"> </layout>
<horstretch>0</horstretch> </widget>
<verstretch>0</verstretch> <item>
</sizepolicy> <widget class="QGroupBox" name="meta_group">
<property name="title">
<string>Game metadata</string>
</property> </property>
<property name="minimum"> <layout class="QVBoxLayout" name="verticalLayout_2">
<number>0</number> <item>
<widget class="QCheckBox" name="win32_cb">
<property name="text">
<string>Load 32 bit data</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mac_cb">
<property name="text">
<string>Load MacOS data</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="refresh_game_meta_btn">
<property name="text">
<string>Refresh game meta</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="right_vspacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property> </property>
<property name="maximum"> <property name="sizeHint" stdset="0">
<number>16</number> <size>
<width>20</width>
<height>40</height>
</size>
</property> </property>
<property name="value"> </spacer>
<number>0</number> </item>
</property> <item row="0" column="0">
</widget> <layout class="QVBoxLayout" name="left_layout">
</item> <item alignment="Qt::AlignTop">
<item> <widget class="QGroupBox" name="install_dir_group">
<widget class="QLabel" name="max_workers_info_label"> <property name="title">
<property name="font"> <string>Default Installation Directory</string>
<font> </property>
<italic>true</italic> <property name="alignment">
</font> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property> </property>
<property name="text"> <layout class="QVBoxLayout" name="install_dir_layout"/>
<string>Less is slower (0: Default)</string> </widget>
</property> </item>
</widget> <item alignment="Qt::AlignTop">
</item> <widget class="QGroupBox" name="download_group">
</layout> <property name="title">
</item> <string>Download Settings</string>
<item row="1" column="0"> </property>
<widget class="QLabel" name="max_memory_label"> <property name="alignment">
<property name="text"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
<string>Max Memory</string> </property>
</property> <layout class="QFormLayout" name="download_layout">
</widget> <property name="labelAlignment">
</item> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
<item row="1" column="1"> </property>
<layout class="QHBoxLayout" name="max_memory_layout"> <item row="0" column="0">
<item> <widget class="QLabel" name="max_workers_label">
<widget class="QSpinBox" name="max_memory_spin"> <property name="sizePolicy">
<property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch>
<horstretch>0</horstretch> <verstretch>0</verstretch>
<verstretch>0</verstretch> </sizepolicy>
</sizepolicy> </property>
</property> <property name="text">
<property name="suffix"> <string>Max Workers</string>
<string>MiB</string> </property>
</property> </widget>
<property name="minimum"> </item>
<number>0</number> <item row="0" column="1">
</property> <layout class="QHBoxLayout" name="max_workers_layout">
<property name="maximum"> <item>
<number>10240</number> <widget class="QSpinBox" name="max_worker_spin">
</property> <property name="sizePolicy">
<property name="singleStep"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<number>128</number> <horstretch>0</horstretch>
</property> <verstretch>0</verstretch>
<property name="value"> </sizepolicy>
<number>1024</number> </property>
</property> <property name="minimum">
</widget> <number>0</number>
</item> </property>
<item> <property name="maximum">
<widget class="QLabel" name="max_memory_info_label"> <number>16</number>
<property name="font"> </property>
<font> <property name="value">
<italic>true</italic> <number>0</number>
</font> </property>
</property> </widget>
<property name="text"> </item>
<string>Less is slower (0: Default)</string> <item>
</property> <widget class="QLabel" name="max_workers_info_label">
</widget> <property name="font">
</item> <font>
</layout> <italic>true</italic>
</item> </font>
<item row="2" column="0"> </property>
<widget class="QLabel" name="preferred_cdn_label"> <property name="text">
<property name="text"> <string>Less is slower (0: Default)</string>
<string>Preferred CDN</string> </property>
</property> </widget>
</widget> </item>
</item> </layout>
<item row="2" column="1"> </item>
<widget class="QLineEdit" name="preferred_cdn_line"> <item row="1" column="0">
<property name="placeholderText"> <widget class="QLabel" name="max_memory_label">
<string>Default</string> <property name="text">
</property> <string>Max Memory</string>
</widget> </property>
</item> </widget>
<item row="3" column="0"> </item>
<widget class="QLabel" name="disable_https_label"> <item row="1" column="1">
<property name="text"> <layout class="QHBoxLayout" name="max_memory_layout">
<string>Disable HTTPS</string> <item>
</property> <widget class="QSpinBox" name="max_memory_spin">
</widget> <property name="sizePolicy">
</item> <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<item row="3" column="1"> <horstretch>0</horstretch>
<widget class="QCheckBox" name="disable_https_check"> <verstretch>0</verstretch>
<property name="text"> </sizepolicy>
<string/> </property>
</property> <property name="suffix">
</widget> <string>MiB</string>
</item> </property>
</layout> <property name="minimum">
</widget> <number>0</number>
</item> </property>
<item alignment="Qt::AlignTop"> <property name="maximum">
<widget class="QGroupBox" name="ubisoft_gb"> <number>10240</number>
<property name="title"> </property>
<string>Link Ubisoft Games</string> <property name="singleStep">
</property> <number>128</number>
<property name="alignment"> </property>
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> <property name="value">
</property> <number>1024</number>
<layout class="QVBoxLayout" name="verticalLayout"/> </property>
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="left_vspacer"> <widget class="QLabel" name="max_memory_info_label">
<property name="orientation"> <property name="font">
<enum>Qt::Vertical</enum> <font>
</property> <italic>true</italic>
<property name="sizeHint" stdset="0"> </font>
<size> </property>
<width>20</width> <property name="text">
<height>40</height> <string>Less is slower (0: Default)</string>
</size> </property>
</property> </widget>
</spacer> </item>
</item> </layout>
</layout> </item>
</item> <item row="2" column="0">
<item> <widget class="QLabel" name="preferred_cdn_label">
<layout class="QVBoxLayout" name="right_layout"> <property name="text">
<item alignment="Qt::AlignTop"> <string>Preferred CDN</string>
<widget class="QGroupBox" name="locale_group"> </property>
<property name="sizePolicy"> </widget>
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> </item>
<horstretch>0</horstretch> <item row="2" column="1">
<verstretch>0</verstretch> <widget class="QLineEdit" name="preferred_cdn_line">
</sizepolicy> <property name="placeholderText">
</property> <string>Default</string>
<property name="title"> </property>
<string>Locale</string> </widget>
</property> </item>
<property name="alignment"> <item row="3" column="0">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> <widget class="QLabel" name="disable_https_label">
</property> <property name="text">
<layout class="QVBoxLayout" name="locale_layout"/> <string>Disable HTTPS</string>
</widget> </property>
</item> </widget>
<item alignment="Qt::AlignTop"> </item>
<widget class="QGroupBox" name="cleanup_group"> <item row="3" column="1">
<property name="title"> <widget class="QCheckBox" name="disable_https_check">
<string>Cleanup</string> <property name="text">
</property> <string/>
<property name="alignment"> </property>
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> </widget>
</property> </item>
<layout class="QVBoxLayout" name="cleanup_layout"> </layout>
<item> </widget>
<widget class="QPushButton" name="clean_keep_manifests_button"> </item>
<property name="text"> <item alignment="Qt::AlignTop">
<string>Clean, but keep manifests</string> <widget class="QGroupBox" name="ubisoft_gb">
</property> <property name="title">
</widget> <string>Link Ubisoft Games</string>
</item> </property>
<item> <property name="alignment">
<widget class="QPushButton" name="clean_button"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
<property name="text"> </property>
<string>Remove everything</string> <layout class="QVBoxLayout" name="verticalLayout"/>
</property> </widget>
</widget> </item>
</item> <item>
<item> <spacer name="left_vspacer">
<widget class="QPushButton" name="refresh_game_meta_btn"> <property name="orientation">
<property name="text"> <enum>Qt::Vertical</enum>
<string>Refresh game meta</string> </property>
</property> <property name="sizeHint" stdset="0">
</widget> <size>
</item> <width>20</width>
</layout> <height>40</height>
</widget> </size>
</item> </property>
<item> </spacer>
<spacer name="right_vspacer"> </item>
<property name="orientation"> </layout>
<enum>Qt::Vertical</enum> </item>
</property> <item>
<property name="sizeHint" stdset="0"> <layout class="QVBoxLayout" name="right_layout">
<size> <item alignment="Qt::AlignTop">
<width>20</width> <widget class="QGroupBox" name="locale_group">
<height>40</height> <property name="sizePolicy">
</size> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
</property> <horstretch>0</horstretch>
</spacer> <verstretch>0</verstretch>
</item> </sizepolicy>
</layout> </property>
</item> <property name="title">
</layout> <string>Locale</string>
</widget> </property>
<resources/> <property name="alignment">
<connections/> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<layout class="QVBoxLayout" name="locale_layout"/>
</widget>
</item>
<item alignment="Qt::AlignTop">
<widget class="QGroupBox" name="cleanup_group">
<property name="title">
<string>Cleanup</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<layout class="QVBoxLayout" name="cleanup_layout">
<item>
<widget class="QPushButton" name="clean_keep_manifests_button">
<property name="text">
<string>Clean, but keep manifests</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clean_button">
<property name="text">
<string>Remove everything</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="refresh_game_meta_btn">
<property name="text">
<string>Refresh game meta</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="right_vspacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<resources/>
<connections/>
</ui> </ui>

View file

@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'rare/ui/components/tabs/settings/proton.ui'
#
# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_ProtonSettings(object):
def setupUi(self, ProtonSettings):
ProtonSettings.setObjectName("ProtonSettings")
ProtonSettings.resize(400, 300)
ProtonSettings.setWindowTitle("GroupBox")
self.formLayout = QtWidgets.QFormLayout(ProtonSettings)
self.formLayout.setObjectName("formLayout")
self.proton_wrapper_label = QtWidgets.QLabel(ProtonSettings)
self.proton_wrapper_label.setObjectName("proton_wrapper_label")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.proton_wrapper_label)
self.proton_combo = QtWidgets.QComboBox(ProtonSettings)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.proton_combo.sizePolicy().hasHeightForWidth())
self.proton_combo.setSizePolicy(sizePolicy)
self.proton_combo.setObjectName("proton_combo")
self.proton_combo.addItem("")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.proton_combo)
self.proton_prefix_label = QtWidgets.QLabel(ProtonSettings)
self.proton_prefix_label.setObjectName("proton_prefix_label")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.proton_prefix_label)
self.placeholder_prefix_edit = QtWidgets.QLineEdit(ProtonSettings)
self.placeholder_prefix_edit.setObjectName("placeholder_prefix_edit")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.placeholder_prefix_edit)
self.retranslateUi(ProtonSettings)
QtCore.QMetaObject.connectSlotsByName(ProtonSettings)
def retranslateUi(self, ProtonSettings):
_translate = QtCore.QCoreApplication.translate
ProtonSettings.setTitle(_translate("ProtonSettings", "Proton Settings"))
self.proton_wrapper_label.setText(_translate("ProtonSettings", "Proton"))
self.proton_combo.setItemText(0, _translate("ProtonSettings", "Don\'t use Proton"))
self.proton_prefix_label.setText(_translate("ProtonSettings", "Prefix"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ProtonSettings = QtWidgets.QGroupBox()
ui = Ui_ProtonSettings()
ui.setupUi(ProtonSettings)
ProtonSettings.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ProtonSettings</class>
<widget class="QGroupBox" name="ProtonSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">GroupBox</string>
</property>
<property name="title">
<string>Proton Settings</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="proton_wrapper_label">
<property name="text">
<string>Proton</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="proton_combo">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Don't use Proton</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="proton_prefix_label">
<property name="text">
<string>Prefix</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="placeholder_prefix_edit"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -150,6 +150,7 @@ class IndicatorReasons:
wrong_format = QCoreApplication.translate("IndicatorReasons", "Given text has wrong format") wrong_format = QCoreApplication.translate("IndicatorReasons", "Given text has wrong format")
game_not_installed = QCoreApplication.translate("IndicatorReasons", "Game is not installed or does not exist") game_not_installed = QCoreApplication.translate("IndicatorReasons", "Game is not installed or does not exist")
dir_not_exist = QCoreApplication.translate("IndicatorReasons", "Directory does not exist") dir_not_exist = QCoreApplication.translate("IndicatorReasons", "Directory does not exist")
file_not_exist = QCoreApplication.translate("IndicatorReasons", "File does not exist")
wrong_path = QCoreApplication.translate("IndicatorReasons", "Wrong Directory") wrong_path = QCoreApplication.translate("IndicatorReasons", "Wrong Directory")
@ -161,7 +162,7 @@ class IndicatorLineEdit(QWidget):
def __init__( def __init__(
self, self,
text: str = "", text: str = "",
ph_text: str = "", placeholder: str = "",
completer: QCompleter = None, completer: QCompleter = None,
edit_func: Callable[[str], Tuple[bool, str, str]] = None, edit_func: Callable[[str], Tuple[bool, str, str]] = None,
save_func: Callable[[str], None] = None, save_func: Callable[[str], None] = None,
@ -176,7 +177,7 @@ class IndicatorLineEdit(QWidget):
# Add line_edit # Add line_edit
self.line_edit = QLineEdit(self) self.line_edit = QLineEdit(self)
self.line_edit.setObjectName("line_edit") self.line_edit.setObjectName("line_edit")
self.line_edit.setPlaceholderText(ph_text) self.line_edit.setPlaceholderText(placeholder)
self.line_edit.setSizePolicy(horiz_policy, QSizePolicy.Fixed) self.line_edit.setSizePolicy(horiz_policy, QSizePolicy.Fixed)
# Add hint_label to line_edit # Add hint_label to line_edit
self.line_edit.setLayout(QHBoxLayout()) self.line_edit.setLayout(QHBoxLayout())
@ -199,7 +200,7 @@ class IndicatorLineEdit(QWidget):
self.indicator_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.indicator_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.layout.addWidget(self.indicator_label) self.layout.addWidget(self.indicator_label)
if not ph_text: if not placeholder:
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
self.line_edit.setPlaceholderText( self.line_edit.setPlaceholderText(
_translate(self.__class__.__name__, "Default") _translate(self.__class__.__name__, "Default")
@ -318,9 +319,12 @@ class PathEdit(IndicatorLineEdit):
self.compl_model.setIconProvider(PathEditIconProvider()) self.compl_model.setIconProvider(PathEditIconProvider())
self.compl_model.setRootPath(path) self.compl_model.setRootPath(path)
self.completer.setModel(self.compl_model) self.completer.setModel(self.compl_model)
edit_func = self._wrap_edit_function(edit_func)
super(PathEdit, self).__init__( super(PathEdit, self).__init__(
text=path, text=path,
ph_text=placeholder, placeholder=placeholder,
completer=self.completer, completer=self.completer,
edit_func=edit_func, edit_func=edit_func,
save_func=save_func, save_func=save_func,
@ -357,6 +361,13 @@ class PathEdit(IndicatorLineEdit):
self.line_edit.setText(names[0]) self.line_edit.setText(names[0])
self.compl_model.setRootPath(names[0]) self.compl_model.setRootPath(names[0])
def _wrap_edit_function(self, edit_function: Callable[[str], Tuple[bool, str, str]]):
if edit_function:
return lambda text: edit_function(os.path.expanduser(text)
if text.startswith("~") else text)
else:
return edit_function
class SideTabBar(QTabBar): class SideTabBar(QTabBar):
def __init__(self, parent=None): def __init__(self, parent=None):

View file

@ -451,14 +451,16 @@ class WineResolverSignals(QObject):
class WineResolver(QRunnable): class WineResolver(QRunnable):
def __init__(self, path: str, app_name: str, core: LegendaryCore): def __init__(self, path: str, app_name: str):
super(WineResolver, self).__init__() super(WineResolver, self).__init__()
self.signals = WineResolverSignals() self.signals = WineResolverSignals()
self.setAutoDelete(True) self.setAutoDelete(True)
self.wine_env = os.environ.copy() self.wine_env = os.environ.copy()
core = LegendaryCoreSingleton()
self.wine_env.update(core.get_app_environment(app_name)) self.wine_env.update(core.get_app_environment(app_name))
self.wine_env["WINEDLLOVERRIDES"] = "winemenubuilder=d;mscoree=d;mshtml=d;" self.wine_env["WINEDLLOVERRIDES"] = "winemenubuilder=d;mscoree=d;mshtml=d;"
self.wine_env["DISPLAY"] = "" self.wine_env["DISPLAY"] = ""
self.wine_binary = core.lgd.config.get( self.wine_binary = core.lgd.config.get(
app_name, app_name,
"wine_executable", "wine_executable",