File indexing completed on 2024-11-10 04:56:46

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2020 <davidedmundson@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 /**
0011  * This tiny executable creates a socket, then starts kwin passing it the FD to the wayland socket
0012  * along with the name of the socket to use
0013  * On any non-zero kwin exit kwin gets restarted.
0014  *
0015  * After restart kwin is relaunched but now with the KWIN_RESTART_COUNT env set to an incrementing counter
0016  *
0017  * It's somewhat  akin to systemd socket activation, but we also need the lock file, finding the next free socket
0018  * and so on, hence our own binary.
0019  *
0020  * Usage kwin_wayland_wrapper [argForKwin] [argForKwin] ...
0021  */
0022 
0023 #include <QCoreApplication>
0024 #include <QDBusConnection>
0025 #include <QDebug>
0026 #include <QProcess>
0027 #include <QTemporaryFile>
0028 
0029 #include <KSignalHandler>
0030 #include <KUpdateLaunchEnvironmentJob>
0031 
0032 #include <signal.h>
0033 
0034 #include "wl-socket.h"
0035 #include "wrapper_logging.h"
0036 #include "xauthority.h"
0037 #include "xwaylandsocket.h"
0038 
0039 class KWinWrapper : public QObject
0040 {
0041     Q_OBJECT
0042 public:
0043     KWinWrapper(QObject *parent);
0044     ~KWinWrapper();
0045     void run();
0046 
0047 private:
0048     wl_socket *m_socket;
0049 
0050     int m_crashCount = 0;
0051     QProcess *m_kwinProcess = nullptr;
0052 
0053     std::unique_ptr<KWin::XwaylandSocket> m_xwlSocket;
0054     QTemporaryFile m_xauthorityFile;
0055 };
0056 
0057 KWinWrapper::KWinWrapper(QObject *parent)
0058     : QObject(parent)
0059     , m_kwinProcess(new QProcess(this))
0060 {
0061     m_socket = wl_socket_create();
0062     if (!m_socket) {
0063         qFatal("Could not create wayland socket");
0064     }
0065 
0066     if (qApp->arguments().contains(QLatin1String("--xwayland"))) {
0067         m_xwlSocket = std::make_unique<KWin::XwaylandSocket>(KWin::XwaylandSocket::OperationMode::TransferFdsOnExec);
0068         if (!m_xwlSocket->isValid()) {
0069             qCWarning(KWIN_WRAPPER) << "Failed to create Xwayland connection sockets";
0070             m_xwlSocket.reset();
0071         }
0072         if (m_xwlSocket) {
0073             if (!qEnvironmentVariableIsSet("KWIN_WAYLAND_NO_XAUTHORITY")) {
0074                 if (!generateXauthorityFile(m_xwlSocket->display(), &m_xauthorityFile)) {
0075                     qCWarning(KWIN_WRAPPER) << "Failed to create an Xauthority file";
0076                 }
0077             }
0078         }
0079     }
0080 }
0081 
0082 KWinWrapper::~KWinWrapper()
0083 {
0084     wl_socket_destroy(m_socket);
0085     if (m_kwinProcess) {
0086         disconnect(m_kwinProcess, nullptr, this, nullptr);
0087         m_kwinProcess->terminate();
0088         m_kwinProcess->waitForFinished();
0089         m_kwinProcess->kill();
0090         m_kwinProcess->waitForFinished();
0091     }
0092 }
0093 
0094 void KWinWrapper::run()
0095 {
0096     m_kwinProcess->setProgram("kwin_wayland");
0097 
0098     QStringList args;
0099 
0100     args << "--wayland-fd" << QString::number(wl_socket_get_fd(m_socket));
0101     args << "--socket" << QString::fromUtf8(wl_socket_get_display_name(m_socket));
0102 
0103     if (m_xwlSocket) {
0104         const auto xwaylandFileDescriptors = m_xwlSocket->fileDescriptors();
0105         for (const int &fileDescriptor : xwaylandFileDescriptors) {
0106             args << "--xwayland-fd" << QString::number(fileDescriptor);
0107         }
0108         args << "--xwayland-display" << m_xwlSocket->name();
0109         if (m_xauthorityFile.open()) {
0110             args << "--xwayland-xauthority" << m_xauthorityFile.fileName();
0111         }
0112     }
0113 
0114     // attach our main process arguments
0115     // the first entry is dropped as it will be our program name
0116     args << qApp->arguments().mid(1);
0117 
0118     m_kwinProcess->setProcessChannelMode(QProcess::ForwardedChannels);
0119     m_kwinProcess->setArguments(args);
0120 
0121     connect(m_kwinProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
0122         if (exitCode == 0) {
0123             qApp->quit();
0124             return;
0125         } else if (exitCode == 133) {
0126             m_crashCount = 0;
0127         } else {
0128             m_crashCount++;
0129         }
0130 
0131         if (m_crashCount > 10) {
0132             qApp->quit();
0133             return;
0134         }
0135         qputenv("KWIN_RESTART_COUNT", QByteArray::number(m_crashCount));
0136         // restart
0137         m_kwinProcess->start();
0138     });
0139 
0140     m_kwinProcess->start();
0141 
0142     QProcessEnvironment env;
0143     env.insert("WAYLAND_DISPLAY", QString::fromUtf8(wl_socket_get_display_name(m_socket)));
0144     if (m_xwlSocket) {
0145         env.insert("DISPLAY", m_xwlSocket->name());
0146         if (m_xauthorityFile.open()) {
0147             env.insert("XAUTHORITY", m_xauthorityFile.fileName());
0148         }
0149     }
0150 
0151     auto envSyncJob = new KUpdateLaunchEnvironmentJob(env);
0152     connect(envSyncJob, &KUpdateLaunchEnvironmentJob::finished, this, []() {
0153         // The service name is merely there to indicate to the world that we're up and ready with all envs exported
0154         QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.KWinWrapper"));
0155     });
0156 }
0157 
0158 int main(int argc, char **argv)
0159 {
0160     QCoreApplication app(argc, argv);
0161     app.setQuitLockEnabled(false); // don't exit when the first KJob finishes
0162 
0163     KSignalHandler::self()->watchSignal(SIGTERM);
0164     QObject::connect(KSignalHandler::self(), &KSignalHandler::signalReceived, &app, [&app](int signal) {
0165         if (signal == SIGTERM) {
0166             app.quit();
0167         }
0168     });
0169 
0170     KWinWrapper wrapper(&app);
0171     wrapper.run();
0172 
0173     return app.exec();
0174 }
0175 
0176 #include "kwin_wrapper.moc"