From 0f31a5d91f13bd6a574bed8db894a7ebb2813292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lind?= Date: Fri, 26 Apr 2013 08:42:17 +0200 Subject: Introduce QXcbXSettings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/plugins/platforms/xcb/qxcbconnection.cpp | 1 + src/plugins/platforms/xcb/qxcbconnection.h | 1 + src/plugins/platforms/xcb/qxcbscreen.cpp | 10 + src/plugins/platforms/xcb/qxcbscreen.h | 5 + src/plugins/platforms/xcb/qxcbxsettings.cpp | 280 +++++++++++++++++++++++++++ src/plugins/platforms/xcb/qxcbxsettings.h | 71 +++++++ src/plugins/platforms/xcb/xcb-plugin.pro | 6 +- 7 files changed, 372 insertions(+), 2 deletions(-) create mode 100644 src/plugins/platforms/xcb/qxcbxsettings.cpp create mode 100644 src/plugins/platforms/xcb/qxcbxsettings.h diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 7381c8c886..db2179c73f 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -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) diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 3aa13a8b3a..cba26e148c 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -269,6 +269,7 @@ namespace QXcbAtom { #if XCB_USE_MAEMO_WINDOW_PROPERTIES MeegoTouchOrientationAngle, #endif + _XSETTINGS_SETTINGS, NPredefinedAtoms, diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index a6ead49a27..37c6c97bc4 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -44,6 +44,7 @@ #include "qxcbcursor.h" #include "qxcbimage.h" #include "qnamespace.h" +#include "qxcbxsettings.h" #include @@ -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(this); + self->m_xSettings = new QXcbXSettings(self); + } + return m_xSettings; +} QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index 0382be8a29..c36492db64 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -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 diff --git a/src/plugins/platforms/xcb/qxcbxsettings.cpp b/src/plugins/platforms/xcb/qxcbxsettings.cpp new file mode 100644 index 0000000000..7ffd3e105f --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbxsettings.cpp @@ -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 + +#include + +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::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 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(xSettings.mid(4,4).constData()); + serial = serial; + uint number_of_settings = *reinterpret_cast(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(*reinterpret_cast(data + offset)); + local_offset += 2; + + quint16 name_len = *reinterpret_cast(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(data + offset + local_offset); + Q_UNUSED(last_change_serial); + local_offset += 4; + + QVariant value; + if (type == XSettingsTypeString) { + int value_length = *reinterpret_cast(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(data + offset + local_offset); + local_offset += 4; + value.setValue(value_length); + } else if (type == XSettingsTypeColor) { + quint16 red = *reinterpret_cast(data + offset + local_offset); + local_offset += 2; + quint16 green = *reinterpret_cast(data + offset + local_offset); + local_offset += 2; + quint16 blue = *reinterpret_cast(data + offset + local_offset); + local_offset += 2; + quint16 alpha= *reinterpret_cast(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 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::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::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 diff --git a/src/plugins/platforms/xcb/qxcbxsettings.h b/src/plugins/platforms/xcb/qxcbxsettings.h new file mode 100644 index 0000000000..16fed862bc --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbxsettings.h @@ -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 diff --git a/src/plugins/platforms/xcb/xcb-plugin.pro b/src/plugins/platforms/xcb/xcb-plugin.pro index f01fa90e2d..82995286c4 100644 --- a/src/plugins/platforms/xcb/xcb-plugin.pro +++ b/src/plugins/platforms/xcb/xcb-plugin.pro @@ -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 -- cgit v1.2.3