Move struts logic to AbstractClient

master
Vlad Zahorodnii 2020-08-17 16:14:20 +03:00
parent 4b978bb403
commit a9fd5ac19f
8 changed files with 153 additions and 196 deletions

View File

@ -1446,6 +1446,22 @@ void AbstractClient::performMoveResize()
emit clientStepUserMovedResized(this, moveResizeGeom);
}
StrutRect AbstractClient::strutRect(StrutArea area) const
{
Q_UNUSED(area)
return StrutRect();
}
StrutRects AbstractClient::strutRects() const
{
StrutRects region;
region += strutRect(StrutAreaTop);
region += strutRect(StrutAreaRight);
region += strutRect(StrutAreaBottom);
region += strutRect(StrutAreaLeft);
return region;
}
bool AbstractClient::hasStrut() const
{
return false;

View File

@ -686,6 +686,8 @@ public:
return m_moveResize.cursor;
}
virtual StrutRect strutRect(StrutArea area) const;
StrutRects strutRects() const;
virtual bool hasStrut() const;
void setModal(bool modal);

View File

@ -1968,6 +1968,80 @@ void Workspace::saveOldScreenSizes()
oldscreensizes.append( screens()->geometry( i ));
}
/**
* Whether or not the window has a strut that expands through the invisible area of
* an xinerama setup where the monitors are not the same resolution.
*/
static bool hasOffscreenXineramaStrut(AbstractClient *client)
{
// Get strut as a QRegion
QRegion region;
region += client->strutRect(StrutAreaTop);
region += client->strutRect(StrutAreaRight);
region += client->strutRect(StrutAreaBottom);
region += client->strutRect(StrutAreaLeft);
// Remove all visible areas so that only the invisible remain
for (int i = 0; i < screens()->count(); i ++) {
region -= screens()->geometry(i);
}
// If there's anything left then we have an offscreen strut
return !region.isEmpty();
}
QRect Workspace::adjustClientArea(AbstractClient *client, const QRect &area) const
{
QRect adjustedArea = area;
QRect strutLeft = client->strutRect(StrutAreaLeft);
QRect strutRight = client->strutRect(StrutAreaRight);
QRect strutTop = client->strutRect(StrutAreaTop);
QRect strutBottom = client->strutRect(StrutAreaBottom);
QRect screenArea = clientArea(ScreenArea, client);
// HACK: workarea handling is not xinerama aware, so if this strut
// reserves place at a xinerama edge that's inside the virtual screen,
// ignore the strut for workspace setting.
if (area == QRect(QPoint(0, 0), screens()->displaySize())) {
if (strutLeft.left() < screenArea.left()) {
strutLeft = QRect();
}
if (strutRight.right() > screenArea.right()) {
strutRight = QRect();
}
if (strutTop.top() < screenArea.top()) {
strutTop = QRect();
}
if (strutBottom.bottom() < screenArea.bottom()) {
strutBottom = QRect();
}
}
// Handle struts at xinerama edges that are inside the virtual screen.
// They're given in virtual screen coordinates, make them affect only
// their xinerama screen.
strutLeft.setLeft(qMax(strutLeft.left(), screenArea.left()));
strutRight.setRight(qMin(strutRight.right(), screenArea.right()));
strutTop.setTop(qMax(strutTop.top(), screenArea.top()));
strutBottom.setBottom(qMin(strutBottom.bottom(), screenArea.bottom()));
if (strutLeft.intersects(area)) {
adjustedArea.setLeft(strutLeft.right() + 1);
}
if (strutRight.intersects(area)) {
adjustedArea.setRight(strutRight.left() - 1);
}
if (strutTop.intersects(area)) {
adjustedArea.setTop(strutTop.bottom() + 1);
}
if (strutBottom.intersects(area)) {
adjustedArea.setBottom(strutBottom.top() - 1);
}
return adjustedArea;
}
/**
* Updates the current client areas according to the current clients.
*
@ -2007,10 +2081,11 @@ void Workspace::updateClientArea(bool force)
iS ++)
new_sareas[ i ][ iS ] = screens[ iS ];
}
for (auto it = clients.constBegin(); it != clients.constEnd(); ++it) {
if (!(*it)->hasStrut())
for (AbstractClient *client : qAsConst(m_allClients)) {
if (!client->hasStrut()) {
continue;
QRect r = (*it)->adjustedClientArea(desktopArea, desktopArea);
}
QRect r = adjustClientArea(client, desktopArea);
// sanity check that a strut doesn't exclude a complete screen geometry
// this is a violation to EWMH, as KWin just ignores the strut
for (int i = 0; i < Screens::self()->count(); i++) {
@ -2020,8 +2095,8 @@ void Workspace::updateClientArea(bool force)
break;
}
}
StrutRects strutRegion = (*it)->strutRects();
const QRect clientsScreenRect = KWin::screens()->geometry((*it)->screen());
StrutRects strutRegion = client->strutRects();
const QRect clientsScreenRect = KWin::screens()->geometry(client->screen());
for (auto strut = strutRegion.begin(); strut != strutRegion.end(); strut++) {
*strut = StrutRect((*strut).intersected(clientsScreenRect), (*strut).area());
}
@ -2032,20 +2107,20 @@ void Workspace::updateClientArea(bool force)
// This goes against the EWMH description of the work area but it is a toss up between
// having unusable sections of the screen (Which can be quite large with newer monitors)
// or having some content appear offscreen (Relatively rare compared to other).
bool hasOffscreenXineramaStrut = (*it)->hasOffscreenXineramaStrut();
bool hasOffscreenStrut = hasOffscreenXineramaStrut(client);
if ((*it)->isOnAllDesktops()) {
if (client->isOnAllDesktops()) {
for (int i = 1;
i <= numberOfDesktops;
++i) {
if (!hasOffscreenXineramaStrut)
if (!hasOffscreenStrut)
new_wareas[ i ] = new_wareas[ i ].intersected(r);
new_rmoveareas[ i ] += strutRegion;
for (int iS = 0;
iS < nscreens;
iS ++) {
const auto geo = new_sareas[ i ][ iS ].intersected(
(*it)->adjustedClientArea(desktopArea, screens[ iS ]));
adjustClientArea(client, screens[ iS ]));
// ignore the geometry if it results in the screen getting removed completely
if (!geo.isEmpty()) {
new_sareas[ i ][ iS ] = geo;
@ -2053,92 +2128,22 @@ void Workspace::updateClientArea(bool force)
}
}
} else {
if (!hasOffscreenXineramaStrut)
new_wareas[(*it)->desktop()] = new_wareas[(*it)->desktop()].intersected(r);
new_rmoveareas[(*it)->desktop()] += strutRegion;
if (!hasOffscreenStrut)
new_wareas[client->desktop()] = new_wareas[client->desktop()].intersected(r);
new_rmoveareas[client->desktop()] += strutRegion;
for (int iS = 0;
iS < nscreens;
iS ++) {
// qDebug() << "adjusting new_sarea: " << screens[ iS ];
const auto geo = new_sareas[(*it)->desktop()][ iS ].intersected(
(*it)->adjustedClientArea(desktopArea, screens[ iS ]));
const auto geo = new_sareas[client->desktop()][ iS ].intersected(
adjustClientArea(client, screens[ iS ]));
// ignore the geometry if it results in the screen getting removed completely
if (!geo.isEmpty()) {
new_sareas[(*it)->desktop()][ iS ] = geo;
new_sareas[client->desktop()][ iS ] = geo;
}
}
}
}
if (waylandServer()) {
auto updateStrutsForWaylandClient = [&] (AbstractClient *c) {
// assuming that only docks have "struts" and that all docks have a strut
if (!c->hasStrut()) {
return;
}
auto margins = [c] (const QRect &geometry) {
QMargins margins;
if (!geometry.intersects(c->frameGeometry())) {
return margins;
}
// figure out which areas of the overall screen setup it borders
const bool left = c->frameGeometry().left() == geometry.left();
const bool right = c->frameGeometry().right() == geometry.right();
const bool top = c->frameGeometry().top() == geometry.top();
const bool bottom = c->frameGeometry().bottom() == geometry.bottom();
const bool horizontal = c->frameGeometry().width() >= c->frameGeometry().height();
if (left && ((!top && !bottom) || !horizontal)) {
margins.setLeft(c->frameGeometry().width());
}
if (right && ((!top && !bottom) || !horizontal)) {
margins.setRight(c->frameGeometry().width());
}
if (top && ((!left && !right) || horizontal)) {
margins.setTop(c->frameGeometry().height());
}
if (bottom && ((!left && !right) || horizontal)) {
margins.setBottom(c->frameGeometry().height());
}
return margins;
};
auto marginsToStrutArea = [] (const QMargins &margins) {
if (margins.left() != 0) {
return StrutAreaLeft;
}
if (margins.right() != 0) {
return StrutAreaRight;
}
if (margins.top() != 0) {
return StrutAreaTop;
}
if (margins.bottom() != 0) {
return StrutAreaBottom;
}
return StrutAreaInvalid;
};
const auto strut = margins(KWin::screens()->geometry(c->screen()));
const StrutRects strutRegion = StrutRects{StrutRect(c->frameGeometry(), marginsToStrutArea(strut))};
QRect r = desktopArea - margins(KWin::screens()->geometry());
if (c->isOnAllDesktops()) {
for (int i = 1; i <= numberOfDesktops; ++i) {
new_wareas[ i ] = new_wareas[ i ].intersected(r);
for (int iS = 0; iS < nscreens; ++iS) {
new_sareas[ i ][ iS ] = new_sareas[ i ][ iS ].intersected(screens[iS] - margins(screens[iS]));
}
new_rmoveareas[ i ] += strutRegion;
}
} else {
new_wareas[c->desktop()] = new_wareas[c->desktop()].intersected(r);
for (int iS = 0; iS < nscreens; iS++) {
new_sareas[c->desktop()][ iS ] = new_sareas[c->desktop()][ iS ].intersected(screens[iS] - margins(screens[iS]));
}
new_rmoveareas[ c->desktop() ] += strutRegion;
}
};
const auto clients = waylandServer()->clients();
for (auto c : clients) {
updateStrutsForWaylandClient(c);
}
}
#if 0
for (int i = 1;
i <= numberOfDesktops();

View File

@ -183,6 +183,7 @@ public:
*/
void setMoveResizeClient(AbstractClient* c);
QRect adjustClientArea(AbstractClient *client, const QRect &area) const;
QPoint adjustClientPosition(AbstractClient* c, QPoint pos, bool unrestricted, double snapAdjust = 1.0);
QRect adjustClientSize(AbstractClient* c, QRect moveResizeGeom, int mode);
void raiseClient(AbstractClient* c, bool nogroup = false);

View File

@ -4798,80 +4798,6 @@ void X11Client::doPerformMoveResize()
} // (leads to sync request races in some clients)
}
/**
* Returns \a area with the client's strut taken into account.
*
* Used from Workspace in updateClientArea.
*/
// TODO move to Workspace?
QRect X11Client::adjustedClientArea(const QRect &desktopArea, const QRect& area) const
{
QRect r = area;
NETExtendedStrut str = strut();
QRect stareaL = QRect(
0,
str . left_start,
str . left_width,
str . left_end - str . left_start + 1);
QRect stareaR = QRect(
desktopArea . right() - str . right_width + 1,
str . right_start,
str . right_width,
str . right_end - str . right_start + 1);
QRect stareaT = QRect(
str . top_start,
0,
str . top_end - str . top_start + 1,
str . top_width);
QRect stareaB = QRect(
str . bottom_start,
desktopArea . bottom() - str . bottom_width + 1,
str . bottom_end - str . bottom_start + 1,
str . bottom_width);
QRect screenarea = workspace()->clientArea(ScreenArea, this);
// HACK: workarea handling is not xinerama aware, so if this strut
// reserves place at a xinerama edge that's inside the virtual screen,
// ignore the strut for workspace setting.
if (area == QRect(QPoint(0, 0), screens()->displaySize())) {
if (stareaL.left() < screenarea.left())
stareaL = QRect();
if (stareaR.right() > screenarea.right())
stareaR = QRect();
if (stareaT.top() < screenarea.top())
stareaT = QRect();
if (stareaB.bottom() < screenarea.bottom())
stareaB = QRect();
}
// Handle struts at xinerama edges that are inside the virtual screen.
// They're given in virtual screen coordinates, make them affect only
// their xinerama screen.
stareaL.setLeft(qMax(stareaL.left(), screenarea.left()));
stareaR.setRight(qMin(stareaR.right(), screenarea.right()));
stareaT.setTop(qMax(stareaT.top(), screenarea.top()));
stareaB.setBottom(qMin(stareaB.bottom(), screenarea.bottom()));
if (stareaL . intersects(area)) {
// qDebug() << "Moving left of: " << r << " to " << stareaL.right() + 1;
r . setLeft(stareaL . right() + 1);
}
if (stareaR . intersects(area)) {
// qDebug() << "Moving right of: " << r << " to " << stareaR.left() - 1;
r . setRight(stareaR . left() - 1);
}
if (stareaT . intersects(area)) {
// qDebug() << "Moving top of: " << r << " to " << stareaT.bottom() + 1;
r . setTop(stareaT . bottom() + 1);
}
if (stareaB . intersects(area)) {
// qDebug() << "Moving bottom of: " << r << " to " << stareaB.top() - 1;
r . setBottom(stareaB . top() - 1);
}
return r;
}
NETExtendedStrut X11Client::strut() const
{
NETExtendedStrut ext = info->extendedStrut();
@ -4944,16 +4870,6 @@ StrutRect X11Client::strutRect(StrutArea area) const
return StrutRect(); // Null rect
}
StrutRects X11Client::strutRects() const
{
StrutRects region;
region += strutRect(StrutAreaTop);
region += strutRect(StrutAreaRight);
region += strutRect(StrutAreaBottom);
region += strutRect(StrutAreaLeft);
return region;
}
bool X11Client::hasStrut() const
{
NETExtendedStrut ext = strut();
@ -4962,23 +4878,6 @@ bool X11Client::hasStrut() const
return true;
}
bool X11Client::hasOffscreenXineramaStrut() const
{
// Get strut as a QRegion
QRegion region;
region += strutRect(StrutAreaTop);
region += strutRect(StrutAreaRight);
region += strutRect(StrutAreaBottom);
region += strutRect(StrutAreaLeft);
// Remove all visible areas so that only the invisible remain
for (int i = 0; i < screens()->count(); i ++)
region -= screens()->geometry(i);
// If there's anything left then we have an offscreen strut
return !region.isEmpty();
}
void X11Client::applyWindowRules()
{
AbstractClient::applyWindowRules();

View File

@ -187,8 +187,6 @@ public:
bool providesContextHelp() const override;
QRect adjustedClientArea(const QRect& desktop, const QRect& area) const;
xcb_colormap_t colormap() const;
/// Updates visibility depending on being shaded, virtual desktop, etc.
@ -237,8 +235,8 @@ public:
void killWindow() override;
void showContextHelp() override;
void checkActiveModal();
StrutRect strutRect(StrutArea area) const;
StrutRects strutRects() const;
StrutRect strutRect(StrutArea area) const override;
bool hasStrut() const override;
/**
@ -249,12 +247,6 @@ public:
*/
void setClientShown(bool shown) override;
/**
* Whether or not the window has a strut that expands through the invisible area of
* an xinerama setup where the monitors are not the same resolution.
*/
bool hasOffscreenXineramaStrut() const;
QRect transparentRect() const override;
bool isClientSideDecorated() const;

View File

@ -851,6 +851,47 @@ bool XdgToplevelClient::supportsWindowRules() const
return !m_plasmaShellSurface;
}
StrutRect XdgToplevelClient::strutRect(StrutArea area) const
{
if (!hasStrut()) {
return StrutRect();
}
const QRect windowRect = frameGeometry();
const QRect outputRect = screens()->geometry(screen());
const bool left = windowRect.left() == outputRect.left();
const bool right = windowRect.right() == outputRect.right();
const bool top = windowRect.top() == outputRect.top();
const bool bottom = windowRect.bottom() == outputRect.bottom();
const bool horizontal = width() >= height();
switch (area) {
case StrutAreaTop:
if (top && ((!left && !right) || horizontal)) {
return StrutRect(windowRect, StrutAreaTop);
}
return StrutRect();
case StrutAreaRight:
if (right && ((!top && !bottom) || !horizontal)) {
return StrutRect(windowRect, StrutAreaRight);
}
return StrutRect();
case StrutAreaBottom:
if (bottom && ((!left && !right) || horizontal)) {
return StrutRect(windowRect, StrutAreaBottom);
}
return StrutRect();
case StrutAreaLeft:
if (left && ((!top && !bottom) || !horizontal)) {
return StrutRect(windowRect, StrutAreaLeft);
}
return StrutRect();
default:
return StrutRect();
}
}
bool XdgToplevelClient::hasStrut() const
{
if (!isShown(true)) {

View File

@ -152,6 +152,7 @@ public:
bool takeFocus() override;
bool wantsInput() const override;
bool dockWantsInput() const override;
StrutRect strutRect(StrutArea area) const override;
bool hasStrut() const override;
void showOnScreenEdge() override;
void setFullScreen(bool set, bool user) override;