1
0
Fork 0
mirror of synced 2024-06-26 18:20:50 +12:00

Merge pull request #237 from loathingKernel/fixups

Update for legendary 0.20.28
This commit is contained in:
Dummerle 2022-09-04 22:05:22 +02:00 committed by GitHub
commit 2a21ca8c66
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 466 additions and 379 deletions

View file

@ -30,7 +30,7 @@ pywebview = [
{ version = "^3.6.3", extras = ["cef"], platform = "windows", optional = true }, { version = "^3.6.3", extras = ["cef"], platform = "windows", optional = true },
{ version = "^3.6.3", extras = ["gtk"], platform = "linux", optional = true }, { version = "^3.6.3", extras = ["gtk"], platform = "linux", optional = true },
] ]
legendary-gl = "^0.20.27" legendary-gl = "^0.20.28"
typing-extensions = "^4.3.0" typing-extensions = "^4.3.0"

View file

@ -45,10 +45,7 @@ def main():
) )
subparsers = parser.add_subparsers(title="Commands", dest="subparser") subparsers = parser.add_subparsers(title="Commands", dest="subparser")
launch_parser = subparsers.add_parser("launch") launch_minimal_parser = subparsers.add_parser("start", aliases=["launch"])
launch_parser.add_argument("app_name", help="Name of the app", metavar="<App Name>")
launch_minimal_parser = subparsers.add_parser("start")
launch_minimal_parser.add_argument("app_name", help="AppName of the game to launch", launch_minimal_parser.add_argument("app_name", help="AppName of the game to launch",
metavar="<App Name>", action="store") metavar="<App Name>", action="store")
launch_minimal_parser.add_argument("--offline", help="Launch game offline", launch_minimal_parser.add_argument("--offline", help="Launch game offline",
@ -56,11 +53,11 @@ def main():
launch_minimal_parser.add_argument("--skip_update_check", help="Do not check for updates", launch_minimal_parser.add_argument("--skip_update_check", help="Do not check for updates",
action="store_true") action="store_true")
launch_minimal_parser.add_argument('--wine-bin', dest='wine_bin', action='store', metavar='<wine binary>', launch_minimal_parser.add_argument('--wine-bin', dest='wine_bin', action='store', metavar='<wine binary>',
default=os.environ.get('LGDRY_WINE_BINARY', None), default=os.environ.get('LGDRY_WINE_BINARY', None),
help='Set WINE binary to use to launch the app') help='Set WINE binary to use to launch the app')
launch_minimal_parser.add_argument('--wine-prefix', dest='wine_pfx', action='store', metavar='<wine pfx path>', launch_minimal_parser.add_argument('--wine-prefix', dest='wine_pfx', action='store', metavar='<wine pfx path>',
default=os.environ.get('LGDRY_WINE_PREFIX', None), default=os.environ.get('LGDRY_WINE_PREFIX', None),
help='Set WINE prefix to use') help='Set WINE prefix to use')
launch_minimal_parser.add_argument("--ask-alyways-sync", help="Ask for cloud saves", launch_minimal_parser.add_argument("--ask-alyways-sync", help="Ask for cloud saves",
action="store_true") action="store_true")
@ -80,10 +77,10 @@ def main():
if args.version: if args.version:
from rare import __version__, code_name from rare import __version__, code_name
print(f"Rare {__version__} Codename: {code_name}") print(f"Rare {__version__} Codename: {code_name}")
return return
if args.subparser == "start":
if args.subparser == "start" or args.subparser == "launch":
from rare import game_launch_helper as helper from rare import game_launch_helper as helper
helper.start_game(args) helper.start_game(args)
return return
@ -99,16 +96,10 @@ def main():
from rare.utils.paths import data_dir from rare.utils.paths import data_dir
with open(os.path.join(data_dir, "lockfile"), "w") as file: with open(os.path.join(data_dir, "lockfile"), "w") as file:
if args.subparser == "launch": file.write("show")
file.write(f"launch {args.app_name}")
else:
file.write("start")
file.close() file.close()
return return
if args.subparser == "launch":
args.silent = True
from rare.app import start from rare.app import start
start(args) start(args)

View file

@ -1,4 +1,3 @@
import configparser
import logging import logging
import os import os
import platform import platform
@ -13,15 +12,18 @@ from typing import Optional
import legendary import legendary
import requests.exceptions import requests.exceptions
from PyQt5.QtCore import QThreadPool, QTimer, QT_VERSION_STR, PYQT_VERSION_STR from PyQt5.QtCore import QThreadPool, QTimer, QT_VERSION_STR, PYQT_VERSION_STR
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMessageBox from PyQt5.QtWidgets import QApplication, QMessageBox
from requests import HTTPError from requests import HTTPError
import rare import rare
from rare.components.dialogs.launch_dialog import LaunchDialog from rare.components.dialogs.launch_dialog import LaunchDialog
from rare.components.main_window import MainWindow from rare.components.main_window import MainWindow
from rare.components.tray_icon import TrayIcon from rare.shared import (
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton LegendaryCoreSingleton,
from rare.shared.image_manager import ImageManagerSingleton GlobalSignalsSingleton,
ArgumentsSingleton,
)
from rare.shared.rare_core import RareCore
from rare.utils import legendary_utils, config_helper from rare.utils import legendary_utils, config_helper
from rare.utils.paths import cache_dir, tmp_dir from rare.utils.paths import cache_dir, tmp_dir
from rare.widgets.rare_app import RareApp from rare.widgets.rare_app import RareApp
@ -54,66 +56,24 @@ def excepthook(exc_type, exc_value, exc_tb):
class App(RareApp): class App(RareApp):
mainwindow: Optional[MainWindow] = None
tray_icon: Optional[QSystemTrayIcon] = None
def __init__(self, args: Namespace): def __init__(self, args: Namespace):
super(App, self).__init__() super(App, self).__init__()
self.args = ArgumentsSingleton(args) # add some options self.rare_core = RareCore(args=args)
self.window_launched = False self.args = ArgumentsSingleton()
self.signals = GlobalSignalsSingleton()
self.core = LegendaryCoreSingleton()
# init Legendary config_helper.init_config_handler(self.core)
try:
self.core = LegendaryCoreSingleton(init=True)
except configparser.MissingSectionHeaderError as e:
logger.warning(f"Config is corrupt: {e}")
if config_path := os.environ.get("XDG_CONFIG_HOME"):
path = os.path.join(config_path, "legendary")
else:
path = os.path.expanduser("~/.config/legendary")
with open(os.path.join(path, "config.ini"), "w") as config_file:
config_file.write("[Legendary]")
self.core = LegendaryCoreSingleton(init=True)
if "Legendary" not in self.core.lgd.config.sections():
self.core.lgd.config.add_section("Legendary")
self.core.lgd.save_config()
lang = self.settings.value("language", self.core.language_code, type=str) lang = self.settings.value("language", self.core.language_code, type=str)
self.load_translator(lang) self.load_translator(lang)
config_helper.init_config_handler(self.core)
# workaround if egl sync enabled, but no programdata_path
# programdata_path might be unset if logging in through the browser
if self.core.egl_sync_enabled:
if self.core.egl.programdata_path is None:
self.core.lgd.config.remove_option("Legendary", "egl_sync")
self.core.lgd.save_config()
else:
if not os.path.exists(self.core.egl.programdata_path):
self.core.lgd.config.remove_option("Legendary", "egl_sync")
self.core.lgd.save_config()
# set Application name for settings # set Application name for settings
self.launch_dialog = None self.mainwindow: Optional[MainWindow] = None
self.launch_dialog: Optional[LaunchDialog] = None
self.signals = GlobalSignalsSingleton(init=True)
self.image_manager = ImageManagerSingleton(init=True)
self.signals.exit_app.connect(self.exit_app)
self.signals.send_notification.connect(
lambda title: self.tray_icon.showMessage(
self.tr("Download finished"),
self.tr("Download finished. {} is playable now").format(title),
QSystemTrayIcon.Information,
4000,
)
if self.settings.value("notification", True, bool)
else None
)
# launch app # launch app
self.launch_dialog = LaunchDialog() self.launch_dialog = LaunchDialog(parent=None)
self.launch_dialog.quit_app.connect(self.launch_dialog.close) self.launch_dialog.quit_app.connect(self.launch_dialog.close)
self.launch_dialog.quit_app.connect(lambda ec: exit(ec)) self.launch_dialog.quit_app.connect(lambda ec: exit(ec))
self.launch_dialog.start_app.connect(self.start_app) self.launch_dialog.start_app.connect(self.start_app)
@ -140,12 +100,6 @@ class App(RareApp):
td = abs(dt_exp - dt_now) td = abs(dt_exp - dt_now)
self.timer.start(int(td.total_seconds() - 60) * 1000) self.timer.start(int(td.total_seconds() - 60) * 1000)
def show_mainwindow(self):
if self.window_launched:
self.mainwindow.show()
else:
self.mainwindow.show_window_centered()
def start_app(self): def start_app(self):
for igame in self.core.get_installed_list(): for igame in self.core.get_installed_list():
if not os.path.exists(igame.install_path): if not os.path.exists(igame.install_path):
@ -167,78 +121,26 @@ class App(RareApp):
logger.info(f"{igame.title} needs verification") logger.info(f"{igame.title} needs verification")
self.mainwindow = MainWindow() self.mainwindow = MainWindow()
self.tray_icon: TrayIcon = TrayIcon(self) self.mainwindow.exit_app.connect(self.exit_app)
self.tray_icon.exit_action.triggered.connect(self.exit_app)
self.tray_icon.start_rare.triggered.connect(self.show_mainwindow)
self.tray_icon.activated.connect(
lambda r: self.show_mainwindow()
if r == QSystemTrayIcon.DoubleClick
else None
)
if not self.args.silent: if not self.args.silent:
self.mainwindow.show_window_centered() self.mainwindow.show()
self.window_launched = True
if self.args.subparser == "launch":
if self.args.app_name in [
i.app_name for i in self.core.get_installed_list()
]:
logger.info(
f"Launching {self.core.get_installed_game(self.args.app_name).title}"
)
self.mainwindow.tab_widget.games_tab.game_utils.prepare_launch(
self.args.app_name
)
else:
logger.error(
f"Could not find {self.args.app_name} in Games or it is not installed"
)
QMessageBox.warning(
self.mainwindow,
"Warning",
self.tr(
"Could not find {} in installed games. Did you modify the shortcut? "
).format(self.args.app_name),
)
if self.args.test_start: if self.args.test_start:
self.exit_app(0) self.exit_app(0)
def tray(self, reason):
if reason == QSystemTrayIcon.DoubleClick:
self.mainwindow.show()
logger.info("Show App")
def exit_app(self, exit_code=0): def exit_app(self, exit_code=0):
# FIXME: Fix this with the downlaod tab redesign
if self.mainwindow is not None:
if not self.args.offline and self.mainwindow.tab_widget.downloadTab.is_download_active:
question = QMessageBox.question(
self.mainwindow,
self.tr("Close"),
self.tr(
"There is a download active. Do you really want to exit app?"
),
QMessageBox.Yes,
QMessageBox.No,
)
if question == QMessageBox.No:
return
else:
# clear queue
self.mainwindow.tab_widget.downloadTab.queue_widget.update_queue([])
self.mainwindow.tab_widget.downloadTab.stop_download()
# FIXME: End of FIXME
self.mainwindow.timer.stop()
self.mainwindow.hide()
threadpool = QThreadPool.globalInstance() threadpool = QThreadPool.globalInstance()
threadpool.waitForDone() threadpool.waitForDone()
self.core.exit() if self.timer is not None:
self.timer.stop()
self.timer.deleteLater()
self.timer = None
if self.mainwindow is not None: if self.mainwindow is not None:
self.mainwindow.close() self.mainwindow.close()
if self.tray_icon is not None: self.mainwindow = None
self.tray_icon.deleteLater() self.rare_core.deleteLater()
del self.rare_core
self.processEvents() self.processEvents()
shutil.rmtree(tmp_dir) shutil.rmtree(tmp_dir)
os.makedirs(tmp_dir) os.makedirs(tmp_dir)
@ -284,3 +186,5 @@ def start(args):
del app del app
if exit_code != -133742: if exit_code != -133742:
break break

View file

@ -145,9 +145,9 @@ class InstallDialog(QDialog):
self.ui.shortcut_cb.setChecked(False) self.ui.shortcut_cb.setChecked(False)
self.ui.shortcut_cb.setToolTip(self.tr("Creating a shortcut is not supported on MacOS")) self.ui.shortcut_cb.setToolTip(self.tr("Creating a shortcut is not supported on MacOS"))
self.ui.install_preqs_lbl.setVisible(False) self.ui.install_prereqs_lbl.setVisible(False)
self.ui.install_preqs_check.setVisible(False) self.ui.install_prereqs_check.setVisible(False)
self.ui.install_preqs_check.stateChanged.connect(lambda: self.non_reload_option_changed("install_preqs")) self.ui.install_prereqs_check.stateChanged.connect(lambda: self.non_reload_option_changed("install_prereqs"))
self.non_reload_option_changed("shortcut") self.non_reload_option_changed("shortcut")
@ -155,7 +155,7 @@ class InstallDialog(QDialog):
self.ui.verify_button.clicked.connect(self.verify_clicked) self.ui.verify_button.clicked.connect(self.verify_clicked)
self.ui.install_button.clicked.connect(self.install_clicked) self.ui.install_button.clicked.connect(self.install_clicked)
self.ui.install_preqs_check.setChecked(self.dl_item.options.install_preqs) self.ui.install_prereqs_check.setChecked(self.dl_item.options.install_prereqs)
self.ui.install_dialog_layout.setSizeConstraint(QLayout.SetFixedSize) self.ui.install_dialog_layout.setSizeConstraint(QLayout.SetFixedSize)
@ -209,7 +209,7 @@ class InstallDialog(QDialog):
self.dl_item.options.ignore_space = self.ui.ignore_space_check.isChecked() self.dl_item.options.ignore_space = self.ui.ignore_space_check.isChecked()
self.dl_item.options.no_install = self.ui.download_only_check.isChecked() self.dl_item.options.no_install = self.ui.download_only_check.isChecked()
self.dl_item.options.platform = self.ui.platform_combo_box.currentText() self.dl_item.options.platform = self.ui.platform_combo_box.currentText()
self.dl_item.options.install_preqs = self.ui.install_preqs_check.isChecked() self.dl_item.options.install_prereqs = self.ui.install_prereqs_check.isChecked()
self.dl_item.options.create_shortcut = self.ui.shortcut_cb.isChecked() self.dl_item.options.create_shortcut = self.ui.shortcut_cb.isChecked()
if self.sdl_list_cbs: if self.sdl_list_cbs:
self.dl_item.options.install_tag = [""] self.dl_item.options.install_tag = [""]
@ -254,8 +254,8 @@ class InstallDialog(QDialog):
elif option == "shortcut": elif option == "shortcut":
QSettings().setValue("create_shortcut", self.ui.shortcut_cb.isChecked()) QSettings().setValue("create_shortcut", self.ui.shortcut_cb.isChecked())
self.dl_item.options.create_shortcut = self.ui.shortcut_cb.isChecked() self.dl_item.options.create_shortcut = self.ui.shortcut_cb.isChecked()
elif option == "install_preqs": elif option == "install_prereqs":
self.dl_item.options.install_preqs = self.ui.install_preqs_check.isChecked() self.dl_item.options.install_prereqs = self.ui.install_prereqs_check.isChecked()
def cancel_clicked(self): def cancel_clicked(self):
if self.config_tags: if self.config_tags:
@ -289,10 +289,10 @@ class InstallDialog(QDialog):
self.ui.cancel_button.setEnabled(True) self.ui.cancel_button.setEnabled(True)
if pf.system() == "Windows" or ArgumentsSingleton().debug: if pf.system() == "Windows" or ArgumentsSingleton().debug:
if dl_item.igame.prereq_info and not dl_item.igame.prereq_info.get("installed", False): if dl_item.igame.prereq_info and not dl_item.igame.prereq_info.get("installed", False):
self.ui.install_preqs_check.setVisible(True) self.ui.install_prereqs_check.setVisible(True)
self.ui.install_preqs_lbl.setVisible(True) self.ui.install_prereqs_lbl.setVisible(True)
self.ui.install_preqs_check.setChecked(True) self.ui.install_prereqs_check.setChecked(True)
self.ui.install_preqs_check.setText( self.ui.install_prereqs_check.setText(
self.tr("Also install: {}").format(dl_item.igame.prereq_info.get("name", "")) self.tr("Also install: {}").format(dl_item.igame.prereq_info.get("name", ""))
) )
if self.silent: if self.silent:

View file

@ -2,17 +2,16 @@ import platform
from logging import getLogger from logging import getLogger
from PyQt5.QtCore import Qt, pyqtSignal, QRunnable, QObject, QThreadPool, QSettings from PyQt5.QtCore import Qt, pyqtSignal, QRunnable, QObject, QThreadPool, QSettings
from PyQt5.QtWidgets import QDialog, qApp from PyQt5.QtWidgets import QDialog, QApplication
from requests.exceptions import ConnectionError, HTTPError from requests.exceptions import ConnectionError, HTTPError
from rare.components.dialogs.login import LoginDialog from rare.components.dialogs.login import LoginDialog
from rare.models.apiresults import ApiResults from rare.models.apiresults import ApiResults
from rare.shared import LegendaryCoreSingleton, ArgumentsSingleton, ApiResultsSingleton from rare.shared import LegendaryCoreSingleton, ArgumentsSingleton, ApiResultsSingleton, ImageManagerSingleton
from rare.shared.image_manager import ImageManagerSingleton
from rare.ui.components.dialogs.launch_dialog import Ui_LaunchDialog from rare.ui.components.dialogs.launch_dialog import Ui_LaunchDialog
from rare.utils.misc import CloudWorker from rare.utils.misc import CloudWorker
logger = getLogger("Login") logger = getLogger("LoginDialog")
class LaunchWorker(QRunnable): class LaunchWorker(QRunnable):
@ -82,14 +81,13 @@ class ApiRequestWorker(LaunchWorker):
self.signals.result.emit(result, "32bit") self.signals.result.emit(result, "32bit")
class LaunchDialog(QDialog, Ui_LaunchDialog): class LaunchDialog(QDialog):
quit_app = pyqtSignal(int) quit_app = pyqtSignal(int)
start_app = pyqtSignal() start_app = pyqtSignal()
completed = 0 completed = 0
def __init__(self, parent=None): def __init__(self, parent=None):
super(LaunchDialog, self).__init__(parent=parent) super(LaunchDialog, self).__init__(parent=parent)
self.setupUi(self)
self.setAttribute(Qt.WA_DeleteOnClose, True) self.setAttribute(Qt.WA_DeleteOnClose, True)
self.setWindowFlags( self.setWindowFlags(
Qt.Window Qt.Window
@ -101,19 +99,26 @@ class LaunchDialog(QDialog, Ui_LaunchDialog):
| Qt.MSWindowsFixedSizeDialogHint | Qt.MSWindowsFixedSizeDialogHint
) )
self.setWindowModality(Qt.WindowModal) self.setWindowModality(Qt.WindowModal)
self.ui = Ui_LaunchDialog()
self.ui.setupUi(self)
self.core = LegendaryCoreSingleton() self.core = LegendaryCoreSingleton()
self.args = ArgumentsSingleton() self.args = ArgumentsSingleton()
self.thread_pool = QThreadPool().globalInstance() self.thread_pool = QThreadPool().globalInstance()
self.api_results = ApiResults() self.api_results = ApiResults()
self.login_dialog = LoginDialog(core=self.core, parent=self)
def login(self): def login(self):
do_launch = True do_launch = True
try: try:
if self.args.offline: if self.args.offline:
pass pass
else: else:
qApp.processEvents() QApplication.instance().processEvents()
# Force an update check and notice in case there are API changes
self.core.check_for_updates(force=True)
self.core.force_show_update = True
if self.core.login(): if self.core.login():
logger.info("You are logged in") logger.info("You are logged in")
else: else:
@ -122,8 +127,8 @@ class LaunchDialog(QDialog, Ui_LaunchDialog):
logger.info(str(e)) logger.info(str(e))
# Do not set parent, because it won't show a task bar icon # Do not set parent, because it won't show a task bar icon
# Update: Inherit the same parent as LaunchDialog # Update: Inherit the same parent as LaunchDialog
do_launch = LoginDialog(core=self.core, parent=self.parent()).login() do_launch = self.login_dialog.login()
except ConnectionError as e: except (HTTPError, ConnectionError) as e:
logger.warning(e) logger.warning(e)
self.args.offline = True self.args.offline = True
finally: finally:
@ -143,7 +148,7 @@ class LaunchDialog(QDialog, Ui_LaunchDialog):
def launch(self): def launch(self):
if not self.args.offline: if not self.args.offline:
self.image_info.setText(self.tr("Downloading Images")) self.ui.image_info.setText(self.tr("Downloading Images"))
image_worker = ImageWorker() image_worker = ImageWorker()
image_worker.signals.result.connect(self.handle_api_worker_result) image_worker.signals.result.connect(self.handle_api_worker_result)
image_worker.signals.progress.connect(self.update_image_progbar) image_worker.signals.progress.connect(self.update_image_progbar)
@ -202,13 +207,13 @@ class LaunchDialog(QDialog, Ui_LaunchDialog):
self.finish() self.finish()
def update_image_progbar(self, i: int): def update_image_progbar(self, i: int):
self.image_prog_bar.setValue(i) self.ui.image_prog_bar.setValue(i)
def finish(self): def finish(self):
self.completed += 1 self.completed += 1
if self.completed == 2: if self.completed == 2:
logger.info("App starting") logger.info("App starting")
self.image_info.setText(self.tr("Starting...")) self.ui.image_info.setText(self.tr("Starting..."))
ApiResultsSingleton(self.api_results) ApiResultsSingleton(self.api_results)
self.completed += 1 self.completed += 1
self.start_app.emit() self.start_app.emit()

View file

@ -36,8 +36,6 @@ class LoginDialog(QDialog):
def __init__(self, core: LegendaryCore, parent=None): def __init__(self, core: LegendaryCore, parent=None):
super(LoginDialog, self).__init__(parent=parent) super(LoginDialog, self).__init__(parent=parent)
self.ui = Ui_LoginDialog()
self.ui.setupUi(self)
self.setAttribute(Qt.WA_DeleteOnClose, True) self.setAttribute(Qt.WA_DeleteOnClose, True)
self.setWindowFlags( self.setWindowFlags(
Qt.Window Qt.Window
@ -50,6 +48,8 @@ class LoginDialog(QDialog):
| Qt.MSWindowsFixedSizeDialogHint | Qt.MSWindowsFixedSizeDialogHint
) )
self.setWindowModality(Qt.WindowModal) self.setWindowModality(Qt.WindowModal)
self.ui = Ui_LoginDialog()
self.ui.setupUi(self)
self.core = core self.core = core
self.args = ArgumentsSingleton() self.args = ArgumentsSingleton()
@ -76,7 +76,10 @@ class LoginDialog(QDialog):
self.ui.back_button.setEnabled(False) self.ui.back_button.setEnabled(False)
self.landing_page.ui.login_browser_radio.clicked.connect(lambda: self.ui.next_button.setEnabled(True)) self.landing_page.ui.login_browser_radio.clicked.connect(lambda: self.ui.next_button.setEnabled(True))
self.landing_page.ui.login_browser_radio.clicked.connect(self.browser_radio_clicked)
self.landing_page.ui.login_import_radio.clicked.connect(lambda: self.ui.next_button.setEnabled(True)) self.landing_page.ui.login_import_radio.clicked.connect(lambda: self.ui.next_button.setEnabled(True))
self.landing_page.ui.login_import_radio.clicked.connect(self.import_radio_clicked)
self.ui.exit_button.clicked.connect(self.close) self.ui.exit_button.clicked.connect(self.close)
self.ui.back_button.clicked.connect(self.back_clicked) self.ui.back_button.clicked.connect(self.back_clicked)
self.ui.next_button.clicked.connect(self.next_clicked) self.ui.next_button.clicked.connect(self.next_clicked)
@ -90,15 +93,22 @@ class LoginDialog(QDialog):
self.ui.next_button.setEnabled(True) self.ui.next_button.setEnabled(True)
self.login_stack.slideInIndex(self.pages.landing) self.login_stack.slideInIndex(self.pages.landing)
def browser_radio_clicked(self):
self.login_stack.slideInIndex(self.pages.browser)
self.ui.back_button.setEnabled(True)
self.ui.next_button.setEnabled(False)
def import_radio_clicked(self):
self.login_stack.slideInIndex(self.pages.import_egl)
self.ui.back_button.setEnabled(True)
self.ui.next_button.setEnabled(self.import_page.is_valid())
def next_clicked(self): def next_clicked(self):
if self.login_stack.currentIndex() == self.pages.landing: if self.login_stack.currentIndex() == self.pages.landing:
if self.landing_page.ui.login_browser_radio.isChecked(): if self.landing_page.ui.login_browser_radio.isChecked():
self.login_stack.slideInIndex(self.pages.browser) self.browser_radio_clicked()
self.ui.next_button.setEnabled(False)
if self.landing_page.ui.login_import_radio.isChecked(): if self.landing_page.ui.login_import_radio.isChecked():
self.login_stack.slideInIndex(self.pages.import_egl) self.import_radio_clicked()
self.ui.next_button.setEnabled(self.import_page.is_valid())
self.ui.back_button.setEnabled(True)
elif self.login_stack.currentIndex() == self.pages.browser: elif self.login_stack.currentIndex() == self.pages.browser:
self.browser_page.do_login() self.browser_page.do_login()
elif self.login_stack.currentIndex() == self.pages.import_egl: elif self.login_stack.currentIndex() == self.pages.import_egl:

View file

@ -18,9 +18,6 @@ logger = getLogger("BrowserLogin")
class BrowserLogin(QFrame): class BrowserLogin(QFrame):
success = pyqtSignal() success = pyqtSignal()
changed = pyqtSignal() changed = pyqtSignal()
login_url = (
"https://www.epicgames.com/id/login?redirectUrl=https%3A%2F%2Fwww.epicgames.com%2Fid%2Fapi%2Fredirect"
)
def __init__(self, core: LegendaryCore, parent=None): def __init__(self, core: LegendaryCore, parent=None):
super(BrowserLogin, self).__init__(parent=parent) super(BrowserLogin, self).__init__(parent=parent)
@ -29,9 +26,10 @@ class BrowserLogin(QFrame):
self.ui.setupUi(self) self.ui.setupUi(self)
self.core = core self.core = core
self.login_url = self.core.egs.get_auth_url()
self.sid_edit = IndicatorLineEdit( self.sid_edit = IndicatorLineEdit(
placeholder=self.tr("Insert SID here"), edit_func=self.text_changed, parent=self placeholder=self.tr("Insert authorizationCode here"), edit_func=self.text_changed, parent=self
) )
self.ui.link_text.setText(self.login_url) self.ui.link_text.setText(self.login_url)
self.ui.copy_button.setIcon(icon("mdi.content-copy", "fa.copy")) self.ui.copy_button.setIcon(icon("mdi.content-copy", "fa.copy"))
@ -56,7 +54,7 @@ class BrowserLogin(QFrame):
text = text.strip() text = text.strip()
if text.startswith("{") and text.endswith("}"): if text.startswith("{") and text.endswith("}"):
try: try:
text = json.loads(text).get("sid") text = json.loads(text).get("authorizationCode")
except json.JSONDecodeError: except json.JSONDecodeError:
return False, text, IndicatorLineEdit.reasons.wrong_format return False, text, IndicatorLineEdit.reasons.wrong_format
elif '"' in text: elif '"' in text:
@ -67,10 +65,9 @@ class BrowserLogin(QFrame):
def do_login(self): def do_login(self):
self.ui.status_label.setText(self.tr("Logging in...")) self.ui.status_label.setText(self.tr("Logging in..."))
sid = self.sid_edit.text() auth_code = self.sid_edit.text()
try: try:
token = self.core.auth_sid(sid) if self.core.auth_code(auth_code):
if self.core.auth_code(token):
logger.info(f"Successfully logged in as {self.core.lgd.userdata['displayName']}") logger.info(f"Successfully logged in as {self.core.lgd.userdata['displayName']}")
self.success.emit() self.success.emit()
else: else:
@ -80,13 +77,11 @@ class BrowserLogin(QFrame):
logger.warning(e) logger.warning(e)
def open_browser(self): def open_browser(self):
if webview_login.webview_available is False: if not webview_login.webview_available:
logger.warning("You don't have webengine installed, " "you will need to manually copy the SID.") logger.warning("You don't have webengine installed, you will need to manually copy the authorizationCode.")
QDesktopServices.openUrl(QUrl(self.login_url)) QDesktopServices.openUrl(QUrl(self.login_url))
else: else:
if webview_login.do_webview_login( if webview_login.do_webview_login(callback_code=self.core.auth_ex_token):
callback_sid=self.core.auth_sid, callback_code=self.core.auth_code
):
logger.info("Successfully logged in as " f"{self.core.lgd.userdata['displayName']}") logger.info("Successfully logged in as " f"{self.core.lgd.userdata['displayName']}")
self.success.emit() self.success.emit()
else: else:

View file

@ -5,6 +5,7 @@ from logging import getLogger
from PyQt5.QtCore import pyqtSignal from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QFrame, QFileDialog from PyQt5.QtWidgets import QFrame, QFileDialog
from legendary.core import LegendaryCore from legendary.core import LegendaryCore
from legendary.utils.wine_helpers import read_registry, get_shell_folders
from rare.ui.components.dialogs.login.import_login import Ui_ImportLogin from rare.ui.components.dialogs.login.import_login import Ui_ImportLogin
@ -18,7 +19,7 @@ class ImportLogin(QFrame):
localappdata = os.path.expandvars("%LOCALAPPDATA%") localappdata = os.path.expandvars("%LOCALAPPDATA%")
else: else:
localappdata = os.path.join("drive_c/users", getuser(), "Local Settings/Application Data") localappdata = os.path.join("drive_c/users", getuser(), "Local Settings/Application Data")
appdata_path = os.path.join(localappdata, "EpicGamesLauncher/Saved/Config/Windows") egl_appdata = os.path.join(localappdata, "EpicGamesLauncher", "Saved", "Config", "Windows")
found = False found = False
def __init__(self, core: LegendaryCore, parent=None): def __init__(self, core: LegendaryCore, parent=None):
@ -33,16 +34,19 @@ class ImportLogin(QFrame):
self.text_egl_notfound = self.tr("Could not find EGL Program Data. ") self.text_egl_notfound = self.tr("Could not find EGL Program Data. ")
if os.name == "nt": if os.name == "nt":
if not self.core.egl.appdata_path and os.path.exists(self.appdata_path): if not self.core.egl.appdata_path and os.path.exists(self.egl_appdata):
self.core.egl.appdata_path = self.appdata_path self.core.egl.appdata_path = self.egl_appdata
if not self.core.egl.appdata_path: if not self.core.egl.appdata_path:
self.ui.status_label.setText(self.text_egl_notfound) self.ui.status_label.setText(self.text_egl_notfound)
else: else:
self.ui.status_label.setText(self.text_egl_found) self.ui.status_label.setText(self.text_egl_found)
self.found = True self.found = True
else: else:
if programdata_path := self.core.egl.programdata_path:
if wine_pfx := programdata_path.split("drive_c")[0]:
self.ui.prefix_combo.addItem(wine_pfx)
self.ui.info_label.setText( self.ui.info_label.setText(
self.tr("Please select the Wine prefix" " where Epic Games Launcher is installed. ") self.tr("Please select the Wine prefix where Epic Games Launcher is installed. ")
+ self.ui.info_label.text() + self.ui.info_label.text()
) )
prefixes = self.get_wine_prefixes() prefixes = self.get_wine_prefixes()
@ -62,7 +66,7 @@ class ImportLogin(QFrame):
] ]
prefixes = [] prefixes = []
for prefix in possible_prefixes: for prefix in possible_prefixes:
if os.path.exists(os.path.join(prefix, self.appdata_path)): if os.path.exists(os.path.join(prefix, self.egl_appdata)):
prefixes.append(prefix) prefixes.append(prefix)
return prefixes return prefixes
@ -73,18 +77,28 @@ class ImportLogin(QFrame):
names = prefix_dialog.selectedFiles() names = prefix_dialog.selectedFiles()
self.ui.prefix_combo.setCurrentText(names[0]) self.ui.prefix_combo.setCurrentText(names[0])
def is_valid(self): def is_valid(self) -> bool:
if os.name == "nt": if os.name == "nt":
return self.found return self.found
else: else:
return os.path.exists(os.path.join(self.ui.prefix_combo.currentText(), self.appdata_path)) egl_wine_pfx = self.ui.prefix_combo.currentText()
try:
wine_folders = get_shell_folders(read_registry(egl_wine_pfx), egl_wine_pfx)
self.egl_appdata = os.path.realpath(
os.path.join(wine_folders['Local AppData'], 'EpicGamesLauncher', 'Saved', 'Config', 'Windows'))
if path_exists := os.path.exists(self.egl_appdata):
self.ui.status_label.setText(self.text_egl_found)
return path_exists
except KeyError:
return False
def do_login(self): def do_login(self):
self.ui.status_label.setText(self.tr("Loading...")) self.ui.status_label.setText(self.tr("Loading..."))
if os.name == "nt": if os.name == "nt":
pass pass
else: else:
self.core.egl.appdata_path = os.path.join(self.ui.prefix_combo.currentText(), self.appdata_path) logger.info(f'Using EGL appdata path at "{self.egl_appdata}"')
self.core.egl.appdata_path = self.egl_appdata
try: try:
if self.core.auth_import(): if self.core.auth_import():
logger.info(f"Logged in as {self.core.lgd.userdata['displayName']}") logger.info(f"Logged in as {self.core.lgd.userdata['displayName']}")

View file

@ -1,20 +1,24 @@
import os import os
from logging import getLogger from logging import getLogger
from PyQt5.QtCore import Qt, QSettings, QTimer, QSize from PyQt5.QtCore import Qt, QSettings, QTimer, QSize, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QCloseEvent, QCursor from PyQt5.QtGui import QCloseEvent, QCursor
from PyQt5.QtWidgets import QMainWindow, QApplication, QStatusBar, QScrollArea, QScroller, QComboBox from PyQt5.QtWidgets import QMainWindow, QApplication, QStatusBar, QScrollArea, QScroller, QComboBox, QMessageBox
from rare.components.tabs import TabWidget from rare.components.tabs import TabWidget
from rare.components.tray_icon import TrayIcon
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
from rare.utils.paths import data_dir from rare.utils.paths import data_dir
logger = getLogger("Window") logger = getLogger("MainWindow")
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
def __init__(self): # int: exit code
super(MainWindow, self).__init__() exit_app: pyqtSignal = pyqtSignal(int)
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=parent)
self.setAttribute(Qt.WA_DeleteOnClose) self.setAttribute(Qt.WA_DeleteOnClose)
self.core = LegendaryCoreSingleton() self.core = LegendaryCoreSingleton()
self.signals = GlobalSignalsSingleton() self.signals = GlobalSignalsSingleton()
@ -46,8 +50,42 @@ class MainWindow(QMainWindow):
self.timer.timeout.connect(self.timer_finished) self.timer.timeout.connect(self.timer_finished)
self.timer.start(1000) self.timer.start(1000)
def show_window_centered(self): self.signals.exit_app.connect(self.on_exit_app)
self.show() self.exit_code = 0
self.accept_close = False
self.tray_icon: TrayIcon = TrayIcon(self)
self.tray_icon.exit_action.triggered.connect(self.on_exit_app)
self.tray_icon.start_rare.triggered.connect(self.show)
self.tray_icon.activated.connect(
lambda r: self.toggle()
if r == self.tray_icon.DoubleClick
else None
)
self.signals.send_notification.connect(
lambda title: self.tray_icon.showMessage(
self.tr("Download finished"),
self.tr("Download finished. {} is playable now").format(title),
self.tray_icon.Information,
4000,
)
if self.settings.value("notification", True, bool)
else None
)
self.window_launched = False
# enable kinetic scrolling
for scroll_area in self.findChildren(QScrollArea):
if not scroll_area.property("no_kinetic_scroll"):
QScroller.grabGesture(scroll_area.viewport(), QScroller.LeftMouseButtonGesture)
# fix scrolling
for combo_box in scroll_area.findChildren(QComboBox):
combo_box.wheelEvent = lambda e: e.ignore()
def center_window(self):
# get the margins of the decorated window # get the margins of the decorated window
margins = self.windowHandle().frameMargins() margins = self.windowHandle().frameMargins()
# get the screen the cursor is on # get the screen the cursor is on
@ -68,14 +106,23 @@ class MainWindow(QMainWindow):
- self.rect().adjusted(0, 0, decor_width, decor_height).center() - self.rect().adjusted(0, 0, decor_width, decor_height).center()
) )
# enable kinetic scrolling def show(self) -> None:
for scroll_area in self.findChildren(QScrollArea): super(MainWindow, self).show()
if not scroll_area.property("no_kinetic_scroll"): if not self.window_launched:
QScroller.grabGesture(scroll_area.viewport(), QScroller.LeftMouseButtonGesture) self.center_window()
self.window_launched = True
# fix scrolling def hide(self) -> None:
for combo_box in scroll_area.findChildren(QComboBox): if self.settings.value("save_size", False, bool):
combo_box.wheelEvent = lambda e: e.ignore() size = self.size().width(), self.size().height()
self.settings.setValue("window_size", size)
super(MainWindow, self).hide()
def toggle(self):
if self.isHidden():
self.show()
else:
self.hide()
def timer_finished(self): def timer_finished(self):
file_path = os.path.join(data_dir, "lockfile") file_path = os.path.join(data_dir, "lockfile")
@ -83,30 +130,48 @@ class MainWindow(QMainWindow):
file = open(file_path, "r") file = open(file_path, "r")
action = file.read() action = file.read()
file.close() file.close()
if action.startswith("launch"): if action.startswith("show"):
game = action.replace("launch ", "").replace("\n", "")
if game in [
i.app_name for i in self.tab_widget.games_tab.game_list
] and self.core.is_installed(game):
self.tab_widget.games_tab.game_utils.prepare_launch(
game, offline=self.args.offline
)
else:
logger.info(f"Could not find {game} in Games")
elif action.startswith("start"):
self.show() self.show()
os.remove(file_path) os.remove(file_path)
self.timer.start(1000) self.timer.start(1000)
@pyqtSlot()
@pyqtSlot(int)
def on_exit_app(self, exit_code=0) -> None:
# FIXME: Fix this with the download tab redesign
if not self.args.offline and self.tab_widget.downloadTab.is_download_active:
question = QMessageBox.question(
self,
self.tr("Close"),
self.tr(
"There is a download active. Do you really want to exit app?"
),
QMessageBox.Yes,
QMessageBox.No,
)
if question == QMessageBox.No:
return
else:
# clear queue
self.tab_widget.downloadTab.queue_widget.update_queue([])
self.tab_widget.downloadTab.stop_download()
# FIXME: End of FIXME
self.exit_code = exit_code
self.close()
def close(self) -> bool:
self.accept_close = True
return super(MainWindow, self).close()
def closeEvent(self, e: QCloseEvent): def closeEvent(self, e: QCloseEvent):
if self.settings.value("save_size", False, bool): if not self.accept_close:
size = self.size().width(), self.size().height() if self.settings.value("sys_tray", True, bool):
self.settings.setValue("window_size", size) self.hide()
if self.settings.value("sys_tray", True, bool): e.ignore()
self.hide() return
e.ignore() self.timer.stop()
return self.tray_icon.deleteLater()
elif self.args.offline: self.hide()
pass self.exit_app.emit(self.exit_code)
self.signals.exit_app.emit(0) super(MainWindow, self).closeEvent(e)
e.ignore() e.accept()

View file

@ -98,7 +98,7 @@ class DownloadThread(QThread):
# postinstall, # postinstall,
# self.item.download.igame, # self.item.download.igame,
# False, # False,
# self.item.options.install_preqs, # self.item.options.install_prereqs,
# ) # )
self._handle_postinstall(postinstall, self.item.download.igame) self._handle_postinstall(postinstall, self.item.download.igame)
@ -140,7 +140,7 @@ class DownloadThread(QThread):
logger.info("This game lists the following prequisites to be installed:") logger.info("This game lists the following prequisites to be installed:")
logger.info(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}') logger.info(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}')
if platform.system() == "Windows": if platform.system() == "Windows":
if not self.item.options.install_preqs: if not self.item.options.install_prereqs:
logger.info("Marking prerequisites as installed...") logger.info("Marking prerequisites as installed...")
self.core.prereq_installed(self.item.download.igame.app_name) self.core.prereq_installed(self.item.download.igame.app_name)
else: else:

View file

@ -11,7 +11,7 @@ from rare.shared import (
ArgumentsSingleton, ArgumentsSingleton,
ApiResultsSingleton, ApiResultsSingleton,
) )
from rare.shared.image_manager import ImageManagerSingleton from rare.shared import ImageManagerSingleton
from rare.widgets.library_layout import LibraryLayout from rare.widgets.library_layout import LibraryLayout
from rare.widgets.sliding_stack import SlidingStackedWidget from rare.widgets.sliding_stack import SlidingStackedWidget
from .cloud_save_utils import CloudSaveUtils from .cloud_save_utils import CloudSaveUtils

View file

@ -3,8 +3,8 @@ from PyQt5.QtWidgets import QFrame, QWidget, QMessageBox
from legendary.models.game import Game from legendary.models.game import Game
from rare.components.tabs.games.game_utils import GameUtils from rare.components.tabs.games.game_utils import GameUtils
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ImageManagerSingleton
from rare.shared.image_manager import ImageManagerSingleton, ImageSize from rare.shared.image_manager import ImageSize
from rare.ui.components.tabs.games.game_info.game_dlc import Ui_GameDlc from rare.ui.components.tabs.games.game_info.game_dlc import Ui_GameDlc
from rare.ui.components.tabs.games.game_info.game_dlc_widget import Ui_GameDlcWidget from rare.ui.components.tabs.games.game_info.game_dlc_widget import Ui_GameDlcWidget
from rare.models.install import InstallOptionsModel from rare.models.install import InstallOptionsModel

View file

@ -27,7 +27,8 @@ from rare.shared import (
GlobalSignalsSingleton, GlobalSignalsSingleton,
ArgumentsSingleton, ArgumentsSingleton,
) )
from rare.shared.image_manager import ImageManagerSingleton, ImageSize from rare.shared import ImageManagerSingleton
from rare.shared.image_manager import ImageSize
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
from rare.utils.legendary_utils import VerifyWorker from rare.utils.legendary_utils import VerifyWorker
from rare.utils.misc import get_size from rare.utils.misc import get_size

View file

@ -11,7 +11,8 @@ from rare.shared import (
ArgumentsSingleton, ArgumentsSingleton,
ApiResultsSingleton, ApiResultsSingleton,
) )
from rare.shared.image_manager import ImageManagerSingleton, ImageSize from rare.shared import ImageManagerSingleton
from rare.shared.image_manager import ImageSize
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
from rare.utils.extra_widgets import SideTabWidget from rare.utils.extra_widgets import SideTabWidget
from rare.utils.json_formatter import QJsonModel from rare.utils.json_formatter import QJsonModel

View file

@ -7,8 +7,8 @@ from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QFrame, QMessageBox, QAction from PyQt5.QtWidgets import QFrame, QMessageBox, QAction
from rare.components.tabs.games.game_utils import GameUtils from rare.components.tabs.games.game_utils import GameUtils
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton, ImageManagerSingleton
from rare.shared.image_manager import ImageManagerSingleton, ImageSize from rare.shared.image_manager import ImageSize
from rare.utils.misc import create_desktop_link from rare.utils.misc import create_desktop_link
from rare.widgets.image_widget import ImageWidget from rare.widgets.image_widget import ImageWidget

View file

@ -4,7 +4,8 @@ from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtWidgets import QFrame, QAction from PyQt5.QtWidgets import QFrame, QAction
from legendary.models.game import Game from legendary.models.game import Game
from rare.shared.image_manager import ImageManagerSingleton, ImageSize from rare.shared import ImageManagerSingleton
from rare.shared.image_manager import ImageSize
from rare.widgets.image_widget import ImageWidget from rare.widgets.image_widget import ImageWidget
logger = getLogger("Uninstalled") logger = getLogger("Uninstalled")

View file

@ -3,8 +3,8 @@ from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QWidget, QFrame from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QWidget, QFrame
from legendary.models.game import Game from legendary.models.game import Game
from rare.shared import LegendaryCoreSingleton from rare.shared import LegendaryCoreSingleton, ImageManagerSingleton
from rare.shared.image_manager import ImageManagerSingleton, ImageSize from rare.shared.image_manager import ImageSize
from rare.widgets.elide_label import ElideLabel from rare.widgets.elide_label import ElideLabel
from .library_widget import LibraryWidget from .library_widget import LibraryWidget

View file

@ -1,5 +1,7 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton
from rare.shared import GlobalSignalsSingleton
class DebugSettings(QWidget): class DebugSettings(QWidget):
def __init__(self): def __init__(self):
@ -9,6 +11,9 @@ class DebugSettings(QWidget):
self.raise_runtime_exception_button = QPushButton("Raise Exception") self.raise_runtime_exception_button = QPushButton("Raise Exception")
self.layout().addWidget(self.raise_runtime_exception_button) self.layout().addWidget(self.raise_runtime_exception_button)
self.raise_runtime_exception_button.clicked.connect(self.raise_exception) self.raise_runtime_exception_button.clicked.connect(self.raise_exception)
self.restart_button = QPushButton("Restart")
self.layout().addWidget(self.restart_button)
self.restart_button.clicked.connect(lambda: GlobalSignalsSingleton().exit_app.emit(-133742))
self.layout().addStretch(1) self.layout().addStretch(1)

View file

@ -13,7 +13,7 @@ logger = getLogger("TrayIcon")
class TrayIcon(QSystemTrayIcon): class TrayIcon(QSystemTrayIcon):
def __init__(self, parent): def __init__(self, parent):
super(TrayIcon, self).__init__(parent) super(TrayIcon, self).__init__(parent=parent)
self.core = LegendaryCoreSingleton() self.core = LegendaryCoreSingleton()
self.setIcon(QIcon(":/images/Rare.png")) self.setIcon(QIcon(":/images/Rare.png"))
@ -33,7 +33,7 @@ class TrayIcon(QSystemTrayIcon):
if len(installed := self.core.get_installed_list()) < 5: if len(installed := self.core.get_installed_list()) < 5:
last_played = [GameMeta(i.app_name) for i in sorted(installed, key=lambda x: x.title)] last_played = [GameMeta(i.app_name) for i in sorted(installed, key=lambda x: x.title)]
elif games := sorted( elif games := sorted(
parent.mainwindow.tab_widget.games_tab.game_utils.game_meta.get_games(), parent.tab_widget.games_tab.game_utils.game_meta.get_games(),
key=lambda x: x.last_played, reverse=True): key=lambda x: x.last_played, reverse=True):
last_played: List[GameMeta] = games[0:5] last_played: List[GameMeta] = games[0:5]
else: else:
@ -46,7 +46,7 @@ class TrayIcon(QSystemTrayIcon):
a.setProperty("app_name", game.app_name) a.setProperty("app_name", game.app_name)
self.game_actions.append(a) self.game_actions.append(a)
a.triggered.connect( a.triggered.connect(
lambda: parent.mainwindow.tab_widget.games_tab.game_utils.prepare_launch( lambda: parent.tab_widget.games_tab.game_utils.prepare_launch(
self.sender().property("app_name")) self.sender().property("app_name"))
) )

View file

@ -11,11 +11,11 @@ from PyQt5.QtCore import QObject, QProcess, pyqtSignal, QUrl, QRunnable, QThread
from PyQt5.QtGui import QDesktopServices from PyQt5.QtGui import QDesktopServices
from PyQt5.QtNetwork import QLocalServer, QLocalSocket from PyQt5.QtNetwork import QLocalServer, QLocalSocket
from rare.lgndr.core import LegendaryCore
from rare.widgets.rare_app import RareApp
from .console import Console from .console import Console
from .lgd_helper import get_launch_args, InitArgs, get_configured_process, LaunchArgs, GameArgsError from .lgd_helper import get_launch_args, InitArgs, get_configured_process, LaunchArgs, GameArgsError
from .message_models import ErrorModel, Actions, FinishedModel, BaseModel, StateChangedModel from .message_models import ErrorModel, Actions, FinishedModel, BaseModel, StateChangedModel
from ..shared import LegendaryCoreSingleton
from ..widgets.rare_app import RareApp
class PreLaunchThread(QRunnable): class PreLaunchThread(QRunnable):
@ -25,9 +25,9 @@ class PreLaunchThread(QRunnable):
pre_launch_command_finished = pyqtSignal(int) # exit_code pre_launch_command_finished = pyqtSignal(int) # exit_code
error_occurred = pyqtSignal(str) error_occurred = pyqtSignal(str)
def __init__(self, args: InitArgs): def __init__(self, core: LegendaryCore, args: InitArgs):
super(PreLaunchThread, self).__init__() super(PreLaunchThread, self).__init__()
self.core = LegendaryCoreSingleton() self.core = core
self.app_name = args.app_name self.app_name = args.app_name
self.signals = self.Signals() self.signals = self.Signals()
@ -69,7 +69,7 @@ class GameProcessApp(RareApp):
self.game_process = QProcess() self.game_process = QProcess()
self.app_name = app_name self.app_name = app_name
self.logger = getLogger(self.app_name) self.logger = getLogger(self.app_name)
self.core = LegendaryCoreSingleton(init=True) self.core = LegendaryCore()
lang = self.settings.value("language", self.core.language_code, type=str) lang = self.settings.value("language", self.core.language_code, type=str)
self.load_translator(lang) self.load_translator(lang)
@ -187,7 +187,7 @@ class GameProcessApp(RareApp):
self.logger.error("Not logged in. Try to launch game offline") self.logger.error("Not logged in. Try to launch game offline")
args.offline = True args.offline = True
worker = PreLaunchThread(args) worker = PreLaunchThread(self.core, args)
worker.signals.ready_to_launch.connect(self.launch_game) worker.signals.ready_to_launch.connect(self.launch_game)
worker.signals.error_occurred.connect(self.error_occurred) worker.signals.error_occurred.connect(self.error_occurred)
# worker.signals.started_pre_launch_command(None) # worker.signals.started_pre_launch_command(None)

View file

@ -6,9 +6,11 @@ from logging import getLogger
from typing import List from typing import List
from PyQt5.QtCore import QProcess, QProcessEnvironment from PyQt5.QtCore import QProcess, QProcessEnvironment
from legendary.core import LegendaryCore
from legendary.models.game import InstalledGame, LaunchParameters from legendary.models.game import InstalledGame, LaunchParameters
from rare.lgndr.core import LegendaryCore
logger = getLogger("Helper") logger = getLogger("Helper")

View file

@ -114,7 +114,7 @@ class LgndrInstallGameRealArgs:
dlm_debug: bool = False dlm_debug: bool = False
yes: bool = False yes: bool = False
# Rare: Extra arguments # Rare: Extra arguments
install_preqs: bool = False install_prereqs: bool = False
indirect_status: LgndrIndirectStatus = LgndrIndirectStatus() indirect_status: LgndrIndirectStatus = LgndrIndirectStatus()
ui_update: Callable[[UIUpdate], None] = lambda ui: None ui_update: Callable[[UIUpdate], None] = lambda ui: None
dlm_signals: DLManagerSignals = DLManagerSignals() dlm_signals: DLManagerSignals = DLManagerSignals()

View file

@ -248,7 +248,7 @@ class LegendaryCLI(LegendaryCLIReal):
postinstall = self.core.install_game(igame) postinstall = self.core.install_game(igame)
if postinstall: if postinstall:
self._handle_postinstall(postinstall, igame, yes=args.yes, choice=args.install_preqs) self._handle_postinstall(postinstall, igame, skip_prereqs=args.yes, choice=args.install_prereqs)
dlcs = self.core.get_dlc_for_game(game.app_name) dlcs = self.core.get_dlc_for_game(game.app_name)
if dlcs and not args.skip_dlcs: if dlcs and not args.skip_dlcs:
@ -301,7 +301,7 @@ class LegendaryCLI(LegendaryCLIReal):
self.core.uninstall_tag(old_igame) self.core.uninstall_tag(old_igame)
self.core.install_game(old_igame) self.core.install_game(old_igame)
def _handle_postinstall(self, postinstall, igame, yes=False, choice=True): def _handle_postinstall(self, postinstall, igame, skip_prereqs=False, choice=True):
# Override logger for the local context to use message as part of the indirect return value # Override logger for the local context to use message as part of the indirect return value
logger = LgndrIndirectLogger(LgndrIndirectStatus(), self.logger) logger = LgndrIndirectLogger(LgndrIndirectStatus(), self.logger)
# noinspection PyShadowingBuiltins # noinspection PyShadowingBuiltins
@ -309,12 +309,12 @@ class LegendaryCLI(LegendaryCLIReal):
# noinspection PyShadowingBuiltins # noinspection PyShadowingBuiltins
def input(x): return 'y' if choice else 'i' def input(x): return 'y' if choice else 'i'
print('\nThis game lists the following prequisites to be installed:') print('\nThis game lists the following prerequisites to be installed:')
print(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}') print(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}')
print('') print('')
if os.name == 'nt': if os.name == 'nt':
if yes: if skip_prereqs:
c = 'n' # we don't want to launch anything, just silent install. c = 'n' # we don't want to launch anything, just silent install.
else: else:
choice = input('Do you wish to install the prerequisites? ([y]es, [n]o, [i]gnore): ') choice = input('Do you wish to install the prerequisites? ([y]es, [n]o, [i]gnore): ')

View file

@ -31,7 +31,7 @@ class InstallOptionsModel:
overlay: bool = False overlay: bool = False
update: bool = False update: bool = False
silent: bool = False silent: bool = False
install_preqs: bool = pf.system() == "Windows" install_prereqs: bool = pf.system() == "Windows"
def __post_init__(self): def __post_init__(self):
self.sdl_prompt: Callable[[str, str], list] = \ self.sdl_prompt: Callable[[str, str], list] = \
@ -41,7 +41,7 @@ class InstallOptionsModel:
return { return {
k: getattr(self, k) k: getattr(self, k)
for k in self.__dict__ for k in self.__dict__
if k not in ["update", "silent", "create_shortcut", "overlay", "install_preqs"] if k not in ["update", "silent", "create_shortcut", "overlay", "install_prereqs"]
} }

View file

@ -5,52 +5,34 @@ Each of the objects in this module should be instantiated ONCE
and only ONCE! and only ONCE!
""" """
import logging
from argparse import Namespace from argparse import Namespace
from typing import Optional from typing import Optional
from rare.lgndr.core import LegendaryCore from rare.lgndr.core import LegendaryCore
from rare.models.apiresults import ApiResults from rare.models.apiresults import ApiResults
from rare.models.signals import GlobalSignals from rare.models.signals import GlobalSignals
from .image_manager import ImageManager
from .rare_core import RareCore
_legendary_core_singleton: Optional[LegendaryCore] = None logger = logging.getLogger("Shared")
_global_signals_singleton: Optional[GlobalSignals] = None
_arguments_singleton: Optional[Namespace] = None
_api_results_singleton: Optional[ApiResults] = None
def LegendaryCoreSingleton(init: bool = False) -> LegendaryCore: def ArgumentsSingleton() -> Optional[Namespace]:
global _legendary_core_singleton return RareCore.instance().args()
if _legendary_core_singleton is None and not init:
raise RuntimeError("Uninitialized use of LegendaryCoreSingleton")
if _legendary_core_singleton is None:
_legendary_core_singleton = LegendaryCore()
return _legendary_core_singleton
def GlobalSignalsSingleton(init: bool = False) -> GlobalSignals: def GlobalSignalsSingleton() -> GlobalSignals:
global _global_signals_singleton return RareCore.instance().signals()
if _global_signals_singleton is None and not init:
raise RuntimeError("Uninitialized use of GlobalSignalsSingleton")
if _global_signals_singleton is None:
_global_signals_singleton = GlobalSignals()
return _global_signals_singleton
def ArgumentsSingleton(args: Namespace = None) -> Optional[Namespace]: def LegendaryCoreSingleton() -> LegendaryCore:
global _arguments_singleton return RareCore.instance().core()
if _arguments_singleton is None and args is None:
raise RuntimeError("Uninitialized use of ArgumentsSingleton")
if _arguments_singleton is None: def ImageManagerSingleton() -> ImageManager:
_arguments_singleton = args return RareCore.instance().image_manager()
return _arguments_singleton
def ApiResultsSingleton(res: ApiResults = None) -> Optional[ApiResults]: def ApiResultsSingleton(res: ApiResults = None) -> Optional[ApiResults]:
global _api_results_singleton return RareCore.instance().api_results(res)
if _api_results_singleton is None and res is None:
raise RuntimeError("Uninitialized use of ApiResultsSingleton")
if _api_results_singleton is None:
_api_results_singleton = res
return _api_results_singleton

View file

@ -7,7 +7,7 @@ import zlib
from logging import getLogger from logging import getLogger
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Tuple, Dict, Union, Type, List, Callable, Optional from typing import Tuple, Dict, Union, Type, List, Callable
import requests import requests
from PyQt5.QtCore import ( from PyQt5.QtCore import (
@ -26,7 +26,8 @@ from PyQt5.QtGui import (
from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication
from legendary.models.game import Game from legendary.models.game import Game
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton from rare.lgndr.core import LegendaryCore
from rare.models.signals import GlobalSignals
from rare.utils.paths import image_dir, resources_path from rare.utils.paths import image_dir, resources_path
if TYPE_CHECKING: if TYPE_CHECKING:
@ -111,10 +112,10 @@ class ImageManager(QObject):
__dl_retries = 1 __dl_retries = 1
__worker_app_names: List[str] = list() __worker_app_names: List[str] = list()
def __init__(self): def __init__(self, signals: GlobalSignals, core: LegendaryCore):
super(QObject, self).__init__() super(QObject, self).__init__()
self.core = LegendaryCoreSingleton() self.signals = signals
self.signals = GlobalSignalsSingleton() self.core = core
self.image_dir = Path(image_dir) self.image_dir = Path(image_dir)
if not self.image_dir.is_dir(): if not self.image_dir.is_dir():
@ -357,15 +358,3 @@ class ImageManager(QObject):
""" """
image: QImage = self.__get_cover(QImage, app_name, color) image: QImage = self.__get_cover(QImage, app_name, color)
return image return image
_image_manager_singleton: Optional[ImageManager] = None
def ImageManagerSingleton(init: bool = False) -> ImageManager:
global _image_manager_singleton
if _image_manager_singleton is None and not init:
raise RuntimeError("Uninitialized use of ImageManagerSingleton")
if _image_manager_singleton is None:
_image_manager_singleton = ImageManager()
return _image_manager_singleton

133
rare/shared/rare_core.py Normal file
View file

@ -0,0 +1,133 @@
import configparser
import os
from argparse import Namespace
from logging import getLogger
from typing import Optional
from PyQt5.QtCore import QObject
from rare.lgndr.core import LegendaryCore
from rare.models.apiresults import ApiResults
from rare.models.signals import GlobalSignals
from .image_manager import ImageManager
logger = getLogger("RareCore")
class RareCore(QObject):
_instance: Optional['RareCore'] = None
def __init__(self, args: Namespace):
if self._instance is not None:
raise RuntimeError("RareCore already initialized")
super(RareCore, self).__init__()
self._args: Optional[Namespace] = None
self._signals: Optional[GlobalSignals] = None
self._core: Optional[LegendaryCore] = None
self._image_manager: Optional[ImageManager] = None
self._api_results: Optional[ApiResults] = None
self.args(args)
self.signals(init=True)
self.core(init=True)
self.image_manager(init=True)
RareCore._instance = self
@staticmethod
def instance() -> 'RareCore':
if RareCore._instance is None:
raise RuntimeError("Uninitialized use of RareCore")
return RareCore._instance
def signals(self, init: bool = False) -> GlobalSignals:
if self._signals is None and not init:
raise RuntimeError("Uninitialized use of GlobalSignalsSingleton")
if self._signals is not None and init:
raise RuntimeError("GlobalSignals already initialized")
if init:
self._signals = GlobalSignals()
return self._signals
def args(self, args: Namespace = None) -> Optional[Namespace]:
if self._args is None and args is None:
raise RuntimeError("Uninitialized use of ArgumentsSingleton")
if self._args is not None and args is not None:
raise RuntimeError("Arguments already initialized")
if args is not None:
self._args = args
return self._args
def core(self, init: bool = False) -> LegendaryCore:
if self._core is None and not init:
raise RuntimeError("Uninitialized use of LegendaryCoreSingleton")
if self._core is not None and init:
raise RuntimeError("LegendaryCore already initialized")
if init:
try:
self._core = LegendaryCore()
except configparser.MissingSectionHeaderError as e:
logger.warning(f"Config is corrupt: {e}")
if config_path := os.environ.get("XDG_CONFIG_HOME"):
path = os.path.join(config_path, "legendary")
else:
path = os.path.expanduser("~/.config/legendary")
with open(os.path.join(path, "config.ini"), "w") as config_file:
config_file.write("[Legendary]")
self._core = LegendaryCore()
if "Legendary" not in self._core.lgd.config.sections():
self._core.lgd.config.add_section("Legendary")
self._core.lgd.save_config()
# workaround if egl sync enabled, but no programdata_path
# programdata_path might be unset if logging in through the browser
if self._core.egl_sync_enabled:
if self._core.egl.programdata_path is None:
self._core.lgd.config.remove_option("Legendary", "egl_sync")
self._core.lgd.save_config()
else:
if not os.path.exists(self._core.egl.programdata_path):
self._core.lgd.config.remove_option("Legendary", "egl_sync")
self._core.lgd.save_config()
return self._core
def image_manager(self, init: bool = False) -> ImageManager:
if self._image_manager is None and not init:
raise RuntimeError("Uninitialized use of ImageManagerSingleton")
if self._image_manager is not None and init:
raise RuntimeError("ImageManager already initialized")
if self._image_manager is None:
self._image_manager = ImageManager(self.signals(), self.core())
return self._image_manager
def api_results(self, res: ApiResults = None) -> Optional[ApiResults]:
if self._api_results is None and res is None:
raise RuntimeError("Uninitialized use of ApiResultsSingleton")
if self._api_results is not None and res is not None:
raise RuntimeError("ApiResults already initialized")
if res is not None:
self._api_results = res
return self._api_results
def deleteLater(self) -> None:
del self._api_results
self._api_results = None
self._image_manager.deleteLater()
del self._image_manager
self._image_manager = None
self._core.exit()
del self._core
self._core = None
self._signals.deleteLater()
del self._signals
self._signals = None
del self._args
self._args = None
RareCore._instance = None
super(RareCore, self).deleteLater()

View file

@ -109,17 +109,17 @@ class Ui_InstallDialog(object):
self.max_memory_info_label.setObjectName("max_memory_info_label") self.max_memory_info_label.setObjectName("max_memory_info_label")
self.max_memory_layout.addWidget(self.max_memory_info_label) self.max_memory_layout.addWidget(self.max_memory_info_label)
self.advanced_layout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.max_memory_layout) self.advanced_layout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.max_memory_layout)
self.install_preqs_lbl = QtWidgets.QLabel(InstallDialog) self.install_prereqs_lbl = QtWidgets.QLabel(InstallDialog)
self.install_preqs_lbl.setObjectName("install_preqs_lbl") self.install_prereqs_lbl.setObjectName("install_prereqs_lbl")
self.advanced_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.install_preqs_lbl) self.advanced_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.install_prereqs_lbl)
self.install_preqs_check = QtWidgets.QCheckBox(InstallDialog) self.install_prereqs_check = QtWidgets.QCheckBox(InstallDialog)
font = QtGui.QFont() font = QtGui.QFont()
font.setItalic(True) font.setItalic(True)
self.install_preqs_check.setFont(font) self.install_prereqs_check.setFont(font)
self.install_preqs_check.setText("") self.install_prereqs_check.setText("")
self.install_preqs_check.setChecked(False) self.install_prereqs_check.setChecked(False)
self.install_preqs_check.setObjectName("install_preqs_check") self.install_prereqs_check.setObjectName("install_prereqs_check")
self.advanced_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.install_preqs_check) self.advanced_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.install_prereqs_check)
self.dl_optimizations_label = QtWidgets.QLabel(InstallDialog) self.dl_optimizations_label = QtWidgets.QLabel(InstallDialog)
self.dl_optimizations_label.setObjectName("dl_optimizations_label") self.dl_optimizations_label.setObjectName("dl_optimizations_label")
self.advanced_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.dl_optimizations_label) self.advanced_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.dl_optimizations_label)
@ -216,7 +216,7 @@ class Ui_InstallDialog(object):
self.max_memory_label.setText(_translate("InstallDialog", "Max shared memory")) self.max_memory_label.setText(_translate("InstallDialog", "Max shared memory"))
self.max_memory_spin.setSuffix(_translate("InstallDialog", "MiB")) self.max_memory_spin.setSuffix(_translate("InstallDialog", "MiB"))
self.max_memory_info_label.setText(_translate("InstallDialog", "Less is slower (0: Default)")) self.max_memory_info_label.setText(_translate("InstallDialog", "Less is slower (0: Default)"))
self.install_preqs_lbl.setText(_translate("InstallDialog", "Install prerequisites")) self.install_prereqs_lbl.setText(_translate("InstallDialog", "Install prerequisites"))
self.dl_optimizations_label.setText(_translate("InstallDialog", "Enable reordering")) self.dl_optimizations_label.setText(_translate("InstallDialog", "Enable reordering"))
self.force_download_label.setText(_translate("InstallDialog", "Force redownload")) self.force_download_label.setText(_translate("InstallDialog", "Force redownload"))
self.ignore_space_label.setText(_translate("InstallDialog", "Ignore free space")) self.ignore_space_label.setText(_translate("InstallDialog", "Ignore free space"))

View file

@ -185,14 +185,14 @@
</layout> </layout>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="install_preqs_lbl"> <widget class="QLabel" name="install_prereqs_lbl">
<property name="text"> <property name="text">
<string>Install prerequisites</string> <string>Install prerequisites</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QCheckBox" name="install_preqs_check"> <widget class="QCheckBox" name="install_prereqs_check">
<property name="font"> <property name="font">
<font> <font>
<italic>true</italic> <italic>true</italic>

View file

@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_InstallDialogAdvanced(object): class Ui_InstallDialogAdvanced(object):
def setupUi(self, InstallDialogAdvanced): def setupUi(self, InstallDialogAdvanced):
InstallDialogAdvanced.setObjectName("InstallDialogAdvanced") InstallDialogAdvanced.setObjectName("InstallDialogAdvanced")
InstallDialogAdvanced.resize(359, 208) InstallDialogAdvanced.resize(379, 208)
self.install_dialog_advanced_layout = QtWidgets.QFormLayout(InstallDialogAdvanced) self.install_dialog_advanced_layout = QtWidgets.QFormLayout(InstallDialogAdvanced)
self.install_dialog_advanced_layout.setObjectName("install_dialog_advanced_layout") self.install_dialog_advanced_layout.setObjectName("install_dialog_advanced_layout")
self.max_workers_label = QtWidgets.QLabel(InstallDialogAdvanced) self.max_workers_label = QtWidgets.QLabel(InstallDialogAdvanced)
@ -63,17 +63,17 @@ class Ui_InstallDialogAdvanced(object):
self.max_memory_info_label.setObjectName("max_memory_info_label") self.max_memory_info_label.setObjectName("max_memory_info_label")
self.max_memory_layout.addWidget(self.max_memory_info_label) self.max_memory_layout.addWidget(self.max_memory_info_label)
self.install_dialog_advanced_layout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.max_memory_layout) self.install_dialog_advanced_layout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.max_memory_layout)
self.install_preqs_lbl = QtWidgets.QLabel(InstallDialogAdvanced) self.install_prereqs_lbl = QtWidgets.QLabel(InstallDialogAdvanced)
self.install_preqs_lbl.setObjectName("install_preqs_lbl") self.install_prereqs_lbl.setObjectName("install_prereqs_lbl")
self.install_dialog_advanced_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.install_preqs_lbl) self.install_dialog_advanced_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.install_prereqs_lbl)
self.install_preqs_check = QtWidgets.QCheckBox(InstallDialogAdvanced) self.install_prereqs_check = QtWidgets.QCheckBox(InstallDialogAdvanced)
font = QtGui.QFont() font = QtGui.QFont()
font.setItalic(True) font.setItalic(True)
self.install_preqs_check.setFont(font) self.install_prereqs_check.setFont(font)
self.install_preqs_check.setText("") self.install_prereqs_check.setText("")
self.install_preqs_check.setChecked(False) self.install_prereqs_check.setChecked(False)
self.install_preqs_check.setObjectName("install_preqs_check") self.install_prereqs_check.setObjectName("install_prereqs_check")
self.install_dialog_advanced_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.install_preqs_check) self.install_dialog_advanced_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.install_prereqs_check)
self.dl_optimizations_label = QtWidgets.QLabel(InstallDialogAdvanced) self.dl_optimizations_label = QtWidgets.QLabel(InstallDialogAdvanced)
self.dl_optimizations_label.setObjectName("dl_optimizations_label") self.dl_optimizations_label.setObjectName("dl_optimizations_label")
self.install_dialog_advanced_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.dl_optimizations_label) self.install_dialog_advanced_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.dl_optimizations_label)
@ -119,7 +119,7 @@ class Ui_InstallDialogAdvanced(object):
self.max_memory_label.setText(_translate("InstallDialogAdvanced", "Max shared memory")) self.max_memory_label.setText(_translate("InstallDialogAdvanced", "Max shared memory"))
self.max_memory_spin.setSuffix(_translate("InstallDialogAdvanced", "MiB")) self.max_memory_spin.setSuffix(_translate("InstallDialogAdvanced", "MiB"))
self.max_memory_info_label.setText(_translate("InstallDialogAdvanced", "Less is slower (0: Default)")) self.max_memory_info_label.setText(_translate("InstallDialogAdvanced", "Less is slower (0: Default)"))
self.install_preqs_lbl.setText(_translate("InstallDialogAdvanced", "Install prerequisites")) self.install_prereqs_lbl.setText(_translate("InstallDialogAdvanced", "Install prerequisites"))
self.dl_optimizations_label.setText(_translate("InstallDialogAdvanced", "Enable reordering")) self.dl_optimizations_label.setText(_translate("InstallDialogAdvanced", "Enable reordering"))
self.force_download_label.setText(_translate("InstallDialogAdvanced", "Force redownload")) self.force_download_label.setText(_translate("InstallDialogAdvanced", "Force redownload"))
self.ignore_space_label.setText(_translate("InstallDialogAdvanced", "Ignore free space")) self.ignore_space_label.setText(_translate("InstallDialogAdvanced", "Ignore free space"))

View file

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>359</width> <width>379</width>
<height>208</height> <height>208</height>
</rect> </rect>
</property> </property>
@ -102,14 +102,14 @@
</layout> </layout>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="install_preqs_lbl"> <widget class="QLabel" name="install_prereqs_lbl">
<property name="text"> <property name="text">
<string>Install prerequisites</string> <string>Install prerequisites</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QCheckBox" name="install_preqs_check"> <widget class="QCheckBox" name="install_prereqs_check">
<property name="font"> <property name="font">
<font> <font>
<italic>true</italic> <italic>true</italic>

View file

@ -14,12 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_BrowserLogin(object): class Ui_BrowserLogin(object):
def setupUi(self, BrowserLogin): def setupUi(self, BrowserLogin):
BrowserLogin.setObjectName("BrowserLogin") BrowserLogin.setObjectName("BrowserLogin")
BrowserLogin.resize(400, 200) BrowserLogin.resize(182, 210)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(BrowserLogin.sizePolicy().hasHeightForWidth())
BrowserLogin.setSizePolicy(sizePolicy)
BrowserLogin.setWindowTitle("BrowserLogin") BrowserLogin.setWindowTitle("BrowserLogin")
self.browser_layout = QtWidgets.QGridLayout(BrowserLogin) self.browser_layout = QtWidgets.QGridLayout(BrowserLogin)
self.browser_layout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint) self.browser_layout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
@ -78,7 +73,7 @@ class Ui_BrowserLogin(object):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
self.open_button.setText(_translate("BrowserLogin", "Open Browser")) self.open_button.setText(_translate("BrowserLogin", "Open Browser"))
self.title_label.setText(_translate("BrowserLogin", "Login through browser")) self.title_label.setText(_translate("BrowserLogin", "Login through browser"))
self.info_label.setText(_translate("BrowserLogin", "Click the button to open the login page in a browser or copy the link and paste it in a browser. After logging in, copy the SID code in the input above.")) self.info_label.setText(_translate("BrowserLogin", "Click the button to open the login page in a browser or copy the link and paste it in a browser. After logging in, copy the <b><code>authorizationCode</code></b> in the input above."))
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>246</width> <width>182</width>
<height>184</height> <height>210</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -94,7 +94,7 @@
</font> </font>
</property> </property>
<property name="text"> <property name="text">
<string>Click the button to open the login page in a browser or copy the link and paste it in a browser. After logging in, copy the SID code in the input above.</string> <string>Click the button to open the login page in a browser or copy the link and paste it in a browser. After logging in, copy the &lt;b&gt;&lt;code&gt;authorizationCode&lt;/code&gt;&lt;/b&gt; in the input above.</string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>

View file

@ -14,12 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_ImportLogin(object): class Ui_ImportLogin(object):
def setupUi(self, ImportLogin): def setupUi(self, ImportLogin):
ImportLogin.setObjectName("ImportLogin") ImportLogin.setObjectName("ImportLogin")
ImportLogin.resize(400, 200) ImportLogin.resize(242, 120)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(ImportLogin.sizePolicy().hasHeightForWidth())
ImportLogin.setSizePolicy(sizePolicy)
ImportLogin.setWindowTitle("ImportLogin") ImportLogin.setWindowTitle("ImportLogin")
self.import_layout = QtWidgets.QGridLayout(ImportLogin) self.import_layout = QtWidgets.QGridLayout(ImportLogin)
self.import_layout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint) self.import_layout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
@ -51,6 +46,7 @@ class Ui_ImportLogin(object):
font.setItalic(True) font.setItalic(True)
self.status_label.setFont(font) self.status_label.setFont(font)
self.status_label.setText("") self.status_label.setText("")
self.status_label.setWordWrap(True)
self.status_label.setObjectName("status_label") self.status_label.setObjectName("status_label")
self.import_layout.addWidget(self.status_label, 2, 1, 1, 2) self.import_layout.addWidget(self.status_label, 2, 1, 1, 2)
self.info_label = QtWidgets.QLabel(ImportLogin) self.info_label = QtWidgets.QLabel(ImportLogin)
@ -68,7 +64,7 @@ class Ui_ImportLogin(object):
def retranslateUi(self, ImportLogin): def retranslateUi(self, ImportLogin):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
self.prefix_label.setText(_translate("ImportLogin", "Select path")) self.prefix_label.setText(_translate("ImportLogin", "Select prefix"))
self.title_label.setText(_translate("ImportLogin", "Import existing session from EGL")) self.title_label.setText(_translate("ImportLogin", "Import existing session from EGL"))
self.prefix_tool.setText(_translate("ImportLogin", "Browse")) self.prefix_tool.setText(_translate("ImportLogin", "Browse"))
self.info_label.setText(_translate("ImportLogin", "You will get logged out from EGL in the process.")) self.info_label.setText(_translate("ImportLogin", "You will get logged out from EGL in the process."))

View file

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>235</width> <width>242</width>
<height>120</height> <height>120</height>
</rect> </rect>
</property> </property>
@ -20,7 +20,7 @@
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="prefix_label"> <widget class="QLabel" name="prefix_label">
<property name="text"> <property name="text">
<string>Select path</string> <string>Select prefix</string>
</property> </property>
</widget> </widget>
</item> </item>

View file

@ -1,38 +1,38 @@
from typing import Callable from typing import Callable, Optional
from legendary.core import LegendaryCore from legendary.core import LegendaryCore
from legendary.utils.config import LGDConf from legendary.utils.config import LGDConf
config: LGDConf = None _config: Optional[LGDConf] = None
save_config: Callable[[], None] = None _save_config: Optional[Callable[[], None]] = None
def init_config_handler(core: LegendaryCore): def init_config_handler(core: LegendaryCore):
global config, save_config global _config, _save_config
config = core.lgd.config _config = core.lgd.config
save_config = core.lgd.save_config _save_config = core.lgd.save_config
def add_option(app_name: str, option: str, value: str): def add_option(app_name: str, option: str, value: str):
value = value.replace("%%", "%").replace("%", "%%") value = value.replace("%%", "%").replace("%", "%%")
if not config.has_section(app_name): if not _config.has_section(app_name):
config[app_name] = {} _config[app_name] = {}
config.set(app_name, option, value) _config.set(app_name, option, value)
save_config() _save_config()
def remove_option(app_name, option): def remove_option(app_name, option):
if config.has_option(app_name, option): if _config.has_option(app_name, option):
config.remove_option(app_name, option) _config.remove_option(app_name, option)
if config.has_section(app_name) and not config[app_name]: if _config.has_section(app_name) and not _config[app_name]:
config.remove_section(app_name) _config.remove_section(app_name)
save_config() _save_config()
def remove_section(app_name): def remove_section(app_name):
if config.has_section(app_name): if _config.has_section(app_name):
config.remove_section(app_name) _config.remove_section(app_name)
save_config() _save_config()

View file

@ -54,7 +54,6 @@ class IndicatorReasons:
class IndicatorLineEdit(QWidget): class IndicatorLineEdit(QWidget):
textChanged = pyqtSignal(str) textChanged = pyqtSignal(str)
is_valid = False
reasons = IndicatorReasons() reasons = IndicatorReasons()
def __init__( def __init__(
@ -97,9 +96,10 @@ class IndicatorLineEdit(QWidget):
layout.addWidget(self.indicator_label) layout.addWidget(self.indicator_label)
if not placeholder: if not placeholder:
_translate = QCoreApplication.translate _translate = QCoreApplication.instance().translate
self.line_edit.setPlaceholderText(_translate(self.__class__.__name__, "Default")) self.line_edit.setPlaceholderText(_translate(self.__class__.__name__, "Default"))
self.is_valid = False
self.edit_func = edit_func self.edit_func = edit_func
self.save_func = save_func self.save_func = save_func
self.line_edit.textChanged.connect(self.__edit) self.line_edit.textChanged.connect(self.__edit)
@ -107,7 +107,7 @@ class IndicatorLineEdit(QWidget):
self.line_edit.textChanged.connect(self.__save) self.line_edit.textChanged.connect(self.__save)
# lk: this can be placed here to trigger __edit # lk: this can be placed here to trigger __edit
# lk: it going to save the input again if it is valid which # lk: it is going to save the input again if it is valid which
# lk: is ok to do given the checks don't misbehave (they shouldn't) # lk: is ok to do given the checks don't misbehave (they shouldn't)
# lk: however it is going to edit any "understood" bad input to good input # lk: however it is going to edit any "understood" bad input to good input
# lk: and we might not want that (but the validity check reports on the edited string) # lk: and we might not want that (but the validity check reports on the edited string)
@ -185,9 +185,6 @@ class PathEditIconProvider(QFileIconProvider):
class PathEdit(IndicatorLineEdit): class PathEdit(IndicatorLineEdit):
completer = QCompleter()
compl_model = QFileSystemModel()
def __init__( def __init__(
self, self,
path: str = "", path: str = "",
@ -200,6 +197,9 @@ class PathEdit(IndicatorLineEdit):
horiz_policy: QSizePolicy = QSizePolicy.Expanding, horiz_policy: QSizePolicy = QSizePolicy.Expanding,
parent=None, parent=None,
): ):
self.completer = QCompleter()
self.compl_model = QFileSystemModel()
try: try:
self.compl_model.setOptions( self.compl_model.setOptions(
QFileSystemModel.DontWatchForChanges QFileSystemModel.DontWatchForChanges
@ -230,7 +230,7 @@ class PathEdit(IndicatorLineEdit):
layout = self.layout() layout = self.layout()
layout.addWidget(self.path_select) layout.addWidget(self.path_select)
_translate = QCoreApplication.translate _translate = QCoreApplication.instance().translate
self.path_select.setText(_translate("PathEdit", "Browse...")) self.path_select.setText(_translate("PathEdit", "Browse..."))
self.type_filter = type_filter self.type_filter = type_filter
@ -414,7 +414,7 @@ class SelectViewWidget(QWidget):
class ImageLabel(QLabel): class ImageLabel(QLabel):
image = None image = None
img_size = None img_size = None
name = str() name = ""
def __init__(self): def __init__(self):
super(ImageLabel, self).__init__() super(ImageLabel, self).__init__()

View file

@ -20,7 +20,6 @@ class RareApp(QApplication):
def __init__(self): def __init__(self):
super(RareApp, self).__init__(sys.argv) super(RareApp, self).__init__(sys.argv)
self.setQuitOnLastWindowClosed(False) self.setQuitOnLastWindowClosed(False)
self.core = LegendaryCore()
if hasattr(Qt, "AA_UseHighDpiPixmaps"): if hasattr(Qt, "AA_UseHighDpiPixmaps"):
self.setAttribute(Qt.AA_UseHighDpiPixmaps) self.setAttribute(Qt.AA_UseHighDpiPixmaps)
@ -36,7 +35,6 @@ class RareApp(QApplication):
# lk: this is a bit silly but serves well until we have a class # lk: this is a bit silly but serves well until we have a class
# lk: store the default qt style name from the system on startup as a property for later reference # lk: store the default qt style name from the system on startup as a property for later reference
self.setProperty("rareDefaultQtStyle", self.style().objectName()) self.setProperty("rareDefaultQtStyle", self.style().objectName())
if ( if (
self.settings.value("color_scheme", None) is None self.settings.value("color_scheme", None) is None
and self.settings.value("style_sheet", None) is None and self.settings.value("style_sheet", None) is None