PROPERTIES INCLUDE authinfo.h
)
qt_add_dbus_interface(kiocore_dbus_SRCS org.kde.KPasswdServer.xml kpasswdserver_interface)
+
+ qt_add_dbus_interface(kiocore_dbus_SRCS org.freedesktop.PowerManagement.Inhibit.xml inhibit_interface)
+
+ qt_add_dbus_interface(kiocore_dbus_SRCS org.freedesktop.portal.Inhibit.xml portal_inhibit_interface)
+ qt_add_dbus_interface(kiocore_dbus_SRCS org.freedesktop.portal.Request.xml portal_request_interface)
endif()
target_sources(KF6KIOCore PRIVATE
/* Defined if sys/acl.h exists */
#cmakedefine01 HAVE_SYS_ACL_H
+#cmakedefine01 HAVE_QTDBUS
+
#define KDE_INSTALL_FULL_LIBEXECDIR_KF "${KDE_INSTALL_FULL_LIBEXECDIR_KF}"
#define KDE_INSTALL_FULL_KIO_PLUGINDIR "${KDE_INSTALL_FULL_PLUGINDIR}/kf6/kio/"
#include <time.h>
#include <KLocalizedString>
+#include <KSandbox>
#include <KStringHandler>
+#include "kiocoredebug.h"
#include "worker_p.h"
#include <kio/jobuidelegateextension.h>
+#if HAVE_QTDBUS
+#include <QDBusConnection>
+#include <QDBusPendingCallWatcher>
+
+#include "inhibit_interface.h"
+#include "portal_inhibit_interface.h"
+#include "portal_request_interface.h"
+#endif
+
using namespace KIO;
+static constexpr QLatin1String g_portalServiceName{"org.freedesktop.portal.Desktop"};
+static constexpr QLatin1String g_portalInhibitObjectPath{"/org/freedesktop/portal/desktop"};
+
+static constexpr QLatin1String g_inhibitServiceName{"org.freedesktop.PowerManagement.Inhibit"};
+static constexpr QLatin1String g_inhibitObjectPath{"/org/freedesktop/PowerManagement/Inhibit"};
+
Job::Job()
: KCompositeJob(nullptr)
, d_ptr(new JobPrivate)
}
KIO::JobPrivate::~JobPrivate()
+{
+ uninhibitSuspend();
+}
+
+void JobPrivate::doInhibitSuspend()
{
}
+void JobPrivate::inhibitSuspend(const QString &reason)
+{
+#if HAVE_QTDBUS
+ if (KSandbox::isInside()) {
+ Q_ASSERT(m_portalInhibitionRequest.path().isEmpty());
+
+ org::freedesktop::portal::Inhibit inhibitInterface{g_portalServiceName, g_portalInhibitObjectPath, QDBusConnection::sessionBus()};
+ QVariantMap args;
+ if (!reason.isEmpty()) {
+ args.insert(QStringLiteral("reason"), reason);
+ }
+ auto call = inhibitInterface.Inhibit(QString() /* TODO window. */, 4 /* Suspend */, args);
+ // This is not parented to the job, so we can properly clean up the inhibiton
+ // should the job finish before the inhibition has been processed.
+ auto *watcher = new QDBusPendingCallWatcher(call);
+ QPointer<Job> guard(q_ptr);
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, [this, guard, watcher, reason] {
+ QDBusPendingReply<QDBusObjectPath> reply = *watcher;
+
+ if (reply.isError()) {
+ qCWarning(KIO_CORE).nospace() << "Failed to inhibit suspend with reason " << reason << ": " << reply.error().message();
+ } else {
+ const QDBusObjectPath requestPath = reply.value();
+
+ // By the time the inhibition returned, the job was already gone. Uninhibit again.
+ if (!guard) {
+ org::freedesktop::portal::Request requestInterface{g_portalServiceName, requestPath.path(), QDBusConnection::sessionBus()};
+ requestInterface.Close();
+ } else {
+ m_portalInhibitionRequest = requestPath;
+ }
+ }
+
+ watcher->deleteLater();
+ });
+ } else {
+ Q_ASSERT(!m_inhibitionCookie);
+
+ QString appName = q_ptr->property("desktopFileName").toString();
+ if (appName.isEmpty()) {
+ // desktopFileName is in QGuiApplication but we're in KIO Core here.
+ appName = QCoreApplication::instance()->property("desktopFileName").toString();
+ }
+ if (appName.isEmpty()) {
+ appName = QCoreApplication::applicationName();
+ }
+
+ org::freedesktop::PowerManagement::Inhibit inhibitInterface{g_inhibitServiceName, g_inhibitObjectPath, QDBusConnection::sessionBus()};
+ auto call = inhibitInterface.Inhibit(appName, reason);
+ auto *watcher = new QDBusPendingCallWatcher(call);
+ QPointer<Job> guard(q_ptr);
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, [this, guard, watcher, appName, reason] {
+ QDBusPendingReply<uint> reply = *watcher;
+
+ if (reply.isError()) {
+ qCWarning(KIO_CORE).nospace() << "Failed to inhibit suspend for " << appName << " with reason " << reason << ": " << reply.error().message();
+ } else {
+ const uint cookie = reply.value();
+
+ if (!guard) {
+ org::freedesktop::PowerManagement::Inhibit inhibitInterface{g_inhibitServiceName, g_inhibitObjectPath, QDBusConnection::sessionBus()};
+ inhibitInterface.UnInhibit(cookie);
+ } else {
+ m_inhibitionCookie = cookie;
+ }
+ }
+
+ watcher->deleteLater();
+ });
+ }
+#else
+ Q_UNUSED(reason)
+#endif
+}
+
+void JobPrivate::uninhibitSuspend()
+{
+#if HAVE_QTDBUS
+ if (!m_portalInhibitionRequest.path().isEmpty()) {
+ org::freedesktop::portal::Request requestInterface{g_portalServiceName, m_portalInhibitionRequest.path(), QDBusConnection::sessionBus()};
+ auto call = requestInterface.Close();
+ auto *watcher = new QDBusPendingCallWatcher(call, q_ptr);
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q_ptr, [this, watcher] {
+ QDBusPendingReply<> reply = *watcher;
+
+ if (reply.isError()) {
+ qCWarning(KIO_CORE) << "Failed to uninhibit suspend:" << reply.error().message();
+ } else {
+ m_portalInhibitionRequest = QDBusObjectPath();
+ }
+
+ watcher->deleteLater();
+ });
+ } else if (m_inhibitionCookie) {
+ org::freedesktop::PowerManagement::Inhibit inhibitInterface{g_inhibitServiceName, g_inhibitObjectPath, QDBusConnection::sessionBus()};
+ const int cookie = *m_inhibitionCookie;
+ auto call = inhibitInterface.UnInhibit(cookie);
+ auto *watcher = new QDBusPendingCallWatcher(call, q_ptr);
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q_ptr, [this, watcher, cookie] {
+ QDBusPendingReply<> reply = *watcher;
+
+ if (reply.isError()) {
+ qCWarning(KIO_CORE).nospace() << "Failed to uninhibit suspend for cookie" << cookie << ": " << reply.error().message();
+ } else {
+ m_inhibitionCookie.reset();
+ }
+
+ watcher->deleteLater();
+ });
+ }
+#endif
+}
+
void JobPrivate::emitMoving(KIO::Job *job, const QUrl &src, const QUrl &dest)
{
static const QString s_title = i18nc("@title job", "Moving");
return false;
}
}
-
+ d_ptr->uninhibitSuspend();
return true;
}
return false;
}
}
-
+ d_ptr->doInhibitSuspend();
return true;
}
#ifndef KIO_JOB_P_H
#define KIO_JOB_P_H
+#include "config-kiocore.h"
+
#include "commands_p.h"
#include "global.h"
#include "jobtracker.h"
#include <kio/jobuidelegateextension.h>
#include <kio/jobuidelegatefactory.h>
+#if HAVE_QTDBUS
+#include <QDBusObjectPath>
+
+#include <optional>
+#endif
+
/* clang-format off */
#define KIO_ARGS \
QByteArray packedArgs; \
MetaData m_outgoingMetaData;
JobUiDelegateExtension *m_uiDelegateExtension;
Job *q_ptr;
+#if HAVE_QTDBUS
+ std::optional<uint> m_inhibitionCookie; // fdo.
+ QDBusObjectPath m_portalInhibitionRequest; // portal.
+#endif
// For privilege operation
bool m_privilegeExecutionEnabled;
QString m_title, m_message;
QByteArray privilegeOperationData();
void slotSpeed(KJob *job, unsigned long speed);
+ void inhibitSuspend(const QString &reason);
+ void uninhibitSuspend();
+ virtual void doInhibitSuspend();
+
static void emitMoving(KIO::Job *, const QUrl &src, const QUrl &dest);
static void emitRenaming(KIO::Job *, const QUrl &src, const QUrl &dest);
static void emitCopying(KIO::Job *, const QUrl &src, const QUrl &dest);
--- /dev/null
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.freedesktop.PowerManagement.Inhibit">
+ <method name="Inhibit">
+ <arg direction="in" type="s" name="application"/>
+ <arg direction="in" type="s" name="reason"/>
+ <arg direction="out" type="u" name="cookie"/>
+ </method>
+ <method name="UnInhibit">
+ <arg direction="in" type="u" name="cookie"/>
+ </method>
+ <signal name="HasInhibitChanged">
+ <arg direction="out" type="b" name="has_inhibit"/>
+ </signal>
+ <method name="HasInhibit">
+ <arg direction="out" type="b" name="has_inhibit"/>
+ </method>
+ </interface>
+</node>
--- /dev/null
+<?xml version="1.0"?>
+<!--
+ Copyright (C) 2016 Red Hat, Inc.
+
+ SPDX-License-Identifier: LGPL-2.1-or-later
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ Author: Matthias Clasen <mclasen@redhat.com>
+-->
+
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+ <!--
+ org.freedesktop.portal.Inhibit:
+ @short_description: Portal for inhibiting session transitions
+
+ This simple interface lets sandboxed applications inhibit the user
+ session from ending, suspending, idling or getting switched away.
+
+ This documentation describes version 3 of this interface.
+ -->
+ <interface name="org.freedesktop.portal.Inhibit">
+ <!--
+ Inhibit:
+ @window: Identifier for the window
+ @flags: Flags identifying what is inhibited
+ @options: Vardict with optional further information
+ @handle: Object path for the :ref:`org.freedesktop.portal.Request` object representing this call
+
+ Inhibits a session status changes. To remove the inhibition,
+ call :ref:`org.freedesktop.portal.Request.Close` on the returned
+ handle.
+
+ The flags determine what changes are inhibited:
+
+ - ``1``: Logout
+ - ``2``: User Switch
+ - ``4``: Suspend
+ - ``8``: Idle
+
+ Supported keys in the @options vardict include:
+
+ * ``handle_token`` (``s``)
+
+ A string that will be used as the last element of the @handle. Must be a valid
+ object path element. See the :ref:`org.freedesktop.portal.Request` documentation for
+ more information about the @handle.
+
+ * ``reason`` (``s``)
+
+ User-visible reason for the inhibition.
+ -->
+ <method name="Inhibit">
+ <arg type="s" name="window" direction="in"/>
+ <arg type="u" name="flags" direction="in"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QVariantMap"/>
+ <arg type="a{sv}" name="options" direction="in"/>
+ <arg type="o" name="handle" direction="out"/>
+ </method>
+
+ <!--
+ CreateMonitor:
+ @window: the parent window
+ @options: Vardict with optional further information
+ @handle: Object path for the :ref:`org.freedesktop.portal.Request` object representing this call
+
+ Creates a monitoring session. While this session is
+ active, the caller will receive StateChanged signals
+ with updates on the session state.
+
+ A successfully created session can at any time be closed using
+ org.freedesktop.portal.Session::Close, or may at any time be closed
+ by the portal implementation, which will be signalled via
+ :ref:`org.freedesktop.portal.Session::Closed`.
+
+ Supported keys in the @options vardict include:
+
+ * ``handle_token`` (``s``)
+
+ A string that will be used as the last element of the @handle. Must be a valid
+ object path element. See the :ref:`org.freedesktop.portal.Request` documentation for
+ more information about the @handle.
+
+ * ``session_handle_token`` (``s``)
+
+ A string that will be used as the last element of the session handle. Must be a valid
+ object path element. See the :ref:`org.freedesktop.portal.Session` documentation for
+ more information about the session handle.
+
+ The following results get returned via the :ref:`org.freedesktop.portal.Request::Response` signal:
+
+ * ``session_handle`` (``s``)
+
+ The session handle. An object path for the
+ :ref:`org.freedesktop.portal.Session` object representing the created
+ session.
+
+ .. note::
+ The ``session_handle`` is an object path that was erroneously implemented
+ as ``s``. For backwards compatibility it will remain this type.
+
+ This method was added in version 2 of this interface.
+ -->
+ <method name="CreateMonitor">
+ <arg type="s" name="window" direction="in"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
+ <arg type="a{sv}" name="options" direction="in"/>
+ <arg type="o" name="handle" direction="out"/>
+ </method>
+
+ <!--
+ StateChanged:
+ @session_handle: Object path for the :ref:`org.freedesktop.portal.Session` object
+ @state: Vardict with information about the session state
+
+ The StateChanged signal is sent to active monitoring sessions when
+ the session state changes.
+
+ When the session state changes to 'Query End', clients with active monitoring
+ sessions are expected to respond by calling
+ org.freedesktop.portal.Inhibit.QueryEndResponse() within a second
+ of receiving the StateChanged signal. They may call org.freedesktop.portal.Inhibit.Inhibit()
+ first to inhibit logout, to prevent the session from proceeding to the Ending state.
+
+ The following information may get returned in the @state vardict:
+
+ * ``screensaver-active`` (``b``)
+
+ Whether the screensaver is active.
+
+ * ``session-state`` (``u``)
+
+ The state of the session. This member is new in version 3.
+
+ - ``1``: Running
+ - ``2``: Query End
+ - ``3``: Ending
+
+ -->
+ <signal name="StateChanged">
+ <arg type="o" name="session_handle" direction="out"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
+ <arg type="a{sv}" name="state" direction="out"/>
+ </signal>
+
+ <!--
+ QueryEndResponse:
+ @session_handle: Object path for the :ref:`org.freedesktop.portal.Session` object
+
+ Acknowledges that the caller received the #org.freedesktop.portal.Inhibit::StateChanged
+ signal. This method should be called within one second or receiving a StateChanged
+ signal with the 'Query End' state.
+
+ Since version 3.
+ -->
+ <method name="QueryEndResponse">
+ <arg type="o" name="session_handle" direction="in"/>
+ </method>
+
+ <property name="version" type="u" access="read"/>
+ </interface>
+</node>
--- /dev/null
+<?xml version="1.0"?>
+<!--
+ Copyright (C) 2015 Red Hat, Inc.
+
+ SPDX-License-Identifier: LGPL-2.1-or-later
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ Author: Alexander Larsson <alexl@redhat.com>
+-->
+
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+ <!--
+ org.freedesktop.portal.Request:
+ @short_description: Shared request interface
+
+ The Request interface is shared by all portal interfaces. When a
+ portal method is called, the reply includes a handle (i.e. object path)
+ for a Request object, which will stay alive for the duration of the
+ user interaction related to the method call.
+
+ The portal indicates that a portal request interaction is over by
+ emitting the #org.freedesktop.portal.Request::Response signal on the
+ Request object.
+
+ The application can abort the interaction calling
+ org.freedesktop.portal.Request.Close() on the Request object.
+
+ Since version 0.9 of xdg-desktop-portal, the handle will be of the form
+
+ ::
+
+ /org/freedesktop/portal/desktop/request/SENDER/TOKEN
+
+
+ where ``SENDER`` is the callers unique name, with the initial ``':'`` removed and
+ all ``'.'`` replaced by ``'_'``, and ``TOKEN`` is a unique token that the caller provided
+ with the handle_token key in the options vardict.
+
+ This change was made to let applications subscribe to the Response signal before
+ making the initial portal call, thereby avoiding a race condition. It is recommended
+ that the caller should verify that the returned handle is what it expected, and update
+ its signal subscription if it isn't. This ensures that applications will work with both
+ old and new versions of xdg-desktop-portal.
+
+ The token that the caller provides should be unique and not guessable. To avoid clashes
+ with calls made from unrelated libraries, it is a good idea to use a per-library prefix
+ combined with a random number.
+ -->
+ <interface name="org.freedesktop.portal.Request">
+
+ <!--
+ Close:
+
+ Closes the portal request to which this object refers and ends all
+ related user interaction (dialogs, etc).
+
+ A Response signal will not be emitted in this case.
+ -->
+ <method name="Close">
+ </method>
+
+ <!--
+ Response:
+ @response: Numeric response
+ @results: Vardict with results. The keys and values in the vardict depend on the request.
+
+ Emitted when the user interaction for a portal request is over.
+
+ The @response indicates how the user interaction ended:
+
+ - 0: Success, the request is carried out
+ - 1: The user cancelled the interaction
+ - 2: The user interaction was ended in some other way
+ -->
+ <signal name="Response">
+ <arg type="u" name="response"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
+ <arg type="a{sv}" name="results"/>
+ </signal>
+ </interface>
+</node>