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