[gameprogrammer] Re: Overlay on windows and mac
- From: "Kevin Fields" <drunkendruid@xxxxxxxxxxx>
- To: gameprogrammer@xxxxxxxxxxxxx
- Date: Tue, 31 Aug 2004 15:24:27 -0300
This is a rather lengthy email that was sent to me, and, I'm afraid, it's
mostly the theory behind creating a "skinned window" using Windows code.
There are some code snippets in it that may also be of some help. However,
it might give people an idea of how they could go about writing their own
"skinned window" system... whether it's within a game, or for Windows.
---- BEGIN ----
Right then here we go,
I'll go ahead and discuss the way to create a sizeable window. First of
all you will need to create the graphics for you window. You will
require a top left corner, top right, bottom left and bottom right (as
if you didn?t already know that :) ). Then you need a sample for each
side. When I say sample I mean a section of the border that will be
repeated (Unless you are wanting too create a non square window, and
this is where the problems really begin). And after that you will
require your Close, Min and Max buttons (pressed states also if you want
them too look like they are being pressed). The messages that we are
going to intercept are the following:-
WM_NCCALCSIZE
WM_NCPAINT - this is the one that I will explain in detail
WM_NCHITTEST
WM_NCACTIVATE
WM_NCLBUTTONDOWN
WM_NCLBUTTONUP
WM_NCLBUTTONDBLCLK
There are a few others but these are the most important.
First we will discuss the WM_NCPAINT message and WM_NCACTIVATE
(WM_NCACTIVATE would be the same as WM_NCPAINT except that you would
need to draw the caption bar's current state if you wanted the window to
show active and non active states). This message is the main message
where you will see the changes in your window. We don?t really need to
use the lParam and wParam, this message is just to notify us that we
need to paint the non-client area. If you really want to go into a good
skin then the lParam is a handle to the update region of the window (I
just draw the whole window again, it's easier). Is this message you
will need to use the following API's to draw the borders and caption
bar.
GetWindowDC
GetWindowRect
OffsetRect
CreateCompatibleDC
SelectObject
DeleteObject
DeleteDC
BitBlt
LoadBitmap
The following API's are if you want text to be displayed in the caption
bar
CreateFontIndirect
SetBkMode
SetTextColor
TextOut
First of all you need to use GetWindowDC to get the DC for the window.
The reason that we need GetWindowDC rather than GetDC is that GetDC only
returns the Client DC rather that the full window which means that we
wont get access to the borders.
Then we will need to use GetWindowRect and OffsetRect to get the size of
the window. GetWindowRect will give us the coordinates to the window
relative to the screen, and then we can use OffsetRect to actually get
the size of the window.
Once this has been done we need to use CreateCompatibleDC that is
compatible with the Window DC so that we can load our bitmap with the
borders, caption bar, etc.... (I have all my borders, caption's, etc...
in one file rather than loading them all separately and they are also
held in the resource file rather than off disk.) This next step can be
done in two places. You can either
a. Use LoadBitmap at the start of your program and load the bitmap into
a global var and the use DeleteObject at the end of the program to free
up resources (this is what I use)
or
b. Use LoadBitmap and DeleteObject in the WM_NCPAINT message every
time.
Then we need to select the bitmap into our newly created DC using
SelectObject. Once we have got this far we can then begin the painting.
Using BitBlt we can paint the 4 corners first. Then we need to create
two loops, one for the horizontal borders and the second for the
vertical borders both are identical except for the fact that one is for
the horizontal position and the other is for the vertical position.
Once this is complete you will have your full border and you can then go
on and draw the caption text and the Min, Max and Close buttons. Here
is an example of a simple WM_NCPAINT.
[code]
HDC windowHDC;
HDC memDC;
HDC offscreenDC;
tagRECT windowRect;
void *oldObject;
HFONT captionFont;
LOGFONT logFont;
windowHDC = GetWindowDC(hWnd);
/************************
* Create Font For use On Caption Bar *
*
*
************************/
logFont.lfHeight = 14;
logFont.lfWidth = 0;
logFont.lfEscapement = 0;
logFont.lfOrientation = 0;
logFont.lfWeight = 500;
logFont.lfItalic = 0;
logFont.lfUnderline = 0;
logFont.lfStrikeOut = 0;
logFont.lfCharSet = ANSI_CHARSET;
logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
logFont.lfQuality = PROOF_QUALITY;
logFont.lfPitchAndFamily = VARIABLE_PITCH |
FF_DECORATIVE;
strcpy(logFont.lfFaceName, "Arial");
//* Create Font and Frame DC's ready for use *//
//*********************************//
captionFont = CreateFontIndirect(&logFont);
oldObject = SelectObject(windowHDC,captionFont);
GetWindowRect(hWnd,&windowRect);
OffsetRect(&windowRect,
-windowRect.left,-windowRect.top);
memDC = CreateCompatibleDC(windowHDC);
DeleteObject(SelectObject(memDC,borderBitmap));
//* Blt the coners for the window border
*//
//*********************************//
BitBlt(windowHDC,0,0,3,18,memDC,7,3,SRCCOPY);
//top left
BitBlt(windowHDC,windowRect.right -
3,0,3,18,memDC,0,3,SRCCOPY); //top right
BitBlt(windowHDC, 0,windowRect.bottom -
2,2,2,memDC,3,0,SRCCOPY); //bottom left
BitBlt(windowHDC, windowRect.right - 2,
windowRect.bottom - 2,2,2,memDC,9,0,SRCCOPY); //bottom right
//* Offset the Top and Left starting position
*//
//*********************************//
long lTop = 18; //my caption bar height is 18
pixels
long lLeft = 2; //my corner width's are 2 pixels
//* Loop for the rest of the height of the
*//
//* frame and Blt the left and right sides
*//
//**********************************//
do{
if(lTop < windowRect.bottom - 2)
{
BitBlt(windowHDC, 0 ,lTop, 2,2,memDC,0,0,SRCCOPY);
BitBlt(windowHDC,windowRect.right - 2,
lTop, 2,2,memDC,12,0 ,SRCCOPY);
}
lTop+=2;
}while(lTop < windowRect.bottom);
//* Same as left and right but for top and
bottom*//
//************************************//
do{
if(lLeft < windowRect.right - 2)
{
BitBlt(windowHDC, lLeft,
windowRect.bottom - 2, 2,2, memDC, 6,0 ,SRCCOPY);
BitBlt(windowHDC, lLeft, 0, 2, 18,
memDC, 4,3 ,SRCCOPY);
}
lLeft+=2;
}while(lLeft < windowRect.right);
//* Now draw caption in the caption bar*//
//*****************************//
COLORREF captionColor = RGB(255,255,255);
//white
TCHAR szCaption[MAX_LOADSTRING];
LoadString(hInst, IDS_APP_TITLE, szCaption,
MAX_LOADSTRING);
SetBkMode(windowHDC,TRANSPARENT);
SetTextColor(windowHDC,captionColor);
TextOut(windowHDC,20,2,szCaption,strlen(szCaption));
SelectObject(windowHDC,oldObject);
DeleteObject(captionFont);
//*Now draw the Max, Min and Close Buttons*//
//**********************************//
BitBlt(windowHDC,windowRect.right - 17,
2,14,14,memDC,30,0,SRCCOPY); //close button
BitBlt(windowHDC,windowRect.right - 17 - 15,
2,14,14,memDC,15,0,SRCCOPY); //min button
//*Now delete the DC's as we dont want them
anymore*//
//*****************************************//
ReleaseDC(hWnd,offscreenDC);
DeleteDC(offscreenDC);
ReleaseDC(hWnd,memDC);
DeleteDC(memDC);
[/code]
This is only an example, this would need some cleaning up, and in the
case of the buttons you would really need to use GetWindowLong with the
GW_STLYE option passed so that you can actually see if the window
properties contain the buttons you will draw (I have just drawn a close
and min button whether the window properties have them or not).
The next important message is the WM_NCCALCSIZE, here we need to
calculate the size of the client area as our borders and caption bar
could be bigger or smaller in size than the window's standard. Here is
an example of some code to go in the WM_NCCALCSIZE.
[code]
NCCALCSIZE_PARAMS *windowParams;
RECT *windowRect;
if(wParam != FALSE){
windowParams =
(NCCALCSIZE_PARAMS*)lParam;
windowParams->rgrc[0].left =
windowParams->lppos->x;
windowParams->rgrc[0].top =
windowParams->lppos->y;
windowParams->rgrc[0].right =
windowParams->lppos->x + windowParams->lppos->cx;
windowParams->rgrc[0].bottom =
windowParams->lppos->y + windowParams->lppos->cy;
windowParams->rgrc[0].left+= 2;
windowParams->rgrc[0].top += 18;
windowParams->rgrc[0].right-= 2;
windowParams->rgrc[0].bottom-= 2;
return(0);
}
else
{
windowRect = (PRECT)lParam;
windowRect->left+=2;
windowRect->top+=18;
windowRect->right-=2;
windowRect->bottom-=2;
return 0;
}
[/code]
We then need to process the WM_NCHITTEST, this will return the new
position's of what ever element that the mouse cursor is currently in.
You can get more info on MSDN about this at msdn.microsoft.com.
Also with the mouse click messages, we need to intercept these as the
default window's procedure will draw the windows standard Close, Min and
Max buttons and obviously we don?t want that. In the NCLBUTTONDOWN is
where you will draw the down state to what ever button the mouse is over
(if it is over the button) and then in the NCLBUTTONUP message is where
you will need to draw everything back to normal.
The best source to get the information on all the messages and the
lParam and wParam contents for each message is MSDN. Just go to
msdn.microsoft.com and type the message in the search box.
This I just a rough guide to give you an idea on how to do it, the code
is far from perfect it's just an example to get you going. If you
require any more help on the matter please let me know.
cheers
---- END ----
Told ya it was rather lengthy ;)
_________________________________________________________________
Is your PC infected? Get a FREE online computer virus scan from McAfee®
Security. http://clinic.mcafee.com/clinic/ibuy/campaign.asp?cid=3963
---------------------
To unsubscribe go to http://gameprogrammer.com/mailinglist.html
Other related posts: