KWayland

connection_thread.cpp
1 /*
2  SPDX-FileCopyrightText: 2014 Martin Gräßlin <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6 #include "connection_thread.h"
7 #include "logging.h"
8 // Qt
9 #include <QAbstractEventDispatcher>
10 #include <QGuiApplication>
11 #include <QDebug>
12 #include <QDir>
13 #include <QFileSystemWatcher>
14 #include <QMutex>
15 #include <QMutexLocker>
16 #include <QSocketNotifier>
17 #include <qpa/qplatformnativeinterface.h>
18 // Wayland
19 #include <wayland-client-protocol.h>
20 
21 namespace KWayland
22 {
23 
24 namespace Client
25 {
26 
27 class Q_DECL_HIDDEN ConnectionThread::Private
28 {
29 public:
30  Private(ConnectionThread *q);
31  ~Private();
32  void doInitConnection();
33  void setupSocketNotifier();
34  void setupSocketFileWatcher();
35 
36  wl_display *display = nullptr;
37  int fd = -1;
38  QString socketName;
39  QDir runtimeDir;
40  QScopedPointer<QSocketNotifier> socketNotifier;
42  bool serverDied = false;
43  bool foreign = false;
44  QMetaObject::Connection eventDispatcherConnection;
45  int error = 0;
46  static QVector<ConnectionThread*> connections;
47  static QMutex mutex;
48 private:
49  ConnectionThread *q;
50 };
51 
52 QVector<ConnectionThread*> ConnectionThread::Private::connections = QVector<ConnectionThread*>{};
53 QMutex ConnectionThread::Private::mutex{QMutex::Recursive};
54 
55 
56 ConnectionThread::Private::Private(ConnectionThread *q)
57  : socketName(QString::fromUtf8(qgetenv("WAYLAND_DISPLAY")))
58  , runtimeDir(QString::fromUtf8(qgetenv("XDG_RUNTIME_DIR")))
59  , q(q)
60 {
61  if (socketName.isEmpty()) {
62  socketName = QStringLiteral("wayland-0");
63  }
64  {
65  QMutexLocker lock(&mutex);
66  connections << q;
67  }
68 }
69 
70 ConnectionThread::Private::~Private()
71 {
72  {
73  QMutexLocker lock(&mutex);
74  connections.removeOne(q);
75  }
76  if (display && !foreign) {
77  wl_display_flush(display);
78  wl_display_disconnect(display);
79  }
80 }
81 
82 void ConnectionThread::Private::doInitConnection()
83 {
84  if (fd != -1) {
85  display = wl_display_connect_to_fd(fd);
86  } else {
87  display = wl_display_connect(socketName.toUtf8().constData());
88  }
89  if (!display) {
90  qCWarning(KWAYLAND_CLIENT) << "Failed connecting to Wayland display";
91  emit q->failed();
92  return;
93  }
94  if (fd != -1) {
95  qCDebug(KWAYLAND_CLIENT) << "Connected to Wayland server over file descriptor:" << fd;
96  } else {
97  qCDebug(KWAYLAND_CLIENT) << "Connected to Wayland server at:" << socketName;
98  }
99 
100  // setup socket notifier
101  setupSocketNotifier();
102  setupSocketFileWatcher();
103  emit q->connected();
104 }
105 
106 void ConnectionThread::Private::setupSocketNotifier()
107 {
108  const int fd = wl_display_get_fd(display);
109  socketNotifier.reset(new QSocketNotifier(fd, QSocketNotifier::Read));
110  QObject::connect(socketNotifier.data(), &QSocketNotifier::activated, q,
111  [this]() {
112  if (!display) {
113  return;
114  }
115  if (wl_display_dispatch(display) == -1) {
116  error = wl_display_get_error(display);
117  if (error != 0) {
118  if (display) {
119  free(display);
120  display = nullptr;
121  }
122  emit q->errorOccurred();
123  return;
124  }
125  }
126  emit q->eventsRead();
127  }
128  );
129 }
130 
131 void ConnectionThread::Private::setupSocketFileWatcher()
132 {
133  if (!runtimeDir.exists() || fd != -1) {
134  return;
135  }
136  socketWatcher.reset(new QFileSystemWatcher);
137  socketWatcher->addPath(runtimeDir.absoluteFilePath(socketName));
138  QObject::connect(socketWatcher.data(), &QFileSystemWatcher::fileChanged, q,
139  [this] (const QString &file) {
140  if (QFile::exists(file) || serverDied) {
141  return;
142  }
143  qCWarning(KWAYLAND_CLIENT) << "Connection to server went away";
144  serverDied = true;
145  if (display) {
146  free(display);
147  display = nullptr;
148  }
149  socketNotifier.reset();
150 
151  // need a new filesystem watcher
152  socketWatcher.reset(new QFileSystemWatcher);
153  socketWatcher->addPath(runtimeDir.absolutePath());
154  QObject::connect(socketWatcher.data(), &QFileSystemWatcher::directoryChanged, q,
155  [this]() {
156  if (!serverDied) {
157  return;
158  }
159  if (runtimeDir.exists(socketName)) {
160  qCDebug(KWAYLAND_CLIENT) << "Socket reappeared";
161  socketWatcher.reset();
162  serverDied = false;
163  error = 0;
164  q->initConnection();
165  }
166  }
167  );
168  emit q->connectionDied();
169  }
170  );
171 }
172 
173 ConnectionThread::ConnectionThread(QObject *parent)
174  : QObject(parent)
175  , d(new Private(this))
176 {
178  [this] {
179  if (d->display) {
180  wl_display_flush(d->display);
181  }
182  },
184 }
185 
186 ConnectionThread::ConnectionThread(wl_display *display, QObject *parent)
187  : QObject(parent)
188  , d(new Private(this))
189 {
190  d->display = display;
191  d->foreign = true;
192 }
193 
194 ConnectionThread::~ConnectionThread()
195 {
196  disconnect(d->eventDispatcherConnection);
197 }
198 
199 ConnectionThread *ConnectionThread::fromApplication(QObject *parent)
200 {
201  QPlatformNativeInterface *native = qApp->platformNativeInterface();
202  if (!native) {
203  return nullptr;
204  }
205  wl_display *display = reinterpret_cast<wl_display*>(native->nativeResourceForIntegration(QByteArrayLiteral("wl_display")));
206  if (!display) {
207  return nullptr;
208  }
209  ConnectionThread *ct = new ConnectionThread(display, parent);
210  connect(native, &QObject::destroyed, ct, &ConnectionThread::connectionDied);
211  return ct;
212 }
213 
214 void ConnectionThread::initConnection()
215 {
216  QMetaObject::invokeMethod(this, "doInitConnection", Qt::QueuedConnection);
217 }
218 
219 void ConnectionThread::doInitConnection()
220 {
221  d->doInitConnection();
222 }
223 
224 void ConnectionThread::setSocketName(const QString &socketName)
225 {
226  if (d->display) {
227  // already initialized
228  return;
229  }
230  d->socketName = socketName;
231 }
232 
233 void ConnectionThread::setSocketFd(int fd)
234 {
235  if (d->display) {
236  // already initialized
237  return;
238  }
239  d->fd = fd;
240 }
241 
242 wl_display *ConnectionThread::display()
243 {
244  return d->display;
245 }
246 
247 QString ConnectionThread::socketName() const
248 {
249  return d->socketName;
250 }
251 
252 void ConnectionThread::flush()
253 {
254  if (!d->display) {
255  return;
256  }
257  wl_display_flush(d->display);
258 }
259 
260 void ConnectionThread::roundtrip()
261 {
262  if (!d->display) {
263  return;
264  }
265  if (d->foreign) {
266  // try to perform roundtrip through the QPA plugin if it's supported
267  if (QPlatformNativeInterface *native = qApp->platformNativeInterface()) {
268  // in case the platform provides a dedicated roundtrip function use that install of wl_display_roundtrip
269  QFunctionPointer roundtripFunction = native->platformFunction(QByteArrayLiteral("roundtrip"));
270  if (roundtripFunction) {
271  roundtripFunction();
272  return;
273  }
274  }
275  }
276  wl_display_roundtrip(d->display);
277 }
278 
279 bool ConnectionThread::hasError() const
280 {
281  return d->error != 0;
282 }
283 
284 int ConnectionThread::errorCode() const
285 {
286  return d->error;
287 }
288 
289 QVector<ConnectionThread*> ConnectionThread::connections()
290 {
291  return Private::connections;
292 }
293 
294 }
295 }
void activated(QSocketDescriptor socket, QSocketNotifier::Type type)
bool exists() const const
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
Creates and manages the connection to a Wayland server.
void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Notify)
void fileChanged(const QString &path)
void directoryChanged(const QString &path)
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
QAbstractEventDispatcher * eventDispatcher()
Display * display()
Definition: global.cpp:79
DirectConnection
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
void destroyed(QObject *obj)
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Thu Aug 13 2020 22:50:19 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.