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

@@ -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 <QApplication>
#include <QDir>
#include <QLibraryInfo>
#include <QTextStream>
#include <QSharedMemory>
#include <QTimer>
#include <QTranslator>
@@ -28,22 +29,12 @@
#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX)
#include "src/core/flameshotdbusadapter.h"
#include "src/utils/dbusutils.h"
#include <QApplication>
#include <QDBusConnection>
#include <QDBusMessage>
#include <desktopinfo.h>
#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<int>>("QList<int>");
qApp->setApplicationVersion(static_cast<QString>(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: