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 // copy from awt.h
27 #ifndef _WIN32_WINNT
28 #define _WIN32_WINNT 0x0600
29 #endif
30
31 // copy from awt.h
32 #ifndef _WIN32_IE
33 #define _WIN32_IE 0x0600
34 #endif
35
36 #include "splashscreen_impl.h"
37 #include <windowsx.h>
38 #include <windows.h>
39 #include <winuser.h>
40 #include "sizecalc.h"
41
42 #ifndef WS_EX_LAYERED
43 #define WS_EX_LAYERED 0x80000
44 #endif
45
46 #ifndef ULW_ALPHA
47 #define ULW_ALPHA 0x00000002
48 #endif
49
50 #ifndef AC_SRC_OVER
51 #define AC_SRC_OVER 0x00
52 #endif
53
54 #ifndef AC_SRC_ALPHA
55 #define AC_SRC_ALPHA 0x01
56 #endif
57
58 #define WM_SPLASHUPDATE WM_USER+1
59 #define WM_SPLASHRECONFIGURE WM_USER+2
60
61 #define BUFF_SIZE 1024
62
63 /* Could use npt but decided to cut down on linked code size */
64 char* SplashConvertStringAlloc(const char* in, int *size) {
65 int len, outChars, rc;
66 WCHAR* buf;
67 if (!in) {
68 return NULL;
69 }
70 len = strlen(in);
71 outChars = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, in, len,
72 NULL, 0);
73 buf = (WCHAR*) SAFE_SIZE_ARRAY_ALLOC(malloc, outChars, sizeof(WCHAR));
74 if (!buf) {
75 return NULL;
76 }
77 rc = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, in, len,
78 buf, outChars);
79 if (rc==0) {
80 free(buf);
81 return NULL;
82 } else {
83 if (size) {
84 *size = rc;
85 }
86 return (char*)buf;
87 }
88 }
89
90 unsigned
91 SplashTime(void)
92 {
93 return GetTickCount();
94 }
95
96 void
97 SplashInitFrameShape(Splash * splash, int imageIndex)
98 {
99 RGNDATA *pRgnData;
100 RGNDATAHEADER *pRgnHdr;
101 ImageRect maskRect;
102
103 if (!splash->maskRequired)
104 return;
105
106 /* reserving memory for the worst case */
107 if (!IS_SAFE_SIZE_MUL(splash->width / 2 + 1, splash->height)) {
108 return;
109 }
110 pRgnData = (RGNDATA *) SAFE_SIZE_STRUCT_ALLOC(malloc, sizeof(RGNDATAHEADER),
111 sizeof(RECT), (splash->width / 2 + 1) * splash->height);
112 if (!pRgnData) {
113 return;
114 }
115 pRgnHdr = (RGNDATAHEADER *) pRgnData;
116 initRect(&maskRect, 0, 0, splash->width, splash->height, 1,
117 splash->width * splash->imageFormat.depthBytes,
118 splash->frames[imageIndex].bitmapBits, &splash->imageFormat);
119
120 pRgnHdr->dwSize = sizeof(RGNDATAHEADER);
121 pRgnHdr->iType = RDH_RECTANGLES;
122 pRgnHdr->nRgnSize = 0;
123 pRgnHdr->rcBound.top = 0;
124 pRgnHdr->rcBound.left = 0;
125 pRgnHdr->rcBound.bottom = splash->height;
126 pRgnHdr->rcBound.right = splash->width;
127
128 pRgnHdr->nCount = BitmapToYXBandedRectangles(&maskRect,
129 (RECT *) (((BYTE *) pRgnData) + sizeof(RGNDATAHEADER)));
130
131 splash->frames[imageIndex].hRgn = ExtCreateRegion(NULL,
132 sizeof(RGNDATAHEADER) + sizeof(RECT) * pRgnHdr->nCount, pRgnData);
133
134 free(pRgnData);
135 }
136
137 /* paint current splash screen frame to hdc
138 this function is unused in layered window mode */
139
140 void
141 SplashPaint(Splash * splash, HDC hdc)
142 {
143 unsigned numColors = splash->screenFormat.colorMap ?
144 splash->screenFormat.numColors : 0;
145 BITMAPV4HEADER *pBmi;
146 HPALETTE hOldPal = NULL;
147
148 if (!splash->frames)
149 return;
150 if (splash->currentFrame < 0 || splash->currentFrame >= splash->frameCount)
151 return;
152 pBmi = (BITMAPV4HEADER *) SAFE_SIZE_STRUCT_ALLOC(alloca, sizeof(BITMAPV4HEADER),
153 sizeof(RGBQUAD), numColors);
154 if (!pBmi) {
155 return;
156 }
157 memset(pBmi, 0, sizeof(BITMAPV4HEADER));
158 if (splash->screenFormat.colorMap)
159 memcpy(((BYTE *) pBmi) + sizeof(BITMAPV4HEADER),
160 splash->screenFormat.colorMap, sizeof(RGBQUAD) * numColors);
161
162 pBmi->bV4Size = sizeof(BITMAPV4HEADER);
163 pBmi->bV4Width = splash->width;
164 pBmi->bV4Height = -splash->height;
165 pBmi->bV4Planes = 1;
166 pBmi->bV4BitCount = (WORD) (splash->screenFormat.depthBytes * 8);
167 /* we're ALWAYS using BGRA in screenFormat */
168 pBmi->bV4V4Compression = BI_RGB;
169 pBmi->bV4ClrUsed = numColors;
170 pBmi->bV4ClrImportant = numColors;
171 pBmi->bV4AlphaMask = splash->screenFormat.mask[3];
172 pBmi->bV4RedMask = splash->screenFormat.mask[2];
173 pBmi->bV4GreenMask = splash->screenFormat.mask[1];
174 pBmi->bV4BlueMask = splash->screenFormat.mask[0];
175
176 /* creating the palette in SplashInitPlatform does not work, so I'm creating it
177 here on demand */
178 if (!splash->hPalette) {
179 unsigned i;
180 LOGPALETTE *pLogPal = (LOGPALETTE *) SAFE_SIZE_STRUCT_ALLOC(malloc,
181 sizeof(LOGPALETTE), sizeof(PALETTEENTRY), numColors);
182 if (!pLogPal) {
183 return;
184 }
185
186 pLogPal->palVersion = 0x300;
187 pLogPal->palNumEntries = (WORD) numColors;
188 for (i = 0; i < numColors; i++) {
189 pLogPal->palPalEntry[i].peRed = (BYTE)
190 QUAD_RED(splash->colorMap[i]);
191 pLogPal->palPalEntry[i].peGreen = (BYTE)
192 QUAD_GREEN(splash->colorMap[i]);
193 pLogPal->palPalEntry[i].peBlue = (BYTE)
194 QUAD_BLUE(splash->colorMap[i]);
195 pLogPal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
196 }
197 splash->hPalette = CreatePalette(pLogPal);
198 free(pLogPal);
199 }
200 if (splash->hPalette) {
201 hOldPal = SelectPalette(hdc, splash->hPalette, FALSE);
202 RealizePalette(hdc);
203 }
204
205 StretchDIBits(hdc, 0, 0, splash->width, splash->height, 0, 0,
206 splash->width, splash->height, splash->screenData,
207 (BITMAPINFO *) pBmi, DIB_RGB_COLORS, SRCCOPY);
208 if (hOldPal)
209 SelectPalette(hdc, hOldPal, FALSE);
210 }
211
212
213 /* The function makes the window visible if it is hidden
214 or is not yet shown. */
215 void
216 SplashRedrawWindow(Splash * splash)
217 {
218 if (!SplashIsStillLooping(splash)) {
219 KillTimer(splash->hWnd, 0);
220 }
221
222 if (splash->currentFrame < 0) {
223 return;
224 }
225
226 SplashUpdateScreenData(splash);
227 if (splash->isLayered) {
228 BLENDFUNCTION bf;
229 POINT ptSrc;
230 HDC hdcSrc = CreateCompatibleDC(NULL), hdcDst;
231 BITMAPINFOHEADER bmi;
232 void *bitmapBits;
233 HBITMAP hBitmap, hOldBitmap;
234 RECT rect;
235 POINT ptDst;
236 SIZE size;
237
238 bf.BlendOp = AC_SRC_OVER;
239 bf.BlendFlags = 0;
240 bf.AlphaFormat = AC_SRC_ALPHA;
241 bf.SourceConstantAlpha = 0xFF;
242 ptSrc.x = ptSrc.y = 0;
243
244 memset(&bmi, 0, sizeof(bmi));
245 bmi.biSize = sizeof(BITMAPINFOHEADER);
246 bmi.biWidth = splash->width;
247 bmi.biHeight = -splash->height;
248 bmi.biPlanes = 1;
249 bmi.biBitCount = 32;
250 bmi.biCompression = BI_RGB;
251
252 // FIXME: this is somewhat ineffective
253 // maybe if we allocate memory for all frames as DIBSections,
254 // then we could select the frames into the DC directly
255
256 hBitmap = CreateDIBSection(NULL, (BITMAPINFO *) & bmi, DIB_RGB_COLORS,
257 &bitmapBits, NULL, 0);
258 memcpy(bitmapBits, splash->screenData,
259 splash->screenStride * splash->height);
260 hOldBitmap = (HBITMAP) SelectObject(hdcSrc, hBitmap);
261 hdcDst = GetDC(splash->hWnd);
262
263 GetWindowRect(splash->hWnd, &rect);
264
265 ptDst.x = rect.left;
266 ptDst.y = rect.top;
267
268 size.cx = splash->width;
269 size.cy = splash->height;
270
271 UpdateLayeredWindow(splash->hWnd, hdcDst, &ptDst, &size,
272 hdcSrc, &ptSrc, 0, &bf, ULW_ALPHA);
273
274 ReleaseDC(splash->hWnd, hdcDst);
275 SelectObject(hdcSrc, hOldBitmap);
276 DeleteObject(hBitmap);
277 DeleteDC(hdcSrc);
278 }
279 else {
280 InvalidateRect(splash->hWnd, NULL, FALSE);
281 if (splash->maskRequired) {
282 HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
283
284 CombineRgn(hRgn, splash->frames[splash->currentFrame].hRgn,
285 splash->frames[splash->currentFrame].hRgn, RGN_COPY);
286 SetWindowRgn(splash->hWnd, hRgn, TRUE);
287 } else {
288 SetWindowRgn(splash->hWnd, NULL, TRUE);
289 }
290 UpdateWindow(splash->hWnd);
291 }
292 if (!IsWindowVisible(splash->hWnd)) {
293 POINT cursorPos;
294 ShowWindow(splash->hWnd, SW_SHOW);
295 // Windows won't update the cursor after the window is shown,
296 // if the cursor is already above the window. need to do this manually.
297 GetCursorPos(&cursorPos);
298 if (WindowFromPoint(cursorPos) == splash->hWnd) {
299 // unfortunately Windows fail to understand that the window
300 // thread should own the cursor, even though the mouse pointer
301 // is over the window, until the mouse has been moved.
302 // we're using SetCursorPos here to fake the mouse movement
303 // and enable proper update of the cursor.
304 SetCursorPos(cursorPos.x, cursorPos.y);
305 SetCursor(LoadCursor(NULL, IDC_WAIT));
306 }
307 }
308 if (SplashIsStillLooping(splash)) {
309 int time = splash->time +
310 splash->frames[splash->currentFrame].delay - SplashTime();
311
312 if (time < 0)
313 time = 0;
314 SetTimer(splash->hWnd, 0, time, NULL);
315 }
316 }
317
318 void SplashReconfigureNow(Splash * splash) {
319 splash->x = (GetSystemMetrics(SM_CXSCREEN) - splash->width) / 2;
320 splash->y = (GetSystemMetrics(SM_CYSCREEN) - splash->height) / 2;
321 if (splash->hWnd) {
322 //Fixed 6474657: splash screen image jumps towards left while
323 // setting the new image using setImageURL()
324 // We may safely hide the splash window because SplashRedrawWindow()
325 // will show the window again.
326 ShowWindow(splash->hWnd, SW_HIDE);
327 MoveWindow(splash->hWnd, splash->x, splash->y, splash->width, splash->height, FALSE);
328 }
329 SplashRedrawWindow(splash);
330 }
331
332 static LRESULT CALLBACK
333 SplashWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
334 {
335 PAINTSTRUCT ps;
336 HDC hdc;
337
338
339 switch (message) {
340
341 case WM_ERASEBKGND:
342 return TRUE; // to avoid flicker
343
344 case WM_SYSCOMMAND:
345 if (wParam==SC_CLOSE||wParam==SC_DEFAULT||wParam==SC_HOTKEY||
346 wParam==SC_KEYMENU||wParam==SC_MAXIMIZE||
347 wParam==SC_MINIMIZE||wParam==SC_MOUSEMENU||wParam==SC_MOVE||
348 wParam==SC_RESTORE||wParam==SC_SIZE)
349 {
350 return 0;
351 }
352
353 /* double switch to avoid prologue/epilogue duplication */
354 case WM_TIMER:
355 case WM_SPLASHUPDATE:
356 case WM_PAINT:
357 case WM_SPLASHRECONFIGURE:
358 {
359 Splash *splash = (Splash *) GetWindowLongPtr(hWnd, GWLP_USERDATA);
360
361 SplashLock(splash);
362 if (splash->isVisible>0) {
363 switch(message) {
364 case WM_TIMER:
365 SplashNextFrame(splash);
366 SplashRedrawWindow(splash);
367 break;
368 case WM_SPLASHUPDATE:
369 SplashRedrawWindow(splash);
370 break;
371 case WM_PAINT:
372 hdc = BeginPaint(hWnd, &ps);
373 SplashPaint(splash, hdc);
374 EndPaint(hWnd, &ps);
375 break;
376 case WM_SPLASHRECONFIGURE:
377 SplashReconfigureNow(splash);
378 break;
379 }
380 }
381 SplashUnlock(splash);
382 break;
383 }
384 case WM_DESTROY:
385 PostQuitMessage(0);
386 break;
387 default:
388 return DefWindowProc(hWnd, message, wParam, lParam);
389
390 }
391 return 0;
392 }
393
394 HWND
395 SplashCreateWindow(Splash * splash)
396 {
397 WNDCLASSEX wcex;
398 ATOM wndClass;
399 DWORD style, exStyle;
400 HWND hWnd;
401
402 ZeroMemory(&wcex, sizeof(WNDCLASSEX));
403
404 wcex.cbSize = sizeof(WNDCLASSEX);
405 wcex.style = CS_HREDRAW | CS_VREDRAW;
406 wcex.lpfnWndProc = (WNDPROC) SplashWndProc;
407 wcex.hInstance = GetModuleHandle(NULL);
408 wcex.lpszClassName = "JavaSplash";
409 wcex.hCursor = LoadCursor(NULL, IDC_WAIT);
410
411 wndClass = RegisterClassEx(&wcex);
412 if (!wndClass) {
413 return 0;
414 }
415
416 splash->x = (GetSystemMetrics(SM_CXSCREEN) - splash->width) / 2;
417 splash->y = (GetSystemMetrics(SM_CYSCREEN) - splash->height) / 2;
418 exStyle = splash->isLayered ? WS_EX_LAYERED : 0;
419 exStyle |= WS_EX_TOOLWINDOW; /* don't show the window on taskbar */
420 style = WS_POPUP;
421 hWnd = CreateWindowEx(exStyle, (LPCSTR) wndClass, "", style,
422 splash->x, splash->y, splash->width, splash->height, NULL, NULL,
423 wcex.hInstance, NULL);
424 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) splash);
425 return hWnd;
426 }
427
428 void
429 SplashLock(Splash * splash)
430 {
431 EnterCriticalSection(&splash->lock);
432 }
433
434 void
435 SplashUnlock(Splash * splash)
436 {
437 LeaveCriticalSection(&splash->lock);
438 }
439
440 void
441 SplashInitPlatform(Splash * splash)
442 {
443 HDC hdc;
444 int paletteMode;
445
446 InitializeCriticalSection(&splash->lock);
447 splash->isLayered = FALSE;
448 hdc = GetDC(NULL);
449 paletteMode = (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) != 0;
450 if (UpdateLayeredWindow && !paletteMode) {
451 splash->isLayered = TRUE;
452 }
453 splash->byteAlignment = 4;
454 if (splash->isLayered) {
455 initFormat(&splash->screenFormat,
456 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
457 splash->screenFormat.premultiplied = 1;
458 splash->maskRequired = 0;
459 }
460 else {
461 splash->maskRequired = 1;
462 if (paletteMode) {
463 int numColors = GetDeviceCaps(hdc, SIZEPALETTE) -
464 GetDeviceCaps(hdc, NUMRESERVED);
465 int i;
466 int numComponents[3];
467
468 initFormat(&splash->screenFormat, 0, 0, 0, 0);
469 /* FIXME: maybe remapping to non-reserved colors would improve performance */
470 for (i = 0; i < numColors; i++) {
471 splash->colorIndex[i] = i;
472 }
473 numColors = quantizeColors(numColors, numComponents);
474 initColorCube(numComponents, splash->colorMap, splash->dithers,
475 splash->colorIndex);
476 splash->screenFormat.colorIndex = splash->colorIndex;
477 splash->screenFormat.depthBytes = 1;
478 splash->screenFormat.colorMap = splash->colorMap;
479 splash->screenFormat.dithers = splash->dithers;
480 splash->screenFormat.numColors = numColors;
481 splash->hPalette = NULL;
482 }
483 else {
484 initFormat(&splash->screenFormat,
485 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
486 }
487 }
488 ReleaseDC(NULL, hdc);
489 }
490
491 void
492 SplashCleanupPlatform(Splash * splash)
493 {
494 int i;
495
496 if (splash->frames) {
497 for (i = 0; i < splash->frameCount; i++) {
498 if (splash->frames[i].hRgn) {
499 DeleteObject(splash->frames[i].hRgn);
500 splash->frames[i].hRgn = NULL;
501 }
502 }
503 }
504 if (splash->hPalette)
505 DeleteObject(splash->hPalette);
506 splash->maskRequired = !splash->isLayered;
507 }
508
509 void
510 SplashDonePlatform(Splash * splash)
511 {
512 if (splash->hWnd)
513 DestroyWindow(splash->hWnd);
514 }
515
516 void
517 SplashMessagePump()
518 {
519 MSG msg;
520
521 while (GetMessage(&msg, NULL, 0, 0)) {
522 TranslateMessage(&msg);
523 DispatchMessage(&msg);
524 }
525 }
526
527 DWORD WINAPI
528 SplashScreenThread(LPVOID param)
529 {
530 Splash *splash = (Splash *) param;
531
532 splash->currentFrame = 0;
533 SplashLock(splash);
534 splash->time = SplashTime();
535 splash->hWnd = SplashCreateWindow(splash);
536 if (splash->hWnd) {
537 SplashRedrawWindow(splash);
538 //map the splash co-ordinates as per system scale
539 splash->x /= splash->scaleFactor;
540 splash->y /= splash->scaleFactor;
541 SplashUnlock(splash);
542 SplashMessagePump();
543 SplashLock(splash);
544 }
545 SplashDone(splash);
546 splash->isVisible = -1;
547 SplashUnlock(splash);
548 return 0;
549 }
550
551 void
552 SplashCreateThread(Splash * splash)
553 {
554 DWORD threadId;
555
556 CreateThread(NULL, 0, SplashScreenThread, (LPVOID) splash, 0, &threadId);
557 }
558
559 void
560 SplashClosePlatform(Splash * splash)
561 {
562 PostMessage(splash->hWnd, WM_QUIT, 0, 0);
563 }
564
565 void
566 SplashUpdate(Splash * splash)
567 {
568 PostMessage(splash->hWnd, WM_SPLASHUPDATE, 0, 0);
569 }
570
571 void
572 SplashReconfigure(Splash * splash)
573 {
574 PostMessage(splash->hWnd, WM_SPLASHRECONFIGURE, 0, 0);
575 }
576
577 jboolean
578 SplashGetScaledImageName(const char* jarName, const char* fileName,
579 float *scaleFactor, char *scaleImageName,
580 const size_t scaledImageLength)
581 {
582 float dpiScaleX = -1.0f;
583 float dpiScaleY = -1.0f;
584 FILE *fp = NULL;
585 *scaleFactor = 1.0;
586 GetScreenDpi(getPrimaryMonitor(), &dpiScaleX, &dpiScaleY);
587 *scaleFactor = dpiScaleX > 0 ? dpiScaleX / 96 : *scaleFactor;
588 return GetScaledImageName(fileName, scaleImageName,
589 scaleFactor, scaledImageLength);
590
591 }
592