File flameshot.cpp#
File List > core > flameshot.cpp
Go to the documentation of this file.
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors
#include "flameshot.h"
#include "flameshotdaemon.h"
#if defined(Q_OS_MACOS)
#include "external/QHotkey/QHotkey"
#endif
#include "abstractlogger.h"
#include "screenshotsaver.h"
#include "src/config/configresolver.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/screengrabber.h"
#include "src/widgets/capture/capturewidget.h"
#include "src/widgets/capturelauncher.h"
#include "src/widgets/imguploaddialog.h"
#include "src/widgets/infowindow.h"
#include "src/widgets/uploadhistory.h"
#include <QApplication>
#include <QBuffer>
#include <QDebug>
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QFile>
#include <QMessageBox>
#include <QThread>
#include <QTimer>
#include <QUrl>
#include <QVersionNumber>
#if defined(Q_OS_MACOS)
#include <QScreen>
#endif
Flameshot::Flameshot()
: m_captureWindow(nullptr)
, m_haveExternalWidget(false)
#if defined(Q_OS_MACOS)
, m_HotkeyScreenshotCapture(nullptr)
, m_HotkeyScreenshotHistory(nullptr)
#endif
{
QString StyleSheet = CaptureButton::globalStyleSheet();
qApp->setStyleSheet(StyleSheet);
#if defined(Q_OS_MACOS)
// Try to take a test screenshot, MacOS will request a "Screen Recording"
// permissions on the first run. Otherwise it will be hidden under the
// CaptureWidget
QScreen* currentScreen = QGuiAppCurrentScreen().currentScreen();
currentScreen->grabWindow(QApplication::desktop()->winId(), 0, 0, 1, 1);
// set global shortcuts for MacOS
m_HotkeyScreenshotCapture = new QHotkey(
QKeySequence(ConfigHandler().shortcut("TAKE_SCREENSHOT")), true, this);
QObject::connect(m_HotkeyScreenshotCapture,
&QHotkey::activated,
qApp,
[this]() { gui(); });
m_HotkeyScreenshotHistory = new QHotkey(
QKeySequence(ConfigHandler().shortcut("SCREENSHOT_HISTORY")), true, this);
QObject::connect(m_HotkeyScreenshotHistory,
&QHotkey::activated,
qApp,
[this]() { history(); });
#endif
}
Flameshot* Flameshot::instance()
{
static Flameshot c;
return &c;
}
CaptureWidget* Flameshot::gui(const CaptureRequest& req)
{
if (!resolveAnyConfigErrors()) {
return nullptr;
}
#if defined(Q_OS_MACOS)
// This is required on MacOS because of Mission Control. If you'll switch to
// another Desktop you cannot take a new screenshot from the tray, you have
// to switch back to the Flameshot Desktop manually. It is not obvious and a
// large number of users are confused and report a bug.
if (m_captureWindow != nullptr) {
m_captureWindow->close();
delete m_captureWindow;
m_captureWindow = nullptr;
}
#endif
if (nullptr == m_captureWindow) {
// TODO is this unnecessary now?
int timeout = 5000; // 5 seconds
const int delay = 100;
QWidget* modalWidget = nullptr;
for (; timeout >= 0; timeout -= delay) {
modalWidget = qApp->activeModalWidget();
if (nullptr == modalWidget) {
break;
}
modalWidget->close();
modalWidget->deleteLater();
QThread::msleep(delay);
}
if (0 == timeout) {
QMessageBox::warning(
nullptr, tr("Error"), tr("Unable to close active modal widgets"));
return nullptr;
}
m_captureWindow = new CaptureWidget(req);
#ifdef Q_OS_WIN
m_captureWindow->show();
#elif defined(Q_OS_MACOS)
// In "Emulate fullscreen mode"
m_captureWindow->showFullScreen();
m_captureWindow->activateWindow();
m_captureWindow->raise();
#else
m_captureWindow->showFullScreen();
// m_captureWindow->show(); // For CaptureWidget Debugging under Linux
#endif
return m_captureWindow;
} else {
emit captureFailed();
return nullptr;
}
}
void Flameshot::screen(CaptureRequest req, const int screenNumber)
{
if (!resolveAnyConfigErrors()) {
return;
}
bool ok = true;
QScreen* screen;
if (screenNumber < 0) {
QPoint globalCursorPos = QCursor::pos();
screen = qApp->screenAt(globalCursorPos);
} else if (screenNumber >= qApp->screens().count()) {
AbstractLogger() << QObject::tr(
"Requested screen exceeds screen count");
emit captureFailed();
return;
} else {
screen = qApp->screens()[screenNumber];
}
QPixmap p(ScreenGrabber().grabScreen(screen, ok));
if (ok) {
QRect geometry = ScreenGrabber().screenGeometry(screen);
QRect region = req.initialSelection();
if (region.isNull()) {
region = ScreenGrabber().screenGeometry(screen);
} else {
QRect screenGeom = ScreenGrabber().screenGeometry(screen);
screenGeom.moveTopLeft({ 0, 0 });
region = region.intersected(screenGeom);
p = p.copy(region);
}
if (req.tasks() & CaptureRequest::PIN) {
// change geometry for pin task
req.addPinTask(region);
}
exportCapture(p, geometry, req);
} else {
emit captureFailed();
}
}
void Flameshot::full(const CaptureRequest& req)
{
if (!resolveAnyConfigErrors()) {
return;
}
bool ok = true;
QPixmap p(ScreenGrabber().grabEntireDesktop(ok));
QRect region = req.initialSelection();
if (!region.isNull()) {
p = p.copy(region);
}
if (ok) {
QRect selection; // `flameshot full` does not support --selection
exportCapture(p, selection, req);
} else {
emit captureFailed();
}
}
void Flameshot::launcher()
{
if (!resolveAnyConfigErrors()) {
return;
}
if (m_launcherWindow == nullptr) {
m_launcherWindow = new CaptureLauncher();
}
m_launcherWindow->show();
#if defined(Q_OS_MACOS)
m_launcherWindow->activateWindow();
m_launcherWindow->raise();
#endif
}
void Flameshot::config()
{
if (!resolveAnyConfigErrors()) {
return;
}
if (m_configWindow == nullptr) {
m_configWindow = new ConfigWindow();
m_configWindow->show();
#if defined(Q_OS_MACOS)
m_configWindow->activateWindow();
m_configWindow->raise();
#endif
}
}
void Flameshot::info()
{
if (m_infoWindow == nullptr) {
m_infoWindow = new InfoWindow();
#if defined(Q_OS_MACOS)
m_infoWindow->activateWindow();
m_infoWindow->raise();
#endif
}
}
void Flameshot::history()
{
static UploadHistory* historyWidget = nullptr;
if (historyWidget == nullptr) {
historyWidget = new UploadHistory;
historyWidget->loadHistory();
connect(historyWidget, &QObject::destroyed, this, []() {
historyWidget = nullptr;
});
}
historyWidget->show();
#if defined(Q_OS_MACOS)
historyWidget->activateWindow();
historyWidget->raise();
#endif
}
void Flameshot::openSavePath()
{
QString savePath = ConfigHandler().savePath();
if (!savePath.isEmpty()) {
QDesktopServices::openUrl(QUrl::fromLocalFile(savePath));
}
}
QVersionNumber Flameshot::getVersion()
{
return QVersionNumber::fromString(
QStringLiteral(APP_VERSION).replace("v", ""));
}
void Flameshot::setOrigin(Origin origin)
{
m_origin = origin;
}
Flameshot::Origin Flameshot::origin()
{
return m_origin;
}
bool Flameshot::resolveAnyConfigErrors()
{
bool resolved = true;
ConfigHandler confighandler;
if (!confighandler.checkUnrecognizedSettings() ||
!confighandler.checkSemantics()) {
auto* resolver = new ConfigResolver();
QObject::connect(
resolver, &ConfigResolver::rejected, [resolver, &resolved]() {
resolved = false;
resolver->deleteLater();
if (origin() == CLI) {
exit(1);
}
});
QObject::connect(
resolver, &ConfigResolver::accepted, [resolver, &resolved]() {
resolved = true;
resolver->close();
resolver->deleteLater();
// Ensure that the dialog is closed before starting capture
qApp->processEvents();
});
resolver->exec();
qApp->processEvents();
}
return resolved;
}
void Flameshot::requestCapture(const CaptureRequest& request)
{
if (!resolveAnyConfigErrors()) {
return;
}
switch (request.captureMode()) {
case CaptureRequest::FULLSCREEN_MODE:
QTimer::singleShot(request.delay(),
[this, request] { full(request); });
break;
case CaptureRequest::SCREEN_MODE: {
int&& number = request.data().toInt();
QTimer::singleShot(request.delay(), [this, request, number]() {
screen(request, number);
});
break;
}
case CaptureRequest::GRAPHICAL_MODE: {
QTimer::singleShot(
request.delay(), this, [this, request]() { gui(request); });
break;
}
default:
emit captureFailed();
break;
}
}
void Flameshot::exportCapture(const QPixmap& capture,
QRect& selection,
const CaptureRequest& req)
{
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()) {
saveToFilesystemGUI(capture);
} else {
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) {
AbstractLogger::info()
<< QObject::tr("Full screen screenshot pinned to screen");
}
}
if (tasks & CR::UPLOAD) {
if (!ConfigHandler().uploadWithoutConfirmation()) {
auto* 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().copyURLAfterUpload()) {
if (!(tasks & CR::COPY)) {
FlameshotDaemon::copyToClipboard(
url.toString(), tr("URL copied to clipboard."));
}
widget->showPostUploadDialog();
}
});
}
if (!(tasks & CR::UPLOAD)) {
emit captureTaken(capture);
}
}
void Flameshot::setExternalWidget(bool b)
{
m_haveExternalWidget = b;
}
bool Flameshot::haveExternalWidget()
{
return m_haveExternalWidget;
}
// STATIC ATTRIBUTES
Flameshot::Origin Flameshot::m_origin = Flameshot::DAEMON;