diff options
author | Johan Klokkhammer Helsing <johan.helsing@qt.io> | 2019-05-07 03:01:52 +0200 |
---|---|---|
committer | Johan Klokkhammer Helsing <johan.helsing@qt.io> | 2019-05-07 12:39:12 +0200 |
commit | 17b1705ce4c8bc9ca19a6b424a00a045134b3aac (patch) | |
tree | 03e7b7d461064dd007179cd29da9c024e6c8f190 | |
parent | 4ab3cbcc5c4da6384e9081993c5ba24303a382bd (diff) | |
parent | a658a10f6a42e67bd762f87851c23cc1c1e3b141 (diff) | |
download | qtwayland-17b1705ce4c8bc9ca19a6b424a00a045134b3aac.tar.gz |
Merge remote-tracking branch 'origin/5.13' into dev
Conflicts:
src/client/qwaylanddisplay_p.h
src/client/qwaylandwindow.cpp
Change-Id: I50eb5c83a8b81e4bdb032b68d41f429b17d0a74d
54 files changed, 1083 insertions, 924 deletions
diff --git a/dist/changes-5.12.3 b/dist/changes-5.12.3 new file mode 100644 index 00000000..da95edbb --- /dev/null +++ b/dist/changes-5.12.3 @@ -0,0 +1,33 @@ +Qt 5.12.3 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0 through 5.12.2. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +https://doc.qt.io/qt-5/index.html + +The Qt version 5.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Compositor * +**************************************************************************** + + - Fixed a slightly unusual cleanup order that would lead to crashes in some + graphics drivers. + +**************************************************************************** +* QPA plugin * +**************************************************************************** + + - Fixed a bug where the window decoration's damaged area didn't cover the + entire decoration. This meant some compositors would not redraw those + areas. diff --git a/examples/wayland/pure-qml/qml/main.qml b/examples/wayland/pure-qml/qml/main.qml index 69be7cf1..483de751 100644 --- a/examples/wayland/pure-qml/qml/main.qml +++ b/examples/wayland/pure-qml/qml/main.qml @@ -72,6 +72,6 @@ WaylandCompositor { onWlShellSurfaceCreated: screen.handleShellSurface(shellSurface) } - // Extension for Virtual keyboard support + // Extension for Input Method (QT_IM_MODULE) support at compositor-side TextInputManager {} } diff --git a/src/3rdparty/protocol/qt_attribution.json b/src/3rdparty/protocol/qt_attribution.json index ad32d3af..7e068f75 100644 --- a/src/3rdparty/protocol/qt_attribution.json +++ b/src/3rdparty/protocol/qt_attribution.json @@ -178,7 +178,7 @@ Copyright © 2015, 2016 Jan Arne Petersen" "Name": "Wayland EGLStream Controller Protocol", "QDocModule": "qtwaylandcompositor", "QtUsage": "Used in the Qt Wayland Compositor", - "Files": "wayland-eglstream-controller.xml", + "Files": "wl-eglstream-controller.xml", "Description": "Allows clients to request that the compositor creates its EGLStream.", "Homepage": "https://github.com/NVIDIA/egl-wayland", diff --git a/src/client/client.pro b/src/client/client.pro index 4be288c8..ff9e845f 100644 --- a/src/client/client.pro +++ b/src/client/client.pro @@ -15,8 +15,9 @@ 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 @@ -49,7 +50,6 @@ SOURCES += qwaylandintegration.cpp \ qwaylandtouch.cpp \ qwaylandqtkey.cpp \ ../shared/qwaylandmimehelper.cpp \ - ../shared/qwaylandxkb.cpp \ ../shared/qwaylandinputmethodeventbuilder.cpp \ qwaylandabstractdecoration.cpp \ qwaylanddecorationfactory.cpp \ @@ -84,7 +84,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/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp index 9a4c76b1..7c32b6bf 100644 --- a/src/client/qwaylanddisplay.cpp +++ b/src/client/qwaylanddisplay.cpp @@ -70,6 +70,8 @@ #include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h> +#include <QtCore/private/qcore_unix_p.h> + #include <QtCore/QAbstractEventDispatcher> #include <QtGui/qpa/qwindowsysteminterface.h> #include <QtGui/private/qguiapplication_p.h> @@ -91,13 +93,6 @@ struct wl_surface *QWaylandDisplay::createSurface(void *handle) return surface; } -QWaylandShellSurface *QWaylandDisplay::createShellSurface(QWaylandWindow *window) -{ - if (!mWaylandIntegration->shellIntegration()) - return nullptr; - return mWaylandIntegration->shellIntegration()->createShellSurface(window); -} - struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion) { struct ::wl_region *region = mCompositor.create_region(); @@ -111,12 +106,18 @@ struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion) ::wl_subsurface *QWaylandDisplay::createSubSurface(QWaylandWindow *window, QWaylandWindow *parent) { if (!mSubCompositor) { + qCWarning(lcQpaWayland) << "Can't create subsurface, not supported by the compositor."; return nullptr; } return mSubCompositor->get_subsurface(window->wlSurface(), parent->wlSurface()); } +QWaylandShellIntegration *QWaylandDisplay::shellIntegration() const +{ + return mWaylandIntegration->shellIntegration(); +} + QWaylandClientBufferIntegration * QWaylandDisplay::clientBufferIntegration() const { return mWaylandIntegration->clientBufferIntegration(); @@ -143,7 +144,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) @@ -158,6 +170,7 @@ QWaylandDisplay::~QWaylandDisplay(void) QWindowSystemInterface::handleScreenRemoved(screen); } mScreens.clear(); + qDeleteAll(mWaitingScreens); #if QT_CONFIG(wayland_datadevice) delete mDndSelectionHandler.take(); @@ -194,7 +207,6 @@ void QWaylandDisplay::flushRequests() wl_display_flush(mDisplay); } - void QWaylandDisplay::blockingReadEvents() { if (wl_display_dispatch(mDisplay) < 0) { @@ -208,6 +220,41 @@ void QWaylandDisplay::exitWithError() ::exit(1); } +wl_event_queue *QWaylandDisplay::createEventQueue() +{ + return wl_display_create_queue(mDisplay); +} + +void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function<bool ()> condition, int timeout) +{ + if (!condition()) + return; + + QElapsedTimer timer; + timer.start(); + struct pollfd pFd = qt_make_pollfd(wl_display_get_fd(mDisplay), POLLIN); + while (timeout == -1 || timer.elapsed() < timeout) { + while (wl_display_prepare_read_queue(mDisplay, queue) != 0) + wl_display_dispatch_queue_pending(mDisplay, queue); + + wl_display_flush(mDisplay); + + const int remaining = qMax(timeout - timer.elapsed(), 0ll); + const int pollTimeout = timeout == -1 ? -1 : remaining; + if (qt_poll_msecs(&pFd, 1, pollTimeout) > 0) + wl_display_read_events(mDisplay); + else + wl_display_cancel_read(mDisplay); + + if (wl_display_dispatch_queue_pending(mDisplay, queue) < 0) { + checkError(); + exitWithError(); + } + if (!condition()) + break; + } +} + QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const { for (int i = 0; i < mScreens.size(); ++i) { @@ -218,6 +265,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(); @@ -242,11 +297,7 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin 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(); - QWindowSystemInterface::handleScreenAdded(screen); + mWaitingScreens << new QWaylandScreen(this, version, id); } else if (interface == QStringLiteral("wl_compositor")) { mCompositorVersion = qMin((int)version, 3); mCompositor.init(registry, id, mCompositorVersion); @@ -267,11 +318,11 @@ 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")) { bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION"); if (!disableHardwareIntegration) { @@ -282,7 +333,7 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin } } else if (interface == QLatin1String("zxdg_output_manager_v1")) { mXdgOutputManager.reset(new QtWayland::zxdg_output_manager_v1(registry, id, qMin(2, int(version)))); - for (auto *screen : qAsConst(mScreens)) + for (auto *screen : qAsConst(mWaitingScreens)) screen->initXdgOutput(xdgOutputManager()); forceRoundTrip(); } @@ -299,6 +350,14 @@ 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); @@ -307,6 +366,12 @@ void QWaylandDisplay::registry_global_remove(uint32_t id) } } } + 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; } diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h index c518cb94..2c7ed323 100644 --- a/src/client/qwaylanddisplay_p.h +++ b/src/client/qwaylanddisplay_p.h @@ -63,6 +63,12 @@ #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 @@ -92,8 +98,8 @@ class QWaylandQtKeyExtension; class QWaylandWindow; class QWaylandIntegration; class QWaylandHardwareIntegration; -class QWaylandShellSurface; class QWaylandSurface; +class QWaylandShellIntegration; class QWaylandCursor; class QWaylandCursorTheme; @@ -110,18 +116,23 @@ 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); - QWaylandShellSurface *createShellSurface(QWaylandWindow *window); struct ::wl_region *createRegion(const QRegion &qregion); struct ::wl_subsurface *createSubSurface(QWaylandWindow *window, QWaylandWindow *parent); + QWaylandShellIntegration *shellIntegration() const; QWaylandClientBufferIntegration *clientBufferIntegration() const; - QWaylandWindowManagerIntegration *windowManagerIntegration() const; + #if QT_CONFIG(cursor) QWaylandCursor *waylandCursor(); QWaylandCursorTheme *loadCursorTheme(const QString &name, int pixelSize); @@ -145,6 +156,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; @@ -181,6 +193,9 @@ public: void handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice); void handleWindowDestroyed(QWaylandWindow *window); + wl_event_queue *createEventQueue(); + void dispatchQueueWhile(wl_event_queue *queue, std::function<bool()> condition, int timeout = -1); + public slots: void blockingReadEvents(); void flushRequests(); @@ -206,6 +221,7 @@ 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; @@ -238,8 +254,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/qwaylandinputcontext.cpp b/src/client/qwaylandinputcontext.cpp index 55e7641e..1d34f06c 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/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp index 89b50f0a..f0fd9956 100644 --- a/src/client/qwaylandinputdevice.cpp +++ b/src/client/qwaylandinputdevice.cpp @@ -52,7 +52,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> @@ -71,10 +70,6 @@ #include <QtGui/QGuiApplication> -#if QT_CONFIG(xkbcommon) -#include <xkbcommon/xkbcommon-compose.h> -#endif - QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -84,85 +79,51 @@ Q_LOGGING_CATEGORY(lcQpaWaylandInput, "qt.qpa.wayland.input"); 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(""); - - 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); - } - } + struct xkb_rule_names names; + names.rules = "evdev"; + names.model = "pc105"; + names.layout = "us"; + names.variant = ""; + names.options = ""; - if (!mXkbContext || !mXkbMap || !mXkbState) { - qWarning() << "xkb_map_new_from_names failed, no key input"; - return false; - } - createComposeState(); - return true; -} + mXkbKeymap.reset(xkb_keymap_new_from_names(ctx, &names, XKB_KEYMAP_COMPILE_NO_FLAGS)); + if (mXkbKeymap) + mXkbState.reset(xkb_state_new(mXkbKeymap.get())); -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"; + if (!mXkbKeymap || !mXkbState) { + qCWarning(lcQpaWayland, "failed to create default keymap"); + return false; } - 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) @@ -402,9 +363,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() @@ -487,12 +448,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) @@ -521,6 +482,17 @@ 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) @@ -537,7 +509,7 @@ Qt::KeyboardModifiers QWaylandInputDevice::Keyboard::modifiers() const if (!mXkbState) return ret; - ret = QWaylandXkb::modifiers(mXkbState); + ret = QXkbCommon::modifiers(mXkbState.get()); #endif return ret; @@ -1045,8 +1017,10 @@ bool QWaylandInputDevice::Pointer::isDefinitelyTerminated(QtWayland::wl_pointer: 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; } @@ -1057,21 +1031,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 @@ -1118,28 +1090,34 @@ void QWaylandInputDevice::Keyboard::keyboard_leave(uint32_t time, struct wl_surf 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) { + 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... @@ -1147,102 +1125,52 @@ void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, 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(mRepeatDelay); - mRepeatTimer.start(); - } else if (mRepeatCode == code) { - mRepeatTimer.stop(); - } -} - -void QWaylandInputDevice::Keyboard::repeatKey() -{ - 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. + } else if (mKeymapFormat == WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP) { + // raw scan code return; } - - mRepeatTimer.setInterval(mRepeatRate); - sendKey(window->window(), mRepeatTime, QEvent::KeyRelease, mRepeatKey, modifiers(), mRepeatCode, -#if QT_CONFIG(xkbcommon) - mRepeatSym, mNativeModifiers, -#else - 0, 0, -#endif - mRepeatText, true); - - sendKey(window->window(), mRepeatTime, QEvent::KeyPress, mRepeatKey, modifiers(), mRepeatCode, -#if QT_CONFIG(xkbcommon) - mRepeatSym, mNativeModifiers, -#else - 0, 0, -#endif - mRepeatText, true); } void QWaylandInputDevice::Keyboard::handleFocusDestroyed() @@ -1275,12 +1203,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); diff --git a/src/client/qwaylandinputdevice_p.h b/src/client/qwaylandinputdevice_p.h index 404bcf57..f44f1ab1 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> @@ -64,8 +65,7 @@ #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> @@ -76,11 +76,6 @@ struct wl_cursor_image; #endif -#if QT_CONFIG(xkbcommon) -struct xkb_compose_state; -struct xkb_compose_table; -#endif - QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -130,6 +125,8 @@ public: QWaylandWindow *keyboardFocus() const; QWaylandWindow *touchFocus() const; + QList<int> possibleKeys(const QKeyEvent *event) const; + QPointF pointerSurfacePosition() const; Qt::KeyboardModifiers modifiers() const; @@ -166,7 +163,7 @@ private: Pointer *mPointer = nullptr; Touch *mTouch = nullptr; - QWaylandTextInput *mTextInput = nullptr; + QScopedPointer<QWaylandTextInput> mTextInput; uint32_t mTime = 0; uint32_t mSerial = 0; @@ -215,41 +212,44 @@ public: QWaylandInputDevice *mParent = nullptr; ::wl_surface *mFocus = nullptr; -#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 + uint32_t mNativeModifiers = 0; - int mRepeatKey; - uint32_t mRepeatCode; - uint32_t mRepeatTime; + 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; - QString mRepeatText; -#if QT_CONFIG(xkbcommon) - xkb_keysym_t mRepeatSym; -#endif - QTimer mRepeatTimer; + + 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 QObject, public QtWayland::wl_pointer diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp index 45957629..ea2b50b4 100644 --- a/src/client/qwaylandintegration.cpp +++ b/src/client/qwaylandintegration.cpp @@ -90,6 +90,10 @@ #include <QtLinuxAccessibilitySupport/private/bridge_p.h> #endif +#if QT_CONFIG(xkbcommon) +#include <QtXkbCommonSupport/private/qxkbcommon_p.h> +#endif + QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -146,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() @@ -301,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(); @@ -462,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 944f635b..3aef2c4d 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. diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp index ee6f949a..d116a807 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); @@ -233,10 +260,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) { @@ -263,7 +295,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()); } @@ -281,7 +315,11 @@ 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) @@ -289,6 +327,12 @@ 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 4ef58c0c..e9e07d9c 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; @@ -116,12 +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/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp index d0cb6317..9bc400f8 100644 --- a/src/client/qwaylandwindow.cpp +++ b/src/client/qwaylandwindow.cpp @@ -51,6 +51,7 @@ #include "qwaylandnativeinterface_p.h" #include "qwaylanddecorationfactory_p.h" #include "qwaylandshmbackingstore_p.h" +#include "qwaylandshellintegration_p.h" #include <QtCore/QFileInfo> #include <QtCore/QPointer> @@ -62,6 +63,7 @@ #include <QtGui/private/qwindow_p.h> #include <QtCore/QDebug> +#include <QtCore/QThread> QT_BEGIN_NAMESPACE @@ -74,6 +76,7 @@ QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr; QWaylandWindow::QWaylandWindow(QWindow *window) : QPlatformWindow(window) , mDisplay(waylandScreen()->display()) + , mFrameQueue(mDisplay->createEventQueue()) , mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP")) { static WId id = 1; @@ -127,8 +130,9 @@ void QWaylandWindow::initWindow() } } else if (shouldCreateShellSurface()) { Q_ASSERT(!mShellSurface); + Q_ASSERT(mDisplay->shellIntegration()); - mShellSurface = mDisplay->createShellSurface(this); + mShellSurface = mDisplay->shellIntegration()->createShellSurface(this); if (mShellSurface) { // Set initial surface title setWindowTitle(window()->title()); @@ -204,6 +208,9 @@ void QWaylandWindow::initializeWlSurface() bool QWaylandWindow::shouldCreateShellSurface() const { + if (!mDisplay->shellIntegration()) + return false; + if (shouldCreateSubSurface()) return false; @@ -339,7 +346,7 @@ void QWaylandWindow::setGeometry(const QRect &rect) sendExposeEvent(exposeGeometry); if (mShellSurface) - mShellSurface->setWindowGeometry(windowGeometry()); + mShellSurface->setWindowGeometry(windowContentGeometry()); } void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset) @@ -357,6 +364,8 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect) { if (!(mShellSurface && mShellSurface->handleExpose(rect))) QWindowSystemInterface::handleExposeEvent(window(), rect); + else + qCDebug(lcQpaWayland) << "sendExposeEvent: intercepted by shell extension, not sending"; mLastExposeGeometry = rect; } @@ -494,15 +503,8 @@ void QWaylandWindow::applyConfigure() void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y) { Q_ASSERT(!buffer->committed()); - if (mFrameCallback) { - wl_callback_destroy(mFrameCallback); - mFrameCallback = nullptr; - } - if (buffer) { - mFrameCallback = mSurface->frame(); - wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this); - mWaitingForFrameSync = true; + handleUpdate(); buffer->setBusy(); mSurface->attach(buffer->buffer(), x, y); @@ -566,32 +568,61 @@ void QWaylandWindow::commit() } const wl_callback_listener QWaylandWindow::callbackListener = { - QWaylandWindow::frameCallback + [](void *data, wl_callback *callback, uint32_t time) { + Q_UNUSED(callback); + Q_UNUSED(time); + auto *window = static_cast<QWaylandWindow*>(data); + if (window->thread() != QThread::currentThread()) + QMetaObject::invokeMethod(window, [=] { window->handleFrameCallback(); }, Qt::QueuedConnection); + else + window->handleFrameCallback(); + } }; -void QWaylandWindow::frameCallback(void *data, struct wl_callback *callback, uint32_t time) +void QWaylandWindow::handleFrameCallback() { - Q_UNUSED(time); - Q_UNUSED(callback); - QWaylandWindow *self = static_cast<QWaylandWindow*>(data); + bool wasExposed = isExposed(); - self->mWaitingForFrameSync = false; - if (self->mUpdateRequested) { - self->mUpdateRequested = false; - self->deliverUpdateRequest(); + if (mFrameCallbackTimerId != -1) { + killTimer(mFrameCallbackTimerId); + mFrameCallbackTimerId = -1; } + + mWaitingForFrameCallback = false; + mFrameCallbackTimedOut = false; + + if (!wasExposed && isExposed()) + sendExposeEvent(QRect(QPoint(), geometry().size())); + if (wasExposed && hasPendingUpdateRequest()) + deliverUpdateRequest(); } QMutex QWaylandWindow::mFrameSyncMutex; -void QWaylandWindow::waitForFrameSync() +bool QWaylandWindow::waitForFrameSync(int timeout) { QMutexLocker locker(&mFrameSyncMutex); - if (!mWaitingForFrameSync) - return; - mDisplay->flushRequests(); - while (mWaitingForFrameSync) - mDisplay->blockingReadEvents(); + if (!mWaitingForFrameCallback) + return true; + + wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(mFrameCallback), mFrameQueue); + mDisplay->dispatchQueueWhile(mFrameQueue, [&]() { return mWaitingForFrameCallback; }, timeout); + + if (mWaitingForFrameCallback) { + qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; + mFrameCallbackTimedOut = true; + mWaitingForUpdate = false; + sendExposeEvent(QRect()); + } + + // Stop current frame timer if any, can't use killTimer directly, because we might be on a diffent thread + if (mFrameCallbackTimerId != -1) { + int id = mFrameCallbackTimerId; + mFrameCallbackTimerId = -1; + QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection); + } + + return !mWaitingForFrameCallback; } QMargins QWaylandWindow::frameMargins() const @@ -613,7 +644,7 @@ QSize QWaylandWindow::surfaceSize() const * 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::windowGeometry() const +QRect QWaylandWindow::windowContentGeometry() const { return QRect(QPoint(), surfaceSize()); } @@ -833,7 +864,7 @@ void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylan #if QT_CONFIG(cursor) if (e.type == QWaylandPointerEvent::Enter) { - QRect contentGeometry = windowGeometry().marginsRemoved(frameMargins()); + QRect contentGeometry = windowContentGeometry().marginsRemoved(frameMargins()); if (contentGeometry.contains(e.local.toPoint())) restoreMouseCursor(inputDevice); } @@ -944,9 +975,19 @@ void QWaylandWindow::requestActivateWindow() bool QWaylandWindow::isExposed() const { + if (!window()->isVisible()) + return false; + + if (mFrameCallbackTimedOut) + return false; + if (mShellSurface) - return window()->isVisible() && mShellSurface->isExposed(); - return QPlatformWindow::isExposed(); + return mShellSurface->isExposed(); + + if (mSubSurfaceWindow) + return mSubSurfaceWindow->parent()->isExposed(); + + return !(shouldCreateShellSurface() || shouldCreateSubSurface()); } bool QWaylandWindow::isActive() const @@ -1015,12 +1056,107 @@ QVariant QWaylandWindow::property(const QString &name, const QVariant &defaultVa return m_properties.value(name, defaultValue); } +void QWaylandWindow::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == mFallbackUpdateTimerId) { + killTimer(mFallbackUpdateTimerId); + mFallbackUpdateTimerId = -1; + qCDebug(lcWaylandBackingstore) << "mFallbackUpdateTimer timed out"; + + if (!isExposed()) { + qCDebug(lcWaylandBackingstore) << "Fallback update timer: Window not exposed," + << "not delivering update request."; + return; + } + + if (mWaitingForUpdate && hasPendingUpdateRequest() && !mWaitingForFrameCallback) { + qCWarning(lcWaylandBackingstore) << "Delivering update request through fallback timer," + << "may not be in sync with display"; + deliverUpdateRequest(); + } + } + + if (event->timerId() == mFrameCallbackTimerId) { + killTimer(mFrameCallbackTimerId); + mFrameCallbackTimerId = -1; + qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; + mFrameCallbackTimedOut = true; + mWaitingForUpdate = false; + sendExposeEvent(QRect()); + } +} + void QWaylandWindow::requestUpdate() { - if (!mWaitingForFrameSync) - QPlatformWindow::requestUpdate(); - else - mUpdateRequested = true; + Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA + + // If we have a frame callback all is good and will be taken care of there + if (mWaitingForFrameCallback) + return; + + // If we've already called deliverUpdateRequest(), but haven't seen any attach+commit/swap yet + if (mWaitingForUpdate) { + // Ideally, we should just have returned here, but we're not guaranteed that the client + // will actually update, so start this timer to deliver another request update after a while + // *IF* the client doesn't update. + int fallbackTimeout = 100; + mFallbackUpdateTimerId = startTimer(fallbackTimeout); + return; + } + + // Some applications (such as Qt Quick) depend on updates being delivered asynchronously, + // so use invokeMethod to delay the delivery a bit. + QMetaObject::invokeMethod(this, [this] { + // Things might have changed in the meantime + if (hasPendingUpdateRequest() && !mWaitingForUpdate && !mWaitingForFrameCallback) + deliverUpdateRequest(); + }, Qt::QueuedConnection); +} + +// Should be called whenever we commit a buffer (directly through wl_surface.commit or indirectly +// with eglSwapBuffers) to know when it's time to commit the next one. +// Can be called from the render thread (without locking anything) so make sure to not make races in this method. +void QWaylandWindow::handleUpdate() +{ + // TODO: Should sync subsurfaces avoid requesting frame callbacks? + + if (mFrameCallback) { + wl_callback_destroy(mFrameCallback); + mFrameCallback = nullptr; + } + + if (mFallbackUpdateTimerId != -1) { + // Ideally, we would stop the fallback timer here, but since we're on another thread, + // it's not allowed. Instead we set mFallbackUpdateTimer to -1 here, so we'll just + // ignore it if it times out before it's cleaned up by the invokeMethod call. + int id = mFallbackUpdateTimerId; + mFallbackUpdateTimerId = -1; + QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection); + } + + mFrameCallback = mSurface->frame(); + wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this); + mWaitingForFrameCallback = true; + mWaitingForUpdate = false; + + // Stop current frame timer if any, can't use killTimer directly, see comment above. + if (mFrameCallbackTimerId != -1) { + int id = mFrameCallbackTimerId; + mFrameCallbackTimerId = -1; + QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection); + } + + // Start a timer for handling the case when the compositor stops sending frame callbacks. + QMetaObject::invokeMethod(this, [=] { // Again; can't do it directly + if (mWaitingForFrameCallback) + mFrameCallbackTimerId = startTimer(100); + }, Qt::QueuedConnection); +} + +void QWaylandWindow::deliverUpdateRequest() +{ + mWaitingForUpdate = true; + QPlatformWindow::deliverUpdateRequest(); } void QWaylandWindow::addAttachOffset(const QPoint point) diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h index 3e9b4385..5eadc02c 100644 --- a/src/client/qwaylandwindow_p.h +++ b/src/client/qwaylandwindow_p.h @@ -121,11 +121,11 @@ public: void commit(); - void waitForFrameSync(); + bool waitForFrameSync(int timeout); QMargins frameMargins() const override; QSize surfaceSize() const; - QRect windowGeometry() const; + QRect windowContentGeometry() const; QWaylandSurface *waylandSurface() const { return mSurface.data(); } ::wl_surface *wlSurface(); @@ -194,7 +194,10 @@ public: bool startSystemMove(const QPoint &pos) override; + void timerEvent(QTimerEvent *event) override; void requestUpdate() override; + void handleUpdate(); + void deliverUpdateRequest() override; public slots: void applyConfigure(); @@ -214,10 +217,17 @@ protected: Qt::MouseButtons mMousePressedInContentArea = Qt::NoButton; WId mWindowId; - bool mWaitingForFrameSync = false; + bool mWaitingForFrameCallback = false; + bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out + int mFrameCallbackTimerId = -1; // Started on commit, reset on frame callback struct ::wl_callback *mFrameCallback = nullptr; + struct ::wl_event_queue *mFrameQueue = nullptr; QWaitCondition mFrameSyncWait; + // True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer + bool mWaitingForUpdate = false; + int mFallbackUpdateTimerId = -1; // Started when waiting for app to commit + QMutex mResizeLock; bool mWaitingToApplyConfigure = false; bool mCanResize = true; @@ -254,11 +264,10 @@ private: void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e); void handleScreensChanged(); - bool mUpdateRequested = false; QRect mLastExposeGeometry; static const wl_callback_listener callbackListener; - static void frameCallback(void *data, struct wl_callback *wl_callback, uint32_t time); + void handleFrameCallback(); static QMutex mFrameSyncMutex; static QWaylandWindow *mMouseGrab; diff --git a/src/compositor/compositor.pro b/src/compositor/compositor.pro index 47be591d..b887cf28 100644 --- a/src/compositor/compositor.pro +++ b/src/compositor/compositor.pro @@ -3,6 +3,10 @@ MODULE = waylandcompositor QT = core gui-private +qtConfig(xkbcommon) { + QT_FOR_PRIVATE += xkbcommon_support-private +} + qtHaveModule(quick): QT += quick CONFIG -= precompile_header diff --git a/src/compositor/compositor_api/compositor_api.pri b/src/compositor/compositor_api/compositor_api.pri index 233815d8..49d0de41 100644 --- a/src/compositor/compositor_api/compositor_api.pri +++ b/src/compositor/compositor_api/compositor_api.pri @@ -17,6 +17,7 @@ HEADERS += \ compositor_api/qwaylandtouch.h \ compositor_api/qwaylandtouch_p.h \ compositor_api/qwaylandoutput.h \ + compositor_api/qwaylandoutput_p.h \ compositor_api/qwaylandoutputmode.h \ compositor_api/qwaylandoutputmode_p.h \ compositor_api/qwaylandbufferref.h \ diff --git a/src/compositor/compositor_api/qwaylandcompositor.cpp b/src/compositor/compositor_api/qwaylandcompositor.cpp index 34384d57..530cf8ed 100644 --- a/src/compositor/compositor_api/qwaylandcompositor.cpp +++ b/src/compositor/compositor_api/qwaylandcompositor.cpp @@ -72,7 +72,6 @@ #include "extensions/qwaylandqtwindowmanager.h" -#include "qwaylandxkb_p.h" #include "qwaylandsharedmemoryformathelper_p.h" #include <QtCore/QCoreApplication> @@ -129,12 +128,12 @@ public: bool isDown = ke->keyType == QEvent::KeyPress; #if QT_CONFIG(xkbcommon) - QString text; - Qt::KeyboardModifiers modifiers = QWaylandXkb::modifiers(keyb->xkbState()); + xkb_state *xkbState = keyb->xkbState(); + Qt::KeyboardModifiers modifiers = QXkbCommon::modifiers(xkbState); - const xkb_keysym_t sym = xkb_state_key_get_one_sym(keyb->xkbState(), code); - int qtkey; - std::tie(qtkey, text) = QWaylandXkb::keysymToQtKey(sym, modifiers); + const xkb_keysym_t sym = xkb_state_key_get_one_sym(xkbState, code); + int qtkey = QXkbCommon::keysymToQtKey(sym, modifiers, xkbState, code); + QString text = QXkbCommon::lookupString(xkbState, code); ke->key = qtkey; ke->modifiers = modifiers; @@ -172,6 +171,14 @@ QWaylandCompositorPrivate::QWaylandCompositorPrivate(QWaylandCompositor *composi timer.start(); QWindowSystemInterfacePrivate::installWindowSystemEventHandler(eventHandler.data()); + +#if QT_CONFIG(xkbcommon) + mXkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS)); + if (!mXkbContext) { + qWarning("Failed to create a XKB context: keymap will not be supported"); + return; + } +#endif } void QWaylandCompositorPrivate::init() diff --git a/src/compositor/compositor_api/qwaylandcompositor_p.h b/src/compositor/compositor_api/qwaylandcompositor_p.h index d91977de..2c962421 100644 --- a/src/compositor/compositor_api/qwaylandcompositor_p.h +++ b/src/compositor/compositor_api/qwaylandcompositor_p.h @@ -60,6 +60,10 @@ #include <QtWaylandCompositor/private/qwayland-server-wayland.h> +#if QT_CONFIG(xkbcommon) +#include <QtXkbCommonSupport/private/qxkbcommon_p.h> +#endif + QT_BEGIN_NAMESPACE namespace QtWayland { @@ -81,6 +85,10 @@ public: QWaylandCompositorPrivate(QWaylandCompositor *compositor); ~QWaylandCompositorPrivate() override; +#if QT_CONFIG(xkbcommon) + struct xkb_context *xkbContext() const { return mXkbContext.get(); } +#endif + void preInit(); void init(); @@ -169,6 +177,10 @@ protected: bool initialized = false; QList<QPointer<QObject> > polish_objects; +#if QT_CONFIG(xkbcommon) + QXkbCommon::ScopedXKBContext mXkbContext; +#endif + Q_DECLARE_PUBLIC(QWaylandCompositor) Q_DISABLE_COPY(QWaylandCompositorPrivate) }; diff --git a/src/compositor/compositor_api/qwaylandkeyboard.cpp b/src/compositor/compositor_api/qwaylandkeyboard.cpp index 5f3bd3d4..2302c0b6 100644 --- a/src/compositor/compositor_api/qwaylandkeyboard.cpp +++ b/src/compositor/compositor_api/qwaylandkeyboard.cpp @@ -54,7 +54,6 @@ #if QT_CONFIG(xkbcommon) #include <sys/mman.h> #include <sys/types.h> -#include <qwaylandxkb_p.h> #endif QT_BEGIN_NAMESPACE @@ -67,12 +66,11 @@ QWaylandKeyboardPrivate::QWaylandKeyboardPrivate(QWaylandSeat *seat) QWaylandKeyboardPrivate::~QWaylandKeyboardPrivate() { #if QT_CONFIG(xkbcommon) - if (xkb_context) { + if (xkbContext()) { if (keymap_area) munmap(keymap_area, keymap_size); - close(keymap_fd); - xkb_context_unref(xkb_context); - xkb_state_unref(xkb_state); + if (keymap_fd >= 0) + close(keymap_fd); } #endif } @@ -137,14 +135,14 @@ void QWaylandKeyboardPrivate::keyboard_bind_resource(wl_keyboard::Resource *reso send_repeat_info(resource->handle, repeatRate, repeatDelay); #if QT_CONFIG(xkbcommon) - if (xkb_context) { + if (xkbContext()) { send_keymap(resource->handle, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymap_fd, keymap_size); } else #endif { int null_fd = open("/dev/null", O_RDONLY); - send_keymap(resource->handle, 0 /* WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP */, + send_keymap(resource->handle, WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP, null_fd, 0); close(null_fd); } @@ -164,11 +162,8 @@ void QWaylandKeyboardPrivate::keyboard_release(wl_keyboard::Resource *resource) void QWaylandKeyboardPrivate::keyEvent(uint code, uint32_t state) { -#if QT_CONFIG(xkbcommon) - uint key = toWaylandXkbV1Key(code); -#else - uint key = code; -#endif + uint key = toWaylandKey(code); + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { keys << key; } else { @@ -180,30 +175,18 @@ void QWaylandKeyboardPrivate::sendKeyEvent(uint code, uint32_t state) { uint32_t time = compositor()->currentTimeMsecs(); uint32_t serial = compositor()->nextSerial(); -#if QT_CONFIG(xkbcommon) - uint key = toWaylandXkbV1Key(code); -#else - uint key = code; -#endif + uint key = toWaylandKey(code); if (focusResource) send_key(focusResource->handle, serial, time, key, state); } -void QWaylandKeyboardPrivate::modifiers(uint32_t serial, uint32_t mods_depressed, - uint32_t mods_latched, uint32_t mods_locked, uint32_t group) -{ - if (focusResource) { - send_modifiers(focusResource->handle, serial, mods_depressed, mods_latched, mods_locked, group); - } -} - #if QT_CONFIG(xkbcommon) void QWaylandKeyboardPrivate::maybeUpdateXkbScanCodeTable() { if (!scanCodesByQtKey.isEmpty() || !xkbState()) return; - if (xkb_keymap *keymap = xkb_state_get_keymap(xkb_state)) { + if (xkb_keymap *keymap = xkb_state_get_keymap(xkbState())) { xkb_keymap_key_for_each(keymap, [](xkb_keymap *keymap, xkb_keycode_t keycode, void *d){ auto *scanCodesByQtKey = static_cast<QMap<ScanCodeKey, uint>*>(d); uint numLayouts = xkb_keymap_num_layouts_for_key(keymap, keycode); @@ -214,7 +197,7 @@ void QWaylandKeyboardPrivate::maybeUpdateXkbScanCodeTable() continue; Qt::KeyboardModifiers mods = {}; - int qtKey = QWaylandXkb::keysymToQtKey(syms[0], mods).first; + int qtKey = QXkbCommon::keysymToQtKey(syms[0], mods); if (qtKey != 0) scanCodesByQtKey->insert({layout, qtKey}, keycode); } @@ -226,15 +209,15 @@ void QWaylandKeyboardPrivate::maybeUpdateXkbScanCodeTable() void QWaylandKeyboardPrivate::updateModifierState(uint code, uint32_t state) { #if QT_CONFIG(xkbcommon) - if (!xkb_context) + if (!xkbContext()) return; - xkb_state_update_key(xkb_state, code, state == WL_KEYBOARD_KEY_STATE_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP); + xkb_state_update_key(xkbState(), code, state == WL_KEYBOARD_KEY_STATE_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP); - uint32_t modsDepressed = xkb_state_serialize_mods(xkb_state, (xkb_state_component)XKB_STATE_DEPRESSED); - uint32_t modsLatched = xkb_state_serialize_mods(xkb_state, (xkb_state_component)XKB_STATE_LATCHED); - uint32_t modsLocked = xkb_state_serialize_mods(xkb_state, (xkb_state_component)XKB_STATE_LOCKED); - uint32_t group = xkb_state_serialize_group(xkb_state, (xkb_state_component)XKB_STATE_EFFECTIVE); + uint32_t modsDepressed = xkb_state_serialize_mods(xkbState(), XKB_STATE_MODS_DEPRESSED); + uint32_t modsLatched = xkb_state_serialize_mods(xkbState(), XKB_STATE_MODS_LATCHED); + uint32_t modsLocked = xkb_state_serialize_mods(xkbState(), XKB_STATE_MODS_LOCKED); + uint32_t group = xkb_state_serialize_layout(xkbState(), XKB_STATE_LAYOUT_EFFECTIVE); if (this->modsDepressed == modsDepressed && this->modsLatched == modsLatched @@ -247,7 +230,10 @@ void QWaylandKeyboardPrivate::updateModifierState(uint code, uint32_t state) this->modsLocked = modsLocked; this->group = group; - modifiers(compositor()->nextSerial(), modsDepressed, modsLatched, modsLocked, group); + if (focusResource) { + send_modifiers(focusResource->handle, compositor()->nextSerial(), modsDepressed, + modsLatched, modsLocked, group); + } #else Q_UNUSED(code); Q_UNUSED(state); @@ -266,7 +252,7 @@ void QWaylandKeyboardPrivate::maybeUpdateKeymap() pendingKeymap = false; #if QT_CONFIG(xkbcommon) - if (!xkb_context) + if (!xkbContext()) return; createXKBKeymap(); @@ -274,7 +260,7 @@ void QWaylandKeyboardPrivate::maybeUpdateKeymap() send_keymap(res->handle, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymap_fd, keymap_size); } - xkb_state_update_mask(xkb_state, 0, modsLatched, modsLocked, 0, 0, 0); + xkb_state_update_mask(xkbState(), 0, modsLatched, modsLocked, 0, 0, 0); if (focusResource) send_modifiers(focusResource->handle, compositor()->nextSerial(), @@ -285,6 +271,24 @@ void QWaylandKeyboardPrivate::maybeUpdateKeymap() #endif } +uint QWaylandKeyboardPrivate::toWaylandKey(const uint nativeScanCode) +{ +#if QT_CONFIG(xkbcommon) + // In all current XKB keymaps there's a constant offset of 8 (for historical + // reasons) from hardware/evdev scancodes to XKB keycodes. On X11, we pass + // XKB keycodes (as sent by X server) via QKeyEvent::nativeScanCode. eglfs+evdev + // adds 8 for consistency, see qtbase/05c07c7636012ebb4131ca099ca4ea093af76410. + // eglfs+libinput also adds 8, for the same reason. Wayland protocol uses + // hardware/evdev scancodes, thus we need to minus 8 before sending the event + // out. + const uint offset = 8; + Q_ASSERT(nativeScanCode >= offset); + return nativeScanCode - offset; +#else + return nativeScanCode; +#endif +} + #if QT_CONFIG(xkbcommon) static int createAnonymousFile(size_t size) { @@ -316,18 +320,6 @@ static int createAnonymousFile(size_t size) return fd; } -void QWaylandKeyboardPrivate::initXKB() -{ - xkb_context = xkb_context_new(static_cast<xkb_context_flags>(0)); - if (!xkb_context) { - qWarning("Failed to create a XKB context: keymap will not be supported"); - return; - } - - createXKBKeymap(); -} - - void QWaylandKeyboardPrivate::createXKBState(xkb_keymap *keymap) { char *keymap_str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1); @@ -356,46 +348,41 @@ void QWaylandKeyboardPrivate::createXKBState(xkb_keymap *keymap) strcpy(keymap_area, keymap_str); free(keymap_str); - if (xkb_state) - xkb_state_unref(xkb_state); - xkb_state = xkb_state_new(keymap); -} - -uint QWaylandKeyboardPrivate::toWaylandXkbV1Key(const uint nativeScanCode) -{ - const uint offset = 8; - Q_ASSERT(nativeScanCode >= offset); - return nativeScanCode - offset; + mXkbState.reset(xkb_state_new(keymap)); + if (!mXkbState) + qWarning("Failed to create XKB state"); } void QWaylandKeyboardPrivate::createXKBKeymap() { - if (!xkb_context) + if (!xkbContext()) return; - auto keymap = seat->keymap(); - struct xkb_rule_names rule_names = { strdup(qPrintable(keymap->rules())), - strdup(qPrintable(keymap->model())), - strdup(qPrintable(keymap->layout())), - strdup(qPrintable(keymap->variant())), - strdup(qPrintable(keymap->options())) }; - struct xkb_keymap *xkbKeymap = xkb_keymap_new_from_names(xkb_context, &rule_names, static_cast<xkb_keymap_compile_flags>(0)); - + QWaylandKeymap *keymap = seat->keymap(); + QByteArray rules = keymap->rules().toLocal8Bit(); + QByteArray model = keymap->model().toLocal8Bit(); + QByteArray layout = keymap->layout().toLocal8Bit(); + QByteArray variant = keymap->variant().toLocal8Bit(); + QByteArray options = keymap->options().toLocal8Bit(); + + struct xkb_rule_names rule_names = { + rules.constData(), + model.constData(), + layout.constData(), + variant.constData(), + options.constData() + }; + + QXkbCommon::ScopedXKBKeymap xkbKeymap(xkb_keymap_new_from_names(xkbContext(), &rule_names, + XKB_KEYMAP_COMPILE_NO_FLAGS)); if (xkbKeymap) { scanCodesByQtKey.clear(); - createXKBState(xkbKeymap); - xkb_keymap_unref(xkbKeymap); + createXKBState(xkbKeymap.get()); } else { qWarning("Failed to load the '%s' XKB keymap.", qPrintable(keymap->layout())); } - - free((char *)rule_names.rules); - free((char *)rule_names.model); - free((char *)rule_names.layout); - free((char *)rule_names.variant); - free((char *)rule_names.options); } -#endif +#endif // QT_CONFIG(xkbcommon) void QWaylandKeyboardPrivate::sendRepeatInfo() { @@ -430,7 +417,7 @@ QWaylandKeyboard::QWaylandKeyboard(QWaylandSeat *seat, QObject *parent) connect(keymap, &QWaylandKeymap::rulesChanged, this, &QWaylandKeyboard::updateKeymap); connect(keymap, &QWaylandKeymap::modelChanged, this, &QWaylandKeyboard::updateKeymap); #if QT_CONFIG(xkbcommon) - d->initXKB(); + d->createXKBKeymap(); #endif } diff --git a/src/compositor/compositor_api/qwaylandkeyboard_p.h b/src/compositor/compositor_api/qwaylandkeyboard_p.h index 87e89e85..4dfe0035 100644 --- a/src/compositor/compositor_api/qwaylandkeyboard_p.h +++ b/src/compositor/compositor_api/qwaylandkeyboard_p.h @@ -51,6 +51,7 @@ // // We mean it. // +#include <QtWaylandCompositor/private/qwaylandcompositor_p.h> #include <QtWaylandCompositor/private/qtwaylandcompositorglobal_p.h> #include <QtWaylandCompositor/qwaylandseat.h> @@ -64,6 +65,7 @@ #if QT_CONFIG(xkbcommon) #include <xkbcommon/xkbcommon.h> +#include <QtXkbCommonSupport/private/qxkbcommon_p.h> #endif @@ -83,11 +85,12 @@ public: QWaylandCompositor *compositor() const { return seat->compositor(); } void focused(QWaylandSurface* surface); - void modifiers(uint32_t serial, uint32_t mods_depressed, - uint32_t mods_latched, uint32_t mods_locked, uint32_t group); #if QT_CONFIG(xkbcommon) - struct xkb_state *xkbState() const { return xkb_state; } + struct xkb_state *xkbState() const { return mXkbState.get(); } + struct xkb_context *xkbContext() const { + return QWaylandCompositorPrivate::get(seat->compositor())->xkbContext(); + } uint32_t xkbModsMask() const { return modsDepressed | modsLatched | modsLocked; } void maybeUpdateXkbScanCodeTable(); #endif @@ -107,11 +110,10 @@ protected: private: #if QT_CONFIG(xkbcommon) - void initXKB(); void createXKBKeymap(); void createXKBState(xkb_keymap *keymap); #endif - static uint toWaylandXkbV1Key(const uint nativeScanCode); + static uint toWaylandKey(const uint nativeScanCode); void sendRepeatInfo(); @@ -134,8 +136,7 @@ private: char *keymap_area = nullptr; using ScanCodeKey = std::pair<uint,int>; // group/layout and QtKey QMap<ScanCodeKey, uint> scanCodesByQtKey; - struct xkb_context *xkb_context = nullptr; - struct xkb_state *xkb_state = nullptr; + QXkbCommon::ScopedXKBState mXkbState; #endif quint32 repeatRate = 40; diff --git a/src/compositor/compositor_api/qwaylandquickcompositor.cpp b/src/compositor/compositor_api/qwaylandquickcompositor.cpp index 98ad7acb..1b3f93e5 100644 --- a/src/compositor/compositor_api/qwaylandquickcompositor.cpp +++ b/src/compositor/compositor_api/qwaylandquickcompositor.cpp @@ -90,15 +90,15 @@ QWaylandQuickCompositor::QWaylandQuickCompositor(QObject *parent) * For instance, the following code would allow the clients to request \c wl_shell * surfaces in the compositor using the \c wl_shell interface. * - * \code - * import QtWayland.Compositor 1.0 + * \qml \QtMinorVersion + * import QtWayland.Compositor 1.\1 * * WaylandCompositor { * WlShell { * // ... * } * } - * \endcode + * \endqml */ void QWaylandQuickCompositor::create() diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp index 9e681dc0..996177be 100644 --- a/src/compositor/compositor_api/qwaylandquickitem.cpp +++ b/src/compositor/compositor_api/qwaylandquickitem.cpp @@ -840,7 +840,6 @@ void QWaylandQuickItem::setOutput(QWaylandOutput *output) * * The default is false. */ - /*! * \property QWaylandQuickItem::bufferLocked * diff --git a/src/compositor/compositor_api/qwaylandsurface.cpp b/src/compositor/compositor_api/qwaylandsurface.cpp index cd78166d..c79787e6 100644 --- a/src/compositor/compositor_api/qwaylandsurface.cpp +++ b/src/compositor/compositor_api/qwaylandsurface.cpp @@ -450,9 +450,7 @@ QWaylandClient *QWaylandSurface::client() const } /*! - * \property QWaylandSurface::waylandClient - * - * This property holds the \c wl_client using this QWaylandSurface. + * Holds the \c wl_client using this QWaylandSurface. */ ::wl_client *QWaylandSurface::waylandClient() const { diff --git a/src/compositor/doc/src/qtwaylandcompositor-qmltypes.qdoc b/src/compositor/doc/src/qtwaylandcompositor-qmltypes.qdoc index 1dadb710..a89e07d7 100644 --- a/src/compositor/doc/src/qtwaylandcompositor-qmltypes.qdoc +++ b/src/compositor/doc/src/qtwaylandcompositor-qmltypes.qdoc @@ -26,7 +26,7 @@ ****************************************************************************/ /*! - \qmlmodule QtWayland.Compositor 1.1 + \qmlmodule QtWayland.Compositor 1.\QtMinorVersion \title Qt Wayland Compositor QML Types \ingroup qmlmodules \brief Provides QML types for writing custom Wayland display servers. @@ -37,15 +37,15 @@ The QML types can be imported into your application using the following import statement: - \code - import QtWayland.Compositor 1.1 + \qml \QtMinorVersion + import QtWayland.Compositor 1.\1 \endcode To link against the module, add this line to your \l qmake \c .pro file: \code QT += waylandcompositor - \endcode + \endqml For more information about using these types in your application, see the \l{Qt Wayland Compositor} documentation. diff --git a/src/compositor/extensions/qwaylandiviapplication.cpp b/src/compositor/extensions/qwaylandiviapplication.cpp index 36690341..a2e9842d 100644 --- a/src/compositor/extensions/qwaylandiviapplication.cpp +++ b/src/compositor/extensions/qwaylandiviapplication.cpp @@ -62,8 +62,9 @@ QT_BEGIN_NAMESPACE * To provide the functionality of the shell extension in a compositor, create * an instance of the IviApplication component and add it to the list of extensions * supported by the compositor: - * \code - * import QtWayland.Compositor 1.0 + * + * \qml \QtMinorVersion + * import QtWayland.Compositor 1.\1 * * WaylandCompositor { * IviApplication { @@ -74,7 +75,7 @@ QT_BEGIN_NAMESPACE * } * } * } - * \endcode + * \endqml */ /*! diff --git a/src/compositor/extensions/qwaylandshell.cpp b/src/compositor/extensions/qwaylandshell.cpp index 12479186..bb3b4675 100644 --- a/src/compositor/extensions/qwaylandshell.cpp +++ b/src/compositor/extensions/qwaylandshell.cpp @@ -92,4 +92,24 @@ void QWaylandShell::setFocusPolicy(QWaylandShell::FocusPolicy focusPolicy) emit focusPolicyChanged(); } +QWaylandShell::QWaylandShell(QWaylandShellPrivate &dd) + : QWaylandCompositorExtension(dd) +{ +} + +QWaylandShell::QWaylandShell(QWaylandObject *container, QWaylandShellPrivate &dd) + : QWaylandCompositorExtension(container, dd) +{ +} + +QWaylandShell::QWaylandShell(QWaylandCompositorExtensionPrivate &dd) + : QWaylandShell(static_cast<QWaylandShellPrivate &>(dd)) +{ +} + +QWaylandShell::QWaylandShell(QWaylandObject *container, QWaylandCompositorExtensionPrivate &dd) + : QWaylandShell(container, static_cast<QWaylandShellPrivate &>(dd)) +{ +} + QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandshell.h b/src/compositor/extensions/qwaylandshell.h index 9241b866..6f494be5 100644 --- a/src/compositor/extensions/qwaylandshell.h +++ b/src/compositor/extensions/qwaylandshell.h @@ -68,8 +68,12 @@ Q_SIGNALS: void focusPolicyChanged(); protected: - QWaylandShell(QWaylandCompositorExtensionPrivate &dd) : QWaylandCompositorExtension(dd) {} - QWaylandShell(QWaylandObject *container, QWaylandCompositorExtensionPrivate &dd) : QWaylandCompositorExtension(container, dd) {} + explicit QWaylandShell(QWaylandShellPrivate &dd); + explicit QWaylandShell(QWaylandObject *container, QWaylandShellPrivate &dd); + + //Qt 6: remove + Q_DECL_DEPRECATED QWaylandShell(QWaylandCompositorExtensionPrivate &dd); + Q_DECL_DEPRECATED QWaylandShell(QWaylandObject *container, QWaylandCompositorExtensionPrivate &dd); }; template <typename T> diff --git a/src/compositor/extensions/qwaylandshellsurface.cpp b/src/compositor/extensions/qwaylandshellsurface.cpp index 3cfe4489..cb6d0364 100644 --- a/src/compositor/extensions/qwaylandshellsurface.cpp +++ b/src/compositor/extensions/qwaylandshellsurface.cpp @@ -87,7 +87,7 @@ */ /*! - * \property QWaylandWlShellSurface::windowType + * \property QWaylandShellSurface::windowType * * This property holds the window type of the QWaylandShellSurface. */ diff --git a/src/compositor/extensions/qwaylandtextinput.cpp b/src/compositor/extensions/qwaylandtextinput.cpp index 6cf33d18..f60a32a1 100644 --- a/src/compositor/extensions/qwaylandtextinput.cpp +++ b/src/compositor/extensions/qwaylandtextinput.cpp @@ -45,12 +45,15 @@ #include "qwaylandsurface.h" #include "qwaylandview.h" -#include "qwaylandxkb_p.h" #include "qwaylandinputmethodeventbuilder_p.h" #include <QGuiApplication> #include <QInputMethodEvent> +#if QT_CONFIG(xkbcommon) +#include <QtXkbCommonSupport/private/qxkbcommon_p.h> +#endif + QT_BEGIN_NAMESPACE QWaylandTextInputClientState::QWaylandTextInputClientState() @@ -203,11 +206,15 @@ void QWaylandTextInputPrivate::sendKeyEvent(QKeyEvent *event) // TODO add support for modifiers - foreach (xkb_keysym_t keysym, QWaylandXkb::toKeysym(event)) { +#if QT_CONFIG(xkbcommon) + for (xkb_keysym_t keysym : QXkbCommon::toKeysym(event)) { send_keysym(focusResource->handle, event->timestamp(), keysym, event->type() == QEvent::KeyPress ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED, 0); } +#else + Q_UNUSED(event); +#endif } void QWaylandTextInputPrivate::sendInputPanelState() diff --git a/src/compositor/extensions/qwaylandwlscaler.cpp b/src/compositor/extensions/qwaylandwlscaler.cpp index e097ea3c..5c8e4b27 100644 --- a/src/compositor/extensions/qwaylandwlscaler.cpp +++ b/src/compositor/extensions/qwaylandwlscaler.cpp @@ -61,14 +61,14 @@ QT_BEGIN_NAMESPACE To provide the functionality of the extension in a compositor, create an instance of the WlScaler component and add it to the list of extensions supported by the compositor: - \code - import QtWayland.Compositor 1.13 + \qml \QtMinorVersion + import QtWayland.Compositor 1.\1 WaylandCompositor { // ... WlScaler {} } - \endcode + \endqml \deprecated */ diff --git a/src/compositor/extensions/qwaylandwlshell.cpp b/src/compositor/extensions/qwaylandwlshell.cpp index 3f673463..9871a8a5 100644 --- a/src/compositor/extensions/qwaylandwlshell.cpp +++ b/src/compositor/extensions/qwaylandwlshell.cpp @@ -267,15 +267,16 @@ void QWaylandWlShellSurfacePrivate::shell_surface_set_class(Resource *resource, * To provide the functionality of the shell extension in a compositor, create * an instance of the WlShell component and add it to the list of extensions * supported by the compositor: - * \code - * import QtWayland.Compositor 1.0 + * + * \qml \QtMinorVersion + * import QtWayland.Compositor 1.\1 * * WaylandCompositor { * WlShell { * // ... * } * } - * \endcode + * \endqml */ /*! diff --git a/src/compositor/extensions/qwaylandxdgdecorationv1.cpp b/src/compositor/extensions/qwaylandxdgdecorationv1.cpp index 89c56acd..2d283ddf 100644 --- a/src/compositor/extensions/qwaylandxdgdecorationv1.cpp +++ b/src/compositor/extensions/qwaylandxdgdecorationv1.cpp @@ -59,8 +59,8 @@ QT_BEGIN_NAMESPACE To provide the functionality of the extension in a compositor, create an instance of the XdgDecorationManagerV1 component and add it to the list of extensions supported by the compositor: - \code - import QtWayland.Compositor 1.3 + \qml \QtMinorVersion + import QtWayland.Compositor 1.\1 WaylandCompositor { // Xdg decoration manager assumes xdg-shell is being used @@ -73,7 +73,7 @@ QT_BEGIN_NAMESPACE preferredMode: XdgToplevel.ServerSideDecoration } } - \endcode + \endqml \sa XdgToplevel::decorationMode */ diff --git a/src/compositor/extensions/qwaylandxdgshell.cpp b/src/compositor/extensions/qwaylandxdgshell.cpp index 560f95ac..eb7b958b 100644 --- a/src/compositor/extensions/qwaylandxdgshell.cpp +++ b/src/compositor/extensions/qwaylandxdgshell.cpp @@ -154,15 +154,16 @@ void QWaylandXdgShellPrivate::xdg_wm_base_pong(Resource *resource, uint32_t seri * To provide the functionality of the shell extension in a compositor, create * an instance of the XdgShell component and add it to the list of extensions * supported by the compositor: - * \code - * import QtWayland.Compositor 1.3 + * + * \qml \QtMinorVersion + * import QtWayland.Compositor 1.\1 * * WaylandCompositor { * XdgShell { * // ... * } * } - * \endcode + * \endqml */ /*! diff --git a/src/compositor/extensions/qwaylandxdgshellv5.cpp b/src/compositor/extensions/qwaylandxdgshellv5.cpp index 9e157a8a..0628f55e 100644 --- a/src/compositor/extensions/qwaylandxdgshellv5.cpp +++ b/src/compositor/extensions/qwaylandxdgshellv5.cpp @@ -513,15 +513,17 @@ void QWaylandXdgPopupV5Private::xdg_popup_destroy(Resource *resource) * * To provide the functionality of the shell extension in a compositor, create * an instance of the XdgShellV5 component and add it as a child of the - * compositor: \code - * import QtWayland.Compositor 1.0 + * compositor: + * + * \qml \QtMinorVersion + * import QtWayland.Compositor 1.\1 * * WaylandCompositor { * XdgShellV5 { * // ... * } * } - * \endcode + * \endqml * * \deprecated */ diff --git a/src/compositor/extensions/qwaylandxdgshellv6.cpp b/src/compositor/extensions/qwaylandxdgshellv6.cpp index d69ed6ca..f971fe5b 100644 --- a/src/compositor/extensions/qwaylandxdgshellv6.cpp +++ b/src/compositor/extensions/qwaylandxdgshellv6.cpp @@ -159,15 +159,16 @@ void QWaylandXdgShellV6Private::zxdg_shell_v6_pong(Resource *resource, uint32_t * To provide the functionality of the shell extension in a compositor, create * an instance of the XdgShellV6 component and add it to the list of extensions * supported by the compositor: - * \code - * import QtWayland.Compositor 1.1 + * + * \qml \QtMinorVersion + * import QtWayland.Compositor 1.\1 * * WaylandCompositor { * XdgShellV6 { * // ... * } * } - * \endcode + * \endqml */ /*! diff --git a/src/compositor/wayland_wrapper/wayland_wrapper.pri b/src/compositor/wayland_wrapper/wayland_wrapper.pri index b0c8371f..08dd38e8 100644 --- a/src/compositor/wayland_wrapper/wayland_wrapper.pri +++ b/src/compositor/wayland_wrapper/wayland_wrapper.pri @@ -5,14 +5,12 @@ WAYLANDSERVERSOURCES += \ HEADERS += \ wayland_wrapper/qwlbuffermanager_p.h \ wayland_wrapper/qwlclientbuffer_p.h \ - wayland_wrapper/qwlregion_p.h \ - ../shared/qwaylandxkb_p.h \ + wayland_wrapper/qwlregion_p.h SOURCES += \ wayland_wrapper/qwlbuffermanager.cpp \ wayland_wrapper/qwlclientbuffer.cpp \ - wayland_wrapper/qwlregion.cpp \ - ../shared/qwaylandxkb.cpp \ + wayland_wrapper/qwlregion.cpp qtConfig(wayland-datadevice) { HEADERS += \ diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp index 4044668b..5bd2760d 100644 --- a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp +++ b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp @@ -325,7 +325,9 @@ QWaylandGLContext::QWaylandGLContext(EGLDisplay eglDisplay, QWaylandDisplay *dis mSupportNonBlockingSwap = false; } if (!mSupportNonBlockingSwap) { - qWarning() << "Non-blocking swap buffers not supported. Subsurface rendering can be affected."; + qWarning(lcQpaWayland) << "Non-blocking swap buffers not supported." + << "Subsurface rendering can be affected." + << "It may also cause the event loop to freeze in some situations"; } updateGLFormat(); @@ -472,20 +474,15 @@ void QWaylandGLContext::swapBuffers(QPlatformSurface *surface) eglMakeCurrent(currentDisplay, currentSurfaceDraw, currentSurfaceRead, currentContext); } - - QWaylandSubSurface *sub = window->subSurfaceWindow(); - if (sub) { - QMutexLocker l(sub->syncMutex()); - - int si = (sub->isSync() && mSupportNonBlockingSwap) ? 0 : m_format.swapInterval(); - - eglSwapInterval(m_eglDisplay, si); - eglSwapBuffers(m_eglDisplay, eglSurface); - } else { - eglSwapInterval(m_eglDisplay, m_format.swapInterval()); - eglSwapBuffers(m_eglDisplay, eglSurface); + int swapInterval = mSupportNonBlockingSwap ? 0 : m_format.swapInterval(); + eglSwapInterval(m_eglDisplay, swapInterval); + if (swapInterval == 0 && m_format.swapInterval() > 0) { + // Emulating a blocking swap + glFlush(); // Flush before waiting so we can swap more quickly when the frame event arrives + window->waitForFrameSync(100); } - + window->handleUpdate(); + eglSwapBuffers(m_eglDisplay, eglSurface); window->setCanResize(true); } diff --git a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglcontext.cpp b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglcontext.cpp index c07ad534..a6fead95 100644 --- a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglcontext.cpp +++ b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglcontext.cpp @@ -65,7 +65,7 @@ void QWaylandXCompositeEGLContext::swapBuffers(QPlatformSurface *surface) QSize size = w->geometry().size(); w->commit(w->buffer(), QRegion(0, 0, size.width(), size.height())); - w->waitForFrameSync(); + w->waitForFrameSync(100); } EGLSurface QWaylandXCompositeEGLContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface) diff --git a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxcontext.cpp b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxcontext.cpp index 33ae2e03..35188741 100644 --- a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxcontext.cpp +++ b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxcontext.cpp @@ -93,7 +93,7 @@ void QWaylandXCompositeGLXContext::swapBuffers(QPlatformSurface *surface) glXSwapBuffers(m_display, w->xWindow()); w->commit(w->buffer(), QRegion(0, 0, size.width(), size.height())); - w->waitForFrameSync(); + w->waitForFrameSync(100); } QFunctionPointer QWaylandXCompositeGLXContext::getProcAddress(const char *procName) diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pri b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pri index 77f6d941..a7630040 100644 --- a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pri +++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pri @@ -1,6 +1,6 @@ INCLUDEPATH += $$PWD -QMAKE_USE_PRIVATE += egl wayland-server wayland-egl +QMAKE_USE_PRIVATE += egl wayland-server CONFIG += wayland-scanner WAYLANDSERVERSOURCES += $$PWD/../../../3rdparty/protocol/linux-dmabuf-unstable-v1.xml diff --git a/src/imports/compositor/qwaylandquickcompositorplugin.cpp b/src/imports/compositor/qwaylandquickcompositorplugin.cpp index c77be966..949d2cfa 100644 --- a/src/imports/compositor/qwaylandquickcompositorplugin.cpp +++ b/src/imports/compositor/qwaylandquickcompositorplugin.cpp @@ -130,6 +130,10 @@ public: static void defineModule(const char *uri) { + // This is needed so to guarantee that the import is available with the current + // Qt minor version even if no new types have been added since the last release. + qmlRegisterModule(uri, 1, QT_VERSION_MINOR); + qmlRegisterType<QWaylandQuickCompositorQuickExtensionContainer>(uri, 1, 0, "WaylandCompositor"); qmlRegisterType<QWaylandQuickItem>(uri, 1, 0, "WaylandQuickItem"); qmlRegisterType<QWaylandQuickItem, 13>(uri, 1, 13, "WaylandQuickItem"); diff --git a/src/plugins/decorations/bradient/main.cpp b/src/plugins/decorations/bradient/main.cpp index 96ad261b..e8e35775 100644 --- a/src/plugins/decorations/bradient/main.cpp +++ b/src/plugins/decorations/bradient/main.cpp @@ -109,21 +109,21 @@ QWaylandBradientDecoration::QWaylandBradientDecoration() QRectF QWaylandBradientDecoration::closeButtonRect() const { - const int windowRight = waylandWindow()->windowGeometry().right() + 1; + const int windowRight = waylandWindow()->windowContentGeometry().right() + 1; return QRectF(windowRight - BUTTON_WIDTH - BUTTON_SPACING * 0 - BUTTONS_RIGHT_MARGIN, (margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH); } QRectF QWaylandBradientDecoration::maximizeButtonRect() const { - const int windowRight = waylandWindow()->windowGeometry().right() + 1; + const int windowRight = waylandWindow()->windowContentGeometry().right() + 1; return QRectF(windowRight - BUTTON_WIDTH * 2 - BUTTON_SPACING * 1 - BUTTONS_RIGHT_MARGIN, (margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH); } QRectF QWaylandBradientDecoration::minimizeButtonRect() const { - const int windowRight = waylandWindow()->windowGeometry().right() + 1; + const int windowRight = waylandWindow()->windowContentGeometry().right() + 1; return QRectF(windowRight - BUTTON_WIDTH * 3 - BUTTON_SPACING * 2 - BUTTONS_RIGHT_MARGIN, (margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH); } @@ -136,7 +136,7 @@ QMargins QWaylandBradientDecoration::margins() const void QWaylandBradientDecoration::paint(QPaintDevice *device) { bool active = window()->handle()->isActive(); - QRect wg = waylandWindow()->windowGeometry(); + QRect wg = waylandWindow()->windowContentGeometry(); QRect clips[] = { QRect(wg.left(), wg.top(), wg.width(), margins().top()), @@ -267,7 +267,7 @@ bool QWaylandBradientDecoration::handleMouse(QWaylandInputDevice *inputDevice, c Q_UNUSED(global); // Figure out what area mouse is in - QRect wg = waylandWindow()->windowGeometry(); + QRect wg = waylandWindow()->windowContentGeometry(); if (local.y() <= wg.top() + margins().top()) { processMouseTop(inputDevice,local,b,mods); } else if (local.y() > wg.bottom() - margins().bottom()) { @@ -312,7 +312,7 @@ bool QWaylandBradientDecoration::handleTouch(QWaylandInputDevice *inputDevice, c void QWaylandBradientDecoration::processMouseTop(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods) { - QRect wg = waylandWindow()->windowGeometry(); + QRect wg = waylandWindow()->windowContentGeometry(); Q_UNUSED(mods); if (local.y() <= wg.top() + margins().bottom()) { if (local.x() <= margins().left()) { diff --git a/src/shared/qwaylandxkb.cpp b/src/shared/qwaylandxkb.cpp deleted file mode 100644 index 3cfc4b07..00000000 --- a/src/shared/qwaylandxkb.cpp +++ /dev/null @@ -1,395 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Jolla Ltd -** Contact: http://www.qt-project.org/legal -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qwaylandxkb_p.h" - -#include <QKeyEvent> -#include <QString> - -#if QT_CONFIG(xkbcommon) -#include <xkbcommon/xkbcommon-keysyms.h> -#endif - -QT_BEGIN_NAMESPACE - -#if QT_CONFIG(xkbcommon) -static const uint32_t KeyTbl[] = { - XKB_KEY_Escape, Qt::Key_Escape, - XKB_KEY_Tab, Qt::Key_Tab, - XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab, - XKB_KEY_BackSpace, Qt::Key_Backspace, - XKB_KEY_Return, Qt::Key_Return, - XKB_KEY_Insert, Qt::Key_Insert, - XKB_KEY_Delete, Qt::Key_Delete, - XKB_KEY_Clear, Qt::Key_Delete, - XKB_KEY_Pause, Qt::Key_Pause, - XKB_KEY_Print, Qt::Key_Print, - - XKB_KEY_Home, Qt::Key_Home, - XKB_KEY_End, Qt::Key_End, - XKB_KEY_Left, Qt::Key_Left, - XKB_KEY_Up, Qt::Key_Up, - XKB_KEY_Right, Qt::Key_Right, - XKB_KEY_Down, Qt::Key_Down, - XKB_KEY_Prior, Qt::Key_PageUp, - XKB_KEY_Next, Qt::Key_PageDown, - - XKB_KEY_Shift_L, Qt::Key_Shift, - XKB_KEY_Shift_R, Qt::Key_Shift, - XKB_KEY_Shift_Lock, Qt::Key_Shift, - XKB_KEY_Control_L, Qt::Key_Control, - XKB_KEY_Control_R, Qt::Key_Control, - XKB_KEY_Meta_L, Qt::Key_Meta, - XKB_KEY_Meta_R, Qt::Key_Meta, - XKB_KEY_Alt_L, Qt::Key_Alt, - XKB_KEY_Alt_R, Qt::Key_Alt, - XKB_KEY_Caps_Lock, Qt::Key_CapsLock, - XKB_KEY_Num_Lock, Qt::Key_NumLock, - XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock, - XKB_KEY_Super_L, Qt::Key_Super_L, - XKB_KEY_Super_R, Qt::Key_Super_R, - XKB_KEY_Menu, Qt::Key_Menu, - XKB_KEY_Hyper_L, Qt::Key_Hyper_L, - XKB_KEY_Hyper_R, Qt::Key_Hyper_R, - XKB_KEY_Help, Qt::Key_Help, - - XKB_KEY_KP_Space, Qt::Key_Space, - XKB_KEY_KP_Tab, Qt::Key_Tab, - XKB_KEY_KP_Enter, Qt::Key_Enter, - XKB_KEY_KP_Home, Qt::Key_Home, - XKB_KEY_KP_Left, Qt::Key_Left, - XKB_KEY_KP_Up, Qt::Key_Up, - XKB_KEY_KP_Right, Qt::Key_Right, - XKB_KEY_KP_Down, Qt::Key_Down, - XKB_KEY_KP_Prior, Qt::Key_PageUp, - XKB_KEY_KP_Next, Qt::Key_PageDown, - XKB_KEY_KP_End, Qt::Key_End, - XKB_KEY_KP_Begin, Qt::Key_Clear, - XKB_KEY_KP_Insert, Qt::Key_Insert, - XKB_KEY_KP_Delete, Qt::Key_Delete, - XKB_KEY_KP_Equal, Qt::Key_Equal, - XKB_KEY_KP_Multiply, Qt::Key_Asterisk, - XKB_KEY_KP_Add, Qt::Key_Plus, - XKB_KEY_KP_Separator, Qt::Key_Comma, - XKB_KEY_KP_Subtract, Qt::Key_Minus, - XKB_KEY_KP_Decimal, Qt::Key_Period, - XKB_KEY_KP_Divide, Qt::Key_Slash, - - XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr, - XKB_KEY_Multi_key, Qt::Key_Multi_key, - XKB_KEY_Codeinput, Qt::Key_Codeinput, - XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate, - XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate, - XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate, - - XKB_KEY_Mode_switch, Qt::Key_Mode_switch, - XKB_KEY_script_switch, Qt::Key_Mode_switch, - - XKB_KEY_XF86Back, Qt::Key_Back, - XKB_KEY_XF86Forward, Qt::Key_Forward, - XKB_KEY_XF86Stop, Qt::Key_Stop, - XKB_KEY_XF86Refresh, Qt::Key_Refresh, - XKB_KEY_XF86Favorites, Qt::Key_Favorites, - XKB_KEY_XF86AudioMedia, Qt::Key_LaunchMedia, - XKB_KEY_XF86OpenURL, Qt::Key_OpenUrl, - XKB_KEY_XF86HomePage, Qt::Key_HomePage, - XKB_KEY_XF86Search, Qt::Key_Search, - XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown, - XKB_KEY_XF86AudioMute, Qt::Key_VolumeMute, - XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp, - XKB_KEY_XF86AudioPlay, Qt::Key_MediaTogglePlayPause, - XKB_KEY_XF86AudioStop, Qt::Key_MediaStop, - XKB_KEY_XF86AudioPrev, Qt::Key_MediaPrevious, - XKB_KEY_XF86AudioNext, Qt::Key_MediaNext, - XKB_KEY_XF86AudioRecord, Qt::Key_MediaRecord, - XKB_KEY_XF86AudioPause, Qt::Key_MediaPause, - XKB_KEY_XF86Mail, Qt::Key_LaunchMail, - XKB_KEY_XF86Calculator, Qt::Key_Calculator, - XKB_KEY_XF86Memo, Qt::Key_Memo, - XKB_KEY_XF86ToDoList, Qt::Key_ToDoList, - XKB_KEY_XF86Calendar, Qt::Key_Calendar, - XKB_KEY_XF86PowerDown, Qt::Key_PowerDown, - XKB_KEY_XF86ContrastAdjust, Qt::Key_ContrastAdjust, - XKB_KEY_XF86Standby, Qt::Key_Standby, - XKB_KEY_XF86MonBrightnessUp, Qt::Key_MonBrightnessUp, - XKB_KEY_XF86MonBrightnessDown, Qt::Key_MonBrightnessDown, - XKB_KEY_XF86KbdLightOnOff, Qt::Key_KeyboardLightOnOff, - XKB_KEY_XF86KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp, - XKB_KEY_XF86KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown, - XKB_KEY_XF86PowerOff, Qt::Key_PowerOff, - XKB_KEY_XF86WakeUp, Qt::Key_WakeUp, - XKB_KEY_XF86Eject, Qt::Key_Eject, - XKB_KEY_XF86ScreenSaver, Qt::Key_ScreenSaver, - XKB_KEY_XF86WWW, Qt::Key_WWW, - XKB_KEY_XF86Sleep, Qt::Key_Sleep, - XKB_KEY_XF86LightBulb, Qt::Key_LightBulb, - XKB_KEY_XF86Shop, Qt::Key_Shop, - XKB_KEY_XF86History, Qt::Key_History, - XKB_KEY_XF86AddFavorite, Qt::Key_AddFavorite, - XKB_KEY_XF86HotLinks, Qt::Key_HotLinks, - XKB_KEY_XF86BrightnessAdjust, Qt::Key_BrightnessAdjust, - XKB_KEY_XF86Finance, Qt::Key_Finance, - XKB_KEY_XF86Community, Qt::Key_Community, - XKB_KEY_XF86AudioRewind, Qt::Key_AudioRewind, - XKB_KEY_XF86BackForward, Qt::Key_BackForward, - XKB_KEY_XF86ApplicationLeft, Qt::Key_ApplicationLeft, - XKB_KEY_XF86ApplicationRight, Qt::Key_ApplicationRight, - XKB_KEY_XF86Book, Qt::Key_Book, - XKB_KEY_XF86CD, Qt::Key_CD, - XKB_KEY_XF86Calculater, Qt::Key_Calculator, - XKB_KEY_XF86Clear, Qt::Key_Clear, - XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab, - XKB_KEY_XF86Close, Qt::Key_Close, - XKB_KEY_XF86Copy, Qt::Key_Copy, - XKB_KEY_XF86Cut, Qt::Key_Cut, - XKB_KEY_XF86Display, Qt::Key_Display, - XKB_KEY_XF86DOS, Qt::Key_DOS, - XKB_KEY_XF86Documents, Qt::Key_Documents, - XKB_KEY_XF86Excel, Qt::Key_Excel, - XKB_KEY_XF86Explorer, Qt::Key_Explorer, - XKB_KEY_XF86Game, Qt::Key_Game, - XKB_KEY_XF86Go, Qt::Key_Go, - XKB_KEY_XF86iTouch, Qt::Key_iTouch, - XKB_KEY_XF86LogOff, Qt::Key_LogOff, - XKB_KEY_XF86Market, Qt::Key_Market, - XKB_KEY_XF86Meeting, Qt::Key_Meeting, - XKB_KEY_XF86MenuKB, Qt::Key_MenuKB, - XKB_KEY_XF86MenuPB, Qt::Key_MenuPB, - XKB_KEY_XF86MySites, Qt::Key_MySites, - XKB_KEY_XF86New, Qt::Key_New, - XKB_KEY_XF86News, Qt::Key_News, - XKB_KEY_XF86OfficeHome, Qt::Key_OfficeHome, - XKB_KEY_XF86Open, Qt::Key_Open, - XKB_KEY_XF86Option, Qt::Key_Option, - XKB_KEY_XF86Paste, Qt::Key_Paste, - XKB_KEY_XF86Phone, Qt::Key_Phone, - XKB_KEY_XF86Reply, Qt::Key_Reply, - XKB_KEY_XF86Reload, Qt::Key_Reload, - XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows, - XKB_KEY_XF86RotationPB, Qt::Key_RotationPB, - XKB_KEY_XF86RotationKB, Qt::Key_RotationKB, - XKB_KEY_XF86Save, Qt::Key_Save, - XKB_KEY_XF86Send, Qt::Key_Send, - XKB_KEY_XF86Spell, Qt::Key_Spell, - XKB_KEY_XF86SplitScreen, Qt::Key_SplitScreen, - XKB_KEY_XF86Support, Qt::Key_Support, - XKB_KEY_XF86TaskPane, Qt::Key_TaskPane, - XKB_KEY_XF86Terminal, Qt::Key_Terminal, - XKB_KEY_XF86Tools, Qt::Key_Tools, - XKB_KEY_XF86Travel, Qt::Key_Travel, - XKB_KEY_XF86Video, Qt::Key_Video, - XKB_KEY_XF86Word, Qt::Key_Word, - XKB_KEY_XF86Xfer, Qt::Key_Xfer, - XKB_KEY_XF86ZoomIn, Qt::Key_ZoomIn, - XKB_KEY_XF86ZoomOut, Qt::Key_ZoomOut, - XKB_KEY_XF86Away, Qt::Key_Away, - XKB_KEY_XF86Messenger, Qt::Key_Messenger, - XKB_KEY_XF86WebCam, Qt::Key_WebCam, - XKB_KEY_XF86MailForward, Qt::Key_MailForward, - XKB_KEY_XF86Pictures, Qt::Key_Pictures, - XKB_KEY_XF86Music, Qt::Key_Music, - XKB_KEY_XF86Battery, Qt::Key_Battery, - XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth, - XKB_KEY_XF86WLAN, Qt::Key_WLAN, - XKB_KEY_XF86UWB, Qt::Key_UWB, - XKB_KEY_XF86AudioForward, Qt::Key_AudioForward, - XKB_KEY_XF86AudioRepeat, Qt::Key_AudioRepeat, - XKB_KEY_XF86AudioRandomPlay, Qt::Key_AudioRandomPlay, - XKB_KEY_XF86Subtitle, Qt::Key_Subtitle, - XKB_KEY_XF86AudioCycleTrack, Qt::Key_AudioCycleTrack, - XKB_KEY_XF86Time, Qt::Key_Time, - XKB_KEY_XF86Select, Qt::Key_Select, - XKB_KEY_XF86View, Qt::Key_View, - XKB_KEY_XF86TopMenu, Qt::Key_TopMenu, - XKB_KEY_XF86Red, Qt::Key_Red, - XKB_KEY_XF86Green, Qt::Key_Green, - XKB_KEY_XF86Yellow, Qt::Key_Yellow, - XKB_KEY_XF86Blue, Qt::Key_Blue, - XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth, - XKB_KEY_XF86Suspend, Qt::Key_Suspend, - XKB_KEY_XF86Hibernate, Qt::Key_Hibernate, - XKB_KEY_XF86TouchpadToggle, Qt::Key_TouchpadToggle, - XKB_KEY_XF86TouchpadOn, Qt::Key_TouchpadOn, - XKB_KEY_XF86TouchpadOff, Qt::Key_TouchpadOff, - XKB_KEY_XF86AudioMicMute, Qt::Key_MicMute, - XKB_KEY_XF86Launch0, Qt::Key_Launch0, - XKB_KEY_XF86Launch1, Qt::Key_Launch1, - XKB_KEY_XF86Launch2, Qt::Key_Launch2, - XKB_KEY_XF86Launch3, Qt::Key_Launch3, - XKB_KEY_XF86Launch4, Qt::Key_Launch4, - XKB_KEY_XF86Launch5, Qt::Key_Launch5, - XKB_KEY_XF86Launch6, Qt::Key_Launch6, - XKB_KEY_XF86Launch7, Qt::Key_Launch7, - XKB_KEY_XF86Launch8, Qt::Key_Launch8, - XKB_KEY_XF86Launch9, Qt::Key_Launch9, - XKB_KEY_XF86LaunchA, Qt::Key_LaunchA, - XKB_KEY_XF86LaunchB, Qt::Key_LaunchB, - XKB_KEY_XF86LaunchC, Qt::Key_LaunchC, - XKB_KEY_XF86LaunchD, Qt::Key_LaunchD, - XKB_KEY_XF86LaunchE, Qt::Key_LaunchE, - XKB_KEY_XF86LaunchF, Qt::Key_LaunchF, - - 0, 0 -}; - -static int lookupKeysym(xkb_keysym_t key) -{ - int code = 0; - int i = 0; - while (KeyTbl[i]) { - if (key == KeyTbl[i]) { - code = (int)KeyTbl[i+1]; - break; - } - i += 2; - } - - return code; -} - -static xkb_keysym_t toKeysymFromTable(uint32_t key) -{ - for (int i = 0; KeyTbl[i]; i += 2) { - if (key == KeyTbl[i + 1]) - return KeyTbl[i]; - } - - return 0; -} -#endif - -std::pair<int, QString> QWaylandXkb::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers &modifiers) -{ -#if QT_CONFIG(xkbcommon) - QString text; - uint utf32 = xkb_keysym_to_utf32(keysym); - if (utf32) - text = QString::fromUcs4(&utf32, 1); - - int code = 0; - - if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) { - code = Qt::Key_F1 + (int(keysym) - XKB_KEY_F1); - } else if (keysym >= XKB_KEY_KP_Space && keysym <= XKB_KEY_KP_9) { - if (keysym >= XKB_KEY_KP_0) { - // numeric keypad keys - code = Qt::Key_0 + ((int)keysym - XKB_KEY_KP_0); - } else { - code = lookupKeysym(keysym); - } - modifiers |= Qt::KeypadModifier; - } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f - && text.unicode()->unicode() != 0x7f - && !(keysym >= XKB_KEY_dead_grave && keysym <= XKB_KEY_dead_currency)) { - code = text.unicode()->toUpper().unicode(); - } else { - // any other keys - code = lookupKeysym(keysym); - } - - // Map control + letter to proper text - if (utf32 >= 'A' && utf32 <= '~' && (modifiers & Qt::ControlModifier)) { - utf32 &= ~0x60; - text = QString::fromUcs4(&utf32, 1); - } - - return { code, text }; -#else - Q_UNUSED(modifiers) - return { keysym, "" }; -#endif -} - -Qt::KeyboardModifiers QWaylandXkb::modifiers(struct xkb_state *state) -{ -#if QT_CONFIG(xkbcommon) - Qt::KeyboardModifiers modifiers = Qt::NoModifier; - - xkb_state_component cstate = static_cast<xkb_state_component>(XKB_STATE_DEPRESSED | XKB_STATE_LATCHED | XKB_STATE_LOCKED); - - if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT, cstate)) - modifiers |= Qt::ShiftModifier; - if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, cstate)) - modifiers |= Qt::ControlModifier; - if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, cstate)) - modifiers |= Qt::AltModifier; - if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO, cstate)) - modifiers |= Qt::MetaModifier; - - return modifiers; -#else - Q_UNUSED(state) - return Qt::NoModifier; -#endif -} - -QEvent::Type QWaylandXkb::toQtEventType(uint32_t state) -{ - return state != 0 ? QEvent::KeyPress : QEvent::KeyRelease; -} - -QVector<xkb_keysym_t> QWaylandXkb::toKeysym(QKeyEvent *event) -{ -#if QT_CONFIG(xkbcommon) - QVector<xkb_keysym_t> keysyms; - if (event->key() >= Qt::Key_F1 && event->key() <= Qt::Key_F35) { - keysyms.append(XKB_KEY_F1 + (event->key() - Qt::Key_F1)); - } else if (event->modifiers() & Qt::KeypadModifier) { - if (event->key() >= Qt::Key_0 && event->key() <= Qt::Key_9) - keysyms.append(XKB_KEY_KP_0 + (event->key() - Qt::Key_0)); - else - keysyms.append(toKeysymFromTable(event->key())); - } else if (!event->text().isEmpty()) { - // From libxkbcommon keysym-utf.c: - // "We allow to represent any UCS character in the range U-00000000 to - // U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff." - foreach (uint utf32, event->text().toUcs4()) { - keysyms.append(utf32 | 0x01000000); - } - } else { - keysyms.append(toKeysymFromTable(event->key())); - } - return keysyms; -#else - return QVector<xkb_keysym_t>() << event->nativeScanCode(); -#endif -} - -QT_END_NAMESPACE diff --git a/src/shared/qwaylandxkb_p.h b/src/shared/qwaylandxkb_p.h deleted file mode 100644 index 4820d94b..00000000 --- a/src/shared/qwaylandxkb_p.h +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Jolla Ltd -** Contact: http://www.qt-project.org/legal -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QWAYLANDXKB_H -#define QWAYLANDXKB_H - -#include <QtGui/private/qtguiglobal_p.h> -#include <Qt> -#include <QEvent> - -#if QT_CONFIG(xkbcommon) -#include <xkbcommon/xkbcommon.h> -#else -typedef quint32 xkb_keysym_t; -struct xkb_state; -#endif - -#include <utility> - -QT_BEGIN_NAMESPACE - -class QKeyEvent; - -class QWaylandXkb -{ -public: - static std::pair<int, QString> keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers &modifiers); - static Qt::KeyboardModifiers modifiers(struct xkb_state *state); - - static QEvent::Type toQtEventType(uint32_t state); - static QVector<xkb_keysym_t> toKeysym(QKeyEvent *event); -}; - -QT_END_NAMESPACE - -#endif diff --git a/tests/auto/client/client.pro b/tests/auto/client/client.pro index 97d99bd3..5e0dcab8 100644 --- a/tests/auto/client/client.pro +++ b/tests/auto/client/client.pro @@ -13,3 +13,5 @@ SUBDIRS += \ xdgoutput \ xdgshell \ xdgshellv6 + +qtConfig(im): SUBDIRS += inputcontext diff --git a/tests/auto/client/inputcontext/inputcontext.pro b/tests/auto/client/inputcontext/inputcontext.pro new file mode 100644 index 00000000..4419b3e7 --- /dev/null +++ b/tests/auto/client/inputcontext/inputcontext.pro @@ -0,0 +1,6 @@ +include (../shared/shared.pri) + +QT += waylandcompositor + +TARGET = tst_inputcontext +SOURCES += tst_inputcontext.cpp diff --git a/tests/auto/client/inputcontext/tst_inputcontext.cpp b/tests/auto/client/inputcontext/tst_inputcontext.cpp new file mode 100644 index 00000000..b1a5a7f1 --- /dev/null +++ b/tests/auto/client/inputcontext/tst_inputcontext.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mockcompositor.h" +#include "textinput.h" + +#include <QtCore/QString> +#include <QtCore/QByteArray> + +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/qpa/qplatforminputcontext.h> +#include <QtGui/qpa/qplatformintegration.h> +#include <QtGui/qpa/qplatforminputcontextfactory_p.h> + +#include <QtTest/QtTest> + +using namespace MockCompositor; + +class tst_inputcontext : public QObject, private DefaultCompositor +{ + Q_OBJECT +private slots: + void initTestCase(); + void selectingInputContext_data(); + void selectingInputContext(); + void inputContextReconfigurationWhenTogglingTextInputExtension(); + +private: + QByteArray inputContextName() const; + void ensureTextInputPresentOnCompositor(); + void ensureTextInputNotPresentOnCompositor(); + + QByteArray mComposeModule = QByteArray("QComposeInputContext"); // default input context + QByteArray mIbusModule = QByteArray("QIBusPlatformInputContext"); + QByteArray mWaylandModule = QByteArray("QtWaylandClient::QWaylandInputContext"); + + TextInputManager *mTextInputManager = nullptr; +}; + +void tst_inputcontext::initTestCase() +{ + // Verify that plugins are present and valid + QPlatformInputContext *context = QPlatformInputContextFactory::create(QStringLiteral("compose")); + QVERIFY(context && context->isValid()); + + context = QPlatformInputContextFactory::create(QStringLiteral("ibus")); + // The ibus plugin depends on properly configured system services, if plugin is not valid + // verify that wayland qpa plugin properly fallbacks to default input context. + if (!context || !context->isValid()) + mIbusModule = mComposeModule; +} + +QByteArray tst_inputcontext::inputContextName() const +{ + QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration(); + if (platformIntegration->inputContext()) + return platformIntegration->inputContext()->metaObject()->className(); + + return QByteArray(""); +} + +void tst_inputcontext::ensureTextInputPresentOnCompositor() +{ + exec([&] { + QVector<TextInputManager *> extensions = getAll<TextInputManager>(); + if (extensions.length() > 1) + QFAIL("TextInputManager is a singleton, hence there should not be more then one object returned"); + if (extensions.length() == 0) + add<TextInputManager>(); + }); +} + +void tst_inputcontext::ensureTextInputNotPresentOnCompositor() +{ + exec([&] { + QVector<TextInputManager *> extensions = getAll<TextInputManager>(); + if (extensions.length() > 1) + QFAIL("TextInputManager is a singleton, hence there should not be more then one object returned"); + if (extensions.length() == 1) + remove(extensions.first()); + }); +} + +void tst_inputcontext::selectingInputContext_data() +{ + QTest::addColumn<QByteArray>("requestedModule"); + QTest::addColumn<QByteArray>("expectedModule"); + + // Test compositor without Text Input extension + QTest::newRow("ibus") << QByteArray("ibus") << mIbusModule; + QTest::newRow("compose") << QByteArray("compose") << mComposeModule; + QTest::newRow("empty") << QByteArray("") << mComposeModule; + QTest::newRow("null") << QByteArray() << mComposeModule; + QTest::newRow("fake") << QByteArray("fake") << mComposeModule; + + // Test compositor with Text Input extension + QTest::newRow("ibus:text-input") << QByteArray("ibus") << mIbusModule; + QTest::newRow("compose:text-input") << QByteArray("compose") << mComposeModule; + QTest::newRow("empty:text-input") << QByteArray("") << mComposeModule; + QTest::newRow("null:text-input") << QByteArray() << mWaylandModule; + QTest::newRow("fake:text-input") << QByteArray("fake") << mComposeModule; +} + +void tst_inputcontext::selectingInputContext() +{ + QFETCH(QByteArray, requestedModule); + QFETCH(QByteArray, expectedModule); + + if (requestedModule.isNull()) + qunsetenv("QT_IM_MODULE"); + else + qputenv("QT_IM_MODULE", requestedModule); + + const bool withTextInputAtCompositorSide = QByteArray(QTest::currentDataTag()).endsWith(":text-input"); + + if (withTextInputAtCompositorSide) + ensureTextInputPresentOnCompositor(); + else + ensureTextInputNotPresentOnCompositor(); + + int argc = 0; + QGuiApplication app(argc, nullptr); // loads the platform plugin + + QCOMPARE(inputContextName(), expectedModule); +} + +void tst_inputcontext::inputContextReconfigurationWhenTogglingTextInputExtension() +{ + qunsetenv("QT_IM_MODULE"); + + ensureTextInputPresentOnCompositor(); + int argc = 0; + QGuiApplication app(argc, nullptr); // loads the platform plugin + QCOMPARE(inputContextName(), mWaylandModule); + + // remove text input extension after the platform plugin has been loaded + ensureTextInputNotPresentOnCompositor(); + // QTRY_* because we need to spin the event loop for wayland QPA plugin + // to handle registry_global_remove() + QTRY_COMPARE(inputContextName(), mComposeModule); + + // add text input extension after the platform plugin has been loaded + ensureTextInputPresentOnCompositor(); + // QTRY_* because we need to spin the event loop for wayland QPA plugin + // to handle registry_global() + QTRY_COMPARE(inputContextName(), mWaylandModule); +} + +int main(int argc, char *argv[]) +{ + qputenv("XDG_RUNTIME_DIR", "."); + qputenv("QT_QPA_PLATFORM", "wayland"); + + tst_inputcontext tc; + QTEST_SET_MAIN_SOURCE_PATH + return QTest::qExec(&tc, argc, argv); +} + +#include "tst_inputcontext.moc" diff --git a/tests/auto/client/output/tst_output.cpp b/tests/auto/client/output/tst_output.cpp index 45167948..2d2c8efd 100644 --- a/tests/auto/client/output/tst_output.cpp +++ b/tests/auto/client/output/tst_output.cpp @@ -215,10 +215,7 @@ void tst_output::screenOrder() QTRY_COMPARE(QGuiApplication::screens().size(), 3); const auto screens = QGuiApplication::screens(); - QEXPECT_FAIL(nullptr, "TODO: fix screen order", Continue); QCOMPARE(screens[1]->model(), "Screen 1"); - - QEXPECT_FAIL(nullptr, "TODO: fix screen order", Continue); QCOMPARE(screens[2]->model(), "Screen 2"); exec([=] { diff --git a/tests/auto/client/shared/shared.pri b/tests/auto/client/shared/shared.pri index 303e1304..c86183b3 100644 --- a/tests/auto/client/shared/shared.pri +++ b/tests/auto/client/shared/shared.pri @@ -4,7 +4,8 @@ QMAKE_USE += wayland-server WAYLANDSERVERSOURCES += \ $$PWD/../../../../src/3rdparty/protocol/wayland.xml \ - $$PWD/../../../../src/3rdparty/protocol/xdg-shell.xml + $$PWD/../../../../src/3rdparty/protocol/xdg-shell.xml \ + $$PWD/../../../../src/3rdparty/protocol/text-input-unstable-v2.xml INCLUDEPATH += ../shared @@ -13,11 +14,13 @@ HEADERS += \ $$PWD/coreprotocol.h \ $$PWD/datadevice.h \ $$PWD/mockcompositor.h \ - $$PWD/xdgshell.h + $$PWD/xdgshell.h \ + $$PWD/textinput.h SOURCES += \ $$PWD/corecompositor.cpp \ $$PWD/coreprotocol.cpp \ $$PWD/datadevice.cpp \ $$PWD/mockcompositor.cpp \ - $$PWD/xdgshell.cpp + $$PWD/xdgshell.cpp \ + $$PWD/textinput.cpp diff --git a/tests/auto/client/shared/textinput.cpp b/tests/auto/client/shared/textinput.cpp new file mode 100644 index 00000000..f9fd287b --- /dev/null +++ b/tests/auto/client/shared/textinput.cpp @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "textinput.h" + +namespace MockCompositor { + +TextInputManager::TextInputManager(CoreCompositor *compositor) +{ + init(compositor->m_display, 1); +} + +void TextInputManager::zwp_text_input_manager_v2_get_text_input(Resource *resource, uint32_t id, wl_resource *seatResource) +{ + Q_UNUSED(resource); + Q_UNUSED(id); + Q_UNUSED(seatResource); +} + +} // namespace MockCompositor diff --git a/tests/auto/client/shared/textinput.h b/tests/auto/client/shared/textinput.h new file mode 100644 index 00000000..85072e74 --- /dev/null +++ b/tests/auto/client/shared/textinput.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MOCKCOMPOSITOR_TEXTINPUT_H +#define MOCKCOMPOSITOR_TEXTINPUT_H + +#include "coreprotocol.h" +#include <qwayland-server-text-input-unstable-v2.h> + +#include <QtGui/qpa/qplatformnativeinterface.h> + +namespace MockCompositor { + +class TextInputManager : public Global, public QtWaylandServer::zwp_text_input_manager_v2 +{ + Q_OBJECT +public: + TextInputManager(CoreCompositor *compositor); + +protected: + void zwp_text_input_manager_v2_get_text_input(Resource *resource, uint32_t id, struct ::wl_resource *seatResource) override; +}; + +} // namespace MockCompositor + +#endif // MOCKCOMPOSITOR_TEXTINPUT_H diff --git a/tests/auto/client/xdgshell/tst_xdgshell.cpp b/tests/auto/client/xdgshell/tst_xdgshell.cpp index 809e564e..038ff662 100644 --- a/tests/auto/client/xdgshell/tst_xdgshell.cpp +++ b/tests/auto/client/xdgshell/tst_xdgshell.cpp @@ -65,7 +65,7 @@ void tst_xdgshell::showMinimized() // Make sure the window on the compositor side is/was created here, and not after the test // finishes, as that may mess up for later tests. - QCOMPOSITOR_TRY_VERIFY(surface()); + QCOMPOSITOR_TRY_VERIFY(xdgSurface()); QVERIFY(!window.isExposed()); } @@ -426,9 +426,16 @@ void tst_xdgshell::switchPopups() void tst_xdgshell::pongs() { - QSignalSpy pongSpy(exec([=] { return get<XdgWmBase>(); }), &XdgWmBase::pong); + // Create and show a window to trigger shell integration initialzation, + // otherwise we don't have anything to send ping events to. + QRasterWindow window; + window.resize(200, 200); + window.show(); + // Verify that the client has bound to the global QCOMPOSITOR_TRY_COMPARE(get<XdgWmBase>()->resourceMap().size(), 1); + + QSignalSpy pongSpy(exec([=] { return get<XdgWmBase>(); }), &XdgWmBase::pong); const uint serial = exec([=] { return nextSerial(); }); exec([=] { auto *base = get<XdgWmBase>(); diff --git a/tests/auto/compositor/compositor/BLACKLIST b/tests/auto/compositor/compositor/BLACKLIST deleted file mode 100644 index df4672be..00000000 --- a/tests/auto/compositor/compositor/BLACKLIST +++ /dev/null @@ -1,2 +0,0 @@ -[keyboardGrab] -ubuntu-14.04 |