2 Commits

Author SHA1 Message Date
4a758f3b3c chore: use flatpak run for flatpak not start.sh
Some checks failed
Code check / Check code (push) Failing after 1m35s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-10-27 23:13:48 +05:00
0853dd1579 chore: use CLI for clear pfx
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-10-27 22:36:14 +05:00
4 changed files with 97 additions and 145 deletions

View File

@@ -19,7 +19,6 @@ __app_id__ = "ru.linux_gaming.PortProtonQt"
__app_name__ = "PortProtonQt" __app_name__ = "PortProtonQt"
__app_version__ = "0.1.8" __app_version__ = "0.1.8"
def get_version(): def get_version():
try: try:
commit = subprocess.check_output( commit = subprocess.check_output(
@@ -30,18 +29,16 @@ def get_version():
except (subprocess.CalledProcessError, FileNotFoundError, OSError): except (subprocess.CalledProcessError, FileNotFoundError, OSError):
return __app_version__ return __app_version__
def main(): def main():
os.environ["PW_CLI"] = "1" os.environ["PW_CLI"] = "1"
os.environ["PROCESS_LOG"] = "1" os.environ["PROCESS_LOG"] = "1"
os.environ["START_FROM_STEAM"] = "1" os.environ["START_FROM_STEAM"] = "1"
portproton_path = get_portproton_location() portproton_path, start_sh = get_portproton_location()
if portproton_path is None: if portproton_path is None or start_sh is None:
return return
script_path = os.path.join(portproton_path, "data", "scripts", "start.sh") subprocess.run(start_sh + ["cli", "--initial"])
subprocess.run([script_path, "cli", "--initial"])
app = QApplication(sys.argv) app = QApplication(sys.argv)
app.setWindowIcon(QIcon.fromTheme(__app_id__)) app.setWindowIcon(QIcon.fromTheme(__app_id__))
@@ -161,6 +158,5 @@ def main():
sys.exit(app.exec()) sys.exit(app.exec())
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -1,11 +1,13 @@
import os import os
import configparser import configparser
import shutil import shutil
import subprocess
from portprotonqt.logger import get_logger from portprotonqt.logger import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
_portproton_location = None _portproton_location = None
_portproton_start_sh = None
# Paths to configuration files # Paths to configuration files
CONFIG_FILE = os.path.join( CONFIG_FILE = os.path.join(
@@ -101,33 +103,65 @@ def read_file_content(file_path):
return f.read().strip() return f.read().strip()
def get_portproton_location(): def get_portproton_location():
"""Returns the path to the PortProton directory. """Returns the path to the PortProton directory and start.sh command list.
Checks the cached path first. If not set, reads from PORTPROTON_CONFIG_FILE. Checks the cached path first. If not set, reads from PORTPROTON_CONFIG_FILE.
If the path is invalid, uses the default directory. If the path is invalid, uses the default flatpak directory.
If Flatpak package ru.linux_gaming.PortProton is installed, returns the Flatpak run command list.
""" """
global _portproton_location global _portproton_location, _portproton_start_sh
if _portproton_location is not None:
return _portproton_location if '_portproton_location' in globals() and _portproton_location is not None:
return _portproton_location, _portproton_start_sh
location = None
if os.path.isfile(PORTPROTON_CONFIG_FILE): if os.path.isfile(PORTPROTON_CONFIG_FILE):
try: try:
location = read_file_content(PORTPROTON_CONFIG_FILE).strip() location = read_file_content(PORTPROTON_CONFIG_FILE).strip()
if location and os.path.isdir(location): if location and os.path.isdir(location):
_portproton_location = location
logger.info(f"PortProton path from configuration: {location}") logger.info(f"PortProton path from configuration: {location}")
return _portproton_location else:
logger.warning(f"Invalid PortProton path in configuration: {location}, using default path") logger.warning(f"Invalid PortProton path in configuration: {location}, using defaults")
location = None
except (OSError, PermissionError) as e: except (OSError, PermissionError) as e:
logger.warning(f"Failed to read PortProton configuration file: {e}, using default path") logger.warning(f"Failed to read PortProton configuration file: {e}")
location = None
default_dir = os.path.join(os.path.expanduser("~"), ".var", "app", "ru.linux_gaming.PortProton") default_flatpak_dir = os.path.join(os.path.expanduser("~"), ".var", "app", "ru.linux_gaming.PortProton")
if os.path.isdir(default_dir): is_flatpak_installed = False
_portproton_location = default_dir try:
logger.info(f"Using flatpak PortProton directory: {default_dir}") result = subprocess.run(
return _portproton_location ["flatpak", "list"],
capture_output=True,
text=True,
check=False
)
if "ru.linux_gaming.PortProton" in result.stdout:
is_flatpak_installed = True
except Exception:
pass
logger.warning("PortProton configuration and flatpak directory not found") if is_flatpak_installed:
return None _portproton_location = default_flatpak_dir if os.path.isdir(default_flatpak_dir) else None
_portproton_start_sh = ["flatpak", "run", "ru.linux_gaming.PortProton"]
logger.info("Detected Flatpak installation: using 'flatpak run ru.linux_gaming.PortProton'")
elif location:
_portproton_location = location
start_sh_path = os.path.join(location, "data", "scripts", "start.sh")
_portproton_start_sh = [start_sh_path] if os.path.exists(start_sh_path) else None
logger.info(f"Using PortProton installation from config path: {_portproton_location}")
elif os.path.isdir(default_flatpak_dir):
_portproton_location = default_flatpak_dir
start_sh_path = os.path.join(default_flatpak_dir, "data", "scripts", "start.sh")
_portproton_start_sh = [start_sh_path] if os.path.exists(start_sh_path) else None
logger.info(f"Using Flatpak PortProton directory: {_portproton_location}")
else:
logger.warning("PortProton configuration and Flatpak directory not found")
_portproton_location = None
_portproton_start_sh = None
return _portproton_location, _portproton_start_sh
def parse_desktop_entry(file_path): def parse_desktop_entry(file_path):
"""Reads and parses a .desktop file using configparser. """Reads and parses a .desktop file using configparser.

View File

@@ -73,7 +73,7 @@ class MainWindow(QMainWindow):
self.game_processes = [] self.game_processes = []
self.target_exe = None self.target_exe = None
self.current_running_button = None self.current_running_button = None
self.portproton_location = get_portproton_location() self.portproton_location, self.start_sh = get_portproton_location()
self.game_library_manager = GameLibraryManager(self, self.theme, None) self.game_library_manager = GameLibraryManager(self, self.theme, None)
@@ -458,11 +458,11 @@ class MainWindow(QMainWindow):
self.current_install_script = script_name self.current_install_script = script_name
self.seen_progress = False self.seen_progress = False
self.current_percent = 0.0 self.current_percent = 0.0
start_sh = os.path.join(self.portproton_location or "", "data", "scripts", "start.sh") if self.portproton_location else "" start_sh = self.start_sh
if not os.path.exists(start_sh): if not start_sh:
self.installing = False self.installing = False
return return
cmd = [start_sh, "cli", "--autoinstall", script_name] cmd = start_sh + ["cli", "--autoinstall", script_name]
self.install_process = QProcess(self) self.install_process = QProcess(self)
self.install_process.finished.connect(self.on_install_finished) self.install_process.finished.connect(self.on_install_finished)
self.install_process.errorOccurred.connect(self.on_install_error) self.install_process.errorOccurred.connect(self.on_install_error)
@@ -1424,12 +1424,10 @@ class MainWindow(QMainWindow):
prefix = self.prefixCombo.currentText() prefix = self.prefixCombo.currentText()
if not wine or not prefix: if not wine or not prefix:
return return
if not self.portproton_location: if not self.portproton_location or not self.start_sh:
return return
start_sh = os.path.join(self.portproton_location, "data", "scripts", "start.sh") start_sh = self.start_sh
if not os.path.exists(start_sh): cmd = start_sh + ["cli", cli_arg, wine, prefix]
return
cmd = [start_sh, "cli", cli_arg, wine, prefix]
# Показываем прогресс-бар перед запуском # Показываем прогресс-бар перед запуском
self.wine_progress_bar.setVisible(True) self.wine_progress_bar.setVisible(True)
@@ -1508,12 +1506,13 @@ class MainWindow(QMainWindow):
QMessageBox.warning(self, _("Error"), f"Failed to launch tool: {error}") QMessageBox.warning(self, _("Error"), f"Failed to launch tool: {error}")
def clear_prefix(self): def clear_prefix(self):
"""Очистка префикса (позже удалить).""" """Очищает префикс"""
selected_prefix = self.prefixCombo.currentText() selected_prefix = self.prefixCombo.currentText()
selected_wine = self.wineCombo.currentText() selected_wine = self.wineCombo.currentText()
if not selected_prefix or not selected_wine: if not selected_prefix or not selected_wine:
return return
if not self.portproton_location: if not self.portproton_location or not self.start_sh:
return return
reply = QMessageBox.question( reply = QMessageBox.question(
@@ -1526,98 +1525,35 @@ class MainWindow(QMainWindow):
if reply != QMessageBox.StandardButton.Yes: if reply != QMessageBox.StandardButton.Yes:
return return
prefix_dir = os.path.join(self.portproton_location, "data", "prefixes", selected_prefix) start_sh = self.start_sh
if not os.path.exists(prefix_dir):
self.wine_progress_bar.setVisible(True)
self.update_status_message.emit(_("Clearing prefix..."), 0)
self.clear_process = QProcess(self)
self.clear_process.finished.connect(lambda exitCode, exitStatus: self._on_clear_prefix_finished(exitCode))
self.clear_process.errorOccurred.connect(lambda error: self._on_clear_prefix_error(error))
cmd = start_sh + ["cli", "--clear_pfx", selected_wine, selected_prefix]
self.clear_process.start(cmd[0], cmd[1:])
if not self.clear_process.waitForStarted(5000):
self.wine_progress_bar.setVisible(False)
self.update_status_message.emit("", 0)
QMessageBox.warning(self, _("Error"), _("Failed to start prefix clear process."))
return return
success = True def _on_clear_prefix_finished(self, exitCode):
errors = [] self.wine_progress_bar.setVisible(False)
self.update_status_message.emit("", 0)
# Удаление файлов if exitCode == 0:
files_to_remove = [ QMessageBox.information(self, _("Success"), _("Prefix cleared successfully."))
os.path.join(prefix_dir, "*.dot*"),
os.path.join(prefix_dir, "*.prog*"),
os.path.join(prefix_dir, ".wine_ver"),
os.path.join(prefix_dir, "system.reg"),
os.path.join(prefix_dir, "user.reg"),
os.path.join(prefix_dir, "userdef.reg"),
os.path.join(prefix_dir, "winetricks.log"),
os.path.join(prefix_dir, ".update-timestamp"),
os.path.join(prefix_dir, "drive_c", ".windows-serial"),
]
import glob
for pattern in files_to_remove:
if "*" in pattern: # Глобальный паттерн
matches = glob.glob(pattern)
for file_path in matches:
try:
if os.path.exists(file_path):
os.remove(file_path)
except Exception as e:
success = False
errors.append(str(e))
else: # Конкретный файл
try:
if os.path.exists(pattern):
os.remove(pattern)
except Exception as e:
success = False
errors.append(str(e))
# Удаление директорий
dirs_to_remove = [
os.path.join(prefix_dir, "drive_c", "windows"),
os.path.join(prefix_dir, "drive_c", "ProgramData", "Setup"),
os.path.join(prefix_dir, "drive_c", "ProgramData", "Windows"),
os.path.join(prefix_dir, "drive_c", "ProgramData", "WindowsTask"),
os.path.join(prefix_dir, "drive_c", "ProgramData", "Package Cache"),
os.path.join(prefix_dir, "drive_c", "users", "Public", "Local Settings", "Application Data", "Microsoft"),
os.path.join(prefix_dir, "drive_c", "users", "Public", "Local Settings", "Application Data", "Temp"),
os.path.join(prefix_dir, "drive_c", "users", "Public", "Local Settings", "Temporary Internet Files"),
os.path.join(prefix_dir, "drive_c", "users", "Public", "Application Data", "Microsoft"),
os.path.join(prefix_dir, "drive_c", "users", "Public", "Application Data", "wine_gecko"),
os.path.join(prefix_dir, "drive_c", "users", "Public", "Temp"),
os.path.join(prefix_dir, "drive_c", "users", "steamuser", "Local Settings", "Application Data", "Microsoft"),
os.path.join(prefix_dir, "drive_c", "users", "steamuser", "Local Settings", "Application Data", "Temp"),
os.path.join(prefix_dir, "drive_c", "users", "steamuser", "Local Settings", "Temporary Internet Files"),
os.path.join(prefix_dir, "drive_c", "users", "steamuser", "Application Data", "Microsoft"),
os.path.join(prefix_dir, "drive_c", "users", "steamuser", "Application Data", "wine_gecko"),
os.path.join(prefix_dir, "drive_c", "users", "steamuser", "Temp"),
os.path.join(prefix_dir, "drive_c", "Program Files", "Internet Explorer"),
os.path.join(prefix_dir, "drive_c", "Program Files", "Windows Media Player"),
os.path.join(prefix_dir, "drive_c", "Program Files", "Windows NT"),
os.path.join(prefix_dir, "drive_c", "Program Files (x86)", "Internet Explorer"),
os.path.join(prefix_dir, "drive_c", "Program Files (x86)", "Windows Media Player"),
os.path.join(prefix_dir, "drive_c", "Program Files (x86)", "Windows NT"),
]
import shutil
for dir_path in dirs_to_remove:
try:
if os.path.exists(dir_path):
shutil.rmtree(dir_path)
except Exception as e:
success = False
errors.append(str(e))
tmp_path = os.path.join(self.portproton_location, "data", "tmp")
if os.path.exists(tmp_path):
import glob
bin_files = glob.glob(os.path.join(tmp_path, "*.bin"))
foz_files = glob.glob(os.path.join(tmp_path, "*.foz"))
for file_path in bin_files + foz_files:
try:
os.remove(file_path)
except Exception as e:
success = False
errors.append(str(e))
if success:
QMessageBox.information(self, _("Success"), _("Prefix '{}' cleared successfully.").format(selected_prefix))
else: else:
error_msg = _("Prefix '{}' cleared with errors:\n{}").format(selected_prefix, "\n".join(errors[:5])) QMessageBox.warning(self, _("Error"), _("Prefix clear failed with exit code {}.").format(exitCode))
QMessageBox.warning(self, _("Warning"), error_msg)
def _on_clear_prefix_error(self, error):
self.wine_progress_bar.setVisible(False)
self.update_status_message.emit("", 0)
QMessageBox.warning(self, _("Error"), _("Failed to run clear prefix command: {}").format(error))
def create_prefix_backup(self): def create_prefix_backup(self):
selected_prefix = self.prefixCombo.currentText() selected_prefix = self.prefixCombo.currentText()
@@ -1629,14 +1565,12 @@ class MainWindow(QMainWindow):
def _perform_backup(self, backup_dir, prefix_name): def _perform_backup(self, backup_dir, prefix_name):
os.makedirs(backup_dir, exist_ok=True) os.makedirs(backup_dir, exist_ok=True)
if not self.portproton_location: if not self.portproton_location or not self.start_sh:
return
start_sh = os.path.join(self.portproton_location, "data", "scripts", "start.sh")
if not os.path.exists(start_sh):
return return
start_sh = self.start_sh
self.backup_process = QProcess(self) self.backup_process = QProcess(self)
self.backup_process.finished.connect(lambda exitCode, exitStatus: self._on_backup_finished(exitCode)) self.backup_process.finished.connect(lambda exitCode, exitStatus: self._on_backup_finished(exitCode))
cmd = [start_sh, "--backup-prefix", prefix_name, backup_dir] cmd = start_sh + ["--backup-prefix", prefix_name, backup_dir]
self.backup_process.start(cmd[0], cmd[1:]) self.backup_process.start(cmd[0], cmd[1:])
if not self.backup_process.waitForStarted(): if not self.backup_process.waitForStarted():
QMessageBox.warning(self, _("Error"), _("Failed to start backup process.")) QMessageBox.warning(self, _("Error"), _("Failed to start backup process."))
@@ -1649,14 +1583,12 @@ class MainWindow(QMainWindow):
def _perform_restore(self, file_path): def _perform_restore(self, file_path):
if not file_path or not os.path.exists(file_path): if not file_path or not os.path.exists(file_path):
return return
if not self.portproton_location: if not self.portproton_location or not self.start_sh:
return
start_sh = os.path.join(self.portproton_location, "data", "scripts", "start.sh")
if not os.path.exists(start_sh):
return return
start_sh = self.start_sh
self.restore_process = QProcess(self) self.restore_process = QProcess(self)
self.restore_process.finished.connect(lambda exitCode, exitStatus: self._on_restore_finished(exitCode)) self.restore_process.finished.connect(lambda exitCode, exitStatus: self._on_restore_finished(exitCode))
cmd = [start_sh, "--restore-prefix", file_path] cmd = start_sh + ["--restore-prefix", file_path]
self.restore_process.start(cmd[0], cmd[1:]) self.restore_process.start(cmd[0], cmd[1:])
if not self.restore_process.waitForStarted(): if not self.restore_process.waitForStarted():
QMessageBox.warning(self, _("Error"), _("Failed to start restore process.")) QMessageBox.warning(self, _("Error"), _("Failed to start restore process."))
@@ -2949,10 +2881,7 @@ class MainWindow(QMainWindow):
env_vars = os.environ.copy() env_vars = os.environ.copy()
env_vars['LEGENDARY_CONFIG_PATH'] = self.legendary_config_path env_vars['LEGENDARY_CONFIG_PATH'] = self.legendary_config_path
wrapper = "flatpak run ru.linux_gaming.PortProton" wrapper = self.start_sh or ""
if self.portproton_location is not None and ".var" not in self.portproton_location:
start_sh = os.path.join(self.portproton_location, "data", "scripts", "start.sh")
wrapper = start_sh
cmd = [wrapper, game_exe] cmd = [wrapper, game_exe]
@@ -3046,13 +2975,6 @@ class MainWindow(QMainWindow):
exe_name = os.path.splitext(current_exe)[0] exe_name = os.path.splitext(current_exe)[0]
env_vars = os.environ.copy() env_vars = os.environ.copy()
if entry_exec_split[0] == "env" and len(entry_exec_split) > 1 and 'data/scripts/start.sh' in entry_exec_split[1]:
env_vars['START_FROM_STEAM'] = '1'
env_vars['PROCESS_LOG'] = '1'
elif entry_exec_split[0] == "flatpak":
env_vars['START_FROM_STEAM'] = '1'
env_vars['PROCESS_LOG'] = '1'
# Запускаем игру # Запускаем игру
try: try:
process = subprocess.Popen(entry_exec_split, env=env_vars, shell=False, preexec_fn=os.setsid) process = subprocess.Popen(entry_exec_split, env=env_vars, shell=False, preexec_fn=os.setsid)

View File

@@ -58,7 +58,7 @@ class PortProtonAPI:
self.xdg_data_home = os.getenv("XDG_DATA_HOME", os.path.join(os.path.expanduser("~"), ".local", "share")) self.xdg_data_home = os.getenv("XDG_DATA_HOME", os.path.join(os.path.expanduser("~"), ".local", "share"))
self.custom_data_dir = os.path.join(self.xdg_data_home, "PortProtonQt", "custom_data") self.custom_data_dir = os.path.join(self.xdg_data_home, "PortProtonQt", "custom_data")
os.makedirs(self.custom_data_dir, exist_ok=True) os.makedirs(self.custom_data_dir, exist_ok=True)
self.portproton_location = get_portproton_location() self.portproton_location, self.portproton_start_sh = get_portproton_location()
self.repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) self.repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
self.builtin_custom_folder = os.path.join(self.repo_root, "custom_data") self.builtin_custom_folder = os.path.join(self.repo_root, "custom_data")
self._topics_data = None self._topics_data = None