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