/******************************Module*Header*******************************\
* Module Name: mtkwin.cxx
*
* Copyright (c) 1996 Microsoft Corporation
*
\**************************************************************************/

#include "mtk.hxx"
#include "glutil.hxx"
#include "mtkwin.hxx"
#include "mtkwproc.hxx"
#include "mtkinit.hxx"

/**************************************************************************\
* MTKWIN constructor
*
\**************************************************************************/

MTKWIN::MTKWIN()
{
    Reset();
}

/**************************************************************************\
* Reset
*
* Reset parameters to default init state
\**************************************************************************/

void
MTKWIN::Reset()
{
    // Basic initialization

    bOwnWindow = FALSE;
    wFlags = 0;
    hwnd = 0;
    hdc = 0;
    hrc = 0;
    pos.x = pos.y = 0;
    size.width = size.height = 0;
    pBackBitmap =   NULL;
    pBackgroundBitmap =   NULL;
    bDoubleBuf =    FALSE;
    bFullScreen =   FALSE;
    execRefCount =  0;

    ReshapeFunc =   NULL;
    RepaintFunc =   NULL;
    DisplayFunc =   NULL;
    MouseMoveFunc = NULL;
    MouseDownFunc = NULL;
    MouseUpFunc =   NULL;
    KeyDownFunc =   NULL;

    FinishFunc =    NULL;
    DataPtr =       NULL;
}

/**************************************************************************\
* MTKWIN destructor
*
* This can be called when a window is closed, or by the ss client
*
\**************************************************************************/

MTKWIN::~MTKWIN()
{
//mf: !!! we're in trouble if user calls this directly, because would then need to
// post a DESTROY msg, here putting us in an endless loop...
// -> could have a flag set so we know if user or internal call

//mf: another potential timing problem here : If user calls MTKWIN::Return(),
// which posts an MTK_WM_RETURN msg to the windows queue, and then calls here
// before the msg is processed, we could delete the MTKWIN here before exiting
// the msg loop.  So here we should make sure the msg loop is exited by
// calling Return() or something.  This should be easy to verify via a
// reference count

    if( execRefCount ) {
        SS_ERROR1( "MTKWIN::~MTKWIN : execRefCount is %d\n", execRefCount );
        // mf: ? can we exit the msg loop here ?
//mf: this din't get through
#if 1
        SendMessage( hwnd, MTK_WM_RETURN, 0, 0l );
#else
        if( ! PostMessage( hwnd, MTK_WM_RETURN, 0, 0l ) )
            SS_ERROR( "MTKWIN dtor : MTK_WM_RETURN msg not posted\n" );
#endif
        
    }

    if( pBackBitmap )
        delete pBackBitmap;

    if( pBackgroundBitmap )
        delete pBackgroundBitmap;

    if( hwnd ) {
        animator.Stop();
        // Remove from SSWTable
        sswTable.Remove( hwnd );
    }

    // Clean up GL

//mf: !!!
//mf: This assumes FinishFunc is only related to gl
    if( hrc ) {
        // FinishFunc still needs gl
        if( FinishFunc )
#if 0
            (*FinishFunc)( DataPtr );
#else
            (*FinishFunc)();
#endif

        wglMakeCurrent( NULL, NULL );
        if( ! (wFlags & SS_HRC_PROXY_BIT) )
            wglDeleteContext( hrc );
    }

    //  Release the dc
    if( hdc ) {
        HWND hwndForHdc = hwnd;
        ReleaseDC(hwndForHdc, hdc);
    }
}

/**************************************************************************\
* Create
*
* Create window.
*
\**************************************************************************/

BOOL
MTKWIN::Create( LPCTSTR pszWindowTitle, ISIZE *pSize, IPOINT2D *pPos,
                UINT winConfig, WNDPROC userWndProc )
{
    HWND    hwndParent;
    UINT    uStyle = 0;
    UINT    uExStyle = 0;
    HINSTANCE  hInstance;
    int     width, height;

    if( ! mtk_Init( this ) )
        return FALSE;
    
    bOwnWindow = TRUE; // We're creating the window, it's not a wrapper

    if( winConfig & MTK_FULLSCREEN ) {
//mf: this really only valid if no border
        bFullScreen = TRUE;
        pos.x = 0;
        pos.y = 0;
        size.width = GetSystemMetrics( SM_CXSCREEN );
        size.height = GetSystemMetrics( SM_CYSCREEN );
        uExStyle |= WS_EX_TOPMOST;
    } else {
        pos = *pPos;
        size = *pSize;
    }

    LPCTSTR pszClass;
    HBRUSH hBrush = ghbrbg;
    HCURSOR hCursor = ghArrowCursor;
    WNDPROC wndProc;

    if( bTransparent = (winConfig & MTK_TRANSPARENT) ) {
        uExStyle |= WS_EX_TRANSPARENT;
        hBrush = NULL;
    }

//mf: if winsize, winpos NULL, pick default size, pos
    if( winConfig & MTK_NOBORDER ) {
        uStyle |= WS_POPUP;
        width = size.width;
        height = size.height;
    } else {
        uStyle |= WS_OVERLAPPEDWINDOW;
        /*
         *  Make window large enough to hold a client area of requested size
         */
        RECT WinRect;

//mf: either of these should work
#if 0
        WinRect.left   = 0;
        WinRect.right  = size.width;
        WinRect.top    = 0;
        WinRect.bottom = size.height;
#else
        WinRect.left   = pos.x;
        WinRect.right  = pos.x + size.width;
        WinRect.top    = pos.y;
        WinRect.bottom = pos.y + size.height;
#endif

        AdjustWindowRectEx(&WinRect, uStyle, FALSE, uExStyle );
        width = WinRect.right - WinRect.left;
        height = WinRect.bottom - WinRect.top;
    }

    if( winConfig & MTK_NOCURSOR )
        hCursor = NULL;

    if( userWndProc )
        wndProc = userWndProc;
    else
        wndProc = mtkWndProc;

    // Register window class
    pszClass = mtk_RegisterClass( wndProc, NULL, hBrush, hCursor );

    hInstance = GetModuleHandle( NULL );
    hwndParent = NULL; // for now
    
    hwnd = CreateWindowEx(
                                 uExStyle,
                                 pszClass,
                                 pszWindowTitle,
                                 uStyle,
                                 pos.x,
                                 pos.y,
                                 width,
                                 height,
                                 hwndParent,
                                 NULL,               // menu
                                 hInstance,
                                 (LPVOID) this
                                );

    if (!hwnd) {
        SS_WARNING( "SSW::CreateSSWindow : CreateWindowEx failure\n" );
        return FALSE;
    }

    if( bTransparent ) {
        // Create a bitmap buffer that tracks the window size.  This will be
        // used to store a window background.
        ConfigureForGdi();
        pBackgroundBitmap = new MTKBMP( hdc );
        if( !pBackgroundBitmap ) {
            SS_WARNING( "MTKWIN::Create: couldn't create background bitmap\n" );
        } else {
            // Set bitmap's size to the window's size
            pBackgroundBitmap->Resize( &size );
        }
    }

    animator.SetHwnd( hwnd );

    ShowWindow(hwnd, SW_SHOW);

    return TRUE;
}

/**************************************************************************\
* ConfigureForGdi
*
* Creates an hdc for the window
*
\**************************************************************************/

BOOL
MTKWIN::ConfigureForGdi()
{
    if( hdc )
        // already configured
        return TRUE;

    // Figure window to get hdc from
#if 0
    HWND hwndForHdc = hwnd ? hwnd : psswParent ? psswParent->hwnd : NULL;
#else
    HWND hwndForHdc = hwnd;
#endif

    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
MTKWIN::Config( UINT glConfig )
{
    return Config( glConfig, NULL );
}

BOOL
MTKWIN::Config( UINT glConfig, PVOID pConfigData )
{
    if( hrc )
        // Already configured...
        return TRUE;

    if( ConfigureForGdi() &&
        (hrc = hrcSetupGL( glConfig, pConfigData )) )
        return TRUE;

    SS_WARNING( "SSW::ConfigureForGL failed\n" );
    return FALSE;
}

/**************************************************************************\
* hrcSetupGL
*
* Setup OpenGL.
*
\**************************************************************************/

#define NULL_RC ((HGLRC) 0)

HGLRC 
MTKWIN::hrcSetupGL( UINT glConfig, PVOID pData )
{
    HGLRC hrc;
    HDC hgldc;
    int pfFlags = 0;
    PIXELFORMATDESCRIPTOR pfd = {0};

    // Setup pixel format flags

    // Double buffering can either be done with a double-buffered pixel
    // format, or by using a local back buffer bitmap that tracks the window
    // size.  The latter allows us more control with buffer swaps.

    bDoubleBuf = glConfig & MTK_DOUBLE;
    BOOL bBitmapBackBuf = glConfig & MTK_BITMAP;
    if( bDoubleBuf ) {
        if( bBitmapBackBuf ) 
            pfFlags |= SS_BITMAP_BIT;
        else
            pfFlags |= SS_DOUBLEBUF_BIT;
    }
    if( glConfig & MTK_DEPTH )
        pfFlags |= SS_DEPTH32_BIT;
    if( glConfig & MTK_DEPTH16 )
        pfFlags |= SS_DEPTH16_BIT;
    if( glConfig & MTK_ALPHA )
        pfFlags |= SS_ALPHA_BIT;
    

    // If preview mode or config mode, don't allow pixel formats that need
    // the system palette, as this will create much ugliness.
    if( !bFullScreen )
        pfFlags |= SS_NO_SYSTEM_PALETTE_BIT;

//mf: don't really need pixel format for window if using back bitmap method,
// but if user wants to draw to front buffer, then we'll need it.  So, we'll
// always set it here.
    if( !SSU_SetupPixelFormat( hdc, pfFlags, &pfd ) )
        return NULL_RC;

//mf: ???
    // Update 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 )
        pfFlags |= SS_GENERIC_UNACCELERATED_BIT;

    if( SSU_bNeedPalette( &pfd ) ) {
        // Note: even if bStretch, need to set up palette here so they match
        if( !gpssPal ) {
            SS_PAL *pssPal;
#if 1
            BOOL bTakeOverPalette = bFullScreen ? TRUE : FALSE;
#else
//mf: For next rev, we don't have to force palette takeover - but it will
// automically be invoked for any case like MCD, etc.
            BOOL bTakeOverPalette = FALSE;
#endif

            // 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( bFullScreen )
                pssPal->paletteManageProc = FullScreenPaletteManageProc;
            else
                // use regular palette manager proc
                pssPal->paletteManageProc = PaletteManageProc;
            gpssPal = pssPal;
        }
        // Realize the global palette in this window
        //mf: assume we're realizing in foreground
        HWND hwndPal = hwnd;
        if( hwndPal )
            gpssPal->Realize( hwndPal, hdc, FALSE );
    }

    if( bBitmapBackBuf ) {
        pBackBitmap = new MTKBMP( hdc );
        if( !pBackBitmap ) {
            SS_WARNING( "MTKWIN::hrcSetupGL : couldn't create back bitmap\n" );
            return NULL_RC;
        }
        // Set bitmap's size to the window's size
        pBackBitmap->Resize( &size );
        hgldc = pBackBitmap->hdc;
        // Setup pixelformat
        if( !SSU_SetupPixelFormat( hgldc, pfFlags, &pfd ) )
            return NULL_RC;
        // If window needed a palette, so does the bitmap...
        if( gpssPal )
            SSDIB_UpdateColorTable( hgldc, hdc, gpssPal->hPal );
    } else {
        hgldc = hdc;
    }

    // Create a new hrc
    hrc = wglCreateContext(hgldc);

    if( !hrc || !wglMakeCurrent(hgldc, hrc) ) {
        SS_WARNING( "SSW::hrcSetupGL : hrc context failure\n" );
        return NULL_RC;
    }

    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).

    gGLCaps.Query();

    // Send another reshape msg to the app, since the first one on window
    // create would have been sent before we had an rc
    Reshape();

    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
MTKWIN::MakeCurrent()
{
    if( ! wglMakeCurrent( hdc, hrc ) )
        SS_WARNING( "SSW::MakeCurrent : wglMakeCurrent failure\n" );
}

// Callback functions:

/******************************Public*Routine******************************\
* ss_ReshapeFunc
*
\**************************************************************************/

void 
MTKWIN::SetReshapeFunc(MTK_RESHAPEPROC Func)
{
    ReshapeFunc = Func;
}

/******************************Public*Routine******************************\
* ss_RepaintFunc
*
\**************************************************************************/

void 
MTKWIN::SetRepaintFunc(MTK_REPAINTPROC Func)
{
    RepaintFunc = Func;
}

void 
MTKWIN::SetDisplayFunc(MTK_DISPLAYPROC Func)
{
    DisplayFunc = Func;
}

/******************************Public*Routine******************************\
* SetAnimateFunc
*
\**************************************************************************/

void 
MTKWIN::SetAnimateFunc(MTK_ANIMATEPROC Func )
{
    animator.SetFunc( Func );
    // If we are in msg loop and Func is non-NULL, have to make sure 
    // animator starts again... (awkward).  If animator was already started,
    // this will do nothing
    if( execRefCount && Func )
        animator.Start();
}

/******************************Public*Routine******************************\
* Animate
*
* Call the animation function
*
* If animate mode is interval (as opposed to continuous),
* animate the number of supplied frames.  The animation count is decremented
* by the WndProc processing the WM_TIMER messages.  Exits the msg loop when
* the desired number fo frames has been animated.
*
\**************************************************************************/

//mf: had to rename from Animate to mtkAnimate due to name conflicts at link
// time

void
MTKWIN::mtkAnimate()
{
    if( ! animator.Draw() )
        Return();
}


/******************************Public*Routine******************************\
* SetAnimateMode
*
*
\**************************************************************************/

void
MTKWIN::SetAnimateMode( UINT mode, float *fParam )
{
    animator.SetMode( mode, fParam );
}

/******************************Public*Routine******************************\
* Exec
*
* Starts the message loop for the window.
*
* If an animation has been requested prior to this call, then a new animation
* timer is setup.  This msg loop can terminate in the following ways :
*   1) The window is closed
*   2) An interval animation was requested, and the required number of frames
*      have been drawn
*   3) The user calls MTKWIN::Return(), which will cause the MTKWIN::Exec()
*      call to return
*
* For now :
* Returns TRUE on normal termination, FALSE if the window it's animating in
* gets closed.
*
\**************************************************************************/

BOOL
MTKWIN::Exec()
{
    // If user is already in here, get out
    if( execRefCount )
        return TRUE;
    execRefCount++;

    // Stop any existing timer (this will flush WM_TIMER msg's)
    animator.Stop();

    // Start new animation timer (if animator modes are set)
    animator.Start();

    MSG msg;
    BOOL bNotQuitMsg;
    while( bNotQuitMsg = GetMessage( &msg, hwnd, 0, 0 ) )
    {
        if( msg.message == MTK_WM_RETURN ) {
            // User or mtk wants to terminate msg loop and return control
            // (mf: could pick up return parameter here...)
//            SS_DBGPRINT1( "MTKWIN::Exec got WM_RETURN for %p\n", this );
            break;
        }
//mf: ? better way of doing this ?
        else if( ! msg.hwnd ) {
            // Window has been destroyed, get out !
            SS_DBGPRINT( "MTKWIN::Exec : hwnd = 0, forcing msg loop exit\n" );
            return FALSE;
        }
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }

    animator.Stop();

    execRefCount--;

    if( bNotQuitMsg )
        return TRUE;
    else {
        SS_DBGPRINT1( "MTKWIN::Exec got WM_QUIT for %p\n", this );
        return FALSE;
    }
}

/******************************Public*Routine******************************\
* Return
*
* Called by the user when they want to return from the Exec() call which
* started the message loop.
*
* mf: could include parameter here
*
\**************************************************************************/

void
MTKWIN::Return() 
{
    animator.Stop();
    PostMessage( hwnd, MTK_WM_RETURN, 0, 0l );
}

void 
MTKWIN::SetMouseMoveFunc(MTK_MOUSEMOVEPROC Func)
{
    MouseMoveFunc = Func;
}

void 
MTKWIN::SetMouseUpFunc(MTK_MOUSEUPPROC Func)
{
    MouseUpFunc = Func;
}

void 
MTKWIN::SetMouseDownFunc(MTK_MOUSEDOWNPROC Func)
{
    MouseDownFunc = Func;
}

void 
MTKWIN::SetKeyDownFunc(MTK_KEYDOWNPROC Func)
{
    KeyDownFunc = Func;
}

void 
MTKWIN::GetMouseLoc( int *x, int *y )
{
    POINT Point;

    *x = 0;
    *y = 0;

    GetCursorPos(&Point);

    /*
     *  GetCursorPos returns screen coordinates,
     *  we want window coordinates
     */

    *x = Point.x - pos.x;
    *y = Point.y - pos.y;
}

void
MTKWIN::Close()
{
    DestroyWindow( hwnd );
}

/******************************Public*Routine******************************\
* ss_FinishFunc
*
\**************************************************************************/

void 
MTKWIN::SetFinishFunc(MTK_FINISHPROC Func)
{
    FinishFunc = Func;
}

/**************************************************************************\
* Resize
*
* Resize wrapper
*
* Called in response to WM_SIZE.
*
\**************************************************************************/

void
MTKWIN::Resize( int width, int height )
{
    size.width  = width;
    size.height = height;

    if( pBackBitmap )
        pBackBitmap->Resize( &size );
    if( pBackgroundBitmap )
        pBackgroundBitmap->Resize( &size );
    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
MTKWIN::Repaint( BOOL bCheckUpdateRect )
{
    if( !hwnd )
        return;

    RECT rect, *pRect = NULL;

    if( bCheckUpdateRect ) {
        GetUpdateRect( hwnd, &rect, FALSE );
//mf
    SS_DBGPRINT4( "MTKWIN::Repaint rect: %d - %d, %d - %d\n", rect.left, rect.right,
                   rect.top, rect.bottom );
        // mf: Above supposed to return NULL if rect is all 0's, 
        // but this doesn't happen
        if( NULL_UPDATE_RECT( &rect ) )
            return;
        pRect = ▭
    }

    // transparent window thing
    if( pBackgroundBitmap ) {
        if( !pRect ) {
            // UpdateBg doesn't handle null rect
            pRect = ▭
            GetClientRect( hwnd, pRect );
        }
        UpdateBackgroundBitmap( pRect );
    }

#if 0
    if( RepaintFunc )
        (*RepaintFunc)( pRect );
#else
#if 0
    Display();
#else
//mf: test: ? help bg update problem ?? nope, din't seem to...
    MSG Message;
    if (!PeekMessage(&Message, hwnd, MTK_WM_REDRAW, MTK_WM_REDRAW, PM_NOREMOVE) )
    {
        PostMessage( hwnd, MTK_WM_REDRAW, 0, 0l );
    }
#endif
#endif
}

void
MTKWIN::Display()
{
    if( DisplayFunc )
        (*DisplayFunc)();
}

//mf: not using these in current scheme, although might if use 'ss' mode
#if 0
/**************************************************************************\
* UpdateWindow
*
* Update the window
*
* Currently 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
MTKWIN::UpdateWindow()
{ 
    if( !AnimateFunc )
        return;

    // bDoubleBuf and pStretch should be mutually exclusive...

    if( bDoubleBuf ) {
        UpdateDoubleBufWin();
    } else {
//mf: ? where's the clearing here ?  (true, no one uses this path...)
#if 0
        (*AnimateFunc)( DataPtr );
#else
        (*AnimateFunc)();
#endif
    }
}

/**************************************************************************\
* UpdateDoubleBufWin
*
* This is used when moving a double buffered window around.  It will
* work for all configurations.
*
\**************************************************************************/

void
MTKWIN::UpdateDoubleBufWin()
{ 
    RECT updateRect;

    // Update the back buffer

#if 0
    (*AnimateFunc)( DataPtr );
#else
    (*AnimateFunc)();
#endif

    // Swap to the new window position
    SwapBuffers( hdc );
}
#endif

/**************************************************************************\
* GetSSWindowRect
*
* Return window position and size in supplied RECT structure
*
* - This rect is relative to the parent
\**************************************************************************/

void
MTKWIN::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
MTKWIN::GLPosY()
{
//mf: !!!
#if 0
    return psswParent->size.height - size.height - pos.y;
#else
    return 0;
#endif
}


/**************************************************************************\
* SwapBuffers
*
\**************************************************************************/

//mf: name problem...
void
MTKWIN::mtkSwapBuffers()
{
    if( bDoubleBuf ) {
        if( pBackBitmap )
            CopyBackBuffer();
        else
            SwapBuffers( hdc );
    }
}

/**************************************************************************\
*
*
\**************************************************************************/

void
MTKWIN::Flush()
{
    glFlush();
    if( bDoubleBuf ) {
        mtkSwapBuffers();
    }
}

/**************************************************************************\
* CopyBackBuffer
*
* Like SwapBuffers, but copies from local bitmap to front buffer
*
* Also capable of copying over 1 or more rects of the bitmap, rather than the
* whole thing. mf: Might need local implementation of swaphintrect here, to
* collect and reduce the rects
\**************************************************************************/

void
MTKWIN::CopyBackBuffer()
{
    if( !pBackBitmap )
        return;

    // Do a BitBlt from back buffer to the window (may as well put stretch in
    // here ?

    if( (size.width == pBackBitmap->size.width) &&
        (size.height == pBackBitmap->size.height) ) // buffers same size
    {
        BitBlt(hdc, 0, 0, size.width, size.height,
               pBackBitmap->hdc, 0, 0, SRCCOPY);
    }
    else
    {
        SS_WARNING( "MTKWIN::CopyBackBuffer: bitmap size mismatch\n" );
        StretchBlt(hdc, 0, 0, 
                   size.width, size.height,
                   pBackBitmap->hdc, 0, 0, 
                   pBackBitmap->size.width, pBackBitmap->size.height,
                   SRCCOPY);
    }
    GdiFlush();
}


/**************************************************************************\
* UpdateBackgroundBitmap
*
* Updates the background bitmap with screen bits
*
\**************************************************************************/

void
MTKWIN::UpdateBackgroundBitmap( RECT *pRect )
{
    if( !pBackgroundBitmap ) {
        SS_WARNING( "MTKWIN::UpdateBackgroundBitmap : No background bitmap\n" );
        return;
    }

//  mf:!!!  handle update rect parameter
    MTKBMP *pBmpDest = pBackgroundBitmap;

    // Get a screen DC
    HDC hdcScreen = GetDC( NULL );

#if DBG
    if( !hdcScreen ) {
        SS_WARNING( "MTKWIN::UpdateBackgroundBitmap : failed to get screen hdc\n" );
        return;
    }
#endif

//mf
#if 0
    SS_DBGPRINT4( "MTKWIN::UpdateBackgroundBitmap : %d - %d, %d - %d\n", pRect->left, pRect->right,
                   pRect->top, pRect->bottom );
#endif
    // Calc the screen origin of the window
    RECT screenRect = {0, 0 }; // just need left and top points
    MapWindowPoints( hwnd, NULL, (POINT *) &screenRect, 2 );

    // Offset screenRect with the supplied rect
    screenRect.left += pRect->left;
    screenRect.top += pRect->top;
    // Set update size
//mf: thought I should have to add 1 here, but I guess pRect is non-inclusive...
    ISIZE updateSize = { pRect->right - pRect->left,
                         pRect->bottom - pRect->top };

    if( (size.width == pBmpDest->size.width) &&
        (size.height == pBmpDest->size.height) ) // buffers same size
    {
        BitBlt(pBmpDest->hdc, 
               pRect->left, pRect->top, 
               updateSize.width, updateSize.height,
               hdcScreen, 
               screenRect.left, screenRect.top, SRCCOPY);
    }
    else
    {
#if 0
//mf: ignore this for now
        // Shouldn't happen, since BackgroundBitmap tracks window size
        StretchBlt(pBmpDest->hdc, 0, 0, 
                   pBmpDest->size.width, pBmpDest->size.height,
                   hdcScreen, screenRect.left, screenRect.top, 
                   size.width, size.height,
                   SRCCOPY);
#else
        SS_WARNING( "MTKWIN::UpdateBackgroundBitmap : bitmap size mismatch\n" );
#endif
    }
    GdiFlush();
}

/**************************************************************************\
* ClearToBackground
*
* Copy from the background bitmap to the window.  If the window is doublebuf,
* then we copy to the backbuffer instead of the window.
*
\**************************************************************************/

void
MTKWIN::ClearToBackground()
{
    if( !pBackgroundBitmap ) {
        SS_WARNING( "MTKWIN::ClearToBackgournd : No background bitmap\n" );
        return;
    }

    MTKBMP *pBmpSrc = pBackgroundBitmap;

    HDC hdcDest;
    if( bDoubleBuf ) {
        if( !pBackBitmap )
            return;
//mf: assumption here that backbitmap size is same as window
        hdcDest = pBackBitmap->hdc;
    } else
        hdcDest = hdc;

    if( (size.width == pBmpSrc->size.width) &&
        (size.height == pBmpSrc->size.height) ) // buffers same size
    {
        BitBlt(hdcDest, 0, 0, size.width, size.height,
               pBmpSrc->hdc, 0, 0, SRCCOPY);
    }
    else
    {
        StretchBlt(hdcDest, 0, 0, 
                   size.width, size.height,
                   pBmpSrc->hdc, 0, 0, 
                   pBmpSrc->size.width, pBmpSrc->size.height,
                   SRCCOPY);
    }
    GdiFlush();
}


/**************************************************************************\
* 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
MTKWIN::Reshape()
{
    // Point to size of window, or bitmap if it has one
    ISIZE *pSize = &size;

    // If the window has an hrc, set default viewport
//mf: so app doesn't have to worry about it ?

    if( hrc ) {
        glViewport( 0, 0, pSize->width, pSize->height );
    }

    if( ReshapeFunc ) {
#if 0
        (*ReshapeFunc)( pSize->width, pSize->height, DataPtr );
#else
        (*ReshapeFunc)( pSize->width, pSize->height );
#endif
    }
}

/******************************Public*Routine******************************\
* GdiClear
*
* Clears window using Gdi FillRect
\**************************************************************************/

void
MTKWIN::GdiClear()
{
    if( !hdc )
        return;

    RECT rect;

    GetClientRect( hwnd, &rect );

//mf: rect is exclusive, so shouldn't we have to add 1 ?
    FillRect( hdc, &rect, ghbrbg );
    GdiFlush();
}

//mf: unicode...
void
MTKWIN::SetTitle( char *title )
{
    SetWindowText( hwnd, title );
}