/*
**  Monitoring implementation
**  © 2002 Kakace Productions
**
*/


#include "Monitor.h"

#ifndef  VLAB_CONTROL_H
#include "VLab_Control.h"
#endif

#ifndef  VLAB_CONFIG_H
#include "VLab_config.h"
#endif


#include <intuition/IntuitionBase.h>

#include <proto/exec.h>
#include <proto/timer.h>
#include <proto/intuition.h>
#include <proto/picasso96.h>
#include <proto/cgxvideo.h>
#include <proto/cybergraphics.h>


/*------------------------------------------------------------------------------------------------*/

/*
** The pointers below may be NULL, thus disabling the monitoring
** feature.
*/

struct Device        *TimerBase;
struct Library       *P96Base;
struct Library       *CGXVideoBase;
struct Library       *CyberGfxBase;
struct IntuitionBase *IntuitionBase;


static struct VLabMonitor Monitoring;       /* Monitoring infos */
static struct IORequest   *ioReq;


struct Resolutions {
    UBYTE XPos;
    UBYTE YPos;
    UWORD Width;
    UWORD Height;
    UWORD Ratio;                            /* Image ratio compared to LoRes non-lace dimensions */
};


/*
** NOTE : Because of a bug in Picasso96 v2.x, the PiP feature doesn't
**      work as expected when the image width is greater than 320 pixels.
*/

static const struct Resolutions resolutions[MONITOR_SIZES * 2] = {
    {10, 19, 160, 120, 2},          /* 0 : PAL (small)    */
    {60, 58, 240, 200, 1},          /* 1 : PAL (medium)   */
    {20, 38, 320, 240, 1},          /* 2 : PAL (high)     */
    {10, 24, 160, 100, 2},          /* 4 : NTSC (small)   */
    {60, 48, 240, 170, 1},          /* 5 : NTSC (medium)  */
    {20, 33, 320, 200, 1},          /* 6 : NTSC (high)    */
};


struct fps_val {
    ULONG quot;                 /* How many images per second    */
    ULONG fracDigit;            /* Fractional digit (ASCII byte) */
};


/*------------------------------------------------------------------------------------------------*/
/*====================================== PRIVATE FUNCTIONS =======================================*/
/*------------------------------------------------------------------------------------------------*/

void ComputeFPS(struct EClockVal *t1, struct EClockVal *t2, struct fps_val *fps, ULONG ticspersec);


/*
** AmigaOS implementation (requires CyberGraphX/Picasso96 RTG)
*/

static struct VLabMonitor *OS_OpenMonitor(struct Screen *ae_screen, ULONG left, ULONG top, ULONG width,
                                          ULONG height, BOOL color);
static struct VLabMonitor *OS_ResizeMonitor(struct VLabMonitor *monitor, ULONG width, ULONG height);
static ULONG               OS_UpdateMonitor(struct VLabMonitor *monitor);
static void                OS_CloseMonitor(struct VLabMonitor *monitor);

static struct Window *open_window(struct Screen *screen, const char *window_name, ULONG left, ULONG top,
                                  ULONG width, ULONG height, BOOL sizeable);

/*
** CyberGraphX/Video-Layer implementation.
*/

static struct VLabMonitor *CGX_OpenPiPMonitor(struct Screen *screen, ULONG left, ULONG top, ULONG width,
                                              ULONG height, BOOL color);
static struct VLabMonitor *CGX_ResizePiPMonitor(struct VLabMonitor *monitor, ULONG width, ULONG height);
static ULONG               CGX_UpdatePiPMonitor(struct VLabMonitor *monitor);
static void                CGX_ClosePiPMonitor(struct VLabMonitor *monitor);


/*
** Picasso96/PiP implementation.
*/

static struct VLabMonitor *P96_OpenPiPMonitor(struct Screen *screen, ULONG left, ULONG top, ULONG width,
                                              ULONG height, BOOL color);
static struct VLabMonitor *P96_ResizePiPMonitor(struct VLabMonitor *monitor, ULONG width, ULONG height);
static ULONG               P96_UpdatePiPMonitor(struct VLabMonitor *monitor);
static void                P96_ClosePiPMonitor(struct VLabMonitor *monitor);


/*------------------------------------------------------------------------------------------------*/

/****** Monitor/InitMonitoring **********************************************
*
*   NAME
*   InitMonitoring -- Initialize the monitoring feature.
*
*   SYNOPSIS
*   InitMonitoring()
*
*   void InitMonitoring(void);
*
*   FUNCTION
*   Open all shared libraries that might be necessary to monitor the
*   selected VLab input.
*
*   SEE ALSO
*   CleanupMonitoring()
*
*****************************************************************************
*
*/


void InitMonitoring(void)
{
    struct IORequest *ior;

    /*
    ** Open the timer.device.
    */

    if ( (ior = (struct IORequest *) AllocVec(sizeof(struct timerequest), MEMF_PUBLIC|MEMF_CLEAR)) ) {
        if ( !OpenDevice("timer.device", UNIT_MICROHZ, ior, 0UL)) {
            TimerBase = ior->io_Device;
            ioReq = ior;
        }
        else
            FreeVec(ior);
    }

    /*
    ** Open the libraries for the monitoring feature. Their availability will
    ** be checked later, and the monitoring will be disabled if a vital one
    ** is missing.
    */

    IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 39);

    P96Base      = OpenLibrary("Picasso96API.library", 2);
    CGXVideoBase = OpenLibrary("cgxvideo.library", 41);
    CyberGfxBase = OpenLibrary("cybergraphics.library", 41);
}


/****** Monitor/CleanupMonitoring *******************************************
*
*   NAME
*   CleanupMonitoring -- Release monitoring resources.
*
*   SYNOPSIS
*   CleanupMonitoring()
*
*   void CleanupMonitoring(void);
*
*   FUNCTION
*   Close shared libraries opened by InitMonitoring().
*
*   SEE ALSO
*   InitMonitoring().
*
*****************************************************************************
*
*/


void CleanupMonitoring(void)
{
    if (P96Base)
        CloseLibrary(P96Base);

    if (CGXVideoBase)
        CloseLibrary(CGXVideoBase);

    if (CyberGfxBase)
        CloseLibrary(CyberGfxBase);

    if (IntuitionBase)
        CloseLibrary((struct Library *) IntuitionBase);

    if (ioReq) {
        CloseDevice(ioReq);
        FreeVec(ioReq);
    }
}


/****i* Monitor/SetClipping *************************************************
*
*   NAME
*   SetClipping -- Initialize clipping informations;
*
*   SYNOPSIS
*   SetClipping(monitor, resolution)
*
*   void SetClipping(struct VLabMonitor *, ULONG);
*
*   FUNCTION
*   Initialize clipping informations for the selected resolution.
*
*   INPUTS
*   monitor    - Pointer to a monitoring object.
*   resolution - Resolution ID.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


static void SetClipping(struct VLabMonitor *monitor, ULONG resolution)
{
    const struct Resolutions *R = &resolutions[resolution];

    monitor->xpos   = R->XPos;
    monitor->ypos   = R->YPos;
    monitor->width  = R->Width;
    monitor->height = R->Height;
    monitor->ratio  = R->Ratio;
}


/****** Monitor/IsMonitoringAvailable ***************************************
*
*   NAME
*   IsMonitoringAvailable -- Verify monitoring capability
*
*   SYNOPSIS
*   available = CheckMonitor(screenName)
*
*   int IsMonitoringAvailable(const char *);
*
*   FUNCTION
*   Check whether the monitoring can be performed. The limiting factor here
*   is the pixel format of ArtEffect's screen.
*   In Hi-Color and True Color modes, the monitoring is always available. On
*   a 8-bit chunky (CLUT) screens, only PiP monitoring is possible.
*
*   INPUTS
*   screenName - Public screen's name.
*
*   RESULT
*   available - TRUE if the monitoring can be done on the specified screen.
*
*   NOTES
*   PiP windows always use the YUV422 (CCIR-601 specs) pixel format.
*
*   BUGS
*   As we can't check for every graphic card supporting PiP, we rely on the
*   availability of the cgxvideo.library to decide whether the PiP feature
*   is available.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


int IsMonitoringAvailable(const char *screenName)
{
    int available = FALSE;

    if (IntuitionBase && CyberGfxBase) {
        /*
        ** We have the 2 basic libraries (the cybergraphics.library is also
        ** available by emulation with Picasso96).
        ** Check the screen's pixel format and, if this is a 8-bit screen,
        ** check whether the PiP feature is available.
        */

        ULONG pixFmt;
        struct BitMap *bm;
        struct Screen *ae_screen;

        if ( (ae_screen = LockPubScreen(screenName)) ) {
            bm = ae_screen->RastPort.BitMap;

            if (GetCyberMapAttr(bm, CYBRMATTR_ISCYBERGFX) && GetCyberMapAttr(bm, CYBRMATTR_ISLINEARMEM)) {
                pixFmt = GetCyberMapAttr(bm, CYBRMATTR_PIXFMT);

                if (pixFmt != PIXFMT_LUT8 || CGXVideoBase) {
                    /*
                    ** If the cgxvideo.library is opened, then we assume the
                    ** PiP is available (we can't check for every chipset).
                    ** This PiP feature allows the monitoring on any foreign
                    ** screen (non-ECS/AGA).
                    ** Otherwise, the pixel format has to be Hi-Color or True
                    ** color.
                    */
                    available = TRUE;
                }
            }
            UnlockPubScreen(NULL, ae_screen);
        }
    }

    return available;
}


/****** Monitor/OpenMonitorWindow *******************************************
*
*   NAME
*   OpenMonitorWindow -- Open the monitor window.
*
*   SYNOPSIS
*   window = OpenMonitorWindow(screenName, left, top, resolution, flags)
*
*   struct VLabMonitor *OpenMonitorWindow(const char *, ULONG, ULONG,
*                   ULONG, ULONG);
*
*   FUNCTION
*   Open the monitor window on the specified screen. This only works if the
*   cybergraphics.library has been opened previously.
*   The function first attempt to open a PiP window if Picasso96 is running.
*   If this fails, it then attempts to use a CyberGraphX video layer. If
*   this also fails, a standard window is opened.
*   PiP (or video layer) support may be disabled but color monitoring is
*   then much slower.
*
*   INPUTS
*   screenName - Screen's name for the monitoring window.
*   resolution - Resolution ID. Values in the range [0; 2] describe PAL
*                resolutions. Values in the range [3; 5] describe NTSC
*                resolutions.
*   flags      - Monitoring flags (MONITOR_#?)
*
*   RESULT
*   window - Pointer to the fully initialized monitor object, or NULL.
*
*   NOTES
*   While monitoring the current input, the vlab hardware grabs in LoRes
*   non-lace mode. Therefore, the maximum resolution is 360×260 (NTSC) or
*   360×310 (PAL).
*   The name of the monitoring window is specific to each implementation :
*   - Standard window : VLab monitor.
*   - CGX Video Layer : Video monitor.
*   - Picasso96 PiP   : PiP monitor.
*   The window's title also reflects error conditions while monitoring.
*
*   BUGS
*   If the video layer is not available, an empty window could appears on
*   screen, then disappear before opening again. It all depends upon which
*   video layer function fails.
*   A work around would be to open a standard window first, then to attempt
*   to attach a video layer on it. But the size gadget comes into play and
*   an arbitration should be done on whether it would be always present or
*   not.
*
*   SEE ALSO
*   CloseMonitorWindow(), OS_OpenMonitor(), CGX_OpenPiPMonitor(),
*   P96_OpenPiPMonitor()
*
*****************************************************************************
*
*/


struct VLabMonitor *OpenMonitorWindow(const char *screenName, ULONG left, ULONG top, ULONG resolution, ULONG flags)
{
    struct Screen *ae_screen;
    struct VLabMonitor *monitor = NULL;

    if (resolution < sizeof(resolutions) / sizeof(resolutions[0])) {
        ULONG width, height, color;

        width  = resolutions[resolution].Width;
        height = resolutions[resolution].Height;
        color  = flags & MONITOR_COLOR;

        if ( (ae_screen = LockPubScreen(screenName)) ) {

            if (left == ~0)
                left = 0;

            if (top == ~0)
                top = ae_screen->BarHeight + 1;

            if ( !(flags & MONITOR_NO_PIP)) {
                /*
                ** First, try to open a PiP monitoring (requires Picasso96)
                */

                if (P96Base)
                    monitor = P96_OpenPiPMonitor(ae_screen, left, top, width, height, color);

                /*
                ** If there's no PiP, try again with a video layer.
                */

                if (monitor == NULL && CGXVideoBase)
                    monitor = CGX_OpenPiPMonitor(ae_screen, left, top, width, height, color);
            }

            /*
            ** No PiP nor video layer available : Use a standard window.
            */

            if (monitor == NULL && CyberGfxBase)        /* Also works with Picasso96 */
                monitor = OS_OpenMonitor(ae_screen, left, top, width, height, color);

            UnlockPubScreen(NULL, ae_screen);
        }

        if (monitor) {
            SetClipping(monitor, resolution);
            monitor->flags = flags;
            VLab_InitHardware();

            ReadEClock(&monitor->timer);
        }
    }

    return monitor;
}


/****** Monitor/UpdateMonitorWindow *****************************************
*
*   NAME
*   UpdateMonitorWindow -- Scan a new image.
*
*   SYNOPSIS
*   UpdateMonitorWindow(monitor)
*
*   void UpdateMonitorWindow(struct VLabMonitor *);
*
*   FUNCTION
*   Scan a new image then display it into the monitoring window. The window
*   title is updated to reflect the current status (in case an error occured)
*   or the current scanning speed (frames per second).
*
*   INPUTS
*   monitor - Pointer to the monitor object, which may be NULL.
*
*   SEE ALSO
*   OS_UpdateMonitor(), CGX_UpdatePiPMonitor(), P96_UpdatePiPMonitor()
*
*****************************************************************************
*
*/


void UpdateMonitorWindow(struct VLabMonitor *monitor)
{
    extern void putCharProc();

    static UBYTE fps_title[30];
    struct EClockVal eClock;
    struct fps_val fps;
    ULONG  tps;
    UBYTE  *title;

    if (monitor) {

        switch(monitor->pfUpdateMonitor(monitor)) {

            case VLABERR_OK:
                title = monitor->defaultName;

                if (TimerBase) {
                    title = monitor->fpsName;
                    tps = ReadEClock(&eClock);
                    ComputeFPS(&monitor->timer, &eClock, &fps, tps);
                    RawDoFmt(monitor->fpsName, &fps, putCharProc, fps_title);
                    title = fps_title;
                    monitor->timer = eClock;
                }
                break;

            case VLABERR_NOVIDEO:
                title = "No video";
                break;

            default:
                title = "Scan error";
                break;
        }

        SetWindowTitles(monitor->window, title, (UBYTE *) ~0);
    }
}


/****** Monitor/MonitorChangeMode *******************************************
*
*   NAME
*   MonitorChangeMode -- Change monitoring color mode.
*
*   SYNOPSIS
*   MonitorChangeMode(monitor, color)
*
*   void MonitorChangeMode(struct VLabMonitor *, BOOL);
*
*   FUNCTION
*   Change the color mode (color or grey scale) of the monitoring window.
*   This action takes place without closing/opening a new window.
*
*   INPUTS
*   monitor - Pointer to the monitor object, which may be NULL.
*   color   - FALSE = grey scale, TRUE = color.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


void MonitorChangeMode(struct VLabMonitor *monitor, BOOL color)
{
    if (monitor) {
        monitor->flags = monitor->flags & ~MONITOR_COLOR;
        if (color)
            monitor->flags |= MONITOR_COLOR;
    }
}


/****** Monitor/ResizeMonitor ***********************************************
*
*   NAME
*   ResizeMonitor -- Change monitoring window's size.
*
*   SYNOPSIS
*   newMonitor = ResizeMonitor(monitor, screenName, resolution)
*
*   struct VLabMonitor * ResizeMonitor(struct VLabMonitor *, const char *, ULONG);
*
*   FUNCTION
*   Change the monitoring window's size to a new resolution. The current
*   window's size is modified whenever possible, otherwise the window is
*   closed and a new one is opened.
*
*
*   INPUTS
*   monitor    - Pointer to the current monitor object (may be NULL)
*   screenName - Name of the public screen, in case we shall open a new
*                window.
*   resolution - Resolution ID.
*
*   RESULT
*   newMonitor - Pointer to the new monitor object, or NULL of the resizing
*                failed. In the later case, the monitoring is no longer
*                active.
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*   OS_ResizeMonitor(), CGX_ResizePiPMonitor(), P96_ResizePiPMonitor()
*
*****************************************************************************
*
*/


struct VLabMonitor *ResizeMonitor(struct VLabMonitor *monitor, const char *screenName, ULONG resolution)
{
    if (monitor && resolution < sizeof(resolutions) / sizeof(resolutions[0])) {
        ULONG left, top, width, height, flags;

        /*
        ** The function pointed to by monitor->pfResizeMonitor() may invalidate
        ** the monitor pointer and close the monitoring window. So we'd better
        ** save important informations before it happens.
        */

        flags = monitor->flags;
        left  = monitor->window->LeftEdge;
        top   = monitor->window->TopEdge;

        width  = resolutions[resolution].Width,
        height = resolutions[resolution].Height;

        if ( !(monitor = monitor->pfResizeMonitor(monitor, width, height)) )
            monitor = OpenMonitorWindow(screenName, left, top, resolution, flags);
        else
            SetClipping(monitor, resolution);
    }

    return monitor;
}


/****** Monitor/CloseMonitorWindow ******************************************
*
*   NAME
*   CloseMonitorWindow -- Close the monitor window.
*
*   SYNOPSIS
*   CloseMonitorWindow(monitor)
*
*   void CloseMonitorWindow(struct VLabMonitor *);
*
*   FUNCTION
*   Close the monitor window.
*
*   INPUTS
*   monitor - Pointer to the monitor object (may be NULL)
*
*   SEE ALSO
*   OpenMonitorWindow()
*
*****************************************************************************
*
*/


void CloseMonitorWindow(struct VLabMonitor *monitor)
{
    if (monitor)
        monitor->pfCloseMonitor(monitor);
}


/*------------------------------------------------------------------------------------------------*/
/*=====================   I N T U I T I O N   I M P L E M E N T A T I O N   ======================*/
/*------------------------------------------------------------------------------------------------*/


/****i* Monitor/open_window *************************************************
*
*   NAME
*   open_window -- Open a standard window.
*
*   SYNOPSIS
*   window = open_window(ae_screen, window_name, left, top, width, height,
*                   sizable)
*
*   struct Window * open_window(struct Screen *, const char *, ULONG, ULONG,
*                   ULONG, ULONG, BOOL);
*
*   FUNCTION
*   Open a standard OS window on the given screen. If the window can be
*   resized, the zoom gadget is initialized to make the window twice as
*   large as the picture (this works until the user changes the window's
*   dimensions).
*
*   INPUTS
*   ae_screen   - Pointer to ArtEffect's screen. The screen is already
*                 locked.
*   window_name - Name of the window.
*   left        - Left position.
*   top         - Top position
*   width       - Inner width.
*   height      - Inner height.
*   sizable     - TRUE : Size gadget in the bottom border, FALSE : The
*                 monitoring window cannot be resized.
*
*   RESULT
*   window - Pointer to the new window, or NULL.
*
*   NOTES
*   The zoom state as well as the limits of the window are loosely defined.
*   The minimum size is equal to the initial inner size, and the maximum
*   size is set the the screen's size.
*   The zoom is merely set to twice the initial inner size.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


static struct Window *open_window(struct Screen *ae_screen, const char *window_name, ULONG left, ULONG top,
        ULONG width, ULONG height, BOOL sizable)
{
    struct Window *win;
    WORD zoom[4] = {~0, ~0, width * 2, height * 2};

    win = OpenWindowTags(NULL,
                WA_Title, (ULONG) window_name,
                WA_Left, left,
                WA_Top, top,
                WA_InnerWidth, width,
                WA_InnerHeight, height,
                WA_DragBar, TRUE,
                WA_DepthGadget, TRUE,
                WA_SimpleRefresh, TRUE,
                WA_CloseGadget, TRUE,
                WA_RMBTrap, TRUE,
                WA_IDCMP, IDCMP_CLOSEWINDOW,
                WA_PubScreen, (ULONG) ae_screen,
                WA_SizeGadget, (sizable ? TRUE : FALSE),
                (sizable ? WA_Zoom : TAG_IGNORE), (ULONG) zoom,
                WA_SizeBBottom, TRUE,
                WA_AutoAdjust, TRUE,
                TAG_DONE);

    if (win && sizable) {
        /*
        ** Set the limits, otherwise the size of the window could not be changed.
        */
        WindowLimits(win, win->Width, win->Height, ~0, ~0);
    }

    return win;
}


/****i* Monitor/resize_window ***********************************************
*
*   NAME
*   resize_window -- Change window's size.
*
*   SYNOPSIS
*   resize_window(window, width, height)
*
*   void resize_window(struct Window *, ULONG, ULONG);
*
*   FUNCTION
*   Change the window's size to the new dimensions.
*
*   INPUTS
*   window - Pointer to the window.
*   width  - New inner width.
*   height - New inner height.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


static void resize_window(struct Window *window, ULONG width, ULONG height)
{
    width  += window->BorderLeft + window->BorderRight;
    height += window->BorderTop  + window->BorderBottom;

    WindowLimits(window, width, height, 0, 0);
    ChangeWindowBox(window, window->LeftEdge, window->TopEdge, width, height);
}


/****i* Monitor/OS_OpenMonitor **********************************************
*
*   NAME
*   OS_OpenMonitor -- Open the monitoring window.
*
*   SYNOPSIS
*   monitor = OS_OpenMonitor(ae_screen, left, top, width, height, color)
*
*   struct VLabMonitor * OS_OpenMonitor(struct Screen *, ULONG, ULONG, ULONG,
*                   ULONG, BOOL);
*
*   FUNCTION
*   Open the monitoring window on ArtEffect's screen. The monitoring
*   requires an RGB buffer which is allocated here.
*
*   INPUTS
*   ae_screen - Pointer to ArtEffect's screen, which is already locked.
*   width     - Monitoring width.
*   height    - Monitoring height.
*   color     - TRUE : color mode, FALSE : Grey scale.
*
*   RESULT
*   monitor - Pointer to the monitor object, or NULL if an error occured.
*
*   SEE ALSO
*   OS_CloseMonitor()
*
*****************************************************************************
*
*/


static struct VLabMonitor *OS_OpenMonitor(struct Screen *ae_screen, ULONG left, ULONG top,
            ULONG width, ULONG height, BOOL color)
{
    struct VLabMonitor *mon = NULL, *monitor = &Monitoring;
    struct Window *win;

    if ( (monitor->outBuff = (UBYTE *) AllocVec((VLAB_MAXWIDTH * VLAB_MAXHEIGHT_PAL / 4) * 3, MEMF_ANY)) ) {
        if ( (win = open_window(ae_screen, "VLab monitor", left, top, width, height, FALSE)) ) {

            monitor->pfCloseMonitor   = OS_CloseMonitor;
            monitor->pfUpdateMonitor  = OS_UpdateMonitor;
            monitor->pfResizeMonitor  = OS_ResizeMonitor;

            mon = monitor;
            monitor->defaultName = "VLab monitor";
            monitor->fpsName     = "VLab %ld.%lc fps";
            monitor->window = win;
        }
        else {
            FreeVec(monitor->outBuff);
            monitor->outBuff = NULL;
        }
    }

    return mon;
}


/****i* Monitor/OS_UpdateMonitor ********************************************
*
*   NAME
*   OS_UpdateMonitor -- Display a new image.
*
*   SYNOPSIS
*   status = OS_UpdateMonitor(monitor)
*
*   ULONG OS_UpdateMonitor(struct VLabMonitor *);
*
*   FUNCTION
*   Display a new image in the monitoring window.
*
*   INPUTS
*   monitor - Pointer to the monitoring object.
*
*   RESULT
*   status - One of the VLABERR_#? code
*
*   NOTES
*   The image is always refreshed, even when VLab_Grab() returns an error
*   code. Thus, a black picture is displayed when no video signal is detected
*
*   SEE ALSO
*
*****************************************************************************
*
*/


static ULONG OS_UpdateMonitor(struct VLabMonitor *monitor)
{
    struct Window *window = monitor->window;
    UBYTE *buffer;
    ULONG pixfmt, mode, modulo, status;

    mode = (monitor->ratio == 1) ? CAPTURE_LORES : CAPTURE_THUMBNAIL;
    status = VLab_Grab(monitor->xpos, monitor->ypos, monitor->width, monitor->height, mode, (monitor->flags & MONITOR_COLOR));

    if (monitor->flags & MONITOR_COLOR) {
        buffer = monitor->outBuff;
        VLab_YUV_to_RGB(buffer, monitor->width * monitor->height, mode);
        modulo = monitor->width * 3;
        pixfmt = RECTFMT_RGB;
    }
    else {
        buffer = VLab_GetYBuffer();
        pixfmt = RECTFMT_GREY8;
        modulo = monitor->width;
    }

    WritePixelArray(buffer, 0, 0, modulo, window->RPort, window->BorderLeft, window->BorderTop,
                            monitor->width, monitor->height, pixfmt);

    return status;
}


/****i* Monitor/OS_ResizeMonitor ********************************************
*
*   NAME
*   OS_ResizeMonitor -- Change monitoring window's size
*
*   SYNOPSIS
*   newMonitor = OS_ResizeMonitor(monitor, width, height)
*
*   struct VLabMonitor * OS_ResizeMonitor(struct VLabMonitor *, ULONG, ULONG);
*
*   FUNCTION
*   Change the size of the monitoring window.
*
*   INPUTS
*   monitor - Current monitor object.
*   width   - New width.
*   height  - New height.
*
*   RESULT
*   newMonitor - New monitor object, or NULL if the resizing failed. In the
*                later case, the monitoring is no longer active.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


static struct VLabMonitor *OS_ResizeMonitor(struct VLabMonitor *monitor, ULONG width, ULONG height)
{
    resize_window(monitor->window, width, height);
    return monitor;
}


/****i* Monitor/OS_CloseMonitor *********************************************
*
*   NAME
*   OS_CloseMonitor -- Close the monitor window.
*
*   SYNOPSIS
*   OS_CloseMonitor(monitor)
*
*   void OS_CloseMonitor(struct VLabMonitor *);
*
*   FUNCTION
*   Close the monitoring window and free associated ressources obtained by
*   OS_OpenMonitor.
*
*   INPUTS
*   monitor - Pointer to the monitor object.
*
*   SEE ALSO
*   OS_OpenMonitor()
*
*****************************************************************************
*
*/


static void OS_CloseMonitor(struct VLabMonitor *monitor)
{
    if (monitor->window)
        CloseWindow(monitor->window);

    if (monitor->outBuff)                   /* We also free the output buffer */
        FreeVec(monitor->outBuff);
}


/*------------------------------------------------------------------------------------------------*/
/*=================   C Y B E R G R A P H I C S   I M P L E M E N T A T I O N   ==================*/
/*------------------------------------------------------------------------------------------------*/

/****i* Monitor/CGX_OpenPiPMonitor ******************************************
*
*   NAME
*   CGX_OpenPiPMonitor -- Open a PiP monitor window.
*
*   SYNOPSIS
*   monitor = CGX_OpenPiPMonitor(ae_screen, left, top, width, height, colorMode)
*
*   struct VLabMonitor * CGX_OpenPiPMonitor(struct Screen *, ULONG, ULONG,
*                   ULONG, ULONG, BOOL);
*
*   FUNCTION
*   Open a monitor window as a PiP layer. This allows the monitoring in all
*   modes on a 8-bit chunky (CLUT) screen.
*
*   INPUTS
*   ae_screen - Pointer to ArtEffect's screen, which is already locked.
*   width     - Monitoring width.
*   height    - Monitoring height.
*   colorMode - TRUE : Color mode, FALSE : Grey scale.
*
*   RESULT
*   monitor - Pointer to the monitor object, or NULL.
*
*   NOTES
*   The UserData member of the associated window is used to store the handle
*   of the PiP video layer.
*
*   BUGS
*   The video layer cannot be depth arranged. If the user send the
*   surrounding window to the back, the layer still stay in front of every-
*   thing else.
*   Perhaps the use of the color keying feature could help here, but I've
*   experienced graphic corruption while testing this.
*
*   SEE ALSO
*   CGX_ClosePiPMonitor()
*
*****************************************************************************
*
*/


static struct VLabMonitor *CGX_OpenPiPMonitor(struct Screen *ae_screen, ULONG left, ULONG top,
                    ULONG width, ULONG height, BOOL colorMode)
{
    struct VLabMonitor *monitor = NULL;
    struct Window *win;
    APTR layer;

    /*
    ** Create the video layer, then open the window it'll be attached to.
    ** Don't do this the other way, or the user would see the window appear,
    ** then vanish, then appear again due to the polymorphic nature of the
    ** main OpenMonitor function.
    */

    if ( (layer = CreateVLayerHandleTags(ae_screen, VOA_SrcType, SRCFMT_YCbCr16,
                    VOA_SrcWidth, width, VOA_SrcHeight, height, TAG_DONE)) ) {
        if ( (win = open_window(ae_screen, "Video monitor", left, top, width, height, TRUE)) ) {
            /*
            ** Attach the video layer to the new window.
            ** win->UserData is then used to store the handle of that video layer.
            */

            if (!AttachVLayerTags(layer, win, TAG_DONE)) {

                monitor = &Monitoring;

                monitor->pfCloseMonitor   = CGX_ClosePiPMonitor;
                monitor->pfUpdateMonitor  = CGX_UpdatePiPMonitor;
                monitor->pfResizeMonitor  = CGX_ResizePiPMonitor;

                monitor->defaultName = "Video monitor";
                monitor->fpsName     = "Video %ld.%lc fps";
                win->UserData = (UBYTE *) layer;
                monitor->window = win;
                monitor->outBuff = NULL;
            }
            else
                CloseWindow(win);
        }

        if (!monitor)
            DeleteVLayerHandle(layer);
    }

    return monitor;
}


/****i* Monitor/CGX_UpdatePiPMonitor ****************************************
*
*   NAME
*   CGX_UpdatePiPMonitor -- Draw a new image in the monitor window;
*
*   SYNOPSIS
*   status = CGX_UpdatePiPMonitor(monitor)
*
*   ULONG CGX_UpdatePiPMonitor(struct VLabMonitor *);
*
*   FUNCTION
*   Draw a new image in the monitor window.
*
*   INPUTS
*   monitor - Pointer to the monitor object.
*
*   RESULT
*   status - One of the VLABERR_#? codes.
*
*   NOTES
*   The image is always refreshed, even when VLab_Grab() returns an error
*   code. Thus, a black picture is displayed when no video signal is detected
*
*   SEE ALSO
*
*****************************************************************************
*
*/


static ULONG CGX_UpdatePiPMonitor(struct VLabMonitor *monitor)
{
    APTR PiP = monitor->window->UserData;
    UBYTE *PiPMem;
    ULONG mode = (monitor->ratio == 2) ? CAPTURE_THUMBNAIL : CAPTURE_LORES;
    ULONG status;

    status = VLab_Grab(monitor->xpos, monitor->ypos, monitor->width, monitor->height, mode, (monitor->flags & MONITOR_COLOR));

    if (LockVLayer(PiP)) {
        PiPMem = (UBYTE *) GetVLayerAttr(PiP, VOA_BaseAddress);

        if (monitor->flags & MONITOR_COLOR)
            VLab_YUV_to_Color(PiPMem, monitor->width * monitor->height, mode);
        else
            VLab_YUV_to_Grey(PiPMem, monitor->width * monitor->height);

        UnlockVLayer(PiP);
    }

    return status;
}


/****i* Monitor/CGX_ResizePiPMonitor ****************************************
*
*   NAME
*   CGX_ResizePiPMonitor -- Change monitoring window's size
*
*   SYNOPSIS
*   newMonitor = CGX_ResizePiPMonitor(monitor, width, height)
*
*   struct VLabMonitor *CGX_ResizePiPMonitor(struct VLabMonitor *, ULONG, ULONG);
*
*   FUNCTION
*   Change the size of the monitoring window.
*
*   INPUTS
*   monitor - Current monitor object.
*   width   - New width.
*   height  - New height.
*
*   RESULT
*   newMonitor - New monitor object, or NULL if the resizing failed. In the
*                later case, the monitoring is no longer active.
*
*   NOTES
*
*   BUGS
*   The alternate dimensions of the monitoring window are not updated, as
*   there's no way to do it.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


static struct VLabMonitor *CGX_ResizePiPMonitor(struct VLabMonitor *monitor, ULONG width, ULONG height)
{
    struct Window *window = monitor->window;
    APTR PiP = window->UserData;

    /*
    ** Delete the video layer attached to the window, then
    ** create a new one.
    */

    if (PiP) {
        DetachVLayer(PiP);
        DeleteVLayerHandle(PiP);
    }
    window->UserData = NULL;

    if ( (PiP = CreateVLayerHandleTags(window->WScreen, VOA_SrcType, SRCFMT_YCbCr16,
                VOA_SrcWidth, width, VOA_SrcHeight, height, TAG_DONE)) ) {

        resize_window(window, width, height);

        if (AttachVLayerTags(PiP, window, TAG_DONE)) {
            /*
            ** If the new video layer cannot be attached, then close
            ** everything : The monitoring is no longer active.
            */
            DeleteVLayerHandle(PiP);
            CloseWindow(window);
            monitor = NULL;
        }
        else
            window->UserData = PiP;
    }
    else {
        CloseWindow(window);
        monitor = NULL;
    }

    return monitor;
}


/****i* Monitor/CGX_ClosePiPMonitor *****************************************
*
*   NAME
*   CGX_ClosePiPMonitor -- Close the monitoring window.
*
*   SYNOPSIS
*   CGX_ClosePiPMonitor(monitor)
*
*   void CGX_ClosePiPMonitor(struct VLabMonitor *);
*
*   FUNCTION
*   Close the PiP monitoring window, and free all associated ressources.
*
*   INPUTS
*   monitor - Pointer to the monitor object.
*
*   NOTES
*   UserData member of the associated Window structure is used to store the
*   handle for the video-layer.
*
*   SEE ALSO
*   CGX_OpenPiPMonitor()
*
*****************************************************************************
*
*/


static void CGX_ClosePiPMonitor(struct VLabMonitor *monitor)
{
    struct Window *window = monitor->window;
    APTR PiP = window->UserData;

    if (PiP) {
        DetachVLayer(PiP);
        DeleteVLayerHandle(PiP);
    }
    CloseWindow(window);
}


/*------------------------------------------------------------------------------------------------*/
/*====================   P I C A S S O   9 6   I M P L E M E N T A T I O N   =====================*/
/*------------------------------------------------------------------------------------------------*/


/****i* Monitor/P96_OpenPiPMonitor ******************************************
*
*   NAME
*   P96_OpenPiPMonitor -- Open a PiP monitor window.
*
*   SYNOPSIS
*   monitor = P96_OpenPiPMonitor(ae_screen, left, top, width, height, colorMode)
*
*   struct VLabMonitor * P96_OpenPiPMonitor(struct Screen *, ULONG, ULONG,
*                   ULONG, ULONG, BOOL);
*
*   FUNCTION
*   Open a monitor window as a PiP layer. This allows the monitoring in all
*   modes on a 8-bit chunky (CLUT) screen.
*
*   INPUTS
*   ae_screen - Pointer to ArtEffect's screen, which is already locked.
*   width     - Monitoring width.
*   height    - Monitoring height.
*   colorMode - TRUE : Color mode, FALSE : Grey scale.
*
*   RESULT
*   monitor - Pointer to the monitor object, or NULL.
*
*   NOTES
*
*   BUGS
*   Due to a bug in Picasso96, the PiP window is twice as large as specified
*   when opened on an Hi-Color screen. If the window is resized by software,
*   the PiP image is cropped on the right border, so there's no way to work
*   around this.
*
*   SEE ALSO
*   P96_ClosePiPMonitor()
*
*****************************************************************************
*
*/


static struct VLabMonitor *P96_OpenPiPMonitor(struct Screen *ae_screen, ULONG left, ULONG top,
                    ULONG width, ULONG height, BOOL colorMode)
{
    struct Window *win;
    struct VLabMonitor *monitor = NULL;
    WORD zoom[4] = {~0, ~0, width * 2, height * 2};

    if ( (win = p96PIP_OpenTags(
                P96PIP_SourceFormat, RGBFB_Y4U2V2,
                P96PIP_SourceWidth, width,
                P96PIP_SourceHeight, height,
                P96PIP_InitialIntScaling, TRUE,     /* Make things look better on Hi-Color screens */
                WA_Title, (ULONG) "PiP monitor",
                WA_Left, left,
                WA_Top, top,
                WA_InnerWidth, width,
                WA_InnerHeight, height,
                WA_DragBar, TRUE,
                WA_DepthGadget, TRUE,
                WA_SimpleRefresh, TRUE,
                WA_SizeGadget, TRUE,
                WA_SizeBBottom, TRUE,
                WA_CloseGadget, TRUE,
                WA_RMBTrap, TRUE,
                WA_IDCMP, IDCMP_CLOSEWINDOW,
                WA_PubScreen, (ULONG) ae_screen,
                WA_Zoom, (ULONG) zoom,
                TAG_DONE)) ) {

        WindowLimits(win, win->Width, win->Height, ~0, ~0);
        monitor = &Monitoring;

        monitor->pfCloseMonitor   = P96_ClosePiPMonitor;
        monitor->pfUpdateMonitor  = P96_UpdatePiPMonitor;
        monitor->pfResizeMonitor  = P96_ResizePiPMonitor;

        monitor->defaultName = "PiP monitor";
        monitor->fpsName     = "PiP %ld.%lc fps";
        monitor->window = win;
        monitor->outBuff = NULL;
    }

    return monitor;
}


/****i* Monitor/P96_UpdatePiPMonitor ****************************************
*
*   NAME
*   P96_UpdatePiPMonitor -- Draw a new image in the monitor window;
*
*   SYNOPSIS
*   status = P96_UpdatePiPMonitor(monitor)
*
*   ULONG P96_UpdatePiPMonitor(struct VLabMonitor *);
*
*   FUNCTION
*   Draw a new image in the monitor window.
*
*   INPUTS
*   monitor - Pointer to the monitor object.
*
*   RESULT
*   status - One of the VLABERR_#? codes.
*
*   NOTES
*   The image is always refreshed, even when VLab_Grab() returns an error
*   code. Thus, a black picture is displayed when no video signal is detected
*
*   SEE ALSO
*
*****************************************************************************
*
*/


static ULONG P96_UpdatePiPMonitor(struct VLabMonitor *monitor)
{
    struct BitMap *PiP;
    UBYTE *PiPMem;
    LONG PiPLock;
    ULONG mode = (monitor->ratio == 2) ? CAPTURE_THUMBNAIL : CAPTURE_LORES;
    ULONG status;

    status = VLab_Grab(monitor->xpos, monitor->ypos, monitor->width, monitor->height, mode, (monitor->flags & MONITOR_COLOR));

    if (p96PIP_GetTags(monitor->window, P96PIP_SourceBitMap, (ULONG) &PiP, TAG_DONE)) {
        PiPLock = p96LockBitMap(PiP, (UBYTE *) &PiPMem, sizeof(UBYTE *));

        if (monitor->flags & MONITOR_COLOR)
            VLab_YUV_to_Color(PiPMem, monitor->width * monitor->height, mode);
        else
            VLab_YUV_to_Grey(PiPMem, monitor->width * monitor->height);

        p96UnlockBitMap(PiP, PiPLock);
    }

    return status;
}


/****i* Monitor/P96_ResizePiPMonitor ****************************************
*
*   NAME
*   P96_ResizePiPMonitor -- Change monitoring window's size
*
*   SYNOPSIS
*   newMonitor = P96_ResizePiPMonitor(monitor, width, height)
*
*   struct VLabMonitor *P96_ResizePiPMonitor(struct VLabMonitor *, ULONG, ULONG);
*
*   FUNCTION
*   Change the size of the monitoring window.
*
*   INPUTS
*   monitor - Current monitor object.
*   width   - New width.
*   height  - New height.
*
*   RESULT
*   newMonitor - Always NULL.
*
*   NOTES
*   Unlike the others implementation, the PiP window content cannot be
*   resized without destroying the existing window.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


static struct VLabMonitor *P96_ResizePiPMonitor(struct VLabMonitor *monitor, ULONG width, ULONG height)
{
    P96_ClosePiPMonitor(monitor);

    return NULL;
}


/****i* Monitor/P96_ClosePiPMonitor *****************************************
*
*   NAME
*   P96_ClosePiPMonitor -- Close the monitoring window.
*
*   SYNOPSIS
*   P96_ClosePiPMonitor(monitor)
*
*   void P96_ClosePiPMonitor(struct VLabMonitor *);
*
*   FUNCTION
*   Close the PiP monitoring window, and free all associated ressources.
*
*   INPUTS
*   monitor - Pointer to the monitor object.
*
*   NOTES
*   UserData member of the associated Window structure is used to store the
*   handle for the video-layer.
*
*   SEE ALSO
*   P96_OpenPiPMonitor()
*
*****************************************************************************
*
*/


static void P96_ClosePiPMonitor(struct VLabMonitor *monitor)
{
    p96PIP_Close(monitor->window);
}

