Commit 38996d97 authored by Vlad Zahorodnii's avatar Vlad Zahorodnii
Browse files

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.
parent b3e70318
......@@ -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;
......
......@@ -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);
......
......@@ -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
//****************************************
......
......@@ -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
......
......@@ -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());
......
......@@ -44,7 +44,7 @@ private:
void hideInfoMessage();
QDBusMessage m_replyMessage;
QRect m_cachedOutputGeometry;
EffectScreen *m_paintedScreen = nullptr;
QPoint m_scheduledPosition;
bool m_picking = false;
};
......
......@@ -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();
}
......
......@@ -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
......
......@@ -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
{
return d->outputGeometry;
}
qreal ScreenPaintData::screenScale() const
EffectScreen *ScreenPaintData::screen() 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
......
......@@ -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;
......
......@@ -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[] = {
......
......@@ -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) {
......
......@@ -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) {
......
......@@ -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