You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
432 lines
12 KiB
432 lines
12 KiB
/**************************************************************************** |
|
* |
|
* (c) 2009-2020 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. |
|
* |
|
****************************************************************************/ |
|
|
|
#include <QtGlobal> |
|
#include <QApplication> |
|
#include <QIcon> |
|
#include <QSslSocket> |
|
#include <QMessageBox> |
|
#include <QProcessEnvironment> |
|
#include <QHostAddress> |
|
#include <QUdpSocket> |
|
#include <QtPlugin> |
|
#include <QStringListModel> |
|
|
|
#include "QGC.h" |
|
#include "QGCApplication.h" |
|
#include "AppMessages.h" |
|
|
|
#ifndef NO_SERIAL_LINK |
|
#include "SerialLink.h" |
|
#endif |
|
|
|
#ifndef __mobile__ |
|
#include "QGCSerialPortInfo.h" |
|
#include "RunGuard.h" |
|
#ifndef NO_SERIAL_LINK |
|
#include <QSerialPort> |
|
#endif |
|
#endif |
|
|
|
#ifdef UNITTEST_BUILD |
|
#include "UnitTest.h" |
|
#endif |
|
|
|
#ifdef QT_DEBUG |
|
#include "CmdLineOptParser.h" |
|
#ifdef Q_OS_WIN |
|
#include <crtdbg.h> |
|
#endif |
|
#endif |
|
|
|
#ifdef QGC_ENABLE_BLUETOOTH |
|
#include <QtBluetooth/QBluetoothSocket> |
|
#endif |
|
|
|
#include <iostream> |
|
#include "QGCMapEngine.h" |
|
|
|
/* SDL does ugly things to main() */ |
|
#ifdef main |
|
#undef main |
|
#endif |
|
|
|
#ifndef __mobile__ |
|
#ifndef NO_SERIAL_LINK |
|
Q_DECLARE_METATYPE(QGCSerialPortInfo) |
|
#endif |
|
#endif |
|
|
|
#ifdef Q_OS_WIN |
|
|
|
#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 |
|
} |
|
|
|
#endif |
|
|
|
#if defined(__android__) |
|
#include <jni.h> |
|
#include "JoystickAndroid.h" |
|
#if defined(QGC_ENABLE_PAIRING) |
|
#include "PairingManager.h" |
|
#endif |
|
#if !defined(NO_SERIAL_LINK) |
|
#include "qserialport.h" |
|
#endif |
|
|
|
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", "()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(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
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(); |
|
|
|
#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) |
|
QSerialPort::setNativeMethods(); |
|
#endif |
|
|
|
JoystickAndroid::setNativeMethods(); |
|
|
|
#if defined(QGC_ENABLE_PAIRING) |
|
PairingManager::setNativeMethods(); |
|
#endif |
|
|
|
return JNI_VERSION_1_6; |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
#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 |
|
|
|
// To shut down QGC on Ctrl+C on Linux |
|
#ifdef Q_OS_LINUX |
|
#include <csignal> |
|
|
|
void sigHandler(int s) |
|
{ |
|
std::signal(s, SIG_DFL); |
|
QApplication::instance()->quit(); |
|
} |
|
|
|
#endif /* Q_OS_LINUX */ |
|
|
|
//----------------------------------------------------------------------------- |
|
/** |
|
* @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 |
|
*/ |
|
|
|
int main(int argc, char *argv[]) |
|
{ |
|
#ifndef __mobile__ |
|
// We make the runguard key different for custom and non custom |
|
// builds, so they can be executed together in the same device. |
|
// Stable and Daily have same QGC_APPLICATION_NAME so they would |
|
// not be able to run at the same time |
|
QString runguardString(QGC_APPLICATION_NAME); |
|
runguardString.append("RunGuardKey"); |
|
|
|
RunGuard guard(runguardString); |
|
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 %1 is already running. Please close the other instance and try again.").arg(QGC_APPLICATION_NAME) |
|
); |
|
return -1; |
|
} |
|
#endif |
|
|
|
//-- Record boot time |
|
QGC::initTimer(); |
|
|
|
#ifdef Q_OS_UNIX |
|
//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 |
|
AppMessages::installHandler(); |
|
|
|
#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 |
|
#endif |
|
|
|
#ifdef Q_OS_WIN |
|
// 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; |
|
} |
|
} |
|
#endif |
|
|
|
#ifdef Q_OS_LINUX |
|
std::signal(SIGINT, sigHandler); |
|
std::signal(SIGTERM, sigHandler); |
|
#endif /* Q_OS_LINUX */ |
|
|
|
// 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. |
|
#ifndef NO_SERIAL_LINK |
|
qRegisterMetaType<QSerialPort::SerialPortError>(); |
|
#endif |
|
#ifdef QGC_ENABLE_BLUETOOTH |
|
qRegisterMetaType<QBluetoothSocket::SocketError>(); |
|
qRegisterMetaType<QBluetoothServiceInfo>(); |
|
#endif |
|
qRegisterMetaType<QAbstractSocket::SocketError>(); |
|
#ifndef __mobile__ |
|
#ifndef NO_SERIAL_LINK |
|
qRegisterMetaType<QGCSerialPortInfo>(); |
|
#endif |
|
#endif |
|
|
|
qRegisterMetaType<Vehicle::MavCmdResultFailureCode_t>("Vehicle::MavCmdResultFailureCode_t"); |
|
|
|
// 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) |
|
|
|
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. |
|
|
|
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 }, |
|
{ "--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); |
|
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 |
|
#endif // QT_DEBUG |
|
|
|
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); |
|
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(); |
|
//-- Initialize Cache System |
|
getQGCMapEngine()->init(); |
|
|
|
int exitCode = 0; |
|
|
|
#ifdef UNITTEST_BUILD |
|
if (runUnitTests) { |
|
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; |
|
} |
|
} |
|
} else |
|
#endif |
|
{ |
|
|
|
#ifdef __android__ |
|
checkAndroidWritePermission(); |
|
#endif |
|
if (!app->_initForNormalAppBoot()) { |
|
return -1; |
|
} |
|
exitCode = app->exec(); |
|
} |
|
|
|
app->_shutdown(); |
|
delete app; |
|
//-- Shutdown Cache System |
|
destroyMapEngine(); |
|
|
|
qDebug() << "After app delete"; |
|
|
|
return exitCode; |
|
}
|
|
|