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

« back to all changes in this revision

Viewing changes to krunner/startupid.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
/* This file is part of the KDE project
 
2
   Copyright (C) 2001 Lubos Lunak <l.lunak@kde.org>
 
3
   Copyright (C) 2010 Martin Gräßlin <kde@martin-graesslin.com>
 
4
 
 
5
   This library is free software; you can redistribute it and/or
 
6
   modify it under the terms of the GNU Library General Public
 
7
   License as published by the Free Software Foundation; either
 
8
   version 2 of the License, or (at your option) any later version.
 
9
 
 
10
   This library is distributed in the hope that it will be useful,
 
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
13
   Library General Public License for more details.
 
14
 
 
15
   You should have received a copy of the GNU Library General Public License
 
16
   along with this library; see the file COPYING.LIB.  If not, write to
 
17
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
18
   Boston, MA 02110-1301, USA.
 
19
*/
 
20
 
 
21
#include "startupid.h"
 
22
 
 
23
#include <config-X11.h>
 
24
 
 
25
#include "klaunchsettings.h"
 
26
 
 
27
#include <kiconloader.h>
 
28
#include <kmanagerselection.h>
 
29
#include <QCursor>
 
30
#include <kapplication.h>
 
31
#include <QImage>
 
32
#include <QBitmap>
 
33
//Added by qt3to4:
 
34
#include <QPixmap>
 
35
#include <QPainter>
 
36
#include <kconfig.h>
 
37
#include <X11/Xlib.h>
 
38
#include <QX11Info>
 
39
#include <X11/Xutil.h>
 
40
#include <X11/Xatom.h>
 
41
#include <X11/extensions/shape.h>
 
42
 
 
43
#define KDE_STARTUP_ICON QLatin1String( "kmenu" )
 
44
 
 
45
#ifdef HAVE_XCURSOR
 
46
#include <X11/Xcursor/Xcursor.h>
 
47
#endif
 
48
 
 
49
enum kde_startup_status_enum { StartupPre, StartupIn, StartupDone };
 
50
static kde_startup_status_enum kde_startup_status = StartupPre;
 
51
static Atom kde_splash_progress;
 
52
 
 
53
StartupId::StartupId( QWidget* parent, const char* name )
 
54
    :   QWidget( parent ),
 
55
        startup_info( KStartupInfo::CleanOnCantDetect ),
 
56
        startup_window( None ),
 
57
        blinking( true ),
 
58
        bouncing( false ),
 
59
        selection_watcher( new KSelectionWatcher( "_KDE_STARTUP_FEEDBACK", -1, this ))
 
60
    {
 
61
        setObjectName( QLatin1String( name ) );
 
62
    hide(); // is QWidget only because of x11Event()
 
63
    if( kde_startup_status == StartupPre )
 
64
        {
 
65
        kde_splash_progress = XInternAtom( QX11Info::display(), "_KDE_SPLASH_PROGRESS", False );
 
66
        XWindowAttributes attrs;
 
67
        XGetWindowAttributes( QX11Info::display(), QX11Info::appRootWindow(), &attrs);
 
68
        XSelectInput( QX11Info::display(), QX11Info::appRootWindow(), attrs.your_event_mask | SubstructureNotifyMask);
 
69
        kapp->installX11EventFilter( this );
 
70
        }
 
71
    update_timer.setSingleShot( true );
 
72
    connect( &update_timer, SIGNAL( timeout()), SLOT( update_startupid()));
 
73
    connect( &startup_info,
 
74
        SIGNAL( gotNewStartup( const KStartupInfoId&, const KStartupInfoData& )),
 
75
        SLOT( gotNewStartup( const KStartupInfoId&, const KStartupInfoData& )));
 
76
    connect( &startup_info,
 
77
        SIGNAL( gotStartupChange( const KStartupInfoId&, const KStartupInfoData& )),
 
78
        SLOT( gotStartupChange( const KStartupInfoId&, const KStartupInfoData& )));
 
79
    connect( &startup_info,
 
80
        SIGNAL( gotRemoveStartup( const KStartupInfoId&, const KStartupInfoData& )),
 
81
        SLOT( gotRemoveStartup( const KStartupInfoId& )));
 
82
    connect( selection_watcher, SIGNAL(newOwner(Window)), SLOT(newOwner()));
 
83
    connect( selection_watcher, SIGNAL(lostOwner()), SLOT(lostOwner()));
 
84
    active_selection = ( selection_watcher->owner() != None );
 
85
    }
 
86
 
 
87
StartupId::~StartupId()
 
88
    {
 
89
    stop_startupid();
 
90
    }
 
91
 
 
92
void StartupId::configure()
 
93
    {
 
94
    startup_info.setTimeout( KLaunchSettings::timeout());
 
95
    blinking = KLaunchSettings::blinking();
 
96
    bouncing = KLaunchSettings::bouncing();
 
97
    }
 
98
 
 
99
void StartupId::gotNewStartup( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
 
100
    {
 
101
    if( active_selection )
 
102
        return;
 
103
    QString icon = data_P.findIcon();
 
104
    current_startup = id_P;
 
105
    startups[ id_P ] = icon;
 
106
    start_startupid( icon );
 
107
    }
 
108
 
 
109
void StartupId::gotStartupChange( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
 
110
    {
 
111
    if( active_selection )
 
112
        return;
 
113
    if( current_startup == id_P )
 
114
        {
 
115
        QString icon = data_P.findIcon();
 
116
        if( !icon.isEmpty() && icon != startups[ current_startup ] )
 
117
            {
 
118
            startups[ id_P ] = icon;
 
119
            start_startupid( icon );
 
120
            }
 
121
        }
 
122
    }
 
123
 
 
124
void StartupId::gotRemoveStartup( const KStartupInfoId& id_P )
 
125
    {
 
126
    if( active_selection )
 
127
        return;
 
128
    startups.remove( id_P );
 
129
    if( startups.count() == 0 )
 
130
        {
 
131
        current_startup = KStartupInfoId(); // null
 
132
        if( kde_startup_status == StartupIn )
 
133
            start_startupid( KDE_STARTUP_ICON );
 
134
        else
 
135
            stop_startupid();
 
136
        return;
 
137
        }
 
138
    current_startup = startups.begin().key();
 
139
    start_startupid( startups[ current_startup ] );
 
140
    }
 
141
 
 
142
bool StartupId::x11Event( XEvent* e )
 
143
    {
 
144
    if( e->type == ClientMessage && e->xclient.window == QX11Info::appRootWindow()
 
145
        && e->xclient.message_type == kde_splash_progress )
 
146
        {
 
147
        const char* s = e->xclient.data.b;
 
148
        if( strcmp( s, "desktop" ) == 0 && kde_startup_status == StartupPre )
 
149
            {
 
150
            kde_startup_status = StartupIn;
 
151
            if( startups.count() == 0 )
 
152
                start_startupid( KDE_STARTUP_ICON );
 
153
            // 60(?) sec timeout - shouldn't be hopefully needed anyway, ksmserver should have it too
 
154
            QTimer::singleShot( 60000, this, SLOT( finishKDEStartup()));
 
155
            }
 
156
        else if( strcmp( s, "ready" ) == 0 && kde_startup_status < StartupDone )
 
157
            QTimer::singleShot( 2000, this, SLOT( finishKDEStartup()));
 
158
        }
 
159
    return false;
 
160
    }
 
161
 
 
162
void StartupId::finishKDEStartup()
 
163
    {
 
164
    kde_startup_status = StartupDone;
 
165
    kapp->removeX11EventFilter( this );
 
166
    if( startups.count() == 0 )
 
167
        stop_startupid();
 
168
    }
 
169
 
 
170
void StartupId::stop_startupid()
 
171
    {
 
172
    if( startup_window != None )
 
173
        XDestroyWindow( QX11Info::display(), startup_window );
 
174
    startup_window = None;
 
175
    if( blinking )
 
176
        for( int i = 0;
 
177
             i < NUM_BLINKING_PIXMAPS;
 
178
             ++i )
 
179
            pixmaps[ i ] = QPixmap(); // null
 
180
    update_timer.stop();
 
181
    }
 
182
 
 
183
static QPixmap scalePixmap( const QPixmap& pm, int w, int h )
 
184
{
 
185
    QImage scaled = pm.toImage().scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
 
186
    if (scaled.format() != QImage::Format_ARGB32_Premultiplied && scaled.format() != QImage::Format_ARGB32)
 
187
        scaled = scaled.convertToFormat(QImage::Format_ARGB32_Premultiplied);
 
188
 
 
189
    QImage result(20, 20, QImage::Format_ARGB32_Premultiplied);
 
190
    QPainter p(&result);
 
191
    p.setCompositionMode(QPainter::CompositionMode_Source);
 
192
    p.fillRect(result.rect(), Qt::transparent);
 
193
    p.drawImage((20 - w) / 2, (20 - h) / 2, scaled, 0, 0, w, h);
 
194
    return QPixmap::fromImage(result);
 
195
}
 
196
 
 
197
// Transparent images are converted to 32bpp pixmaps, but
 
198
// setting those as window background needs ARGB visual
 
199
// and compositing - convert to 24bpp, at least for now.
 
200
static QPixmap make24bpp( const QPixmap& p )
 
201
    {
 
202
    QPixmap ret( p.size());
 
203
    QPainter pt( &ret );
 
204
    pt.drawPixmap( 0, 0, p );
 
205
    pt.end();
 
206
    ret.setMask( p.mask());
 
207
    return ret;
 
208
    }
 
209
 
 
210
void StartupId::start_startupid( const QString& icon_P )
 
211
    {
 
212
 
 
213
    const QColor startup_colors[ StartupId::NUM_BLINKING_PIXMAPS ]
 
214
    = { Qt::black, Qt::darkGray, Qt::lightGray, Qt::white, Qt::white };
 
215
 
 
216
 
 
217
    QPixmap icon_pixmap = KIconLoader::global()->loadIcon( icon_P, KIconLoader::Small, 0,
 
218
        KIconLoader::DefaultState, QStringList(), 0, true ); // return null pixmap if not found
 
219
    if( icon_pixmap.isNull())
 
220
        icon_pixmap = SmallIcon( QLatin1String( "system-run" ) );
 
221
    if( startup_window == None )
 
222
        {
 
223
        XSetWindowAttributes attrs;
 
224
        attrs.override_redirect = True;
 
225
        attrs.save_under = True; // useful saveunder if possible to avoid redrawing
 
226
        attrs.colormap = QX11Info::appColormap();
 
227
        attrs.background_pixel = WhitePixel( QX11Info::display(), QX11Info::appScreen());
 
228
        attrs.border_pixel = BlackPixel( QX11Info::display(), QX11Info::appScreen());
 
229
        startup_window = XCreateWindow( QX11Info::display(), DefaultRootWindow( QX11Info::display()),
 
230
            0, 0, 1, 1, 0, QX11Info::appDepth(), InputOutput, static_cast< Visual* >( QX11Info::appVisual()),
 
231
            CWOverrideRedirect | CWSaveUnder | CWColormap | CWBackPixel | CWBorderPixel, &attrs );
 
232
        XClassHint class_hint;
 
233
        QByteArray cls = qAppName().toLatin1();
 
234
        class_hint.res_name = cls.data();
 
235
        class_hint.res_class = const_cast< char* >( QX11Info::appClass());
 
236
        XSetWMProperties( QX11Info::display(), startup_window, NULL, NULL, NULL, 0, NULL, NULL, &class_hint );
 
237
        XChangeProperty( QX11Info::display(), winId(),
 
238
            XInternAtom( QX11Info::display(), "WM_WINDOW_ROLE", False ), XA_STRING, 8, PropModeReplace,
 
239
            (unsigned char *)"startupfeedback", strlen( "startupfeedback" ));
 
240
        }
 
241
    XResizeWindow( QX11Info::display(), startup_window, icon_pixmap.width(), icon_pixmap.height());
 
242
    if( blinking )
 
243
        { // no mask
 
244
        XShapeCombineMask( QX11Info::display(), startup_window, ShapeBounding, 0, 0, None, ShapeSet );
 
245
        int window_w = icon_pixmap.width();
 
246
        int window_h = icon_pixmap.height();
 
247
        for( int i = 0;
 
248
             i < NUM_BLINKING_PIXMAPS;
 
249
             ++i )
 
250
            {
 
251
            pixmaps[ i ] = QPixmap( window_w, window_h );
 
252
            pixmaps[ i ].fill( startup_colors[ i ] );
 
253
            QPainter p( &pixmaps[ i ] );
 
254
            p.drawPixmap( 0, 0, icon_pixmap );
 
255
            p.end();
 
256
            }
 
257
        color_index = 0;
 
258
        }
 
259
    else if( bouncing )
 
260
        {
 
261
        XResizeWindow( QX11Info::display(), startup_window, 20, 20 );
 
262
        pixmaps[ 0 ] = make24bpp( scalePixmap( icon_pixmap, 16, 16 ));
 
263
        pixmaps[ 1 ] = make24bpp( scalePixmap( icon_pixmap, 14, 18 ));
 
264
        pixmaps[ 2 ] = make24bpp( scalePixmap( icon_pixmap, 12, 20 ));
 
265
        pixmaps[ 3 ] = make24bpp( scalePixmap( icon_pixmap, 18, 14 ));
 
266
        pixmaps[ 4 ] = make24bpp( scalePixmap( icon_pixmap, 20, 12 ));
 
267
        frame = 0;
 
268
        }
 
269
    else
 
270
        {
 
271
        icon_pixmap = make24bpp( icon_pixmap );
 
272
        if( !icon_pixmap.mask().isNull() ) // set mask
 
273
            XShapeCombineMask( QX11Info::display(), startup_window, ShapeBounding, 0, 0,
 
274
                icon_pixmap.mask().handle(), ShapeSet );
 
275
        else // clear mask
 
276
            XShapeCombineMask( QX11Info::display(), startup_window, ShapeBounding, 0, 0, None, ShapeSet );
 
277
        XSetWindowBackgroundPixmap( QX11Info::display(), startup_window, icon_pixmap.handle());
 
278
        XClearWindow( QX11Info::display(), startup_window );
 
279
        }
 
280
    update_startupid();
 
281
    }
 
282
 
 
283
namespace
 
284
{
 
285
const int X_DIFF = 15;
 
286
const int Y_DIFF = 15;
 
287
const int color_to_pixmap[] = { 0, 1, 2, 3, 2, 1 };
 
288
const int frame_to_yoffset[] =
 
289
  {
 
290
    -5, -1, 2, 5, 8, 10, 12, 13, 15, 15, 15, 15, 14, 12, 10, 8, 5, 2, -1, -5
 
291
  };
 
292
const int frame_to_pixmap[] =
 
293
  {
 
294
    0, 0, 0, 1, 2, 2, 1, 0, 3, 4, 4, 3, 0, 1, 2, 2, 1, 0, 0, 0
 
295
  };
 
296
}
 
297
 
 
298
void StartupId::update_startupid()
 
299
    {
 
300
    int yoffset = 0;
 
301
    if( blinking )
 
302
        {
 
303
        XSetWindowBackgroundPixmap( QX11Info::display(), startup_window,
 
304
            pixmaps[ color_to_pixmap[ color_index ]].handle());
 
305
        XClearWindow( QX11Info::display(), startup_window );
 
306
        if( ++color_index >= ( sizeof( color_to_pixmap ) / sizeof( color_to_pixmap[ 0 ] )))
 
307
            color_index = 0;
 
308
        }
 
309
    else if( bouncing )
 
310
        {
 
311
        yoffset = frame_to_yoffset[ frame ];
 
312
        QPixmap pixmap = pixmaps[ frame_to_pixmap[ frame ] ];
 
313
        XSetWindowBackgroundPixmap( QX11Info::display(), startup_window, pixmap.handle());
 
314
        XClearWindow( QX11Info::display(), startup_window );
 
315
        if ( !pixmap.mask().isNull() ) // set mask
 
316
            XShapeCombineMask( QX11Info::display(), startup_window, ShapeBounding, 0, 0,
 
317
                pixmap.mask().handle(), ShapeSet );
 
318
        else // clear mask
 
319
            XShapeCombineMask( QX11Info::display(), startup_window, ShapeBounding, 0, 0, None, ShapeSet );
 
320
        if ( ++frame >= ( sizeof( frame_to_yoffset ) / sizeof( frame_to_yoffset[ 0 ] ) ) )
 
321
            frame = 0;
 
322
        }
 
323
    Window dummy1, dummy2;
 
324
    int x, y;
 
325
    int dummy3, dummy4;
 
326
    unsigned int dummy5;
 
327
    if( !XQueryPointer( QX11Info::display(), QX11Info::appRootWindow(), &dummy1, &dummy2, &x, &y, &dummy3, &dummy4, &dummy5 ))
 
328
        {
 
329
        XUnmapWindow( QX11Info::display(), startup_window );
 
330
        update_timer.start( 100 );
 
331
        return;
 
332
        }
 
333
    QPoint c_pos( x, y );
 
334
    int cursor_size = 0;
 
335
#ifdef HAVE_XCURSOR
 
336
    cursor_size = XcursorGetDefaultSize( QX11Info::display());
 
337
#endif
 
338
    int X_DIFF;
 
339
    if( cursor_size <= 16 )
 
340
        X_DIFF = 8 + 7;
 
341
    else if( cursor_size <= 32 )
 
342
        X_DIFF = 16 + 7;
 
343
    else if( cursor_size <= 48 )
 
344
        X_DIFF = 24 + 7;
 
345
    else
 
346
        X_DIFF = 32 + 7;
 
347
    int Y_DIFF = X_DIFF;
 
348
    XMoveWindow( QX11Info::display(), startup_window, c_pos.x() + X_DIFF, c_pos.y() + Y_DIFF + yoffset );
 
349
    XMapWindow( QX11Info::display(), startup_window );
 
350
    XRaiseWindow( QX11Info::display(), startup_window );
 
351
    update_timer.start( bouncing ? 30 : 100 );
 
352
    QApplication::flush();
 
353
    }
 
354
 
 
355
void StartupId::newOwner()
 
356
    {
 
357
    active_selection = true;
 
358
    }
 
359
 
 
360
void StartupId::lostOwner()
 
361
    {
 
362
    active_selection = false;
 
363
    }
 
364
 
 
365
#include "startupid.moc"