OSDN Git Service

Happy new year 2017!
[mutilities/MUtilities.git] / src / Startup.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2017 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 //
19 // http://www.gnu.org/licenses/lgpl-2.1.txt
20 //////////////////////////////////////////////////////////////////////////////////
21
22 //MUtils
23 #include <MUtils/Startup.h>
24 #include <MUtils/OSSupport.h>
25 #include <MUtils/Terminal.h>
26 #include <MUtils/ErrorHandler.h>
27 #include <MUtils/Registry.h>
28 #include <MUtils/Exception.h>
29
30 //Qt
31 #include <QApplication>
32 #include <QMutex>
33 #include <QStringList>
34 #include <QLibraryInfo>
35 #include <QTextCodec>
36 #include <QImageReader>
37 #include <QFont>
38 #include <QMessageBox>
39 #include <QtPlugin>
40 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
41 #include <QAbstractNativeEventFilter>
42 #endif
43
44 //CRT
45 #include <string.h>
46
47 ///////////////////////////////////////////////////////////////////////////////
48 // Qt Static Initialization
49 ///////////////////////////////////////////////////////////////////////////////
50
51 #ifdef QT_NODLL
52
53 #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
54 Q_IMPORT_PLUGIN(qico)
55 Q_IMPORT_PLUGIN(qsvg)
56 #else
57 Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
58 Q_IMPORT_PLUGIN(QICOPlugin)
59 #endif
60
61 static void doInitializeResources(void)
62 {
63         Q_INIT_RESOURCE(MUtilsData);
64 }
65
66 static void doCleanupResources(void)
67 {
68         Q_CLEANUP_RESOURCE(MUtilsData);
69 }
70
71 namespace MUtils
72 {
73         namespace Startup
74         {
75                 namespace Internal
76                 {
77                         class ResourceInitializer
78                         {
79                         public:
80                                 ResourceInitializer(void)
81                                 {
82                                         doInitializeResources();
83                                 }
84
85                                 ~ResourceInitializer(void)
86                                 {
87                                         doCleanupResources();
88                                 }
89                         };
90
91                         static ResourceInitializer resourceInitializer;
92                 }
93         }
94 }
95
96 #endif //QT_NODLL
97
98 ///////////////////////////////////////////////////////////////////////////////
99 // MESSAGE HANDLER
100 ///////////////////////////////////////////////////////////////////////////////
101
102 #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
103 static void qt_message_handler(QtMsgType type, const char *const msg)
104 {
105         if (msg && msg[0])
106         {
107                 MUtils::Terminal::write(type, msg);
108                 if ((type == QtCriticalMsg) || (type == QtFatalMsg))
109                 {
110                         MUtils::OS::fatal_exit(MUTILS_WCHR(QString::fromUtf8(msg)));
111                 }
112         }
113 }
114 #else
115 #define qInstallMsgHandler(X) qInstallMessageHandler((X))
116 static void qt_message_handler(QtMsgType type, const QMessageLogContext&, const QString &msg)
117 {
118         if (!msg.isEmpty())
119         {
120                 MUtils::Terminal::write(type, msg.toUtf8().constData());
121                 if ((type == QtCriticalMsg) || (type == QtFatalMsg))
122                 {
123                         MUtils::OS::fatal_exit(MUTILS_WCHR(msg));
124                 }
125         }
126 }
127 #endif
128
129 #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
130 static bool qt_event_filter(void *message, long *result)
131 {
132         return MUtils::OS::handle_os_message(message, result);
133 }
134 #else
135 namespace MUtils
136 {
137         namespace Startup
138         {
139                 namespace Internal
140                 {
141                         class NativeEventFilter : public QAbstractNativeEventFilter
142                         {
143                         public:
144                                 bool nativeEventFilter(const QByteArray&, void *message, long *result) Q_DECL_OVERRIDE
145                                 {
146                                         return MUtils::OS::handle_os_message(message, result);
147                                 };
148                         };
149                 }
150         }
151 }
152 static QScopedPointer<MUtils::Startup::Internal::NativeEventFilter> qt_event_filter;
153 #endif
154
155 ///////////////////////////////////////////////////////////////////////////////
156 // STARTUP FUNCTION
157 ///////////////////////////////////////////////////////////////////////////////
158
159 static int startup_main(int &argc, char **argv, MUtils::Startup::main_function_t *const entry_point, const char* const appName, const bool &debugConsole)
160 {
161         qInstallMsgHandler(qt_message_handler);
162         MUtils::Terminal::setup(argc, argv, appName, MUTILS_DEBUG || debugConsole);
163         return entry_point(argc, argv);
164 }
165
166 static int startup_helper(int &argc, char **argv, MUtils::Startup::main_function_t *const entry_point, const char* const appName, const bool &debugConsole)
167 {
168         int iResult = -1;
169         try
170         {
171                 iResult = startup_main(argc, argv, entry_point, appName, debugConsole);
172         }
173         catch(const std::exception &error)
174         {
175                 MUTILS_PRINT_ERROR("\nGURU MEDITATION !!!\n\nException error:\n%s\n", error.what());
176                 MUtils::OS::fatal_exit(L"Unhandeled C++ exception error, application will exit!");
177         }
178         catch(...)
179         {
180                 MUTILS_PRINT_ERROR("\nGURU MEDITATION !!!\n\nUnknown exception error!\n");
181                 MUtils::OS::fatal_exit(L"Unhandeled C++ exception error, application will exit!");
182         }
183         return iResult;
184 }
185
186 int MUtils::Startup::startup(int &argc, char **argv, main_function_t *const entry_point, const char* const appName, const bool &debugConsole)
187 {
188         int iResult = -1;
189 #if (MUTILS_DEBUG)
190 #ifdef _MSC_VER
191         _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF || _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG));
192 #endif //_MSCVER
193         iResult = startup_main(argc, argv, entry_point, appName, debugConsole);
194 #else //MUTILS_DEBUG
195 #ifdef _MSC_VER
196         __try
197         {
198                 MUtils::ErrorHandler::initialize();
199                 MUtils::OS::check_debugger();
200                 iResult = startup_helper(argc, argv, entry_point, appName, debugConsole);
201         }
202         __except(1)
203         {
204                 MUTILS_PRINT_ERROR("\nGURU MEDITATION !!!\n\nUnhandeled structured exception error!\n");
205                 MUtils::OS::fatal_exit(L"Unhandeled structured exception error, application will exit!");
206         }
207 #else //_MSCVER
208         MUtils::ErrorHandler::initialize();
209         MUtils::OS::check_debugger();
210         iResult = startup_helper(argc, argv, entry_point, appName, debugConsole);
211 #endif //_MSCVER
212 #endif //MUTILS_DEBUG
213         return iResult;
214 }
215
216 ///////////////////////////////////////////////////////////////////////////////
217 // QT INITIALIZATION
218 ///////////////////////////////////////////////////////////////////////////////
219
220 static QMutex g_init_lock;
221 static const char *const g_imageformats[] = {"bmp", "png", "jpg", "gif", "ico", "xpm", "svg", NULL};
222
223 static QString getExecutableName(int &argc, char **argv)
224 {
225         if(argc >= 1)
226         {
227                 const char *argv0 = argv[0];
228                 for (int i = 0; i < 2; i++)
229                 {
230                         static const char SEP[2] = { '/', '\\' };
231                         if (const char *const ptr = strrchr(argv0, SEP[i]))
232                         {
233                                 argv0 = ptr + 1;
234                         }
235                 }
236                 if(strlen(argv0) > 1)
237                 {
238                         return QString::fromLatin1(argv0);
239                 }
240         }
241         return QLatin1String("Program.exe");
242 }
243
244 static void qt_registry_cleanup(void)
245 {
246         static const wchar_t *const QT_JUNK_KEY = L"Software\\Trolltech\\OrganizationDefaults";
247         MUtils::Registry::reg_key_delete(MUtils::Registry::root_user, MUTILS_QSTR(QT_JUNK_KEY), true, true);
248 }
249
250 QApplication *MUtils::Startup::create_qt(int &argc, char **argv, const QString &appName)
251 {
252         QMutexLocker lock(&g_init_lock);
253         const OS::ArgumentMap &arguments = MUtils::OS::arguments();
254
255         //Don't initialized again, if done already
256         if(QApplication::instance() != NULL)
257         {
258                 qWarning("Qt is already initialized!");
259                 return NULL;
260         }
261
262         //Extract executable name from argv[] array
263         const QString executableName = getExecutableName(argc, argv);
264
265         //Check Qt version
266 #ifdef QT_BUILD_KEY
267         qDebug("Using Qt v%s [%s], %s, %s", qVersion(), QLibraryInfo::buildDate().toString(Qt::ISODate).toLatin1().constData(), (qSharedBuild() ? "DLL" : "Static"), QLibraryInfo::buildKey().toLatin1().constData());
268         qDebug("Compiled with Qt v%s, %s\n", QT_VERSION_STR, QT_BUILD_KEY);
269         if(_stricmp(qVersion(), QT_VERSION_STR))
270         {
271                 qFatal("%s", QApplication::tr("Executable '%1' requires Qt v%2, but found Qt v%3.").arg(executableName, QString::fromLatin1(QT_VERSION_STR), QString::fromLatin1(qVersion())).toLatin1().constData());
272                 return false;
273         }
274         if(QLibraryInfo::buildKey().compare(QString::fromLatin1(QT_BUILD_KEY), Qt::CaseInsensitive))
275         {
276                 qFatal("%s", QApplication::tr("Executable '%1' was built for Qt '%2', but found Qt '%3'.").arg(executableName, QString::fromLatin1(QT_BUILD_KEY), QLibraryInfo::buildKey()).toLatin1().constData());
277                 return false;
278         }
279 #else
280         qDebug("Using Qt v%s [%s], %s", qVersion(), QLibraryInfo::buildDate().toString(Qt::ISODate).toLatin1().constData(), (qSharedBuild() ? "DLL" : "Static"));
281         qDebug("Compiled with Qt v%s\n", QT_VERSION_STR);
282 #endif
283
284         //Check the Windows version
285         const MUtils::OS::Version::os_version_t &osVersion = MUtils::OS::os_version();
286         if((osVersion.type != MUtils::OS::Version::OS_WINDOWS) || (osVersion < MUtils::OS::Version::WINDOWS_WINXP))
287         {
288                 qFatal("%s", QApplication::tr("Executable '%1' requires Windows XP or later.").arg(executableName).toLatin1().constData());
289         }
290
291         //Check whether we are running on a supported Windows version
292         if(const char *const friendlyName = MUtils::OS::os_friendly_name(osVersion))
293         {
294                 qDebug("Running on %s (NT v%u.%u.%u).\n", friendlyName, osVersion.versionMajor, osVersion.versionMinor, osVersion.versionBuild);
295         }
296         else
297         {
298                 const QString message = QString().sprintf("Running on an unknown WindowsNT-based system (v%u.%u.%u).", osVersion.versionMajor, osVersion.versionMinor, osVersion.versionBuild);
299                 qWarning("%s\n", MUTILS_UTF8(message));
300                 MUtils::OS::system_message_wrn(MUTILS_WCHR(executableName), MUTILS_WCHR(message));
301         }
302
303         //Check for compat mode
304         if(osVersion.overrideFlag && (osVersion <= MUtils::OS::Version::WINDOWS_WN100))
305         {
306                 qWarning("Windows compatibility mode detected!");
307                 if(!arguments.contains("ignore-compat-mode"))
308                 {
309                         qFatal("%s", QApplication::tr("Executable '%1' doesn't support Windows compatibility mode.").arg(executableName).toLatin1().constData());
310                         return NULL;
311                 }
312         }
313
314         //Check for Wine
315         if(MUtils::OS::running_on_wine())
316         {
317                 qWarning("It appears we are running under Wine, unexpected things might happen!\n");
318         }
319
320         //Set text Codec for locale
321         QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
322
323         //Create Qt application instance
324         QApplication *application = new QApplication(argc, argv);
325
326         //Register the Qt clean-up function
327         atexit(qt_registry_cleanup);
328
329         //Load plugins from application directory
330         QCoreApplication::setLibraryPaths(QStringList() << QApplication::applicationDirPath());
331         qDebug("Library Path:\n%s\n", MUTILS_UTF8(QApplication::libraryPaths().first()));
332
333         //Set application properties
334         application->setApplicationName(appName);
335         application->setOrganizationName("LoRd_MuldeR");
336         application->setOrganizationDomain("mulder.at.gg");
337 #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
338         application->setEventFilter(qt_event_filter);
339 #else
340         qt_event_filter.reset(new Internal::NativeEventFilter);
341         application->installNativeEventFilter(qt_event_filter.data());
342 #endif
343
344         //Check for supported image formats
345         QList<QByteArray> supportedFormats = QImageReader::supportedImageFormats();
346         for(int i = 0; g_imageformats[i]; i++)
347         {
348                 if(!supportedFormats.contains(g_imageformats[i]))
349                 {
350                         qFatal("Qt initialization error: QImageIOHandler for '%s' missing!", g_imageformats[i]);
351                         MUTILS_DELETE(application);
352                         return NULL;
353                 }
354         }
355         
356         //Setup console icon
357         MUtils::Terminal::set_icon(QIcon(":/mutils/icons/bug.png"));
358
359         //Enable larger/smaller font size
360         double fontScaleFactor = 1.0;
361         if(arguments.contains("huge-font" )) fontScaleFactor = 1.500;
362         if(arguments.contains("big-font"  )) fontScaleFactor = 1.250;
363         if(arguments.contains("small-font")) fontScaleFactor = 0.875;
364         if(arguments.contains("tiny-font" )) fontScaleFactor = 0.750;
365         if(!qFuzzyCompare(fontScaleFactor, 1.0))
366         {
367                 qWarning("Application font scale factor set to: %.3f\n", fontScaleFactor);
368                 QFont appFont = application->font();
369                 appFont.setPointSizeF(appFont.pointSizeF() * fontScaleFactor);
370                 application->setFont(appFont);
371         }
372
373         //Check for process elevation
374         if(MUtils::OS::is_elevated() && (!MUtils::OS::running_on_wine()))
375         {
376                 QMessageBox messageBox(QMessageBox::Warning, executableName, "<nobr>This program was started with 'elevated' rights, altough it does not need these rights.<br>Running an applications with unnecessary rights is a potential security risk!</nobr>", QMessageBox::NoButton, NULL, Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::WindowStaysOnTopHint);
377                 messageBox.addButton("Quit Program (Recommended)", QMessageBox::NoRole);
378                 messageBox.addButton("Ignore", QMessageBox::NoRole);
379                 if(messageBox.exec() == 0)
380                 {
381                         MUTILS_DELETE(application);
382                         return NULL;
383                 }
384         }
385
386         //Qt created successfully
387         return application;
388 }
389
390 ///////////////////////////////////////////////////////////////////////////////
391