mirror of
https://github.com/arcan1s/awesome-widgets.git
synced 2026-01-31 05:19:47 +00:00
it has been found in #174, that if there are multiple instances of widget, it keeps subscribing on all sources, because of how dbus interface works. In order to hande it, all subscriptions are now kept from singleton with tracking active clients
332 lines
12 KiB
C++
332 lines
12 KiB
C++
/***************************************************************************
|
|
* This file is part of awesome-widgets *
|
|
* *
|
|
* awesome-widgets is free software: you can redistribute it and/or *
|
|
* modify it under the terms of the GNU General Public License as *
|
|
* published by the Free Software Foundation, either version 3 of the *
|
|
* License, or (at your option) any later version. *
|
|
* *
|
|
* awesome-widgets is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with awesome-widgets. If not, see http://www.gnu.org/licenses/ *
|
|
***************************************************************************/
|
|
|
|
#include "awkeys.h"
|
|
|
|
#include <QDBusConnection>
|
|
#include <QDBusError>
|
|
#include <QRegularExpression>
|
|
#include <QTimer>
|
|
|
|
#include "awdataaggregator.h"
|
|
#include "awdataengineaggregator.h"
|
|
#include "awdbusadaptor.h"
|
|
#include "awdebug.h"
|
|
#include "awkeycache.h"
|
|
#include "awkeyoperations.h"
|
|
#include "awkeysaggregator.h"
|
|
#include "awpatternfunctions.h"
|
|
#include "graphicalitem.h"
|
|
|
|
|
|
AWKeys::AWKeys(QObject *_parent)
|
|
: QObject(_parent)
|
|
{
|
|
qSetMessagePattern(AWDebug::LOG_FORMAT);
|
|
qCDebug(LOG_AW) << __PRETTY_FUNCTION__;
|
|
for (auto &metadata : AWDebug::getBuildData())
|
|
qCDebug(LOG_AW) << metadata;
|
|
|
|
m_aggregator = new AWKeysAggregator(this);
|
|
m_dataAggregator = new AWDataAggregator(this);
|
|
m_dataEngineAggregator = AWDataEngineAggregator::instance(this);
|
|
m_keyOperator = new AWKeyOperations(this);
|
|
|
|
m_timer = new QTimer(this);
|
|
m_timer->setSingleShot(false);
|
|
|
|
createDBusInterface();
|
|
|
|
// update key data if required
|
|
connect(m_keyOperator, &AWKeyOperations::updateKeys, this, &AWKeys::reinitKeys);
|
|
connect(m_timer, &QTimer::timeout, this, &AWKeys::updateTextData);
|
|
|
|
// transfer signal from AWDataAggregator object to QML ui
|
|
connect(m_dataAggregator, &AWDataAggregator::toolTipPainted,
|
|
[this](const QString &_tooltip) { emit(needToolTipToBeUpdated(_tooltip)); });
|
|
|
|
connect(this, &AWKeys::dropSourceFromDataengine, this,
|
|
[this](const QString &_source) { m_dataEngineAggregator->dropSourceForClient(this, _source); });
|
|
connect(m_dataEngineAggregator, &AWDataEngineAggregator::dataUpdated, this, &AWKeys::dataUpdated);
|
|
// transfer signal from dataengine to update source list
|
|
connect(m_dataEngineAggregator, &AWDataEngineAggregator::deviceAdded, m_keyOperator, &AWKeyOperations::addDevice);
|
|
}
|
|
|
|
|
|
AWKeys::~AWKeys()
|
|
{
|
|
qCDebug(LOG_AW) << __PRETTY_FUNCTION__;
|
|
|
|
m_dataEngineAggregator->unregisterClient(this);
|
|
|
|
m_timer->stop();
|
|
// delete dbus session
|
|
auto id = reinterpret_cast<qlonglong>(this);
|
|
QDBusConnection::sessionBus().unregisterObject(QString("/%1").arg(id));
|
|
}
|
|
|
|
|
|
void AWKeys::initDataAggregator(const QVariantMap &_tooltipParams)
|
|
{
|
|
qCDebug(LOG_AW) << "Tooltip parameters" << _tooltipParams;
|
|
|
|
// store parameters to generate m_requiredKeys
|
|
m_tooltipParams = _tooltipParams;
|
|
m_dataAggregator->setParameters(m_tooltipParams);
|
|
}
|
|
|
|
|
|
void AWKeys::initKeys(const QString &_currentPattern, const int _interval, const bool _optimize)
|
|
{
|
|
qCDebug(LOG_AW) << "Pattern" << _currentPattern << "with interval" << _interval << "with optimization" << _optimize;
|
|
|
|
// init
|
|
m_optimize = _optimize;
|
|
// child objects
|
|
m_aggregator->initFormatters();
|
|
m_keyOperator->setPattern(_currentPattern);
|
|
m_keyOperator->updateCache();
|
|
m_dataEngineAggregator->loadSources();
|
|
|
|
// timer
|
|
m_timer->setInterval(_interval);
|
|
m_timer->start();
|
|
}
|
|
|
|
|
|
void AWKeys::setAggregatorProperty(const QString &_key, const QVariant &_value)
|
|
{
|
|
qCDebug(LOG_AW) << "Key" << _key << "with value" << _value;
|
|
|
|
m_aggregator->setProperty(_key.toUtf8().constData(), _value);
|
|
}
|
|
|
|
|
|
void AWKeys::setWrapNewLines(const bool _wrap)
|
|
{
|
|
qCDebug(LOG_AW) << "Is wrapping enabled" << _wrap;
|
|
|
|
m_wrapNewLines = _wrap;
|
|
}
|
|
|
|
|
|
void AWKeys::updateCache()
|
|
{
|
|
return m_keyOperator->updateCache();
|
|
}
|
|
|
|
|
|
QStringList AWKeys::dictKeys(const bool _sorted, const QString &_regexp) const
|
|
{
|
|
qCDebug(LOG_AW) << "Should be sorted" << _sorted << "and filter applied" << _regexp;
|
|
|
|
// check if functions asked
|
|
if (_regexp == "functions")
|
|
return QString(STATIC_FUNCTIONS).split(',');
|
|
// check if user defined keys asked
|
|
if (_regexp == "userdefined")
|
|
return m_keyOperator->userKeys();
|
|
|
|
QStringList allKeys = m_keyOperator->dictKeys();
|
|
// sort if required
|
|
if (_sorted)
|
|
allKeys.sort();
|
|
|
|
return allKeys.filter(QRegularExpression(_regexp));
|
|
}
|
|
|
|
|
|
QString AWKeys::infoByKey(const QString &_key) const
|
|
{
|
|
qCDebug(LOG_AW) << "Requested info for key" << _key;
|
|
|
|
return m_keyOperator->infoByKey(_key);
|
|
}
|
|
|
|
|
|
// HACK this method requires to define tag value from bar from UI interface
|
|
QString AWKeys::valueByKey(const QString &_key) const
|
|
{
|
|
qCDebug(LOG_AW) << "Requested value for key" << _key;
|
|
|
|
auto realKey = _key.startsWith("bar") ? m_keyOperator->infoByKey(_key) : _key;
|
|
|
|
return m_aggregator->formatter(m_values[realKey], realKey, true);
|
|
}
|
|
|
|
|
|
void AWKeys::editItem(const QString &_type)
|
|
{
|
|
qCDebug(LOG_AW) << "Item type" << _type;
|
|
|
|
return m_keyOperator->editItem(_type);
|
|
}
|
|
|
|
|
|
void AWKeys::dataUpdated(const QHash<QString, KSysGuard::SensorInfo> &_sensors, const KSysGuard::SensorDataList &_data)
|
|
{
|
|
qCDebug(LOG_AW) << "Update data for" << _data.count() << "items";
|
|
|
|
for (auto &data : _data) {
|
|
if (!_sensors.contains(data.sensorProperty))
|
|
continue;
|
|
auto sensor = _sensors[data.sensorProperty];
|
|
setDataBySource(data.sensorProperty, sensor, data.payload);
|
|
}
|
|
}
|
|
|
|
|
|
void AWKeys::reinitKeys(const QStringList &_currentKeys)
|
|
{
|
|
qCDebug(LOG_AW) << "Update found keys by using list" << _currentKeys;
|
|
|
|
// append lists
|
|
m_foundBars = AWPatternFunctions::findKeys(m_keyOperator->pattern(), _currentKeys, true);
|
|
m_foundKeys = AWPatternFunctions::findKeys(m_keyOperator->pattern(), _currentKeys, false);
|
|
m_foundLambdas = AWPatternFunctions::findLambdas(m_keyOperator->pattern());
|
|
// generate list of required keys for bars
|
|
QStringList barKeys;
|
|
for (auto &bar : m_foundBars) {
|
|
auto item = m_keyOperator->giByKey(bar);
|
|
if (item->isCustom())
|
|
item->setUsedKeys(AWPatternFunctions::findKeys(item->bar(), _currentKeys, false));
|
|
else
|
|
item->setUsedKeys(QStringList() << item->bar());
|
|
barKeys.append(item->usedKeys());
|
|
}
|
|
// get required keys
|
|
m_requiredKeys = AWKeyCache::getRequiredKeys(m_foundKeys, barKeys, m_tooltipParams,
|
|
m_keyOperator->requiredUserKeys(), _currentKeys);
|
|
|
|
// set key data to m_aggregator
|
|
m_aggregator->setDevices(m_keyOperator->devices());
|
|
}
|
|
|
|
|
|
void AWKeys::updateTextData()
|
|
{
|
|
// do not do it in parallel to avoid race condition
|
|
calculateValues();
|
|
auto text = parsePattern(m_keyOperator->pattern());
|
|
// update tooltip values under lock
|
|
m_dataAggregator->dataUpdate(m_values);
|
|
|
|
emit(needTextToBeUpdated(text));
|
|
}
|
|
|
|
|
|
// HACK this method is required since I could not define some values by using
|
|
// specified pattern. Usually they are values which depend on several others
|
|
void AWKeys::calculateValues()
|
|
{
|
|
auto devices = m_keyOperator->devices();
|
|
|
|
// up, down, upkb, downkb, upunits, downunits
|
|
auto netIndex = devices.network.indexOf(m_values["netdev"].toString());
|
|
m_values["down"] = m_values[QString("down%1").arg(netIndex)];
|
|
m_values["downkb"] = m_values[QString("downkb%1").arg(netIndex)];
|
|
m_values["downtot"] = m_values[QString("downtot%1").arg(netIndex)];
|
|
m_values["downtotkb"] = m_values[QString("downtotkb%1").arg(netIndex)];
|
|
m_values["downunits"] = m_values[QString("downunits%1").arg(netIndex)];
|
|
m_values["up"] = m_values[QString("up%1").arg(netIndex)];
|
|
m_values["upkb"] = m_values[QString("upkb%1").arg(netIndex)];
|
|
m_values["uptot"] = m_values[QString("uptot%1").arg(netIndex)];
|
|
m_values["uptotkb"] = m_values[QString("uptotkb%1").arg(netIndex)];
|
|
m_values["upunits"] = m_values[QString("upunits%1").arg(netIndex)];
|
|
|
|
// user defined keys
|
|
for (auto &key : m_keyOperator->userKeys())
|
|
m_values[key] = m_values[m_keyOperator->userKeySource(key)];
|
|
|
|
// lambdas
|
|
for (auto &key : m_foundLambdas)
|
|
m_values[key] = AWPatternFunctions::expandLambdas(key, m_aggregator, m_values, m_foundKeys);
|
|
}
|
|
|
|
|
|
void AWKeys::createDBusInterface()
|
|
{
|
|
// get this object id
|
|
auto id = reinterpret_cast<qlonglong>(this);
|
|
|
|
// create session
|
|
auto instanceBus = QDBusConnection::sessionBus();
|
|
// HACK we are going to use different services because it binds to
|
|
// application
|
|
if (instanceBus.registerService(QString("%1.i%2").arg(AWDBUS_SERVICE).arg(id))) {
|
|
if (!instanceBus.registerObject(AWDBUS_PATH, new AWDBusAdaptor(this), QDBusConnection::ExportAllContents))
|
|
qCWarning(LOG_AW) << "Could not register DBus object, last error" << instanceBus.lastError().message();
|
|
} else {
|
|
qCWarning(LOG_AW) << "Could not register DBus service, last error" << instanceBus.lastError().message();
|
|
}
|
|
|
|
// and same instance but for id independent service
|
|
auto commonBus = QDBusConnection::sessionBus();
|
|
if (commonBus.registerService(AWDBUS_SERVICE))
|
|
commonBus.registerObject(AWDBUS_PATH, new AWDBusAdaptor(this), QDBusConnection::ExportAllContents);
|
|
}
|
|
|
|
|
|
QString AWKeys::parsePattern(QString _pattern) const
|
|
{
|
|
// screen sign
|
|
_pattern.replace("$$", QChar(0x1d));
|
|
|
|
// lambdas
|
|
for (auto &key : m_foundLambdas)
|
|
_pattern.replace(QString("${{%1}}").arg(key), m_values[key].toString());
|
|
|
|
// main keys
|
|
for (auto &key : m_foundKeys)
|
|
_pattern.replace(QString("$%1").arg(key), m_aggregator->formatter(m_values[key], key, true));
|
|
|
|
// bars
|
|
for (auto &bar : m_foundBars) {
|
|
auto item = m_keyOperator->giByKey(bar);
|
|
auto image = item->isCustom() ? item->image(AWPatternFunctions::expandLambdas(item->bar(), m_aggregator,
|
|
m_values, item->usedKeys()))
|
|
: item->image(m_values[item->bar()]);
|
|
_pattern.replace(QString("$%1").arg(bar), image);
|
|
}
|
|
|
|
// prepare strings
|
|
_pattern.replace(QChar(0x1d), "$");
|
|
if (m_wrapNewLines)
|
|
_pattern.replace("\n", "<br>");
|
|
|
|
return _pattern;
|
|
}
|
|
|
|
|
|
void AWKeys::setDataBySource(const QString &_source, const KSysGuard::SensorInfo &_sensor, const QVariant &_value)
|
|
{
|
|
qCDebug(LOG_AW) << "Source" << _source << _sensor.name << "with data" << _value;
|
|
|
|
// first list init
|
|
auto tags = m_aggregator->keysFromSource(_source);
|
|
if (tags.isEmpty())
|
|
tags = m_aggregator->registerSource(_source, _sensor.unit, m_requiredKeys, m_optimize);
|
|
|
|
// update data or drop source if there are no matches and exit
|
|
if (tags.isEmpty()) {
|
|
qCInfo(LOG_AW) << "Sensor" << _source << "not found";
|
|
return emit(dropSourceFromDataengine(_source));
|
|
}
|
|
|
|
std::for_each(tags.cbegin(), tags.cend(), [this, _value](const QString &tag) { m_values[tag] = _value; });
|
|
}
|