summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/client/qwaylandshellsurface_p.h9
-rw-r--r--src/client/qwaylandwindow.cpp120
-rw-r--r--src/client/qwaylandwindow_p.h36
-rw-r--r--src/client/qwaylandwlshellsurface.cpp81
-rw-r--r--src/client/qwaylandwlshellsurface_p.h21
-rw-r--r--src/client/qwaylandxdgshellv6.cpp119
-rw-r--r--src/client/qwaylandxdgshellv6_p.h14
-rw-r--r--src/client/qwaylandxdgshellv6integration.cpp14
-rw-r--r--src/client/qwaylandxdgshellv6integration_p.h1
-rw-r--r--src/client/qwaylandxdgsurface.cpp149
-rw-r--r--src/client/qwaylandxdgsurface_p.h21
-rw-r--r--src/plugins/decorations/bradient/main.cpp7
-rw-r--r--src/plugins/shellintegration/ivi-shell/qwaylandivisurface.cpp8
-rw-r--r--src/plugins/shellintegration/ivi-shell/qwaylandivisurface_p.h2
-rw-r--r--tests/auto/client/client/tst_client.cpp20
-rw-r--r--tests/auto/client/shared/mockcompositor.cpp5
-rw-r--r--tests/auto/client/shared/mockcompositor.h14
-rw-r--r--tests/auto/client/shared/mockxdgshellv6.cpp35
-rw-r--r--tests/auto/client/shared/mockxdgshellv6.h5
-rw-r--r--tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp189
20 files changed, 591 insertions, 279 deletions
diff --git a/src/client/qwaylandshellsurface_p.h b/src/client/qwaylandshellsurface_p.h
index fdc309a63..6b6bb9e84 100644
--- a/src/client/qwaylandshellsurface_p.h
+++ b/src/client/qwaylandshellsurface_p.h
@@ -96,12 +96,9 @@ public:
inline QWaylandWindow *window() { return m_window; }
virtual void setType(Qt::WindowType type, QWaylandWindow *transientParent) = 0;
-
-protected:
- virtual void setMaximized() {}
- virtual void setFullscreen() {}
- virtual void setNormal() {}
- virtual void setMinimized() {}
+ virtual void applyConfigure() {}
+ virtual void requestWindowStates(Qt::WindowStates states) {Q_UNUSED(states);}
+ virtual bool wantsDecorations() const { return false; }
private:
QWaylandWindow *m_window = nullptr;
diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp
index 6154689df..21c9f82b0 100644
--- a/src/client/qwaylandwindow.cpp
+++ b/src/client/qwaylandwindow.cpp
@@ -200,11 +200,8 @@ void QWaylandWindow::initWindow()
else
setGeometry_helper(window()->geometry());
setMask(window()->mask());
- // setWindowStateInternal is a no-op if the argument is equal to mState,
- // but since we're creating the shellsurface only now we reset mState to
- // make sure the state gets sent out to the compositor
- mState = Qt::WindowNoState;
- setWindowStateInternal(window()->windowStates());
+ if (mShellSurface)
+ mShellSurface->requestWindowStates(window()->windowStates());
handleContentOrientationChange(window()->contentOrientation());
mFlags = window()->flags();
}
@@ -333,7 +330,16 @@ void QWaylandWindow::setGeometry(const QRect &rect)
sendExposeEvent(QRect(QPoint(), geometry().size()));
}
+void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset)
+{
+ QMargins margins = frameMargins();
+ int widthWithoutMargins = qMax(sizeWithMargins.width() - (margins.left()+margins.right()), 1);
+ int heightWithoutMargins = qMax(sizeWithMargins.height() - (margins.top()+margins.bottom()), 1);
+ QRect geometry(QPoint(), QSize(widthWithoutMargins, heightWithoutMargins));
+ mOffset += offset;
+ setGeometry(geometry);
+}
void QWaylandWindow::sendExposeEvent(const QRect &rect)
{
@@ -421,46 +427,24 @@ void QWaylandWindow::setMask(const QRegion &mask)
wl_surface::commit();
}
-void QWaylandWindow::configure(uint32_t edges, int32_t width, int32_t height)
+void QWaylandWindow::applyConfigureWhenPossible()
{
QMutexLocker resizeLocker(&mResizeLock);
- mConfigure.edges |= edges;
- mConfigure.width = width;
- mConfigure.height = height;
-
- if (!mRequestResizeSent && !mConfigure.isEmpty()) {
- mRequestResizeSent= true;
- QMetaObject::invokeMethod(this, "requestResize", Qt::QueuedConnection);
+ if (!mWaitingToApplyConfigure) {
+ mWaitingToApplyConfigure = true;
+ QMetaObject::invokeMethod(this, "applyConfigure", Qt::QueuedConnection);
}
}
-void QWaylandWindow::doResize()
+void QWaylandWindow::doApplyConfigure()
{
- if (mConfigure.isEmpty()) {
+ if (!mWaitingToApplyConfigure)
return;
- }
-
- int widthWithoutMargins = qMax(mConfigure.width-(frameMargins().left() +frameMargins().right()),1);
- int heightWithoutMargins = qMax(mConfigure.height-(frameMargins().top()+frameMargins().bottom()),1);
- widthWithoutMargins = qMax(widthWithoutMargins, window()->minimumSize().width());
- heightWithoutMargins = qMax(heightWithoutMargins, window()->minimumSize().height());
- QRect geometry = QRect(0,0, widthWithoutMargins, heightWithoutMargins);
-
- int x = 0;
- int y = 0;
- QSize size = this->geometry().size();
- if (mConfigure.edges & WL_SHELL_SURFACE_RESIZE_LEFT) {
- x = size.width() - geometry.width();
- }
- if (mConfigure.edges & WL_SHELL_SURFACE_RESIZE_TOP) {
- y = size.height() - geometry.height();
- }
- mOffset += QPoint(x, y);
-
- setGeometry(geometry);
+ if (mShellSurface)
+ mShellSurface->applyConfigure();
- mConfigure.clear();
+ mWaitingToApplyConfigure = false;
}
void QWaylandWindow::setCanResize(bool canResize)
@@ -472,8 +456,8 @@ void QWaylandWindow::setCanResize(bool canResize)
if (mResizeDirty) {
QWindowSystemInterface::handleGeometryChange(window(), geometry());
}
- if (!mConfigure.isEmpty()) {
- doResize();
+ if (mWaitingToApplyConfigure) {
+ doApplyConfigure();
sendExposeEvent(QRect(QPoint(), geometry().size()));
} else if (mResizeDirty) {
mResizeDirty = false;
@@ -482,15 +466,13 @@ void QWaylandWindow::setCanResize(bool canResize)
}
}
-void QWaylandWindow::requestResize()
+void QWaylandWindow::applyConfigure()
{
QMutexLocker lock(&mResizeLock);
- if (mCanResize || !mSentInitialResize) {
- doResize();
- }
+ if (mCanResize || !mSentInitialResize)
+ doApplyConfigure();
- mRequestResizeSent = false;
lock.unlock();
sendExposeEvent(QRect(QPoint(), geometry().size()));
QWindowSystemInterface::flushWindowSystemEvents();
@@ -672,10 +654,10 @@ void QWaylandWindow::setOrientationMask(Qt::ScreenOrientations mask)
mShellSurface->setContentOrientationMask(mask);
}
-void QWaylandWindow::setWindowState(Qt::WindowStates state)
+void QWaylandWindow::setWindowState(Qt::WindowStates states)
{
- if (setWindowStateInternal(state))
- QWindowSystemInterface::flushWindowSystemEvents(); // Required for oldState to work on WindowStateChanged
+ if (mShellSurface)
+ mShellSurface->requestWindowStates(states);
}
void QWaylandWindow::setWindowFlags(Qt::WindowFlags flags)
@@ -689,20 +671,6 @@ void QWaylandWindow::setWindowFlags(Qt::WindowFlags flags)
bool QWaylandWindow::createDecoration()
{
- // so far only xdg-shell support this "unminimize" trick, may be moved elsewhere
- if (mState & Qt::WindowMinimized) {
- QWaylandXdgSurface *xdgSurface = qobject_cast<QWaylandXdgSurface *>(mShellSurface);
- if ( xdgSurface ) {
- Qt::WindowStates states;
- if (xdgSurface->isFullscreen())
- states |= Qt::WindowFullScreen;
- if (xdgSurface->isMaximized())
- states |= Qt::WindowMaximized;
-
- setWindowStateInternal(states);
- }
- }
-
if (!mDisplay->supportsWindowDecoration())
return false;
@@ -719,12 +687,14 @@ bool QWaylandWindow::createDecoration()
default:
break;
}
- if (mFlags & Qt::FramelessWindowHint || isFullscreen())
+ if (mFlags & Qt::FramelessWindowHint)
decoration = false;
if (mFlags & Qt::BypassWindowManagerHint)
decoration = false;
if (mSubSurfaceWindow)
decoration = false;
+ if (mShellSurface && !mShellSurface->wantsDecorations())
+ decoration = false;
bool hadDecoration = mWindowDecoration;
if (decoration && !decorationPluginFailed) {
@@ -970,31 +940,11 @@ bool QWaylandWindow::setMouseGrabEnabled(bool grab)
return true;
}
-bool QWaylandWindow::setWindowStateInternal(Qt::WindowStates state)
+void QWaylandWindow::handleWindowStatesChanged(Qt::WindowStates states)
{
- if (mState == state) {
- return false;
- }
-
- // As of february 2013 QWindow::setWindowState sets the new state value after
- // QPlatformWindow::setWindowState returns, so we cannot rely on QWindow::windowState
- // here. We use then this mState variable.
- mState = state;
-
- if (mShellSurface) {
- createDecoration();
- if (state & Qt::WindowMaximized)
- mShellSurface->setMaximized();
- if (state & Qt::WindowFullScreen)
- mShellSurface->setFullscreen();
- if (state & Qt::WindowMinimized)
- mShellSurface->setMinimized();
- if (!state)
- mShellSurface->setNormal();
- }
-
- QWindowSystemInterface::handleWindowStateChanged(window(), mState);
- return true;
+ createDecoration();
+ QWindowSystemInterface::handleWindowStateChanged(window(), states, mLastReportedWindowStates);
+ mLastReportedWindowStates = states;
}
void QWaylandWindow::sendProperty(const QString &name, const QVariant &value)
diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h
index 3324bf700..55f3a515f 100644
--- a/src/client/qwaylandwindow_p.h
+++ b/src/client/qwaylandwindow_p.h
@@ -77,23 +77,6 @@ class QWaylandScreen;
class QWaylandShmBackingStore;
class QWaylandPointerEvent;
-class Q_WAYLAND_CLIENT_EXPORT QWaylandWindowConfigure
-{
-public:
- QWaylandWindowConfigure()
- { }
-
- void clear()
- { width = height = edges = 0; }
-
- bool isEmpty() const
- { return !height || !width; }
-
- int width = 0;
- int height = 0;
- uint32_t edges = 0;
-};
-
class Q_WAYLAND_CLIENT_EXPORT QWaylandWindow : public QObject, public QPlatformWindow, public QtWayland::wl_surface
{
Q_OBJECT
@@ -118,8 +101,9 @@ public:
void setWindowIcon(const QIcon &icon) override;
void setGeometry(const QRect &rect) override;
+ void resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset = {0, 0});
- void configure(uint32_t edges, int32_t width, int32_t height);
+ void applyConfigureWhenPossible(); //rename to possible?
using QtWayland::wl_surface::attach;
void attach(QWaylandBuffer *buffer, int x, int y);
@@ -145,8 +129,9 @@ public:
void handleContentOrientationChange(Qt::ScreenOrientation orientation) override;
void setOrientationMask(Qt::ScreenOrientations mask);
- void setWindowState(Qt::WindowStates state) override;
+ void setWindowState(Qt::WindowStates states) override;
void setWindowFlags(Qt::WindowFlags flags) override;
+ void handleWindowStatesChanged(Qt::WindowStates states);
void raise() override;
void lower() override;
@@ -170,9 +155,6 @@ public:
bool createDecoration();
- inline bool isMaximized() const { return mState & Qt::WindowMaximized; }
- inline bool isFullscreen() const { return mState & Qt::WindowFullScreen; }
-
#if QT_CONFIG(cursor)
void setMouseCursor(QWaylandInputDevice *device, const QCursor &cursor);
void restoreMouseCursor(QWaylandInputDevice *device);
@@ -181,7 +163,7 @@ public:
QWaylandWindow *transientParent() const;
QMutex *resizeMutex() { return &mResizeLock; }
- void doResize();
+ void doApplyConfigure();
void setCanResize(bool canResize);
bool setMouseGrabEnabled(bool grab) override;
@@ -206,7 +188,7 @@ public:
void requestUpdate() override;
public slots:
- void requestResize();
+ void applyConfigure();
protected:
void surface_enter(struct ::wl_output *output) override;
@@ -228,8 +210,7 @@ protected:
QWaitCondition mFrameSyncWait;
QMutex mResizeLock;
- QWaylandWindowConfigure mConfigure;
- bool mRequestResizeSent = false;
+ bool mWaitingToApplyConfigure = false;
bool mCanResize = true;
bool mResizeDirty = false;
bool mResizeAfterSwap;
@@ -241,9 +222,9 @@ protected:
QIcon mWindowIcon;
- Qt::WindowStates mState = Qt::WindowNoState;
Qt::WindowFlags mFlags;
QRegion mMask;
+ Qt::WindowStates mLastReportedWindowStates = Qt::WindowNoState;
QWaylandShmBackingStore *mBackingStore = nullptr;
@@ -251,7 +232,6 @@ private slots:
void handleScreenRemoved(QScreen *qScreen);
private:
- bool setWindowStateInternal(Qt::WindowStates flags);
void setGeometry_helper(const QRect &rect);
void initWindow();
void initializeWlSurface();
diff --git a/src/client/qwaylandwlshellsurface.cpp b/src/client/qwaylandwlshellsurface.cpp
index 098eddcbc..3601fd437 100644
--- a/src/client/qwaylandwlshellsurface.cpp
+++ b/src/client/qwaylandwlshellsurface.cpp
@@ -121,35 +121,70 @@ void QWaylandWlShellSurface::sendProperty(const QString &name, const QVariant &v
m_extendedWindow->updateGenericProperty(name, value);
}
-void QWaylandWlShellSurface::setMaximized()
+void QWaylandWlShellSurface::applyConfigure()
{
- m_maximized = true;
- m_size = m_window->window()->geometry().size();
- set_maximized(nullptr);
+ if ((m_pending.states & (Qt::WindowMaximized|Qt::WindowFullScreen))
+ && !(m_applied.states & (Qt::WindowMaximized|Qt::WindowFullScreen))) {
+ m_normalSize = m_window->window()->frameGeometry().size();
+ }
+
+ if (m_pending.states != m_applied.states)
+ m_window->handleWindowStatesChanged(m_pending.states);
+
+ if (!m_pending.size.isEmpty()) {
+ int x = 0;
+ int y = 0;
+ if (m_pending.edges & resize_left)
+ x = m_applied.size.width() - m_pending.size.width();
+ if (m_pending.edges & resize_top)
+ y = m_applied.size.height() - m_pending.size.height();
+ QPoint offset(x, y);
+ m_window->resizeFromApplyConfigure(m_pending.size, offset);
+ } else if (m_pending.size.isValid() && !m_normalSize.isEmpty()) {
+ m_window->resizeFromApplyConfigure(m_normalSize);
+ }
+
+ m_applied = m_pending;
}
-void QWaylandWlShellSurface::setFullscreen()
+bool QWaylandWlShellSurface::wantsDecorations() const
{
- m_fullscreen = true;
- m_size = m_window->window()->geometry().size();
- set_fullscreen(WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, nullptr);
+ return !(m_pending.states & Qt::WindowFullScreen);
}
-void QWaylandWlShellSurface::setNormal()
+void QWaylandWlShellSurface::requestWindowStates(Qt::WindowStates states)
{
- if (m_fullscreen || m_maximized) {
- m_fullscreen = m_maximized = false;
- setTopLevel();
- QMargins m = m_window->frameMargins();
- m_window->configure(0, m_size.width() + m.left() + m.right(), m_size.height() + m.top() + m.bottom());
+ // On wl-shell the client is in charge of states, so diff from the pending state
+ Qt::WindowStates changedStates = m_pending.states ^ states;
+ Qt::WindowStates addedStates = changedStates & states;
+
+ if (addedStates & Qt::WindowMinimized)
+ qCWarning(lcQpaWayland) << "Minimizing is not supported on wl-shell. Consider using xdg-shell instead.";
+
+ if (addedStates & Qt::WindowMaximized) {
+ set_maximized(nullptr);
+ m_window->applyConfigureWhenPossible();
}
-}
-void QWaylandWlShellSurface::setMinimized()
-{
- qCWarning(lcQpaWayland) << "Minimization is not supported on wl-shell. Consider using xdg-shell instead.";
+ if (addedStates & Qt::WindowFullScreen) {
+ set_fullscreen(WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, nullptr);
+ m_window->applyConfigureWhenPossible();
+ }
+
+ bool isNormal = ~states & (Qt::WindowMaximized | Qt::WindowFullScreen);
+ if (isNormal && (changedStates & (Qt::WindowMaximized | Qt::WindowFullScreen))) {
+ setTopLevel(); // set normal window
+ // There's usually no configure event after this, so just clear the rest of the pending
+ // configure here and queue the applyConfigure call
+ m_pending.size = {0, 0};
+ m_pending.edges = resize_none;
+ m_window->applyConfigureWhenPossible();
+ }
+
+ m_pending.states = states & ~Qt::WindowMinimized;
}
+
void QWaylandWlShellSurface::setTopLevel()
{
set_toplevel();
@@ -230,11 +265,13 @@ void QWaylandWlShellSurface::shell_surface_ping(uint32_t serial)
pong(serial);
}
-void QWaylandWlShellSurface::shell_surface_configure(uint32_t edges,
- int32_t width,
- int32_t height)
+void QWaylandWlShellSurface::shell_surface_configure(uint32_t edges, int32_t width, int32_t height)
{
- m_window->configure(edges, width, height);
+ m_pending.size = QSize(width, height);
+ m_pending.edges = static_cast<enum resize>(edges);
+ if (m_pending.edges && !m_pending.size.isEmpty())
+ m_normalSize = m_pending.size;
+ m_window->applyConfigureWhenPossible();
}
void QWaylandWlShellSurface::shell_surface_popup_done()
diff --git a/src/client/qwaylandwlshellsurface_p.h b/src/client/qwaylandwlshellsurface_p.h
index 497ec6043..ca81dd685 100644
--- a/src/client/qwaylandwlshellsurface_p.h
+++ b/src/client/qwaylandwlshellsurface_p.h
@@ -93,21 +93,26 @@ public:
void sendProperty(const QString &name, const QVariant &value) override;
void setType(Qt::WindowType type, QWaylandWindow *transientParent) override;
+ void applyConfigure() override;
+ bool wantsDecorations() const override;
-private:
- void setMaximized() override;
- void setFullscreen() override;
- void setNormal() override;
- void setMinimized() override;
+protected:
+ void requestWindowStates(Qt::WindowStates states) override;
+private:
void setTopLevel();
void updateTransientParent(QWindow *parent);
void setPopup(QWaylandWindow *parent, QWaylandInputDevice *device, uint serial);
QWaylandWindow *m_window = nullptr;
- bool m_maximized = false;
- bool m_fullscreen = false;
- QSize m_size;
+ struct {
+ Qt::WindowStates states = Qt::WindowNoState;
+ QSize size;
+ enum resize edges = resize_none;
+ } m_applied, m_pending;
+ QSize m_normalSize;
+ // There's really no need to have pending and applied state on wl-shell, but we do it just to
+ // keep the different shell implementations more similar.
QWaylandExtendedSurface *m_extendedWindow = nullptr;
void shell_surface_ping(uint32_t serial) override;
diff --git a/src/client/qwaylandxdgshellv6.cpp b/src/client/qwaylandxdgshellv6.cpp
index a166a3bc9..beabddbbb 100644
--- a/src/client/qwaylandxdgshellv6.cpp
+++ b/src/client/qwaylandxdgshellv6.cpp
@@ -46,8 +46,6 @@
#include "qwaylandscreen_p.h"
#include "qwaylandabstractdecoration_p.h"
-#include <QtCore/QDebug>
-
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@@ -56,32 +54,65 @@ QWaylandXdgSurfaceV6::Toplevel::Toplevel(QWaylandXdgSurfaceV6 *xdgSurface)
: QtWayland::zxdg_toplevel_v6(xdgSurface->get_toplevel())
, m_xdgSurface(xdgSurface)
{
+ requestWindowStates(xdgSurface->window()->window()->windowStates());
}
QWaylandXdgSurfaceV6::Toplevel::~Toplevel()
{
+ if (m_applied.states & Qt::WindowActive) {
+ QWaylandWindow *window = m_xdgSurface->window();
+ window->display()->handleWindowDeactivated(window);
+ }
if (isInitialized())
destroy();
}
void QWaylandXdgSurfaceV6::Toplevel::applyConfigure()
{
- //TODO: resize, activate etc
- m_xdgSurface->m_window->configure(0, m_configureState.width, m_configureState.height);
+ if (!(m_applied.states & (Qt::WindowMaximized|Qt::WindowFullScreen)))
+ m_normalSize = m_xdgSurface->m_window->window()->frameGeometry().size();
+
+ if (m_pending.size.isEmpty() && !m_normalSize.isEmpty())
+ m_pending.size = m_normalSize;
+
+ if ((m_pending.states & Qt::WindowActive) && !(m_applied.states & Qt::WindowActive))
+ m_xdgSurface->m_window->display()->handleWindowActivated(m_xdgSurface->m_window);
+
+ if (!(m_pending.states & Qt::WindowActive) && (m_applied.states & Qt::WindowActive))
+ m_xdgSurface->m_window->display()->handleWindowDeactivated(m_xdgSurface->m_window);
+
+ // TODO: none of the other plugins send WindowActive either, but is it on purpose?
+ Qt::WindowStates statesWithoutActive = m_pending.states & ~Qt::WindowActive;
+
+ m_xdgSurface->m_window->handleWindowStatesChanged(statesWithoutActive);
+ m_xdgSurface->m_window->resizeFromApplyConfigure(m_pending.size);
+ m_applied = m_pending;
}
void QWaylandXdgSurfaceV6::Toplevel::zxdg_toplevel_v6_configure(int32_t width, int32_t height, wl_array *states)
{
- m_configureState.width = width;
- m_configureState.height = height;
+ m_pending.size = QSize(width, height);
- uint32_t *state = reinterpret_cast<uint32_t *>(states->data);
+ auto *xdgStates = static_cast<uint32_t *>(states->data);
size_t numStates = states->size / sizeof(uint32_t);
- m_configureState.states.reserve(numStates);
- m_configureState.states.clear();
- for (size_t i = 0; i < numStates; i++)
- m_configureState.states << state[i];
+ m_pending.states = Qt::WindowNoState;
+
+ for (size_t i = 0; i < numStates; i++) {
+ switch (xdgStates[i]) {
+ case ZXDG_TOPLEVEL_V6_STATE_ACTIVATED:
+ m_pending.states |= Qt::WindowActive;
+ break;
+ case ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED:
+ m_pending.states |= Qt::WindowMaximized;
+ break;
+ case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN:
+ m_pending.states |= Qt::WindowFullScreen;
+ break;
+ default:
+ break;
+ }
+ }
}
void QWaylandXdgSurfaceV6::Toplevel::zxdg_toplevel_v6_close()
@@ -89,6 +120,32 @@ void QWaylandXdgSurfaceV6::Toplevel::zxdg_toplevel_v6_close()
m_xdgSurface->m_window->window()->close();
}
+void QWaylandXdgSurfaceV6::Toplevel::requestWindowStates(Qt::WindowStates states)
+{
+ // Re-send what's different from the applied state
+ Qt::WindowStates changedStates = m_applied.states ^ states;
+
+ if (changedStates & Qt::WindowMaximized) {
+ if (states & Qt::WindowMaximized)
+ set_maximized();
+ else
+ unset_maximized();
+ }
+
+ if (changedStates & Qt::WindowFullScreen) {
+ if (states & Qt::WindowFullScreen)
+ set_fullscreen(nullptr);
+ else
+ unset_fullscreen();
+ }
+
+ // Minimized state is not reported by the protocol, so always send it
+ if (states & Qt::WindowMinimized) {
+ set_minimized();
+ m_xdgSurface->window()->handleWindowStatesChanged(states & ~Qt::WindowMinimized);
+ }
+}
+
QWaylandXdgSurfaceV6::Popup::Popup(QWaylandXdgSurfaceV6 *xdgSurface, QWaylandXdgSurfaceV6 *parent,
QtWayland::zxdg_positioner_v6 *positioner)
: zxdg_popup_v6(xdgSurface->get_popup(parent->object(), positioner->object()))
@@ -104,7 +161,6 @@ QWaylandXdgSurfaceV6::Popup::~Popup()
void QWaylandXdgSurfaceV6::Popup::applyConfigure()
{
-
}
void QWaylandXdgSurfaceV6::Popup::zxdg_popup_v6_popup_done()
@@ -187,6 +243,34 @@ bool QWaylandXdgSurfaceV6::handleExpose(const QRegion &region)
return false;
}
+void QWaylandXdgSurfaceV6::applyConfigure()
+{
+ Q_ASSERT(m_pendingConfigureSerial != 0);
+
+ if (m_toplevel)
+ m_toplevel->applyConfigure();
+ if (m_popup)
+ m_popup->applyConfigure();
+
+ m_configured = true;
+ ack_configure(m_pendingConfigureSerial);
+
+ m_pendingConfigureSerial = 0;
+}
+
+bool QWaylandXdgSurfaceV6::wantsDecorations() const
+{
+ return m_toplevel && !(m_toplevel->m_pending.states & Qt::WindowFullScreen);
+}
+
+void QWaylandXdgSurfaceV6::requestWindowStates(Qt::WindowStates states)
+{
+ if (m_toplevel)
+ m_toplevel->requestWindowStates(states);
+ else
+ qCWarning(lcQpaWayland) << "Non-toplevel surfaces can't request window states";
+}
+
void QWaylandXdgSurfaceV6::setToplevel()
{
Q_ASSERT(!m_toplevel && !m_popup);
@@ -220,21 +304,14 @@ void QWaylandXdgSurfaceV6::setPopup(QWaylandWindow *parent, QWaylandInputDevice
void QWaylandXdgSurfaceV6::zxdg_surface_v6_configure(uint32_t serial)
{
- m_configured = true;
- if (m_toplevel)
- m_toplevel->applyConfigure();
- else if (m_popup)
- m_popup->applyConfigure();
-
+ m_window->applyConfigureWhenPossible();
+ m_pendingConfigureSerial = serial;
if (!m_exposeRegion.isEmpty()) {
QWindowSystemInterface::handleExposeEvent(m_window->window(), m_exposeRegion);
m_exposeRegion = QRegion();
}
- ack_configure(serial);
}
-
-
QWaylandXdgShellV6::QWaylandXdgShellV6(struct ::wl_registry *registry, uint32_t id, uint32_t availableVersion)
: QtWayland::zxdg_shell_v6(registry, id, qMin(availableVersion, 1u))
{
diff --git a/src/client/qwaylandxdgshellv6_p.h b/src/client/qwaylandxdgshellv6_p.h
index b72d3d18a..d2448bc66 100644
--- a/src/client/qwaylandxdgshellv6_p.h
+++ b/src/client/qwaylandxdgshellv6_p.h
@@ -73,6 +73,7 @@ class QWaylandXdgShellV6;
class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgSurfaceV6 : public QWaylandShellSurface, public QtWayland::zxdg_surface_v6
{
+ Q_OBJECT
public:
QWaylandXdgSurfaceV6(QWaylandXdgShellV6 *shell, ::zxdg_surface_v6 *surface, QWaylandWindow *window);
~QWaylandXdgSurfaceV6() override;
@@ -85,8 +86,12 @@ public:
void setType(Qt::WindowType type, QWaylandWindow *transientParent) override;
bool handleExpose(const QRegion &) override;
+ bool handlesActiveState() const { return m_toplevel; }
+ void applyConfigure() override;
+ bool wantsDecorations() const override;
protected:
+ void requestWindowStates(Qt::WindowStates states) override;
void zxdg_surface_v6_configure(uint32_t serial) override;
private:
@@ -101,10 +106,12 @@ private:
void zxdg_toplevel_v6_configure(int32_t width, int32_t height, wl_array *states) override;
void zxdg_toplevel_v6_close() override;
+ void requestWindowStates(Qt::WindowStates states);
struct {
- int32_t width, height;
- QVarLengthArray<uint32_t> states;
- } m_configureState;
+ QSize size = {0, 0};
+ Qt::WindowStates states = Qt::WindowNoState;
+ } m_pending, m_applied;
+ QSize m_normalSize;
QWaylandXdgSurfaceV6 *m_xdgSurface = nullptr;
};
@@ -129,6 +136,7 @@ private:
Popup *m_popup = nullptr;
bool m_configured = false;
QRegion m_exposeRegion;
+ uint m_pendingConfigureSerial = 0;
};
class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgShellV6 : public QtWayland::zxdg_shell_v6
diff --git a/src/client/qwaylandxdgshellv6integration.cpp b/src/client/qwaylandxdgshellv6integration.cpp
index cb82354b6..d3327ff39 100644
--- a/src/client/qwaylandxdgshellv6integration.cpp
+++ b/src/client/qwaylandxdgshellv6integration.cpp
@@ -78,6 +78,20 @@ QWaylandShellSurface *QWaylandXdgShellV6Integration::createShellSurface(QWayland
return m_xdgShell->getXdgSurface(window);
}
+void QWaylandXdgShellV6Integration::handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus)
+{
+ if (newFocus) {
+ auto *xdgSurface = qobject_cast<QWaylandXdgSurfaceV6 *>(newFocus->shellSurface());
+ if (xdgSurface && xdgSurface->handlesActiveState())
+ m_display->handleWindowActivated(newFocus);
+ }
+ if (oldFocus && qobject_cast<QWaylandXdgPopup *>(oldFocus->shellSurface())) {
+ auto *xdgSurface = qobject_cast<QWaylandXdgSurfaceV6 *>(oldFocus->shellSurface());
+ if (xdgSurface && xdgSurface->handlesActiveState())
+ m_display->handleWindowDeactivated(oldFocus);
+ }
+}
+
}
QT_END_NAMESPACE
diff --git a/src/client/qwaylandxdgshellv6integration_p.h b/src/client/qwaylandxdgshellv6integration_p.h
index bdfd19723..66323a775 100644
--- a/src/client/qwaylandxdgshellv6integration_p.h
+++ b/src/client/qwaylandxdgshellv6integration_p.h
@@ -67,6 +67,7 @@ public:
static QWaylandXdgShellV6Integration *create(QWaylandDisplay* display);
bool initialize(QWaylandDisplay *display) override;
QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override;
+ void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) override;
private:
QWaylandXdgShellV6Integration(QWaylandDisplay *display);
diff --git a/src/client/qwaylandxdgsurface.cpp b/src/client/qwaylandxdgsurface.cpp
index 4dfc5e6da..5b5aacbf4 100644
--- a/src/client/qwaylandxdgsurface.cpp
+++ b/src/client/qwaylandxdgsurface.cpp
@@ -64,7 +64,7 @@ QWaylandXdgSurface::QWaylandXdgSurface(QWaylandXdgShell *shell, QWaylandWindow *
QWaylandXdgSurface::~QWaylandXdgSurface()
{
- if (m_active)
+ if (m_acked.states & Qt::WindowActive)
window()->display()->handleWindowDeactivated(m_window);
xdg_surface_destroy(object());
@@ -92,38 +92,6 @@ bool QWaylandXdgSurface::move(QWaylandInputDevice *inputDevice)
return true;
}
-void QWaylandXdgSurface::setMaximized()
-{
- if (!m_maximized)
- set_maximized();
-}
-
-void QWaylandXdgSurface::setFullscreen()
-{
- if (!m_fullscreen)
- set_fullscreen(nullptr);
-}
-
-void QWaylandXdgSurface::setNormal()
-{
- if (m_fullscreen || m_maximized || m_minimized) {
- if (m_maximized) {
- unset_maximized();
- }
- if (m_fullscreen) {
- unset_fullscreen();
- }
-
- m_fullscreen = m_maximized = m_minimized = false;
- }
-}
-
-void QWaylandXdgSurface::setMinimized()
-{
- m_minimized = true;
- set_minimized();
-}
-
void QWaylandXdgSurface::updateTransientParent(QWaylandWindow *parent)
{
if (!parent)
@@ -180,71 +148,88 @@ void QWaylandXdgSurface::setType(Qt::WindowType type, QWaylandWindow *transientP
updateTransientParent(transientParent);
}
+void QWaylandXdgSurface::applyConfigure()
+{
+ if (m_pending.isResizing)
+ m_normalSize = m_pending.size;
+ else if (!(m_acked.states & (Qt::WindowMaximized|Qt::WindowFullScreen)))
+ m_normalSize = m_window->window()->frameGeometry().size();
+
+ if ((m_pending.states & Qt::WindowActive) && !(m_acked.states & Qt::WindowActive))
+ m_window->display()->handleWindowActivated(m_window);
+
+ if (!(m_pending.states & Qt::WindowActive) && (m_acked.states & Qt::WindowActive))
+ m_window->display()->handleWindowDeactivated(m_window);
+
+ // TODO: none of the other plugins send WindowActive either, but is it on purpose?
+ Qt::WindowStates statesWithoutActive = m_pending.states & ~Qt::WindowActive;
+
+ m_window->handleWindowStatesChanged(statesWithoutActive);
+ if (!m_pending.size.isEmpty())
+ m_window->resizeFromApplyConfigure(m_pending.size);
+ else if (!m_normalSize.isEmpty())
+ m_window->resizeFromApplyConfigure(m_normalSize);
+ ack_configure(m_pending.serial);
+ m_acked = m_pending;
+}
+
+void QWaylandXdgSurface::requestWindowStates(Qt::WindowStates states)
+{
+ Qt::WindowStates changedStates = m_acked.states ^ states;
+
+ if (changedStates & Qt::WindowMaximized) {
+ if (states & Qt::WindowMaximized)
+ set_maximized();
+ else
+ unset_maximized();
+ }
+
+ if (changedStates & Qt::WindowFullScreen) {
+ if (states & Qt::WindowFullScreen)
+ set_fullscreen(nullptr);
+ else
+ unset_fullscreen();
+ }
+
+ // Minimized state is not reported by the protocol, so always send it
+ if (states & Qt::WindowMinimized) {
+ set_minimized();
+ window()->handleWindowStatesChanged(states & ~Qt::WindowMinimized);
+ }
+}
+
+bool QWaylandXdgSurface::wantsDecorations() const
+{
+ return !(m_pending.states & Qt::WindowFullScreen);
+}
+
void QWaylandXdgSurface::xdg_surface_configure(int32_t width, int32_t height, struct wl_array *states,uint32_t serial)
{
- uint32_t *state = reinterpret_cast<uint32_t*>(states->data);
+ uint32_t *xdgStates = reinterpret_cast<uint32_t*>(states->data);
size_t numStates = states->size / sizeof(uint32_t);
- bool aboutToMaximize = false;
- bool aboutToFullScreen = false;
- bool aboutToActivate = false;
-
+ m_pending.serial = serial;
+ m_pending.size = QSize(width, height);
+ m_pending.isResizing = false;
+ m_pending.states = Qt::WindowNoState;
for (size_t i = 0; i < numStates; i++) {
- switch (state[i]) {
+ switch (xdgStates[i]) {
case XDG_SURFACE_STATE_MAXIMIZED:
- aboutToMaximize = ((width > 0) && (height > 0));
+ m_pending.states |= Qt::WindowMaximized;
break;
case XDG_SURFACE_STATE_FULLSCREEN:
- aboutToFullScreen = true;
+ m_pending.states |= Qt::WindowFullScreen;
break;
case XDG_SURFACE_STATE_RESIZING:
- m_normalSize = QSize(width, height);
+ m_pending.isResizing = true;
break;
case XDG_SURFACE_STATE_ACTIVATED:
- aboutToActivate = true;
+ m_pending.states |= Qt::WindowActive;
break;
default:
break;
}
}
-
- if (!m_active && aboutToActivate) {
- m_active = true;
- window()->display()->handleWindowActivated(m_window);
- } else if (m_active && !aboutToActivate) {
- m_active = false;
- window()->display()->handleWindowDeactivated(m_window);
- }
-
- if (!m_fullscreen && aboutToFullScreen) {
- if (!m_maximized)
- m_normalSize = m_window->window()->frameGeometry().size();
- m_fullscreen = true;
- m_window->window()->showFullScreen();
- } else if (m_fullscreen && !aboutToFullScreen) {
- m_fullscreen = false;
- if ( m_maximized ) {
- m_window->window()->showMaximized();
- } else {
- m_window->window()->showNormal();
- }
- } else if (!m_maximized && aboutToMaximize) {
- if (!m_fullscreen)
- m_normalSize = m_window->window()->frameGeometry().size();
- m_maximized = true;
- m_window->window()->showMaximized();
- } else if (m_maximized && !aboutToMaximize) {
- m_maximized = false;
- m_window->window()->showNormal();
- }
-
- if (width <= 0 || height <= 0) {
- if (!m_normalSize.isEmpty())
- m_window->configure(0, m_normalSize.width(), m_normalSize.height());
- } else {
- m_window->configure(0, width, height);
- }
-
- ack_configure(serial);
+ m_window->applyConfigureWhenPossible();
}
void QWaylandXdgSurface::xdg_surface_close()
diff --git a/src/client/qwaylandxdgsurface_p.h b/src/client/qwaylandxdgsurface_p.h
index b8dd93f47..059e79d87 100644
--- a/src/client/qwaylandxdgsurface_p.h
+++ b/src/client/qwaylandxdgsurface_p.h
@@ -96,26 +96,23 @@ public:
void setWindowFlags(Qt::WindowFlags flags) override;
void sendProperty(const QString &name, const QVariant &value) override;
- bool isFullscreen() const { return m_fullscreen; }
- bool isMaximized() const { return m_maximized; }
-
void setType(Qt::WindowType type, QWaylandWindow *transientParent) override;
+ void applyConfigure() override;
+ void requestWindowStates(Qt::WindowStates states) override;
+ bool wantsDecorations() const override;
private:
- void setMaximized() override;
- void setFullscreen() override;
- void setNormal() override;
- void setMinimized() override;
-
void updateTransientParent(QWaylandWindow *parent);
private:
QWaylandWindow *m_window = nullptr;
QWaylandXdgShell* m_shell = nullptr;
- bool m_maximized = false;
- bool m_minimized = false;
- bool m_fullscreen = false;
- bool m_active = false;
+ struct {
+ Qt::WindowStates states = Qt::WindowNoState;
+ bool isResizing = false;
+ QSize size = {0, 0};
+ uint serial = 0;
+ } m_acked, m_pending;
QSize m_normalSize;
QMargins m_margins;
QWaylandExtendedSurface *m_extendedWindow = nullptr;
diff --git a/src/plugins/decorations/bradient/main.cpp b/src/plugins/decorations/bradient/main.cpp
index 9a7e91e6f..d3627d2f7 100644
--- a/src/plugins/decorations/bradient/main.cpp
+++ b/src/plugins/decorations/bradient/main.cpp
@@ -270,8 +270,7 @@ void QWaylandBradientDecoration::paint(QPaintDevice *device)
p.drawPixmap(closeButtonRect(), closePixmap, closePixmap.rect());
// Maximize button
- QPixmap maximizePixmap(waylandWindow()->isMaximized()
- ? qt_normalizeup_xpm : qt_maximize_xpm);
+ QPixmap maximizePixmap((window()->windowStates() & Qt::WindowMaximized) ? qt_normalizeup_xpm : qt_maximize_xpm);
p.drawPixmap(maximizeButtonRect(), maximizePixmap, maximizePixmap.rect());
// Minimize button
@@ -356,7 +355,7 @@ bool QWaylandBradientDecoration::handleMouse(QWaylandInputDevice *inputDevice, c
QWindowSystemInterface::handleCloseEvent(window());
} else if (maximizeButtonRect().contains(local)) {
if (clickButton(b, Maximize))
- window()->setWindowState(waylandWindow()->isMaximized() ? Qt::WindowNoState : Qt::WindowMaximized);
+ window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized);
} else if (minimizeButtonRect().contains(local)) {
if (clickButton(b, Minimize))
window()->setWindowState(Qt::WindowMinimized);
@@ -390,7 +389,7 @@ bool QWaylandBradientDecoration::handleTouch(QWaylandInputDevice *inputDevice, c
if (closeButtonRect().contains(local))
QWindowSystemInterface::handleCloseEvent(window());
else if (maximizeButtonRect().contains(local))
- window()->setWindowState(waylandWindow()->isMaximized() ? Qt::WindowNoState : Qt::WindowMaximized);
+ window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized);
else if (minimizeButtonRect().contains(local))
window()->setWindowState(Qt::WindowMinimized);
else if (local.y() <= margins().top())
diff --git a/src/plugins/shellintegration/ivi-shell/qwaylandivisurface.cpp b/src/plugins/shellintegration/ivi-shell/qwaylandivisurface.cpp
index ec529b124..871709cdf 100644
--- a/src/plugins/shellintegration/ivi-shell/qwaylandivisurface.cpp
+++ b/src/plugins/shellintegration/ivi-shell/qwaylandivisurface.cpp
@@ -82,6 +82,11 @@ void QWaylandIviSurface::setType(Qt::WindowType type, QWaylandWindow *transientP
Q_UNUSED(transientParent)
}
+void QWaylandIviSurface::applyConfigure()
+{
+ m_window->resizeFromApplyConfigure(m_pendingSize);
+}
+
void QWaylandIviSurface::createExtendedSurface(QWaylandWindow *window)
{
if (window->display()->windowExtension())
@@ -90,7 +95,8 @@ void QWaylandIviSurface::createExtendedSurface(QWaylandWindow *window)
void QWaylandIviSurface::ivi_surface_configure(int32_t width, int32_t height)
{
- this->m_window->configure(0, width, height);
+ m_pendingSize = {width, height};
+ m_window->applyConfigureWhenPossible();
}
void QWaylandIviSurface::ivi_controller_surface_visibility(int32_t visibility)
diff --git a/src/plugins/shellintegration/ivi-shell/qwaylandivisurface_p.h b/src/plugins/shellintegration/ivi-shell/qwaylandivisurface_p.h
index ff943060b..6ec28e758 100644
--- a/src/plugins/shellintegration/ivi-shell/qwaylandivisurface_p.h
+++ b/src/plugins/shellintegration/ivi-shell/qwaylandivisurface_p.h
@@ -63,6 +63,7 @@ public:
~QWaylandIviSurface() override;
void setType(Qt::WindowType type, QWaylandWindow *transientParent) override;
+ void applyConfigure() override;
private:
void createExtendedSurface(QWaylandWindow *window);
@@ -71,6 +72,7 @@ private:
QWaylandWindow *m_window = nullptr;
QWaylandExtendedSurface *m_extendedWindow = nullptr;
+ QSize m_pendingSize = {0, 0};
};
}
diff --git a/tests/auto/client/client/tst_client.cpp b/tests/auto/client/client/tst_client.cpp
index 05b8bac1a..7a7c7fa85 100644
--- a/tests/auto/client/client/tst_client.cpp
+++ b/tests/auto/client/client/tst_client.cpp
@@ -172,6 +172,7 @@ private slots:
void windowScreens();
void removePrimaryScreen();
void createDestroyWindow();
+ void activeWindowFollowsKeyboardFocus();
void events();
void backingStore();
void touchDrag();
@@ -313,7 +314,7 @@ void tst_WaylandClient::createDestroyWindow()
QTRY_VERIFY(!compositor->surface());
}
-void tst_WaylandClient::events()
+void tst_WaylandClient::activeWindowFollowsKeyboardFocus()
{
TestWindow window;
window.show();
@@ -324,6 +325,9 @@ void tst_WaylandClient::events()
QTRY_VERIFY(window.isExposed());
+ if (compositor->xdgToplevelV6())
+ QSKIP("On xdg-shell v6 focus is handled by configure events");
+
QCOMPARE(window.focusInEventCount, 0);
compositor->setKeyboardFocus(surface);
QTRY_COMPARE(window.focusInEventCount, 1);
@@ -333,9 +337,21 @@ void tst_WaylandClient::events()
compositor->setKeyboardFocus(QSharedPointer<MockSurface>(nullptr));
QTRY_COMPARE(window.focusOutEventCount, 1);
QTRY_COMPARE(QGuiApplication::focusWindow(), static_cast<QWindow *>(nullptr));
+}
+
+void tst_WaylandClient::events()
+{
+ TestWindow window;
+ window.show();
+
+ QSharedPointer<MockSurface> surface;
+ QTRY_VERIFY(surface = compositor->surface());
+ compositor->sendShellSurfaceConfigure(surface);
+
+ QTRY_VERIFY(window.isExposed());
compositor->setKeyboardFocus(surface);
- QTRY_COMPARE(window.focusInEventCount, 2);
+ QTRY_COMPARE(window.focusInEventCount, 1);
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
uint keyCode = 80; // arbitrarily chosen
diff --git a/tests/auto/client/shared/mockcompositor.cpp b/tests/auto/client/shared/mockcompositor.cpp
index 9ef08ad95..11b40d9fc 100644
--- a/tests/auto/client/shared/mockcompositor.cpp
+++ b/tests/auto/client/shared/mockcompositor.cpp
@@ -236,11 +236,14 @@ void MockCompositor::sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface
processCommand(command);
}
-void MockCompositor::sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size)
+void MockCompositor::sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size, const QVector<uint> &states)
{
Command command = makeCommand(Impl::Compositor::sendXdgToplevelV6Configure, m_compositor);
command.parameters << QVariant::fromValue(toplevel);
command.parameters << QVariant::fromValue(size);
+ auto statesBytes = QByteArray::fromRawData(reinterpret_cast<const char *>(states.data()),
+ states.size() * static_cast<int>(sizeof(uint)));
+ command.parameters << statesBytes;
processCommand(command);
}
diff --git a/tests/auto/client/shared/mockcompositor.h b/tests/auto/client/shared/mockcompositor.h
index 34c20943a..d3568c165 100644
--- a/tests/auto/client/shared/mockcompositor.h
+++ b/tests/auto/client/shared/mockcompositor.h
@@ -175,12 +175,21 @@ private:
Q_DECLARE_METATYPE(QSharedPointer<MockIviSurface>)
-class MockXdgToplevelV6
+class MockXdgToplevelV6 : public QObject
{
+ Q_OBJECT
public:
Impl::XdgToplevelV6 *handle() const { return m_toplevel; }
void sendConfigure(const QSharedPointer<MockXdgToplevelV6> toplevel);
+
+signals:
+ uint setMinimizedRequested();
+ uint setMaximizedRequested();
+ uint unsetMaximizedRequested();
+ uint setFullscreenRequested();
+ uint unsetFullscreenRequested();
+
private:
MockXdgToplevelV6(Impl::XdgToplevelV6 *toplevel) : m_toplevel(toplevel) {}
friend class Impl::Compositor;
@@ -234,7 +243,8 @@ public:
void sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output);
void sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size = QSize(0, 0));
void sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size);
- void sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size = QSize(0, 0));
+ void sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size = QSize(0, 0),
+ const QVector<uint> &states = { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED });
void waitForStartDrag();
QSharedPointer<MockSurface> surface();
diff --git a/tests/auto/client/shared/mockxdgshellv6.cpp b/tests/auto/client/shared/mockxdgshellv6.cpp
index 39e03296d..6f6f0b905 100644
--- a/tests/auto/client/shared/mockxdgshellv6.cpp
+++ b/tests/auto/client/shared/mockxdgshellv6.cpp
@@ -39,8 +39,8 @@ void Compositor::sendXdgToplevelV6Configure(void *data, const QList<QVariant> &p
Q_ASSERT(toplevel && toplevel->resource());
QSize size = parameters.at(1).toSize();
Q_ASSERT(size.isValid());
- QByteArray states;
- toplevel->send_configure(size.width(), size.height(), states);
+ auto statesBytes = parameters.at(2).toByteArray();
+ toplevel->send_configure(size.width(), size.height(), statesBytes);
toplevel->xdgSurface()->send_configure(compositor->nextSerial());
}
@@ -87,6 +87,37 @@ void XdgToplevelV6::zxdg_toplevel_v6_destroy(QtWaylandServer::zxdg_toplevel_v6::
wl_resource_destroy(resource->handle);
}
+void XdgToplevelV6::zxdg_toplevel_v6_set_minimized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource)
+{
+ Q_UNUSED(resource);
+ emit m_mockToplevel->setMinimizedRequested();
+}
+
+void XdgToplevelV6::zxdg_toplevel_v6_set_maximized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource)
+{
+ Q_UNUSED(resource);
+ emit m_mockToplevel->setMaximizedRequested();
+}
+
+void XdgToplevelV6::zxdg_toplevel_v6_unset_maximized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource)
+{
+ Q_UNUSED(resource);
+ emit m_mockToplevel->unsetMaximizedRequested();
+}
+
+void XdgToplevelV6::zxdg_toplevel_v6_set_fullscreen(QtWaylandServer::zxdg_toplevel_v6::Resource *resource, wl_resource *output)
+{
+ Q_UNUSED(resource);
+ Q_UNUSED(output);
+ emit m_mockToplevel->setFullscreenRequested();
+}
+
+void XdgToplevelV6::zxdg_toplevel_v6_unset_fullscreen(QtWaylandServer::zxdg_toplevel_v6::Resource *resource)
+{
+ Q_UNUSED(resource);
+ emit m_mockToplevel->unsetFullscreenRequested();
+}
+
void Impl::XdgShellV6::zxdg_shell_v6_get_xdg_surface(QtWaylandServer::zxdg_shell_v6::Resource *resource, uint32_t id, wl_resource *surface)
{
new XdgSurfaceV6(this, Surface::fromResource(surface), resource->client(), id);
diff --git a/tests/auto/client/shared/mockxdgshellv6.h b/tests/auto/client/shared/mockxdgshellv6.h
index 92b808ba8..faadb785a 100644
--- a/tests/auto/client/shared/mockxdgshellv6.h
+++ b/tests/auto/client/shared/mockxdgshellv6.h
@@ -74,6 +74,11 @@ public:
protected:
void zxdg_toplevel_v6_destroy_resource(Resource *) override { delete this; }
void zxdg_toplevel_v6_destroy(Resource *resource) override;
+ void zxdg_toplevel_v6_set_minimized(Resource *resource) override;
+ void zxdg_toplevel_v6_set_maximized(Resource *resource) override;
+ void zxdg_toplevel_v6_unset_maximized(Resource *resource) override;
+ void zxdg_toplevel_v6_set_fullscreen(Resource *resource, struct ::wl_resource *output) override;
+ void zxdg_toplevel_v6_unset_fullscreen(Resource *resource) override;
private:
XdgSurfaceV6 *m_xdgSurface = nullptr;
diff --git a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp
index 364cd1099..0f72f58a9 100644
--- a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp
+++ b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp
@@ -42,6 +42,7 @@ static const QSize screenSize(1600, 1200);
class TestWindow : public QWindow
{
+ Q_OBJECT
public:
TestWindow()
{
@@ -49,6 +50,16 @@ public:
setGeometry(0, 0, 32, 32);
create();
}
+
+ bool event(QEvent *event) override
+ {
+ if (event->type() == QEvent::WindowStateChange)
+ emit windowStateChangeEventReceived(static_cast<QWindowStateChangeEvent *>(event)->oldState());
+ return QWindow::event(event);
+ }
+
+signals:
+ void windowStateChangeEventReceived(uint oldState);
};
class tst_WaylandClientXdgShellV6 : public QObject
@@ -58,6 +69,7 @@ public:
tst_WaylandClientXdgShellV6(MockCompositor *c)
: m_compositor(c)
{
+ qRegisterMetaType<Qt::WindowState>();
QSocketNotifier *notifier = new QSocketNotifier(m_compositor->waylandFileDescriptor(), QSocketNotifier::Read, this);
connect(notifier, &QSocketNotifier::activated, this, &tst_WaylandClientXdgShellV6::processWaylandEvents);
// connect to the event dispatcher to make sure to flush out the outgoing message queue
@@ -82,6 +94,11 @@ public slots:
private slots:
void createDestroyWindow();
void configure();
+ void showMinimized();
+ void setMinimized();
+ void unsetMaximized();
+ void focusWindowFollowsConfigure();
+ void windowStateChangedEvents();
private:
MockCompositor *m_compositor = nullptr;
@@ -118,10 +135,182 @@ void tst_WaylandClientXdgShellV6::configure()
QSharedPointer<MockXdgToplevelV6> toplevel;
QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6());
+
const QSize newSize(123, 456);
m_compositor->sendXdgToplevelV6Configure(toplevel, newSize);
QTRY_VERIFY(window.isExposed());
+ QTRY_COMPARE(window.visibility(), QWindow::Windowed);
+ QTRY_COMPARE(window.windowStates(), Qt::WindowNoState);
QTRY_COMPARE(window.frameGeometry(), QRect(QPoint(), newSize));
+
+ m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED, ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED });
+ QTRY_COMPARE(window.visibility(), QWindow::Maximized);
+ QTRY_COMPARE(window.windowStates(), Qt::WindowMaximized);
+ QTRY_COMPARE(window.frameGeometry(), QRect(QPoint(), screenSize));
+
+ m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED, ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN });
+ QTRY_COMPARE(window.visibility(), QWindow::FullScreen);
+ QTRY_COMPARE(window.windowStates(), Qt::WindowFullScreen);
+ QTRY_COMPARE(window.frameGeometry(), QRect(QPoint(), screenSize));
+
+ //The window should remember it's original size
+ m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED });
+ QTRY_COMPARE(window.visibility(), QWindow::Windowed);
+ QTRY_COMPARE(window.windowStates(), Qt::WindowNoState);
+ QTRY_COMPARE(window.frameGeometry(), QRect(QPoint(), newSize));
+}
+
+void tst_WaylandClientXdgShellV6::showMinimized()
+{
+ // On xdg-shell v6 there's really no way for the compositor to tell the window if it's minimized
+ // There are wl_surface.enter events and so on, but there's really no way to differentiate
+ // between a window preview and an unminimized window.
+ TestWindow window;
+ window.showMinimized();
+ QCOMPARE(window.windowStates(), Qt::WindowMinimized); // should return minimized until
+ QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); // rejected by handleWindowStateChanged
+}
+
+void tst_WaylandClientXdgShellV6::setMinimized()
+{
+ TestWindow window;
+ window.show();
+
+ QSharedPointer<MockXdgToplevelV6> toplevel;
+ QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6());
+
+ m_compositor->sendXdgToplevelV6Configure(toplevel);
+ QTRY_COMPARE(window.visibility(), QWindow::Windowed);
+ QTRY_COMPARE(window.windowStates(), Qt::WindowNoState);
+
+ QSignalSpy setMinimizedSpy(toplevel.data(), SIGNAL(setMinimizedRequested()));
+ QSignalSpy windowStateChangeSpy(&window, SIGNAL(windowStateChangeEventReceived(uint)));
+
+ window.setVisibility(QWindow::Minimized);
+ QCOMPARE(window.visibility(), QWindow::Minimized);
+ QCOMPARE(window.windowStates(), Qt::WindowMinimized);
+ QTRY_COMPARE(setMinimizedSpy.count(), 1);
+ {
+ QTRY_VERIFY(windowStateChangeSpy.count() > 0);
+ Qt::WindowStates oldStates(windowStateChangeSpy.takeFirst().at(0).toUInt());
+ QCOMPARE(oldStates, Qt::WindowNoState);
+ }
+
+ // In the meantime the compositor may minimize, do nothing or reshow the window without
+ // telling us.
+
+ QTRY_COMPARE(window.visibility(), QWindow::Windowed); // verify that we don't know anything
+ QTRY_COMPARE(window.windowStates(), Qt::WindowNoState);
+ {
+ QTRY_COMPARE(windowStateChangeSpy.count(), 1);
+ Qt::WindowStates oldStates(windowStateChangeSpy.takeFirst().at(0).toUInt());
+ QCOMPARE(oldStates, Qt::WindowNoState); // because the window never was minimized
+ }
+
+ // Setting visibility again should send another set_minimized request
+ window.setVisibility(QWindow::Minimized);
+ QTRY_COMPARE(setMinimizedSpy.count(), 2);
+}
+
+void tst_WaylandClientXdgShellV6::unsetMaximized()
+{
+ TestWindow window;
+ window.show();
+
+ QSharedPointer<MockXdgToplevelV6> toplevel;
+ QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6());
+
+ QSignalSpy unsetMaximizedSpy(toplevel.data(), SIGNAL(unsetMaximizedRequested()));
+
+ QSignalSpy windowStateChangedSpy(&window, SIGNAL(windowStateChanged(Qt::WindowState)));
+
+ m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED });
+
+ QTRY_COMPARE(windowStateChangedSpy.count(), 1);
+ QCOMPARE(windowStateChangedSpy.takeFirst().at(0).toUInt(), Qt::WindowMaximized);
+
+ window.setWindowStates(Qt::WindowNoState);
+
+ QTRY_COMPARE(unsetMaximizedSpy.count(), 1);
+ QTRY_COMPARE(windowStateChangedSpy.count(), 1);
+ QCOMPARE(windowStateChangedSpy.takeFirst().at(0).toUInt(), Qt::WindowNoState);
+
+ m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), {});
+
+ QTRY_COMPARE(windowStateChangedSpy.count(), 1);
+ QCOMPARE(windowStateChangedSpy.takeFirst().at(0).toUInt(), Qt::WindowNoState);
+}
+
+void tst_WaylandClientXdgShellV6::focusWindowFollowsConfigure()
+{
+ TestWindow window;
+ window.show();
+
+ QSharedPointer<MockXdgToplevelV6> toplevel;
+ QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6());
+ QTRY_VERIFY(!window.isActive());
+
+ QSignalSpy windowStateChangeSpy(&window, SIGNAL(windowStateChangeEventReceived(uint)));
+
+ m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED });
+ QTRY_VERIFY(window.isActive());
+
+ m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), {});
+ QTRY_VERIFY(!window.isActive());
+}
+
+void tst_WaylandClientXdgShellV6::windowStateChangedEvents()
+{
+ TestWindow window;
+ window.show();
+
+ QSharedPointer<MockXdgToplevelV6> toplevel;
+ QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6());
+
+ QSignalSpy eventSpy(&window, SIGNAL(windowStateChangeEventReceived(uint)));
+ QSignalSpy signalSpy(&window, SIGNAL(windowStateChanged(Qt::WindowState)));
+
+ m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED });
+
+ QTRY_COMPARE(window.windowStates(), Qt::WindowMaximized);
+ QTRY_COMPARE(window.windowState(), Qt::WindowMaximized);
+ {
+ QTRY_COMPARE(eventSpy.count(), 1);
+ Qt::WindowStates oldStates(eventSpy.takeFirst().at(0).toUInt());
+ QCOMPARE(oldStates, Qt::WindowNoState);
+
+ QTRY_COMPARE(signalSpy.count(), 1);
+ uint newState = signalSpy.takeFirst().at(0).toUInt();
+ QCOMPARE(newState, Qt::WindowMaximized);
+ }
+
+ m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN });
+
+ QTRY_COMPARE(window.windowStates(), Qt::WindowFullScreen);
+ QTRY_COMPARE(window.windowState(), Qt::WindowFullScreen);
+ {
+ QTRY_COMPARE(eventSpy.count(), 1);
+ Qt::WindowStates oldStates(eventSpy.takeFirst().at(0).toUInt());
+ QCOMPARE(oldStates, Qt::WindowMaximized);
+
+ QTRY_COMPARE(signalSpy.count(), 1);
+ uint newState = signalSpy.takeFirst().at(0).toUInt();
+ QCOMPARE(newState, Qt::WindowFullScreen);
+ }
+
+ m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), {});
+
+ QTRY_COMPARE(window.windowStates(), Qt::WindowNoState);
+ QTRY_COMPARE(window.windowState(), Qt::WindowNoState);
+ {
+ QTRY_COMPARE(eventSpy.count(), 1);
+ Qt::WindowStates oldStates(eventSpy.takeFirst().at(0).toUInt());
+ QCOMPARE(oldStates, Qt::WindowFullScreen);
+
+ QTRY_COMPARE(signalSpy.count(), 1);
+ uint newState = signalSpy.takeFirst().at(0).toUInt();
+ QCOMPARE(newState, Qt::WindowNoState);
+ }
}
int main(int argc, char **argv)