diff --git a/src/core/flameshotdbusadapter.cpp b/src/core/flameshotdbusadapter.cpp index 8718705739..e5fbccb145 100644 --- a/src/core/flameshotdbusadapter.cpp +++ b/src/core/flameshotdbusadapter.cpp @@ -4,6 +4,7 @@ #include "flameshotdbusadapter.h" #include "core/flameshot.h" #include "core/flameshotdaemon.h" +#include "utils/systemnotification.h" FlameshotDBusAdapter::FlameshotDBusAdapter(QObject* parent) : QDBusAbstractAdaptor(parent) @@ -31,3 +32,18 @@ void FlameshotDBusAdapter::attachPin(const QByteArray& data) { FlameshotDaemon::instance()->attachPin(data); } + +void FlameshotDBusAdapter::registerNotificationPath(uint id, + const QString& path) +{ + SystemNotification::registerNotificationPath(id, path); +} + +void FlameshotDBusAdapter::showSaveNotification(const QString& path) +{ + SystemNotification sysNotif; + sysNotif.sendMessage( + QObject::tr("Capture saved as ") + path, + QObject::tr("Flameshot Info"), + path); +} diff --git a/src/core/flameshotdbusadapter.h b/src/core/flameshotdbusadapter.h index 3372be0ce4..58c3e7b803 100644 --- a/src/core/flameshotdbusadapter.h +++ b/src/core/flameshotdbusadapter.h @@ -20,4 +20,6 @@ public slots: Q_NOREPLY void attachTextToClipboard(const QString& text, const QString& notification); Q_NOREPLY void attachPin(const QByteArray& data); + Q_NOREPLY void registerNotificationPath(uint id, const QString& path); + Q_NOREPLY void showSaveNotification(const QString& path); }; diff --git a/src/main.cpp b/src/main.cpp index c57601611a..b97e16e1e0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,11 +19,13 @@ #include "utils/confighandler.h" #include "utils/filenamehandler.h" #include "utils/pathinfo.h" +#include "utils/systemnotification.h" #include "utils/valuehandler.h" #if !(defined(Q_OS_MACOS) || defined(Q_OS_WIN)) #include "core/flameshotdbusadapter.h" #include +#include #include #endif @@ -73,9 +75,50 @@ int requestCaptureAndWait(const CaptureRequest& req) qApp->exit(0); } #else - // if this instance is not daemon, make sure it exit after caputre finish - if (FlameshotDaemon::instance() == nullptr && !Flameshot::instance()->haveExternalWidget()) { + if (FlameshotDaemon::instance() == nullptr && + !Flameshot::instance()->haveExternalWidget()) { +#if !(defined(Q_OS_MACOS) || defined(Q_OS_WIN)) + auto pendingDaemon = + SystemNotification::takePendingDaemonNotifications(); + if (!pendingDaemon.isEmpty()) { + bool delegated = false; + auto bus = QDBusConnection::sessionBus(); + if (bus.isConnected() && + bus.interface()->isServiceRegistered( + QStringLiteral("org.flameshot.Flameshot"))) { + QDBusMessage msg = QDBusMessage::createMethodCall( + QStringLiteral("org.flameshot.Flameshot"), + QStringLiteral("/"), + QLatin1String(""), + QStringLiteral("showSaveNotification")); + delegated = true; + for (const auto& path : pendingDaemon) { + QDBusMessage m = msg; + m << path; + if (!bus.send(m)) { + delegated = false; + break; + } + } + } + if (delegated) { + qApp->exit(E_OK); + } else { + // Fallback: send notification ourselves and wait + SystemNotification sysNotif; + for (const auto& path : pendingDaemon) { + sysNotif.sendMessage( + QObject::tr("Capture saved as ") + path, path); + } + SystemNotification::setExitOnLastAction(true); + QTimer::singleShot(10000, qApp, &QCoreApplication::quit); + } + } else { + qApp->exit(E_OK); + } +#else qApp->exit(E_OK); +#endif } #endif }); diff --git a/src/utils/screenshotsaver.cpp b/src/utils/screenshotsaver.cpp index 29fb9d28ce..a83ef0d16b 100644 --- a/src/utils/screenshotsaver.cpp +++ b/src/utils/screenshotsaver.cpp @@ -8,6 +8,7 @@ #include "utils/desktopinfo.h" #include "utils/filenamehandler.h" #include "utils/globalvalues.h" +#include "utils/systemnotification.h" #include #include @@ -62,8 +63,14 @@ bool saveToFilesystem(const QPixmap& capture, if (okay) { saveMessage += QObject::tr("Capture saved as ") + completePath; - AbstractLogger::info().attachNotificationPath(notificationPath) - << saveMessage; + if (FlameshotDaemon::instance() == nullptr) { + SystemNotification::addPendingDaemonNotification( + notificationPath); + qDebug().noquote() << saveMessage; + } else { + AbstractLogger::info().attachNotificationPath(notificationPath) + << saveMessage; + } } else { saveMessage += QObject::tr("Error trying to save as ") + completePath; @@ -237,7 +244,7 @@ class ClipboardWatcherMimeData : public QMimeData if (m_notified || m_owner.isNull()) return; m_notified = true; - AbstractLogger::info() << QObject::tr("Capture saved to clipboard."); + qDebug() << "Capture saved to clipboard."; QPointer guard = m_owner; QTimer::singleShot(0, [guard]() { if (guard) @@ -317,7 +324,12 @@ bool saveToFilesystemGUI(const QPixmap& capture) ConfigHandler().setSavePath(pathNoFile); QString msg = QObject::tr("Capture saved as ") + savePath; - AbstractLogger().attachNotificationPath(savePath) << msg; + if (FlameshotDaemon::instance() == nullptr) { + SystemNotification::addPendingDaemonNotification(savePath); + qDebug().noquote() << msg; + } else { + AbstractLogger().attachNotificationPath(savePath) << msg; + } if (config.copyPathAfterSave()) { #ifdef Q_OS_WIN diff --git a/src/utils/systemnotification.cpp b/src/utils/systemnotification.cpp index 04aeadb02a..6dd1243200 100644 --- a/src/utils/systemnotification.cpp +++ b/src/utils/systemnotification.cpp @@ -3,12 +3,14 @@ #include "utils/confighandler.h" #include +#include #include #if !(defined(Q_OS_MACOS) || defined(Q_OS_WIN)) #include #include #include #include +#include #else #include "core/flameshotdaemon.h" #endif @@ -45,6 +47,87 @@ SystemNotification::SystemNotification(QObject* parent) #endif } +QMap SystemNotification::s_pendingPaths; +QStringList SystemNotification::s_pendingDaemonNotifications; +bool SystemNotification::s_exitOnLastAction = false; + +SystemNotification* SystemNotification::actionHandler() +{ + static SystemNotification* handler = [] { + auto* h = new SystemNotification(qApp); +#if !(defined(Q_OS_MACOS) || defined(Q_OS_WIN)) + QDBusConnection::sessionBus().connect( + QStringLiteral("org.freedesktop.Notifications"), + QStringLiteral("/org/freedesktop/Notifications"), + QStringLiteral("org.freedesktop.Notifications"), + QStringLiteral("ActionInvoked"), + h, + SLOT(onActionInvoked(uint, QString))); +#endif + return h; + }(); + return handler; +} + +void SystemNotification::onActionInvoked(uint id, const QString& actionKey) +{ + if (actionKey == QLatin1String("default")) { + auto it = s_pendingPaths.find(id); + if (it != s_pendingPaths.end()) { + QDesktopServices::openUrl(QUrl::fromLocalFile(it.value())); + s_pendingPaths.erase(it); + if (s_pendingPaths.isEmpty() && s_exitOnLastAction) { + qApp->exit(); + } + } + } +} + +bool SystemNotification::hasPendingPaths() +{ + return !s_pendingPaths.isEmpty(); +} + +void SystemNotification::setExitOnLastAction(bool exit) +{ + s_exitOnLastAction = exit; +} + +void SystemNotification::registerNotificationPath(uint id, + const QString& path) +{ + actionHandler(); + s_pendingPaths[id] = path; +} + +void SystemNotification::registerNotificationPath( + const QMap& paths) +{ + actionHandler(); + for (auto it = paths.cbegin(); it != paths.cend(); ++it) { + s_pendingPaths.insert(it.key(), it.value()); + } +} + +QMap SystemNotification::takePendingPaths() +{ + auto paths = s_pendingPaths; + s_pendingPaths.clear(); + return paths; +} + +void SystemNotification::addPendingDaemonNotification(const QString& path) +{ + s_pendingDaemonNotifications.append(path); +} + +QStringList SystemNotification::takePendingDaemonNotifications() +{ + auto paths = s_pendingDaemonNotifications; + s_pendingDaemonNotifications.clear(); + return paths; +} + void SystemNotification::sendMessage(const QString& text, const QString& savePath) { @@ -75,11 +158,14 @@ void SystemNotification::sendMessage(const QString& text, if (nullptr != m_interface && m_interface->isValid()) { QList args; QVariantMap hintsMap; + QStringList actions; if (!savePath.isEmpty()) { QUrl fullPath = QUrl::fromLocalFile(savePath); // allows the notification to be dragged and dropped hintsMap[QStringLiteral("x-kde-urls")] = QStringList({ fullPath.toString() }); + // makes the notification body clickable (freedesktop spec) + actions << QStringLiteral("default") << QString(); } args << (qAppName()) // appname @@ -87,11 +173,17 @@ void SystemNotification::sendMessage(const QString& text, << FLAMESHOT_ICON // icon << title // summary << text // body - << QStringList() // actions + << actions // actions << hintsMap // hints << timeout; // timeout - m_interface->callWithArgumentList( + + QDBusReply reply = m_interface->callWithArgumentList( QDBus::AutoDetect, QStringLiteral("Notify"), args); + + if (reply.isValid() && !savePath.isEmpty()) { + actionHandler(); + s_pendingPaths[reply.value()] = savePath; + } } #endif } diff --git a/src/utils/systemnotification.h b/src/utils/systemnotification.h index 51eb65b2db..4ab28f7a15 100644 --- a/src/utils/systemnotification.h +++ b/src/utils/systemnotification.h @@ -3,6 +3,7 @@ #pragma once +#include #include class QDBusInterface; @@ -20,6 +21,22 @@ class SystemNotification : public QObject const QString& savePath, const int timeout = 5000); + static bool hasPendingPaths(); + static void setExitOnLastAction(bool exit); + static void registerNotificationPath(uint id, const QString& path); + static void registerNotificationPath(const QMap& paths); + static QMap takePendingPaths(); + + static void addPendingDaemonNotification(const QString& path); + static QStringList takePendingDaemonNotifications(); + private: + Q_SLOT void onActionInvoked(uint id, const QString& actionKey); + + static SystemNotification* actionHandler(); + QDBusInterface* m_interface; + static QMap s_pendingPaths; + static QStringList s_pendingDaemonNotifications; + static bool s_exitOnLastAction; }; diff --git a/src/widgets/capture/capturewidget.cpp b/src/widgets/capture/capturewidget.cpp index 25a887a8dc..974b63e4b2 100644 --- a/src/widgets/capture/capturewidget.cpp +++ b/src/widgets/capture/capturewidget.cpp @@ -643,9 +643,8 @@ void CaptureWidget::closeEvent(QCloseEvent* event) event->ignore(); m_clipboardWorkaroundDone = true; m_context.request.removeTask(CaptureRequest::COPY); - AbstractLogger::info() - << "GNOME Wayland detected; keeping capture window alive until " - "clipboard data is fetched."; + qDebug() << "GNOME Wayland detected; keeping capture window alive " + "until clipboard data is fetched."; saveToClipboardGnomeWorkaround(pixmap(), this); return; }