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 <config-X11.h>
22 #include "startupid.h"
23 #include "klaunchsettings.h"
25 #include <kiconloader.h>
27 #include <kapplication.h>
37 #define KDE_STARTUP_ICON "kmenu"
40 #include <X11/Xcursor/Xcursor.h>
43 enum kde_startup_status_enum
{ StartupPre
, StartupIn
, StartupDone
};
44 static kde_startup_status_enum kde_startup_status
= StartupPre
;
45 static Atom kde_splash_progress
;
47 StartupId::StartupId( QWidget
* parent
, const char* name
)
49 startup_info( KStartupInfo::CleanOnCantDetect
),
50 startup_widget( NULL
),
54 setObjectName( name
);
55 hide(); // is QWidget only because of x11Event()
56 if( kde_startup_status
== StartupPre
)
58 kde_splash_progress
= XInternAtom( QX11Info::display(), "_KDE_SPLASH_PROGRESS", False
);
59 XWindowAttributes attrs
;
60 XGetWindowAttributes( QX11Info::display(), QX11Info::appRootWindow(), &attrs
);
61 XSelectInput( QX11Info::display(), QX11Info::appRootWindow(), attrs
.your_event_mask
| SubstructureNotifyMask
);
62 kapp
->installX11EventFilter( this );
64 update_timer
.setSingleShot( true );
65 connect( &update_timer
, SIGNAL( timeout()), SLOT( update_startupid()));
66 connect( &startup_info
,
67 SIGNAL( gotNewStartup( const KStartupInfoId
&, const KStartupInfoData
& )),
68 SLOT( gotNewStartup( const KStartupInfoId
&, const KStartupInfoData
& )));
69 connect( &startup_info
,
70 SIGNAL( gotStartupChange( const KStartupInfoId
&, const KStartupInfoData
& )),
71 SLOT( gotStartupChange( const KStartupInfoId
&, const KStartupInfoData
& )));
72 connect( &startup_info
,
73 SIGNAL( gotRemoveStartup( const KStartupInfoId
&, const KStartupInfoData
& )),
74 SLOT( gotRemoveStartup( const KStartupInfoId
& )));
77 StartupId::~StartupId()
82 void StartupId::configure()
84 startup_info
.setTimeout( KLaunchSettings::timeout());
85 blinking
= KLaunchSettings::blinking();
86 bouncing
= KLaunchSettings::bouncing();
89 void StartupId::gotNewStartup( const KStartupInfoId
& id_P
, const KStartupInfoData
& data_P
)
91 QString icon
= data_P
.findIcon();
92 current_startup
= id_P
;
93 startups
[ id_P
] = icon
;
94 start_startupid( icon
);
97 void StartupId::gotStartupChange( const KStartupInfoId
& id_P
, const KStartupInfoData
& data_P
)
99 if( current_startup
== id_P
)
101 QString icon
= data_P
.findIcon();
102 if( !icon
.isEmpty() && icon
!= startups
[ current_startup
] )
104 startups
[ id_P
] = icon
;
105 start_startupid( icon
);
110 void StartupId::gotRemoveStartup( const KStartupInfoId
& id_P
)
112 startups
.remove( id_P
);
113 if( startups
.count() == 0 )
115 current_startup
= KStartupInfoId(); // null
116 if( kde_startup_status
== StartupIn
)
117 start_startupid( KDE_STARTUP_ICON
);
122 current_startup
= startups
.begin().key();
123 start_startupid( startups
[ current_startup
] );
126 bool StartupId::x11Event( XEvent
* e
)
128 if( e
->type
== ClientMessage
&& e
->xclient
.window
== QX11Info::appRootWindow()
129 && e
->xclient
.message_type
== kde_splash_progress
)
131 const char* s
= e
->xclient
.data
.b
;
132 if( strcmp( s
, "kicker" ) == 0 && kde_startup_status
== StartupPre
)
134 kde_startup_status
= StartupIn
;
135 if( startups
.count() == 0 )
136 start_startupid( KDE_STARTUP_ICON
);
137 // 60(?) sec timeout - shouldn't be hopefully needed anyway, ksmserver should have it too
138 QTimer::singleShot( 60000, this, SLOT( finishKDEStartup()));
140 else if( strcmp( s
, "session ready" ) == 0 && kde_startup_status
< StartupDone
)
141 QTimer::singleShot( 2000, this, SLOT( finishKDEStartup()));
146 void StartupId::finishKDEStartup()
148 kde_startup_status
= StartupDone
;
149 kapp
->removeX11EventFilter( this );
150 if( startups
.count() == 0 )
154 void StartupId::stop_startupid()
156 delete startup_widget
;
157 startup_widget
= NULL
;
160 i
< NUM_BLINKING_PIXMAPS
;
162 pixmaps
[ i
] = QPixmap(); // null
166 static QPixmap
scalePixmap( const QPixmap
& pm
, int w
, int h
)
168 QImage scaled
= pm
.toImage().scaled(w
, h
, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
);
169 if (scaled
.format() != QImage::Format_ARGB32_Premultiplied
&& scaled
.format() != QImage::Format_ARGB32
)
170 scaled
= scaled
.convertToFormat(QImage::Format_ARGB32_Premultiplied
);
172 QImage
result(20, 20, QImage::Format_ARGB32_Premultiplied
);
174 p
.setCompositionMode(QPainter::CompositionMode_Source
);
175 p
.fillRect(result
.rect(), Qt::transparent
);
176 p
.drawImage((20 - w
) / 2, (20 - h
) / 2, scaled
, 0, 0, w
, h
);
177 return QPixmap::fromImage(result
);
180 void StartupId::start_startupid( const QString
& icon_P
)
183 const QColor startup_colors
[ StartupId::NUM_BLINKING_PIXMAPS
]
184 = { Qt::black
, Qt::darkGray
, Qt::lightGray
, Qt::white
, Qt::white
};
187 QPixmap icon_pixmap
= KIconLoader::global()->loadIcon( icon_P
, K3Icon::Small
, 0,
188 K3Icon::DefaultState
, 0, true ); // return null pixmap if not found
189 if( icon_pixmap
.isNull())
190 icon_pixmap
= SmallIcon( "exec" );
191 if( startup_widget
== NULL
)
193 startup_widget
= new QWidget( 0, Qt::X11BypassWindowManagerHint
);
194 XSetWindowAttributes attr
;
195 attr
.save_under
= True
; // useful saveunder if possible to avoid redrawing
196 XChangeWindowAttributes( QX11Info::display(), startup_widget
->winId(), CWSaveUnder
, &attr
);
198 startup_widget
->resize( icon_pixmap
.width(), icon_pixmap
.height());
201 startup_widget
->clearMask();
202 int window_w
= icon_pixmap
.width();
203 int window_h
= icon_pixmap
.height();
205 i
< NUM_BLINKING_PIXMAPS
;
208 pixmaps
[ i
] = QPixmap( window_w
, window_h
);
209 pixmaps
[ i
].fill( startup_colors
[ i
] );
210 QPainter
p( &pixmaps
[ i
] );
211 p
.drawPixmap( 0, 0, icon_pixmap
);
218 startup_widget
->resize( 20, 20 );
219 pixmaps
[ 0 ] = scalePixmap( icon_pixmap
, 16, 16 );
220 pixmaps
[ 1 ] = scalePixmap( icon_pixmap
, 14, 18 );
221 pixmaps
[ 2 ] = scalePixmap( icon_pixmap
, 12, 20 );
222 pixmaps
[ 3 ] = scalePixmap( icon_pixmap
, 18, 14 );
223 pixmaps
[ 4 ] = scalePixmap( icon_pixmap
, 20, 12 );
228 if( !icon_pixmap
.mask().isNull() )
229 startup_widget
->setMask( icon_pixmap
.mask() );
231 startup_widget
->clearMask();
234 palette
.setBrush( startup_widget
->backgroundRole(), QBrush( icon_pixmap
) );
235 startup_widget
->setPalette( palette
);
236 startup_widget
->update();
243 const int X_DIFF
= 15;
244 const int Y_DIFF
= 15;
245 const int color_to_pixmap
[] = { 0, 1, 2, 3, 2, 1 };
246 const int frame_to_yoffset
[] =
248 -5, -1, 2, 5, 8, 10, 12, 13, 15, 15, 15, 15, 14, 12, 10, 8, 5, 2, -1, -5
250 const int frame_to_pixmap
[] =
252 0, 0, 0, 1, 2, 2, 1, 0, 3, 4, 4, 3, 0, 1, 2, 2, 1, 0, 0, 0
256 void StartupId::update_startupid()
262 palette
.setBrush( startup_widget
->backgroundRole(), QBrush( pixmaps
[ color_to_pixmap
[ color_index
]] ) );
263 startup_widget
->setPalette( palette
);
264 if( ++color_index
>= ( sizeof( color_to_pixmap
) / sizeof( color_to_pixmap
[ 0 ] )))
269 yoffset
= frame_to_yoffset
[ frame
];
270 QPixmap pm
= pixmaps
[ frame_to_pixmap
[ frame
] ];
272 palette
.setBrush( startup_widget
->backgroundRole(), QBrush( pm
) );
273 startup_widget
->setPalette( palette
);
274 if ( !pm
.mask().isNull() )
275 startup_widget
->setMask( pm
.mask() );
277 startup_widget
->clearMask();
278 if ( ++frame
>= ( sizeof( frame_to_yoffset
) / sizeof( frame_to_yoffset
[ 0 ] ) ) )
281 Window dummy1
, dummy2
;
285 if( !XQueryPointer( QX11Info::display(), QX11Info::appRootWindow(), &dummy1
, &dummy2
, &x
, &y
, &dummy3
, &dummy4
, &dummy5
))
287 startup_widget
->hide();
288 update_timer
.start( 100 );
291 QPoint
c_pos( x
, y
);
294 cursor_size
= XcursorGetDefaultSize( QX11Info::display());
297 if( cursor_size
<= 16 )
299 else if( cursor_size
<= 32 )
301 else if( cursor_size
<= 48 )
306 if( startup_widget
->x() != c_pos
.x() + X_DIFF
307 || startup_widget
->y() != c_pos
.y() + Y_DIFF
+ yoffset
)
308 startup_widget
->move( c_pos
.x() + X_DIFF
, c_pos
.y() + Y_DIFF
+ yoffset
);
309 startup_widget
->show();
310 XRaiseWindow( QX11Info::display(), startup_widget
->winId());
311 update_timer
.start( bouncing
? 30 : 100 );
312 QApplication::flush();
315 #include "startupid.moc"