Added support to Proton-EM
This commit is contained in:
@@ -290,7 +290,6 @@ class Main(Gtk.Window):
|
||||
self.current_prefix = None
|
||||
self.games = []
|
||||
self.flowbox_child = None
|
||||
self.play_button_locked = False
|
||||
self.updated_steam_id = None
|
||||
self.game_running = False
|
||||
|
||||
@@ -299,6 +298,7 @@ class Main(Gtk.Window):
|
||||
self.double_click_time_threshold = 500
|
||||
|
||||
self.processos = {}
|
||||
self.button_locked = {}
|
||||
|
||||
self.working_directory = faugus_launcher_dir
|
||||
os.chdir(self.working_directory)
|
||||
@@ -442,6 +442,8 @@ class Main(Gtk.Window):
|
||||
for title in to_remove:
|
||||
del processos[title]
|
||||
updated = True
|
||||
if title in self.button_locked:
|
||||
del self.button_locked[title]
|
||||
|
||||
if updated:
|
||||
with open(running_games, "w") as f:
|
||||
@@ -458,8 +460,7 @@ class Main(Gtk.Window):
|
||||
game_label = hbox.get_children()[1]
|
||||
selected_title = game_label.get_text()
|
||||
|
||||
if self.play_button_locked == False:
|
||||
self.on_item_selected(self.flowbox, selected_child)
|
||||
self.on_item_selected(self.flowbox, selected_child)
|
||||
|
||||
return True
|
||||
|
||||
@@ -1437,7 +1438,11 @@ class Main(Gtk.Window):
|
||||
Gtk.Image.new_from_icon_name("faugus-play-symbolic", Gtk.IconSize.BUTTON))
|
||||
else:
|
||||
processos = self.load_processes_from_file()
|
||||
if title in processos:
|
||||
if title in self.button_locked:
|
||||
self.menu_item_play.set_sensitive(False)
|
||||
self.button_play.set_sensitive(False)
|
||||
self.button_play.set_image(Gtk.Image.new_from_icon_name("faugus-stop-symbolic", Gtk.IconSize.BUTTON))
|
||||
elif title in processos:
|
||||
self.menu_item_play.set_sensitive(True)
|
||||
self.button_play.set_sensitive(True)
|
||||
self.button_play.set_image(Gtk.Image.new_from_icon_name("faugus-stop-symbolic", Gtk.IconSize.BUTTON))
|
||||
@@ -1446,6 +1451,7 @@ class Main(Gtk.Window):
|
||||
self.button_play.set_sensitive(True)
|
||||
self.button_play.set_image(
|
||||
Gtk.Image.new_from_icon_name("faugus-play-symbolic", Gtk.IconSize.BUTTON))
|
||||
|
||||
else:
|
||||
self.menu_item_edit.set_sensitive(False)
|
||||
self.menu_item_delete.set_sensitive(False)
|
||||
@@ -1494,6 +1500,8 @@ class Main(Gtk.Window):
|
||||
default_runner = ""
|
||||
if default_runner == "GE-Proton Latest (default)":
|
||||
default_runner = "GE-Proton"
|
||||
if default_runner == "Proton-EM Latest":
|
||||
default_runner = "Proton-EM"
|
||||
|
||||
# Handle dialog response
|
||||
if response_id == Gtk.ResponseType.OK:
|
||||
@@ -1603,21 +1611,22 @@ class Main(Gtk.Window):
|
||||
title = game_label.get_text()
|
||||
|
||||
processos = self.load_processes_from_file()
|
||||
self.play_button_locked = True
|
||||
self.button_locked[title] = True
|
||||
|
||||
if title in processos:
|
||||
data = processos[title]
|
||||
pid = data.get("main")
|
||||
if pid:
|
||||
parent = psutil.Process(pid)
|
||||
children = parent.children(recursive=True)
|
||||
|
||||
for child in children:
|
||||
child.terminate()
|
||||
for key in ("umu", "main"):
|
||||
pid = data.get(key)
|
||||
if pid:
|
||||
try:
|
||||
proc = psutil.Process(pid)
|
||||
for child in proc.children(recursive=True):
|
||||
child.terminate()
|
||||
proc.terminate()
|
||||
except psutil.NoSuchProcess:
|
||||
continue
|
||||
|
||||
parent.terminate()
|
||||
|
||||
self.play_button_locked = False
|
||||
return
|
||||
|
||||
# Find the selected game object
|
||||
@@ -1727,6 +1736,7 @@ class Main(Gtk.Window):
|
||||
|
||||
def find_pid(self, game):
|
||||
try:
|
||||
# faugus-run é o processo main, que é um python3
|
||||
parent = psutil.Process(self.processo.pid)
|
||||
all_descendants = parent.children(recursive=True)
|
||||
except psutil.NoSuchProcess:
|
||||
@@ -1737,28 +1747,28 @@ class Main(Gtk.Window):
|
||||
for proc in all_descendants:
|
||||
try:
|
||||
name = os.path.splitext(proc.name())[0].lower()
|
||||
if name == "umu-run" or name == "python3":
|
||||
if name == "umu-run":
|
||||
umu_run_pid = proc.pid
|
||||
break
|
||||
except psutil.NoSuchProcess:
|
||||
continue
|
||||
|
||||
if umu_run_pid:
|
||||
self.save_process_to_file(game.title, umu_run_pid)
|
||||
|
||||
self.menu_item_play.set_sensitive(True)
|
||||
self.button_play.set_sensitive(True)
|
||||
self.button_play.set_image(Gtk.Image.new_from_icon_name("faugus-stop-symbolic", Gtk.IconSize.BUTTON))
|
||||
self.play_button_locked = False
|
||||
return True
|
||||
# Salva apenas o processo python3 (main) e o umu-run (se existir)
|
||||
self.save_process_to_file(
|
||||
game.title,
|
||||
main_pid=self.processo.pid,
|
||||
umu_pid=umu_run_pid
|
||||
)
|
||||
|
||||
self.menu_item_play.set_sensitive(True)
|
||||
self.button_play.set_sensitive(True)
|
||||
self.button_play.set_image(Gtk.Image.new_from_icon_name("faugus-play-symbolic", Gtk.IconSize.BUTTON))
|
||||
self.button_play.set_image(Gtk.Image.new_from_icon_name("faugus-stop-symbolic", Gtk.IconSize.BUTTON))
|
||||
if game.title in self.button_locked:
|
||||
del self.button_locked[game.title]
|
||||
|
||||
return False
|
||||
return True
|
||||
|
||||
def save_process_to_file(self, title, processo_pid, _unused=None):
|
||||
def save_process_to_file(self, title, main_pid, umu_pid=None):
|
||||
os.makedirs(os.path.dirname(running_games), exist_ok=True)
|
||||
|
||||
try:
|
||||
@@ -1767,7 +1777,10 @@ class Main(Gtk.Window):
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
processos = {}
|
||||
|
||||
processos[title] = {"main": processo_pid}
|
||||
processos[title] = {
|
||||
"main": main_pid,
|
||||
"umu": umu_pid
|
||||
}
|
||||
|
||||
with open(running_games, "w") as f:
|
||||
json.dump(processos, f, indent=2)
|
||||
@@ -1801,6 +1814,7 @@ class Main(Gtk.Window):
|
||||
done
|
||||
""", shell=True)
|
||||
self.game_running = False
|
||||
self.button_locked.clear()
|
||||
|
||||
def on_button_add_clicked(self, widget):
|
||||
file_path = ""
|
||||
@@ -1836,6 +1850,8 @@ class Main(Gtk.Window):
|
||||
game_runner = "GE-Proton Latest (default)"
|
||||
if game.runner == "":
|
||||
game_runner = "UMU-Proton Latest"
|
||||
if game.runner == "Proton-EM":
|
||||
game_runner = "Proton-EM Latest"
|
||||
if game_runner == "Linux-Native":
|
||||
edit_game_dialog.combo_box_launcher.set_active(1)
|
||||
|
||||
@@ -2134,6 +2150,8 @@ class Main(Gtk.Window):
|
||||
runner = ""
|
||||
if runner == "GE-Proton Latest (default)":
|
||||
runner = "GE-Proton"
|
||||
if runner == "Proton-EM Latest":
|
||||
runner = "Proton-EM"
|
||||
if add_game_dialog.combo_box_launcher.get_active() == 1:
|
||||
runner = "Linux-Native"
|
||||
|
||||
@@ -2519,6 +2537,8 @@ class Main(Gtk.Window):
|
||||
game.runner = ""
|
||||
if game.runner == "GE-Proton Latest (default)":
|
||||
game.runner = "GE-Proton"
|
||||
if game.runner == "Proton-EM Latest":
|
||||
game.runner = "Proton-EM"
|
||||
if edit_game_dialog.combo_box_launcher.get_active() == 1:
|
||||
game.runner = "Linux-Native"
|
||||
|
||||
@@ -3146,7 +3166,7 @@ class Settings(Gtk.Dialog):
|
||||
self.label_runner.set_halign(Gtk.Align.START)
|
||||
self.combo_box_runner = Gtk.ComboBoxText()
|
||||
|
||||
self.button_proton_manager = Gtk.Button(label=_("GE-Proton Manager"))
|
||||
self.button_proton_manager = Gtk.Button(label=_("Proton Manager"))
|
||||
self.button_proton_manager.connect("clicked", self.on_button_proton_manager_clicked)
|
||||
|
||||
self.label_miscellaneous = Gtk.Label(label=_("Miscellaneous"))
|
||||
@@ -3547,6 +3567,8 @@ class Settings(Gtk.Dialog):
|
||||
default_runner = ""
|
||||
if default_runner == "GE-Proton Latest (default)":
|
||||
default_runner = "GE-Proton"
|
||||
if default_runner == "Proton-EM Latest":
|
||||
default_runner = "Proton-EM"
|
||||
|
||||
config = ConfigManager()
|
||||
config.save_with_values(checkbox_state, default_prefix, mangohud_state, gamemode_state, disable_hidraw_state,
|
||||
@@ -3581,6 +3603,7 @@ class Settings(Gtk.Dialog):
|
||||
# List of default entries
|
||||
self.combo_box_runner.append_text("GE-Proton Latest (default)")
|
||||
self.combo_box_runner.append_text("UMU-Proton Latest")
|
||||
self.combo_box_runner.append_text("Proton-EM Latest")
|
||||
|
||||
# Path to the directory containing the folders
|
||||
if IS_FLATPAK:
|
||||
@@ -3657,6 +3680,8 @@ class Settings(Gtk.Dialog):
|
||||
default_runner = ""
|
||||
if default_runner == "GE-Proton Latest (default)":
|
||||
default_runner = "GE-Proton"
|
||||
if default_runner == "Proton-EM Latest":
|
||||
default_runner = "Proton-EM"
|
||||
|
||||
config = ConfigManager()
|
||||
config.save_with_values(checkbox_state, default_prefix, mangohud_state, gamemode_state, disable_hidraw_state,
|
||||
@@ -3817,6 +3842,8 @@ class Settings(Gtk.Dialog):
|
||||
default_runner = ""
|
||||
if default_runner == "GE-Proton Latest (default)":
|
||||
default_runner = "GE-Proton"
|
||||
if default_runner == "Proton-EM Latest":
|
||||
default_runner = "Proton-EM"
|
||||
|
||||
config = ConfigManager()
|
||||
config.save_with_values(checkbox_state, default_prefix, mangohud_state, gamemode_state, disable_hidraw_state,
|
||||
@@ -3904,6 +3931,8 @@ class Settings(Gtk.Dialog):
|
||||
default_runner = ""
|
||||
if default_runner == "GE-Proton Latest (default)":
|
||||
default_runner = "GE-Proton"
|
||||
if default_runner == "Proton-EM Latest":
|
||||
default_runner = "Proton-EM"
|
||||
|
||||
config = ConfigManager()
|
||||
config.save_with_values(checkbox_state, default_prefix, mangohud_state, gamemode_state, disable_hidraw_state,
|
||||
@@ -4949,6 +4978,8 @@ class AddGame(Gtk.Dialog):
|
||||
self.default_runner = "UMU-Proton Latest"
|
||||
if self.default_runner == "GE-Proton":
|
||||
self.default_runner = "GE-Proton Latest (default)"
|
||||
if self.default_runner == "Proton-EM":
|
||||
self.default_runner = "Proton-EM Latest"
|
||||
|
||||
for i, row in enumerate(model):
|
||||
if row[0] == self.default_runner:
|
||||
@@ -5406,6 +5437,7 @@ class AddGame(Gtk.Dialog):
|
||||
# List of default entries
|
||||
self.combo_box_runner.append_text("GE-Proton Latest (default)")
|
||||
self.combo_box_runner.append_text("UMU-Proton Latest")
|
||||
self.combo_box_runner.append_text("Proton-EM Latest")
|
||||
|
||||
# Path to the directory containing the folders
|
||||
if IS_FLATPAK:
|
||||
@@ -5543,6 +5575,8 @@ class AddGame(Gtk.Dialog):
|
||||
runner = ""
|
||||
if runner == "GE-Proton Latest (default)":
|
||||
runner = "GE-Proton"
|
||||
if runner == "Proton-EM Latest":
|
||||
runner = "Proton-EM"
|
||||
|
||||
command_parts = []
|
||||
|
||||
@@ -5864,6 +5898,8 @@ class AddGame(Gtk.Dialog):
|
||||
runner = ""
|
||||
if runner == "GE-Proton Latest (default)":
|
||||
runner = "GE-Proton"
|
||||
if runner == "Proton-EM Latest":
|
||||
runner = "Proton-EM"
|
||||
|
||||
command_parts = []
|
||||
|
||||
@@ -5919,6 +5955,8 @@ class AddGame(Gtk.Dialog):
|
||||
runner = ""
|
||||
if runner == "GE-Proton Latest (default)":
|
||||
runner = "GE-Proton"
|
||||
if runner == "Proton-EM Latest":
|
||||
runner = "Proton-EM"
|
||||
|
||||
command_parts = []
|
||||
|
||||
@@ -6909,6 +6947,8 @@ def run_file(file_path):
|
||||
default_runner = ""
|
||||
if default_runner == "GE-Proton Latest (default)":
|
||||
default_runner = "GE-Proton"
|
||||
if default_runner == "Proton-EM Latest":
|
||||
default_runner = "Proton-EM"
|
||||
|
||||
command_parts = []
|
||||
|
||||
|
||||
71
faugus_proton_downloader.py
Normal file
71
faugus_proton_downloader.py
Normal file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import os
|
||||
import requests
|
||||
import tarfile
|
||||
import re
|
||||
from pathlib import Path
|
||||
import threading
|
||||
|
||||
STEAM_COMPAT_DIR = Path.home() / ".local/share/Steam/compatibilitytools.d"
|
||||
GITHUB_API_URL = "https://api.github.com/repos/Etaash-mathamsetty/Proton/releases/latest"
|
||||
DOWNLOAD_BASE_URL = "https://github.com/Etaash-mathamsetty/Proton/releases/download"
|
||||
|
||||
def get_latest_release_tag():
|
||||
response = requests.get(GITHUB_API_URL)
|
||||
if response.status_code == 200:
|
||||
return response.json()["tag_name"]
|
||||
else:
|
||||
print("Failed to access GitHub API:", response.status_code, flush=True)
|
||||
return None
|
||||
|
||||
def get_installed_proton_versions():
|
||||
if not STEAM_COMPAT_DIR.exists():
|
||||
return []
|
||||
return sorted([
|
||||
entry.name.replace("proton-", "")
|
||||
for entry in STEAM_COMPAT_DIR.iterdir()
|
||||
if entry.is_dir() and re.match(r"proton-EM-\d+\.\d+-\d+", entry.name)
|
||||
])
|
||||
|
||||
def download_and_extract(version_tag):
|
||||
tar_name = f"proton-{version_tag}.tar.xz"
|
||||
url = f"{DOWNLOAD_BASE_URL}/{version_tag}/{tar_name}"
|
||||
tar_path = STEAM_COMPAT_DIR / tar_name
|
||||
|
||||
print(f"Downloading {tar_name}...", flush=True)
|
||||
response = requests.get(url, stream=True)
|
||||
if response.status_code == 200:
|
||||
with open(tar_path, "wb") as f:
|
||||
for chunk in response.iter_content(chunk_size=8192):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
else:
|
||||
print("Failed to download:", response.status_code)
|
||||
return
|
||||
|
||||
print("Extracting archive...", flush=True)
|
||||
with tarfile.open(tar_path, "r:xz") as tar:
|
||||
tar.extractall(path=STEAM_COMPAT_DIR)
|
||||
|
||||
os.remove(tar_path)
|
||||
print("Proton installed successfully.", flush=True)
|
||||
|
||||
def main():
|
||||
latest_version = get_latest_release_tag()
|
||||
if not latest_version:
|
||||
return
|
||||
|
||||
installed_versions = get_installed_proton_versions()
|
||||
print("Latest available version:", latest_version, flush=True)
|
||||
print("Installed versions:", ", ".join(installed_versions) or "none", flush=True)
|
||||
|
||||
if latest_version not in installed_versions:
|
||||
thread = threading.Thread(target=download_and_extract, args=(latest_version,))
|
||||
thread.start()
|
||||
thread.join()
|
||||
else:
|
||||
print("The latest version is already installed.", flush=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -5,7 +5,6 @@ import gi
|
||||
import os
|
||||
import tarfile
|
||||
import shutil
|
||||
import sys
|
||||
import gettext
|
||||
import locale
|
||||
from pathlib import Path
|
||||
@@ -43,9 +42,7 @@ class PathManager:
|
||||
for path in icon_paths:
|
||||
if Path(path).exists():
|
||||
return path
|
||||
return icon_paths[-1] # Fallback
|
||||
|
||||
GITHUB_API_URL = "https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases"
|
||||
return icon_paths[-1]
|
||||
|
||||
IS_FLATPAK = 'FLATPAK_ID' in os.environ or os.path.exists('/.flatpak-info')
|
||||
if IS_FLATPAK:
|
||||
@@ -53,11 +50,10 @@ if IS_FLATPAK:
|
||||
STEAM_COMPATIBILITY_PATH = Path(os.path.expanduser("~/.local/share/Steam/compatibilitytools.d"))
|
||||
else:
|
||||
faugus_png = PathManager.get_icon('faugus-launcher.png')
|
||||
STEAM_COMPATIBILITY_PATH = PathManager.user_data("Steam/compatibilitytools.d")
|
||||
STEAM_COMPATIBILITY_PATH = Path(PathManager.user_data("Steam/compatibilitytools.d"))
|
||||
|
||||
config_file_dir = PathManager.user_config('faugus-launcher/config.ini')
|
||||
faugus_launcher_dir = PathManager.user_config('faugus-launcher')
|
||||
prefixes_dir = PathManager.user_config('faugus-launcher/prefixes')
|
||||
|
||||
def get_system_locale():
|
||||
lang = os.environ.get('LANG') or os.environ.get('LC_MESSAGES')
|
||||
@@ -65,13 +61,9 @@ def get_system_locale():
|
||||
return lang.split('.')[0]
|
||||
|
||||
try:
|
||||
loc = locale.getdefaultlocale()[0]
|
||||
if loc:
|
||||
return loc
|
||||
return locale.getdefaultlocale()[0] or 'en_US'
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return 'en_US'
|
||||
return 'en_US'
|
||||
|
||||
def get_language_from_config():
|
||||
if os.path.exists(config_file_dir):
|
||||
@@ -82,9 +74,7 @@ def get_language_from_config():
|
||||
return line.split('=', 1)[1].strip()
|
||||
return None
|
||||
|
||||
lang = get_language_from_config()
|
||||
if not lang:
|
||||
lang = get_system_locale()
|
||||
lang = get_language_from_config() or get_system_locale()
|
||||
|
||||
LOCALE_DIR = (
|
||||
PathManager.system_data('locale')
|
||||
@@ -93,42 +83,18 @@ LOCALE_DIR = (
|
||||
)
|
||||
|
||||
try:
|
||||
translation = gettext.translation(
|
||||
'faugus-proton-manager',
|
||||
localedir=LOCALE_DIR,
|
||||
languages=[lang] if lang else ['en_US']
|
||||
)
|
||||
translation = gettext.translation('faugus-proton-manager', localedir=LOCALE_DIR, languages=[lang])
|
||||
translation.install()
|
||||
globals()['_'] = translation.gettext
|
||||
_ = translation.gettext
|
||||
except FileNotFoundError:
|
||||
gettext.install('faugus-proton-manager', localedir=LOCALE_DIR)
|
||||
globals()['_'] = gettext.gettext
|
||||
_ = gettext.gettext
|
||||
|
||||
class ConfigManager:
|
||||
def __init__(self):
|
||||
self.default_config = {
|
||||
'close-onlaunch': 'False',
|
||||
'default-prefix': prefixes_dir,
|
||||
'mangohud': 'False',
|
||||
'gamemode': 'False',
|
||||
'disable-hidraw': 'False',
|
||||
'default-runner': 'GE-Proton',
|
||||
'discrete-gpu': 'False',
|
||||
'splash-disable': 'False',
|
||||
'system-tray': 'False',
|
||||
'start-boot': 'False',
|
||||
'mono-icon': 'False',
|
||||
'interface-mode': 'List',
|
||||
'start-maximized': 'False',
|
||||
'start-fullscreen': 'False',
|
||||
'show-labels': 'False',
|
||||
'smaller-banners': 'False',
|
||||
'enable-logging': 'False',
|
||||
'wayland-driver': 'False',
|
||||
'enable-hdr': 'False',
|
||||
'language': lang,
|
||||
}
|
||||
|
||||
self.config = {}
|
||||
self.load_config()
|
||||
|
||||
@@ -138,9 +104,7 @@ class ConfigManager:
|
||||
for line in f.read().splitlines():
|
||||
if '=' in line:
|
||||
key, value = line.split('=', 1)
|
||||
key = key.strip()
|
||||
value = value.strip().strip('"')
|
||||
self.config[key] = value
|
||||
self.config[key.strip()] = value.strip().strip('"')
|
||||
|
||||
updated = False
|
||||
for key, default_value in self.default_config.items():
|
||||
@@ -157,14 +121,11 @@ class ConfigManager:
|
||||
|
||||
with open(config_file_dir, 'w') as f:
|
||||
for key, value in self.config.items():
|
||||
if key in ['default-prefix', 'default-runner']:
|
||||
f.write(f'{key}="{value}"\n')
|
||||
else:
|
||||
f.write(f'{key}={value}\n')
|
||||
f.write(f'{key}={value}\n')
|
||||
|
||||
class ProtonDownloader(Gtk.Dialog):
|
||||
def __init__(self):
|
||||
super().__init__(title=_("Faugus GE-Proton Manager"))
|
||||
super().__init__(title=_("Faugus Proton Manager"))
|
||||
self.set_resizable(False)
|
||||
self.set_modal(True)
|
||||
self.set_icon_from_file(faugus_png)
|
||||
@@ -195,28 +156,58 @@ class ProtonDownloader(Gtk.Dialog):
|
||||
self.progress_bar.set_margin_bottom(10)
|
||||
self.content_area.add(self.progress_bar)
|
||||
|
||||
# Scrolled window to hold the Grid
|
||||
self.scrolled_window = Gtk.ScrolledWindow()
|
||||
self.scrolled_window.set_size_request(400, 400)
|
||||
self.scrolled_window.set_margin_start(10)
|
||||
self.scrolled_window.set_margin_end(10)
|
||||
self.scrolled_window.set_margin_top(10)
|
||||
self.scrolled_window.set_margin_bottom(10)
|
||||
self.notebook = Gtk.Notebook()
|
||||
self.notebook.set_halign(Gtk.Align.FILL)
|
||||
self.notebook.set_valign(Gtk.Align.FILL)
|
||||
self.notebook.set_vexpand(True)
|
||||
self.notebook.set_hexpand(True)
|
||||
frame.add(self.notebook)
|
||||
|
||||
# Grid for releases
|
||||
self.grid = Gtk.Grid()
|
||||
self.scrolled_window.add(self.grid)
|
||||
# Tab 1: GE-Proton
|
||||
self.grid_ge = Gtk.Grid()
|
||||
self.grid_ge.set_hexpand(True)
|
||||
self.grid_ge.set_row_spacing(5)
|
||||
self.grid_ge.set_column_spacing(10)
|
||||
scroll_ge = Gtk.ScrolledWindow()
|
||||
scroll_ge.set_size_request(400, 400)
|
||||
scroll_ge.set_margin_top(10)
|
||||
scroll_ge.set_margin_bottom(10)
|
||||
scroll_ge.set_margin_start(10)
|
||||
scroll_ge.set_margin_end(10)
|
||||
scroll_ge.add(self.grid_ge)
|
||||
|
||||
frame.add(self.scrolled_window)
|
||||
tab_box_ge = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
tab_label_ge = Gtk.Label(label="GE-Proton")
|
||||
tab_label_ge.set_width_chars(15)
|
||||
tab_label_ge.set_xalign(0.5)
|
||||
tab_box_ge.pack_start(tab_label_ge, True, True, 0)
|
||||
tab_box_ge.set_hexpand(True)
|
||||
tab_box_ge.show_all()
|
||||
self.notebook.append_page(scroll_ge, tab_box_ge)
|
||||
|
||||
# Set row and column spacing
|
||||
self.grid.set_row_spacing(5)
|
||||
self.grid.set_column_spacing(10)
|
||||
# Tab 2: Proton-EM
|
||||
self.grid_em = Gtk.Grid()
|
||||
self.grid_em.set_hexpand(True)
|
||||
self.grid_em.set_row_spacing(5)
|
||||
self.grid_em.set_column_spacing(10)
|
||||
scroll_em = Gtk.ScrolledWindow()
|
||||
scroll_em.set_size_request(400, 400)
|
||||
scroll_em.set_margin_top(10)
|
||||
scroll_em.set_margin_bottom(10)
|
||||
scroll_em.set_margin_start(10)
|
||||
scroll_em.set_margin_end(10)
|
||||
scroll_em.add(self.grid_em)
|
||||
|
||||
tab_box_em = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
tab_label_em = Gtk.Label(label="Proton-EM")
|
||||
tab_label_em.set_width_chars(15)
|
||||
tab_label_em.set_xalign(0.5)
|
||||
tab_box_em.pack_start(tab_label_em, True, True, 0)
|
||||
tab_box_em.set_hexpand(True)
|
||||
tab_box_em.show_all()
|
||||
self.notebook.append_page(scroll_em, tab_box_em)
|
||||
|
||||
self.load_config()
|
||||
|
||||
# Fetch and populate releases in the Grid
|
||||
self.releases = []
|
||||
self.get_releases()
|
||||
self.show_all()
|
||||
self.progress_bar.set_visible(False)
|
||||
@@ -226,50 +217,105 @@ class ProtonDownloader(Gtk.Dialog):
|
||||
cfg = ConfigManager()
|
||||
self.language = cfg.config.get('language', '')
|
||||
|
||||
def filter_releases(self):
|
||||
filtered_releases = []
|
||||
for release in self.releases:
|
||||
filtered_releases.append(release)
|
||||
if release["tag_name"] == "GE-Proton8-1":
|
||||
break
|
||||
return filtered_releases
|
||||
|
||||
def get_releases(self):
|
||||
self.fetch_releases_from_url(
|
||||
"https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases",
|
||||
self.grid_ge
|
||||
)
|
||||
self.fetch_releases_from_url(
|
||||
"https://api.github.com/repos/Etaash-mathamsetty/Proton/releases",
|
||||
self.grid_em
|
||||
)
|
||||
|
||||
def fetch_releases_from_url(self, url, grid):
|
||||
page = 1
|
||||
releases = []
|
||||
while True:
|
||||
response = requests.get(GITHUB_API_URL, params={"page": page, "per_page": 100})
|
||||
response = requests.get(url, params={"page": page, "per_page": 100})
|
||||
if response.status_code == 200:
|
||||
releases = response.json()
|
||||
if not releases:
|
||||
page_releases = response.json()
|
||||
if not page_releases:
|
||||
break
|
||||
self.releases.extend(releases)
|
||||
releases.extend(page_releases)
|
||||
page += 1
|
||||
else:
|
||||
print(_("Error fetching releases:"), response.status_code)
|
||||
break
|
||||
|
||||
self.releases = self.filter_releases()
|
||||
for release in releases:
|
||||
tag_name = release["tag_name"]
|
||||
|
||||
for release in self.releases:
|
||||
self.add_release_to_grid(release)
|
||||
if "GloriousEggroll" in url:
|
||||
# Esperado: GE-Proton8-1
|
||||
if not tag_name.startswith("GE-Proton"):
|
||||
continue
|
||||
try:
|
||||
version_str = tag_name.replace("GE-Proton", "")
|
||||
major, minor = map(int, version_str.split("-"))
|
||||
if (major, minor) < (8, 1):
|
||||
continue
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
def add_release_to_grid(self, release):
|
||||
row_index = len(self.grid.get_children()) // 2
|
||||
elif "Etaash-mathamsetty" in url:
|
||||
# Esperado: EM-10.0-4
|
||||
if not tag_name.startswith("EM-"):
|
||||
continue
|
||||
try:
|
||||
version_str = tag_name.replace("EM-", "")
|
||||
part1, part2 = version_str.split("-")
|
||||
major, minor = map(int, part1.split("."))
|
||||
patch = int(part2)
|
||||
if (major, minor, patch) < (10, 0, 4):
|
||||
continue
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
label = Gtk.Label(label=release["tag_name"], xalign=0)
|
||||
self.add_release_to_grid(release, grid)
|
||||
|
||||
def add_release_to_grid(self, release, grid):
|
||||
tag_name = release["tag_name"]
|
||||
display_tag_name = f"proton-{tag_name}" if tag_name.startswith("EM-") else tag_name
|
||||
|
||||
row_index = len(grid.get_children()) // 2
|
||||
|
||||
label = Gtk.Label(label=display_tag_name, xalign=0)
|
||||
label.set_halign(Gtk.Align.START)
|
||||
label.set_hexpand(True)
|
||||
self.grid.attach(label, 0, row_index, 1, 1)
|
||||
grid.attach(label, 0, row_index, 1, 1)
|
||||
|
||||
version_path = os.path.join(STEAM_COMPATIBILITY_PATH, release["tag_name"])
|
||||
button = Gtk.Button(label=_("Remove") if os.path.exists(version_path) else _("Download"))
|
||||
version_path = self.get_installed_path(display_tag_name)
|
||||
is_installed = os.path.exists(version_path)
|
||||
|
||||
button = Gtk.Button(label=_("Remove") if is_installed else _("Download"))
|
||||
button.connect("clicked", self.on_button_clicked, release)
|
||||
button.set_size_request(120, -1)
|
||||
grid.attach(button, 1, row_index, 1, 1)
|
||||
|
||||
self.grid.attach(button, 1, row_index, 1, 1)
|
||||
def get_installed_path(self, tag_name):
|
||||
if not STEAM_COMPATIBILITY_PATH.exists():
|
||||
return None
|
||||
|
||||
tag_lower = tag_name.lower()
|
||||
for folder in STEAM_COMPATIBILITY_PATH.iterdir():
|
||||
folder_name_lower = folder.name.lower()
|
||||
if folder_name_lower.endswith(tag_lower):
|
||||
return folder
|
||||
if tag_lower.startswith("proton-"):
|
||||
if folder_name_lower.endswith(tag_lower[len("proton-"):]):
|
||||
return folder
|
||||
|
||||
return STEAM_COMPATIBILITY_PATH / tag_name
|
||||
|
||||
def update_button(self, button, new_label):
|
||||
button.set_label(new_label)
|
||||
button.set_sensitive(True)
|
||||
|
||||
def on_button_clicked(self, widget, release):
|
||||
version_path = os.path.join(STEAM_COMPATIBILITY_PATH, release["tag_name"])
|
||||
tag_name = release["tag_name"]
|
||||
if tag_name.startswith("EM-"):
|
||||
tag_name = f"proton-{tag_name}"
|
||||
|
||||
version_path = self.get_installed_path(tag_name)
|
||||
|
||||
if os.path.exists(version_path):
|
||||
self.on_remove_clicked(widget, release)
|
||||
@@ -279,29 +325,33 @@ class ProtonDownloader(Gtk.Dialog):
|
||||
self.on_download_clicked(widget, release)
|
||||
|
||||
def disable_all_buttons(self):
|
||||
for child in self.grid.get_children():
|
||||
if isinstance(child, Gtk.Button):
|
||||
child.set_sensitive(False)
|
||||
for grid in (self.grid_ge, self.grid_em):
|
||||
for child in grid.get_children():
|
||||
if isinstance(child, Gtk.Button):
|
||||
child.set_sensitive(False)
|
||||
|
||||
def enable_all_buttons(self):
|
||||
for child in self.grid.get_children():
|
||||
if isinstance(child, Gtk.Button):
|
||||
child.set_sensitive(True)
|
||||
for grid in (self.grid_ge, self.grid_em):
|
||||
for child in grid.get_children():
|
||||
if isinstance(child, Gtk.Button):
|
||||
child.set_sensitive(True)
|
||||
|
||||
def on_download_clicked(self, widget, release):
|
||||
self.disable_all_buttons()
|
||||
for asset in release["assets"]:
|
||||
if asset["name"].endswith(".tar.gz"):
|
||||
download_url = asset["browser_download_url"]
|
||||
self.download_and_extract(download_url, asset["name"], release["tag_name"], widget)
|
||||
if asset["name"].endswith((".tar.gz", ".tar.xz")):
|
||||
self.download_and_extract(
|
||||
asset["browser_download_url"],
|
||||
asset["name"],
|
||||
release["tag_name"],
|
||||
widget
|
||||
)
|
||||
break
|
||||
|
||||
def download_and_extract(self, url, filename, tag_name, button):
|
||||
dialog = Gtk.Dialog(title=_("Downloading"), parent=self, modal=True)
|
||||
dialog.set_resizable(False)
|
||||
|
||||
button.set_label(_("Downloading..."))
|
||||
self.progress_label.set_text(_("Downloading {tag}...").format(tag=tag_name))
|
||||
display_tag_name = f"proton-{tag_name}" if tag_name.startswith("EM-") else tag_name
|
||||
self.progress_label.set_text(_("Downloading {tag}...").format(tag=display_tag_name))
|
||||
button.set_sensitive(False)
|
||||
|
||||
if not os.path.exists(STEAM_COMPATIBILITY_PATH):
|
||||
@@ -321,16 +371,35 @@ class ProtonDownloader(Gtk.Dialog):
|
||||
self.progress_bar.set_text(f"{int(progress * 100)}%")
|
||||
Gtk.main_iteration_do(False)
|
||||
|
||||
dialog.destroy()
|
||||
|
||||
self.extract_tar_and_update_button(tar_file_path, tag_name, button)
|
||||
|
||||
def extract_tar_and_update_button(self, tar_file_path, tag_name, button):
|
||||
button.set_label(_("Extracting..."))
|
||||
self.progress_label.set_text(_("Extracting {tag}...").format(tag=tag_name))
|
||||
display_tag_name = f"proton-{tag_name}" if tag_name.startswith("EM-") else tag_name
|
||||
self.progress_label.set_text(_("Extracting {tag}...").format(tag=display_tag_name))
|
||||
Gtk.main_iteration_do(False)
|
||||
|
||||
self.extract_tar(tar_file_path, STEAM_COMPATIBILITY_PATH, self.progress_bar)
|
||||
mode = 'r:xz' if tar_file_path.endswith('.tar.xz') else 'r:gz'
|
||||
|
||||
with tarfile.open(tar_file_path, mode) as tar:
|
||||
temp_dir = os.path.join(STEAM_COMPATIBILITY_PATH, f"temp_{tag_name}")
|
||||
os.makedirs(temp_dir, exist_ok=True)
|
||||
tar.extractall(path=temp_dir)
|
||||
|
||||
extracted_dir = None
|
||||
for item in os.listdir(temp_dir):
|
||||
item_path = os.path.join(temp_dir, item)
|
||||
if os.path.isdir(item_path):
|
||||
extracted_dir = item_path
|
||||
break
|
||||
|
||||
if extracted_dir:
|
||||
final_dir = os.path.join(STEAM_COMPATIBILITY_PATH, os.path.basename(extracted_dir))
|
||||
if os.path.exists(final_dir):
|
||||
shutil.rmtree(final_dir)
|
||||
shutil.move(extracted_dir, STEAM_COMPATIBILITY_PATH)
|
||||
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
os.remove(tar_file_path)
|
||||
|
||||
@@ -340,31 +409,14 @@ class ProtonDownloader(Gtk.Dialog):
|
||||
self.enable_all_buttons()
|
||||
button.set_sensitive(True)
|
||||
|
||||
def extract_tar(self, tar_file_path, extract_to, progress_bar):
|
||||
try:
|
||||
with tarfile.open(tar_file_path, "r:gz") as tar:
|
||||
members = tar.getmembers()
|
||||
total_members = len(members)
|
||||
for index, member in enumerate(members, start=1):
|
||||
tar.extract(member, path=extract_to)
|
||||
progress = index / total_members
|
||||
progress_bar.set_fraction(progress)
|
||||
progress_bar.set_text(_("Extracting... {percent}%").format(percent=int(progress * 100)))
|
||||
Gtk.main_iteration_do(False)
|
||||
except Exception as e:
|
||||
print(_("Failed to extract {tar_file_path}: {error}").format(tar_file_path=tar_file_path, error=e))
|
||||
|
||||
def on_remove_clicked(self, widget, release):
|
||||
version_path = os.path.join(STEAM_COMPATIBILITY_PATH, release["tag_name"])
|
||||
if os.path.exists(version_path):
|
||||
version_path = self.get_installed_path(release["tag_name"])
|
||||
if version_path and os.path.exists(version_path):
|
||||
try:
|
||||
shutil.rmtree(version_path)
|
||||
self.update_button(widget, _("Download"))
|
||||
except Exception as e:
|
||||
print(_("Failed to remove {version_path}: {error}").format(version_path=version_path, error=e))
|
||||
|
||||
def update_button(self, button, new_label):
|
||||
button.set_label(new_label)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def apply_dark_theme():
|
||||
desktop_env = Gio.Settings.new("org.gnome.desktop.interface")
|
||||
|
||||
117
faugus_run.py
117
faugus_run.py
@@ -78,6 +78,7 @@ else:
|
||||
config_file_dir = PathManager.user_config('faugus-launcher/config.ini')
|
||||
faugus_launcher_dir = PathManager.user_config('faugus-launcher')
|
||||
faugus_components = PathManager.find_binary('faugus-components')
|
||||
faugus_proton_downloader = PathManager.find_binary('faugus-proton-downloader')
|
||||
prefixes_dir = str(Path.home() / 'Faugus')
|
||||
logs_dir = PathManager.user_config('faugus-launcher/logs')
|
||||
faugus_notification = PathManager.system_data('faugus-launcher/faugus-notification.ogg')
|
||||
@@ -245,9 +246,29 @@ class FaugusRun:
|
||||
Gtk.main_quit()
|
||||
sys.exit()
|
||||
|
||||
def update_protonpath(self, message):
|
||||
compatibility_dir = os.path.expanduser("~/.local/share/Steam/compatibilitytools.d")
|
||||
|
||||
versions = [
|
||||
d for d in os.listdir(compatibility_dir)
|
||||
if os.path.isdir(os.path.join(compatibility_dir, d)) and d.startswith("proton-EM-")
|
||||
]
|
||||
|
||||
if not versions:
|
||||
return message
|
||||
|
||||
versions.sort(key=lambda v: [int(x) for x in re.findall(r'\d+', v)], reverse=True)
|
||||
latest_version = versions[0]
|
||||
updated_message = re.sub(r'PROTONPATH=Proton-EM\b', f'PROTONPATH={latest_version}', message)
|
||||
return updated_message
|
||||
|
||||
def update_test(self):
|
||||
if "Proton-EM" in self.message:
|
||||
self.message = self.update_protonpath(self.message)
|
||||
|
||||
def start_process(self, command):
|
||||
protonpath = next((part.split('=')[1] for part in self.message.split() if part.startswith("PROTONPATH=")), None)
|
||||
if protonpath and protonpath != "GE-Proton":
|
||||
if protonpath and protonpath != "GE-Proton" and protonpath != "Proton-EM":
|
||||
protonpath_path = Path(share_dir) / 'Steam/compatibilitytools.d' / protonpath
|
||||
if not protonpath_path.is_dir():
|
||||
self.close_warning_dialog()
|
||||
@@ -257,14 +278,16 @@ class FaugusRun:
|
||||
self.default_runner = ""
|
||||
if self.default_runner == "GE-Proton Latest (default)":
|
||||
self.default_runner = "GE-Proton"
|
||||
if self.default_runner == "Proton-EM Latest":
|
||||
self.default_runner = "Proton-EM"
|
||||
|
||||
discrete_gpu = "DRI_PRIME=1"
|
||||
self.discrete_gpu = "DRI_PRIME=1"
|
||||
if not self.discrete_gpu:
|
||||
discrete_gpu = "DRI_PRIME=0"
|
||||
self.discrete_gpu = "DRI_PRIME=0"
|
||||
if self.discrete_gpu:
|
||||
discrete_gpu = "DRI_PRIME=1"
|
||||
self.discrete_gpu = "DRI_PRIME=1"
|
||||
if self.discrete_gpu == None:
|
||||
discrete_gpu = "DRI_PRIME=1"
|
||||
self.discrete_gpu = "DRI_PRIME=1"
|
||||
|
||||
if "WINEPREFIX" not in self.message:
|
||||
if self.default_runner:
|
||||
@@ -300,29 +323,83 @@ class FaugusRun:
|
||||
if match:
|
||||
self.game_title = match.group(1).split("/")[-1]
|
||||
|
||||
if "UMU_NO_PROTON" not in self.message:
|
||||
self.run_processes_sequentially()
|
||||
|
||||
def run_processes_sequentially(self):
|
||||
if "UMU_NO_PROTON" not in self.message and "Proton-EM" in self.message:
|
||||
if self.enable_logging:
|
||||
self.message = f'UMU_LOG=1 PROTON_LOG_DIR={logs_dir}/{self.game_title} PROTON_LOG=1 {self.message}'
|
||||
|
||||
self.process = subprocess.Popen(
|
||||
[PathManager.find_binary("bash"), "-c", f"{faugus_components}; {discrete_gpu} {eac_dir} {be_dir} {self.message}"],
|
||||
[PathManager.find_binary("bash"), "-c", f"{faugus_proton_downloader}"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
bufsize=8192,
|
||||
text=True
|
||||
)
|
||||
|
||||
self.stdout_watch_id = GLib.io_add_watch(
|
||||
self.process.stdout,
|
||||
GLib.PRIORITY_LOW,
|
||||
GLib.IO_IN,
|
||||
self.on_output
|
||||
)
|
||||
self.stderr_watch_id = GLib.io_add_watch(
|
||||
self.process.stderr,
|
||||
GLib.PRIORITY_LOW,
|
||||
GLib.IO_IN,
|
||||
self.on_output
|
||||
)
|
||||
|
||||
GLib.child_watch_add(
|
||||
GLib.PRIORITY_DEFAULT,
|
||||
self.process.pid,
|
||||
self.on_proton_downloader_finished
|
||||
)
|
||||
else:
|
||||
self.process = subprocess.Popen(
|
||||
[PathManager.find_binary("bash"), "-c", f"{discrete_gpu} {eac_dir} {be_dir} {self.message}"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
self.execute_final_command()
|
||||
|
||||
print(self.message)
|
||||
def on_proton_downloader_finished(self, pid, status):
|
||||
if hasattr(self, 'stdout_watch_id'):
|
||||
GLib.source_remove(self.stdout_watch_id)
|
||||
if hasattr(self, 'stderr_watch_id'):
|
||||
GLib.source_remove(self.stderr_watch_id)
|
||||
self.update_test()
|
||||
|
||||
GLib.io_add_watch(self.process.stdout, GLib.IO_IN, self.on_output)
|
||||
GLib.io_add_watch(self.process.stderr, GLib.IO_IN, self.on_output)
|
||||
self.execute_final_command()
|
||||
|
||||
GLib.child_watch_add(self.process.pid, self.on_process_exit)
|
||||
def execute_final_command(self):
|
||||
if "UMU_NO_PROTON" not in self.message:
|
||||
cmd = f"{faugus_components}; {self.discrete_gpu} {eac_dir} {be_dir} {self.message}"
|
||||
else:
|
||||
cmd = f"{self.discrete_gpu} {eac_dir} {be_dir} {self.message}"
|
||||
|
||||
self.process = subprocess.Popen(
|
||||
[PathManager.find_binary("bash"), "-c", cmd],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
bufsize=8192,
|
||||
text=True
|
||||
)
|
||||
|
||||
self.stdout_watch_id = GLib.io_add_watch(
|
||||
self.process.stdout,
|
||||
GLib.PRIORITY_LOW,
|
||||
GLib.IO_IN,
|
||||
self.on_output
|
||||
)
|
||||
self.stderr_watch_id = GLib.io_add_watch(
|
||||
self.process.stderr,
|
||||
GLib.PRIORITY_LOW,
|
||||
GLib.IO_IN,
|
||||
self.on_output
|
||||
)
|
||||
|
||||
GLib.child_watch_add(
|
||||
GLib.PRIORITY_DEFAULT,
|
||||
self.process.pid,
|
||||
self.on_process_exit
|
||||
)
|
||||
|
||||
def set_ld_preload(self):
|
||||
lib_paths = [
|
||||
@@ -486,6 +563,12 @@ class FaugusRun:
|
||||
self.label.set_text(_("UMU-Proton is up to date"))
|
||||
if "mtree is OK" in clean_line:
|
||||
self.label2.set_text(_("Steam Runtime is up to date"))
|
||||
if "Downloading proton-EM" in clean_line:
|
||||
self.label.set_text(_("Downloading Proton-EM..."))
|
||||
if "Extracting archive" in clean_line:
|
||||
self.label.set_text(_("Extracting Proton-EM..."))
|
||||
if "Proton installed successfully" in clean_line:
|
||||
self.label.set_text(_("Proton-EM is up to date"))
|
||||
|
||||
if "UMU_NO_PROTON" in self.message:
|
||||
if "steamrt3 is up to date" in clean_line or "mtree is OK" in clean_line:
|
||||
|
||||
Reference in New Issue
Block a user