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