2022-05-04 09:02:28 +12:00
|
|
|
import json
|
2022-06-09 07:14:48 +12:00
|
|
|
import logging
|
2023-01-09 09:02:58 +13:00
|
|
|
import platform
|
|
|
|
import subprocess
|
2022-05-04 09:02:28 +12:00
|
|
|
import sys
|
|
|
|
import time
|
2022-06-09 07:14:48 +12:00
|
|
|
import traceback
|
2022-05-19 09:41:48 +12:00
|
|
|
from argparse import Namespace
|
2022-05-04 09:02:28 +12:00
|
|
|
from logging import getLogger
|
2023-04-08 09:33:00 +12:00
|
|
|
from signal import signal, SIGINT, SIGTERM, strsignal
|
2022-06-27 10:53:28 +12:00
|
|
|
from typing import Union, Optional
|
2022-05-04 09:02:28 +12:00
|
|
|
|
2022-10-26 11:33:49 +13:00
|
|
|
from PyQt5.QtCore import QObject, QProcess, pyqtSignal, QUrl, QRunnable, QThreadPool, QSettings, Qt
|
2022-05-14 08:11:24 +12:00
|
|
|
from PyQt5.QtGui import QDesktopServices
|
2022-05-04 09:02:28 +12:00
|
|
|
from PyQt5.QtNetwork import QLocalServer, QLocalSocket
|
2022-10-26 11:33:49 +13:00
|
|
|
from PyQt5.QtWidgets import QApplication
|
2023-03-19 04:14:01 +13:00
|
|
|
from legendary.models.game import SaveGameStatus
|
2022-05-04 09:02:28 +12:00
|
|
|
|
2022-09-05 06:40:41 +12:00
|
|
|
from rare.lgndr.core import LegendaryCore
|
2023-01-11 08:40:57 +13:00
|
|
|
from rare.models.launcher import ErrorModel, Actions, FinishedModel, BaseModel, StateChangedModel
|
2023-03-06 09:21:49 +13:00
|
|
|
from rare.widgets.rare_app import RareApp, RareAppException
|
2022-07-12 06:03:39 +12:00
|
|
|
from .console import Console
|
2022-06-09 07:14:48 +12:00
|
|
|
from .lgd_helper import get_launch_args, InitArgs, get_configured_process, LaunchArgs, GameArgsError
|
2023-02-28 11:59:57 +13:00
|
|
|
from ..models.base_game import RareGameSlim
|
2023-03-03 11:39:03 +13:00
|
|
|
from rare.components.dialogs.cloud_save_dialog import CloudSaveDialog
|
2023-02-13 05:13:05 +13:00
|
|
|
|
2022-09-08 23:58:11 +12:00
|
|
|
logger = logging.getLogger("RareLauncher")
|
|
|
|
|
2023-01-09 09:02:58 +13:00
|
|
|
DETACHED_APP_NAMES = [
|
|
|
|
"0a2d9f6403244d12969e11da6713137b"
|
|
|
|
]
|
|
|
|
|
2022-06-09 07:14:48 +12:00
|
|
|
|
|
|
|
class PreLaunchThread(QRunnable):
|
|
|
|
class Signals(QObject):
|
|
|
|
ready_to_launch = pyqtSignal(LaunchArgs)
|
|
|
|
started_pre_launch_command = pyqtSignal()
|
|
|
|
pre_launch_command_finished = pyqtSignal(int) # exit_code
|
|
|
|
error_occurred = pyqtSignal(str)
|
|
|
|
|
2023-03-03 11:39:03 +13:00
|
|
|
def __init__(self, core: LegendaryCore, args: InitArgs, rgame: RareGameSlim, sync_action=None):
|
2022-06-09 07:14:48 +12:00
|
|
|
super(PreLaunchThread, self).__init__()
|
2022-09-05 06:40:41 +12:00
|
|
|
self.core = core
|
2022-06-09 07:14:48 +12:00
|
|
|
self.app_name = args.app_name
|
|
|
|
self.signals = self.Signals()
|
2022-10-01 11:44:37 +13:00
|
|
|
self.args = args
|
2023-03-03 11:39:03 +13:00
|
|
|
self.rgame = rgame
|
|
|
|
self.sync_action = sync_action
|
2022-06-09 07:14:48 +12:00
|
|
|
|
|
|
|
def run(self) -> None:
|
2023-03-03 11:39:03 +13:00
|
|
|
logger.info(f"Sync action: {self.sync_action}")
|
|
|
|
if self.sync_action == CloudSaveDialog.UPLOAD:
|
|
|
|
self.rgame.upload_saves(False)
|
|
|
|
elif self.sync_action == CloudSaveDialog.DOWNLOAD:
|
|
|
|
self.rgame.download_saves(False)
|
|
|
|
else:
|
|
|
|
logger.info("No sync action")
|
|
|
|
|
2022-10-01 11:44:37 +13:00
|
|
|
args = self.prepare_launch(self.args)
|
2022-06-09 07:14:48 +12:00
|
|
|
if not args:
|
|
|
|
return
|
|
|
|
self.signals.ready_to_launch.emit(args)
|
|
|
|
|
2022-10-01 11:44:37 +13:00
|
|
|
def prepare_launch(self, args: InitArgs) -> Union[LaunchArgs, None]:
|
2022-06-09 07:14:48 +12:00
|
|
|
try:
|
2022-10-01 11:44:37 +13:00
|
|
|
args = get_launch_args(self.core, args)
|
2022-08-12 07:09:21 +12:00
|
|
|
except Exception as e:
|
2022-06-09 07:14:48 +12:00
|
|
|
self.signals.error_occurred.emit(str(e))
|
|
|
|
return None
|
|
|
|
if not args:
|
|
|
|
return None
|
|
|
|
|
|
|
|
if args.pre_launch_command:
|
|
|
|
proc = get_configured_process()
|
|
|
|
proc.setProcessEnvironment(args.env)
|
|
|
|
self.signals.started_pre_launch_command.emit()
|
|
|
|
proc.start(args.pre_launch_command[0], args.pre_launch_command[1:])
|
|
|
|
if args.pre_launch_wait:
|
|
|
|
proc.waitForFinished(-1)
|
|
|
|
return args
|
2022-05-19 09:41:48 +12:00
|
|
|
|
2022-05-04 09:02:28 +12:00
|
|
|
|
2023-03-03 11:39:03 +13:00
|
|
|
class SyncCheckWorker(QRunnable):
|
|
|
|
class Signals(QObject):
|
|
|
|
sync_state_ready = pyqtSignal()
|
|
|
|
error_occurred = pyqtSignal(str)
|
|
|
|
|
|
|
|
def __init__(self, core: LegendaryCore, rgame: RareGameSlim):
|
|
|
|
super().__init__()
|
|
|
|
self.signals = self.Signals()
|
|
|
|
self.core = core
|
|
|
|
self.rgame = rgame
|
|
|
|
|
|
|
|
def run(self) -> None:
|
|
|
|
try:
|
|
|
|
self.rgame.update_saves()
|
|
|
|
except Exception as e:
|
|
|
|
self.signals.error_occurred.emit(str(e))
|
|
|
|
return
|
|
|
|
self.signals.sync_state_ready.emit()
|
|
|
|
|
|
|
|
|
2023-03-06 09:21:49 +13:00
|
|
|
class RareLauncherException(RareAppException):
|
|
|
|
def __init__(self, app: 'RareLauncher', args: Namespace, parent=None):
|
|
|
|
super(RareLauncherException, self).__init__(parent=parent)
|
|
|
|
self.__app = app
|
|
|
|
self.__args = args
|
|
|
|
|
|
|
|
def _handler(self, exc_type, exc_value, exc_tb) -> bool:
|
|
|
|
try:
|
|
|
|
self.__app.send_message(ErrorModel(
|
|
|
|
app_name=self.__args.app_name,
|
|
|
|
action=Actions.error,
|
|
|
|
error_string="".join(traceback.format_exception(exc_type, exc_value, exc_tb))
|
|
|
|
))
|
|
|
|
except RuntimeError:
|
|
|
|
pass
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
class RareLauncher(RareApp):
|
2022-05-04 09:02:28 +12:00
|
|
|
game_process: QProcess
|
|
|
|
server: QLocalServer
|
2022-06-27 10:53:28 +12:00
|
|
|
socket: Optional[QLocalSocket] = None
|
2022-05-04 09:02:28 +12:00
|
|
|
exit_app = pyqtSignal()
|
2022-09-08 22:33:46 +12:00
|
|
|
console: Optional[Console] = None
|
2022-05-04 09:02:28 +12:00
|
|
|
success: bool = True
|
|
|
|
|
2023-03-03 11:39:03 +13:00
|
|
|
def __init__(self, args: InitArgs):
|
2022-09-08 23:58:11 +12:00
|
|
|
log_file = f"Rare_Launcher_{args.app_name}" + "_{0}.log"
|
2023-03-06 09:21:49 +13:00
|
|
|
super(RareLauncher, self).__init__(args, log_file)
|
|
|
|
self._hook.deleteLater()
|
|
|
|
self._hook = RareLauncherException(self, args, self)
|
2022-05-04 09:02:28 +12:00
|
|
|
self.game_process = QProcess()
|
2022-09-08 10:27:37 +12:00
|
|
|
self.app_name = args.app_name
|
2022-05-04 09:02:28 +12:00
|
|
|
self.logger = getLogger(self.app_name)
|
2022-09-05 06:40:41 +12:00
|
|
|
self.core = LegendaryCore()
|
2023-02-13 05:13:05 +13:00
|
|
|
self.args = args
|
|
|
|
|
2023-04-08 05:37:27 +12:00
|
|
|
self.no_sync_on_exit = False
|
|
|
|
|
2023-02-28 11:59:57 +13:00
|
|
|
game = self.core.get_game(self.app_name)
|
|
|
|
self.rgame = RareGameSlim(self.core, game)
|
2022-05-04 09:02:28 +12:00
|
|
|
|
2022-06-24 07:26:08 +12:00
|
|
|
lang = self.settings.value("language", self.core.language_code, type=str)
|
|
|
|
self.load_translator(lang)
|
|
|
|
|
|
|
|
if QSettings().value("show_console", False, bool):
|
|
|
|
self.console = Console()
|
|
|
|
self.console.show()
|
|
|
|
|
2022-05-04 09:02:28 +12:00
|
|
|
self.server = QLocalServer()
|
2022-05-19 09:41:48 +12:00
|
|
|
ret = self.server.listen(f"rare_{self.app_name}")
|
2022-05-04 09:02:28 +12:00
|
|
|
if not ret:
|
2022-06-09 07:14:48 +12:00
|
|
|
self.logger.error(self.server.errorString())
|
|
|
|
self.logger.info("Server is running")
|
2022-05-04 09:02:28 +12:00
|
|
|
self.server.close()
|
|
|
|
self.success = False
|
|
|
|
return
|
|
|
|
self.server.newConnection.connect(self.new_server_connection)
|
|
|
|
self.game_process.finished.connect(self.game_finished)
|
2022-06-12 02:59:53 +12:00
|
|
|
self.game_process.errorOccurred.connect(
|
|
|
|
lambda err: self.error_occurred(self.game_process.errorString()))
|
2022-06-27 10:53:28 +12:00
|
|
|
if self.console:
|
|
|
|
self.game_process.readyReadStandardOutput.connect(
|
|
|
|
lambda: self.console.log(
|
2022-08-01 11:22:37 +12:00
|
|
|
self.game_process.readAllStandardOutput().data().decode("utf-8", "ignore")
|
2022-07-12 06:03:39 +12:00
|
|
|
)
|
|
|
|
)
|
|
|
|
self.game_process.readyReadStandardError.connect(
|
2022-09-09 00:32:54 +12:00
|
|
|
lambda: self.console.error(
|
2022-07-12 06:03:39 +12:00
|
|
|
self.game_process.readAllStandardError().data().decode("utf-8", "ignore")
|
2022-06-27 10:53:28 +12:00
|
|
|
)
|
2022-06-24 07:26:08 +12:00
|
|
|
)
|
2022-07-12 07:02:57 +12:00
|
|
|
self.console.term.connect(lambda: self.game_process.terminate())
|
|
|
|
self.console.kill.connect(lambda: self.game_process.kill())
|
2022-06-24 07:26:08 +12:00
|
|
|
|
2022-05-04 09:02:28 +12:00
|
|
|
self.start_time = time.time()
|
|
|
|
|
|
|
|
def new_server_connection(self):
|
|
|
|
if self.socket is not None:
|
2022-06-12 02:59:53 +12:00
|
|
|
try:
|
|
|
|
self.socket.disconnectFromServer()
|
|
|
|
except RuntimeError:
|
|
|
|
pass
|
2022-05-04 09:02:28 +12:00
|
|
|
self.logger.info("New connection")
|
|
|
|
self.socket = self.server.nextPendingConnection()
|
2022-06-27 10:53:28 +12:00
|
|
|
self.socket.disconnected.connect(self.socket_disconnected)
|
2022-05-04 09:02:28 +12:00
|
|
|
self.socket.flush()
|
|
|
|
|
2022-06-27 10:53:28 +12:00
|
|
|
def socket_disconnected(self):
|
|
|
|
self.logger.info("Server disconnected")
|
|
|
|
self.socket.deleteLater()
|
|
|
|
self.socket = None
|
|
|
|
|
2022-06-09 07:14:48 +12:00
|
|
|
def send_message(self, message: BaseModel):
|
2022-05-04 09:02:28 +12:00
|
|
|
if self.socket:
|
2022-06-09 07:14:48 +12:00
|
|
|
self.socket.write(json.dumps(message.__dict__).encode("utf-8"))
|
2022-05-04 09:02:28 +12:00
|
|
|
self.socket.flush()
|
|
|
|
else:
|
2022-06-09 07:14:48 +12:00
|
|
|
self.logger.error("Can't send message")
|
2022-05-04 09:02:28 +12:00
|
|
|
|
2023-03-19 04:14:01 +13:00
|
|
|
def check_saves_finished(self, exit_code: int):
|
|
|
|
self.rgame.signals.widget.update.connect(lambda: self.on_exit(exit_code))
|
|
|
|
|
|
|
|
state, (dt_local, dt_remote) = self.rgame.save_game_state
|
2023-04-08 05:37:27 +12:00
|
|
|
|
|
|
|
if state == SaveGameStatus.LOCAL_NEWER and not self.no_sync_on_exit:
|
2023-03-19 04:14:01 +13:00
|
|
|
action = CloudSaveDialog.UPLOAD
|
|
|
|
else:
|
|
|
|
action = CloudSaveDialog(self.rgame.igame, dt_local, dt_remote).get_action()
|
2023-04-08 05:37:27 +12:00
|
|
|
|
2023-03-19 04:14:01 +13:00
|
|
|
if action == CloudSaveDialog.UPLOAD:
|
2023-04-08 05:37:27 +12:00
|
|
|
if self.console:
|
|
|
|
self.console.log("upload saves...")
|
2023-03-19 04:14:01 +13:00
|
|
|
self.rgame.upload_saves()
|
|
|
|
elif action == CloudSaveDialog.DOWNLOAD:
|
2023-04-08 05:37:27 +12:00
|
|
|
if self.console:
|
|
|
|
self.console.log("Download saves...")
|
2023-03-19 04:14:01 +13:00
|
|
|
self.rgame.download_saves()
|
2023-04-08 05:37:27 +12:00
|
|
|
else:
|
|
|
|
self.on_exit(exit_code)
|
|
|
|
|
2023-03-19 04:14:01 +13:00
|
|
|
|
2022-05-04 09:02:28 +12:00
|
|
|
def game_finished(self, exit_code):
|
2023-03-19 04:14:01 +13:00
|
|
|
self.logger.info("Game finished")
|
|
|
|
|
2023-03-20 08:23:44 +13:00
|
|
|
if self.rgame.auto_sync_saves:
|
2023-03-19 04:14:01 +13:00
|
|
|
self.check_saves_finished(exit_code)
|
|
|
|
else:
|
|
|
|
self.on_exit(exit_code)
|
|
|
|
|
|
|
|
def on_exit(self, exit_code: int):
|
2022-09-09 00:32:54 +12:00
|
|
|
if self.console:
|
|
|
|
self.console.on_process_exit(self.core.get_game(self.app_name).app_title, exit_code)
|
2023-03-19 04:14:01 +13:00
|
|
|
|
2022-05-04 09:02:28 +12:00
|
|
|
self.send_message(
|
2022-06-09 07:14:48 +12:00
|
|
|
FinishedModel(
|
|
|
|
action=Actions.finished,
|
|
|
|
app_name=self.app_name,
|
|
|
|
exit_code=exit_code,
|
|
|
|
playtime=int(time.time() - self.start_time)
|
|
|
|
)
|
|
|
|
|
2022-05-04 09:02:28 +12:00
|
|
|
)
|
2022-06-12 02:59:53 +12:00
|
|
|
self.stop()
|
2022-05-04 09:02:28 +12:00
|
|
|
|
2022-06-09 07:14:48 +12:00
|
|
|
def launch_game(self, args: LaunchArgs):
|
|
|
|
# should never happen
|
|
|
|
if not args:
|
|
|
|
self.stop()
|
|
|
|
return
|
2022-06-24 07:26:08 +12:00
|
|
|
if self.console:
|
|
|
|
self.console.set_env(args.env)
|
2022-06-09 07:14:48 +12:00
|
|
|
self.start_time = time.time()
|
|
|
|
|
|
|
|
if args.is_origin_game:
|
|
|
|
QDesktopServices.openUrl(QUrl(args.executable))
|
2022-06-19 09:55:35 +12:00
|
|
|
self.stop() # stop because it is no subprocess
|
2022-06-09 07:14:48 +12:00
|
|
|
return
|
|
|
|
|
2022-06-12 02:59:53 +12:00
|
|
|
if args.cwd:
|
|
|
|
self.game_process.setWorkingDirectory(args.cwd)
|
2022-06-20 08:07:21 +12:00
|
|
|
self.game_process.setProcessEnvironment(args.env)
|
2022-08-01 11:22:37 +12:00
|
|
|
# send start message after process started
|
|
|
|
self.game_process.started.connect(lambda: self.send_message(
|
2022-06-09 07:14:48 +12:00
|
|
|
StateChangedModel(
|
|
|
|
action=Actions.state_update, app_name=self.app_name,
|
|
|
|
new_state=StateChangedModel.States.started
|
|
|
|
)
|
2022-08-01 11:22:37 +12:00
|
|
|
))
|
2023-01-09 09:02:58 +13:00
|
|
|
if self.app_name in DETACHED_APP_NAMES and platform.system() == "Windows":
|
|
|
|
self.game_process.deleteLater()
|
|
|
|
subprocess.Popen([args.executable] + args.args, cwd=args.cwd,
|
|
|
|
env={i: args.env.value(i) for i in args.env.keys()})
|
|
|
|
if self.console:
|
|
|
|
self.console.log("Launching game detached")
|
|
|
|
self.stop()
|
|
|
|
return
|
2023-02-13 05:13:05 +13:00
|
|
|
if self.args.dry_run:
|
|
|
|
logger.info("Dry run activated")
|
|
|
|
if self.console:
|
|
|
|
self.console.log(f"{args.executable} {' '.join(args.args)}")
|
|
|
|
self.console.log(f"Do not start {self.app_name}")
|
|
|
|
self.console.accept_close = True
|
|
|
|
print(args.executable, " ".join(args.args))
|
|
|
|
self.stop()
|
2023-02-28 11:59:57 +13:00
|
|
|
return
|
2022-08-01 11:22:37 +12:00
|
|
|
self.game_process.start(args.executable, args.args)
|
2022-06-09 07:14:48 +12:00
|
|
|
|
|
|
|
def error_occurred(self, error_str: str):
|
|
|
|
self.logger.warning(error_str)
|
2022-09-09 00:32:54 +12:00
|
|
|
if self.console:
|
|
|
|
self.console.on_process_exit(self.core.get_game(self.app_name).app_title, error_str)
|
2022-06-09 07:14:48 +12:00
|
|
|
self.send_message(ErrorModel(
|
|
|
|
error_string=error_str, app_name=self.app_name,
|
|
|
|
action=Actions.error)
|
|
|
|
)
|
|
|
|
self.stop()
|
|
|
|
|
2023-03-03 11:39:03 +13:00
|
|
|
def start_prepare(self, sync_action=None):
|
|
|
|
worker = PreLaunchThread(self.core, self.args, self.rgame, sync_action)
|
|
|
|
worker.signals.ready_to_launch.connect(self.launch_game)
|
|
|
|
worker.signals.error_occurred.connect(self.error_occurred)
|
|
|
|
# worker.signals.started_pre_launch_command(None)
|
|
|
|
|
|
|
|
QThreadPool.globalInstance().start(worker)
|
|
|
|
|
|
|
|
def sync_ready(self):
|
|
|
|
if self.rgame.is_save_up_to_date:
|
|
|
|
if self.console:
|
|
|
|
self.console.log("Sync worker ready. Sync not required")
|
|
|
|
self.start_prepare()
|
|
|
|
return
|
|
|
|
|
|
|
|
_, (dt_local, dt_remote) = self.rgame.save_game_state
|
|
|
|
dlg = CloudSaveDialog(self.rgame.igame, dt_local, dt_remote)
|
|
|
|
action = dlg.get_action()
|
2023-04-08 05:37:27 +12:00
|
|
|
if action == CloudSaveDialog.CANCEL:
|
|
|
|
self.no_sync_on_exit = True
|
2023-03-19 04:14:01 +13:00
|
|
|
if self.console:
|
|
|
|
if action == CloudSaveDialog.DOWNLOAD:
|
|
|
|
self.console.log("Downloading saves")
|
|
|
|
elif action == CloudSaveDialog.UPLOAD:
|
|
|
|
self.console.log("Uloading saves")
|
2023-03-03 11:39:03 +13:00
|
|
|
self.start_prepare(action)
|
|
|
|
|
2022-06-09 07:14:48 +12:00
|
|
|
def start(self, args: InitArgs):
|
2022-05-19 09:41:48 +12:00
|
|
|
if not args.offline:
|
|
|
|
try:
|
|
|
|
if not self.core.login():
|
|
|
|
raise ValueError("You are not logged in")
|
|
|
|
except ValueError:
|
|
|
|
# automatically launch offline if available
|
|
|
|
self.logger.error("Not logged in. Try to launch game offline")
|
2022-06-09 07:14:48 +12:00
|
|
|
args.offline = True
|
2022-05-19 09:41:48 +12:00
|
|
|
|
2023-03-03 11:47:10 +13:00
|
|
|
if not args.offline and self.rgame.auto_sync_saves:
|
2023-03-03 11:39:03 +13:00
|
|
|
logger.info("Start sync worker")
|
|
|
|
worker = SyncCheckWorker(self.core, self.rgame)
|
|
|
|
worker.signals.error_occurred.connect(self.error_occurred)
|
|
|
|
worker.signals.sync_state_ready.connect(self.sync_ready)
|
|
|
|
QThreadPool.globalInstance().start(worker)
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
self.start_prepare()
|
2022-06-09 07:14:48 +12:00
|
|
|
|
|
|
|
def stop(self):
|
2022-06-12 02:59:53 +12:00
|
|
|
self.logger.info("Stopping server")
|
2022-08-12 07:09:21 +12:00
|
|
|
try:
|
|
|
|
self.server.close()
|
|
|
|
self.server.deleteLater()
|
|
|
|
except RuntimeError:
|
|
|
|
pass
|
2022-09-17 02:33:26 +12:00
|
|
|
self.processEvents()
|
|
|
|
if not self.console:
|
|
|
|
self.exit()
|
2023-01-09 09:02:58 +13:00
|
|
|
else:
|
|
|
|
self.console.on_process_exit(self.app_name, 0)
|
2022-05-04 09:02:28 +12:00
|
|
|
|
|
|
|
|
2022-05-19 09:41:48 +12:00
|
|
|
def start_game(args: Namespace):
|
|
|
|
args = InitArgs.from_argparse(args)
|
2022-06-09 07:14:48 +12:00
|
|
|
|
2022-10-26 11:33:49 +13:00
|
|
|
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
|
|
|
|
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
|
|
|
|
|
2023-03-06 09:21:49 +13:00
|
|
|
app = RareLauncher(args)
|
2022-08-01 11:22:37 +12:00
|
|
|
app.setQuitOnLastWindowClosed(True)
|
2022-06-09 07:14:48 +12:00
|
|
|
|
2023-04-08 09:33:00 +12:00
|
|
|
# This prevents ghost QLocalSockets, which block the name, which makes it unable to start
|
|
|
|
# No handling for SIGKILL
|
|
|
|
def sighandler(s, frame):
|
|
|
|
logger.info(f"{strsignal(s)} received. Stopping")
|
|
|
|
app.stop()
|
|
|
|
app.exit(1)
|
|
|
|
signal(SIGINT, sighandler)
|
|
|
|
signal(SIGTERM, sighandler)
|
|
|
|
|
2022-06-24 07:26:08 +12:00
|
|
|
if not app.success:
|
2022-05-04 09:02:28 +12:00
|
|
|
return
|
2022-06-24 07:26:08 +12:00
|
|
|
app.start(args)
|
2022-08-01 11:22:37 +12:00
|
|
|
# app.exit_app.connect(lambda: app.exit(0))
|
2022-05-14 08:11:24 +12:00
|
|
|
|
2022-08-01 11:22:37 +12:00
|
|
|
sys.exit(app.exec_())
|