Commit 1fb44b5b authored by Vlad Zahorodnii's avatar Vlad Zahorodnii
Browse files

effects/screenshot: Prepare for versioned dbus interface

On Wayland, when the compositor sends a screenshot to the requesting
app, it encodes the screenshot as a PNG image and sends the encoded data
over the pipe. The requesting app (Spectacle) then needs to decode the
data.

The issue is that encoding PNG images is not cheap. This is the main
reason why Spectacle is shown with a huge delay after you press the
PrtScr key.

In order to fix the latency issue, we need to transfer raw image data.
Unfortunately, the current dbus api of the screenshot is too cluttered
and the best option at the moment is to start with a clean slate.

This change prepares the screenshot effect for versioned dbus interface.
Most of existing dbus logic was moved out in a separate class. In order
to schedule screen shots, the screenshot effect got some new API.

    QFuture<QImage> scheduleScreenShot(window, flags)
    QFuture<QImage> scheduleScreenShot(area, flags)
    QFuture<QImage> scheduleScreenShot(screen, flags)

If a dbus interface needs to take a screenshot, it needs to call one of
the overloaded scheduleScreenShot() functions. Every overload returns a
QFuture object that can be used for querying the result.

This change also introduces "sink" and "source" objects in the dbus api
implementation to simplify handling of QFuture objects.

Note that the QFutureInterface is undocumented, so if you use it, you do
it on your own risk. However, since Qt 5.15 is frozen for non-commercial
use and some other Plasma projects already use QFutureInterface, this
is not a big concern. For what it's worth, in Qt 6, there's the QPromise
class, which is equivalent to the QFutureInterface class.

CCBUG: 433776
CCBUG: 430869
parent 38996d97
......@@ -5,4 +5,5 @@
set(kwin4_effect_builtins_sources ${kwin4_effect_builtins_sources}
../service_utils.cpp
screenshot/screenshot.cpp
screenshot/screenshotdbusinterface1.cpp
)
This diff is collapsed.
......@@ -3,6 +3,7 @@
This file is part of the KDE project.
SPDX-FileCopyrightText: 2010 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -11,175 +12,95 @@
#define KWIN_SCREENSHOT_H
#include <kwineffects.h>
#include <QDBusContext>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusUnixFileDescriptor>
#include <QObject>
#include <QFuture>
#include <QFutureInterface>
#include <QImage>
#include <QObject>
class ComparableQPoint;
namespace KWin
{
/**
* This enum type is used to specify how a screenshot needs to be taken.
*/
enum ScreenShotFlag {
ScreenShotIncludeDecoration = 0x1, ///< Include window titlebar and borders
ScreenShotIncludeCursor = 0x2, ///< Include the cursor
ScreenShotNativeResolution = 0x4, ///< Take the screenshot at the native resolution
};
Q_DECLARE_FLAGS(ScreenShotFlags, ScreenShotFlag)
class ScreenShotDBusInterface1;
struct ScreenShotWindowData;
struct ScreenShotAreaData;
struct ScreenShotScreenData;
/**
* 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
class ScreenShotEffect : public Effect
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Screenshot")
public:
enum ScreenShotType {
INCLUDE_DECORATION = 1 << 0,
INCLUDE_CURSOR = 1 << 1
};
ScreenShotEffect();
~ScreenShotEffect() override;
void paintScreen(int mask, const QRegion &region, ScreenPaintData &data) override;
void postPaintScreen() override;
bool isActive() const override;
int requestedEffectChainPosition() const override {
return 50;
}
static bool supported();
static void convertFromGLImage(QImage &img, int w, int h);
void scheduleScreenshotWindowUnderCursor();
public Q_SLOTS:
Q_SCRIPTABLE void screenshotForWindow(qulonglong winid, int mask = 0);
/**
* Starts an interactive window screenshot session. The user can select a window to
* screenshot.
*
* Once the window is selected the screenshot is saved into a file and the path gets
* returned to the DBus peer.
*
* @param mask The mask for what to include in the screenshot
*/
Q_SCRIPTABLE QString interactive(int mask = 0);
/**
* Starts an interactive window screenshot session. The user can select a window to
* screenshot.
*
* Once the window is selected the screenshot is 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
* read from the pipe. The image gets written into the fd using a QDataStream.
*
* @param fd File descriptor into which the screenshot should be saved
* @param mask The mask for what to include in the screenshot
*/
Q_SCRIPTABLE void interactive(QDBusUnixFileDescriptor fd, int mask = 0);
Q_SCRIPTABLE void screenshotWindowUnderCursor(int mask = 0);
/**
* Saves a screenshot of all screen into a file and returns the path to the file.
* Functionality requires hardware support, if not available a null string is returned.
* @param captureCursor Whether to include the cursor in the image
* @returns Path to stored screenshot, or null string in failure case.
* Schedules a screenshot of the given @a screen. The returned QFuture can be used to query
* the image data. If the screen is removed before the screenshot is taken, the future will
* be cancelled.
*/
Q_SCRIPTABLE QString screenshotFullscreen(bool captureCursor = false);
/**
* 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
* read from the pipe. The image gets written into the fd using a QDataStream.
*
* @param fd File descriptor into which the screenshot should be saved
* @param captureCursor Whether to include the mouse cursor
* @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.
* @param screen Number of screen as numbered by QDesktopWidget
* @param captureCursor Whether to include the cursor in the image
* @returns Path to stored screenshot, or null string in failure case.
*/
Q_SCRIPTABLE QString screenshotScreen(int screen, bool captureCursor = false);
QFuture<QImage> scheduleScreenShot(EffectScreen *screen, ScreenShotFlags flags = {});
/**
* Starts an interactive screenshot of a screen session.
*
* The user is asked to select the screen to screenshot.
*
* 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
* read from the pipe. The image gets written into the fd using a QDataStream.
*
* @param fd File descriptor into which the screenshot should be saved
* @param captureCursor Whether to include the mouse cursor
* Schedules a screenshot of the given @a area. The returned QFuture can be used to query the
* image data.
*/
Q_SCRIPTABLE void screenshotScreen(QDBusUnixFileDescriptor fd, bool captureCursor = false);
QFuture<QImage> scheduleScreenShot(const QRect &area, ScreenShotFlags flags = {});
/**
* Saves a screenshot of the selected geometry into a file and returns the path to the file.
* Functionality requires hardware support, if not available a null string is returned.
* @param x Left upper x coord of region
* @param y Left upper y coord of region
* @param width Width of the region to screenshot
* @param height Height of the region to screenshot
* @param captureCursor Whether to include the cursor in the image
* @returns Path to stored screenshot, or null string in failure case.
* Schedules a screenshot of the given @a window. The returned QFuture can be used to query
* the image data. If the window is removed before the screenshot is taken, the future will
* be cancelled.
*/
Q_SCRIPTABLE QString screenshotArea(int x, int y, int width, int height, bool captureCursor = false);
QFuture<QImage> scheduleScreenShot(EffectWindow *window, ScreenShotFlags flags = {});
void paintScreen(int mask, const QRegion &region, ScreenPaintData &data) override;
void postPaintScreen() override;
bool isActive() const override;
int requestedEffectChainPosition() const override;
Q_SIGNALS:
Q_SCRIPTABLE void screenshotCreated(qulonglong handle);
static bool supported();
private Q_SLOTS:
void windowClosed( KWin::EffectWindow* w );
void handleWindowClosed(EffectWindow *window);
void handleScreenAdded();
void handleScreenRemoved(EffectScreen *screen);
private:
void takeScreenShot(ScreenShotWindowData *screenshot);
bool takeScreenShot(ScreenShotAreaData *screenshot);
bool takeScreenShot(ScreenShotScreenData *screenshot);
void cancelWindowScreenShots();
void cancelAreaScreenShots();
void cancelScreenScreenShots();
void grabPointerImage(QImage& snapshot, int offsetx, int offsety);
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
};
void showInfoMessage(InfoMessageMode mode);
void hideInfoMessage();
bool isTakingScreenshot() const;
void computeCoordinatesAfterScaling();
bool checkCall() const;
EffectWindow *m_scheduledScreenshot;
ScreenShotType m_type;
QRect m_scheduledGeometry;
QDBusMessage m_replyMessage;
QRegion m_multipleOutputsRendered;
QMap<ComparableQPoint, QImage> m_cacheOutputsImages;
QList<QPoint> m_orderImg;
bool m_captureCursor = false;
bool m_nativeSize = false;
enum class WindowMode {
NoCapture,
Xpixmap,
File,
FileDescriptor
};
WindowMode m_windowMode = WindowMode::NoCapture;
int m_fd = -1;
QVector<ScreenShotWindowData> m_windowScreenShots;
QVector<ScreenShotAreaData> m_areaScreenShots;
QVector<ScreenShotScreenData> m_screenScreenShots;
QScopedPointer<ScreenShotDBusInterface1> m_dbusInterface1;
EffectScreen *m_paintedScreen = nullptr;
};
} // namespace
Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::ScreenShotFlags)
#endif // KWIN_SCREENSHOT_H
This diff is collapsed.
/*
SPDX-FileCopyrightText: 2010 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "screenshot.h"
#include <QDBusContext>
#include <QDBusMessage>
#include <QDBusUnixFileDescriptor>
namespace KWin
{
class ScreenShotSink1;
class ScreenShotSource1;
/**
* The ScreenshotDBusInterface1 class provides a d-bus api to take screenshots. This implements
* the org.kde.kwin.Screenshot interface.
*
* An application that requests a screenshot must have "org.kde.kwin.Screenshot" listed in its
* X-KDE-DBUS-Restricted-Interfaces desktop file field.
*/
class ScreenShotDBusInterface1 : public QObject, protected QDBusContext
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Screenshot")
public:
explicit ScreenShotDBusInterface1(ScreenShotEffect *effect, QObject *parent = nullptr);
~ScreenShotDBusInterface1() override;
Q_SIGNALS:
/**
* This signal is emitted when a screenshot has been written in an Xpixmap with the
* specified @a handle.
*/
Q_SCRIPTABLE void screenshotCreated(qulonglong handle);
public Q_SLOTS:
Q_SCRIPTABLE void screenshotForWindow(qulonglong winid, int mask = 0);
/**
* Starts an interactive window screenshot session. The user can select a window to
* screenshot.
*
* Once the window is selected the screenshot is saved into a file and the path gets
* returned to the DBus peer.
*
* @param mask The mask for what to include in the screenshot
*/
Q_SCRIPTABLE QString interactive(int mask = 0);
/**
* Starts an interactive window screenshot session. The user can select a window to
* screenshot.
*
* Once the window is selected the screenshot is 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
* read from the pipe. The image gets written into the fd using a QDataStream.
*
* @param fd File descriptor into which the screenshot should be saved
* @param mask The mask for what to include in the screenshot
*/
Q_SCRIPTABLE void interactive(QDBusUnixFileDescriptor fd, int mask = 0);
Q_SCRIPTABLE void screenshotWindowUnderCursor(int mask = 0);
/**
* Saves a screenshot of all screen into a file and returns the path to the file.
* Functionality requires hardware support, if not available a null string is returned.
* @param captureCursor Whether to include the cursor in the image
* @returns Path to stored screenshot, or null string in failure case.
*/
Q_SCRIPTABLE QString screenshotFullscreen(bool captureCursor = false);
/**
* 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
* read from the pipe. The image gets written into the fd using a QDataStream.
*
* @param fd File descriptor into which the screenshot should be saved
* @param captureCursor Whether to include the mouse cursor
* @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.
* @param screen Number of screen as numbered by QDesktopWidget
* @param captureCursor Whether to include the cursor in the image
* @returns Path to stored screenshot, or null string in failure case.
*/
Q_SCRIPTABLE QString screenshotScreen(int screen, bool captureCursor = false);
/**
* Starts an interactive screenshot of a screen session.
*
* The user is asked to select the screen to screenshot.
*
* 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
* read from the pipe. The image gets written into the fd using a QDataStream.
*
* @param fd File descriptor into which the screenshot should be saved
* @param captureCursor Whether to include the mouse cursor
*/
Q_SCRIPTABLE void screenshotScreen(QDBusUnixFileDescriptor fd, bool captureCursor = false);
/**
* Saves a screenshot of the selected geometry into a file and returns the path to the file.
* Functionality requires hardware support, if not available a null string is returned.
* @param x Left upper x coord of region
* @param y Left upper y coord of region
* @param width Width of the region to screenshot
* @param height Height of the region to screenshot
* @param captureCursor Whether to include the cursor in the image
* @returns Path to stored screenshot, or null string in failure case.
*/
Q_SCRIPTABLE QString screenshotArea(int x, int y, int width, int height, bool captureCursor = false);
private Q_SLOTS:
void handleSourceCompleted();
void handleSourceCancelled();
private:
enum class InfoMessageMode { Window, Screen, };
void takeScreenShot(EffectScreen *screen, ScreenShotFlags flags, ScreenShotSink1 *sink);
void takeScreenShot(const QList<EffectScreen *> &screens, ScreenShotFlags flags, ScreenShotSink1 *sink);
void takeScreenShot(const QRect &area, ScreenShotFlags flags, ScreenShotSink1 *sink);
void takeScreenShot(EffectWindow *window, ScreenShotFlags flags, ScreenShotSink1 *sink);
void bind(ScreenShotSink1 *sink, ScreenShotSource1 *source);
bool checkCall() const;
bool isTakingScreenshot() const;
void showInfoMessage(InfoMessageMode mode);
void hideInfoMessage();
ScreenShotEffect *m_effect;
QScopedPointer<ScreenShotSink1> m_sink;
QScopedPointer<ScreenShotSource1> m_source;
};
} // namespace KWin
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment