diff options
Diffstat (limited to 'src/gui/platform/unix/qgenericunixservices.cpp')
-rw-r--r-- | src/gui/platform/unix/qgenericunixservices.cpp | 224 |
1 files changed, 170 insertions, 54 deletions
diff --git a/src/gui/platform/unix/qgenericunixservices.cpp b/src/gui/platform/unix/qgenericunixservices.cpp index eb66e5b1a4..bfd2556b1e 100644 --- a/src/gui/platform/unix/qgenericunixservices.cpp +++ b/src/gui/platform/unix/qgenericunixservices.cpp @@ -5,6 +5,9 @@ #include <QtGui/private/qtguiglobal_p.h> #include "qguiapplication.h" #include "qwindow.h" +#include <QtGui/qpa/qplatformwindow_p.h> +#include <QtGui/qpa/qplatformwindow.h> +#include <QtGui/qpa/qplatformnativeinterface.h> #include <QtCore/QDebug> #include <QtCore/QFile> @@ -128,8 +131,13 @@ static inline bool detectWebBrowser(const QByteArray &desktop, return false; } -static inline bool launch(const QString &launcher, const QUrl &url) +static inline bool launch(const QString &launcher, const QUrl &url, + const QString &xdgActivationToken) { + if (!xdgActivationToken.isEmpty()) { + qputenv("XDG_ACTIVATION_TOKEN", xdgActivationToken.toUtf8()); + } + const QString command = launcher + u' ' + QLatin1StringView(url.toEncoded()); if (debug) qDebug("Launching %s", qPrintable(command)); @@ -145,16 +153,20 @@ static inline bool launch(const QString &launcher, const QUrl &url) #endif if (!ok) qWarning("Launch failed (%s)", qPrintable(command)); + + qunsetenv("XDG_ACTIVATION_TOKEN"); + return ok; } #if QT_CONFIG(dbus) static inline bool checkNeedPortalSupport() { - return !QStandardPaths::locate(QStandardPaths::RuntimeLocation, "flatpak-info"_L1).isEmpty() || qEnvironmentVariableIsSet("SNAP"); + return QFileInfo::exists("/.flatpak-info"_L1) || qEnvironmentVariableIsSet("SNAP"); } -static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url, const QString &parentWindow) +static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url, const QString &parentWindow, + const QString &xdgActivationToken) { // DBus signature: // OpenFile (IN s parent_window, @@ -165,8 +177,7 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url, const QStri // handle_token (s) - A string that will be used as the last element of the @handle. // writable (b) - Whether to allow the chosen application to write to the file. -#ifdef O_PATH - const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_PATH); + const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_RDONLY); if (fd != -1) { QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop"_L1, "/org/freedesktop/portal/desktop"_L1, @@ -176,21 +187,22 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url, const QStri QDBusUnixFileDescriptor descriptor; descriptor.giveFileDescriptor(fd); - const QVariantMap options = {{"writable"_L1, true}}; + QVariantMap options = {}; + + if (!xdgActivationToken.isEmpty()) { + options.insert("activation_token"_L1, xdgActivationToken); + } message << parentWindow << QVariant::fromValue(descriptor) << options; return QDBusConnection::sessionBus().call(message); } -#else - Q_UNUSED(url); - Q_UNUSED(parentWindow) -#endif return QDBusMessage::createError(QDBusError::InternalError, qt_error_string()); } -static inline QDBusMessage xdgDesktopPortalOpenUrl(const QUrl &url, const QString &parentWindow) +static inline QDBusMessage xdgDesktopPortalOpenUrl(const QUrl &url, const QString &parentWindow, + const QString &xdgActivationToken) { // DBus signature: // OpenURI (IN s parent_window, @@ -208,12 +220,19 @@ static inline QDBusMessage xdgDesktopPortalOpenUrl(const QUrl &url, const QStrin "org.freedesktop.portal.OpenURI"_L1, "OpenURI"_L1); // FIXME parent_window_id and handle writable option - message << parentWindow << url.toString() << QVariantMap(); + QVariantMap options; + + if (!xdgActivationToken.isEmpty()) { + options.insert("activation_token"_L1, xdgActivationToken); + } + + message << parentWindow << url.toString() << options; return QDBusConnection::sessionBus().call(message); } -static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url, const QString &parentWindow) +static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url, const QString &parentWindow, + const QString &xdgActivationToken) { // DBus signature: // ComposeEmail (IN s parent_window, @@ -248,6 +267,10 @@ static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url, const QStr options.insert("attachment_fds"_L1, QVariant::fromValue(attachments)); #endif + if (!xdgActivationToken.isEmpty()) { + options.insert("activation_token"_L1, xdgActivationToken); + } + QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop"_L1, "/org/freedesktop/portal/desktop"_L1, "org.freedesktop.portal.Email"_L1, @@ -331,9 +354,13 @@ private Q_SLOTS: { if (result != 0) return; - XDGDesktopColor color{}; - map.value(u"color"_s).value<QDBusArgument>() >> color; - Q_EMIT colorPicked(color.toQColor()); + if (map.contains(u"color"_s)) { + XDGDesktopColor color{}; + map.value(u"color"_s).value<QDBusArgument>() >> color; + Q_EMIT colorPicked(color.toQColor()); + } else { + Q_EMIT colorPicked({}); + } deleteLater(); } @@ -347,6 +374,9 @@ private: QGenericUnixServices::QGenericUnixServices() { #if QT_CONFIG(dbus) + if (qEnvironmentVariableIntValue("QT_NO_XDG_DESKTOP_PORTAL") > 0) { + return; + } QDBusMessage message = QDBusMessage::createMethodCall( "org.freedesktop.portal.Desktop"_L1, "/org/freedesktop/portal/desktop"_L1, "org.freedesktop.DBus.Properties"_L1, "Get"_L1); @@ -389,60 +419,117 @@ QByteArray QGenericUnixServices::desktopEnvironment() const return result; } +template<typename F> +void runWithXdgActivationToken(F &&functionToCall) +{ +#if QT_CONFIG(wayland) + QWindow *window = qGuiApp->focusWindow(); + + if (!window) { + functionToCall({}); + return; + } + + auto waylandApp = dynamic_cast<QNativeInterface::QWaylandApplication *>( + qGuiApp->platformNativeInterface()); + auto waylandWindow = + dynamic_cast<QNativeInterface::Private::QWaylandWindow *>(window->handle()); + + if (!waylandWindow || !waylandApp) { + functionToCall({}); + return; + } + + QObject::connect(waylandWindow, + &QNativeInterface::Private::QWaylandWindow::xdgActivationTokenCreated, + waylandWindow, functionToCall, Qt::SingleShotConnection); + waylandWindow->requestXdgActivationToken(waylandApp->lastInputSerial()); +#else + functionToCall({}); +#endif +} + bool QGenericUnixServices::openUrl(const QUrl &url) { - if (url.scheme() == "mailto"_L1) { -#if QT_CONFIG(dbus) + auto openUrlInternal = [this](const QUrl &url, const QString &xdgActivationToken) { + if (url.scheme() == "mailto"_L1) { +# if QT_CONFIG(dbus) + if (checkNeedPortalSupport()) { + const QString parentWindow = QGuiApplication::focusWindow() + ? portalWindowIdentifier(QGuiApplication::focusWindow()) + : QString(); + QDBusError error = xdgDesktopPortalSendEmail(url, parentWindow, xdgActivationToken); + if (!error.isValid()) + return true; + + // service not running, fall back + } +# endif + return openDocument(url); + } + +# if QT_CONFIG(dbus) if (checkNeedPortalSupport()) { const QString parentWindow = QGuiApplication::focusWindow() ? portalWindowIdentifier(QGuiApplication::focusWindow()) : QString(); - QDBusError error = xdgDesktopPortalSendEmail(url, parentWindow); + QDBusError error = xdgDesktopPortalOpenUrl(url, parentWindow, xdgActivationToken); if (!error.isValid()) return true; + } +# endif - // service not running, fall back + if (m_webBrowser.isEmpty() + && !detectWebBrowser(desktopEnvironment(), true, &m_webBrowser)) { + qWarning("Unable to detect a web browser to launch '%s'", qPrintable(url.toString())); + return false; } -#endif - return openDocument(url); - } + return launch(m_webBrowser, url, xdgActivationToken); + }; -#if QT_CONFIG(dbus) - if (checkNeedPortalSupport()) { - const QString parentWindow = QGuiApplication::focusWindow() - ? portalWindowIdentifier(QGuiApplication::focusWindow()) - : QString(); - QDBusError error = xdgDesktopPortalOpenUrl(url, parentWindow); - if (!error.isValid()) - return true; - } -#endif + if (QGuiApplication::platformName().startsWith("wayland"_L1)) { + runWithXdgActivationToken( + [openUrlInternal, url](const QString &token) { openUrlInternal(url, token); }); + + return true; - if (m_webBrowser.isEmpty() && !detectWebBrowser(desktopEnvironment(), true, &m_webBrowser)) { - qWarning("Unable to detect a web browser to launch '%s'", qPrintable(url.toString())); - return false; + } else { + return openUrlInternal(url, QString()); } - return launch(m_webBrowser, url); } bool QGenericUnixServices::openDocument(const QUrl &url) { -#if QT_CONFIG(dbus) - if (checkNeedPortalSupport()) { - const QString parentWindow = QGuiApplication::focusWindow() - ? portalWindowIdentifier(QGuiApplication::focusWindow()) - : QString(); - QDBusError error = xdgDesktopPortalOpenFile(url, parentWindow); - if (!error.isValid()) - return true; - } -#endif + auto openDocumentInternal = [this](const QUrl &url, const QString &xdgActivationToken) { + +# if QT_CONFIG(dbus) + if (checkNeedPortalSupport()) { + const QString parentWindow = QGuiApplication::focusWindow() + ? portalWindowIdentifier(QGuiApplication::focusWindow()) + : QString(); + QDBusError error = xdgDesktopPortalOpenFile(url, parentWindow, xdgActivationToken); + if (!error.isValid()) + return true; + } +# endif + + if (m_documentLauncher.isEmpty() + && !detectWebBrowser(desktopEnvironment(), false, &m_documentLauncher)) { + qWarning("Unable to detect a launcher for '%s'", qPrintable(url.toString())); + return false; + } + return launch(m_documentLauncher, url, xdgActivationToken); + }; + + if (QGuiApplication::platformName().startsWith("wayland"_L1)) { + runWithXdgActivationToken([openDocumentInternal, url](const QString &token) { + openDocumentInternal(url, token); + }); - if (m_documentLauncher.isEmpty() && !detectWebBrowser(desktopEnvironment(), false, &m_documentLauncher)) { - qWarning("Unable to detect a launcher for '%s'", qPrintable(url.toString())); - return false; + return true; + } else { + return openDocumentInternal(url, QString()); } - return launch(m_documentLauncher, url); } #else @@ -477,9 +564,7 @@ QPlatformServiceColorPicker *QGenericUnixServices::colorPicker(QWindow *parent) QString QGenericUnixServices::portalWindowIdentifier(QWindow *window) { - if (QGuiApplication::platformName() == QLatin1String("xcb")) - return "x11:"_L1 + QString::number(window->winId(), 16); - + Q_UNUSED(window); return QString(); } @@ -492,6 +577,37 @@ bool QGenericUnixServices::hasCapability(Capability capability) const return false; } +void QGenericUnixServices::setApplicationBadge(qint64 number) +{ +#if QT_CONFIG(dbus) + if (qGuiApp->desktopFileName().isEmpty()) { + qWarning("QGuiApplication::desktopFileName() is empty"); + return; + } + + + const QString launcherUrl = QStringLiteral("application://") + qGuiApp->desktopFileName() + QStringLiteral(".desktop"); + const qint64 count = qBound(0, number, 9999); + QVariantMap dbusUnityProperties; + + if (count > 0) { + dbusUnityProperties[QStringLiteral("count")] = count; + dbusUnityProperties[QStringLiteral("count-visible")] = true; + } else { + dbusUnityProperties[QStringLiteral("count-visible")] = false; + } + + auto signal = QDBusMessage::createSignal(QStringLiteral("/com/canonical/unity/launcherentry/") + + qGuiApp->applicationName(), QStringLiteral("com.canonical.Unity.LauncherEntry"), QStringLiteral("Update")); + + signal.setArguments({launcherUrl, dbusUnityProperties}); + + QDBusConnection::sessionBus().send(signal); +#else + Q_UNUSED(number) +#endif +} + QT_END_NAMESPACE #include "qgenericunixservices.moc" |