21#include <QCoreApplication>
30static const QByteArray s_contrastAtomName = QByteArrayLiteral(
"_KDE_NET_WM_BACKGROUND_CONTRAST_REGION");
32ContrastManagerInterface *ContrastEffect::s_contrastManager =
nullptr;
33QTimer *ContrastEffect::s_contrastManagerRemoveTimer =
nullptr;
37 m_shader = std::make_unique<ContrastShader>();
42 if (m_shader && m_shader->isValid()) {
47 if (!s_contrastManagerRemoveTimer) {
48 s_contrastManagerRemoveTimer =
new QTimer(QCoreApplication::instance());
49 s_contrastManagerRemoveTimer->setSingleShot(
true);
50 s_contrastManagerRemoveTimer->callOnTimeout([]() {
51 s_contrastManager->
remove();
52 s_contrastManager =
nullptr;
55 s_contrastManagerRemoveTimer->stop();
56 if (!s_contrastManager) {
67 if (m_shader && m_shader->isValid()) {
68 m_net_wm_contrast_region = effects->announceSupportProperty(s_contrastAtomName, this);
82 if (s_contrastManager) {
83 s_contrastManagerRemoveTimer->start(1000);
97 updateContrastRegion(window);
101void ContrastEffect::updateContrastRegion(
EffectWindow *w)
105 float colorTransform[16];
108 if (m_net_wm_contrast_region != XCB_ATOM_NONE) {
109 const QByteArray value = w->
readProperty(m_net_wm_contrast_region, m_net_wm_contrast_region, 32);
111 if (value.size() > 0 && !((value.size() - (16 *
sizeof(uint32_t))) % ((4 *
sizeof(uint32_t))))) {
112 const uint32_t *cardinals =
reinterpret_cast<const uint32_t *
>(value.constData());
113 const float *floatCardinals =
reinterpret_cast<const float *
>(value.constData());
115 for (; i < ((value.size() - (16 *
sizeof(uint32_t)))) /
sizeof(uint32_t);) {
116 int x = cardinals[i++];
117 int y = cardinals[i++];
118 int w = cardinals[i++];
119 int h = cardinals[i++];
123 for (
unsigned int j = 0; j < 16; ++j) {
124 colorTransform[j] = floatCardinals[i + j];
127 matrix = QMatrix4x4(colorTransform);
130 valid = !value.isNull();
133 SurfaceInterface *surf = w->surface();
135 if (surf && surf->contrast()) {
136 region = surf->contrast()->region();
137 matrix =
colorMatrix(surf->contrast()->contrast(), surf->contrast()->intensity(), surf->contrast()->saturation());
141 if (
auto internal = w->internalWindow()) {
142 const auto property = internal->property(
"kwin_background_region");
143 if (property.isValid()) {
144 region =
property.value<QRegion>();
146 qreal contrast = internal->property(
"kwin_background_contrast").toReal(&ok);
150 qreal intensity = internal->property(
"kwin_background_intensity").toReal(&ok);
154 qreal saturation = internal->property(
"kwin_background_saturation").toReal(&ok);
158 matrix =
colorMatrix(contrast, intensity, saturation);
164 Data &data = m_windowData[w];
165 data.colorMatrix = matrix;
166 data.contrastRegion = region;
168 if (
auto it = m_windowData.find(w); it != m_windowData.end()) {
170 m_windowData.erase(it);
182 updateContrastRegion(w);
188 internal->installEventFilter(
this);
191 updateContrastRegion(w);
196 auto internal = qobject_cast<QWindow *>(watched);
197 if (internal && event->type() == QEvent::DynamicPropertyChange) {
198 QDynamicPropertyChangeEvent *pe =
static_cast<QDynamicPropertyChangeEvent *
>(event);
199 if (pe->propertyName() ==
"kwin_background_region" || pe->propertyName() ==
"kwin_background_contrast" || pe->propertyName() ==
"kwin_background_intensity" || pe->propertyName() ==
"kwin_background_saturation") {
201 updateContrastRegion(w);
210 if (m_contrastChangedConnections.contains(w)) {
211 disconnect(m_contrastChangedConnections[w]);
212 m_contrastChangedConnections.remove(w);
214 if (
auto it = m_windowData.find(w); it != m_windowData.end()) {
216 m_windowData.erase(it);
222 if (w && atom == m_net_wm_contrast_region && m_net_wm_contrast_region != XCB_ATOM_NONE) {
223 updateContrastRegion(w);
229 QMatrix4x4 satMatrix;
230 QMatrix4x4 intMatrix;
231 QMatrix4x4 contMatrix;
234 if (!qFuzzyCompare(saturation, 1.0)) {
235 const qreal rval = (1.0 - saturation) * .2126;
236 const qreal gval = (1.0 - saturation) * .7152;
237 const qreal bval = (1.0 - saturation) * .0722;
239 satMatrix = QMatrix4x4(rval + saturation, rval, rval, 0.0,
240 gval, gval + saturation, gval, 0.0,
241 bval, bval, bval + saturation, 0.0,
246 if (!qFuzzyCompare(intensity, 1.0)) {
247 intMatrix.scale(intensity, intensity, intensity);
251 if (!qFuzzyCompare(contrast, 1.0)) {
252 const float transl = (1.0 - contrast) / 2.0;
254 contMatrix = QMatrix4x4(contrast, 0, 0, 0.0,
257 transl, transl, transl, 1.0);
260 QMatrix4x4
colorMatrix = contMatrix * satMatrix * intMatrix;
292 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
295 if (screenSize.width() > maxTexSize || screenSize.height() > maxTexSize) {
302QRegion ContrastEffect::contrastRegion(
const EffectWindow *w)
const
305 if (
const auto it = m_windowData.find(w); it != m_windowData.end()) {
306 const QRegion &appRegion = it->second.contrastRegion;
307 if (!appRegion.isEmpty()) {
319void ContrastEffect::uploadRegion(std::span<QVector2D> map,
const QRegion ®ion, qreal scale)
322 for (
const QRect &r : region) {
324 const QVector2D topLeft =
roundVector(QVector2D(deviceRect.x(), deviceRect.y()));
325 const QVector2D topRight =
roundVector(QVector2D(deviceRect.x() + deviceRect.width(), deviceRect.y()));
326 const QVector2D bottomLeft =
roundVector(QVector2D(deviceRect.x(), deviceRect.y() + deviceRect.height()));
327 const QVector2D bottomRight =
roundVector(QVector2D(deviceRect.x() + deviceRect.width(), deviceRect.y() + deviceRect.height()));
330 map[index++] = topRight;
331 map[index++] = topLeft;
332 map[index++] = bottomLeft;
335 map[index++] = bottomLeft;
336 map[index++] = bottomRight;
337 map[index++] = topRight;
341bool ContrastEffect::uploadGeometry(GLVertexBuffer *vbo,
const QRegion ®ion, qreal scale)
343 const int vertexCount = region.rectCount() * 6;
348 const auto map = vbo->map<QVector2D>(vertexCount);
352 uploadRegion(*map, region, scale);
355 constexpr std::array layout{
369 vbo->setAttribLayout(std::span(layout),
sizeof(QVector2D));
373bool ContrastEffect::shouldContrast(
const EffectWindow *w,
int mask,
const WindowPaintData &data)
const
375 if (!m_shader || !m_shader->isValid()) {
383 if (w->isDesktop()) {
387 bool scaled = !qFuzzyCompare(data.xScale(), 1.0) && !qFuzzyCompare(data.yScale(), 1.0);
388 bool translated = data.xTranslation() || data.yTranslation();
399 if (shouldContrast(w, mask, data)) {
400 const QRect screen = viewport.
renderRect().toRect();
401 QRegion shape = region & contrastRegion(w).translated(w->
pos().toPoint()) & screen;
405 const bool scaled = data.
xScale() != 1 || data.
yScale() != 1;
407 QPoint pt = shape.boundingRect().topLeft();
409 for (QRect r : shape) {
410 const QPointF topLeft(pt.x() + (r.x() - pt.x()) * data.
xScale() + data.
xTranslation(),
412 const QPoint bottomRight(std::floor(topLeft.x() + r.width() * data.
xScale()) - 1,
413 std::floor(topLeft.y() + r.height() * data.
yScale()) - 1);
414 scaledShape += QRect(QPoint(std::floor(topLeft.x()), std::floor(topLeft.y())), bottomRight);
416 shape = scaledShape & region;
419 }
else if (translated) {
421 for (QRect r : shape) {
423 const QPoint topLeft(std::ceil(t.x()), std::ceil(t.y()));
424 const QPoint bottomRight(std::floor(t.x() + t.width() - 1), std::floor(t.y() + t.height() - 1));
425 translated += QRect(topLeft, bottomRight);
427 shape = translated & region;
430 if (!shape.isEmpty()) {
439void ContrastEffect::doContrast(
const RenderTarget &renderTarget,
const RenderViewport &viewport,
EffectWindow *w,
const QRegion &shape,
const QRect &screen,
const float opacity,
const QMatrix4x4 &screenProjection)
441 const qreal scale = viewport.
scale();
442 const QRegion actualShape = shape & screen;
448 if (!uploadGeometry(vbo, actualShape, scale)) {
453 Q_ASSERT(m_windowData.contains(w));
454 auto &windowData = m_windowData[w];
455 if (!windowData.texture || (renderTarget.
texture() && windowData.
texture->internalFormat() != renderTarget.
texture()->
internalFormat()) || windowData.texture->size() != r.size()) {
457 if (!windowData.texture) {
460 windowData.fbo = std::make_unique<GLFramebuffer>(windowData.texture.get());
461 windowData.texture->setFilter(GL_LINEAR);
462 windowData.texture->setWrapMode(GL_CLAMP_TO_EDGE);
464 GLTexture *contrastTexture = windowData.texture.get();
465 contrastTexture->bind();
467 windowData.fbo->blitFromFramebuffer(r.toRect(), QRect(QPoint(), contrastTexture->size()));
469 m_shader->setColorMatrix(m_windowData[w].
colorMatrix);
472 m_shader->setOpacity(opacity);
475 const QRectF boundingRect = actualShape.boundingRect();
476 QMatrix4x4 textureMatrix;
477 textureMatrix.scale(1, -1);
478 textureMatrix.translate(0, -1);
480 textureMatrix.translate(0.5, 0.5);
482 textureMatrix.translate(-0.5, -0.5);
484 textureMatrix.scale(1.0 / boundingRect.width(), 1.0 / boundingRect.height(), 1);
485 textureMatrix.translate(-boundingRect.x(), -boundingRect.y(), 0);
486 textureMatrix.scale(1.0 / viewport.
scale(), 1.0 / viewport.
scale());
488 m_shader->setTextureMatrix(textureMatrix);
489 m_shader->setModelViewProjectionMatrix(screenProjection);
491 vbo->
draw(GL_TRIANGLES, 0, actualShape.rectCount() * 6);
493 contrastTexture->unbind();
516#include "moc_contrast.cpp"
void slotPropertyNotify(KWin::EffectWindow *w, long atom)
bool eventFilter(QObject *watched, QEvent *event) override
void drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion ®ion, WindowPaintData &data) override
bool blocksDirectScanout() const override
void slotWindowAdded(KWin::EffectWindow *w)
void slotScreenGeometryChanged()
bool isActive() const override
static bool enabledByDefault()
static QMatrix4x4 colorMatrix(qreal contrast, qreal intensity, qreal saturation)
~ContrastEffect() override
void slotWindowDeleted(KWin::EffectWindow *w)
Represents the Global for org_kde_kwin_contrast_manager interface.
Representation of a window used by/for Effect classes.
QByteArray readProperty(long atom, long type, int format) const
QRectF decorationInnerRect
SurfaceInterface * surface() const
Display * waylandDisplay() const
void drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion ®ion, WindowPaintData &data)
void propertyNotify(KWin::EffectWindow *w, long atom)
void windowDeleted(KWin::EffectWindow *w)
Q_SCRIPTABLE KWin::EffectWindow * findWindow(WId id) const
void reloadEffect(Effect *effect)
QList< EffectWindow * > stackingOrder
bool makeOpenGLContextCurrent()
Makes the OpenGL compositing context current.
bool isScreenLocked() const
xcb_atom_t announceSupportProperty(const QByteArray &propertyName, Effect *effect)
Announces support for the feature with the given name. If no other Effect has announced support for t...
void virtualScreenGeometryChanged()
bool isOpenGLCompositing() const
Whether the Compositor is OpenGL based (either GL 1 or 2).
void xcbConnectionChanged()
xcb_connection_t * xcbConnection() const
void windowAdded(KWin::EffectWindow *w)
Effect * activeFullScreenEffect() const
static std::unique_ptr< GLTexture > allocate(GLenum internalFormat, const QSize &size, int levels=1)
GLenum internalFormat() const
void draw(GLenum primitiveMode, int first, int count)
static GLVertexBuffer * streamingBuffer()
GLTexture * texture() const
OutputTransform transform() const
QRectF renderRect() const
QRectF mapToRenderTarget(const QRectF &logicalGeometry) const
Resource representing a wl_surface.
qreal yTranslation() const
QMatrix4x4 projectionMatrix() const
qreal xTranslation() const
@ PAINT_WINDOW_TRANSFORMED
qreal fromXNative(int value)
@ WindowForceBackgroundContrastRole
For fullscreen effects to enforce the background contrast,.
KWIN_EXPORT QRectF scaledRect(const QRectF &rect, qreal scale)
KWIN_EXPORT QVector2D roundVector(const QVector2D &input)