#include <QDir>
#include <QSettings>
#include <QLocale>
#include <QApplication>
#include <QProcess>
#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;
}
bool XSession::isValid() const {
QFileInfo fi(this->exec_);
return !fi.isAbsolute() || (fi.isFile() && fi.isExecutable());
}
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<int, QProcess::ExitStatus>::of(&QProcess::finished), QApplication::instance(), &QCoreApplication::quit);
if (_process->state() == QProcess::Starting || _process->state() == QProcess::Running)
return true;
else
return false;
}
int XSession::type() const {
return Session::XSESSION;
}
QList<Session*> XSession::readSessions(const QString& path) {
// load the priorities from the config file
loadXSessionsConfig();
QList<Session*> 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<QString>& 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]/<regex>/<modifiers>/<priority>/[+-]<text>
// - 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();
}