//------------------------------------------------------------------------------
// File: DDrawObj.cpp
//
// Desc: DirectShow sample code - implementation of DDrawObject class.
//
// Copyright (c) 1993 - 2000, Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------


#include <streams.h>
#include <windows.h>

#include "DDrawObj.h"
#include "VidPlay.h"


//
// CDDrawObject constructor
//
CDDrawObject::CDDrawObject(HWND hWndApp)
{
    DbgLog((LOG_TRACE, 3, TEXT("CDDrawObject c-tor entered"))) ;
    
    m_pDDObject = NULL ;
    m_pPrimary = NULL ;
    m_pBackBuff = NULL ;
    
    // Default colors to be used for filling
    m_dwScrnColor = RGB(0, 0, 0) ;
    m_dwVideoKeyColor = RGB(255, 0, 255) ;
    
    // Rects for the screen area and video area
    SetRect(&m_RectScrn, 0, 0, 0, 0) ;
    SetRect(&m_RectVideo, 0, 0, 0, 0) ;
    
    // some flags, count, messages for buffer flipping
    m_bInExclMode = FALSE ;
    m_iCount = 0 ;
    m_bFrontBuff = TRUE ;
    m_szFrontMsg = "Front Buffer" ;
    m_szBackMsg  = "Back Buffer" ;
    m_szDirection = "Press the 'Arrow' keys to move the ball. Hit 'Esc' to stop playback." ;
    
    // Create brush and pen for drawing the filled ball
    LOGBRUSH  lb ;
    lb.lbStyle = BS_HATCHED ;
    lb.lbColor = RGB(0, 255, 0) ;
    lb.lbHatch = HS_CROSS ;
    m_hBrush = CreateBrushIndirect(&lb) ;
    ASSERT(NULL != m_hBrush) ;
    
    m_hPen = CreatePen(PS_SOLID, 2, RGB(0, 0, 255)) ;
    ASSERT(NULL != m_hPen) ;

    HRESULT  hr ;
    m_pOverlayCallback = (IDDrawExclModeVideoCallback *) new COverlayCallback(this, hWndApp, &hr) ;
    ASSERT(SUCCEEDED(hr)) ;
    m_bOverlayVisible = FALSE ;  // don't draw color key until it's OK-ed by OverlayMixer
}


//
// CDDrawObject destructor
//
CDDrawObject::~CDDrawObject(void)
{
    DbgLog((LOG_TRACE, 3, TEXT("CDDrawObject d-tor entered"))) ;

    // Let go of the overlay notification callback object now
    if (m_pOverlayCallback)
        m_pOverlayCallback->Release() ;  // done with callback object now

    // delete the pen and brush we created for drawing the ball
    if (m_hPen)
        DeleteObject(m_hPen) ;
    if (m_hBrush)
        DeleteObject(m_hBrush) ;
    
    if (m_pBackBuff)
        m_pBackBuff->Release() ;
    if (m_pPrimary)
        m_pPrimary->Release() ;
    
    if (m_pDDObject)
        m_pDDObject->Release() ;
    
    DbgLog((LOG_TRACE, 3, TEXT("CDDrawObject d-tor exiting..."))) ;
}


//
// CDDrawObject::Initialize(): Just creates a DDraw object.
//
BOOL CDDrawObject::Initialize(HWND hWndApp)
{
    DbgLog((LOG_TRACE, 5, TEXT("CDDrawObject::Initialize() entered"))) ;
    
    HRESULT hr = DirectDrawCreate(NULL, &m_pDDObject, NULL) ;
    if (FAILED(hr) || NULL == m_pDDObject)
    {
        MessageBox(hWndApp, 
            TEXT("Can't create a DirectDraw object.\nPress OK to end the app."), 
            TEXT("Error"), MB_OK | MB_ICONSTOP) ;
        return FALSE ;
    }
    
    return TRUE;
}


#define SCRN_WIDTH    800
#define SCRN_HEIGHT   600
#define SCRN_BITDEPTH   8

//
// CDDrawObject::StartExclusiveMode(): Gets into fullscreen exclusive mode, sets
// display mode to 800x600x8, creates a flipping primary surface with one back 
// buffer, gets the surface size and inits some variables.
//
// NOTE: Please don't bother about the "#ifndef NOFLIP"s.  They were there
// for testing.
//
HRESULT CDDrawObject::StartExclusiveMode(HWND hWndApp)
{
    DbgLog((LOG_TRACE, 5, TEXT("CDDrawObject::StartExclusiveMode() entered"))) ;
    
    HRESULT         hr ;
    DDSURFACEDESC   ddsd ;
    DDSCAPS         ddscaps ;
    
    if (! Initialize(hWndApp) )
        return E_FAIL ;

    // Get into fullscreen exclusive mode
#ifndef NOFLIP
    hr = m_pDDObject->SetCooperativeLevel(hWndApp,
        DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT) ;
    if (FAILED(hr))
    {
        MessageBox(hWndApp, 
            TEXT("SetCooperativeLevel() failed to go into exclusive mode"), 
            TEXT("Error"), MB_OK | MB_ICONSTOP) ;
        return hr ;
    }
#else
    hr = m_pDDObject->SetCooperativeLevel(hWndApp, DDSCL_NORMAL) ;
    if (FAILED(hr))
    {
        MessageBox(hWndApp, 
            TEXT("SetCooperativeLevel() failed to go into normal mode"), 
            TEXT("Error"), MB_OK | MB_ICONSTOP) ;
        return hr ;
    }
#endif // NOFLIP
    
#ifndef NOFLIP
    hr = m_pDDObject->SetDisplayMode(SCRN_WIDTH, SCRN_HEIGHT, SCRN_BITDEPTH) ;
    if (FAILED(hr))
    {
        DbgLog((LOG_ERROR, 0, TEXT("ERROR: SetDisplayMode(800x600x8) failed (Error 0x%lx)"), hr)) ;
        MessageBox(hWndApp, TEXT("SetDisplayMode(640, 480, 8) failed"), 
            TEXT("Error"), MB_OK | MB_ICONSTOP) ;
        return hr ;
    }
#endif // NOFLIP
    
    // Create the primary surface with 1 back buffer
    ZeroMemory(&ddsd, sizeof(ddsd)) ;
    ddsd.dwSize = sizeof(ddsd) ;
    
#ifndef NOFLIP
    ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT ;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
        DDSCAPS_FLIP | 
        DDSCAPS_COMPLEX ;
    ddsd.dwBackBufferCount = 1 ;
#else
    ddsd.dwFlags = DDSD_CAPS ;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE ;
#endif  // NOFLIP
    
    hr = m_pDDObject->CreateSurface(&ddsd, &m_pPrimary, NULL) ;
    if (FAILED(hr))
    {
#ifndef NOFLIP
        MessageBox(hWndApp, TEXT("CreateSurface(Primary, flip(1), complex) failed"), 
            TEXT("Error"), MB_OK | MB_ICONSTOP) ;
#else
        MessageBox(hWndApp, TEXT("CreateSurface(Primary, noflip, no backbuffer) failed"), 
            TEXT("Error"), MB_OK | MB_ICONSTOP) ;
#endif // NOFLIP
        return hr ;
    }
    
#ifndef NOFLIP
    // Get a pointer to the back buffer
    ddscaps.dwCaps = DDSCAPS_BACKBUFFER ;
    hr = m_pPrimary->GetAttachedSurface(&ddscaps, &m_pBackBuff) ;
    if (FAILED(hr))
    {
        MessageBox(hWndApp, TEXT("GetAttachedSurface() failed to get back buffer"),
            TEXT("Error"), MB_OK | MB_ICONSTOP) ;
        return hr ;
    }
#endif // NOFLIP
    
    // Get the screen size and save it as a rect
    ZeroMemory(&ddsd, sizeof(ddsd)) ;
    ddsd.dwSize = sizeof(ddsd) ;
    hr = m_pPrimary->GetSurfaceDesc(&ddsd) ;
    if (! (SUCCEEDED(hr) && (ddsd.dwFlags & DDSD_WIDTH) && (ddsd.dwFlags & DDSD_HEIGHT)) )
    {
        MessageBox(hWndApp, TEXT("GetSurfaceDesc() failed to get surface width & height"),
            TEXT("Error"), MB_OK | MB_ICONSTOP) ;
        return hr ;
    }
    
    SetRect(&m_RectScrn, 0, 0, ddsd.dwWidth, ddsd.dwHeight) ;
    
    // Reset some buffer drawing flags, values etc.
    m_iCount = 0 ;
    m_bFrontBuff = TRUE ;
    m_bInExclMode = TRUE ;
    
    return S_OK ;
}


//
// CDDrawObject::DrawOnSurface(): Private method to draw stuff on back buffer
//
void CDDrawObject::DrawOnSurface(LPDIRECTDRAWSURFACE pSurface)
{
    DbgLog((LOG_TRACE, 5, TEXT("CDDrawObject::DrawOnSurface() entered"))) ;
    
    HDC     hDC ;
    TCHAR   achBuffer[30] ;
    
    if (DD_OK == pSurface->GetDC(&hDC))
    {
        // write fonr/back to show buffer flipping
        SetBkColor(hDC, RGB(0, 0, 255)) ;
        SetTextColor(hDC, RGB(255, 255, 0)) ;
        wsprintf(achBuffer, " %-12s: %6.6d ", m_bFrontBuff ? m_szFrontMsg : m_szBackMsg, m_iCount) ;
        TextOut(hDC, 20, 10, achBuffer, lstrlen(achBuffer)) ;
        
        // Write the instructions
        SetBkColor(hDC, RGB(0, 0, 0)) ;
        SetTextColor(hDC, RGB(0, 255, 255)) ;
        TextOut(hDC, m_RectScrn.left + 100, m_RectScrn.bottom - 20, m_szDirection, lstrlen(m_szDirection)) ;
        
        // Draw the ball on screen now
        HBRUSH hBrush = (HBRUSH) SelectObject(hDC, m_hBrush) ; // select special brush
        HPEN   hPen   = (HPEN) SelectObject(hDC, m_hPen) ;     // select special pen 
        Ellipse(hDC, m_iBallCenterX - BALL_RADIUS, m_iBallCenterY - BALL_RADIUS, 
            m_iBallCenterX + BALL_RADIUS, m_iBallCenterY + BALL_RADIUS) ;
        SelectObject(hDC, hBrush) ;  // restore original brush
        SelectObject(hDC, hPen) ;    // restore original pen
        
        pSurface->ReleaseDC(hDC) ;  // done now; let go of the DC
    }
}


//
// CDDrawObject::StopExclusiveMode(): Releases primary buffer, gets out exclusive
// mode, resets some variables/flags.
//
HRESULT CDDrawObject::StopExclusiveMode(HWND hWndApp)
{
    DbgLog((LOG_TRACE, 5, TEXT("CDDrawObject::StopExclusiveMode() entered"))) ;
    
    HRESULT hr ;
    
    if (m_pBackBuff)
    {
        hr = m_pBackBuff->Release() ;  // release back buffer
        m_pBackBuff = NULL ;  // no back buffer anymore
    }

    // Release the primary surface (and attached buffer with it)
    if (m_pPrimary)
    {
        hr = m_pPrimary->Release() ;  // release primary surface
        m_pPrimary = NULL ;
    }
    
    // Get out of fullscreen exclusive mode
    if (m_pDDObject)
    {
        hr = m_pDDObject->SetCooperativeLevel(hWndApp, DDSCL_NORMAL) ;
        if (FAILED(hr))
        {
            MessageBox(hWndApp, 
                TEXT("SetCooperativeLevel() failed to go back to normal mode"), 
                TEXT("Error"), MB_OK | MB_ICONSTOP) ;
            return hr ;
        }

        m_pDDObject->Release() ;  // now release the DDraw object
        m_pDDObject = NULL ;      // no more DDraw object
    }
    
    // Reset the color key and screen, video rects.
    m_dwVideoKeyColor = RGB(255, 0, 255) ;
    SetRect(&m_RectScrn, 0, 0, 0, 0) ;
    SetRect(&m_RectVideo, 0, 0, 0, 0) ;
    
    m_bInExclMode = FALSE ;
    
    return S_OK ;
}


//
// CDDrawObject::ConvertColorRefToPhysColor(): Converts a COLORREF to a
// DWORD physical color value based on current display mode.
//
// NOTE: This method is useful ONLY if you want to get the COLORREF from
// OverlayMixer (rather than the physical color as a DWORD) and convert
// it yourself.  But OverlayMixer does it for you anyway; so the utility
// of this method is quite minimal.
//
HRESULT CDDrawObject::ConvertColorRefToPhysColor(COLORREF rgb, DWORD *pdwPhysColor)
{
    HRESULT         hr ;
    COLORREF        rgbCurr ;
    HDC             hDC ;
    DWORD           dw = CLR_INVALID ;
    DDSURFACEDESC   ddsd ;
    
    ASSERT(rgb != CLR_INVALID) ;
    
    //  use GDI SetPixel to color match for us
    hr = m_pBackBuff->GetDC(&hDC) ;
    if (DD_OK != hr)
    {
        DbgLog((LOG_ERROR, 0, TEXT("m_pBackBuff->GetDC() failed (Error 0x%lx)"), hr)) ;
        return hr ;
    }
    rgbCurr = GetPixel(hDC, 0, 0) ;             // save current pixel value
    SetPixel(hDC, 0, 0, rgb) ;               // set our value
    m_pBackBuff->ReleaseDC(hDC) ;
    
    // now lock the surface so we can read back the converted color
    ZeroMemory(&ddsd, sizeof(ddsd)) ;
    ddsd.dwSize = sizeof(ddsd) ;
    while (DDERR_WASSTILLDRAWING == (hr = m_pBackBuff->Lock(NULL, &ddsd, 0, NULL)))  // until unlocked...
        ;               // ...just wait
    
    if (DD_OK == hr)
    {
        // get DWORD
        *pdwPhysColor = *(DWORD *) ddsd.lpSurface ;
        
        // mask it to bpp
        if (ddsd.ddpfPixelFormat.dwRGBBitCount < 32)
            *pdwPhysColor &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount) - 1 ;
        m_pBackBuff->Unlock(NULL) ;
    }
    else
    {
        DbgLog((LOG_ERROR, 0, TEXT("m_pBackBuff->Lock() failed (Error 0x%lx)"), hr)) ;
        return hr ;
    }
    
    //  now restote the pixel to the color that was there
    hr = m_pBackBuff->GetDC(&hDC) ;
    if (DD_OK != hr)
    {
        DbgLog((LOG_ERROR, 0, TEXT("m_pBackBuff->GetDC() failed (Error 0x%lx)"), hr)) ;
        return hr ;
    }
    SetPixel(hDC, 0, 0, rgbCurr) ;
    m_pBackBuff->ReleaseDC(hDC) ;
    
    return S_OK ;
}


#if 0
//
// CDDrawObject::SetColorKey(): Creates a DWORD physical color out COLORREF.
// 
// NOTE: This method is useful only if you want to do the conversion. We have
// defined an inline version in ddrawobj.h file.
//
HRESULT CDDrawObject::SetColorKey(COLORKEY *pColorKey)
{
    if (NULL == pColorKey)
        return E_INVALIDARG ;
    
    DWORD    dwPhysColor ;
    HRESULT  hr = ConvertColorRefToPhysColor(pColorKey->LowColorValue, &dwPhysColor) ;
    if (FAILED(hr))
        return hr ;
    
    m_dwVideoKeyColor = dwPhysColor ;
    return S_OK ;
}
#endif // #if 0


//
// CDDrawObject::FillSurface(): Private method to fill the back buffer with black
// color and put the color key in teh video area.
//
// NOTE: Re-filling the back buffer every time is required ONLY IF the video 
// position keeps changing. Otherwise setting it once should be fine.
// 
HRESULT CDDrawObject::FillSurface(IDirectDrawSurface *pDDSurface)
{
    DbgLog((LOG_TRACE, 5, TEXT("CDDrawObject::FillSurface() entered"))) ;

    if (NULL == pDDSurface)
        return E_INVALIDARG ;
    
    // Repaint the whole specified surface
    HRESULT  hr ;
    DDBLTFX  ddFX ;
    ZeroMemory(&ddFX, sizeof(ddFX)) ;
    ddFX.dwSize = sizeof(ddFX) ;
    ddFX.dwFillColor = m_dwScrnColor ;
    
    hr = pDDSurface->Blt(&m_RectScrn, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddFX) ;
    if (FAILED(hr))
    {
        DbgLog((LOG_ERROR, 0, TEXT("pDDSurface->Blt for screen failed (Error 0x%lx)"), hr)) ;
        DbgLog((LOG_ERROR, 0, TEXT("Destination Rect: (%ld, %ld, %ld, %ld), Color = 0x%lx"), 
            m_RectScrn.left, m_RectScrn.top, m_RectScrn.right, m_RectScrn.bottom, m_dwScrnColor)) ;
        return hr ;
    }
    
    // Draw color key on the video area of given surface, ONLY IF we are supposed
    // to paint color key in the video area.
    if (m_bOverlayVisible)
    {
        ddFX.dwFillColor = m_dwVideoKeyColor ;
        hr = pDDSurface->Blt(&m_RectVideo, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddFX) ;
        if (FAILED(hr))
        {
            DbgLog((LOG_ERROR, 0, TEXT("pDDSurface->Blt for video failed (Error 0x%lx)"), hr)) ;
            DbgLog((LOG_ERROR, 0, TEXT("Destination Rect: (%ld, %ld, %ld, %ld), Color = 0x%lx"), 
                m_RectVideo.left, m_RectVideo.top, m_RectVideo.right, m_RectVideo.bottom, 
                m_dwVideoKeyColor)) ;
            return hr ;
        }
    }
    else
        DbgLog((LOG_TRACE, 5, TEXT("Color keying of video area skipped"))) ;
    
    return S_OK ;
}


//
// CDDrawObject::UpdateAndFlipSurfaces(): Prepares the back buffer and flips.
//
HRESULT CDDrawObject::UpdateAndFlipSurfaces(void)
{
    DbgLog((LOG_TRACE, 5, TEXT("CDDrawObject::UpdateAndFlipSurfaces() entered"))) ;
    
    // Draw screen and color key on the current back buffer
    HRESULT  hr = FillSurface(m_pBackBuff) ;
    if (FAILED(hr))
    {
        DbgLog((LOG_ERROR, 1, TEXT("UpdateAndFlipSurfaces() skipped as FillSurface() failed"), hr)) ;
        return hr ;   // or return S_OK??
    }
    
    IncCount() ;                    // increment flip count first
    m_bFrontBuff = !m_bFrontBuff ;  // toggle flag
    DrawOnSurface(m_pBackBuff) ;  // draw next text on the back buffer
    
    // Keep trying to flip the buffers until successful
    while (1)
    {
        hr = m_pPrimary->Flip(NULL, 0) ;  // flip the surfaces
        if (DD_OK == hr)  // success!!
        {
            break ;
        }
        if (DDERR_SURFACELOST == hr)   // surface lost; try to restore
        {
            DbgLog((LOG_TRACE, 5, TEXT("DirectDraw surface was lost. Trying to restore..."))) ;
            hr = m_pPrimary->Restore() ;
            if (DD_OK != hr)  // couldn't restore surface
            {
                DbgLog((LOG_ERROR, 0, TEXT("IDirectDrawSurface::Restore() failed (Error 0x%lx)"), hr)) ;
                break ;
            }
        }
        if (DDERR_WASSTILLDRAWING != hr)  // some weird error -- bail out
        {
            DbgLog((LOG_ERROR, 0, TEXT("IDirectDrawSurface::Flip() failed (Error 0x%lx)"), hr)) ;
            break ;
        }
    }
    
    return hr ;
}


//
// CDDrawObject::SetVideoPosition(): Receives the video position in terms of screen
// coordinates.
//
void CDDrawObject::SetVideoPosition(DWORD dwVideoLeft, DWORD dwVideoTop, 
                                    DWORD dwVideoWidth, DWORD dwVideoHeight)
{
    DbgLog((LOG_TRACE, 5, TEXT("CDDrawObject::SetVideoPosition() entered"))) ;
    
    SetRect(&m_RectVideo, dwVideoLeft, dwVideoTop, dwVideoLeft + dwVideoWidth,
        dwVideoTop + dwVideoHeight) ;
}


//
// CDDrawObject::SetBallPosition(): Uses the video position to initialize the ball's
// position to the center of the video.
//
void CDDrawObject::SetBallPosition(DWORD dwVideoLeft, DWORD dwVideoTop, 
                                   DWORD dwVideoWidth, DWORD dwVideoHeight)
{
    DbgLog((LOG_TRACE, 5, TEXT("CDDrawObject::SetBallPosition() entered"))) ;
    
    // Place the ball at the middle of video on start
    m_iBallCenterX = dwVideoLeft + dwVideoWidth  / 2 ;
    m_iBallCenterY = dwVideoTop  + dwVideoHeight / 2 ;
}


//
// CDDrawObject::MoveBallPosition(): Moves the ball L/R/U/D and keeps it within
// the video area..
//
void CDDrawObject::MoveBallPosition(int iDirX, int iDirY)
{
    DbgLog((LOG_TRACE, 5, TEXT("CDDrawObject::MoveBallPosition(%d, %d) entered"),
        iDirX, iDirY)) ;
    
    m_iBallCenterX += iDirX ;
    m_iBallCenterY += iDirY ;
    
    // Keep the ball inside the video rect
    if (m_iBallCenterX < m_RectVideo.left + BALL_RADIUS)
        m_iBallCenterX = m_RectVideo.left + BALL_RADIUS ;
    else if (m_iBallCenterX > m_RectVideo.right - BALL_RADIUS)
        m_iBallCenterX = m_RectVideo.right - BALL_RADIUS ;
    
    if (m_iBallCenterY < m_RectVideo.top + BALL_RADIUS)
        m_iBallCenterY = m_RectVideo.top + BALL_RADIUS ;
    else if (m_iBallCenterY > m_RectVideo.bottom - BALL_RADIUS)
        m_iBallCenterY = m_RectVideo.bottom - BALL_RADIUS ;
}



//
// Overlay Callback object implementation
//
COverlayCallback::COverlayCallback(CDDrawObject *pDDrawObj, HWND hWndApp, HRESULT *phr) :
CUnknown("Overlay Callback Object", NULL, phr),
m_pDDrawObj(pDDrawObj),
m_hWndApp(hWndApp)
{
    DbgLog((LOG_TRACE, 5, TEXT("COverlayCallback::COverlayCallback() entered"))) ;
    ASSERT(m_pDDrawObj) ;
    AddRef() ;    // increment ref count for itself
}


COverlayCallback::~COverlayCallback()
{
    DbgLog((LOG_TRACE, 5, TEXT("COverlayCallback::~COverlayCallback() entered"))) ;
}


HRESULT COverlayCallback::OnUpdateOverlay(BOOL  bBefore,
                                          DWORD dwFlags,
                                          BOOL  bOldVisible,
                                          const RECT *prcSrcOld,
                                          const RECT *prcDestOld,
                                          BOOL  bNewVisible,
                                          const RECT *prcSrcNew,
                                          const RECT *prcDestNew)
{
    DbgLog((LOG_TRACE, 5, TEXT("COverlayCallback::OnUpdateOverlay(%s, ...) entered"),
		    bBefore ? "TRUE" : "FALSE")) ;

    if (NULL == m_pDDrawObj)
    {
        DbgLog((LOG_ERROR, 1, TEXT("ERROR: NULL DDraw object pointer was specified"))) ;
        return E_POINTER ;
    }

    if (bBefore)  // overlay is going to be updated
    {
        DbgLog((LOG_TRACE, 5, TEXT("Just turn off color keying and return"))) ;
        m_pDDrawObj->SetOverlayState(FALSE) ;   // don't paint color key in video's position
#ifndef NOFLIP
        m_pDDrawObj->UpdateAndFlipSurfaces() ;  // flip the surface so that video doesn't show anymore
#endif // NOFLIP
        return S_OK ;
    }

    //
    // After overlay has been updated. Turn on/off overlay color keying based on 
    // flags and use new source/dest position etc.
    //
    if (dwFlags & (AM_OVERLAY_NOTIFY_VISIBLE_CHANGE |
                   AM_OVERLAY_NOTIFY_SOURCE_CHANGE  |
                   AM_OVERLAY_NOTIFY_DEST_CHANGE))   // it's a valid flag
        m_pDDrawObj->SetOverlayState(bNewVisible) ;  // paint/don't paint color key based on param

    if (dwFlags & AM_OVERLAY_NOTIFY_VISIBLE_CHANGE)  // overlay visibility state change
    {
        DbgLog((LOG_TRACE, 5, TEXT(".._NOTIFY_VISIBLE_CHANGE from %s to %s"), 
                bOldVisible ? "TRUE" : "FALSE", bNewVisible ? "TRUE" : "FALSE")) ;
    }

    if (dwFlags & AM_OVERLAY_NOTIFY_SOURCE_CHANGE)   // overlay source rect change
    {
        DbgLog((LOG_TRACE, 5, TEXT(".._NOTIFY_SOURCE_CHANGE from (%ld, %ld, %ld, %ld) to (%ld, %ld, %ld, %ld)"), 
                prcSrcOld->left, prcSrcOld->top, prcSrcOld->right, prcSrcOld->bottom,
                prcSrcNew->left, prcSrcNew->top, prcSrcNew->right, prcSrcNew->bottom)) ;
    }

    if (dwFlags & AM_OVERLAY_NOTIFY_DEST_CHANGE)     // overlay destination rect change
    {
        DbgLog((LOG_TRACE, 5, TEXT(".._NOTIFY_DEST_CHANGE from (%ld, %ld, %ld, %ld) to (%ld, %ld, %ld, %ld)"), 
                prcDestOld->left, prcDestOld->top, prcDestOld->right, prcDestOld->bottom,
                prcDestNew->left, prcDestNew->top, prcDestNew->right, prcDestNew->bottom)) ;
        m_pDDrawObj->SetVideoPosition(prcDestNew->left, prcDestNew->top, 
                                      RECTWIDTH(*prcDestNew), RECTHEIGHT(*prcDestNew)) ;
    }

    return S_OK ;
}


HRESULT COverlayCallback::OnUpdateColorKey(COLORKEY const *pKey,
                                           DWORD           dwColor)
{
    DbgLog((LOG_TRACE, 5, TEXT("COverlayCallback::OnUpdateColorKey(.., 0x%lx) entered"), dwColor)) ;

    return S_OK ;
}


HRESULT COverlayCallback::OnUpdateSize(DWORD dwWidth, DWORD dwHeight, 
                                       DWORD dwARWidth, DWORD dwARHeight)
{
    DbgLog((LOG_TRACE, 5, TEXT("COverlayCallback::OnUpdateSize(%lu, %lu, %lu, %lu) entered"), 
            dwWidth, dwHeight, dwARWidth, dwARHeight)) ;

    PostMessage(m_hWndApp, WM_SIZE_CHANGE, 0, 0) ;

    return S_OK ;
}
