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"