libkwineffects: Introduce EffectScreen

This provides the compositor a way to indicate what output is being
rendered. The effects such as the screenshot can check the provided
screen object in order to function as expected.
This commit is contained in:
Vlad Zahorodnii 2021-03-01 11:45:52 +02:00
parent b3e7031893
commit 38996d9725
14 changed files with 212 additions and 69 deletions

View file

@ -273,6 +273,21 @@ public:
KWin::SessionState sessionState() const override {
return KWin::SessionState::Normal;
}
QList<KWin::EffectScreen *> screens() const override {
return {};
}
KWin::EffectScreen *screenAt(const QPoint &point) const override {
Q_UNUSED(point)
return nullptr;
}
KWin::EffectScreen *findScreen(const QString &name) const override {
Q_UNUSED(name)
return nullptr;
}
KWin::EffectScreen *findScreen(int screenId) const override {
Q_UNUSED(screenId)
return nullptr;
}
private:
bool m_animationsSuported = true;

View file

@ -47,12 +47,12 @@ void TestScreenPaintData::testCtor()
QCOMPARE(data.rotationAngle(), 0.0);
QCOMPARE(data.rotationOrigin(), QVector3D());
QCOMPARE(data.rotationAxis(), QVector3D(0.0, 0.0, 1.0));
QCOMPARE(data.outputGeometry(), QRect());
QCOMPARE(data.screen(), nullptr);
}
void TestScreenPaintData::testCopyCtor()
{
ScreenPaintData data(QMatrix4x4(), QRect(10, 20, 30, 40));
ScreenPaintData data;
ScreenPaintData data2(data);
// no value had been changed
QCOMPARE(data2.xScale(), 1.0);
@ -65,7 +65,6 @@ void TestScreenPaintData::testCopyCtor()
QCOMPARE(data2.rotationAngle(), 0.0);
QCOMPARE(data2.rotationOrigin(), QVector3D());
QCOMPARE(data2.rotationAxis(), QVector3D(0.0, 0.0, 1.0));
QCOMPARE(data2.outputGeometry(), QRect(10, 20, 30, 40));
data2.setScale(QVector3D(0.5, 2.0, 3.0));
data2.translate(0.5, 2.0, 3.0);
@ -89,14 +88,13 @@ void TestScreenPaintData::testCopyCtor()
void TestScreenPaintData::testAssignmentOperator()
{
ScreenPaintData data;
ScreenPaintData data2(QMatrix4x4(), QRect(10, 20, 30, 40));
ScreenPaintData data2;
data2.setScale(QVector3D(0.5, 2.0, 3.0));
data2.translate(0.5, 2.0, 3.0);
data2.setRotationAngle(45.0);
data2.setRotationOrigin(QVector3D(1.0, 2.0, 3.0));
data2.setRotationAxis(QVector3D(1.0, 1.0, 0.0));
QCOMPARE(data2.outputGeometry(), QRect(10, 20, 30, 40));
data = data2;
// data and data2 should be the same
@ -110,7 +108,6 @@ void TestScreenPaintData::testAssignmentOperator()
QCOMPARE(data.rotationAngle(), 45.0);
QCOMPARE(data.rotationOrigin(), QVector3D(1.0, 2.0, 3.0));
QCOMPARE(data.rotationAxis(), QVector3D(1.0, 1.0, 0.0));
QCOMPARE(data.outputGeometry(), QRect(10, 20, 30, 40));
// data 2
QCOMPARE(data2.xScale(), 0.5);
QCOMPARE(data2.yScale(), 2.0);

View file

@ -10,6 +10,7 @@
#include "effects.h"
#include "abstract_output.h"
#include "effectsadaptor.h"
#include "effectloader.h"
#ifdef KWIN_BUILD_ACTIVITIES
@ -189,9 +190,9 @@ EffectsHandlerImpl::EffectsHandlerImpl(Compositor *compositor, Scene *scene)
&KWin::EffectsHandler::sessionStateChanged);
connect(vds, &VirtualDesktopManager::countChanged, this, &EffectsHandler::numberDesktopsChanged);
connect(Cursors::self()->mouse(), &Cursor::mouseChanged, this, &EffectsHandler::mouseChanged);
connect(screens(), &Screens::countChanged, this, &EffectsHandler::numberScreensChanged);
connect(screens(), &Screens::sizeChanged, this, &EffectsHandler::virtualScreenSizeChanged);
connect(screens(), &Screens::geometryChanged, this, &EffectsHandler::virtualScreenGeometryChanged);
connect(Screens::self(), &Screens::countChanged, this, &EffectsHandler::numberScreensChanged);
connect(Screens::self(), &Screens::sizeChanged, this, &EffectsHandler::virtualScreenSizeChanged);
connect(Screens::self(), &Screens::geometryChanged, this, &EffectsHandler::virtualScreenGeometryChanged);
#ifdef KWIN_BUILD_ACTIVITIES
if (Activities *activities = Activities::self()) {
connect(activities, &Activities::added, this, &EffectsHandler::activityAdded);
@ -251,6 +252,14 @@ EffectsHandlerImpl::EffectsHandlerImpl(Compositor *compositor, Scene *scene)
setupClientConnections(client);
}
connect(kwinApp()->platform(), &Platform::outputEnabled, this, &EffectsHandlerImpl::slotOutputEnabled);
connect(kwinApp()->platform(), &Platform::outputDisabled, this, &EffectsHandlerImpl::slotOutputDisabled);
const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs();
for (AbstractOutput *output : outputs) {
slotOutputEnabled(output);
}
reconfigure();
}
@ -977,12 +986,12 @@ int EffectsHandlerImpl::desktopGridHeight() const
int EffectsHandlerImpl::workspaceWidth() const
{
return desktopGridWidth() * screens()->size().width();
return desktopGridWidth() * Screens::self()->size().width();
}
int EffectsHandlerImpl::workspaceHeight() const
{
return desktopGridHeight() * screens()->size().height();
return desktopGridHeight() * Screens::self()->size().height();
}
int EffectsHandlerImpl::desktopAtCoords(QPoint coords) const
@ -1003,7 +1012,7 @@ QPoint EffectsHandlerImpl::desktopCoords(int id) const
QPoint coords = VirtualDesktopManager::self()->grid().gridCoords(id);
if (coords.x() == -1)
return QPoint(-1, -1);
const QSize displaySize = screens()->size();
const QSize displaySize = Screens::self()->size();
return QPoint(coords.x() * displaySize.width(), coords.y() * displaySize.height());
}
@ -1208,17 +1217,17 @@ void EffectsHandlerImpl::addRepaint(int x, int y, int w, int h)
int EffectsHandlerImpl::activeScreen() const
{
return screens()->current();
return Screens::self()->current();
}
int EffectsHandlerImpl::numScreens() const
{
return screens()->count();
return Screens::self()->count();
}
int EffectsHandlerImpl::screenNumber(const QPoint& pos) const
{
return screens()->number(pos);
return Screens::self()->number(pos);
}
QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, int screen, int desktop) const
@ -1243,12 +1252,12 @@ QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, const QPoint& p, int
QRect EffectsHandlerImpl::virtualScreenGeometry() const
{
return screens()->geometry();
return Screens::self()->geometry();
}
QSize EffectsHandlerImpl::virtualScreenSize() const
{
return screens()->size();
return Screens::self()->size();
}
void EffectsHandlerImpl::defineCursor(Qt::CursorShape shape)
@ -1697,6 +1706,81 @@ SessionState EffectsHandlerImpl::sessionState() const
return Workspace::self()->sessionManager()->state();
}
QList<EffectScreen *> EffectsHandlerImpl::screens() const
{
return m_effectScreens;
}
EffectScreen *EffectsHandlerImpl::screenAt(const QPoint &point) const
{
return m_effectScreens.value(screenNumber(point));
}
EffectScreen *EffectsHandlerImpl::findScreen(const QString &name) const
{
for (EffectScreen *screen : qAsConst(m_effectScreens)) {
if (screen->name() == name) {
return screen;
}
}
return nullptr;
}
EffectScreen *EffectsHandlerImpl::findScreen(int screenId) const
{
return m_effectScreens.value(screenId);
}
void EffectsHandlerImpl::slotOutputEnabled(AbstractOutput *output)
{
EffectScreen *screen = new EffectScreenImpl(output, this);
m_effectScreens.append(screen);
emit screenAdded(screen);
}
void EffectsHandlerImpl::slotOutputDisabled(AbstractOutput *output)
{
auto it = std::find_if(m_effectScreens.begin(), m_effectScreens.end(), [&output](EffectScreen *screen) {
return static_cast<EffectScreenImpl *>(screen)->platformOutput() == output;
});
if (it != m_effectScreens.end()) {
EffectScreen *screen = *it;
m_effectScreens.erase(it);
emit screenRemoved(screen);
delete screen;
}
}
//****************************************
// EffectScreenImpl
//****************************************
EffectScreenImpl::EffectScreenImpl(AbstractOutput *output, QObject *parent)
: EffectScreen(parent)
, m_platformOutput(output)
{
}
AbstractOutput *EffectScreenImpl::platformOutput() const
{
return m_platformOutput;
}
QString EffectScreenImpl::name() const
{
return m_platformOutput->name();
}
qreal EffectScreenImpl::devicePixelRatio() const
{
return m_platformOutput->scale();
}
QRect EffectScreenImpl::geometry() const
{
return m_platformOutput->geometry();
}
//****************************************
// EffectWindowImpl
//****************************************

View file

@ -267,6 +267,10 @@ public:
void renderEffectQuickView(EffectQuickView *effectQuickView) const override;
SessionState sessionState() const override;
QList<EffectScreen *> screens() const override;
EffectScreen *screenAt(const QPoint &point) const override;
EffectScreen *findScreen(const QString &name) const override;
EffectScreen *findScreen(int screenId) const override;
public Q_SLOTS:
void slotCurrentTabAboutToChange(EffectWindow* from, EffectWindow* to);
@ -295,6 +299,8 @@ protected Q_SLOTS:
void slotFrameGeometryChanged(Toplevel *toplevel, const QRect &oldGeometry);
void slotPaddingChanged(KWin::Toplevel *t, const QRect &old);
void slotWindowDamaged(KWin::Toplevel *t, const QRegion& r);
void slotOutputEnabled(AbstractOutput *output);
void slotOutputDisabled(AbstractOutput *output);
protected:
void connectNotify(const QMetaMethod &signal) override;
@ -357,6 +363,24 @@ private:
EffectLoader *m_effectLoader;
int m_trackingCursorChanges;
std::unique_ptr<WindowPropertyNotifyX11Filter> m_x11WindowPropertyNotify;
QList<EffectScreen *> m_effectScreens;
};
class EffectScreenImpl : public EffectScreen
{
Q_OBJECT
public:
explicit EffectScreenImpl(AbstractOutput *output, QObject *parent = nullptr);
AbstractOutput *platformOutput() const;
QString name() const override;
qreal devicePixelRatio() const override;
QRect geometry() const override;
private:
AbstractOutput *m_platformOutput;
};
class EffectWindowImpl : public EffectWindow

View file

@ -52,7 +52,7 @@ ColorPickerEffect::~ColorPickerEffect() = default;
void ColorPickerEffect::paintScreen(int mask, const QRegion &region, ScreenPaintData &data)
{
m_cachedOutputGeometry = data.outputGeometry();
m_paintedScreen = data.screen();
effects->paintScreen(mask, region, data);
}
@ -60,7 +60,7 @@ void ColorPickerEffect::postPaintScreen()
{
effects->postPaintScreen();
if (m_scheduledPosition != QPoint(-1, -1) && (m_cachedOutputGeometry.isEmpty() || m_cachedOutputGeometry.contains(m_scheduledPosition))) {
if (m_scheduledPosition != QPoint(-1, -1) && (!m_paintedScreen || m_paintedScreen->geometry().contains(m_scheduledPosition))) {
uint8_t data[3];
const QRect geo = GLRenderTarget::virtualScreenGeometry();
const QPoint screenPosition(m_scheduledPosition.x() - geo.x(), m_scheduledPosition.y() - geo.y());

View file

@ -44,7 +44,7 @@ private:
void hideInfoMessage();
QDBusMessage m_replyMessage;
QRect m_cachedOutputGeometry;
EffectScreen *m_paintedScreen = nullptr;
QPoint m_scheduledPosition;
bool m_picking = false;
};

View file

@ -179,10 +179,7 @@ static xcb_pixmap_t xpixmapFromImage(const QImage &image)
void ScreenShotEffect::paintScreen(int mask, const QRegion &region, ScreenPaintData &data)
{
m_cachedOutputGeometry = data.outputGeometry();
// When taking a non-nativeSize fullscreenshot, pretend we have a uniform 1.0 ratio
// so the screenshot size will match the virtualGeometry
m_cachedScale = m_nativeSize ? data.screenScale() : 1.0;
m_paintedScreen = data.screen();
effects->paintScreen(mask, region, data);
}
@ -340,22 +337,23 @@ void ScreenShotEffect::postPaintScreen()
}
if (!m_scheduledGeometry.isNull()) {
if (!m_cachedOutputGeometry.isNull()) {
if (m_paintedScreen) {
// special handling for per-output geometry rendering
const QRect intersection = m_scheduledGeometry.intersected(m_cachedOutputGeometry);
const QRect intersection = m_scheduledGeometry.intersected(m_paintedScreen->geometry());
if (intersection.isEmpty()) {
// doesn't intersect, not going onto this screenshot
return;
}
QImage img = blitScreenshot(intersection, m_cachedScale);
if (img.size() == (m_scheduledGeometry.size() * m_cachedScale)) {
const qreal devicePixelRatio = m_nativeSize ? m_paintedScreen->devicePixelRatio() : 1.0;
QImage img = blitScreenshot(intersection, devicePixelRatio);
if (img.size() == (m_scheduledGeometry.size() * devicePixelRatio)) {
// we are done
sendReplyImage(img);
return;
}
img.setDevicePixelRatio(m_cachedScale);
img.setDevicePixelRatio(devicePixelRatio);
m_cacheOutputsImages.insert(ComparableQPoint(m_cachedOutputGeometry.topLeft()), img);
m_cacheOutputsImages.insert(ComparableQPoint(m_paintedScreen->geometry().topLeft()), img);
m_multipleOutputsRendered = m_multipleOutputsRendered.united(intersection);
if (m_multipleOutputsRendered.boundingRect() == m_scheduledGeometry) {
@ -460,7 +458,6 @@ void ScreenShotEffect::clearState()
m_captureCursor = false;
m_windowMode = WindowMode::NoCapture;
m_cacheOutputsImages.clear();
m_cachedOutputGeometry = QRect();
m_nativeSize = false;
m_orderImg.clear();
}

View file

@ -164,7 +164,6 @@ private:
ScreenShotType m_type;
QRect m_scheduledGeometry;
QDBusMessage m_replyMessage;
QRect m_cachedOutputGeometry;
QRegion m_multipleOutputsRendered;
QMap<ComparableQPoint, QImage> m_cacheOutputsImages;
QList<QPoint> m_orderImg;
@ -178,7 +177,7 @@ private:
};
WindowMode m_windowMode = WindowMode::NoCapture;
int m_fd = -1;
qreal m_cachedScale;
EffectScreen *m_paintedScreen = nullptr;
};
} // namespace

View file

@ -422,8 +422,7 @@ class ScreenPaintData::Private
{
public:
QMatrix4x4 projectionMatrix;
QRect outputGeometry;
qreal screenScale;
EffectScreen *screen = nullptr;
};
ScreenPaintData::ScreenPaintData()
@ -432,13 +431,12 @@ ScreenPaintData::ScreenPaintData()
{
}
ScreenPaintData::ScreenPaintData(const QMatrix4x4 &projectionMatrix, const QRect &outputGeometry, const qreal screenScale)
ScreenPaintData::ScreenPaintData(const QMatrix4x4 &projectionMatrix, EffectScreen *screen)
: PaintData()
, d(new Private())
{
d->projectionMatrix = projectionMatrix;
d->outputGeometry = outputGeometry;
d->screenScale = screenScale;
d->screen = screen;
}
ScreenPaintData::~ScreenPaintData() = default;
@ -455,7 +453,7 @@ ScreenPaintData::ScreenPaintData(const ScreenPaintData &other)
setRotationAxis(other.rotationAxis());
setRotationAngle(other.rotationAngle());
d->projectionMatrix = other.d->projectionMatrix;
d->outputGeometry = other.d->outputGeometry;
d->screen = other.d->screen;
}
ScreenPaintData &ScreenPaintData::operator=(const ScreenPaintData &rhs)
@ -470,7 +468,7 @@ ScreenPaintData &ScreenPaintData::operator=(const ScreenPaintData &rhs)
setRotationAxis(rhs.rotationAxis());
setRotationAngle(rhs.rotationAngle());
d->projectionMatrix = rhs.d->projectionMatrix;
d->outputGeometry = rhs.d->outputGeometry;
d->screen = rhs.d->screen;
return *this;
}
@ -523,14 +521,9 @@ QMatrix4x4 ScreenPaintData::projectionMatrix() const
return d->projectionMatrix;
}
QRect ScreenPaintData::outputGeometry() const
EffectScreen *ScreenPaintData::screen() const
{
return d->outputGeometry;
}
qreal ScreenPaintData::screenScale() const
{
return d->screenScale;
return d->screen;
}
//****************************************
@ -763,6 +756,10 @@ bool EffectsHandler::isOpenGLCompositing() const
EffectsHandler* effects = nullptr;
EffectScreen::EffectScreen(QObject *parent)
: QObject(parent)
{
}
//****************************************
// EffectWindow

View file

@ -69,6 +69,7 @@ class EffectWindowGroup;
class EffectFrame;
class EffectFramePrivate;
class EffectQuickView;
class EffectScreen;
class Effect;
class WindowQuad;
class GLShader;
@ -174,7 +175,7 @@ X-KDE-Library=kwin4_effect_cooleffect
#define KWIN_EFFECT_API_MAKE_VERSION( major, minor ) (( major ) << 8 | ( minor ))
#define KWIN_EFFECT_API_VERSION_MAJOR 0
#define KWIN_EFFECT_API_VERSION_MINOR 232
#define KWIN_EFFECT_API_VERSION_MINOR 233
#define KWIN_EFFECT_API_VERSION KWIN_EFFECT_API_MAKE_VERSION( \
KWIN_EFFECT_API_VERSION_MAJOR, KWIN_EFFECT_API_VERSION_MINOR )
@ -1378,7 +1379,24 @@ public:
* @since 5.18
*/
virtual SessionState sessionState() const = 0;
/**
* Returns the list of all the screens connected to the system.
*/
virtual QList<EffectScreen *> screens() const = 0;
virtual EffectScreen *screenAt(const QPoint &point) const = 0;
virtual EffectScreen *findScreen(const QString &name) const = 0;
virtual EffectScreen *findScreen(int screenId) const = 0;
Q_SIGNALS:
/**
* This signal is emitted whenever a new @a screen is added to the system.
*/
void screenAdded(KWin::EffectScreen *screen);
/**
* This signal is emitted whenever a @a screen is removed from the system.
*/
void screenRemoved(KWin::EffectScreen *screen);
/**
* Signal emitted when the current desktop changed.
* @param oldDesktop The previously current desktop
@ -1833,6 +1851,31 @@ protected:
CompositingType compositing_type;
};
/**
* The EffectScreen class represents a screen used by/for Effect classes.
*/
class KWINEFFECTS_EXPORT EffectScreen : public QObject
{
Q_OBJECT
public:
explicit EffectScreen(QObject *parent = nullptr);
/**
* Returns the name of the screen, e.g. "DP-1".
*/
virtual QString name() const = 0;
/**
* Returns the screen's ratio between physical pixels and device-independent pixels.
*/
virtual qreal devicePixelRatio() const = 0;
/**
* Returns the screen's geometry in the device-independent pixels.
*/
virtual QRect geometry() const = 0;
};
/**
* @short Representation of a window used by/for Effect classes.
@ -3000,7 +3043,7 @@ class KWINEFFECTS_EXPORT ScreenPaintData : public PaintData
{
public:
ScreenPaintData();
ScreenPaintData(const QMatrix4x4 &projectionMatrix, const QRect &outputGeometry = QRect(), const qreal screenScale = 1.0);
ScreenPaintData(const QMatrix4x4 &projectionMatrix, EffectScreen *screen = nullptr);
ScreenPaintData(const ScreenPaintData &other);
~ScreenPaintData() override;
/**
@ -3054,21 +3097,10 @@ public:
QMatrix4x4 projectionMatrix() const;
/**
* The geometry of the currently rendered output.
* Only set for per-output rendering (e.g. Wayland).
*
* This geometry can be used as a hint about the native window the OpenGL context
* is bound. OpenGL calls need to be translated to this geometry.
* @since 5.9
* Returns the currently rendered screen. Only set for per-screen rendering, e.g. Wayland.
*/
QRect outputGeometry() const;
EffectScreen *screen() const;
/**
* The scale factor for the output
*
* @since 5.19
*/
qreal screenScale() const;
private:
class Private;
QScopedPointer<Private> d;

View file

@ -67,7 +67,7 @@ void EffectsHandlerImplX11::doStartMouseInterception(Qt::CursorShape shape)
// NOTE: it is intended to not perform an XPointerGrab on X11. See documentation in kwineffects.h
// The mouse grab is implemented by using a full screen input only window
if (!m_mouseInterceptionWindow.isValid()) {
const QSize &s = screens()->size();
const QSize &s = Screens::self()->size();
const QRect geo(0, 0, s.width(), s.height());
const uint32_t mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
const uint32_t values[] = {

View file

@ -675,7 +675,7 @@ void SceneOpenGL::paint(int screenId, const QRegion &damage, const QList<Topleve
updateProjectionMatrix();
paintScreen(&mask, damage.intersected(geo), repaint, &update, &valid,
renderLoop, projectionMatrix(), geo, scaling); // call generic implementation
renderLoop, projectionMatrix()); // call generic implementation
paintCursor(valid);
if (!GLPlatform::instance()->isGLES() && screenId == -1) {

View file

@ -147,8 +147,7 @@ void Scene::reallocRepaints()
// returns mask and possibly modified region
void Scene::paintScreen(int* mask, const QRegion &damage, const QRegion &repaint,
QRegion *updateRegion, QRegion *validRegion, RenderLoop *renderLoop,
const QMatrix4x4 &projection, const QRect &outputGeometry,
qreal screenScale)
const QMatrix4x4 &projection)
{
const QSize &screenSize = screens()->size();
const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
@ -193,7 +192,7 @@ void Scene::paintScreen(int* mask, const QRegion &damage, const QRegion &repaint
painted_region = region;
repaint_region = repaint;
ScreenPaintData data(projection, outputGeometry, screenScale);
ScreenPaintData data(projection, effects->findScreen(painted_screen));
effects->paintScreen(*mask, region, data);
foreach (Window *w, stacking_order) {

View file

@ -210,8 +210,7 @@ protected:
// shared implementation, starts painting the screen
void paintScreen(int *mask, const QRegion &damage, const QRegion &repaint,
QRegion *updateRegion, QRegion *validRegion, RenderLoop *renderLoop,
const QMatrix4x4 &projection = QMatrix4x4(),
const QRect &outputGeometry = QRect(), qreal screenScale = 1.0);
const QMatrix4x4 &projection = QMatrix4x4());
// Render cursor texture in case hardware cursor is disabled/non-applicable
virtual void paintCursor(const QRegion &region) = 0;
friend class EffectsHandlerImpl;