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 *********************************************************************/
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>
41 #include <sys/types.h>
49 #include <X11/Xutil.h>
51 # ifdef HAVE_SYS_TIME_H
52 # include <sys/time.h>
58 AnimData( int x
, int y
, PixmapData
* frames
, int num_frames
, int delay
, int repeat
);
60 bool updateFrame( int change
);
71 AnimData::AnimData( int x
, int y
, PixmapData
* frames
, int num_frames
, int delay
, int repeat
)
75 , num_frames( num_frames
)
78 , remaining_delay( delay
)
80 , remaining_repeat( repeat
)
90 if( frames
[ i
].hd
!= None
)
91 XFreePixmap( qt_xdisplay(), frames
[ i
].hd
);
96 bool AnimData::updateFrame( int change
)
98 remaining_delay
-= change
;
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
107 remaining_delay
= 100000000;
112 remaining_delay
+= delay
;
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 ];
131 static int parent_pipe
;
132 static time_t final_time
;
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 ];
143 DIR* dir
= opendir( theme_dir
);
146 while( dirent
* file
= readdir( dir
))
150 ? sscanf( file
->d_name
, "%dx%d-locolor", &w
, &h
) == 2
151 : sscanf( file
->d_name
, "%dx%d", &w
, &h
) == 2 )
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
);
160 fprintf( stderr
, "FINDFILE3: %s %s\n", name
, tmp
);
162 if( access( tmp
, R_OK
) == 0 )
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
);
192 fprintf( stderr
, "FINDFILE1: %s %s\n", name
, tmp
);
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 );
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
);
206 fprintf( stderr
, "FINDFILE2: %s %s\n", name
, tmp
);
208 struct stat stat_buf
;
209 if( stat( tmp
, &stat_buf
) != 0 || stat_buf
.st_mtime
!= timestamp
)
213 fprintf( stderr
, "FINDFILE2 TIMESTAMP FAILURE\n" );
217 if( access( tmp
, R_OK
) == 0 )
225 if( w
== NULL
|| h
== NULL
) // no scaling possible
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
);
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.
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 ] )
252 strncpy( lvar
, getenv( lvarname
), bufsz
);
253 lvar
[ bufsz
- 1 ] = '\0';
255 // Go through colon-separated list of languages.
256 char* lang
= strtok( lvar
, ":" );
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
);
268 if( ( lang
= strtok( 0, ":" ) ) == 0 )
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
)
289 if( locolor
!= NULL
)
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
297 static void pregeneratePixmap( const char* file
, const char* real_file
, int width
, int height
, bool locolor
, QRect geom
)
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
);
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
);
314 int maxf
= sysconf( _SC_OPEN_MAX
);
322 args
[ 0 ] = const_cast< char* >( "ksplashx_scale" );
323 args
[ 1 ] = theme_name
;
324 args
[ 2 ] = ( char* ) file
;
325 args
[ 3 ] = ( char* ) real_file
;
331 args
[ 9 ] = ( char* )( locolor
? "locolor" : "no-locolor" );
333 execvp( args
[ 0 ], args
);
338 static QImage
loadImage( const char* file
, QRect geom
)
342 const char* real_file
= findFile( file
, &w
, &h
, &locolor
, geom
); // points to a static !
343 FILE* f
= fopen( real_file
, "r" );
346 QImage img
= splash_read_png_image( f
);
347 if( img
.depth() != 32 )
348 img
= img
.convertDepth( 32 );
352 fprintf( stderr
, "Failed to load: %s\n", file
);
355 if( img
.depth() != 32 )
357 fprintf( stderr
, "Not 32bpp: %s\n", file
);
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;
365 fprintf( stderr
, "PIXMAP SCALING: %f %f\n", ratiox
, ratioy
);
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
);
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();
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
)
392 const char* real_file
= findFile( file
, &w
, &h
, &locolor
); // points to a static !
393 FILE* f
= fopen( real_file
, "r" );
396 fprintf( stderr
, "Bad anim file: %s\n", file
);
399 QImage img
= splash_read_png_image( f
);
400 if( img
.depth() != 32 )
401 img
= img
.convertDepth( 32 );
404 if( frames
< ANIM_IMAGES_ROW
)
406 if( img
.width() % frames
!= 0 )
408 fprintf( stderr
, "Bad anim size: %s\n", file
);
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
);
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;
427 fprintf( stderr
, "ANIM SCALING: %f %f\n", ratiox
, ratioy
);
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 );
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
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
));
455 if( ratiox
* ratioy
> 1 ) // only downscale
456 pregeneratePixmap( file
, real_file
, img
.width(), img
.height(), locolor
, screenGeometry(0) );
461 static PixmapData
* imageAnimToPixmaps( const QImage
& img
, int frames
)
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 );
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 );
478 ret
[ frame
].w
= framew
;
479 ret
[ frame
].h
= frameh
;
480 ret
[ frame
].d
= x11Depth();
483 XFreePixmap( qt_xdisplay(), pix
.hd
);
487 static void doPaint( const QRect
& area
)
490 fprintf( stderr
, "PAINT: %d,%d-%dx%d\n", area
.x(), area
.y(), area
.width(), area
.height());
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 );
506 AnimData
* anim
= animations
[ i
];
507 PixmapData
* frame
= anim
!= NULL
? &anim
->frames
[ anim
->current_frame
] : 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
);
525 fprintf( stderr
, "GEOMETRY: %d %d %d %d\n", geometry
.x(), geometry
.y(), geometry
.width(), geometry
.height());
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
)
559 if( splash_image
.isNull())
561 fprintf( stderr
, "No window contents\n" );
564 time_t test_time
= time( NULL
) + 2;
566 fprintf( stderr
,"AWATING STATE: %d (%s)\n", expected_state
, states
[ expected_state
] );
568 if( parent_pipe
>= 0 )
569 { // wait for paint being finished, and tell parent to exit
570 XSync( qt_xdisplay(), False
);
572 write( parent_pipe
, &buf
, 1 );
573 close( parent_pipe
);
578 while( XPending( qt_xdisplay()))
581 XNextEvent( qt_xdisplay(), &ev
);
582 if( ev
.type
== ButtonPress
&& ev
.xbutton
.window
== window
&& ev
.xbutton
.button
== Button1
)
584 final_time
= time( NULL
);
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
)
595 const char* s
= ev
.xclient
.data
.b
;
597 fprintf( stderr
,"MESSAGE: %s\n", s
);
601 i
< int( sizeof( states
) / sizeof( states
[ 0 ] ));
604 if( strcmp( s
, states
[ i
] ) == 0 )
610 if( new_state
== -1 )
613 fprintf( stderr
, "UNKNOWN SPLASH STATE: %s\n", s
);
616 else if( new_state
> state
)
619 if( state
>= LAST_STATE
)
620 final_time
= time( NULL
) + 1; // quit after short time
624 if( test
&& time( NULL
) >= test_time
)
627 test_time
= time( NULL
) + 2;
629 if( expected_state
<= state
)
631 struct timeval tm_start
, tm_end
;
632 gettimeofday( &tm_start
, NULL
);
635 FD_SET( XConnectionNumber( qt_xdisplay()), &set
);
640 if( animations
[ i
] != NULL
&& animations
[ i
]->delay
< delay
* 2 )
641 delay
= animations
[ i
]->delay
/ 2;
644 tv
.tv_usec
= delay
* 1000;
645 select( XConnectionNumber( qt_xdisplay()) + 1, &set
, NULL
, NULL
, &tv
);
646 if( time( NULL
) >= final_time
)
649 fprintf( stderr
, "EXITING\n" );
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;
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' )
670 if( strchr( "LRC", ref
[ 0 ] ) == NULL
)
672 if( strchr( "TBC", ref
[ 1 ] ) == NULL
)
677 static int makeAbsolute( char screen
, int val
, char image
, int size
, int screen_size
)
691 pos
= screen_size
/ 2;
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 )
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
743 QRgb
* s
= (( QRgb
* )( splash_image
.scanLine( y
+ y_pos
))) + x_pos
;
744 QRgb
* d
= (( QRgb
* )( img
.scanLine( y
+ y_img
))) + x_img
;
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
)
763 frameSize( img
, frames
, framew
, frameh
);
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
)
780 QRgb
* s
= (( QRgb
* )( img
.scanLine( y
)));
781 QRgb
* d
= (( QRgb
* )( splash_image
.scanLine( y
+ y_pos
))) + x_pos
;
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" ));
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
);
814 splash_image
= QImage();
815 splash_pixmap
= None
;
816 final_time
= time( NULL
) + 300;
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" );
835 struct stat stat_buf
;
836 if( fstat( fileno( datafile
), &stat_buf
) != 0 )
838 fprintf( stderr
, "Cannot read description.txt file.\n" );
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
);
849 animations
[ i
] = NULL
;
850 while( !feof( datafile
))
853 if( !freadline( line
, 1024, datafile
))
855 strip_whitespace( line
);
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 ];
861 bool handled
= false;
862 if( line
[ 0 ] == '#' || line
[ 0 ] == '\0' )
864 else if( sscanf( line
, "SCALEX %1023s", buf
) == 1 )
867 if( strcmp( buf
, "ON" ) == 0 )
869 else if( strcmp( buf
, "OFF" ) == 0 )
873 fprintf( stderr
, "Bad scale x: %s\n", line
);
877 else if( sscanf( line
, "SCALEY %1023s", buf
) == 1 )
880 if( strcmp( buf
, "ON" ) == 0 )
882 else if( strcmp( buf
, "OFF" ) == 0 )
886 fprintf( stderr
, "Bad scale y: %s\n", line
);
890 else if( sscanf( line
, "SCALE %1023s", buf
) == 1 )
893 if( strcmp( buf
, "ON" ) == 0 )
894 scalex
= scaley
= true;
895 else if( strcmp( buf
, "OFF" ) == 0 )
896 scalex
= scaley
= false;
899 fprintf( stderr
, "Bad scale: %s\n", line
);
903 else if( sscanf( line
, "GEOMETRY %d %d %d %d", &x
, &y
, &w
, &h
) == 4 )
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
;
914 x
+= screenGeometry(0).width();
916 y
+= screenGeometry(0).height();
917 QRect
r( x
, y
, w
, h
);
918 if( screenGeometry(0).contains( r
))
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
;
933 fprintf( stderr
, "Wrong geometry: %s\n", line
);
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 )
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
);
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
))
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
;
972 fprintf( stderr
, "Wrong geometry: %s\n", line
);
976 else if( sscanf( line
, "BACKGROUND_IMAGE %d %d %1023s", &x
, &y
, buf
) == 3 )
978 int screens
= screenCount();
980 if( scalex
|| scaley
)
982 x
= scalex
? round( x
/ ratiox
) : x
;
983 y
= scaley
? round( y
/ ratioy
) : y
;
985 if( splash_image
.isNull())
987 for (int i
= 0; i
< screens
; ++i
) {
988 QRect geom
= screenGeometry(i
);
989 QImage img
= loadImage( buf
, geom
);
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()));
998 fprintf( stderr
, "Bad image: %s\n", line
);
1003 else if( sscanf( line
, "BACKGROUND %1023s", buf
) == 1 )
1006 QColor background
= QColor( buf
);
1007 QRect geom
= totalScreenGeometry();
1008 if( !background
.isValid())
1010 fprintf( stderr
, "Bad color: %s\n", line
);
1013 if( splash_image
.isNull())
1014 createSplashImage();
1015 splash_image
.fill( background
.rgb());
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 )
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) );
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
);
1045 updateSplashImage( img
, x
, y
);
1046 doPaint( QRect( x
, y
, img
.width(), img
.height()));
1050 fprintf( stderr
, "Bad image: %s\n", line
);
1054 else if( sscanf( line
, "IMAGE_REL %2s %d %d %2s %1023s",
1055 window_ref
, &x_rel
, &y_rel
, image_ref
, buf
) == 5 )
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
);
1069 if( splash_image
.isNull())
1070 createSplashImage();
1071 QImage img
= loadImage( buf
, screenGeometry(0) );
1074 x
= makeAbsoluteX( window_ref
, x_rel
, image_ref
, img
.width());
1075 y
= makeAbsoluteY( window_ref
, y_rel
, image_ref
, img
.height());
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
);
1085 updateSplashImage( img
, x
, y
);
1086 doPaint( QRect( x
, y
, img
.width(), img
.height()));
1090 fprintf( stderr
, "Bad image: %s\n", line
);
1094 items
= sscanf( line
, "ANIM %d %d %d %d %1023s %d %d", &number
, &x
, &y
, &frames
, buf
, &delay
, &repeat
);
1096 repeat
= 0; // default
1097 if( items
== 6 || items
== 7 )
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
);
1110 if( frames
<= 0 || frames
> MAX_ITEMS
)
1112 fprintf( stderr
, "Frames limit reached: %s\n", line
);
1115 if( splash_image
.isNull())
1116 createSplashImage();
1117 QImage imgs
= loadAnimImage( buf
, frames
);
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
);
1129 repeat
= 0; // default
1130 if( items
== 8 || items
== 9 )
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
);
1143 if( !checkRelative( window_ref
)
1144 || !checkRelative( window_ref
))
1146 fprintf( stderr
,"Bad reference point: %s\n", line
);
1149 if( frames
<= 0 || frames
> MAX_ITEMS
)
1151 fprintf( stderr
, "Frames limit reached: %s\n", line
);
1154 if( splash_image
.isNull())
1155 createSplashImage();
1156 QImage imgs
= loadAnimImage( buf
, frames
);
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 )
1172 if( number
<= 0 || number
>= MAX_ITEMS
|| animations
[ number
] == NULL
)
1174 fprintf( stderr
,"Bad number: %s\n", line
);
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 )
1187 i
< int( sizeof( states
) / sizeof( states
[ 0 ] ));
1190 if( strcmp( buf
, states
[ i
] ) == 0 )
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
1203 if( waitState( new_state
))
1209 fprintf( stderr
, "Unknown line: %s\n", line
);
1214 XDestroyWindow( qt_xdisplay(), window
);
1216 XFreePixmap( qt_xdisplay(), splash_pixmap
);
1217 splash_pixmap
= None
;
1218 splash_image
= QImage();