File indexing completed on 2024-04-21 05:51:23
0001 /* 0002 SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 // To time the creation and total launch time (i. e. until window is 0008 // visible/responsive): 0009 // #define PROFILE_STARTUP 0010 0011 // Own 0012 #include "Application.h" 0013 #include "KonsoleSettings.h" 0014 #include "MainWindow.h" 0015 #include "ViewManager.h" 0016 #include "config-konsole.h" 0017 #include "widgets/ViewContainer.h" 0018 0019 // OS specific 0020 #include <QApplication> 0021 #include <QCommandLineParser> 0022 #include <QDir> 0023 #include <QProxyStyle> 0024 #include <QStandardPaths> 0025 #include <qplatformdefs.h> 0026 0027 // KDE 0028 #include <KAboutData> 0029 #include <KCrash> 0030 #include <KLocalizedString> 0031 #include <kconfigwidgets_version.h> 0032 #include <kdbusservice.h> 0033 0034 using Konsole::Application; 0035 0036 #ifdef PROFILE_STARTUP 0037 #include <QDebug> 0038 #include <QElapsedTimer> 0039 #include <QTimer> 0040 #endif 0041 0042 // fill the KAboutData structure with information about contributors to Konsole. 0043 void fillAboutData(KAboutData &aboutData); 0044 0045 // check and report whether this konsole instance should use a new konsole 0046 // process, or re-use an existing konsole process. 0047 bool shouldUseNewProcess(int argc, char *argv[]); 0048 0049 // restore sessions saved by KDE. 0050 void restoreSession(Application &app); 0051 0052 // Workaround for a bug in KDBusService: https://bugs.kde.org/show_bug.cgi?id=355545 0053 // It calls exit(), but the program can't exit before the QApplication is deleted: 0054 // https://bugreports.qt.io/browse/QTBUG-48709 0055 static bool needToDeleteQApplication = false; 0056 void deleteQApplication() 0057 { 0058 if (needToDeleteQApplication) { 0059 delete qApp; 0060 } 0061 } 0062 0063 // This override resolves following problem: since some qt version if 0064 // XDG_CURRENT_DESKTOP ≠ kde, then pressing and immediately releasing Alt 0065 // key makes focus get stuck in QMenu. 0066 // Upstream report: https://bugreports.qt.io/browse/QTBUG-77355 0067 class MenuStyle : public QProxyStyle 0068 { 0069 public: 0070 int styleHint(const StyleHint stylehint, const QStyleOption *opt, const QWidget *widget, QStyleHintReturn *returnData) const override 0071 { 0072 return (stylehint == QStyle::SH_MenuBar_AltKeyNavigation) ? 0 : QProxyStyle::styleHint(stylehint, opt, widget, returnData); 0073 } 0074 }; 0075 0076 // Used to control migrating config entries. 0077 // Increment when there are new keys to migrate. 0078 static int CurrentConfigVersion = 1; 0079 0080 static void migrateRenamedConfigKeys() 0081 { 0082 KSharedConfigPtr konsoleConfig = KSharedConfig::openConfig(QStringLiteral("konsolerc")); 0083 KConfigGroup verGroup = konsoleConfig->group(QStringLiteral("General")); 0084 const int savedVersion = verGroup.readEntry<int>("ConfigVersion", 0); 0085 if (savedVersion < CurrentConfigVersion) { 0086 struct KeyInfo { 0087 const char *groupName; 0088 const char *oldKeyName; 0089 const char *newKeyName; 0090 }; 0091 0092 static const KeyInfo keys[] = {{"KonsoleWindow", "SaveGeometryOnExit", "RememberWindowSize"}}; 0093 0094 // Migrate renamed config keys 0095 for (const auto &[group, oldName, newName] : keys) { 0096 KConfigGroup cg = konsoleConfig->group(QLatin1String(group)); 0097 if (cg.exists() && cg.hasKey(oldName)) { 0098 const bool value = cg.readEntry(oldName, false); 0099 cg.deleteEntry(oldName); 0100 cg.writeEntry(newName, value); 0101 } 0102 } 0103 0104 // With 5.93 KColorSchemeManager from KConfigWidgets, handles the loading 0105 // and saving of the widget color scheme, and uses "ColorScheme" as the 0106 // entry name, so clean-up here 0107 KConfigGroup cg(konsoleConfig, QStringLiteral("UiSettings")); 0108 const QString schemeName = cg.readEntry("WindowColorScheme"); 0109 cg.deleteEntry("WindowColorScheme"); 0110 cg.writeEntry("ColorScheme", schemeName); 0111 0112 verGroup.writeEntry("ConfigVersion", CurrentConfigVersion); 0113 konsoleConfig->sync(); 0114 } 0115 } 0116 0117 // *** 0118 // Entry point into the Konsole terminal application. 0119 // *** 0120 int main(int argc, char *argv[]) 0121 { 0122 #ifdef PROFILE_STARTUP 0123 QElapsedTimer timer; 0124 timer.start(); 0125 #endif 0126 0127 /** 0128 * enable dark mode for title bar on Windows 0129 */ 0130 #if defined(Q_OS_WIN) 0131 if (!qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) { 0132 qputenv("QT_QPA_PLATFORM", "windows:darkmode=1"); 0133 } 0134 #endif 0135 0136 // Check if any of the arguments makes it impossible to re-use an existing process. 0137 // We need to do this manually and before creating a QApplication, because 0138 // QApplication takes/removes the Qt specific arguments that are incompatible. 0139 const bool needNewProcess = shouldUseNewProcess(argc, argv); 0140 if (!needNewProcess) { // We need to avoid crashing 0141 needToDeleteQApplication = true; 0142 } 0143 0144 auto app = new QApplication(argc, argv); 0145 /** 0146 * For Windows: use Breeze if available 0147 * Of all tested styles that works the best for us 0148 * NOTE: Taken from Kate 0149 */ 0150 #if defined(Q_OS_WIN) 0151 QApplication::setStyle(QStringLiteral("breeze")); 0152 #else 0153 app->setStyle(new MenuStyle()); 0154 #endif 0155 0156 migrateRenamedConfigKeys(); 0157 0158 app->setWindowIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal"))); 0159 0160 KLocalizedString::setApplicationDomain("konsole"); 0161 0162 KAboutData about(QStringLiteral("konsole"), 0163 i18nc("@title", "Konsole"), 0164 QStringLiteral(KONSOLE_VERSION), 0165 i18nc("@title", "Terminal emulator"), 0166 KAboutLicense::GPL_V2, 0167 i18nc("@info:credit", "(c) 1997-2022, The Konsole Developers"), 0168 QString(), 0169 QStringLiteral("https://konsole.kde.org/")); 0170 fillAboutData(about); 0171 0172 KAboutData::setApplicationData(about); 0173 0174 KCrash::initialize(); 0175 0176 QSharedPointer<QCommandLineParser> parser(new QCommandLineParser); 0177 parser->setApplicationDescription(about.shortDescription()); 0178 about.setupCommandLine(parser.data()); 0179 0180 QStringList args = app->arguments(); 0181 QStringList customCommand = Application::getCustomCommand(args); 0182 0183 Application::populateCommandLineParser(parser.data()); 0184 0185 parser->process(args); 0186 about.processCommandLine(parser.data()); 0187 0188 /// ! DON'T TOUCH THIS ! /// 0189 const KDBusService::StartupOption startupOption = 0190 Konsole::KonsoleSettings::useSingleInstance() && !needNewProcess ? KDBusService::Unique : KDBusService::Multiple; 0191 /// ! DON'T TOUCH THIS ! /// 0192 // If you need to change something here, add your logic _at the bottom_ of 0193 // shouldUseNewProcess(), after reading the explanations there for why you 0194 // probably shouldn't. 0195 0196 atexit(deleteQApplication); 0197 // Ensure that we only launch a new instance if we need to 0198 // If there is already an instance running, we will quit here 0199 KDBusService dbusService(startupOption | KDBusService::NoExitOnFailure); 0200 0201 needToDeleteQApplication = false; 0202 0203 // If we reach this location, there was no existing copy of Konsole 0204 // running, so create a new instance. 0205 Application konsoleApp(parser, customCommand); 0206 0207 // The activateRequested() signal is emitted when a second instance 0208 // of Konsole is started. 0209 QObject::connect(&dbusService, &KDBusService::activateRequested, &konsoleApp, &Application::slotActivateRequested); 0210 0211 if (app->isSessionRestored()) { 0212 restoreSession(konsoleApp); 0213 } else { 0214 // Do not finish starting Konsole due to: 0215 // 1. An argument was given to just printed info 0216 // 2. An invalid situation occurred 0217 const bool continueStarting = (konsoleApp.newInstance() != 0); 0218 if (!continueStarting) { 0219 delete app; 0220 return 0; 0221 } 0222 } 0223 0224 #ifdef PROFILE_STARTUP 0225 qDebug() << "Construction completed in" << timer.elapsed() << "ms"; 0226 QTimer::singleShot(0, [&timer]() { 0227 qDebug() << "Startup complete in" << timer.elapsed() << "ms"; 0228 }); 0229 #endif 0230 0231 // Since we've allocated the QApplication on the heap for the KDBusService workaround, 0232 // we need to delete it manually before returning from main(). 0233 int ret = app->exec(); 0234 delete app; 0235 return ret; 0236 } 0237 0238 bool shouldUseNewProcess(int argc, char *argv[]) 0239 { 0240 // The "unique process" model of konsole is incompatible with some or all 0241 // Qt/KDE options. When those incompatible options are given, konsole must 0242 // use new process 0243 // 0244 // TODO: make sure the existing list is OK and add more incompatible options. 0245 0246 // We need to manually parse the arguments because QApplication removes the 0247 // Qt specific arguments (like --reverse) 0248 QStringList arguments; 0249 arguments.reserve(argc); 0250 for (int i = 0; i < argc; i++) { 0251 arguments.append(QString::fromLocal8Bit(argv[i])); 0252 } 0253 0254 if (arguments.contains(QLatin1String("--force-reuse"))) { 0255 return false; 0256 } 0257 0258 // take Qt options into consideration 0259 QStringList qtProblematicOptions; 0260 qtProblematicOptions << QStringLiteral("--session") << QStringLiteral("--name") << QStringLiteral("--reverse") << QStringLiteral("--stylesheet") 0261 << QStringLiteral("--graphicssystem"); 0262 #if HAVE_X11 0263 qtProblematicOptions << QStringLiteral("--display") << QStringLiteral("--visual"); 0264 #endif 0265 for (const QString &option : std::as_const(qtProblematicOptions)) { 0266 if (arguments.contains(option)) { 0267 return true; 0268 } 0269 } 0270 0271 // take KDE options into consideration 0272 QStringList kdeProblematicOptions; 0273 kdeProblematicOptions << QStringLiteral("--config") << QStringLiteral("--style"); 0274 #if HAVE_X11 0275 kdeProblematicOptions << QStringLiteral("--waitforwm"); 0276 #endif 0277 0278 for (const QString &option : std::as_const(kdeProblematicOptions)) { 0279 if (arguments.contains(option)) { 0280 return true; 0281 } 0282 } 0283 0284 // if users have explicitly requested starting a new process 0285 // Support --nofork to retain argument compatibility with older 0286 // versions. 0287 if (arguments.contains(QStringLiteral("--separate")) || arguments.contains(QStringLiteral("--nofork"))) { 0288 return true; 0289 } 0290 0291 // the only way to create new tab is to reuse existing Konsole process. 0292 if (arguments.contains(QStringLiteral("--new-tab"))) { 0293 return false; 0294 } 0295 0296 // when starting Konsole from a terminal, a new process must be used 0297 // so that the current environment is propagated into the shells of the new 0298 // Konsole and any debug output or warnings from Konsole are written to 0299 // the current terminal 0300 bool hasControllingTTY = false; 0301 const int fd = QT_OPEN("/dev/tty", O_RDONLY); 0302 if (fd != -1) { 0303 hasControllingTTY = true; 0304 close(fd); 0305 } 0306 0307 return hasControllingTTY; 0308 } 0309 0310 void fillAboutData(KAboutData &aboutData) 0311 { 0312 aboutData.setOrganizationDomain("kde.org"); 0313 0314 aboutData.addAuthor(i18nc("@info:credit", "Kurt Hindenburg"), 0315 i18nc("@info:credit", 0316 "General maintainer, bug fixes and general" 0317 " improvements"), 0318 QStringLiteral("kurt.hindenburg@gmail.com")); 0319 aboutData.addAuthor(i18nc("@info:credit", "Robert Knight"), 0320 i18nc("@info:credit", "Previous maintainer, ported to KDE4"), 0321 QStringLiteral("robertknight@gmail.com")); 0322 aboutData.addAuthor(i18nc("@info:credit", "Lars Doelle"), i18nc("@info:credit", "Original author"), QStringLiteral("lars.doelle@on-line.de")); 0323 aboutData.addCredit(i18nc("@info:credit", "Ahmad Samir"), 0324 i18nc("@info:credit", "Major refactoring, bug fixes and major improvements"), 0325 QStringLiteral("a.samirh78@gmail.com")); 0326 aboutData.addCredit(i18nc("@info:credit", "Carlos Alves"), 0327 i18nc("@info:credit", "Major refactoring, bug fixes and major improvements"), 0328 QStringLiteral("cbc.alves@gmail.com")); 0329 aboutData.addCredit(i18nc("@info:credit", "Tomaz Canabrava"), 0330 i18nc("@info:credit", "Major refactoring, bug fixes and major improvements"), 0331 QStringLiteral("tcanabrava@kde.org")); 0332 aboutData.addCredit(i18nc("@info:credit", "Gustavo Carneiro"), 0333 i18nc("@info:credit", "Major refactoring, bug fixes and major improvements"), 0334 QStringLiteral("gcarneiroa@hotmail.com")); 0335 aboutData.addCredit(i18nc("@info:credit", "Edwin Pujols"), 0336 i18nc("@info:credit", "Bug fixes and general improvements"), 0337 QStringLiteral("edwin.pujols5@outlook.com")); 0338 aboutData.addCredit(i18nc("@info:credit", "Martin T. H. Sandsmark"), 0339 i18nc("@info:credit", "Bug fixes and general improvements"), 0340 QStringLiteral("martin.sandsmark@kde.org")); 0341 aboutData.addCredit(i18nc("@info:credit", "Nate Graham"), i18nc("@info:credit", "Bug fixes and general improvements"), QStringLiteral("nate@kde.org")); 0342 aboutData.addCredit(i18nc("@info:credit", "Mariusz Glebocki"), 0343 i18nc("@info:credit", "Bug fixes and major improvements"), 0344 QStringLiteral("mglb@arccos-1.net")); 0345 aboutData.addCredit(i18nc("@info:credit", "Thomas Surrel"), 0346 i18nc("@info:credit", "Bug fixes and general improvements"), 0347 QStringLiteral("thomas.surrel@protonmail.com")); 0348 aboutData.addCredit(i18nc("@info:credit", "Jekyll Wu"), i18nc("@info:credit", "Bug fixes and general improvements"), QStringLiteral("adaptee@gmail.com")); 0349 aboutData.addCredit(i18nc("@info:credit", "Waldo Bastian"), i18nc("@info:credit", "Bug fixes and general improvements"), QStringLiteral("bastian@kde.org")); 0350 aboutData.addCredit(i18nc("@info:credit", "Stephan Binner"), i18nc("@info:credit", "Bug fixes and general improvements"), QStringLiteral("binner@kde.org")); 0351 aboutData.addCredit(i18nc("@info:credit", "Thomas Dreibholz"), i18nc("@info:credit", "General improvements"), QStringLiteral("dreibh@iem.uni-due.de")); 0352 aboutData.addCredit(i18nc("@info:credit", "Chris Machemer"), i18nc("@info:credit", "Bug fixes"), QStringLiteral("machey@ceinetworks.com")); 0353 aboutData.addCredit(i18nc("@info:credit", "Francesco Cecconi"), i18nc("@info:credit", "Bug fixes"), QStringLiteral("francesco.cecconi@gmail.com")); 0354 aboutData.addCredit(i18nc("@info:credit", "Stephan Kulow"), i18nc("@info:credit", "Solaris support and history"), QStringLiteral("coolo@kde.org")); 0355 aboutData.addCredit(i18nc("@info:credit", "Alexander Neundorf"), 0356 i18nc("@info:credit", "Bug fixes and improved startup performance"), 0357 QStringLiteral("neundorf@kde.org")); 0358 aboutData.addCredit(i18nc("@info:credit", "Peter Silva"), i18nc("@info:credit", "Marking improvements"), QStringLiteral("Peter.A.Silva@gmail.com")); 0359 aboutData.addCredit(i18nc("@info:credit", "Lotzi Boloni"), 0360 i18nc("@info:credit", 0361 "Embedded Konsole\n" 0362 "Toolbar and session names"), 0363 QStringLiteral("boloni@cs.purdue.edu")); 0364 aboutData.addCredit(i18nc("@info:credit", "David Faure"), 0365 i18nc("@info:credit", 0366 "Embedded Konsole\n" 0367 "General improvements"), 0368 QStringLiteral("faure@kde.org")); 0369 aboutData.addCredit(i18nc("@info:credit", "Antonio Larrosa"), i18nc("@info:credit", "Visual effects"), QStringLiteral("larrosa@kde.org")); 0370 aboutData.addCredit(i18nc("@info:credit", "Matthias Ettrich"), 0371 i18nc("@info:credit", 0372 "Code from the kvt project\n" 0373 "General improvements"), 0374 QStringLiteral("ettrich@kde.org")); 0375 aboutData.addCredit(i18nc("@info:credit", "Warwick Allison"), 0376 i18nc("@info:credit", "Schema and text selection improvements"), 0377 QStringLiteral("warwick@troll.no")); 0378 aboutData.addCredit(i18nc("@info:credit", "Dan Pilone"), i18nc("@info:credit", "SGI port"), QStringLiteral("pilone@slac.com")); 0379 aboutData.addCredit(i18nc("@info:credit", "Kevin Street"), i18nc("@info:credit", "FreeBSD port"), QStringLiteral("street@iname.com")); 0380 aboutData.addCredit(i18nc("@info:credit", "Sven Fischer"), i18nc("@info:credit", "Bug fixes"), QStringLiteral("herpes@kawo2.renditionwth-aachen.de")); 0381 aboutData.addCredit(i18nc("@info:credit", "Dale M. Flaven"), i18nc("@info:credit", "Bug fixes"), QStringLiteral("dflaven@netport.com")); 0382 aboutData.addCredit(i18nc("@info:credit", "Martin Jones"), i18nc("@info:credit", "Bug fixes"), QStringLiteral("mjones@powerup.com.au")); 0383 aboutData.addCredit(i18nc("@info:credit", "Lars Knoll"), i18nc("@info:credit", "Bug fixes"), QStringLiteral("knoll@mpi-hd.mpg.de")); 0384 aboutData.addCredit(i18nc("@info:credit", "Thanks to many others.\n")); 0385 } 0386 0387 void restoreSession(Application &app) 0388 { 0389 int n = 1; 0390 0391 while (KMainWindow::canBeRestored(n)) { 0392 auto mainWindow = app.newMainWindow(); 0393 mainWindow->restore(n++); 0394 mainWindow->viewManager()->toggleActionsBasedOnState(); 0395 mainWindow->show(); 0396 0397 // TODO: HACK without the code below the sessions would be `uninitialized` 0398 // and the tabs wouldn't display the correct information. 0399 auto tabbedContainer = qobject_cast<Konsole::TabbedViewContainer *>(mainWindow->centralWidget()); 0400 for (int i = 0; i < tabbedContainer->count(); i++) { 0401 tabbedContainer->setCurrentIndex(i); 0402 } 0403 } 0404 }