2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2015-03-04 08:21:10 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
2015-03-04 08:21:10 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2019-08-30 21:36:58 +00:00
|
|
|
#include "xdgshellclient.h"
|
2020-11-23 18:24:37 +00:00
|
|
|
#include "abstract_wayland_output.h"
|
2022-01-15 11:20:16 +00:00
|
|
|
#if KWIN_BUILD_ACTIVITIES
|
|
|
|
#include "activities.h"
|
|
|
|
#endif
|
2021-02-24 08:35:18 +00:00
|
|
|
#include "decorations/decorationbridge.h"
|
2015-03-04 08:21:10 +00:00
|
|
|
#include "deleted.h"
|
2020-11-23 18:24:37 +00:00
|
|
|
#include "platform.h"
|
2016-09-15 19:03:40 +00:00
|
|
|
#include "screenedge.h"
|
2022-01-15 11:20:16 +00:00
|
|
|
#include "touch_input.h"
|
|
|
|
#include "utils/subsurfacemonitor.h"
|
2021-08-12 14:16:08 +00:00
|
|
|
#include "virtualdesktops.h"
|
2020-02-17 18:39:17 +00:00
|
|
|
#include "wayland_server.h"
|
|
|
|
#include "workspace.h"
|
|
|
|
|
2015-12-17 14:47:36 +00:00
|
|
|
#include <KDecoration2/DecoratedClient>
|
2019-09-26 16:49:04 +00:00
|
|
|
#include <KDecoration2/Decoration>
|
2020-04-29 15:18:41 +00:00
|
|
|
#include <KWaylandServer/appmenu_interface.h>
|
2020-02-17 18:39:17 +00:00
|
|
|
#include <KWaylandServer/output_interface.h>
|
2020-04-29 15:18:41 +00:00
|
|
|
#include <KWaylandServer/plasmashell_interface.h>
|
|
|
|
#include <KWaylandServer/seat_interface.h>
|
|
|
|
#include <KWaylandServer/server_decoration_interface.h>
|
|
|
|
#include <KWaylandServer/server_decoration_palette_interface.h>
|
|
|
|
#include <KWaylandServer/surface_interface.h>
|
2020-02-17 18:39:17 +00:00
|
|
|
#include <KWaylandServer/xdgdecoration_v1_interface.h>
|
2019-01-13 16:50:32 +00:00
|
|
|
|
2020-04-29 15:18:41 +00:00
|
|
|
using namespace KWaylandServer;
|
2015-03-04 08:21:10 +00:00
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
XdgSurfaceClient::XdgSurfaceClient(XdgSurfaceInterface *shellSurface)
|
|
|
|
: WaylandClient(shellSurface->surface())
|
|
|
|
, m_shellSurface(shellSurface)
|
|
|
|
, m_configureTimer(new QTimer(this))
|
2016-04-18 09:55:40 +00:00
|
|
|
{
|
2021-11-15 14:03:53 +00:00
|
|
|
setupPlasmaShellIntegration();
|
2020-02-17 18:39:17 +00:00
|
|
|
connect(shellSurface, &XdgSurfaceInterface::configureAcknowledged,
|
|
|
|
this, &XdgSurfaceClient::handleConfigureAcknowledged);
|
2020-05-07 14:29:41 +00:00
|
|
|
connect(shellSurface, &XdgSurfaceInterface::resetOccurred,
|
|
|
|
this, &XdgSurfaceClient::destroyClient);
|
2020-02-17 18:39:17 +00:00
|
|
|
connect(shellSurface->surface(), &SurfaceInterface::committed,
|
|
|
|
this, &XdgSurfaceClient::handleCommit);
|
2020-05-07 14:29:41 +00:00
|
|
|
#if 0 // TODO: Refactor kwin core in order to uncomment this code.
|
|
|
|
connect(shellSurface->surface(), &SurfaceInterface::mapped,
|
|
|
|
this, &XdgSurfaceClient::setReadyForPainting);
|
|
|
|
#endif
|
2021-08-12 10:03:35 +00:00
|
|
|
connect(shellSurface, &XdgSurfaceInterface::aboutToBeDestroyed,
|
|
|
|
this, &XdgSurfaceClient::destroyClient);
|
2020-07-16 14:02:06 +00:00
|
|
|
connect(shellSurface->surface(), &SurfaceInterface::aboutToBeDestroyed,
|
2020-02-17 18:39:17 +00:00
|
|
|
this, &XdgSurfaceClient::destroyClient);
|
2019-09-15 09:54:50 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
// The effective window geometry is determined by two things: (a) the rectangle that bounds
|
|
|
|
// the main surface and all of its sub-surfaces, (b) the client-specified window geometry, if
|
|
|
|
// any. If the client hasn't provided the window geometry, we fallback to the bounding sub-
|
|
|
|
// surface rectangle. If the client has provided the window geometry, we intersect it with
|
|
|
|
// the bounding rectangle and that will be the effective window geometry. It's worth to point
|
|
|
|
// out that geometry updates do not occur that frequently, so we don't need to recompute the
|
|
|
|
// bounding geometry every time the client commits the surface.
|
2018-10-05 14:27:05 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
SubSurfaceMonitor *treeMonitor = new SubSurfaceMonitor(surface(), this);
|
2015-07-15 09:24:19 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
connect(treeMonitor, &SubSurfaceMonitor::subSurfaceAdded,
|
|
|
|
this, &XdgSurfaceClient::setHaveNextWindowGeometry);
|
|
|
|
connect(treeMonitor, &SubSurfaceMonitor::subSurfaceRemoved,
|
|
|
|
this, &XdgSurfaceClient::setHaveNextWindowGeometry);
|
|
|
|
connect(treeMonitor, &SubSurfaceMonitor::subSurfaceMoved,
|
|
|
|
this, &XdgSurfaceClient::setHaveNextWindowGeometry);
|
|
|
|
connect(treeMonitor, &SubSurfaceMonitor::subSurfaceResized,
|
|
|
|
this, &XdgSurfaceClient::setHaveNextWindowGeometry);
|
|
|
|
connect(shellSurface, &XdgSurfaceInterface::windowGeometryChanged,
|
|
|
|
this, &XdgSurfaceClient::setHaveNextWindowGeometry);
|
|
|
|
connect(surface(), &SurfaceInterface::sizeChanged,
|
|
|
|
this, &XdgSurfaceClient::setHaveNextWindowGeometry);
|
2017-10-07 12:09:07 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
// Configure events are not sent immediately, but rather scheduled to be sent when the event
|
|
|
|
// loop is about to be idle. By doing this, we can avoid sending configure events that do
|
|
|
|
// nothing, and implementation-wise, it's simpler.
|
2015-09-11 10:11:01 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
m_configureTimer->setSingleShot(true);
|
|
|
|
connect(m_configureTimer, &QTimer::timeout, this, &XdgSurfaceClient::sendConfigure);
|
2015-03-04 08:21:10 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
XdgSurfaceClient::~XdgSurfaceClient()
|
2019-09-15 11:26:33 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
qDeleteAll(m_configureEvents);
|
2019-02-26 13:41:07 +00:00
|
|
|
}
|
|
|
|
|
2021-11-15 14:03:53 +00:00
|
|
|
NET::WindowType XdgSurfaceClient::windowType(bool direct, int supported_types) const
|
|
|
|
{
|
|
|
|
Q_UNUSED(direct)
|
|
|
|
Q_UNUSED(supported_types)
|
|
|
|
return m_windowType;
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
QRect XdgSurfaceClient::inputGeometry() const
|
2015-03-04 08:21:10 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
return isDecorated() ? AbstractClient::inputGeometry() : bufferGeometry();
|
2015-03-04 08:21:10 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
QMatrix4x4 XdgSurfaceClient::inputTransformation() const
|
2015-03-04 08:21:10 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
QMatrix4x4 transformation;
|
2020-08-18 12:51:20 +00:00
|
|
|
transformation.translate(-bufferGeometry().x(), -bufferGeometry().y());
|
2020-02-17 18:39:17 +00:00
|
|
|
return transformation;
|
2015-03-04 08:21:10 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
XdgSurfaceConfigure *XdgSurfaceClient::lastAcknowledgedConfigure() const
|
2019-02-27 10:52:44 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
return m_lastAcknowledgedConfigure.data();
|
2019-02-27 10:52:44 +00:00
|
|
|
}
|
|
|
|
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
void XdgSurfaceClient::scheduleConfigure()
|
2015-03-16 08:13:19 +00:00
|
|
|
{
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
if (!isZombie()) {
|
2020-02-17 18:39:17 +00:00
|
|
|
m_configureTimer->start();
|
|
|
|
}
|
2015-03-04 08:21:10 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgSurfaceClient::sendConfigure()
|
2015-07-09 07:10:33 +00:00
|
|
|
{
|
2021-05-13 07:18:10 +00:00
|
|
|
XdgSurfaceConfigure *configureEvent = sendRoleConfigure();
|
|
|
|
|
2021-09-15 11:58:45 +00:00
|
|
|
// The configure event inherits configure flags from the previous event.
|
|
|
|
if (!m_configureEvents.isEmpty()) {
|
|
|
|
const XdgSurfaceConfigure *previousEvent = m_configureEvents.constLast();
|
|
|
|
configureEvent->flags = previousEvent->flags;
|
|
|
|
}
|
|
|
|
|
2022-01-06 10:16:09 +00:00
|
|
|
configureEvent->gravity = m_nextGravity;
|
2021-09-15 11:58:45 +00:00
|
|
|
configureEvent->flags |= m_configureFlags;
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
m_configureFlags = {};
|
2021-09-15 11:58:45 +00:00
|
|
|
|
2021-05-13 07:18:10 +00:00
|
|
|
m_configureEvents.append(configureEvent);
|
2015-07-09 07:10:33 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgSurfaceClient::handleConfigureAcknowledged(quint32 serial)
|
2015-12-17 14:47:36 +00:00
|
|
|
{
|
2022-01-24 13:16:45 +00:00
|
|
|
m_lastAcknowledgedConfigureSerial = serial;
|
2015-12-17 14:47:36 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgSurfaceClient::handleCommit()
|
2015-10-13 08:52:01 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
if (!surface()->buffer()) {
|
2017-11-22 16:33:21 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-09-26 15:14:42 +00:00
|
|
|
|
2022-01-24 13:16:45 +00:00
|
|
|
if (m_lastAcknowledgedConfigureSerial.has_value()) {
|
|
|
|
const quint32 serial = m_lastAcknowledgedConfigureSerial.value();
|
|
|
|
while (!m_configureEvents.isEmpty()) {
|
|
|
|
if (serial < m_configureEvents.constFirst()->serial) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
m_lastAcknowledgedConfigure.reset(m_configureEvents.takeFirst());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
handleRolePrecommit();
|
2020-02-17 18:39:17 +00:00
|
|
|
if (haveNextWindowGeometry()) {
|
|
|
|
handleNextWindowGeometry();
|
|
|
|
resetHaveNextWindowGeometry();
|
2017-11-22 16:33:21 +00:00
|
|
|
}
|
2019-09-26 15:14:42 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
handleRoleCommit();
|
|
|
|
m_lastAcknowledgedConfigure.reset();
|
2022-01-24 13:16:45 +00:00
|
|
|
m_lastAcknowledgedConfigureSerial.reset();
|
2019-04-09 10:13:38 +00:00
|
|
|
|
2020-05-07 14:29:41 +00:00
|
|
|
setReadyForPainting();
|
2020-02-17 18:39:17 +00:00
|
|
|
updateDepth();
|
2015-10-13 08:52:01 +00:00
|
|
|
}
|
|
|
|
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
void XdgSurfaceClient::handleRolePrecommit()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgSurfaceClient::handleRoleCommit()
|
2019-10-03 19:43:28 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
void XdgSurfaceClient::maybeUpdateMoveResizeGeometry(const QRect &rect)
|
|
|
|
{
|
|
|
|
// We are about to send a configure event, ignore the committed window geometry.
|
|
|
|
if (m_configureTimer->isActive()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there are unacknowledged configure events that change the geometry, don't sync
|
|
|
|
// the move resize geometry in order to avoid rolling back to old state. When the last
|
|
|
|
// configure event is acknowledged, the move resize geometry will be synchronized.
|
|
|
|
for (int i = m_configureEvents.count() - 1; i >= 0; --i) {
|
|
|
|
if (m_configureEvents[i]->flags & XdgSurfaceConfigure::ConfigurePosition) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setMoveResizeGeometry(rect);
|
|
|
|
}
|
|
|
|
|
2022-01-06 10:16:09 +00:00
|
|
|
static QRect gravitateGeometry(const QRect &rect, const QRect &bounds, Gravity gravity)
|
|
|
|
{
|
|
|
|
QRect geometry = rect;
|
|
|
|
|
|
|
|
switch (gravity) {
|
|
|
|
case Gravity::TopLeft:
|
|
|
|
geometry.moveRight(bounds.right());
|
|
|
|
geometry.moveBottom(bounds.bottom());
|
|
|
|
break;
|
|
|
|
case Gravity::Top:
|
|
|
|
case Gravity::TopRight:
|
|
|
|
geometry.moveLeft(bounds.left());
|
|
|
|
geometry.moveBottom(bounds.bottom());
|
|
|
|
break;
|
|
|
|
case Gravity::Right:
|
|
|
|
case Gravity::BottomRight:
|
|
|
|
case Gravity::Bottom:
|
|
|
|
case Gravity::None:
|
|
|
|
geometry.moveLeft(bounds.left());
|
|
|
|
geometry.moveTop(bounds.top());
|
|
|
|
break;
|
|
|
|
case Gravity::BottomLeft:
|
|
|
|
case Gravity::Left:
|
|
|
|
geometry.moveRight(bounds.right());
|
|
|
|
geometry.moveTop(bounds.top());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return geometry;
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgSurfaceClient::handleNextWindowGeometry()
|
2015-03-04 08:21:10 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
const QRect boundingGeometry = surface()->boundingRect();
|
2019-10-03 19:43:28 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
// The effective window geometry is defined as the intersection of the window geometry
|
|
|
|
// and the rectangle that bounds the main surface and all of its sub-surfaces. If the
|
|
|
|
// client hasn't specified the window geometry, we must fallback to the bounding geometry.
|
|
|
|
// Note that the xdg-shell spec is not clear about when exactly we have to clamp the
|
|
|
|
// window geometry.
|
2019-10-03 19:43:28 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
m_windowGeometry = m_shellSurface->windowGeometry();
|
|
|
|
if (m_windowGeometry.isValid()) {
|
|
|
|
m_windowGeometry &= boundingGeometry;
|
|
|
|
} else {
|
|
|
|
m_windowGeometry = boundingGeometry;
|
2015-08-20 07:38:19 +00:00
|
|
|
}
|
2019-07-09 17:28:05 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
if (m_windowGeometry.isEmpty()) {
|
|
|
|
qCWarning(KWIN_CORE) << "Committed empty window geometry, dealing with a buggy client!";
|
2019-10-03 19:43:28 +00:00
|
|
|
}
|
2015-10-14 10:11:56 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
QRect frameGeometry(pos(), clientSizeToFrameSize(m_windowGeometry.size()));
|
2022-01-06 10:16:09 +00:00
|
|
|
if (const XdgSurfaceConfigure *configureEvent = lastAcknowledgedConfigure()) {
|
|
|
|
if (configureEvent->flags & XdgSurfaceConfigure::ConfigurePosition) {
|
|
|
|
frameGeometry = gravitateGeometry(frameGeometry, configureEvent->bounds, configureEvent->gravity);
|
2019-10-03 19:43:28 +00:00
|
|
|
}
|
2022-01-06 10:16:09 +00:00
|
|
|
}
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
|
2022-01-06 10:16:09 +00:00
|
|
|
if (!isInteractiveMoveResize()) {
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
// Both the compositor and the client can change the window geometry. If the client
|
|
|
|
// sets a new window geometry, the compositor's move-resize geometry will be invalid.
|
|
|
|
maybeUpdateMoveResizeGeometry(frameGeometry);
|
2016-06-10 08:50:02 +00:00
|
|
|
}
|
2019-10-03 19:43:28 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
updateGeometry(frameGeometry);
|
2015-03-04 08:21:10 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgSurfaceClient::haveNextWindowGeometry() const
|
2019-10-03 19:43:28 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
return m_haveNextWindowGeometry || m_lastAcknowledgedConfigure;
|
2019-10-03 19:43:28 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgSurfaceClient::setHaveNextWindowGeometry()
|
2015-03-04 08:21:10 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
m_haveNextWindowGeometry = true;
|
2015-03-04 08:21:10 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgSurfaceClient::resetHaveNextWindowGeometry()
|
2015-03-16 08:14:04 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
m_haveNextWindowGeometry = false;
|
2015-03-16 08:14:04 +00:00
|
|
|
}
|
|
|
|
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
void XdgSurfaceClient::moveResizeInternal(const QRect &rect, MoveResizeMode mode)
|
2015-03-16 08:14:04 +00:00
|
|
|
{
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
if (areGeometryUpdatesBlocked()) {
|
|
|
|
setPendingMoveResizeMode(mode);
|
|
|
|
return;
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
if (mode != MoveResizeMode::Move) {
|
|
|
|
const QSize requestedClientSize = frameSizeToClientSize(rect.size());
|
|
|
|
if (requestedClientSize == clientSize()) {
|
|
|
|
updateGeometry(rect);
|
|
|
|
} else {
|
|
|
|
m_configureFlags |= XdgSurfaceConfigure::ConfigurePosition;
|
|
|
|
scheduleConfigure();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If the window is moved, cancel any queued window position updates.
|
|
|
|
for (XdgSurfaceConfigure *configureEvent : qAsConst(m_configureEvents)) {
|
|
|
|
configureEvent->flags.setFlag(XdgSurfaceConfigure::ConfigurePosition, false);
|
|
|
|
}
|
|
|
|
m_configureFlags.setFlag(XdgSurfaceConfigure::ConfigurePosition, false);
|
|
|
|
updateGeometry(QRect(rect.topLeft(), size()));
|
|
|
|
}
|
2015-03-16 08:14:04 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
QRect XdgSurfaceClient::frameRectToBufferRect(const QRect &rect) const
|
2015-03-16 08:14:04 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
const int left = rect.left() + borderLeft() - m_windowGeometry.left();
|
|
|
|
const int top = rect.top() + borderTop() - m_windowGeometry.top();
|
|
|
|
return QRect(QPoint(left, top), surface()->size());
|
2015-03-16 08:14:04 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgSurfaceClient::destroyClient()
|
|
|
|
{
|
2020-07-16 07:17:19 +00:00
|
|
|
markAsZombie();
|
2021-04-30 18:06:58 +00:00
|
|
|
if (isInteractiveMoveResize()) {
|
|
|
|
leaveInteractiveMoveResize();
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT clientFinishUserMovedResized(this);
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
2021-05-14 05:32:49 +00:00
|
|
|
m_configureTimer->stop();
|
2020-02-17 18:39:17 +00:00
|
|
|
cleanTabBox();
|
|
|
|
Deleted *deleted = Deleted::create(this);
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT windowClosed(this, deleted);
|
2019-07-09 17:28:05 +00:00
|
|
|
StackingUpdatesBlocker blocker(workspace());
|
2020-02-17 18:39:17 +00:00
|
|
|
RuleBook::self()->discardUsed(this, true);
|
2021-12-09 11:55:13 +00:00
|
|
|
setDecoration(nullptr);
|
2020-02-17 18:39:17 +00:00
|
|
|
cleanGrouping();
|
|
|
|
waylandServer()->removeClient(this);
|
|
|
|
deleted->unrefWindow();
|
|
|
|
delete this;
|
|
|
|
}
|
2019-07-09 17:28:05 +00:00
|
|
|
|
2021-11-15 14:03:53 +00:00
|
|
|
void XdgSurfaceClient::updateClientArea()
|
|
|
|
{
|
|
|
|
if (hasStrut()) {
|
|
|
|
workspace()->updateClientArea();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgSurfaceClient::updateShowOnScreenEdge()
|
|
|
|
{
|
|
|
|
if (!ScreenEdges::self()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!readyForPainting() || !m_plasmaShellSurface ||
|
|
|
|
m_plasmaShellSurface->role() != PlasmaShellSurfaceInterface::Role::Panel) {
|
|
|
|
ScreenEdges::self()->reserve(this, ElectricNone);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const PlasmaShellSurfaceInterface::PanelBehavior panelBehavior = m_plasmaShellSurface->panelBehavior();
|
|
|
|
if ((panelBehavior == PlasmaShellSurfaceInterface::PanelBehavior::AutoHide && isHidden()) ||
|
|
|
|
panelBehavior == PlasmaShellSurfaceInterface::PanelBehavior::WindowsCanCover) {
|
|
|
|
// Screen edge API requires an edge, thus we need to figure out which edge the window borders.
|
|
|
|
const QRect clientGeometry = frameGeometry();
|
|
|
|
Qt::Edges edges;
|
|
|
|
|
|
|
|
const auto outputs = kwinApp()->platform()->enabledOutputs();
|
|
|
|
for (const AbstractOutput *output : outputs) {
|
|
|
|
const QRect screenGeometry = output->geometry();
|
|
|
|
if (screenGeometry.left() == clientGeometry.left()) {
|
|
|
|
edges |= Qt::LeftEdge;
|
|
|
|
}
|
|
|
|
if (screenGeometry.right() == clientGeometry.right()) {
|
|
|
|
edges |= Qt::RightEdge;
|
|
|
|
}
|
|
|
|
if (screenGeometry.top() == clientGeometry.top()) {
|
|
|
|
edges |= Qt::TopEdge;
|
|
|
|
}
|
|
|
|
if (screenGeometry.bottom() == clientGeometry.bottom()) {
|
|
|
|
edges |= Qt::BottomEdge;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A panel might border multiple screen edges. E.g. a horizontal panel at the bottom will
|
|
|
|
// also border the left and right edge. Let's remove such cases.
|
|
|
|
if (edges & Qt::LeftEdge && edges & Qt::RightEdge) {
|
|
|
|
edges = edges & (~(Qt::LeftEdge | Qt::RightEdge));
|
|
|
|
}
|
|
|
|
if (edges & Qt::TopEdge && edges & Qt::BottomEdge) {
|
|
|
|
edges = edges & (~(Qt::TopEdge | Qt::BottomEdge));
|
|
|
|
}
|
|
|
|
|
|
|
|
// It's still possible that a panel borders two edges, e.g. bottom and left
|
|
|
|
// in that case the one which is sharing more with the edge wins.
|
|
|
|
auto check = [clientGeometry](Qt::Edges edges, Qt::Edge horizontal, Qt::Edge vertical) {
|
|
|
|
if (edges & horizontal && edges & vertical) {
|
|
|
|
if (clientGeometry.width() >= clientGeometry.height()) {
|
|
|
|
return edges & ~horizontal;
|
|
|
|
} else {
|
|
|
|
return edges & ~vertical;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return edges;
|
|
|
|
};
|
|
|
|
edges = check(edges, Qt::LeftEdge, Qt::TopEdge);
|
|
|
|
edges = check(edges, Qt::LeftEdge, Qt::BottomEdge);
|
|
|
|
edges = check(edges, Qt::RightEdge, Qt::TopEdge);
|
|
|
|
edges = check(edges, Qt::RightEdge, Qt::BottomEdge);
|
|
|
|
|
|
|
|
ElectricBorder border = ElectricNone;
|
|
|
|
if (edges & Qt::LeftEdge) {
|
|
|
|
border = ElectricLeft;
|
|
|
|
}
|
|
|
|
if (edges & Qt::RightEdge) {
|
|
|
|
border = ElectricRight;
|
|
|
|
}
|
|
|
|
if (edges & Qt::TopEdge) {
|
|
|
|
border = ElectricTop;
|
|
|
|
}
|
|
|
|
if (edges & Qt::BottomEdge) {
|
|
|
|
border = ElectricBottom;
|
|
|
|
}
|
|
|
|
ScreenEdges::self()->reserve(this, border);
|
|
|
|
} else {
|
|
|
|
ScreenEdges::self()->reserve(this, ElectricNone);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \todo This whole plasma shell surface thing doesn't seem right. It turns xdg-toplevel into
|
|
|
|
* something completely different! Perhaps plasmashell surfaces need to be implemented via a
|
|
|
|
* proprietary protocol that doesn't piggyback on existing shell surface protocols. It'll lead
|
|
|
|
* to cleaner code and will be technically correct, but I'm not sure whether this is do-able.
|
|
|
|
*/
|
|
|
|
void XdgSurfaceClient::installPlasmaShellSurface(PlasmaShellSurfaceInterface *shellSurface)
|
|
|
|
{
|
|
|
|
m_plasmaShellSurface = shellSurface;
|
|
|
|
|
|
|
|
auto updatePosition = [this, shellSurface] { move(shellSurface->position()); };
|
|
|
|
auto updateRole = [this, shellSurface] {
|
|
|
|
NET::WindowType type = NET::Unknown;
|
|
|
|
switch (shellSurface->role()) {
|
|
|
|
case PlasmaShellSurfaceInterface::Role::Desktop:
|
|
|
|
type = NET::Desktop;
|
|
|
|
break;
|
|
|
|
case PlasmaShellSurfaceInterface::Role::Panel:
|
|
|
|
type = NET::Dock;
|
|
|
|
break;
|
|
|
|
case PlasmaShellSurfaceInterface::Role::OnScreenDisplay:
|
|
|
|
type = NET::OnScreenDisplay;
|
|
|
|
break;
|
|
|
|
case PlasmaShellSurfaceInterface::Role::Notification:
|
|
|
|
type = NET::Notification;
|
|
|
|
break;
|
|
|
|
case PlasmaShellSurfaceInterface::Role::ToolTip:
|
|
|
|
type = NET::Tooltip;
|
|
|
|
break;
|
|
|
|
case PlasmaShellSurfaceInterface::Role::CriticalNotification:
|
|
|
|
type = NET::CriticalNotification;
|
|
|
|
break;
|
|
|
|
case PlasmaShellSurfaceInterface::Role::Normal:
|
|
|
|
default:
|
|
|
|
type = NET::Normal;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (m_windowType == type) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_windowType = type;
|
|
|
|
switch (m_windowType) {
|
|
|
|
case NET::Desktop:
|
|
|
|
case NET::Dock:
|
|
|
|
case NET::OnScreenDisplay:
|
|
|
|
case NET::Notification:
|
|
|
|
case NET::CriticalNotification:
|
|
|
|
case NET::Tooltip:
|
|
|
|
setOnAllDesktops(true);
|
|
|
|
#if KWIN_BUILD_ACTIVITIES
|
|
|
|
setOnAllActivities(true);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
workspace()->updateClientArea();
|
|
|
|
};
|
|
|
|
connect(shellSurface, &PlasmaShellSurfaceInterface::positionChanged, this, updatePosition);
|
|
|
|
connect(shellSurface, &PlasmaShellSurfaceInterface::roleChanged, this, updateRole);
|
|
|
|
connect(shellSurface, &PlasmaShellSurfaceInterface::panelBehaviorChanged, this, [this] {
|
|
|
|
updateShowOnScreenEdge();
|
|
|
|
workspace()->updateClientArea();
|
|
|
|
});
|
|
|
|
connect(shellSurface, &PlasmaShellSurfaceInterface::panelAutoHideHideRequested, this, [this] {
|
|
|
|
if (m_plasmaShellSurface->panelBehavior() == PlasmaShellSurfaceInterface::PanelBehavior::AutoHide) {
|
|
|
|
hideClient();
|
|
|
|
m_plasmaShellSurface->hideAutoHidingPanel();
|
|
|
|
}
|
|
|
|
updateShowOnScreenEdge();
|
|
|
|
});
|
|
|
|
connect(shellSurface, &PlasmaShellSurfaceInterface::panelAutoHideShowRequested, this, [this] {
|
|
|
|
showClient();
|
|
|
|
ScreenEdges::self()->reserve(this, ElectricNone);
|
|
|
|
m_plasmaShellSurface->showAutoHidingPanel();
|
|
|
|
});
|
|
|
|
connect(shellSurface, &PlasmaShellSurfaceInterface::panelTakesFocusChanged, this, [this] {
|
|
|
|
if (m_plasmaShellSurface->panelTakesFocus()) {
|
|
|
|
workspace()->activateClient(this);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (shellSurface->isPositionSet()) {
|
|
|
|
updatePosition();
|
|
|
|
}
|
|
|
|
updateRole();
|
|
|
|
updateShowOnScreenEdge();
|
|
|
|
connect(this, &XdgSurfaceClient::frameGeometryChanged,
|
|
|
|
this, &XdgSurfaceClient::updateShowOnScreenEdge);
|
|
|
|
connect(this, &XdgSurfaceClient::windowShown,
|
|
|
|
this, &XdgSurfaceClient::updateShowOnScreenEdge);
|
|
|
|
|
|
|
|
setSkipTaskbar(shellSurface->skipTaskbar());
|
|
|
|
connect(shellSurface, &PlasmaShellSurfaceInterface::skipTaskbarChanged, this, [this] {
|
|
|
|
setSkipTaskbar(m_plasmaShellSurface->skipTaskbar());
|
|
|
|
});
|
|
|
|
|
|
|
|
setSkipSwitcher(shellSurface->skipSwitcher());
|
|
|
|
connect(shellSurface, &PlasmaShellSurfaceInterface::skipSwitcherChanged, this, [this] {
|
|
|
|
setSkipSwitcher(m_plasmaShellSurface->skipSwitcher());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgSurfaceClient::setupPlasmaShellIntegration()
|
|
|
|
{
|
|
|
|
connect(surface(), &SurfaceInterface::mapped,
|
|
|
|
this, &XdgSurfaceClient::updateShowOnScreenEdge);
|
|
|
|
connect(this, &XdgSurfaceClient::frameGeometryChanged,
|
|
|
|
this, &XdgSurfaceClient::updateClientArea);
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
XdgToplevelClient::XdgToplevelClient(XdgToplevelInterface *shellSurface)
|
|
|
|
: XdgSurfaceClient(shellSurface->xdgSurface())
|
|
|
|
, m_shellSurface(shellSurface)
|
|
|
|
{
|
2021-08-13 06:37:20 +00:00
|
|
|
setDesktops({VirtualDesktopManager::self()->currentDesktop()});
|
2021-06-19 20:43:08 +00:00
|
|
|
#if KWIN_BUILD_ACTIVITIES
|
|
|
|
if (auto a = Activities::self()) {
|
|
|
|
setOnActivities({a->current()});
|
|
|
|
}
|
|
|
|
#endif
|
2021-09-05 15:23:36 +00:00
|
|
|
move(workspace()->activeOutput()->geometry().center());
|
2020-02-17 18:39:17 +00:00
|
|
|
|
|
|
|
connect(shellSurface, &XdgToplevelInterface::windowTitleChanged,
|
|
|
|
this, &XdgToplevelClient::handleWindowTitleChanged);
|
|
|
|
connect(shellSurface, &XdgToplevelInterface::windowClassChanged,
|
|
|
|
this, &XdgToplevelClient::handleWindowClassChanged);
|
|
|
|
connect(shellSurface, &XdgToplevelInterface::windowMenuRequested,
|
|
|
|
this, &XdgToplevelClient::handleWindowMenuRequested);
|
|
|
|
connect(shellSurface, &XdgToplevelInterface::moveRequested,
|
|
|
|
this, &XdgToplevelClient::handleMoveRequested);
|
|
|
|
connect(shellSurface, &XdgToplevelInterface::resizeRequested,
|
|
|
|
this, &XdgToplevelClient::handleResizeRequested);
|
|
|
|
connect(shellSurface, &XdgToplevelInterface::maximizeRequested,
|
|
|
|
this, &XdgToplevelClient::handleMaximizeRequested);
|
|
|
|
connect(shellSurface, &XdgToplevelInterface::unmaximizeRequested,
|
|
|
|
this, &XdgToplevelClient::handleUnmaximizeRequested);
|
|
|
|
connect(shellSurface, &XdgToplevelInterface::fullscreenRequested,
|
|
|
|
this, &XdgToplevelClient::handleFullscreenRequested);
|
|
|
|
connect(shellSurface, &XdgToplevelInterface::unfullscreenRequested,
|
|
|
|
this, &XdgToplevelClient::handleUnfullscreenRequested);
|
|
|
|
connect(shellSurface, &XdgToplevelInterface::minimizeRequested,
|
|
|
|
this, &XdgToplevelClient::handleMinimizeRequested);
|
|
|
|
connect(shellSurface, &XdgToplevelInterface::parentXdgToplevelChanged,
|
|
|
|
this, &XdgToplevelClient::handleTransientForChanged);
|
|
|
|
connect(shellSurface, &XdgToplevelInterface::initializeRequested,
|
|
|
|
this, &XdgToplevelClient::initialize);
|
2021-08-12 10:03:35 +00:00
|
|
|
connect(shellSurface, &XdgToplevelInterface::aboutToBeDestroyed,
|
2020-02-17 18:39:17 +00:00
|
|
|
this, &XdgToplevelClient::destroyClient);
|
2021-07-07 11:50:02 +00:00
|
|
|
connect(shellSurface, &XdgToplevelInterface::maximumSizeChanged,
|
|
|
|
this, &XdgToplevelClient::handleMaximumSizeChanged);
|
|
|
|
connect(shellSurface, &XdgToplevelInterface::minimumSizeChanged,
|
|
|
|
this, &XdgToplevelClient::handleMinimumSizeChanged);
|
2020-02-17 18:39:17 +00:00
|
|
|
connect(shellSurface->shell(), &XdgShellInterface::pingTimeout,
|
|
|
|
this, &XdgToplevelClient::handlePingTimeout);
|
|
|
|
connect(shellSurface->shell(), &XdgShellInterface::pingDelayed,
|
|
|
|
this, &XdgToplevelClient::handlePingDelayed);
|
|
|
|
connect(shellSurface->shell(), &XdgShellInterface::pongReceived,
|
|
|
|
this, &XdgToplevelClient::handlePongReceived);
|
2016-09-16 12:27:50 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
connect(waylandServer(), &WaylandServer::foreignTransientChanged,
|
|
|
|
this, &XdgToplevelClient::handleForeignTransientForChanged);
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgToplevelClient::~XdgToplevelClient()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-05-07 14:29:41 +00:00
|
|
|
XdgToplevelInterface *XdgToplevelClient::shellSurface() const
|
|
|
|
{
|
|
|
|
return m_shellSurface;
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
MaximizeMode XdgToplevelClient::maximizeMode() const
|
2015-03-16 08:14:04 +00:00
|
|
|
{
|
2015-06-07 01:28:38 +00:00
|
|
|
return m_maximizeMode;
|
2015-03-16 08:14:04 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
MaximizeMode XdgToplevelClient::requestedMaximizeMode() const
|
2018-10-07 16:51:42 +00:00
|
|
|
{
|
|
|
|
return m_requestedMaximizeMode;
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
QSize XdgToplevelClient::minSize() const
|
2015-03-16 08:14:04 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
return rules()->checkMinSize(m_shellSurface->minimumSize());
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize XdgToplevelClient::maxSize() const
|
|
|
|
{
|
|
|
|
return rules()->checkMaxSize(m_shellSurface->maximumSize());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XdgToplevelClient::isFullScreen() const
|
|
|
|
{
|
|
|
|
return m_isFullScreen;
|
|
|
|
}
|
|
|
|
|
2021-05-01 15:07:11 +00:00
|
|
|
bool XdgToplevelClient::isRequestedFullScreen() const
|
|
|
|
{
|
|
|
|
return m_isRequestedFullScreen;
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgToplevelClient::isMovable() const
|
|
|
|
{
|
2021-05-01 15:07:11 +00:00
|
|
|
if (isRequestedFullScreen()) {
|
2020-02-17 18:39:17 +00:00
|
|
|
return false;
|
2015-12-17 14:47:36 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
if (isSpecialWindow() && !isSplash() && !isToolbar()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (rules()->checkPosition(invalidPoint) != invalidPoint) {
|
|
|
|
return false;
|
2019-01-01 17:37:18 +00:00
|
|
|
}
|
2015-03-16 08:14:04 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgToplevelClient::isMovableAcrossScreens() const
|
2019-01-09 14:20:19 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
if (isSpecialWindow() && !isSplash() && !isToolbar()) {
|
2019-01-09 14:20:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
if (rules()->checkPosition(invalidPoint) != invalidPoint) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2019-01-09 14:20:19 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgToplevelClient::isResizable() const
|
2015-03-16 08:14:04 +00:00
|
|
|
{
|
2021-05-01 15:07:11 +00:00
|
|
|
if (isRequestedFullScreen()) {
|
2020-02-17 18:39:17 +00:00
|
|
|
return false;
|
2019-01-09 19:49:10 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
if (isSpecialWindow() || isSplash() || isToolbar()) {
|
|
|
|
return false;
|
2019-01-09 19:49:10 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
if (rules()->checkSize(QSize()).isValid()) {
|
|
|
|
return false;
|
2019-01-09 19:49:10 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
const QSize min = minSize();
|
|
|
|
const QSize max = maxSize();
|
|
|
|
return min.width() < max.width() || min.height() < max.height();
|
|
|
|
}
|
2019-01-09 19:49:10 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgToplevelClient::isCloseable() const
|
|
|
|
{
|
|
|
|
return !isDesktop() && !isDock();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XdgToplevelClient::isFullScreenable() const
|
|
|
|
{
|
|
|
|
if (!rules()->checkFullScreen(true)) {
|
|
|
|
return false;
|
2018-11-16 11:59:33 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
return !isSpecialWindow();
|
|
|
|
}
|
2019-01-09 19:49:10 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgToplevelClient::isMaximizable() const
|
|
|
|
{
|
|
|
|
if (!isResizable()) {
|
|
|
|
return false;
|
2017-10-07 09:41:17 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
if (rules()->checkMaximize(MaximizeRestore) != MaximizeRestore ||
|
|
|
|
rules()->checkMaximize(MaximizeFull) != MaximizeFull) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2019-01-09 19:49:10 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgToplevelClient::isMinimizable() const
|
|
|
|
{
|
|
|
|
if (isSpecialWindow() && !isTransient()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!rules()->checkMinimize(true)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2019-01-09 19:49:10 +00:00
|
|
|
|
2020-11-19 15:31:42 +00:00
|
|
|
bool XdgToplevelClient::isPlaceable() const
|
|
|
|
{
|
|
|
|
return !m_plasmaShellSurface || !m_plasmaShellSurface->isPositionSet();
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgToplevelClient::isTransient() const
|
|
|
|
{
|
|
|
|
return m_isTransient;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XdgToplevelClient::userCanSetFullScreen() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XdgToplevelClient::userCanSetNoBorder() const
|
|
|
|
{
|
2022-01-21 15:41:30 +00:00
|
|
|
return (m_serverDecoration || m_xdgDecoration) && !isFullScreen() && !isShade();
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool XdgToplevelClient::noBorder() const
|
|
|
|
{
|
wayland: Allow setting "no border" flag even without xdg decoration
Historically, noBorder() was used for two things:
* as a substitute for AbstractClient::isDecorated()
* to determine whether the AbstractClient should have a decoration
With async decoration updates refactoring, a few things around
noBorder() have changed, which exposed an existing bug in the handling
of borderless maximized windows.
It's possible to have a case where an initially maximized window makes
an xdg_toplevel.set_maximized request before the initial commit, but
creates the decoration object after the initial commit.
Since XdgToplevelClient::userCanSetNoBorder() would return false when
maximize() is called in XdgToplevelClient::initialize(), m_userNoBorder
won't be updated and therefore the window can end up having a server
side decoration.
Previously, it wasn't the case because kwin would do nothing if the
decoration is installed and its preferred mode changes after the initial
commit but before the surface is mapped. With async decoration fixes,
kwin would react as expected, which unfortunately has exposed the bug.
The root cause of the problem is the fact that noBorder() is overloaded,
which makes it error-prone.
This patch changes how the noBorder property is treated. Now, it only
indicates whether the compositor wants the window to have no borders. If
noBorder() is true, it means that the compositor doesn't want the window
to have a server-side decoration; on the other hand, if noBorder() is
false, it doesn't imply that the window should have a decoration.
BUG: 448740
2022-01-20 15:10:08 +00:00
|
|
|
return m_userNoBorder;
|
2015-03-16 08:14:04 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::setNoBorder(bool set)
|
2015-03-16 08:14:04 +00:00
|
|
|
{
|
2015-12-17 14:47:36 +00:00
|
|
|
set = rules()->checkNoBorder(set);
|
|
|
|
if (m_userNoBorder == set) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_userNoBorder = set;
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
configureDecoration();
|
2015-12-17 14:47:36 +00:00
|
|
|
updateWindowRules(Rules::NoBorder);
|
2015-03-16 08:14:04 +00:00
|
|
|
}
|
|
|
|
|
2021-12-08 13:21:48 +00:00
|
|
|
void XdgToplevelClient::invalidateDecoration()
|
|
|
|
{
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
clearDecoration();
|
|
|
|
configureDecoration();
|
2021-12-08 13:21:48 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgToplevelClient::supportsWindowRules() const
|
2015-07-07 14:15:58 +00:00
|
|
|
{
|
2021-11-17 16:36:00 +00:00
|
|
|
return true;
|
2015-07-07 14:15:58 +00:00
|
|
|
}
|
|
|
|
|
2020-08-17 13:14:20 +00:00
|
|
|
StrutRect XdgToplevelClient::strutRect(StrutArea area) const
|
|
|
|
{
|
|
|
|
if (!hasStrut()) {
|
|
|
|
return StrutRect();
|
|
|
|
}
|
|
|
|
|
|
|
|
const QRect windowRect = frameGeometry();
|
2021-08-28 13:43:03 +00:00
|
|
|
const QRect outputRect = output()->geometry();
|
2020-08-17 13:14:20 +00:00
|
|
|
|
|
|
|
const bool left = windowRect.left() == outputRect.left();
|
|
|
|
const bool right = windowRect.right() == outputRect.right();
|
|
|
|
const bool top = windowRect.top() == outputRect.top();
|
|
|
|
const bool bottom = windowRect.bottom() == outputRect.bottom();
|
|
|
|
const bool horizontal = width() >= height();
|
|
|
|
|
|
|
|
switch (area) {
|
|
|
|
case StrutAreaTop:
|
|
|
|
if (top && ((!left && !right) || horizontal)) {
|
|
|
|
return StrutRect(windowRect, StrutAreaTop);
|
|
|
|
}
|
|
|
|
return StrutRect();
|
|
|
|
case StrutAreaRight:
|
|
|
|
if (right && ((!top && !bottom) || !horizontal)) {
|
|
|
|
return StrutRect(windowRect, StrutAreaRight);
|
|
|
|
}
|
|
|
|
return StrutRect();
|
|
|
|
case StrutAreaBottom:
|
|
|
|
if (bottom && ((!left && !right) || horizontal)) {
|
|
|
|
return StrutRect(windowRect, StrutAreaBottom);
|
|
|
|
}
|
|
|
|
return StrutRect();
|
|
|
|
case StrutAreaLeft:
|
|
|
|
if (left && ((!top && !bottom) || !horizontal)) {
|
|
|
|
return StrutRect(windowRect, StrutAreaLeft);
|
|
|
|
}
|
|
|
|
return StrutRect();
|
|
|
|
default:
|
|
|
|
return StrutRect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgToplevelClient::hasStrut() const
|
2015-03-16 08:14:04 +00:00
|
|
|
{
|
2021-11-23 12:13:56 +00:00
|
|
|
if (!isShown()) {
|
2020-02-17 18:39:17 +00:00
|
|
|
return false;
|
2017-10-07 09:41:17 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
if (!m_plasmaShellSurface) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (m_plasmaShellSurface->role() != PlasmaShellSurfaceInterface::Role::Panel) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return m_plasmaShellSurface->panelBehavior() == PlasmaShellSurfaceInterface::PanelBehavior::AlwaysVisible;
|
2015-03-16 08:14:04 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::showOnScreenEdge()
|
2015-03-16 08:14:04 +00:00
|
|
|
{
|
2020-06-25 05:29:53 +00:00
|
|
|
// ShowOnScreenEdge can be called by an Edge, and hideClient could destroy the Edge
|
|
|
|
// Use the singleshot to avoid use-after-free
|
2020-09-24 01:52:11 +00:00
|
|
|
QTimer::singleShot(0, this, [this](){
|
2021-11-03 09:40:31 +00:00
|
|
|
showClient();
|
2020-06-25 05:29:53 +00:00
|
|
|
workspace()->raiseClient(this);
|
2021-08-28 08:17:46 +00:00
|
|
|
if (m_plasmaShellSurface && m_plasmaShellSurface->panelBehavior() == PlasmaShellSurfaceInterface::PanelBehavior::AutoHide) {
|
2020-06-25 05:29:53 +00:00
|
|
|
m_plasmaShellSurface->showAutoHidingPanel();
|
2020-11-27 19:57:24 +00:00
|
|
|
}
|
2020-06-25 05:29:53 +00:00
|
|
|
});
|
2015-03-16 08:14:04 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::closeWindow()
|
2015-03-16 08:14:04 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
if (isCloseable()) {
|
|
|
|
sendPing(PingReason::CloseWindow);
|
|
|
|
m_shellSurface->sendClose();
|
2015-06-13 02:06:12 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
2019-12-23 12:04:59 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
XdgSurfaceConfigure *XdgToplevelClient::sendRoleConfigure() const
|
|
|
|
{
|
2021-12-16 19:16:27 +00:00
|
|
|
QSize framePadding(0, 0);
|
|
|
|
if (m_nextDecoration) {
|
|
|
|
framePadding.setWidth(m_nextDecoration->borderLeft() + m_nextDecoration->borderRight());
|
|
|
|
framePadding.setHeight(m_nextDecoration->borderTop() + m_nextDecoration->borderBottom());
|
|
|
|
}
|
|
|
|
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
QSize nextClientSize = moveResizeGeometry().size();
|
|
|
|
if (!nextClientSize.isEmpty()) {
|
2021-12-16 19:16:27 +00:00
|
|
|
nextClientSize.rwidth() -= framePadding.width();
|
|
|
|
nextClientSize.rheight() -= framePadding.height();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nextClientSize.isEmpty()) {
|
|
|
|
QSize bounds = workspace()->clientArea(PlacementArea, this, output()).size();
|
|
|
|
bounds.rwidth() -= framePadding.width();
|
|
|
|
bounds.rheight() -= framePadding.height();
|
|
|
|
m_shellSurface->sendConfigureBounds(bounds);
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
}
|
|
|
|
|
2022-01-04 16:24:32 +00:00
|
|
|
const quint32 serial = m_shellSurface->sendConfigure(nextClientSize, m_nextStates);
|
2020-02-17 18:39:17 +00:00
|
|
|
|
|
|
|
XdgToplevelConfigure *configureEvent = new XdgToplevelConfigure();
|
2022-01-06 10:16:09 +00:00
|
|
|
configureEvent->bounds = moveResizeGeometry();
|
2022-01-04 16:24:32 +00:00
|
|
|
configureEvent->states = m_nextStates;
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
configureEvent->decoration = m_nextDecoration;
|
2020-02-17 18:39:17 +00:00
|
|
|
configureEvent->serial = serial;
|
|
|
|
|
|
|
|
return configureEvent;
|
2015-03-16 08:14:04 +00:00
|
|
|
}
|
|
|
|
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
void XdgToplevelClient::handleRolePrecommit()
|
|
|
|
{
|
|
|
|
auto configureEvent = static_cast<XdgToplevelConfigure *>(lastAcknowledgedConfigure());
|
|
|
|
if (configureEvent && decoration() != configureEvent->decoration) {
|
|
|
|
setDecoration(configureEvent->decoration);
|
|
|
|
updateShadow();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::handleRoleCommit()
|
2017-05-13 15:02:30 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
auto configureEvent = static_cast<XdgToplevelConfigure *>(lastAcknowledgedConfigure());
|
|
|
|
if (configureEvent) {
|
|
|
|
handleStatesAcknowledged(configureEvent->states);
|
|
|
|
}
|
2017-05-13 15:02:30 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::doMinimize()
|
2015-11-06 14:08:13 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
if (isMinimized()) {
|
|
|
|
workspace()->clientHidden(this);
|
|
|
|
} else {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT windowShown(this);
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
workspace()->updateMinimizedOfTransients(this);
|
2015-11-06 14:08:13 +00:00
|
|
|
}
|
|
|
|
|
2021-04-30 18:06:58 +00:00
|
|
|
void XdgToplevelClient::doInteractiveResizeSync()
|
2015-11-06 14:08:13 +00:00
|
|
|
{
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
moveResizeInternal(moveResizeGeometry(), MoveResizeMode::Resize);
|
2015-11-06 14:08:13 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::doSetActive()
|
2015-05-19 10:03:53 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
WaylandClient::doSetActive();
|
|
|
|
|
|
|
|
if (isActive()) {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates |= XdgToplevelInterface::State::Activated;
|
2020-02-17 18:39:17 +00:00
|
|
|
} else {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates &= ~XdgToplevelInterface::State::Activated;
|
2016-06-09 14:54:06 +00:00
|
|
|
}
|
2018-10-05 14:27:05 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
scheduleConfigure();
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::doSetFullScreen()
|
|
|
|
{
|
2021-05-01 15:07:11 +00:00
|
|
|
if (isRequestedFullScreen()) {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates |= XdgToplevelInterface::State::FullScreen;
|
2019-02-26 13:40:54 +00:00
|
|
|
} else {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates &= ~XdgToplevelInterface::State::FullScreen;
|
2019-02-26 13:40:54 +00:00
|
|
|
}
|
2018-10-10 11:03:22 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
scheduleConfigure();
|
|
|
|
}
|
2019-02-26 13:40:54 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::doSetMaximized()
|
|
|
|
{
|
|
|
|
if (requestedMaximizeMode() & MaximizeHorizontal) {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates |= XdgToplevelInterface::State::MaximizedHorizontal;
|
2020-02-17 18:39:17 +00:00
|
|
|
} else {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates &= ~XdgToplevelInterface::State::MaximizedHorizontal;
|
2016-04-18 11:33:23 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
|
|
|
|
if (requestedMaximizeMode() & MaximizeVertical) {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates |= XdgToplevelInterface::State::MaximizedVertical;
|
2020-02-17 18:39:17 +00:00
|
|
|
} else {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates &= ~XdgToplevelInterface::State::MaximizedVertical;
|
XdgV6 - Kwin side
Summary:
Adds XDGV6 support for the kwin side.
Popup placement support is limited to the stuff v5 had,
a simple offset, rather than the awesome new positioner.
But Qt doesn't make use of it yet either.
Also ideally we should do all the positioning before sending the first
configure, but again Qt doesn't actually do anything with that anyway.
Also integrate pinging clients
Test Plan: gtk3-demo works nicely.
Reviewers: #plasma, graesslin, mart
Reviewed By: #plasma, graesslin
Subscribers: mart, graesslin, kwin, plasma-devel, #kwin
Tags: #plasma
Differential Revision: https://phabricator.kde.org/D6591
2017-09-25 15:37:59 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
scheduleConfigure();
|
|
|
|
}
|
|
|
|
|
2020-08-23 11:23:23 +00:00
|
|
|
static Qt::Edges anchorsForQuickTileMode(QuickTileMode mode)
|
|
|
|
{
|
|
|
|
if (mode == QuickTileMode(QuickTileFlag::None)) {
|
|
|
|
return Qt::Edges();
|
|
|
|
}
|
|
|
|
|
|
|
|
Qt::Edges anchors = Qt::LeftEdge | Qt::TopEdge | Qt::RightEdge | Qt::BottomEdge;
|
|
|
|
|
|
|
|
if ((mode & QuickTileFlag::Left) && !(mode & QuickTileFlag::Right)) {
|
|
|
|
anchors &= ~Qt::RightEdge;
|
|
|
|
}
|
|
|
|
if ((mode & QuickTileFlag::Right) && !(mode & QuickTileFlag::Left)) {
|
|
|
|
anchors &= ~Qt::LeftEdge;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((mode & QuickTileFlag::Top) && !(mode & QuickTileFlag::Bottom)) {
|
|
|
|
anchors &= ~Qt::BottomEdge;
|
|
|
|
}
|
|
|
|
if ((mode & QuickTileFlag::Bottom) && !(mode & QuickTileFlag::Top)) {
|
|
|
|
anchors &= ~Qt::TopEdge;
|
|
|
|
}
|
|
|
|
|
|
|
|
return anchors;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::doSetQuickTileMode()
|
|
|
|
{
|
|
|
|
const Qt::Edges anchors = anchorsForQuickTileMode(quickTileMode());
|
|
|
|
|
|
|
|
if (anchors & Qt::LeftEdge) {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates |= XdgToplevelInterface::State::TiledLeft;
|
2020-08-23 11:23:23 +00:00
|
|
|
} else {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates &= ~XdgToplevelInterface::State::TiledLeft;
|
2020-08-23 11:23:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (anchors & Qt::RightEdge) {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates |= XdgToplevelInterface::State::TiledRight;
|
2020-08-23 11:23:23 +00:00
|
|
|
} else {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates &= ~XdgToplevelInterface::State::TiledRight;
|
2020-08-23 11:23:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (anchors & Qt::TopEdge) {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates |= XdgToplevelInterface::State::TiledTop;
|
2020-08-23 11:23:23 +00:00
|
|
|
} else {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates &= ~XdgToplevelInterface::State::TiledTop;
|
2020-08-23 11:23:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (anchors & Qt::BottomEdge) {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates |= XdgToplevelInterface::State::TiledBottom;
|
2020-08-23 11:23:23 +00:00
|
|
|
} else {
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates &= ~XdgToplevelInterface::State::TiledBottom;
|
2020-08-23 11:23:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
scheduleConfigure();
|
|
|
|
}
|
|
|
|
|
2021-04-30 18:06:58 +00:00
|
|
|
bool XdgToplevelClient::doStartInteractiveMoveResize()
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
2021-12-24 12:05:41 +00:00
|
|
|
if (interactiveMoveResizeGravity() != Gravity::None) {
|
2022-01-06 10:16:09 +00:00
|
|
|
m_nextGravity = interactiveMoveResizeGravity();
|
2022-01-04 16:24:32 +00:00
|
|
|
m_nextStates |= XdgToplevelInterface::State::Resizing;
|
2022-01-04 16:27:36 +00:00
|
|
|
scheduleConfigure();
|
2019-02-26 13:40:54 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
return true;
|
2015-05-19 10:03:53 +00:00
|
|
|
}
|
|
|
|
|
2021-04-30 18:06:58 +00:00
|
|
|
void XdgToplevelClient::doFinishInteractiveMoveResize()
|
2018-10-05 14:27:05 +00:00
|
|
|
{
|
2022-01-04 16:27:36 +00:00
|
|
|
if (m_nextStates & XdgToplevelInterface::State::Resizing) {
|
|
|
|
m_nextStates &= ~XdgToplevelInterface::State::Resizing;
|
|
|
|
scheduleConfigure();
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
2018-10-05 14:27:05 +00:00
|
|
|
|
2020-07-22 11:00:11 +00:00
|
|
|
bool XdgToplevelClient::takeFocus()
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
|
|
|
if (wantsInput()) {
|
|
|
|
sendPing(PingReason::FocusWindow);
|
|
|
|
setActive(true);
|
2020-01-23 21:40:34 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
if (!keepAbove() && !isOnScreenDisplay() && !belongsToDesktop()) {
|
|
|
|
workspace()->setShowingDesktop(false);
|
2020-01-23 21:40:34 +00:00
|
|
|
}
|
2020-07-22 11:00:11 +00:00
|
|
|
return true;
|
2018-10-05 14:27:05 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgToplevelClient::wantsInput() const
|
2019-09-15 10:54:53 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
return rules()->checkAcceptFocus(acceptsFocus());
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgToplevelClient::dockWantsInput() const
|
2019-09-15 10:54:53 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
if (m_plasmaShellSurface) {
|
|
|
|
if (m_plasmaShellSurface->role() == PlasmaShellSurfaceInterface::Role::Panel) {
|
|
|
|
return m_plasmaShellSurface->panelTakesFocus();
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XdgToplevelClient::acceptsFocus() const
|
|
|
|
{
|
|
|
|
if (m_plasmaShellSurface) {
|
|
|
|
if (m_plasmaShellSurface->role() == PlasmaShellSurfaceInterface::Role::OnScreenDisplay ||
|
|
|
|
m_plasmaShellSurface->role() == PlasmaShellSurfaceInterface::Role::ToolTip) {
|
|
|
|
return false;
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
if (m_plasmaShellSurface->role() == PlasmaShellSurfaceInterface::Role::Notification ||
|
|
|
|
m_plasmaShellSurface->role() == PlasmaShellSurfaceInterface::Role::CriticalNotification) {
|
|
|
|
return m_plasmaShellSurface->panelTakesFocus();
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-16 07:17:19 +00:00
|
|
|
return !isZombie() && readyForPainting();
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
Layer XdgToplevelClient::layerForDock() const
|
2019-09-15 10:54:53 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
if (m_plasmaShellSurface) {
|
|
|
|
switch (m_plasmaShellSurface->panelBehavior()) {
|
|
|
|
case PlasmaShellSurfaceInterface::PanelBehavior::WindowsCanCover:
|
|
|
|
return NormalLayer;
|
|
|
|
case PlasmaShellSurfaceInterface::PanelBehavior::AutoHide:
|
|
|
|
case PlasmaShellSurfaceInterface::PanelBehavior::WindowsGoBelow:
|
2020-06-02 13:51:05 +00:00
|
|
|
return AboveLayer;
|
2020-02-17 18:39:17 +00:00
|
|
|
case PlasmaShellSurfaceInterface::PanelBehavior::AlwaysVisible:
|
|
|
|
return DockLayer;
|
|
|
|
default:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
break;
|
|
|
|
}
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
return AbstractClient::layerForDock();
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::handleWindowTitleChanged()
|
2019-10-03 19:43:28 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
setCaption(m_shellSurface->windowTitle());
|
2019-10-03 19:43:28 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::handleWindowClassChanged()
|
2019-09-15 10:54:53 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
const QByteArray applicationId = m_shellSurface->windowClass().toUtf8();
|
|
|
|
setResourceClass(resourceName(), applicationId);
|
2021-11-17 16:36:00 +00:00
|
|
|
if (shellSurface()->isConfigured()) {
|
2020-02-17 18:39:17 +00:00
|
|
|
evaluateWindowRules();
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
setDesktopFileName(applicationId);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::handleWindowMenuRequested(SeatInterface *seat, const QPoint &surfacePos,
|
|
|
|
quint32 serial)
|
|
|
|
{
|
|
|
|
Q_UNUSED(seat)
|
|
|
|
Q_UNUSED(serial)
|
|
|
|
performMouseCommand(Options::MouseOperationsMenu, pos() + surfacePos);
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::handleMoveRequested(SeatInterface *seat, quint32 serial)
|
2019-09-15 10:54:53 +00:00
|
|
|
{
|
2020-12-06 17:04:40 +00:00
|
|
|
if (!seat->hasImplicitPointerGrab(serial) && !seat->hasImplicitTouchGrab(serial)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-12-06 17:09:15 +00:00
|
|
|
if (isMovable()) {
|
2021-06-30 23:34:54 +00:00
|
|
|
QPoint cursorPos;
|
|
|
|
if (seat->hasImplicitPointerGrab(serial)) {
|
|
|
|
cursorPos = Cursors::self()->mouse()->pos();
|
|
|
|
} else {
|
|
|
|
cursorPos = input()->touch()->position().toPoint();
|
|
|
|
}
|
|
|
|
performMouseCommand(Options::MouseMove, cursorPos);
|
2020-12-06 17:09:15 +00:00
|
|
|
} else {
|
|
|
|
qCDebug(KWIN_CORE) << this << "is immovable, ignoring the move request";
|
|
|
|
}
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
|
|
|
|
2022-01-12 12:19:14 +00:00
|
|
|
void XdgToplevelClient::handleResizeRequested(SeatInterface *seat, XdgToplevelInterface::ResizeAnchor anchor, quint32 serial)
|
2019-09-15 10:54:53 +00:00
|
|
|
{
|
2020-12-06 17:04:40 +00:00
|
|
|
if (!seat->hasImplicitPointerGrab(serial) && !seat->hasImplicitTouchGrab(serial)) {
|
|
|
|
return;
|
|
|
|
}
|
2019-09-15 10:54:53 +00:00
|
|
|
if (!isResizable() || isShade()) {
|
|
|
|
return;
|
|
|
|
}
|
2021-04-30 18:06:58 +00:00
|
|
|
if (isInteractiveMoveResize()) {
|
|
|
|
finishInteractiveMoveResize(false);
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
2021-04-30 18:06:58 +00:00
|
|
|
setInteractiveMoveResizePointerButtonDown(true);
|
2021-06-30 23:34:54 +00:00
|
|
|
QPoint cursorPos;
|
|
|
|
if (seat->hasImplicitPointerGrab(serial)) {
|
|
|
|
cursorPos = Cursors::self()->mouse()->pos();
|
|
|
|
} else {
|
|
|
|
cursorPos = input()->touch()->position().toPoint();
|
|
|
|
}
|
|
|
|
setInteractiveMoveOffset(cursorPos - pos()); // map from global
|
2021-04-30 18:06:58 +00:00
|
|
|
setInvertedInteractiveMoveOffset(rect().bottomRight() - interactiveMoveOffset());
|
|
|
|
setUnrestrictedInteractiveMoveResize(false);
|
2021-12-24 12:05:41 +00:00
|
|
|
Gravity gravity;
|
2022-01-12 12:19:14 +00:00
|
|
|
switch (anchor) {
|
|
|
|
case XdgToplevelInterface::ResizeAnchor::TopLeft:
|
2021-12-24 12:05:41 +00:00
|
|
|
gravity = Gravity::TopLeft;
|
2022-01-12 12:19:14 +00:00
|
|
|
break;
|
|
|
|
case XdgToplevelInterface::ResizeAnchor::Top:
|
2021-12-24 12:05:41 +00:00
|
|
|
gravity = Gravity::Top;
|
2022-01-12 12:19:14 +00:00
|
|
|
break;
|
|
|
|
case XdgToplevelInterface::ResizeAnchor::TopRight:
|
2021-12-24 12:05:41 +00:00
|
|
|
gravity = Gravity::TopRight;
|
2022-01-12 12:19:14 +00:00
|
|
|
break;
|
|
|
|
case XdgToplevelInterface::ResizeAnchor::Right:
|
2021-12-24 12:05:41 +00:00
|
|
|
gravity = Gravity::Right;
|
2022-01-12 12:19:14 +00:00
|
|
|
break;
|
|
|
|
case XdgToplevelInterface::ResizeAnchor::BottomRight:
|
2021-12-24 12:05:41 +00:00
|
|
|
gravity = Gravity::BottomRight;
|
2022-01-12 12:19:14 +00:00
|
|
|
break;
|
|
|
|
case XdgToplevelInterface::ResizeAnchor::Bottom:
|
2021-12-24 12:05:41 +00:00
|
|
|
gravity = Gravity::Bottom;
|
2022-01-12 12:19:14 +00:00
|
|
|
break;
|
|
|
|
case XdgToplevelInterface::ResizeAnchor::BottomLeft:
|
2021-12-24 12:05:41 +00:00
|
|
|
gravity = Gravity::BottomLeft;
|
2022-01-12 12:19:14 +00:00
|
|
|
break;
|
|
|
|
case XdgToplevelInterface::ResizeAnchor::Left:
|
2021-12-24 12:05:41 +00:00
|
|
|
gravity = Gravity::Left;
|
2022-01-12 12:19:14 +00:00
|
|
|
break;
|
|
|
|
default:
|
2021-12-24 12:05:41 +00:00
|
|
|
gravity = Gravity::None;
|
2022-01-12 12:19:14 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-12-24 12:05:41 +00:00
|
|
|
setInteractiveMoveResizeGravity(gravity);
|
2021-04-30 18:06:58 +00:00
|
|
|
if (!startInteractiveMoveResize()) {
|
|
|
|
setInteractiveMoveResizePointerButtonDown(false);
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
|
|
|
updateCursor();
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::handleStatesAcknowledged(const XdgToplevelInterface::States &states)
|
|
|
|
{
|
|
|
|
const XdgToplevelInterface::States delta = m_acknowledgedStates ^ states;
|
|
|
|
|
|
|
|
if (delta & XdgToplevelInterface::State::Maximized) {
|
|
|
|
MaximizeMode maximizeMode = MaximizeRestore;
|
|
|
|
if (states & XdgToplevelInterface::State::MaximizedHorizontal) {
|
|
|
|
maximizeMode = MaximizeMode(maximizeMode | MaximizeHorizontal);
|
|
|
|
}
|
|
|
|
if (states & XdgToplevelInterface::State::MaximizedVertical) {
|
|
|
|
maximizeMode = MaximizeMode(maximizeMode | MaximizeVertical);
|
|
|
|
}
|
|
|
|
updateMaximizeMode(maximizeMode);
|
|
|
|
}
|
|
|
|
if (delta & XdgToplevelInterface::State::FullScreen) {
|
|
|
|
updateFullScreenMode(states & XdgToplevelInterface::State::FullScreen);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_acknowledgedStates = states;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::handleMaximizeRequested()
|
|
|
|
{
|
2020-06-26 09:14:38 +00:00
|
|
|
if (m_isInitialized) {
|
|
|
|
maximize(MaximizeFull);
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
scheduleConfigure();
|
2020-06-26 09:14:38 +00:00
|
|
|
} else {
|
|
|
|
m_initialStates |= XdgToplevelInterface::State::Maximized;
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::handleUnmaximizeRequested()
|
|
|
|
{
|
2020-06-26 09:14:38 +00:00
|
|
|
if (m_isInitialized) {
|
|
|
|
maximize(MaximizeRestore);
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
scheduleConfigure();
|
2020-06-26 09:14:38 +00:00
|
|
|
} else {
|
|
|
|
m_initialStates &= ~XdgToplevelInterface::State::Maximized;
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::handleFullscreenRequested(OutputInterface *output)
|
|
|
|
{
|
2020-11-23 18:24:37 +00:00
|
|
|
m_fullScreenRequestedOutput = waylandServer()->findOutput(output);
|
|
|
|
|
2020-06-26 09:14:38 +00:00
|
|
|
if (m_isInitialized) {
|
|
|
|
setFullScreen(/* set */ true, /* user */ false);
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
scheduleConfigure();
|
2020-06-26 09:14:38 +00:00
|
|
|
} else {
|
|
|
|
m_initialStates |= XdgToplevelInterface::State::FullScreen;
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::handleUnfullscreenRequested()
|
|
|
|
{
|
2020-11-23 18:24:37 +00:00
|
|
|
m_fullScreenRequestedOutput.clear();
|
2020-06-26 09:14:38 +00:00
|
|
|
if (m_isInitialized) {
|
|
|
|
setFullScreen(/* set */ false, /* user */ false);
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
scheduleConfigure();
|
2020-06-26 09:14:38 +00:00
|
|
|
} else {
|
|
|
|
m_initialStates &= ~XdgToplevelInterface::State::FullScreen;
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::handleMinimizeRequested()
|
2019-09-15 10:54:53 +00:00
|
|
|
{
|
2020-04-02 16:18:01 +00:00
|
|
|
performMouseCommand(Options::MouseMinimize, Cursors::self()->mouse()->pos());
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::handleTransientForChanged()
|
|
|
|
{
|
|
|
|
SurfaceInterface *transientForSurface = nullptr;
|
|
|
|
if (XdgToplevelInterface *parentToplevel = m_shellSurface->parentXdgToplevel()) {
|
|
|
|
transientForSurface = parentToplevel->surface();
|
|
|
|
}
|
|
|
|
if (!transientForSurface) {
|
|
|
|
transientForSurface = waylandServer()->findForeignTransientForSurface(surface());
|
|
|
|
}
|
|
|
|
AbstractClient *transientForClient = waylandServer()->findClient(transientForSurface);
|
|
|
|
if (transientForClient != transientFor()) {
|
|
|
|
if (transientFor()) {
|
|
|
|
transientFor()->removeTransient(this);
|
|
|
|
}
|
|
|
|
if (transientForClient) {
|
|
|
|
transientForClient->addTransient(this);
|
|
|
|
}
|
|
|
|
setTransientFor(transientForClient);
|
|
|
|
}
|
|
|
|
m_isTransient = transientForClient;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::handleForeignTransientForChanged(SurfaceInterface *child)
|
|
|
|
{
|
|
|
|
if (surface() == child) {
|
|
|
|
handleTransientForChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::handlePingTimeout(quint32 serial)
|
|
|
|
{
|
|
|
|
auto pingIt = m_pings.find(serial);
|
|
|
|
if (pingIt == m_pings.end()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (pingIt.value() == PingReason::CloseWindow) {
|
|
|
|
qCDebug(KWIN_CORE) << "Final ping timeout on a close attempt, asking to kill:" << caption();
|
|
|
|
|
|
|
|
//for internal windows, killing the window will delete this
|
|
|
|
QPointer<QObject> guard(this);
|
|
|
|
killWindow();
|
|
|
|
if (!guard) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_pings.erase(pingIt);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::handlePingDelayed(quint32 serial)
|
|
|
|
{
|
|
|
|
auto it = m_pings.find(serial);
|
|
|
|
if (it != m_pings.end()) {
|
|
|
|
qCDebug(KWIN_CORE) << "First ping timeout:" << caption();
|
|
|
|
setUnresponsive(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::handlePongReceived(quint32 serial)
|
|
|
|
{
|
2021-01-26 15:22:37 +00:00
|
|
|
m_pings.remove(serial);
|
|
|
|
setUnresponsive(false);
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
|
2021-07-07 11:50:02 +00:00
|
|
|
void XdgToplevelClient::handleMaximumSizeChanged()
|
|
|
|
{
|
|
|
|
Q_EMIT maximizeableChanged(isMaximizable());
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::handleMinimumSizeChanged()
|
|
|
|
{
|
|
|
|
Q_EMIT maximizeableChanged(isMaximizable());
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::sendPing(PingReason reason)
|
|
|
|
{
|
|
|
|
XdgShellInterface *shell = m_shellSurface->shell();
|
|
|
|
XdgSurfaceInterface *surface = m_shellSurface->xdgSurface();
|
|
|
|
|
|
|
|
const quint32 serial = shell->ping(surface);
|
|
|
|
m_pings.insert(serial, reason);
|
|
|
|
}
|
|
|
|
|
2020-06-26 09:14:38 +00:00
|
|
|
MaximizeMode XdgToplevelClient::initialMaximizeMode() const
|
|
|
|
{
|
|
|
|
MaximizeMode maximizeMode = MaximizeRestore;
|
|
|
|
if (m_initialStates & XdgToplevelInterface::State::MaximizedHorizontal) {
|
|
|
|
maximizeMode = MaximizeMode(maximizeMode | MaximizeHorizontal);
|
|
|
|
}
|
|
|
|
if (m_initialStates & XdgToplevelInterface::State::MaximizedVertical) {
|
|
|
|
maximizeMode = MaximizeMode(maximizeMode | MaximizeVertical);
|
|
|
|
}
|
|
|
|
return maximizeMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XdgToplevelClient::initialFullScreenMode() const
|
|
|
|
{
|
|
|
|
return m_initialStates & XdgToplevelInterface::State::FullScreen;
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::initialize()
|
2019-09-15 10:54:53 +00:00
|
|
|
{
|
2020-11-27 08:41:13 +00:00
|
|
|
bool needsPlacement = isPlaceable();
|
2021-11-17 16:36:00 +00:00
|
|
|
setupWindowRules(false);
|
|
|
|
|
2021-12-14 09:06:39 +00:00
|
|
|
// Move or resize the window only if enforced by a window rule.
|
|
|
|
const QPoint forcedPosition = rules()->checkPosition(invalidPoint, true);
|
|
|
|
if (forcedPosition != invalidPoint) {
|
|
|
|
move(forcedPosition);
|
|
|
|
}
|
|
|
|
const QSize forcedSize = rules()->checkSize(QSize(), true);
|
|
|
|
if (forcedSize.isValid()) {
|
|
|
|
resize(forcedSize);
|
|
|
|
}
|
|
|
|
|
2021-11-17 16:36:00 +00:00
|
|
|
maximize(rules()->checkMaximize(initialMaximizeMode(), true));
|
|
|
|
setFullScreen(rules()->checkFullScreen(initialFullScreenMode(), true), false);
|
|
|
|
setOnActivities(rules()->checkActivity(activities(), true));
|
|
|
|
setDesktops(rules()->checkDesktops(desktops(), true));
|
|
|
|
setDesktopFileName(rules()->checkDesktopFile(desktopFileName(), true).toUtf8());
|
|
|
|
if (rules()->checkMinimize(isMinimized(), true)) {
|
|
|
|
minimize(true); // No animation.
|
|
|
|
}
|
|
|
|
setSkipTaskbar(rules()->checkSkipTaskbar(skipTaskbar(), true));
|
|
|
|
setSkipPager(rules()->checkSkipPager(skipPager(), true));
|
|
|
|
setSkipSwitcher(rules()->checkSkipSwitcher(skipSwitcher(), true));
|
|
|
|
setKeepAbove(rules()->checkKeepAbove(keepAbove(), true));
|
|
|
|
setKeepBelow(rules()->checkKeepBelow(keepBelow(), true));
|
|
|
|
setShortcut(rules()->checkShortcut(shortcut().toString(), true));
|
|
|
|
setNoBorder(rules()->checkNoBorder(noBorder(), true));
|
|
|
|
|
|
|
|
// Don't place the client if its position is set by a rule.
|
|
|
|
if (rules()->checkPosition(invalidPoint, true) != invalidPoint) {
|
|
|
|
needsPlacement = false;
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
|
2021-11-17 16:36:00 +00:00
|
|
|
// Don't place the client if the maximize state is set by a rule.
|
|
|
|
if (requestedMaximizeMode() != MaximizeRestore) {
|
|
|
|
needsPlacement = false;
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
2021-11-17 16:36:00 +00:00
|
|
|
|
|
|
|
discardTemporaryRules();
|
|
|
|
RuleBook::self()->discardUsed(this, false); // Remove Apply Now rules.
|
|
|
|
updateWindowRules(Rules::All);
|
|
|
|
|
2021-05-01 15:07:11 +00:00
|
|
|
if (isRequestedFullScreen()) {
|
2020-02-17 18:39:17 +00:00
|
|
|
needsPlacement = false;
|
|
|
|
}
|
|
|
|
if (needsPlacement) {
|
2021-08-28 18:58:29 +00:00
|
|
|
const QRect area = workspace()->clientArea(PlacementArea, this, workspace()->activeOutput());
|
2021-11-03 09:35:00 +00:00
|
|
|
Placement::self()->place(this, area);
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
2019-09-15 10:54:53 +00:00
|
|
|
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
configureDecoration();
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
scheduleConfigure();
|
2020-02-17 18:39:17 +00:00
|
|
|
updateColorScheme();
|
2021-09-01 09:43:10 +00:00
|
|
|
setupWindowManagementInterface();
|
2021-08-29 09:26:06 +00:00
|
|
|
|
2020-06-26 09:14:38 +00:00
|
|
|
m_isInitialized = true;
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::updateMaximizeMode(MaximizeMode maximizeMode)
|
2015-05-19 10:03:53 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
if (m_maximizeMode == maximizeMode) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_maximizeMode = maximizeMode;
|
|
|
|
updateWindowRules(Rules::MaximizeVert | Rules::MaximizeHoriz);
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT clientMaximizedStateChanged(this, maximizeMode);
|
|
|
|
Q_EMIT clientMaximizedStateChanged(this, maximizeMode & MaximizeHorizontal, maximizeMode & MaximizeVertical);
|
2015-05-19 10:03:53 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::updateFullScreenMode(bool set)
|
2019-09-15 10:54:53 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
if (m_isFullScreen == set) {
|
|
|
|
return;
|
|
|
|
}
|
2021-05-01 15:07:11 +00:00
|
|
|
StackingUpdatesBlocker blocker1(workspace());
|
2020-02-17 18:39:17 +00:00
|
|
|
m_isFullScreen = set;
|
2021-05-01 15:07:11 +00:00
|
|
|
updateLayer();
|
2020-02-17 18:39:17 +00:00
|
|
|
updateWindowRules(Rules::Fullscreen);
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT fullScreenChanged();
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
|
|
|
|
2020-08-19 09:21:00 +00:00
|
|
|
QString XdgToplevelClient::preferredColorScheme() const
|
2019-09-15 10:54:53 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
if (m_paletteInterface) {
|
2020-08-19 09:21:00 +00:00
|
|
|
return rules()->checkDecoColor(m_paletteInterface->palette());
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
2020-08-19 09:21:00 +00:00
|
|
|
return rules()->checkDecoColor(QString());
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::installAppMenu(AppMenuInterface *appMenu)
|
2019-09-15 10:54:53 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
m_appMenuInterface = appMenu;
|
|
|
|
|
|
|
|
auto updateMenu = [this](const AppMenuInterface::InterfaceAddress &address) {
|
|
|
|
updateApplicationMenuServiceName(address.serviceName);
|
|
|
|
updateApplicationMenuObjectPath(address.objectPath);
|
|
|
|
};
|
|
|
|
connect(m_appMenuInterface, &AppMenuInterface::addressChanged, this, updateMenu);
|
|
|
|
updateMenu(appMenu->address());
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
|
|
|
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
XdgToplevelClient::DecorationMode XdgToplevelClient::preferredDecorationMode() const
|
2019-09-15 10:54:53 +00:00
|
|
|
{
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
if (!Decoration::DecorationBridge::hasPlugin()) {
|
|
|
|
return DecorationMode::Client;
|
|
|
|
} else if (m_userNoBorder || isRequestedFullScreen()) {
|
|
|
|
return DecorationMode::None;
|
|
|
|
}
|
2019-09-15 10:54:53 +00:00
|
|
|
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
if (m_xdgDecoration) {
|
|
|
|
switch (m_xdgDecoration->preferredMode()) {
|
|
|
|
case XdgToplevelDecorationV1Interface::Mode::Undefined:
|
|
|
|
return DecorationMode::Server;
|
|
|
|
case XdgToplevelDecorationV1Interface::Mode::None:
|
|
|
|
return DecorationMode::None;
|
|
|
|
case XdgToplevelDecorationV1Interface::Mode::Client:
|
|
|
|
return DecorationMode::Client;
|
|
|
|
case XdgToplevelDecorationV1Interface::Mode::Server:
|
|
|
|
return DecorationMode::Server;
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_serverDecoration) {
|
|
|
|
switch (m_serverDecoration->preferredMode()) {
|
|
|
|
case ServerSideDecorationManagerInterface::Mode::None:
|
|
|
|
return DecorationMode::None;
|
|
|
|
case ServerSideDecorationManagerInterface::Mode::Client:
|
|
|
|
return DecorationMode::Client;
|
|
|
|
case ServerSideDecorationManagerInterface::Mode::Server:
|
|
|
|
return DecorationMode::Server;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return DecorationMode::Client;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::clearDecoration()
|
|
|
|
{
|
|
|
|
m_nextDecoration = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::configureDecoration()
|
|
|
|
{
|
|
|
|
const DecorationMode decorationMode = preferredDecorationMode();
|
|
|
|
switch (decorationMode) {
|
|
|
|
case DecorationMode::None:
|
|
|
|
case DecorationMode::Client:
|
|
|
|
clearDecoration();
|
|
|
|
break;
|
|
|
|
case DecorationMode::Server:
|
|
|
|
if (!m_nextDecoration) {
|
|
|
|
m_nextDecoration.reset(Decoration::DecorationBridge::self()->createDecoration(this));
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// All decoration updates are synchronized to toplevel configure events.
|
|
|
|
if (m_xdgDecoration) {
|
|
|
|
configureXdgDecoration(decorationMode);
|
|
|
|
} else if (m_serverDecoration) {
|
|
|
|
configureServerDecoration(decorationMode);
|
2019-09-15 10:54:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
void XdgToplevelClient::configureXdgDecoration(DecorationMode decorationMode)
|
|
|
|
{
|
|
|
|
switch (decorationMode) {
|
|
|
|
case DecorationMode::None: // Faked as server side mode under the hood.
|
|
|
|
m_xdgDecoration->sendConfigure(XdgToplevelDecorationV1Interface::Mode::None);
|
|
|
|
break;
|
|
|
|
case DecorationMode::Client:
|
|
|
|
m_xdgDecoration->sendConfigure(XdgToplevelDecorationV1Interface::Mode::Client);
|
|
|
|
break;
|
|
|
|
case DecorationMode::Server:
|
|
|
|
m_xdgDecoration->sendConfigure(XdgToplevelDecorationV1Interface::Mode::Server);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
scheduleConfigure();
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::configureServerDecoration(DecorationMode decorationMode)
|
|
|
|
{
|
|
|
|
switch (decorationMode) {
|
|
|
|
case DecorationMode::None:
|
|
|
|
m_serverDecoration->setMode(ServerSideDecorationManagerInterface::Mode::None);
|
|
|
|
break;
|
|
|
|
case DecorationMode::Client:
|
|
|
|
m_serverDecoration->setMode(ServerSideDecorationManagerInterface::Mode::Client);
|
|
|
|
break;
|
|
|
|
case DecorationMode::Server:
|
|
|
|
m_serverDecoration->setMode(ServerSideDecorationManagerInterface::Mode::Server);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
scheduleConfigure();
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::installXdgDecoration(XdgToplevelDecorationV1Interface *decoration)
|
2019-09-26 15:02:09 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
m_xdgDecoration = decoration;
|
2019-09-26 15:02:09 +00:00
|
|
|
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
connect(m_xdgDecoration, &XdgToplevelDecorationV1Interface::destroyed,
|
|
|
|
this, &XdgToplevelClient::clearDecoration);
|
2020-02-17 18:39:17 +00:00
|
|
|
connect(m_xdgDecoration, &XdgToplevelDecorationV1Interface::preferredModeChanged, this, [this] {
|
2020-06-26 09:29:08 +00:00
|
|
|
if (m_isInitialized) {
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
configureDecoration();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelClient::installServerDecoration(ServerSideDecorationInterface *decoration)
|
|
|
|
{
|
|
|
|
m_serverDecoration = decoration;
|
|
|
|
if (m_isInitialized) {
|
|
|
|
configureDecoration();
|
|
|
|
}
|
|
|
|
|
|
|
|
connect(m_serverDecoration, &ServerSideDecorationInterface::destroyed,
|
|
|
|
this, &XdgToplevelClient::clearDecoration);
|
|
|
|
connect(m_serverDecoration, &ServerSideDecorationInterface::preferredModeChanged, this, [this]() {
|
|
|
|
if (m_isInitialized) {
|
|
|
|
configureDecoration();
|
2020-06-26 09:29:08 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
});
|
2019-09-26 15:02:09 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::installPalette(ServerSideDecorationPaletteInterface *palette)
|
2015-05-27 09:48:33 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
m_paletteInterface = palette;
|
2015-05-27 09:48:33 +00:00
|
|
|
|
2020-08-19 09:21:00 +00:00
|
|
|
connect(m_paletteInterface, &ServerSideDecorationPaletteInterface::paletteChanged,
|
|
|
|
this, &XdgToplevelClient::updateColorScheme);
|
|
|
|
connect(m_paletteInterface, &QObject::destroyed,
|
|
|
|
this, &XdgToplevelClient::updateColorScheme);
|
|
|
|
updateColorScheme();
|
2015-06-03 19:19:00 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgToplevelClient::setFullScreen(bool set, bool user)
|
|
|
|
{
|
|
|
|
set = rules()->checkFullScreen(set);
|
2022-02-03 08:22:51 +00:00
|
|
|
if (m_isRequestedFullScreen == set) {
|
2020-02-17 18:39:17 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (isSpecialWindow()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (user && !userCanSetFullScreen()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-01 15:07:11 +00:00
|
|
|
m_isRequestedFullScreen = set;
|
wayland: Properly handle async xdg-decoration updates
Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.
The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.
At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.
If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.
If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.
Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.
BUG: 445259
2021-01-27 15:35:13 +00:00
|
|
|
configureDecoration();
|
2020-02-17 18:39:17 +00:00
|
|
|
|
|
|
|
if (set) {
|
2021-08-27 15:49:54 +00:00
|
|
|
const AbstractOutput *output = m_fullScreenRequestedOutput ? m_fullScreenRequestedOutput.data() : kwinApp()->platform()->outputAt(moveResizeGeometry().center());
|
2022-02-03 08:22:51 +00:00
|
|
|
setFullscreenGeometryRestore(moveResizeGeometry());
|
2021-08-27 15:49:54 +00:00
|
|
|
moveResize(workspace()->clientArea(FullScreenArea, this, output));
|
2020-02-17 18:39:17 +00:00
|
|
|
} else {
|
2020-11-23 18:24:37 +00:00
|
|
|
m_fullScreenRequestedOutput.clear();
|
2021-01-20 19:36:07 +00:00
|
|
|
if (fullscreenGeometryRestore().isValid()) {
|
2021-08-28 14:51:00 +00:00
|
|
|
AbstractOutput *currentOutput = output();
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
moveResize(QRect(fullscreenGeometryRestore().topLeft(),
|
|
|
|
constrainFrameSize(fullscreenGeometryRestore().size())));
|
2021-08-28 14:51:00 +00:00
|
|
|
if (currentOutput != output()) {
|
|
|
|
workspace()->sendClientToOutput(this, currentOutput);
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
} else {
|
|
|
|
// this can happen when the window was first shown already fullscreen,
|
|
|
|
// so let the client set the size by itself
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
moveResize(QRect(workspace()->clientArea(PlacementArea, this).topLeft(), QSize(0, 0)));
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
doSetFullScreen();
|
2017-12-22 14:22:24 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
/**
|
|
|
|
* \todo Move to AbstractClient.
|
|
|
|
*/
|
|
|
|
static bool changeMaximizeRecursion = false;
|
|
|
|
void XdgToplevelClient::changeMaximize(bool horizontal, bool vertical, bool adjust)
|
2018-01-04 18:32:18 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
if (changeMaximizeRecursion) {
|
|
|
|
return;
|
|
|
|
}
|
2018-01-04 18:32:18 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
if (!isResizable()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QRect clientArea = isElectricBorderMaximizing() ?
|
2021-08-17 11:55:34 +00:00
|
|
|
workspace()->clientArea(MaximizeArea, this, Cursors::self()->mouse()->pos()) :
|
|
|
|
workspace()->clientArea(MaximizeArea, this, moveResizeGeometry().center());
|
2020-02-17 18:39:17 +00:00
|
|
|
|
|
|
|
const MaximizeMode oldMode = m_requestedMaximizeMode;
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
const QRect oldGeometry = moveResizeGeometry();
|
2020-02-17 18:39:17 +00:00
|
|
|
|
|
|
|
// 'adjust == true' means to update the size only, e.g. after changing workspace size
|
|
|
|
if (!adjust) {
|
|
|
|
if (vertical)
|
|
|
|
m_requestedMaximizeMode = MaximizeMode(m_requestedMaximizeMode ^ MaximizeVertical);
|
|
|
|
if (horizontal)
|
|
|
|
m_requestedMaximizeMode = MaximizeMode(m_requestedMaximizeMode ^ MaximizeHorizontal);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_requestedMaximizeMode = rules()->checkMaximize(m_requestedMaximizeMode);
|
|
|
|
if (!adjust && m_requestedMaximizeMode == oldMode) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// call into decoration update borders
|
2022-02-14 10:06:35 +00:00
|
|
|
if (m_nextDecoration && !(options->borderlessMaximizedWindows() && m_requestedMaximizeMode == KWin::MaximizeFull)) {
|
2020-02-17 18:39:17 +00:00
|
|
|
changeMaximizeRecursion = true;
|
2022-02-14 10:06:35 +00:00
|
|
|
const auto c = m_nextDecoration->client().toStrongRef();
|
2020-02-17 18:39:17 +00:00
|
|
|
if ((m_requestedMaximizeMode & MaximizeVertical) != (oldMode & MaximizeVertical)) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT c->maximizedVerticallyChanged(m_requestedMaximizeMode & MaximizeVertical);
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
if ((m_requestedMaximizeMode & MaximizeHorizontal) != (oldMode & MaximizeHorizontal)) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT c->maximizedHorizontallyChanged(m_requestedMaximizeMode & MaximizeHorizontal);
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
if ((m_requestedMaximizeMode == MaximizeFull) != (oldMode == MaximizeFull)) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT c->maximizedChanged(m_requestedMaximizeMode == MaximizeFull);
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
changeMaximizeRecursion = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options->borderlessMaximizedWindows()) {
|
2022-01-22 10:48:13 +00:00
|
|
|
setNoBorder(m_requestedMaximizeMode == MaximizeFull);
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
|
2020-09-10 10:59:56 +00:00
|
|
|
if (quickTileMode() == QuickTileMode(QuickTileFlag::None)) {
|
|
|
|
QRect savedGeometry = geometryRestore();
|
|
|
|
if (!adjust && !(oldMode & MaximizeVertical)) {
|
|
|
|
savedGeometry.setTop(oldGeometry.top());
|
|
|
|
savedGeometry.setBottom(oldGeometry.bottom());
|
|
|
|
}
|
|
|
|
if (!adjust && !(oldMode & MaximizeHorizontal)) {
|
|
|
|
savedGeometry.setLeft(oldGeometry.left());
|
|
|
|
savedGeometry.setRight(oldGeometry.right());
|
|
|
|
}
|
|
|
|
setGeometryRestore(savedGeometry);
|
|
|
|
}
|
|
|
|
|
|
|
|
const MaximizeMode delta = m_requestedMaximizeMode ^ oldMode;
|
|
|
|
QRect geometry = oldGeometry;
|
|
|
|
|
|
|
|
if (adjust || (delta & MaximizeHorizontal)) {
|
|
|
|
if (m_requestedMaximizeMode & MaximizeHorizontal) {
|
|
|
|
// Stretch the window vertically to fit the size of the maximize area.
|
|
|
|
geometry.setX(clientArea.x());
|
|
|
|
geometry.setWidth(clientArea.width());
|
|
|
|
} else if (geometryRestore().isValid()) {
|
|
|
|
// The window is no longer maximized horizontally and the saved geometry is valid.
|
|
|
|
geometry.setX(geometryRestore().x());
|
|
|
|
geometry.setWidth(geometryRestore().width());
|
|
|
|
} else {
|
|
|
|
// The window is no longer maximized horizontally and the saved geometry is
|
|
|
|
// invalid. This would happen if the window had been mapped in the maximized state.
|
|
|
|
// We ask the client to resize the window horizontally to its preferred size.
|
|
|
|
geometry.setX(clientArea.x());
|
|
|
|
geometry.setWidth(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (adjust || (delta & MaximizeVertical)) {
|
|
|
|
if (m_requestedMaximizeMode & MaximizeVertical) {
|
|
|
|
// Stretch the window horizontally to fit the size of the maximize area.
|
|
|
|
geometry.setY(clientArea.y());
|
|
|
|
geometry.setHeight(clientArea.height());
|
|
|
|
} else if (geometryRestore().isValid()) {
|
|
|
|
// The window is no longer maximized vertically and the saved geometry is valid.
|
|
|
|
geometry.setY(geometryRestore().y());
|
|
|
|
geometry.setHeight(geometryRestore().height());
|
|
|
|
} else {
|
|
|
|
// The window is no longer maximized vertically and the saved geometry is
|
|
|
|
// invalid. This would happen if the window had been mapped in the maximized state.
|
|
|
|
// We ask the client to resize the window vertically to its preferred size.
|
|
|
|
geometry.setY(clientArea.y());
|
|
|
|
geometry.setHeight(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-03 08:43:36 +00:00
|
|
|
const auto oldQuickTileMode = quickTileMode();
|
2020-02-17 18:39:17 +00:00
|
|
|
if (m_requestedMaximizeMode == MaximizeFull) {
|
|
|
|
if (options->electricBorderMaximize()) {
|
|
|
|
updateQuickTileMode(QuickTileFlag::Maximize);
|
|
|
|
} else {
|
|
|
|
updateQuickTileMode(QuickTileFlag::None);
|
|
|
|
}
|
2022-02-03 08:43:36 +00:00
|
|
|
} else {
|
2020-09-10 10:59:56 +00:00
|
|
|
updateQuickTileMode(QuickTileFlag::None);
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
moveResize(geometry);
|
2020-09-10 10:59:56 +00:00
|
|
|
|
|
|
|
if (oldQuickTileMode != quickTileMode()) {
|
|
|
|
doSetQuickTileMode();
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT quickTileModeChanged();
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
doSetMaximized();
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgPopupClient::XdgPopupClient(XdgPopupInterface *shellSurface)
|
|
|
|
: XdgSurfaceClient(shellSurface->xdgSurface())
|
|
|
|
, m_shellSurface(shellSurface)
|
|
|
|
{
|
2021-11-15 14:03:53 +00:00
|
|
|
m_windowType = NET::Unknown;
|
2021-08-13 06:37:20 +00:00
|
|
|
setDesktops({VirtualDesktopManager::self()->currentDesktop()});
|
2021-06-19 20:43:08 +00:00
|
|
|
#if KWIN_BUILD_ACTIVITIES
|
|
|
|
if (auto a = Activities::self()) {
|
|
|
|
setOnActivities({a->current()});
|
|
|
|
}
|
|
|
|
#endif
|
2020-02-17 18:39:17 +00:00
|
|
|
|
|
|
|
connect(shellSurface, &XdgPopupInterface::grabRequested,
|
|
|
|
this, &XdgPopupClient::handleGrabRequested);
|
|
|
|
connect(shellSurface, &XdgPopupInterface::initializeRequested,
|
|
|
|
this, &XdgPopupClient::initialize);
|
2021-01-06 01:08:25 +00:00
|
|
|
connect(shellSurface, &XdgPopupInterface::repositionRequested,
|
|
|
|
this, &XdgPopupClient::handleRepositionRequested);
|
2021-08-12 10:03:35 +00:00
|
|
|
connect(shellSurface, &XdgPopupInterface::aboutToBeDestroyed,
|
2020-02-17 18:39:17 +00:00
|
|
|
this, &XdgPopupClient::destroyClient);
|
|
|
|
}
|
|
|
|
|
2021-02-01 07:59:02 +00:00
|
|
|
void XdgPopupClient::updateReactive()
|
2021-01-06 01:08:25 +00:00
|
|
|
{
|
|
|
|
if (m_shellSurface->positioner().isReactive()) {
|
|
|
|
connect(transientFor(), &AbstractClient::frameGeometryChanged,
|
|
|
|
this, &XdgPopupClient::relayout, Qt::UniqueConnection);
|
|
|
|
} else {
|
|
|
|
disconnect(transientFor(), &AbstractClient::frameGeometryChanged,
|
|
|
|
this, &XdgPopupClient::relayout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgPopupClient::handleRepositionRequested(quint32 token)
|
|
|
|
{
|
2021-02-01 07:59:02 +00:00
|
|
|
updateReactive();
|
2021-01-06 01:08:25 +00:00
|
|
|
m_shellSurface->sendRepositioned(token);
|
|
|
|
relayout();
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgPopupClient::relayout()
|
|
|
|
{
|
|
|
|
Placement::self()->place(this, QRect());
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
scheduleConfigure();
|
2021-01-06 01:08:25 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
XdgPopupClient::~XdgPopupClient()
|
|
|
|
{
|
2018-01-04 18:32:18 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgPopupClient::hasPopupGrab() const
|
|
|
|
{
|
|
|
|
return m_haveExplicitGrab;
|
|
|
|
}
|
[wayland] Asyncronously update maximise flags
Summary:
A window maximising is an async operation. We work out what size we want
the client to be, then request the client to update. The window isn't
really maximised until we get that new buffer with the new size.
This patch splits the requested, pending and current state, updating as
appropriate.
Things are a bit complex with things like borders. Technically we
shouldn't update them till we get a response, but we also need to have
the correct geometry of the full size window in our request. For now
they behave as before, updating when we request the change.
X code is untouched.
This hopefully fixes maximise animations on wayland as now we update the
geometry before emitting maximisedChanged.
Test Plan:
Maximised a window with the button and double clicking title bar.
I get only the following events on maximise/restore:
19:51:39.156 KWin::EffectsHandlerImpl::slotGeometryShapeChanged geometry
shape changed QRect(47,24 640x509) QRect(0,0 716x573)
19:51:39.157 KWin::EffectsHandlerImpl::slotClientMaximized slot client
maximised true true
19:51:40.522 KWin::EffectsHandlerImpl::slotGeometryShapeChanged geometry
shape changed QRect(0,0 716x573) QRect(47,24 640x509)
19:51:40.522 KWin::EffectsHandlerImpl::slotClientMaximized slot client
maximised false false
BUG: 382698
Reviewers: #kwin, romangg
Reviewed By: #kwin, romangg
Subscribers: romangg, zzag, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D15150
2018-10-05 14:36:02 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgPopupClient::popupDone()
|
|
|
|
{
|
|
|
|
m_shellSurface->sendPopupDone();
|
[wayland] Asyncronously update maximise flags
Summary:
A window maximising is an async operation. We work out what size we want
the client to be, then request the client to update. The window isn't
really maximised until we get that new buffer with the new size.
This patch splits the requested, pending and current state, updating as
appropriate.
Things are a bit complex with things like borders. Technically we
shouldn't update them till we get a response, but we also need to have
the correct geometry of the full size window in our request. For now
they behave as before, updating when we request the change.
X code is untouched.
This hopefully fixes maximise animations on wayland as now we update the
geometry before emitting maximisedChanged.
Test Plan:
Maximised a window with the button and double clicking title bar.
I get only the following events on maximise/restore:
19:51:39.156 KWin::EffectsHandlerImpl::slotGeometryShapeChanged geometry
shape changed QRect(47,24 640x509) QRect(0,0 716x573)
19:51:39.157 KWin::EffectsHandlerImpl::slotClientMaximized slot client
maximised true true
19:51:40.522 KWin::EffectsHandlerImpl::slotGeometryShapeChanged geometry
shape changed QRect(0,0 716x573) QRect(47,24 640x509)
19:51:40.522 KWin::EffectsHandlerImpl::slotClientMaximized slot client
maximised false false
BUG: 382698
Reviewers: #kwin, romangg
Reviewed By: #kwin, romangg
Subscribers: romangg, zzag, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D15150
2018-10-05 14:36:02 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgPopupClient::isPopupWindow() const
|
2015-06-19 22:14:49 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
return true;
|
2015-06-19 22:14:49 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgPopupClient::isTransient() const
|
2019-09-15 09:41:21 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
return true;
|
2019-09-15 09:41:21 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgPopupClient::isResizable() const
|
2015-06-19 23:11:42 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XdgPopupClient::isMovable() const
|
|
|
|
{
|
|
|
|
return false;
|
2015-06-19 23:11:42 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgPopupClient::isMovableAcrossScreens() const
|
2015-09-11 10:11:01 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
return false;
|
2015-09-11 10:11:01 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgPopupClient::hasTransientPlacementHint() const
|
2015-09-11 11:31:41 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
return true;
|
2015-09-11 11:31:41 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
QRect XdgPopupClient::transientPlacement(const QRect &bounds) const
|
|
|
|
{
|
|
|
|
const XdgPositioner positioner = m_shellSurface->positioner();
|
2020-05-07 14:29:41 +00:00
|
|
|
|
2021-04-23 20:36:18 +00:00
|
|
|
const QSize desiredSize = positioner.size();
|
2018-10-19 22:21:54 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
const QPoint parentPosition = transientFor()->framePosToClientPos(transientFor()->pos());
|
2018-10-19 22:21:54 +00:00
|
|
|
|
|
|
|
// returns if a target is within the supplied bounds, optional edges argument states which side to check
|
|
|
|
auto inBounds = [bounds](const QRect &target, Qt::Edges edges = Qt::LeftEdge | Qt::RightEdge | Qt::TopEdge | Qt::BottomEdge) -> bool {
|
|
|
|
if (edges & Qt::LeftEdge && target.left() < bounds.left()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (edges & Qt::TopEdge && target.top() < bounds.top()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (edges & Qt::RightEdge && target.right() > bounds.right()) {
|
|
|
|
//normal QRect::right issue cancels out
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (edges & Qt::BottomEdge && target.bottom() > bounds.bottom()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
QRect popupRect(popupOffset(positioner.anchorRect(), positioner.anchorEdges(), positioner.gravityEdges(), desiredSize) + positioner.offset() + parentPosition, desiredSize);
|
2018-10-19 22:21:54 +00:00
|
|
|
|
|
|
|
//if that fits, we don't need to do anything
|
2020-02-03 12:43:48 +00:00
|
|
|
if (inBounds(popupRect)) {
|
|
|
|
return popupRect;
|
2018-10-19 22:21:54 +00:00
|
|
|
}
|
|
|
|
//otherwise apply constraint adjustment per axis in order XDG Shell Popup states
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
if (positioner.flipConstraintAdjustments() & Qt::Horizontal) {
|
2020-02-03 12:43:48 +00:00
|
|
|
if (!inBounds(popupRect, Qt::LeftEdge | Qt::RightEdge)) {
|
2018-10-19 22:21:54 +00:00
|
|
|
//flip both edges (if either bit is set, XOR both)
|
2020-02-17 18:39:17 +00:00
|
|
|
auto flippedAnchorEdge = positioner.anchorEdges();
|
2018-10-19 22:21:54 +00:00
|
|
|
if (flippedAnchorEdge & (Qt::LeftEdge | Qt::RightEdge)) {
|
|
|
|
flippedAnchorEdge ^= (Qt::LeftEdge | Qt::RightEdge);
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
auto flippedGravity = positioner.gravityEdges();
|
2018-10-19 22:21:54 +00:00
|
|
|
if (flippedGravity & (Qt::LeftEdge | Qt::RightEdge)) {
|
|
|
|
flippedGravity ^= (Qt::LeftEdge | Qt::RightEdge);
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
auto flippedPopupRect = QRect(popupOffset(positioner.anchorRect(), flippedAnchorEdge, flippedGravity, desiredSize) + positioner.offset() + parentPosition, desiredSize);
|
2018-10-19 22:21:54 +00:00
|
|
|
|
|
|
|
//if it still doesn't fit we should continue with the unflipped version
|
2020-02-03 12:43:48 +00:00
|
|
|
if (inBounds(flippedPopupRect, Qt::LeftEdge | Qt::RightEdge)) {
|
|
|
|
popupRect.moveLeft(flippedPopupRect.left());
|
2018-10-19 22:21:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
if (positioner.slideConstraintAdjustments() & Qt::Horizontal) {
|
2020-02-03 12:43:48 +00:00
|
|
|
if (!inBounds(popupRect, Qt::LeftEdge)) {
|
|
|
|
popupRect.moveLeft(bounds.left());
|
2018-10-19 22:21:54 +00:00
|
|
|
}
|
2020-02-03 12:43:48 +00:00
|
|
|
if (!inBounds(popupRect, Qt::RightEdge)) {
|
|
|
|
popupRect.moveRight(bounds.right());
|
2018-10-19 22:21:54 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
if (positioner.resizeConstraintAdjustments() & Qt::Horizontal) {
|
2020-02-03 12:43:48 +00:00
|
|
|
QRect unconstrainedRect = popupRect;
|
2020-01-31 02:36:45 +00:00
|
|
|
|
|
|
|
if (!inBounds(unconstrainedRect, Qt::LeftEdge)) {
|
|
|
|
unconstrainedRect.setLeft(bounds.left());
|
|
|
|
}
|
|
|
|
if (!inBounds(unconstrainedRect, Qt::RightEdge)) {
|
|
|
|
unconstrainedRect.setRight(bounds.right());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unconstrainedRect.isValid()) {
|
2020-02-03 12:43:48 +00:00
|
|
|
popupRect = unconstrainedRect;
|
2020-01-31 02:36:45 +00:00
|
|
|
}
|
2018-10-19 22:21:54 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
if (positioner.flipConstraintAdjustments() & Qt::Vertical) {
|
2020-02-03 12:43:48 +00:00
|
|
|
if (!inBounds(popupRect, Qt::TopEdge | Qt::BottomEdge)) {
|
2018-10-19 22:21:54 +00:00
|
|
|
//flip both edges (if either bit is set, XOR both)
|
2020-02-17 18:39:17 +00:00
|
|
|
auto flippedAnchorEdge = positioner.anchorEdges();
|
2018-10-19 22:21:54 +00:00
|
|
|
if (flippedAnchorEdge & (Qt::TopEdge | Qt::BottomEdge)) {
|
|
|
|
flippedAnchorEdge ^= (Qt::TopEdge | Qt::BottomEdge);
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
auto flippedGravity = positioner.gravityEdges();
|
2018-10-19 22:21:54 +00:00
|
|
|
if (flippedGravity & (Qt::TopEdge | Qt::BottomEdge)) {
|
|
|
|
flippedGravity ^= (Qt::TopEdge | Qt::BottomEdge);
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
auto flippedPopupRect = QRect(popupOffset(positioner.anchorRect(), flippedAnchorEdge, flippedGravity, desiredSize) + positioner.offset() + parentPosition, desiredSize);
|
2018-10-19 22:21:54 +00:00
|
|
|
|
|
|
|
//if it still doesn't fit we should continue with the unflipped version
|
2020-02-03 12:43:48 +00:00
|
|
|
if (inBounds(flippedPopupRect, Qt::TopEdge | Qt::BottomEdge)) {
|
|
|
|
popupRect.moveTop(flippedPopupRect.top());
|
2018-10-19 22:21:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
if (positioner.slideConstraintAdjustments() & Qt::Vertical) {
|
2020-02-03 12:43:48 +00:00
|
|
|
if (!inBounds(popupRect, Qt::TopEdge)) {
|
|
|
|
popupRect.moveTop(bounds.top());
|
2018-10-19 22:21:54 +00:00
|
|
|
}
|
2020-02-03 12:43:48 +00:00
|
|
|
if (!inBounds(popupRect, Qt::BottomEdge)) {
|
|
|
|
popupRect.moveBottom(bounds.bottom());
|
2018-10-19 22:21:54 +00:00
|
|
|
}
|
2016-04-22 12:13:37 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
if (positioner.resizeConstraintAdjustments() & Qt::Vertical) {
|
2020-02-03 12:43:48 +00:00
|
|
|
QRect unconstrainedRect = popupRect;
|
2020-01-31 02:36:45 +00:00
|
|
|
|
|
|
|
if (!inBounds(unconstrainedRect, Qt::TopEdge)) {
|
|
|
|
unconstrainedRect.setTop(bounds.top());
|
|
|
|
}
|
|
|
|
if (!inBounds(unconstrainedRect, Qt::BottomEdge)) {
|
|
|
|
unconstrainedRect.setBottom(bounds.bottom());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unconstrainedRect.isValid()) {
|
2020-02-03 12:43:48 +00:00
|
|
|
popupRect = unconstrainedRect;
|
2020-01-31 02:36:45 +00:00
|
|
|
}
|
2018-10-19 22:21:54 +00:00
|
|
|
}
|
|
|
|
|
2020-02-03 12:43:48 +00:00
|
|
|
return popupRect;
|
2018-10-19 22:21:54 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgPopupClient::isCloseable() const
|
2015-12-07 13:27:06 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
return false;
|
2015-12-07 13:27:06 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgPopupClient::closeWindow()
|
2015-12-17 10:14:54 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgPopupClient::wantsInput() const
|
2019-09-15 09:41:21 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
return false;
|
2019-09-15 09:41:21 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 11:00:11 +00:00
|
|
|
bool XdgPopupClient::takeFocus()
|
2017-03-25 17:41:28 +00:00
|
|
|
{
|
2020-07-22 11:00:11 +00:00
|
|
|
return false;
|
2017-03-25 17:41:28 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
bool XdgPopupClient::acceptsFocus() const
|
2017-03-25 17:41:28 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
return false;
|
2017-03-25 17:41:28 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
XdgSurfaceConfigure *XdgPopupClient::sendRoleConfigure() const
|
2017-08-24 16:57:30 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
const QPoint parentPosition = transientFor()->framePosToClientPos(transientFor()->pos());
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
const QPoint popupPosition = moveResizeGeometry().topLeft() - parentPosition;
|
2017-08-24 16:57:30 +00:00
|
|
|
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
const quint32 serial = m_shellSurface->sendConfigure(QRect(popupPosition, moveResizeGeometry().size()));
|
[effects] Make Scale and Glide effects Wayland-friendly
Summary:
The Scale effect and the Glide effect have to animate only ordinary
windows(i.e. the ones that are considered to be apps).
On X11, in order to distinguish ordinary windows from combo box popups,
popup menus, and other popups, those effects check whether given window
is managed.
On Wayland, there is no concept of managed/unmanaged windows.
XDG Shell protocol defines 2 surface roles:
* xdg_toplevel;
* and, xdg_popup.
The former can be used to implement typical windows, the ones that can
be minimized, maximized, etc.
The latter can be used to implement tooltips, popup menus, etc. Thus,
that's a good criteria to filter popup windows.
CCBUG: 398100
Reviewers: #kwin, graesslin, davidedmundson
Reviewed By: #kwin, graesslin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D15117
2018-10-08 18:08:13 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
XdgSurfaceConfigure *configureEvent = new XdgSurfaceConfigure();
|
2022-01-06 10:16:09 +00:00
|
|
|
configureEvent->bounds = moveResizeGeometry();
|
2020-02-17 18:39:17 +00:00
|
|
|
configureEvent->serial = serial;
|
[wayland] Apply window rules only to xdg-shell clients
Summary:
There are rules that have to be applied only once, e.g. every Remember
and Apply Initially rule, as well rules that need to configure the client,
e.g. size, etc. In the best scenario the compositor would evaluate such
rules when the client is about to be mapped.
This change limits window rules only to xdg-shell clients because right
now only this protocol lets compositors to intervene in the client
initialization process. Also, it makes things a bit easier for us on the
compositor side.
xdg-shell protocol satisfies most of ours requirements to implement window
rules, but not all of them. If the client is about to be mapped for the
second time and its size is forced by a rule, then compositor may need
to configure it. Currently, xdg-shell protocol doesn't have any mechanism
that a client could use to notify the compositor about its intent to map.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: fmonteiro, davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D19411
2019-07-09 11:58:57 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
return configureEvent;
|
2020-01-23 21:40:34 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgPopupClient::handleGrabRequested(SeatInterface *seat, quint32 serial)
|
2020-01-23 21:40:34 +00:00
|
|
|
{
|
2020-02-17 18:39:17 +00:00
|
|
|
Q_UNUSED(seat)
|
|
|
|
Q_UNUSED(serial)
|
|
|
|
m_haveExplicitGrab = true;
|
2020-01-23 21:40:34 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
void XdgPopupClient::initialize()
|
2020-01-28 22:02:05 +00:00
|
|
|
{
|
2020-08-07 18:43:59 +00:00
|
|
|
AbstractClient *parentClient = waylandServer()->findClient(m_shellSurface->parentSurface());
|
|
|
|
parentClient->addTransient(this);
|
|
|
|
setTransientFor(parentClient);
|
|
|
|
|
2021-02-01 07:59:02 +00:00
|
|
|
updateReactive();
|
|
|
|
|
2021-08-28 18:58:29 +00:00
|
|
|
const QRect area = workspace()->clientArea(PlacementArea, this, workspace()->activeOutput());
|
2021-11-03 09:35:00 +00:00
|
|
|
Placement::self()->place(this, area);
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
scheduleConfigure();
|
2020-01-28 22:02:05 +00:00
|
|
|
}
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
} // namespace KWin
|