diff --git a/src/plugins/platforms/wayland/qwaylanddataoffer.cpp b/src/plugins/platforms/wayland/qwaylanddataoffer.cpp index e31e1220f46..4c06277fe87 100644 --- a/src/plugins/platforms/wayland/qwaylanddataoffer.cpp +++ b/src/plugins/platforms/wayland/qwaylanddataoffer.cpp @@ -141,7 +141,7 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T } int pipefd[2]; - if (qt_safe_pipe(pipefd, O_NONBLOCK) == -1) { + if (qt_safe_pipe(pipefd) == -1) { qWarning("QWaylandMimeData: pipe2() failed"); return QVariant(); } @@ -163,23 +163,32 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T int QWaylandMimeData::readData(int fd, QByteArray &data) const { - char buf[4096]; - int retryCount = 0; - int n; - while (true) { - n = QT_READ(fd, buf, sizeof buf); - if (n == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) && ++retryCount < 1000) - usleep(1000); - else - break; - } - if (retryCount >= 1000) + fd_set readset; + FD_ZERO(&readset); + FD_SET(fd, &readset); + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + int ready = select(FD_SETSIZE, &readset, nullptr, nullptr, &timeout); + if (ready < 0) { + qWarning() << "QWaylandDataOffer: select() failed"; + return -1; + } else if (ready == 0) { qWarning("QWaylandDataOffer: timeout reading from pipe"); - if (n > 0) { - data.append(buf, n); - n = readData(fd, data); + return -1; + } else { + char buf[4096]; + int n = QT_READ(fd, buf, sizeof buf); + + if (n > 0) { + data.append(buf, n); + n = readData(fd, data); + } else if (n < 0) { + qWarning("QWaylandDataOffer: read() failed"); + } + return n; } - return n; } } diff --git a/src/plugins/platforms/wayland/qwaylanddisplay.cpp b/src/plugins/platforms/wayland/qwaylanddisplay.cpp index cb4f190ad86..a17e8917a66 100644 --- a/src/plugins/platforms/wayland/qwaylanddisplay.cpp +++ b/src/plugins/platforms/wayland/qwaylanddisplay.cpp @@ -189,9 +189,9 @@ void QWaylandDisplay::checkError() const int ecode = wl_display_get_error(mDisplay); if ((ecode == EPIPE || ecode == ECONNRESET)) { // special case this to provide a nicer error - qWarning("The Wayland connection broke. Did the Wayland compositor die?"); + qFatal("The Wayland connection broke. Did the Wayland compositor die?"); } else { - qErrnoWarning(ecode, "The Wayland connection experienced a fatal error"); + qFatal("The Wayland connection experienced a fatal error: %s", strerror(ecode)); } } @@ -201,25 +201,16 @@ void QWaylandDisplay::flushRequests() wl_display_read_events(mDisplay); } - if (wl_display_dispatch_pending(mDisplay) < 0) { + if (wl_display_dispatch_pending(mDisplay) < 0) checkError(); - exitWithError(); - } wl_display_flush(mDisplay); } void QWaylandDisplay::blockingReadEvents() { - if (wl_display_dispatch(mDisplay) < 0) { + if (wl_display_dispatch(mDisplay) < 0) checkError(); - exitWithError(); - } -} - -void QWaylandDisplay::exitWithError() -{ - ::exit(1); } wl_event_queue *QWaylandDisplay::createEventQueue() @@ -248,10 +239,9 @@ void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::functionsurface->handleMouse(mParent, *mFrameData.event); + if (auto *event = mFrameData.event) { + if (auto window = event->surface) { + window->handleMouse(mParent, *event); + } else if (mFrameData.event->type == QWaylandPointerEvent::Type::Release) { + // If the window has been destroyed, we still need to report an up event, but it can't + // be handled by the destroyed window (obviously), so send the event here instead. + QWindowSystemInterface::handleMouseEvent(nullptr, event->timestamp, event->local, + event->global, event->buttons, event->modifiers); + } delete mFrameData.event; mFrameData.event = nullptr; } diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h index 4ac1dca3525..7fbb5667f70 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h +++ b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h @@ -446,7 +446,7 @@ public: QPoint pixelDelta; QPoint angleDelta; Qt::MouseEventSource source = Qt::MouseEventNotSynthesized; - QWaylandWindow *surface = nullptr; + QPointer surface; }; } diff --git a/src/plugins/platforms/wayland/qwaylandintegration.cpp b/src/plugins/platforms/wayland/qwaylandintegration.cpp index 7f79d6b9863..f6a80e18fde 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration.cpp +++ b/src/plugins/platforms/wayland/qwaylandintegration.cpp @@ -310,11 +310,14 @@ QPlatformTheme *QWaylandIntegration::createPlatformTheme(const QString &name) co return GenericWaylandTheme::createUnixTheme(name); } +// May be called from non-GUI threads QWaylandClientBufferIntegration *QWaylandIntegration::clientBufferIntegration() const { - if (!mClientBufferIntegrationInitialized) + // Do an inexpensive check first to avoid locking whenever possible + if (Q_UNLIKELY(!mClientBufferIntegrationInitialized)) const_cast(this)->initializeClientBufferIntegration(); + Q_ASSERT(mClientBufferIntegrationInitialized); return mClientBufferIntegration && mClientBufferIntegration->isValid() ? mClientBufferIntegration.data() : nullptr; } @@ -334,9 +337,12 @@ QWaylandShellIntegration *QWaylandIntegration::shellIntegration() const return mShellIntegration.data(); } +// May be called from non-GUI threads void QWaylandIntegration::initializeClientBufferIntegration() { - mClientBufferIntegrationInitialized = true; + QMutexLocker lock(&mClientBufferInitLock); + if (mClientBufferIntegrationInitialized) + return; QString targetKey = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_CLIENT_BUFFER_INTEGRATION")); @@ -352,22 +358,25 @@ void QWaylandIntegration::initializeClientBufferIntegration() if (targetKey.isEmpty()) { qWarning("Failed to determine what client buffer integration to use"); - return; - } - - QStringList keys = QWaylandClientBufferIntegrationFactory::keys(); - qCDebug(lcQpaWayland) << "Available client buffer integrations:" << keys; - - if (keys.contains(targetKey)) - mClientBufferIntegration.reset(QWaylandClientBufferIntegrationFactory::create(targetKey, QStringList())); - - if (mClientBufferIntegration) { - qCDebug(lcQpaWayland) << "Initializing client buffer integration" << targetKey; - mClientBufferIntegration->initialize(mDisplay.data()); } else { - qCWarning(lcQpaWayland) << "Failed to load client buffer integration:" << targetKey; - qCWarning(lcQpaWayland) << "Available client buffer integrations:" << keys; + QStringList keys = QWaylandClientBufferIntegrationFactory::keys(); + qCDebug(lcQpaWayland) << "Available client buffer integrations:" << keys; + + if (keys.contains(targetKey)) + mClientBufferIntegration.reset(QWaylandClientBufferIntegrationFactory::create(targetKey, QStringList())); + + if (mClientBufferIntegration) { + qCDebug(lcQpaWayland) << "Initializing client buffer integration" << targetKey; + mClientBufferIntegration->initialize(mDisplay.data()); + } else { + qCWarning(lcQpaWayland) << "Failed to load client buffer integration:" << targetKey; + qCWarning(lcQpaWayland) << "Available client buffer integrations:" << keys; + } } + + // This must be set last to make sure other threads don't use the + // integration before initialization is complete. + mClientBufferIntegrationInitialized = true; } void QWaylandIntegration::initializeServerBufferIntegration() diff --git a/src/plugins/platforms/wayland/qwaylandintegration_p.h b/src/plugins/platforms/wayland/qwaylandintegration_p.h index 3aef2c4d988..a66999c7f22 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration_p.h +++ b/src/plugins/platforms/wayland/qwaylandintegration_p.h @@ -54,6 +54,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -152,6 +153,7 @@ private: mutable QScopedPointer mAccessibility; #endif bool mFailed = false; + QMutex mClientBufferInitLock; bool mClientBufferIntegrationInitialized = false; bool mServerBufferIntegrationInitialized = false; bool mShellIntegrationInitialized = false; diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index ce58003cc67..60317cda725 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -205,6 +205,7 @@ void QWaylandWindow::initializeWlSurface() connect(mSurface.data(), &QWaylandSurface::screensChanged, this, &QWaylandWindow::handleScreensChanged); mSurface->m_window = this; + emit wlSurfaceCreated(); } bool QWaylandWindow::shouldCreateShellSurface() const diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index 8d8565d7761..471ace4cdb3 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -203,6 +203,7 @@ public slots: void applyConfigure(); signals: + void wlSurfaceCreated(); void wlSurfaceDestroyed(); protected: diff --git a/src/plugins/platforms/wayland/shared/qwaylandsharedmemoryformathelper_p.h b/src/plugins/platforms/wayland/shared/qwaylandsharedmemoryformathelper_p.h index 85905c02fa3..72cc8401c26 100644 --- a/src/plugins/platforms/wayland/shared/qwaylandsharedmemoryformathelper_p.h +++ b/src/plugins/platforms/wayland/shared/qwaylandsharedmemoryformathelper_p.h @@ -51,7 +51,26 @@ class QWaylandSharedMemoryFormatHelper { public: static inline wl_shm_format fromQImageFormat(QImage::Format format); - static inline QImage::Format fromWaylandShmFormat(wl_shm_format format); + static inline QImage::Format fromWaylandShmFormat(wl_shm_format format) + { + switch (format) { + case WL_SHM_FORMAT_XRGB8888: return QImage::Format_RGB32; + case WL_SHM_FORMAT_ARGB8888: return QImage::Format_ARGB32_Premultiplied; + case WL_SHM_FORMAT_RGB565: return QImage::Format_RGB16; + case WL_SHM_FORMAT_XRGB1555: return QImage::Format_RGB555; + case WL_SHM_FORMAT_RGB888: return QImage::Format_RGB888; + case WL_SHM_FORMAT_XRGB4444: return QImage::Format_RGB444; + case WL_SHM_FORMAT_ARGB4444: return QImage::Format_ARGB4444_Premultiplied; + case WL_SHM_FORMAT_XBGR8888: return QImage::Format_RGBX8888; + case WL_SHM_FORMAT_ABGR8888: return QImage::Format_RGBA8888_Premultiplied; + case WL_SHM_FORMAT_XBGR2101010: return QImage::Format_BGR30; + case WL_SHM_FORMAT_ABGR2101010: return QImage::Format_A2BGR30_Premultiplied; + case WL_SHM_FORMAT_XRGB2101010: return QImage::Format_RGB30; + case WL_SHM_FORMAT_ARGB2101010: return QImage::Format_A2RGB30_Premultiplied; + case WL_SHM_FORMAT_C8: return QImage::Format_Alpha8; + default: return QImage::Format_Invalid; + } + } static inline QVector supportedWaylandFormats(); private: @@ -108,16 +127,6 @@ wl_shm_format QWaylandSharedMemoryFormatHelper::fromQImageFormat(QImage::Format return array.data[format]; } -QImage::Format QWaylandSharedMemoryFormatHelper::fromWaylandShmFormat(wl_shm_format format) -{ - Array array = getData(); - for (size_t i = 0; i < array.size; i++) { - if (array.data[i] == format) - return QImage::Format(i); - } - return QImage::Format_Invalid; -} - QVector QWaylandSharedMemoryFormatHelper::supportedWaylandFormats() { QVector retFormats; diff --git a/tests/auto/wayland/shared/mockcompositor.h b/tests/auto/wayland/shared/mockcompositor.h index 3cb9b337c7c..fc4d7cc4640 100644 --- a/tests/auto/wayland/shared/mockcompositor.h +++ b/tests/auto/wayland/shared/mockcompositor.h @@ -85,6 +85,7 @@ public: int main(int argc, char **argv) \ { \ setenv("XDG_RUNTIME_DIR", ".", 1); \ + setenv("XDG_CURRENT_DESKTOP", "qtwaylandtests", 1); \ setenv("QT_QPA_PLATFORM", "wayland", 1); \ test tc; \ QGuiApplication app(argc, argv); \