#include #include #include #include #include #include #include "xsession.h" #include "globals.h" #include "sessionsiconholder.h" struct DisplayConfig { QString nameChange; QRegularExpression regex; int priority; QChar type; }; static QList< DisplayConfig > priorityList; void XSession::init(const QString& name, const QString& exec, const QString& comment, const QString& icon) { this->name_ = name; this->exec_ = exec; this->comment_ = comment; this->icon_ = icon; this->priority_ = 0; } bool XSession::init(const QString& filename) { this->priority_ = 0; QSettings settings(filename, QSettings::IniFormat); settings.setIniCodec("UTF-8"); settings.beginGroup("Desktop Entry"); if (settings.value("NoDisplay").toString().compare("true") == 0 || settings.value("Hidden").toString().compare("true") == 0) { return false; } if (!settings.contains("Exec")) { return false; } QString exec(settings.value("Exec").toString()); QString locale(QLocale::system().name()); QString language(locale.split("_").at(0)); QString defaultName(""); if (settings.contains("Name")) defaultName = settings.value("Name").toString(); QString name; if (settings.contains("Name[" + locale + "]")) { name = settings.value("Name[" + locale + "]").toString(); } else if (settings.contains("Name[" + language + "]")) { name = settings.value("Name[" + language + "]").toString(); } else if (!defaultName.isEmpty()) { name = defaultName; } else { return false; } QString comment; if (settings.contains("Comment[" + locale + "]")) { comment = settings.value("Comment[" + locale + "]").toString(); } else if (settings.contains("Comment[" + language + "]")) { comment = settings.value("Comment[" + language + "]").toString(); } else { comment = settings.value("Comment").toString(); } QString icon(settings.value("Icon").toString()); if (QDir::isRelativePath(icon)) { // icons with relative paths are too complicated to find // see http://freedesktop.org/wiki/Specifications/icon-theme-spec // let's just ignore them icon = QString(); } // check for xsession priority/name change for (auto &entry : priorityList) { if ((entry.type == 't' && entry.regex.match(defaultName).hasMatch()) || (entry.type == 'e' && entry.regex.match(exec).hasMatch()) || (entry.type == 'f' && entry.regex.match(filename).hasMatch())) { this->priority_ = entry.priority; if (entry.nameChange.isEmpty()) continue; // check if we have a +/- to append/prepend the given text // TODO support localized name changes... QChar pend = entry.nameChange.at(0); if (pend == '+') { name.append(entry.nameChange.mid(1)); continue; } else if (pend == '-') { name.prepend(entry.nameChange.mid(1)); } else { name = entry.nameChange; } } } this->name_ = name; this->exec_ = exec; this->comment_ = comment; this->icon_ = icon; _process = new QProcess(); return true; } bool XSession::isActive() const { return true; } QString XSession::checkCanRunInternal() const { QString exe = this->exec_.left(this->exec_.indexOf(' ')); // -1 means entire string QFileInfo fi(exe); if (!fi.isAbsolute()) { if (!QStandardPaths::findExecutable(exe).isEmpty()) return QString(); } if (fi.isFile() && fi.isExecutable()) return QString(); // Not found return QObject::trUtf8("Binary %1 not found.").arg(exe); } bool XSession::isLocked() const { return false; } QIcon XSession::icon() const { QIcon retIcon; if (!this->icon_.isEmpty()) { retIcon = SessionsIconHolder::get()->getIcon(this->icon_); } if (retIcon.isNull()) { QString icon; if (this->exec_.contains("kde", Qt::CaseInsensitive)) { icon = "kde"; } else if (this->exec_.contains("gnome", Qt::CaseInsensitive)) { icon = "gnome"; } else if (this->exec_.contains("xfce", Qt::CaseInsensitive)) { icon = "xfce"; } else if (this->exec_.startsWith("i3", Qt::CaseInsensitive) || this->exec_.contains("/i3", Qt::CaseInsensitive)) { icon = "i3"; } else if (this->exec_.contains("lxde", Qt::CaseInsensitive)) { icon = "lxde"; } else if (this->exec_.contains("term", Qt::CaseInsensitive)) { icon = "term"; } else { icon = "linux"; } retIcon = SessionsIconHolder::get()->getIcon(icon); } return retIcon; } bool XSession::run() const { _process->start(this->exec_); QObject::connect(_process, QOverload::of(&QProcess::finished), QApplication::instance(), &QCoreApplication::quit); return _process->state() == QProcess::Starting || _process->state() == QProcess::Running; } int XSession::type() const { return Session::XSESSION; } QList XSession::readSessions(const QString& path) { // load the priorities from the config file loadXSessionsConfig(); QList retval; foreach (QFileInfo fi, QDir(path).entryInfoList(QStringList("*.desktop"))) { if (fi.baseName().compare("default") == 0) { continue; } XSession* session = new XSession; if (session->init(fi.absoluteFilePath())) { retval.append(session); } else { delete session; } } return retval; } bool XSession::operator<(const Session& other) const { int p0 = this->priority(); int p1 = other.priority(); if (p0 < p1) return true; if (p0 == p1) { QString d0 = this->shortDescription(); QString d1 = other.shortDescription(); return d0.localeAwareCompare(d1) < 0; } return false; } bool XSession::containsKeywords(const QList& keywords) const { for (int j = 0; j < keywords.length(); ++j) { if (!this->shortDescription().contains(keywords[j], Qt::CaseInsensitive) && !this->description().contains(keywords[j], Qt::CaseInsensitive)) { return false; } } return true; } void XSession::loadXSessionsConfig() { int idx = 0; QFile inputFile(CONFIG_FILE_XSESSIONS); if (!inputFile.open(QIODevice::ReadOnly)) return; QTextStream in(&inputFile); in.setCodec("UTF-8"); while (!in.atEnd()) { QString line = in.readLine(); idx++; if (line.isEmpty()) continue; // start parsing line, the expected format is: // [tef]////[+-] // - t, e, f denotes to match against xsession's title, exec command, filename, respectively. // - regex to match // - modifiers to use when matching (e.g. insensitive) // - priority to set for the xsession // - text to replace the matched title with, only valid in combination with 't'. Optional +/- to append/prepend. QChar matchType = line.at(0); if (!QString("tef").contains(matchType)) continue; QChar delim = line.at(1); int first = find(delim, 2, line); if (first == -1) continue; int second = find(delim, first + 1, line); if (second == -1) continue; int third = find(delim, second + 1, line); QString regex = line.mid(2, first - 2).replace(QString("\\") + delim, QString(delim)); QString flags = line.mid(first + 1, second - first - 1); QString nameChanges(""); int priorityLength = line.length() - second; if (third != -1) { nameChanges = line.mid(third + 1); priorityLength = third - second - 1; } int priority = line.mid(second + 1, priorityLength).toInt(); QRegularExpression::PatternOptions opts = QRegularExpression::NoPatternOption; if (flags.contains("i")) opts |= QRegularExpression::CaseInsensitiveOption; QRegularExpression re(regex, opts); if (!re.isValid()) { qWarning() << "Invalid regex:" << regex << "on line" << idx; continue; } DisplayConfig config; config.type = matchType; config.regex = re; config.nameChange = nameChanges; config.priority = priority; priorityList.append(config); } inputFile.close(); }