You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
10 KiB
281 lines
10 KiB
/******************************************************************** |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
Copyright (C) 2014 Martin Gräßlin <mgraesslin@kde.org> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*********************************************************************/ |
|
#include "../screens_xrandr.h" |
|
#include "../cursor.h" |
|
#include "../xcbutils.h" |
|
#include "mock_workspace.h" |
|
// Qt |
|
#include <QtTest/QtTest> |
|
|
|
// mocking |
|
namespace KWin |
|
{ |
|
|
|
QPoint Cursor::pos() |
|
{ |
|
return QPoint(0, 0); |
|
} |
|
} // namespace KWin |
|
|
|
static xcb_window_t s_rootWindow = XCB_WINDOW_NONE; |
|
static xcb_connection_t *s_connection = nullptr; |
|
|
|
unsigned long QX11Info::appRootWindow(int) |
|
{ |
|
return s_rootWindow; |
|
} |
|
xcb_connection_t *QX11Info::connection() |
|
{ |
|
return s_connection; |
|
} |
|
|
|
using namespace KWin; |
|
using namespace KWin::Xcb; |
|
|
|
class TestXRandRScreens : public QObject |
|
{ |
|
Q_OBJECT |
|
private Q_SLOTS: |
|
void initTestCase(); |
|
void cleanupTestCase(); |
|
void testStartup(); |
|
void testChange(); |
|
void testMultipleChanges(); |
|
private: |
|
QScopedPointer<QProcess> m_xserver; |
|
}; |
|
|
|
void TestXRandRScreens::initTestCase() |
|
{ |
|
// TODO: turn into init instead of initTestCase |
|
// needs to be initTestCase as KWin::connection caches the first created xcb_connection_t |
|
// thus changing X server for each test run would create problems |
|
qsrand(QDateTime::currentMSecsSinceEpoch()); |
|
// first reset just to be sure |
|
s_connection = nullptr; |
|
s_rootWindow = XCB_WINDOW_NONE; |
|
// start X Server |
|
m_xserver.reset(new QProcess); |
|
// randomize the display id in [1, 98] |
|
// 0 is not used because it conflicts with "normal" X server |
|
// 99 is not used because it's used by KDE's CI infrastructure |
|
const QString id = QStringLiteral(":") + QString::number((qrand() % 98) + 1); |
|
// using Xephyr as Xvfb doesn't support render extension |
|
m_xserver->start(QStringLiteral("Xephyr"), QStringList() << id); |
|
QVERIFY(m_xserver->waitForStarted()); |
|
QCOMPARE(m_xserver->state(), QProcess::Running); |
|
// give it some time before we open the X Display |
|
QTest::qWait(100); |
|
// create X connection |
|
int screen = 0; |
|
s_connection = xcb_connect(qPrintable(id), &screen); |
|
QVERIFY(s_connection); |
|
|
|
// set root window |
|
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(s_connection)); |
|
for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(s_connection)); |
|
it.rem; |
|
--screen, xcb_screen_next(&it)) { |
|
if (screen == 0) { |
|
s_rootWindow = iter.data->root; |
|
break; |
|
} |
|
} |
|
QVERIFY(s_rootWindow != XCB_WINDOW_NONE); |
|
|
|
// get the extensions |
|
if (!Extensions::self()->isRandrAvailable()) { |
|
QSKIP("XRandR extension required"); |
|
} |
|
for (const auto &extension : Extensions::self()->extensions()) { |
|
if (extension.name == QByteArrayLiteral("RANDR")) { |
|
if (extension.version < 1 * 0x10 + 4) { |
|
QSKIP("At least XRandR 1.4 required"); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void TestXRandRScreens::cleanupTestCase() |
|
{ |
|
Extensions::destroy(); |
|
// close connection |
|
xcb_disconnect(s_connection); |
|
s_connection = nullptr; |
|
s_rootWindow = XCB_WINDOW_NONE; |
|
// kill X |
|
m_xserver->terminate(); |
|
m_xserver->waitForFinished(); |
|
} |
|
|
|
void TestXRandRScreens::testStartup() |
|
{ |
|
KWin::MockWorkspace ws; |
|
QScopedPointer<XRandRScreens> screens(new XRandRScreens(this)); |
|
QVERIFY(screens->eventType() != 0); |
|
QCOMPARE(screens->eventType(), Xcb::Extensions::self()->randrNotifyEvent()); |
|
QCOMPARE(screens->extension(), 0); |
|
QCOMPARE(screens->genericEventType(), 0); |
|
screens->init(); |
|
QRect xephyrDefault = QRect(0, 0, 640, 480); |
|
QCOMPARE(screens->count(), 1); |
|
QCOMPARE(screens->geometry(0), xephyrDefault); |
|
QCOMPARE(screens->geometry(1), QRect()); |
|
QCOMPARE(screens->geometry(-1), QRect()); |
|
QCOMPARE(static_cast<Screens*>(screens.data())->geometry(), xephyrDefault); |
|
QCOMPARE(screens->size(0), xephyrDefault.size()); |
|
QCOMPARE(screens->size(1), QSize()); |
|
QCOMPARE(screens->size(-1), QSize()); |
|
QCOMPARE(static_cast<Screens*>(screens.data())->size(), xephyrDefault.size()); |
|
// unfortunately we only have one output, so let's try at least to test somewhat |
|
QCOMPARE(screens->number(QPoint(0, 0)), 0); |
|
QCOMPARE(screens->number(QPoint(639, 479)), 0); |
|
QCOMPARE(screens->number(QPoint(1280, 1024)), 0); |
|
|
|
// let's change the mode |
|
RandR::CurrentResources resources(s_rootWindow); |
|
auto *crtcs = resources.crtcs(); |
|
auto *modes = xcb_randr_get_screen_resources_current_modes(resources.data()); |
|
auto *outputs = xcb_randr_get_screen_resources_current_outputs(resources.data()); |
|
RandR::SetCrtcConfig setter(crtcs[0], resources->timestamp, resources->config_timestamp, 0, 0, modes[0].id, XCB_RANDR_ROTATION_ROTATE_0, 1, outputs); |
|
QVERIFY(!setter.isNull()); |
|
|
|
// now let's recreate the XRandRScreens |
|
screens.reset(new XRandRScreens(this)); |
|
screens->init(); |
|
QRect geo = QRect(0, 0, modes[0].width, modes[0].height); |
|
QCOMPARE(screens->count(), 1); |
|
QCOMPARE(screens->geometry(0), geo); |
|
QCOMPARE(static_cast<Screens*>(screens.data())->geometry(), geo); |
|
QCOMPARE(screens->size(0), geo.size()); |
|
QCOMPARE(static_cast<Screens*>(screens.data())->size(), geo.size()); |
|
} |
|
|
|
void TestXRandRScreens::testChange() |
|
{ |
|
KWin::MockWorkspace ws; |
|
QScopedPointer<XRandRScreens> screens(new XRandRScreens(this)); |
|
screens->init(); |
|
|
|
// create some signal spys |
|
QSignalSpy changedSpy(screens.data(), SIGNAL(changed())); |
|
QVERIFY(changedSpy.isValid()); |
|
QVERIFY(changedSpy.isEmpty()); |
|
QVERIFY(changedSpy.wait()); |
|
changedSpy.clear(); |
|
QSignalSpy geometrySpy(screens.data(), SIGNAL(geometryChanged())); |
|
QVERIFY(geometrySpy.isValid()); |
|
QVERIFY(geometrySpy.isEmpty()); |
|
QSignalSpy sizeSpy(screens.data(), SIGNAL(sizeChanged())); |
|
QVERIFY(sizeSpy.isValid()); |
|
QVERIFY(sizeSpy.isEmpty()); |
|
|
|
// clear the event loop |
|
while (xcb_generic_event_t *e = xcb_poll_for_event(s_connection)) { |
|
free(e); |
|
} |
|
|
|
// let's change |
|
RandR::CurrentResources resources(s_rootWindow); |
|
auto *crtcs = resources.crtcs(); |
|
auto *modes = xcb_randr_get_screen_resources_current_modes(resources.data()); |
|
auto *outputs = xcb_randr_get_screen_resources_current_outputs(resources.data()); |
|
RandR::SetCrtcConfig setter(crtcs[0], resources->timestamp, resources->config_timestamp, 0, 0, modes[1].id, XCB_RANDR_ROTATION_ROTATE_0, 1, outputs); |
|
xcb_flush(s_connection); |
|
QVERIFY(!setter.isNull()); |
|
QVERIFY(setter->status == XCB_RANDR_SET_CONFIG_SUCCESS); |
|
|
|
xcb_generic_event_t *e = xcb_wait_for_event(s_connection); |
|
screens->event(e); |
|
free(e); |
|
|
|
QVERIFY(changedSpy.wait()); |
|
QCOMPARE(changedSpy.size(), 1); |
|
QCOMPARE(sizeSpy.size(), 1); |
|
QCOMPARE(geometrySpy.size(), 1); |
|
QRect geo = QRect(0, 0, modes[1].width, modes[1].height); |
|
QCOMPARE(screens->count(), 1); |
|
QCOMPARE(screens->geometry(0), geo); |
|
QCOMPARE(static_cast<Screens*>(screens.data())->geometry(), geo); |
|
QCOMPARE(screens->size(0), geo.size()); |
|
QCOMPARE(static_cast<Screens*>(screens.data())->size(), geo.size()); |
|
} |
|
|
|
void TestXRandRScreens::testMultipleChanges() |
|
{ |
|
KWin::MockWorkspace ws; |
|
// multiple changes should only hit one changed signal |
|
QScopedPointer<XRandRScreens> screens(new XRandRScreens(this)); |
|
screens->init(); |
|
|
|
// create some signal spys |
|
QSignalSpy changedSpy(screens.data(), SIGNAL(changed())); |
|
QVERIFY(changedSpy.isValid()); |
|
QVERIFY(changedSpy.isEmpty()); |
|
QVERIFY(changedSpy.wait()); |
|
changedSpy.clear(); |
|
QSignalSpy geometrySpy(screens.data(), SIGNAL(geometryChanged())); |
|
QVERIFY(geometrySpy.isValid()); |
|
QVERIFY(geometrySpy.isEmpty()); |
|
QSignalSpy sizeSpy(screens.data(), SIGNAL(sizeChanged())); |
|
QVERIFY(sizeSpy.isValid()); |
|
QVERIFY(sizeSpy.isEmpty()); |
|
|
|
// clear the event loop |
|
while (xcb_generic_event_t *e = xcb_poll_for_event(s_connection)) { |
|
free(e); |
|
} |
|
|
|
// first change |
|
RandR::CurrentResources resources(s_rootWindow); |
|
auto *crtcs = resources.crtcs(); |
|
auto *modes = xcb_randr_get_screen_resources_current_modes(resources.data()); |
|
auto *outputs = xcb_randr_get_screen_resources_current_outputs(resources.data()); |
|
RandR::SetCrtcConfig setter(crtcs[0], resources->timestamp, resources->config_timestamp, 0, 0, modes[0].id, XCB_RANDR_ROTATION_ROTATE_0, 1, outputs); |
|
QVERIFY(!setter.isNull()); |
|
QVERIFY(setter->status == XCB_RANDR_SET_CONFIG_SUCCESS); |
|
// second change |
|
RandR::SetCrtcConfig setter2(crtcs[0], setter->timestamp, resources->config_timestamp, 0, 0, modes[1].id, XCB_RANDR_ROTATION_ROTATE_0, 1, outputs); |
|
QVERIFY(!setter2.isNull()); |
|
QVERIFY(setter2->status == XCB_RANDR_SET_CONFIG_SUCCESS); |
|
|
|
auto passEvent = [&screens]() { |
|
xcb_generic_event_t *e = xcb_wait_for_event(s_connection); |
|
screens->event(e); |
|
free(e); |
|
}; |
|
passEvent(); |
|
passEvent(); |
|
|
|
QVERIFY(changedSpy.wait()); |
|
QCOMPARE(changedSpy.size(), 1); |
|
// previous state was modes[1] so the size didn't change |
|
QVERIFY(sizeSpy.isEmpty()); |
|
QVERIFY(geometrySpy.isEmpty()); |
|
QRect geo = QRect(0, 0, modes[1].width, modes[1].height); |
|
QCOMPARE(screens->count(), 1); |
|
QCOMPARE(screens->geometry(0), geo); |
|
QCOMPARE(static_cast<Screens*>(screens.data())->geometry(), geo); |
|
QCOMPARE(screens->size(0), geo.size()); |
|
QCOMPARE(static_cast<Screens*>(screens.data())->size(), geo.size()); |
|
} |
|
|
|
QTEST_GUILESS_MAIN(TestXRandRScreens) |
|
#include "test_xrandr_screens.moc"
|
|
|