You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
176 lines
5.7 KiB
C++
176 lines
5.7 KiB
C++
#include "applicationruntime.h"
|
|
#include "languageenvironment.h"
|
|
#include "proxysettings.h"
|
|
|
|
#include <QDBusConnection>
|
|
#include <QDBusInterface>
|
|
#include <QDBusReply>
|
|
#include <QProcess>
|
|
#include <QProcessEnvironment>
|
|
|
|
static const char *kService = "com.cutefish.AppRuntime";
|
|
static const char *kPath = "/AppRuntime";
|
|
|
|
namespace
|
|
{
|
|
// The environment an ordinary application must be started in.
|
|
//
|
|
// cutefish-shell runs on a privileged Wayland connection it inherited as
|
|
// WAYLAND_SOCKET. That connection is the shell's proof of identity, so nothing
|
|
// it launches may inherit it: an application that did would be handed the
|
|
// private cutefish_shell_v1 and cutefish_window_management_v1 globals. The fd
|
|
// itself is close-on-exec, but the variable naming it has to go as well, so
|
|
// children fall back to the public WAYLAND_DISPLAY the session also set.
|
|
QProcessEnvironment applicationEnvironment()
|
|
{
|
|
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
|
|
environment.remove(QStringLiteral("WAYLAND_SOCKET"));
|
|
LanguageEnvironment::apply(environment);
|
|
ProxyEnvironment::apply(environment);
|
|
|
|
// Only used when the caller was started without the public socket name.
|
|
if (!environment.contains(QStringLiteral("WAYLAND_DISPLAY"))) {
|
|
const QString display = qEnvironmentVariable("CUTEFISH_PUBLIC_WAYLAND_DISPLAY");
|
|
if (!display.isEmpty())
|
|
environment.insert(QStringLiteral("WAYLAND_DISPLAY"), display);
|
|
}
|
|
|
|
return environment;
|
|
}
|
|
|
|
template<typename... Args>
|
|
bool callBool(QDBusInterface *iface, const QString &method, const Args &...args)
|
|
{
|
|
if (!iface->isValid())
|
|
return false;
|
|
|
|
const QDBusReply<bool> reply = iface->call(method, args...);
|
|
|
|
return reply.isValid() && reply.value();
|
|
}
|
|
}
|
|
|
|
ApplicationRuntime *ApplicationRuntime::instance()
|
|
{
|
|
static ApplicationRuntime *runtime = new ApplicationRuntime;
|
|
return runtime;
|
|
}
|
|
|
|
ApplicationRuntime::ApplicationRuntime(QObject *parent)
|
|
: QObject(parent)
|
|
{
|
|
}
|
|
|
|
QDBusInterface *ApplicationRuntime::interface()
|
|
{
|
|
if (!m_interface) {
|
|
m_interface = new QDBusInterface(QLatin1String(kService), QLatin1String(kPath),
|
|
QLatin1String(kService),
|
|
QDBusConnection::sessionBus(), this);
|
|
// Launching is a blocking call from user interface code, so a runtime
|
|
// that stops answering must not freeze the caller for the default
|
|
// half minute.
|
|
m_interface->setTimeout(5000);
|
|
QDBusConnection::sessionBus().connect(QLatin1String(kService), QLatin1String(kPath),
|
|
QLatin1String(kService),
|
|
QStringLiteral("applicationLaunched"), this,
|
|
SIGNAL(applicationLaunched(QString, quint32)));
|
|
QDBusConnection::sessionBus().connect(QLatin1String(kService), QLatin1String(kPath),
|
|
QLatin1String(kService),
|
|
QStringLiteral("applicationQuit"), this,
|
|
SIGNAL(applicationQuit(QString, quint32)));
|
|
}
|
|
|
|
return m_interface;
|
|
}
|
|
|
|
bool ApplicationRuntime::available()
|
|
{
|
|
return interface()->isValid();
|
|
}
|
|
|
|
bool ApplicationRuntime::launchApplication(const QString &appId, const QStringList &arguments)
|
|
{
|
|
if (appId.isEmpty())
|
|
return false;
|
|
|
|
QDBusInterface *iface = interface();
|
|
if (!iface->isValid())
|
|
return false;
|
|
|
|
const QDBusReply<uint> reply = iface->call(QStringLiteral("launchApplication"), appId, arguments);
|
|
|
|
return reply.isValid() && reply.value() != 0;
|
|
}
|
|
|
|
bool ApplicationRuntime::launchCommand(const QStringList &command,
|
|
const QString &workingDirectory,
|
|
const QString &appId)
|
|
{
|
|
if (command.isEmpty() || command.first().isEmpty())
|
|
return false;
|
|
|
|
QDBusInterface *iface = interface();
|
|
if (iface->isValid()) {
|
|
const QDBusReply<uint> reply = iface->call(QStringLiteral("launchCommand"), appId,
|
|
command, workingDirectory);
|
|
if (reply.isValid() && reply.value() != 0)
|
|
return true;
|
|
}
|
|
|
|
QProcess process;
|
|
process.setProgram(command.first());
|
|
process.setArguments(command.mid(1));
|
|
process.setWorkingDirectory(workingDirectory);
|
|
process.setProcessEnvironment(applicationEnvironment());
|
|
return process.startDetached();
|
|
}
|
|
|
|
bool ApplicationRuntime::quitApplication(const QString &appId)
|
|
{
|
|
if (appId.isEmpty())
|
|
return false;
|
|
|
|
return callBool(interface(), QStringLiteral("quitApplication"), appId);
|
|
}
|
|
|
|
bool ApplicationRuntime::quitAll()
|
|
{
|
|
return callBool(interface(), QStringLiteral("quitAll"));
|
|
}
|
|
|
|
bool ApplicationRuntime::quitByPid(quint32 pid)
|
|
{
|
|
if (pid == 0)
|
|
return false;
|
|
|
|
return callBool(interface(), QStringLiteral("quitByPid"), pid);
|
|
}
|
|
|
|
bool ApplicationRuntime::isRunning(const QString &appId)
|
|
{
|
|
return callBool(interface(), QStringLiteral("isRunning"), appId);
|
|
}
|
|
|
|
QStringList ApplicationRuntime::runningApplications()
|
|
{
|
|
QDBusInterface *iface = interface();
|
|
if (!iface->isValid())
|
|
return QStringList();
|
|
|
|
const QDBusReply<QStringList> reply = iface->call(QStringLiteral("runningApplications"));
|
|
|
|
return reply.isValid() ? reply.value() : QStringList();
|
|
}
|
|
|
|
QList<quint32> ApplicationRuntime::pidsForApplication(const QString &appId)
|
|
{
|
|
QDBusInterface *iface = interface();
|
|
if (!iface->isValid())
|
|
return QList<quint32>();
|
|
|
|
const QDBusReply<QList<uint>> reply = iface->call(QStringLiteral("pidsForApplication"), appId);
|
|
|
|
return reply.isValid() ? reply.value() : QList<quint32>();
|
|
}
|