Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 : #include "baseapplication.h"
23 :
24 : #include "global/types/version.h"
25 : #include "global/globalmodule.h"
26 : #include "global/async/processevents.h"
27 :
28 : #include "muse_framework_config.h"
29 :
30 : #ifndef NO_QT_SUPPORT
31 : #include <QApplication>
32 : #endif
33 :
34 : #ifdef QT_QPROCESS_SUPPORTED
35 : #include <QProcess>
36 : #endif
37 :
38 : #ifdef QT_CONCURRENT_SUPPORTED
39 : #include <QThreadPool>
40 : #endif
41 :
42 : #include "log.h"
43 :
44 : using namespace muse;
45 :
46 92 : String BaseApplication::appName()
47 : {
48 : #ifdef MUSE_APP_NAME_MACHINE_READABLE
49 92 : return String::fromAscii(MUSE_APP_NAME_MACHINE_READABLE);
50 : #else
51 : return String();
52 : #endif
53 : }
54 :
55 0 : String BaseApplication::appTitle()
56 : {
57 : #ifdef MUSE_APP_NAME_HUMAN_READABLE
58 : #ifdef MUSE_APP_UNSTABLE
59 0 : return String::fromAscii(MUSE_APP_NAME_HUMAN_READABLE) + u" Development";
60 : #else
61 : return String::fromAscii(MUSE_APP_NAME_HUMAN_READABLE);
62 : #endif
63 : #else
64 : return String();
65 : #endif
66 : }
67 :
68 0 : bool BaseApplication::appUnstable()
69 : {
70 : #ifdef MUSE_APP_UNSTABLE
71 0 : return true;
72 : #else
73 : return false;
74 : #endif
75 : }
76 :
77 0 : Version BaseApplication::appVersion()
78 : {
79 : #ifdef MUSE_APP_VERSION
80 0 : static Version v(MUSE_APP_VERSION);
81 : #else
82 : static Version v("0.0.0");
83 : #endif
84 0 : return v;
85 : }
86 :
87 0 : Version BaseApplication::appFullVersion()
88 : {
89 0 : static Version fv(appVersion());
90 :
91 : #ifdef MUSE_APP_VERSION_LABEL
92 : static bool once = false;
93 : if (!once) {
94 : String versionLabel = String::fromAscii(MUSE_APP_VERSION_LABEL);
95 : if (!versionLabel.isEmpty()) {
96 : fv.setSuffix(versionLabel);
97 : }
98 : once = true;
99 : }
100 : #endif
101 :
102 0 : return fv;
103 : }
104 :
105 0 : String BaseApplication::appBuild()
106 : {
107 : #ifdef MUSE_APP_BUILD_NUMBER
108 0 : return String::fromAscii(MUSE_APP_BUILD_NUMBER);
109 : #else
110 : return String();
111 : #endif
112 : }
113 :
114 0 : String BaseApplication::appRevision()
115 : {
116 : #ifdef MUSE_APP_REVISION
117 0 : return String::fromAscii(MUSE_APP_REVISION);
118 : #else
119 : return String();
120 : #endif
121 : }
122 :
123 0 : BaseApplication::BaseApplication(const std::shared_ptr<CmdOptions>& appOptions)
124 0 : : m_appOptions(appOptions)
125 : {
126 0 : }
127 :
128 0 : void BaseApplication::setRunMode(const RunMode& mode)
129 : {
130 0 : m_runMode = mode;
131 0 : }
132 :
133 0 : IApplication::RunMode BaseApplication::runMode() const
134 : {
135 0 : return m_runMode;
136 : }
137 :
138 0 : bool BaseApplication::noGui() const
139 : {
140 0 : switch (m_runMode) {
141 0 : case RunMode::GuiApp: return false;
142 0 : case RunMode::ConsoleApp: return true;
143 0 : case RunMode::AudioPluginRegistration: return true;
144 : }
145 0 : return false;
146 : }
147 :
148 0 : void BaseApplication::addModule(muse::modularity::IModuleSetup* module)
149 : {
150 0 : m_modules.push_back(module);
151 0 : }
152 :
153 0 : void BaseApplication::setup()
154 : {
155 0 : doSetup(m_appOptions);
156 0 : }
157 :
158 0 : void BaseApplication::doSetup(const std::shared_ptr<CmdOptions>& options)
159 : {
160 0 : IF_ASSERT_FAILED(options) {
161 0 : return;
162 : }
163 :
164 0 : const IApplication::RunMode runMode = options->runMode;
165 :
166 0 : setRunMode(runMode);
167 :
168 : // ====================================================
169 : // Setup modules: Resources, Exports, Imports, UiTypes
170 : // ====================================================
171 :
172 0 : modularity::globalIoc()->registerExport<IApplication>("global", shared_from_this());
173 :
174 0 : m_globalModule = new GlobalModule();
175 0 : m_globalModule->registerResources();
176 0 : m_globalModule->registerExports();
177 0 : m_globalModule->registerUiTypes();
178 :
179 0 : for (modularity::IModuleSetup* m : m_modules) {
180 0 : m->registerResources();
181 0 : m->registerExports();
182 : }
183 :
184 0 : m_globalModule->resolveImports();
185 0 : m_globalModule->registerApi();
186 0 : for (modularity::IModuleSetup* m : m_modules) {
187 0 : m->registerUiTypes();
188 0 : m->resolveImports();
189 : }
190 :
191 : // ====================================================
192 : // Setup modules: apply the command line options
193 : // ====================================================
194 0 : applyCommandLineOptions(options);
195 :
196 : // ====================================================
197 : // Setup modules: onPreInit
198 : // ====================================================
199 0 : m_globalModule->onPreInit(runMode);
200 0 : for (modularity::IModuleSetup* m : m_modules) {
201 0 : m->onPreInit(runMode);
202 : }
203 :
204 : // ====================================================
205 : // Setup modules: onInit
206 : // ====================================================
207 0 : m_globalModule->onInit(runMode);
208 0 : for (modularity::IModuleSetup* m : m_modules) {
209 0 : m->onInit(runMode);
210 0 : m->registerApi();
211 : }
212 :
213 : // ====================================================
214 : // Setup modules: onAllInited
215 : // ====================================================
216 0 : m_globalModule->onAllInited(runMode);
217 0 : for (modularity::IModuleSetup* m : m_modules) {
218 0 : m->onAllInited(runMode);
219 : }
220 :
221 : // ====================================================
222 : // Setup modules: onStartApp (on next event loop)
223 : // ====================================================
224 0 : QMetaObject::invokeMethod(qApp, [this]() {
225 0 : m_globalModule->onStartApp();
226 0 : for (modularity::IModuleSetup* m : m_modules) {
227 0 : m->onStartApp();
228 : }
229 0 : }, Qt::QueuedConnection);
230 : }
231 :
232 0 : void BaseApplication::applyCommandLineOptions(const std::shared_ptr<CmdOptions>& options)
233 : {
234 0 : if (options->global.loggerLevel) {
235 0 : m_globalModule->setLoggerLevel(options->global.loggerLevel.value());
236 : }
237 0 : }
238 :
239 0 : BaseApplication::ContextData& BaseApplication::contextData(const muse::modularity::ContextPtr& ctx)
240 : {
241 0 : for (ContextData& c : m_contexts) {
242 0 : if (c.ctxId->id == ctx->id) {
243 0 : return c;
244 : }
245 : }
246 :
247 0 : m_contexts.emplace_back();
248 :
249 0 : ContextData& ref = m_contexts.back();
250 0 : ref.ctxId = ctx;
251 :
252 0 : modularity::IContextSetup* global = m_globalModule->newContext(ctx);
253 0 : if (global) {
254 0 : ref.setups.push_back(global);
255 : }
256 :
257 0 : for (modularity::IModuleSetup* m : m_modules) {
258 0 : modularity::IContextSetup* s = m->newContext(ctx);
259 0 : if (s) {
260 0 : ref.setups.push_back(s);
261 : }
262 : }
263 :
264 0 : return ref;
265 : }
266 :
267 0 : std::vector<muse::modularity::ContextPtr> BaseApplication::contexts() const
268 : {
269 0 : std::vector<muse::modularity::ContextPtr> ctxs;
270 0 : ctxs.reserve(m_contexts.size());
271 0 : for (const ContextData& c : m_contexts) {
272 0 : ctxs.push_back(c.ctxId);
273 : }
274 0 : return ctxs;
275 0 : }
276 :
277 0 : size_t BaseApplication::contextCount() const
278 : {
279 0 : return m_contexts.size();
280 : }
281 :
282 0 : std::shared_ptr<CmdOptions> BaseApplication::makeContextOptions(const muse::StringList&) const
283 : {
284 0 : return m_appOptions;
285 : }
286 :
287 0 : void BaseApplication::showContextSplash(const muse::modularity::ContextPtr&)
288 : {
289 0 : }
290 :
291 0 : muse::modularity::ContextPtr BaseApplication::setupNewContext(const StringList& args)
292 : {
293 : #ifndef MUSE_MODULE_MULTIWINDOWS_SINGLEPROC_MODE
294 : static bool once = false;
295 : IF_ASSERT_FAILED(!once) {
296 : return nullptr;
297 : }
298 : once = true;
299 : #endif
300 :
301 : static int lastId = 0;
302 :
303 0 : modularity::ContextPtr ctxId = std::make_shared<modularity::Context>();
304 0 : ++lastId;
305 0 : ctxId->id = lastId;
306 :
307 0 : ContextData& ctx = contextData(ctxId);
308 0 : ctx.initializing = true;
309 0 : ctx.options = makeContextOptions(args);
310 :
311 0 : LOGI() << "Creating new context with id: " << ctxId->id;
312 :
313 0 : QMetaObject::invokeMethod(qApp, [this, ctxId]() {
314 0 : showContextSplash(ctxId);
315 0 : QMetaObject::invokeMethod(qApp, [this, ctxId]() {
316 0 : setupContext(ctxId);
317 0 : QMetaObject::invokeMethod(qApp, [this, ctxId]() {
318 0 : ContextData& ctx = contextData(ctxId);
319 0 : ctx.initializing = false;
320 0 : startupScenario(ctxId);
321 0 : }, Qt::QueuedConnection);
322 0 : }, Qt::QueuedConnection);
323 0 : }, Qt::QueuedConnection);
324 :
325 0 : return ctxId;
326 0 : }
327 :
328 0 : void BaseApplication::setupContext(const muse::modularity::ContextPtr& ctxId)
329 : {
330 0 : const IApplication::RunMode runMode = m_appOptions->runMode;
331 : // Setup
332 0 : std::vector<muse::modularity::IContextSetup*>& csetups = contextData(ctxId).setups;
333 :
334 0 : for (modularity::IContextSetup* s : csetups) {
335 0 : s->registerExports();
336 : }
337 :
338 0 : for (modularity::IContextSetup* s : csetups) {
339 0 : s->resolveImports();
340 : }
341 :
342 0 : for (modularity::IContextSetup* s : csetups) {
343 0 : s->onPreInit(runMode);
344 : }
345 :
346 0 : for (modularity::IContextSetup* s : csetups) {
347 0 : s->onInit(runMode);
348 : }
349 :
350 0 : for (modularity::IContextSetup* s : csetups) {
351 0 : s->onAllInited(runMode);
352 : }
353 0 : }
354 :
355 0 : void BaseApplication::destroyContext(const modularity::ContextPtr& ctx)
356 : {
357 0 : TRACEFUNC;
358 :
359 0 : IF_ASSERT_FAILED(ctx) {
360 0 : return;
361 : }
362 :
363 0 : LOGI() << "Destroying context with id: " << ctx->id;
364 :
365 0 : auto it = std::find_if(m_contexts.begin(), m_contexts.end(),
366 0 : [&ctx](const ContextData& c) { return c.ctxId->id == ctx->id; });
367 0 : if (it == m_contexts.end()) {
368 0 : LOGW() << "Context not found: " << ctx->id;
369 0 : return;
370 : }
371 :
372 : //! The context is in the process of initializing.
373 : //! It is not possible to delete the context until initialization is complete.
374 0 : IF_ASSERT_FAILED(!it->initializing) {
375 0 : return;
376 : }
377 :
378 0 : doDestroyContext(*it);
379 :
380 0 : m_contexts.erase(it);
381 0 : }
382 :
383 0 : void BaseApplication::doDestroyContext(const ContextData& data)
384 : {
385 0 : for (modularity::IContextSetup* s : data.setups) {
386 0 : s->onDeinit();
387 : }
388 :
389 0 : for (modularity::IContextSetup* s : data.setups) {
390 0 : s->onDestroy();
391 : }
392 :
393 0 : qDeleteAll(data.setups);
394 :
395 0 : modularity::removeIoC(data.ctxId);
396 0 : }
397 :
398 : #ifndef NO_QT_SUPPORT
399 0 : QWindow* BaseApplication::focusWindow() const
400 : {
401 0 : return qApp->focusWindow();
402 : }
403 :
404 0 : bool BaseApplication::notify(QObject* object, QEvent* event)
405 : {
406 0 : return qApp->notify(object, event);
407 : }
408 :
409 0 : Qt::KeyboardModifiers BaseApplication::keyboardModifiers() const
410 : {
411 0 : return QApplication::keyboardModifiers();
412 : }
413 :
414 : #endif
415 :
416 0 : void BaseApplication::restart()
417 : {
418 0 : m_finishMode = FinishMode::Restart;
419 0 : QCoreApplication::exit();
420 0 : }
421 :
422 0 : void BaseApplication::finish()
423 : {
424 0 : TRACEFUNC;
425 :
426 0 : doFinish();
427 :
428 0 : PROFILER_PRINT;
429 0 : }
430 :
431 0 : void BaseApplication::doFinish()
432 : {
433 : // Wait Thread Poll
434 : #ifdef QT_CONCURRENT_SUPPORTED
435 0 : QThreadPool* globalThreadPool = QThreadPool::globalInstance();
436 0 : if (globalThreadPool) {
437 0 : LOGI() << "activeThreadCount: " << globalThreadPool->activeThreadCount();
438 0 : globalThreadPool->waitForDone();
439 : }
440 : #endif
441 :
442 : // Deinit
443 0 : async::processMessages();
444 :
445 : // Deinit and delete contexts
446 0 : std::vector<muse::modularity::ContextPtr> ctxs = contexts();
447 0 : for (auto& c : ctxs) {
448 0 : destroyContext(c);
449 : }
450 :
451 0 : for (modularity::IModuleSetup* m : m_modules) {
452 0 : m->onDeinit();
453 : }
454 :
455 0 : m_globalModule->onDeinit();
456 :
457 0 : for (modularity::IModuleSetup* m : m_modules) {
458 0 : m->onDestroy();
459 : }
460 :
461 0 : m_globalModule->onDestroy();
462 :
463 : // Delete modules
464 0 : qDeleteAll(m_modules);
465 0 : m_modules.clear();
466 :
467 0 : delete m_globalModule;
468 0 : m_globalModule = nullptr;
469 :
470 0 : muse::modularity::resetAll();
471 :
472 0 : if (m_finishMode == IApplication::FinishMode::Restart) {
473 : #ifdef QT_QPROCESS_SUPPORTED
474 0 : QString program = qApp->arguments()[0];
475 :
476 : // NOTE: remove the first argument - the program name
477 0 : QStringList arguments = qApp->arguments().mid(1);
478 :
479 0 : QProcess::startDetached(program, arguments);
480 : #else
481 : NOT_SUPPORTED;
482 : #endif
483 0 : }
484 0 : }
485 :
486 0 : void BaseApplication::processEvents()
487 : {
488 0 : qApp->processEvents();
489 0 : tickerProvider()->forceSchedule();
490 0 : }
|