File indexing completed on 2024-12-22 05:09:19
0001 /* 0002 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 #include "connection_thread.h" 0007 #include "logging.h" 0008 // Qt 0009 #include <QAbstractEventDispatcher> 0010 #include <QDebug> 0011 #include <QDir> 0012 #include <QFileSystemWatcher> 0013 #include <QGuiApplication> 0014 #include <QMutex> 0015 #include <QMutexLocker> 0016 #include <QSocketNotifier> 0017 #include <qpa/qplatformnativeinterface.h> 0018 // Wayland 0019 #include <wayland-client-protocol.h> 0020 0021 #include <poll.h> 0022 0023 namespace KWayland 0024 { 0025 namespace Client 0026 { 0027 class Q_DECL_HIDDEN ConnectionThread::Private 0028 { 0029 public: 0030 Private(ConnectionThread *q); 0031 ~Private(); 0032 void doInitConnection(); 0033 void setupSocketNotifier(); 0034 void setupSocketFileWatcher(); 0035 void dispatchEvents(); 0036 0037 wl_display *display = nullptr; 0038 int fd = -1; 0039 QString socketName; 0040 QDir runtimeDir; 0041 QScopedPointer<QSocketNotifier> socketNotifier; 0042 QScopedPointer<QFileSystemWatcher> socketWatcher; 0043 bool serverDied = false; 0044 bool foreign = false; 0045 QMetaObject::Connection eventDispatcherConnection; 0046 int error = 0; 0047 static QList<ConnectionThread *> connections; 0048 static QRecursiveMutex mutex; 0049 0050 private: 0051 ConnectionThread *q; 0052 }; 0053 0054 QList<ConnectionThread *> ConnectionThread::Private::connections = QList<ConnectionThread *>{}; 0055 QRecursiveMutex ConnectionThread::Private::mutex; 0056 0057 ConnectionThread::Private::Private(ConnectionThread *q) 0058 : socketName(QString::fromUtf8(qgetenv("WAYLAND_DISPLAY"))) 0059 , runtimeDir(QString::fromUtf8(qgetenv("XDG_RUNTIME_DIR"))) 0060 , q(q) 0061 { 0062 if (socketName.isEmpty()) { 0063 socketName = QStringLiteral("wayland-0"); 0064 } 0065 { 0066 QMutexLocker lock(&mutex); 0067 connections << q; 0068 } 0069 } 0070 0071 ConnectionThread::Private::~Private() 0072 { 0073 { 0074 QMutexLocker lock(&mutex); 0075 connections.removeOne(q); 0076 } 0077 if (display && !foreign) { 0078 wl_display_flush(display); 0079 wl_display_disconnect(display); 0080 } 0081 } 0082 0083 void ConnectionThread::Private::doInitConnection() 0084 { 0085 if (fd != -1) { 0086 display = wl_display_connect_to_fd(fd); 0087 } else { 0088 display = wl_display_connect(socketName.toUtf8().constData()); 0089 } 0090 if (!display) { 0091 qCWarning(KWAYLAND_CLIENT) << "Failed connecting to Wayland display"; 0092 Q_EMIT q->failed(); 0093 return; 0094 } 0095 if (fd != -1) { 0096 qCDebug(KWAYLAND_CLIENT) << "Connected to Wayland server over file descriptor:" << fd; 0097 } else { 0098 qCDebug(KWAYLAND_CLIENT) << "Connected to Wayland server at:" << socketName; 0099 } 0100 0101 // setup socket notifier 0102 setupSocketNotifier(); 0103 setupSocketFileWatcher(); 0104 Q_EMIT q->connected(); 0105 } 0106 0107 void ConnectionThread::Private::setupSocketNotifier() 0108 { 0109 const int fd = wl_display_get_fd(display); 0110 socketNotifier.reset(new QSocketNotifier(fd, QSocketNotifier::Read)); 0111 QObject::connect(socketNotifier.data(), &QSocketNotifier::activated, q, [this]() { 0112 dispatchEvents(); 0113 }); 0114 } 0115 0116 void ConnectionThread::Private::dispatchEvents() 0117 { 0118 if (!display) { 0119 return; 0120 } 0121 // first dispatch any pending events on the default queue 0122 while (wl_display_prepare_read(display) != 0) { 0123 wl_display_dispatch_pending(display); 0124 } 0125 wl_display_flush(display); 0126 // then check if there are any new events waiting to be read 0127 struct pollfd pfd; 0128 pfd.fd = wl_display_get_fd(display); 0129 pfd.events = POLLIN; 0130 int ret = poll(&pfd, 1, 0); 0131 if (ret > 0) { 0132 // if yes, read them now 0133 wl_display_read_events(display); 0134 } else { 0135 wl_display_cancel_read(display); 0136 } 0137 0138 // finally, dispatch the default queue and all frame queues 0139 if (wl_display_dispatch_pending(display) == -1) { 0140 error = wl_display_get_error(display); 0141 if (error != 0) { 0142 if (display) { 0143 free(display); 0144 display = nullptr; 0145 } 0146 Q_EMIT q->errorOccurred(); 0147 return; 0148 } 0149 } 0150 Q_EMIT q->eventsRead(); 0151 } 0152 0153 void ConnectionThread::Private::setupSocketFileWatcher() 0154 { 0155 if (!runtimeDir.exists() || fd != -1) { 0156 return; 0157 } 0158 socketWatcher.reset(new QFileSystemWatcher); 0159 socketWatcher->addPath(runtimeDir.absoluteFilePath(socketName)); 0160 QObject::connect(socketWatcher.data(), &QFileSystemWatcher::fileChanged, q, [this](const QString &file) { 0161 if (QFile::exists(file) || serverDied) { 0162 return; 0163 } 0164 qCWarning(KWAYLAND_CLIENT) << "Connection to server went away"; 0165 serverDied = true; 0166 if (display) { 0167 free(display); 0168 display = nullptr; 0169 } 0170 socketNotifier.reset(); 0171 0172 // need a new filesystem watcher 0173 socketWatcher.reset(new QFileSystemWatcher); 0174 socketWatcher->addPath(runtimeDir.absolutePath()); 0175 QObject::connect(socketWatcher.data(), &QFileSystemWatcher::directoryChanged, q, [this]() { 0176 if (!serverDied) { 0177 return; 0178 } 0179 if (runtimeDir.exists(socketName)) { 0180 qCDebug(KWAYLAND_CLIENT) << "Socket reappeared"; 0181 socketWatcher.reset(); 0182 serverDied = false; 0183 error = 0; 0184 q->initConnection(); 0185 } 0186 }); 0187 Q_EMIT q->connectionDied(); 0188 }); 0189 } 0190 0191 ConnectionThread::ConnectionThread(QObject *parent) 0192 : QObject(parent) 0193 , d(new Private(this)) 0194 { 0195 d->eventDispatcherConnection = connect( 0196 QCoreApplication::eventDispatcher(), 0197 &QAbstractEventDispatcher::aboutToBlock, 0198 this, 0199 [this] { 0200 if (d->display) { 0201 wl_display_flush(d->display); 0202 } 0203 }, 0204 Qt::DirectConnection); 0205 } 0206 0207 ConnectionThread::ConnectionThread(wl_display *display, QObject *parent) 0208 : QObject(parent) 0209 , d(new Private(this)) 0210 { 0211 d->display = display; 0212 d->foreign = true; 0213 } 0214 0215 ConnectionThread::~ConnectionThread() 0216 { 0217 disconnect(d->eventDispatcherConnection); 0218 } 0219 0220 ConnectionThread *ConnectionThread::fromApplication(QObject *parent) 0221 { 0222 QPlatformNativeInterface *native = qApp->platformNativeInterface(); 0223 if (!native) { 0224 return nullptr; 0225 } 0226 wl_display *display = reinterpret_cast<wl_display *>(native->nativeResourceForIntegration(QByteArrayLiteral("wl_display"))); 0227 if (!display) { 0228 return nullptr; 0229 } 0230 ConnectionThread *ct = new ConnectionThread(display, parent); 0231 connect(native, &QObject::destroyed, ct, &ConnectionThread::connectionDied); 0232 return ct; 0233 } 0234 0235 void ConnectionThread::initConnection() 0236 { 0237 QMetaObject::invokeMethod(this, &ConnectionThread::doInitConnection, Qt::QueuedConnection); 0238 } 0239 0240 void ConnectionThread::doInitConnection() 0241 { 0242 d->doInitConnection(); 0243 } 0244 0245 void ConnectionThread::setSocketName(const QString &socketName) 0246 { 0247 if (d->display) { 0248 // already initialized 0249 return; 0250 } 0251 d->socketName = socketName; 0252 } 0253 0254 void ConnectionThread::setSocketFd(int fd) 0255 { 0256 if (d->display) { 0257 // already initialized 0258 return; 0259 } 0260 d->fd = fd; 0261 } 0262 0263 wl_display *ConnectionThread::display() 0264 { 0265 return d->display; 0266 } 0267 0268 QString ConnectionThread::socketName() const 0269 { 0270 return d->socketName; 0271 } 0272 0273 void ConnectionThread::flush() 0274 { 0275 if (!d->display) { 0276 return; 0277 } 0278 wl_display_flush(d->display); 0279 } 0280 0281 void ConnectionThread::roundtrip() 0282 { 0283 if (!d->display) { 0284 return; 0285 } 0286 if (d->foreign) { 0287 // try to perform roundtrip through the QPA plugin if it's supported 0288 if (QPlatformNativeInterface *native = qApp->platformNativeInterface()) { 0289 // in case the platform provides a dedicated roundtrip function use that install of wl_display_roundtrip 0290 QFunctionPointer roundtripFunction = native->platformFunction(QByteArrayLiteral("roundtrip")); 0291 if (roundtripFunction) { 0292 roundtripFunction(); 0293 return; 0294 } 0295 } 0296 } 0297 wl_display_roundtrip(d->display); 0298 } 0299 0300 bool ConnectionThread::hasError() const 0301 { 0302 return d->error != 0; 0303 } 0304 0305 int ConnectionThread::errorCode() const 0306 { 0307 return d->error; 0308 } 0309 0310 QList<ConnectionThread *> ConnectionThread::connections() 0311 { 0312 return Private::connections; 0313 } 0314 0315 } 0316 } 0317 0318 #include "moc_connection_thread.cpp"