CelestiaContent/src/celestia/win32/winsplash.cpp

360 lines
9.8 KiB
C++

// winsplash.cpp
//
// Copyright (C) 2005, Chris Laurel <claurel@shatters.net>
//
// Win32 splash window
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
#include "winsplash.h"
#include <celutil/util.h>
#include <celutil/winutil.h>
#include <string>
#include <winuser.h>
#include <commctrl.h>
#include "res/resource.h"
#include <iostream>
using namespace std;
// Required for transparent Windows, but not present in VC6 headers. Only present
// on Win2k or later.
typedef BOOL (WINAPI *lpfnSetLayeredWindowAttributes)
(HWND hWnd, COLORREF cr, BYTE bAlpha, DWORD dwFlags);
typedef BOOL (WINAPI *lpfnUpdateLayeredWindow)
(HWND hwnd, HDC hdcDst, POINT* pptDst, SIZE* psize,
HDC hdcSrc, POINT* pptSrc, COLORREF crKey, BLENDFUNCTION* pblend,
DWORD dwFlags);
lpfnSetLayeredWindowAttributes winSetLayeredWindowAttributes = NULL;
lpfnUpdateLayeredWindow winUpdateLayeredWindow = NULL;
#define WS_EX_LAYERED 0x00080000
static LRESULT CALLBACK SplashWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static SplashWindow *splash = NULL;
if (uMsg == WM_CREATE)
{
splash = reinterpret_cast<SplashWindow*>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);
}
if (splash)
return splash->windowProc(hwnd, uMsg, wParam, lParam);
else
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
SplashWindow::SplashWindow(const string& _imageFileName) :
hwnd(NULL),
className(NULL),
imageFileName(_imageFileName),
image(NULL),
hBitmap(0),
hCompositionBitmap(0),
useLayeredWindow(false),
winWidth(640),
winHeight(480)
{
init();
}
SplashWindow::~SplashWindow()
{
if (hBitmap)
::DeleteObject(hBitmap);
if (hCompositionBitmap)
::DeleteObject(hCompositionBitmap);
}
LRESULT CALLBACK
SplashWindow::windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_PAINT:
if (!useLayeredWindow)
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hwnd, &ps);
paint(hDC);
EndPaint(hwnd, &ps);
}
return TRUE;
}
return DefWindowProc (hwnd, uMsg, wParam, lParam) ;
}
void
SplashWindow::paint(HDC hDC)
{
RECT rect;
::GetClientRect(hwnd, &rect);
if (hBitmap)
{
// Display the splash image
HDC hMemDC = ::CreateCompatibleDC(hDC);
HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(hMemDC, hBitmap);
BitBlt(hDC, 0, 0, winWidth, winHeight, hMemDC, 0, 0, SRCCOPY);
::SelectObject(hMemDC, hOldBitmap);
::DeleteDC(hMemDC);
SetTextColor(hDC, RGB(255, 255, 255));
SetBkMode(hDC, TRANSPARENT);
}
else
{
// If the splash image couldn't be loaded, just paint a black background
HBRUSH hbrush = CreateSolidBrush(RGB(0, 0, 0));
FillRect(hDC, &rect, hbrush);
DeleteObject(hbrush);
}
// Show the message text
RECT r;
r.left = rect.right - 250;
r.top = rect.bottom - 70;
r.right = rect.right;
r.bottom = r.top + 30;
HFONT hFont = reinterpret_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT));
SelectObject(hDC, hFont);
// string s;
// s += UTF8ToCurrentCP(_("Version: "));
string text = UTF8ToCurrentCP(_("Version: ")) + string(VERSION_STRING "\n") + message;
DrawText(hDC, text.c_str(), text.length(), &r, DT_LEFT | DT_VCENTER);
}
void
SplashWindow::init()
{
hwnd = NULL;
className = TEXT("CELSPLASH");
// Get the function pointer for SetLayeredWindowAttributes
HMODULE user32 = GetModuleHandle(TEXT("USER32.DLL"));
winSetLayeredWindowAttributes = (lpfnSetLayeredWindowAttributes) GetProcAddress(user32, "SetLayeredWindowAttributes");
winUpdateLayeredWindow = (lpfnUpdateLayeredWindow) GetProcAddress(user32, "UpdateLayeredWindow");
if (winSetLayeredWindowAttributes != NULL && winUpdateLayeredWindow != NULL)
useLayeredWindow = true;
image = LoadImageFromFile(imageFileName);
}
void
SplashWindow::updateWindow()
{
// If we're using layered windows, draw into the compositing bitmap and use it to
// as the source for UpdateLayeredWindow. Otherwise, we use the traditional approach
// and send a paint message to the window.
if (useLayeredWindow)
{
HDC hwndDC = GetDC(hwnd);
HDC hDC = CreateCompatibleDC(hwndDC);
HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(hDC, hCompositionBitmap);
paint(hDC);
SIZE size;
POINT origin = { 0, 0 };
size.cx = winWidth;
size.cy = winHeight;
BLENDFUNCTION blend;
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.AlphaFormat = AC_SRC_ALPHA;
blend.SourceConstantAlpha = 0xff;
winUpdateLayeredWindow(hwnd, hwndDC, NULL, &size, hDC, &origin, 0, &blend, ULW_ALPHA);
::SelectObject(hDC, hOldBitmap);
::DeleteDC(hDC);
}
::UpdateWindow(hwnd);
}
HWND
SplashWindow::createWindow()
{
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof (wndclass);
wndclass.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW;
wndclass.lpfnWndProc = SplashWndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = DLGWINDOWEXTRA;
wndclass.hInstance = ::GetModuleHandle(NULL);
wndclass.hIcon = NULL;
wndclass.hCursor = ::LoadCursor( NULL, IDC_WAIT );
wndclass.hbrBackground = (HBRUSH)::GetStockObject(LTGRAY_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = className;
wndclass.hIconSm = NULL;
if (!RegisterClassEx(&wndclass))
return NULL;
if (image != NULL)
{
winWidth = image->getWidth();
winHeight = image->getHeight();
}
// Create the application window centered in the middle of the screen
DWORD nScrWidth = ::GetSystemMetrics(SM_CXFULLSCREEN);
DWORD nScrHeight = ::GetSystemMetrics(SM_CYFULLSCREEN);
int x = (nScrWidth - winWidth) / 2;
int y = (nScrHeight - winHeight) / 2;
hwnd = ::CreateWindowEx(WS_EX_TOOLWINDOW, className,
TEXT("Banner"), WS_POPUP, x, y,
winWidth, winHeight, NULL, NULL, NULL, this);
if (hwnd)
{
createBitmap();
// If this version of Windows supports layered windows, set the window
// style to layered.
if (useLayeredWindow)
{
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
}
ShowWindow(hwnd, SW_SHOW);
updateWindow();
}
delete image;
image = NULL;
return hwnd;
}
bool
SplashWindow::createBitmap()
{
if (image != NULL)
{
BITMAPINFO bmi;
ZeroMemory(&bmi, sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = image->getWidth();
bmi.bmiHeader.biHeight = image->getHeight();
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = image->getWidth() * image->getHeight() * 4;
HDC hwndDC = GetDC(hwnd);
HDC hDC = CreateCompatibleDC(hwndDC);
void* bmPixels = NULL;
// create our DIB section and select the bitmap into the dc
hBitmap = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, &bmPixels, NULL, 0x0);
// Windows bitmaps are upside-down and the order of the color channels is different
// than for a PNG. When copying pixels to the bitmap, we remap the color channels and
// flip the image vertically.
if (bmPixels != NULL)
{
unsigned char* src = image->getPixels();
unsigned char* dst = reinterpret_cast<unsigned char*>(bmPixels);
for (unsigned int i = 0; i < (unsigned int) image->getHeight(); i++)
{
for (unsigned int j = 0; j < (unsigned int) image->getWidth(); j++)
{
unsigned char* srcPix = &src[4 * (image->getWidth() * (image->getHeight() - i - 1) + j)];
unsigned char* dstPix = &dst[4 * (image->getWidth() * i + j)];
// Windows requires that we premultiply by alpha
unsigned int alpha = srcPix[3];
dstPix[0] = (unsigned char) ((srcPix[2] * alpha) / 255);
dstPix[1] = (unsigned char) ((srcPix[1] * alpha) / 255);
dstPix[2] = (unsigned char) ((srcPix[0] * alpha) / 255);
dstPix[3] = srcPix[3];
}
}
}
DeleteDC(hDC);
// Create the composition bitmap (always created, though it's only used when we've got
// layered window support.)
if (hBitmap)
{
hCompositionBitmap = CreateCompatibleBitmap(hwndDC, image->getWidth(), image->getHeight());
}
}
return hBitmap != 0 && hCompositionBitmap != 0;
}
int SplashWindow::messageLoop()
{
if (!hwnd)
showSplash();
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam ;
}
// Redraw the window with a new text message
void SplashWindow::setMessage(const string& msg)
{
message = msg;
InvalidateRect(hwnd, NULL, FALSE);
updateWindow();
}
void SplashWindow::showSplash()
{
close();
createWindow();
}
int SplashWindow::close()
{
if (hwnd)
{
DestroyWindow(hwnd);
hwnd = 0;
UnregisterClass(className, ::GetModuleHandle(NULL));
return 1;
}
return 0;
}