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