summaryrefslogtreecommitdiffstats
path: root/src/client
diff options
context:
space:
mode:
authorJohan Klokkhammer Helsing <johan.helsing@qt.io>2019-05-07 03:01:52 +0200
committerJohan Klokkhammer Helsing <johan.helsing@qt.io>2019-05-07 12:39:12 +0200
commit17b1705ce4c8bc9ca19a6b424a00a045134b3aac (patch)
tree03e7b7d461064dd007179cd29da9c024e6c8f190 /src/client
parent4ab3cbcc5c4da6384e9081993c5ba24303a382bd (diff)
parenta658a10f6a42e67bd762f87851c23cc1c1e3b141 (diff)
Merge remote-tracking branch 'origin/5.13' into dev
Conflicts: src/client/qwaylanddisplay_p.h src/client/qwaylandwindow.cpp Change-Id: I50eb5c83a8b81e4bdb032b68d41f429b17d0a74d
Diffstat (limited to 'src/client')
-rw-r--r--src/client/client.pro7
-rw-r--r--src/client/qwaylanddisplay.cpp99
-rw-r--r--src/client/qwaylanddisplay_p.h31
-rw-r--r--src/client/qwaylandinputcontext.cpp15
-rw-r--r--src/client/qwaylandinputdevice.cpp281
-rw-r--r--src/client/qwaylandinputdevice_p.h56
-rw-r--r--src/client/qwaylandintegration.cpp63
-rw-r--r--src/client/qwaylandintegration_p.h4
-rw-r--r--src/client/qwaylandscreen.cpp52
-rw-r--r--src/client/qwaylandscreen_p.h7
-rw-r--r--src/client/qwaylandwindow.cpp202
-rw-r--r--src/client/qwaylandwindow_p.h19
12 files changed, 546 insertions, 290 deletions
diff --git a/src/client/client.pro b/src/client/client.pro
index 4be288c81..ff9e845f1 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 9a4c76b1d..7c32b6bf5 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 c518cb94e..2c7ed3231 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 55e7641e5..1d34f06cc 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 89b50f0ad..f0fd99563 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 404bcf571..f44f1ab1f 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 45957629f..ea2b50b4a 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 944f635bb..3aef2c4d9 100644
--- a/src/client/qwaylandintegration_p.h
+++ b/src/client/qwaylandintegration_p.h
@@ -106,6 +106,8 @@ public:
QWaylandDisplay *display() const;
+ QList<int> possibleKeys(const QKeyEvent *event) const override;
+
QStringList themeNames() const override;
QPlatformTheme *createPlatformTheme(const QString &name) const override;
@@ -116,6 +118,8 @@ public:
virtual QWaylandServerBufferIntegration *serverBufferIntegration() const;
virtual QWaylandShellIntegration *shellIntegration() const;
+ void reconfigureInputContext();
+
private:
// NOTE: mDisplay *must* be destructed after mDrag and mClientBufferIntegration
// and mShellIntegration.
diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp
index ee6f949aa..d116a807b 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 4ef58c0c1..e9e07d9cd 100644
--- a/src/client/qwaylandscreen_p.h
+++ b/src/client/qwaylandscreen_p.h
@@ -71,6 +71,8 @@ public:
QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id);
~QWaylandScreen() override;
+ void maybeInitialize();
+
void initXdgOutput(QtWayland::zxdg_output_manager_v1 *xdgOutputManager);
QWaylandDisplay *display() const;
@@ -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 d0cb63172..9bc400f8a 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 3e9b43851..5eadc02c8 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;