diff --git a/rare/__main__.py b/rare/__main__.py index 21254aa1..5ba902a9 100644 --- a/rare/__main__.py +++ b/rare/__main__.py @@ -58,13 +58,13 @@ def main(): if args.desktop_shortcut: from rare.utils import utils - utils.create_rare_desktop_link("desktop") + utils.create_desktop_link(type_of_link="desktop", for_rare=True) print("Link created") if args.startmenu_shortcut: from rare.utils import utils - utils.create_rare_desktop_link("start_menu") + utils.create_desktop_link(type_of_link="start_menu", for_rare=True) print("link created") if args.version: diff --git a/rare/components/tabs/games/game_widgets/base_installed_widget.py b/rare/components/tabs/games/game_widgets/base_installed_widget.py index ae54f0a9..8653863f 100644 --- a/rare/components/tabs/games/game_widgets/base_installed_widget.py +++ b/rare/components/tabs/games/game_widgets/base_installed_widget.py @@ -2,7 +2,7 @@ import os import platform from logging import getLogger -from PyQt5.QtCore import pyqtSignal, QProcess, QSettings, Qt, QByteArray +from PyQt5.QtCore import pyqtSignal, QProcess, QSettings, QStandardPaths, Qt, QByteArray from PyQt5.QtGui import QPixmap from PyQt5.QtWidgets import QGroupBox, QMessageBox, QAction, QLabel @@ -83,9 +83,10 @@ class BaseInstalledWidget(QGroupBox): sync.triggered.connect(self.sync_game) self.addAction(sync) + desktop = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation) if os.path.exists( - os.path.expanduser(f"~/Desktop/{self.game.app_title}.desktop") - ) or os.path.exists(os.path.expanduser(f"~/Desktop/{self.game.app_title}.lnk")): + os.path.join(desktop, f"{self.game.app_title}.desktop") + ) or os.path.exists(os.path.join(desktop, f"{self.game.app_title}.lnk")): self.create_desktop = QAction(self.tr("Remove Desktop link")) else: self.create_desktop = QAction(self.tr("Create Desktop link")) @@ -95,14 +96,11 @@ class BaseInstalledWidget(QGroupBox): ) self.addAction(self.create_desktop) + applications = QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation) if platform.system() == "Linux": - start_menu_file = os.path.expanduser( - f"~/.local/share/applications/{self.game.app_title}.desktop" - ) + start_menu_file = os.path.join(applications, f"{self.game.app_title}.desktop") elif platform.system() == "Windows": - start_menu_file = os.path.expandvars( - "%appdata%/Microsoft/Windows/Start Menu" - ) + start_menu_file = os.path.join(applications, "..", f"{self.game.app_title}.lnk") else: start_menu_file = "" if platform.system() in ["Windows", "Linux"]: @@ -136,23 +134,27 @@ class BaseInstalledWidget(QGroupBox): ) def create_desktop_link(self, type_of_link): - if platform.system() not in ["Windows", "Linux"]: + if type_of_link == "desktop": + shortcut_path = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation) + elif type_of_link == "start_menu": + shortcut_path = QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation) + else: + return + + if platform.system() == "Windows": + shortcut_path = os.path.join(shortcut_path, f"{self.game.app_title}.lnk") + elif platform.system() == "Linux": + shortcut_path = os.path.join(shortcut_path, f"{self.game.app_title}.desktop") + else: QMessageBox.warning( self, "Warning", f"Create a Desktop link is currently not supported on {platform.system()}", ) return - if type_of_link == "desktop": - path = os.path.expanduser(f"~/Desktop/") - elif type_of_link == "start_menu": - path = os.path.expanduser("~/.local/share/applications/") - else: - return - if not ( - os.path.exists(os.path.expanduser(f"{path}{self.game.app_title}.desktop")) - or os.path.exists(os.path.expanduser(f"{path}{self.game.app_title}.lnk")) - ): + + + if not os.path.exists(shortcut_path): try: if not create_desktop_link(self.game.app_name, self.core, type_of_link): return @@ -165,12 +167,8 @@ class BaseInstalledWidget(QGroupBox): elif type_of_link == "start_menu": self.create_start_menu.setText(self.tr("Remove Start menu link")) else: - if os.path.exists( - os.path.expanduser(f"{path}{self.game.app_title}.desktop") - ): - os.remove(os.path.expanduser(f"{path}{self.game.app_title}.desktop")) - elif os.path.exists(os.path.expanduser(f"{path}{self.game.app_title}.lnk")): - os.remove(os.path.expanduser(f"{path}{self.game.app_title}.lnk")) + if os.path.exists(shortcut_path): + os.remove(shortcut_path) if type_of_link == "desktop": self.create_desktop.setText(self.tr("Create Desktop link")) diff --git a/rare/components/tabs/settings/rare.py b/rare/components/tabs/settings/rare.py index 5a32aca7..187fb94d 100644 --- a/rare/components/tabs/settings/rare.py +++ b/rare/components/tabs/settings/rare.py @@ -4,7 +4,7 @@ import subprocess import sys from logging import getLogger -from PyQt5.QtCore import QSettings, Qt +from PyQt5.QtCore import QSettings, QStandardPaths, Qt from PyQt5.QtWidgets import QWidget, QMessageBox from rare.shared import LegendaryCoreSingleton @@ -113,16 +113,14 @@ class RareSettings(QWidget, Ui_RareSettings): lambda: self.settings.setValue("show_console", self.log_games.isChecked()) ) + desktop = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation) + applications = QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation) if platform.system() == "Linux": - self.desktop_file = os.path.expanduser("~/Desktop/Rare.desktop") - self.start_menu_link = os.path.expanduser( - "~/.local/share/applications/Rare.desktop" - ) + self.desktop_file = os.path.join(desktop, "Rare.desktop") + self.start_menu_link = os.path.join(applications, "Rare.desktop") elif platform.system() == "Windows": - self.desktop_file = os.path.expanduser("~/Desktop/Rare.lnk") - self.start_menu_link = os.path.expandvars( - "%appdata%\\Microsoft\\Windows\\Start Menu\\Rare.lnk" - ) + self.desktop_file = os.path.join(desktop, "Rare.lnk") + self.start_menu_link = os.path.join(applications, "..", "Rare.lnk") else: self.desktop_link_btn.setText(self.tr("Not supported")) self.desktop_link_btn.setDisabled(True) @@ -162,7 +160,7 @@ class RareSettings(QWidget, Ui_RareSettings): def create_start_menu_link(self): try: if not os.path.exists(self.start_menu_link): - utils.create_rare_desktop_link("start_menu") + utils.create_desktop_link(type_of_link="start_menu", for_rare=True) self.startmenu_link_btn.setText(self.tr("Remove start menu link")) else: os.remove(self.start_menu_link) @@ -178,7 +176,7 @@ class RareSettings(QWidget, Ui_RareSettings): def create_desktop_link(self): try: if not os.path.exists(self.desktop_file): - utils.create_rare_desktop_link("desktop") + utils.create_desktop_link(type_of_link="desktop", for_rare=True) self.desktop_link_btn.setText(self.tr("Remove Desktop link")) else: os.remove(self.desktop_file) diff --git a/rare/utils/legendary_utils.py b/rare/utils/legendary_utils.py index 17ef38cd..77e09788 100644 --- a/rare/utils/legendary_utils.py +++ b/rare/utils/legendary_utils.py @@ -3,7 +3,7 @@ import platform import shutil from logging import getLogger -from PyQt5.QtCore import pyqtSignal, QCoreApplication, QObject, QRunnable +from PyQt5.QtCore import pyqtSignal, QCoreApplication, QObject, QRunnable, QStandardPaths from legendary.core import LegendaryCore from legendary.models.game import VerifyResult @@ -20,31 +20,26 @@ def uninstall(app_name: str, core: LegendaryCore, options=None): igame = core.get_installed_game(app_name) # remove shortcuts link + desktop = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation) + applications = QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation) if platform.system() == "Linux": - if os.path.exists(os.path.expanduser(f"~/Desktop/{igame.title}.desktop")): - os.remove(os.path.expanduser(f"~/Desktop/{igame.title}.desktop")) - if os.path.exists( - os.path.expanduser(f"~/.local/share/applications/{igame.title}.desktop") - ): - os.remove( - os.path.expanduser(f"~/.local/share/applications/{igame.title}.desktop") - ) + desktop_shortcut = os.path.join(desktop, f"{igame.title}.desktop") + if os.path.exists(desktop_shortcut): + os.remove(desktop_shortcut) + + applications_shortcut = os.path.join(applications, f"{igame.title}.desktop") + if os.path.exists(applications_shortcut): + os.remove(applications_shortcut) elif platform.system() == "Windows": - if os.path.exists( - os.path.expanduser(f"~/Desktop/{igame.title.split(':')[0]}.lnk") - ): - os.remove(os.path.expanduser(f"~/Desktop/{igame.title.split(':')[0]}.lnk")) - elif os.path.exists( - os.path.expandvars( - f"%appdata%/Microsoft/Windows/Start Menu/{igame.title.split(':')[0]}.lnk" - ) - ): - os.remove( - os.path.expandvars( - f"%appdata%/Microsoft/Windows/Start Menu/{igame.title.split(':')[0]}.lnk" - ) - ) + game_title = igame.title.split(":")[0] + desktop_shortcut = os.path.join(desktop, f"{game_title}.lnk") + if os.path.exists(desktop_shortcut): + os.remove(desktop_shortcut) + + start_menu_shortcut = os.path.join(applications, "..", f"{game_title}.lnk") + if os.path.exists(start_menu_shortcut): + os.remove(start_menu_shortcut) try: # Remove DLC first so directory is empty when game uninstall runs diff --git a/rare/utils/utils.py b/rare/utils/utils.py index f9043b18..659fd567 100644 --- a/rare/utils/utils.py +++ b/rare/utils/utils.py @@ -2,6 +2,7 @@ import json import math import os import platform +import shlex import shutil import subprocess import sys @@ -16,6 +17,7 @@ from PyQt5.QtCore import ( QObject, QRunnable, QSettings, + QStandardPaths, Qt, QFile, QDir, @@ -249,87 +251,27 @@ def get_size(b: int) -> str: b /= 1024 -def create_rare_desktop_link(type_of_link): - # Linux - if platform.system() == "Linux": - if type_of_link == "desktop": - path = os.path.expanduser("~/Desktop/") - elif type_of_link == "start_menu": - path = os.path.expanduser("~/.local/share/applications/") - else: - return - if not os.path.exists(path): - return +def create_desktop_link(app_name=None, core: LegendaryCore = None, type_of_link="desktop", for_rare: bool = False) -> bool: + if not for_rare: + igame = core.get_installed_game(app_name) - if p := os.environ.get("APPIMAGE"): - executable = p + if os.path.exists(p := os.path.join(image_dir, igame.app_name, "Thumbnail.png")): + icon = p + elif os.path.exists( + p := os.path.join(image_dir, igame.app_name, "DieselGameBoxLogo.png") + ): + icon = p else: - executable = f"{sys.executable} {os.path.abspath(sys.argv[0])}" - with open(os.path.join(path, "Rare.desktop"), "w") as desktop_file: - desktop_file.write( - "[Desktop Entry]\n" - f"Name=Rare\n" - f"Type=Application\n" - f"Categories=Game;\n" - f"Icon={os.path.join(resources_path, 'images', 'Rare.png')}\n" - f"Exec={executable}\n" - "Terminal=false\n" - "StartupWMClass=rare\n" + icon = os.path.join( + os.path.join(image_dir, igame.app_name, "DieselGameBoxTall.png") ) - desktop_file.close() - os.chmod(os.path.expanduser(os.path.join(path, "Rare.desktop")), 0o755) + icon = icon.replace(".png", "") - elif platform.system() == "Windows": - # Target of shortcut - if type_of_link == "desktop": - target_folder = os.path.expanduser("~/Desktop/") - elif type_of_link == "start_menu": - target_folder = os.path.expandvars("%appdata%/Microsoft/Windows/Start Menu") - else: - logger.warning("No valid type of link") - return - linkName = "Rare.lnk" - - # Path to location of link file - pathLink = os.path.join(target_folder, linkName) - - executable = sys.executable - executable = executable.replace("python.exe", "pythonw.exe") - logger.debug(executable) - # Add shortcut - shell = Dispatch("WScript.Shell") - shortcut = shell.CreateShortCut(pathLink) - shortcut.Targetpath = executable - if not sys.executable.endswith("Rare.exe"): - shortcut.Arguments = os.path.abspath(sys.argv[0]) - - # Icon - shortcut.IconLocation = os.path.join(resources_path, "images", "Rare.ico") - - shortcut.save() - - -def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop") -> bool: - igame = core.get_installed_game(app_name) - - if os.path.exists(p := os.path.join(image_dir, igame.app_name, "Thumbnail.png")): - icon = p - elif os.path.exists( - p := os.path.join(image_dir, igame.app_name, "DieselGameBoxLogo.png") - ): - icon = p - else: - icon = os.path.join( - os.path.join(image_dir, igame.app_name, "DieselGameBoxTall.png") - ) - icon = icon.replace(".png", "") - - # Linux if platform.system() == "Linux": if type_of_link == "desktop": - path = os.path.expanduser(f"~/Desktop/") + path = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation) elif type_of_link == "start_menu": - path = os.path.expanduser("~/.local/share/applications/") + path = QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation) else: return False if not os.path.exists(path): @@ -338,39 +280,59 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop") - executable = p else: executable = f"{sys.executable} {os.path.abspath(sys.argv[0])}" - with open(f"{path}{igame.title}.desktop", "w") as desktop_file: - desktop_file.write( - "[Desktop Entry]\n" - f"Name={igame.title}\n" - f"Type=Application\n" - f"Categories=Game;\n" - f"Icon={icon}.png\n" - f"Exec={executable} launch {app_name}\n" - "Terminal=false\n" - "StartupWMClass=rare-game\n" - ) - desktop_file.close() - os.chmod(os.path.expanduser(f"{path}{igame.title}.desktop"), 0o755) - # Windows + if for_rare: + with open(os.path.join(path, "Rare.desktop"), "w") as desktop_file: + desktop_file.write( + "[Desktop Entry]\n" + f"Name=Rare\n" + f"Type=Application\n" + f"Categories=Game;\n" + f"Icon={os.path.join(resources_path, 'images', 'Rare.png')}\n" + f"Exec={executable}\n" + "Terminal=false\n" + "StartupWMClass=rare\n" + ) + else: + with open(os.path.join(path, f"{igame.title}.desktop"), "w") as desktop_file: + desktop_file.write( + "[Desktop Entry]\n" + f"Name={igame.title}\n" + f"Type=Application\n" + f"Categories=Game;\n" + f"Icon={icon}.png\n" + f"Exec={executable} launch {app_name}\n" + "Terminal=false\n" + "StartupWMClass=rare-game\n" + ) + os.chmod(os.path.join(path, f"{igame.title}.desktop"), 0o755) + + return True + elif platform.system() == "Windows": # Target of shortcut if type_of_link == "desktop": - target_folder = os.path.expanduser("~/Desktop/") + target_folder = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation) elif type_of_link == "start_menu": - target_folder = os.path.expandvars("%appdata%/Microsoft/Windows/Start Menu") + target_folder = os.path.join( + QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation), + ".." + ) else: logger.warning("No valid type of link") return False if not os.path.exists(target_folder): return False - # Name of link file - linkName = igame.title - for c in r'<>?":|\/*': - linkName.replace(c, "") + if for_rare: + linkName = "Rare.lnk" + else: + linkName = igame.title + # TODO: this conversion is not applied everywhere (see base_installed_widget), should it? + for c in r'<>?":|\/*': + linkName.replace(c, "") - linkName = f"{linkName.strip()}.lnk" + linkName = f"{linkName.strip()}.lnk" # Path to location of link file pathLink = os.path.join(target_folder, linkName) @@ -378,25 +340,40 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop") - # Add shortcut shell = Dispatch("WScript.Shell") shortcut = shell.CreateShortCut(pathLink) - if sys.executable.endswith("Rare.exe"): - executable = sys.executable - else: - executable = f"{sys.executable} {os.path.abspath(sys.argv[0])}" + + executable = sys.executable + arguments = [] + + if not sys.executable.endswith("Rare.exe"): + # be sure to start consoleless then + executable = sys.executable.replace("python.exe", "pythonw.exe") + arguments.append(os.path.abspath(sys.argv[0])) + + if not for_rare: + arguments.extend(["launch", app_name]) + shortcut.Targetpath = executable - shortcut.Arguments = f"launch {app_name}" - shortcut.WorkingDirectory = os.getcwd() + shortcut.Arguments = shlex.join(arguments) + + if for_rare: + shortcut.WorkingDirectory = os.getcwd() # Icon - if not os.path.exists(f"{icon}.ico"): - img = QImage() - img.load(f"{icon}.png") - img.save(f"{icon}.ico") - logger.info("Create Icon") - shortcut.IconLocation = os.path.join(f"{icon}.ico") + if for_rare: + icon_location = os.path.join(resources_path, "images", "Rare.ico") + else: + if not os.path.exists(f"{icon}.ico"): + img = QImage() + img.load(f"{icon}.png") + img.save(f"{icon}.ico") + logger.info("Create Icon") + icon_location = f"{icon}.ico" + shortcut.IconLocation = os.path.abspath(icon_location) shortcut.save() return True + # mac OS is based on Darwin elif platform.system() == "Darwin": return False