File indexing completed on 2024-12-08 13:22:28
0001 /* 0002 * Copyright 2016 Smith AR <audoban@openmailbox.org> 0003 * Michail Vourlakos <mvourlakos@gmail.com> 0004 * 0005 * This file is part of Latte-Dock 0006 * 0007 * Latte-Dock is free software; you can redistribute it and/or 0008 * modify it under the terms of the GNU General Public License as 0009 * published by the Free Software Foundation; either version 2 of 0010 * the License, or (at your option) any later version. 0011 * 0012 * Latte-Dock is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0015 * GNU General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU General Public License 0018 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0019 */ 0020 0021 // local 0022 #include "config-latte.h" 0023 #include "lattecorona.h" 0024 #include "layouts/importer.h" 0025 #include "../liblatte2/types.h" 0026 0027 // C++ 0028 #include <memory> 0029 #include <csignal> 0030 0031 // Qt 0032 #include <QApplication> 0033 #include <QDebug> 0034 #include <QQuickWindow> 0035 #include <QCommandLineParser> 0036 #include <QCommandLineOption> 0037 #include <QDir> 0038 #include <QLockFile> 0039 #include <QSharedMemory> 0040 0041 // KDE 0042 #include <KCrash> 0043 #include <KLocalizedString> 0044 #include <KAboutData> 0045 #include <KDBusService> 0046 #include <KQuickAddons/QtQuickSettings> 0047 0048 //! COLORS 0049 #define CNORMAL "\e[0m" 0050 #define CIGREEN "\e[1;32m" 0051 #define CGREEN "\e[0;32m" 0052 #define CICYAN "\e[1;36m" 0053 #define CCYAN "\e[0;36m" 0054 #define CIRED "\e[1;31m" 0055 #define CRED "\e[0;31m" 0056 0057 inline void configureAboutData(); 0058 inline void detectPlatform(int argc, char **argv); 0059 0060 int main(int argc, char **argv) 0061 { 0062 //Plasma scales itself to font DPI 0063 //on X, where we don't have compositor scaling, this generally works fine. 0064 //also there are bugs on older Qt, especially when it comes to fractional scaling 0065 //there's advantages to disabling, and (other than small context menu icons) few advantages in enabling 0066 0067 //On wayland, it's different. Everything is simpler as all co-ordinates are in the same co-ordinate system 0068 //we don't have fractional scaling on the client so don't hit most the remaining bugs and 0069 //even if we don't use Qt scaling the compositor will try to scale us anyway so we have no choice 0070 if (!qEnvironmentVariableIsSet("PLASMA_USE_QT_SCALING")) { 0071 qunsetenv("QT_DEVICE_PIXEL_RATIO"); 0072 QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); 0073 } else { 0074 QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); 0075 } 0076 0077 QQuickWindow::setDefaultAlphaBuffer(true); 0078 0079 const bool qpaVariable = qEnvironmentVariableIsSet("QT_QPA_PLATFORM"); 0080 detectPlatform(argc, argv); 0081 QApplication app(argc, argv); 0082 0083 if (!qpaVariable) { 0084 // don't leak the env variable to processes we start 0085 qunsetenv("QT_QPA_PLATFORM"); 0086 } 0087 0088 KQuickAddons::QtQuickSettings::init(); 0089 0090 KLocalizedString::setApplicationDomain("latte-dock"); 0091 app.setWindowIcon(QIcon::fromTheme(QStringLiteral("latte-dock"))); 0092 //protect from closing app when changing to "alternative session" and back 0093 app.setQuitOnLastWindowClosed(false); 0094 0095 configureAboutData(); 0096 0097 QCommandLineParser parser; 0098 parser.addHelpOption(); 0099 parser.addVersionOption(); 0100 parser.addOptions({ 0101 {{"r", "replace"}, i18nc("command line", "Replace the current Latte instance.")} 0102 , {{"d", "debug"}, i18nc("command line", "Show the debugging messages on stdout.")} 0103 , {{"cc", "clear-cache"}, i18nc("command line", "Clear qml cache. It can be useful after system upgrades.")} 0104 , {"default-layout", i18nc("command line", "Import and load default layout on startup.")} 0105 , {"available-layouts", i18nc("command line", "Print available layouts")} 0106 , {"layout", i18nc("command line", "Load specific layout on startup."), i18nc("command line: load", "layout_name")} 0107 , {"import-layout", i18nc("command line", "Import and load a layout."), i18nc("command line: import", "file_name")} 0108 , {"import-full", i18nc("command line", "Import full configuration."), i18nc("command line: import", "file_name")} 0109 , {"single", i18nc("command line", "Single layout memory mode. Only one layout is active at any case.")} 0110 , {"multiple", i18nc("command line", "Multiple layouts memory mode. Multiple layouts can be active at any time based on Activities running.")} 0111 }); 0112 0113 //! START: Hidden options for Developer and Debugging usage 0114 QCommandLineOption graphicsOption(QStringList() << QStringLiteral("graphics")); 0115 graphicsOption.setDescription(QStringLiteral("Draw boxes around of the applets.")); 0116 graphicsOption.setFlags(QCommandLineOption::HiddenFromHelp); 0117 parser.addOption(graphicsOption); 0118 0119 QCommandLineOption withWindowOption(QStringList() << QStringLiteral("with-window")); 0120 withWindowOption.setDescription(QStringLiteral("Open a window with much debug information")); 0121 withWindowOption.setFlags(QCommandLineOption::HiddenFromHelp); 0122 parser.addOption(withWindowOption); 0123 0124 QCommandLineOption maskOption(QStringList() << QStringLiteral("mask")); 0125 maskOption.setDescription(QStringLiteral("Show messages of debugging for the mask (Only useful to devs).")); 0126 maskOption.setFlags(QCommandLineOption::HiddenFromHelp); 0127 parser.addOption(maskOption); 0128 0129 QCommandLineOption timersOption(QStringList() << QStringLiteral("timers")); 0130 timersOption.setDescription(QStringLiteral("Show messages for debugging the timers (Only useful to devs).")); 0131 timersOption.setFlags(QCommandLineOption::HiddenFromHelp); 0132 parser.addOption(timersOption); 0133 0134 QCommandLineOption spacersOption(QStringList() << QStringLiteral("spacers")); 0135 spacersOption.setDescription(QStringLiteral("Show visual indicators for debugging spacers (Only useful to devs).")); 0136 spacersOption.setFlags(QCommandLineOption::HiddenFromHelp); 0137 parser.addOption(spacersOption); 0138 0139 QCommandLineOption overloadedIconsOption(QStringList() << QStringLiteral("overloaded-icons")); 0140 overloadedIconsOption.setDescription(QStringLiteral("Show visual indicators for debugging overloaded applets icons (Only useful to devs).")); 0141 overloadedIconsOption.setFlags(QCommandLineOption::HiddenFromHelp); 0142 parser.addOption(overloadedIconsOption); 0143 0144 QCommandLineOption edgesOption(QStringList() << QStringLiteral("kwinedges")); 0145 graphicsOption.setDescription(QStringLiteral("Show visual window indicators for hidden screen edge windows.")); 0146 graphicsOption.setFlags(QCommandLineOption::HiddenFromHelp); 0147 parser.addOption(edgesOption); 0148 //! END: Hidden options 0149 0150 parser.process(app); 0151 0152 //! print available-layouts 0153 if (parser.isSet(QStringLiteral("available-layouts"))) { 0154 QStringList layouts = Latte::Layouts::Importer::availableLayouts(); 0155 0156 if (layouts.count() > 0) { 0157 qInfo() << i18n("Available layouts that can be used to start Latte:"); 0158 0159 for (const auto &layout : layouts) { 0160 qInfo() << " " << layout; 0161 } 0162 } else { 0163 qInfo() << i18n("There are no available layouts, during startup Default will be used."); 0164 } 0165 0166 qGuiApp->exit(); 0167 return 0; 0168 } 0169 0170 bool defaultLayoutOnStartup = false; 0171 int memoryUsage = -1; 0172 QString layoutNameOnStartup = ""; 0173 0174 //! --default-layout option 0175 if (parser.isSet(QStringLiteral("default-layout"))) { 0176 defaultLayoutOnStartup = true; 0177 } else if (parser.isSet(QStringLiteral("layout"))) { 0178 layoutNameOnStartup = parser.value(QStringLiteral("layout")); 0179 0180 if (!Latte::Layouts::Importer::layoutExists(layoutNameOnStartup)) { 0181 qInfo() << i18nc("layout missing", "This layout doesn't exist in the system."); 0182 qGuiApp->exit(); 0183 return 0; 0184 } 0185 } 0186 0187 //! --replace option 0188 QString username = qgetenv("USER"); 0189 0190 if (username.isEmpty()) 0191 username = qgetenv("USERNAME"); 0192 0193 QLockFile lockFile {QDir::tempPath() + "/latte-dock." + username + ".lock"}; 0194 0195 int timeout {100}; 0196 0197 if (parser.isSet(QStringLiteral("replace")) || parser.isSet(QStringLiteral("import-full"))) { 0198 qint64 pid{ -1}; 0199 0200 if (lockFile.getLockInfo(&pid, nullptr, nullptr)) { 0201 kill(static_cast<pid_t>(pid), SIGINT); 0202 timeout = -1; 0203 } 0204 } 0205 0206 if (!lockFile.tryLock(timeout)) { 0207 qInfo() << i18n("An instance is already running!, use --replace to restart Latte"); 0208 qGuiApp->exit(); 0209 return 0; 0210 } 0211 0212 //! clear-cache option 0213 if (parser.isSet(QStringLiteral("clear-cache"))) { 0214 QDir cacheDir(QDir::homePath() + "/.cache/lattedock/qmlcache"); 0215 0216 if (cacheDir.exists()) { 0217 cacheDir.removeRecursively(); 0218 qDebug() << "Cache directory found and cleared..."; 0219 } 0220 } 0221 0222 //! import-full option 0223 if (parser.isSet(QStringLiteral("import-full"))) { 0224 bool imported = Latte::Layouts::Importer::importHelper(parser.value(QStringLiteral("import-full"))); 0225 0226 if (!imported) { 0227 qInfo() << i18n("The configuration cannot be imported"); 0228 qGuiApp->exit(); 0229 return 0; 0230 } 0231 } 0232 0233 //! import-layout option 0234 if (parser.isSet(QStringLiteral("import-layout"))) { 0235 QString importedLayout = Latte::Layouts::Importer::importLayoutHelper(parser.value(QStringLiteral("import-layout"))); 0236 0237 if (importedLayout.isEmpty()) { 0238 qInfo() << i18n("The layout cannot be imported"); 0239 qGuiApp->exit(); 0240 return 0; 0241 } else { 0242 layoutNameOnStartup = importedLayout; 0243 } 0244 } 0245 0246 //! memory usage option 0247 if (parser.isSet(QStringLiteral("multiple"))) { 0248 memoryUsage = (int)(Latte::Types::MultipleLayouts); 0249 } else if (parser.isSet(QStringLiteral("single"))) { 0250 memoryUsage = (int)(Latte::Types::SingleLayout); 0251 } 0252 0253 //! debug/mask options 0254 if (parser.isSet(QStringLiteral("debug")) || parser.isSet(QStringLiteral("mask"))) { 0255 //! set pattern for debug messages 0256 //! [%{type}] [%{function}:%{line}] - %{message} [%{backtrace}] 0257 0258 qSetMessagePattern(QStringLiteral( 0259 CIGREEN "[%{type} " CGREEN "%{time h:mm:ss.zz}" CIGREEN "]" CNORMAL 0260 #ifndef QT_NO_DEBUG 0261 CIRED " [" CCYAN "%{function}" CIRED ":" CCYAN "%{line}" CIRED "]" 0262 #endif 0263 CICYAN " - " CNORMAL "%{message}" 0264 CIRED "%{if-fatal}\n%{backtrace depth=8 separator=\"\n\"}%{endif}" 0265 "%{if-critical}\n%{backtrace depth=8 separator=\"\n\"}%{endif}" CNORMAL)); 0266 } else { 0267 const auto noMessageOutput = [](QtMsgType, const QMessageLogContext &, const QString &) {}; 0268 qInstallMessageHandler(noMessageOutput); 0269 } 0270 0271 0272 auto signal_handler = [](int) { 0273 qGuiApp->exit(); 0274 }; 0275 0276 std::signal(SIGKILL, signal_handler); 0277 std::signal(SIGINT, signal_handler); 0278 0279 KCrash::setDrKonqiEnabled(true); 0280 KCrash::setFlags(KCrash::AutoRestart | KCrash::AlwaysDirectly); 0281 0282 Latte::Corona corona(defaultLayoutOnStartup, layoutNameOnStartup, memoryUsage); 0283 KDBusService service(KDBusService::Unique); 0284 0285 return app.exec(); 0286 } 0287 0288 inline void configureAboutData() 0289 { 0290 KAboutData about(QStringLiteral("lattedock") 0291 , QStringLiteral("Latte Dock") 0292 , QStringLiteral(VERSION) 0293 , i18n("Latte is a dock based on plasma frameworks that provides an elegant and " 0294 "intuitive experience for your tasks and plasmoids. It animates its contents " 0295 "by using parabolic zoom effect and tries to be there only when it is needed." 0296 "\n\n\"Art in Coffee\"") 0297 , KAboutLicense::GPL_V2 0298 , QStringLiteral("\251 2016-2017 Michail Vourlakos, Smith AR")); 0299 0300 about.setHomepage(WEBSITE); 0301 about.setProgramLogo(QIcon::fromTheme(QStringLiteral("latte-dock"))); 0302 about.setDesktopFileName(QStringLiteral("latte-dock")); 0303 0304 // Authors 0305 about.addAuthor(QStringLiteral("Michail Vourlakos"), QString(), QStringLiteral("mvourlakos@gmail.com")); 0306 about.addAuthor(QStringLiteral("Smith AR"), QString(), QStringLiteral("audoban@openmailbox.org")); 0307 0308 KAboutData::setApplicationData(about); 0309 } 0310 0311 //! used the version provided by PW:KWorkspace 0312 inline void detectPlatform(int argc, char **argv) 0313 { 0314 if (qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) { 0315 return; 0316 } 0317 0318 for (int i = 0; i < argc; i++) { 0319 if (qstrcmp(argv[i], "-platform") == 0 || 0320 qstrcmp(argv[i], "--platform") == 0 || 0321 QByteArray(argv[i]).startsWith("-platform=") || 0322 QByteArray(argv[i]).startsWith("--platform=")) { 0323 return; 0324 } 0325 } 0326 0327 const QByteArray sessionType = qgetenv("XDG_SESSION_TYPE"); 0328 0329 if (sessionType.isEmpty()) { 0330 return; 0331 } 0332 0333 if (qstrcmp(sessionType, "wayland") == 0) { 0334 qputenv("QT_QPA_PLATFORM", "wayland"); 0335 } else if (qstrcmp(sessionType, "x11") == 0) { 0336 qputenv("QT_QPA_PLATFORM", "xcb"); 0337 } 0338 }