diff --git a/src/client/qwaylandclipboard.cpp b/src/client/qwaylandclipboard.cpp index 226a374abfac569eb37227be560b74fd7d860807..3e25875f04d1425779bfe6fbf401d59dc64c2553 100644 --- a/src/client/qwaylandclipboard.cpp +++ b/src/client/qwaylandclipboard.cpp @@ -88,6 +88,10 @@ void QWaylandClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) if (!inputDevice || !inputDevice->dataDevice()) 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) : 0); emitChanged(mode); diff --git a/src/client/qwaylanddataoffer.cpp b/src/client/qwaylanddataoffer.cpp index 10bf2f1e1ba927604326181195ebfb69e5f667e3..7b69ce6ce904d5c05bf87a05b12e9369eeb3d729 100644 --- a/src/client/qwaylanddataoffer.cpp +++ b/src/client/qwaylanddataoffer.cpp @@ -53,6 +53,11 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { +static QString utf8Text() +{ + return QStringLiteral("text/plain;charset=utf-8"); +} + QWaylandDataOffer::QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer) : QtWayland::wl_data_offer(offer) , m_mimeData(new QWaylandMimeData(this, display)) @@ -102,7 +107,13 @@ void QWaylandMimeData::appendFormat(const QString &mimeType) bool QWaylandMimeData::hasFormat_sys(const QString &mimeType) const { - return m_types.contains(mimeType); + if (m_types.contains(mimeType)) + return true; + + if (mimeType == QStringLiteral("text/plain") && m_types.contains(utf8Text())) + return true; + + return false; } QStringList QWaylandMimeData::formats_sys() const @@ -117,8 +128,14 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T if (m_data.contains(mimeType)) return m_data.value(mimeType); - if (!m_types.contains(mimeType)) - return QVariant(); + QString mime = mimeType; + + if (!m_types.contains(mimeType)) { + if (mimeType == QStringLiteral("text/plain") && m_types.contains(utf8Text())) + mime = utf8Text(); + else + return QVariant(); + } int pipefd[2]; if (::pipe2(pipefd, O_CLOEXEC|O_NONBLOCK) == -1) { @@ -126,7 +143,7 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T return QVariant(); } - m_dataOffer->receive(mimeType, pipefd[1]); + m_dataOffer->receive(mime, pipefd[1]); m_display->flushRequests(); close(pipefd[1]); diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp index ea7fe1a6a8b4f7eadfb823de62bd3bc8b01349b4..1284d39a168f3b2eb1476cf71a7b82c6019f05ab 100644 --- a/src/client/qwaylanddisplay.cpp +++ b/src/client/qwaylanddisplay.cpp @@ -140,6 +140,9 @@ QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration) , mQtKeyExtension(0) , mTextInputManager(0) , mHardwareIntegration(0) + , mLastInputSerial(0) + , mLastInputDevice(0) + , mLastInputWindow(0) { qRegisterMetaType<uint32_t>("uint32_t"); @@ -373,6 +376,11 @@ bool QWaylandDisplay::supportsWindowDecoration() const return integrationSupport; } +void QWaylandDisplay::setLastInputDevice(QWaylandInputDevice *device, uint32_t serial, QWaylandWindow *win) +{ + mLastInputDevice = device; + mLastInputSerial = serial; + mLastInputWindow = win; } QT_END_NAMESPACE diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h index 9d4413f6fe64c05760d13f189095790e795dd122..394fd427017d118e5c0813934b7a771f7c91520c 100644 --- a/src/client/qwaylanddisplay_p.h +++ b/src/client/qwaylanddisplay_p.h @@ -163,6 +163,11 @@ public: bool supportsWindowDecoration() const; + uint32_t lastInputSerial() const { return mLastInputSerial; } + QWaylandInputDevice *lastInputDevice() const { return mLastInputDevice; } + QWaylandWindow *lastInputWindow() const { return mLastInputWindow; } + void setLastInputDevice(QWaylandInputDevice *device, uint32_t serial, QWaylandWindow *window); + public slots: void blockingReadEvents(); void flushRequests(); @@ -204,6 +209,9 @@ private: bool mScreensInitialized; QList<RegistryGlobal> mGlobals; int mCompositorVersion; + uint32_t mLastInputSerial; + QWaylandInputDevice *mLastInputDevice; + QWaylandWindow *mLastInputWindow; void registry_global(uint32_t id, const QString &interface, uint32_t version) Q_DECL_OVERRIDE; void registry_global_remove(uint32_t id) Q_DECL_OVERRIDE; diff --git a/src/client/qwaylanddnd.cpp b/src/client/qwaylanddnd.cpp index 514383d300addbf251c704f0d641c739a3e677a3..85deaf27f0f68d34db8a3e2c0be81a4901b07a9b 100644 --- a/src/client/qwaylanddnd.cpp +++ b/src/client/qwaylanddnd.cpp @@ -73,13 +73,20 @@ QMimeData * QWaylandDrag::platformDropData() void QWaylandDrag::startDrag() { + bool cancel = false; if (!shapedPixmapWindow()) { QBasicDrag::startDrag(); - QBasicDrag::cancel(); + // Don't call cancel() here, since that will hide 'shapedPixmapWindow()', and + // QWaylandWindow::setVisible(false) will flush the window system queue, + // ending up trying to render the window, which doesn't have a role yet, + // and so blocking waiting for a frame callback. + cancel = true; } QWaylandWindow *icon = static_cast<QWaylandWindow *>(shapedPixmapWindow()->handle()); m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), icon); + if (cancel) + QBasicDrag::cancel(); QBasicDrag::startDrag(); } diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp index db43e525b2bd87cd305fc9ab024103d1ed25c520..63bbeb14acb288bc891953f4160ef2ec8e54be8d 100644 --- a/src/client/qwaylandinputdevice.cpp +++ b/src/client/qwaylandinputdevice.cpp @@ -439,10 +439,10 @@ 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, Qt::NoModifier); + MotionEvent e(time, pos, global, mButtons, mParent->modifiers()); grab->handleMouse(mParent, e); } else { - MotionEvent e(time, mSurfacePos, mGlobalPos, mButtons, Qt::NoModifier); + MotionEvent e(time, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers()); window->handleMouse(mParent, e); } } @@ -450,7 +450,6 @@ void QWaylandInputDevice::Pointer::pointer_motion(uint32_t time, wl_fixed_t surf void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { - Q_UNUSED(serial); QWaylandWindow *window = mFocus; Qt::MouseButton qt_button; @@ -483,15 +482,17 @@ void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time mParent->mTime = time; mParent->mSerial = serial; + if (state) + 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, Qt::NoModifier); + MotionEvent e(time, pos, global, mButtons, mParent->modifiers()); grab->handleMouse(mParent, e); } else if (window) { - MotionEvent e(time, mSurfacePos, mGlobalPos, mButtons, Qt::NoModifier); + MotionEvent e(time, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers()); window->handleMouse(mParent, e); } } @@ -736,13 +737,13 @@ void QWaylandInputDevice::Keyboard::focusCallback(void *data, struct wl_callback void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { - Q_UNUSED(serial); QWaylandWindow *window = mFocus; uint32_t code = key + 8; bool isDown = state != 0; QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease; QString text; int qtkey = key + 8; // qt-compositor substracts 8 for some reason + mParent->mSerial = serial; if (!window) { // We destroyed the keyboard focus surface, but the server @@ -750,6 +751,9 @@ void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, return; } + if (isDown) + mParent->mQDisplay->setLastInputDevice(mParent, serial, window); + #ifndef QT_NO_WAYLAND_XKB if (!createDefaultKeyMap()) { return; @@ -761,7 +765,8 @@ void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, Qt::KeyboardModifiers modifiers = mParent->modifiers(); uint utf32 = xkb_keysym_to_utf32(sym); - text = QString::fromUcs4(&utf32, 1); + if (utf32) + text = QString::fromUcs4(&utf32, 1); qtkey = keysymToQtKey(sym, modifiers, text); @@ -855,6 +860,7 @@ void QWaylandInputDevice::Touch::touch_down(uint32_t serial, mParent->mTime = time; mParent->mSerial = serial; mFocus = QWaylandWindow::fromWlSurface(surface); + mParent->mQDisplay->setLastInputDevice(mParent, serial, mFocus); mParent->handleTouchPoint(id, wl_fixed_to_double(x), wl_fixed_to_double(y), Qt::TouchPointPressed); } diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp index a1aff8d1cf57e8654991eb9ffc68f76b9f7f93ac..56885531cef86708bb0429b9889e4fd6b2657d34 100644 --- a/src/client/qwaylandscreen.cpp +++ b/src/client/qwaylandscreen.cpp @@ -118,6 +118,14 @@ QDpi QWaylandScreen::logicalDpi() const return QPlatformScreen::logicalDpi(); } +QList<QPlatformScreen *> QWaylandScreen::virtualSiblings() const +{ + QList<QPlatformScreen *> list; + foreach (QWaylandScreen *screen, mWaylandDisplay->screens()) + list << screen; + return list; +} + void QWaylandScreen::setOrientationUpdateMask(Qt::ScreenOrientations mask) { foreach (QWindow *window, QGuiApplication::allWindows()) { diff --git a/src/client/qwaylandscreen_p.h b/src/client/qwaylandscreen_p.h index c337d9be3c14375cea744a33ac5ea90aeb2e291e..b1b4a73f7b3b609fac1c127bf3923996b80a2e35 100644 --- a/src/client/qwaylandscreen_p.h +++ b/src/client/qwaylandscreen_p.h @@ -70,6 +70,7 @@ public: QSizeF physicalSize() const Q_DECL_OVERRIDE; QDpi logicalDpi() const Q_DECL_OVERRIDE; + QList<QPlatformScreen *> virtualSiblings() const Q_DECL_OVERRIDE; void setOrientationUpdateMask(Qt::ScreenOrientations mask); diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp index cf4e0e67218290e81a9b3cf0046edef9be407db2..b01e1f108722aae499bf7daa9e517290330a8278 100644 --- a/src/client/qwaylandwindow.cpp +++ b/src/client/qwaylandwindow.cpp @@ -90,8 +90,6 @@ QWaylandWindow::QWaylandWindow(QWindow *window) , mResizeDirty(false) , mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP")) , mSentInitialResize(false) - , mMouseDevice(0) - , mMouseSerial(0) , mState(Qt::WindowNoState) , mMask() , mBackingStore(Q_NULLPTR) @@ -218,7 +216,7 @@ void QWaylandWindow::setGeometry(const QRect &rect) { setGeometry_helper(rect); - if (window()->isVisible()) { + if (window()->isVisible() && rect.isValid()) { if (mWindowDecoration) mWindowDecoration->update(); @@ -236,14 +234,17 @@ void QWaylandWindow::setGeometry(const QRect &rect) void QWaylandWindow::setVisible(bool visible) { if (visible) { - if (window()->type() == Qt::Popup && transientParent()) { + if (window()->type() == Qt::Popup) { QWaylandWindow *parent = transientParent(); - mMouseDevice = parent->mMouseDevice; - mMouseSerial = parent->mMouseSerial; - - QWaylandWlShellSurface *wlshellSurface = dynamic_cast<QWaylandWlShellSurface*>(mShellSurface); - if (mMouseDevice && wlshellSurface) { - wlshellSurface->setPopup(transientParent(), mMouseDevice, mMouseSerial); + if (!parent) { + // Try with the current focus window. It should be the right one and anyway + // better than having no parent at all. + parent = mDisplay->lastInputWindow(); + } + if (parent) { + QWaylandWlShellSurface *wlshellSurface = dynamic_cast<QWaylandWlShellSurface*>(mShellSurface); + if (wlshellSurface) + wlshellSurface->setPopup(parent, mDisplay->lastInputDevice(), mDisplay->lastInputSerial()); } } @@ -364,7 +365,7 @@ void QWaylandWindow::requestResize() { QMutexLocker lock(&mResizeLock); - if (mCanResize) { + if (mCanResize || !mSentInitialResize) { doResize(); } @@ -599,7 +600,7 @@ QWaylandWindow *QWaylandWindow::transientParent() const if (window()->transientParent()) { // Take the top level window here, since the transient parent may be a QWidgetWindow // or some other window without a shell surface, which is then not able to get mouse - // events, nor set mMouseSerial and mMouseDevice. + // events. return static_cast<QWaylandWindow *>(topLevelWindow(window()->transientParent())->handle()); } return 0; @@ -607,10 +608,6 @@ QWaylandWindow *QWaylandWindow::transientParent() const void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e) { - if (e.buttons != Qt::NoButton) { - mMouseSerial = inputDevice->serial(); - mMouseDevice = inputDevice; - } if (mWindowDecoration) { handleMouseEventWithDecoration(inputDevice, e); diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h index de59e4e20b6b1d44dc60ee1e58d0ac8cd8bfc6f2..21d4886ccf14fb3ccafc85703f1a77d28fb48e1c 100644 --- a/src/client/qwaylandwindow_p.h +++ b/src/client/qwaylandwindow_p.h @@ -222,8 +222,6 @@ protected: QPoint mOffset; QIcon mWindowIcon; - QWaylandInputDevice *mMouseDevice; - int mMouseSerial; Qt::WindowState mState; QRegion mMask; diff --git a/src/client/qwaylandwlshellsurface.cpp b/src/client/qwaylandwlshellsurface.cpp index 550e6caebb15febda395a1f84bf3d012c4757c7d..0f249e36e8833a589aa7365b412911308d41a556 100644 --- a/src/client/qwaylandwlshellsurface.cpp +++ b/src/client/qwaylandwlshellsurface.cpp @@ -189,8 +189,14 @@ void QWaylandWlShellSurface::updateTransientParent(QWindow *parent) void QWaylandWlShellSurface::setPopup(QWaylandWindow *parent, QWaylandInputDevice *device, int serial) { QWaylandWindow *parent_wayland_window = parent; - if (!parent_wayland_window) + if (!parent_wayland_window) { + qWarning("setPopup called without parent window"); + return; + } + if (!device) { + qWarning("setPopup called without input device"); return; + } // set_popup expects a position relative to the parent QPoint transientPos = m_window->geometry().topLeft(); // this is absolute diff --git a/src/compositor/compositor_api/qwaylandsurfaceitem.cpp b/src/compositor/compositor_api/qwaylandsurfaceitem.cpp index 8a0d6bd28071892f9737a25736b12486c9b93f17..972a9d34ac571cff2d024687d6d1b5d0cd2a47a4 100644 --- a/src/compositor/compositor_api/qwaylandsurfaceitem.cpp +++ b/src/compositor/compositor_api/qwaylandsurfaceitem.cpp @@ -229,6 +229,12 @@ void QWaylandSurfaceItem::touchEvent(QTouchEvent *event) if (m_touchEventsEnabled) { QWaylandInputDevice *inputDevice = compositor()->inputDeviceFor(event); + if (event->type() == QEvent::TouchBegin) { + QQuickItem *grabber = window()->mouseGrabberItem(); + if (grabber != this) + grabMouse(); + } + QPoint pointPos; const QList<QTouchEvent::TouchPoint> &points = event->touchPoints(); if (!points.isEmpty()) @@ -249,6 +255,14 @@ void QWaylandSurfaceItem::touchEvent(QTouchEvent *event) } } +void QWaylandSurfaceItem::mouseUngrabEvent() +{ + if (surface()) { + QTouchEvent e(QEvent::TouchCancel); + touchEvent(&e); + } +} + void QWaylandSurfaceItem::takeFocus(QWaylandInputDevice *device) { setFocus(true); diff --git a/src/compositor/compositor_api/qwaylandsurfaceitem.h b/src/compositor/compositor_api/qwaylandsurfaceitem.h index 2573fba3ee2f53e5e32033728226f8f5dd5a9780..2bf822f54036f1178173cbd900a2cf744f64b015 100644 --- a/src/compositor/compositor_api/qwaylandsurfaceitem.h +++ b/src/compositor/compositor_api/qwaylandsurfaceitem.h @@ -101,6 +101,7 @@ protected: void keyReleaseEvent(QKeyEvent *event); void touchEvent(QTouchEvent *event); + void mouseUngrabEvent() Q_DECL_OVERRIDE; public slots: virtual void takeFocus(QWaylandInputDevice *device = 0); diff --git a/src/compositor/compositor_api/qwaylandsurfaceview.cpp b/src/compositor/compositor_api/qwaylandsurfaceview.cpp index e6fd905870b6d52fb1ba351fbe204429116bd158..664ab98059ae7ce8f778f6f139738f9188cb6fff 100644 --- a/src/compositor/compositor_api/qwaylandsurfaceview.cpp +++ b/src/compositor/compositor_api/qwaylandsurfaceview.cpp @@ -42,6 +42,8 @@ #include "qwaylandsurfaceview.h" #include "qwaylandsurface.h" #include "qwaylandsurface_p.h" +#include "qwaylandcompositor.h" +#include "qwaylandinput.h" QT_BEGIN_NAMESPACE @@ -65,6 +67,10 @@ QWaylandSurfaceView::QWaylandSurfaceView(QWaylandSurface *surf) QWaylandSurfaceView::~QWaylandSurfaceView() { if (d->surface) { + QWaylandInputDevice *i = d->surface->compositor()->defaultInputDevice(); + if (i->mouseFocus() == this) + i->setMouseFocus(nullptr, QPointF()); + d->surface->destroy(); d->surface->d_func()->views.removeOne(this); }