/**************************************************************************
 *
 *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
 *  PURPOSE.
 *
 *  Copyright (c) 1992 - 1995  Microsoft Corporation.  All Rights Reserved.
 *
 **************************************************************************/
/****************************************************************************
 *
 *   vidcap.c: WinMain and command processing
 *
 *   Vidcap32 Source code
 *
 ***************************************************************************/
 
#include <windows.h>
#include <windowsx.h>
#include <commdlg.h>
#include <mmsystem.h>
#include <mmreg.h>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <dos.h>
#include <shellapi.h>
#include <vfw.h>
#include <tchar.h>

#include "vidcap.h"
#include "vidframe.h"
#include "profile.h"

// generic window control classes
#include "toolbar.h"
#include "status.h"
#include "arrow.h"
#include "rlmeter.h"
#include "help.h"

// the standard toolbar class 'exports' this but doesn't put it in the
// header file
extern TCHAR     szToolBarClass[];//HACK!


// height of the buttons on a toolbar - depends on the
// size of the bitmaps within IDBMP_TOOLBAR
#define BUTTONWIDTH     24
#define BUTTONHEIGHT    22
#define TOOLBAR_HEIGHT          BUTTONHEIGHT + 6


// description and layout of toolbar buttons within IDBMP_TOOLBAR
#define APP_NUMTOOLS 8

#define BTN_SETFILE		0
#define BTN_EDITCAP		1
#define BTN_LIVE		2
#define BTN_CAPFRAME		3
#define BTN_CAPSEL		4
#define BTN_CAPAVI		5
#define BTN_CAPPAL		6
#define BTN_OVERLAY		7

static int           aiButton[] = {BTN_SETFILE, BTN_EDITCAP,
                            BTN_LIVE, BTN_OVERLAY, BTN_CAPFRAME,
                            BTN_CAPSEL, BTN_CAPAVI, BTN_CAPPAL };
static int           aiState[] = {BTNST_FOCUSUP, BTNST_UP,
                            BTNST_UP, BTNST_UP, BTNST_UP,
                            BTNST_UP, BTNST_UP, BTNST_UP};
static int           aiType[] ={BTNTYPE_PUSH, BTNTYPE_PUSH,
                            BTNTYPE_CHECKBOX, BTNTYPE_CHECKBOX,
                            BTNTYPE_PUSH,
                            BTNTYPE_PUSH, BTNTYPE_PUSH, BTNTYPE_PUSH};
static int           aiString[] = { IDC_toolbarSETFILE,
                            IDC_toolbarEDITCAP, IDC_toolbarLIVE,
                            IDC_toolbarOVERLAY,
                            IDC_toolbarCAPFRAME, IDC_toolbarCAPSEL,
                            IDC_toolbarCAPAVI, IDC_toolbarCAPPAL };
static int           aPos[] = { 10, 35, 75, 100, 150, 175, 200, 225 };




//
// Global Variables
//

// preferences
BOOL gbCentre;
BOOL gbToolBar;
BOOL gbStatusBar;
BOOL gbAutoSizeFrame;
int gBackColour;

BOOL gbLive, gbOverlay;
BOOL gfIsRTL;

// saved window sizes
int gWinX, gWinY;
int gWinCX, gWinCY;
int gWinShow;

// command line options
int gCmdLineDeviceID = -1;


TCHAR          gachAppName[]  = "vidcapApp" ;
TCHAR          gachIconName[] = "vidcapIcon" ;
TCHAR          gachMenuName[] = "vidcapMenu" ;
TCHAR          gachAppTitle[20];    //VidCap
TCHAR          gachCaptureFile[_MAX_PATH];
TCHAR          gachMCIDeviceName[21];
TCHAR          gachString[128] ;
TCHAR          gachBuffer[200] ;
TCHAR          gachLastError[256];


HINSTANCE      ghInstApp ;
HWND           ghWndMain = NULL ;
HWND           ghWndFrame;      // child of ghWndMain  - frames and scrolls
HWND           ghWndCap  ;      // child of ghWndCap
HWND           ghWndToolBar;
HWND           ghWndStatus;

HANDLE         ghAccel ;
WORD           gwDeviceIndex ;
WORD           gwPalFrames = DEF_PALNUMFRAMES ;
WORD           gwPalColors = DEF_PALNUMCOLORS ;
WORD           gwCapFileSize ;

CAPSTATUS      gCapStatus ;
CAPDRIVERCAPS  gCapDriverCaps ;
CAPTUREPARMS   gCapParms ;
BOOL           gbHaveHardware;
UINT           gDriverCount;
BOOL           gbIsScrncap;  // For Scrncap.drv, we must yield
BOOL           gbInLayout;
UINT           gAVStreamMaster;

HANDLE         ghwfex ;
LPWAVEFORMATEX glpwfex ;

FARPROC        fpErrorCallback ;
FARPROC        fpStatusCallback ;
FARPROC        fpYieldCallback ;


// set to false when we capture a palette (or if we have warned him and
// he says its ok
BOOL bDefaultPalette = TRUE;

#ifdef DEBUG
int	nTestCount;
#endif

// c-runtime cmd line
extern char ** __argv;
extern int __argc;

#define LimitRange(Val,Low,Hi) (max(Low,(min(Val,Hi))))


//
// Function prototypes
//
LRESULT FAR PASCAL MainWndProc(HWND, UINT, WPARAM, LPARAM) ;
LRESULT FAR PASCAL ErrorCallbackProc(HWND, int, LPSTR) ;
LRESULT FAR PASCAL StatusCallbackProc(HWND, int, LPSTR) ;
LRESULT FAR PASCAL YieldCallbackProc(HWND) ;
void vidcapSetLive(BOOL bLive);
void vidcapSetOverlay(BOOL bOverlay);
void vidcapSetCaptureFile(LPTSTR pFileName);

BOOL vidcapRegisterClasses(HINSTANCE hInstance, HINSTANCE hPrevInstance);
BOOL vidcapCreateWindows(HINSTANCE hInstance, HINSTANCE hPrevInstance);
void vidcapLayout(HWND hwnd);
BOOL vidcapEnumerateDrivers(HWND hwnd);
BOOL vidcapInitHardware(HWND hwnd, HWND hwndCap, UINT uIndex);
void vidcapReadProfile(void);
void vidcapWriteProfile(void);
void vidcapReadSettingsProfile(void);
void vidcapWriteSettingsProfile(void);


/* --- initialisation -------------------------------------------------- */


//
// WinMain: Application Entry Point Function
//
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
///////////////////////////////////////////////////////////////////////
//  hInstance:      handle for this instance
//  hPrevInstance:  handle for possible previous instances
//  lpszCmdLine:    long pointer to exec command line
//  nCmdShow:       Show code for main window display
///////////////////////////////////////////////////////////////////////

    MSG          msg ;
    BOOL bValidCmdline;
    BOOL fOK;
    int i;
    char ach[2];

    ghInstApp = hInstance ;
    LoadString(hInstance, IDS_CAP_RTL, ach, sizeof(ach));
    gfIsRTL = ach[0] == '1';
    gCmdLineDeviceID = -1;

    // read the app title string - used in several message boxes
    LoadString(hInstance, IDS_APP_TITLE, gachAppTitle, sizeof(gachAppTitle));

    // read defaults out of the registry
    vidcapReadProfile();

    // look for cmd line options
    bValidCmdline = TRUE;

    for ( i = 1; (i < __argc) && bValidCmdline; i++) {
        if ((__argv[i][0] == '/') || (__argv[i][0] == '-')) {

            switch(__argv[i][1]) {
            case 'D':
            case 'd':
                if (gCmdLineDeviceID < 0) {
                    // allow "-d0" and "-d 0"
                    PSTR p = &__argv[i][2];

                    if ((*p == 0) && ((i+1) < __argc)) {
                        p = __argv[++i];
                    }


                    gCmdLineDeviceID = atoi(p);
                } else {
                    bValidCmdline = FALSE;
                }
                break;

            default:
                bValidCmdline = FALSE;
            }
        } else {
            bValidCmdline = FALSE;
        }
    }
    
    if (gCmdLineDeviceID == -1)
	gCmdLineDeviceID = 0;


    if (!bValidCmdline) {
        MessageBoxID(IDS_ERR_CMDLINE, MB_OK|MB_ICONEXCLAMATION);
        return(0);
    }

    if (!vidcapRegisterClasses(hInstance, hPrevInstance)) {

        MessageBoxID(IDS_ERR_REGISTER_CLASS,
#ifdef BIDI
            MB_RTL_READING |
#endif

        MB_ICONEXCLAMATION) ;
        return 0 ;
    }


    if (!vidcapCreateWindows(hInstance, hPrevInstance)) {

        MessageBoxID(IDS_ERR_CREATE_WINDOW,
#ifdef BIDI
                MB_RTL_READING |
#endif

        MB_ICONEXCLAMATION | MB_OK) ;
        return IDS_ERR_CREATE_WINDOW ;
    }

    // Get the default setup for video capture from the AVICap window
    capCaptureGetSetup(ghWndCap, &gCapParms, sizeof(CAPTUREPARMS)) ;

    // Overwrite the defaults with settings we have saved in the profile
    vidcapReadSettingsProfile();

    // Show the main window before connecting the hardware as this can be
    // time consuming and the user should see something happening first...
    ShowWindow(ghWndMain, nCmdShow) ;
    UpdateWindow(ghWndMain) ;
    ghAccel = LoadAccelerators(hInstance, "VIDCAP") ;

    // Create a list of all capture drivers and append them to the Options menu
    if (!(fOK = vidcapEnumerateDrivers(ghWndMain))) {
	LoadString(ghInstApp, IDS_ERR_FIND_HARDWARE, gachLastError, sizeof(gachLastError)/sizeof(*gachLastError));
    }
    // Try to connect to a capture driver
    else if (fOK = vidcapInitHardware(ghWndMain, ghWndCap, 
			       bValidCmdline ? gCmdLineDeviceID : 0)) {
	// Hooray, we now have a capture driver connected!
        vidcapSetCaptureFile(gachCaptureFile);
    }
    
    if (!fOK) {
        if (!DoDialog(ghWndMain, IDD_NoCapHardware, NoHardwareDlgProc,
                        (LONG_PTR) (LPSTR) gachLastError)) {
            // The user has asked to abort, since no driver was available
            PostMessage(ghWndMain, WM_COMMAND,
                        GET_WM_COMMAND_MPS(IDM_F_EXIT, 0, 0));
        }
    }
    
    // All set; get and process messages
    while (GetMessage(&msg, NULL, 0, 0)) {
        if (! TranslateAccelerator(ghWndMain, ghAccel, &msg)) {
            TranslateMessage(&msg) ;
            DispatchMessage(&msg) ;
        }
    }

    return (int) msg.wParam;
}  // End of WinMain


BOOL
vidcapRegisterClasses(HINSTANCE hInstance, HINSTANCE hPrevInstance)
{
    WNDCLASS wc;

    if (! hPrevInstance) {
        // If it's the first instance, register the window class
        wc.lpszClassName = gachAppName ;
        wc.hInstance     = hInstance ;
        wc.lpfnWndProc   = MainWndProc ;
        wc.hCursor       = LoadCursor(NULL, IDC_ARROW) ;
        wc.hIcon         = LoadIcon(hInstance, gachIconName) ;
        wc.lpszMenuName  = gachMenuName ;
        wc.hbrBackground = GetStockObject(WHITE_BRUSH) ;
        wc.style         = CS_HREDRAW | CS_VREDRAW ;
        wc.cbClsExtra    = 0 ;
        wc.cbWndExtra    = 0 ;

        if (!RegisterClass(&wc)) {
            return(FALSE);
        }

        if (!ArrowInit(hInstance)) {
            return(FALSE);
        }

        if (!RLMeter_Register(hInstance)) {
            return(FALSE);
        }
    }

    if (!toolbarInit(hInstance, hPrevInstance)) {
        return(FALSE);
    }

    if (!statusInit(hInstance, hPrevInstance)) {
        return(FALSE);
    }
    return(TRUE);

}

BOOL
vidcapCreateWindows(HINSTANCE hInstance, HINSTANCE hPrevInstance)
{

    POINT pt;
    RECT rc;
    TOOLBUTTON tb;
    int i;

    // Create Application's Main window
    ghWndMain = CreateWindowEx(
            gfIsRTL ? WS_EX_LEFTSCROLLBAR | WS_EX_RIGHT | WS_EX_RTLREADING : 0,
            gachAppName,
            gachAppTitle,
            WS_CAPTION      |
            WS_SYSMENU      |
            WS_MINIMIZEBOX  |
            WS_MAXIMIZEBOX  |
            WS_THICKFRAME   |
            WS_CLIPCHILDREN |
            WS_OVERLAPPED,
            gWinX, gWinY,
            gWinCX, gWinCY,
            NULL,
            NULL,
            hInstance,
            0) ;

    if (ghWndMain == NULL) {
        return(FALSE);
    }


    /*
     * create a vidframe child window - this will create a child
     * AVICAP window within itself.
     *
     * Don't worry about size and position - vidcapLayout will do this
     * later (once we know the video format size).
     */
    ghWndFrame = vidframeCreate(
                    ghWndMain,
                    hInstance,
                    hPrevInstance,
                    0, 0, 0, 0,
                    &ghWndCap);

    if ((ghWndFrame == NULL) || (ghWndCap == NULL)) {
        return(FALSE);
    }

    // Register the status and error callbacks before driver connects
    // so we can get feedback about the connection process
    fpErrorCallback = MakeProcInstance((FARPROC)ErrorCallbackProc, ghInstApp) ;
    capSetCallbackOnError(ghWndCap, fpErrorCallback) ;

    fpStatusCallback = MakeProcInstance((FARPROC)StatusCallbackProc, ghInstApp) ;
    capSetCallbackOnStatus(ghWndCap, fpStatusCallback) ;

    // We'll only install a yield callback later if using Scrncap.drv
    fpYieldCallback = MakeProcInstance((FARPROC)YieldCallbackProc, ghInstApp) ;
    

    /*
     * CREATE THE TOOL BAR WINDOW
     */
    /* NOTE: let vidcapLayout() position it */
    ghWndToolBar = CreateWindowEx(
            gfIsRTL ? WS_EX_LEFTSCROLLBAR | WS_EX_RIGHT | WS_EX_RTLREADING : 0,
            szToolBarClass,
            NULL,
            WS_CHILD|WS_BORDER|WS_VISIBLE|WS_TABSTOP|
            WS_CLIPSIBLINGS,
            0, 0,
            0, 0,
            ghWndMain,
            NULL,
            hInstance,
            NULL);


    if (ghWndToolBar == NULL) {
        return(FALSE);
    }

    /* set the bitmap and button size to be used for this toolbar */
    pt.x = BUTTONWIDTH;
    pt.y = BUTTONHEIGHT;
    toolbarSetBitmap(ghWndToolBar, hInstance, IDBMP_TOOLBAR, pt);

    for (i = 0; i < APP_NUMTOOLS; i++) {
	rc.left = aPos[i];
	rc.top = 2;
	rc.right = rc.left + pt.x;
	rc.bottom = rc.top + pt.y;
	tb.rc = rc;
	tb.iButton = aiButton[i];
	tb.iState = aiState[i];
	tb.iType = aiType[i];
	tb.iString = aiString[i];
	toolbarAddTool(ghWndToolBar, tb);
    }

    // create the status bar - let vidcapLayout do the positioning
    ghWndStatus = CreateWindowEx(
                    gfIsRTL ? WS_EX_LEFTSCROLLBAR | WS_EX_RIGHT | WS_EX_RTLREADING : 0,
                    szStatusClass,
                    NULL,
                    WS_CHILD|WS_BORDER|WS_VISIBLE|WS_CLIPSIBLINGS,
                    0, 0,
                    0, 0,
                    ghWndMain,
                    NULL,
                    hInstance,
                    NULL);
    if (ghWndStatus == NULL) {
        return(FALSE);
    }

    return(TRUE);

}

/*
 * Enumerate the potential capture drivers and add the list to the Options
 * menu.  This function is only called once at startup.
 * Returns FALSE if no drivers are available.
 */
BOOL
vidcapEnumerateDrivers(HWND hwnd)
{
    TCHAR   achDeviceVersion[80] ;
    TCHAR   achDeviceAndVersion[160] ;
    UINT    uIndex ;
    HMENU   hMenuSub;

    gDriverCount = 0 ;

    hMenuSub = GetSubMenu (GetMenu (hwnd), 2);  // Options menu

    for (uIndex = 0 ; uIndex < MAXVIDDRIVERS ; uIndex++) {
        if (capGetDriverDescription(uIndex,
                       (LPSTR)achDeviceAndVersion, sizeof(achDeviceAndVersion),
                       (LPSTR)achDeviceVersion, sizeof(achDeviceVersion))) {
            // Concatenate the device name and version strings
            lstrcat (achDeviceAndVersion, ",   ");
            lstrcat (achDeviceAndVersion, achDeviceVersion);

            AppendMenu (hMenuSub, 
                        MF_STRING,
                        IDM_O_DRIVER0 + uIndex, 
                        achDeviceAndVersion);
            gDriverCount++;
        }
        else
            break;
    }		 

    // Now refresh menu, position capture window, start driver etc
    DrawMenuBar(ghWndMain) ;

    return (gDriverCount);
}

/*
 * Connect the capture window to a capture driver.
 * uIndex specifies the index of the driver to use.
 * Returns TRUE on success, or FALSE if the driver connection failed.
 */
BOOL
vidcapInitHardware(HWND hwnd, HWND hwndCap, UINT uIndex)
{
    UINT    uError ;
    UINT    uI;
    HMENU   hMenu;
    TCHAR   szName[MAX_PATH];
    TCHAR   szVersion[MAX_PATH];

    // Since the driver may not provide a reliable error string
    // provide a default
    LoadString(ghInstApp, IDS_ERR_FIND_HARDWARE, gachLastError, sizeof(gachLastError)/sizeof(*gachLastError));

    // Try connecting to the capture driver
    if (uError = capDriverConnect(hwndCap, uIndex)) {
        gbHaveHardware = TRUE;
        gwDeviceIndex = (WORD) uIndex;
    }
    else {
        gbHaveHardware = FALSE;
        gbLive = FALSE;
        gbOverlay = FALSE;
    }

    // Get the capabilities of the capture driver
    capDriverGetCaps(hwndCap, &gCapDriverCaps, sizeof(CAPDRIVERCAPS)) ;

    // Get the settings for the capture window
    capGetStatus(hwndCap, &gCapStatus , sizeof(gCapStatus));

    // Modify the toolbar buttons
    toolbarModifyState(ghWndToolBar, BTN_CAPFRAME, 
        gbHaveHardware ? BTNST_UP : BTNST_GRAYED);
    toolbarModifyState(ghWndToolBar, BTN_CAPSEL, 
        gbHaveHardware ? BTNST_UP : BTNST_GRAYED);
    toolbarModifyState(ghWndToolBar, BTN_CAPAVI, 
        gbHaveHardware ? BTNST_UP : BTNST_GRAYED);
    toolbarModifyState(ghWndToolBar, BTN_LIVE, 
        gbHaveHardware ? BTNST_UP : BTNST_GRAYED);

    // Is overlay supported?
    toolbarModifyState(ghWndToolBar, BTN_OVERLAY, 
        (gbHaveHardware && gCapDriverCaps.fHasOverlay) ? 
        BTNST_UP : BTNST_GRAYED);

    // Can the device create palettes?
    toolbarModifyState(ghWndToolBar, BTN_CAPPAL, 
        (gbHaveHardware && gCapDriverCaps.fDriverSuppliesPalettes) ? 
        BTNST_UP : BTNST_GRAYED);

    // Check the appropriate driver in the Options menu
    hMenu = GetMenu (hwnd);
    for (uI = 0; uI < gDriverCount; uI++) {
        CheckMenuItem (hMenu, IDM_O_DRIVER0 + uI, 
                MF_BYCOMMAND | ((uIndex == uI) ? MF_CHECKED : MF_UNCHECKED));
    } 

    // Unlike all other capture drivers, Scrncap.drv needs to use
    // a Yield callback, and we don't want to abort on mouse clicks,
    // so determine if the current driver is Scrncap.drv
    capGetDriverDescription (uIndex, 
                szName, sizeof (szName),
                szVersion, sizeof (szVersion));

    // Set a flag if we're using Scrncap.drv
    gbIsScrncap = (_tcsstr (szName, "Screen Capture") != NULL);

    // Get video format and adjust capture window
    vidcapLayout(ghWndMain);
    InvalidateRect(ghWndMain, NULL, TRUE);

    // set the preview rate (units are millisecs)
    capPreviewRate(hwndCap, gbHaveHardware ? 33 : 0); 

    // set live/overlay to default
    vidcapSetLive(gbLive);
    vidcapSetOverlay(gbOverlay);

    _tcscat (szName, ",   ");
    _tcscat (szName, szVersion);

    statusUpdateStatus(ghWndStatus, 
        gbHaveHardware ? szName : gachLastError);

    return gbHaveHardware;
}


/*
 * layout the main window. Put the toolbar at the top and the status
 * line at the bottom, and then give all the rest to vidframe,
 *  - it will centre or scroll the AVICAP window appropriately.
 */
void
vidcapLayout(HWND hwnd)
{
    RECT rc;
    RECT rw;
    int cy;
    int cyBorder, cxBorder;
    int cyTotal;
    int cxToolbar;
    int cyMenuAndToolbarAndCaption;

    gbInLayout = TRUE;  // So that we process WM_GETMINMAXINFO normally

    /* for both the toolbar and status bar window,
     * we want just one of the four borders. We do this
     * by setting the WS_BORDER style, and sizing and positioning
     * the window so that the 3 unwanted borders are outside the parent.
     */
    cyBorder = GetSystemMetrics(SM_CYBORDER);
    cxBorder = GetSystemMetrics(SM_CXBORDER);

    // Figure out the height of the menu, toolbar, and caption
    GetWindowRect (hwnd, &rw);
    GetClientRect (hwnd, &rc);

    ClientToScreen (hwnd, (LPPOINT) &rc);
    cyMenuAndToolbarAndCaption = (rc.top - rw.top) + TOOLBAR_HEIGHT;

    cxToolbar = aPos[APP_NUMTOOLS - 1] + BUTTONWIDTH * 3;

    if (gbAutoSizeFrame && gbHaveHardware && gCapStatus.uiImageWidth) {
        cyTotal = gCapStatus.uiImageHeight +
                cyMenuAndToolbarAndCaption +
                (gbStatusBar ? statusGetHeight() : 0) +
                cyBorder * 2 + 
                12;     // vidFrame height
        // Never make the frame smaller than the toolbar
        if (gCapStatus.uiImageWidth >= (UINT) cxToolbar) {
            SetWindowPos(
                hwnd,
                0,	// placement-order handle
                0,	// horizontal position
                0,	// vertical position
                gCapStatus.uiImageWidth + cxBorder * 24,	// width
                cyTotal,	// height
                SWP_NOZORDER | SWP_NOMOVE 	// window-positioning flags
                );
        } else {
            SetWindowPos(
                hwnd,
                0,	// placement-order handle
                0,	// horizontal position
                0,	// vertical position
                cxToolbar,	// width
                cyTotal,	// height
                SWP_NOZORDER | SWP_NOMOVE 	// window-positioning flags
                );
        }
    }

    GetClientRect(hwnd, &rc);

    if (gbToolBar) {
        // put the toolbar at the top - in fact, just off the top so as to
        // hide it's border
        MoveWindow(
            ghWndToolBar,
            -cxBorder, -cyBorder,
            RECTWIDTH(rc)+ (cxBorder * 2),
            TOOLBAR_HEIGHT,
            TRUE);
        rc.top += (TOOLBAR_HEIGHT - cyBorder);
    } else {
        MoveWindow(ghWndToolBar, 0, 0, 0, 0, TRUE);
    }

    // status bar at the bottom
    if (gbStatusBar) {
        cy = statusGetHeight() + cyBorder;
        MoveWindow(
            ghWndStatus,
            -cxBorder, rc.bottom - cy,
            RECTWIDTH(rc) + (2 * cxBorder), cy + cyBorder,
            TRUE);
        rc.bottom -= cy;
    } else {
        MoveWindow(ghWndStatus, 0, 0, 0, 0, TRUE);
    }

    // rest of window goes to vidframe window
    MoveWindow(
        ghWndFrame,
        rc.left, rc.top,
        RECTWIDTH(rc), RECTHEIGHT(rc),
        TRUE);

    // Always layout the frame window, since it is aligned on a
    // DWORD boundary for maximum codec drawing efficiency
    vidframeLayout(ghWndFrame, ghWndCap);

    gbInLayout = FALSE; 
}

/*
 * initialise settings from the profile used before window creation time
 */
void
vidcapReadProfile(void)
{
    // read defaults out of the registry
    gbCentre = mmGetProfileFlag(gachAppTitle, "CenterImage", TRUE);
    gbToolBar = mmGetProfileFlag(gachAppTitle, "ToolBar", TRUE);
    gbStatusBar = mmGetProfileFlag(gachAppTitle, "StatusBar", TRUE);
    gbAutoSizeFrame = mmGetProfileFlag(gachAppTitle, "AutoSizeFrame", TRUE);
    gBackColour = mmGetProfileInt(gachAppTitle, "BackgroundColor", IDD_PrefsLtGrey);

    gWinX = mmGetProfileInt(gachAppTitle, "WindowXPos", (UINT) CW_USEDEFAULT);
	if (gWinX != (UINT) CW_USEDEFAULT)
    	gWinX = LimitRange(gWinX, 0, GetSystemMetrics (SM_CXSCREEN) - 40);
    gWinY = mmGetProfileInt(gachAppTitle, "WindowYPos", 0);
    gWinY = LimitRange(gWinY, 0, GetSystemMetrics (SM_CYSCREEN) - 40);
    gWinCX = mmGetProfileInt(gachAppTitle, "WindowWidth", 320);
    gWinCX = LimitRange(gWinCX, 20, GetSystemMetrics (SM_CXSCREEN));
    gWinCY = mmGetProfileInt(gachAppTitle, "WindowHeight", 240);
    gWinCY = LimitRange(gWinCY, 20, GetSystemMetrics (SM_CYSCREEN));
    gWinShow = mmGetProfileInt(gachAppTitle, "WindowShow", SW_SHOWDEFAULT);
    gWinShow = LimitRange(gWinShow, SW_SHOWNORMAL, SW_SHOWDEFAULT);

    gbOverlay = mmGetProfileInt(gachAppTitle, "OverlayWindow", FALSE);
    gbLive = mmGetProfileInt(gachAppTitle, "LiveWindow", TRUE);
}


void
vidcapWriteProfile(void)
{
    mmWriteProfileFlag(gachAppTitle, "CenterImage", gbCentre, TRUE);
    mmWriteProfileFlag(gachAppTitle, "ToolBar", gbToolBar, TRUE);
    mmWriteProfileFlag(gachAppTitle, "StatusBar", gbStatusBar, TRUE);
    mmWriteProfileFlag(gachAppTitle, "AutoSizeFrame", gbAutoSizeFrame, TRUE);
    mmWriteProfileInt(gachAppTitle,  "BackgroundColor", gBackColour, IDD_PrefsLtGrey);

    mmWriteProfileInt(gachAppTitle, "WindowXPos", gWinX, (UINT) CW_USEDEFAULT);
    mmWriteProfileInt(gachAppTitle, "WindowYPos", gWinY, 0);
    mmWriteProfileInt(gachAppTitle, "WindowWidth", gWinCX, 320);
    mmWriteProfileInt(gachAppTitle, "WindowHeight", gWinCY, 240);
    mmWriteProfileInt(gachAppTitle, "WindowShow", gWinShow, SW_SHOWDEFAULT);

    mmWriteProfileInt(gachAppTitle, "OverlayWindow", gbOverlay, FALSE);
    mmWriteProfileInt(gachAppTitle, "LiveWindow", gbLive, TRUE);
}

/*
 * initialise settings from the profile used AFTER window creation time
 */
void
vidcapReadSettingsProfile(void)
{
    DWORD dwSize;
    
    mmGetProfileString(gachAppTitle, "CaptureFile", "",
        gachCaptureFile, sizeof(gachCaptureFile));

    mmGetProfileString(gachAppTitle, "MCIDevice", "VideoDisc",
                gachMCIDeviceName, sizeof(gachMCIDeviceName));

    gCapParms.dwRequestMicroSecPerFrame = 
                mmGetProfileInt(gachAppTitle, "MicroSecPerFrame", 
                DEF_CAPTURE_RATE);

    gCapParms.dwRequestMicroSecPerFrame = 
                mmGetProfileInt(gachAppTitle, "MicroSecPerFrame", 
                DEF_CAPTURE_RATE);

    gCapParms.fCaptureAudio = mmGetProfileFlag(gachAppTitle, "CaptureAudio", 
                gCapStatus.fAudioHardware);

    gCapParms.fLimitEnabled = mmGetProfileFlag(gachAppTitle, "LimitEnabled", 
                FALSE);

    gCapParms.wTimeLimit = 
                mmGetProfileInt(gachAppTitle, "TimeLimit", 30);

    gCapParms.fMCIControl= mmGetProfileFlag(gachAppTitle, "MCIControl", FALSE);

    gCapParms.fStepMCIDevice= mmGetProfileFlag(gachAppTitle, "StepMCIDevice", FALSE);

    gCapParms.dwMCIStartTime = 
                mmGetProfileInt(gachAppTitle, "MCIStartTime", 10000);

    gCapParms.dwMCIStopTime = 
                mmGetProfileInt(gachAppTitle, "MCIStopTime", 20000);

    gCapParms.fStepCaptureAt2x = mmGetProfileFlag(gachAppTitle, "StepCapture2x", 
                FALSE);

    gCapParms.wStepCaptureAverageFrames = 
                mmGetProfileInt(gachAppTitle, "StepCaptureAverageFrames", 3);

    gCapParms.AVStreamMaster = mmGetProfileInt (gachAppTitle, "AVStreamMaster",
                AVSTREAMMASTER_AUDIO);

    gCapParms.fUsingDOSMemory = mmGetProfileFlag (gachAppTitle, "CaptureToDisk",
                TRUE);

    gCapParms.dwIndexSize = 
                mmGetProfileInt(gachAppTitle, "IndexSize", 
                CAP_SMALL_INDEX);
    
    // Retrieve the saved audio format
    // Ask the ACM what the largest known wave format is
    acmMetrics(NULL,
               ACM_METRIC_MAX_SIZE_FORMAT,
               &dwSize);

    // If a wave format was saved in the registry, use that size
    dwSize = max (dwSize, mmGetProfileBinary(gachAppTitle, "WaveFormatBinary",
			   NULL,
			   NULL,
			   0));
		  
    if (glpwfex = (LPWAVEFORMATEX) GlobalAllocPtr(GHND, dwSize)) {
	capGetAudioFormat(ghWndCap, glpwfex, (WORD)dwSize) ;
	mmGetProfileBinary(gachAppTitle, "WaveFormatBinary",
			   glpwfex,
			   glpwfex,
			   dwSize);

	// Do some sanity checking
	if (MMSYSERR_NOERROR == waveInOpen (NULL, WAVE_MAPPER,
					    glpwfex, 0, 0, WAVE_FORMAT_QUERY)) {
	    capSetAudioFormat(ghWndCap, glpwfex, (WORD)dwSize) ;
	} 
	GlobalFreePtr(glpwfex) ;
    }
}


void
vidcapWriteSettingsProfile(void)
{
    mmWriteProfileString(gachAppTitle, "CaptureFile", gachCaptureFile);

    mmWriteProfileString(gachAppTitle, "MCIDevice", gachMCIDeviceName);

    mmWriteProfileInt(gachAppTitle, "MicroSecPerFrame", 
                gCapParms.dwRequestMicroSecPerFrame, DEF_CAPTURE_RATE);

    mmWriteProfileFlag(gachAppTitle, "CaptureAudio", 
                gCapParms.fCaptureAudio, gCapStatus.fAudioHardware);

    mmWriteProfileFlag(gachAppTitle, "LimitEnabled", 
                gCapParms.fLimitEnabled, FALSE);

    mmWriteProfileInt(gachAppTitle, "TimeLimit", 
                gCapParms.wTimeLimit, 30);

    mmWriteProfileFlag(gachAppTitle, "MCIControl", 
                gCapParms.fMCIControl, FALSE);

    mmWriteProfileFlag(gachAppTitle, "StepMCIDevice", 
                gCapParms.fStepMCIDevice, FALSE);

    mmWriteProfileInt(gachAppTitle, "MCIStartTime", 
                gCapParms.dwMCIStartTime, 10000);

    mmWriteProfileInt(gachAppTitle, "MCIStopTime", 
                gCapParms.dwMCIStopTime, 20000);

    mmWriteProfileFlag(gachAppTitle, "StepCapture2x", 
                gCapParms.fStepCaptureAt2x, FALSE);

    mmWriteProfileInt(gachAppTitle, "StepCaptureAverageFrames", 
                gCapParms.wStepCaptureAverageFrames, 3);

    mmWriteProfileInt(gachAppTitle, "AVStreamMaster", 
                gCapParms.AVStreamMaster, AVSTREAMMASTER_AUDIO);

    mmWriteProfileFlag(gachAppTitle, "CaptureToDisk", 
                gCapParms.fUsingDOSMemory, TRUE);

    mmWriteProfileInt(gachAppTitle, "IndexSize", 
                gCapParms.dwIndexSize, CAP_SMALL_INDEX);

    // The audio format is written whenever it is changed via dlg
}




/* --- error/status functions -------------------------------------------*/

/*
 * put up a message box loading a string from the
 * resource file
 */
int
MessageBoxID(UINT idString, UINT fuStyle)
{
    TCHAR achMessage[256];   // max message length

    LoadString(ghInstApp, idString, achMessage, sizeof(achMessage));

    return MessageBox(ghWndMain, achMessage, gachAppTitle, fuStyle);
}



//
// ErrorCallbackProc: Error Callback Function
//
LRESULT FAR PASCAL ErrorCallbackProc(HWND hWnd, int nErrID, LPSTR lpErrorText)
{
////////////////////////////////////////////////////////////////////////
//  hWnd:          Application main window handle
//  nErrID:        Error code for the encountered error
//  lpErrorText:   Error text string for the encountered error
////////////////////////////////////////////////////////////////////////

    if (!ghWndMain)
        return FALSE;

    if (nErrID == 0)            // Starting a new major function
        return TRUE;            // Clear out old errors...

    // save the error message for use in NoHardwareDlgProc
    _tcscpy(gachLastError, lpErrorText);

    // Show the error ID and text

    MessageBox(hWnd, lpErrorText, gachAppTitle,
#ifdef BIDI
                MB_RTL_READING |
#endif
                MB_OK | MB_ICONEXCLAMATION) ;

    return (LRESULT) TRUE ;
}


//
// StatusCallbackProc: Status Callback Function
//
LRESULT FAR PASCAL StatusCallbackProc(HWND hWnd, int nID, LPSTR lpStatusText)
{
////////////////////////////////////////////////////////////////////////
//  hWnd:           Application main window handle
//  nID:            Status code for the current status
//  lpStatusText:   Status text string for the current status
////////////////////////////////////////////////////////////////////////

    static int CurrentID;

    if (!ghWndMain) {
        return FALSE;
    }

    // the CAP_END message sometimes overwrites a useful
    // statistics message.
    if (nID == IDS_CAP_END) {
        if ((CurrentID == IDS_CAP_STAT_VIDEOAUDIO) ||
            (CurrentID == IDS_CAP_STAT_VIDEOONLY)) {

            return(TRUE);
        }
    }
    CurrentID = nID;


    statusUpdateStatus(ghWndStatus, lpStatusText);

    return (LRESULT) TRUE ;
}


//
// YieldCallbackProc: Status Callback Function
// (Only used for Scrncap.drv driver)
//
LRESULT FAR PASCAL YieldCallbackProc(HWND hWnd)
{
////////////////////////////////////////////////////////////////////////
//  hWnd:           Application main window handle
////////////////////////////////////////////////////////////////////////

    MSG msg;

    if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // Return TRUE to continue capturing
    return (LRESULT) TRUE;
}



/*
 * load a string from the string table and return
 * a pointer to it for temporary use. Each call
 * overwrites the previous
 */
LPTSTR
tmpString(UINT idString)
{
    static TCHAR ach[350];

    LoadString(ghInstApp, idString, ach, sizeof(ach)/sizeof(TCHAR));

    // ensure null terminated
    ach[sizeof(ach) -1] = 0;

    return(ach);
}




/* --- connect to and init hardware ------------------------------------- */


void
vidcapSetLive(BOOL bLive)
{
    capPreview(ghWndCap, bLive);
    toolbarModifyState(ghWndToolBar, BTN_LIVE, bLive? BTNST_DOWN : BTNST_UP);
    CheckMenuItem(GetMenu(ghWndMain), IDM_O_PREVIEW,
        MF_BYCOMMAND | (bLive ? MF_CHECKED : MF_UNCHECKED));

    gbLive = bLive;

    if (bLive == TRUE) {
        vidcapSetOverlay(FALSE);
    }
}

void
vidcapSetOverlay(BOOL bOverlay)
{
    if (!gCapDriverCaps.fHasOverlay) {
        CheckMenuItem(GetMenu(ghWndMain), IDM_O_OVERLAY,
            MF_BYCOMMAND | MF_UNCHECKED);
        gbOverlay = FALSE;
        return;
    }

    capOverlay(ghWndCap, bOverlay);
    toolbarModifyState(ghWndToolBar, BTN_OVERLAY, bOverlay ? BTNST_DOWN : BTNST_UP);
    CheckMenuItem(GetMenu(ghWndMain), IDM_O_OVERLAY,
        MF_BYCOMMAND | (bOverlay ? MF_CHECKED : MF_UNCHECKED));

    gbOverlay = bOverlay;

    if (bOverlay == TRUE) {
        vidcapSetLive(FALSE);
    }
}

void
vidcapSetCaptureFile(LPTSTR pFileName)
{
    TCHAR achBuffer[_MAX_PATH];

    if ((pFileName != NULL) && (_tcslen(pFileName)  > 0)) {
        // record the capture filename
        if (_tcscmp(gachCaptureFile, pFileName)) {
            _tcscpy(gachCaptureFile, pFileName);
        }

        // and set window title
        wsprintf(achBuffer, "%s - %s", gachAppTitle, pFileName);
    } else {
        gachCaptureFile[0] = 0;
        _tcscpy(achBuffer, gachAppTitle);
    }

    capFileSetCaptureFile(ghWndCap, gachCaptureFile);
    SetWindowText(ghWndMain, achBuffer);
}

/* --- winproc and message handling --------------------------------------- */

/*
 * called from WM_COMMAND processing if the
 * message is from the toolbar. iButton contains the
 * button ID in the lower 8 bits, and the flags in the upper 8 bits/
 */
LONG FAR PASCAL
toolbarCommand (HWND hWnd, int iButton, HWND hwndToolbar)
{
    int iBtnPos, iState, iActivity, iString;


    // check repeat bit
    if (iButton & BTN_REPEAT) {
        return(0);
    }
    iButton &= 0xff;

    iBtnPos = toolbarIndexFromButton(hwndToolbar, iButton);
    iState = toolbarStateFromButton(hwndToolbar, iButton);
    iActivity = toolbarActivityFromButton(hwndToolbar, iButton);
    iString = toolbarStringFromIndex(hwndToolbar, iBtnPos);

    switch(iActivity) {

    case BTNACT_MOUSEDOWN:
    case BTNACT_KEYDOWN:
    case BTNACT_MOUSEMOVEON:
        statusUpdateStatus(ghWndStatus, MAKEINTRESOURCE(iString));
        break;

    case BTNACT_MOUSEMOVEOFF:
        statusUpdateStatus(ghWndStatus, NULL);
        break;

    case BTNACT_MOUSEUP:
    case BTNACT_KEYUP:

        statusUpdateStatus(ghWndStatus, NULL);
        switch(iButton) {
        case BTN_SETFILE:
            SendMessage(hWnd, WM_COMMAND,
                GET_WM_COMMAND_MPS(IDM_F_SETCAPTUREFILE, NULL, 0));
                break;

        case BTN_EDITCAP:
            // edit captured video
            SendMessage(hWnd, WM_COMMAND,
                GET_WM_COMMAND_MPS(IDM_F_EDITVIDEO, NULL, 0));
            break;

        case BTN_LIVE:
            SendMessage(hWnd,WM_COMMAND,
                GET_WM_COMMAND_MPS(IDM_O_PREVIEW, NULL, 0));
            break;

        case BTN_CAPFRAME:
            SendMessage(hWnd, WM_COMMAND,
                GET_WM_COMMAND_MPS(IDM_C_CAPTUREFRAME, NULL, 0));
            break;

        case BTN_CAPSEL:
            // capture selected frames
            SendMessage(hWnd, WM_COMMAND,
                GET_WM_COMMAND_MPS(IDM_C_CAPSEL, NULL, 0));
            break;

        case BTN_CAPAVI:
            SendMessage(hWnd,WM_COMMAND,
                GET_WM_COMMAND_MPS(IDM_C_CAPTUREVIDEO, NULL, 0));
            break;

        case BTN_CAPPAL:
            SendMessage(hWnd, WM_COMMAND,
                GET_WM_COMMAND_MPS(IDM_C_PALETTE, NULL, 0));
            break;

        case BTN_OVERLAY:
            SendMessage(hWnd, WM_COMMAND,
                GET_WM_COMMAND_MPS(IDM_O_OVERLAY, NULL, 0));
            break;
        }
        break;
    }
    return(0);
}


/*
 * Put up a dialog to allow the user to select a capture file.
 */
LONG FAR PASCAL
cmdSetCaptureFile(HWND hWnd)
{
    OPENFILENAME ofn ;
    LPTSTR p;
    TCHAR        achFileName[_MAX_PATH];
    TCHAR        achBuffer[_MAX_PATH] ;
    UINT         wError ;
    HANDLE hFilter;
    int oldhelpid;


    // Get current capture file name and
    // then try to get the new capture file name
    if (wError = capFileGetCaptureFile(ghWndCap, achFileName,
                                sizeof(achFileName))) {

        // Get just the path info
        // Terminate the full path at the last backslash
        _tcscpy (achBuffer, achFileName);
        for (p = achBuffer + _tcslen(achBuffer); p > achBuffer; p--) {
            if (*p == '\\') {
                *(p+1) = '\0';
                break;
            }
        }

        _fmemset(&ofn, 0, sizeof(OPENFILENAME)) ;
        ofn.lStructSize = sizeof(OPENFILENAME) ;
        ofn.hwndOwner = hWnd ;

        //load filters from resource stringtable
		if ( (hFilter = FindResource(ghInstApp, MAKEINTRESOURCE(ID_FILTER_AVI), RT_RCDATA))
		  && (hFilter = LoadResource(ghInstApp, hFilter)) )
		{
            ofn.lpstrFilter = LockResource(hFilter);
		}
		else
		{
            ofn.lpstrFilter = NULL;
		}

        ofn.nFilterIndex = 0 ;
        ofn.lpstrFile = achFileName ;
        ofn.nMaxFile = sizeof(achFileName) ;
        ofn.lpstrFileTitle = NULL;
        ofn.lpstrTitle = tmpString(IDS_TITLE_SETCAPTUREFILE);
        ofn.nMaxFileTitle = 0 ;
        ofn.lpstrInitialDir = achBuffer;
        ofn.Flags =
#ifdef BIDI
        OFN_BIDIDIALOG |
#endif
        OFN_HIDEREADONLY |
        OFN_NOREADONLYRETURN |
        OFN_PATHMUSTEXIST ;

        // set help context for dialog
        oldhelpid = SetCurrentHelpContext(IDA_SETCAPFILE);

        if (GetOpenFileName(&ofn)) {
            OFSTRUCT os;

            vidcapSetCaptureFile(achFileName);


            /*
             * if this is a new file, then invite the user to
             * allocate some space
             */
            if (OpenFile(achFileName, &os, OF_EXIST) == HFILE_ERROR) {

                /*
                 * show the allocate file space dialog to encourage
                 * the user to pre-allocate space
                 */
                if (DoDialog(hWnd, IDD_AllocCapFileSpace, AllocCapFileProc, 0)) {

		    // ensure repaint after dismissing dialog before
		    // possibly lengthy operation
		    UpdateWindow(ghWndMain);

                    // If user has hit OK then alloc requested capture file space
                    if (! capFileAlloc(ghWndCap, (long) gwCapFileSize * ONEMEG)) {
                        MessageBoxID(IDS_ERR_CANT_PREALLOC,
#ifdef BIDI
                                    MB_RTL_READING |
#endif
                                    MB_OK | MB_ICONEXCLAMATION) ;
                    }
                }
            }

        }

        // restore old help context
        SetCurrentHelpContext(oldhelpid);

        if (hFilter) {
            UnlockResource(hFilter);
        }

    }
    return(0);
}

/*
 * query the user for a filename, and then save the captured video
 * to that file
 */
LONG FAR PASCAL
cmdSaveVideoAs(HWND hWnd)
{
    OPENFILENAME ofn ;
    TCHAR        achFileName[_MAX_PATH];
    UINT         wError ;
    HANDLE       hFilter;
    int          oldhelpid;



    // Get the current capture file name and
    // then get the substitute file name to save video in
    if (wError = capFileGetCaptureFile(ghWndCap, achFileName, sizeof(achFileName))) {

        _fmemset(&ofn, 0, sizeof(OPENFILENAME)) ;
        ofn.lStructSize = sizeof(OPENFILENAME) ;
        ofn.hwndOwner = hWnd ;

        //load filters from resource stringtable
		if ( (hFilter = FindResource(ghInstApp, MAKEINTRESOURCE(ID_FILTER_AVI), RT_RCDATA))
		  && (hFilter = LoadResource(ghInstApp, hFilter)) )
		{
            ofn.lpstrFilter = LockResource(hFilter);
		}
		else
		{
            ofn.lpstrFilter = NULL;
		}

        ofn.nFilterIndex = 0 ;
        ofn.lpstrFile = achFileName ;
        ofn.nMaxFile = sizeof(achFileName) ;
        ofn.lpstrFileTitle = NULL ;
        ofn.lpstrTitle = tmpString(IDS_TITLE_SAVEAS);
        ofn.nMaxFileTitle = 0 ;
        ofn.lpstrInitialDir = NULL ;
        ofn.Flags =
#ifdef BIDI
        OFN_BIDIDIALOG |
#endif
        OFN_OVERWRITEPROMPT |  OFN_PATHMUSTEXIST ;


        // set help context
        oldhelpid = SetCurrentHelpContext(IDA_SAVECAPFILE);

        if (GetSaveFileName(&ofn)) {
            // If the user has hit OK then set save file name
            capFileSaveAs(ghWndCap, achFileName) ;
        }

        SetCurrentHelpContext(oldhelpid);

        if (hFilter) {
            UnlockResource(hFilter);
        }
    }
    return(0);
}


/*
 * Put up a dialog to allow the user to select a palette file and then
 * load that palette
 */
LONG FAR PASCAL
cmdLoadPalette(HWND hWnd)
{
    OPENFILENAME ofn ;
    TCHAR        achFileName[_MAX_PATH];
    HANDLE       hFilter;
    int          oldhelpid;



    achFileName[0] = 0;

    _fmemset(&ofn, 0, sizeof(OPENFILENAME));
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = hWnd;

    //load filters from resource stringtable
	if ( (hFilter = FindResource(ghInstApp, MAKEINTRESOURCE(ID_FILTER_PALETTE), RT_RCDATA))
	  && (hFilter = LoadResource(ghInstApp, hFilter)) )
	{
        ofn.lpstrFilter = LockResource(hFilter);
	}
	else
	{
        ofn.lpstrFilter = NULL;
	}

    ofn.nFilterIndex = 1;
    ofn.lpstrFile = achFileName;
    ofn.nMaxFile = sizeof(achFileName);
    ofn.lpstrFileTitle = NULL;
    ofn.lpstrTitle = tmpString(IDS_TITLE_LOADPALETTE);
    ofn.nMaxFileTitle = 0;
    ofn.lpstrInitialDir = NULL;
    ofn.Flags =
#ifdef BIDI
    OFN_BIDIDIALOG |
#endif
    OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;


    // set help context id
    oldhelpid = SetCurrentHelpContext(IDA_LOADPAL);

    if (GetOpenFileName(&ofn)) {
        // If the user has hit OK then load palette
        capPaletteOpen(ghWndCap, achFileName);
    }

    SetCurrentHelpContext(oldhelpid);

    if (hFilter) {
        UnlockResource(hFilter);
    }
    return(0);
}

/*
 * query the user for a filename, and then save the current palette
 * to that file
 */
LONG FAR PASCAL
cmdSavePalette(HWND hWnd)
{
    OPENFILENAME ofn ;
    TCHAR        achFileName[_MAX_PATH];
    HANDLE       hFilter;
    int          oldhelpid;


    achFileName[0] = 0;

    _fmemset(&ofn, 0, sizeof(OPENFILENAME)) ;
    ofn.lStructSize = sizeof(OPENFILENAME) ;
    ofn.hwndOwner = hWnd ;

    //load filters from resource stringtable
	if ( (hFilter = FindResource(ghInstApp, MAKEINTRESOURCE(ID_FILTER_PALETTE), RT_RCDATA))
	  && (hFilter = LoadResource(ghInstApp, hFilter)) )
	{
        ofn.lpstrFilter = LockResource(hFilter);
	}
	else
	{
        ofn.lpstrFilter = NULL;
	}

    ofn.nFilterIndex = 1;
    ofn.lpstrFile = achFileName;
    ofn.nMaxFile = sizeof(achFileName);
    ofn.lpstrFileTitle = NULL;
    ofn.lpstrTitle = tmpString(IDS_TITLE_SAVEPALETTE);
    ofn.nMaxFileTitle = 0;
    ofn.lpstrInitialDir = NULL;
    ofn.Flags =
#ifdef BIDI
    OFN_BIDIDIALOG |
#endif
    OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;

    // set help context for F1 key
    oldhelpid = SetCurrentHelpContext(IDA_SAVEPAL);

    if (GetSaveFileName(&ofn)) {
        // If the user has hit OK then set save file name
        capPaletteSave(ghWndCap, achFileName);
    }

    SetCurrentHelpContext(oldhelpid);

    if (hFilter) {
        UnlockResource(hFilter);
    }

    return(0);
}


/*
 * query the user for a filename, and then save the current frame
 * to that file
 */
LONG FAR PASCAL
cmdSaveDIB(HWND hWnd)
{
    OPENFILENAME ofn ;
    TCHAR        achFileName[_MAX_PATH];
    HANDLE       hFilter;
    int          oldhelpid;


    achFileName[0] = 0;

    _fmemset(&ofn, 0, sizeof(OPENFILENAME)) ;
    ofn.lStructSize = sizeof(OPENFILENAME) ;
    ofn.hwndOwner = hWnd ;

    //load filters from resource stringtable
	if ( (hFilter = FindResource(ghInstApp, MAKEINTRESOURCE(ID_FILTER_DIB), RT_RCDATA) )
	  && (hFilter = LoadResource(ghInstApp, hFilter) ) )
	{
        ofn.lpstrFilter = LockResource(hFilter);
    }
	else
	{
		ofn.lpstrFilter = NULL;
	}

    ofn.nFilterIndex = 1;
    ofn.lpstrFile = achFileName;
    ofn.nMaxFile = sizeof(achFileName);
    ofn.lpstrFileTitle = NULL;
    ofn.lpstrTitle = tmpString(IDS_TITLE_SAVEDIB);
    ofn.nMaxFileTitle = 0;
    ofn.lpstrInitialDir = NULL;
    ofn.Flags =
#ifdef BIDI
    OFN_BIDIDIALOG |
#endif
    OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;

    // set help context for F1 handling
    oldhelpid = SetCurrentHelpContext(IDA_SAVEDIB);

    if (GetSaveFileName(&ofn)) {

        // If the user has hit OK then set save file name
        capFileSaveDIB(ghWndCap, achFileName);
    }

    SetCurrentHelpContext(oldhelpid);

    if (hFilter) {
        UnlockResource(hFilter);
    }

    return(0);
}

//
// MenuProc: Processes All Menu-based Operations
//
LRESULT FAR PASCAL MenuProc(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
////////////////////////////////////////////////////////////////////////
//  hWnd:      Application main window handle
//  hMenu:     Application menu handle
//  wParam:    Menu option
//  lParam:    Additional info for any menu option
////////////////////////////////////////////////////////////////////////

    BOOL         fResult ;
    DWORD        dwSize = 0;
    int          oldhelpid;

    HMENU hMenu = GetMenu(hWnd) ;

    switch (GET_WM_COMMAND_ID(wParam, lParam)) {

	case IDC_TOOLBAR:
            return toolbarCommand(hWnd, GET_WM_COMMAND_CMD(wParam, lParam), ghWndToolBar);

/* --- file --- */
        case IDM_F_SETCAPTUREFILE:
            return cmdSetCaptureFile(hWnd);

        case IDM_F_SAVEVIDEOAS:
            return cmdSaveVideoAs(hWnd);
            break;

        case IDM_F_ALLOCATESPACE:
            if (DoDialog(hWnd, IDD_AllocCapFileSpace, AllocCapFileProc, 0)) {

		// ensure repaint after dismissing dialog before
		// possibly lengthy operation
		UpdateWindow(ghWndMain);


                // If user has hit OK then alloc requested capture file space
                if (! capFileAlloc(ghWndCap, (long) gwCapFileSize * ONEMEG)) {
                    MessageBoxID(IDS_ERR_CANT_PREALLOC,
#ifdef BIDI
                                MB_RTL_READING |
#endif
                                MB_OK | MB_ICONEXCLAMATION) ;
                }
            }
            break ;

        case IDM_F_EXIT:
            DestroyWindow(hWnd) ;
            break;

        case IDM_F_LOADPALETTE:
            return cmdLoadPalette(hWnd);

        case IDM_F_SAVEPALETTE:
            return cmdSavePalette(hWnd);

        case IDM_F_SAVEFRAME:
            return cmdSaveDIB(hWnd);

        case IDM_F_EDITVIDEO:
        {
            HINSTANCE  u;
            BOOL	f = TRUE;	/* assume the best */
            HCURSOR     hOldCursor;

            /* build up the command line "AviEdit -n filename" */
            if (lstrlen(gachCaptureFile) > 0) {

                hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
                u = ShellExecute (hWnd, TEXT("open"), gachCaptureFile, NULL, NULL, SW_SHOWNORMAL);
                if ((UINT_PTR) u < 32){
            	/* report error on forking VidEdit */
                    MessageBoxID(IDS_ERR_VIDEDIT, MB_OK|MB_ICONEXCLAMATION);
            	f = FALSE;
                }

                SetCursor(hOldCursor);
            }
            return f;

        }


/* --- edit --- */

        case IDM_E_COPY:
            capEditCopy(ghWndCap) ;
            break;

        case IDM_E_PASTEPALETTE:
            capPalettePaste(ghWndCap) ;
            break;

        case IDM_E_PREFS:
            {
                if (DoDialog(hWnd, IDD_Prefs, PrefsDlgProc, 0)) {

                        // write prefs to profile

                        // force new brush
                        vidframeSetBrush(ghWndFrame, gBackColour);

                        // re-do layout
                        vidcapLayout(hWnd);

                }
            }
            break;

/* --- options --- */

        case IDM_O_PREVIEW:
            // Toggle Preview
    	    capGetStatus(ghWndCap, &gCapStatus, sizeof(CAPSTATUS)) ;
            vidcapSetLive(!gCapStatus.fLiveWindow) ;
            break;

        case IDM_O_OVERLAY:
            // Toggle Overlay
    	    capGetStatus(ghWndCap, &gCapStatus, sizeof(CAPSTATUS)) ;
            vidcapSetOverlay(!gCapStatus.fOverlayWindow);
            break ;

        case IDM_O_AUDIOFORMAT:
#ifdef  USE_ACM
            {
                ACMFORMATCHOOSE cfmt;
                static BOOL fDialogUp = FALSE;

                if (fDialogUp)
                    return FALSE;

                fDialogUp = TRUE;
                // Ask the ACM what the largest wave format is.....
                acmMetrics(NULL,
                            ACM_METRIC_MAX_SIZE_FORMAT,
                            &dwSize);

                // Get the current audio format
                dwSize = max (dwSize, capGetAudioFormatSize (ghWndCap));
                if (glpwfex = (LPWAVEFORMATEX) GlobalAllocPtr(GHND, dwSize)) {
                    capGetAudioFormat(ghWndCap, glpwfex, (WORD)dwSize) ;

		    _fmemset (&cfmt, 0, sizeof (ACMFORMATCHOOSE));
		    cfmt.cbStruct = sizeof (ACMFORMATCHOOSE);
		    cfmt.fdwStyle =  ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT;
		    cfmt.fdwEnum =   ACM_FORMATENUMF_HARDWARE |
				     ACM_FORMATENUMF_INPUT;
		    cfmt.hwndOwner = hWnd;
		    cfmt.pwfx =     glpwfex;
		    cfmt.cbwfx =    dwSize;

		    //oldhelpid = SetCurrentHelpContext(IDA_AUDIOSETUP);
		    if (!acmFormatChoose(&cfmt)) {
			capSetAudioFormat(ghWndCap, glpwfex, (WORD)glpwfex->cbSize +
					  sizeof (WAVEFORMATEX)) ;
			mmWriteProfileBinary(gachAppTitle, "WaveFormatBinary",
					     (LPVOID) glpwfex, glpwfex->cbSize +
					     sizeof (WAVEFORMATEX));
		    }
		    //SetCurrentHelpContext(oldhelpid);

		    GlobalFreePtr(glpwfex) ;
		}
                fDialogUp = FALSE;
            }
#else
            {
                // Get current audio format and then find required format
                dwSize = capGetAudioFormatSize (ghWndCap);  
                glpwfex = (LPWAVEFORMATEX) GlobalAllocPtr(GHND, dwSize) ;
                capGetAudioFormat(ghWndCap, glpwfex, (WORD)dwSize) ;

                if (DoDialog(hWnd, IDD_AudioFormat, AudioFormatProc, 0)) {
                        // If the user has hit OK, set the new audio format
                        capSetAudioFormat(ghWndCap, glpwfex, (WORD)dwSize) ;
			mmWriteProfileBinary(gachAppTitle, "WaveFormatBinary",
					 (LPVOID) glpwfex, dwSize);
                }
                GlobalFreePtr(glpwfex) ;
            }
#endif
            break ;

        case IDM_O_VIDEOFORMAT:
            if (gCapDriverCaps.fHasDlgVideoFormat) {
                // Only if the driver has a "Video Format" dialog box
                oldhelpid = SetCurrentHelpContext(IDA_VIDFORMAT);
                if (capDlgVideoFormat(ghWndCap)) {  // If successful,
                    // Get the new image dimension and center capture window
                    capGetStatus(ghWndCap, &gCapStatus, sizeof(CAPSTATUS)) ;
                    vidcapLayout(hWnd);
                }
                SetCurrentHelpContext(oldhelpid);
            }
            break;

        case IDM_O_VIDEOSOURCE:
            if (gCapDriverCaps.fHasDlgVideoSource) {
                // Only if the driver has a "Video Source" dialog box
                oldhelpid = SetCurrentHelpContext(IDA_VIDSOURCE);
                capDlgVideoSource(ghWndCap) ;
                capGetStatus(ghWndCap, &gCapStatus, sizeof(CAPSTATUS)) ;
                vidcapLayout(hWnd);
                SetCurrentHelpContext(oldhelpid);
            }
            break ;

        case IDM_O_VIDEODISPLAY:
            if (gCapDriverCaps.fHasDlgVideoDisplay) {
                // Only if the driver has a "Video Display" dialog box
                oldhelpid = SetCurrentHelpContext(IDA_VIDDISPLAY);
                capDlgVideoDisplay(ghWndCap) ;
                capGetStatus(ghWndCap, &gCapStatus, sizeof(CAPSTATUS)) ;
                SetCurrentHelpContext(oldhelpid);
            }
            break ;

        case IDM_O_CHOOSECOMPRESSOR:
            oldhelpid = SetCurrentHelpContext(IDA_COMPRESSION);
            capDlgVideoCompression(ghWndCap);
            SetCurrentHelpContext(oldhelpid);
            break;

        // Select a driver to activate
        case IDM_O_DRIVER0:
        case IDM_O_DRIVER1:
        case IDM_O_DRIVER2:
        case IDM_O_DRIVER3:
        case IDM_O_DRIVER4:
        case IDM_O_DRIVER5:
        case IDM_O_DRIVER6:
        case IDM_O_DRIVER7:
        case IDM_O_DRIVER8:
        case IDM_O_DRIVER9:
            vidcapInitHardware(ghWndMain, ghWndCap, (UINT) (wParam - IDM_O_DRIVER0));
            break;

/* --- capture --- */


        case IDM_C_PALETTE:
            if (DoDialog(hWnd, IDD_MakePalette, MakePaletteProc, 0)) {
                // Palette is created within the dialog
                bDefaultPalette = FALSE;
            }
            break;

        case IDM_C_CAPTUREVIDEO:

            // warn user if he is still using the default palette
            if (bDefaultPalette) {

		LPBITMAPINFOHEADER lpbi;
		int sz;

		// fUsingDefaultPalette will be TRUE even if the
		// current capture format is non-palettised. This is a
		// bizarre decision of Jay's.

		sz = (int)capGetVideoFormatSize(ghWndCap);
		lpbi = (LPBITMAPINFOHEADER)LocalAlloc(LPTR, sz);

		if (lpbi) {    // We can warn s/he
		    if (capGetVideoFormat(ghWndCap, lpbi, sz) &&
			(lpbi->biCompression == BI_RGB) &&
			(lpbi->biBitCount <= 8)) {

			CAPSTATUS cs;

			// if we've warned him once, we can forget it
			bDefaultPalette = FALSE;

			capGetStatus(ghWndCap, &cs, sizeof(cs));

			if (cs.fUsingDefaultPalette) {

			    if (MessageBoxID(IDS_WARN_DEFAULT_PALETTE,
       				     MB_OKCANCEL| MB_ICONEXCLAMATION)== IDCANCEL) {
				break;
			    }
			}
		    }
		    LocalFree(lpbi);
		}
            }

            // Invoke a Dlg box to setup all the params
            if (DoDialog(hWnd, IDD_CapSetUp, CapSetUpProc, 0)) {

                // set the defaults we won't bother the user with
                gCapParms.fMakeUserHitOKToCapture = !gCapParms.fMCIControl;
                gCapParms.wPercentDropForError = 10;

                // fUsingDOSMemory is obsolete, but we use it here as
                // a flag which is TRUE if "CapturingToDisk"
                // The number of video buffers should be enough to get through
                // disk seeks and thermal recalibrations if "CapturingToDisk"
                // If "CapturingToMemory", get as many buffers as we can.

                gCapParms.wNumVideoRequested = 
                        gCapParms.fUsingDOSMemory ? 32 : 1000;

                // Don't abort on the left mouse anymore!
                gCapParms.fAbortLeftMouse = FALSE;
                gCapParms.fAbortRightMouse = TRUE;

                // If the Driver is Scrncap.drv, the following values are special

                // If wChunkGranularity is zero, the granularity will be set to the
                // disk sector size.
                gCapParms.wChunkGranularity = (gbIsScrncap ? 32 : 0);

                // Scrncap requires a callback for the message pump
                capSetCallbackOnYield(ghWndCap, 
                        (gbIsScrncap ? fpYieldCallback : NULL));

                // If the user has hit OK, set the new setup info
                capCaptureSetSetup(ghWndCap, &gCapParms, sizeof(CAPTUREPARMS)) ;
            } else {
                break;
            }

            // if no capture file, get that
            if (lstrlen(gachCaptureFile) <= 0) {
                cmdSetCaptureFile(hWnd);
                if (lstrlen(gachCaptureFile) <= 0) {
                    break;
                }
            }

            // Capture video sequence
            fResult = capCaptureSequence(ghWndCap) ;
            break;

        case IDM_C_CAPTUREFRAME:
            // Turn off overlay / preview (gets turned off by frame capture)
            vidcapSetLive(FALSE);
            vidcapSetOverlay(FALSE);

            // Grab a frame
            fResult = capGrabFrameNoStop(ghWndCap) ;
            break;


        case IDM_C_CAPSEL:
            {
                FARPROC fproc;

                // if no capture file, get that
                if (lstrlen(gachCaptureFile) <= 0) {
                    cmdSetCaptureFile(hWnd);
                    if (lstrlen(gachCaptureFile) <= 0) {
                        break;
                    }
                }

                fproc = MakeProcInstance(CapFramesProc, ghInstApp);
                DialogBox(ghInstApp, MAKEINTRESOURCE(IDD_CAPFRAMES), hWnd, (DLGPROC) fproc);
                FreeProcInstance(fproc);
            }
            break;

#ifdef DEBUG
        case IDM_C_TEST:
	    nTestCount = 0;
	    // Intentional fall through
	    
        case IDM_C_TESTAGAIN:
            // set the defaults we won't bother the user with
            gCapParms.fMakeUserHitOKToCapture = FALSE;
            gCapParms.wPercentDropForError = 100;

            gCapParms.wNumVideoRequested = 
                    gCapParms.fUsingDOSMemory ? 32 : 1000;

            // Don't abort on the left mouse anymore!
            gCapParms.fAbortLeftMouse = FALSE;
            gCapParms.fAbortRightMouse = TRUE;

            // If wChunkGranularity is zero, the granularity will be set to the
            // disk sector size.
            gCapParms.wChunkGranularity = (gbIsScrncap ? 32 : 0);

            // If the user has hit OK, set the new setup info
            capCaptureSetSetup(ghWndCap, &gCapParms, sizeof(CAPTUREPARMS)) ;

            // if no capture file, get that
            if (lstrlen(gachCaptureFile) <= 0) {
                cmdSetCaptureFile(hWnd);
                if (lstrlen(gachCaptureFile) <= 0) {
                    break;
                }
            }
	    
	    {
		TCHAR buf[80];

      gCapParms.wNumVideoRequested = 10;
      gCapParms.wNumAudioRequested = 5;
		gCapParms.fLimitEnabled = TRUE;
		if (gCapParms.wTimeLimit == 0)
		    gCapParms.wTimeLimit = 5;
		capCaptureSetSetup(ghWndCap, &gCapParms, sizeof(CAPTUREPARMS)) ;
		
		// Capture video sequence
      fResult = capCaptureSequence(ghWndCap) ;
		
		wsprintf (buf, "TestCount = %d", nTestCount++);
		statusUpdateStatus(ghWndStatus, buf);
		
		// Hold down the right mouse button to abort
		if (!GetAsyncKeyState(VK_RBUTTON) & 0x0001)
		    PostMessage (hWnd, WM_COMMAND, IDM_C_TESTAGAIN, 0L);
            }
            break;
#endif
	    
/* --- help --- */
        case IDM_H_CONTENTS:
            HelpContents();
            break;

        case IDM_H_ABOUT:
            ShellAbout(
                hWnd,
                "VidCap",
                "Video Capture Tool",
                LoadIcon(ghInstApp,  gachIconName)
            );
            //DoDialog(hWnd, IDD_HelpAboutBox, AboutProc, 0);
            break ;


    }

    return 0L ;
}

/* --- menu help and enable/disable handling ------------------------ */

// write or clear status line help text when the user brings up or cancels a
// menu. This depends on there being strings in the string table with
// the same ID as the corresponding menu item.
// Help text for the items along the menu bar (File, Edit etc) depends
// on IDM_FILE, IDM_EDIT being defined with values 100 apart in the same
// order as their index in the menu
void
MenuSelect(HWND hwnd, UINT cmd, UINT flags, HMENU hmenu)
{
    if ((LOWORD(flags) == 0xffff) && (hmenu == NULL)) {
        //menu closing - remove message
        statusUpdateStatus(ghWndStatus, NULL);
    } else if ( (flags & (MF_SYSMENU|MF_POPUP)) == (MF_SYSMENU|MF_POPUP)) {
        // the system menu itself
        statusUpdateStatus(ghWndStatus, MAKEINTRESOURCE(IDM_SYSMENU));
    } else if ((flags & MF_POPUP) == 0) {
        // a menu command item
        statusUpdateStatus(ghWndStatus, MAKEINTRESOURCE(cmd));
    } else {
        //a popup menu - we need to search to find which one.
        // note that the cmd item in Win16 will now have a
        // menu handle, whereas in Win32 it has an index.
        // NOTE: this code assumes that the menu items
        // are #defined 100 apart in the same order, starting
        // with IDM_FILE
#ifdef _WIN32
        statusUpdateStatus(ghWndStatus, MAKEINTRESOURCE(IDM_FILE + (cmd * 100)));
#else
        int i,c;
        HMENU hmenuMain; 

        hmenuMain = GetMenu(hWnd);
        c = GetMenuItemCount(hmenuMain);

        for(i = 0; i < c; i++) {
            if (hmenu == GetSubMenu(hmenuMain, i)) {
                statusUpdateStatus(MAKEINTRESOURCE(IDM_FILE + (cmd*100)));
                return(0);
            }
        }
        statusUpdateStatus(NULL);
#endif
    }
}

// a popup menu is being selected - enable or disable menu items
int
InitMenuPopup(
    HWND hwnd,
    HMENU hmenu,
    int index
)
{
    int i = MF_ENABLED;
    CAPSTATUS cs;
    BOOL bUsesPalettes;

    capGetStatus(ghWndCap, &cs, sizeof(cs));

    // try to see if the driver uses palettes
    if ((cs.hPalCurrent != NULL) || (cs.fUsingDefaultPalette)) {
        bUsesPalettes = TRUE;
    } else {
        bUsesPalettes = FALSE;
    }


    switch(index) {
    case 0:         // IDM_FILE

        if (lstrlen(gachCaptureFile) <= 0) {
            i = MF_GRAYED;
        }
        // save as enabled only if we have a capture file
        EnableMenuItem(hmenu, IDM_F_SAVEVIDEOAS, i);
        // edit video possible only if we have a capture file AND we've
        // captured something
        EnableMenuItem(hmenu, IDM_F_EDITVIDEO,
            (cs.dwCurrentVideoFrame > 0) ? i : MF_GRAYED);

        // allow save palette if there is one
        EnableMenuItem(hmenu, IDM_F_SAVEPALETTE,
            (cs.hPalCurrent != NULL) ? MF_ENABLED:MF_GRAYED);

        // allow load palette if the driver uses palettes
        EnableMenuItem(hmenu, IDM_F_LOADPALETTE,
            bUsesPalettes ? MF_ENABLED : MF_GRAYED);

        break;

    case 1:         // IDM_EDIT

        // paste palettes if driver uses them and there is one pastable
        EnableMenuItem(hmenu, IDM_E_PASTEPALETTE,
            (bUsesPalettes && IsClipboardFormatAvailable(CF_PALETTE)) ? MF_ENABLED:MF_GRAYED);

        break;

    case 2:         // IDM_OPTIONS

        EnableMenuItem(hmenu, IDM_O_AUDIOFORMAT,
            cs.fAudioHardware ? MF_ENABLED : MF_GRAYED);

        EnableMenuItem(hmenu, IDM_O_OVERLAY,
            gCapDriverCaps.fHasOverlay ? MF_ENABLED:MF_GRAYED);

        EnableMenuItem(hmenu, IDM_O_VIDEOFORMAT,
            gCapDriverCaps.fHasDlgVideoFormat ? MF_ENABLED:MF_GRAYED);

        EnableMenuItem(hmenu, IDM_O_VIDEODISPLAY,
            gCapDriverCaps.fHasDlgVideoDisplay ? MF_ENABLED:MF_GRAYED);

        EnableMenuItem(hmenu, IDM_O_VIDEOSOURCE,
            gCapDriverCaps.fHasDlgVideoSource ? MF_ENABLED:MF_GRAYED);

        EnableMenuItem(hmenu, IDM_O_PREVIEW,
                gbHaveHardware ? MF_ENABLED:MF_GRAYED);


    case 3:     // IDM_CAPTURE
        if (!gbHaveHardware) {
            i = MF_GRAYED;
        }
        EnableMenuItem(hmenu, IDM_C_CAPSEL, i);
        EnableMenuItem(hmenu, IDM_C_CAPTUREFRAME, i);
        EnableMenuItem(hmenu, IDM_C_CAPTUREVIDEO, i);
        EnableMenuItem(hmenu, IDM_C_PALETTE, (gbHaveHardware &&
            gCapDriverCaps.fDriverSuppliesPalettes) ? MF_ENABLED : MF_GRAYED);

        break;
    }
    return(0);
}




//
// MainWndProc: Application Main Window Procedure
//
LRESULT FAR PASCAL MainWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
////////////////////////////////////////////////////////////////////////
//  hWnd:      Application main window handle
//  Message:   Next message to be processed
//  wParam:    WORD param for the message
//  lParam:    LONG param for the message
////////////////////////////////////////////////////////////////////////

    switch (Message) {

        static BOOL fMinimized;

        case WM_SYSCOMMAND:
	    if ((wParam & 0xfff0) == SC_MAXIMIZE)
	    	fMinimized = FALSE;
	    else if ((wParam & 0xfff0) == SC_RESTORE)
	    	fMinimized = FALSE;
	    else if ((wParam & 0xfff0) == SC_MINIMIZE)
	    	fMinimized = TRUE;	
	    return DefWindowProc(hWnd, Message, wParam, lParam);			
	    break;

        case WM_COMMAND:
            MenuProc(hWnd, wParam, lParam) ;
            break ;

        case WM_CREATE:
            HelpInit(ghInstApp, "vidcap.hlp", hWnd);
            break;

        case WM_NCHITTEST:
        {
            LRESULT dw;

            dw = DefWindowProc(hWnd, Message, wParam, lParam);
            // Don't allow border resize if autosizing
            if (gbAutoSizeFrame) {
                if (dw >= HTSIZEFIRST && dw <= HTSIZELAST)
                    dw = HTCAPTION;
            }
            return dw;
                
        }
            break;

        case WM_GETMINMAXINFO:
            // Don't allow manual sizing if window locked to the capture size
            if (gbHaveHardware && gbAutoSizeFrame && !gbInLayout) {
                RECT rW;

                LPMINMAXINFO lpMMI = (LPMINMAXINFO) lParam;

                GetWindowRect (hWnd, &rW);
                lpMMI->ptMinTrackSize.x = rW.right - rW.left;
                lpMMI->ptMinTrackSize.y = rW.bottom - rW.top;
                lpMMI->ptMaxTrackSize = lpMMI->ptMinTrackSize;
            }
            break;

        case WM_MOVE:
	    if (!fMinimized) {
	    	vidcapLayout (hWnd);
	    }
	    break;

        case WM_SIZE:
	    if (!fMinimized) {
	    	vidcapLayout (hWnd);
	    }
	    break;

        case WM_MENUSELECT:
            {
                UINT cmd = GET_WM_MENUSELECT_CMD(wParam, lParam);
                UINT flags = GET_WM_MENUSELECT_FLAGS(wParam, lParam);
                HMENU hmenu = GET_WM_MENUSELECT_HMENU(wParam, lParam);

                MenuSelect(hWnd, cmd, flags, hmenu);
            }
            break;

        case WM_INITMENUPOPUP:
            {
                BOOL bSystem = (BOOL) HIWORD(lParam);

                if (!bSystem) {
                    return InitMenuPopup(hWnd,
                            (HMENU) wParam, (int) LOWORD(lParam));
                } else {
                    return(DefWindowProc(hWnd, Message, wParam, lParam));
                }
            }


        case WM_SYSCOLORCHANGE:
            // we don't use this ourselves, but we should pass
            // it on to all three children
            SendMessage(ghWndFrame, Message, wParam, lParam);
            SendMessage(ghWndToolBar, Message, wParam, lParam);
            SendMessage(ghWndStatus, Message, wParam, lParam);
            return (TRUE);


        case WM_PALETTECHANGED:
        case WM_QUERYNEWPALETTE:
            // Pass the buck to Capture window proc
            return SendMessage(ghWndCap, Message, wParam, lParam) ;
            break ;

        case WM_SETFOCUS:
            // the toolbar is the only part that needs the focus
            SetFocus(ghWndToolBar);
            break;


        case WM_ACTIVATEAPP:
            if (wParam && ghWndCap) 
                capPreviewRate(ghWndCap, 15); // Fast preview when active
            else
                capPreviewRate(ghWndCap, 1000); // Slow preview when inactive
            break;

        case WM_NEXTDLGCTL:
            // if anyone is tabbing about, move the focus to the
            // toolbar
            SetFocus(ghWndToolBar);

            // select the correct button to handle moving off one
            // end and back on the other end
            if (lParam == FALSE) {
                // are we moving forwards or backwards ?
                if (wParam == 0) {
                    // move to next - so select first button
                    toolbarSetFocus(ghWndToolBar, TB_FIRST);
                } else {
                    // move to previous - so select last
                    toolbarSetFocus(ghWndToolBar, TB_LAST);
                }
            }
            break;

        case WM_PAINT:
        {
            HDC           hDC ;
            PAINTSTRUCT   ps ;

            hDC = BeginPaint(hWnd, &ps) ;

            // Included in case the background is not a pure color
            SetBkMode(hDC, TRANSPARENT) ;

            EndPaint(hWnd, &ps) ;
            break ;
        }

        case WM_CLOSE:
            // Disable and free all the callbacks 
            capSetCallbackOnError(ghWndCap, NULL) ;
			if (fpErrorCallback) {
            	FreeProcInstance(fpErrorCallback) ;
				fpErrorCallback = NULL;
			}

            capSetCallbackOnStatus(ghWndCap, NULL) ;
			if (fpStatusCallback) {
            	FreeProcInstance(fpStatusCallback) ;
				fpStatusCallback = NULL;
			}

            capSetCallbackOnYield(ghWndCap, NULL) ;
			if (fpYieldCallback) {
            	FreeProcInstance(fpYieldCallback) ;
				fpYieldCallback = NULL;
			}

            // Disconnect the current capture driver
            capDriverDisconnect (ghWndCap);

            // Destroy child windows, modeless dialogs, then this window...
            // DestroyWindow(ghWndCap) ;
            DestroyWindow(hWnd) ;
            break ;

        case WM_DESTROY:
            {
                // remember window size and position
                // - this will be written to the profile
                WINDOWPLACEMENT wp;

                wp.length = sizeof (WINDOWPLACEMENT);
                GetWindowPlacement(hWnd, &wp);

                gWinShow = wp.showCmd;
                gWinX = wp.rcNormalPosition.left;
                gWinY = wp.rcNormalPosition.top;
                gWinCX = RECTWIDTH(wp.rcNormalPosition);
                gWinCY = RECTHEIGHT(wp.rcNormalPosition);

                // write defaults out to the registry
                vidcapWriteProfile();
                vidcapWriteSettingsProfile();

                HelpShutdown();

            }

            PostQuitMessage(0) ;
            break ;

        default:
            return DefWindowProc(hWnd, Message, wParam, lParam) ;
    }

    return 0L;
}   // End of MainWndProc
