From 233c765b1fa82a2bfd92cfd7bfbb324464165efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haris=20Gu=C5=A1i=C4=87?= Date: Wed, 8 Dec 2021 22:18:39 +0100 Subject: [PATCH] Reduce dependence on D-Bus (#2003) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Handle captures without sigslots Signed-off-by: Haris Gušić * Set {app,organization}Name and version consistently Signed-off-by: Haris Gušić * Make 'full' dbus-free Signed-off-by: Haris Gušić * Move CaptureRequest::exportCapture to Controller We need to wait until the upload widget (or similar widgets) have finished before exiting. This must be done using a signal. The problem is that CaptureRequest can't be guaranteed to survive until the widget has finished what it's doing. Signed-off-by: Haris Gušić * Use QApplication with the 'full' subcommand Signed-off-by: Haris Gušić * Do unto 'screen' as we did to 'full' Signed-off-by: Haris Gušić * Add FlameshotDaemon singleton class Signed-off-by: Haris Gušić * Support clipboard hosting for both pixmaps and text * Fix upload handling Signed-off-by: Haris Gušić * Do not show tray icon if not daemon Signed-off-by: Haris Gušić * Clean up handling of pin task Signed-off-by: Haris Gušić * Remove annoying Qt warning messages The messages were caused by the color wheel. Signed-off-by: Haris Gušić * Fix small bug in Controller::exportCapture * Fix --raw output * Make 'gui' dbus-independent Signed-off-by: Haris Gušić * Fix accept on select bug Signed-off-by: Haris Gušić * Fix compile error on Windows Signed-off-by: Haris Gušić * Make it work on Windows * Remove obsolete function in main.cpp Signed-off-by: Haris Gušić * Make 'launcher' work without dbus Signed-off-by: Haris Gušić * clang-format, sigh Signed-off-by: Haris Gušić * Enable CLI parsing on MacOS Signed-off-by: Haris Gušić * Make 'config' work without dbus Signed-off-by: Haris Gušić * Small refactor of capture request handling Signed-off-by: Haris Gušić * Remove obsolete DBusUtils Signed-off-by: Haris Gušić * Remove unused D-Bus sigslots Signed-off-by: Haris Gušić * Remove D-Bus methods openConfig, autostartEnabled and trayIconEnabled Signed-off-by: Haris Gušić * Remove D-Bus method requestCapture Signed-off-by: Haris Gušić * Remove CaptureRequest id mechanism Signed-off-by: Haris Gušić * Fix 'launcher' crash Signed-off-by: Haris Gušić * Handle clipboard notifications properly Signed-off-by: Haris Gušić * Add 'autoCloseIdleDaemon' option Signed-off-by: Haris Gušić * Document FlameshotDaemon class Signed-off-by: Haris Gušić * Make 'flameshot gui' run in single-application mode Signed-off-by: Haris Gušić * Add `allowmultipleGuiInstances` config option Signed-off-by: Haris Gušić * Fix endless loop with multiple GUI instances Signed-off-by: Haris Gušić * Move upload confirmation dialog where it belongs Signed-off-by: Haris Gušić * Add the new config options to the GUI as well Signed-off-by: Haris Gušić * Fix failing build on Windows Signed-off-by: Haris Gušić * Handle persistence on MacOS Signed-off-by: Haris Gušić * fixed notifications on macos * Fixed display on macos * Reformat tests/action_options.sh Signed-off-by: Haris Gušić * Fix infinite recursion in tests/action_options.sh Signed-off-by: Haris Gušić Co-authored-by: Dearsh Oberoi <59907159+deo002@users.noreply.github.com> Co-authored-by: Jeremy Borgman --- data/dbus/org.flameshot.Flameshot.xml | 90 ++---- flameshot.example.ini | 6 + src/config/generalconf.cpp | 59 +++- src/config/generalconf.h | 7 +- src/core/CMakeLists.txt | 15 +- src/core/capturerequest.cpp | 113 +------- src/core/capturerequest.h | 6 - src/core/controller.cpp | 211 +++++++++----- src/core/controller.h | 27 +- src/core/flameshotdaemon.cpp | 262 +++++++++++++++++ src/core/flameshotdaemon.h | 46 +++ src/core/flameshotdbusadapter.cpp | 66 +---- src/core/flameshotdbusadapter.h | 17 +- src/main.cpp | 266 +++++++----------- src/tools/accept/accepttool.cpp | 4 +- src/tools/capturecontext.cpp | 10 - src/tools/capturecontext.h | 4 +- src/tools/copy/copytool.cpp | 2 +- src/tools/imgupload/imguploadertool.cpp | 2 +- .../imgupload/storages/imguploaderbase.cpp | 8 +- src/tools/pin/pintool.cpp | 4 +- src/tools/save/savetool.cpp | 2 +- src/utils/CMakeLists.txt | 4 +- src/utils/confighandler.cpp | 4 + src/utils/confighandler.h | 13 +- src/utils/dbusutils.cpp | 94 ------- src/utils/dbusutils.h | 27 -- src/utils/screenshotsaver.cpp | 24 +- src/utils/screenshotsaver.h | 2 - src/utils/systemnotification.cpp | 4 +- src/widgets/capture/capturewidget.cpp | 41 +-- src/widgets/capture/capturewidget.h | 7 +- src/widgets/capturelauncher.cpp | 19 +- src/widgets/capturelauncher.h | 5 +- src/widgets/historywidget.cpp | 5 +- src/widgets/historywidget.h | 4 +- src/widgets/infowindow.cpp | 6 +- tests/action_options.sh | 48 +++- 38 files changed, 768 insertions(+), 766 deletions(-) create mode 100644 src/core/flameshotdaemon.cpp create mode 100644 src/core/flameshotdaemon.h delete mode 100644 src/utils/dbusutils.cpp delete mode 100644 src/utils/dbusutils.h diff --git a/data/dbus/org.flameshot.Flameshot.xml b/data/dbus/org.flameshot.Flameshot.xml index c2e94db8..2463d8f5 100644 --- a/data/dbus/org.flameshot.Flameshot.xml +++ b/data/dbus/org.flameshot.Flameshot.xml @@ -3,90 +3,38 @@ - - + + - - + + - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/flameshot.example.ini b/flameshot.example.ini index 97f330cc..65f1200f 100644 --- a/flameshot.example.ini +++ b/flameshot.example.ini @@ -48,6 +48,12 @@ ;; Whether the tray icon is disabled (bool) ;disabledTrayIcon=false ; +;; Automatically close daemon when it's not needed (not available on Windows) +;autoCloseIdleDaemon=false +; +;; Allow multiple instances of `flameshot gui` to run at the same time +;allowMultipleGuiInstances=false +; ;; Last used tool thickness (int) ;drawThickness=1 ; diff --git a/src/config/generalconf.cpp b/src/config/generalconf.cpp index 60e486ad..15cbf306 100644 --- a/src/config/generalconf.cpp +++ b/src/config/generalconf.cpp @@ -49,6 +49,10 @@ GeneralConf::GeneralConf(QWidget* parent) initSaveAfterCopy(); inituploadHistoryMax(); initUndoLimit(); + initAllowMultipleGuiInstances(); +#if !defined(Q_OS_WIN) + initAutoCloseIdleDaemon(); +#endif m_layout->addStretch(); @@ -73,6 +77,8 @@ void GeneralConf::_updateComponents(bool allowEmptySavePath) m_historyConfirmationToDelete->setChecked( config.historyConfirmationToDelete()); m_checkForUpdates->setChecked(config.checkForUpdates()); + m_allowMultipleGuiInstances->setChecked(config.allowMultipleGuiInstances()); + m_autoCloseIdleDaemon->setChecked(config.autoCloseIdleDaemon()); m_showStartupLaunchMessage->setChecked(config.showStartupLaunchMessage()); m_screenshotPathFixedCheck->setChecked(config.savePathFixed()); m_uploadHistoryMax->setValue(config.uploadHistoryMax()); @@ -106,22 +112,22 @@ void GeneralConf::showDesktopNotificationChanged(bool checked) ConfigHandler().setShowDesktopNotification(checked); } -void GeneralConf::showTrayIconChanged(bool checked) -{ - auto controller = Controller::getInstance(); - if (checked) { - controller->enableTrayIcon(); - } else { - controller->disableTrayIcon(); - } -} - void GeneralConf::checkForUpdatesChanged(bool checked) { ConfigHandler().setCheckForUpdates(checked); Controller::getInstance()->setCheckForUpdatesEnabled(checked); } +void GeneralConf::allowMultipleGuiInstancesChanged(bool checked) +{ + ConfigHandler().setAllowMultipleGuiInstances(checked); +} + +void GeneralConf::autoCloseIdleDaemonChanged(bool checked) +{ + ConfigHandler().setAutoCloseIdleDaemon(checked); +} + void GeneralConf::autostartChanged(bool checked) { ConfigHandler().setStartupLaunch(checked); @@ -250,10 +256,9 @@ void GeneralConf::initShowTrayIcon() m_showTray->setToolTip(tr("Show the systemtray icon")); m_scrollAreaLayout->addWidget(m_showTray); - connect(m_showTray, - &QCheckBox::stateChanged, - this, - &GeneralConf::showTrayIconChanged); + connect(m_showTray, &QCheckBox::clicked, this, [](bool checked) { + ConfigHandler().setDisabledTrayIcon(!checked); + }); #endif } @@ -314,6 +319,32 @@ void GeneralConf::initCheckForUpdates() &GeneralConf::checkForUpdatesChanged); } +void GeneralConf::initAllowMultipleGuiInstances() +{ + m_allowMultipleGuiInstances = new QCheckBox( + tr("Allow multiple flameshot GUI instances simultaneously"), this); + m_allowMultipleGuiInstances->setToolTip(tr( + "This allows you to take screenshots of flameshot itself for example.")); + m_scrollAreaLayout->addWidget(m_allowMultipleGuiInstances); + connect(m_allowMultipleGuiInstances, + &QCheckBox::clicked, + this, + &GeneralConf::allowMultipleGuiInstancesChanged); +} + +void GeneralConf::initAutoCloseIdleDaemon() +{ + m_autoCloseIdleDaemon = new QCheckBox( + tr("Automatically close daemon when it is not needed"), this); + m_autoCloseIdleDaemon->setToolTip( + tr("Automatically close daemon when it is not needed")); + m_scrollAreaLayout->addWidget(m_autoCloseIdleDaemon); + connect(m_autoCloseIdleDaemon, + &QCheckBox::clicked, + this, + &GeneralConf::autoCloseIdleDaemonChanged); +} + void GeneralConf::initAutostart() { m_autostart = new QCheckBox(tr("Launch at startup"), this); diff --git a/src/config/generalconf.h b/src/config/generalconf.h index 90abdb6c..05262744 100644 --- a/src/config/generalconf.h +++ b/src/config/generalconf.h @@ -27,8 +27,9 @@ private slots: void showHelpChanged(bool checked); void showSidePanelButtonChanged(bool checked); void showDesktopNotificationChanged(bool checked); - void showTrayIconChanged(bool checked); void checkForUpdatesChanged(bool checked); + void allowMultipleGuiInstancesChanged(bool checked); + void autoCloseIdleDaemonChanged(bool checked); void autostartChanged(bool checked); void historyConfirmationToDelete(bool checked); void uploadHistoryMaxChanged(int max); @@ -55,6 +56,8 @@ private: void initUndoLimit(); void initConfigButtons(); void initCheckForUpdates(); + void initAllowMultipleGuiInstances(); + void initAutoCloseIdleDaemon(); void initAutostart(); void initShowStartupLaunchMessage(); void initCopyAndCloseAfterUpload(); @@ -75,6 +78,8 @@ private: QCheckBox* m_helpMessage; QCheckBox* m_sidePanelButton; QCheckBox* m_checkForUpdates; + QCheckBox* m_allowMultipleGuiInstances; + QCheckBox* m_autoCloseIdleDaemon; QCheckBox* m_autostart; QCheckBox* m_showStartupLaunchMessage; QCheckBox* m_copyAndCloseAfterUpload; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8518184b..482d71ca 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,6 +1,17 @@ -target_sources(flameshot PRIVATE controller.h flameshotdbusadapter.h qguiappcurrentscreen.h) +target_sources(flameshot PRIVATE + controller.h + flameshotdaemon.h + flameshotdbusadapter.h + qguiappcurrentscreen.h +) -target_sources(flameshot PRIVATE capturerequest.cpp controller.cpp flameshotdbusadapter.cpp qguiappcurrentscreen.cpp) +target_sources(flameshot PRIVATE + capturerequest.cpp + controller.cpp + flameshotdaemon.cpp + flameshotdbusadapter.cpp + qguiappcurrentscreen.cpp +) IF (WIN32) target_sources(flameshot PRIVATE globalshortcutfilter.h globalshortcutfilter.cpp) diff --git a/src/core/capturerequest.cpp b/src/core/capturerequest.cpp index 7a7250f6..07ba5308 100644 --- a/src/core/capturerequest.cpp +++ b/src/core/capturerequest.cpp @@ -23,63 +23,8 @@ CaptureRequest::CaptureRequest(CaptureRequest::CaptureMode mode, , m_delay(delay) , m_tasks(tasks) , m_data(data) - , m_forcedID(false) - , m_id(0) {} -void CaptureRequest::setStaticID(uint id) -{ - m_forcedID = true; - m_id = id; -} - -uint CaptureRequest::id() const -{ - if (m_forcedID) { - return m_id; - } - - uint id = 0; - QVector v; - v << qHash(m_mode) << qHash(m_delay * QDateTime::currentMSecsSinceEpoch()) - << qHash(m_path) << qHash(m_tasks) << m_data.toInt(); - for (uint i : v) { - id ^= i + 0x9e3779b9 + (id << 6) + (id >> 2); - } - return id; -} - -QByteArray CaptureRequest::serialize() const -{ - QByteArray data; - QDataStream stream(&data, QIODevice::WriteOnly); - // Convert enums to integers - qint32 tasks = m_tasks, mode = m_mode; - stream << mode << m_delay << tasks << m_data << m_forcedID << m_id << m_path - << m_initialSelection; - return data; -} - -CaptureRequest CaptureRequest::deserialize(const QByteArray& data) -{ - QDataStream stream(data); - CaptureRequest request; - qint32 tasks, mode; - stream >> mode; - stream >> request.m_delay; - stream >> tasks; - stream >> request.m_data; - stream >> request.m_forcedID; - stream >> request.m_id; - stream >> request.m_path; - stream >> request.m_initialSelection; - - // Convert integers to enums - request.m_tasks = static_cast(tasks); - request.m_mode = static_cast(mode); - return request; -} - CaptureRequest::CaptureMode CaptureRequest::captureMode() const { return m_mode; @@ -118,7 +63,7 @@ void CaptureRequest::addTask(CaptureRequest::ExportTask task) m_tasks |= task; } -void CaptureRequest::removeTask(CaptureRequest::ExportTask task) +void CaptureRequest::removeTask(ExportTask task) { ((int&)m_tasks) &= ~task; } @@ -139,59 +84,3 @@ void CaptureRequest::setInitialSelection(const QRect& selection) { m_initialSelection = selection; } - -void CaptureRequest::exportCapture(const QPixmap& capture) -{ - if (m_tasks & SAVE) { - if (m_path.isEmpty()) { - ScreenshotSaver(m_id).saveToFilesystemGUI(capture); - } else { - ScreenshotSaver(m_id).saveToFilesystem(capture, m_path); - } - } - - if (m_tasks & COPY) { - ScreenshotSaver().saveToClipboard(capture); - } - - if (m_tasks & PIN) { - QWidget* widget = new PinWidget(capture, m_pinWindowGeometry); - widget->show(); - widget->activateWindow(); - if (m_mode == SCREEN_MODE || m_mode == FULLSCREEN_MODE) { - SystemNotification().sendMessage( - QObject::tr("Full screen screenshot pinned to screen")); - } - } - - if (m_tasks & UPLOAD) { - if (!ConfigHandler().uploadWithoutConfirmation()) { - ImgUploadDialog* dialog = new ImgUploadDialog(); - if (dialog->exec() == QDialog::Rejected) { - return; - } - } - - ImgUploaderBase* widget = ImgUploaderManager().uploader(capture); - widget->show(); - widget->activateWindow(); - // NOTE: lambda can't capture 'this' because it might be destroyed later - ExportTask tasks = m_tasks; - QObject::connect( - widget, &ImgUploaderBase::uploadOk, [widget, tasks](const QUrl& url) { - if (ConfigHandler().copyAndCloseAfterUpload()) { - if (!(tasks & COPY)) { - SystemNotification().sendMessage( - QObject::tr("URL copied to clipboard.")); - - QApplication::clipboard()->setText(url.toString()); - widget->close(); - } else { - widget->showPostUploadDialog(); - } - } else { - widget->showPostUploadDialog(); - } - }); - } -} diff --git a/src/core/capturerequest.h b/src/core/capturerequest.h index eda997b6..0cb79716 100644 --- a/src/core/capturerequest.h +++ b/src/core/capturerequest.h @@ -37,8 +37,6 @@ public: void setStaticID(uint id); uint id() const; - QByteArray serialize() const; - static CaptureRequest deserialize(const QByteArray& data); uint delay() const; QString path() const; QVariant data() const; @@ -51,7 +49,6 @@ public: void addSaveTask(const QString& path = QString()); void addPinTask(const QRect& pinWindowGeometry); void setInitialSelection(const QRect& selection); - void exportCapture(const QPixmap& capture); private: CaptureMode m_mode; @@ -61,9 +58,6 @@ private: QVariant m_data; QRect m_pinWindowGeometry, m_initialSelection; - bool m_forcedID; - uint m_id; - CaptureRequest() {} }; diff --git a/src/core/controller.cpp b/src/core/controller.cpp index 6ffb5d7f..487d8973 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -2,13 +2,18 @@ // SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors #include "controller.h" +#include "flameshotdaemon.h" #if defined(Q_OS_MACOS) #include "external/QHotkey/QHotkey" #endif +#include "pinwidget.h" +#include "screenshotsaver.h" #include "src/config/configwindow.h" #include "src/core/qguiappcurrentscreen.h" +#include "src/tools/imgupload/imguploadermanager.h" +#include "src/tools/imgupload/storages/imguploaderbase.h" #include "src/utils/confighandler.h" #include "src/utils/globalvalues.h" #include "src/utils/history.h" @@ -18,14 +23,17 @@ #include "src/widgets/capture/capturewidget.h" #include "src/widgets/capturelauncher.h" #include "src/widgets/historywidget.h" +#include "src/widgets/imguploaddialog.h" #include "src/widgets/infowindow.h" #include "src/widgets/notificationwidget.h" #include #include +#include #include #include #include #include +#include #include #include #include @@ -64,21 +72,6 @@ Controller::Controller() m_appLatestVersion = QStringLiteral(APP_VERSION).replace("v", ""); qApp->setQuitOnLastWindowClosed(false); - // init tray icon -#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX) - if (!ConfigHandler().disabledTrayIcon()) { - enableTrayIcon(); - } -#elif defined(Q_OS_WIN) - enableTrayIcon(); - - GlobalShortcutFilter* nativeFilter = new GlobalShortcutFilter(this); - qApp->installNativeEventFilter(nativeFilter); - connect(nativeFilter, &GlobalShortcutFilter::printPressed, this, [this]() { - this->requestCapture(CaptureRequest(CaptureRequest::GRAPHICAL_MODE)); - }); -#endif - QString StyleSheet = CaptureButton::globalStyleSheet(); qApp->setStyleSheet(StyleSheet); @@ -103,6 +96,18 @@ Controller::Controller() qApp, [&]() { this->showRecentUploads(); }); #endif + connect(ConfigHandler::getInstance(), + &ConfigHandler::fileChanged, + this, + [this]() { + ConfigHandler config; + if (config.disabledTrayIcon()) { + disableTrayIcon(); + } else { + enableTrayIcon(); + } + }); + if (ConfigHandler().checkForUpdates()) { getLatestAvailableVersion(); } @@ -119,14 +124,6 @@ Controller* Controller::getInstance() return &c; } -void Controller::enableExports() -{ - connect( - this, &Controller::captureTaken, this, &Controller::handleCaptureTaken); - connect( - this, &Controller::captureFailed, this, &Controller::handleCaptureFailed); -} - void Controller::setCheckForUpdatesEnabled(const bool enabled) { if (m_appUpdates != nullptr) { @@ -217,38 +214,33 @@ void Controller::appUpdates() void Controller::requestCapture(const CaptureRequest& request) { - uint id = request.id(); - m_requestMap.insert(id, request); - switch (request.captureMode()) { case CaptureRequest::FULLSCREEN_MODE: - doLater(request.delay(), this, [this, id]() { - this->startFullscreenCapture(id); + doLater(request.delay(), this, [this, request]() { + startFullscreenCapture(request); }); break; case CaptureRequest::SCREEN_MODE: { int&& number = request.data().toInt(); - doLater(request.delay(), this, [this, id, number]() { - this->startScreenGrab(id, number); + doLater(request.delay(), this, [this, request, number]() { + startScreenGrab(request, number); }); break; } case CaptureRequest::GRAPHICAL_MODE: { - QString&& path = request.path(); - doLater(request.delay(), this, [this, id, path]() { - this->startVisualCapture(id, path); + doLater(request.delay(), this, [this, request]() { + startVisualCapture(request); }); break; } default: - emit captureFailed(id); + handleCaptureFailed(); break; } } // creation of a new capture in GUI mode -void Controller::startVisualCapture(const uint id, - const QString& forcedSavePath) +void Controller::startVisualCapture(const CaptureRequest& req) { #if defined(Q_OS_MACOS) // This is required on MacOS because of Mission Control. If you'll switch to @@ -263,6 +255,7 @@ void Controller::startVisualCapture(const uint id, #endif if (nullptr == m_captureWindow) { + // TODO is this unnecessary now? int timeout = 5000; // 5 seconds const int delay = 100; QWidget* modalWidget = nullptr; @@ -281,17 +274,9 @@ void Controller::startVisualCapture(const uint id, return; } - m_captureWindow = new CaptureWidget(id, forcedSavePath); - // m_captureWindow = new CaptureWidget(id, forcedSavePath, false); // + m_captureWindow = new CaptureWidget(req); + // m_captureWindow = new CaptureWidget(forcedSavePath, false); // // debug - connect(m_captureWindow, - &CaptureWidget::captureFailed, - this, - &Controller::captureFailed); - connect(m_captureWindow, - &CaptureWidget::captureTaken, - this, - &Controller::captureTaken); #ifdef Q_OS_WIN m_captureWindow->show(); @@ -311,11 +296,11 @@ void Controller::startVisualCapture(const uint id, m_appLatestUrl); } } else { - emit captureFailed(id); + emit captureFailed(); } } -void Controller::startScreenGrab(const uint id, const int screenNumber) +void Controller::startScreenGrab(CaptureRequest req, const int screenNumber) { bool ok = true; QScreen* screen; @@ -333,7 +318,7 @@ void Controller::startScreenGrab(const uint id, const int screenNumber) } QPixmap p(ScreenGrabber().grabScreen(screen, ok)); if (ok) { - CaptureRequest& req = *requests().find(id); + QRect geometry = ScreenGrabber().screenGeometry(screen); QRect region = req.initialSelection(); if (region.isNull()) { region = ScreenGrabber().screenGeometry(screen); @@ -347,9 +332,9 @@ void Controller::startScreenGrab(const uint id, const int screenNumber) // change geometry for pin task req.addPinTask(region); } - emit captureTaken(id, p, region); + exportCapture(p, geometry, req); } else { - emit captureFailed(id); + handleCaptureFailed(); } } @@ -390,6 +375,23 @@ void Controller::openLauncherWindow() #endif } +void Controller::initTrayIcon() +{ +#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX) + if (!ConfigHandler().disabledTrayIcon()) { + enableTrayIcon(); + } +#elif defined(Q_OS_WIN) + enableTrayIcon(); + + GlobalShortcutFilter* nativeFilter = new GlobalShortcutFilter(this); + qApp->installNativeEventFilter(nativeFilter); + connect(nativeFilter, &GlobalShortcutFilter::printPressed, this, [this]() { + this->requestCapture(CaptureRequest(CaptureRequest::GRAPHICAL_MODE)); + }); +#endif +} + void Controller::enableTrayIcon() { ConfigHandler().setDisabledTrayIcon(false); @@ -415,7 +417,7 @@ void Controller::enableTrayIcon() } #else // Wait 400 ms to hide the QMenu - doLater(400, this, [this]() { this->startVisualCapture(); }); + doLater(400, this, [this]() { startVisualCapture(); }); #endif }); QAction* launcherAction = new QAction(tr("&Open Launcher"), this); @@ -560,40 +562,115 @@ void Controller::showRecentUploads() #endif } -void Controller::sendCaptureSaved(uint id, const QString& savePath) +void Controller::exportCapture(QPixmap capture, + QRect& selection, + const CaptureRequest& req) { - emit captureSaved(id, savePath); + using CR = CaptureRequest; + int tasks = req.tasks(), mode = req.captureMode(); + QString path = req.path(); + + if (tasks & CR::PRINT_GEOMETRY) { + QByteArray byteArray; + QBuffer buffer(&byteArray); + QTextStream(stdout) + << selection.width() << "x" << selection.height() << "+" + << selection.x() << "+" << selection.y() << "\n"; + } + + if (tasks & CR::PRINT_RAW) { + QByteArray byteArray; + QBuffer buffer(&byteArray); + capture.save(&buffer, "PNG"); + QFile file; + file.open(stdout, QIODevice::WriteOnly); + + file.write(byteArray); + file.close(); + } + + if (tasks & CR::SAVE) { + if (req.path().isEmpty()) { + ScreenshotSaver().saveToFilesystemGUI(capture); + } else { + ScreenshotSaver().saveToFilesystem(capture, path); + } + } + + if (tasks & CR::COPY) { + FlameshotDaemon::copyToClipboard(capture); + } + + if (tasks & CR::PIN) { + FlameshotDaemon::createPin(capture, selection); + if (mode == CR::SCREEN_MODE || mode == CR::FULLSCREEN_MODE) { + SystemNotification().sendMessage( + QObject::tr("Full screen screenshot pinned to screen")); + } + } + + if (tasks & CR::UPLOAD) { + if (!ConfigHandler().uploadWithoutConfirmation()) { + ImgUploadDialog* dialog = new ImgUploadDialog(); + if (dialog->exec() == QDialog::Rejected) { + return; + } + } + + ImgUploaderBase* widget = ImgUploaderManager().uploader(capture); + widget->show(); + widget->activateWindow(); + // NOTE: lambda can't capture 'this' because it might be destroyed later + CR::ExportTask tasks = tasks; + QObject::connect( + widget, &ImgUploaderBase::uploadOk, [=](const QUrl& url) { + if (ConfigHandler().copyAndCloseAfterUpload()) { + if (!(tasks & CR::COPY)) { + SystemNotification().sendMessage( + QObject::tr("URL copied to clipboard.")); + + QApplication::clipboard()->setText(url.toString()); + widget->close(); + } else { + widget->showPostUploadDialog(); + } + } else { + widget->showPostUploadDialog(); + } + }); + } + + if (!(tasks & CR::UPLOAD)) { + emit captureTaken(capture, selection); + } } -void Controller::startFullscreenCapture(const uint id) +void Controller::startFullscreenCapture(const CaptureRequest& req) { bool ok = true; QPixmap p(ScreenGrabber().grabEntireDesktop(ok)); - CaptureRequest req(*requests().find(id)); QRect region = req.initialSelection(); if (!region.isNull()) { p = p.copy(region); } if (ok) { - // selection parameter is unused here - emit captureTaken(id, p, {}); + QRect selection; // `flameshot full` does not support --selection + exportCapture(p, selection, req); } else { - emit captureFailed(id); + handleCaptureFailed(); } } -void Controller::handleCaptureTaken(uint id, QPixmap p) +void Controller::handleCaptureTaken(const CaptureRequest& req, + QPixmap p, + QRect selection) { - auto it = m_requestMap.find(id); - if (it != m_requestMap.end()) { - it.value().exportCapture(p); - m_requestMap.erase(it); - } + exportCapture(p, selection, req); } -void Controller::handleCaptureFailed(uint id) +void Controller::handleCaptureFailed() { - m_requestMap.remove(id); + emit captureFailed(); } void Controller::doLater(int msec, QObject* receiver, lambda func) diff --git a/src/core/controller.h b/src/core/controller.h index 9263aaa4..08ac0c1c 100644 --- a/src/core/controller.h +++ b/src/core/controller.h @@ -37,16 +37,14 @@ public: ~Controller(); void operator=(const Controller&) = delete; - void enableExports(); - void setCheckForUpdatesEnabled(const bool enabled); QMap& requests(); signals: - void captureTaken(uint id, QPixmap p, const QRect& selection); - void captureFailed(uint id); - void captureSaved(uint id, QString savePath); + // TODO remove all parameters from captureTaken and update dependencies + void captureTaken(QPixmap p, const QRect& selection); + void captureFailed(); public slots: void requestCapture(const CaptureRequest& request); @@ -55,6 +53,8 @@ public slots: void openInfoWindow(); void appUpdates(); void openLauncherWindow(); + // TODO move tray icon handling to FlameshotDaemon + void initTrayIcon(); void enableTrayIcon(); void disableTrayIcon(); void sendTrayNotification( @@ -66,16 +66,19 @@ public slots: void showRecentUploads(); - void sendCaptureSaved(uint id, const QString& savePath); + void exportCapture(QPixmap p, QRect& selection, const CaptureRequest& req); private slots: - void startFullscreenCapture(const uint id = 0); - void startVisualCapture(const uint id = 0, - const QString& forcedSavePath = QString()); - void startScreenGrab(const uint id = 0, const int screenNumber = -1); + void startFullscreenCapture(const CaptureRequest& req); + void startVisualCapture( + const CaptureRequest& req = CaptureRequest::GRAPHICAL_MODE); + void startScreenGrab(CaptureRequest req, const int screenNumber = -1); - void handleCaptureTaken(uint id, QPixmap p); - void handleCaptureFailed(uint id); +public slots: // TODO move these up + void handleCaptureTaken(const CaptureRequest& req, + QPixmap p, + QRect selection); + void handleCaptureFailed(); void handleReplyCheckUpdates(QNetworkReply* reply); diff --git a/src/core/flameshotdaemon.cpp b/src/core/flameshotdaemon.cpp new file mode 100644 index 00000000..ba66210c --- /dev/null +++ b/src/core/flameshotdaemon.cpp @@ -0,0 +1,262 @@ +#include "flameshotdaemon.h" + +#include "confighandler.h" +#include "controller.h" +#include "pinwidget.h" +#include "screenshotsaver.h" +#include "systemnotification.h" +#include +#include +#include +#include +#include +#include + +/** + * @brief A way of accessing the flameshot daemon both from the daemon itself, + * and from subcommands. + * + * The daemon is necessary in order to: + * - Host the system tray, + * - Listen for hotkey events that will trigger captures, + * - Host pinned screenshot widgets, + * - Host the clipboard on X11, where the clipboard gets lost once flameshot + * quits. + * + * If the `autoCloseIdleDaemon` option is true, the daemon will close as soon as + * it is not needed to host pinned screenshots and the clipboard. On Windows, + * this option is disabled and the daemon always persists, because the system + * tray is currently the only way to interact with flameshot. + * + * Both the daemon and non-daemon flameshot processes use the same public API, + * which is implemented as static methods. In the daemon process, this class is + * also instantiated as a singleton, so it can listen to D-Bus calls via the + * sigslot mechanism. The instantiation is done by calling `start` (this must be + * done only in the daemon process). + * + * @note The daemon will be automatically launched where necessary, via D-Bus. + * This applies only on Linux. + */ +FlameshotDaemon::FlameshotDaemon() + : m_persist(false) + , m_hostingClipboard(false) + , m_clipboardSignalBlocked(false) +{ + connect( + QApplication::clipboard(), &QClipboard::dataChanged, this, [this]() { + if (!m_hostingClipboard || m_clipboardSignalBlocked) { + m_clipboardSignalBlocked = false; + return; + } + m_hostingClipboard = false; + quitIfIdle(); + }); + // init tray icon + Controller::getInstance()->initTrayIcon(); +#ifdef Q_OS_WIN + m_persist = true; +#else + m_persist = !ConfigHandler().autoCloseIdleDaemon(); + connect(ConfigHandler::getInstance(), + &ConfigHandler::fileChanged, + this, + [this]() { m_persist = !ConfigHandler().autoCloseIdleDaemon(); }); +#endif +} + +void FlameshotDaemon::start() +{ + if (!m_instance) { + m_instance = new FlameshotDaemon(); + } +} + +void FlameshotDaemon::createPin(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(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(QString text, 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); +} + +void FlameshotDaemon::enableTrayIcon(bool enable) +{ +#if !defined(Q_OS_WIN) + if (!instance()) { + return; + } + if (enable) { + Controller::getInstance()->enableTrayIcon(); + } else { + Controller::getInstance()->disableTrayIcon(); + } +#endif +} + +/** + * @brief Is this instance of flameshot hosting any windows as a daemon? + */ +bool FlameshotDaemon::isThisInstanceHostingWidgets() +{ + return instance() && !instance()->m_widgets.isEmpty(); +} + +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; +} + +/** + * @brief Quit the daemon if it has nothing to do and the 'persist' flag is not + * set. + */ +void FlameshotDaemon::quitIfIdle() +{ + if (m_persist && !instance()) { + return; + } + if (!m_hostingClipboard && m_widgets.isEmpty()) { + qApp->exit(0); + } +} + +// SERVICE METHODS + +void FlameshotDaemon::attachPin(QPixmap pixmap, QRect geometry) +{ + PinWidget* 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(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; + ScreenshotSaver().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(QString text, QString 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); + if (!notification.isEmpty()) { + SystemNotification().sendMessage(notification); + } + clipboard->blockSignals(false); +} + +QDBusMessage FlameshotDaemon::createMethodCall(QString method) +{ + QDBusMessage m = + QDBusMessage::createMethodCall(QStringLiteral("org.flameshot.Flameshot"), + QStringLiteral("/"), + QLatin1String(""), + method); + return m; +} + +void FlameshotDaemon::checkDBusConnection(const QDBusConnection& connection) +{ + if (!connection.isConnected()) { + SystemNotification().sendMessage(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; diff --git a/src/core/flameshotdaemon.h b/src/core/flameshotdaemon.h new file mode 100644 index 00000000..25206066 --- /dev/null +++ b/src/core/flameshotdaemon.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +class QPixmap; +class QRect; +class QDBusMessage; +class QDBusConnection; + +class FlameshotDaemon : private QObject +{ + Q_OBJECT +public: + static void start(); + static FlameshotDaemon* instance(); + static void createPin(QPixmap capture, QRect geometry); + static void copyToClipboard(QPixmap capture); + static void copyToClipboard(QString text, QString notification = ""); + static void enableTrayIcon(bool enable); + static bool isThisInstanceHostingWidgets(); + +private: + FlameshotDaemon(); + void quitIfIdle(); + void attachPin(QPixmap pixmap, QRect geometry); + void attachScreenshotToClipboard(QPixmap pixmap); + + void attachPin(const QByteArray& data); + void attachScreenshotToClipboard(const QByteArray& screenshot); + void attachTextToClipboard(QString text, QString notification); + +private: + static QDBusMessage createMethodCall(QString method); + static void checkDBusConnection(const QDBusConnection& connection); + static void call(const QDBusMessage& m); + + bool m_persist; + bool m_hostingClipboard; + bool m_clipboardSignalBlocked; + QList m_widgets; + static FlameshotDaemon* m_instance; + + friend class FlameshotDBusAdapter; +}; diff --git a/src/core/flameshotdbusadapter.cpp b/src/core/flameshotdbusadapter.cpp index 9b66d13e..e6e5ee20 100644 --- a/src/core/flameshotdbusadapter.cpp +++ b/src/core/flameshotdbusadapter.cpp @@ -2,72 +2,26 @@ // SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors #include "flameshotdbusadapter.h" -#include "src/core/controller.h" -#include "src/utils/confighandler.h" -#include "src/utils/screengrabber.h" -#include "src/utils/screenshotsaver.h" -#include "src/utils/systemnotification.h" -#include +#include "src/core/flameshotdaemon.h" + FlameshotDBusAdapter::FlameshotDBusAdapter(QObject* parent) : QDBusAbstractAdaptor(parent) -{ - auto controller = Controller::getInstance(); - connect(controller, - &Controller::captureFailed, - this, - &FlameshotDBusAdapter::captureFailed); - connect(controller, - &Controller::captureTaken, - this, - &FlameshotDBusAdapter::handleCaptureTaken); - connect(controller, - &Controller::captureSaved, - this, - &FlameshotDBusAdapter::captureSaved); -} +{} FlameshotDBusAdapter::~FlameshotDBusAdapter() {} -void FlameshotDBusAdapter::requestCapture(const QByteArray& requestData) +void FlameshotDBusAdapter::attachScreenshotToClipboard(const QByteArray& data) { - CaptureRequest req = CaptureRequest::deserialize(requestData); - Controller::getInstance()->requestCapture(req); + FlameshotDaemon::instance()->attachScreenshotToClipboard(data); } -void FlameshotDBusAdapter::openLauncher() +void FlameshotDBusAdapter::attachTextToClipboard(QString text, + QString notification) { - Controller::getInstance()->openLauncherWindow(); + FlameshotDaemon::instance()->attachTextToClipboard(text, notification); } -void FlameshotDBusAdapter::openConfig() +void FlameshotDBusAdapter::attachPin(const QByteArray& data) { - Controller::getInstance()->openConfigWindow(); -} - -void FlameshotDBusAdapter::trayIconEnabled(bool enabled) -{ - auto controller = Controller::getInstance(); - if (enabled) { - controller->enableTrayIcon(); - } else { - controller->disableTrayIcon(); - } -} - -void FlameshotDBusAdapter::autostartEnabled(bool enabled) -{ - ConfigHandler().setStartupLaunch(enabled); - auto controller = Controller::getInstance(); - // Autostart is not saved in a .ini file, requires manual update - controller->updateConfigComponents(); -} - -void FlameshotDBusAdapter::handleCaptureTaken(uint id, - const QPixmap& p, - const QRect& selection) -{ - QByteArray byteArray; - QBuffer buffer(&byteArray); - p.save(&buffer, "PNG"); - emit captureTaken(id, byteArray, selection); + FlameshotDaemon::instance()->attachPin(data); } diff --git a/src/core/flameshotdbusadapter.h b/src/core/flameshotdbusadapter.h index 655739a9..d9e96632 100644 --- a/src/core/flameshotdbusadapter.h +++ b/src/core/flameshotdbusadapter.h @@ -3,7 +3,6 @@ #pragma once -#include "src/core/controller.h" #include class FlameshotDBusAdapter : public QDBusAbstractAdaptor @@ -15,18 +14,8 @@ public: explicit FlameshotDBusAdapter(QObject* parent = nullptr); virtual ~FlameshotDBusAdapter(); -signals: - void captureTaken(uint id, QByteArray rawImage, QRect selection); - void captureFailed(uint id); - void captureSaved(uint id, QString savePath); - public slots: - Q_NOREPLY void requestCapture(const QByteArray& requestData); - Q_NOREPLY void openLauncher(); - Q_NOREPLY void openConfig(); - Q_NOREPLY void trayIconEnabled(bool enabled); - Q_NOREPLY void autostartEnabled(bool enabled); - -private slots: - void handleCaptureTaken(uint id, const QPixmap& p, const QRect& selection); + Q_NOREPLY void attachScreenshotToClipboard(const QByteArray& data); + Q_NOREPLY void attachTextToClipboard(QString text, QString notification); + Q_NOREPLY void attachPin(const QByteArray& data); }; diff --git a/src/main.cpp b/src/main.cpp index 8b78c6f0..ec4fd0f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include "src/config/styleoverride.h" #include "src/core/capturerequest.h" #include "src/core/controller.h" +#include "src/core/flameshotdaemon.h" #include "src/utils/confighandler.h" #include "src/utils/filenamehandler.h" #include "src/utils/pathinfo.h" @@ -19,7 +20,7 @@ #include #include #include -#include +#include #include #include @@ -28,22 +29,12 @@ #if defined(Q_OS_LINUX) || defined(Q_OS_UNIX) #include "src/core/flameshotdbusadapter.h" -#include "src/utils/dbusutils.h" +#include #include #include #include #endif -int waitAfterConnecting(int delay, QCoreApplication& app) -{ - QTimer t; - t.setInterval(delay + 1000 * 60 * 15); // 15 minutes timeout - QObject::connect(&t, &QTimer::timeout, qApp, &QCoreApplication::quit); - t.start(); - // wait - return app.exec(); -} - #ifdef Q_OS_LINUX // source: https://github.com/ksnip/ksnip/issues/416 void wayland_hacks() @@ -56,6 +47,42 @@ void wayland_hacks() } #endif +void requestCaptureAndWait(const CaptureRequest& req) +{ + Controller* controller = Controller::getInstance(); + controller->requestCapture(req); + QObject::connect( + controller, &Controller::captureTaken, [&](QPixmap, QRect) { + // Only useful on MacOS because each instance hosts its own widgets + if (!FlameshotDaemon::isThisInstanceHostingWidgets()) { + qApp->exit(0); + } + }); + QObject::connect(controller, &Controller::captureFailed, []() { + // TODO use abstract logger + // TODO do we have to do more stuff here? + QTextStream(stderr) << "screenshot aborted\n"; + qApp->exit(1); + }); + qApp->exec(); +} + +QSharedMemory* guiMutexLock() +{ + QString key = "org.flameshot.Flameshot-" APP_VERSION; + auto* shm = new QSharedMemory(key); +#ifdef Q_OS_UNIX + // Destroy shared memory if the last instance crashed on Unix + shm->attach(); + delete shm; + shm = new QSharedMemory(key); +#endif + if (!shm->create(1)) { + return nullptr; + } + return shm; +} + int main(int argc, char* argv[]) { #ifdef Q_OS_LINUX @@ -67,7 +94,9 @@ int main(int argc, char* argv[]) // required for the button serialization // TODO: change to QVector in v1.0 qRegisterMetaTypeStreamOperators>("QList"); - qApp->setApplicationVersion(static_cast(APP_VERSION)); + QCoreApplication::setApplicationVersion(APP_VERSION); + QCoreApplication::setApplicationName(QStringLiteral("flameshot")); + QCoreApplication::setOrganizationName(QStringLiteral("flameshot")); // no arguments, just launch Flameshot if (argc == 1) { @@ -97,14 +126,14 @@ int main(int argc, char* argv[]) "_", QLibraryInfo::location(QLibraryInfo::TranslationsPath)); - app.installTranslator(&translator); - app.installTranslator(&qtTranslator); - app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); - app.setApplicationName(QStringLiteral("flameshot")); - app.setOrganizationName(QStringLiteral("flameshot")); + qApp->installTranslator(&translator); + qApp->installTranslator(&qtTranslator); + qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); auto c = Controller::getInstance(); -#if not(defined(Q_OS_MACOS) || defined(Q_OS_WIN)) + FlameshotDaemon::start(); + +#if !(defined(Q_OS_MACOS) || defined(Q_OS_WIN)) new FlameshotDBusAdapter(c); QDBusConnection dbus = QDBusConnection::sessionBus(); if (!dbus.isConnected()) { @@ -114,20 +143,14 @@ int main(int argc, char* argv[]) dbus.registerObject(QStringLiteral("/"), c); dbus.registerService(QStringLiteral("org.flameshot.Flameshot")); #endif - // Exporting captures must be connected after the dbus interface - // or the dbus signal gets blocked until we end the exports. - c->enableExports(); - return app.exec(); + return qApp->exec(); } -#if not(defined(Q_OS_MACOS) || defined(Q_OS_WIN)) +#if !defined(Q_OS_WIN) /*--------------| * CLI parsing | * ------------*/ - QCoreApplication app(argc, argv); - app.setApplicationName(QStringLiteral("flameshot")); - app.setOrganizationName(QStringLiteral("flameshot")); - app.setApplicationVersion(qApp->applicationVersion()); + new QCoreApplication(argc, argv); CommandLineParser parser; // Add description parser.setDescription( @@ -305,7 +328,7 @@ int main(int argc, char* argv[]) checkOption }, configArgument); // Parse - if (!parser.parse(app.arguments())) { + if (!parser.parse(qApp->arguments())) { goto finish; } @@ -313,18 +336,28 @@ int main(int argc, char* argv[]) //-------------- if (parser.isSet(helpOption) || parser.isSet(versionOption)) { } else if (parser.isSet(launcherArgument)) { // LAUNCHER - QDBusMessage m = QDBusMessage::createMethodCall( - QStringLiteral("org.flameshot.Flameshot"), - QStringLiteral("/"), - QLatin1String(""), - QStringLiteral("openLauncher")); - QDBusConnection sessionBus = QDBusConnection::sessionBus(); - if (!sessionBus.isConnected()) { - SystemNotification().sendMessage( - QObject::tr("Unable to connect via DBus")); - } - sessionBus.call(m); + delete qApp; + new QApplication(argc, argv); + Controller* controller = Controller::getInstance(); + controller->openLauncherWindow(); + qApp->exec(); } else if (parser.isSet(guiArgument)) { // GUI + delete qApp; + new QApplication(argc, argv); + // Prevent multiple instances of 'flameshot gui' from running if not + // configured to do so. + if (!ConfigHandler().allowMultipleGuiInstances()) { + auto* mutex = guiMutexLock(); + if (!mutex) { + return 1; + } + QObject::connect( + qApp, &QCoreApplication::aboutToQuit, qApp, [mutex]() { + mutex->detach(); + delete mutex; + }); + } + // Option values QString path = parser.value(pathOption); if (!path.isEmpty()) { @@ -338,7 +371,6 @@ int main(int argc, char* argv[]) bool pin = parser.isSet(pinOption); bool upload = parser.isSet(uploadOption); bool acceptOnSelect = parser.isSet(acceptOnSelectOption); - DBusUtils dbusUtils; CaptureRequest req(CaptureRequest::GRAPHICAL_MODE, delay, path); if (!region.isEmpty()) { req.setInitialSelection(Region().value(region).toRect()); @@ -368,28 +400,12 @@ int main(int argc, char* argv[]) req.addSaveTask(); } } - uint id = req.id(); - req.setStaticID(id); - - // Send message - QDBusMessage m = QDBusMessage::createMethodCall( - QStringLiteral("org.flameshot.Flameshot"), - QStringLiteral("/"), - QLatin1String(""), - QStringLiteral("requestCapture")); - m << req.serialize(); - QDBusConnection sessionBus = QDBusConnection::sessionBus(); - dbusUtils.checkDBusConnection(sessionBus); - sessionBus.call(m); - - if (raw) { - dbusUtils.connectPrintCapture(sessionBus, id); - return waitAfterConnecting(delay, app); - } else if (printGeometry) { - dbusUtils.connectSelectionCapture(sessionBus, id); - return waitAfterConnecting(delay, app); - } + requestCaptureAndWait(req); } else if (parser.isSet(fullArgument)) { // FULL + // Recreate the application as a QApplication + // TODO find a way so we don't have to do this + delete qApp; + new QApplication(argc, argv); // Option values QString path = parser.value(pathOption); if (!path.isEmpty()) { @@ -421,33 +437,12 @@ int main(int argc, char* argv[]) if (!clipboard && path.isEmpty() && !raw && !upload) { req.addSaveTask(); } - uint id = req.id(); - req.setStaticID(id); - DBusUtils dbusUtils; - - // Send message - QDBusMessage m = QDBusMessage::createMethodCall( - QStringLiteral("org.flameshot.Flameshot"), - QStringLiteral("/"), - QLatin1String(""), - QStringLiteral("requestCapture")); - m << req.serialize(); - QDBusConnection sessionBus = QDBusConnection::sessionBus(); - dbusUtils.checkDBusConnection(sessionBus); - sessionBus.call(m); - - if (raw) { - dbusUtils.connectPrintCapture(sessionBus, id); - // timeout just in case - QTimer t; - t.setInterval(delay + 2000); - QObject::connect( - &t, &QTimer::timeout, qApp, &QCoreApplication::quit); - t.start(); - // wait - return app.exec(); - } + requestCaptureAndWait(req); } else if (parser.isSet(screenArgument)) { // SCREEN + // Recreate the application as a QApplication + // TODO find a way so we don't have to do this + delete qApp; + new QApplication(argc, argv); QString numberStr = parser.value(screenNumberOption); // Option values int number = @@ -494,47 +489,21 @@ int main(int argc, char* argv[]) req.addSaveTask(); } - uint id = req.id(); - req.setStaticID(id); - DBusUtils dbusUtils; - - // Send message - QDBusMessage m = QDBusMessage::createMethodCall( - QStringLiteral("org.flameshot.Flameshot"), - QStringLiteral("/"), - QLatin1String(""), - QStringLiteral("requestCapture")); - m << req.serialize(); - QDBusConnection sessionBus = QDBusConnection::sessionBus(); - dbusUtils.checkDBusConnection(sessionBus); - sessionBus.call(m); - - if (raw) { - dbusUtils.connectPrintCapture(sessionBus, id); - // timeout just in case - QTimer t; - t.setInterval(delay + 2000); - QObject::connect( - &t, &QTimer::timeout, qApp, &QCoreApplication::quit); - t.start(); - // wait - return app.exec(); - } + requestCaptureAndWait(req); } else if (parser.isSet(configArgument)) { // CONFIG bool autostart = parser.isSet(autostartOption); bool filename = parser.isSet(filenameOption); bool tray = parser.isSet(trayOption); - bool help = parser.isSet(showHelpOption); bool mainColor = parser.isSet(mainColorOption); bool contrastColor = parser.isSet(contrastColorOption); bool check = parser.isSet(checkOption); bool someFlagSet = - (filename || tray || help || mainColor || contrastColor || check); + (filename || tray || mainColor || contrastColor || check); if (check) { - QTextStream stream(stderr); - bool ok = ConfigHandler(true).checkForErrors(&stream); + QTextStream err(stderr); + bool ok = ConfigHandler(true).checkForErrors(&err); if (ok) { - stream << QStringLiteral("No errors detected.\n"); + err << QStringLiteral("No errors detected.\n"); goto finish; } else { return 1; @@ -542,22 +511,7 @@ int main(int argc, char* argv[]) } ConfigHandler config; if (autostart) { - QDBusMessage m = QDBusMessage::createMethodCall( - QStringLiteral("org.flameshot.Flameshot"), - QStringLiteral("/"), - QLatin1String(""), - QStringLiteral("autostartEnabled")); - if (parser.value(autostartOption) == QLatin1String("false")) { - m << false; - } else if (parser.value(autostartOption) == QLatin1String("true")) { - m << true; - } - QDBusConnection sessionBus = QDBusConnection::sessionBus(); - if (!sessionBus.isConnected()) { - SystemNotification().sendMessage( - QObject::tr("Unable to connect via DBus")); - } - sessionBus.call(m); + config.setStartupLaunch(parser.value(autostartOption) == "true"); } if (filename) { QString newFilename(parser.value(filenameOption)); @@ -570,31 +524,10 @@ int main(int argc, char* argv[]) .arg(fh.parsedPattern()); } if (tray) { - QDBusMessage m = QDBusMessage::createMethodCall( - QStringLiteral("org.flameshot.Flameshot"), - QStringLiteral("/"), - QLatin1String(""), - QStringLiteral("trayIconEnabled")); - if (parser.value(trayOption) == QLatin1String("false")) { - m << false; - } else if (parser.value(trayOption) == QLatin1String("true")) { - m << true; - } - QDBusConnection sessionBus = QDBusConnection::sessionBus(); - if (!sessionBus.isConnected()) { - SystemNotification().sendMessage( - QObject::tr("Unable to connect via DBus")); - } - sessionBus.call(m); - } - if (help) { - if (parser.value(showHelpOption) == QLatin1String("false")) { - config.setShowHelp(false); - } else if (parser.value(showHelpOption) == QLatin1String("true")) { - config.setShowHelp(true); - } + config.setDisabledTrayIcon(parser.value(trayOption) == "false"); } if (mainColor) { + // TODO use value handler QString colorCode = parser.value(mainColorOption); QColor parsedColor(colorCode); config.setUiColor(parsedColor); @@ -607,17 +540,12 @@ int main(int argc, char* argv[]) // Open gui when no options if (!someFlagSet) { - QDBusMessage m = QDBusMessage::createMethodCall( - QStringLiteral("org.flameshot.Flameshot"), - QStringLiteral("/"), - QLatin1String(""), - QStringLiteral("openConfig")); - QDBusConnection sessionBus = QDBusConnection::sessionBus(); - if (!sessionBus.isConnected()) { - SystemNotification().sendMessage( - QObject::tr("Unable to connect via DBus")); - } - sessionBus.call(m); + delete qApp; + new QApplication(argc, argv); + QObject::connect( + qApp, &QApplication::lastWindowClosed, qApp, &QApplication::quit); + Controller::getInstance()->openConfigWindow(); + qApp->exec(); } } finish: diff --git a/src/tools/accept/accepttool.cpp b/src/tools/accept/accepttool.cpp index 3792a305..f636cd63 100644 --- a/src/tools/accept/accepttool.cpp +++ b/src/tools/accept/accepttool.cpp @@ -49,10 +49,10 @@ CaptureTool* AcceptTool::copy(QObject* parent) void AcceptTool::pressed(CaptureContext& context) { emit requestAction(REQ_CAPTURE_DONE_OK); - if (context.request()->tasks() & CaptureRequest::PIN) { + if (context.request.tasks() & CaptureRequest::PIN) { QRect geometry = context.selection; geometry.moveTopLeft(geometry.topLeft() + context.widgetOffset); - context.request()->addPinTask(geometry); + context.request.addTask(CaptureRequest::PIN); } emit requestAction(REQ_CLOSE_GUI); } diff --git a/src/tools/capturecontext.cpp b/src/tools/capturecontext.cpp index 6059e9a7..843a1dc0 100644 --- a/src/tools/capturecontext.cpp +++ b/src/tools/capturecontext.cpp @@ -14,13 +14,3 @@ QPixmap CaptureContext::selectedScreenshotArea() const return screenshot.copy(selection); } } - -CaptureRequest* CaptureContext::request() -{ - return &*Controller::getInstance()->requests().find(requestId); -} - -CaptureRequest* CaptureContext::request() const -{ - return &*Controller::getInstance()->requests().find(requestId); -} diff --git a/src/tools/capturecontext.h b/src/tools/capturecontext.h index 23231d2e..1e9fc4b0 100644 --- a/src/tools/capturecontext.h +++ b/src/tools/capturecontext.h @@ -29,9 +29,7 @@ struct CaptureContext int toolSize; // Mode of the capture widget bool fullscreen; - uint requestId; + CaptureRequest request = CaptureRequest::GRAPHICAL_MODE; QPixmap selectedScreenshotArea() const; - CaptureRequest* request(); - CaptureRequest* request() const; }; diff --git a/src/tools/copy/copytool.cpp b/src/tools/copy/copytool.cpp index 2aa95efd..f9b02d3e 100644 --- a/src/tools/copy/copytool.cpp +++ b/src/tools/copy/copytool.cpp @@ -41,7 +41,7 @@ CaptureTool* CopyTool::copy(QObject* parent) void CopyTool::pressed(CaptureContext& context) { - context.request()->addTask(CaptureRequest::COPY); + context.request.addTask(CaptureRequest::COPY); emit requestAction(REQ_CAPTURE_DONE_OK); emit requestAction(REQ_CLOSE_GUI); } diff --git a/src/tools/imgupload/imguploadertool.cpp b/src/tools/imgupload/imguploadertool.cpp index 5623dc1b..f6057a49 100644 --- a/src/tools/imgupload/imguploadertool.cpp +++ b/src/tools/imgupload/imguploadertool.cpp @@ -41,6 +41,6 @@ CaptureTool* ImgUploaderTool::copy(QObject* parent) void ImgUploaderTool::pressed(CaptureContext& context) { emit requestAction(REQ_CAPTURE_DONE_OK); - context.request()->addTask(CaptureRequest::UPLOAD); + context.request.addTask(CaptureRequest::UPLOAD); emit requestAction(REQ_CLOSE_GUI); } diff --git a/src/tools/imgupload/storages/imguploaderbase.cpp b/src/tools/imgupload/storages/imguploaderbase.cpp index f9367dd2..6eee3c1a 100644 --- a/src/tools/imgupload/storages/imguploaderbase.cpp +++ b/src/tools/imgupload/storages/imguploaderbase.cpp @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors #include "imguploaderbase.h" +#include "src/core/flameshotdaemon.h" #include "src/utils/confighandler.h" #include "src/utils/globalvalues.h" #include "src/utils/history.h" @@ -9,6 +10,7 @@ #include "src/widgets/loadspinner.h" #include "src/widgets/notificationwidget.h" #include +// FIXME #include #include #include #include @@ -157,13 +159,13 @@ void ImgUploaderBase::openURL() void ImgUploaderBase::copyURL() { - QApplication::clipboard()->setText(m_imageURL.toString()); + FlameshotDaemon::copyToClipboard(m_imageURL.toString()); m_notification->showMessage(tr("URL copied to clipboard.")); } void ImgUploaderBase::copyImage() { - QApplication::clipboard()->setPixmap(m_pixmap); + FlameshotDaemon::copyToClipboard(m_pixmap); m_notification->showMessage(tr("Screenshot copied to clipboard.")); } @@ -173,4 +175,4 @@ void ImgUploaderBase::deleteCurrentImage() HISTORY_FILE_NAME unpackFileName = history.unpackFileName(m_currentImageName); deleteImage(unpackFileName.file, unpackFileName.token); -} \ No newline at end of file +} diff --git a/src/tools/pin/pintool.cpp b/src/tools/pin/pintool.cpp index de53110e..a022e07f 100644 --- a/src/tools/pin/pintool.cpp +++ b/src/tools/pin/pintool.cpp @@ -43,8 +43,6 @@ CaptureTool* PinTool::copy(QObject* parent) void PinTool::pressed(CaptureContext& context) { emit requestAction(REQ_CAPTURE_DONE_OK); - QRect geometry = context.selection; - geometry.setTopLeft(geometry.topLeft() + context.widgetOffset); - context.request()->addPinTask(geometry); + context.request.addTask(CaptureRequest::PIN); emit requestAction(REQ_CLOSE_GUI); } diff --git a/src/tools/save/savetool.cpp b/src/tools/save/savetool.cpp index bc2460cf..95856660 100644 --- a/src/tools/save/savetool.cpp +++ b/src/tools/save/savetool.cpp @@ -41,7 +41,7 @@ CaptureTool* SaveTool::copy(QObject* parent) void SaveTool::pressed(CaptureContext& context) { - context.request()->addSaveTask(); + context.request.addSaveTask(); emit requestAction(REQ_CAPTURE_DONE_OK); emit requestAction(REQ_CLOSE_GUI); } diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index afb3c29a..dae6ca46 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -1,8 +1,7 @@ # Required to generate MOC target_sources( flameshot - PRIVATE dbusutils.h - filenamehandler.h + PRIVATE filenamehandler.h screengrabber.h systemnotification.h valuehandler.h @@ -18,7 +17,6 @@ target_sources( systemnotification.cpp valuehandler.cpp screenshotsaver.cpp - dbusutils.cpp globalvalues.cpp desktopfileparse.cpp desktopinfo.cpp diff --git a/src/utils/confighandler.cpp b/src/utils/confighandler.cpp index 91c5121c..b4741945 100644 --- a/src/utils/confighandler.cpp +++ b/src/utils/confighandler.cpp @@ -81,6 +81,10 @@ static QMap> OPTION("disabledTrayIcon" ,Bool ( false )), OPTION("historyConfirmationToDelete" ,Bool ( true )), OPTION("checkForUpdates" ,Bool ( true )), + OPTION("allowMultipleGuiInstances" ,Bool ( false )), +#if !defined(Q_OS_WIN) + OPTION("autoCloseIdleDaemon" ,Bool ( false )), +#endif #if defined(Q_OS_MACOS) OPTION("startupLaunch" ,Bool ( false )), #else diff --git a/src/utils/confighandler.h b/src/utils/confighandler.h index bc760a40..fc94ab1e 100644 --- a/src/utils/confighandler.h +++ b/src/utils/confighandler.h @@ -29,9 +29,14 @@ class QTextStream; * and `TYPE` is the C++ type. */ #define CONFIG_SETTER(FUNC, KEY, TYPE) \ - void FUNC(const TYPE& value) \ + void FUNC(const TYPE& val) \ { \ - setValue(QStringLiteral(#KEY), QVariant::fromValue(value)); \ + QString key = QStringLiteral(#KEY); \ + /* Without this check, multiple `flameshot gui` instances running */ \ + /* simultaneously would cause an endless loop of fileWatcher calls */ \ + if (QVariant::fromValue(val) != value(key)) { \ + setValue(key, QVariant::fromValue(val)); \ + } \ } /** @@ -75,6 +80,10 @@ public: CONFIG_GETTER_SETTER(drawFontSize, setDrawFontSize, int) CONFIG_GETTER_SETTER(keepOpenAppLauncher, setKeepOpenAppLauncher, bool) CONFIG_GETTER_SETTER(checkForUpdates, setCheckForUpdates, bool) + CONFIG_GETTER_SETTER(allowMultipleGuiInstances, + setAllowMultipleGuiInstances, + bool) + CONFIG_GETTER_SETTER(autoCloseIdleDaemon, setAutoCloseIdleDaemon, bool) CONFIG_GETTER_SETTER(showStartupLaunchMessage, setShowStartupLaunchMessage, bool) diff --git a/src/utils/dbusutils.cpp b/src/utils/dbusutils.cpp deleted file mode 100644 index 1a19977c..00000000 --- a/src/utils/dbusutils.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -// SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors - -#include "dbusutils.h" -#include "src/utils/systemnotification.h" -#include -#include -#include -#include - -DBusUtils::DBusUtils(QObject* parent) - : QObject(parent) -{} - -void DBusUtils::connectPrintCapture(QDBusConnection& session, uint id) -{ - m_id = id; - // captureTaken - session.connect(QStringLiteral("org.flameshot.Flameshot"), - QStringLiteral("/"), - QLatin1String(""), - QStringLiteral("captureTaken"), - this, - SLOT(captureTaken(uint, QByteArray, QRect))); - // captureFailed - session.connect(QStringLiteral("org.flameshot.Flameshot"), - QStringLiteral("/"), - QLatin1String(""), - QStringLiteral("captureFailed"), - this, - SLOT(captureFailed(uint))); -} - -void DBusUtils::connectSelectionCapture(QDBusConnection& session, uint id) -{ - m_id = id; - // captureTaken - session.connect(QStringLiteral("org.flameshot.Flameshot"), - QStringLiteral("/"), - QLatin1String(""), - QStringLiteral("captureTaken"), - this, - SLOT(selectionTaken(uint, QByteArray, QRect))); - // captureFailed - session.connect(QStringLiteral("org.flameshot.Flameshot"), - QStringLiteral("/"), - QLatin1String(""), - QStringLiteral("captureFailed"), - this, - SLOT(captureFailed(uint))); -} - -void DBusUtils::checkDBusConnection(const QDBusConnection& connection) -{ - if (!connection.isConnected()) { - SystemNotification().sendMessage(tr("Unable to connect via DBus")); - qApp->exit(1); - } -} - -void DBusUtils::captureTaken(uint id, QByteArray rawImage, QRect selection) -{ - if (m_id == id) { - QFile file; - file.open(stdout, QIODevice::WriteOnly); - - file.write(rawImage); - file.close(); - qApp->exit(); - } -} - -void DBusUtils::captureFailed(uint id) -{ - if (m_id == id) { - QTextStream(stdout) << "screenshot aborted\n"; - qApp->exit(1); - } -} - -void DBusUtils::selectionTaken(uint id, QByteArray rawImage, QRect selection) -{ - if (m_id == id) { - QFile file; - file.open(stdout, QIODevice::WriteOnly); - - QTextStream out(&file); - // TODO also make this change in D-Bus refactor branch - out << selection.width() << "x" << selection.height() << "+" - << selection.x() << "+" << selection.y() << "\n"; - file.close(); - qApp->exit(); - } -} diff --git a/src/utils/dbusutils.h b/src/utils/dbusutils.h deleted file mode 100644 index 1000cb10..00000000 --- a/src/utils/dbusutils.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -// SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors - -#pragma once - -#include "src/cli/commandlineparser.h" -#include -#include - -class DBusUtils : public QObject -{ - Q_OBJECT -public: - explicit DBusUtils(QObject* parent = nullptr); - - void connectPrintCapture(QDBusConnection& session, uint id); - void checkDBusConnection(const QDBusConnection& connection); - void connectSelectionCapture(QDBusConnection& session, uint id); - -public slots: - void selectionTaken(uint id, QByteArray rawImage, QRect selection); - void captureTaken(uint id, QByteArray rawImage, QRect selection); - void captureFailed(uint id); - -private: - uint m_id; -}; diff --git a/src/utils/screenshotsaver.cpp b/src/utils/screenshotsaver.cpp index 02b488ba..b17f5c53 100644 --- a/src/utils/screenshotsaver.cpp +++ b/src/utils/screenshotsaver.cpp @@ -3,6 +3,7 @@ #include "screenshotsaver.h" #include "src/core/controller.h" +#include "src/core/flameshotdaemon.h" #include "src/utils/confighandler.h" #include "src/utils/filenamehandler.h" #include "src/utils/globalvalues.h" @@ -22,13 +23,7 @@ #include "src/widgets/capture/capturewidget.h" #endif -ScreenshotSaver::ScreenshotSaver() - : m_id(0) -{} - -ScreenshotSaver::ScreenshotSaver(const unsigned id) - : m_id(id) -{} +ScreenshotSaver::ScreenshotSaver() {} void ScreenshotSaver::saveToClipboardMime(const QPixmap& capture, const QString& imageType) @@ -102,8 +97,6 @@ bool ScreenshotSaver::saveToFilesystem(const QPixmap& capture, if (ok) { saveMessage += QObject::tr("Capture saved as ") + completePath; - Controller::getInstance()->sendCaptureSaved( - m_id, QFileInfo(completePath).canonicalFilePath()); } else { saveMessage += QObject::tr("Error trying to save as ") + completePath; if (file.error() != QFile::NoError) { @@ -192,20 +185,11 @@ bool ScreenshotSaver::saveToFilesystemGUI(const QPixmap& capture) ConfigHandler().setSavePath(pathNoFile); QString msg = QObject::tr("Capture saved as ") + savePath; - - if (config.copyPathAfterSave()) { - msg = - QObject::tr("Capture is saved and copied to the clipboard as ") + - savePath; - } - SystemNotification().sendMessage(msg, savePath); - Controller::getInstance()->sendCaptureSaved( - m_id, QFileInfo(savePath).canonicalFilePath()); - if (config.copyPathAfterSave()) { - QApplication::clipboard()->setText(savePath); + FlameshotDaemon::copyToClipboard( + savePath, QObject::tr("Path copied to clipboard as ") + savePath); } } else { diff --git a/src/utils/screenshotsaver.h b/src/utils/screenshotsaver.h index 73413c1a..42e6edfe 100644 --- a/src/utils/screenshotsaver.h +++ b/src/utils/screenshotsaver.h @@ -12,7 +12,6 @@ class ScreenshotSaver { public: ScreenshotSaver(); - ScreenshotSaver(const unsigned id); void saveToClipboard(const QPixmap& capture); void saveToClipboardMime(const QPixmap& capture, const QString& imageType); @@ -22,7 +21,6 @@ public: bool saveToFilesystemGUI(const QPixmap& capture); private: - unsigned m_id; QString ShowSaveFileDialog(QWidget* parent, const QString& title, const QString& directory); diff --git a/src/utils/systemnotification.cpp b/src/utils/systemnotification.cpp index 2081c106..d1a5deb0 100644 --- a/src/utils/systemnotification.cpp +++ b/src/utils/systemnotification.cpp @@ -4,7 +4,7 @@ #include #include -#if not(defined(Q_OS_MACOS) || defined(Q_OS_WIN)) +#if !(defined(Q_OS_MACOS) || defined(Q_OS_WIN)) #include #include #include @@ -14,7 +14,7 @@ SystemNotification::SystemNotification(QObject* parent) : QObject(parent) , m_interface(nullptr) { -#if not(defined(Q_OS_MACOS) || defined(Q_OS_WIN)) +#if !(defined(Q_OS_MACOS) || defined(Q_OS_WIN)) m_interface = new QDBusInterface(QStringLiteral("org.freedesktop.Notifications"), QStringLiteral("/org/freedesktop/Notifications"), diff --git a/src/widgets/capture/capturewidget.cpp b/src/widgets/capture/capturewidget.cpp index 73fd33dd..af8b0d57 100644 --- a/src/widgets/capture/capturewidget.cpp +++ b/src/widgets/capture/capturewidget.cpp @@ -48,8 +48,8 @@ // an area of selection with its respective buttons. // enableSaveWindow -CaptureWidget::CaptureWidget(uint id, - const QString& savePath, + +CaptureWidget::CaptureWidget(const CaptureRequest& req, bool fullScreen, QWidget* parent) : QWidget(parent) @@ -90,7 +90,7 @@ CaptureWidget::CaptureWidget(uint id, m_uiColor = m_config.uiColor(); m_contrastUiColor = m_config.contrastUiColor(); setMouseTracking(true); - initContext(fullScreen, id); + initContext(fullScreen, req); #if (defined(Q_OS_WIN) || defined(Q_OS_MACOS)) // Top left of the whole set of screens QPoint topLeft(0, 0); @@ -251,9 +251,12 @@ CaptureWidget::~CaptureWidget() } #endif if (m_captureDone) { - emit captureTaken(m_context.requestId, pixmap(), m_context.selection); + QRect geometry(m_context.selection); + geometry.setTopLeft(geometry.topLeft() + m_context.widgetOffset); + Controller::getInstance()->exportCapture( + pixmap(), geometry, m_context.request); } else { - emit captureFailed(m_context.requestId); + Controller::getInstance()->handleCaptureFailed(); } } @@ -261,7 +264,7 @@ void CaptureWidget::initButtons() { auto allButtonTypes = CaptureToolButton::getIterableButtonTypes(); auto visibleButtonTypes = m_config.buttons(); - if (m_context.request()->tasks() == CaptureRequest::NO_TASK) { + if (m_context.request.tasks() == CaptureRequest::NO_TASK) { allButtonTypes.removeOne(CaptureTool::TYPE_ACCEPT); visibleButtonTypes.removeOne(CaptureTool::TYPE_ACCEPT); } else { @@ -891,7 +894,7 @@ void CaptureWidget::changeEvent(QEvent* e) } } -void CaptureWidget::initContext(bool fullscreen, uint requestId) +void CaptureWidget::initContext(bool fullscreen, const CaptureRequest& req) { m_context.color = m_config.drawColor(); m_context.widgetOffset = mapToGlobal(QPoint(0, 0)); @@ -900,15 +903,7 @@ void CaptureWidget::initContext(bool fullscreen, uint requestId) m_context.fullscreen = fullscreen; // initialize m_context.request - if (requestId != 0) { - m_context.requestId = requestId; - } else { - CaptureRequest req(CaptureRequest::GRAPHICAL_MODE); - uint id = req.id(); - req.setStaticID(id); - Controller::getInstance()->requests().insert(id, req); - m_context.requestId = id; - } + m_context.request = req; } void CaptureWidget::initPanel() @@ -1040,7 +1035,7 @@ void CaptureWidget::initSelection() { // Be mindful of the order of statements, so that slots are called properly m_selection = new SelectionWidget(m_uiColor, this); - QRect initialSelection = m_context.request()->initialSelection(); + QRect initialSelection = m_context.request.initialSelection(); connect(m_selection, &SelectionWidget::geometryChanged, this, [this]() { QRect constrainedToCaptureArea = m_selection->geometry().intersected(rect()); @@ -1052,16 +1047,10 @@ void CaptureWidget::initSelection() }); connect(m_selection, &SelectionWidget::geometrySettled, this, [this]() { if (m_selection->isVisibleTo(this)) { - auto req = m_context.request(); - if (req->tasks() & CaptureRequest::ACCEPT_ON_SELECT) { - req->removeTask(CaptureRequest::ACCEPT_ON_SELECT); + auto& req = m_context.request; + if (req.tasks() & CaptureRequest::ACCEPT_ON_SELECT) { + req.removeTask(CaptureRequest::ACCEPT_ON_SELECT); m_captureDone = true; - if (req->tasks() & CaptureRequest::PIN) { - QRect geometry = m_context.selection; - geometry.setTopLeft(geometry.topLeft() + - m_context.widgetOffset); - req->addPinTask(geometry); - } close(); } m_buttonHandler->updatePosition(m_selection->geometry()); diff --git a/src/widgets/capture/capturewidget.h b/src/widgets/capture/capturewidget.h index 959c8137..f213eac3 100644 --- a/src/widgets/capture/capturewidget.h +++ b/src/widgets/capture/capturewidget.h @@ -41,8 +41,7 @@ class CaptureWidget : public QWidget Q_OBJECT public: - explicit CaptureWidget(uint id = 0, - const QString& savePath = QString(), + explicit CaptureWidget(const CaptureRequest& req, bool fullScreen = true, QWidget* parent = nullptr); ~CaptureWidget(); @@ -57,8 +56,6 @@ public slots: void deleteToolWidgetOrClose(); signals: - void captureTaken(uint id, const QPixmap& capture, const QRect& selection); - void captureFailed(uint id); void colorChanged(const QColor& c); void toolSizeChanged(int size); @@ -105,7 +102,7 @@ private: void showColorPicker(const QPoint& pos); bool startDrawObjectTool(const QPoint& pos); QPointer activeToolObject(); - void initContext(bool fullscreen, uint requestId); + void initContext(bool fullscreen, const CaptureRequest& req); void initPanel(); void initSelection(); void initShortcuts(); diff --git a/src/widgets/capturelauncher.cpp b/src/widgets/capturelauncher.cpp index 67ed3619..91e8ecab 100644 --- a/src/widgets/capturelauncher.cpp +++ b/src/widgets/capturelauncher.cpp @@ -21,7 +21,6 @@ CaptureLauncher::CaptureLauncher(QDialog* parent) : QDialog(parent) - , m_id(0) { setAttribute(Qt::WA_DeleteOnClose); setWindowIcon(QIcon(GlobalValues::iconPath())); @@ -108,7 +107,6 @@ void CaptureLauncher::startCapture() auto mode = static_cast( m_captureType->currentData().toInt()); CaptureRequest req(mode, 600 + m_delaySpinBox->value() * 1000); - m_id = req.id(); connectCaptureSlots(); Controller::getInstance()->requestCapture(req); } @@ -166,16 +164,13 @@ void CaptureLauncher::disconnectCaptureSlots() &CaptureLauncher::captureFailed); } -void CaptureLauncher::captureTaken(uint id, QPixmap p, const QRect& selection) +void CaptureLauncher::captureTaken(QPixmap p, const QRect&) { // MacOS specific, more details in the function disconnectCaptureSlots() disconnectCaptureSlots(); - if (id == m_id) { - m_id = 0; - m_imageLabel->setScreenshot(p); - show(); - } + m_imageLabel->setScreenshot(p); + show(); auto mode = static_cast( m_captureType->currentData().toInt()); @@ -186,14 +181,10 @@ void CaptureLauncher::captureTaken(uint id, QPixmap p, const QRect& selection) m_launchButton->setEnabled(true); } -void CaptureLauncher::captureFailed(uint id) +void CaptureLauncher::captureFailed() { // MacOS specific, more details in the function disconnectCaptureSlots() disconnectCaptureSlots(); - - if (id == m_id) { - m_id = 0; - show(); - } + show(); m_launchButton->setEnabled(true); } diff --git a/src/widgets/capturelauncher.h b/src/widgets/capturelauncher.h index f8574c1e..a1340678 100644 --- a/src/widgets/capturelauncher.h +++ b/src/widgets/capturelauncher.h @@ -26,8 +26,8 @@ private: private slots: void startCapture(); void startDrag(); - void captureTaken(uint id, QPixmap p, const QRect& selection); - void captureFailed(uint id); + void captureTaken(QPixmap p, const QRect& selection); + void captureFailed(); private: QSpinBox* m_delaySpinBox; @@ -36,5 +36,4 @@ private: QPushButton* m_launchButton; QLabel* m_CaptureModeLabel; ImageLabel* m_imageLabel; - uint m_id; }; diff --git a/src/widgets/historywidget.cpp b/src/widgets/historywidget.cpp index 84c05477..91c82860 100644 --- a/src/widgets/historywidget.cpp +++ b/src/widgets/historywidget.cpp @@ -1,11 +1,10 @@ #include "historywidget.h" +#include "src/core/flameshotdaemon.h" #include "src/tools/imgupload/imguploadermanager.h" #include "src/utils/confighandler.h" #include "src/utils/globalvalues.h" #include "src/utils/history.h" #include "src/widgets/notificationwidget.h" -#include -#include #include #include #include @@ -143,7 +142,7 @@ void HistoryWidget::addLine(const QString& path, const QString& fileName) buttonCopyUrl->setText(tr("Copy URL")); buttonCopyUrl->setMinimumHeight(HISTORYPIXMAP_MAX_PREVIEW_HEIGHT); connect(buttonCopyUrl, &QPushButton::clicked, this, [=]() { - QApplication::clipboard()->setText(url); + FlameshotDaemon::copyToClipboard(url); m_notification->showMessage(tr("URL copied to clipboard.")); this->close(); }); diff --git a/src/widgets/historywidget.h b/src/widgets/historywidget.h index 163342bc..c8ab1da5 100644 --- a/src/widgets/historywidget.h +++ b/src/widgets/historywidget.h @@ -2,10 +2,8 @@ #define HISTORYWIDGET_H #include -#include -#include -#include +class QString; class QLayout; class QVBoxLayout; class NotificationWidget; diff --git a/src/widgets/infowindow.cpp b/src/widgets/infowindow.cpp index 00a8916f..e1215bb7 100644 --- a/src/widgets/infowindow.cpp +++ b/src/widgets/infowindow.cpp @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors #include "infowindow.h" +#include "src/core/flameshotdaemon.h" #include "src/core/qguiappcurrentscreen.h" #include "src/utils/globalvalues.h" #include @@ -88,9 +89,8 @@ void InfoWindow::initLabels() void InfoWindow::copyInfo() { - QClipboard* clipboard = QApplication::clipboard(); - clipboard->setText(GlobalValues::versionInfo() + "\n" + - generateKernelString()); + FlameshotDaemon::copyToClipboard(GlobalValues::versionInfo() + "\n" + + generateKernelString()); } void InfoWindow::keyPressEvent(QKeyEvent* e) diff --git a/tests/action_options.sh b/tests/action_options.sh index 0b3a33cc..93663fba 100644 --- a/tests/action_options.sh +++ b/tests/action_options.sh @@ -41,6 +41,32 @@ cmd() { sleep 1 } +notify() { + if [ "$FLAMESHOT_PLATFORM" = "MAC" ] + then +osascript - "$1" <&2 && read ____ } @@ -54,7 +80,7 @@ for subcommand in full screen do cmd flameshot "$subcommand" --path /tmp/ cmd flameshot "$subcommand" --clipboard - cmd command "$FLAMESHOT" "$subcommand" --raw | display + cmd command "$FLAMESHOT" "$subcommand" --raw | display_img [ "$subcommand" = "full" ] && sleep 1 echo done @@ -70,30 +96,30 @@ sleep 1 # ┗━━━━━━━━━━━━━━━┛ wait_for_key -notify-send "GUI Test 1: --path" "Make a selection, then accept" +notify "GUI Test 1: --path" #"Make a selection, then accept" cmd flameshot gui --path /tmp/ wait_for_key -notify-send "GUI Test 2: Clipboard" "Make a selection, then accept" +notify "GUI Test 2: Clipboard" "Make a selection, then accept" cmd flameshot gui --clipboard wait_for_key -notify-send "GUI Test 3: Print geometry" "Make a selection, then accept" +notify "GUI Test 3: Print geometry" "Make a selection, then accept" cmd command "$FLAMESHOT" gui --print-geometry wait_for_key -notify-send "GUI Test 4: Pin" "Make a selection, then accept" +notify "GUI Test 4: Pin" "Make a selection, then accept" cmd flameshot gui --pin wait_for_key -notify-send "GUI Test 5: Print raw" "Make a selection, then accept" -cmd command "$FLAMESHOT" gui --raw | display +notify "GUI Test 5: Print raw" "Make a selection, then accept" +cmd command "$FLAMESHOT" gui --raw | display_img wait_for_key -notify-send "GUI Test 6: Copy on select" "Make a selection, flameshot will close automatically" +notify "GUI Test 6: Copy on select" "Make a selection, flameshot will close automatically" cmd flameshot gui --clipboard --accept-on-select wait_for_key -notify-send "GUI Test 7: File dialog on select" "After selecting, a file dialog will open" +notify "GUI Test 7: File dialog on select" "After selecting, a file dialog will open" cmd flameshot gui --accept-on-select # All options except for --print-geometry (incompatible with --raw) wait_for_key -notify-send "GUI Test 8: All actions except print-geometry" "Just make a selection" -cmd command "$FLAMESHOT" gui -p /tmp/ -c -r --pin | display +notify "GUI Test 8: All actions except print-geometry" "Just make a selection" +cmd command "$FLAMESHOT" gui -p /tmp/ -c -r --pin | display_img echo '>> All tests done.'