Skip to content

Commit b72cf4e

Browse files
committed
Utilities: Add Platform Environment/Signal Handling
1 parent 64ac35c commit b72cf4e

File tree

10 files changed

+456
-213
lines changed

10 files changed

+456
-213
lines changed

src/QGCApplication.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99

1010
#pragma once
1111

12+
#include <QtCore/QLoggingCategory>
1213
#include <QtCore/QElapsedTimer>
1314
#include <QtCore/QMap>
1415
#include <QtCore/QSet>
1516
#include <QtCore/QTimer>
1617
#include <QtCore/QTranslator>
17-
1818
#include <QtWidgets/QApplication>
1919

2020
class QQmlApplicationEngine;
@@ -38,6 +38,8 @@ class QMetaObject;
3838

3939
#define qgcApp() qApp
4040

41+
Q_DECLARE_LOGGING_CATEGORY(QGCApplicationLog)
42+
4143
/// The main application and management class.
4244
/// Needs QApplication base to support QtCharts module.
4345
/// TODO: Use QtGraphs to convert to QGuiApplication
@@ -129,15 +131,16 @@ private slots:
129131
bool compressEvent(QEvent *event, QObject *receiver, QPostEventList *postedEvents) final;
130132

131133
void _initVideo();
132-
134+
133135
/// Initialize the application for normal application boot. Or in other words we are not going to run unit tests.
134136
void _initForNormalAppBoot();
135137

136138
QObject *_rootQmlObject();
137139
void _checkForNewVersion();
138140

139141
bool _runningUnitTests = false;
140-
bool _simpleBootTest = false;
142+
bool _simpleBootTest = false;
143+
141144
static constexpr int _missingParamsDelayedDisplayTimerTimeout = 1000; ///< Timeout to wait for next missing fact to come in before display
142145
QTimer _missingParamsDelayedDisplayTimer; ///< Timer use to delay missing fact display
143146
QList<QPair<int,QString>> _missingParams; ///< List of missing parameter component id:name

src/RunGuard.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,6 @@ class RunGuard
3636
const QString _key;
3737
const QString _lockFilePath;
3838
QLockFile _lockFile;
39+
40+
Q_DISABLE_COPY(RunGuard)
3941
};

src/Utilities/CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ target_sources(${CMAKE_PROJECT_NAME}
2222
StateMachine.h
2323
)
2424

25-
if(LINUX)
25+
if(NOT ANDROID AND NOT IOS)
2626
target_sources(${CMAKE_PROJECT_NAME}
2727
PRIVATE
2828
SignalHandler.cc
2929
SignalHandler.h
3030
)
31-
elseif(IOS)
31+
endif()
32+
33+
if(IOS)
3234
target_sources(${CMAKE_PROJECT_NAME}
3335
PRIVATE
3436
MobileScreenMgr.mm

src/Utilities/Platform.cc

Lines changed: 148 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,158 @@
1+
/****************************************************************************
2+
*
3+
* (c) 2009-2024 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4+
*
5+
* QGroundControl is licensed according to the terms in the file
6+
* COPYING.md in the root of the source code directory.
7+
*
8+
****************************************************************************/
9+
110
#include "Platform.h"
211

3-
#ifdef Q_OS_MAC
4-
#include <CoreFoundation/CoreFoundation.h>
12+
#include <QtCore/QCoreApplication>
13+
#include <QtCore/QProcessEnvironment>
14+
15+
#if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID)
16+
#include "SignalHandler.h"
17+
#endif
518

6-
void Platform::disableAppNapViaInfoDict()
19+
#if defined(Q_OS_MACOS)
20+
#include <CoreFoundation/CoreFoundation.h>
21+
#elif defined(Q_OS_WIN)
22+
#include <qt_windows.h>
23+
#include <iostream>
24+
#include <iterator> // std::size
25+
#include <cwchar> // swprintf
26+
#if defined(_MSC_VER)
27+
#include <crtdbg.h>
28+
#include <stdlib.h>
29+
#include <cstdio> // _snwprintf_s
30+
#endif
31+
#endif
32+
33+
namespace {
34+
35+
#if defined(Q_OS_MACOS)
36+
void disableAppNapViaInfoDict()
737
{
838
CFBundleRef bundle = CFBundleGetMainBundle();
9-
if (!bundle) return;
39+
if (!bundle) {
40+
return;
41+
}
42+
CFMutableDictionaryRef infoDict = const_cast<CFMutableDictionaryRef>(CFBundleGetInfoDictionary(bundle));
43+
if (infoDict) {
44+
CFDictionarySetValue(infoDict, CFSTR("NSAppSleepDisabled"), kCFBooleanTrue);
45+
}
46+
}
47+
#endif // Q_OS_MACOS
1048

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

15-
// Inject the key → true
16-
if (infoDict) {
17-
CFDictionarySetValue(infoDict,
18-
CFSTR("NSAppSleepDisabled"),
19-
kCFBooleanTrue);
51+
#if defined(_MSC_VER)
52+
int __cdecl WindowsCrtReportHook(int reportType, char* message, int* returnValue)
53+
{
54+
if (message) {
55+
std::cerr << message << std::endl;
56+
}
57+
if (reportType == _CRT_ASSERT) {
58+
if (returnValue) {
59+
*returnValue = 0;
60+
}
61+
return 1; // handled
62+
}
63+
return 0; // let CRT continue
64+
}
65+
66+
void __cdecl WindowsPurecallHandler()
67+
{
68+
(void) OutputDebugStringW(L"QGC: _purecall\n");
69+
}
70+
71+
void WindowsInvalidParameterHandler([[maybe_unused]] const wchar_t* expression,
72+
[[maybe_unused]] const wchar_t* function,
73+
[[maybe_unused]] const wchar_t* file,
74+
[[maybe_unused]] unsigned int line,
75+
[[maybe_unused]] uintptr_t pReserved)
76+
{
77+
78+
}
79+
#endif // _MSC_VER
80+
81+
LPTOP_LEVEL_EXCEPTION_FILTER g_prevUef = nullptr;
82+
83+
LONG WINAPI WindowsUnhandledExceptionFilter(EXCEPTION_POINTERS* ep)
84+
{
85+
const DWORD code = (ep && ep->ExceptionRecord) ? ep->ExceptionRecord->ExceptionCode : 0;
86+
wchar_t buf[128] = {};
87+
#if defined(_MSC_VER)
88+
(void) _snwprintf_s(buf, _TRUNCATE, L"QGC: unhandled SEH 0x%08lX\n", static_cast<unsigned long>(code));
89+
#else
90+
(void) swprintf(buf, static_cast<int>(std::size(buf)), L"QGC: unhandled SEH 0x%08lX\n", static_cast<unsigned long>(code));
91+
#endif
92+
(void) OutputDebugStringW(buf);
93+
94+
const HANDLE h = GetStdHandle(STD_ERROR_HANDLE);
95+
if (h && (h != INVALID_HANDLE_VALUE)) {
96+
DWORD ignored = 0;
97+
const char narrow[] = "QGC: unhandled SEH\n";
98+
(void) WriteFile(h, narrow, (DWORD)sizeof(narrow) - 1, &ignored, nullptr);
2099
}
100+
101+
return EXCEPTION_EXECUTE_HANDLER;
21102
}
103+
104+
void setWindowsErrorModes(bool quietWindowsAsserts)
105+
{
106+
(void) SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
107+
g_prevUef = SetUnhandledExceptionFilter(WindowsUnhandledExceptionFilter);
108+
109+
#if defined(_MSC_VER)
110+
(void) _set_invalid_parameter_handler(WindowsInvalidParameterHandler);
111+
(void) _set_purecall_handler(WindowsPurecallHandler);
112+
113+
if (quietWindowsAsserts) {
114+
(void) _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
115+
(void) _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
116+
(void) _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
117+
(void) _CrtSetReportHook2(_CRT_RPTHOOK_INSTALL, WindowsCrtReportHook);
118+
(void) _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
119+
(void) _set_error_mode(_OUT_TO_STDERR);
120+
}
121+
#else
122+
Q_UNUSED(quietWindowsAsserts);
22123
#endif
124+
}
125+
#endif // Q_OS_WIN
126+
127+
} // namespace
128+
129+
void Platform::setupPreApp(bool quietWindowsAsserts)
130+
{
131+
#ifdef Q_OS_UNIX
132+
if (!qEnvironmentVariableIsSet("QT_ASSUME_STDERR_HAS_CONSOLE")) {
133+
(void) qputenv("QT_ASSUME_STDERR_HAS_CONSOLE", "1");
134+
}
135+
if (!qEnvironmentVariableIsSet("QT_FORCE_STDERR_LOGGING")) {
136+
(void) qputenv("QT_FORCE_STDERR_LOGGING", "1");
137+
}
138+
#endif
139+
140+
#ifdef Q_OS_WIN
141+
if (!qEnvironmentVariableIsSet("QT_WIN_DEBUG_CONSOLE")) {
142+
(void) qputenv("QT_WIN_DEBUG_CONSOLE", "attach");
143+
}
144+
setWindowsErrorModes(quietWindowsAsserts);
145+
#endif
146+
147+
#ifdef Q_OS_MACOS
148+
disableAppNapViaInfoDict();
149+
#endif
150+
}
151+
152+
void Platform::setupPostApp()
153+
{
154+
#if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID)
155+
SignalHandler* signalHandler = new SignalHandler(QCoreApplication::instance());
156+
(void) signalHandler->setupSignalHandlers();
157+
#endif
158+
}

src/Utilities/Platform.h

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
1-
#pragma once
1+
/****************************************************************************
2+
*
3+
* (c) 2009-2024 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4+
*
5+
* QGroundControl is licensed according to the terms in the file
6+
* COPYING.md in the root of the source code directory.
7+
*
8+
****************************************************************************/
29

3-
#include <QtCore/QtSystemDetection>
10+
#pragma once
411

512
namespace Platform {
6-
#ifdef Q_OS_MAC
7-
/// Prevent Apple's app nap from screwing us over
8-
/// tip: the domain can be cross-checked on the command line with <defaults domains>
9-
void disableAppNapViaInfoDict();
10-
#endif
11-
}
13+
14+
// Call before constructing Q(Core)Application.
15+
void setupPreApp(bool quietWindowsAsserts);
16+
17+
// Call after Q(Core)Application exists and logging is installed.
18+
void setupPostApp();
19+
20+
} // namespace Platform

src/Utilities/QGCLogging.cc

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,6 @@
2020

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

23-
#ifdef Q_OS_WIN
24-
25-
#include <crtdbg.h>
26-
#include <windows.h>
27-
#include <iostream>
28-
29-
/// CRT Report Hook installed using _CrtSetReportHook. We install this hook when
30-
/// we don't want asserts to pop a dialog on windows.
31-
static int WindowsCrtReportHook(int reportType, char *message, int *returnValue)
32-
{
33-
Q_UNUSED(reportType);
34-
35-
std::cerr << message << std::endl; // Output message to stderr
36-
*returnValue = 0; // Don't break into debugger
37-
return true; // We handled this fully ourselves
38-
}
39-
40-
#endif
41-
4223
Q_GLOBAL_STATIC(QGCLogging, _qgcLogging)
4324

4425
static QtMessageHandler defaultHandler = nullptr;
@@ -88,16 +69,8 @@ QGCLogging::~QGCLogging()
8869
qCDebug(QGCLoggingLog) << this;
8970
}
9071

91-
void QGCLogging::installHandler(bool quietWindowsAsserts)
72+
void QGCLogging::installHandler()
9273
{
93-
#ifdef Q_OS_WIN
94-
if (quietWindowsAsserts) {
95-
_CrtSetReportHook(WindowsCrtReportHook);
96-
}
97-
#else
98-
Q_UNUSED(quietWindowsAsserts)
99-
#endif
100-
10174
// Define the format for qDebug/qWarning/etc output
10275
qSetMessagePattern(QStringLiteral("%{category}:: %{time process} - %{type}: %{message} (%{function}:%{line})"));
10376

src/Utilities/QGCLogging.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class QGCLogging : public QStringListModel
2828
static QGCLogging *instance();
2929

3030
/// Install Qt message handler to route logs through this class
31-
static void installHandler(bool quietWindowsAsserts);
31+
static void installHandler();
3232

3333
/// Write current log messages to a file asynchronously
3434
Q_INVOKABLE void writeMessages(const QString &destFile);

0 commit comments

Comments
 (0)