1 /*
   2  * Copyright (c) 2005, 2016, 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 (splash->currentFrame < 0 || !shapeSupported || !splash->maskRequired) {
 285         return;
 286     }
 287     XShapeCombineRectangles(splash->display, splash->window, ShapeClip, 0, 0,
 288             splash->frames[splash->currentFrame].rects,
 289             splash->frames[splash->currentFrame].numRects, ShapeSet, YXBanded);
 290     XShapeCombineRectangles(splash->display, splash->window, ShapeBounding,
 291             0, 0, splash->frames[splash->currentFrame].rects,
 292             splash->frames[splash->currentFrame].numRects, ShapeSet, YXBanded);
 293 }
 294 
 295 /* for reverting the visible shape of a window to an rectangular form */
 296 void
 297 SplashRevertShape(Splash * splash) {
 298     if (!shapeSupported)
 299         return;
 300     if (splash->maskRequired)
 301         return;
 302 
 303     XShapeCombineMask (splash->display, splash->window, ShapeClip,
 304                        0, 0, None, ShapeSet);
 305     XShapeCombineMask (splash->display, splash->window , ShapeBounding,
 306                        0, 0, None, ShapeSet);
 307 }
 308 
 309 int
 310 ByteOrderToX(int byteOrder) {
 311     if (byteOrder == BYTE_ORDER_NATIVE)
 312         byteOrder = platformByteOrder();
 313     switch (byteOrder) {
 314     case BYTE_ORDER_LSBFIRST:
 315         return LSBFirst;
 316     case BYTE_ORDER_MSBFIRST:
 317         return MSBFirst;
 318     default:
 319         return -1;
 320     }
 321 }
 322 
 323 void
 324 SplashRedrawWindow(Splash * splash) {
 325     if (splash->currentFrame < 0) {
 326         return;
 327     }
 328 
 329     XImage *ximage;
 330 
 331     // making this method redraw a part of the image does not make
 332     // much sense as SplashUpdateScreenData always re-generates
 333     // the image completely, so whole window is always redrawn
 334 
 335     SplashUpdateScreenData(splash);
 336     ximage = XCreateImage(splash->display, splash->visual,
 337             splash->screenFormat.depthBytes * 8, ZPixmap, 0, (char *) NULL,
 338             splash->width, splash->height, 8, 0);
 339     ximage->data = (char *) splash->screenData;
 340     ximage->bits_per_pixel = ximage->depth;
 341     ximage->bytes_per_line = ximage->depth * ximage->width / 8;
 342     ximage->byte_order = ByteOrderToX(splash->screenFormat.byteOrder);
 343     ximage->bitmap_unit = 8;
 344     XPutImage(splash->display, splash->window,
 345             XDefaultGCOfScreen(splash->screen), ximage, 0, 0, 0, 0,
 346             splash->width, splash->height);
 347     ximage->data = NULL;
 348     XDestroyImage(ximage);
 349     SplashRemoveDecoration(splash);
 350     XMapWindow(splash->display, splash->window);
 351     XFlush(splash->display);
 352 }
 353 
 354 void SplashReconfigureNow(Splash * splash) {
 355     SplashCenter(splash);
 356     if (splash->window) {
 357         XUnmapWindow(splash->display, splash->window);
 358         XMoveResizeWindow(splash->display, splash->window,
 359             splash->x, splash->y,
 360             splash->width, splash->height);
 361         SplashUpdateSizeHints(splash);
 362     }
 363     if (splash->maskRequired) {
 364         SplashUpdateShape(splash);
 365     } else {
 366         SplashRevertShape(splash);
 367     }
 368     SplashRedrawWindow(splash);
 369 }
 370 
 371 
 372 void
 373 sendctl(Splash * splash, char code) {
 374 //    if (splash->isVisible>0) {
 375     if (splash && splash->controlpipe[1]) {
 376         write(splash->controlpipe[1], &code, 1);
 377     }
 378 }
 379 
 380 int
 381 HandleError(Display * disp, XErrorEvent * err) {
 382     // silently ignore non-fatal errors
 383     /*
 384     char msg[0x1000];
 385     char buf[0x1000];
 386     XGetErrorText(disp, err->error_code, msg, sizeof(msg));
 387     fprintf(stderr, "Xerror %s, XID %x, ser# %d\n", msg, err->resourceid,
 388         err->serial);
 389     sprintf(buf, "%d", err->request_code);
 390     XGetErrorDatabaseText(disp, "XRequest", buf, "Unknown", msg, sizeof(msg));
 391     fprintf(stderr, "Major opcode %d (%s)\n", err->request_code, msg);
 392     if (err->request_code > 128) {
 393         fprintf(stderr, "Minor opcode %d\n", err->minor_code);
 394     }
 395     */
 396     return 0;
 397 }
 398 
 399 int
 400 HandleIOError(Display * display) {
 401     // for really bad errors, we should exit the thread we're on
 402     SplashCleanup(SplashGetInstance());
 403     pthread_exit(NULL);
 404     return 0;
 405 }
 406 
 407 void
 408 SplashInitPlatform(Splash * splash) {
 409     int shapeVersionMajor, shapeVersionMinor;
 410 
 411     // This setting enables the synchronous Xlib mode!
 412     // Don't use it == 1 in production builds!
 413 #if (defined DEBUG)
 414     _Xdebug = 1;
 415 #endif
 416 
 417     pthread_mutex_init(&splash->lock, NULL);
 418 
 419     // We should not ignore any errors.
 420     //XSetErrorHandler(HandleError);
 421 //    XSetIOErrorHandler(HandleIOError);
 422     XSetIOErrorHandler(NULL);
 423     splash->display = XOpenDisplay(NULL);
 424     if (!splash->display) {
 425         splash->isVisible = -1;
 426         return;
 427     }
 428 
 429     shapeSupported = XShapeQueryExtension(splash->display, &shapeEventBase,
 430             &shapeErrorBase);
 431     if (shapeSupported) {
 432         XShapeQueryVersion(splash->display, &shapeVersionMajor,
 433                 &shapeVersionMinor);
 434     }
 435 
 436     splash->screen = XDefaultScreenOfDisplay(splash->display);
 437     splash->visual = XDefaultVisualOfScreen(splash->screen);
 438     switch (splash->visual->class) {
 439     case TrueColor: {
 440             int depth = XDefaultDepthOfScreen(splash->screen);
 441 
 442             splash->byteAlignment = 1;
 443             splash->maskRequired = shapeSupported;
 444             initFormat(&splash->screenFormat, splash->visual->red_mask,
 445                     splash->visual->green_mask, splash->visual->blue_mask, 0);
 446             splash->screenFormat.byteOrder =
 447                 (XImageByteOrder(splash->display) == LSBFirst ?
 448                  BYTE_ORDER_LSBFIRST : BYTE_ORDER_MSBFIRST);
 449             splash->screenFormat.depthBytes = (depth + 7) / 8;
 450             // TrueColor depth probably can't be less
 451             // than 8 bits, and it's always byte padded
 452             break;
 453         }
 454     case PseudoColor: {
 455             int availableColors;
 456             int numColors;
 457             int numComponents[3];
 458             unsigned long colorIndex[SPLASH_COLOR_MAP_SIZE];
 459             XColor xColors[SPLASH_COLOR_MAP_SIZE];
 460             int i;
 461             int depth = XDefaultDepthOfScreen(splash->screen);
 462             int scale = 65535 / MAX_COLOR_VALUE;
 463 
 464             availableColors = GetNumAvailableColors(splash->display, splash->screen,
 465                     splash->visual->map_entries);
 466             numColors = quantizeColors(availableColors, numComponents);
 467             if (numColors > availableColors) {
 468                 // Could not allocate the color cells. Most probably
 469                 // the pool got exhausted. Disable the splash screen.
 470                 XCloseDisplay(splash->display);
 471                 splash->isVisible = -1;
 472                 splash->display = NULL;
 473                 splash->screen = NULL;
 474                 splash->visual = NULL;
 475                 fprintf(stderr, "Warning: unable to initialize the splashscreen. Not enough available color cells.\n");
 476                 return;
 477             }
 478             splash->cmap = AllocColors(splash->display, splash->screen,
 479                     numColors, colorIndex);
 480             for (i = 0; i < numColors; i++) {
 481                 splash->colorIndex[i] = colorIndex[i];
 482             }
 483             initColorCube(numComponents, splash->colorMap, splash->dithers,
 484                     splash->colorIndex);
 485             for (i = 0; i < numColors; i++) {
 486                 xColors[i].pixel = colorIndex[i];
 487                 xColors[i].red = (unsigned short)
 488                     QUAD_RED(splash->colorMap[colorIndex[i]]) * scale;
 489                 xColors[i].green = (unsigned short)
 490                     QUAD_GREEN(splash->colorMap[colorIndex[i]]) * scale;
 491                 xColors[i].blue = (unsigned short)
 492                     QUAD_BLUE(splash->colorMap[colorIndex[i]]) * scale;
 493                 xColors[i].flags = DoRed | DoGreen | DoBlue;
 494             }
 495             XStoreColors(splash->display, splash->cmap, xColors, numColors);
 496             initFormat(&splash->screenFormat, 0, 0, 0, 0);
 497             splash->screenFormat.colorIndex = splash->colorIndex;
 498             splash->screenFormat.depthBytes = (depth + 7) / 8;  // or always 8?
 499             splash->screenFormat.colorMap = splash->colorMap;
 500             splash->screenFormat.dithers = splash->dithers;
 501             splash->screenFormat.numColors = numColors;
 502             splash->screenFormat.byteOrder = BYTE_ORDER_NATIVE;
 503             break;
 504         }
 505     default:
 506         ; /* FIXME: should probably be fixed, but javaws splash screen doesn't support other visuals either */
 507     }
 508 }
 509 
 510 
 511 void
 512 SplashCleanupPlatform(Splash * splash) {
 513     int i;
 514 
 515     if (splash->frames) {
 516         for (i = 0; i < splash->frameCount; i++) {
 517             if (splash->frames[i].rects) {
 518                 free(splash->frames[i].rects);
 519                 splash->frames[i].rects = NULL;
 520             }
 521         }
 522     }
 523     splash->maskRequired = shapeSupported;
 524 }
 525 
 526 void
 527 SplashDonePlatform(Splash * splash) {
 528     pthread_mutex_destroy(&splash->lock);
 529     if (splash->cmap) {
 530         unsigned long colorIndex[SPLASH_COLOR_MAP_SIZE];
 531         int i;
 532 
 533         for (i = 0; i < splash->screenFormat.numColors; i++) {
 534             colorIndex[i] = splash->colorIndex[i];
 535         }
 536         FreeColors(splash->display, splash->screen,
 537                 splash->screenFormat.numColors, colorIndex);
 538     }
 539     if (splash->window)
 540         XDestroyWindow(splash->display, splash->window);
 541     if (splash->wmHints)
 542         XFree(splash->wmHints);
 543     if (splash->cursor)
 544         XFreeCursor(splash->display, splash->cursor);
 545     if (splash->display)
 546         XCloseDisplay(splash->display);
 547 }
 548 
 549 void
 550 SplashEventLoop(Splash * splash) {
 551 
 552     /*      Different from win32 implementation - this loop
 553        uses poll timeouts instead of a timer */
 554     /* we should have splash _locked_ on entry!!! */
 555 
 556     int xconn = XConnectionNumber(splash->display);
 557 
 558     while (1) {
 559         struct pollfd pfd[2];
 560         int timeout = -1;
 561         int ctl = splash->controlpipe[0];
 562         int rc;
 563         int pipes_empty;
 564 
 565         pfd[0].fd = xconn;
 566         pfd[0].events = POLLIN | POLLPRI;
 567 
 568         pfd[1].fd = ctl;
 569         pfd[1].events = POLLIN | POLLPRI;
 570 
 571         errno = 0;
 572         if (splash->isVisible>0 && SplashIsStillLooping(splash)) {
 573             timeout = splash->time + splash->frames[splash->currentFrame].delay
 574                 - SplashTime();
 575             if (timeout < 0) {
 576                 timeout = 0;
 577             }
 578         }
 579         SplashUnlock(splash);
 580         rc = poll(pfd, 2, timeout);
 581         SplashLock(splash);
 582         if (splash->isVisible > 0 && splash->currentFrame >= 0 &&
 583                 SplashTime() >= splash->time + splash->frames[splash->currentFrame].delay) {
 584             SplashNextFrame(splash);
 585             SplashUpdateShape(splash);
 586             SplashRedrawWindow(splash);
 587         }
 588         if (rc <= 0) {
 589             errno = 0;
 590             continue;
 591         }
 592         pipes_empty = 0;
 593         while(!pipes_empty) {
 594             char buf;
 595 
 596             pipes_empty = 1;
 597             if (read(ctl, &buf, sizeof(buf)) > 0) {
 598                 pipes_empty = 0;
 599                 switch (buf) {
 600                 case SPLASHCTL_UPDATE:
 601                     if (splash->isVisible>0) {
 602                         SplashRedrawWindow(splash);
 603                     }
 604                     break;
 605                 case SPLASHCTL_RECONFIGURE:
 606                     if (splash->isVisible>0) {
 607                         SplashReconfigureNow(splash);
 608                     }
 609                     break;
 610                 case SPLASHCTL_QUIT:
 611                     return;
 612                 }
 613             }
 614             // we're not using "while(XPending)", processing one event
 615             // at a time to avoid control pipe starvation
 616             if (XPending(splash->display)) {
 617                 XEvent evt;
 618 
 619                 pipes_empty = 0;
 620                 XNextEvent(splash->display, &evt);
 621                 switch (evt.type) {
 622                     case Expose:
 623                         if (splash->isVisible>0) {
 624                             // we're doing full redraw so we just
 625                             // skip the remaining painting events in the queue
 626                             while(XCheckTypedEvent(splash->display, Expose,
 627                                 &evt));
 628                             SplashRedrawWindow(splash);
 629                         }
 630                         break;
 631                     /* ... */
 632                 }
 633             }
 634         }
 635     }
 636 }
 637 
 638 /*  we can't use OverrideRedirect for the window as the window should not be
 639     always-on-top, so we must set appropriate wm hints
 640 
 641     this functions sets olwm, mwm and EWMH hints for undecorated window at once
 642 
 643     It works for: mwm, openbox, wmaker, metacity, KWin (FIXME: test more wm's)
 644     Should work for: fvwm2.5.x, blackbox, olwm
 645     Maybe works for: enlightenment, icewm
 646     Does not work for: twm, fvwm2.4.7
 647 
 648 */
 649 
 650 void
 651 SplashRemoveDecoration(Splash * splash) {
 652     Atom atom_set;
 653     Atom atom_list[4];
 654 
 655     /* the struct below was copied from MwmUtil.h */
 656 
 657     struct PROPMOTIFWMHINTS {
 658     /* 32-bit property items are stored as long on the client (whether
 659      * that means 32 bits or 64).  XChangeProperty handles the conversion
 660      * to the actual 32-bit quantities sent to the server.
 661      */
 662         unsigned long   flags;
 663         unsigned long   functions;
 664         unsigned long   decorations;
 665         long            inputMode;
 666         unsigned long   status;
 667     }
 668     mwm_hints;
 669 
 670     /* WM_TAKE_FOCUS hint to avoid wm's transfer of focus to this window */
 671     /* WM_DELETE_WINDOW hint to avoid closing this window with Alt-F4. See bug 6474035 */
 672     atom_set = XInternAtom(splash->display, "WM_PROTOCOLS", True);
 673     if (atom_set != None) {
 674         atom_list[0] = XInternAtom(splash->display, "WM_TAKE_FOCUS", True);
 675         atom_list[1] = XInternAtom(splash->display, "WM_DELETE_WINDOW", True);
 676 
 677         XChangeProperty(splash->display, splash->window, atom_set, XA_ATOM, 32,
 678                 PropModeReplace, (unsigned char *) atom_list, 2);
 679     }
 680 
 681     /* mwm hints */
 682     atom_set = XInternAtom(splash->display, "_MOTIF_WM_HINTS", True);
 683     if (atom_set != None) {
 684         /* flags for decoration and functions */
 685         mwm_hints.flags = (1L << 1) | (1L << 0);
 686         mwm_hints.decorations = 0;
 687         mwm_hints.functions = 0;
 688         XChangeProperty(splash->display, splash->window, atom_set, atom_set,
 689                 32, PropModeReplace, (unsigned char *) &mwm_hints, 5);
 690     }
 691 
 692     /* olwm hints */
 693     atom_set = XInternAtom(splash->display, "_OL_DECOR_DEL", True);
 694     if (atom_set != None) {
 695         atom_list[0] = XInternAtom(splash->display, "_OL_DECOR_RESIZE", True);
 696         atom_list[1] = XInternAtom(splash->display, "_OL_DECOR_HEADER", True);
 697         atom_list[2] = XInternAtom(splash->display, "_OL_DECOR_PIN", True);
 698         atom_list[3] = XInternAtom(splash->display, "_OL_DECOR_CLOSE", True);
 699         XChangeProperty(splash->display, splash->window, atom_set, XA_ATOM, 32,
 700                 PropModeReplace, (unsigned char *) atom_list, 4);
 701     }
 702 
 703     /* generic EMWH hints
 704        we do not set _NET_WM_WINDOW_TYPE to _NET_WM_WINDOW_TYPE_SPLASH
 705        hint support due to gnome making this window always-on-top
 706        so we have to set _NET_WM_STATE and _NET_WM_ALLOWED_ACTIONS correctly
 707        _NET_WM_STATE: SKIP_TASKBAR and SKIP_PAGER
 708        _NET_WM_ALLOWED_ACTIONS: disable all actions */
 709     atom_set = XInternAtom(splash->display, "_NET_WM_STATE", True);
 710     if (atom_set != None) {
 711         atom_list[0] = XInternAtom(splash->display,
 712                 "_NET_WM_STATE_SKIP_TASKBAR", True);
 713         atom_list[1] = XInternAtom(splash->display,
 714                 "_NET_WM_STATE_SKIP_PAGER", True);
 715         XChangeProperty(splash->display, splash->window, atom_set, XA_ATOM, 32,
 716                 PropModeReplace, (unsigned char *) atom_list, 2);
 717     }
 718     atom_set = XInternAtom(splash->display, "_NET_WM_ALLOWED_ACTIONS", True);
 719     if (atom_set != None) {
 720         XChangeProperty(splash->display, splash->window, atom_set, XA_ATOM, 32,
 721                 PropModeReplace, (unsigned char *) atom_list, 0);
 722     }
 723 }
 724 
 725 void
 726 SplashPThreadDestructor(void *arg) {
 727     /* this will be used in case of emergency thread exit on xlib error */
 728     Splash *splash = (Splash *) arg;
 729 
 730     if (splash) {
 731         SplashCleanup(splash);
 732     }
 733 }
 734 
 735 void *
 736 SplashScreenThread(void *param) {
 737     Splash *splash = (Splash *) param;
 738 //    pthread_key_t key;
 739 
 740 //    pthread_key_create(&key, SplashPThreadDestructor);
 741 //    pthread_setspecific(key, splash);
 742 
 743     SplashLock(splash);
 744     pipe(splash->controlpipe);
 745     fcntl(splash->controlpipe[0], F_SETFL,
 746         fcntl(splash->controlpipe[0], F_GETFL, 0) | O_NONBLOCK);
 747     splash->time = SplashTime();
 748     SplashCreateWindow(splash);
 749     fflush(stdout);
 750     if (splash->window) {
 751         SplashRemoveDecoration(splash);
 752         XStoreName(splash->display, splash->window, "Java");
 753         XMapRaised(splash->display, splash->window);
 754         SplashUpdateShape(splash);
 755         SplashRedrawWindow(splash);
 756         //map the splash co-ordinates as per system scale
 757         splash->x /= splash->scaleFactor;
 758         splash->y /= splash->scaleFactor;
 759         SplashEventLoop(splash);
 760     }
 761     SplashUnlock(splash);
 762     SplashDone(splash);
 763 
 764     splash->isVisible=-1;
 765     return 0;
 766 }
 767 
 768 void
 769 SplashCreateThread(Splash * splash) {
 770     pthread_t thr;
 771     pthread_attr_t attr;
 772     int rc;
 773 
 774     pthread_attr_init(&attr);
 775     rc = pthread_create(&thr, &attr, SplashScreenThread, (void *) splash);
 776 }
 777 
 778 void
 779 SplashLock(Splash * splash) {
 780     pthread_mutex_lock(&splash->lock);
 781 }
 782 
 783 void
 784 SplashUnlock(Splash * splash) {
 785     pthread_mutex_unlock(&splash->lock);
 786 }
 787 
 788 void
 789 SplashClosePlatform(Splash * splash) {
 790     sendctl(splash, SPLASHCTL_QUIT);
 791 }
 792 
 793 void
 794 SplashUpdate(Splash * splash) {
 795     sendctl(splash, SPLASHCTL_UPDATE);
 796 }
 797 
 798 void
 799 SplashReconfigure(Splash * splash) {
 800     sendctl(splash, SPLASHCTL_RECONFIGURE);
 801 }
 802 
 803 SPLASHEXPORT jboolean
 804 SplashGetScaledImageName(const char* jarName, const char* fileName,
 805                            float *scaleFactor, char *scaledImgName,
 806                            const size_t scaledImageNameLength)
 807 {
 808     *scaleFactor = 1;
 809 #ifndef __linux__
 810     return JNI_FALSE;
 811 #endif
 812     *scaleFactor = (float)getNativeScaleFactor(NULL);
 813     return GetScaledImageName(fileName, scaledImgName, scaleFactor, scaledImageNameLength);
 814 }
 815