diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index e2da2da..071d8a1 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -26,3 +26,4 @@ endif() # add_subdirectory(blur) add_subdirectory(decoration) +add_subdirectory(roundedwindow) \ No newline at end of file diff --git a/plugins/decoration/decoration.cpp b/plugins/decoration/decoration.cpp index a38db59..58dd6b6 100644 --- a/plugins/decoration/decoration.cpp +++ b/plugins/decoration/decoration.cpp @@ -173,6 +173,7 @@ void Decoration::init() void Decoration::reconfigure() { recalculateBorders(); + updateResizeBorders(); updateShadow(); } @@ -202,11 +203,10 @@ void Decoration::updateResizeBorders() { QMargins borders; - const int extender = settings()->largeSpacing(); - borders.setLeft(extender); - borders.setTop(extender); - borders.setRight(extender); - borders.setBottom(extender); + borders.setLeft(5); + borders.setTop(5); + borders.setRight(5); + borders.setBottom(5); setResizeOnlyBorders(borders); } @@ -261,7 +261,8 @@ void Decoration::updateShadow() g_shadowSize = 70; g_shadowStrength = 30; g_shadowColor = Qt::black; - const int shadowOverlap = m_frameRadius; + const int frameRadius = 12; + const int shadowOverlap = frameRadius; // const int shadowOffset = qMax(6 * g_shadowSize / 16, shadowOverlap * 2); const int shadowOffset = shadowOverlap; @@ -304,13 +305,13 @@ void Decoration::updateShadow() painter.setPen( gradientStopColor(g_shadowColor, g_shadowStrength * 0.5)); painter.setBrush(Qt::NoBrush); - painter.drawRoundedRect(innerRect, -0.5 + m_frameRadius, -0.5 + m_frameRadius); + painter.drawRoundedRect(innerRect, -0.5 + frameRadius, -0.5 + frameRadius); // mask out inner rect painter.setPen(Qt::NoPen); painter.setBrush(Qt::black); painter.setCompositionMode(QPainter::CompositionMode_DestinationOut); - painter.drawRoundedRect(innerRect, 0.5 + m_frameRadius, 0.5 + m_frameRadius); + painter.drawRoundedRect(innerRect, 0.5 + frameRadius, 0.5 + frameRadius); painter.end(); g_sShadow = QSharedPointer::create(); @@ -386,31 +387,12 @@ void Decoration::paintFrameBackground(QPainter *painter, const QRect &repaintReg painter->fillRect(rect(), Qt::transparent); painter->setRenderHint(QPainter::Antialiasing); painter->setPen(Qt::NoPen); - // painter->setBrush(decoratedClient->color( - // decoratedClient->isActive() - // ? KDecoration2::ColorGroup::Active - // : KDecoration2::ColorGroup::Inactive, - // KDecoration2::ColorRole::Frame)); - // painter->setClipRect(0, borderTop(), size().width(), size().height() - borderTop(), Qt::IntersectClip); - // painter->drawRect(rect()); - painter->restore(); } QColor Decoration::titleBarBackgroundColor() const { return darkMode() ? m_titleBarBgDarkColor : m_titleBarBgColor; - - // const auto *decoratedClient = client().toStrongRef().data(); - // const auto group = decoratedClient->isActive() - // ? KDecoration2::ColorGroup::Active - // : KDecoration2::ColorGroup::Inactive; - // const qreal opacity = decoratedClient->isActive() - // ? s_titleBarOpacityActive - // : s_titleBarOpacityInactive; - // QColor color = decoratedClient->color(group, KDecoration2::ColorRole::TitleBar); - // color.setAlphaF(opacity); - // return color; } QColor Decoration::titleBarForegroundColor() const @@ -437,10 +419,8 @@ void Decoration::paintTitleBarBackground(QPainter *painter, const QRect &repaint painter->save(); painter->setRenderHint(QPainter::Antialiasing); painter->setPen(Qt::NoPen); - // painter->setBrush(titleBarBackgroundColor()); painter->setBrush(Qt::red); painter->drawRoundedRect(QRect(0, 0, decoratedClient->width(), titleBarHeight()), 6, 6); - // painter->drawRect(QRect(0, 0, decoratedClient->width(), titleBarHeight())); painter->restore(); } diff --git a/plugins/decoration/decoration.h b/plugins/decoration/decoration.h index 5a20c8d..ffa5e0e 100644 --- a/plugins/decoration/decoration.h +++ b/plugins/decoration/decoration.h @@ -90,7 +90,7 @@ private: private: int m_titleBarHeight = 38; - int m_frameRadius = 10; + int m_frameRadius = 0; QColor m_titleBarBgColor = QColor(255, 255, 255, 255); QColor m_titleBarFgColor = QColor(56, 56, 56, 255); QColor m_unfocusedFgColor = QColor(127, 127, 127, 255); diff --git a/plugins/roundedwindow/CMakeLists.txt b/plugins/roundedwindow/CMakeLists.txt new file mode 100644 index 0000000..e4edba0 --- /dev/null +++ b/plugins/roundedwindow/CMakeLists.txt @@ -0,0 +1,66 @@ +find_package(Qt5 CONFIG REQUIRED COMPONENTS + Gui + Core + DBus + UiTools + Widgets + X11Extras + OpenGL + Network + Xml +) + +find_package(KF5CoreAddons) +find_package(KF5Config) +find_package(KF5WindowSystem) + +include_directories(${Qt5Widgets_INCLUDE_DIRS} ${Qt5X11Extras_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5OpenGL_INCLUDE_DIRS} ${Qt5Xml_INCLUDE_DIRS}) +add_definitions(${Qt5Widgets_DEFINITIONS}) + +find_path(EFFECTS_H kwineffects.h PATH_SUFFIXES kf5) + +if (EFFECTS_H) + include_directories(${EFFECTS_H}) +else (EFFECTS_H) + message(STATUS "didnt find kwineffects.h, not building effects") +endif (EFFECTS_H) + +find_library(KWIN_EFFECTS NAMES kwineffects PATH_SUFFIXES kf5) +find_library(KWIN_GLUTILS NAMES kwinglutils PATH_SUFFIXES kf5) +find_library(OPENGL NAMES GL) + +if (NOT KWIN_EFFECTS) + message(STATUS "didnt find kwineffects lib, not building effects") +endif (NOT KWIN_EFFECTS) + +if (NOT KWIN_GLUTILS) + message(STATUS "didnt find kwin glutils lib, not building effects") +endif (NOT KWIN_GLUTILS) + +if (NOT OPENGL) + message(STATUS "didnt find opengl, not building effects") +endif (NOT OPENGL) + +if (NOT EFFECTS_H OR NOT KWIN_GLUTILS OR NOT KWIN_EFFECTS OR NOT OPENGL) + message(FATAL_ERROR "cant continue") +endif (NOT EFFECTS_H OR NOT KWIN_GLUTILS OR NOT KWIN_EFFECTS OR NOT OPENGL) + +add_library(roundedwindow MODULE roundedwindow.cpp resources.qrc) + +target_link_libraries(roundedwindow + PUBLIC + Qt5::Core + Qt5::Gui + Qt5::DBus + ${KWIN_EFFECTS} + ${KWIN_GLUTILS} + epoxy + GL + PRIVATE + KF5::CoreAddons + KF5::ConfigCore + KF5::WindowSystem +) + +install (TARGETS roundedwindow + DESTINATION ${QT_PLUGINS_DIR}/kwin/effects/plugins) \ No newline at end of file diff --git a/plugins/roundedwindow/resources.qrc b/plugins/roundedwindow/resources.qrc new file mode 100644 index 0000000..15cc7cc --- /dev/null +++ b/plugins/roundedwindow/resources.qrc @@ -0,0 +1,6 @@ + + + shaders.frag.110 + shaders.frag.140 + + \ No newline at end of file diff --git a/plugins/roundedwindow/roundedwindow.cpp b/plugins/roundedwindow/roundedwindow.cpp new file mode 100644 index 0000000..07a8f66 --- /dev/null +++ b/plugins/roundedwindow/roundedwindow.cpp @@ -0,0 +1,231 @@ +/* + * Copyright © 2021 Reven Martin + * Copyright © 2015 Robert Metsäranta + * + * 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 +#include + +// KWin +#include +#include + +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" diff --git a/plugins/roundedwindow/roundedwindow.h b/plugins/roundedwindow/roundedwindow.h new file mode 100644 index 0000000..b8fd670 --- /dev/null +++ b/plugins/roundedwindow/roundedwindow.h @@ -0,0 +1,60 @@ +/* + * Copyright © 2021 Reven Martin + * Copyright © 2015 Robert Metsäranta + * + * 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. + */ + +#ifndef ROUNDEDWINDOW_H +#define ROUNDEDWINDOW_H + +#include + +namespace KWin { class GLTexture; } + +class RoundedWindow : public KWin::Effect +{ + Q_OBJECT + +public: + enum { TopLeft = 0, + TopRight, + BottomRight, + BottomLeft, + NTex }; + + RoundedWindow(); + ~RoundedWindow(); + + static bool supported(); + static bool enabledByDefault(); + bool hasShadow(KWin::WindowQuadList &qds); + + void paintWindow(KWin::EffectWindow *w, int mask, QRegion region, KWin::WindowPaintData &data); + +private: + void genMasks(); + void genRect(); + +private: + KWin::GLShader *m_shader; + KWin::GLTexture *m_tex[NTex]; + KWin::GLTexture *m_rect[NTex]; + int m_frameRadius; + QSize m_corner; +}; + +#endif diff --git a/plugins/roundedwindow/roundedwindow.json b/plugins/roundedwindow/roundedwindow.json new file mode 100644 index 0000000..2171d6e --- /dev/null +++ b/plugins/roundedwindow/roundedwindow.json @@ -0,0 +1,31 @@ +{ + "KPlugin": { + "Authors": [ + { + "Email": "therealestrob@gmail.com", + "Name": "Rob" + } + ], + "Category": "Appearance", + "Dependencies": [ + ], + "Description": "An effect that shapes/rounds corners of windows.", + "EnabledByDefault": true, + "Icon": "", + "Id": "kwin4_effect_roundedwindow", + "License": "GPL", + "Name": "RoundedWindow", + "ServiceTypes": [ + "KWin/Effect" + ], + "Version": "git" + }, + "org.kde.kwin.effect": { + "video": "", + "exclusiveGroup": "", + "enabledByDefaultMethod": true + }, + "X-KDE-Ordering": "5", + "X-Plasma-API": "", + "X-Plasma-MainScript": "" +} diff --git a/plugins/roundedwindow/shaders.frag.110 b/plugins/roundedwindow/shaders.frag.110 new file mode 100644 index 0000000..4d42e27 --- /dev/null +++ b/plugins/roundedwindow/shaders.frag.110 @@ -0,0 +1,14 @@ +#version 110 + +uniform sampler2D sampler; +uniform sampler2D corner; + +varying vec2 texcoord0; + +void main() +{ + vec4 tex = texture2D(sampler, texcoord0); + vec4 texCorner = texture2D(corner, texcoord0); + tex.a = texCorner.a; + gl_FragColor = tex; +} \ No newline at end of file diff --git a/plugins/roundedwindow/shaders.frag.140 b/plugins/roundedwindow/shaders.frag.140 new file mode 100644 index 0000000..76da8e5 --- /dev/null +++ b/plugins/roundedwindow/shaders.frag.140 @@ -0,0 +1,15 @@ +#version 140 + +uniform sampler2D sampler; +uniform sampler2D corner; + +in vec2 texcoord0; +out vec4 fragColor; + +void main(void) +{ + vec4 tex = texture(sampler, texcoord0); + vec4 texCorner = texture(corner, texcoord0); + tex.a = texCorner.a; + fragColor = tex; +} \ No newline at end of file