/******************************Module*Header*******************************\
* Module Name: sswindow.cxx
*
* Copyright 1996 - 1998 Microsoft Corporation
*
\**************************************************************************/
#include <windows.h>
#include <windowsx.h>
#include "scrnsave.h"
#include "glscrnsv.h"
#include "ssintrnl.hxx"
#include "sswindow.hxx"
#include "ssutil.hxx"
static void (__stdcall *glAddSwapHintRect)(GLint, GLint, GLint, GLint);
// externs from ssinit.cxx
extern void *gDataPtr;
extern void (*gReshapeFunc)(int, int, void *);
extern void (*gRepaintFunc)( LPRECT, void *);
extern void (*gUpdateFunc)( void *);
extern void (*gInitFunc)( void *);
extern void (*gFinishFunc)( void *);
extern void (*gFloaterBounceFunc)( void *);
// forwards
static void GetWindowSize( HWND hwnd, ISIZE *pSize );
static void ss_QueryAddSwapHintRect();
static void DrawGdiDeltaRect( HDC hdc, HBRUSH hbr, RECT *pRect1, RECT *pRect2 );
static void DrawGLDeltaRect( GLRECT *pRect1, GLRECT *pRect2 );
/**************************************************************************\
* SSW constructor
*
\**************************************************************************/
SSW::SSW( PSSW psswParentArg, ISIZE *pSize, IPOINT2D *pPos, BOOL bMotion,
SSCHILDSIZEPROC ChildSizeFuncArg )
{
// Basic initialization
Reset();
// Initialization based on constructor parameters
psswParent = psswParentArg;
ChildSizeFunc = ChildSizeFuncArg;
if( pSize )
size = *pSize;
if( pPos )
pos = *pPos;
else
pos.x = pos.y = 0;
if( bMotion && psswParent ) {
// Allocate motion structure
pMotion = (MOTION *)
LocalAlloc( LMEM_ZEROINIT | LMEM_FIXED, sizeof(MOTION) );
// If pMotion is NULL, then motion is disabled
}
// Call back to the client screen saver to determine size and motion
// characteristics of child based on its parent size.
GetChildInfo(); // this can set pos and size
if( pMotion ) {
if( pPos == NULL ) {
// Set a random window pos
pos.x = ss_iRand2( 0, (psswParent->size.width - size.width) );
pos.y = ss_iRand2( 0, (psswParent->size.height - size.height) );
// Set the motion parameters
ResetMotion();
}
// Have to make sure parent has an hdc so it can draw background when
// this child moves
if( !psswParent->hdc )
psswParent->hdc = GetDC( psswParent->hwnd );
}
if( psswParent ) {
// Need to add this pssw to its parent's list
psswParent->AddChild( this );
// Default is to be subWindow of parent
psswParent->iSubWindow++; // increment reference count
}
}
/**************************************************************************\
* SSW constructor
*
* Used when wrapping an SSW around an already existing window
* (as when drawing GL on dialog buttons)
\**************************************************************************/
SSW::SSW( PSSW psswParentArg, HWND hwndArg )
{
Reset();
psswParent = psswParentArg;
if( psswParent )
// Need to add this pssw to its parent's list
psswParent->AddChild( this );
hwnd = hwndArg;
if( !hwnd ) {
SS_ERROR( "SSW::SSW : NULL hwnd\n" );
return;
}
bOwnWindow = FALSE;
// Get the window size
GetWindowSize( hwnd, &size );
gpss->sswTable.Register( hwnd, this );
}
/**************************************************************************\
* Reset
*
* Reset parameters to default init state
\**************************************************************************/
void
SSW::Reset()
{
// Basic initialization
bOwnWindow = TRUE;
iSubWindow = 0;
bValidateBg = FALSE;
wFlags = 0;
hwnd = 0;
hdc = 0;
hrc = 0;
pos.x = pos.y = 0;
size.width = size.height = 0;
psswParent = NULL;
psswSibling = NULL;
psswChildren = NULL;
bDoubleBuf = FALSE;
pStretch = NULL;
pMotion = NULL;
pGLc = NULL;
InitFunc = NULL;
UpdateFunc = NULL;
ReshapeFunc = NULL;
RepaintFunc = NULL;
FloaterBounceFunc = NULL;
FinishFunc = NULL;
ChildSizeFunc = NULL;
DataPtr = NULL;
}
/**************************************************************************\
* SSW destructor
*
* This can be called when a window is closed, or by the ss client
*
\**************************************************************************/
SSW::~SSW()
{
// If this window has any children, they will have to be terminated too
if( psswChildren ) {
PSSW psswChild = psswChildren;
while( psswChild ) {
// Delete first child in list
if( psswChild->hwnd && bOwnWindow ) {
// We created this window, we must destroy it
DestroyWindow( psswChild->hwnd );
} else {
delete psswChild;
}
// Next child is now first child in list
psswChild = psswChildren;
}
}
if( psswParent )
// Need to remove this pssw from its parent's list
psswParent->RemoveChild( this );
if( hwnd ) {
// Remove from SSWTable
gpss->sswTable.Remove( hwnd );
} else {
// subWindow
if( psswParent ) {
SS_ASSERT1( (psswParent->iSubWindow > 0),
"Invalid subWindow reference count for pssw=0x%x\n", this );
psswParent->iSubWindow--; // decrement subWindow reference count
}
}
// Clean up GL
if( hrc ) {
// FinishFunc still needs gl
if( FinishFunc )
(*FinishFunc)( DataPtr );
wglMakeCurrent( NULL, NULL );
if( ! (wFlags & SS_HRC_PROXY_BIT) )
wglDeleteContext( hrc );
}
// Clean up any bitmaps
if( pStretch ) {
SS_BITMAP *pssbm = &pStretch->ssbm;
DeleteObject(SelectObject(pssbm->hdc, pssbm->hbmOld));
DeleteDC(pssbm->hdc);
}
// Release the dc
if( hdc ) {
HWND hwndForHdc = hwnd ? hwnd : psswParent ? psswParent->hwnd : NULL;
ReleaseDC(hwndForHdc, hdc);
}
}
/**************************************************************************\
* AddChild
*
* Add the supplied child SSW to this SSW.
\**************************************************************************/
void
SSW::AddChild( PSSW psswChild )
{
if( !psswChildren ) {
psswChildren = psswChild;
return;
}
// Else travel along the sibling chain of psswChildren and deposit
// psswChild at the end
PSSW pssw = psswChildren;
while( pssw->psswSibling )
pssw = pssw->psswSibling;
pssw->psswSibling = psswChild;
}
/**************************************************************************\
* RemoveChild
*
* Remove this child from the parent's list
*
* Whoever calls this needs to update SSW_TABLE too...
\**************************************************************************/
BOOL
SSW::RemoveChild( PSSW psswChild )
{
if( !psswChildren ) {
// Something wrong - this window has no children
SS_ERROR( "SSW::RemoveChild : no children\n" );
return FALSE;
}
PSSW psswPrev;
PSSW pssw = psswChildren;
while( pssw != NULL ) {
if( pssw == psswChild ) {
// found it !
if( psswChild == psswChildren )
// The child being removed is the first in the list
psswChildren = psswChild->psswSibling;
else
psswPrev->psswSibling = pssw->psswSibling;
return TRUE;
}
// Move up the pointers
psswPrev = pssw;
pssw = psswPrev->psswSibling;
}
SS_ERROR( "SSW::RemoveChild : child not found\n" );
return FALSE;
}
/**************************************************************************\
* GetWindowSize
*
\**************************************************************************/
static void
GetWindowSize( HWND hwnd, ISIZE *pSize )
{
RECT clientRect;
GetClientRect( hwnd, &clientRect );
pSize->width = clientRect.right - clientRect.left + 1;
pSize->height = clientRect.bottom - clientRect.top + 1;
}
/**************************************************************************\
* CreateSSWindow
*
* Create OpenGL floater window. This window floats on top of the screen
* saver window, bouncing off each of the screen edges.
*
\**************************************************************************/
BOOL
SSW::CreateSSWindow(HINSTANCE hMainInstance, UINT uStyle, UINT uExStyle ,
LPCTSTR pszWindowTitle, WNDPROC wndProcArg, LPCTSTR pszClassName, HWND hwndParentOverride )
{
IPOINT2D startPos;
HWND hwndParent;
if( hwndParentOverride )
hwndParent = hwndParentOverride;
else
hwndParent = psswParent ? psswParent->hwnd : NULL;
wndProc = wndProcArg;
if( !pMotion )
startPos = pos;
else {
// Initialize start position off screen to work around win95 screen
// validation bug
startPos.x = pos.x - psswParent->size.width;
startPos.y = pos.y - psswParent->size.height;
}
hwnd = CreateWindowEx(
uExStyle,
pszClassName,
pszWindowTitle,
uStyle,
startPos.x,
startPos.y,
size.width, // width
size.height, // height
hwndParent,
NULL, // menu
hMainInstance,
(LPVOID) this
);
if (!hwnd) {
SS_WARNING( "SSW::CreateSSWindow : CreateWindowEx failure\n" );
return FALSE;
}
// This window is on its own now
if( psswParent ) {
SS_ASSERT1( (psswParent->iSubWindow > 0),
"Invalid subWindow reference count for pssw=0x%x\n", this );
psswParent->iSubWindow--; // decrement subWindow reference count
}
ShowWindow(hwnd, SW_SHOW);
return TRUE;
}
/**************************************************************************\
* GetChildInfo
*
* Call the window's ChildSizeFunc
\**************************************************************************/
void
SSW::GetChildInfo( )
{
if( !ChildSizeFunc )
return;
CHILD_INFO childInfo;
// Call the client's SizeFunc to get required info
(*ChildSizeFunc)( &psswParent->size, &childInfo );
// Pull required values into pssw and validate them
size = childInfo.size;
ValidateChildSize();
if( !pMotion ) {
pos = childInfo.pos;
bValidateChildPos();
} else {
pMotion->posInc = childInfo.motionInfo.posInc;
pMotion->posIncVary = childInfo.motionInfo.posIncVary;
pMotion->posIncCur = pMotion->posInc;
}
}
/**************************************************************************\
* ConfigureForGdi
*
* Creates an hdc for the window
*
\**************************************************************************/
BOOL
SSW::ConfigureForGdi()
{
if( hdc )
// already configured
return TRUE;
// Figure window to get hdc from
HWND hwndForHdc = hwnd ? hwnd : psswParent ? psswParent->hwnd : NULL;
if( !hwndForHdc || !(hdc = GetDC(hwndForHdc)) ) {
SS_WARNING( "SSW::ConfigureForGdi failed\n" );
return FALSE;
}
return TRUE;
}
/**************************************************************************\
* ConfigureForGL
*
* Creates a GL rendering context for the specified window
*
\**************************************************************************/
BOOL
SSW::ConfigureForGL( SS_GL_CONFIG *pGLcArg )
{
pGLc = pGLcArg;
return ConfigureForGL();
}
BOOL
SSW::ConfigureForGL()
{
if( hrc )
return TRUE;
if( ConfigureForGdi() &&
(hrc = hrcSetupGL()) )
return TRUE;
SS_WARNING( "SSW::ConfigureForGL failed\n" );
return FALSE;
}
/**************************************************************************\
* hrcSetupGL
*
* Setup OpenGL.
*
\**************************************************************************/
#define NULL_RC ((HGLRC) 0)
HGLRC
SSW::hrcSetupGL()
{
if( !pGLc )
return NULL_RC;
HGLRC hrc;
HDC hgldc;
int pfFlags = pGLc->pfFlags;
PIXELFORMATDESCRIPTOR pfd = {0};
pStretch = pGLc->pStretch;
if( pStretch ) {
if( NeedStretchedWindow() ) {
// Only need single buffered pixel format
pfFlags &= ~SS_DOUBLEBUF_BIT;
pfFlags |= SS_BITMAP_BIT; // yup, BOTH window and bitmap need this
} else
// Turn off stretching
pStretch = NULL;
}
// If preview mode or config mode, don't allow pixel formats that need
// the system palette, as this will create much ugliness.
if( ss_fPreviewMode() || ss_fConfigMode() )
pfFlags |= SS_NO_SYSTEM_PALETTE_BIT;
// If config mode, force a non-accelerated pixel format, as WNDOBJ's
// seem to have problems with MCD, ICD
if( ss_fConfigMode() )
pfFlags |= SS_GENERIC_UNACCELERATED_BIT;
bDoubleBuf = SS_HAS_DOUBLEBUF( pfFlags );
if( !SSU_SetupPixelFormat( hdc, pfFlags, &pfd ) )
return NULL_RC;
// Update pGLc->pfFlags based on pfd returned
// (for now, the only ones we care about are the generic/accelerated flags)
if( (pfd.dwFlags & (PFD_GENERIC_FORMAT|PFD_GENERIC_ACCELERATED))
== PFD_GENERIC_FORMAT )
pGLc->pfFlags |= SS_GENERIC_UNACCELERATED_BIT;
if( SSU_bNeedPalette( &pfd ) ) {
// Note: even if bStretch, need to set up palette here so they match
if( !gpss->pssPal ) {
SS_PAL *pssPal;
BOOL bTakeOverPalette = ss_fFullScreenMode() ? TRUE : FALSE;
// The global palette has not been created yet - do it
// SS_PAL creation requires pixel format descriptor for color bit
// information, etc. (the pfd is cached in SS_PAL, since for
// palette purposes it is the same for all windows)
pssPal = new SS_PAL( hdc, &pfd, bTakeOverPalette );
if( !pssPal )
return NULL_RC;
// Set approppriate palette manage proc
if( ss_fFullScreenMode() )
pssPal->paletteManageProc = FullScreenPaletteManageProc;
else
// use regular palette manager proc
pssPal->paletteManageProc = PaletteManageProc;
gpss->pssPal = pssPal;
}
// Realize the global palette in this window
// assume we're realizing in foreground
HWND hwndPal = hwnd ? hwnd : psswParent ? psswParent->hwnd : NULL;
if( hwndPal )
gpss->pssPal->Realize( hwndPal, hdc, FALSE );
}
if( pStretch ) {
// Stretch blt mode: For every frame, we'll be doing a StretchBlt
// from a DIB to the screen. Need to set up a compatible memdc.
SS_BITMAP *pssbm;
pssbm = &pStretch->ssbm;
pssbm->hdc = CreateCompatibleDC(hdc);
if( !pssbm->hdc )
return NULL_RC;
ResizeStretch(); // this creates the DIB Section
pfFlags = 0;
pfFlags |= SS_BITMAP_BIT;
if( !SSU_SetupPixelFormat( pssbm->hdc, pfFlags, &pfd ) ) {
return NULL_RC;
}
// this ppfd's palette bits must match the window's !!
// If window needs palette, so does bitmap...
if( gpss->pssPal ) {
SS_PAL *pssPal = gpss->pssPal;
extern void ssw_UpdateDIBColorTable( HDC, HDC );
ssw_UpdateDIBColorTable( pssbm->hdc, hdc );
}
hgldc = pssbm->hdc;
} else {
hgldc = hdc;
}
if( pGLc->hrc ) {
// Use the supplied hrc
hrc = pGLc->hrc;
// Set flag so we don't delete this borrowed hrc when the SSW terminates
wFlags |= SS_HRC_PROXY_BIT;
} else
// Create a new hrc
hrc = wglCreateContext(hgldc);
if( !hrc || !wglMakeCurrent(hgldc, hrc) ) {
SS_WARNING( "SSW::hrcSetupGL : hrc context failure\n" );
return NULL_RC;
}
if( !hwnd && (bDoubleBuf || pStretch) ) {
// enable scissoring
glEnable( GL_SCISSOR_TEST );
if( !(pGLc->pfFlags & SS_GENERIC_UNACCELERATED_BIT) ) {
// MCD or ICD, possible hardware implementation - we maintain
// a lastRect to handle SwapBuffer issues
lastRect.x = lastRect.y = lastRect.width = lastRect.height = 0;
}
}
SS_DBGLEVEL2( SS_LEVEL_INFO,
"SSW::hrcSetupGL: wglMakeCurrent( hrc=0x%x, hwnd=0x%x )\n", hrc, hwnd );
// Note that these queries are based on a single gl window screen saver. In
// a more complicated scenario, these capabilities could be queried on a
// per-window basis (since support could vary with pixel formats).
// Query the GL version - sets support for any new (e.g. 1.1) functionality
ss_QueryGLVersion();
// Query paletted texture extension
ss_QueryPalettedTextureEXT();
// Query the AddSwapHintRect WIN extension
ss_QueryAddSwapHintRect();
// Pull in any Func's that were already defined (for compatibility with
// old mechanism)
InitFunc = gInitFunc;
UpdateFunc = gUpdateFunc;
ReshapeFunc = gReshapeFunc;
RepaintFunc = gRepaintFunc;
FloaterBounceFunc = gFloaterBounceFunc;
FinishFunc = gFinishFunc;
DataPtr = gDataPtr;
return hrc;
}
/**************************************************************************\
* MakeCurrent
*
* Call wglMakeCurrent for this window's hrc. Note: an ss client may have
* more than one hrc (e.g. pipes), in which case it is the client's
* responsibility to make current.
\**************************************************************************/
void
SSW::MakeCurrent()
{
if( ! wglMakeCurrent( hdc, hrc ) )
SS_WARNING( "SSW::MakeCurrent : wglMakeCurrent failure\n" );
}
/**************************************************************************\
* InitGL
*
* Call the window's GL Init Func
*
* Priority is raised to expedite any initialization (e.g. loading and
* processing textures can take a while.
*
* A Reshape msg is sent to the client ss, as this is required for setting
* glViewport, etc.
\**************************************************************************/
void
SSW::InitGL()
{
PSSW psswChild = psswChildren;
// Configure the window for GL if pGLc non-NULL
if( pGLc && (! ConfigureForGL()) ) {
// This is fatal for this window - if it is the main window,
// the ss will terminate
if( hwnd )
PostMessage( hwnd, WM_CLOSE, 0, 0l );
return;
}
// If window configured for GL, hrc will have been set...
// Call the InitFunc
if( hrc && InitFunc ) {
DWORD oldPriority;
// Bump up priority during initialization phase
oldPriority = GetPriorityClass( GetCurrentProcess() );
SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS );
SS_DBGLEVEL1( SS_LEVEL_INFO,
"SSW::InitGL: Calling client GLInit for 0x%x\n", hwnd );
(*InitFunc)( DataPtr );
// restore original priority
SetPriorityClass( GetCurrentProcess(), oldPriority );
}
/* Send another Reshape, since initial one triggered by window
* creation would have been received before GL init'd
*/
Reshape();
// Next, init any child windows. This has to be done after the parent
// window initialization.
while( psswChild ) {
if( psswChild->hwnd )
SendMessage( psswChild->hwnd, SS_WM_INITGL, 0, 0 );
else
// Must be a logical sub-window
psswChild->InitGL();
psswChild = psswChild->psswSibling;
}
}
/**************************************************************************\
* ResizeStretch
*
* Resize the compatible bitmap for stretch blt mode
*
* There are 2 sizing modes. If bRatioMode, then we set the bitmap size
* by dividing the window dimensions by the supplied ratios. In this case,
* the base* values set a lower limit for the bitmap dimensions. Otherwise, the
* base* values determine the bitmap dimensions.
*
\**************************************************************************/
void
SSW::ResizeStretch()
{
HBITMAP hbmNew;
PVOID pvBits;
int width, height;
int cwidth, cheight; // client area size
SS_BITMAP *pssbm = &pStretch->ssbm;
cwidth = size.width;
cheight = size.height;
if( pStretch->bRatioMode ) {
width = (int) ( (float)cwidth / pStretch->widthRatio );
height = (int) ( (float)cheight / pStretch->heightRatio );
if( width < pStretch->baseWidth )
width = pStretch->baseWidth;
if( height < pStretch->baseHeight )
height = pStretch->baseHeight ;
} else {
width = pStretch->baseWidth;
height = pStretch->baseHeight;
}
// Limit width, height to window dimensions
if( width > cwidth )
width = cwidth;
if( height > cheight )
height = cheight;
// If same size, get out
if( (width == pssbm->size.width) && (height == pssbm->size.height) )
return;
pssbm->size.width = width;
pssbm->size.height = height;
// Use system palette
hbmNew = SSDIB_CreateCompatibleDIB(hdc, NULL, width, height, &pvBits);
if (hbmNew)
{
if (pssbm->hbm != (HBITMAP) 0)
{
SelectObject( pssbm->hdc, pssbm->hbmOld );
DeleteObject( pssbm->hbm );
}
pssbm->hbm = hbmNew;
pssbm->hbmOld = (HBITMAP) SelectObject( pssbm->hdc, pssbm->hbm );
}
}
/**************************************************************************\
* Resize
*
* Resize wrapper
*
* Called in response to WM_SIZE.
*
\**************************************************************************/
void
SSW::Resize( int width, int height )
{
size.width = width;
size.height = height;
if( pStretch ) {
// May have to resize associated bitmap
ResizeStretch();
}
if( psswChildren ) {
// May need to resize any children
PSSW pssw = psswChildren;
while( pssw ) {
// Get new size/motion for the floater
pssw->GetChildInfo();
pssw->SetSSWindowPos();
if( !pssw->hwnd ) {
// Handle sub-window case
// Need to call Reshape, since win32 system won't send WM_SIZE.
pssw->Reshape();
}
pssw = pssw->psswSibling;
}
}
Reshape();
}
/**************************************************************************\
* Repaint
*
* Repaint wrapper
*
* Called in response to WM_PAINT.
*
\**************************************************************************/
#define NULL_UPDATE_RECT( pRect ) \
( ((pRect)->left == 0) && \
((pRect)->right == 0) && \
((pRect)->top == 0) && \
((pRect)->bottom == 0) )
void
SSW::Repaint( BOOL bCheckUpdateRect )
{
if( !hwnd )
return;
RECT rect, *pRect = NULL;
if( bCheckUpdateRect ) {
GetUpdateRect( hwnd, &rect, FALSE );
// Above supposed to return NULL if rect is all 0's,
// but this doesn't happen
if( NULL_UPDATE_RECT( &rect ) )
return;
pRect = ▭
}
if( RepaintFunc )
(*RepaintFunc)( pRect, DataPtr );
}
/**************************************************************************\
* NeedStretchedWindow
*
* Check if stretch mode is necessary
*
\**************************************************************************/
BOOL
SSW::NeedStretchedWindow()
{
if( (pStretch->baseWidth >= size.width) &&
(pStretch->baseHeight >= size.height) ) {
return FALSE;
}
return TRUE;
}
/**************************************************************************\
* SetSSWindowPos
*
* Set new size and position for an SSW
*
\**************************************************************************/
void
SSW::SetSSWindowPos()
{
SetSSWindowPos( 0 );
}
void
SSW::SetSSWindowPos( int flags )
{
if( hwnd ) {
SetWindowPos(hwnd, 0, pos.x, pos.y,
size.width, size.height,
SWP_NOCOPYBITS | SWP_NOZORDER | SWP_NOACTIVATE | flags );
// Note: If flags does not specify SWP_NOREDRAW, this generates a WM_PAINT
// msg for the entire window region
} else if( psswParent && !pStretch ) {
// Set viewport for new position
// (if pStretch, shouldn't need to do anything, as viewport should
// have been already set to the bitmap)
int posy = GLPosY();
glViewport( pos.x, posy, size.width, size.height );
glScissor( pos.x, posy, size.width, size.height );
glAddSwapHintRect( pos.x, posy, size.width, size.height );
}
}
/******************************Public*Routine******************************\
* SetAspectRatio
*
* Resize a child window to conform to the supplied aspect ratio. We do this by
* maintaining the existing width, and adjusting the height.
*
* Window resize seems to be executed synchronously, so gl should be able to
* immediately validate its buffer dimensions (we count on it).
*
* Returns TRUE if new height is different from last, else FALSE.
\**************************************************************************/
BOOL
SSW::SetAspectRatio( FLOAT fAspect )
{
if( !psswParent )
return FALSE;
int oldHeight;
UINT uFlags = 0;
// Check for zero fAspect
if( fAspect == 0.0f ) fAspect = 1.0f;
oldHeight = size.height;
// Set the new height, based on desired aspect ratio
size.height = (int) ((FLOAT) size.width / fAspect);
// make sure new height not TOO big!
ValidateChildSize();
if( size.height == oldHeight )
// no change
return FALSE;
// make sure current position still valid when height changes
if( !bValidateChildPos() ) {
// current position OK, don't need to move it
uFlags |= SWP_NOMOVE;
}
SetSSWindowPos( uFlags );
// ! remember not to call the client's ReshapeFunc here, as SetAspectRatio
// may be called by the client in response to a window resize, resulting in
// an infinite loop
// but if there is no size change (above) we return, avoiding the
// infinite loop
if( !hwnd )
// Need to call Reshape, since win32 system won't send WM_SIZE. Need
// to fix to avoid possible infinite loops
Reshape();
return TRUE;
}
/**************************************************************************\
* CalcNextWindowPos
*
* Calculate the next position for a moving window, using the pMotion data.
* If the new position would cause the window to bounce off the
* edge of its parent, return TRUE.
*
\**************************************************************************/
BOOL
SSW::CalcNextWindowPos()
{
POINT2D *fpos = &pMotion->pos;
IPOINT2D *ipos = &pos;
POINT2D *posInc = &pMotion->posInc;
POINT2D *posIncV = &pMotion->posIncVary;
POINT2D *posIncCur = &pMotion->posIncCur;
BOOL bounce = FALSE;
if( !psswParent )
return FALSE;
// Compute the next window position.
fpos->x += posIncCur->x;
fpos->y += posIncCur->y;
ipos->x = (int) fpos->x;
ipos->y = (int) fpos->y;
if ( (ipos->x + size.width) > psswParent->size.width) {
// Right hit
ipos->x = psswParent->size.width - size.width;
fpos->x = (float) ipos->x;
posIncCur->x =
- ss_fRand( posInc->x - posIncV->x, posInc->x + posIncV->x );
if( posIncCur->x > -0.5f ) posIncCur->x = -0.5f;
bounce = TRUE;
} else if (ipos->x < 0) {
// Left hit
ipos->x = 0;
fpos->x = 0.0f;
posIncCur->x =
ss_fRand( posInc->x - posIncV->x, posInc->x + posIncV->x );
if( posIncCur->x < 0.5f ) posIncCur->x = 0.5f;
bounce = TRUE;
}
if ( (ipos->y + size.height) > psswParent->size.height) {
// Bottom hit
ipos->y = psswParent->size.height - size.height;
fpos->y = (float) (ipos->y);
posIncCur->y =
- ss_fRand( posInc->y - posIncV->y, posInc->y + posIncV->y );
if( posIncCur->y > -0.5f ) posIncCur->y = -0.5f;
bounce = TRUE;
} else if (ipos->y < 0) {
// Top hit
ipos->y = 0;
fpos->y = 0.0f;
posIncCur->y =
ss_fRand( posInc->y - posIncV->y, posInc->y + posIncV->y );
if( posIncCur->y < 0.5f ) posIncCur->y = 0.5f;
bounce = TRUE;
}
return bounce;
}
/**************************************************************************\
* MoveSSWindow
*
* This is the function that moves the OpenGL window around, causing it to
* bounce around. Each time the window is moved, the contents of the
* window are updated from the hidden (or back) buffer by SwapBuffers().
*
* The bRedrawBg flag determines whether the area that was covered by the old
* position should be updated by the parent window.
*
\**************************************************************************/
void
SSW::MoveSSWindow( BOOL bRedrawBg )
{
BOOL bounce;
int flags = SWP_NOSIZE;
// Synchronize with OpenGL. Flush OpenGL commands and wait for completion.
glFinish();
// Move the window
bounce = CalcNextWindowPos();
if( bounce && FloaterBounceFunc )
// The window bounced off one of the sides
// ! This function should *not* be used for rendering - for
// for informational purposes only (.e.g. changing the spin of a
// rotating object).
(*FloaterBounceFunc)( DataPtr );
if( !bRedrawBg )
flags |= SWP_NOREDRAW;
SetSSWindowPos( flags );
}
/**************************************************************************\
* UpdateWindow
*
* Update the window
*
* Ccurrently this assumes all windows are being animated (i.e. not showing
* a static image)
*
* Things *must* happen in the order defined here, so they work on generic as
* well as hardware implementations.
* Note: Move must happen after SwapBuf, and will cause some encroaching on
* the current display, as the parent window repaints after the move. Therefore
* apps must take care to leave an empty border around their rendered image,
* equal to the maximum window move delta.
*
\**************************************************************************/
void
SSW::UpdateWindow()
{
// update any children first
PSSW pssw = psswChildren;
while( pssw ) {
pssw->UpdateWindow();
pssw = pssw->psswSibling;
}
// If this window is a subWindow in a non-generic implementation, the
// background of the parent may be invalid (this may eventually be
// useful for regular windows, which is why we don't && !hwnd)
if( psswParent &&
psswParent->bValidateBg &&
pGLc &&
!(pGLc->pfFlags & SS_GENERIC_UNACCELERATED_BIT) )
{
// Clear the entire parent window
// mf: I think this is only needed for double-buffered schemes, since
// the windowing system should repaint the front buffer in this case.
glDisable( GL_SCISSOR_TEST );
glClear( GL_COLOR_BUFFER_BIT );
glEnable( GL_SCISSOR_TEST );
psswParent->bValidateBg = FALSE;
}
if( !UpdateFunc )
return;
// bDoubleBuf and pStretch should be mutually exclusive...
if( bDoubleBuf || pStretch ) {
UpdateDoubleBufWin();
} else {
if( pMotion )
MoveSSWindow( TRUE );
(*UpdateFunc)( DataPtr );
}
}
/**************************************************************************\
* UpdateDoubleBufWin
*
* This is used when moving a double buffered window around. It will
* work for all configurations.
*
\**************************************************************************/
void
SSW::UpdateDoubleBufWin()
{
if( !hwnd ) {
UpdateDoubleBufSubWin();
return;
}
RECT updateRect;
// Move the window
if( pMotion ) {
// Save child update rect before move
GetSSWindowRect( &updateRect );
// Move window, without repainting exposed area
MoveSSWindow( FALSE );
}
// Update the back buffer
(*UpdateFunc)( DataPtr );
if( pMotion ) {
// (pMotion will be NULL if this window has no parent)
if( hwnd ) {
// Paint the exposed area with bg brush (the current image will
// be partially erased momentarily, until the SwapBuffers() call
// comes through)
// (This rect should be clipped to our new window position...)
DrawGdiRect( psswParent->hdc, gpss->hbrBg, &updateRect );
} else {
// currently this path not possible, since if !hwnd, we use one of the
// UpdateDoubleBufSubWin* functions
SS_WARNING( "SSW::UpdateDoubleBufWin: no hwnd\n" );
// sub-window case : need to do our own clipping
RECT newRect;
GetSSWindowRect( &newRect );
DrawGdiDeltaRect( psswParent->hdc, gpss->hbrBg, &updateRect, &newRect );
}
}
// Swap to the new window position
SwapSSBuffers();
}
/**************************************************************************\
* UpdateDoubleBufSubWin
*
* Used for generic double buffered gl sub-windows.
*
\**************************************************************************/
void
SSW::UpdateDoubleBufSubWin()
{
GLRECT curRect, newRect;
// AddSwapHintRect for current position
glAddSwapHintRect( pos.x, GLPosY(), size.width, size.height );
if( pMotion ) {
// Save current rect
curRect.x = pos.x;
curRect.y = GLPosY();
curRect.width = size.width;
curRect.height = size.height;
// Move window, without repainting exposed area
MoveSSWindow( FALSE );
// Get new rect
newRect.x = pos.x;
newRect.y = GLPosY();
newRect.width = size.width;
newRect.height = size.height;
DrawGLDeltaRect( &curRect, &newRect );
// Have to consider previous rect for ICD or MCD
if( !(pGLc->pfFlags & SS_GENERIC_UNACCELERATED_BIT) ) {
DrawGLDeltaRect( &lastRect, &newRect );
lastRect = curRect;
}
// Reset scissor to new rect (this *was* set by MoveSSWindow, but
// DrawGLDeltaRect sets scissor to do its clearing
glScissor( newRect.x, newRect.y, newRect.width, newRect.height );
}
// Update the back buffer
(*UpdateFunc)( DataPtr );
// Swap to the new window position
SwapSSBuffers();
}
/******************************Public*Routine******************************\
* RandomWindowPos
*
* Sets a new random window position and motion
*
\**************************************************************************/
void
SSW::RandomWindowPos()
{
if( psswParent ) {
if( !hwnd ) {
// sub-window : manually clear old window rect
if( bDoubleBuf ) {
glClear( GL_COLOR_BUFFER_BIT );
} else {
RECT oldRect;
GetSSWindowRect( &oldRect );
DrawGdiRect( psswParent->hdc, gpss->hbrBg, &oldRect );
}
}
// Calc and set new position
pos.x = ss_iRand2( 0, (psswParent->size.width - size.width) );
pos.y = ss_iRand2( 0, (psswParent->size.height - size.height) );
SetSSWindowPos( SWP_NOSIZE );
// Reset motion
if( pMotion )
ResetMotion();
}
}
/**************************************************************************\
* ResetMotion
*
* Calculate a random position and motion vector for the floater window
* Note that a floating point position is maintained for DDA window movement
*
\**************************************************************************/
void
SSW::ResetMotion()
{
if( !psswParent || !pMotion )
// Only child windows can be reset
return;
// Set floating point pos also, for DDA
pMotion->pos.x = (float) pos.x;
pMotion->pos.y = (float) pos.y;
// also reset the window motion directions
if( ss_iRand(2) ) // 0 or 1
pMotion->posIncCur.x = - pMotion->posIncCur.x;
if( ss_iRand(2) )
pMotion->posIncCur.y = - pMotion->posIncCur.y;
}
/**************************************************************************\
* ValidateChildSize
*
* Make sure it's not bigger than its parent
*
\**************************************************************************/
void
SSW::ValidateChildSize()
{
if( !psswParent )
return;
SS_CLAMP_TO_RANGE2( size.width, 0, psswParent->size.width );
SS_CLAMP_TO_RANGE2( size.height, 0, psswParent->size.height );
}
/**************************************************************************\
* bValidateChildPos
*
* Make sure that with the current window position, none of the floating
* window extends beyond the parent window.
*
\**************************************************************************/
BOOL
SSW::bValidateChildPos()
{
BOOL bRet = FALSE;
if( !psswParent )
return FALSE;
if ( (pos.x + size.width) > psswParent->size.width) {
pos.x = psswParent->size.width - size.width;
bRet = TRUE;
}
if ( (pos.y + size.height) > psswParent->size.height) {
pos.y = psswParent->size.height - size.height;
bRet = TRUE;
}
return bRet;
}
/**************************************************************************\
* GetSSWindowRect
*
* Return window position and size in supplied RECT structure
*
* this rect is relative to the parent
\**************************************************************************/
void
SSW::GetSSWindowRect( LPRECT lpRect )
{
lpRect->left = pos.x;
lpRect->top = pos.y;
lpRect->right = pos.x + size.width;
lpRect->bottom = pos.y + size.height;
}
/**************************************************************************\
* GLPosY
*
* Return y-coord of window position in GL coordinates (a win32 window position
* (starts from top left, while GL starts from bottom left)
*
\**************************************************************************/
int
SSW::GLPosY()
{
if( !psswParent )
return 0;
return psswParent->size.height - size.height - pos.y;
}
/**************************************************************************\
* SwapStretchBuffers
*
* Swaps from the stretch buffer to the GL window, using StretchBlt
*
\**************************************************************************/
void
SSW::SwapStretchBuffers()
{
SS_BITMAP *pssbm = &pStretch->ssbm;
if( (size.width == pssbm->size.width) &&
(size.height == pssbm->size.height) ) // buffers same size
{
BitBlt(hdc, 0, 0, size.width, size.height,
pssbm->hdc, 0, 0, SRCCOPY);
}
else
{
StretchBlt(hdc, 0, 0, size.width, size.height,
pssbm->hdc, 0, 0, pssbm->size.width, pssbm->size.height,
SRCCOPY);
}
GdiFlush();
}
/**************************************************************************\
* SwapBuffers
*
* Wrapper for SwapBuffers / SwapStretchBuffers
*
\**************************************************************************/
void
SSW::SwapSSBuffers()
{
if( pStretch )
SwapStretchBuffers();
else if( bDoubleBuf )
SwapBuffers( hdc );
}
/**************************************************************************\
* Reshape
*
* Reshape wrapper
* Sends reshape msg to screen saver
* This is the size of the surface that gl renders onto, which can be a bitmap.
*
\**************************************************************************/
void
SSW::Reshape()
{
// Point to size of window, or bitmap if it has one
ISIZE *pSize = &size;
if( pStretch )
pSize = &pStretch->ssbm.size;
// If the window has an hrc, set default viewport
if( hrc ) {
if( hwnd )
glViewport( 0, 0, pSize->width, pSize->height );
else if ( psswParent ) {
// sub-window (only 1 level of sub-windowing supported)
// clear entire window to black
glDisable( GL_SCISSOR_TEST );
glClear( GL_COLOR_BUFFER_BIT );
glEnable( GL_SCISSOR_TEST );
// Convert win32 y-coord to GL
glViewport( pos.x, GLPosY(), pSize->width, pSize->height );
}
}
if( ReshapeFunc ) {
(*ReshapeFunc)( pSize->width, pSize->height, DataPtr );
}
}
/******************************Public*Routine******************************\
* GdiClear
*
* Clears window using Gdi FillRect
\**************************************************************************/
void
SSW::GdiClear()
{
if( !hdc )
return;
RECT rect;
GetSSWindowRect( &rect );
FillRect( hdc, &rect, gpss->hbrBg );
GdiFlush();
}
/******************************Public*Routine******************************\
* MyAddSwapHintRect
*
\**************************************************************************/
static void _stdcall
MyAddSwapHintRect(GLint xs, GLint ys, GLint xe, GLint ye)
{
return;
}
/******************************Public*Routine******************************\
* QueryAddSwapHintRectWIN
*
\**************************************************************************/
static void
ss_QueryAddSwapHintRect()
{
glAddSwapHintRect = (PFNGLADDSWAPHINTRECTWINPROC)
wglGetProcAddress("glAddSwapHintRectWIN");
if (glAddSwapHintRect == NULL) {
glAddSwapHintRect = MyAddSwapHintRect;
}
}
/******************************Public*Routine******************************\
* DrawGdiDeltaRect
*
* Draw the exposed area by transition from rect1 to rect2
\**************************************************************************/
static void
DrawGdiDeltaRect( HDC hdc, HBRUSH hbr, RECT *pRect1, RECT *pRect2 )
{
if( (pRect1 == NULL) || (pRect2 == NULL) ) {
SS_WARNING( "DrawGdiDeltaRect : one or both rects are NULL\n" );
return;
}
// Draw 2 rects
RECT rect;
// Rect exposed in x-direction:
rect.top = pRect1->top;
rect.bottom = pRect1->bottom;
if( pRect2->left > pRect1->left ) {
// moving right
rect.left = pRect1->left;
rect.right = pRect2->left;
} else {
// moving left
rect.left = pRect2->right;
rect.right = pRect1->right;
}
FillRect( hdc, &rect, hbr );
// Rect exposed in y-direction:
rect.left = pRect1->left;
rect.right = pRect1->right;
if( pRect2->bottom > pRect1->bottom ) {
// moving down
rect.top = pRect1->top;
rect.bottom = pRect2->top;
} else {
// moving up
rect.top = pRect2->bottom;
rect.bottom = pRect1->bottom;
}
FillRect( hdc, &rect, hbr );
GdiFlush();
}
/******************************Public*Routine******************************\
* DrawGLDeltaRect
*
* Draw the exposed area by transition from rect1 to rect2
\**************************************************************************/
static void
DrawGLDeltaRect( GLRECT *pRect1, GLRECT *pRect2 )
{
if( (pRect1 == NULL) || (pRect2 == NULL) ) {
SS_WARNING( "DrawGLDeltaRect : one or both rects are NULL\n" );
return;
}
// Draw 2 rects :
//mf: !!! this assumes rect1 and rect2 have same dimensions !
GLRECT rect;
// Rect exposed in x-direction:
rect.height = pRect1->height;
rect.y = pRect1->y;
if( pRect2->x > pRect1->x ) {
// moving right
rect.width = pRect2->x - pRect1->x;
rect.x = pRect1->x;
} else {
// moving left
rect.width = pRect1->x - pRect2->x;
rect.x = pRect2->x + pRect2->width;
}
glScissor( rect.x, rect.y, rect.width, rect.height );
glClear( GL_COLOR_BUFFER_BIT );
// Rect exposed in y-direction:
rect.width = pRect1->width;
rect.x = pRect1->x;
if( pRect2->y > pRect1->y ) {
// moving up
rect.height = pRect2->y - pRect1->y;
rect.y = pRect1->y;
} else {
// moving down
rect.height = pRect1->y - pRect2->y;
rect.y = pRect2->y + pRect2->height;
}
glScissor( rect.x, rect.y, rect.width, rect.height );
glClear( GL_COLOR_BUFFER_BIT );
}