1 /*
   2  * Copyright (c) 2005, 2013, 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     SplashUpdateScreenData(splash);
 217     if (splash->isLayered) {
 218         BLENDFUNCTION bf;
 219         POINT ptSrc;
 220         HDC hdcSrc = CreateCompatibleDC(NULL), hdcDst;
 221         BITMAPINFOHEADER bmi;
 222         void *bitmapBits;
 223         HBITMAP hBitmap, hOldBitmap;
 224         RECT rect;
 225         POINT ptDst;
 226         SIZE size;
 227 
 228         bf.BlendOp = AC_SRC_OVER;
 229         bf.BlendFlags = 0;
 230         bf.AlphaFormat = AC_SRC_ALPHA;
 231         bf.SourceConstantAlpha = 0xFF;
 232         ptSrc.x = ptSrc.y = 0;
 233 
 234         memset(&bmi, 0, sizeof(bmi));
 235         bmi.biSize = sizeof(BITMAPINFOHEADER);
 236         bmi.biWidth = splash->width;
 237         bmi.biHeight = -splash->height;
 238         bmi.biPlanes = 1;
 239         bmi.biBitCount = 32;
 240         bmi.biCompression = BI_RGB;
 241 
 242         //      FIXME: this is somewhat ineffective
 243         //      maybe if we allocate memory for all frames as DIBSections,
 244         //      then we could select the frames into the DC directly
 245 
 246         hBitmap = CreateDIBSection(NULL, (BITMAPINFO *) & bmi, DIB_RGB_COLORS,
 247                 &bitmapBits, NULL, 0);
 248         memcpy(bitmapBits, splash->screenData,
 249                 splash->screenStride * splash->height);
 250         hOldBitmap = (HBITMAP) SelectObject(hdcSrc, hBitmap);
 251         hdcDst = GetDC(splash->hWnd);
 252 
 253         GetWindowRect(splash->hWnd, &rect);
 254 
 255         ptDst.x = rect.left;
 256         ptDst.y = rect.top;
 257 
 258         size.cx = splash->width;
 259         size.cy = splash->height;
 260 
 261         UpdateLayeredWindow(splash->hWnd, hdcDst, &ptDst, &size,
 262                 hdcSrc, &ptSrc, 0, &bf, ULW_ALPHA);
 263 
 264         ReleaseDC(splash->hWnd, hdcDst);
 265         SelectObject(hdcSrc, hOldBitmap);
 266         DeleteObject(hBitmap);
 267         DeleteDC(hdcSrc);
 268     }
 269     else {
 270        InvalidateRect(splash->hWnd, NULL, FALSE);
 271        if (splash->maskRequired) {
 272             HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
 273 
 274             CombineRgn(hRgn, splash->frames[splash->currentFrame].hRgn,
 275                     splash->frames[splash->currentFrame].hRgn, RGN_COPY);
 276             SetWindowRgn(splash->hWnd, hRgn, TRUE);
 277         } else {
 278             SetWindowRgn(splash->hWnd, NULL, TRUE);
 279         }
 280         UpdateWindow(splash->hWnd);
 281     }
 282     if (!IsWindowVisible(splash->hWnd)) {
 283         POINT cursorPos;
 284         ShowWindow(splash->hWnd, SW_SHOW);
 285         // Windows won't update the cursor after the window is shown,
 286         // if the cursor is already above the window. need to do this manually.
 287         GetCursorPos(&cursorPos);
 288         if (WindowFromPoint(cursorPos) == splash->hWnd) {
 289             // unfortunately Windows fail to understand that the window
 290             // thread should own the cursor, even though the mouse pointer
 291             // is over the window, until the mouse has been moved.
 292             // we're using SetCursorPos here to fake the mouse movement
 293             // and enable proper update of the cursor.
 294             SetCursorPos(cursorPos.x, cursorPos.y);
 295             SetCursor(LoadCursor(NULL, IDC_WAIT));
 296         }
 297     }
 298     if (SplashIsStillLooping(splash)) {
 299         int time = splash->time +
 300             splash->frames[splash->currentFrame].delay - SplashTime();
 301 
 302         if (time < 0)
 303             time = 0;
 304         SetTimer(splash->hWnd, 0, time, NULL);
 305     }
 306     else {
 307         KillTimer(splash->hWnd, 0);
 308     }
 309 }
 310 
 311 void SplashReconfigureNow(Splash * splash) {
 312     splash->x = (GetSystemMetrics(SM_CXSCREEN) - splash->width) / 2;
 313     splash->y = (GetSystemMetrics(SM_CYSCREEN) - splash->height) / 2;
 314     if (splash->hWnd) {
 315         //Fixed 6474657: splash screen image jumps towards left while
 316         //    setting the new image using setImageURL()
 317         // We may safely hide the splash window because SplashRedrawWindow()
 318         //    will show the window again.
 319         ShowWindow(splash->hWnd, SW_HIDE);
 320         MoveWindow(splash->hWnd, splash->x, splash->y, splash->width, splash->height, FALSE);
 321     }
 322     SplashRedrawWindow(splash);
 323 }
 324 
 325 static LRESULT CALLBACK
 326 SplashWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 327 {
 328     PAINTSTRUCT ps;
 329     HDC hdc;
 330 
 331 
 332     switch (message) {
 333 
 334     case WM_ERASEBKGND:
 335         return TRUE;            // to avoid flicker
 336 
 337     case WM_SYSCOMMAND:
 338         if (wParam==SC_CLOSE||wParam==SC_DEFAULT||wParam==SC_HOTKEY||
 339             wParam==SC_KEYMENU||wParam==SC_MAXIMIZE||
 340             wParam==SC_MINIMIZE||wParam==SC_MOUSEMENU||wParam==SC_MOVE||
 341             wParam==SC_RESTORE||wParam==SC_SIZE)
 342         {
 343             return 0;
 344         }
 345 
 346     /* double switch to avoid prologue/epilogue duplication */
 347     case WM_TIMER:
 348     case WM_SPLASHUPDATE:
 349     case WM_PAINT:
 350     case WM_SPLASHRECONFIGURE:
 351         {
 352             Splash *splash = (Splash *) GetWindowLongPtr(hWnd, GWLP_USERDATA);
 353 
 354             SplashLock(splash);
 355             if (splash->isVisible>0) {
 356                 switch(message) {
 357                 case WM_TIMER:
 358                     SplashNextFrame(splash);
 359                     SplashRedrawWindow(splash);
 360                     break;
 361                 case WM_SPLASHUPDATE:
 362                     SplashRedrawWindow(splash);
 363                     break;
 364                 case WM_PAINT:
 365                     hdc = BeginPaint(hWnd, &ps);
 366                     SplashPaint(splash, hdc);
 367                     EndPaint(hWnd, &ps);
 368                     break;
 369                 case WM_SPLASHRECONFIGURE:
 370                     SplashReconfigureNow(splash);
 371                     break;
 372                 }
 373             }
 374             SplashUnlock(splash);
 375             break;
 376         }
 377     case WM_DESTROY:
 378         PostQuitMessage(0);
 379         break;
 380     default:
 381         return DefWindowProc(hWnd, message, wParam, lParam);
 382 
 383     }
 384     return 0;
 385 }
 386 
 387 HWND
 388 SplashCreateWindow(Splash * splash)
 389 {
 390     WNDCLASSEX wcex;
 391     ATOM wndClass;
 392     DWORD style, exStyle;
 393     HWND hWnd;
 394 
 395     ZeroMemory(&wcex, sizeof(WNDCLASSEX));
 396 
 397     wcex.cbSize = sizeof(WNDCLASSEX);
 398     wcex.style = CS_HREDRAW | CS_VREDRAW;
 399     wcex.lpfnWndProc = (WNDPROC) SplashWndProc;
 400     wcex.hInstance = GetModuleHandle(NULL);
 401     wcex.lpszClassName = "JavaSplash";
 402     wcex.hCursor = LoadCursor(NULL, IDC_WAIT);
 403 
 404     wndClass = RegisterClassEx(&wcex);
 405     if (!wndClass) {
 406         return 0;
 407     }
 408 
 409     splash->x = (GetSystemMetrics(SM_CXSCREEN) - splash->width) / 2;
 410     splash->y = (GetSystemMetrics(SM_CYSCREEN) - splash->height) / 2;
 411     exStyle = splash->isLayered ? WS_EX_LAYERED : 0;
 412     exStyle |= WS_EX_TOOLWINDOW;        /* don't show the window on taskbar */
 413     style = WS_POPUP;
 414     hWnd = CreateWindowEx(exStyle, (LPCSTR) wndClass, "", style,
 415             splash->x, splash->y, splash->width, splash->height, NULL, NULL,
 416             wcex.hInstance, NULL);
 417     SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) splash);
 418     return hWnd;
 419 }
 420 
 421 void
 422 SplashLock(Splash * splash)
 423 {
 424     EnterCriticalSection(&splash->lock);
 425 }
 426 
 427 void
 428 SplashUnlock(Splash * splash)
 429 {
 430     LeaveCriticalSection(&splash->lock);
 431 }
 432 
 433 void
 434 SplashInitPlatform(Splash * splash)
 435 {
 436     HDC hdc;
 437     int paletteMode;
 438 
 439     InitializeCriticalSection(&splash->lock);
 440     splash->isLayered = FALSE;
 441     hdc = GetDC(NULL);
 442     paletteMode = (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) != 0;
 443     if (UpdateLayeredWindow && !paletteMode) {
 444         splash->isLayered = TRUE;
 445     }
 446     splash->byteAlignment = 4;
 447     if (splash->isLayered) {
 448         initFormat(&splash->screenFormat,
 449                 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
 450         splash->screenFormat.premultiplied = 1;
 451         splash->maskRequired = 0;
 452     }
 453     else {
 454         splash->maskRequired = 1;
 455         if (paletteMode) {
 456             int numColors = GetDeviceCaps(hdc, SIZEPALETTE) -
 457                 GetDeviceCaps(hdc, NUMRESERVED);
 458             int i;
 459             int numComponents[3];
 460 
 461             initFormat(&splash->screenFormat, 0, 0, 0, 0);
 462             /*      FIXME: maybe remapping to non-reserved colors would improve performance */
 463             for (i = 0; i < numColors; i++) {
 464                 splash->colorIndex[i] = i;
 465             }
 466             numColors = quantizeColors(numColors, numComponents);
 467             initColorCube(numComponents, splash->colorMap, splash->dithers,
 468                     splash->colorIndex);
 469             splash->screenFormat.colorIndex = splash->colorIndex;
 470             splash->screenFormat.depthBytes = 1;
 471             splash->screenFormat.colorMap = splash->colorMap;
 472             splash->screenFormat.dithers = splash->dithers;
 473             splash->screenFormat.numColors = numColors;
 474             splash->hPalette = NULL;
 475         }
 476         else {
 477             initFormat(&splash->screenFormat,
 478                     0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
 479         }
 480     }
 481     ReleaseDC(NULL, hdc);
 482 }
 483 
 484 void
 485 SplashCleanupPlatform(Splash * splash)
 486 {
 487     int i;
 488 
 489     if (splash->frames) {
 490         for (i = 0; i < splash->frameCount; i++) {
 491             if (splash->frames[i].hRgn) {
 492                 DeleteObject(splash->frames[i].hRgn);
 493                 splash->frames[i].hRgn = NULL;
 494             }
 495         }
 496     }
 497     if (splash->hPalette)
 498         DeleteObject(splash->hPalette);
 499     splash->maskRequired = !splash->isLayered;
 500 }
 501 
 502 void
 503 SplashDonePlatform(Splash * splash)
 504 {
 505     if (splash->hWnd)
 506         DestroyWindow(splash->hWnd);
 507 }
 508 
 509 void
 510 SplashMessagePump()
 511 {
 512     MSG msg;
 513 
 514     while (GetMessage(&msg, NULL, 0, 0)) {
 515         TranslateMessage(&msg);
 516         DispatchMessage(&msg);
 517     }
 518 }
 519 
 520 DWORD WINAPI
 521 SplashScreenThread(LPVOID param)
 522 {
 523     Splash *splash = (Splash *) param;
 524 
 525     splash->currentFrame = 0;
 526     SplashLock(splash);
 527     splash->time = SplashTime();
 528     splash->hWnd = SplashCreateWindow(splash);
 529     if (splash->hWnd) {
 530         SplashRedrawWindow(splash);
 531         SplashUnlock(splash);
 532         SplashMessagePump();
 533         SplashLock(splash);
 534     }
 535     SplashDone(splash);
 536     splash->isVisible = -1;
 537     SplashUnlock(splash);
 538     return 0;
 539 }
 540 
 541 void
 542 SplashCreateThread(Splash * splash)
 543 {
 544     DWORD threadId;
 545 
 546     CreateThread(NULL, 0, SplashScreenThread, (LPVOID) splash, 0, &threadId);
 547 }
 548 
 549 void
 550 SplashClosePlatform(Splash * splash)
 551 {
 552     PostMessage(splash->hWnd, WM_QUIT, 0, 0);
 553 }
 554 
 555 void
 556 SplashUpdate(Splash * splash)
 557 {
 558     PostMessage(splash->hWnd, WM_SPLASHUPDATE, 0, 0);
 559 }
 560 
 561 void
 562 SplashReconfigure(Splash * splash)
 563 {
 564     PostMessage(splash->hWnd, WM_SPLASHRECONFIGURE, 0, 0);
 565 }