Commit a0c4a8e7 authored by Vlad Zahorodnii's avatar Vlad Zahorodnii Committed by Vlad Zahorodnii
Browse files

[x11] Force FocusIn events for already focused windows

Depending on the current focus stealing prevention level, it's possible
for kwin to call XSetInputFocus() on a window that already has the input
focus. In which case, we won't receive the corresponding FocusIn event
and the client will remain inactive from kwin's perspective even though
it isn't.

In order to work around this issue, we can move the input focus to the
null window. By doing so, it's guaranteed that we're going to receive
the matching FocusIn event for the client.

This commit indirectly fixes a bug where fullscreen games are displayed
below panels.
parent 6a42eccf
......@@ -57,6 +57,7 @@ private Q_SLOTS:
void testCaptionWmName();
void testCaptionMultipleWindows();
void testFullscreenWindowGroups();
void testActivateFocusedWindow();
};
void X11ClientTest::initTestCase()
......@@ -626,5 +627,71 @@ void X11ClientTest::testFullscreenWindowGroups()
QTRY_COMPARE(client->layer(), ActiveLayer);
}
void X11ClientTest::testActivateFocusedWindow()
{
// The window manager may call XSetInputFocus() on a window that already has focus, in which
// case no FocusIn event will be generated and the window won't be marked as active. This test
// verifies that we handle that subtle case properly.
QScopedPointer<xcb_connection_t, XcbConnectionDeleter> connection(xcb_connect(nullptr, nullptr));
QVERIFY(!xcb_connection_has_error(connection.data()));
QSignalSpy windowCreatedSpy(workspace(), &Workspace::clientAdded);
QVERIFY(windowCreatedSpy.isValid());
const QRect windowGeometry(0, 0, 100, 200);
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());
// Create the first test window.
const xcb_window_t window1 = xcb_generate_id(connection.data());
xcb_create_window(connection.data(), XCB_COPY_FROM_PARENT, window1, rootWindow(),
windowGeometry.x(), windowGeometry.y(),
windowGeometry.width(), windowGeometry.height(),
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
xcb_icccm_set_wm_normal_hints(connection.data(), window1, &hints);
xcb_change_property(connection.data(), XCB_PROP_MODE_REPLACE, window1,
atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &window1);
xcb_map_window(connection.data(), window1);
xcb_flush(connection.data());
QVERIFY(windowCreatedSpy.wait());
X11Client *client1 = windowCreatedSpy.first().first().value<X11Client *>();
QVERIFY(client1);
QCOMPARE(client1->windowId(), window1);
QCOMPARE(client1->isActive(), true);
// Create the second test window.
const xcb_window_t window2 = xcb_generate_id(connection.data());
xcb_create_window(connection.data(), XCB_COPY_FROM_PARENT, window2, rootWindow(),
windowGeometry.x(), windowGeometry.y(),
windowGeometry.width(), windowGeometry.height(),
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
xcb_icccm_set_wm_normal_hints(connection.data(), window2, &hints);
xcb_change_property(connection.data(), XCB_PROP_MODE_REPLACE, window2,
atoms->wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &window2);
xcb_map_window(connection.data(), window2);
xcb_flush(connection.data());
QVERIFY(windowCreatedSpy.wait());
X11Client *client2 = windowCreatedSpy.last().first().value<X11Client *>();
QVERIFY(client2);
QCOMPARE(client2->windowId(), window2);
QCOMPARE(client2->isActive(), true);
// When the second test window is destroyed, the window manager will attempt to activate the
// next client in the focus chain, which is the first window.
xcb_set_input_focus(connection.data(), XCB_INPUT_FOCUS_POINTER_ROOT, window1, XCB_CURRENT_TIME);
xcb_destroy_window(connection.data(), window2);
xcb_flush(connection.data());
QVERIFY(Test::waitForWindowDestroyed(client2));
QVERIFY(client1->isActive());
// Destroy the first test window.
xcb_destroy_window(connection.data(), window1);
xcb_flush(connection.data());
QVERIFY(Test::waitForWindowDestroyed(client1));
}
WAYLANDTEST_MAIN(X11ClientTest)
#include "x11_client_test.moc"
......@@ -2059,6 +2059,11 @@ void X11Client::setOnAllActivities(bool on)
*/
void X11Client::takeFocus()
{
// Force a FocusIn event if the window is already focused but inactive.
Xcb::CurrentInput currentInput;
if (!currentInput.isNull() && currentInput.window() == window())
workspace()->focusToNull();
if (rules()->checkAcceptFocus(info->input()))
m_client.focus();
else
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment