summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb/qxcbxsettings.cpp
blob: 6b62864addf138b54b3b77e002116074aeea7528 (plain)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
// Copyright (C) 2016 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#include"qxcbxsettings.h"#include <QtCore/QByteArray>#include <QtCore/QtEndian>#include <vector>#include <algorithm> QT_BEGIN_NAMESPACE /* Implementation of http://standards.freedesktop.org/xsettings-spec/xsettings-0.5.html */enum XSettingsType { XSettingsTypeInteger =0, XSettingsTypeString =1, XSettingsTypeColor =2};struct QXcbXSettingsCallback {QXcbXSettings::PropertyChangeFunc func;void*handle;};class QXcbXSettingsPropertyValue {public:QXcbXSettingsPropertyValue():last_change_serial(-1){}voidupdateValue(QXcbVirtualDesktop *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;for(constauto&callback : callback_links) callback.func(screen, name, value, callback.handle);}voidaddCallback(QXcbXSettings::PropertyChangeFunc func,void*handle){ QXcbXSettingsCallback callback = { func, handle }; callback_links.push_back(callback);} QVariant value;int last_change_serial;std::vector<QXcbXSettingsCallback> callback_links;};class QXcbXSettingsPrivate {public:QXcbXSettingsPrivate(QXcbVirtualDesktop *screen):screen(screen),initialized(false){} QByteArray getSettings(){ QXcbConnectionGrabber connectionGrabber(screen->connection());int offset =0; QByteArray settings; xcb_atom_t _xsettings_atom = screen->connection()->atom(QXcbAtom::Atom_XSETTINGS_SETTINGS);while(1) {auto reply =Q_XCB_REPLY_UNCHECKED(xcb_get_property, screen->xcb_connection(),false, x_settings_window, _xsettings_atom, _xsettings_atom, offset/4,8192);bool more =false;if(!reply)return settings;constauto property_value_length =xcb_get_property_value_length(reply.get()); settings.append(static_cast<const char*>(xcb_get_property_value(reply.get())), property_value_length); offset += property_value_length; more = reply->bytes_after !=0;if(!more)break;}return settings;}static intround_to_nearest_multiple_of_4(int value){int remainder = value %4;if(!remainder)return value;return value +4- remainder;}voidpopulateSettings(const QByteArray &xSettings){if(xSettings.size() <12)return;char byteOrder = xSettings.at(0);if(byteOrder != XCB_IMAGE_ORDER_LSB_FIRST && byteOrder != XCB_IMAGE_ORDER_MSB_FIRST) {qWarning("ByteOrder byte %d not 0 or 1", byteOrder);return;}#define ADJUST_BO(b, t, x) \ ((b == XCB_IMAGE_ORDER_LSB_FIRST) ? \ qFromLittleEndian<t>(x) : \ qFromBigEndian<t>(x))#define VALIDATE_LENGTH(x) \ if ((size_t)xSettings.length() < (offset + local_offset + 12 + x)) { \ qWarning("Length %d runs past end of data", x); \ return; \ } uint number_of_settings =ADJUST_BO(byteOrder, quint32, 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;VALIDATE_LENGTH(2); XSettingsType type =static_cast<XSettingsType>(*reinterpret_cast<const quint8 *>(data + offset)); local_offset +=2;VALIDATE_LENGTH(2); quint16 name_len =ADJUST_BO(byteOrder, quint16, data + offset + local_offset); local_offset +=2;VALIDATE_LENGTH(name_len); QByteArray name(data + offset + local_offset, name_len); local_offset +=round_to_nearest_multiple_of_4(name_len);VALIDATE_LENGTH(4);int last_change_serial =ADJUST_BO(byteOrder, qint32, data + offset + local_offset);Q_UNUSED(last_change_serial); local_offset +=4; QVariant value;if(type == XSettingsTypeString) {VALIDATE_LENGTH(4);int value_length =ADJUST_BO(byteOrder, qint32, data + offset + local_offset); local_offset+=4;VALIDATE_LENGTH(value_length); 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) {VALIDATE_LENGTH(4);int value_length =ADJUST_BO(byteOrder, qint32, data + offset + local_offset); local_offset +=4; value.setValue(value_length);}else if(type == XSettingsTypeColor) {VALIDATE_LENGTH(2*4); quint16 red =ADJUST_BO(byteOrder, quint16, data + offset + local_offset); local_offset +=2; quint16 green =ADJUST_BO(byteOrder, quint16, data + offset + local_offset); local_offset +=2; quint16 blue =ADJUST_BO(byteOrder, quint16, data + offset + local_offset); local_offset +=2; quint16 alpha=ADJUST_BO(byteOrder, 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);}} QXcbVirtualDesktop *screen; xcb_window_t x_settings_window; QMap<QByteArray, QXcbXSettingsPropertyValue> settings;bool initialized;};QXcbXSettings::QXcbXSettings(QXcbVirtualDesktop *screen):d_ptr(newQXcbXSettingsPrivate(screen)){ QByteArray settings_atom_for_screen("_XSETTINGS_S"); settings_atom_for_screen.append(QByteArray::number(screen->number()));auto atom_reply =Q_XCB_REPLY(xcb_intern_atom, screen->xcb_connection(),true, settings_atom_for_screen.size(), settings_atom_for_screen.constData());if(!atom_reply)return; xcb_atom_t selection_owner_atom = atom_reply->atom; xcb_window_t owner = screen->connection()->selectionOwner(selection_owner_atom);if(owner == XCB_NONE)return; d_ptr->x_settings_window = owner;if(!d_ptr->x_settings_window)return; screen->connection()->addWindowEventListener(d_ptr->x_settings_window,this);const uint32_t event = XCB_CW_EVENT_MASK;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,event,event_mask); d_ptr->populateSettings(d_ptr->getSettings()); d_ptr->initialized =true;}QXcbXSettings::~QXcbXSettings(){delete d_ptr; d_ptr =nullptr;}boolQXcbXSettings::initialized()const{Q_D(const QXcbXSettings);return d->initialized;}voidQXcbXSettings::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event){Q_D(QXcbXSettings);if(event->window != d->x_settings_window)return; d->populateSettings(d->getSettings());}voidQXcbXSettings::registerCallbackForProperty(const QByteArray &property,QXcbXSettings::PropertyChangeFunc func,void*handle){Q_D(QXcbXSettings); d->settings[property].addCallback(func,handle);}voidQXcbXSettings::removeCallbackForHandle(const QByteArray &property,void*handle){Q_D(QXcbXSettings);auto&callbacks = d->settings[property].callback_links;auto isCallbackForHandle = [handle](const QXcbXSettingsCallback &cb) {return cb.handle == handle; }; callbacks.erase(std::remove_if(callbacks.begin(), callbacks.end(), isCallbackForHandle), callbacks.end());}voidQXcbXSettings::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 
close