1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #ifdef ENABLE_QUICKSTART_LIBPNG
13 #include <X11/Xatom.h>
14 #include <X11/Xutil.h>
17 #include <X11/extensions/Xinerama.h>
20 #include "osl/endian.h"
30 #include <osl/process.h>
31 #include <osl/thread.h>
32 #include <rtl/bootstrap.h>
33 #include <rtl/ustrbuf.h>
38 unsigned char b
, g
, r
;
55 // Progress bar values
56 // taken from desktop/source/splash/splash.cxx
68 unsigned char** bitmap_rows
;
74 #define WINDOW_WIDTH 440
75 #define WINDOW_HEIGHT 299
77 #define PROGRESS_XOFFSET 12
78 #define PROGRESS_YOFFSET 18
79 #define PROGRESS_BARSPACE 2
82 #ifndef PNG_TRANSFORM_GRAY_TO_RGB
83 # define PNG_TRANSFORM_GRAY_TO_RGB 0x2000
86 static int splash_load_bmp( struct splash
* splash
, const char *filename
)
90 if ( !(file
= fopen( filename
, "r" ) ) )
93 splash
->png_ptr
= png_create_read_struct( PNG_LIBPNG_VER_STRING
, 0, 0, 0 );
94 splash
->info_ptr
= png_create_info_struct(splash
->png_ptr
);
95 png_init_io( splash
->png_ptr
, file
);
97 if( setjmp( png_jmpbuf( splash
->png_ptr
) ) )
99 png_destroy_read_struct( &(splash
->png_ptr
), &(splash
->info_ptr
), NULL
);
104 png_read_png( splash
->png_ptr
, splash
->info_ptr
,
105 PNG_TRANSFORM_EXPAND
| PNG_TRANSFORM_STRIP_ALPHA
|
106 PNG_TRANSFORM_GRAY_TO_RGB
| PNG_TRANSFORM_BGR
, NULL
);
108 splash
->bitmap_rows
= png_get_rows( splash
->png_ptr
, splash
->info_ptr
);
109 splash
->width
= png_get_image_width( splash
->png_ptr
, splash
->info_ptr
);
110 splash
->height
= png_get_image_height( splash
->png_ptr
, splash
->info_ptr
);
116 static void setup_color( int val
[3], color_t
*col
)
118 if ( val
[0] < 0 || val
[1] < 0 || val
[2] < 0 )
121 #define CONVERT_COLOR( from,to ) if ( from < 0 ) to = 0; else if ( from > 255 ) to = 255; else to = from;
122 CONVERT_COLOR( val
[0], col
->r
);
123 CONVERT_COLOR( val
[1], col
->g
);
124 CONVERT_COLOR( val
[2], col
->b
);
128 /* Fill 'array' with values of the key 'name'.
129 Its value is a comma delimited list of integers */
130 static void get_bootstrap_value( int *array
, int size
, rtlBootstrapHandle handle
, const char *name
)
132 rtl_uString
*pKey
= NULL
, *pValue
= NULL
;
134 /* get the value from the ini file */
135 rtl_uString_newFromAscii( &pKey
, name
);
136 rtl_bootstrap_get_from_handle( handle
, pKey
, &pValue
, NULL
);
138 /* the value is several numbers delimited by ',' - parse it */
139 if ( rtl_uString_getLength( pValue
) > 0 )
141 rtl_uString
*pToken
= NULL
;
143 sal_Int32 nIndex
= 0;
144 for ( ; ( nIndex
>= 0 ) && ( i
< size
); ++i
)
146 nIndex
= rtl_uString_getToken( &pToken
, pValue
, 0, ',', nIndex
);
147 array
[i
] = rtl_ustr_toInt32( rtl_uString_getStr( pToken
), 10 );
150 rtl_uString_release( pToken
);
154 rtl_uString_release( pKey
);
155 rtl_uString_release( pValue
);
159 static void splash_setup( struct splash
* splash
, int barc
[3], int framec
[3], int posx
, int posy
, int w
, int h
)
161 if ( splash
->width
<= 500 )
163 splash
->barwidth
= splash
->width
- ( 2 * PROGRESS_XOFFSET
);
164 splash
->barheight
= 6;
165 splash
->tlx
= PROGRESS_XOFFSET
;
166 splash
->tly
= splash
->height
- PROGRESS_YOFFSET
;
168 splash
->barcol
.r
= 0;
169 splash
->barcol
.g
= 0;
170 splash
->barcol
.b
= 128;
178 splash
->barwidth
= w
;
180 splash
->barheight
= h
;
182 setup_color( barc
, &(splash
->barcol
) );
183 setup_color( framec
, &(splash
->framecol
) );
186 // Universal shift: bits >= 0 - left, otherwise right
187 #define SHIFT( x, bits ) ( ( (bits) >= 0 )? ( (x) << (bits) ): ( (x) >> -(bits) ) )
189 // Position of the highest bit (more or less integer log2)
190 static inline int HIGHEST_BIT( unsigned long x
)
199 // Number of bits set to 1
200 static inline int BITS( unsigned long x
)
210 // Set 'bitmap' as the background of our 'win' window
211 static void create_pixmap(struct splash
* splash
)
215 unsigned long value_mask
= 0;
218 if ( !splash
->bitmap_rows
)
222 pixmap
= XCreatePixmap( splash
->display
, splash
->win
, splash
->width
, splash
->height
, splash
->depth
);
224 pixmap_gc
= XCreateGC( splash
->display
, pixmap
, value_mask
, &values
);
226 if ( splash
->visual
->class == TrueColor
)
228 unsigned long red_mask
= splash
->visual
->red_mask
;
229 unsigned long green_mask
= splash
->visual
->green_mask
;
230 unsigned long blue_mask
= splash
->visual
->blue_mask
;
232 unsigned long red_delta_mask
= ( 1UL << ( 8 - BITS( red_mask
) ) ) - 1;
233 unsigned long green_delta_mask
= ( 1UL << ( 8 - BITS( green_mask
) ) ) - 1;
234 unsigned long blue_delta_mask
= ( 1UL << ( 8 - BITS( blue_mask
) ) ) - 1;
236 int red_shift
= HIGHEST_BIT( red_mask
) - 8;
237 int green_shift
= HIGHEST_BIT( green_mask
) - 8;
238 int blue_shift
= HIGHEST_BIT( blue_mask
) - 8;
240 XImage
* image
= XCreateImage( splash
->display
, splash
->visual
, splash
->depth
, ZPixmap
,
241 0, NULL
, splash
->width
, splash
->height
, 32, 0 );
243 int bytes_per_line
= image
->bytes_per_line
;
244 int bpp
= image
->bits_per_pixel
;
245 int byte_order
= image
->byte_order
;
246 #if defined( _LITTLE_ENDIAN )
247 int machine_byte_order
= LSBFirst
;
248 #elif defined( _BIG_ENDIAN )
249 int machine_byte_order
= MSBFirst
;
252 fprintf( stderr
, "Unsupported machine endianity.\n" );
253 XFreeGC( splash
->display
, pixmap_gc
);
254 XFreePixmap( splash
->display
, pixmap
);
255 XDestroyImage( image
);
260 char *data
= malloc( splash
->height
* bytes_per_line
);
264 // The following dithers & converts the color_t color to one
265 // acceptable for the visual
266 #define COPY_IN_OUT( pix_size, code ) \
269 for ( y = 0; y < splash->height; ++y ) \
271 unsigned long red_delta = 0, green_delta = 0, blue_delta = 0; \
272 color_t *in = (color_t *)(splash->bitmap_rows[y]); \
273 out = data + y * bytes_per_line; \
274 for ( x = 0; x < splash->width; ++x, ++in ) \
276 unsigned long red = in->r + red_delta; \
277 unsigned long green = in->g + green_delta; \
278 unsigned long blue = in->b + blue_delta; \
279 unsigned long pixel = 0; \
282 red_delta = red & red_delta_mask; \
283 green_delta = green & green_delta_mask; \
284 blue_delta = blue & blue_delta_mask; \
292 ( SHIFT( red, red_shift ) & red_mask ) | \
293 ( SHIFT( green, green_shift ) & green_mask ) | \
294 ( SHIFT( blue, blue_shift ) & blue_mask ); \
302 if ( machine_byte_order
== byte_order
)
303 COPY_IN_OUT( 4, *( (uint32_t *)out
) = (uint32_t)pixel
; out
+= 4; )
305 COPY_IN_OUT( 4, tmp
= pixel
;
306 *( (uint8_t *)out
) = *( (uint8_t *)(&tmp
) + 3 );
307 *( (uint8_t *)out
+ 1 ) = *( (uint8_t *)(&tmp
) + 2 );
308 *( (uint8_t *)out
+ 2 ) = *( (uint8_t *)(&tmp
) + 1 );
309 *( (uint8_t *)out
+ 3 ) = *( (uint8_t *)(&tmp
) );
312 else if ( bpp
== 24 )
314 if ( machine_byte_order
== byte_order
&& byte_order
== LSBFirst
)
315 COPY_IN_OUT( 3, *( (color_t
*)out
) = *( (color_t
*)( &pixel
) ); out
+= 3; )
316 else if ( machine_byte_order
== byte_order
&& byte_order
== MSBFirst
)
317 COPY_IN_OUT( 3, tmp
= pixel
;
318 *( (uint8_t *)out
) = *( (uint8_t *)(&tmp
) + 1 );
319 *( (uint8_t *)out
+ 1 ) = *( (uint8_t *)(&tmp
) + 2 );
320 *( (uint8_t *)out
+ 2 ) = *( (uint8_t *)(&tmp
) + 3 );
323 COPY_IN_OUT( 3, tmp
= pixel
;
324 *( (uint8_t *)out
) = *( (uint8_t *)(&tmp
) + 3 );
325 *( (uint8_t *)out
+ 1 ) = *( (uint8_t *)(&tmp
) + 2 );
326 *( (uint8_t *)out
+ 2 ) = *( (uint8_t *)(&tmp
) + 1 );
329 else if ( bpp
== 16 )
331 if ( machine_byte_order
== byte_order
)
332 COPY_IN_OUT( 2, *( (uint16_t *)out
) = (uint16_t)pixel
; out
+= 2; )
334 COPY_IN_OUT( 2, tmp
= pixel
;
335 *( (uint8_t *)out
) = *( (uint8_t *)(&tmp
) + 1 );
336 *( (uint8_t *)out
+ 1 ) = *( (uint8_t *)(&tmp
) );
341 COPY_IN_OUT( 1, *( (uint8_t *)out
) = (uint8_t)pixel
; ++out
; )
345 fprintf( stderr
, "Unsupported depth: %d bits per pixel.\n", bpp
);
346 XFreeGC( splash
->display
, pixmap_gc
);
347 XFreePixmap( splash
->display
, pixmap
);
348 XDestroyImage( image
);
354 XPutImage( splash
->display
, pixmap
, pixmap_gc
, image
, 0, 0, 0, 0, splash
->width
, splash
->height
);
355 XDestroyImage( image
);
357 else //if ( depth == 1 || visual->class == DirectColor )
359 // FIXME Something like the following, but faster ;-) - XDrawPoint is not
362 for ( y
= 0; y
< splash
->height
; ++y
)
364 color_t
* color
= (color_t
*)&(splash
->bitmap_rows
[y
]);
367 for ( x
= 0; x
< splash
->width
; ++x
, ++color
)
369 int rnd
= (int)( ( (long)( random() - RAND_MAX
/2 ) * 32000 )/RAND_MAX
);
370 int luminance
= delta
+ rnd
+ 299 * (int)color
->r
+ 587 * (int)color
->g
+ 114 * (int)color
->b
;
372 if ( luminance
< 128000 )
374 XSetForeground( splash
->display
, pixmap_gc
, BlackPixel( splash
->display
, splash
->screen
) );
379 XSetForeground( splash
->display
, pixmap_gc
, WhitePixel( splash
->display
, splash
->screen
) );
380 delta
= luminance
- 255000;
383 XDrawPoint( splash
->display
, pixmap
, pixmap_gc
, x
, y
);
388 XSetWindowBackgroundPixmap( splash
->display
, splash
->win
, pixmap
);
390 XFreeGC( splash
->display
, pixmap_gc
);
391 XFreePixmap( splash
->display
, pixmap
);
394 // The old method of hiding the window decorations
395 static void suppress_decorations_motif(struct splash
* splash
)
399 unsigned long flags
, functions
, decorations
;
403 Atom a
= XInternAtom( splash
->display
, "_MOTIF_WM_HINTS", False
);
405 mwmhints
.flags
= 15; // functions, decorations, input_mode, status
406 mwmhints
.functions
= 2; // ?
407 mwmhints
.decorations
= 0;
408 mwmhints
.input_mode
= 0;
410 XChangeProperty( splash
->display
, splash
->win
, a
, a
, 32,
411 PropModeReplace
, (unsigned char*)&mwmhints
, 5 );
414 // This is a splash, set it as such.
415 // If it fails, just hide the decorations...
416 static void suppress_decorations(struct splash
* splash
)
418 Atom atom_type
= XInternAtom( splash
->display
, "_NET_WM_WINDOW_TYPE", True
);
419 Atom atom_splash
= XInternAtom( splash
->display
, "_NET_WM_WINDOW_TYPE_SPLASH", True
);
421 if ( atom_type
!= None
&& atom_splash
!= None
)
422 XChangeProperty( splash
->display
, splash
->win
, atom_type
, XA_ATOM
, 32,
423 PropModeReplace
, (unsigned char*)&atom_splash
, 1 );
425 suppress_decorations_motif(splash
); // FIXME: Unconditional until Metacity/compiz's SPLASH handling is fixed
429 // Return: 1 - success, 0 - failure
430 static int splash_create_window( struct splash
* splash
, int argc
, char** argv
)
432 char *display_name
= NULL
;
435 int display_width
= 0;
436 int display_height
= 0;
437 unsigned long value_mask
= 0;
439 const char* name
= "LibreOffice";
440 const char* icon
= "icon"; // FIXME
441 XSizeHints size_hints
;
443 int n_xinerama_screens
= 1;
444 XineramaScreenInfo
* p_screens
= NULL
;
447 for ( i
= 0; i
< argc
; i
++ )
449 if ( !strcmp( argv
[i
], "-display" ) || !strcmp( argv
[i
], "--display" ) )
451 display_name
= ( i
+ 1 < argc
)? argv
[i
+1]: NULL
;
457 display_name
= getenv( "DISPLAY" );
460 splash
->display
= XOpenDisplay( display_name
);
461 if ( !splash
->display
)
463 fprintf( stderr
, "Failed to open display\n" );
468 splash
->screen
= DefaultScreen( splash
->display
);
469 splash
->depth
= DefaultDepth( splash
->display
, splash
->screen
);
470 splash
->color_map
= DefaultColormap( splash
->display
, splash
->screen
);
471 splash
->visual
= DefaultVisual( splash
->display
, splash
->screen
);
473 root_win
= RootWindow( splash
->display
, splash
->screen
);
474 display_width
= DisplayWidth( splash
->display
, splash
->screen
);
475 display_height
= DisplayHeight( splash
->display
, splash
->screen
);
478 p_screens
= XineramaQueryScreens( splash
->display
, &n_xinerama_screens
);
482 for( ; j
< n_xinerama_screens
; j
++ )
484 if ( p_screens
[j
].screen_number
== splash
->screen
)
486 display_width
= p_screens
[j
].width
;
487 display_height
= p_screens
[j
].height
;
495 splash
->win
= XCreateSimpleWindow( splash
->display
, root_win
,
496 ( display_width
- splash
->width
) / 2, ( display_height
- splash
->height
) / 2,
497 splash
->width
, splash
->height
, 0,
498 BlackPixel( splash
->display
, splash
->screen
), BlackPixel( splash
->display
, splash
->screen
) );
500 XSetWindowColormap( splash
->display
, splash
->win
, splash
->color_map
);
503 #define FILL_COLOR( xcol,col ) xcol.red = 256*col.r; xcol.green = 256*col.g; xcol.blue = 256*col.b;
504 FILL_COLOR( splash
->barcolor
, splash
->barcol
);
505 FILL_COLOR( splash
->framecolor
, splash
->framecol
);
508 XAllocColor( splash
->display
, splash
->color_map
, &(splash
->barcolor
) );
509 XAllocColor( splash
->display
, splash
->color_map
, &(splash
->framecolor
) );
511 // not resizable, no decorations, etc.
512 splash
->gc
= XCreateGC( splash
->display
, splash
->win
, value_mask
, &values
);
514 size_hints
.flags
= PPosition
| PSize
| PMinSize
| PMaxSize
;
515 size_hints
.min_width
= splash
->width
;
516 size_hints
.max_width
= splash
->width
;
517 size_hints
.min_height
= splash
->height
;
518 size_hints
.max_height
= splash
->height
;
520 XSetStandardProperties( splash
->display
, splash
->win
, name
, icon
, None
,
524 suppress_decorations(splash
);
525 create_pixmap(splash
);
528 XSelectInput( splash
->display
, splash
->win
, 0 );
529 XMapWindow( splash
->display
, splash
->win
);
534 // Re-draw & process the events
535 // Just throwing them away - we do not need anything more...
536 static void process_events(struct splash
* splash
)
541 XFlush( splash
->display
);
542 num_events
= XPending( splash
->display
);
543 while ( num_events
> 0 )
546 XNextEvent( splash
->display
, &xev
);
551 static rtl_String
* ustr_to_str( rtl_uString
* pStr
)
553 rtl_String
*pOut
= NULL
;
555 rtl_uString2String( &pOut
, rtl_uString_getStr( pStr
),
556 rtl_uString_getLength( pStr
), osl_getThreadTextEncoding(), OUSTRING_TO_OSTRING_CVTFLAGS
);
561 #define IMG_SUFFIX ".png"
563 static void splash_load_image( struct splash
* splash
, rtl_uString
* pUAppPath
)
565 /* FIXME-BCP47: if we wanted to support language tags here that would get
566 * complicated, this is C-source not C++ so LanguageTag can't be used. For
567 * now the splash screen will have to get along with language-territory. */
569 char *pBuffer
, *pSuffix
, *pLocale
;
571 rtl_Locale
*pLoc
= NULL
;
572 rtl_String
*pLang
, *pCountry
, *pAppPath
;
574 osl_getProcessLocale (&pLoc
);
575 pLang
= ustr_to_str (pLoc
->Language
);
576 pCountry
= ustr_to_str (pLoc
->Country
);
578 nLocSize
= strlen (pLang
->buffer
) + strlen (pCountry
->buffer
) + 8;
579 pLocale
= malloc (nLocSize
);
581 strcpy (pLocale
+ 1, pLang
->buffer
);
582 strcat (pLocale
, "_");
583 strcat (pLocale
, pCountry
->buffer
);
585 rtl_string_release( pCountry
);
586 rtl_string_release( pLang
);
588 pAppPath
= ustr_to_str (pUAppPath
);
589 pBuffer
= malloc (pAppPath
->length
+ nLocSize
+ 256);
590 strcpy (pBuffer
, pAppPath
->buffer
);
591 pSuffix
= pBuffer
+ pAppPath
->length
;
592 rtl_string_release( pAppPath
);
594 strcpy (pSuffix
, "intro");
595 strcat (pSuffix
, pLocale
);
596 strcat (pSuffix
, IMG_SUFFIX
);
597 if ( splash_load_bmp( splash
, pBuffer
) )
600 strcpy (pSuffix
, "intro" IMG_SUFFIX
);
601 if ( splash_load_bmp( splash
, pBuffer
) )
604 fprintf (stderr
, "Failed to find intro image\n");
611 /* Load the colors and size of the splash. */
612 static void splash_load_defaults( struct splash
* splash
, rtl_uString
* pAppPath
, sal_Bool
* bNoDefaults
)
614 rtl_uString
*pSettings
= NULL
, *pTmp
= NULL
;
615 rtlBootstrapHandle handle
;
616 int logo
[1] = { -1 },
617 bar
[3] = { -1, -1, -1 },
618 frame
[3] = { -1, -1, -1 },
620 size
[2] = { -1, -1 };
622 /* costruct the sofficerc file location */
623 rtl_uString_newFromAscii( &pSettings
, "file://" );
624 rtl_uString_newConcat( &pSettings
, pSettings
, pAppPath
);
625 rtl_uString_newConcat( &pSettings
, pSettings
, pTmp
);
626 rtl_uString_newFromAscii( &pTmp
, SAL_CONFIGFILE( "soffice" ) );
627 rtl_uString_newConcat( &pSettings
, pSettings
, pTmp
);
629 /* use it as the bootstrap file */
630 handle
= rtl_bootstrap_args_open( pSettings
);
633 get_bootstrap_value( logo
, 1, handle
, "Logo" );
634 get_bootstrap_value( bar
, 3, handle
, "ProgressBarColor" );
635 get_bootstrap_value( frame
, 3, handle
, "ProgressFrameColor" );
636 get_bootstrap_value( pos
, 2, handle
, "ProgressPosition" );
637 get_bootstrap_value( size
, 2, handle
, "ProgressSize" );
641 *bNoDefaults
= sal_True
;
644 splash_setup( splash
, bar
, frame
, pos
[0], pos
[1], size
[0], size
[1] );
647 rtl_bootstrap_args_close( handle
);
648 rtl_uString_release( pSettings
);
649 rtl_uString_release( pTmp
);
654 void splash_draw_progress( struct splash
* splash
, int progress
)
667 if ( progress
> 100 )
672 length
= ( progress
* splash
->barwidth
/ 100 ) - ( 2 * splash
->barspace
);
678 XSetForeground( splash
->display
, splash
->gc
, splash
->framecolor
.pixel
);
679 XDrawRectangle( splash
->display
, splash
->win
, splash
->gc
, splash
->tlx
, splash
->tly
,
680 splash
->barwidth
, splash
->barheight
);
683 XSetForeground( splash
->display
, splash
->gc
, splash
->barcolor
.pixel
);
684 XFillRectangle( splash
->display
, splash
->win
, splash
->gc
,
685 splash
->tlx
+ splash
->barspace
, splash
->tly
+ splash
->barspace
,
686 length
+ 1, splash
->barheight
- 2 * splash
->barspace
+ 1 );
689 process_events(splash
);
692 void splash_destroy(struct splash
* splash
)
700 XFreeGC(splash
->display
, splash
->gc
);
704 XCloseDisplay( splash
->display
);
705 splash
->display
= NULL
;
706 png_destroy_read_struct( &(splash
->png_ptr
), &(splash
->info_ptr
), NULL
);
712 struct splash
* splash_create(rtl_uString
* pAppPath
, int argc
, char** argv
)
714 struct splash
* splash
;
715 sal_Bool bNoDefaults
= sal_False
;
717 splash
= calloc(1, sizeof(struct splash
));
720 splash
->width
= WINDOW_WIDTH
;
721 splash
->height
= WINDOW_HEIGHT
;
725 splash
->barwidth
= 263;
726 splash
->barheight
= 8;
727 splash
->barspace
= PROGRESS_BARSPACE
;
728 splash
->barcol
.b
= 18;
729 splash
->barcol
.g
= 202;
730 splash
->barcol
.r
= 157;
731 splash
->framecol
.b
= 0xD3;
732 splash
->framecol
.g
= 0xD3;
733 splash
->framecol
.r
= 0xD3;
735 splash_load_image( splash
, pAppPath
);
736 splash_load_defaults( splash
, pAppPath
, &bNoDefaults
);
738 if (!bNoDefaults
&& splash_create_window( splash
, argc
, argv
) )
740 splash_draw_progress( splash
, 0 );
744 splash_destroy(splash
);
751 #else /* not ENABLE_QUICKSTART_LIBPNG */
753 #include <rtl/ustrbuf.h>
759 /* Stubs that will never be called in this case */
760 void splash_draw_progress( struct splash
* splash
, int progress
)
762 (void)splash
; (void)progress
;
765 void splash_destroy(struct splash
* splash
)
770 struct splash
* splash_create(rtl_uString
* pAppPath
, int argc
, char** argv
)
772 (void)pAppPath
; (void)argc
; (void)argv
;
777 #endif // ENABLE_QUICKSTART_LIBPNG
779 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */