|
|
165ad0 |
From 91d36a2497f3289996d788c8974583bccac3c842 Mon Sep 17 00:00:00 2001
|
|
|
165ad0 |
From: Adrien Faveraux <af@brain-networks.fr>
|
|
|
165ad0 |
Date: Fri, 26 Nov 2021 09:18:58 +0100
|
|
|
165ad0 |
Subject: [PATCH 30/40] Move the wayland socket polling to a separate event
|
|
|
165ad0 |
thread
|
|
|
165ad0 |
|
|
|
165ad0 |
New event threads is introduced which calls poll() on the wayland fd,
|
|
|
165ad0 |
instead of relying on the event dispatcher by using the QSocketNotifier.
|
|
|
165ad0 |
This allows to call in the proper order the wl_display_prepare_read(),
|
|
|
165ad0 |
poll() and wl_display_read_events() functions.
|
|
|
165ad0 |
|
|
|
165ad0 |
One thread is responsible for the default queue; when needed, it emit
|
|
|
165ad0 |
a signal so that the main thread can dispatch the queue. Another thread
|
|
|
165ad0 |
is responsible for the dedicated queue for frame callbacks; this thread
|
|
|
165ad0 |
will dispatch events on the thread itself.
|
|
|
165ad0 |
|
|
|
165ad0 |
QWaylandWindow is updated to, instead of each window's dedicated event
|
|
|
165ad0 |
queue, use this queue for frame callbacks.
|
|
|
165ad0 |
|
|
|
165ad0 |
Co-authored-by: Ratchanan Srirattanamet <ratchanan@ubports.com>
|
|
|
165ad0 |
Task-number: QTBUG-66075
|
|
|
165ad0 |
Change-Id: Ibb33ad7f4193b866d1b8d7a0405a94d59dcad5eb
|
|
|
165ad0 |
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
|
|
|
165ad0 |
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
|
|
|
165ad0 |
(cherry picked from commit 92a7904d9651348b0c307e84251c8440c6f75b22)
|
|
|
165ad0 |
---
|
|
|
165ad0 |
src/client/qwaylanddisplay.cpp | 302 +++++++++++++++++++++--------
|
|
|
165ad0 |
src/client/qwaylanddisplay_p.h | 21 +-
|
|
|
165ad0 |
src/client/qwaylandintegration.cpp | 4 +-
|
|
|
165ad0 |
src/client/qwaylandwindow.cpp | 34 +++-
|
|
|
165ad0 |
src/client/qwaylandwindow_p.h | 2 +-
|
|
|
165ad0 |
5 files changed, 255 insertions(+), 108 deletions(-)
|
|
|
165ad0 |
|
|
|
165ad0 |
diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp
|
|
|
165ad0 |
index 0f75cb7e..a7ce280a 100644
|
|
|
165ad0 |
--- a/src/client/qwaylanddisplay.cpp
|
|
|
165ad0 |
+++ b/src/client/qwaylanddisplay.cpp
|
|
|
165ad0 |
@@ -85,10 +85,203 @@
|
|
|
165ad0 |
|
|
|
165ad0 |
#include <errno.h>
|
|
|
165ad0 |
|
|
|
165ad0 |
+#include <tuple> // for std::tie
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+static void checkWaylandError(struct wl_display *display)
|
|
|
165ad0 |
+{
|
|
|
165ad0 |
+ int ecode = wl_display_get_error(display);
|
|
|
165ad0 |
+ if ((ecode == EPIPE || ecode == ECONNRESET)) {
|
|
|
165ad0 |
+ // special case this to provide a nicer error
|
|
|
165ad0 |
+ qWarning("The Wayland connection broke. Did the Wayland compositor die?");
|
|
|
165ad0 |
+ } else {
|
|
|
165ad0 |
+ qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode));
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+ _exit(1);
|
|
|
165ad0 |
+}
|
|
|
165ad0 |
+
|
|
|
165ad0 |
QT_BEGIN_NAMESPACE
|
|
|
165ad0 |
|
|
|
165ad0 |
namespace QtWaylandClient {
|
|
|
165ad0 |
|
|
|
165ad0 |
+class EventThread : public QThread
|
|
|
165ad0 |
+{
|
|
|
165ad0 |
+ Q_OBJECT
|
|
|
165ad0 |
+public:
|
|
|
165ad0 |
+ enum OperatingMode {
|
|
|
165ad0 |
+ EmitToDispatch, // Emit the signal, allow dispatching in a differnt thread.
|
|
|
165ad0 |
+ SelfDispatch, // Dispatch the events inside this thread.
|
|
|
165ad0 |
+ };
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ EventThread(struct wl_display * wl, struct wl_event_queue * ev_queue,
|
|
|
165ad0 |
+ OperatingMode mode)
|
|
|
165ad0 |
+ : m_fd(wl_display_get_fd(wl))
|
|
|
165ad0 |
+ , m_pipefd{ -1, -1 }
|
|
|
165ad0 |
+ , m_wldisplay(wl)
|
|
|
165ad0 |
+ , m_wlevqueue(ev_queue)
|
|
|
165ad0 |
+ , m_mode(mode)
|
|
|
165ad0 |
+ , m_reading(true)
|
|
|
165ad0 |
+ , m_quitting(false)
|
|
|
165ad0 |
+ {
|
|
|
165ad0 |
+ setObjectName(QStringLiteral("WaylandEventThread"));
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ void readAndDispatchEvents()
|
|
|
165ad0 |
+ {
|
|
|
165ad0 |
+ /*
|
|
|
165ad0 |
+ * Dispatch pending events and flush the requests at least once. If the event thread
|
|
|
165ad0 |
+ * is not reading, try to call _prepare_read() to allow the event thread to poll().
|
|
|
165ad0 |
+ * If that fails, re-try dispatch & flush again until _prepare_read() is successful.
|
|
|
165ad0 |
+ *
|
|
|
165ad0 |
+ * This allow any call to readAndDispatchEvents() to start event thread's polling,
|
|
|
165ad0 |
+ * not only the one issued from event thread's waitForReading(), which means functions
|
|
|
165ad0 |
+ * called from dispatch_pending() can safely spin an event loop.
|
|
|
165ad0 |
+ */
|
|
|
165ad0 |
+ for (;;) {
|
|
|
165ad0 |
+ if (dispatchQueuePending() < 0) {
|
|
|
165ad0 |
+ checkWaylandError(m_wldisplay);
|
|
|
165ad0 |
+ return;
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ wl_display_flush(m_wldisplay);
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ // We have to check if event thread is reading every time we dispatch
|
|
|
165ad0 |
+ // something, as that may recursively call this function.
|
|
|
165ad0 |
+ if (m_reading.loadAcquire())
|
|
|
165ad0 |
+ break;
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ if (prepareReadQueue() == 0) {
|
|
|
165ad0 |
+ QMutexLocker l(&m_mutex);
|
|
|
165ad0 |
+ m_reading.storeRelease(true);
|
|
|
165ad0 |
+ m_cond.wakeOne();
|
|
|
165ad0 |
+ break;
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ void stop()
|
|
|
165ad0 |
+ {
|
|
|
165ad0 |
+ // We have to both write to the pipe and set the flag, as the thread may be
|
|
|
165ad0 |
+ // either in the poll() or waiting for _prepare_read().
|
|
|
165ad0 |
+ if (m_pipefd[1] != -1 && write(m_pipefd[1], "\0", 1) == -1)
|
|
|
165ad0 |
+ qWarning("Failed to write to the pipe: %s.", strerror(errno));
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ {
|
|
|
165ad0 |
+ QMutexLocker l(&m_mutex);
|
|
|
165ad0 |
+ m_quitting = true;
|
|
|
165ad0 |
+ m_cond.wakeOne();
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ wait();
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+Q_SIGNALS:
|
|
|
165ad0 |
+ void needReadAndDispatch();
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+protected:
|
|
|
165ad0 |
+ void run() override
|
|
|
165ad0 |
+ {
|
|
|
165ad0 |
+ // we use this pipe to make the loop exit otherwise if we simply used a flag on the loop condition, if stop() gets
|
|
|
165ad0 |
+ // called while poll() is blocking the thread will never quit since there are no wayland messages coming anymore.
|
|
|
165ad0 |
+ struct Pipe
|
|
|
165ad0 |
+ {
|
|
|
165ad0 |
+ Pipe(int *fds)
|
|
|
165ad0 |
+ : fds(fds)
|
|
|
165ad0 |
+ {
|
|
|
165ad0 |
+ if (qt_safe_pipe(fds) != 0)
|
|
|
165ad0 |
+ qWarning("Pipe creation failed. Quitting may hang.");
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+ ~Pipe()
|
|
|
165ad0 |
+ {
|
|
|
165ad0 |
+ if (fds[0] != -1) {
|
|
|
165ad0 |
+ close(fds[0]);
|
|
|
165ad0 |
+ close(fds[1]);
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ int *fds;
|
|
|
165ad0 |
+ } pipe(m_pipefd);
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ // Make the main thread call wl_prepare_read(), dispatch the pending messages and flush the
|
|
|
165ad0 |
+ // outbound ones. Wait until it's done before proceeding, unless we're told to quit.
|
|
|
165ad0 |
+ while (waitForReading()) {
|
|
|
165ad0 |
+ pollfd fds[2] = { { m_fd, POLLIN, 0 }, { m_pipefd[0], POLLIN, 0 } };
|
|
|
165ad0 |
+ poll(fds, 2, -1);
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ if (fds[1].revents & POLLIN) {
|
|
|
165ad0 |
+ // we don't really care to read the byte that was written here since we're closing down
|
|
|
165ad0 |
+ wl_display_cancel_read(m_wldisplay);
|
|
|
165ad0 |
+ break;
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ if (fds[0].revents & POLLIN)
|
|
|
165ad0 |
+ wl_display_read_events(m_wldisplay);
|
|
|
165ad0 |
+ // The polll was succesfull and the event thread did the wl_display_read_events(). On the next iteration of the loop
|
|
|
165ad0 |
+ // the event sent to the main thread will cause it to dispatch the messages just read, unless the loop exits in which
|
|
|
165ad0 |
+ // case we don't care anymore about them.
|
|
|
165ad0 |
+ else
|
|
|
165ad0 |
+ wl_display_cancel_read(m_wldisplay);
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+private:
|
|
|
165ad0 |
+ bool waitForReading()
|
|
|
165ad0 |
+ {
|
|
|
165ad0 |
+ Q_ASSERT(QThread::currentThread() == this);
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ m_reading.storeRelease(false);
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ if (m_mode == SelfDispatch) {
|
|
|
165ad0 |
+ readAndDispatchEvents();
|
|
|
165ad0 |
+ } else {
|
|
|
165ad0 |
+ Q_EMIT needReadAndDispatch();
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ QMutexLocker lock(&m_mutex);
|
|
|
165ad0 |
+ // m_reading might be set from our emit or some other invocation of
|
|
|
165ad0 |
+ // readAndDispatchEvents().
|
|
|
165ad0 |
+ while (!m_reading.loadRelaxed() && !m_quitting)
|
|
|
165ad0 |
+ m_cond.wait(&m_mutex);
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ return !m_quitting;
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ int dispatchQueuePending()
|
|
|
165ad0 |
+ {
|
|
|
165ad0 |
+ if (m_wlevqueue)
|
|
|
165ad0 |
+ return wl_display_dispatch_queue_pending(m_wldisplay, m_wlevqueue);
|
|
|
165ad0 |
+ else
|
|
|
165ad0 |
+ return wl_display_dispatch_pending(m_wldisplay);
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ int prepareReadQueue()
|
|
|
165ad0 |
+ {
|
|
|
165ad0 |
+ if (m_wlevqueue)
|
|
|
165ad0 |
+ return wl_display_prepare_read_queue(m_wldisplay, m_wlevqueue);
|
|
|
165ad0 |
+ else
|
|
|
165ad0 |
+ return wl_display_prepare_read(m_wldisplay);
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ int m_fd;
|
|
|
165ad0 |
+ int m_pipefd[2];
|
|
|
165ad0 |
+ wl_display *m_wldisplay;
|
|
|
165ad0 |
+ wl_event_queue *m_wlevqueue;
|
|
|
165ad0 |
+ OperatingMode m_mode;
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ /* Concurrency note when operating in EmitToDispatch mode:
|
|
|
165ad0 |
+ * m_reading is set to false inside event thread's waitForReading(), and is
|
|
|
165ad0 |
+ * set to true inside main thread's readAndDispatchEvents().
|
|
|
165ad0 |
+ * The lock is not taken when setting m_reading to false, as the main thread
|
|
|
165ad0 |
+ * is not actively waiting for it to turn false. However, the lock is taken
|
|
|
165ad0 |
+ * inside readAndDispatchEvents() before setting m_reading to true,
|
|
|
165ad0 |
+ * as the event thread is actively waiting for it under the wait condition.
|
|
|
165ad0 |
+ */
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ QAtomicInteger<bool> m_reading;
|
|
|
165ad0 |
+ bool m_quitting;
|
|
|
165ad0 |
+ QMutex m_mutex;
|
|
|
165ad0 |
+ QWaitCondition m_cond;
|
|
|
165ad0 |
+};
|
|
|
165ad0 |
+
|
|
|
165ad0 |
Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland"); // for general (uncategorized) Wayland platform logging
|
|
|
165ad0 |
|
|
|
165ad0 |
struct wl_surface *QWaylandDisplay::createSurface(void *handle)
|
|
|
165ad0 |
@@ -162,6 +355,12 @@ QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration)
|
|
|
165ad0 |
|
|
|
165ad0 |
QWaylandDisplay::~QWaylandDisplay(void)
|
|
|
165ad0 |
{
|
|
|
165ad0 |
+ if (m_eventThread)
|
|
|
165ad0 |
+ m_eventThread->stop();
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ if (m_frameEventQueueThread)
|
|
|
165ad0 |
+ m_frameEventQueueThread->stop();
|
|
|
165ad0 |
+
|
|
|
165ad0 |
if (mSyncCallback)
|
|
|
165ad0 |
wl_callback_destroy(mSyncCallback);
|
|
|
165ad0 |
|
|
|
165ad0 |
@@ -208,98 +407,37 @@ void QWaylandDisplay::ensureScreen()
|
|
|
165ad0 |
|
|
|
165ad0 |
void QWaylandDisplay::checkError() const
|
|
|
165ad0 |
{
|
|
|
165ad0 |
- int ecode = wl_display_get_error(mDisplay);
|
|
|
165ad0 |
- if ((ecode == EPIPE || ecode == ECONNRESET)) {
|
|
|
165ad0 |
- // special case this to provide a nicer error
|
|
|
165ad0 |
- qWarning("The Wayland connection broke. Did the Wayland compositor die?");
|
|
|
165ad0 |
- } else {
|
|
|
165ad0 |
- qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode));
|
|
|
165ad0 |
- }
|
|
|
165ad0 |
- _exit(1);
|
|
|
165ad0 |
+ checkWaylandError(mDisplay);
|
|
|
165ad0 |
}
|
|
|
165ad0 |
|
|
|
165ad0 |
+// Called in main thread, either from queued signal or directly.
|
|
|
165ad0 |
void QWaylandDisplay::flushRequests()
|
|
|
165ad0 |
{
|
|
|
165ad0 |
- if (wl_display_prepare_read(mDisplay) == 0) {
|
|
|
165ad0 |
- wl_display_read_events(mDisplay);
|
|
|
165ad0 |
- }
|
|
|
165ad0 |
-
|
|
|
165ad0 |
- if (wl_display_dispatch_pending(mDisplay) < 0)
|
|
|
165ad0 |
- checkError();
|
|
|
165ad0 |
-
|
|
|
165ad0 |
- {
|
|
|
165ad0 |
- QReadLocker locker(&m_frameQueueLock);
|
|
|
165ad0 |
- for (const FrameQueue &q : mExternalQueues) {
|
|
|
165ad0 |
- QMutexLocker locker(q.mutex);
|
|
|
165ad0 |
- while (wl_display_prepare_read_queue(mDisplay, q.queue) != 0)
|
|
|
165ad0 |
- wl_display_dispatch_queue_pending(mDisplay, q.queue);
|
|
|
165ad0 |
- wl_display_read_events(mDisplay);
|
|
|
165ad0 |
- wl_display_dispatch_queue_pending(mDisplay, q.queue);
|
|
|
165ad0 |
- }
|
|
|
165ad0 |
- }
|
|
|
165ad0 |
-
|
|
|
165ad0 |
- wl_display_flush(mDisplay);
|
|
|
165ad0 |
-}
|
|
|
165ad0 |
-
|
|
|
165ad0 |
-void QWaylandDisplay::blockingReadEvents()
|
|
|
165ad0 |
-{
|
|
|
165ad0 |
- if (wl_display_dispatch(mDisplay) < 0)
|
|
|
165ad0 |
- checkError();
|
|
|
165ad0 |
-}
|
|
|
165ad0 |
-
|
|
|
165ad0 |
-void QWaylandDisplay::destroyFrameQueue(const QWaylandDisplay::FrameQueue &q)
|
|
|
165ad0 |
-{
|
|
|
165ad0 |
- QWriteLocker locker(&m_frameQueueLock);
|
|
|
165ad0 |
- auto it = std::find_if(mExternalQueues.begin(),
|
|
|
165ad0 |
- mExternalQueues.end(),
|
|
|
165ad0 |
- [&q] (const QWaylandDisplay::FrameQueue &other){ return other.queue == q.queue; });
|
|
|
165ad0 |
- Q_ASSERT(it != mExternalQueues.end());
|
|
|
165ad0 |
- mExternalQueues.erase(it);
|
|
|
165ad0 |
- if (q.queue != nullptr)
|
|
|
165ad0 |
- wl_event_queue_destroy(q.queue);
|
|
|
165ad0 |
- delete q.mutex;
|
|
|
165ad0 |
+ m_eventThread->readAndDispatchEvents();
|
|
|
165ad0 |
}
|
|
|
165ad0 |
|
|
|
165ad0 |
-QWaylandDisplay::FrameQueue QWaylandDisplay::createFrameQueue()
|
|
|
165ad0 |
+// We have to wait until we have an eventDispatcher before creating the eventThread,
|
|
|
165ad0 |
+// otherwise forceRoundTrip() may block inside _events_read() because eventThread is
|
|
|
165ad0 |
+// polling.
|
|
|
165ad0 |
+void QWaylandDisplay::initEventThread()
|
|
|
165ad0 |
{
|
|
|
165ad0 |
- QWriteLocker locker(&m_frameQueueLock);
|
|
|
165ad0 |
- FrameQueue q{createEventQueue()};
|
|
|
165ad0 |
- mExternalQueues.append(q);
|
|
|
165ad0 |
- return q;
|
|
|
165ad0 |
-}
|
|
|
165ad0 |
+ m_eventThread.reset(
|
|
|
165ad0 |
+ new EventThread(mDisplay, /* default queue */ nullptr, EventThread::EmitToDispatch));
|
|
|
165ad0 |
+ connect(m_eventThread.get(), &EventThread::needReadAndDispatch, this,
|
|
|
165ad0 |
+ &QWaylandDisplay::flushRequests, Qt::QueuedConnection);
|
|
|
165ad0 |
+ m_eventThread->start();
|
|
|
165ad0 |
|
|
|
165ad0 |
-wl_event_queue *QWaylandDisplay::createEventQueue()
|
|
|
165ad0 |
-{
|
|
|
165ad0 |
- return wl_display_create_queue(mDisplay);
|
|
|
165ad0 |
+ // wl_display_disconnect() free this.
|
|
|
165ad0 |
+ m_frameEventQueue = wl_display_create_queue(mDisplay);
|
|
|
165ad0 |
+ m_frameEventQueueThread.reset(
|
|
|
165ad0 |
+ new EventThread(mDisplay, m_frameEventQueue, EventThread::SelfDispatch));
|
|
|
165ad0 |
+ m_frameEventQueueThread->start();
|
|
|
165ad0 |
}
|
|
|
165ad0 |
|
|
|
165ad0 |
-void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function<bool ()> condition, int timeout)
|
|
|
165ad0 |
+void QWaylandDisplay::blockingReadEvents()
|
|
|
165ad0 |
{
|
|
|
165ad0 |
- if (!condition())
|
|
|
165ad0 |
- return;
|
|
|
165ad0 |
-
|
|
|
165ad0 |
- QElapsedTimer timer;
|
|
|
165ad0 |
- timer.start();
|
|
|
165ad0 |
- struct pollfd pFd = qt_make_pollfd(wl_display_get_fd(mDisplay), POLLIN);
|
|
|
165ad0 |
- while (timeout == -1 || timer.elapsed() < timeout) {
|
|
|
165ad0 |
- while (wl_display_prepare_read_queue(mDisplay, queue) != 0)
|
|
|
165ad0 |
- wl_display_dispatch_queue_pending(mDisplay, queue);
|
|
|
165ad0 |
-
|
|
|
165ad0 |
- wl_display_flush(mDisplay);
|
|
|
165ad0 |
-
|
|
|
165ad0 |
- const int remaining = qMax(timeout - timer.elapsed(), 0ll);
|
|
|
165ad0 |
- const int pollTimeout = timeout == -1 ? -1 : remaining;
|
|
|
165ad0 |
- if (qt_poll_msecs(&pFd, 1, pollTimeout) > 0)
|
|
|
165ad0 |
- wl_display_read_events(mDisplay);
|
|
|
165ad0 |
- else
|
|
|
165ad0 |
- wl_display_cancel_read(mDisplay);
|
|
|
165ad0 |
-
|
|
|
165ad0 |
- if (wl_display_dispatch_queue_pending(mDisplay, queue) < 0)
|
|
|
165ad0 |
- checkError();
|
|
|
165ad0 |
-
|
|
|
165ad0 |
- if (!condition())
|
|
|
165ad0 |
- break;
|
|
|
165ad0 |
- }
|
|
|
165ad0 |
+ if (wl_display_dispatch(mDisplay) < 0)
|
|
|
165ad0 |
+ checkWaylandError(mDisplay);
|
|
|
165ad0 |
}
|
|
|
165ad0 |
|
|
|
165ad0 |
QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const
|
|
|
165ad0 |
@@ -674,4 +812,6 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int p
|
|
|
165ad0 |
|
|
|
165ad0 |
} // namespace QtWaylandClient
|
|
|
165ad0 |
|
|
|
165ad0 |
+#include "qwaylanddisplay.moc"
|
|
|
165ad0 |
+
|
|
|
165ad0 |
QT_END_NAMESPACE
|
|
|
165ad0 |
diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h
|
|
|
165ad0 |
index d9c8849f..42bc661d 100644
|
|
|
165ad0 |
--- a/src/client/qwaylanddisplay_p.h
|
|
|
165ad0 |
+++ b/src/client/qwaylanddisplay_p.h
|
|
|
165ad0 |
@@ -109,6 +109,7 @@ class QWaylandSurface;
|
|
|
165ad0 |
class QWaylandShellIntegration;
|
|
|
165ad0 |
class QWaylandCursor;
|
|
|
165ad0 |
class QWaylandCursorTheme;
|
|
|
165ad0 |
+class EventThread;
|
|
|
165ad0 |
|
|
|
165ad0 |
typedef void (*RegistryListener)(void *data,
|
|
|
165ad0 |
struct wl_registry *registry,
|
|
|
165ad0 |
@@ -120,12 +121,6 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandDisplay : public QObject, public QtWayland
|
|
|
165ad0 |
Q_OBJECT
|
|
|
165ad0 |
|
|
|
165ad0 |
public:
|
|
|
165ad0 |
- struct FrameQueue {
|
|
|
165ad0 |
- FrameQueue(wl_event_queue *q = nullptr) : queue(q), mutex(new QMutex) {}
|
|
|
165ad0 |
- wl_event_queue *queue;
|
|
|
165ad0 |
- QMutex *mutex;
|
|
|
165ad0 |
- };
|
|
|
165ad0 |
-
|
|
|
165ad0 |
QWaylandDisplay(QWaylandIntegration *waylandIntegration);
|
|
|
165ad0 |
~QWaylandDisplay(void) override;
|
|
|
165ad0 |
|
|
|
165ad0 |
@@ -212,12 +207,11 @@ public:
|
|
|
165ad0 |
void handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice);
|
|
|
165ad0 |
void handleWindowDestroyed(QWaylandWindow *window);
|
|
|
165ad0 |
|
|
|
165ad0 |
- wl_event_queue *createEventQueue();
|
|
|
165ad0 |
- FrameQueue createFrameQueue();
|
|
|
165ad0 |
- void destroyFrameQueue(const FrameQueue &q);
|
|
|
165ad0 |
- void dispatchQueueWhile(wl_event_queue *queue, std::function<bool()> condition, int timeout = -1);
|
|
|
165ad0 |
+ wl_event_queue *frameEventQueue() { return m_frameEventQueue; };
|
|
|
165ad0 |
|
|
|
165ad0 |
bool isKeyboardAvailable() const;
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ void initEventThread();
|
|
|
165ad0 |
public slots:
|
|
|
165ad0 |
void blockingReadEvents();
|
|
|
165ad0 |
void flushRequests();
|
|
|
165ad0 |
@@ -240,6 +234,9 @@ private:
|
|
|
165ad0 |
};
|
|
|
165ad0 |
|
|
|
165ad0 |
struct wl_display *mDisplay = nullptr;
|
|
|
165ad0 |
+ QScopedPointer<EventThread> m_eventThread;
|
|
|
165ad0 |
+ wl_event_queue *m_frameEventQueue = nullptr;
|
|
|
165ad0 |
+ QScopedPointer<EventThread> m_frameEventQueueThread;
|
|
|
165ad0 |
QtWayland::wl_compositor mCompositor;
|
|
|
165ad0 |
QScopedPointer<QWaylandShm> mShm;
|
|
|
165ad0 |
QList<QWaylandScreen *> mWaitingScreens;
|
|
|
165ad0 |
@@ -276,11 +273,9 @@ private:
|
|
|
165ad0 |
QWaylandInputDevice *mLastInputDevice = nullptr;
|
|
|
165ad0 |
QPointer<QWaylandWindow> mLastInputWindow;
|
|
|
165ad0 |
QPointer<QWaylandWindow> mLastKeyboardFocus;
|
|
|
165ad0 |
- QVector<QWaylandWindow *> mActiveWindows;
|
|
|
165ad0 |
- QVector<FrameQueue> mExternalQueues;
|
|
|
165ad0 |
+ QList<QWaylandWindow *> mActiveWindows;
|
|
|
165ad0 |
struct wl_callback *mSyncCallback = nullptr;
|
|
|
165ad0 |
static const wl_callback_listener syncCallbackListener;
|
|
|
165ad0 |
- QReadWriteLock m_frameQueueLock;
|
|
|
165ad0 |
|
|
|
165ad0 |
bool mClientSideInputContextRequested = !QPlatformInputContextFactory::requested().isNull();
|
|
|
165ad0 |
|
|
|
165ad0 |
diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp
|
|
|
165ad0 |
index 3a6fa651..3b876047 100644
|
|
|
165ad0 |
--- a/src/client/qwaylandintegration.cpp
|
|
|
165ad0 |
+++ b/src/client/qwaylandintegration.cpp
|
|
|
165ad0 |
@@ -192,9 +192,7 @@ QAbstractEventDispatcher *QWaylandIntegration::createEventDispatcher() const
|
|
|
165ad0 |
|
|
|
165ad0 |
void QWaylandIntegration::initialize()
|
|
|
165ad0 |
{
|
|
|
165ad0 |
- int fd = wl_display_get_fd(mDisplay->wl_display());
|
|
|
165ad0 |
- QSocketNotifier *sn = new QSocketNotifier(fd, QSocketNotifier::Read, mDisplay.data());
|
|
|
165ad0 |
- QObject::connect(sn, SIGNAL(activated(QSocketDescriptor)), mDisplay.data(), SLOT(flushRequests()));
|
|
|
165ad0 |
+ mDisplay->initEventThread();
|
|
|
165ad0 |
|
|
|
165ad0 |
// Call after eventDispatcher is fully connected, for QWaylandDisplay::forceRoundTrip()
|
|
|
165ad0 |
mDisplay->initialize();
|
|
|
165ad0 |
diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp
|
|
|
165ad0 |
index 1597f67e..7de19a74 100644
|
|
|
165ad0 |
--- a/src/client/qwaylandwindow.cpp
|
|
|
165ad0 |
+++ b/src/client/qwaylandwindow.cpp
|
|
|
165ad0 |
@@ -76,7 +76,6 @@ QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr;
|
|
|
165ad0 |
QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display)
|
|
|
165ad0 |
: QPlatformWindow(window)
|
|
|
165ad0 |
, mDisplay(display)
|
|
|
165ad0 |
- , mFrameQueue(mDisplay->createFrameQueue())
|
|
|
165ad0 |
, mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP"))
|
|
|
165ad0 |
{
|
|
|
165ad0 |
{
|
|
|
165ad0 |
@@ -95,8 +94,6 @@ QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display)
|
|
|
165ad0 |
|
|
|
165ad0 |
QWaylandWindow::~QWaylandWindow()
|
|
|
165ad0 |
{
|
|
|
165ad0 |
- mDisplay->destroyFrameQueue(mFrameQueue);
|
|
|
165ad0 |
-
|
|
|
165ad0 |
delete mWindowDecoration;
|
|
|
165ad0 |
|
|
|
165ad0 |
if (mSurface)
|
|
|
165ad0 |
@@ -635,6 +632,8 @@ const wl_callback_listener QWaylandWindow::callbackListener = {
|
|
|
165ad0 |
|
|
|
165ad0 |
void QWaylandWindow::handleFrameCallback()
|
|
|
165ad0 |
{
|
|
|
165ad0 |
+ QMutexLocker locker(&mFrameSyncMutex);
|
|
|
165ad0 |
+
|
|
|
165ad0 |
mWaitingForFrameCallback = false;
|
|
|
165ad0 |
mFrameCallbackElapsedTimer.invalidate();
|
|
|
165ad0 |
|
|
|
165ad0 |
@@ -656,12 +655,16 @@ void QWaylandWindow::handleFrameCallback()
|
|
|
165ad0 |
mWaitingForUpdateDelivery = true;
|
|
|
165ad0 |
QMetaObject::invokeMethod(this, doHandleExpose, Qt::QueuedConnection);
|
|
|
165ad0 |
}
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ mFrameSyncWait.notify_all();
|
|
|
165ad0 |
}
|
|
|
165ad0 |
|
|
|
165ad0 |
bool QWaylandWindow::waitForFrameSync(int timeout)
|
|
|
165ad0 |
{
|
|
|
165ad0 |
- QMutexLocker locker(mFrameQueue.mutex);
|
|
|
165ad0 |
- mDisplay->dispatchQueueWhile(mFrameQueue.queue, [&]() { return mWaitingForFrameCallback; }, timeout);
|
|
|
165ad0 |
+ QMutexLocker locker(&mFrameSyncMutex);
|
|
|
165ad0 |
+
|
|
|
165ad0 |
+ QDeadlineTimer deadline(timeout);
|
|
|
165ad0 |
+ while (mWaitingForFrameCallback && mFrameSyncWait.wait(&mFrameSyncMutex, deadline)) { }
|
|
|
165ad0 |
|
|
|
165ad0 |
if (mWaitingForFrameCallback) {
|
|
|
165ad0 |
qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
|
|
|
165ad0 |
@@ -1157,8 +1160,11 @@ void QWaylandWindow::requestUpdate()
|
|
|
165ad0 |
Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA
|
|
|
165ad0 |
|
|
|
165ad0 |
// If we have a frame callback all is good and will be taken care of there
|
|
|
165ad0 |
- if (mWaitingForFrameCallback)
|
|
|
165ad0 |
- return;
|
|
|
165ad0 |
+ {
|
|
|
165ad0 |
+ QMutexLocker locker(&mFrameSyncMutex);
|
|
|
165ad0 |
+ if (mWaitingForFrameCallback)
|
|
|
165ad0 |
+ return;
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
|
|
|
165ad0 |
// If we've already called deliverUpdateRequest(), but haven't seen any attach+commit/swap yet
|
|
|
165ad0 |
// This is a somewhat redundant behavior and might indicate a bug in the calling code, so log
|
|
|
165ad0 |
@@ -1171,7 +1177,12 @@ void QWaylandWindow::requestUpdate()
|
|
|
165ad0 |
// so use invokeMethod to delay the delivery a bit.
|
|
|
165ad0 |
QMetaObject::invokeMethod(this, [this] {
|
|
|
165ad0 |
// Things might have changed in the meantime
|
|
|
165ad0 |
- if (hasPendingUpdateRequest() && !mWaitingForFrameCallback)
|
|
|
165ad0 |
+ {
|
|
|
165ad0 |
+ QMutexLocker locker(&mFrameSyncMutex);
|
|
|
165ad0 |
+ if (mWaitingForFrameCallback)
|
|
|
165ad0 |
+ return;
|
|
|
165ad0 |
+ }
|
|
|
165ad0 |
+ if (hasPendingUpdateRequest())
|
|
|
165ad0 |
deliverUpdateRequest();
|
|
|
165ad0 |
}, Qt::QueuedConnection);
|
|
|
165ad0 |
}
|
|
|
165ad0 |
@@ -1191,9 +1202,10 @@ void QWaylandWindow::handleUpdate()
|
|
|
165ad0 |
if (!mSurface)
|
|
|
165ad0 |
return;
|
|
|
165ad0 |
|
|
|
165ad0 |
- QMutexLocker locker(mFrameQueue.mutex);
|
|
|
165ad0 |
+ QMutexLocker locker(&mFrameSyncMutex);
|
|
|
165ad0 |
+
|
|
|
165ad0 |
struct ::wl_surface *wrappedSurface = reinterpret_cast<struct ::wl_surface *>(wl_proxy_create_wrapper(mSurface->object()));
|
|
|
165ad0 |
- wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(wrappedSurface), mFrameQueue.queue);
|
|
|
165ad0 |
+ wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(wrappedSurface), mDisplay->frameEventQueue());
|
|
|
165ad0 |
mFrameCallback = wl_surface_frame(wrappedSurface);
|
|
|
165ad0 |
wl_proxy_wrapper_destroy(wrappedSurface);
|
|
|
165ad0 |
wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
|
|
|
165ad0 |
@@ -1203,6 +1215,8 @@ void QWaylandWindow::handleUpdate()
|
|
|
165ad0 |
// Start a timer for handling the case when the compositor stops sending frame callbacks.
|
|
|
165ad0 |
if (mFrameCallbackTimeout > 0) {
|
|
|
165ad0 |
QMetaObject::invokeMethod(this, [this] {
|
|
|
165ad0 |
+ QMutexLocker locker(&mFrameSyncMutex);
|
|
|
165ad0 |
+
|
|
|
165ad0 |
if (mWaitingForFrameCallback) {
|
|
|
165ad0 |
if (mFrameCallbackCheckIntervalTimerId < 0)
|
|
|
165ad0 |
mFrameCallbackCheckIntervalTimerId = startTimer(mFrameCallbackTimeout);
|
|
|
165ad0 |
diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h
|
|
|
165ad0 |
index e0687962..d45980a8 100644
|
|
|
165ad0 |
--- a/src/client/qwaylandwindow_p.h
|
|
|
165ad0 |
+++ b/src/client/qwaylandwindow_p.h
|
|
|
165ad0 |
@@ -232,7 +232,7 @@ protected:
|
|
|
165ad0 |
int mFrameCallbackCheckIntervalTimerId = -1;
|
|
|
165ad0 |
QElapsedTimer mFrameCallbackElapsedTimer;
|
|
|
165ad0 |
struct ::wl_callback *mFrameCallback = nullptr;
|
|
|
165ad0 |
- QWaylandDisplay::FrameQueue mFrameQueue;
|
|
|
165ad0 |
+ QMutex mFrameSyncMutex;
|
|
|
165ad0 |
QWaitCondition mFrameSyncWait;
|
|
|
165ad0 |
|
|
|
165ad0 |
// True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer
|
|
|
165ad0 |
--
|
|
|
165ad0 |
2.35.1
|
|
|
165ad0 |
|