kumir2-launcher.cpp 12.4 KB
Newer Older
Alexander A. Maly's avatar
Alexander A. Maly committed
1 2 3 4 5 6 7 8 9
#include <QDateTime>
#include <QDebug>
#include <QMessageBox>
#include <QTranslator>
#include <QDir>
#include <QIcon>
#include <QPainter>
#include <QSplashScreen>
#include <QApplication>
victor's avatar
 
victor committed
10

11 12
#include <kumir2-libs/extensionsystem/pluginmanager.h>
#include <kumir2-libs/extensionsystem/logger.h>
victor's avatar
 
victor committed
13

14
static QString resolvePath(const char *what)
15
{
16 17 18 19 20 21 22 23 24 25 26 27
	static QString ExecDir = QString::fromLatin1(KUMIR2_EXEC_DIR);
	static QString MyDir = QCoreApplication::applicationDirPath();
	static int RootDistLevelUp = ExecDir.isEmpty()
		? 0
		: ExecDir.count("/") + 1;
	QString result = MyDir;
	for (int i = 0; i < RootDistLevelUp; ++i) {
		result += "/../";
	}
	result += QString::fromLatin1(what);
	result = QDir::cleanPath(result);
	return result;
28
}
victor's avatar
 
victor committed
29

Victor Yacovlev's avatar
Victor Yacovlev committed
30
#if QT_VERSION < 0x050000
victor's avatar
 
victor committed
31
void GuiMessageOutput(QtMsgType type, const char *msg)
Victor Yacovlev's avatar
Victor Yacovlev committed
32
#else
33
void GuiMessageOutput(QtMsgType type, const QMessageLogContext &, const QString &msg)
Victor Yacovlev's avatar
Victor Yacovlev committed
34
#endif
victor's avatar
 
victor committed
35
{
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
	ExtensionSystem::Logger *logger = ExtensionSystem::Logger::instance();
	switch (type) {
	case QtDebugMsg:
		logger->debug(msg);
		break;
	case QtWarningMsg:
		logger->warning(msg);
		break;
	case QtCriticalMsg:
		logger->critical(msg);
		break;
	case QtFatalMsg:
		logger->fatal(msg);
		abort();
	default:
		break;
	}
victor's avatar
 
victor committed
53 54 55 56
}

void ConsoleMessageOutput(QtMsgType type, const char *msg)
{
57 58
	switch (type) {
	case QtDebugMsg:
victor's avatar
 
victor committed
59
//        fprintf(stderr, "Debug: %s\n", msg);
60 61
		break;
	case QtWarningMsg:
victor's avatar
 
victor committed
62
//        fprintf(stderr, "Warning: %s\n", msg);
63 64 65 66 67 68 69 70 71 72
		break;
	case QtCriticalMsg:
		fprintf(stderr, "Critical: %s\n", msg);
		break;
	case QtFatalMsg:
		fprintf(stderr, "Fatal: %s\n", msg);
		abort();
	default:
		break;
	}
victor's avatar
 
victor committed
73 74
}

75
void showErrorMessage(const QString &text)
victor's avatar
 
victor committed
76
{
77
	bool gui = true;
78
#ifdef Q_OS_LINUX
79
	gui = gui && getenv("DISPLAY") != 0;
victor's avatar
 
victor committed
80 81
#endif

82 83 84 85 86
	if (gui) {
		QMessageBox::critical(0, "Kumir 2 Launcher", text);
	} else {
		fprintf(stderr, "%s", qPrintable(text));
	}
victor's avatar
 
victor committed
87 88
}

victor's avatar
 
victor committed
89 90
QString getLanguage()
{
91
	return "ru"; // TODO implement sometime in future...
victor's avatar
 
victor committed
92 93
}

94 95 96
static
QList<QDir> translationsDirs()
{
97
	QList<QDir> result;
98

99 100 101 102 103 104
	// Translations dir from base distribution
	const QString sharePath = resolvePath(KUMIR2_RESOURCES_DIR);
	QDir baseTranslationsDir(sharePath + "/translations");
	if (baseTranslationsDir.exists()) {
		result.append(baseTranslationsDir);
	}
105 106

#ifdef Q_OS_LINUX
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
	// Search additional paths
	const QString homePath = QString::fromLocal8Bit(::getenv("HOME"));
	const QStringList extraPaths = QStringList()
		<< "/usr/share/kumir2/translations"
		<< "/usr/local/share/kumir2/translations"
		<< "/opt/kumir2/share/translations"
		<< "/opt/kumir/share/translations"
		<< homePath + "/.local/share/kumir2/translations"
		<< QDir::currentPath() + "/translations"
		;

	Q_FOREACH (const QString &path, extraPaths) {
		QDir candidate(path);
		if (candidate.exists()) {
			result.append(candidate);
		}
	}
124 125
#endif

126
	return result;
127
}
victor's avatar
 
victor committed
128

129
class Application : QObject
Victor Yacovlev's avatar
Victor Yacovlev committed
130 131
{
public:
Alexander A. Maly's avatar
Alexander A. Maly committed
132
	explicit Application(int &argc, char **argv, bool gui)
133 134 135 136 137 138 139 140 141 142 143 144 145 146
		: QObject()
		, _qApp(0)
		, _timerId(-1)
		, _splashScreen(nullptr)
		, _started(false)
	{
		if (gui) {
			_qApp = new QApplication(argc, argv);
		} else {
			_qApp = new QCoreApplication(argc, argv);
		}
		_qApp->installEventFilter(this);
	}

Alexander A. Maly's avatar
Alexander A. Maly committed
147
	void setSplashScreen(QSplashScreen *s)
148 149 150 151
	{
		_splashScreen = s;
	}

Alexander A. Maly's avatar
Alexander A. Maly committed
152
	void initialize()
153
	{
Alexander A. Maly's avatar
Alexander A. Maly committed
154
		QStringList arguments = _qApp->arguments();
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
		qDebug() << "Arguments: " << arguments;
		bool mustShowHelpAndExit = false;
		bool mustShowVersionAndExit = false;
		for (int i = 1; i < arguments.size(); i++) {
			const QString &argument = arguments[i];
			if (argument == "--help" || argument == "-h" || argument == "/?") {
				mustShowHelpAndExit = true;
				break;
			} else if (argument == "--version") {
				mustShowVersionAndExit = true;
				break;
			} else if (!argument.startsWith("-")) {
				break;
			}
		}

		bool gui = true;
172
#ifdef Q_OS_LINUX
173
		gui = gui && getenv("DISPLAY") != 0;
174
#endif
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
		const QList<QDir> tsDirs = translationsDirs();
		Q_FOREACH (const QDir &translationsDir, tsDirs) {
			QStringList ts_files = translationsDir.entryList(QStringList() << "*_" + getLanguage() + ".qm");
			foreach (QString tsname, ts_files) {
				tsname = tsname.mid(0, tsname.length() - 3);
				QTranslator *tr = new QTranslator(_qApp);
				if (tr->load(tsname, translationsDir.absolutePath())) {
					_qApp->installTranslator(tr);
				}
			}
		}

		const QString sharePath = resolvePath(KUMIR2_RESOURCES_DIR);
		_qApp->setProperty("sharePath", sharePath);

		QSettings::setDefaultFormat(QSettings::IniFormat);
		_qApp->addLibraryPath(resolvePath(KUMIR2_LIBS_DIR));
		_qApp->addLibraryPath(resolvePath(KUMIR2_PLUGINS_DIR));
		ExtensionSystem::PluginManager *manager = ExtensionSystem::PluginManager::instance();
		manager->setPluginPath(resolvePath(KUMIR2_PLUGINS_DIR));
		manager->setSharePath(sharePath);
		QString error;
		qDebug() << "Initialized plugin manager";
#ifdef CONFIGURATION_TEMPLATE
		const QByteArray defaultTemplate = CONFIGURATION_TEMPLATE;
#else
#error No default configuration passed to GCC
#endif
		QByteArray templ = defaultTemplate;
		for (int i = 1; i < arguments.size(); i++) {
			QByteArray arg = arguments[i].toLatin1();
			if (arg.startsWith("[") && arg.endsWith("]")) {
				templ = arg.mid(1, arg.length() - 2);
			}
		}
		qDebug() << "Loading plugins by template: " << templ;
		error = manager->loadPluginsByTemplate(templ);
		if (!gui && manager->isGuiRequired()) {
			if (_splashScreen) {
				_splashScreen->finish(0);
			}
			showErrorMessage("Requires X11 session to run this configuration");
			_qApp->exit(1);
		}
219

Victor Yacovlev's avatar
Victor Yacovlev committed
220 221 222
//        qInstallMsgHandler(manager->isGuiRequired()
//                           ? GuiMessageOutput
//                           : ConsoleMessageOutput);
223

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
		if (!error.isEmpty()) {
			if (_splashScreen) {
				_splashScreen->finish(0);
			}
			showErrorMessage(error);
			_qApp->exit(1);
		}

		qDebug() << "Done loading all plugins by template";

		if (mustShowHelpAndExit) {
			if (_splashScreen) {
				_splashScreen->finish(0);
			}
			const QString msg = manager->commandLineHelp();
239
#ifdef Q_OS_WIN32
240 241
			QTextCodec *codec = QTextCodec::codecForName("CP866");
			fprintf(stderr, "%s", codec->fromUnicode(msg).constData());
242
#else
243
			fprintf(stderr, "%s", msg.toLocal8Bit().data());
244
#endif
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
			_qApp->exit(0);
			return;
		}

		if (mustShowVersionAndExit) {
			fprintf(stderr, "%s\n", qPrintable(_qApp->applicationVersion()));
			_qApp->exit(0);
			return;
		}
		qDebug() << "Begin plugins initialization";
		error = manager->initializePlugins();
		if (!error.isEmpty()) {
			if (_splashScreen) {
				_splashScreen->finish(0);
			}
			showErrorMessage(error);
			_qApp->exit(_qApp->property("returnCode").isValid()
				? _qApp->property("returnCode").toInt() : 1);
		}
		// GUI requirement may be changed as result of plugins initialization,
		// so check it again
		qDebug() << "Plugins initialization done";
		if (!gui && manager->isGuiRequired()) {
			showErrorMessage("Requires X11 session to run this configuration");
			_qApp->exit(_qApp->property("returnCode").isValid()
				? _qApp->property("returnCode").toInt() : 1);
		}
		if (_splashScreen) {
			_splashScreen->finish(0);
		}
		qDebug() << "Starting entry point plugin";
		error = manager->start();
		if (!error.isEmpty()) {
			if (_splashScreen) {
				_splashScreen->finish(0);
			}
			showErrorMessage(error);
			_qApp->exit(_qApp->property("returnCode").isValid()
				? _qApp->property("returnCode").toInt() : 1);
		}
		if (!manager->isGuiRequired()) {
			_qApp->quit();
		}
	}

Alexander A. Maly's avatar
Alexander A. Maly committed
290
	bool eventFilter(QObject *obj, QEvent *event)
291 292 293 294 295 296 297 298 299 300 301 302 303
	{
		if (event->type() == QEvent::Timer && obj == _qApp && !_started) {
			_started = true;
			_qApp->killTimer(_timerId);
			qDebug() << "Begin initialization";
			initialize();
			qDebug() << "Initialization done";
			return true;
		} else {
			return QObject::eventFilter(obj, event);
		}
	}

Alexander A. Maly's avatar
Alexander A. Maly committed
304
	int main()
305 306 307 308 309 310 311 312 313 314
	{
		_timerId = _qApp->startTimer(250);
		int ret = _qApp->exec();
		if (ret == 0) {
			return _qApp->property("returnCode").isValid()
				? _qApp->property("returnCode").toInt() : 0;
		} else {
			return ret;
		}
	}
315

316
private:
317 318 319 320
	QCoreApplication *_qApp;
	int _timerId;
	QSplashScreen *_splashScreen;
	bool _started;
321

Victor Yacovlev's avatar
Victor Yacovlev committed
322 323
};

324 325

bool
326
setup_custom_vendor_information(QCoreApplication *app)
327
{
328 329 330 331 332 333
	QString appName;
	QString appVendor;
	QString appVersion;
	QString appLicenseFileName;
	QString appAboutFileName;
	bool result = false;
334
#ifdef APP_NAME
335 336
	appName = QString::fromLatin1(APP_NAME);
	result = true;
337 338
#endif
#ifdef APP_NAME_ru
339 340
	appName = QString::fromUtf8(APP_NAME_ru);
	result = true;
341 342
#endif
#ifdef APP_VENDOR
343
	appVendor = QString::fromLatin1(APP_VENDOR);
344 345
#endif
#ifdef APP_VENDOR_RU
346
	appVendor = QString::fromUtf8(APP_VENDOR_ru);
347 348
#endif
#ifdef APP_VERSION
349
	appVersion = QString::fromLatin1(APP_VERSION);
350 351
#endif
#ifdef APP_LICENSE
352
	appLicenseFileName = ":/kumir2-launcher/" + QString::fromLatin1(APP_LICENSE);
353 354
#endif
#ifdef APP_LICENSE_ru
355
	appLicenseFileName = ":/kumir2-launcher/" + QString::fromLatin1(APP_LICENSE_ru);
356 357
#endif
#ifdef APP_ABOUT
358
	appAboutFileName = ":/kumir2-launcher/" + QString::fromLatin1(APP_ABOUT);
359 360
#endif
#ifdef APP_ABOUT_ru
361
	appAboutFileName = ":/kumir2-launcher/" + QString::fromLatin1(APP_ABOUT_ru);
362
#endif
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
	if (appName.length() > 0) {
		app->setProperty("customAppName", appName);
	}
	if (appVendor.length() > 0) {
		app->setProperty("customAppVendor", appVendor);
	}
	if (appVersion.length() > 0) {
		app->setProperty("customAppVersion", appVersion);
	}
	if (appLicenseFileName.length() > 0) {
		app->setProperty("customAppLicense", appLicenseFileName);
	}
	if (appAboutFileName.length() > 0) {
		app->setProperty("customAppAbout", appAboutFileName);
	}
	return result;
379 380
}

victor's avatar
 
victor committed
381
int main(int argc, char **argv)
382
{
Victor Yacovlev's avatar
Victor Yacovlev committed
383
#if QT_VERSION < 0x050000
384
	qInstallMsgHandler(GuiMessageOutput);
Victor Yacovlev's avatar
Victor Yacovlev committed
385
#else
386
	qInstallMessageHandler(GuiMessageOutput);
Victor Yacovlev's avatar
Victor Yacovlev committed
387
#endif
388 389 390 391
	QString gitHash = QString::fromLatin1(GIT_HASH);
	QString gitTag = QString::fromLatin1(GIT_TAG);
	QString gitBranch = QString::fromLatin1(GIT_BRANCH);
	QDateTime gitTimeStamp = QDateTime::fromTime_t(QString::fromLatin1(GIT_TIMESTAMP).toUInt());
392

393
	bool gui = true;
394

395
#ifdef Q_OS_LINUX
396
	gui = gui && getenv("DISPLAY") != 0;
victor's avatar
 
victor committed
397
#endif
398 399 400

	Application *app = new Application(argc, argv, gui);

401
#ifdef WINDOW_ICON
402 403 404 405 406 407
	if (gui) {
		QApplication *guiApp = qobject_cast<QApplication *>(qApp);
		QString imgPath = ":/kumir2-launcher/" + QString::fromLatin1(WINDOW_ICON);
		QIcon icon(imgPath);
		guiApp->setWindowIcon(icon);
	}
408
#endif
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438

	QLocale russian = QLocale("ru_RU");
	QLocale::setDefault(russian);

	qApp->addLibraryPath(resolvePath(KUMIR2_LIBS_DIR));
	qApp->addLibraryPath(resolvePath(KUMIR2_PLUGINS_DIR));

	qApp->setApplicationVersion(gitTag.length() > 0 && gitTag != "unknown"
		? gitTag : gitBranch + "/" + gitHash);
	qApp->setProperty("gitTimeStamp", gitTimeStamp);


	QString sharePath = resolvePath(KUMIR2_RESOURCES_DIR);

	QStringList arguments = QCoreApplication::instance()->arguments();
	bool mustShowHelpAndExit = false;
	bool mustShowVersionAndExit = false;
	for (int i = 1; i < arguments.size(); i++) {
		const QString &argument = arguments[i];
		if (argument == "--help" || argument == "-h" || argument == "/?") {
			mustShowHelpAndExit = true;
			break;
		} else if (argument == "--version") {
			mustShowVersionAndExit = true;
			break;
		} else if (!argument.startsWith("-")) {
			break;
		}
	}

Alexander A. Maly's avatar
Alexander A. Maly committed
439 440 441 442
	bool customAppAndVendorInformation = setup_custom_vendor_information(qobject_cast<QCoreApplication *>(qApp));

	Q_UNUSED(mustShowHelpAndExit);
	Q_UNUSED(mustShowVersionAndExit);
443
	Q_UNUSED(customAppAndVendorInformation);
444

victor's avatar
 
victor committed
445
#ifdef SPLASHSCREEN
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
	QSplashScreen *splashScreen = 0;
	if (gui && !mustShowHelpAndExit && !mustShowVersionAndExit) {
		QString imgPath = ":/kumir2-launcher/" + QString::fromLatin1(SPLASHSCREEN);
		splashScreen = new QSplashScreen();
		QImage img(imgPath);
		QPainter p(&img);
		p.setPen(QColor(Qt::black));
		p.setBrush(QColor(Qt::black));
		QFont f = p.font();

		f.setPixelSize(12);
		p.setFont(f);

		QString v;
		if (customAppAndVendorInformation) {
			v = qApp->property("customAppVersion").toString() + " ";
			v += "[based on Kumir ";
		}
		v = qApp->applicationVersion();
		if (qApp->property("gitHash").isValid()) {
			v += " (GIT " + qApp->property("gitHash").toString() + ")";
		}
		if (customAppAndVendorInformation) {
			v += "]";
		}
		int tw = QFontMetrics(f).width(v);
		int th = QFontMetrics(f).height() * 5;
		int x = img.width() - tw - 8;
		int y = 8;
		p.drawText(x, y, tw, th, 0, v);
		p.end();
		QPixmap px = QPixmap::fromImage(img);
		splashScreen->setPixmap(px);
		splashScreen->show();
		qApp->processEvents();
		app->setSplashScreen(splashScreen);
	}
#endif

	int ret = app->main();
	ExtensionSystem::PluginManager::destroy();
	delete app;
	return ret;
victor's avatar
 
victor committed
489 490
}