Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/QGCApplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@

#pragma once

#include <QtCore/QLoggingCategory>
#include <QtCore/QElapsedTimer>
#include <QtCore/QMap>
#include <QtCore/QSet>
#include <QtCore/QTimer>
#include <QtCore/QTranslator>

#include <QtWidgets/QApplication>

class QQmlApplicationEngine;
Expand All @@ -38,6 +38,8 @@ class QMetaObject;

#define qgcApp() qApp

Q_DECLARE_LOGGING_CATEGORY(QGCApplicationLog)

/// The main application and management class.
/// Needs QApplication base to support QtCharts module.
/// TODO: Use QtGraphs to convert to QGuiApplication
Expand Down Expand Up @@ -129,15 +131,16 @@ private slots:
bool compressEvent(QEvent *event, QObject *receiver, QPostEventList *postedEvents) final;

void _initVideo();

/// Initialize the application for normal application boot. Or in other words we are not going to run unit tests.
void _initForNormalAppBoot();

QObject *_rootQmlObject();
void _checkForNewVersion();

bool _runningUnitTests = false;
bool _simpleBootTest = false;
bool _simpleBootTest = false;

static constexpr int _missingParamsDelayedDisplayTimerTimeout = 1000; ///< Timeout to wait for next missing fact to come in before display
QTimer _missingParamsDelayedDisplayTimer; ///< Timer use to delay missing fact display
QList<QPair<int,QString>> _missingParams; ///< List of missing parameter component id:name
Expand Down
2 changes: 2 additions & 0 deletions src/RunGuard.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,6 @@ class RunGuard
const QString _key;
const QString _lockFilePath;
QLockFile _lockFile;

Q_DISABLE_COPY(RunGuard)
};
6 changes: 4 additions & 2 deletions src/Utilities/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ target_sources(${CMAKE_PROJECT_NAME}
StateMachine.h
)

if(LINUX)
if(NOT ANDROID AND NOT IOS)
target_sources(${CMAKE_PROJECT_NAME}
PRIVATE
SignalHandler.cc
SignalHandler.h
)
elseif(IOS)
endif()

if(IOS)
target_sources(${CMAKE_PROJECT_NAME}
PRIVATE
MobileScreenMgr.mm
Expand Down
160 changes: 148 additions & 12 deletions src/Utilities/Platform.cc
Original file line number Diff line number Diff line change
@@ -1,22 +1,158 @@
/****************************************************************************
*
* (c) 2009-2024 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 "Platform.h"

#ifdef Q_OS_MAC
#include <CoreFoundation/CoreFoundation.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QProcessEnvironment>

#if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID)
#include "SignalHandler.h"
#endif

void Platform::disableAppNapViaInfoDict()
#if defined(Q_OS_MACOS)
#include <CoreFoundation/CoreFoundation.h>
#elif defined(Q_OS_WIN)
#include <qt_windows.h>
#include <iostream>
#include <iterator> // std::size
#include <cwchar> // swprintf
#if defined(_MSC_VER)
#include <crtdbg.h>
#include <stdlib.h>
#include <cstdio> // _snwprintf_s
#endif
#endif

namespace {

#if defined(Q_OS_MACOS)
void disableAppNapViaInfoDict()
{
CFBundleRef bundle = CFBundleGetMainBundle();
if (!bundle) return;
if (!bundle) {
return;
}
CFMutableDictionaryRef infoDict = const_cast<CFMutableDictionaryRef>(CFBundleGetInfoDictionary(bundle));
if (infoDict) {
CFDictionarySetValue(infoDict, CFSTR("NSAppSleepDisabled"), kCFBooleanTrue);
}
}
#endif // Q_OS_MACOS

// CFBundleGetInfoDictionary returns the dictionary the OS already
// parsed from Info.plist. Cast it to mutable so we can tweak it.
CFMutableDictionaryRef infoDict = (CFMutableDictionaryRef) CFBundleGetInfoDictionary(bundle);
#if defined(Q_OS_WIN)

// Inject the key → true
if (infoDict) {
CFDictionarySetValue(infoDict,
CFSTR("NSAppSleepDisabled"),
kCFBooleanTrue);
#if defined(_MSC_VER)
int __cdecl WindowsCrtReportHook(int reportType, char* message, int* returnValue)
{
if (message) {
std::cerr << message << std::endl;
}
if (reportType == _CRT_ASSERT) {
if (returnValue) {
*returnValue = 0;
}
return 1; // handled
}
return 0; // let CRT continue
}

void __cdecl WindowsPurecallHandler()
{
(void) OutputDebugStringW(L"QGC: _purecall\n");
}

void WindowsInvalidParameterHandler([[maybe_unused]] const wchar_t* expression,
[[maybe_unused]] const wchar_t* function,
[[maybe_unused]] const wchar_t* file,
[[maybe_unused]] unsigned int line,
[[maybe_unused]] uintptr_t pReserved)
{

}
#endif // _MSC_VER

LPTOP_LEVEL_EXCEPTION_FILTER g_prevUef = nullptr;

LONG WINAPI WindowsUnhandledExceptionFilter(EXCEPTION_POINTERS* ep)
{
const DWORD code = (ep && ep->ExceptionRecord) ? ep->ExceptionRecord->ExceptionCode : 0;
wchar_t buf[128] = {};
#if defined(_MSC_VER)
(void) _snwprintf_s(buf, _TRUNCATE, L"QGC: unhandled SEH 0x%08lX\n", static_cast<unsigned long>(code));
Copy link
Preview

Copilot AI Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The buffer size (128) is hardcoded. Consider using a named constant or sizeof(buf) to make the code more maintainable and less error-prone.

Suggested change
(void) _snwprintf_s(buf, _TRUNCATE, L"QGC: unhandled SEH 0x%08lX\n", static_cast<unsigned long>(code));
(void) _snwprintf_s(buf, sizeof(buf)/sizeof(buf[0]), L"QGC: unhandled SEH 0x%08lX\n", static_cast<unsigned long>(code));

Copilot uses AI. Check for mistakes.

#else
(void) swprintf(buf, static_cast<int>(std::size(buf)), L"QGC: unhandled SEH 0x%08lX\n", static_cast<unsigned long>(code));
#endif
(void) OutputDebugStringW(buf);

const HANDLE h = GetStdHandle(STD_ERROR_HANDLE);
if (h && (h != INVALID_HANDLE_VALUE)) {
DWORD ignored = 0;
const char narrow[] = "QGC: unhandled SEH\n";
(void) WriteFile(h, narrow, (DWORD)sizeof(narrow) - 1, &ignored, nullptr);
}

return EXCEPTION_EXECUTE_HANDLER;
}

void setWindowsErrorModes(bool quietWindowsAsserts)
{
(void) SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
g_prevUef = SetUnhandledExceptionFilter(WindowsUnhandledExceptionFilter);

#if defined(_MSC_VER)
(void) _set_invalid_parameter_handler(WindowsInvalidParameterHandler);
Copy link
Preview

Copilot AI Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WindowsInvalidParameterHandler function is empty, which may not provide meaningful error handling. Consider adding logging or appropriate error handling logic.

Copilot uses AI. Check for mistakes.

(void) _set_purecall_handler(WindowsPurecallHandler);

if (quietWindowsAsserts) {
(void) _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
(void) _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
(void) _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
(void) _CrtSetReportHook2(_CRT_RPTHOOK_INSTALL, WindowsCrtReportHook);
(void) _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
(void) _set_error_mode(_OUT_TO_STDERR);
}
#else
Q_UNUSED(quietWindowsAsserts);
#endif
}
#endif // Q_OS_WIN

} // namespace

void Platform::setupPreApp(bool quietWindowsAsserts)
{
#ifdef Q_OS_UNIX
if (!qEnvironmentVariableIsSet("QT_ASSUME_STDERR_HAS_CONSOLE")) {
(void) qputenv("QT_ASSUME_STDERR_HAS_CONSOLE", "1");
}
if (!qEnvironmentVariableIsSet("QT_FORCE_STDERR_LOGGING")) {
(void) qputenv("QT_FORCE_STDERR_LOGGING", "1");
}
#endif

#ifdef Q_OS_WIN
if (!qEnvironmentVariableIsSet("QT_WIN_DEBUG_CONSOLE")) {
(void) qputenv("QT_WIN_DEBUG_CONSOLE", "attach");
}
setWindowsErrorModes(quietWindowsAsserts);
#endif

Copy link
Preview

Copilot AI Aug 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The console control handler is installed unconditionally, but it might be more appropriate to only install it when needed (e.g., when running unit tests or in debug mode). Consider making this conditional based on the application's requirements.

Suggested change
#ifdef QT_DEBUG
(void) SetConsoleCtrlHandler((PHANDLER_ROUTINE) WindowsConsoleCtrlHandler, TRUE);
#endif

Copilot uses AI. Check for mistakes.

#ifdef Q_OS_MACOS
disableAppNapViaInfoDict();
#endif
}

void Platform::setupPostApp()
{
#if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID)
SignalHandler* signalHandler = new SignalHandler(QCoreApplication::instance());
(void) signalHandler->setupSignalHandlers();
#endif
}
25 changes: 17 additions & 8 deletions src/Utilities/Platform.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
#pragma once
/****************************************************************************
*
* (c) 2009-2024 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 <QtCore/QtSystemDetection>
#pragma once

namespace Platform {
#ifdef Q_OS_MAC
/// Prevent Apple's app nap from screwing us over
/// tip: the domain can be cross-checked on the command line with <defaults domains>
void disableAppNapViaInfoDict();
#endif
}

// Call before constructing Q(Core)Application.
void setupPreApp(bool quietWindowsAsserts);

// Call after Q(Core)Application exists and logging is installed.
void setupPostApp();

} // namespace Platform
29 changes: 1 addition & 28 deletions src/Utilities/QGCLogging.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,6 @@

QGC_LOGGING_CATEGORY(QGCLoggingLog, "qgc.utilities.qgclogging")

#ifdef Q_OS_WIN

#include <crtdbg.h>
#include <windows.h>
#include <iostream>

/// CRT Report Hook installed using _CrtSetReportHook. We install this hook when
/// we don't want asserts to pop a dialog on windows.
static 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

Q_GLOBAL_STATIC(QGCLogging, _qgcLogging)

static QtMessageHandler defaultHandler = nullptr;
Expand Down Expand Up @@ -88,16 +69,8 @@ QGCLogging::~QGCLogging()
qCDebug(QGCLoggingLog) << this;
}

void QGCLogging::installHandler(bool quietWindowsAsserts)
void QGCLogging::installHandler()
{
#ifdef Q_OS_WIN
if (quietWindowsAsserts) {
_CrtSetReportHook(WindowsCrtReportHook);
}
#else
Q_UNUSED(quietWindowsAsserts)
#endif

// Define the format for qDebug/qWarning/etc output
qSetMessagePattern(QStringLiteral("%{category}:: %{time process} - %{type}: %{message} (%{function}:%{line})"));

Expand Down
2 changes: 1 addition & 1 deletion src/Utilities/QGCLogging.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class QGCLogging : public QStringListModel
static QGCLogging *instance();

/// Install Qt message handler to route logs through this class
static void installHandler(bool quietWindowsAsserts);
static void installHandler();

/// Write current log messages to a file asynchronously
Q_INVOKABLE void writeMessages(const QString &destFile);
Expand Down
Loading
Loading