not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / ksplash / ksplashx / splash.cpp
blob22f2d5741764ecf0249f8d8e544b0a42704dfbe3
1 /********************************************************************
3 Copyright (C) 2007 Lubos Lunak <l.lunak@kde.org>
5 Please see file LICENSE for the licensing terms of ksplashx as a whole.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *********************************************************************/
21 //#define DEBUG
23 const int MAX_ITEMS = 100;
24 const int ANIM_IMAGES_ROW = 10;
26 // for converting from startup states to (internal) numbers
27 // these are also in the simple splash and in krunner
28 const char states[][ 12 ] =
29 { "initial", "kded", "confupdate", "kcminit", "ksmserver", "wm", "desktop", "ready" };
30 // State "ready" isn't used, make splash go away as soon as desktop is ready.
31 const int LAST_STATE = 6;
33 #include <config-workspace.h>
35 #include "splash.h"
36 #include "qcolor.h"
37 #include "qimage.h"
38 #include "pixmap.h"
39 #include "scale.h"
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44 #include <string.h>
45 #include <assert.h>
46 #include <dirent.h>
47 #include <libgen.h>
49 #include <X11/Xutil.h>
51 # ifdef HAVE_SYS_TIME_H
52 # include <sys/time.h>
53 # endif
54 #include <time.h>
56 struct AnimData
58 AnimData( int x, int y, PixmapData* frames, int num_frames, int delay, int repeat );
59 ~AnimData();
60 bool updateFrame( int change );
61 int x, y;
62 PixmapData* frames;
63 int num_frames;
64 int current_frame;
65 int delay;
66 int remaining_delay;
67 int repeat;
68 int remaining_repeat;
71 AnimData::AnimData( int x, int y, PixmapData* frames, int num_frames, int delay, int repeat )
72 : x( x )
73 , y( y )
74 , frames( frames )
75 , num_frames( num_frames )
76 , current_frame( 0 )
77 , delay( delay )
78 , remaining_delay( delay )
79 , repeat( repeat )
80 , remaining_repeat( repeat )
84 AnimData::~AnimData()
86 for( int i = 0;
87 i < num_frames;
88 ++i )
90 if( frames[ i ].hd != None )
91 XFreePixmap( qt_xdisplay(), frames[ i ].hd );
93 delete[] frames;
96 bool AnimData::updateFrame( int change )
98 remaining_delay -= change;
99 bool ret = false;
100 while( remaining_delay <= 0 )
102 if( ++current_frame == num_frames )
104 if( repeat > 0 && --remaining_repeat <= 0 ) // non-infinite and finished
105 { // stay at the last frame
106 --current_frame;
107 remaining_delay = 100000000;
108 return false;
110 current_frame = 0;
112 remaining_delay += delay;
113 ret = true;
115 return ret;
118 static QImage splash_image; // contents of the splash window (needed for alphablending)
119 static Pixmap splash_pixmap; // the pixmap with window contents
120 static AnimData* animations[ MAX_ITEMS ];
121 static int anim_count;
122 static Window window = None;
123 static QRect geometry;
124 static bool scalex = true;
125 static bool scaley = true;
126 static Atom kde_splash_progress;
127 static char kdehome[ 1024 ];
128 static char theme_name[ 1024 ];
129 static char theme_dir[ 1024 ];
130 static bool test;
131 static int parent_pipe;
132 static time_t final_time;
133 static int state;
134 static time_t timestamp; // timestamp of the description.txt file, used for caching
136 // returns a pointer to a static !
137 static const char* findFileHelper( const char* name, int* w, int* h, bool locolor, bool lame, QRect geom )
139 static char tmp[ 1024 ];
140 char best[ 1024 ];
141 int best_w = -1;
142 int best_h = -1;
143 DIR* dir = opendir( theme_dir );
144 if( dir != NULL )
146 while( dirent* file = readdir( dir ))
148 int w, h;
149 if( locolor
150 ? sscanf( file->d_name, "%dx%d-locolor", &w, &h ) == 2
151 : sscanf( file->d_name, "%dx%d", &w, &h ) == 2 )
153 if( w > best_w
154 // only derive from themes with the same ratio if lame resolutions are not allowed, damn 1280x1024
155 && ( lame || w * geom.height() == h * geom.width())
158 snprintf( tmp, 1024, "%s/%dx%d%s/%s", theme_dir, w, h, locolor ? "-locolor" : "", name );
159 #ifdef DEBUG
160 fprintf( stderr, "FINDFILE3: %s %s\n", name, tmp );
161 #endif
162 if( access( tmp, R_OK ) == 0 )
164 best_w = w;
165 best_h = h;
166 strcpy( best, tmp );
171 closedir( dir );
173 if( best_w > 0 )
175 if( w != NULL )
176 *w = best_w;
177 if( h != NULL )
178 *h = best_h;
179 strcpy( tmp, best );
180 return tmp;
182 return "";
185 // returns a pointer to a static !
186 static const char* findFileWithDepth( const char* name, int* w, int* h, bool locolor, QRect geom )
188 static char tmp[ 1024 ];
189 snprintf( tmp, 1024, "%s/%dx%d%s/%s", theme_dir, geom.width(), geom.height(),
190 locolor ? "-locolor" : "", name );
191 #ifdef DEBUG
192 fprintf( stderr, "FINDFILE1: %s %s\n", name, tmp );
193 #endif
194 if( access( tmp, R_OK ) != 0 )
196 // ksplash/<theme>-<resolution>-<file> in 'kde-config --path cache'
197 static char hostname[ 1024 ];
198 if( getenv("XAUTHLOCALHOSTNAME"))
199 strncpy( hostname, getenv("XAUTHLOCALHOSTNAME"), 1023 );
200 else
201 gethostname( hostname, 1023 );
202 hostname[ 1023 ] = '\0';
203 snprintf( tmp, 1024, "%s/cache-%s/ksplashx/%s-%dx%d%s-%s", kdehome, hostname, theme_name,
204 geom.width(), geom.height(), locolor ? "-locolor" : "", name );
205 #ifdef DEBUG
206 fprintf( stderr, "FINDFILE2: %s %s\n", name, tmp );
207 #endif
208 struct stat stat_buf;
209 if( stat( tmp, &stat_buf ) != 0 || stat_buf.st_mtime != timestamp )
211 tmp[ 0 ] = '\0';
212 #ifdef DEBUG
213 fprintf( stderr, "FINDFILE2 TIMESTAMP FAILURE\n" );
214 #endif
217 if( access( tmp, R_OK ) == 0 )
219 if( w != NULL )
220 *w = geom.width();
221 if( h != NULL )
222 *h = geom.height();
223 return tmp;
225 if( w == NULL || h == NULL ) // no scaling possible
226 return "";
227 const char* ret = findFileHelper( name, w, h, locolor, false, geom );
228 if( ret == NULL || *ret == '\0' )
229 ret = findFileHelper( name, w, h, locolor, true, geom );
230 return ret;
233 // returns a pointer to a static !
234 static const char* findLocalizedFileWithDepth( const char* name, int* w, int* h, bool locolor, QRect geom )
236 const int bufsz = 1024;
238 // Split name into dirname and basename.
239 char name2[ bufsz ];
240 strncpy( name2, name, bufsz );
241 name2[ bufsz - 1 ] = '\0';
242 char* basn = basename( name2 ); // must preceed dirname
243 char* dirn = dirname( name2 ); // modifies name2
245 // Check for localized file by parsing languages from KLOCALE_LANGUAGES,
246 // as provided by startkde via kstartupconfig4. It contains list of
247 // language codes, colon-separated and ordered by decreasing priority.
248 const char* lvarname = "KLOCALE_LANGUAGES";
249 if( getenv( lvarname ) && getenv( lvarname )[ 0 ] )
251 char lvar[ bufsz ];
252 strncpy( lvar, getenv( lvarname ), bufsz );
253 lvar[ bufsz - 1 ] = '\0';
255 // Go through colon-separated list of languages.
256 char* lang = strtok( lvar, ":" );
257 while( 1 )
259 char locname[ bufsz ];
260 snprintf( locname, bufsz, "%s/l10n/%s/%s", dirn, lang, basn );
261 locname[ bufsz - 1 ] = '\0';
263 // Check if this path exists.
264 const char* path = findFileWithDepth( locname, w, h, locolor, geom );
265 if( path[ 0 ] )
266 return path;
268 if( ( lang = strtok( 0, ":" ) ) == 0 )
269 break;
273 // Fall back to unlocalized file.
274 return findFileWithDepth( name, w, h, locolor, geom );
277 // returns a pointer to a static !
278 static const char* findFile( const char* name, int* w = NULL, int* h = NULL, bool* locolor = NULL, QRect geom = screenGeometry(0) )
280 if( x11Depth() <= 8 )
282 if( const char* ret = findLocalizedFileWithDepth( name, w, h, true, geom )) // try locolor
284 if( locolor != NULL )
285 *locolor = true;
286 return ret;
289 if( locolor != NULL )
290 *locolor = false;
291 return findLocalizedFileWithDepth( name, w, h, false, geom); // no locolor
294 // If a properly sized image doesn't exist save it in the cache location
295 // for the next use, because that means no scaling and a smaller png image
296 // to load.
297 static void pregeneratePixmap( const char* file, const char* real_file, int width, int height, bool locolor, QRect geom )
299 #ifdef DEBUG
300 static char cmd[ 1024 ];
301 snprintf( cmd, 1024, "ksplashx_scale \"%s\" \"%s\" \"%s\" %d %d %d %d %ld %s", theme_name,
302 file, real_file, width, height, geom.width(), geom.height(), timestamp,
303 locolor ? "locolor" : "no-locolor" );
304 fprintf( stderr, "PREGENERATE PIXMAP CMD:%s\n", cmd );
305 #endif
306 char w[ 20 ], h[ 20 ], sw[ 20 ], sh[ 20 ], t[ 40 ];
307 sprintf( w, "%d", width );
308 sprintf( h, "%d", height );
309 sprintf( sw, "%d", geom.width());
310 sprintf( sh, "%d", geom.height());
311 sprintf( t, "%ld", timestamp );
312 if( fork() == 0 )
314 int maxf = sysconf( _SC_OPEN_MAX );
315 for( int f = 0;
316 f < maxf;
317 ++f )
318 close( f );
319 nice( 10 );
320 sleep( 30 );
321 char* args[ 20 ];
322 args[ 0 ] = const_cast< char* >( "ksplashx_scale" );
323 args[ 1 ] = theme_name;
324 args[ 2 ] = ( char* ) file;
325 args[ 3 ] = ( char* ) real_file;
326 args[ 4 ] = w;
327 args[ 5 ] = h;
328 args[ 6 ] = sw;
329 args[ 7 ] = sh;
330 args[ 8 ] = t;
331 args[ 9 ] = ( char* )( locolor ? "locolor" : "no-locolor" );
332 args[ 10 ] = NULL;
333 execvp( args[ 0 ], args );
334 _exit( 0 );
338 static QImage loadImage( const char* file, QRect geom )
340 int w, h;
341 bool locolor;
342 const char* real_file = findFile( file, &w, &h, &locolor, geom ); // points to a static !
343 FILE* f = fopen( real_file, "r" );
344 if( f == NULL )
345 return QImage();
346 QImage img = splash_read_png_image( f );
347 if( img.depth() != 32 )
348 img = img.convertDepth( 32 );
349 fclose( f );
350 if( img.isNull())
352 fprintf( stderr, "Failed to load: %s\n", file );
353 exit( 3 );
355 if( img.depth() != 32 )
357 fprintf( stderr, "Not 32bpp: %s\n", file );
358 exit( 3 );
360 if(( scalex && w != geom.width()) || ( scaley && h != geom.height()))
362 double ratiox = scalex ? double( w ) / geom.width() : 1;
363 double ratioy = scaley ? double( h ) / geom.height() : 1;
364 #ifdef DEBUG
365 fprintf( stderr, "PIXMAP SCALING: %f %f\n", ratiox, ratioy );
366 #endif
367 img = scale( img, round( img.width() / ratiox ), round( img.height() / ratioy ));
368 if( ratiox * ratioy > 1 ) // only downscale
369 pregeneratePixmap( file, real_file, img.width(), img.height(), locolor, geom );
371 return img;
374 static void frameSize( const QImage& img, int frames, int& framew, int& frameh )
376 if( frames < ANIM_IMAGES_ROW )
378 framew = img.width() / frames;
379 frameh = img.height();
381 else
383 framew = img.width() / ANIM_IMAGES_ROW;
384 frameh = img.height() / (( frames + ANIM_IMAGES_ROW - 1 ) / ANIM_IMAGES_ROW );
388 static QImage loadAnimImage( const char* file, int frames )
390 int w, h;
391 bool locolor;
392 const char* real_file = findFile( file, &w, &h, &locolor ); // points to a static !
393 FILE* f = fopen( real_file, "r" );
394 if( f == NULL )
396 fprintf( stderr, "Bad anim file: %s\n", file );
397 exit( 3 );
399 QImage img = splash_read_png_image( f );
400 if( img.depth() != 32 )
401 img = img.convertDepth( 32 );
402 fclose( f );
403 int framew, frameh;
404 if( frames < ANIM_IMAGES_ROW )
406 if( img.width() % frames != 0 )
408 fprintf( stderr, "Bad anim size: %s\n", file );
409 exit( 3 );
412 else
414 if( img.width() % ANIM_IMAGES_ROW != 0
415 || img.height() % (( frames + ANIM_IMAGES_ROW - 1 ) / ANIM_IMAGES_ROW ) != 0 )
417 fprintf( stderr, "Bad anim size: %s\n", file );
418 exit( 3 );
421 frameSize( img, frames, framew, frameh );
422 if(( scalex && w != screenGeometry(0).width()) || ( scaley && h != screenGeometry(0).height()))
424 double ratiox = scalex ? double( w ) / screenGeometry(0).width() : 1;
425 double ratioy = scaley ? double( h ) / screenGeometry(0).height() : 1;
426 #ifdef DEBUG
427 fprintf( stderr, "ANIM SCALING: %f %f\n", ratiox, ratioy );
428 #endif
429 int framewnew = round( framew / ratiox );
430 int framehnew = round( frameh / ratioy );
431 QImage imgnew( framewnew * qMin( frames, ANIM_IMAGES_ROW ),
432 framehnew * (( frames + ANIM_IMAGES_ROW - 1 ) / ANIM_IMAGES_ROW ), img.depth());
433 if( img.hasAlphaBuffer())
434 imgnew.setAlphaBuffer( true );
435 for( int frame = 0;
436 frame < frames;
437 ++frame )
439 QImage im2 = img.copy( ( frame % ANIM_IMAGES_ROW ) * framew, ( frame / ANIM_IMAGES_ROW ) * frameh, framew, frameh );
440 im2 = scale( im2, framewnew, framehnew );
441 // don't use bitBlt(), it'd apply also alpha
442 for( int y = 0;
443 y < im2.height();
444 ++y )
446 QRgb* s = ( QRgb* ) im2.scanLine( y );
447 QRgb* d = (( QRgb* ) imgnew.scanLine( y + ( frame / ANIM_IMAGES_ROW ) * framehnew ))
448 + ( frame % ANIM_IMAGES_ROW ) * framewnew;
449 memcpy( d, s, im2.width() * sizeof( QRgb ));
452 framew = framewnew;
453 frameh = framehnew;
454 img = imgnew;
455 if( ratiox * ratioy > 1 ) // only downscale
456 pregeneratePixmap( file, real_file, img.width(), img.height(), locolor, screenGeometry(0) );
458 return img;
461 static PixmapData* imageAnimToPixmaps( const QImage& img, int frames )
463 if( img.isNull())
464 return NULL;
465 int framew, frameh;
466 frameSize( img, frames, framew, frameh );
467 PixmapData pix = imageToPixmap( img );
468 PixmapData* ret = new PixmapData[ MAX_ITEMS ];
469 GC gc = qt_xget_temp_gc( x11Screen(), false );
470 for( int frame = 0;
471 frame < frames;
472 ++frame )
474 Pixmap p = XCreatePixmap( qt_xdisplay(), DefaultRootWindow( qt_xdisplay()), framew, frameh, x11Depth());
475 XCopyArea( qt_xdisplay(), pix.hd, p, gc,
476 ( frame % ANIM_IMAGES_ROW ) * framew, ( frame / ANIM_IMAGES_ROW ) * frameh, framew, frameh, 0, 0 );
477 ret[ frame ].hd = p;
478 ret[ frame ].w = framew;
479 ret[ frame ].h = frameh;
480 ret[ frame ].d = x11Depth();
482 if( pix.hd != None )
483 XFreePixmap( qt_xdisplay(), pix.hd );
484 return ret;
487 static void doPaint( const QRect& area )
489 #if 0
490 fprintf( stderr, "PAINT: %d,%d-%dx%d\n", area.x(), area.y(), area.width(), area.height());
491 #endif
492 if( window == None )
493 return; // delayed
494 // double-buffer
495 Pixmap pixmap = XCreatePixmap( qt_xdisplay(), DefaultRootWindow( qt_xdisplay()),
496 area.width(), area.height(), x11Depth());
497 GC gc = qt_xget_temp_gc( x11Screen(), false );
498 // copy splash pixmap
499 XCopyArea( qt_xdisplay(), splash_pixmap, pixmap, gc,
500 area.x(), area.y(), area.width(), area.height(), 0, 0 );
501 // add animations
502 for( int i = 0;
503 i < MAX_ITEMS;
504 ++i )
506 AnimData* anim = animations[ i ];
507 PixmapData* frame = anim != NULL ? &anim->frames[ anim->current_frame ] : NULL;
508 if( anim != NULL
509 && area.intersects( QRect( anim->x, anim->y, frame->w, frame->h )))
511 XCopyArea( qt_xdisplay(), frame->hd, pixmap, gc,
512 qMax( 0, area.x() - anim->x ), qMax( 0, area.y() - anim->y ),
513 area.x() - anim->x + area.width(), area.y() - anim->y + area.height(),
514 qMax( 0, anim->x - area.x()), qMax( 0, anim->y - area.y()));
517 XCopyArea( qt_xdisplay(), pixmap, window, gc, 0, 0, area.width(), area.height(), area.x(), area.y());
518 XFreePixmap( qt_xdisplay(), pixmap );
521 static void createWindow()
523 assert( window == None );
524 #ifdef DEBUG
525 fprintf( stderr, "GEOMETRY: %d %d %d %d\n", geometry.x(), geometry.y(), geometry.width(), geometry.height());
526 #endif
527 XSetWindowAttributes attrs;
528 QRect geom = totalScreenGeometry();
529 attrs.override_redirect = True;
530 attrs.background_pixmap = None;
531 // attrs.override_redirect = False;
532 window = XCreateWindow( qt_xdisplay(), DefaultRootWindow( qt_xdisplay()),
533 geom.x(), geom.y(), geom.width(), geom.height(),
534 0, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect | CWBackPixmap, &attrs );
535 XSelectInput( qt_xdisplay(), window, ButtonPressMask | ExposureMask );
536 XClassHint class_hint;
537 class_hint.res_name = const_cast< char* >( "ksplashx" );
538 class_hint.res_class = const_cast< char* >( "ksplashx" );
539 XSetWMProperties( qt_xdisplay(), window, NULL, NULL, NULL, NULL, NULL, NULL, &class_hint );
540 XMapRaised( qt_xdisplay(), window );
543 static void createSplashImage()
545 QRect geom = totalScreenGeometry();
546 assert( splash_image.isNull());
547 assert( splash_pixmap == None );
548 splash_image = QImage( geom.size(), 32 );
549 splash_pixmap = XCreatePixmap( qt_xdisplay(), DefaultRootWindow( qt_xdisplay()),
550 geom.width(), geom.height(), x11Depth());
553 static bool waitState( int expected_state )
555 if( expected_state <= state )
556 return false;
557 if( window == None )
558 createWindow();
559 if( splash_image.isNull())
561 fprintf( stderr, "No window contents\n" );
562 exit( 3 );
564 time_t test_time = time( NULL ) + 2;
565 #ifdef DEBUG
566 fprintf( stderr,"AWATING STATE: %d (%s)\n", expected_state, states[ expected_state ] );
567 #endif
568 if( parent_pipe >= 0 )
569 { // wait for paint being finished, and tell parent to exit
570 XSync( qt_xdisplay(), False );
571 char buf = '\0';
572 write( parent_pipe, &buf, 1 );
573 close( parent_pipe );
574 parent_pipe = -1;
576 for(;;)
578 while( XPending( qt_xdisplay()))
580 XEvent ev;
581 XNextEvent( qt_xdisplay(), &ev );
582 if( ev.type == ButtonPress && ev.xbutton.window == window && ev.xbutton.button == Button1 )
584 final_time = time( NULL );
585 break;
587 if( ev.type == Expose && ev.xexpose.window == window )
588 doPaint( QRect( ev.xexpose.x, ev.xexpose.y, ev.xexpose.width, ev.xexpose.height ));
589 if( ev.type == ConfigureNotify && ev.xconfigure.event == DefaultRootWindow( qt_xdisplay()))
590 XRaiseWindow( qt_xdisplay(), window );
591 if( ev.type == ClientMessage && ev.xclient.window == DefaultRootWindow( qt_xdisplay())
592 && ev.xclient.message_type == kde_splash_progress )
594 // based on ksplash
595 const char* s = ev.xclient.data.b;
596 #ifdef DEBUG
597 fprintf( stderr,"MESSAGE: %s\n", s );
598 #endif
599 int new_state = -1;
600 for( int i = 0;
601 i < int( sizeof( states ) / sizeof( states[ 0 ] ));
602 ++i )
604 if( strcmp( s, states[ i ] ) == 0 )
606 new_state = i;
607 break;
610 if( new_state == -1 )
612 #ifdef DEBUG
613 fprintf( stderr, "UNKNOWN SPLASH STATE: %s\n", s );
614 #endif
616 else if( new_state > state )
618 state = new_state;
619 if( state >= LAST_STATE )
620 final_time = time( NULL ) + 1; // quit after short time
624 if( test && time( NULL ) >= test_time )
626 ++state;
627 test_time = time( NULL ) + 2;
629 if( expected_state <= state )
630 return false;
631 struct timeval tm_start, tm_end;
632 gettimeofday( &tm_start, NULL );
633 fd_set set;
634 FD_ZERO( &set );
635 FD_SET( XConnectionNumber( qt_xdisplay()), &set );
636 int delay = 1000;
637 for( int i = 0;
638 i < MAX_ITEMS;
639 ++i )
640 if( animations[ i ] != NULL && animations[ i ]->delay < delay * 2 )
641 delay = animations[ i ]->delay / 2;
642 struct timeval tv;
643 tv.tv_sec = 0;
644 tv.tv_usec = delay * 1000;
645 select( XConnectionNumber( qt_xdisplay()) + 1, &set, NULL, NULL, &tv );
646 if( time( NULL ) >= final_time )
648 #ifdef DEBUG
649 fprintf( stderr, "EXITING\n" );
650 #endif
651 return true; // --->
653 gettimeofday( &tm_end, NULL );
654 int real_delay = (( tm_end.tv_sec - tm_start.tv_sec ) * 1000000 + tm_end.tv_usec - tm_start.tv_usec ) / 1000;
655 for( int i = 0;
656 i < MAX_ITEMS;
657 ++i )
659 AnimData* anim = animations[ i ];
660 if( anim != NULL && anim->updateFrame( real_delay ))
661 doPaint( QRect( anim->x, anim->y, anim->frames[ anim->current_frame ].w, anim->frames[ anim->current_frame ].h ));
666 static bool checkRelative( const char* ref )
668 if( ref[ 0 ] == '\0' || ref[ 1 ] == '\0' || ref[ 2 ] != '\0' )
669 return false;
670 if( strchr( "LRC", ref[ 0 ] ) == NULL )
671 return false;
672 if( strchr( "TBC", ref[ 1 ] ) == NULL )
673 return false;
674 return true;
677 static int makeAbsolute( char screen, int val, char image, int size, int screen_size )
679 int pos;
680 switch( screen )
682 case 'L':
683 case 'T':
684 pos = 0;
685 break;
686 case 'R':
687 case 'B':
688 pos = screen_size;
689 break;
690 case 'C':
691 pos = screen_size / 2;
692 break;
693 default:
694 exit( 3 );
696 pos += val;
697 switch( image )
699 case 'L':
700 case 'T':
701 pos -= 0;
702 break;
703 case 'R':
704 case 'B':
705 pos -= size;
706 break;
707 case 'C':
708 pos -= size / 2;
709 break;
710 default:
711 exit( 3 );
713 return pos;
716 static int makeAbsoluteX( const char* screen_ref, int x_rel, const char* image_ref, int width )
718 return makeAbsolute( screen_ref[ 0 ], x_rel, image_ref[ 0 ], width, geometry.width());
721 static int makeAbsoluteY( const char* screen_ref, int y_rel, const char* image_ref, int height )
723 return makeAbsolute( screen_ref[ 1 ], y_rel, image_ref[ 1 ], height, geometry.height());
726 static inline QRgb blend( QRgb c, QRgb background )
728 if( qAlpha( c ) == 255 )
729 return c;
730 return qRgb( ( qRed( background ) * ( 255 - qAlpha( c ) ) + qRed( c ) * qAlpha( c ) ) / 255,
731 ( qGreen( background ) * ( 255 - qAlpha( c ) ) + qGreen( c ) * qAlpha( c ) ) / 255,
732 ( qBlue( background ) * ( 255 - qAlpha( c ) ) + qBlue( c ) * qAlpha( c ) ) / 255 );
735 static void blend( QImage& img, int x_pos, int y_pos, int x_img, int y_img, int w_img, int h_img )
737 if( !img.hasAlphaBuffer())
738 return; // it doesn't have alpha, so it is the blended result
739 for( int y = 0;
740 y < h_img;
741 ++y )
743 QRgb* s = (( QRgb* )( splash_image.scanLine( y + y_pos ))) + x_pos;
744 QRgb* d = (( QRgb* )( img.scanLine( y + y_img ))) + x_img;
745 for( int x = 0;
746 x < w_img;
747 ++x, ++d, ++s )
749 *d = blend( *d, *s );
754 static void blend( QImage& img, int x_pos, int y_pos )
756 blend( img, x_pos, y_pos, 0, 0, img.width(), img.height());
757 img.setAlphaBuffer( false );
760 static void blendAnim( QImage& img, int x_pos, int y_pos, int frames )
762 int framew, frameh;
763 frameSize( img, frames, framew, frameh );
764 for( int frame = 0;
765 frame < frames;
766 ++frame )
768 blend( img, x_pos, y_pos,
769 ( frame % ANIM_IMAGES_ROW ) * framew, ( frame / ANIM_IMAGES_ROW ) * frameh, framew, frameh );
771 img.setAlphaBuffer( false );
774 static void updateSplashImage( const QImage& img, int x_pos, int y_pos )
776 for( int y = 0;
777 y < img.height();
778 ++y )
780 QRgb* s = (( QRgb* )( img.scanLine( y )));
781 QRgb* d = (( QRgb* )( splash_image.scanLine( y + y_pos ))) + x_pos;
782 for( int x = 0;
783 x < img.width();
784 ++x, ++d, ++s )
786 *d = *s;
789 PixmapData pix = imageToPixmap( img );
790 GC gc = qt_xget_temp_gc( x11Screen(), false );
791 XCopyArea( qt_xdisplay(), pix.hd, splash_pixmap, gc, 0, 0, img.width(), img.height(), x_pos, y_pos );
792 XFreePixmap( qt_xdisplay(), pix.hd );
795 void runSplash( const char* them, bool t, int p )
797 geometry = screenGeometry(0);
798 // fetch the $KDEHOME environment variable that may point to e.g. "~/.kde4"
799 if( getenv( "KDEHOME" ) && getenv( "KDEHOME" )[ 0 ] )
800 snprintf( kdehome, 1024, "%s", getenv( "KDEHOME" ));
801 else
802 snprintf( kdehome, 1024, "%s/"KDE_DEFAULT_HOME, getenv( "HOME" ) ? getenv( "HOME" ) : "" );
804 // fetch the name of the theme which is also used as directory name.
805 snprintf( theme_name, 1024, "%s", them );
806 // fetch the theme-directory,e.g. "/opt/kde4/share/apps/ksplash/Themes/MyKSplashXThemeName"
807 snprintf( theme_dir, 1024, "%s/ksplash/Themes/%s", KDE_DATADIR, them );
809 test = t;
810 parent_pipe = p;
811 anim_count = 0;
812 state = 0;
813 window = None;
814 splash_image = QImage();
815 splash_pixmap = None;
816 final_time = time( NULL ) + 300;
817 int desc_w, desc_h;
819 // try to load the themes description.txt file from within the theme_dir
820 FILE* datafile = fopen( findFile( "description.txt", &desc_w, &desc_h ), "r" );
821 if( datafile == NULL )
823 // if we failed to read it, try it with $KDEHOME as theme_dir. This
824 // is needed to be able to load local (aka by the user in his local
825 // home-directory) installed themes.
826 snprintf( theme_dir, 1024, "%s/share/apps/ksplash/Themes/%s", kdehome, them );
827 datafile = fopen( findFile( "description.txt", &desc_w, &desc_h ), "r" );
828 if( datafile == NULL )
830 fprintf( stderr, "Cannot find description.txt file.\n" );
831 exit( 2 );
835 struct stat stat_buf;
836 if( fstat( fileno( datafile ), &stat_buf ) != 0 )
838 fprintf( stderr, "Cannot read description.txt file.\n" );
839 exit( 2 );
841 timestamp = stat_buf.st_mtime;
842 double ratiox = double( desc_w ) / screenGeometry(0).width(); // only for coordinates in the description file
843 double ratioy = double( desc_h ) / screenGeometry(0).height(); // only for coordinates in the description file
844 XSelectInput( qt_xdisplay(), DefaultRootWindow( qt_xdisplay()), SubstructureNotifyMask );
845 kde_splash_progress = XInternAtom( qt_xdisplay(), "_KDE_SPLASH_PROGRESS", False );
846 for( int i = 0;
847 i < MAX_ITEMS;
848 ++i )
849 animations[ i ] = NULL;
850 while( !feof( datafile ))
852 char line[ 1024 ];
853 if( !freadline( line, 1024, datafile ))
854 break;
855 strip_whitespace( line );
856 char buf[ 1024 ];
857 int number, x, y, w, h, x_rel, y_rel, frames, delay, repeat, items;
858 char screen_ref[ 3 ];
859 char window_ref[ 3 ];
860 char image_ref[ 3 ];
861 bool handled = false;
862 if( line[ 0 ] == '#' || line[ 0 ] == '\0' )
863 continue;
864 else if( sscanf( line, "SCALEX %1023s", buf ) == 1 )
866 handled = true;
867 if( strcmp( buf, "ON" ) == 0 )
868 scalex = true;
869 else if( strcmp( buf, "OFF" ) == 0 )
870 scalex = false;
871 else
873 fprintf( stderr, "Bad scale x: %s\n", line );
874 exit( 3 );
877 else if( sscanf( line, "SCALEY %1023s", buf ) == 1 )
879 handled = true;
880 if( strcmp( buf, "ON" ) == 0 )
881 scaley = true;
882 else if( strcmp( buf, "OFF" ) == 0 )
883 scaley = false;
884 else
886 fprintf( stderr, "Bad scale y: %s\n", line );
887 exit( 3 );
890 else if( sscanf( line, "SCALE %1023s", buf ) == 1 )
892 handled = true;
893 if( strcmp( buf, "ON" ) == 0 )
894 scalex = scaley = true;
895 else if( strcmp( buf, "OFF" ) == 0 )
896 scalex = scaley = false;
897 else
899 fprintf( stderr, "Bad scale: %s\n", line );
900 exit( 3 );
903 else if( sscanf( line, "GEOMETRY %d %d %d %d", &x, &y, &w, &h ) == 4 )
905 handled = true;
906 if( scalex || scaley )
908 x = scalex ? round( x / ratiox ) : x;
909 y = scaley ? round( y / ratioy ) : y;
910 w = scalex ? round( w / ratiox ) : w;
911 h = scaley ? round( h / ratioy ) : h;
913 if( x < 0 )
914 x += screenGeometry(0).width();
915 if( y < 0 )
916 y += screenGeometry(0).height();
917 QRect r( x, y, w, h );
918 if( screenGeometry(0).contains( r ))
920 geometry = r;
921 if( window != None )
922 XMoveResizeWindow( qt_xdisplay(), window, x, y, w, h );
923 if( !splash_image.isNull())
924 { // destroy and then recreate
925 splash_image = QImage();
926 XFreePixmap( qt_xdisplay(), splash_pixmap );
927 splash_pixmap = None;
929 createSplashImage();
931 else
933 fprintf( stderr, "Wrong geometry: %s\n", line );
934 exit( 3 );
937 else if( sscanf( line, "GEOMETRY_REL %2s %d %d %2s %d %d",
938 screen_ref, &x_rel, &y_rel, window_ref, &w, &h ) == 6 )
940 handled = true;
941 if( scalex || scaley )
943 x_rel = scalex ? round( x_rel / ratiox ) : x_rel;
944 y_rel = scaley ? round( y_rel / ratioy ) : y_rel;
945 w = scalex ? round( w / ratiox ) : w;
946 h = scaley ? round( h / ratioy ) : h;
948 if( !checkRelative( screen_ref )
949 || !checkRelative( window_ref ))
951 fprintf( stderr,"Bad reference point: %s\n", line );
952 exit( 3 );
954 x = makeAbsoluteX( screen_ref, x_rel, window_ref, w );
955 y = makeAbsoluteY( screen_ref, y_rel, window_ref, h );
956 QRect r( x, y, w, h );
957 if( screenGeometry(0).contains( r ))
959 geometry = r;
960 if( window != None )
961 XMoveResizeWindow( qt_xdisplay(), window, x, y, w, h );
962 if( !splash_image.isNull())
963 { // destroy and then recreate
964 splash_image = QImage();
965 XFreePixmap( qt_xdisplay(), splash_pixmap );
966 splash_pixmap = None;
968 createSplashImage();
970 else
972 fprintf( stderr, "Wrong geometry: %s\n", line );
973 exit( 3 );
976 else if( sscanf( line, "BACKGROUND_IMAGE %d %d %1023s", &x, &y, buf ) == 3 )
978 int screens = screenCount();
979 handled = true;
980 if( scalex || scaley )
982 x = scalex ? round( x / ratiox ) : x;
983 y = scaley ? round( y / ratioy ) : y;
985 if( splash_image.isNull())
986 createSplashImage();
987 for (int i = 0; i < screens; ++i) {
988 QRect geom = screenGeometry(i);
989 QImage img = loadImage( buf, geom );
990 if( !img.isNull())
992 blend( img, geom.x() + x, geom.y() + y );
993 updateSplashImage( img, geom.x() + x, geom.y() + y );
994 doPaint( QRect( geom.x() + x, geom.y() + y, img.width(), img.height()));
996 else
998 fprintf( stderr, "Bad image: %s\n", line );
999 exit( 3 );
1003 else if( sscanf( line, "BACKGROUND %1023s", buf ) == 1 )
1005 handled = true;
1006 QColor background = QColor( buf );
1007 QRect geom = totalScreenGeometry();
1008 if( !background.isValid())
1010 fprintf( stderr, "Bad color: %s\n", line );
1011 exit( 3 );
1013 if( splash_image.isNull())
1014 createSplashImage();
1015 splash_image.fill( background.rgb());
1016 XGCValues xgc;
1017 xgc.foreground = background.pixel();
1018 GC gc = XCreateGC( qt_xdisplay(), splash_pixmap, GCForeground, &xgc );
1019 XFillRectangle( qt_xdisplay(), splash_pixmap, gc, 0, 0, geom.width(), geom.height());
1020 XFreeGC( qt_xdisplay(), gc );
1021 doPaint( QRect( 0, 0, geom.width(), geom.height()));
1023 else if( sscanf( line, "IMAGE %d %d %1023s", &x, &y, buf ) == 3 )
1025 handled = true;
1026 if( scalex || scaley )
1028 x = scalex ? round( x / ratiox ) : x;
1029 y = scaley ? round( y / ratioy ) : y;
1031 if( splash_image.isNull())
1032 createSplashImage();
1033 QImage img = loadImage( buf, screenGeometry(0) );
1034 if( !img.isNull())
1036 #if 0
1037 if( !QRect( 0, 0, geometry.width(), geometry.height())
1038 .contains( QRect( x, y, img.width(), img.height())))
1040 fprintf( stderr, "Image outside of geometry: %s\n", line );
1041 exit( 3 );
1043 #endif
1044 blend( img, x, y );
1045 updateSplashImage( img, x, y );
1046 doPaint( QRect( x, y, img.width(), img.height()));
1048 else
1050 fprintf( stderr, "Bad image: %s\n", line );
1051 exit( 3 );
1054 else if( sscanf( line, "IMAGE_REL %2s %d %d %2s %1023s",
1055 window_ref, &x_rel, &y_rel, image_ref, buf ) == 5 )
1057 handled = true;
1058 if( scalex || scaley )
1060 x_rel = scalex ? round( x_rel / ratiox ) : x_rel;
1061 y_rel = scaley ? round( y_rel / ratioy ) : y_rel;
1063 if( !checkRelative( window_ref )
1064 || !checkRelative( window_ref ))
1066 fprintf( stderr,"Bad reference point: %s\n", line );
1067 exit( 3 );
1069 if( splash_image.isNull())
1070 createSplashImage();
1071 QImage img = loadImage( buf, screenGeometry(0) );
1072 if( !img.isNull())
1074 x = makeAbsoluteX( window_ref, x_rel, image_ref, img.width());
1075 y = makeAbsoluteY( window_ref, y_rel, image_ref, img.height());
1076 #if 0
1077 if( !QRect( 0, 0, geometry.width(), geometry.height())
1078 .contains( QRect( x, y, img.width(), img.height())))
1080 fprintf( stderr, "Image outside of geometry: %s\n", line );
1081 exit( 3 );
1083 #endif
1084 blend( img, x, y );
1085 updateSplashImage( img, x, y );
1086 doPaint( QRect( x, y, img.width(), img.height()));
1088 else
1090 fprintf( stderr, "Bad image: %s\n", line );
1091 exit( 3 );
1094 items = sscanf( line, "ANIM %d %d %d %d %1023s %d %d", &number, &x, &y, &frames, buf, &delay, &repeat );
1095 if( items == 6 )
1096 repeat = 0; // default
1097 if( items == 6 || items == 7 )
1099 handled = true;
1100 if( scalex || scaley )
1102 x = scalex ? round( x / ratiox ) : x;
1103 y = scaley ? round( y / ratioy ) : y;
1105 if( number <= 0 || number >= MAX_ITEMS )
1107 fprintf( stderr,"Bad number: %s\n", line );
1108 exit( 3 );
1110 if( frames <= 0 || frames > MAX_ITEMS )
1112 fprintf( stderr, "Frames limit reached: %s\n", line );
1113 exit( 3 );
1115 if( splash_image.isNull())
1116 createSplashImage();
1117 QImage imgs = loadAnimImage( buf, frames );
1118 if( !imgs.isNull())
1120 blendAnim( imgs, x, y, frames );
1121 PixmapData* pixs = imageAnimToPixmaps( imgs, frames );
1122 delete animations[ number ];
1123 animations[ number ] = new AnimData( x, y, pixs, frames, delay, repeat );
1126 items = sscanf( line, "ANIM_REL %d %2s %d %d %2s %d %1023s %d %d",
1127 &number, window_ref, &x_rel, &y_rel, image_ref, &frames, buf, &delay, &repeat );
1128 if( items == 8 )
1129 repeat = 0; // default
1130 if( items == 8 || items == 9 )
1132 handled = true;
1133 if( scalex || scaley )
1135 x_rel = scalex ? round( x_rel / ratiox ) : x_rel;
1136 y_rel = scaley ? round( y_rel / ratioy ) : y_rel;
1138 if( number <= 0 || number >= MAX_ITEMS )
1140 fprintf( stderr,"Bad number: %s\n", line );
1141 exit( 3 );
1143 if( !checkRelative( window_ref )
1144 || !checkRelative( window_ref ))
1146 fprintf( stderr,"Bad reference point: %s\n", line );
1147 exit( 3 );
1149 if( frames <= 0 || frames > MAX_ITEMS )
1151 fprintf( stderr, "Frames limit reached: %s\n", line );
1152 exit( 3 );
1154 if( splash_image.isNull())
1155 createSplashImage();
1156 QImage imgs = loadAnimImage( buf, frames );
1157 if( !imgs.isNull())
1159 int framew, frameh;
1160 frameSize( imgs, frames, framew, frameh );
1161 x = makeAbsoluteX( window_ref, x_rel, image_ref, framew );
1162 y = makeAbsoluteY( window_ref, y_rel, image_ref, frameh );
1163 blendAnim( imgs, x, y, frames );
1164 PixmapData* pixs = imageAnimToPixmaps( imgs, frames );
1165 delete animations[ number ];
1166 animations[ number ] = new AnimData( x, y, pixs, frames, delay, repeat );
1169 else if( sscanf( line, "STOP_ANIM %d", &number ) == 1 )
1171 handled = true;
1172 if( number <= 0 || number >= MAX_ITEMS || animations[ number ] == NULL )
1174 fprintf( stderr,"Bad number: %s\n", line );
1175 exit( 3 );
1177 AnimData* anim = animations[ number ];
1178 doPaint( QRect( anim->x, anim->y, anim->frames[ 0 ].w, anim->frames[ 0 ].h ));
1179 delete animations[ number ];
1180 animations[ number ] = NULL;
1182 else if( sscanf( line, "WAIT_STATE %s", buf ) == 1 )
1184 handled = true;
1185 int new_state = -1;
1186 for( int i = 0;
1187 i < int( sizeof( states ) / sizeof( states[ 0 ] ));
1188 ++i )
1190 if( strcmp( buf, states[ i ] ) == 0 )
1192 new_state = i;
1193 break;
1196 if( new_state == -1 )
1198 fprintf( stderr, "Unknown splash state: %s\n", buf );
1199 // don't make fatal, may be a theme for a newer version
1201 else
1203 if( waitState( new_state ))
1204 break; // exiting
1207 if( !handled )
1209 fprintf( stderr, "Unknown line: %s\n", line );
1210 exit( 3 );
1213 fclose( datafile );
1214 XDestroyWindow( qt_xdisplay(), window );
1215 window = None;
1216 XFreePixmap( qt_xdisplay(), splash_pixmap );
1217 splash_pixmap = None;
1218 splash_image = QImage();