Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,649 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "nettesthelper.h"
|
||||
#include <netwm.h>
|
||||
|
||||
#include <QProcess>
|
||||
#include <QStandardPaths>
|
||||
#include <qtest_widgets.h>
|
||||
|
||||
// system
|
||||
#include <unistd.h>
|
||||
|
||||
Q_DECLARE_METATYPE(NET::State)
|
||||
Q_DECLARE_METATYPE(NET::States)
|
||||
Q_DECLARE_METATYPE(NET::Actions)
|
||||
|
||||
using Property = UniqueCPointer<xcb_get_property_reply_t>;
|
||||
|
||||
// clang-format off
|
||||
#define INFO NETWinInfo info(m_connection, m_testWindow, m_rootWindow, NET::WMAllProperties, NET::WM2AllProperties, NET::WindowManager);
|
||||
|
||||
#define ATOM(name) \
|
||||
KXUtils::Atom atom(connection(), QByteArrayLiteral(#name));
|
||||
|
||||
#define UTF8 KXUtils::Atom utf8String(connection(), QByteArrayLiteral("UTF8_STRING"));
|
||||
|
||||
#define GETPROP(type, length, formatSize) \
|
||||
xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, m_testWindow, \
|
||||
atom, type, 0, length); \
|
||||
Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); \
|
||||
QVERIFY(reply); \
|
||||
QCOMPARE(reply->format, uint8_t(formatSize)); \
|
||||
QCOMPARE(reply->value_len, uint32_t(length));
|
||||
|
||||
#define VERIFYDELETED(t) \
|
||||
xcb_get_property_cookie_t cookieDeleted = xcb_get_property_unchecked(connection(), false, m_testWindow, \
|
||||
atom, t, 0, 1); \
|
||||
Property replyDeleted(xcb_get_property_reply(connection(), cookieDeleted, nullptr)); \
|
||||
QVERIFY(replyDeleted); \
|
||||
QVERIFY(replyDeleted->type == XCB_ATOM_NONE);
|
||||
|
||||
class NetWinInfoTestWM : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
void testState_data();
|
||||
void testState();
|
||||
void testVisibleName();
|
||||
void testVisibleIconName();
|
||||
void testDesktop_data();
|
||||
void testDesktop();
|
||||
void testOpacity_data();
|
||||
void testOpacity();
|
||||
void testAllowedActions_data();
|
||||
void testAllowedActions();
|
||||
void testFrameExtents();
|
||||
void testFrameExtentsKDE();
|
||||
void testFrameOverlap();
|
||||
void testFullscreenMonitors();
|
||||
|
||||
private:
|
||||
bool hasAtomFlag(const xcb_atom_t *atoms, int atomsLenght, const QByteArray &actionName);
|
||||
void testStrut(xcb_atom_t atom, NETStrut(NETWinInfo:: *getter)(void)const, void (NETWinInfo:: *setter)(NETStrut), NET::Property property, NET::Property2 property2 = NET::Property2(0));
|
||||
void waitForPropertyChange(NETWinInfo *info, xcb_atom_t atom, NET::Property prop, NET::Property2 prop2 = NET::Property2(0));
|
||||
xcb_connection_t *connection()
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
xcb_connection_t *m_connection;
|
||||
QList<xcb_connection_t*> m_connections;
|
||||
std::unique_ptr<QProcess> m_xvfb;
|
||||
xcb_window_t m_rootWindow;
|
||||
xcb_window_t m_testWindow;
|
||||
QByteArray m_displayNumber;
|
||||
};
|
||||
|
||||
void NetWinInfoTestWM::initTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::cleanupTestCase()
|
||||
{
|
||||
// close connection
|
||||
while (!m_connections.isEmpty()) {
|
||||
xcb_disconnect(m_connections.takeFirst());
|
||||
}
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::init()
|
||||
{
|
||||
// first reset just to be sure
|
||||
m_connection = nullptr;
|
||||
m_rootWindow = XCB_WINDOW_NONE;
|
||||
m_testWindow = XCB_WINDOW_NONE;
|
||||
|
||||
const QString xfvbExec = QStandardPaths::findExecutable(QStringLiteral("Xvfb"));
|
||||
QVERIFY(!xfvbExec.isEmpty());
|
||||
|
||||
// start Xvfb
|
||||
m_xvfb.reset(new QProcess);
|
||||
|
||||
// use pipe to pass fd to Xvfb to get back the display id
|
||||
int pipeFds[2];
|
||||
QVERIFY(pipe(pipeFds) == 0);
|
||||
m_xvfb->start(QStringLiteral("Xvfb"), QStringList{ QStringLiteral("-displayfd"), QString::number(pipeFds[1]) });
|
||||
QVERIFY(m_xvfb->waitForStarted());
|
||||
QCOMPARE(m_xvfb->state(), QProcess::Running);
|
||||
|
||||
// reads from pipe, closes write side
|
||||
close(pipeFds[1]);
|
||||
|
||||
QFile readPipe;
|
||||
QVERIFY(readPipe.open(pipeFds[0], QIODevice::ReadOnly, QFileDevice::AutoCloseHandle));
|
||||
QByteArray displayNumber = readPipe.readLine();
|
||||
readPipe.close();
|
||||
|
||||
displayNumber.prepend(QByteArray(":"));
|
||||
displayNumber.remove(displayNumber.size() -1, 1);
|
||||
m_displayNumber = displayNumber;
|
||||
|
||||
// create X connection
|
||||
int screen = 0;
|
||||
m_connection = xcb_connect(displayNumber.constData(), &screen);
|
||||
QVERIFY(m_connection);
|
||||
QVERIFY(!xcb_connection_has_error(m_connection));
|
||||
m_rootWindow = KXUtils::rootWindow(m_connection, screen);
|
||||
|
||||
// create test window
|
||||
m_testWindow = xcb_generate_id(m_connection);
|
||||
uint32_t values[] = {XCB_EVENT_MASK_PROPERTY_CHANGE};
|
||||
xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, m_testWindow,
|
||||
m_rootWindow,
|
||||
0, 0, 100, 100, 0, XCB_COPY_FROM_PARENT,
|
||||
XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values);
|
||||
// and map it
|
||||
xcb_map_window(m_connection, m_testWindow);
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::cleanup()
|
||||
{
|
||||
// destroy test window
|
||||
xcb_unmap_window(m_connection, m_testWindow);
|
||||
xcb_destroy_window(m_connection, m_testWindow);
|
||||
m_testWindow = XCB_WINDOW_NONE;
|
||||
|
||||
// delay till clenupTestCase as otherwise xcb reuses the same memory address
|
||||
m_connections << connection();
|
||||
// kill Xvfb
|
||||
m_xvfb->terminate();
|
||||
m_xvfb->waitForFinished();
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::waitForPropertyChange(NETWinInfo *info, xcb_atom_t atom, NET::Property prop, NET::Property2 prop2)
|
||||
{
|
||||
while (true) {
|
||||
UniqueCPointer<xcb_generic_event_t> event(xcb_wait_for_event(connection()));
|
||||
if (!event) {
|
||||
break;
|
||||
}
|
||||
if ((event->response_type & ~0x80) != XCB_PROPERTY_NOTIFY) {
|
||||
continue;
|
||||
}
|
||||
xcb_property_notify_event_t *pe = reinterpret_cast<xcb_property_notify_event_t *>(event.get());
|
||||
if (pe->window != m_testWindow) {
|
||||
continue;
|
||||
}
|
||||
if (pe->atom != atom) {
|
||||
continue;
|
||||
}
|
||||
NET::Properties dirty;
|
||||
NET::Properties2 dirty2;
|
||||
info->event(event.get(), &dirty, &dirty2);
|
||||
if (prop != 0) {
|
||||
QVERIFY(dirty & prop);
|
||||
}
|
||||
if (prop2 != 0) {
|
||||
QVERIFY(dirty2 & prop2);
|
||||
}
|
||||
if (!prop) {
|
||||
QCOMPARE(dirty, NET::Properties());
|
||||
}
|
||||
if (!prop2) {
|
||||
QCOMPARE(dirty2, NET::Properties2());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool NetWinInfoTestWM::hasAtomFlag(const xcb_atom_t *atoms, int atomsLength, const QByteArray &actionName)
|
||||
{
|
||||
KXUtils::Atom atom(connection(), actionName);
|
||||
if (atom == XCB_ATOM_NONE) {
|
||||
qDebug() << "get atom failed";
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < atomsLength; ++i) {
|
||||
if (atoms[i] == atom) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::testAllowedActions_data()
|
||||
{
|
||||
QTest::addColumn<NET::Actions>("actions");
|
||||
QTest::addColumn<QList<QByteArray> >("names");
|
||||
|
||||
const QByteArray move = QByteArrayLiteral("_NET_WM_ACTION_MOVE");
|
||||
const QByteArray resize = QByteArrayLiteral("_NET_WM_ACTION_RESIZE");
|
||||
const QByteArray minimize = QByteArrayLiteral("_NET_WM_ACTION_MINIMIZE");
|
||||
const QByteArray shade = QByteArrayLiteral("_NET_WM_ACTION_SHADE");
|
||||
const QByteArray stick = QByteArrayLiteral("_NET_WM_ACTION_STICK");
|
||||
const QByteArray maxVert = QByteArrayLiteral("_NET_WM_ACTION_MAXIMIZE_VERT");
|
||||
const QByteArray maxHoriz = QByteArrayLiteral("_NET_WM_ACTION_MAXIMIZE_HORZ");
|
||||
const QByteArray fullscreen = QByteArrayLiteral("_NET_WM_ACTION_FULLSCREEN");
|
||||
const QByteArray desktop = QByteArrayLiteral("_NET_WM_ACTION_CHANGE_DESKTOP");
|
||||
const QByteArray close = QByteArrayLiteral("_NET_WM_ACTION_CLOSE");
|
||||
|
||||
QTest::newRow("move") << NET::Actions(NET::ActionMove) << (QList<QByteArray>() << move);
|
||||
QTest::newRow("resize") << NET::Actions(NET::ActionResize) << (QList<QByteArray>() << resize);
|
||||
QTest::newRow("minimize") << NET::Actions(NET::ActionMinimize) << (QList<QByteArray>() << minimize);
|
||||
QTest::newRow("shade") << NET::Actions(NET::ActionShade) << (QList<QByteArray>() << shade);
|
||||
QTest::newRow("stick") << NET::Actions(NET::ActionStick) << (QList<QByteArray>() << stick);
|
||||
QTest::newRow("maxVert") << NET::Actions(NET::ActionMaxVert) << (QList<QByteArray>() << maxVert);
|
||||
QTest::newRow("maxHoriz") << NET::Actions(NET::ActionMaxHoriz) << (QList<QByteArray>() << maxHoriz);
|
||||
QTest::newRow("fullscreen") << NET::Actions(NET::ActionFullScreen) << (QList<QByteArray>() << fullscreen);
|
||||
QTest::newRow("desktop") << NET::Actions(NET::ActionChangeDesktop) << (QList<QByteArray>() << desktop);
|
||||
QTest::newRow("close") << NET::Actions(NET::ActionClose) << (QList<QByteArray>() << close);
|
||||
|
||||
QTest::newRow("none") << NET::Actions() << QList<QByteArray>();
|
||||
|
||||
QTest::newRow("all") << NET::Actions(NET::ActionMove |
|
||||
NET::ActionResize |
|
||||
NET::ActionMinimize |
|
||||
NET::ActionShade |
|
||||
NET::ActionStick |
|
||||
NET::ActionMaxVert |
|
||||
NET::ActionMaxHoriz |
|
||||
NET::ActionFullScreen |
|
||||
NET::ActionChangeDesktop |
|
||||
NET::ActionClose)
|
||||
<< (QList<QByteArray>() << move << resize << minimize << shade <<
|
||||
stick << maxVert << maxHoriz <<
|
||||
fullscreen << desktop << close);
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::testAllowedActions()
|
||||
{
|
||||
QVERIFY(connection());
|
||||
ATOM(_NET_WM_ALLOWED_ACTIONS)
|
||||
INFO
|
||||
|
||||
QCOMPARE(info.allowedActions(), NET::Actions());
|
||||
QFETCH(NET::Actions, actions);
|
||||
info.setAllowedActions(actions);
|
||||
QCOMPARE(info.allowedActions(), actions);
|
||||
|
||||
// compare with the X property
|
||||
QFETCH(QList<QByteArray>, names);
|
||||
QVERIFY(atom != XCB_ATOM_NONE);
|
||||
GETPROP(XCB_ATOM_ATOM, names.size(), 32)
|
||||
xcb_atom_t *atoms = reinterpret_cast<xcb_atom_t *>(xcb_get_property_value(reply.get()));
|
||||
for (int i = 0; i < names.size(); ++i) {
|
||||
QVERIFY(hasAtomFlag(atoms, names.size(), names.at(i)));
|
||||
}
|
||||
|
||||
// and wait for our event
|
||||
waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2AllowedActions);
|
||||
QCOMPARE(info.allowedActions(), actions);
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::testDesktop_data()
|
||||
{
|
||||
QTest::addColumn<int>("desktop");
|
||||
QTest::addColumn<uint32_t>("propertyDesktop");
|
||||
|
||||
QTest::newRow("1") << 1 << uint32_t(0);
|
||||
QTest::newRow("4") << 4 << uint32_t(3);
|
||||
QTest::newRow("on all") << int(NET::OnAllDesktops) << uint32_t(~0);
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::testDesktop()
|
||||
{
|
||||
QVERIFY(connection());
|
||||
ATOM(_NET_WM_DESKTOP)
|
||||
INFO
|
||||
|
||||
QCOMPARE(info.desktop(), 0);
|
||||
QFETCH(int, desktop);
|
||||
info.setDesktop(desktop);
|
||||
QCOMPARE(info.desktop(), desktop);
|
||||
|
||||
// compare with the X property
|
||||
QVERIFY(atom != XCB_ATOM_NONE);
|
||||
GETPROP(XCB_ATOM_CARDINAL, 1, 32)
|
||||
QTEST(reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.get()))[0], "propertyDesktop");
|
||||
|
||||
// and wait for our event
|
||||
waitForPropertyChange(&info, atom, NET::WMDesktop);
|
||||
QCOMPARE(info.desktop(), desktop);
|
||||
|
||||
// delete it
|
||||
info.setDesktop(0);
|
||||
QCOMPARE(info.desktop(), 0);
|
||||
VERIFYDELETED(XCB_ATOM_CARDINAL)
|
||||
|
||||
// and wait for our event
|
||||
waitForPropertyChange(&info, atom, NET::WMDesktop);
|
||||
QCOMPARE(info.desktop(), 0);
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::testStrut(xcb_atom_t atom, NETStrut(NETWinInfo:: *getter)(void)const, void (NETWinInfo:: *setter)(NETStrut), NET::Property property, NET::Property2 property2)
|
||||
{
|
||||
INFO
|
||||
|
||||
NETStrut extents = (info.*getter)();
|
||||
QCOMPARE(extents.bottom, 0);
|
||||
QCOMPARE(extents.left, 0);
|
||||
QCOMPARE(extents.right, 0);
|
||||
QCOMPARE(extents.top, 0);
|
||||
|
||||
NETStrut newExtents;
|
||||
newExtents.bottom = 10;
|
||||
newExtents.left = 20;
|
||||
newExtents.right = 30;
|
||||
newExtents.top = 40;
|
||||
(info.*setter)(newExtents);
|
||||
extents = (info.*getter)();
|
||||
QCOMPARE(extents.bottom, newExtents.bottom);
|
||||
QCOMPARE(extents.left, newExtents.left);
|
||||
QCOMPARE(extents.right, newExtents.right);
|
||||
QCOMPARE(extents.top, newExtents.top);
|
||||
|
||||
// compare with the X property
|
||||
QVERIFY(atom != XCB_ATOM_NONE);
|
||||
GETPROP(XCB_ATOM_CARDINAL, 4, 32)
|
||||
uint32_t *data = reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.get()));
|
||||
QCOMPARE(data[0], uint32_t(newExtents.left));
|
||||
QCOMPARE(data[1], uint32_t(newExtents.right));
|
||||
QCOMPARE(data[2], uint32_t(newExtents.top));
|
||||
QCOMPARE(data[3], uint32_t(newExtents.bottom));
|
||||
|
||||
// and wait for our event
|
||||
waitForPropertyChange(&info, atom, property, property2);
|
||||
extents = (info.*getter)();
|
||||
QCOMPARE(extents.bottom, newExtents.bottom);
|
||||
QCOMPARE(extents.left, newExtents.left);
|
||||
QCOMPARE(extents.right, newExtents.right);
|
||||
QCOMPARE(extents.top, newExtents.top);
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::testFrameExtents()
|
||||
{
|
||||
QVERIFY(connection());
|
||||
ATOM(_NET_FRAME_EXTENTS)
|
||||
testStrut(atom, &NETWinInfo::frameExtents, &NETWinInfo::setFrameExtents, NET::WMFrameExtents);
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::testFrameExtentsKDE()
|
||||
{
|
||||
// same as testFrameExtents just with a different atom name
|
||||
QVERIFY(connection());
|
||||
ATOM(_KDE_NET_WM_FRAME_STRUT)
|
||||
testStrut(atom, &NETWinInfo::frameExtents, &NETWinInfo::setFrameExtents, NET::WMFrameExtents);
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::testFrameOverlap()
|
||||
{
|
||||
QVERIFY(connection());
|
||||
ATOM(_NET_WM_FRAME_OVERLAP)
|
||||
testStrut(atom, &NETWinInfo::frameOverlap, &NETWinInfo::setFrameOverlap, NET::Property(0), NET::WM2FrameOverlap);
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::testOpacity_data()
|
||||
{
|
||||
QTest::addColumn<uint32_t>("opacity");
|
||||
|
||||
QTest::newRow("0 %") << uint32_t(0);
|
||||
QTest::newRow("50 %") << uint32_t(0x0000ffff);
|
||||
QTest::newRow("100 %") << uint32_t(0xffffffff);
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::testOpacity()
|
||||
{
|
||||
QVERIFY(connection());
|
||||
ATOM(_NET_WM_WINDOW_OPACITY)
|
||||
INFO
|
||||
|
||||
QCOMPARE(info.opacity(), static_cast<unsigned long>(0xffffffffU));
|
||||
QFETCH(uint32_t, opacity);
|
||||
info.setOpacity(opacity);
|
||||
QCOMPARE(info.opacity(), static_cast<unsigned long>(opacity));
|
||||
|
||||
// compare with the X property
|
||||
QVERIFY(atom != XCB_ATOM_NONE);
|
||||
GETPROP(XCB_ATOM_CARDINAL, 1, 32)
|
||||
QCOMPARE(reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.get()))[0], opacity);
|
||||
|
||||
// and wait for our event
|
||||
waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2Opacity);
|
||||
QCOMPARE(info.opacity(), static_cast<unsigned long>(opacity));
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::testState_data()
|
||||
{
|
||||
QTest::addColumn<NET::States>("states");
|
||||
QTest::addColumn<QList<QByteArray> >("names");
|
||||
|
||||
const QByteArray modal = QByteArrayLiteral("_NET_WM_STATE_MODAL");
|
||||
const QByteArray sticky = QByteArrayLiteral("_NET_WM_STATE_STICKY");
|
||||
const QByteArray maxVert = QByteArrayLiteral("_NET_WM_STATE_MAXIMIZED_VERT");
|
||||
const QByteArray maxHoriz = QByteArrayLiteral("_NET_WM_STATE_MAXIMIZED_HORZ");
|
||||
const QByteArray shaded = QByteArrayLiteral("_NET_WM_STATE_SHADED");
|
||||
const QByteArray skipTaskbar = QByteArrayLiteral("_NET_WM_STATE_SKIP_TASKBAR");
|
||||
const QByteArray skipSwitcher = QByteArrayLiteral("_KDE_NET_WM_STATE_SKIP_SWITCHER");
|
||||
const QByteArray keepAbove = QByteArrayLiteral("_NET_WM_STATE_ABOVE");
|
||||
const QByteArray staysOnTop = QByteArrayLiteral("_NET_WM_STATE_STAYS_ON_TOP");
|
||||
const QByteArray skipPager = QByteArrayLiteral("_NET_WM_STATE_SKIP_PAGER");
|
||||
const QByteArray hidden = QByteArrayLiteral("_NET_WM_STATE_HIDDEN");
|
||||
const QByteArray fullScreen = QByteArrayLiteral("_NET_WM_STATE_FULLSCREEN");
|
||||
const QByteArray keepBelow = QByteArrayLiteral("_NET_WM_STATE_BELOW");
|
||||
const QByteArray demandsAttention = QByteArrayLiteral("_NET_WM_STATE_DEMANDS_ATTENTION");
|
||||
const QByteArray focused = QByteArrayLiteral("_NET_WM_STATE_FOCUSED");
|
||||
|
||||
QTest::newRow("modal") << NET::States(NET::Modal) << (QList<QByteArray>() << modal);
|
||||
QTest::newRow("sticky") << NET::States(NET::Sticky) << (QList<QByteArray>() << sticky);
|
||||
QTest::newRow("maxVert") << NET::States(NET::MaxVert) << (QList<QByteArray>() << maxVert);
|
||||
QTest::newRow("maxHoriz") << NET::States(NET::MaxHoriz) << (QList<QByteArray>() << maxHoriz);
|
||||
QTest::newRow("shaded") << NET::States(NET::Shaded) << (QList<QByteArray>() << shaded);
|
||||
QTest::newRow("skipTaskbar") << NET::States(NET::SkipTaskbar) << (QList<QByteArray>() << skipTaskbar);
|
||||
QTest::newRow("keepAbove") << NET::States(NET::KeepAbove) << (QList<QByteArray>() << keepAbove << staysOnTop);
|
||||
QTest::newRow("skipPager") << NET::States(NET::SkipPager) << (QList<QByteArray>() << skipPager);
|
||||
QTest::newRow("hidden") << NET::States(NET::Hidden) << (QList<QByteArray>() << hidden);
|
||||
QTest::newRow("fullScreen") << NET::States(NET::FullScreen) << (QList<QByteArray>() << fullScreen);
|
||||
QTest::newRow("keepBelow") << NET::States(NET::KeepBelow) << (QList<QByteArray>() << keepBelow);
|
||||
QTest::newRow("demandsAttention") << NET::States(NET::DemandsAttention) << (QList<QByteArray>() << demandsAttention);
|
||||
QTest::newRow("skipSwitcher") << NET::States(NET::SkipSwitcher) << (QList<QByteArray>() << skipSwitcher);
|
||||
QTest::newRow("focused") << NET::States(NET::Focused) << (QList<QByteArray>() << focused);
|
||||
|
||||
// TODO: it's possible to be keep above and below at the same time?!?
|
||||
QTest::newRow("all") << NET::States(NET::Modal |
|
||||
NET::Sticky |
|
||||
NET::Max |
|
||||
NET::Shaded |
|
||||
NET::SkipTaskbar |
|
||||
NET::SkipPager |
|
||||
NET::KeepAbove |
|
||||
NET::KeepBelow |
|
||||
NET::Hidden |
|
||||
NET::FullScreen |
|
||||
NET::DemandsAttention |
|
||||
NET::SkipSwitcher |
|
||||
NET::Focused)
|
||||
<< (QList<QByteArray>() << modal << sticky << maxVert << maxHoriz
|
||||
<< shaded << skipTaskbar << keepAbove
|
||||
<< skipPager << hidden << fullScreen
|
||||
<< keepBelow << demandsAttention << staysOnTop << skipSwitcher << focused);
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::testState()
|
||||
{
|
||||
QVERIFY(connection());
|
||||
ATOM(_NET_WM_STATE)
|
||||
INFO
|
||||
|
||||
QCOMPARE(info.state(), NET::States());
|
||||
QFETCH(NET::States, states);
|
||||
info.setState(states, NET::States());
|
||||
QCOMPARE(info.state(), states);
|
||||
|
||||
// compare with the X property
|
||||
QFETCH(QList<QByteArray>, names);
|
||||
QVERIFY(atom != XCB_ATOM_NONE);
|
||||
GETPROP(XCB_ATOM_ATOM, names.size(), 32)
|
||||
xcb_atom_t *atoms = reinterpret_cast<xcb_atom_t *>(xcb_get_property_value(reply.get()));
|
||||
for (int i = 0; i < names.size(); ++i) {
|
||||
QVERIFY(hasAtomFlag(atoms, names.size(), names.at(i)));
|
||||
}
|
||||
|
||||
// and wait for our event
|
||||
waitForPropertyChange(&info, atom, NET::WMState);
|
||||
QCOMPARE(info.state(), states);
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::testVisibleIconName()
|
||||
{
|
||||
QVERIFY(connection());
|
||||
ATOM(_NET_WM_VISIBLE_ICON_NAME)
|
||||
UTF8
|
||||
INFO
|
||||
|
||||
QVERIFY(!info.visibleIconName());
|
||||
info.setVisibleIconName("foo");
|
||||
QCOMPARE(info.visibleIconName(), "foo");
|
||||
|
||||
// compare with the X property
|
||||
QVERIFY(atom != XCB_ATOM_NONE);
|
||||
QVERIFY(utf8String != XCB_ATOM_NONE);
|
||||
GETPROP(utf8String, 3, 8)
|
||||
QCOMPARE(reinterpret_cast<const char *>(xcb_get_property_value(reply.get())), "foo");
|
||||
|
||||
// and wait for our event
|
||||
waitForPropertyChange(&info, atom, NET::WMVisibleIconName);
|
||||
QCOMPARE(info.visibleIconName(), "foo");
|
||||
|
||||
// delete the string
|
||||
info.setVisibleIconName("");
|
||||
QCOMPARE(info.visibleIconName(), "");
|
||||
VERIFYDELETED(utf8String)
|
||||
// and wait for our event
|
||||
waitForPropertyChange(&info, atom, NET::WMVisibleIconName);
|
||||
QVERIFY(!info.visibleIconName());
|
||||
|
||||
// set again, to ensure we don't leak on tear down
|
||||
info.setVisibleIconName("bar");
|
||||
xcb_flush(connection());
|
||||
waitForPropertyChange(&info, atom, NET::WMVisibleIconName);
|
||||
QCOMPARE(info.visibleIconName(), "bar");
|
||||
}
|
||||
|
||||
void NetWinInfoTestWM::testVisibleName()
|
||||
{
|
||||
QVERIFY(connection());
|
||||
ATOM(_NET_WM_VISIBLE_NAME)
|
||||
UTF8
|
||||
INFO
|
||||
|
||||
QVERIFY(!info.visibleName());
|
||||
info.setVisibleName("foo");
|
||||
QCOMPARE(info.visibleName(), "foo");
|
||||
|
||||
// compare with the X property
|
||||
QVERIFY(atom != XCB_ATOM_NONE);
|
||||
QVERIFY(utf8String != XCB_ATOM_NONE);
|
||||
GETPROP(utf8String, 3, 8)
|
||||
QCOMPARE(reinterpret_cast<const char *>(xcb_get_property_value(reply.get())), "foo");
|
||||
|
||||
// and wait for our event
|
||||
waitForPropertyChange(&info, atom, NET::WMVisibleName);
|
||||
QCOMPARE(info.visibleName(), "foo");
|
||||
|
||||
// delete the string
|
||||
info.setVisibleName("");
|
||||
QCOMPARE(info.visibleName(), "");
|
||||
VERIFYDELETED(utf8String)
|
||||
|
||||
// and wait for our event
|
||||
waitForPropertyChange(&info, atom, NET::WMVisibleName);
|
||||
QVERIFY(!info.visibleName());
|
||||
|
||||
// set again, to ensure we don't leak on tear down
|
||||
info.setVisibleName("bar");
|
||||
xcb_flush(connection());
|
||||
waitForPropertyChange(&info, atom, NET::WMVisibleName);
|
||||
QCOMPARE(info.visibleName(), "bar");
|
||||
}
|
||||
|
||||
class MockWinInfo : public NETWinInfo
|
||||
{
|
||||
public:
|
||||
MockWinInfo(xcb_connection_t *connection, xcb_window_t window, xcb_window_t rootWindow)
|
||||
: NETWinInfo(connection, window, rootWindow, NET::WMAllProperties, NET::WM2AllProperties, NET::WindowManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void changeFullscreenMonitors(NETFullscreenMonitors topology) override
|
||||
{
|
||||
setFullscreenMonitors(topology);
|
||||
}
|
||||
};
|
||||
|
||||
void NetWinInfoTestWM::testFullscreenMonitors()
|
||||
{
|
||||
// test case for BUG 391960
|
||||
QVERIFY(connection());
|
||||
const uint32_t maskValues[] = {
|
||||
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
|
||||
XCB_EVENT_MASK_KEY_PRESS |
|
||||
XCB_EVENT_MASK_PROPERTY_CHANGE |
|
||||
XCB_EVENT_MASK_COLOR_MAP_CHANGE |
|
||||
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
|
||||
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
|
||||
XCB_EVENT_MASK_FOCUS_CHANGE | // For NotifyDetailNone
|
||||
XCB_EVENT_MASK_EXPOSURE
|
||||
};
|
||||
UniqueCPointer<xcb_generic_error_t> redirectCheck(xcb_request_check(connection(), xcb_change_window_attributes_checked(connection(),
|
||||
m_rootWindow, XCB_CW_EVENT_MASK, maskValues)));
|
||||
QVERIFY(!redirectCheck);
|
||||
|
||||
KXUtils::Atom atom(connection(), QByteArrayLiteral("_NET_WM_FULLSCREEN_MONITORS"));
|
||||
|
||||
// create client connection
|
||||
auto clientConnection = xcb_connect(m_displayNumber.constData(), nullptr);
|
||||
QVERIFY(clientConnection);
|
||||
QVERIFY(!xcb_connection_has_error(clientConnection));
|
||||
|
||||
NETWinInfo clientInfo(clientConnection, m_testWindow, m_rootWindow, NET::WMAllProperties, NET::WM2AllProperties);
|
||||
NETFullscreenMonitors topology;
|
||||
topology.top = 1;
|
||||
topology.bottom = 2;
|
||||
topology.left = 3;
|
||||
topology.right = 4;
|
||||
clientInfo.setFullscreenMonitors(topology);
|
||||
xcb_flush(clientConnection);
|
||||
|
||||
MockWinInfo info(connection(), m_testWindow, m_rootWindow);
|
||||
|
||||
while (true) {
|
||||
UniqueCPointer<xcb_generic_event_t> event(xcb_wait_for_event(connection()));
|
||||
if (!event) {
|
||||
break;
|
||||
}
|
||||
if ((event->response_type & ~0x80) != XCB_CLIENT_MESSAGE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NET::Properties dirtyProtocols;
|
||||
NET::Properties2 dirtyProtocols2;
|
||||
QCOMPARE(info.fullscreenMonitors().isSet(), false);
|
||||
info.event(event.get(), &dirtyProtocols, &dirtyProtocols2);
|
||||
QCOMPARE(info.fullscreenMonitors().isSet(), true);
|
||||
break;
|
||||
}
|
||||
xcb_flush(connection());
|
||||
// now the property should be updated
|
||||
waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2FullscreenMonitors);
|
||||
|
||||
QCOMPARE(info.fullscreenMonitors().top, 1);
|
||||
QCOMPARE(info.fullscreenMonitors().bottom, 2);
|
||||
QCOMPARE(info.fullscreenMonitors().left, 3);
|
||||
QCOMPARE(info.fullscreenMonitors().right, 4);
|
||||
|
||||
xcb_disconnect(clientConnection);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(NetWinInfoTestWM)
|
||||
|
||||
#include "netwininfotestwm.moc"
|
||||
Reference in New Issue
Block a user