Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7bcd2c3e76 | ||
![]() |
594fc3e665 |
@ -14,7 +14,7 @@ win32{
|
|||||||
LIBS += -lUser32
|
LIBS += -lUser32
|
||||||
}
|
}
|
||||||
linux{
|
linux{
|
||||||
QT += x11extras
|
QT += x11extras dbus
|
||||||
LIBS += -lX11
|
LIBS += -lX11
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +37,7 @@ HEADERS += \
|
|||||||
src/capture/captureareaeditor.h \
|
src/capture/captureareaeditor.h \
|
||||||
src/capture/captureareaselector.h \
|
src/capture/captureareaselector.h \
|
||||||
src/capture/capturer.h \
|
src/capture/capturer.h \
|
||||||
|
src/capture/waylandcapturer.h \
|
||||||
src/commonmodels.h \
|
src/commonmodels.h \
|
||||||
src/correct/corrector.h \
|
src/correct/corrector.h \
|
||||||
src/correct/correctorworker.h \
|
src/correct/correctorworker.h \
|
||||||
@ -73,6 +74,7 @@ SOURCES += \
|
|||||||
src/capture/captureareaeditor.cpp \
|
src/capture/captureareaeditor.cpp \
|
||||||
src/capture/captureareaselector.cpp \
|
src/capture/captureareaselector.cpp \
|
||||||
src/capture/capturer.cpp \
|
src/capture/capturer.cpp \
|
||||||
|
src/capture/waylandcapturer.cpp \
|
||||||
src/commonmodels.cpp \
|
src/commonmodels.cpp \
|
||||||
src/correct/corrector.cpp \
|
src/correct/corrector.cpp \
|
||||||
src/correct/correctorworker.cpp \
|
src/correct/correctorworker.cpp \
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "task.h"
|
#include "waylandcapturer.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
@ -16,6 +16,7 @@ Capturer::Capturer(Manager &manager, const Settings &settings,
|
|||||||
, settings_(settings)
|
, settings_(settings)
|
||||||
, selector_(std::make_unique<CaptureAreaSelector>(*this, settings_, models,
|
, selector_(std::make_unique<CaptureAreaSelector>(*this, settings_, models,
|
||||||
pixmap_))
|
pixmap_))
|
||||||
|
, wayland_(WaylandCapturer::create())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,11 +58,20 @@ void Capturer::updatePixmap()
|
|||||||
QPixmap combined(rect.size());
|
QPixmap combined(rect.size());
|
||||||
QPainter p(&combined);
|
QPainter p(&combined);
|
||||||
|
|
||||||
for (const auto screen : screens) {
|
if (!wayland_) {
|
||||||
const auto geometry = screen->geometry();
|
for (const auto screen : screens) {
|
||||||
const auto pixmap =
|
const auto geometry = screen->geometry();
|
||||||
screen->grabWindow(0, 0, 0, geometry.width(), geometry.height());
|
const auto pixmap =
|
||||||
p.drawPixmap(geometry, pixmap);
|
screen->grabWindow(0, 0, 0, geometry.width(), geometry.height());
|
||||||
|
p.drawPixmap(geometry, pixmap);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto pix = wayland_->grab();
|
||||||
|
if (!pix.isNull()) {
|
||||||
|
p.drawPixmap(0, 0, pix);
|
||||||
|
} else {
|
||||||
|
combined.fill(Qt::black);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SOFT_ASSERT(selector_, return );
|
SOFT_ASSERT(selector_, return );
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
|
||||||
|
class WaylandCapturer;
|
||||||
|
|
||||||
class Capturer
|
class Capturer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -27,4 +29,5 @@ private:
|
|||||||
const Settings &settings_;
|
const Settings &settings_;
|
||||||
QPixmap pixmap_;
|
QPixmap pixmap_;
|
||||||
std::unique_ptr<CaptureAreaSelector> selector_;
|
std::unique_ptr<CaptureAreaSelector> selector_;
|
||||||
|
std::unique_ptr<WaylandCapturer> wayland_;
|
||||||
};
|
};
|
||||||
|
197
src/capture/waylandcapturer.cpp
Normal file
197
src/capture/waylandcapturer.cpp
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
#include "waylandcapturer.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDBusConnection>
|
||||||
|
#include <QDBusPendingReply>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
QString desktopFile()
|
||||||
|
{
|
||||||
|
auto name = QCoreApplication::applicationName().toLower() +
|
||||||
|
QLatin1String("-screenshot-permission");
|
||||||
|
name.remove(QLatin1Char(' '));
|
||||||
|
const auto result = QDir::homePath() +
|
||||||
|
QLatin1String("/.local/share/applications/") + name +
|
||||||
|
QLatin1String(".desktop");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeDesktop()
|
||||||
|
{
|
||||||
|
QFile::remove(desktopFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeDesktop()
|
||||||
|
{
|
||||||
|
QFile f(desktopFile());
|
||||||
|
if (!f.open(QFile::WriteOnly))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto contents = QString(R"([Desktop Entry]
|
||||||
|
Name=%1-screen-permission
|
||||||
|
Exec=%2
|
||||||
|
X-KDE-DBUS-Restricted-Interfaces=org.kde.kwin.Screenshot
|
||||||
|
)")
|
||||||
|
.arg(QCoreApplication::applicationName(),
|
||||||
|
QCoreApplication::applicationFilePath());
|
||||||
|
f.write(contents.toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool WaylandCapturerImpl::isWayland()
|
||||||
|
{
|
||||||
|
return qEnvironmentVariable("XDG_SESSION_TYPE").toLower() ==
|
||||||
|
QStringLiteral("wayland");
|
||||||
|
}
|
||||||
|
|
||||||
|
WaylandCapturerImpl::Method WaylandCapturerImpl::getMethod()
|
||||||
|
{
|
||||||
|
auto de = qEnvironmentVariable("XDG_CURRENT_DESKTOP").toLower();
|
||||||
|
if (de == QLatin1String("kde")) {
|
||||||
|
return Method::Kde;
|
||||||
|
} else if (de.endsWith(QLatin1String("gnome"))) {
|
||||||
|
return Method::Gnome;
|
||||||
|
}
|
||||||
|
return Method::Freedesktop;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaylandCapturerImpl::WaylandCapturerImpl()
|
||||||
|
: method_(getMethod())
|
||||||
|
{
|
||||||
|
if (method_ == Method::Kde)
|
||||||
|
writeDesktop();
|
||||||
|
}
|
||||||
|
|
||||||
|
WaylandCapturerImpl::~WaylandCapturerImpl()
|
||||||
|
{
|
||||||
|
if (method_ == Method::Kde)
|
||||||
|
removeDesktop();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap WaylandCapturerImpl::grab()
|
||||||
|
{
|
||||||
|
switch (method_) {
|
||||||
|
case Method::Gnome: return grabGnome();
|
||||||
|
case Method::Kde: return grabKde();
|
||||||
|
case Method::Freedesktop: return grabFreedesktop();
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap WaylandCapturerImpl::grabKde()
|
||||||
|
{
|
||||||
|
auto request = QDBusMessage::createMethodCall(
|
||||||
|
QStringLiteral("org.kde.KWin"), QStringLiteral("/Screenshot"),
|
||||||
|
QStringLiteral("org.kde.kwin.Screenshot"),
|
||||||
|
QStringLiteral("screenshotFullscreen"));
|
||||||
|
|
||||||
|
request << false;
|
||||||
|
|
||||||
|
auto reply = QDBusConnection::sessionBus().call(request);
|
||||||
|
const auto args = reply.arguments();
|
||||||
|
if (reply.type() == QDBusMessage::ErrorMessage || args.isEmpty()) {
|
||||||
|
LERROR() << "kde capture error" << reply;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto fileName = args.first().toString();
|
||||||
|
auto result = QPixmap(fileName);
|
||||||
|
QFile::remove(fileName);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap WaylandCapturerImpl::grabGnome()
|
||||||
|
{
|
||||||
|
auto request = QDBusMessage::createMethodCall(
|
||||||
|
QStringLiteral("org.gnome.Shell.Screenshot"),
|
||||||
|
QStringLiteral("/org/gnome/Shell/Screenshot"),
|
||||||
|
QStringLiteral("org.gnome.Shell.Screenshot"),
|
||||||
|
QStringLiteral("Screenshot"));
|
||||||
|
|
||||||
|
QTemporaryFile f;
|
||||||
|
if (!f.open()) {
|
||||||
|
LERROR() << "failed to create temp file" << f.errorString();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
request << false << false << f.fileName();
|
||||||
|
|
||||||
|
auto reply = QDBusConnection::sessionBus().call(request);
|
||||||
|
const auto args = reply.arguments();
|
||||||
|
if (reply.type() == QDBusMessage::ErrorMessage || args.isEmpty()) {
|
||||||
|
LERROR() << "gnome capture error" << reply;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!args.first().toBool()) {
|
||||||
|
LERROR() << "gnome capture error";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return QPixmap(f.fileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap WaylandCapturerImpl::grabFreedesktop()
|
||||||
|
{
|
||||||
|
auto request = QDBusMessage::createMethodCall(
|
||||||
|
QStringLiteral("org.freedesktop.portal.Desktop"),
|
||||||
|
QStringLiteral("/org/freedesktop/portal/desktop"),
|
||||||
|
QStringLiteral("org.freedesktop.portal.Screenshot"),
|
||||||
|
QStringLiteral("Screenshot"));
|
||||||
|
|
||||||
|
request << QStringLiteral("") << QVariantMap{};
|
||||||
|
|
||||||
|
auto reply = QDBusConnection::sessionBus().call(request);
|
||||||
|
const auto args = reply.arguments();
|
||||||
|
if (reply.type() == QDBusMessage::ErrorMessage || args.isEmpty()) {
|
||||||
|
LERROR() << "freedesktop capture error" << reply;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto handleArg = args.first();
|
||||||
|
if (!handleArg.canConvert<QDBusObjectPath>()) {
|
||||||
|
LERROR() << "wrong type in freedesktop ansert" << handleArg.userType();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto handle = handleArg.value<QDBusObjectPath>().path();
|
||||||
|
|
||||||
|
QDBusConnection::sessionBus().connect(
|
||||||
|
QStringLiteral("org.freedesktop.portal.Desktop"), handle,
|
||||||
|
QStringLiteral("org.freedesktop.portal.Request"),
|
||||||
|
QStringLiteral("Response"), this, SLOT(getScreen(uint, QVariantMap)));
|
||||||
|
|
||||||
|
loop_.exec();
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaylandCapturerImpl::parseFreedesktopResult(uint response,
|
||||||
|
const QVariantMap &results)
|
||||||
|
{
|
||||||
|
if (response == 0) {
|
||||||
|
const auto name = QUrl(results["uri"].toString()).toLocalFile();
|
||||||
|
result_.load(name);
|
||||||
|
QFile::remove(name);
|
||||||
|
}
|
||||||
|
loop_.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::unique_ptr<WaylandCapturer> WaylandCapturer::create()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
if (WaylandCapturerImpl::isWayland())
|
||||||
|
return std::make_unique<WaylandCapturerImpl>();
|
||||||
|
#endif
|
||||||
|
return {};
|
||||||
|
}
|
46
src/capture/waylandcapturer.h
Normal file
46
src/capture/waylandcapturer.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
|
class WaylandCapturer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~WaylandCapturer() = default;
|
||||||
|
|
||||||
|
virtual QPixmap grab() = 0;
|
||||||
|
|
||||||
|
static std::unique_ptr<WaylandCapturer> create();
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
|
||||||
|
class WaylandCapturerImpl : public QObject, public WaylandCapturer
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
WaylandCapturerImpl();
|
||||||
|
~WaylandCapturerImpl();
|
||||||
|
|
||||||
|
static bool isWayland();
|
||||||
|
|
||||||
|
QPixmap grab() override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void parseFreedesktopResult(uint response, const QVariantMap &results);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class Method { Gnome, Kde, Freedesktop };
|
||||||
|
static Method getMethod();
|
||||||
|
|
||||||
|
QPixmap grabKde();
|
||||||
|
QPixmap grabGnome();
|
||||||
|
QPixmap grabFreedesktop();
|
||||||
|
|
||||||
|
Method method_;
|
||||||
|
QEventLoop loop_;
|
||||||
|
QPixmap result_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user