diff --git a/src/Application.cpp b/src/Application.cpp index 7e23feeb..4676359f 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -1,6 +1,7 @@ #include "Application.hpp" #include +#include #include #include #include @@ -20,6 +21,15 @@ namespace Bloom Application::Application(std::vector&& arguments) : arguments(std::move(arguments)) + , qtApplication( + ( + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true), +#ifndef BLOOM_DEBUG_BUILD + QCoreApplication::addLibraryPath(QString::fromStdString(Services::PathService::applicationDirPath() + "/plugins")), +#endif + QApplication(this->qtApplicationArgc, this->qtApplicationArgv.data()) + ) + ) {} int Application::run() { @@ -57,21 +67,22 @@ namespace Bloom this->startup(); #ifndef EXCLUDE_INSIGHT - this->insightActivationPending = this->insightConfig->activateOnStartup; -#endif - - // Main event loop - while (Thread::getThreadState() == ThreadState::READY) { -#ifndef EXCLUDE_INSIGHT - if (this->insightActivationPending) { - this->insightActivationPending = false; - this->startInsight(); - continue; - } -#endif - - this->applicationEventListener->waitAndDispatch(); + if (this->insightConfig->activateOnStartup) { + this->activateInsight(); } +#endif + + /* + * We can't run our own event loop here - we have to use Qt's event loop. But we still need to be able to + * process our events. To address this, we use a QTimer to dispatch our events on an interval. + * + * This allows us to use Qt's event loop whilst still being able to process our own events. + */ + auto* eventDispatchTimer = new QTimer(&(this->qtApplication)); + QObject::connect(eventDispatchTimer, &QTimer::timeout, this, &Application::dispatchEvents); + eventDispatchTimer->start(100); + + this->qtApplication.exec(); } catch (const InvalidConfig& exception) { Logger::error("Invalid project configuration (bloom.yaml) - " + exception.getMessage()); @@ -176,6 +187,7 @@ namespace Bloom this->stopSignalHandler(); this->saveProjectSettings(); + this->qtApplication.exit(0); Thread::threadState = ThreadState::STOPPED; } @@ -498,8 +510,12 @@ namespace Bloom } } + void Application::dispatchEvents() { + this->applicationEventListener->dispatchCurrentEvents(); + } + #ifndef EXCLUDE_INSIGHT - void Application::startInsight() { + void Application::activateInsight() { assert(!this->insight); this->insight = std::make_unique( @@ -507,29 +523,21 @@ namespace Bloom this->projectConfig.value(), this->environmentConfig.value(), this->insightConfig.value(), - this->projectSettings.value().insightSettings + this->projectSettings.value().insightSettings, + &(this->qtApplication) ); - /* - * Before letting Insight occupy the main thread, process any pending events that accumulated - * during startup. - */ - this->applicationEventListener->dispatchCurrentEvents(); - - if (Thread::getThreadState() == ThreadState::READY) { - this->insight->run(); - Logger::debug("Insight closed"); - } + this->insight->activate(); } void Application::onInsightActivationRequest(const Events::InsightActivationRequested&) { if (this->insight) { - // Insight has already been started + // Insight has already been activated this->insight->showMainWindow(); return; } - this->insightActivationPending = true; + this->activateInsight(); } #endif @@ -540,7 +548,7 @@ namespace Bloom void Application::onTargetControllerThreadStateChanged(const Events::TargetControllerThreadStateChanged& event) { if (event.getState() == ThreadState::STOPPED || event.getState() == ThreadState::SHUTDOWN_INITIATED) { - // TargetController has unexpectedly shutdown - it must have encountered a fatal error. + // TargetController has unexpectedly shutdown. this->shutdown(); } } diff --git a/src/Application.hpp b/src/Application.hpp index b4a726cf..a46b37e0 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "src/Helpers/Thread.hpp" @@ -35,8 +36,10 @@ namespace Bloom * This is the main entry-point of execution for the Bloom program. The methods within will run on the main * thread. If Insight is enabled, execution will be passed over to Insight::run() upon start up. */ - class Application: public Thread + class Application: public QObject, public Thread { + Q_OBJECT + public: static const inline VersionNumber VERSION = VersionNumber(std::string(BLOOM_VERSION)); @@ -52,6 +55,11 @@ namespace Bloom private: std::vector arguments; + std::string qtApplicationName = "Bloom"; + std::array qtApplicationArgv = {this->qtApplicationName.data()}; + int qtApplicationArgc = 1; + QApplication qtApplication; + EventListenerPointer applicationEventListener = std::make_shared("ApplicationEventListener"); /** @@ -238,13 +246,18 @@ namespace Bloom */ void stopDebugServer(); + /** + * Dispatches any pending events. This function will be called periodically, via a QTimer. + */ + void dispatchEvents(); + #ifndef EXCLUDE_INSIGHT /** - * Starts the Insight GUI. + * Activate the Insight GUI. * * This function should never be called more than once. */ - void startInsight(); + void activateInsight(); /** * Handles requests to start the Insight GUI. diff --git a/src/Insight/Insight.cpp b/src/Insight/Insight.cpp index 2c44ae7e..cc31f0e2 100644 --- a/src/Insight/Insight.cpp +++ b/src/Insight/Insight.cpp @@ -28,52 +28,24 @@ namespace Bloom const ProjectConfig& projectConfig, const EnvironmentConfig& environmentConfig, const InsightConfig& insightConfig, - InsightProjectSettings& insightProjectSettings + InsightProjectSettings& insightProjectSettings, + QApplication* parent ) - : eventListener(eventListener) + : QObject(parent) + , eventListener(eventListener) , projectConfig(projectConfig) , environmentConfig(environmentConfig) , insightConfig(insightConfig) , insightProjectSettings(insightProjectSettings) - , application( - ( - QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true), -#ifndef BLOOM_DEBUG_BUILD - QCoreApplication::addLibraryPath(QString::fromStdString(Services::PathService::applicationDirPath() + "/plugins")), -#endif - QApplication(this->qtApplicationArgc, this->qtApplicationArgv.data()) - ) - ) {} - void Insight::run() { - try { - this->startup(); - - this->threadState = ThreadState::READY; - Logger::info("Insight ready"); - this->application.exec(); - - } catch (const Exception& exception) { - Logger::error("Insight encountered a fatal error. See below for errors:"); - Logger::error(exception.getMessage()); - - } catch (const std::exception& exception) { - Logger::error("Insight encountered a fatal error. See below for errors:"); - Logger::error(std::string(exception.what())); - } - - this->shutdown(); - } - void Insight::showMainWindow() { this->mainWindow->show(); this->mainWindow->activateWindow(); } - void Insight::startup() { + void Insight::activate() { Logger::info("Starting Insight"); - this->threadState = ThreadState::STARTING; this->eventListener.registerCallbackForEventType( std::bind(&Insight::onTargetStoppedEvent, this, std::placeholders::_1) @@ -116,8 +88,6 @@ namespace Bloom throw Exception("Failed to open global stylesheet file"); } - this->application.setStyleSheet(globalStylesheet.readAll()); - qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); @@ -168,16 +138,6 @@ namespace Bloom QString::fromStdString(Services::PathService::resourcesDirPath() + "/fonts/Ubuntu/Ubuntu-Th.ttf") ); - /* - * We can't run our own event loop here - we have to use Qt's event loop. But we still need to be able to - * process our events. To address this, we use a QTimer to dispatch our events on an interval. - * - * This allows us to use Qt's event loop whilst still being able to process our own events. - */ - auto* eventDispatchTimer = new QTimer(&(this->application)); - QObject::connect(eventDispatchTimer, &QTimer::timeout, this, &Insight::dispatchEvents); - eventDispatchTimer->start(100); - QObject::connect( this->mainWindow, &InsightWindow::activatedSignal, @@ -185,6 +145,8 @@ namespace Bloom &Insight::onInsightWindowActivated ); + this->mainWindow->setStyleSheet(globalStylesheet.readAll()); + this->mainWindow->setInsightConfig(this->insightConfig); this->mainWindow->setEnvironmentConfig(this->environmentConfig); @@ -212,10 +174,6 @@ namespace Bloom } void Insight::shutdown() { - if (this->getThreadState() == ThreadState::STOPPED) { - return; - } - Logger::info("Shutting down Insight"); this->mainWindow->close(); @@ -230,9 +188,6 @@ namespace Bloom workerThread->wait(); } } - - this->application.exit(0); - this->threadState = ThreadState::STOPPED; } void Insight::checkBloomVersion() { diff --git a/src/Insight/Insight.hpp b/src/Insight/Insight.hpp index f19cfcbc..56484b3e 100644 --- a/src/Insight/Insight.hpp +++ b/src/Insight/Insight.hpp @@ -35,7 +35,7 @@ namespace Bloom * * The Insight component occupies the Bloom's main thread. See Application::run() for more. */ - class Insight: public QObject, public Thread + class Insight: public QObject { Q_OBJECT @@ -53,13 +53,14 @@ namespace Bloom const ProjectConfig& projectConfig, const EnvironmentConfig& environmentConfig, const InsightConfig& insightConfig, - InsightProjectSettings& insightProjectSettings + InsightProjectSettings& insightProjectSettings, + QApplication* parent ); /** * Entry point for Insight. */ - void run(); + void activate(); /** * Opens main window and obtains focus. @@ -73,9 +74,6 @@ namespace Bloom private: static constexpr std::uint8_t INSIGHT_WORKER_COUNT = 3; - std::string qtApplicationName = "Bloom"; - std::array qtApplicationArgv = {this->qtApplicationName.data()}; - int qtApplicationArgc = 1; ProjectConfig projectConfig; EnvironmentConfig environmentConfig; @@ -86,8 +84,6 @@ namespace Bloom EventListener& eventListener; Services::TargetControllerService targetControllerService = Services::TargetControllerService(); - QApplication application; - std::map> insightWorkersById; InsightWindow* mainWindow = new InsightWindow( this->environmentConfig, @@ -102,8 +98,6 @@ namespace Bloom InsightSignals* insightSignals = InsightSignals::instance(); - void startup(); - /** * Queries the Bloom server for the latest version number. If the current version number doesn't match the * latest version number returned by the server, we'll display a warning in the logs to instruct the user to @@ -111,17 +105,6 @@ namespace Bloom */ void checkBloomVersion(); - /** - * Dispatches any events currently in the queue. - * - * Because Insight is effectively a Qt application, we cannot use our own event loop. We must use Qt's event - * loop. We do this by utilizing a QTimer instance to call this method on an interval. - * See Insight::startup() for more. - */ - void dispatchEvents() { - this->eventListener.dispatchCurrentEvents(); - }; - void onInsightWindowActivated(); void onTargetStoppedEvent(const Events::TargetExecutionStopped& event); void onTargetResumedEvent(const Events::TargetExecutionResumed& event);