diff options
Diffstat (limited to 'src/client')
25 files changed, 884 insertions, 566 deletions
diff --git a/src/client/client.pro b/src/client/client.pro index 30f32dd7e..db91bd691 100644 --- a/src/client/client.pro +++ b/src/client/client.pro @@ -15,8 +15,12 @@ use_gold_linker: CONFIG += no_linker_version_script CONFIG -= precompile_header CONFIG += link_pkgconfig wayland-scanner -qtConfig(xkbcommon): \ - QMAKE_USE_PRIVATE += xkbcommon +qtConfig(xkbcommon) { + QT_FOR_PRIVATE += xkbcommon_support-private +} + +qtHaveModule(linuxaccessibility_support_private): \ + QT += linuxaccessibility_support_private QMAKE_USE += wayland-client @@ -29,9 +33,7 @@ WAYLANDCLIENTSOURCES += \ ../extensions/qt-windowmanager.xml \ ../3rdparty/protocol/text-input-unstable-v2.xml \ ../3rdparty/protocol/xdg-output-unstable-v1.xml \ - -WAYLANDCLIENTSOURCES_SYSTEM += \ - ../3rdparty/protocol/wayland.xml \ + ../3rdparty/protocol/wayland.xml SOURCES += qwaylandintegration.cpp \ qwaylandnativeinterface.cpp \ @@ -47,7 +49,6 @@ SOURCES += qwaylandintegration.cpp \ qwaylandtouch.cpp \ qwaylandqtkey.cpp \ ../shared/qwaylandmimehelper.cpp \ - ../shared/qwaylandxkb.cpp \ ../shared/qwaylandinputmethodeventbuilder.cpp \ qwaylandabstractdecoration.cpp \ qwaylanddecorationfactory.cpp \ @@ -81,7 +82,6 @@ HEADERS += qwaylandintegration_p.h \ qtwaylandclientglobal_p.h \ ../shared/qwaylandinputmethodeventbuilder_p.h \ ../shared/qwaylandmimehelper_p.h \ - ../shared/qwaylandxkb_p.h \ ../shared/qwaylandsharedmemoryformathelper_p.h \ qtConfig(clipboard) { diff --git a/src/client/configure.json b/src/client/configure.json index 586da6f66..93c5d4e49 100644 --- a/src/client/configure.json +++ b/src/client/configure.json @@ -88,6 +88,36 @@ "condition": "features.draganddrop || features.clipboard", "output": [ "privateFeature" ] }, + "wayland-client-fullscreen-shell-v1": { + "label": "fullscreen-shell-v1", + "condition": "features.wayland-client", + "output": [ "privateFeature" ] + }, + "wayland-client-ivi-shell": { + "label": "ivi-shell", + "condition": "features.wayland-client", + "output": [ "privateFeature" ] + }, + "wayland-client-wl-shell": { + "label": "wl-shell (deprecated)", + "condition": "features.wayland-client", + "output": [ "privateFeature" ] + }, + "wayland-client-xdg-shell": { + "label": "xdg-shell", + "condition": "features.wayland-client", + "output": [ "privateFeature" ] + }, + "wayland-client-xdg-shell-v5": { + "label": "xdg-shell unstable v5 (deprecated)", + "condition": "features.wayland-client", + "output": [ "privateFeature" ] + }, + "wayland-client-xdg-shell-v6": { + "label": "xdg-shell unstable v6", + "condition": "features.wayland-client", + "output": [ "privateFeature" ] + }, "wayland-egl": { "label": "EGL", "condition": "features.wayland-client && features.opengl && features.egl && libs.wayland-egl", @@ -152,6 +182,17 @@ "wayland-shm-emulation-server-buffer" ] }, + { + "section": "Qt Wayland Client Shell Integrations", + "condition": "features.wayland-client", + "entries": [ + "wayland-client-xdg-shell", + "wayland-client-xdg-shell-v5", + "wayland-client-xdg-shell-v6", + "wayland-client-ivi-shell", + "wayland-client-wl-shell" + ] + }, "wayland-client" ] } diff --git a/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h b/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h index 7439087d8..632429bef 100644 --- a/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h +++ b/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h @@ -85,7 +85,7 @@ public: void *userData() const; protected: - Format m_format; + Format m_format = RGBA32; QSize m_size; private: diff --git a/src/client/qwaylandabstractdecoration.cpp b/src/client/qwaylandabstractdecoration.cpp index 98a0b17d5..87dd6cea0 100644 --- a/src/client/qwaylandabstractdecoration.cpp +++ b/src/client/qwaylandabstractdecoration.cpp @@ -120,16 +120,17 @@ const QImage &QWaylandAbstractDecoration::contentImage() { Q_D(QWaylandAbstractDecoration); if (d->m_isDirty) { - //Update the decoration backingstore + // Update the decoration backingstore - const int scale = waylandWindow()->scale(); - const QSize imageSize = window()->frameGeometry().size() * scale; + const int bufferScale = waylandWindow()->scale(); + const QSize imageSize = waylandWindow()->surfaceSize() * bufferScale; d->m_decorationContentImage = QImage(imageSize, QImage::Format_ARGB32_Premultiplied); - d->m_decorationContentImage.setDevicePixelRatio(scale); + // Only scale by buffer scale, not QT_SCALE_FACTOR etc. + d->m_decorationContentImage.setDevicePixelRatio(bufferScale); d->m_decorationContentImage.fill(Qt::transparent); this->paint(&d->m_decorationContentImage); - QRegion damage = marginsRegion(window()->geometry().size(), window()->frameMargins()); + QRegion damage = marginsRegion(waylandWindow()->surfaceSize(), waylandWindow()->frameMargins()); for (QRect r : damage) waylandWindow()->damage(r); @@ -151,11 +152,11 @@ void QWaylandAbstractDecoration::setMouseButtons(Qt::MouseButtons mb) d->m_mouseButtons = mb; } -void QWaylandAbstractDecoration::startResize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize resize, Qt::MouseButtons buttons) +void QWaylandAbstractDecoration::startResize(QWaylandInputDevice *inputDevice, Qt::Edges edges, Qt::MouseButtons buttons) { Q_D(QWaylandAbstractDecoration); if (isLeftClicked(buttons) && d->m_wayland_window->shellSurface()) { - d->m_wayland_window->shellSurface()->resize(inputDevice, resize); + d->m_wayland_window->shellSurface()->resize(inputDevice, edges); inputDevice->removeMouseButtonFromState(Qt::LeftButton); } } @@ -169,20 +170,29 @@ void QWaylandAbstractDecoration::startMove(QWaylandInputDevice *inputDevice, Qt: } } +void QWaylandAbstractDecoration::showWindowMenu(QWaylandInputDevice *inputDevice) +{ + Q_D(QWaylandAbstractDecoration); + if (auto *s = d->m_wayland_window->shellSurface()) + s->showWindowMenu(inputDevice); +} + bool QWaylandAbstractDecoration::isLeftClicked(Qt::MouseButtons newMouseButtonState) { Q_D(QWaylandAbstractDecoration); - if (!(d->m_mouseButtons & Qt::LeftButton) && (newMouseButtonState & Qt::LeftButton)) - return true; - return false; + return !(d->m_mouseButtons & Qt::LeftButton) && (newMouseButtonState & Qt::LeftButton); +} + +bool QWaylandAbstractDecoration::isRightClicked(Qt::MouseButtons newMouseButtonState) +{ + Q_D(QWaylandAbstractDecoration); + return !(d->m_mouseButtons & Qt::RightButton) && (newMouseButtonState & Qt::RightButton); } bool QWaylandAbstractDecoration::isLeftReleased(Qt::MouseButtons newMouseButtonState) { Q_D(QWaylandAbstractDecoration); - if ((d->m_mouseButtons & Qt::LeftButton) && !(newMouseButtonState & Qt::LeftButton)) - return true; - return false; + return (d->m_mouseButtons & Qt::LeftButton) && !(newMouseButtonState & Qt::LeftButton); } bool QWaylandAbstractDecoration::isDirty() const diff --git a/src/client/qwaylandabstractdecoration_p.h b/src/client/qwaylandabstractdecoration_p.h index 84a6d4dd7..81c8e1771 100644 --- a/src/client/qwaylandabstractdecoration_p.h +++ b/src/client/qwaylandabstractdecoration_p.h @@ -61,8 +61,6 @@ #include <QtGui/QImage> #include <QtWaylandClient/qtwaylandclientglobal.h> -#include <wayland-client.h> - #include <QtCore/QDebug> QT_BEGIN_NAMESPACE @@ -105,10 +103,12 @@ protected: void setMouseButtons(Qt::MouseButtons mb); - void startResize(QWaylandInputDevice *inputDevice,enum wl_shell_surface_resize resize, Qt::MouseButtons buttons); + void startResize(QWaylandInputDevice *inputDevice, Qt::Edges edges, Qt::MouseButtons buttons); void startMove(QWaylandInputDevice *inputDevice, Qt::MouseButtons buttons); + void showWindowMenu(QWaylandInputDevice *inputDevice); bool isLeftClicked(Qt::MouseButtons newMouseButtonState); + bool isRightClicked(Qt::MouseButtons newMouseButtonState); bool isLeftReleased(Qt::MouseButtons newMouseButtonState); }; diff --git a/src/client/qwaylandbuffer_p.h b/src/client/qwaylandbuffer_p.h index eea090f35..945f1279a 100644 --- a/src/client/qwaylandbuffer_p.h +++ b/src/client/qwaylandbuffer_p.h @@ -56,8 +56,7 @@ #include <QtCore/QSize> #include <QtCore/QRect> -#include <wayland-client.h> -#include <wayland-client-protocol.h> +#include <QtWaylandClient/private/wayland-wayland-client-protocol.h> QT_BEGIN_NAMESPACE diff --git a/src/client/qwaylandcursor.cpp b/src/client/qwaylandcursor.cpp index 6947e97f1..8b2ed036d 100644 --- a/src/client/qwaylandcursor.cpp +++ b/src/client/qwaylandcursor.cpp @@ -41,7 +41,6 @@ #include "qwaylanddisplay_p.h" #include "qwaylandinputdevice_p.h" -#include "qwaylandscreen_p.h" #include "qwaylandshmbackingstore_p.h" #include <QtGui/QImageReader> @@ -53,12 +52,6 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -QWaylandCursorTheme *QWaylandCursorTheme::create(QWaylandShm *shm, int size) -{ - static QString themeName = qEnvironmentVariable("XCURSOR_THEME", QStringLiteral("default")); - return create(shm, size, themeName); -} - QWaylandCursorTheme *QWaylandCursorTheme::create(QWaylandShm *shm, int size, const QString &themeName) { QByteArray nameBytes = themeName.toLocal8Bit(); @@ -244,56 +237,32 @@ struct wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape) return image; } -QWaylandCursor::QWaylandCursor(QWaylandScreen *screen) - : mDisplay(screen->display()) - , mCursorTheme(mDisplay->loadCursorTheme(screen->devicePixelRatio())) +QWaylandCursor::QWaylandCursor(QWaylandDisplay *display) + : mDisplay(display) { } -QSharedPointer<QWaylandBuffer> QWaylandCursor::cursorBitmapImage(const QCursor *cursor) +QSharedPointer<QWaylandBuffer> QWaylandCursor::cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor) { - if (cursor->shape() != Qt::BitmapCursor) - return QSharedPointer<QWaylandShmBuffer>(); - + Q_ASSERT(cursor->shape() == Qt::BitmapCursor); const QImage &img = cursor->pixmap().toImage(); - QSharedPointer<QWaylandShmBuffer> buffer(new QWaylandShmBuffer(mDisplay, img.size(), img.format())); - memcpy(buffer->image()->bits(), img.bits(), img.sizeInBytes()); - return buffer; -} - -struct wl_cursor_image *QWaylandCursor::cursorImage(Qt::CursorShape shape) -{ - if (!mCursorTheme) - return nullptr; - return mCursorTheme->cursorImage(shape); + QSharedPointer<QWaylandShmBuffer> buffer(new QWaylandShmBuffer(display, img.size(), img.format())); + memcpy(buffer->image()->bits(), img.bits(), size_t(img.sizeInBytes())); + return std::move(buffer); } void QWaylandCursor::changeCursor(QCursor *cursor, QWindow *window) { - const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor; - - if (newShape == Qt::BlankCursor) { - mDisplay->setCursor(nullptr, nullptr, 1); - return; - } - - if (newShape == Qt::BitmapCursor) { - mDisplay->setCursor(cursorBitmapImage(cursor), cursor->hotSpot(), window->screen()->devicePixelRatio()); - return; - } - - if (!mCursorTheme) { - qCWarning(lcQpaWayland) << "Can't set cursor from shape with no cursor theme"; - return; - } - - if (struct ::wl_cursor_image *image = mCursorTheme->cursorImage(newShape)) { - struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); - mDisplay->setCursor(buffer, image, window->screen()->devicePixelRatio()); - return; - } - - qCWarning(lcQpaWayland) << "Unable to change to cursor" << cursor; + Q_UNUSED(window); + // Create the buffer here so we don't have to create one per input device + QSharedPointer<QWaylandBuffer> bitmapBuffer; + if (cursor && cursor->shape() == Qt::BitmapCursor) + bitmapBuffer = cursorBitmapBuffer(mDisplay, cursor); + + int fallbackOutputScale = int(window->devicePixelRatio()); + const auto seats = mDisplay->inputDevices(); + for (auto *seat : seats) + seat->setCursor(cursor, bitmapBuffer, fallbackOutputScale); } void QWaylandCursor::pointerEvent(const QMouseEvent &event) @@ -312,6 +281,6 @@ void QWaylandCursor::setPos(const QPoint &pos) qCWarning(lcQpaWayland) << "Setting cursor position is not possible on wayland"; } -} +} // namespace QtWaylandClient QT_END_NAMESPACE diff --git a/src/client/qwaylandcursor_p.h b/src/client/qwaylandcursor_p.h index 71f9cd1b8..6c48fb628 100644 --- a/src/client/qwaylandcursor_p.h +++ b/src/client/qwaylandcursor_p.h @@ -73,7 +73,6 @@ class QWaylandShm; class Q_WAYLAND_CLIENT_EXPORT QWaylandCursorTheme { public: - static QWaylandCursorTheme *create(QWaylandShm *shm, int size); static QWaylandCursorTheme *create(QWaylandShm *shm, int size, const QString &themeName); ~QWaylandCursorTheme(); struct wl_cursor_image *cursorImage(Qt::CursorShape shape); @@ -122,19 +121,18 @@ private: class Q_WAYLAND_CLIENT_EXPORT QWaylandCursor : public QPlatformCursor { public: - QWaylandCursor(QWaylandScreen *screen); + explicit QWaylandCursor(QWaylandDisplay *display); void changeCursor(QCursor *cursor, QWindow *window) override; void pointerEvent(const QMouseEvent &event) override; QPoint pos() const override; void setPos(const QPoint &pos) override; - QSharedPointer<QWaylandBuffer> cursorBitmapImage(const QCursor *cursor); + static QSharedPointer<QWaylandBuffer> cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor); struct wl_cursor_image *cursorImage(Qt::CursorShape shape); private: QWaylandDisplay *mDisplay = nullptr; - QWaylandCursorTheme *mCursorTheme = nullptr; QPoint mLastPos; }; diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp index 82003a308..4a91c1c96 100644 --- a/src/client/qwaylanddisplay.cpp +++ b/src/client/qwaylanddisplay.cpp @@ -50,6 +50,7 @@ #endif #if QT_CONFIG(wayland_datadevice) #include "qwaylanddatadevicemanager_p.h" +#include "qwaylanddatadevice_p.h" #endif #if QT_CONFIG(cursor) #include <wayland-cursor.h> @@ -71,6 +72,7 @@ #include <QtCore/private/qcore_unix_p.h> #include <QtCore/QAbstractEventDispatcher> +#include <QtGui/qpa/qwindowsysteminterface.h> #include <QtGui/private/qguiapplication_p.h> #include <QtCore/QDebug> @@ -141,7 +143,18 @@ QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration) mWindowManagerIntegration.reset(new QWaylandWindowManagerIntegration(this)); +#if QT_CONFIG(xkbcommon) + mXkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS)); + if (!mXkbContext) + qCWarning(lcQpaWayland, "failed to create xkb context"); +#endif + forceRoundTrip(); + + if (!mWaitingScreens.isEmpty()) { + // Give wl_output.done and zxdg_output_v1.done events a chance to arrive + forceRoundTrip(); + } } QWaylandDisplay::~QWaylandDisplay(void) @@ -153,15 +166,16 @@ QWaylandDisplay::~QWaylandDisplay(void) mInputDevices.clear(); foreach (QWaylandScreen *screen, mScreens) { - mWaylandIntegration->destroyScreen(screen); + QWindowSystemInterface::handleScreenRemoved(screen); } mScreens.clear(); + qDeleteAll(mWaitingScreens); #if QT_CONFIG(wayland_datadevice) delete mDndSelectionHandler.take(); #endif #if QT_CONFIG(cursor) - qDeleteAll(mCursorThemesBySize); + qDeleteAll(mCursorThemes); #endif if (mDisplay) wl_display_disconnect(mDisplay); @@ -250,6 +264,14 @@ QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const return nullptr; } +void QWaylandDisplay::handleScreenInitialized(QWaylandScreen *screen) +{ + if (!mWaitingScreens.removeOne(screen)) + return; + mScreens.append(screen); + QWindowSystemInterface::handleScreenAdded(screen); +} + void QWaylandDisplay::waitForScreens() { flushRequests(); @@ -271,16 +293,10 @@ void QWaylandDisplay::waitForScreens() void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uint32_t version) { - Q_UNUSED(version); - struct ::wl_registry *registry = object(); if (interface == QStringLiteral("wl_output")) { - QWaylandScreen *screen = new QWaylandScreen(this, version, id); - mScreens.append(screen); - // We need to get the output events before creating surfaces - forceRoundTrip(); - mWaylandIntegration->screenAdded(screen); + mWaitingScreens << new QWaylandScreen(this, version, id); } else if (interface == QStringLiteral("wl_compositor")) { mCompositorVersion = qMin((int)version, 3); mCompositor.init(registry, id, mCompositorVersion); @@ -301,19 +317,22 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin mTouchExtension.reset(new QWaylandTouchExtension(this, id)); } else if (interface == QStringLiteral("zqt_key_v1")) { mQtKeyExtension.reset(new QWaylandQtKeyExtension(this, id)); - } else if (interface == QStringLiteral("zwp_text_input_manager_v2")) { + } else if (interface == QStringLiteral("zwp_text_input_manager_v2") && !mClientSideInputContextRequested) { mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1)); - foreach (QWaylandInputDevice *inputDevice, mInputDevices) { + for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices)) inputDevice->setTextInput(new QWaylandTextInput(this, mTextInputManager->get_text_input(inputDevice->wl_seat()))); - } + mWaylandIntegration->reconfigureInputContext(); } else if (interface == QStringLiteral("qt_hardware_integration")) { - mHardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id)); - // make a roundtrip here since we need to receive the events sent by - // qt_hardware_integration before creating windows - forceRoundTrip(); + bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION"); + if (!disableHardwareIntegration) { + mHardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id)); + // make a roundtrip here since we need to receive the events sent by + // qt_hardware_integration before creating windows + forceRoundTrip(); + } } else if (interface == QLatin1String("zxdg_output_manager_v1")) { - mXdgOutputManager.reset(new QtWayland::zxdg_output_manager_v1(registry, id, 1)); - for (auto *screen : qAsConst(mScreens)) + mXdgOutputManager.reset(new QtWayland::zxdg_output_manager_v1(registry, id, qMin(2, int(version)))); + for (auto *screen : qAsConst(mWaitingScreens)) screen->initXdgOutput(xdgOutputManager()); forceRoundTrip(); } @@ -330,14 +349,28 @@ void QWaylandDisplay::registry_global_remove(uint32_t id) RegistryGlobal &global = mGlobals[i]; if (global.id == id) { if (global.interface == QStringLiteral("wl_output")) { + for (auto *screen : mWaitingScreens) { + if (screen->outputId() == id) { + mWaitingScreens.removeOne(screen); + delete screen; + break; + } + } + foreach (QWaylandScreen *screen, mScreens) { if (screen->outputId() == id) { mScreens.removeOne(screen); - mWaylandIntegration->destroyScreen(screen); + QWindowSystemInterface::handleScreenRemoved(screen); break; } } } + if (global.interface == QStringLiteral("zwp_text_input_manager_v2")) { + mTextInputManager.reset(); + for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices)) + inputDevice->setTextInput(nullptr); + mWaylandIntegration->reconfigureInputContext(); + } mGlobals.removeAt(i); break; } @@ -539,40 +572,20 @@ QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const #if QT_CONFIG(cursor) -void QWaylandDisplay::setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, qreal dpr) -{ - /* Qt doesn't tell us which input device we should set the cursor - * for, so set it for all devices. */ - for (int i = 0; i < mInputDevices.count(); i++) { - QWaylandInputDevice *inputDevice = mInputDevices.at(i); - inputDevice->setCursor(buffer, image, dpr); - } -} - -void QWaylandDisplay::setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, qreal dpr) +QWaylandCursor *QWaylandDisplay::waylandCursor() { - /* Qt doesn't tell us which input device we should set the cursor - * for, so set it for all devices. */ - for (int i = 0; i < mInputDevices.count(); i++) { - QWaylandInputDevice *inputDevice = mInputDevices.at(i); - inputDevice->setCursor(buffer, hotSpot, dpr); - } + if (!mCursor) + mCursor.reset(new QWaylandCursor(this)); + return mCursor.data(); } -QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(qreal devicePixelRatio) +QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int pixelSize) { - constexpr int defaultCursorSize = 32; - static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE"); - int cursorSize = xCursorSize > 0 ? xCursorSize : defaultCursorSize; - - if (compositorVersion() >= 3) // set_buffer_scale is not supported on earlier versions - cursorSize *= devicePixelRatio; - - if (auto *theme = mCursorThemesBySize.value(cursorSize, nullptr)) + if (auto *theme = mCursorThemes.value({name, pixelSize}, nullptr)) return theme; - if (auto *theme = QWaylandCursorTheme::create(shm(), cursorSize)) { - mCursorThemesBySize[cursorSize] = theme; + if (auto *theme = QWaylandCursorTheme::create(shm(), pixelSize, name)) { + mCursorThemes[{name, pixelSize}] = theme; return theme; } @@ -581,6 +594,6 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(qreal devicePixelRatio) #endif // QT_CONFIG(cursor) -} +} // namespace QtWaylandClient QT_END_NAMESPACE diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h index 6bf6abd5d..558d8d9b5 100644 --- a/src/client/qwaylanddisplay_p.h +++ b/src/client/qwaylanddisplay_p.h @@ -59,12 +59,16 @@ #include <QtCore/QWaitCondition> #include <QtCore/QLoggingCategory> -#include <wayland-client.h> - #include <QtWaylandClient/private/qwayland-wayland.h> #include <QtWaylandClient/private/qtwaylandclientglobal_p.h> #include <QtWaylandClient/private/qwaylandshm_p.h> +#include <qpa/qplatforminputcontextfactory_p.h> + +#if QT_CONFIG(xkbcommon) +#include <QtXkbCommonSupport/private/qxkbcommon_p.h> +#endif + struct wl_cursor_image; QT_BEGIN_NAMESPACE @@ -95,6 +99,7 @@ class QWaylandWindow; class QWaylandIntegration; class QWaylandHardwareIntegration; class QWaylandShellIntegration; +class QWaylandCursor; class QWaylandCursorTheme; typedef void (*RegistryListener)(void *data, @@ -110,9 +115,14 @@ public: QWaylandDisplay(QWaylandIntegration *waylandIntegration); ~QWaylandDisplay(void) override; +#if QT_CONFIG(xkbcommon) + struct xkb_context *xkbContext() const { return mXkbContext.get(); } +#endif + QList<QWaylandScreen *> screens() const { return mScreens; } QWaylandScreen *screenForOutput(struct wl_output *output) const; + void handleScreenInitialized(QWaylandScreen *screen); struct wl_surface *createSurface(void *handle); struct ::wl_region *createRegion(const QRegion &qregion); @@ -123,9 +133,8 @@ public: QWaylandWindowManagerIntegration *windowManagerIntegration() const; #if QT_CONFIG(cursor) - void setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, qreal dpr); - void setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, qreal dpr); - QWaylandCursorTheme *loadCursorTheme(qreal devicePixelRatio); + QWaylandCursor *waylandCursor(); + QWaylandCursorTheme *loadCursorTheme(const QString &name, int pixelSize); #endif struct wl_display *wl_display() const { return mDisplay; } struct ::wl_registry *wl_registry() { return object(); } @@ -146,6 +155,7 @@ public: QWaylandHardwareIntegration *hardwareIntegration() const { return mHardwareIntegration.data(); } QtWayland::zxdg_output_manager_v1 *xdgOutputManager() const { return mXdgOutputManager.data(); } + bool usingInputContextFromCompositor() const { return mUsingInputContextFromCompositor; } struct RegistryGlobal { uint32_t id; @@ -210,12 +220,14 @@ private: struct wl_display *mDisplay = nullptr; QtWayland::wl_compositor mCompositor; QScopedPointer<QWaylandShm> mShm; + QList<QWaylandScreen *> mWaitingScreens; QList<QWaylandScreen *> mScreens; QList<QWaylandInputDevice *> mInputDevices; QList<Listener> mRegistryListeners; QWaylandIntegration *mWaylandIntegration = nullptr; #if QT_CONFIG(cursor) - QMap<int, QWaylandCursorTheme *> mCursorThemesBySize; + QMap<std::pair<QString, int>, QWaylandCursorTheme *> mCursorThemes; // theme name and size + QScopedPointer<QWaylandCursor> mCursor; #endif #if QT_CONFIG(wayland_datadevice) QScopedPointer<QWaylandDataDeviceManager> mDndSelectionHandler; @@ -229,10 +241,10 @@ private: QScopedPointer<QWaylandHardwareIntegration> mHardwareIntegration; QScopedPointer<QtWayland::zxdg_output_manager_v1> mXdgOutputManager; QSocketNotifier *mReadNotifier = nullptr; - int mFd; - int mWritableNotificationFd; + int mFd = -1; + int mWritableNotificationFd = -1; QList<RegistryGlobal> mGlobals; - int mCompositorVersion; + int mCompositorVersion = -1; uint32_t mLastInputSerial = 0; QWaylandInputDevice *mLastInputDevice = nullptr; QPointer<QWaylandWindow> mLastInputWindow; @@ -241,8 +253,17 @@ private: struct wl_callback *mSyncCallback = nullptr; static const wl_callback_listener syncCallbackListener; + bool mClientSideInputContextRequested = !QPlatformInputContextFactory::requested().isNull(); + bool mUsingInputContextFromCompositor = false; + void registry_global(uint32_t id, const QString &interface, uint32_t version) override; void registry_global_remove(uint32_t id) override; + +#if QT_CONFIG(xkbcommon) + QXkbCommon::ScopedXKBContext mXkbContext; +#endif + + friend class QWaylandIntegration; }; } diff --git a/src/client/qwaylandextendedsurface_p.h b/src/client/qwaylandextendedsurface_p.h index cd604f342..d71ac6be9 100644 --- a/src/client/qwaylandextendedsurface_p.h +++ b/src/client/qwaylandextendedsurface_p.h @@ -56,7 +56,6 @@ #include <QtWaylandClient/qtwaylandclientglobal.h> -#include <wayland-client.h> #include <QtWaylandClient/private/qwayland-surface-extension.h> QT_BEGIN_NAMESPACE diff --git a/src/client/qwaylandinputcontext.cpp b/src/client/qwaylandinputcontext.cpp index e85faaf8e..c6f287dda 100644 --- a/src/client/qwaylandinputcontext.cpp +++ b/src/client/qwaylandinputcontext.cpp @@ -50,7 +50,6 @@ #include "qwaylandinputdevice_p.h" #include "qwaylandinputmethodeventbuilder_p.h" #include "qwaylandwindow_p.h" -#include "qwaylandxkb_p.h" QT_BEGIN_NAMESPACE @@ -315,6 +314,7 @@ void QWaylandTextInput::zwp_text_input_v2_delete_surrounding_text(uint32_t befor void QWaylandTextInput::zwp_text_input_v2_keysym(uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers) { +#if QT_CONFIG(xkbcommon) if (m_resetCallback) { qCDebug(qLcQpaInputMethods()) << "discard keysym: reset not confirmed"; return; @@ -325,13 +325,18 @@ void QWaylandTextInput::zwp_text_input_v2_keysym(uint32_t time, uint32_t sym, ui Qt::KeyboardModifiers qtModifiers = modifiersToQtModifiers(modifiers); - QEvent::Type type = QWaylandXkb::toQtEventType(state); - QString text; - int qtkey; - std::tie(qtkey, text) = QWaylandXkb::keysymToQtKey(sym, qtModifiers); + QEvent::Type type = state == WL_KEYBOARD_KEY_STATE_PRESSED ? QEvent::KeyPress : QEvent::KeyRelease; + QString text = QXkbCommon::lookupStringNoKeysymTransformations(sym); + int qtkey = QXkbCommon::keysymToQtKey(sym, qtModifiers); QWindowSystemInterface::handleKeyEvent(QGuiApplication::focusWindow(), time, type, qtkey, qtModifiers, text); +#else + Q_UNUSED(time); + Q_UNUSED(sym); + Q_UNUSED(state); + Q_UNUSED(modifiers); +#endif } void QWaylandTextInput::zwp_text_input_v2_language(const QString &language) diff --git a/src/client/qwaylandinputcontext_p.h b/src/client/qwaylandinputcontext_p.h index 93300e1f5..10132dfe1 100644 --- a/src/client/qwaylandinputcontext_p.h +++ b/src/client/qwaylandinputcontext_p.h @@ -62,6 +62,9 @@ #include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h> #include <qwaylandinputmethodeventbuilder_p.h> +struct wl_callback; +struct wl_callback_listener; + QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(qLcQpaInputMethods) diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp index 90e138a3d..39c02d962 100644 --- a/src/client/qwaylandinputdevice.cpp +++ b/src/client/qwaylandinputdevice.cpp @@ -51,7 +51,6 @@ #include "qwaylandcursor_p.h" #include "qwaylanddisplay_p.h" #include "qwaylandshmbackingstore_p.h" -#include "../shared/qwaylandxkb_p.h" #include "qwaylandinputcontext_p.h" #include <QtGui/private/qpixmap_raster_p.h> @@ -70,10 +69,6 @@ #include <QtGui/QGuiApplication> -#if QT_CONFIG(xkbcommon) -#include <xkbcommon/xkbcommon-compose.h> -#endif - QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -81,85 +76,51 @@ namespace QtWaylandClient { QWaylandInputDevice::Keyboard::Keyboard(QWaylandInputDevice *p) : mParent(p) { - connect(&mRepeatTimer, SIGNAL(timeout()), this, SLOT(repeatKey())); + mRepeatTimer.callOnTimeout([&]() { + if (!focusWindow()) { + // We destroyed the keyboard focus surface, but the server didn't get the message yet... + // or the server didn't send an enter event first. + return; + } + mRepeatTimer.setInterval(mRepeatRate); + handleKey(mRepeatKey.time, QEvent::KeyRelease, mRepeatKey.key, mRepeatKey.modifiers, + mRepeatKey.code, mRepeatKey.nativeVirtualKey, mRepeatKey.nativeModifiers, + mRepeatKey.text, true); + handleKey(mRepeatKey.time, QEvent::KeyPress, mRepeatKey.key, mRepeatKey.modifiers, + mRepeatKey.code, mRepeatKey.nativeVirtualKey, mRepeatKey.nativeModifiers, + mRepeatKey.text, true); + }); } #if QT_CONFIG(xkbcommon) -bool QWaylandInputDevice::Keyboard::createDefaultKeyMap() +bool QWaylandInputDevice::Keyboard::createDefaultKeymap() { - if (mXkbContext && mXkbMap && mXkbState) { - return true; - } + struct xkb_context *ctx = mParent->mQDisplay->xkbContext(); + if (!ctx) + return false; - xkb_rule_names names; - names.rules = strdup("evdev"); - names.model = strdup("pc105"); - names.layout = strdup("us"); - names.variant = strdup(""); - names.options = strdup(""); + struct xkb_rule_names names; + names.rules = "evdev"; + names.model = "pc105"; + names.layout = "us"; + names.variant = ""; + names.options = ""; - mXkbContext = xkb_context_new(xkb_context_flags(0)); - if (mXkbContext) { - mXkbMap = xkb_map_new_from_names(mXkbContext, &names, xkb_map_compile_flags(0)); - if (mXkbMap) { - mXkbState = xkb_state_new(mXkbMap); - } - } + mXkbKeymap.reset(xkb_keymap_new_from_names(ctx, &names, XKB_KEYMAP_COMPILE_NO_FLAGS)); + if (mXkbKeymap) + mXkbState.reset(xkb_state_new(mXkbKeymap.get())); - if (!mXkbContext || !mXkbMap || !mXkbState) { - qWarning() << "xkb_map_new_from_names failed, no key input"; + if (!mXkbKeymap || !mXkbState) { + qCWarning(lcQpaWayland, "failed to create default keymap"); return false; } - createComposeState(); - return true; -} -void QWaylandInputDevice::Keyboard::releaseKeyMap() -{ - if (mXkbState) - xkb_state_unref(mXkbState); - if (mXkbMap) - xkb_map_unref(mXkbMap); - if (mXkbContext) - xkb_context_unref(mXkbContext); -} - -void QWaylandInputDevice::Keyboard::createComposeState() -{ - static const char *locale = nullptr; - if (!locale) { - locale = getenv("LC_ALL"); - if (!locale) - locale = getenv("LC_CTYPE"); - if (!locale) - locale = getenv("LANG"); - if (!locale) - locale = "C"; - } - - mXkbComposeTable = xkb_compose_table_new_from_locale(mXkbContext, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); - if (mXkbComposeTable) - mXkbComposeState = xkb_compose_state_new(mXkbComposeTable, XKB_COMPOSE_STATE_NO_FLAGS); -} - -void QWaylandInputDevice::Keyboard::releaseComposeState() -{ - if (mXkbComposeState) - xkb_compose_state_unref(mXkbComposeState); - if (mXkbComposeTable) - xkb_compose_table_unref(mXkbComposeTable); - mXkbComposeState = nullptr; - mXkbComposeTable = nullptr; + return true; } - #endif QWaylandInputDevice::Keyboard::~Keyboard() { -#if QT_CONFIG(xkbcommon) - releaseComposeState(); - releaseKeyMap(); -#endif if (mFocus) QWindowSystemInterface::handleWindowActivated(nullptr); if (mParent->mVersion >= 3) @@ -168,13 +129,13 @@ QWaylandInputDevice::Keyboard::~Keyboard() wl_keyboard_destroy(object()); } -void QWaylandInputDevice::Keyboard::stopRepeat() +QWaylandWindow *QWaylandInputDevice::Keyboard::focusWindow() const { - mRepeatTimer.stop(); + return mFocus ? QWaylandWindow::fromWlSurface(mFocus) : nullptr; } -QWaylandInputDevice::Pointer::Pointer(QWaylandInputDevice *p) - : mParent(p) +QWaylandInputDevice::Pointer::Pointer(QWaylandInputDevice *seat) + : mParent(seat) { } @@ -186,6 +147,191 @@ QWaylandInputDevice::Pointer::~Pointer() wl_pointer_destroy(object()); } +#if QT_CONFIG(cursor) + +class CursorSurface : public QObject, public QtWayland::wl_surface +{ +public: + explicit CursorSurface(QWaylandInputDevice::Pointer *pointer, QWaylandDisplay *display) + : m_pointer(pointer) + { + init(display->createSurface(this)); + //TODO: When we upgrade to libwayland 1.10, use wl_surface_get_version instead. + m_version = display->compositorVersion(); + connect(qApp, &QGuiApplication::screenRemoved, this, [this](QScreen *screen) { + int oldScale = outputScale(); + if (!m_screens.removeOne(static_cast<QWaylandScreen *>(screen->handle()))) + return; + + if (outputScale() != oldScale) + m_pointer->updateCursor(); + }); + } + + void hide() + { + uint serial = m_pointer->mEnterSerial; + Q_ASSERT(serial); + m_pointer->set_cursor(serial, nullptr, 0, 0); + m_setSerial = 0; + } + + // Size and hotspot are in surface coordinates + void update(wl_buffer *buffer, const QPoint &hotspot, const QSize &size, int bufferScale) + { + // Calling code needs to ensure buffer scale is supported if != 1 + Q_ASSERT(bufferScale == 1 || m_version >= 3); + + auto enterSerial = m_pointer->mEnterSerial; + if (m_setSerial < enterSerial || m_hotspot != hotspot) { + m_pointer->set_cursor(m_pointer->mEnterSerial, object(), hotspot.x(), hotspot.y()); + m_setSerial = enterSerial; + m_hotspot = hotspot; + } + + if (m_version >= 3) + set_buffer_scale(bufferScale); + + attach(buffer, 0, 0); + damage(0, 0, size.width(), size.height()); + commit(); + } + + int outputScale() const + { + int scale = 0; + for (auto *screen : m_screens) + scale = qMax(scale, screen->scale()); + return scale; + } + +protected: + void surface_enter(struct ::wl_output *output) override + { + int oldScale = outputScale(); + auto *screen = QWaylandScreen::fromWlOutput(output); + if (m_screens.contains(screen)) + return; + + m_screens.append(screen); + + if (outputScale() != oldScale) + m_pointer->updateCursor(); + } + + void surface_leave(struct ::wl_output *output) override + { + int oldScale = outputScale(); + auto *screen = QWaylandScreen::fromWlOutput(output); + + if (!m_screens.removeOne(screen)) + return; + + if (outputScale() != oldScale) + m_pointer->updateCursor(); + } + +private: + QWaylandInputDevice::Pointer *m_pointer = nullptr; + uint m_version = 0; + uint m_setSerial = 0; + QPoint m_hotspot; + QVector<QWaylandScreen *> m_screens; +}; + +QString QWaylandInputDevice::Pointer::cursorThemeName() const +{ + static QString themeName = qEnvironmentVariable("XCURSOR_THEME", QStringLiteral("default")); + return themeName; +} + +int QWaylandInputDevice::Pointer::cursorSize() const +{ + constexpr int defaultCursorSize = 32; + static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE"); + return xCursorSize > 0 ? xCursorSize : defaultCursorSize; +} + +int QWaylandInputDevice::Pointer::idealCursorScale() const +{ + // set_buffer_scale is not supported on earlier versions + if (seat()->mQDisplay->compositorVersion() < 3) + return 1; + + if (auto *s = mCursor.surface.data()) { + if (s->outputScale() > 0) + return s->outputScale(); + } + + return seat()->mCursor.fallbackOutputScale; +} + +void QWaylandInputDevice::Pointer::updateCursorTheme() +{ + int scale = idealCursorScale(); + int pixelSize = cursorSize() * scale; + auto *display = seat()->mQDisplay; + mCursor.theme = display->loadCursorTheme(cursorThemeName(), pixelSize); + if (auto *arrow = mCursor.theme->cursorImage(Qt::ArrowCursor)) { + int arrowPixelSize = qMax(arrow->width, arrow->height); // Not all cursor themes are square + while (scale > 1 && arrowPixelSize / scale < cursorSize()) + --scale; + } else { + qCWarning(lcQpaWayland) << "Cursor theme does not support the arrow cursor"; + } + mCursor.themeBufferScale = scale; +} + +void QWaylandInputDevice::Pointer::updateCursor() +{ + if (mEnterSerial == 0) + return; + + auto shape = seat()->mCursor.shape; + + if (shape == Qt::BlankCursor) { + if (mCursor.surface) + mCursor.surface->hide(); + return; + } + + if (shape == Qt::BitmapCursor) { + auto buffer = seat()->mCursor.bitmapBuffer; + if (!buffer) { + qCWarning(lcQpaWayland) << "No buffer for bitmap cursor, can't set cursor"; + return; + } + auto hotspot = seat()->mCursor.hotspot; + int bufferScale = seat()->mCursor.bitmapScale; + getOrCreateCursorSurface()->update(buffer->buffer(), hotspot, buffer->size(), bufferScale); + return; + } + + if (!mCursor.theme || idealCursorScale() != mCursor.themeBufferScale) + updateCursorTheme(); + + // Set from shape using theme + if (struct ::wl_cursor_image *image = mCursor.theme->cursorImage(shape)) { + struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); + int bufferScale = mCursor.themeBufferScale; + QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale; + QSize size = QSize(image->width, image->height) / bufferScale; + getOrCreateCursorSurface()->update(buffer, hotspot, size, bufferScale); + return; + } + + qCWarning(lcQpaWayland) << "Unable to change to cursor" << shape; +} + +CursorSurface *QWaylandInputDevice::Pointer::getOrCreateCursorSurface() +{ + if (!mCursor.surface) + mCursor.surface.reset(new CursorSurface(this, seat()->mQDisplay)); + return mCursor.surface.get(); +} + +#endif // QT_CONFIG(cursor) + QWaylandInputDevice::Touch::Touch(QWaylandInputDevice *p) : mParent(p) { @@ -211,9 +357,9 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version, } #endif - if (mQDisplay->textInputManager()) { - mTextInput = new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat())); - } + if (mQDisplay->textInputManager()) + mTextInput.reset(new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat()))); + } QWaylandInputDevice::~QWaylandInputDevice() @@ -238,7 +384,6 @@ void QWaylandInputDevice::seat_capabilities(uint32_t caps) if (caps & WL_SEAT_CAPABILITY_POINTER && !mPointer) { mPointer = createPointer(this); mPointer->init(get_pointer()); - pointerSurface = mQDisplay->createSurface(this); } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && mPointer) { delete mPointer; mPointer = nullptr; @@ -275,12 +420,6 @@ QWaylandInputDevice::Touch *QWaylandInputDevice::createTouch(QWaylandInputDevice return new Touch(device); } -void QWaylandInputDevice::handleWindowDestroyed(QWaylandWindow *window) -{ - if (mKeyboard && window == mKeyboard->mFocus) - mKeyboard->stopRepeat(); -} - void QWaylandInputDevice::handleEndDrag() { if (mTouch) @@ -303,12 +442,12 @@ QWaylandDataDevice *QWaylandInputDevice::dataDevice() const void QWaylandInputDevice::setTextInput(QWaylandTextInput *textInput) { - mTextInput = textInput; + mTextInput.reset(textInput); } QWaylandTextInput *QWaylandInputDevice::textInput() const { - return mTextInput; + return mTextInput.data(); } void QWaylandInputDevice::removeMouseButtonFromState(Qt::MouseButton button) @@ -324,7 +463,7 @@ QWaylandWindow *QWaylandInputDevice::pointerFocus() const QWaylandWindow *QWaylandInputDevice::keyboardFocus() const { - return mKeyboard ? mKeyboard->mFocus : nullptr; + return mKeyboard ? mKeyboard->focusWindow() : nullptr; } QWaylandWindow *QWaylandInputDevice::touchFocus() const @@ -332,6 +471,22 @@ QWaylandWindow *QWaylandInputDevice::touchFocus() const return mTouch ? mTouch->mFocus : nullptr; } +QPointF QWaylandInputDevice::pointerSurfacePosition() const +{ + return mPointer ? mPointer->mSurfacePos : QPointF(); +} + +QList<int> QWaylandInputDevice::possibleKeys(const QKeyEvent *event) const +{ +#if QT_CONFIG(xkbcommon) + if (mKeyboard && mKeyboard->mXkbState) + return QXkbCommon::possibleKeys(mKeyboard->mXkbState.get(), event); +#else + Q_UNUSED(event); +#endif + return {}; +} + Qt::KeyboardModifiers QWaylandInputDevice::modifiers() const { if (!mKeyboard) @@ -348,91 +503,40 @@ Qt::KeyboardModifiers QWaylandInputDevice::Keyboard::modifiers() const if (!mXkbState) return ret; - ret = QWaylandXkb::modifiers(mXkbState); + ret = QXkbCommon::modifiers(mXkbState.get()); #endif return ret; } #if QT_CONFIG(cursor) -uint32_t QWaylandInputDevice::cursorSerial() const -{ - if (mPointer) - return mPointer->mCursorSerial; - return 0; -} - -void QWaylandInputDevice::setCursor(Qt::CursorShape newShape, QWaylandScreen *screen) +void QWaylandInputDevice::setCursor(const QCursor *cursor, const QSharedPointer<QWaylandBuffer> &cachedBuffer, int fallbackOutputScale) { - struct wl_cursor_image *image = screen->waylandCursor()->cursorImage(newShape); - if (!image) { - return; + CursorState oldCursor = mCursor; + mCursor = CursorState(); // Clear any previous state + mCursor.shape = cursor ? cursor->shape() : Qt::ArrowCursor; + mCursor.hotspot = cursor ? cursor->hotSpot() : QPoint(); + mCursor.fallbackOutputScale = fallbackOutputScale; + + if (mCursor.shape == Qt::BitmapCursor) { + mCursor.bitmapBuffer = cachedBuffer ? cachedBuffer : QWaylandCursor::cursorBitmapBuffer(mQDisplay, cursor); + qreal dpr = cursor->pixmap().devicePixelRatio(); + mCursor.bitmapScale = int(dpr); // Wayland doesn't support fractional buffer scale + // If there was a fractional part of the dpr, we need to scale the hotspot accordingly + if (mCursor.bitmapScale < dpr) + mCursor.hotspot *= dpr / mCursor.bitmapScale; } - struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); - setCursor(buffer, image, screen->devicePixelRatio()); -} - -void QWaylandInputDevice::setCursor(const QCursor &cursor, QWaylandScreen *screen) -{ - if (mPointer->mCursorSerial >= mPointer->mEnterSerial && (cursor.shape() != Qt::BitmapCursor && cursor.shape() == mPointer->mCursorShape)) + // Return early if setCursor was called redundantly (mostly happens from decorations) + if (mCursor.shape != Qt::BitmapCursor + && mCursor.shape == oldCursor.shape + && mCursor.hotspot == oldCursor.hotspot + && mCursor.fallbackOutputScale == oldCursor.fallbackOutputScale) { return; - - mPointer->mCursorShape = cursor.shape(); - if (cursor.shape() == Qt::BitmapCursor) { - setCursor(screen->waylandCursor()->cursorBitmapImage(&cursor), cursor.hotSpot(), screen->devicePixelRatio()); - return; - } - setCursor(cursor.shape(), screen); -} - -void QWaylandInputDevice::setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, int bufferScale) -{ - if (image) { - // Convert from pixel coordinates to surface coordinates - QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale; - QSize size = QSize(image->width, image->height) / bufferScale; - setCursor(buffer, hotspot, size, bufferScale); - } else { - setCursor(buffer, QPoint(), QSize(), bufferScale); } -} - -// size and hotspot are in surface coordinates -void QWaylandInputDevice::setCursor(struct wl_buffer *buffer, const QPoint &hotSpot, const QSize &size, int bufferScale) -{ - if (mCaps & WL_SEAT_CAPABILITY_POINTER) { - bool force = mPointer->mEnterSerial > mPointer->mCursorSerial; - - if (!force && mPointer->mCursorBuffer == buffer) - return; - - mPixmapCursor.clear(); - mPointer->mCursorSerial = mPointer->mEnterSerial; - - mPointer->mCursorBuffer = buffer; - /* Hide cursor */ - if (!buffer) - { - mPointer->set_cursor(mPointer->mEnterSerial, nullptr, 0, 0); - return; - } - - mPointer->set_cursor(mPointer->mEnterSerial, pointerSurface, - hotSpot.x(), hotSpot.y()); - wl_surface_attach(pointerSurface, buffer, 0, 0); - if (mQDisplay->compositorVersion() >= 3) - wl_surface_set_buffer_scale(pointerSurface, bufferScale); - wl_surface_damage(pointerSurface, 0, 0, size.width(), size.height()); - wl_surface_commit(pointerSurface); - } -} - -void QWaylandInputDevice::setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, int bufferScale) -{ - setCursor(buffer->buffer(), hotSpot, buffer->size(), bufferScale); - mPixmapCursor = buffer; + if (mPointer) + mPointer->updateCursor(); } #endif @@ -451,7 +555,16 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf return; QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface); + + if (mFocus) { + qCWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a" + << "leave event first, this is not allowed by the wayland protocol" + << "attempting to work around it by invalidating the current focus"; + invalidateFocus(); + } mFocus = window; + connect(mFocus, &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed); + mSurfacePos = QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy)); mGlobalPos = window->window()->mapToGlobal(mSurfacePos.toPoint()); @@ -460,7 +573,7 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf #if QT_CONFIG(cursor) // Depends on mEnterSerial being updated - window->window()->setCursor(window->window()->cursor()); + updateCursor(); #endif QWaylandWindow *grab = QWaylandWindow::mouseGrab(); @@ -481,7 +594,8 @@ void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surfac QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface); window->handleMouseLeave(mParent); } - mFocus = nullptr; + + invalidateFocus(); mButtons = Qt::NoButton; mParent->mTime = time; @@ -584,6 +698,13 @@ void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time } } +void QWaylandInputDevice::Pointer::invalidateFocus() +{ + disconnect(mFocus, &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed); + mFocus = nullptr; + mEnterSerial = 0; +} + void QWaylandInputDevice::Pointer::releaseButtons() { mButtons = Qt::NoButton; @@ -630,8 +751,10 @@ void QWaylandInputDevice::Pointer::pointer_axis(uint32_t time, uint32_t axis, in void QWaylandInputDevice::Keyboard::keyboard_keymap(uint32_t format, int32_t fd, uint32_t size) { + mKeymapFormat = format; #if QT_CONFIG(xkbcommon) if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + qCWarning(lcQpaWayland) << "unknown keymap format:" << format; close(fd); return; } @@ -642,21 +765,19 @@ void QWaylandInputDevice::Keyboard::keyboard_keymap(uint32_t format, int32_t fd, return; } - // Release the old keymap resources in the case they were already created in - // the key event or when the compositor issues a new map - releaseComposeState(); - releaseKeyMap(); + mXkbKeymap.reset(xkb_keymap_new_from_string(mParent->mQDisplay->xkbContext(), map_str, + XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS)); + QXkbCommon::verifyHasLatinLayout(mXkbKeymap.get()); - mXkbContext = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - mXkbMap = xkb_map_new_from_string(mXkbContext, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); munmap(map_str, size); close(fd); - mXkbState = xkb_state_new(mXkbMap); - createComposeState(); - + if (mXkbKeymap) + mXkbState.reset(xkb_state_new(mXkbKeymap.get())); + else + mXkbState.reset(nullptr); #else - Q_UNUSED(format); Q_UNUSED(fd); Q_UNUSED(size); #endif @@ -667,12 +788,19 @@ void QWaylandInputDevice::Keyboard::keyboard_enter(uint32_t time, struct wl_surf Q_UNUSED(time); Q_UNUSED(keys); - if (!surface) + if (!surface) { + // Ignoring wl_keyboard.enter event with null surface. This is either a compositor bug, + // or it's a race with a wl_surface.destroy request. In either case, ignore the event. return; + } + if (mFocus) { + qCWarning(lcQpaWayland()) << "Unexpected wl_keyboard.enter event. Keyboard already has focus"; + disconnect(focusWindow(), &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed); + } - QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface); - mFocus = window; + mFocus = surface; + connect(focusWindow(), &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed); mParent->mQDisplay->handleKeyboardFocusChanged(mParent); } @@ -680,144 +808,124 @@ void QWaylandInputDevice::Keyboard::keyboard_enter(uint32_t time, struct wl_surf void QWaylandInputDevice::Keyboard::keyboard_leave(uint32_t time, struct wl_surface *surface) { Q_UNUSED(time); - Q_UNUSED(surface); - if (surface) { - QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface); - window->unfocus(); + if (!surface) { + // Either a compositor bug, or a race condition with wl_surface.destroy, ignore the event. + return; } - mFocus = nullptr; - - mParent->mQDisplay->handleKeyboardFocusChanged(mParent); - - mRepeatTimer.stop(); + if (surface != mFocus) { + qCWarning(lcQpaWayland) << "Ignoring unexpected wl_keyboard.leave event." + << "wl_surface argument does not match the current focus" + << "This is most likely a compositor bug"; + return; + } + disconnect(focusWindow(), &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed); + handleFocusLost(); } -static void sendKey(QWindow *tlw, ulong timestamp, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, - quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, - const QString& text = QString(), bool autorep = false, ushort count = 1) +void QWaylandInputDevice::Keyboard::handleKey(ulong timestamp, QEvent::Type type, int key, + Qt::KeyboardModifiers modifiers, quint32 nativeScanCode, + quint32 nativeVirtualKey, quint32 nativeModifiers, + const QString &text, bool autorepeat, ushort count) { QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext(); bool filtered = false; - if (inputContext) { - QKeyEvent event(type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, - text, autorep, count); + if (inputContext && !mParent->mQDisplay->usingInputContextFromCompositor()) { + QKeyEvent event(type, key, modifiers, nativeScanCode, nativeVirtualKey, + nativeModifiers, text, autorepeat, count); event.setTimestamp(timestamp); filtered = inputContext->filterEvent(&event); } if (!filtered) { - QWindowSystemInterface::handleExtendedKeyEvent(tlw, timestamp, type, key, modifiers, - nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count); + QWindowSystemInterface::handleExtendedKeyEvent(focusWindow()->window(), timestamp, type, key, modifiers, + nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorepeat, count); } } void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { - QWaylandWindow *window = mFocus; + if (mKeymapFormat != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 && mKeymapFormat != WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP) { + qCWarning(lcQpaWayland) << Q_FUNC_INFO << "unknown keymap format:" << mKeymapFormat; + return; + } + + auto *window = focusWindow(); if (!window) { // We destroyed the keyboard focus surface, but the server didn't get the message yet... // or the server didn't send an enter event first. In either case, ignore the event. return; } - uint32_t code = key + 8; - bool isDown = state != WL_KEYBOARD_KEY_STATE_RELEASED; - QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease; - QString text; - int qtkey = key + 8; // qt-compositor substracts 8 for some reason mParent->mSerial = serial; + const bool isDown = state != WL_KEYBOARD_KEY_STATE_RELEASED; if (isDown) mParent->mQDisplay->setLastInputDevice(mParent, serial, window); + if (mKeymapFormat == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { #if QT_CONFIG(xkbcommon) - if (!createDefaultKeyMap()) { - return; - } + if ((!mXkbKeymap || !mXkbState) && !createDefaultKeymap()) + return; - QString composedText; - xkb_keysym_t sym = xkb_state_key_get_one_sym(mXkbState, code); - if (mXkbComposeState) { - if (isDown) - xkb_compose_state_feed(mXkbComposeState, sym); - xkb_compose_status status = xkb_compose_state_get_status(mXkbComposeState); - - switch (status) { - case XKB_COMPOSE_COMPOSED: { - int size = xkb_compose_state_get_utf8(mXkbComposeState, nullptr, 0); - QVarLengthArray<char, 32> buffer(size + 1); - xkb_compose_state_get_utf8(mXkbComposeState, buffer.data(), buffer.size()); - composedText = QString::fromUtf8(buffer.constData()); - sym = xkb_compose_state_get_one_sym(mXkbComposeState); - xkb_compose_state_reset(mXkbComposeState); - } break; - case XKB_COMPOSE_COMPOSING: - case XKB_COMPOSE_CANCELLED: - return; - case XKB_COMPOSE_NOTHING: - break; - } - } + auto code = key + 8; // map to wl_keyboard::keymap_format::keymap_format_xkb_v1 - Qt::KeyboardModifiers modifiers = mParent->modifiers(); + xkb_keysym_t sym = xkb_state_key_get_one_sym(mXkbState.get(), code); - std::tie(qtkey, text) = QWaylandXkb::keysymToQtKey(sym, modifiers); + Qt::KeyboardModifiers modifiers = mParent->modifiers(); - if (!composedText.isNull()) - text = composedText; + int qtkey = QXkbCommon::keysymToQtKey(sym, modifiers, mXkbState.get(), code); + QString text = QXkbCommon::lookupString(mXkbState.get(), code); - sendKey(window->window(), time, type, qtkey, modifiers, code, sym, mNativeModifiers, text); -#else - // Generic fallback for single hard keys: Assume 'key' is a Qt key code. - sendKey(window->window(), time, type, qtkey, Qt::NoModifier, code, 0, 0); -#endif + QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease; + handleKey(time, type, qtkey, modifiers, code, sym, mNativeModifiers, text); - if (state == WL_KEYBOARD_KEY_STATE_PRESSED -#if QT_CONFIG(xkbcommon) - && xkb_keymap_key_repeats(mXkbMap, code) -#endif - ) { - mRepeatKey = qtkey; - mRepeatCode = code; - mRepeatTime = time; - mRepeatText = text; -#if QT_CONFIG(xkbcommon) - mRepeatSym = sym; + if (state == WL_KEYBOARD_KEY_STATE_PRESSED && xkb_keymap_key_repeats(mXkbKeymap.get(), code)) { + mRepeatKey.key = qtkey; + mRepeatKey.code = code; + mRepeatKey.time = time; + mRepeatKey.text = text; + mRepeatKey.modifiers = modifiers; + mRepeatKey.nativeModifiers = mNativeModifiers; + mRepeatKey.nativeVirtualKey = sym; + mRepeatTimer.setInterval(mRepeatDelay); + mRepeatTimer.start(); + } else if (mRepeatKey.code == code) { + mRepeatTimer.stop(); + } +#else + Q_UNUSED(time); + Q_UNUSED(key); + qCWarning(lcQpaWayland, "xkbcommon not available on this build, not performing key mapping"); + return; #endif - mRepeatTimer.setInterval(400); - mRepeatTimer.start(); - } else if (mRepeatCode == code) { - mRepeatTimer.stop(); + } else if (mKeymapFormat == WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP) { + // raw scan code + return; } } -void QWaylandInputDevice::Keyboard::repeatKey() +void QWaylandInputDevice::Keyboard::handleFocusDestroyed() { - if (!mFocus) { - // We destroyed the keyboard focus surface, but the server didn't get the message yet... - // or the server didn't send an enter event first. - return; - } - - mRepeatTimer.setInterval(25); - sendKey(mFocus->window(), mRepeatTime, QEvent::KeyRelease, mRepeatKey, modifiers(), mRepeatCode, -#if QT_CONFIG(xkbcommon) - mRepeatSym, mNativeModifiers, -#else - 0, 0, -#endif - mRepeatText, true); + // The signal is emitted by QWaylandWindow, which is not necessarily destroyed along with the + // surface, so we still need to disconnect the signal + auto *window = qobject_cast<QWaylandWindow *>(sender()); + disconnect(window, &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed); + Q_ASSERT(window->object() == mFocus); + handleFocusLost(); +} - sendKey(mFocus->window(), mRepeatTime, QEvent::KeyPress, mRepeatKey, modifiers(), mRepeatCode, -#if QT_CONFIG(xkbcommon) - mRepeatSym, mNativeModifiers, -#else - 0, 0, +void QWaylandInputDevice::Keyboard::handleFocusLost() +{ + mFocus = nullptr; +#if QT_CONFIG(clipboard) + if (auto *dataDevice = mParent->dataDevice()) + dataDevice->invalidateSelectionOffer(); #endif - mRepeatText, true); + mParent->mQDisplay->handleKeyboardFocusChanged(mParent); + mRepeatTimer.stop(); } void QWaylandInputDevice::Keyboard::keyboard_modifiers(uint32_t serial, @@ -829,12 +937,11 @@ void QWaylandInputDevice::Keyboard::keyboard_modifiers(uint32_t serial, Q_UNUSED(serial); #if QT_CONFIG(xkbcommon) if (mXkbState) - xkb_state_update_mask(mXkbState, + xkb_state_update_mask(mXkbState.get(), mods_depressed, mods_latched, mods_locked, 0, 0, group); mNativeModifiers = mods_depressed | mods_latched | mods_locked; #else - Q_UNUSED(serial); Q_UNUSED(mods_depressed); Q_UNUSED(mods_latched); Q_UNUSED(mods_locked); @@ -842,6 +949,12 @@ void QWaylandInputDevice::Keyboard::keyboard_modifiers(uint32_t serial, #endif } +void QWaylandInputDevice::Keyboard::keyboard_repeat_info(int32_t rate, int32_t delay) +{ + mRepeatRate = rate; + mRepeatDelay = delay; +} + void QWaylandInputDevice::Touch::touch_down(uint32_t serial, uint32_t time, struct wl_surface *surface, @@ -915,7 +1028,7 @@ void QWaylandInputDevice::handleTouchPoint(int id, double x, double y, Qt::Touch if (!win && mPointer) win = mPointer->mFocus; if (!win && mKeyboard) - win = mKeyboard->mFocus; + win = mKeyboard->focusWindow(); if (!win || !win->window()) return; diff --git a/src/client/qwaylandinputdevice_p.h b/src/client/qwaylandinputdevice_p.h index 7aa86539b..39ca9dca5 100644 --- a/src/client/qwaylandinputdevice_p.h +++ b/src/client/qwaylandinputdevice_p.h @@ -54,6 +54,7 @@ #include <QtWaylandClient/private/qtwaylandclientglobal_p.h> #include <QtWaylandClient/private/qwaylandwindow_p.h> +#include <QtCore/QScopedPointer> #include <QSocketNotifier> #include <QObject> #include <QTimer> @@ -61,13 +62,10 @@ #include <qpa/qplatformscreen.h> #include <qpa/qwindowsysteminterface.h> -#include <wayland-client.h> - #include <QtWaylandClient/private/qwayland-wayland.h> #if QT_CONFIG(xkbcommon) -#include <xkbcommon/xkbcommon.h> -#include <xkbcommon/xkbcommon-keysyms.h> +#include <QtXkbCommonSupport/private/qxkbcommon_p.h> #endif #include <QtCore/QDebug> @@ -77,11 +75,6 @@ struct wl_cursor_image; #endif -#if QT_CONFIG(xkbcommon) -struct xkb_compose_state; -struct xkb_compose_table; -#endif - QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -90,6 +83,10 @@ class QWaylandWindow; class QWaylandDisplay; class QWaylandDataDevice; class QWaylandTextInput; +#if QT_CONFIG(cursor) +class QWaylandCursorTheme; +class CursorSurface; +#endif class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice : public QObject @@ -109,12 +106,8 @@ public: struct ::wl_seat *wl_seat() { return QtWayland::wl_seat::object(); } #if QT_CONFIG(cursor) - void setCursor(const QCursor &cursor, QWaylandScreen *screen); - void setCursor(struct wl_buffer *buffer, struct ::wl_cursor_image *image, int bufferScale); - void setCursor(struct wl_buffer *buffer, const QPoint &hotSpot, const QSize &size, int bufferScale); - void setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, int bufferScale); + void setCursor(const QCursor *cursor, const QSharedPointer<QWaylandBuffer> &cachedBuffer = {}, int fallbackOutputScale = 1); #endif - void handleWindowDestroyed(QWaylandWindow *window); void handleEndDrag(); #if QT_CONFIG(wayland_datadevice) @@ -131,25 +124,34 @@ public: QWaylandWindow *keyboardFocus() const; QWaylandWindow *touchFocus() const; + QList<int> possibleKeys(const QKeyEvent *event) const; + + QPointF pointerSurfacePosition() const; + Qt::KeyboardModifiers modifiers() const; uint32_t serial() const; - uint32_t cursorSerial() const; virtual Keyboard *createKeyboard(QWaylandInputDevice *device); virtual Pointer *createPointer(QWaylandInputDevice *device); virtual Touch *createTouch(QWaylandInputDevice *device); private: - void setCursor(Qt::CursorShape cursor, QWaylandScreen *screen); - QWaylandDisplay *mQDisplay = nullptr; struct wl_display *mDisplay = nullptr; int mVersion; uint32_t mCaps = 0; - struct wl_surface *pointerSurface = nullptr; +#if QT_CONFIG(cursor) + struct CursorState { + QSharedPointer<QWaylandBuffer> bitmapBuffer; // not used with shape cursors + int bitmapScale = 1; + Qt::CursorShape shape = Qt::ArrowCursor; + int fallbackOutputScale = 1; + QPoint hotspot; + } mCursor; +#endif #if QT_CONFIG(wayland_datadevice) QWaylandDataDevice *mDataDevice = nullptr; @@ -159,7 +161,7 @@ private: Pointer *mPointer = nullptr; Touch *mTouch = nullptr; - QWaylandTextInput *mTextInput = nullptr; + QScopedPointer<QWaylandTextInput> mTextInput; uint32_t mTime = 0; uint32_t mSerial = 0; @@ -169,8 +171,6 @@ private: QTouchDevice *mTouchDevice = nullptr; - QSharedPointer<QWaylandBuffer> mPixmapCursor; - friend class QWaylandTouchExtension; friend class QWaylandQtKeyExtension; }; @@ -189,7 +189,7 @@ public: Keyboard(QWaylandInputDevice *p); ~Keyboard() override; - void stopRepeat(); + QWaylandWindow *focusWindow() const; void keyboard_keymap(uint32_t format, int32_t fd, @@ -206,49 +206,67 @@ public: uint32_t mods_latched, uint32_t mods_locked, uint32_t group) override; + void keyboard_repeat_info(int32_t rate, int32_t delay) override; QWaylandInputDevice *mParent = nullptr; - QPointer<QWaylandWindow> mFocus; -#if QT_CONFIG(xkbcommon) - xkb_context *mXkbContext = nullptr; - xkb_keymap *mXkbMap = nullptr; - xkb_state *mXkbState = nullptr; - xkb_compose_table *mXkbComposeTable = nullptr; - xkb_compose_state *mXkbComposeState = nullptr; -#endif + ::wl_surface *mFocus = nullptr; + uint32_t mNativeModifiers = 0; - int mRepeatKey; - uint32_t mRepeatCode; - uint32_t mRepeatTime; - QString mRepeatText; -#if QT_CONFIG(xkbcommon) - xkb_keysym_t mRepeatSym; -#endif + struct repeatKey { + int key; + uint32_t code; + uint32_t time; + QString text; + Qt::KeyboardModifiers modifiers; + uint32_t nativeVirtualKey; + uint32_t nativeModifiers; + } mRepeatKey; + QTimer mRepeatTimer; + int mRepeatRate = 25; + int mRepeatDelay = 400; + + uint32_t mKeymapFormat = WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1; Qt::KeyboardModifiers modifiers() const; private slots: - void repeatKey(); + void handleFocusDestroyed(); + void handleFocusLost(); private: #if QT_CONFIG(xkbcommon) - bool createDefaultKeyMap(); - void releaseKeyMap(); - void createComposeState(); - void releaseComposeState(); + bool createDefaultKeymap(); #endif + void handleKey(ulong timestamp, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, + quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, + const QString &text, bool autorepeat = false, ushort count = 1); +#if QT_CONFIG(xkbcommon) + QXkbCommon::ScopedXKBKeymap mXkbKeymap; + QXkbCommon::ScopedXKBState mXkbState; +#endif + friend class QWaylandInputDevice; }; -class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Pointer : public QtWayland::wl_pointer +class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Pointer : public QObject, public QtWayland::wl_pointer { - + Q_OBJECT public: - Pointer(QWaylandInputDevice *p); + explicit Pointer(QWaylandInputDevice *seat); ~Pointer() override; +#if QT_CONFIG(cursor) + QString cursorThemeName() const; + int cursorSize() const; // in surface coordinates + int idealCursorScale() const; + void updateCursorTheme(); + void updateCursor(); + CursorSurface *getOrCreateCursorSurface(); +#endif + QWaylandInputDevice *seat() const { return mParent; } +protected: void pointer_enter(uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy) override; void pointer_leave(uint32_t time, struct wl_surface *surface) override; @@ -260,13 +278,24 @@ public: uint32_t axis, wl_fixed_t value) override; +private slots: + void handleFocusDestroyed() { invalidateFocus(); } + +private: + void invalidateFocus(); + +public: void releaseButtons(); QWaylandInputDevice *mParent = nullptr; QPointer<QWaylandWindow> mFocus; uint32_t mEnterSerial = 0; #if QT_CONFIG(cursor) - uint32_t mCursorSerial = 0; + struct { + QWaylandCursorTheme *theme = nullptr; + int themeBufferScale = 0; + QScopedPointer<CursorSurface> surface; + } mCursor; #endif QPointF mSurfacePos; QPointF mGlobalPos; diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp index 97e0203cd..ea2b50b4a 100644 --- a/src/client/qwaylandintegration.cpp +++ b/src/client/qwaylandintegration.cpp @@ -86,6 +86,14 @@ #include "qwaylandinputdeviceintegration_p.h" #include "qwaylandinputdeviceintegrationfactory_p.h" +#ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE +#include <QtLinuxAccessibilitySupport/private/bridge_p.h> +#endif + +#if QT_CONFIG(xkbcommon) +#include <QtXkbCommonSupport/private/qxkbcommon_p.h> +#endif + QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -129,9 +137,6 @@ QWaylandIntegration::QWaylandIntegration() : mFontDb(new QGenericUnixFontDatabase()) #endif , mNativeInterface(new QWaylandNativeInterface(this)) -#if QT_CONFIG(accessibility) - , mAccessibility(new QPlatformAccessibility()) -#endif { initializeInputDeviceIntegration(); mDisplay.reset(new QWaylandDisplay(this)); @@ -145,20 +150,8 @@ QWaylandIntegration::QWaylandIntegration() #if QT_CONFIG(draganddrop) mDrag.reset(new QWaylandDrag(mDisplay.data())); #endif - QString icStr = QPlatformInputContextFactory::requested(); - if (!icStr.isNull()) { - mInputContext.reset(QPlatformInputContextFactory::create(icStr)); - } else { - //try to use the input context using the wl_text_input interface - QPlatformInputContext *ctx = new QWaylandInputContext(mDisplay.data()); - mInputContext.reset(ctx); - - //use the traditional way for on screen keyboards for now - if (!mInputContext.data()->isValid()) { - ctx = QPlatformInputContextFactory::create(); - mInputContext.reset(ctx); - } - } + + reconfigureInputContext(); } QWaylandIntegration::~QWaylandIntegration() @@ -277,6 +270,15 @@ QVariant QWaylandIntegration::styleHint(StyleHint hint) const #if QT_CONFIG(accessibility) QPlatformAccessibility *QWaylandIntegration::accessibility() const { + if (!mAccessibility) { +#ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE + Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QXcbIntegration", + "Initializing accessibility without event-dispatcher!"); + mAccessibility.reset(new QSpiAccessibleBridge()); +#else + mAccessibility.reset(new QPlatformAccessibility()); +#endif + } return mAccessibility.data(); } #endif @@ -291,6 +293,13 @@ QWaylandDisplay *QWaylandIntegration::display() const return mDisplay.data(); } +QList<int> QWaylandIntegration::possibleKeys(const QKeyEvent *event) const +{ + if (auto *seat = mDisplay->currentInputDevice()) + return seat->possibleKeys(event); + return {}; +} + QStringList QWaylandIntegration::themeNames() const { return GenericWaylandTheme::themeNames(); @@ -329,18 +338,16 @@ void QWaylandIntegration::initializeClientBufferIntegration() { mClientBufferIntegrationInitialized = true; - QString targetKey; - bool disableHardwareIntegration = qEnvironmentVariableIsSet("QT_WAYLAND_DISABLE_HW_INTEGRATION"); - disableHardwareIntegration = disableHardwareIntegration || !mDisplay->hardwareIntegration(); - if (disableHardwareIntegration) { - QByteArray clientBufferIntegrationName = qgetenv("QT_WAYLAND_CLIENT_BUFFER_INTEGRATION"); - if (clientBufferIntegrationName.isEmpty()) - clientBufferIntegrationName = QByteArrayLiteral("wayland-egl"); - targetKey = QString::fromLocal8Bit(clientBufferIntegrationName); - } else { - targetKey = mDisplay->hardwareIntegration()->clientBufferIntegration(); - if (targetKey == QLatin1String("wayland-eglstream-controller")) - targetKey = QLatin1String("wayland-egl"); + QString targetKey = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_CLIENT_BUFFER_INTEGRATION")); + + if (targetKey.isEmpty()) { + if (mDisplay->hardwareIntegration() + && mDisplay->hardwareIntegration()->clientBufferIntegration() != QLatin1String("wayland-eglstream-controller") + && mDisplay->hardwareIntegration()->clientBufferIntegration() != QLatin1String("linux-dmabuf-unstable-v1")) { + targetKey = mDisplay->hardwareIntegration()->clientBufferIntegration(); + } else { + targetKey = QLatin1Literal("wayland-egl"); + } } if (targetKey.isEmpty()) { @@ -349,29 +356,28 @@ void QWaylandIntegration::initializeClientBufferIntegration() } QStringList keys = QWaylandClientBufferIntegrationFactory::keys(); - if (keys.contains(targetKey)) { + qCDebug(lcQpaWayland) << "Available client buffer integrations:" << keys; + + if (keys.contains(targetKey)) mClientBufferIntegration.reset(QWaylandClientBufferIntegrationFactory::create(targetKey, QStringList())); - } - if (mClientBufferIntegration) + + if (mClientBufferIntegration) { + qCDebug(lcQpaWayland) << "Initializing client buffer integration" << targetKey; mClientBufferIntegration->initialize(mDisplay.data()); - else - qWarning("Failed to load client buffer integration: %s\n", qPrintable(targetKey)); + } else { + qCWarning(lcQpaWayland) << "Failed to load client buffer integration:" << targetKey; + qCWarning(lcQpaWayland) << "Available client buffer integrations:" << keys; + } } void QWaylandIntegration::initializeServerBufferIntegration() { mServerBufferIntegrationInitialized = true; - QString targetKey; + QString targetKey = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_SERVER_BUFFER_INTEGRATION")); - bool disableHardwareIntegration = qEnvironmentVariableIsSet("QT_WAYLAND_DISABLE_HW_INTEGRATION"); - disableHardwareIntegration = disableHardwareIntegration || !mDisplay->hardwareIntegration(); - if (disableHardwareIntegration) { - QByteArray serverBufferIntegrationName = qgetenv("QT_WAYLAND_SERVER_BUFFER_INTEGRATION"); - targetKey = QString::fromLocal8Bit(serverBufferIntegrationName); - } else { + if (targetKey.isEmpty() && mDisplay->hardwareIntegration()) targetKey = mDisplay->hardwareIntegration()->serverBufferIntegration(); - } if (targetKey.isEmpty()) { qWarning("Failed to determine what server buffer integration to use"); @@ -379,13 +385,18 @@ void QWaylandIntegration::initializeServerBufferIntegration() } QStringList keys = QWaylandServerBufferIntegrationFactory::keys(); - if (keys.contains(targetKey)) { + qCDebug(lcQpaWayland) << "Available server buffer integrations:" << keys; + + if (keys.contains(targetKey)) mServerBufferIntegration.reset(QWaylandServerBufferIntegrationFactory::create(targetKey, QStringList())); - } - if (mServerBufferIntegration) + + if (mServerBufferIntegration) { + qCDebug(lcQpaWayland) << "Initializing server buffer integration" << targetKey; mServerBufferIntegration->initialize(mDisplay.data()); - else - qWarning("Failed to load server buffer integration %s\n", qPrintable(targetKey)); + } else { + qCWarning(lcQpaWayland) << "Failed to load server buffer integration: " << targetKey; + qCWarning(lcQpaWayland) << "Available server buffer integrations:" << keys; + } } void QWaylandIntegration::initializeShellIntegration() @@ -413,7 +424,7 @@ void QWaylandIntegration::initializeShellIntegration() Q_FOREACH (QString preferredShell, preferredShells) { mShellIntegration.reset(createShellIntegration(preferredShell)); if (mShellIntegration) { - qDebug("Using the '%s' shell integration", qPrintable(preferredShell)); + qCDebug(lcQpaWayland, "Using the '%s' shell integration", qPrintable(preferredShell)); break; } } @@ -450,6 +461,42 @@ void QWaylandIntegration::initializeInputDeviceIntegration() } } +void QWaylandIntegration::reconfigureInputContext() +{ + if (!mDisplay) { + // This function can be called from QWaylandDisplay::registry_global() when we + // are in process of constructing QWaylandDisplay. Configuring input context + // in that case is done by calling reconfigureInputContext() from QWaylandIntegration + // constructor, after QWaylandDisplay has been constructed. + return; + } + + const QString &requested = QPlatformInputContextFactory::requested(); + if (requested == QLatin1String("qtvirtualkeyboard")) + qCWarning(lcQpaWayland) << "qtvirtualkeyboard currently is not supported at client-side," + " use QT_IM_MODULE=qtvirtualkeyboard at compositor-side."; + + if (requested.isNull()) + mInputContext.reset(new QWaylandInputContext(mDisplay.data())); + else + mInputContext.reset(QPlatformInputContextFactory::create(requested)); + + const QString defaultInputContext(QStringLiteral("compose")); + if ((!mInputContext || !mInputContext->isValid()) && requested != defaultInputContext) + mInputContext.reset(QPlatformInputContextFactory::create(defaultInputContext)); + +#if QT_CONFIG(xkbcommon) + QXkbCommon::setXkbContext(mInputContext.data(), mDisplay->xkbContext()); +#endif + + // Even if compositor-side input context handling has been requested, we fallback to + // client-side handling if compositor does not provide the text-input extension. This + // is why we need to check here which input context actually is being used. + mDisplay->mUsingInputContextFromCompositor = qobject_cast<QWaylandInputContext *>(mInputContext.data()); + + qCDebug(lcQpaWayland) << "using input method:" << inputContext()->metaObject()->className(); +} + QWaylandShellIntegration *QWaylandIntegration::createShellIntegration(const QString &integrationName) { if (QWaylandShellIntegrationFactory::keys().contains(integrationName)) { diff --git a/src/client/qwaylandintegration_p.h b/src/client/qwaylandintegration_p.h index a5a3d7b69..3aef2c4d9 100644 --- a/src/client/qwaylandintegration_p.h +++ b/src/client/qwaylandintegration_p.h @@ -106,6 +106,8 @@ public: QWaylandDisplay *display() const; + QList<int> possibleKeys(const QKeyEvent *event) const override; + QStringList themeNames() const override; QPlatformTheme *createPlatformTheme(const QString &name) const override; @@ -116,6 +118,8 @@ public: virtual QWaylandServerBufferIntegration *serverBufferIntegration() const; virtual QWaylandShellIntegration *shellIntegration() const; + void reconfigureInputContext(); + private: // NOTE: mDisplay *must* be destructed after mDrag and mClientBufferIntegration // and mShellIntegration. @@ -145,7 +149,7 @@ private: QScopedPointer<QPlatformNativeInterface> mNativeInterface; QScopedPointer<QPlatformInputContext> mInputContext; #if QT_CONFIG(accessibility) - QScopedPointer<QPlatformAccessibility> mAccessibility; + mutable QScopedPointer<QPlatformAccessibility> mAccessibility; #endif bool mFailed = false; bool mClientBufferIntegrationInitialized = false; diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp index 38d61f88c..5b04ae609 100644 --- a/src/client/qwaylandscreen.cpp +++ b/src/client/qwaylandscreen.cpp @@ -40,6 +40,7 @@ #include "qwaylandscreen_p.h" #include "qwaylanddisplay_p.h" +#include "qwaylandintegration_p.h" #include "qwaylandcursor_p.h" #include "qwaylandwindow_p.h" @@ -60,6 +61,14 @@ QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uin { if (auto *xdgOutputManager = waylandDisplay->xdgOutputManager()) initXdgOutput(xdgOutputManager); + + if (version < WL_OUTPUT_DONE_SINCE_VERSION) { + qCWarning(lcQpaWayland) << "wl_output done event not supported by compositor," + << "QScreen may not work correctly"; + mWaylandDisplay->forceRoundTrip(); // Give the compositor a chance to send geometry etc. + mOutputDone = true; // Fake the done event + maybeInitialize(); + } } QWaylandScreen::~QWaylandScreen() @@ -68,6 +77,24 @@ QWaylandScreen::~QWaylandScreen() zxdg_output_v1::destroy(); } +void QWaylandScreen::maybeInitialize() +{ + Q_ASSERT(!mInitialized); + + if (!mOutputDone) + return; + + if (mWaylandDisplay->xdgOutputManager() && !mXdgOutputDone) + return; + + mInitialized = true; + mWaylandDisplay->handleScreenInitialized(this); + + updateOutputProperties(); + if (zxdg_output_v1::isInitialized()) + updateXdgOutputProperties(); +} + void QWaylandScreen::initXdgOutput(QtWayland::zxdg_output_manager_v1 *xdgOutputManager) { Q_ASSERT(xdgOutputManager); @@ -176,19 +203,10 @@ qreal QWaylandScreen::refreshRate() const } #if QT_CONFIG(cursor) - QPlatformCursor *QWaylandScreen::cursor() const { - return const_cast<QWaylandScreen *>(this)->waylandCursor(); -} - -QWaylandCursor *QWaylandScreen::waylandCursor() -{ - if (!mWaylandCursor) - mWaylandCursor.reset(new QWaylandCursor(this)); - return mWaylandCursor.data(); + return mWaylandDisplay->waylandCursor(); } - #endif // QT_CONFIG(cursor) QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window) @@ -241,10 +259,15 @@ void QWaylandScreen::output_scale(int32_t factor) void QWaylandScreen::output_done() { - // the done event is sent after all the geometry and the mode events are sent, - // and the last mode event to be sent is the active one, so we can trust the - // values of mGeometry and mRefreshRate here + mOutputDone = true; + if (mInitialized) + updateOutputProperties(); + else + maybeInitialize(); +} +void QWaylandScreen::updateOutputProperties() +{ if (mTransform >= 0) { bool isPortrait = mGeometry.height() > mGeometry.width(); switch (mTransform) { @@ -271,7 +294,9 @@ void QWaylandScreen::output_done() QWindowSystemInterface::handleScreenOrientationChange(screen(), m_orientation); mTransform = -1; } + QWindowSystemInterface::handleScreenRefreshRateChange(screen(), refreshRate()); + if (!zxdg_output_v1::isInitialized()) QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), geometry()); } @@ -289,9 +314,24 @@ void QWaylandScreen::zxdg_output_v1_logical_size(int32_t width, int32_t height) void QWaylandScreen::zxdg_output_v1_done() { - QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), geometry()); + mXdgOutputDone = true; + if (mInitialized) + updateXdgOutputProperties(); + else + maybeInitialize(); } +void QWaylandScreen::zxdg_output_v1_name(const QString &name) +{ + mOutputName = name; } +void QWaylandScreen::updateXdgOutputProperties() +{ + Q_ASSERT(zxdg_output_v1::isInitialized()); + QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), geometry()); +} + +} // namespace QtWaylandClient + QT_END_NAMESPACE diff --git a/src/client/qwaylandscreen_p.h b/src/client/qwaylandscreen_p.h index 6e4ed94f7..e9e07d9cd 100644 --- a/src/client/qwaylandscreen_p.h +++ b/src/client/qwaylandscreen_p.h @@ -71,6 +71,8 @@ public: QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id); ~QWaylandScreen() override; + void maybeInitialize(); + void initXdgOutput(QtWayland::zxdg_output_manager_v1 *xdgOutputManager); QWaylandDisplay *display() const; @@ -98,7 +100,6 @@ public: #if QT_CONFIG(cursor) QPlatformCursor *cursor() const override; - QWaylandCursor *waylandCursor(); #endif uint32_t outputId() const { return m_outputId; } @@ -117,11 +118,14 @@ private: int32_t transform) override; void output_scale(int32_t factor) override; void output_done() override; + void updateOutputProperties(); // XdgOutput void zxdg_output_v1_logical_position(int32_t x, int32_t y) override; void zxdg_output_v1_logical_size(int32_t width, int32_t height) override; void zxdg_output_v1_done() override; + void zxdg_output_v1_name(const QString &name) override; + void updateXdgOutputProperties(); int m_outputId; QWaylandDisplay *mWaylandDisplay = nullptr; @@ -137,6 +141,9 @@ private: QSize mPhysicalSize; QString mOutputName; Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation; + bool mOutputDone = false; + bool mXdgOutputDone = false; + bool mInitialized = false; #if QT_CONFIG(cursor) QScopedPointer<QWaylandCursor> mWaylandCursor; diff --git a/src/client/qwaylandshellsurface_p.h b/src/client/qwaylandshellsurface_p.h index 6bc3258c7..f5f202d08 100644 --- a/src/client/qwaylandshellsurface_p.h +++ b/src/client/qwaylandshellsurface_p.h @@ -54,8 +54,6 @@ #include <QtCore/QSize> #include <QObject> -#include <wayland-client.h> - #include <QtWaylandClient/private/qwayland-wayland.h> #include <QtWaylandClient/qtwaylandclientglobal.h> @@ -75,10 +73,10 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandShellSurface : public QObject public: explicit QWaylandShellSurface(QWaylandWindow *window); ~QWaylandShellSurface() override {} - virtual void resize(QWaylandInputDevice * /*inputDevice*/, enum wl_shell_surface_resize /*edges*/) - {} + virtual void resize(QWaylandInputDevice * /*inputDevice*/, Qt::Edges /*edges*/) {} virtual bool move(QWaylandInputDevice *) { return false; } + virtual bool showWindowMenu(QWaylandInputDevice *seat) { Q_UNUSED(seat); return false; } virtual void setTitle(const QString & /*title*/) {} virtual void setAppId(const QString & /*appId*/) {} @@ -99,6 +97,10 @@ public: virtual void requestWindowStates(Qt::WindowStates states) {Q_UNUSED(states);} virtual bool wantsDecorations() const { return false; } + virtual void propagateSizeHints() {} + + virtual void setWindowGeometry(const QRect &rect) { Q_UNUSED(rect); } + private: QWaylandWindow *m_window = nullptr; friend class QWaylandWindow; diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp index 3fe2ce80c..34044ec9b 100644 --- a/src/client/qwaylandshmbackingstore.cpp +++ b/src/client/qwaylandshmbackingstore.cpp @@ -49,8 +49,7 @@ #include <QtGui/QPainter> #include <QMutexLocker> -#include <wayland-client.h> -#include <wayland-client-protocol.h> +#include <QtWaylandClient/private/wayland-wayland-client-protocol.h> #include <unistd.h> #include <sys/mman.h> diff --git a/src/client/qwaylandsubsurface_p.h b/src/client/qwaylandsubsurface_p.h index e9a7cb20e..76da10b24 100644 --- a/src/client/qwaylandsubsurface_p.h +++ b/src/client/qwaylandsubsurface_p.h @@ -51,8 +51,6 @@ // We mean it. // -#include <wayland-client.h> - #include <QtCore/qglobal.h> #include <QtCore/qmutex.h> diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp index cecdbda92..8b2c12277 100644 --- a/src/client/qwaylandwindow.cpp +++ b/src/client/qwaylandwindow.cpp @@ -52,11 +52,6 @@ #include "qwaylandshmbackingstore_p.h" #include "qwaylandshellintegration_p.h" -#if QT_CONFIG(wayland_datadevice) -#include "qwaylanddatadevice_p.h" -#endif - - #include <QtCore/QFileInfo> #include <QtCore/QPointer> #include <QtCore/QRegularExpression> @@ -69,8 +64,6 @@ #include <QtCore/QDebug> #include <QtCore/QThread> -#include <wayland-client.h> - QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -100,10 +93,6 @@ QWaylandWindow::~QWaylandWindow() if (isInitialized()) reset(false); - QList<QWaylandInputDevice *> inputDevices = mDisplay->inputDevices(); - for (int i = 0; i < inputDevices.size(); ++i) - inputDevices.at(i)->handleWindowDestroyed(this); - const QWindow *parent = window(); foreach (QWindow *w, QGuiApplication::topLevelWindows()) { if (w->transientParent() == parent) @@ -245,8 +234,10 @@ void QWaylandWindow::reset(bool sendDestroyEvent) mShellSurface = nullptr; delete mSubSurfaceWindow; mSubSurfaceWindow = nullptr; - if (isInitialized()) + if (isInitialized()) { + emit wlSurfaceDestroyed(); destroy(); + } mScreens.clear(); if (mFrameCallback) { @@ -348,6 +339,9 @@ void QWaylandWindow::setGeometry(const QRect &rect) QRect exposeGeometry(QPoint(), geometry().size()); if (exposeGeometry != mLastExposeGeometry) sendExposeEvent(exposeGeometry); + + if (mShellSurface) + mShellSurface->setWindowGeometry(windowContentGeometry()); } void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset) @@ -502,9 +496,11 @@ void QWaylandWindow::surface_enter(wl_output *output) auto addedScreen = QWaylandScreen::fromWlOutput(output); if (mScreens.contains(addedScreen)) { - qWarning() << "Unexpected wl_surface.enter received for output with id:" - << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output)) - << "screen name:" << addedScreen->name() << "screen model:" << addedScreen->model(); + qCWarning(lcQpaWayland) + << "Ignoring unexpected wl_surface.enter received for output with id:" + << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output)) + << "screen name:" << addedScreen->name() << "screen model:" << addedScreen->model() + << "This is most likely a bug in the compositor."; return; } @@ -521,9 +517,12 @@ void QWaylandWindow::surface_leave(wl_output *output) auto *removedScreen = QWaylandScreen::fromWlOutput(output); bool wasRemoved = mScreens.removeOne(removedScreen); if (!wasRemoved) { - qWarning() << "Unexpected wl_surface.leave received for output with id:" - << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output)) - << "screen name:" << removedScreen->name() << "screen model:" << removedScreen->model(); + qCWarning(lcQpaWayland) + << "Ignoring unexpected wl_surface.leave received for output with id:" + << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output)) + << "screen name:" << removedScreen->name() + << "screen model:" << removedScreen->model() + << "This is most likely a bug in the compositor."; return; } @@ -605,6 +604,11 @@ void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage) wl_surface::commit(); } +void QWaylandWindow::commit() +{ + wl_surface::commit(); +} + const wl_callback_listener QWaylandWindow::callbackListener = { [](void *data, wl_callback *callback, uint32_t time) { Q_UNUSED(callback); @@ -670,6 +674,23 @@ QMargins QWaylandWindow::frameMargins() const return QPlatformWindow::frameMargins(); } +/*! + * Size, with decorations (including including eventual shadows) in wl_surface coordinates + */ +QSize QWaylandWindow::surfaceSize() const +{ + return geometry().marginsAdded(frameMargins()).size(); +} + +/*! + * Window geometry as defined by the xdg-shell spec (in wl_surface coordinates) + * topLeft is where the shadow stops and the decorations border start. + */ +QRect QWaylandWindow::windowContentGeometry() const +{ + return QRect(QPoint(), surfaceSize()); +} + QWaylandShellSurface *QWaylandWindow::shellSurface() const { return mShellSurface; @@ -864,9 +885,7 @@ void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylan #if QT_CONFIG(cursor) if (e.type == QWaylandPointerEvent::Enter) { - QRect windowGeometry = window()->frameGeometry(); - windowGeometry.moveTopLeft({0, 0}); // convert to wayland surface coordinates - QRect contentGeometry = windowGeometry.marginsRemoved(frameMargins()); + QRect contentGeometry = windowContentGeometry().marginsRemoved(frameMargins()); if (contentGeometry.contains(e.local.toPoint())) restoreMouseCursor(inputDevice); } @@ -963,7 +982,8 @@ void QWaylandWindow::handleScreenChanged() #if QT_CONFIG(cursor) void QWaylandWindow::setMouseCursor(QWaylandInputDevice *device, const QCursor &cursor) { - device->setCursor(cursor, waylandScreen()); + int fallbackBufferScale = int(devicePixelRatio()); + device->setCursor(&cursor, {}, fallbackBufferScale); } void QWaylandWindow::restoreMouseCursor(QWaylandInputDevice *device) @@ -977,16 +997,6 @@ void QWaylandWindow::requestActivateWindow() qCWarning(lcQpaWayland) << "Wayland does not support QWindow::requestActivate()"; } -void QWaylandWindow::unfocus() -{ -#if QT_CONFIG(clipboard) - QWaylandInputDevice *inputDevice = mDisplay->currentInputDevice(); - if (inputDevice && inputDevice->dataDevice()) { - inputDevice->dataDevice()->invalidateSelectionOffer(); - } -#endif -} - bool QWaylandWindow::isExposed() const { if (!window()->isVisible()) @@ -1178,6 +1188,12 @@ void QWaylandWindow::addAttachOffset(const QPoint point) mOffset += point; } +void QWaylandWindow::propagateSizeHints() +{ + if (mShellSurface) + mShellSurface->propagateSizeHints(); +} + bool QtWaylandClient::QWaylandWindow::startSystemMove(const QPoint &pos) { Q_UNUSED(pos); diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h index c47123dc9..8c1ebe167 100644 --- a/src/client/qwaylandwindow_p.h +++ b/src/client/qwaylandwindow_p.h @@ -120,9 +120,13 @@ public: void handleExpose(const QRegion ®ion); void commit(QWaylandBuffer *buffer, const QRegion &damage); + void commit(); + bool waitForFrameSync(int timeout); QMargins frameMargins() const override; + QSize surfaceSize() const; + QRect windowContentGeometry() const; static QWaylandWindow *fromWlSurface(::wl_surface *surface); @@ -149,7 +153,6 @@ public: void requestActivateWindow() override; bool isExposed() const override; bool isActive() const override; - void unfocus(); QWaylandAbstractDecoration *decoration() const; @@ -186,7 +189,7 @@ public: QWaylandShmBackingStore *backingStore() const { return mBackingStore; } bool setKeyboardGrabEnabled(bool) override { return false; } - void propagateSizeHints() override { } + void propagateSizeHints() override; void addAttachOffset(const QPoint point); bool startSystemMove(const QPoint &pos) override; @@ -199,6 +202,9 @@ public: public slots: void applyConfigure(); +signals: + void wlSurfaceDestroyed(); + protected: void surface_enter(struct ::wl_output *output) override; void surface_leave(struct ::wl_output *output) override; diff --git a/src/client/qwaylandwindowmanagerintegration_p.h b/src/client/qwaylandwindowmanagerintegration_p.h index 1319abd91..31de6ddd3 100644 --- a/src/client/qwaylandwindowmanagerintegration_p.h +++ b/src/client/qwaylandwindowmanagerintegration_p.h @@ -54,7 +54,6 @@ #include <QtCore/QObject> #include <QtCore/QScopedPointer> -#include <wayland-client.h> #include <QtServiceSupport/private/qgenericunixservices_p.h> #include <QtWaylandClient/private/qwayland-qt-windowmanager.h> |