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 // 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 int
 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     return 1;
 490 }
 491 
 492 void
 493 SplashCleanupPlatform(Splash * splash)
 494 {
 495     int i;
 496 
 497     if (splash->frames) {
 498         for (i = 0; i < splash->frameCount; i++) {
 499             if (splash->frames[i].hRgn) {
 500                 DeleteObject(splash->frames[i].hRgn);
 501                 splash->frames[i].hRgn = NULL;
 502             }
 503         }
 504     }
 505     if (splash->hPalette)
 506         DeleteObject(splash->hPalette);
 507     splash->maskRequired = !splash->isLayered;
 508 }
 509 
 510 void
 511 SplashDonePlatform(Splash * splash)
 512 {
 513     if (splash->hWnd)
 514         DestroyWindow(splash->hWnd);
 515 }
 516 
 517 void
 518 SplashMessagePump()
 519 {
 520     MSG msg;
 521 
 522     while (GetMessage(&msg, NULL, 0, 0)) {
 523         TranslateMessage(&msg);
 524         DispatchMessage(&msg);
 525     }
 526 }
 527 
 528 DWORD WINAPI
 529 SplashScreenThread(LPVOID param)
 530 {
 531     Splash *splash = (Splash *) param;
 532 
 533     splash->currentFrame = 0;
 534     SplashLock(splash);
 535     splash->time = SplashTime();
 536     splash->hWnd = SplashCreateWindow(splash);
 537     if (splash->hWnd) {
 538         SplashRedrawWindow(splash);
 539         //map the splash co-ordinates as per system scale
 540         splash->x /= splash->scaleFactor;
 541         splash->y /= splash->scaleFactor;
 542         SplashUnlock(splash);
 543         SplashMessagePump();
 544         SplashLock(splash);
 545     }
 546     SplashDone(splash);
 547     splash->isVisible = -1;
 548     SplashUnlock(splash);
 549     return 0;
 550 }
 551 
 552 void
 553 SplashCreateThread(Splash * splash)
 554 {
 555     DWORD threadId;
 556 
 557     CreateThread(NULL, 0, SplashScreenThread, (LPVOID) splash, 0, &threadId);
 558 }
 559 
 560 void
 561 SplashClosePlatform(Splash * splash)
 562 {
 563     PostMessage(splash->hWnd, WM_QUIT, 0, 0);
 564 }
 565 
 566 void
 567 SplashUpdate(Splash * splash)
 568 {
 569     PostMessage(splash->hWnd, WM_SPLASHUPDATE, 0, 0);
 570 }
 571 
 572 void
 573 SplashReconfigure(Splash * splash)
 574 {
 575     PostMessage(splash->hWnd, WM_SPLASHRECONFIGURE, 0, 0);
 576 }
 577 
 578 JNIEXPORT jboolean
 579 SplashGetScaledImageName(const char* jarName, const char* fileName,
 580                            float *scaleFactor, char *scaleImageName,
 581                            const size_t scaledImageLength)
 582 {
 583     float dpiScaleX = -1.0f;
 584     float dpiScaleY = -1.0f;
 585     FILE *fp = NULL;
 586     *scaleFactor = 1.0;
 587     GetScreenDpi(getPrimaryMonitor(), &dpiScaleX, &dpiScaleY);
 588     *scaleFactor = dpiScaleX > 0 ? dpiScaleX / 96 : *scaleFactor;
 589     return GetScaledImageName(fileName, scaleImageName,
 590         scaleFactor, scaledImageLength);
 591 }
 592