summaryrefslogblamecommitdiffstats
path: root/src/xsession.cpp
blob: 05f4d382e319a4f284ff5a1426bb123a46a6faf9 (plain) (tree)
1
2
3
4
5
6
7
8
9




                       
                         

                     
                    
                               
 
                      
                       

                             
               



                                           





                                                                  
                        


                                              
                        













                                                                      

                                              

                                  
                                                        




                                                                   

                                        




















                                                                         





















                                                                               













                                 










                                                                                       





                                 
                              
                  
                                



                                                                  



                                                                        

                                                                       

                                                                                                                           

                                                                       

                                                                       


                           

                                                           
                   



                                 
                                                                                                                                                 
                                                                                             






                                                             


                                               







                                                                                

                           
















                                                      
 







                                                                                     
 
 







































                                                                                                                          
 
                                                                                      















                                                                        
#include <QDir>
#include <QSettings>
#include <QLocale>
#include <QApplication>
#include <QProcess>
#include <QStandardPaths>

#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<int, QProcess::ExitStatus>::of(&QProcess::finished), QApplication::instance(), &QCoreApplication::quit);
    return _process->state() == QProcess::Starting || _process->state() == QProcess::Running;
}

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();
}