Reduce dependence on D-Bus (#2003)

* Handle captures without sigslots

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Set {app,organization}Name and version consistently

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Make 'full' dbus-free

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* 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ć <harisgusic.dev@gmail.com>

* Use QApplication with the 'full' subcommand

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Do unto 'screen' as we did to 'full'

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add FlameshotDaemon singleton class

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Support clipboard hosting for both pixmaps and text

* Fix upload handling

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Do not show tray icon if not daemon

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Clean up handling of pin task

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Remove annoying Qt warning messages

The messages were caused by the color wheel.

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix small bug in Controller::exportCapture

* Fix --raw output

* Make 'gui' dbus-independent

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix accept on select bug

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix compile error on Windows

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Make it work on Windows

* Remove obsolete function in main.cpp

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Make 'launcher' work without dbus

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* clang-format, sigh

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Enable CLI parsing on MacOS

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Make 'config' work without dbus

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Small refactor of capture request handling

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Remove obsolete DBusUtils

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Remove unused D-Bus sigslots

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Remove D-Bus methods openConfig, autostartEnabled and trayIconEnabled

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Remove D-Bus method requestCapture

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Remove CaptureRequest id mechanism

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix 'launcher' crash

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Handle clipboard notifications properly

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add 'autoCloseIdleDaemon' option

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Document FlameshotDaemon class

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Make 'flameshot gui' run in single-application mode

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add `allowmultipleGuiInstances` config option

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix endless loop with multiple GUI instances

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Move upload confirmation dialog where it belongs

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add the new config options to the GUI as well

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix failing build on Windows

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Handle persistence on MacOS

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* fixed notifications on macos

* Fixed display on macos

* Reformat tests/action_options.sh

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix infinite recursion in tests/action_options.sh

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

Co-authored-by: Dearsh Oberoi <59907159+deo002@users.noreply.github.com>
Co-authored-by: Jeremy Borgman <borgman.jeremy@pm.me>
This commit is contained in:
Haris Gušić
2021-12-08 22:18:39 +01:00
committed by GitHub
parent 203b5baab6
commit 233c765b1f
38 changed files with 768 additions and 766 deletions

View File

@@ -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 <QAction>
#include <QApplication>
#include <QBuffer>
#include <QClipboard>
#include <QDebug>
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QMenu>
@@ -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)