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