Some code restructuring

This commit is contained in:
Faugus
2026-01-03 18:20:31 -03:00
parent 6f8fc60cdc
commit 2cc29fcf63
13 changed files with 1202 additions and 1181 deletions

View File

@@ -1,7 +1,7 @@
[Desktop Entry]
Type=Application
Name=Faugus Create Shortcut
Exec=faugus-launcher --shortcut %f
Exec=python -m faugus.shortcut %f
MimeType=application/x-ms-dos-executable;application/x-msi;application/x-ms-shortcut;application/x-bat;text/x-ms-regedit
Icon=@ICON@
NoDisplay=true

72
faugus/config_manager.py Normal file
View File

@@ -0,0 +1,72 @@
from faugus.language_config import *
faugus_launcher_dir = PathManager.user_config('faugus-launcher')
prefixes_dir = str(Path.home() / 'Faugus')
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',
'lossless-location': '',
'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',
'enable-wow64': 'False',
'language': lang,
'logging-warning': 'False',
'show-hidden': 'False',
}
self.config = {}
self.load_config()
def load_config(self):
if os.path.isfile(config_file_dir):
with open(config_file_dir, 'r') as f:
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
updated = False
for key, default_value in self.default_config.items():
if key not in self.config:
self.config[key] = default_value
updated = True
if updated or not os.path.isfile(config_file_dir):
self.save_config()
def save_config(self):
if not os.path.exists(faugus_launcher_dir):
os.makedirs(faugus_launcher_dir)
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')
def save_with_values(self, *args):
keys = list(self.default_config.keys())
for key, value in zip(keys, args):
self.config[key] = str(value)
self.save_config()

28
faugus/dark_theme.py Normal file
View File

@@ -0,0 +1,28 @@
import os
from gi.repository import Gtk, Gio, GLib
IS_FLATPAK = 'FLATPAK_ID' in os.environ or os.path.exists('/.flatpak-info')
def apply_dark_theme():
if IS_FLATPAK:
if (os.environ.get("XDG_CURRENT_DESKTOP")) == "KDE":
Gtk.Settings.get_default().set_property("gtk-theme-name", "Breeze")
try:
proxy = Gio.DBusProxy.new_sync(
Gio.bus_get_sync(Gio.BusType.SESSION, None), 0, None,
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Settings", None)
is_dark = proxy.call_sync(
"Read", GLib.Variant("(ss)", ("org.freedesktop.appearance", "color-scheme")),
0, -1, None).unpack()[0] == 1
except:
is_dark = False
Gtk.Settings.get_default().set_property("gtk-application-prefer-dark-theme", is_dark)
else:
desktop_env = Gio.Settings.new("org.gnome.desktop.interface")
try:
is_dark_theme = desktop_env.get_string("color-scheme") == "prefer-dark"
except Exception:
is_dark_theme = "-dark" in desktop_env.get_string("gtk-theme")
if is_dark_theme:
Gtk.Settings.get_default().set_property("gtk-application-prefer-dark-theme", True)

38
faugus/language_config.py Normal file
View File

@@ -0,0 +1,38 @@
import locale
from faugus.path_manager import *
config_file_dir = PathManager.user_config('faugus-launcher/config.ini')
def get_system_locale():
lang = os.environ.get('LANG') or os.environ.get('LC_MESSAGES')
if lang:
return lang.split('.')[0]
try:
loc = locale.getdefaultlocale()[0]
if loc:
return loc
except Exception:
pass
return 'en_US'
def get_language_from_config():
if os.path.exists(config_file_dir):
with open(config_file_dir, 'r') as f:
for line in f:
line = line.strip()
if line.startswith('language='):
return line.split('=', 1)[1].strip()
return None
lang = get_language_from_config()
if not lang:
lang = get_system_locale()
LOCALE_DIR = (
PathManager.system_data('locale')
if os.path.isdir(PathManager.system_data('locale'))
else os.path.join(os.path.dirname(__file__), 'locale')
)

10
faugus/meson.build Normal file
View File

@@ -0,0 +1,10 @@
py.install_sources(
'components.py',
'proton_downloader.py',
'shortcut.py',
'config_manager.py',
'path_manager.py',
'language_config.py',
'dark_theme.py',
subdir: 'faugus',
)

45
faugus/path_manager.py Normal file
View File

@@ -0,0 +1,45 @@
#!/usr/bin/python3
import os
from pathlib import Path
class PathManager:
@staticmethod
def system_data(*relative_paths):
xdg_data_dirs = os.getenv('XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(':')
for data_dir in xdg_data_dirs:
path = Path(data_dir).joinpath(*relative_paths)
if path.exists():
return str(path)
return str(Path(xdg_data_dirs[0]).joinpath(*relative_paths))
@staticmethod
def user_data(*relative_paths):
xdg_data_home = Path(os.getenv('XDG_DATA_HOME', Path.home() / '.local/share'))
return str(xdg_data_home.joinpath(*relative_paths))
@staticmethod
def user_config(*relative_paths):
xdg_config_home = Path(os.getenv('XDG_CONFIG_HOME', Path.home() / '.config'))
return str(xdg_config_home.joinpath(*relative_paths))
@staticmethod
def find_binary(binary_name):
paths = os.getenv('PATH', '').split(':')
for path in paths:
binary_path = Path(path) / binary_name
if binary_path.exists():
return str(binary_path)
return f'/usr/bin/{binary_name}' # Fallback
@staticmethod
def get_icon(icon_name):
icon_paths = [
PathManager.user_data('icons', icon_name),
PathManager.system_data('icons/hicolor/256x256/apps', icon_name),
PathManager.system_data('icons', icon_name)
]
for path in icon_paths:
if Path(path).exists():
return path
return icon_paths[-1] # Fallback

993
faugus/shortcut.py Executable file
View File

@@ -0,0 +1,993 @@
#!/usr/bin/python3
import gi
import sys
import gettext
import shutil
import subprocess
import re
import webbrowser
import locale
gi.require_version("Gtk", "3.0")
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk, Gdk, GdkPixbuf, GLib
from PIL import Image
from faugus.path_manager import *
from faugus.dark_theme import *
IS_FLATPAK = 'FLATPAK_ID' in os.environ or os.path.exists('/.flatpak-info')
if IS_FLATPAK:
app_dir = str(Path.home() / '.local/share/applications')
faugus_png = PathManager.get_icon('io.github.Faugus.faugus-launcher.png')
lsfgvk_path = Path("/usr/lib/extensions/vulkan/lsfgvk/lib/liblsfg-vk.so")
lsfgvk_path = lsfgvk_path if lsfgvk_path.exists() else Path(os.path.expanduser('~/.local/lib/liblsfg-vk.so'))
else:
app_dir = PathManager.user_data('applications')
faugus_png = PathManager.get_icon('faugus-launcher.png')
lsfgvk_possible_paths = [
Path("/usr/lib/liblsfg-vk.so"),
Path("/usr/lib64/liblsfg-vk.so"),
Path(os.path.expanduser('~/.local/lib/liblsfg-vk.so'))
]
lsfgvk_path = next((p for p in lsfgvk_possible_paths if p.exists()), lsfgvk_possible_paths[-1])
icons_dir = PathManager.user_config('faugus-launcher/icons')
config_file_dir = PathManager.user_config('faugus-launcher/config.ini')
prefixes_dir = str(Path.home() / 'Faugus')
mangohud_dir = PathManager.find_binary('mangohud')
gamemoderun = PathManager.find_binary('gamemoderun')
umu_run = PathManager.user_data('faugus-launcher/umu-run')
faugus_run = PathManager.find_binary('faugus-run')
faugus_launcher_dir = PathManager.user_config('faugus-launcher')
faugus_notification = PathManager.system_data('faugus-launcher/faugus-notification.ogg')
def get_desktop_dir():
try:
desktop_dir = subprocess.check_output(['xdg-user-dir', 'DESKTOP'], text=True).strip()
return desktop_dir
except (FileNotFoundError, subprocess.CalledProcessError):
print("xdg-user-dir not found or failed; falling back to ~/Desktop")
return str(Path.home() / 'Desktop')
desktop_dir = get_desktop_dir()
def find_lossless_dll():
possible_common_locations = [
Path.home() / '.local' / 'share' / 'Steam' / 'steamapps' / 'common',
Path.home() / '.steam' / 'steam' / 'steamapps' / 'common',
Path.home() / '.steam' / 'root' / 'steamapps' / 'common',
Path.home() / 'SteamLibrary' / 'steamapps' / 'common',
Path(os.path.expanduser('~/.var/app/com.valvesoftware.Steam/.steam/steamapps/common/'))
]
for location in possible_common_locations:
dll_candidate = location / 'Lossless Scaling' / 'Lossless.dll'
if dll_candidate.exists():
return str(dll_candidate)
return ""
def get_system_locale():
lang = os.environ.get('LANG') or os.environ.get('LC_MESSAGES')
if lang:
return lang.split('.')[0]
try:
loc = locale.getdefaultlocale()[0]
if loc:
return loc
except Exception:
pass
return 'en_US'
def get_language_from_config():
if os.path.exists(config_file_dir):
with open(config_file_dir, 'r') as f:
for line in f:
line = line.strip()
if line.startswith('language='):
return line.split('=', 1)[1].strip()
return None
lang = get_language_from_config()
if not lang:
lang = get_system_locale()
LOCALE_DIR = (
PathManager.system_data('locale')
if os.path.isdir(PathManager.system_data('locale'))
else os.path.join(os.path.dirname(__file__), 'locale')
)
try:
translation = gettext.translation(
'faugus-launcher',
localedir=LOCALE_DIR,
languages=[lang] if lang else ['en_US']
)
translation.install()
globals()['_'] = translation.gettext
except FileNotFoundError:
gettext.install('faugus-launcher', localedir=LOCALE_DIR)
globals()['_'] = gettext.gettext
def format_title(title):
title = title.strip().lower()
title = re.sub(r"[']", "", title)
title = re.sub(r"[^a-z0-9]+", "-", title)
title = title.strip("-")
return title
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',
'lossless-location': '',
'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',
'enable-wow64': 'False',
'language': lang,
'logging-warning': 'False',
'show-hidden': 'False',
}
self.config = {}
self.load_config()
def load_config(self):
if os.path.isfile(config_file_dir):
with open(config_file_dir, 'r') as f:
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
updated = False
for key, default_value in self.default_config.items():
if key not in self.config:
self.config[key] = default_value
updated = True
if updated or not os.path.isfile(config_file_dir):
self.save_config()
def save_config(self):
if not os.path.exists(faugus_launcher_dir):
os.makedirs(faugus_launcher_dir)
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')
def save_with_values(self, *args):
keys = list(self.default_config.keys())
for key, value in zip(keys, args):
self.config[key] = str(value)
self.save_config()
class CreateShortcut(Gtk.Window):
def __init__(self, file_path):
super().__init__(title="Faugus Launcher")
self.file_path = file_path
self.set_resizable(False)
self.set_icon_from_file(faugus_png)
game_title = os.path.basename(file_path)
self.set_title(game_title)
print(self.file_path)
self.icon_directory = f"{icons_dir}/icon_temp/"
if not os.path.exists(self.icon_directory):
os.makedirs(self.icon_directory)
self.icons_path = icons_dir
self.icon_extracted = os.path.expanduser(f'{self.icons_path}/icon_temp/icon.ico')
self.icon_converted = os.path.expanduser(f'{self.icons_path}/icon_temp/icon.png')
self.icon_temp = f'{self.icons_path}/icon_temp.ico'
self.default_prefix = ""
self.lossless_enabled = False
self.lossless_multiplier = 1
self.lossless_flow = 100
self.lossless_performance = False
self.lossless_hdr = False
self.label_title = Gtk.Label(label=_("Title"))
self.label_title.set_halign(Gtk.Align.START)
self.entry_title = Gtk.Entry()
self.entry_title.connect("changed", self.on_entry_changed, self.entry_title)
self.entry_title.set_tooltip_text(_("Game Title"))
self.label_protonfix = Gtk.Label(label="Protonfix")
self.label_protonfix.set_halign(Gtk.Align.START)
self.entry_protonfix = Gtk.Entry()
self.entry_protonfix.set_tooltip_text("UMU ID")
self.button_search_protonfix = Gtk.Button()
self.button_search_protonfix.set_image(
Gtk.Image.new_from_icon_name("system-search-symbolic", Gtk.IconSize.BUTTON))
self.button_search_protonfix.connect("clicked", self.on_button_search_protonfix_clicked)
self.button_search_protonfix.set_size_request(50, -1)
self.label_launch_arguments = Gtk.Label(label=_("Launch Arguments"))
self.label_launch_arguments.set_halign(Gtk.Align.START)
self.entry_launch_arguments = Gtk.Entry()
self.entry_launch_arguments.set_tooltip_text(_("e.g.: PROTON_USE_WINED3D=1 gamescope -W 2560 -H 1440"))
self.label_game_arguments = Gtk.Label(label=_("Game Arguments"))
self.label_game_arguments.set_halign(Gtk.Align.START)
self.entry_game_arguments = Gtk.Entry()
self.entry_game_arguments.set_tooltip_text(_("e.g.: -d3d11 -fullscreen"))
self.button_lossless = Gtk.Button(label=_("Lossless Scaling Frame Generation"))
self.button_lossless.connect("clicked", self.on_button_lossless_clicked)
self.label_addapp = Gtk.Label(label=_("Additional Application"))
self.label_addapp.set_halign(Gtk.Align.START)
self.entry_addapp = Gtk.Entry()
self.entry_addapp.set_tooltip_text(_("/path/to/the/app"))
self.button_search_addapp = Gtk.Button()
self.button_search_addapp.set_image(Gtk.Image.new_from_icon_name("system-search-symbolic", Gtk.IconSize.BUTTON))
self.button_search_addapp.connect("clicked", self.on_button_search_addapp_clicked)
self.button_search_addapp.set_size_request(50, -1)
self.button_shortcut_icon = Gtk.Button()
self.button_shortcut_icon.set_size_request(120, -1)
self.button_shortcut_icon.set_tooltip_text(_("Select an icon for the shortcut"))
self.button_shortcut_icon.connect("clicked", self.on_button_shortcut_icon_clicked)
self.checkbox_mangohud = Gtk.CheckButton(label="MangoHud")
self.checkbox_mangohud.set_tooltip_text(
_("Shows an overlay for monitoring FPS, temperatures, CPU/GPU load and more."))
self.checkbox_gamemode = Gtk.CheckButton(label="GameMode")
self.checkbox_gamemode.set_tooltip_text(_("Tweaks your system to improve performance."))
self.checkbox_disable_hidraw = Gtk.CheckButton(label=_("Disable Hidraw"))
self.checkbox_disable_hidraw.set_tooltip_text(
_("May fix controller issues with some games. Only works with GE-Proton10 or Proton-EM-10."))
# Button Cancel
self.button_cancel = Gtk.Button(label=_("Cancel"))
self.button_cancel.connect("clicked", self.on_cancel_clicked)
self.button_cancel.set_size_request(150, -1)
# Button Ok
self.button_ok = Gtk.Button(label=_("Ok"))
self.button_ok.connect("clicked", self.on_ok_clicked)
self.button_ok.set_size_request(150, -1)
css_provider = Gtk.CssProvider()
css = """
.entry {
border-color: Red;
}
"""
css_provider.load_from_data(css.encode('utf-8'))
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), css_provider,
Gtk.STYLE_PROVIDER_PRIORITY_USER)
self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
self.box.set_margin_start(0)
self.box.set_margin_end(0)
self.box.set_margin_top(0)
self.box.set_margin_bottom(0)
frame = Gtk.Frame()
frame.set_margin_start(10)
frame.set_margin_end(10)
frame.set_margin_top(10)
frame.set_margin_bottom(10)
self.grid_title = Gtk.Grid()
self.grid_title.set_row_spacing(10)
self.grid_title.set_column_spacing(10)
self.grid_title.set_margin_start(10)
self.grid_title.set_margin_end(10)
self.grid_title.set_margin_top(10)
self.grid_protonfix = Gtk.Grid()
self.grid_protonfix.set_row_spacing(10)
self.grid_protonfix.set_column_spacing(10)
self.grid_protonfix.set_margin_start(10)
self.grid_protonfix.set_margin_end(10)
self.grid_protonfix.set_margin_top(10)
self.grid_launch_arguments = Gtk.Grid()
self.grid_launch_arguments.set_row_spacing(10)
self.grid_launch_arguments.set_column_spacing(10)
self.grid_launch_arguments.set_margin_start(10)
self.grid_launch_arguments.set_margin_end(10)
self.grid_launch_arguments.set_margin_top(10)
self.grid_game_arguments = Gtk.Grid()
self.grid_game_arguments.set_row_spacing(10)
self.grid_game_arguments.set_column_spacing(10)
self.grid_game_arguments.set_margin_start(10)
self.grid_game_arguments.set_margin_end(10)
self.grid_game_arguments.set_margin_top(10)
self.grid_lossless = Gtk.Grid()
self.grid_lossless.set_row_spacing(10)
self.grid_lossless.set_column_spacing(10)
self.grid_lossless.set_margin_start(10)
self.grid_lossless.set_margin_end(10)
self.grid_lossless.set_margin_top(10)
self.grid_addapp = Gtk.Grid()
self.grid_addapp.set_row_spacing(10)
self.grid_addapp.set_column_spacing(10)
self.grid_addapp.set_margin_start(10)
self.grid_addapp.set_margin_end(10)
self.grid_addapp.set_margin_top(10)
self.grid_title.attach(self.label_title, 0, 0, 4, 1)
self.grid_title.attach(self.entry_title, 0, 1, 4, 1)
self.entry_title.set_hexpand(True)
self.grid_protonfix.attach(self.label_protonfix, 0, 0, 1, 1)
self.grid_protonfix.attach(self.entry_protonfix, 0, 1, 3, 1)
self.entry_protonfix.set_hexpand(True)
self.grid_protonfix.attach(self.button_search_protonfix, 3, 1, 1, 1)
self.grid_launch_arguments.attach(self.label_launch_arguments, 0, 0, 4, 1)
self.grid_launch_arguments.attach(self.entry_launch_arguments, 0, 1, 4, 1)
self.entry_launch_arguments.set_hexpand(True)
self.grid_game_arguments.attach(self.label_game_arguments, 0, 0, 4, 1)
self.grid_game_arguments.attach(self.entry_game_arguments, 0, 1, 4, 1)
self.entry_game_arguments.set_hexpand(True)
self.grid_addapp.attach(self.label_addapp, 0, 0, 1, 1)
self.grid_addapp.attach(self.entry_addapp, 0, 1, 3, 1)
self.entry_addapp.set_hexpand(True)
self.grid_addapp.attach(self.button_search_addapp, 3, 1, 1, 1)
self.grid_lossless.attach(self.button_lossless, 0, 0, 1, 1)
self.button_lossless.set_hexpand(True)
self.grid_tools = Gtk.Grid(orientation=Gtk.Orientation.VERTICAL)
self.grid_tools.set_row_spacing(10)
self.grid_tools.set_column_spacing(10)
self.grid_tools.set_margin_start(10)
self.grid_tools.set_margin_end(10)
self.grid_tools.set_margin_top(10)
self.grid_tools.set_margin_bottom(10)
self.grid_shortcut_icon = Gtk.Grid(orientation=Gtk.Orientation.VERTICAL)
self.grid_shortcut_icon.set_row_spacing(10)
self.grid_shortcut_icon.set_column_spacing(10)
self.grid_shortcut_icon.set_margin_start(10)
self.grid_shortcut_icon.set_margin_end(10)
self.grid_shortcut_icon.set_margin_top(10)
self.grid_shortcut_icon.set_margin_bottom(10)
self.grid_tools.add(self.checkbox_mangohud)
self.grid_tools.add(self.checkbox_gamemode)
self.grid_tools.add(self.checkbox_disable_hidraw)
self.grid_shortcut_icon.add(self.button_shortcut_icon)
self.grid_shortcut_icon.set_valign(Gtk.Align.CENTER)
self.box_tools = Gtk.Box()
self.box_tools.pack_start(self.grid_tools, False, False, 0)
self.box_tools.pack_end(self.grid_shortcut_icon, False, False, 0)
bottom_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
bottom_box.set_margin_start(10)
bottom_box.set_margin_end(10)
# botton_box.set_margin_top(10)
bottom_box.set_margin_bottom(10)
self.button_cancel.set_hexpand(True)
self.button_ok.set_hexpand(True)
bottom_box.pack_start(self.button_cancel, True, True, 0)
bottom_box.pack_start(self.button_ok, True, True, 0)
self.main_grid = Gtk.Grid(orientation=Gtk.Orientation.VERTICAL)
self.main_grid.add(self.grid_title)
self.main_grid.add(self.grid_protonfix)
self.main_grid.add(self.grid_launch_arguments)
self.main_grid.add(self.grid_game_arguments)
self.main_grid.add(self.grid_addapp)
self.main_grid.add(self.grid_lossless)
self.main_grid.add(self.box_tools)
self.load_config()
self.mangohud_enabled = os.path.exists(mangohud_dir)
if not self.mangohud_enabled:
self.checkbox_mangohud.set_sensitive(False)
self.checkbox_mangohud.set_active(False)
self.checkbox_mangohud.set_tooltip_text(
_("Shows an overlay for monitoring FPS, temperatures, CPU/GPU load and more. NOT INSTALLED."))
self.gamemode_enabled = os.path.exists(gamemoderun) or os.path.exists("/usr/games/gamemoderun")
if not self.gamemode_enabled:
self.checkbox_gamemode.set_sensitive(False)
self.checkbox_gamemode.set_active(False)
self.checkbox_gamemode.set_tooltip_text(_("Tweaks your system to improve performance. NOT INSTALLED."))
lossless_dll_path = find_lossless_dll()
if os.path.exists(lsfgvk_path):
if lossless_dll_path or os.path.exists(self.lossless_location):
self.button_lossless.set_sensitive(True)
else:
self.button_lossless.set_sensitive(False)
self.button_lossless.set_tooltip_text(_("Lossless.dll NOT FOUND. If it's installed, go to Faugus Launcher's settings and set the location."))
else:
self.button_lossless.set_sensitive(False)
self.button_lossless.set_tooltip_text(_("Lossless Scaling Vulkan Layer NOT INSTALLED."))
frame.add(self.main_grid)
self.box.add(frame)
self.box.add(bottom_box)
self.add(self.box)
if not os.path.exists(self.icon_directory):
os.makedirs(self.icon_directory)
try:
# Attempt to extract the icon
command = f'icoextract "{file_path}" "{self.icon_extracted}"'
result = subprocess.run(command, shell=True, text=True, capture_output=True)
# Check if there was an error in executing the command
if result.returncode != 0:
if "NoIconsAvailableError" in result.stderr or "PEFormatError" in result.stderr:
print("The file does not contain icons.")
self.button_shortcut_icon.set_image(self.set_image_shortcut_icon())
else:
print(f"Error extracting icon: {result.stderr}")
else:
# Convert the extracted icon to PNG
command_magick = shutil.which("magick") or shutil.which("convert")
os.system(f'{command_magick} "{self.icon_extracted}" "{self.icon_converted}"')
if os.path.isfile(self.icon_extracted):
os.remove(self.icon_extracted)
largest_image = self.find_largest_resolution(self.icon_directory)
shutil.move(largest_image, os.path.expanduser(self.icon_temp))
pixbuf = GdkPixbuf.Pixbuf.new_from_file(self.icon_temp)
scaled_pixbuf = pixbuf.scale_simple(50, 50, GdkPixbuf.InterpType.BILINEAR)
image = Gtk.Image.new_from_file(self.icon_temp)
image.set_from_pixbuf(scaled_pixbuf)
self.button_shortcut_icon.set_image(image)
except Exception as e:
print(f"An error occurred: {e}")
shutil.rmtree(self.icon_directory)
# Connect the destroy signal to Gtk.main_quit
self.connect("destroy", Gtk.main_quit)
def on_button_lossless_clicked(self, widget):
dialog = Gtk.Dialog(title=_("Lossless Scaling Frame Generation"), parent=self, flags=0)
dialog.set_resizable(False)
dialog.set_icon_from_file(faugus_png)
frame = Gtk.Frame()
frame.set_margin_start(10)
frame.set_margin_end(10)
frame.set_margin_top(10)
frame.set_margin_bottom(10)
grid = Gtk.Grid()
grid.set_row_spacing(10)
grid.set_column_spacing(10)
grid.set_margin_top(10)
grid.set_margin_bottom(10)
grid.set_margin_start(10)
grid.set_margin_end(10)
enabled = val if (val := getattr(self, "lossless_enabled", False)) != "" else False
multiplier = val if (val := getattr(self, "lossless_multiplier", 1)) != "" else 1
flow = val if (val := getattr(self, "lossless_flow", 100)) != "" else 100
performance = val if (val := getattr(self, "lossless_performance", False)) != "" else False
hdr = val if (val := getattr(self, "lossless_hdr", False)) != "" else False
checkbox_enable = Gtk.CheckButton(label="Enable")
checkbox_enable.set_active(enabled)
checkbox_enable.set_halign(Gtk.Align.START)
label_multiplier = Gtk.Label(label=_("Multiplier"))
label_multiplier.set_halign(Gtk.Align.START)
spin_multiplier = Gtk.SpinButton()
spin_multiplier.set_adjustment(Gtk.Adjustment(value=multiplier, lower=1, upper=20, step_increment=1))
spin_multiplier.set_numeric(True)
spin_multiplier.set_tooltip_text(_("Multiply the FPS."))
label_flow = Gtk.Label(label=_("Flow Scale"))
label_flow.set_halign(Gtk.Align.START)
adjustment = Gtk.Adjustment(value=flow, lower=25, upper=100, step_increment=1)
scale_flow = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=adjustment)
scale_flow.set_digits(0)
scale_flow.set_hexpand(True)
scale_flow.set_value_pos(Gtk.PositionType.RIGHT)
scale_flow.set_tooltip_text(_("Lower the internal motion estimation resolution."))
checkbox_performance = Gtk.CheckButton(label=_("Performance Mode"))
checkbox_performance.set_tooltip_text(_("Massively improve performance at the cost of quality."))
checkbox_performance.set_active(performance)
checkbox_hdr = Gtk.CheckButton(label=_("HDR Mode"))
checkbox_hdr.set_tooltip_text(_("Enable special HDR-only behavior."))
checkbox_hdr.set_active(hdr)
def on_enable_toggled(cb):
active = cb.get_active()
label_multiplier.set_sensitive(active)
spin_multiplier.set_sensitive(active)
label_flow.set_sensitive(active)
scale_flow.set_sensitive(active)
checkbox_performance.set_sensitive(active)
checkbox_hdr.set_sensitive(active)
checkbox_enable.connect("toggled", on_enable_toggled)
on_enable_toggled(checkbox_enable)
grid.attach(checkbox_enable, 0, 0, 1, 1)
grid.attach(label_multiplier, 0, 1, 1, 1)
grid.attach(spin_multiplier, 0, 2, 1, 1)
grid.attach(label_flow, 0, 3, 1, 1)
grid.attach(scale_flow, 0, 4, 1, 1)
grid.attach(checkbox_performance, 0, 5, 1, 1)
grid.attach(checkbox_hdr, 0, 6, 1, 1)
frame.add(grid)
button_cancel = Gtk.Button(label=_("Cancel"))
button_cancel.set_size_request(150, -1)
button_cancel.set_hexpand(True)
button_cancel.connect("clicked", lambda b: dialog.response(Gtk.ResponseType.CANCEL))
button_ok = Gtk.Button(label=_("Ok"))
button_ok.set_size_request(150, -1)
button_ok.set_hexpand(True)
button_ok.connect("clicked", lambda b: dialog.response(Gtk.ResponseType.OK))
bottom_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
bottom_box.set_margin_start(10)
bottom_box.set_margin_end(10)
bottom_box.set_margin_bottom(10)
bottom_box.pack_start(button_cancel, True, True, 0)
bottom_box.pack_start(button_ok, True, True, 0)
content_area = dialog.get_content_area()
content_area.pack_start(frame, True, True, 0)
content_area.pack_start(bottom_box, False, False, 0)
dialog.show_all()
response = dialog.run()
if response == Gtk.ResponseType.OK:
self.lossless_enabled = checkbox_enable.get_active()
self.lossless_multiplier = spin_multiplier.get_value_as_int()
self.lossless_flow = scale_flow.get_value()
self.lossless_performance = checkbox_performance.get_active()
self.lossless_hdr = checkbox_hdr.get_active()
dialog.destroy()
return response
def on_button_search_addapp_clicked(self, widget):
filechooser = Gtk.FileChooserNative(
title=_("Select an additional application"),
transient_for=self,
action=Gtk.FileChooserAction.OPEN,
accept_label=_("Open"),
cancel_label=_("Cancel"),
)
filechooser.set_current_folder(os.path.expanduser("~/"))
windows_filter = Gtk.FileFilter()
windows_filter.set_name(_("Windows files"))
windows_filter.add_pattern("*.exe")
windows_filter.add_pattern("*.msi")
windows_filter.add_pattern("*.bat")
windows_filter.add_pattern("*.lnk")
windows_filter.add_pattern("*.reg")
all_files_filter = Gtk.FileFilter()
all_files_filter.set_name(_("All files"))
all_files_filter.add_pattern("*")
filechooser.add_filter(windows_filter)
filechooser.add_filter(all_files_filter)
filechooser.set_filter(windows_filter)
response = filechooser.run()
if response == Gtk.ResponseType.ACCEPT:
self.entry_addapp.set_text(filechooser.get_filename())
filechooser.destroy()
def find_largest_resolution(self, directory):
largest_image = None
largest_resolution = (0, 0) # (width, height)
# Define a set of valid image extensions
valid_image_extensions = {'.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff'}
for file_name in os.listdir(directory):
file_path = os.path.join(directory, file_name)
if os.path.isfile(file_path):
# Check if the file has a valid image extension
if os.path.splitext(file_name)[1].lower() in valid_image_extensions:
try:
with Image.open(file_path) as img:
width, height = img.size
if width * height > largest_resolution[0] * largest_resolution[1]:
largest_resolution = (width, height)
largest_image = file_path
except IOError:
print(f"Unable to open {file_path}")
return largest_image
def on_button_search_protonfix_clicked(self, widget):
webbrowser.open("https://umu.openwinecomponents.org/")
def load_config(self):
cfg = ConfigManager()
self.default_prefix = cfg.config.get('default-prefix', '').strip('"')
mangohud = cfg.config.get('mangohud', 'False') == 'True'
gamemode = cfg.config.get('gamemode', 'False') == 'True'
disable_hidraw = cfg.config.get('disable-hidraw', 'False') == 'True'
self.default_runner = cfg.config.get('default-runner', '').strip('"')
self.lossless_location = cfg.config.get('lossless-location', '')
self.checkbox_mangohud.set_active(mangohud)
self.checkbox_gamemode.set_active(gamemode)
self.checkbox_disable_hidraw.set_active(disable_hidraw)
def on_cancel_clicked(self, widget):
if os.path.isfile(self.icon_temp):
os.remove(self.icon_temp)
if os.path.isdir(self.icon_directory):
shutil.rmtree(self.icon_directory)
self.destroy()
def on_ok_clicked(self, widget):
validation_result = self.validate_fields()
if not validation_result:
self.set_sensitive(True)
return
title = self.entry_title.get_text()
title_formatted = format_title(title)
addapp = self.entry_addapp.get_text()
addapp_bat = f"{os.path.dirname(self.file_path)}/faugus-{title_formatted}.bat"
if self.entry_addapp.get_text():
with open(addapp_bat, "w") as bat_file:
bat_file.write(f'start "" "z:{addapp}"\n')
bat_file.write(f'start "" "z:{self.file_path}"\n')
if os.path.isfile(os.path.expanduser(self.icon_temp)):
os.rename(os.path.expanduser(self.icon_temp), f'{self.icons_path}/{title_formatted}.ico')
# Check if the icon file exists
new_icon_path = f"{icons_dir}/{title_formatted}.ico"
if not os.path.exists(new_icon_path):
new_icon_path = faugus_png
protonfix = self.entry_protonfix.get_text()
launch_arguments = self.entry_launch_arguments.get_text()
game_arguments = self.entry_game_arguments.get_text()
lossless_enabled = self.lossless_enabled
lossless_multiplier = self.lossless_multiplier
lossless_flow = self.lossless_flow
lossless_performance = self.lossless_performance
lossless_hdr = self.lossless_hdr
mangohud = "MANGOHUD=1" if self.checkbox_mangohud.get_active() else ""
gamemode = "gamemoderun" if self.checkbox_gamemode.get_active() else ""
disable_hidraw = "PROTON_DISABLE_HIDRAW=1" if self.checkbox_disable_hidraw.get_active() else ""
# Get the directory containing the executable
game_directory = os.path.dirname(self.file_path)
command_parts = []
# Add command parts if they are not empty
if mangohud:
command_parts.append(mangohud)
if disable_hidraw:
command_parts.append(disable_hidraw)
# command_parts.append(f'WINEPREFIX={self.default_prefix}/default')
if protonfix:
command_parts.append(f'GAMEID={protonfix}')
else:
command_parts.append(f'GAMEID={title_formatted}')
if gamemode:
command_parts.append(gamemode)
if launch_arguments:
command_parts.append(launch_arguments)
if lossless_enabled:
command_parts.append("LSFG_LEGACY=1")
if lossless_multiplier:
command_parts.append(f"LSFG_MULTIPLIER={lossless_multiplier}")
if lossless_flow:
command_parts.append(f"LSFG_FLOW_SCALE={lossless_flow/100}")
if lossless_performance:
command_parts.append(f"LSFG_PERFORMANCE_MODE={1 if lossless_performance == 'true' else 0}")
if lossless_hdr:
command_parts.append(f"LSFG_HDR_MODE={1 if lossless_hdr == 'true' else 0}")
# Add the fixed command and remaining arguments
command_parts.append(f"'{umu_run}'")
if self.entry_addapp.get_text():
command_parts.append(f"'{addapp_bat}'")
elif self.file_path:
command_parts.append(f"'{self.file_path}'")
if game_arguments:
command_parts.append(f"{game_arguments}")
# Join all parts into a single command
command = ' '.join(command_parts)
# Create a .desktop file
if IS_FLATPAK:
desktop_file_content = (
f'[Desktop Entry]\n'
f'Name={title}\n'
f'Exec=flatpak run --command={faugus_run} io.github.Faugus.faugus-launcher "{command}"\n'
f'Icon={new_icon_path}\n'
f'Type=Application\n'
f'Categories=Game;\n'
f'Path={game_directory}\n'
)
else:
desktop_file_content = (
f'[Desktop Entry]\n'
f'Name={title}\n'
f'Exec={faugus_run} "{command}"\n'
f'Icon={new_icon_path}\n'
f'Type=Application\n'
f'Categories=Game;\n'
f'Path={game_directory}\n'
)
# Check if the destination directory exists and create if it doesn't
applications_directory = app_dir
if not os.path.exists(applications_directory):
os.makedirs(applications_directory)
desktop_directory = desktop_dir
if not os.path.exists(desktop_directory):
os.makedirs(desktop_directory)
applications_shortcut_path = f"{app_dir}/{title_formatted}.desktop"
with open(applications_shortcut_path, 'w') as desktop_file:
desktop_file.write(desktop_file_content)
# Make the .desktop file executable
os.chmod(applications_shortcut_path, 0o755)
# Copy the shortcut to Desktop
desktop_shortcut_path = f"{desktop_dir}/{title_formatted}.desktop"
shutil.copyfile(applications_shortcut_path, desktop_shortcut_path)
os.chmod(desktop_shortcut_path, 0o755)
if os.path.isfile(self.icon_temp):
os.remove(self.icon_temp)
if os.path.isdir(self.icon_directory):
shutil.rmtree(self.icon_directory)
self.destroy()
def on_entry_changed(self, widget, entry):
if entry.get_text():
entry.get_style_context().remove_class("entry")
def set_image_shortcut_icon(self):
image_path = faugus_png
pixbuf = GdkPixbuf.Pixbuf.new_from_file(image_path)
scaled_pixbuf = pixbuf.scale_simple(50, 50, GdkPixbuf.InterpType.BILINEAR)
image = Gtk.Image.new_from_pixbuf(scaled_pixbuf)
return image
def on_button_shortcut_icon_clicked(self, widget):
self.set_sensitive(False)
path = self.file_path
if not os.path.exists(self.icon_directory):
os.makedirs(self.icon_directory)
try:
command = f'icoextract "{path}" "{self.icon_extracted}"'
result = subprocess.run(command, shell=True, text=True, capture_output=True)
if result.returncode != 0:
if "NoIconsAvailableError" in result.stderr or "PEFormatError" in result.stderr:
print("The file does not contain icons.")
self.button_shortcut_icon.set_image(self.set_image_shortcut_icon())
else:
print(f"Error extracting icon: {result.stderr}")
else:
command_magick = shutil.which("magick") or shutil.which("convert")
os.system(f'{command_magick} "{self.icon_extracted}" "{self.icon_converted}"')
if os.path.isfile(self.icon_extracted):
os.remove(self.icon_extracted)
except Exception as e:
print(f"An error occurred: {e}")
def is_valid_image(file_path):
try:
pixbuf = GdkPixbuf.Pixbuf.new_from_file(file_path)
return pixbuf is not None
except Exception:
return False
filechooser = Gtk.FileChooserNative.new(
_("Select an icon for the shortcut"),
self,
Gtk.FileChooserAction.OPEN,
_("Open"),
_("Cancel")
)
filter_ico = Gtk.FileFilter()
filter_ico.set_name(_("Image files"))
filter_ico.add_pattern("*.png")
filter_ico.add_pattern("*.jpg")
filter_ico.add_pattern("*.jpeg")
filter_ico.add_pattern("*.bmp")
filter_ico.add_pattern("*.gif")
filter_ico.add_pattern("*.svg")
filter_ico.add_pattern("*.ico")
filechooser.add_filter(filter_ico)
filechooser.set_current_folder(self.icon_directory)
response = filechooser.run()
if response == Gtk.ResponseType.ACCEPT:
file_path = filechooser.get_filename()
if not file_path or not is_valid_image(file_path):
dialog_image = Gtk.Dialog(title="Faugus Launcher", transient_for=self, modal=True)
dialog_image.set_resizable(False)
dialog_image.set_icon_from_file(faugus_png)
subprocess.Popen(["canberra-gtk-play", "-f", faugus_notification])
label = Gtk.Label()
label.set_label(_("The selected file is not a valid image."))
label.set_halign(Gtk.Align.CENTER)
label2 = Gtk.Label()
label2.set_label(_("Please choose another one."))
label2.set_halign(Gtk.Align.CENTER)
button_yes = Gtk.Button(label=_("Ok"))
button_yes.set_size_request(150, -1)
button_yes.connect("clicked", lambda x: dialog_image.response(Gtk.ResponseType.YES))
content_area = dialog_image.get_content_area()
content_area.set_border_width(0)
content_area.set_halign(Gtk.Align.CENTER)
content_area.set_valign(Gtk.Align.CENTER)
content_area.set_vexpand(True)
content_area.set_hexpand(True)
box_top = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
box_top.set_margin_start(20)
box_top.set_margin_end(20)
box_top.set_margin_top(20)
box_top.set_margin_bottom(20)
box_bottom = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
box_bottom.set_margin_start(10)
box_bottom.set_margin_end(10)
box_bottom.set_margin_bottom(10)
box_top.pack_start(label, True, True, 0)
box_top.pack_start(label2, True, True, 0)
box_bottom.pack_start(button_yes, True, True, 0)
content_area.add(box_top)
content_area.add(box_bottom)
dialog_image.show_all()
dialog_image.run()
dialog_image.destroy()
else:
shutil.copyfile(file_path, self.icon_temp)
pixbuf = GdkPixbuf.Pixbuf.new_from_file(self.icon_temp)
scaled_pixbuf = pixbuf.scale_simple(50, 50, GdkPixbuf.InterpType.BILINEAR)
image = Gtk.Image.new_from_file(self.icon_temp)
image.set_from_pixbuf(scaled_pixbuf)
self.button_shortcut_icon.set_image(image)
filechooser.destroy()
if os.path.isdir(self.icon_directory):
shutil.rmtree(self.icon_directory)
self.set_sensitive(True)
def update_preview(self, dialog):
if file_path := dialog.get_preview_filename():
try:
pixbuf = GdkPixbuf.Pixbuf.new_from_file(file_path)
max_width = 400
max_height = 400
width = pixbuf.get_width()
height = pixbuf.get_height()
if width > max_width or height > max_height:
ratio = min(max_width / width, max_height / height)
new_width = int(width * ratio)
new_height = int(height * ratio)
pixbuf = pixbuf.scale_simple(new_width, new_height, GdkPixbuf.InterpType.BILINEAR)
image = Gtk.Image.new_from_pixbuf(pixbuf)
dialog.set_preview_widget(image)
dialog.set_preview_widget_active(True)
dialog.get_preview_widget().set_size_request(max_width, max_height)
except GLib.Error:
dialog.set_preview_widget_active(False)
else:
dialog.set_preview_widget_active(False)
def validate_fields(self):
title = self.entry_title.get_text()
self.entry_title.get_style_context().remove_class("entry")
if not title:
self.entry_title.get_style_context().add_class("entry")
return False
return True
def main():
os.environ["GTK_USE_PORTAL"] = "1"
apply_dark_theme()
exec_path = sys.argv[1]
win = CreateShortcut(exec_path)
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load Diff

View File

@@ -2,47 +2,15 @@
import requests
import gi
import os
import tarfile
import shutil
import gettext
import locale
from pathlib import Path
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gio, GLib
class PathManager:
@staticmethod
def system_data(*relative_paths):
xdg_data_dirs = os.getenv('XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(':')
for data_dir in xdg_data_dirs:
path = Path(data_dir).joinpath(*relative_paths)
if path.exists():
return str(path)
return str(Path(xdg_data_dirs[0]).joinpath(*relative_paths))
@staticmethod
def user_data(*relative_paths):
xdg_data_home = Path(os.getenv('XDG_DATA_HOME', Path.home() / '.local/share'))
return str(xdg_data_home.joinpath(*relative_paths))
@staticmethod
def user_config(*relative_paths):
xdg_config_home = Path(os.getenv('XDG_CONFIG_HOME', Path.home() / '.config'))
return str(xdg_config_home.joinpath(*relative_paths))
@staticmethod
def get_icon(icon_name):
icon_paths = [
PathManager.user_data('icons', icon_name),
PathManager.system_data('icons/hicolor/256x256/apps', icon_name),
PathManager.system_data('icons', icon_name)
]
for path in icon_paths:
if Path(path).exists():
return path
return icon_paths[-1]
from faugus.language_config import *
from faugus.dark_theme import *
IS_FLATPAK = 'FLATPAK_ID' in os.environ or os.path.exists('/.flatpak-info')
if IS_FLATPAK:
@@ -55,33 +23,6 @@ else:
config_file_dir = PathManager.user_config('faugus-launcher/config.ini')
faugus_launcher_dir = PathManager.user_config('faugus-launcher')
def get_system_locale():
lang = os.environ.get('LANG') or os.environ.get('LC_MESSAGES')
if lang:
return lang.split('.')[0]
try:
return locale.getdefaultlocale()[0] or 'en_US'
except Exception:
return 'en_US'
def get_language_from_config():
if os.path.exists(config_file_dir):
with open(config_file_dir, 'r') as f:
for line in f:
line = line.strip()
if line.startswith('language='):
return line.split('=', 1)[1].strip()
return None
lang = get_language_from_config() or get_system_locale()
LOCALE_DIR = (
PathManager.system_data('locale')
if os.path.isdir(PathManager.system_data('locale'))
else os.path.join(os.path.dirname(__file__), 'locale')
)
try:
translation = gettext.translation('faugus-proton-manager', localedir=LOCALE_DIR, languages=[lang])
translation.install()
@@ -444,31 +385,6 @@ class ProtonDownloader(Gtk.Dialog):
except Exception:
pass
def apply_dark_theme():
if IS_FLATPAK:
if (os.environ.get("XDG_CURRENT_DESKTOP")) == "KDE":
Gtk.Settings.get_default().set_property("gtk-theme-name", "Breeze")
try:
proxy = Gio.DBusProxy.new_sync(
Gio.bus_get_sync(Gio.BusType.SESSION, None), 0, None,
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Settings", None)
is_dark = proxy.call_sync(
"Read", GLib.Variant("(ss)", ("org.freedesktop.appearance", "color-scheme")),
0, -1, None).unpack()[0] == 1
except:
is_dark = False
Gtk.Settings.get_default().set_property("gtk-application-prefer-dark-theme", is_dark)
else:
desktop_env = Gio.Settings.new("org.gnome.desktop.interface")
try:
is_dark_theme = desktop_env.get_string("color-scheme") == "prefer-dark"
except Exception:
is_dark_theme = "-dark" in desktop_env.get_string("gtk-theme")
if is_dark_theme:
Gtk.Settings.get_default().set_property("gtk-application-prefer-dark-theme", True)
def main():
apply_dark_theme()
win = ProtonDownloader()

View File

@@ -1,74 +1,21 @@
#!/usr/bin/env python3
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib, GdkPixbuf, Gio
from threading import Thread
from pathlib import Path
import sys
import subprocess
import argparse
import re
import os
import gettext
import locale
import json
import time
import shlex
import gettext
class PathManager:
@staticmethod
def system_data(*relative_paths):
xdg_data_dirs = os.getenv('XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(':')
for data_dir in xdg_data_dirs:
path = Path(data_dir).joinpath(*relative_paths)
if path.exists():
return str(path)
return str(Path(xdg_data_dirs[0]).joinpath(*relative_paths))
gi.require_version("Gtk", "3.0")
@staticmethod
def user_data(*relative_paths):
xdg_data_home = Path(os.getenv('XDG_DATA_HOME', Path.home() / '.local/share'))
return str(xdg_data_home.joinpath(*relative_paths))
@staticmethod
def user_config(*relative_paths):
xdg_config_home = Path(os.getenv('XDG_CONFIG_HOME', Path.home() / '.config'))
return str(xdg_config_home.joinpath(*relative_paths))
@staticmethod
def find_binary(binary_name):
paths = os.getenv('PATH', '').split(':')
for path in paths:
binary_path = Path(path) / binary_name
if binary_path.exists():
return str(binary_path)
return f'/usr/bin/{binary_name}'
@staticmethod
def get_icon(icon_name):
icon_paths = [
PathManager.user_data('icons', icon_name),
PathManager.system_data('icons/hicolor/256x256/apps', icon_name),
PathManager.system_data('icons', icon_name)
]
for path in icon_paths:
if Path(path).exists():
return path
return icon_paths[-1]
@staticmethod
def find_library(lib_name):
lib_paths = [
Path("/usr/lib") / lib_name,
Path("/usr/lib32") / lib_name,
Path("/usr/lib/x86_64-linux-gnu") / lib_name,
Path("/usr/lib64") / lib_name
]
for path in lib_paths:
if path.exists():
return str(path)
return None
from gi.repository import Gtk, GLib, GdkPixbuf, Gio
from threading import Thread
from faugus.config_manager import *
from faugus.dark_theme import *
IS_FLATPAK = 'FLATPAK_ID' in os.environ or os.path.exists('/.flatpak-info')
if IS_FLATPAK:
@@ -95,39 +42,6 @@ proton_cachyos = PathManager.system_data('steam/compatibilitytools.d/proton-cach
compatibility_dir = os.path.expanduser("~/.local/share/Steam/compatibilitytools.d")
os.makedirs(compatibility_dir, exist_ok=True)
def get_system_locale():
lang = os.environ.get('LANG') or os.environ.get('LC_MESSAGES')
if lang:
return lang.split('.')[0]
try:
loc = locale.getdefaultlocale()[0]
if loc:
return loc
except Exception:
pass
return 'en_US'
def get_language_from_config():
if os.path.exists(config_file_dir):
with open(config_file_dir, 'r') as f:
for line in f:
line = line.strip()
if line.startswith('language='):
return line.split('=', 1)[1].strip()
return None
lang = get_language_from_config()
if not lang:
lang = get_system_locale()
LOCALE_DIR = (
PathManager.system_data('locale')
if os.path.isdir(PathManager.system_data('locale'))
else os.path.join(os.path.dirname(__file__), 'locale')
)
try:
translation = gettext.translation(
'faugus-run',
@@ -383,7 +297,7 @@ class FaugusRun:
if "Proton-EM" in self.message:
self.process = subprocess.Popen(
[PathManager.find_binary("bash"), "-c", f"{faugus_proton_downloader}"],
[sys.executable, "-m", "faugus.proton_downloader"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=8192,
@@ -424,7 +338,7 @@ class FaugusRun:
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}"
cmd = f"{sys.executable} -m faugus.components; {self.discrete_gpu} {eac_dir} {be_dir} {self.message}"
else:
cmd = f"{self.discrete_gpu} {eac_dir} {be_dir} {self.message}"
@@ -754,31 +668,6 @@ def handle_command(message, command=None):
process_thread.join()
sys.exit(0)
def apply_dark_theme():
if IS_FLATPAK:
if (os.environ.get("XDG_CURRENT_DESKTOP")) == "KDE":
Gtk.Settings.get_default().set_property("gtk-theme-name", "Breeze")
try:
proxy = Gio.DBusProxy.new_sync(
Gio.bus_get_sync(Gio.BusType.SESSION, None), 0, None,
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Settings", None)
is_dark = proxy.call_sync(
"Read", GLib.Variant("(ss)", ("org.freedesktop.appearance", "color-scheme")),
0, -1, None).unpack()[0] == 1
except:
is_dark = False
Gtk.Settings.get_default().set_property("gtk-application-prefer-dark-theme", is_dark)
else:
desktop_env = Gio.Settings.new("org.gnome.desktop.interface")
try:
is_dark_theme = desktop_env.get_string("color-scheme") == "prefer-dark"
except Exception:
is_dark_theme = "-dark" in desktop_env.get_string("gtk-theme")
if is_dark_theme:
Gtk.Settings.get_default().set_property("gtk-application-prefer-dark-theme", True)
def build_launch_command(game):
gameid = game.get("gameid", "")
path = game.get("path", "")

View File

@@ -13,19 +13,16 @@ gnome = import('gnome')
subdir('assets')
subdir('languages')
subdir('data')
subdir('faugus')
install_data(
'faugus_launcher.py',
'faugus_run.py',
'faugus_proton_manager.py',
'faugus_components.py',
'faugus_proton_downloader.py',
rename: [
'faugus-launcher',
'faugus-run',
'faugus-proton-manager',
'faugus-components',
'faugus-proton-downloader',
],
install_mode: 'rwxr-xr-x',
install_dir: get_option('bindir'),