~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to kwin/effects/logout/logout.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/********************************************************************
 
2
 KWin - the KDE window manager
 
3
 This file is part of the KDE project.
 
4
 
 
5
Copyright (C) 2007 Lubos Lunak <l.lunak@kde.org>
 
6
Copyright (C) 2009 Martin Gräßlin <kde@martin-graesslin.com>
 
7
Copyright (C) 2009, 2010 Lucas Murray <lmurray@undefinedfire.com>
 
8
 
 
9
This program is free software; you can redistribute it and/or modify
 
10
it under the terms of the GNU General Public License as published by
 
11
the Free Software Foundation; either version 2 of the License, or
 
12
(at your option) any later version.
 
13
 
 
14
This program is distributed in the hope that it will be useful,
 
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
GNU General Public License for more details.
 
18
 
 
19
You should have received a copy of the GNU General Public License
 
20
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
21
*********************************************************************/
 
22
 
 
23
#include "logout.h"
 
24
 
 
25
#include "kwinglutils.h"
 
26
 
 
27
#include <math.h>
 
28
#include <kconfiggroup.h>
 
29
#include <kdebug.h>
 
30
 
 
31
namespace KWin
 
32
{
 
33
 
 
34
KWIN_EFFECT(logout, LogoutEffect)
 
35
 
 
36
LogoutEffect::LogoutEffect()
 
37
    : progress(0.0)
 
38
    , displayEffect(false)
 
39
    , logoutWindow(NULL)
 
40
    , logoutWindowClosed(true)
 
41
    , logoutWindowPassed(false)
 
42
    , canDoPersistent(false)
 
43
    , ignoredWindows()
 
44
{
 
45
    // Persistent effect
 
46
    logoutAtom = XInternAtom(display(), "_KDE_LOGGING_OUT", False);
 
47
    effects->registerPropertyType(logoutAtom, true);
 
48
 
 
49
    // Block KSMServer's effect
 
50
    char net_wm_cm_name[ 100 ];
 
51
    sprintf(net_wm_cm_name, "_NET_WM_CM_S%d", DefaultScreen(display()));
 
52
    Atom net_wm_cm = XInternAtom(display(), net_wm_cm_name, False);
 
53
    Window sel = XGetSelectionOwner(display(), net_wm_cm);
 
54
    Atom hack = XInternAtom(display(), "_KWIN_LOGOUT_EFFECT", False);
 
55
    XChangeProperty(display(), sel, hack, hack, 8, PropModeReplace, (unsigned char*)&hack, 1);
 
56
    // the atom is not removed when effect is destroyed, this is temporary anyway
 
57
 
 
58
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
59
    blurTexture = NULL;
 
60
    blurTarget = NULL;
 
61
#endif
 
62
    reconfigure(ReconfigureAll);
 
63
    connect(effects, SIGNAL(windowAdded(EffectWindow*)), this, SLOT(slotWindowAdded(EffectWindow*)));
 
64
    connect(effects, SIGNAL(windowClosed(EffectWindow*)), this, SLOT(slotWindowClosed(EffectWindow*)));
 
65
    connect(effects, SIGNAL(windowDeleted(EffectWindow*)), this, SLOT(slotWindowDeleted(EffectWindow*)));
 
66
    connect(effects, SIGNAL(propertyNotify(EffectWindow*,long)), this, SLOT(slotPropertyNotify(EffectWindow*,long)));
 
67
}
 
68
 
 
69
LogoutEffect::~LogoutEffect()
 
70
{
 
71
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
72
    delete blurTexture;
 
73
    delete blurTarget;
 
74
#endif
 
75
}
 
76
 
 
77
void LogoutEffect::reconfigure(ReconfigureFlags)
 
78
{
 
79
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
80
    frameDelay = 0;
 
81
    KConfigGroup conf = effects->effectConfig("Logout");
 
82
    useBlur = conf.readEntry("UseBlur", true);
 
83
    delete blurTexture;
 
84
    blurTexture = NULL;
 
85
    delete blurTarget;
 
86
    blurTarget = NULL;
 
87
    blurSupported = false;
 
88
#endif
 
89
}
 
90
 
 
91
void LogoutEffect::prePaintScreen(ScreenPrePaintData& data, int time)
 
92
{
 
93
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
94
    if (!displayEffect && progress == 0.0) {
 
95
        if (blurTexture) {
 
96
            delete blurTexture;
 
97
            blurTexture = NULL;
 
98
            delete blurTarget;
 
99
            blurTarget = NULL;
 
100
            blurSupported = false;
 
101
        }
 
102
    } else if (!blurTexture) {
 
103
        blurSupported = false;
 
104
        delete blurTarget; // catch as we just tested the texture ;-P
 
105
        if (effects->compositingType() == OpenGLCompositing && GLTexture::NPOTTextureSupported() && useBlur) {
 
106
            // TODO: It seems that it is not possible to create a GLRenderTarget that has
 
107
            //       a different size than the display right now. Most likely a KWin core bug.
 
108
            // Create texture and render target
 
109
            blurTexture = new GLTexture(displayWidth(), displayHeight());
 
110
            blurTexture->setFilter(GL_LINEAR_MIPMAP_LINEAR);
 
111
            blurTexture->setWrapMode(GL_CLAMP_TO_EDGE);
 
112
 
 
113
            blurTarget = new GLRenderTarget(blurTexture);
 
114
            if (blurTarget->valid())
 
115
                blurSupported = true;
 
116
 
 
117
            // As creating the render target takes time it can cause the first two frames of the
 
118
            // blur animation to be jerky. For this reason we only start the animation after the
 
119
            // third frame.
 
120
            frameDelay = 2;
 
121
        }
 
122
    }
 
123
 
 
124
    if (frameDelay)
 
125
        --frameDelay;
 
126
    else
 
127
#endif
 
128
    {
 
129
        if (displayEffect)
 
130
            progress = qMin(1.0, progress + time / animationTime(2000.0));
 
131
        else if (progress > 0.0)
 
132
            progress = qMax(0.0, progress - time / animationTime(500.0));
 
133
    }
 
134
 
 
135
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
136
    if (blurSupported && progress > 0.0) {
 
137
        data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
 
138
    }
 
139
#endif
 
140
 
 
141
    effects->prePaintScreen(data, time);
 
142
}
 
143
 
 
144
void LogoutEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
 
145
{
 
146
    if (progress > 0.0) {
 
147
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
148
        if (effects->compositingType() == KWin::OpenGLCompositing) {
 
149
            // In OpenGL mode we add vignetting and, if supported, a slight blur
 
150
            if (blurSupported) {
 
151
                // When using blur we render everything to an FBO and as such don't do the vignetting
 
152
                // until after we render the FBO to the screen.
 
153
                if (w == logoutWindow) {
 
154
                    // Window is rendered after the FBO
 
155
                    windowOpacity = data.opacity;
 
156
                    data.opacity = 0.0; // Cheat, we need the opacity for later but don't want to blur it
 
157
                } else {
 
158
                    if (logoutWindowPassed || ignoredWindows.contains(w)) {
 
159
                        // Window is rendered after the FBO
 
160
                        windows.append(w);
 
161
                        windowsOpacities[ w ] = data.opacity;
 
162
                        data.opacity = 0.0;
 
163
                    } else // Window is added to the FBO
 
164
                        data.saturation *= (1.0 - progress * 0.2);
 
165
                }
 
166
            } else {
 
167
                // If we are not blurring then we are not rendering to an FBO
 
168
                if (w == logoutWindow)
 
169
                    // This is the logout window don't alter it but render our vignetting now
 
170
                    renderVignetting();
 
171
                else if (!logoutWindowPassed && !ignoredWindows.contains(w))
 
172
                    // Window is in the background, desaturate
 
173
                    data.saturation *= (1.0 - progress * 0.2);
 
174
                // All other windows are unaltered
 
175
            }
 
176
        }
 
177
#endif
 
178
        if (effects->compositingType() == KWin::XRenderCompositing) {
 
179
            // Since we can't do vignetting in XRender just do a stronger desaturation and darken
 
180
            if (w != logoutWindow && !logoutWindowPassed && !ignoredWindows.contains(w)) {
 
181
                data.saturation *= (1.0 - progress * 0.8);
 
182
                data.brightness *= (1.0 - progress * 0.3);
 
183
            }
 
184
        }
 
185
        if (w == logoutWindow ||
 
186
                ignoredWindows.contains(w))   // HACK: All windows past the first ignored one should not be
 
187
            //       blurred as it affects the stacking order.
 
188
            // All following windows are on top of the logout window and should not be altered either
 
189
            logoutWindowPassed = true;
 
190
    }
 
191
    effects->paintWindow(w, mask, region, data);
 
192
}
 
193
 
 
194
void LogoutEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data)
 
195
{
 
196
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
197
    if (blurSupported && progress > 0.0)
 
198
        GLRenderTarget::pushRenderTarget(blurTarget);
 
199
#endif
 
200
    effects->paintScreen(mask, region, data);
 
201
 
 
202
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
203
    if (effects->compositingType() == KWin::OpenGLCompositing && progress > 0.0) {
 
204
        if (!blurSupported) {
 
205
            if (!logoutWindowPassed)
 
206
                // The logout window has been deleted but we still want to fade out the vignetting, thus
 
207
                // render it on the top of everything if still animating. We don't check if logoutWindow
 
208
                // is set as it may still be even if it wasn't rendered.
 
209
                renderVignetting();
 
210
        } else {
 
211
            GLRenderTarget* target = GLRenderTarget::popRenderTarget();
 
212
            assert(target == blurTarget);
 
213
            Q_UNUSED(target);
 
214
 
 
215
            //--------------------------
 
216
            // Render the screen effect
 
217
 
 
218
            // HACK: the GL code is still OpenGL 1, so we have to unbind the shader.
 
219
            GLint shader = 0;
 
220
            if (ShaderManager::instance()->isShaderBound()) {
 
221
                glGetIntegerv(GL_CURRENT_PROGRAM, &shader);
 
222
                glUseProgram(0);
 
223
            }
 
224
            glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT);
 
225
 
 
226
            // Unmodified base image
 
227
            blurTexture->bind();
 
228
            glBegin(GL_QUADS);
 
229
            glTexCoord2f(0.0, 0.0);
 
230
            glVertex2f(0.0, displayHeight());
 
231
            glTexCoord2f(1.0, 0.0);
 
232
            glVertex2f(displayWidth(), displayHeight());
 
233
            glTexCoord2f(1.0, 1.0);
 
234
            glVertex2f(displayWidth(), 0.0);
 
235
            glTexCoord2f(0.0, 1.0);
 
236
            glVertex2f(0.0, 0.0);
 
237
            glEnd();
 
238
 
 
239
            // Blurred image
 
240
            GLfloat bias[1];
 
241
            glGetTexEnvfv(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, bias);
 
242
            glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 1.75);
 
243
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 
244
            glEnable(GL_BLEND);
 
245
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
246
            glColor4f(1.0f, 1.0f, 1.0f, progress * 0.4);
 
247
            glBegin(GL_QUADS);
 
248
            glTexCoord2f(0.0, 0.0);
 
249
            glVertex2f(0.0, displayHeight());
 
250
            glTexCoord2f(1.0, 0.0);
 
251
            glVertex2f(displayWidth(), displayHeight());
 
252
            glTexCoord2f(1.0, 1.0);
 
253
            glVertex2f(displayWidth(), 0.0);
 
254
            glTexCoord2f(0.0, 1.0);
 
255
            glVertex2f(0.0, 0.0);
 
256
            glEnd();
 
257
            glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, bias[0]);
 
258
            blurTexture->unbind();
 
259
 
 
260
            // Vignetting (Radial gradient with transparent middle and black edges)
 
261
            renderVignetting();
 
262
 
 
263
            glPopAttrib();
 
264
            // HACK: rebind previously bound shader
 
265
            if (ShaderManager::instance()->isShaderBound()) {
 
266
                glUseProgram(shader);
 
267
            }
 
268
 
 
269
            //--------------------------
 
270
 
 
271
            // Render the logout window
 
272
            if (logoutWindow) {
 
273
                int winMask = logoutWindow->hasAlpha() ? PAINT_WINDOW_TRANSLUCENT : PAINT_WINDOW_OPAQUE;
 
274
                WindowPaintData winData(logoutWindow);
 
275
                winData.opacity = windowOpacity;
 
276
                effects->drawWindow(logoutWindow, winMask, region, winData);
 
277
            }
 
278
 
 
279
            // Render all windows on top of logout window
 
280
            foreach (EffectWindow * w, windows) {
 
281
                int winMask = w->hasAlpha() ? PAINT_WINDOW_TRANSLUCENT : PAINT_WINDOW_OPAQUE;
 
282
                WindowPaintData winData(w);
 
283
                winData.opacity = windowsOpacities[ w ];
 
284
                effects->drawWindow(w, winMask, region, winData);
 
285
            }
 
286
 
 
287
            windows.clear();
 
288
            windowsOpacities.clear();
 
289
        }
 
290
    }
 
291
#endif
 
292
}
 
293
 
 
294
void LogoutEffect::postPaintScreen()
 
295
{
 
296
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
297
    if ((progress != 0.0 && progress != 1.0) || frameDelay)
 
298
        effects->addRepaintFull();
 
299
#else
 
300
    if (progress != 0.0 && progress != 1.0)
 
301
        effects->addRepaintFull();
 
302
#endif
 
303
 
 
304
    if (progress > 0.0)
 
305
        logoutWindowPassed = false;
 
306
    effects->postPaintScreen();
 
307
}
 
308
 
 
309
void LogoutEffect::slotWindowAdded(EffectWindow* w)
 
310
{
 
311
    if (isLogoutDialog(w)) {
 
312
        logoutWindow = w;
 
313
        logoutWindowClosed = false; // So we don't blur the window on close
 
314
        progress = 0.0;
 
315
        displayEffect = true;
 
316
        ignoredWindows.clear();
 
317
        effects->addRepaintFull();
 
318
    } else if (canDoPersistent)
 
319
        // TODO: Add parent
 
320
        ignoredWindows.append(w);
 
321
}
 
322
 
 
323
void LogoutEffect::slotWindowClosed(EffectWindow* w)
 
324
{
 
325
    if (w == logoutWindow) {
 
326
        logoutWindowClosed = true;
 
327
        if (!canDoPersistent)
 
328
            displayEffect = false; // Fade back to normal
 
329
        effects->addRepaintFull();
 
330
    }
 
331
}
 
332
 
 
333
void LogoutEffect::slotWindowDeleted(EffectWindow* w)
 
334
{
 
335
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
336
    windows.removeAll(w);
 
337
#endif
 
338
    ignoredWindows.removeAll(w);
 
339
    if (w == logoutWindow)
 
340
        logoutWindow = NULL;
 
341
}
 
342
 
 
343
bool LogoutEffect::isLogoutDialog(EffectWindow* w)
 
344
{
 
345
    // TODO there should be probably a better way (window type?)
 
346
    if (w->windowClass() == "ksmserver ksmserver"
 
347
            && (w->windowRole() == "logoutdialog" || w->windowRole() == "logouteffect")) {
 
348
        return true;
 
349
    }
 
350
    return false;
 
351
}
 
352
 
 
353
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
354
void LogoutEffect::renderVignetting()
 
355
{
 
356
    glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT);
 
357
    glEnable(GL_BLEND);   // If not already (Such as when rendered straight to the screen)
 
358
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
359
    for (int screen = 0; screen < effects->numScreens(); screen++) {
 
360
        QRect screenGeom = effects->clientArea(ScreenArea, screen, 0);
 
361
        glScissor(screenGeom.x(), displayHeight() - screenGeom.y() - screenGeom.height(),
 
362
                  screenGeom.width(), screenGeom.height());  // GL coords are flipped
 
363
        glEnable(GL_SCISSOR_TEST);   // Geom must be set before enable
 
364
        const float cenX = screenGeom.x() + screenGeom.width() / 2;
 
365
        const float cenY = screenGeom.y() + screenGeom.height() / 2;
 
366
        const float a = M_PI / 16.0f; // Angle of increment
 
367
        const float r = float((screenGeom.width() > screenGeom.height())
 
368
                              ? screenGeom.width() : screenGeom.height()) * 0.8f;  // Radius
 
369
        glBegin(GL_TRIANGLE_FAN);
 
370
        glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
 
371
        glVertex3f(cenX, cenY, 0.0f);
 
372
        glColor4f(0.0f, 0.0f, 0.0f, progress * 0.9f);
 
373
        for (float i = 0.0f; i <= M_PI * 2.01f; i += a)
 
374
            glVertex3f(cenX + r * cos(i), cenY + r * sin(i), 0.0f);
 
375
        glEnd();
 
376
        glDisable(GL_SCISSOR_TEST);
 
377
    }
 
378
    glPopAttrib();
 
379
}
 
380
#endif
 
381
 
 
382
void LogoutEffect::slotPropertyNotify(EffectWindow* w, long a)
 
383
{
 
384
    if (w || a != logoutAtom)
 
385
        return; // Not our atom
 
386
 
 
387
    QByteArray byteData = effects->readRootProperty(logoutAtom, logoutAtom, 8);
 
388
    if (byteData.length() < 1) {
 
389
        // Property was deleted
 
390
        displayEffect = false;
 
391
        return;
 
392
    }
 
393
 
 
394
    // We are using a compatible KSMServer therefore only terminate the effect when the
 
395
    // atom is deleted, not when the dialog is closed.
 
396
    canDoPersistent = true;
 
397
}
 
398
 
 
399
} // namespace