Skip to content

Commit 9828d61

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

File tree

10 files changed

+448
-210
lines changed

10 files changed

+448
-210
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: 153 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,163 @@
1+
// Platform.cpp
2+
/****************************************************************************
3+
*
4+
* (c) 2009-2024 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
5+
*
6+
* QGroundControl is licensed according to the terms in the file
7+
* COPYING.md in the root of the source code directory.
8+
*
9+
****************************************************************************/
10+
111
#include "Platform.h"
212

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

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

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);
50+
#if defined(Q_OS_WIN)
1451

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

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 setupEarlyProcess(bool quietWindowsAsserts);
16+
17+
// Call after Q(Core)Application exists and logging is installed.
18+
void setupLateProcess();
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)