mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 10:22:48 +00:00
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:
parent
64e890cc1a
commit
a50c72de91
13 changed files with 185 additions and 367 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() )
|
||||
|
|
|
@ -37,7 +37,6 @@ private Q_SLOTS:
|
|||
void testTraderConstraints();
|
||||
void testHasServiceType1();
|
||||
void testHasServiceType2();
|
||||
void testDBUSStartupType();
|
||||
void testByStorageId();
|
||||
void testActionsAndDataStream();
|
||||
void testServiceGroups();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -144,67 +144,12 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<widget class="QLabel" name="textLabel12" >
|
||||
<property name="text" >
|
||||
<string>&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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue