1 /*
2  * Copyright (c) 2005, 2014, 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 */
SplashConvertStringAlloc(const char * in,int * 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
SplashInitFrameShape(Splash * splash,int imageIndex)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
SplashTime(void)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
msec2timeval(unsigned time,struct timeval * tv)152 msec2timeval(unsigned time, struct timeval *tv) {
153     tv->tv_sec = time / 1000;
154     tv->tv_usec = (time % 1000) * 1000;
155 }
156 
157 int
GetNumAvailableColors(Display * display,Screen * screen,unsigned map_entries)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
AllocColors(Display * display,Screen * screen,int numColors,unsigned long * pr)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
FreeColors(Display * display,Screen * screen,int numColors,unsigned long * pr)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 
SplashCenter(Splash * splash)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 
SplashUpdateSizeHints(Splash * splash)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
SplashCreateWindow(Splash * splash)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
SplashUpdateShape(Splash * splash)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
SplashRevertShape(Splash * splash)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
ByteOrderToX(int byteOrder)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
SplashRedrawWindow(Splash * splash)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 
SplashReconfigureNow(Splash * splash)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
sendctl(Splash * splash,char code)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
HandleError(Display * disp,XErrorEvent * err)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
HandleIOError(Display * display)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
SplashInitPlatform(Splash * splash)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
SplashCleanupPlatform(Splash * splash)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
SplashDonePlatform(Splash * splash)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
SplashEventLoop(Splash * splash)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
SplashRemoveDecoration(Splash * splash)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
SplashPThreadDestructor(void * arg)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 *
SplashScreenThread(void * param)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         SplashEventLoop(splash);
757     }
758     SplashUnlock(splash);
759     SplashDone(splash);
760 
761     splash->isVisible=-1;
762     return 0;
763 }
764 
765 void
SplashCreateThread(Splash * splash)766 SplashCreateThread(Splash * splash) {
767     pthread_t thr;
768     pthread_attr_t attr;
769     int rc;
770 
771     pthread_attr_init(&attr);
772     rc = pthread_create(&thr, &attr, SplashScreenThread, (void *) splash);
773 }
774 
775 void
SplashLock(Splash * splash)776 SplashLock(Splash * splash) {
777     pthread_mutex_lock(&splash->lock);
778 }
779 
780 void
SplashUnlock(Splash * splash)781 SplashUnlock(Splash * splash) {
782     pthread_mutex_unlock(&splash->lock);
783 }
784 
785 void
SplashClosePlatform(Splash * splash)786 SplashClosePlatform(Splash * splash) {
787     sendctl(splash, SPLASHCTL_QUIT);
788 }
789 
790 void
SplashUpdate(Splash * splash)791 SplashUpdate(Splash * splash) {
792     sendctl(splash, SPLASHCTL_UPDATE);
793 }
794 
795 void
SplashReconfigure(Splash * splash)796 SplashReconfigure(Splash * splash) {
797     sendctl(splash, SPLASHCTL_RECONFIGURE);
798 }
799 
800 SPLASHEXPORT char*
SplashGetScaledImageName(const char * jarName,const char * fileName,float * scaleFactor)801 SplashGetScaledImageName(const char* jarName, const char* fileName,
802                            float *scaleFactor)
803 {
804     *scaleFactor = 1;
805     return NULL;
806 }
807