diff --git a/abstract_client.h b/abstract_client.h --- a/abstract_client.h +++ b/abstract_client.h @@ -633,19 +633,9 @@ SizeModeFixedH, ///< Try not to affect height SizeModeMax ///< Try not to make it larger in either direction }; - /** - * Calculates the appropriate frame size for the given client size @p wsize. - * - * @p wsize is adapted according to the window's size hints (minimum, maximum and incremental size changes). - * - * Default implementation returns the passed in @p wsize. - */ - virtual QSize sizeForClientSize(const QSize &wsize, SizeMode mode = SizeModeAny, bool noframe = false) const; - /** - * Adjust the frame size @p frame according to the window's size hints. - */ - QSize adjustedSize(const QSize&, SizeMode mode = SizeModeAny) const; + virtual QSize constrainClientSize(const QSize &size, SizeMode mode = SizeModeAny) const; + QSize constrainFrameSize(const QSize &size, SizeMode mode = SizeModeAny) const; QSize adjustedSize() const; /** diff --git a/abstract_client.cpp b/abstract_client.cpp --- a/abstract_client.cpp +++ b/abstract_client.cpp @@ -1182,7 +1182,7 @@ } // Always obey size hints, even when in "unrestricted" mode - QSize size = adjustedSize(moveResizeGeometry().size(), sizeMode); + QSize size = constrainFrameSize(moveResizeGeometry().size(), sizeMode); // the new topleft and bottomright corners (after checking size constrains), if they'll be needed topleft = QPoint(moveResizeGeometry().right() - size.width() + 1, moveResizeGeometry().bottom() - size.height() + 1); bottomright = QPoint(moveResizeGeometry().left() + size.width() - 1, moveResizeGeometry().top() + size.height() - 1); @@ -1209,7 +1209,7 @@ setMoveResizeGeometry(workspace()->clientArea(FullScreenArea, screen, 0)); else { QRect moveResizeGeom = workspace()->clientArea(MaximizeArea, screen, 0); - QSize adjSize = adjustedSize(moveResizeGeom.size(), SizeModeMax); + QSize adjSize = constrainFrameSize(moveResizeGeom.size(), SizeModeMax); if (adjSize != moveResizeGeom.size()) { QRect r(moveResizeGeom); moveResizeGeom.setSize(adjSize); @@ -1851,13 +1851,6 @@ BORDER(Top) #undef BORDER -QSize AbstractClient::sizeForClientSize(const QSize &wsize, SizeMode mode, bool noframe) const -{ - Q_UNUSED(mode) - Q_UNUSED(noframe) - return wsize + QSize(borderLeft() + borderRight(), borderTop() + borderBottom()); -} - void AbstractClient::addRepaintDuringGeometryUpdates() { const QRect deco_rect = visibleRect(); @@ -3075,7 +3068,7 @@ checkOffscreenPosition(&newGeom, screenArea); // Obey size hints. TODO: We really should make sure it stays in the right place if (!isShade()) - newGeom.setSize(adjustedSize(newGeom.size())); + newGeom.setSize(constrainFrameSize(newGeom.size())); if (newGeom != frameGeometry()) setFrameGeometry(newGeom); @@ -3095,21 +3088,46 @@ } } -QSize AbstractClient::adjustedSize(const QSize& frame, SizeMode mode) const +/** + * Returns the appropriate frame size for the current client size. + * + * This is equivalent to clientSizeToFrameSize(constrainClientSize(clientSize())). + */ +QSize AbstractClient::adjustedSize() const +{ + return clientSizeToFrameSize(constrainClientSize(clientSize())); +} + +/** + * Constrains the client size @p size according to a set of the window's size hints. + */ +QSize AbstractClient::constrainClientSize(const QSize &size, SizeMode mode) const { - // first, get the window size for the given frame size s - QSize wsize = frameSizeToClientSize(frame); - if (wsize.isEmpty()) - wsize = QSize(qMax(wsize.width(), 1), qMax(wsize.height(), 1)); + Q_UNUSED(mode) - return sizeForClientSize(wsize, mode, false); + int width = size.width(); + int height = size.height(); + + // When user is resizing the window, the move resize geometry may have negative width or + // height. In which case, we need to set negative dimensions to reasonable values. + if (width < 1) { + width = 1; + } + if (height < 1) { + height = 1; + } + + return QSize(width, height); } -// this helper returns proper size even if the window is shaded -// see also the comment in X11Client::setGeometry() -QSize AbstractClient::adjustedSize() const +/** + * Constrains the frame size @p size according to a set of the window's size hints. + */ +QSize AbstractClient::constrainFrameSize(const QSize &size, SizeMode mode) const { - return sizeForClientSize(clientSize()); + const QSize unconstrainedClientSize = frameSizeToClientSize(size); + const QSize constrainedClientSize = constrainClientSize(unconstrainedClientSize, mode); + return clientSizeToFrameSize(constrainedClientSize); } /** diff --git a/autotests/integration/xdgshellclient_test.cpp b/autotests/integration/xdgshellclient_test.cpp --- a/autotests/integration/xdgshellclient_test.cpp +++ b/autotests/integration/xdgshellclient_test.cpp @@ -458,7 +458,7 @@ QVERIFY(!c->isFullScreen()); QCOMPARE(c->clientSize(), QSize(100, 50)); QCOMPARE(c->isDecorated(), decoMode == ServerSideDecoration::Mode::Server); - QCOMPARE(c->sizeForClientSize(c->clientSize()), c->frameGeometry().size()); + QCOMPARE(c->clientSizeToFrameSize(c->clientSize()), c->frameGeometry().size()); QSignalSpy fullscreenChangedSpy(c, &XdgShellClient::fullScreenChanged); QVERIFY(fullscreenChangedSpy.isValid()); QSignalSpy frameGeometryChangedSpy(c, &XdgShellClient::frameGeometryChanged); diff --git a/internal_client.cpp b/internal_client.cpp --- a/internal_client.cpp +++ b/internal_client.cpp @@ -436,7 +436,7 @@ const QSize bufferSize = fbo->size() / bufferScale(); - commitGeometry(QRect(pos(), sizeForClientSize(bufferSize))); + commitGeometry(QRect(pos(), clientSizeToFrameSize(bufferSize))); markAsMapped(); if (m_internalFBO != fbo) { @@ -455,7 +455,7 @@ const QSize bufferSize = image.size() / bufferScale(); - commitGeometry(QRect(pos(), sizeForClientSize(bufferSize))); + commitGeometry(QRect(pos(), clientSizeToFrameSize(bufferSize))); markAsMapped(); if (m_internalImage.size() != image.size()) { diff --git a/placement.cpp b/placement.cpp --- a/placement.cpp +++ b/placement.cpp @@ -748,17 +748,17 @@ return; QRect geom = frameGeometry(); geom.setRight(workspace()->packPositionRight(this, geom.right(), true)); - QSize adjsize = adjustedSize(geom.size(), SizeModeFixedW); + QSize adjsize = constrainFrameSize(geom.size(), SizeModeFixedW); if (frameGeometry().size() == adjsize && geom.size() != adjsize && resizeIncrements().width() > 1) { // take care of size increments int newright = workspace()->packPositionRight(this, geom.right() + resizeIncrements().width() - 1, true); // check that it hasn't grown outside of the area, due to size increments // TODO this may be wrong? if (workspace()->clientArea(MovementArea, QPoint((x() + newright) / 2, frameGeometry().center().y()), desktop()).right() >= newright) geom.setRight(newright); } - geom.setSize(adjustedSize(geom.size(), SizeModeFixedW)); - geom.setSize(adjustedSize(geom.size(), SizeModeFixedH)); + geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedW)); + geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedH)); workspace()->updateFocusMousePosition(Cursor::pos()); // may cause leave event; setFrameGeometry(geom); } @@ -777,7 +777,7 @@ geom.setRight(workspace()->packPositionLeft(this, geom.right(), false)); if (geom.width() <= 1) return; - geom.setSize(adjustedSize(geom.size(), SizeModeFixedW)); + geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedW)); if (geom.width() > 20) { workspace()->updateFocusMousePosition(Cursor::pos()); // may cause leave event; setFrameGeometry(geom); @@ -796,15 +796,15 @@ return; QRect geom = frameGeometry(); geom.setBottom(workspace()->packPositionDown(this, geom.bottom(), true)); - QSize adjsize = adjustedSize(geom.size(), SizeModeFixedH); + QSize adjsize = constrainFrameSize(geom.size(), SizeModeFixedH); if (frameGeometry().size() == adjsize && geom.size() != adjsize && resizeIncrements().height() > 1) { // take care of size increments int newbottom = workspace()->packPositionDown(this, geom.bottom() + resizeIncrements().height() - 1, true); // check that it hasn't grown outside of the area, due to size increments if (workspace()->clientArea(MovementArea, QPoint(frameGeometry().center().x(), (y() + newbottom) / 2), desktop()).bottom() >= newbottom) geom.setBottom(newbottom); } - geom.setSize(adjustedSize(geom.size(), SizeModeFixedH)); + geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedH)); workspace()->updateFocusMousePosition(Cursor::pos()); // may cause leave event; setFrameGeometry(geom); } @@ -824,7 +824,7 @@ geom.setBottom(workspace()->packPositionUp(this, geom.bottom(), false)); if (geom.height() <= 1) return; - geom.setSize(adjustedSize(geom.size(), SizeModeFixedH)); + geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedH)); if (geom.height() > 20) { workspace()->updateFocusMousePosition(Cursor::pos()); // may cause leave event; setFrameGeometry(geom); diff --git a/rules.cpp b/rules.cpp --- a/rules.cpp +++ b/rules.cpp @@ -905,7 +905,7 @@ // Placement - does need explicit update, just like some others below // Geometry : setGeometry() doesn't check rules auto client_rules = rules(); - QRect orig_geom = QRect(pos(), sizeForClientSize(clientSize())); // handle shading + QRect orig_geom = QRect(pos(), adjustedSize()); // handle shading QRect geom = client_rules->checkGeometry(orig_geom); if (geom != orig_geom) setFrameGeometry(geom); diff --git a/x11client.h b/x11client.h --- a/x11client.h +++ b/x11client.h @@ -197,7 +197,7 @@ void resizeWithChecks(int w, int h, ForceGeometry_t force = NormalGeometrySet) override; void resizeWithChecks(int w, int h, xcb_gravity_t gravity, ForceGeometry_t force = NormalGeometrySet); void resizeWithChecks(const QSize& s, xcb_gravity_t gravity, ForceGeometry_t force = NormalGeometrySet); - QSize sizeForClientSize(const QSize&, SizeMode mode = SizeModeAny, bool noframe = false) const override; + QSize constrainClientSize(const QSize &size, SizeMode mode = SizeModeAny) const override; bool providesContextHelp() const override; diff --git a/x11client.cpp b/x11client.cpp --- a/x11client.cpp +++ b/x11client.cpp @@ -624,7 +624,8 @@ updateDecoration(false); // Also gravitates // TODO: Is CentralGravity right here, when resizing is done after gravitating? - plainResize(rules()->checkSize(sizeForClientSize(geom.size()), !isMapped)); + const QSize constrainedClientSize = constrainClientSize(geom.size()); + plainResize(rules()->checkSize(clientSizeToFrameSize(constrainedClientSize), !isMapped)); QPoint forced_pos = rules()->checkPosition(invalidPoint, !isMapped); if (forced_pos != invalidPoint) { @@ -1069,7 +1070,7 @@ // move(calculateGravitation(true)); // move(calculateGravitation(false)); QRect oldgeom = frameGeometry(); - plainResize(sizeForClientSize(clientSize()), ForceGeometrySet); + plainResize(adjustedSize(), ForceGeometrySet); if (!isShade()) checkWorkspacePosition(oldgeom); emit geometryShapeChanged(this, oldgeom); @@ -1081,7 +1082,7 @@ setDecoration(decoration); move(calculateGravitation(false)); - plainResize(sizeForClientSize(clientSize()), ForceGeometrySet); + plainResize(adjustedSize(), ForceGeometrySet); if (Compositor::compositing()) { discardWindowPixmap(); } @@ -1094,7 +1095,7 @@ if (isDecorated()) { QPoint grav = calculateGravitation(true); AbstractClient::destroyDecoration(); - plainResize(sizeForClientSize(clientSize()), ForceGeometrySet); + plainResize(adjustedSize(), ForceGeometrySet); move(grav); if (compositing()) discardWindowPixmap(); @@ -1252,8 +1253,9 @@ } if (rules()->checkStrictGeometry(true)) { // check geometry constraints (rule to obey is set) - const QRect fsarea = workspace()->clientArea(FullScreenArea, this); - if (sizeForClientSize(fsarea.size(), SizeModeAny, true) != fsarea.size()) { + const QRect fullScreenArea = workspace()->clientArea(FullScreenArea, this); + const QSize constrainedClientSize = constrainClientSize(fullScreenArea.size()); + if (rules()->checkSize(constrainedClientSize) != fullScreenArea.size()) { return false; // the app wouldn't fit exactly fullscreen geometry due to its strict geometry requirements } } @@ -1501,7 +1503,7 @@ addWorkspaceRepaint(visibleRect()); // Shade shade_geometry_change = true; - QSize s(sizeForClientSize(QSize(clientSize()))); + QSize s(adjustedSize()); s.setHeight(borderTop() + borderBottom()); m_wrapper.selectInput(ClientWinMask); // Avoid getting UnmapNotify m_wrapper.unmap(); @@ -1522,7 +1524,7 @@ shade_geometry_change = true; if (decoratedClient()) decoratedClient()->signalShadeChange(); - QSize s(sizeForClientSize(clientSize())); + QSize s(adjustedSize()); shade_geometry_change = false; plainResize(s); setGeometryRestore(frameGeometry()); @@ -3594,20 +3596,11 @@ } } -/** - * Calculate the appropriate frame size for the given client size \a - * wsize. - * - * \a wsize is adapted according to the window's size hints (minimum, - * maximum and incremental size changes). - */ -QSize X11Client::sizeForClientSize(const QSize& wsize, SizeMode mode, bool noframe) const +QSize X11Client::constrainClientSize(const QSize &size, SizeMode mode) const { - int w = wsize.width(); - int h = wsize.height(); - if (w < 1 || h < 1) { - qCWarning(KWIN_CORE) << "sizeForClientSize() with empty size!" ; - } + int w = size.width(); + int h = size.height(); + if (w < 1) w = 1; if (h < 1) h = 1; @@ -3763,11 +3756,7 @@ h = h1; } - QSize size(w, h); - if (!noframe) { - size = clientSizeToFrameSize(size); - } - return rules()->checkSize(size); + return QSize(w, h); } /** @@ -3991,16 +3980,18 @@ if (value_mask & XCB_CONFIG_WINDOW_HEIGHT) { nh = rh; } - QSize ns = sizeForClientSize(QSize(nw, nh)); // enforces size if needed + const QSize requestedClientSize = constrainClientSize(QSize(nw, nh)); + QSize requestedFrameSize = clientSizeToFrameSize(requestedClientSize); + requestedFrameSize = rules()->checkSize(requestedFrameSize); new_pos = rules()->checkPosition(new_pos); - int newScreen = screens()->number(QRect(new_pos, ns).center()); + int newScreen = screens()->number(QRect(new_pos, requestedFrameSize).center()); if (newScreen != rules()->checkScreen(newScreen)) return; // not allowed by rule QRect origClientGeometry = m_clientGeometry; GeometryUpdatesBlocker blocker(this); move(new_pos); - plainResize(ns); + plainResize(requestedFrameSize); QRect area = workspace()->clientArea(WorkArea, this); if (!from_tool && (!isSpecialWindow() || isToolbar()) && !isFullScreen() && area.contains(origClientGeometry)) @@ -4023,12 +4014,15 @@ if (value_mask & XCB_CONFIG_WINDOW_HEIGHT) { nh = rh; } - QSize ns = sizeForClientSize(QSize(nw, nh)); - if (ns != size()) { // don't restore if some app sets its own size again + const QSize requestedClientSize = constrainClientSize(QSize(nw, nh)); + QSize requestedFrameSize = clientSizeToFrameSize(requestedClientSize); + requestedFrameSize = rules()->checkSize(requestedFrameSize); + + if (requestedFrameSize != size()) { // don't restore if some app sets its own size again QRect origClientGeometry = m_clientGeometry; GeometryUpdatesBlocker blocker(this); - resizeWithChecks(ns, xcb_gravity_t(gravity)); + resizeWithChecks(requestedFrameSize, xcb_gravity_t(gravity)); if (!from_tool && (!isSpecialWindow() || isToolbar()) && !isFullScreen()) { // try to keep the window in its xinerama screen if possible, // if that fails at least keep it visible somewhere @@ -4063,7 +4057,7 @@ w = area.width(); if (h > area.height()) h = area.height(); - QSize tmp = adjustedSize(QSize(w, h)); // checks size constraints, including min/max size + QSize tmp = constrainFrameSize(QSize(w, h)); // checks size constraints, including min/max size w = tmp.width(); h = tmp.height(); if (gravity == 0) { @@ -4410,7 +4404,7 @@ // save sizes for restoring, if maximalizing QSize sz; if (isShade()) - sz = sizeForClientSize(clientSize()); + sz = adjustedSize(); else sz = size(); @@ -4473,16 +4467,16 @@ if (old_mode & MaximizeHorizontal) { // actually restoring from MaximizeFull if (geometryRestore().width() == 0 || !clientArea.contains(geometryRestore().center())) { // needs placement - plainResize(adjustedSize(QSize(width() * 2 / 3, clientArea.height()), SizeModeFixedH), geom_mode); + plainResize(constrainFrameSize(QSize(width() * 2 / 3, clientArea.height()), SizeModeFixedH), geom_mode); Placement::self()->placeSmart(this, clientArea); } else { setFrameGeometry(QRect(QPoint(geometryRestore().x(), clientArea.top()), - adjustedSize(QSize(geometryRestore().width(), clientArea.height()), SizeModeFixedH)), geom_mode); + constrainFrameSize(QSize(geometryRestore().width(), clientArea.height()), SizeModeFixedH)), geom_mode); } } else { QRect r(x(), clientArea.top(), width(), clientArea.height()); r.setTopLeft(rules()->checkPosition(r.topLeft())); - r.setSize(adjustedSize(r.size(), SizeModeFixedH)); + r.setSize(constrainFrameSize(r.size(), SizeModeFixedH)); setFrameGeometry(r, geom_mode); } info->setState(NET::MaxVert, NET::Max); @@ -4493,16 +4487,16 @@ if (old_mode & MaximizeVertical) { // actually restoring from MaximizeFull if (geometryRestore().height() == 0 || !clientArea.contains(geometryRestore().center())) { // needs placement - plainResize(adjustedSize(QSize(clientArea.width(), height() * 2 / 3), SizeModeFixedW), geom_mode); + plainResize(constrainFrameSize(QSize(clientArea.width(), height() * 2 / 3), SizeModeFixedW), geom_mode); Placement::self()->placeSmart(this, clientArea); } else { setFrameGeometry(QRect(QPoint(clientArea.left(), geometryRestore().y()), - adjustedSize(QSize(clientArea.width(), geometryRestore().height()), SizeModeFixedW)), geom_mode); + constrainFrameSize(QSize(clientArea.width(), geometryRestore().height()), SizeModeFixedW)), geom_mode); } } else { QRect r(clientArea.left(), y(), clientArea.width(), height()); r.setTopLeft(rules()->checkPosition(r.topLeft())); - r.setSize(adjustedSize(r.size(), SizeModeFixedW)); + r.setSize(constrainFrameSize(r.size(), SizeModeFixedW)); setFrameGeometry(r, geom_mode); } info->setState(NET::MaxHoriz, NET::Max); @@ -4528,7 +4522,7 @@ if (geometryRestore().height() > 0) { s.setHeight(geometryRestore().height()); } - plainResize(adjustedSize(s)); + plainResize(constrainFrameSize(s)); Placement::self()->placeSmart(this, clientArea); restore = frameGeometry(); if (geometryRestore().width() > 0) { @@ -4540,7 +4534,7 @@ setGeometryRestore(restore); // relevant for mouse pos calculation, bug #298646 } if (m_geometryHints.hasAspect()) { - restore.setSize(adjustedSize(restore.size(), SizeModeAny)); + restore.setSize(constrainFrameSize(restore.size(), SizeModeAny)); } setFrameGeometry(restore, geom_mode); if (!clientArea.contains(geometryRestore().center())) { // Not restoring to the same screen @@ -4554,7 +4548,7 @@ case MaximizeFull: { QRect r(clientArea); r.setTopLeft(rules()->checkPosition(r.topLeft())); - r.setSize(adjustedSize(r.size(), SizeModeMax)); + r.setSize(constrainFrameSize(r.size(), SizeModeMax)); if (r.size() != clientArea.size()) { // to avoid off-by-one errors... if (isElectricBorderMaximizing() && r.width() < clientArea.width()) { r.moveLeft(qMax(clientArea.left(), Cursor::pos().x() - r.width()/2)); @@ -4663,7 +4657,7 @@ } else { Q_ASSERT(!geom_fs_restore.isNull()); const int currentScreen = screen(); - setFrameGeometry(QRect(geom_fs_restore.topLeft(), adjustedSize(geom_fs_restore.size()))); + setFrameGeometry(QRect(geom_fs_restore.topLeft(), constrainFrameSize(geom_fs_restore.size()))); if(currentScreen != screen()) { workspace()->sendClientToScreen(this, currentScreen); } diff --git a/xdgshellclient.cpp b/xdgshellclient.cpp --- a/xdgshellclient.cpp +++ b/xdgshellclient.cpp @@ -192,7 +192,7 @@ if (supportsWindowRules()) { setupWindowRules(false); - const QRect originalGeometry = QRect(pos(), sizeForClientSize(clientSize())); + const QRect originalGeometry = frameGeometry(); const QRect ruledGeometry = rules()->checkGeometry(originalGeometry, true); if (originalGeometry != ruledGeometry) { setFrameGeometry(ruledGeometry); @@ -942,7 +942,7 @@ } else { if (m_geomFsRestore.isValid()) { int currentScreen = screen(); - setFrameGeometry(QRect(m_geomFsRestore.topLeft(), adjustedSize(m_geomFsRestore.size()))); + setFrameGeometry(QRect(m_geomFsRestore.topLeft(), constrainFrameSize(m_geomFsRestore.size()))); if( currentScreen != screen()) workspace()->sendClientToScreen( this, currentScreen ); } else {