File indexing completed on 2024-04-14 15:32:48

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
0004     SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org>
0005 */
0006 
0007 #include "statusnotifier_activationclosetimer.h"
0008 
0009 #include <QDBusConnectionInterface>
0010 
0011 #include <KIdleTime>
0012 
0013 #include "drkonqi_debug.h"
0014 
0015 void ActivationCloseTimer::refresh()
0016 {
0017     qCDebug(DRKONQI_LOG) << m_idle << m_pendingActivations << m_closeTimer.isActive();
0018     if (m_idle) {
0019         m_closeTimer.stop();
0020     } else { // not idle
0021         if (m_pendingActivations > 0) { // going for auto activation instead of closing
0022             m_closeTimer.stop();
0023         } else if (!m_closeTimer.isActive()) { // otherwise we may auto close if not already running
0024             m_closeTimer.start();
0025         }
0026     }
0027 }
0028 
0029 void ActivationCloseTimer::watchIdle(IdleWatcher *idleWatcher)
0030 {
0031     // if nobody bothered to look at the crash after 1 minute, just close
0032     m_closeTimer.setSingleShot(true);
0033     m_closeTimer.setInterval(m_closeTimeout);
0034     connect(&m_closeTimer, &QTimer::timeout, this, &ActivationCloseTimer::autoClose);
0035     m_closeTimer.start();
0036 
0037     // make sure the user doesn't miss the SNI by stopping the auto hide timer when the session becomes idle
0038     connect(idleWatcher, &IdleWatcher::idle, this, [this] {
0039         m_idle = true;
0040         refresh();
0041     });
0042     connect(idleWatcher, &IdleWatcher::notIdle, this, [this] {
0043         m_idle = false;
0044         refresh();
0045     });
0046 
0047     idleWatcher->start();
0048 }
0049 
0050 void ActivationCloseTimer::watchDBus(DBusServiceWatcher *watcher)
0051 {
0052     // Should the SNI host implode and not return within 10s, automatically
0053     // open the dialog.
0054     // We are tracking the related Notifications service here, because actually
0055     // tracking the Host interface is fairly involved with no tangible advantage.
0056 
0057     auto activationTimer = new QTimer(this);
0058     activationTimer->setInterval(m_activationTimeout);
0059     connect(activationTimer, &QTimer::timeout, this, &ActivationCloseTimer::autoActivate);
0060 
0061     connect(watcher, &DBusServiceWatcher::serviceUnregistered, activationTimer, [this, activationTimer] {
0062         ++m_pendingActivations;
0063         activationTimer->start();
0064         refresh();
0065     });
0066     connect(watcher, &DBusServiceWatcher::serviceRegistered, activationTimer, [this, activationTimer] {
0067         if (m_pendingActivations > 0) {
0068             --m_pendingActivations;
0069         }
0070         activationTimer->stop();
0071         refresh();
0072     });
0073 
0074     watcher->start();
0075 }
0076 
0077 void ActivationCloseTimer::start(DBusServiceWatcher *dbusWatcher, IdleWatcher *idleWatcher)
0078 {
0079     connect(this, &ActivationCloseTimer::autoActivate, this, [this] {
0080         m_closeTimer.stop(); // make double sure we don't close once activated!
0081     });
0082     watchDBus(dbusWatcher);
0083     watchIdle(idleWatcher);
0084 }
0085 
0086 void IdleWatcher::start()
0087 {
0088     // make sure the user doesn't miss the SNI by stopping the auto hide timer when the session becomes idle
0089     const auto idleTime = 30s;
0090     const int idleId = KIdleTime::instance()->addIdleTimeout(int(std::chrono::milliseconds(idleTime).count()));
0091     connect(KIdleTime::instance(), static_cast<void (KIdleTime::*)(int, int)>(&KIdleTime::timeoutReached), this, [this, idleId](int id) {
0092         if (idleId == id) {
0093             Q_EMIT idle();
0094         }
0095         // this is apparently needed or else resumingFromIdle is never called
0096         KIdleTime::instance()->catchNextResumeEvent();
0097     });
0098     connect(KIdleTime::instance(), &KIdleTime::resumingFromIdle, this, &IdleWatcher::notIdle);
0099 
0100     if (std::chrono::milliseconds(KIdleTime::instance()->idleTime()) >= idleTime) {
0101         Q_EMIT idle();
0102     }
0103 }
0104 
0105 QVector<QString> DBusServiceWatcher::serviceNames() const
0106 {
0107     return m_serviceNames;
0108 }
0109 
0110 void DBusServiceWatcher::start()
0111 {
0112     const QDBusConnection sessionBus = QDBusConnection::sessionBus();
0113     const QDBusConnectionInterface *sessionInterface = sessionBus.interface();
0114     Q_ASSERT(sessionInterface);
0115 
0116     m_watcher = new QDBusServiceWatcher(this);
0117     m_watcher->setConnection(sessionBus);
0118     m_watcher->setWatchMode(QDBusServiceWatcher::WatchForOwnerChange);
0119     for (const auto &serviceName : m_serviceNames) {
0120         m_watcher->addWatchedService(serviceName);
0121 
0122         // if not currently available queue the activation - this is in case the service isn't available *right now*
0123         // in which case we'll not get any registration events
0124         if (!sessionInterface->isServiceRegistered(serviceName)) {
0125             Q_EMIT serviceUnregistered();
0126         }
0127     }
0128 
0129     connect(m_watcher, &QDBusServiceWatcher::serviceUnregistered, this, &DBusServiceWatcher::serviceUnregistered);
0130     connect(m_watcher, &QDBusServiceWatcher::serviceRegistered, this, &DBusServiceWatcher::serviceRegistered);
0131 }
0132 
0133 void ActivationCloseTimer::setActivationTimeout(std::chrono::milliseconds timeout)
0134 {
0135     m_activationTimeout = timeout;
0136 }
0137 
0138 void ActivationCloseTimer::setCloseTimeout(std::chrono::milliseconds timeout)
0139 {
0140     m_closeTimeout = timeout;
0141 }