Introduce QXcbXSettings

This allows you to easily retrieve properties in the XSETTINGS
specification. It is also possible to add listeners to get notified when
a specific property changes.

XSETTINGS is lazy initialized, so it will not be instansiated before
someone uses it. For now the intended use is a fallback for finding
cursor theme

Change-Id: Id47f0613f5876424cd47d721b40da17d3f63429e
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
This commit is contained in:
Jørgen Lind 2013-04-26 08:42:17 +02:00 committed by The Qt Project
parent 3c69751ef2
commit 0f31a5d91f
7 changed files with 372 additions and 2 deletions

View File

@ -1397,6 +1397,7 @@ static const char * xcb_atomnames = {
#if XCB_USE_MAEMO_WINDOW_PROPERTIES
"_MEEGOTOUCH_ORIENTATION_ANGLE\0"
#endif
"_XSETTINGS_SETTINGS"
};
xcb_atom_t QXcbConnection::atom(QXcbAtom::Atom atom)

View File

@ -269,6 +269,7 @@ namespace QXcbAtom {
#if XCB_USE_MAEMO_WINDOW_PROPERTIES
MeegoTouchOrientationAngle,
#endif
_XSETTINGS_SETTINGS,
NPredefinedAtoms,

View File

@ -44,6 +44,7 @@
#include "qxcbcursor.h"
#include "qxcbimage.h"
#include "qnamespace.h"
#include "qxcbxsettings.h"
#include <stdio.h>
@ -68,6 +69,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr,
, m_refreshRate(60)
, m_forcedDpi(-1)
, m_hintStyle(QFontEngine::HintStyle(-1))
, m_xSettings(0)
{
if (connection->hasXRandr())
xcb_randr_select_input(xcb_connection(), screen()->root, true);
@ -580,4 +582,12 @@ void QXcbScreen::readXResources()
}
}
QXcbXSettings *QXcbScreen::xSettings() const
{
if (!m_xSettings) {
QXcbScreen *self = const_cast<QXcbScreen *>(this);
self->m_xSettings = new QXcbXSettings(self);
}
return m_xSettings;
}
QT_END_NAMESPACE

View File

@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE
class QXcbConnection;
class QXcbCursor;
class QXcbXSettings;
class QXcbScreen : public QXcbObject, public QPlatformScreen
{
@ -102,6 +103,9 @@ public:
void readXResources();
QFontEngine::HintStyle hintStyle() const { return m_hintStyle; }
QXcbXSettings *xSettings() const;
private:
static bool xResource(const QByteArray &identifier,
const QByteArray &expectedIdentifier,
@ -127,6 +131,7 @@ private:
int m_refreshRate;
int m_forcedDpi;
QFontEngine::HintStyle m_hintStyle;
QXcbXSettings *m_xSettings;
};
QT_END_NAMESPACE

View File

@ -0,0 +1,280 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins 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 Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qxcbxsettings.h"
#include <QtCore/QByteArray>
#include <X11/extensions/XIproto.h>
QT_BEGIN_NAMESPACE
/* Implementation of http://standards.freedesktop.org/xsettings-spec/xsettings-0.5.html */
enum XSettingsType {
XSettingsTypeInteger = 0,
XSettingsTypeString = 1,
XSettingsTypeColor = 2
};
class QXcbXSettingsCallback
{
public:
QXcbXSettings::PropertyChangeFunc func;
void *handle;
};
class QXcbXSettingsPropertyValue
{
public:
QXcbXSettingsPropertyValue()
: last_change_serial(-1)
{}
void updateValue(QXcbScreen *screen, const QByteArray &name, const QVariant &value, int last_change_serial)
{
if (last_change_serial <= this->last_change_serial)
return;
this->value = value;
this->last_change_serial = last_change_serial;
QLinkedList<QXcbXSettingsCallback>::const_iterator it = callback_links.begin();
for (;it != callback_links.end();++it) {
it->func(screen,name,value,it->handle);
}
}
void addCallback(QXcbXSettings::PropertyChangeFunc func, void *handle)
{
QXcbXSettingsCallback callback;
callback.func = func;
callback.handle = handle;
callback_links.append(callback);
}
QVariant value;
int last_change_serial;
QLinkedList<QXcbXSettingsCallback> callback_links;
};
class QXcbXSettingsPrivate
{
public:
QXcbXSettingsPrivate(QXcbScreen *screen)
: screen(screen)
{
}
QByteArray getSettings()
{
QXcbConnectionGrabber connectionGrabber(screen->connection());
int offset = 0;
QByteArray settings;
xcb_atom_t _xsettings_atom = screen->connection()->atom(QXcbAtom::_XSETTINGS_SETTINGS);
while (1) {
xcb_get_property_cookie_t get_prop_cookie =
xcb_get_property_unchecked(screen->xcb_connection(),
false,
x_settings_window,
_xsettings_atom,
_xsettings_atom,
offset/4,
8192);
xcb_get_property_reply_t *reply = xcb_get_property_reply(screen->xcb_connection(), get_prop_cookie, NULL);
bool more = false;
if (!reply)
return settings;
settings += QByteArray((const char *)xcb_get_property_value(reply), xcb_get_property_value_length(reply));
offset += xcb_get_property_value_length(reply);
more = reply->bytes_after != 0;
free(reply);
if (!more)
break;
}
return settings;
}
static int round_to_nearest_multiple_of_4(int value)
{
int remainder = value % 4;
if (!remainder)
return value;
return value + 4 - remainder;
}
void populateSettings(const QByteArray &xSettings)
{
if (xSettings.length() < 12)
return;
// we ignore byteorder for now
char byteOrder = xSettings.at(1);
Q_UNUSED(byteOrder);
uint serial = *reinterpret_cast<const uint *>(xSettings.mid(4,4).constData());
serial = serial;
uint number_of_settings = *reinterpret_cast<const uint *>(xSettings.mid(8,4).constData());
const char *data = xSettings.constData() + 12;
size_t offset = 0;
for (uint i = 0; i < number_of_settings; i++) {
int local_offset = 0;
XSettingsType type = static_cast<XSettingsType>(*reinterpret_cast<const quint8 *>(data + offset));
local_offset += 2;
quint16 name_len = *reinterpret_cast<const quint16 *>(data + offset + local_offset);
local_offset += 2;
QByteArray name(data + offset + local_offset, name_len);
local_offset += round_to_nearest_multiple_of_4(name_len);
int last_change_serial = *reinterpret_cast<const int *>(data + offset + local_offset);
Q_UNUSED(last_change_serial);
local_offset += 4;
QVariant value;
if (type == XSettingsTypeString) {
int value_length = *reinterpret_cast<const int *>(data + offset + local_offset);
local_offset+=4;
QByteArray value_string(data + offset + local_offset, value_length);
value.setValue(value_string);
local_offset += round_to_nearest_multiple_of_4(value_length);
} else if (type == XSettingsTypeInteger) {
int value_length = *reinterpret_cast<const int *>(data + offset + local_offset);
local_offset += 4;
value.setValue(value_length);
} else if (type == XSettingsTypeColor) {
quint16 red = *reinterpret_cast<const quint16 *>(data + offset + local_offset);
local_offset += 2;
quint16 green = *reinterpret_cast<const quint16 *>(data + offset + local_offset);
local_offset += 2;
quint16 blue = *reinterpret_cast<const quint16 *>(data + offset + local_offset);
local_offset += 2;
quint16 alpha= *reinterpret_cast<const quint16 *>(data + offset + local_offset);
local_offset += 2;
QColor color_value(red,green,blue,alpha);
value.setValue(color_value);
}
offset += local_offset;
settings[name].updateValue(screen,name,value,last_change_serial);
}
}
QXcbScreen *screen;
xcb_window_t x_settings_window;
int serial;
QMap<QByteArray, QXcbXSettingsPropertyValue> settings;
};
QXcbXSettings::QXcbXSettings(QXcbScreen *screen)
: d_ptr(new QXcbXSettingsPrivate(screen))
{
QByteArray settings_atom_for_screen("_XSETTINGS_S");
settings_atom_for_screen.append(QByteArray::number(screen->screenNumber()));
xcb_intern_atom_cookie_t atom_cookie = xcb_intern_atom(screen->xcb_connection(),
false,
settings_atom_for_screen.length(),
settings_atom_for_screen.constData());
xcb_intern_atom_reply_t *atom_reply = xcb_intern_atom_reply(screen->xcb_connection(),atom_cookie,NULL);
xcb_atom_t selection_owner_atom = atom_reply->atom;
free(atom_reply);
xcb_get_selection_owner_cookie_t selection_cookie =
xcb_get_selection_owner(screen->xcb_connection(), selection_owner_atom);
xcb_get_selection_owner_reply_t *selection_result =
xcb_get_selection_owner_reply(screen->xcb_connection(), selection_cookie, NULL);
d_ptr->x_settings_window = selection_result->owner;
free(selection_result);
const uint32_t event_mask[] = { XCB_EVENT_MASK_STRUCTURE_NOTIFY|XCB_EVENT_MASK_PROPERTY_CHANGE };
xcb_change_window_attributes(screen->xcb_connection(),d_ptr->x_settings_window,XCB_CW_EVENT_MASK,event_mask);
d_ptr->populateSettings(d_ptr->getSettings());
}
void QXcbXSettings::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event)
{
Q_D(QXcbXSettings);
if (event->window != d->x_settings_window)
return;
d->populateSettings(d->getSettings());
}
void QXcbXSettings::registerCallbackForProperty(const QByteArray &property, QXcbXSettings::PropertyChangeFunc func, void *handle)
{
Q_D(QXcbXSettings);
d->settings[property].addCallback(func,handle);
}
void QXcbXSettings::removeCallbackForHandle(const QByteArray &property, void *handle)
{
Q_D(QXcbXSettings);
QXcbXSettingsPropertyValue &value = d->settings[property];
QLinkedList<QXcbXSettingsCallback>::iterator it = value.callback_links.begin();
while (it != value.callback_links.end()) {
if (it->handle == handle)
it = value.callback_links.erase(it);
else
++it;
}
}
void QXcbXSettings::removeCallbackForHandle(void *handle)
{
Q_D(QXcbXSettings);
for (QMap<QByteArray, QXcbXSettingsPropertyValue>::const_iterator it = d->settings.cbegin();
it != d->settings.cend(); ++it) {
removeCallbackForHandle(it.key(),handle);
}
}
QVariant QXcbXSettings::setting(const QByteArray &property) const
{
Q_D(const QXcbXSettings);
return d->settings.value(property).value;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,71 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins 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 Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QXCBXSETTINGS_H
#define QXCBXSETTINGS_H
#include "qxcbscreen.h"
QT_BEGIN_NAMESPACE
class QXcbXSettingsPrivate;
class QXcbXSettings : public QXcbWindowEventListener
{
Q_DECLARE_PRIVATE(QXcbXSettings)
public:
QXcbXSettings(QXcbScreen *screen);
QVariant setting(const QByteArray &property) const;
typedef void (*PropertyChangeFunc)(QXcbScreen *screen, const QByteArray &name, const QVariant &property, void *handle);
void registerCallbackForProperty(const QByteArray &property, PropertyChangeFunc func, void *handle);
void removeCallbackForHandle(const QByteArray &property, void *handle);
void removeCallbackForHandle(void *handle);
void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) Q_DECL_OVERRIDE;
private:
QXcbXSettingsPrivate *d_ptr;
};
QT_END_NAMESPACE
#endif // QXCBXSETTINGS_H

View File

@ -20,7 +20,8 @@ SOURCES = \
main.cpp \
qxcbnativeinterface.cpp \
qxcbcursor.cpp \
qxcbimage.cpp
qxcbimage.cpp \
qxcbxsettings.cpp
HEADERS = \
qxcbclipboard.h \
@ -36,7 +37,8 @@ HEADERS = \
qxcbwmsupport.h \
qxcbnativeinterface.h \
qxcbcursor.h \
qxcbimage.h
qxcbimage.h \
qxcbxsettings.h
LIBS += -ldl