diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 2b91efc..ba49cd3 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -1,7 +1,6 @@ configure_file("Constants.h.in" "Constants.h" IMMEDIATE @ONLY) set(PUBLIC_HEADERS - ${CMAKE_SOURCE_DIR}/src/common/ConfigReader.h - ${CMAKE_SOURCE_DIR}/src/common/Configuration.h + ${CMAKE_SOURCE_DIR}/src/common/Config.h ${CMAKE_SOURCE_DIR}/src/common/MessageHandler.h ${CMAKE_SOURCE_DIR}/src/common/Messages.h ${CMAKE_SOURCE_DIR}/src/common/Session.h @@ -13,8 +12,6 @@ set(PUBLIC_HEADERS ) set(SRCS - ${CMAKE_SOURCE_DIR}/src/common/ConfigReader.cpp - ${CMAKE_SOURCE_DIR}/src/common/Configuration.cpp ${CMAKE_SOURCE_DIR}/src/common/Session.cpp ${CMAKE_SOURCE_DIR}/src/common/SignalHandler.cpp ${CMAKE_SOURCE_DIR}/src/common/SocketWriter.cpp diff --git a/src/common/Config.h b/src/common/Config.h new file mode 100644 index 0000000..61e2d95 --- /dev/null +++ b/src/common/Config.h @@ -0,0 +1,283 @@ +// Copyright (C) 2026 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include + +#include "Constants.h" + +namespace DDM { + class Config : public QObject { + Q_OBJECT + public: + /** + * Constructor. + * + * @param configPath Path to the configuration file. + * @param defaults Default configuration values and descriptions. + * @param parent Parent QObject. + */ + Config(const QString &configPath, + // section entry default description + QMap>> defaults = {}, + QObject *parent = nullptr) + : QObject(parent) + , m_configPath(configPath) + , m_defaults(defaults) { } + + /** + * Get a configuration entry value. + * If the entry is not found in the loaded configuration, the default value is returned. + * If no default value is set, an exception is thrown. + * + * @param section The configuration section name. + * @param entry The configuration entry name. + * @return The value of the configuration entry. + */ + template + inline T get(const QString §ion, const QString &entry) const { + if (m_data.contains(section) && m_data[section].contains(entry)) + return m_data[section][entry].value(); + else if (m_defaults.contains(section) && m_defaults[section].contains(entry)) + return m_defaults[section][entry].first.value(); + else + throw std::runtime_error(QStringLiteral("Config entry '%1/%2' not found") + .arg(section, entry) + .toStdString()); + } + + /** + * Get a configuration entry value inside the default section. + * If the entry is not found in the loaded configuration, the default value is returned. + * If no default value is set, an exception is thrown. + * + * @param entry The configuration entry name. + * @return The value of the configuration entry. + */ + template + inline T get(const QString &entry) const { + return get({}, entry); + } + + /** + * Set a configuration entry value. + * + * @param section The configuration section name. + * @param entry The configuration entry name. + * @param value The value to set. + */ + template + inline void set(const QString §ion, const QString &entry, const T &value) { + m_data[section][entry] = QVariant::fromValue(value); + } + + /** + * Set a configuration entry value inside the default section. + * + * @param entry The configuration entry name. + * @param value The value to set. + */ + template + inline void set(const QString &entry, const T &value) { + m_data[{}][entry] = QVariant::fromValue(value); + } + + /** + * Reset a configuration entry to its default value. + * + * @param section The configuration section name. + * @param entry The configuration entry name. + * @return True if the entry was reset, false if it was not found or already default. + */ + inline bool setDefault(const QString §ion, const QString &entry) { + if (m_data.contains(section) && m_data[section].contains(entry)) { + m_data[section].remove(entry); + return true; + } + return false; + } + + /** + * Reset a configuration entry to its default value inside the default section. + * + * @param entry The configuration entry name. + * @return True if the entry was reset, false if it was not found or already default. + */ + inline bool setDefault(const QString &entry) { + return setDefault({}, entry); + } + + /** + * Load the configuration from the file. + * + * @return True if the configuration was loaded successfully, false otherwise. + */ + bool load() { + QFile file(m_configPath); + if (!file.open(QIODevice::ReadOnly)) { + return false; + } + QString currentSection{}; + while (!file.atEnd()) { + QString line = QString::fromLocal8Bit(file.readLine()).trimmed(); + if (line.isEmpty() || line.startsWith('#')) + continue; + if (line.startsWith('[') && line.endsWith(']')) { + currentSection = line.mid(1, line.length() - 2).trimmed(); + } else { + int separatorPosition = line.indexOf('='); + if (separatorPosition >= 0) { + if (!m_data.contains(currentSection)) + m_data[currentSection] = {}; + QString name = line.left(separatorPosition).trimmed(); + QString value = line.mid(separatorPosition + 1).trimmed(); + if (value.isEmpty()) { + m_data[currentSection][name] = QVariant(); + } else if (value.indexOf(',') >= 0) { + QStringList list = value.split(',', Qt::SkipEmptyParts); + for (QString &item : list) + item = item.trimmed(); + m_data[currentSection][name] = QVariant(list); + } else + m_data[currentSection][name] = QVariant(value); + } + } + } + return true; + } + + /** + * Save the configuration to the file. + * + * @return True if the configuration was saved successfully, false otherwise. + */ + bool save() const { + QFile file(m_configPath); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + return false; + } + QTextStream out(&file); + for (auto section = m_data.cbegin(); section != m_data.cend(); ++section) { + const QString §ionName = section.key(); + if (!sectionName.isEmpty()) + out << "[" << sectionName << "]\n"; + const auto &entries = section.value(); + for (auto entry = entries.constBegin(); entry != entries.constEnd(); ++entry) { + if (entry.value().userType() == QMetaType::QStringList) + out << entry.key() << "=" << entry.value().toStringList().join(", ") << "\n"; + else + out << entry.key() << "=" << entry.value().toString() << "\n"; + } + out << "\n"; + } + return true; + } + + /** + * Serialize the default configuration to the format of the + * configuration file, as a string. + * + * @return The default configuration as a string. + */ + QString defaultConfig() const { + QString str; + QTextStream out(&str); + for (auto section = m_defaults.cbegin(); section != m_defaults.cend(); ++section) { + const QString §ionName = section.key(); + if (!sectionName.isEmpty()) + out << "[" << sectionName << "]\n\n"; + const auto &entries = section.value(); + for (auto entry = entries.constBegin(); entry != entries.constEnd(); ++entry) { + const QString &description = entry.value().second; + if (!description.isEmpty()) + for (const QString &line : description.split('\n')) + out << "# " << line << "\n"; + if (entry.value().first.userType() == QMetaType::QStringList) + out << entry.key() << "=" << entry.value().first.toStringList().join(", ") << "\n\n"; + else + out << entry.key() << "=" << entry.value().first.toString() << "\n\n"; + } + } + return str; + } + + /** + * Wipe all loaded configuration data, reset all entries to default. + */ + inline void wipe() { + m_data.clear(); + } + + private: + /** Path to the configuration file. */ + QString m_configPath{}; + // section entry value + QMap> m_data; + // section entry default description + QMap>> m_defaults; + }; + + /** Main configuration instance. */ + inline Config mainConfig( + CONFIG_FILE, + { { {}, + { { "HaltCommand", { HALT_COMMAND, "Halt command" } }, + { "RebootCommand", { REBOOT_COMMAND, "Reboot command" } }, + { "Namespaces", + { QStringList(), + "List of Linux namespaces for user session to enter" } } } }, + { "Theme", + { { "CursorTheme", { QString(), "Cursor theme used in the greeter" } }, + { "CursorSize", { QString(), "Cursor size used in the greeter" } } } }, + { "X11", + { { "ServerPath", { "/usr/bin/X", "Path to X server binary" } }, + { "ServerArguments", + { "-nolisten tcp", "Arguments passed to the X server invocation" } }, + { "SessionDir", + { QStringList{ "/usr/local/share/xsessions", "/usr/share/xsessions" }, + "Comma-separated list of directories containing available X sessions" } }, + { "SessionCommand", + { SESSION_COMMAND, + "Path to a script to execute when starting the desktop session" } }, + { "SessionLogFile", + { ".local/share/ddm/xorg-session.log", "Path to the user session log file" } }, + { "DisplayCommand", + { QStringLiteral(DATA_INSTALL_DIR "/scripts/Xsetup"), + "Path to a script to execute when starting the display server" } }, + { "DisplayStopCommand", + { QStringLiteral(DATA_INSTALL_DIR "/scripts/Xstop"), + "Path to a script to execute when stopping the display server" } } } }, + { "Wayland", + { { "SessionDir", + { QStringList{ "/usr/local/share/wayland-sessions", "/usr/share/wayland-sessions" }, + "Comma-separated list of directories containing available Wayland sessions" } }, + { "SessionCommand", + { WAYLAND_SESSION_COMMAND, + "Path to a script to execute when starting the desktop session" } }, + { "SessionLogFile", + { ".local/share/ddm/wayland-session.log", + "Path to the user session log file" } } } }, + { "Single", + { { "SessionCommand", + { WAYLAND_SESSION_COMMAND, + "Path to a script to execute when starting the desktop session" } } } }, + { "Users", + { { "DefaultPath", + { "/usr/local/bin:/usr/bin:/bin", "Default $PATH for logged in users" } }, + { "RememberLastUser", { true, "Remember the last successfully logged in user" } }, + { "RememberLastSession", + { true, "Remember the session of the last successfully logged in user" } } } } }); + + /** State configuration instance. */ + inline Config stateConfig( + QStringLiteral(STATE_DIR "/state.conf"), + { { "Last", + { { "User", { QString(), "The last successfully logged in user" } }, + { "Session", + { QString(), "The session of the last successfully logged in user" } } } } }); +} // namespace DDM diff --git a/src/common/ConfigReader.cpp b/src/common/ConfigReader.cpp deleted file mode 100644 index 00baf75..0000000 --- a/src/common/ConfigReader.cpp +++ /dev/null @@ -1,373 +0,0 @@ -/* - * INI Configuration parser classes - * Copyright (C) 2014 Martin Bříza - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "ConfigReader.h" -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -QTextStream &operator>>(QTextStream &str, QStringList &list) { - list.clear(); - - QString line = str.readLine(); - const auto strings = QStringView{line}.split(u','); - for (const QStringView &s : strings) { - QStringView trimmed = s.trimmed(); - if (!trimmed.isEmpty()) - list.append(trimmed.toString()); - } - return str; -} - -QTextStream &operator<<(QTextStream &str, const QStringList &list) { - str << list.join(QLatin1Char(',')); - return str; -} - -QTextStream &operator>>(QTextStream &str, bool &val) { - QString line = str.readLine(); - val = (0 == QStringView(line).trimmed().compare(QLatin1String("true"), Qt::CaseInsensitive)); - return str; -} - -QTextStream &operator<<(QTextStream &str, const bool &val) { - if (val) - str << "true"; - else - str << "false"; - return str; -} - -namespace DDM { - // has to be specialised because QTextStream reads only words into a QString - template <> void ConfigEntry::setValue(const QString &str) { - m_value = str.trimmed(); - } - - - ConfigSection::ConfigSection(ConfigBase *parent, const QString &name) : m_parent(parent), - m_name(name) { - m_parent->m_sections.insert(name, this); - } - - ConfigEntryBase *ConfigSection::entry(const QString &name) { - auto it = m_entries.find(name); - if (it != m_entries.end()) - return it.value(); - return nullptr; - } - - const ConfigEntryBase *ConfigSection::entry(const QString &name) const { - auto it = m_entries.find(name); - if (it != m_entries.end()) - return it.value(); - return nullptr; - } - - const QMap &ConfigSection::entries() const { - return m_entries; - } - - - const QString &ConfigSection::name() const { - return m_name; - } - - void ConfigSection::save(ConfigEntryBase *entry) { - m_parent->save(this, entry); - } - - void ConfigSection::clear() { - for (auto it : m_entries) { - it->setDefault(); - } - } - - QString ConfigSection::toConfigFull() const { - QString final = QStringLiteral("[%1]\n").arg(m_name); - for (const ConfigEntryBase *entry : m_entries) - final.append(entry->toConfigFull()); - return final; - } - - QString ConfigSection::toConfigShort() const { - return QStringLiteral("[%1]").arg(name()); - } - - - - ConfigBase::ConfigBase(const QString &configPath, const QString &configDir, const QString &sysConfigDir) : - m_path(configPath), - m_configDir(configDir), - m_sysConfigDir(sysConfigDir) - { - } - - bool ConfigBase::hasUnused() const { - return m_unusedSections || m_unusedVariables; - } - - QString ConfigBase::toConfigFull() const { - QString ret; - for (ConfigSection *s : m_sections) { - ret.append(s->toConfigFull()); - ret.append(QLatin1Char('\n')); - } - return ret; - } - - void ConfigBase::load() - { - //order of priority from least influence to most influence, is - // * m_sysConfigDir (system settings /usr/lib/ddm/ddm.conf.d/) in alphabetical order - // * m_configDir (user settings in /etc/ddm.conf.d/) in alphabetical order - // * m_path (classic fallback /etc/ddm.conf) - - QStringList files; - QDateTime latestModificationTime = QFileInfo(m_path).lastModified(); - - if (!m_sysConfigDir.isEmpty()) { - //include the configDir in modification time so we also reload on any files added/removed - QDir dir(m_sysConfigDir); - if (dir.exists()) { - latestModificationTime = std::max(latestModificationTime, QFileInfo(m_sysConfigDir).lastModified()); - const auto dirFiles = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::LocaleAware); - for (const QFileInfo &file : dirFiles) { - files << (file.absoluteFilePath()); - latestModificationTime = std::max(latestModificationTime, file.lastModified()); - } - } - } - if (!m_configDir.isEmpty()) { - //include the configDir in modification time so we also reload on any files added/removed - QDir dir(m_configDir); - if (dir.exists()) { - latestModificationTime = std::max(latestModificationTime, QFileInfo(m_configDir).lastModified()); - const auto dirFiles = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::LocaleAware); - for (const QFileInfo &file : dirFiles) { - files << (file.absoluteFilePath()); - latestModificationTime = std::max(latestModificationTime, file.lastModified()); - } - } - } - - files << m_path; - - if (latestModificationTime <= m_fileModificationTime) { - return; - } - m_fileModificationTime = latestModificationTime; - - for (const QString &filepath : std::as_const(files)) { - loadInternal(filepath); - } - } - - - void ConfigBase::loadInternal(const QString &filepath) { - QString currentSection = QStringLiteral(IMPLICIT_SECTION); - - QFile in(filepath); - - if (!in.open(QIODevice::ReadOnly)) - return; - while (!in.atEnd()) { - QString line = QString::fromUtf8(in.readLine()); - QStringView lineRef = QStringView(line).trimmed(); - // get rid of comments first - lineRef = lineRef.left(lineRef.indexOf(QLatin1Char('#'))).trimmed(); - - // In version 0.14.0, these sections were renamed - if (currentSection == QStringLiteral("XDisplay")) - currentSection = QStringLiteral("X11"); - else if (currentSection == QStringLiteral("WaylandDisplay")) - currentSection = QStringLiteral("Wayland"); - - // value assignment - int separatorPosition = lineRef.indexOf(QLatin1Char('=')); - if (separatorPosition >= 0) { - QString name = lineRef.left(separatorPosition).trimmed().toString(); - QStringView value = lineRef.mid(separatorPosition + 1).trimmed(); - - auto sectionIterator = m_sections.constFind(currentSection); - if (sectionIterator != m_sections.constEnd() && sectionIterator.value()->entry(name)) - sectionIterator.value()->entry(name)->setValue(value.toString()); - else - // if we don't have such member in the config, nag about it - m_unusedVariables = true; - } - // section start - else if (lineRef.startsWith(QLatin1Char('[')) && lineRef.endsWith(QLatin1Char(']'))) - currentSection = lineRef.mid(1, lineRef.length() - 2).toString(); - } - } - - void ConfigBase::save(const ConfigSection *section, const ConfigEntryBase *entry) { - // to know if we should overwrite the config or not - bool changed = false; - // stores the order of the loaded sections - // each one could be there only once - if it occurs more times in the config, the occurrences are merged - QVector sectionOrder; - // the actual bytearray data for every section - QHash sectionData; - // map of nondefault entries which should be saved if they are not found in the current config file - QMultiHash remainingEntries; - - - /* - * Initialization of the map of nondefault values to be saved - */ - if (section) { - if (entry && !entry->matchesDefault()) - remainingEntries.insert(section, entry); - else { - for (const ConfigEntryBase *b : std::as_const(section->entries())) - if (!b->matchesDefault()) - remainingEntries.insert(section, b); - } - } - else { - for (const ConfigSection *s : std::as_const(m_sections)) { - for (const ConfigEntryBase *b : std::as_const(s->entries())) - if (!b->matchesDefault()) - remainingEntries.insert(s, b); - } - } - - // initialize the current section - General, usually - const ConfigSection *currentSection = m_sections.value(QStringLiteral(IMPLICIT_SECTION)); - - // stuff to store the pre-section stuff (comments) to the start of the right section, not the end of the previous one - QByteArray junk; - // stores the junk to the temporary storage - auto collectJunk = [&junk](const QString &data) { - junk.append(data.toUtf8()); - }; - - // a short function to assign the current junk and current line to the right section, eventually create a new one - auto writeSectionData = [¤tSection, &junk, §ionOrder, §ionData](const QString &data) { - if (currentSection && !sectionOrder.contains(currentSection)) { - sectionOrder.append(currentSection); - sectionData[currentSection] = QByteArray(); - } - sectionData[currentSection].append(junk); - sectionData[currentSection].append(data.toUtf8()); - junk.clear(); - }; - - // loading and checking phase - QFile file(m_path); - bool ok = file.open(QIODevice::ReadOnly); // first just for reading - Q_ASSERT(ok); - while (!file.atEnd()) { - const QString line = QString::fromUtf8(file.readLine()); - // get rid of comments first - QStringView trimmedLine = QStringView{line}.left(line.indexOf(QLatin1Char('#'))).trimmed(); - QStringView comment; - if (line.indexOf(QLatin1Char('#')) >= 0) - comment = QStringView{line}.mid(line.indexOf(QLatin1Char('#'))).trimmed(); - - // value assignment - int separatorPosition = trimmedLine.indexOf(QLatin1Char('=')); - if (separatorPosition >= 0) { - QString name = trimmedLine.left(separatorPosition).trimmed().toString(); - QStringView value = trimmedLine.mid(separatorPosition + 1).trimmed(); - - if (currentSection && currentSection->entry(name)) { - // this monstrous condition checks the parameters if only one entry/section should be saved - if ((entry && section && section->name() == currentSection->name() && entry->name() == name) || - (!entry && section && section->name() == currentSection->name()) || - value != currentSection->entry(name)->value()) { - changed = true; - writeSectionData(QStringLiteral("%1=%2 %3\n").arg(name).arg(currentSection->entry(name)->value()).arg(comment.toString())); - } - else - writeSectionData(line); - remainingEntries.remove(currentSection, currentSection->entry(name)); - } - else { - if (currentSection) - m_unusedVariables = true; - writeSectionData(QStringLiteral("%1 %2\n").arg(trimmedLine.toString()).arg(QStringLiteral(UNUSED_VARIABLE_COMMENT))); - } - } - - // section start - else if (trimmedLine.startsWith(QLatin1Char('[')) && trimmedLine.endsWith(QLatin1Char(']'))) { - const QString name = trimmedLine.mid(1, trimmedLine.length() - 2).toString(); - auto sectionIterator = m_sections.constFind(name); - if (sectionIterator != m_sections.constEnd()) { - currentSection = sectionIterator.value(); - if (!sectionOrder.contains(currentSection)) - writeSectionData(line); - } - else { - m_unusedSections = true; - currentSection = nullptr; - writeSectionData(line); - } - } - - // other stuff, like comments and whatnot - else { - if (line != QStringLiteral(UNUSED_SECTION_COMMENT)) - collectJunk(line); - } - } - file.close(); - - for (auto it = remainingEntries.begin(); it != remainingEntries.end(); it++) { - changed = true; - currentSection = it.key(); - if (!sectionOrder.contains(currentSection)) - writeSectionData(currentSection->toConfigShort()); - writeSectionData(QStringLiteral("\n")); - writeSectionData(it.value()->toConfigFull()); - } - - // rewrite the whole thing only if there are changes - if (changed) { - bool ok = file.open(QIODevice::WriteOnly | QIODevice::Truncate); - Q_ASSERT(ok); - for (const ConfigSection *s : sectionOrder) - file.write(sectionData.value(s)); - - if (sectionData.contains(nullptr)) { - file.write("\n"); - file.write(UNUSED_SECTION_COMMENT); - file.write(sectionData.value(nullptr).trimmed()); - file.write("\n"); - } - } - } - - void ConfigBase::wipe() { - for (auto it : m_sections) { - it->clear(); - } - } -} diff --git a/src/common/ConfigReader.h b/src/common/ConfigReader.h deleted file mode 100644 index d9ff441..0000000 --- a/src/common/ConfigReader.h +++ /dev/null @@ -1,213 +0,0 @@ -/* - * INI Configuration parser classes - * Copyright (C) 2014 Martin Bříza - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef CONFIGREADER_H -#define CONFIGREADER_H - -#include -#include -#include -#include -#include -#include - -#define IMPLICIT_SECTION "General" -#define UNUSED_VARIABLE_COMMENT "# Unused variable" -#define UNUSED_SECTION_COMMENT "### These sections and their variables were not used: ###\n" - -///// convenience macros -// efficient qstring initializer -#define _S(x) QStringLiteral(x) - -// config wrapper -#define Config(name, file, dir, sysDir, ...) \ - class name : public DDM::ConfigBase, public DDM::ConfigSection { \ - public: \ - name() : DDM::ConfigBase(file, dir, sysDir), DDM::ConfigSection(this, QStringLiteral(IMPLICIT_SECTION)) { \ - load(); \ - } \ - void save() { DDM::ConfigBase::save(nullptr, nullptr); } \ - void save(DDM::ConfigEntryBase *) const = delete; \ - QString toConfigFull() const { \ - return DDM::ConfigBase::toConfigFull(); \ - } \ - __VA_ARGS__ \ - } -// entry wrapper -#define Entry(name, type, default, description, ...) \ - DDM::ConfigEntry name { this, QStringLiteral(#name), default, description, __VA_ARGS__ } -// section wrapper -#define Section(name, ...) \ - class name : public DDM::ConfigSection { \ - public: \ - name (DDM::ConfigBase *_parent, const QString &_name) : DDM::ConfigSection(_parent, _name) { } \ - __VA_ARGS__ \ - } name { this, QStringLiteral(#name) }; - -QTextStream &operator>>(QTextStream &str, QStringList &list); -QTextStream &operator<<(QTextStream &str, const QStringList &list); -QTextStream &operator>>(QTextStream &str, bool &val); -QTextStream &operator<<(QTextStream &str, const bool &val); - -namespace DDM { - template class ConfigEntry; - class ConfigSection; - class ConfigBase; - - class ConfigEntryBase { - public: - virtual const QString &name() const = 0; - virtual QString value() const = 0; - virtual void setValue(const QString &str) = 0; - virtual QString toConfigShort() const = 0; - virtual QString toConfigFull() const = 0; - virtual bool matchesDefault() const = 0; - virtual bool isDefault() const = 0; - virtual bool setDefault() = 0; - }; - - class ConfigSection { - public: - ConfigSection(ConfigBase *parent, const QString &name); - ConfigEntryBase *entry(const QString &name); - const ConfigEntryBase *entry(const QString &name) const; - void save(ConfigEntryBase *entry); - void clear(); - const QString &name() const; - QString toConfigShort() const; - QString toConfigFull() const; - const QMap &entries() const; - private: - template friend class ConfigEntryPrivate; - QMap m_entries {}; - - ConfigBase *m_parent { nullptr }; - QString m_name { }; - template friend class ConfigEntry; - }; - - template - class ConfigEntry : public ConfigEntryBase { - public: - ConfigEntry(ConfigSection *parent, const QString &name, const T &value, const QString &description) : ConfigEntryBase(), - m_name(name), - m_description(description), - m_default(value), - m_value(value), - m_isDefault(true), - m_parent(parent) { - m_parent->m_entries[name] = this; - } - - T get() const { - return m_value; - } - - void set(const T val) { - m_value = val; - m_isDefault = false; - } - - bool matchesDefault() const { - return m_value == m_default; - } - - bool isDefault() const { - return m_isDefault; - } - - bool setDefault() { - m_isDefault = true; - if (m_value == m_default) - return false; - m_value = m_default; - return true; - } - - void save() { - m_parent->save(this); - } - - const QString &name() const { - return m_name; - } - - QString value() const { - QString str; - QTextStream out(&str); - out << m_value; - return str; - } - - // specialised for QString - void setValue(const QString &str) { - m_isDefault = false; - QTextStream in(qPrintable(str)); - in >> m_value; - } - - QString toConfigShort() const { - return QStringLiteral("%1=%2").arg(m_name).arg(value()); - } - - QString toConfigFull() const { - QString str; - for (const QString &line : m_description.split(QLatin1Char('\n'))) - str.append(QStringLiteral("# %1\n").arg(line)); - str.append(QStringLiteral("%1=%2\n\n").arg(m_name).arg(value())); - return str; - } - private: - const QString m_name; - const QString m_description; - T m_default; - T m_value; - bool m_isDefault; - ConfigSection *m_parent; - }; - - // Base has to be separate from the Config itself - order of initialization - class ConfigBase { - public: - ConfigBase(const QString &configPath, const QString &configDir=QString(), const QString &sysConfigDir=QString()); - - void load(); - void save(const ConfigSection *section = nullptr, const ConfigEntryBase *entry = nullptr); - void wipe(); - bool hasUnused() const; - QString toConfigFull() const; - protected: - bool m_unusedVariables { false }; - bool m_unusedSections { false }; - - QString m_path {}; - QString m_configDir; - QString m_sysConfigDir; - QMap m_sections; - friend class ConfigSection; - private: - QDateTime dirLatestModifiedTime(const QString &directory); - void loadInternal(const QString &filepath); - QDateTime m_fileModificationTime; - }; -} - - -#endif // CONFIGREADER_H diff --git a/src/common/Configuration.cpp b/src/common/Configuration.cpp deleted file mode 100644 index 83563b7..0000000 --- a/src/common/Configuration.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * DDM configuration - * Copyright (C) 2014 Martin Bříza - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include "Configuration.h" -#include "Constants.h" - -namespace DDM { - MainConfig mainConfig; - StateConfig stateConfig; -} diff --git a/src/common/Configuration.h b/src/common/Configuration.h deleted file mode 100644 index b258980..0000000 --- a/src/common/Configuration.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * DDM configuration - * Copyright (C) 2014 Martin Bříza - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#ifndef DDM_CONFIGURATION_H -#define DDM_CONFIGURATION_H - -#include -#include -#include -#include -#include - -#include "Constants.h" - -#include "ConfigReader.h" - -namespace DDM { - // Name File Sections and/or Entries (but anything else too, it's a class) - Entries in a Config are assumed to be in the General section - Config(MainConfig, QStringLiteral(CONFIG_FILE), QStringLiteral(CONFIG_DIR), QStringLiteral(SYSTEM_CONFIG_DIR), - - // Name Type Default value Description - Entry(HaltCommand, QString, _S(HALT_COMMAND), _S("Halt command")); - Entry(RebootCommand, QString, _S(REBOOT_COMMAND), _S("Reboot command")); - Entry(Namespaces, QStringList, QStringList(), _S("Comma-separated list of Linux namespaces for user session to enter")); - // Name Entries (but it's a regular class again) - Section(Theme, - Entry(CursorTheme, QString, QString(), _S("Cursor theme used in the greeter")); - Entry(CursorSize, QString, QString(), _S("Cursor size used in the greeter")); - ); - - // TODO: Not absolutely sure if everything belongs here. Xsessions, VT and probably some more seem universal - Section(X11, - Entry(ServerPath, QString, _S("/usr/bin/X"), _S("Path to X server binary")); - Entry(ServerArguments, QString, _S("-nolisten tcp"), _S("Arguments passed to the X server invocation")); - Entry(SessionDir, QStringList, {_S("/usr/local/share/xsessions"), - _S("/usr/share/xsessions")}, _S("Comma-separated list of directories containing available X sessions")); - Entry(SessionCommand, QString, _S(SESSION_COMMAND), _S("Path to a script to execute when starting the desktop session")); - Entry(SessionLogFile, QString, _S(".local/share/ddm/xorg-session.log"), _S("Path to the user session log file")); - Entry(DisplayCommand, QString, _S(DATA_INSTALL_DIR "/scripts/Xsetup"), _S("Path to a script to execute when starting the display server")); - Entry(DisplayStopCommand, QString, _S(DATA_INSTALL_DIR "/scripts/Xstop"), _S("Path to a script to execute when stopping the display server")); - ); - - Section(Wayland, - Entry(SessionDir, QStringList, {_S("/usr/local/share/wayland-sessions"), - _S("/usr/share/wayland-sessions")}, _S("Comma-separated list of directories containing available Wayland sessions")); - Entry(SessionCommand, QString, _S(WAYLAND_SESSION_COMMAND), _S("Path to a script to execute when starting the desktop session")); - Entry(SessionLogFile, QString, _S(".local/share/ddm/wayland-session.log"),_S("Path to the user session log file")); - ); - - Section(Single, - Entry(SessionCommand, QString, _S(WAYLAND_SESSION_COMMAND), _S("Path to a script to execute when starting the desktop session")); - ); - - Section(Users, - Entry(DefaultPath, QString, _S("/usr/local/bin:/usr/bin:/bin"), _S("Default $PATH for logged in users")); - Entry(RememberLastUser, bool, true, _S("Remember the last successfully logged in user")); - Entry(RememberLastSession, bool, true, _S("Remember the session of the last successfully logged in user")); - ); - ); - - Config(StateConfig, []()->QString{auto tmp = getpwnam("ddm"); return tmp ? QString::fromLocal8Bit(tmp->pw_dir) : QStringLiteral(STATE_DIR);}().append(QStringLiteral("/state.conf")), QString(), QString(), - Section(Last, - Entry(Session, QString, QString(), _S("Name of the session for the last logged-in user.\n" - "This session will be preselected when the login screen appears.")); - Entry(User, QString, QString(), _S("Name of the last logged-in user.\n" - "This user will be preselected when the login screen appears")); - ); - ); - - extern MainConfig mainConfig; - extern StateConfig stateConfig; -} - -#endif // DDM_CONFIGURATION_H diff --git a/src/common/Session.cpp b/src/common/Session.cpp index 963e3d1..65230f1 100644 --- a/src/common/Session.cpp +++ b/src/common/Session.cpp @@ -28,7 +28,7 @@ #include #include -#include "Configuration.h" +#include "Config.h" #include "Session.h" const QString s_entryExtention = QStringLiteral(".desktop"); @@ -201,11 +201,11 @@ namespace DDM { switch (type) { case WaylandSession: - sessionDirs = mainConfig.Wayland.SessionDir.get(); + sessionDirs = mainConfig.get("Wayland", "SessionDir"); m_xdgSessionType = QStringLiteral("wayland"); break; case X11Session: - sessionDirs = mainConfig.X11.SessionDir.get(); + sessionDirs = mainConfig.get("X11", "SessionDir"); m_xdgSessionType = QStringLiteral("x11"); break; default: diff --git a/src/common/XAuth.cpp b/src/common/XAuth.cpp index a4ef10c..fb2e94e 100644 --- a/src/common/XAuth.cpp +++ b/src/common/XAuth.cpp @@ -35,7 +35,6 @@ #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX #endif -#include "Configuration.h" #include "Constants.h" #include "XAuth.h" diff --git a/src/daemon/DaemonApp.cpp b/src/daemon/DaemonApp.cpp index 4a07e10..df7a408 100644 --- a/src/daemon/DaemonApp.cpp +++ b/src/daemon/DaemonApp.cpp @@ -19,7 +19,7 @@ #include "DaemonApp.h" -#include "Configuration.h" +#include "Config.h" #include "Constants.h" #include "DisplayManager.h" #include "PowerManager.h" @@ -122,7 +122,7 @@ int main(int argc, char **argv) { // spit a complete config file on stdout and quit on demand if (arguments.contains(QStringLiteral("--example-config"))) { DDM::mainConfig.wipe(); - QTextStream(stdout) << DDM::mainConfig.toConfigFull(); + QTextStream(stdout) << DDM::mainConfig.defaultConfig(); return EXIT_SUCCESS; } diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp index 36da2a0..aeff5a8 100644 --- a/src/daemon/Display.cpp +++ b/src/daemon/Display.cpp @@ -22,7 +22,7 @@ #include "Display.h" #include "Auth.h" -#include "Configuration.h" +#include "Config.h" #include "DaemonApp.h" #include "DisplayManager.h" #include "XorgDisplayServer.h" @@ -257,14 +257,14 @@ namespace DDM { // save last user and last session DaemonApp::instance()->displayManager()->setLastActivatedUser(user); - if (mainConfig.Users.RememberLastUser.get()) - stateConfig.Last.User.set(auth->user); + if (mainConfig.get("Users", "RememberLastUser")) + stateConfig.set("Last", "User", auth->user); else - stateConfig.Last.User.setDefault(); - if (mainConfig.Users.RememberLastSession.get()) - stateConfig.Last.Session.set(session.fileName()); + stateConfig.setDefault("Last", "User"); + if (mainConfig.get("Users", "RememberLastSession")) + stateConfig.set("Last", "Session", session.fileName()); else - stateConfig.Last.Session.setDefault(); + stateConfig.setDefault("Last", "Session"); stateConfig.save(); // Prepare session environment @@ -276,7 +276,7 @@ namespace DDM { env.insert(QStringLiteral("XDG_SESSION_PATH"), daemonApp->displayManager()->sessionPath(sessionId)); auth->sessionId = sessionId; - env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get()); + env.insert(QStringLiteral("PATH"), mainConfig.get("Users", "DefaultPath")); env.insert(QStringLiteral("DESKTOP_SESSION"), session.desktopSession()); if (!session.desktopNames().isEmpty()) env.insert(QStringLiteral("XDG_CURRENT_DESKTOP"), session.desktopNames()); @@ -385,10 +385,10 @@ namespace DDM { // Save last user DaemonApp::instance()->displayManager()->setLastActivatedUser(user); - if (mainConfig.Users.RememberLastUser.get()) - stateConfig.Last.User.set(user); + if (mainConfig.get("Users", "RememberLastUser")) + stateConfig.set("Last", "User", user); else - stateConfig.Last.User.setDefault(); + stateConfig.setDefault("Last", "User"); stateConfig.save(); // Find the auth that started the session, which contains full informations diff --git a/src/daemon/PowerManager.cpp b/src/daemon/PowerManager.cpp index 0686378..a3c901e 100644 --- a/src/daemon/PowerManager.cpp +++ b/src/daemon/PowerManager.cpp @@ -19,7 +19,7 @@ #include "PowerManager.h" -#include "Configuration.h" +#include "Config.h" #include "DaemonApp.h" #include "Messages.h" @@ -87,13 +87,13 @@ const QString UPOWER_OBJECT = QStringLiteral("org.freedesktop.UPower"); } void powerOff() const { - auto command = QProcess::splitCommand(mainConfig.HaltCommand.get()); + auto command = QProcess::splitCommand(mainConfig.get("HaltCommand")); const QString program = command.takeFirst(); QProcess::execute(program, command); } void reboot() const { - auto command = QProcess::splitCommand(mainConfig.RebootCommand.get()); + auto command = QProcess::splitCommand(mainConfig.get("RebootCommand")); const QString program = command.takeFirst(); QProcess::execute(program, command); } diff --git a/src/daemon/SeatManager.cpp b/src/daemon/SeatManager.cpp index fcd9899..b894ea5 100644 --- a/src/daemon/SeatManager.cpp +++ b/src/daemon/SeatManager.cpp @@ -19,7 +19,7 @@ #include "SeatManager.h" -#include "Configuration.h" +#include "Config.h" #include "DaemonApp.h" #include "Display.h" diff --git a/src/daemon/UserSession.cpp b/src/daemon/UserSession.cpp index c9732b0..86ca749 100644 --- a/src/daemon/UserSession.cpp +++ b/src/daemon/UserSession.cpp @@ -23,7 +23,7 @@ #include #include "Auth.h" -#include "Configuration.h" +#include "Config.h" #include "TreelandConnector.h" #include "UserSession.h" #include "VirtualTerminal.h" @@ -56,7 +56,7 @@ namespace DDM { switch (type) { case Display::Treeland: { - setProgram(mainConfig.Single.SessionCommand.get()); + setProgram(mainConfig.get("Single", "SessionCommand")); setArguments(QStringList{ command }); qInfo() << "Starting Wayland user session:" << program() << command; QProcess::start(); @@ -92,13 +92,13 @@ namespace DDM { setProcessEnvironment(env); qInfo() << "Starting X11 user session:" << command; - setProgram(mainConfig.X11.SessionCommand.get()); + setProgram(mainConfig.get("X11", "SessionCommand")); setArguments(QStringList{ command }); QProcess::start(); return; } case Display::Wayland: { - setProgram(mainConfig.Wayland.SessionCommand.get()); + setProgram(mainConfig.get("Wayland", "SessionCommand")); setArguments(QStringList{ command }); qInfo() << "Starting Wayland user session:" << program() << command; QProcess::start(); @@ -172,7 +172,7 @@ namespace DDM { } // enter Linux namespaces - for (const QString &ns: mainConfig.Namespaces.get()) { + for (const QString &ns: mainConfig.get("Namespaces")) { qInfo() << "Entering namespace" << ns; int fd = ::open(qPrintable(ns), O_RDONLY); if (fd < 0) { @@ -285,8 +285,8 @@ namespace DDM { QString sessionLog = QStringLiteral("%1/%2") .arg(QString::fromLocal8Bit(pw.pw_dir)) .arg(auth->type == Display::X11 - ? mainConfig.X11.SessionLogFile.get() - : mainConfig.Wayland.SessionLogFile.get()); + ? mainConfig.get("X11", "SessionLogFile") + : mainConfig.get("Wayland", "SessionLogFile")); // create the path QFileInfo finfo(sessionLog); diff --git a/src/daemon/XorgDisplayServer.cpp b/src/daemon/XorgDisplayServer.cpp index 60a77e0..3c07928 100644 --- a/src/daemon/XorgDisplayServer.cpp +++ b/src/daemon/XorgDisplayServer.cpp @@ -20,7 +20,7 @@ #include "XorgDisplayServer.h" -#include "Configuration.h" +#include "Config.h" #include "Display.h" #include @@ -74,8 +74,8 @@ namespace DDM { // set process environment QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get()); - QString xcursorSize = mainConfig.Theme.CursorSize.get(); + env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.get("Theme", "CursorTheme")); + QString xcursorSize = mainConfig.get("Theme", "CursorSize"); if (!xcursorSize.isEmpty()) env.insert(QStringLiteral("XCURSOR_SIZE"), xcursorSize); process->setProcessEnvironment(env); @@ -89,8 +89,8 @@ namespace DDM { // start display server QStringList args; - process->setProgram(mainConfig.X11.ServerPath.get()); - args << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), Qt::SkipEmptyParts) + process->setProgram(mainConfig.get("X11", "ServerPath")); + args << mainConfig.get("X11", "ServerArguments").split(QLatin1Char(' '), Qt::SkipEmptyParts) << QStringLiteral("-background") << QStringLiteral("none") << QStringLiteral("-seat") << static_cast(parent())->name << QStringLiteral("vt%1").arg(vt) @@ -195,7 +195,7 @@ namespace DDM { // log message qDebug() << "Display server stopped."; - QStringList displayStopCommand = QProcess::splitCommand(mainConfig.X11.DisplayStopCommand.get()); + QStringList displayStopCommand = QProcess::splitCommand(mainConfig.get("X11", "DisplayStopCommand")); // create display setup script process QProcess *displayStopScript = new QProcess(); @@ -204,7 +204,7 @@ namespace DDM { QProcessEnvironment env; env.insert(QStringLiteral("DISPLAY"), display); env.insert(QStringLiteral("HOME"), QStringLiteral("/")); - env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get()); + env.insert(QStringLiteral("PATH"), mainConfig.get("Users", "DefaultPath")); env.insert(QStringLiteral("SHELL"), QStringLiteral("/bin/sh")); displayStopScript->setProcessEnvironment(env); @@ -235,11 +235,11 @@ namespace DDM { QProcessEnvironment env; env.insert(QStringLiteral("DISPLAY"), display); env.insert(QStringLiteral("HOME"), QStringLiteral("/")); - env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get()); + env.insert(QStringLiteral("PATH"), mainConfig.get("Users", "DefaultPath")); env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath()); env.insert(QStringLiteral("SHELL"), QStringLiteral("/bin/sh")); - env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get()); - QString xcursorSize = mainConfig.Theme.CursorSize.get(); + env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.get("Theme", "CursorTheme")); + QString xcursorSize = mainConfig.get("Theme", "CursorSize"); if (!xcursorSize.isEmpty()) env.insert(QStringLiteral("XCURSOR_SIZE"), xcursorSize); setCursor->setProcessEnvironment(env); @@ -258,8 +258,8 @@ namespace DDM { } // start display setup script - qDebug() << "Running display setup script " << mainConfig.X11.DisplayCommand.get(); - QStringList displayCommand = QProcess::splitCommand(mainConfig.X11.DisplayCommand.get()); + qDebug() << "Running display setup script " << mainConfig.get("X11", "DisplayCommand"); + QStringList displayCommand = QProcess::splitCommand(mainConfig.get("X11", "DisplayCommand")); const QString program = displayCommand.takeFirst(); displayScript->start(program, displayCommand);