1 /*
2 * Copyright (c) 1999 Matthias Elter <me@kde.org>
3 * Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
4 * Copyright (c) 2015 Boudewijn Rempt <boud@valdyas.org>
5 *
6 *  This program is free software; you can redistribute it and/or modify
7 *  it under the terms of the GNU General Public License as published by
8 *  the Free Software Foundation; either version 2 of the License, or
9 *  (at your option) any later version.
10 *
11 *  This program is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 *  GNU General Public License for more details.
15 *
16 *  You should have received a copy of the GNU General Public License
17 *  along with this program; if not, write to the Free Software
18 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20 
21 #include <stdlib.h>
22 
23 #include <QString>
24 #include <QPixmap>
25 #include <kis_debug.h>
26 #include <QProcess>
27 #include <QProcessEnvironment>
28 #include <QStandardPaths>
29 #include <QDir>
30 #include <QDate>
31 #include <QLocale>
32 #include <QSettings>
33 #include <QByteArray>
34 #include <QMessageBox>
35 #include <QThread>
36 
37 #include <QOperatingSystemVersion>
38 
39 #include <time.h>
40 
41 #include <KisApplication.h>
42 #include <KoConfig.h>
43 #include <KoResourcePaths.h>
44 #include <kis_config.h>
45 
46 #include "data/splash/splash_screen.xpm"
47 #include "data/splash/splash_holidays.xpm"
48 #include "data/splash/splash_screen_x2.xpm"
49 #include "data/splash/splash_holidays_x2.xpm"
50 #include "KisDocument.h"
51 #include "kis_splash_screen.h"
52 #include "KisPart.h"
53 #include "KisApplicationArguments.h"
54 #include <opengl/kis_opengl.h>
55 #include "input/KisQtWidgetsTweaker.h"
56 #include <KisUsageLogger.h>
57 #include <kis_image_config.h>
58 
59 #ifdef Q_OS_ANDROID
60 #include <QtAndroid>
61 #endif
62 
63 #if defined Q_OS_WIN
64 #include "config_use_qt_tablet_windows.h"
65 #include <windows.h>
66 #ifndef USE_QT_TABLET_WINDOWS
67 #include <kis_tablet_support_win.h>
68 #include <kis_tablet_support_win8.h>
69 #else
70 #include <dialogs/KisDlgCustomTabletResolution.h>
71 #endif
72 #include "config-high-dpi-scale-factor-rounding-policy.h"
73 #include "config-set-has-border-in-full-screen-default.h"
74 #ifdef HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT
75 #include <QtPlatformHeaders/QWindowsWindowFunctions>
76 #endif
77 #include <QLibrary>
78 #endif
79 #if defined HAVE_KCRASH
80 #include <kcrash.h>
81 #elif defined USE_DRMINGW
82 namespace
83 {
tryInitDrMingw()84 void tryInitDrMingw()
85 {
86     wchar_t path[MAX_PATH];
87     QString pathStr = QCoreApplication::applicationDirPath().replace(L'/', L'\\') + QStringLiteral("\\exchndl.dll");
88     if (pathStr.size() > MAX_PATH - 1) {
89         return;
90     }
91     int pathLen = pathStr.toWCharArray(path);
92     path[pathLen] = L'\0'; // toWCharArray doesn't add NULL terminator
93     HMODULE hMod = LoadLibraryW(path);
94     if (!hMod) {
95         return;
96     }
97     // No need to call ExcHndlInit since the crash handler is installed on DllMain
98     auto myExcHndlSetLogFileNameA = reinterpret_cast<BOOL (APIENTRY *)(const char *)>(GetProcAddress(hMod, "ExcHndlSetLogFileNameA"));
99     if (!myExcHndlSetLogFileNameA) {
100         return;
101     }
102     // Set the log file path to %LocalAppData%\kritacrash.log
103     QString logFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation).replace(L'/', L'\\') + QStringLiteral("\\kritacrash.log");
104     myExcHndlSetLogFileNameA(logFile.toLocal8Bit());
105 }
106 } // namespace
107 #endif
108 
109 #ifdef Q_OS_WIN
110 namespace
111 {
112 typedef enum ORIENTATION_PREFERENCE {
113     ORIENTATION_PREFERENCE_NONE = 0x0,
114     ORIENTATION_PREFERENCE_LANDSCAPE = 0x1,
115     ORIENTATION_PREFERENCE_PORTRAIT = 0x2,
116     ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED = 0x4,
117     ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED = 0x8
118 } ORIENTATION_PREFERENCE;
119 typedef BOOL WINAPI (*pSetDisplayAutoRotationPreferences_t)(
120         ORIENTATION_PREFERENCE orientation
121         );
resetRotation()122 void resetRotation()
123 {
124     QLibrary user32Lib("user32");
125     if (!user32Lib.load()) {
126         qWarning() << "Failed to load user32.dll! This really should not happen.";
127         return;
128     }
129     pSetDisplayAutoRotationPreferences_t pSetDisplayAutoRotationPreferences
130             = reinterpret_cast<pSetDisplayAutoRotationPreferences_t>(user32Lib.resolve("SetDisplayAutoRotationPreferences"));
131     if (!pSetDisplayAutoRotationPreferences) {
132         dbgKrita << "Failed to load function SetDisplayAutoRotationPreferences";
133         return;
134     }
135     bool result = pSetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE);
136     dbgKrita << "SetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE) returned" << result;
137 }
138 } // namespace
139 #endif
140 
141 #ifdef Q_OS_ANDROID
142 extern "C" JNIEXPORT void JNICALL
Java_org_krita_android_JNIWrappers_saveState(JNIEnv *,jobject,jint)143 Java_org_krita_android_JNIWrappers_saveState(JNIEnv* /*env*/,
144                                              jobject /*obj*/,
145                                              jint    /*n*/)
146 {
147     if (!KisPart::exists()) return;
148 
149     KisPart *kisPart = KisPart::instance();
150     QList<QPointer<KisDocument>> list = kisPart->documents();
151     for (QPointer<KisDocument> &doc: list)
152     {
153         doc->autoSaveOnPause();
154     }
155 
156     const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
157     QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
158     kritarc.setValue("canvasState", "OPENGL_SUCCESS");
159 }
160 
161 extern "C" JNIEXPORT jboolean JNICALL
Java_org_krita_android_JNIWrappers_exitFullScreen(JNIEnv *,jobject,jint)162 Java_org_krita_android_JNIWrappers_exitFullScreen(JNIEnv* /*env*/,
163                                                   jobject /*obj*/,
164                                                   jint    /*n*/)
165 {
166     if (!KisPart::exists()) {
167         return false;
168     }
169 
170     KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow();
171     if (mainWindow) {
172         mainWindow->viewFullscreen(false);
173         return true;
174     } else {
175         return false;
176     }
177 }
178 
179 extern "C" JNIEXPORT void JNICALL
Java_org_krita_android_JNIWrappers_openFileFromIntent(JNIEnv *,jobject,jstring str)180 Java_org_krita_android_JNIWrappers_openFileFromIntent(JNIEnv* /*env*/,
181                                                       jobject /*obj*/,
182                                                       jstring str)
183 {
184     QAndroidJniObject jUri(str);
185     if (jUri.isValid()) {
186         QString uri = jUri.toString();
187         QMetaObject::invokeMethod(KisApplication::instance(), "fileOpenRequested",
188                                   Qt::QueuedConnection, Q_ARG(QString, uri));
189     }
190 }
191 
192 __attribute__ ((visibility ("default")))
193 #endif
main(int argc,char ** argv)194 extern "C" int main(int argc, char **argv)
195 {
196     // The global initialization of the random generator
197     qsrand(time(0));
198     bool runningInKDE = !qgetenv("KDE_FULL_SESSION").isEmpty();
199 
200 #if defined HAVE_X11
201     qputenv("QT_QPA_PLATFORM", "xcb");
202 #endif
203 
204     // Workaround a bug in QNetworkManager
205     qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
206 
207     // A per-user unique string, without /, because QLocalServer cannot use names with a / in it
208     QString key = "Krita4" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation).replace("/", "_");
209     key = key.replace(":", "_").replace("\\","_");
210 
211     QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
212 
213     QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
214     QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
215 
216     QCoreApplication::setAttribute(Qt::AA_DisableShaderDiskCache, true);
217 
218 #ifdef HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY
219     // This rounding policy depends on a series of patches to Qt related to
220     // https://bugreports.qt.io/browse/QTBUG-53022. These patches are applied
221     // in ext_qt for WIndows (patches 0031-0036).
222     //
223     // The rounding policy can be set externally by setting the environment
224     // variable `QT_SCALE_FACTOR_ROUNDING_POLICY` to one of the following:
225     //   Round:            Round up for .5 and above.
226     //   Ceil:             Always round up.
227     //   Floor:            Always round down.
228     //   RoundPreferFloor: Round up for .75 and above.
229     //   PassThrough:      Don't round.
230     //
231     // The default is set to RoundPreferFloor for better behaviour than before,
232     // but can be overridden by the above environment variable.
233     QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor);
234 #endif
235 
236 #ifdef Q_OS_ANDROID
237     const QString write_permission = "android.permission.WRITE_EXTERNAL_STORAGE";
238     const QStringList permissions = { write_permission };
239     const QtAndroid::PermissionResultMap resultHash =
240             QtAndroid::requestPermissionsSync(QStringList(permissions));
241 
242     if (resultHash[write_permission] == QtAndroid::PermissionResult::Denied) {
243         // TODO: show a dialog and graciously exit
244         dbgKrita << "Permission denied by the user";
245     }
246     else {
247         dbgKrita << "Permission granted";
248     }
249 #endif
250 
251     const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
252     QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
253 
254     bool singleApplication = true;
255     bool enableOpenGLDebug = false;
256     bool openGLDebugSynchronous = false;
257     bool logUsage = true;
258     {
259 
260         singleApplication = kritarc.value("EnableSingleApplication", true).toBool();
261         if (kritarc.value("EnableHiDPI", true).toBool()) {
262             QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
263         }
264         if (!qgetenv("KRITA_HIDPI").isEmpty()) {
265             QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
266         }
267 #ifdef HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY
268         if (kritarc.value("EnableHiDPIFractionalScaling", false).toBool()) {
269             QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
270         }
271 #endif
272 
273         if (!qgetenv("KRITA_OPENGL_DEBUG").isEmpty()) {
274             enableOpenGLDebug = true;
275         } else {
276             enableOpenGLDebug = kritarc.value("EnableOpenGLDebug", false).toBool();
277         }
278         if (enableOpenGLDebug && (qgetenv("KRITA_OPENGL_DEBUG") == "sync" || kritarc.value("OpenGLDebugSynchronous", false).toBool())) {
279             openGLDebugSynchronous = true;
280         }
281 
282         KisConfig::RootSurfaceFormat rootSurfaceFormat = KisConfig::rootSurfaceFormat(&kritarc);
283         KisOpenGL::OpenGLRenderer preferredRenderer = KisOpenGL::RendererAuto;
284 
285         logUsage = kritarc.value("LogUsage", true).toBool();
286 
287 #ifdef Q_OS_WIN
288         const QString preferredRendererString = kritarc.value("OpenGLRenderer", "angle").toString();
289 #else
290         const QString preferredRendererString = kritarc.value("OpenGLRenderer", "auto").toString();
291 #endif
292         preferredRenderer = KisOpenGL::convertConfigToOpenGLRenderer(preferredRendererString);
293 
294         const KisOpenGL::RendererConfig config =
295             KisOpenGL::selectSurfaceConfig(preferredRenderer, rootSurfaceFormat, enableOpenGLDebug);
296 
297         KisOpenGL::setDefaultSurfaceConfig(config);
298         KisOpenGL::setDebugSynchronous(openGLDebugSynchronous);
299 
300 #ifdef Q_OS_WIN
301         // HACK: https://bugs.kde.org/show_bug.cgi?id=390651
302         resetRotation();
303 #endif
304     }
305 
306     if (logUsage) {
307         KisUsageLogger::initialize();
308     }
309 
310 
311     QString root;
312     QString language;
313     {
314         // Create a temporary application to get the root
315         QCoreApplication app(argc, argv);
316         Q_UNUSED(app);
317         root = KoResourcePaths::getApplicationRoot();
318         QSettings languageoverride(configPath + QStringLiteral("/klanguageoverridesrc"), QSettings::IniFormat);
319         languageoverride.beginGroup(QStringLiteral("Language"));
320         language = languageoverride.value(qAppName(), "").toString();
321     }
322 
323 
324 #ifdef Q_OS_LINUX
325     {
326         QByteArray originalXdgDataDirs = qgetenv("XDG_DATA_DIRS");
327         if (originalXdgDataDirs.isEmpty()) {
328             // We don't want to completely override the default
329             originalXdgDataDirs = "/usr/local/share/:/usr/share/";
330         }
331         qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share") + ":" + originalXdgDataDirs);
332 
333         // APPIMAGE SOUND ADDITIONS
334         // GStreamer needs a few environment variables to properly function in an appimage context.
335         // The following code should be configured to **only** run when we detect that Krita is being
336         // run within an appimage. Checking for the presence of an APPDIR path env variable seems to be
337         // enough to filter out this step for non-appimage krita builds.
338 
339         const bool isInAppimage = qEnvironmentVariableIsSet("APPIMAGE");
340         if (isInAppimage) {
341             QByteArray appimageMountDir = qgetenv("APPDIR");
342 
343             //We need to add new gstreamer plugin paths for the system to find the
344             //appropriate plugins.
345             const QByteArray gstPluginSystemPath = qgetenv("GST_PLUGIN_SYSTEM_PATH_1_0");
346             const QByteArray gstPluginScannerPath = qgetenv("GST_PLUGIN_SCANNER");
347 
348             //Plugins Path is where libgstreamer-1.0 should expect to find plugin libraries.
349             qputenv("GST_PLUGIN_SYSTEM_PATH_1_0", appimageMountDir + QFile::encodeName("/usr/lib/gstreamer-1.0/") + ":" + gstPluginSystemPath);
350 
351             //Plugin scanner is where gstreamer should expect to find the plugin scanner.
352             //Perhaps invoking the scanenr earlier in the code manually could allow ldd to quickly find all plugin dependencies?
353             qputenv("GST_PLUGIN_SCANNER", appimageMountDir + QFile::encodeName("/usr/lib/gstreamer-1.0/gst-plugin-scanner"));
354         }
355     }
356 #else
357     qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share"));
358 #endif
359 
360     dbgKrita << "Setting XDG_DATA_DIRS" << qgetenv("XDG_DATA_DIRS");
361 
362     // Now that the paths are set, set the language. First check the override from the language
363     // selection dialog.
364 
365     dbgKrita << "Override language:" << language;
366     bool rightToLeft = false;
367     if (!language.isEmpty()) {
368         KLocalizedString::setLanguages(language.split(":"));
369 
370         // And override Qt's locale, too
371         QLocale locale(language.split(":").first());
372         QLocale::setDefault(locale);
373 #ifdef Q_OS_MAC
374         // prevents python >=3.7 nl_langinfo(CODESET) fail bug 417312.
375         qputenv("LANG", (locale.name() + ".UTF-8").toLocal8Bit());
376 #else
377         qputenv("LANG", locale.name().toLocal8Bit());
378 #endif
379 
380         const QStringList rtlLanguages = QStringList()
381                 << "ar" << "dv" << "he" << "ha" << "ku" << "fa" << "ps" << "ur" << "yi";
382 
383         if (rtlLanguages.contains(language.split(':').first())) {
384             rightToLeft = true;
385         }
386     }
387     else {
388         dbgKrita << "Qt UI languages:" << QLocale::system().uiLanguages() << qgetenv("LANG");
389 
390         // And if there isn't one, check the one set by the system.
391         QLocale locale = QLocale::system();
392 
393 #ifdef Q_OS_ANDROID
394         // QLocale::uiLanguages() fails on Android, so if the fallback locale is being
395         // used we, try to fetch the device's default locale.
396         if (locale.name() == QLocale::c().name()) {
397             QAndroidJniObject localeJniObj = QAndroidJniObject::callStaticObjectMethod(
398                 "java/util/Locale", "getDefault", "()Ljava/util/Locale;");
399 
400             if (localeJniObj.isValid()) {
401                 QAndroidJniObject tag = localeJniObj.callObjectMethod("toLanguageTag",
402                                                                       "()Ljava/lang/String;");
403                 if (tag.isValid()) {
404                     locale = QLocale(tag.toString());
405                 }
406             }
407         }
408 #endif
409         if (locale.name() != QStringLiteral("en")) {
410             QStringList uiLanguages = locale.uiLanguages();
411             for (QString &uiLanguage : uiLanguages) {
412 
413                 // This list of language codes that can have a specifier should
414                 // be extended whenever we have translations that need it; right
415                 // now, only en, pt, zh are in this situation.
416 
417                 if (uiLanguage.startsWith("en") || uiLanguage.startsWith("pt")) {
418                     uiLanguage.replace(QChar('-'), QChar('_'));
419                 }
420                 else if (uiLanguage.startsWith("zh-Hant") || uiLanguage.startsWith("zh-TW")) {
421                     uiLanguage = "zh_TW";
422                 }
423                 else if (uiLanguage.startsWith("zh-Hans") || uiLanguage.startsWith("zh-CN")) {
424                     uiLanguage = "zh_CN";
425                 }
426             }
427 
428             if (uiLanguages.size() > 0 ) {
429                 QString envLanguage = uiLanguages.first();
430                 envLanguage.replace(QChar('-'), QChar('_'));
431 
432                 for (int i = 0; i < uiLanguages.size(); i++) {
433                     QString uiLanguage = uiLanguages[i];
434                     // Strip the country code
435                     int idx = uiLanguage.indexOf(QChar('-'));
436 
437                     if (idx != -1) {
438                         uiLanguage = uiLanguage.left(idx);
439                         uiLanguages.replace(i, uiLanguage);
440                     }
441                 }
442                 dbgKrita << "Converted ui languages:" << uiLanguages;
443 #ifdef Q_OS_MAC
444                 // See https://bugs.kde.org/show_bug.cgi?id=396370
445                 KLocalizedString::setLanguages(QStringList() << uiLanguages.first());
446                 qputenv("LANG", (envLanguage + ".UTF-8").toLocal8Bit());
447 #else
448                 KLocalizedString::setLanguages(QStringList() << uiLanguages);
449                 qputenv("LANG", envLanguage.toLocal8Bit());
450 #endif
451             }
452         }
453     }
454 
455 #if defined Q_OS_WIN && defined USE_QT_TABLET_WINDOWS && defined QT_HAS_WINTAB_SWITCH
456     const bool forceWinTab = !KisConfig::useWin8PointerInputNoApp(&kritarc);
457     QCoreApplication::setAttribute(Qt::AA_MSWindowsUseWinTabAPI, forceWinTab);
458 
459     if (qEnvironmentVariableIsEmpty("QT_WINTAB_DESKTOP_RECT") &&
460         qEnvironmentVariableIsEmpty("QT_IGNORE_WINTAB_MAPPING")) {
461 
462         QRect customTabletRect;
463         KisDlgCustomTabletResolution::Mode tabletMode =
464             KisDlgCustomTabletResolution::getTabletMode(&customTabletRect);
465         KisDlgCustomTabletResolution::applyConfiguration(tabletMode, customTabletRect);
466     }
467 #endif
468 
469     // first create the application so we can create a pixmap
470     KisApplication app(key, argc, argv);
471 
472     if (app.platformName() == "wayland") {
473         QMessageBox::critical(0, i18nc("@title:window", "Fatal Error"), i18n("Krita does not support the Wayland platform. Use XWayland to run Krita on Wayland. Krita will close now."));
474         return -1;
475     }
476 
477     KisUsageLogger::writeHeader();
478     KisOpenGL::initialize();
479 
480 #ifdef HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT
481     if (QCoreApplication::testAttribute(Qt::AA_UseDesktopOpenGL)) {
482         QWindowsWindowFunctions::setHasBorderInFullScreenDefault(true);
483     }
484 #endif
485 
486 
487     if (!language.isEmpty()) {
488         if (rightToLeft) {
489             app.setLayoutDirection(Qt::RightToLeft);
490         }
491         else {
492             app.setLayoutDirection(Qt::LeftToRight);
493         }
494     }
495 #ifdef Q_OS_ANDROID
496     // TODO: remove "share" - sh_zam
497     // points to /data/data/org.krita/files/share/locale
498     KLocalizedString::addDomainLocaleDir("krita", QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/share/locale");
499 #endif
500 
501     KLocalizedString::setApplicationDomain("krita");
502 
503     dbgKrita << "Available translations" << KLocalizedString::availableApplicationTranslations();
504     dbgKrita << "Available domain translations" << KLocalizedString::availableDomainTranslations("krita");
505 
506 
507 #ifdef Q_OS_WIN
508     QDir appdir(KoResourcePaths::getApplicationRoot());
509     QString path = qgetenv("PATH");
510     qputenv("PATH", QFile::encodeName(appdir.absolutePath() + "/bin" + ";"
511                                       + appdir.absolutePath() + "/lib" + ";"
512                                       + appdir.absolutePath() + "/Frameworks" + ";"
513                                       + appdir.absolutePath() + ";"
514                                       + path));
515 
516     dbgKrita << "PATH" << qgetenv("PATH");
517 #endif
518 
519     if (qApp->applicationDirPath().contains(KRITA_BUILD_DIR)) {
520         qFatal("FATAL: You're trying to run krita from the build location. You can only run Krita from the installation location.");
521     }
522 
523 
524 #if defined HAVE_KCRASH
525     KCrash::initialize();
526 #elif defined USE_DRMINGW
527     tryInitDrMingw();
528 #endif
529 
530     KisApplicationArguments args(app);
531 
532     if (singleApplication && app.isRunning()) {
533         // only pass arguments to main instance if they are not for batch processing
534         // any batch processing would be done in this separate instance
535         const bool batchRun = args.exportAs() || args.exportSequence();
536 
537         if (!batchRun) {
538             QByteArray ba = args.serialize();
539             if (app.sendMessage(ba)) {
540                 return 0;
541             }
542         }
543     }
544 
545     if (!runningInKDE) {
546         // Icons in menus are ugly and distracting
547         app.setAttribute(Qt::AA_DontShowIconsInMenus);
548     }
549 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
550     app.setAttribute(Qt::AA_DisableWindowContextHelpButton);
551 #endif
552     app.installEventFilter(KisQtWidgetsTweaker::instance());
553 
554     if (!args.noSplash()) {
555         // then create the pixmap from an xpm: we cannot get the
556         // location of our datadir before we've started our components,
557         // so use an xpm.
558         QDate currentDate = QDate::currentDate();
559         QWidget *splash = 0;
560         if (currentDate > QDate(currentDate.year(), 12, 4) ||
561                 currentDate < QDate(currentDate.year(), 1, 9)) {
562             splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_holidays_xpm), QPixmap(splash_holidays_x2_xpm));
563         }
564         else {
565             splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_screen_xpm), QPixmap(splash_screen_x2_xpm));
566         }
567 
568         app.setSplashScreen(splash);
569     }
570 
571 #if defined Q_OS_WIN
572     KisConfig cfg(false);
573     bool supportedWindowsVersion = true;
574     QOperatingSystemVersion osVersion = QOperatingSystemVersion::current();
575     if (osVersion.type() == QOperatingSystemVersion::Windows) {
576         if (osVersion.majorVersion() >= QOperatingSystemVersion::Windows7.majorVersion()) {
577             supportedWindowsVersion  = true;
578         }
579         else {
580             supportedWindowsVersion  = false;
581             if (cfg.readEntry("WarnedAboutUnsupportedWindows", false)) {
582                 QMessageBox::information(0,
583                                          i18nc("@title:window", "Krita: Warning"),
584                                          i18n("You are running an unsupported version of Windows: %1.\n"
585                                               "This is not recommended. Do not report any bugs.\n"
586                                               "Please update to a supported version of Windows: Windows 7, 8, 8.1 or 10.", osVersion.name()));
587                 cfg.writeEntry("WarnedAboutUnsupportedWindows", true);
588 
589             }
590         }
591     }
592 #ifndef USE_QT_TABLET_WINDOWS
593     {
594         if (cfg.useWin8PointerInput() && !KisTabletSupportWin8::isAvailable()) {
595             cfg.setUseWin8PointerInput(false);
596         }
597         if (!cfg.useWin8PointerInput()) {
598             bool hasWinTab = KisTabletSupportWin::init();
599             if (!hasWinTab && supportedWindowsVersion) {
600                 if (KisTabletSupportWin8::isPenDeviceAvailable()) {
601                     // Use WinInk automatically
602                     cfg.setUseWin8PointerInput(true);
603                 } else if (!cfg.readEntry("WarnedAboutMissingWinTab", false)) {
604                     if (KisTabletSupportWin8::isAvailable()) {
605                         QMessageBox::information(nullptr,
606                                                  i18n("Krita Tablet Support"),
607                                                  i18n("Cannot load WinTab driver and no Windows Ink pen devices are found. If you have a drawing tablet, please make sure the tablet driver is properly installed."),
608                                                  QMessageBox::Ok, QMessageBox::Ok);
609                     } else {
610                         QMessageBox::information(nullptr,
611                                                  i18n("Krita Tablet Support"),
612                                                  i18n("Cannot load WinTab driver. If you have a drawing tablet, please make sure the tablet driver is properly installed."),
613                                                  QMessageBox::Ok, QMessageBox::Ok);
614                     }
615                     cfg.writeEntry("WarnedAboutMissingWinTab", true);
616                 }
617             }
618         }
619         if (cfg.useWin8PointerInput()) {
620             KisTabletSupportWin8 *penFilter = new KisTabletSupportWin8();
621             if (penFilter->init()) {
622                 // penFilter.registerPointerDeviceNotifications();
623                 app.installNativeEventFilter(penFilter);
624                 dbgKrita << "Using Win8 Pointer Input for tablet support";
625             } else {
626                 dbgKrita << "No Win8 Pointer Input available";
627                 delete penFilter;
628             }
629         }
630     }
631 #elif defined QT_HAS_WINTAB_SWITCH
632 
633     // Check if WinTab/WinInk has actually activated
634     const bool useWinInkAPI = !app.testAttribute(Qt::AA_MSWindowsUseWinTabAPI);
635 
636     if (useWinInkAPI != cfg.useWin8PointerInput()) {
637         KisUsageLogger::log("WARNING: WinTab tablet protocol is not supported on this device. Switching to WinInk...");
638 
639         cfg.setUseWin8PointerInput(useWinInkAPI);
640         cfg.setUseRightMiddleTabletButtonWorkaround(true);
641     }
642 
643 #endif
644 #endif
645     app.setAttribute(Qt::AA_CompressHighFrequencyEvents, false);
646 
647     // Set up remote arguments.
648     QObject::connect(&app, SIGNAL(messageReceived(QByteArray,QObject*)),
649                      &app, SLOT(remoteArguments(QByteArray,QObject*)));
650 
651     QObject::connect(&app, SIGNAL(fileOpenRequest(QString)),
652                      &app, SLOT(fileOpenRequested(QString)));
653 
654     // Hardware information
655     KisUsageLogger::writeSysInfo("\nHardware Information\n");
656     KisUsageLogger::writeSysInfo(QString("  GPU Acceleration: %1").arg(kritarc.value("OpenGLRenderer", "auto").toString()));
657     KisUsageLogger::writeSysInfo(QString("  Memory: %1 Mb").arg(KisImageConfig(true).totalRAM()));
658     KisUsageLogger::writeSysInfo(QString("  Number of Cores: %1").arg(QThread::idealThreadCount()));
659     KisUsageLogger::writeSysInfo(QString("  Swap Location: %1\n").arg(KisImageConfig(true).swapDir()));
660 
661     KisConfig(true).logImportantSettings();
662 
663     if (!app.start(args)) {
664         KisUsageLogger::log("Could not start Krita Application");
665         return 1;
666     }
667 
668 
669     int state = app.exec();
670 
671     {
672         QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
673         kritarc.setValue("canvasState", "OPENGL_SUCCESS");
674     }
675 
676     if (logUsage) {
677         KisUsageLogger::close();
678     }
679 
680     return state;
681 }
682