<commit_before>/*****************************************************************************
 * kva.c: KVA video output plugin for vlc
 *****************************************************************************
 * Copyright (C) 2010, 2011, 2012 VLC authors and VideoLAN
 *
 * Authors: <NAME> <<EMAIL>>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_vout_display.h>
#include <vlc_picture_pool.h>

#include <ctype.h>
#include <float.h>
#include <assert.h>

#include <fourcc.h>

#include <kva.h>

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

#define KVA_FIXT23_TEXT N_( \
    "Enable a workaround for T23" )
#define KVA_FIXT23_LONGTEXT N_( \
    "Enable this option if the diagonal stripes are displayed " \
    "when the window size is equal to or smaller than the movie size." )
#define KVA_VIDEO_MODE_TEXT N_( \
    "Video mode" )
#define KVA_VIDEO_MODE_LONGTEXT N_( \
    "Select a proper video mode to be used by KVA." )

static const char *const ppsz_kva_video_mode[] = {
    "auto", "snap", "wo", "vman", "dive" };
static const char *const ppsz_kva_video_mode_text[] = {
    N_("Auto"), N_("SNAP"), N_("WarpOverlay!"), N_("VMAN"), N_("DIVE") };

vlc_module_begin ()
    set_shortname( "KVA" )
    set_category( CAT_VIDEO )
    set_subcategory( SUBCAT_VIDEO_VOUT )
    add_string( "kva-video-mode", ppsz_kva_video_mode[0], KVA_VIDEO_MODE_TEXT,
                KVA_VIDEO_MODE_LONGTEXT, false )
        change_string_list( ppsz_kva_video_mode, ppsz_kva_video_mode_text )
    add_bool( "kva-fixt23", false, KVA_FIXT23_TEXT, KVA_FIXT23_LONGTEXT, true )
    set_description( N_("K Video Acceleration video output") )
    set_capability( "vout display", 100 )
    add_shortcut( "kva" )
    set_callbacks( Open, Close )
vlc_module_end ()

/*****************************************************************************
 * vout_display_sys_t: video output method descriptor
 *****************************************************************************
 * This structure is part of the video output thread descriptor.
 * It describes the module specific properties of an output thread.
 *****************************************************************************/
struct vout_display_sys_t
{
    TID                tid;
    HEV                ack_event;
    int                i_result;
    HAB                hab;
    HMQ                hmq;
    HWND               frame;
    HWND               client;
    KVASETUP           kvas;
    KVACAPS            kvac;
    LONG               i_screen_width;
    LONG               i_screen_height;
    bool               b_fixt23;
    PFNWP              p_old_frame;
    RECTL              client_rect;
    vout_window_t     *parent_window;
    HWND               parent;
    RECTL              parent_rect;
    picture_pool_t    *pool;
    unsigned           button_pressed;
    bool               is_mouse_hidden;
    bool               is_on_top;
};

struct picture_sys_t
{
    int i_chroma_shift;
};

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static picture_pool_t *Pool   (vout_display_t *, unsigned);
static void            Display(vout_display_t *, picture_t *, subpicture_t * );
static int             Control(vout_display_t *, int, va_list);
static void            Manage (vout_display_t *);

static int  OpenDisplay ( vout_display_t *, video_format_t * );
static void CloseDisplay( vout_display_t * );

static int  KVALock( picture_t * );
static void KVAUnlock( picture_t * );

static void             MorphToPM     ( void );
static int              ConvertKey    ( USHORT );
static MRESULT EXPENTRY MyFrameWndProc( HWND, ULONG, MPARAM, MPARAM );
static MRESULT EXPENTRY WndProc       ( HWND, ULONG, MPARAM, MPARAM );

#define WC_VLC_KVA "WC_VLC_KVA"

#define COLOR_KEY 0x0F0F0F

#define WM_VLC_MANAGE               ( WM_USER + 1 )
#define WM_VLC_FULLSCREEN_CHANGE    ( WM_USER + 2 )
#define WM_VLC_SIZE_CHANGE          ( WM_USER + 3 )

static const char *psz_video_mode[ 4 ] = {"DIVE", "WarpOverlay!", "SNAP",
                                          "VMAN"};

static void PMThread( void *arg )
{
    vout_display_t *vd = ( vout_display_t * )arg;
    vout_display_sys_t * sys = vd->sys;
    ULONG i_frame_flags;
    QMSG qm;
    char *psz_mode;
    ULONG i_kva_mode;

    /* */
    video_format_t fmt;
    video_format_ApplyRotation(&fmt, &vd->fmt);

    /* */
    vout_display_info_t info = vd->info;
    info.is_slow = false;
    info.has_double_click = true;
    info.has_hide_mouse = false;
    info.has_pictures_invalid = false;

    MorphToPM();

    sys->hab = WinInitialize( 0 );
    sys->hmq = WinCreateMsgQueue( sys->hab, 0);

    WinRegisterClass( sys->hab,
                      WC_VLC_KVA,
                      WndProc,
                      CS_SIZEREDRAW | CS_MOVENOTIFY,
                      sizeof( PVOID ));

    sys->b_fixt23 = var_CreateGetBool( vd, "kva-fixt23");

    if( !sys->b_fixt23 )
    {
        vout_window_cfg_t wnd_cfg;

        wnd_cfg.is_standalone = false;
        wnd_cfg.type          = VOUT_WINDOW_TYPE_HWND;
        wnd_cfg.x             = var_InheritInteger(vd, "video-x");
        wnd_cfg.y             = var_InheritInteger(vd, "video-y");
        wnd_cfg.width         = vd->cfg->display.width;
        wnd_cfg.height        = vd->cfg->display.height;

        /* If an external window was specified, we'll draw in it. */
        sys->parent_window =
            vout_display_NewWindow( vd, &wnd_cfg );
    }

    if( sys->parent_window )
    {
        sys->parent = ( HWND )sys->parent_window->handle.hwnd;

        ULONG i_style = WinQueryWindowULong( sys->parent, QWL_STYLE );
        WinSetWindowULong( sys->parent, QWL_STYLE,
                           i_style | WS_CLIPCHILDREN );

        i_frame_flags = FCF_TITLEBAR;
    }
    else
    {
        sys->parent = HWND_DESKTOP;

        i_frame_flags = FCF_SYSMENU    | FCF_TITLEBAR | FCF_MINMAX |
                        FCF_SIZEBORDER | FCF_TASKLIST;
    }

    sys->frame =
        WinCreateStdWindow( sys->parent,      /* parent window handle */
                            WS_VISIBLE,       /* frame window style */
                            &i_frame_flags,   /* window style */
                            WC_VLC_KVA,       /* class name */
                            "",               /* window title */
                            0L,               /* default client style */
                            NULLHANDLE,       /* resource in exe file */
                            1,                /* frame window id */
                            &sys->client );   /* client window handle */

    if( sys->frame == NULLHANDLE )
    {
        msg_Err( vd, "cannot create a frame window");

        goto exit_frame;
    }

    WinSetWindowPtr( sys->client, 0, vd );

    if( !sys->parent_window )
    {
        WinSetWindowPtr( sys->frame, 0, vd );
        sys->p_old_frame = WinSubclassWindow( sys->frame, MyFrameWndProc );
    }

    psz_mode = var_CreateGetString( vd, "kva-video-mode" );

    i_kva_mode = KVAM_AUTO;
    if( strcmp( psz_mode, "snap" ) == 0 )
        i_kva_mode = KVAM_SNAP;
    else if( strcmp( psz_mode, "wo" ) == 0 )
        i_kva_mode = KVAM_WO;
    else if( strcmp( psz_mode, "vman" ) == 0 )
        i_kva_mode = KVAM_VMAN;
    else if( strcmp( psz_mode, "dive" ) == 0 )
        i_kva_mode = KVAM_DIVE;

    free( psz_mode );

    if( kvaInit( i_kva_mode, sys->client, COLOR_KEY ))
    {
        msg_Err( vd, "cannot initialize KVA");

        goto exit_kva_init;
    }

    kvaCaps( &sys->kvac );

    msg_Dbg( vd, "selected video mode = %s",
             psz_video_mode[ sys->kvac.ulMode - 1 ]);

    if( OpenDisplay( vd, &fmt ) )
    {
        msg_Err( vd, "cannot open display");

        goto exit_open_display;
    }

    if( vd->cfg->is_fullscreen )
    {
        if( sys->parent_window )
            vout_window_SetFullScreen(sys->parent_window, true);
        else
            WinPostMsg( sys->client, WM_VLC_FULLSCREEN_CHANGE,
                        MPFROMLONG( true ), 0 );
    }

    kvaDisableScreenSaver();

    /* Setup vout_display now that everything is fine */
    vd->fmt     = fmt;
    vd->info    = info;

    vd->pool    = Pool;
    vd->prepare = NULL;
    vd->display = Display;
    vd->control = Control;
    vd->manage  = Manage;

    /* Prevent SIG_FPE */
    _control87(MCW_EM, MCW_EM);

    sys->i_result = VLC_SUCCESS;
    DosPostEventSem( sys->ack_event );

    if( !sys->parent_window )
        WinSetVisibleRegionNotify( sys->frame, TRUE );

    while( WinGetMsg( sys->hab, &qm, NULLHANDLE, 0, 0 ))
        WinDispatchMsg( sys->hab, &qm );

    if( !sys->parent_window )
        WinSetVisibleRegionNotify( sys->frame, FALSE );

    kvaEnableScreenSaver();

    CloseDisplay( vd );

    /* fall through */

exit_open_display :
    kvaDone();

exit_kva_init :
    if( !sys->parent_window )
        WinSubclassWindow( sys->frame, sys->p_old_frame );

    WinDestroyWindow( sys->frame );

exit_frame :
    vout_display_DeleteWindow( vd, sys->parent_window );

    if( sys->is_mouse_hidden )
        WinShowPointer( HWND_DESKTOP, TRUE );

    WinDestroyMsgQueue( sys->hmq );
    WinTerminate( sys->hab );

    sys->i_result = VLC_EGENERIC;
    DosPostEventSem( sys->ack_event );
}

/**
 * This function initializes KVA vout method.
 */
static int Open ( vlc_object_t *object )
{
    vout_display_t *vd = (vout_display_t *)object;
    vout_display_sys_t *sys;

    vd->sys = sys = calloc( 1, sizeof( *sys ));
    if( !sys )
        return VLC_ENOMEM;

    DosCreateEventSem( NULL, &sys->ack_event, 0, FALSE );

    sys->tid = _beginthread( PMThread, NULL, 1024 * 1024, vd );
    DosWaitEventSem( sys->ack_event, SEM_INDEFINITE_WAIT );

    if( sys->i_result != VLC_SUCCESS )
    {
        DosCloseEventSem( sys->ack_event );

        free( sys );

        return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close: destroy KVA video thread output method
 *****************************************************************************
 * Terminate an output method created by Open
 *****************************************************************************/
static void Close ( vlc_object_t *object )
{
    vout_display_t * vd = (vout_display_t *)object;
    vout_display_sys_t * sys = vd->sys;

    WinPostQueueMsg( sys->hmq, WM_QUIT, 0, 0 );

    DosWaitThread( &sys->tid, DCWW_WAIT );

    if( sys->pool )
        picture_pool_Delete( sys->pool );

    DosCloseEventSem( sys->ack_event );

    free( sys );
}

/**
 * Return a pool of direct buffers
 */
static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
{
    vout_display_sys_t *sys = vd->sys;
    VLC_UNUSED(count);

    return sys->pool;
}

/*****************************************************************************
 * Display: displays previously rendered output
 *****************************************************************************
 * This function sends the currently rendered image to the display.
 *****************************************************************************/
static void Display( vout_display_t *vd, picture_t *picture,
                     subpicture_t *subpicture )
{
    VLC_UNUSED( vd );
    VLC_UNUSED( subpicture );

    picture_Release( picture );
}

/*****************************************************************************
 * Manage: handle Sys events
 *****************************************************************************
 * This function should be called regularly by video output thread. It returns
 * a non null value if an error occurred.
 *****************************************************************************/
static void Manage( vout_display_t *vd )
{
    vout_display_sys_t * sys = vd->sys;

    /* Let a window procedure manage instead because if resizing a frame window
     * here, WM_SIZE is not sent to its child window.
     * Maybe, is this due to the different threads ? */
    WinPostMsg( sys->client, WM_VLC_MANAGE, 0, 0 );
}

/*****************************************************************************
 * Control: control facility for the vout
 *****************************************************************************/
static int Control( vout_display_t *vd, int query, va_list args )
{
    vout_display_sys_t *sys = vd->sys;

    switch (query)
    {
    case VOUT_DISPLAY_HIDE_MOUSE:
    {
        POINTL ptl;

        WinQueryPointerPos( HWND_DESKTOP, &ptl );
        if( !sys->is_mouse_hidden &&
            WinWindowFromPoint( HWND_DESKTOP, &ptl, TRUE ) == sys->client )
        {
            WinShowPointer( HWND_DESKTOP, FALSE );
            sys->is_mouse_hidden = true;
        }

        return VLC_SUCCESS;
    }

    case VOUT_DISPLAY_CHANGE_FULLSCREEN:
    {
        vout_display_cfg_t cfg = *va_arg(args, const vout_display_cfg_t *);

        if( sys->parent_window )
            vout_window_SetFullScreen(sys->parent_window, cfg.is_fullscreen);
        else
            WinPostMsg( sys->client, WM_VLC_FULLSCREEN_CHANGE,
                        MPFROMLONG( cfg.is_fullscreen ), 0 );

        return VLC_SUCCESS;
    }

    case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
    {
        const unsigned state = va_arg( args, unsigned );
        const bool is_on_top = (state & VOUT_WINDOW_STATE_ABOVE) != 0;

        if( sys->parent_window )
        {
            if( vout_window_SetState( sys->parent_window, state ))
                return VLC_EGENERIC;
        }
        else if( is_on_top )
            WinSetWindowPos( sys->frame, HWND_TOP, 0, 0, 0, 0, SWP_ZORDER );

        sys->is_on_top = is_on_top;

        return VLC_SUCCESS;
    }

    case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
    case VOUT_DISPLAY_CHANGE_ZOOM:
    {
        const vout_display_cfg_t *cfg = va_arg(args, const vout_display_cfg_t *);
        bool  is_forced = query == VOUT_DISPLAY_CHANGE_ZOOM ||
                          va_arg(args, int);

        if( is_forced )
        {
            if( sys->parent_window )
            {
                vout_window_SetSize(sys->parent_window,
                                    cfg->display.width, cfg->display.height);
            }
            else
                WinPostMsg( sys->client, WM_VLC_SIZE_CHANGE,
                            MPFROMLONG( cfg->display.width ),
                            MPFROMLONG( cfg->display.height ));
        }

        return VLC_SUCCESS;
    }

    case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
    case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
    {
        const video_format_t *src = va_arg(args, const video_format_t *);

        if( query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT )
        {
            vout_display_place_t place;
            vout_display_PlacePicture(&place, src, vd->cfg, false);

            sys->kvas.ulAspectWidth  = place.width;
            sys->kvas.ulAspectHeight = place.height;
        }
        else
        {
            video_format_t src_rot;
            video_format_ApplyRotation(&src_rot, src);

            sys->kvas.rclSrcRect.xLeft   = src_rot.i_x_offset;
            sys->kvas.rclSrcRect.yTop    = src_rot.i_y_offset;
            sys->kvas.rclSrcRect.xRight  = src_rot.i_x_offset +
                                           src_rot.i_visible_width;
            sys->kvas.rclSrcRect.yBottom = src_rot.i_y_offset +
                                           src_rot.i_visible_height;
        }

        kvaSetup( &sys->kvas );

        return VLC_SUCCESS;
    }

    case VOUT_DISPLAY_RESET_PICTURES:
    case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
    case VOUT_DISPLAY_GET_OPENGL:
        /* TODO */
        break;
    }

    msg_Err(vd, "Unsupported query(=%d) in vout display KVA", query);
    return VLC_EGENERIC;
}

/* following functions are local */

/*****************************************************************************
 * OpenDisplay: open and initialize KVA device
 *****************************************************************************
 * Open and initialize display according to preferences specified in the vout
 * thread fields.
 *****************************************************************************/
static int OpenDisplay( vout_display_t *vd, video_format_t *fmt )
{
    vout_display_sys_t * sys = vd->sys;
    const vlc_fourcc_t *fallback;
    bool b_hw_accel = 0;
    FOURCC i_kva_fourcc;
    int i_chroma_shift;
    char sz_title[ 256 ];
    RECTL rcl;
    int w, h;

    msg_Dbg( vd, "render chroma = %4.4s", ( const char * )&fmt->i_chroma );

    for( int pass = 0; pass < 2 && !b_hw_accel; pass++ )
    {
        fallback = ( pass == 0 ) ? vlc_fourcc_GetYUVFallback( fmt->i_chroma ) :
                                   vlc_fourcc_GetRGBFallback( fmt->i_chroma );

        for( int i = 0; fallback[ i ]; i++ )
        {
            switch( fallback[ i ])
            {
                case VLC_CODEC_YV12:
                    b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_YV12;
                    i_kva_fourcc = FOURCC_YV12;
                    i_chroma_shift = 1;
                    break;

                case VLC_CODEC_YUYV:
                    b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_YUY2;
                    i_kva_fourcc = FOURCC_Y422;
                    i_chroma_shift = 0;
                    break;

                case VLC_CODEC_YV9:
                    b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_YVU9;
                    i_kva_fourcc = FOURCC_YVU9;
                    i_chroma_shift = 2;
                    break;

                case VLC_CODEC_RGB32:
                    b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_BGR32;
                    i_kva_fourcc = FOURCC_BGR4;
                    i_chroma_shift = 0;
                    break;

                case VLC_CODEC_RGB24:
                    b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_BGR24;
                    i_kva_fourcc = FOURCC_BGR3;
                    i_chroma_shift = 0;
                    break;

                case VLC_CODEC_RGB16:
                    b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_BGR16;
                    i_kva_fourcc = FOURCC_R565;
                    i_chroma_shift = 0;
                    break;

                case VLC_CODEC_RGB15:
                    b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_BGR15;
                    i_kva_fourcc = FOURCC_R555;
                    i_chroma_shift = 0;
                    break;
            }

            if( b_hw_accel )
            {
                fmt->i_chroma = fallback[ i ];
                break;
            }
        }
    }

    if( !b_hw_accel )
    {
        msg_Err( vd, "Ooops. There is no fourcc supported by KVA at all.");

        return VLC_EGENERIC;
    }

    /* Set the RGB masks */
    fmt->i_rmask = sys->kvac.ulRMask;
    fmt->i_gmask = sys->kvac.ulGMask;
    fmt->i_bmask = sys->kvac.ulBMask;

    msg_Dbg( vd, "output chroma = %4.4s", ( const char * )&fmt->i_chroma );
    msg_Dbg( vd, "KVA chroma = %4.4s", ( const char * )&i_kva_fourcc );

    w = fmt->i_width;
    h = fmt->i_height;

    sys->kvas.ulLength           = sizeof( KVASETUP );
    sys->kvas.szlSrcSize.cx      = w;
    sys->kvas.szlSrcSize.cy      = h;
    sys->kvas.rclSrcRect.xLeft   = 0;
    sys->kvas.rclSrcRect.yTop    = 0;
    sys->kvas.rclSrcRect.xRight  = w;
    sys->kvas.rclSrcRect.yBottom = h;
    sys->kvas.ulRatio            = KVAR_FORCEANY;
    sys->kvas.ulAspectWidth      = w;
    sys->kvas.ulAspectHeight     = h;
    sys->kvas.fccSrcColor        = i_kva_fourcc;
    sys->kvas.fDither            = TRUE;

    if( kvaSetup( &sys->kvas ))
    {
        msg_Err( vd, "cannot set up KVA");

        return VLC_EGENERIC;
    }

    /* Create the associated picture */
    picture_sys_t *picsys = malloc( sizeof( *picsys ) );
    if( picsys == NULL )
        return VLC_ENOMEM;
    picsys->i_chroma_shift = i_chroma_shift;<commit_msg>kva: set visible region correctly<commit_after>/*****************************************************************************
 * kva.c: KVA video output plugin for vlc
 *****************************************************************************
 * Copyright (C) 2010, 2011, 2012 VLC authors and VideoLAN
 *
 * Authors: <NAME> <<EMAIL>>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_vout_display.h>
#include <vlc_picture_pool.h>

#include <ctype.h>
#include <float.h>
#include <assert.h>

#include <fourcc.h>

#include <kva.h>

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

#define KVA_FIXT23_TEXT N_( \
    "Enable a workaround for T23" )
#define KVA_FIXT23_LONGTEXT N_( \
    "Enable this option if the diagonal stripes are displayed " \
    "when the window size is equal to or smaller than the movie size." )
#define KVA_VIDEO_MODE_TEXT N_( \
    "Video mode" )
#define KVA_VIDEO_MODE_LONGTEXT N_( \
    "Select a proper video mode to be used by KVA." )

static const char *const ppsz_kva_video_mode[] = {
    "auto", "snap", "wo", "vman", "dive" };
static const char *const ppsz_kva_video_mode_text[] = {
    N_("Auto"), N_("SNAP"), N_("WarpOverlay!"), N_("VMAN"), N_("DIVE") };

vlc_module_begin ()
    set_shortname( "KVA" )
    set_category( CAT_VIDEO )
    set_subcategory( SUBCAT_VIDEO_VOUT )
    add_string( "kva-video-mode", ppsz_kva_video_mode[0], KVA_VIDEO_MODE_TEXT,
                KVA_VIDEO_MODE_LONGTEXT, false )
        change_string_list( ppsz_kva_video_mode, ppsz_kva_video_mode_text )
    add_bool( "kva-fixt23", false, KVA_FIXT23_TEXT, KVA_FIXT23_LONGTEXT, true )
    set_description( N_("K Video Acceleration video output") )
    set_capability( "vout display", 100 )
    add_shortcut( "kva" )
    set_callbacks( Open, Close )
vlc_module_end ()

/*****************************************************************************
 * vout_display_sys_t: video output method descriptor
 *****************************************************************************
 * This structure is part of the video output thread descriptor.
 * It describes the module specific properties of an output thread.
 *****************************************************************************/
struct vout_display_sys_t
{
    TID                tid;
    HEV                ack_event;
    int                i_result;
    HAB                hab;
    HMQ                hmq;
    HWND               frame;
    HWND               client;
    KVASETUP           kvas;
    KVACAPS            kvac;
    LONG               i_screen_width;
    LONG               i_screen_height;
    bool               b_fixt23;
    PFNWP              p_old_frame;
    RECTL              client_rect;
    vout_window_t     *parent_window;
    HWND               parent;
    RECTL              parent_rect;
    picture_pool_t    *pool;
    unsigned           button_pressed;
    bool               is_mouse_hidden;
    bool               is_on_top;
};

struct picture_sys_t
{
    int i_chroma_shift;
};

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static picture_pool_t *Pool   (vout_display_t *, unsigned);
static void            Display(vout_display_t *, picture_t *, subpicture_t * );
static int             Control(vout_display_t *, int, va_list);
static void            Manage (vout_display_t *);

static int  OpenDisplay ( vout_display_t *, video_format_t * );
static void CloseDisplay( vout_display_t * );

static int  KVALock( picture_t * );
static void KVAUnlock( picture_t * );

static void             MorphToPM     ( void );
static int              ConvertKey    ( USHORT );
static MRESULT EXPENTRY MyFrameWndProc( HWND, ULONG, MPARAM, MPARAM );
static MRESULT EXPENTRY WndProc       ( HWND, ULONG, MPARAM, MPARAM );

#define WC_VLC_KVA "WC_VLC_KVA"

#define COLOR_KEY 0x0F0F0F

#define WM_VLC_MANAGE               ( WM_USER + 1 )
#define WM_VLC_FULLSCREEN_CHANGE    ( WM_USER + 2 )
#define WM_VLC_SIZE_CHANGE          ( WM_USER + 3 )

static const char *psz_video_mode[ 4 ] = {"DIVE", "WarpOverlay!", "SNAP",
                                          "VMAN"};

static void PMThread( void *arg )
{
    vout_display_t *vd = ( vout_display_t * )arg;
    vout_display_sys_t * sys = vd->sys;
    ULONG i_frame_flags;
    QMSG qm;
    char *psz_mode;
    ULONG i_kva_mode;

    /* */
    video_format_t fmt;
    video_format_ApplyRotation(&fmt, &vd->fmt);

    /* */
    vout_display_info_t info = vd->info;
    info.is_slow = false;
    info.has_double_click = true;
    info.has_hide_mouse = false;
    info.has_pictures_invalid = false;

    MorphToPM();

    sys->hab = WinInitialize( 0 );
    sys->hmq = WinCreateMsgQueue( sys->hab, 0);

    WinRegisterClass( sys->hab,
                      WC_VLC_KVA,
                      WndProc,
                      CS_SIZEREDRAW | CS_MOVENOTIFY,
                      sizeof( PVOID ));

    sys->b_fixt23 = var_CreateGetBool( vd, "kva-fixt23");

    if( !sys->b_fixt23 )
    {
        vout_window_cfg_t wnd_cfg;

        wnd_cfg.is_standalone = false;
        wnd_cfg.type          = VOUT_WINDOW_TYPE_HWND;
        wnd_cfg.x             = var_InheritInteger(vd, "video-x");
        wnd_cfg.y             = var_InheritInteger(vd, "video-y");
        wnd_cfg.width         = vd->cfg->display.width;
        wnd_cfg.height        = vd->cfg->display.height;

        /* If an external window was specified, we'll draw in it. */
        sys->parent_window =
            vout_display_NewWindow( vd, &wnd_cfg );
    }

    if( sys->parent_window )
    {
        sys->parent = ( HWND )sys->parent_window->handle.hwnd;

        ULONG i_style = WinQueryWindowULong( sys->parent, QWL_STYLE );
        WinSetWindowULong( sys->parent, QWL_STYLE,
                           i_style | WS_CLIPCHILDREN );

        i_frame_flags = FCF_TITLEBAR;
    }
    else
    {
        sys->parent = HWND_DESKTOP;

        i_frame_flags = FCF_SYSMENU    | FCF_TITLEBAR | FCF_MINMAX |
                        FCF_SIZEBORDER | FCF_TASKLIST;
    }

    sys->frame =
        WinCreateStdWindow( sys->parent,      /* parent window handle */
                            WS_VISIBLE,       /* frame window style */
                            &i_frame_flags,   /* window style */
                            WC_VLC_KVA,       /* class name */
                            "",               /* window title */
                            0L,               /* default client style */
                            NULLHANDLE,       /* resource in exe file */
                            1,                /* frame window id */
                            &sys->client );   /* client window handle */

    if( sys->frame == NULLHANDLE )
    {
        msg_Err( vd, "cannot create a frame window");

        goto exit_frame;
    }

    WinSetWindowPtr( sys->client, 0, vd );

    if( !sys->parent_window )
    {
        WinSetWindowPtr( sys->frame, 0, vd );
        sys->p_old_frame = WinSubclassWindow( sys->frame, MyFrameWndProc );
    }

    psz_mode = var_CreateGetString( vd, "kva-video-mode" );

    i_kva_mode = KVAM_AUTO;
    if( strcmp( psz_mode, "snap" ) == 0 )
        i_kva_mode = KVAM_SNAP;
    else if( strcmp( psz_mode, "wo" ) == 0 )
        i_kva_mode = KVAM_WO;
    else if( strcmp( psz_mode, "vman" ) == 0 )
        i_kva_mode = KVAM_VMAN;
    else if( strcmp( psz_mode, "dive" ) == 0 )
        i_kva_mode = KVAM_DIVE;

    free( psz_mode );

    if( kvaInit( i_kva_mode, sys->client, COLOR_KEY ))
    {
        msg_Err( vd, "cannot initialize KVA");

        goto exit_kva_init;
    }

    kvaCaps( &sys->kvac );

    msg_Dbg( vd, "selected video mode = %s",
             psz_video_mode[ sys->kvac.ulMode - 1 ]);

    if( OpenDisplay( vd, &fmt ) )
    {
        msg_Err( vd, "cannot open display");

        goto exit_open_display;
    }

    if( vd->cfg->is_fullscreen )
    {
        if( sys->parent_window )
            vout_window_SetFullScreen(sys->parent_window, true);
        else
            WinPostMsg( sys->client, WM_VLC_FULLSCREEN_CHANGE,
                        MPFROMLONG( true ), 0 );
    }

    kvaDisableScreenSaver();

    /* Setup vout_display now that everything is fine */
    vd->fmt     = fmt;
    vd->info    = info;

    vd->pool    = Pool;
    vd->prepare = NULL;
    vd->display = Display;
    vd->control = Control;
    vd->manage  = Manage;

    /* Prevent SIG_FPE */
    _control87(MCW_EM, MCW_EM);

    sys->i_result = VLC_SUCCESS;
    DosPostEventSem( sys->ack_event );

    if( !sys->parent_window )
        WinSetVisibleRegionNotify( sys->frame, TRUE );

    while( WinGetMsg( sys->hab, &qm, NULLHANDLE, 0, 0 ))
        WinDispatchMsg( sys->hab, &qm );

    if( !sys->parent_window )
        WinSetVisibleRegionNotify( sys->frame, FALSE );

    kvaEnableScreenSaver();

    CloseDisplay( vd );

    /* fall through */

exit_open_display :
    kvaDone();

exit_kva_init :
    if( !sys->parent_window )
        WinSubclassWindow( sys->frame, sys->p_old_frame );

    WinDestroyWindow( sys->frame );

exit_frame :
    vout_display_DeleteWindow( vd, sys->parent_window );

    if( sys->is_mouse_hidden )
        WinShowPointer( HWND_DESKTOP, TRUE );

    WinDestroyMsgQueue( sys->hmq );
    WinTerminate( sys->hab );

    sys->i_result = VLC_EGENERIC;
    DosPostEventSem( sys->ack_event );
}

/**
 * This function initializes KVA vout method.
 */
static int Open ( vlc_object_t *object )
{
    vout_display_t *vd = (vout_display_t *)object;
    vout_display_sys_t *sys;

    vd->sys = sys = calloc( 1, sizeof( *sys ));
    if( !sys )
        return VLC_ENOMEM;

    DosCreateEventSem( NULL, &sys->ack_event, 0, FALSE );

    sys->tid = _beginthread( PMThread, NULL, 1024 * 1024, vd );
    DosWaitEventSem( sys->ack_event, SEM_INDEFINITE_WAIT );

    if( sys->i_result != VLC_SUCCESS )
    {
        DosCloseEventSem( sys->ack_event );

        free( sys );

        return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close: destroy KVA video thread output method
 *****************************************************************************
 * Terminate an output method created by Open
 *****************************************************************************/
static void Close ( vlc_object_t *object )
{
    vout_display_t * vd = (vout_display_t *)object;
    vout_display_sys_t * sys = vd->sys;

    WinPostQueueMsg( sys->hmq, WM_QUIT, 0, 0 );

    DosWaitThread( &sys->tid, DCWW_WAIT );

    if( sys->pool )
        picture_pool_Delete( sys->pool );

    DosCloseEventSem( sys->ack_event );

    free( sys );
}

/**
 * Return a pool of direct buffers
 */
static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
{
    vout_display_sys_t *sys = vd->sys;
    VLC_UNUSED(count);

    return sys->pool;
}

/*****************************************************************************
 * Display: displays previously rendered output
 *****************************************************************************
 * This function sends the currently rendered image to the display.
 *****************************************************************************/
static void Display( vout_display_t *vd, picture_t *picture,
                     subpicture_t *subpicture )
{
    VLC_UNUSED( vd );
    VLC_UNUSED( subpicture );

    picture_Release( picture );
}

/*****************************************************************************
 * Manage: handle Sys events
 *****************************************************************************
 * This function should be called regularly by video output thread. It returns
 * a non null value if an error occurred.
 *****************************************************************************/
static void Manage( vout_display_t *vd )
{
    vout_display_sys_t * sys = vd->sys;

    /* Let a window procedure manage instead because if resizing a frame window
     * here, WM_SIZE is not sent to its child window.
     * Maybe, is this due to the different threads ? */
    WinPostMsg( sys->client, WM_VLC_MANAGE, 0, 0 );
}

/*****************************************************************************
 * Control: control facility for the vout
 *****************************************************************************/
static int Control( vout_display_t *vd, int query, va_list args )
{
    vout_display_sys_t *sys = vd->sys;

    switch (query)
    {
    case VOUT_DISPLAY_HIDE_MOUSE:
    {
        POINTL ptl;

        WinQueryPointerPos( HWND_DESKTOP, &ptl );
        if( !sys->is_mouse_hidden &&
            WinWindowFromPoint( HWND_DESKTOP, &ptl, TRUE ) == sys->client )
        {
            WinShowPointer( HWND_DESKTOP, FALSE );
            sys->is_mouse_hidden = true;
        }

        return VLC_SUCCESS;
    }

    case VOUT_DISPLAY_CHANGE_FULLSCREEN:
    {
        vout_display_cfg_t cfg = *va_arg(args, const vout_display_cfg_t *);

        if( sys->parent_window )
            vout_window_SetFullScreen(sys->parent_window, cfg.is_fullscreen);
        else
            WinPostMsg( sys->client, WM_VLC_FULLSCREEN_CHANGE,
                        MPFROMLONG( cfg.is_fullscreen ), 0 );

        return VLC_SUCCESS;
    }

    case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
    {
        const unsigned state = va_arg( args, unsigned );
        const bool is_on_top = (state & VOUT_WINDOW_STATE_ABOVE) != 0;

        if( sys->parent_window )
        {
            if( vout_window_SetState( sys->parent_window, state ))
                return VLC_EGENERIC;
        }
        else if( is_on_top )
            WinSetWindowPos( sys->frame, HWND_TOP, 0, 0, 0, 0, SWP_ZORDER );

        sys->is_on_top = is_on_top;

        return VLC_SUCCESS;
    }

    case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
    case VOUT_DISPLAY_CHANGE_ZOOM:
    {
        const vout_display_cfg_t *cfg = va_arg(args, const vout_display_cfg_t *);
        bool  is_forced = query == VOUT_DISPLAY_CHANGE_ZOOM ||
                          va_arg(args, int);

        if( is_forced )
        {
            if( sys->parent_window )
            {
                vout_window_SetSize(sys->parent_window,
                                    cfg->display.width, cfg->display.height);
            }
            else
                WinPostMsg( sys->client, WM_VLC_SIZE_CHANGE,
                            MPFROMLONG( cfg->display.width ),
                            MPFROMLONG( cfg->display.height ));
        }

        return VLC_SUCCESS;
    }

    case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
    case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
    {
        const video_format_t *src = va_arg(args, const video_format_t *);

        if( query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT )
        {
            vout_display_place_t place;
            vout_display_PlacePicture(&place, src, vd->cfg, false);

            sys->kvas.ulAspectWidth  = place.width;
            sys->kvas.ulAspectHeight = place.height;
        }
        else
        {
            video_format_t src_rot;
            video_format_ApplyRotation(&src_rot, src);

            sys->kvas.rclSrcRect.xLeft   = src_rot.i_x_offset;
            sys->kvas.rclSrcRect.yTop    = src_rot.i_y_offset;
            sys->kvas.rclSrcRect.xRight  = src_rot.i_x_offset +
                                           src_rot.i_visible_width;
            sys->kvas.rclSrcRect.yBottom = src_rot.i_y_offset +
                                           src_rot.i_visible_height;
        }

        kvaSetup( &sys->kvas );

        return VLC_SUCCESS;
    }

    case VOUT_DISPLAY_RESET_PICTURES:
    case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
    case VOUT_DISPLAY_GET_OPENGL:
        /* TODO */
        break;
    }

    msg_Err(vd, "Unsupported query(=%d) in vout display KVA", query);
    return VLC_EGENERIC;
}

/* following functions are local */

/*****************************************************************************
 * OpenDisplay: open and initialize KVA device
 *****************************************************************************
 * Open and initialize display according to preferences specified in the vout
 * thread fields.
 *****************************************************************************/
static int OpenDisplay( vout_display_t *vd, video_format_t *fmt )
{
    vout_display_sys_t * sys = vd->sys;
    const vlc_fourcc_t *fallback;
    bool b_hw_accel = 0;
    FOURCC i_kva_fourcc;
    int i_chroma_shift;
    char sz_title[ 256 ];
    RECTL rcl;
    int w, h;

    msg_Dbg( vd, "render chroma = %4.4s", ( const char * )&fmt->i_chroma );

    for( int pass = 0; pass < 2 && !b_hw_accel; pass++ )
    {
        fallback = ( pass == 0 ) ? vlc_fourcc_GetYUVFallback( fmt->i_chroma ) :
                                   vlc_fourcc_GetRGBFallback( fmt->i_chroma );

        for( int i = 0; fallback[ i ]; i++ )
        {
            switch( fallback[ i ])
            {
                case VLC_CODEC_YV12:
                    b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_YV12;
                    i_kva_fourcc = FOURCC_YV12;
                    i_chroma_shift = 1;
                    break;

                case VLC_CODEC_YUYV:
                    b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_YUY2;
                    i_kva_fourcc = FOURCC_Y422;
                    i_chroma_shift = 0;
                    break;

                case VLC_CODEC_YV9:
                    b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_YVU9;
                    i_kva_fourcc = FOURCC_YVU9;
                    i_chroma_shift = 2;
                    break;

                case VLC_CODEC_RGB32:
                    b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_BGR32;
                    i_kva_fourcc = FOURCC_BGR4;
                    i_chroma_shift = 0;
                    break;

                case VLC_CODEC_RGB24:
                    b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_BGR24;
                    i_kva_fourcc = FOURCC_BGR3;
                    i_chroma_shift = 0;
                    break;

                case VLC_CODEC_RGB16:
                    b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_BGR16;
                    i_kva_fourcc = FOURCC_R565;
                    i_chroma_shift = 0;
                    break;

                case VLC_CODEC_RGB15:
                    b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_BGR15;
                    i_kva_fourcc = FOURCC_R555;
                    i_chroma_shift = 0;
                    break;
            }

            if( b_hw_accel )
            {
                fmt->i_chroma = fallback[ i ];
                break;
            }
        }
    }

    if( !b_hw_accel )
    {
        msg_Err( vd, "Ooops. There is no fourcc supported by KVA at all.");

        return VLC_EGENERIC;
    }

    /* Set the RGB masks */
    fmt->i_rmask = sys->kvac.ulRMask;
    fmt->i_gmask = sys->kvac.ulGMask;
    fmt->i_bmask = sys->kvac.ulBMask;

    msg_Dbg( vd, "output chroma = %4.4s", ( const char * )&fmt->i_chroma );
    msg_Dbg( vd, "KVA chroma = %4.4s", ( const char * )&i_kva_fourcc );

    w = fmt->i_width;
    h = fmt->i_height;

    sys->kvas.ulLength           = sizeof( KVASETUP );
    sys->kvas.szlSrcSize.cx      = w;
    sys->kvas.szlSrcSize.cy      = h;
    sys->kvas.rclSrcRect.xLeft   = fmt->i_x_offset;
    sys->kvas.rclSrcRect.yTop    = fmt->i_y_offset;
    sys->kvas.rclSrcRect.xRight  = fmt->i_x_offset + fmt->i_visible_width;
    sys->kvas.rclSrcRect.yBottom = fmt->i_y_offset + fmt->i_visible_height;
    sys->kvas.ulRatio            = KVAR_FORCEANY;
    sys->kvas.ulAspectWidth      = w;
    sys->kvas.ulAspectHeight     = h;
    sys->kvas.fccSrcColor        = i_kva_fourcc;
    sys->kvas.fDither            = TRUE;

    if( kvaSetup( &sys->kvas ))
    {
        msg_Err( vd, "cannot set up KVA");

        return VLC_EGENERIC;
    }

    /* Create the associated picture */
    picture_sys_t *picsys = malloc( sizeof( *picsys ) );
    if( picsys == NULL )
        return VLC_ENOMEM;
    picsys->i_chroma_shift = i_chroma_shift;
{'fraction_non_alphanumeric': 0.18279822439345186, 'fraction_numerical': 0.0071400051671090025, 'mean_word_length': 2.2937263092751605, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 313, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3681288', 'n_tokens_mistral': 13941, 'n_tokens_neox': 13125, 'n_words': 2895}