autotests: Add _NET_WM_STATE_MODAL tests
This commit is contained in:
parent
3ebe34bc3f
commit
fb6ce3707a
5 changed files with 298 additions and 2 deletions
|
@ -100,6 +100,12 @@ private Q_SLOTS:
|
|||
void testCaptionWmName();
|
||||
void testActivateFocusedWindow();
|
||||
void testReentrantMoveResize();
|
||||
void testModal();
|
||||
void testGroupModal();
|
||||
void testCloseModal();
|
||||
void testCloseInactiveModal();
|
||||
void testCloseGroupModal();
|
||||
void testCloseInactiveGroupModal();
|
||||
};
|
||||
|
||||
void X11WindowTest::initTestCase_data()
|
||||
|
@ -2498,5 +2504,282 @@ void X11WindowTest::testReentrantMoveResize()
|
|||
QVERIFY(Test::waitForWindowClosed(window));
|
||||
}
|
||||
|
||||
void X11WindowTest::testModal()
|
||||
{
|
||||
// Create a parent and a child windows.
|
||||
Test::XcbConnectionPtr c = Test::createX11Connection();
|
||||
QVERIFY(!xcb_connection_has_error(c.get()));
|
||||
X11Window *parent = createWindow(c.get(), QRect(0, 0, 100, 200));
|
||||
X11Window *child = createWindow(c.get(), QRect(0, 0, 100, 200), [&c, &parent](xcb_window_t windowId) {
|
||||
xcb_icccm_set_wm_transient_for(c.get(), windowId, parent->window());
|
||||
});
|
||||
QVERIFY(!child->isModal());
|
||||
QCOMPARE(child->transientFor(), parent);
|
||||
|
||||
// Set modal state.
|
||||
{
|
||||
NETWinInfo info(c.get(), child->window(), kwinApp()->x11RootWindow(), NET::WMState, NET::Properties2());
|
||||
info.setState(NET::Modal, NET::Modal);
|
||||
xcb_flush(c.get());
|
||||
}
|
||||
QSignalSpy modalChangedSpy(child, &Window::modalChanged);
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(child->isModal());
|
||||
|
||||
// Unset modal state.
|
||||
{
|
||||
NETWinInfo info(c.get(), child->window(), kwinApp()->x11RootWindow(), NET::WMState, NET::Properties2());
|
||||
info.setState(NET::State(), NET::Modal);
|
||||
xcb_flush(c.get());
|
||||
}
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(!child->isModal());
|
||||
|
||||
// Set modal state and try to activate the parent window, it should not succeed.
|
||||
{
|
||||
NETWinInfo info(c.get(), child->window(), kwinApp()->x11RootWindow(), NET::WMState, NET::Properties2());
|
||||
info.setState(NET::Modal, NET::Modal);
|
||||
xcb_flush(c.get());
|
||||
}
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(child->isModal());
|
||||
workspace()->activateWindow(parent);
|
||||
QCOMPARE(workspace()->activeWindow(), child);
|
||||
|
||||
// It should be okay to activate an unrelated window.
|
||||
Test::XcbConnectionPtr c1 = Test::createX11Connection();
|
||||
QVERIFY(!xcb_connection_has_error(c.get()));
|
||||
X11Window *unrelated = createWindow(c1.get(), QRect(0, 0, 100, 200));
|
||||
QCOMPARE(workspace()->activeWindow(), unrelated);
|
||||
}
|
||||
|
||||
void X11WindowTest::testGroupModal()
|
||||
{
|
||||
// This test verifies that a dialog can be modal to the window group.
|
||||
|
||||
// Create the leader, a follower and a dialog window.
|
||||
Test::XcbConnectionPtr c = Test::createX11Connection();
|
||||
QVERIFY(!xcb_connection_has_error(c.get()));
|
||||
X11Window *leader = createWindow(c.get(), QRect(0, 0, 100, 200), [&c](xcb_window_t windowId) {
|
||||
xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &windowId);
|
||||
});
|
||||
X11Window *follower = createWindow(c.get(), QRect(0, 0, 100, 200), [&c, &leader](xcb_window_t windowId) {
|
||||
const xcb_window_t leaderId = leader->window();
|
||||
xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &leaderId);
|
||||
});
|
||||
X11Window *dialog = createWindow(c.get(), QRect(0, 0, 100, 200), [&c, &leader](xcb_window_t windowId) {
|
||||
const xcb_window_t leaderId = leader->window();
|
||||
xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &leaderId);
|
||||
xcb_icccm_set_wm_transient_for(c.get(), windowId, kwinApp()->x11RootWindow());
|
||||
});
|
||||
QVERIFY(dialog->isTransient());
|
||||
QVERIFY(leader->hasTransient(dialog, true));
|
||||
QVERIFY(follower->hasTransient(dialog, true));
|
||||
|
||||
// Set modal state.
|
||||
{
|
||||
NETWinInfo info(c.get(), dialog->window(), kwinApp()->x11RootWindow(), NET::WMState, NET::Properties2());
|
||||
info.setState(NET::Modal, NET::Modal);
|
||||
xcb_flush(c.get());
|
||||
}
|
||||
QSignalSpy modalChangedSpy(dialog, &Window::modalChanged);
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(dialog->isModal());
|
||||
|
||||
// Unset modal state.
|
||||
{
|
||||
NETWinInfo info(c.get(), dialog->window(), kwinApp()->x11RootWindow(), NET::WMState, NET::Properties2());
|
||||
info.setState(NET::State(), NET::Modal);
|
||||
xcb_flush(c.get());
|
||||
}
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(!dialog->isModal());
|
||||
|
||||
// Set modal state and try to activate other windows in the group, it should not succeed.
|
||||
{
|
||||
NETWinInfo info(c.get(), dialog->window(), kwinApp()->x11RootWindow(), NET::WMState, NET::Properties2());
|
||||
info.setState(NET::Modal, NET::Modal);
|
||||
xcb_flush(c.get());
|
||||
}
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(dialog->isModal());
|
||||
workspace()->activateWindow(leader);
|
||||
QCOMPARE(workspace()->activeWindow(), dialog);
|
||||
workspace()->activateWindow(follower);
|
||||
QCOMPARE(workspace()->activeWindow(), dialog);
|
||||
|
||||
// It should be okay to activate an unrelated window.
|
||||
Test::XcbConnectionPtr c1 = Test::createX11Connection();
|
||||
QVERIFY(!xcb_connection_has_error(c.get()));
|
||||
X11Window *unrelated = createWindow(c1.get(), QRect(0, 0, 100, 200));
|
||||
QCOMPARE(workspace()->activeWindow(), unrelated);
|
||||
}
|
||||
|
||||
void X11WindowTest::testCloseModal()
|
||||
{
|
||||
// This test verifies that the parent window will be activated when an active modal dialog is closed.
|
||||
|
||||
// Create a parent and a child windows.
|
||||
Test::XcbConnectionPtr c = Test::createX11Connection();
|
||||
QVERIFY(!xcb_connection_has_error(c.get()));
|
||||
X11Window *parent = createWindow(c.get(), QRect(0, 0, 100, 200));
|
||||
X11Window *child = createWindow(c.get(), QRect(0, 0, 100, 200), [&c, &parent](xcb_window_t windowId) {
|
||||
xcb_icccm_set_wm_transient_for(c.get(), windowId, parent->window());
|
||||
});
|
||||
QVERIFY(!child->isModal());
|
||||
QCOMPARE(child->transientFor(), parent);
|
||||
|
||||
// Set modal state.
|
||||
{
|
||||
NETWinInfo info(c.get(), child->window(), kwinApp()->x11RootWindow(), NET::WMState, NET::Properties2());
|
||||
info.setState(NET::Modal, NET::Modal);
|
||||
xcb_flush(c.get());
|
||||
}
|
||||
QSignalSpy modalChangedSpy(child, &Window::modalChanged);
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(child->isModal());
|
||||
QCOMPARE(workspace()->activeWindow(), child);
|
||||
|
||||
// Close the child.
|
||||
QSignalSpy childClosedSpy(child, &Window::closed);
|
||||
xcb_unmap_window(c.get(), child->window());
|
||||
xcb_destroy_window(c.get(), child->window());
|
||||
xcb_flush(c.get());
|
||||
QVERIFY(childClosedSpy.wait());
|
||||
QCOMPARE(workspace()->activeWindow(), parent);
|
||||
}
|
||||
|
||||
void X11WindowTest::testCloseInactiveModal()
|
||||
{
|
||||
// This test verifies that the parent window will not be activated when an inactive modal dialog is closed.
|
||||
|
||||
// Create a parent and a child windows.
|
||||
Test::XcbConnectionPtr c = Test::createX11Connection();
|
||||
QVERIFY(!xcb_connection_has_error(c.get()));
|
||||
X11Window *parent = createWindow(c.get(), QRect(0, 0, 100, 200));
|
||||
X11Window *child = createWindow(c.get(), QRect(0, 0, 100, 200), [&c, &parent](xcb_window_t windowId) {
|
||||
xcb_icccm_set_wm_transient_for(c.get(), windowId, parent->window());
|
||||
});
|
||||
QVERIFY(!child->isModal());
|
||||
QCOMPARE(child->transientFor(), parent);
|
||||
|
||||
// Set modal state.
|
||||
{
|
||||
NETWinInfo info(c.get(), child->window(), kwinApp()->x11RootWindow(), NET::WMState, NET::Properties2());
|
||||
info.setState(NET::Modal, NET::Modal);
|
||||
xcb_flush(c.get());
|
||||
}
|
||||
QSignalSpy modalChangedSpy(child, &Window::modalChanged);
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(child->isModal());
|
||||
QCOMPARE(workspace()->activeWindow(), child);
|
||||
|
||||
// Show another window.
|
||||
Test::XcbConnectionPtr c1 = Test::createX11Connection();
|
||||
QVERIFY(!xcb_connection_has_error(c.get()));
|
||||
X11Window *unrelated = createWindow(c1.get(), QRect(0, 0, 100, 200));
|
||||
QCOMPARE(workspace()->activeWindow(), unrelated);
|
||||
|
||||
// Close the child.
|
||||
QSignalSpy childClosedSpy(child, &Window::closed);
|
||||
xcb_unmap_window(c.get(), child->window());
|
||||
xcb_destroy_window(c.get(), child->window());
|
||||
xcb_flush(c.get());
|
||||
QVERIFY(childClosedSpy.wait());
|
||||
QCOMPARE(workspace()->activeWindow(), unrelated);
|
||||
}
|
||||
|
||||
void X11WindowTest::testCloseGroupModal()
|
||||
{
|
||||
// This test verifies that when an active modal group dialog is closed, the focus will be passed to one of its main windows.
|
||||
|
||||
// Create the leader, a follower and a dialog window.
|
||||
Test::XcbConnectionPtr c = Test::createX11Connection();
|
||||
QVERIFY(!xcb_connection_has_error(c.get()));
|
||||
X11Window *leader = createWindow(c.get(), QRect(0, 0, 100, 200), [&c](xcb_window_t windowId) {
|
||||
xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &windowId);
|
||||
});
|
||||
X11Window *follower = createWindow(c.get(), QRect(0, 0, 100, 200), [&c, &leader](xcb_window_t windowId) {
|
||||
const xcb_window_t leaderId = leader->window();
|
||||
xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &leaderId);
|
||||
});
|
||||
X11Window *dialog = createWindow(c.get(), QRect(0, 0, 100, 200), [&c, &leader](xcb_window_t windowId) {
|
||||
const xcb_window_t leaderId = leader->window();
|
||||
xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &leaderId);
|
||||
xcb_icccm_set_wm_transient_for(c.get(), windowId, kwinApp()->x11RootWindow());
|
||||
});
|
||||
QVERIFY(dialog->isTransient());
|
||||
QVERIFY(leader->hasTransient(dialog, true));
|
||||
QVERIFY(follower->hasTransient(dialog, true));
|
||||
|
||||
// Set modal state.
|
||||
{
|
||||
NETWinInfo info(c.get(), dialog->window(), kwinApp()->x11RootWindow(), NET::WMState, NET::Properties2());
|
||||
info.setState(NET::Modal, NET::Modal);
|
||||
xcb_flush(c.get());
|
||||
}
|
||||
QSignalSpy modalChangedSpy(dialog, &Window::modalChanged);
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(dialog->isModal());
|
||||
QCOMPARE(workspace()->activeWindow(), dialog);
|
||||
|
||||
// Close the dialog.
|
||||
QSignalSpy dialogClosedSpy(dialog, &Window::closed);
|
||||
xcb_unmap_window(c.get(), dialog->window());
|
||||
xcb_destroy_window(c.get(), dialog->window());
|
||||
xcb_flush(c.get());
|
||||
QVERIFY(dialogClosedSpy.wait());
|
||||
QVERIFY(workspace()->activeWindow() == leader || workspace()->activeWindow() == follower);
|
||||
}
|
||||
|
||||
void X11WindowTest::testCloseInactiveGroupModal()
|
||||
{
|
||||
// This test verifies that when an inactive modal group dialog is closed, the focus will not be passed to one of its main windows.
|
||||
|
||||
// Create the leader, a follower and a dialog window.
|
||||
Test::XcbConnectionPtr c = Test::createX11Connection();
|
||||
QVERIFY(!xcb_connection_has_error(c.get()));
|
||||
X11Window *leader = createWindow(c.get(), QRect(0, 0, 100, 200), [&c](xcb_window_t windowId) {
|
||||
xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &windowId);
|
||||
});
|
||||
X11Window *follower = createWindow(c.get(), QRect(0, 0, 100, 200), [&c, &leader](xcb_window_t windowId) {
|
||||
const xcb_window_t leaderId = leader->window();
|
||||
xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &leaderId);
|
||||
});
|
||||
X11Window *dialog = createWindow(c.get(), QRect(0, 0, 100, 200), [&c, &leader](xcb_window_t windowId) {
|
||||
const xcb_window_t leaderId = leader->window();
|
||||
xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &leaderId);
|
||||
xcb_icccm_set_wm_transient_for(c.get(), windowId, kwinApp()->x11RootWindow());
|
||||
});
|
||||
QVERIFY(dialog->isTransient());
|
||||
QVERIFY(leader->hasTransient(dialog, true));
|
||||
QVERIFY(follower->hasTransient(dialog, true));
|
||||
|
||||
// Set modal state.
|
||||
{
|
||||
NETWinInfo info(c.get(), dialog->window(), kwinApp()->x11RootWindow(), NET::WMState, NET::Properties2());
|
||||
info.setState(NET::Modal, NET::Modal);
|
||||
xcb_flush(c.get());
|
||||
}
|
||||
QSignalSpy modalChangedSpy(dialog, &Window::modalChanged);
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(dialog->isModal());
|
||||
QCOMPARE(workspace()->activeWindow(), dialog);
|
||||
|
||||
// Show another window.
|
||||
Test::XcbConnectionPtr c1 = Test::createX11Connection();
|
||||
QVERIFY(!xcb_connection_has_error(c.get()));
|
||||
X11Window *unrelated = createWindow(c1.get(), QRect(0, 0, 100, 200));
|
||||
QCOMPARE(workspace()->activeWindow(), unrelated);
|
||||
|
||||
// Close the dialog.
|
||||
QSignalSpy dialogClosedSpy(dialog, &Window::closed);
|
||||
xcb_unmap_window(c.get(), dialog->window());
|
||||
xcb_destroy_window(c.get(), dialog->window());
|
||||
xcb_flush(c.get());
|
||||
QVERIFY(dialogClosedSpy.wait());
|
||||
QCOMPARE(workspace()->activeWindow(), unrelated);
|
||||
}
|
||||
|
||||
WAYLANDTEST_MAIN(X11WindowTest)
|
||||
#include "x11_window_test.moc"
|
||||
|
|
|
@ -2266,6 +2266,7 @@ void Window::setModal(bool m)
|
|||
return;
|
||||
}
|
||||
m_modal = m;
|
||||
doSetModal();
|
||||
Q_EMIT modalChanged();
|
||||
// Changing modality for a mapped window is weird (?)
|
||||
// _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG
|
||||
|
@ -4321,6 +4322,10 @@ void Window::doSetSuspended()
|
|||
{
|
||||
}
|
||||
|
||||
void Window::doSetModal()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include "moc_window.cpp"
|
||||
|
|
|
@ -1520,6 +1520,7 @@ protected:
|
|||
virtual void doSetHidden();
|
||||
virtual void doSetHiddenByShowDesktop();
|
||||
virtual void doSetSuspended();
|
||||
virtual void doSetModal();
|
||||
|
||||
void setupWindowManagementInterface();
|
||||
void destroyWindowManagementInterface();
|
||||
|
|
|
@ -418,7 +418,6 @@ void X11Window::releaseWindow(bool on_shutdown)
|
|||
// and repareting to root an atomic operation (https://lists.kde.org/?l=kde-devel&m=116448102901184&w=2)
|
||||
grabXServer();
|
||||
exportMappingState(XCB_ICCCM_WM_STATE_WITHDRAWN);
|
||||
setModal(false); // Otherwise its mainwindow wouldn't get focus
|
||||
if (!on_shutdown) {
|
||||
workspace()->activateNextWindow(this);
|
||||
}
|
||||
|
@ -492,7 +491,6 @@ void X11Window::destroyWindow()
|
|||
}
|
||||
finishWindowRules();
|
||||
blockGeometryUpdates();
|
||||
setModal(false);
|
||||
workspace()->activateNextWindow(this);
|
||||
cleanGrouping();
|
||||
workspace()->removeX11Window(this);
|
||||
|
@ -2187,6 +2185,11 @@ void X11Window::doSetHiddenByShowDesktop()
|
|||
updateVisibility();
|
||||
}
|
||||
|
||||
void X11Window::doSetModal()
|
||||
{
|
||||
info->setState(isModal() ? NET::Modal : NET::States(), NET::Modal);
|
||||
}
|
||||
|
||||
void X11Window::doSetOnActivities(const QStringList &activityList)
|
||||
{
|
||||
#if KWIN_BUILD_ACTIVITIES
|
||||
|
@ -3475,6 +3478,9 @@ QList<Window *> X11Window::mainWindows() const
|
|||
|
||||
Window *X11Window::findModal(bool allow_itself)
|
||||
{
|
||||
if (isDeleted()) {
|
||||
return nullptr;
|
||||
}
|
||||
for (auto it = transients().constBegin(); it != transients().constEnd(); ++it) {
|
||||
if (Window *ret = (*it)->findModal(true)) {
|
||||
return ret;
|
||||
|
|
|
@ -342,6 +342,7 @@ protected:
|
|||
void doSetDemandsAttention() override;
|
||||
void doSetHidden() override;
|
||||
void doSetHiddenByShowDesktop() override;
|
||||
void doSetModal() override;
|
||||
bool belongsToDesktop() const override;
|
||||
bool doStartInteractiveMoveResize() override;
|
||||
bool isWaitingForInteractiveResizeSync() const override;
|
||||
|
|
Loading…
Reference in a new issue