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 */ |
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 |
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 |
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 |
153 | msec2timeval(unsigned time, struct timeval *tv) { |
154 | tv->tv_sec = time / 1000; |
155 | tv->tv_usec = (time % 1000) * 1000; |
156 | } |
157 | |
158 | int |
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 |
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 |
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 | |
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 | |
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 |
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 |
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 |
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 |
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 |
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 | |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 * |
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 |
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 |
781 | SplashLock(Splash * splash) { |
782 | pthread_mutex_lock(&splash->lock); |
783 | } |
784 | |
785 | void |
786 | SplashUnlock(Splash * splash) { |
787 | pthread_mutex_unlock(&splash->lock); |
788 | } |
789 | |
790 | void |
791 | SplashClosePlatform(Splash * splash) { |
792 | sendctl(splash, SPLASHCTL_QUIT); |
793 | } |
794 | |
795 | void |
796 | SplashUpdate(Splash * splash) { |
797 | sendctl(splash, SPLASHCTL_UPDATE); |
798 | } |
799 | |
800 | void |
801 | SplashReconfigure(Splash * splash) { |
802 | sendctl(splash, SPLASHCTL_RECONFIGURE); |
803 | } |
804 | |
805 | JNIEXPORT jboolean |
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 | |