x11window: Disable strict geometry placement by default in wayland

Strict geometry disables certain user activites; full screen requests
and only sending configure events at sizes the client claims to support.

This was added as a workaround for xterm in 19 years ago. It's a client
side bug as applications can still choose to ignore the configure event,
so kwin shouldn't have to sanitise them in advance. xterm seems to have
fixed it's bug, and pragmatically we know not all window managers
perform these checks so most clients should not be relying on it.

On Wayland this additional check is proving problematic, the handling of
scaling especially fractional scaling is hitting cases where it's better
to always ask the client to do what kwin wants.

Tests that refer to sizeIncrements are dropped as they are only used in
the strict geometry passes which is being obsoleted. Resizing in general
is still tested.

BUG: 481456
This commit is contained in:
David Edmundson 2024-02-26 10:32:49 +00:00
parent 024db60ccd
commit 32be54b19d
2 changed files with 11 additions and 174 deletions

View file

@ -35,8 +35,6 @@ private Q_SLOTS:
void testMinimumSize();
void testMaximumSize();
void testResizeIncrements();
void testResizeIncrementsNoBaseSize();
void testTrimCaption_data();
void testTrimCaption();
void testFullscreenLayerWithActiveWaylandWindow();
@ -290,175 +288,6 @@ void X11WindowTest::testMaximumSize()
c.reset();
}
void X11WindowTest::testResizeIncrements()
{
// This test verifies that the resize increments constraint is correctly applied.
QFETCH_GLOBAL(qreal, scale);
kwinApp()->setXwaylandScale(scale);
// Create an xcb window.
Test::XcbConnectionPtr c = Test::createX11Connection();
QVERIFY(!xcb_connection_has_error(c.get()));
const QRect windowGeometry(0, 0, 100, 200);
xcb_window_t windowId = xcb_generate_id(c.get());
xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
windowGeometry.x(),
windowGeometry.y(),
windowGeometry.width(),
windowGeometry.height(),
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
xcb_size_hints_t hints;
memset(&hints, 0, sizeof(hints));
xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
xcb_icccm_size_hints_set_base_size(&hints, windowGeometry.width(), windowGeometry.height());
xcb_icccm_size_hints_set_resize_inc(&hints, 3, 5);
xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
xcb_map_window(c.get(), windowId);
xcb_flush(c.get());
QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
QVERIFY(windowCreatedSpy.wait());
X11Window *window = windowCreatedSpy.last().first().value<X11Window *>();
QVERIFY(window);
QVERIFY(window->isDecorated());
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
// Begin resize.
QCOMPARE(workspace()->moveResizeWindow(), nullptr);
QVERIFY(!window->isInteractiveResize());
workspace()->slotWindowResize();
QCOMPARE(workspace()->moveResizeWindow(), window);
QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
QVERIFY(window->isInteractiveResize());
const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
window->keyPressEvent(Qt::Key_Right);
window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QVERIFY(frameGeometryChangedSpy.wait());
// 100 + 8 logical pixels, rounded to resize increments. This will differ on scale
const qreal horizontalResizeInc = 3 / scale;
const qreal verticalResizeInc = 5 / scale;
const qreal expectedHorizontalResizeInc = std::floor(8. / horizontalResizeInc) * horizontalResizeInc;
const qreal expectedVerticalResizeInc = std::floor(8. / verticalResizeInc) * verticalResizeInc;
QCOMPARE(window->clientSize(), QSizeF(100, 200) / scale + QSizeF(expectedHorizontalResizeInc, 0));
window->keyPressEvent(Qt::Key_Down);
window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 8));
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->clientSize(), QSize(100, 200) / scale + QSizeF(expectedHorizontalResizeInc, expectedVerticalResizeInc));
// Finish the resize operation.
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 0);
window->keyPressEvent(Qt::Key_Enter);
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
QCOMPARE(workspace()->moveResizeWindow(), nullptr);
QVERIFY(!window->isInteractiveResize());
// Destroy the window.
QSignalSpy windowClosedSpy(window, &X11Window::closed);
xcb_unmap_window(c.get(), windowId);
xcb_destroy_window(c.get(), windowId);
xcb_flush(c.get());
QVERIFY(windowClosedSpy.wait());
c.reset();
}
void X11WindowTest::testResizeIncrementsNoBaseSize()
{
QFETCH_GLOBAL(qreal, scale);
kwinApp()->setXwaylandScale(scale);
// Create an xcb window.
Test::XcbConnectionPtr c = Test::createX11Connection();
QVERIFY(!xcb_connection_has_error(c.get()));
const QRect windowGeometry(0, 0, 100, 200);
xcb_window_t windowId = xcb_generate_id(c.get());
xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
windowGeometry.x(),
windowGeometry.y(),
windowGeometry.width(),
windowGeometry.height(),
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
xcb_size_hints_t hints;
memset(&hints, 0, sizeof(hints));
xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
xcb_icccm_size_hints_set_min_size(&hints, windowGeometry.width(), windowGeometry.height());
xcb_icccm_size_hints_set_resize_inc(&hints, 3, 5);
xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
xcb_map_window(c.get(), windowId);
xcb_flush(c.get());
QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
QVERIFY(windowCreatedSpy.wait());
X11Window *window = windowCreatedSpy.last().first().value<X11Window *>();
QVERIFY(window);
QVERIFY(window->isDecorated());
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
// Begin resize.
QCOMPARE(workspace()->moveResizeWindow(), nullptr);
QVERIFY(!window->isInteractiveResize());
workspace()->slotWindowResize();
QCOMPARE(workspace()->moveResizeWindow(), window);
QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
QVERIFY(window->isInteractiveResize());
const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
window->keyPressEvent(Qt::Key_Right);
window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QVERIFY(frameGeometryChangedSpy.wait());
// 100 + 8 pixels, rounded to resize increments. This will differ on scale
const qreal horizontalResizeInc = 3 / scale;
const qreal verticalResizeInc = 5 / scale;
const qreal expectedHorizontalResizeInc = std::floor(8. / horizontalResizeInc) * horizontalResizeInc;
const qreal expectedVerticalResizeInc = std::floor(8. / verticalResizeInc) * verticalResizeInc;
QCOMPARE(window->clientSize(), QSizeF(100, 200) / scale + QSizeF(expectedHorizontalResizeInc, 0));
window->keyPressEvent(Qt::Key_Down);
window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 8));
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->clientSize(), QSizeF(100, 200) / scale + QSizeF(expectedHorizontalResizeInc, expectedVerticalResizeInc));
// Finish the resize operation.
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 0);
window->keyPressEvent(Qt::Key_Enter);
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
QCOMPARE(workspace()->moveResizeWindow(), nullptr);
QVERIFY(!window->isInteractiveResize());
// Destroy the window.
QSignalSpy windowClosedSpy(window, &X11Window::closed);
xcb_unmap_window(c.get(), windowId);
xcb_destroy_window(c.get(), windowId);
xcb_flush(c.get());
QVERIFY(windowClosedSpy.wait());
c.reset();
}
void X11WindowTest::testTrimCaption_data()
{
QFETCH_GLOBAL(qreal, scale);

View file

@ -1490,7 +1490,8 @@ bool X11Window::isFullScreenable() const
if (!rules()->checkFullScreen(true)) {
return false;
}
if (rules()->checkStrictGeometry(true)) {
const bool isX11Mode = kwinApp()->operationMode() == Application::OperationModeX11;
if (rules()->checkStrictGeometry(isX11Mode)) {
// check geometry constraints (rule to obey is set)
const QRectF fullScreenArea = workspace()->clientArea(FullScreenArea, this);
const QSizeF constrainedClientSize = constrainClientSize(fullScreenArea.size());
@ -3693,7 +3694,8 @@ QSizeF X11Window::constrainClientSize(const QSizeF &size, SizeMode mode) const
w = std::max(min_size.width(), w);
h = std::max(min_size.height(), h);
if (!rules()->checkStrictGeometry(!isFullScreen())) {
const bool isX11Mode = kwinApp()->operationMode() == Application::OperationModeX11;
if (!rules()->checkStrictGeometry(!isFullScreen() && isX11Mode)) {
// Disobey increments and aspect by explicit rule.
return QSizeF(w, h);
}
@ -3878,6 +3880,10 @@ QSizeF X11Window::maxSize() const
QSizeF X11Window::basicUnit() const
{
const bool isX11Mode = kwinApp()->operationMode() == Application::OperationModeX11;
if (!isX11Mode) {
return QSize(1, 1);
}
return m_geometryHints.resizeIncrements();
}
@ -4445,9 +4451,11 @@ void X11Window::maximize(MaximizeMode mode)
// if the client insist on a fix aspect ratio, we check whether the maximizing will get us
// out of screen bounds and take that as a "full maximization with aspect check" then
const bool isX11Mode = kwinApp()->operationMode() == Application::OperationModeX11;
if (m_geometryHints.hasAspect() && // fixed aspect
(mode == MaximizeVertical || mode == MaximizeHorizontal) && // ondimensional maximization
rules()->checkStrictGeometry(true)) { // obey aspect
rules()->checkStrictGeometry(isX11Mode)) { // obey aspect
const QSize minAspect = m_geometryHints.minAspect();
const QSize maxAspect = m_geometryHints.maxAspect();
if (mode == MaximizeVertical || (old_mode & MaximizeVertical)) {