service/DesktopIntegration.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "DesktopIntegration.h" | ||
| 2 | |||
| 3 | #include <QByteArray> | ||
| 4 | #include <QDBusConnection> | ||
| 5 | #include <QDBusConnectionInterface> | ||
| 6 | #include <QDBusReply> | ||
| 7 | #include <QGuiApplication> | ||
| 8 | #include <QLoggingCategory> | ||
| 9 | #include <QString> | ||
| 10 | |||
| 11 |
4/7✓ Branch 2 → 3 taken 23 times.
✓ Branch 2 → 8 taken 1591 times.
✓ Branch 4 → 5 taken 23 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 23 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
|
1614 | Q_LOGGING_CATEGORY(lcDesktopIntegration, "mailjd.desktop.integration") |
| 12 | |||
| 13 | namespace { | ||
| 14 | #ifdef MAILJD_UNIT_TEST | ||
| 15 | std::optional<DesktopCapabilities> capabilityOverride; | ||
| 16 | #endif | ||
| 17 | |||
| 18 | 4 | QString validPlatformValue(const QVariantMap &data, const QString &key) { | |
| 19 |
2/2✓ Branch 3 → 4 taken 4 times.
✓ Branch 4 → 5 taken 4 times.
|
4 | const QString value = data.value(key).toString(); |
| 20 |
5/6✓ Branch 8 → 9 taken 4 times.
✗ Branch 8 → 11 not taken.
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 3 times.
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 15 taken 3 times.
|
4 | if (value.isEmpty() || value.size() > 4096) |
| 21 | 1 | return {}; | |
| 22 |
2/2✓ Branch 27 → 17 taken 28 times.
✓ Branch 27 → 28 taken 2 times.
|
30 | for (const QChar character : value) { |
| 23 |
5/6✓ Branch 18 → 19 taken 28 times.
✗ Branch 18 → 21 not taken.
✓ Branch 20 → 21 taken 1 time.
✓ Branch 20 → 22 taken 27 times.
✓ Branch 23 → 24 taken 1 time.
✓ Branch 23 → 26 taken 27 times.
|
28 | if (character.isNull() || character.category() == QChar::Other_Control) |
| 24 | 1 | return {}; | |
| 25 | } | ||
| 26 | 2 | return value; | |
| 27 | 4 | } | |
| 28 | |||
| 29 | 3228 | bool serviceAvailable(const QString &name) { | |
| 30 |
2/2✓ Branch 2 → 3 taken 3228 times.
✓ Branch 3 → 4 taken 3228 times.
|
3228 | auto *interface = QDBusConnection::sessionBus().interface(); |
| 31 |
1/2✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 3228 times.
|
3228 | if (!interface) |
| 32 | ✗ | return false; | |
| 33 |
1/1✓ Branch 7 → 8 taken 3228 times.
|
3228 | const QDBusReply<bool> reply = interface->isServiceRegistered(name); |
| 34 |
4/5✓ Branch 8 → 9 taken 3228 times.
✓ Branch 9 → 10 taken 3228 times.
✗ Branch 9 → 13 not taken.
✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 13 taken 3226 times.
|
3228 | return reply.isValid() && reply.value(); |
| 35 | 3228 | } | |
| 36 | |||
| 37 | 1614 | bool isKdeSession() { | |
| 38 |
2/2✓ Branch 2 → 3 taken 1614 times.
✓ Branch 3 → 4 taken 1614 times.
|
1614 | const QByteArray currentDesktop = qgetenv("XDG_CURRENT_DESKTOP").toUpper(); |
| 39 |
2/2✓ Branch 5 → 6 taken 1614 times.
✓ Branch 6 → 7 taken 1614 times.
|
1614 | const QByteArray sessionDesktop = qgetenv("XDG_SESSION_DESKTOP").toUpper(); |
| 40 |
1/2✓ Branch 12 → 13 taken 1614 times.
✗ Branch 12 → 20 not taken.
|
1614 | return qEnvironmentVariableIsSet("KDE_FULL_SESSION") || |
| 41 |
2/5✓ Branch 10 → 11 taken 1614 times.
✓ Branch 15 → 16 taken 1614 times.
✗ Branch 15 → 20 not taken.
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 45 not taken.
|
4842 | currentDesktop.split(':').contains("KDE") || |
| 42 |
3/7✓ Branch 13 → 14 taken 1614 times.
✓ Branch 17 → 18 taken 1614 times.
✗ Branch 17 → 20 not taken.
✓ Branch 25 → 26 taken 1614 times.
✗ Branch 25 → 27 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
|
4842 | currentDesktop.split(':').contains("PLASMA") || |
| 43 |
3/6✓ Branch 9 → 10 taken 1614 times.
✗ Branch 9 → 20 not taken.
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 1614 times.
✓ Branch 23 → 24 taken 1614 times.
✗ Branch 23 → 25 not taken.
|
6456 | sessionDesktop == "KDE" || sessionDesktop == "PLASMA"; |
| 44 | 1614 | } | |
| 45 | } // namespace | ||
| 46 | |||
| 47 | 1615 | DesktopCapabilities DesktopIntegration::capabilities() { | |
| 48 | #ifdef MAILJD_UNIT_TEST | ||
| 49 |
2/2✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 6 taken 7 times.
|
8 | if (capabilityOverride) |
| 50 | 1 | return *capabilityOverride; | |
| 51 | #endif | ||
| 52 | 1614 | DesktopCapabilities result; | |
| 53 |
2/2✓ Branch 2 → 3 taken 1607 times.
✓ Branch 6 → 7 taken 7 times.
|
1614 | result.kdeSession = isKdeSession(); |
| 54 |
4/4✓ Branch 3 → 4 taken 1607 times.
✓ Branch 4 → 5 taken 1607 times.
✓ Branch 7 → 8 taken 7 times.
✓ Branch 8 → 9 taken 7 times.
|
1614 | result.sessionBus = QDBusConnection::sessionBus().isConnected(); |
| 55 |
2/4✓ Branch 6 → 7 taken 1607 times.
✗ Branch 6 → 20 not taken.
✓ Branch 10 → 11 taken 7 times.
✗ Branch 10 → 24 not taken.
|
1614 | if (result.sessionBus) { |
| 56 | 1614 | result.notifications = | |
| 57 |
2/2✓ Branch 10 → 11 taken 1607 times.
✓ Branch 14 → 15 taken 7 times.
|
1614 | serviceAvailable(QStringLiteral("org.freedesktop.Notifications")); |
| 58 | 1614 | result.statusNotifierHost = | |
| 59 |
2/2✓ Branch 16 → 17 taken 1607 times.
✓ Branch 20 → 21 taken 7 times.
|
1614 | serviceAvailable(QStringLiteral("org.kde.StatusNotifierWatcher")); |
| 60 | } | ||
| 61 | 1614 | return result; | |
| 62 | } | ||
| 63 | |||
| 64 | 1619 | DesktopBackends DesktopIntegration::select( | |
| 65 | const DesktopCapabilities &capabilities, bool kdeCompiled, | ||
| 66 | bool windowSystemCompiled) { | ||
| 67 | 1619 | DesktopBackends result; | |
| 68 |
4/4✓ Branch 2 → 3 taken 1614 times.
✓ Branch 2 → 6 taken 5 times.
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 6 taken 1611 times.
|
1622 | result.kdeNotifications = kdeCompiled && capabilities.kdeSession && |
| 69 |
2/2✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 6 taken 1 time.
|
3 | capabilities.notifications; |
| 70 |
4/4✓ Branch 7 → 8 taken 1614 times.
✓ Branch 7 → 11 taken 5 times.
✓ Branch 8 → 9 taken 3 times.
✓ Branch 8 → 11 taken 1611 times.
|
1622 | result.kdeStatusNotifier = kdeCompiled && capabilities.kdeSession && |
| 71 |
2/2✓ Branch 9 → 10 taken 2 times.
✓ Branch 9 → 11 taken 1 time.
|
3 | capabilities.statusNotifierHost; |
| 72 | 1619 | result.kdeDbusService = | |
| 73 |
5/6✓ Branch 12 → 13 taken 1614 times.
✓ Branch 12 → 16 taken 5 times.
✓ Branch 13 → 14 taken 3 times.
✓ Branch 13 → 16 taken 1611 times.
✓ Branch 14 → 15 taken 3 times.
✗ Branch 14 → 16 not taken.
|
1619 | kdeCompiled && capabilities.kdeSession && capabilities.sessionBus; |
| 74 | // KWindowSystem's activation-token bridge is a window-system facility, not | ||
| 75 | // a Plasma-only backend. It is required for token-aware activation on | ||
| 76 | // Mutter and other Wayland compositors too. | ||
| 77 | 1619 | result.kdeWindowActivation = windowSystemCompiled; | |
| 78 | 1619 | return result; | |
| 79 | } | ||
| 80 | |||
| 81 | 1614 | DesktopBackends DesktopIntegration::current() { | |
| 82 | #ifdef MAILJD_KDE_INTEGRATION | ||
| 83 | 1610 | constexpr bool kdeCompiled = true; | |
| 84 | #else | ||
| 85 | 4 | constexpr bool kdeCompiled = false; | |
| 86 | #endif | ||
| 87 | #ifdef MAILJD_HAVE_KWINDOWSYSTEM | ||
| 88 | 1607 | constexpr bool windowSystemCompiled = true; | |
| 89 | #else | ||
| 90 | 7 | constexpr bool windowSystemCompiled = false; | |
| 91 | #endif | ||
| 92 | const DesktopBackends result = | ||
| 93 |
1/1✓ Branch 2 → 3 taken 1614 times.
|
1614 | select(capabilities(), kdeCompiled, windowSystemCompiled); |
| 94 |
4/4✓ Branch 4 → 5 taken 1614 times.
✓ Branch 8 → 9 taken 1614 times.
✓ Branch 32 → 6 taken 1614 times.
✓ Branch 32 → 33 taken 1614 times.
|
3228 | qCInfo(lcDesktopIntegration) |
| 95 |
1/1✓ Branch 9 → 10 taken 1614 times.
|
1614 | << "selected desktop backends: notifications=" |
| 96 |
2/3✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 1614 times.
✓ Branch 13 → 14 taken 1614 times.
|
1614 | << (result.kdeNotifications ? "KDE" : "freedesktop") |
| 97 |
3/4✓ Branch 14 → 15 taken 1614 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 1614 times.
✓ Branch 18 → 19 taken 1614 times.
|
1614 | << "tray=" << (result.kdeStatusNotifier ? "KDE" : "Qt") |
| 98 |
3/4✓ Branch 19 → 20 taken 1614 times.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 1614 times.
✓ Branch 23 → 24 taken 1614 times.
|
1614 | << "single-instance=" << (result.kdeDbusService ? "KDE" : "D-Bus") |
| 99 |
4/4✓ Branch 24 → 25 taken 1614 times.
✓ Branch 25 → 26 taken 1607 times.
✓ Branch 25 → 27 taken 7 times.
✓ Branch 28 → 29 taken 1614 times.
|
1614 | << "activation=" << (result.kdeWindowActivation ? "KDE" : "Qt"); |
| 100 | 1614 | return result; | |
| 101 | } | ||
| 102 | |||
| 103 | 6 | StatusNotifierState DesktopIntegration::statusNotifierState( | |
| 104 | int unreadCount, bool windowVisible) { | ||
| 105 | // NeedsAttention is deliberately not part of this policy. Plasma may blink | ||
| 106 | // or pulse items in that state, while an unread counter is persistent | ||
| 107 | // application status rather than a request for immediate intervention. | ||
| 108 |
4/4✓ Branch 2 → 3 taken 4 times.
✓ Branch 2 → 4 taken 2 times.
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 5 taken 2 times.
|
6 | return (unreadCount > 0 || !windowVisible) ? StatusNotifierState::Active |
| 109 | 6 | : StatusNotifierState::Passive; | |
| 110 | } | ||
| 111 | |||
| 112 | DesktopActivationContext | ||
| 113 | 2 | DesktopIntegration::activationContext(const QVariantMap &platformData) { | |
| 114 | 2 | DesktopActivationContext result; | |
| 115 | result.activationToken = | ||
| 116 |
1/1✓ Branch 5 → 6 taken 2 times.
|
2 | validPlatformValue(platformData, QStringLiteral("activation-token")); |
| 117 |
1/1✓ Branch 13 → 14 taken 2 times.
|
4 | result.desktopStartupId = validPlatformValue( |
| 118 | 6 | platformData, QStringLiteral("desktop-startup-id")); | |
| 119 | 2 | return result; | |
| 120 | ✗ | } | |
| 121 | |||
| 122 | 2 | QVariantMap DesktopIntegration::currentActivationPlatformData() { | |
| 123 | 2 | QVariantMap result; | |
| 124 | const QString activationToken = | ||
| 125 |
2/2✓ Branch 2 → 3 taken 2 times.
✓ Branch 3 → 4 taken 2 times.
|
2 | QString::fromUtf8(qgetenv("XDG_ACTIVATION_TOKEN")); |
| 126 |
2/2✓ Branch 5 → 6 taken 2 times.
✓ Branch 6 → 7 taken 2 times.
|
2 | const QString startupId = QString::fromUtf8(qgetenv("DESKTOP_STARTUP_ID")); |
| 127 |
1/2✗ Branch 9 → 10 not taken.
✓ Branch 9 → 19 taken 2 times.
|
2 | if (!activationToken.isEmpty()) |
| 128 | ✗ | result.insert(QStringLiteral("activation-token"), activationToken); | |
| 129 |
1/2✗ Branch 20 → 21 not taken.
✓ Branch 20 → 30 taken 2 times.
|
2 | if (!startupId.isEmpty()) |
| 130 | ✗ | result.insert(QStringLiteral("desktop-startup-id"), startupId); | |
| 131 | 2 | return result; | |
| 132 | 2 | } | |
| 133 | |||
| 134 | 17 | bool DesktopIntegration::isWaylandSession() { | |
| 135 |
2/2✓ Branch 2 → 3 taken 17 times.
✓ Branch 3 → 4 taken 17 times.
|
17 | const QString platform = QGuiApplication::platformName().toLower(); |
| 136 |
5/10✓ Branch 6 → 7 taken 17 times.
✗ Branch 6 → 13 not taken.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 17 times.
✓ Branch 14 → 15 taken 17 times.
✗ Branch 14 → 16 not taken.
✓ Branch 16 → 17 taken 17 times.
✗ Branch 16 → 18 not taken.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 27 taken 17 times.
|
34 | if (!platform.isEmpty() && platform != QStringLiteral("offscreen")) |
| 137 | ✗ | return platform.startsWith(QStringLiteral("wayland")); | |
| 138 | // Offscreen UI tests cannot load a second QPA plugin after QApplication was | ||
| 139 | // created. A wayland-test marker exercises the policy without pretending to | ||
| 140 | // provide compositor acceptance; production always uses platformName(). | ||
| 141 |
2/2✓ Branch 27 → 28 taken 17 times.
✓ Branch 28 → 29 taken 17 times.
|
17 | return qgetenv("QT_QPA_PLATFORM").toLower().startsWith("wayland"); |
| 142 | 17 | } | |
| 143 | |||
| 144 | #ifdef MAILJD_UNIT_TEST | ||
| 145 | 2 | void DesktopIntegration::setCapabilitiesForTest( | |
| 146 | const std::optional<DesktopCapabilities> &capabilities) { | ||
| 147 | 2 | capabilityOverride = capabilities; | |
| 148 | 2 | } | |
| 149 | |||
| 150 | #endif | ||
| 151 |