From b45eeae3526de4308ce1f8f6a53fd0cb44d828ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Tue, 15 Apr 2014 10:09:25 +0200 Subject: [PATCH] [Xcb::Wrapper] Introduce a Property and StringProperty Wrapper subclass The Xcb::Property can wrap the xcb_get_property call and provides convenient access methods to read the value of the reply with checks applied. For this it provides a templated ::value method for reading a single value or reading an array. There's also a ::toBool and ::toByteArray which performs the conversion directly with default values for the type and format checks. Xcb::TransientFor is changed to be derived from Property instead of Wrapper directly, so that the reading of the property value can be shared. Xcb::StringProperty is a convenient wrapper derived from Property to handle the reading of a string property providing a cast to QByteArray operator. This replaces the ::getStringProperty from utils. Though the separator functionality from ::getStringProperty is not provided as that is only used in one function and handled there. All the custom usages of xcb_get_property or getStringProperty are replaced to use this new wrapper. That simplifies the code and ensures that all properties are read in the same way. REVIEW: 117574 --- activation.cpp | 9 +- autotests/test_xcb_wrapper.cpp | 93 +++++++++++++++ client.cpp | 93 ++++++--------- effects.cpp | 11 +- shadow.cpp | 9 +- toplevel.cpp | 39 ++----- toplevel.h | 1 - utils.cpp | 22 ---- utils.h | 1 - xcbutils.h | 201 +++++++++++++++++++++++++++++++-- 10 files changed, 334 insertions(+), 145 deletions(-) diff --git a/activation.cpp b/activation.cpp index f8a9414ffd..d98c7d95b7 100644 --- a/activation.cpp +++ b/activation.cpp @@ -674,13 +674,8 @@ void Client::updateUserTime(xcb_timestamp_t time) xcb_timestamp_t Client::readUserCreationTime() const { - const xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, window(), - atoms->kde_net_wm_user_creation_time, XCB_ATOM_CARDINAL, 0, 10000); - ScopedCPointer property(xcb_get_property_reply(connection(), cookie, NULL)); - if (property.isNull() || xcb_get_property_value_length(property.data()) == 0) { - return -1; - } - return *(reinterpret_cast(xcb_get_property_value(property.data()))); + Xcb::Property prop(false, window(), atoms->kde_net_wm_user_creation_time, XCB_ATOM_CARDINAL, 0, 1); + return prop.value(-1); } void Client::demandAttention(bool set) diff --git a/autotests/test_xcb_wrapper.cpp b/autotests/test_xcb_wrapper.cpp index 769cd1736a..2ade4b0e9a 100644 --- a/autotests/test_xcb_wrapper.cpp +++ b/autotests/test_xcb_wrapper.cpp @@ -23,6 +23,7 @@ along with this program. If not, see . // Qt #include #include +#include // xcb #include @@ -47,6 +48,8 @@ private Q_SLOTS: void testQueryTree(); void testCurrentInput(); void testTransientFor(); + void testPropertyByteArray(); + void testPropertyBool(); private: void testEmpty(WindowGeometry &geometry); void testGeometry(WindowGeometry &geometry, const QRect &rect); @@ -275,6 +278,12 @@ void TestXcbWrapper::testTransientFor() xcb_window_t compareWindow = XCB_WINDOW_NONE; QVERIFY(!transient.getTransientFor(&compareWindow)); QCOMPARE(compareWindow, xcb_window_t(XCB_WINDOW_NONE)); + bool ok = true; + QCOMPARE(transient.value(32, XCB_ATOM_WINDOW, XCB_WINDOW_NONE, &ok), xcb_window_t(XCB_WINDOW_NONE)); + QVERIFY(!ok); + ok = true; + QCOMPARE(transient.value(XCB_WINDOW_NONE, &ok), xcb_window_t(XCB_WINDOW_NONE)); + QVERIFY(!ok); // Create a Window with a transient for hint Window transientWindow(createWindow()); @@ -284,11 +293,95 @@ void TestXcbWrapper::testTransientFor() TransientFor realTransient(transientWindow); QVERIFY(realTransient.getTransientFor(&compareWindow)); QCOMPARE(compareWindow, m_testWindow); + ok = false; + QCOMPARE(realTransient.value(32, XCB_ATOM_WINDOW, XCB_WINDOW_NONE, &ok), m_testWindow); + QVERIFY(ok); + ok = false; + QCOMPARE(realTransient.value(XCB_WINDOW_NONE, &ok), m_testWindow); + QVERIFY(ok); + ok = false; + QCOMPARE(realTransient.value(), m_testWindow); + QCOMPARE(realTransient.value(nullptr, &ok)[0], m_testWindow); + QVERIFY(ok); + QCOMPARE(realTransient.value()[0], m_testWindow); // test for a not existing window TransientFor doesntExist(XCB_WINDOW_NONE); QVERIFY(!doesntExist.getTransientFor(&compareWindow)); } +void TestXcbWrapper::testPropertyByteArray() +{ + Window testWindow(createWindow()); + Property prop(false, testWindow, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 100000); + QCOMPARE(prop.toByteArray(), QByteArray()); + bool ok = true; + QCOMPARE(prop.toByteArray(&ok), QByteArray()); + QVERIFY(!ok); + ok = true; + QVERIFY(!prop.value()); + QCOMPARE(prop.value("bar", &ok), "bar"); + QVERIFY(!ok); + QCOMPARE(QByteArray(StringProperty(testWindow, XCB_ATOM_WM_NAME)), QByteArray()); + + testWindow.changeProperty(XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, 3, "foo"); + prop = Property(false, testWindow, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 100000); + QCOMPARE(prop.toByteArray(), QByteArrayLiteral("foo")); + QCOMPARE(prop.toByteArray(&ok), QByteArrayLiteral("foo")); + QVERIFY(ok); + QCOMPARE(prop.value(nullptr, &ok), "foo"); + QVERIFY(ok); + QCOMPARE(QByteArray(StringProperty(testWindow, XCB_ATOM_WM_NAME)), QByteArrayLiteral("foo")); + + // verify incorrect format and type + QCOMPARE(prop.toByteArray(32), QByteArray()); + QCOMPARE(prop.toByteArray(8, XCB_ATOM_CARDINAL), QByteArray()); + + // verify empty property + testWindow.changeProperty(XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, 0, nullptr); + prop = Property(false, testWindow, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 100000); + QCOMPARE(prop.toByteArray(), QByteArray()); + QCOMPARE(prop.toByteArray(&ok), QByteArray()); + QVERIFY(!ok); + QVERIFY(!prop.value()); + QCOMPARE(QByteArray(StringProperty(testWindow, XCB_ATOM_WM_NAME)), QByteArray()); +} + +void TestXcbWrapper::testPropertyBool() +{ + Window testWindow(createWindow()); + Atom blockCompositing(QByteArrayLiteral("_KDE_NET_WM_BLOCK_COMPOSITING")); + QVERIFY(blockCompositing != XCB_ATOM_NONE); + NETWinInfo info(QX11Info::connection(), testWindow, QX11Info::appRootWindow(), NET::Properties(), NET::WM2BlockCompositing); + + Property prop(false, testWindow, blockCompositing, XCB_ATOM_CARDINAL, 0, 100000); + bool ok = true; + QVERIFY(!prop.toBool()); + QVERIFY(!prop.toBool(&ok)); + QVERIFY(!ok); + + info.setBlockingCompositing(true); + xcb_flush(QX11Info::connection()); + prop = Property(false, testWindow, blockCompositing, XCB_ATOM_CARDINAL, 0, 100000); + QVERIFY(prop.toBool()); + QVERIFY(prop.toBool(&ok)); + QVERIFY(ok); + + // incorrect type and format + QVERIFY(!prop.toBool(8)); + QVERIFY(!prop.toBool(32, blockCompositing)); + QVERIFY(!prop.toBool(32, blockCompositing, &ok)); + QVERIFY(!ok); + + // incorrect value: + uint32_t d[] = {1, 0}; + testWindow.changeProperty(blockCompositing, XCB_ATOM_CARDINAL, 32, 2, d); + prop = Property(false, testWindow, blockCompositing, XCB_ATOM_CARDINAL, 0, 100000); + QVERIFY(!prop.toBool()); + ok = true; + QVERIFY(!prop.toBool(&ok)); + QVERIFY(!ok); +} + KWIN_TEST_MAIN(TestXcbWrapper) #include "test_xcb_wrapper.moc" diff --git a/client.cpp b/client.cpp index dedffbe05a..0eb4086b87 100644 --- a/client.cpp +++ b/client.cpp @@ -2388,7 +2388,7 @@ void Client::checkActivities() { #ifdef KWIN_BUILD_ACTIVITIES QStringList newActivitiesList; - QByteArray prop = getStringProperty(window(), atoms->activities); + QByteArray prop = Xcb::StringProperty(window(), atoms->activities); activitiesDefined = !prop.isEmpty(); if (QString::fromUtf8(prop) == Activities::nullUuid()) { //copied from setOnAllActivities to avoid a redundant XChangeProperty. @@ -2456,36 +2456,20 @@ KDecorationDefines::Position Client::titlebarPosition() const void Client::updateFirstInTabBox() { // TODO: move into KWindowInfo - xcb_connection_t *c = connection(); - const auto cookie = xcb_get_property_unchecked(c, false, m_client, atoms->kde_first_in_window_list, - atoms->kde_first_in_window_list, 0, 1); - ScopedCPointer prop(xcb_get_property_reply(c, cookie, nullptr)); - if (!prop.isNull() && prop->format == 32 && prop->value_len == 1) { - setFirstInTabBox(true); - } else { - setFirstInTabBox(false); - } + Xcb::Property property(false, m_client, atoms->kde_first_in_window_list, + atoms->kde_first_in_window_list, 0, 1); + setFirstInTabBox(property.toBool(32, atoms->kde_first_in_window_list)); } void Client::updateColorScheme() { // TODO: move into KWindowInfo - xcb_connection_t *c = connection(); - const auto cookie = xcb_get_property_unchecked(c, false, m_client, atoms->kde_color_sheme, - XCB_ATOM_STRING, 0, 10000); - ScopedCPointer prop(xcb_get_property_reply(c, cookie, nullptr)); - auto resetToDefault = [this]() { - m_palette = QApplication::palette(); - }; - QString path; - if (!prop.isNull() && prop->format == 8 && prop->value_len > 0) { - path = QString::fromUtf8(static_cast(xcb_get_property_value(prop.data()))); - } + QString path = QString::fromUtf8(Xcb::StringProperty(m_client, atoms->kde_color_sheme)); path = rules()->checkDecoColor(path); if (!path.isNull()) { m_palette = KColorScheme::createApplicationPalette(KSharedConfig::openConfig(path)); } else { - resetToDefault(); + m_palette = QApplication::palette(); } triggerDecorationRepaint(); } @@ -2563,48 +2547,35 @@ xcb_window_t Client::frameId() const void Client::updateShowOnScreenEdge() { - auto cookie = xcb_get_property_unchecked(connection(), false, window(), atoms->kde_screen_edge_show, XCB_ATOM_CARDINAL, 0, 1); - ScopedCPointer reply(xcb_get_property_reply(connection(), cookie, nullptr)); - - auto restore = [this]() { + Xcb::Property property(false, window(), atoms->kde_screen_edge_show, XCB_ATOM_CARDINAL, 0, 1); + const uint32_t value = property.value(ElectricNone); + ElectricBorder border = ElectricNone; + switch (value) { + case 0: + border = ElectricTop; + break; + case 1: + border = ElectricRight; + break; + case 2: + border = ElectricBottom; + break; + case 3: + border = ElectricLeft; + break; + } + if (border != ElectricNone) { + hideClient(true); + ScreenEdges::self()->reserve(this, border); + } else if (!property.isNull() && property->type != XCB_ATOM_NONE) { + // property value is incorrect, delete the property + // so that the client knows that it is not hidden + xcb_delete_property(connection(), window(), atoms->kde_screen_edge_show); + } else { + // restore // TODO: add proper unreserve ScreenEdges::self()->reserve(this, ElectricNone); hideClient(false); - }; - - if (!reply.isNull()) { - if (reply->format == 32 && reply->type == XCB_ATOM_CARDINAL && reply->value_len == 1) { - const uint32_t value = *reinterpret_cast(xcb_get_property_value(reply.data())); - ElectricBorder border = ElectricNone; - switch (value) { - case 0: - border = ElectricTop; - break; - case 1: - border = ElectricRight; - break; - case 2: - border = ElectricBottom; - break; - case 3: - border = ElectricLeft; - break; - } - if (border != ElectricNone) { - hideClient(true); - ScreenEdges::self()->reserve(this, border); - } else { - // property value is incorrect, delete the property - // so that the client knows that it is not hidden - xcb_delete_property(connection(), window(), atoms->kde_screen_edge_show); - } - - } else if (reply->type == XCB_ATOM_NONE) { - // the property got deleted, show the client again - restore(); - } - } else { - restore(); } } diff --git a/effects.cpp b/effects.cpp index 1963c48216..4d882088e2 100644 --- a/effects.cpp +++ b/effects.cpp @@ -166,10 +166,8 @@ void ScreenLockerWatcher::setLocked(bool activated) static QByteArray readWindowProperty(xcb_window_t win, xcb_atom_t atom, xcb_atom_t type, int format) { uint32_t len = 32768; - xcb_connection_t *c = connection(); for (;;) { - const auto cookie = xcb_get_property_unchecked(c, false, win, atom, XCB_ATOM_ANY, 0, len); - ScopedCPointer prop(xcb_get_property_reply(c, cookie, nullptr)); + Xcb::Property prop(false, win, atom, XCB_ATOM_ANY, 0, len); if (prop.isNull()) { // get property failed return QByteArray(); @@ -178,12 +176,7 @@ static QByteArray readWindowProperty(xcb_window_t win, xcb_atom_t atom, xcb_atom len *= 2; continue; } - if (prop->type == type && prop->format == format) { - return QByteArray(reinterpret_cast< const char* >(xcb_get_property_value(prop.data())), - xcb_get_property_value_length(prop.data())); - } else { - return QByteArray(); - } + return prop.toByteArray(format, type); } } diff --git a/shadow.cpp b/shadow.cpp index 10c95946a7..ca13947f1b 100644 --- a/shadow.cpp +++ b/shadow.cpp @@ -63,12 +63,9 @@ Shadow *Shadow::createShadow(Toplevel *toplevel) QVector< uint32_t > Shadow::readX11ShadowProperty(xcb_window_t id) { QVector ret; - xcb_connection_t *c = connection(); - const auto cookie = xcb_get_property_unchecked(c, false, id, atoms->kde_net_wm_shadow, - XCB_ATOM_CARDINAL, 0, 12); - ScopedCPointer prop(xcb_get_property_reply(c, cookie, nullptr)); - if (!prop.isNull() && prop->type == XCB_ATOM_CARDINAL && prop->format == 32 ) { - uint32_t* shadow = reinterpret_cast< uint32_t* >(xcb_get_property_value(prop.data())); + Xcb::Property property(false, id, atoms->kde_net_wm_shadow, XCB_ATOM_CARDINAL, 0, 12); + uint32_t *shadow = property.value(); + if (shadow) { ret.reserve(12); for (int i=0; i<12; ++i) { ret << shadow[i]; diff --git a/toplevel.cpp b/toplevel.cpp index a945757a87..b88ef04672 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -145,24 +145,10 @@ QRect Toplevel::visibleRect() const return r.translated(geometry().topLeft()); } -/*! - Returns WM_CLIENT_LEADER property for a given window. - */ -xcb_window_t Toplevel::staticWmClientLeader(xcb_window_t w) -{ - xcb_connection_t *c = connection(); - auto cookie = xcb_get_property_unchecked(c, false, w, atoms->wm_client_leader, XCB_ATOM_WINDOW, 0, 10000); - ScopedCPointer prop(xcb_get_property_reply(c, cookie, nullptr)); - if (prop.isNull() || prop->value_len <= 0) { - return w; - } - return static_cast(xcb_get_property_value(prop.data()))[0]; -} - - void Toplevel::getWmClientLeader() { - wmClientLeaderWin = staticWmClientLeader(window()); + Xcb::Property prop(false, window(), atoms->wm_client_leader, XCB_ATOM_WINDOW, 0, 10000); + wmClientLeaderWin = prop.value(window()); } /*! @@ -171,9 +157,9 @@ void Toplevel::getWmClientLeader() */ QByteArray Toplevel::sessionId() const { - QByteArray result = getStringProperty(window(), atoms->sm_client_id); + QByteArray result = Xcb::StringProperty(window(), atoms->sm_client_id); if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin != window()) - result = getStringProperty(wmClientLeaderWin, atoms->sm_client_id); + result = Xcb::StringProperty(wmClientLeaderWin, atoms->sm_client_id); return result; } @@ -183,9 +169,10 @@ QByteArray Toplevel::sessionId() const */ QByteArray Toplevel::wmCommand() { - QByteArray result = getStringProperty(window(), XCB_ATOM_WM_COMMAND, ' '); + QByteArray result = Xcb::StringProperty(window(), XCB_ATOM_WM_COMMAND); if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin != window()) - result = getStringProperty(wmClientLeaderWin, XCB_ATOM_WM_COMMAND, ' '); + result = Xcb::StringProperty(wmClientLeaderWin, XCB_ATOM_WM_COMMAND); + result.replace(0, ' '); return result; } @@ -438,16 +425,8 @@ xcb_window_t Toplevel::frameId() const void Toplevel::getSkipCloseAnimation() { - auto cookie = xcb_get_property_unchecked(connection(), false, window(), atoms->kde_skip_close_animation, XCB_ATOM_CARDINAL, 0, 1); - ScopedCPointer reply(xcb_get_property_reply(connection(), cookie, nullptr)); - bool newValue = false; - if (!reply.isNull()) { - if (reply->format == 32 && reply->type == XCB_ATOM_CARDINAL && reply->value_len == 1) { - const uint32_t value = *reinterpret_cast(xcb_get_property_value(reply.data())); - newValue = (value != 0); - } - } - setSkipCloseAnimation(newValue); + Xcb::Property property(false, window(), atoms->kde_skip_close_animation, XCB_ATOM_CARDINAL, 0, 1); + setSkipCloseAnimation(property.toBool()); } bool Toplevel::skipsCloseAnimation() const diff --git a/toplevel.h b/toplevel.h index 305f667be7..2fce2c1060 100644 --- a/toplevel.h +++ b/toplevel.h @@ -438,7 +438,6 @@ protected: bool m_isDamaged; private: - static xcb_window_t staticWmClientLeader(xcb_window_t); // when adding new data members, check also copyToDeleted() Xcb::Window m_client; xcb_damage_damage_t damage_handle; diff --git a/utils.cpp b/utils.cpp index 88ab2d8d3b..856818d214 100644 --- a/utils.cpp +++ b/utils.cpp @@ -67,28 +67,6 @@ StrutRect::StrutRect(const StrutRect& other) #endif -QByteArray getStringProperty(xcb_window_t w, xcb_atom_t prop, char separator) -{ - const xcb_get_property_cookie_t c = xcb_get_property_unchecked(connection(), false, w, prop, - XCB_ATOM_STRING, 0, 10000); - ScopedCPointer property(xcb_get_property_reply(connection(), c, NULL)); - if (property.isNull() || property->type == XCB_ATOM_NONE) { - return QByteArray(); - } - char *data = static_cast(xcb_get_property_value(property.data())); - int length = property->value_len; - if (data && separator) { - for (uint32_t i = 0; i < property->value_len; ++i) { - if (!data[i] && i + 1 < property->value_len) { - data[i] = separator; - } else { - length = i; - } - } - } - return QByteArray(data, length); -} - #ifndef KCMRULES /* Updates xTime(). This used to simply fetch current timestamp from the server, diff --git a/utils.h b/utils.h index 198db9253f..e96adbfeb4 100644 --- a/utils.h +++ b/utils.h @@ -122,7 +122,6 @@ enum ShadeMode { template using ScopedCPointer = QScopedPointer; -QByteArray getStringProperty(xcb_window_t w, xcb_atom_t prop, char separator = 0); void updateXTime(); void grabXServer(); void ungrabXServer(); diff --git a/xcbutils.h b/xcbutils.h index 61611b7696..2762f2e0ac 100644 --- a/xcbutils.h +++ b/xcbutils.h @@ -592,11 +592,199 @@ public: }; XCB_WRAPPER_DATA(PropertyData, xcb_get_property, uint8_t, xcb_window_t, xcb_atom_t, xcb_atom_t, uint32_t, uint32_t) -class TransientFor : public Wrapper +class Property : public Wrapper +{ +public: + Property() + : Wrapper() + , m_type(XCB_ATOM_NONE) + { + } + Property(const Property &other) + : Wrapper(other) + , m_type(other.m_type) + { + } + explicit Property(uint8_t _delete, xcb_window_t window, xcb_atom_t property, xcb_atom_t type, uint32_t long_offset, uint32_t long_length) + : Wrapper(window, _delete, window, property, type, long_offset, long_length) + , m_type(type) + { + } + Property &operator=(const Property &other) { + Wrapper::operator=(other); + m_type = other.m_type; + return *this; + } + + /** + * @brief Overloaded method for convenience. + * + * Uses the type which got passed into the ctor and derives the format from the sizeof(T). + * Note: for the automatic format detection the size of the type T may not vary between + * architectures. Thus one needs to use e.g. uint32_t instead of long. In general all xcb + * data types can be used, all Xlib data types can not be used. + * + * @param defaultValue The default value to return in case of error + * @param ok Set to @c false in case of error, @c true in case of success + * @return The read value or @p defaultValue in error case + */ + template + inline typename std::enable_if::value, T>::type value(T defaultValue = T(), bool *ok = nullptr) { + return value(sizeof(T) * 8, m_type, defaultValue, ok); + } + /** + * @brief Reads the property as a POD type. + * + * Returns the first value of the property data. In case of @p format or @p type mismatch + * the @p defaultValue is returned. The optional argument @p ok is set + * to @c false in case of error and to @c true in case of successful reading of + * the property. + * + * @param format The expected format of the property value, e.g. 32 for XCB_ATOM_CARDINAL + * @param type The expected type of the property value, e.g. XCB_ATOM_CARDINAL + * @param defaultValue The default value to return in case of error + * @param ok Set to @c false in case of error, @c true in case of success + * @return The read value or @p defaultValue in error case + **/ + template + inline typename std::enable_if::value, T>::type value(uint8_t format, xcb_atom_t type, T defaultValue = T(), bool *ok = nullptr) { + T *reply = value(format, type, nullptr, ok); + if (!reply) { + return defaultValue; + } + return reply[0]; + } + /** + * @brief Overloaded method for convenience. + * + * Uses the type which got passed into the ctor and derives the format from the sizeof(T). + * Note: for the automatic format detection the size of the type T may not vary between + * architectures. Thus one needs to use e.g. uint32_t instead of long. In general all xcb + * data types can be used, all Xlib data types can not be used. + * + * @param defaultValue The default value to return in case of error + * @param ok Set to @c false in case of error, @c true in case of success + * @return The read value or @p defaultValue in error case + */ + template + inline typename std::enable_if::value, T>::type value(T defaultValue = nullptr, bool *ok = nullptr) { + return value(sizeof(typename std::remove_pointer::type) * 8, m_type, defaultValue, ok); + } + /** + * @brief Reads the property as an array of T. + * + * This method is an overload for the case that T is a pointer type. + * + * Return the property value casted to the pointer type T. In case of @p format + * or @p type mismatch the @p defaultValue is returned. Also if the value length + * is @c 0 the @p defaultValue is returned. The optional argument @p ok is set + * to @c false in case of error and to @c true in case of successful reading of + * the property. + * + * @param format The expected format of the property value, e.g. 32 for XCB_ATOM_CARDINAL + * @param type The expected type of the property value, e.g. XCB_ATOM_CARDINAL + * @param defaultValue The default value to return in case of error + * @param ok Set to @c false in case of error, @c true in case of success + * @return The read value or @p defaultValue in error case + **/ + template + inline typename std::enable_if::value, T>::type value(uint8_t format, xcb_atom_t type, T defaultValue = nullptr, bool *ok = nullptr) { + if (ok) { + *ok = false; + } + const PropertyData::reply_type *reply = data(); + if (!reply) { + return defaultValue; + } + if (reply->type != type) { + return defaultValue; + } + if (reply->format != format) { + return defaultValue; + } + if (xcb_get_property_value_length(reply) == 0) { + return defaultValue; + } + if (ok) { + *ok = true; + } + return reinterpret_cast(xcb_get_property_value(reply)); + } + /** + * @brief Reads the property as string and returns a QByteArray. + * + * In case of error this method returns a null QByteArray. + **/ + inline QByteArray toByteArray(uint8_t format = 8, xcb_atom_t type = XCB_ATOM_STRING, bool *ok = nullptr) { + const char *reply = value(format, type, nullptr, ok); + if (!reply) { + return QByteArray(); + } + return QByteArray(reply, xcb_get_property_value_length(data())); + } + /** + * @brief Overloaded method for convenience. + **/ + inline QByteArray toByteArray(bool *ok) { + return toByteArray(8, m_type, ok); + } + /** + * @brief Reads the property as a boolean value. + * + * If the property reply length is @c 1 the first element is interpreted as a boolean + * value returning @c true for any value unequal to @c 0 and @c false otherwise. + * + * In case of error this method returns @c false. Thus it is not possible to distinguish + * between error case and a read @c false value. Use the optional argument @p ok to + * distinguish the error case. + * + * @param format Expected format. Defaults to 32. + * @param type Expected type Defaults to XCB_ATOM_CARDINAL. + * @param ok Set to @c false in case of error, @c true in case of success + * @return bool The first element interpreted as a boolean value or @c false in error case + * @see value + */ + inline bool toBool(uint8_t format = 32, xcb_atom_t type = XCB_ATOM_CARDINAL, bool *ok = nullptr) { + bool *reply = value(format, type, nullptr, ok); + if (!reply) { + return false; + } + if (data()->value_len != 1) { + if (ok) { + *ok = false; + } + return false; + } + return reply[0] != 0; + } + /** + * @brief Overloaded method for convenience. + **/ + inline bool toBool(bool *ok) { + return toBool(32, m_type, ok); + } +private: + xcb_atom_t m_type; +}; + +class StringProperty : public Property +{ +public: + StringProperty() = default; + explicit StringProperty(xcb_window_t w, xcb_atom_t p) + : Property(false, w, p, XCB_ATOM_STRING, 0, 10000) + { + } + operator QByteArray() { + return toByteArray(); + } +}; + +class TransientFor : public Property { public: explicit TransientFor(WindowId window) - : Wrapper(window, 0, window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 1) + : Property(0, window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 1) { } @@ -606,15 +794,12 @@ public: * @returns @c true on success, @c false otherwise **/ inline bool getTransientFor(WindowId *prop) { - if (isNull()) { + WindowId *windows = value(); + if (!windows) { return false; } - const xcb_get_property_reply_t *reply = data(); - if (!reply || reply->type != XCB_ATOM_WINDOW || reply->format != 32 || reply->length == 0) - return false; - - *prop = *reinterpret_cast(xcb_get_property_value(reply)); + *prop = *windows; return true; } };