/*
 *  vo_kva.c
 *
 *  Copyleft 2007 by KO Myung-Hun (komh@chollian.net)
 *
 *  Changes :
 *      KO Myung-Hun <komh@chollian.net> 2007/06/11
 *          - Added workaround for T23 laptop with S3 Video
 *
 *      KO Myung-Hun <komh@chollian.net> 2007/06/17
 *          - Implemented VOCTRL_SET_EQUALIZER and VOCTRL_GET_EQUALIZER
 *          - Added YV12 as a supported image format
 *          - Added process VK_ENTER and VK_NEWLINE
 *
 */

#define INCL_WIN
#include <os2.h>

#include <fourcc.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "config.h"
#include "mp_msg.h"
#include "help_mp.h"
#include "video_out.h"
#include "video_out_internal.h"

#include "fastmemcpy.h"
#include "osdep/keycodes.h"
#include "input/input.h"
#include "input/mouse.h"
#include "subopt-helper.h"

#include "cpudetect.h"
#include "libswscale/swscale.h"

#include "kva.h"

static vo_info_t info =
{
    "DIVE/WarpOverlay! video output",
    "kva",
    "KO Myung-Hun <komh@chollian.net>",
    ""
};

LIBVO_EXTERN( kva )

#define WC_MPLAYER      "WC_MPLAYER"
#define COLOR_OVERLAY   0x000008

#define SRC_WIDTH   m_int.kvas.szlSrcSize.cx
#define SRC_HEIGHT  m_int.kvas.szlSrcSize.cy

#define SCREEN_WIDTH    m_int.kvac.cxScreen
#define SCREEN_HEIGHT   m_int.kvac.cyScreen

typedef struct tagVOKVAINTERNAL
{
    HAB         hab;
    HMQ         hmq;
    HWND        hwndFrame;
    HWND        hwndClient;
    HWND        hwndSysMenu;
    HWND        hwndTitleBar;
    HWND        hwndMinMax;
    BOOL        fFullScreen;
    FOURCC      fcc;
    INT         iImageFormat;
    KVASETUP    kvas;
    KVACAPS     kvac;
    RECTL       rclDst;
    INT         bpp;
    LONG        lStride;
    PBYTE       pbImage;
    BOOL        fFixT23;
    PFNWP       pfnwpOldFrame;
    uint8_t    *yv12Planes[ 3 ];    // use as YUV420P format unlike name
    int         yv12Stride[ 3 ];
    struct SwsContext *sws;
} VOKVAINTERNAL, *PVOKVAINTERNAL;

static VOKVAINTERNAL m_int;

extern void mplayer_put_key( int code );            // let mplayer handel the keyevents

static PBYTE imgCreate( VOID )
{
    int size;

    size = SRC_HEIGHT * m_int.lStride;

    if( m_int.iImageFormat == IMGFMT_YV12 )
        size += size / 2;

    return malloc( size );
}

static VOID imgFree( PBYTE pbImage )
{
    if( pbImage )
        free( pbImage );
}

static VOID imgDisplay( PBYTE pbImage )
{
    PVOID       pBuffer;
    ULONG       ulBPL;

    if( !kvaLockBuffer( &pBuffer, &ulBPL ))
    {
        if( m_int.iImageFormat == IMGFMT_YV12 )
        {
            sws_scale( m_int.sws, m_int.yv12Planes, m_int.yv12Stride, 0, SRC_HEIGHT,
                       &pBuffer, &ulBPL );

            if( gCpuCaps.hasMMX )
                asm volatile ("emms\n\t");
        }
        else
            memcpy( pBuffer, pbImage, SRC_HEIGHT * ulBPL );

        kvaUnlockBuffer();
    }
}

#define MRETURN( ret )  return ( MRESULT )( ret )

static MRESULT EXPENTRY NewFrameWndProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
{
    switch( msg )
    {
        case WM_QUERYTRACKINFO :
        {
            //USHORT      ustflags = SHORT1FROMMP( mp1 );
            PTRACKINFO  pti = ( PTRACKINFO )mp2;
            RECTL       rcl;

            if( m_int.fFullScreen )
                break;

            m_int.pfnwpOldFrame( hwnd, msg, mp1, mp2 );

            //if( ustflags & ( TF_LEFT | TF_RIGHT | TF_TOP | TF_BOTTOM | TF_SETPOINTERPOS ))
            {
            #if 0
                pti->rclBoundary.xLeft = 0;
                pti->rclBoundary.yBottom = 0;
                pti->rclBoundary.xRight = SCREEN_WIDTH;
                pti->rclBoundary.yTop = SCREEN_HEIGHT;
            #endif

                rcl.xLeft = 0;
                rcl.yBottom = 0;
                rcl.xRight = SRC_WIDTH + 1 ;
                rcl.yTop = SRC_HEIGHT + 1;

                WinCalcFrameRect( hwnd, &rcl, FALSE );

                pti->ptlMinTrackSize.x = rcl.xRight - rcl.xLeft;
                pti->ptlMinTrackSize.y = rcl.yTop - rcl.yBottom;

                pti->ptlMaxTrackSize.x = SCREEN_WIDTH;
                pti->ptlMaxTrackSize.y = SCREEN_HEIGHT;
            }

            MRETURN( TRUE );
        }

        case WM_ADJUSTWINDOWPOS :
        {
            PSWP    pswp = ( PSWP )mp1;
            RECTL   rcl;

            if( m_int.fFullScreen )
                break;

            if( pswp->fl & SWP_SIZE )
            {
                rcl.xLeft = pswp->x;
                rcl.yBottom = pswp->y;
                rcl.xRight = rcl.xLeft + pswp->cx;
                rcl.yTop = rcl.yBottom + pswp->cy;

                WinCalcFrameRect( hwnd, &rcl, TRUE );

                if( rcl.xRight - rcl.xLeft <= SRC_WIDTH )
                    rcl.xRight = rcl.xLeft + ( SRC_WIDTH + 1 );

                if( rcl.yTop - rcl.yBottom <= SRC_HEIGHT )
                    rcl.yTop = rcl.yBottom + ( SRC_HEIGHT + 1 );

                WinCalcFrameRect( hwnd, &rcl, FALSE );

                if( rcl.xRight - rcl.xLeft > SCREEN_WIDTH )
                {
                    rcl.xLeft = 0;
                    rcl.xRight = SCREEN_WIDTH;
                }

                if( rcl.yTop - rcl.yBottom > SCREEN_HEIGHT )
                {
                    rcl.yBottom = 0;
                    rcl.yTop = SCREEN_HEIGHT;
                }

                pswp->fl |= SWP_MOVE;
                pswp->x = rcl.xLeft;
                pswp->y = rcl.yBottom;

                pswp->cx = rcl.xRight - rcl.xLeft;
                pswp->cy = rcl.yTop - rcl.yBottom;
            }

            break;
        }
    }

    MRETURN( m_int.pfnwpOldFrame( hwnd, msg, mp1, mp2 ));
}

static MRESULT EXPENTRY WndProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
{
    switch( msg )
    {
        case WM_CLOSE :
            mplayer_put_key( KEY_CLOSE_WIN );

            MRETURN( 0 );

        case WM_CHAR :
        {
            USHORT fsFlags = SHORT1FROMMP( mp1 );
            USHORT usCh = SHORT1FROMMP( mp2 );
            USHORT usVk = SHORT2FROMMP( mp2 );

            if( fsFlags & KC_KEYUP )
                break;

            if( fsFlags & KC_VIRTUALKEY )
            {
                switch( usVk )
                {
                    case VK_LEFT :
                        mplayer_put_key( KEY_LEFT );
                        break;

                    case VK_UP :
                        mplayer_put_key( KEY_UP );
                        break;

                    case VK_RIGHT :
                        mplayer_put_key( KEY_RIGHT );
                        break;

                    case VK_DOWN :
                        mplayer_put_key( KEY_DOWN );
                        break;

                    case VK_TAB :
                        mplayer_put_key( KEY_TAB );
                        break;

                    case VK_BACKSPACE :
                        mplayer_put_key( KEY_BS );
                        break;

                    case VK_DELETE :
                        mplayer_put_key( KEY_DELETE );
                        break;

                    case VK_INSERT :
                        mplayer_put_key( KEY_INSERT );
                        break;

                    case VK_HOME :
                        mplayer_put_key( KEY_HOME );
                        break;

                    case VK_END :
                        mplayer_put_key( KEY_END );
                        break;

                    case VK_PAGEUP :
                        mplayer_put_key( KEY_PAGE_UP );
                        break;

                    case VK_PAGEDOWN :
                        mplayer_put_key( KEY_PAGE_DOWN );
                        break;

                    case VK_ESC :
                        mplayer_put_key( KEY_ESC );
                        break;

                    case VK_SPACE :
                        mplayer_put_key(' ');
                        break;

                    //case VK_ENTER :
                    case VK_NEWLINE :
                        mplayer_put_key( KEY_ENTER );
                        break;
                }
            }
            else if(( fsFlags & KC_CHAR ) && !HIBYTE( usCh ))
                mplayer_put_key( usCh );

            MRETURN( TRUE );
        }

        case WM_BUTTON1DOWN :
            if( WinQueryFocus( HWND_DESKTOP ) != hwnd )
            {
                WinSetFocus( HWND_DESKTOP, hwnd );
                MRETURN( TRUE );
            }

            if( !vo_nomouse_input )
                mplayer_put_key( MOUSE_BTN0 );

            MRETURN( TRUE );

        case WM_BUTTON3DOWN :
            if( WinQueryFocus( HWND_DESKTOP ) != hwnd )
            {
                WinSetFocus( HWND_DESKTOP, hwnd );
                MRETURN( TRUE );
            }

            if( !vo_nomouse_input )
               mplayer_put_key( MOUSE_BTN1 );

            MRETURN( TRUE );

        case WM_BUTTON2DOWN :
            if( WinQueryFocus( HWND_DESKTOP ) != hwnd )
            {
                WinSetFocus( HWND_DESKTOP, hwnd );
                MRETURN( TRUE );
            }

            if( !vo_nomouse_input )
                mplayer_put_key( MOUSE_BTN2 );

            MRETURN( TRUE );

        case WM_BUTTON1DBLCLK :
            if( !vo_nomouse_input )
                mplayer_put_key( MOUSE_BTN0_DBL );

            MRETURN( TRUE );

        case WM_BUTTON3DBLCLK :
            if( !vo_nomouse_input )
                mplayer_put_key( MOUSE_BTN1_DBL );

            MRETURN( TRUE );

        case WM_BUTTON2DBLCLK :
            if( !vo_nomouse_input)
                mplayer_put_key( MOUSE_BTN2_DBL );

            MRETURN( TRUE );
    }

    MRETURN( WinDefWindowProc( hwnd, msg, mp1, mp2 ));
}

static int preinit( const char *arg )
{
    ULONG   flFrameFlags;

    BOOL    fUseDive = FALSE;
    BOOL    fFixT23 = FALSE;

    opt_t subopts[] = {
        {"dive", OPT_ARG_BOOL,  &fUseDive, NULL, 0 },
        {"t23", OPT_ARG_BOOL, &fFixT23, NULL, 0 },
        { NULL, 0, NULL, NULL, 0 }
    };

    if( subopt_parse( arg, subopts ) != 0 )
        return -1;

    memset( &m_int, 0, sizeof( VOKVAINTERNAL ));

    m_int.hab = WinInitialize( 0 );
    m_int.hmq = WinCreateMsgQueue( m_int.hab, 0);

    WinRegisterClass(
        m_int.hab,
        WC_MPLAYER,
        WndProc,
        CS_SIZEREDRAW | CS_MOVENOTIFY,
        sizeof( PVOID )
    );

    flFrameFlags = FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_SIZEBORDER |
                   FCF_TASKLIST;

    m_int.hwndFrame =
        WinCreateStdWindow(
            HWND_DESKTOP,               // parent window handle
            WS_VISIBLE,                 // frame window style
            &flFrameFlags,              // window style
            WC_MPLAYER,                 // class name
            "",                         // window title
            0L,                         // default client style
            NULLHANDLE,                 // resource in exe file
            1,                          // frame window id
            &m_int.hwndClient           // client window handle
        );

    if( m_int.hwndFrame == NULLHANDLE )
        return -1;

    m_int.hwndSysMenu = WinWindowFromID( m_int.hwndFrame, FID_SYSMENU );
    m_int.hwndTitleBar = WinWindowFromID( m_int.hwndFrame, FID_TITLEBAR );
    m_int.hwndMinMax = WinWindowFromID( m_int.hwndFrame, FID_MINMAX );

    m_int.fFullScreen = FALSE;

    m_int.fFixT23 = fFixT23;

    if( m_int.fFixT23 )
        m_int.pfnwpOldFrame = WinSubclassWindow( m_int.hwndFrame, NewFrameWndProc );

    if( kvaInit( fUseDive, m_int.hwndClient, COLOR_OVERLAY ))
        return -1;

    kvaCaps( &m_int.kvac );

    switch( m_int.kvac.ulMode )
    {
        case KVAM_WO :
            m_int.fcc = FOURCC_Y422;
            m_int.iImageFormat = IMGFMT_YUY2;
            m_int.bpp = 2;
            break;

        case KVAM_DIVE :
        default :
            switch( m_int.kvac.fccScreen )
            {
                case FOURCC_BGR4 :
                case FOURCC_BGR3 :
                case FOURCC_LUT8 :  // maybe best T.T
                    m_int.fcc = FOURCC_BGR3;
                    m_int.iImageFormat = IMGFMT_BGR24;
                    m_int.bpp = 3;
                    break;

                case FOURCC_R565 :
                    m_int.fcc = FOURCC_R565;
                    m_int.iImageFormat = IMGFMT_BGR16;
                    m_int.bpp = 2;
                    break;

                case FOURCC_R555 :
                    m_int.fcc = FOURCC_R555;
                    m_int.iImageFormat = IMGFMT_BGR15;
                    m_int.bpp = 2;
                    break;
            }
            break;
    }

    return 0;
}

static void uninit( void )
{
    if( m_int.pbImage )
        imgFree( m_int.pbImage );

    if( m_int.sws )
        sws_freeContext( m_int.sws );

    if( m_int.hwndFrame != NULLHANDLE )
    {
        if( m_int.fFixT23 )
            WinSubclassWindow( m_int.hwndFrame, m_int.pfnwpOldFrame );

        kvaResetAttr();
        kvaDone();
        WinDestroyWindow( m_int.hwndFrame );
    }

    WinDestroyMsgQueue( m_int.hmq );
    WinTerminate( m_int.hab );
}

static int config( uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t flags, char *title, uint32_t format )
{
    RECTL   rcl;

    if( format != m_int.iImageFormat )
    {
        mp_msg( MSGT_VO, MSGL_ERR, "KVA : Unsupported image format 0x%X(%s)\n", format, vo_format_name(format));
        return -1;
    }

    if( mp_msg_test( MSGT_VO, MSGL_V ))
        mp_msg( MSGT_VO, MSGL_V, "KVA: Using 0x%X (%s) image format\n", format, vo_format_name(format));

    if( m_int.pbImage )
        imgFree( m_int.pbImage );

    m_int.kvas.ulLength = sizeof( KVASETUP );
    m_int.kvas.szlSrcSize.cx = width;
    m_int.kvas.szlSrcSize.cy = height;
    m_int.kvas.rclSrcRect.xLeft = 0;
    m_int.kvas.rclSrcRect.yTop = 0;
    m_int.kvas.rclSrcRect.xRight = width;
    m_int.kvas.rclSrcRect.yBottom = height;
    m_int.kvas.ulRatio = KVAR_ORIGINAL;
    m_int.kvas.fccSrcColor = m_int.fcc;

    kvaSetup( &m_int.kvas );

    m_int.lStride = width * m_int.bpp;

    m_int.pbImage = imgCreate();

    if( m_int.iImageFormat == IMGFMT_YV12 )
    {
        int dstFormat;

        if( m_int.sws )
            sws_freeContext( m_int.sws );

        m_int.yv12Planes[ 0 ] = m_int.pbImage;
        m_int.yv12Planes[ 1 ] = m_int.yv12Planes[ 0 ] + ( m_int.lStride * height );
        m_int.yv12Planes[ 2 ] = m_int.yv12Planes[ 1 ] + ( m_int.lStride * height ) / 4;

        m_int.yv12Stride[ 0 ] = m_int.lStride;
        m_int.yv12Stride[ 1 ] = m_int.lStride / 2;
        m_int.yv12Stride[ 2 ] = m_int.lStride / 2;

        switch( m_int.fcc )
        {
            case FOURCC_Y422 :
                dstFormat = PIX_FMT_YUYV422;
                break;

            case FOURCC_BGR3 :
                dstFormat = PIX_FMT_BGR24;
                break;

            case FOURCC_R565 :
                dstFormat = PIX_FMT_BGR565;
                break;

            case FOURCC_R555 :
                dstFormat = PIX_FMT_BGR555;
                break;
        }

        m_int.sws = sws_getContext( width, height, PIX_FMT_YUV420P,
                                    width, height, dstFormat,
                                    SWS_FAST_BILINEAR | SWS_PRINT_INFO | get_sws_cpuflags(),
                                    NULL, NULL, NULL );
    }

    WinSetWindowText( m_int.hwndFrame, title );

    if( !d_width )
        d_width = width;

    if( !d_height )
        d_height = height;

    m_int.fFullScreen = flags & VOFLAG_FULLSCREEN;

    // workaround for T23 laptop with S3 Video by Franz Bakan
    if( !m_int.fFullScreen && m_int.fFixT23 )
    {
        d_width++;
        d_height++;
    }

    m_int.rclDst.xLeft = (( LONG )SCREEN_WIDTH - ( LONG )d_width ) / 2;
    m_int.rclDst.yBottom = (( LONG )SCREEN_HEIGHT - ( LONG )d_height ) /2 ;
    m_int.rclDst.xRight = m_int.rclDst.xLeft + d_width;
    m_int.rclDst.yTop = m_int.rclDst.yBottom + d_height;

    if( m_int.fFullScreen )
    {
        d_width = SCREEN_WIDTH;
        d_height = SCREEN_HEIGHT;

        // when -fs option is used without this, title bar is not highlighted
        WinSetActiveWindow( HWND_DESKTOP, m_int.hwndFrame );

        WinSetParent( m_int.hwndSysMenu, HWND_OBJECT, FALSE );
        WinSetParent( m_int.hwndTitleBar, HWND_OBJECT, FALSE );
        WinSetParent( m_int.hwndMinMax, HWND_OBJECT, FALSE );
    }

    rcl.xLeft = (( LONG )SCREEN_WIDTH - ( LONG )d_width ) / 2;
    rcl.yBottom = (( LONG )SCREEN_HEIGHT - ( LONG )d_height ) /2 ;
    rcl.xRight = rcl.xLeft + d_width;
    rcl.yTop = rcl.yBottom + d_height;

    WinCalcFrameRect( m_int.hwndFrame, &rcl, FALSE );

    WinSetWindowPos( m_int.hwndFrame, HWND_TOP,
                     rcl.xLeft, rcl.yBottom,
                     rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
                     SWP_SIZE | SWP_MOVE | SWP_ZORDER | SWP_SHOW | SWP_ACTIVATE );

    return 0;
}

static uint32_t get_image( mp_image_t *mpi )
{
    if( mpi->imgfmt != m_int.iImageFormat )
        return VO_FALSE;

    if( m_int.iImageFormat == IMGFMT_YV12 )
    {
        mpi->planes[ 1 ] = m_int.yv12Planes[ 1 ];
        mpi->planes[ 2 ] = m_int.yv12Planes[ 2 ];

        mpi->stride[ 1 ] = m_int.yv12Stride[ 1 ];
        mpi->stride[ 2 ] = m_int.yv12Stride[ 2 ];
    }

    mpi->planes[0] = m_int.pbImage;
    mpi->stride[0] = m_int.lStride;
    mpi->flags |= MP_IMGFLAG_DIRECT;

    return VO_TRUE;
}

static int query_format( uint32_t format )
{
    int res = 0;

    // here, modify image format and bpp in case of YV12
    if( format == IMGFMT_YV12 )
    {
        m_int.iImageFormat = IMGFMT_YV12;
        m_int.bpp = 1;
    }

    if( format == m_int.iImageFormat )
    {
        res = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_OSD |
               VFCAP_HWSCALE_UP;

        if( !m_int.fFixT23 )
            res |= VFCAP_HWSCALE_DOWN;
    }

    return res;
}

static uint32_t color_ctrl_set( char *what, int value )
{
    ULONG   ulAttr;
    ULONG   ulValue;

    if( !strcmp( what, "brightness"))
        ulAttr = KVAA_BRIGHTNESS;
    else if( !strcmp( what, "contrast"))
        ulAttr = KVAA_CONTRAST;
    else if( !strcmp( what, "hue"))
        ulAttr = KVAA_HUE;
    else if( !strcmp( what, "saturation"))
        ulAttr = KVAA_SATURATION;
    else
        return VO_NOTIMPL;

    ulValue = ( value + 100 ) * 255 / 200;

    if( kvaSetAttr( ulAttr, &ulValue ))
        return VO_NOTIMPL;

    return VO_TRUE;
}

static uint32_t color_ctrl_get( char *what, int *value )
{
    ULONG   ulAttr;
    ULONG   ulValue;

    if( !strcmp( what, "brightness"))
        ulAttr = KVAA_BRIGHTNESS;
    else if( !strcmp( what, "contrast"))
        ulAttr = KVAA_CONTRAST;
    else if( !strcmp( what, "hue"))
        ulAttr = KVAA_HUE;
    else if( !strcmp( what, "saturation"))
        ulAttr = KVAA_SATURATION;
    else
        return VO_NOTIMPL;

    if( kvaQueryAttr( ulAttr, &ulValue ))
        return VO_NOTIMPL;

    // add 1 to adjust range
    *value = (( ulValue + 1 ) * 200 / 255 ) - 100;

    return VO_TRUE;
}

static int control( uint32_t request, void *data, ... )
{
    switch( request )
    {
        case VOCTRL_GET_IMAGE :
            return get_image( data );

        case VOCTRL_QUERY_FORMAT :
            return query_format( *(( uint32_t * )data ));

        case VOCTRL_FULLSCREEN :
        {
            RECTL   rcl;

            m_int.fFullScreen = !m_int.fFullScreen;

            if( m_int.fFullScreen )
            {
                WinSetParent( m_int.hwndSysMenu, HWND_OBJECT, FALSE );
                WinSetParent( m_int.hwndTitleBar, HWND_OBJECT, FALSE );
                WinSetParent( m_int.hwndMinMax, HWND_OBJECT, FALSE );

                rcl.xLeft = 0;
                rcl.yBottom = 0;
                rcl.xRight = SCREEN_WIDTH;
                rcl.yTop = SCREEN_HEIGHT;
            }
            else
            {
                WinSetParent( m_int.hwndSysMenu, m_int.hwndFrame, FALSE );
                WinSetParent( m_int.hwndTitleBar, m_int.hwndFrame, FALSE );
                WinSetParent( m_int.hwndMinMax, m_int.hwndFrame, FALSE );

                rcl = m_int.rclDst;
            }

            WinCalcFrameRect( m_int.hwndFrame, &rcl, FALSE );

            WinSetWindowPos( m_int.hwndFrame, HWND_TOP,
                             rcl.xLeft, rcl.yBottom,
                             rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
                             SWP_SIZE | SWP_MOVE | SWP_ZORDER | SWP_SHOW | SWP_ACTIVATE );

            return VO_TRUE;
        }

        case VOCTRL_SET_EQUALIZER :
        {
            va_list ap;
            int     value;

            va_start( ap, data );
            value = va_arg( ap, int );
            va_end( ap );

            return color_ctrl_set( data, value );
        }

        case VOCTRL_GET_EQUALIZER :
        {
            va_list ap;
            int     *value;

            va_start( ap, data );
            value = va_arg( ap, int * );
            va_end( ap );

            return color_ctrl_get( data, value );
        }
    }

    return VO_NOTIMPL;
}

static int draw_frame( uint8_t *src[] )
{
    memcpy( m_int.pbImage, src[ 0 ], SRC_HEIGHT * m_int.lStride );

    return 0;
}

static int draw_slice( uint8_t *src[], int stride[], int w, int h, int x, int y )
{
    uint8_t *s;
    uint8_t *d;

    // copy Y
    d = m_int.yv12Planes[ 0 ] + m_int.yv12Stride[ 0 ] * y + x;
    s = src[ 0 ];
    mem2agpcpy_pic( d, s, w, h, m_int.yv12Stride[ 0 ], stride[ 0 ]);

    w /= 2; h /= 2; x /= 2; y /= 2;

    // copy U
    d = m_int.yv12Planes[ 1 ] + m_int.yv12Stride[ 1 ] * y + x;
    s = src[ 1 ];
    mem2agpcpy_pic( d, s, w, h, m_int.yv12Stride[ 1 ], stride[ 1 ]);

    // copy V
    d = m_int.yv12Planes[ 2 ] + m_int.yv12Stride[ 2 ] * y + x;
    s = src[ 2 ];
    mem2agpcpy_pic( d, s, w, h, m_int.yv12Stride[ 2 ], stride[ 2 ]);

    return 0;
}

#define vo_draw_alpha( imgfmt ) \
    vo_draw_alpha_##imgfmt( w, h, src, srca, stride, \
                            m_int.pbImage + m_int.lStride * y0 + m_int.bpp * x0 , \
                            m_int.lStride )

static void draw_alpha(int x0, int y0, int w, int h, unsigned char *src,
        unsigned char *srca, int stride)
{
    switch( m_int.iImageFormat )
    {
        case IMGFMT_YV12 :
            vo_draw_alpha( yv12 );
            break;

        case IMGFMT_YUY2 :
            vo_draw_alpha( yuy2 );
            break;

        case IMGFMT_BGR24 :
            vo_draw_alpha( rgb24 );
            break;

        case IMGFMT_BGR16 :
            vo_draw_alpha( rgb16 );
            break;

        case IMGFMT_BGR15 :
            vo_draw_alpha( rgb15 );
            break;
    }
}

static void draw_osd( void )
{
    vo_draw_text( SRC_WIDTH, SRC_HEIGHT, draw_alpha );
}

static void flip_page( void )
{
    imgDisplay( m_int.pbImage );
}

static void check_events( void )
{
    QMSG    qm;

    while( WinPeekMsg( m_int.hab, &qm, NULLHANDLE, 0, 0, PM_REMOVE ))
        WinDispatchMsg( m_int.hab, &qm );
}


