Screenshot effect: add a screenshotScreens(...) to screenshot specific screens
This commit is contained in:
parent
99bed106bf
commit
309a656e00
3 changed files with 140 additions and 39 deletions
|
@ -23,6 +23,8 @@
|
|||
#include <QMatrix4x4>
|
||||
#include <xcb/xcb_image.h>
|
||||
#include <QPoint>
|
||||
#include <QGuiApplication>
|
||||
#include <QScreen>
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <KNotification>
|
||||
|
@ -30,6 +32,8 @@
|
|||
#include <unistd.h>
|
||||
#include "../service_utils.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QStringList)
|
||||
|
||||
class ComparableQPoint : public QPoint
|
||||
{
|
||||
public:
|
||||
|
@ -66,6 +70,8 @@ const static QString s_errorInvalidAreaMsg = QStringLiteral("Invalid area reques
|
|||
const static QString s_errorInvalidScreen = QStringLiteral("org.kde.kwin.Screenshot.Error.InvalidScreen");
|
||||
const static QString s_errorInvalidScreenMsg = QStringLiteral("Invalid screen requested");
|
||||
const static QString s_dbusInterfaceName = QStringLiteral("org.kde.kwin.Screenshot");
|
||||
const static QString s_errorScreenMissing = QStringLiteral("org.kde.kwin.Screenshot.Error.ScreenMissing");
|
||||
const static QString s_errorScreenMissingMsg = QStringLiteral("Screen not found");
|
||||
|
||||
bool ScreenShotEffect::supported()
|
||||
{
|
||||
|
@ -355,39 +361,43 @@ void ScreenShotEffect::postPaintScreen()
|
|||
m_multipleOutputsRendered = m_multipleOutputsRendered.united(intersection);
|
||||
if (m_multipleOutputsRendered.boundingRect() == m_scheduledGeometry) {
|
||||
|
||||
// Recompute coordinates
|
||||
if (m_nativeSize) {
|
||||
computeCoordinatesAfterScaling();
|
||||
if (m_orderImg.isEmpty()) {
|
||||
// Recompute coordinates
|
||||
if (m_nativeSize) {
|
||||
computeCoordinatesAfterScaling();
|
||||
}
|
||||
|
||||
// find the output image size
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
QMap<ComparableQPoint, QImage>::const_iterator i;
|
||||
for (i = m_cacheOutputsImages.constBegin(); i != m_cacheOutputsImages.constEnd(); ++i) {
|
||||
const auto pos = i.key();
|
||||
const auto img = i.value();
|
||||
|
||||
width = qMax(width, pos.x() + img.width());
|
||||
height = qMax(height, pos.y() + img.height());
|
||||
}
|
||||
|
||||
QImage multipleOutputsImage = QImage(width, height, QImage::Format_ARGB32);
|
||||
|
||||
QPainter p;
|
||||
p.begin(&multipleOutputsImage);
|
||||
|
||||
// reassemble images together
|
||||
for (i = m_cacheOutputsImages.constBegin(); i != m_cacheOutputsImages.constEnd(); ++i) {
|
||||
auto pos = i.key();
|
||||
auto img = i.value();
|
||||
// disable dpr rendering, we already took care of this
|
||||
img.setDevicePixelRatio(1.0);
|
||||
p.drawImage(pos, img);
|
||||
}
|
||||
p.end();
|
||||
|
||||
sendReplyImage(multipleOutputsImage);
|
||||
} else {
|
||||
sendReplyImages();
|
||||
}
|
||||
|
||||
// find the output image size
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
QMap<ComparableQPoint, QImage>::const_iterator i;
|
||||
for (i = m_cacheOutputsImages.constBegin(); i != m_cacheOutputsImages.constEnd(); ++i) {
|
||||
const auto pos = i.key();
|
||||
const auto img = i.value();
|
||||
|
||||
width = qMax(width, pos.x() + img.width());
|
||||
height = qMax(height, pos.y() + img.height());
|
||||
}
|
||||
|
||||
QImage multipleOutputsImage = QImage(width, height, QImage::Format_ARGB32);
|
||||
|
||||
QPainter p;
|
||||
p.begin(&multipleOutputsImage);
|
||||
|
||||
// reassemble images together
|
||||
for (i = m_cacheOutputsImages.constBegin(); i != m_cacheOutputsImages.constEnd(); ++i) {
|
||||
auto pos = i.key();
|
||||
auto img = i.value();
|
||||
// disable dpr rendering, we already took care of this
|
||||
img.setDevicePixelRatio(1.0);
|
||||
p.drawImage(pos, img);
|
||||
}
|
||||
p.end();
|
||||
|
||||
sendReplyImage(multipleOutputsImage);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -411,10 +421,41 @@ void ScreenShotEffect::sendReplyImage(const QImage &img)
|
|||
close(fd);
|
||||
}
|
||||
}, m_fd, img);
|
||||
m_fd = -1;
|
||||
} else {
|
||||
QDBusConnection::sessionBus().send(m_replyMessage.createReply(saveTempImage(img)));
|
||||
}
|
||||
|
||||
clearState();
|
||||
}
|
||||
|
||||
void ScreenShotEffect::sendReplyImages()
|
||||
{
|
||||
QList<QImage> outputImages;
|
||||
for (const QPoint &pos : qAsConst(m_orderImg)) {
|
||||
auto it = m_cacheOutputsImages.find(pos);
|
||||
if (it != m_cacheOutputsImages.constEnd()) {
|
||||
outputImages.append(*it);
|
||||
}
|
||||
}
|
||||
QtConcurrent::run(
|
||||
[] (int fd, const QList<QImage> &outputImages) {
|
||||
QFile file;
|
||||
if (file.open(fd, QIODevice::WriteOnly, QFileDevice::AutoCloseHandle)) {
|
||||
QDataStream ds(&file);
|
||||
ds.setVersion(QDataStream::Qt_DefaultCompiledVersion);
|
||||
ds << outputImages;
|
||||
file.close();
|
||||
} else {
|
||||
close(fd);
|
||||
}
|
||||
}, m_fd, outputImages);
|
||||
|
||||
clearState();
|
||||
}
|
||||
|
||||
void ScreenShotEffect::clearState()
|
||||
{
|
||||
m_fd = -1;
|
||||
m_scheduledGeometry = QRect();
|
||||
m_multipleOutputsRendered = QRegion();
|
||||
m_captureCursor = false;
|
||||
|
@ -422,6 +463,7 @@ void ScreenShotEffect::sendReplyImage(const QImage &img)
|
|||
m_cacheOutputsImages.clear();
|
||||
m_cachedOutputGeometry = QRect();
|
||||
m_nativeSize = false;
|
||||
m_orderImg.clear();
|
||||
}
|
||||
|
||||
QString ScreenShotEffect::saveTempImage(const QImage &img)
|
||||
|
@ -661,6 +703,50 @@ void ScreenShotEffect::screenshotScreen(QDBusUnixFileDescriptor fd, bool capture
|
|||
);
|
||||
}
|
||||
|
||||
void ScreenShotEffect::screenshotScreens(QDBusUnixFileDescriptor fd, const QStringList &screensNames, bool captureCursor, bool shouldReturnNativeSize)
|
||||
{
|
||||
if (!checkCall()) {
|
||||
return;
|
||||
}
|
||||
m_fd = dup(fd.fileDescriptor());
|
||||
if (m_fd == -1) {
|
||||
sendErrorReply(s_errorFd, s_errorFdMsg);
|
||||
return;
|
||||
}
|
||||
m_captureCursor = captureCursor;
|
||||
m_nativeSize = shouldReturnNativeSize;
|
||||
m_orderImg = QList<QPoint>();
|
||||
m_scheduledGeometry = QRect();
|
||||
|
||||
const QList<QScreen *> screens = QGuiApplication::screens();
|
||||
|
||||
QStringList lscreensNames = screensNames;
|
||||
for (const QScreen *screen : screens) {
|
||||
const int indexName = lscreensNames.indexOf(screen->name());
|
||||
if (indexName != -1) {
|
||||
lscreensNames.removeAt(indexName);
|
||||
const auto screenGeom = screen->geometry();
|
||||
if (!screenGeom.isValid()) {
|
||||
close(m_fd);
|
||||
clearState();
|
||||
sendErrorReply(s_errorScreenMissing, s_errorScreenMissingMsg + " : " + screen->name());
|
||||
return;
|
||||
}
|
||||
m_scheduledGeometry = m_scheduledGeometry.united(screenGeom);
|
||||
m_orderImg.insert(indexName, screenGeom.topLeft());
|
||||
}
|
||||
}
|
||||
|
||||
if (!lscreensNames.isEmpty()) {
|
||||
close(m_fd);
|
||||
clearState();
|
||||
sendErrorReply(s_errorScreenMissing, s_errorScreenMissingMsg + " : " + lscreensNames.join(", "));
|
||||
return;
|
||||
}
|
||||
|
||||
effects->addRepaint(m_scheduledGeometry);
|
||||
}
|
||||
|
||||
QString ScreenShotEffect::screenshotArea(int x, int y, int width, int height, bool captureCursor)
|
||||
{
|
||||
if (!checkCall()) {
|
||||
|
|
|
@ -22,6 +22,11 @@ class ComparableQPoint;
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
/**
|
||||
* The screenshot effet allows to takes screenshot, by window, area, screen, etc...
|
||||
*
|
||||
* A using application must have "org.kde.kwin.Screenshot" in its X-KDE-DBUS-Restricted-Interfaces application service file field.
|
||||
*/
|
||||
class ScreenShotEffect : public Effect, protected QDBusContext
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -77,10 +82,7 @@ public Q_SLOTS:
|
|||
*/
|
||||
Q_SCRIPTABLE QString screenshotFullscreen(bool captureCursor = false);
|
||||
/**
|
||||
* Starts an interactive screenshot session.
|
||||
*
|
||||
* The user is asked to confirm that a screenshot is taken by having to actively
|
||||
* click and giving the possibility to cancel.
|
||||
* Takes a full screen screenshot in a one file format.
|
||||
*
|
||||
* Once the screenshot is taken it gets saved into the @p fd passed to the
|
||||
* method. It is intended to be used with a pipe, so that the invoking side can just
|
||||
|
@ -91,6 +93,16 @@ public Q_SLOTS:
|
|||
* @param shouldReturnNativeSize Whether to return an image according to the virtualGeometry, or according to pixel on screen size
|
||||
*/
|
||||
Q_SCRIPTABLE void screenshotFullscreen(QDBusUnixFileDescriptor fd, bool captureCursor = false, bool shouldReturnNativeSize = false);
|
||||
/**
|
||||
* Take a screenshot of the passed screens and return a QList<QImage> in the fd response,
|
||||
* an image for each screen in pixel-on-screen size when shouldReturnNativeSize is passed, or converted to using logicale size if not
|
||||
*
|
||||
* @param fd
|
||||
* @param screensNames the names of the screens whose screenshot to return
|
||||
* @param captureCursor
|
||||
* @param shouldReturnNativeSize
|
||||
*/
|
||||
Q_SCRIPTABLE void screenshotScreens(QDBusUnixFileDescriptor fd, const QStringList &screensNames, bool captureCursor = false, bool shouldReturnNativeSize = false);
|
||||
/**
|
||||
* Saves a screenshot of the screen identified by @p screen into a file and returns the path to the file.
|
||||
* Functionality requires hardware support, if not available a null string is returned.
|
||||
|
@ -135,6 +147,8 @@ private:
|
|||
QImage blitScreenshot(const QRect &geometry, const qreal scale = 1.0);
|
||||
QString saveTempImage(const QImage &img);
|
||||
void sendReplyImage(const QImage &img);
|
||||
void sendReplyImages();
|
||||
void clearState();
|
||||
enum class InfoMessageMode {
|
||||
Window,
|
||||
Screen
|
||||
|
@ -152,6 +166,7 @@ private:
|
|||
QRect m_cachedOutputGeometry;
|
||||
QRegion m_multipleOutputsRendered;
|
||||
QMap<ComparableQPoint, QImage> m_cacheOutputsImages;
|
||||
QList<QPoint> m_orderImg;
|
||||
bool m_captureCursor = false;
|
||||
bool m_nativeSize = false;
|
||||
enum class WindowMode {
|
||||
|
|
|
@ -50,12 +50,12 @@ static QStringList fetchProcessServiceField(const QString &executablePath, const
|
|||
return fieldValues;
|
||||
}
|
||||
|
||||
static QStringList fetchRequestedInterfaces(const QString &executablePath)
|
||||
static inline QStringList fetchRequestedInterfaces(const QString &executablePath)
|
||||
{
|
||||
return fetchProcessServiceField(executablePath, s_waylandInterfaceName);
|
||||
}
|
||||
|
||||
static QStringList fetchRestrictedDBusInterfacesFromPid(const uint pid)
|
||||
static inline QStringList fetchRestrictedDBusInterfacesFromPid(const uint pid)
|
||||
{
|
||||
const auto executablePath = QFileInfo(QStringLiteral("/proc/%1/exe").arg(pid)).symLinkTarget();
|
||||
return fetchProcessServiceField(executablePath, s_dbusRestrictedInterfaceName);
|
||||
|
|
Loading…
Reference in a new issue