2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 1997-2002 Cristian Tibirna <tibirna@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
#include "placement.h"
|
|
|
|
|
|
|
|
#ifndef KCMRULES
|
|
|
|
#include "workspace.h"
|
2019-09-24 08:48:08 +00:00
|
|
|
#include "x11client.h"
|
2014-03-19 13:45:27 +00:00
|
|
|
#include "cursor.h"
|
2007-04-29 17:35:43 +00:00
|
|
|
#include "options.h"
|
|
|
|
#include "rules.h"
|
2013-09-16 14:26:21 +00:00
|
|
|
#include "screens.h"
|
2007-04-29 17:35:43 +00:00
|
|
|
#endif
|
|
|
|
|
2019-07-09 19:19:26 +00:00
|
|
|
#include <QTextStream>
|
2020-06-12 13:50:24 +00:00
|
|
|
#include <QTimer>
|
2019-07-09 19:19:26 +00:00
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
|
|
|
#ifndef KCMRULES
|
|
|
|
|
2013-04-05 07:41:25 +00:00
|
|
|
KWIN_SINGLETON_FACTORY(Placement)
|
2012-11-22 12:06:42 +00:00
|
|
|
|
2013-04-05 07:41:25 +00:00
|
|
|
Placement::Placement(QObject*)
|
2012-11-22 12:06:42 +00:00
|
|
|
{
|
2011-01-30 14:34:42 +00:00
|
|
|
reinitCascading(0);
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2012-11-22 12:06:42 +00:00
|
|
|
Placement::~Placement()
|
|
|
|
{
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
s_self = nullptr;
|
2012-11-22 12:06:42 +00:00
|
|
|
}
|
|
|
|
|
2019-02-02 18:17:44 +00:00
|
|
|
/**
|
|
|
|
* Places the client \a c according to the workspace's layout policy
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2019-09-12 14:52:47 +00:00
|
|
|
void Placement::place(AbstractClient *c, const QRect &area)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
Policy policy = c->rules()->checkPlacement(Default);
|
|
|
|
if (policy != Default) {
|
|
|
|
place(c, area, policy);
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if (c->isUtility())
|
2012-02-20 09:25:13 +00:00
|
|
|
placeUtility(c, area, options->placement());
|
2011-01-30 14:34:42 +00:00
|
|
|
else if (c->isDialog())
|
2012-02-20 09:25:13 +00:00
|
|
|
placeDialog(c, area, options->placement());
|
2011-01-30 14:34:42 +00:00
|
|
|
else if (c->isSplash())
|
|
|
|
placeOnMainWindow(c, area); // on mainwindow, if any, otherwise centered
|
2019-05-02 08:29:38 +00:00
|
|
|
else if (c->isOnScreenDisplay() || c->isNotification() || c->isCriticalNotification())
|
2015-01-02 11:11:54 +00:00
|
|
|
placeOnScreenDisplay(c, area);
|
2015-09-11 11:31:41 +00:00
|
|
|
else if (c->isTransient() && c->hasTransientPlacementHint())
|
|
|
|
placeTransient(c);
|
2018-10-23 11:18:03 +00:00
|
|
|
else if (c->isTransient() && c->surface())
|
|
|
|
placeDialog(c, area, options->placement());
|
2007-04-29 17:35:43 +00:00
|
|
|
else
|
2012-02-20 09:25:13 +00:00
|
|
|
place(c, area, options->placement());
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-12 14:52:47 +00:00
|
|
|
void Placement::place(AbstractClient *c, const QRect &area, Policy policy, Policy nextPlacement)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (policy == Unknown)
|
2007-04-29 17:35:43 +00:00
|
|
|
policy = Default;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (policy == Default)
|
2012-02-20 09:25:13 +00:00
|
|
|
policy = options->placement();
|
2011-01-30 14:34:42 +00:00
|
|
|
if (policy == NoPlacement)
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
|
|
|
else if (policy == Random)
|
|
|
|
placeAtRandom(c, area, nextPlacement);
|
|
|
|
else if (policy == Cascade)
|
|
|
|
placeCascaded(c, area, nextPlacement);
|
|
|
|
else if (policy == Centered)
|
|
|
|
placeCentered(c, area, nextPlacement);
|
|
|
|
else if (policy == ZeroCornered)
|
|
|
|
placeZeroCornered(c, area, nextPlacement);
|
|
|
|
else if (policy == UnderMouse)
|
|
|
|
placeUnderMouse(c, area, nextPlacement);
|
|
|
|
else if (policy == OnMainWindow)
|
|
|
|
placeOnMainWindow(c, area, nextPlacement);
|
2011-01-30 14:34:42 +00:00
|
|
|
else if (policy == Maximizing)
|
2007-04-29 17:35:43 +00:00
|
|
|
placeMaximizing(c, area, nextPlacement);
|
|
|
|
else
|
|
|
|
placeSmart(c, area, nextPlacement);
|
2013-08-15 09:53:02 +00:00
|
|
|
|
|
|
|
if (options->borderSnapZone()) {
|
|
|
|
// snap to titlebar / snap to window borders on inner screen edges
|
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 geo(c->moveResizeGeometry());
|
2013-08-15 09:53:02 +00:00
|
|
|
QPoint corner = geo.topLeft();
|
2019-09-24 13:24:06 +00:00
|
|
|
const QMargins frameMargins = c->frameMargins();
|
2019-04-23 22:49:10 +00:00
|
|
|
AbstractClient::Position titlePos = c->titlebarPosition();
|
2013-08-15 09:53:02 +00:00
|
|
|
|
|
|
|
const QRect fullRect = workspace()->clientArea(FullArea, c);
|
2014-12-02 12:49:08 +00:00
|
|
|
if (!(c->maximizeMode() & MaximizeHorizontal)) {
|
2019-09-24 13:24:06 +00:00
|
|
|
if (titlePos != AbstractClient::PositionRight && geo.right() == fullRect.right()) {
|
|
|
|
corner.rx() += frameMargins.right();
|
|
|
|
}
|
|
|
|
if (titlePos != AbstractClient::PositionLeft && geo.left() == fullRect.left()) {
|
|
|
|
corner.rx() -= frameMargins.left();
|
|
|
|
}
|
2013-08-15 09:53:02 +00:00
|
|
|
}
|
2014-12-02 12:49:08 +00:00
|
|
|
if (!(c->maximizeMode() & MaximizeVertical)) {
|
2019-09-24 13:24:06 +00:00
|
|
|
if (titlePos != AbstractClient::PositionBottom && geo.bottom() == fullRect.bottom()) {
|
|
|
|
corner.ry() += frameMargins.bottom();
|
|
|
|
}
|
|
|
|
if (titlePos != AbstractClient::PositionTop && geo.top() == fullRect.top()) {
|
|
|
|
corner.ry() -= frameMargins.top();
|
|
|
|
}
|
2013-08-15 09:53:02 +00:00
|
|
|
}
|
|
|
|
c->move(corner);
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-02-02 18:17:44 +00:00
|
|
|
/**
|
|
|
|
* Place the client \a c according to a simply "random" placement algorithm.
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2015-05-27 08:34:15 +00:00
|
|
|
void Placement::placeAtRandom(AbstractClient* c, const QRect& area, Policy /*next*/)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
const int step = 24;
|
|
|
|
static int px = step;
|
|
|
|
static int py = 2 * step;
|
2011-01-30 14:34:42 +00:00
|
|
|
int tx, ty;
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-13 09:19:32 +00:00
|
|
|
if (px < area.x()) {
|
|
|
|
px = area.x();
|
|
|
|
}
|
|
|
|
if (py < area.y()) {
|
|
|
|
py = area.y();
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
px += step;
|
2011-01-30 14:34:42 +00:00
|
|
|
py += 2 * step;
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-13 09:19:32 +00:00
|
|
|
if (px > area.width() / 2) {
|
|
|
|
px = area.x() + step;
|
|
|
|
}
|
|
|
|
if (py > area.height() / 2) {
|
|
|
|
py = area.y() + step;
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
tx = px;
|
|
|
|
ty = py;
|
2019-09-13 09:19:32 +00:00
|
|
|
if (tx + c->width() > area.right()) {
|
|
|
|
tx = area.right() - c->width();
|
2007-04-29 17:35:43 +00:00
|
|
|
if (tx < 0)
|
|
|
|
tx = 0;
|
2019-09-13 09:19:32 +00:00
|
|
|
px = area.x();
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2019-09-13 09:19:32 +00:00
|
|
|
if (ty + c->height() > area.bottom()) {
|
|
|
|
ty = area.bottom() - c->height();
|
2007-04-29 17:35:43 +00:00
|
|
|
if (ty < 0)
|
|
|
|
ty = 0;
|
2019-09-13 09:19:32 +00:00
|
|
|
py = area.y();
|
2007-04-29 17:35:43 +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
|
|
|
c->move(QPoint(tx, ty));
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2013-01-22 19:09:42 +00:00
|
|
|
// TODO: one day, there'll be C++11 ...
|
2015-03-12 09:44:34 +00:00
|
|
|
static inline bool isIrrelevant(const AbstractClient *client, const AbstractClient *regarding, int desktop)
|
2013-01-22 19:09:42 +00:00
|
|
|
{
|
|
|
|
if (!client)
|
|
|
|
return true;
|
|
|
|
if (client == regarding)
|
|
|
|
return true;
|
|
|
|
if (!client->isShown(false))
|
|
|
|
return true;
|
|
|
|
if (!client->isOnDesktop(desktop))
|
|
|
|
return true;
|
|
|
|
if (!client->isOnCurrentActivity())
|
|
|
|
return true;
|
2013-06-25 13:35:13 +00:00
|
|
|
if (client->isDesktop())
|
|
|
|
return true;
|
2013-01-22 19:09:42 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-02-02 18:17:44 +00:00
|
|
|
/**
|
|
|
|
* Place the client \a c according to a really smart placement algorithm :-)
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2015-05-27 08:32:42 +00:00
|
|
|
void Placement::placeSmart(AbstractClient* c, const QRect& area, Policy /*next*/)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
/*
|
|
|
|
* SmartPlacement by Cristian Tibirna (tibirna@kde.org)
|
|
|
|
* adapted for kwm (16-19jan98) and for kwin (16Nov1999) using (with
|
|
|
|
* permission) ideas from fvwm, authored by
|
|
|
|
* Anthony Martin (amartin@engr.csulb.edu).
|
|
|
|
* Xinerama supported added by Balaji Ramani (balaji@yablibli.com)
|
|
|
|
* with ideas from xfce.
|
|
|
|
*/
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
if (!c->frameGeometry().isValid()) {
|
2019-06-24 20:59:21 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
const int none = 0, h_wrong = -1, w_wrong = -2; // overlap types
|
|
|
|
long int overlap, min_overlap = 0;
|
|
|
|
int x_optimal, y_optimal;
|
|
|
|
int possible;
|
2012-11-16 07:23:47 +00:00
|
|
|
int desktop = c->desktop() == 0 || c->isOnAllDesktops() ? VirtualDesktopManager::self()->current() : c->desktop();
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
int cxl, cxr, cyt, cyb; //temp coords
|
|
|
|
int xl, xr, yt, yb; //temp coords
|
|
|
|
int basket; //temp holder
|
|
|
|
|
|
|
|
// get the maximum allowed windows space
|
2019-09-13 09:19:32 +00:00
|
|
|
int x = area.left();
|
|
|
|
int y = area.top();
|
2007-04-29 17:35:43 +00:00
|
|
|
x_optimal = x; y_optimal = y;
|
|
|
|
|
|
|
|
//client gabarit
|
|
|
|
int ch = c->height() - 1;
|
|
|
|
int cw = c->width() - 1;
|
|
|
|
|
|
|
|
bool first_pass = true; //CT lame flag. Don't like it. What else would do?
|
|
|
|
|
|
|
|
//loop over possible positions
|
2011-01-30 14:34:42 +00:00
|
|
|
do {
|
2007-04-29 17:35:43 +00:00
|
|
|
//test if enough room in x and y directions
|
2019-09-13 09:19:32 +00:00
|
|
|
if (y + ch > area.bottom() && ch < area.height()) {
|
2007-04-29 17:35:43 +00:00
|
|
|
overlap = h_wrong; // this throws the algorithm to an exit
|
2019-09-13 09:19:32 +00:00
|
|
|
} else if (x + cw > area.right()) {
|
2007-04-29 17:35:43 +00:00
|
|
|
overlap = w_wrong;
|
2019-09-13 09:19:32 +00:00
|
|
|
} else {
|
2007-04-29 17:35:43 +00:00
|
|
|
overlap = none; //initialize
|
|
|
|
|
|
|
|
cxl = x; cxr = x + cw;
|
|
|
|
cyt = y; cyb = y + ch;
|
Drop some custom list typedefs
Summary:
Qt has its own thing where a type might also have corresponding list
alias, e.g. QObject and QObjectList, QWidget and QWidgetList. I don't
know why Qt does that, maybe for some historical reasons, but what
matters is that we copy this pattern here in KWin. While this pattern
might be useful with some long list types, for example
QList<QWeakPointer<TabBoxClient>> TabBoxClientList
in general, it causes more harm than good. For example, we've got two
new client types, do we need corresponding list typedefs for them? If
no, why do we have ClientList and so on?
Another problem with these typedefs is that you need to include utils.h
header in order to use them. A better way to handle such things is to
just forward declare a client class (if that's possible) and use it
directly with QList or QVector. This way translation units don't get
"bloated" with utils.h stuff for no apparent reason.
So, in order to make code more consistent and easier to follow, this
change drops some of our custom typedefs. Namely ConstClientList,
ClientList, DeletedList, UnmanagedList, ToplevelList, and GroupList.
Test Plan: Compiles.
Reviewers: #kwin
Subscribers: kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D24950
2019-10-16 09:11:04 +00:00
|
|
|
for (auto l = workspace()->stackingOrder().constBegin(); l != workspace()->stackingOrder().constEnd() ; ++l) {
|
2015-05-27 08:32:42 +00:00
|
|
|
AbstractClient *client = qobject_cast<AbstractClient*>(*l);
|
2013-01-22 19:09:42 +00:00
|
|
|
if (isIrrelevant(client, c, desktop)) {
|
2012-04-08 08:07:35 +00:00
|
|
|
continue;
|
|
|
|
}
|
2013-01-22 19:09:42 +00:00
|
|
|
xl = client->x(); yt = client->y();
|
|
|
|
xr = xl + client->width(); yb = yt + client->height();
|
|
|
|
|
|
|
|
//if windows overlap, calc the overall overlapping
|
|
|
|
if ((cxl < xr) && (cxr > xl) &&
|
|
|
|
(cyt < yb) && (cyb > yt)) {
|
|
|
|
xl = qMax(cxl, xl); xr = qMin(cxr, xr);
|
|
|
|
yt = qMax(cyt, yt); yb = qMin(cyb, yb);
|
|
|
|
if (client->keepAbove())
|
|
|
|
overlap += 16 * (xr - xl) * (yb - yt);
|
|
|
|
else if (client->keepBelow() && !client->isDock()) // ignore KeepBelow windows
|
2019-09-24 08:48:08 +00:00
|
|
|
overlap += 0; // for placement (see X11Client::belongsToLayer() for Dock)
|
2013-01-22 19:09:42 +00:00
|
|
|
else
|
|
|
|
overlap += (xr - xl) * (yb - yt);
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
//CT first time we get no overlap we stop.
|
2011-01-30 14:34:42 +00:00
|
|
|
if (overlap == none) {
|
2007-04-29 17:35:43 +00:00
|
|
|
x_optimal = x;
|
|
|
|
y_optimal = y;
|
|
|
|
break;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if (first_pass) {
|
2007-04-29 17:35:43 +00:00
|
|
|
first_pass = false;
|
|
|
|
min_overlap = overlap;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
//CT save the best position and the minimum overlap up to now
|
2011-01-30 14:34:42 +00:00
|
|
|
else if (overlap >= none && overlap < min_overlap) {
|
2007-04-29 17:35:43 +00:00
|
|
|
min_overlap = overlap;
|
|
|
|
x_optimal = x;
|
|
|
|
y_optimal = y;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// really need to loop? test if there's any overlap
|
2011-01-30 14:34:42 +00:00
|
|
|
if (overlap > none) {
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-13 09:19:32 +00:00
|
|
|
possible = area.right();
|
2007-04-29 17:35:43 +00:00
|
|
|
if (possible - cw > x) possible -= cw;
|
|
|
|
|
|
|
|
// compare to the position of each client on the same desk
|
Drop some custom list typedefs
Summary:
Qt has its own thing where a type might also have corresponding list
alias, e.g. QObject and QObjectList, QWidget and QWidgetList. I don't
know why Qt does that, maybe for some historical reasons, but what
matters is that we copy this pattern here in KWin. While this pattern
might be useful with some long list types, for example
QList<QWeakPointer<TabBoxClient>> TabBoxClientList
in general, it causes more harm than good. For example, we've got two
new client types, do we need corresponding list typedefs for them? If
no, why do we have ClientList and so on?
Another problem with these typedefs is that you need to include utils.h
header in order to use them. A better way to handle such things is to
just forward declare a client class (if that's possible) and use it
directly with QList or QVector. This way translation units don't get
"bloated" with utils.h stuff for no apparent reason.
So, in order to make code more consistent and easier to follow, this
change drops some of our custom typedefs. Namely ConstClientList,
ClientList, DeletedList, UnmanagedList, ToplevelList, and GroupList.
Test Plan: Compiles.
Reviewers: #kwin
Subscribers: kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D24950
2019-10-16 09:11:04 +00:00
|
|
|
for (auto l = workspace()->stackingOrder().constBegin(); l != workspace()->stackingOrder().constEnd() ; ++l) {
|
2015-05-27 08:32:42 +00:00
|
|
|
AbstractClient *client = qobject_cast<AbstractClient*>(*l);
|
2013-01-22 19:09:42 +00:00
|
|
|
if (isIrrelevant(client, c, desktop)) {
|
2012-04-08 08:07:35 +00:00
|
|
|
continue;
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2013-01-22 19:09:42 +00:00
|
|
|
xl = client->x(); yt = client->y();
|
|
|
|
xr = xl + client->width(); yb = yt + client->height();
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2013-01-22 19:09:42 +00:00
|
|
|
// if not enough room above or under the current tested client
|
|
|
|
// determine the first non-overlapped x position
|
|
|
|
if ((y < yb) && (yt < ch + y)) {
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2013-01-22 19:09:42 +00:00
|
|
|
if ((xr > x) && (possible > xr)) possible = xr;
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2013-01-22 19:09:42 +00:00
|
|
|
basket = xl - cw;
|
|
|
|
if ((basket > x) && (possible > basket)) possible = basket;
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
x = possible;
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// ... else ==> not enough x dimension (overlap was wrong on horizontal)
|
2011-01-30 14:34:42 +00:00
|
|
|
else if (overlap == w_wrong) {
|
2019-09-13 09:19:32 +00:00
|
|
|
x = area.left();
|
|
|
|
possible = area.bottom();
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
if (possible - ch > y) possible -= ch;
|
|
|
|
|
|
|
|
//test the position of each window on the desk
|
Drop some custom list typedefs
Summary:
Qt has its own thing where a type might also have corresponding list
alias, e.g. QObject and QObjectList, QWidget and QWidgetList. I don't
know why Qt does that, maybe for some historical reasons, but what
matters is that we copy this pattern here in KWin. While this pattern
might be useful with some long list types, for example
QList<QWeakPointer<TabBoxClient>> TabBoxClientList
in general, it causes more harm than good. For example, we've got two
new client types, do we need corresponding list typedefs for them? If
no, why do we have ClientList and so on?
Another problem with these typedefs is that you need to include utils.h
header in order to use them. A better way to handle such things is to
just forward declare a client class (if that's possible) and use it
directly with QList or QVector. This way translation units don't get
"bloated" with utils.h stuff for no apparent reason.
So, in order to make code more consistent and easier to follow, this
change drops some of our custom typedefs. Namely ConstClientList,
ClientList, DeletedList, UnmanagedList, ToplevelList, and GroupList.
Test Plan: Compiles.
Reviewers: #kwin
Subscribers: kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D24950
2019-10-16 09:11:04 +00:00
|
|
|
for (auto l = workspace()->stackingOrder().constBegin(); l != workspace()->stackingOrder().constEnd() ; ++l) {
|
2015-05-27 08:32:42 +00:00
|
|
|
AbstractClient *client = qobject_cast<AbstractClient*>(*l);
|
2013-01-22 19:09:42 +00:00
|
|
|
if (isIrrelevant(client, c, desktop)) {
|
2012-04-08 08:07:35 +00:00
|
|
|
continue;
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2013-01-22 19:09:42 +00:00
|
|
|
xl = client->x(); yt = client->y();
|
|
|
|
xr = xl + client->width(); yb = yt + client->height();
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2013-01-22 19:09:42 +00:00
|
|
|
// if not enough room to the left or right of the current tested client
|
|
|
|
// determine the first non-overlapped y position
|
|
|
|
if ((yb > y) && (possible > yb)) possible = yb;
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2013-01-22 19:09:42 +00:00
|
|
|
basket = yt - ch;
|
|
|
|
if ((basket > y) && (possible > basket)) possible = basket;
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
y = possible;
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2019-09-13 09:19:32 +00:00
|
|
|
} while ((overlap != none) && (overlap != h_wrong) && (y < area.bottom()));
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-13 09:19:32 +00:00
|
|
|
if (ch >= area.height()) {
|
|
|
|
y_optimal = area.top();
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// place the window
|
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
|
|
|
c->move(QPoint(x_optimal, y_optimal));
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
void Placement::reinitCascading(int desktop)
|
|
|
|
{
|
|
|
|
// desktop == 0 - reinit all
|
|
|
|
if (desktop == 0) {
|
2007-04-29 17:35:43 +00:00
|
|
|
cci.clear();
|
2012-11-16 07:23:47 +00:00
|
|
|
for (uint i = 0; i < VirtualDesktopManager::self()->count(); ++i) {
|
2007-04-29 17:35:43 +00:00
|
|
|
DesktopCascadingInfo inf;
|
2011-01-30 14:34:42 +00:00
|
|
|
inf.pos = QPoint(-1, -1);
|
2007-04-29 17:35:43 +00:00
|
|
|
inf.col = 0;
|
|
|
|
inf.row = 0;
|
|
|
|
cci.append(inf);
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
} else {
|
2007-04-29 17:35:43 +00:00
|
|
|
cci[desktop - 1].pos = QPoint(-1, -1);
|
|
|
|
cci[desktop - 1].col = cci[desktop - 1].row = 0;
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2015-03-06 08:31:14 +00:00
|
|
|
QPoint Workspace::cascadeOffset(const AbstractClient *c) const
|
2012-08-25 16:20:34 +00:00
|
|
|
{
|
2019-09-27 10:01:10 +00:00
|
|
|
QRect area = clientArea(PlacementArea, c->frameGeometry().center(), c->desktop());
|
2012-08-25 16:20:34 +00:00
|
|
|
return QPoint(area.width()/48, area.height()/48);
|
|
|
|
}
|
|
|
|
|
2019-02-02 18:17:44 +00:00
|
|
|
/**
|
|
|
|
* Place windows in a cascading order, remembering positions for each desktop
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2019-09-12 14:52:47 +00:00
|
|
|
void Placement::placeCascaded(AbstractClient *c, const QRect &area, Policy nextPlacement)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
if (!c->frameGeometry().isValid()) {
|
2019-04-09 10:15:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
/* cascadePlacement by Cristian Tibirna (tibirna@kde.org) (30Jan98)
|
|
|
|
*/
|
|
|
|
// work coords
|
|
|
|
int xp, yp;
|
|
|
|
|
|
|
|
//CT how do I get from the 'Client' class the size that NW squarish "handle"
|
2013-05-08 11:55:20 +00:00
|
|
|
const QPoint delta = workspace()->cascadeOffset(c);
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2012-11-16 07:23:47 +00:00
|
|
|
const int dn = c->desktop() == 0 || c->isOnAllDesktops() ? (VirtualDesktopManager::self()->current() - 1) : (c->desktop() - 1);
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// initialize often used vars: width and height of c; we gain speed
|
|
|
|
const int ch = c->height();
|
|
|
|
const int cw = c->width();
|
2019-09-13 09:19:32 +00:00
|
|
|
const int X = area.left();
|
|
|
|
const int Y = area.top();
|
|
|
|
const int H = area.height();
|
|
|
|
const int W = area.width();
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if (nextPlacement == Unknown)
|
2007-04-29 17:35:43 +00:00
|
|
|
nextPlacement = Smart;
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
//initialize if needed
|
|
|
|
if (cci[dn].pos.x() < 0 || cci[dn].pos.x() < X || cci[dn].pos.y() < Y) {
|
2007-04-29 17:35:43 +00:00
|
|
|
cci[dn].pos = QPoint(X, Y);
|
|
|
|
cci[dn].col = cci[dn].row = 0;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
xp = cci[dn].pos.x();
|
|
|
|
yp = cci[dn].pos.y();
|
|
|
|
|
|
|
|
//here to touch in case people vote for resize on placement
|
|
|
|
if ((yp + ch) > H) yp = Y;
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if ((xp + cw) > W) {
|
|
|
|
if (!yp) {
|
|
|
|
place(c, area, nextPlacement);
|
|
|
|
return;
|
|
|
|
} else xp = X;
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
//if this isn't the first window
|
|
|
|
if (cci[dn].pos.x() != X && cci[dn].pos.y() != Y) {
|
2007-04-29 17:35:43 +00:00
|
|
|
/* The following statements cause an internal compiler error with
|
|
|
|
* egcs-2.91.66 on SuSE Linux 6.3. The equivalent forms compile fine.
|
|
|
|
* 22-Dec-1999 CS
|
|
|
|
*
|
2012-08-25 16:20:34 +00:00
|
|
|
* if (xp != X && yp == Y) xp = delta.x() * (++(cci[dn].col));
|
|
|
|
* if (yp != Y && xp == X) yp = delta.y() * (++(cci[dn].row));
|
2007-04-29 17:35:43 +00:00
|
|
|
*/
|
2011-01-30 14:34:42 +00:00
|
|
|
if (xp != X && yp == Y) {
|
2007-04-29 17:35:43 +00:00
|
|
|
++(cci[dn].col);
|
2012-08-25 16:20:34 +00:00
|
|
|
xp = delta.x() * cci[dn].col;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
if (yp != Y && xp == X) {
|
2007-04-29 17:35:43 +00:00
|
|
|
++(cci[dn].row);
|
2012-08-25 16:20:34 +00:00
|
|
|
yp = delta.y() * cci[dn].row;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// last resort: if still doesn't fit, smart place it
|
2011-01-30 14:34:42 +00:00
|
|
|
if (((xp + cw) > W - X) || ((yp + ch) > H - Y)) {
|
|
|
|
place(c, area, nextPlacement);
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// place the window
|
|
|
|
c->move(QPoint(xp, yp));
|
|
|
|
|
|
|
|
// new position
|
2012-08-25 16:20:34 +00:00
|
|
|
cci[dn].pos = QPoint(xp + delta.x(), yp + delta.y());
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-02-02 18:17:44 +00:00
|
|
|
/**
|
|
|
|
* Place windows centered, on top of all others
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2015-05-27 08:28:23 +00:00
|
|
|
void Placement::placeCentered(AbstractClient* c, const QRect& area, Policy /*next*/)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-13 09:19:32 +00:00
|
|
|
const int xp = area.left() + (area.width() - c->width()) / 2;
|
|
|
|
const int yp = area.top() + (area.height() - c->height()) / 2;
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// place the window
|
|
|
|
c->move(QPoint(xp, yp));
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-02-02 18:17:44 +00:00
|
|
|
/**
|
|
|
|
* Place windows in the (0,0) corner, on top of all others
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2015-05-27 08:25:04 +00:00
|
|
|
void Placement::placeZeroCornered(AbstractClient* c, const QRect& area, Policy /*next*/)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
// get the maximum allowed windows space and desk's origin
|
2019-09-13 09:19:32 +00:00
|
|
|
c->move(area.topLeft());
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-12 14:52:47 +00:00
|
|
|
void Placement::placeUtility(AbstractClient *c, const QRect &area, Policy /*next*/)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2007-04-29 17:35:43 +00:00
|
|
|
// TODO kwin should try to place utility windows next to their mainwindow,
|
|
|
|
// preferably at the right edge, and going down if there are more of them
|
|
|
|
// if there's not enough place outside the mainwindow, it should prefer
|
|
|
|
// top-right corner
|
|
|
|
// use the default placement for now
|
2011-01-30 14:34:42 +00:00
|
|
|
place(c, area, Default);
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-12 14:52:47 +00:00
|
|
|
void Placement::placeOnScreenDisplay(AbstractClient *c, const QRect &area)
|
2014-02-12 09:50:13 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
Position OSD a bit farther down
Summary:
A common user complains is that our OSDs--particularly the volume OSD--are too intrusive
and get in the way of the screen content. For example when adjusting the volume while
watching a full-screen video, the volume change OSD will typically appear right in the
middle of an actor's face.
D20569 was an attempt to use a horizontal OSD to alleviate this issue. It mostly worked,
but IMO it was still positioned too high up.
This patch moved the placement down a little bit to make the OSD appear even farther from
the center of the screen to make it less likely to
I tried not to move it down too far or else it would interfere with subtitles in videos.
Test Plan:
With D20569:
{F8269172}
{F8269164}
{F8269163}
Reviewers: #kwin, #vdg, broulik, ndavis, zzag
Reviewed By: #kwin, #vdg, ndavis, zzag
Subscribers: meven, ndavis, niccolove, baberts, davidedmundson, filipf, zzag, kori, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D29263
2020-04-28 18:29:48 +00:00
|
|
|
// place at lower area of the screen
|
2014-02-12 09:50:13 +00:00
|
|
|
const int x = area.left() + (area.width() - c->width()) / 2;
|
Position OSD a bit farther down
Summary:
A common user complains is that our OSDs--particularly the volume OSD--are too intrusive
and get in the way of the screen content. For example when adjusting the volume while
watching a full-screen video, the volume change OSD will typically appear right in the
middle of an actor's face.
D20569 was an attempt to use a horizontal OSD to alleviate this issue. It mostly worked,
but IMO it was still positioned too high up.
This patch moved the placement down a little bit to make the OSD appear even farther from
the center of the screen to make it less likely to
I tried not to move it down too far or else it would interfere with subtitles in videos.
Test Plan:
With D20569:
{F8269172}
{F8269164}
{F8269163}
Reviewers: #kwin, #vdg, broulik, ndavis, zzag
Reviewed By: #kwin, #vdg, ndavis, zzag
Subscribers: meven, ndavis, niccolove, baberts, davidedmundson, filipf, zzag, kori, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D29263
2020-04-28 18:29:48 +00:00
|
|
|
const int y = area.top() + 2 * area.height() / 3 - c->height() / 2;
|
2014-02-12 09:50:13 +00:00
|
|
|
|
|
|
|
c->move(QPoint(x, y));
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2015-09-11 11:31:41 +00:00
|
|
|
void Placement::placeTransient(AbstractClient *c)
|
|
|
|
{
|
2018-12-27 19:22:53 +00:00
|
|
|
const auto parent = c->transientFor();
|
|
|
|
const QRect screen = Workspace::self()->clientArea(parent->isFullScreen() ? FullScreenArea : PlacementArea, parent);
|
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
|
|
|
c->moveResize(c->transientPlacement(screen));
|
2018-10-19 22:21:54 +00:00
|
|
|
|
|
|
|
// Potentially a client could set no constraint adjustments
|
|
|
|
// and we'll be offscreen.
|
|
|
|
|
|
|
|
// The spec implies we should place window the offscreen. However,
|
|
|
|
// practically Qt doesn't set any constraint adjustments yet so we can't.
|
|
|
|
// Also kwin generally doesn't let clients do what they want
|
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 (!screen.contains(c->moveResizeGeometry())) {
|
2018-10-19 22:21:54 +00:00
|
|
|
c->keepInArea(screen);
|
2016-03-03 14:38:18 +00:00
|
|
|
}
|
2015-09-11 11:31:41 +00:00
|
|
|
}
|
|
|
|
|
2019-09-12 14:52:47 +00:00
|
|
|
void Placement::placeDialog(AbstractClient *c, const QRect &area, Policy nextPlacement)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
placeOnMainWindow(c, area, nextPlacement);
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-12 14:52:47 +00:00
|
|
|
void Placement::placeUnderMouse(AbstractClient *c, const QRect &area, Policy /*next*/)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
2019-09-27 10:01:10 +00:00
|
|
|
QRect geom = c->frameGeometry();
|
2020-04-02 16:18:01 +00:00
|
|
|
geom.moveCenter(Cursors::self()->mouse()->pos());
|
2011-01-30 14:34:42 +00:00
|
|
|
c->move(geom.topLeft());
|
|
|
|
c->keepInArea(area); // make sure it's kept inside workarea
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-12 14:52:47 +00:00
|
|
|
void Placement::placeOnMainWindow(AbstractClient *c, const QRect &area, Policy nextPlacement)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if (nextPlacement == Unknown)
|
2007-04-29 17:35:43 +00:00
|
|
|
nextPlacement = Centered;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (nextPlacement == Maximizing) // maximize if needed
|
|
|
|
placeMaximizing(c, area, NoPlacement);
|
2015-09-11 13:55:23 +00:00
|
|
|
auto mainwindows = c->mainClients();
|
2015-05-27 11:22:50 +00:00
|
|
|
AbstractClient* place_on = nullptr;
|
|
|
|
AbstractClient* place_on2 = nullptr;
|
2007-04-29 17:35:43 +00:00
|
|
|
int mains_count = 0;
|
2015-09-11 13:55:23 +00:00
|
|
|
for (auto it = mainwindows.constBegin();
|
2011-01-30 14:34:42 +00:00
|
|
|
it != mainwindows.constEnd();
|
|
|
|
++it) {
|
|
|
|
if (mainwindows.count() > 1 && (*it)->isSpecialWindow())
|
2007-04-29 17:35:43 +00:00
|
|
|
continue; // don't consider toolbars etc when placing
|
|
|
|
++mains_count;
|
|
|
|
place_on2 = *it;
|
2011-01-30 14:34:42 +00:00
|
|
|
if ((*it)->isOnCurrentDesktop()) {
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
if (place_on == nullptr)
|
2007-04-29 17:35:43 +00:00
|
|
|
place_on = *it;
|
2011-01-30 14:34:42 +00:00
|
|
|
else {
|
|
|
|
// two or more on current desktop -> center
|
|
|
|
// That's the default at least. However, with maximizing placement
|
|
|
|
// policy as the default, the dialog should be either maximized or
|
|
|
|
// made as large as its maximum size and then placed centered.
|
|
|
|
// So the nextPlacement argument allows chaining. In this case, nextPlacement
|
|
|
|
// is Maximizing and it will call placeCentered().
|
|
|
|
place(c, area, Centered);
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
if (place_on == nullptr) {
|
2011-01-30 14:34:42 +00:00
|
|
|
// 'mains_count' is used because it doesn't include ignored mainwindows
|
|
|
|
if (mains_count != 1) {
|
|
|
|
place(c, area, Centered);
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
place_on = place_on2; // use the only window filtered together with 'mains_count'
|
|
|
|
}
|
|
|
|
if (place_on->isDesktop()) {
|
|
|
|
place(c, area, Centered);
|
2007-05-07 12:37:12 +00:00
|
|
|
return;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2019-09-27 10:01:10 +00:00
|
|
|
QRect geom = c->frameGeometry();
|
|
|
|
geom.moveCenter(place_on->frameGeometry().center());
|
2011-01-30 14:34:42 +00:00
|
|
|
c->move(geom.topLeft());
|
2007-04-29 17:35:43 +00:00
|
|
|
// get area again, because the mainwindow may be on different xinerama screen
|
2019-09-13 09:19:32 +00:00
|
|
|
const QRect placementArea = workspace()->clientArea(PlacementArea, c);
|
2019-09-12 14:52:47 +00:00
|
|
|
c->keepInArea(placementArea); // make sure it's kept inside workarea
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-12 14:52:47 +00:00
|
|
|
void Placement::placeMaximizing(AbstractClient *c, const QRect &area, Policy nextPlacement)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if (nextPlacement == Unknown)
|
2007-04-29 17:35:43 +00:00
|
|
|
nextPlacement = Smart;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (c->isMaximizable() && c->maxSize().width() >= area.width() && c->maxSize().height() >= area.height()) {
|
2013-05-08 11:55:20 +00:00
|
|
|
if (workspace()->clientArea(MaximizeArea, c) == area)
|
2014-12-02 12:49:08 +00:00
|
|
|
c->maximize(MaximizeFull);
|
2011-01-30 14:34:42 +00:00
|
|
|
else { // if the geometry doesn't match default maximize area (xinerama case?),
|
|
|
|
// it's probably better to use the given 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
|
|
|
c->moveResize(area);
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
} else {
|
|
|
|
c->resizeWithChecks(c->maxSize().boundedTo(area.size()));
|
|
|
|
place(c, area, nextPlacement);
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2012-11-22 10:07:33 +00:00
|
|
|
void Placement::cascadeDesktop()
|
|
|
|
{
|
|
|
|
Workspace *ws = Workspace::self();
|
2012-11-16 07:23:47 +00:00
|
|
|
const int desktop = VirtualDesktopManager::self()->current();
|
2012-11-22 10:07:33 +00:00
|
|
|
reinitCascading(desktop);
|
|
|
|
foreach (Toplevel *toplevel, ws->stackingOrder()) {
|
2017-06-19 15:56:15 +00:00
|
|
|
auto client = qobject_cast<AbstractClient*>(toplevel);
|
2012-11-22 10:07:33 +00:00
|
|
|
if (!client ||
|
|
|
|
(!client->isOnCurrentDesktop()) ||
|
|
|
|
(client->isMinimized()) ||
|
|
|
|
(client->isOnAllDesktops()) ||
|
|
|
|
(!client->isMovable()))
|
|
|
|
continue;
|
2019-09-12 15:21:59 +00:00
|
|
|
const QRect placementArea = workspace()->clientArea(PlacementArea, client);
|
|
|
|
placeCascaded(client, placementArea);
|
2012-11-22 10:07:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Placement::unclutterDesktop()
|
|
|
|
{
|
2017-06-19 15:56:15 +00:00
|
|
|
const auto &clients = Workspace::self()->allClientList();
|
2012-11-22 10:07:33 +00:00
|
|
|
for (int i = clients.size() - 1; i >= 0; i--) {
|
2017-06-19 15:56:15 +00:00
|
|
|
auto client = clients.at(i);
|
2012-11-22 10:07:33 +00:00
|
|
|
if ((!client->isOnCurrentDesktop()) ||
|
|
|
|
(client->isMinimized()) ||
|
|
|
|
(client->isOnAllDesktops()) ||
|
|
|
|
(!client->isMovable()))
|
|
|
|
continue;
|
2019-09-13 09:19:32 +00:00
|
|
|
const QRect placementArea = workspace()->clientArea(PlacementArea, client);
|
|
|
|
placeSmart(client, placementArea);
|
2012-11-22 10:07:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
#endif
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
const char* Placement::policyToString(Policy policy)
|
|
|
|
{
|
|
|
|
const char* const policies[] = {
|
|
|
|
"NoPlacement", "Default", "XXX should never see", "Random", "Smart", "Cascade", "Centered",
|
|
|
|
"ZeroCornered", "UnderMouse", "OnMainWindow", "Maximizing"
|
|
|
|
};
|
2019-08-31 14:28:37 +00:00
|
|
|
Q_ASSERT(policy < int(sizeof(policies) / sizeof(policies[ 0 ])));
|
2007-04-29 17:35:43 +00:00
|
|
|
return policies[ policy ];
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
#ifndef KCMRULES
|
|
|
|
|
|
|
|
// ********************
|
|
|
|
// Workspace
|
|
|
|
// ********************
|
|
|
|
|
2015-10-26 10:30:34 +00:00
|
|
|
void AbstractClient::packTo(int left, int top)
|
2013-04-15 18:43:50 +00:00
|
|
|
{
|
2020-04-02 16:18:01 +00:00
|
|
|
workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
|
2015-01-28 23:14:47 +00:00
|
|
|
|
2013-04-15 18:43:50 +00:00
|
|
|
const int oldScreen = screen();
|
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
|
|
|
move(QPoint(left, top));
|
2013-04-15 18:43:50 +00:00
|
|
|
if (screen() != oldScreen) {
|
|
|
|
workspace()->sendClientToScreen(this, screen()); // checks rule validity
|
|
|
|
if (maximizeMode() != MaximizeRestore)
|
|
|
|
checkWorkspacePosition();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-02 18:17:44 +00:00
|
|
|
/**
|
|
|
|
* Moves active window left until in bumps into another window or workarea edge.
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2007-04-29 17:35:43 +00:00
|
|
|
void Workspace::slotWindowPackLeft()
|
2011-01-30 14:34:42 +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 (active_client && active_client->isMovable()) {
|
|
|
|
const QRect geometry = active_client->moveResizeGeometry();
|
|
|
|
active_client->packTo(packPositionLeft(active_client, geometry.left(), true),
|
|
|
|
geometry.y());
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
void Workspace::slotWindowPackRight()
|
2011-01-30 14:34:42 +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 (active_client && active_client->isMovable()) {
|
|
|
|
const QRect geometry = active_client->moveResizeGeometry();
|
|
|
|
active_client->packTo(packPositionRight(active_client, geometry.right(), true) - geometry.width() + 1,
|
|
|
|
geometry.y());
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
void Workspace::slotWindowPackUp()
|
2011-01-30 14:34:42 +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 (active_client && active_client->isMovable()) {
|
|
|
|
const QRect geometry = active_client->moveResizeGeometry();
|
|
|
|
active_client->packTo(geometry.x(),
|
|
|
|
packPositionUp(active_client, geometry.top(), true));
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
void Workspace::slotWindowPackDown()
|
2011-01-30 14:34:42 +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 (active_client && active_client->isMovable()) {
|
|
|
|
const QRect geometry = active_client->moveResizeGeometry();
|
|
|
|
active_client->packTo(geometry.x(),
|
|
|
|
packPositionDown(active_client, geometry.bottom(), true) - geometry.height() + 1);
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
void Workspace::slotWindowGrowHorizontal()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (active_client)
|
2007-04-29 17:35:43 +00:00
|
|
|
active_client->growHorizontal();
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2015-10-26 14:49:03 +00:00
|
|
|
void AbstractClient::growHorizontal()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (!isResizable() || isShade())
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
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
|
|
|
QRect geom = moveResizeGeometry();
|
2011-01-30 14:34:42 +00:00
|
|
|
geom.setRight(workspace()->packPositionRight(this, geom.right(), true));
|
Refactor geometry constraints code
Summary:
Currently, there are a couple of issues with sizeForClientSize(). First
of all, we have a method called clientSizeToFrameSize() which does similar
thing except applying geometry constraints and checking window rules. The
other issue is that sizeForClientSize() is doing a bit too much, it checks
window rules, it applies a bunch of geometry constrains. Sometimes it
does not perform conversion between client sizes and frame sizes!
This change attempts to address those issues by replacing sizeForClientSize
with two similar methods and changing semantics of some methods of the
X11Client class.
The most significant difference between sizeForClientSize() and the new
methods is that neither constrainClientSize() nor constrainFrameSize()
check window rules. This is up to users of those methods. In many places,
we don't have to check window rules because we check isResizable(),
which returns false if the frame size is enforced by a window rule.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D26828
2020-02-12 10:38:40 +00:00
|
|
|
QSize adjsize = constrainFrameSize(geom.size(), SizeModeFixedW);
|
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 (moveResizeGeometry().size() == adjsize && geom.size() != adjsize && resizeIncrements().width() > 1) { // take care of size increments
|
2015-10-26 14:49:03 +00:00
|
|
|
int newright = workspace()->packPositionRight(this, geom.right() + resizeIncrements().width() - 1, true);
|
2007-04-29 17:35:43 +00:00
|
|
|
// check that it hasn't grown outside of the area, due to size increments
|
|
|
|
// TODO this may be wrong?
|
2011-01-30 14:34:42 +00:00
|
|
|
if (workspace()->clientArea(MovementArea,
|
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
|
|
|
QPoint((x() + newright) / 2, moveResizeGeometry().center().y()), desktop()).right() >= newright)
|
2011-01-30 14:34:42 +00:00
|
|
|
geom.setRight(newright);
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
Refactor geometry constraints code
Summary:
Currently, there are a couple of issues with sizeForClientSize(). First
of all, we have a method called clientSizeToFrameSize() which does similar
thing except applying geometry constraints and checking window rules. The
other issue is that sizeForClientSize() is doing a bit too much, it checks
window rules, it applies a bunch of geometry constrains. Sometimes it
does not perform conversion between client sizes and frame sizes!
This change attempts to address those issues by replacing sizeForClientSize
with two similar methods and changing semantics of some methods of the
X11Client class.
The most significant difference between sizeForClientSize() and the new
methods is that neither constrainClientSize() nor constrainFrameSize()
check window rules. This is up to users of those methods. In many places,
we don't have to check window rules because we check isResizable(),
which returns false if the frame size is enforced by a window rule.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D26828
2020-02-12 10:38:40 +00:00
|
|
|
geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedW));
|
|
|
|
geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedH));
|
2020-04-02 16:18:01 +00:00
|
|
|
workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
|
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(geom);
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
void Workspace::slotWindowShrinkHorizontal()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (active_client)
|
2007-04-29 17:35:43 +00:00
|
|
|
active_client->shrinkHorizontal();
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2015-10-26 14:49:03 +00:00
|
|
|
void AbstractClient::shrinkHorizontal()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (!isResizable() || isShade())
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
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
|
|
|
QRect geom = moveResizeGeometry();
|
2011-01-30 14:34:42 +00:00
|
|
|
geom.setRight(workspace()->packPositionLeft(this, geom.right(), false));
|
|
|
|
if (geom.width() <= 1)
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
Refactor geometry constraints code
Summary:
Currently, there are a couple of issues with sizeForClientSize(). First
of all, we have a method called clientSizeToFrameSize() which does similar
thing except applying geometry constraints and checking window rules. The
other issue is that sizeForClientSize() is doing a bit too much, it checks
window rules, it applies a bunch of geometry constrains. Sometimes it
does not perform conversion between client sizes and frame sizes!
This change attempts to address those issues by replacing sizeForClientSize
with two similar methods and changing semantics of some methods of the
X11Client class.
The most significant difference between sizeForClientSize() and the new
methods is that neither constrainClientSize() nor constrainFrameSize()
check window rules. This is up to users of those methods. In many places,
we don't have to check window rules because we check isResizable(),
which returns false if the frame size is enforced by a window rule.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D26828
2020-02-12 10:38:40 +00:00
|
|
|
geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedW));
|
2015-01-28 23:14:47 +00:00
|
|
|
if (geom.width() > 20) {
|
2020-04-02 16:18:01 +00:00
|
|
|
workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
|
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(geom);
|
2015-01-28 23:14:47 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
void Workspace::slotWindowGrowVertical()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (active_client)
|
2007-04-29 17:35:43 +00:00
|
|
|
active_client->growVertical();
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2015-10-26 14:49:03 +00:00
|
|
|
void AbstractClient::growVertical()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (!isResizable() || isShade())
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
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
|
|
|
QRect geom = moveResizeGeometry();
|
2011-01-30 14:34:42 +00:00
|
|
|
geom.setBottom(workspace()->packPositionDown(this, geom.bottom(), true));
|
Refactor geometry constraints code
Summary:
Currently, there are a couple of issues with sizeForClientSize(). First
of all, we have a method called clientSizeToFrameSize() which does similar
thing except applying geometry constraints and checking window rules. The
other issue is that sizeForClientSize() is doing a bit too much, it checks
window rules, it applies a bunch of geometry constrains. Sometimes it
does not perform conversion between client sizes and frame sizes!
This change attempts to address those issues by replacing sizeForClientSize
with two similar methods and changing semantics of some methods of the
X11Client class.
The most significant difference between sizeForClientSize() and the new
methods is that neither constrainClientSize() nor constrainFrameSize()
check window rules. This is up to users of those methods. In many places,
we don't have to check window rules because we check isResizable(),
which returns false if the frame size is enforced by a window rule.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D26828
2020-02-12 10:38:40 +00:00
|
|
|
QSize adjsize = constrainFrameSize(geom.size(), SizeModeFixedH);
|
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 (moveResizeGeometry().size() == adjsize && geom.size() != adjsize && resizeIncrements().height() > 1) { // take care of size increments
|
2015-10-26 14:49:03 +00:00
|
|
|
int newbottom = workspace()->packPositionDown(this, geom.bottom() + resizeIncrements().height() - 1, true);
|
2007-04-29 17:35:43 +00:00
|
|
|
// check that it hasn't grown outside of the area, due to size increments
|
2011-01-30 14:34:42 +00:00
|
|
|
if (workspace()->clientArea(MovementArea,
|
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
|
|
|
QPoint(moveResizeGeometry().center().x(), (y() + newbottom) / 2), desktop()).bottom() >= newbottom)
|
2011-01-30 14:34:42 +00:00
|
|
|
geom.setBottom(newbottom);
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
Refactor geometry constraints code
Summary:
Currently, there are a couple of issues with sizeForClientSize(). First
of all, we have a method called clientSizeToFrameSize() which does similar
thing except applying geometry constraints and checking window rules. The
other issue is that sizeForClientSize() is doing a bit too much, it checks
window rules, it applies a bunch of geometry constrains. Sometimes it
does not perform conversion between client sizes and frame sizes!
This change attempts to address those issues by replacing sizeForClientSize
with two similar methods and changing semantics of some methods of the
X11Client class.
The most significant difference between sizeForClientSize() and the new
methods is that neither constrainClientSize() nor constrainFrameSize()
check window rules. This is up to users of those methods. In many places,
we don't have to check window rules because we check isResizable(),
which returns false if the frame size is enforced by a window rule.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D26828
2020-02-12 10:38:40 +00:00
|
|
|
geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedH));
|
2020-04-02 16:18:01 +00:00
|
|
|
workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
|
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(geom);
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
void Workspace::slotWindowShrinkVertical()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (active_client)
|
2007-04-29 17:35:43 +00:00
|
|
|
active_client->shrinkVertical();
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2015-10-26 14:49:03 +00:00
|
|
|
void AbstractClient::shrinkVertical()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (!isResizable() || isShade())
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
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
|
|
|
QRect geom = moveResizeGeometry();
|
2011-01-30 14:34:42 +00:00
|
|
|
geom.setBottom(workspace()->packPositionUp(this, geom.bottom(), false));
|
|
|
|
if (geom.height() <= 1)
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
Refactor geometry constraints code
Summary:
Currently, there are a couple of issues with sizeForClientSize(). First
of all, we have a method called clientSizeToFrameSize() which does similar
thing except applying geometry constraints and checking window rules. The
other issue is that sizeForClientSize() is doing a bit too much, it checks
window rules, it applies a bunch of geometry constrains. Sometimes it
does not perform conversion between client sizes and frame sizes!
This change attempts to address those issues by replacing sizeForClientSize
with two similar methods and changing semantics of some methods of the
X11Client class.
The most significant difference between sizeForClientSize() and the new
methods is that neither constrainClientSize() nor constrainFrameSize()
check window rules. This is up to users of those methods. In many places,
we don't have to check window rules because we check isResizable(),
which returns false if the frame size is enforced by a window rule.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D26828
2020-02-12 10:38:40 +00:00
|
|
|
geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedH));
|
2015-01-28 23:14:47 +00:00
|
|
|
if (geom.height() > 20) {
|
2020-04-02 16:18:01 +00:00
|
|
|
workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
|
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(geom);
|
2015-01-28 23:14:47 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2017-07-18 19:12:37 +00:00
|
|
|
void Workspace::quickTileWindow(QuickTileMode mode)
|
2011-02-07 18:19:51 +00:00
|
|
|
{
|
|
|
|
if (!active_client) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-12 13:50:24 +00:00
|
|
|
// If the user invokes two of these commands in a one second period, try to
|
|
|
|
// combine them together to enable easy and intuitive corner tiling
|
|
|
|
#define FLAG(name) QuickTileMode(QuickTileFlag::name)
|
|
|
|
if (!m_quickTileCombineTimer->isActive()) {
|
|
|
|
m_quickTileCombineTimer->start(1000);
|
|
|
|
m_lastTilingMode = mode;
|
|
|
|
} else {
|
|
|
|
if (
|
|
|
|
( (m_lastTilingMode == FLAG(Left) || m_lastTilingMode == FLAG(Right)) && (mode == FLAG(Top) || mode == FLAG(Bottom)) )
|
|
|
|
||
|
|
|
|
( (m_lastTilingMode == FLAG(Top) || m_lastTilingMode == FLAG(Bottom)) && (mode == FLAG(Left) || mode == FLAG(Right)) )
|
|
|
|
#undef FLAG
|
|
|
|
) {
|
|
|
|
mode |= m_lastTilingMode;
|
|
|
|
}
|
|
|
|
m_quickTileCombineTimer->stop();
|
|
|
|
}
|
|
|
|
|
2017-07-18 19:12:37 +00:00
|
|
|
active_client->setQuickTileMode(mode, true);
|
2011-02-07 18:19:51 +00:00
|
|
|
}
|
|
|
|
|
2019-09-28 15:36:15 +00:00
|
|
|
int Workspace::packPositionLeft(const AbstractClient *client, int oldX, bool leftEdge) const
|
|
|
|
{
|
|
|
|
int newX = clientArea(MaximizeArea, client).left();
|
|
|
|
if (oldX <= newX) { // try another Xinerama screen
|
|
|
|
newX = clientArea(MaximizeArea,
|
|
|
|
QPoint(client->frameGeometry().left() - 1, client->frameGeometry().center().y()), client->desktop()).left();
|
|
|
|
}
|
|
|
|
if (client->titlebarPosition() != AbstractClient::PositionLeft) {
|
2019-09-28 15:37:58 +00:00
|
|
|
const int right = newX - client->frameMargins().left();
|
2019-09-28 15:36:15 +00:00
|
|
|
QRect frameGeometry = client->frameGeometry();
|
|
|
|
frameGeometry.moveRight(right);
|
|
|
|
if (screens()->intersecting(frameGeometry) < 2) {
|
|
|
|
newX = right;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (oldX <= newX) {
|
|
|
|
return oldX;
|
|
|
|
}
|
|
|
|
const int desktop = client->desktop() == 0 || client->isOnAllDesktops() ? VirtualDesktopManager::self()->current() : client->desktop();
|
2015-10-26 12:25:47 +00:00
|
|
|
for (auto it = m_allClients.constBegin(), end = m_allClients.constEnd(); it != end; ++it) {
|
2019-09-28 15:36:15 +00:00
|
|
|
if (isIrrelevant(*it, client, desktop)) {
|
2007-04-29 17:35:43 +00:00
|
|
|
continue;
|
2019-09-28 15:36:15 +00:00
|
|
|
}
|
|
|
|
const int x = leftEdge ? (*it)->frameGeometry().right() + 1 : (*it)->frameGeometry().left() - 1;
|
|
|
|
if (x > newX && x < oldX
|
|
|
|
&& !(client->frameGeometry().top() > (*it)->frameGeometry().bottom() // they overlap in Y direction
|
|
|
|
|| client->frameGeometry().bottom() < (*it)->frameGeometry().top())) {
|
|
|
|
newX = x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newX;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Workspace::packPositionRight(const AbstractClient *client, int oldX, bool rightEdge) const
|
|
|
|
{
|
|
|
|
int newX = clientArea(MaximizeArea, client).right();
|
|
|
|
if (oldX >= newX) { // try another Xinerama screen
|
|
|
|
newX = clientArea(MaximizeArea,
|
|
|
|
QPoint(client->frameGeometry().right() + 1, client->frameGeometry().center().y()), client->desktop()).right();
|
|
|
|
}
|
|
|
|
if (client->titlebarPosition() != AbstractClient::PositionRight) {
|
2019-09-28 15:37:58 +00:00
|
|
|
const int right = newX + client->frameMargins().right();
|
2019-09-28 15:36:15 +00:00
|
|
|
QRect frameGeometry = client->frameGeometry();
|
|
|
|
frameGeometry.moveRight(right);
|
|
|
|
if (screens()->intersecting(frameGeometry) < 2) {
|
|
|
|
newX = right;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (oldX >= newX) {
|
|
|
|
return oldX;
|
|
|
|
}
|
|
|
|
const int desktop = client->desktop() == 0 || client->isOnAllDesktops() ? VirtualDesktopManager::self()->current() : client->desktop();
|
2015-10-26 12:25:47 +00:00
|
|
|
for (auto it = m_allClients.constBegin(), end = m_allClients.constEnd(); it != end; ++it) {
|
2019-09-28 15:36:15 +00:00
|
|
|
if (isIrrelevant(*it, client, desktop)) {
|
2007-04-29 17:35:43 +00:00
|
|
|
continue;
|
2019-09-28 15:36:15 +00:00
|
|
|
}
|
|
|
|
const int x = rightEdge ? (*it)->frameGeometry().left() - 1 : (*it)->frameGeometry().right() + 1;
|
|
|
|
if (x < newX && x > oldX
|
|
|
|
&& !(client->frameGeometry().top() > (*it)->frameGeometry().bottom()
|
|
|
|
|| client->frameGeometry().bottom() < (*it)->frameGeometry().top())) {
|
|
|
|
newX = x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newX;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Workspace::packPositionUp(const AbstractClient *client, int oldY, bool topEdge) const
|
|
|
|
{
|
|
|
|
int newY = clientArea(MaximizeArea, client).top();
|
|
|
|
if (oldY <= newY) { // try another Xinerama screen
|
|
|
|
newY = clientArea(MaximizeArea,
|
|
|
|
QPoint(client->frameGeometry().center().x(), client->frameGeometry().top() - 1), client->desktop()).top();
|
|
|
|
}
|
|
|
|
if (client->titlebarPosition() != AbstractClient::PositionTop) {
|
2019-09-28 15:37:58 +00:00
|
|
|
const int top = newY - client->frameMargins().top();
|
2019-09-28 15:36:15 +00:00
|
|
|
QRect frameGeometry = client->frameGeometry();
|
|
|
|
frameGeometry.moveTop(top);
|
|
|
|
if (screens()->intersecting(frameGeometry) < 2) {
|
|
|
|
newY = top;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (oldY <= newY) {
|
|
|
|
return oldY;
|
|
|
|
}
|
|
|
|
const int desktop = client->desktop() == 0 || client->isOnAllDesktops() ? VirtualDesktopManager::self()->current() : client->desktop();
|
2015-10-26 12:25:47 +00:00
|
|
|
for (auto it = m_allClients.constBegin(), end = m_allClients.constEnd(); it != end; ++it) {
|
2019-09-28 15:36:15 +00:00
|
|
|
if (isIrrelevant(*it, client, desktop)) {
|
2007-04-29 17:35:43 +00:00
|
|
|
continue;
|
2019-09-28 15:36:15 +00:00
|
|
|
}
|
|
|
|
const int y = topEdge ? (*it)->frameGeometry().bottom() + 1 : (*it)->frameGeometry().top() - 1;
|
|
|
|
if (y > newY && y < oldY
|
|
|
|
&& !(client->frameGeometry().left() > (*it)->frameGeometry().right() // they overlap in X direction
|
|
|
|
|| client->frameGeometry().right() < (*it)->frameGeometry().left())) {
|
|
|
|
newY = y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newY;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Workspace::packPositionDown(const AbstractClient *client, int oldY, bool bottomEdge) const
|
|
|
|
{
|
|
|
|
int newY = clientArea(MaximizeArea, client).bottom();
|
|
|
|
if (oldY >= newY) { // try another Xinerama screen
|
|
|
|
newY = clientArea(MaximizeArea,
|
|
|
|
QPoint(client->frameGeometry().center().x(), client->frameGeometry().bottom() + 1), client->desktop()).bottom();
|
|
|
|
}
|
|
|
|
if (client->titlebarPosition() != AbstractClient::PositionBottom) {
|
2019-09-28 15:37:58 +00:00
|
|
|
const int bottom = newY + client->frameMargins().bottom();
|
2019-09-28 15:36:15 +00:00
|
|
|
QRect frameGeometry = client->frameGeometry();
|
|
|
|
frameGeometry.moveBottom(bottom);
|
|
|
|
if (screens()->intersecting(frameGeometry) < 2) {
|
|
|
|
newY = bottom;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (oldY >= newY) {
|
|
|
|
return oldY;
|
|
|
|
}
|
|
|
|
const int desktop = client->desktop() == 0 || client->isOnAllDesktops() ? VirtualDesktopManager::self()->current() : client->desktop();
|
2015-10-26 12:25:47 +00:00
|
|
|
for (auto it = m_allClients.constBegin(), end = m_allClients.constEnd(); it != end; ++it) {
|
2019-09-28 15:36:15 +00:00
|
|
|
if (isIrrelevant(*it, client, desktop)) {
|
2007-04-29 17:35:43 +00:00
|
|
|
continue;
|
2019-09-28 15:36:15 +00:00
|
|
|
}
|
|
|
|
const int y = bottomEdge ? (*it)->frameGeometry().top() - 1 : (*it)->frameGeometry().bottom() + 1;
|
|
|
|
if (y < newY && y > oldY
|
|
|
|
&& !(client->frameGeometry().left() > (*it)->frameGeometry().right()
|
|
|
|
|| client->frameGeometry().right() < (*it)->frameGeometry().left())) {
|
|
|
|
newY = y;
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2019-09-28 15:36:15 +00:00
|
|
|
return newY;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
} // namespace
|