summaryrefslogtreecommitdiffstats
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/client.pro8
-rw-r--r--src/client/configure.json12
-rw-r--r--src/client/qwaylandclipboard.cpp71
-rw-r--r--src/client/qwaylandcursor.cpp25
-rw-r--r--src/client/qwaylandcursor_p.h2
-rw-r--r--src/client/qwaylanddatadevice.cpp8
-rw-r--r--src/client/qwaylanddataoffer.cpp15
-rw-r--r--src/client/qwaylanddataoffer_p.h24
-rw-r--r--src/client/qwaylanddatasource.cpp3
-rw-r--r--src/client/qwaylanddisplay.cpp28
-rw-r--r--src/client/qwaylanddisplay_p.h12
-rw-r--r--src/client/qwaylandextendedsurface.cpp2
-rw-r--r--src/client/qwaylandinputcontext.cpp12
-rw-r--r--src/client/qwaylandinputdevice.cpp515
-rw-r--r--src/client/qwaylandinputdevice_p.h116
-rw-r--r--src/client/qwaylandintegration.cpp6
-rw-r--r--src/client/qwaylandnativeinterface.cpp24
-rw-r--r--src/client/qwaylandprimaryselectionv1.cpp162
-rw-r--r--src/client/qwaylandprimaryselectionv1_p.h148
-rw-r--r--src/client/qwaylandscreen.cpp3
-rw-r--r--src/client/qwaylandshmbackingstore.cpp30
-rw-r--r--src/client/qwaylandshmbackingstore_p.h5
-rw-r--r--src/client/qwaylandsurface.cpp122
-rw-r--r--src/client/qwaylandsurface_p.h97
-rw-r--r--src/client/qwaylandtouch.cpp1
-rw-r--r--src/client/qwaylandwindow.cpp178
-rw-r--r--src/client/qwaylandwindow_p.h19
27 files changed, 1331 insertions, 317 deletions
diff --git a/src/client/client.pro b/src/client/client.pro
index 4233ac950..d0ae9009e 100644
--- a/src/client/client.pro
+++ b/src/client/client.pro
@@ -31,6 +31,7 @@ WAYLANDCLIENTSOURCES += \
../extensions/touch-extension.xml \
../extensions/qt-key-unstable-v1.xml \
../extensions/qt-windowmanager.xml \
+ ../3rdparty/protocol/wp-primary-selection-unstable-v1.xml \
../3rdparty/protocol/text-input-unstable-v2.xml \
../3rdparty/protocol/xdg-output-unstable-v1.xml \
../3rdparty/protocol/wayland.xml
@@ -46,6 +47,7 @@ SOURCES += qwaylandintegration.cpp \
qwaylandshellsurface.cpp \
qwaylandextendedsurface.cpp \
qwaylandsubsurface.cpp \
+ qwaylandsurface.cpp \
qwaylandtouch.cpp \
qwaylandqtkey.cpp \
../shared/qwaylandmimehelper.cpp \
@@ -70,6 +72,7 @@ HEADERS += qwaylandintegration_p.h \
qwaylandshellsurface_p.h \
qwaylandextendedsurface_p.h \
qwaylandsubsurface_p.h \
+ qwaylandsurface_p.h \
qwaylandtouch_p.h \
qwaylandqtkey_p.h \
qwaylandabstractdecoration_p.h \
@@ -116,6 +119,11 @@ qtConfig(wayland-datadevice) {
qwaylanddatasource.cpp
}
+qtConfig(wayland-client-primary-selection) {
+ HEADERS += qwaylandprimaryselectionv1_p.h
+ SOURCES += qwaylandprimaryselectionv1.cpp
+}
+
qtConfig(draganddrop) {
HEADERS += \
qwaylanddnd_p.h
diff --git a/src/client/configure.json b/src/client/configure.json
index 91024c9d3..e9e16324b 100644
--- a/src/client/configure.json
+++ b/src/client/configure.json
@@ -93,6 +93,11 @@
"condition": "features.draganddrop || features.clipboard",
"output": [ "privateFeature" ]
},
+ "wayland-client-primary-selection": {
+ "label": "primary-selection clipboard",
+ "condition": "features.clipboard",
+ "output": [ "privateFeature" ]
+ },
"wayland-client-fullscreen-shell-v1": {
"label": "fullscreen-shell-v1",
"condition": "features.wayland-client",
@@ -158,14 +163,9 @@
"condition": "features.wayland-client && features.opengl && features.egl && tests.dmabuf-server-buffer",
"output": [ "privateFeature" ]
},
- "wayland-client-texture-sharing-experimental" : {
- "label": "Texture sharing (experimental)",
- "autoDetect": "false",
- "output": [ "privateFeature" ]
- },
"wayland-vulkan-server-buffer": {
"label": "Vulkan-based server buffer integration",
- "condition": "features.wayland-client && features.opengl && features.egl && tests.vulkan-server-buffer && features.wayland-client-texture-sharing-experimental",
+ "condition": "features.wayland-client && features.opengl && features.egl && tests.vulkan-server-buffer",
"output": [ "privateFeature" ]
},
"wayland-shm-emulation-server-buffer": {
diff --git a/src/client/qwaylandclipboard.cpp b/src/client/qwaylandclipboard.cpp
index 60820da92..369c6ec07 100644
--- a/src/client/qwaylandclipboard.cpp
+++ b/src/client/qwaylandclipboard.cpp
@@ -43,6 +43,9 @@
#include "qwaylanddataoffer_p.h"
#include "qwaylanddatasource_p.h"
#include "qwaylanddatadevice_p.h"
+#if QT_CONFIG(wayland_client_primary_selection)
+#include "qwaylandprimaryselectionv1_p.h"
+#endif
QT_BEGIN_NAMESPACE
@@ -59,44 +62,74 @@ QWaylandClipboard::~QWaylandClipboard()
QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode)
{
- if (mode != QClipboard::Clipboard)
+ auto *seat = mDisplay->currentInputDevice();
+ if (!seat)
return &m_emptyData;
- QWaylandInputDevice *inputDevice = mDisplay->currentInputDevice();
- if (!inputDevice || !inputDevice->dataDevice())
+ switch (mode) {
+ case QClipboard::Clipboard:
+ if (auto *dataDevice = seat->dataDevice()) {
+ if (auto *source = dataDevice->selectionSource())
+ return source->mimeData();
+ if (auto *offer = dataDevice->selectionOffer())
+ return offer->mimeData();
+ }
+ return &m_emptyData;
+ case QClipboard::Selection:
+#if QT_CONFIG(wayland_client_primary_selection)
+ if (auto *selectionDevice = seat->primarySelectionDevice()) {
+ if (auto *source = selectionDevice->selectionSource())
+ return source->mimeData();
+ if (auto *offer = selectionDevice->selectionOffer())
+ return offer->mimeData();
+ }
+#endif
+ return &m_emptyData;
+ default:
return &m_emptyData;
-
- QWaylandDataSource *source = inputDevice->dataDevice()->selectionSource();
- if (source) {
- return source->mimeData();
}
-
- if (inputDevice->dataDevice()->selectionOffer())
- return inputDevice->dataDevice()->selectionOffer()->mimeData();
-
- return &m_emptyData;
}
void QWaylandClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
{
- if (mode != QClipboard::Clipboard)
- return;
-
- QWaylandInputDevice *inputDevice = mDisplay->currentInputDevice();
- if (!inputDevice || !inputDevice->dataDevice())
+ auto *seat = mDisplay->currentInputDevice();
+ if (!seat)
return;
static const QString plain = QStringLiteral("text/plain");
static const QString utf8 = QStringLiteral("text/plain;charset=utf-8");
+
if (data && data->hasFormat(plain) && !data->hasFormat(utf8))
data->setData(utf8, data->data(plain));
- inputDevice->dataDevice()->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(), data) : nullptr);
- emitChanged(mode);
+ switch (mode) {
+ case QClipboard::Clipboard:
+ if (auto *dataDevice = seat->dataDevice()) {
+ dataDevice->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(), data) : nullptr);
+ emitChanged(mode);
+ }
+ break;
+ case QClipboard::Selection:
+#if QT_CONFIG(wayland_client_primary_selection)
+ if (auto *selectionDevice = seat->primarySelectionDevice()) {
+ selectionDevice->setSelectionSource(data ? new QWaylandPrimarySelectionSourceV1(mDisplay->primarySelectionManager(), data) : nullptr);
+ emitChanged(mode);
+ }
+#endif
+ break;
+ default:
+ break;
+ }
}
bool QWaylandClipboard::supportsMode(QClipboard::Mode mode) const
{
+#if QT_CONFIG(wayland_client_primary_selection)
+ if (mode == QClipboard::Selection) {
+ auto *seat = mDisplay->currentInputDevice();
+ return seat && seat->primarySelectionDevice();
+ }
+#endif
return mode == QClipboard::Clipboard;
}
diff --git a/src/client/qwaylandcursor.cpp b/src/client/qwaylandcursor.cpp
index 8b2ed036d..4356b23a0 100644
--- a/src/client/qwaylandcursor.cpp
+++ b/src/client/qwaylandcursor.cpp
@@ -48,6 +48,8 @@
#include <wayland-cursor.h>
+#include <algorithm>
+
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@@ -75,7 +77,10 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape)
if (struct wl_cursor *cursor = m_cursors.value(shape, nullptr))
return cursor;
- static const QMultiMap<WaylandCursor, QByteArray>cursorNamesMap {
+ static Q_CONSTEXPR struct ShapeAndName {
+ WaylandCursor shape;
+ const char name[33];
+ } cursorNamesMap[] = {
{ArrowCursor, "left_ptr"},
{ArrowCursor, "default"},
{ArrowCursor, "top_left_arrow"},
@@ -193,9 +198,14 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape)
{ResizeSouthWestCursor, "bottom_left_corner"},
};
- QList<QByteArray> cursorNames = cursorNamesMap.values(shape);
- for (auto &name : qAsConst(cursorNames)) {
- if (wl_cursor *cursor = wl_cursor_theme_get_cursor(m_theme, name.constData())) {
+ const auto byShape = [](ShapeAndName lhs, ShapeAndName rhs) {
+ return lhs.shape < rhs.shape;
+ };
+ Q_ASSERT(std::is_sorted(std::begin(cursorNamesMap), std::end(cursorNamesMap), byShape));
+ const auto p = std::equal_range(std::begin(cursorNamesMap), std::end(cursorNamesMap),
+ ShapeAndName{shape, ""}, byShape);
+ for (auto it = p.first; it != p.second; ++it) {
+ if (wl_cursor *cursor = wl_cursor_theme_get_cursor(m_theme, it->name)) {
m_cursors.insert(shape, cursor);
return cursor;
}
@@ -209,7 +219,7 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape)
return nullptr;
}
-struct wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape)
+::wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape, uint millisecondsIntoAnimation)
{
struct wl_cursor *waylandCursor = nullptr;
@@ -227,8 +237,9 @@ struct wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape)
return nullptr;
}
- struct wl_cursor_image *image = waylandCursor->images[0];
- struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
+ int frame = wl_cursor_frame(waylandCursor, millisecondsIntoAnimation);
+ ::wl_cursor_image *image = waylandCursor->images[frame];
+ ::wl_buffer *buffer = wl_cursor_image_get_buffer(image);
if (!buffer) {
qCWarning(lcQpaWayland) << "Could not find buffer for cursor";
return nullptr;
diff --git a/src/client/qwaylandcursor_p.h b/src/client/qwaylandcursor_p.h
index 6c48fb628..a4605f3d2 100644
--- a/src/client/qwaylandcursor_p.h
+++ b/src/client/qwaylandcursor_p.h
@@ -75,7 +75,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandCursorTheme
public:
static QWaylandCursorTheme *create(QWaylandShm *shm, int size, const QString &themeName);
~QWaylandCursorTheme();
- struct wl_cursor_image *cursorImage(Qt::CursorShape shape);
+ ::wl_cursor_image *cursorImage(Qt::CursorShape shape, uint millisecondsIntoAnimation = 0);
private:
enum WaylandCursor {
diff --git a/src/client/qwaylanddatadevice.cpp b/src/client/qwaylanddatadevice.cpp
index 990f92ba9..fc3c7077a 100644
--- a/src/client/qwaylanddatadevice.cpp
+++ b/src/client/qwaylanddatadevice.cpp
@@ -47,6 +47,7 @@
#include "qwaylandinputdevice_p.h"
#include "qwaylanddisplay_p.h"
#include "qwaylandabstractdecoration_p.h"
+#include "qwaylandsurface_p.h"
#include <QtCore/QMimeData>
#include <QtGui/QGuiApplication>
@@ -104,9 +105,10 @@ QWaylandDataOffer *QWaylandDataDevice::dragOffer() const
bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon)
{
- QWaylandWindow *origin = m_display->currentInputDevice()->pointerFocus();
+ auto *seat = m_display->currentInputDevice();
+ auto *origin = seat->pointerFocus();
if (!origin)
- origin = m_display->currentInputDevice()->touchFocus();
+ origin = seat->touchFocus();
if (!origin) {
qCDebug(lcQpaWayland) << "Couldn't start a drag because the origin window could not be found.";
@@ -116,7 +118,7 @@ bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon)
m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData));
connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled);
- start_drag(m_dragSource->object(), origin->object(), icon->object(), m_display->currentInputDevice()->serial());
+ start_drag(m_dragSource->object(), origin->wlSurface(), icon->wlSurface(), m_display->currentInputDevice()->serial());
return true;
}
diff --git a/src/client/qwaylanddataoffer.cpp b/src/client/qwaylanddataoffer.cpp
index 3da16ed00..4c06277fe 100644
--- a/src/client/qwaylanddataoffer.cpp
+++ b/src/client/qwaylanddataoffer.cpp
@@ -58,7 +58,8 @@ static QString utf8Text()
QWaylandDataOffer::QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer)
: QtWayland::wl_data_offer(offer)
- , m_mimeData(new QWaylandMimeData(this, display))
+ , m_display(display)
+ , m_mimeData(new QWaylandMimeData(this))
{
}
@@ -81,14 +82,19 @@ QMimeData *QWaylandDataOffer::mimeData()
return m_mimeData.data();
}
+void QWaylandDataOffer::startReceiving(const QString &mimeType, int fd)
+{
+ receive(mimeType, fd);
+ wl_display_flush(m_display->wl_display());
+}
+
void QWaylandDataOffer::data_offer_offer(const QString &mime_type)
{
m_mimeData->appendFormat(mime_type);
}
-QWaylandMimeData::QWaylandMimeData(QWaylandDataOffer *dataOffer, QWaylandDisplay *display)
+QWaylandMimeData::QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer)
: m_dataOffer(dataOffer)
- , m_display(display)
{
}
@@ -140,8 +146,7 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T
return QVariant();
}
- m_dataOffer->receive(mime, pipefd[1]);
- wl_display_flush(m_display->wl_display());
+ m_dataOffer->startReceiving(mime, pipefd[1]);
close(pipefd[1]);
diff --git a/src/client/qwaylanddataoffer_p.h b/src/client/qwaylanddataoffer_p.h
index 5412400a5..9cf1483ca 100644
--- a/src/client/qwaylanddataoffer_p.h
+++ b/src/client/qwaylanddataoffer_p.h
@@ -65,27 +65,40 @@ namespace QtWaylandClient {
class QWaylandDisplay;
class QWaylandMimeData;
-class Q_WAYLAND_CLIENT_EXPORT QWaylandDataOffer : public QtWayland::wl_data_offer
+class QWaylandAbstractDataOffer
+{
+public:
+ virtual void startReceiving(const QString &mimeType, int fd) = 0;
+ virtual QMimeData *mimeData() = 0;
+
+ virtual ~QWaylandAbstractDataOffer() = default;
+};
+
+class Q_WAYLAND_CLIENT_EXPORT QWaylandDataOffer
+ : public QtWayland::wl_data_offer // needs to be the first because we do static casts from the user pointer to the wrapper
+ , public QWaylandAbstractDataOffer
{
public:
explicit QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer);
~QWaylandDataOffer() override;
+ QMimeData *mimeData() override;
QString firstFormat() const;
- QMimeData *mimeData();
+ void startReceiving(const QString &mimeType, int fd) override;
protected:
void data_offer_offer(const QString &mime_type) override;
private:
+ QWaylandDisplay *m_display = nullptr;
QScopedPointer<QWaylandMimeData> m_mimeData;
};
class QWaylandMimeData : public QInternalMimeData {
public:
- explicit QWaylandMimeData(QWaylandDataOffer *dataOffer, QWaylandDisplay *display);
+ explicit QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer);
~QWaylandMimeData() override;
void appendFormat(const QString &mimeType);
@@ -98,13 +111,12 @@ protected:
private:
int readData(int fd, QByteArray &data) const;
- mutable QWaylandDataOffer *m_dataOffer = nullptr;
- QWaylandDisplay *m_display = nullptr;
+ QWaylandAbstractDataOffer *m_dataOffer = nullptr;
mutable QStringList m_types;
mutable QHash<QString, QByteArray> m_data;
};
-}
+} // namespace QtWaylandClient
QT_END_NAMESPACE
#endif
diff --git a/src/client/qwaylanddatasource.cpp b/src/client/qwaylanddatasource.cpp
index 0c6ad50e4..ea76943a7 100644
--- a/src/client/qwaylanddatasource.cpp
+++ b/src/client/qwaylanddatasource.cpp
@@ -60,7 +60,8 @@ QWaylandDataSource::QWaylandDataSource(QWaylandDataDeviceManager *dataDeviceMana
{
if (!mimeData)
return;
- Q_FOREACH (const QString &format, mimeData->formats()) {
+ const auto formats = mimeData->formats();
+ for (const QString &format : formats) {
offer(format);
}
}
diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp
index 78524f6fc..a17e8917a 100644
--- a/src/client/qwaylanddisplay.cpp
+++ b/src/client/qwaylanddisplay.cpp
@@ -41,6 +41,7 @@
#include "qwaylandintegration_p.h"
#include "qwaylandwindow_p.h"
+#include "qwaylandsurface_p.h"
#include "qwaylandabstractdecoration_p.h"
#include "qwaylandscreen_p.h"
#include "qwaylandcursor_p.h"
@@ -51,7 +52,10 @@
#if QT_CONFIG(wayland_datadevice)
#include "qwaylanddatadevicemanager_p.h"
#include "qwaylanddatadevice_p.h"
-#endif
+#endif // QT_CONFIG(wayland_datadevice)
+#if QT_CONFIG(wayland_client_primary_selection)
+#include "qwaylandprimaryselectionv1_p.h"
+#endif // QT_CONFIG(wayland_client_primary_selection)
#if QT_CONFIG(cursor)
#include <wayland-cursor.h>
#endif
@@ -68,6 +72,7 @@
#include "qwaylandqtkey_p.h"
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
+#include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h>
#include <QtCore/private/qcore_unix_p.h>
@@ -109,7 +114,7 @@ struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
return nullptr;
}
- return mSubCompositor->get_subsurface(window->object(), parent->object());
+ return mSubCompositor->get_subsurface(window->wlSurface(), parent->wlSurface());
}
QWaylandShellIntegration *QWaylandDisplay::shellIntegration() const
@@ -162,13 +167,11 @@ QWaylandDisplay::~QWaylandDisplay(void)
if (mSyncCallback)
wl_callback_destroy(mSyncCallback);
- qDeleteAll(mInputDevices);
- mInputDevices.clear();
+ qDeleteAll(qExchange(mInputDevices, {}));
- foreach (QWaylandScreen *screen, mScreens) {
+ for (QWaylandScreen *screen : qExchange(mScreens, {})) {
QWindowSystemInterface::handleScreenRemoved(screen);
}
- mScreens.clear();
qDeleteAll(mWaitingScreens);
#if QT_CONFIG(wayland_datadevice)
@@ -307,6 +310,10 @@ 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));
+#if QT_CONFIG(wayland_client_primary_selection)
+ } else if (interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) {
+ mPrimarySelectionManager.reset(new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1));
+#endif
} else if (interface == QStringLiteral("zwp_text_input_manager_v2") && !mClientSideInputContextRequested) {
mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1));
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
@@ -329,7 +336,8 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
mGlobals.append(RegistryGlobal(id, interface, version, registry));
- foreach (Listener l, mRegistryListeners)
+ const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification
+ for (Listener l : copy)
(*l.listener)(l.data, registry, id, interface, version);
}
@@ -347,7 +355,7 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
}
}
- foreach (QWaylandScreen *screen, mScreens) {
+ for (QWaylandScreen *screen : qAsConst(mScreens)) {
if (screen->outputId() == id) {
mScreens.removeOne(screen);
QWindowSystemInterface::handleScreenRemoved(screen);
@@ -367,9 +375,9 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
}
}
-bool QWaylandDisplay::hasRegistryGlobal(const QString &interfaceName)
+bool QWaylandDisplay::hasRegistryGlobal(QStringView interfaceName) const
{
- Q_FOREACH (const RegistryGlobal &global, mGlobals)
+ for (const RegistryGlobal &global : mGlobals)
if (global.interface == interfaceName)
return true;
diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h
index 7cfbc19b8..14bb77198 100644
--- a/src/client/qwaylanddisplay_p.h
+++ b/src/client/qwaylanddisplay_p.h
@@ -93,11 +93,15 @@ class QWaylandScreen;
class QWaylandClientBufferIntegration;
class QWaylandWindowManagerIntegration;
class QWaylandDataDeviceManager;
+#if QT_CONFIG(wayland_client_primary_selection)
+class QWaylandPrimarySelectionDeviceManagerV1;
+#endif
class QWaylandTouchExtension;
class QWaylandQtKeyExtension;
class QWaylandWindow;
class QWaylandIntegration;
class QWaylandHardwareIntegration;
+class QWaylandSurface;
class QWaylandShellIntegration;
class QWaylandCursor;
class QWaylandCursorTheme;
@@ -149,6 +153,9 @@ public:
#if QT_CONFIG(wayland_datadevice)
QWaylandDataDeviceManager *dndSelectionHandler() const { return mDndSelectionHandler.data(); }
#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+ QWaylandPrimarySelectionDeviceManagerV1 *primarySelectionManager() const { return mPrimarySelectionManager.data(); }
+#endif
QtWayland::qt_surface_extension *windowExtension() const { return mWindowExtension.data(); }
QWaylandTouchExtension *touchExtension() const { return mTouchExtension.data(); }
QtWayland::zwp_text_input_manager_v2 *textInputManager() const { return mTextInputManager.data(); }
@@ -166,7 +173,7 @@ public:
: id(id_), interface(interface_), version(version_), registry(registry_) { }
};
QList<RegistryGlobal> globals() const { return mGlobals; }
- bool hasRegistryGlobal(const QString &interfaceName);
+ bool hasRegistryGlobal(QStringView interfaceName) const;
/* wl_registry_add_listener does not add but rather sets a listener, so this function is used
* to enable many listeners at once. */
@@ -236,6 +243,9 @@ private:
QScopedPointer<QWaylandTouchExtension> mTouchExtension;
QScopedPointer<QWaylandQtKeyExtension> mQtKeyExtension;
QScopedPointer<QWaylandWindowManagerIntegration> mWindowManagerIntegration;
+#if QT_CONFIG(wayland_client_primary_selection)
+ QScopedPointer<QWaylandPrimarySelectionDeviceManagerV1> mPrimarySelectionManager;
+#endif
QScopedPointer<QtWayland::zwp_text_input_manager_v2> mTextInputManager;
QScopedPointer<QWaylandHardwareIntegration> mHardwareIntegration;
QScopedPointer<QtWayland::zxdg_output_manager_v1> mXdgOutputManager;
diff --git a/src/client/qwaylandextendedsurface.cpp b/src/client/qwaylandextendedsurface.cpp
index c5db6d7ba..a7836e292 100644
--- a/src/client/qwaylandextendedsurface.cpp
+++ b/src/client/qwaylandextendedsurface.cpp
@@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
QWaylandExtendedSurface::QWaylandExtendedSurface(QWaylandWindow *window)
- : QtWayland::qt_extended_surface(window->display()->windowExtension()->get_extended_surface(window->object()))
+ : QtWayland::qt_extended_surface(window->display()->windowExtension()->get_extended_surface(window->wlSurface()))
, m_window(window)
{
}
diff --git a/src/client/qwaylandinputcontext.cpp b/src/client/qwaylandinputcontext.cpp
index 0f27f551d..e9afe05ed 100644
--- a/src/client/qwaylandinputcontext.cpp
+++ b/src/client/qwaylandinputcontext.cpp
@@ -120,7 +120,7 @@ void QWaylandTextInput::updateState(Qt::InputMethodQueries queries, uint32_t fla
return;
auto *window = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle());
- auto *surface = window->object();
+ auto *surface = window->wlSurface();
if (!surface || (surface != m_surface))
return;
@@ -224,11 +224,11 @@ void QWaylandTextInput::zwp_text_input_v2_leave(uint32_t serial, ::wl_surface *s
void QWaylandTextInput::zwp_text_input_v2_modifiers_map(wl_array *map)
{
- QList<QByteArray> modifiersMap = QByteArray::fromRawData(static_cast<const char*>(map->data), map->size).split('\0');
+ const QList<QByteArray> modifiersMap = QByteArray::fromRawData(static_cast<const char*>(map->data), map->size).split('\0');
m_modifiersMap.clear();
- Q_FOREACH (const QByteArray &modifier, modifiersMap) {
+ for (const QByteArray &modifier : modifiersMap) {
if (modifier == "Shift")
m_modifiersMap.append(Qt::ShiftModifier);
else if (modifier == "Control")
@@ -431,7 +431,7 @@ static ::wl_surface *surfaceForWindow(QWindow *window)
return nullptr;
auto *waylandWindow = static_cast<QWaylandWindow *>(window->handle());
- return waylandWindow->wl_surface::object();
+ return waylandWindow->wlSurface();
}
void QWaylandInputContext::update(Qt::InputMethodQueries queries)
@@ -537,7 +537,7 @@ void QWaylandInputContext::setFocusObject(QObject *)
if (mCurrentWindow && mCurrentWindow->handle()) {
if (mCurrentWindow.data() != window || !inputMethodAccepted()) {
- struct ::wl_surface *surface = static_cast<QWaylandWindow *>(mCurrentWindow->handle())->object();
+ auto *surface = static_cast<QWaylandWindow *>(mCurrentWindow->handle())->wlSurface();
if (surface)
textInput()->disable(surface);
mCurrentWindow.clear();
@@ -546,7 +546,7 @@ void QWaylandInputContext::setFocusObject(QObject *)
if (window && window->handle() && inputMethodAccepted()) {
if (mCurrentWindow.data() != window) {
- struct ::wl_surface *surface = static_cast<QWaylandWindow *>(window->handle())->object();
+ auto *surface = static_cast<QWaylandWindow *>(window->handle())->wlSurface();
if (surface) {
textInput()->enable(surface);
mCurrentWindow = window;
diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp
index 8f3df8e4d..a9da452dc 100644
--- a/src/client/qwaylandinputdevice.cpp
+++ b/src/client/qwaylandinputdevice.cpp
@@ -41,11 +41,15 @@
#include "qwaylandintegration_p.h"
#include "qwaylandwindow_p.h"
+#include "qwaylandsurface_p.h"
#include "qwaylandbuffer_p.h"
#if QT_CONFIG(wayland_datadevice)
#include "qwaylanddatadevice_p.h"
#include "qwaylanddatadevicemanager_p.h"
#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+#include "qwaylandprimaryselectionv1_p.h"
+#endif
#include "qwaylandtouch_p.h"
#include "qwaylandscreen_p.h"
#include "qwaylandcursor_p.h"
@@ -73,6 +77,8 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
+Q_LOGGING_CATEGORY(lcQpaWaylandInput, "qt.qpa.wayland.input");
+
QWaylandInputDevice::Keyboard::Keyboard(QWaylandInputDevice *p)
: mParent(p)
{
@@ -147,25 +153,45 @@ QWaylandInputDevice::Pointer::~Pointer()
wl_pointer_destroy(object());
}
+QWaylandWindow *QWaylandInputDevice::Pointer::focusWindow() const
+{
+ return mFocus ? mFocus->waylandWindow() : nullptr;
+}
+
#if QT_CONFIG(cursor)
-class CursorSurface : public QObject, public QtWayland::wl_surface
+class WlCallback : public QtWayland::wl_callback {
+public:
+ explicit WlCallback(::wl_callback *callback, std::function<void(uint32_t)> fn, bool autoDelete = false)
+ : QtWayland::wl_callback(callback)
+ , m_fn(fn)
+ , m_autoDelete(autoDelete)
+ {}
+ ~WlCallback() override { wl_callback_destroy(object()); }
+ bool done() const { return m_done; }
+ void callback_done(uint32_t callback_data) override {
+ m_done = true;
+ m_fn(callback_data);
+ if (m_autoDelete)
+ delete this;
+ }
+private:
+ bool m_done = false;
+ std::function<void(uint32_t)> m_fn;
+ bool m_autoDelete = false;
+};
+
+class CursorSurface : public QWaylandSurface
{
public:
explicit CursorSurface(QWaylandInputDevice::Pointer *pointer, QWaylandDisplay *display)
- : m_pointer(pointer)
+ : QWaylandSurface(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();
- });
+ connect(this, &QWaylandSurface::screensChanged,
+ m_pointer, &QWaylandInputDevice::Pointer::updateCursor);
}
void hide()
@@ -177,7 +203,7 @@ public:
}
// Size and hotspot are in surface coordinates
- void update(wl_buffer *buffer, const QPoint &hotspot, const QSize &size, int bufferScale)
+ void update(wl_buffer *buffer, const QPoint &hotspot, const QSize &size, int bufferScale, bool animated = false)
{
// Calling code needs to ensure buffer scale is supported if != 1
Q_ASSERT(bufferScale == 1 || m_version >= 3);
@@ -194,6 +220,13 @@ public:
attach(buffer, 0, 0);
damage(0, 0, size.width(), size.height());
+ m_frameCallback.reset();
+ if (animated) {
+ m_frameCallback.reset(new WlCallback(frame(), [this](uint32_t time){
+ Q_UNUSED(time);
+ m_pointer->updateCursor();
+ }));
+ }
commit();
}
@@ -205,38 +238,12 @@ public:
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:
+ QScopedPointer<WlCallback> m_frameCallback;
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
@@ -318,12 +325,14 @@ void QWaylandInputDevice::Pointer::updateCursor()
return;
// Set from shape using theme
- if (struct ::wl_cursor_image *image = mCursor.theme->cursorImage(shape)) {
+ uint time = seat()->mCursor.animationTimer.elapsed();
+ if (struct ::wl_cursor_image *image = mCursor.theme->cursorImage(shape, time)) {
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);
+ bool animated = image->delay > 0;
+ getOrCreateCursorSurface()->update(buffer, hotspot, size, bufferScale, animated);
return;
}
@@ -353,10 +362,10 @@ QWaylandInputDevice::Touch::~Touch()
}
QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version, uint32_t id)
- : QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 4))
+ : QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 5))
, mQDisplay(display)
, mDisplay(display->wl_display())
- , mVersion(qMin(version, 4))
+ , mVersion(qMin(version, 5))
{
#if QT_CONFIG(wayland_datadevice)
if (mQDisplay->dndSelectionHandler()) {
@@ -364,6 +373,12 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version,
}
#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+ // TODO: Could probably decouple this more if there was a signal for new seat added
+ if (auto *psm = mQDisplay->primarySelectionManager())
+ setPrimarySelectionDevice(psm->createDevice(this));
+#endif
+
if (mQDisplay->textInputManager())
mTextInput.reset(new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat())));
@@ -427,6 +442,21 @@ QWaylandInputDevice::Touch *QWaylandInputDevice::createTouch(QWaylandInputDevice
return new Touch(device);
}
+QWaylandInputDevice::Keyboard *QWaylandInputDevice::keyboard() const
+{
+ return mKeyboard;
+}
+
+QWaylandInputDevice::Pointer *QWaylandInputDevice::pointer() const
+{
+ return mPointer;
+}
+
+QWaylandInputDevice::Touch *QWaylandInputDevice::touch() const
+{
+ return mTouch;
+}
+
void QWaylandInputDevice::handleEndDrag()
{
if (mTouch)
@@ -447,6 +477,18 @@ QWaylandDataDevice *QWaylandInputDevice::dataDevice() const
}
#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+void QWaylandInputDevice::setPrimarySelectionDevice(QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice)
+{
+ mPrimarySelectionDevice.reset(primarySelectionDevice);
+}
+
+QWaylandPrimarySelectionDeviceV1 *QWaylandInputDevice::primarySelectionDevice() const
+{
+ return mPrimarySelectionDevice.data();
+}
+#endif
+
void QWaylandInputDevice::setTextInput(QWaylandTextInput *textInput)
{
mTextInput.reset(textInput);
@@ -465,7 +507,7 @@ void QWaylandInputDevice::removeMouseButtonFromState(Qt::MouseButton button)
QWaylandWindow *QWaylandInputDevice::pointerFocus() const
{
- return mPointer ? mPointer->mFocus : nullptr;
+ return mPointer ? mPointer->focusWindow() : nullptr;
}
QWaylandWindow *QWaylandInputDevice::keyboardFocus() const
@@ -524,6 +566,7 @@ void QWaylandInputDevice::setCursor(const QCursor *cursor, const QSharedPointer<
mCursor.shape = cursor ? cursor->shape() : Qt::ArrowCursor;
mCursor.hotspot = cursor ? cursor->hotSpot() : QPoint();
mCursor.fallbackOutputScale = fallbackOutputScale;
+ mCursor.animationTimer.start();
if (mCursor.shape == Qt::BitmapCursor) {
mCursor.bitmapBuffer = cachedBuffer ? cachedBuffer : QWaylandCursor::cursorBitmapBuffer(mQDisplay, cursor);
@@ -550,8 +593,9 @@ void QWaylandInputDevice::setCursor(const QCursor *cursor, const QSharedPointer<
class EnterEvent : public QWaylandPointerEvent
{
public:
- EnterEvent(const QPointF &l, const QPointF &g)
- : QWaylandPointerEvent(QWaylandPointerEvent::Enter, 0, l, g, nullptr, Qt::NoModifier)
+ EnterEvent(QWaylandWindow *surface, const QPointF &local, const QPointF &global)
+ : QWaylandPointerEvent(QWaylandPointerEvent::Enter, Qt::NoScrollPhase, surface, 0,
+ local, global, nullptr, Qt::NoModifier)
{}
};
@@ -562,6 +606,7 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf
return;
QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
+
if (!window)
return; // Ignore foreign surfaces
@@ -571,9 +616,8 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf
<< "attempting to work around it by invalidating the current focus";
invalidateFocus();
}
-
- mFocus = window;
- connect(mFocus.data(), &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed);
+ mFocus = window->waylandSurface();
+ connect(mFocus.data(), &QObject::destroyed, this, &Pointer::handleFocusDestroyed);
mSurfacePos = QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy));
mGlobalPos = window->window()->mapToGlobal(mSurfacePos.toPoint());
@@ -587,12 +631,19 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf
#endif
QWaylandWindow *grab = QWaylandWindow::mouseGrab();
- if (!grab) {
- EnterEvent evt(mSurfacePos, mGlobalPos);
- window->handleMouse(mParent, evt);
- }
+ if (!grab)
+ setFrameEvent(new EnterEvent(window, mSurfacePos, mGlobalPos));
}
+class LeaveEvent : public QWaylandPointerEvent
+{
+public:
+ LeaveEvent(QWaylandWindow *surface, const QPointF &localPos, const QPointF &globalPos)
+ : QWaylandPointerEvent(QWaylandPointerEvent::Leave, Qt::NoScrollPhase, surface, 0,
+ localPos, globalPos, nullptr, Qt::NoModifier)
+ {}
+};
+
void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surface *surface)
{
// The event may arrive after destroying the window, indicated by
@@ -604,10 +655,8 @@ void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surfac
if (!window)
return; // Ignore foreign surfaces
- if (!QWaylandWindow::mouseGrab()) {
- QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
- window->handleMouseLeave(mParent);
- }
+ if (!QWaylandWindow::mouseGrab())
+ setFrameEvent(new LeaveEvent(window, mSurfacePos, mGlobalPos));
invalidateFocus();
mButtons = Qt::NoButton;
@@ -618,15 +667,17 @@ void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surfac
class MotionEvent : public QWaylandPointerEvent
{
public:
- MotionEvent(ulong t, const QPointF &l, const QPointF &g, Qt::MouseButtons b, Qt::KeyboardModifiers m)
- : QWaylandPointerEvent(QWaylandPointerEvent::Motion, t, l, g, b, m)
+ MotionEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
+ const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
+ : QWaylandPointerEvent(QWaylandPointerEvent::Motion, Qt::NoScrollPhase, surface,
+ timestamp, localPos, globalPos, buttons, modifiers)
{
}
};
void QWaylandInputDevice::Pointer::pointer_motion(uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y)
{
- QWaylandWindow *window = mFocus;
+ QWaylandWindow *window = focusWindow();
if (!window) {
// We destroyed the pointer 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.
@@ -648,18 +699,37 @@ void QWaylandInputDevice::Pointer::pointer_motion(uint32_t time, wl_fixed_t surf
// so we just set it outside of the window boundaries.
pos = QPointF(-1, -1);
global = grab->window()->mapToGlobal(pos.toPoint());
- MotionEvent e(time, pos, global, mButtons, mParent->modifiers());
- grab->handleMouse(mParent, e);
- } else {
- MotionEvent e(time, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers());
- window->handleMouse(mParent, e);
+ window = grab;
}
+ setFrameEvent(new MotionEvent(window, time, pos, global, mButtons, mParent->modifiers()));
}
+class PressEvent : public QWaylandPointerEvent
+{
+public:
+ PressEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
+ const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
+ : QWaylandPointerEvent(QWaylandPointerEvent::Press, Qt::NoScrollPhase, surface,
+ timestamp, localPos, globalPos, buttons, modifiers)
+ {
+ }
+};
+
+class ReleaseEvent : public QWaylandPointerEvent
+{
+public:
+ ReleaseEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
+ const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
+ : QWaylandPointerEvent(QWaylandPointerEvent::Release, Qt::NoScrollPhase, surface,
+ timestamp, localPos, globalPos, buttons, modifiers)
+ {
+ }
+};
+
void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time,
uint32_t button, uint32_t state)
{
- QWaylandWindow *window = mFocus;
+ QWaylandWindow *window = focusWindow();
if (!window) {
// We destroyed the pointer 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.
@@ -701,66 +771,300 @@ void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time
mParent->mQDisplay->setLastInputDevice(mParent, serial, window);
QWaylandWindow *grab = QWaylandWindow::mouseGrab();
- if (grab && grab != mFocus) {
- QPointF pos = QPointF(-1, -1);
- QPointF global = grab->window()->mapToGlobal(pos.toPoint());
- MotionEvent e(time, pos, global, mButtons, mParent->modifiers());
- grab->handleMouse(mParent, e);
- } else if (window) {
- MotionEvent e(time, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers());
- window->handleMouse(mParent, e);
+
+ QPointF pos = mSurfacePos;
+ QPointF global = mGlobalPos;
+ if (grab && grab != focusWindow()) {
+ pos = QPointF(-1, -1);
+ global = grab->window()->mapToGlobal(pos.toPoint());
+
+ window = grab;
}
+
+ if (state)
+ setFrameEvent(new PressEvent(window, time, pos, global, mButtons, mParent->modifiers()));
+ else
+ setFrameEvent(new ReleaseEvent(window, time, pos, global, mButtons, mParent->modifiers()));
}
void QWaylandInputDevice::Pointer::invalidateFocus()
{
- disconnect(mFocus.data(), &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed);
- mFocus = nullptr;
+ if (mFocus) {
+ disconnect(mFocus.data(), &QObject::destroyed, this, &Pointer::handleFocusDestroyed);
+ mFocus = nullptr;
+ }
mEnterSerial = 0;
}
void QWaylandInputDevice::Pointer::releaseButtons()
{
mButtons = Qt::NoButton;
- MotionEvent e(mParent->mTime, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers());
- if (mFocus)
- mFocus->handleMouse(mParent, e);
+
+ if (auto *window = focusWindow()) {
+ MotionEvent e(focusWindow(), mParent->mTime, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers());
+ window->handleMouse(mParent, e);
+ }
}
class WheelEvent : public QWaylandPointerEvent
{
public:
- WheelEvent(ulong t, const QPointF &l, const QPointF &g, const QPoint &pd, const QPoint &ad, Qt::KeyboardModifiers m)
- : QWaylandPointerEvent(QWaylandPointerEvent::Wheel, t, l, g, pd, ad, m)
+ WheelEvent(QWaylandWindow *surface, Qt::ScrollPhase phase, ulong timestamp, const QPointF &local,
+ const QPointF &global, const QPoint &pixelDelta, const QPoint &angleDelta,
+ Qt::MouseEventSource source, Qt::KeyboardModifiers modifiers)
+ : QWaylandPointerEvent(QWaylandPointerEvent::Wheel, phase, surface, timestamp,
+ local, global, pixelDelta, angleDelta, source, modifiers)
{
}
};
void QWaylandInputDevice::Pointer::pointer_axis(uint32_t time, uint32_t axis, int32_t value)
{
- QWaylandWindow *window = mFocus;
- if (!window) {
+ if (!focusWindow()) {
// We destroyed the pointer 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;
}
- QPoint pixelDelta;
- QPoint angleDelta;
+ // Get the delta and convert it into the expected range
+ switch (axis) {
+ case WL_POINTER_AXIS_VERTICAL_SCROLL:
+ mFrameData.delta.ry() += wl_fixed_to_double(value);
+ qCDebug(lcQpaWaylandInput) << "wl_pointer.axis vertical:" << mFrameData.delta.y();
+ break;
+ case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
+ mFrameData.delta.rx() += wl_fixed_to_double(value);
+ qCDebug(lcQpaWaylandInput) << "wl_pointer.axis horizontal:" << mFrameData.delta.x();
+ break;
+ default:
+ //TODO: is this really needed?
+ qCWarning(lcQpaWaylandInput) << "wl_pointer.axis: Unknown axis:" << axis;
+ return;
+ }
+
+ mParent->mTime = time;
- //normalize value and inverse axis
- int valueDelta = wl_fixed_to_int(value) * -12;
+ if (mParent->mVersion < WL_POINTER_FRAME_SINCE_VERSION) {
+ qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
+ flushFrameEvent();
+ }
+}
- if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
- pixelDelta = QPoint();
- angleDelta.setX(valueDelta);
- } else {
- pixelDelta = QPoint();
- angleDelta.setY(valueDelta);
+void QWaylandInputDevice::Pointer::pointer_frame()
+{
+ flushFrameEvent();
+}
+
+void QWaylandInputDevice::Pointer::pointer_axis_source(uint32_t source)
+{
+ switch (source) {
+ case axis_source_wheel:
+ qCDebug(lcQpaWaylandInput) << "Axis source wheel";
+ break;
+ case axis_source_finger:
+ qCDebug(lcQpaWaylandInput) << "Axis source finger";
+ break;
+ case axis_source_continuous:
+ qCDebug(lcQpaWaylandInput) << "Axis source continuous";
+ break;
+ }
+ mFrameData.axisSource = axis_source(source);
+}
+
+void QWaylandInputDevice::Pointer::pointer_axis_stop(uint32_t time, uint32_t axis)
+{
+ if (!focusWindow())
+ return;
+
+ mParent->mTime = time;
+ switch (axis) {
+ case axis_vertical_scroll:
+ qCDebug(lcQpaWaylandInput) << "Received vertical wl_pointer.axis_stop";
+ mFrameData.delta.setY(0); //TODO: what's the point of doing this?
+ break;
+ case axis_horizontal_scroll:
+ qCDebug(lcQpaWaylandInput) << "Received horizontal wl_pointer.axis_stop";
+ mFrameData.delta.setX(0);
+ break;
+ default:
+ qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_stop: Unknown axis: " << axis
+ << "This is most likely a compositor bug";
+ return;
+ }
+
+ // May receive axis_stop for events we haven't sent a ScrollBegin for because
+ // most axis_sources do not mandate an axis_stop event to be sent.
+ if (!mScrollBeginSent) {
+ // TODO: For now, we just ignore these events, but we could perhaps take this as an
+ // indication that this compositor will in fact send axis_stop events for these sources
+ // and send a ScrollBegin the next time an axis_source event with this type is encountered.
+ return;
+ }
+
+ QWaylandWindow *target = QWaylandWindow::mouseGrab();
+ if (!target)
+ target = focusWindow();
+ Qt::KeyboardModifiers mods = mParent->modifiers();
+ WheelEvent wheelEvent(focusWindow(), Qt::ScrollEnd, mParent->mTime, mSurfacePos, mGlobalPos,
+ QPoint(), QPoint(), Qt::MouseEventNotSynthesized, mods);
+ target->handleMouse(mParent, wheelEvent);
+ mScrollBeginSent = false;
+ mScrollDeltaRemainder = QPointF();
+}
+
+void QWaylandInputDevice::Pointer::pointer_axis_discrete(uint32_t axis, int32_t value)
+{
+ if (!focusWindow())
+ return;
+
+ switch (axis) {
+ case axis_vertical_scroll:
+ qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete vertical:" << value;
+ mFrameData.discreteDelta.ry() += value;
+ break;
+ case axis_horizontal_scroll:
+ qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete horizontal:" << value;
+ mFrameData.discreteDelta.rx() += value;
+ break;
+ default:
+ //TODO: is this really needed?
+ qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_discrete: Unknown axis:" << axis;
+ return;
+ }
+}
+
+void QWaylandInputDevice::Pointer::setFrameEvent(QWaylandPointerEvent *event)
+{
+ qCDebug(lcQpaWaylandInput) << "Setting frame event " << event->type;
+ if (mFrameData.event && mFrameData.event->type != event->type) {
+ qCDebug(lcQpaWaylandInput) << "Flushing; previous was " << mFrameData.event->type;
+ flushFrameEvent();
}
- WheelEvent e(time, mSurfacePos, mGlobalPos, pixelDelta, angleDelta, mParent->modifiers());
- window->handleMouse(mParent, e);
+ mFrameData.event = event;
+
+ if (mParent->mVersion < WL_POINTER_FRAME_SINCE_VERSION) {
+ qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
+ flushFrameEvent();
+ }
+}
+
+void QWaylandInputDevice::Pointer::FrameData::resetScrollData()
+{
+ discreteDelta = QPoint();
+ delta = QPointF();
+ axisSource = axis_source_wheel;
+}
+
+bool QWaylandInputDevice::Pointer::FrameData::hasPixelDelta() const
+{
+ switch (axisSource) {
+ case axis_source_wheel_tilt: // sideways tilt of the wheel
+ case axis_source_wheel:
+ // In the case of wheel events, a pixel delta doesn't really make sense,
+ // and will make Qt think this is a continuous scroll event when it isn't,
+ // so just ignore it.
+ return false;
+ case axis_source_finger:
+ case axis_source_continuous:
+ return !delta.isNull();
+ }
+}
+
+QPoint QWaylandInputDevice::Pointer::FrameData::pixelDeltaAndError(QPointF *accumulatedError) const
+{
+ if (!hasPixelDelta())
+ return QPoint();
+
+ Q_ASSERT(accumulatedError);
+ // Add accumulated rounding error before rounding again
+ QPoint pixelDelta = (delta + *accumulatedError).toPoint();
+ *accumulatedError += delta - pixelDelta;
+ Q_ASSERT(qAbs(accumulatedError->x()) < 1.0);
+ Q_ASSERT(qAbs(accumulatedError->y()) < 1.0);
+ return pixelDelta;
+}
+
+QPoint QWaylandInputDevice::Pointer::FrameData::angleDelta() const
+{
+ if (discreteDelta.isNull()) {
+ // If we didn't get any discrete events, then we need to fall back to
+ // the continuous information.
+ return (delta * -12).toPoint(); //TODO: why multiply by 12?
+ }
+
+ // The angle delta is in eights of degrees, and our docs says most mice have
+ // 1 click = 15 degrees. It's also in the opposite direction of surface space.
+ return -discreteDelta * 15 * 8;
+}
+
+Qt::MouseEventSource QWaylandInputDevice::Pointer::FrameData::wheelEventSource() const
+{
+ switch (axisSource) {
+ case axis_source_wheel_tilt: // sideways tilt of the wheel
+ case axis_source_wheel:
+ return Qt::MouseEventNotSynthesized;
+ case axis_source_finger:
+ case axis_source_continuous:
+ default: // Whatever other sources might be added are probably not mouse wheels
+ return Qt::MouseEventSynthesizedBySystem;
+ }
+}
+
+void QWaylandInputDevice::Pointer::flushScrollEvent()
+{
+ QPoint angleDelta = mFrameData.angleDelta();
+
+ // Angle delta is required for Qt wheel events, so don't try to send events if it's zero
+ if (!angleDelta.isNull()) {
+ QWaylandWindow *target = QWaylandWindow::mouseGrab();
+ if (!target)
+ target = focusWindow();
+
+ if (isDefinitelyTerminated(mFrameData.axisSource) && !mScrollBeginSent) {
+ qCDebug(lcQpaWaylandInput) << "Flushing scroll event sending ScrollBegin";
+ target->handleMouse(mParent, WheelEvent(focusWindow(), Qt::ScrollBegin, mParent->mTime,
+ mSurfacePos, mGlobalPos, QPoint(), QPoint(),
+ Qt::MouseEventNotSynthesized,
+ mParent->modifiers()));
+ mScrollBeginSent = true;
+ mScrollDeltaRemainder = QPointF();
+ }
+
+ Qt::ScrollPhase phase = mScrollBeginSent ? Qt::ScrollUpdate : Qt::NoScrollPhase;
+ QPoint pixelDelta = mFrameData.pixelDeltaAndError(&mScrollDeltaRemainder);
+ Qt::MouseEventSource source = mFrameData.wheelEventSource();
+
+ qCDebug(lcQpaWaylandInput) << "Flushing scroll event" << phase << pixelDelta << angleDelta;
+ target->handleMouse(mParent, WheelEvent(focusWindow(), phase, mParent->mTime, mSurfacePos, mGlobalPos,
+ pixelDelta, angleDelta, source, mParent->modifiers()));
+ }
+
+ mFrameData.resetScrollData();
+}
+
+void QWaylandInputDevice::Pointer::flushFrameEvent()
+{
+ if (auto *event = mFrameData.event) {
+ if (auto window = event->surface) {
+ window->handleMouse(mParent, *event);
+ } else if (mFrameData.event->type == QWaylandPointerEvent::Type::Release) {
+ // If the window has been destroyed, we still need to report an up event, but it can't
+ // be handled by the destroyed window (obviously), so send the event here instead.
+ QWindowSystemInterface::handleMouseEvent(nullptr, event->timestamp, event->local,
+ event->global, event->buttons, event->modifiers);
+ }
+ delete mFrameData.event;
+ mFrameData.event = nullptr;
+ }
+
+ //TODO: do modifiers get passed correctly here?
+ flushScrollEvent();
+}
+
+bool QWaylandInputDevice::Pointer::isDefinitelyTerminated(QtWayland::wl_pointer::axis_source source) const
+{
+ return source == axis_source_finger;
}
void QWaylandInputDevice::Keyboard::keyboard_keymap(uint32_t format, int32_t fd, uint32_t size)
@@ -854,7 +1158,18 @@ void QWaylandInputDevice::Keyboard::handleKey(ulong timestamp, QEvent::Type type
}
if (!filtered) {
- QWindowSystemInterface::handleExtendedKeyEvent(focusWindow()->window(), timestamp, type, key, modifiers,
+ auto window = focusWindow()->window();
+
+ if (type == QEvent::KeyPress && key == Qt::Key_Menu) {
+ auto cursor = window->screen()->handle()->cursor();
+ if (cursor) {
+ const QPoint globalPos = cursor->pos();
+ const QPoint pos = window->mapFromGlobal(globalPos);
+ QWindowSystemInterface::handleContextMenuEvent(window, false, pos, globalPos, modifiers);
+ }
+ }
+
+ QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp, type, key, modifiers,
nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorepeat, count);
}
}
@@ -927,7 +1242,7 @@ void QWaylandInputDevice::Keyboard::handleFocusDestroyed()
// 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);
+ Q_ASSERT(window->wlSurface() == mFocus);
handleFocusLost();
}
@@ -938,6 +1253,10 @@ void QWaylandInputDevice::Keyboard::handleFocusLost()
if (auto *dataDevice = mParent->dataDevice())
dataDevice->invalidateSelectionOffer();
#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+ if (auto *device = mParent->primarySelectionDevice())
+ device->invalidateSelectionOffer();
+#endif
mParent->mQDisplay->handleKeyboardFocusChanged(mParent);
mRepeatTimer.stop();
}
@@ -1046,7 +1365,7 @@ void QWaylandInputDevice::handleTouchPoint(int id, Qt::TouchPointState state, co
//is it possible that mTouchFocus is null;
if (!win && mPointer)
- win = mPointer->mFocus;
+ win = mPointer->focusWindow();
if (!win && mKeyboard)
win = mKeyboard->focusWindow();
if (!win || !win->window())
diff --git a/src/client/qwaylandinputdevice_p.h b/src/client/qwaylandinputdevice_p.h
index 143e11220..60d6f2c17 100644
--- a/src/client/qwaylandinputdevice_p.h
+++ b/src/client/qwaylandinputdevice_p.h
@@ -69,7 +69,8 @@
#endif
#include <QtCore/QDebug>
-#include <QPointer>
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QPointer>
#if QT_CONFIG(cursor)
struct wl_cursor_image;
@@ -77,11 +78,17 @@ struct wl_cursor_image;
QT_BEGIN_NAMESPACE
+namespace QtWayland {
+class zwp_primary_selection_device_v1;
+} //namespace QtWayland
+
namespace QtWaylandClient {
-class QWaylandWindow;
-class QWaylandDisplay;
class QWaylandDataDevice;
+class QWaylandDisplay;
+#if QT_CONFIG(wayland_client_primary_selection)
+class QWaylandPrimarySelectionDeviceV1;
+#endif
class QWaylandTextInput;
#if QT_CONFIG(cursor)
class QWaylandCursorTheme;
@@ -115,6 +122,11 @@ public:
QWaylandDataDevice *dataDevice() const;
#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+ void setPrimarySelectionDevice(QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice);
+ QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice() const;
+#endif
+
void setTextInput(QWaylandTextInput *textInput);
QWaylandTextInput *textInput() const;
@@ -136,6 +148,10 @@ public:
virtual Pointer *createPointer(QWaylandInputDevice *device);
virtual Touch *createTouch(QWaylandInputDevice *device);
+ Keyboard *keyboard() const;
+ Pointer *pointer() const;
+ Touch *touch() const;
+
private:
QWaylandDisplay *mQDisplay = nullptr;
struct wl_display *mDisplay = nullptr;
@@ -150,6 +166,7 @@ private:
Qt::CursorShape shape = Qt::ArrowCursor;
int fallbackOutputScale = 1;
QPoint hotspot;
+ QElapsedTimer animationTimer;
} mCursor;
#endif
@@ -157,6 +174,10 @@ private:
QWaylandDataDevice *mDataDevice = nullptr;
#endif
+#if QT_CONFIG(wayland_client_primary_selection)
+ QScopedPointer<QWaylandPrimarySelectionDeviceV1> mPrimarySelectionDevice;
+#endif
+
Keyboard *mKeyboard = nullptr;
Pointer *mPointer = nullptr;
Touch *mTouch = nullptr;
@@ -231,6 +252,8 @@ public:
Qt::KeyboardModifiers modifiers() const;
+ struct ::wl_keyboard *wl_keyboard() { return QtWayland::wl_keyboard::object(); }
+
private slots:
void handleFocusDestroyed();
void handleFocusLost();
@@ -256,6 +279,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Pointer : public QObject, pub
public:
explicit Pointer(QWaylandInputDevice *seat);
~Pointer() override;
+ QWaylandWindow *focusWindow() const;
#if QT_CONFIG(cursor)
QString cursorThemeName() const;
int cursorSize() const; // in surface coordinates
@@ -266,6 +290,8 @@ public:
#endif
QWaylandInputDevice *seat() const { return mParent; }
+ struct ::wl_pointer *wl_pointer() { return QtWayland::wl_pointer::object(); }
+
protected:
void pointer_enter(uint32_t serial, struct wl_surface *surface,
wl_fixed_t sx, wl_fixed_t sy) override;
@@ -277,6 +303,10 @@ protected:
void pointer_axis(uint32_t time,
uint32_t axis,
wl_fixed_t value) override;
+ void pointer_axis_source(uint32_t source) override;
+ void pointer_axis_stop(uint32_t time, uint32_t axis) override;
+ void pointer_axis_discrete(uint32_t axis, int32_t value) override;
+ void pointer_frame() override;
private slots:
void handleFocusDestroyed() { invalidateFocus(); }
@@ -288,7 +318,7 @@ public:
void releaseButtons();
QWaylandInputDevice *mParent = nullptr;
- QPointer<QWaylandWindow> mFocus;
+ QPointer<QWaylandSurface> mFocus;
uint32_t mEnterSerial = 0;
#if QT_CONFIG(cursor)
struct {
@@ -304,6 +334,30 @@ public:
wl_buffer *mCursorBuffer = nullptr;
Qt::CursorShape mCursorShape = Qt::BitmapCursor;
#endif
+
+ struct FrameData {
+ QWaylandPointerEvent *event = nullptr;
+
+ QPointF delta;
+ QPoint discreteDelta;
+ axis_source axisSource = axis_source_wheel;
+
+ void resetScrollData();
+ bool hasPixelDelta() const;
+ QPoint pixelDeltaAndError(QPointF *accumulatedError) const;
+ QPoint pixelDelta() const { return hasPixelDelta() ? delta.toPoint() : QPoint(); }
+ QPoint angleDelta() const;
+ Qt::MouseEventSource wheelEventSource() const;
+ } mFrameData;
+
+ bool mScrollBeginSent = false;
+ QPointF mScrollDeltaRemainder;
+
+ void setFrameEvent(QWaylandPointerEvent *event);
+ void flushScrollEvent();
+ void flushFrameEvent();
+private: //TODO: should other methods be private as well?
+ bool isDefinitelyTerminated(axis_source source) const;
};
class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Touch : public QtWayland::wl_touch
@@ -331,6 +385,8 @@ public:
bool allTouchPointsReleased();
void releasePoints();
+ struct ::wl_touch *wl_touch() { return QtWayland::wl_touch::object(); }
+
QWaylandInputDevice *mParent = nullptr;
QPointer<QWaylandWindow> mFocus;
QList<QWindowSystemInterface::TouchPoint> mPendingTouchPoints;
@@ -338,38 +394,58 @@ public:
class QWaylandPointerEvent
{
+ Q_GADGET
public:
enum Type {
Enter,
+ Leave,
Motion,
+ Press,
+ Release,
Wheel
};
- inline QWaylandPointerEvent(Type t, ulong ts, const QPointF &l, const QPointF &g, Qt::MouseButtons b, Qt::KeyboardModifiers m)
- : type(t)
- , timestamp(ts)
- , local(l)
- , global(g)
- , buttons(b)
- , modifiers(m)
+ Q_ENUM(Type)
+
+ inline QWaylandPointerEvent(Type type, Qt::ScrollPhase phase, QWaylandWindow *surface,
+ ulong timestamp, const QPointF &localPos, const QPointF &globalPos,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
+ : type(type)
+ , phase(phase)
+ , timestamp(timestamp)
+ , local(localPos)
+ , global(globalPos)
+ , buttons(buttons)
+ , modifiers(modifiers)
+ , surface(surface)
{}
- inline QWaylandPointerEvent(Type t, ulong ts, const QPointF &l, const QPointF &g, const QPoint &pd, const QPoint &ad, Qt::KeyboardModifiers m)
- : type(t)
- , timestamp(ts)
- , local(l)
- , global(g)
- , modifiers(m)
- , pixelDelta(pd)
- , angleDelta(ad)
+ inline QWaylandPointerEvent(Type type, Qt::ScrollPhase phase, QWaylandWindow *surface,
+ ulong timestamp, const QPointF &local, const QPointF &global,
+ const QPoint &pixelDelta, const QPoint &angleDelta,
+ Qt::MouseEventSource source,
+ Qt::KeyboardModifiers modifiers)
+ : type(type)
+ , phase(phase)
+ , timestamp(timestamp)
+ , local(local)
+ , global(global)
+ , modifiers(modifiers)
+ , pixelDelta(pixelDelta)
+ , angleDelta(angleDelta)
+ , source(source)
+ , surface(surface)
{}
Type type;
- ulong timestamp;
+ Qt::ScrollPhase phase = Qt::NoScrollPhase;
+ ulong timestamp = 0;
QPointF local;
QPointF global;
Qt::MouseButtons buttons;
Qt::KeyboardModifiers modifiers;
QPoint pixelDelta;
QPoint angleDelta;
+ Qt::MouseEventSource source = Qt::MouseEventNotSynthesized;
+ QPointer<QWaylandWindow> surface;
};
}
diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp
index 85fcef43f..f6a80e18f 100644
--- a/src/client/qwaylandintegration.cpp
+++ b/src/client/qwaylandintegration.cpp
@@ -272,7 +272,7 @@ QPlatformAccessibility *QWaylandIntegration::accessibility() const
{
if (!mAccessibility) {
#ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE
- Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QXcbIntegration",
+ Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QWaylandIntegration",
"Initializing accessibility without event-dispatcher!");
mAccessibility.reset(new QSpiAccessibleBridge());
#else
@@ -352,7 +352,7 @@ void QWaylandIntegration::initializeClientBufferIntegration()
&& mDisplay->hardwareIntegration()->clientBufferIntegration() != QLatin1String("linux-dmabuf-unstable-v1")) {
targetKey = mDisplay->hardwareIntegration()->clientBufferIntegration();
} else {
- targetKey = QLatin1Literal("wayland-egl");
+ targetKey = QLatin1String("wayland-egl");
}
}
@@ -430,7 +430,7 @@ void QWaylandIntegration::initializeShellIntegration()
preferredShells << QLatin1String("wl-shell") << QLatin1String("ivi-shell");
}
- Q_FOREACH (QString preferredShell, preferredShells) {
+ for (const QString &preferredShell : qAsConst(preferredShells)) {
mShellIntegration.reset(createShellIntegration(preferredShell));
if (mShellIntegration) {
qCDebug(lcQpaWayland, "Using the '%s' shell integration", qPrintable(preferredShell));
diff --git a/src/client/qwaylandnativeinterface.cpp b/src/client/qwaylandnativeinterface.cpp
index 76acb526b..b4ecc0090 100644
--- a/src/client/qwaylandnativeinterface.cpp
+++ b/src/client/qwaylandnativeinterface.cpp
@@ -47,6 +47,7 @@
#include "qwaylanddisplay_p.h"
#include "qwaylandwindowmanagerintegration_p.h"
#include "qwaylandscreen_p.h"
+#include "qwaylandinputdevice_p.h"
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/QScreen>
#include <QtWaylandClient/private/qwaylandclientbufferintegration_p.h>
@@ -76,6 +77,27 @@ void *QWaylandNativeInterface::nativeResourceForIntegration(const QByteArray &re
if (lowerCaseResource == "egldisplay" && m_integration->clientBufferIntegration())
return m_integration->clientBufferIntegration()->nativeResource(QWaylandClientBufferIntegration::EglDisplay);
+ if (lowerCaseResource == "wl_seat")
+ return m_integration->display()->defaultInputDevice()->wl_seat();
+ if (lowerCaseResource == "wl_keyboard") {
+ auto *keyboard = m_integration->display()->defaultInputDevice()->keyboard();
+ if (keyboard)
+ return keyboard->wl_keyboard();
+ return nullptr;
+ }
+ if (lowerCaseResource == "wl_pointer") {
+ auto *pointer = m_integration->display()->defaultInputDevice()->pointer();
+ if (pointer)
+ return pointer->wl_pointer();
+ return nullptr;
+ }
+ if (lowerCaseResource == "wl_touch") {
+ auto *touch = m_integration->display()->defaultInputDevice()->touch();
+ if (touch)
+ return touch->wl_touch();
+ return nullptr;
+ }
+
return nullptr;
}
@@ -89,7 +111,7 @@ void *QWaylandNativeInterface::nativeResourceForWindow(const QByteArray &resourc
return const_cast<wl_compositor *>(m_integration->display()->wl_compositor());
if (lowerCaseResource == "surface") {
QWaylandWindow *w = static_cast<QWaylandWindow*>(window->handle());
- return w ? w->object() : nullptr;
+ return w ? w->wlSurface() : nullptr;
}
if (lowerCaseResource == "egldisplay" && m_integration->clientBufferIntegration())
diff --git a/src/client/qwaylandprimaryselectionv1.cpp b/src/client/qwaylandprimaryselectionv1.cpp
new file mode 100644
index 000000000..3ddf6dac3
--- /dev/null
+++ b/src/client/qwaylandprimaryselectionv1.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandprimaryselectionv1_p.h"
+#include "qwaylandinputdevice_p.h"
+#include "qwaylanddisplay_p.h"
+#include "qwaylandmimehelper_p.h"
+
+#include <QtGui/private/qguiapplication_p.h>
+
+#include <qpa/qplatformclipboard.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+QWaylandPrimarySelectionDeviceManagerV1::QWaylandPrimarySelectionDeviceManagerV1(QWaylandDisplay *display, uint id, uint version)
+ : zwp_primary_selection_device_manager_v1(display->wl_registry(), id, qMin(version, uint(1)))
+ , m_display(display)
+{
+ // Create devices for all seats.
+ // This only works if we get the global before all devices
+ const auto seats = m_display->inputDevices();
+ for (auto *seat : seats)
+ seat->setPrimarySelectionDevice(createDevice(seat));
+}
+
+QWaylandPrimarySelectionDeviceV1 *QWaylandPrimarySelectionDeviceManagerV1::createDevice(QWaylandInputDevice *seat)
+{
+ return new QWaylandPrimarySelectionDeviceV1(this, seat);
+}
+
+QWaylandPrimarySelectionOfferV1::QWaylandPrimarySelectionOfferV1(QWaylandDisplay *display, ::zwp_primary_selection_offer_v1 *offer)
+ : zwp_primary_selection_offer_v1(offer)
+ , m_display(display)
+ , m_mimeData(new QWaylandMimeData(this))
+{}
+
+void QWaylandPrimarySelectionOfferV1::startReceiving(const QString &mimeType, int fd)
+{
+ receive(mimeType, fd);
+ wl_display_flush(m_display->wl_display());
+}
+
+void QWaylandPrimarySelectionOfferV1::zwp_primary_selection_offer_v1_offer(const QString &mime_type)
+{
+ m_mimeData->appendFormat(mime_type);
+}
+
+QWaylandPrimarySelectionDeviceV1::QWaylandPrimarySelectionDeviceV1(
+ QWaylandPrimarySelectionDeviceManagerV1 *manager, QWaylandInputDevice *seat)
+ : QtWayland::zwp_primary_selection_device_v1(manager->get_device(seat->wl_seat()))
+ , m_display(manager->display())
+ , m_seat(seat)
+{
+}
+
+QWaylandPrimarySelectionDeviceV1::~QWaylandPrimarySelectionDeviceV1()
+{
+ destroy();
+}
+
+void QWaylandPrimarySelectionDeviceV1::setSelectionSource(QWaylandPrimarySelectionSourceV1 *source)
+{
+ if (source) {
+ connect(source, &QWaylandPrimarySelectionSourceV1::cancelled, this, [this]() {
+ m_selectionSource.reset();
+ QGuiApplicationPrivate::platformIntegration()->clipboard()->emitChanged(QClipboard::Selection);
+ });
+ }
+ set_selection(source ? source->object() : nullptr, m_seat->serial());
+ m_selectionSource.reset(source);
+}
+
+void QWaylandPrimarySelectionDeviceV1::zwp_primary_selection_device_v1_data_offer(zwp_primary_selection_offer_v1 *offer)
+{
+ new QWaylandPrimarySelectionOfferV1(m_display, offer);
+}
+
+void QWaylandPrimarySelectionDeviceV1::zwp_primary_selection_device_v1_selection(zwp_primary_selection_offer_v1 *id)
+{
+
+ if (id)
+ m_selectionOffer.reset(static_cast<QWaylandPrimarySelectionOfferV1 *>(zwp_primary_selection_offer_v1_get_user_data(id)));
+ else
+ m_selectionOffer.reset();
+
+ QGuiApplicationPrivate::platformIntegration()->clipboard()->emitChanged(QClipboard::Selection);
+}
+
+QWaylandPrimarySelectionSourceV1::QWaylandPrimarySelectionSourceV1(QWaylandPrimarySelectionDeviceManagerV1 *manager, QMimeData *mimeData)
+ : QtWayland::zwp_primary_selection_source_v1(manager->create_source())
+ , m_mimeData(mimeData)
+{
+ if (!mimeData)
+ return;
+ for (auto &format : mimeData->formats())
+ offer(format);
+}
+
+QWaylandPrimarySelectionSourceV1::~QWaylandPrimarySelectionSourceV1()
+{
+ destroy();
+}
+
+void QWaylandPrimarySelectionSourceV1::zwp_primary_selection_source_v1_send(const QString &mime_type, int32_t fd)
+{
+ QByteArray content = QWaylandMimeHelper::getByteArray(m_mimeData, mime_type);
+ if (!content.isEmpty()) {
+ // Create a sigpipe handler that does nothing, or clients may be forced to terminate
+ // if the pipe is closed in the other end.
+ struct sigaction action, oldAction;
+ action.sa_handler = SIG_IGN;
+ sigemptyset (&action.sa_mask);
+ action.sa_flags = 0;
+
+ sigaction(SIGPIPE, &action, &oldAction);
+ write(fd, content.constData(), size_t(content.size()));
+ sigaction(SIGPIPE, &oldAction, nullptr);
+ }
+ close(fd);
+}
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
diff --git a/src/client/qwaylandprimaryselectionv1_p.h b/src/client/qwaylandprimaryselectionv1_p.h
new file mode 100644
index 000000000..b165c51b8
--- /dev/null
+++ b/src/client/qwaylandprimaryselectionv1_p.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDPRIMARYSELECTIONV1_P_H
+#define QWAYLANDPRIMARYSELECTIONV1_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h>
+
+#include <QtWaylandClient/private/qtwaylandclientglobal_p.h>
+#include <QtWaylandClient/private/qwaylanddataoffer_p.h>
+
+#include <QtCore/QObject>
+
+QT_REQUIRE_CONFIG(wayland_client_primary_selection);
+
+QT_BEGIN_NAMESPACE
+
+class QMimeData;
+
+namespace QtWaylandClient {
+
+class QWaylandInputDevice;
+class QWaylandPrimarySelectionDeviceV1;
+
+class QWaylandPrimarySelectionDeviceManagerV1 : public QtWayland::zwp_primary_selection_device_manager_v1
+{
+public:
+ explicit QWaylandPrimarySelectionDeviceManagerV1(QWaylandDisplay *display, uint id, uint version);
+ QWaylandPrimarySelectionDeviceV1 *createDevice(QWaylandInputDevice *seat);
+ QWaylandDisplay *display() const { return m_display; }
+
+private:
+ QWaylandDisplay *m_display = nullptr;
+};
+
+class QWaylandPrimarySelectionOfferV1 : public QtWayland::zwp_primary_selection_offer_v1, public QWaylandAbstractDataOffer
+{
+public:
+ explicit QWaylandPrimarySelectionOfferV1(QWaylandDisplay *display, ::zwp_primary_selection_offer_v1 *offer);
+ ~QWaylandPrimarySelectionOfferV1() override { destroy(); }
+ void startReceiving(const QString &mimeType, int fd) override;
+ QMimeData *mimeData() override { return m_mimeData.data(); }
+
+protected:
+ void zwp_primary_selection_offer_v1_offer(const QString &mime_type) override;
+
+private:
+ QWaylandDisplay *m_display = nullptr;
+ QScopedPointer<QWaylandMimeData> m_mimeData;
+};
+
+class Q_WAYLAND_CLIENT_EXPORT QWaylandPrimarySelectionSourceV1 : public QObject, public QtWayland::zwp_primary_selection_source_v1
+{
+ Q_OBJECT
+public:
+ explicit QWaylandPrimarySelectionSourceV1(QWaylandPrimarySelectionDeviceManagerV1 *manager, QMimeData *mimeData);
+ ~QWaylandPrimarySelectionSourceV1() override;
+
+ QMimeData *mimeData() const { return m_mimeData; }
+
+signals:
+ void cancelled();
+
+protected:
+ void zwp_primary_selection_source_v1_send(const QString &mime_type, int32_t fd) override;
+ void zwp_primary_selection_source_v1_cancelled() override { emit cancelled(); }
+
+private:
+ QWaylandDisplay *m_display = nullptr;
+ QMimeData *m_mimeData = nullptr;
+};
+
+class QWaylandPrimarySelectionDeviceV1 : public QObject, public QtWayland::zwp_primary_selection_device_v1
+{
+ Q_OBJECT
+ QWaylandPrimarySelectionDeviceV1(QWaylandPrimarySelectionDeviceManagerV1 *manager, QWaylandInputDevice *seat);
+
+public:
+ ~QWaylandPrimarySelectionDeviceV1() override;
+ QWaylandPrimarySelectionOfferV1 *selectionOffer() const { return m_selectionOffer.data(); }
+ void invalidateSelectionOffer() { m_selectionOffer.reset(); }
+ QWaylandPrimarySelectionSourceV1 *selectionSource() const { return m_selectionSource.data(); }
+ void setSelectionSource(QWaylandPrimarySelectionSourceV1 *source);
+
+protected:
+ void zwp_primary_selection_device_v1_data_offer(struct ::zwp_primary_selection_offer_v1 *offer) override;
+ void zwp_primary_selection_device_v1_selection(struct ::zwp_primary_selection_offer_v1 *id) override;
+
+private:
+ QWaylandDisplay *m_display = nullptr;
+ QWaylandInputDevice *m_seat = nullptr;
+ QScopedPointer<QWaylandPrimarySelectionOfferV1> m_selectionOffer;
+ QScopedPointer<QWaylandPrimarySelectionSourceV1> m_selectionSource;
+ friend class QWaylandPrimarySelectionDeviceManagerV1;
+};
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDPRIMARYSELECTIONV1_P_H
diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp
index d116a807b..e70796832 100644
--- a/src/client/qwaylandscreen.cpp
+++ b/src/client/qwaylandscreen.cpp
@@ -175,7 +175,8 @@ QList<QPlatformScreen *> QWaylandScreen::virtualSiblings() const
void QWaylandScreen::setOrientationUpdateMask(Qt::ScreenOrientations mask)
{
- foreach (QWindow *window, QGuiApplication::allWindows()) {
+ const auto allWindows = QGuiApplication::allWindows();
+ for (QWindow *window : allWindows) {
QWaylandWindow *w = static_cast<QWaylandWindow *>(window->handle());
if (w && w->waylandScreen() == this)
w->setOrientationMask(mask);
diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp
index 34044ec9b..9b5971a21 100644
--- a/src/client/qwaylandshmbackingstore.cpp
+++ b/src/client/qwaylandshmbackingstore.cpp
@@ -243,12 +243,13 @@ void QWaylandShmBackingStore::resize(const QSize &size, const QRegion &)
QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size)
{
- foreach (QWaylandShmBuffer *b, mBuffers) {
+ const auto copy = mBuffers; // remove when ported to vector<unique_ptr> + remove_if
+ for (QWaylandShmBuffer *b : copy) {
if (!b->busy()) {
if (b->size() == size) {
return b;
} else {
- mBuffers.removeOne(b);
+ mBuffers.remove(b);
if (mBackBuffer == b)
mBackBuffer = nullptr;
delete b;
@@ -256,11 +257,11 @@ QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size)
}
}
- static const int MAX_BUFFERS = 5;
- if (mBuffers.count() < MAX_BUFFERS) {
+ static const size_t MAX_BUFFERS = 5;
+ if (mBuffers.size() < MAX_BUFFERS) {
QImage::Format format = QPlatformScreen::platformScreenForWindow(window())->format();
QWaylandShmBuffer *b = new QWaylandShmBuffer(mDisplay, size, format, waylandWindow()->scale());
- mBuffers.prepend(b);
+ mBuffers.push_front(b);
return b;
}
return nullptr;
@@ -288,20 +289,23 @@ void QWaylandShmBackingStore::resize(const QSize &size)
buffer = getBuffer(sizeWithMargins);
}
- qsizetype oldSize = mBackBuffer ? mBackBuffer->image()->sizeInBytes() : 0;
+ qsizetype oldSizeInBytes = mBackBuffer ? mBackBuffer->image()->sizeInBytes() : 0;
+ qsizetype newSizeInBytes = buffer->image()->sizeInBytes();
+
// mBackBuffer may have been deleted here but if so it means its size was different so we wouldn't copy it anyway
- if (mBackBuffer != buffer && oldSize == buffer->image()->sizeInBytes()) {
- memcpy(buffer->image()->bits(), mBackBuffer->image()->constBits(), buffer->image()->sizeInBytes());
- }
+ if (mBackBuffer != buffer && oldSizeInBytes == newSizeInBytes)
+ memcpy(buffer->image()->bits(), mBackBuffer->image()->constBits(), newSizeInBytes);
+
mBackBuffer = buffer;
+
// ensure the new buffer is at the beginning of the list so next time getBuffer() will pick
// it if possible
- if (mBuffers.first() != buffer) {
- mBuffers.removeOne(buffer);
- mBuffers.prepend(buffer);
+ if (mBuffers.front() != buffer) {
+ mBuffers.remove(buffer);
+ mBuffers.push_front(buffer);
}
- if (windowDecoration() && window()->isVisible())
+ if (windowDecoration() && window()->isVisible() && oldSizeInBytes != newSizeInBytes)
windowDecoration()->update();
}
diff --git a/src/client/qwaylandshmbackingstore_p.h b/src/client/qwaylandshmbackingstore_p.h
index 88ecfc5ec..8a85cd7f3 100644
--- a/src/client/qwaylandshmbackingstore_p.h
+++ b/src/client/qwaylandshmbackingstore_p.h
@@ -57,7 +57,8 @@
#include <QtGui/QImage>
#include <qpa/qplatformwindow.h>
#include <QMutex>
-#include <QLinkedList>
+
+#include <list>
QT_BEGIN_NAMESPACE
@@ -116,7 +117,7 @@ private:
QWaylandShmBuffer *getBuffer(const QSize &size);
QWaylandDisplay *mDisplay = nullptr;
- QLinkedList<QWaylandShmBuffer *> mBuffers;
+ std::list<QWaylandShmBuffer *> mBuffers;
QWaylandShmBuffer *mFrontBuffer = nullptr;
QWaylandShmBuffer *mBackBuffer = nullptr;
bool mPainting = false;
diff --git a/src/client/qwaylandsurface.cpp b/src/client/qwaylandsurface.cpp
new file mode 100644
index 000000000..c35f01b56
--- /dev/null
+++ b/src/client/qwaylandsurface.cpp
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the config.tests of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandsurface_p.h"
+#include "qwaylanddisplay_p.h"
+#include "qwaylandscreen_p.h"
+
+#include <QtGui/QGuiApplication>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+QWaylandSurface::QWaylandSurface(QWaylandDisplay *display)
+ : wl_surface(display->createSurface(this))
+{
+ connect(qApp, &QGuiApplication::screenRemoved, this, &QWaylandSurface::handleScreenRemoved);
+}
+
+QWaylandSurface::~QWaylandSurface()
+{
+ destroy();
+}
+
+QWaylandScreen *QWaylandSurface::oldestEnteredScreen()
+{
+ return m_screens.value(0, nullptr);
+}
+
+QWaylandSurface *QWaylandSurface::fromWlSurface(::wl_surface *surface)
+{
+ if (auto *s = QtWayland::wl_surface::fromObject(surface))
+ return static_cast<QWaylandSurface *>(s);
+ return nullptr;
+}
+
+void QWaylandSurface::handleScreenRemoved(QScreen *qScreen)
+{
+ auto *screen = static_cast<QWaylandScreen *>(qScreen->handle());
+ if (m_screens.removeOne(screen))
+ emit screensChanged();
+}
+
+void QWaylandSurface::surface_enter(wl_output *output)
+{
+ auto addedScreen = QWaylandScreen::fromWlOutput(output);
+
+ if (!addedScreen)
+ return;
+
+ if (m_screens.contains(addedScreen)) {
+ 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;
+ }
+
+ m_screens.append(addedScreen);
+ emit screensChanged();
+}
+
+void QWaylandSurface::surface_leave(wl_output *output)
+{
+ auto *removedScreen = QWaylandScreen::fromWlOutput(output);
+
+ if (!removedScreen)
+ return;
+
+ bool wasRemoved = m_screens.removeOne(removedScreen);
+ if (!wasRemoved) {
+ 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;
+ }
+ emit screensChanged();
+}
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
diff --git a/src/client/qwaylandsurface_p.h b/src/client/qwaylandsurface_p.h
new file mode 100644
index 000000000..541010934
--- /dev/null
+++ b/src/client/qwaylandsurface_p.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the config.tests of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDSURFACE_P_H
+#define QWAYLANDSURFACE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/QScreen>
+
+#include <QtWaylandClient/private/qwayland-wayland.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+class QWaylandScreen;
+class QWaylandWindow;
+class QWaylandDisplay;
+
+class QWaylandSurface : public QObject, public QtWayland::wl_surface
+{
+ Q_OBJECT
+public:
+ explicit QWaylandSurface(QWaylandDisplay *display);
+ ~QWaylandSurface() override;
+ QWaylandScreen *oldestEnteredScreen();
+ QWaylandWindow *waylandWindow() const { return m_window; }
+
+ static QWaylandSurface *fromWlSurface(::wl_surface *surface);
+
+signals:
+ void screensChanged();
+
+private slots:
+ void handleScreenRemoved(QScreen *qScreen);
+
+protected:
+ void surface_enter(struct ::wl_output *output) override;
+ void surface_leave(struct ::wl_output *output) override;
+
+ QVector<QWaylandScreen *> m_screens; //As seen by wl_surface.enter/leave events. Chronological order.
+ QWaylandWindow *m_window = nullptr;
+
+ friend class QWaylandWindow; // TODO: shouldn't need to be friends
+};
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDSURFACE_P_H
diff --git a/src/client/qwaylandtouch.cpp b/src/client/qwaylandtouch.cpp
index 48c869a60..0394aef31 100644
--- a/src/client/qwaylandtouch.cpp
+++ b/src/client/qwaylandtouch.cpp
@@ -40,6 +40,7 @@
#include "qwaylandtouch_p.h"
#include "qwaylandinputdevice_p.h"
#include "qwaylanddisplay_p.h"
+#include "qwaylandsurface_p.h"
QT_BEGIN_NAMESPACE
diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp
index ae26ba049..2704705e7 100644
--- a/src/client/qwaylandwindow.cpp
+++ b/src/client/qwaylandwindow.cpp
@@ -41,6 +41,7 @@
#include "qwaylandbuffer_p.h"
#include "qwaylanddisplay_p.h"
+#include "qwaylandsurface_p.h"
#include "qwaylandinputdevice_p.h"
#include "qwaylandscreen_p.h"
#include "qwaylandshellsurface_p.h"
@@ -80,7 +81,6 @@ QWaylandWindow::QWaylandWindow(QWindow *window)
{
static WId id = 1;
mWindowId = id++;
- connect(qApp, &QGuiApplication::screenRemoved, this, &QWaylandWindow::handleScreenRemoved);
initializeWlSurface();
}
@@ -90,11 +90,12 @@ QWaylandWindow::~QWaylandWindow()
delete mWindowDecoration;
- if (isInitialized())
+ if (mSurface)
reset(false);
const QWindow *parent = window();
- foreach (QWindow *w, QGuiApplication::topLevelWindows()) {
+ const auto tlw = QGuiApplication::topLevelWindows();
+ for (QWindow *w : tlw) {
if (w->transientParent() == parent)
QWindowSystemInterface::handleCloseEvent(w);
}
@@ -115,7 +116,7 @@ void QWaylandWindow::initWindow()
if (window()->type() == Qt::Desktop)
return;
- if (!isInitialized()) {
+ if (!mSurface) {
initializeWlSurface();
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
QGuiApplication::sendEvent(window(), &e);
@@ -181,7 +182,7 @@ void QWaylandWindow::initWindow()
// typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
// to inform the compositor that high-resolution buffers will be provided.
if (mDisplay->compositorVersion() >= 3)
- set_buffer_scale(scale());
+ mSurface->set_buffer_scale(scale());
if (QScreen *s = window()->screen())
setOrientationMask(s->orientationUpdateMask());
@@ -199,10 +200,13 @@ void QWaylandWindow::initWindow()
void QWaylandWindow::initializeWlSurface()
{
- Q_ASSERT(!isInitialized());
+ Q_ASSERT(!mSurface);
{
QWriteLocker lock(&mSurfaceLock);
- init(mDisplay->createSurface(static_cast<QtWayland::wl_surface *>(this)));
+ mSurface.reset(new QWaylandSurface(mDisplay));
+ connect(mSurface.data(), &QWaylandSurface::screensChanged,
+ this, &QWaylandWindow::handleScreensChanged);
+ mSurface->m_window = this;
}
emit wlSurfaceCreated();
}
@@ -231,7 +235,7 @@ bool QWaylandWindow::shouldCreateSubSurface() const
void QWaylandWindow::reset(bool sendDestroyEvent)
{
- if (isInitialized() && sendDestroyEvent) {
+ if (mSurface && sendDestroyEvent) {
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
QGuiApplication::sendEvent(window(), &e);
}
@@ -239,12 +243,11 @@ void QWaylandWindow::reset(bool sendDestroyEvent)
mShellSurface = nullptr;
delete mSubSurfaceWindow;
mSubSurfaceWindow = nullptr;
- if (isInitialized()) {
+ if (mSurface) {
emit wlSurfaceDestroyed();
QWriteLocker lock(&mSurfaceLock);
- destroy();
+ mSurface.reset();
}
- mScreens.clear();
if (mFrameCallback) {
wl_callback_destroy(mFrameCallback);
@@ -264,7 +267,9 @@ void QWaylandWindow::reset(bool sendDestroyEvent)
QWaylandWindow *QWaylandWindow::fromWlSurface(::wl_surface *surface)
{
- return static_cast<QWaylandWindow *>(static_cast<QtWayland::wl_surface *>(wl_surface_get_user_data(surface)));
+ if (auto *s = QWaylandSurface::fromWlSurface(surface))
+ return s->m_window;
+ return nullptr;
}
WId QWaylandWindow::winId() const
@@ -398,7 +403,12 @@ void QWaylandWindow::closePopups(QWaylandWindow *parent)
QWaylandScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const
{
- return mScreens.isEmpty() ? waylandScreen() : mScreens.first();
+ if (mSurface) {
+ if (auto *screen = mSurface->oldestEnteredScreen())
+ return screen;
+ }
+
+ return waylandScreen();
}
void QWaylandWindow::setVisible(bool visible)
@@ -442,18 +452,18 @@ void QWaylandWindow::setMask(const QRegion &mask)
mMask = mask;
- if (!isInitialized())
+ if (!mSurface)
return;
if (mMask.isEmpty()) {
- set_input_region(nullptr);
+ mSurface->set_input_region(nullptr);
} else {
struct ::wl_region *region = mDisplay->createRegion(mMask);
- set_input_region(region);
+ mSurface->set_input_region(region);
wl_region_destroy(region);
}
- wl_surface::commit();
+ mSurface->commit();
}
void QWaylandWindow::applyConfigureWhenPossible()
@@ -507,58 +517,6 @@ void QWaylandWindow::applyConfigure()
QWindowSystemInterface::flushWindowSystemEvents();
}
-void QWaylandWindow::surface_enter(wl_output *output)
-{
- QWaylandScreen *oldScreen = calculateScreenFromSurfaceEvents();
- auto addedScreen = QWaylandScreen::fromWlOutput(output);
-
- if (mScreens.contains(addedScreen)) {
- 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;
- }
-
- mScreens.append(addedScreen);
-
- QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
- if (oldScreen != newScreen) //currently this will only happen if the first wl_surface.enter is for a non-primary screen
- handleScreenChanged();
-}
-
-void QWaylandWindow::surface_leave(wl_output *output)
-{
- QWaylandScreen *oldScreen = calculateScreenFromSurfaceEvents();
- auto *removedScreen = QWaylandScreen::fromWlOutput(output);
- bool wasRemoved = mScreens.removeOne(removedScreen);
- if (!wasRemoved) {
- 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;
- }
-
- QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
- if (oldScreen != newScreen)
- handleScreenChanged();
-}
-
-void QWaylandWindow::handleScreenRemoved(QScreen *qScreen)
-{
- QWaylandScreen *oldScreen = calculateScreenFromSurfaceEvents();
- bool wasRemoved = mScreens.removeOne(static_cast<QWaylandScreen *>(qScreen->handle()));
- if (wasRemoved) {
- QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
- if (oldScreen != newScreen)
- handleScreenChanged();
- }
-}
-
void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
{
Q_ASSERT(!buffer->committed());
@@ -566,9 +524,9 @@ void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
handleUpdate();
buffer->setBusy();
- QtWayland::wl_surface::attach(buffer->buffer(), x, y);
+ mSurface->attach(buffer->buffer(), x, y);
} else {
- QtWayland::wl_surface::attach(nullptr, 0, 0);
+ mSurface->attach(nullptr, 0, 0);
}
}
@@ -580,7 +538,7 @@ void QWaylandWindow::attachOffset(QWaylandBuffer *buffer)
void QWaylandWindow::damage(const QRect &rect)
{
- damage(rect.x(), rect.y(), rect.width(), rect.height());
+ mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
}
void QWaylandWindow::safeCommit(QWaylandBuffer *buffer, const QRegion &damage)
@@ -610,20 +568,20 @@ void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage)
qCDebug(lcWaylandBackingstore) << "Buffer already committed, ignoring.";
return;
}
- if (!isInitialized())
+ if (!mSurface)
return;
attachOffset(buffer);
for (const QRect &rect: damage)
- wl_surface::damage(rect.x(), rect.y(), rect.width(), rect.height());
+ mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
Q_ASSERT(!buffer->committed());
buffer->setCommitted();
- wl_surface::commit();
+ mSurface->commit();
}
void QWaylandWindow::commit()
{
- wl_surface::commit();
+ mSurface->commit();
}
const wl_callback_listener QWaylandWindow::callbackListener = {
@@ -714,6 +672,11 @@ QRect QWaylandWindow::windowContentGeometry() const
return QRect(QPoint(), surfaceSize());
}
+wl_surface *QWaylandWindow::wlSurface()
+{
+ return mSurface ? mSurface->object() : nullptr;
+}
+
QWaylandShellSurface *QWaylandWindow::shellSurface() const
{
return mShellSurface;
@@ -755,9 +718,9 @@ void QWaylandWindow::handleContentOrientationChange(Qt::ScreenOrientation orient
default:
Q_UNREACHABLE();
}
- set_buffer_transform(transform);
+ mSurface->set_buffer_transform(transform);
// set_buffer_transform is double buffered, we need to commit.
- wl_surface::commit();
+ mSurface->commit();
}
void QWaylandWindow::setOrientationMask(Qt::ScreenOrientations mask)
@@ -846,7 +809,7 @@ bool QWaylandWindow::createDecoration()
}
if (hadDecoration != (bool)mWindowDecoration) {
- foreach (QWaylandSubSurface *subsurf, mChildren) {
+ for (QWaylandSubSurface *subsurf : qAsConst(mChildren)) {
QPoint pos = subsurf->window()->geometry().topLeft();
QMargins m = frameMargins();
subsurf->set_position(pos.x() + m.left(), pos.y() + m.top());
@@ -889,6 +852,18 @@ QWaylandWindow *QWaylandWindow::transientParent() const
void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e)
{
+ if (e.type == QWaylandPointerEvent::Leave) {
+ if (mWindowDecoration) {
+ if (mMouseEventsInContentArea)
+ QWindowSystemInterface::handleLeaveEvent(window());
+ } else {
+ QWindowSystemInterface::handleLeaveEvent(window());
+ }
+#if QT_CONFIG(cursor)
+ restoreMouseCursor(inputDevice);
+#endif
+ return;
+ }
if (mWindowDecoration) {
handleMouseEventWithDecoration(inputDevice, e);
@@ -897,11 +872,15 @@ void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylan
case QWaylandPointerEvent::Enter:
QWindowSystemInterface::handleEnterEvent(window(), e.local, e.global);
break;
+ case QWaylandPointerEvent::Press:
+ case QWaylandPointerEvent::Release:
case QWaylandPointerEvent::Motion:
QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, e.local, e.global, e.buttons, e.modifiers);
break;
case QWaylandPointerEvent::Wheel:
- QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, e.local, e.global, e.pixelDelta, e.angleDelta, e.modifiers);
+ QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, e.local, e.global,
+ e.pixelDelta, e.angleDelta, e.modifiers,
+ e.phase, e.source, false);
break;
}
}
@@ -915,20 +894,6 @@ void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylan
#endif
}
-void QWaylandWindow::handleMouseLeave(QWaylandInputDevice *inputDevice)
-{
- if (mWindowDecoration) {
- if (mMouseEventsInContentArea) {
- QWindowSystemInterface::handleLeaveEvent(window());
- }
- } else {
- QWindowSystemInterface::handleLeaveEvent(window());
- }
-#if QT_CONFIG(cursor)
- restoreMouseCursor(inputDevice);
-#endif
-}
-
bool QWaylandWindow::touchDragDecoration(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::TouchPointState state, Qt::KeyboardModifiers mods)
{
if (!mWindowDecoration)
@@ -970,12 +935,18 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe
case QWaylandPointerEvent::Enter:
QWindowSystemInterface::handleEnterEvent(window(), localTranslated, globalTranslated);
break;
+ case QWaylandPointerEvent::Press:
+ case QWaylandPointerEvent::Release:
case QWaylandPointerEvent::Motion:
QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, localTranslated, globalTranslated, e.buttons, e.modifiers);
break;
- case QWaylandPointerEvent::Wheel:
- QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, localTranslated, globalTranslated, e.pixelDelta, e.angleDelta, e.modifiers);
+ case QWaylandPointerEvent::Wheel: {
+ QWindowSystemInterface::handleWheelEvent(window(), e.timestamp,
+ localTranslated, globalTranslated,
+ e.pixelDelta, e.angleDelta, e.modifiers,
+ e.phase, e.source, false);
break;
+ }
}
mMouseEventsInContentArea = true;
@@ -988,16 +959,21 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe
}
}
-void QWaylandWindow::handleScreenChanged()
+void QWaylandWindow::handleScreensChanged()
{
QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
+
+ if (newScreen == mLastReportedScreen)
+ return;
+
QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen());
+ mLastReportedScreen = newScreen;
int scale = newScreen->scale();
if (scale != mScale) {
mScale = scale;
- if (isInitialized() && mDisplay->compositorVersion() >= 3)
- set_buffer_scale(mScale);
+ if (mSurface && mDisplay->compositorVersion() >= 3)
+ mSurface->set_buffer_scale(mScale);
ensureSize();
}
}
@@ -1167,7 +1143,7 @@ void QWaylandWindow::handleUpdate()
{
// TODO: Should sync subsurfaces avoid requesting frame callbacks?
QReadLocker lock(&mSurfaceLock);
- if (!isInitialized())
+ if (!mSurface)
return;
if (mFrameCallback) {
@@ -1184,7 +1160,7 @@ void QWaylandWindow::handleUpdate()
QMetaObject::invokeMethod(this, [this, id] { killTimer(id); }, Qt::QueuedConnection);
}
- mFrameCallback = frame();
+ mFrameCallback = mSurface->frame();
wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
mWaitingForFrameCallback = true;
mWaitingForUpdate = false;
diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h
index b03d92e56..52e57c72a 100644
--- a/src/client/qwaylandwindow_p.h
+++ b/src/client/qwaylandwindow_p.h
@@ -81,8 +81,9 @@ class QWaylandInputDevice;
class QWaylandScreen;
class QWaylandShmBackingStore;
class QWaylandPointerEvent;
+class QWaylandSurface;
-class Q_WAYLAND_CLIENT_EXPORT QWaylandWindow : public QObject, public QPlatformWindow, public QtWayland::wl_surface
+class Q_WAYLAND_CLIENT_EXPORT QWaylandWindow : public QObject, public QPlatformWindow
{
Q_OBJECT
public:
@@ -110,12 +111,10 @@ public:
void applyConfigureWhenPossible(); //rename to possible?
- using QtWayland::wl_surface::attach;
void attach(QWaylandBuffer *buffer, int x, int y);
void attachOffset(QWaylandBuffer *buffer);
QPoint attachOffset() const;
- using QtWayland::wl_surface::damage;
void damage(const QRect &rect);
void safeCommit(QWaylandBuffer *buffer, const QRegion &damage);
@@ -130,6 +129,8 @@ public:
QSize surfaceSize() const;
QRect windowContentGeometry() const;
+ QWaylandSurface *waylandSurface() const { return mSurface.data(); }
+ ::wl_surface *wlSurface();
static QWaylandWindow *fromWlSurface(::wl_surface *surface);
QWaylandDisplay *display() const { return mDisplay; }
@@ -159,7 +160,6 @@ public:
QWaylandAbstractDecoration *decoration() const;
void handleMouse(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e);
- void handleMouseLeave(QWaylandInputDevice *inputDevice);
bool touchDragDecoration(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global,
Qt::TouchPointState state, Qt::KeyboardModifiers mods);
@@ -209,11 +209,8 @@ signals:
void wlSurfaceDestroyed();
protected:
- void surface_enter(struct ::wl_output *output) override;
- void surface_leave(struct ::wl_output *output) override;
-
- QVector<QWaylandScreen *> mScreens; //As seen by wl_surface.enter/leave events. Chronological order.
QWaylandDisplay *mDisplay = nullptr;
+ QScopedPointer<QWaylandSurface> mSurface;
QWaylandShellSurface *mShellSurface = nullptr;
QWaylandSubSurface *mSubSurfaceWindow = nullptr;
QVector<QWaylandSubSurface *> mChildren;
@@ -244,6 +241,7 @@ protected:
bool mSentInitialResize = false;
QPoint mOffset;
int mScale = 1;
+ QWaylandScreen *mLastReportedScreen = nullptr;
QIcon mWindowIcon;
@@ -255,9 +253,6 @@ protected:
QWaylandBuffer *mQueuedBuffer = nullptr;
QRegion mQueuedBufferDamage;
-private slots:
- void handleScreenRemoved(QScreen *qScreen);
-
private:
void setGeometry_helper(const QRect &rect);
void initWindow();
@@ -270,7 +265,7 @@ private:
QWaylandScreen *calculateScreenFromSurfaceEvents() const;
void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e);
- void handleScreenChanged();
+ void handleScreensChanged();
bool mInResizeFromApplyConfigure = false;
QRect mLastExposeGeometry;