/**Module*Header**\ * Module Name: sswindow.cxx * Copyright (c) 1996 Microsoft Corporation \**/ #include #include #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. * History * Apr. 28, 95 : [marcfo] * - Floater motion characteristics now defined by caller * Aug. 14, 95 : [marcfo] * - Position the window offscreen initially, to workaround a win95 bug * with a corrupted initial clip rect. \**/ 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) { //mf: could still continue here by using sub-windows 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 ) // Already configured... 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; //mf: This routine does not yet fully support logical sub-windows... 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. Do the same thing if a // monitor configuration is detected, since only the generic implementation // will work properly in this case on all displays. if( ss_fConfigMode() || (GetSystemMetrics(SM_CMONITORS) > 1) ) 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 // !!! mf: klugey, fix after SUR // (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 //mf: 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; } //mf: 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 ); //mf: 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. * Feb. 12, 96 : [marcfo] \**/ void SSW::ResizeStretch() { RECT rc; 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; #if 1 // Use system palette hbmNew = SSDIB_CreateCompatibleDIB(hdc, NULL, width, height, &pvBits); #else // Use log palette hbmNew = SSDIB_CreateCompatibleDIB(hdc, gpss->pssPal ? gpss->pssPal->hPal : NULL, width, height, &pvBits); #endif 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 ); // mf: 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 // (mf: 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 //mf: 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 // 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; } //mf: semi-kluge // 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 { //mf: ? where's the clearing here ? (true, no one uses this path...) 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 { //mf: 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 * mf: 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) //mf: klugey ? - should take into account non-GL and single buffer... #if 1 // clear entire window to black glDisable( GL_SCISSOR_TEST ); glClear( GL_COLOR_BUFFER_BIT ); glEnable( GL_SCISSOR_TEST ); #endif // 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; //mf: this should use GetClientRect 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 ); }