[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
icc-effect-5.14.5
Martin Gräßlin 2014-04-15 10:09:25 +02:00
parent 8d3b12b928
commit b45eeae352
10 changed files with 334 additions and 145 deletions

View File

@ -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<xcb_get_property_reply_t> 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_timestamp_t*>(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<xcb_timestamp_t>(-1);
}
void Client::demandAttention(bool set)

View File

@ -23,6 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Qt
#include <QApplication>
#include <QtTest/QtTest>
#include <netwm.h>
// xcb
#include <xcb/xcb.h>
@ -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<xcb_window_t>(32, XCB_ATOM_WINDOW, XCB_WINDOW_NONE, &ok), xcb_window_t(XCB_WINDOW_NONE));
QVERIFY(!ok);
ok = true;
QCOMPARE(transient.value<xcb_window_t>(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<xcb_window_t>(32, XCB_ATOM_WINDOW, XCB_WINDOW_NONE, &ok), m_testWindow);
QVERIFY(ok);
ok = false;
QCOMPARE(realTransient.value<xcb_window_t>(XCB_WINDOW_NONE, &ok), m_testWindow);
QVERIFY(ok);
ok = false;
QCOMPARE(realTransient.value<xcb_window_t>(), m_testWindow);
QCOMPARE(realTransient.value<xcb_window_t*>(nullptr, &ok)[0], m_testWindow);
QVERIFY(ok);
QCOMPARE(realTransient.value<xcb_window_t*>()[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<const char*>());
QCOMPARE(prop.value<const char*>("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<const char*>(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<const char*>());
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"

View File

@ -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<xcb_get_property_reply_t> 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<xcb_get_property_reply_t> 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<const char*>(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<xcb_get_property_reply_t> 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<uint32_t>(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<uint32_t*>(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();
}
}

View File

@ -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<xcb_get_property_reply_t> 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);
}
}

View File

@ -63,12 +63,9 @@ Shadow *Shadow::createShadow(Toplevel *toplevel)
QVector< uint32_t > Shadow::readX11ShadowProperty(xcb_window_t id)
{
QVector<uint32_t> 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<xcb_get_property_reply_t> 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<uint32_t*>();
if (shadow) {
ret.reserve(12);
for (int i=0; i<12; ++i) {
ret << shadow[i];

View File

@ -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<xcb_get_property_reply_t> prop(xcb_get_property_reply(c, cookie, nullptr));
if (prop.isNull() || prop->value_len <= 0) {
return w;
}
return static_cast<xcb_window_t*>(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<xcb_window_t>(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<xcb_get_property_reply_t> 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<uint32_t*>(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

View File

@ -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;

View File

@ -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<xcb_get_property_reply_t> property(xcb_get_property_reply(connection(), c, NULL));
if (property.isNull() || property->type == XCB_ATOM_NONE) {
return QByteArray();
}
char *data = static_cast<char*>(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,

View File

@ -122,7 +122,6 @@ enum ShadeMode {
template <typename T> using ScopedCPointer = QScopedPointer<T, QScopedPointerPodDeleter>;
QByteArray getStringProperty(xcb_window_t w, xcb_atom_t prop, char separator = 0);
void updateXTime();
void grabXServer();
void ungrabXServer();

View File

@ -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<PropertyData, uint8_t, xcb_window_t, xcb_atom_t, xcb_atom_t, uint32_t, uint32_t>
class Property : public Wrapper<PropertyData, uint8_t, xcb_window_t, xcb_atom_t, xcb_atom_t, uint32_t, uint32_t>
{
public:
Property()
: Wrapper<PropertyData, uint8_t, xcb_window_t, xcb_atom_t, xcb_atom_t, uint32_t, uint32_t>()
, m_type(XCB_ATOM_NONE)
{
}
Property(const Property &other)
: Wrapper<PropertyData, uint8_t, xcb_window_t, xcb_atom_t, xcb_atom_t, uint32_t, uint32_t>(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<PropertyData, uint8_t, xcb_window_t, xcb_atom_t, xcb_atom_t, uint32_t, uint32_t>(window, _delete, window, property, type, long_offset, long_length)
, m_type(type)
{
}
Property &operator=(const Property &other) {
Wrapper<PropertyData, uint8_t, xcb_window_t, xcb_atom_t, xcb_atom_t, uint32_t, uint32_t>::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 <typename T>
inline typename std::enable_if<!std::is_pointer<T>::value, T>::type value(T defaultValue = T(), bool *ok = nullptr) {
return value<T>(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 <typename T>
inline typename std::enable_if<!std::is_pointer<T>::value, T>::type value(uint8_t format, xcb_atom_t type, T defaultValue = T(), bool *ok = nullptr) {
T *reply = value<T*>(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 <typename T>
inline typename std::enable_if<std::is_pointer<T>::value, T>::type value(T defaultValue = nullptr, bool *ok = nullptr) {
return value<T>(sizeof(typename std::remove_pointer<T>::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 <typename T>
inline typename std::enable_if<std::is_pointer<T>::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<T>(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<const char*>(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<bool*>(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<PropertyData, uint8_t, xcb_window_t, xcb_atom_t, xcb_atom_t, uint32_t, uint32_t>(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<WindowId*>();
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<WindowId *>(xcb_get_property_value(reply));
*prop = *windows;
return true;
}
};