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>
37 #include <X11/Xutil.h>
38 #include <X11/Xatom.h>
39 #include <X11/extensions/shape.h>
41 #define KDE_STARTUP_ICON "kmenu"
44 #include <X11/Xcursor/Xcursor.h>
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
)
53 startup_info( KStartupInfo::CleanOnCantDetect
),
54 startup_window( None
),
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()
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
);
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()));
150 void StartupId::finishKDEStartup()
152 kde_startup_status
= StartupDone
;
153 kapp
->removeX11EventFilter( this );
154 if( startups
.count() == 0 )
158 void StartupId::stop_startupid()
160 if( startup_window
!= None
)
161 XDestroyWindow( QX11Info::display(), startup_window
);
162 startup_window
= None
;
165 i
< NUM_BLINKING_PIXMAPS
;
167 pixmaps
[ i
] = QPixmap(); // null
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
);
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());
192 pt
.drawPixmap( 0, 0, p
);
194 ret
.setMask( p
.mask());
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());
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();
236 i
< NUM_BLINKING_PIXMAPS
;
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
);
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 ));
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
);
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
);
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()
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 ] )))
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
);
307 XShapeCombineMask( QX11Info::display(), startup_window
, ShapeBounding
, 0, 0, None
, ShapeSet
);
308 if ( ++frame
>= ( sizeof( frame_to_yoffset
) / sizeof( frame_to_yoffset
[ 0 ] ) ) )
311 Window dummy1
, dummy2
;
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 );
321 QPoint
c_pos( x
, y
);
324 cursor_size
= XcursorGetDefaultSize( QX11Info::display());
327 if( cursor_size
<= 16 )
329 else if( cursor_size
<= 32 )
331 else if( cursor_size
<= 48 )
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"