diff --git a/effects/CMakeLists.txt b/effects/CMakeLists.txt index b8d458e6be..ab8b7ec778 100644 --- a/effects/CMakeLists.txt +++ b/effects/CMakeLists.txt @@ -95,6 +95,7 @@ include( dialogparent/CMakeLists.txt ) include( login/CMakeLists.txt ) include( outline/CMakeLists.txt ) include( presentwindows/CMakeLists.txt ) +include( screenedge/CMakeLists.txt ) include( slidingpopups/CMakeLists.txt ) include( taskbarthumbnail/CMakeLists.txt ) diff --git a/effects/screenedge/CMakeLists.txt b/effects/screenedge/CMakeLists.txt new file mode 100644 index 0000000000..6b2057173f --- /dev/null +++ b/effects/screenedge/CMakeLists.txt @@ -0,0 +1,12 @@ +####################################### +# Effect + +# Source files +set( kwin4_effect_builtins_sources ${kwin4_effect_builtins_sources} + screenedge/screenedgeeffect.cpp + ) + +# .desktop files +install( FILES + screenedge/screenedgeeffect.desktop + DESTINATION ${SERVICES_INSTALL_DIR}/kwin ) diff --git a/effects/screenedge/screenedgeeffect.cpp b/effects/screenedge/screenedgeeffect.cpp new file mode 100644 index 0000000000..365045a8ed --- /dev/null +++ b/effects/screenedge/screenedgeeffect.cpp @@ -0,0 +1,226 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2013 Martin Gräßlin + +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. If not, see . +*********************************************************************/ +#include "screenedgeeffect.h" +// KWin +#include +#include +// KDE +#include +// Qt +#include +#include + +namespace KWin { + +KWIN_EFFECT(screenedge, ScreenEdgeEffect) + +ScreenEdgeEffect::ScreenEdgeEffect() + : Effect() + , m_glow(new Plasma::Svg(this)) + , m_cleanupTimer(new QTimer(this)) +{ + m_glow->setImagePath("widgets/glowbar"); + connect(effects, SIGNAL(screenEdgeApproaching(ElectricBorder,qreal,QRect)), SLOT(edgeApproaching(ElectricBorder,qreal,QRect))); + m_cleanupTimer->setInterval(5000); + m_cleanupTimer->setSingleShot(true); + connect(m_cleanupTimer, SIGNAL(timeout()), SLOT(cleanup())); +} + +ScreenEdgeEffect::~ScreenEdgeEffect() +{ + cleanup(); +} + +void ScreenEdgeEffect::cleanup() +{ + for (QHash::iterator it = m_borders.begin(); + it != m_borders.end(); + ++it) { + effects->addRepaint((*it)->geometry); + } + qDeleteAll(m_borders); + m_borders.clear(); +} + +void ScreenEdgeEffect::prePaintScreen(ScreenPrePaintData &data, int time) +{ + effects->prePaintScreen(data, time); + for (QHash::iterator it = m_borders.begin(); + it != m_borders.end(); + ++it) { + if ((*it)->strength == 0.0) { + continue; + } + data.paint += (*it)->geometry; + } +} + +void ScreenEdgeEffect::paintScreen(int mask, QRegion region, ScreenPaintData &data) +{ + effects->paintScreen(mask, region, data); + for (QHash::iterator it = m_borders.begin(); + it != m_borders.end(); + ++it) { + const qreal opacity = (*it)->strength; + if (opacity == 0.0) { + continue; + } + if (effects->isOpenGLCompositing()) { + GLTexture *texture = (*it)->texture.data(); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + texture->bind(); + if (effects->compositingType() == OpenGL2Compositing) { + ShaderBinder binder(ShaderManager::SimpleShader); + const QVector4D constant(opacity, opacity, opacity, opacity); + binder.shader()->setUniform(GLShader::ModulationConstant, constant); + texture->render(infiniteRegion(), (*it)->geometry); + } else if (effects->compositingType() == OpenGL1Compositing) { +#ifdef KWIN_HAVE_OPENGL_1 + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColor4f(1.0, 1.0, 1.0, opacity); + texture->render(infiniteRegion(), (*it)->geometry); +#endif + } + texture->unbind(); + glDisable(GL_BLEND); + } + } +} + +void ScreenEdgeEffect::edgeApproaching(ElectricBorder border, qreal factor, const QRect &geometry) +{ + QHash::iterator it = m_borders.find(border); + if (it != m_borders.end()) { + // need to update + effects->addRepaint((*it)->geometry); + (*it)->strength = factor; + if ((*it)->geometry != geometry) { + (*it)->geometry = geometry; + effects->addRepaint((*it)->geometry); + if (border == ElectricLeft || border == ElectricRight || border == ElectricTop || border == ElectricBottom) { + (*it)->texture.reset(createEdgeGlow(border, geometry.size())); + } + } + if (factor == 0.0) { + m_cleanupTimer->start(); + } else { + m_cleanupTimer->stop(); + } + } else if (factor != 0.0) { + // need to generate new Glow + Glow *glow = createGlow(border, factor, geometry); + if (glow) { + m_borders.insert(border, glow); + effects->addRepaint(glow->geometry); + } + } +} + +Glow *ScreenEdgeEffect::createGlow(ElectricBorder border, qreal factor, const QRect &geometry) +{ + Glow *glow = new Glow(); + glow->strength = factor; + glow->geometry = geometry; + + // render the glow image + if (border == ElectricTopLeft || border == ElectricTopRight || border == ElectricBottomRight || border == ElectricBottomLeft) { + glow->texture.reset(createCornerGlow(border)); + } else { + glow->texture.reset(createEdgeGlow(border, geometry.size())); + } + if (glow->texture.isNull()) { + delete glow; + return NULL; + } + glow->texture->setWrapMode(GL_CLAMP_TO_EDGE); + + return glow; +} + +GLTexture *ScreenEdgeEffect::createCornerGlow(ElectricBorder border) +{ + switch (border) { + case ElectricTopLeft: + return new GLTexture(m_glow->pixmap("bottomright")); + case ElectricTopRight: + return new GLTexture(m_glow->pixmap("bottomleft")); + case ElectricBottomRight: + return new GLTexture(m_glow->pixmap("topleft")); + case ElectricBottomLeft: + return new GLTexture(m_glow->pixmap("topright")); + default: + return NULL; + } +} + +GLTexture *ScreenEdgeEffect::createEdgeGlow(ElectricBorder border, const QSize &size) +{ + QPoint pixmapPosition(0, 0); + QPixmap l, r, c; + switch (border) { + case ElectricTop: + l = m_glow->pixmap("bottomleft"); + r = m_glow->pixmap("bottomright"); + c = m_glow->pixmap("bottom"); + break; + case ElectricBottom: + l = m_glow->pixmap("topleft"); + r = m_glow->pixmap("topright"); + c = m_glow->pixmap("top"); + pixmapPosition = QPoint(0, size.height() - c.height()); + break; + case ElectricLeft: + l = m_glow->pixmap("topright"); + r = m_glow->pixmap("bottomright"); + c = m_glow->pixmap("right"); + break; + case ElectricRight: + l = m_glow->pixmap("topleft"); + r = m_glow->pixmap("bottomleft"); + c = m_glow->pixmap("left"); + pixmapPosition = QPoint(size.width() - c.width(), 0); + break; + default: + return NULL; + } + QImage image(size, QImage::Format_ARGB32); + image.fill(Qt::transparent); + QPainter p; + p.begin(&image); + if (border == ElectricBottom || border == ElectricTop) { + p.drawPixmap(pixmapPosition, l); + p.drawTiledPixmap(QRect(l.width(), pixmapPosition.y(), size.width() - l.width() - r.width(), c.height()), c); + p.drawPixmap(QPoint(size.width() - r.width(), pixmapPosition.y()), r); + } else { + p.drawPixmap(pixmapPosition, l); + p.drawTiledPixmap(QRect(pixmapPosition.x(), l.height(), c.width(), size.height() - l.height() - r.height()), c); + p.drawPixmap(QPoint(pixmapPosition.x(), size.height() - r.height()), r); + } + p.end(); + return new GLTexture(image); +} + +bool ScreenEdgeEffect::isActive() const +{ + return !m_borders.isEmpty(); +} + +} // namespace diff --git a/effects/screenedge/screenedgeeffect.desktop b/effects/screenedge/screenedgeeffect.desktop new file mode 100644 index 0000000000..13acc73caf --- /dev/null +++ b/effects/screenedge/screenedgeeffect.desktop @@ -0,0 +1,17 @@ +[Desktop Entry] +Name=Screen Edge +Icon=preferences-system-windows-effect-screenedge +Comment=Highlights a screen edge when approaching + +Type=Service +X-KDE-ServiceTypes=KWin/Effect +X-KDE-PluginInfo-Author=Martin Gräßlin +X-KDE-PluginInfo-Email=mgraesslin@kde.org +X-KDE-PluginInfo-Name=kwin4_effect_screenedge +X-KDE-PluginInfo-Version=0.1.0 +X-KDE-PluginInfo-Category=Appearance +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true +X-KDE-Library=kwin4_effect_builtins +X-KDE-Ordering=90 diff --git a/effects/screenedge/screenedgeeffect.h b/effects/screenedge/screenedgeeffect.h new file mode 100644 index 0000000000..7f3b5c9f2a --- /dev/null +++ b/effects/screenedge/screenedgeeffect.h @@ -0,0 +1,64 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2013 Martin Gräßlin + +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. If not, see . +*********************************************************************/ +#ifndef KWIN_SCREEN_EDGE_EFFECT_H +#define KWIN_SCREEN_EDGE_EFFECT_H +#include + +class QTimer; +namespace Plasma { + class Svg; +} + +namespace KWin { +class Glow; +class GLTexture; + +class ScreenEdgeEffect : public Effect +{ + Q_OBJECT +public: + ScreenEdgeEffect(); + virtual ~ScreenEdgeEffect(); + virtual void prePaintScreen(ScreenPrePaintData &data, int time); + virtual void paintScreen(int mask, QRegion region, ScreenPaintData &data); + virtual bool isActive() const; +private Q_SLOTS: + void edgeApproaching(ElectricBorder border, qreal factor, const QRect &geometry); + void cleanup(); +private: + Glow *createGlow(ElectricBorder border, qreal factor, const QRect &geometry); + GLTexture *createCornerGlow(ElectricBorder border); + GLTexture *createEdgeGlow(ElectricBorder border, const QSize &size); + Plasma::Svg *m_glow; + QHash m_borders; + QTimer *m_cleanupTimer; +}; + +class Glow +{ +public: + QScopedPointer texture; + qreal strength; + QRect geometry; +}; + +} + +#endif