sync current state

This commit is contained in:
2026-03-01 11:43:52 +01:00
parent 4af11981e8
commit 549acc2e0f
4 changed files with 136 additions and 39 deletions

View File

@@ -2,6 +2,7 @@ py.install_sources(
'components.py',
'proton_downloader.py',
'shortcut.py',
'shortcut_utils.py',
'config_manager.py',
'path_manager.py',
'language_config.py',

32
faugus/shortcut_utils.py Normal file
View File

@@ -0,0 +1,32 @@
#!/usr/bin/python3
import os
def get_shortcut_icon_path(gameid, icons_dir, fallback_icon):
icon_png = os.path.join(icons_dir, f"{gameid}.png")
if os.path.exists(icon_png):
return icon_png
icon_ico = os.path.join(icons_dir, f"{gameid}.ico")
if os.path.exists(icon_ico):
return icon_ico
return fallback_icon
def build_desktop_entry(name, exec_command, icon_path, game_directory, startup_wm_class=""):
entry = [
"[Desktop Entry]",
f"Name={name}",
f"Exec={exec_command}",
f"Icon={icon_path}",
"Type=Application",
"Categories=Game;",
f"Path={game_directory}",
]
if startup_wm_class:
entry.append(f"StartupWMClass={startup_wm_class}")
return "\n".join(entry) + "\n"

View File

@@ -25,6 +25,7 @@ from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, AyatanaAppIndicator3, Gio,
from PIL import Image
from faugus.config_manager import *
from faugus.dark_theme import *
from faugus.shortcut_utils import build_desktop_entry, get_shortcut_icon_path
from faugus.steam_setup import *
VERSION = "1.15.1"
@@ -1456,7 +1457,8 @@ class Main(Gtk.ApplicationWindow):
game_data.get("playtime", 0),
game_data.get("hidden", False),
game_data.get("prevent_sleep", False),
game_data.get("favorite", False)
game_data.get("favorite", False),
game_data.get("startup_wm_class", ""),
)
if not self.show_hidden and game.hidden:
@@ -1491,7 +1493,7 @@ class Main(Gtk.ApplicationWindow):
else:
hbox.get_style_context().add_class("hbox-normal")
game_icon = f'{icons_dir}/{game.gameid}.ico'
game_icon = get_shortcut_icon_path(game.gameid, icons_dir, faugus_png)
game_label = Gtk.Label.new(game.title)
if self.interface_mode in ("Blocks", "Banners"):
@@ -1499,9 +1501,6 @@ class Main(Gtk.ApplicationWindow):
game_label.set_max_width_chars(1)
game_label.set_justify(Gtk.Justification.CENTER)
if not os.path.isfile(game_icon):
game_icon = faugus_png
self.flowbox_child = Gtk.FlowBoxChild()
pixbuf = GdkPixbuf.Pixbuf.new_from_file(game_icon)
@@ -2063,12 +2062,11 @@ class Main(Gtk.ApplicationWindow):
def set_image_shortcut_icon(self, title, icons_path, icon_temp):
title_formatted = format_title(title)
# Check if the icon file exists
icon_path = os.path.join(icons_path, f"{title_formatted}.ico")
icon_path = get_shortcut_icon_path(title_formatted, icons_path, faugus_png)
if os.path.exists(icon_path):
if icon_path != faugus_png:
shutil.copyfile(icon_path, icon_temp)
if not os.path.exists(icon_path):
else:
icon_temp = faugus_png
pixbuf = GdkPixbuf.Pixbuf.new_from_file(icon_temp)
@@ -2278,9 +2276,11 @@ class Main(Gtk.ApplicationWindow):
playtime = 0
hidden = False
favorite = False
startup_wm_class = ""
if add_game_dialog.combobox_launcher.get_active() == 3:
path = f"{prefix}/drive_c/Program Files (x86)/Battle.net/Battle.net.exe"
startup_wm_class = "battle.net.exe"
if add_game_dialog.combobox_launcher.get_active() == 4:
path = f"{prefix}/drive_c/Program Files/Electronic Arts/EA Desktop/EA Desktop/EALauncher.exe"
if add_game_dialog.combobox_launcher.get_active() == 5:
@@ -2346,6 +2346,7 @@ class Main(Gtk.ApplicationWindow):
hidden,
prevent_sleep,
favorite,
startup_wm_class,
)
# Determine the state of the shortcut checkbox
@@ -2354,7 +2355,7 @@ class Main(Gtk.ApplicationWindow):
steam_shortcut_state = add_game_dialog.checkbox_shortcut_steam.get_active()
icon_temp = os.path.expanduser(add_game_dialog.icon_temp)
icon_final = f'{add_game_dialog.icons_path}/{title_formatted}.ico'
icon_final = f'{add_game_dialog.icons_path}/{title_formatted}.png'
def check_internet_connection():
try:
@@ -2414,6 +2415,7 @@ class Main(Gtk.ApplicationWindow):
"hidden": hidden,
"prevent_sleep": prevent_sleep,
"favorite": favorite,
"startup_wm_class": startup_wm_class,
}
games = []
@@ -2729,9 +2731,13 @@ class Main(Gtk.ApplicationWindow):
game.runner = "Linux-Native"
if edit_game_dialog.combobox_launcher.get_active() == 2:
game.runner = "Steam"
if edit_game_dialog.combobox_launcher.get_active() == 3:
game.startup_wm_class = "battle.net.exe"
elif getattr(game, "startup_wm_class", "") == "battle.net.exe":
game.startup_wm_class = ""
icon_temp = os.path.expanduser(edit_game_dialog.icon_temp)
icon_final = f'{edit_game_dialog.icons_path}/{title_formatted}.ico'
icon_final = f'{edit_game_dialog.icons_path}/{title_formatted}.png'
# Determine the state of the shortcut checkbox
desktop_shortcut_state = edit_game_dialog.checkbox_shortcut_desktop.get_active()
@@ -2787,34 +2793,27 @@ class Main(Gtk.ApplicationWindow):
if os.path.isfile(os.path.expanduser(icon_temp)):
os.rename(os.path.expanduser(icon_temp), icon_final)
# Check if the icon file exists
new_icon_path = f"{icons_dir}/{game.gameid}.ico"
if not os.path.exists(new_icon_path):
new_icon_path = faugus_png
new_icon_path = get_shortcut_icon_path(game.gameid, icons_dir, faugus_png)
# Get the directory containing the executable
game_directory = os.path.dirname(game.path)
# Create a .desktop file
if IS_FLATPAK:
desktop_file_content = (
f'[Desktop Entry]\n'
f'Name={game.title}\n'
f'Exec=flatpak run --command={faugus_run} io.github.Faugus.faugus-launcher --game {game.gameid}\n'
f'Icon={new_icon_path}\n'
f'Type=Application\n'
f'Categories=Game;\n'
f'Path={game_directory}\n'
desktop_file_content = build_desktop_entry(
game.title,
f"flatpak run --command={faugus_run} io.github.Faugus.faugus-launcher --game {game.gameid}",
new_icon_path,
game_directory,
getattr(game, "startup_wm_class", ""),
)
else:
desktop_file_content = (
f'[Desktop Entry]\n'
f'Name={game.title}\n'
f'Exec={faugus_run} --game {game.gameid}\n'
f'Icon={new_icon_path}\n'
f'Type=Application\n'
f'Categories=Game;\n'
f'Path={game_directory}\n'
desktop_file_content = build_desktop_entry(
game.title,
f"{faugus_run} --game {game.gameid}",
new_icon_path,
game_directory,
getattr(game, "startup_wm_class", ""),
)
# Check if the destination directory exists and create if it doesn't
@@ -2967,10 +2966,7 @@ class Main(Gtk.ApplicationWindow):
if os.path.isfile(os.path.expanduser(icon_temp)):
os.rename(os.path.expanduser(icon_temp), icon_final)
# Check if the icon file exists
new_icon_path = f"{icons_dir}/{game.gameid}.ico"
if not os.path.exists(new_icon_path):
new_icon_path = faugus_png
new_icon_path = get_shortcut_icon_path(game.gameid, icons_dir, faugus_png)
# Get the directory containing the executable
game_directory = os.path.dirname(game.path)
@@ -3007,11 +3003,14 @@ class Main(Gtk.ApplicationWindow):
def remove_banner_icon(self, game):
banner_file_path = f"{banners_dir}/{game.gameid}.png"
icon_file_path = f"{icons_dir}/{game.gameid}.ico"
icon_file_path_png = f"{icons_dir}/{game.gameid}.png"
icon_file_path_ico = f"{icons_dir}/{game.gameid}.ico"
if os.path.exists(banner_file_path):
os.remove(banner_file_path)
if os.path.exists(icon_file_path):
os.remove(icon_file_path)
if os.path.exists(icon_file_path_png):
os.remove(icon_file_path_png)
if os.path.exists(icon_file_path_ico):
os.remove(icon_file_path_ico)
def remove_shortcut(self, game, shortcut):
applications_shortcut_path = f"{app_dir}/{game.gameid}.desktop"
@@ -3097,6 +3096,7 @@ class Main(Gtk.ApplicationWindow):
"hidden": hidden,
"prevent_sleep": game.prevent_sleep,
"favorite": game.favorite,
"startup_wm_class": game.startup_wm_class,
}
new_games_data.append(game_data)
@@ -4596,6 +4596,7 @@ class Game:
hidden,
prevent_sleep,
favorite,
startup_wm_class="",
):
self.gameid = gameid
self.title = title
@@ -4622,6 +4623,7 @@ class Game:
self.hidden = hidden
self.prevent_sleep = prevent_sleep
self.favorite = favorite
self.startup_wm_class = startup_wm_class
class DuplicateDialog(Gtk.Dialog):
@@ -4804,7 +4806,7 @@ class AddGame(Gtk.Dialog):
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.icon_temp = f'{self.icons_path}/icon_temp.png'
self.box = self.get_content_area()
self.box.set_margin_start(0)
@@ -6767,6 +6769,10 @@ def update_games_and_config():
game["runner"] = "Proton-GE Latest"
changed = True
if title == "Battle.net" and game.get("startup_wm_class", "") != "battle.net.exe":
game["startup_wm_class"] = "battle.net.exe"
changed = True
if changed:
with open(games_json, "w", encoding="utf-8") as f:
json.dump(games, f, indent=4, ensure_ascii=False)

View File

@@ -0,0 +1,58 @@
import os
import tempfile
import unittest
from faugus.shortcut_utils import build_desktop_entry, get_shortcut_icon_path
class ShortcutUtilsTest(unittest.TestCase):
def test_get_shortcut_icon_path_prefers_png(self):
with tempfile.TemporaryDirectory() as tmpdir:
png = os.path.join(tmpdir, "battlenet.png")
ico = os.path.join(tmpdir, "battlenet.ico")
with open(png, "wb") as f:
f.write(b"png")
with open(ico, "wb") as f:
f.write(b"ico")
path = get_shortcut_icon_path("battlenet", tmpdir, "/fallback.png")
self.assertEqual(path, png)
def test_get_shortcut_icon_path_falls_back_to_ico(self):
with tempfile.TemporaryDirectory() as tmpdir:
ico = os.path.join(tmpdir, "battlenet.ico")
with open(ico, "wb") as f:
f.write(b"ico")
path = get_shortcut_icon_path("battlenet", tmpdir, "/fallback.png")
self.assertEqual(path, ico)
def test_get_shortcut_icon_path_uses_fallback(self):
with tempfile.TemporaryDirectory() as tmpdir:
path = get_shortcut_icon_path("battlenet", tmpdir, "/fallback.png")
self.assertEqual(path, "/fallback.png")
def test_build_desktop_entry_includes_startup_wm_class(self):
entry = build_desktop_entry(
"Battle.net",
"faugus-run --game battlenet",
"/icons/battlenet.png",
"/games",
"battle.net.exe",
)
self.assertIn("StartupWMClass=battle.net.exe\n", entry)
self.assertIn("Icon=/icons/battlenet.png\n", entry)
def test_build_desktop_entry_omits_empty_startup_wm_class(self):
entry = build_desktop_entry(
"Game",
"faugus-run --game game",
"/icons/game.png",
"/games",
"",
)
self.assertNotIn("StartupWMClass=", entry)
if __name__ == "__main__":
unittest.main()