directx.c 79.9 KB
Newer Older
1
/*****************************************************************************
gbazin's avatar
 
gbazin committed
2
 * vout.c: Windows DirectX video output display method
3
 *****************************************************************************
gbazin's avatar
 
gbazin committed
4
 * Copyright (C) 2001-2004 VideoLAN
5
 * $Id$
6
 *
gbazin's avatar
gbazin committed
7
 * Authors: Gildas Bazin <gbazin@videolan.org>
gbazin's avatar
 
gbazin committed
8
 *
9 10 11 12
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
gbazin's avatar
 
gbazin committed
13
 *
14 15 16 17 18 19 20 21 22 23 24
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
gbazin's avatar
 
gbazin committed
25 26 27 28 29 30 31 32 33 34
 * Preamble:
 *
 * This plugin will use YUV overlay if supported, using overlay will result in
 * the best video quality (hardware interpolation when rescaling the picture)
 * and the fastest display as it requires less processing.
 *
 * If YUV overlay is not supported this plugin will use RGB offscreen video
 * surfaces that will be blitted onto the primary surface (display) to
 * effectively display the pictures. This fallback method also enables us to
 * display video in window mode.
35
 *
36
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
37 38 39
#include <errno.h>                                                 /* ENOMEM */
#include <stdlib.h>                                                /* free() */
#include <string.h>                                            /* strerror() */
40 41

#include <vlc/vlc.h>
gbazin's avatar
 
gbazin committed
42 43 44
#include <vlc/intf.h>
#include <vlc/vout.h>

gbazin's avatar
 
gbazin committed
45
#include <windows.h>
gbazin's avatar
 
gbazin committed
46
#include <ddraw.h>
47
#include <commctrl.h>
gbazin's avatar
 
gbazin committed
48

49 50 51
#ifndef UNDER_CE
#   include <multimon.h>
#endif
52 53
#undef GetSystemMetrics

gbazin's avatar
 
gbazin committed
54 55 56 57
#ifndef MONITOR_DEFAULTTONEAREST
#   define MONITOR_DEFAULTTONEAREST 2
#endif

gbazin's avatar
 
gbazin committed
58 59 60 61 62 63 64 65 66 67
#include "vout.h"

/*****************************************************************************
 * DirectDraw GUIDs.
 * Defining them here allows us to get rid of the dxguid library during
 * the linking stage.
 *****************************************************************************/
#include <initguid.h>
DEFINE_GUID( IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 );
DEFINE_GUID( IID_IDirectDrawSurface2, 0x57805885,0x6eec,0x11cf,0x94,0x41,0xa8,0x23,0x03,0xc1,0x0e,0x27 );
68 69

/*****************************************************************************
gbazin's avatar
 
gbazin committed
70
 * Local prototypes.
71
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
72 73
static int  OpenVideo  ( vlc_object_t * );
static void CloseVideo ( vlc_object_t * );
74

gbazin's avatar
 
gbazin committed
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
static int  Init      ( vout_thread_t * );
static void End       ( vout_thread_t * );
static int  Manage    ( vout_thread_t * );
static void Display   ( vout_thread_t *, picture_t * );

static int  NewPictureVec  ( vout_thread_t *, picture_t *, int );
static void FreePictureVec ( vout_thread_t *, picture_t *, int );
static int  UpdatePictureStruct( vout_thread_t *, picture_t *, int );

static int  DirectXInitDDraw      ( vout_thread_t *p_vout );
static void DirectXCloseDDraw     ( vout_thread_t *p_vout );
static int  DirectXCreateDisplay  ( vout_thread_t *p_vout );
static void DirectXCloseDisplay   ( vout_thread_t *p_vout );
static int  DirectXCreateSurface  ( vout_thread_t *p_vout,
                                    LPDIRECTDRAWSURFACE2 *, int, int, int );
static void DirectXCloseSurface   ( vout_thread_t *p_vout,
                                    LPDIRECTDRAWSURFACE2 );
static int  DirectXCreateClipper  ( vout_thread_t *p_vout );
static void DirectXGetDDrawCaps   ( vout_thread_t *p_vout );
gbazin's avatar
 
gbazin committed
94 95
static int  DirectXLockSurface    ( vout_thread_t *p_vout, picture_t *p_pic );
static int  DirectXUnlockSurface  ( vout_thread_t *p_vout, picture_t *p_pic );
96

97
static DWORD DirectXFindColorkey( vout_thread_t *p_vout, uint32_t *i_color );
98

99 100
void SwitchWallpaperMode( vout_thread_t *, vlc_bool_t );

gbazin's avatar
 
gbazin committed
101
/* Object variables callbacks */
gbazin's avatar
 
gbazin committed
102 103
static int FindDevicesCallback( vlc_object_t *, char const *,
                                vlc_value_t, vlc_value_t, void * );
104 105
static int WallpaperCallback( vlc_object_t *, char const *,
                              vlc_value_t, vlc_value_t, void * );
gbazin's avatar
 
gbazin committed
106

107 108 109
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Christophe Massiot's avatar
Christophe Massiot committed
110
#define HW_YUV_TEXT N_("Use hardware YUV->RGB conversions")
111 112 113
#define HW_YUV_LONGTEXT N_( \
    "Try to use hardware acceleration for YUV->RGB conversions. " \
    "This option doesn't have any effect when using overlays." )
zorglub's avatar
zorglub committed
114

Christophe Massiot's avatar
Christophe Massiot committed
115
#define SYSMEM_TEXT N_("Use video buffers in system memory")
116 117 118 119 120
#define SYSMEM_LONGTEXT N_( \
    "Create video buffers in system memory instead of video memory. This " \
    "isn't recommended as usually using video memory allows to benefit from " \
    "more hardware acceleration (like rescaling or YUV->RGB conversions). " \
    "This option doesn't have any effect when using overlays." )
zorglub's avatar
zorglub committed
121

Christophe Massiot's avatar
Christophe Massiot committed
122
#define TRIPLEBUF_TEXT N_("Use triple buffering for overlays")
gbazin's avatar
 
gbazin committed
123
#define TRIPLEBUF_LONGTEXT N_( \
124
    "Try to use triple buffering when using YUV overlays. That results in " \
gbazin's avatar
 
gbazin committed
125
    "much better video quality (no flickering)." )
zorglub's avatar
zorglub committed
126

gbazin's avatar
 
gbazin committed
127
#define DEVICE_TEXT N_("Name of desired display device")
128 129 130 131
#define DEVICE_LONGTEXT N_("In a multiple monitor configuration, you can " \
    "specify the Windows device name of the display that you want the video " \
    "window to open on. For example, \"\\\\.\\DISPLAY1\" or " \
    "\"\\\\.\\DISPLAY2\"." )
gbazin's avatar
 
gbazin committed
132

133 134 135 136 137 138
#define WALLPAPER_TEXT N_("Enable wallpaper mode ")
#define WALLPAPER_LONGTEXT N_( \
    "The wallpaper mode allows you to display the video as the desktop " \
    "background. Note that this feature only works in overlay mode and " \
    "the desktop must not already have a wallpaper." )

gbazin's avatar
 
gbazin committed
139 140
static char *ppsz_dev[] = { "" };
static char *ppsz_dev_text[] = { N_("Default") };
141 142

vlc_module_begin();
zorglub's avatar
zorglub committed
143 144
    set_category( CAT_VIDEO );
    set_subcategory( SUBCAT_VIDEO_VOUT );
gbazin's avatar
 
gbazin committed
145 146 147 148 149 150 151 152 153 154
    add_bool( "directx-hw-yuv", 1, NULL, HW_YUV_TEXT, HW_YUV_LONGTEXT,
              VLC_TRUE );
    add_bool( "directx-use-sysmem", 0, NULL, SYSMEM_TEXT, SYSMEM_LONGTEXT,
              VLC_TRUE );
    add_bool( "directx-3buffering", 1, NULL, TRIPLEBUF_TEXT,
              TRIPLEBUF_LONGTEXT, VLC_TRUE );

    add_string( "directx-device", "", NULL, DEVICE_TEXT, DEVICE_LONGTEXT,
                VLC_TRUE );
        change_string_list( ppsz_dev, ppsz_dev_text, FindDevicesCallback );
gbazin's avatar
 
gbazin committed
155
        change_action_add( FindDevicesCallback, N_("Refresh list") );
gbazin's avatar
 
gbazin committed
156

157 158 159
    add_bool( "directx-wallpaper", 0, NULL, WALLPAPER_TEXT, WALLPAPER_LONGTEXT,
              VLC_TRUE );

gbazin's avatar
 
gbazin committed
160
    set_description( _("DirectX video output") );
gbazin's avatar
 
gbazin committed
161
    set_capability( "video output", 100 );
162
    add_shortcut( "directx" );
gbazin's avatar
 
gbazin committed
163
    set_callbacks( OpenVideo, CloseVideo );
164 165 166

    /* FIXME: Hack to avoid unregistering our window class */
    linked_with_a_crap_library_which_uses_atexit( );
167 168 169 170 171 172 173 174 175 176
vlc_module_end();

#if 0 /* FIXME */
    /* check if we registered a window class because we need to
     * unregister it */
    WNDCLASS wndclass;
    if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) )
        UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) );
#endif

gbazin's avatar
 
gbazin committed
177 178 179 180 181 182 183 184
/*****************************************************************************
 * OpenVideo: allocate DirectX video thread output method
 *****************************************************************************
 * This function allocates and initialize the DirectX vout method.
 *****************************************************************************/
static int OpenVideo( vlc_object_t *p_this )
{
    vout_thread_t * p_vout = (vout_thread_t *)p_this;
gbazin's avatar
gbazin committed
185
    vlc_value_t val;
186
    HMODULE huser32;
gbazin's avatar
 
gbazin committed
187 188 189 190 191 192

    /* Allocate structure */
    p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
    if( p_vout->p_sys == NULL )
    {
        msg_Err( p_vout, "out of memory" );
193
        return VLC_ENOMEM;
gbazin's avatar
 
gbazin committed
194
    }
195
    memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
gbazin's avatar
 
gbazin committed
196 197 198 199 200 201 202 203 204 205 206 207

    /* Initialisations */
    p_vout->pf_init = Init;
    p_vout->pf_end = End;
    p_vout->pf_manage = Manage;
    p_vout->pf_render = NULL;
    p_vout->pf_display = Display;

    p_vout->p_sys->p_ddobject = NULL;
    p_vout->p_sys->p_display = NULL;
    p_vout->p_sys->p_current_surface = NULL;
    p_vout->p_sys->p_clipper = NULL;
208
    p_vout->p_sys->hwnd = p_vout->p_sys->hvideownd = NULL;
209
    p_vout->p_sys->hparent = p_vout->p_sys->hfswnd = NULL;
gbazin's avatar
 
gbazin committed
210
    p_vout->p_sys->i_changes = 0;
211
    p_vout->p_sys->b_wallpaper = 0;
gbazin's avatar
gbazin committed
212 213 214
    vlc_mutex_init( p_vout, &p_vout->p_sys->lock );
    SetRectEmpty( &p_vout->p_sys->rect_display );
    SetRectEmpty( &p_vout->p_sys->rect_parent );
215 216 217 218 219 220

    /* Multimonitor stuff */
    p_vout->p_sys->hmonitor = NULL;
    p_vout->p_sys->p_display_driver = NULL;
    p_vout->p_sys->MonitorFromWindow = NULL;
    p_vout->p_sys->GetMonitorInfo = NULL;
221
    if( (huser32 = GetModuleHandle( _T("USER32") ) ) )
222
    {
223
        p_vout->p_sys->MonitorFromWindow = (HMONITOR (WINAPI *)( HWND, DWORD ))
224
            GetProcAddress( huser32, _T("MonitorFromWindow") );
225
        p_vout->p_sys->GetMonitorInfo =
226
#ifndef UNICODE
227
            GetProcAddress( huser32, "GetMonitorInfoA" );
228 229 230
#else
            GetProcAddress( huser32, _T("GetMonitorInfoW") );
#endif
231 232 233 234 235 236
    }

    var_Create( p_vout, "overlay", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
    var_Create( p_vout, "directx-use-sysmem", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
    var_Create( p_vout, "directx-hw-yuv", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
    var_Create( p_vout, "directx-3buffering", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
gbazin's avatar
 
gbazin committed
237
    var_Create( p_vout, "directx-device", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
238
    var_Create( p_vout, "video-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
gbazin's avatar
 
gbazin committed
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257

    p_vout->p_sys->b_cursor_hidden = 0;
    p_vout->p_sys->i_lastmoved = mdate();

    /* Set main window's size */
    p_vout->p_sys->i_window_width = p_vout->i_window_width;
    p_vout->p_sys->i_window_height = p_vout->i_window_height;

    /* Create the DirectXEventThread, this thread is created by us to isolate
     * the Win32 PeekMessage function calls. We want to do this because
     * Windows can stay blocked inside this call for a long time, and when
     * this happens it thus blocks vlc's video_output thread.
     * DirectXEventThread will take care of the creation of the video
     * window (because PeekMessage has to be called from the same thread which
     * created the window). */
    msg_Dbg( p_vout, "creating DirectXEventThread" );
    p_vout->p_sys->p_event =
        vlc_object_create( p_vout, sizeof(event_thread_t) );
    p_vout->p_sys->p_event->p_vout = p_vout;
258
    if( vlc_thread_create( p_vout->p_sys->p_event, "DirectX Events Thread",
259
                           E_(DirectXEventThread), 0, 1 ) )
gbazin's avatar
 
gbazin committed
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
    {
        msg_Err( p_vout, "cannot create DirectXEventThread" );
        vlc_object_destroy( p_vout->p_sys->p_event );
        p_vout->p_sys->p_event = NULL;
        goto error;
    }

    if( p_vout->p_sys->p_event->b_error )
    {
        msg_Err( p_vout, "DirectXEventThread failed" );
        goto error;
    }

    vlc_object_attach( p_vout->p_sys->p_event, p_vout );

    msg_Dbg( p_vout, "DirectXEventThread running" );

    /* Initialise DirectDraw */
    if( DirectXInitDDraw( p_vout ) )
    {
        msg_Err( p_vout, "cannot initialize DirectDraw" );
        goto error;
    }

    /* Create the directx display */
    if( DirectXCreateDisplay( p_vout ) )
    {
        msg_Err( p_vout, "cannot initialize DirectDraw" );
        goto error;
    }

291 292
    /* Variable to indicate if the window should be on top of others */
    /* Trigger a callback right now */
gbazin's avatar
 
gbazin committed
293
    var_Get( p_vout, "video-on-top", &val );
294
    var_Set( p_vout, "video-on-top", val );
295

296 297 298 299 300 301 302 303 304
    /* Variable to indicate if the window should be on top of others */
    /* Trigger a callback right now */
    var_Create( p_vout, "directx-wallpaper", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
    val.psz_string = _("Wallpaper");
    var_Change( p_vout, "directx-wallpaper", VLC_VAR_SETTEXT, &val, NULL );
    var_AddCallback( p_vout, "directx-wallpaper", WallpaperCallback, NULL );
    var_Get( p_vout, "directx-wallpaper", &val );
    var_Set( p_vout, "directx-wallpaper", val );

305
    return VLC_SUCCESS;
gbazin's avatar
 
gbazin committed
306 307 308

 error:
    CloseVideo( VLC_OBJECT(p_vout) );
309
    return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
310 311 312 313 314 315 316 317 318 319 320
}

/*****************************************************************************
 * Init: initialize DirectX video thread output method
 *****************************************************************************
 * This function create the directx surfaces needed by the output thread.
 * It is called at the beginning of the thread.
 *****************************************************************************/
static int Init( vout_thread_t *p_vout )
{
    int i_chroma_backup;
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
    vlc_value_t val;

    /* Get a few default parameters */
    var_Get( p_vout, "overlay", &val );
    p_vout->p_sys->b_using_overlay = val.b_bool;
    var_Get( p_vout, "directx-use-sysmem", &val );
    p_vout->p_sys->b_use_sysmem = val.b_bool;
    var_Get( p_vout, "directx-hw-yuv", &val );
    p_vout->p_sys->b_hw_yuv = val.b_bool;
    var_Get( p_vout, "directx-3buffering", &val );
    p_vout->p_sys->b_3buf_overlay = val.b_bool;

    /* Initialise DirectDraw if not already done.
     * We do this here because on multi-monitor systems we may have to
     * re-create the directdraw surfaces. */
    if( !p_vout->p_sys->p_ddobject &&
        DirectXInitDDraw( p_vout ) != VLC_SUCCESS )
    {
        msg_Err( p_vout, "cannot initialize DirectDraw" );
        return VLC_EGENERIC;
    }

    /* Create the directx display */
    if( !p_vout->p_sys->p_display &&
        DirectXCreateDisplay( p_vout ) != VLC_SUCCESS )
    {
        msg_Err( p_vout, "cannot initialize DirectDraw" );
        return VLC_EGENERIC;
    }
gbazin's avatar
 
gbazin committed
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389

    /* Initialize the output structure.
     * Since DirectDraw can do rescaling for us, stick to the default
     * coordinates and aspect. */
    p_vout->output.i_width  = p_vout->render.i_width;
    p_vout->output.i_height = p_vout->render.i_height;
    p_vout->output.i_aspect = p_vout->render.i_aspect;

#define MAX_DIRECTBUFFERS 1
    /* Right now we use only 1 directbuffer because we don't want the
     * video decoder to decode directly into direct buffers as they are
     * created into video memory and video memory is _really_ slow */

    /* Choose the chroma we will try first. */
    switch( p_vout->render.i_chroma )
    {
        case VLC_FOURCC('Y','U','Y','2'):
        case VLC_FOURCC('Y','U','N','V'):
            p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
            break;
        case VLC_FOURCC('U','Y','V','Y'):
        case VLC_FOURCC('U','Y','N','V'):
        case VLC_FOURCC('Y','4','2','2'):
            p_vout->output.i_chroma = VLC_FOURCC('U','Y','V','Y');
            break;
        case VLC_FOURCC('Y','V','Y','U'):
            p_vout->output.i_chroma = VLC_FOURCC('Y','V','Y','U');
            break;
        default:
            p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2');
            break;
    }

    NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS );

    i_chroma_backup = p_vout->output.i_chroma;

    if( !I_OUTPUTPICTURES )
    {
        /* hmmm, it didn't work! Let's try commonly supported chromas */
390
        if( p_vout->output.i_chroma != VLC_FOURCC('I','4','2','0') )
gbazin's avatar
 
gbazin committed
391 392 393 394
        {
            p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2');
            NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS );
        }
gbazin's avatar
 
gbazin committed
395 396
        if( !I_OUTPUTPICTURES )
        {
gbazin's avatar
 
gbazin committed
397
            /* hmmm, it still didn't work! Let's try another one */
gbazin's avatar
 
gbazin committed
398 399 400 401 402 403 404 405 406 407 408 409 410 411
            p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
            NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS );
        }
    }

    if( !I_OUTPUTPICTURES )
    {
        /* If it still didn't work then don't try to use an overlay */
        p_vout->output.i_chroma = i_chroma_backup;
        p_vout->p_sys->b_using_overlay = 0;
        NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS );
    }

    /* Change the window title bar text */
gbazin's avatar
gbazin committed
412
    PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
gbazin's avatar
 
gbazin committed
413

414
    return VLC_SUCCESS;
gbazin's avatar
 
gbazin committed
415 416 417 418 419 420 421 422 423 424 425
}

/*****************************************************************************
 * End: terminate Sys video thread output method
 *****************************************************************************
 * Terminate an output method created by Create.
 * It is called at the end of the thread.
 *****************************************************************************/
static void End( vout_thread_t *p_vout )
{
    FreePictureVec( p_vout, p_vout->p_picture, I_OUTPUTPICTURES );
426 427 428 429

    DirectXCloseDisplay( p_vout );
    DirectXCloseDDraw( p_vout );

gbazin's avatar
 
gbazin committed
430 431 432 433 434 435 436 437 438
    return;
}

/*****************************************************************************
 * CloseVideo: destroy Sys video thread output method
 *****************************************************************************
 * Terminate an output method created by Create
 *****************************************************************************/
static void CloseVideo( vlc_object_t *p_this )
439
{
gbazin's avatar
 
gbazin committed
440
    vout_thread_t * p_vout = (vout_thread_t *)p_this;
441

gbazin's avatar
 
gbazin committed
442 443 444 445 446 447 448
    msg_Dbg( p_vout, "CloseVideo" );

    if( p_vout->p_sys->p_event )
    {
        vlc_object_detach( p_vout->p_sys->p_event );

        /* Kill DirectXEventThread */
449
        p_vout->p_sys->p_event->b_die = VLC_TRUE;
gbazin's avatar
 
gbazin committed
450 451 452 453

        /* we need to be sure DirectXEventThread won't stay stuck in
         * GetMessage, so we send a fake message */
        if( p_vout->p_sys->hwnd )
454
        {
gbazin's avatar
 
gbazin committed
455
            PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0);
456
        }
gbazin's avatar
 
gbazin committed
457 458 459 460 461

        vlc_thread_join( p_vout->p_sys->p_event );
        vlc_object_destroy( p_vout->p_sys->p_event );
    }

gbazin's avatar
gbazin committed
462 463
    vlc_mutex_destroy( &p_vout->p_sys->lock );

464 465 466
    /* Make sure the wallpaper is restored */
    SwitchWallpaperMode( p_vout, VLC_FALSE );

gbazin's avatar
 
gbazin committed
467 468 469 470 471 472 473 474 475 476 477
    if( p_vout->p_sys )
    {
        free( p_vout->p_sys );
        p_vout->p_sys = NULL;
    }
}

/*****************************************************************************
 * Manage: handle Sys events
 *****************************************************************************
 * This function should be called regularly by the video output thread.
Sam Hocevar's avatar
Sam Hocevar committed
478
 * It returns a non null value if an error occurred.
gbazin's avatar
 
gbazin committed
479 480 481 482 483
 *****************************************************************************/
static int Manage( vout_thread_t *p_vout )
{
    WINDOWPLACEMENT window_placement;

484 485
    /* If we do not control our window, we check for geometry changes
     * ourselves because the parent might not send us its events. */
gbazin's avatar
gbazin committed
486
    vlc_mutex_lock( &p_vout->p_sys->lock );
487
    if( p_vout->p_sys->hparent && !p_vout->b_fullscreen )
488
    {
gbazin's avatar
gbazin committed
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
        RECT rect_parent;
        POINT point;

        vlc_mutex_unlock( &p_vout->p_sys->lock );

        GetClientRect( p_vout->p_sys->hparent, &rect_parent );
        point.x = point.y = 0;
        ClientToScreen( p_vout->p_sys->hparent, &point );
        OffsetRect( &rect_parent, point.x, point.y );

        if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) )
        {
            p_vout->p_sys->rect_parent = rect_parent;

            /* This one is to force the update even if only
504
             * the position has changed */
gbazin's avatar
gbazin committed
505 506 507 508 509 510 511 512 513 514 515 516
            SetWindowPos( p_vout->p_sys->hwnd, 0, 1, 1,
                          rect_parent.right - rect_parent.left,
                          rect_parent.bottom - rect_parent.top, 0 );

            SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
                          rect_parent.right - rect_parent.left,
                          rect_parent.bottom - rect_parent.top, 0 );
        }
    }
    else
    {
        vlc_mutex_unlock( &p_vout->p_sys->lock );
517 518
    }

gbazin's avatar
 
gbazin committed
519
    /*
520
     * Position Change
gbazin's avatar
 
gbazin committed
521
     */
522
    if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE )
gbazin's avatar
 
gbazin committed
523
    {
524 525
        p_vout->p_sys->i_changes &= ~DX_POSITION_CHANGE;

526 527 528 529 530 531 532 533 534
        /* Check if we are still on the same monitor */
        if( p_vout->p_sys->MonitorFromWindow &&
            p_vout->p_sys->hmonitor !=
                p_vout->p_sys->MonitorFromWindow( p_vout->p_sys->hwnd,
                                                  MONITOR_DEFAULTTONEAREST ) )
        {
            /* This will force the vout core to recreate the picture buffers */
            p_vout->i_changes |= VOUT_PICTURE_BUFFERS_CHANGE;
        }
gbazin's avatar
 
gbazin committed
535 536
    }

537 538 539 540 541
    /* We used to call the Win32 PeekMessage function here to read the window
     * messages. But since window can stay blocked into this function for a
     * long time (for example when you move your window on the screen), I
     * decided to isolate PeekMessage in another thread. */

542 543 544 545
    if( p_vout->p_sys->i_changes & DX_WALLPAPER_CHANGE )
    {
        SwitchWallpaperMode( p_vout, !p_vout->p_sys->b_wallpaper );
        p_vout->p_sys->i_changes &= ~DX_WALLPAPER_CHANGE;
546
        E_(DirectXUpdateOverlay)( p_vout );
547 548
    }

gbazin's avatar
 
gbazin committed
549 550 551 552 553 554
    /*
     * Fullscreen change
     */
    if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
        || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
    {
gbazin's avatar
 
gbazin committed
555
        vlc_value_t val;
556 557
        HWND hwnd = (p_vout->p_sys->hparent && p_vout->p_sys->hfswnd) ?
            p_vout->p_sys->hfswnd : p_vout->p_sys->hwnd;
gbazin's avatar
 
gbazin committed
558

gbazin's avatar
 
gbazin committed
559 560 561 562
        p_vout->b_fullscreen = ! p_vout->b_fullscreen;

        /* We need to switch between Maximized and Normal sized window */
        window_placement.length = sizeof(WINDOWPLACEMENT);
563
        GetWindowPlacement( hwnd, &window_placement );
gbazin's avatar
 
gbazin committed
564 565
        if( p_vout->b_fullscreen )
        {
566 567 568 569
            /* Change window style, no borders and no title bar */
            int i_style = WS_CLIPCHILDREN | WS_VISIBLE;
            SetWindowLong( hwnd, GWL_STYLE, i_style );

570 571 572 573 574 575 576 577 578 579 580 581 582 583
            if( p_vout->p_sys->hparent )
            {
                /* Retrieve current window position so fullscreen will happen
                 * on the right screen */
                POINT point = {0,0};
                RECT rect;
                ClientToScreen( p_vout->p_sys->hwnd, &point );
                GetClientRect( p_vout->p_sys->hwnd, &rect );
                SetWindowPos( hwnd, 0, point.x, point.y,
                              rect.right, rect.bottom,
                              SWP_NOZORDER|SWP_FRAMECHANGED );
                GetWindowPlacement( hwnd, &window_placement );
            }

584 585 586 587 588 589
            /* Maximize window */
            window_placement.showCmd = SW_SHOWMAXIMIZED;
            SetWindowPlacement( hwnd, &window_placement );
            SetWindowPos( hwnd, 0, 0, 0, 0, 0,
                          SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);

590 591
            if( p_vout->p_sys->hparent )
            {
592 593 594 595 596 597
                RECT rect;
                GetClientRect( hwnd, &rect );
                SetParent( p_vout->p_sys->hwnd, hwnd );
                SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
                              rect.right, rect.bottom,
                              SWP_NOZORDER|SWP_FRAMECHANGED );
598
            }
gbazin's avatar
gbazin committed
599

600
            SetForegroundWindow( hwnd );
gbazin's avatar
 
gbazin committed
601 602 603
        }
        else
        {
604
            /* Change window style, no borders and no title bar */
605
            SetWindowLong( hwnd, GWL_STYLE, p_vout->p_sys->i_window_style );
606 607 608 609 610 611 612

            /* Normal window */
            window_placement.showCmd = SW_SHOWNORMAL;
            SetWindowPlacement( hwnd, &window_placement );
            SetWindowPos( hwnd, 0, 0, 0, 0, 0,
                          SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);

gbazin's avatar
gbazin committed
613 614
            if( p_vout->p_sys->hparent )
            {
615 616
                RECT rect;
                GetClientRect( p_vout->p_sys->hparent, &rect );
gbazin's avatar
gbazin committed
617
                SetParent( p_vout->p_sys->hwnd, p_vout->p_sys->hparent );
618 619 620 621 622
                SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
                              rect.right, rect.bottom,
                              SWP_NOZORDER|SWP_FRAMECHANGED );

                ShowWindow( hwnd, SW_HIDE );
623
                SetForegroundWindow( p_vout->p_sys->hparent );
gbazin's avatar
gbazin committed
624
            }
625 626 627

            /* Make sure the mouse cursor is displayed */
            PostMessage( p_vout->p_sys->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 );
gbazin's avatar
 
gbazin committed
628 629
        }

gbazin's avatar
 
gbazin committed
630 631 632 633
        /* Update the object variable and trigger callback */
        val.b_bool = p_vout->b_fullscreen;
        var_Set( p_vout, "fullscreen", val );

gbazin's avatar
 
gbazin committed
634 635 636 637 638 639 640
        p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
        p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
    }

    /*
     * Pointer change
     */
641 642
    if( p_vout->b_fullscreen && !p_vout->p_sys->b_cursor_hidden &&
        (mdate() - p_vout->p_sys->i_lastmoved) > 5000000 )
gbazin's avatar
 
gbazin committed
643
    {
gbazin's avatar
gbazin committed
644
        POINT point;
645
        HWND hwnd;
gbazin's avatar
gbazin committed
646 647 648

        /* Hide the cursor only if it is inside our window */
        GetCursorPos( &point );
649 650
        hwnd = WindowFromPoint(point);
        if( hwnd == p_vout->p_sys->hwnd || hwnd == p_vout->p_sys->hvideownd )
651 652 653
        {
            PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
        }
gbazin's avatar
gbazin committed
654 655 656 657
        else
        {
            p_vout->p_sys->i_lastmoved = mdate();
        }
gbazin's avatar
 
gbazin committed
658 659
    }

660 661 662
    /*
     * "Always on top" status change
     */
gbazin's avatar
 
gbazin committed
663
    if( p_vout->p_sys->b_on_top_change )
664
    {
gbazin's avatar
 
gbazin committed
665 666 667
        vlc_value_t val;
        HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );

gbazin's avatar
 
gbazin committed
668
        var_Get( p_vout, "video-on-top", &val );
zorglub's avatar
zorglub committed
669

670
        /* Set the window on top if necessary */
gbazin's avatar
 
gbazin committed
671 672
        if( val.b_bool && !( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
                           & WS_EX_TOPMOST ) )
673 674 675
        {
            CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
                           MF_BYCOMMAND | MFS_CHECKED );
gbazin's avatar
 
gbazin committed
676 677
            SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0,
                          SWP_NOSIZE | SWP_NOMOVE );
678
        }
gbazin's avatar
 
gbazin committed
679
        else
680
        /* The window shouldn't be on top */
gbazin's avatar
 
gbazin committed
681 682
        if( !val.b_bool && ( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
                           & WS_EX_TOPMOST ) )
683 684 685
        {
            CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
                           MF_BYCOMMAND | MFS_UNCHECKED );
gbazin's avatar
 
gbazin committed
686
            SetWindowPos( p_vout->p_sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
687 688
                          SWP_NOSIZE | SWP_NOMOVE );
        }
gbazin's avatar
 
gbazin committed
689 690

        p_vout->p_sys->b_on_top_change = VLC_FALSE;
691 692
    }

gbazin's avatar
 
gbazin committed
693 694
    /* Check if the event thread is still running */
    if( p_vout->p_sys->p_event->b_die )
695 696 697
    {
        return VLC_EGENERIC; /* exit */
    }
gbazin's avatar
 
gbazin committed
698

699
    return VLC_SUCCESS;
gbazin's avatar
 
gbazin committed
700 701 702 703 704 705 706 707 708 709 710 711 712 713
}

/*****************************************************************************
 * Display: displays previously rendered output
 *****************************************************************************
 * This function sends the currently rendered image to the display, wait until
 * it is displayed and switch the two rendering buffers, preparing next frame.
 *****************************************************************************/
static void Display( vout_thread_t *p_vout, picture_t *p_pic )
{
    HRESULT dxresult;

    if( (p_vout->p_sys->p_display == NULL) )
    {
zorglub's avatar
zorglub committed
714
        msg_Warn( p_vout, "no display!" );
gbazin's avatar
 
gbazin committed
715 716 717
        return;
    }

gbazin's avatar
 
gbazin committed
718 719 720 721 722 723 724
    /* Our surface can be lost so be sure to check this
     * and restore it if need be */
    if( IDirectDrawSurface2_IsLost( p_vout->p_sys->p_display )
        == DDERR_SURFACELOST )
    {
        if( IDirectDrawSurface2_Restore( p_vout->p_sys->p_display ) == DD_OK &&
            p_vout->p_sys->b_using_overlay )
725
            E_(DirectXUpdateOverlay)( p_vout );
gbazin's avatar
 
gbazin committed
726 727
    }

gbazin's avatar
 
gbazin committed
728 729 730 731 732 733 734 735 736 737
    if( !p_vout->p_sys->b_using_overlay )
    {
        DDBLTFX  ddbltfx;

        /* We ask for the "NOTEARING" option */
        memset( &ddbltfx, 0, sizeof(DDBLTFX) );
        ddbltfx.dwSize = sizeof(DDBLTFX);
        ddbltfx.dwDDFX = DDBLTFX_NOTEARING;

        /* Blit video surface to display */
738 739 740 741 742
        dxresult = IDirectDrawSurface2_Blt( p_vout->p_sys->p_display,
                                            &p_vout->p_sys->rect_dest_clipped,
                                            p_pic->p_sys->p_surface,
                                            &p_vout->p_sys->rect_src_clipped,
                                            DDBLT_ASYNC, &ddbltfx );
gbazin's avatar
 
gbazin committed
743 744
        if( dxresult != DD_OK )
        {
gbazin's avatar
 
gbazin committed
745
            msg_Warn( p_vout, "could not blit surface (error %li)", dxresult );
gbazin's avatar
 
gbazin committed
746 747 748 749 750 751 752 753
            return;
        }

    }
    else /* using overlay */
    {
        /* Flip the overlay buffers if we are using back buffers */
        if( p_pic->p_sys->p_front_surface == p_pic->p_sys->p_surface )
754
        {
gbazin's avatar
 
gbazin committed
755
            return;
756
        }
gbazin's avatar
 
gbazin committed
757 758 759 760

        dxresult = IDirectDrawSurface2_Flip( p_pic->p_sys->p_front_surface,
                                             NULL, DDFLIP_WAIT );
        if( dxresult != DD_OK )
761
        {
gbazin's avatar
 
gbazin committed
762
            msg_Warn( p_vout, "could not flip overlay (error %li)", dxresult );
763
        }
gbazin's avatar
 
gbazin committed
764

gbazin's avatar
 
gbazin committed
765 766
        /* set currently displayed pic */
        p_vout->p_sys->p_current_surface = p_pic->p_sys->p_front_surface;
gbazin's avatar
 
gbazin committed
767

gbazin's avatar
 
gbazin committed
768 769
        /* Lock surface to get all the required info */
        if( DirectXLockSurface( p_vout, p_pic ) )
gbazin's avatar
 
gbazin committed
770 771
        {
            /* AAARRGG */
gbazin's avatar
 
gbazin committed
772
            msg_Warn( p_vout, "cannot lock surface" );
gbazin's avatar
 
gbazin committed
773 774
            return;
        }
gbazin's avatar
 
gbazin committed
775
        DirectXUnlockSurface( p_vout, p_pic );
gbazin's avatar
 
gbazin committed
776 777 778 779 780
    }
}

/* following functions are local */

781 782 783
/*****************************************************************************
 * DirectXEnumCallback: Device enumeration
 *****************************************************************************
zorglub's avatar
zorglub committed
784
 * This callback function is called by DirectDraw once for each
785 786 787 788 789 790 791
 * available DirectDraw device.
 *****************************************************************************/
BOOL WINAPI DirectXEnumCallback( GUID* p_guid, LPTSTR psz_desc,
                                 LPTSTR psz_drivername, VOID* p_context,
                                 HMONITOR hmon )
{
    vout_thread_t *p_vout = (vout_thread_t *)p_context;
gbazin's avatar
 
gbazin committed
792 793
    vlc_value_t device;

794 795
    msg_Dbg( p_vout, "DirectXEnumCallback: %s, %s", psz_desc, psz_drivername );

gbazin's avatar
 
gbazin committed
796
    if( hmon )
797
    {
798 799 800
        var_Get( p_vout, "directx-device", &device );

        if( ( !device.psz_string || !*device.psz_string ) &&
gbazin's avatar
 
gbazin committed
801 802
            hmon == p_vout->p_sys->hmonitor )
        {
803
            if( device.psz_string ) free( device.psz_string );
gbazin's avatar
 
gbazin committed
804 805 806
        }
        else if( strcmp( psz_drivername, device.psz_string ) == 0 )
        {
zorglub's avatar
zorglub committed
807 808
            MONITORINFO monitor_info;
            monitor_info.cbSize = sizeof( MONITORINFO );
gbazin's avatar
 
gbazin committed
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827

            if( p_vout->p_sys->GetMonitorInfo( hmon, &monitor_info ) )
            {
                RECT rect;

                /* Move window to the right screen */
                GetWindowRect( p_vout->p_sys->hwnd, &rect );
                if( !IntersectRect( &rect, &rect, &monitor_info.rcWork ) )
                {
                    rect.left = monitor_info.rcWork.left;
                    rect.top = monitor_info.rcWork.top;
                    msg_Dbg( p_vout, "DirectXEnumCallback: Setting window "
                             "position to %d,%d", rect.left, rect.top );
                    SetWindowPos( p_vout->p_sys->hwnd, NULL,
                                  rect.left, rect.top, 0, 0,
                                  SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
                }
            }

gbazin's avatar
 
gbazin committed
828
            p_vout->p_sys->hmonitor = hmon;
829 830 831 832 833 834
            if( device.psz_string ) free( device.psz_string );
        }
        else
        {
            if( device.psz_string ) free( device.psz_string );
            return TRUE; /* Keep enumerating */
gbazin's avatar
 
gbazin committed
835 836
        }

837 838 839 840 841 842
        msg_Dbg( p_vout, "selecting %s, %s", psz_desc, psz_drivername );
        p_vout->p_sys->p_display_driver = malloc( sizeof(GUID) );
        if( p_vout->p_sys->p_display_driver )
            memcpy( p_vout->p_sys->p_display_driver, p_guid, sizeof(GUID) );
    }

843 844 845
    return TRUE; /* Keep enumerating */
}

gbazin's avatar
 
gbazin committed
846 847 848 849 850 851 852
/*****************************************************************************
 * DirectXInitDDraw: Takes care of all the DirectDraw initialisations
 *****************************************************************************
 * This function initialise and allocate resources for DirectDraw.
 *****************************************************************************/
static int DirectXInitDDraw( vout_thread_t *p_vout )
{
853 854 855 856 857
    HRESULT dxresult;
    HRESULT (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *);
    HRESULT (WINAPI *OurDirectDrawEnumerateEx)( LPDDENUMCALLBACKEXA, LPVOID,
                                                DWORD );
    LPDIRECTDRAW p_ddobject;
gbazin's avatar
 
gbazin committed
858 859 860

    msg_Dbg( p_vout, "DirectXInitDDraw" );

861
    /* Load direct draw DLL */
862
    p_vout->p_sys->hddraw_dll = LoadLibrary(_T("DDRAW.DLL"));
gbazin's avatar
 
gbazin committed
863 864 865 866 867
    if( p_vout->p_sys->hddraw_dll == NULL )
    {
        msg_Warn( p_vout, "DirectXInitDDraw failed loading ddraw.dll" );
        goto error;
    }
868 869

    OurDirectDrawCreate =
870 871
      (void *)GetProcAddress( p_vout->p_sys->hddraw_dll,
                              _T("DirectDrawCreate") );
872
    if( OurDirectDrawCreate == NULL )
gbazin's avatar
 
gbazin committed
873 874 875 876 877
    {
        msg_Err( p_vout, "DirectXInitDDraw failed GetProcAddress" );
        goto error;
    }

878 879
    OurDirectDrawEnumerateEx =
      (void *)GetProcAddress( p_vout->p_sys->hddraw_dll,
880
#ifndef UNICODE
881
                              "DirectDrawEnumerateExA" );
882 883 884
#else
                              _T("DirectDrawEnumerateExW") );
#endif
885

886
    if( OurDirectDrawEnumerateEx && p_vout->p_sys->MonitorFromWindow )
887
    {
gbazin's avatar
 
gbazin committed
888 889 890
        vlc_value_t device;

        var_Get( p_vout, "directx-device", &device );
891 892 893 894 895
        if( device.psz_string )
        {
            msg_Dbg( p_vout, "directx-device: %s", device.psz_string );
            free( device.psz_string );
        }
gbazin's avatar
 
gbazin committed
896

897 898 899 900
        p_vout->p_sys->hmonitor =
            p_vout->p_sys->MonitorFromWindow( p_vout->p_sys->hwnd,
                                              MONITOR_DEFAULTTONEAREST );

901
        /* Enumerate displays */
zorglub's avatar
zorglub committed
902
        OurDirectDrawEnumerateEx( DirectXEnumCallback, p_vout,
903 904 905
                                  DDENUM_ATTACHEDSECONDARYDEVICES );
    }

gbazin's avatar
 
gbazin committed
906
    /* Initialize DirectDraw now */
907 908
    dxresult = OurDirectDrawCreate( p_vout->p_sys->p_display_driver,
                                    &p_ddobject, NULL );
gbazin's avatar
 
gbazin committed
909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928
    if( dxresult != DD_OK )
    {
        msg_Err( p_vout, "DirectXInitDDraw cannot initialize DDraw" );
        goto error;
    }

    /* Get the IDirectDraw2 interface */
    dxresult = IDirectDraw_QueryInterface( p_ddobject, &IID_IDirectDraw2,
                                        (LPVOID *)&p_vout->p_sys->p_ddobject );
    /* Release the unused interface */
    IDirectDraw_Release( p_ddobject );
    if( dxresult != DD_OK )
    {
        msg_Err( p_vout, "cannot get IDirectDraw2 interface" );
        goto error;
    }

    /* Set DirectDraw Cooperative level, ie what control we want over Windows
     * display */
    dxresult = IDirectDraw2_SetCooperativeLevel( p_vout->p_sys->p_ddobject,
929
                                                 NULL, DDSCL_NORMAL );
gbazin's avatar
 
gbazin committed
930 931 932 933 934 935
    if( dxresult != DD_OK )
    {
        msg_Err( p_vout, "cannot set direct draw cooperative level" );
        goto error;
    }

936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952
    /* Get the size of the current display device */
    if( p_vout->p_sys->hmonitor && p_vout->p_sys->GetMonitorInfo )
    {
        MONITORINFO monitor_info;
        monitor_info.cbSize = sizeof( MONITORINFO );
        p_vout->p_sys->GetMonitorInfo( p_vout->p_sys->hmonitor,
                                       &monitor_info );
        p_vout->p_sys->rect_display = monitor_info.rcMonitor;
    }
    else
    {
        p_vout->p_sys->rect_display.left = 0;
        p_vout->p_sys->rect_display.top = 0;
        p_vout->p_sys->rect_display.right  = GetSystemMetrics(SM_CXSCREEN);
        p_vout->p_sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
    }

gbazin's avatar
 
gbazin committed
953 954 955 956 957
    msg_Dbg( p_vout, "screen dimensions (%ix%i,%ix%i)",
             p_vout->p_sys->rect_display.left,
             p_vout->p_sys->rect_display.top,
             p_vout->p_sys->rect_display.right,
             p_vout->p_sys->rect_display.bottom );
958

gbazin's avatar
 
gbazin committed
959 960 961 962
    /* Probe the capabilities of the hardware */
    DirectXGetDDrawCaps( p_vout );

    msg_Dbg( p_vout, "End DirectXInitDDraw" );
963
    return VLC_SUCCESS;
gbazin's avatar
 
gbazin committed
964 965 966 967 968 969 970 971

 error:
    if( p_vout->p_sys->p_ddobject )
        IDirectDraw2_Release( p_vout->p_sys->p_ddobject );
    if( p_vout->p_sys->hddraw_dll )
        FreeLibrary( p_vout->p_sys->hddraw_dll );
    p_vout->p_sys->hddraw_dll = NULL;
    p_vout->p_sys->p_ddobject = NULL;
972
    return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996
}

/*****************************************************************************
 * DirectXCreateDisplay: create the DirectDraw display.
 *****************************************************************************
 * Create and initialize display according to preferences specified in the vout
 * thread fields.
 *****************************************************************************/
static int DirectXCreateDisplay( vout_thread_t *p_vout )
{
    HRESULT              dxresult;
    DDSURFACEDESC        ddsd;
    LPDIRECTDRAWSURFACE  p_display;

    msg_Dbg( p_vout, "DirectXCreateDisplay" );

    /* Now get the primary surface. This surface is what you actually see
     * on your screen */
    memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
    ddsd.dwSize = sizeof(DDSURFACEDESC);
    ddsd.dwFlags = DDSD_CAPS;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

    dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
997
                                           &ddsd, &p_display, NULL );
gbazin's avatar
 
gbazin committed
998 999
    if( dxresult != DD_OK )
    {
gbazin's avatar
 
gbazin committed
1000
        msg_Err( p_vout, "cannot get primary surface (error %li)", dxresult );
1001
        return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
1002 1003 1004 1005 1006 1007 1008 1009 1010
    }

    dxresult = IDirectDrawSurface_QueryInterface( p_display,
                                         &IID_IDirectDrawSurface2,
                                         (LPVOID *)&p_vout->p_sys->p_display );
    /* Release the old interface */
    IDirectDrawSurface_Release( p_display );
    if ( dxresult != DD_OK )
    {
1011
        msg_Err( p_vout, "cannot query IDirectDrawSurface2 interface "
gbazin's avatar
 
gbazin committed
1012
                         "(error %li)", dxresult );
1013
        return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
1014 1015 1016 1017 1018
    }

    /* The clipper will be used only in non-overlay mode */
    DirectXCreateClipper( p_vout );

1019 1020 1021
    /* Make sure the colorkey will be painted */
    p_vout->p_sys->i_colorkey = 1;
    p_vout->p_sys->i_rgb_colorkey =
1022
        DirectXFindColorkey( p_vout, &p_vout->p_sys->i_colorkey );
gbazin's avatar
 
gbazin committed
1023

1024 1025 1026 1027
    /* Create the actual brush */
    SetClassLong( p_vout->p_sys->hvideownd, GCL_HBRBACKGROUND,
                  (LONG)CreateSolidBrush( p_vout->p_sys->i_rgb_colorkey ) );
    InvalidateRect( p_vout->p_sys->hvideownd, NULL, TRUE );
1028
    E_(DirectXUpdateRects)( p_vout, VLC_TRUE );
gbazin's avatar
 
gbazin committed
1029

1030
    return VLC_SUCCESS;
gbazin's avatar
 
gbazin committed
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
}

/*****************************************************************************
 * DirectXCreateClipper: Create a clipper that will be used when blitting the
 *                       RGB surface to the main display.
 *****************************************************************************
 * This clipper prevents us to modify by mistake anything on the screen
 * which doesn't belong to our window. For example when a part of our video
 * window is hidden by another window.
 *****************************************************************************/
static int DirectXCreateClipper( vout_thread_t *p_vout )
{
    HRESULT dxresult;

    msg_Dbg( p_vout, "DirectXCreateClipper" );

    /* Create the clipper */
    dxresult = IDirectDraw2_CreateClipper( p_vout->p_sys->p_ddobject, 0,
                                           &p_vout->p_sys->p_clipper, NULL );
    if( dxresult != DD_OK )
    {
gbazin's avatar
 
gbazin committed
1052
        msg_Warn( p_vout, "cannot create clipper (error %li)", dxresult );
gbazin's avatar
 
gbazin committed
1053 1054 1055
        goto error;
    }

1056
    /* Associate the clipper to the window */
1057 1058
    dxresult = IDirectDrawClipper_SetHWnd( p_vout->p_sys->p_clipper, 0,
                                           p_vout->p_sys->hvideownd );
gbazin's avatar
 
gbazin committed
1059 1060
    if( dxresult != DD_OK )
    {
gbazin's avatar
 
gbazin committed
1061
        msg_Warn( p_vout, "cannot attach clipper to window (error %li)",
1062
                          dxresult );
gbazin's avatar
 
gbazin committed
1063 1064 1065 1066 1067 1068 1069 1070
        goto error;
    }

    /* associate the clipper with the surface */
    dxresult = IDirectDrawSurface_SetClipper(p_vout->p_sys->p_display,
                                             p_vout->p_sys->p_clipper);
    if( dxresult != DD_OK )
    {
gbazin's avatar
 
gbazin committed
1071
        msg_Warn( p_vout, "cannot attach clipper to surface (error %li)",
1072
                          dxresult );
gbazin's avatar
 
gbazin committed
1073
        goto error;
1074
    }
gbazin's avatar
 
gbazin committed