generic: drop support for startup notification via D-Bus service

just another way to do what ASN does, the KService::DBusWait mode was
not used too. with this change however all of the process setup code is
moved to a seperate class and the responsibility of KLauncher about ASN
is reduced (ASN now works better for process that fork but if application
claims ASN support and does not send ASN finish then the timeout will be
reached)

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2023-08-19 19:25:01 +03:00
parent 64e890cc1a
commit a50c72de91
13 changed files with 185 additions and 367 deletions

View file

@ -373,9 +373,6 @@ public:
*
* Used by the automatic registration to D-Bus done by KApplication and KUniqueApplication.
*
* IMPORTANT: if the organization domain is set, the .desktop file that describes your
* application should have an entry like X-DBUS-ServiceName=reversed_domain.kmyapp.
*
* @param domain the domain name, for instance kde.org, koffice.org, kdevelop.org, etc.
*/
KAboutData& setOrganizationDomain(const QByteArray &domain);

View file

@ -242,17 +242,6 @@ void KServicePrivate::init( const KDesktopFile *config, KService* q )
parseActions(config, q);
}
QString dbusStartupType = desktopGroup.readEntry("X-DBUS-StartupType").toLower();
entryMap.remove(QLatin1String("X-DBUS-StartupType"));
if (dbusStartupType == QLatin1String("unique"))
m_DBUSStartusType = KService::DBusUnique;
else if (dbusStartupType == QLatin1String("multi"))
m_DBUSStartusType = KService::DBusMulti;
else if (dbusStartupType == QLatin1String("wait"))
m_DBUSStartusType = KService::DBusWait;
else
m_DBUSStartusType = KService::DBusNone;
m_strDesktopEntryName = _name.toLower();
m_bAllowAsDefault = desktopGroup.readEntry("AllowDefault", true);
@ -319,14 +308,13 @@ void KServicePrivate::parseActions(const KDesktopFile *config, KService* q)
void KServicePrivate::load(QDataStream& s)
{
qint8 def, term;
qint8 dst, initpref;
qint8 initpref;
// NOTE: make sure to update the version number in ksycoca.cpp
s >> m_strType >> m_strName >> m_strExec >> m_strIcon
>> term >> m_strTerminalOptions
>> m_strPath >> m_strComment >> def >> m_mapProps
>> m_strLibrary
>> dst
>> m_strDesktopEntryName
>> initpref
>> m_lstKeywords >> m_strGenName
@ -334,7 +322,6 @@ void KServicePrivate::load(QDataStream& s)
m_bAllowAsDefault = (bool)def;
m_bTerminal = (bool)term;
m_DBUSStartusType = (KService::DBusStartupType) dst;
m_initialPreference = initpref;
m_bValid = true;
@ -345,14 +332,12 @@ void KServicePrivate::save(QDataStream& s)
KSycocaEntryPrivate::save( s );
qint8 def = m_bAllowAsDefault, initpref = m_initialPreference;
qint8 term = m_bTerminal;
qint8 dst = (qint8) m_DBUSStartusType;
// NOTE: make sure to update the version number in ksycoca.cpp
s << m_strType << m_strName << m_strExec << m_strIcon
<< term << m_strTerminalOptions
<< m_strPath << m_strComment << def << m_mapProps
<< m_strLibrary
<< dst
<< m_strDesktopEntryName
<< initpref
<< m_lstKeywords << m_strGenName
@ -829,12 +814,6 @@ QString KService::desktopEntryName() const
return d->m_strDesktopEntryName;
}
KService::DBusStartupType KService::dbusStartupType() const
{
Q_D(const KService);
return d->m_DBUSStartusType;
}
QString KService::path() const
{
Q_D(const KService);

View file

@ -163,26 +163,6 @@ public:
*/
QString storageId() const;
/**
* Describes the DBUS Startup type of the service.
* @li None - This service has no DBUS support
* @li Unique - This service provides a unique DBUS service.
* The service name is equal to the desktopEntryName.
* @li Multi - This service provides a DBUS service which can be run
* with multiple instances in parallel. The service name of
* an instance is equal to the desktopEntryName + "-" +
* the PID of the process.
* @li Wait - This service has no DBUS support, the launcher will wait
* till it is finished.
*/
enum DBusStartupType { DBusNone = 0, DBusUnique, DBusMulti, DBusWait };
/**
* Returns the DBUSStartupType supported by this service.
* @return the DBUSStartupType supported by this service
*/
DBusStartupType dbusStartupType() const;
/**
* Returns the working directory to run the program in.
* @return the working directory to run the program in,
@ -268,8 +248,8 @@ public:
QList<KServiceAction> actions() const;
/**
* Checks whether this service can handle several files as
* startup arguments.
* Checks whether this service can handle several files as startup arguments.
*
* @return true if multiple files may be passed to this service at
* startup. False if only one file at a time may be passed.
*/

View file

@ -87,7 +87,6 @@ public:
QVector<KService::ServiceTypeAndPreference> m_serviceTypes;
QString m_strDesktopEntryName;
KService::DBusStartupType m_DBUSStartusType;
QMap<QString,QVariant> m_mapProps;
QStringList m_lstKeywords;
QString m_strGenName;

View file

@ -230,20 +230,6 @@ static bool offerListHasService( const KService::List& offers,
return found;
}
void KServiceTest::testDBUSStartupType()
{
if ( !KSycoca::isAvailable() )
QSKIP( "ksycoca not available", SkipAll );
if ( !m_hasKde4Konsole )
QSKIP( "kde4-konsole.desktop not available", SkipAll );
//KService::Ptr konsole = KService::serviceByMenuId( "kde4-konsole.desktop" );
KService::Ptr konsole = KService::serviceByDesktopName( "konsole" );
QVERIFY(konsole);
QCOMPARE(konsole->menuId(), QString("kde4-konsole.desktop"));
//qDebug() << konsole->entryPath();
QCOMPARE((int)konsole->dbusStartupType(), (int)KService::DBusMulti);
}
void KServiceTest::testByStorageId()
{
if ( !KSycoca::isAvailable() )

View file

@ -37,7 +37,6 @@ private Q_SLOTS:
void testTraderConstraints();
void testHasServiceType1();
void testHasServiceType2();
void testDBUSStartupType();
void testByStorageId();
void testActionsAndDataStream();
void testServiceGroups();

View file

@ -33,33 +33,7 @@
class KUrl;
/**
* KToolInvocation: for starting other programs
*
* @section desktopfiles Desktop files for startServiceBy
*
* The way a service gets started depends on the 'X-DBUS-StartupType'
* entry in the desktop file of the service:
*
* There are three possibilities:
* @li X-DBUS-StartupType=None (default)
* Always start a new service,
* don't wait till the service registers with D-Bus.
* @li X-DBUS-StartupType=Multi
* Always start a new service,
* wait until the service has registered with D-Bus.
* @li X-DBUS-StartupType=Unique
* Only start the service if it isn't already running,
* wait until the service has registered with D-Bus.
* The .desktop file can specify the name that the application will use when registering
* using X-DBUS-ServiceName=org.domain.mykapp. Otherwise org.kde.binaryname is assumed.
*
* @section thread Multi-threading
*
* The static members (apart from self()), have to be called from the QApplication main thread.
* Calls to members are only allowed if there is a Q(Core)Application object created
* If you call the members with signal/slot connections across threads, you can't use the return values
* If a function is called from the wrong thread and it has a return value -1 is returned
* Investigate if this is really needed or if D-Bus is threadsafe anyway
* KToolInvocation starts other programs
*/
class KDECORE_EXPORT KToolInvocation : public QObject
{
@ -276,13 +250,6 @@ public:
static int kdeinitExecWait(const QString &name, const QStringList &args = QStringList(),
QString *error = 0, const QByteArray &startup_id = QByteArray());
Q_SIGNALS:
/**
* Hook for KApplication in kdeui
* @internal
*/
void kapplication_hook(QStringList &env , QByteArray &startup_id);
private:
/**
* @internal

View file

@ -39,9 +39,6 @@
* your application can only be opened once per user or once per host, you
* need to ensure this independently of KUniqueApplication.
*
* The .desktop file for the application should state X-DBUS-StartupType=Unique,
* see ktoolinvocation.h
*
* If your application is used to open files, it should also support the --tempfile
* option (see KCmdLineArgs::addTempFileOption()), to delete tempfiles after use.
* Add X-KDE-HasTempFileOption=true to the .desktop file to indicate this.

View file

@ -19,7 +19,6 @@
#include "klauncher_adaptor.h"
#include "krun.h"
#include "kstandarddirs.h"
#include "kservice.h"
#include "kautostart.h"
#include "kshell.h"
#include "kconfiggroup.h"
@ -28,10 +27,6 @@
#include <QDir>
#include <QApplication>
#include <QThread>
#include <QElapsedTimer>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusReply>
#include <sys/types.h>
#include <sys/wait.h>
@ -59,13 +54,138 @@ static inline int getExitStatus(const pid_t pid)
return WEXITSTATUS(pidstate);
}
static inline bool isASNValid(const QByteArray &asn)
{
return (!asn.isEmpty() && asn != "0");
}
KLauncherProcess::KLauncherProcess(QObject *parent)
: QProcess(parent),
m_kstartupinfo(nullptr),
m_startuptimer(nullptr)
{
connect(
this, SIGNAL(stateChanged(QProcess::ProcessState)),
this, SLOT(slotProcessStateChanged(QProcess::ProcessState))
);
}
void KLauncherProcess::setupStartup(const QByteArray &startup_id, const QString &appexe,
const KService::Ptr kservice, const qint64 timeout)
{
Q_ASSERT(m_kstartupinfoid.none() == true);
bool startupsilent = false;
QByteArray startupwmclass;
if (KRun::checkStartupNotify(kservice.data(), &startupsilent, &startupwmclass)) {
m_kstartupinfoid.initId(!isASNValid(startup_id) ? KStartupInfo::createNewStartupId() : startup_id);
kDebug() << "setting up ASN for" << kservice->entryPath() << m_kstartupinfoid.id();
m_kstartupinfodata.setHostname();
m_kstartupinfodata.setBin(QFileInfo(appexe).fileName());
m_kstartupinfodata.setDescription(i18n("Launching %1", kservice->name()));
m_kstartupinfodata.setIcon(kservice->icon());
m_kstartupinfodata.setApplicationId(kservice->entryPath());
m_kstartupinfodata.setSilent(startupsilent ? KStartupInfoData::Yes : KStartupInfoData::No);
m_kstartupinfodata.setWMClass(startupwmclass);
QProcessEnvironment processenv = QProcess::processEnvironment();
processenv.insert(QString::fromLatin1("DESKTOP_STARTUP_ID"), m_kstartupinfoid.id());
QProcess::setProcessEnvironment(processenv);
sendSIStart(timeout);
} else if (isASNValid(startup_id)) {
kDebug() << "setting up ASN for" << startup_id;
m_kstartupinfoid.initId(startup_id);
m_kstartupinfodata.setHostname();
m_kstartupinfodata.setBin(QFileInfo(appexe).fileName());
m_kstartupinfodata.setDescription(i18n("Launching %1", m_kstartupinfodata.bin()));
QProcessEnvironment processenv = QProcess::processEnvironment();
processenv.insert(QString::fromLatin1("DESKTOP_STARTUP_ID"), QString::fromLatin1(startup_id.constData(), startup_id.size()));
QProcess::setProcessEnvironment(processenv);
sendSIStart(timeout);
} else {
kDebug() << "no ASN for" << appexe;
}
}
void KLauncherProcess::slotProcessStateChanged(QProcess::ProcessState state)
{
kDebug() << "process state changed" << this << state;
if (state == QProcess::Starting && !m_kstartupinfoid.none()) {
m_kstartupinfodata.addPid(QProcess::pid());
sendSIChange();
} else if (state == QProcess::NotRunning && !m_kstartupinfoid.none()) {
sendSIFinish();
}
}
void KLauncherProcess::slotStartupRemoved(const KStartupInfoId &kstartupinfoid,
const KStartupInfoData &kstartupinfodata)
{
if (m_kstartupinfoid.none()) {
return;
}
kDebug() << "startup removed" << kstartupinfoid.id() << m_kstartupinfoid.id();
if (kstartupinfoid.id() == m_kstartupinfoid.id() || kstartupinfodata.is_pid(QProcess::pid())) {
kDebug() << "startup done for process" << this;
sendSIFinish();
}
}
void KLauncherProcess::slotStartupTimeout()
{
kWarning() << "timed out while waiting for process" << this;
sendSIFinish();
}
void KLauncherProcess::sendSIStart(const qint64 timeout)
{
if (m_kstartupinfoid.none()) {
return;
}
kDebug() << "sending ASN start for" << m_kstartupinfodata.bin();
m_kstartupinfo = new KStartupInfo(this);
connect(
m_kstartupinfo, SIGNAL(gotRemoveStartup(KStartupInfoId,KStartupInfoData)),
this, SLOT(slotStartupRemoved(KStartupInfoId,KStartupInfoData))
);
m_startuptimer = new QTimer(this);
m_startuptimer->setSingleShot(true);
m_startuptimer->setInterval(timeout);
connect(m_startuptimer, SIGNAL(timeout()), this, SLOT(slotStartupTimeout()));
m_startuptimer->start();
KStartupInfo::sendStartup(m_kstartupinfoid, m_kstartupinfodata);
}
void KLauncherProcess::sendSIChange()
{
if (m_kstartupinfoid.none()) {
return;
}
kDebug() << "sending ASN change for" << m_kstartupinfodata.bin();
KStartupInfo::sendChange(m_kstartupinfoid, m_kstartupinfodata);
}
void KLauncherProcess::sendSIFinish()
{
if (m_kstartupinfoid.none()) {
return;
}
kDebug() << "sending ASN finish for" << m_kstartupinfodata.bin();
KStartupInfo::sendFinish(m_kstartupinfoid, m_kstartupinfodata);
m_kstartupinfoid = KStartupInfoId();
m_kstartupinfodata = KStartupInfoData();
if (m_startuptimer) {
m_startuptimer->stop();
}
}
KLauncherAdaptor::KLauncherAdaptor(QObject *parent)
: QDBusAbstractAdaptor(parent),
m_dbusconnectioninterface(nullptr),
m_startuptimeout(s_startuptimeout * 1000)
m_startuptimeout(0)
{
m_environment = QProcessEnvironment::systemEnvironment();
m_dbusconnectioninterface = QDBusConnection::sessionBus().interface();
// TODO: config watch
KConfig klauncherconfig("klaunchrc", KConfig::NoGlobals);
@ -78,7 +198,6 @@ KLauncherAdaptor::KLauncherAdaptor(QObject *parent)
KLauncherAdaptor::~KLauncherAdaptor()
{
kDebug() << "terminating processes" << m_processes.size();
cleanup();
}
@ -154,8 +273,9 @@ void KLauncherAdaptor::exec_blind(const QString &name, const QStringList &arg_li
void KLauncherAdaptor::cleanup()
{
kDebug() << "terminating processes" << m_processes.size();
while (!m_processes.isEmpty()) {
QProcess* process = m_processes.takeLast();
KLauncherProcess* process = m_processes.takeLast();
disconnect(process, 0, this, 0);
process->terminate();
if (!process->waitForFinished(s_processtimeout)) {
@ -173,9 +293,8 @@ int KLauncherAdaptor::kdeinit_exec(const QString &app, const QStringList &args,
int KLauncherAdaptor::kdeinit_exec_wait(const QString &app, const QStringList &args, const QStringList &envs, const QString &startup_id)
{
QMutexLocker locker(&m_mutex);
qint64 pid = 0;
int result = startProgram(app, args, envs, startup_id, QDir::currentPath(), pid);
int result = startProgram(app, args, envs, startup_id, QDir::currentPath(), pid, m_startuptimeout);
if (result != KLauncherAdaptor::NoError) {
return result;
}
@ -191,9 +310,8 @@ int KLauncherAdaptor::kdeinit_exec_wait(const QString &app, const QStringList &a
int KLauncherAdaptor::kdeinit_exec_with_workdir(const QString &app, const QStringList &args, const QStringList &envs, const QString &startup_id, const QString &workdir)
{
QMutexLocker locker(&m_mutex);
qint64 pid = 0;
return startProgram(app, args, envs, startup_id, workdir, pid);
return startProgram(app, args, envs, startup_id, workdir, pid, m_startuptimeout);
}
void KLauncherAdaptor::setLaunchEnv(const QString &name, const QString &value)
@ -218,29 +336,12 @@ int KLauncherAdaptor::start_service_by_desktop_name(const QString &serviceName,
int KLauncherAdaptor::start_service_by_desktop_path(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id)
{
QMutexLocker locker(&m_mutex);
KService::Ptr kservice = KService::serviceByStorageId(serviceName);
if (!kservice) {
kWarning() << "invalid service path" << serviceName;
return KLauncherAdaptor::ServiceError;
}
const KService::DBusStartupType dbusstartuptype = kservice->dbusStartupType();
QString dbusServiceName = kservice->property(QString::fromLatin1("X-DBUS-ServiceName"), QVariant::String).toString();
// any unique Katana application/service checks if another instance is running, if it is
// already running starting it may raise its window instead (if it uses KUniqueApplication)
if (dbusstartuptype == KService::DBusUnique && !dbusServiceName.startsWith(QLatin1String("org.kde."))) {
QDBusReply<bool> sessionreply = m_dbusconnectioninterface->isServiceRegistered(dbusServiceName);
if (!sessionreply.isValid()) {
kWarning() << "invalid D-Bus reply for" << dbusServiceName;
return KLauncherAdaptor::DBusError;
}
if (sessionreply.value() == true) {
kDebug() << "service already started" << dbusServiceName;
return KLauncherAdaptor::NoError;
}
}
if (urls.size() > 1 && !kservice->allowMultipleFiles()) {
// TODO: start multiple instances for each URL
kWarning() << "service does not support multiple files" << serviceName;
return KLauncherAdaptor::ServiceError;
}
@ -249,80 +350,10 @@ int KLauncherAdaptor::start_service_by_desktop_path(const QString &serviceName,
kWarning() << "could not process service" << kservice->entryPath();
return KLauncherAdaptor::ArgumentsError;
}
kDebug() << "starting" << kservice->entryPath() << urls << dbusServiceName;
kDebug() << "starting" << kservice->entryPath() << urls;
const QString program = programandargs.takeFirst();
const QStringList programargs = programandargs;
Q_ASSERT(m_kstartupinfoid.none() == true);
m_kstartupinfoid = KStartupInfoId();
m_kstartupinfodata = KStartupInfoData();
bool startupsilent = false;
QByteArray startupwmclass;
if (KRun::checkStartupNotify(kservice.data(), &startupsilent, &startupwmclass)) {
m_kstartupinfoid.initId(startup_id.toLatin1());
m_kstartupinfodata.setBin(QFileInfo(program).fileName());
m_kstartupinfodata.setDescription(i18n("Launching %1", kservice->name()));
m_kstartupinfodata.setIcon(kservice->icon());
m_kstartupinfodata.setApplicationId(kservice->entryPath());
m_kstartupinfodata.setSilent(startupsilent ? KStartupInfoData::Yes : KStartupInfoData::No);
m_kstartupinfodata.setWMClass(startupwmclass);
sendSIStart();
} else {
kDebug() << "no ASN for" << kservice->entryPath();
}
qint64 pid = 0;
int result = startProgram(program, programargs, envs, QString(), QDir::currentPath(), pid);
if (result != KLauncherAdaptor::NoError) {
// sendSIFinish() is called on exec error
return result;
}
if (dbusstartuptype == KService::DBusNone) {
sendSIFinish();
return result;
} else if (dbusstartuptype != KService::DBusNone && dbusServiceName.isEmpty()) {
// not going to guess what service to wait for, bud
kWarning() << "X-DBUS-ServiceName not specified in" << kservice->entryPath();
sendSIFinish();
return result;
} else if (dbusstartuptype == KService::DBusMulti) {
dbusServiceName.append(QLatin1Char('-'));
dbusServiceName.append(QString::number(pid));
}
kDebug() << "waiting for" << pid << dbusServiceName << m_startuptimeout;
QElapsedTimer elapsedtime;
elapsedtime.start();
while (true) {
QDBusReply<bool> sessionreply = m_dbusconnectioninterface->isServiceRegistered(dbusServiceName);
if (!sessionreply.isValid()) {
kWarning() << "invalid D-Bus reply for" << dbusServiceName;
sendSIFinish();
return KLauncherAdaptor::DBusError;
}
// the service unregistered
if (sessionreply.value() == false && dbusstartuptype == KService::DBusWait) {
kDebug() << "service unregistered" << dbusServiceName;
break;
}
// the service registered
if (sessionreply.value() == true && dbusstartuptype != KService::DBusWait) {
kDebug() << "service registered" << dbusServiceName;
break;
}
// or the program is just not registering the service at all
if (elapsedtime.elapsed() >= m_startuptimeout && dbusstartuptype != KService::DBusWait) {
kWarning() << "timed out while waiting for service" << dbusServiceName;
break;
}
// or the program is not even running
if (!isPIDAlive(pid)) {
kWarning() << "service process is not running" << dbusServiceName;
result = getExitStatus(pid);
break;
}
QApplication::processEvents(QEventLoop::AllEvents, s_eventstime);
QThread::msleep(s_sleeptime);
}
sendSIFinish();
return result;
return startProgram(program, programandargs, envs, QString(), QDir::currentPath(), pid, m_startuptimeout, kservice);
}
#ifdef KLAUNCHER_DEBUG
@ -332,21 +363,9 @@ QStringList KLauncherAdaptor::environment() const
}
#endif
void KLauncherAdaptor::slotProcessStateChanged(QProcess::ProcessState state)
{
QProcess* process = qobject_cast<QProcess*>(sender());
kDebug() << "process state changed" << process << state;
if (state == QProcess::Starting && !m_kstartupinfoid.none()) {
m_kstartupinfodata.addPid(process->pid());
sendSIChange();
} else if (state == QProcess::NotRunning && m_kstartupinfodata.is_pid(process->pid())) {
sendSIFinish();
}
}
void KLauncherAdaptor::slotProcessFinished(int exitcode)
{
QProcess* process = qobject_cast<QProcess*>(sender());
KLauncherProcess* process = qobject_cast<KLauncherProcess*>(sender());
kDebug() << "process finished" << process << exitcode;
m_processes.removeAll(process);
}
@ -364,7 +383,9 @@ QString KLauncherAdaptor::findExe(const QString &app) const
return KStandardDirs::findExe(app, environmentpath);
}
int KLauncherAdaptor::startProgram(const QString &app, const QStringList &args, const QStringList &envs, const QString &startup_id, const QString &workdir, qint64 &pid)
int KLauncherAdaptor::startProgram(const QString &app, const QStringList &args, const QStringList &envs,
const QString &startup_id, const QString &workdir, qint64 &pid,
const qint64 timeout, const KService::Ptr kservice)
{
const QString appexe = findExe(app);
if (appexe.isEmpty()) {
@ -372,8 +393,7 @@ int KLauncherAdaptor::startProgram(const QString &app, const QStringList &args,
return KLauncherAdaptor::FindError;
}
QProcess* process = new QProcess(this);
connect(process, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(slotProcessStateChanged(QProcess::ProcessState)));
KLauncherProcess* process = new KLauncherProcess(this);
connect(process, SIGNAL(finished(int)), this, SLOT(slotProcessFinished(int)));
m_processes.append(process);
QProcessEnvironment processenv = m_environment;
@ -390,61 +410,20 @@ int KLauncherAdaptor::startProgram(const QString &app, const QStringList &args,
}
process->setProcessEnvironment(processenv);
process->setWorkingDirectory(workdir);
kDebug() << "starting" << appexe << args << envs;
if (!startup_id.isEmpty()) {
Q_ASSERT(m_kstartupinfoid.none() == true);
m_kstartupinfoid = KStartupInfoId();
m_kstartupinfodata = KStartupInfoData();
m_kstartupinfoid.initId(startup_id.toLatin1());
m_kstartupinfodata.setBin(QFileInfo(appexe).fileName());
m_kstartupinfodata.setDescription(i18n("Launching %1", m_kstartupinfodata.bin()));
sendSIStart();
}
process->setupStartup(startup_id.toLatin1(), appexe, kservice, timeout);
kDebug() << "starting" << appexe << args << envs << workdir;
process->start(appexe, args);
while (process->state() == QProcess::Starting) {
QApplication::processEvents(QEventLoop::AllEvents, s_eventstime);
QThread::msleep(s_sleeptime);
}
if (process->error() == QProcess::FailedToStart || process->error() == QProcess::Crashed) {
sendSIFinish();
kWarning() << "could not start" << appexe;
return KLauncherAdaptor::ExecError;
}
if (!startup_id.isEmpty()) {
sendSIFinish();
}
pid = process->pid();
return KLauncherAdaptor::NoError;
}
void KLauncherAdaptor::sendSIStart() const
{
if (m_kstartupinfoid.none()) {
return;
}
kDebug() << "sending ASN start for" << m_kstartupinfodata.bin();
KStartupInfo::sendStartup(m_kstartupinfoid, m_kstartupinfodata);
}
void KLauncherAdaptor::sendSIChange()
{
if (m_kstartupinfoid.none()) {
return;
}
kDebug() << "sending ASN change for" << m_kstartupinfodata.bin();
KStartupInfo::sendChange(m_kstartupinfoid, m_kstartupinfodata);
}
void KLauncherAdaptor::sendSIFinish()
{
if (m_kstartupinfoid.none()) {
return;
}
kDebug() << "sending ASN finish for" << m_kstartupinfodata.bin();
KStartupInfo::sendFinish(m_kstartupinfoid, m_kstartupinfodata);
m_kstartupinfoid = KStartupInfoId();
m_kstartupinfodata = KStartupInfoData();
}
#include "moc_klauncher_adaptor.cpp"

View file

@ -20,15 +20,41 @@
#define KLAUNCHER_ADAPTOR_H
#include "kstartupinfo.h"
#include "kservice.h"
#include <QDBusAbstractAdaptor>
#include <QDBusMessage>
#include <QDBusConnectionInterface>
#include <QProcess>
#include <QMutex>
#include <QTimer>
// #define KLAUNCHER_DEBUG
class KLauncherProcess : public QProcess
{
Q_OBJECT
public:
explicit KLauncherProcess(QObject *parent);
void setupStartup(const QByteArray &startup_id, const QString &appexe,
const KService::Ptr kservice, const qint64 timeout);
private Q_SLOTS:
void slotProcessStateChanged(QProcess::ProcessState state);
void slotStartupRemoved(const KStartupInfoId &id, const KStartupInfoData &data);
void slotStartupTimeout();
private:
Q_DISABLE_COPY(KLauncherProcess);
void sendSIStart(const qint64 timeout);
void sendSIChange();
void sendSIFinish();
KStartupInfo* m_kstartupinfo;
QTimer* m_startuptimer;
KStartupInfoId m_kstartupinfoid;
KStartupInfoData m_kstartupinfodata;
};
// Adaptor class for interface org.kde.KLauncher
class KLauncherAdaptor: public QDBusAbstractAdaptor
{
@ -40,8 +66,7 @@ public:
ServiceError = -1,
FindError = -2,
ArgumentsError = -3,
ExecError = -4,
DBusError = -5
ExecError = -4
};
KLauncherAdaptor(QObject *parent);
@ -75,23 +100,17 @@ Q_SIGNALS:
void autoStart2Done();
private Q_SLOTS:
void slotProcessStateChanged(QProcess::ProcessState state);
void slotProcessFinished(int exitcode);
private:
QString findExe(const QString &app) const;
int startProgram(const QString &app, const QStringList &args, const QStringList &envs, const QString &startup_id, const QString &workdir, qint64 &pid);
void sendSIStart() const;
void sendSIChange();
void sendSIFinish();
int startProgram(const QString &app, const QStringList &args, const QStringList &envs,
const QString &startup_id, const QString &workdir, qint64 &pid,
const qint64 timeout, const KService::Ptr kservice = KService::Ptr(nullptr));
QMutex m_mutex;
QProcessEnvironment m_environment;
QDBusConnectionInterface* m_dbusconnectioninterface;
qint64 m_startuptimeout;
KStartupInfoId m_kstartupinfoid;
KStartupInfoData m_kstartupinfodata;
QList<QProcess*> m_processes;
QList<KLauncherProcess*> m_processes;
QStringList m_autostart;
};

View file

@ -118,12 +118,6 @@ Type=QString
[PropertyDef::StartupNotify]
Type=bool
[PropertyDef::X-DBUS-ServiceName]
Type=QString
[PropertyDef::X-DBUS-StartupType]
Type=QString
[PropertyDef::X-KDE-ParentApp]
Type=QString

View file

@ -144,67 +144,12 @@
</property>
</widget>
</item>
<item row="2" column="0" >
<widget class="QLabel" name="textLabel12" >
<property name="text" >
<string>&amp;D-Bus registration:</string>
</property>
<property name="buddy" >
<cstring>dbusCombo</cstring>
</property>
</widget>
</item>
<item row="2" column="1" >
<widget class="KComboBox" name="dbusCombo" >
<item>
<property name="text" >
<string>None</string>
</property>
</item>
<item>
<property name="text" >
<string>Multiple Instances</string>
</property>
</item>
<item>
<property name="text" >
<string>Single Instance</string>
</property>
</item>
<item>
<property name="text" >
<string>Run Until Finished</string>
</property>
</item>
</widget>
</item>
<item row="2" column="2" >
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>50</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KComboBox</class>
<extends>QComboBox</extends>
<header>kcombobox.h</header>
</customwidget>
<customwidget>
<class>KLineEdit</class>
<extends>QLineEdit</extends>

View file

@ -2989,6 +2989,10 @@ public:
KDesktopPropsPluginPrivate()
: w(new Ui_KPropertiesDesktopBase())
, m_frame(new QFrame())
, m_terminalBool(false)
, m_suidBool(false)
, m_startupBool(false)
, m_systrayBool(false)
{
}
~KDesktopPropsPluginPrivate()
@ -3002,8 +3006,6 @@ public:
QString m_origCommandStr;
QString m_terminalOptionStr;
QString m_suidUserStr;
QString m_dbusStartupType;
QString m_dbusServiceName;
QString m_origDesktopFile;
bool m_terminalBool;
bool m_suidBool;
@ -3078,11 +3080,7 @@ KDesktopPropsPlugin::KDesktopPropsPlugin(KPropertiesDialog *props)
d->m_terminalOptionStr = config.readEntry( "TerminalOptions");
d->m_suidBool = config.readEntry("X-KDE-SubstituteUID", false);
d->m_suidUserStr = config.readEntry("X-KDE-Username");
d->m_startupBool = config.readEntry("StartupNotify", true);
d->m_dbusStartupType = config.readEntry("X-DBUS-StartupType").toLower();
// ### should there be a GUI for this setting?
// At least we're copying it over to the local file, to avoid side effects (#157853)
d->m_dbusServiceName = config.readEntry("X-DBUS-ServiceName");
d->m_startupBool = config.readEntry("StartupNotify", false);
const QStringList mimeTypes = config.readXdgListEntry("MimeType");
@ -3183,8 +3181,6 @@ void KDesktopPropsPlugin::checkCommandChanged()
{
if (KRun::binaryName(d->w->commandEdit->text(), true) != KRun::binaryName(d->m_origCommandStr, true)) {
d->m_origCommandStr = d->w->commandEdit->text();
d->m_dbusStartupType.clear(); // Reset
d->m_dbusServiceName.clear();
}
}
@ -3254,8 +3250,7 @@ void KDesktopPropsPlugin::applyChanges()
config.writeEntry("X-KDE-SubstituteUID", d->m_suidBool);
config.writeEntry("X-KDE-Username", d->m_suidUserStr);
config.writeEntry("StartupNotify", d->m_startupBool);
config.writeEntry("X-DBUS-StartupType", d->m_dbusStartupType);
config.writeEntry("X-DBUS-ServiceName", d->m_dbusServiceName);
#warning TODO: StartupWMClass
config.sync();
// KSycoca update needed?
@ -3327,16 +3322,6 @@ void KDesktopPropsPlugin::slotAdvanced()
w.startupInfoCheck->setChecked(d->m_startupBool);
w.systrayCheck->setChecked(d->m_systrayBool);
if (d->m_dbusStartupType == QLatin1String("unique")) {
w.dbusCombo->setCurrentIndex(2);
} else if (d->m_dbusStartupType == QLatin1String("multi")) {
w.dbusCombo->setCurrentIndex(1);
} else if (d->m_dbusStartupType == QLatin1String("wait")) {
w.dbusCombo->setCurrentIndex(3);
} else {
w.dbusCombo->setCurrentIndex(0);
}
// Provide username completion up to 1000 users.
KCompletion *kcom = new KCompletion;
kcom->setOrder(KCompletion::Sorted);
@ -3361,7 +3346,6 @@ void KDesktopPropsPlugin::slotAdvanced()
connect(w.suidEdit, SIGNAL(textChanged(QString)), this, SIGNAL(changed()) );
connect(w.startupInfoCheck, SIGNAL(toggled(bool)), this, SIGNAL(changed()) );
connect(w.systrayCheck, SIGNAL(toggled(bool)), this, SIGNAL(changed()) );
connect(w.dbusCombo, SIGNAL(activated(int)), this, SIGNAL(changed()) );
if (dlg.exec() == QDialog::Accepted) {
d->m_terminalOptionStr = w.terminalEdit->text().trimmed();
@ -3374,13 +3358,6 @@ void KDesktopPropsPlugin::slotAdvanced()
if (w.terminalCloseCheck->isChecked()) {
d->m_terminalOptionStr.append(" --noclose");
}
switch(w.dbusCombo->currentIndex()) {
case 1: d->m_dbusStartupType = "multi"; break;
case 2: d->m_dbusStartupType = "unique"; break;
case 3: d->m_dbusStartupType = "wait"; break;
default: d->m_dbusStartupType = "none"; break;
}
}
}