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

victor's avatar
 
victor committed
94

95
class Application : QObject
Victor Yacovlev's avatar
Victor Yacovlev committed
96 97
{
public:
98
    inline explicit Application(int & argc, char **argv, bool gui)
99 100 101 102 103 104 105 106 107 108 109 110 111 112
        : 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);
    }
113 114

    inline void setSplashScreen(QSplashScreen * s) {
115
        _splashScreen = s;
116 117 118
    }

    inline void initialize() {
119
        const QStringList arguments = _qApp->arguments();
Victor Yacovlev's avatar
Victor Yacovlev committed
120
        qDebug() << "Arguments: " << arguments;
121
        bool mustShowHelpAndExit = false;
Victor Yacovlev's avatar
Victor Yacovlev committed
122
        bool mustShowVersionAndExit = false;
123 124 125 126 127 128
        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
129 130 131 132
            else if (argument=="--version") {
                mustShowVersionAndExit = true;
                break;
            }
133 134 135 136 137
            else if (!argument.startsWith("-")) {
                break;
            }
        }

138
        bool gui = true;
139
#ifdef Q_OS_LINUX
140 141
        gui = gui && getenv("DISPLAY")!=0;
#endif
142
        const QString sharePath = resolvePath(KUMIR2_RESOURCES_DIR);
143 144 145 146
        QDir translationsDir(sharePath+"/translations");
        QStringList ts_files = translationsDir.entryList(QStringList() << "*_"+getLanguage()+".qm");
        foreach (QString tsname, ts_files) {
            tsname = tsname.mid(0, tsname.length()-3);
147
            QTranslator * tr = new QTranslator(_qApp);
148
            if (tr->load(tsname, sharePath+"/translations")) {
149
                _qApp->installTranslator(tr);
150 151
            }
        }
Victor Yacovlev's avatar
Victor Yacovlev committed
152
        qDebug() << "Loaded translator files";
153
        _qApp->setProperty("sharePath", sharePath);
154 155

        QSettings::setDefaultFormat(QSettings::IniFormat);
156 157
        _qApp->addLibraryPath(resolvePath(KUMIR2_LIBS_DIR));
        _qApp->addLibraryPath(resolvePath(KUMIR2_PLUGINS_DIR));
Victor Yacovlev's avatar
Victor Yacovlev committed
158
        ExtensionSystem::PluginManager * manager = ExtensionSystem::PluginManager::instance();
159
        manager->setPluginPath(resolvePath(KUMIR2_PLUGINS_DIR));
160
        manager->setSharePath(sharePath);
161
        QString error;
Victor Yacovlev's avatar
Victor Yacovlev committed
162
        qDebug() << "Initialized plugin manager";
163
    #ifdef CONFIGURATION_TEMPLATE
164
        const QByteArray defaultTemplate = CONFIGURATION_TEMPLATE;
165 166 167
    #else
    #error No default configuration passed to GCC
    #endif
168
        QByteArray templ = defaultTemplate;
Victor Yacovlev's avatar
Victor Yacovlev committed
169
        for (int i=1; i<arguments.size(); i++) {
170
            QByteArray arg = arguments[i].toLatin1();
171 172 173 174
            if (arg.startsWith("[") && arg.endsWith("]")) {
                templ = arg.mid(1, arg.length()-2);
            }
        }
Victor Yacovlev's avatar
Victor Yacovlev committed
175
        qDebug() << "Loading plugins by template: " << templ;
176 177
        error = manager->loadPluginsByTemplate(templ);
        if (!gui && manager->isGuiRequired()) {
178 179
            if (_splashScreen)
                _splashScreen->finish(0);
180
            showErrorMessage("Requires X11 session to run this configuration");
181
            _qApp->exit(1);
182 183
        }

Victor Yacovlev's avatar
Victor Yacovlev committed
184 185 186
//        qInstallMsgHandler(manager->isGuiRequired()
//                           ? GuiMessageOutput
//                           : ConsoleMessageOutput);
187 188

        if (!error.isEmpty()) {
189 190
            if (_splashScreen)
                _splashScreen->finish(0);
191
            showErrorMessage(error);
192
            _qApp->exit(1);
193
        }
194

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

197
        if (mustShowHelpAndExit) {
198 199
            if (_splashScreen)
                _splashScreen->finish(0);
200 201 202 203 204 205 206
            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
207
            _qApp->exit(0);
208 209
            return;
        }
Victor Yacovlev's avatar
Victor Yacovlev committed
210 211

        if (mustShowVersionAndExit) {
212 213
            fprintf(stderr, "%s\n", qPrintable(_qApp->applicationVersion()));
            _qApp->exit(0);
Victor Yacovlev's avatar
Victor Yacovlev committed
214
            return;
Victor Yacovlev's avatar
Victor Yacovlev committed
215 216
        }        
        qDebug() << "Begin plugins initialization";
217 218
        error = manager->initializePlugins();
        if (!error.isEmpty()) {
219 220
            if (_splashScreen)
                _splashScreen->finish(0);
221
            showErrorMessage(error);
222 223
            _qApp->exit(_qApp->property("returnCode").isValid()
                    ? _qApp->property("returnCode").toInt() : 1);
224 225 226
        }
        // GUI requirement may be changed as result of plugins initialization,
        // so check it again
Victor Yacovlev's avatar
Victor Yacovlev committed
227
        qDebug() << "Plugins initialization done";
228 229
        if (!gui && manager->isGuiRequired()) {
            showErrorMessage("Requires X11 session to run this configuration");
230 231
            _qApp->exit(_qApp->property("returnCode").isValid()
                    ? _qApp->property("returnCode").toInt() : 1);
232
        }
233 234
        if (_splashScreen)
            _splashScreen->finish(0);
Victor Yacovlev's avatar
Victor Yacovlev committed
235
        qDebug() << "Starting entry point plugin";
236 237
        error = manager->start();
        if (!error.isEmpty()) {
238 239
            if (_splashScreen)
                _splashScreen->finish(0);
240
            showErrorMessage(error);
241 242
            _qApp->exit(_qApp->property("returnCode").isValid()
                    ? _qApp->property("returnCode").toInt() : 1);
243 244
        }
        if (!manager->isGuiRequired()) {
245
            _qApp->quit();
246 247 248
        }
    }

249 250 251 252
    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
253
            qDebug() << "Begin initialization";
254
            initialize();
Victor Yacovlev's avatar
Victor Yacovlev committed
255
            qDebug() << "Initialization done";
256 257 258 259
            return true;
        }
        else {
            return QObject::eventFilter(obj, event);
260
        }
261 262
    }

263 264 265
    inline int main() {        
        _timerId = _qApp->startTimer(250);
        int ret = _qApp->exec();
266
        if (ret == 0) {
267 268
            return _qApp->property("returnCode").isValid()
                    ? _qApp->property("returnCode").toInt() : 0;
269 270 271
        }
        else {
            return ret;
272
        }        
273 274
    }   

275
private:
276 277 278 279 280
    QCoreApplication *_qApp;
    int _timerId;
    QSplashScreen * _splashScreen;
    bool _started;

Victor Yacovlev's avatar
Victor Yacovlev committed
281 282
};

victor's avatar
 
victor committed
283 284
int main(int argc, char **argv)
{ 
Victor Yacovlev's avatar
Victor Yacovlev committed
285
#if QT_VERSION < 0x050000
Victor Yacovlev's avatar
Victor Yacovlev committed
286
    qInstallMsgHandler(GuiMessageOutput);
Victor Yacovlev's avatar
Victor Yacovlev committed
287 288 289 290 291 292 293
#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());
294 295


victor's avatar
 
victor committed
296
    bool gui = true;
297
#ifdef Q_OS_LINUX
victor's avatar
 
victor committed
298 299
    gui = gui && getenv("DISPLAY")!=0;
#endif
300
    Application * app = new Application(argc, argv, gui);
301 302 303 304 305 306 307 308
#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
309 310
    QLocale russian = QLocale("ru_RU");
    QLocale::setDefault(russian);
311 312 313

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

315
    qApp->setApplicationVersion(gitTag.length() > 0 && gitTag!="unknown"
316
                               ? gitTag : gitBranch + "/" + gitHash);
317
    qApp->setProperty("gitTimeStamp", gitTimeStamp);
victor's avatar
 
victor committed
318 319
    QSplashScreen * splashScreen = 0;

320
    const QString sharePath = resolvePath(KUMIR2_RESOURCES_DIR);
victor's avatar
 
victor committed
321

322 323
    const QStringList arguments = QCoreApplication::instance()->arguments();
    bool mustShowHelpAndExit = false;
Victor Yacovlev's avatar
Victor Yacovlev committed
324
    bool mustShowVersionAndExit = false;
325 326 327 328 329 330
    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
331 332 333 334
        else if (argument=="--version") {
            mustShowVersionAndExit = true;
            break;
        }
335 336 337 338 339
        else if (!argument.startsWith("-")) {
            break;
        }
    }

victor's avatar
 
victor committed
340
#ifdef SPLASHSCREEN
Victor Yacovlev's avatar
Victor Yacovlev committed
341
    if (gui && !mustShowHelpAndExit && !mustShowVersionAndExit) {
342
        QString imgPath = ":/kumir2-launcher/" + QString::fromLatin1(SPLASHSCREEN);
victor's avatar
 
victor committed
343 344 345 346 347 348
        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
349

victor's avatar
 
victor committed
350
        f.setPixelSize(12);
victor's avatar
 
victor committed
351
        p.setFont(f);
victor's avatar
 
victor committed
352
        QString v = qApp->applicationVersion();
353 354
        if (qApp->property("gitHash").isValid()) {
            v += " (GIT "+qApp->property("gitHash").toString()+")";
victor's avatar
 
victor committed
355 356 357
        }
        int tw = QFontMetrics(f).width(v);
        int th = QFontMetrics(f).height();
victor's avatar
 
victor committed
358
        int x = img.width() - tw - 8;
victor's avatar
 
victor committed
359 360 361 362 363 364
        int y = 8;
        p.drawText(x, y, tw, th, 0, v);
        p.end();
        QPixmap px = QPixmap::fromImage(img);
        splashScreen->setPixmap(px);
        splashScreen->show();
365 366
        qApp->processEvents();
        app->setSplashScreen(splashScreen);
victor's avatar
 
victor committed
367 368
    }
#endif
369
    int ret = app->main();
370 371 372
    ExtensionSystem::PluginManager::destroy();
    delete app;
    return ret;
victor's avatar
 
victor committed
373 374
}