File screengrabber.cpp#
File List > src > utils > screengrabber.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 "screengrabber.h"
#include "abstractlogger.h"
#include "src/core/qguiappcurrentscreen.h"
#include "src/utils/confighandler.h"
#include "src/utils/filenamehandler.h"
#include "src/utils/systemnotification.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QGuiApplication>
#include <QPixmap>
#include <QProcess>
#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::generalGrimScreenshot(bool& ok, QPixmap& res)
{
#ifdef USE_WAYLAND_GRIM
#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX)
QProcess Process;
QString program = "grim";
QStringList arguments;
arguments << "-";
Process.start(program, arguments);
if (Process.waitForFinished()) {
res.loadFromData(Process.readAll());
ok = true;
} else {
ok = false;
AbstractLogger::error()
<< tr("The universal wayland screen capture adapter requires Grim as "
"the screen capture component of wayland. If the screen "
"capture component is missing, please install it!");
}
#endif
#endif
}
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:
freeDesktopPortal(ok, res);
break;
case DesktopInfo::QTILE:
case DesktopInfo::SWAY:
case DesktopInfo::HYPRLAND:
case DesktopInfo::OTHER: {
#ifndef USE_WAYLAND_GRIM
AbstractLogger::warning() << tr(
"If the USE_WAYLAND_GRIM option is not activated, the dbus "
"protocol will be used. It should be noted that using the "
"dbus protocol under wayland is not recommended. It is "
"recommended to recompile with the USE_WAYLAND_GRIM flag to "
"activate the grim-based general wayland screenshot adapter");
freeDesktopPortal(ok, res);
#else
if (!ConfigHandler().disabledGrimWarning()) {
AbstractLogger::warning() << tr(
"grim's screenshot component is implemented based on "
"wlroots, it may not be used in GNOME or similar "
"desktop environments");
}
generalGrimScreenshot(ok, res);
#endif
break;
}
default:
ok = false;
AbstractLogger::error()
<< tr("Unable to detect desktop environment (GNOME? KDE? "
"Qile? Sway? ...)");
AbstractLogger::error()
<< tr("Hint: try setting the XDG_CURRENT_DESKTOP environment "
"variable.");
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(QApplication::desktop()->winId(),
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;
}