Compare commits

...

2 Commits

Author SHA1 Message Date
Gres
7bcd2c3e76 Make wayland implementation ifdef'ed 2021-04-24 12:15:57 +03:00
Gres
594fc3e665 Add wayland support 2021-04-23 21:02:40 +03:00
5 changed files with 265 additions and 7 deletions

View File

@ -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 \

View File

@ -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 );

View File

@ -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_;
}; };

View 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 {};
}

View 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