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

« back to all changes in this revision

Viewing changes to kwin/effects/startupfeedback/startupfeedback.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) 2010 Martin Gräßlin <kde@martin-graesslin.com>
 
6
 
 
7
This program is free software; you can redistribute it and/or modify
 
8
it under the terms of the GNU General Public License as published by
 
9
the Free Software Foundation; either version 2 of the License, or
 
10
(at your option) any later version.
 
11
 
 
12
This program is distributed in the hope that it will be useful,
 
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
GNU General Public License for more details.
 
16
 
 
17
You should have received a copy of the GNU General Public License
 
18
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
*********************************************************************/
 
20
#include "startupfeedback.h"
 
21
// Qt
 
22
#include <QtCore/QSize>
 
23
#include <QtGui/QPainter>
 
24
// KDE
 
25
#include <KDE/KConfigGroup>
 
26
#include <KDE/KDebug>
 
27
#include <KDE/KGlobal>
 
28
#include <KDE/KIconLoader>
 
29
#include <KDE/KStandardDirs>
 
30
#include <KDE/KStartupInfo>
 
31
#include <KDE/KSelectionOwner>
 
32
// KWin
 
33
#include <kwinglutils.h>
 
34
// X11
 
35
#include <X11/Xcursor/Xcursor.h>
 
36
 
 
37
// based on StartupId in KRunner by Lubos Lunak
 
38
// Copyright (C) 2001 Lubos Lunak <l.lunak@kde.org>
 
39
 
 
40
namespace KWin
 
41
{
 
42
 
 
43
KWIN_EFFECT(startupfeedback, StartupFeedbackEffect)
 
44
KWIN_EFFECT_SUPPORTED(startupfeedback, StartupFeedbackEffect::supported())
 
45
 
 
46
// number of key frames for bouncing animation
 
47
static const int BOUNCE_FRAMES = 20;
 
48
// duration between two key frames in msec
 
49
static const int BOUNCE_FRAME_DURATION = 30;
 
50
// duration of one bounce animation
 
51
static const int BOUNCE_DURATION = BOUNCE_FRAME_DURATION * BOUNCE_FRAMES;
 
52
// number of key frames for blinking animation
 
53
static const int BLINKING_FRAMES = 5;
 
54
// duration between two key frames in msec
 
55
static const int BLINKING_FRAME_DURATION = 100;
 
56
// duration of one blinking animation
 
57
static const int BLINKING_DURATION = BLINKING_FRAME_DURATION * BLINKING_FRAMES;
 
58
//const int color_to_pixmap[] = { 0, 1, 2, 3, 2, 1 };
 
59
static const int FRAME_TO_BOUNCE_YOFFSET[] = {
 
60
    -5, -1, 2, 5, 8, 10, 12, 13, 15, 15, 15, 15, 14, 12, 10, 8, 5, 2, -1, -5
 
61
};
 
62
static const QSize BOUNCE_SIZES[] = {
 
63
    QSize(16, 16), QSize(14, 18), QSize(12, 20), QSize(18, 14), QSize(20, 12)
 
64
};
 
65
static const int FRAME_TO_BOUNCE_TEXTURE[] = {
 
66
    0, 0, 0, 1, 2, 2, 1, 0, 3, 4, 4, 3, 0, 1, 2, 2, 1, 0, 0, 0
 
67
};
 
68
static const int FRAME_TO_BLINKING_COLOR[] = {
 
69
    0, 1, 2, 3, 2, 1
 
70
};
 
71
static const QColor BLINKING_COLORS[] = {
 
72
    Qt::black, Qt::darkGray, Qt::lightGray, Qt::white, Qt::white
 
73
};
 
74
 
 
75
StartupFeedbackEffect::StartupFeedbackEffect()
 
76
    : m_startupInfo(new KStartupInfo(KStartupInfo::CleanOnCantDetect, this))
 
77
    , m_selection(new KSelectionOwner("_KDE_STARTUP_FEEDBACK", -1, this))
 
78
    , m_active(false)
 
79
    , m_frame(0)
 
80
    , m_progress(0)
 
81
    , m_texture(0)
 
82
    , m_type(BouncingFeedback)
 
83
    , m_blinkingShader(0)
 
84
{
 
85
    for (int i = 0; i < 5; ++i) {
 
86
        m_bouncingTextures[i] = 0;
 
87
    }
 
88
    m_selection->claim(true);
 
89
    connect(m_startupInfo, SIGNAL(gotNewStartup(KStartupInfoId, KStartupInfoData)), SLOT(gotNewStartup(KStartupInfoId, KStartupInfoData)));
 
90
    connect(m_startupInfo, SIGNAL(gotRemoveStartup(KStartupInfoId, KStartupInfoData)), SLOT(gotRemoveStartup(KStartupInfoId, KStartupInfoData)));
 
91
    connect(m_startupInfo, SIGNAL(gotStartupChange(KStartupInfoId, KStartupInfoData)), SLOT(gotStartupChange(KStartupInfoId, KStartupInfoData)));
 
92
    connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)),
 
93
            this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)));
 
94
    reconfigure(ReconfigureAll);
 
95
}
 
96
 
 
97
StartupFeedbackEffect::~StartupFeedbackEffect()
 
98
{
 
99
    if (m_active) {
 
100
        effects->stopMousePolling();
 
101
    }
 
102
    for (int i = 0; i < 5; ++i) {
 
103
        delete m_bouncingTextures[i];
 
104
    }
 
105
    delete m_texture;
 
106
    delete m_blinkingShader;
 
107
}
 
108
 
 
109
bool StartupFeedbackEffect::supported()
 
110
{
 
111
    return effects->compositingType() == OpenGLCompositing;
 
112
}
 
113
 
 
114
void StartupFeedbackEffect::reconfigure(Effect::ReconfigureFlags flags)
 
115
{
 
116
    Q_UNUSED(flags)
 
117
    KConfig conf("klaunchrc", KConfig::NoGlobals);
 
118
    KConfigGroup c = conf.group("FeedbackStyle");
 
119
    const bool busyCursor = c.readEntry("BusyCursor", true);
 
120
 
 
121
    c = conf.group("BusyCursorSettings");
 
122
    m_startupInfo->setTimeout(c.readEntry("Timeout", 30));
 
123
    const bool busyBlinking = c.readEntry("Blinking", false);
 
124
    const bool busyBouncing = c.readEntry("Bouncing", true);
 
125
    if (!busyCursor)
 
126
        m_type = NoFeedback;
 
127
    else if (busyBouncing)
 
128
        m_type = BouncingFeedback;
 
129
    else if (busyBlinking) {
 
130
        m_type = BlinkingFeedback;
 
131
        if (ShaderManager::instance()->isValid()) {
 
132
            delete m_blinkingShader;
 
133
            m_blinkingShader = 0;
 
134
            const QString shader = KGlobal::dirs()->findResource("data", "kwin/blinking-startup-fragment.glsl");
 
135
            m_blinkingShader = ShaderManager::instance()->loadFragmentShader(ShaderManager::SimpleShader, shader);
 
136
            if (m_blinkingShader->isValid()) {
 
137
                kDebug(1212) << "Blinking Shader is valid";
 
138
            } else {
 
139
                kDebug(1212) << "Blinking Shader is not valid";
 
140
            }
 
141
        }
 
142
    } else
 
143
        m_type = PassiveFeedback;
 
144
    if (m_active) {
 
145
        stop();
 
146
        start(m_startups[ m_currentStartup ]);
 
147
    }
 
148
}
 
149
 
 
150
void StartupFeedbackEffect::prePaintScreen(ScreenPrePaintData& data, int time)
 
151
{
 
152
    if (m_active) {
 
153
        // need the unclipped version
 
154
        switch(m_type) {
 
155
        case BouncingFeedback:
 
156
            m_progress = (m_progress + time) % BOUNCE_DURATION;
 
157
            m_frame = qRound((qreal)m_progress / (qreal)BOUNCE_FRAME_DURATION) % BOUNCE_FRAMES;;
 
158
            break;
 
159
        case BlinkingFeedback:
 
160
            m_progress = (m_progress + time) % BLINKING_DURATION;
 
161
            m_frame = qRound((qreal)m_progress / (qreal)BLINKING_FRAME_DURATION) % BLINKING_FRAMES;
 
162
            break;
 
163
        default:
 
164
            break; // nothing
 
165
        }
 
166
        m_currentGeometry = feedbackRect();
 
167
        data.paint.unite(m_currentGeometry);
 
168
    }
 
169
    effects->prePaintScreen(data, time);
 
170
}
 
171
 
 
172
void StartupFeedbackEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data)
 
173
{
 
174
    effects->paintScreen(mask, region, data);
 
175
    if (m_active) {
 
176
        GLTexture* texture;
 
177
        switch(m_type) {
 
178
        case BouncingFeedback:
 
179
            texture = m_bouncingTextures[ FRAME_TO_BOUNCE_TEXTURE[ m_frame ]];
 
180
            break;
 
181
        case BlinkingFeedback: // fall through
 
182
        case PassiveFeedback:
 
183
            texture = m_texture;
 
184
            break;
 
185
        default:
 
186
            return; // safety
 
187
        }
 
188
#ifndef KWIN_HAVE_OPENGLES
 
189
        glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT);
 
190
#endif
 
191
        glEnable(GL_BLEND);
 
192
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
193
        texture->bind();
 
194
        bool useShader = false;
 
195
        if (m_type == BlinkingFeedback) {
 
196
            const QColor& blinkingColor = BLINKING_COLORS[ FRAME_TO_BLINKING_COLOR[ m_frame ]];
 
197
            if (m_blinkingShader && m_blinkingShader->isValid()) {
 
198
                useShader = true;
 
199
                ShaderManager::instance()->pushShader(m_blinkingShader);
 
200
                m_blinkingShader->setUniform("u_color", blinkingColor);
 
201
            } else {
 
202
#ifndef KWIN_HAVE_OPENGLES
 
203
                // texture transformation
 
204
                float color[4] = { blinkingColor.redF(), blinkingColor.greenF(), blinkingColor.blueF(), 1.0f };
 
205
                glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
 
206
                glColor4fv(color);
 
207
                glActiveTexture(GL_TEXTURE1);
 
208
                texture->bind();
 
209
                glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
 
210
                glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
 
211
                glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
 
212
                glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
 
213
                glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT);
 
214
                glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
 
215
                glActiveTexture(GL_TEXTURE0);
 
216
                glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color);
 
217
#endif
 
218
            }
 
219
        } else if (ShaderManager::instance()->isValid()) {
 
220
            useShader = true;
 
221
            ShaderManager::instance()->pushShader(ShaderManager::SimpleShader);
 
222
        }
 
223
        texture->render(m_currentGeometry, m_currentGeometry);
 
224
        if (useShader) {
 
225
            ShaderManager::instance()->popShader();
 
226
        }
 
227
        if (m_type == BlinkingFeedback && !useShader) {
 
228
#ifndef KWIN_HAVE_OPENGLES
 
229
            // resture states
 
230
            glActiveTexture(GL_TEXTURE1);
 
231
            texture->unbind();
 
232
            glActiveTexture(GL_TEXTURE0);
 
233
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
 
234
            glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
 
235
#endif
 
236
        }
 
237
        texture->unbind();
 
238
        glDisable(GL_BLEND);
 
239
#ifndef KWIN_HAVE_OPENGLES
 
240
        glPopAttrib();
 
241
#endif
 
242
    }
 
243
}
 
244
 
 
245
void StartupFeedbackEffect::postPaintScreen()
 
246
{
 
247
    if (m_active) {
 
248
        switch(m_type) {
 
249
        case BouncingFeedback: // fall through
 
250
        case BlinkingFeedback:
 
251
            // repaint the icon
 
252
            effects->addRepaint(m_currentGeometry);
 
253
            break;
 
254
        case PassiveFeedback: // fall through
 
255
        default:
 
256
            // no need to repaint - no change
 
257
            break;
 
258
        }
 
259
    }
 
260
    effects->postPaintScreen();
 
261
}
 
262
 
 
263
void StartupFeedbackEffect::slotMouseChanged(const QPoint& pos, const QPoint& oldpos, Qt::MouseButtons buttons,
 
264
        Qt::MouseButtons oldbuttons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers)
 
265
{
 
266
    Q_UNUSED(pos)
 
267
    Q_UNUSED(oldpos)
 
268
    Q_UNUSED(buttons)
 
269
    Q_UNUSED(oldbuttons)
 
270
    Q_UNUSED(modifiers)
 
271
    Q_UNUSED(oldmodifiers)
 
272
    if (m_active) {
 
273
        effects->addRepaint(m_currentGeometry);
 
274
        effects->addRepaint(feedbackRect());
 
275
    }
 
276
}
 
277
 
 
278
void StartupFeedbackEffect::gotNewStartup(const KStartupInfoId& id, const KStartupInfoData& data)
 
279
{
 
280
    const QString& icon = data.findIcon();
 
281
    m_currentStartup = id;
 
282
    m_startups[ id ] = icon;
 
283
    start(icon);
 
284
}
 
285
 
 
286
void StartupFeedbackEffect::gotRemoveStartup(const KStartupInfoId& id, const KStartupInfoData& data)
 
287
{
 
288
    Q_UNUSED( data )
 
289
    m_startups.remove(id);
 
290
    if (m_startups.count() == 0) {
 
291
        m_currentStartup = KStartupInfoId(); // null
 
292
        stop();
 
293
        return;
 
294
    }
 
295
    m_currentStartup = m_startups.begin().key();
 
296
    start(m_startups[ m_currentStartup ]);
 
297
}
 
298
 
 
299
void StartupFeedbackEffect::gotStartupChange(const KStartupInfoId& id, const KStartupInfoData& data)
 
300
{
 
301
    if (m_currentStartup == id) {
 
302
        const QString& icon = data.findIcon();
 
303
        if (!icon.isEmpty() && icon != m_startups[ m_currentStartup ]) {
 
304
            m_startups[ id ] = icon;
 
305
            start(icon);
 
306
        }
 
307
    }
 
308
}
 
309
 
 
310
void StartupFeedbackEffect::start(const QString& icon)
 
311
{
 
312
    if (m_type == NoFeedback)
 
313
        return;
 
314
    if (!m_active)
 
315
        effects->startMousePolling();
 
316
    m_active = true;
 
317
    QPixmap iconPixmap = KIconLoader::global()->loadIcon(icon, KIconLoader::Small, 0,
 
318
                         KIconLoader::DefaultState, QStringList(), 0, true);  // return null pixmap if not found
 
319
    if (iconPixmap.isNull())
 
320
        iconPixmap = SmallIcon("system-run");
 
321
    prepareTextures(iconPixmap);
 
322
    effects->addRepaintFull();
 
323
}
 
324
 
 
325
void StartupFeedbackEffect::stop()
 
326
{
 
327
    if (m_active)
 
328
        effects->stopMousePolling();
 
329
    m_active = false;
 
330
    switch(m_type) {
 
331
    case BouncingFeedback:
 
332
        for (int i = 0; i < 5; ++i) {
 
333
            delete m_bouncingTextures[i];
 
334
            m_bouncingTextures[i] = 0;
 
335
        }
 
336
        break;
 
337
    case BlinkingFeedback:
 
338
    case PassiveFeedback:
 
339
        delete m_texture;
 
340
        m_texture = 0;
 
341
        break;
 
342
    case NoFeedback:
 
343
        return; // don't want the full repaint
 
344
    default:
 
345
        break; // impossible
 
346
    }
 
347
    effects->addRepaintFull();
 
348
}
 
349
 
 
350
void StartupFeedbackEffect::prepareTextures(const QPixmap& pix)
 
351
{
 
352
    switch(m_type) {
 
353
    case BouncingFeedback:
 
354
        for (int i = 0; i < 5; ++i) {
 
355
            delete m_bouncingTextures[i];
 
356
            m_bouncingTextures[i] = new GLTexture(scalePixmap(pix, BOUNCE_SIZES[i]));
 
357
        }
 
358
        break;
 
359
    case BlinkingFeedback:
 
360
    case PassiveFeedback:
 
361
        m_texture = new GLTexture(pix);
 
362
        break;
 
363
    default:
 
364
        // for safety
 
365
        m_active = false;
 
366
        break;
 
367
    }
 
368
}
 
369
 
 
370
QImage StartupFeedbackEffect::scalePixmap(const QPixmap& pm, const QSize& size) const
 
371
{
 
372
    QImage scaled = pm.toImage().scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
 
373
    if (scaled.format() != QImage::Format_ARGB32_Premultiplied && scaled.format() != QImage::Format_ARGB32)
 
374
        scaled = scaled.convertToFormat(QImage::Format_ARGB32);
 
375
 
 
376
    QImage result(20, 20, QImage::Format_ARGB32);
 
377
    QPainter p(&result);
 
378
    p.setCompositionMode(QPainter::CompositionMode_Source);
 
379
    p.fillRect(result.rect(), Qt::transparent);
 
380
    p.drawImage((20 - size.width()) / 2, (20 - size.height()) / 2, scaled, 0, 0, size.width(), size.height());
 
381
    return result;
 
382
}
 
383
 
 
384
QRect StartupFeedbackEffect::feedbackRect() const
 
385
{
 
386
    int cursorSize = 0;
 
387
    cursorSize = XcursorGetDefaultSize(QX11Info::display());
 
388
    int xDiff;
 
389
    if (cursorSize <= 16)
 
390
        xDiff = 8 + 7;
 
391
    else if (cursorSize <= 32)
 
392
        xDiff = 16 + 7;
 
393
    else if (cursorSize <= 48)
 
394
        xDiff = 24 + 7;
 
395
    else
 
396
        xDiff = 32 + 7;
 
397
    int yDiff = xDiff;
 
398
    GLTexture* texture = 0;
 
399
    int yOffset = 0;
 
400
    switch(m_type) {
 
401
    case BouncingFeedback:
 
402
        texture = m_bouncingTextures[ FRAME_TO_BOUNCE_TEXTURE[ m_frame ]];
 
403
        yOffset = FRAME_TO_BOUNCE_YOFFSET[ m_frame ];
 
404
        break;
 
405
    case BlinkingFeedback: // fall through
 
406
    case PassiveFeedback:
 
407
        texture = m_texture;
 
408
        break;
 
409
    default:
 
410
        // nothing
 
411
        break;
 
412
    }
 
413
    const QPoint cursorPos = effects->cursorPos() + QPoint(xDiff, yDiff + yOffset);
 
414
    QRect rect;
 
415
    if( texture )
 
416
       rect = QRect(cursorPos, texture->size());
 
417
    return rect;
 
418
}
 
419
 
 
420
} // namespace