From 533ce706e5f9e02729427f7f0df4ba987f452b6d Mon Sep 17 00:00:00 2001 From: Christian Heimlich Date: Fri, 18 Oct 2024 17:08:35 -0400 Subject: [PATCH] Greatly improve frontend/backend communication hygiene + logging --- CMakeLists.txt | 4 +- app/CMakeLists.txt | 6 +- app/src/command/c-download.cpp | 11 +- app/src/command/c-link.cpp | 18 +- app/src/command/c-link_linux.cpp | 7 +- app/src/command/c-link_win.cpp | 2 +- app/src/command/c-play.cpp | 27 +- app/src/command/c-play.h | 1 + app/src/command/c-prepare.cpp | 5 +- app/src/command/c-run.cpp | 3 +- app/src/command/c-share.cpp | 13 +- app/src/command/c-show.cpp | 7 +- app/src/command/c-update.cpp | 35 +-- app/src/command/c-update.h | 1 + app/src/command/command.cpp | 34 +-- app/src/command/command.h | 28 +- app/src/command/title-command.cpp | 31 ++- app/src/command/title-command.h | 3 + app/src/controller.cpp | 21 +- app/src/controller.h | 3 +- app/src/frontend/message.h | 14 - app/src/frontend/statusrelay.cpp | 192 +++++++------ app/src/frontend/statusrelay.h | 60 +++-- app/src/kernel/core.cpp | 330 ++++------------------- app/src/kernel/core.h | 134 +-------- app/src/kernel/directive.h | 160 +++++++++++ app/src/kernel/director.cpp | 187 +++++++++++++ app/src/kernel/director.h | 168 ++++++++++++ app/src/kernel/directorate.cpp | 24 ++ app/src/kernel/directorate.h | 62 +++++ app/src/kernel/driver.cpp | 57 +--- app/src/kernel/driver.h | 36 +-- app/src/main.cpp | 6 +- app/src/task/t-awaitdocker.cpp | 22 +- app/src/task/t-awaitdocker.h | 12 +- app/src/task/t-bideprocess.cpp | 18 +- app/src/task/t-bideprocess.h | 2 +- app/src/task/t-download.cpp | 41 +-- app/src/task/t-download.h | 2 +- app/src/task/t-exec.cpp | 37 +-- app/src/task/t-exec.h | 3 +- app/src/task/t-exec_linux.cpp | 12 +- app/src/task/t-exec_win.cpp | 4 +- app/src/task/t-extra.cpp | 8 +- app/src/task/t-extra.h | 2 +- app/src/task/t-extract.cpp | 8 +- app/src/task/t-extract.h | 2 +- app/src/task/t-generic.cpp | 10 +- app/src/task/t-generic.h | 2 +- app/src/task/t-message.cpp | 16 +- app/src/task/t-message.h | 2 +- app/src/task/t-mount.cpp | 21 +- app/src/task/t-mount.h | 5 +- app/src/task/t-sleep.cpp | 10 +- app/src/task/t-sleep.h | 2 +- app/src/task/task.cpp | 12 +- app/src/task/task.h | 25 +- app/src/tools/blockingprocessmanager.cpp | 12 +- app/src/tools/blockingprocessmanager.h | 12 +- app/src/tools/deferredprocessmanager.cpp | 33 +-- app/src/tools/deferredprocessmanager.h | 22 +- app/src/tools/mounter_game_server.cpp | 23 +- app/src/tools/mounter_game_server.h | 17 +- app/src/tools/mounter_qmp.cpp | 27 +- app/src/tools/mounter_qmp.h | 13 +- app/src/tools/mounter_router.cpp | 23 +- app/src/tools/mounter_router.h | 13 +- 67 files changed, 1196 insertions(+), 967 deletions(-) delete mode 100644 app/src/frontend/message.h create mode 100644 app/src/kernel/directive.h create mode 100644 app/src/kernel/director.cpp create mode 100644 app/src/kernel/director.h create mode 100644 app/src/kernel/directorate.cpp create mode 100644 app/src/kernel/directorate.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7055748..7d4b781 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,7 @@ endif() include(OB/FetchQx) ob_fetch_qx( - REF "b29e03cf42dfed65a5ecaf18ce192f3051f0e25f" + REF "0715db2b21a0cd02a71786184b3fe40bf9e38183" COMPONENTS ${CLIFP_QX_COMPONENTS} ) @@ -99,7 +99,7 @@ ob_fetch_quazip( # Fetch Neargye's Magic Enum include(OB/FetchMagicEnum) -ob_fetch_magicenum("v0.9.3") +ob_fetch_magicenum("v0.9.6") # Process Targets set(APP_TARGET_NAME ${PROJECT_NAMESPACE_LC}_${PROJECT_NAMESPACE_LC}) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index eca7181..902de0c 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -5,6 +5,11 @@ set(CLIFP_SOURCE kernel/buildinfo.h kernel/core.h kernel/core.cpp + kernel/directive.h + kernel/director.h + kernel/director.cpp + kernel/directorate.h + kernel/directorate.cpp kernel/driver.h kernel/driver.cpp kernel/errorstatus.h @@ -57,7 +62,6 @@ set(CLIFP_SOURCE tools/mounter_qmp.cpp tools/mounter_router.h tools/mounter_router.cpp - frontend/message.h frontend/statusrelay.h frontend/statusrelay.cpp controller.h diff --git a/app/src/command/c-download.cpp b/app/src/command/c-download.cpp index d921e68..6f1ef59 100644 --- a/app/src/command/c-download.cpp +++ b/app/src/command/c-download.cpp @@ -2,6 +2,7 @@ #include "c-download.h" // Project Includes +#include "kernel/core.h" #include "task/t-download.h" #include "task/t-generic.h" @@ -61,13 +62,13 @@ Qx::Error CDownload::perform() if(pItr == playlists.cend()) { CDownloadError err(CDownloadError::InvalidPlaylist, playlistName); - postError(err); + postDirective(err); return err; } logEvent(LOG_EVENT_PLAYLIST_MATCH.arg(pItr->id().toString(QUuid::WithoutBraces))); // Queue downloads for each game - TDownload* downloadTask = new TDownload(&mCore); + TDownload* downloadTask = new TDownload(mCore); downloadTask->setStage(Task::Stage::Primary); downloadTask->setDescription(u"playlist data packs"_s); QList dataIds; @@ -83,7 +84,7 @@ Qx::Error CDownload::perform() Fp::GameData gameData; if(Fp::DbError gdErr = db->getGameData(gameData, pg.gameId()); gdErr.isValid()) { - postError(gdErr); + postDirective(gdErr); return gdErr; } @@ -100,7 +101,7 @@ Qx::Error CDownload::perform() TDownloadError packError = downloadTask->addDatapack(tk, &gameData); if(packError.isValid()) { - postError(packError); + postDirective(packError); return packError; } @@ -119,7 +120,7 @@ Qx::Error CDownload::perform() // Enqueue onDiskState update task Core* corePtr = &mCore; // Safe, will outlive task - TGeneric* onDiskUpdateTask = new TGeneric(corePtr); + TGeneric* onDiskUpdateTask = new TGeneric(mCore); onDiskUpdateTask->setStage(Task::Stage::Primary); onDiskUpdateTask->setDescription(u"Update GameData onDisk state."_s); onDiskUpdateTask->setAction([dataIds, corePtr]{ diff --git a/app/src/command/c-link.cpp b/app/src/command/c-link.cpp index 959eb0b..7e09540 100644 --- a/app/src/command/c-link.cpp +++ b/app/src/command/c-link.cpp @@ -1,6 +1,9 @@ // Unit Include #include "c-link.h" +// Project Includes +#include "kernel/core.h" + //=============================================================================================================== // CLinkError //=============================================================================================================== @@ -59,7 +62,7 @@ Qx::Error CLink::perform() Fp::DbError dbError = database->getEntry(entry_v, shortcutId); if(dbError.isValid()) { - postError(dbError); + postDirective(dbError); return dbError; } @@ -76,7 +79,7 @@ Qx::Error CLink::perform() Fp::DbError dbError = database->getEntry(entry_v, addApp.parentId()); if(dbError.isValid()) { - postError(dbError); + postDirective(dbError); return dbError; } Q_ASSERT(std::holds_alternative(entry_v)); @@ -99,11 +102,12 @@ Qx::Error CLink::perform() logEvent(LOG_EVENT_NO_PATH); // Prompt user for path - Core::ExistingDirRequest edr{ + QString selectedPath; // Default is empty dir + postDirective(DExistingDir{ .caption = DIAG_CAPTION, - .dir = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) - }; - QString selectedPath = mCore.requestExistingDirPath(edr); + .startingDir = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation), + .response = &selectedPath + }); if(selectedPath.isEmpty()) { @@ -123,7 +127,7 @@ Qx::Error CLink::perform() if(!shortcutDir.mkpath(shortcutDir.absolutePath())) { CLinkError err(CLinkError::InvalidPath); - postError(err); + postDirective(err); return err; } logEvent(LOG_EVENT_CREATED_DIR_PATH.arg(QDir::toNativeSeparators(shortcutDir.absolutePath()))); diff --git a/app/src/command/c-link_linux.cpp b/app/src/command/c-link_linux.cpp index 9fb1c24..14e671e 100644 --- a/app/src/command/c-link_linux.cpp +++ b/app/src/command/c-link_linux.cpp @@ -6,6 +6,7 @@ // Project Includes #include "c-play.h" +#include "project_vars.h" #include "utility.h" //=============================================================================================================== @@ -20,7 +21,7 @@ Qx::Error CLink::createShortcut(const QString& name, const QDir& dir, QUuid id) if(!Utility::installAppIconForUser()) { CLinkError err(CLinkError::IconInstallFailed); - mCore.postError(NAME, err); + postDirective(err); return err; } @@ -41,11 +42,11 @@ Qx::Error CLink::createShortcut(const QString& name, const QDir& dir, QUuid id) // Check for write error if(writeReport.isFailure()) { - mCore.postError(NAME, writeReport); + postDirective(writeReport); return writeReport; } - mCore.logEvent(NAME, LOG_EVENT_CREATED_SHORTCUT.arg(id.toString(QUuid::WithoutBraces), QDir::toNativeSeparators(fullEntryPath))); + logEvent(LOG_EVENT_CREATED_SHORTCUT.arg(id.toString(QUuid::WithoutBraces), QDir::toNativeSeparators(fullEntryPath))); // Return success return CLinkError(); diff --git a/app/src/command/c-link_win.cpp b/app/src/command/c-link_win.cpp index d97dab1..cb0cd1d 100644 --- a/app/src/command/c-link_win.cpp +++ b/app/src/command/c-link_win.cpp @@ -31,7 +31,7 @@ Qx::Error CLink::createShortcut(const QString& name, const QDir& dir, QUuid id) // Check for creation failure if(shortcutError.isValid()) { - postError(shortcutError); + postDirective(shortcutError); return shortcutError; } else diff --git a/app/src/command/c-play.cpp b/app/src/command/c-play.cpp index 85a3556..30e9eef 100644 --- a/app/src/command/c-play.cpp +++ b/app/src/command/c-play.cpp @@ -8,9 +8,10 @@ #include // Project Includes -#include "../task/t-exec.h" -#include "../task/t-message.h" -#include "../task/t-extra.h" +#include "kernel/core.h" +#include "task/t-exec.h" +#include "task/t-message.h" +#include "task/t-extra.h" //=============================================================================================================== // CPlayError @@ -100,7 +101,7 @@ Qx::Error CPlay::handleEntry(const Fp::Game& game) Fp::GameData gameData; if(Fp::DbError gdErr = db->getGameData(gameData, game.id()); gdErr.isValid()) { - postError(gdErr); + postDirective(gdErr); return gdErr; } bool hasDatapack = !gameData.isNull(); @@ -130,7 +131,7 @@ Qx::Error CPlay::handleEntry(const Fp::Game& game) addAppSearchError = db->queryEntrys(addAppSearchResult, addAppFilter); if(addAppSearchError.isValid()) { - postError(addAppSearchError); + postDirective(addAppSearchError); return addAppSearchError; } @@ -176,7 +177,7 @@ Qx::Error CPlay::handleEntry(const Fp::AddApp& addApp) Fp::GameData parentGameData; if(Fp::DbError gdErr = db->getGameData(parentGameData, parentId); gdErr.isValid()) { - postError(gdErr); + postDirective(gdErr); return gdErr; } bool hasDatapack = !parentGameData.isNull(); @@ -205,7 +206,7 @@ Qx::Error CPlay::handleEntry(const Fp::AddApp& addApp) if(Fp::DbError pge = db->queryEntrys(parentResult, parentFilter); pge.isValid()) { - postError(pge); + postDirective(pge); return pge; } @@ -232,7 +233,7 @@ Qx::Error CPlay::enqueueAdditionalApp(const Fp::AddApp& addApp, const QString& p { if(addApp.appPath() == Fp::Db::Table_Add_App::ENTRY_MESSAGE) { - TMessage* messageTask = new TMessage(&mCore); + TMessage* messageTask = new TMessage(mCore); messageTask->setStage(taskStage); messageTask->setText(addApp.launchCommand()); messageTask->setBlocking(addApp.isWaitExit()); @@ -241,7 +242,7 @@ Qx::Error CPlay::enqueueAdditionalApp(const Fp::AddApp& addApp, const QString& p } else if(addApp.appPath() == Fp::Db::Table_Add_App::ENTRY_EXTRAS) { - TExtra* extraTask = new TExtra(&mCore); + TExtra* extraTask = new TExtra(mCore); extraTask->setStage(taskStage); extraTask->setDirectory(QDir(mCore.fpInstall().extrasDirectory().absolutePath() + '/' + addApp.launchCommand())); @@ -254,7 +255,7 @@ Qx::Error CPlay::enqueueAdditionalApp(const Fp::AddApp& addApp, const QString& p QString param = addApp.launchCommand(); addPassthroughParameters(param); - TExec* addAppTask = new TExec(&mCore); + TExec* addAppTask = new TExec(mCore); addAppTask->setIdentifier(addApp.name()); addAppTask->setStage(taskStage); addAppTask->setExecutable(QDir::cleanPath(addAppPathInfo.absoluteFilePath())); // Like canonical but doesn't care if path DNE @@ -284,7 +285,7 @@ Qx::Error CPlay::enqueueGame(const Fp::Game& game, const Fp::GameData& gameData, QString param = !gameData.isNull() ? gameData.launchCommand() : game.launchCommand(); addPassthroughParameters(param); - TExec* gameTask = new TExec(&mCore); + TExec* gameTask = new TExec(mCore); gameTask->setIdentifier(game.title()); gameTask->setStage(taskStage); gameTask->setExecutable(QDir::cleanPath(gamePathInfo.absoluteFilePath())); // Like canonical but doesn't care if path DNE @@ -320,7 +321,7 @@ Qx::Error CPlay::perform() if(!urlMatch.hasMatch()) { CPlayError err(CPlayError::InvalidUrl); - postError(err); + postDirective(err); return err; } @@ -341,7 +342,7 @@ Qx::Error CPlay::perform() Fp::Entry entry; if(Fp::DbError eErr = db->getEntry(entry, titleId); eErr.isValid()) { - postError(eErr); + postDirective(eErr); return eErr; } diff --git a/app/src/command/c-play.h b/app/src/command/c-play.h index d120554..6b6298c 100644 --- a/app/src/command/c-play.h +++ b/app/src/command/c-play.h @@ -6,6 +6,7 @@ // Project Includes #include "command/title-command.h" +#include "task/task.h" class QX_ERROR_TYPE(CPlayError, "CPlayError", 1212) { diff --git a/app/src/command/c-prepare.cpp b/app/src/command/c-prepare.cpp index f233d03..4474cf8 100644 --- a/app/src/command/c-prepare.cpp +++ b/app/src/command/c-prepare.cpp @@ -4,6 +4,9 @@ // Qx Includes #include +// Project Includes +#include "kernel/core.h" + //=============================================================================================================== // CPREPARE //=============================================================================================================== @@ -28,7 +31,7 @@ Qx::Error CPrepare::perform() Fp::GameData titleGameData; if(Fp::DbError gdErr = mCore.fpInstall().database()->getGameData(titleGameData, id); gdErr.isValid()) { - postError(gdErr); + postDirective(gdErr); return gdErr; } diff --git a/app/src/command/c-run.cpp b/app/src/command/c-run.cpp index 36218c1..fabbc79 100644 --- a/app/src/command/c-run.cpp +++ b/app/src/command/c-run.cpp @@ -2,6 +2,7 @@ #include "c-run.h" // Project Includes +#include "kernel/core.h" #include "task/t-exec.h" //=============================================================================================================== @@ -50,7 +51,7 @@ Qx::Error CRun::perform() QString inputPath = mCore.resolveFullAppPath(mParser.value(CL_OPTION_APP), u""_s); // No way of knowing platform QFileInfo inputInfo = QFileInfo(inputPath); - TExec* runTask = new TExec(&mCore); + TExec* runTask = new TExec(mCore); runTask->setIdentifier(NAME + u" program"_s); runTask->setStage(Task::Stage::Primary); runTask->setExecutable(QDir::cleanPath(inputInfo.absoluteFilePath())); // Like canonical but doesn't care if path DNE diff --git a/app/src/command/c-share.cpp b/app/src/command/c-share.cpp index afc298f..c9ef53b 100644 --- a/app/src/command/c-share.cpp +++ b/app/src/command/c-share.cpp @@ -5,6 +5,7 @@ #include // Project Includes +#include "kernel/core.h" #include "task/t-message.h" #include "utility.h" @@ -54,12 +55,12 @@ Qx::Error CShare::perform() if(!Qx::setDefaultProtocolHandler(SCHEME, SCHEME_NAME)) { CShareError err(CShareError::RegistrationFailed); - postError(err); + postDirective(err); return err; } // Enqueue success message task - TMessage* successMsg = new TMessage(&mCore); + TMessage* successMsg = new TMessage(mCore); successMsg->setStage(Task::Stage::Primary); successMsg->setText(MSG_REGISTRATION_COMPLETE); mCore.enqueueSingleTask(successMsg); @@ -77,12 +78,12 @@ Qx::Error CShare::perform() #endif { CShareError err(CShareError::UnregistrationFailed); - postError(err); + postDirective(err); return err; } // Enqueue success message task - TMessage* successMsg = new TMessage(&mCore); + TMessage* successMsg = new TMessage(mCore); successMsg->setStage(Task::Stage::Primary); successMsg->setText(MSG_UNREGISTRATION_COMPLETE); mCore.enqueueSingleTask(successMsg); @@ -103,10 +104,10 @@ Qx::Error CShare::perform() logEvent(LOG_EVENT_URL.arg(shareUrl)); // Add URL to clipboard - mCore.requestClipboardUpdate(shareUrl); + postDirective(shareUrl); // Enqueue message task - TMessage* urlMsg = new TMessage(&mCore); + TMessage* urlMsg = new TMessage(mCore); urlMsg->setStage(Task::Stage::Primary); urlMsg->setText(MSG_GENERATED_URL.arg(shareUrl)); urlMsg->setSelectable(true); diff --git a/app/src/command/c-show.cpp b/app/src/command/c-show.cpp index 2671d84..dd205fd 100644 --- a/app/src/command/c-show.cpp +++ b/app/src/command/c-show.cpp @@ -2,6 +2,7 @@ #include "c-show.h" // Project Includes +#include "kernel/core.h" #include "task/t-message.h" #include "task/t-extra.h" @@ -46,7 +47,7 @@ Qx::Error CShow::perform() // Enqueue show task if(mParser.isSet(CL_OPTION_MSG)) { - TMessage* messageTask = new TMessage(&mCore); + TMessage* messageTask = new TMessage(mCore); messageTask->setStage(Task::Stage::Primary); messageTask->setText(mParser.value(CL_OPTION_MSG)); @@ -55,7 +56,7 @@ Qx::Error CShow::perform() } else if(mParser.isSet(CL_OPTION_EXTRA)) { - TExtra* extraTask = new TExtra(&mCore); + TExtra* extraTask = new TExtra(mCore); extraTask->setStage(Task::Stage::Primary); extraTask->setDirectory(QDir(mCore.fpInstall().extrasDirectory().absolutePath() + '/' + mParser.value(CL_OPTION_EXTRA))); @@ -65,7 +66,7 @@ Qx::Error CShow::perform() else { CShowError err(CShowError::MissingThing); - postError(err); + postDirective(err); return err; } diff --git a/app/src/command/c-update.cpp b/app/src/command/c-update.cpp index 5d0c27d..a6db691 100644 --- a/app/src/command/c-update.cpp +++ b/app/src/command/c-update.cpp @@ -4,12 +4,13 @@ // Qt Includes #include #include +#include // Qx Includes -#include #include // Project Includes +#include "kernel/core.h" #include "task/t-download.h" #include "task/t-extract.h" #include "task/t-exec.h" @@ -240,7 +241,7 @@ CUpdateError CUpdate::handleTransfers(const UpdateTransfers& transfers) const if(!doTransfer(ft, true, true, true)) { CUpdateError err(CUpdateError::TransferFail, ft.dest); - postError(err); + postDirective(err); return err; } restoreTransfers << FileTransfer{.source = ft.dest, .dest = ft.source}; @@ -253,7 +254,7 @@ CUpdateError CUpdate::handleTransfers(const UpdateTransfers& transfers) const if(!doTransfer(ft, true, false, false)) { CUpdateError err(CUpdateError::TransferFail, ft.dest); - postError(err); + postDirective(err); return err; } } @@ -268,7 +269,7 @@ CUpdateError CUpdate::checkAndPrepareUpdate() const if(!mCore.blockNewInstances()) { CUpdateError err(CUpdateError::AlreadyOpen); - postError(err); + postDirective(err); return err; } @@ -280,7 +281,7 @@ CUpdateError CUpdate::checkAndPrepareUpdate() const ReleaseData rd; if(CUpdateError ue = getLatestReleaseData(rd); ue.isValid()) { - postError(ue); + postDirective(ue); return ue; } @@ -291,13 +292,13 @@ CUpdateError CUpdate::checkAndPrepareUpdate() const if(newVersion.isNull()) { CUpdateError err(CUpdateError::InvalidReleaseVersion); - postError(err); + postDirective(err); return err; } if(newVersion <= currentVersion) { - mCore.postMessage(Message{.text = MSG_NO_UPDATES}); + postDirective(MSG_NO_UPDATES); logEvent(MSG_NO_UPDATES); return CUpdateError(); } @@ -315,11 +316,13 @@ CUpdateError CUpdate::checkAndPrepareUpdate() const if(aItr == rd.assets.cend()) { - postError(Qx::GenericError(Qx::Warning, 12181, WRN_NO_MATCHING_BUILD_P, WRN_NO_MATCHING_BUILD_S)); + postDirective(Qx::GenericError(Qx::Warning, 12181, WRN_NO_MATCHING_BUILD_P, WRN_NO_MATCHING_BUILD_S)); return CUpdateError(); } - if(mCore.requestQuestionAnswer(QUES_UPDATE.arg(rd.name))) + bool shouldUpdate = false; + postDirective(QUES_UPDATE.arg(rd.name), &shouldUpdate); + if(shouldUpdate) { logEvent(LOG_EVENT_UPDATE_ACCEPED); @@ -328,19 +331,19 @@ CUpdateError CUpdate::checkAndPrepareUpdate() const QDir uDataDir = updateDataDir(); QString tempName = u"clifp_update.zip"_s; - TDownload* downloadTask = new TDownload(&mCore); + TDownload* downloadTask = new TDownload(mCore); downloadTask->setStage(Task::Stage::Primary); downloadTask->setDescription(u"update"_s); downloadTask->addFile({.target = aItr->browser_download_url, .dest = uDownloadDir.absoluteFilePath(tempName)}); mCore.enqueueSingleTask(downloadTask); - TExtract* extractTask = new TExtract(&mCore); + TExtract* extractTask = new TExtract(mCore); extractTask->setStage(Task::Stage::Primary); extractTask->setPackPath(uDownloadDir.absoluteFilePath(tempName)); extractTask->setDestinationPath(uDataDir.absolutePath()); mCore.enqueueSingleTask(extractTask); - TExec* execTask = new TExec(&mCore); + TExec* execTask = new TExec(mCore); execTask->setStage(Task::Stage::Primary); execTask->setIdentifier(UPDATE_STAGE_NAME); QString newAppExecPath = uDataDir.absolutePath() + u"/bin"_s; @@ -382,7 +385,7 @@ Qx::Error CUpdate::installUpdate(const QFileInfo& existingAppInfo) const if(!haveLock) { CUpdateError err(CUpdateError::OldProcessNotFinished, "Aborting update."); - postError(err); + postDirective(err); return err; } @@ -393,7 +396,7 @@ Qx::Error CUpdate::installUpdate(const QFileInfo& existingAppInfo) const if(!existingAppInfo.exists()) { CUpdateError err(CUpdateError::InvalidPath, "Missing " + existingAppInfo.absoluteFilePath()); - postError(err); + postDirective(err); return err; } @@ -410,7 +413,7 @@ Qx::Error CUpdate::installUpdate(const QFileInfo& existingAppInfo) const QStringList updateFiles; if(Qx::IoOpReport rep = determineNewFiles(updateFiles, ts.updateRoot); rep.isFailure()) { - postError(rep); + postDirective(rep); return rep; } @@ -422,7 +425,7 @@ Qx::Error CUpdate::installUpdate(const QFileInfo& existingAppInfo) const // Success logEvent(MSG_UPDATE_COMPLETE); - mCore.postMessage(Message{.text = MSG_UPDATE_COMPLETE}); + postDirective(MSG_UPDATE_COMPLETE); return CUpdateError(); } diff --git a/app/src/command/c-update.h b/app/src/command/c-update.h index eecfe42..916051d 100644 --- a/app/src/command/c-update.h +++ b/app/src/command/c-update.h @@ -2,6 +2,7 @@ #define CUPDATE_H // Qx Includes +#include #include // Project Includes diff --git a/app/src/command/command.cpp b/app/src/command/command.cpp index dec1087..754e562 100644 --- a/app/src/command/command.cpp +++ b/app/src/command/command.cpp @@ -7,6 +7,9 @@ // Qx Includes #include +// Project Includes +#include "kernel/core.h" + //=============================================================================================================== // CommandError //=============================================================================================================== @@ -43,7 +46,10 @@ QString CommandError::errorString() const { return mString; } //-Constructor---------------------------------------------------------------------------------------------------------- //Public: -Command::Command(Core& coreRef) : mCore(coreRef) {} +Command::Command(Core& coreRef) : + Directorate(coreRef.director()), + mCore(coreRef) +{} //-Class Functions------------------------------------------------------------------ //Private: @@ -93,16 +99,19 @@ CommandError Command::parse(const QStringList& commandLine) if(optionsStr.isEmpty()) optionsStr = Core::LOG_NO_PARAMS; - // Log command - mCore.logCommand(NAME, commandLine.first()); - mCore.logCommandOptions(NAME, optionsStr); + /* Log command (ensure "Command" name is used) + * + * TODO: Do we really want to force "Command" here? Don't recall why + */ + Command::logCommand(commandLine.first()); + Command::logCommandOptions(optionsStr); if(validArgs) return CommandError(); else { CommandError parseErr = CommandError(ERR_INVALID_ARGS).wDetails(mParser.errorText()); - postError(parseErr); + postDirective(parseErr); return parseErr; } } @@ -168,22 +177,13 @@ void Command::showHelp() } // Show help - mCore.postMessage(Message{.text = helpStr}); + postDirective(helpStr); } //Protected: QList Command::options() const { return CL_OPTIONS_STANDARD; } QSet Command::requiredOptions() const { return {}; } - -// Notifications/Logging (core-forwarders) -void Command::logCommand(QString commandName) const {mCore.logCommand(name(), commandName); } -void Command::logCommandOptions(QString commandOptions) const {mCore.logCommandOptions(name(), commandOptions); } -void Command::logError(Qx::Error error) const {mCore.logError(name(), error); } -void Command::logEvent(QString event) const {mCore.logEvent(name(), event); } -void Command::logTask(const Task* task) const {mCore.logTask(name(), task); } -ErrorCode Command::logFinish(Qx::Error errorState) const {return mCore.logFinish(name(), errorState); } -void Command::postError(Qx::Error error, bool log) const {mCore.postError(name(), error, log); } -int Command::postBlockingError(Qx::Error error, bool log, QMessageBox::StandardButtons bs, QMessageBox::StandardButton def) const {return mCore.postBlockingError(name(), error, log); } +QString Command::name() const { return NAME; }; //Public: bool Command::requiresFlashpoint() const { return true; } @@ -205,7 +205,7 @@ Qx::Error Command::process(const QStringList& commandLine) processError = checkRequiredOptions(); if(processError.isValid()) { - postError(processError); + postDirective(processError); return processError; } diff --git a/app/src/command/command.h b/app/src/command/command.h index 54b1e5e..c4c3d88 100644 --- a/app/src/command/command.h +++ b/app/src/command/command.h @@ -1,13 +1,17 @@ #ifndef COMMAND_H #define COMMAND_H +// Qt Includes +#include + // Qx Includes #include -// Project Includes -#include "kernel/core.h" +// Project Includes +#include "kernel/directorate.h" class CommandFactory; +class Core; //-Macros------------------------------------------------------------------------------------------------------------------- #define REGISTER_COMMAND(name, command, desc) \ @@ -27,10 +31,10 @@ class QX_ERROR_TYPE(CommandError, "CommandError", 1210) public: enum Type { - NoError = 0, - InvalidArguments = 1, - InvalidCommand = 2, - MissingRequiredOption = 3 + NoError, + InvalidArguments, + InvalidCommand, + MissingRequiredOption }; //-Class Variables------------------------------------------------------------------------------------------------ @@ -74,7 +78,7 @@ class QX_ERROR_TYPE(CommandError, "CommandError", 1210) QString errorString() const; }; -class Command +class Command : public Directorate { //-Class Structs------------------------------------------------------------------------------------------------------ protected: @@ -161,16 +165,6 @@ class Command virtual QString name() const = 0; virtual Qx::Error perform() = 0; - // Notifications/Logging (core-forwarders) - void logCommand(QString commandName) const; - void logCommandOptions(QString commandOptions) const; - void logError(Qx::Error error) const; - void logEvent(QString event) const; - void logTask(const Task* task) const; - ErrorCode logFinish(Qx::Error errorState) const; - void postError(Qx::Error error, bool log = true) const; - int postBlockingError(Qx::Error error, bool log = true, QMessageBox::StandardButtons bs = QMessageBox::Ok, QMessageBox::StandardButton def = QMessageBox::NoButton) const; - public: virtual bool requiresFlashpoint() const; virtual bool requiresServices() const; diff --git a/app/src/command/title-command.cpp b/app/src/command/title-command.cpp index 86c9e0e..4e0b8bc 100644 --- a/app/src/command/title-command.cpp +++ b/app/src/command/title-command.cpp @@ -4,6 +4,9 @@ // Qx Includes #include +// Project Includes +#include "kernel/core.h" + //=============================================================================================================== // TitleCommandError //=============================================================================================================== @@ -58,7 +61,7 @@ Qx::Error TitleCommand::randomlySelectId(QUuid& mainIdBuffer, QUuid& subIdBuffer searchError = database->queryAllGameIds(mainGameIdQuery, lbFilter); if(searchError.isValid()) { - postError(searchError); + postDirective(searchError); return searchError; } @@ -91,7 +94,7 @@ Qx::Error TitleCommand::randomlySelectId(QUuid& mainIdBuffer, QUuid& subIdBuffer searchError = database->queryEntrys(addAppQuery, addAppFilter); if(searchError.isValid()) { - postError(searchError); + postDirective(searchError); return searchError; } logEvent(LOG_EVENT_INIT_RAND_PLAY_ADD_COUNT.arg(addAppQuery.size)); @@ -149,7 +152,7 @@ Qx::Error TitleCommand::getRandomSelectionInfo(QString& infoBuffer, QUuid mainId searchError = database->getEntry(entry_v, mainId); if(searchError.isValid()) { - postError(searchError); + postDirective(searchError); return searchError; } @@ -171,7 +174,7 @@ Qx::Error TitleCommand::getRandomSelectionInfo(QString& infoBuffer, QUuid mainId searchError = database->getEntry(entry_v, subId); if(searchError.isValid()) { - postError(searchError); + postDirective(searchError); return searchError; } @@ -207,7 +210,7 @@ Qx::Error TitleCommand::getTitleId(QUuid& id) if((titleId = QUuid(idStr)).isNull()) { TitleCommandError err(TitleCommandError::InvalidId, idStr); - postError(err); + postDirective(err); return err; } QUuid origId = titleId; @@ -257,7 +260,7 @@ Qx::Error TitleCommand::getTitleId(QUuid& id) else { TitleCommandError err(TitleCommandError::InvalidRandomFilter, rawRandFilter); - postError(err); + postDirective(err); return err; } @@ -265,21 +268,17 @@ Qx::Error TitleCommand::getTitleId(QUuid& id) if(Qx::Error re = randomlySelectId(titleId, addAppId, randFilter); re.isValid()) return re; - // Show selection info - if(mCore.notifcationVerbosity() == Core::NotificationVerbosity::Full) - { - QString selInfo; - if(Qx::Error rse = getRandomSelectionInfo(selInfo, titleId, addAppId); rse.isValid()) - return rse; + QString selInfo; + if(Qx::Error rse = getRandomSelectionInfo(selInfo, titleId, addAppId); rse.isValid()) + return rse; - // Display selection info - mCore.postMessage(Message{.text = selInfo}); - } + // Display selection info + postDirective(selInfo); } else { TitleCommandError err(TitleCommandError::MissingTitle); - postError(err); + postDirective(err); return err; } diff --git a/app/src/command/title-command.h b/app/src/command/title-command.h index eb0a1f9..5b8df28 100644 --- a/app/src/command/title-command.h +++ b/app/src/command/title-command.h @@ -1,6 +1,9 @@ #ifndef TITLE_COMMAND_H #define TITLE_COMMAND_H +// libfp Includes +#include + // Project Includes #include "command/command.h" diff --git a/app/src/controller.cpp b/app/src/controller.cpp index 1ab58e7..4c41f81 100644 --- a/app/src/controller.cpp +++ b/app/src/controller.cpp @@ -30,24 +30,13 @@ Controller::Controller(QObject* parent) : connect(driver, &Driver::finished, &mWorkerThread, &QThread::quit); // Have driver finish cause thread finish connect(&mWorkerThread, &QThread::finished, this, &Controller::finisher); // Finish execution when thread quits - // Connect driver - Status/Non-blocking - connect(driver, &Driver::statusChanged, &mStatusRelay, &StatusRelay::statusChangeHandler); - connect(driver, &Driver::errorOccurred, &mStatusRelay, &StatusRelay::errorHandler); - connect(driver, &Driver::longTaskProgressChanged, &mStatusRelay, &StatusRelay::longTaskProgressHandler); - connect(driver, &Driver::longTaskTotalChanged, &mStatusRelay, &StatusRelay::longTaskTotalHandler); - connect(driver, &Driver::longTaskStarted, &mStatusRelay, &StatusRelay::longTaskStartedHandler); - connect(driver, &Driver::longTaskFinished, &mStatusRelay, &StatusRelay::longTaskFinishedHandler); + // Connect driver - Directives + connect(driver, &Driver::asyncDirectiveAccounced, &mStatusRelay, &StatusRelay::asyncDirectiveHandler); + connect(driver, &Driver::syncDirectiveAccounced, &mStatusRelay, &StatusRelay::syncDirectiveHandler, Qt::BlockingQueuedConnection); + + // Connect driver - Task Cancellation connect(&mStatusRelay, &StatusRelay::longTaskCanceled, driver, &Driver::cancelActiveLongTask); connect(&mStatusRelay, &StatusRelay::longTaskCanceled, this, &Controller::longTaskCanceledHandler); - connect(driver, &Driver::clipboardUpdateRequested, &mStatusRelay, &StatusRelay::clipboardUpdateRequestHandler); - - // Connect driver - Response Requests/Blocking (BlockingQueuedConnection since response must be waited for) - connect(driver, &Driver::message, &mStatusRelay, &StatusRelay::messageHandler, Qt::BlockingQueuedConnection); // Allows optional blocking - connect(driver, &Driver::blockingErrorOccurred, &mStatusRelay, &StatusRelay::blockingErrorHandler, Qt::BlockingQueuedConnection); - connect(driver, &Driver::saveFileRequested, &mStatusRelay, &StatusRelay::saveFileRequestHandler, Qt::BlockingQueuedConnection); - connect(driver, &Driver::existingDirRequested, &mStatusRelay, &StatusRelay::existingDirectoryRequestHandler, Qt::BlockingQueuedConnection); - connect(driver, &Driver::itemSelectionRequested, &mStatusRelay, &StatusRelay::itemSelectionRequestHandler, Qt::BlockingQueuedConnection); - connect(driver, &Driver::questionAnswerRequested, &mStatusRelay, &StatusRelay::questionAnswerRequestHandler, Qt::BlockingQueuedConnection); // Connect quit handler connect(&mStatusRelay, &StatusRelay::quitRequested, this, &Controller::quitRequestHandler); diff --git a/app/src/controller.h b/app/src/controller.h index 49bab33..86c3bd8 100644 --- a/app/src/controller.h +++ b/app/src/controller.h @@ -3,10 +3,11 @@ // Qt Includes #include +#include // Project Includes #include "frontend/statusrelay.h" -#include "kernel/core.h" +#include "kernel/director.h" class Controller : public QObject { diff --git a/app/src/frontend/message.h b/app/src/frontend/message.h deleted file mode 100644 index cfa4880..0000000 --- a/app/src/frontend/message.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef MESSAGE_H -#define MESSAGE_H - -// Qt Includes -#include - -struct Message -{ - QString text; - bool blocking = false; - bool selectable = false; -}; - -#endif // MESSAGE_H diff --git a/app/src/frontend/statusrelay.cpp b/app/src/frontend/statusrelay.cpp index 7aa1f77..1f1d1ed 100644 --- a/app/src/frontend/statusrelay.cpp +++ b/app/src/frontend/statusrelay.cpp @@ -4,9 +4,15 @@ // Qt Includes #include #include +#include +#include // Qx Includes #include +#include + +// Magic enum +#include "magic_enum_utility.hpp" //=============================================================================================================== // STATUS RELAY @@ -21,6 +27,36 @@ StatusRelay::StatusRelay(QObject* parent) : setupProgressDialog(); } +//-Class Functions---------------------------------------------------------------------------------------------------- +//Private: +QMessageBox::StandardButtons StatusRelay::choicesToButtons(DBlockingError::Choices cs) +{ + QMessageBox::StandardButtons bs; + + magic_enum::enum_for_each([&] (auto val) { + constexpr DBlockingError::Choice c = val; + if(cs.testFlag(c)) + bs.setFlag(smChoiceButtonMap.toRight(c)); + }); + + return bs; +} + +template + requires Qx::any_of +QMessageBox* StatusRelay::prepareMessageBox(const MessageT& dMsg) +{ + QMessageBox* msg = new QMessageBox(); + msg->setIcon(QMessageBox::Information); + msg->setWindowTitle(QApplication::applicationName()); // This should be the default, but hey being explicit never hurt + msg->setText(dMsg.text); + + if(dMsg.selectable) + msg->setTextInteractionFlags(Qt::TextSelectableByMouse); + + return msg; +} + //-Instance Functions-------------------------------------------------------------------------------------------------- //Private: void StatusRelay::setupTrayIcon() @@ -59,120 +95,104 @@ void StatusRelay::setupProgressDialog() connect(&mLongTaskProgressDialog, &QProgressDialog::canceled, this, &StatusRelay::longTaskCanceled); } -//-Signals & Slots------------------------------------------------------------- -//Public Slots: -void StatusRelay::statusChangeHandler(const QString& statusHeading, const QString& statusMessage) -{ - mStatusHeading = statusHeading; - mStatusMessage = statusMessage; -} - -void StatusRelay::errorHandler(Core::Error error) +void StatusRelay::handleMessage(const DMessage& d) { - Qx::postError(error.errorInfo); + auto mb = prepareMessageBox(d); + mb->setAttribute(Qt::WA_DeleteOnClose); + mb->show(); } -void StatusRelay::blockingErrorHandler(QSharedPointer response, Core::BlockingError blockingError) -{ - if(response) - *response = Qx::postBlockingError(blockingError.errorInfo, blockingError.choices, blockingError.defaultChoice); - else - qFatal("No response argument provided!"); -} +void StatusRelay::handleError(const DError& d) { Qx::postError(d.error); } -void StatusRelay::messageHandler(const Message& message) +void StatusRelay::handleProcedureStart(const DProcedureStart& d) { - QMessageBox* msg = new QMessageBox(); - msg->setIcon(QMessageBox::Information); - msg->setWindowTitle(QApplication::applicationName()); // This should be the default, but hey being explicit never hurt - msg->setText(message.text); - - if(message.selectable) - msg->setTextInteractionFlags(Qt::TextSelectableByMouse); + // Set label + mLongTaskProgressDialog.setLabelText(d.label); - if(message.blocking) - { - msg->exec(); - delete msg; - } - else - { - msg->setAttribute(Qt::WA_DeleteOnClose); - msg->show(); - } + // Show right away + mLongTaskProgressDialog.setValue(0); } -void StatusRelay::saveFileRequestHandler(QSharedPointer file, Core::SaveFileRequest request) +void StatusRelay::handleProcedureStop(const DProcedureStop& d) { - if(file) - { - *file = QFileDialog::getSaveFileName(nullptr, request.caption, request.dir, - request.filter, request.selectedFilter, request.options); - } - else - qFatal("No response argument provided!"); + Q_UNUSED(d); + /* Always reset the dialog regardless of whether it is visible or not as it may not be currently visible, + * but queued to be visible on the next event loop iteration and therefore still needs to be hidden + * immediately after. + */ + mLongTaskProgressDialog.reset(); } -void StatusRelay::existingDirectoryRequestHandler(QSharedPointer dir, Core::ExistingDirRequest request) -{ - if(dir) - { - *dir = QFileDialog::getExistingDirectory(nullptr, request.caption, request.dir, request.options); - } - else - qFatal("No response argument provided!"); -} +void StatusRelay::handleProcedureProgress(const DProcedureProgress& d) { mLongTaskProgressDialog.setValue(d.current); } +void StatusRelay::handleProcedureScale(const DProcedureScale& d) { mLongTaskProgressDialog.setMaximum(d.max); } +void StatusRelay::handleClipboardUpdate(const DClipboardUpdate& d) { mSystemClipboard->setText(d.text); } +void StatusRelay::handleStatusUpdate(const DStatusUpdate& d) { mStatusHeading = d.heading; mStatusMessage = d.message; } -void StatusRelay::itemSelectionRequestHandler(QSharedPointer item, const Core::ItemSelectionRequest& request) +// Sync directive handlers +void StatusRelay::handleBlockingMessage(const DBlockingMessage& d) { - if(item) - { - bool accepted; - QString selected = QInputDialog::getItem(nullptr, request.caption, request.label, request.items, 0, false, &accepted); - *item = accepted ? selected : QString(); - } - else - qFatal("No response argument provided!"); + auto mb = prepareMessageBox(d); + mb->exec(); + delete mb; } -void StatusRelay::clipboardUpdateRequestHandler(const QString& text) +void StatusRelay::handleBlockingError(const DBlockingError& d) { - mSystemClipboard->setText(text); + Q_ASSERT(d.response); + auto btns = choicesToButtons(d.choices); + auto def = smChoiceButtonMap.toRight(d.defaultChoice); + int rawRes = Qx::postBlockingError(d.error, btns, def); + *d.response = smChoiceButtonMap.toLeft(static_cast(rawRes)); } -void StatusRelay::questionAnswerRequestHandler(QSharedPointer response, const QString& question) +void StatusRelay::handleSaveFilename(const DSaveFilename& d) { - - if(response) - *response = QMessageBox::question(nullptr, QString(), question) == QMessageBox::Yes; - else - qFatal("No response argument provided!"); + Q_ASSERT(d.response); + *d.response = QFileDialog::getSaveFileName(nullptr, d.caption, d.dir, d.filter, d.selectedFilter); } -void StatusRelay::longTaskProgressHandler(quint64 progress) +void StatusRelay::handleExistingDir(const DExistingDir& d) { - mLongTaskProgressDialog.setValue(progress); + Q_ASSERT(d.response); + *d.response = QFileDialog::getExistingDirectory(nullptr, d.caption, d.startingDir); } -void StatusRelay::longTaskTotalHandler(quint64 total) +void StatusRelay::handleItemSelection(const DItemSelection& d) { - mLongTaskProgressDialog.setMaximum(total); + Q_ASSERT(d.response); + *d.response = QInputDialog::getItem(nullptr, d.caption, d.label, d.items, 0, false); } -void StatusRelay::longTaskStartedHandler(QString task) +void StatusRelay::handleYesOrNo(const DYesOrNo& d) { - // Set label - mLongTaskProgressDialog.setLabelText(task); - - // Show right away - mLongTaskProgressDialog.setValue(0); + Q_ASSERT(d.response); + *d.response = QMessageBox::question(nullptr, QString(), d.question) == QMessageBox::Yes; } -void StatusRelay::longTaskFinishedHandler() -{ - /* Always reset the dialog regardless of whether it is visible or not as it may not be currently visible, - * but queued to be visible on the next event loop iteration and therefore still needs to be hidden - * immediately after. - */ - mLongTaskProgressDialog.reset(); +//-Signals & Slots------------------------------------------------------------- +//Public Slots: +void StatusRelay::asyncDirectiveHandler(const AsyncDirective& aDirective) +{ + std::visit(qxFuncAggregate{ + [this](DMessage d){ handleMessage(d); }, + [this](DError d) { handleError(d); }, + [this](DProcedureStart d) { handleProcedureStart(d); }, + [this](DProcedureStop d) { handleProcedureStop(d); }, + [this](DProcedureProgress d) { handleProcedureProgress(d); }, + [this](DProcedureScale d) { handleProcedureScale(d); }, + [this](DClipboardUpdate d) { handleClipboardUpdate(d); }, + [this](DStatusUpdate d) { handleStatusUpdate(d); }, + }, aDirective); +} + +void StatusRelay::syncDirectiveHandler(const SyncDirective& sDirective) +{ + std::visit(qxFuncAggregate{ + [this](DBlockingMessage d){ handleBlockingMessage(d); }, + [this](DBlockingError d){ handleBlockingError(d); }, + [this](DSaveFilename d) { handleSaveFilename(d); }, + [this](DExistingDir d) { handleExistingDir(d); }, + [this](DItemSelection d) { handleItemSelection(d); }, + [this](DYesOrNo d) { handleYesOrNo(d); } + }, sDirective); } diff --git a/app/src/frontend/statusrelay.h b/app/src/frontend/statusrelay.h index 9894afa..242ecfb 100644 --- a/app/src/frontend/statusrelay.h +++ b/app/src/frontend/statusrelay.h @@ -6,15 +6,16 @@ #include #include #include -#include -#include #include +#include // Qx Includes +#include #include +#include // Project Includes -#include "kernel/core.h" +#include "kernel/directive.h" class StatusRelay : public QObject { @@ -24,6 +25,14 @@ class StatusRelay : public QObject // System Messages static inline const QString SYS_TRAY_STATUS = u"CLIFp is running"_s; +//-Class Variables-------------------------------------------------------------------------------------------------------- +private: + Qx::Bimap smChoiceButtonMap{ + {DBlockingError::Choice::Ok, QMessageBox::Ok}, + {DBlockingError::Choice::Yes, QMessageBox::Yes}, + {DBlockingError::Choice::No, QMessageBox::No}, + }; + //-Instance Variables------------------------------------------------------------------------------------------------------ public: QProgressDialog mLongTaskProgressDialog; @@ -38,29 +47,42 @@ class StatusRelay : public QObject public: explicit StatusRelay(QObject* parent = nullptr); +//-Instance Functions-------------------------------------------------------------------------------------------------- +private: + QMessageBox::StandardButtons choicesToButtons(DBlockingError::Choices cs); + + template + requires Qx::any_of + QMessageBox* prepareMessageBox(const MessageT& dMsg); + //-Instance Functions-------------------------------------------------------------------------------------------------- private: void setupTrayIcon(); void setupProgressDialog(); + // Async directive handlers + void handleMessage(const DMessage& d); + void handleError(const DError& d); + void handleProcedureStart(const DProcedureStart& d); + void handleProcedureStop(const DProcedureStop& d); + void handleProcedureProgress(const DProcedureProgress& d); + void handleProcedureScale(const DProcedureScale& d); + void handleClipboardUpdate(const DClipboardUpdate& d); + void handleStatusUpdate(const DStatusUpdate& d); + + // Sync directive handlers + void handleBlockingMessage(const DBlockingMessage& d); + void handleBlockingError(const DBlockingError& d); + void handleSaveFilename(const DSaveFilename& d); + void handleExistingDir(const DExistingDir& d); + void handleItemSelection(const DItemSelection& d); + void handleYesOrNo(const DYesOrNo& d); + //-Signals & Slots------------------------------------------------------------------------------------------------------ public slots: - // Request/status handlers - void statusChangeHandler(const QString& statusHeading, const QString& statusMessage); - void errorHandler(Core::Error error); - void blockingErrorHandler(QSharedPointer response, Core::BlockingError blockingError); - void messageHandler(const Message& message); - void saveFileRequestHandler(QSharedPointer file, Core::SaveFileRequest request); - void existingDirectoryRequestHandler(QSharedPointer dir, Core::ExistingDirRequest request); - void itemSelectionRequestHandler(QSharedPointer item, const Core::ItemSelectionRequest& request); - void clipboardUpdateRequestHandler(const QString& text); - void questionAnswerRequestHandler(QSharedPointer response, const QString& question); - - // Long Job - void longTaskProgressHandler(quint64 progress); - void longTaskTotalHandler(quint64 total); - void longTaskStartedHandler(QString task); - void longTaskFinishedHandler(); + // Directive handlers + void asyncDirectiveHandler(const AsyncDirective& aDirective); + void syncDirectiveHandler(const SyncDirective& sDirective); signals: void longTaskCanceled(); diff --git a/app/src/kernel/core.cpp b/app/src/kernel/core.cpp index 827b530..2ca012b 100644 --- a/app/src/kernel/core.cpp +++ b/app/src/kernel/core.cpp @@ -53,47 +53,23 @@ QString CoreError::derivePrimary() const { return ERR_STRINGS.value(mType); } QString CoreError::deriveSecondary() const { return mSpecific; } //=============================================================================================================== -// CORE +// Core //=============================================================================================================== //-Constructor------------------------------------------------------------- +//Public: Core::Core(QObject* parent) : QObject(parent), - mCriticalErrorOccurred(false), + Directorate(&mDirector), + mServicesMode(ServicesMode::Standalone), mStatusHeading(u"Initializing"_s), - mStatusMessage(u"..."_s), - mServicesMode(ServicesMode::Standalone) -{ - establishCanonCore(*this); // Ignore return value as there should never be more than one Core with current design -} - -//-Class Functions------------------------------------------------------------------------------------------------------ -//Private: -bool Core::establishCanonCore(Core& cc) -{ - if(!smDefaultMessageHandler) - smDefaultMessageHandler = qInstallMessageHandler(qtMessageHandler); - - if(smCanonCore) - return false; - - smCanonCore = &cc; - return true; -} - -void Core::qtMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) -{ - // Log messages - if(smCanonCore && smCanonCore->isLogOpen()) - smCanonCore->logQtMessage(type, context, msg); - - // Defer to default behavior - if(smDefaultMessageHandler) - smDefaultMessageHandler(type, context, msg); -} + mStatusMessage(u"..."_s) +{} //-Instance Functions------------------------------------------------------------- //Private: +QString Core::name() const { return NAME; } + bool Core::isActionableOptionSet(const QCommandLineParser& clParser) const { QSet::const_iterator i; @@ -139,13 +115,13 @@ void Core::showHelp() } // Show help - postMessage(Message{.text = helpStr}); + postDirective(helpStr); } void Core::showVersion() { setStatus(STATUS_DISPLAY, STATUS_DISPLAY_VERSION); - postMessage(Message{.text = CL_VERSION_MESSAGE}); + postDirective(CL_VERSION_MESSAGE); } Qx::Error Core::searchAndFilterEntity(QUuid& returnBuffer, QString name, bool exactName, QUuid parent) @@ -167,7 +143,7 @@ Qx::Error Core::searchAndFilterEntity(QUuid& returnBuffer, QString name, bool ex if((searchError = mFlashpointInstall->database()->queryEntrys(searchResult, filter)).isValid()) { - postError(searchError); + postDirective(searchError); return searchError; } @@ -176,7 +152,7 @@ Qx::Error Core::searchAndFilterEntity(QUuid& returnBuffer, QString name, bool ex if(searchResult.size < 1) { CoreError err(CoreError::TitleNotFound, name); - postError(err); + postDirective(err); return err; } else if(searchResult.size == 1) @@ -197,7 +173,7 @@ Qx::Error Core::searchAndFilterEntity(QUuid& returnBuffer, QString name, bool ex else if (searchResult.size > FIND_ENTRY_LIMIT) { CoreError err(CoreError::TooManyResults, name); - postError(err); + postDirective(err); return err; } else @@ -233,12 +209,13 @@ Qx::Error Core::searchAndFilterEntity(QUuid& returnBuffer, QString name, bool ex } // Get user choice - Core::ItemSelectionRequest isr{ + QString userChoice = idChoices.front(); // Default + postDirective(DItemSelection{ .caption = MULTI_TITLE_SEL_CAP, .label = MULTI_TITLE_SEL_LABEL, - .items = idChoices - }; - QString userChoice = requestItemSelection(isr); + .items = idChoices, + .response = &userChoice + }); if(userChoice.isNull()) logEvent(LOG_EVENT_TITLE_SEL_CANCELED); @@ -253,50 +230,6 @@ Qx::Error Core::searchAndFilterEntity(QUuid& returnBuffer, QString name, bool ex } } -void Core::logQtMessage(QtMsgType type, const QMessageLogContext& context, const QString& msg) -{ -#if defined QT_NO_MESSAGELOGCONTEXT || !defined QT_MESSAGELOGCONTEXT - QString msgWithContext = msg; -#else - static const QString cTemplate = u"(%1:%2, %3) %4"_s; - static const QString unk = u"Unk."_s; - QString msgWithContext = cTemplate.arg( - context.file ? QString(context.file) : unk, - context.line >= 0 ? QString::number(context.line) : unk, - context.function ? QString(context.function) : unk, - msg - ); -#endif - - switch (type) - { - case QtDebugMsg: - logEvent(u"SYSTEM DEBUG) "_s + msgWithContext); - break; - case QtInfoMsg: - logEvent(u"SYSTEM INFO) "_s + msgWithContext); - break; - case QtWarningMsg: - logError(CoreError(CoreError::InternalError, msgWithContext, Qx::Warning)); - break; - case QtCriticalMsg: - logError(CoreError(CoreError::InternalError, msgWithContext, Qx::Err)); - break; - case QtFatalMsg: - logError(CoreError(CoreError::InternalError, msgWithContext, Qx::Critical)); - break; - } -} - -void Core::logCommand(const QString& commandName) { logCommand(NAME, commandName); } -void Core::logCommandOptions(const QString& commandOptions) { logCommandOptions(NAME, commandOptions); } -void Core::logError(const Qx::Error& error) { logError(NAME, error); } -void Core::logEvent(const QString& event) { logEvent(NAME, event); } -void Core::logTask(const Task* task) { logTask(NAME, task); } -ErrorCode Core::logFinish(const Qx::Error& errorState) { return logFinish(NAME, errorState); } -void Core::postError(const Qx::Error& error, bool log) { postError(NAME, error, log); } -int Core::postBlockingError(const Qx::Error& error, bool log, QMessageBox::StandardButtons bs, QMessageBox::StandardButton def) { return postBlockingError(NAME, error, log, bs, def); } - //Public: Qx::Error Core::initialize(QStringList& commandLine) { @@ -332,18 +265,8 @@ Qx::Error Core::initialize(QStringList& commandLine) // Remove app name from command line string commandLine.removeFirst(); - // Create logger instance - QString logPath = CLIFP_DIR_PATH + '/' + CLIFP_CUR_APP_BASENAME + '.' + LOG_FILE_EXT; - mLogger = std::make_unique(logPath); - mLogger->setApplicationName(PROJECT_SHORT_NAME); - mLogger->setApplicationVersion(PROJECT_VERSION_STR); - mLogger->setApplicationArguments(commandLine); - mLogger->setMaximumEntries(LOG_MAX_ENTRIES); - // Open log - Qx::IoOpReport logOpen = mLogger->openLog(); - if(logOpen.isFailure()) - postError(Qx::Error(logOpen).setSeverity(Qx::Warning), false); + mDirector.openLog(commandLine); // Log initialization step logEvent(LOG_EVENT_INIT); @@ -358,14 +281,14 @@ Qx::Error Core::initialize(QStringList& commandLine) showHelp(); CoreError err(CoreError::InvalidOptions, clParser.errorText()); - postError(err); + postDirective(err); return err; } // Handle each global option - mNotificationVerbosity = clParser.isSet(CL_OPTION_SILENT) ? NotificationVerbosity::Silent : - clParser.isSet(CL_OPTION_QUIET) ? NotificationVerbosity::Quiet : NotificationVerbosity::Full; - logEvent(LOG_EVENT_NOTIFCATION_LEVEL.arg(ENUM_NAME(mNotificationVerbosity))); + Director::Verbosity v = clParser.isSet(CL_OPTION_SILENT) ? Director::Verbosity::Silent : + clParser.isSet(CL_OPTION_QUIET) ? Director::Verbosity::Quiet : Director::Verbosity::Full; + mDirector.setVerbosity(v); if(clParser.isSet(CL_OPTION_VERSION)) { @@ -422,7 +345,7 @@ void Core::watchLauncher() connect(&mLauncherWatcher, &Qx::ProcessBider::finished, this, [this]{ // Launcher closed (or can't be hooked), need to bail CoreError err(CoreError::CompanionModeLauncherClose, LOG_EVENT_LAUNCHER_CLOSED_RESULT); - postError(err); + postDirective(err); emit abort(err); }); @@ -569,7 +492,7 @@ CoreError Core::enqueueStartupTasks(const QString& serverOverride) if(mFlashpointInstall->outfittedDaemon() == Fp::Daemon::Docker) { - TExec* xhostSet = new TExec(this); + TExec* xhostSet = new TExec(*this); xhostSet->setIdentifier(u"xhost Set"_s); xhostSet->setStage(Task::Stage::Startup); xhostSet->setExecutable(u"xhost"_s); @@ -594,7 +517,7 @@ CoreError Core::enqueueStartupTasks(const QString& serverOverride) auto wineEnv = TExec::defaultProcessEnvironment(); wineEnv.insert(u"WINEDLLOVERRIDES"_s, u"control.exe,explorer.exe,mscoree,plugplay.exe,services.exe,winedevice.exe,winemenubuilder.exe=d"_s); - TExec* wineReg = new TExec(this); + TExec* wineReg = new TExec(*this); wineReg->setIdentifier(u"WINE Registry Setup"_s); wineReg->setStage(Task::Stage::Startup); wineReg->setExecutable(u"wine"_s); @@ -628,7 +551,7 @@ CoreError Core::enqueueStartupTasks(const QString& serverOverride) // Add Start entries from services for(const Fp::StartStop& startEntry : qAsConst(fpServices.start)) { - TExec* currentTask = new TExec(this); + TExec* currentTask = new TExec(*this); currentTask->setIdentifier(startEntry.filename); currentTask->setStage(Task::Stage::Startup); currentTask->setExecutable(startEntry.filename); @@ -647,13 +570,13 @@ CoreError Core::enqueueStartupTasks(const QString& serverOverride) if(!foundServer) { CoreError err(CoreError::ConfiguredServerMissing); - postError(err); + postDirective(err); return err; } Fp::ServerDaemon server = foundServer.value(); - TExec* serverTask = new TExec(this); + TExec* serverTask = new TExec(*this); serverTask->setIdentifier(u"Server"_s); serverTask->setStage(Task::Stage::Startup); serverTask->setExecutable(server.filename); @@ -668,7 +591,7 @@ CoreError Core::enqueueStartupTasks(const QString& serverOverride) // Add Daemon entry from services for(const Fp::ServerDaemon& d : qAsConst(fpServices.daemon)) { - TExec* currentTask = new TExec(this); + TExec* currentTask = new TExec(*this); currentTask->setIdentifier(u"Daemon"_s); currentTask->setStage(Task::Stage::Startup); currentTask->setExecutable(d.filename); @@ -684,7 +607,7 @@ CoreError Core::enqueueStartupTasks(const QString& serverOverride) // On Linux the startup tasks take a while so make sure the docker image is actually running before proceeding if(mFlashpointInstall->outfittedDaemon() == Fp::Daemon::Docker) { - TAwaitDocker* dockerWait = new TAwaitDocker(this); + TAwaitDocker* dockerWait = new TAwaitDocker(*this); dockerWait->setStage(Task::Stage::Startup); // NOTE: Other than maybe picking it out of the 2nd argument of the stop docker StartStop, there's no clean way to get this name dockerWait->setImageName(u"gamezip"_s); @@ -700,7 +623,7 @@ CoreError Core::enqueueStartupTasks(const QString& serverOverride) * This is especially important for docker, as the mount server inside seems to take an extra moment to initialize (gives * "Connection Closed" if a mount attempt is made right away). */ - TSleep* initDelay = new TSleep(this); + TSleep* initDelay = new TSleep(*this); initDelay->setStage(Task::Stage::Startup); initDelay->setDuration(1500); // NOTE: Might need to be made longer @@ -724,7 +647,7 @@ void Core::enqueueShutdownTasks() // Add Stop entries from services for(const Fp::StartStop& stopEntry : qxAsConst(mFlashpointInstall->services().stop)) { - TExec* shutdownTask = new TExec(this); + TExec* shutdownTask = new TExec(*this); shutdownTask->setIdentifier(stopEntry.filename); shutdownTask->setStage(Task::Stage::Shutdown); shutdownTask->setExecutable(stopEntry.filename); @@ -740,7 +663,7 @@ void Core::enqueueShutdownTasks() // Undo xhost permissions modifications related to docker if(mFlashpointInstall->outfittedDaemon() == Fp::Daemon::Docker) { - TExec* xhostClear = new TExec(this); + TExec* xhostClear = new TExec(*this); xhostClear->setIdentifier(u"xhost Clear"_s); xhostClear->setStage(Task::Stage::Shutdown); xhostClear->setExecutable(u"xhost"_s); @@ -757,7 +680,7 @@ void Core::enqueueShutdownTasks() QDir saveSrc(mFlashpointInstall->dir().absoluteFilePath(u"FPSoftware/Wine/drive_c/users/"_s + uname + u"/AppData/Roaming/Macromedia/Flash Player/#SharedObjects"_s)); QDir saveDest(mFlashpointInstall->dir().absoluteFilePath(u"FPSoftware/.winebak/drive_c/users/"_s + uname + u"/AppData/Roaming/Macromedia/Flash Player"_s)); - TGeneric* wineSaveBackup = new TGeneric(this); + TGeneric* wineSaveBackup = new TGeneric(*this); wineSaveBackup->setStage(Task::Stage::Shutdown); wineSaveBackup->setDescription(u"Backup Flash WINE saves"_s); wineSaveBackup->setAction([saveSrc, saveDest]{ @@ -780,13 +703,13 @@ Qx::Error Core::conditionallyEnqueueBideTask(QFileInfo precedingAppInfo) Qx::Error securePlayerCheckError = tk->appInvolvesSecurePlayer(involvesSecurePlayer, precedingAppInfo); if(securePlayerCheckError.isValid()) { - postError(securePlayerCheckError); + postDirective(securePlayerCheckError); return securePlayerCheckError; } if(involvesSecurePlayer) { - TBideProcess* waitTask = new TBideProcess(this); + TBideProcess* waitTask = new TBideProcess(*this); waitTask->setStage(Task::Stage::Auxiliary); waitTask->setProcessName(tk->SECURE_PLAYER_INFO.fileName()); @@ -816,13 +739,13 @@ Qx::Error Core::enqueueDataPackTasks(const Fp::GameData& gameData) { logEvent(LOG_EVENT_DATA_PACK_MISS); - TDownload* downloadTask = new TDownload(this); + TDownload* downloadTask = new TDownload(*this); downloadTask->setStage(Task::Stage::Auxiliary); downloadTask->setDescription(u"data pack "_s + packFilename); TDownloadError packError = downloadTask->addDatapack(tk, &gameData); if(packError.isValid()) { - postError(packError); + postDirective(packError); return packError; } @@ -832,7 +755,7 @@ Qx::Error Core::enqueueDataPackTasks(const Fp::GameData& gameData) // Add task to update DB with onDiskState int gameDataId = gameData.id(); - TGeneric* onDiskUpdateTask = new TGeneric(this); + TGeneric* onDiskUpdateTask = new TGeneric(*this); onDiskUpdateTask->setStage(Task::Stage::Auxiliary); onDiskUpdateTask->setDescription(u"Update GameData onDisk state."_s); onDiskUpdateTask->setAction([gameDataId, this]{ @@ -848,7 +771,7 @@ Qx::Error Core::enqueueDataPackTasks(const Fp::GameData& gameData) // Handle datapack parameters Fp::GameDataParameters param = gameData.parameters(); if(param.hasError()) - postError(CoreError(CoreError::UnknownDatapackParam, param.errorString(), Qx::Warning)); + postDirective(CoreError(CoreError::UnknownDatapackParam, param.errorString(), Qx::Warning)); if(param.isExtract()) { @@ -861,7 +784,7 @@ Qx::Error Core::enqueueDataPackTasks(const Fp::GameData& gameData) logEvent(LOG_EVENT_DATA_PACK_ALREADY_EXTRACTED); else { - TExtract* extractTask = new TExtract(this); + TExtract* extractTask = new TExtract(*this); extractTask->setStage(Task::Stage::Auxiliary); extractTask->setPackPath(packPath); extractTask->setPathInPack(u"content"_s); @@ -876,7 +799,7 @@ Qx::Error Core::enqueueDataPackTasks(const Fp::GameData& gameData) logEvent(LOG_EVENT_DATA_PACK_NEEDS_MOUNT); // Create task - TMount* mountTask = new TMount(this); + TMount* mountTask = new TMount(*this); mountTask->setStage(Task::Stage::Auxiliary); mountTask->setTitleId(gameData.gameId()); mountTask->setPath(packPath); @@ -892,168 +815,10 @@ Qx::Error Core::enqueueDataPackTasks(const Fp::GameData& gameData) void Core::enqueueSingleTask(Task* task) { mTaskQueue.push(task); logTask(task); } -bool Core::isLogOpen() const { return mLogger->isOpen(); } - -void Core::logCommand(const QString& src, const QString& commandName) -{ - Qx::IoOpReport logReport = mLogger->recordGeneralEvent(src, COMMAND_LABEL.arg(commandName)); - if(logReport.isFailure()) - postError(src, Qx::Error(logReport).setSeverity(Qx::Warning), false); -} - -void Core::logCommandOptions(const QString& src, const QString& commandOptions) -{ - Qx::IoOpReport logReport = mLogger->recordGeneralEvent(src, COMMAND_OPT_LABEL.arg(commandOptions)); - if(logReport.isFailure()) - postError(src, Qx::Error(logReport).setSeverity(Qx::Warning), false); -} - -void Core::logError(const QString& src, const Qx::Error& error) -{ - Qx::IoOpReport logReport = mLogger->recordErrorEvent(src, error); - - if(logReport.isFailure()) - postError(src, Qx::Error(logReport).setSeverity(Qx::Warning), false); - - if(error.severity() == Qx::Critical) - mCriticalErrorOccurred = true; -} - -void Core::logEvent(const QString& src, const QString& event) -{ - Qx::IoOpReport logReport = mLogger->recordGeneralEvent(src, event); - if(logReport.isFailure()) - postError(src, Qx::Error(logReport).setSeverity(Qx::Warning), false); -} - -void Core::logTask(const QString& src, const Task* task) { logEvent(src, LOG_EVENT_TASK_ENQ.arg(task->name(), task->members().join(u", "_s))); } - -ErrorCode Core::logFinish(const QString& src, const Qx::Error& errorState) -{ - if(mCriticalErrorOccurred) - logEvent(src, LOG_ERR_CRITICAL); - - ErrorCode code = errorState.typeCode(); - - Qx::IoOpReport logReport = mLogger->finish(code); - if(logReport.isFailure()) - postError(src, Qx::Error(logReport).setSeverity(Qx::Warning), false); - - // Return exit code so main function can return with this one - return code; -} - -void Core::postError(const QString& src, const Qx::Error& error, bool log) -{ - // Logging - if(log) - logError(src, error); - - // Show error if applicable - if(mNotificationVerbosity == NotificationVerbosity::Full || - (mNotificationVerbosity == NotificationVerbosity::Quiet && error.severity() == Qx::Critical)) - { - // Box error - Error e; - e.source = src; - e.errorInfo = error; - - // Emit - emit errorOccurred(e); - } -} - -int Core::postBlockingError(const QString& src, const Qx::Error& error, bool log, QMessageBox::StandardButtons bs, QMessageBox::StandardButton def) -{ - // Logging - if(log) - logError(src, error); - - // Show error if applicable - if(mNotificationVerbosity == NotificationVerbosity::Full || - (mNotificationVerbosity == NotificationVerbosity::Quiet && error.severity() == Qx::Critical)) - { - // Box error - BlockingError be; - be.source = src; - be.errorInfo = error; - be.choices = bs; - be.defaultChoice = def; - - // Response holder - QSharedPointer response = QSharedPointer::create(def); - - // Emit and get response - emit blockingErrorOccurred(response, be); - - // Return response - return *response; - } - else - return def; -} - -void Core::postMessage(const Message& msg) { emit message(msg); } - -QString Core::requestSaveFilePath(const SaveFileRequest& request) -{ - // Response holder - QSharedPointer file = QSharedPointer::create(); - - // Emit and get response - emit saveFileRequested(file, request); - - // Return response - return *file; -} - -QString Core::requestExistingDirPath(const ExistingDirRequest& request) -{ - // Response holder - QSharedPointer dir = QSharedPointer::create(); - - // Emit and get response - emit existingDirRequested(dir, request); - - // Return response - return *dir; -} - -QString Core::requestItemSelection(const ItemSelectionRequest& request) -{ - // Response holder - QSharedPointer item = QSharedPointer::create(); - - // Emit and get response - emit itemSelectionRequested(item, request); - - // Return response - return *item; -} - -void Core::requestClipboardUpdate(const QString& text) { emit clipboardUpdateRequested(text); } - -bool Core::requestQuestionAnswer(const QString& question) -{ - // Show question if allowed - if(mNotificationVerbosity != NotificationVerbosity::Silent) - { - // Response holder - QSharedPointer response = QSharedPointer::create(false); - - // Emit and get response - emit questionAnswerRequested(response, question); - - // Return response - return *response; - } - else - return false; // Assume "No" -} - +Director* Core::director() { return &mDirector; } +Core::ServicesMode Core::mode() const { return mServicesMode; } Fp::Install& Core::fpInstall() { return *mFlashpointInstall; } const QProcessEnvironment& Core::childTitleProcessEnvironment() { return mChildTitleProcEnv; } -Core::NotificationVerbosity Core::notifcationVerbosity() const { return mNotificationVerbosity; } size_t Core::taskCount() const { return mTaskQueue.size(); } bool Core::hasTasks() const { return mTaskQueue.size() > 0; } Task* Core::frontTask() { return mTaskQueue.front(); } @@ -1063,9 +828,12 @@ QString Core::statusHeading() { return mStatusHeading; } QString Core::statusMessage() { return mStatusMessage;} void Core::setStatus(QString heading, QString message) { + /* TODO: Probably can do away with this and just use postDirective() where it's needed. + * The stored status is never used currently and I can't think of any reason it would b + */ mStatusHeading = heading; mStatusMessage = message; - emit statusChanged(heading, message); + postDirective(heading, message); } BuildInfo Core::buildInfo() const diff --git a/app/src/kernel/core.h b/app/src/kernel/core.h index fefae10..a3819e0 100644 --- a/app/src/kernel/core.h +++ b/app/src/kernel/core.h @@ -14,7 +14,6 @@ #include // Qx Includes -#include #include #include @@ -22,12 +21,10 @@ #include // Project Includes +#include "kernel/buildinfo.h" +#include "kernel/directorate.h" #include "task/task.h" #include "project_vars.h" -#include "kernel/buildinfo.h" - -// General Aliases -using ErrorCode = quint32; class QX_ERROR_TYPE(CoreError, "CoreError", 1200) { @@ -37,7 +34,6 @@ class QX_ERROR_TYPE(CoreError, "CoreError", 1200) enum Type { NoError, - InternalError, CompanionModeLauncherClose, CompanionModeServerOverride, InvalidOptions, @@ -51,7 +47,6 @@ class QX_ERROR_TYPE(CoreError, "CoreError", 1200) private: static inline const QHash ERR_STRINGS{ {NoError, u""_s}, - {InternalError, u"Internal error."_s}, {CompanionModeLauncherClose, u"The standard launcher was closed while in companion mode."_s}, {CompanionModeServerOverride, u"Cannot enact game server override in companion mode."_s}, {InvalidOptions, u"Invalid global options provided."_s}, @@ -84,57 +79,13 @@ class QX_ERROR_TYPE(CoreError, "CoreError", 1200) QString deriveSecondary() const override; }; -class Core : public QObject +class Core : public QObject, public Directorate { Q_OBJECT; //-Class Enums----------------------------------------------------------------------- public: - enum class NotificationVerbosity { Full, Quiet, Silent }; enum ServicesMode { Standalone, Companion }; -//-Class Structs--------------------------------------------------------------------- -public: - /* TODO: These should be made their own files like message.h is in frontend - * (or one file, like "requests.h"), or message.h should be removed with its - * struct moved to here - */ - struct Error - { - QString source; - Qx::Error errorInfo; - }; - - struct BlockingError - { - QString source; - Qx::Error errorInfo; - QMessageBox::StandardButtons choices; - QMessageBox::StandardButton defaultChoice; - }; - - struct SaveFileRequest - { - QString caption; - QString dir; - QString filter; - QString* selectedFilter = nullptr; - QFileDialog::Options options; - }; - - struct ExistingDirRequest - { - QString caption; - QString dir; - QFileDialog::Options options = QFileDialog::ShowDirsOnly; - }; - - struct ItemSelectionRequest - { - QString caption; - QString label; - QStringList items; - }; - //-Class Variables------------------------------------------------------------------------------------------------------ public: // Single Instance ID @@ -145,18 +96,12 @@ class Core : public QObject static inline const QString STATUS_DISPLAY_HELP = u"Help"_s; static inline const QString STATUS_DISPLAY_VERSION = u"Version"_s; - // Logging - Primary Labels - static inline const QString COMMAND_LABEL = u"Command: %1"_s; - static inline const QString COMMAND_OPT_LABEL = u"Command Options: %1"_s; - // Logging - Primary Values - static inline const QString LOG_FILE_EXT = u"log"_s; + static inline const QString LOG_NO_PARAMS = u"*None*"_s; - static const int LOG_MAX_ENTRIES = 50; // Logging - Errors static inline const QString LOG_ERR_INVALID_PARAM = u"Invalid parameters provided"_s; - static inline const QString LOG_ERR_CRITICAL = u"Aborting execution due to previous critical errors"_s; static inline const QString LOG_ERR_FAILED_SETTING_RUFFLE_PERMS= u"Failed to mark ruffle as executable!"_s; // Logging - Messages @@ -167,7 +112,6 @@ class Core : public QObject static inline const QString LOG_EVENT_FURTHER_INSTANCE_BLOCK_FAIL = u"Failed to lock standard instance count"_s; static inline const QString LOG_EVENT_G_HELP_SHOWN = u"Displayed general help information"_s; static inline const QString LOG_EVENT_VER_SHOWN = u"Displayed version information"_s; - static inline const QString LOG_EVENT_NOTIFCATION_LEVEL = u"Notification Level is: %1"_s; static inline const QString LOG_EVENT_PROTOCOL_FORWARD = u"Delegated protocol request to 'play'"_s; static inline const QString LOG_EVENT_FLASHPOINT_VERSION_TXT = u"Flashpoint version.txt: %1"_s; static inline const QString LOG_EVENT_FLASHPOINT_VERSION = u"Flashpoint version: %1"_s; @@ -182,7 +126,6 @@ class Core : public QObject static inline const QString LOG_EVENT_DATA_PACK_NEEDS_MOUNT = u"Title Data Pack requires mounting"_s; static inline const QString LOG_EVENT_DATA_PACK_NEEDS_EXTRACT = u"Title Data Pack requires extraction"_s; static inline const QString LOG_EVENT_DATA_PACK_ALREADY_EXTRACTED = u"Extracted files already present"_s; - static inline const QString LOG_EVENT_TASK_ENQ = u"Enqueued %1: {%2}"_s; static inline const QString LOG_EVENT_APP_PATH_ALT = u"App path \"%1\" maps to alternative \"%2\"."_s; static inline const QString LOG_EVENT_SERVICES_FROM_LAUNCHER = u"Using services from standard Launcher due to companion mode."_s; static inline const QString LOG_EVENT_LAUNCHER_WATCH = u"Starting bide on Launcher process..."_s; @@ -259,20 +202,16 @@ class Core : public QObject // Meta static inline const QString NAME = u"core"_s; - // Qt Message Handling - static inline constinit QtMessageHandler smDefaultMessageHandler = nullptr; - static inline QPointer smCanonCore; - //-Instance Variables------------------------------------------------------------------------------------------------------ private: + // Director + Director mDirector; + // Handles std::unique_ptr mFlashpointInstall; - std::unique_ptr mLogger; // Processing ServicesMode mServicesMode; - bool mCriticalErrorOccurred; - NotificationVerbosity mNotificationVerbosity; std::queue mTaskQueue; // Info @@ -287,35 +226,15 @@ class Core : public QObject public: explicit Core(QObject* parent); -//-Class Functions------------------------------------------------------------------------------------------------------ -private: - // Qt Message Handling - NOTE: Storing a static instance of core is required due to the C-function pointer interface of qInstallMessageHandler() - static bool establishCanonCore(Core& cc); - static void qtMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg); - //-Instance Functions------------------------------------------------------------------------------------------------------ private: + QString name() const override; bool isActionableOptionSet(const QCommandLineParser& clParser) const; void showHelp(); void showVersion(); // Helper Qx::Error searchAndFilterEntity(QUuid& returnBuffer, QString name, bool exactName, QUuid parent = QUuid()); - void logQtMessage(QtMsgType type, const QMessageLogContext& context, const QString& msg); - - /* TODO: See if instead of repeating these with auto-source overloads everywhere if instead a template function can be made that just works - * in all places where core is available. This would likely require a public ::NAME static member for each type that uses core, though this - * would be tricky for the tasks that emit signals instead of using core directly. - */ - // Notifications/Logging (self-forwarders) - void logCommand(const QString& commandName); - void logCommandOptions(const QString& commandOptions); - void logError(const Qx::Error& error); - void logEvent(const QString& event); - void logTask(const Task* task); - ErrorCode logFinish(const Qx::Error& errorState); - void postError(const Qx::Error& error, bool log = true); - int postBlockingError(const Qx::Error& error, bool log = true, QMessageBox::StandardButtons bs = QMessageBox::Ok, QMessageBox::StandardButton def = QMessageBox::NoButton); public: // Setup @@ -339,31 +258,11 @@ class Core : public QObject Qx::Error enqueueDataPackTasks(const Fp::GameData& gameData); void enqueueSingleTask(Task* task); - // Notifications/Logging - /* TODO: Within each place that uses the log options that need the src parameter, like the Commands, and maybe even Core itself, add methods - * with the same names that call mCore.logX(NAME, ...) automatically so that NAME doesn't need to be passed every time - */ - bool isLogOpen() const; - void logCommand(const QString& src, const QString& commandName); - void logCommandOptions(const QString& src, const QString& commandOptions); - void logError(const QString& src, const Qx::Error& error); - void logEvent(const QString& src, const QString& event); - void logTask(const QString& src, const Task* task); - ErrorCode logFinish(const QString& src, const Qx::Error& errorState); - void postError(const QString& src, const Qx::Error& error, bool log = true); - int postBlockingError(const QString& src, const Qx::Error& error, bool log = true, QMessageBox::StandardButtons bs = QMessageBox::Ok, QMessageBox::StandardButton def = QMessageBox::NoButton); - void postMessage(const Message& msg); - QString requestSaveFilePath(const SaveFileRequest& request); - QString requestExistingDirPath(const ExistingDirRequest& request); - QString requestItemSelection(const ItemSelectionRequest& request); - void requestClipboardUpdate(const QString& text); - bool requestQuestionAnswer(const QString& question); - // Member access + Director* director(); ServicesMode mode() const; Fp::Install& fpInstall(); const QProcessEnvironment& childTitleProcessEnvironment(); - NotificationVerbosity notifcationVerbosity() const; size_t taskCount() const; bool hasTasks() const; Task* frontTask(); @@ -379,22 +278,7 @@ class Core : public QObject //-Signals & Slots------------------------------------------------------------------------------------------------------------ signals: - void statusChanged(const QString& statusHeading, const QString& statusMessage); - void errorOccurred(const Core::Error& error); - void blockingErrorOccurred(QSharedPointer response, const Core::BlockingError& blockingError); - void saveFileRequested(QSharedPointer file, const Core::SaveFileRequest& request); - void existingDirRequested(QSharedPointer dir, const Core::ExistingDirRequest& request); - void itemSelectionRequested(QSharedPointer item, const Core::ItemSelectionRequest& request); - void message(const Message& message); - void clipboardUpdateRequested(const QString& text); - void questionAnswerRequested(QSharedPointer response, const QString& question); - - // Driver specific void abort(CoreError err); }; -//-Metatype Declarations----------------------------------------------------------------------------------------- -Q_DECLARE_METATYPE(Core::Error); -Q_DECLARE_METATYPE(Core::BlockingError); - #endif // CORE_H diff --git a/app/src/kernel/directive.h b/app/src/kernel/directive.h new file mode 100644 index 0000000..74668e8 --- /dev/null +++ b/app/src/kernel/directive.h @@ -0,0 +1,160 @@ +#ifndef DIRECTIVE_H +#define DIRECTIVE_H + +// Qt Includes +#include + +// Qx Includes +#include + +/* TODO: + * + * In this file there are some structs with redundant members where one could easily conceive + * that they should instead be part of an inheritance chain (i.e. DBlockingError inheriting + * from DError); however, C++ currently does not allow using designated-initializers for + * base classes, so even though with this inheritance chain: + * + * struct A { int one; } + * struct B { int two; } + * + * You can do: + * + * B b; b.one = 1; b.two = 2; + * + * You cannot do: + * B b{ .one =1, b.two = 2; } + * + * We very much would like to support the latter syntax for all directive creation, so that means + * we cannot use base classes for them. P2287 has be proposed to allow this: + * https://github.com/cplusplus/papers/issues/978 + * + * If it's accepted, once it's standardized we can switch to using it, but that won't be for some time, + * so in the meanwhile we keep the redundant members and use template functions where a function + * could have instead taken a pointer to base. + */ + +//-Non-blocking Directives----------------------------------------------------------------- +struct DMessage +{ + QString text; + bool selectable = false; +}; + +struct DError +{ + Qx::Error error; +}; + +struct DProcedureStart +{ + QString label; +}; + +struct DProcedureStop {}; + +struct DProcedureProgress +{ + quint64 current; +}; + +struct DProcedureScale +{ + quint64 max; +}; + +struct DClipboardUpdate +{ + QString text; +}; + +struct DStatusUpdate +{ + QString heading; + QString message; +}; + +using AsyncDirective = std::variant< + DMessage, + DError, + DProcedureStart, + DProcedureStop, + DProcedureProgress, + DProcedureScale, + DClipboardUpdate, + DStatusUpdate +>; + +template +concept AsyncDirectiveT = requires(AsyncDirective ad, T t) { ad = t; }; + +//-Blocking Directives--------------------------------------------------------------------- +struct DBlockingMessage +{ + QString text; + bool selectable = false; +}; + +struct DBlockingError +{ + enum class Choice {Ok, Yes, No}; + Q_DECLARE_FLAGS(Choices, Choice); + + Qx::Error error; + Choices choices = Choice::Ok; + Choice defaultChoice = Choice::No; + Choice* response = nullptr; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(DBlockingError::Choices); + +struct DSaveFilename +{ + QString caption; + QString dir; + QString filter; + QString* selectedFilter = nullptr; + QString* response = nullptr; +}; + +struct DExistingDir +{ + QString caption; + QString startingDir; + QString* response = nullptr; + // TODO: Make sure to use QFileDialog::ShowDirsOnly on receiving end +}; + +struct DItemSelection +{ + QString caption; + QString label; + QStringList items; + QString* response = nullptr; +}; + +struct DYesOrNo +{ + QString question; + bool* response = nullptr; +}; + +using SyncDirective = std::variant< + DBlockingMessage, + DBlockingError, + DSaveFilename, + DExistingDir, + DItemSelection, + DYesOrNo +>; + +template +concept SyncDirectiveT = requires(SyncDirective sd, T t) { sd = t; }; + +//-Any--------------------------------------------------------------------- +template +concept DirectiveT = AsyncDirectiveT || SyncDirectiveT; + +//-Metatype Declarations----------------------------------------------------------------------------------------- +Q_DECLARE_METATYPE(AsyncDirective); +Q_DECLARE_METATYPE(SyncDirective); + +#endif // DIRECTIVE_H diff --git a/app/src/kernel/director.cpp b/app/src/kernel/director.cpp new file mode 100644 index 0000000..8291e2d --- /dev/null +++ b/app/src/kernel/director.cpp @@ -0,0 +1,187 @@ +// Unit Include +#include "director.h" + +// Qt Includes +#include + +// Project Includes +#include "project_vars.h" +#include "utility.h" +#include "task/task.h" + +//=============================================================================================================== +// DirectorError +//=============================================================================================================== + +//-Constructor------------------------------------------------------------- +//Private: +DirectorError::DirectorError(Type t, const QString& s, Qx::Severity sv) : + mType(t), + mSpecific(s), + mSeverity(sv) +{} + +//-Instance Functions------------------------------------------------------------- +//Public: +bool DirectorError::isValid() const { return mType != NoError; } +QString DirectorError::specific() const { return mSpecific; } +DirectorError::Type DirectorError::type() const { return mType; } + +//Private: +Qx::Severity DirectorError::deriveSeverity() const { return mSeverity; } +quint32 DirectorError::deriveValue() const { return mType; } +QString DirectorError::derivePrimary() const { return ERR_STRINGS.value(mType); } +QString DirectorError::deriveSecondary() const { return mSpecific; } + +//=============================================================================================================== +// Director +//=============================================================================================================== + +//-Constructor------------------------------------------------------------- +//Public: +Director::Director() : + mLogger(CLIFP_DIR_PATH + '/' + CLIFP_CUR_APP_BASENAME + '.' + LOG_FILE_EXT), + mVerbosity(Verbosity::Full), + mCriticalErrorOccurred(false) +{ + bool established = establishCanonDirector(*this); + Q_ASSERT(established); // No reason for more than one Director currently + + mLogger.setApplicationName(PROJECT_SHORT_NAME); + mLogger.setApplicationVersion(PROJECT_VERSION_STR); + mLogger.setMaximumEntries(LOG_MAX_ENTRIES); +} + +//-Class Functions------------------------------------------------------------------------------------------------------ +//Private: +bool Director::establishCanonDirector(Director& cd) +{ + if(!smDefaultMessageHandler) + smDefaultMessageHandler = qInstallMessageHandler(qtMessageHandler); + + if(smCanonDirector) + return false; + + smCanonDirector = &cd; + return true; +} + +void Director::qtMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) +{ + // Log messages + if(smCanonDirector && smCanonDirector->isLogOpen()) + smCanonDirector->logQtMessage(type, context, msg); + + // Defer to default behavior + if(smDefaultMessageHandler) + smDefaultMessageHandler(type, context, msg); +} + +//-Instance Functions------------------------------------------------------------- +//Private: +template + requires Qx::defines_call_for_s +void Director::log(F logFunc) +{ + if(mLogger.hasError()) + return; + + if(auto r = logFunc(); r.isFailure()) + postDirective(NAME, DError{Qx::Error(r).setSeverity(Qx::Warning)}); +} + + +bool Director::isLogOpen() const { return mLogger.isOpen(); } + +void Director::logQtMessage(QtMsgType type, const QMessageLogContext& context, const QString& msg) +{ +#if defined QT_NO_MESSAGELOGCONTEXT || !defined QT_MESSAGELOGCONTEXT + QString msgWithContext = msg; +#else + static const QString cTemplate = u"(%1:%2, %3) %4"_s; + static const QString unk = u"Unk."_s; + QString msgWithContext = cTemplate.arg( + context.file ? QString(context.file) : unk, + context.line >= 0 ? QString::number(context.line) : unk, + context.function ? QString(context.function) : unk, + msg + ); +#endif + + switch (type) + { + case QtDebugMsg: + logEvent(NAME, u"SYSTEM DEBUG) "_s + msgWithContext); + break; + case QtInfoMsg: + logEvent(NAME, u"SYSTEM INFO) "_s + msgWithContext); + break; + case QtWarningMsg: + logError(NAME, DirectorError(DirectorError::InternalError, msgWithContext, Qx::Warning)); + break; + case QtCriticalMsg: + logError(NAME, DirectorError(DirectorError::InternalError, msgWithContext, Qx::Err)); + break; + case QtFatalMsg: + logError(NAME, DirectorError(DirectorError::InternalError, msgWithContext, Qx::Critical)); + break; + } +} + +//Public: +Director::Verbosity Director::verbosity() const { return mVerbosity; } +bool Director::criticalErrorOccurred() const { return mCriticalErrorOccurred; } + +void Director::openLog(const QStringList& arguments) +{ + mLogger.setApplicationArguments(arguments); + log([&](){ return mLogger.openLog(); }); +} + +void Director::setVerbosity(Verbosity verbosity) +{ + mVerbosity = verbosity; + logEvent(NAME, LOG_EVENT_NOTIFCATION_LEVEL.arg(ENUM_NAME(verbosity))); +} + +void Director::logCommand(const QString& src, const QString& commandName) +{ + log([&](){ return mLogger.recordGeneralEvent(src, COMMAND_LABEL.arg(commandName)); }); +} + +void Director::logCommandOptions(const QString& src, const QString& commandOptions) +{ + log([&](){ return mLogger.recordGeneralEvent(src, COMMAND_OPT_LABEL.arg(commandOptions)); }); +} + +void Director::logError(const QString& src, const Qx::Error& error) +{ + log([&](){ return mLogger.recordErrorEvent(src, error); }); + + if(error.severity() == Qx::Critical) + mCriticalErrorOccurred = true; +} + +void Director::logEvent(const QString& src, const QString& event) +{ + log([&](){ return mLogger.recordGeneralEvent(src, event); }); +} + + +// TODO: Have task have a toString function/operator instead of "members()" +void Director::logTask(const QString& src, const Task* task) { logEvent(src, LOG_EVENT_TASK_ENQ.arg(task->name(), task->members().join(u", "_s))); } + +ErrorCode Director::logFinish(const QString& src, const Qx::Error& errorState) +{ + if(mCriticalErrorOccurred) + logEvent(src, LOG_ERR_CRITICAL); + + ErrorCode code = errorState.typeCode(); + + log([&](){ return mLogger.finish(code); }); + + // Return exit code so main function can return with this one + return code; +} + + diff --git a/app/src/kernel/director.h b/app/src/kernel/director.h new file mode 100644 index 0000000..b16e6de --- /dev/null +++ b/app/src/kernel/director.h @@ -0,0 +1,168 @@ +#ifndef DIRECTOR_H +#define DIRECTOR_H + +// Qt Includes +#include +#include + +// Qx Includes +#include +#include + +// Project Includes +#include "kernel/directive.h" + +// General Aliases +using ErrorCode = quint32; + +class Task; + +class QX_ERROR_TYPE(DirectorError, "DirectorError", 1201) +{ + friend class Director; +//-Class Enums------------------------------------------------------------- +public: + enum Type + { + NoError, + InternalError + }; + +//-Class Variables------------------------------------------------------------- +private: + static inline const QHash ERR_STRINGS{ + {NoError, u""_s}, + {InternalError, u"Internal error."_s}, + }; + +//-Instance Variables------------------------------------------------------------- +private: + Type mType; + QString mSpecific; + Qx::Severity mSeverity; + +//-Constructor------------------------------------------------------------- +private: + DirectorError(Type t = NoError, const QString& s = {}, Qx::Severity sv = Qx::Critical); + +//-Instance Functions------------------------------------------------------------- +public: + bool isValid() const; + Type type() const; + QString specific() const; + +private: + Qx::Severity deriveSeverity() const override; + quint32 deriveValue() const override; + QString derivePrimary() const override; + QString deriveSecondary() const override; +}; + +class Director : public QObject +{ + Q_OBJECT; +//-Class Enums----------------------------------------------------------------------- +public: + enum class Verbosity { Full, Quiet, Silent }; + +//-Class Variables------------------------------------------------------------------------------------------------------ +private: + // Qt Message Handling + static inline constinit QtMessageHandler smDefaultMessageHandler = nullptr; + static inline QPointer smCanonDirector; + + // Logging + static const int LOG_MAX_ENTRIES = 50; + static inline const QString LOG_FILE_EXT = u"log"_s; + + // Logging - Primary Labels + static inline const QString COMMAND_LABEL = u"Command: %1"_s; + static inline const QString COMMAND_OPT_LABEL = u"Command Options: %1"_s; + + // Logging - Messages + static inline const QString LOG_EVENT_NOTIFCATION_LEVEL = u"Notification Level is: %1"_s; + static inline const QString LOG_EVENT_TASK_ENQ = u"Enqueued %1: {%2}"_s; + + // Logging - Errors + static inline const QString LOG_ERR_CRITICAL = u"Aborting execution due to previous critical errors"_s; + + // Meta + static inline const QString NAME = u"Director"_s; + +//-Instance Variables------------------------------------------------------------------------------------------------------ +private: + Qx::ApplicationLogger mLogger; + Verbosity mVerbosity; + bool mCriticalErrorOccurred; + +//-Constructor------------------------------------------------------------------------------------------------------------ +public: + Director(); + +//-Class Functions------------------------------------------------------------------------------------------------------ +private: + // Qt Message Handling - NOTE: Storing a static instance of director is required due to the C-function pointer interface of qInstallMessageHandler() + static bool establishCanonDirector(Director& cd); + static void qtMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg); + +//-Instance Functions------------------------------------------------------------------------------------------------------ +private: + // Logging + template + requires Qx::defines_call_for_s + void log(F logFunc); + + bool isLogOpen() const; + void logQtMessage(QtMsgType type, const QMessageLogContext& context, const QString& msg); + +public: + // Data + Verbosity verbosity() const; + bool criticalErrorOccurred() const; + + // Logging + void openLog(const QStringList& arguments); + void setVerbosity(Verbosity verbosity); + void logCommand(const QString& src, const QString& commandName); + void logCommandOptions(const QString& src, const QString& commandOptions); + void logError(const QString& src, const Qx::Error& error); + void logEvent(const QString& src, const QString& event); + void logTask(const QString& src, const Task* task); + ErrorCode logFinish(const QString& src, const Qx::Error& errorState); + + // Directives + template + void postDirective(const QString& src, const T& directive) + { + // Special handling + if constexpr(Qx::any_of) + { + logError(src, directive.error); + if(mVerbosity == Verbosity::Silent || (mVerbosity == Verbosity::Quiet && directive.error.severity() != Qx::Critical)) + return; + } + else + { + if(mVerbosity != Verbosity::Full) + return; + } + + // Send + if constexpr(AsyncDirectiveT) + { + emit announceAsyncDirective(directive); + } + else + { + static_assert(SyncDirectiveT); + emit announceSyncDirective(directive); + } + } + +//-Signals & Slots------------------------------------------------------------------------------------------------------------ +signals: + void announceAsyncDirective(const AsyncDirective& aDirective); + void announceSyncDirective(const SyncDirective& sDirective); +}; + +#endif // DIRECTOR_H diff --git a/app/src/kernel/directorate.cpp b/app/src/kernel/directorate.cpp new file mode 100644 index 0000000..92accf9 --- /dev/null +++ b/app/src/kernel/directorate.cpp @@ -0,0 +1,24 @@ +// Unit Include +#include "directorate.h" + +//=============================================================================================================== +// Directorate +//=============================================================================================================== + +//-Constructor------------------------------------------------------------- +//Public: +Directorate::Directorate(Director* director) : + mDirector(director) +{} + +//-Instance Functions------------------------------------------------------------- +//Protected: +void Directorate::logCommand(const QString& commandName) const { Q_ASSERT(mDirector); return; mDirector->logCommand(name(), commandName); } +void Directorate::logCommandOptions(const QString& commandOptions) const { Q_ASSERT(mDirector); mDirector->logCommandOptions(name(), commandOptions); } +void Directorate::logError(const Qx::Error& error) const { Q_ASSERT(mDirector); mDirector->logError(name(), error); } +void Directorate::logEvent(const QString& event) const { Q_ASSERT(mDirector); mDirector->logEvent(name(), event); } +void Directorate::logTask(const Task* task) const { Q_ASSERT(mDirector); mDirector->logTask(name(), task); } +ErrorCode Directorate::logFinish(const Qx::Error& errorState) const { Q_ASSERT(mDirector); return mDirector->logFinish(name(), errorState); } + +//Public: +void Directorate::setDirector(Director* director) { mDirector = director; } diff --git a/app/src/kernel/directorate.h b/app/src/kernel/directorate.h new file mode 100644 index 0000000..e6e3fa8 --- /dev/null +++ b/app/src/kernel/directorate.h @@ -0,0 +1,62 @@ +#ifndef DIRECTORATE_H +#define DIRECTORATE_H + +// Standard Library Includes +#include + +// Qt Includes +#include + +// Project Includes +#include "kernel/director.h" + +/* TODO: It would be challenging (at least to do in a clean way), but we could try to split the template + * definitions of postDirective et. al. so that the directives that should return a value actually return + * the value instead of a pointer within the directive. + */ + +class Directorate +{ +//-Instance Variables------------------------------------------------------------------------------------------------------ +private: + Director* mDirector; + +//-Constructor------------------------------------------------------------------------------------------------------------ +public: + Directorate(Director* director = nullptr); + +//-Destructor------------------------------------------------------------------------------------------------------------- +public: + virtual ~Directorate() = default; // Future proofing if this is ever used polymorphically + +//-Instance Functions------------------------------------------------------------------------------------------------------ +protected: + //TODO: Some of these probably should be only under specific derivatives + void logCommand(const QString& commandName) const; + void logCommandOptions(const QString& commandOptions) const; + void logError(const Qx::Error& error) const; + void logEvent(const QString& event) const; + void logTask(const Task* task) const; + ErrorCode logFinish(const Qx::Error& errorState) const; + + template + void postDirective(const T& t) const + { + Q_ASSERT(mDirector); + mDirector->postDirective(name(), t); + } + + template + void postDirective(Args&&... args) const + { + Q_ASSERT(mDirector); + mDirector->postDirective(name(), T{std::forward(args)...}); + } + +public: + virtual QString name() const = 0; + + void setDirector(Director* director); +}; + +#endif // DIRECTORATE_H diff --git a/app/src/kernel/driver.cpp b/app/src/kernel/driver.cpp index b2e2832..243ac46 100644 --- a/app/src/kernel/driver.cpp +++ b/app/src/kernel/driver.cpp @@ -38,7 +38,7 @@ QString DriverError::derivePrimary() const { return ERR_STRINGS.value(mType); } QString DriverError::deriveSecondary() const { return mSpecific; } //=============================================================================================================== -// DRIVER +// Driver //=============================================================================================================== //-Constructor-------------------------------------------------------------------- @@ -53,26 +53,23 @@ Driver::Driver(QStringList arguments) : //-Instance Functions------------------------------------------------------------- //Private: +QString Driver::name() const { return NAME; } + void Driver::init() { - // Create core + // Create core, attach director to self mCore = new Core(this); + Director* dtor = mCore->director(); + setDirector(mCore->director()); - //-Setup Core--------------------------- - connect(mCore, &Core::statusChanged, this, &Driver::statusChanged); - connect(mCore, &Core::errorOccurred, this, &Driver::errorOccurred); - connect(mCore, &Core::blockingErrorOccurred, this, &Driver::blockingErrorOccurred); - connect(mCore, &Core::message, this, &Driver::message); - connect(mCore, &Core::saveFileRequested, this, &Driver::saveFileRequested); - connect(mCore, &Core::existingDirRequested, this, &Driver::existingDirRequested); - connect(mCore, &Core::itemSelectionRequested, this, &Driver::itemSelectionRequested); - connect(mCore, &Core::clipboardUpdateRequested, this, &Driver::clipboardUpdateRequested); - connect(mCore, &Core::questionAnswerRequested, this, &Driver::questionAnswerRequested); + //-Setup Core & Director--------------------------- connect(mCore, &Core::abort, this, [this](CoreError err){ logEvent(LOG_EVENT_CORE_ABORT); mErrorStatus = err; quit(); }); + connect(dtor, &Director::announceAsyncDirective, this, &Driver::asyncDirectiveAccounced); + connect(dtor, &Director::announceSyncDirective, this, &Driver::syncDirectiveAccounced); //-Setup deferred process manager------ /* NOTE: It looks like the manager should just be a stack member of TExec that is constructed @@ -84,10 +81,7 @@ void Driver::init() * task except from the correct thread (which would only ever happen anyway), but then that * would make deleting the object slightly tricky. This way it can just be parented to core */ - DeferredProcessManager* dpm = new DeferredProcessManager(mCore); - // qOverload because it gets confused with the shorter versions within core even though they're private :/ - connect(dpm, &DeferredProcessManager::eventOccurred, mCore, qOverload(&Core::logEvent)); - connect(dpm, &DeferredProcessManager::errorOccurred, mCore, qOverload(&Core::logError)); + DeferredProcessManager* dpm = new DeferredProcessManager(*mCore); TExec::installDeferredProcessManager(dpm); } @@ -127,21 +121,6 @@ void Driver::startNextTask() } else { - // Connect task notifiers - connect(mCurrentTask, &Task::notificationReady, mCore, &Core::postMessage); - connect(mCurrentTask, &Task::eventOccurred, mCore, qOverload(&Core::logEvent)); - connect(mCurrentTask, &Task::errorOccurred, mCore, [this](QString taskName, Qx::Error error){ - mCore->postError(taskName, error); // Can't connect directly because newer connect syntax doesn't support default args - }); - connect(mCurrentTask, &Task::blockingErrorOccurred, this, - [this](QString taskName, int* response, Qx::Error error, QMessageBox::StandardButtons choices) { - *response = mCore->postBlockingError(taskName, error, true, choices); - }); - connect(mCurrentTask, &Task::longTaskStarted, this, &Driver::longTaskStarted); - connect(mCurrentTask, &Task::longTaskTotalChanged, this, &Driver::longTaskTotalChanged); - connect(mCurrentTask, &Task::longTaskProgressChanged, this, &Driver::longTaskProgressChanged); - connect(mCurrentTask, &Task::longTaskFinished, this, &Driver::longTaskFinished); - // QueuedConnection, allow event processing between tasks connect(mCurrentTask, &Task::complete, this, &Driver::completeTaskHandler, Qt::QueuedConnection); @@ -220,16 +199,6 @@ std::unique_ptr Driver::findFlashpointInstall() return std::move(fpInstall); } -// Notifications/Logging (core-forwarders) -void Driver::logCommand(QString commandName) { Q_ASSERT(mCore); mCore->logCommand(NAME, commandName); } -void Driver::logCommandOptions(QString commandOptions) { Q_ASSERT(mCore); mCore->logCommandOptions(NAME, commandOptions); } -void Driver::logError(Qx::Error error) { Q_ASSERT(mCore); mCore->logError(NAME, error); } -void Driver::logEvent(QString event) { Q_ASSERT(mCore); mCore->logEvent(NAME, event); } -void Driver::logTask(const Task* task) { Q_ASSERT(mCore); mCore->logTask(NAME, task); } -ErrorCode Driver::logFinish(Qx::Error errorState) { Q_ASSERT(mCore); return mCore->logFinish(NAME, errorState); } -void Driver::postError(Qx::Error error, bool log) { Q_ASSERT(mCore); mCore->postError(NAME, error, log); } -int Driver::postBlockingError(Qx::Error error, bool log, QMessageBox::StandardButtons bs, QMessageBox::StandardButton def) { Q_ASSERT(mCore); return mCore->postBlockingError(NAME, error, log); } - //-Slots-------------------------------------------------------------------------------- //Private: void Driver::completeTaskHandler(Qx::Error e) @@ -276,7 +245,7 @@ void Driver::drive() // Check for valid command if(CommandError ce = Command::isRegistered(commandStr); ce.isValid()) { - postError(ce); + postDirective(ce); mErrorStatus = ce; finish(); return; @@ -295,7 +264,7 @@ void Driver::drive() if(commandProcessor->autoBlockNewInstances() && !mCore->blockNewInstances()) { DriverError err(DriverError::AlreadyOpen); - postError(err); + postDirective(err); mErrorStatus = err; finish(); return; @@ -311,7 +280,7 @@ void Driver::drive() if(!(flashpointInstall = findFlashpointInstall())) { DriverError err(DriverError::InvalidInstall, ERR_INSTALL_INVALID_TIP); - postError(err); + postDirective(err); mErrorStatus = err; finish(); return; diff --git a/app/src/kernel/driver.h b/app/src/kernel/driver.h index 50a795d..ec968c2 100644 --- a/app/src/kernel/driver.h +++ b/app/src/kernel/driver.h @@ -10,9 +10,10 @@ // Project Includes #include "kernel/errorstatus.h" +#include "kernel/directorate.h" #include "kernel/core.h" -class QX_ERROR_TYPE(DriverError, "DriverError", 1201) +class QX_ERROR_TYPE(DriverError, "DriverError", 1202) { friend class Driver; //-Class Enums------------------------------------------------------------- @@ -54,7 +55,7 @@ class QX_ERROR_TYPE(DriverError, "DriverError", 1201) QString deriveSecondary() const override; }; -class Driver : public QObject +class Driver : public QObject, public Directorate { Q_OBJECT //-Class Variables------------------------------------------------------------------------------------------------------ @@ -117,6 +118,8 @@ class Driver : public QObject //-Instance Functions------------------------------------------------------------------------------------------------------------ private: + QString name() const override; + // Setup void init(); @@ -130,16 +133,6 @@ class Driver : public QObject // Helper std::unique_ptr findFlashpointInstall(); - // Notifications/Logging (core-forwarders) - void logCommand(QString commandName); - void logCommandOptions(QString commandOptions); - void logError(Qx::Error error); - void logEvent(QString event); - void logTask(const Task* task); - ErrorCode logFinish(Qx::Error errorState); - void postError(Qx::Error error, bool log = true); - int postBlockingError(Qx::Error error, bool log = true, QMessageBox::StandardButtons bs = QMessageBox::Ok, QMessageBox::StandardButton def = QMessageBox::NoButton); - //-Signals & Slots------------------------------------------------------------------------------------------------------------ private slots: void completeTaskHandler(Qx::Error e = {}); @@ -156,22 +149,9 @@ public slots: // Worker status void finished(ErrorCode errorCode); - // Core forwarders - void statusChanged(const QString& statusHeading, const QString& statusMessage); - void errorOccurred(const Core::Error& error); - void blockingErrorOccurred(QSharedPointer response, const Core::BlockingError& blockingError); - void message(const Message& message); - void saveFileRequested(QSharedPointer file, const Core::SaveFileRequest& request); - void existingDirRequested(QSharedPointer dir, const Core::ExistingDirRequest& request); - void itemSelectionRequested(QSharedPointer item, const Core::ItemSelectionRequest& request); - void clipboardUpdateRequested(const QString& text); - void questionAnswerRequested(QSharedPointer response, const QString& question); - - // Long task - void longTaskProgressChanged(quint64 progress); - void longTaskTotalChanged(quint64 total); - void longTaskStarted(QString task); - void longTaskFinished(); + // Director forwarders + void asyncDirectiveAccounced(const AsyncDirective& aDirective); + void syncDirectiveAccounced(const SyncDirective& sDirective); }; #endif // DRIVER_H diff --git a/app/src/main.cpp b/app/src/main.cpp index 3760112..6e7e8d6 100644 --- a/app/src/main.cpp +++ b/app/src/main.cpp @@ -2,7 +2,7 @@ #include // Project Includes -#include "kernel/core.h" +#include "kernel/directive.h" #include "controller.h" #ifdef __linux__ #include "utility.h" @@ -27,8 +27,8 @@ int main(int argc, char *argv[]) #endif // Register metatypes - qRegisterMetaType(); - qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); // Create application controller Controller appController(&app); diff --git a/app/src/task/t-awaitdocker.cpp b/app/src/task/t-awaitdocker.cpp index 1216eac..4dd418d 100644 --- a/app/src/task/t-awaitdocker.cpp +++ b/app/src/task/t-awaitdocker.cpp @@ -30,8 +30,8 @@ QString TAwaitDockerError::deriveSecondary() const { return mSpecific; } //-Constructor-------------------------------------------------------------------- //Public: -TAwaitDocker::TAwaitDocker(QObject* parent) : - Task(parent) +TAwaitDocker::TAwaitDocker(Core& core) : + Task(core) { // Setup event listener mEventListener.setProgram(DOCKER); @@ -67,7 +67,7 @@ TAwaitDockerError TAwaitDocker::imageRunningCheck(bool& running) if(!dockerPs.waitForStarted(1000)) { TAwaitDockerError err(TAwaitDockerError::DirectQueryFailed); - emit errorOccurred(NAME, err); + postDirective(err); return err; } if(!dockerPs.waitForFinished(1000)) @@ -76,7 +76,7 @@ TAwaitDockerError TAwaitDocker::imageRunningCheck(bool& running) dockerPs.waitForFinished(); TAwaitDockerError err(TAwaitDockerError::DirectQueryFailed); - emit errorOccurred(NAME, err); + postDirective(err); return err; } @@ -105,7 +105,7 @@ TAwaitDockerError TAwaitDocker::startEventListener() if(!mEventListener.waitForStarted(1000)) { TAwaitDockerError err(TAwaitDockerError::ListenFailed); - emit errorOccurred(NAME, err); + postDirective(err); return err; } mTimeoutTimer.start(mTimeout); @@ -115,7 +115,7 @@ TAwaitDockerError TAwaitDocker::startEventListener() void TAwaitDocker::stopEventListening() { - emit eventOccurred(NAME, LOG_EVENT_STOPPING_LISTENER); + logEvent(LOG_EVENT_STOPPING_LISTENER); // Just kill it, clean shutdown isn't needed mEventListener.close(); @@ -144,7 +144,7 @@ void TAwaitDocker::perform() TAwaitDockerError errorStatus; // Check if image is running - emit eventOccurred(NAME, LOG_EVENT_DIRECT_QUERY.arg(mImageName)); + logEvent(LOG_EVENT_DIRECT_QUERY.arg(mImageName)); bool running; errorStatus = imageRunningCheck(running); @@ -155,7 +155,7 @@ void TAwaitDocker::perform() } // Listen for image started event - emit eventOccurred(NAME, LOG_EVENT_STARTING_LISTENER); + logEvent(LOG_EVENT_STARTING_LISTENER); errorStatus = startEventListener(); if(errorStatus.isValid()) emit complete(errorStatus); @@ -188,7 +188,7 @@ void TAwaitDocker::eventDataReceived() if(eventData == mImageName) { mTimeoutTimer.stop(); - emit eventOccurred(NAME, LOG_EVENT_START_RECEIVED); + logEvent(LOG_EVENT_START_RECEIVED); stopEventListening(); emit complete(TAwaitDockerError()); } @@ -205,13 +205,13 @@ void TAwaitDocker::timeoutOccurred() if(running) { - emit eventOccurred(NAME, LOG_EVENT_FINAL_CHECK_PASS); + logEvent(LOG_EVENT_FINAL_CHECK_PASS); emit complete(TAwaitDockerError()); } else { TAwaitDockerError err(TAwaitDockerError::StartFailed, mImageName); - emit errorOccurred(NAME, err); + postDirective(err); emit complete(err); } } diff --git a/app/src/task/t-awaitdocker.h b/app/src/task/t-awaitdocker.h index b81c94b..7942844 100644 --- a/app/src/task/t-awaitdocker.h +++ b/app/src/task/t-awaitdocker.h @@ -11,7 +11,7 @@ class QX_ERROR_TYPE(TAwaitDockerError, "TAwaitDockerError", 1260) { friend class TAwaitDocker; - //-Class Enums------------------------------------------------------------- +//-Class Enums------------------------------------------------------------- public: enum Type { @@ -21,7 +21,7 @@ class QX_ERROR_TYPE(TAwaitDockerError, "TAwaitDockerError", 1260) StartFailed = 3 }; - //-Class Variables------------------------------------------------------------- +//-Class Variables------------------------------------------------------------- private: static inline const QHash ERR_STRINGS{ {NoError, u""_s}, @@ -30,16 +30,16 @@ class QX_ERROR_TYPE(TAwaitDockerError, "TAwaitDockerError", 1260) {StartFailed, u"The start of the docker image timed out."_s} }; - //-Instance Variables------------------------------------------------------------- +//-Instance Variables------------------------------------------------------------- private: Type mType; QString mSpecific; - //-Constructor------------------------------------------------------------- +//-Constructor------------------------------------------------------------- private: TAwaitDockerError(Type t = NoError, const QString& s = {}); - //-Instance Functions------------------------------------------------------------- +//-Instance Functions------------------------------------------------------------- public: bool isValid() const; Type type() const; @@ -82,7 +82,7 @@ class TAwaitDocker : public Task //-Constructor---------------------------------------------------------------------------------------------------------- public: - TAwaitDocker(QObject* parent = nullptr); + TAwaitDocker(Core& core); //-Instance Functions------------------------------------------------------------------------------------------------------ private: diff --git a/app/src/task/t-bideprocess.cpp b/app/src/task/t-bideprocess.cpp index 36e3032..c432cc6 100644 --- a/app/src/task/t-bideprocess.cpp +++ b/app/src/task/t-bideprocess.cpp @@ -30,8 +30,8 @@ QString TBideProcessError::deriveSecondary() const { return mProcessName; } //-Constructor-------------------------------------------------------------------- //Public: -TBideProcess::TBideProcess(QObject* parent) : - Task(parent) +TBideProcess::TBideProcess(Core& core) : + Task(core) { // Setup bider using namespace std::chrono_literals; @@ -39,17 +39,17 @@ TBideProcess::TBideProcess(QObject* parent) : mProcessBider.setRespawnGrace(grace); mProcessBider.setInitialGrace(true); // Process will be stopped at first connect(&mProcessBider, &Qx::ProcessBider::established, this, [this]{ - emitEventOccurred(LOG_EVENT_BIDE_RUNNING.arg(mProcessName)); - emitEventOccurred(LOG_EVENT_BIDE_ON.arg(mProcessName)); + logEvent(LOG_EVENT_BIDE_RUNNING.arg(mProcessName)); + logEvent(LOG_EVENT_BIDE_ON.arg(mProcessName)); }); connect(&mProcessBider, &Qx::ProcessBider::processStopped, this, [this]{ - emitEventOccurred(LOG_EVENT_BIDE_QUIT.arg(mProcessName)); + logEvent(LOG_EVENT_BIDE_QUIT.arg(mProcessName)); }); connect(&mProcessBider, &Qx::ProcessBider::graceStarted, this, [this]{ - emitEventOccurred(LOG_EVENT_BIDE_GRACE.arg(QString::number(grace.count()), mProcessName)); + logEvent(LOG_EVENT_BIDE_GRACE.arg(QString::number(grace.count()), mProcessName)); }); connect(&mProcessBider, &Qx::ProcessBider::errorOccurred, this, [this](Qx::ProcessBiderError err){ - emitErrorOccurred(err); + postDirective(err); }); connect(&mProcessBider, &Qx::ProcessBider::finished, this, &TBideProcess::postBide); } @@ -79,7 +79,7 @@ void TBideProcess::stop() { if(mProcessBider.isBiding()) { - emitEventOccurred(LOG_EVENT_STOPPING_BIDE_PROCESS); + logEvent(LOG_EVENT_STOPPING_BIDE_PROCESS); mProcessBider.closeProcess(); } } @@ -92,7 +92,7 @@ void TBideProcess::postBide(Qx::ProcessBider::ResultType type) emit complete(TBideProcessError(mProcessName, TBideProcessError::BideFail)); else { - emitEventOccurred(LOG_EVENT_BIDE_FINISHED.arg(mProcessName)); + logEvent(LOG_EVENT_BIDE_FINISHED.arg(mProcessName)); emit complete(TBideProcessError()); } } diff --git a/app/src/task/t-bideprocess.h b/app/src/task/t-bideprocess.h index 9c4e6f9..4bd213b 100644 --- a/app/src/task/t-bideprocess.h +++ b/app/src/task/t-bideprocess.h @@ -76,7 +76,7 @@ class TBideProcess : public Task //-Constructor---------------------------------------------------------------------------------------------------------- public: - TBideProcess(QObject* parent); + TBideProcess(Core& core); //-Instance Functions------------------------------------------------------------------------------------------------------ public: diff --git a/app/src/task/t-download.cpp b/app/src/task/t-download.cpp index 7041bab..2b0e1aa 100644 --- a/app/src/task/t-download.cpp +++ b/app/src/task/t-download.cpp @@ -42,8 +42,8 @@ QString TDownloadError::deriveDetails() const { return mDetails; } //-Constructor------------------------------------------------------------- //Public: -TDownload::TDownload(QObject* parent) : - Task(parent) +TDownload::TDownload(Core& core) : + Task(core) { // Setup download manager mDownloadManager.setOverwrite(true); @@ -52,25 +52,34 @@ TDownload::TDownload(QObject* parent) : // Download event handlers connect(&mDownloadManager, &Qx::AsyncDownloadManager::sslErrors, this, [this](Qx::Error errorMsg, bool* ignore) { - int choice; - emitBlockingErrorOccurred(&choice, errorMsg, QMessageBox::Yes | QMessageBox::No); - *ignore = choice == QMessageBox::Yes; + DBlockingError::Choice choice; + postDirective(DBlockingError{ + .error = errorMsg, + .choices = DBlockingError::Choice::Yes | DBlockingError::Choice::No, + .defaultChoice = DBlockingError::Choice::No, + .response = &choice + }); + *ignore = choice == DBlockingError::Choice::Yes; }); connect(&mDownloadManager, &Qx::AsyncDownloadManager::authenticationRequired, this, [this](QString prompt) { - emitEventOccurred(LOG_EVENT_DOWNLOAD_AUTH.arg(prompt)); + logEvent(LOG_EVENT_DOWNLOAD_AUTH.arg(prompt)); }); connect(&mDownloadManager, &Qx::AsyncDownloadManager::preSharedKeyAuthenticationRequired, this, [this](QString prompt) { - emitEventOccurred(LOG_EVENT_DOWNLOAD_AUTH.arg(prompt)); + logEvent(LOG_EVENT_DOWNLOAD_AUTH.arg(prompt)); }); connect(&mDownloadManager, &Qx::AsyncDownloadManager::proxyAuthenticationRequired, this, [this](QString prompt) { - emitEventOccurred(LOG_EVENT_DOWNLOAD_AUTH.arg(prompt)); + logEvent(LOG_EVENT_DOWNLOAD_AUTH.arg(prompt)); }); - connect(&mDownloadManager, &Qx::AsyncDownloadManager::downloadTotalChanged, this, &TDownload::longTaskTotalChanged); - connect(&mDownloadManager, &Qx::AsyncDownloadManager::downloadProgress, this, &TDownload::longTaskProgressChanged); + connect(&mDownloadManager, &Qx::AsyncDownloadManager::downloadTotalChanged, this, [this](quint64 total){ + postDirective(total); + }); + connect(&mDownloadManager, &Qx::AsyncDownloadManager::downloadProgress, this, [this](quint64 bytes){ + postDirective(bytes); + }); connect(&mDownloadManager, &Qx::AsyncDownloadManager::finished, this, &TDownload::postDownload); } @@ -127,10 +136,10 @@ void TDownload::perform() // Log/label string QString label = LOG_EVENT_DOWNLOAD.arg(mDescription); - emitEventOccurred(label); + logEvent(label); // Start download - emit longTaskStarted(label); + postDirective(label); mDownloadManager.processQueue(); } @@ -138,7 +147,7 @@ void TDownload::stop() { if(mDownloadManager.isProcessing()) { - emitEventOccurred(LOG_EVENT_STOPPING_DOWNLOADS); + logEvent(LOG_EVENT_STOPPING_DOWNLOADS); mDownloadManager.abort(); } } @@ -150,13 +159,13 @@ void TDownload::postDownload(Qx::DownloadManagerReport downloadReport) Qx::Error errorStatus; // Handle result - emit longTaskFinished(); + postDirective(); if(downloadReport.wasSuccessful()) - emitEventOccurred(LOG_EVENT_DOWNLOAD_SUCC); + logEvent(LOG_EVENT_DOWNLOAD_SUCC); else { errorStatus = TDownloadError(downloadReport); - emitErrorOccurred(errorStatus); + postDirective(errorStatus); } emit complete(errorStatus); diff --git a/app/src/task/t-download.h b/app/src/task/t-download.h index 1662055..10ca52e 100644 --- a/app/src/task/t-download.h +++ b/app/src/task/t-download.h @@ -90,7 +90,7 @@ class TDownload : public Task //-Constructor---------------------------------------------------------------------------------------------------------- public: - TDownload(QObject* parent); + TDownload(Core& core); //-Instance Functions------------------------------------------------------------------------------------------------------ public: diff --git a/app/src/task/t-exec.cpp b/app/src/task/t-exec.cpp index 6f95f5d..6571ee0 100644 --- a/app/src/task/t-exec.cpp +++ b/app/src/task/t-exec.cpp @@ -5,6 +5,7 @@ #include // Project Includes +#include "kernel/core.h" #include "utility.h" // TODO: See if any quote handling here can be replaced with std::quoted() @@ -39,9 +40,10 @@ QString TExecError::deriveSecondary() const { return mSpecific; } //-Constructor------------------------------------------------------------- //Public: -TExec::TExec(QObject* parent) : - Task(parent), +TExec::TExec(Core& core) : + Task(core), mBlockingProcessManager(nullptr), + mCore(core), mEnvironment(smDefaultEnv) {} @@ -86,7 +88,7 @@ QString TExec::createEscapedShellArguments() QString args = std::get(mParameters); escapedArgs = escapeForShell(args); if(args != escapedArgs) - emitEventOccurred(LOG_EVENT_ARGS_ESCAPED.arg(args, escapedArgs)); + logEvent(LOG_EVENT_ARGS_ESCAPED.arg(args, escapedArgs)); } else { @@ -100,7 +102,7 @@ QString TExec::createEscapedShellArguments() QStringList rebuild = QProcess::splitCommand(escapedArgs); if(rebuild != parameters) { - emitEventOccurred(LOG_EVENT_ARGS_ESCAPED.arg(u"{\""_s + parameters.join(uR"(", ")"_s) + u"\"}"_s, + logEvent(LOG_EVENT_ARGS_ESCAPED.arg(u"{\""_s + parameters.join(uR"(", ")"_s) + u"\"}"_s, u"{\""_s + rebuild.join(uR"(", ")"_s) + u"\"}"_s)); } } @@ -140,7 +142,7 @@ void TExec::removeRedundantFullQuotes(QProcess& process) if(redundant) { - emitEventOccurred(LOG_EVENT_REMOVED_REDUNDANT_QUOTES.arg(a)); + logEvent(LOG_EVENT_REMOVED_REDUNDANT_QUOTES.arg(a)); a = inner.toString(); } } @@ -156,27 +158,27 @@ TExecError TExec::cleanStartProcess(QProcess* process) // Go to working directory QDir::setCurrent(newDirPath); - emitEventOccurred(LOG_EVENT_CD.arg(QDir::toNativeSeparators(newDirPath))); + logEvent(LOG_EVENT_CD.arg(QDir::toNativeSeparators(newDirPath))); // Start process process->start(); - emitEventOccurred(LOG_EVENT_STARTING.arg(mIdentifier, process->program())); + logEvent(LOG_EVENT_STARTING.arg(mIdentifier, process->program())); // Return to previous working directory QDir::setCurrent(currentDirPath); - emitEventOccurred(LOG_EVENT_CD.arg(QDir::toNativeSeparators(currentDirPath))); + logEvent(LOG_EVENT_CD.arg(QDir::toNativeSeparators(currentDirPath))); // Make sure process starts if(!process->waitForStarted()) { TExecError err(TExecError::CouldNotStart, ERR_DETAILS_TEMPLATE.arg(process->program(), ENUM_NAME(process->error()))); - emitErrorOccurred(err); + postDirective(err); delete process; // Clear finished process handle from heap return err; } // Return success - emitEventOccurred(LOG_EVENT_STARTED_PROCESS.arg(mIdentifier)); + logEvent(LOG_EVENT_STARTED_PROCESS.arg(mIdentifier)); return TExecError(); } @@ -219,14 +221,14 @@ void TExec::setIdentifier(QString identifier) { mIdentifier = identifier; } void TExec::perform() { - emitEventOccurred(LOG_EVENT_PREPARING_PROCESS.arg(ENUM_NAME(mProcessType), mIdentifier, mExecutable)); + logEvent(LOG_EVENT_PREPARING_PROCESS.arg(ENUM_NAME(mProcessType), mIdentifier, mExecutable)); // Get final executable path QString execPath = resolveExecutablePath(); if(execPath.isEmpty()) { TExecError err(TExecError::CouldNotFind, mExecutable, mStage == Stage::Shutdown ? Qx::Err : Qx::Critical); - emitErrorOccurred(err); + postDirective(err); emit complete(err); return; } @@ -244,8 +246,7 @@ void TExec::perform() { case ProcessType::Blocking: // Setup blocking process manager (it adopts the process) - mBlockingProcessManager = new BlockingProcessManager(taskProcess, mIdentifier, this); - connect(mBlockingProcessManager, &BlockingProcessManager::eventOccurred, this, &TExec::eventOccurred); + mBlockingProcessManager = new BlockingProcessManager(mCore, taskProcess, mIdentifier); connect(mBlockingProcessManager, &BlockingProcessManager::finished, this, &TExec::postBlockingProcess); // Setup and start process @@ -259,8 +260,8 @@ void TExec::perform() return; case ProcessType::Deferred: - // Can't use 'this' as parent since process will outlive this instance, so use parent of task - taskProcess->setParent(this->parent()); + // Can't use 'this' as parent since process will outlive this instance, so use core + taskProcess->setParent(&mCore); if(TExecError se = cleanStartProcess(taskProcess); se.isValid()) { emit complete(se); @@ -284,7 +285,7 @@ void TExec::perform() if(!taskProcess->startDetached()) { TExecError err(TExecError::CouldNotStart, ERR_DETAILS_TEMPLATE.arg(taskProcess->program(), ENUM_NAME(taskProcess->error()))); - emitErrorOccurred(err); + postDirective(err); emit complete(err); return; } @@ -299,7 +300,7 @@ void TExec::stop() { if(mBlockingProcessManager) { - emitEventOccurred(LOG_EVENT_STOPPING_BLOCKING_PROCESS.arg(mIdentifier)); + logEvent(LOG_EVENT_STOPPING_BLOCKING_PROCESS.arg(mIdentifier)); mBlockingProcessManager->closeProcess(); } } diff --git a/app/src/task/t-exec.h b/app/src/task/t-exec.h index a2b8cc2..98e9281 100644 --- a/app/src/task/t-exec.h +++ b/app/src/task/t-exec.h @@ -104,6 +104,7 @@ class TExec : public Task private: // Functional BlockingProcessManager* mBlockingProcessManager; + Core& mCore; // Data QString mExecutable; @@ -115,7 +116,7 @@ class TExec : public Task //-Constructor---------------------------------------------------------------------------------------------------------- public: - TExec(QObject* parent); + TExec(Core& core); //-Class Functions----------------------------------------------------------------------------------------------------- public: diff --git a/app/src/task/t-exec_linux.cpp b/app/src/task/t-exec_linux.cpp index 21f173b..3639d63 100644 --- a/app/src/task/t-exec_linux.cpp +++ b/app/src/task/t-exec_linux.cpp @@ -94,7 +94,7 @@ QString TExec::resolveExecutablePath() if(execInfo.suffix() == SHELL_EXT_WIN) { execInfo.setFile(mExecutable.chopped(SHELL_EXT_WIN.size()) + SHELL_EXT_LINUX); - emit eventOccurred(NAME, LOG_EVENT_FORCED_BASH); + logEvent(LOG_EVENT_FORCED_BASH); } // Mostly standard processing @@ -144,7 +144,7 @@ QProcess* TExec::prepareProcess(const QFileInfo& execInfo) { if(execInfo.suffix() == EXECUTABLE_EXT_WIN) { - emit eventOccurred(NAME, LOG_EVENT_FORCED_WIN); + logEvent(LOG_EVENT_FORCED_WIN); // Resolve passed parameters QStringList exeParam = std::holds_alternative(mParameters) ? @@ -172,8 +172,8 @@ QProcess* TExec::prepareProcess(const QFileInfo& execInfo) void TExec::logPreparedProcess(const QProcess* process) { - emit eventOccurred(NAME, LOG_EVENT_FINAL_EXECUTABLE.arg(process->program())); - emit eventOccurred(NAME, LOG_EVENT_FINAL_PARAMETERS.arg(!process->arguments().isEmpty() ? - u"{\""_s + process->arguments().join(uR"(", ")"_s) + u"\"}"_s : - u""_s)); + logEvent(LOG_EVENT_FINAL_EXECUTABLE.arg(process->program())); + logEvent(LOG_EVENT_FINAL_PARAMETERS.arg(!process->arguments().isEmpty() ? + u"{\""_s + process->arguments().join(uR"(", ")"_s) + u"\"}"_s : + u""_s)); } diff --git a/app/src/task/t-exec_win.cpp b/app/src/task/t-exec_win.cpp index 8a051b6..22ac39e 100644 --- a/app/src/task/t-exec_win.cpp +++ b/app/src/task/t-exec_win.cpp @@ -113,8 +113,8 @@ QProcess* TExec::prepareProcess(const QFileInfo& execInfo) void TExec::logPreparedProcess(const QProcess* process) { - emitEventOccurred(LOG_EVENT_FINAL_EXECUTABLE.arg(process->program())); - emitEventOccurred(LOG_EVENT_FINAL_PARAMETERS.arg(!process->nativeArguments().isEmpty() ? + logEvent(LOG_EVENT_FINAL_EXECUTABLE.arg(process->program())); + logEvent(LOG_EVENT_FINAL_PARAMETERS.arg(!process->nativeArguments().isEmpty() ? process->nativeArguments() : !process->arguments().isEmpty() ? u"{\""_s + process->arguments().join(uR"(", ")"_s) + u"\"}"_s : diff --git a/app/src/task/t-extra.cpp b/app/src/task/t-extra.cpp index d4a1015..89874c1 100644 --- a/app/src/task/t-extra.cpp +++ b/app/src/task/t-extra.cpp @@ -34,8 +34,8 @@ QString TExtraError::deriveSecondary() const { return mSpecific; } //-Constructor-------------------------------------------------------------------- //Public: -TExtra::TExtra(QObject* parent) : - Task(parent) +TExtra::TExtra(Core& core) : + Task(core) {} //-Instance Functions------------------------------------------------------------- @@ -62,12 +62,12 @@ void TExtra::perform() { // Open extra QDesktopServices::openUrl(QUrl::fromLocalFile(mDirectory.absolutePath())); - emitEventOccurred(LOG_EVENT_SHOW_EXTRA.arg(QDir::toNativeSeparators(mDirectory.path()))); + logEvent(LOG_EVENT_SHOW_EXTRA.arg(QDir::toNativeSeparators(mDirectory.path()))); } else { errorStatus = TExtraError(TExtraError::NotFound, QDir::toNativeSeparators(mDirectory.path())); - emitErrorOccurred(errorStatus); + postDirective(errorStatus); } emit complete(errorStatus); diff --git a/app/src/task/t-extra.h b/app/src/task/t-extra.h index b87a069..6ad2c34 100644 --- a/app/src/task/t-extra.h +++ b/app/src/task/t-extra.h @@ -71,7 +71,7 @@ class TExtra : public Task //-Constructor---------------------------------------------------------------------------------------------------------- public: - TExtra(QObject* parent); + TExtra(Core& core); //-Instance Functions------------------------------------------------------------------------------------------------------ public: diff --git a/app/src/task/t-extract.cpp b/app/src/task/t-extract.cpp index 3500644..dcd7d28 100644 --- a/app/src/task/t-extract.cpp +++ b/app/src/task/t-extract.cpp @@ -187,8 +187,8 @@ class TExtract::Extractor //-Constructor-------------------------------------------------------------------- //Public: -TExtract::TExtract(QObject* parent) : - Task(parent) +TExtract::TExtract(Core& core) : + Task(core) {} //-Instance Functions------------------------------------------------------------- @@ -215,13 +215,13 @@ void TExtract::perform() { // Log string QFileInfo packFileInfo(mPackPath); - emitEventOccurred(LOG_EVENT_EXTRACTING_ARCHIVE.arg(packFileInfo.fileName())); + logEvent(LOG_EVENT_EXTRACTING_ARCHIVE.arg(packFileInfo.fileName())); // Extract pack Extractor extractor(mPackPath, mPathInPack, mDestinationPath); TExtractError ee = extractor.extract(); if(ee.isValid()) - emitErrorOccurred(ee); + postDirective(ee); emit complete(ee); } diff --git a/app/src/task/t-extract.h b/app/src/task/t-extract.h index cc31adf..3ba456d 100644 --- a/app/src/task/t-extract.h +++ b/app/src/task/t-extract.h @@ -92,7 +92,7 @@ class TExtract : public Task //-Constructor---------------------------------------------------------------------------------------------------------- public: - TExtract(QObject* parent); + TExtract(Core& core); //-Instance Functions------------------------------------------------------------------------------------------------------ public: diff --git a/app/src/task/t-generic.cpp b/app/src/task/t-generic.cpp index 16ae5b5..4b0c13d 100644 --- a/app/src/task/t-generic.cpp +++ b/app/src/task/t-generic.cpp @@ -7,8 +7,8 @@ //-Constructor-------------------------------------------------------------------- //Public: -TGeneric::TGeneric(QObject* parent) : - Task(parent) +TGeneric::TGeneric(Core& core) : + Task(core) {} //-Instance Functions------------------------------------------------------------- @@ -28,12 +28,12 @@ void TGeneric::setAction(std::function action) { mAction = action; void TGeneric::perform() { - emitEventOccurred(LOG_EVENT_START_ACTION.arg(mDescription)); + logEvent(LOG_EVENT_START_ACTION.arg(mDescription)); Qx::Error err = mAction(); if(err.isValid()) - emitErrorOccurred(err); + postDirective(err); - emitEventOccurred(LOG_EVENT_END_ACTION); + logEvent(LOG_EVENT_END_ACTION); emit complete(err); } diff --git a/app/src/task/t-generic.h b/app/src/task/t-generic.h index 10ab92e..cea7aa4 100644 --- a/app/src/task/t-generic.h +++ b/app/src/task/t-generic.h @@ -23,7 +23,7 @@ class TGeneric : public Task //-Constructor---------------------------------------------------------------------------------------------------------- public: - TGeneric(QObject* parent); + TGeneric(Core& core); //-Instance Functions------------------------------------------------------------------------------------------------------ public: diff --git a/app/src/task/t-message.cpp b/app/src/task/t-message.cpp index 43582cf..52b4744 100644 --- a/app/src/task/t-message.cpp +++ b/app/src/task/t-message.cpp @@ -7,8 +7,8 @@ //-Constructor-------------------------------------------------------------------- //Public: -TMessage::TMessage(QObject* parent) : - Task(parent) +TMessage::TMessage(Core& core) : + Task(core) {} //-Instance Functions------------------------------------------------------------- @@ -33,12 +33,12 @@ void TMessage::setSelectable(bool sel) { mSelectable = sel; } void TMessage::perform() { - emit notificationReady(Message{ - .text = mText, - .blocking = mBlocking, - .selectable = mSelectable - }); - emitEventOccurred(LOG_EVENT_SHOW_MESSAGE); + if(mBlocking) + postDirective(mText, mSelectable); + else + postDirective(mText, mSelectable); + + logEvent(LOG_EVENT_SHOW_MESSAGE); // Return success emit complete(Qx::Error()); diff --git a/app/src/task/t-message.h b/app/src/task/t-message.h index d6f3f20..903d695 100644 --- a/app/src/task/t-message.h +++ b/app/src/task/t-message.h @@ -27,7 +27,7 @@ class TMessage : public Task //-Constructor---------------------------------------------------------------------------------------------------------- public: - TMessage(QObject* parent); + TMessage(Core& core); //-Instance Functions------------------------------------------------------------------------------------------------------ public: diff --git a/app/src/task/t-mount.cpp b/app/src/task/t-mount.cpp index 6f5ad07..e99a2ad 100644 --- a/app/src/task/t-mount.cpp +++ b/app/src/task/t-mount.cpp @@ -9,6 +9,7 @@ #include // Project Includes +#include "kernel/core.h" #include "utility.h" //=============================================================================================================== @@ -17,8 +18,9 @@ //-Constructor-------------------------------------------------------------------- //Public: -TMount::TMount(QObject* parent) : - Task(parent), +TMount::TMount(Core& core) : + Task(core), + mDirector(core.director()), mMounterProxy(nullptr), mMounterQmp(nullptr), mMounterRouter(nullptr), @@ -32,10 +34,7 @@ template requires Qx::any_of void TMount::initMounter(M*& mounter) { - mounter = new M(this); - - connect(mounter, &M::eventOccurred, this, &Task::eventOccurred); - connect(mounter, &M::errorOccurred, this, &Task::errorOccurred); + mounter = new M(this, mDirector); connect(mounter, &M::mountFinished, this, &TMount::mounterFinishHandler); } @@ -67,11 +66,11 @@ void TMount::perform() QString label = LOG_EVENT_MOUNTING_DATA_PACK.arg(packFileInfo.fileName()); // Start mount - emit longTaskStarted(label); + postDirective(label); // Update state - emit longTaskProgressChanged(0); - emit longTaskTotalChanged(0); // Cause busy state + postDirective(0u); + postDirective(0u); // Cause busy state //-Setup Mounter(s)------------------------------------ @@ -146,7 +145,7 @@ void TMount::stop() { if(mMounting) { - emitEventOccurred(LOG_EVENT_STOPPING_MOUNT); + logEvent(LOG_EVENT_STOPPING_MOUNT); // TODO: This could benefit from the mounters using a shared base, or // some other kind of type erasure like the duck typing above. @@ -179,6 +178,6 @@ void TMount::postMount(Qx::Error errorStatus) mMounterRouter = nullptr; // Handle result - emit longTaskFinished(); + postDirective(); emit complete(errorStatus); } diff --git a/app/src/task/t-mount.h b/app/src/task/t-mount.h index 84478d6..8e8ecd2 100644 --- a/app/src/task/t-mount.h +++ b/app/src/task/t-mount.h @@ -31,6 +31,9 @@ class TMount : public Task //-Instance Variables------------------------------------------------------------------------------------------------ private: + // Director + Director* mDirector; // TODO: Won't need to store this once the old mounter are dropped; pass to final mounters constructor + // Mounters MounterGameServer* mMounterProxy; MounterQmp* mMounterQmp; @@ -46,7 +49,7 @@ class TMount : public Task //-Constructor---------------------------------------------------------------------------------------------------------- public: - TMount(QObject* parent); + TMount(Core& core); //-Instance Functions------------------------------------------------------------------------------------------------------ private: diff --git a/app/src/task/t-sleep.cpp b/app/src/task/t-sleep.cpp index f3661b1..24ac94d 100644 --- a/app/src/task/t-sleep.cpp +++ b/app/src/task/t-sleep.cpp @@ -7,8 +7,8 @@ //-Constructor-------------------------------------------------------------------- //Public: -TSleep::TSleep(QObject* parent) : - Task(parent) +TSleep::TSleep(Core& core) : + Task(core) { // Setup timer mSleeper.setSingleShot(true); @@ -31,13 +31,13 @@ void TSleep::setDuration(uint msecs) { mDuration = msecs; } void TSleep::perform() { - emitEventOccurred(LOG_EVENT_START_SLEEP.arg(mDuration)); + logEvent(LOG_EVENT_START_SLEEP.arg(mDuration)); mSleeper.start(mDuration); } void TSleep::stop() { - emitEventOccurred(LOG_EVENT_SLEEP_INTERUPTED); + logEvent(LOG_EVENT_SLEEP_INTERUPTED); mSleeper.stop(); emit complete(Qx::Error()); } @@ -46,6 +46,6 @@ void TSleep::stop() //Privates Slots: void TSleep::timerFinished() { - emitEventOccurred(LOG_EVENT_FINISH_SLEEP); + logEvent(LOG_EVENT_FINISH_SLEEP); emit complete(Qx::Error()); } diff --git a/app/src/task/t-sleep.h b/app/src/task/t-sleep.h index 2daa62b..e490dff 100644 --- a/app/src/task/t-sleep.h +++ b/app/src/task/t-sleep.h @@ -33,7 +33,7 @@ class TSleep : public Task //-Constructor---------------------------------------------------------------------------------------------------------- public: - TSleep(QObject* parent); + TSleep(Core& core); //-Instance Functions------------------------------------------------------------------------------------------------------ public: diff --git a/app/src/task/task.cpp b/app/src/task/task.cpp index 7480e48..b68a24b 100644 --- a/app/src/task/task.cpp +++ b/app/src/task/task.cpp @@ -2,6 +2,7 @@ #include "task.h" // Project Includes +#include "kernel/core.h" #include "utility.h" /* TODO: If a task ever needs to be executed conditionally (after being enqueued), create a Condition class with @@ -17,17 +18,12 @@ //-Constructor------------------------------------------------------------- //Public: -Task::Task(QObject* parent) : - QObject(parent) +Task::Task(Core& core) : + QObject(&core), + Directorate(core.director()) {} //-Instance Functions------------------------------------------------------------- -//Protected: -// Notifications/Logging (signal-forwarders) -void Task::emitEventOccurred(const QString& event) { emit eventOccurred(name(), event); } -void Task::emitErrorOccurred(const Qx::Error& error) { emit errorOccurred(name(), error); } -void Task::emitBlockingErrorOccurred(int* response, const Qx::Error& error, QMessageBox::StandardButtons choices) { emit blockingErrorOccurred(name(), response, error, choices); } - //Public: QStringList Task::members() const { return {u".stage() = "_s + ENUM_NAME(mStage)}; } diff --git a/app/src/task/task.h b/app/src/task/task.h index 304c078..9887822 100644 --- a/app/src/task/task.h +++ b/app/src/task/task.h @@ -9,9 +9,11 @@ #include // Project Includes -#include "frontend/message.h" +#include "kernel/directorate.h" -class Task : public QObject +class Core; + +class Task : public QObject, public Directorate { Q_OBJECT; //-Class Enums----------------------------------------------------------------------------------------------------- @@ -24,21 +26,14 @@ class Task : public QObject //-Constructor---------------------------------------------------------------------------------------------------------- public: - Task(QObject* parent); // Require tasks to have a parent + Task(Core& core); //-Destructor---------------------------------------------------------------------------------------------------------- public: virtual ~Task() = default; //-Instance Functions------------------------------------------------------------------------------------------------------ -protected: - // Notifications/Logging (signal-forwarders) - void emitEventOccurred(const QString& event); - void emitErrorOccurred(const Qx::Error& error); - void emitBlockingErrorOccurred(int* response, const Qx::Error& error, QMessageBox::StandardButtons choices); - public: - virtual QString name() const = 0; virtual QStringList members() const; Stage stage() const; @@ -49,16 +44,6 @@ class Task : public QObject //-Signals & Slots------------------------------------------------------------------------------------------------------------ signals: - void notificationReady(const Message& msg); - void eventOccurred(const QString& taskName, const QString& event); - void errorOccurred(const QString& taskName, const Qx::Error& error); - void blockingErrorOccurred(const QString& taskName, int* response, const Qx::Error& error, QMessageBox::StandardButtons choices); - - void longTaskStarted(const QString& procedure); - void longTaskTotalChanged(quint64 total); - void longTaskProgressChanged(quint64 progress); - void longTaskFinished(); - void complete(const Qx::Error& errorState); }; diff --git a/app/src/tools/blockingprocessmanager.cpp b/app/src/tools/blockingprocessmanager.cpp index b34e6b7..96c0eb8 100644 --- a/app/src/tools/blockingprocessmanager.cpp +++ b/app/src/tools/blockingprocessmanager.cpp @@ -2,6 +2,7 @@ #include "blockingprocessmanager.h" // Project Includes +#include "kernel/core.h" #include "utility.h" //=============================================================================================================== @@ -10,8 +11,9 @@ //-Constructor------------------------------------------------------------- //Public: -BlockingProcessManager::BlockingProcessManager(QProcess* process, const QString& identifier, QObject* parent) : - QObject(parent), +BlockingProcessManager::BlockingProcessManager(Core& core, QProcess* process, const QString& identifier) : + QObject(&core), + Directorate(core.director()), mProcess(process), mIdentifier(identifier) { @@ -26,7 +28,7 @@ BlockingProcessManager::BlockingProcessManager(QProcess* process, const QString& //-Instance Functions------------------------------------------------------------- //Private: -void BlockingProcessManager::signalEvent(const QString& event) { emit eventOccurred(NAME, event); } +QString BlockingProcessManager::name() const { return NAME; } void BlockingProcessManager::signalProcessDataReceived(const QString& msgTemplate) { @@ -41,7 +43,7 @@ void BlockingProcessManager::signalProcessDataReceived(const QString& msgTemplat output.chop(1); // Signal data - signalEvent(msgTemplate.arg(pid, output)); + logEvent(msgTemplate.arg(pid, output)); } //Public: @@ -91,7 +93,7 @@ void BlockingProcessManager::processFinishedHandler(int exitCode, QProcess::Exit mProcess->close(); // Wipe IO buffers for posterity // Notify process end - signalEvent(LOG_EVENT_PROCCESS_CLOSED.arg(mIdentifier, program, status, code)); + logEvent(LOG_EVENT_PROCCESS_CLOSED.arg(mIdentifier, program, status, code)); // Notify management completion emit finished(); diff --git a/app/src/tools/blockingprocessmanager.h b/app/src/tools/blockingprocessmanager.h index f2b7b00..a943c5b 100644 --- a/app/src/tools/blockingprocessmanager.h +++ b/app/src/tools/blockingprocessmanager.h @@ -9,7 +9,12 @@ #include #include -class BlockingProcessManager : public QObject +// Project Includes +#include "kernel/directorate.h" + +class Core; + +class BlockingProcessManager : public QObject, public Directorate { Q_OBJECT; //-Class Variables------------------------------------------------------------------------------------------------------ @@ -29,11 +34,11 @@ class BlockingProcessManager : public QObject //-Constructor---------------------------------------------------------------------------------------------------------- public: - BlockingProcessManager(QProcess* process, const QString& identifier, QObject* parent); + BlockingProcessManager(Core& core, QProcess* process, const QString& identifier); //-Instance Functions------------------------------------------------------------------------------------------------------ private: - void signalEvent(const QString& event); + QString name() const override; void signalProcessDataReceived(const QString& msgTemplate); public: @@ -46,7 +51,6 @@ private slots: void processStandardErrorHandler(); signals: - void eventOccurred(const QString& name, const QString& event); void finished(); }; diff --git a/app/src/tools/deferredprocessmanager.cpp b/app/src/tools/deferredprocessmanager.cpp index 2235ff7..30b937b 100644 --- a/app/src/tools/deferredprocessmanager.cpp +++ b/app/src/tools/deferredprocessmanager.cpp @@ -5,6 +5,7 @@ #include // Project Includes +#include "kernel/core.h" #include "utility.h" //=============================================================================================================== @@ -13,17 +14,17 @@ //-Constructor------------------------------------------------------------- //Public: -DeferredProcessManager::DeferredProcessManager(QObject* parent) : - QObject(parent), +DeferredProcessManager::DeferredProcessManager(Core& core) : + QObject(&core), + Directorate(core.director()), mClosingClients(false) {} //-Instance Functions------------------------------------------------------------- //Private: -void DeferredProcessManager::signalEvent(const QString& event) { emit eventOccurred(NAME, event); } -void DeferredProcessManager::signalError(const Qx::GenericError& error) { emit errorOccurred(NAME, error); } +QString DeferredProcessManager::name() const { return NAME; } -void DeferredProcessManager::signalProcessDataReceived(QProcess* process, const QString& msgTemplate) +void DeferredProcessManager::handleProcessDataReceived(QProcess* process, const QString& msgTemplate) { // Assemble details QString identifier = process->objectName(); @@ -38,17 +39,17 @@ void DeferredProcessManager::signalProcessDataReceived(QProcess* process, const output.chop(1); // Signal data - signalEvent(msgTemplate.arg(identifier, program, pid, output)); + logEvent(msgTemplate.arg(identifier, program, pid, output)); } -void DeferredProcessManager::signalProcessStdOutMessage(QProcess* process) +void DeferredProcessManager::handleProcessStdOutMessage(QProcess* process) { - signalProcessDataReceived(process, LOG_EVENT_PROCCESS_STDOUT); + handleProcessDataReceived(process, LOG_EVENT_PROCCESS_STDOUT); } -void DeferredProcessManager::signalProcessStdErrMessage(QProcess* process) +void DeferredProcessManager::handleProcessStdErrMessage(QProcess* process) { - signalProcessDataReceived(process, LOG_EVENT_PROCCESS_STDERR); + handleProcessDataReceived(process, LOG_EVENT_PROCCESS_STDERR); } // Public: @@ -127,10 +128,10 @@ void DeferredProcessManager::processFinishedHandler(int exitCode, QProcess::Exit // Flush incomplete messages process->setReadChannel(QProcess::StandardOutput); if(!process->atEnd()) - signalProcessStdOutMessage(process); + handleProcessStdOutMessage(process); process->setReadChannel(QProcess::StandardError); if(!process->atEnd()) - signalProcessStdErrMessage(process); + handleProcessStdErrMessage(process); // Assemble details QString identifier = process->objectName(); @@ -144,9 +145,9 @@ void DeferredProcessManager::processFinishedHandler(int exitCode, QProcess::Exit // Emit message based on whether the process was expected to be closed if(mClosingClients) - signalEvent(LOG_EVENT_PROCCESS_CLOSED.arg(identifier, program, status, code)); + logEvent(LOG_EVENT_PROCCESS_CLOSED.arg(identifier, program, status, code)); else - signalError(Qx::GenericError(Qx::Warning, 12311, ERR_PROCESS_END_PREMATURE.arg(identifier, program, status, code))); + postDirective(Qx::GenericError(Qx::Warning, 12311, ERR_PROCESS_END_PREMATURE.arg(identifier, program, status, code))); } void DeferredProcessManager::processStandardOutHandler() @@ -159,7 +160,7 @@ void DeferredProcessManager::processStandardOutHandler() // Signal data if complete message is in buffer process->setReadChannel(QProcess::StandardOutput); if(process->canReadLine()) - signalProcessStdOutMessage(process); + handleProcessStdOutMessage(process); } void DeferredProcessManager::processStandardErrorHandler() @@ -172,5 +173,5 @@ void DeferredProcessManager::processStandardErrorHandler() // Signal data if complete message is in buffer process->setReadChannel(QProcess::StandardError); if(process->canReadLine()) - signalProcessStdErrMessage(process); + handleProcessStdErrMessage(process); } diff --git a/app/src/tools/deferredprocessmanager.h b/app/src/tools/deferredprocessmanager.h index 724e257..68ebe03 100644 --- a/app/src/tools/deferredprocessmanager.h +++ b/app/src/tools/deferredprocessmanager.h @@ -10,7 +10,12 @@ #include #include -class DeferredProcessManager : public QObject +// Project Includes +#include "kernel/directorate.h" + +class Core; + +class DeferredProcessManager : public QObject, public Directorate { Q_OBJECT; //-Class Variables------------------------------------------------------------------------------------------------------ @@ -33,15 +38,14 @@ class DeferredProcessManager : public QObject //-Constructor---------------------------------------------------------------------------------------------------------- public: - DeferredProcessManager(QObject* parent); + DeferredProcessManager(Core& core); //-Instance Functions------------------------------------------------------------------------------------------------------ private: - void signalEvent(const QString& event); - void signalError(const Qx::GenericError& error); - void signalProcessDataReceived(QProcess* process, const QString& msgTemplate); - void signalProcessStdOutMessage(QProcess* process); - void signalProcessStdErrMessage(QProcess* process); + QString name() const override; + void handleProcessDataReceived(QProcess* process, const QString& msgTemplate); + void handleProcessStdOutMessage(QProcess* process); + void handleProcessStdErrMessage(QProcess* process); public: void manage(const QString& identifier, QProcess* process); @@ -52,10 +56,6 @@ private slots: void processFinishedHandler(int exitCode, QProcess::ExitStatus exitStatus); void processStandardOutHandler(); void processStandardErrorHandler(); - -signals: - void eventOccurred(const QString& name, const QString& event); - void errorOccurred(const QString& name, const Qx::GenericError& error); }; #endif // DEFERREDPROCESSMANAGER_H diff --git a/app/src/tools/mounter_game_server.cpp b/app/src/tools/mounter_game_server.cpp index 7aee5b8..5995b55 100644 --- a/app/src/tools/mounter_game_server.cpp +++ b/app/src/tools/mounter_game_server.cpp @@ -43,8 +43,9 @@ QString MounterGameServerError::deriveSecondary() const { return mSpecific; } //-Constructor---------------------------------------------------------------------------------------------------------- //Public: -MounterGameServer::MounterGameServer(QObject* parent) : +MounterGameServer::MounterGameServer(QObject* parent, Director* director) : QObject(parent), + Directorate(director), mMounting(false), mGameServerPort(0) { @@ -59,18 +60,18 @@ MounterGameServer::MounterGameServer(QObject* parent) : * them to be used as to help make that clear in the logs when the update causes this to stop working). */ connect(&mNam, &QNetworkAccessManager::authenticationRequired, this, [this](){ - signalEventOccurred(u"Unexpected use of authentication by PHP server!"_s); + logEvent(u"Unexpected use of authentication by PHP server!"_s); }); connect(&mNam, &QNetworkAccessManager::preSharedKeyAuthenticationRequired, this, [this](){ - signalEventOccurred(u"Unexpected use of PSK authentication by PHP server!"_s); + logEvent(u"Unexpected use of PSK authentication by PHP server!"_s); }); connect(&mNam, &QNetworkAccessManager::proxyAuthenticationRequired, this, [this](){ - signalEventOccurred(u"Unexpected use of proxy by PHP server!"_s); + logEvent(u"Unexpected use of proxy by PHP server!"_s); }); connect(&mNam, &QNetworkAccessManager::sslErrors, this, [this](QNetworkReply* reply, const QList& errors){ Q_UNUSED(reply); QString errStrList = Qx::String::join(errors, [](const QSslError& err){ return err.errorString(); }, u","_s); - signalEventOccurred(u"Unexpected SSL errors from PHP server! {"_s + errStrList + u"}"_s"}"); + logEvent(u"Unexpected SSL errors from PHP server! {"_s + errStrList + u"}"_s"}"); }); } @@ -84,18 +85,16 @@ void MounterGameServer::finish(const MounterGameServerError& errorState) void MounterGameServer::noteProxyRequest(QNetworkAccessManager::Operation op, const QUrl& url, QByteArrayView data) { - signalEventOccurred(EVENT_REQUEST_SENT.arg(ENUM_NAME(op), url.toString(), QString::fromLatin1(data))); + logEvent(EVENT_REQUEST_SENT.arg(ENUM_NAME(op), url.toString(), QString::fromLatin1(data))); } void MounterGameServer::noteProxyResponse(const QString& response) { - signalEventOccurred(EVENT_GAMESERVER_RESPONSE.arg(response)); + logEvent(EVENT_GAMESERVER_RESPONSE.arg(response)); } -void MounterGameServer::signalEventOccurred(const QString& event) { emit eventOccurred(NAME, event); } -void MounterGameServer::signalErrorOccurred(const MounterGameServerError& errorMessage) { emit errorOccurred(NAME, errorMessage); } - //Public: +QString MounterGameServer::name() const { return NAME; } bool MounterGameServer::isMounting() { return mMounting; } quint16 MounterGameServer::gameServerPort() const { return mGameServerPort; } @@ -115,7 +114,7 @@ void MounterGameServer::gameServerMountFinishedHandler(QNetworkReply* reply) if(reply->error() != QNetworkReply::NoError) { err = MounterGameServerError(MounterGameServerError::ProxyMount, reply->errorString()); - signalErrorOccurred(err); + postDirective(err); } else { @@ -129,7 +128,7 @@ void MounterGameServer::gameServerMountFinishedHandler(QNetworkReply* reply) //Public Slots: void MounterGameServer::mount() { - signalEventOccurred(EVENT_MOUNTING); + logEvent(EVENT_MOUNTING); //-Create mount request------------------------- diff --git a/app/src/tools/mounter_game_server.h b/app/src/tools/mounter_game_server.h index 2a1cb74..a9a6ac3 100644 --- a/app/src/tools/mounter_game_server.h +++ b/app/src/tools/mounter_game_server.h @@ -11,6 +11,9 @@ #include #include +// Project Includes +#include "kernel/directorate.h" + class QX_ERROR_TYPE(MounterGameServerError, "MounterError", 1232) { friend class MounterGameServer; @@ -51,7 +54,7 @@ class QX_ERROR_TYPE(MounterGameServerError, "MounterError", 1232) QString deriveSecondary() const override; }; -class MounterGameServer : public QObject +class MounterGameServer : public QObject, public Directorate { Q_OBJECT //-Class Variables------------------------------------------------------------------------------------------------------ @@ -83,7 +86,7 @@ class MounterGameServer : public QObject //-Constructor------------------------------------------------------------------------------------------------- public: - explicit MounterGameServer(QObject* parent = nullptr); + explicit MounterGameServer(QObject* parent, Director* director); //-Instance Functions--------------------------------------------------------------------------------------------------------- private: @@ -91,10 +94,8 @@ class MounterGameServer : public QObject void noteProxyRequest(QNetworkAccessManager::Operation op, const QUrl& url, QByteArrayView data); void noteProxyResponse(const QString& response); - void signalEventOccurred(const QString& event); - void signalErrorOccurred(const MounterGameServerError& errorMessage); - public: + QString name() const override; bool isMounting(); quint16 gameServerPort() const; @@ -112,13 +113,7 @@ public slots: void abort(); signals: - void eventOccurred(const QString& name, const QString& event); - void errorOccurred(const QString& name, const MounterGameServerError& errorMessage); void mountFinished(const MounterGameServerError& errorState); - - // For now these just cause a busy state - void mountProgress(qint64 progress); - void mountProgressMaximumChanged(qint64 maximum); }; #endif // MOUNTER_GAME_SERVER_H diff --git a/app/src/tools/mounter_qmp.cpp b/app/src/tools/mounter_qmp.cpp index 3341e89..b710d53 100644 --- a/app/src/tools/mounter_qmp.cpp +++ b/app/src/tools/mounter_qmp.cpp @@ -36,8 +36,9 @@ QString MounterQmpError::deriveSecondary() const { return mSpecific; } //-Constructor---------------------------------------------------------------------------------------------------------- //Public: -MounterQmp::MounterQmp(QObject* parent) : +MounterQmp::MounterQmp(QObject* parent, Director* director) : QObject(parent), + Directorate(director), mMounting(false), mErrorStatus(MounterQmpError::NoError), mQemuMounter(QHostAddress::LocalHost, 0), @@ -74,7 +75,7 @@ void MounterQmp::finish() void MounterQmp::createMountPoint() { - signalEventOccurred(EVENT_CREATING_MOUNT_POINT); + logEvent(EVENT_CREATING_MOUNT_POINT); // Build commands QString blockDevAddCmd = u"blockdev-add"_s; @@ -113,10 +114,8 @@ void MounterQmp::createMountPoint() // Await finished() signal... } -void MounterQmp::signalEventOccurred(const QString& event) { emit eventOccurred(NAME, event); } -void MounterQmp::signalErrorOccurred(const MounterQmpError& errorMessage) { emit errorOccurred(NAME, errorMessage); } - //Public: +QString MounterQmp::name() const { return NAME; } bool MounterQmp::isMounting() { return mMounting; } QString MounterQmp::driveId() const { return mDriveId; } @@ -139,12 +138,12 @@ void MounterQmp::qmpiConnectedHandler(QJsonObject version, QJsonArray capabiliti QString versionStr = formatter.toJson(QJsonDocument::Compact); formatter.setArray(capabilities); QString capabilitiesStr = formatter.toJson(QJsonDocument::Compact); - signalEventOccurred(EVENT_QMP_WELCOME_MESSAGE.arg(versionStr, capabilitiesStr)); + logEvent(EVENT_QMP_WELCOME_MESSAGE.arg(versionStr, capabilitiesStr)); } void MounterQmp::qmpiCommandsExhaustedHandler() { - signalEventOccurred(EVENT_DISCONNECTING_FROM_QEMU); + logEvent(EVENT_DISCONNECTING_FROM_QEMU); mQemuMounter.disconnectFromHost(); } @@ -160,7 +159,7 @@ void MounterQmp::qmpiConnectionErrorHandler(QAbstractSocket::SocketError error) MounterQmpError err(MounterQmpError::QemuConnection, ENUM_NAME(error)); mErrorStatus = err; - signalErrorOccurred(err); + postDirective(err); } void MounterQmp::qmpiCommunicationErrorHandler(Qmpi::CommunicationError error) @@ -168,7 +167,7 @@ void MounterQmp::qmpiCommunicationErrorHandler(Qmpi::CommunicationError error) MounterQmpError err(MounterQmpError::QemuCommunication, ENUM_NAME(error)); mErrorStatus = err; - signalErrorOccurred(err); + postDirective(err); } void MounterQmp::qmpiCommandErrorHandler(QString errorClass, QString description, std::any context) @@ -178,13 +177,13 @@ void MounterQmp::qmpiCommandErrorHandler(QString errorClass, QString description MounterQmpError err(MounterQmpError::QemuCommand, commandErr); mErrorStatus = err; - signalErrorOccurred(err); + postDirective(err); mQemuMounter.abort(); } void MounterQmp::qmpiCommandResponseHandler(QJsonValue value, std::any context) { - signalEventOccurred(EVENT_QMP_COMMAND_RESPONSE.arg(std::any_cast(context), Qx::asString(value))); + logEvent(EVENT_QMP_COMMAND_RESPONSE.arg(std::any_cast(context), Qx::asString(value))); } void MounterQmp::qmpiEventOccurredHandler(QString name, QJsonObject data, QDateTime timestamp) @@ -192,14 +191,14 @@ void MounterQmp::qmpiEventOccurredHandler(QString name, QJsonObject data, QDateT QJsonDocument formatter(data); QString dataStr = formatter.toJson(QJsonDocument::Compact); QString timestampStr = timestamp.toString(u"hh:mm:s s.zzz"_s); - signalEventOccurred(EVENT_QMP_EVENT.arg(name, dataStr, timestampStr)); + logEvent(EVENT_QMP_EVENT.arg(name, dataStr, timestampStr)); } //Public Slots: void MounterQmp::mount() { // Connect to QEMU instance - signalEventOccurred(EVENT_CONNECTING_TO_QEMU); + logEvent(EVENT_CONNECTING_TO_QEMU); mQemuMounter.connectToHost(); // Await readyForCommands() signal... @@ -213,7 +212,7 @@ void MounterQmp::abort() MounterQmpError err(MounterQmpError::QemuConnection, ERR_QMP_CONNECTION_ABORT); mErrorStatus = err; - signalErrorOccurred(err); + postDirective(err); mQemuMounter.abort(); // Call last here because it causes finished signal to emit immediately } } diff --git a/app/src/tools/mounter_qmp.h b/app/src/tools/mounter_qmp.h index 0e3fe58..69c067d 100644 --- a/app/src/tools/mounter_qmp.h +++ b/app/src/tools/mounter_qmp.h @@ -12,6 +12,9 @@ // QI-QMP Includes #include +// Project Includes +#include "kernel/directorate.h" + class QX_ERROR_TYPE(MounterQmpError, "MounterQmpError", 1233) { friend class MounterQmp; @@ -56,7 +59,7 @@ class QX_ERROR_TYPE(MounterQmpError, "MounterQmpError", 1233) QString deriveSecondary() const override; }; -class MounterQmp : public QObject +class MounterQmp : public QObject, public Directorate { Q_OBJECT @@ -103,17 +106,15 @@ class MounterQmp : public QObject //-Constructor------------------------------------------------------------------------------------------------- public: - explicit MounterQmp(QObject* parent = nullptr); + explicit MounterQmp(QObject* parent, Director* director); //-Instance Functions--------------------------------------------------------------------------------------------------------- private: void finish(); void createMountPoint(); - void signalEventOccurred(const QString& event); - void signalErrorOccurred(const MounterQmpError& errorMessage); - public: + QString name() const override; bool isMounting(); QString driveId() const; @@ -146,8 +147,6 @@ public slots: void abort(); signals: - void eventOccurred(const QString& name, const QString& event); - void errorOccurred(const QString& name, const MounterQmpError& errorMessage); void mountFinished(const MounterQmpError& errorState); }; diff --git a/app/src/tools/mounter_router.cpp b/app/src/tools/mounter_router.cpp index 81beeb7..425f9fc 100644 --- a/app/src/tools/mounter_router.cpp +++ b/app/src/tools/mounter_router.cpp @@ -40,8 +40,9 @@ QString MounterRouterError::deriveSecondary() const { return mSpecific; } //-Constructor---------------------------------------------------------------------------------------------------------- //Public: -MounterRouter::MounterRouter(QObject* parent) : +MounterRouter::MounterRouter(QObject* parent, Director* director) : QObject(parent), + Directorate(director), mMounting(false), mRouterPort(0) { @@ -56,18 +57,18 @@ MounterRouter::MounterRouter(QObject* parent) : * them to be used as to help make that clear in the logs when the update causes this to stop working). */ connect(&mNam, &QNetworkAccessManager::authenticationRequired, this, [this](){ - signalEventOccurred(u"Unexpected use of authentication by PHP server!"_s); + logEvent(u"Unexpected use of authentication by PHP server!"_s); }); connect(&mNam, &QNetworkAccessManager::preSharedKeyAuthenticationRequired, this, [this](){ - signalEventOccurred(u"Unexpected use of PSK authentication by PHP server!"_s); + logEvent(u"Unexpected use of PSK authentication by PHP server!"_s); }); connect(&mNam, &QNetworkAccessManager::proxyAuthenticationRequired, this, [this](){ - signalEventOccurred(u"Unexpected use of proxy by PHP server!"_s); + logEvent(u"Unexpected use of proxy by PHP server!"_s); }); connect(&mNam, &QNetworkAccessManager::sslErrors, this, [this](QNetworkReply* reply, const QList& errors){ Q_UNUSED(reply); QString errStrList = Qx::String::join(errors, [](const QSslError& err){ return err.errorString(); }, u","_s); - signalEventOccurred(u"Unexpected SSL errors from PHP server! {"_s + errStrList + u"}"_s"}"); + logEvent(u"Unexpected SSL errors from PHP server! {"_s + errStrList + u"}"_s"}"); }); } @@ -80,6 +81,7 @@ void MounterRouter::finish(const MounterRouterError& result) } //Public: +QString MounterRouter::name() const { return NAME; } bool MounterRouter::isMounting() { return mMounting; } quint16 MounterRouter::routerPort() const { return mRouterPort; } @@ -88,9 +90,6 @@ QString MounterRouter::mountValue() const { return mMountValue; } void MounterRouter::setRouterPort(quint16 port) { mRouterPort = port; } void MounterRouter::setMountValue(const QString& value) { mMountValue = value; } -void MounterRouter::signalEventOccurred(const QString& event) { emit eventOccurred(NAME, event); } -void MounterRouter::signalErrorOccurred(const MounterRouterError& errorMessage) { emit errorOccurred(NAME, errorMessage); } - //-Signals & Slots------------------------------------------------------------------------------------------------------------ //Private Slots: void MounterRouter::mountFinishedHandler(QNetworkReply* reply) @@ -103,12 +102,12 @@ void MounterRouter::mountFinishedHandler(QNetworkReply* reply) if(reply->error() != QNetworkReply::NoError && reply->error() != QNetworkReply::InternalServerError) { err = MounterRouterError(MounterRouterError::Failed, reply->errorString()); - signalErrorOccurred(err); + postDirective(err); } else { QByteArray response = reply->readAll(); - signalEventOccurred(EVENT_ROUTER_RESPONSE.arg(response)); + logEvent(EVENT_ROUTER_RESPONSE.arg(response)); } finish(err); @@ -117,7 +116,7 @@ void MounterRouter::mountFinishedHandler(QNetworkReply* reply) //Public Slots: void MounterRouter::mount() { - signalEventOccurred(EVENT_MOUNTING_THROUGH_ROUTER); + logEvent(EVENT_MOUNTING_THROUGH_ROUTER); // Create mount request QUrl mountUrl; @@ -138,7 +137,7 @@ void MounterRouter::mount() mRouterMountReply = mNam.get(mountReq); // Log request - signalEventOccurred(EVENT_REQUEST_SENT.arg(ENUM_NAME(mRouterMountReply->operation()), mountUrl.toString())); + logEvent(EVENT_REQUEST_SENT.arg(ENUM_NAME(mRouterMountReply->operation()), mountUrl.toString())); // Await finished() signal... } diff --git a/app/src/tools/mounter_router.h b/app/src/tools/mounter_router.h index ea85892..a406777 100644 --- a/app/src/tools/mounter_router.h +++ b/app/src/tools/mounter_router.h @@ -11,6 +11,9 @@ #include #include +// Project Includes +#include "kernel/directorate.h" + class QX_ERROR_TYPE(MounterRouterError, "MounterRouterError", 1234) { friend class MounterRouter; @@ -51,7 +54,7 @@ class QX_ERROR_TYPE(MounterRouterError, "MounterRouterError", 1234) QString deriveSecondary() const override; }; -class MounterRouter : public QObject +class MounterRouter : public QObject, public Directorate { Q_OBJECT @@ -85,16 +88,14 @@ class MounterRouter : public QObject //-Constructor------------------------------------------------------------------------------------------------- public: - explicit MounterRouter(QObject* parent = nullptr); + explicit MounterRouter(QObject* parent, Director* director); //-Instance Functions--------------------------------------------------------------------------------------------------------- private: void finish(const MounterRouterError& result); - void signalEventOccurred(const QString& event); - void signalErrorOccurred(const MounterRouterError& errorMessage); - public: + QString name() const override; bool isMounting(); quint16 routerPort() const; @@ -112,8 +113,6 @@ public slots: void abort(); signals: - void eventOccurred(const QString& name, const QString& event); - void errorOccurred(const QString& name, const MounterRouterError& errorMessage); void mountFinished(MounterRouterError errorState); };