File indexing completed on 2025-01-05 03:51:16
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2009-09-08 0007 * Description : global macros, variables and flags - Bundle functions. 0008 * 0009 * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "digikam_globals_p.h" 0016 0017 namespace Digikam 0018 { 0019 0020 bool isRunningInAppImageBundle() 0021 { 0022 QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); 0023 0024 if (env.contains(QLatin1String("APPIMAGE_ORIGINAL_LD_LIBRARY_PATH")) && 0025 env.contains(QLatin1String("APPIMAGE_ORIGINAL_QT_PLUGIN_PATH")) && 0026 env.contains(QLatin1String("APPIMAGE_ORIGINAL_XDG_DATA_DIRS")) && 0027 env.contains(QLatin1String("APPIMAGE_ORIGINAL_PATH"))) 0028 { 0029 return true; 0030 } 0031 0032 return false; 0033 } 0034 0035 QProcessEnvironment adjustedEnvironmentForAppImage() 0036 { 0037 QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); 0038 0039 // If we are running into AppImage bundle, switch env var to the right values. 0040 0041 if (isRunningInAppImageBundle()) 0042 { 0043 qCDebug(DIGIKAM_GENERAL_LOG) << "Adjusting environment variables for AppImage bundle"; 0044 0045 if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_LD_LIBRARY_PATH")).isEmpty()) 0046 { 0047 env.insert(QLatin1String("LD_LIBRARY_PATH"), 0048 env.value(QLatin1String("APPIMAGE_ORIGINAL_LD_LIBRARY_PATH"))); 0049 } 0050 else 0051 { 0052 env.remove(QLatin1String("LD_LIBRARY_PATH")); 0053 } 0054 0055 if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_QT_PLUGIN_PATH")).isEmpty()) 0056 { 0057 env.insert(QLatin1String("QT_PLUGIN_PATH"), 0058 env.value(QLatin1String("APPIMAGE_ORIGINAL_QT_PLUGIN_PATH"))); 0059 } 0060 else 0061 { 0062 env.remove(QLatin1String("QT_PLUGIN_PATH")); 0063 } 0064 0065 if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_DATA_DIRS")).isEmpty()) 0066 { 0067 env.insert(QLatin1String("XDG_DATA_DIRS"), 0068 env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_DATA_DIRS"))); 0069 } 0070 else 0071 { 0072 env.remove(QLatin1String("XDG_DATA_DIRS")); 0073 } 0074 0075 if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_LD_PRELOAD")).isEmpty()) 0076 { 0077 env.insert(QLatin1String("LD_PRELOAD"), 0078 env.value(QLatin1String("APPIMAGE_ORIGINAL_LD_PRELOAD"))); 0079 } 0080 else 0081 { 0082 env.remove(QLatin1String("LD_PRELOAD")); 0083 } 0084 0085 if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_PATH")).isEmpty()) 0086 { 0087 env.insert(QLatin1String("PATH"), 0088 env.value(QLatin1String("APPIMAGE_ORIGINAL_PATH"))); 0089 } 0090 else 0091 { 0092 env.remove(QLatin1String("PATH")); 0093 } 0094 0095 if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_KDE_FULL_SESSION")).isEmpty()) 0096 { 0097 env.insert(QLatin1String("KDE_FULL_SESSION"), 0098 env.value(QLatin1String("APPIMAGE_ORIGINAL_KDE_FULL_SESSION"))); 0099 } 0100 else 0101 { 0102 env.remove(QLatin1String("KDE_FULL_SESSION")); 0103 } 0104 0105 if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_DESKTOP_SESSION")).isEmpty()) 0106 { 0107 env.insert(QLatin1String("DESKTOP_SESSION"), 0108 env.value(QLatin1String("APPIMAGE_ORIGINAL_DESKTOP_SESSION"))); 0109 } 0110 else 0111 { 0112 env.remove(QLatin1String("DESKTOP_SESSION")); 0113 } 0114 0115 if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_CURRENT_DESKTOP")).isEmpty()) 0116 { 0117 env.insert(QLatin1String("XDG_CURRENT_DESKTOP"), 0118 env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_CURRENT_DESKTOP"))); 0119 } 0120 else 0121 { 0122 env.remove(QLatin1String("XDG_CURRENT_DESKTOP")); 0123 } 0124 0125 if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_SESSION_DESKTOP")).isEmpty()) 0126 { 0127 env.insert(QLatin1String("XDG_SESSION_DESKTOP"), 0128 env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_SESSION_DESKTOP"))); 0129 } 0130 else 0131 { 0132 env.remove(QLatin1String("XDG_SESSION_DESKTOP")); 0133 } 0134 } 0135 0136 return env; 0137 } 0138 0139 void tryInitDrMingw() 0140 { 0141 0142 #ifdef HAVE_DRMINGW 0143 0144 // Envirronnement variable under Windows to disable DrMinGw 0145 0146 QByteArray drmingwEnv = qgetenv("DK_DISABLE_DRMINGW"); 0147 0148 if (drmingwEnv.isEmpty()) 0149 { 0150 0151 qCDebug(DIGIKAM_GENERAL_LOG) << "Loading DrMinGw run-time..."; 0152 /* 0153 // Windows version check for DrMinGW 0.9.2. It's not necessary with new DrMinGW 0.9.4 version. 0154 0155 QRegExp versionRegExp(QLatin1String("(\\d+[.]*\\d*)")); 0156 QSysInfo::productVersion().indexOf(versionRegExp); 0157 double version = versionRegExp.capturedTexts().constFirst().toDouble(); 0158 0159 if ( 0160 ((version < 2000.0) && (version < 10.0)) || 0161 ((version > 2000.0) && (version < 2016.0)) 0162 ) 0163 { 0164 qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw: unsupported Windows version" << version; 0165 0166 return; 0167 } 0168 */ 0169 QString appPath = QCoreApplication::applicationDirPath(); 0170 QString excFile = QDir::toNativeSeparators(appPath + QLatin1String("/exchndl.dll")); 0171 0172 HMODULE hModExc = LoadLibraryW((LPCWSTR)excFile.utf16()); 0173 0174 if (!hModExc) 0175 { 0176 qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw: cannot init crash handler dll."; 0177 0178 return; 0179 } 0180 0181 // No need to call ExcHndlInit since the crash handler is installed on DllMain 0182 0183 auto myExcHndlSetLogFileNameA = reinterpret_cast<BOOL (APIENTRY*)(const char*)> 0184 (GetProcAddress(hModExc, "ExcHndlSetLogFileNameA")); 0185 0186 if (!myExcHndlSetLogFileNameA) 0187 { 0188 qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw: cannot init customized crash file."; 0189 0190 return; 0191 } 0192 0193 // Set the log file path to %LocalAppData%\digikam_crash.log 0194 0195 QString logPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); 0196 QString logFile = QDir::toNativeSeparators(logPath + QLatin1String("/digikam_crash.log")); 0197 myExcHndlSetLogFileNameA(logFile.toLocal8Bit().data()); 0198 0199 qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw run-time loaded."; 0200 qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw crash-file will be located at: " << logFile; 0201 } 0202 0203 #endif // HAVE_DRMINGW 0204 0205 } 0206 0207 QString macOSBundlePrefix() 0208 { 0209 return QString::fromUtf8("/Applications/digiKam.org/digikam.app/Contents/"); 0210 } 0211 0212 void unloadQtTranslationFiles(QApplication& app) 0213 { 0214 // HACK: We try to remove all the translators installed by ECMQmLoader. 0215 // The reason is that it always load translations for the system locale 0216 // which interferes with our effort to handle override languages. Since 0217 // `en_US` (or `en`) strings are defined in code, the QTranslator doesn't 0218 // actually handle translations for them, so even if we try to install 0219 // a QTranslator loaded from `en`, the strings always get translated by 0220 // the system language QTranslator that ECMQmLoader installed instead 0221 // of the English one. 0222 0223 // ECMQmLoader creates all QTranslator's parented to the active QApplication instance. 0224 0225 QList<QTranslator*> translators = app.findChildren<QTranslator*>(QString(), Qt::FindDirectChildrenOnly); 0226 0227 Q_FOREACH (const auto& translator, translators) 0228 { 0229 app.removeTranslator(translator); 0230 } 0231 0232 qCDebug(DIGIKAM_GENERAL_LOG) << "Qt standard translations removed:" << translators.size(); 0233 } 0234 0235 void loadStdQtTranslationFiles(QApplication& app) 0236 { 0237 QString transPath = QStandardPaths::locate(QStandardPaths::AppLocalDataLocation, 0238 QLatin1String("translations"), 0239 QStandardPaths::LocateDirectory); 0240 0241 if (!transPath.isEmpty()) 0242 { 0243 QString languagePath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + 0244 QLatin1Char('/') + 0245 QLatin1String("klanguageoverridesrc"); 0246 0247 qCDebug(DIGIKAM_GENERAL_LOG) << "Qt standard translations path:" << transPath; 0248 0249 QLocale locale; 0250 0251 if (!languagePath.isEmpty()) 0252 { 0253 QSettings settings(languagePath, QSettings::IniFormat); 0254 settings.beginGroup(QLatin1String("Language")); 0255 QString language = settings.value(qApp->applicationName(), QString()).toString(); 0256 settings.endGroup(); 0257 0258 if (!language.isEmpty()) 0259 { 0260 QString languageName = language.split(QLatin1Char(':')).first(); 0261 0262 qCDebug(DIGIKAM_GENERAL_LOG) << "Language set to:" << languageName; 0263 0264 locale = QLocale(languageName); 0265 } 0266 } 0267 0268 const QStringList qtCatalogs = 0269 { 0270 QLatin1String("qt"), 0271 QLatin1String("qtbase"), 0272 QLatin1String("qt_help"), 0273 QLatin1String("qtdeclarative"), 0274 QLatin1String("qtquickcontrols"), 0275 QLatin1String("qtquickcontrols2"), 0276 QLatin1String("qtmultimedia"), 0277 0278 #ifdef HAVE_QWEBENGINE 0279 0280 QLatin1String("qtwebengine"), 0281 0282 #endif 0283 0284 #ifdef HAVE_QTXMLPATTERNS 0285 0286 QLatin1String("qtxmlpatterns"), 0287 0288 #endif 0289 0290 }; 0291 0292 Q_FOREACH (const QString& catalog, qtCatalogs) 0293 { 0294 QTranslator* const translator = new QTranslator(&app); 0295 0296 if (translator->load(locale, catalog, QLatin1String("_"), transPath)) 0297 { 0298 qCDebug(DIGIKAM_GENERAL_LOG) << "Loaded Qt standard translations" 0299 << locale.name() 0300 << "from catalog" 0301 << catalog; 0302 0303 app.installTranslator(translator); 0304 } 0305 else 0306 { 0307 delete translator; 0308 } 0309 } 0310 } 0311 } 0312 0313 void loadEcmQtTranslationFiles(QApplication& app) 0314 { 0315 // Load translations created by the ECMPoQmTools module. 0316 // This function is based on the code in: 0317 // https://invent.kde.org/frameworks/extra-cmake-modules/-/blob/master/modules/ECMQmLoader.cpp.in 0318 0319 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0320 0321 const QStringList ecmCatalogs = 0322 { 0323 // QLatin1String("kauth6_qt"), Do not exists. 0324 QLatin1String("kbookmarks6_qt"), 0325 QLatin1String("kcodecs6_qt"), 0326 QLatin1String("kcompletion6_qt"), 0327 QLatin1String("kconfig6_qt"), 0328 QLatin1String("kcoreaddons6_qt"), 0329 QLatin1String("kdbusaddons6_qt"), 0330 // QLatin1String("kde6_xml_mimetypes"), Do not exists. 0331 // QLatin1String("kglobalaccel6_qt"), Do not exists. 0332 QLatin1String("kitemviews6_qt"), 0333 QLatin1String("kwidgetsaddons6_qt"), 0334 QLatin1String("kwindowsystem6_qt"), 0335 QLatin1String("solid6_qt"), 0336 0337 #else 0338 0339 const QStringList ecmCatalogs = 0340 { 0341 QLatin1String("kauth5_qt"), 0342 QLatin1String("kbookmarks5_qt"), 0343 QLatin1String("kcodecs5_qt"), 0344 QLatin1String("kcompletion5_qt"), 0345 QLatin1String("kconfig5_qt"), 0346 QLatin1String("kcoreaddons5_qt"), 0347 QLatin1String("kdbusaddons5_qt"), 0348 QLatin1String("kde5_xml_mimetypes"), 0349 QLatin1String("kglobalaccel5_qt"), 0350 QLatin1String("kitemviews5_qt"), 0351 QLatin1String("kwidgetsaddons5_qt"), 0352 QLatin1String("kwindowsystem5_qt"), 0353 QLatin1String("solid5_qt"), 0354 0355 #endif 0356 0357 }; 0358 0359 QStringList ecmLangs = KLocalizedString::languages(); 0360 const QString langEn = QLatin1String("en"); 0361 0362 // Replace "en_US" with "en" because that's what we have in the locale dir. 0363 0364 int indexOfEnUs = ecmLangs.indexOf(QLatin1String("en_US")); 0365 0366 if (indexOfEnUs != -1) 0367 { 0368 ecmLangs[indexOfEnUs] = langEn; 0369 } 0370 0371 // We need to have "en" to the end of the list, because we explicitly 0372 // removed the "en" translators added by ECMQmLoader. 0373 // If "en" is already on the list, we truncate the ones after, because 0374 // "en" is the catch-all fallback that has the strings in code. 0375 0376 int indexOfEn = ecmLangs.indexOf(langEn); 0377 0378 if (indexOfEn != -1) 0379 { 0380 for (int i = (ecmLangs.size() - indexOfEn - 1) ; i > 0 ; i--) 0381 { 0382 ecmLangs.removeLast(); 0383 } 0384 } 0385 else 0386 { 0387 ecmLangs.append(langEn); 0388 } 0389 0390 // The last added one has the highest precedence, so we iterate the list backwards. 0391 0392 QStringListIterator it(ecmLangs); 0393 it.toBack(); 0394 0395 while (it.hasPrevious()) 0396 { 0397 const QString& localeDirName = it.previous(); 0398 0399 Q_FOREACH (const auto& catalog, ecmCatalogs) 0400 { 0401 QString subPath = QLatin1String("locale/") + 0402 localeDirName + 0403 QLatin1String("/LC_MESSAGES/") + 0404 catalog + 0405 QLatin1String(".qm"); 0406 0407 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0408 0409 const QString root = QLibraryInfo::path(QLibraryInfo::PrefixPath); 0410 0411 #else 0412 0413 const QString root = QLibraryInfo::location(QLibraryInfo::PrefixPath); 0414 0415 #endif 0416 0417 // For AppImage transalotion files uses AppDataLocation. 0418 0419 QString fullPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, subPath); 0420 0421 if (fullPath.isEmpty()) 0422 { 0423 // For distro builds probably still use GenericDataLocation, so check that too. 0424 0425 fullPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, subPath); 0426 } 0427 0428 if (fullPath.isEmpty()) 0429 { 0430 // And, failing all, use the deps install folder 0431 0432 fullPath = root + QLatin1String("/share/") + subPath; 0433 } 0434 0435 if (!QFile::exists(fullPath)) 0436 { 0437 continue; 0438 } 0439 0440 QTranslator* const translator = new QTranslator(&app); 0441 0442 if (translator->load(fullPath)) 0443 { 0444 qCDebug(DIGIKAM_GENERAL_LOG) << "Loaded Qt ECM translations" 0445 << localeDirName 0446 << "from catalog" 0447 << catalog; 0448 0449 translator->setObjectName(QString::fromUtf8("QTranslator.%1.%2").arg(localeDirName, catalog)); 0450 app.installTranslator(translator); 0451 } 0452 else 0453 { 0454 delete translator; 0455 } 0456 } 0457 } 0458 } 0459 0460 void installQtTranslationFiles(QApplication& app) 0461 { 0462 0463 #if defined Q_OS_WIN || defined Q_OS_MACOS 0464 0465 bool installTranslations = true; 0466 0467 #else 0468 0469 bool installTranslations = isRunningInAppImageBundle(); 0470 0471 #endif 0472 0473 if (installTranslations) 0474 { 0475 unloadQtTranslationFiles(app); 0476 loadStdQtTranslationFiles(app); 0477 loadEcmQtTranslationFiles(app); 0478 } 0479 } 0480 0481 void setupKSycocaDatabaseFile() 0482 { 0483 if (isRunningInAppImageBundle()) 0484 { 0485 QString cachePath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation); 0486 0487 if (!cachePath.isEmpty()) 0488 { 0489 QDir dir(cachePath); 0490 QStringList searchList({ QLatin1String("ksycoca*") }); 0491 0492 QFileInfoList infoList = dir.entryInfoList(searchList, QDir::Files); 0493 0494 while (!infoList.isEmpty()) 0495 { 0496 QFileInfo info = infoList.takeFirst(); 0497 0498 if (info.suffix() == QLatin1String("lock")) 0499 { 0500 continue; 0501 } 0502 0503 if ((info.fileName() != QLatin1String("ksycoca5_appimage")) && 0504 (info.fileName() != QLatin1String("ksycoca6_appimage"))) 0505 { 0506 return; 0507 } 0508 } 0509 0510 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0511 0512 QString ksycoca = cachePath + QLatin1String("/ksycoca6_appimage"); 0513 0514 #else 0515 0516 QString ksycoca = cachePath + QLatin1String("/ksycoca5_appimage"); 0517 0518 #endif 0519 0520 qputenv("KDESYCOCA", ksycoca.toUtf8()); 0521 0522 qCDebug(DIGIKAM_GENERAL_LOG) << "AppImage KSycoca file:" << ksycoca; 0523 } 0524 } 0525 } 0526 0527 } // namespace Digikam