summaryrefslogtreecommitdiff
path: root/desktop/unx/source/splashx.c
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/unx/source/splashx.c')
-rwxr-xr-xdesktop/unx/source/splashx.c648
1 files changed, 648 insertions, 0 deletions
diff --git a/desktop/unx/source/splashx.c b/desktop/unx/source/splashx.c
new file mode 100755
index 000000000000..6bcb10ef149d
--- /dev/null
+++ b/desktop/unx/source/splashx.c
@@ -0,0 +1,648 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Version: MPL 1.1 / GPLv3+ / LGPLv3+
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Initial Developer of the Original Code is
+ * Novell, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2010 the
+ * Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Jan Holesovsky <kendy@novell.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
+ * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
+ * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
+ * instead of those above.
+ */
+
+#ifdef ENABLE_QUICKSTART_LIBPNG
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+
+#define USE_LIBPNG
+
+#include "osl/endian.h"
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef USE_LIBPNG
+# include <png.h>
+#endif
+
+#include "splashx.h"
+
+typedef struct {
+ unsigned char b, g, r;
+} color_t;
+
+#define WINDOW_WIDTH 440
+#define WINDOW_HEIGHT 299
+
+#define PROGRESS_XOFFSET 12
+#define PROGRESS_YOFFSET 18
+#define PROGRESS_BARSPACE 2
+
+static Display *display = NULL;
+static int screen;
+static int depth;
+static Visual *visual = NULL;
+
+static int width = WINDOW_WIDTH;
+static int height = WINDOW_HEIGHT;
+
+static Colormap color_map;
+static Window win;
+static GC gc;
+
+// Progress bar values
+// taken from desktop/source/splash/splash.cxx
+static int tlx = 212;
+static int tly = 216;
+static int barwidth = 263;
+static int barheight = 8;
+static int barspace = PROGRESS_BARSPACE;
+static color_t barcol = { 18, 202, 157 };
+static color_t framecol = { 0xD3, 0xD3, 0xD3 };
+
+static XColor barcolor;
+static XColor framecolor;
+
+static unsigned char **bitmap_rows = NULL;
+
+#define BMP_HEADER_LEN 14
+#define WIN_INFO_LEN 40
+
+#define UINT8( x ) ( (unsigned int)( ( (uint8_t *)( x ) )[0] ) )
+
+#define UINT16( x ) ( ( (unsigned int)( ( (uint8_t *)( x ) )[0] ) ) + \
+ ( ( (unsigned int)( ( (uint8_t *)( x ) )[1] ) ) << 8 ) )
+
+#define UINT32( x ) ( ( (unsigned int)( ( (uint8_t *)( x ) )[0] ) ) + \
+ ( ( (unsigned int)( ( (uint8_t *)( x ) )[1] ) ) << 8 ) + \
+ ( ( (unsigned int)( ( (uint8_t *)( x ) )[2] ) ) << 16 ) + \
+ ( ( (unsigned int)( ( (uint8_t *)( x ) )[3] ) ) << 24 ) )
+
+#define MAX( x, y ) ( ( (x) > (y) )? (x): (y) )
+
+#define LOAD_FAILURE( msg ) \
+ { \
+ fprintf( stderr, "%s: " msg, filename ); \
+ close( fd ); \
+ return 0; \
+ }
+
+#ifdef USE_LIBPNG
+
+/* libpng-1.2.41 */
+#ifndef PNG_TRANSFORM_GRAY_TO_RGB
+# define PNG_TRANSFORM_GRAY_TO_RGB 0x2000
+#endif
+
+png_structp png_ptr = NULL;
+png_infop info_ptr = NULL;
+
+int splash_load_bmp( const char *filename )
+{
+ FILE *file;
+
+ if ( !(file = fopen( filename, "r" ) ) )
+ return 0;
+
+ png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );
+ info_ptr = png_create_info_struct(png_ptr);
+ png_init_io( png_ptr, file );
+
+ if( setjmp( png_jmpbuf( png_ptr ) ) )
+ {
+ png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
+ fclose( file );
+ return 0;
+ }
+
+ png_read_png( png_ptr, info_ptr,
+ PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_ALPHA |
+ PNG_TRANSFORM_GRAY_TO_RGB | PNG_TRANSFORM_BGR, NULL);
+
+ bitmap_rows = png_get_rows( png_ptr, info_ptr );
+ width = png_get_image_width( png_ptr, info_ptr );
+ height = png_get_image_height( png_ptr, info_ptr );
+
+ return 1;
+}
+#else
+
+/* Load the specified Windows 24bit BMP to 'bitmap'
+ * Return: 1 - success, 0 - failure */
+int splash_load_bmp( const char *filename )
+{
+ int fd = open( filename, O_RDONLY );
+ if ( fd < 0 )
+ return 0;
+
+ char file_header[ BMP_HEADER_LEN ];
+
+ if ( read( fd, file_header, BMP_HEADER_LEN ) != BMP_HEADER_LEN || file_header[0] != 'B' || file_header[1] != 'M' )
+ LOAD_FAILURE( "Not a bitmap.\n" );
+
+ char info_header[ WIN_INFO_LEN ];
+ if ( read( fd, info_header, 4 ) != 4 )
+ LOAD_FAILURE( "Unable to read the header.\n" );
+
+ int header_size = UINT32( info_header );
+ if ( header_size != WIN_INFO_LEN )
+ LOAD_FAILURE( "Not a Windows bitmap.\n" );
+
+ if ( read( fd, info_header + 4, WIN_INFO_LEN - 4 ) != WIN_INFO_LEN - 4 )
+ LOAD_FAILURE( "The header ended too early.\n" );
+
+ width = UINT32( info_header + 4 );
+ height = UINT32( info_header + 8 );
+
+ int bits = UINT16( info_header + 14 );
+ int compression = UINT16( info_header + 16 );
+
+ if ( bits != 24 )
+ LOAD_FAILURE( "Just 24 bpp bitmaps are supported.\n" );
+
+ if ( compression != 0 )
+ LOAD_FAILURE( "Just uncompressed bitmaps are supported.\n" );
+
+ size_t bitmap_size = width * height * 3;
+ unsigned char *bitmap = malloc( bitmap_size );
+ if ( bitmap == NULL )
+ LOAD_FAILURE( "Cannot allocate memory for the data.\n" );
+
+ if ( read( fd, bitmap, bitmap_size ) != bitmap_size )
+ LOAD_FAILURE( "Cannot read the bitmap data.\n" );
+
+ bitmap_rows = malloc (sizeof (unsigned char*) * height);
+ int i;
+ for (i = 0; i < height; i++)
+ bitmap_rows[i] = bitmap + (width * height * 3) - width * 3 * (i + 1);
+
+ close( fd );
+ return 1;
+}
+#endif
+
+static void setup_color( int val[3], color_t *col )
+{
+ if ( val[0] < 0 || val[1] < 0 || val[2] < 0 )
+ return;
+
+#define CONVERT_COLOR( from,to ) if ( from < 0 ) to = 0; else if ( from > 255 ) to = 255; else to = from;
+ CONVERT_COLOR( val[0], col->r );
+ CONVERT_COLOR( val[1], col->g );
+ CONVERT_COLOR( val[2], col->b );
+#undef CONVERT_COLOR
+}
+
+// setup
+void splash_setup( int barc[3], int framec[3], int posx, int posy, int w, int h )
+{
+ if ( width <= 500 )
+ {
+ barwidth = width - ( 2 * PROGRESS_XOFFSET );
+ barheight = 6;
+ tlx = PROGRESS_XOFFSET;
+ tly = height - PROGRESS_YOFFSET;
+
+ barcol.r = 0;
+ barcol.g = 0;
+ barcol.b = 128;
+ }
+
+ if ( posx >= 0 )
+ tlx = posx;
+ if ( posy >= 0 )
+ tly = posy;
+ if ( w >= 0 )
+ barwidth = w;
+ if ( h >= 0 )
+ barheight = h;
+
+ setup_color( barc, &barcol );
+ setup_color( framec, &framecol );
+}
+
+// Universal shift: bits >= 0 - left, otherwise right
+#define SHIFT( x, bits ) ( ( (bits) >= 0 )? ( (x) << (bits) ): ( (x) >> -(bits) ) )
+
+// Position of the highest bit (more or less integer log2)
+inline int HIGHEST_BIT( unsigned long x )
+{
+ int i = 0;
+ for ( ; x; ++i )
+ x >>= 1;
+
+ return i;
+}
+
+// Number of bits set to 1
+inline int BITS( unsigned long x )
+{
+ int i = 0;
+ for ( ; x; x >>= 1 )
+ if ( x & 1UL )
+ ++i;
+
+ return i;
+}
+
+// Set 'bitmap' as the background of our 'win' window
+static void create_pixmap()
+{
+ if ( !bitmap_rows )
+ return;
+
+ Pixmap pixmap = XCreatePixmap( display, win, width, height, depth );
+
+ unsigned long value_mask = 0;
+ XGCValues values;
+ GC pixmap_gc = XCreateGC( display, pixmap, value_mask, &values );
+
+ if ( visual->class == TrueColor )
+ {
+ unsigned long red_mask = visual->red_mask;
+ unsigned long green_mask = visual->green_mask;
+ unsigned long blue_mask = visual->blue_mask;
+
+ unsigned long red_delta_mask = ( 1UL << ( 8 - BITS( red_mask ) ) ) - 1;
+ unsigned long green_delta_mask = ( 1UL << ( 8 - BITS( green_mask ) ) ) - 1;
+ unsigned long blue_delta_mask = ( 1UL << ( 8 - BITS( blue_mask ) ) ) - 1;
+
+ int red_shift = HIGHEST_BIT( red_mask ) - 8;
+ int green_shift = HIGHEST_BIT( green_mask ) - 8;
+ int blue_shift = HIGHEST_BIT( blue_mask ) - 8;
+
+ XImage *image = XCreateImage( display, visual, depth, ZPixmap,
+ 0, NULL, width, height, 32, 0 );
+
+ int bytes_per_line = image->bytes_per_line;
+ int bpp = image->bits_per_pixel;
+ int byte_order = image->byte_order;
+#if defined( _LITTLE_ENDIAN )
+ int machine_byte_order = LSBFirst;
+#elif defined( _BIG_ENDIAN )
+ int machine_byte_order = MSBFirst;
+#else
+ {
+ fprintf( stderr, "Unsupported machine endianity.\n" );
+ XFreeGC( display, pixmap_gc );
+ XFreePixmap( display, pixmap );
+ XDestroyImage( image );
+ return;
+ }
+#endif
+
+ char *data = malloc( height * bytes_per_line );
+ image->data = data;
+
+ // The following dithers & converts the color_t color to one
+ // acceptable for the visual
+#define COPY_IN_OUT( pix_size, code ) \
+ { \
+ int x, y; \
+ for ( y = 0; y < height; ++y ) \
+ { \
+ out = data + y * bytes_per_line; \
+ unsigned long red_delta = 0, green_delta = 0, blue_delta = 0; \
+ color_t *in = (color_t *)bitmap_rows[y]; \
+ for ( x = 0; x < width; ++x, ++in ) \
+ { \
+ unsigned long red = in->r + red_delta; \
+ unsigned long green = in->g + green_delta; \
+ unsigned long blue = in->b + blue_delta; \
+ red_delta = red & red_delta_mask; \
+ green_delta = green & green_delta_mask; \
+ blue_delta = blue & blue_delta_mask; \
+ if ( red > 255 ) \
+ red = 255; \
+ if ( green > 255 ) \
+ green = 255; \
+ if ( blue > 255 ) \
+ blue = 255; \
+ unsigned long pixel = \
+ ( SHIFT( red, red_shift ) & red_mask ) | \
+ ( SHIFT( green, green_shift ) & green_mask ) | \
+ ( SHIFT( blue, blue_shift ) & blue_mask ); \
+ code \
+ } \
+ } \
+ }
+
+ char *out = data;
+
+ if ( bpp == 32 )
+ {
+ if ( machine_byte_order == byte_order )
+ COPY_IN_OUT( 4, *( (uint32_t *)out ) = (uint32_t)pixel; out += 4; )
+ else
+ COPY_IN_OUT( 4, uint32_t tmp = pixel;
+ *( (uint8_t *)out ) = *( (uint8_t *)(&tmp) + 3 );
+ *( (uint8_t *)out + 1 ) = *( (uint8_t *)(&tmp) + 2 );
+ *( (uint8_t *)out + 2 ) = *( (uint8_t *)(&tmp) + 1 );
+ *( (uint8_t *)out + 3 ) = *( (uint8_t *)(&tmp) );
+ out += 4; )
+ }
+ else if ( bpp == 24 )
+ {
+ if ( machine_byte_order == byte_order && byte_order == LSBFirst )
+ COPY_IN_OUT( 3, *( (color_t *)out ) = *( (color_t *)( &pixel ) ); out += 3; )
+ else if ( machine_byte_order == byte_order && byte_order == MSBFirst )
+ COPY_IN_OUT( 3, uint32_t tmp = pixel;
+ *( (uint8_t *)out ) = *( (uint8_t *)(&tmp) + 1 );
+ *( (uint8_t *)out + 1 ) = *( (uint8_t *)(&tmp) + 2 );
+ *( (uint8_t *)out + 2 ) = *( (uint8_t *)(&tmp) + 3 );
+ out += 3; )
+ else
+ COPY_IN_OUT( 3, uint32_t tmp = pixel;
+ *( (uint8_t *)out ) = *( (uint8_t *)(&tmp) + 3 );
+ *( (uint8_t *)out + 1 ) = *( (uint8_t *)(&tmp) + 2 );
+ *( (uint8_t *)out + 2 ) = *( (uint8_t *)(&tmp) + 1 );
+ out += 3; )
+ }
+ else if ( bpp == 16 )
+ {
+ if ( machine_byte_order == byte_order )
+ COPY_IN_OUT( 2, *( (uint16_t *)out ) = (uint16_t)pixel; out += 2; )
+ else
+ COPY_IN_OUT( 2, uint16_t tmp = pixel;
+ *( (uint8_t *)out ) = *( (uint8_t *)(&tmp) + 1 );
+ *( (uint8_t *)out + 1 ) = *( (uint8_t *)(&tmp) );
+ out += 2; );
+ }
+ else if ( bpp == 8 )
+ {
+ COPY_IN_OUT( 1, *( (uint8_t *)out ) = (uint8_t)pixel; ++out; )
+ }
+ else
+ {
+ fprintf( stderr, "Unsupported depth: %d bits per pixel.\n", bpp );
+ XFreeGC( display, pixmap_gc );
+ XFreePixmap( display, pixmap );
+ XDestroyImage( image );
+ return;
+ }
+
+#undef COPY_IN_OUT
+
+ XPutImage( display, pixmap, pixmap_gc, image, 0, 0, 0, 0, width, height );
+ XDestroyImage( image );
+ }
+ else //if ( depth == 1 || visual->class == DirectColor )
+ {
+ // FIXME Something like the following, but faster ;-) - XDrawPoint is not
+ // a good idea...
+ int x, y;
+ for ( y = 0; y < height; ++y )
+ {
+ color_t *color = (color_t *)&bitmap_rows[y];
+
+ int delta = 0;
+ for ( x = 0; x < width; ++x, ++color )
+ {
+ int rnd = (int)( ( (long)( random() - RAND_MAX/2 ) * 32000 )/RAND_MAX );
+ int luminance = delta + rnd + 299 * (int)color->r + 587 * (int)color->g + 114 * (int)color->b;
+
+ if ( luminance < 128000 )
+ {
+ XSetForeground( display, pixmap_gc, BlackPixel( display, screen ) );
+ delta = luminance;
+ }
+ else
+ {
+ XSetForeground( display, pixmap_gc, WhitePixel( display, screen ) );
+ delta = luminance - 255000;
+ }
+
+ XDrawPoint( display, pixmap, pixmap_gc, x, y );
+ }
+ }
+ }
+
+ XSetWindowBackgroundPixmap( display, win, pixmap );
+
+ XFreeGC( display, pixmap_gc );
+ XFreePixmap( display, pixmap );
+}
+
+// The old method of hiding the window decorations
+static void suppress_decorations_motif()
+{
+ struct {
+ unsigned long flags, functions, decorations;
+ long input_mode;
+ } mwmhints;
+
+ Atom a = XInternAtom( display, "_MOTIF_WM_HINTS", False );
+
+ mwmhints.flags = 15; // functions, decorations, input_mode, status
+ mwmhints.functions = 2; // ?
+ mwmhints.decorations = 0;
+ mwmhints.input_mode = 0;
+
+ XChangeProperty( display, win, a, a, 32,
+ PropModeReplace, (unsigned char*)&mwmhints, 5 );
+}
+
+// This is a splash, set it as such.
+// If it fails, just hide the decorations...
+static void suppress_decorations()
+{
+ Atom atom_type = XInternAtom( display, "_NET_WM_WINDOW_TYPE", True );
+ Atom atom_splash = XInternAtom( display, "_NET_WM_WINDOW_TYPE_SPLASH", True );
+
+ if ( atom_type != None && atom_splash != None )
+ XChangeProperty( display, win, atom_type, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&atom_splash, 1 );
+ //else
+ suppress_decorations_motif(); // FIXME: Unconditional until Metacity/compiz's SPLASH handling is fixed
+}
+
+// Create the window
+// Return: 1 - success, 0 - failure
+int splash_create_window( int argc, char** argv )
+{
+ char *display_name = NULL;
+ int i;
+ for ( i = 0; i < argc; i++ )
+ {
+ if ( !strcmp( argv[i], "-display" ) || !strcmp( argv[i], "--display" ) )
+ display_name = ( i + 1 < argc )? argv[i+1]: NULL;
+ }
+
+ if ( !display_name )
+ display_name = getenv( "DISPLAY" );
+
+ // init display
+ display = XOpenDisplay( display_name );
+ if ( !display )
+ {
+ fprintf( stderr, "Failed to open display\n" );
+ return 0;
+ }
+
+ // create the window
+ screen = DefaultScreen( display );
+ depth = DefaultDepth( display, screen );
+ color_map = DefaultColormap( display, screen );
+ visual = DefaultVisual( display, screen );
+
+ Window root_win = RootWindow( display, screen );
+ int display_width = DisplayWidth( display, screen );
+ int display_height = DisplayHeight( display, screen );
+
+ win = XCreateSimpleWindow( display, root_win,
+ ( display_width - width ) / 2, ( display_height - height ) / 2,
+ width, height, 0,
+ BlackPixel( display, screen ), BlackPixel( display, screen ) );
+
+ XSetWindowColormap( display, win, color_map );
+
+ // setup colors
+#define FILL_COLOR( xcol,col ) xcol.red = 256*col.r; xcol.green = 256*col.g; xcol.blue = 256*col.b;
+ FILL_COLOR( barcolor, barcol );
+ FILL_COLOR( framecolor, framecol );
+#undef FILL_COLOR
+
+ XAllocColor( display, color_map, &barcolor );
+ XAllocColor( display, color_map, &framecolor );
+
+ // not resizable, no decorations, etc.
+ unsigned long value_mask = 0;
+ XGCValues values;
+ gc = XCreateGC( display, win, value_mask, &values );
+
+ XSizeHints size_hints;
+ size_hints.flags = PPosition | PSize | PMinSize | PMaxSize;
+ size_hints.min_width = width;
+ size_hints.max_width = width;
+ size_hints.min_height = height;
+ size_hints.max_height = height;
+
+ char *name = "LibreOffice";
+ char *icon = "icon"; // FIXME
+
+ XSetStandardProperties( display, win, name, icon, None,
+ 0, 0, &size_hints );
+
+ // the actual work
+ suppress_decorations();
+ create_pixmap();
+
+ // show it
+ XSelectInput( display, win, 0 );
+ XMapWindow( display, win );
+
+ return 1;
+}
+
+// Re-draw & process the events
+// Just throwing them away - we do not need anything more...
+static void process_events()
+{
+ XEvent xev;
+ int num_events;
+
+ XFlush( display );
+ num_events = XPending( display );
+ while ( num_events > 0 )
+ {
+ num_events--;
+ XNextEvent( display, &xev );
+ }
+}
+
+// Draw the progress
+void splash_draw_progress( int progress )
+{
+ if (!display)
+ return;
+
+ // sanity
+ if ( progress < 0 )
+ progress = 0;
+ if ( progress > 100 )
+ progress = 100;
+
+ // draw progress...
+ int length = ( progress * barwidth / 100 ) - ( 2 * barspace );
+ if ( length < 0 )
+ length = 0;
+
+ // border
+ XSetForeground( display, gc, framecolor.pixel );
+ XDrawRectangle( display, win, gc,
+ tlx, tly,
+ barwidth, barheight );
+
+ // progress bar
+ XSetForeground( display, gc, barcolor.pixel );
+ XFillRectangle( display, win, gc,
+ tlx + barspace, tly + barspace,
+ length + 1, barheight - 2*barspace + 1 );
+
+ // pending events
+ process_events();
+}
+
+// Close the window & cleanup
+void splash_close_window()
+{
+ if (display)
+ XCloseDisplay( display );
+#ifdef USE_LIBPNG
+ png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
+#else
+ free( bitmap_rows );
+#endif
+ bitmap_rows = NULL;
+}
+
+#else /* not ENABLE_QUICKSTART_LIBPNG */
+
+/* Stubs that will never be called in this case */
+
+int splash_load_bmp( const char *filename )
+{
+ (void)filename;
+ return 1;
+}
+void splash_setup( int barc[3], int framec[3], int posx, int posy, int w, int h )
+{
+ (void)barc; (void)framec; (void)posx; (void)posy; (void)w; (void)h;
+}
+int splash_create_window( int argc, char** argv )
+{
+ (void)argc; (void)argv;
+ return 1;
+}
+void splash_close_window()
+{
+}
+void splash_draw_progress( int progress )
+{
+ (void)progress;
+}
+
+#endif // ENABLE_QUICKSTART_LIBPNG
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */