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