You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
7.7 KiB
C++
232 lines
7.7 KiB
C++
/*
|
|
* Copyright © 2021 Reven Martin <revenmartin@gmail.com>
|
|
* Copyright © 2015 Robert Metsäranta <therealestrob@gmail.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; see the file COPYING. if not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "roundedwindow.h"
|
|
|
|
// Qt
|
|
#include <QFile>
|
|
#include <QPainter>
|
|
|
|
// KWin
|
|
#include <kwinglplatform.h>
|
|
#include <kwinglutils.h>
|
|
|
|
KWIN_EFFECT_FACTORY_SUPPORTED_ENABLED(RoundedWindowFactory,
|
|
RoundedWindow,
|
|
"roundedwindow.json",
|
|
return RoundedWindow::supported();,
|
|
return RoundedWindow::enabledByDefault();)
|
|
|
|
RoundedWindow::RoundedWindow()
|
|
: KWin::Effect()
|
|
, m_shader(nullptr)
|
|
, m_frameRadius(12)
|
|
, m_corner(m_frameRadius, m_frameRadius)
|
|
{
|
|
QString versionStr = "110";
|
|
#ifdef KWIN_HAVE_OPENGLES
|
|
const qint64 coreVersionNumber = kVersionNumber(3, 0);
|
|
#else
|
|
const qint64 version = KWin::kVersionNumber(1, 40);
|
|
#endif
|
|
if (KWin::GLPlatform::instance()->glslVersion() >= version)
|
|
versionStr = "140";
|
|
|
|
QFile file(QString(":/shaders.frag.%1").arg(versionStr));
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
|
qDebug() << "RoundedWindow: cannot open " + file.fileName();
|
|
}
|
|
|
|
m_shader = KWin::ShaderManager::instance()->generateCustomShader(KWin::ShaderTrait::MapTexture, QByteArray(), file.readAll());
|
|
file.close();
|
|
|
|
if (m_shader->isValid()) {
|
|
const int sampler = m_shader->uniformLocation("sampler");
|
|
const int corner = m_shader->uniformLocation("corner");
|
|
KWin::ShaderManager::instance()->pushShader(m_shader);
|
|
m_shader->setUniform(corner, 1);
|
|
m_shader->setUniform(sampler, 0);
|
|
KWin::ShaderManager::instance()->popShader();
|
|
|
|
for (int i = 0; i < NTex; ++i) {
|
|
m_tex[i] = 0;
|
|
m_rect[i] = 0;
|
|
}
|
|
|
|
genMasks();
|
|
genRect();
|
|
} else {
|
|
qDebug() << "RoundedWindow: no valid shaders found!";
|
|
deleteLater();
|
|
}
|
|
}
|
|
|
|
RoundedWindow::~RoundedWindow()
|
|
{
|
|
}
|
|
|
|
bool RoundedWindow::supported()
|
|
{
|
|
return KWin::effects->isOpenGLCompositing() && KWin::GLRenderTarget::supported();
|
|
}
|
|
|
|
bool RoundedWindow::enabledByDefault()
|
|
{
|
|
return supported();
|
|
}
|
|
|
|
bool RoundedWindow::hasShadow(KWin::WindowQuadList &qds)
|
|
{
|
|
for (int i = 0; i < qds.count(); ++i)
|
|
if (qds.at(i).type() == KWin::WindowQuadShadow)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void RoundedWindow::paintWindow(KWin::EffectWindow *w, int mask, QRegion region, KWin::WindowPaintData &data)
|
|
{
|
|
if (!m_shader->isValid()
|
|
|| !w->isPaintingEnabled()
|
|
|| KWin::effects->hasActiveFullScreenEffect()
|
|
|| w->isDesktop()
|
|
|| w->isMenu()
|
|
|| w->isDock()
|
|
|| w->isPopupWindow()
|
|
|| w->isPopupMenu()
|
|
|| data.quads.isTransformed()
|
|
|| (mask & (PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS))
|
|
|| !hasShadow(data.quads)) {
|
|
KWin::effects->paintWindow(w, mask, region, data);
|
|
return;
|
|
}
|
|
|
|
//map the corners
|
|
const QRect geo(w->geometry());
|
|
const QRect rect[NTex] = {
|
|
QRect(geo.topLeft(), m_corner),
|
|
QRect(geo.topRight()-QPoint(m_frameRadius - 1, 0), m_corner),
|
|
QRect(geo.bottomRight()-QPoint(m_frameRadius - 1, m_frameRadius - 1), m_corner),
|
|
QRect(geo.bottomLeft()-QPoint(0, m_frameRadius - 1), m_corner)
|
|
};
|
|
|
|
const KWin::WindowQuadList qds(data.quads);
|
|
|
|
//paint the shadow
|
|
data.quads = qds.select(KWin::WindowQuadShadow);
|
|
KWin::effects->paintWindow(w, mask, region, data);
|
|
|
|
//copy the corner regions
|
|
KWin::GLTexture tex[NTex];
|
|
const QRect s(KWin::effects->virtualScreenGeometry());
|
|
for (int i = 0; i < NTex; ++i) {
|
|
tex[i] = KWin::GLTexture(GL_RGBA8, rect[i].size());
|
|
tex[i].bind();
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rect[i].x(), s.height() - rect[i].y() - rect[i].height(), rect[i].width(), rect[i].height());
|
|
tex[i].unbind();
|
|
}
|
|
|
|
//paint the actual window
|
|
data.quads = qds.filterOut(KWin::WindowQuadShadow);
|
|
KWin::effects->paintWindow(w, mask, region, data);
|
|
|
|
//'shape' the corners
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
const int mvpMatrixLocation = m_shader->uniformLocation("modelViewProjectionMatrix");
|
|
KWin::ShaderManager *sm = KWin::ShaderManager::instance();
|
|
sm->pushShader(m_shader);
|
|
for (int i = 0; i < NTex; ++i) {
|
|
QMatrix4x4 mvp = data.screenProjectionMatrix();
|
|
mvp.translate(rect[i].x(), rect[i].y());
|
|
m_shader->setUniform(mvpMatrixLocation, mvp);
|
|
glActiveTexture(GL_TEXTURE1);
|
|
m_tex[3-i]->bind();
|
|
glActiveTexture(GL_TEXTURE0);
|
|
tex[i].bind();
|
|
tex[i].render(region, rect[i]);
|
|
tex[i].unbind();
|
|
m_tex[3 - i]->unbind();
|
|
}
|
|
sm->popShader();
|
|
data.quads = qds;
|
|
|
|
glDisable(GL_BLEND);
|
|
}
|
|
|
|
void RoundedWindow::genMasks()
|
|
{
|
|
for (int i = 0; i < NTex; ++i)
|
|
if (m_tex[i])
|
|
delete m_tex[i];
|
|
|
|
QImage img(m_frameRadius * 2, m_frameRadius * 2, QImage::Format_ARGB32_Premultiplied);
|
|
img.fill(Qt::transparent);
|
|
QPainter p(&img);
|
|
p.fillRect(img.rect(), Qt::white);
|
|
p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
|
|
p.setPen(Qt::NoPen);
|
|
p.setBrush(Qt::white);
|
|
p.setRenderHint(QPainter::Antialiasing);
|
|
p.drawEllipse(img.rect());
|
|
p.end();
|
|
|
|
m_tex[TopLeft] = new KWin::GLTexture(img.copy(0, 0, m_frameRadius, m_frameRadius));
|
|
m_tex[TopRight] = new KWin::GLTexture(img.copy(m_frameRadius, 0, m_frameRadius, m_frameRadius));
|
|
m_tex[BottomRight] = new KWin::GLTexture(img.copy(m_frameRadius, m_frameRadius, m_frameRadius, m_frameRadius));
|
|
m_tex[BottomLeft] = new KWin::GLTexture(img.copy(0, m_frameRadius, m_frameRadius, m_frameRadius));
|
|
}
|
|
|
|
void RoundedWindow::genRect()
|
|
{
|
|
for (int i = 0; i < NTex; ++i)
|
|
if (m_rect[i])
|
|
delete m_rect[i];
|
|
|
|
int m_rSize = m_frameRadius + 1;
|
|
QImage img(m_rSize * 2, m_rSize * 2, QImage::Format_ARGB32_Premultiplied);
|
|
img.fill(Qt::transparent);
|
|
QPainter p(&img);
|
|
QRect r(img.rect());
|
|
p.setPen(Qt::NoPen);
|
|
p.setBrush(QColor(0, 0, 0, 0xff));
|
|
p.setRenderHint(QPainter::Antialiasing);
|
|
p.drawEllipse(r);
|
|
p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
|
|
p.setBrush(Qt::black);
|
|
r.adjust(1, 1, -1, -1);
|
|
p.drawEllipse(r);
|
|
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
|
p.setBrush(QColor(255, 255, 255, 63));
|
|
p.drawEllipse(r);
|
|
p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
|
|
p.setBrush(Qt::black);
|
|
r.adjust(0, 1, 0, 0);
|
|
p.drawEllipse(r);
|
|
p.end();
|
|
|
|
m_rect[TopLeft] = new KWin::GLTexture(img.copy(0, 0, m_rSize, m_rSize));
|
|
m_rect[TopRight] = new KWin::GLTexture(img.copy(m_rSize, 0, m_rSize, m_rSize));
|
|
m_rect[BottomRight] = new KWin::GLTexture(img.copy(m_rSize, m_rSize, m_rSize, m_rSize));
|
|
m_rect[BottomLeft] = new KWin::GLTexture(img.copy(0, m_rSize, m_rSize, m_rSize));
|
|
}
|
|
|
|
#include "roundedwindow.moc"
|