diff options
Diffstat (limited to 'desktop/unx/source')
-rw-r--r-- | desktop/unx/source/args.c | 138 | ||||
-rw-r--r-- | desktop/unx/source/args.h | 28 | ||||
-rw-r--r-- | desktop/unx/source/file_image.h | 69 | ||||
-rw-r--r-- | desktop/unx/source/file_image_unx.c | 121 | ||||
-rw-r--r-- | desktop/unx/source/pagein.c | 102 | ||||
-rw-r--r-- | desktop/unx/source/pagein.h | 34 | ||||
-rw-r--r-- | desktop/unx/source/splashx.c | 811 | ||||
-rw-r--r-- | desktop/unx/source/splashx.h | 29 | ||||
-rw-r--r-- | desktop/unx/source/start.c | 887 |
9 files changed, 2219 insertions, 0 deletions
diff --git a/desktop/unx/source/args.c b/desktop/unx/source/args.c new file mode 100644 index 0000000000..199b58a8e5 --- /dev/null +++ b/desktop/unx/source/args.c @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#include <stdlib.h> +#include <string.h> +#include <osl/process.h> + +#include "args.h" + +/* do we start -env: */ +static int +is_env_arg (rtl_uString const *str) +{ + return !rtl_ustr_ascii_compare_WithLength (str->buffer, 5, "-env:"); +} + +static const struct { + const char *name; + unsigned int bInhibitSplash : 1; + unsigned int bInhibitPagein : 1; + unsigned int bInhibitJavaLdx : 1; + unsigned int bInhibitPipe : 1; + const char *pPageinType; +} pArgDescr[] = { + /* have a trailing argument */ + { "pt", 1, 0, 0, 0, NULL }, + { "p", 1, 0, 0, 0, NULL }, + { "display", 0, 0, 0, 0, NULL }, + + /* no splash */ + { "nologo", 1, 0, 0, 0, NULL }, + { "headless", 1, 0, 0, 0, NULL }, + { "invisible", 1, 0, 0, 0, NULL }, + { "quickstart", 1, 0, 0, 0, NULL }, + { "minimized", 1, 0, 0, 0, NULL }, + { "convert-to", 1, 0, 0, 0, NULL }, + { "cat", 1, 0, 0, 0, NULL }, + + /* pagein bits */ + { "writer", 0, 0, 0, 0, "pagein-writer" }, + { "calc", 0, 0, 0, 0, "pagein-calc" }, + { "draw", 0, 0, 0, 0, "pagein-draw" }, + { "impress", 0, 0, 0, 0, "pagein-impress" }, + + /* Do not send --help/--version over the pipe, as their output shall go to + the calling process's stdout (ideally, this would also happen in the + presence of unknown options); also prevent splash/pagein/javaldx overhead + (as these options will be processed early in soffice_main): */ + { "version", 1, 1, 1, 1, NULL }, + { "help", 1, 1, 1, 1, NULL }, + { "h", 1, 1, 1, 1, NULL }, + { "?", 1, 1, 1, 1, NULL }, +}; + +Args *args_parse (void) +{ + Args *args; + sal_uInt32 nArgs, i, j; + + nArgs = osl_getCommandArgCount(); + i = sizeof (Args) + sizeof (rtl_uString *) * nArgs; + args = malloc (i); + memset (args, 0, i); + args->nArgsTotal = nArgs; + + j = 0; + + /* sort the -env: args to the front */ + for ( i = 0; i < nArgs; ++i ) + { + rtl_uString *pTmp = NULL; + osl_getCommandArg( i, &pTmp ); + if (is_env_arg (pTmp)) + args->ppArgs[j++] = pTmp; + else + rtl_uString_release (pTmp); + } + args->nArgsEnv = j; + + /* Then the other args */ + for ( i = 0; i < nArgs; ++i ) + { + rtl_uString *pTmp = NULL; + + osl_getCommandArg( i, &pTmp ); + if (!is_env_arg (pTmp)) + args->ppArgs[j++] = pTmp; + else + rtl_uString_release (pTmp); + } + + for ( i = args->nArgsEnv; i < args->nArgsTotal; i++ ) + { + const sal_Unicode *arg = args->ppArgs[i]->buffer; + sal_Int32 length = args->ppArgs[i]->length; + + /* grok only parameters */ + if (arg[0] != '-') + continue; + + while (length > 1 && arg[0] == '-') { + arg++; + length--; + } + + for ( j = 0; j < SAL_N_ELEMENTS (pArgDescr); ++j ) { + if (rtl_ustr_ascii_compare_WithLength( + arg, length, pArgDescr[j].name) + == 0) + { + args->bInhibitSplash |= pArgDescr[j].bInhibitSplash; + args->bInhibitPagein |= pArgDescr[j].bInhibitPagein; + args->bInhibitJavaLdx |= pArgDescr[j].bInhibitJavaLdx; + args->bInhibitPipe |= pArgDescr[j].bInhibitPipe; + if (pArgDescr[j].pPageinType) + args->pPageinType = pArgDescr[j].pPageinType; + break; + } + } + } + + return args; +} + +void +args_free (Args *args) +{ + /* FIXME: free ppArgs */ + rtl_uString_release( args->pAppPath ); + free (args); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/unx/source/args.h b/desktop/unx/source/args.h new file mode 100644 index 0000000000..f0fe7ce39b --- /dev/null +++ b/desktop/unx/source/args.h @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#include <sal/types.h> +#include <rtl/ustring.h> + +typedef struct { + rtl_uString *pAppPath; + const char *pPageinType; // @pagein-writer for - writer etc. else NULL + sal_Bool bInhibitSplash; // should we show a splash screen + sal_Bool bInhibitPagein; // should we run pagein ? + sal_Bool bInhibitJavaLdx; // should we run javaldx ? + sal_Bool bInhibitPipe; // for --help and --version + + sal_uInt32 nArgsEnv; // number of -env: style args + sal_uInt32 nArgsTotal; // number of -env: as well as -writer style args + rtl_uString *ppArgs[1]; // sorted argument array +} Args; + +Args *args_parse (void); +void args_free (Args *args); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/unx/source/file_image.h b/desktop/unx/source/file_image.h new file mode 100644 index 0000000000..6f0194d17e --- /dev/null +++ b/desktop/unx/source/file_image.h @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#ifndef INCLUDED_STDDEF_H +#include <stddef.h> +#define INCLUDED_STDDEF_H +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** file_image. + */ +struct file_image_st +{ + void * m_base; + size_t m_size; +}; + +typedef struct file_image_st file_image; + +#define FILE_IMAGE_INITIALIZER { NULL, 0 } + + +/** file_image_open. + */ +int file_image_open ( + file_image * image, + const char * filename); + + +/** file_image_pagein. + */ +int file_image_pagein ( + file_image * image); + + +/** file_image_close. + */ +int file_image_close ( + file_image * image); + + +/** Epilog. + */ +#ifdef __cplusplus +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/unx/source/file_image_unx.c b/desktop/unx/source/file_image_unx.c new file mode 100644 index 0000000000..4294a57611 --- /dev/null +++ b/desktop/unx/source/file_image_unx.c @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 . + */ + +#include "file_image.h" + +#include <unistd.h> + +#include <errno.h> +#include <fcntl.h> + +#include <sys/mman.h> +#include <sys/stat.h> + +#include <string.h> + +/* + * file_image_open + */ +int file_image_open (file_image * image, const char * filename) +{ + int result = 0; + int fd; + struct stat st; + void * p; + + if (image == NULL) + return EINVAL; + + image->m_base = MAP_FAILED; + image->m_size = 0; + + if ((fd = open (filename, O_RDONLY)) == -1) + return errno; + + if (fstat (fd, &st) == -1) + { + result = errno; + goto cleanup_and_leave; + } + + p = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (p == MAP_FAILED) + { + result = errno; + goto cleanup_and_leave; + } + + image->m_base = p; + image->m_size = st.st_size; + +cleanup_and_leave: + close (fd); + return result; +} + +/* + * file_image_pagein. + */ +int file_image_pagein (file_image * image) +{ + long s = -1; + volatile char c =0; + size_t idx; + + if (image == NULL) + return EINVAL; + if (image->m_base == NULL) + return EINVAL; + if (image->m_size == 0) + return 0; + + if (madvise (image->m_base, image->m_size, MADV_WILLNEED) == -1) + return errno; + + s = sysconf (_SC_PAGESIZE); + if (s == -1) + s = 0x1000; + // force touching of each page despite the optimizer + for(idx = 0; idx < image->m_size; idx += (size_t)s) + { + c ^= ((volatile const char*)(image->m_base))[idx]; + } + c ^= ((volatile const char*)(image->m_base))[image->m_size-1]; + (void)c; // silence Clang 13 trunk -Wunused-but-set-variable + + return 0; +} + +/* + * file_image_close + */ +int file_image_close (file_image * image) +{ + if (image == NULL) + return EINVAL; + + if (munmap (image->m_base, image->m_size) == -1) + return errno; + + image->m_base = NULL; + image->m_size = 0; + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/unx/source/pagein.c b/desktop/unx/source/pagein.c new file mode 100644 index 0000000000..87bbb699f8 --- /dev/null +++ b/desktop/unx/source/pagein.c @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 . + */ + +#include "file_image.h" +#include "pagein.h" + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#ifdef LINUX +#include <sys/sysmacros.h> +#endif + +/* do_pagein */ +static void do_pagein (const char * filename) +{ + int result; + file_image image = FILE_IMAGE_INITIALIZER; + + if (file_image_open (&image, filename) != 0) + return; + + if ((result = file_image_pagein (&image)) != 0) + { + fprintf (stderr, "file_image_pagein %s: %s\n", filename, strerror(result)); + } + + file_image_close (&image); +} + +static int isRotational(char const * path) +{ + int ret = 1; +#ifdef LINUX + FILE * fp = NULL; + char fullpath[4096]; + struct stat out; + int major, minor; + char type; + if (stat(path , &out) == -1) + return ret; + major = major(out.st_dev); + minor = 0; /* minor(out.st_dev); only the device itself has a queue */ + sprintf(fullpath,"/sys/dev/block/%d:%d/queue/rotational",major,minor); + if ((fp = fopen(fullpath, "r")) == NULL) + return ret; + if (fgets(&type, 1, fp)) + ret = type == '1'; + fclose(fp); +#endif + return ret; +} + +void pagein_execute(char const * path, char const * file) +{ + char fullpath[4096]; + char *p = NULL; + FILE * fp = NULL; + if(!isRotational(path)) + return; + memset(fullpath, 0, sizeof(fullpath)); + strncpy (fullpath, path, 3000); + if (!(p = strrchr (fullpath, '/'))) + p = fullpath; + else + p++; + strncpy(p, file, 1024); + p[strlen(p)] = '\0'; + if ((fp = fopen (fullpath, "r")) == NULL) + { + + fprintf (stderr, "fopen %s: %s\n", fullpath, strerror(errno)); + return; + } + while (fgets (p, 1024, fp) != NULL) + { + p[strlen(p) - 1] = '\0'; + + /* paths relative to the location of the pagein file */ + do_pagein (fullpath); + } + fclose (fp); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/unx/source/pagein.h b/desktop/unx/source/pagein.h new file mode 100644 index 0000000000..7fb8b6a918 --- /dev/null +++ b/desktop/unx/source/pagein.h @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <config_features.h> + +#if HAVE_FEATURE_PAGEIN +void pagein_execute(char const* path, char const* file); +#else +inline void pagein_execute(char const* path, char const* file) +{ + (void)path; + (void)file; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/unx/source/splashx.c b/desktop/unx/source/splashx.c new file mode 100644 index 0000000000..4dc50a7406 --- /dev/null +++ b/desktop/unx/source/splashx.c @@ -0,0 +1,811 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <config_features.h> +#include "splashx.h" + +#if defined(ENABLE_QUICKSTART_LIBPNG) && HAVE_FEATURE_UI + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> + +#include <X11/extensions/Xinerama.h> + +#include <osl/endian.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <png.h> + +#include <osl/process.h> +#include <osl/thread.h> +#include <rtl/bootstrap.h> +#include <rtl/ustrbuf.h> + +typedef struct { + unsigned char b, g, r; +} color_t; + +struct splash +{ + Display* display; + int screen; + int depth; + int display_width; + int display_height; + int display_x_pos; + int display_y_pos; + Visual* visual; + + int width; + int height; + + Colormap color_map; + Window win; + GC gc; + //true when intro-highres loaded successfully + sal_Bool bHasHiDpiImage; + +// Progress bar values +// taken from desktop/source/splash/splash.cxx + int tlx; + int tly; + int barwidth; + int barheight; + int barspace; + color_t barcol; + color_t framecol; + + XColor barcolor; + XColor framecolor; + + unsigned char** bitmap_rows; + png_structp png_ptr; + png_infop info_ptr; + +}; + +#define WINDOW_WIDTH 440 +#define WINDOW_HEIGHT 299 + +#define PROGRESS_XOFFSET 12 +#define PROGRESS_YOFFSET 18 +#define PROGRESS_BARSPACE 2 + +/* libpng-1.2.41 */ +#ifndef PNG_TRANSFORM_GRAY_TO_RGB +# define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 +#endif + +static int splash_load_bmp( struct splash* splash, const char *filename ) +{ + FILE *file; + + if ( !(file = fopen( filename, "r" ) ) ) + return 0; + + splash->png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL ); + splash->info_ptr = png_create_info_struct(splash->png_ptr); + png_init_io( splash->png_ptr, file ); + + if( setjmp( png_jmpbuf( splash->png_ptr ) ) ) + { + png_destroy_read_struct( &(splash->png_ptr), &(splash->info_ptr), NULL ); + fclose( file ); + return 0; + } + + png_read_png( splash->png_ptr, splash->info_ptr, + PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_ALPHA | + PNG_TRANSFORM_GRAY_TO_RGB | PNG_TRANSFORM_BGR, NULL); + + splash->bitmap_rows = png_get_rows( splash->png_ptr, splash->info_ptr ); + splash->width = png_get_image_width( splash->png_ptr, splash->info_ptr ); + splash->height = png_get_image_height( splash->png_ptr, splash->info_ptr ); + + fclose( file ); + return 1; +} + +static void setup_color( int const 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 +} + +/* Fill 'array' with values of the key 'name'. + Its value is a comma delimited list of integers */ +static void get_bootstrap_value( int *array, int size, rtlBootstrapHandle handle, const char *name ) +{ + rtl_uString *pKey = NULL, *pValue = NULL; + + /* get the value from the ini file */ + rtl_uString_newFromAscii( &pKey, name ); + rtl_bootstrap_get_from_handle( handle, pKey, &pValue, NULL ); + + /* the value is several numbers delimited by ',' - parse it */ + if ( rtl_uString_getLength( pValue ) > 0 ) + { + rtl_uString *pToken = NULL; + int i = 0; + sal_Int32 nIndex = 0; + for ( ; ( nIndex >= 0 ) && ( i < size ); ++i ) + { + nIndex = rtl_uString_getToken( &pToken, pValue, 0, ',', nIndex ); + array[i] = rtl_ustr_toInt32( rtl_uString_getStr( pToken ), 10 ); + } + + rtl_uString_release( pToken ); + } + + /* cleanup */ + rtl_uString_release( pKey ); + rtl_uString_release( pValue ); +} + +// setup +static void splash_setup( struct splash* splash, int const barc[3], int const framec[3], int posx, int posy, int w, int h ) +{ + if ( splash->width <= 500 ) + { + splash->barwidth = splash->width - ( 2 * PROGRESS_XOFFSET ); + splash->barheight = 6; + splash->tlx = PROGRESS_XOFFSET; + splash->tly = splash->height - PROGRESS_YOFFSET; + + splash->barcol.r = 0; + splash->barcol.g = 0; + splash->barcol.b = 128; + } + + if ( posx >= 0 ) + splash->tlx = posx; + if ( posy >= 0 ) + splash->tly = posy; + if ( w >= 0 ) + splash->barwidth = w; + if ( h >= 0 ) + splash->barheight = h; + + setup_color( barc, &(splash->barcol) ); + setup_color( framec, &(splash->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) +static int HIGHEST_BIT( unsigned long x ) +{ + int i = 0; + for ( ; x; ++i ) + x >>= 1; + + return i; +} + +// Number of bits set to 1 +static 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(struct splash* splash) +{ + Pixmap pixmap; + GC pixmap_gc; + XGCValues values; + + if ( !splash->bitmap_rows ) + { + return; + } + pixmap = XCreatePixmap( splash->display, splash->win, splash->width, splash->height, splash->depth ); + + pixmap_gc = XCreateGC( splash->display, pixmap, 0/*value_mask*/, &values ); + + if ( splash->visual->class == TrueColor ) + { + const unsigned long red_mask = splash->visual->red_mask; + const unsigned long green_mask = splash->visual->green_mask; + const unsigned long blue_mask = splash->visual->blue_mask; + + const unsigned long red_delta_mask = ( 1UL << ( 8 - BITS( red_mask ) ) ) - 1; + const unsigned long green_delta_mask = ( 1UL << ( 8 - BITS( green_mask ) ) ) - 1; + const unsigned long blue_delta_mask = ( 1UL << ( 8 - BITS( blue_mask ) ) ) - 1; + + const int red_shift = HIGHEST_BIT( red_mask ) - 8; + const int green_shift = HIGHEST_BIT( green_mask ) - 8; + const int blue_shift = HIGHEST_BIT( blue_mask ) - 8; + + XImage* image = XCreateImage( splash->display, splash->visual, splash->depth, ZPixmap, + 0, NULL, splash->width, splash->height, 32, 0 ); + + const int bytes_per_line = image->bytes_per_line; + const int bpp = image->bits_per_pixel; + const int byte_order = image->byte_order; +#if defined OSL_LITENDIAN + const int machine_byte_order = LSBFirst; +#else /* OSL_BIGENDIAN */ + const int machine_byte_order = MSBFirst; +#endif + + char *data = malloc( splash->height * bytes_per_line ); + char *out = data; + 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 < splash->height; ++y ) \ + { \ + unsigned long red_delta = 0, green_delta = 0, blue_delta = 0; \ + color_t *in = (color_t *)(splash->bitmap_rows[y]); \ + out = data + y * bytes_per_line; \ + for ( x = 0; x < splash->width; ++x, ++in ) \ + { \ + unsigned long red = in->r + red_delta; \ + unsigned long green = in->g + green_delta; \ + unsigned long blue = in->b + blue_delta; \ + unsigned long pixel = 0; \ + uint32_t tmp = 0; \ + (void) tmp; \ + 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; \ + pixel = \ + ( SHIFT( red, red_shift ) & red_mask ) | \ + ( SHIFT( green, green_shift ) & green_mask ) | \ + ( SHIFT( blue, blue_shift ) & blue_mask ); \ + code \ + } \ + } \ + } + + 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, 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) + { +#if defined OSL_LITENDIAN + COPY_IN_OUT( 3, memcpy(out, &pixel, sizeof (color_t)); out += 3; ) +#else /* OSL_BIGENDIAN */ + COPY_IN_OUT( 3, 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; ) +#endif + } + else + COPY_IN_OUT( 3, 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, 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( splash->display, pixmap_gc ); + XFreePixmap( splash->display, pixmap ); + XDestroyImage( image ); + return; + } + +#undef COPY_IN_OUT + + XPutImage( splash->display, pixmap, pixmap_gc, image, 0, 0, 0, 0, splash->width, splash->height ); + XDestroyImage( image ); + } + + XSetWindowBackgroundPixmap( splash->display, splash->win, pixmap ); + + XFreeGC( splash->display, pixmap_gc ); + XFreePixmap( splash->display, pixmap ); +} + +// The old method of hiding the window decorations +static void suppress_decorations_motif(struct splash* splash) +{ + struct + { + unsigned long flags, functions, decorations; + long input_mode; + } mwmhints; + + Atom a = XInternAtom( splash->display, "_MOTIF_WM_HINTS", False ); + + mwmhints.flags = 15; // functions, decorations, input_mode, status + mwmhints.functions = 2; // ? + mwmhints.decorations = 0; + mwmhints.input_mode = 0; + + XChangeProperty( splash->display, splash->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(struct splash* splash) +{ + Atom atom_type = XInternAtom( splash->display, "_NET_WM_WINDOW_TYPE", True ); + Atom atom_splash = XInternAtom( splash->display, "_NET_WM_WINDOW_TYPE_SPLASH", True ); + + if ( atom_type != None && atom_splash != None ) + XChangeProperty( splash->display, splash->win, atom_type, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&atom_splash, 1 ); + //else + suppress_decorations_motif(splash); // FIXME: Unconditional until Metacity/compiz's SPLASH handling is fixed +} + +/** + * Connects to the display and initializes splash with the screen details + * + * @return Success: 1; Failure: 0 + */ +static int splash_init_display( struct splash* splash, int argc, char** argv ) +{ + char *display_name = NULL; + int i; + int n_xinerama_screens = 1; + XineramaScreenInfo* p_screens = NULL; + + 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 + splash->display = XOpenDisplay( display_name ); + if ( !splash->display ) + { + fprintf( stderr, "Failed to open display\n" ); + return 0; + } + + // create the window + splash->screen = DefaultScreen( splash->display ); + splash->depth = DefaultDepth( splash->display, splash->screen ); + splash->color_map = DefaultColormap( splash->display, splash->screen ); + splash->visual = DefaultVisual( splash->display, splash->screen ); + + splash->display_width = DisplayWidth( splash->display, splash->screen ); + splash->display_height = DisplayHeight( splash->display, splash->screen ); + splash->display_x_pos = 0; + splash->display_y_pos = 0; + + p_screens = XineramaQueryScreens( splash->display, &n_xinerama_screens ); + if( p_screens ) + { + for( i=0; i < n_xinerama_screens; i++ ) + { + if ( p_screens[i].screen_number == splash->screen ) + { + splash->display_width = p_screens[i].width; + splash->display_height = p_screens[i].height; + splash->display_x_pos = p_screens[i].x_org; + splash->display_y_pos = p_screens[i].y_org; + break; + } + } + XFree( p_screens ); + } + return 1; +} + +/** + * Create the window for the splash screen + * + * @return Success: 1; Failure: 0 + */ +static int splash_create_window(struct splash* splash) +{ + Window root_win; + XGCValues values; + const char* name = "LibreOffice"; + const char* icon = "icon"; // FIXME + XSizeHints size_hints; + + root_win = RootWindow( splash->display, splash->screen ); + + splash->win = XCreateSimpleWindow( splash->display, root_win, + (splash->display_x_pos + (splash->display_width - splash->width)/2), + (splash->display_y_pos + (splash->display_height - splash->height)/2), + splash->width, splash->height, 0, + BlackPixel( splash->display, splash->screen ), BlackPixel( splash->display, splash->screen ) ); + + XSetWindowColormap( splash->display, splash->win, splash->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( splash->barcolor, splash->barcol ); + FILL_COLOR( splash->framecolor, splash->framecol ); +#undef FILL_COLOR + + XAllocColor( splash->display, splash->color_map, &(splash->barcolor) ); + XAllocColor( splash->display, splash->color_map, &(splash->framecolor) ); + + // not resizable, no decorations, etc. + splash->gc = XCreateGC( splash->display, splash->win, 0/*value_mask*/, &values ); + + size_hints.flags = PPosition | PSize | PMinSize | PMaxSize; + size_hints.x = splash->display_x_pos; + size_hints.y = splash->display_y_pos; + size_hints.width = splash->width; + size_hints.height = splash->height; + size_hints.min_width = splash->width; + size_hints.max_width = splash->width; + size_hints.min_height = splash->height; + size_hints.max_height = splash->height; + + XSetStandardProperties( splash->display, splash->win, name, icon, None, + NULL, 0, &size_hints ); + + // the actual work + suppress_decorations(splash); + create_pixmap(splash); + + // show it + XSelectInput( splash->display, splash->win, 0 ); + XMapWindow( splash->display, splash->win ); + + return 1; +} + +// Re-draw & process the events +// Just throwing them away - we do not need anything more... +static void process_events(struct splash* splash) +{ + XEvent xev; + int num_events; + + XFlush( splash->display ); + num_events = XPending( splash->display ); + while ( num_events > 0 ) + { + num_events--; + XNextEvent( splash->display, &xev ); + } +} + + +static rtl_String* ustr_to_str( rtl_uString* pStr ) +{ + rtl_String *pOut = NULL; + + rtl_uString2String( &pOut, rtl_uString_getStr( pStr ), + rtl_uString_getLength( pStr ), osl_getThreadTextEncoding(), OUSTRING_TO_OSTRING_CVTFLAGS ); + + return pOut; +} + +static sal_Bool isHiDPI(struct splash* splash) +{ + const char* pValStr; + double nDPI; + + /* + * GNOME currently enables HiDPI support when the screen resolution is at least 192 dpi + * and the screen height (in device pixels) is at least 1200. + */ + + if (splash->display_height < 1200) + return sal_False; + + pValStr = XGetDefault(splash->display, "Xft", "dpi"); + /* if it's too old to have this, assume it's not hidpi */ + if (!pValStr) + return sal_False; + + nDPI = strtod(pValStr, NULL); + if (nDPI < 192) + return sal_False; + + return sal_True; +} + +#define IMG_SUFFIX ".png" + +static void splash_load_image( struct splash* splash, rtl_uString* pUAppPath ) +{ + /* FIXME-BCP47: if we wanted to support language tags here that would get + * complicated, this is C-source not C++ so LanguageTag can't be used. For + * now the splash screen will have to get along with language-territory. */ + + char *pBuffer, *pSuffix, *pLocale; + int nLocSize; + rtl_Locale *pLoc = NULL; + rtl_String *pLang, *pCountry, *pAppPath; + + osl_getProcessLocale (&pLoc); + pLang = ustr_to_str (pLoc->Language); + pCountry = ustr_to_str (pLoc->Country); + + nLocSize = strlen (pLang->buffer) + strlen (pCountry->buffer) + 3; + pLocale = malloc (nLocSize); + pLocale[0] = '-'; + strcpy (pLocale + 1, pLang->buffer); + strcat (pLocale, "_"); + strcat (pLocale, pCountry->buffer); + + rtl_string_release( pCountry ); + rtl_string_release( pLang ); + + pAppPath = ustr_to_str (pUAppPath); + pBuffer = malloc (pAppPath->length + nLocSize + 256); + strcpy (pBuffer, pAppPath->buffer); + pSuffix = pBuffer + pAppPath->length; + rtl_string_release( pAppPath ); + + strcpy (pSuffix, "intro"); + strcat (pSuffix, pLocale); + strcat (pSuffix, IMG_SUFFIX); + if ( splash_load_bmp( splash, pBuffer ) ) + goto cleanup; /* success */ + + /* load high resolution splash image */ + splash->bHasHiDpiImage = sal_False; + if (isHiDPI(splash)) + { + strcpy (pSuffix, "intro-highres" IMG_SUFFIX); + if ( splash_load_bmp( splash, pBuffer ) ) + { + splash->bHasHiDpiImage = sal_True; + goto cleanup; /* success */ + } + } + /* load standard resolution splash image */ + strcpy (pSuffix, "intro" IMG_SUFFIX); + if ( splash_load_bmp( splash, pBuffer ) ) + goto cleanup; /* success */ + + fprintf (stderr, "Failed to find intro image\n"); + + cleanup: + free (pLocale); + free (pBuffer); +} + +/* Load the colors and size of the splash. */ +static void splash_load_defaults( struct splash* splash, rtl_uString* pAppPath, sal_Bool* bNoDefaults ) +{ + rtl_uString *pSettings = NULL, *pTmp = NULL; + rtlBootstrapHandle handle; + int logo[1] = { -1 }, + bar[3] = { -1, -1, -1 }, + frame[3] = { -1, -1, -1 }, + pos[2] = { -1, -1 }, + size[2] = { -1, -1 }; + + /* construct the sofficerc file location */ + rtl_uString_newFromAscii( &pSettings, "file://" ); + rtl_uString_newConcat( &pSettings, pSettings, pAppPath ); + rtl_uString_newConcat( &pSettings, pSettings, pTmp ); + rtl_uString_newFromAscii( &pTmp, SAL_CONFIGFILE( "soffice" ) ); + rtl_uString_newConcat( &pSettings, pSettings, pTmp ); + + /* use it as the bootstrap file */ + handle = rtl_bootstrap_args_open( pSettings ); + + /* get the values */ + get_bootstrap_value( logo, 1, handle, "Logo" ); + get_bootstrap_value( bar, 3, handle, "ProgressBarColor" ); + get_bootstrap_value( frame, 3, handle, "ProgressFrameColor" ); + if (isHiDPI(splash) && splash->bHasHiDpiImage) + { + get_bootstrap_value( pos, 2, handle, "ProgressPositionHigh" ); + get_bootstrap_value( size, 2, handle, "ProgressSizeHigh" ); + } + else + { + get_bootstrap_value( pos, 2, handle, "ProgressPosition" ); + get_bootstrap_value( size, 2, handle, "ProgressSize" ); + } + + if ( logo[0] == 0 ) + { + *bNoDefaults = sal_True; + } + + splash_setup( splash, bar, frame, pos[0], pos[1], size[0], size[1] ); + + /* cleanup */ + rtl_bootstrap_args_close( handle ); + rtl_uString_release( pSettings ); + rtl_uString_release( pTmp ); +} + + +// Draw the progress +void splash_draw_progress( struct splash* splash, int progress ) +{ + int length = 0; + + if (!splash) + { + return; + } + // sanity + if ( progress < 0 ) + { + progress = 0; + } + if ( progress > 100 ) + { + progress = 100; + } + // draw progress... + length = ( progress * splash->barwidth / 100 ) - ( 2 * splash->barspace ); + if ( length < 0 ) + { + length = 0; + } + // border + XSetForeground( splash->display, splash->gc, splash->framecolor.pixel ); + XDrawRectangle( splash->display, splash->win, splash->gc, splash->tlx, splash->tly, + splash->barwidth, splash->barheight ); + + // progress bar + XSetForeground( splash->display, splash->gc, splash->barcolor.pixel ); + XFillRectangle( splash->display, splash->win, splash->gc, + splash->tlx + splash->barspace, splash->tly + splash->barspace, + length + 1, splash->barheight - 2 * splash->barspace + 1 ); + + // pending events + process_events(splash); +} + +void splash_destroy(struct splash* splash) +{ + if(!splash) + return; + + if(splash->display) + { + if(splash->gc) + { + XFreeGC(splash->display, splash->gc); + splash->gc = NULL; + } + + XCloseDisplay( splash->display ); + splash->display = NULL; + png_destroy_read_struct( &(splash->png_ptr), &(splash->info_ptr), NULL ); + } + free(splash); +} + +struct splash* splash_create(rtl_uString* pAppPath, int argc, char** argv) +{ + struct splash* splash; + sal_Bool bNoDefaults = sal_False; + + splash = calloc(1, sizeof(struct splash)); + if (splash && !splash_init_display(splash, argc, argv)) + { + splash_destroy(splash); + splash = NULL; + } + + if (!splash) + return NULL; + + splash->width = WINDOW_WIDTH; + splash->height = WINDOW_HEIGHT; + + splash->tlx = 212; + splash->tly = 216; + splash->barwidth = 263; + splash->barheight = 8; + splash->barspace = PROGRESS_BARSPACE; + splash->barcol.b = 18; + splash->barcol.g = 202; + splash->barcol.r = 157; + splash->framecol.b = 0xD3; + splash->framecol.g = 0xD3; + splash->framecol.r = 0xD3; + + splash_load_image( splash, pAppPath ); + splash_load_defaults( splash, pAppPath, &bNoDefaults ); + + if (!bNoDefaults && splash_create_window(splash)) + { + splash_draw_progress( splash, 0 ); + return splash; + } + + splash_destroy(splash); + return NULL; +} + +#else /* not ENABLE_QUICKSTART_LIBPNG */ + +struct splash +{ +}; + +/* Stubs that will never be called in this case */ +void splash_draw_progress( struct splash* splash, int progress ) +{ + (void)splash; (void)progress; +} + +void splash_destroy(struct splash* splash) +{ + (void)splash; +} + +struct splash* splash_create(rtl_uString* pAppPath, int argc, char** argv) +{ + (void)pAppPath; (void)argc; (void)argv; + return NULL; +} + + +#endif // defined(ENABLE_QUICKSTART_LIBPNG) && HAVE_FEATURE_UI + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/unx/source/splashx.h b/desktop/unx/source/splashx.h new file mode 100644 index 0000000000..0ca1ba6559 --- /dev/null +++ b/desktop/unx/source/splashx.h @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include <rtl/ustring.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct splash; + +struct splash* splash_create(rtl_uString* pAppPath, int argc, char** argv); + +void splash_destroy(struct splash* splash); + +void splash_draw_progress(struct splash* splash, int progress); + +#ifdef __cplusplus +} // extern "C" +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/unx/source/start.c b/desktop/unx/source/start.c new file mode 100644 index 0000000000..0bb009d9c5 --- /dev/null +++ b/desktop/unx/source/start.c @@ -0,0 +1,887 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <config_java.h> + +#include <signal.h> +#include <unistd.h> +#include <limits.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <sys/un.h> +#include <poll.h> +#include <fcntl.h> +#include <stdio.h> +#include <libgen.h> +#include <string.h> +#include <errno.h> + +#include <desktop/exithelper.h> +#include <osl/process.h> +#include <osl/thread.h> +#include <rtl/bootstrap.h> +#include <rtl/digest.h> +#include <rtl/process.h> +#include <rtl/ustrbuf.h> +#include <sal/main.h> + +#include "args.h" +#include "pagein.h" +#include "splashx.h" + +#define PIPEDEFAULTPATH "/tmp" +#define PIPEALTERNATEPATH "/var/tmp" + +/* Easier conversions: rtl_uString to rtl_String */ +static rtl_String *ustr_to_str(rtl_uString *pStr) +{ + rtl_String *pOut = NULL; + + rtl_uString2String(&pOut, rtl_uString_getStr(pStr), + rtl_uString_getLength(pStr), osl_getThreadTextEncoding(), OUSTRING_TO_OSTRING_CVTFLAGS); + + return pOut; +} + +/* Easier conversions: char * to rtl_uString */ +static rtl_uString *charp_to_ustr(const char *pStr) +{ + rtl_uString *pOut = NULL; + + rtl_string2UString(&pOut, pStr, strlen(pStr), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS); + + return pOut; +} + +typedef struct { + int status_fd; + oslProcess child; +} ChildInfo; + +static int +child_info_get_status_fd(ChildInfo const *info) +{ + return info->status_fd; +} + +static void +child_info_destroy(ChildInfo *info) +{ + close (info->status_fd); + osl_freeProcessHandle (info->child); + free (info); +} + +static ChildInfo * child_spawn(Args *args, sal_Bool bAllArgs, sal_Bool bWithStatus) +{ + rtl_uString *pApp = NULL, *pTmp = NULL; + rtl_uString **ppArgs; + sal_uInt32 nArgs, i; + ChildInfo *info; + int status_pipe[2]; + oslProcessError nError; + + info = calloc (1, sizeof (ChildInfo)); + + /* create pipe */ + if (pipe(status_pipe) < 0) + { + fprintf(stderr, "ERROR: no file handles\n"); + exit(1); + } + info->status_fd = status_pipe[0]; + + /* application name */ + rtl_uString_newFromAscii(&pApp, "file://"); + rtl_uString_newConcat(&pApp, pApp, args->pAppPath); + rtl_uString_newFromAscii(&pTmp, "soffice.bin"); + rtl_uString_newConcat(&pApp, pApp, pTmp); + rtl_uString_release(pTmp); + pTmp = NULL; + + /* copy args */ + nArgs = bAllArgs ? args->nArgsTotal : args->nArgsEnv; + ppArgs = (rtl_uString **)calloc(nArgs + 1, sizeof(rtl_uString*)); + for (i = 0; i < nArgs; ++i) + ppArgs[i] = args->ppArgs[i]; + + if(bWithStatus) + { + char buffer[64]; + + /* add the pipe arg */ + snprintf(buffer, 63, "--splash-pipe=%d", status_pipe[1]); + rtl_uString_newFromAscii( &pTmp, buffer ); + ppArgs[nArgs] = pTmp; + ++nArgs; + } + + /* start the main process */ + nError = osl_executeProcess(pApp, ppArgs, nArgs, + osl_Process_NORMAL, + NULL, + NULL, + NULL, 0, + &info->child ); + + if (pTmp) + rtl_uString_release(pTmp); + free (ppArgs); + + if (nError != osl_Process_E_None) + { + fprintf(stderr, "ERROR %d forking process\n", nError); + rtl_uString_release(pApp); + _exit (1); + } + + rtl_uString_release(pApp); + close( status_pipe[1] ); + + return info; +} + +static sal_Bool child_exited_wait(ChildInfo *info, sal_Bool bShortWait) +{ + TimeValue t = { 0, 250 /* ms */ * 1000 * 1000 }; + if (!bShortWait) + t.Seconds = 1024; + + return osl_joinProcessWithTimeout(info->child, &t) != osl_Process_E_TimedOut; +} + +static int child_get_exit_code(ChildInfo *info) +{ + oslProcessInfo inf; + + inf.Code = -1; + inf.Size = sizeof(inf); + + if (osl_getProcessInfo(info->child, osl_Process_EXITCODE, &inf) != osl_Process_E_None) + { + fprintf(stderr, "Warning: failed to fetch libreoffice exit status\n"); + return -1; + } + + return inf.Code; +} + +typedef enum { ProgressContinue, ProgressRestart, ProgressExit } ProgressStatus; + +/* Path of the application, with trailing slash. */ +static rtl_uString *get_app_path(const char *pAppExec) +{ + char pRealPath[PATH_MAX]; + rtl_uString *pResult; + sal_Int32 len; + char* dummy; + + char *pOrigPath = strdup(pAppExec); + char *pPath = dirname(pOrigPath); + + dummy = realpath(pPath, pRealPath); + (void)dummy; + pResult = charp_to_ustr(pRealPath); + free(pOrigPath); + + len = rtl_uString_getLength(pResult); + if (len > 0 && rtl_uString_getStr(pResult)[len - 1] != '/') + { + rtl_uString *pSlash = NULL; + rtl_uString_newFromAscii(&pSlash, "/"); + rtl_uString_newConcat(&pResult, pResult, pSlash); + rtl_uString_release(pSlash); + } + + return pResult; +} + +/* Compute the OOo md5 hash from 'pText' */ +static rtl_uString *get_md5hash(rtl_uString *pText) +{ + rtl_uString *pResult = NULL; + sal_Int32 nCapacity = 100; + unsigned char *pData = NULL; + sal_uInt32 nSize = 0; + rtlDigest digest; + sal_uInt32 md5_key_len = 0; + sal_uInt8* md5_buf = NULL; + sal_uInt32 i = 0; + + if ( !pText ) + return NULL; + + pData = (unsigned char *)rtl_uString_getStr(pText); + nSize = rtl_uString_getLength(pText) * sizeof(sal_Unicode); + if (!pData) + return NULL; + + digest = rtl_digest_create(rtl_Digest_AlgorithmMD5); + if (!digest) + return NULL; + + md5_key_len = rtl_digest_queryLength(digest); + md5_buf = (sal_uInt8 *)calloc(md5_key_len, sizeof(sal_uInt8)); + + rtl_digest_init(digest, pData , nSize); + rtl_digest_update(digest, pData, nSize); + rtl_digest_get(digest, md5_buf, md5_key_len); + rtl_digest_destroy(digest); + + /* create hex-value string from the MD5 value to keep + the string size minimal */ + rtl_uString_new_WithLength(&pResult, nCapacity); + for (; i < md5_key_len; ++i) + { + char val[3]; + snprintf(val, 3, "%x", md5_buf[i]); /* sic! we ignore some of the 0's */ + + rtl_uStringbuffer_insert_ascii(&pResult, &nCapacity, rtl_uString_getLength(pResult), + val, strlen(val)); + } + + /* cleanup */ + free(md5_buf); + + return pResult; +} + +/* Construct the pipe name */ +static rtl_uString *get_pipe_path(rtl_uString *pAppPath) +{ + rtl_uString *pPath = NULL, *pTmp = NULL, *pUserInstallation = NULL; + rtl_uString *pResult = NULL, *pBasePath = NULL, *pAbsUserInstallation = NULL; + rtlBootstrapHandle handle; + rtl_uString *pMd5hash = NULL; + sal_Unicode pUnicode[RTL_USTR_MAX_VALUEOFINT32]; + + /* setup bootstrap filename */ + rtl_uString_newFromAscii(&pPath, "file://"); + rtl_uString_newConcat(&pPath, pPath, pAppPath); + rtl_uString_newConcat(&pPath, pPath, pTmp); + rtl_uString_newFromAscii(&pTmp, SAL_CONFIGFILE("bootstrap")); + rtl_uString_newConcat(&pPath, pPath, pTmp); + + /* read userinstallation value */ + handle = rtl_bootstrap_args_open(pPath); + + rtl_uString_newFromAscii(&pTmp, "UserInstallation"); + rtl_bootstrap_get_from_handle(handle, pTmp, &pUserInstallation, NULL); + + rtl_bootstrap_args_close(handle); + + /* turn it into an absolute path - unwinding symlinks etc. */ + if (osl_getProcessWorkingDir(&pBasePath) || + osl_getAbsoluteFileURL(pBasePath, pUserInstallation, &pAbsUserInstallation)) + rtl_uString_newFromString(&pAbsUserInstallation, pUserInstallation); + + /* create the pipe name */ + pMd5hash = get_md5hash(pAbsUserInstallation); + if (!pMd5hash) + rtl_uString_new(&pMd5hash); + + if (access(PIPEDEFAULTPATH, W_OK) == 0) + { + rtl_uString_newFromAscii(&pResult, PIPEDEFAULTPATH); + } + else if (access(PIPEALTERNATEPATH, W_OK) == 0) + { + rtl_uString_newFromAscii(&pResult, PIPEALTERNATEPATH); + } + else + { + fprintf(stderr, "ERROR: no valid pipe path found.\n"); + exit(1); + } + + rtl_uString_newFromAscii(&pTmp, "/OSL_PIPE_"); + rtl_uString_newConcat(&pResult, pResult, pTmp); + + rtl_ustr_valueOfInt32(pUnicode, (int)getuid(), 10); + rtl_uString_newFromStr(&pTmp, pUnicode); + rtl_uString_newConcat(&pResult, pResult, pTmp); + + rtl_uString_newFromAscii(&pTmp, "_SingleOfficeIPC_"); + rtl_uString_newConcat(&pResult, pResult, pTmp); + + rtl_uString_newConcat(&pResult, pResult, pMd5hash); + + /* cleanup */ + rtl_uString_release(pMd5hash); + rtl_uString_release(pPath); + rtl_uString_release(pTmp); + + if (pBasePath) + rtl_uString_release(pBasePath); + + rtl_uString_release(pUserInstallation); + rtl_uString_release(pAbsUserInstallation); + + return pResult; +} + +/* Get fd of the pipe of the already running OOo. */ +static int connect_pipe(rtl_uString *pPipePath) +{ + int fd; + size_t len; + struct sockaddr_un addr; + + rtl_String *pPipeStr = ustr_to_str(pPipePath); + + memset(&addr, 0, sizeof(addr)); + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + return fd; + + (void)fcntl(fd, F_SETFD, FD_CLOEXEC); + + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, rtl_string_getStr(pPipeStr), sizeof(addr.sun_path) - 1); + rtl_string_release(pPipeStr); + +/* cut / paste from osl's pipe.c */ +#if defined(FREEBSD) + len = SUN_LEN(&addr); +#else + len = sizeof(addr); +#endif + + if (connect(fd, (struct sockaddr *)&addr, len) < 0) + { + close(fd); + fd = -1; + } + return fd; +} + +/* Escape: "," -> "\\,", "\0" -> "\\0", "\\" -> "\\\\" */ +static rtl_uString *escape_path(rtl_uString const *pToEscape) +{ + rtl_uString *pBuffer = NULL; + sal_Int32 nCapacity = 1000; + sal_Int32 i = 0; + sal_Int32 nEscapeLength = rtl_uString_getLength(pToEscape); + + rtl_uString_new_WithLength(&pBuffer, nCapacity); + + for (; i < nEscapeLength; ++i) + { + sal_Unicode c = pToEscape->buffer[i]; + switch (c) + { + case '\0': + rtl_uStringbuffer_insert_ascii(&pBuffer, &nCapacity, + rtl_uString_getLength(pBuffer), + RTL_CONSTASCII_STRINGPARAM("\\0")); + break; + case ',': + rtl_uStringbuffer_insert_ascii(&pBuffer, &nCapacity, + rtl_uString_getLength(pBuffer), + RTL_CONSTASCII_STRINGPARAM("\\,")); + break; + case '\\': + rtl_uStringbuffer_insert_ascii(&pBuffer, &nCapacity, + rtl_uString_getLength(pBuffer), + RTL_CONSTASCII_STRINGPARAM("\\\\")); + break; + default: + rtl_uStringbuffer_insert(&pBuffer, &nCapacity, + rtl_uString_getLength(pBuffer), + &c, 1); + } + } + + return pBuffer; +} + +/* Send args to the LO instance (using the 'fd' file descriptor) */ +static sal_Bool send_args(int fd, rtl_uString const *pCwdPath) +{ + rtl_uString *pBuffer = NULL, *pTmp = NULL; + sal_Int32 nCapacity = 1000; + rtl_String *pOut = NULL; + sal_Bool bResult; + size_t nLen; + rtl_uString *pEscapedCwdPath = escape_path(pCwdPath); + sal_uInt32 nArg = 0; + sal_uInt32 nArgCount = rtl_getAppCommandArgCount(); + + rtl_uString_new_WithLength(&pBuffer, nCapacity); + rtl_uString_new(&pTmp); + + rtl_uStringbuffer_insert_ascii(&pBuffer, &nCapacity, + rtl_uString_getLength(pBuffer), + RTL_CONSTASCII_STRINGPARAM("InternalIPC::Arguments")); + + if (rtl_uString_getLength(pEscapedCwdPath)) + { + rtl_uStringbuffer_insert_ascii(&pBuffer, &nCapacity, + rtl_uString_getLength(pBuffer), + RTL_CONSTASCII_STRINGPARAM("1")); + + rtl_uStringbuffer_insert(&pBuffer, &nCapacity, + rtl_uString_getLength(pBuffer), + rtl_uString_getStr(pEscapedCwdPath), + rtl_uString_getLength(pEscapedCwdPath)); + } + else + { + rtl_uStringbuffer_insert_ascii(&pBuffer, &nCapacity, + rtl_uString_getLength(pBuffer), + RTL_CONSTASCII_STRINGPARAM("0")); + } + + for (nArg = 0; nArg < nArgCount; ++nArg) + { + rtl_uString *pEscapedTmp = NULL; + rtl_uStringbuffer_insert_ascii(&pBuffer, &nCapacity, + rtl_uString_getLength(pBuffer), + ",", 1); + + rtl_getAppCommandArg(nArg, &pTmp); + + pEscapedTmp = escape_path(pTmp); + + rtl_uStringbuffer_insert(&pBuffer, &nCapacity, + rtl_uString_getLength(pBuffer), + rtl_uString_getStr(pEscapedTmp), + rtl_uString_getLength(pEscapedTmp)); + + rtl_uString_release(pEscapedTmp); + } + + if (!rtl_convertUStringToString( + &pOut, rtl_uString_getStr(pBuffer), + rtl_uString_getLength(pBuffer), RTL_TEXTENCODING_UTF8, + (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR + | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))) + { + fprintf(stderr, "ERROR: cannot convert arguments to UTF-8\n"); + exit(1); + } + + nLen = rtl_string_getLength(pOut) + 1; + ssize_t n = write(fd, rtl_string_getStr(pOut), nLen); + bResult = (n >= 0 && (size_t) n == nLen); + + if ( bResult ) + { + char resp[SAL_N_ELEMENTS("InternalIPC::ProcessingDone")]; + n = read(fd, resp, SAL_N_ELEMENTS(resp)); + bResult = n == SAL_N_ELEMENTS(resp) + && (memcmp( + resp, "InternalIPC::ProcessingDone", + SAL_N_ELEMENTS(resp)) + == 0); + } + + /* cleanup */ + rtl_uString_release(pEscapedCwdPath); + rtl_uString_release(pBuffer); + rtl_uString_release(pTmp); + rtl_string_release(pOut); + + return bResult; +} + + +#define BUFFER_LEN 255 + +/* Read the percent to show in splash. */ +static ProgressStatus read_percent(ChildInfo const *info, int *pPercent) +{ + static char pBuffer[BUFFER_LEN + 1]; + static char *pNext = pBuffer; + static ssize_t nRead = 0; + + char *pBegin; + char *pIter; + char c; + + /* from the last call */ + int nNotProcessed = nRead - (pNext - pBuffer); + if (nNotProcessed >= BUFFER_LEN) + return ProgressContinue; + + memmove(pBuffer, pNext, nNotProcessed); + + /* read data */ + nRead = read(child_info_get_status_fd(info), + pBuffer + nNotProcessed, BUFFER_LEN - nNotProcessed); + + if (nRead < 0) + { + if (errno == EINTR) + return ProgressContinue; + + return ProgressExit; + } + + nRead += nNotProcessed; + pBuffer[nRead] = '\0'; + + /* skip old data */ + pBegin = pBuffer; + pNext = pBuffer; + for (pIter = pBuffer; *pIter; ++pIter) + { + if (*pIter == '\n') + { + pBegin = pNext; + pNext = pIter + 1; + } + } + + if (!strncasecmp(pBegin, "end", 3)) + return ProgressExit; + else if (!strncasecmp(pBegin, "restart", 7)) + return ProgressRestart; + else if (sscanf(pBegin, "%d%c", pPercent, &c) == 2 && c == '%') + return ProgressContinue; + + /* unexpected - let's exit the splash to be safe */ + return ProgressExit; +} + +/* Simple system check. */ +static void system_checks(void) +{ +#ifdef LINUX + struct stat buf; + + /* check proc is mounted - lots of things fail otherwise */ + if (stat("/proc/version", &buf) != 0) + { + fprintf(stderr, "ERROR: /proc not mounted - LibreOffice is unlikely to work well if at all\n"); + exit(1); + } +#endif +} + +static void exec_pagein (Args *args) +{ + rtl_String * path = ustr_to_str(args->pAppPath); + pagein_execute(rtl_string_getStr(path), "pagein-common"); + + if (args->pPageinType) + pagein_execute(rtl_string_getStr(path), args->pPageinType); + + rtl_string_release(path); +} + +#if HAVE_FEATURE_JAVA + +static void extend_library_path(const char *new_element) +{ + rtl_uString *pEnvName=NULL, *pOrigEnvVar=NULL, *pNewEnvVar=NULL; + + rtl_uString_newFromAscii(&pEnvName, "LD_LIBRARY_PATH"); + rtl_uString_newFromAscii(&pNewEnvVar, new_element); + + osl_getEnvironment(pEnvName, &pOrigEnvVar); + if (pOrigEnvVar && pOrigEnvVar->length) + { + rtl_uString *pDelim = NULL; + rtl_uString_newFromAscii(&pDelim, ":"); + rtl_uString_newConcat(&pNewEnvVar, pNewEnvVar, pDelim); + rtl_uString_newConcat(&pNewEnvVar, pNewEnvVar, pOrigEnvVar); + rtl_uString_release(pDelim); + } + + osl_setEnvironment(pEnvName, pNewEnvVar); + + if (pOrigEnvVar) + rtl_uString_release(pOrigEnvVar); + + rtl_uString_release(pNewEnvVar); + rtl_uString_release(pEnvName); +} + +static void exec_javaldx(Args *args) +{ + char newpath[4096]; + sal_uInt32 nArgs; + rtl_uString *pApp; + rtl_uString **ppArgs; + rtl_uString *pTmp, *pTmp2; + + oslProcess javaldx = NULL; + oslFileHandle fileOut = NULL; + oslProcessError err; + + ppArgs = (rtl_uString **)calloc(args->nArgsEnv + 2, sizeof(rtl_uString*)); + + for (nArgs = 0; nArgs < args->nArgsEnv; ++nArgs) + ppArgs[nArgs] = args->ppArgs[nArgs]; + + /* Use absolute path to redirectrc */ + pTmp = NULL; + rtl_uString_newFromAscii(&pTmp, "-env:INIFILENAME=vnd.sun.star.pathname:"); + rtl_uString_newConcat(&pTmp, pTmp, args->pAppPath); + pTmp2 = NULL; + rtl_uString_newFromAscii(&pTmp2, "redirectrc"); + rtl_uString_newConcat(&pTmp, pTmp, pTmp2); + ppArgs[nArgs] = pTmp; + rtl_uString_release (pTmp2); + nArgs++; + + /* And also to javaldx */ + pApp = NULL; + rtl_uString_newFromAscii(&pApp, "file://"); + rtl_uString_newConcat(&pApp, pApp, args->pAppPath); + pTmp = NULL; + rtl_uString_newFromAscii(&pTmp, "javaldx"); + rtl_uString_newConcat(&pApp, pApp, pTmp); + rtl_uString_release(pTmp); + + err = osl_executeProcess_WithRedirectedIO(pApp, ppArgs, nArgs, + osl_Process_NORMAL, + NULL, // security + NULL, // work dir + NULL, 0, + &javaldx, // process handle + NULL, + &fileOut, + NULL); + + rtl_uString_release(ppArgs[nArgs-1]); + rtl_uString_release(pApp); + free(ppArgs); + + if(err != osl_Process_E_None) + { + fprintf (stderr, "Warning: failed to launch javaldx - java may not function correctly\n"); + + if (javaldx) + osl_freeProcessHandle(javaldx); + if (fileOut) + osl_closeFile(fileOut); + return; + } + else + { + char *chomp; + sal_uInt64 bytes_read; + + /* Magically osl_readLine doesn't work with pipes with E_SPIPE - so be this lame instead: */ + while (osl_readFile (fileOut, newpath, SAL_N_ELEMENTS (newpath), &bytes_read) == osl_File_E_INTR); + + if (bytes_read <= 0) + { + fprintf (stderr, "Warning: failed to read path from javaldx\n"); + + if (javaldx) + osl_freeProcessHandle(javaldx); + + if (fileOut) + osl_closeFile(fileOut); + + return; + } + + newpath[bytes_read] = '\0'; + + if ((chomp = strstr (newpath, "\n"))) + *chomp = '\0'; + } + + if (newpath[0] != '\0') { + extend_library_path(newpath); + } + + if (javaldx) + osl_freeProcessHandle(javaldx); + + if (fileOut) + osl_closeFile(fileOut); +} + +#endif + +// has to be a global :( +static oslProcess * volatile g_pProcess = NULL; + +static void sigterm_handler(int ignored) +{ + (void) ignored; + + if (g_pProcess) { + int SigTermSucceded = 0; + oslProcessInfo info; + info.Size = sizeof(oslProcessInfo); + + // forward SIGTERM to soffice.bin and give it a chance to semi-gracefully exit + // enough to remove named pipe + if (osl_getProcessInfo(g_pProcess, osl_Process_IDENTIFIER, &info) == osl_Process_E_None) { + TimeValue delay = { 1, 0 }; // 1 sec + SigTermSucceded = kill(info.Ident, SIGTERM) == 0 && + osl_joinProcessWithTimeout(g_pProcess, &delay) == osl_Process_E_None; + } + + // didn't work, SIGKILL instead + if (!SigTermSucceded) { + osl_terminateProcess(g_pProcess); // uses SIGKILL to terminate soffice.bin + osl_joinProcess(g_pProcess); + } + } + + _exit(255); +} + + +SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv) +{ + sal_Bool bSentArgs = sal_False; + const char* pUsePlugin; + rtl_uString *pPipePath = NULL; + Args *args; + int status = 0; + struct splash* splash = NULL; + struct sigaction sigpipe_action; + struct sigaction sigterm_action; + + /* turn SIGPIPE into an error */ + memset(&sigpipe_action, 0, sizeof(struct sigaction)); + sigpipe_action.sa_handler = SIG_IGN; + sigemptyset(&sigpipe_action.sa_mask); + sigaction(SIGPIPE, &sigpipe_action, NULL); + memset(&sigterm_action, 0, sizeof(struct sigaction)); + sigterm_action.sa_handler = &sigterm_handler; + sigemptyset(&sigterm_action.sa_mask); + sigaction(SIGTERM, &sigterm_action, NULL); + + args = args_parse(); + args->pAppPath = get_app_path(argv[0]); + if (!args->pAppPath) + { + fprintf(stderr, "ERROR: Can't read app link\n"); + exit(1); + } + +#ifndef ENABLE_QUICKSTART_LIBPNG + /* we can't load and render it anyway */ + args->bInhibitSplash = sal_True; +#endif + + pUsePlugin = getenv("SAL_USE_VCLPLUGIN"); + if (pUsePlugin && !strcmp(pUsePlugin, "svp")) + args->bInhibitSplash = sal_True; + + if (!args->bInhibitPipe && !getenv("LIBO_FLATPAK")) + { + int fd = 0; + pPipePath = get_pipe_path(args->pAppPath); + + if ((fd=connect_pipe(pPipePath)) >= 0) + { + // Wait for answer + char resp[strlen("InternalIPC::SendArguments") + 1]; + ssize_t n = read(fd, resp, SAL_N_ELEMENTS(resp)); + if (n == (ssize_t) SAL_N_ELEMENTS(resp) && + (memcmp(resp, "InternalIPC::SendArguments", + SAL_N_ELEMENTS(resp) - 1) == 0)) + { + rtl_uString *pCwdPath = NULL; + osl_getProcessWorkingDir(&pCwdPath); + + // Then send args + bSentArgs = send_args(fd, pCwdPath); + } + + close(fd); + } + } + + if (!bSentArgs) + { + /* we have to prepare for, and exec the binary */ + int nPercent = 0; + ChildInfo *info; + sal_Bool bAllArgs = sal_True; + sal_Bool bShortWait, bRestart; + + /* sanity check pieces */ + system_checks(); + + /* load splash image and create window */ + if (!args->bInhibitSplash) + splash = splash_create(args->pAppPath, argc, argv); + + /* pagein */ + if (!args->bInhibitPagein) + exec_pagein(args); + + /* javaldx */ +#if HAVE_FEATURE_JAVA + if (!args->bInhibitJavaLdx) + exec_javaldx (args); +#endif + + do + { + bRestart = sal_False; + + /* fast updates if we have somewhere to update it to */ + bShortWait = splash ? sal_True : sal_False; + + /* Periodically update the splash & the percent according + to what status_fd says, poll quickly only while starting */ + info = child_spawn (args, bAllArgs, bShortWait); + g_pProcess = info->child; + + while (!child_exited_wait(info, bShortWait)) + { + ProgressStatus eResult; + + splash_draw_progress(splash, nPercent); + eResult = read_percent(info, &nPercent); + + if (eResult != ProgressContinue) + { + splash_destroy(splash); + splash = NULL; + bShortWait = sal_False; + } + } + + status = child_get_exit_code(info); + g_pProcess = NULL; // reset + + switch (status) + { + case EXITHELPER_CRASH_WITH_RESTART: // re-start with just -env: parameters + bRestart = sal_True; + bAllArgs = sal_False; + break; + case EXITHELPER_NORMAL_RESTART: // re-start with all arguments + bRestart = sal_True; + bAllArgs = sal_True; + break; + default: + break; + } + + child_info_destroy(info); + } while (bRestart); + } + + /* cleanup */ + if (pPipePath) + rtl_uString_release(pPipePath); + + args_free(args); + + return status; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |