summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--dist/changes-5.10.030
-rw-r--r--src/x11extras/qx11info_x11.cpp158
-rw-r--r--src/x11extras/qx11info_x11.h17
-rw-r--r--tests/auto/qx11info/qx11info.pro1
-rw-r--r--tests/auto/qx11info/tst_qx11info.cpp214
-rw-r--r--tests/manual/manual.pro3
-rw-r--r--tests/manual/peeker/main.cpp111
-rw-r--r--tests/manual/peeker/peeker.pro6
-rw-r--r--tests/tests.pro2
10 files changed, 540 insertions, 4 deletions
diff --git a/.qmake.conf b/.qmake.conf
index e795316..08b8470 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -3,4 +3,4 @@ load(qt_build_config)
CONFIG += warning_clean
DEFINES += QT_NO_FOREACH
-MODULE_VERSION = 5.9.3
+MODULE_VERSION = 5.10.0
diff --git a/dist/changes-5.10.0 b/dist/changes-5.10.0
new file mode 100644
index 0000000..79bc713
--- /dev/null
+++ b/dist/changes-5.10.0
@@ -0,0 +1,30 @@
+Qt 5.10 introduces many new features and improvements as well as bugfixes
+over the 5.9.x series. For more details, refer to the online documentation
+included in this distribution. The documentation is also available online:
+
+http://doc.qt.io/qt-5/index.html
+
+The Qt version 5.10 series is binary compatible with the 5.9.x series.
+Applications compiled for 5.9 will continue to run with 5.10.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* Qt 5.10.0 Changes *
+****************************************************************************
+
+ - This release contains only minor code improvements.
+
+QX11Info
+--------
+
+ - [QTBUG-50358] Added new API to peek into the XCB event queue -
+ peekEventQueue(). This enables porting certain Qt4-based applications to
+ Qt5 (those using Xlib's event handling functions to peek into the X11
+ event queue).
diff --git a/src/x11extras/qx11info_x11.cpp b/src/x11extras/qx11info_x11.cpp
index 31bc405..e64ba9c 100644
--- a/src/x11extras/qx11info_x11.cpp
+++ b/src/x11extras/qx11info_x11.cpp
@@ -403,4 +403,162 @@ bool QX11Info::isCompositingManagerRunning(int screen)
return native->nativeResourceForScreen(QByteArray("compositingEnabled"), scr);
}
+/*!
+ Returns a new peeker id or -1 if some interal error has occurred.
+ Each peeker id is associated with an index in the buffered native
+ event queue.
+
+ For more details see QX11Info::PeekOption and peekEventQueue().
+
+ \sa peekEventQueue(), removePeekerId()
+ \since 5.10
+*/
+qint32 QX11Info::generatePeekerId()
+{
+ if (!qApp)
+ return -1;
+ QPlatformNativeInterface *native = qApp->platformNativeInterface();
+ if (!native)
+ return -1;
+
+ typedef qint32 (*GeneratePeekerIdFunc)(void);
+ GeneratePeekerIdFunc generatepeekerid = reinterpret_cast<GeneratePeekerIdFunc>(
+ native->nativeResourceFunctionForIntegration("generatepeekerid"));
+ if (!generatepeekerid) {
+ qWarning("Internal error: QPA plugin doesn't implement generatePeekerId");
+ return -1;
+ }
+
+ return generatepeekerid();
+}
+
+/*!
+ Removes \a peekerId, which was earlier obtained via generatePeekerId().
+
+ Returns \c true on success or \c false if unknown peeker id was
+ provided or some interal error has occurred.
+
+ \sa generatePeekerId()
+ \since 5.10
+*/
+bool QX11Info::removePeekerId(qint32 peekerId)
+{
+ if (!qApp)
+ return false;
+ QPlatformNativeInterface *native = qApp->platformNativeInterface();
+ if (!native)
+ return false;
+
+ typedef bool (*RemovePeekerIdFunc)(qint32);
+ RemovePeekerIdFunc removePeekerId = reinterpret_cast<RemovePeekerIdFunc>(
+ native->nativeResourceFunctionForIntegration("removepeekerid"));
+ if (!removePeekerId) {
+ qWarning("Internal error: QPA plugin doesn't implement removePeekerId");
+ return false;
+ }
+
+ return removePeekerId(peekerId);
+}
+
+/*!
+ \enum QX11Info::PeekOption
+ \brief An enum to tune the behavior of QX11Info::peekEventQueue().
+
+ \value PeekDefault
+ Peek from the beginning of the buffered native event queue. A peeker
+ id is optional with PeekDefault. If a peeker id is provided to
+ peekEventQueue() when using PeekDefault, then peeking starts from
+ the beginning of the queue, not from the cached index; thus, this
+ can be used to manually reset a cached index to peek from the start
+ of the queue. When this operation completes, the associated index
+ will be updated to the new position in the queue.
+
+ \value PeekFromCachedIndex
+ QX11Info::peekEventQueue() can optimize the peeking algorithm by
+ skipping events that it already has seen in earlier calls to
+ peekEventQueue(). When control returns to the main event loop,
+ which causes the buffered native event queue to be flushed to Qt's
+ event queue, the cached indices are marked invalid and will be
+ reset on the next access. The same is true if the program
+ explicitly flushes the buffered native event queue by
+ QCoreApplication::processEvents().
+
+ \since 5.10
+*/
+
+/*!
+ \typedef QX11Info::PeekerCallback
+ Typedef for a pointer to a function with the following signature:
+
+ \code
+ bool (*PeekerCallback)(xcb_generic_event_t *event, void *peekerData);
+ \endcode
+
+ The \a event is a native XCB event.
+ The \a peekerData is a pointer to data, passed in via peekEventQueue().
+
+ Return \c true from this function to stop examining the buffered
+ native event queue or \c false to continue.
+
+ \note A non-capturing lambda can serve as a PeekerCallback.
+ \since 5.10
+*/
+
+/*!
+ \brief Peek into the buffered XCB event queue.
+
+ You can call peekEventQueue() periodically, when your program is busy
+ performing a long-running operation, to peek into the buffered native
+ event queue. The more time the long-running operation blocks the
+ program from returning control to the main event loop, the more
+ events will accumulate in the buffered XCB event queue. Once control
+ returns to the main event loop these events will be flushed to Qt's
+ event queue, which is a separate event queue from the queue this
+ function is peeking into.
+
+ \note It is usually better to run CPU-intensive operations in a
+ non-GUI thread, instead of blocking the main event loop.
+
+ The buffered XCB event queue is populated from a non-GUI thread and
+ therefore might be ahead of the current GUI state. To handle native
+ events as they are processed by the GUI thread, see
+ QAbstractNativeEventFilter::nativeEventFilter().
+
+ The \a peeker is a callback function as documented in PeekerCallback.
+ The \a peekerData can be used to pass in arbitrary data to the \a
+ peeker callback.
+ The \a option is an enum that tunes the behavior of peekEventQueue().
+ The \a peekerId is used to track an index in the queue, for more
+ details see QX11Info::PeekOption. There can be several indices,
+ each tracked individually by a peeker id obtained via generatePeekerId().
+
+ This function returns \c true when the peeker has stopped the event
+ proccesing by returning \c true from the callback. If there were no
+ events in the buffered native event queue to peek at or all the
+ events have been processed by the peeker, this function returns \c
+ false.
+
+ \sa generatePeekerId(), QAbstractNativeEventFilter::nativeEventFilter()
+ \since 5.10
+*/
+bool QX11Info::peekEventQueue(PeekerCallback peeker, void *peekerData, PeekOptions option,
+ qint32 peekerId)
+{
+ if (!peeker || !qApp)
+ return false;
+ QPlatformNativeInterface *native = qApp->platformNativeInterface();
+ if (!native)
+ return false;
+
+ typedef bool (*PeekEventQueueFunc)(PeekerCallback, void *, PeekOptions, qint32);
+ PeekEventQueueFunc peekeventqueue = reinterpret_cast<PeekEventQueueFunc>(
+ native->nativeResourceFunctionForIntegration("peekeventqueue"));
+ if (!peekeventqueue) {
+ qWarning("Internal error: QPA plugin doesn't implement peekEventQueue");
+ return false;
+ }
+
+ return peekeventqueue(peeker, peekerData, option, peekerId);
+}
+
QT_END_NAMESPACE
diff --git a/src/x11extras/qx11info_x11.h b/src/x11extras/qx11info_x11.h
index ab72686..c0bfbf8 100644
--- a/src/x11extras/qx11info_x11.h
+++ b/src/x11extras/qx11info_x11.h
@@ -43,14 +43,21 @@
#include <QtCore/qnamespace.h>
#include "QtX11Extras/qtx11extrasglobal.h"
+#include <xcb/xcb.h>
+
typedef struct _XDisplay Display;
-struct xcb_connection_t;
QT_BEGIN_NAMESPACE
class Q_X11EXTRAS_EXPORT QX11Info
{
public:
+ enum PeekOption {
+ PeekDefault = 0,
+ PeekFromCachedIndex = 1
+ };
+ Q_DECLARE_FLAGS(PeekOptions, PeekOption)
+
static bool isPlatformX11();
static int appDpiX(int screen=-1);
@@ -75,10 +82,18 @@ public:
static bool isCompositingManagerRunning(int screen = -1);
+ static qint32 generatePeekerId();
+ static bool removePeekerId(qint32 peekerId);
+ typedef bool (*PeekerCallback)(xcb_generic_event_t *event, void *peekerData);
+ static bool peekEventQueue(PeekerCallback peeker, void *peekerData = nullptr,
+ PeekOptions option = PeekDefault, qint32 peekerId = -1);
+
private:
QX11Info();
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QX11Info::PeekOptions)
+
QT_END_NAMESPACE
#endif // QX11INFO_X11_H
diff --git a/tests/auto/qx11info/qx11info.pro b/tests/auto/qx11info/qx11info.pro
index 9059baa..3a34ecd 100644
--- a/tests/auto/qx11info/qx11info.pro
+++ b/tests/auto/qx11info/qx11info.pro
@@ -2,3 +2,4 @@ CONFIG += testcase
TARGET = tst_qx11info
QT += x11extras widgets testlib
SOURCES += tst_qx11info.cpp
+LIBS += -lxcb
diff --git a/tests/auto/qx11info/tst_qx11info.cpp b/tests/auto/qx11info/tst_qx11info.cpp
index a27f443..e997832 100644
--- a/tests/auto/qx11info/tst_qx11info.cpp
+++ b/tests/auto/qx11info/tst_qx11info.cpp
@@ -27,7 +27,7 @@
**
****************************************************************************/
-
+#include <QtGui>
#include <QtTest/QtTest>
#include <QApplication>
@@ -42,6 +42,7 @@ private slots:
void startupId();
void isPlatformX11();
void appTime();
+ void peeker();
};
void tst_QX11Info::staticFunctionsBeforeQApplication()
@@ -162,6 +163,217 @@ void tst_QX11Info::appTime()
QCOMPARE(QX11Info::appUserTime(), u1);
}
+class PeekerTest : public QWindow
+{
+public:
+ PeekerTest()
+ {
+ setGeometry(100, 100, 400, 400);
+ m_peekerFirstId = QX11Info::generatePeekerId();
+ QVERIFY(m_peekerFirstId >= 0);
+ m_peekerSecondId = QX11Info::generatePeekerId();
+ QVERIFY(m_peekerSecondId == m_peekerFirstId + 1);
+ // Get X atoms
+ xcb_connection_t *c = QX11Info::connection();
+ const char *first = "QT_XCB_PEEKER_TEST_FIRST";
+ const char *second = "QT_XCB_PEEKER_TEST_SECOND";
+ xcb_intern_atom_reply_t *reply =
+ xcb_intern_atom_reply(c, xcb_intern_atom(c, false, strlen(first), first), 0);
+ QVERIFY2(reply != nullptr, first);
+ m_atomFirst = reply->atom;
+ free(reply);
+ reply = xcb_intern_atom_reply(c, xcb_intern_atom(c, false, strlen(second), second), 0);
+ QVERIFY2(reply != nullptr, second);
+ m_atomSecond = reply->atom;
+ free(reply);
+ }
+
+protected:
+ void triggerPropertyNotifyEvents()
+ {
+ xcb_window_t rootWindow = QX11Info::appRootWindow();
+ xcb_connection_t *connection = QX11Info::connection();
+ xcb_change_property(connection, XCB_PROP_MODE_APPEND, rootWindow, m_atomFirst,
+ XCB_ATOM_INTEGER, 32, 0, nullptr);
+ xcb_change_property(connection, XCB_PROP_MODE_APPEND, rootWindow, m_atomSecond,
+ XCB_ATOM_INTEGER, 32, 0, nullptr);
+ xcb_flush(connection);
+ }
+
+ static bool checkForPropertyNotifyByAtom(xcb_generic_event_t *event, void *data)
+ {
+ bool isPropertyNotify = (event->response_type & ~0x80) == XCB_PROPERTY_NOTIFY;
+ if (isPropertyNotify) {
+ xcb_property_notify_event_t *pn =
+ reinterpret_cast<xcb_property_notify_event_t *>(event);
+ xcb_atom_t *atom = static_cast<xcb_atom_t *>(data);
+ if (pn->atom == *atom)
+ return true;
+ }
+ return false;
+ }
+
+ bool sanityCheckPeeking(xcb_generic_event_t *event)
+ {
+ m_countWithCaching++;
+ bool isPropertyNotify = (event->response_type & ~0x80) == XCB_PROPERTY_NOTIFY;
+ if (isPropertyNotify) {
+ xcb_property_notify_event_t *pn =
+ reinterpret_cast<xcb_property_notify_event_t *>(event);
+ if (pn->atom == m_atomFirst) {
+ if (m_indexFirst == -1) {
+ m_indexFirst = m_countWithCaching;
+ // continue peeking, maybe the second event is already in the queue
+ return false;
+ }
+ m_foundFirstEventAgain = true;
+ // Return so we can fail the test with QVERIFY2, for more details
+ // see QTBUG-62354
+ return true;
+ }
+ // Let it peek to the end, even when the second event was found
+ if (pn->atom == m_atomSecond) {
+ m_indexSecond = m_countWithCaching;
+ if (m_indexFirst == -1) {
+ m_foundSecondBeforeFirst = true;
+ // Same as above, see QTBUG-62354
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ void exposeEvent(QExposeEvent *)
+ {
+ if (m_ignoreSubsequentExpose)
+ return;
+ // We don't want to execute this handler again in case there are more expose
+ // events after calling QCoreApplication::processEvents() later in this test
+ m_ignoreSubsequentExpose = true;
+
+ xcb_flush(QX11Info::connection());
+ bool found = QX11Info::peekEventQueue(checkForPropertyNotifyByAtom, &m_atomFirst);
+ QVERIFY2(!found, "Found m_atomFirst which should not be in the queue yet");
+ found = QX11Info::peekEventQueue(checkForPropertyNotifyByAtom, &m_atomSecond);
+ QVERIFY2(!found, "Found m_atomSecond which should not be in the queue yet");
+
+ triggerPropertyNotifyEvents();
+
+ bool earlyExit = false;
+ while (!earlyExit && (m_indexFirst == -1 || m_indexSecond == -1)) {
+
+ earlyExit = QX11Info::peekEventQueue([](xcb_generic_event_t *event, void *data) {
+ return static_cast<PeekerTest *>(data)->sanityCheckPeeking(event);
+ }, this, QX11Info::PeekFromCachedIndex, m_peekerFirstId);
+
+ if (m_countWithCaching == -1)
+ QVERIFY2(!earlyExit, "Unexpected return value for an empty queue");
+ }
+
+ QVERIFY2(!m_foundFirstEventAgain,
+ "Found the same notify event twice, maybe broken index cache?");
+ QVERIFY2(!m_foundSecondBeforeFirst, "Found second event before first");
+ QVERIFY2(!earlyExit,
+ "Unexpected return value for a peeker that always scans to the end of the queue");
+
+ found = QX11Info::peekEventQueue(checkForPropertyNotifyByAtom, &m_atomFirst,
+ QX11Info::PeekDefault, m_peekerFirstId);
+ QVERIFY2(found, "Failed to find m_atomFirst, when peeking from start and ignoring "
+ "the cached index associated with the passed in peeker id");
+ // The above call updated index cache for m_peekerFirstId to the position of
+ // event(m_atomFirst) + 1
+ found = QX11Info::peekEventQueue(checkForPropertyNotifyByAtom, &m_atomSecond,
+ QX11Info::PeekFromCachedIndex, m_peekerFirstId);
+ QVERIFY2(found, "Unexpectedly failed to find m_atomSecond");
+
+ QVERIFY(m_indexFirst <= m_countWithCaching);
+ QVERIFY(m_indexSecond <= m_countWithCaching);
+ QVERIFY(m_indexFirst < m_indexSecond);
+ QX11Info::peekEventQueue([](xcb_generic_event_t *, void *data) {
+ static_cast<PeekerTest *>(data)->m_countFromStart++;
+ return false;
+ }, this);
+ QVERIFY(m_countWithCaching <= m_countFromStart);
+ found = QX11Info::peekEventQueue(checkForPropertyNotifyByAtom, &m_atomFirst,
+ QX11Info::PeekFromCachedIndex, m_peekerSecondId);
+ QVERIFY2(found, "m_peekerSecondId failed to find the event");
+
+ // Remove peeker id from within the peeker while using it for peeking
+ QX11Info::peekEventQueue([](xcb_generic_event_t *, void *data) {
+ PeekerTest *obj = static_cast<PeekerTest *>(data);
+ QX11Info::removePeekerId(obj->m_peekerSecondId);
+ return true;
+ }, this, QX11Info::PeekFromCachedIndex, m_peekerSecondId);
+ // Check that it really has been removed from the cache
+ bool ok = QX11Info::peekEventQueue([](xcb_generic_event_t *, void *) {
+ return true;
+ }, nullptr, QX11Info::PeekFromCachedIndex, m_peekerSecondId);
+ QVERIFY2(!ok, "Unexpected return value when attempting to peek from cached "
+ "index when peeker id has been removed from the cache");
+
+ // Sanity check other input combinations
+ QVERIFY(!QX11Info::removePeekerId(-99));
+ ok = QX11Info::peekEventQueue([](xcb_generic_event_t *, void *) {
+ return true;
+ }, nullptr, QX11Info::PeekFromCachedIndex, -100);
+ QVERIFY2(!ok, "PeekFromCachedIndex with invalid peeker id unexpectedly succeeded");
+ ok = QX11Info::peekEventQueue([](xcb_generic_event_t *, void *) {
+ return true;
+ }, nullptr, QX11Info::PeekDefault, -100);
+ QVERIFY2(!ok, "PeekDefault with invalid peeker id unexpectedly succeeded");
+ ok = QX11Info::peekEventQueue([](xcb_generic_event_t *, void *) {
+ return true;
+ }, nullptr, QX11Info::PeekFromCachedIndex);
+ QVERIFY2(!ok, "PeekFromCachedIndex without peeker id unexpectedly succeeded");
+ ok = QX11Info::peekEventQueue([](xcb_generic_event_t *, void *) {
+ return true;
+ }, nullptr, QX11Info::PeekDefault);
+ QVERIFY2(ok, "PeekDefault without peeker id unexpectedly failed");
+
+ QCoreApplication::processEvents(); // Flush buffered events
+
+ found = QX11Info::peekEventQueue(checkForPropertyNotifyByAtom, &m_atomFirst);
+ QVERIFY2(!found, "Found m_atomFirst in the queue after flushing");
+ found = QX11Info::peekEventQueue(checkForPropertyNotifyByAtom, &m_atomSecond);
+ QVERIFY2(!found, "Found m_atomSecond in the queue after flushing");
+
+ QVERIFY(QX11Info::removePeekerId(m_peekerFirstId));
+ QVERIFY2(!QX11Info::removePeekerId(m_peekerFirstId),
+ "Removing the same peeker id twice unexpectedly succeeded");
+#if 0
+ qDebug() << "Buffered event queue size (caching) : " << m_countWithCaching + 1;
+ qDebug() << "Buffered event queue size (from start) : " << m_countFromStart + 1;
+ qDebug() << "PropertyNotify[FIRST] at : " << m_indexFirst;
+ qDebug() << "PropertyNotify[SECOND] at : " << m_indexSecond;
+#endif
+ }
+
+private:
+ xcb_atom_t m_atomFirst = -1;
+ xcb_atom_t m_atomSecond = -1;
+ qint32 m_peekerFirstId = -1;
+ qint32 m_peekerSecondId = -1;
+ qint32 m_indexFirst = -1;
+ qint32 m_indexSecond = -1;
+ bool m_foundFirstEventAgain = false;
+ qint32 m_countWithCaching = -1;
+ qint32 m_countFromStart = -1;
+ bool m_ignoreSubsequentExpose = false;
+ bool m_foundSecondBeforeFirst = false;
+};
+
+void tst_QX11Info::peeker()
+{
+ int argc = 0;
+ QGuiApplication app(argc, 0);
+
+ PeekerTest test;
+ test.show();
+
+ QVERIFY(QTest::qWaitForWindowExposed(&test));
+}
+
QTEST_APPLESS_MAIN(tst_QX11Info)
#include "tst_qx11info.moc"
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
new file mode 100644
index 0000000..1ef0dcd
--- /dev/null
+++ b/tests/manual/manual.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+
+SUBDIRS = peeker
diff --git a/tests/manual/peeker/main.cpp b/tests/manual/peeker/main.cpp
new file mode 100644
index 0000000..8c7b280
--- /dev/null
+++ b/tests/manual/peeker/main.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui>
+#include <QtX11Extras/QX11Info>
+
+class IntensivePainter : public QWindow
+{
+public:
+ IntensivePainter() : m_backingStore(new QBackingStore(this))
+ { setGeometry(100, 100, 900, 600); }
+
+protected:
+ bool interrupted() const { return m_stopPainting; }
+ void stopPainting() { m_stopPainting = true; }
+ void beginPaining() { m_stopPainting = false; }
+
+ void exposeEvent(QExposeEvent *)
+ {
+ bool useGreenColor = false;
+ QRect rect(0, 0, width(), height());
+ m_backingStore->resize(QSize(width(), height()));
+
+ qint32 peekerId = QX11Info::generatePeekerId();
+ if (peekerId == -1) {
+ qWarning() << "Internal error: QX11Info::generatePeekerId() returned -1";
+ exit(EXIT_FAILURE);
+ }
+
+ while (!interrupted()) { // Begin a long operation
+ m_backingStore->beginPaint(rect);
+ QPaintDevice *device = m_backingStore->paintDevice();
+ QPainter painter(device);
+ painter.fillRect(rect, useGreenColor ? Qt::green : Qt::red);
+ useGreenColor = !useGreenColor;
+ painter.drawText(rect, Qt::AlignCenter, QStringLiteral("Press any key to exit.\n"
+ "(make sure the window is in focus when testing)\n\n If key press does not "
+ "immediately exit the application,\n then that should be considered a bug/regression."
+ "\n\n A demo might appear frozen to some window managers\n and therefore "
+ "might get grayed out (e.g Unity), this is the expected behavior.\n On KWin, for "
+ "example, this demo does not get grayed out.\n Resizing the window on any system will "
+ "cause rendering artefacts, that is not a bug,\n but simply the way the test is "
+ "implemented."));
+ m_backingStore->endPaint();
+ m_backingStore->flush(rect);
+ QThread::msleep(500); // Reduce the speed of re-painting (blinking)
+
+ QX11Info::peekEventQueue([](xcb_generic_event_t *event, void *data) {
+ bool isKeyPress = (event->response_type & ~0x80) == XCB_KEY_PRESS;
+ if (isKeyPress) {
+ IntensivePainter *painter = static_cast<IntensivePainter *>(data);
+ painter->stopPainting();
+ }
+ return isKeyPress;
+ }, this, QX11Info::PeekOption::PeekFromCachedIndex, peekerId);
+ }
+
+ QX11Info::removePeekerId(peekerId);
+ exit(EXIT_SUCCESS);
+ }
+
+private:
+ QBackingStore *m_backingStore;
+ bool m_stopPainting = false;
+};
+
+int main(int argc, char** argv)
+{
+ QGuiApplication app(argc, argv);
+
+ IntensivePainter painterWindow;
+ painterWindow.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/peeker/peeker.pro b/tests/manual/peeker/peeker.pro
new file mode 100644
index 0000000..aef58cd
--- /dev/null
+++ b/tests/manual/peeker/peeker.pro
@@ -0,0 +1,6 @@
+QT += x11extras
+
+TARGET = peeker
+TEMPLATE = app
+
+SOURCES += main.cpp
diff --git a/tests/tests.pro b/tests/tests.pro
index d1a5e17..3febcf1 100644
--- a/tests/tests.pro
+++ b/tests/tests.pro
@@ -2,5 +2,5 @@ TEMPLATE = subdirs
QT_FOR_CONFIG += gui-private
qtConfig(xcb) {
- SUBDIRS += auto
+ SUBDIRS += auto manual
}