360 lines
9.8 KiB
C++
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;
|
|
}
|