File flameshotdaemon.cpp#
File List > core > flameshotdaemon.cpp
Go to the documentation of this file.
#include "flameshotdaemon.h"
#include "abstractlogger.h"
#include "confighandler.h"
#include "flameshot.h"
#include "pinwidget.h"
#include "screenshotsaver.h"
#include "src/utils/globalvalues.h"
#include "src/widgets/capture/capturewidget.h"
#include "src/widgets/trayicon.h"
#include <QApplication>
#include <QClipboard>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QPixmap>
#include <QRect>
#if !defined(DISABLE_UPDATE_CHECKER)
#include <QDesktopServices>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTimer>
#include <QUrl>
#endif
#ifdef Q_OS_WIN
#include "src/core/globalshortcutfilter.h"
#endif
FlameshotDaemon::FlameshotDaemon()
: m_persist(false)
, m_hostingClipboard(false)
, m_clipboardSignalBlocked(false)
, m_trayIcon(nullptr)
#if !defined(DISABLE_UPDATE_CHECKER)
, m_networkCheckUpdates(nullptr)
, m_showCheckAppUpdateStatus(false)
, m_appLatestVersion(QStringLiteral(APP_VERSION).replace("v", ""))
#endif
{
connect(
QApplication::clipboard(), &QClipboard::dataChanged, this, [this]() {
if (!m_hostingClipboard || m_clipboardSignalBlocked) {
m_clipboardSignalBlocked = false;
return;
}
m_hostingClipboard = false;
quitIfIdle();
});
#ifdef Q_OS_WIN
m_persist = true;
#else
m_persist = !ConfigHandler().autoCloseIdleDaemon();
connect(ConfigHandler::getInstance(),
&ConfigHandler::fileChanged,
this,
[this]() {
ConfigHandler config;
enableTrayIcon(!config.disabledTrayIcon());
m_persist = !config.autoCloseIdleDaemon();
});
#endif
#if !defined(DISABLE_UPDATE_CHECKER)
if (ConfigHandler().checkForUpdates()) {
getLatestAvailableVersion();
}
#endif
}
void FlameshotDaemon::start()
{
if (!m_instance) {
m_instance = new FlameshotDaemon();
// Tray icon needs FlameshotDaemon::instance() to be non-null
m_instance->initTrayIcon();
qApp->setQuitOnLastWindowClosed(false);
}
}
void FlameshotDaemon::createPin(const QPixmap& capture, QRect geometry)
{
if (instance()) {
instance()->attachPin(capture, geometry);
return;
}
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << capture;
stream << geometry;
QDBusMessage m = createMethodCall(QStringLiteral("attachPin"));
m << data;
call(m);
}
void FlameshotDaemon::copyToClipboard(const QPixmap& capture)
{
if (instance()) {
instance()->attachScreenshotToClipboard(capture);
return;
}
QDBusMessage m =
createMethodCall(QStringLiteral("attachScreenshotToClipboard"));
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << capture;
m << data;
call(m);
}
void FlameshotDaemon::copyToClipboard(const QString& text,
const QString& notification)
{
if (instance()) {
instance()->attachTextToClipboard(text, notification);
return;
}
auto m = createMethodCall(QStringLiteral("attachTextToClipboard"));
m << text << notification;
QDBusConnection sessionBus = QDBusConnection::sessionBus();
checkDBusConnection(sessionBus);
sessionBus.call(m);
}
bool FlameshotDaemon::isThisInstanceHostingWidgets()
{
return instance() && !instance()->m_widgets.isEmpty();
}
void FlameshotDaemon::sendTrayNotification(const QString& text,
const QString& title,
const int timeout)
{
if (m_trayIcon) {
m_trayIcon->showMessage(
title, text, QIcon(GlobalValues::iconPath()), timeout);
}
}
#if !defined(DISABLE_UPDATE_CHECKER)
void FlameshotDaemon::showUpdateNotificationIfAvailable(CaptureWidget* widget)
{
if (!m_appLatestUrl.isEmpty() &&
ConfigHandler().ignoreUpdateToVersion().compare(m_appLatestVersion) <
0) {
widget->showAppUpdateNotification(m_appLatestVersion, m_appLatestUrl);
}
}
void FlameshotDaemon::getLatestAvailableVersion()
{
// This features is required for MacOS and Windows user and for Linux users
// who installed Flameshot not from the repository.
QNetworkRequest requestCheckUpdates(QUrl(FLAMESHOT_APP_VERSION_URL));
if (nullptr == m_networkCheckUpdates) {
m_networkCheckUpdates = new QNetworkAccessManager(this);
connect(m_networkCheckUpdates,
&QNetworkAccessManager::finished,
this,
&FlameshotDaemon::handleReplyCheckUpdates);
}
m_networkCheckUpdates->get(requestCheckUpdates);
// check for updates each 24 hours
QTimer::singleShot(1000 * 60 * 60 * 24, [this]() {
if (ConfigHandler().checkForUpdates()) {
this->getLatestAvailableVersion();
}
});
}
void FlameshotDaemon::checkForUpdates()
{
if (m_appLatestUrl.isEmpty()) {
m_showCheckAppUpdateStatus = true;
getLatestAvailableVersion();
} else {
QDesktopServices::openUrl(QUrl(m_appLatestUrl));
}
}
#endif
FlameshotDaemon* FlameshotDaemon::instance()
{
// Because we don't use DBus on MacOS, each instance of flameshot is its own
// mini-daemon, responsible for hosting its own persistent widgets (e.g.
// pins).
#if defined(Q_OS_MACOS)
start();
#endif
return m_instance;
}
void FlameshotDaemon::quitIfIdle()
{
if (m_persist) {
return;
}
if (!m_hostingClipboard && m_widgets.isEmpty()) {
qApp->exit(0);
}
}
// SERVICE METHODS
void FlameshotDaemon::attachPin(const QPixmap& pixmap, QRect geometry)
{
auto* pinWidget = new PinWidget(pixmap, geometry);
m_widgets.append(pinWidget);
connect(pinWidget, &QObject::destroyed, this, [=]() {
m_widgets.removeOne(pinWidget);
quitIfIdle();
});
pinWidget->show();
pinWidget->activateWindow();
}
void FlameshotDaemon::attachScreenshotToClipboard(const QPixmap& pixmap)
{
m_hostingClipboard = true;
QClipboard* clipboard = QApplication::clipboard();
clipboard->blockSignals(true);
// This variable is necessary because the signal doesn't get blocked on
// windows for some reason
m_clipboardSignalBlocked = true;
saveToClipboard(pixmap);
clipboard->blockSignals(false);
}
// D-BUS ADAPTER METHODS
void FlameshotDaemon::attachPin(const QByteArray& data)
{
QDataStream stream(data);
QPixmap pixmap;
QRect geometry;
stream >> pixmap;
stream >> geometry;
attachPin(pixmap, geometry);
}
void FlameshotDaemon::attachScreenshotToClipboard(const QByteArray& screenshot)
{
QDataStream stream(screenshot);
QPixmap p;
stream >> p;
attachScreenshotToClipboard(p);
}
void FlameshotDaemon::attachTextToClipboard(const QString& text,
const QString& notification)
{
// Must send notification before clipboard modification on linux
if (!notification.isEmpty()) {
AbstractLogger::info() << notification;
}
m_hostingClipboard = true;
QClipboard* clipboard = QApplication::clipboard();
clipboard->blockSignals(true);
// This variable is necessary because the signal doesn't get blocked on
// windows for some reason
m_clipboardSignalBlocked = true;
clipboard->setText(text);
clipboard->blockSignals(false);
}
void FlameshotDaemon::initTrayIcon()
{
#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX)
if (!ConfigHandler().disabledTrayIcon()) {
enableTrayIcon(true);
}
#elif defined(Q_OS_WIN)
enableTrayIcon(true);
GlobalShortcutFilter* nativeFilter = new GlobalShortcutFilter(this);
qApp->installNativeEventFilter(nativeFilter);
connect(nativeFilter, &GlobalShortcutFilter::printPressed, this, [this]() {
Flameshot::instance()->gui();
});
#endif
}
void FlameshotDaemon::enableTrayIcon(bool enable)
{
if (enable) {
if (m_trayIcon == nullptr) {
m_trayIcon = new TrayIcon();
} else {
m_trayIcon->show();
return;
}
} else if (m_trayIcon) {
m_trayIcon->hide();
}
}
#if !defined(DISABLE_UPDATE_CHECKER)
void FlameshotDaemon::handleReplyCheckUpdates(QNetworkReply* reply)
{
if (!ConfigHandler().checkForUpdates()) {
return;
}
if (reply->error() == QNetworkReply::NoError) {
QJsonDocument response = QJsonDocument::fromJson(reply->readAll());
QJsonObject json = response.object();
m_appLatestVersion = json["tag_name"].toString().replace("v", "");
QVersionNumber appLatestVersion =
QVersionNumber::fromString(m_appLatestVersion);
if (Flameshot::instance()->getVersion() < appLatestVersion) {
emit newVersionAvailable(appLatestVersion);
m_appLatestUrl = json["html_url"].toString();
QString newVersion =
tr("New version %1 is available").arg(m_appLatestVersion);
if (m_showCheckAppUpdateStatus) {
sendTrayNotification(newVersion, "Flameshot");
QDesktopServices::openUrl(QUrl(m_appLatestUrl));
}
} else if (m_showCheckAppUpdateStatus) {
sendTrayNotification(tr("You have the latest version"),
"Flameshot");
}
} else {
qWarning() << "Failed to get information about the latest version. "
<< reply->errorString();
if (m_showCheckAppUpdateStatus) {
if (FlameshotDaemon::instance()) {
FlameshotDaemon::instance()->sendTrayNotification(
tr("Failed to get information about the latest version."),
"Flameshot");
}
}
}
m_showCheckAppUpdateStatus = false;
}
#endif
QDBusMessage FlameshotDaemon::createMethodCall(const QString& method)
{
QDBusMessage m =
QDBusMessage::createMethodCall(QStringLiteral("org.flameshot.Flameshot"),
QStringLiteral("/"),
QLatin1String(""),
method);
return m;
}
void FlameshotDaemon::checkDBusConnection(const QDBusConnection& connection)
{
if (!connection.isConnected()) {
AbstractLogger::error() << tr("Unable to connect via DBus");
qApp->exit(1);
}
}
void FlameshotDaemon::call(const QDBusMessage& m)
{
QDBusConnection sessionBus = QDBusConnection::sessionBus();
checkDBusConnection(sessionBus);
sessionBus.call(m);
}
// STATIC ATTRIBUTES
FlameshotDaemon* FlameshotDaemon::m_instance = nullptr;