File indexing completed on 2024-04-28 15:52:01
0001 /* 0002 SPDX-FileCopyrightText: 2002 Wilco Greven <greven@kde.org> 0003 SPDX-FileCopyrightText: 2003 Christophe Devriese <Christophe.Devriese@student.kuleuven.ac.be> 0004 SPDX-FileCopyrightText: 2003 Laurent Montel <montel@kde.org> 0005 SPDX-FileCopyrightText: 2003-2007 Albert Astals Cid <aacid@kde.org> 0006 SPDX-FileCopyrightText: 2004 Andy Goossens <andygoossens@telenet.be> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "okular_main.h" 0012 0013 #include "aboutdata.h" 0014 #include "shell.h" 0015 #include "shellutils.h" 0016 #include <KLocalizedString> 0017 #include <KWindowSystem> 0018 #include <QApplication> 0019 #include <QMimeData> 0020 #include <QTemporaryFile> 0021 #include <QTextStream> 0022 0023 #include "config-okular.h" 0024 #if HAVE_X11 0025 #include <KX11Extras> 0026 #include <private/qtx11extras_p.h> 0027 #endif 0028 #if HAVE_DBUS 0029 #include <QDBusConnectionInterface> 0030 #include <QDBusInterface> 0031 #endif // HAVE_DBUS 0032 0033 #include <iostream> 0034 0035 static QString startupId() 0036 { 0037 QString result; 0038 if (KWindowSystem::isPlatformWayland()) { 0039 result = qEnvironmentVariable("XDG_ACTIVATION_TOKEN"); 0040 qunsetenv("XDG_ACTIVATION_TOKEN"); 0041 } else if (KWindowSystem::isPlatformX11()) { 0042 #if HAVE_X11 0043 result = QString::fromUtf8(QX11Info::nextStartupId()); 0044 #endif 0045 } 0046 0047 return result; 0048 } 0049 0050 static bool attachUniqueInstance(const QStringList &paths, const QString &serializedOptions) 0051 { 0052 #if HAVE_DBUS 0053 if (!ShellUtils::unique(serializedOptions) || paths.count() != 1) { 0054 return false; 0055 } 0056 0057 QDBusInterface iface(QStringLiteral("org.kde.okular"), QStringLiteral("/okularshell"), QStringLiteral("org.kde.okular")); 0058 if (!iface.isValid()) { 0059 return false; 0060 } 0061 0062 if (!ShellUtils::editorCmd(serializedOptions).isEmpty()) { 0063 QString message = 0064 i18n("You cannot set the editor command in an already running okular instance. Please disable the tabs and try again. Please note, that unique is also not supported when setting the editor command at the commandline.\n"); 0065 std::cerr << message.toStdString(); 0066 exit(1); 0067 } 0068 0069 const QString page = ShellUtils::page(serializedOptions); 0070 iface.call(QStringLiteral("openDocument"), ShellUtils::urlFromArg(paths[0], ShellUtils::qfileExistFunc(), page).url(), serializedOptions); 0071 if (!ShellUtils::noRaise(serializedOptions)) { 0072 iface.call(QStringLiteral("tryRaise"), startupId()); 0073 } 0074 0075 return true; 0076 #else // HAVE_DBUS 0077 return false; 0078 #endif // HAVE_DBUS 0079 } 0080 0081 // Ask an existing non-unique instance to open new tabs 0082 static bool attachExistingInstance(const QStringList &paths, const QString &serializedOptions) 0083 { 0084 #if HAVE_DBUS 0085 if (paths.count() < 1) { 0086 return false; 0087 } 0088 0089 // Don't try to attach to an existing instance with --print-and-exit because that would mean 0090 // we're going to exit that other instance and that's just rude 0091 if (ShellUtils::showPrintDialogAndExit(serializedOptions)) { 0092 return false; 0093 } 0094 0095 // If DBus isn't running, we can't attach to an existing instance. 0096 auto *sessionInterface = QDBusConnection::sessionBus().interface(); 0097 if (!sessionInterface) { 0098 return false; 0099 } 0100 0101 const QStringList services = sessionInterface->registeredServiceNames().value(); 0102 0103 // Don't match the service without trailing "-" (unique instance) 0104 const QString pattern = QStringLiteral("org.kde.okular-"); 0105 const QString myPid = QString::number(qApp->applicationPid()); 0106 QScopedPointer<QDBusInterface> bestService; 0107 #if HAVE_X11 0108 const int desktop = KX11Extras::currentDesktop(); 0109 #else 0110 const int desktop = 0; 0111 #endif 0112 0113 // Select the first instance that isn't us (metric may change in future) 0114 for (const QString &service : services) { 0115 if (service.startsWith(pattern) && !service.endsWith(myPid)) { 0116 bestService.reset(new QDBusInterface(service, QStringLiteral("/okularshell"), QStringLiteral("org.kde.okular"))); 0117 0118 // Find a window that can handle our documents 0119 const QDBusReply<bool> reply = bestService->call(QStringLiteral("canOpenDocs"), (int)paths.count(), desktop); 0120 if (reply.isValid() && reply.value()) { 0121 break; 0122 } 0123 0124 bestService.reset(); 0125 } 0126 } 0127 0128 if (!bestService) { 0129 return false; 0130 } 0131 0132 for (const QString &arg : paths) { 0133 // Copy stdin to temporary file which can be opened by the existing 0134 // window. The temp file is automatically deleted after it has been 0135 // opened. Not sure if this behavior is safe on all platforms. 0136 QScopedPointer<QTemporaryFile> tempFile; 0137 QString path; 0138 if (arg == QLatin1String("-")) { 0139 tempFile.reset(new QTemporaryFile); 0140 QFile stdinFile; 0141 if (!tempFile->open() || !stdinFile.open(stdin, QIODevice::ReadOnly)) { 0142 return false; 0143 } 0144 0145 const size_t bufSize = 1024 * 1024; 0146 QScopedPointer<char, QScopedPointerArrayDeleter<char>> buf(new char[bufSize]); 0147 size_t bytes; 0148 do { 0149 bytes = stdinFile.read(buf.data(), bufSize); 0150 tempFile->write(buf.data(), bytes); 0151 } while (bytes != 0); 0152 0153 path = tempFile->fileName(); 0154 } else { 0155 // Page only makes sense if we are opening one file 0156 const QString page = ShellUtils::page(serializedOptions); 0157 path = ShellUtils::urlFromArg(arg, ShellUtils::qfileExistFunc(), page).url(); 0158 } 0159 0160 // Returns false if it can't fit another document 0161 const QDBusReply<bool> reply = bestService->call(QStringLiteral("openDocument"), path, serializedOptions); 0162 if (!reply.isValid() || !reply.value()) { 0163 return false; 0164 } 0165 } 0166 0167 if (!ShellUtils::editorCmd(serializedOptions).isEmpty()) { 0168 QString message( 0169 i18n("You cannot set the editor command in an already running okular instance. Please disable the tabs and try again. Please note, that unique is also not supported when setting the editor command at the commandline.\n")); 0170 std::cerr << message.toStdString(); 0171 exit(1); 0172 } 0173 0174 bestService->call(QStringLiteral("tryRaise"), startupId()); 0175 0176 return true; 0177 #else // HAVE_DBUS 0178 return false; 0179 #endif // HAVE_DBUS 0180 } 0181 0182 namespace Okular 0183 { 0184 Status main(const QStringList &paths, const QString &serializedOptions) 0185 { 0186 if (ShellUtils::unique(serializedOptions) && paths.count() > 1) { 0187 QTextStream stream(stderr); 0188 stream << i18n("Error: Can't open more than one document with the --unique switch") << '\n'; 0189 return Error; 0190 } 0191 0192 if (ShellUtils::startInPresentation(serializedOptions) && paths.count() > 1) { 0193 QTextStream stream(stderr); 0194 stream << i18n("Error: Can't open more than one document with the --presentation switch") << '\n'; 0195 return Error; 0196 } 0197 0198 if (ShellUtils::showPrintDialog(serializedOptions) && paths.count() > 1) { 0199 QTextStream stream(stderr); 0200 stream << i18n("Error: Can't open more than one document with the --print switch") << '\n'; 0201 return Error; 0202 } 0203 0204 if (!ShellUtils::page(serializedOptions).isEmpty() && paths.count() > 1) { 0205 QTextStream stream(stderr); 0206 stream << i18n("Error: Can't open more than one document with the --page switch") << '\n'; 0207 return Error; 0208 } 0209 0210 if (!ShellUtils::find(serializedOptions).isEmpty() && paths.count() > 1) { 0211 QTextStream stream(stderr); 0212 stream << i18n("Error: Can't open more than one document with the --find switch") << '\n'; 0213 return Error; 0214 } 0215 0216 // try to attach to existing session, unique or not 0217 if (attachUniqueInstance(paths, serializedOptions) || attachExistingInstance(paths, serializedOptions)) { 0218 return AttachedOtherProcess; 0219 } 0220 0221 Shell *shell = new Shell(serializedOptions); 0222 if (!shell->isValid()) { 0223 return Error; 0224 } 0225 0226 shell->show(); 0227 for (int i = 0; i < paths.count();) { 0228 // Page only makes sense if we are opening one file 0229 const QString page = ShellUtils::page(serializedOptions); 0230 const QUrl url = ShellUtils::urlFromArg(paths[i], ShellUtils::qfileExistFunc(), page); 0231 if (shell->openDocument(url, serializedOptions)) { 0232 ++i; 0233 } else { 0234 shell = new Shell(serializedOptions); 0235 if (!shell->isValid()) { 0236 return Error; 0237 } 0238 shell->show(); 0239 } 0240 } 0241 0242 return Success; 0243 } 0244 0245 } 0246 0247 /* kate: replace-tabs on; indent-width 4; */