diff options
-rw-r--r-- | .qmake.conf | 2 | ||||
-rw-r--r-- | src/client/qwaylandabstractdecoration.cpp | 8 | ||||
-rw-r--r-- | src/client/qwaylanddisplay.cpp | 5 | ||||
-rw-r--r-- | src/client/qwaylanddisplay_p.h | 3 | ||||
-rw-r--r-- | src/client/qwaylandinputcontext.cpp | 95 | ||||
-rw-r--r-- | src/client/qwaylandinputcontext_p.h | 21 | ||||
-rw-r--r-- | src/client/qwaylandinputdevice.cpp | 10 | ||||
-rw-r--r-- | src/client/qwaylandintegration.cpp | 8 | ||||
-rw-r--r-- | src/client/qwaylandwindow.cpp | 15 | ||||
-rw-r--r-- | src/compositor/extensions/qwaylandtextinputmanager.cpp | 2 | ||||
-rw-r--r-- | src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp | 9 | ||||
-rw-r--r-- | tests/auto/client/surface/tst_surface.cpp | 33 |
12 files changed, 182 insertions, 29 deletions
diff --git a/.qmake.conf b/.qmake.conf index bb8d0645..d1dc9832 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -4,4 +4,4 @@ DEFINES += QT_NO_FOREACH DEFINES += QT_NO_JAVA_STYLE_ITERATORS DEFINES += QT_NO_LINKED_LIST -MODULE_VERSION = 5.15.3 +MODULE_VERSION = 5.15.4 diff --git a/src/client/qwaylandabstractdecoration.cpp b/src/client/qwaylandabstractdecoration.cpp index 87dd6cea..b6ee43c9 100644 --- a/src/client/qwaylandabstractdecoration.cpp +++ b/src/client/qwaylandabstractdecoration.cpp @@ -108,11 +108,11 @@ void QWaylandAbstractDecoration::setWaylandWindow(QWaylandWindow *window) static QRegion marginsRegion(const QSize &size, const QMargins &margins) { QRegion r; - const int widthWithMargins = margins.left() + size.width() + margins.right(); - r += QRect(0, 0, widthWithMargins, margins.top()); // top - r += QRect(0, size.height()+margins.top(), widthWithMargins, margins.bottom()); //bottom + + r += QRect(0, 0, size.width(), margins.top()); // top + r += QRect(0, size.height()-margins.bottom(), size.width(), margins.bottom()); //bottom r += QRect(0, margins.top(), margins.left(), size.height()); //left - r += QRect(size.width()+margins.left(), margins.top(), margins.right(), size.height()); // right + r += QRect(size.width()-margins.left(), margins.top(), margins.right(), size.height()-margins.top()); // right return r; } diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp index fe094f6f..f10c1f79 100644 --- a/src/client/qwaylanddisplay.cpp +++ b/src/client/qwaylanddisplay.cpp @@ -206,10 +206,11 @@ void QWaylandDisplay::checkError() const int ecode = wl_display_get_error(mDisplay); if ((ecode == EPIPE || ecode == ECONNRESET)) { // special case this to provide a nicer error - qFatal("The Wayland connection broke. Did the Wayland compositor die?"); + qWarning("The Wayland connection broke. Did the Wayland compositor die?"); } else { - qFatal("The Wayland connection experienced a fatal error: %s", strerror(ecode)); + qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode)); } + _exit(1); } void QWaylandDisplay::flushRequests() diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h index 188e9131..3b092bc8 100644 --- a/src/client/qwaylanddisplay_p.h +++ b/src/client/qwaylanddisplay_p.h @@ -175,8 +175,6 @@ public: QWaylandHardwareIntegration *hardwareIntegration() const { return mHardwareIntegration.data(); } QWaylandXdgOutputManagerV1 *xdgOutputManager() const { return mXdgOutputManager.data(); } - bool usingInputContextFromCompositor() const { return mUsingInputContextFromCompositor; } - struct RegistryGlobal { uint32_t id; QString interface; @@ -282,7 +280,6 @@ private: QReadWriteLock m_frameQueueLock; 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; diff --git a/src/client/qwaylandinputcontext.cpp b/src/client/qwaylandinputcontext.cpp index e9afe05e..ef5aa375 100644 --- a/src/client/qwaylandinputcontext.cpp +++ b/src/client/qwaylandinputcontext.cpp @@ -406,6 +406,8 @@ bool QWaylandInputContext::isValid() const void QWaylandInputContext::reset() { qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; + if (m_composeState) + xkb_compose_state_reset(m_composeState); QPlatformInputContext::reset(); @@ -526,9 +528,14 @@ Qt::LayoutDirection QWaylandInputContext::inputDirection() const return textInput()->inputDirection(); } -void QWaylandInputContext::setFocusObject(QObject *) +void QWaylandInputContext::setFocusObject(QObject *object) { qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; +#if QT_CONFIG(xkbcommon) + m_focusObject = object; +#else + Q_UNUSED(object); +#endif if (!textInput()) return; @@ -561,6 +568,92 @@ QWaylandTextInput *QWaylandInputContext::textInput() const return mDisplay->defaultInputDevice()->textInput(); } +#if QT_CONFIG(xkbcommon) + +void QWaylandInputContext::ensureInitialized() +{ + if (m_initialized) + return; + + if (!m_XkbContext) { + qCWarning(qLcQpaInputMethods) << "error: xkb context has not been set on" << metaObject()->className(); + return; + } + + m_initialized = true; + const char *locale = setlocale(LC_CTYPE, ""); + if (!locale) + locale = setlocale(LC_CTYPE, nullptr); + qCDebug(qLcQpaInputMethods) << "detected locale (LC_CTYPE):" << locale; + + m_composeTable = xkb_compose_table_new_from_locale(m_XkbContext, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); + if (m_composeTable) + m_composeState = xkb_compose_state_new(m_composeTable, XKB_COMPOSE_STATE_NO_FLAGS); + + if (!m_composeTable) { + qCWarning(qLcQpaInputMethods, "failed to create compose table"); + return; + } + if (!m_composeState) { + qCWarning(qLcQpaInputMethods, "failed to create compose state"); + return; + } +} + +bool QWaylandInputContext::filterEvent(const QEvent *event) +{ + auto keyEvent = static_cast<const QKeyEvent *>(event); + if (keyEvent->type() != QEvent::KeyPress) + return false; + + if (!inputMethodAccepted()) + return false; + + // lazy initialization - we don't want to do this on an app startup + ensureInitialized(); + + if (!m_composeTable || !m_composeState) + return false; + + xkb_compose_state_feed(m_composeState, keyEvent->nativeVirtualKey()); + + switch (xkb_compose_state_get_status(m_composeState)) { + case XKB_COMPOSE_COMPOSING: + return true; + case XKB_COMPOSE_CANCELLED: + reset(); + return false; + case XKB_COMPOSE_COMPOSED: + { + const int size = xkb_compose_state_get_utf8(m_composeState, nullptr, 0); + QVarLengthArray<char, 32> buffer(size + 1); + xkb_compose_state_get_utf8(m_composeState, buffer.data(), buffer.size()); + QString composedText = QString::fromUtf8(buffer.constData()); + + QInputMethodEvent event; + event.setCommitString(composedText); + + if (!m_focusObject && qApp) + m_focusObject = qApp->focusObject(); + + if (m_focusObject) + QCoreApplication::sendEvent(m_focusObject, &event); + else + qCWarning(qLcQpaInputMethods, "no focus object"); + + reset(); + return true; + } + case XKB_COMPOSE_NOTHING: + return false; + default: + Q_UNREACHABLE(); + return false; + } +} + +#endif + } QT_END_NAMESPACE diff --git a/src/client/qwaylandinputcontext_p.h b/src/client/qwaylandinputcontext_p.h index 10132dfe..50db6344 100644 --- a/src/client/qwaylandinputcontext_p.h +++ b/src/client/qwaylandinputcontext_p.h @@ -61,6 +61,10 @@ #include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h> #include <qwaylandinputmethodeventbuilder_p.h> +#include <qtwaylandclientglobal_p.h> +#if QT_CONFIG(xkbcommon) +#include <xkbcommon/xkbcommon-compose.h> +#endif struct wl_callback; struct wl_callback_listener; @@ -155,11 +159,28 @@ public: void setFocusObject(QObject *object) override; +#if QT_CONFIG(xkbcommon) + bool filterEvent(const QEvent *event) override; + + // This invokable is called from QXkbCommon::setXkbContext(). + Q_INVOKABLE void setXkbContext(struct xkb_context *context) { m_XkbContext = context; } +#endif + private: QWaylandTextInput *textInput() const; QWaylandDisplay *mDisplay = nullptr; QPointer<QWindow> mCurrentWindow; + +#if QT_CONFIG(xkbcommon) + void ensureInitialized(); + + bool m_initialized = false; + QObject *m_focusObject = nullptr; + xkb_compose_table *m_composeTable = nullptr; + xkb_compose_state *m_composeState = nullptr; + struct xkb_context *m_XkbContext = nullptr; +#endif }; } diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp index ed4a0eb4..613fe862 100644 --- a/src/client/qwaylandinputdevice.cpp +++ b/src/client/qwaylandinputdevice.cpp @@ -1201,7 +1201,7 @@ void QWaylandInputDevice::Keyboard::handleKey(ulong timestamp, QEvent::Type type QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext(); bool filtered = false; - if (inputContext && !mParent->mQDisplay->usingInputContextFromCompositor()) { + if (inputContext) { QKeyEvent event(type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorepeat, count); event.setTimestamp(timestamp); @@ -1407,6 +1407,14 @@ void QWaylandInputDevice::handleTouchPoint(int id, Qt::TouchPointState state, co it = mTouch->mPendingTouchPoints.insert(end, QWindowSystemInterface::TouchPoint()); it->id = id; } + // If the touch points were up and down in same frame, send out frame right away + else if ((it->state == Qt::TouchPointPressed && state == Qt::TouchPointReleased) + || (it->state == Qt::TouchPointReleased && state == Qt::TouchPointPressed)) { + mTouch->touch_frame(); + it = mTouch->mPendingTouchPoints.insert(mTouch->mPendingTouchPoints.end(), QWindowSystemInterface::TouchPoint()); + it->id = id; + } + QWindowSystemInterface::TouchPoint &tp = *it; // Only moved and pressed needs to update/set position diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp index 7ad8e05e..c53ccb78 100644 --- a/src/client/qwaylandintegration.cpp +++ b/src/client/qwaylandintegration.cpp @@ -474,13 +474,11 @@ void QWaylandIntegration::reconfigureInputContext() #if QT_CONFIG(xkbcommon) QXkbCommon::setXkbContext(mInputContext.data(), mDisplay->xkbContext()); + if (QWaylandInputContext* waylandInput = qobject_cast<QWaylandInputContext*>(mInputContext.get())) { + waylandInput->setXkbContext(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(); } diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp index e875af3a..e96d8fe9 100644 --- a/src/client/qwaylandwindow.cpp +++ b/src/client/qwaylandwindow.cpp @@ -342,7 +342,12 @@ void QWaylandWindow::setGeometry_helper(const QRect &rect) if (mSubSurfaceWindow) { QMargins m = QPlatformWindow::parent()->frameMargins(); mSubSurfaceWindow->set_position(rect.x() + m.left(), rect.y() + m.top()); - mSubSurfaceWindow->parent()->window()->requestUpdate(); + + QWaylandWindow *parentWindow = mSubSurfaceWindow->parent(); + if (parentWindow && parentWindow->isExposed()) { + QRect parentExposeGeometry(QPoint(), parentWindow->geometry().size()); + parentWindow->sendExposeEvent(parentExposeGeometry); + } } } @@ -1234,12 +1239,14 @@ bool QWaylandWindow::isOpaque() const void QWaylandWindow::setOpaqueArea(const QRegion &opaqueArea) { - if (opaqueArea == mOpaqueArea || !mSurface) + const QRegion translatedOpaqueArea = opaqueArea.translated(frameMargins().left(), frameMargins().top()); + + if (translatedOpaqueArea == mOpaqueArea || !mSurface) return; - mOpaqueArea = opaqueArea; + mOpaqueArea = translatedOpaqueArea; - struct ::wl_region *region = mDisplay->createRegion(opaqueArea); + struct ::wl_region *region = mDisplay->createRegion(translatedOpaqueArea); mSurface->set_opaque_region(region); wl_region_destroy(region); } diff --git a/src/compositor/extensions/qwaylandtextinputmanager.cpp b/src/compositor/extensions/qwaylandtextinputmanager.cpp index 495ebae8..d9938d1f 100644 --- a/src/compositor/extensions/qwaylandtextinputmanager.cpp +++ b/src/compositor/extensions/qwaylandtextinputmanager.cpp @@ -51,6 +51,8 @@ void QWaylandTextInputManagerPrivate::zwp_text_input_manager_v2_get_text_input(R textInput = new QWaylandTextInput(seat, compositor); } textInput->add(resource->client(), id, wl_resource_get_version(resource->handle)); + if (!textInput->isInitialized()) + textInput->initialize(); } QWaylandTextInputManager::QWaylandTextInputManager() diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp index 1c762944..3a1569f7 100644 --- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp @@ -178,9 +178,12 @@ void QWaylandXdgSurface::Toplevel::requestWindowStates(Qt::WindowStates states) } if (changedStates & Qt::WindowFullScreen) { - if (states & Qt::WindowFullScreen) - set_fullscreen(nullptr); - else + if (states & Qt::WindowFullScreen) { + auto screen = m_xdgSurface->window()->waylandScreen(); + if (screen) { + set_fullscreen(screen->output()); + } + } else unset_fullscreen(); } diff --git a/tests/auto/client/surface/tst_surface.cpp b/tests/auto/client/surface/tst_surface.cpp index b8a65f15..95e4e609 100644 --- a/tests/auto/client/surface/tst_surface.cpp +++ b/tests/auto/client/surface/tst_surface.cpp @@ -167,17 +167,40 @@ void tst_surface::negotiateShmFormat() void tst_surface::createSubsurface() { QRasterWindow window; - window.resize(64, 64); - window.show(); - QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); - exec([=] { xdgToplevel()->sendCompleteConfigure(); }); - QCOMPOSITOR_TRY_VERIFY(xdgSurface()->m_committedConfigureSerial); + window.setObjectName("main"); + window.resize(200, 200); QRasterWindow subWindow; + subWindow.setObjectName("subwindow"); subWindow.setParent(&window); subWindow.resize(64, 64); + + window.show(); subWindow.show(); + QCOMPOSITOR_TRY_VERIFY(subSurface()); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + exec([=] { xdgToplevel()->sendCompleteConfigure(); }); + QCOMPOSITOR_TRY_VERIFY(xdgSurface()->m_committedConfigureSerial); + + const Surface *mainSurface = exec([=] {return surface(0);}); + const Surface *childSurface = exec([=] {return surface(1);}); + QSignalSpy mainSurfaceCommitSpy(mainSurface, &Surface::commit); + QSignalSpy childSurfaceCommitSpy(childSurface, &Surface::commit); + + // Move subsurface. The parent should redraw and commit + subWindow.setGeometry(100, 100, 64, 64); + // the toplevel should commit to indicate the subsurface moved + QCOMPOSITOR_TRY_COMPARE(mainSurfaceCommitSpy.count(), 1); + mainSurfaceCommitSpy.clear(); + childSurfaceCommitSpy.clear(); + + // Move and resize the subSurface. The parent should redraw and commit + // The child should also redraw + subWindow.setGeometry(50, 50, 80, 80); + QCOMPOSITOR_TRY_COMPARE(mainSurfaceCommitSpy.count(), 1); + QCOMPOSITOR_TRY_COMPARE(childSurfaceCommitSpy.count(), 1); + } // Used to cause a crash in libwayland (QTBUG-79674) |