/****************************************************************************
 *
 *   capwin.c
 *
 *   Main window proceedure.
 *
 *   Microsoft Video for Windows Sample Capture Class
 *
 *   Copyright (c) 1992 - 1995 Microsoft Corporation.  All Rights Reserved.
 *
 *    You have a royalty-free right to use, modify, reproduce and
 *    distribute the Sample Files (and/or any modified version) in
 *    any way you find useful, provided that you agree that
 *    Microsoft has no warranty obligations or liability for any
 *    Sample Application Files which are modified.
 *
 ***************************************************************************/

#define INC_OLE2
#pragma warning(disable:4103)
#include <windows.h>
#include <windowsx.h>
#include <win32.h>
#include <mmsystem.h>
#include <vfw.h>
#include <mmreg.h>
#include <memory.h>

#include "ivideo32.h"
#include "avicapi.h"
#include "cappal.h"
#include "capdib.h"
#include "dibmap.h"

#ifdef UNICODE
#include <stdlib.h>
#endif

// GetWindowLong assignments
#define GWL_CAPSTREAM   0

#define ID_PREVIEWTIMER 9

//#ifdef _DEBUG
#ifdef PLASTIQUE
    #define MB(lpsz) MessageBoxA(NULL, lpsz, "", MB_OK);
#else
    #define MB(lpsz)
#endif


// #if defined _WIN32 && defined CHICAGO
#if defined NO_LONGER_USED

#include <mmdevldr.h>
#include <vmm.h>
#include "mmdebug.h"

#pragma message (SQUAWK "move these defines later")
#define MMDEVLDR_IOCTL_PAGEALLOCATE  7
#define MMDEVLDR_IOCTL_PAGEFREE      8
#define PageContig      0x00000004
#define PageFixed       0x00000008
//end

HANDLE hMMDevLdr = NULL;

/*****************************************************************************

  @doc INTERNAL

  @function HANDLE | OpenMMDEVLDR | Open a file handle to the MMDEVLDR VxD
  in order to access the DeviceIoControl functions.

  @rdesc opens a shared handle to MMDEVLDR

*****************************************************************************/

VOID WINAPI OpenMMDEVLDR(
    void)
{
    AuxDebugEx (5, DEBUGLINE "OpenMMDEVLDR()r\n");

    if (hMMDevLdr)
        return;

    hMMDevLdr = CreateFile(
        "\\\\.\\MMDEVLDR.VXD", // magic name to attach to an already loaded vxd
        GENERIC_WRITE,
        FILE_SHARE_WRITE,
        NULL,
        OPEN_ALWAYS,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_GLOBAL_HANDLE,
        NULL);

    AuxDebugEx (5, DEBUGLINE "OpenMMDEVLDR returns %08Xr\n", hMMDevLdr);
    return;
}

VOID WINAPI CloseMMDEVLDR(
    void)
{
    if (! hMMDevLdr)
        return;

    CloseHandle (hMMDevLdr);
    hMMDevLdr = NULL;
}

/*****************************************************************************

  @doc INTERNAL

  @function DWORD | LinPageLock | Call the VMM service LinPageLock via
   DeviceIoControl through MMDEVLDR.

  @parm DWORD | dwStartPage | Starting page of the linear region to lock.

  @parm DWORD | dwPageCount | Number of 4K pages to lock.

  @parm DWORD | fdwLinPageLock | Flags expected by the VMM service.
   @flag PAGEMAPGLOBAL | Return an alias to the locked region which
    is valid in all process contexts.

  @rdesc Meaningless unless PAGEMAPGLOBAL specified. If it was, then the
   return value is the alias pointer to the start of the linear region
   (NOTE: A *POINTER*, NOT a page address). The pointer will be page
   aligned (i.e. the low 12 bits will be zero.)

*****************************************************************************/

DWORD WINAPI LinPageLock(
    DWORD           dwStartPage,
    DWORD           dwPageCount,
    DWORD           fdwLinPageLock)
{
    LOCKUNLOCKPARMS lup;
    DWORD           dwRet;
    DWORD           cbRet;

    AuxDebugEx (6, DEBUGLINE "LinPageLock(%08x,%08x,%08x)\r\n",
                 dwStartPage, dwPageCount, fdwLinPageLock);

    assert (hMMDevLdr != NULL);
    if (INVALID_HANDLE_VALUE == hMMDevLdr)
        return 0;

    lup.dwStartPage = dwStartPage;
    lup.dwPageCount = dwPageCount;
    lup.fdwOperation= fdwLinPageLock;


    if ( ! DeviceIoControl (hMMDevLdr,
                            MMDEVLDR_IOCTL_LINPAGELOCK,
                            &lup,
                            sizeof(lup),
                            &dwRet,
                            sizeof(dwRet),
                            &cbRet,
                            NULL))
    {
        AuxDebug("LinPageLock failed!!!");
        dwRet = 0;
    }

    return dwRet;
}

/*****************************************************************************

  @doc INTERNAL

  @function DWORD | LinPageUnLock | Call the VMM service LinPageUnLock via
   DeviceIoControl through MMDEVLDR.

  @parm DWORD | dwStartPage | Starting page of the linear region to unlock.

  @parm DWORD | dwPageCount | Number of 4K pages to lock.

  @parm DWORD | fdwLinPageLock | Flags expected by the VMM service.
   @flag PAGEMAPGLOBAL | Return an alias to the locked region which
    is valid in all process contexts.

  @comm
   If PAGEMAPGLOBAL was specified on the <f LinPageLock> call, it must
   also be specified here. In this case, <p dwStartPage> should be the
   page address of the returned alias pointer in global memory.

*****************************************************************************/

void WINAPI LinPageUnLock(
    DWORD           dwStartPage,
    DWORD           dwPageCount,
    DWORD           fdwLinPageLock)
{
    LOCKUNLOCKPARMS lup;

    AuxDebugEx (6, DEBUGLINE "LinPageUnLock (%08x,%08x,%08x)\r\n",
                dwStartPage, dwPageCount, fdwLinPageLock);

    assert (hMMDevLdr != NULL);
    assert (INVALID_HANDLE_VALUE != hMMDevLdr);
    if (INVALID_HANDLE_VALUE == hMMDevLdr)
        return;

    lup.dwStartPage = dwStartPage;
    lup.dwPageCount = dwPageCount;
    lup.fdwOperation = fdwLinPageLock;

    DeviceIoControl (hMMDevLdr,
                     MMDEVLDR_IOCTL_LINPAGEUNLOCK,
                     &lup,
                     sizeof(lup),
                     NULL,
                     0,
                     NULL,
                     NULL);
}

/*+ FreeContigMem
 *
 *-==================================================================*/

VOID FreeContigMem (
    DWORD hMemContig)
{
    DWORD dwRet;
    DWORD cbRet;

    assert (hMMDevLdr != NULL);
    assert (INVALID_HANDLE_VALUE != hMMDevLdr);
    if (INVALID_HANDLE_VALUE == hMMDevLdr)
        return;

    DeviceIoControl (hMMDevLdr,
                     MMDEVLDR_IOCTL_PAGEFREE,
                     &hMemContig,
                     sizeof(hMemContig),
                     &dwRet,
                     sizeof(dwRet),
                     &cbRet,
                     NULL);
}

/*+ AllocContigMem
 *
 *-==================================================================*/

LPVOID AllocContigMem (
    DWORD   cbSize,
    LPDWORD phMemContig)
{
    struct _memparms {
       DWORD flags;
       DWORD nPages;
       } mp;
    struct _memret {
       LPVOID lpv;
       DWORD  hMem;
       DWORD  nPages;
       DWORD  dwPhys;
       } mr;
    DWORD  cbRet;

    mr.lpv = NULL;
    *phMemContig = 0;

    mp.nPages = (cbSize + 4095) >> 12;
    mp.flags = PageContig+PageFixed;

    AuxDebugEx (2, DEBUGLINE "Contig allocate %08X pages\r\n", mp.nPages);

    assert (hMMDevLdr != NULL);
    assert (INVALID_HANDLE_VALUE != hMMDevLdr);
    if (INVALID_HANDLE_VALUE == hMMDevLdr)
        return NULL;

    if ( ! DeviceIoControl (hMMDevLdr,
                            MMDEVLDR_IOCTL_PAGEALLOCATE,
                            &mp,
                            sizeof(mp),
                            &mr,
                            sizeof(mr),
                            &cbRet,
                            NULL))
    {
        AuxDebugEx(0, "Contig Allocate failed!!!\r\n");
        mr.lpv = NULL;
        mr.hMem = 0;
        mr.nPages = 0;
        mr.dwPhys = 0;
    }

    *phMemContig = mr.hMem;

    AuxDebugEx(2, "Contig Allocate returns %08X\r\n", mr.lpv);
    return mr.lpv;
}

/*+
 *
 *-================================================================*/

PVOID WINAPI CreateGlobalAlias (
    PVOID   pOriginal,
    DWORD   cbOriginal,
    LPDWORD pnPages)
{
    DWORD   dwStartPage;
    DWORD   dwPageCount;
    DWORD   dwPageOffset;
    DWORD   dwAliasBase;
    PVOID   pAlias;

    AuxDebugEx (6, DEBUGLINE "CreateGlobalAlias(%08X,%08X,..)\r\n",
                pOriginal, cbOriginal);

    dwStartPage  = ((DWORD)pOriginal) >> 12;
    dwPageOffset = ((DWORD)pOriginal) & ((1 << 12)-1);
    dwPageCount  = ((((DWORD)pOriginal) + cbOriginal - 1) >> 12) - dwStartPage + 1;

    *pnPages = 0;
    dwAliasBase = LinPageLock (dwStartPage, dwPageCount, PAGEMAPGLOBAL);
    if ( ! dwAliasBase)
        return NULL;

    pAlias = (PVOID)(dwAliasBase + dwPageOffset);
    *pnPages = dwPageCount;

    AuxDebugEx (6, DEBUGLINE "CreateGlobalAlias returns %08X nPages %d\r\n", pAlias, dwPageCount);
    return pAlias;
}

/*+
 *
 *-================================================================*/

VOID WINAPI FreeGlobalAlias(
    PVOID        pAlias,
    DWORD        nPages)
{
    AuxDebugEx (6, DEBUGLINE "FreeGlobalAlias(%08X,%08X)\r\n", pAlias, nPages);

    LinPageUnLock (((DWORD)pAlias) >> 12, nPages, PAGEMAPGLOBAL);
}
#endif


#if defined _WIN32 && defined CHICAGO


/*+ videoFrame
 *
 *-================================================================*/

DWORD WINAPI videoFrame (
    HVIDEO hVideo,
    LPVIDEOHDR lpVHdr)
{
    return vidxFrame (hVideo, lpVHdr);
}

#endif



//
// Set the overlay rectangles on capture cards which support
// overlay, and then enable/disable the key color.
//
static void SetOverlayRectangles (LPCAPSTREAM lpcs)
{
    HDC hdc;
    BOOL fVisible;
    RECT rc;

    if (!lpcs->hVideoDisplay)
        return;

    hdc = GetDC (lpcs->hwnd);
    fVisible = (GetClipBox (hdc, &rc) != NULLREGION);
    ReleaseDC (lpcs->hwnd, hdc);

    if (!fVisible)  // disable the overlay if iconic
        videoStreamFini (lpcs->hVideoDisplay);
    else {
        // Destination
        GetClientRect (lpcs->hwnd, &rc);
        ClientToScreen (lpcs->hwnd, (LPPOINT)&rc);
        ClientToScreen (lpcs->hwnd, (LPPOINT)&rc+1);

        videoSetRect (lpcs->hVideoDisplay, DVM_DST_RECT, rc);

        // Overlay channel Source rectangle
        SetRect (&rc, lpcs->ptScroll.x, lpcs->ptScroll.y,
                lpcs->ptScroll.x + rc.right - rc.left,
                lpcs->ptScroll.y + rc.bottom - rc.top);
        videoSetRect (lpcs->hVideoDisplay, DVM_SRC_RECT, rc);

        videoStreamInit (lpcs->hVideoDisplay, 0L, 0L, 0L, 0L);
    }
}

// WM_POSITIONCHANGED and WM_POSITIONCHANGING don't do enough to
// handle clipping of the overlay window on the Intel board,
// which keys on black.  Do this routine on WM_PAINT and
// WM_ENTERIDLE messages.

void CheckWindowMove(LPCAPSTREAM lpcs, HDC hdcWnd, BOOL fForce)
{
    UINT    wRgn;
    RECT    rc;
#ifdef _WIN32
    POINT   ptOrg;
#else
    DWORD   dwOrg;
#endif
    HDC     hdc;
    BOOL    f;

    if (!lpcs->hwnd || !lpcs->hVideoDisplay || !lpcs->fOverlayWindow)
        return;

    //
    //  when the screen is locked for update by a window move operation
    //  we dont want to turn off the video.
    //
    //  we can tell if the screen is locked by checking a DC to the screen.
    //
    hdc = GetDC(NULL);
    f = GetClipBox(hdc, &rc) == NULLREGION;
    ReleaseDC(NULL, hdc);

    if (f) {
        lpcs->uiRegion = (UINT) -1;
        return;
    }

    if (fForce)
        lpcs->uiRegion = (UINT) -1;

    hdc = GetDC (lpcs->hwnd);
    wRgn = GetClipBox(hdc, &rc);
#ifdef _WIN32
    GetDCOrgEx(hdc, &ptOrg);
#else
    dwOrg = GetDCOrg(hdc);
#endif
    ReleaseDC(lpcs->hwnd, hdc);

    if (wRgn == lpcs->uiRegion &&
#ifdef _WIN32
                ptOrg.x == lpcs->ptRegionOrigin.x &&
		ptOrg.y == lpcs->ptRegionOrigin.y &&
#else
                dwOrg == lpcs->dwRegionOrigin &&
#endif
                EqualRect(&rc, &lpcs->rcRegionRect))
        return;

    lpcs->uiRegion       = wRgn;
#ifdef _WIN32
    lpcs->ptRegionOrigin = ptOrg;
#else
    lpcs->dwRegionOrigin = dwOrg;
#endif

    lpcs->rcRegionRect   = rc;

    SetOverlayRectangles (lpcs);

    if (hdcWnd)
        videoUpdate (lpcs->hVideoDisplay, lpcs->hwnd, hdcWnd);
    else
        InvalidateRect (lpcs->hwnd, NULL, TRUE);
}

//
// Create our little world
//
LPCAPSTREAM CapWinCreate (HWND hwnd)
{
    LPCAPSTREAM lpcs;
    WAVEFORMATEX wfex;

    if (!(lpcs = (LPCAPSTREAM) GlobalAllocPtr (GHND, sizeof (CAPSTREAM))))
        return NULL;

    SetWindowLongPtr (hwnd, GWL_CAPSTREAM, (LONG_PTR)lpcs);

    lpcs->dwSize = sizeof (CAPSTREAM);
    lpcs->uiVersion = CAPSTREAM_VERSION;
    lpcs->hwnd = hwnd;
    lpcs->hInst = ghInstDll;
    lpcs->hWaitCursor = LoadCursor(NULL, IDC_WAIT);
    lpcs->hdd = DrawDibOpen();
    lpcs->fAudioHardware = !!waveOutGetNumDevs();    // force 1 or 0


    // Video defaults
    lpcs->sCapParms.dwRequestMicroSecPerFrame = 66667;   // 15fps
    lpcs->sCapParms.vKeyAbort          = VK_ESCAPE;
    lpcs->sCapParms.fAbortLeftMouse    = TRUE;
    lpcs->sCapParms.fAbortRightMouse   = TRUE;
    lpcs->sCapParms.wNumVideoRequested = MIN_VIDEO_BUFFERS;
    lpcs->sCapParms.wPercentDropForError = 10;   // error msg if dropped > 10%
    lpcs->sCapParms.wChunkGranularity  = 0;

    // Audio defaults to 11K, 8bit, Mono
    lpcs->sCapParms.fCaptureAudio = lpcs->fAudioHardware;
    lpcs->sCapParms.wNumAudioRequested = DEF_WAVE_BUFFERS;

    lpcs->fCaptureFlags |= CAP_fCapturingToDisk;
    lpcs->fCaptureFlags |= CAP_fDefaultVideoBuffers;
    lpcs->fCaptureFlags |= CAP_fDefaultAudioBuffers;

    wfex.wFormatTag = WAVE_FORMAT_PCM;
    wfex.nChannels = 1;
    wfex.nSamplesPerSec = 11025;
    wfex.nAvgBytesPerSec = 11025;
    wfex.nBlockAlign = 1;
    wfex.wBitsPerSample = 8;
    wfex.cbSize = 0;
    SendMessage (hwnd, WM_CAP_SET_AUDIOFORMAT, 0, (LPARAM)(LPVOID)&wfex);

    // Palette defaults
    lpcs->nPaletteColors = 256;

    // Capture defaults
    lpcs->sCapParms.fUsingDOSMemory = FALSE;
    lstrcpy (lpcs->achFile, TEXT("C:\\CAPTURE.AVI"));    // Default capture file
    lpcs->fCapFileExists = fileCapFileIsAVI (lpcs->achFile);

    // Allocate index to 32K frames plus proportionate number of audio chunks
    lpcs->sCapParms.dwIndexSize = (32768ul + (32768ul / 15));
    lpcs->sCapParms.fDisableWriteCache = FALSE;

#ifdef NEW_COMPMAN
    // Init the COMPVARS structure
    lpcs->CompVars.cbSize = sizeof (COMPVARS);
    lpcs->CompVars.dwFlags = 0;
#endif

    return lpcs;
}

//
// Destroy our little world
//
void CapWinDestroy (LPCAPSTREAM lpcs)
{
    // Uh, oh.  Somebodys trying to kill us while capturing
    if (lpcs->fCaptureFlags & CAP_fCapturingNow) {
	if (lpcs->fCaptureFlags & CAP_fFrameCapturingNow) {
	    // Single frame capture in progress
	    SingleFrameCaptureClose (lpcs);
	}
	else {
	    // Streaming capture in progress, OR
	    // MCI step capture in progress

	    lpcs->fCaptureFlags |= CAP_fAbortCapture;
#ifdef _WIN32
	    // wait for capture thread to go away

	    // we must have a capture thread
	    WinAssert(lpcs->hThreadCapture != 0);
	    while (MsgWaitForMultipleObjects(1, &lpcs->hThreadCapture, FALSE,
		INFINITE, QS_SENDMESSAGE) != WAIT_OBJECT_0) {
		MSG msg;

		// just a single peekmessage with NOREMOVE will
		// process the inter-thread send and not affect the queue
		PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
	    }
	    CloseHandle(lpcs->hThreadCapture);
	    lpcs->hThreadCapture = 0;

	    // it should have stopped capturing
	    WinAssert(!(lpcs->fCaptureFlags & CAP_fCapturingNow));

#else
	    while (lpcs->fCapturingNow)
		Yield ();
#endif
    	}
    }

    if (lpcs->idTimer)
        KillTimer(lpcs->hwnd, lpcs->idTimer);

    PalFini (lpcs);
    DibFini (lpcs);

    CapWinDisconnectHardware (lpcs);

    DrawDibClose (lpcs->hdd);

    if (lpcs->lpWaveFormat)
        GlobalFreePtr (lpcs->lpWaveFormat);

#ifdef NEW_COMPMAN
    if (lpcs->CompVars.hic)
        ICCompressorFree(&lpcs->CompVars);
#endif

    if (lpcs->lpInfoChunks)
        GlobalFreePtr(lpcs->lpInfoChunks);

    WinAssert (!lpcs->pAsync);
    GlobalFreePtr (lpcs);       // Free the instance memory
}

UINT GetSizeOfWaveFormat (LPWAVEFORMATEX lpwf)
{
    UINT wSize;

    if ((lpwf == NULL) || (lpwf->wFormatTag == WAVE_FORMAT_PCM))
        wSize = sizeof (PCMWAVEFORMAT);
    else
        wSize = sizeof (WAVEFORMATEX) + lpwf->cbSize;

    return wSize;
}

// Returns TRUE if we got a new frame, else FALSE
// if fForce, then always get a new frame
BOOL GetAFrameThenCallback (LPCAPSTREAM lpcs, BOOL fForce)
{
    BOOL fOK = FALSE;
    static BOOL fRecursion = FALSE;
    BOOL fVisible;
    RECT rc;
    HDC  hdc;

    if (fRecursion)
        return FALSE;

    if (!lpcs->sCapDrvCaps.fCaptureInitialized)
        return fOK;

    fRecursion = TRUE;

    // Update the preview window if we got a timer and not saving to disk
    if (lpcs->fOverlayWindow)
        CheckWindowMove(lpcs, NULL, FALSE);

    if ((!(lpcs->fCaptureFlags & CAP_fCapturingNow))
       || (lpcs->fCaptureFlags & CAP_fStepCapturingNow)
       || (lpcs->fCaptureFlags & CAP_fFrameCapturingNow)) {
        hdc = GetDC (lpcs->hwnd);
        fVisible = (GetClipBox (hdc, &rc) != NULLREGION);
        ReleaseDC (lpcs->hwnd, hdc);

        if (fForce || (fVisible && (lpcs->fLiveWindow || lpcs->CallbackOnVideoFrame))) {
            videoFrame (lpcs->hVideoIn, &lpcs->VidHdr);
            fOK = TRUE;

            if (lpcs->CallbackOnVideoFrame)
                lpcs->CallbackOnVideoFrame(lpcs->hwnd, &lpcs->VidHdr);

            if (fForce || lpcs->fLiveWindow) {
                InvalidateRect (lpcs->hwnd, NULL, TRUE);
                UpdateWindow (lpcs->hwnd);
            }
        } // if visible
    } // if we're not streaming

    fRecursion = FALSE;

    return fOK;
}

// Clear the Status and Error strings via callback
__inline void FAR PASCAL ClearStatusAndError (LPCAPSTREAM lpcs)
{
    statusUpdateStatus(lpcs, 0);     // Clear status
    errorUpdateError(lpcs, 0);       // Clear error

}

// Process class specific commands >= WM_USER

DWORD_PTR PASCAL ProcessCommandMessages (LPCAPSTREAM lpcs, UINT msg, WPARAM wParam, LPARAM lParam)
{
    DWORD_PTR dwReturn = 0L;
    DWORD dwT;

    switch (msg) {
        // Don't clear status and error on the following innocuous msgs
        case WM_CAP_GET_CAPSTREAMPTR:
        case WM_CAP_GET_USER_DATA:
        case WM_CAP_DRIVER_GET_NAME:
        case WM_CAP_DRIVER_GET_VERSION:
        case WM_CAP_DRIVER_GET_CAPS:
        case WM_CAP_GET_AUDIOFORMAT:
        case WM_CAP_GET_VIDEOFORMAT:
        case WM_CAP_GET_STATUS:
        case WM_CAP_SET_SEQUENCE_SETUP:
        case WM_CAP_GET_SEQUENCE_SETUP:
        case WM_CAP_GET_MCI_DEVICE:
        case WM_CAP_SET_PREVIEWRATE:
        case WM_CAP_SET_SCROLL:
#ifdef UNICODE
        // ...or on the ansi thunks for these messages
        case WM_CAP_DRIVER_GET_NAMEA:
        case WM_CAP_DRIVER_GET_VERSIONA:
        case WM_CAP_GET_MCI_DEVICEA:
#endif
            break;

        default:
            ClearStatusAndError (lpcs);
            break;
    }

    switch (msg) {
    case WM_CAP_GET_CAPSTREAMPTR:
        // return a pointer to the CAPSTREAM
        return (DWORD_PTR) (LPVOID) lpcs;

    case WM_CAP_GET_USER_DATA:
	return lpcs->lUser;

    // unicode and win-16 version - see ansi thunk below
    case WM_CAP_DRIVER_GET_NAME:
        // Return the name of the capture driver in use
        // wParam is the length of the buffer pointed to by lParam
        if (!lpcs->fHardwareConnected)
            return FALSE;
        return (capInternalGetDriverDesc (lpcs->sCapDrvCaps.wDeviceIndex,
                (LPTSTR) lParam, (int) wParam, NULL, 0));

    // unicode and win-16 version - see ansi thunk below
    case WM_CAP_DRIVER_GET_VERSION:
        // Return the version of the capture driver in use as text
        // wParam is the length of the buffer pointed to by lParam
        if (!lpcs->fHardwareConnected)
            return FALSE;
        return (capInternalGetDriverDesc (lpcs->sCapDrvCaps.wDeviceIndex,
                NULL, 0, (LPTSTR) lParam, (int) wParam));

#ifdef UNICODE
    // ansi/unicode thunk versions of the above entrypoint
    case WM_CAP_DRIVER_GET_NAMEA:
        if (!lpcs->fHardwareConnected)
            return FALSE;
        return capInternalGetDriverDescA(lpcs->sCapDrvCaps.wDeviceIndex,
                (LPSTR) lParam, (int) wParam, NULL, 0);

    // ansi/unicode thunk versions of the above entrypoint
    case WM_CAP_DRIVER_GET_VERSIONA:
        if (!lpcs->fHardwareConnected)
            return FALSE;
        return capInternalGetDriverDescA(lpcs->sCapDrvCaps.wDeviceIndex,
                NULL, 0, (LPSTR) lParam, (int) wParam);
#endif


    case WM_CAP_DRIVER_GET_CAPS:
        // wParam is the size of the CAPDRIVERCAPS struct
        // lParam points to a CAPDRIVERCAPS struct
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (wParam <= sizeof (CAPDRIVERCAPS) &&
                !IsBadWritePtr ((LPVOID) lParam, (UINT) wParam)) {
            dwT = min ((UINT) wParam, sizeof (CAPDRIVERCAPS));
            _fmemcpy ((LPVOID) lParam, (LPVOID) &lpcs->sCapDrvCaps, (UINT) dwT);
            dwReturn = TRUE;
        }
        break;


    // unicode and win-16 version - see ansi thunk below
    case WM_CAP_FILE_GET_CAPTURE_FILE:
        // wParam is the size (in characters)
        // lParam points to a buffer in which capture file name is copied
        if (lParam) {
            lstrcpyn ((LPTSTR) lParam, lpcs->achFile, (UINT) wParam);
            dwReturn = TRUE;
        }
        break;
#ifdef UNICODE
    // ansi/unicode thunk
    case WM_CAP_FILE_GET_CAPTURE_FILEA:
        if (lParam) {
            Iwcstombs((LPSTR) lParam, lpcs->achFile, (int) wParam);
            dwReturn = TRUE;
        }
        break;
#endif


    case WM_CAP_GET_AUDIOFORMAT:
        // if lParam == NULL, return the size
        // if lParam != NULL, wParam is the size, return bytes copied
        if (lpcs->lpWaveFormat == NULL)
            return FALSE;
        dwT = GetSizeOfWaveFormat ((LPWAVEFORMATEX) lpcs->lpWaveFormat);
        if (lParam == 0)
            return (dwT);
        else {
            if (wParam < (UINT) dwT)
                return FALSE;
            else {
                hmemcpy ((LPVOID) lParam, (LPVOID) lpcs->lpWaveFormat, dwT);
                dwReturn = dwT;
            }
        }
        break;

    // unicode and win-16 version - see ansi thunk below
    case WM_CAP_GET_MCI_DEVICE:
        // wParam is the size in characters
        // lParam points to a buffer in which MCI device name is copied
        if (lParam) {
            lstrcpyn ((LPTSTR) lParam, lpcs->achMCIDevice, (UINT) wParam);
            dwReturn = TRUE;
        }
        break;
#ifdef UNICODE
    // ansi thunk of above
    case WM_CAP_GET_MCI_DEVICEA:
        if (lParam) {
            Iwcstombs( (LPSTR) lParam, lpcs->achMCIDevice, (int) wParam);
            dwReturn = TRUE;
        }
        break;
#endif

    case WM_CAP_GET_STATUS:
        // wParam is the size of the CAPSTATUS struct pointed to by lParam
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (IsBadWritePtr ((LPVOID) lParam, (UINT) wParam))
            return FALSE;

        if (wParam >= sizeof (CAPSTATUS)) {
            LPCAPSTATUS lpcc = (LPCAPSTATUS) lParam;

            lpcc->fLiveWindow          = lpcs->fLiveWindow;
            lpcc->fOverlayWindow       = lpcs->fOverlayWindow;
            lpcc->fScale               = lpcs->fScale;
            lpcc->ptScroll             = lpcs->ptScroll;
            lpcc->fUsingDefaultPalette = lpcs->fUsingDefaultPalette;
            lpcc->fCapFileExists       = lpcs->fCapFileExists;
            lpcc->fAudioHardware       = lpcs->fAudioHardware;
            lpcc->uiImageWidth         = lpcs->dxBits;
            lpcc->uiImageHeight        = lpcs->dyBits;

            // The following are updated dynamically during capture
            lpcc->dwCurrentVideoFrame          = lpcs->dwVideoChunkCount;
            lpcc->dwCurrentVideoFramesDropped  = lpcs->dwFramesDropped;
            if (lpcs->lpWaveFormat != NULL) {
            lpcc->dwCurrentWaveSamples         =
                  MulDiv (lpcs->dwWaveBytes,
                          lpcs->lpWaveFormat->nSamplesPerSec,
                          lpcs->lpWaveFormat->nAvgBytesPerSec);
            }
            lpcc->dwCurrentTimeElapsedMS       = lpcs->dwTimeElapsedMS;

            // Added post alpha release
	    if (lpcs->fCaptureFlags & CAP_fCapturingNow) {
		lpcc->fCapturingNow    = TRUE;
	    } else {
		lpcc->fCapturingNow    = FALSE;
	    }
            lpcc->hPalCurrent          = lpcs->hPalCurrent;
            lpcc->dwReturn             = lpcs->dwReturn;
            lpcc->wNumVideoAllocated   = lpcs->iNumVideo;
            lpcc->wNumAudioAllocated   = lpcs->iNumAudio;

            dwReturn = TRUE;
        }
        break;

    case WM_CAP_GET_SEQUENCE_SETUP:
        // wParam is sizeof CAPTUREPARMS
        // lParam = LPCAPTUREPARMS
        if (wParam <= sizeof (CAPTUREPARMS) &&
                !IsBadWritePtr ((LPVOID) lParam, (UINT) wParam)) {
            dwT = min ((UINT) wParam, sizeof (CAPTUREPARMS));
            _fmemcpy ((LPVOID) lParam, (LPVOID) &lpcs->sCapParms, (UINT) dwT);
            dwReturn = TRUE;
        }
        break;

    case WM_CAP_STOP:
        // Stop capturing a sequence
        if (lpcs->fCaptureFlags & CAP_fCapturingNow) {
            lpcs->fCaptureFlags |= CAP_fStopCapture;
            dwReturn = TRUE;
        }
        break;

    case WM_CAP_ABORT:
        // Stop capturing a sequence
        if (lpcs->fCaptureFlags & CAP_fCapturingNow) {
            lpcs->fCaptureFlags |= CAP_fAbortCapture;
            dwReturn = TRUE;
        }
        break;

    case WM_CAP_GET_VIDEOFORMAT:
        // if lParam == NULL, return the size
        // if lParam != NULL, wParam is the size, return bytes copied
        if (!lpcs->fHardwareConnected)
            return FALSE;
        dwT = ((LPBITMAPINFOHEADER)lpcs->lpBitsInfo)->biSize +
	      ((LPBITMAPINFOHEADER)lpcs->lpBitsInfo)->biClrUsed * sizeof(RGBQUAD);
        if (lParam == 0)
            return dwT;
        else {
            if (wParam < (UINT) dwT)
                return FALSE;
            else {
                hmemcpy ((LPVOID) lParam, (LPVOID) lpcs->lpBitsInfo, dwT);
                dwReturn = dwT;
            }
        }
        break;

    case WM_CAP_SINGLE_FRAME_OPEN:
        // wParam is not used
        // lParam is not used
        if (!lpcs->fHardwareConnected)
            return FALSE;
        return SingleFrameCaptureOpen (lpcs);

    case WM_CAP_SINGLE_FRAME_CLOSE:
        // wParam is not used
        // lParam is not used
        if (!lpcs->fHardwareConnected)
            return FALSE;
        return SingleFrameCaptureClose (lpcs);

    case WM_CAP_SINGLE_FRAME:
        // wParam is not used
        // lParam is not used
        if (!lpcs->fHardwareConnected)
            return FALSE;
        return SingleFrameCapture (lpcs);

    // unicode and win-16 version - see ansi thunk below
    case WM_CAP_SET_CALLBACK_STATUS:
        // Set the status callback proc
        if (lParam != 0 && IsBadCodePtr ((FARPROC) lParam))
            return FALSE;
        lpcs->CallbackOnStatus = (CAPSTATUSCALLBACK) lParam;
	lpcs->fLastStatusWasNULL = TRUE;
#ifdef UNICODE
        lpcs->fUnicode &= ~VUNICODE_STATUSISANSI;
#endif
        return TRUE;

#ifdef UNICODE
    // ansi thunk for above
    case WM_CAP_SET_CALLBACK_STATUSA:
        // Set the status callback proc
        if (lParam != 0 && IsBadCodePtr ((FARPROC) lParam))
            return FALSE;
        lpcs->CallbackOnStatus = (CAPSTATUSCALLBACK) lParam;
	lpcs->fLastStatusWasNULL = TRUE;
        lpcs->fUnicode |= VUNICODE_STATUSISANSI;
        return TRUE;
#endif

    // unicode and win-16 version - see ansi version below
    case WM_CAP_SET_CALLBACK_ERROR:
        // Set the error callback proc
        if (lParam != 0 && IsBadCodePtr ((FARPROC) lParam))
            return FALSE;
        lpcs->CallbackOnError = (CAPERRORCALLBACK) lParam;
	lpcs->fLastErrorWasNULL = TRUE;
#ifdef UNICODE
        lpcs->fUnicode &= ~VUNICODE_ERRORISANSI;
#endif
        return TRUE;


#ifdef UNICODE
    // ansi version of above
    case WM_CAP_SET_CALLBACK_ERRORA:
        // Set the error callback proc
        if (lParam != 0 && IsBadCodePtr ((FARPROC) lParam))
            return FALSE;
        lpcs->CallbackOnError = (CAPERRORCALLBACK) lParam;
	lpcs->fLastErrorWasNULL = TRUE;
        lpcs->fUnicode |= VUNICODE_ERRORISANSI;
        return TRUE;
#endif

    case WM_CAP_SET_CALLBACK_FRAME:
        // Set the callback proc for single frame during preview
        if (lParam != 0 && IsBadCodePtr ((FARPROC) lParam))
            return FALSE;
        lpcs->CallbackOnVideoFrame = (CAPVIDEOCALLBACK) lParam;
        return TRUE;

    default:
        break;
    }

    // Once we start capturing, don't change anything
    if (lpcs->fCaptureFlags & CAP_fCapturingNow)
        return dwReturn;

    switch (msg) {

    case WM_CAP_SET_CALLBACK_YIELD:
        // Set the callback proc for wave buffer processing to net
        if (lParam != 0 && IsBadCodePtr ((FARPROC) lParam))
            return FALSE;
        lpcs->CallbackOnYield = (CAPYIELDCALLBACK) lParam;
        return TRUE;

    case WM_CAP_SET_CALLBACK_VIDEOSTREAM:
        // Set the callback proc for video buffer processing to net
        if (lParam != 0 && IsBadCodePtr ((FARPROC) lParam))
            return FALSE;
        lpcs->CallbackOnVideoStream = (CAPVIDEOCALLBACK) lParam;
        return TRUE;

    case WM_CAP_SET_CALLBACK_WAVESTREAM:
        // Set the callback proc for wave buffer processing to net
        if (lParam != 0 && IsBadCodePtr ((FARPROC) lParam))
            return FALSE;
        lpcs->CallbackOnWaveStream = (CAPWAVECALLBACK) lParam;
        return TRUE;

    case WM_CAP_SET_CALLBACK_CAPCONTROL:
        // Set the callback proc for frame accurate capture start/stop
        if (lParam != 0 && IsBadCodePtr ((FARPROC) lParam))
            return FALSE;
        lpcs->CallbackOnControl = (CAPCONTROLCALLBACK) lParam;
        return TRUE;

    case WM_CAP_SET_USER_DATA:
	lpcs->lUser = lParam;
	return TRUE;

    case WM_CAP_DRIVER_CONNECT:
        // Connect to a device
        // wParam contains the index of the driver

        // If the same driver ID is requested, skip the request
        // Prevents multiple Inits from VB apps
        if (lpcs->fHardwareConnected &&
                (lpcs->sCapDrvCaps.wDeviceIndex == wParam))
            return TRUE;

        // First disconnect from any (possibly) existing device
        SendMessage (lpcs->hwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0l);

        // and then connect to the new device
        if (CapWinConnectHardware (lpcs, (UINT) wParam /*wDeviceIndex*/)) {
            if (!DibGetNewFormatFromDriver (lpcs)) {  // Allocate our bitspace
                // Use the cached palette if available
                if (lpcs->hPalCurrent && lpcs->lpCacheXlateTable) {
                    PalSendPaletteToDriver (lpcs, lpcs->hPalCurrent, lpcs->lpCacheXlateTable);
                }
                else
                    PalGetPaletteFromDriver (lpcs);

                // Get a frame using the possibly cached palette
                videoFrame (lpcs->hVideoIn, &lpcs->VidHdr);
        	InvalidateRect(lpcs->hwnd, NULL, TRUE);
                lpcs->sCapDrvCaps.fCaptureInitialized = TRUE; // everything AOK!
                dwReturn = TRUE;
            }
        }
        break;

    case WM_CAP_DRIVER_DISCONNECT:
        MB ("About to disconnect from driver");
        // Disconnect from a device
        // wParam and lParam unused
        if (!lpcs->fHardwareConnected)
            return FALSE;
        CapWinDisconnectHardware (lpcs);
        DibFini (lpcs);
        /* PalFini (lpcs); keep the palette cached for reconnections */
        InvalidateRect(lpcs->hwnd, NULL, TRUE);
        lpcs->sCapDrvCaps.fCaptureInitialized = FALSE;
        dwReturn = TRUE;
        break;

    // unicode and win-16 version - see ansi thunk below
    case WM_CAP_FILE_SET_CAPTURE_FILE:
        // lParam points to the name of the capture file
        if (lParam) {
            BOOL fAlreadyExists;        // Don't create a file if new name
#ifndef _WIN32
            OFSTRUCT of;
#endif
            HANDLE hFile;

            // Check for valid file names...
#ifdef _WIN32
    // can't use OpenFile for UNICODE names
            if ((hFile = CreateFile(
                            (LPTSTR) lParam,
                            GENERIC_WRITE,
                            0,
                            NULL,
                            OPEN_EXISTING,
                            FILE_ATTRIBUTE_NORMAL,
                            NULL)) == INVALID_HANDLE_VALUE) {
                if ((hFile = CreateFile(
                                (LPTSTR) lParam,
                                GENERIC_WRITE,
                                0,
                                NULL,
                                CREATE_NEW,
                                FILE_ATTRIBUTE_NORMAL,
                                NULL)) == INVALID_HANDLE_VALUE) {
#else

            if ((hFile = OpenFile ((LPTSTR) lParam, &of, OF_WRITE)) == -1) {
                if ((hFile = OpenFile ((LPTSTR) lParam, &of, OF_CREATE | OF_WRITE)) == -1) {
#endif
                    return FALSE;
                }
                fAlreadyExists = FALSE;
            }
            else
                fAlreadyExists = TRUE;

#ifdef _WIN32
            CloseHandle(hFile);
#else
            _lclose (hFile);
#endif
            lstrcpyn (lpcs->achFile, (LPTSTR) lParam, NUMELMS(lpcs->achFile));
            lpcs->fCapFileExists = fileCapFileIsAVI (lpcs->achFile);

            if (!fAlreadyExists) {
		// Delete the file created by CREATE_NEW (or OF_CREATE)
		// when verifying that we can write to this file location
#ifdef _WIN32
                DeleteFile ((LPTSTR) lParam);
#else
                OpenFile ((LPTSTR) lParam, &of, OF_DELETE);
#endif
            }
            dwReturn = TRUE;
        }
        break;

#ifdef UNICODE
    // Ansi thunk for above.
    case WM_CAP_FILE_SET_CAPTURE_FILEA:
        // lParam points to the name of the capture file
        if (lParam) {
            LPWSTR pw;
            int chsize;

            // remember the null
            chsize = lstrlenA( (LPSTR) lParam) + 1;
            pw = LocalAlloc(LPTR, chsize * sizeof(WCHAR));
	    if (pw) {
                Imbstowcs(pw, (LPSTR) lParam, chsize);
                dwReturn = ProcessCommandMessages(lpcs, WM_CAP_FILE_SET_CAPTURE_FILEW,
                                0, (LPARAM)pw);
                LocalFree(pw);
	    }
        }
        break;
#endif

    case WM_CAP_FILE_ALLOCATE:
        // lParam contains the size to preallocate the capture file in bytes
        return fileAllocCapFile(lpcs, (DWORD) lParam);

    // unicode and win-16 version - see ansi thunk below
    case WM_CAP_FILE_SAVEAS:
        // lParam points to the name of the SaveAs file
        if (lParam) {
            lstrcpyn (lpcs->achSaveAsFile, (LPTSTR) lParam,
                        NUMELMS(lpcs->achSaveAsFile));
            return (fileSaveCopy(lpcs));
        }
        break;

#ifdef UNICODE
    // ansi thunk for above
    case WM_CAP_FILE_SAVEASA:
        // lParam points to the name of the SaveAs file
        if (lParam) {
            LPWSTR pw;
            int chsize;

            // remember the null
            chsize = lstrlenA( (LPSTR) lParam)+1;
            pw = LocalAlloc(LPTR, chsize * sizeof(WCHAR));
	    if (pw) {
                Imbstowcs(pw, (LPSTR) lParam, chsize);
                dwReturn = ProcessCommandMessages(lpcs, WM_CAP_FILE_SAVEASW,
                                0, (LPARAM)pw);
                LocalFree(pw);
	    }
        }
        break;
#endif

    case WM_CAP_FILE_SET_INFOCHUNK:
        // wParam is not used
        // lParam is an LPCAPINFOCHUNK
        if (lParam) {
            return (SetInfoChunk(lpcs, (LPCAPINFOCHUNK) lParam));
        }
        break;

    // unicode and win-16 version - see ansi thunk below
    case WM_CAP_FILE_SAVEDIB:
        // lParam points to the name of the DIB file
        if (lParam) {
            if (lpcs->fOverlayWindow)
                GetAFrameThenCallback (lpcs, TRUE /*fForce*/);

            return (fileSaveDIB(lpcs, (LPTSTR)lParam));
        }
        break;

#ifdef UNICODE
    // ansi thunk for above
    case WM_CAP_FILE_SAVEDIBA:
        if (lParam) {
            LPWSTR pw;
            int chsize;

            if (lpcs->fOverlayWindow)
                GetAFrameThenCallback (lpcs, TRUE /*fForce*/);

            // remember the null
            chsize = lstrlenA( (LPSTR) lParam)+1;
            pw = LocalAlloc(LPTR, chsize * sizeof(WCHAR));
	    if (pw) {
                Imbstowcs(pw, (LPSTR) lParam, chsize);
                dwReturn = fileSaveDIB(lpcs, pw);
                LocalFree(pw);
	    }
        }
        break;
#endif


    case WM_CAP_EDIT_COPY:
        // Copy the current image and palette to the clipboard
        // wParam and lParam unused
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (lpcs->fOverlayWindow)
            GetAFrameThenCallback (lpcs, TRUE /*fForce*/);

        if (lpcs->sCapDrvCaps.fCaptureInitialized && OpenClipboard (lpcs->hwnd)) {
            EmptyClipboard();

            // put a copy of the current palette in the clipboard
            if (lpcs->hPalCurrent && lpcs->lpBitsInfo->bmiHeader.biBitCount <= 8)
                SetClipboardData(CF_PALETTE, CopyPalette (lpcs->hPalCurrent));

            // make a packed DIB out of the current image
            if (lpcs->lpBits && lpcs->lpBitsInfo ) {
                if (SetClipboardData (CF_DIB, CreatePackedDib (lpcs->lpBitsInfo,
                        lpcs->lpBits, lpcs->hPalCurrent)))
                    dwReturn = TRUE;
                else
                    errorUpdateError (lpcs, IDS_CAP_OUTOFMEM);
            }

            CloseClipboard();
        }
        break;

    case WM_CAP_SET_AUDIOFORMAT:
        {
            // wParam is unused
            // lParam is LPWAVEFORMAT or LPWAVEFORMATEX
            UINT wSize;
            LPWAVEFORMATEX lpwf = (LPWAVEFORMATEX) lParam;
            UINT uiError;

            // Verify the waveformat is valid
            uiError = waveInOpen(NULL, WAVE_MAPPER, lpwf, 0, 0L,WAVE_FORMAT_QUERY);

            if (uiError) {
                errorUpdateError (lpcs, IDS_CAP_WAVE_OPEN_ERROR);
                return FALSE;
            }

            if (lpcs->lpWaveFormat)
                GlobalFreePtr (lpcs->lpWaveFormat);

            wSize = GetSizeOfWaveFormat (lpwf);
            if (lpcs->lpWaveFormat = (LPWAVEFORMATEX)
                    GlobalAllocPtr (GHND, sizeof (CAPSTREAM))) {
                hmemcpy (lpcs->lpWaveFormat, lpwf, (LONG) wSize);
            }
            dwReturn = TRUE;
        }
        break;

    case WM_CAP_DLG_VIDEOSOURCE:
        // Show the dialog which controls the video source
        // NTSC vs PAL, input channel selection, etc.
        // wParam and lParam are unused
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (lpcs->dwDlgsActive & VDLG_VIDEOSOURCE)
            return FALSE;
        if (lpcs->sCapDrvCaps.fHasDlgVideoSource) {
	    lpcs->dwDlgsActive |= VDLG_VIDEOSOURCE;
            videoDialog (lpcs->hVideoCapture, lpcs->hwnd, 0L );
            // Changing from NTSC to PAL could affect image dimensions!!!
            DibGetNewFormatFromDriver (lpcs);
            PalGetPaletteFromDriver (lpcs);

            // May need to inform parent of new layout here!
            InvalidateRect(lpcs->hwnd, NULL, TRUE);
            UpdateWindow(lpcs->hwnd);
	    lpcs->dwDlgsActive &= ~VDLG_VIDEOSOURCE;
        }
        return (lpcs->sCapDrvCaps.fHasDlgVideoSource);

    case WM_CAP_DLG_VIDEOFORMAT:
        // Show the format dialog, user selects dimensions, depth, compression
        // wParam and lParam are unused
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (lpcs->dwDlgsActive & VDLG_VIDEOFORMAT)
            return FALSE;
        if (lpcs->sCapDrvCaps.fHasDlgVideoFormat) {
	    lpcs->dwDlgsActive |= VDLG_VIDEOFORMAT;
            videoDialog (lpcs->hVideoIn, lpcs->hwnd, 0L );
            DibGetNewFormatFromDriver (lpcs);
            PalGetPaletteFromDriver (lpcs);

            // May need to inform parent of new layout here!
            InvalidateRect(lpcs->hwnd, NULL, TRUE);
            UpdateWindow(lpcs->hwnd);
	    lpcs->dwDlgsActive &= ~VDLG_VIDEOFORMAT;
        }
        return (lpcs->sCapDrvCaps.fHasDlgVideoFormat);

    case WM_CAP_DLG_VIDEODISPLAY:
        // Show the dialog which controls output.
        // This dialog only affects the presentation, never the data format
        // wParam and lParam are unused
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (lpcs->dwDlgsActive & VDLG_VIDEODISPLAY)
            return FALSE;
        if (lpcs->sCapDrvCaps.fHasDlgVideoDisplay) {
	    lpcs->dwDlgsActive |= VDLG_VIDEODISPLAY;
            videoDialog (lpcs->hVideoDisplay, lpcs->hwnd, 0L);
	    lpcs->dwDlgsActive &= ~VDLG_VIDEODISPLAY;
        }
        return (lpcs->sCapDrvCaps.fHasDlgVideoDisplay);

    case WM_CAP_DLG_VIDEOCOMPRESSION:
#ifndef NEW_COMPMAN
	return FALSE;
#else
        // Show the dialog which selects video compression options.
        // wParam and lParam are unused
        if (!lpcs->fHardwareConnected)
            return FALSE;
	if (lpcs->dwDlgsActive & VDLG_COMPRESSION)
            return FALSE;
	lpcs->dwDlgsActive |= VDLG_COMPRESSION;
        ICCompressorChoose(
                lpcs->hwnd,            // parent window for dialog
                ICMF_CHOOSE_KEYFRAME,  // want "key frame every" box
                lpcs->lpBitsInfo,      // input format (optional)
                NULL,                  // input data (optional)
                &lpcs->CompVars,       // data about the compressor/dlg
                NULL);                 // title bar (optional)
	lpcs->dwDlgsActive &= ~VDLG_COMPRESSION;
        return TRUE;
#endif

    case WM_CAP_SET_VIDEOFORMAT:
        // wParam is the size of the BITMAPINFO
        // lParam is an LPBITMAPINFO
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (IsBadReadPtr ((LPVOID) lParam, (UINT) wParam))
            return FALSE;

        return (DibNewFormatFromApp (lpcs, (LPBITMAPINFO) lParam, (UINT) wParam));

    case WM_CAP_SET_PREVIEW:
        // if wParam, enable preview via drawdib
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (wParam) {
            // turn off the overlay, if it is in use
            if (lpcs->fOverlayWindow)
                SendMessage(lpcs->hwnd, WM_CAP_SET_OVERLAY, 0, 0L);
            lpcs->fLiveWindow = TRUE;
            statusUpdateStatus(lpcs, IDS_CAP_STAT_LIVE_MODE);
         } // endif enabling preview
         else {
            lpcs->fLiveWindow = FALSE;
        }
        InvalidateRect (lpcs->hwnd, NULL, TRUE);
        return TRUE;

    case WM_CAP_SET_OVERLAY:
        // if wParam, enable overlay in hardware
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (wParam && lpcs->sCapDrvCaps.fHasOverlay) {
            if (lpcs->fLiveWindow)   // turn off preview mode
                SendMessage(lpcs->hwnd, WM_CAP_SET_PREVIEW, 0, 0L);
            lpcs->fOverlayWindow = TRUE;
            statusUpdateStatus(lpcs, IDS_CAP_STAT_OVERLAY_MODE);
        }
        else {
            lpcs->fOverlayWindow = FALSE;
            videoStreamFini (lpcs->hVideoDisplay); // disable overlay on hardware
        }
        InvalidateRect (lpcs->hwnd, NULL, TRUE);
        return (lpcs->sCapDrvCaps.fHasOverlay);

    case WM_CAP_SET_PREVIEWRATE:
        // wParam contains preview update rate in mS.
        // if wParam == 0 no timer is in use.
        if (lpcs->idTimer) {
            KillTimer(lpcs->hwnd, ID_PREVIEWTIMER);
            lpcs->idTimer = 0;
        }
        if (wParam != 0) {
            lpcs->idTimer = SetTimer (lpcs->hwnd, ID_PREVIEWTIMER,
                        (UINT) wParam, NULL);
        }
        lpcs->uTimeout = (UINT) wParam;
        dwReturn = TRUE;
        break;

    case WM_CAP_GRAB_FRAME:
        // grab a single frame
        // wParam and lParam unused
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (lpcs->sCapDrvCaps.fCaptureInitialized) {

           dwReturn = (DWORD) GetAFrameThenCallback (lpcs, TRUE /*fForce*/);

           // disable live and overlay mode when capturing a single frame
           if (lpcs->fLiveWindow)
               SendMessage(lpcs->hwnd, WM_CAP_SET_PREVIEW, 0, 0L);
           else if (lpcs->fOverlayWindow)
               SendMessage(lpcs->hwnd, WM_CAP_SET_OVERLAY, 0, 0L);
        }
        break;

    case WM_CAP_GRAB_FRAME_NOSTOP:
        // grab a single frame, but don't change state of overlay/preview
        // wParam and lParam unused
        if (!lpcs->fHardwareConnected)
            return FALSE;
        dwReturn = (LONG) GetAFrameThenCallback (lpcs, TRUE /*fForce*/);
        break;

    case WM_CAP_SEQUENCE:
        // This is the main entry for streaming video capture
        // wParam is unused
        // lParam is unused
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (lpcs->sCapDrvCaps.fCaptureInitialized) {
            lpcs->fCaptureFlags |= CAP_fCapturingToDisk;
            return (AVICapture(lpcs));
        }
        break;

    case WM_CAP_SEQUENCE_NOFILE:
        // wParam is unused
        // lParam is unused
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (lpcs->sCapDrvCaps.fCaptureInitialized) {
            lpcs->fCaptureFlags &= ~CAP_fCapturingToDisk;
            return (AVICapture(lpcs));
        }
        break;

    case WM_CAP_SET_SEQUENCE_SETUP:
        // wParam is sizeof CAPTUREPARMS
        // lParam = LPCAPTUREPARMS
        // The following were added after the Beta, init in case the client
        // has a smaller structure and doesn't access them.
	// WHICH BETA ?? (SteveDav)  We should change the comment to include a date

        lpcs->sCapParms.dwAudioBufferSize = 0;
        lpcs->sCapParms.fDisableWriteCache = TRUE;
        lpcs->sCapParms.AVStreamMaster = AVSTREAMMASTER_AUDIO;

        if (wParam <= sizeof (CAPTUREPARMS)) {
            dwT = min (sizeof (CAPTUREPARMS), (UINT) wParam);
            if (IsBadReadPtr ((LPVOID) lParam, (UINT) dwT))
                break;

            _fmemcpy ((LPVOID) &lpcs->sCapParms, (LPVOID) lParam, (UINT) dwT);

            // Validate stuff that isn't handled elsewhere
            if (lpcs->sCapParms.wChunkGranularity != 0 &&
                lpcs->sCapParms.wChunkGranularity < 16)
                lpcs->sCapParms.wChunkGranularity = 16;
            if (lpcs->sCapParms.wChunkGranularity > 16384)
                lpcs->sCapParms.wChunkGranularity = 16384;

            if (lpcs->sCapParms.fLimitEnabled && (lpcs->sCapParms.wTimeLimit == 0))
                lpcs->sCapParms.wTimeLimit = 1;

            // Force Step MCI off if not using MCI control
            if (lpcs->sCapParms.fStepMCIDevice && !lpcs->sCapParms.fMCIControl)
                    lpcs->sCapParms.fStepMCIDevice = FALSE;

            // Prevent audio capture if no audio hardware
            lpcs->sCapParms.fCaptureAudio =
                lpcs->fAudioHardware && lpcs->sCapParms.fCaptureAudio;

            // Limit audio buffers
            lpcs->sCapParms.wNumAudioRequested =
                min (MAX_WAVE_BUFFERS, lpcs->sCapParms.wNumAudioRequested);

            // Limit video buffers
            lpcs->sCapParms.wNumVideoRequested =
                min (MAX_VIDEO_BUFFERS, lpcs->sCapParms.wNumVideoRequested);

            dwReturn = TRUE;
        }
        break;

    // unicode and win-16 version - see ansi thunk below
    case WM_CAP_SET_MCI_DEVICE:
        // lParam points to the name of the MCI Device
        if (IsBadStringPtr ((LPVOID) lParam, 1))
            return FALSE;
        if (lParam) {
            lstrcpyn (lpcs->achMCIDevice, (LPTSTR) lParam, NUMELMS(lpcs->achMCIDevice));
            dwReturn = TRUE;
        }
        break;
#ifdef UNICODE
    // ansi thunk for above
    case WM_CAP_SET_MCI_DEVICEA:
        // lParam points to Ansi name of MCI device
        if (lParam) {
            //remember the null
            int chsize = lstrlenA( (LPSTR) lParam)+1;
            Imbstowcs(lpcs->achMCIDevice, (LPSTR) lParam,
                min(chsize, NUMELMS(lpcs->achMCIDevice)));
            dwReturn = TRUE;
        }
        break;
#endif


    case WM_CAP_SET_SCROLL:
        // lParam is an LPPOINT which points to the new scroll position
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (IsBadReadPtr ((LPVOID) lParam, sizeof (POINT)))
            return FALSE;

        {
            LPPOINT lpP = (LPPOINT) lParam;

            if (lpP->x < lpcs->dxBits && lpP->y < lpcs->dyBits) {
                lpcs->ptScroll = *lpP;
                InvalidateRect (lpcs->hwnd, NULL, TRUE);
                dwReturn = TRUE;
            }
        }
        break;

    case WM_CAP_SET_SCALE:
        // if wParam, Scale the window to the client region?
        if (!lpcs->fHardwareConnected)
            return FALSE;
        lpcs->fScale = (BOOL) wParam;
        return TRUE;

    // unicode and win-16 version - see ansi thunk below
    case WM_CAP_PAL_OPEN:
        // Open a new palette
        // wParam is unused
        // lParam contains an LPTSTR to the file
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (IsBadStringPtr ((LPVOID) lParam, 1))
            return FALSE;
        return fileOpenPalette(lpcs, (LPTSTR) lParam /*lpszFileName*/);
#ifdef UNICODE
    // ansi thunk for above
    case WM_CAP_PAL_OPENA:
        // lParam contains (ANSI) lpstr for filename
        if (lParam) {
            // remember the null
            int chsize = lstrlenA( (LPSTR) lParam)+1;
            LPWSTR pw = LocalAlloc(LPTR, chsize * sizeof(WCHAR));
	    if (pw) {
                Imbstowcs(pw, (LPSTR) lParam, chsize);
                dwReturn = fileOpenPalette(lpcs, pw);
                LocalFree(pw);
	    }
        }
        break;
#endif

    // unicode and win-16 version - see ansi thunk below
    case WM_CAP_PAL_SAVE:
        // Save the current palette in a file
        // wParam is unused
        // lParam contains an LPTSTR to the file
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (IsBadStringPtr ((LPVOID) lParam, 1))
            return FALSE;
        return fileSavePalette(lpcs, (LPTSTR) lParam /*lpszFileName*/);
#ifdef UNICODE
    // ansi thunk for above
    case WM_CAP_PAL_SAVEA:
        // lParam contains (ANSI) lpstr for filename
        if (lParam) {
            // remember the null
            int chsize = lstrlenA( (LPSTR) lParam)+1;
            LPWSTR pw = LocalAlloc(LPTR, chsize * sizeof(WCHAR));
	    if (pw) {
                Imbstowcs(pw, (LPSTR) lParam, chsize);
                dwReturn = fileSavePalette(lpcs, pw);
                LocalFree(pw);
	    }
        }
        break;
#endif


    case WM_CAP_PAL_AUTOCREATE:
        // Automatically capture a palette
        // wParam contains a count of the number of frames to average
        // lParam contains the number of colors desired in the palette
        if (!lpcs->fHardwareConnected)
            return FALSE;
        return CapturePaletteAuto (lpcs, (int) wParam, (int) lParam);

    case WM_CAP_PAL_MANUALCREATE:
        // Manually capture a palette
        // wParam contains TRUE for each frame to capture, FALSE when done
        // lParam contains the number of colors desired in the palette
        if (!lpcs->fHardwareConnected)
            return FALSE;
        return CapturePaletteManual (lpcs, (BOOL) wParam, (int) lParam);

    case WM_CAP_PAL_PASTE:
        // Paste a palette from the clipboard, send to the driver
        if (!lpcs->fHardwareConnected)
            return FALSE;
        if (lpcs->sCapDrvCaps.fCaptureInitialized && OpenClipboard(lpcs->hwnd)) {
            HANDLE  hPal;

            hPal = GetClipboardData(CF_PALETTE);
            CloseClipboard();
            if (hPal) {
                PalSendPaletteToDriver (lpcs, CopyPalette(hPal),  NULL /* XlateTable */);
                InvalidateRect(lpcs->hwnd, NULL, TRUE);
                dwReturn = TRUE;
            }
        }
        break;

    default:
        break;
    }
    return dwReturn;
}


/*--------------------------------------------------------------+
| ****************** THE WINDOW PROCEDURE ********************* |
+--------------------------------------------------------------*/
LRESULT FAR PASCAL LOADDS EXPORT CapWndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    LPCAPSTREAM lpcs;
    PAINTSTRUCT ps;
    HDC         hdc;
    MSG         PMsg;
    int         f;

    lpcs = (LPCAPSTREAM) GetWindowLongPtr (hwnd, GWL_CAPSTREAM);

    if (msg >= WM_CAP_START && msg <= WM_CAP_END)
        return (ProcessCommandMessages (lpcs, msg, wParam, lParam));

    switch (msg) {

    case WM_CREATE:
        lpcs = CapWinCreate (hwnd);
        break;

    case WM_TIMER:
        // Update the preview window if we got a timer and not saving to disk
        GetAFrameThenCallback (lpcs, FALSE /*fForce*/);

        // Added VFW 1.1b, Clear the queue of additional timer msgs!!!

        // Even in Win32, processing frame timers can swamp all other
        // activity in the app, so clear the queue after each frame is done.

        // This successfully corrected a problem with the "Hit OK to continue"
        // dialog not appearing bug due to app message queue
        // swamping with timer messages at large
        // image dimensions and preview rates.

        PeekMessage (&PMsg, hwnd, WM_TIMER, WM_TIMER,PM_REMOVE|PM_NOYIELD);
        break;

    case WM_CLOSE:
        break;

    case WM_DESTROY:
        CapWinDestroy (lpcs);
        break;

    case WM_PALETTECHANGED:
        if (lpcs->hdd == NULL)
            break;

        hdc = GetDC(hwnd);
        if (f = DrawDibRealize(lpcs->hdd, hdc, TRUE /*fBackground*/))
            InvalidateRect(hwnd,NULL,TRUE);
        ReleaseDC(hwnd,hdc);
        return f;

    case WM_QUERYNEWPALETTE:
        if (lpcs->hdd == NULL)
            break;
        hdc = GetDC(hwnd);
        f = DrawDibRealize(lpcs->hdd, hdc, FALSE);
        ReleaseDC(hwnd, hdc);

        if (f)
            InvalidateRect(hwnd, NULL, TRUE);
        return f;

    case WM_SIZE:
    case WM_MOVE:
        if (lpcs->fOverlayWindow)    // Make the driver paint the key color
            InvalidateRect(hwnd, NULL, TRUE);
        break;

    case WM_WINDOWPOSCHANGED:
        if (lpcs->fOverlayWindow)    // Make the driver paint the key color
            InvalidateRect(hwnd, NULL, TRUE);
        return 0;

    case WM_ERASEBKGND:
        return 0;  // don't bother to erase it

    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        if (lpcs->fOverlayWindow) {
            CheckWindowMove(lpcs, ps.hdc, TRUE);
        }
        else {
#ifdef _WIN32
            SetWindowOrgEx(hdc, lpcs->ptScroll.x, lpcs->ptScroll.y, NULL);
#else
            SetWindowOrg(hdc, lpcs->ptScroll.x, lpcs->ptScroll.y);
#endif
            DibPaint(lpcs, hdc);
        }
        EndPaint(hwnd, &ps);
        break;

    default:
        break;
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

#if 0
void dummyTest ()
{
    HWND hwnd;
    FARPROC fpProc;
    DWORD dwSize;
    WORD  wSize;
    BOOL f;
    int i;
    char szName[80];
    char szVer[80];
    DWORD dwMS;
    int iFrames, iColors;
    char s;
    LPPOINT lpP;

    capSetCallbackOnError(hwnd, fpProc);
    capSetCallbackOnStatus(hwnd, fpProc);
    capSetCallbackOnYield(hwnd, fpProc);
    capSetCallbackOnFrame(hwnd, fpProc);
    capSetCallbackOnVideoStream(hwnd, fpProc);
    capSetCallbackOnWaveStream(hwnd, fpProc);

    capDriverConnect(hwnd, i);
    capDriverDisconnect(hwnd);
    capDriverGetName(hwnd, szName, wSize);
    capDriverGetVersion(hwnd, szVer, wSize);
    capDriverGetCaps(hwnd, s, wSize);

    capFileSetCaptureFile(hwnd, szName);
    capFileGetCaptureFile(hwnd, szName, wSize);
    capFileAlloc(hwnd, dwSize);
    capFileSaveAs(hwnd, szName);

    capEditCopy(hwnd);

    capSetAudioFormat(hwnd, s, wSize);
    capGetAudioFormat(hwnd, s, wSize);
    capGetAudioFormatSize(hwnd);

    capDlgVideoFormat(hwnd);
    capDlgVideoSource(hwnd);
    capDlgVideoDisplay(hwnd);

    capPreview(hwnd, f);
    capPreviewRate(hwnd, dwMS);
    capOverlay(hwnd, f);
    capPreviewScale(hwnd, f);
    capGetStatus(hwnd, s, wSize);
    capSetScrollPos(hwnd, lpP);

    capGrabFrame(hwnd);
    capGrabFrameNoStop(hwnd);
    capCaptureSequence(hwnd);
    capCaptureSequenceNoFile(hwnd);
    capCaptureGetSetup(hwnd, s, wSize);
    capCaptureSetSetup(hwnd, s, wSize);

    capCaptureSingleFrameOpen(hwnd);
    capCaptureSingleFrameClose(hwnd);
    capCaptureSingleFrame(hwnd);

    capSetMCIDeviceName(hwnd, szName);
    capGetMCIDeviceName(hwnd, szName, wSize);

    capPalettePaste(hwnd);
    capPaletteAuto(hwnd, iFrames, iColors);
}

#endif