File indexing completed on 2024-04-28 15:40:39

0001 /*
0002 * SPDX-FileCopyrightText: 1999 Matthias Elter <me@kde.org>
0003 * SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
0004 * SPDX-FileCopyrightText: 2015 Boudewijn Rempt <boud@valdyas.org>
0005 *
0006 *  SPDX-License-Identifier: GPL-2.0-or-later
0007 *
0008 *  This program is distributed in the hope that it will be useful,
0009 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0010 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0011 *  GNU General Public License for more details.
0012 *
0013 *  You should have received a copy of the GNU General Public License
0014 *  along with this program; if not, write to the Free Software
0015 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
0016 */
0017 
0018 #include <stdlib.h>
0019 
0020 #include <QString>
0021 #include <QPixmap>
0022 #include <kis_debug.h>
0023 #include <QProcess>
0024 #include <QProcessEnvironment>
0025 #include <QStandardPaths>
0026 #include <QDir>
0027 #include <QDate>
0028 #include <QLocale>
0029 #include <QSettings>
0030 #include <QByteArray>
0031 #include <QMessageBox>
0032 #include <QThread>
0033 #include <QLibraryInfo>
0034 #include <QTranslator>
0035 
0036 #include <QOperatingSystemVersion>
0037 
0038 #include <time.h>
0039 
0040 #include <KisApplication.h>
0041 #include <KoConfig.h>
0042 #include <KoResourcePaths.h>
0043 #include <kis_config.h>
0044 
0045 #include "KisDocument.h"
0046 #include "kis_splash_screen.h"
0047 #include "KisPart.h"
0048 #include "KisApplicationArguments.h"
0049 #include <opengl/kis_opengl.h>
0050 #include "input/KisQtWidgetsTweaker.h"
0051 #include <KisUsageLogger.h>
0052 #include <kis_image_config.h>
0053 #include "KisUiFont.h"
0054 #include <KisMainWindow.h>
0055 
0056 #include <KisSupportedArchitectures.h>
0057 
0058 
0059 
0060 #include <KLocalizedTranslator>
0061 
0062 #ifdef Q_OS_ANDROID
0063 #include <QtAndroid>
0064 #include <KisAndroidCrashHandler.h>
0065 #endif
0066 
0067 #if defined Q_OS_WIN
0068 #include "config_use_qt_tablet_windows.h"
0069 #include <windows.h>
0070 #ifndef USE_QT_TABLET_WINDOWS
0071 #include <kis_tablet_support_win.h>
0072 #include <kis_tablet_support_win8.h>
0073 #else
0074 #include <dialogs/KisDlgCustomTabletResolution.h>
0075 #endif
0076 #include "config-high-dpi-scale-factor-rounding-policy.h"
0077 #include "config-set-has-border-in-full-screen-default.h"
0078 #ifdef HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT
0079 #include <QtPlatformHeaders/QWindowsWindowFunctions>
0080 #endif
0081 #include <QLibrary>
0082 #endif
0083 #if defined HAVE_KCRASH
0084 #include <kcrash.h>
0085 #elif defined USE_DRMINGW
0086 namespace
0087 {
0088 void tryInitDrMingw()
0089 {
0090     wchar_t path[MAX_PATH];
0091     QString pathStr = QCoreApplication::applicationDirPath().replace(L'/', L'\\') + QStringLiteral("\\exchndl.dll");
0092     if (pathStr.size() > MAX_PATH - 1) {
0093         return;
0094     }
0095     int pathLen = pathStr.toWCharArray(path);
0096     path[pathLen] = L'\0'; // toWCharArray doesn't add NULL terminator
0097     HMODULE hMod = LoadLibraryW(path);
0098     if (!hMod) {
0099         return;
0100     }
0101     // No need to call ExcHndlInit since the crash handler is installed on DllMain
0102     auto myExcHndlSetLogFileNameA = reinterpret_cast<BOOL (APIENTRY *)(const char *)>(GetProcAddress(hMod, "ExcHndlSetLogFileNameA"));
0103     if (!myExcHndlSetLogFileNameA) {
0104         return;
0105     }
0106     // Set the log file path to %LocalAppData%\kritacrash.log
0107     QString logFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation).replace(L'/', L'\\') + QStringLiteral("\\kritacrash.log");
0108     myExcHndlSetLogFileNameA(logFile.toLocal8Bit());
0109 }
0110 } // namespace
0111 #endif
0112 
0113 namespace
0114 {
0115 
0116 void installTranslators(KisApplication &app);
0117 
0118 } // namespace
0119 
0120 #ifdef Q_OS_WIN
0121 namespace
0122 {
0123 typedef enum ORIENTATION_PREFERENCE {
0124     ORIENTATION_PREFERENCE_NONE = 0x0,
0125     ORIENTATION_PREFERENCE_LANDSCAPE = 0x1,
0126     ORIENTATION_PREFERENCE_PORTRAIT = 0x2,
0127     ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED = 0x4,
0128     ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED = 0x8
0129 } ORIENTATION_PREFERENCE;
0130 #if !defined(_MSC_VER)
0131     typedef BOOL WINAPI (*pSetDisplayAutoRotationPreferences_t)(
0132             ORIENTATION_PREFERENCE orientation
0133             );
0134 #else
0135     typedef BOOL (WINAPI *pSetDisplayAutoRotationPreferences_t)(
0136         ORIENTATION_PREFERENCE orientation
0137         );
0138 #endif
0139 void resetRotation()
0140 {
0141     QLibrary user32Lib("user32");
0142     if (!user32Lib.load()) {
0143         qWarning() << "Failed to load user32.dll! This really should not happen.";
0144         return;
0145     }
0146     pSetDisplayAutoRotationPreferences_t pSetDisplayAutoRotationPreferences
0147             = reinterpret_cast<pSetDisplayAutoRotationPreferences_t>(user32Lib.resolve("SetDisplayAutoRotationPreferences"));
0148     if (!pSetDisplayAutoRotationPreferences) {
0149         dbgKrita << "Failed to load function SetDisplayAutoRotationPreferences";
0150         return;
0151     }
0152     bool result = pSetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE);
0153     dbgKrita << "SetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE) returned" << result;
0154 }
0155 } // namespace
0156 #endif
0157 
0158 #ifdef Q_OS_ANDROID
0159 extern "C" JNIEXPORT void JNICALL
0160 Java_org_krita_android_JNIWrappers_saveState(JNIEnv* /*env*/,
0161                                              jobject /*obj*/,
0162                                              jint    /*n*/)
0163 {
0164     if (!KisPart::exists()) return;
0165 
0166     KisPart *kisPart = KisPart::instance();
0167     QList<QPointer<KisDocument>> list = kisPart->documents();
0168     for (QPointer<KisDocument> &doc: list)
0169     {
0170         doc->autoSaveOnPause();
0171     }
0172 
0173     const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
0174     QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
0175     kritarc.setValue("canvasState", "OPENGL_SUCCESS");
0176 }
0177 
0178 extern "C" JNIEXPORT jboolean JNICALL
0179 Java_org_krita_android_JNIWrappers_exitFullScreen(JNIEnv* /*env*/,
0180                                                   jobject /*obj*/,
0181                                                   jint    /*n*/)
0182 {
0183     if (!KisPart::exists()) {
0184         return false;
0185     }
0186 
0187     KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow();
0188     if (mainWindow && mainWindow->isFullScreen()) {
0189         // since, this calls KisConfig, we need to make sure it happens on that
0190         // thread (we get here from the Android Main thread)
0191         QMetaObject::invokeMethod(mainWindow, "viewFullscreen",
0192                                   Qt::QueuedConnection, Q_ARG(bool, false));
0193         return true;
0194     } else {
0195         return false;
0196     }
0197 }
0198 
0199 extern "C" JNIEXPORT jboolean JNICALL
0200 Java_org_krita_android_JNIWrappers_hasMainWindowLoaded(JNIEnv * /*env*/,
0201                                                        jobject /*obj*/,
0202                                                        jint /*n*/)
0203 {
0204     if (!KisPart::exists()) {
0205         return false;
0206     }
0207 
0208     KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow();
0209     return (bool)mainWindow;
0210 }
0211 
0212 extern "C" JNIEXPORT void JNICALL
0213 Java_org_krita_android_JNIWrappers_openFileFromIntent(JNIEnv* /*env*/,
0214                                                       jobject /*obj*/,
0215                                                       jstring str)
0216 {
0217     QAndroidJniObject jUri(str);
0218     if (jUri.isValid()) {
0219         QString uri = jUri.toString();
0220         QMetaObject::invokeMethod(KisApplication::instance(), "fileOpenRequested",
0221                                   Qt::QueuedConnection, Q_ARG(QString, uri));
0222     }
0223 }
0224 
0225 #define MAIN_EXPORT __attribute__ ((visibility ("default")))
0226 #define MAIN_FN main
0227 #elif defined Q_OS_WIN
0228 #define MAIN_EXPORT __declspec(dllexport)
0229 #define MAIN_FN krita_main
0230 #else
0231 #define MAIN_EXPORT
0232 #define MAIN_FN main
0233 #endif
0234 
0235 extern "C" MAIN_EXPORT int MAIN_FN(int argc, char **argv)
0236 {
0237 #ifdef Q_OS_WIN
0238     // Fix QCommandLineParser help output with UTF-8 codepage:
0239     if (GetACP() == CP_UTF8) {
0240         SetConsoleOutputCP(CP_UTF8);
0241     }
0242 #endif
0243 
0244     // The global initialization of the random generator
0245     qsrand(time(0));
0246     bool runningInKDE = !qgetenv("KDE_FULL_SESSION").isEmpty();
0247 
0248 #if defined HAVE_X11
0249     qputenv("QT_QPA_PLATFORM", "xcb");
0250 #endif
0251 
0252     // Workaround a bug in QNetworkManager
0253     qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
0254 
0255     // A per-user unique string, without /, because QLocalServer cannot use names with a / in it
0256     QString key = "Krita5" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation).replace("/", "_");
0257     key = key.replace(":", "_").replace("\\","_");
0258 
0259     QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
0260 
0261     QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
0262     QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
0263 
0264     QCoreApplication::setAttribute(Qt::AA_DisableShaderDiskCache, true);
0265 
0266 #ifdef HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY
0267     // This rounding policy depends on a series of patches to Qt related to
0268     // https://bugreports.qt.io/browse/QTBUG-53022. These patches are applied
0269     // in ext_qt for WIndows (patches 0031-0036).
0270     //
0271     // The rounding policy can be set externally by setting the environment
0272     // variable `QT_SCALE_FACTOR_ROUNDING_POLICY` to one of the following:
0273     //   Round:            Round up for .5 and above.
0274     //   Ceil:             Always round up.
0275     //   Floor:            Always round down.
0276     //   RoundPreferFloor: Round up for .75 and above.
0277     //   PassThrough:      Don't round.
0278     //
0279     // The default is set to RoundPreferFloor for better behaviour than before,
0280     // but can be overridden by the above environment variable.
0281     QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor);
0282 #endif
0283 
0284 #ifdef Q_OS_ANDROID
0285     const QString write_permission = "android.permission.WRITE_EXTERNAL_STORAGE";
0286     const QStringList permissions = { write_permission };
0287     const QtAndroid::PermissionResultMap resultHash =
0288             QtAndroid::requestPermissionsSync(QStringList(permissions));
0289 
0290     if (resultHash[write_permission] == QtAndroid::PermissionResult::Denied) {
0291         // TODO: show a dialog and graciously exit
0292         dbgKrita << "Permission denied by the user";
0293     }
0294     else {
0295         dbgKrita << "Permission granted";
0296     }
0297 
0298     KisAndroidCrashHandler::handler_init();
0299 #endif
0300 
0301     const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
0302     QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
0303 
0304     bool enableOpenGLDebug = false;
0305     bool openGLDebugSynchronous = false;
0306     bool logUsage = true;
0307     {
0308         if (kritarc.value("EnableHiDPI", true).toBool()) {
0309             QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
0310         }
0311         if (!qgetenv("KRITA_HIDPI").isEmpty()) {
0312             QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
0313         }
0314 #ifdef HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY
0315         if (kritarc.value("EnableHiDPIFractionalScaling", false).toBool()) {
0316             QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
0317         }
0318 #endif
0319 
0320         if (!qEnvironmentVariableIsEmpty("KRITA_OPENGL_DEBUG")) {
0321             enableOpenGLDebug = true;
0322         } else {
0323             enableOpenGLDebug = kritarc.value("EnableOpenGLDebug", false).toBool();
0324         }
0325         if (enableOpenGLDebug && (qgetenv("KRITA_OPENGL_DEBUG") == "sync" || kritarc.value("OpenGLDebugSynchronous", false).toBool())) {
0326             openGLDebugSynchronous = true;
0327         }
0328 
0329         KisConfig::RootSurfaceFormat rootSurfaceFormat = KisConfig::rootSurfaceFormat(&kritarc);
0330         KisOpenGL::OpenGLRenderer preferredRenderer = KisOpenGL::RendererAuto;
0331 
0332         logUsage = kritarc.value("LogUsage", true).toBool();
0333 
0334 #ifdef Q_OS_WIN
0335         const QString preferredRendererString = kritarc.value("OpenGLRenderer", "angle").toString();
0336 #else
0337         const QString preferredRendererString = kritarc.value("OpenGLRenderer", "auto").toString();
0338 #endif
0339         preferredRenderer = KisOpenGL::convertConfigToOpenGLRenderer(preferredRendererString);
0340 
0341         const KisOpenGL::RendererConfig config =
0342             KisOpenGL::selectSurfaceConfig(preferredRenderer, rootSurfaceFormat, enableOpenGLDebug);
0343 
0344         KisOpenGL::setDefaultSurfaceConfig(config);
0345         KisOpenGL::setDebugSynchronous(openGLDebugSynchronous);
0346 
0347 #ifdef Q_OS_WIN
0348         // HACK: https://bugs.kde.org/show_bug.cgi?id=390651
0349         resetRotation();
0350 #endif
0351     }
0352 
0353     if (logUsage) {
0354         KisUsageLogger::initialize();
0355     }
0356 
0357 
0358     QString root;
0359     QString language;
0360     {
0361         // Create a temporary application to get the root
0362         QCoreApplication app(argc, argv);
0363         Q_UNUSED(app);
0364         root = KoResourcePaths::getApplicationRoot();
0365         QSettings languageoverride(configPath + QStringLiteral("/klanguageoverridesrc"), QSettings::IniFormat);
0366         languageoverride.beginGroup(QStringLiteral("Language"));
0367         language = languageoverride.value(qAppName(), "").toString();
0368     }
0369 
0370 
0371 #ifdef Q_OS_LINUX
0372     {
0373         QByteArray originalXdgDataDirs = qgetenv("XDG_DATA_DIRS");
0374         if (originalXdgDataDirs.isEmpty()) {
0375             // We don't want to completely override the default
0376             originalXdgDataDirs = "/usr/local/share/:/usr/share/";
0377         }
0378         qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share") + ":" + originalXdgDataDirs);
0379 
0380         // APPIMAGE SOUND ADDITIONS
0381         // GStreamer needs a few environment variables to properly function in an appimage context.
0382         // The following code should be configured to **only** run when we detect that Krita is being
0383         // run within an appimage. Checking for the presence of an APPDIR path env variable seems to be
0384         // enough to filter out this step for non-appimage krita builds.
0385 
0386         const bool isInAppimage = qEnvironmentVariableIsSet("APPIMAGE");
0387         if (isInAppimage) {
0388             QByteArray appimageMountDir = qgetenv("APPDIR");
0389 
0390             //We need to add new gstreamer plugin paths for the system to find the
0391             //appropriate plugins.
0392             const QByteArray gstPluginSystemPath = qgetenv("GST_PLUGIN_SYSTEM_PATH_1_0");
0393             const QByteArray gstPluginScannerPath = qgetenv("GST_PLUGIN_SCANNER");
0394 
0395             //Plugins Path is where libgstreamer-1.0 should expect to find plugin libraries.
0396             qputenv("GST_PLUGIN_SYSTEM_PATH_1_0", appimageMountDir + QFile::encodeName("/usr/lib/gstreamer-1.0/") + ":" + gstPluginSystemPath);
0397 
0398             //Plugin scanner is where gstreamer should expect to find the plugin scanner.
0399             //Perhaps invoking the scanenr earlier in the code manually could allow ldd to quickly find all plugin dependencies?
0400             qputenv("GST_PLUGIN_SCANNER", appimageMountDir + QFile::encodeName("/usr/lib/gstreamer-1.0/gst-plugin-scanner"));
0401         }
0402     }
0403 #else
0404     qputenv("XDG_DATA_DIRS", QFile::encodeName(QDir(root + "share").absolutePath()));
0405 #endif
0406 
0407     dbgKrita << "Setting XDG_DATA_DIRS" << qgetenv("XDG_DATA_DIRS");
0408 
0409     // Now that the paths are set, set the language. First check the override from the language
0410     // selection dialog.
0411 
0412     dbgLocale << "Override language:" << language;
0413     bool rightToLeft = false;
0414     if (!language.isEmpty()) {
0415         KLocalizedString::setLanguages(language.split(":"));
0416 
0417         // And override Qt's locale, too
0418         QLocale locale(language.split(":").first());
0419         QLocale::setDefault(locale);
0420 #ifdef Q_OS_MAC
0421         // prevents python >=3.7 nl_langinfo(CODESET) fail bug 417312.
0422         qputenv("LANG", (locale.name() + ".UTF-8").toLocal8Bit());
0423 #else
0424         qputenv("LANG", locale.name().toLocal8Bit());
0425 #endif
0426 
0427         const QStringList rtlLanguages = QStringList()
0428                 << "ar" << "dv" << "he" << "ha" << "ku" << "fa" << "ps" << "ur" << "yi";
0429 
0430         if (rtlLanguages.contains(language.split(':').first())) {
0431             rightToLeft = true;
0432         }
0433     }
0434     else {
0435         dbgLocale << "Qt UI languages:" << QLocale::system().uiLanguages() << qgetenv("LANG");
0436 
0437         // And if there isn't one, check the one set by the system.
0438         QLocale locale = QLocale::system();
0439 
0440 #ifdef Q_OS_ANDROID
0441         // QLocale::uiLanguages() fails on Android, so if the fallback locale is being
0442         // used we, try to fetch the device's default locale.
0443         if (locale.name() == QLocale::c().name()) {
0444             QAndroidJniObject localeJniObj = QAndroidJniObject::callStaticObjectMethod(
0445                 "java/util/Locale", "getDefault", "()Ljava/util/Locale;");
0446 
0447             if (localeJniObj.isValid()) {
0448                 QAndroidJniObject tag = localeJniObj.callObjectMethod("toLanguageTag",
0449                                                                       "()Ljava/lang/String;");
0450                 if (tag.isValid()) {
0451                     locale = QLocale(tag.toString());
0452                 }
0453             }
0454         }
0455 #endif
0456         if (locale.name() != QStringLiteral("en")) {
0457             QStringList uiLanguages = locale.uiLanguages();
0458             for (QString &uiLanguage : uiLanguages) {
0459 
0460                 // This list of language codes that can have a specifier should
0461                 // be extended whenever we have translations that need it; right
0462                 // now, only en, pt, zh are in this situation.
0463 
0464                 if (uiLanguage.startsWith("en") || uiLanguage.startsWith("pt")) {
0465                     uiLanguage.replace(QChar('-'), QChar('_'));
0466                 }
0467                 else if (uiLanguage.startsWith("zh-Hant") || uiLanguage.startsWith("zh-TW")) {
0468                     uiLanguage = "zh_TW";
0469                 }
0470                 else if (uiLanguage.startsWith("zh-Hans") || uiLanguage.startsWith("zh-CN")) {
0471                     uiLanguage = "zh_CN";
0472                 }
0473             }
0474 
0475             if (uiLanguages.size() > 0 ) {
0476                 QString envLanguage = uiLanguages.first();
0477                 envLanguage.replace(QChar('-'), QChar('_'));
0478 
0479                 for (int i = 0; i < uiLanguages.size(); i++) {
0480                     QString uiLanguage = uiLanguages[i];
0481                     // Strip the country code
0482                     int idx = uiLanguage.indexOf(QChar('-'));
0483 
0484                     if (idx != -1) {
0485                         uiLanguage = uiLanguage.left(idx);
0486                         uiLanguages.replace(i, uiLanguage);
0487                     }
0488                 }
0489                 dbgLocale << "Converted ui languages:" << uiLanguages;
0490 #ifdef Q_OS_MAC
0491                 // See https://bugs.kde.org/show_bug.cgi?id=396370
0492                 KLocalizedString::setLanguages(QStringList() << uiLanguages.first());
0493                 qputenv("LANG", (envLanguage + ".UTF-8").toLocal8Bit());
0494 #else
0495                 KLocalizedString::setLanguages(QStringList() << uiLanguages);
0496                 qputenv("LANG", envLanguage.toLocal8Bit());
0497 #endif
0498             }
0499         }
0500     }
0501 
0502     KisUsageLogger::writeLocaleSysInfo();
0503 
0504 #if defined Q_OS_WIN && defined USE_QT_TABLET_WINDOWS && defined QT_HAS_WINTAB_SWITCH
0505     const bool forceWinTab = !KisConfig::useWin8PointerInputNoApp(&kritarc);
0506     QCoreApplication::setAttribute(Qt::AA_MSWindowsUseWinTabAPI, forceWinTab);
0507 
0508     if (qEnvironmentVariableIsEmpty("QT_WINTAB_DESKTOP_RECT") &&
0509         qEnvironmentVariableIsEmpty("QT_IGNORE_WINTAB_MAPPING")) {
0510 
0511         QRect customTabletRect;
0512         KisDlgCustomTabletResolution::Mode tabletMode =
0513             KisDlgCustomTabletResolution::getTabletMode(&customTabletRect);
0514         KisDlgCustomTabletResolution::applyConfiguration(tabletMode, customTabletRect);
0515     }
0516 #endif
0517 
0518     // first create the application so we can create a pixmap
0519     KisApplication app(key, argc, argv);
0520 
0521     installTranslators(app);
0522 
0523     if (app.platformName() == "wayland") {
0524         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."));
0525         return -1;
0526     }
0527 
0528     KisUsageLogger::writeHeader();
0529     KisOpenGL::initialize();
0530 
0531 #ifdef HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT
0532     if (QCoreApplication::testAttribute(Qt::AA_UseDesktopOpenGL)) {
0533         QWindowsWindowFunctions::setHasBorderInFullScreenDefault(true);
0534     }
0535 #endif
0536 
0537 
0538     if (!language.isEmpty()) {
0539         if (rightToLeft) {
0540             app.setLayoutDirection(Qt::RightToLeft);
0541         }
0542         else {
0543             app.setLayoutDirection(Qt::LeftToRight);
0544         }
0545     }
0546 #ifdef Q_OS_ANDROID
0547     KisApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
0548 #endif
0549     // Enable debugging translations from undeployed apps
0550     KLocalizedString::addDomainLocaleDir("krita", QDir(root + "share/locale").absolutePath());
0551 
0552     KLocalizedString::setApplicationDomain("krita");
0553 
0554     dbgLocale << "Available translations" << KLocalizedString::availableApplicationTranslations();
0555     dbgLocale << "Available domain translations" << KLocalizedString::availableDomainTranslations("krita");
0556 
0557 
0558 #ifdef Q_OS_WIN
0559     QDir appdir(KoResourcePaths::getApplicationRoot());
0560     QString path = qgetenv("PATH");
0561     qputenv("PATH", QFile::encodeName(appdir.absolutePath() + "/bin" + ";"
0562                                       + appdir.absolutePath() + "/lib" + ";"
0563                                       + appdir.absolutePath() + "/Frameworks" + ";"
0564                                       + appdir.absolutePath() + ";"
0565                                       + path));
0566 
0567     dbgKrita << "PATH" << qgetenv("PATH");
0568 #endif
0569 
0570     if (qApp->applicationDirPath().contains(KRITA_BUILD_DIR)) {
0571         qFatal("FATAL: You're trying to run krita from the build location. You can only run Krita from the installation location.");
0572     }
0573 
0574 
0575 #if defined HAVE_KCRASH
0576     KCrash::initialize();
0577 #elif defined USE_DRMINGW
0578     tryInitDrMingw();
0579 #endif
0580 
0581     KisApplicationArguments args(app);
0582 
0583     if (app.isRunning()) {
0584         // only pass arguments to main instance if they are not for batch processing
0585         // any batch processing would be done in this separate instance
0586         const bool batchRun = args.exportAs() || args.exportSequence();
0587 
0588         if (!batchRun) {
0589             QByteArray ba = args.serialize();
0590             if (app.sendMessage(ba)) {
0591                 return 0;
0592             }
0593         }
0594     }
0595 
0596     if (!runningInKDE) {
0597         // Icons in menus are ugly and distracting
0598         app.setAttribute(Qt::AA_DontShowIconsInMenus);
0599     }
0600 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
0601     app.setAttribute(Qt::AA_DisableWindowContextHelpButton);
0602 #endif
0603     app.installEventFilter(KisQtWidgetsTweaker::instance());
0604 
0605     if (!args.noSplash()) {
0606         QWidget *splash = new KisSplashScreen();
0607         app.setSplashScreen(splash);
0608     }
0609 
0610 #if defined Q_OS_WIN
0611     KisConfig cfg(false);
0612     bool supportedWindowsVersion = true;
0613     QOperatingSystemVersion osVersion = QOperatingSystemVersion::current();
0614     if (osVersion.type() == QOperatingSystemVersion::Windows) {
0615         if (osVersion.majorVersion() >= QOperatingSystemVersion::Windows7.majorVersion()) {
0616             supportedWindowsVersion  = true;
0617         }
0618         else {
0619             supportedWindowsVersion  = false;
0620             if (cfg.readEntry("WarnedAboutUnsupportedWindows", false)) {
0621                 QMessageBox::information(nullptr,
0622                                          i18nc("@title:window", "Krita: Warning"),
0623                                          i18n("You are running an unsupported version of Windows: %1.\n"
0624                                               "This is not recommended. Do not report any bugs.\n"
0625                                               "Please update to a supported version of Windows: Windows 7, 8, 8.1 or 10.", osVersion.name()));
0626                 cfg.writeEntry("WarnedAboutUnsupportedWindows", true);
0627 
0628             }
0629         }
0630     }
0631 #ifndef USE_QT_TABLET_WINDOWS
0632     {
0633         if (cfg.useWin8PointerInput() && !KisTabletSupportWin8::isAvailable()) {
0634             cfg.setUseWin8PointerInput(false);
0635         }
0636         if (!cfg.useWin8PointerInput()) {
0637             bool hasWinTab = KisTabletSupportWin::init();
0638             if (!hasWinTab && supportedWindowsVersion) {
0639                 if (KisTabletSupportWin8::isPenDeviceAvailable()) {
0640                     // Use WinInk automatically
0641                     cfg.setUseWin8PointerInput(true);
0642                 } else if (!cfg.readEntry("WarnedAboutMissingWinTab", false)) {
0643                     if (KisTabletSupportWin8::isAvailable()) {
0644                         QMessageBox::information(nullptr,
0645                                                  i18n("Krita Tablet Support"),
0646                                                  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."),
0647                                                  QMessageBox::Ok, QMessageBox::Ok);
0648                     } else {
0649                         QMessageBox::information(nullptr,
0650                                                  i18n("Krita Tablet Support"),
0651                                                  i18n("Cannot load WinTab driver. If you have a drawing tablet, please make sure the tablet driver is properly installed."),
0652                                                  QMessageBox::Ok, QMessageBox::Ok);
0653                     }
0654                     cfg.writeEntry("WarnedAboutMissingWinTab", true);
0655                 }
0656             }
0657         }
0658         if (cfg.useWin8PointerInput()) {
0659             KisTabletSupportWin8 *penFilter = new KisTabletSupportWin8();
0660             if (penFilter->init()) {
0661                 // penFilter.registerPointerDeviceNotifications();
0662                 app.installNativeEventFilter(penFilter);
0663                 dbgKrita << "Using Win8 Pointer Input for tablet support";
0664             } else {
0665                 dbgKrita << "No Win8 Pointer Input available";
0666                 delete penFilter;
0667             }
0668         }
0669     }
0670 #elif defined QT_HAS_WINTAB_SWITCH
0671     Q_UNUSED(supportedWindowsVersion);
0672 
0673     // Check if WinTab/WinInk has actually activated
0674     const bool useWinInkAPI = !app.testAttribute(Qt::AA_MSWindowsUseWinTabAPI);
0675 
0676     if (useWinInkAPI != cfg.useWin8PointerInput()) {
0677         KisUsageLogger::log("WARNING: WinTab tablet protocol is not supported on this device. Switching to WinInk...");
0678 
0679         cfg.setUseWin8PointerInput(useWinInkAPI);
0680         cfg.setUseRightMiddleTabletButtonWorkaround(true);
0681     }
0682 
0683 #endif
0684 #endif
0685     app.setAttribute(Qt::AA_CompressHighFrequencyEvents, false);
0686 
0687     // Set up remote arguments.
0688     QObject::connect(&app, SIGNAL(messageReceived(QByteArray,QObject*)),
0689                      &app, SLOT(remoteArguments(QByteArray,QObject*)));
0690 
0691     QObject::connect(&app, SIGNAL(fileOpenRequest(QString)),
0692                      &app, SLOT(fileOpenRequested(QString)));
0693 
0694     // Hardware information
0695     KisUsageLogger::writeSysInfo("\nHardware Information\n");
0696     KisUsageLogger::writeSysInfo(QString("  GPU Acceleration: %1").arg(kritarc.value("OpenGLRenderer", "auto").toString()));
0697     KisUsageLogger::writeSysInfo(QString("  Memory: %1 Mb").arg(KisImageConfig(true).totalRAM()));
0698     KisUsageLogger::writeSysInfo(QString("  Number of Cores: %1").arg(QThread::idealThreadCount()));
0699     KisUsageLogger::writeSysInfo(QString("  Swap Location: %1").arg(KisImageConfig(true).swapDir()));
0700     KisUsageLogger::writeSysInfo(QString("  Built for: %1").arg(xsimd::current_arch::name()));
0701     KisUsageLogger::writeSysInfo(QString("  Base instruction set: %1").arg(KisSupportedArchitectures<QString>::currentArchitecture()));
0702     KisUsageLogger::writeSysInfo(QString("  Supported instruction sets: %1").arg(KisSupportedArchitectures<QString>::supportedInstructionSets()));
0703 
0704     KisUsageLogger::writeSysInfo("");
0705 
0706     KisConfig(true).logImportantSettings();
0707 
0708     app.setFont(KisUiFont::normalFont());
0709 
0710     if (!app.start(args)) {
0711         KisUsageLogger::log("Could not start Krita Application");
0712         return 1;
0713     }
0714 
0715 
0716     int state = app.exec();
0717 
0718     {
0719         QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
0720         kritarc.setValue("canvasState", "OPENGL_SUCCESS");
0721     }
0722 
0723     if (logUsage) {
0724         KisUsageLogger::close();
0725     }
0726 
0727     return state;
0728 }
0729 
0730 namespace
0731 {
0732 
0733 void removeInstalledTranslators(KisApplication &app)
0734 {
0735     // HACK: We try to remove all the translators installed by ECMQmLoader.
0736     // The reason is that it always load translations for the system locale
0737     // which interferes with our effort to handle override languages. Since
0738     // `en_US` (or `en`) strings are defined in code, the QTranslator doesn't
0739     // actually handle translations for them, so even if we try to install
0740     // a QTranslator loaded from `en`, the strings always get translated by
0741     // the system language QTranslator that ECMQmLoader installed instead
0742     // of the English one.
0743 
0744     // ECMQmLoader creates all QTranslator's parented to the active QApp.
0745     QList<QTranslator *> translators = app.findChildren<QTranslator *>(QString(), Qt::FindDirectChildrenOnly);
0746     Q_FOREACH(const auto &translator, translators) {
0747         app.removeTranslator(translator);
0748     }
0749     dbgLocale << "Removed" << translators.size() << "QTranslator's";
0750 }
0751 
0752 void installPythonPluginUITranslator(KisApplication &app)
0753 {
0754     // Install a KLocalizedTranslator, so that when the bundled Python plugins
0755     // load their UI files using uic.loadUi() it can be translated.
0756     // These UI files must specify "pykrita_plugin_ui" as their class names.
0757     KLocalizedTranslator *translator = new KLocalizedTranslator(&app);
0758     translator->setObjectName(QStringLiteral("KLocalizedTranslator.pykrita_plugin_ui"));
0759     translator->setTranslationDomain(QStringLiteral("krita"));
0760     translator->addContextToMonitor(QStringLiteral("pykrita_plugin_ui"));
0761     app.installTranslator(translator);
0762 }
0763 
0764 void installQtTranslations(KisApplication &app)
0765 {
0766     QStringList qtCatalogs = {
0767         QStringLiteral("qt_"),
0768         QStringLiteral("qtbase_"),
0769         QStringLiteral("qtmultimedia_"),
0770         QStringLiteral("qtdeclarative_"),
0771     };
0772     // A list of locale to add, note that the last added one has the
0773     // highest precedence.
0774     QList<QLocale> localeList;
0775     // We always use English as the final fallback.
0776     localeList.append(QLocale(QLocale::English));
0777     QLocale defaultLocale;
0778     if (defaultLocale.language() != QLocale::English) {
0779         localeList.append(defaultLocale);
0780     }
0781 
0782     QString translationsPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
0783     dbgLocale << "Qt translations path:" << translationsPath;
0784 
0785     Q_FOREACH(const auto &localeToLoad, localeList) {
0786         Q_FOREACH(const auto &catalog, qtCatalogs) {
0787             QTranslator *translator = new QTranslator(&app);
0788             if (translator->load(localeToLoad, catalog, QString(), translationsPath)) {
0789                 dbgLocale << "Loaded Qt translations for" << localeToLoad << catalog;
0790                 translator->setObjectName(QStringLiteral("QTranslator.%1.%2").arg(localeToLoad.name(), catalog));
0791                 app.installTranslator(translator);
0792             } else {
0793                 delete translator;
0794             }
0795         }
0796     }
0797 }
0798 
0799 void installEcmTranslations(KisApplication &app)
0800 {
0801     // Load translations created using the ECMPoQmTools module.
0802     // This function is based on the code in:
0803     // https://invent.kde.org/frameworks/extra-cmake-modules/-/blob/master/modules/ECMQmLoader.cpp.in
0804 
0805     QStringList ecmCatalogs = {
0806         QStringLiteral("kcompletion5_qt"),
0807         QStringLiteral("kconfig5_qt"),
0808         QStringLiteral("kcoreaddons5_qt"),
0809         QStringLiteral("kitemviews5_qt"),
0810         QStringLiteral("kwidgetsaddons5_qt"),
0811         QStringLiteral("kwindowsystem5_qt"),
0812         QStringLiteral("seexpr2_qt"),
0813     };
0814 
0815     QStringList ki18nLangs = KLocalizedString::languages();
0816     const QString langEn = QStringLiteral("en");
0817     // Replace "en_US" with "en" because that's what we have in the locale dir.
0818     int indexOfEnUs = ki18nLangs.indexOf(QStringLiteral("en_US"));
0819     if (indexOfEnUs != -1) {
0820         ki18nLangs[indexOfEnUs] = langEn;
0821     }
0822     // We need to have "en" to the end of the list, because we explicitly
0823     // removed the "en" translators added by ECMQmLoader.
0824     // If "en" is already on the list, we truncate the ones after, because
0825     // "en" is the catch-all fallback that has the strings in code.
0826     int indexOfEn = ki18nLangs.indexOf(langEn);
0827     if (indexOfEn != -1) {
0828         for (int i = ki18nLangs.size() - indexOfEn - 1; i > 0; i--) {
0829             ki18nLangs.removeLast();
0830         }
0831     } else {
0832         ki18nLangs.append(langEn);
0833     }
0834 
0835     // The last added one has the highest precedence, so we iterate the
0836     // list backwards.
0837     QStringListIterator langIter(ki18nLangs);
0838     langIter.toBack();
0839 
0840     while (langIter.hasPrevious()) {
0841         const QString &localeDirName = langIter.previous();
0842         Q_FOREACH(const auto &catalog, ecmCatalogs) {
0843             QString subPath = QStringLiteral("locale/") % localeDirName % QStringLiteral("/LC_MESSAGES/") % catalog % QStringLiteral(".qm");
0844 #if defined(Q_OS_ANDROID)
0845             const QString fullPath = QStringLiteral("assets:/") + subPath;
0846 #else
0847             const QString root = QLibraryInfo::location(QLibraryInfo::PrefixPath);
0848 
0849             // Our patched k18n uses AppDataLocation (for AppImage). Not using
0850             // KoResourcePaths::getAppDataLocation is correct here, because we
0851             // need to look into the installation folder, not the configured appdata
0852             // folder.
0853             QString fullPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, subPath);
0854 
0855             if (fullPath.isEmpty()) {
0856                 // ... but distro builds probably still use GenericDataLocation,
0857                 // so check that too.
0858                 fullPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, subPath);
0859             }
0860 
0861             if (fullPath.isEmpty()) {
0862                 // And, failing all, use the deps install folder
0863                 fullPath = root + "/share/" + subPath;
0864             }
0865 #endif
0866             if (!QFile::exists(fullPath)) {
0867                 continue;
0868             }
0869 
0870             QTranslator *translator = new QTranslator(&app);
0871             if (translator->load(fullPath)) {
0872                 dbgLocale << "Loaded ECM translations for" << localeDirName << catalog;
0873                 translator->setObjectName(QStringLiteral("QTranslator.%1.%2").arg(localeDirName, catalog));
0874                 app.installTranslator(translator);
0875             } else {
0876                 delete translator;
0877             }
0878         }
0879     }
0880 }
0881 
0882 void installTranslators(KisApplication &app)
0883 {
0884     removeInstalledTranslators(app);
0885     installPythonPluginUITranslator(app);
0886     installQtTranslations(app);
0887     installEcmTranslations(app);
0888 }
0889 
0890 } // namespace