kumir2-launcher.cpp 12.4 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
};

victor's avatar
 
victor committed
319 320
int main(int argc, char **argv)
{ 
Victor Yacovlev's avatar
Victor Yacovlev committed
321
#if QT_VERSION < 0x050000
Victor Yacovlev's avatar
Victor Yacovlev committed
322
    qInstallMsgHandler(GuiMessageOutput);
Victor Yacovlev's avatar
Victor Yacovlev committed
323 324 325 326 327 328 329
#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());
330 331


victor's avatar
 
victor committed
332
    bool gui = true;
333
#ifdef Q_OS_LINUX
victor's avatar
 
victor committed
334 335
    gui = gui && getenv("DISPLAY")!=0;
#endif
336
    Application * app = new Application(argc, argv, gui);
337 338 339 340 341 342 343 344
#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
345 346
    QLocale russian = QLocale("ru_RU");
    QLocale::setDefault(russian);
347 348 349

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

351
    qApp->setApplicationVersion(gitTag.length() > 0 && gitTag!="unknown"
352
                               ? gitTag : gitBranch + "/" + gitHash);
353
    qApp->setProperty("gitTimeStamp", gitTimeStamp);
victor's avatar
 
victor committed
354 355
    QSplashScreen * splashScreen = 0;

356
    const QString sharePath = resolvePath(KUMIR2_RESOURCES_DIR);
victor's avatar
 
victor committed
357

358 359
    const QStringList arguments = QCoreApplication::instance()->arguments();
    bool mustShowHelpAndExit = false;
Victor Yacovlev's avatar
Victor Yacovlev committed
360
    bool mustShowVersionAndExit = false;
361 362 363 364 365 366
    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
367 368 369 370
        else if (argument=="--version") {
            mustShowVersionAndExit = true;
            break;
        }
371 372 373 374 375
        else if (!argument.startsWith("-")) {
            break;
        }
    }

victor's avatar
 
victor committed
376
#ifdef SPLASHSCREEN
Victor Yacovlev's avatar
Victor Yacovlev committed
377
    if (gui && !mustShowHelpAndExit && !mustShowVersionAndExit) {
378
        QString imgPath = ":/kumir2-launcher/" + QString::fromLatin1(SPLASHSCREEN);
victor's avatar
 
victor committed
379 380 381 382 383 384
        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
385

victor's avatar
 
victor committed
386
        f.setPixelSize(12);
victor's avatar
 
victor committed
387
        p.setFont(f);
victor's avatar
 
victor committed
388
        QString v = qApp->applicationVersion();
389 390
        if (qApp->property("gitHash").isValid()) {
            v += " (GIT "+qApp->property("gitHash").toString()+")";
victor's avatar
 
victor committed
391 392 393
        }
        int tw = QFontMetrics(f).width(v);
        int th = QFontMetrics(f).height();
victor's avatar
 
victor committed
394
        int x = img.width() - tw - 8;
victor's avatar
 
victor committed
395 396 397 398 399 400
        int y = 8;
        p.drawText(x, y, tw, th, 0, v);
        p.end();
        QPixmap px = QPixmap::fromImage(img);
        splashScreen->setPixmap(px);
        splashScreen->show();
401 402
        qApp->processEvents();
        app->setSplashScreen(splashScreen);
victor's avatar
 
victor committed
403 404
    }
#endif
405
    int ret = app->main();
406 407 408
    ExtensionSystem::PluginManager::destroy();
    delete app;
    return ret;
victor's avatar
 
victor committed
409 410
}