summaryrefslogtreecommitdiff
path: root/tests/auto/qx11info/tst_qx11info.cpp
diff options
context:
space:
mode:
authorGatis Paeglis <gatis.paeglis@qt.io>2017-07-26 14:54:23 +0200
committerGatis Paeglis <gatis.paeglis@qt.io>2017-08-29 14:18:29 +0000
commitec2c0f4db6350a304dcf901b9adbadc895864c14 (patch)
treedb8c6df6b3143b67f18feeab78a6d2cccd92b8f1 /tests/auto/qx11info/tst_qx11info.cpp
parent9749c5085dc21d2064177aa748f3a9395592a7cf (diff)
downloadqtx11extras-ec2c0f4db6350a304dcf901b9adbadc895864c14.tar.gz
[ChangeLog][QX11Info] 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). In Qt5 we set XCB to be the owner of the X11 event queue with XSetEventQueueOwner(dpy, XCBOwnsEventQueue), which means that only XCB event handling functions can be used to read events from the X server. XCB does not have an equivalent for Xlib's peeker API. For filtering out unwanted native events Qt5 provides QAbstractNativeEventFilter::nativeEventFilter(), but there isn't any API in Qt to just *peek* into the queue while the GUI thread is busy processing a long task. The peekEventQueue() function adds this capability via QX11Info. Manual and auto test included. Task-number: QTBUG-50358 Change-Id: Id31f797f7ff76d011ad7a55a9b6c13756aaaca60 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'tests/auto/qx11info/tst_qx11info.cpp')
-rw-r--r--tests/auto/qx11info/tst_qx11info.cpp214
1 files changed, 213 insertions, 1 deletions
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"