Merge remote-tracking branch 'origin/5.14' into 5.15
Change-Id: I1fdf822938737aebe0c13a2eba9e478ba8f8ba14
This commit is contained in:
commit
0d39c32532
@ -140,7 +140,7 @@
|
|||||||
},
|
},
|
||||||
"xcomposite-egl": {
|
"xcomposite-egl": {
|
||||||
"label": "XComposite EGL",
|
"label": "XComposite EGL",
|
||||||
"condition": "features.wayland-client && features.opengl && features.egl && features.xlib && libs.xcomposite",
|
"condition": "features.wayland-client && features.opengl && features.egl && features.xlib && libs.xcomposite && features.egl_x11",
|
||||||
"output": [ "privateFeature" ]
|
"output": [ "privateFeature" ]
|
||||||
},
|
},
|
||||||
"xcomposite-glx": {
|
"xcomposite-glx": {
|
||||||
|
@ -342,7 +342,7 @@ void QWaylandXdgSurfaceV6::requestWindowStates(Qt::WindowStates states)
|
|||||||
if (m_toplevel)
|
if (m_toplevel)
|
||||||
m_toplevel->requestWindowStates(states);
|
m_toplevel->requestWindowStates(states);
|
||||||
else
|
else
|
||||||
qCWarning(lcQpaWayland) << "Non-toplevel surfaces can't request window states";
|
qCDebug(lcQpaWayland) << "Ignoring window states requested by non-toplevel.";
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWaylandXdgSurfaceV6::setToplevel()
|
void QWaylandXdgSurfaceV6::setToplevel()
|
||||||
|
@ -377,7 +377,7 @@ void QWaylandXdgSurface::requestWindowStates(Qt::WindowStates states)
|
|||||||
if (m_toplevel)
|
if (m_toplevel)
|
||||||
m_toplevel->requestWindowStates(states);
|
m_toplevel->requestWindowStates(states);
|
||||||
else
|
else
|
||||||
qCWarning(lcQpaWayland) << "Non-toplevel surfaces can't request window states";
|
qCDebug(lcQpaWayland) << "Ignoring window states requested by non-toplevel zxdg_surface_v6.";
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWaylandXdgSurface::setToplevel()
|
void QWaylandXdgSurface::setToplevel()
|
||||||
|
@ -103,20 +103,23 @@ QWaylandDataOffer *QWaylandDataDevice::dragOffer() const
|
|||||||
return m_dragOffer.data();
|
return m_dragOffer.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon)
|
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);
|
|
||||||
|
|
||||||
auto *seat = m_display->currentInputDevice();
|
auto *seat = m_display->currentInputDevice();
|
||||||
auto *origin = seat->pointerFocus();
|
auto *origin = seat->pointerFocus();
|
||||||
if (!origin)
|
if (!origin)
|
||||||
origin = seat->touchFocus();
|
origin = seat->touchFocus();
|
||||||
|
|
||||||
if (origin)
|
if (!origin) {
|
||||||
start_drag(m_dragSource->object(), origin->wlSurface(), icon->wlSurface(), m_display->currentInputDevice()->serial());
|
|
||||||
else
|
|
||||||
qCDebug(lcQpaWayland) << "Couldn't start a drag because the origin window could not be found.";
|
qCDebug(lcQpaWayland) << "Couldn't start a drag because the origin window could not be found.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData));
|
||||||
|
connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled);
|
||||||
|
|
||||||
|
start_drag(m_dragSource->object(), origin->wlSurface(), icon->wlSurface(), m_display->currentInputDevice()->serial());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWaylandDataDevice::cancelDrag()
|
void QWaylandDataDevice::cancelDrag()
|
||||||
|
@ -89,7 +89,7 @@ public:
|
|||||||
|
|
||||||
#if QT_CONFIG(draganddrop)
|
#if QT_CONFIG(draganddrop)
|
||||||
QWaylandDataOffer *dragOffer() const;
|
QWaylandDataOffer *dragOffer() const;
|
||||||
void startDrag(QMimeData *mimeData, QWaylandWindow *icon);
|
bool startDrag(QMimeData *mimeData, QWaylandWindow *icon);
|
||||||
void cancelDrag();
|
void cancelDrag();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -66,8 +66,13 @@ void QWaylandDrag::startDrag()
|
|||||||
{
|
{
|
||||||
QBasicDrag::startDrag();
|
QBasicDrag::startDrag();
|
||||||
QWaylandWindow *icon = static_cast<QWaylandWindow *>(shapedPixmapWindow()->handle());
|
QWaylandWindow *icon = static_cast<QWaylandWindow *>(shapedPixmapWindow()->handle());
|
||||||
m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), icon);
|
if (m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), icon)) {
|
||||||
icon->addAttachOffset(-drag()->hotSpot());
|
icon->addAttachOffset(-drag()->hotSpot());
|
||||||
|
} else {
|
||||||
|
// Cancelling immediately does not work, since the event loop for QDrag::exec is started
|
||||||
|
// after this function returns.
|
||||||
|
QMetaObject::invokeMethod(this, [this](){ cancelDrag(); }, Qt::QueuedConnection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWaylandDrag::cancel()
|
void QWaylandDrag::cancel()
|
||||||
|
@ -119,7 +119,8 @@ void QWaylandTextInput::updateState(Qt::InputMethodQueries queries, uint32_t fla
|
|||||||
if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle())
|
if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto *surface = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle())->wlSurface();
|
auto *window = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle());
|
||||||
|
auto *surface = window->wlSurface();
|
||||||
if (!surface || (surface != m_surface))
|
if (!surface || (surface != m_surface))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -157,8 +158,10 @@ void QWaylandTextInput::updateState(Qt::InputMethodQueries queries, uint32_t fla
|
|||||||
|
|
||||||
if (queries & Qt::ImCursorRectangle) {
|
if (queries & Qt::ImCursorRectangle) {
|
||||||
const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect();
|
const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect();
|
||||||
const QRect &tRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect);
|
const QRect &windowRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect);
|
||||||
set_cursor_rectangle(tRect.x(), tRect.y(), tRect.width(), tRect.height());
|
const QMargins margins = window->frameMargins();
|
||||||
|
const QRect &surfaceRect = windowRect.translated(margins.left(), margins.top());
|
||||||
|
set_cursor_rectangle(surfaceRect.x(), surfaceRect.y(), surfaceRect.width(), surfaceRect.height());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queries & Qt::ImPreferredLanguage) {
|
if (queries & Qt::ImPreferredLanguage) {
|
||||||
|
@ -1200,7 +1200,7 @@ void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time,
|
|||||||
QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease;
|
QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease;
|
||||||
handleKey(time, type, qtkey, modifiers, code, sym, mNativeModifiers, text);
|
handleKey(time, type, qtkey, modifiers, code, sym, mNativeModifiers, text);
|
||||||
|
|
||||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED && xkb_keymap_key_repeats(mXkbKeymap.get(), code)) {
|
if (state == WL_KEYBOARD_KEY_STATE_PRESSED && xkb_keymap_key_repeats(mXkbKeymap.get(), code) && mRepeatRate > 0) {
|
||||||
mRepeatKey.key = qtkey;
|
mRepeatKey.key = qtkey;
|
||||||
mRepeatKey.code = code;
|
mRepeatKey.code = code;
|
||||||
mRepeatKey.time = time;
|
mRepeatKey.time = time;
|
||||||
@ -1295,34 +1295,40 @@ void QWaylandInputDevice::Touch::touch_down(uint32_t serial,
|
|||||||
mParent->mSerial = serial;
|
mParent->mSerial = serial;
|
||||||
mFocus = window;
|
mFocus = window;
|
||||||
mParent->mQDisplay->setLastInputDevice(mParent, serial, mFocus);
|
mParent->mQDisplay->setLastInputDevice(mParent, serial, mFocus);
|
||||||
mParent->handleTouchPoint(id, wl_fixed_to_double(x), wl_fixed_to_double(y), Qt::TouchPointPressed);
|
QPointF position(wl_fixed_to_double(x), wl_fixed_to_double(y));
|
||||||
|
mParent->handleTouchPoint(id, Qt::TouchPointPressed, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWaylandInputDevice::Touch::touch_up(uint32_t serial, uint32_t time, int32_t id)
|
void QWaylandInputDevice::Touch::touch_up(uint32_t serial, uint32_t time, int32_t id)
|
||||||
{
|
{
|
||||||
Q_UNUSED(serial);
|
Q_UNUSED(serial);
|
||||||
Q_UNUSED(time);
|
Q_UNUSED(time);
|
||||||
mFocus = nullptr;
|
mParent->handleTouchPoint(id, Qt::TouchPointReleased);
|
||||||
mParent->handleTouchPoint(id, 0, 0, Qt::TouchPointReleased);
|
|
||||||
|
|
||||||
// As of Weston 1.5.90 there is no touch_frame after the last touch_up
|
if (allTouchPointsReleased()) {
|
||||||
// (i.e. when the last finger is released). To accommodate for this, issue a
|
mFocus = nullptr;
|
||||||
// touch_frame. This cannot hurt since it is safe to call the touch_frame
|
|
||||||
// handler multiple times when there are no points left.
|
// As of Weston 7.0.0 there is no touch_frame after the last touch_up
|
||||||
if (allTouchPointsReleased())
|
// (i.e. when the last finger is released). To accommodate for this, issue a
|
||||||
|
// touch_frame. This cannot hurt since it is safe to call the touch_frame
|
||||||
|
// handler multiple times when there are no points left.
|
||||||
|
// See: https://gitlab.freedesktop.org/wayland/weston/issues/44
|
||||||
|
// TODO: change logging category to lcQpaWaylandInput in newer versions.
|
||||||
|
qCDebug(lcQpaWayland, "Generating fake frame event to work around Weston bug");
|
||||||
touch_frame();
|
touch_frame();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWaylandInputDevice::Touch::touch_motion(uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y)
|
void QWaylandInputDevice::Touch::touch_motion(uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y)
|
||||||
{
|
{
|
||||||
Q_UNUSED(time);
|
Q_UNUSED(time);
|
||||||
mParent->handleTouchPoint(id, wl_fixed_to_double(x), wl_fixed_to_double(y), Qt::TouchPointMoved);
|
QPointF position(wl_fixed_to_double(x), wl_fixed_to_double(y));
|
||||||
|
mParent->handleTouchPoint(id, Qt::TouchPointMoved, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWaylandInputDevice::Touch::touch_cancel()
|
void QWaylandInputDevice::Touch::touch_cancel()
|
||||||
{
|
{
|
||||||
mPrevTouchPoints.clear();
|
mPendingTouchPoints.clear();
|
||||||
mTouchPoints.clear();
|
|
||||||
|
|
||||||
QWaylandTouchExtension *touchExt = mParent->mQDisplay->touchExtension();
|
QWaylandTouchExtension *touchExt = mParent->mQDisplay->touchExtension();
|
||||||
if (touchExt)
|
if (touchExt)
|
||||||
@ -1331,22 +1337,18 @@ void QWaylandInputDevice::Touch::touch_cancel()
|
|||||||
QWindowSystemInterface::handleTouchCancelEvent(nullptr, mParent->mTouchDevice);
|
QWindowSystemInterface::handleTouchCancelEvent(nullptr, mParent->mTouchDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWaylandInputDevice::handleTouchPoint(int id, double x, double y, Qt::TouchPointState state)
|
void QWaylandInputDevice::handleTouchPoint(int id, Qt::TouchPointState state, const QPointF &surfacePosition)
|
||||||
{
|
{
|
||||||
QWindowSystemInterface::TouchPoint tp;
|
auto end = mTouch->mPendingTouchPoints.end();
|
||||||
|
auto it = std::find_if(mTouch->mPendingTouchPoints.begin(), end, [id](auto tp){ return tp.id == id; });
|
||||||
|
if (it == end) {
|
||||||
|
it = mTouch->mPendingTouchPoints.insert(end, QWindowSystemInterface::TouchPoint());
|
||||||
|
it->id = id;
|
||||||
|
}
|
||||||
|
QWindowSystemInterface::TouchPoint &tp = *it;
|
||||||
|
|
||||||
// Find out the coordinates for Released events.
|
// Only moved and pressed needs to update/set position
|
||||||
bool coordsOk = false;
|
if (state == Qt::TouchPointMoved || state == Qt::TouchPointPressed) {
|
||||||
if (state == Qt::TouchPointReleased)
|
|
||||||
for (int i = 0; i < mTouch->mPrevTouchPoints.count(); ++i)
|
|
||||||
if (mTouch->mPrevTouchPoints.at(i).id == id) {
|
|
||||||
tp.area = mTouch->mPrevTouchPoints.at(i).area;
|
|
||||||
coordsOk = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!coordsOk) {
|
|
||||||
// x and y are surface relative.
|
|
||||||
// We need a global (screen) position.
|
// We need a global (screen) position.
|
||||||
QWaylandWindow *win = mTouch->mFocus;
|
QWaylandWindow *win = mTouch->mFocus;
|
||||||
|
|
||||||
@ -1360,63 +1362,46 @@ void QWaylandInputDevice::handleTouchPoint(int id, double x, double y, Qt::Touch
|
|||||||
|
|
||||||
tp.area = QRectF(0, 0, 8, 8);
|
tp.area = QRectF(0, 0, 8, 8);
|
||||||
QMargins margins = win->frameMargins();
|
QMargins margins = win->frameMargins();
|
||||||
tp.area.moveCenter(win->window()->mapToGlobal(QPoint(x - margins.left(), y - margins.top())));
|
QPointF localPosition = surfacePosition - QPointF(margins.left(), margins.top());
|
||||||
|
// TODO: This doesn't account for high dpi scaling for the delta, but at least it matches
|
||||||
|
// what we have for mouse input.
|
||||||
|
QPointF delta = localPosition - localPosition.toPoint();
|
||||||
|
QPointF globalPosition = win->window()->mapToGlobal(localPosition.toPoint()) + delta;
|
||||||
|
tp.area.moveCenter(globalPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
tp.state = state;
|
tp.state = state;
|
||||||
tp.id = id;
|
|
||||||
tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1;
|
tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1;
|
||||||
mTouch->mTouchPoints.append(tp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QWaylandInputDevice::Touch::allTouchPointsReleased()
|
bool QWaylandInputDevice::Touch::allTouchPointsReleased()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < mTouchPoints.count(); ++i)
|
for (const auto &tp : qAsConst(mPendingTouchPoints)) {
|
||||||
if (mTouchPoints.at(i).state != Qt::TouchPointReleased)
|
if (tp.state != Qt::TouchPointReleased)
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWaylandInputDevice::Touch::releasePoints()
|
void QWaylandInputDevice::Touch::releasePoints()
|
||||||
{
|
{
|
||||||
for (const QWindowSystemInterface::TouchPoint &previousPoint : qAsConst(mPrevTouchPoints)) {
|
if (mPendingTouchPoints.empty())
|
||||||
QWindowSystemInterface::TouchPoint tp = previousPoint;
|
return;
|
||||||
|
|
||||||
|
for (QWindowSystemInterface::TouchPoint &tp : mPendingTouchPoints)
|
||||||
tp.state = Qt::TouchPointReleased;
|
tp.state = Qt::TouchPointReleased;
|
||||||
mTouchPoints.append(tp);
|
|
||||||
}
|
|
||||||
touch_frame();
|
touch_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWaylandInputDevice::Touch::touch_frame()
|
void QWaylandInputDevice::Touch::touch_frame()
|
||||||
{
|
{
|
||||||
// Copy all points, that are in the previous but not in the current list, as stationary.
|
// TODO: early return if no events?
|
||||||
for (int i = 0; i < mPrevTouchPoints.count(); ++i) {
|
|
||||||
const QWindowSystemInterface::TouchPoint &prevPoint(mPrevTouchPoints.at(i));
|
|
||||||
if (prevPoint.state == Qt::TouchPointReleased)
|
|
||||||
continue;
|
|
||||||
bool found = false;
|
|
||||||
for (int j = 0; j < mTouchPoints.count(); ++j)
|
|
||||||
if (mTouchPoints.at(j).id == prevPoint.id) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
QWindowSystemInterface::TouchPoint p = prevPoint;
|
|
||||||
p.state = Qt::TouchPointStationary;
|
|
||||||
mTouchPoints.append(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mTouchPoints.isEmpty()) {
|
|
||||||
mPrevTouchPoints.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QWindow *window = mFocus ? mFocus->window() : nullptr;
|
QWindow *window = mFocus ? mFocus->window() : nullptr;
|
||||||
|
|
||||||
if (mFocus) {
|
if (mFocus) {
|
||||||
const QWindowSystemInterface::TouchPoint &tp = mTouchPoints.last();
|
const QWindowSystemInterface::TouchPoint &tp = mPendingTouchPoints.last();
|
||||||
// When the touch event is received, the global pos is calculated with the margins
|
// When the touch event is received, the global pos is calculated with the margins
|
||||||
// in mind. Now we need to adjust again to get the correct local pos back.
|
// in mind. Now we need to adjust again to get the correct local pos back.
|
||||||
QMargins margins = window->frameMargins();
|
QMargins margins = window->frameMargins();
|
||||||
@ -1425,14 +1410,21 @@ void QWaylandInputDevice::Touch::touch_frame()
|
|||||||
if (mFocus->touchDragDecoration(mParent, localPos, tp.area.center(), tp.state, mParent->modifiers()))
|
if (mFocus->touchDragDecoration(mParent, localPos, tp.area.center(), tp.state, mParent->modifiers()))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QWindowSystemInterface::handleTouchEvent(window, mParent->mTouchDevice, mTouchPoints);
|
|
||||||
|
|
||||||
if (allTouchPointsReleased())
|
QWindowSystemInterface::handleTouchEvent(window, mParent->mTouchDevice, mPendingTouchPoints);
|
||||||
mPrevTouchPoints.clear();
|
|
||||||
else
|
// Prepare state for next frame
|
||||||
mPrevTouchPoints = mTouchPoints;
|
const auto prevTouchPoints = mPendingTouchPoints;
|
||||||
|
mPendingTouchPoints.clear();
|
||||||
|
for (const auto &prevPoint: prevTouchPoints) {
|
||||||
|
// All non-released touch points should be part of the next touch event
|
||||||
|
if (prevPoint.state != Qt::TouchPointReleased) {
|
||||||
|
QWindowSystemInterface::TouchPoint tp = prevPoint;
|
||||||
|
tp.state = Qt::TouchPointStationary; // ... as stationary (unless proven otherwise)
|
||||||
|
mPendingTouchPoints.append(tp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mTouchPoints.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ private:
|
|||||||
uint32_t mSerial = 0;
|
uint32_t mSerial = 0;
|
||||||
|
|
||||||
void seat_capabilities(uint32_t caps) override;
|
void seat_capabilities(uint32_t caps) override;
|
||||||
void handleTouchPoint(int id, double x, double y, Qt::TouchPointState state);
|
void handleTouchPoint(int id, Qt::TouchPointState state, const QPointF &surfacePosition = QPoint());
|
||||||
|
|
||||||
QTouchDevice *mTouchDevice = nullptr;
|
QTouchDevice *mTouchDevice = nullptr;
|
||||||
|
|
||||||
@ -389,8 +389,7 @@ public:
|
|||||||
|
|
||||||
QWaylandInputDevice *mParent = nullptr;
|
QWaylandInputDevice *mParent = nullptr;
|
||||||
QPointer<QWaylandWindow> mFocus;
|
QPointer<QWaylandWindow> mFocus;
|
||||||
QList<QWindowSystemInterface::TouchPoint> mTouchPoints;
|
QList<QWindowSystemInterface::TouchPoint> mPendingTouchPoints;
|
||||||
QList<QWindowSystemInterface::TouchPoint> mPrevTouchPoints;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class QWaylandPointerEvent
|
class QWaylandPointerEvent
|
||||||
|
@ -201,10 +201,13 @@ void QWaylandWindow::initWindow()
|
|||||||
void QWaylandWindow::initializeWlSurface()
|
void QWaylandWindow::initializeWlSurface()
|
||||||
{
|
{
|
||||||
Q_ASSERT(!mSurface);
|
Q_ASSERT(!mSurface);
|
||||||
mSurface.reset(new QWaylandSurface(mDisplay));
|
{
|
||||||
connect(mSurface.data(), &QWaylandSurface::screensChanged,
|
QWriteLocker lock(&mSurfaceLock);
|
||||||
this, &QWaylandWindow::handleScreensChanged);
|
mSurface.reset(new QWaylandSurface(mDisplay));
|
||||||
mSurface->m_window = this;
|
connect(mSurface.data(), &QWaylandSurface::screensChanged,
|
||||||
|
this, &QWaylandWindow::handleScreensChanged);
|
||||||
|
mSurface->m_window = this;
|
||||||
|
}
|
||||||
emit wlSurfaceCreated();
|
emit wlSurfaceCreated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,6 +245,7 @@ void QWaylandWindow::reset(bool sendDestroyEvent)
|
|||||||
mSubSurfaceWindow = nullptr;
|
mSubSurfaceWindow = nullptr;
|
||||||
if (mSurface) {
|
if (mSurface) {
|
||||||
emit wlSurfaceDestroyed();
|
emit wlSurfaceDestroyed();
|
||||||
|
QWriteLocker lock(&mSurfaceLock);
|
||||||
mSurface.reset();
|
mSurface.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -617,10 +621,11 @@ QMutex QWaylandWindow::mFrameSyncMutex;
|
|||||||
|
|
||||||
bool QWaylandWindow::waitForFrameSync(int timeout)
|
bool QWaylandWindow::waitForFrameSync(int timeout)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mFrameSyncMutex);
|
|
||||||
if (!mWaitingForFrameCallback)
|
if (!mWaitingForFrameCallback)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
QMutexLocker locker(&mFrameSyncMutex);
|
||||||
|
|
||||||
wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(mFrameCallback), mFrameQueue);
|
wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(mFrameCallback), mFrameQueue);
|
||||||
mDisplay->dispatchQueueWhile(mFrameQueue, [&]() { return mWaitingForFrameCallback; }, timeout);
|
mDisplay->dispatchQueueWhile(mFrameQueue, [&]() { return mWaitingForFrameCallback; }, timeout);
|
||||||
|
|
||||||
@ -1135,6 +1140,9 @@ void QWaylandWindow::requestUpdate()
|
|||||||
void QWaylandWindow::handleUpdate()
|
void QWaylandWindow::handleUpdate()
|
||||||
{
|
{
|
||||||
// TODO: Should sync subsurfaces avoid requesting frame callbacks?
|
// TODO: Should sync subsurfaces avoid requesting frame callbacks?
|
||||||
|
QReadLocker lock(&mSurfaceLock);
|
||||||
|
if (!mSurface)
|
||||||
|
return;
|
||||||
|
|
||||||
if (mFrameCallback) {
|
if (mFrameCallback) {
|
||||||
wl_callback_destroy(mFrameCallback);
|
wl_callback_destroy(mFrameCallback);
|
||||||
|
@ -53,6 +53,8 @@
|
|||||||
|
|
||||||
#include <QtCore/QWaitCondition>
|
#include <QtCore/QWaitCondition>
|
||||||
#include <QtCore/QMutex>
|
#include <QtCore/QMutex>
|
||||||
|
#include <QtCore/QReadWriteLock>
|
||||||
|
|
||||||
#include <QtGui/QIcon>
|
#include <QtGui/QIcon>
|
||||||
#include <QtCore/QVariant>
|
#include <QtCore/QVariant>
|
||||||
#include <QtCore/QLoggingCategory>
|
#include <QtCore/QLoggingCategory>
|
||||||
@ -274,6 +276,8 @@ private:
|
|||||||
static QMutex mFrameSyncMutex;
|
static QMutex mFrameSyncMutex;
|
||||||
static QWaylandWindow *mMouseGrab;
|
static QWaylandWindow *mMouseGrab;
|
||||||
|
|
||||||
|
QReadWriteLock mSurfaceLock;
|
||||||
|
|
||||||
friend class QWaylandSubSurface;
|
friend class QWaylandSubSurface;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ SUBDIRS += \
|
|||||||
seatv5 \
|
seatv5 \
|
||||||
surface \
|
surface \
|
||||||
wl_connect \
|
wl_connect \
|
||||||
|
xdgdecorationv1 \
|
||||||
xdgoutput \
|
xdgoutput \
|
||||||
xdgshell \
|
xdgshell \
|
||||||
xdgshellv6
|
xdgshellv6
|
||||||
|
@ -30,9 +30,8 @@
|
|||||||
|
|
||||||
#include <QtGui/QRasterWindow>
|
#include <QtGui/QRasterWindow>
|
||||||
#include <QtGui/QOpenGLWindow>
|
#include <QtGui/QOpenGLWindow>
|
||||||
|
|
||||||
//TODO: move?
|
|
||||||
#include <QtGui/QClipboard>
|
#include <QtGui/QClipboard>
|
||||||
|
#include <QtGui/QDrag>
|
||||||
|
|
||||||
using namespace MockCompositor;
|
using namespace MockCompositor;
|
||||||
|
|
||||||
@ -60,6 +59,7 @@ private slots:
|
|||||||
void pasteUtf8();
|
void pasteUtf8();
|
||||||
void destroysPreviousSelection();
|
void destroysPreviousSelection();
|
||||||
void destroysSelectionWithSurface();
|
void destroysSelectionWithSurface();
|
||||||
|
void dragWithoutFocus();
|
||||||
};
|
};
|
||||||
|
|
||||||
void tst_datadevicev1::initTestCase()
|
void tst_datadevicev1::initTestCase()
|
||||||
@ -215,5 +215,22 @@ void tst_datadevicev1::destroysSelectionWithSurface()
|
|||||||
QCOMPOSITOR_TRY_COMPARE(dataDevice()->m_sentSelectionOffers.size(), 0);
|
QCOMPOSITOR_TRY_COMPARE(dataDevice()->m_sentSelectionOffers.size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The application should not crash if it attempts to start a drag operation
|
||||||
|
// when it doesn't have input focus (QTBUG-76368)
|
||||||
|
void tst_datadevicev1::dragWithoutFocus()
|
||||||
|
{
|
||||||
|
QRasterWindow window;
|
||||||
|
window.resize(64, 64);
|
||||||
|
window.show();
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||||
|
|
||||||
|
auto *mimeData = new QMimeData;
|
||||||
|
const QByteArray data("testData");
|
||||||
|
mimeData->setData("text/plain", data);
|
||||||
|
QDrag drag(&window);
|
||||||
|
drag.setMimeData(mimeData);
|
||||||
|
drag.exec();
|
||||||
|
}
|
||||||
|
|
||||||
QCOMPOSITOR_TEST_MAIN(tst_datadevicev1)
|
QCOMPOSITOR_TEST_MAIN(tst_datadevicev1)
|
||||||
#include "tst_datadevicev1.moc"
|
#include "tst_datadevicev1.moc"
|
||||||
|
@ -72,6 +72,7 @@ private slots:
|
|||||||
void usesEnterSerial();
|
void usesEnterSerial();
|
||||||
void focusDestruction();
|
void focusDestruction();
|
||||||
void mousePress();
|
void mousePress();
|
||||||
|
void mousePressFloat();
|
||||||
void simpleAxis_data();
|
void simpleAxis_data();
|
||||||
void simpleAxis();
|
void simpleAxis();
|
||||||
void invalidPointerEvents();
|
void invalidPointerEvents();
|
||||||
@ -208,6 +209,30 @@ void tst_seatv4::mousePress()
|
|||||||
QTRY_VERIFY(window.m_pressed);
|
QTRY_VERIFY(window.m_pressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_seatv4::mousePressFloat()
|
||||||
|
{
|
||||||
|
class Window : public QRasterWindow {
|
||||||
|
public:
|
||||||
|
void mousePressEvent(QMouseEvent *e) override { m_position = e->localPos(); }
|
||||||
|
QPointF m_position;
|
||||||
|
};
|
||||||
|
|
||||||
|
Window window;
|
||||||
|
window.resize(64, 64);
|
||||||
|
window.show();
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||||
|
|
||||||
|
exec([&] {
|
||||||
|
auto *surface = xdgSurface()->m_surface;
|
||||||
|
pointer()->sendEnter(surface, {32.75, 32.25});
|
||||||
|
pointer()->sendButton(client(), BTN_LEFT, 1);
|
||||||
|
pointer()->sendButton(client(), BTN_LEFT, 0);
|
||||||
|
});
|
||||||
|
QMargins m = window.frameMargins();
|
||||||
|
QPointF pressedPosition(32.75 -m.left(), 32.25 - m.top());
|
||||||
|
QTRY_COMPARE(window.m_position, pressedPosition);
|
||||||
|
}
|
||||||
|
|
||||||
void tst_seatv4::simpleAxis_data()
|
void tst_seatv4::simpleAxis_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<uint>("axis");
|
QTest::addColumn<uint>("axis");
|
||||||
|
@ -68,6 +68,7 @@ private slots:
|
|||||||
// Touch tests
|
// Touch tests
|
||||||
void createsTouch();
|
void createsTouch();
|
||||||
void singleTap();
|
void singleTap();
|
||||||
|
void singleTapFloat();
|
||||||
};
|
};
|
||||||
|
|
||||||
void tst_seatv5::bindsToSeat()
|
void tst_seatv5::bindsToSeat()
|
||||||
@ -431,5 +432,36 @@ void tst_seatv5::singleTap()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_seatv5::singleTapFloat()
|
||||||
|
{
|
||||||
|
TouchWindow window;
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||||
|
|
||||||
|
exec([=] {
|
||||||
|
auto *t = touch();
|
||||||
|
auto *c = client();
|
||||||
|
t->sendDown(xdgToplevel()->surface(), {32.75, 32.25}, 1);
|
||||||
|
t->sendFrame(c);
|
||||||
|
t->sendUp(c, 1);
|
||||||
|
t->sendFrame(c);
|
||||||
|
});
|
||||||
|
|
||||||
|
QTRY_VERIFY(!window.m_events.empty());
|
||||||
|
{
|
||||||
|
auto e = window.m_events.takeFirst();
|
||||||
|
QCOMPARE(e.type, QEvent::TouchBegin);
|
||||||
|
QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointPressed);
|
||||||
|
QCOMPARE(e.touchPoints.length(), 1);
|
||||||
|
QCOMPARE(e.touchPoints.first().pos(), QPointF(32.75-window.frameMargins().left(), 32.25-window.frameMargins().top()));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto e = window.m_events.takeFirst();
|
||||||
|
QCOMPARE(e.type, QEvent::TouchEnd);
|
||||||
|
QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointReleased);
|
||||||
|
QCOMPARE(e.touchPoints.length(), 1);
|
||||||
|
QCOMPARE(e.touchPoints.first().pos(), QPointF(32.75-window.frameMargins().left(), 32.25-window.frameMargins().top()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QCOMPOSITOR_TEST_MAIN(tst_seatv5)
|
QCOMPOSITOR_TEST_MAIN(tst_seatv5)
|
||||||
#include "tst_seatv5.moc"
|
#include "tst_seatv5.moc"
|
||||||
|
186
tests/auto/wayland/xdgdecorationv1/tst_xdgdecorationv1.cpp
Normal file
186
tests/auto/wayland/xdgdecorationv1/tst_xdgdecorationv1.cpp
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2018 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the test suite of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "mockcompositor.h"
|
||||||
|
|
||||||
|
#include <qwayland-server-xdg-decoration-unstable-v1.h>
|
||||||
|
|
||||||
|
#include <QtGui/QRasterWindow>
|
||||||
|
#include <QtGui/QOpenGLWindow>
|
||||||
|
#include <QtGui/QClipboard>
|
||||||
|
#include <QtCore/private/qcore_unix_p.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
using namespace MockCompositor;
|
||||||
|
|
||||||
|
constexpr int xdgDecorationVersion = 1; // protocol VERSION, not the name suffix (_v1)
|
||||||
|
|
||||||
|
class XdgDecorationManagerV1;
|
||||||
|
class XdgToplevelDecorationV1 : public QObject, public QtWaylandServer::zxdg_toplevel_decoration_v1
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XdgToplevelDecorationV1(XdgDecorationManagerV1 *manager, XdgToplevel *toplevel, int id, int version)
|
||||||
|
: zxdg_toplevel_decoration_v1(toplevel->resource()->client(), id, version)
|
||||||
|
, m_manager(manager)
|
||||||
|
, m_toplevel(toplevel)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void sendConfigure(mode mode)
|
||||||
|
{
|
||||||
|
if (!m_configureSent) {
|
||||||
|
// Attaching buffers before the configure is a protocol error
|
||||||
|
QVERIFY(!m_toplevel->surface()->m_pending.buffer);
|
||||||
|
QVERIFY(!m_toplevel->surface()->m_committed.buffer);
|
||||||
|
}
|
||||||
|
send_configure(mode);
|
||||||
|
m_configureSent = true;
|
||||||
|
}
|
||||||
|
void zxdg_toplevel_decoration_v1_destroy(Resource *resource) override
|
||||||
|
{
|
||||||
|
wl_resource_destroy(resource->handle);
|
||||||
|
}
|
||||||
|
void zxdg_toplevel_decoration_v1_destroy_resource(Resource *resource) override;
|
||||||
|
void zxdg_toplevel_decoration_v1_set_mode(Resource *resource, uint32_t mode) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(resource);
|
||||||
|
m_unsetModeRequested = false;
|
||||||
|
m_requestedMode = XdgToplevelDecorationV1::mode(mode);
|
||||||
|
}
|
||||||
|
void zxdg_toplevel_decoration_v1_unset_mode(Resource *resource) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(resource);
|
||||||
|
m_unsetModeRequested = true;
|
||||||
|
m_requestedMode = mode(0);
|
||||||
|
}
|
||||||
|
XdgDecorationManagerV1 *m_manager = nullptr;
|
||||||
|
XdgToplevel *m_toplevel = nullptr;
|
||||||
|
mode m_requestedMode = mode(0);
|
||||||
|
bool m_unsetModeRequested = false;
|
||||||
|
bool m_configureSent = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class XdgDecorationManagerV1 : public Global, public QtWaylandServer::zxdg_decoration_manager_v1
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XdgDecorationManagerV1(CoreCompositor *compositor, int version = 1)
|
||||||
|
: QtWaylandServer::zxdg_decoration_manager_v1(compositor->m_display, version)
|
||||||
|
, m_version(version)
|
||||||
|
{}
|
||||||
|
bool isClean() override { return m_decorations.empty(); }
|
||||||
|
XdgToplevelDecorationV1 *decorationFor(XdgToplevel *toplevel)
|
||||||
|
{
|
||||||
|
return m_decorations.value(toplevel, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int m_version = 1; // TODO: Remove on libwayland upgrade
|
||||||
|
QMap<XdgToplevel *, XdgToplevelDecorationV1 *> m_decorations;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void zxdg_decoration_manager_v1_destroy(Resource *resource) override
|
||||||
|
{
|
||||||
|
//TODO: Should the decorations be destroyed at this point?
|
||||||
|
wl_resource_destroy(resource->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void zxdg_decoration_manager_v1_get_toplevel_decoration(Resource *resource, uint32_t id, ::wl_resource *toplevelResource) override
|
||||||
|
{
|
||||||
|
auto *toplevel = fromResource<XdgToplevel>(toplevelResource);
|
||||||
|
QVERIFY(toplevel);
|
||||||
|
QVERIFY(!decorationFor(toplevel));
|
||||||
|
|
||||||
|
// Attaching buffers before the configure is a protocol error
|
||||||
|
QVERIFY(!toplevel->surface()->m_pending.buffer);
|
||||||
|
QVERIFY(!toplevel->surface()->m_committed.buffer);
|
||||||
|
|
||||||
|
m_decorations[toplevel] = new XdgToplevelDecorationV1(this, toplevel, id, resource->version());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void XdgToplevelDecorationV1::zxdg_toplevel_decoration_v1_destroy_resource(QtWaylandServer::zxdg_toplevel_decoration_v1::Resource *resource)
|
||||||
|
{
|
||||||
|
Q_UNUSED(resource);
|
||||||
|
int removed = m_manager->m_decorations.remove(m_toplevel);
|
||||||
|
Q_ASSERT(removed == 1);
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
class XdgDecorationCompositor : public DefaultCompositor {
|
||||||
|
public:
|
||||||
|
explicit XdgDecorationCompositor()
|
||||||
|
{
|
||||||
|
exec([this] {
|
||||||
|
m_config.autoConfigure = true;
|
||||||
|
add<XdgDecorationManagerV1>(xdgDecorationVersion);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
XdgToplevelDecorationV1 *toplevelDecoration(int i = 0) {
|
||||||
|
return get<XdgDecorationManagerV1>()->decorationFor(xdgToplevel(i));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class tst_xdgdecorationv1 : public QObject, private XdgDecorationCompositor
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); }
|
||||||
|
void clientSidePreferredByCompositor();
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_xdgdecorationv1::initTestCase()
|
||||||
|
{
|
||||||
|
if (qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_WINDOWDECORATION"))
|
||||||
|
QSKIP("This test doesn't make sense when QT_WAYLAND_DISABLE_WINDOWDECORATION is set in the environment");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_xdgdecorationv1::clientSidePreferredByCompositor()
|
||||||
|
{
|
||||||
|
QRasterWindow window;
|
||||||
|
window.show();
|
||||||
|
QCOMPOSITOR_TRY_COMPARE(get<XdgDecorationManagerV1>()->resourceMap().size(), 1);
|
||||||
|
QCOMPOSITOR_TRY_COMPARE(get<XdgDecorationManagerV1>()->resourceMap().first()->version(), xdgDecorationVersion);
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(toplevelDecoration()); // The client creates a toplevel object
|
||||||
|
|
||||||
|
// Check that we don't assume decorations before the server has configured them
|
||||||
|
QVERIFY(window.frameMargins().isNull());
|
||||||
|
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(toplevelDecoration()->m_unsetModeRequested);
|
||||||
|
QVERIFY(window.frameMargins().isNull()); // We're still waiting for a configure
|
||||||
|
exec([=] {
|
||||||
|
toplevelDecoration()->sendConfigure(XdgToplevelDecorationV1::mode_client_side);
|
||||||
|
xdgToplevel()->sendCompleteConfigure();
|
||||||
|
});
|
||||||
|
QTRY_VERIFY(!window.frameMargins().isNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
QCOMPOSITOR_TEST_MAIN(tst_xdgdecorationv1)
|
||||||
|
#include "tst_xdgdecorationv1.moc"
|
7
tests/auto/wayland/xdgdecorationv1/xdgdecorationv1.pro
Normal file
7
tests/auto/wayland/xdgdecorationv1/xdgdecorationv1.pro
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
include (../shared/shared.pri)
|
||||||
|
|
||||||
|
WAYLANDSERVERSOURCES += \
|
||||||
|
$$PWD/../../../../src/3rdparty/protocol/xdg-decoration-unstable-v1.xml
|
||||||
|
|
||||||
|
TARGET = tst_xdgdecorationv1
|
||||||
|
SOURCES += tst_xdgdecorationv1.cpp
|
Loading…
x
Reference in New Issue
Block a user