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>
28 #include <kapplication.h>
38 #define KDE_STARTUP_ICON "kmenu"
41 #include <X11/Xcursor/Xcursor.h>
44 enum kde_startup_status_enum
{ StartupPre
, StartupIn
, StartupDone
};
45 static kde_startup_status_enum kde_startup_status
= StartupPre
;
46 static Atom kde_splash_progress
;
48 StartupId::StartupId( QWidget
* parent
, const char* name
)
50 startup_info( KStartupInfo::CleanOnCantDetect
),
51 startup_widget( NULL
),
55 setObjectName( name
);
56 hide(); // is QWidget only because of x11Event()
57 if( kde_startup_status
== StartupPre
)
59 kde_splash_progress
= XInternAtom( QX11Info::display(), "_KDE_SPLASH_PROGRESS", False
);
60 XWindowAttributes attrs
;
61 XGetWindowAttributes( QX11Info::display(), QX11Info::appRootWindow(), &attrs
);
62 XSelectInput( QX11Info::display(), QX11Info::appRootWindow(), attrs
.your_event_mask
| SubstructureNotifyMask
);
63 kapp
->installX11EventFilter( this );
65 update_timer
.setSingleShot( true );
66 connect( &update_timer
, SIGNAL( timeout()), SLOT( update_startupid()));
67 connect( &startup_info
,
68 SIGNAL( gotNewStartup( const KStartupInfoId
&, const KStartupInfoData
& )),
69 SLOT( gotNewStartup( const KStartupInfoId
&, const KStartupInfoData
& )));
70 connect( &startup_info
,
71 SIGNAL( gotStartupChange( const KStartupInfoId
&, const KStartupInfoData
& )),
72 SLOT( gotStartupChange( const KStartupInfoId
&, const KStartupInfoData
& )));
73 connect( &startup_info
,
74 SIGNAL( gotRemoveStartup( const KStartupInfoId
&, const KStartupInfoData
& )),
75 SLOT( gotRemoveStartup( const KStartupInfoId
& )));
78 StartupId::~StartupId()
83 void StartupId::configure()
85 startup_info
.setTimeout( KLaunchSettings::timeout());
86 blinking
= KLaunchSettings::blinking();
87 bouncing
= KLaunchSettings::bouncing();
90 void StartupId::gotNewStartup( const KStartupInfoId
& id_P
, const KStartupInfoData
& data_P
)
92 QString icon
= data_P
.findIcon();
93 current_startup
= id_P
;
94 startups
[ id_P
] = icon
;
95 start_startupid( icon
);
98 void StartupId::gotStartupChange( const KStartupInfoId
& id_P
, const KStartupInfoData
& data_P
)
100 if( current_startup
== id_P
)
102 QString icon
= data_P
.findIcon();
103 if( !icon
.isEmpty() && icon
!= startups
[ current_startup
] )
105 startups
[ id_P
] = icon
;
106 start_startupid( icon
);
111 void StartupId::gotRemoveStartup( const KStartupInfoId
& id_P
)
113 startups
.remove( id_P
);
114 if( startups
.count() == 0 )
116 current_startup
= KStartupInfoId(); // null
117 if( kde_startup_status
== StartupIn
)
118 start_startupid( KDE_STARTUP_ICON
);
123 current_startup
= startups
.begin().key();
124 start_startupid( startups
[ current_startup
] );
127 bool StartupId::x11Event( XEvent
* e
)
129 if( e
->type
== ClientMessage
&& e
->xclient
.window
== QX11Info::appRootWindow()
130 && e
->xclient
.message_type
== kde_splash_progress
)
132 const char* s
= e
->xclient
.data
.b
;
133 if( strcmp( s
, "kicker" ) == 0 && kde_startup_status
== StartupPre
)
135 kde_startup_status
= StartupIn
;
136 if( startups
.count() == 0 )
137 start_startupid( KDE_STARTUP_ICON
);
138 // 60(?) sec timeout - shouldn't be hopefully needed anyway, ksmserver should have it too
139 QTimer::singleShot( 60000, this, SLOT( finishKDEStartup()));
141 else if( strcmp( s
, "session ready" ) == 0 && kde_startup_status
< StartupDone
)
142 QTimer::singleShot( 2000, this, SLOT( finishKDEStartup()));
147 void StartupId::finishKDEStartup()
149 kde_startup_status
= StartupDone
;
150 kapp
->removeX11EventFilter( this );
151 if( startups
.count() == 0 )
155 void StartupId::stop_startupid()
157 delete startup_widget
;
158 startup_widget
= NULL
;
161 i
< NUM_BLINKING_PIXMAPS
;
163 pixmaps
[ i
] = QPixmap(); // null
167 static QPixmap
scalePixmap( const QPixmap
& pm
, int w
, int h
)
169 QImage scaled
= pm
.toImage().scaled(w
, h
, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
);
170 if (scaled
.format() != QImage::Format_ARGB32_Premultiplied
&& scaled
.format() != QImage::Format_ARGB32
)
171 scaled
= scaled
.convertToFormat(QImage::Format_ARGB32_Premultiplied
);
173 QImage
result(20, 20, QImage::Format_ARGB32_Premultiplied
);
175 p
.setCompositionMode(QPainter::CompositionMode_Source
);
176 p
.fillRect(result
.rect(), Qt::transparent
);
177 p
.drawImage((20 - w
) / 2, (20 - h
) / 2, scaled
, 0, 0, w
, h
);
178 return QPixmap::fromImage(result
);
181 void StartupId::start_startupid( const QString
& icon_P
)
184 const QColor startup_colors
[ StartupId::NUM_BLINKING_PIXMAPS
]
185 = { Qt::black
, Qt::darkGray
, Qt::lightGray
, Qt::white
, Qt::white
};
188 QPixmap icon_pixmap
= KIconLoader::global()->loadIcon( icon_P
, K3Icon::Small
, 0,
189 K3Icon::DefaultState
, QStringList(), 0, true ); // return null pixmap if not found
190 if( icon_pixmap
.isNull())
191 icon_pixmap
= SmallIcon( "exec" );
192 if( startup_widget
== NULL
)
194 startup_widget
= new QWidget( 0, Qt::X11BypassWindowManagerHint
);
195 XSetWindowAttributes attr
;
196 attr
.save_under
= True
; // useful saveunder if possible to avoid redrawing
197 XChangeWindowAttributes( QX11Info::display(), startup_widget
->winId(), CWSaveUnder
, &attr
);
199 startup_widget
->resize( icon_pixmap
.width(), icon_pixmap
.height());
202 startup_widget
->clearMask();
203 int window_w
= icon_pixmap
.width();
204 int window_h
= icon_pixmap
.height();
206 i
< NUM_BLINKING_PIXMAPS
;
209 pixmaps
[ i
] = QPixmap( window_w
, window_h
);
210 pixmaps
[ i
].fill( startup_colors
[ i
] );
211 QPainter
p( &pixmaps
[ i
] );
212 p
.drawPixmap( 0, 0, icon_pixmap
);
219 startup_widget
->resize( 20, 20 );
220 pixmaps
[ 0 ] = scalePixmap( icon_pixmap
, 16, 16 );
221 pixmaps
[ 1 ] = scalePixmap( icon_pixmap
, 14, 18 );
222 pixmaps
[ 2 ] = scalePixmap( icon_pixmap
, 12, 20 );
223 pixmaps
[ 3 ] = scalePixmap( icon_pixmap
, 18, 14 );
224 pixmaps
[ 4 ] = scalePixmap( icon_pixmap
, 20, 12 );
229 if( !icon_pixmap
.mask().isNull() )
230 startup_widget
->setMask( icon_pixmap
.mask() );
232 startup_widget
->clearMask();
235 palette
.setBrush( startup_widget
->backgroundRole(), QBrush( icon_pixmap
) );
236 startup_widget
->setPalette( palette
);
237 startup_widget
->update();
244 const int X_DIFF
= 15;
245 const int Y_DIFF
= 15;
246 const int color_to_pixmap
[] = { 0, 1, 2, 3, 2, 1 };
247 const int frame_to_yoffset
[] =
249 -5, -1, 2, 5, 8, 10, 12, 13, 15, 15, 15, 15, 14, 12, 10, 8, 5, 2, -1, -5
251 const int frame_to_pixmap
[] =
253 0, 0, 0, 1, 2, 2, 1, 0, 3, 4, 4, 3, 0, 1, 2, 2, 1, 0, 0, 0
257 void StartupId::update_startupid()
263 palette
.setBrush( startup_widget
->backgroundRole(), QBrush( pixmaps
[ color_to_pixmap
[ color_index
]] ) );
264 startup_widget
->setPalette( palette
);
265 if( ++color_index
>= ( sizeof( color_to_pixmap
) / sizeof( color_to_pixmap
[ 0 ] )))
270 yoffset
= frame_to_yoffset
[ frame
];
271 QPixmap pm
= pixmaps
[ frame_to_pixmap
[ frame
] ];
273 palette
.setBrush( startup_widget
->backgroundRole(), QBrush( pm
) );
274 startup_widget
->setPalette( palette
);
275 if ( !pm
.mask().isNull() )
276 startup_widget
->setMask( pm
.mask() );
278 startup_widget
->clearMask();
279 if ( ++frame
>= ( sizeof( frame_to_yoffset
) / sizeof( frame_to_yoffset
[ 0 ] ) ) )
282 Window dummy1
, dummy2
;
286 if( !XQueryPointer( QX11Info::display(), QX11Info::appRootWindow(), &dummy1
, &dummy2
, &x
, &y
, &dummy3
, &dummy4
, &dummy5
))
288 startup_widget
->hide();
289 update_timer
.start( 100 );
292 QPoint
c_pos( x
, y
);
295 cursor_size
= XcursorGetDefaultSize( QX11Info::display());
298 if( cursor_size
<= 16 )
300 else if( cursor_size
<= 32 )
302 else if( cursor_size
<= 48 )
307 if( startup_widget
->x() != c_pos
.x() + X_DIFF
308 || startup_widget
->y() != c_pos
.y() + Y_DIFF
+ yoffset
)
309 startup_widget
->move( c_pos
.x() + X_DIFF
, c_pos
.y() + Y_DIFF
+ yoffset
);
310 startup_widget
->show();
311 XRaiseWindow( QX11Info::display(), startup_widget
->winId());
312 update_timer
.start( bouncing
? 30 : 100 );
313 QApplication::flush();
316 #include "startupid.moc"