Skip to content
main.cc 11.3 KiB
Newer Older
/****************************************************************************
 *
 *   (c) 2009-2019 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/
pixhawk's avatar
pixhawk committed


/**
 * @file
 *   @brief Main executable
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QtGlobal>
#include <QApplication>
#include <QIcon>
Don Gagne's avatar
Don Gagne committed
#include <QSslSocket>
#include <QProcessEnvironment>
#include <QHostAddress>
#include <QUdpSocket>
Don Gagne's avatar
Don Gagne committed
#include <QtPlugin>
Don Gagne's avatar
Don Gagne committed
#include "QGCApplication.h"
#include "AppMessages.h"
#ifndef __mobile__
    #include "QGCSerialPortInfo.h"
    #include "RunGuard.h"
#ifdef UNITTEST_BUILD
    #include "UnitTest.h"
#endif

#ifdef QT_DEBUG
    #include "CmdLineOptParser.h"
    #ifdef Q_OS_WIN
        #include <crtdbg.h>
    #endif
dogmaphobic's avatar
dogmaphobic committed
#ifdef QGC_ENABLE_BLUETOOTH
dogmaphobic's avatar
dogmaphobic committed
#include <QtBluetooth/QBluetoothSocket>
#endif

Don Gagne's avatar
Don Gagne committed
#include <iostream>
dogmaphobic's avatar
dogmaphobic committed
#include "QGCMapEngine.h"
unknown's avatar
unknown committed

/* SDL does ugly things to main() */
#undef main
#ifndef __mobile__
    Q_DECLARE_METATYPE(QGCSerialPortInfo)

#include <windows.h>

/// @brief CRT Report Hook installed using _CrtSetReportHook. We install this hook when
/// we don't want asserts to pop a dialog on windows.
int WindowsCrtReportHook(int reportType, char* message, int* returnValue)
{
    Q_UNUSED(reportType);
    std::cerr << message << std::endl;  // Output message to stderr
    *returnValue = 0;                   // Don't break into debugger
    return true;                        // We handled this fully ourselves
}

#if defined(__android__)
Don Gagne's avatar
Don Gagne committed
#include <jni.h>
#include "JoystickAndroid.h"
#if !defined(NO_SERIAL_LINK)
Don Gagne's avatar
Don Gagne committed
#include "qserialport.h"
static jobject _class_loader = nullptr;
static jobject _context = nullptr;

//-----------------------------------------------------------------------------
extern "C" {
    void gst_amc_jni_set_java_vm(JavaVM *java_vm);

    jobject gst_android_get_application_class_loader(void)
    {
        return _class_loader;
    }
}

//-----------------------------------------------------------------------------
static void
gst_android_init(JNIEnv* env, jobject context)
{
    jobject class_loader = nullptr;

    jclass context_cls = env->GetObjectClass(context);
    if (!context_cls) {
        return;
    }

    jmethodID get_class_loader_id = env->GetMethodID(context_cls, "getClassLoader", "()Ljava/lang/ClassLoader;");
    if (env->ExceptionCheck()) {
        env->ExceptionDescribe();
        env->ExceptionClear();
        return;
    }

    class_loader = env->CallObjectMethod(context, get_class_loader_id);
    if (env->ExceptionCheck()) {
        env->ExceptionDescribe();
        env->ExceptionClear();
        return;
    }

    _context = env->NewGlobalRef(context);
    _class_loader = env->NewGlobalRef (class_loader);
}

//-----------------------------------------------------------------------------
static const char kJniClassName[] {"org/mavlink/qgroundcontrol/QGCActivity"};

void setNativeMethods(void)
{
    JNINativeMethod javaMethods[] {
        {"nativeInit", "(Landroid/content/Context;)V", reinterpret_cast<void *>(gst_android_init)}
    };

    QAndroidJniEnvironment jniEnv;
    if (jniEnv->ExceptionCheck()) {
        jniEnv->ExceptionDescribe();
        jniEnv->ExceptionClear();
    }

    jclass objectClass = jniEnv->FindClass(kJniClassName);
    if(!objectClass) {
        qWarning() << "Couldn't find class:" << kJniClassName;
        return;
    }

    jint val = jniEnv->RegisterNatives(objectClass, javaMethods, sizeof(javaMethods) / sizeof(javaMethods[0]));

    if (val < 0) {
        qWarning() << "Error registering methods: " << val;
    } else {
        qDebug() << "Main Native Functions Registered";
    }

    if (jniEnv->ExceptionCheck()) {
        jniEnv->ExceptionDescribe();
        jniEnv->ExceptionClear();
    }
}

//-----------------------------------------------------------------------------
Don Gagne's avatar
Don Gagne committed
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    Q_UNUSED(reserved);

    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    setNativeMethods();

    QAndroidJniObject resultL = QAndroidJniObject::callStaticObjectMethod(
        kJniClassName,
        "jniOnLoad",
        "();");

#if defined(QGC_GST_STREAMING)
    // Tell the androidmedia plugin about the Java VM
    gst_amc_jni_set_java_vm(vm);
#endif

 #if !defined(NO_SERIAL_LINK)
Don Gagne's avatar
Don Gagne committed
    QSerialPort::setNativeMethods();
 #endif
    JoystickAndroid::setNativeMethods();
Don Gagne's avatar
Don Gagne committed

    return JNI_VERSION_1_6;
}
#endif

//-----------------------------------------------------------------------------
Don Gagne's avatar
 
Don Gagne committed
#ifdef __android__
#include <QtAndroid>
bool checkAndroidWritePermission() {
    QtAndroid::PermissionResult r = QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE");
    if(r == QtAndroid::PermissionResult::Denied) {
        QtAndroid::requestPermissionsSync( QStringList() << "android.permission.WRITE_EXTERNAL_STORAGE" );
        r = QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE");
        if(r == QtAndroid::PermissionResult::Denied) {
             return false;
        }
   }
   return true;
}
#endif

//-----------------------------------------------------------------------------
pixhawk's avatar
pixhawk committed
/**
 * @brief Starts the application
 *
 * @param argc Number of commandline arguments
 * @param argv Commandline arguments
 * @return exit code, 0 for normal exit and !=0 for error cases
 */
pixhawk's avatar
pixhawk committed
int main(int argc, char *argv[])
{
#ifndef __mobile__
    RunGuard guard("QGroundControlRunGuardKey");
    if (!guard.tryToRun()) {
        // QApplication is necessary to use QMessageBox
        QApplication errorApp(argc, argv);
        QMessageBox::critical(nullptr, QObject::tr("Error"),
            QObject::tr("A second instance of QGroundControl is already running. Please close the other instance and try again.")
        );
        return -1;
Daniel Agar's avatar
Daniel Agar committed
    //Force writing to the console on UNIX/BSD devices
    if (!qEnvironmentVariableIsSet("QT_LOGGING_TO_CONSOLE"))
        qputenv("QT_LOGGING_TO_CONSOLE", "1");
#endif

    // install the message handler
Lorenz Meier's avatar
Lorenz Meier committed
#ifdef Q_OS_MAC
#ifndef __ios__
    // Prevent Apple's app nap from screwing us over
    // tip: the domain can be cross-checked on the command line with <defaults domains>
    QProcess::execute("defaults write org.qgroundcontrol.qgroundcontrol NSAppSleepDisabled -bool YES");
#endif
Lorenz Meier's avatar
Lorenz Meier committed
#endif
    // Set our own OpenGL buglist
    qputenv("QT_OPENGL_BUGLIST", ":/opengl/resources/opengl/buglist.json");

    // Allow for command line override of renderer
    for (int i = 0; i < argc; i++) {
        const QString arg(argv[i]);
        if (arg == QStringLiteral("-angle")) {
            QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
            break;
        } else if (arg == QStringLiteral("-swrast")) {
            QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
            break;
        }

    // The following calls to qRegisterMetaType are done to silence debug output which warns
    // that we use these types in signals, and without calling qRegisterMetaType we can't queue
    // these signals. In general we don't queue these signals, but we do what the warning says
    // anyway to silence the debug output.
Gus Grubba's avatar
Gus Grubba committed
#ifndef NO_SERIAL_LINK
    qRegisterMetaType<QSerialPort::SerialPortError>();
dogmaphobic's avatar
dogmaphobic committed
#endif
dogmaphobic's avatar
dogmaphobic committed
#ifdef QGC_ENABLE_BLUETOOTH
dogmaphobic's avatar
dogmaphobic committed
    qRegisterMetaType<QBluetoothSocket::SocketError>();
    qRegisterMetaType<QBluetoothServiceInfo>();
dogmaphobic's avatar
dogmaphobic committed
#endif
    qRegisterMetaType<QAbstractSocket::SocketError>();
#ifndef __mobile__
    qRegisterMetaType<QGCSerialPortInfo>();
dogmaphobic's avatar
dogmaphobic committed

    // We statically link our own QtLocation plugin

#ifdef Q_OS_WIN
    // In Windows, the compiler doesn't see the use of the class created by Q_IMPORT_PLUGIN
#pragma warning( disable : 4930 4101 )
#endif

    Q_IMPORT_PLUGIN(QGeoServiceProviderFactoryQGC)
Don Gagne's avatar
Don Gagne committed
    bool runUnitTests = false;          // Run unit tests
#ifdef QT_DEBUG
    // We parse a small set of command line options here prior to QGCApplication in order to handle the ones
    // which need to be handled before a QApplication object is started.
Don Gagne's avatar
Don Gagne committed
    bool stressUnitTests = false;       // Stress test unit tests
    bool quietWindowsAsserts = false;   // Don't let asserts pop dialog boxes
    QString unitTestOptions;
    CmdLineOpt_t rgCmdLineOptions[] = {
        { "--unittest",             &runUnitTests,          &unitTestOptions },
Don Gagne's avatar
Don Gagne committed
        { "--unittest-stress",      &stressUnitTests,       &unitTestOptions },
        { "--no-windows-assert-ui", &quietWindowsAsserts,   nullptr },
        // Add additional command line option flags here
    };
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
Don Gagne's avatar
Don Gagne committed
    if (stressUnitTests) {
        runUnitTests = true;
    }
    if (quietWindowsAsserts) {
#ifdef Q_OS_WIN
        _CrtSetReportHook(WindowsCrtReportHook);
#endif
    }

#ifdef Q_OS_WIN
    if (runUnitTests) {
        // Don't pop up Windows Error Reporting dialog when app crashes. This prevents TeamCity from
        // hanging.
        DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
        SetErrorMode(dwMode | SEM_NOGPFAULTERRORBOX);
    }
#endif // QT_DEBUG
    QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
Don Gagne's avatar
Don Gagne committed
    QGCApplication* app = new QGCApplication(argc, argv, runUnitTests);
    Q_CHECK_PTR(app);
    if(app->isErrorState()) {
        app->exec();
        return -1;
    }
#ifdef Q_OS_LINUX
    QApplication::setWindowIcon(QIcon(":/res/resources/icons/qgroundcontrol.ico"));
#endif /* Q_OS_LINUX */

    // There appears to be a threading issue in qRegisterMetaType which can cause it to throw a qWarning
    // about duplicate type converters. This is caused by a race condition in the Qt code. Still working
    // with them on tracking down the bug. For now we register the type which is giving us problems here
    // while we only have the main thread. That should prevent it from hitting the race condition later
    // on in the code.
    qRegisterMetaType<QList<QPair<QByteArray,QByteArray> > >();
    app->_initCommon();
dogmaphobic's avatar
dogmaphobic committed
    //-- Initialize Cache System
    getQGCMapEngine()->init();
Don Gagne's avatar
Don Gagne committed
    int exitCode = 0;
#ifdef UNITTEST_BUILD
    if (runUnitTests) {
Don Gagne's avatar
Don Gagne committed
        for (int i=0; i < (stressUnitTests ? 20 : 1); i++) {
            if (!app->_initForUnitTests()) {
                return -1;
            }

            // Run the test
            int failures = UnitTest::run(unitTestOptions);
            if (failures == 0) {
                qDebug() << "ALL TESTS PASSED";
                exitCode = 0;
            } else {
                qDebug() << failures << " TESTS FAILED!";
                exitCode = -failures;
                break;
            }
Don Gagne's avatar
 
Don Gagne committed

#ifdef __android__
        checkAndroidWritePermission();
#endif
        if (!app->_initForNormalAppBoot()) {
            return -1;
        }
Don Gagne's avatar
Don Gagne committed
        exitCode = app->exec();
Don Gagne's avatar
Don Gagne committed
    app->_shutdown();
Don Gagne's avatar
Don Gagne committed
    delete app;
dogmaphobic's avatar
dogmaphobic committed
    //-- Shutdown Cache System
    destroyMapEngine();
Don Gagne's avatar
Don Gagne committed
    qDebug() << "After app delete";
Don Gagne's avatar
Don Gagne committed
    return exitCode;
pixhawk's avatar
pixhawk committed
}