diff --git a/abstract_client.cpp b/abstract_client.cpp index 278ca58f2..41124881d 100644 --- a/abstract_client.cpp +++ b/abstract_client.cpp @@ -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; diff --git a/abstract_client.h b/abstract_client.h index cc0d787f1..0035dc378 100644 --- a/abstract_client.h +++ b/abstract_client.h @@ -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); diff --git a/workspace.cpp b/workspace.cpp index 31056bbb0..50467d3c3 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -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(); diff --git a/workspace.h b/workspace.h index 831292431..489d7bae4 100644 --- a/workspace.h +++ b/workspace.h @@ -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); diff --git a/x11client.cpp b/x11client.cpp index 387672684..814237dad 100644 --- a/x11client.cpp +++ b/x11client.cpp @@ -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(); diff --git a/x11client.h b/x11client.h index 03ce2892b..ba40f4885 100644 --- a/x11client.h +++ b/x11client.h @@ -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; diff --git a/xdgshellclient.cpp b/xdgshellclient.cpp index d10706aea..f8bcdb0d9 100644 --- a/xdgshellclient.cpp +++ b/xdgshellclient.cpp @@ -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)) { diff --git a/xdgshellclient.h b/xdgshellclient.h index df56220a8..8f5b4c299 100644 --- a/xdgshellclient.h +++ b/xdgshellclient.h @@ -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;