diff options
author | Johan Klokkhammer Helsing <johan.helsing@qt.io> | 2018-03-06 13:06:11 +0100 |
---|---|---|
committer | Johan Helsing <johan.helsing@qt.io> | 2018-05-07 11:34:41 +0000 |
commit | c5ab40d2573fde27a14d7c05d8693966c65a400c (patch) | |
tree | d93c4647c90b0d72059b9148a6b4ca1d3f7cc272 /src/client/qwaylandxdgshellv6.cpp | |
parent | 9db98c7e0a2656988d5c549f0618b2ba882f1fc9 (diff) |
Client: Add acked configure support and implement it for xdg-shell v6
The problem:
The code in QWaylandWindow::setWindowStateInternal made many assumptions about
how the shell surface responded to new window states, and when and if they were
applied. Particularly:
- The shell integrations support different subsets of Qt::WindowStates, so it
doesn't make sense to map from states to setFullscreen, setNormal, etc. in
QWaylandWindow because it really depends on the shell integration how it
should be handled.
- Some states are not supported/unknown on some shells and should immediately be
rejected.
- On some shells, particularly those where the current state is unknown, flags
need to be resent even though they didn't change.
- Should handleWindowStatesChanged be called immediately (client decides) or
should it wait for a configure event (compositor decides)? I.e. the mState
variable only makes sense for some shells.
Furthermore, when state changes come from the compositors, some shell
integrations require that a configure event is acked by the client and that the
next committed buffer's size and content matches that configure event.
Previously, we would just ack immediately and still send a buffer with the old
size before a correct frame was drawn. i.e. sending incorrect acks, which would
lead to protocol errors on some compositors.
Additionally, QWaylandWindow::createDecoration() also assumed that windows
should have decorations unless they are fullscreen. This is not always the
case, particularly on ivi-application and probably on future shell integrations
for embedded or tiling window managers etc.
The Solution:
The responsibility of mapping requested window states to Wayland requests have
been moved to the QWaylandShellSurface implementation. QWaylandWindow now calls
a new virtual, QWaylandShellSurface::requestWindowStates(Qt::WindowStates),
instead of trying to be smart about it. The virtual getters and setters for
window states have now been removed from the QWaylandShellSurface interface.
It's now also the shell surface implementation's responsibility to call
QWaylandWindow::handleWindowStatesChanged if and when it knows a new state is
effective.
QWaylandWindow::configure has been replaced with
QWaylandWindow::applyConfigureWhenPossible(), which causes another new virtual,
QWaylandShellSurface::applyConfigure(), to be called whenever we're free to
resize and change the states of the next buffer (this is when states should be
acked). This means that shells that use acked states need to store the pending
states themselves, call applyConfigureWhenPossible(), wait for applyConfigure()
to be called, call resizeFromApplyConfigure() and handleWindowStatesChanged(),
and finally ack the applied state.
Acked state for xdg-shell v5 and v6 has now been implemented, which also means
we've now:
[ChangeLog][QPA plugin] Implemented support for maximizing, minimizing, and
setting fullscreen with xdg-shell unstable v6.
[ChangeLog][QPA plugin] QWindow::isActive now follows configure events on
xdg-shell unstable v6 (like v5).
QWaylandWindow::createDecoration queries QWaylandShellSurface::wantsDecoration
before creating window decorations, instead of using the previously unreliable
QWaylandWindow::isFullscreen().
[ChangeLog][QPA plugin] Window decorations are now automatically disabled for
ivi-application.
The refactor also removes a couple of hacks:
- QWindowSystemInterface::flushWindowSystemEvents() was called in
QWaylandWindow::setWindowStates. Since this hack was introduced, the events
now have oldState and newState members, and their values seem to make sense
(ensured in the tests).
- The hack for unminimizing on xdg-shell v5 in QWaylandWindow::createDecoration
was not needed anymore.
Finally, tests have been added for xdg-shell v6 to ensure that the right Wayland
requests are sent, that we respond to configure events from the compositor, and
that the Qt events and signals emitted on the client side make sense.
Task-number: QTBUG-53702
Task-number: QTBUG-63417
Task-number: QTBUG-63748
Task-number: QTBUG-66928
Change-Id: Ib4c36b69105750f9dbdcc78adcf71e2e994cc70d
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
Diffstat (limited to 'src/client/qwaylandxdgshellv6.cpp')
-rw-r--r-- | src/client/qwaylandxdgshellv6.cpp | 119 |
1 files changed, 98 insertions, 21 deletions
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 ®ion) 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)) { |