Files
flameshot/src/utils/screengrabber.cpp
2022-04-04 19:34:12 -05:00

187 lines
5.7 KiB
C++

// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors
#include "screengrabber.h"
#include "abstractlogger.h"
#include "src/core/qguiappcurrentscreen.h"
#include "src/utils/filenamehandler.h"
#include "src/utils/systemnotification.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QGuiApplication>
#include <QPixmap>
#include <QScreen>
#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX)
#include "request.h"
#include <QDBusInterface>
#include <QDBusReply>
#include <QDir>
#include <QUrl>
#include <QUuid>
#endif
ScreenGrabber::ScreenGrabber(QObject* parent)
: QObject(parent)
{}
void ScreenGrabber::freeDesktopPortal(bool& ok, QPixmap& res)
{
#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX)
QDBusInterface screenshotInterface(
QStringLiteral("org.freedesktop.portal.Desktop"),
QStringLiteral("/org/freedesktop/portal/desktop"),
QStringLiteral("org.freedesktop.portal.Screenshot"));
// unique token
QString token =
QUuid::createUuid().toString().remove('-').remove('{').remove('}');
// premake interface
auto* request = new OrgFreedesktopPortalRequestInterface(
QStringLiteral("org.freedesktop.portal.Desktop"),
"/org/freedesktop/portal/desktop/request/" +
QDBusConnection::sessionBus().baseService().remove(':').replace('.',
'_') +
"/" + token,
QDBusConnection::sessionBus(),
this);
QEventLoop loop;
const auto gotSignal = [&res, &loop](uint status, const QVariantMap& map) {
if (status == 0) {
// Parse this as URI to handle unicode properly
QUrl uri = map.value("uri").toString();
QString uriString = uri.toLocalFile();
res = QPixmap(uriString);
res.setDevicePixelRatio(qApp->devicePixelRatio());
QFile imgFile(uriString);
imgFile.remove();
}
loop.quit();
};
// prevent racy situations and listen before calling screenshot
QMetaObject::Connection conn = QObject::connect(
request, &org::freedesktop::portal::Request::Response, gotSignal);
screenshotInterface.call(
QStringLiteral("Screenshot"),
"",
QMap<QString, QVariant>({ { "handle_token", QVariant(token) },
{ "interactive", QVariant(false) } }));
loop.exec();
QObject::disconnect(conn);
request->Close().waitForFinished();
request->deleteLater();
if (res.isNull()) {
ok = false;
}
#endif
}
QPixmap ScreenGrabber::grabEntireDesktop(bool& ok)
{
ok = true;
#if defined(Q_OS_MACOS)
QScreen* currentScreen = QGuiAppCurrentScreen().currentScreen();
QPixmap screenPixmap(
currentScreen->grabWindow(QApplication::desktop()->winId(),
currentScreen->geometry().x(),
currentScreen->geometry().y(),
currentScreen->geometry().width(),
currentScreen->geometry().height()));
screenPixmap.setDevicePixelRatio(currentScreen->devicePixelRatio());
return screenPixmap;
#elif defined(Q_OS_LINUX) || defined(Q_OS_UNIX)
if (m_info.waylandDetected()) {
QPixmap res;
// handle screenshot based on DE
switch (m_info.windowManager()) {
case DesktopInfo::GNOME:
case DesktopInfo::KDE:
case DesktopInfo::SWAY: {
freeDesktopPortal(ok, res);
break;
}
default:
ok = false;
break;
}
if (!ok) {
AbstractLogger::error() << tr("Unable to capture screen");
}
return res;
}
#endif
#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX) || defined(Q_OS_WIN)
QRect geometry = desktopGeometry();
QPixmap p(QApplication::primaryScreen()->grabWindow(
QApplication::desktop()->winId(),
geometry.x(),
geometry.y(),
geometry.width(),
geometry.height()));
auto screenNumber = QApplication::desktop()->screenNumber();
QScreen* screen = QApplication::screens()[screenNumber];
p.setDevicePixelRatio(screen->devicePixelRatio());
return p;
#endif
}
QRect ScreenGrabber::screenGeometry(QScreen* screen)
{
QPixmap p;
QRect geometry;
if (m_info.waylandDetected()) {
QPoint topLeft(0, 0);
#ifdef Q_OS_WIN
for (QScreen* const screen : QGuiApplication::screens()) {
QPoint topLeftScreen = screen->geometry().topLeft();
if (topLeft.x() > topLeftScreen.x() ||
topLeft.y() > topLeftScreen.y()) {
topLeft = topLeftScreen;
}
}
#endif
geometry = screen->geometry();
geometry.moveTo(geometry.topLeft() - topLeft);
} else {
QScreen* currentScreen = QGuiAppCurrentScreen().currentScreen();
geometry = currentScreen->geometry();
}
return geometry;
}
QPixmap ScreenGrabber::grabScreen(QScreen* screen, bool& ok)
{
QPixmap p;
QRect geometry = screenGeometry(screen);
if (m_info.waylandDetected()) {
p = grabEntireDesktop(ok);
if (ok) {
return p.copy(geometry);
}
} else {
ok = true;
return screen->grabWindow(
0, geometry.x(), geometry.y(), geometry.width(), geometry.height());
}
return p;
}
QRect ScreenGrabber::desktopGeometry()
{
QRect geometry;
for (QScreen* const screen : QGuiApplication::screens()) {
QRect scrRect = screen->geometry();
scrRect.moveTo(scrRect.x() / screen->devicePixelRatio(),
scrRect.y() / screen->devicePixelRatio());
geometry = geometry.united(scrRect);
}
return geometry;
}