/kde-workspace-4.8.97/kwin/effects/logout/logout.cpp
C++ | 451 lines | 345 code | 44 blank | 62 comment | 89 complexity | a802d8650ec0ee8c134669f108d66528 MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, LGPL-3.0
- /********************************************************************
- KWin - the KDE window manager
- This file is part of the KDE project.
- Copyright (C) 2007 Lubos Lunak <l.lunak@kde.org>
- Copyright (C) 2009 Martin Grдяlin <kde@martin-graesslin.com>
- Copyright (C) 2009, 2010 Lucas Murray <lmurray@undefinedfire.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. If not, see <http://www.gnu.org/licenses/>.
- *********************************************************************/
- #include "logout.h"
- #include "kwinglutils.h"
- #include <math.h>
- #include <kconfiggroup.h>
- #include <kdebug.h>
- #include <KDE/KStandardDirs>
- #include <QtGui/QMatrix4x4>
- #include <QtGui/QVector2D>
- namespace KWin
- {
- KWIN_EFFECT(logout, LogoutEffect)
- LogoutEffect::LogoutEffect()
- : progress(0.0)
- , displayEffect(false)
- , logoutWindow(NULL)
- , logoutWindowClosed(true)
- , logoutWindowPassed(false)
- , canDoPersistent(false)
- , ignoredWindows()
- , m_vignettingShader(NULL)
- , m_blurShader(NULL)
- {
- // Persistent effect
- logoutAtom = XInternAtom(display(), "_KDE_LOGGING_OUT", False);
- effects->registerPropertyType(logoutAtom, true);
- // Block KSMServer's effect
- char net_wm_cm_name[ 100 ];
- sprintf(net_wm_cm_name, "_NET_WM_CM_S%d", DefaultScreen(display()));
- Atom net_wm_cm = XInternAtom(display(), net_wm_cm_name, False);
- Window sel = XGetSelectionOwner(display(), net_wm_cm);
- Atom hack = XInternAtom(display(), "_KWIN_LOGOUT_EFFECT", False);
- XChangeProperty(display(), sel, hack, hack, 8, PropModeReplace, (unsigned char*)&hack, 1);
- // the atom is not removed when effect is destroyed, this is temporary anyway
- blurTexture = NULL;
- blurTarget = NULL;
- reconfigure(ReconfigureAll);
- connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*)));
- connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*)));
- connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*)));
- connect(effects, SIGNAL(propertyNotify(KWin::EffectWindow*,long)), this, SLOT(slotPropertyNotify(KWin::EffectWindow*,long)));
- }
- LogoutEffect::~LogoutEffect()
- {
- delete blurTexture;
- delete blurTarget;
- delete m_vignettingShader;
- delete m_blurShader;
- }
- void LogoutEffect::reconfigure(ReconfigureFlags)
- {
- frameDelay = 0;
- KConfigGroup conf = effects->effectConfig("Logout");
- useBlur = conf.readEntry("UseBlur", true);
- delete blurTexture;
- blurTexture = NULL;
- delete blurTarget;
- blurTarget = NULL;
- blurSupported = false;
- delete m_blurShader;
- m_blurShader = NULL;
- }
- void LogoutEffect::prePaintScreen(ScreenPrePaintData& data, int time)
- {
- if (!displayEffect && progress == 0.0) {
- if (blurTexture) {
- delete blurTexture;
- blurTexture = NULL;
- delete blurTarget;
- blurTarget = NULL;
- blurSupported = false;
- }
- } else if (!blurTexture) {
- blurSupported = false;
- delete blurTarget; // catch as we just tested the texture ;-P
- if (effects->compositingType() == OpenGLCompositing && GLTexture::NPOTTextureSupported() && GLRenderTarget::blitSupported() && useBlur) {
- // TODO: It seems that it is not possible to create a GLRenderTarget that has
- // a different size than the display right now. Most likely a KWin core bug.
- // Create texture and render target
- blurTexture = new GLTexture(displayWidth(), displayHeight());
- blurTexture->setFilter(GL_LINEAR_MIPMAP_LINEAR);
- blurTexture->setWrapMode(GL_CLAMP_TO_EDGE);
- blurTarget = new GLRenderTarget(*blurTexture);
- if (blurTarget->valid())
- blurSupported = true;
- // As creating the render target takes time it can cause the first two frames of the
- // blur animation to be jerky. For this reason we only start the animation after the
- // third frame.
- frameDelay = 2;
- }
- }
- if (frameDelay)
- --frameDelay;
- else {
- if (displayEffect)
- progress = qMin(1.0, progress + time / animationTime(2000.0));
- else if (progress > 0.0)
- progress = qMax(0.0, progress - time / animationTime(500.0));
- }
- if (blurSupported && progress > 0.0) {
- data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
- }
- effects->prePaintScreen(data, time);
- }
- void LogoutEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
- {
- if (progress > 0.0) {
- if (effects->compositingType() == KWin::OpenGLCompositing) {
- // In OpenGL mode we add vignetting and, if supported, a slight blur
- if (blurSupported) {
- // When using blur we render everything to an FBO and as such don't do the vignetting
- // until after we render the FBO to the screen.
- if (w == logoutWindow) {
- // Window is rendered after the FBO
- windowOpacity = data.opacity;
- data.opacity = 0.0; // Cheat, we need the opacity for later but don't want to blur it
- } else {
- if (logoutWindowPassed || ignoredWindows.contains(w)) {
- // Window is rendered after the FBO
- windows.append(w);
- windowsOpacities[ w ] = data.opacity;
- data.opacity = 0.0;
- } else // Window is added to the FBO
- data.saturation *= (1.0 - progress * 0.2);
- }
- } else {
- // If we are not blurring then we are not rendering to an FBO
- if (w == logoutWindow)
- // This is the logout window don't alter it but render our vignetting now
- renderVignetting();
- else if (!logoutWindowPassed && !ignoredWindows.contains(w))
- // Window is in the background, desaturate
- data.saturation *= (1.0 - progress * 0.2);
- // All other windows are unaltered
- }
- }
- if (effects->compositingType() == KWin::XRenderCompositing) {
- // Since we can't do vignetting in XRender just do a stronger desaturation and darken
- if (w != logoutWindow && !logoutWindowPassed && !ignoredWindows.contains(w)) {
- data.saturation *= (1.0 - progress * 0.8);
- data.brightness *= (1.0 - progress * 0.3);
- }
- }
- if (w == logoutWindow ||
- ignoredWindows.contains(w)) // HACK: All windows past the first ignored one should not be
- // blurred as it affects the stacking order.
- // All following windows are on top of the logout window and should not be altered either
- logoutWindowPassed = true;
- }
- effects->paintWindow(w, mask, region, data);
- }
- void LogoutEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data)
- {
- effects->paintScreen(mask, region, data);
- if (effects->compositingType() == KWin::OpenGLCompositing && progress > 0.0) {
- if (!blurSupported) {
- if (!logoutWindowPassed)
- // The logout window has been deleted but we still want to fade out the vignetting, thus
- // render it on the top of everything if still animating. We don't check if logoutWindow
- // is set as it may still be even if it wasn't rendered.
- renderVignetting();
- } else {
- GLRenderTarget::pushRenderTarget(blurTarget);
- blurTarget->blitFromFramebuffer();
- GLRenderTarget::popRenderTarget();
- //--------------------------
- // Render the screen effect
- renderBlurTexture();
- // Vignetting (Radial gradient with transparent middle and black edges)
- renderVignetting();
- //--------------------------
- // Render the logout window
- if (logoutWindow) {
- int winMask = logoutWindow->hasAlpha() ? PAINT_WINDOW_TRANSLUCENT : PAINT_WINDOW_OPAQUE;
- WindowPaintData winData(logoutWindow);
- winData.opacity = windowOpacity;
- effects->drawWindow(logoutWindow, winMask, region, winData);
- }
- // Render all windows on top of logout window
- foreach (EffectWindow * w, windows) {
- int winMask = w->hasAlpha() ? PAINT_WINDOW_TRANSLUCENT : PAINT_WINDOW_OPAQUE;
- WindowPaintData winData(w);
- winData.opacity = windowsOpacities[ w ];
- effects->drawWindow(w, winMask, region, winData);
- }
- windows.clear();
- windowsOpacities.clear();
- }
- }
- }
- void LogoutEffect::postPaintScreen()
- {
- if ((progress != 0.0 && progress != 1.0) || frameDelay)
- effects->addRepaintFull();
- if (progress > 0.0)
- logoutWindowPassed = false;
- effects->postPaintScreen();
- }
- void LogoutEffect::slotWindowAdded(EffectWindow* w)
- {
- if (isLogoutDialog(w)) {
- logoutWindow = w;
- logoutWindowClosed = false; // So we don't blur the window on close
- progress = 0.0;
- displayEffect = true;
- ignoredWindows.clear();
- effects->addRepaintFull();
- } else if (canDoPersistent)
- // TODO: Add parent
- ignoredWindows.append(w);
- }
- void LogoutEffect::slotWindowClosed(EffectWindow* w)
- {
- if (w == logoutWindow) {
- logoutWindowClosed = true;
- if (!canDoPersistent)
- displayEffect = false; // Fade back to normal
- effects->addRepaintFull();
- }
- }
- void LogoutEffect::slotWindowDeleted(EffectWindow* w)
- {
- windows.removeAll(w);
- ignoredWindows.removeAll(w);
- if (w == logoutWindow)
- logoutWindow = NULL;
- }
- bool LogoutEffect::isLogoutDialog(EffectWindow* w)
- {
- // TODO there should be probably a better way (window type?)
- if (w->windowClass() == "ksmserver ksmserver"
- && (w->windowRole() == "logoutdialog" || w->windowRole() == "logouteffect")) {
- return true;
- }
- return false;
- }
- void LogoutEffect::renderVignetting()
- {
- if (!ShaderManager::instance()->isValid()) {
- renderVignettingLegacy();
- return;
- }
- if (!m_vignettingShader) {
- m_vignettingShader = ShaderManager::instance()->loadFragmentShader(KWin::ShaderManager::ColorShader,
- KGlobal::dirs()->findResource("data", "kwin/vignetting.frag"));
- if (!m_vignettingShader->isValid()) {
- kDebug(1212) << "Vignetting Shader failed to load";
- return;
- }
- } else if (!m_vignettingShader->isValid()) {
- // shader broken
- return;
- }
- // need to get the projection matrix from the ortho shader for the vignetting shader
- QMatrix4x4 projection = ShaderManager::instance()->pushShader(KWin::ShaderManager::SimpleShader)->getUniformMatrix4x4("projection");
- ShaderManager::instance()->popShader();
- ShaderManager::instance()->pushShader(m_vignettingShader);
- m_vignettingShader->setUniform(KWin::GLShader::ProjectionMatrix, projection);
- m_vignettingShader->setUniform("u_progress", (float)progress * 0.9f);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_SCISSOR_TEST);
- const QRect fullArea = effects->clientArea(FullArea, 0, 0);
- for (int screen = 0; screen < effects->numScreens(); screen++) {
- const QRect screenGeom = effects->clientArea(ScreenArea, screen, 0);
- glScissor(screenGeom.x(), displayHeight() - screenGeom.y() - screenGeom.height(),
- screenGeom.width(), screenGeom.height()); // GL coords are flipped
- const float cenX = screenGeom.x() + screenGeom.width() / 2;
- const float cenY = fullArea.height() - screenGeom.y() - screenGeom.height() / 2;
- const float r = float((screenGeom.width() > screenGeom.height())
- ? screenGeom.width() : screenGeom.height()) * 0.8f; // Radius
- m_vignettingShader->setUniform("u_center", QVector2D(cenX, cenY));
- m_vignettingShader->setUniform("u_radius", r);
- QVector<float> vertices;
- vertices << screenGeom.x() << screenGeom.y();
- vertices << screenGeom.x() << screenGeom.y() + screenGeom.height();
- vertices << screenGeom.x() + screenGeom.width() << screenGeom.y();
- vertices << screenGeom.x() + screenGeom.width() << screenGeom.y() + screenGeom.height();
- GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
- vbo->setData(vertices.count()/2, 2, vertices.constData(), NULL);
- vbo->render(GL_TRIANGLE_STRIP);
- }
- glDisable(GL_SCISSOR_TEST);
- glDisable(GL_BLEND);
- ShaderManager::instance()->popShader();
- }
- void LogoutEffect::renderVignettingLegacy()
- {
- #ifndef KWIN_HAVE_OPENGLES
- glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT);
- glEnable(GL_BLEND); // If not already (Such as when rendered straight to the screen)
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- for (int screen = 0; screen < effects->numScreens(); screen++) {
- QRect screenGeom = effects->clientArea(ScreenArea, screen, 0);
- glScissor(screenGeom.x(), displayHeight() - screenGeom.y() - screenGeom.height(),
- screenGeom.width(), screenGeom.height()); // GL coords are flipped
- glEnable(GL_SCISSOR_TEST); // Geom must be set before enable
- const float cenX = screenGeom.x() + screenGeom.width() / 2;
- const float cenY = screenGeom.y() + screenGeom.height() / 2;
- const float a = M_PI / 16.0f; // Angle of increment
- const float r = float((screenGeom.width() > screenGeom.height())
- ? screenGeom.width() : screenGeom.height()) * 0.8f; // Radius
- glBegin(GL_TRIANGLE_FAN);
- glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
- glVertex3f(cenX, cenY, 0.0f);
- glColor4f(0.0f, 0.0f, 0.0f, progress * 0.9f);
- for (float i = 0.0f; i <= M_PI * 2.01f; i += a)
- glVertex3f(cenX + r * cos(i), cenY + r * sin(i), 0.0f);
- glEnd();
- glDisable(GL_SCISSOR_TEST);
- }
- glPopAttrib();
- #endif
- }
- void LogoutEffect::renderBlurTexture()
- {
- if (!ShaderManager::instance()->isValid()) {
- renderBlurTextureLegacy();
- return;
- }
- if (!m_blurShader) {
- m_blurShader = ShaderManager::instance()->loadFragmentShader(KWin::ShaderManager::SimpleShader,
- KGlobal::dirs()->findResource("data", "kwin/logout-blur.frag"));
- if (!m_blurShader->isValid()) {
- kDebug(1212) << "Logout blur shader failed to load";
- }
- } else if (!m_blurShader->isValid()) {
- // shader is broken - no need to continue here
- return;
- }
- // Unmodified base image
- ShaderManager::instance()->pushShader(m_blurShader);
- m_blurShader->setUniform(GLShader::Offset, QVector2D(0, 0));
- m_blurShader->setUniform(GLShader::ModulationConstant, QVector4D(1.0, 1.0, 1.0, 1.0));
- m_blurShader->setUniform(GLShader::Saturation, 1.0);
- m_blurShader->setUniform(GLShader::AlphaToOne, 1);
- m_blurShader->setUniform("u_alphaProgress", (float)progress * 0.4f);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- blurTexture->bind();
- blurTexture->render(infiniteRegion(), QRect(0, 0, displayWidth(), displayHeight()));
- blurTexture->unbind();
- glDisable(GL_BLEND);
- ShaderManager::instance()->popShader();
- checkGLError("Render blur texture");
- }
- void LogoutEffect::renderBlurTextureLegacy()
- {
- #ifndef KWIN_HAVE_OPENGLES
- glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT);
- // Unmodified base image
- blurTexture->bind();
- blurTexture->render(infiniteRegion(), QRect(0, 0, displayWidth(), displayHeight()));
- // Blurred image
- GLfloat bias[1];
- glGetTexEnvfv(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, bias);
- glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 1.75);
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glColor4f(1.0f, 1.0f, 1.0f, progress * 0.4);
- blurTexture->render(infiniteRegion(), QRect(0, 0, displayWidth(), displayHeight()));
- glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, bias[0]);
- blurTexture->unbind();
- glPopAttrib();
- #endif
- }
- void LogoutEffect::slotPropertyNotify(EffectWindow* w, long a)
- {
- if (w || a != logoutAtom)
- return; // Not our atom
- QByteArray byteData = effects->readRootProperty(logoutAtom, logoutAtom, 8);
- if (byteData.length() < 1) {
- // Property was deleted
- displayEffect = false;
- return;
- }
- // We are using a compatible KSMServer therefore only terminate the effect when the
- // atom is deleted, not when the dialog is closed.
- canDoPersistent = true;
- effects->addRepaintFull();
- }
- bool LogoutEffect::isActive() const
- {
- return progress != 0 || logoutWindow;
- }
- } // namespace