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 */
64char* 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
90unsigned
91SplashTime(void)
92{
93    return GetTickCount();
94}
95
96void
97SplashInitFrameShape(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
140void
141SplashPaint(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. */
215void
216SplashRedrawWindow(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
318void 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
332static LRESULT CALLBACK
333SplashWndProc(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
394HWND
395SplashCreateWindow(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
428void
429SplashLock(Splash * splash)
430{
431    EnterCriticalSection(&splash->lock);
432}
433
434void
435SplashUnlock(Splash * splash)
436{
437    LeaveCriticalSection(&splash->lock);
438}
439
440void
441SplashInitPlatform(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
491void
492SplashCleanupPlatform(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
509void
510SplashDonePlatform(Splash * splash)
511{
512    if (splash->hWnd)
513        DestroyWindow(splash->hWnd);
514}
515
516void
517SplashMessagePump()
518{
519    MSG msg;
520
521    while (GetMessage(&msg, NULL, 0, 0)) {
522        TranslateMessage(&msg);
523        DispatchMessage(&msg);
524    }
525}
526
527DWORD WINAPI
528SplashScreenThread(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
551void
552SplashCreateThread(Splash * splash)
553{
554    DWORD threadId;
555
556    CreateThread(NULL, 0, SplashScreenThread, (LPVOID) splash, 0, &threadId);
557}
558
559void
560SplashClosePlatform(Splash * splash)
561{
562    PostMessage(splash->hWnd, WM_QUIT, 0, 0);
563}
564
565void
566SplashUpdate(Splash * splash)
567{
568    PostMessage(splash->hWnd, WM_SPLASHUPDATE, 0, 0);
569}
570
571void
572SplashReconfigure(Splash * splash)
573{
574    PostMessage(splash->hWnd, WM_SPLASHRECONFIGURE, 0, 0);
575}
576
577jboolean
578SplashGetScaledImageName(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