1 /*
   2  * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "splashscreen_impl.h"
  27 #include <X11/Xlib.h>
  28 #include <X11/Xutil.h>
  29 #include <X11/extensions/shape.h>
  30 #include <X11/Xmd.h>
  31 #include <X11/Xatom.h>
  32 #include <X11/cursorfont.h>
  33 #include <sys/types.h>
  34 #include <pthread.h>
  35 #include <signal.h>
  36 #include <unistd.h>
  37 #include <sys/time.h>
  38 #include <errno.h>
  39 #include <iconv.h>
  40 #include <langinfo.h>
  41 #include <locale.h>
  42 #include <fcntl.h>
  43 #include <poll.h>
  44 #include <sizecalc.h>
  45 
  46 static Bool shapeSupported;
  47 static int shapeEventBase, shapeErrorBase;
  48 
  49 void SplashRemoveDecoration(Splash * splash);
  50 
  51 
  52 /* Could use npt but decided to cut down on linked code size */
  53 char* SplashConvertStringAlloc(const char* in, int* size) {
  54     const char     *codeset;
  55     const char     *codeset_out;
  56     iconv_t         cd;
  57     size_t          rc;
  58     char           *buf = NULL, *out;
  59     size_t          bufSize, inSize, outSize;
  60     const char* old_locale;
  61 
  62     if (!in) {
  63         return NULL;
  64     }
  65     old_locale = setlocale(LC_ALL, "");
  66 
  67     codeset = nl_langinfo(CODESET);
  68     if ( codeset == NULL || codeset[0] == 0 ) {
  69         goto done;
  70     }
  71     /* we don't need BOM in output so we choose native BE or LE encoding here */
  72     codeset_out = (platformByteOrder()==BYTE_ORDER_MSBFIRST) ?
  73         "UCS-2BE" : "UCS-2LE";
  74 
  75     cd = iconv_open(codeset_out, codeset);
  76     if (cd == (iconv_t)-1 ) {
  77         goto done;
  78     }
  79     inSize = strlen(in);
  80     buf = SAFE_SIZE_ARRAY_ALLOC(malloc, inSize, 2);
  81     if (!buf) {
  82         return NULL;
  83     }
  84     bufSize = inSize*2; // need 2 bytes per char for UCS-2, this is
  85                         // 2 bytes per source byte max
  86     out = buf; outSize = bufSize;
  87     /* linux iconv wants char** source and solaris wants const char**...
  88        cast to void* */
  89     rc = iconv(cd, (void*)&in, &inSize, &out, &outSize);
  90     iconv_close(cd);
  91 
  92     if (rc == (size_t)-1) {
  93         free(buf);
  94         buf = NULL;
  95     } else {
  96         if (size) {
  97             *size = (bufSize-outSize)/2; /* bytes to wchars */
  98         }
  99     }
 100 done:
 101     setlocale(LC_ALL, old_locale);
 102     return buf;
 103 }
 104 
 105 void
 106 SplashInitFrameShape(Splash * splash, int imageIndex) {
 107     ImageRect maskRect;
 108     XRectangle *rects;
 109     SplashImage *frame = splash->frames + imageIndex;
 110 
 111     frame->rects = NULL;
 112     frame->numRects = 0;
 113 
 114     if (!splash->maskRequired)
 115         return;
 116     if (!shapeSupported)
 117         return;
 118     initRect(&maskRect, 0, 0, splash->width, splash->height, 1,
 119             splash->width * splash->imageFormat.depthBytes,
 120             splash->frames[imageIndex].bitmapBits, &splash->imageFormat);
 121     if (!IS_SAFE_SIZE_MUL(splash->width / 2 + 1, splash->height)) {
 122         return;
 123     }
 124     rects = SAFE_SIZE_ARRAY_ALLOC(malloc,
 125             sizeof(XRectangle), (splash->width / 2 + 1) * splash->height);
 126     if (!rects) {
 127         return;
 128     }
 129 
 130     frame->numRects = BitmapToYXBandedRectangles(&maskRect, rects);
 131     frame->rects = SAFE_SIZE_ARRAY_ALLOC(malloc, frame->numRects, sizeof(XRectangle));
 132     if (frame->rects) { // handle the error after the if(){}
 133         memcpy(frame->rects, rects, frame->numRects * sizeof(XRectangle));
 134     }
 135     free(rects);
 136 }
 137 
 138 unsigned
 139 SplashTime(void) {
 140     struct timeval tv;
 141     struct timezone tz;
 142     unsigned long long msec;
 143 
 144     gettimeofday(&tv, &tz);
 145     msec = (unsigned long long) tv.tv_sec * 1000 +
 146         (unsigned long long) tv.tv_usec / 1000;
 147 
 148     return (unsigned) msec;
 149 }
 150 
 151 void
 152 msec2timeval(unsigned time, struct timeval *tv) {
 153     tv->tv_sec = time / 1000;
 154     tv->tv_usec = (time % 1000) * 1000;
 155 }
 156 
 157 int
 158 GetNumAvailableColors(Display * display, Screen * screen, unsigned map_entries) {
 159     unsigned long pmr[1];
 160     unsigned long pr[SPLASH_COLOR_MAP_SIZE];
 161     unsigned nFailed, nAllocated, done = 0, nPlanes = 0;
 162     Colormap cmap;
 163     unsigned numColors = SPLASH_COLOR_MAP_SIZE; // never try allocating more than that
 164 
 165     if (numColors > map_entries) {
 166         numColors = map_entries;
 167     }
 168     cmap = XDefaultColormapOfScreen(screen);
 169     nAllocated = 0;             /* lower bound */
 170     nFailed = numColors + 1;    /* upper bound */
 171 
 172     /* Binary search to determine the number of available cells */
 173     for (done = 0; !done;) {
 174         if (XAllocColorCells(display, cmap, 0, pmr, nPlanes, pr, numColors)) {
 175             nAllocated = numColors;
 176             XFreeColors(display, cmap, pr, numColors, 0);
 177             if (nAllocated < (nFailed - 1)) {
 178                 numColors = (nAllocated + nFailed) / 2;
 179             } else
 180                 done = 1;
 181         } else {
 182             nFailed = numColors;
 183             if (nFailed > (nAllocated + 1))
 184                 numColors = (nAllocated + nFailed) / 2;
 185             else
 186                 done = 1;
 187         }
 188     }
 189     return nAllocated;
 190 }
 191 
 192 Colormap
 193 AllocColors(Display * display, Screen * screen, int numColors,
 194         unsigned long *pr) {
 195     unsigned long pmr[1];
 196     Colormap cmap = XDefaultColormapOfScreen(screen);
 197 
 198     XAllocColorCells(display, cmap, 0, pmr, 0, pr, numColors);
 199     return cmap;
 200 }
 201 
 202 void
 203 FreeColors(Display * display, Screen * screen, int numColors,
 204         unsigned long *pr) {
 205     Colormap cmap = XDefaultColormapOfScreen(screen);
 206 
 207     XFreeColors(display, cmap, pr, numColors, 0);
 208 }
 209 
 210 static void SplashCenter(Splash * splash) {
 211     Atom type, atom, actual_type;
 212     int status, actual_format;
 213     unsigned long nitems, bytes_after;
 214     CARD16 *prop = NULL;
 215 
 216     /*  try centering using Xinerama hint
 217         if there's no hint, use the center of the screen */
 218     atom = XInternAtom(splash->display, "XINERAMA_CENTER_HINT", True);
 219     if (atom != None) {
 220         status = XGetWindowProperty(splash->display,
 221             XRootWindowOfScreen(splash->screen), atom, 0, 1, False, XA_INTEGER,
 222             &actual_type, &actual_format, &nitems,
 223             &bytes_after, (unsigned char**)(&prop));
 224         if (status == Success && actual_type != None && prop != NULL) {
 225             splash->x = prop[0] - splash->width/2;
 226             splash->y = prop[1] - splash->height/2;
 227             XFree(prop);
 228             return;
 229         }
 230         if (prop != NULL) {
 231             XFree(prop);
 232         }
 233     }
 234     splash->x = (XWidthOfScreen(splash->screen) - splash->width) / 2;
 235     splash->y = (XHeightOfScreen(splash->screen) - splash->height) / 2;
 236 }
 237 
 238 static void SplashUpdateSizeHints(Splash * splash) {
 239     if (splash->window) {
 240         XSizeHints sizeHints;
 241 
 242         sizeHints.flags = USPosition | PPosition | USSize | PSize | PMinSize | PMaxSize | PWinGravity;
 243         sizeHints.width = sizeHints.base_width = sizeHints.min_width = sizeHints.max_width = splash->width;
 244         sizeHints.height = sizeHints.base_height = sizeHints.min_height = sizeHints.max_height = splash->height;
 245         sizeHints.win_gravity = NorthWestGravity;
 246 
 247         XSetWMNormalHints(splash->display, splash->window, &sizeHints);
 248     }
 249 }
 250 
 251 void
 252 SplashCreateWindow(Splash * splash) {
 253     XSizeHints sizeHints;
 254 
 255     XSetWindowAttributes attr;
 256 
 257     attr.backing_store = NotUseful;
 258     attr.colormap = XDefaultColormapOfScreen(splash->screen);
 259     attr.save_under = True;
 260     attr.cursor = splash->cursor = XCreateFontCursor(splash->display, XC_watch);
 261     attr.event_mask = ExposureMask;
 262 
 263     SplashCenter(splash);
 264 
 265     splash->window = XCreateWindow(splash->display, XRootWindowOfScreen(splash->screen),
 266         splash->x, splash->y, splash->width, splash->height, 0, CopyFromParent,
 267         InputOutput, CopyFromParent, CWColormap | CWBackingStore | CWSaveUnder | CWCursor | CWEventMask,
 268         &attr);
 269     SplashUpdateSizeHints(splash);
 270 
 271 
 272     splash->wmHints = XAllocWMHints();
 273     if (splash->wmHints) {
 274         splash->wmHints->flags = InputHint | StateHint;
 275         splash->wmHints->input = False;
 276         splash->wmHints->initial_state = NormalState;
 277         XSetWMHints(splash->display, splash->window, splash->wmHints);
 278     }
 279 }
 280 
 281 /* for changing the visible shape of a window to an nonrectangular form */
 282 void
 283 SplashUpdateShape(Splash * splash) {
 284     if (!shapeSupported)
 285         return;
 286     if (!splash->maskRequired) {
 287         return;
 288     }
 289     XShapeCombineRectangles(splash->display, splash->window, ShapeClip, 0, 0,
 290             splash->frames[splash->currentFrame].rects,
 291             splash->frames[splash->currentFrame].numRects, ShapeSet, YXBanded);
 292     XShapeCombineRectangles(splash->display, splash->window, ShapeBounding,
 293             0, 0, splash->frames[splash->currentFrame].rects,
 294             splash->frames[splash->currentFrame].numRects, ShapeSet, YXBanded);
 295 }
 296 
 297 /* for reverting the visible shape of a window to an rectangular form */
 298 void
 299 SplashRevertShape(Splash * splash) {
 300     if (!shapeSupported)
 301         return;
 302     if (splash->maskRequired)
 303         return;
 304 
 305     XShapeCombineMask (splash->display, splash->window, ShapeClip,
 306                        0, 0, None, ShapeSet);
 307     XShapeCombineMask (splash->display, splash->window , ShapeBounding,
 308                        0, 0, None, ShapeSet);
 309 }
 310 
 311 int
 312 ByteOrderToX(int byteOrder) {
 313     if (byteOrder == BYTE_ORDER_NATIVE)
 314         byteOrder = platformByteOrder();
 315     switch (byteOrder) {
 316     case BYTE_ORDER_LSBFIRST:
 317         return LSBFirst;
 318     case BYTE_ORDER_MSBFIRST:
 319         return MSBFirst;
 320     default:
 321         return -1;
 322     }
 323 }
 324 
 325 void
 326 SplashRedrawWindow(Splash * splash) {
 327     XImage *ximage;
 328 
 329     // making this method redraw a part of the image does not make
 330     // much sense as SplashUpdateScreenData always re-generates
 331     // the image completely, so whole window is always redrawn
 332 
 333     SplashUpdateScreenData(splash);
 334     ximage = XCreateImage(splash->display, splash->visual,
 335             splash->screenFormat.depthBytes * 8, ZPixmap, 0, (char *) NULL,
 336             splash->width, splash->height, 8, 0);
 337     ximage->data = (char *) splash->screenData;
 338     ximage->bits_per_pixel = ximage->depth;
 339     ximage->bytes_per_line = ximage->depth * ximage->width / 8;
 340     ximage->byte_order = ByteOrderToX(splash->screenFormat.byteOrder);
 341     ximage->bitmap_unit = 8;
 342     XPutImage(splash->display, splash->window,
 343             XDefaultGCOfScreen(splash->screen), ximage, 0, 0, 0, 0,
 344             splash->width, splash->height);
 345     ximage->data = NULL;
 346     XDestroyImage(ximage);
 347     SplashRemoveDecoration(splash);
 348     XMapWindow(splash->display, splash->window);
 349     XFlush(splash->display);
 350 }
 351 
 352 void SplashReconfigureNow(Splash * splash) {
 353     SplashCenter(splash);
 354     if (splash->window) {
 355         XUnmapWindow(splash->display, splash->window);
 356         XMoveResizeWindow(splash->display, splash->window,
 357             splash->x, splash->y,
 358             splash->width, splash->height);
 359         SplashUpdateSizeHints(splash);
 360     }
 361     if (splash->maskRequired) {
 362         SplashUpdateShape(splash);
 363     } else {
 364         SplashRevertShape(splash);
 365     }
 366     SplashRedrawWindow(splash);
 367 }
 368 
 369 
 370 void
 371 sendctl(Splash * splash, char code) {
 372 //    if (splash->isVisible>0) {
 373     if (splash && splash->controlpipe[1]) {
 374         write(splash->controlpipe[1], &code, 1);
 375     }
 376 }
 377 
 378 int
 379 HandleError(Display * disp, XErrorEvent * err) {
 380     // silently ignore non-fatal errors
 381     /*
 382     char msg[0x1000];
 383     char buf[0x1000];
 384     XGetErrorText(disp, err->error_code, msg, sizeof(msg));
 385     fprintf(stderr, "Xerror %s, XID %x, ser# %d\n", msg, err->resourceid,
 386         err->serial);
 387     sprintf(buf, "%d", err->request_code);
 388     XGetErrorDatabaseText(disp, "XRequest", buf, "Unknown", msg, sizeof(msg));
 389     fprintf(stderr, "Major opcode %d (%s)\n", err->request_code, msg);
 390     if (err->request_code > 128) {
 391         fprintf(stderr, "Minor opcode %d\n", err->minor_code);
 392     }
 393     */
 394     return 0;
 395 }
 396 
 397 int
 398 HandleIOError(Display * display) {
 399     // for really bad errors, we should exit the thread we're on
 400     SplashCleanup(SplashGetInstance());
 401     pthread_exit(NULL);
 402     return 0;
 403 }
 404 
 405 void
 406 SplashInitPlatform(Splash * splash) {
 407     int shapeVersionMajor, shapeVersionMinor;
 408 
 409     // This setting enables the synchronous Xlib mode!
 410     // Don't use it == 1 in production builds!
 411 #if (defined DEBUG)
 412     _Xdebug = 1;
 413 #endif
 414 
 415     pthread_mutex_init(&splash->lock, NULL);
 416 
 417     // We should not ignore any errors.
 418     //XSetErrorHandler(HandleError);
 419 //    XSetIOErrorHandler(HandleIOError);
 420     XSetIOErrorHandler(NULL);
 421     splash->display = XOpenDisplay(NULL);
 422     if (!splash->display) {
 423         splash->isVisible = -1;
 424         return;
 425     }
 426 
 427     shapeSupported = XShapeQueryExtension(splash->display, &shapeEventBase,
 428             &shapeErrorBase);
 429     if (shapeSupported) {
 430         XShapeQueryVersion(splash->display, &shapeVersionMajor,
 431                 &shapeVersionMinor);
 432     }
 433 
 434     splash->screen = XDefaultScreenOfDisplay(splash->display);
 435     splash->visual = XDefaultVisualOfScreen(splash->screen);
 436     switch (splash->visual->class) {
 437     case TrueColor: {
 438             int depth = XDefaultDepthOfScreen(splash->screen);
 439 
 440             splash->byteAlignment = 1;
 441             splash->maskRequired = shapeSupported;
 442             initFormat(&splash->screenFormat, splash->visual->red_mask,
 443                     splash->visual->green_mask, splash->visual->blue_mask, 0);
 444             splash->screenFormat.byteOrder =
 445                 (XImageByteOrder(splash->display) == LSBFirst ?
 446                  BYTE_ORDER_LSBFIRST : BYTE_ORDER_MSBFIRST);
 447             splash->screenFormat.depthBytes = (depth + 7) / 8;
 448             // TrueColor depth probably can't be less
 449             // than 8 bits, and it's always byte padded
 450             break;
 451         }
 452     case PseudoColor: {
 453             int availableColors;
 454             int numColors;
 455             int numComponents[3];
 456             unsigned long colorIndex[SPLASH_COLOR_MAP_SIZE];
 457             XColor xColors[SPLASH_COLOR_MAP_SIZE];
 458             int i;
 459             int depth = XDefaultDepthOfScreen(splash->screen);
 460             int scale = 65535 / MAX_COLOR_VALUE;
 461 
 462             availableColors = GetNumAvailableColors(splash->display, splash->screen,
 463                     splash->visual->map_entries);
 464             numColors = quantizeColors(availableColors, numComponents);
 465             if (numColors > availableColors) {
 466                 // Could not allocate the color cells. Most probably
 467                 // the pool got exhausted. Disable the splash screen.
 468                 XCloseDisplay(splash->display);
 469                 splash->isVisible = -1;
 470                 splash->display = NULL;
 471                 splash->screen = NULL;
 472                 splash->visual = NULL;
 473                 fprintf(stderr, "Warning: unable to initialize the splashscreen. Not enough available color cells.\n");
 474                 return;
 475             }
 476             splash->cmap = AllocColors(splash->display, splash->screen,
 477                     numColors, colorIndex);
 478             for (i = 0; i < numColors; i++) {
 479                 splash->colorIndex[i] = colorIndex[i];
 480             }
 481             initColorCube(numComponents, splash->colorMap, splash->dithers,
 482                     splash->colorIndex);
 483             for (i = 0; i < numColors; i++) {
 484                 xColors[i].pixel = colorIndex[i];
 485                 xColors[i].red = (unsigned short)
 486                     QUAD_RED(splash->colorMap[colorIndex[i]]) * scale;
 487                 xColors[i].green = (unsigned short)
 488                     QUAD_GREEN(splash->colorMap[colorIndex[i]]) * scale;
 489                 xColors[i].blue = (unsigned short)
 490                     QUAD_BLUE(splash->colorMap[colorIndex[i]]) * scale;
 491                 xColors[i].flags = DoRed | DoGreen | DoBlue;
 492             }
 493             XStoreColors(splash->display, splash->cmap, xColors, numColors);
 494             initFormat(&splash->screenFormat, 0, 0, 0, 0);
 495             splash->screenFormat.colorIndex = splash->colorIndex;
 496             splash->screenFormat.depthBytes = (depth + 7) / 8;  // or always 8?
 497             splash->screenFormat.colorMap = splash->colorMap;
 498             splash->screenFormat.dithers = splash->dithers;
 499             splash->screenFormat.numColors = numColors;
 500             splash->screenFormat.byteOrder = BYTE_ORDER_NATIVE;
 501             break;
 502         }
 503     default:
 504         ; /* FIXME: should probably be fixed, but javaws splash screen doesn't support other visuals either */
 505     }
 506 }
 507 
 508 
 509 void
 510 SplashCleanupPlatform(Splash * splash) {
 511     int i;
 512 
 513     if (splash->frames) {
 514         for (i = 0; i < splash->frameCount; i++) {
 515             if (splash->frames[i].rects) {
 516                 free(splash->frames[i].rects);
 517                 splash->frames[i].rects = NULL;
 518             }
 519         }
 520     }
 521     splash->maskRequired = shapeSupported;
 522 }
 523 
 524 void
 525 SplashDonePlatform(Splash * splash) {
 526     pthread_mutex_destroy(&splash->lock);
 527     if (splash->cmap) {
 528         unsigned long colorIndex[SPLASH_COLOR_MAP_SIZE];
 529         int i;
 530 
 531         for (i = 0; i < splash->screenFormat.numColors; i++) {
 532             colorIndex[i] = splash->colorIndex[i];
 533         }
 534         FreeColors(splash->display, splash->screen,
 535                 splash->screenFormat.numColors, colorIndex);
 536     }
 537     if (splash->window)
 538         XDestroyWindow(splash->display, splash->window);
 539     if (splash->wmHints)
 540         XFree(splash->wmHints);
 541     if (splash->cursor)
 542         XFreeCursor(splash->display, splash->cursor);
 543     if (splash->display)
 544         XCloseDisplay(splash->display);
 545 }
 546 
 547 void
 548 SplashEventLoop(Splash * splash) {
 549 
 550     /*      Different from win32 implementation - this loop
 551        uses poll timeouts instead of a timer */
 552     /* we should have splash _locked_ on entry!!! */
 553 
 554     int xconn = XConnectionNumber(splash->display);
 555 
 556     while (1) {
 557         struct pollfd pfd[2];
 558         int timeout = -1;
 559         int ctl = splash->controlpipe[0];
 560         int rc;
 561         int pipes_empty;
 562 
 563         pfd[0].fd = xconn;
 564         pfd[0].events = POLLIN | POLLPRI;
 565 
 566         pfd[1].fd = ctl;
 567         pfd[1].events = POLLIN | POLLPRI;
 568 
 569         errno = 0;
 570         if (splash->isVisible>0 && SplashIsStillLooping(splash)) {
 571             timeout = splash->time + splash->frames[splash->currentFrame].delay
 572                 - SplashTime();
 573             if (timeout < 0) {
 574                 timeout = 0;
 575             }
 576         }
 577         SplashUnlock(splash);
 578         rc = poll(pfd, 2, timeout);
 579         SplashLock(splash);
 580         if (splash->isVisible > 0 && splash->currentFrame >= 0 &&
 581                 SplashTime() >= splash->time + splash->frames[splash->currentFrame].delay) {
 582             SplashNextFrame(splash);
 583             SplashUpdateShape(splash);
 584             SplashRedrawWindow(splash);
 585         }
 586         if (rc <= 0) {
 587             errno = 0;
 588             continue;
 589         }
 590         pipes_empty = 0;
 591         while(!pipes_empty) {
 592             char buf;
 593 
 594             pipes_empty = 1;
 595             if (read(ctl, &buf, sizeof(buf)) > 0) {
 596                 pipes_empty = 0;
 597                 switch (buf) {
 598                 case SPLASHCTL_UPDATE:
 599                     if (splash->isVisible>0) {
 600                         SplashRedrawWindow(splash);
 601                     }
 602                     break;
 603                 case SPLASHCTL_RECONFIGURE:
 604                     if (splash->isVisible>0) {
 605                         SplashReconfigureNow(splash);
 606                     }
 607                     break;
 608                 case SPLASHCTL_QUIT:
 609                     return;
 610                 }
 611             }
 612             // we're not using "while(XPending)", processing one event
 613             // at a time to avoid control pipe starvation
 614             if (XPending(splash->display)) {
 615                 XEvent evt;
 616 
 617                 pipes_empty = 0;
 618                 XNextEvent(splash->display, &evt);
 619                 switch (evt.type) {
 620                     case Expose:
 621                         if (splash->isVisible>0) {
 622                             // we're doing full redraw so we just
 623                             // skip the remaining painting events in the queue
 624                             while(XCheckTypedEvent(splash->display, Expose,
 625                                 &evt));
 626                             SplashRedrawWindow(splash);
 627                         }
 628                         break;
 629                     /* ... */
 630                 }
 631             }
 632         }
 633     }
 634 }
 635 
 636 /*  we can't use OverrideRedirect for the window as the window should not be
 637     always-on-top, so we must set appropriate wm hints
 638 
 639     this functions sets olwm, mwm and EWMH hints for undecorated window at once
 640 
 641     It works for: mwm, openbox, wmaker, metacity, KWin (FIXME: test more wm's)
 642     Should work for: fvwm2.5.x, blackbox, olwm
 643     Maybe works for: enlightenment, icewm
 644     Does not work for: twm, fvwm2.4.7
 645 
 646 */
 647 
 648 void
 649 SplashRemoveDecoration(Splash * splash) {
 650     Atom atom_set;
 651     Atom atom_list[4];
 652 
 653     /* the struct below was copied from MwmUtil.h */
 654 
 655     struct PROPMOTIFWMHINTS {
 656     /* 32-bit property items are stored as long on the client (whether
 657      * that means 32 bits or 64).  XChangeProperty handles the conversion
 658      * to the actual 32-bit quantities sent to the server.
 659      */
 660         unsigned long   flags;
 661         unsigned long   functions;
 662         unsigned long   decorations;
 663         long            inputMode;
 664         unsigned long   status;
 665     }
 666     mwm_hints;
 667 
 668     /* WM_TAKE_FOCUS hint to avoid wm's transfer of focus to this window */
 669     /* WM_DELETE_WINDOW hint to avoid closing this window with Alt-F4. See bug 6474035 */
 670     atom_set = XInternAtom(splash->display, "WM_PROTOCOLS", True);
 671     if (atom_set != None) {
 672         atom_list[0] = XInternAtom(splash->display, "WM_TAKE_FOCUS", True);
 673         atom_list[1] = XInternAtom(splash->display, "WM_DELETE_WINDOW", True);
 674 
 675         XChangeProperty(splash->display, splash->window, atom_set, XA_ATOM, 32,
 676                 PropModeReplace, (unsigned char *) atom_list, 2);
 677     }
 678 
 679     /* mwm hints */
 680     atom_set = XInternAtom(splash->display, "_MOTIF_WM_HINTS", True);
 681     if (atom_set != None) {
 682         /* flags for decoration and functions */
 683         mwm_hints.flags = (1L << 1) | (1L << 0);
 684         mwm_hints.decorations = 0;
 685         mwm_hints.functions = 0;
 686         XChangeProperty(splash->display, splash->window, atom_set, atom_set,
 687                 32, PropModeReplace, (unsigned char *) &mwm_hints, 5);
 688     }
 689 
 690     /* olwm hints */
 691     atom_set = XInternAtom(splash->display, "_OL_DECOR_DEL", True);
 692     if (atom_set != None) {
 693         atom_list[0] = XInternAtom(splash->display, "_OL_DECOR_RESIZE", True);
 694         atom_list[1] = XInternAtom(splash->display, "_OL_DECOR_HEADER", True);
 695         atom_list[2] = XInternAtom(splash->display, "_OL_DECOR_PIN", True);
 696         atom_list[3] = XInternAtom(splash->display, "_OL_DECOR_CLOSE", True);
 697         XChangeProperty(splash->display, splash->window, atom_set, XA_ATOM, 32,
 698                 PropModeReplace, (unsigned char *) atom_list, 4);
 699     }
 700 
 701     /* generic EMWH hints
 702        we do not set _NET_WM_WINDOW_TYPE to _NET_WM_WINDOW_TYPE_SPLASH
 703        hint support due to gnome making this window always-on-top
 704        so we have to set _NET_WM_STATE and _NET_WM_ALLOWED_ACTIONS correctly
 705        _NET_WM_STATE: SKIP_TASKBAR and SKIP_PAGER
 706        _NET_WM_ALLOWED_ACTIONS: disable all actions */
 707     atom_set = XInternAtom(splash->display, "_NET_WM_STATE", True);
 708     if (atom_set != None) {
 709         atom_list[0] = XInternAtom(splash->display,
 710                 "_NET_WM_STATE_SKIP_TASKBAR", True);
 711         atom_list[1] = XInternAtom(splash->display,
 712                 "_NET_WM_STATE_SKIP_PAGER", True);
 713         XChangeProperty(splash->display, splash->window, atom_set, XA_ATOM, 32,
 714                 PropModeReplace, (unsigned char *) atom_list, 2);
 715     }
 716     atom_set = XInternAtom(splash->display, "_NET_WM_ALLOWED_ACTIONS", True);
 717     if (atom_set != None) {
 718         XChangeProperty(splash->display, splash->window, atom_set, XA_ATOM, 32,
 719                 PropModeReplace, (unsigned char *) atom_list, 0);
 720     }
 721 }
 722 
 723 void
 724 SplashPThreadDestructor(void *arg) {
 725     /* this will be used in case of emergency thread exit on xlib error */
 726     Splash *splash = (Splash *) arg;
 727 
 728     if (splash) {
 729         SplashCleanup(splash);
 730     }
 731 }
 732 
 733 void *
 734 SplashScreenThread(void *param) {
 735     Splash *splash = (Splash *) param;
 736 //    pthread_key_t key;
 737 
 738 //    pthread_key_create(&key, SplashPThreadDestructor);
 739 //    pthread_setspecific(key, splash);
 740 
 741     SplashLock(splash);
 742     pipe(splash->controlpipe);
 743     fcntl(splash->controlpipe[0], F_SETFL,
 744         fcntl(splash->controlpipe[0], F_GETFL, 0) | O_NONBLOCK);
 745     splash->time = SplashTime();
 746     SplashCreateWindow(splash);
 747     fflush(stdout);
 748     if (splash->window) {
 749         SplashRemoveDecoration(splash);
 750         XStoreName(splash->display, splash->window, "Java");
 751         XMapRaised(splash->display, splash->window);
 752         SplashUpdateShape(splash);
 753         SplashRedrawWindow(splash);
 754         SplashEventLoop(splash);
 755     }
 756     SplashUnlock(splash);
 757     SplashDone(splash);
 758 
 759     splash->isVisible=-1;
 760     return 0;
 761 }
 762 
 763 void
 764 SplashCreateThread(Splash * splash) {
 765     pthread_t thr;
 766     pthread_attr_t attr;
 767     int rc;
 768 
 769     pthread_attr_init(&attr);
 770     rc = pthread_create(&thr, &attr, SplashScreenThread, (void *) splash);
 771 }
 772 
 773 void
 774 SplashLock(Splash * splash) {
 775     pthread_mutex_lock(&splash->lock);
 776 }
 777 
 778 void
 779 SplashUnlock(Splash * splash) {
 780     pthread_mutex_unlock(&splash->lock);
 781 }
 782 
 783 void
 784 SplashClosePlatform(Splash * splash) {
 785     sendctl(splash, SPLASHCTL_QUIT);
 786 }
 787 
 788 void
 789 SplashUpdate(Splash * splash) {
 790     sendctl(splash, SPLASHCTL_UPDATE);
 791 }
 792 
 793 void
 794 SplashReconfigure(Splash * splash) {
 795     sendctl(splash, SPLASHCTL_RECONFIGURE);
 796 }