kumir2-launcher.cpp 14.3 KB
Newer Older
victor's avatar
 
victor committed
1
#include <QtCore>
Victor Yacovlev's avatar
Victor Yacovlev committed
2 3 4
#if QT_VERSION >= 0x050000
#include <QtWidgets>
#else
victor's avatar
 
victor committed
5
#include <QtGui>
Victor Yacovlev's avatar
Victor Yacovlev committed
6
#endif
victor's avatar
 
victor committed
7

8 9
#include <kumir2-libs/extensionsystem/pluginmanager.h>
#include <kumir2-libs/extensionsystem/logger.h>
victor's avatar
 
victor committed
10 11


12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
static
QString resolvePath(const char *what)
{
    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;
}
victor's avatar
 
victor committed
28

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

void ConsoleMessageOutput(QtMsgType type, const char *msg)
{
    switch (type) {
    case QtDebugMsg:
//        fprintf(stderr, "Debug: %s\n", msg);
        break;
    case QtWarningMsg:
//        fprintf(stderr, "Warning: %s\n", msg);
        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
74 75 76
void showErrorMessage(const QString & text)
{
    bool gui = true;
77
#ifdef Q_OS_LINUX
victor's avatar
 
victor committed
78 79 80 81 82 83 84
    gui = gui && getenv("DISPLAY")!=0;
#endif

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

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

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
static
QList<QDir> translationsDirs()
{
    QList<QDir> result;

    // Translations dir from base distribution
    const QString sharePath = resolvePath(KUMIR2_RESOURCES_DIR);
    QDir baseTranslationsDir(sharePath+"/translations");
    if (baseTranslationsDir.exists()) {
        result.append(baseTranslationsDir);
    }

#ifdef Q_OS_LINUX
    // 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);
        }
    }
#endif

    return result;
}
victor's avatar
 
victor committed
128

129
class Application : QObject
Victor Yacovlev's avatar
Victor Yacovlev committed
130 131
{
public:
132
    inline 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);
    }
147 148

    inline void setSplashScreen(QSplashScreen * s) {
149
        _splashScreen = s;
150 151 152
    }

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

172
        bool gui = true;
173
#ifdef Q_OS_LINUX
174 175
        gui = gui && getenv("DISPLAY")!=0;
#endif
176 177 178 179 180 181 182 183 184
        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);
                }
185 186
            }
        }
187 188

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

        QSettings::setDefaultFormat(QSettings::IniFormat);
192 193
        _qApp->addLibraryPath(resolvePath(KUMIR2_LIBS_DIR));
        _qApp->addLibraryPath(resolvePath(KUMIR2_PLUGINS_DIR));
Victor Yacovlev's avatar
Victor Yacovlev committed
194
        ExtensionSystem::PluginManager * manager = ExtensionSystem::PluginManager::instance();
195
        manager->setPluginPath(resolvePath(KUMIR2_PLUGINS_DIR));
196
        manager->setSharePath(sharePath);
197
        QString error;
Victor Yacovlev's avatar
Victor Yacovlev committed
198
        qDebug() << "Initialized plugin manager";
199
    #ifdef CONFIGURATION_TEMPLATE
200
        const QByteArray defaultTemplate = CONFIGURATION_TEMPLATE;
201 202 203
    #else
    #error No default configuration passed to GCC
    #endif
204
        QByteArray templ = defaultTemplate;
Victor Yacovlev's avatar
Victor Yacovlev committed
205
        for (int i=1; i<arguments.size(); i++) {
206
            QByteArray arg = arguments[i].toLatin1();
207 208 209 210
            if (arg.startsWith("[") && arg.endsWith("]")) {
                templ = arg.mid(1, arg.length()-2);
            }
        }
Victor Yacovlev's avatar
Victor Yacovlev committed
211
        qDebug() << "Loading plugins by template: " << templ;
212 213
        error = manager->loadPluginsByTemplate(templ);
        if (!gui && manager->isGuiRequired()) {
214 215
            if (_splashScreen)
                _splashScreen->finish(0);
216
            showErrorMessage("Requires X11 session to run this configuration");
217
            _qApp->exit(1);
218 219
        }

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

        if (!error.isEmpty()) {
225 226
            if (_splashScreen)
                _splashScreen->finish(0);
227
            showErrorMessage(error);
228
            _qApp->exit(1);
229
        }
230

Victor Yacovlev's avatar
Victor Yacovlev committed
231 232
        qDebug() << "Done loading all plugins by template";

233
        if (mustShowHelpAndExit) {
234 235
            if (_splashScreen)
                _splashScreen->finish(0);
236 237 238 239 240 241 242
            const QString msg = manager->commandLineHelp();
#ifdef Q_OS_WIN32
            QTextCodec * codec = QTextCodec::codecForName("CP866");
            fprintf(stderr, "%s", codec->fromUnicode(msg).constData());
#else
            fprintf(stderr, "%s", msg.toLocal8Bit().data());
#endif
243
            _qApp->exit(0);
244 245
            return;
        }
Victor Yacovlev's avatar
Victor Yacovlev committed
246 247

        if (mustShowVersionAndExit) {
248 249
            fprintf(stderr, "%s\n", qPrintable(_qApp->applicationVersion()));
            _qApp->exit(0);
Victor Yacovlev's avatar
Victor Yacovlev committed
250
            return;
Victor Yacovlev's avatar
Victor Yacovlev committed
251 252
        }        
        qDebug() << "Begin plugins initialization";
253 254
        error = manager->initializePlugins();
        if (!error.isEmpty()) {
255 256
            if (_splashScreen)
                _splashScreen->finish(0);
257
            showErrorMessage(error);
258 259
            _qApp->exit(_qApp->property("returnCode").isValid()
                    ? _qApp->property("returnCode").toInt() : 1);
260 261 262
        }
        // GUI requirement may be changed as result of plugins initialization,
        // so check it again
Victor Yacovlev's avatar
Victor Yacovlev committed
263
        qDebug() << "Plugins initialization done";
264 265
        if (!gui && manager->isGuiRequired()) {
            showErrorMessage("Requires X11 session to run this configuration");
266 267
            _qApp->exit(_qApp->property("returnCode").isValid()
                    ? _qApp->property("returnCode").toInt() : 1);
268
        }
269 270
        if (_splashScreen)
            _splashScreen->finish(0);
Victor Yacovlev's avatar
Victor Yacovlev committed
271
        qDebug() << "Starting entry point plugin";
272 273
        error = manager->start();
        if (!error.isEmpty()) {
274 275
            if (_splashScreen)
                _splashScreen->finish(0);
276
            showErrorMessage(error);
277 278
            _qApp->exit(_qApp->property("returnCode").isValid()
                    ? _qApp->property("returnCode").toInt() : 1);
279 280
        }
        if (!manager->isGuiRequired()) {
281
            _qApp->quit();
282 283 284
        }
    }

285 286 287 288
    inline bool eventFilter(QObject *obj, QEvent * event) {
        if (event->type()==QEvent::Timer && obj==_qApp && !_started) {
            _started = true;
            _qApp->killTimer(_timerId);
Victor Yacovlev's avatar
Victor Yacovlev committed
289
            qDebug() << "Begin initialization";
290
            initialize();
Victor Yacovlev's avatar
Victor Yacovlev committed
291
            qDebug() << "Initialization done";
292 293 294 295
            return true;
        }
        else {
            return QObject::eventFilter(obj, event);
296
        }
297 298
    }

299 300 301
    inline int main() {        
        _timerId = _qApp->startTimer(250);
        int ret = _qApp->exec();
302
        if (ret == 0) {
303 304
            return _qApp->property("returnCode").isValid()
                    ? _qApp->property("returnCode").toInt() : 0;
305 306 307
        }
        else {
            return ret;
308
        }        
309 310
    }   

311
private:
312 313 314 315 316
    QCoreApplication *_qApp;
    int _timerId;
    QSplashScreen * _splashScreen;
    bool _started;

Victor Yacovlev's avatar
Victor Yacovlev committed
317 318
};

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375

bool
setup_custom_vendor_information(QCoreApplication * app)
{
    QString appName;
    QString appVendor;
    QString appVersion;
    QString appLicenseFileName;
    QString appAboutFileName;
    bool result = false;
#ifdef APP_NAME
    appName = QString::fromLatin1(APP_NAME);
    result = true;
#endif
#ifdef APP_NAME_ru
    appName = QString::fromUtf8(APP_NAME_ru);
    result = true;
#endif
#ifdef APP_VENDOR
    appVendor = QString::fromLatin1(APP_VENDOR);
#endif
#ifdef APP_VENDOR_RU
    appVendor = QString::fromUtf8(APP_VENDOR_ru);
#endif
#ifdef APP_VERSION
    appVersion = QString::fromLatin1(APP_VERSION);
#endif
#ifdef APP_LICENSE
    appLicenseFileName = ":/kumir2-launcher/" + QString::fromLatin1(APP_LICENSE);
#endif
#ifdef APP_LICENSE_ru
    appLicenseFileName = ":/kumir2-launcher/" + QString::fromLatin1(APP_LICENSE_ru);
#endif
#ifdef APP_ABOUT
    appAboutFileName = ":/kumir2-launcher/" + QString::fromLatin1(APP_ABOUT);
#endif
#ifdef APP_ABOUT_ru
    appAboutFileName = ":/kumir2-launcher/" + QString::fromLatin1(APP_ABOUT_ru);
#endif
    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;
}

victor's avatar
 
victor committed
376 377
int main(int argc, char **argv)
{ 
Victor Yacovlev's avatar
Victor Yacovlev committed
378
#if QT_VERSION < 0x050000
Victor Yacovlev's avatar
Victor Yacovlev committed
379
    qInstallMsgHandler(GuiMessageOutput);
Victor Yacovlev's avatar
Victor Yacovlev committed
380 381 382 383 384 385 386
#else
    qInstallMessageHandler(GuiMessageOutput);
#endif
    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());
387 388


victor's avatar
 
victor committed
389
    bool gui = true;
390
#ifdef Q_OS_LINUX
victor's avatar
 
victor committed
391 392
    gui = gui && getenv("DISPLAY")!=0;
#endif
393
    Application * app = new Application(argc, argv, gui);
394 395 396 397 398 399 400 401
#ifdef WINDOW_ICON
    if (gui) {
        QApplication* guiApp = qobject_cast<QApplication*>(qApp);
        QString imgPath = ":/kumir2-launcher/" + QString::fromLatin1(WINDOW_ICON);
        QIcon icon(imgPath);
        guiApp->setWindowIcon(icon);
    }
#endif
402 403
    QLocale russian = QLocale("ru_RU");
    QLocale::setDefault(russian);
404 405 406

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

408
    qApp->setApplicationVersion(gitTag.length() > 0 && gitTag!="unknown"
409
                               ? gitTag : gitBranch + "/" + gitHash);
410
    qApp->setProperty("gitTimeStamp", gitTimeStamp);
victor's avatar
 
victor committed
411 412
    QSplashScreen * splashScreen = 0;

413
    const QString sharePath = resolvePath(KUMIR2_RESOURCES_DIR);
victor's avatar
 
victor committed
414

415 416
    const QStringList arguments = QCoreApplication::instance()->arguments();
    bool mustShowHelpAndExit = false;
Victor Yacovlev's avatar
Victor Yacovlev committed
417
    bool mustShowVersionAndExit = false;
418 419 420 421 422 423
    for (int i=1; i<arguments.size(); i++) {
        const QString & argument = arguments[i];
        if (argument=="--help" || argument=="-h" || argument=="/?") {
            mustShowHelpAndExit = true;
            break;
        }
Victor Yacovlev's avatar
Victor Yacovlev committed
424 425 426 427
        else if (argument=="--version") {
            mustShowVersionAndExit = true;
            break;
        }
428 429 430 431 432
        else if (!argument.startsWith("-")) {
            break;
        }
    }

433 434 435
    const bool customAppAndVendorInformation = setup_custom_vendor_information(qobject_cast<QCoreApplication*>(qApp));
    Q_UNUSED(customAppAndVendorInformation);

victor's avatar
 
victor committed
436
#ifdef SPLASHSCREEN
Victor Yacovlev's avatar
Victor Yacovlev committed
437
    if (gui && !mustShowHelpAndExit && !mustShowVersionAndExit) {
438
        QString imgPath = ":/kumir2-launcher/" + QString::fromLatin1(SPLASHSCREEN);
victor's avatar
 
victor committed
439 440 441 442 443 444
        splashScreen = new QSplashScreen();
        QImage img(imgPath);
        QPainter p(&img);
        p.setPen(QColor(Qt::black));
        p.setBrush(QColor(Qt::black));
        QFont f = p.font();
victor's avatar
 
victor committed
445

victor's avatar
 
victor committed
446
        f.setPixelSize(12);
victor's avatar
 
victor committed
447
        p.setFont(f);
448 449 450 451 452 453 454

        QString v;
        if (customAppAndVendorInformation) {
            v = qApp->property("customAppVersion").toString() + " ";
            v += "[based on Kumir ";
        }
        v = qApp->applicationVersion();
455 456
        if (qApp->property("gitHash").isValid()) {
            v += " (GIT "+qApp->property("gitHash").toString()+")";
victor's avatar
 
victor committed
457
        }
458 459 460
        if (customAppAndVendorInformation) {
            v += "]";
        }
victor's avatar
 
victor committed
461
        int tw = QFontMetrics(f).width(v);
462
        int th = QFontMetrics(f).height() * 5;
victor's avatar
 
victor committed
463
        int x = img.width() - tw - 8;
victor's avatar
 
victor committed
464 465 466 467 468 469
        int y = 8;
        p.drawText(x, y, tw, th, 0, v);
        p.end();
        QPixmap px = QPixmap::fromImage(img);
        splashScreen->setPixmap(px);
        splashScreen->show();
470 471
        qApp->processEvents();
        app->setSplashScreen(splashScreen);
victor's avatar
 
victor committed
472
    }
473
#endif    
474
    int ret = app->main();
475 476 477
    ExtensionSystem::PluginManager::destroy();
    delete app;
    return ret;
victor's avatar
 
victor committed
478 479
}