]> git.sesse.net Git - vlc/blob - modules/video_output/directx/directx.c
* modules/video_output/directx/*: take care of hardware overlay alignment constraints.
[vlc] / modules / video_output / directx / directx.c
1 /*****************************************************************************
2  * vout.c: Windows DirectX video output display method
3  *****************************************************************************
4  * Copyright (C) 2001-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble:
26  *
27  * This plugin will use YUV overlay if supported, using overlay will result in
28  * the best video quality (hardware interpolation when rescaling the picture)
29  * and the fastest display as it requires less processing.
30  *
31  * If YUV overlay is not supported this plugin will use RGB offscreen video
32  * surfaces that will be blitted onto the primary surface (display) to
33  * effectively display the pictures. This fallback method also enables us to
34  * display video in window mode.
35  *
36  *****************************************************************************/
37 #include <errno.h>                                                 /* ENOMEM */
38 #include <stdlib.h>                                                /* free() */
39 #include <string.h>                                            /* strerror() */
40
41 #include <vlc/vlc.h>
42 #include <vlc/intf.h>
43 #include <vlc/vout.h>
44
45 #include <windows.h>
46 #include <ddraw.h>
47
48 #include <multimon.h>
49 #undef GetSystemMetrics
50
51 #ifndef MONITOR_DEFAULTTONEAREST
52 #   define MONITOR_DEFAULTTONEAREST 2
53 #endif
54
55 #include "vout.h"
56
57 /*****************************************************************************
58  * DirectDraw GUIDs.
59  * Defining them here allows us to get rid of the dxguid library during
60  * the linking stage.
61  *****************************************************************************/
62 #include <initguid.h>
63 DEFINE_GUID( IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 );
64 DEFINE_GUID( IID_IDirectDrawSurface2, 0x57805885,0x6eec,0x11cf,0x94,0x41,0xa8,0x23,0x03,0xc1,0x0e,0x27 );
65
66 /*****************************************************************************
67  * Local prototypes.
68  *****************************************************************************/
69 static int  OpenVideo  ( vlc_object_t * );
70 static void CloseVideo ( vlc_object_t * );
71
72 static int  Init      ( vout_thread_t * );
73 static void End       ( vout_thread_t * );
74 static int  Manage    ( vout_thread_t * );
75 static void Display   ( vout_thread_t *, picture_t * );
76
77 static int  NewPictureVec  ( vout_thread_t *, picture_t *, int );
78 static void FreePictureVec ( vout_thread_t *, picture_t *, int );
79 static int  UpdatePictureStruct( vout_thread_t *, picture_t *, int );
80
81 static int  DirectXInitDDraw      ( vout_thread_t *p_vout );
82 static void DirectXCloseDDraw     ( vout_thread_t *p_vout );
83 static int  DirectXCreateDisplay  ( vout_thread_t *p_vout );
84 static void DirectXCloseDisplay   ( vout_thread_t *p_vout );
85 static int  DirectXCreateSurface  ( vout_thread_t *p_vout,
86                                     LPDIRECTDRAWSURFACE2 *, int, int, int );
87 static void DirectXCloseSurface   ( vout_thread_t *p_vout,
88                                     LPDIRECTDRAWSURFACE2 );
89 static int  DirectXCreateClipper  ( vout_thread_t *p_vout );
90 static void DirectXGetDDrawCaps   ( vout_thread_t *p_vout );
91 static int  DirectXLockSurface    ( vout_thread_t *p_vout, picture_t *p_pic );
92 static int  DirectXUnlockSurface  ( vout_thread_t *p_vout, picture_t *p_pic );
93
94 static DWORD DirectXFindColorkey( vout_thread_t *p_vout, uint32_t i_color );
95
96 /* Object variables callbacks */
97 static int OnTopCallback( vlc_object_t *, char const *,
98                           vlc_value_t, vlc_value_t, void * );
99 static int FindDevicesCallback( vlc_object_t *, char const *,
100                                 vlc_value_t, vlc_value_t, void * );
101
102 /*****************************************************************************
103  * Module descriptor
104  *****************************************************************************/
105 #define HW_YUV_TEXT N_("Use hardware YUV->RGB conversions")
106 #define HW_YUV_LONGTEXT N_( \
107     "Try to use hardware acceleration for YUV->RGB conversions. " \
108     "This option doesn't have any effect when using overlays." )
109
110 #define SYSMEM_TEXT N_("Use video buffers in system memory")
111 #define SYSMEM_LONGTEXT N_( \
112     "Create video buffers in system memory instead of video memory. This " \
113     "isn't recommended as usually using video memory allows to benefit from " \
114     "more hardware acceleration (like rescaling or YUV->RGB conversions). " \
115     "This option doesn't have any effect when using overlays." )
116
117 #define TRIPLEBUF_TEXT N_("Use triple buffering for overlays")
118 #define TRIPLEBUF_LONGTEXT N_( \
119     "Try to use triple bufferring when using YUV overlays. That results in " \
120     "much better video quality (no flickering)." )
121
122 #define DEVICE_TEXT N_("Name of desired display device")
123 #define DEVICE_LONGTEXT N_("In a multimonitor configuration, you can specify "\
124     "the Windows device name of the display that you want the video window " \
125     "to open on. For example, \"\\\\.\\DISPLAY1\" or \"\\\\.\\DISPLAY2\"." )
126
127 static char *ppsz_dev[] = { "" };
128 static char *ppsz_dev_text[] = { N_("Default") };
129
130 vlc_module_begin();
131     add_bool( "directx-hw-yuv", 1, NULL, HW_YUV_TEXT, HW_YUV_LONGTEXT,
132               VLC_TRUE );
133     add_bool( "directx-use-sysmem", 0, NULL, SYSMEM_TEXT, SYSMEM_LONGTEXT,
134               VLC_TRUE );
135     add_bool( "directx-3buffering", 1, NULL, TRIPLEBUF_TEXT,
136               TRIPLEBUF_LONGTEXT, VLC_TRUE );
137
138     add_string( "directx-device", "", NULL, DEVICE_TEXT, DEVICE_LONGTEXT,
139                 VLC_TRUE );
140         change_string_list( ppsz_dev, ppsz_dev_text, FindDevicesCallback );
141         change_action_add( FindDevicesCallback, N_("Refresh list") );
142
143     set_description( _("DirectX video output") );
144     set_capability( "video output", 100 );
145     add_shortcut( "directx" );
146     set_callbacks( OpenVideo, CloseVideo );
147 vlc_module_end();
148
149 #if 0 /* FIXME */
150     /* check if we registered a window class because we need to
151      * unregister it */
152     WNDCLASS wndclass;
153     if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) )
154         UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) );
155 #endif
156
157 /*****************************************************************************
158  * OpenVideo: allocate DirectX video thread output method
159  *****************************************************************************
160  * This function allocates and initialize the DirectX vout method.
161  *****************************************************************************/
162 static int OpenVideo( vlc_object_t *p_this )
163 {
164     vout_thread_t * p_vout = (vout_thread_t *)p_this;
165     vlc_value_t val, text;
166     HMODULE huser32;
167
168     /* Allocate structure */
169     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
170     if( p_vout->p_sys == NULL )
171     {
172         msg_Err( p_vout, "out of memory" );
173         return VLC_ENOMEM;
174     }
175     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
176
177     /* Initialisations */
178     p_vout->pf_init = Init;
179     p_vout->pf_end = End;
180     p_vout->pf_manage = Manage;
181     p_vout->pf_render = NULL;
182     p_vout->pf_display = Display;
183
184     p_vout->p_sys->p_ddobject = NULL;
185     p_vout->p_sys->p_display = NULL;
186     p_vout->p_sys->p_current_surface = NULL;
187     p_vout->p_sys->p_clipper = NULL;
188     p_vout->p_sys->hwnd = p_vout->p_sys->hvideownd = NULL;
189     p_vout->p_sys->hparent = NULL;
190     p_vout->p_sys->i_changes = 0;
191     SetRectEmpty( &p_vout->p_sys->rect_display  );
192
193     /* Multimonitor stuff */
194     p_vout->p_sys->hmonitor = NULL;
195     p_vout->p_sys->p_display_driver = NULL;
196     p_vout->p_sys->MonitorFromWindow = NULL;
197     p_vout->p_sys->GetMonitorInfo = NULL;
198     if( (huser32 = GetModuleHandle( "USER32" ) ) )
199     {
200         p_vout->p_sys->MonitorFromWindow =
201             GetProcAddress( huser32, "MonitorFromWindow" );
202         p_vout->p_sys->GetMonitorInfo =
203             GetProcAddress( huser32, "GetMonitorInfoA" );
204     }
205
206     var_Create( p_vout, "overlay", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
207     var_Create( p_vout, "directx-use-sysmem", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
208     var_Create( p_vout, "directx-hw-yuv", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
209     var_Create( p_vout, "directx-3buffering", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
210     var_Create( p_vout, "directx-device", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
211
212     p_vout->p_sys->b_cursor_hidden = 0;
213     p_vout->p_sys->i_lastmoved = mdate();
214
215     /* Set main window's size */
216     p_vout->p_sys->i_window_width = p_vout->i_window_width;
217     p_vout->p_sys->i_window_height = p_vout->i_window_height;
218
219     /* Create the DirectXEventThread, this thread is created by us to isolate
220      * the Win32 PeekMessage function calls. We want to do this because
221      * Windows can stay blocked inside this call for a long time, and when
222      * this happens it thus blocks vlc's video_output thread.
223      * DirectXEventThread will take care of the creation of the video
224      * window (because PeekMessage has to be called from the same thread which
225      * created the window). */
226     msg_Dbg( p_vout, "creating DirectXEventThread" );
227     p_vout->p_sys->p_event =
228         vlc_object_create( p_vout, sizeof(event_thread_t) );
229     p_vout->p_sys->p_event->p_vout = p_vout;
230     if( vlc_thread_create( p_vout->p_sys->p_event, "DirectX Events Thread",
231                            DirectXEventThread, 0, 1 ) )
232     {
233         msg_Err( p_vout, "cannot create DirectXEventThread" );
234         vlc_object_destroy( p_vout->p_sys->p_event );
235         p_vout->p_sys->p_event = NULL;
236         goto error;
237     }
238
239     if( p_vout->p_sys->p_event->b_error )
240     {
241         msg_Err( p_vout, "DirectXEventThread failed" );
242         goto error;
243     }
244
245     vlc_object_attach( p_vout->p_sys->p_event, p_vout );
246
247     msg_Dbg( p_vout, "DirectXEventThread running" );
248
249     /* Initialise DirectDraw */
250     if( DirectXInitDDraw( p_vout ) )
251     {
252         msg_Err( p_vout, "cannot initialize DirectDraw" );
253         goto error;
254     }
255
256     /* Create the directx display */
257     if( DirectXCreateDisplay( p_vout ) )
258     {
259         msg_Err( p_vout, "cannot initialize DirectDraw" );
260         goto error;
261     }
262
263     /* Add a variable to indicate if the window should be on top of others */
264     var_Create( p_vout, "video-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
265     text.psz_string = _("Always on top");
266     var_Change( p_vout, "video-on-top", VLC_VAR_SETTEXT, &text, NULL );
267     var_Get( p_vout, "video-on-top", &val );
268     p_vout->p_sys->b_on_top_change = val.b_bool;
269     var_AddCallback( p_vout, "video-on-top", OnTopCallback, NULL );
270
271     return VLC_SUCCESS;
272
273  error:
274     CloseVideo( VLC_OBJECT(p_vout) );
275     return VLC_EGENERIC;
276 }
277
278 /*****************************************************************************
279  * Init: initialize DirectX video thread output method
280  *****************************************************************************
281  * This function create the directx surfaces needed by the output thread.
282  * It is called at the beginning of the thread.
283  *****************************************************************************/
284 static int Init( vout_thread_t *p_vout )
285 {
286     int i_chroma_backup;
287     vlc_value_t val;
288
289     /* Get a few default parameters */
290     var_Get( p_vout, "overlay", &val );
291     p_vout->p_sys->b_using_overlay = val.b_bool;
292     var_Get( p_vout, "directx-use-sysmem", &val );
293     p_vout->p_sys->b_use_sysmem = val.b_bool;
294     var_Get( p_vout, "directx-hw-yuv", &val );
295     p_vout->p_sys->b_hw_yuv = val.b_bool;
296     var_Get( p_vout, "directx-3buffering", &val );
297     p_vout->p_sys->b_3buf_overlay = val.b_bool;
298
299     /* Initialise DirectDraw if not already done.
300      * We do this here because on multi-monitor systems we may have to
301      * re-create the directdraw surfaces. */
302     if( !p_vout->p_sys->p_ddobject &&
303         DirectXInitDDraw( p_vout ) != VLC_SUCCESS )
304     {
305         msg_Err( p_vout, "cannot initialize DirectDraw" );
306         return VLC_EGENERIC;
307     }
308
309     /* Create the directx display */
310     if( !p_vout->p_sys->p_display &&
311         DirectXCreateDisplay( p_vout ) != VLC_SUCCESS )
312     {
313         msg_Err( p_vout, "cannot initialize DirectDraw" );
314         return VLC_EGENERIC;
315     }
316
317     /* Initialize the output structure.
318      * Since DirectDraw can do rescaling for us, stick to the default
319      * coordinates and aspect. */
320     p_vout->output.i_width  = p_vout->render.i_width;
321     p_vout->output.i_height = p_vout->render.i_height;
322     p_vout->output.i_aspect = p_vout->render.i_aspect;
323
324 #define MAX_DIRECTBUFFERS 1
325     /* Right now we use only 1 directbuffer because we don't want the
326      * video decoder to decode directly into direct buffers as they are
327      * created into video memory and video memory is _really_ slow */
328
329     /* Choose the chroma we will try first. */
330     switch( p_vout->render.i_chroma )
331     {
332         case VLC_FOURCC('Y','U','Y','2'):
333         case VLC_FOURCC('Y','U','N','V'):
334             p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
335             break;
336         case VLC_FOURCC('U','Y','V','Y'):
337         case VLC_FOURCC('U','Y','N','V'):
338         case VLC_FOURCC('Y','4','2','2'):
339             p_vout->output.i_chroma = VLC_FOURCC('U','Y','V','Y');
340             break;
341         case VLC_FOURCC('Y','V','Y','U'):
342             p_vout->output.i_chroma = VLC_FOURCC('Y','V','Y','U');
343             break;
344         default:
345             p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2');
346             break;
347     }
348
349     NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS );
350
351     i_chroma_backup = p_vout->output.i_chroma;
352
353     if( !I_OUTPUTPICTURES )
354     {
355         /* hmmm, it didn't work! Let's try commonly supported chromas */
356         if( p_vout->output.i_chroma != VLC_FOURCC('Y','V','1','2') )
357         {
358             p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2');
359             NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS );
360         }
361         if( !I_OUTPUTPICTURES )
362         {
363             /* hmmm, it still didn't work! Let's try another one */
364             p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
365             NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS );
366         }
367     }
368
369     if( !I_OUTPUTPICTURES )
370     {
371         /* If it still didn't work then don't try to use an overlay */
372         p_vout->output.i_chroma = i_chroma_backup;
373         p_vout->p_sys->b_using_overlay = 0;
374         NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS );
375     }
376
377     /* Change the window title bar text */
378     if( p_vout->p_sys->hparent ) ; /* Do nothing */
379     else PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
380
381     return VLC_SUCCESS;
382 }
383
384 /*****************************************************************************
385  * End: terminate Sys video thread output method
386  *****************************************************************************
387  * Terminate an output method created by Create.
388  * It is called at the end of the thread.
389  *****************************************************************************/
390 static void End( vout_thread_t *p_vout )
391 {
392     FreePictureVec( p_vout, p_vout->p_picture, I_OUTPUTPICTURES );
393
394     DirectXCloseDisplay( p_vout );
395     DirectXCloseDDraw( p_vout );
396
397     return;
398 }
399
400 /*****************************************************************************
401  * CloseVideo: destroy Sys video thread output method
402  *****************************************************************************
403  * Terminate an output method created by Create
404  *****************************************************************************/
405 static void CloseVideo( vlc_object_t *p_this )
406 {
407     vout_thread_t * p_vout = (vout_thread_t *)p_this;
408
409     msg_Dbg( p_vout, "CloseVideo" );
410
411     var_Destroy( p_vout, "video-on-top" );
412
413     if( p_vout->p_sys->p_event )
414     {
415         vlc_object_detach( p_vout->p_sys->p_event );
416
417         /* Kill DirectXEventThread */
418         p_vout->p_sys->p_event->b_die = VLC_TRUE;
419
420         /* we need to be sure DirectXEventThread won't stay stuck in
421          * GetMessage, so we send a fake message */
422         if( p_vout->p_sys->hwnd )
423         {
424             PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0);
425         }
426
427         vlc_thread_join( p_vout->p_sys->p_event );
428         vlc_object_destroy( p_vout->p_sys->p_event );
429     }
430
431     if( p_vout->p_sys )
432     {
433         free( p_vout->p_sys );
434         p_vout->p_sys = NULL;
435     }
436 }
437
438 /*****************************************************************************
439  * Manage: handle Sys events
440  *****************************************************************************
441  * This function should be called regularly by the video output thread.
442  * It returns a non null value if an error occured.
443  *****************************************************************************/
444 static int Manage( vout_thread_t *p_vout )
445 {
446     WINDOWPLACEMENT window_placement;
447
448     /* If we do not control our window, we check for geometry changes
449      * ourselves because the parent might not send us its events. */
450     if( p_vout->p_sys->hparent )
451     {
452         DirectXUpdateRects( p_vout, VLC_FALSE );
453     }
454
455     /*
456      * Position Change
457      */
458     if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE )
459     {
460         if( p_vout->p_sys->b_using_overlay )
461             DirectXUpdateOverlay( p_vout );
462
463         /* Check if we are still on the same monitor */
464         if( p_vout->p_sys->MonitorFromWindow &&
465             p_vout->p_sys->hmonitor !=
466                 p_vout->p_sys->MonitorFromWindow( p_vout->p_sys->hwnd,
467                                                   MONITOR_DEFAULTTONEAREST ) )
468         {
469             /* This will force the vout core to recreate the picture buffers */
470             p_vout->i_changes |= VOUT_PICTURE_BUFFERS_CHANGE;
471         }
472
473         p_vout->p_sys->i_changes &= ~DX_POSITION_CHANGE;
474     }
475
476     /* We used to call the Win32 PeekMessage function here to read the window
477      * messages. But since window can stay blocked into this function for a
478      * long time (for example when you move your window on the screen), I
479      * decided to isolate PeekMessage in another thread. */
480
481     /*
482      * Fullscreen change
483      */
484     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
485         || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
486     {
487         vlc_value_t val;
488
489         p_vout->b_fullscreen = ! p_vout->b_fullscreen;
490
491         /* We need to switch between Maximized and Normal sized window */
492         window_placement.length = sizeof(WINDOWPLACEMENT);
493         GetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
494         if( p_vout->b_fullscreen )
495         {
496             /* Maximized window */
497             window_placement.showCmd = SW_SHOWMAXIMIZED;
498             /* Change window style, no borders and no title bar */
499             SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, WS_CLIPCHILDREN );
500
501         }
502         else
503         {
504             /* Normal window */
505             window_placement.showCmd = SW_SHOWNORMAL;
506             /* Change window style, borders and title bar */
507             SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, WS_CLIPCHILDREN |
508                            WS_OVERLAPPEDWINDOW | WS_SIZEBOX | WS_VISIBLE );
509         }
510
511         SetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
512
513         /* Update the object variable and trigger callback */
514         val.b_bool = p_vout->b_fullscreen;
515         var_Set( p_vout, "fullscreen", val );
516
517         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
518         p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
519     }
520
521     /*
522      * Pointer change
523      */
524     if( (!p_vout->p_sys->b_cursor_hidden) &&
525         ( (mdate() - p_vout->p_sys->i_lastmoved) > 5000000 ) )
526     {
527         /* Hide the mouse automatically */
528         if( p_vout->p_sys->hwnd != p_vout->p_sys->hparent )
529         {
530             p_vout->p_sys->b_cursor_hidden = VLC_TRUE;
531             PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
532         }
533     }
534
535     /*
536      * "Always on top" status change
537      */
538     if( p_vout->p_sys->b_on_top_change )
539     {
540         vlc_value_t val;
541         HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
542
543         var_Get( p_vout, "video-on-top", &val );
544
545         /* Set the window on top if necessary */
546         if( val.b_bool && !( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
547                            & WS_EX_TOPMOST ) )
548         {
549             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
550                            MF_BYCOMMAND | MFS_CHECKED );
551             SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0,
552                           SWP_NOSIZE | SWP_NOMOVE );
553         }
554         else
555         /* The window shouldn't be on top */
556         if( !val.b_bool && ( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
557                            & WS_EX_TOPMOST ) )
558         {
559             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
560                            MF_BYCOMMAND | MFS_UNCHECKED );
561             SetWindowPos( p_vout->p_sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
562                           SWP_NOSIZE | SWP_NOMOVE );
563         }
564
565         p_vout->p_sys->b_on_top_change = VLC_FALSE;
566     }
567
568     /* Check if the event thread is still running */
569     if( p_vout->p_sys->p_event->b_die )
570     {
571         return VLC_EGENERIC; /* exit */
572     }
573
574     return VLC_SUCCESS;
575 }
576
577 /*****************************************************************************
578  * Display: displays previously rendered output
579  *****************************************************************************
580  * This function sends the currently rendered image to the display, wait until
581  * it is displayed and switch the two rendering buffers, preparing next frame.
582  *****************************************************************************/
583 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
584 {
585     HRESULT dxresult;
586
587     if( (p_vout->p_sys->p_display == NULL) )
588     {
589         msg_Warn( p_vout, "no display!" );
590         return;
591     }
592
593     /* Our surface can be lost so be sure to check this
594      * and restore it if need be */
595     if( IDirectDrawSurface2_IsLost( p_vout->p_sys->p_display )
596         == DDERR_SURFACELOST )
597     {
598         if( IDirectDrawSurface2_Restore( p_vout->p_sys->p_display ) == DD_OK &&
599             p_vout->p_sys->b_using_overlay )
600             DirectXUpdateOverlay( p_vout );
601     }
602
603     if( !p_vout->p_sys->b_using_overlay )
604     {
605         DDBLTFX  ddbltfx;
606
607         /* We ask for the "NOTEARING" option */
608         memset( &ddbltfx, 0, sizeof(DDBLTFX) );
609         ddbltfx.dwSize = sizeof(DDBLTFX);
610         ddbltfx.dwDDFX = DDBLTFX_NOTEARING;
611
612         /* Blit video surface to display */
613         dxresult = IDirectDrawSurface2_Blt( p_vout->p_sys->p_display,
614                                             &p_vout->p_sys->rect_dest_clipped,
615                                             p_pic->p_sys->p_surface,
616                                             &p_vout->p_sys->rect_src_clipped,
617                                             DDBLT_ASYNC, &ddbltfx );
618         if( dxresult != DD_OK )
619         {
620             msg_Warn( p_vout, "could not blit surface (error %li)", dxresult );
621             return;
622         }
623
624     }
625     else /* using overlay */
626     {
627         /* Flip the overlay buffers if we are using back buffers */
628         if( p_pic->p_sys->p_front_surface == p_pic->p_sys->p_surface )
629         {
630             return;
631         }
632
633         dxresult = IDirectDrawSurface2_Flip( p_pic->p_sys->p_front_surface,
634                                              NULL, DDFLIP_WAIT );
635         if( dxresult != DD_OK )
636         {
637             msg_Warn( p_vout, "could not flip overlay (error %li)", dxresult );
638         }
639
640         /* set currently displayed pic */
641         p_vout->p_sys->p_current_surface = p_pic->p_sys->p_front_surface;
642
643         /* Lock surface to get all the required info */
644         if( DirectXLockSurface( p_vout, p_pic ) )
645         {
646             /* AAARRGG */
647             msg_Warn( p_vout, "cannot lock surface" );
648             return;
649         }
650         DirectXUnlockSurface( p_vout, p_pic );
651     }
652 }
653
654 /* following functions are local */
655
656 /*****************************************************************************
657  * DirectXEnumCallback: Device enumeration
658  *****************************************************************************
659  * This callback function is called by DirectDraw once for each
660  * available DirectDraw device.
661  *****************************************************************************/
662 BOOL WINAPI DirectXEnumCallback( GUID* p_guid, LPTSTR psz_desc,
663                                  LPTSTR psz_drivername, VOID* p_context,
664                                  HMONITOR hmon )
665 {
666     vout_thread_t *p_vout = (vout_thread_t *)p_context;
667     vlc_value_t device;
668
669     msg_Dbg( p_vout, "DirectXEnumCallback: %s, %s", psz_desc, psz_drivername );
670
671     if( hmon )
672     {
673         var_Get( p_vout, "directx-device", &device );
674
675         if( ( !device.psz_string || !*device.psz_string ) &&
676             hmon == p_vout->p_sys->hmonitor )
677         {
678             if( device.psz_string ) free( device.psz_string );
679         }
680         else if( strcmp( psz_drivername, device.psz_string ) == 0 )
681         {
682             MONITORINFO monitor_info;
683             monitor_info.cbSize = sizeof( MONITORINFO );
684
685             if( p_vout->p_sys->GetMonitorInfo( hmon, &monitor_info ) )
686             {
687                 RECT rect;
688
689                 /* Move window to the right screen */
690                 GetWindowRect( p_vout->p_sys->hwnd, &rect );
691                 if( !IntersectRect( &rect, &rect, &monitor_info.rcWork ) )
692                 {
693                     rect.left = monitor_info.rcWork.left;
694                     rect.top = monitor_info.rcWork.top;
695                     msg_Dbg( p_vout, "DirectXEnumCallback: Setting window "
696                              "position to %d,%d", rect.left, rect.top );
697                     SetWindowPos( p_vout->p_sys->hwnd, NULL,
698                                   rect.left, rect.top, 0, 0,
699                                   SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
700                 }
701             }
702
703             p_vout->p_sys->hmonitor = hmon;
704             if( device.psz_string ) free( device.psz_string );
705         }
706         else
707         {
708             if( device.psz_string ) free( device.psz_string );
709             return TRUE; /* Keep enumerating */
710         }
711
712         msg_Dbg( p_vout, "selecting %s, %s", psz_desc, psz_drivername );
713         p_vout->p_sys->p_display_driver = malloc( sizeof(GUID) );
714         if( p_vout->p_sys->p_display_driver )
715             memcpy( p_vout->p_sys->p_display_driver, p_guid, sizeof(GUID) );
716     }
717
718     return TRUE; /* Keep enumerating */
719 }
720
721 /*****************************************************************************
722  * DirectXInitDDraw: Takes care of all the DirectDraw initialisations
723  *****************************************************************************
724  * This function initialise and allocate resources for DirectDraw.
725  *****************************************************************************/
726 static int DirectXInitDDraw( vout_thread_t *p_vout )
727 {
728     HRESULT dxresult;
729     HRESULT (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *);
730     HRESULT (WINAPI *OurDirectDrawEnumerateEx)( LPDDENUMCALLBACKEXA, LPVOID,
731                                                 DWORD );
732     LPDIRECTDRAW p_ddobject;
733
734     msg_Dbg( p_vout, "DirectXInitDDraw" );
735
736     /* Load direct draw DLL */
737     p_vout->p_sys->hddraw_dll = LoadLibrary("DDRAW.DLL");
738     if( p_vout->p_sys->hddraw_dll == NULL )
739     {
740         msg_Warn( p_vout, "DirectXInitDDraw failed loading ddraw.dll" );
741         goto error;
742     }
743
744     OurDirectDrawCreate =
745       (void *)GetProcAddress(p_vout->p_sys->hddraw_dll, "DirectDrawCreate");
746     if( OurDirectDrawCreate == NULL )
747     {
748         msg_Err( p_vout, "DirectXInitDDraw failed GetProcAddress" );
749         goto error;
750     }
751
752     OurDirectDrawEnumerateEx =
753       (void *)GetProcAddress( p_vout->p_sys->hddraw_dll,
754                               "DirectDrawEnumerateExA" );
755
756     if( OurDirectDrawEnumerateEx && p_vout->p_sys->MonitorFromWindow )
757     {
758         vlc_value_t device;
759
760         var_Get( p_vout, "directx-device", &device );
761         if( device.psz_string )
762         {
763             msg_Dbg( p_vout, "directx-device: %s", device.psz_string );
764             free( device.psz_string );
765         }
766
767         p_vout->p_sys->hmonitor =
768             p_vout->p_sys->MonitorFromWindow( p_vout->p_sys->hwnd,
769                                               MONITOR_DEFAULTTONEAREST );
770
771         /* Enumerate displays */
772         OurDirectDrawEnumerateEx( DirectXEnumCallback, p_vout,
773                                   DDENUM_ATTACHEDSECONDARYDEVICES );
774     }
775
776     /* Initialize DirectDraw now */
777     dxresult = OurDirectDrawCreate( p_vout->p_sys->p_display_driver,
778                                     &p_ddobject, NULL );
779     if( dxresult != DD_OK )
780     {
781         msg_Err( p_vout, "DirectXInitDDraw cannot initialize DDraw" );
782         goto error;
783     }
784
785     /* Get the IDirectDraw2 interface */
786     dxresult = IDirectDraw_QueryInterface( p_ddobject, &IID_IDirectDraw2,
787                                         (LPVOID *)&p_vout->p_sys->p_ddobject );
788     /* Release the unused interface */
789     IDirectDraw_Release( p_ddobject );
790     if( dxresult != DD_OK )
791     {
792         msg_Err( p_vout, "cannot get IDirectDraw2 interface" );
793         goto error;
794     }
795
796     /* Set DirectDraw Cooperative level, ie what control we want over Windows
797      * display */
798     dxresult = IDirectDraw2_SetCooperativeLevel( p_vout->p_sys->p_ddobject,
799                                                  NULL, DDSCL_NORMAL );
800     if( dxresult != DD_OK )
801     {
802         msg_Err( p_vout, "cannot set direct draw cooperative level" );
803         goto error;
804     }
805
806     /* Get the size of the current display device */
807     if( p_vout->p_sys->hmonitor && p_vout->p_sys->GetMonitorInfo )
808     {
809         MONITORINFO monitor_info;
810         monitor_info.cbSize = sizeof( MONITORINFO );
811         p_vout->p_sys->GetMonitorInfo( p_vout->p_sys->hmonitor,
812                                        &monitor_info );
813         p_vout->p_sys->rect_display = monitor_info.rcMonitor;
814     }
815     else
816     {
817         p_vout->p_sys->rect_display.left = 0;
818         p_vout->p_sys->rect_display.top = 0;
819         p_vout->p_sys->rect_display.right  = GetSystemMetrics(SM_CXSCREEN);
820         p_vout->p_sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
821     }
822
823     msg_Dbg( p_vout, "screen dimensions (%ix%i,%ix%i)",
824              p_vout->p_sys->rect_display.left,
825              p_vout->p_sys->rect_display.top,
826              p_vout->p_sys->rect_display.right,
827              p_vout->p_sys->rect_display.bottom );
828
829     /* Probe the capabilities of the hardware */
830     DirectXGetDDrawCaps( p_vout );
831
832     msg_Dbg( p_vout, "End DirectXInitDDraw" );
833     return VLC_SUCCESS;
834
835  error:
836     if( p_vout->p_sys->p_ddobject )
837         IDirectDraw2_Release( p_vout->p_sys->p_ddobject );
838     if( p_vout->p_sys->hddraw_dll )
839         FreeLibrary( p_vout->p_sys->hddraw_dll );
840     p_vout->p_sys->hddraw_dll = NULL;
841     p_vout->p_sys->p_ddobject = NULL;
842     return VLC_EGENERIC;
843 }
844
845 /*****************************************************************************
846  * DirectXCreateDisplay: create the DirectDraw display.
847  *****************************************************************************
848  * Create and initialize display according to preferences specified in the vout
849  * thread fields.
850  *****************************************************************************/
851 static int DirectXCreateDisplay( vout_thread_t *p_vout )
852 {
853     HRESULT              dxresult;
854     DDSURFACEDESC        ddsd;
855     LPDIRECTDRAWSURFACE  p_display;
856
857     msg_Dbg( p_vout, "DirectXCreateDisplay" );
858
859     /* Now get the primary surface. This surface is what you actually see
860      * on your screen */
861     memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
862     ddsd.dwSize = sizeof(DDSURFACEDESC);
863     ddsd.dwFlags = DDSD_CAPS;
864     ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
865
866     dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
867                                            &ddsd, &p_display, NULL );
868     if( dxresult != DD_OK )
869     {
870         msg_Err( p_vout, "cannot get primary surface (error %li)", dxresult );
871         return VLC_EGENERIC;
872     }
873
874     dxresult = IDirectDrawSurface_QueryInterface( p_display,
875                                          &IID_IDirectDrawSurface2,
876                                          (LPVOID *)&p_vout->p_sys->p_display );
877     /* Release the old interface */
878     IDirectDrawSurface_Release( p_display );
879     if ( dxresult != DD_OK )
880     {
881         msg_Err( p_vout, "cannot query IDirectDrawSurface2 interface "
882                          "(error %li)", dxresult );
883         return VLC_EGENERIC;
884     }
885
886     /* The clipper will be used only in non-overlay mode */
887     DirectXCreateClipper( p_vout );
888
889     /* Make sure the colorkey will be painted */
890     p_vout->p_sys->i_colorkey = 1;
891     p_vout->p_sys->i_rgb_colorkey =
892         DirectXFindColorkey( p_vout, p_vout->p_sys->i_colorkey );
893
894     /* Create the actual brush */
895     SetClassLong( p_vout->p_sys->hvideownd, GCL_HBRBACKGROUND,
896                   (LONG)CreateSolidBrush( p_vout->p_sys->i_rgb_colorkey ) );
897     InvalidateRect( p_vout->p_sys->hvideownd, NULL, TRUE );
898     DirectXUpdateRects( p_vout, VLC_TRUE );
899
900     return VLC_SUCCESS;
901 }
902
903 /*****************************************************************************
904  * DirectXCreateClipper: Create a clipper that will be used when blitting the
905  *                       RGB surface to the main display.
906  *****************************************************************************
907  * This clipper prevents us to modify by mistake anything on the screen
908  * which doesn't belong to our window. For example when a part of our video
909  * window is hidden by another window.
910  *****************************************************************************/
911 static int DirectXCreateClipper( vout_thread_t *p_vout )
912 {
913     HRESULT dxresult;
914
915     msg_Dbg( p_vout, "DirectXCreateClipper" );
916
917     /* Create the clipper */
918     dxresult = IDirectDraw2_CreateClipper( p_vout->p_sys->p_ddobject, 0,
919                                            &p_vout->p_sys->p_clipper, NULL );
920     if( dxresult != DD_OK )
921     {
922         msg_Warn( p_vout, "cannot create clipper (error %li)", dxresult );
923         goto error;
924     }
925
926     /* Associate the clipper to the window */
927     dxresult = IDirectDrawClipper_SetHWnd( p_vout->p_sys->p_clipper, 0,
928                                            p_vout->p_sys->hvideownd );
929     if( dxresult != DD_OK )
930     {
931         msg_Warn( p_vout, "cannot attach clipper to window (error %li)",
932                           dxresult );
933         goto error;
934     }
935
936     /* associate the clipper with the surface */
937     dxresult = IDirectDrawSurface_SetClipper(p_vout->p_sys->p_display,
938                                              p_vout->p_sys->p_clipper);
939     if( dxresult != DD_OK )
940     {
941         msg_Warn( p_vout, "cannot attach clipper to surface (error %li)",
942                           dxresult );
943         goto error;
944     }
945
946     return VLC_SUCCESS;
947
948  error:
949     if( p_vout->p_sys->p_clipper )
950     {
951         IDirectDrawClipper_Release( p_vout->p_sys->p_clipper );
952     }
953     p_vout->p_sys->p_clipper = NULL;
954     return VLC_EGENERIC;
955 }
956
957 /*****************************************************************************
958  * DirectXCreateSurface: create an YUV overlay or RGB surface for the video.
959  *****************************************************************************
960  * The best method of display is with an YUV overlay because the YUV->RGB
961  * conversion is done in hardware.
962  * You can also create a plain RGB surface.
963  * ( Maybe we could also try an RGB overlay surface, which could have hardware
964  * scaling and which would also be faster in window mode because you don't
965  * need to do any blitting to the main display...)
966  *****************************************************************************/
967 static int DirectXCreateSurface( vout_thread_t *p_vout,
968                                  LPDIRECTDRAWSURFACE2 *pp_surface_final,
969                                  int i_chroma, int b_overlay,
970                                  int i_backbuffers )
971 {
972     HRESULT dxresult;
973     LPDIRECTDRAWSURFACE p_surface;
974     DDSURFACEDESC ddsd;
975
976     /* Create the video surface */
977     if( b_overlay )
978     {
979         /* Now try to create the YUV overlay surface.
980          * This overlay will be displayed on top of the primary surface.
981          * A color key is used to determine whether or not the overlay will be
982          * displayed, ie the overlay will be displayed in place of the primary
983          * surface wherever the primary surface will have this color.
984          * The video window has been created with a background of this color so
985          * the overlay will be only displayed on top of this window */
986
987         memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
988         ddsd.dwSize = sizeof(DDSURFACEDESC);
989         ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
990         ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
991         ddsd.ddpfPixelFormat.dwFourCC = i_chroma;
992         ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
993         ddsd.dwFlags |= (i_backbuffers ? DDSD_BACKBUFFERCOUNT : 0);
994         ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
995         ddsd.ddsCaps.dwCaps |= (i_backbuffers ? DDSCAPS_COMPLEX | DDSCAPS_FLIP
996                                 : 0 );
997         ddsd.dwHeight = p_vout->render.i_height;
998         ddsd.dwWidth = p_vout->render.i_width;
999         ddsd.dwBackBufferCount = i_backbuffers;
1000
1001         dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
1002                                                &ddsd, &p_surface, NULL );
1003         if( dxresult != DD_OK )
1004         {
1005             *pp_surface_final = NULL;
1006             return VLC_EGENERIC;
1007         }
1008     }
1009
1010     if( !b_overlay )
1011     {
1012         vlc_bool_t b_rgb_surface =
1013             ( i_chroma == VLC_FOURCC('R','G','B','2') )
1014           || ( i_chroma == VLC_FOURCC('R','V','1','5') )
1015            || ( i_chroma == VLC_FOURCC('R','V','1','6') )
1016             || ( i_chroma == VLC_FOURCC('R','V','2','4') )
1017              || ( i_chroma == VLC_FOURCC('R','V','3','2') );
1018
1019         memset( &ddsd, 0, sizeof( DDSURFACEDESC ) );
1020         ddsd.dwSize = sizeof(DDSURFACEDESC);
1021         ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
1022         ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH | DDSD_CAPS;
1023         ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
1024         ddsd.dwHeight = p_vout->render.i_height;
1025         ddsd.dwWidth = p_vout->render.i_width;
1026
1027         if( p_vout->p_sys->b_use_sysmem )
1028             ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
1029         else
1030             ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
1031
1032         if( !b_rgb_surface )
1033         {
1034             ddsd.dwFlags |= DDSD_PIXELFORMAT;
1035             ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
1036             ddsd.ddpfPixelFormat.dwFourCC = i_chroma;
1037         }
1038
1039         dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
1040                                                &ddsd, &p_surface, NULL );
1041         if( dxresult != DD_OK )
1042         {
1043             *pp_surface_final = NULL;
1044             return VLC_EGENERIC;
1045         }
1046     }
1047
1048     /* Now that the surface is created, try to get a newer DirectX interface */
1049     dxresult = IDirectDrawSurface_QueryInterface( p_surface,
1050                                      &IID_IDirectDrawSurface2,
1051                                      (LPVOID *)pp_surface_final );
1052     IDirectDrawSurface_Release( p_surface );    /* Release the old interface */
1053     if ( dxresult != DD_OK )
1054     {
1055         msg_Err( p_vout, "cannot query IDirectDrawSurface2 interface "
1056                          "(error %li)", dxresult );
1057         *pp_surface_final = NULL;
1058         return VLC_EGENERIC;
1059     }
1060
1061     if( b_overlay )
1062     {
1063         /* Check the overlay is useable as some graphics cards allow creating
1064          * several overlays but only one can be used at one time. */
1065         p_vout->p_sys->p_current_surface = *pp_surface_final;
1066         if( DirectXUpdateOverlay( p_vout ) != VLC_SUCCESS )
1067         {
1068             IDirectDrawSurface2_Release( *pp_surface_final );
1069             *pp_surface_final = NULL;
1070             msg_Err( p_vout, "overlay unuseable (might already be in use)" );
1071             return VLC_EGENERIC;
1072         }
1073     }
1074
1075     return VLC_SUCCESS;
1076 }
1077
1078 /*****************************************************************************
1079  * DirectXUpdateOverlay: Move or resize overlay surface on video display.
1080  *****************************************************************************
1081  * This function is used to move or resize an overlay surface on the screen.
1082  * Ususally the overlay is moved by the user and thus, by a move or resize
1083  * event (in Manage).
1084  *****************************************************************************/
1085 int DirectXUpdateOverlay( vout_thread_t *p_vout )
1086 {
1087     DDOVERLAYFX     ddofx;
1088     DWORD           dwFlags;
1089     HRESULT         dxresult;
1090
1091     if( p_vout->p_sys->p_current_surface == NULL ||
1092         !p_vout->p_sys->b_using_overlay )
1093         return VLC_EGENERIC;
1094
1095     /* The new window dimensions should already have been computed by the
1096      * caller of this function */
1097
1098     /* Position and show the overlay */
1099     memset(&ddofx, 0, sizeof(DDOVERLAYFX));
1100     ddofx.dwSize = sizeof(DDOVERLAYFX);
1101     ddofx.dckDestColorkey.dwColorSpaceLowValue = p_vout->p_sys->i_colorkey;
1102     ddofx.dckDestColorkey.dwColorSpaceHighValue = p_vout->p_sys->i_colorkey;
1103
1104     dwFlags = DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE;
1105
1106     dxresult = IDirectDrawSurface2_UpdateOverlay(
1107                                          p_vout->p_sys->p_current_surface,
1108                                          &p_vout->p_sys->rect_src_clipped,
1109                                          p_vout->p_sys->p_display,
1110                                          &p_vout->p_sys->rect_dest_clipped,
1111                                          dwFlags, &ddofx );
1112     if(dxresult != DD_OK)
1113     {
1114         msg_Warn( p_vout,
1115                   "DirectXUpdateOverlay cannot move or resize overlay" );
1116         return VLC_EGENERIC;
1117     }
1118
1119     return VLC_SUCCESS;
1120 }
1121
1122 /*****************************************************************************
1123  * DirectXCloseDDraw: Release the DDraw object allocated by DirectXInitDDraw
1124  *****************************************************************************
1125  * This function returns all resources allocated by DirectXInitDDraw.
1126  *****************************************************************************/
1127 static void DirectXCloseDDraw( vout_thread_t *p_vout )
1128 {
1129     msg_Dbg( p_vout, "DirectXCloseDDraw" );
1130     if( p_vout->p_sys->p_ddobject != NULL )
1131     {
1132         IDirectDraw2_Release(p_vout->p_sys->p_ddobject);
1133         p_vout->p_sys->p_ddobject = NULL;
1134     }
1135
1136     if( p_vout->p_sys->hddraw_dll != NULL )
1137     {
1138         FreeLibrary( p_vout->p_sys->hddraw_dll );
1139         p_vout->p_sys->hddraw_dll = NULL;
1140     }
1141
1142     if( p_vout->p_sys->p_display_driver != NULL )
1143     {
1144         free( p_vout->p_sys->p_display_driver );
1145         p_vout->p_sys->p_display_driver = NULL;
1146     }
1147
1148     p_vout->p_sys->hmonitor = NULL;
1149 }
1150
1151 /*****************************************************************************
1152  * DirectXCloseDisplay: close and reset the DirectX display device
1153  *****************************************************************************
1154  * This function returns all resources allocated by DirectXCreateDisplay.
1155  *****************************************************************************/
1156 static void DirectXCloseDisplay( vout_thread_t *p_vout )
1157 {
1158     msg_Dbg( p_vout, "DirectXCloseDisplay" );
1159
1160     if( p_vout->p_sys->p_clipper != NULL )
1161     {
1162         msg_Dbg( p_vout, "DirectXCloseDisplay clipper" );
1163         IDirectDrawClipper_Release( p_vout->p_sys->p_clipper );
1164         p_vout->p_sys->p_clipper = NULL;
1165     }
1166
1167     if( p_vout->p_sys->p_display != NULL )
1168     {
1169         msg_Dbg( p_vout, "DirectXCloseDisplay display" );
1170         IDirectDrawSurface2_Release( p_vout->p_sys->p_display );
1171         p_vout->p_sys->p_display = NULL;
1172     }
1173 }
1174
1175 /*****************************************************************************
1176  * DirectXCloseSurface: close the YUV overlay or RGB surface.
1177  *****************************************************************************
1178  * This function returns all resources allocated for the surface.
1179  *****************************************************************************/
1180 static void DirectXCloseSurface( vout_thread_t *p_vout,
1181                                  LPDIRECTDRAWSURFACE2 p_surface )
1182 {
1183     msg_Dbg( p_vout, "DirectXCloseSurface" );
1184     if( p_surface != NULL )
1185     {
1186         IDirectDrawSurface2_Release( p_surface );
1187     }
1188 }
1189
1190 /*****************************************************************************
1191  * NewPictureVec: allocate a vector of identical pictures
1192  *****************************************************************************
1193  * Returns 0 on success, -1 otherwise
1194  *****************************************************************************/
1195 static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic,
1196                           int i_num_pics )
1197 {
1198     int i;
1199     int i_ret = VLC_SUCCESS;
1200     LPDIRECTDRAWSURFACE2 p_surface;
1201
1202     msg_Dbg( p_vout, "NewPictureVec overlay:%s chroma:%.4s",
1203              p_vout->p_sys->b_using_overlay ? "yes" : "no",
1204              (char *)&p_vout->output.i_chroma );
1205
1206     I_OUTPUTPICTURES = 0;
1207
1208     /* First we try to use an YUV overlay surface.
1209      * The overlay surface that we create won't be used to decode directly
1210      * into it because accessing video memory directly is way to slow (remember
1211      * that pictures are decoded macroblock per macroblock). Instead the video
1212      * will be decoded in picture buffers in system memory which will then be
1213      * memcpy() to the overlay surface. */
1214     if( p_vout->p_sys->b_using_overlay )
1215     {
1216         /* Triple buffering rocks! it doesn't have any processing overhead
1217          * (you don't have to wait for the vsync) and provides for a very nice
1218          * video quality (no tearing). */
1219         if( p_vout->p_sys->b_3buf_overlay )
1220             i_ret = DirectXCreateSurface( p_vout, &p_surface,
1221                                           p_vout->output.i_chroma,
1222                                           p_vout->p_sys->b_using_overlay,
1223                                           2 /* number of backbuffers */ );
1224
1225         if( !p_vout->p_sys->b_3buf_overlay || i_ret != VLC_SUCCESS )
1226         {
1227             /* Try to reduce the number of backbuffers */
1228             i_ret = DirectXCreateSurface( p_vout, &p_surface,
1229                                           p_vout->output.i_chroma,
1230                                           p_vout->p_sys->b_using_overlay,
1231                                           0 /* number of backbuffers */ );
1232         }
1233
1234         if( i_ret == VLC_SUCCESS )
1235         {
1236             DDSCAPS dds_caps;
1237             picture_t front_pic;
1238             picture_sys_t front_pic_sys;
1239             front_pic.p_sys = &front_pic_sys;
1240
1241             /* Allocate internal structure */
1242             p_pic[0].p_sys = malloc( sizeof( picture_sys_t ) );
1243             if( p_pic[0].p_sys == NULL )
1244             {
1245                 DirectXCloseSurface( p_vout, p_surface );
1246                 return VLC_ENOMEM;
1247             }
1248
1249             /* set front buffer */
1250             p_pic[0].p_sys->p_front_surface = p_surface;
1251
1252             /* Get the back buffer */
1253             memset( &dds_caps, 0, sizeof( DDSCAPS ) );
1254             dds_caps.dwCaps = DDSCAPS_BACKBUFFER;
1255             if( DD_OK != IDirectDrawSurface2_GetAttachedSurface(
1256                                                 p_surface, &dds_caps,
1257                                                 &p_pic[0].p_sys->p_surface ) )
1258             {
1259                 msg_Warn( p_vout, "NewPictureVec could not get back buffer" );
1260                 /* front buffer is the same as back buffer */
1261                 p_pic[0].p_sys->p_surface = p_surface;
1262             }
1263
1264
1265             p_vout->p_sys->p_current_surface = front_pic.p_sys->p_surface =
1266                 p_pic[0].p_sys->p_front_surface;
1267
1268             /* Reset the front buffer memory */
1269             if( DirectXLockSurface( p_vout, &front_pic ) == VLC_SUCCESS )
1270             {
1271                 int i,j;
1272                 for( i = 0; i < front_pic.i_planes; i++ )
1273                     for( j = 0; j < front_pic.p[i].i_lines; j++)
1274                         memset( front_pic.p[i].p_pixels + j *
1275                                 front_pic.p[i].i_pitch, 127,
1276                                 front_pic.p[i].i_visible_pitch );
1277
1278                 DirectXUnlockSurface( p_vout, &front_pic );
1279             }
1280
1281             DirectXUpdateOverlay( p_vout );
1282             I_OUTPUTPICTURES = 1;
1283             msg_Dbg( p_vout, "YUV overlay created successfully" );
1284         }
1285     }
1286
1287     /* As we can't have an overlay, we'll try to create a plain offscreen
1288      * surface. This surface will reside in video memory because there's a
1289      * better chance then that we'll be able to use some kind of hardware
1290      * acceleration like rescaling, blitting or YUV->RGB conversions.
1291      * We then only need to blit this surface onto the main display when we
1292      * want to display it */
1293     if( !p_vout->p_sys->b_using_overlay )
1294     {
1295         if( p_vout->p_sys->b_hw_yuv )
1296         {
1297             DWORD i_codes;
1298             DWORD *pi_codes;
1299             vlc_bool_t b_result = VLC_FALSE;
1300
1301             /* Check if the chroma is supported first. This is required
1302              * because a few buggy drivers don't mind creating the surface
1303              * even if they don't know about the chroma. */
1304             if( IDirectDraw2_GetFourCCCodes( p_vout->p_sys->p_ddobject,
1305                                              &i_codes, NULL ) == DD_OK )
1306             {
1307                 pi_codes = malloc( i_codes * sizeof(DWORD) );
1308                 if( pi_codes && IDirectDraw2_GetFourCCCodes(
1309                     p_vout->p_sys->p_ddobject, &i_codes, pi_codes ) == DD_OK )
1310                 {
1311                     for( i = 0; i < (int)i_codes; i++ )
1312                     {
1313                         if( p_vout->output.i_chroma == pi_codes[i] )
1314                         {
1315                             b_result = VLC_TRUE;
1316                             break;
1317                         }
1318                     }
1319                 }
1320             }
1321
1322             if( b_result )
1323                 i_ret = DirectXCreateSurface( p_vout, &p_surface,
1324                                               p_vout->output.i_chroma,
1325                                               0 /* no overlay */,
1326                                               0 /* no back buffers */ );
1327             else
1328                 p_vout->p_sys->b_hw_yuv = VLC_FALSE;
1329         }
1330
1331         if( i_ret || !p_vout->p_sys->b_hw_yuv )
1332         {
1333             /* Our last choice is to use a plain RGB surface */
1334             DDPIXELFORMAT ddpfPixelFormat;
1335
1336             ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
1337             IDirectDrawSurface2_GetPixelFormat( p_vout->p_sys->p_display,
1338                                                 &ddpfPixelFormat );
1339
1340             if( ddpfPixelFormat.dwFlags & DDPF_RGB )
1341             {
1342                 switch( ddpfPixelFormat.dwRGBBitCount )
1343                 {
1344                 case 8: /* FIXME: set the palette */
1345                     p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2');
1346                     break;
1347                 case 15:
1348                     p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5');
1349                     break;
1350                 case 16:
1351                     p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
1352                     break;
1353                 case 24:
1354                     p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
1355                     break;
1356                 case 32:
1357                     p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
1358                     break;
1359                 default:
1360                     msg_Err( p_vout, "unknown screen depth" );
1361                     return VLC_EGENERIC;
1362                 }
1363                 p_vout->output.i_rmask = ddpfPixelFormat.dwRBitMask;
1364                 p_vout->output.i_gmask = ddpfPixelFormat.dwGBitMask;
1365                 p_vout->output.i_bmask = ddpfPixelFormat.dwBBitMask;
1366             }
1367
1368             p_vout->p_sys->b_hw_yuv = 0;
1369
1370             i_ret = DirectXCreateSurface( p_vout, &p_surface,
1371                                           p_vout->output.i_chroma,
1372                                           0 /* no overlay */,
1373                                           0 /* no back buffers */ );
1374
1375             if( i_ret && !p_vout->p_sys->b_use_sysmem )
1376             {
1377                 /* Give it a last try with b_use_sysmem enabled */
1378                 p_vout->p_sys->b_use_sysmem = 1;
1379
1380                 i_ret = DirectXCreateSurface( p_vout, &p_surface,
1381                                               p_vout->output.i_chroma,
1382                                               0 /* no overlay */,
1383                                               0 /* no back buffers */ );
1384             }
1385         }
1386
1387         if( i_ret == VLC_SUCCESS )
1388         {
1389             /* Allocate internal structure */
1390             p_pic[0].p_sys = malloc( sizeof( picture_sys_t ) );
1391             if( p_pic[0].p_sys == NULL )
1392             {
1393                 DirectXCloseSurface( p_vout, p_surface );
1394                 return VLC_ENOMEM;
1395             }
1396
1397             p_pic[0].p_sys->p_surface = p_pic[0].p_sys->p_front_surface
1398                 = p_surface;
1399
1400             I_OUTPUTPICTURES = 1;
1401
1402             msg_Dbg( p_vout, "created plain surface of chroma:%.4s",
1403                      (char *)&p_vout->output.i_chroma );
1404         }
1405     }
1406
1407
1408     /* Now that we've got all our direct-buffers, we can finish filling in the
1409      * picture_t structures */
1410     for( i = 0; i < I_OUTPUTPICTURES; i++ )
1411     {
1412         p_pic[i].i_status = DESTROYED_PICTURE;
1413         p_pic[i].i_type   = DIRECT_PICTURE;
1414         p_pic[i].pf_lock  = DirectXLockSurface;
1415         p_pic[i].pf_unlock = DirectXUnlockSurface;
1416         PP_OUTPUTPICTURE[i] = &p_pic[i];
1417
1418         if( DirectXLockSurface( p_vout, &p_pic[i] ) != VLC_SUCCESS )
1419         {
1420             /* AAARRGG */
1421             FreePictureVec( p_vout, p_pic, I_OUTPUTPICTURES );
1422             I_OUTPUTPICTURES = 0;
1423             msg_Err( p_vout, "cannot lock surface" );
1424             return VLC_EGENERIC;
1425         }
1426         DirectXUnlockSurface( p_vout, &p_pic[i] );
1427     }
1428
1429     msg_Dbg( p_vout, "End NewPictureVec (%s)",
1430              I_OUTPUTPICTURES ? "succeeded" : "failed" );
1431
1432     return VLC_SUCCESS;
1433 }
1434
1435 /*****************************************************************************
1436  * FreePicture: destroy a picture vector allocated with NewPictureVec
1437  *****************************************************************************
1438  *
1439  *****************************************************************************/
1440 static void FreePictureVec( vout_thread_t *p_vout, picture_t *p_pic,
1441                             int i_num_pics )
1442 {
1443     int i;
1444
1445     for( i = 0; i < i_num_pics; i++ )
1446     {
1447         DirectXCloseSurface( p_vout, p_pic[i].p_sys->p_front_surface );
1448
1449         for( i = 0; i < i_num_pics; i++ )
1450         {
1451             free( p_pic[i].p_sys );
1452         }
1453     }
1454
1455     p_vout->p_sys->p_current_surface = 0;
1456 }
1457
1458 /*****************************************************************************
1459  * UpdatePictureStruct: updates the internal data in the picture_t structure
1460  *****************************************************************************
1461  * This will setup stuff for use by the video_output thread
1462  *****************************************************************************/
1463 static int UpdatePictureStruct( vout_thread_t *p_vout, picture_t *p_pic,
1464                                 int i_chroma )
1465 {
1466     switch( p_vout->output.i_chroma )
1467     {
1468         case VLC_FOURCC('R','G','B','2'):
1469         case VLC_FOURCC('R','V','1','5'):
1470         case VLC_FOURCC('R','V','1','6'):
1471         case VLC_FOURCC('R','V','2','4'):
1472         case VLC_FOURCC('R','V','3','2'):
1473             p_pic->p->p_pixels = p_pic->p_sys->ddsd.lpSurface;
1474             p_pic->p->i_lines = p_vout->output.i_height;
1475             p_pic->p->i_pitch = p_pic->p_sys->ddsd.lPitch;
1476             switch( p_vout->output.i_chroma )
1477             {
1478                 case VLC_FOURCC('R','G','B','2'):
1479                     p_pic->p->i_pixel_pitch = 1;
1480                     break;
1481                 case VLC_FOURCC('R','V','1','5'):
1482                 case VLC_FOURCC('R','V','1','6'):
1483                     p_pic->p->i_pixel_pitch = 2;
1484                     break;
1485                 case VLC_FOURCC('R','V','2','4'):
1486                     p_pic->p->i_pixel_pitch = 3;
1487                     break;
1488                 case VLC_FOURCC('R','V','3','2'):
1489                     p_pic->p->i_pixel_pitch = 4;
1490                     break;
1491                 default:
1492                     return VLC_EGENERIC;
1493             }
1494             p_pic->p->i_visible_pitch = p_vout->output.i_width *
1495               p_pic->p->i_pixel_pitch;
1496             p_pic->i_planes = 1;
1497             break;
1498
1499         case VLC_FOURCC('Y','V','1','2'):
1500
1501             p_pic->Y_PIXELS = p_pic->p_sys->ddsd.lpSurface;
1502             p_pic->p[Y_PLANE].i_lines = p_vout->output.i_height;
1503             p_pic->p[Y_PLANE].i_pitch = p_pic->p_sys->ddsd.lPitch;
1504             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
1505             p_pic->p[Y_PLANE].i_visible_pitch = p_vout->output.i_width *
1506               p_pic->p[Y_PLANE].i_pixel_pitch;
1507
1508             p_pic->V_PIXELS =  p_pic->Y_PIXELS
1509               + p_pic->p[Y_PLANE].i_lines * p_pic->p[Y_PLANE].i_pitch;
1510             p_pic->p[V_PLANE].i_lines = p_vout->output.i_height / 2;
1511             p_pic->p[V_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2;
1512             p_pic->p[V_PLANE].i_pixel_pitch = 1;
1513             p_pic->p[V_PLANE].i_visible_pitch = p_vout->output.i_width / 2 *
1514               p_pic->p[V_PLANE].i_pixel_pitch;
1515
1516             p_pic->U_PIXELS = p_pic->V_PIXELS
1517               + p_pic->p[V_PLANE].i_lines * p_pic->p[V_PLANE].i_pitch;
1518             p_pic->p[U_PLANE].i_lines = p_vout->output.i_height / 2;
1519             p_pic->p[U_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2;
1520             p_pic->p[U_PLANE].i_pixel_pitch = 1;
1521             p_pic->p[U_PLANE].i_visible_pitch = p_vout->output.i_width / 2 *
1522               p_pic->p[U_PLANE].i_pixel_pitch;
1523
1524             p_pic->i_planes = 3;
1525             break;
1526
1527         case VLC_FOURCC('I','Y','U','V'):
1528
1529             p_pic->Y_PIXELS = p_pic->p_sys->ddsd.lpSurface;
1530             p_pic->p[Y_PLANE].i_lines = p_vout->output.i_height;
1531             p_pic->p[Y_PLANE].i_pitch = p_pic->p_sys->ddsd.lPitch;
1532             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
1533             p_pic->p[Y_PLANE].i_visible_pitch = p_vout->output.i_width *
1534               p_pic->p[Y_PLANE].i_pixel_pitch;
1535
1536             p_pic->U_PIXELS = p_pic->Y_PIXELS
1537               + p_pic->p[Y_PLANE].i_lines * p_pic->p[Y_PLANE].i_pitch;
1538             p_pic->p[U_PLANE].i_lines = p_vout->output.i_height / 2;
1539             p_pic->p[U_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2;
1540             p_pic->p[U_PLANE].i_pixel_pitch = 1;
1541             p_pic->p[U_PLANE].i_visible_pitch = p_vout->output.i_width / 2 *
1542               p_pic->p[U_PLANE].i_pixel_pitch;
1543
1544             p_pic->V_PIXELS =  p_pic->U_PIXELS
1545               + p_pic->p[U_PLANE].i_lines * p_pic->p[U_PLANE].i_pitch;
1546             p_pic->p[V_PLANE].i_lines = p_vout->output.i_height / 2;
1547             p_pic->p[V_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2;
1548             p_pic->p[V_PLANE].i_pixel_pitch = 1;
1549             p_pic->p[V_PLANE].i_visible_pitch = p_vout->output.i_width / 2 *
1550               p_pic->p[V_PLANE].i_pixel_pitch;
1551
1552             p_pic->i_planes = 3;
1553             break;
1554
1555         case VLC_FOURCC('Y','U','Y','2'):
1556
1557             p_pic->p->p_pixels = p_pic->p_sys->ddsd.lpSurface;
1558             p_pic->p->i_lines = p_vout->output.i_height;
1559             p_pic->p->i_pitch = p_pic->p_sys->ddsd.lPitch;
1560             p_pic->p->i_pixel_pitch = 2;
1561             p_pic->p->i_visible_pitch = p_vout->output.i_width *
1562               p_pic->p->i_pixel_pitch;
1563
1564             p_pic->i_planes = 1;
1565             break;
1566
1567         default:
1568             /* Unknown chroma, tell the guy to get lost */
1569             msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
1570                      p_vout->output.i_chroma,
1571                      (char*)&p_vout->output.i_chroma );
1572             return VLC_EGENERIC;
1573     }
1574
1575     return VLC_SUCCESS;
1576 }
1577
1578 /*****************************************************************************
1579  * DirectXGetDDrawCaps: Probe the capabilities of the hardware
1580  *****************************************************************************
1581  * It is nice to know which features are supported by the hardware so we can
1582  * find ways to optimize our rendering.
1583  *****************************************************************************/
1584 static void DirectXGetDDrawCaps( vout_thread_t *p_vout )
1585 {
1586     DDCAPS ddcaps;
1587     HRESULT dxresult;
1588
1589     /* This is just an indication of whether or not we'll support overlay,
1590      * but with this test we don't know if we support YUV overlay */
1591     memset( &ddcaps, 0, sizeof( DDCAPS ));
1592     ddcaps.dwSize = sizeof(DDCAPS);
1593     dxresult = IDirectDraw2_GetCaps( p_vout->p_sys->p_ddobject,
1594                                      &ddcaps, NULL );
1595     if(dxresult != DD_OK )
1596     {
1597         msg_Warn( p_vout, "cannot get caps" );
1598     }
1599     else
1600     {
1601         vlc_bool_t bHasOverlay, bHasOverlayFourCC, bCanDeinterlace,
1602              bHasColorKey, bCanStretch, bCanBltFourcc,
1603              bAlignBoundarySrc, bAlignBoundaryDest,
1604              bAlignSizeSrc, bAlignSizeDest;
1605
1606         /* Determine if the hardware supports overlay surfaces */
1607         bHasOverlay = (ddcaps.dwCaps & DDCAPS_OVERLAY) ? 1 : 0;
1608         /* Determine if the hardware supports overlay surfaces */
1609         bHasOverlayFourCC = (ddcaps.dwCaps & DDCAPS_OVERLAYFOURCC) ? 1 : 0;
1610         /* Determine if the hardware supports overlay deinterlacing */
1611         bCanDeinterlace = (ddcaps.dwCaps & DDCAPS2_CANFLIPODDEVEN) ? 1 : 0;
1612         /* Determine if the hardware supports colorkeying */
1613         bHasColorKey = (ddcaps.dwCaps & DDCAPS_COLORKEY) ? 1 : 0;
1614         /* Determine if the hardware supports scaling of the overlay surface */
1615         bCanStretch = (ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH) ? 1 : 0;
1616         /* Determine if the hardware supports color conversion during a blit */
1617         bCanBltFourcc = (ddcaps.dwCaps & DDCAPS_BLTFOURCC) ? 1 : 0;
1618         /* Determine overlay source boundary alignment */
1619         bAlignBoundarySrc = (ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYSRC) ? 1 : 0;
1620         /* Determine overlay destination boundary alignment */
1621         bAlignBoundaryDest = (ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYDEST) ? 1:0;
1622         /* Determine overlay destination size alignment */
1623         bAlignSizeSrc = (ddcaps.dwCaps & DDCAPS_ALIGNSIZESRC) ? 1 : 0;
1624         /* Determine overlay destination size alignment */
1625         bAlignSizeDest = (ddcaps.dwCaps & DDCAPS_ALIGNSIZEDEST) ? 1 : 0;
1626  
1627         msg_Dbg( p_vout, "DirectDraw Capabilities: overlay=%i yuvoverlay=%i "
1628                          "can_deinterlace_overlay=%i colorkey=%i stretch=%i "
1629                          "bltfourcc=%i",
1630                          bHasOverlay, bHasOverlayFourCC, bCanDeinterlace,
1631                          bHasColorKey, bCanStretch, bCanBltFourcc );
1632
1633         if( bAlignBoundarySrc || bAlignBoundaryDest ||
1634             bAlignSizeSrc || bAlignSizeDest )
1635         {
1636             if( bAlignBoundarySrc ) p_vout->p_sys->i_align_src_boundary =
1637                 ddcaps.dwAlignBoundarySrc;
1638             if( bAlignBoundaryDest ) p_vout->p_sys->i_align_dest_boundary =
1639                 ddcaps.dwAlignBoundaryDest;
1640             if( bAlignSizeDest ) p_vout->p_sys->i_align_src_size =
1641                 ddcaps.dwAlignSizeSrc;
1642             if( bAlignSizeDest ) p_vout->p_sys->i_align_dest_size =
1643                 ddcaps.dwAlignSizeDest;
1644
1645             msg_Dbg( p_vout, "align_boundary_src=%i,%i "
1646                      "align_boundary_dest=%i,%i "
1647                      "align_size_src=%i,%i align_size_dest=%i,%i",
1648                      bAlignBoundarySrc, p_vout->p_sys->i_align_src_boundary,
1649                      bAlignBoundaryDest, p_vout->p_sys->i_align_dest_boundary,
1650                      bAlignSizeSrc, p_vout->p_sys->i_align_src_size,
1651                      bAlignSizeDest, p_vout->p_sys->i_align_dest_size );
1652         }
1653
1654         /* Don't ask for troubles */
1655         if( !bCanBltFourcc ) p_vout->p_sys->b_hw_yuv = FALSE;
1656     }
1657 }
1658
1659 /*****************************************************************************
1660  * DirectXLockSurface: Lock surface and get picture data pointer
1661  *****************************************************************************
1662  * This function locks a surface and get the surface descriptor which amongst
1663  * other things has the pointer to the picture data.
1664  *****************************************************************************/
1665 static int DirectXLockSurface( vout_thread_t *p_vout, picture_t *p_pic )
1666 {
1667     HRESULT dxresult;
1668
1669     /* Lock the surface to get a valid pointer to the picture buffer */
1670     memset( &p_pic->p_sys->ddsd, 0, sizeof( DDSURFACEDESC ));
1671     p_pic->p_sys->ddsd.dwSize = sizeof(DDSURFACEDESC);
1672     dxresult = IDirectDrawSurface2_Lock( p_pic->p_sys->p_surface,
1673                                          NULL, &p_pic->p_sys->ddsd,
1674                                          DDLOCK_NOSYSLOCK | DDLOCK_WAIT,
1675                                          NULL );
1676     if( dxresult != DD_OK )
1677     {
1678         if( dxresult == DDERR_INVALIDPARAMS )
1679         {
1680             /* DirectX 3 doesn't support the DDLOCK_NOSYSLOCK flag, resulting
1681              * in an invalid params error */
1682             dxresult = IDirectDrawSurface2_Lock( p_pic->p_sys->p_surface, NULL,
1683                                              &p_pic->p_sys->ddsd,
1684                                              DDLOCK_WAIT, NULL);
1685         }
1686         if( dxresult == DDERR_SURFACELOST )
1687         {
1688             /* Your surface can be lost so be sure
1689              * to check this and restore it if needed */
1690
1691             /* When using overlays with back-buffers, we need to restore
1692              * the front buffer so the back-buffers get restored as well. */
1693             if( p_vout->p_sys->b_using_overlay  )
1694                 IDirectDrawSurface2_Restore( p_pic->p_sys->p_front_surface );
1695             else
1696                 IDirectDrawSurface2_Restore( p_pic->p_sys->p_surface );
1697
1698             dxresult = IDirectDrawSurface2_Lock( p_pic->p_sys->p_surface, NULL,
1699                                                  &p_pic->p_sys->ddsd,
1700                                                  DDLOCK_WAIT, NULL);
1701             if( dxresult == DDERR_SURFACELOST )
1702                 msg_Dbg( p_vout, "DirectXLockSurface: DDERR_SURFACELOST" );
1703         }
1704         if( dxresult != DD_OK )
1705         {
1706             return VLC_EGENERIC;
1707         }
1708     }
1709
1710     /* Now we have a pointer to the surface memory, we can update our picture
1711      * structure. */
1712     if( UpdatePictureStruct( p_vout, p_pic, p_vout->output.i_chroma )
1713         != VLC_SUCCESS )
1714     {
1715         DirectXUnlockSurface( p_vout, p_pic );
1716         return VLC_EGENERIC;
1717     }
1718     else
1719         return VLC_SUCCESS;
1720 }
1721
1722 /*****************************************************************************
1723  * DirectXUnlockSurface: Unlock a surface locked by DirectXLockSurface().
1724  *****************************************************************************/
1725 static int DirectXUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
1726 {
1727     /* Unlock the Surface */
1728     if( IDirectDrawSurface2_Unlock( p_pic->p_sys->p_surface, NULL ) == DD_OK )
1729         return VLC_SUCCESS;
1730     else
1731         return VLC_EGENERIC;
1732 }
1733
1734 /*****************************************************************************
1735  * DirectXFindColorkey: Finds out the 32bits RGB pixel value of the colorkey
1736  *****************************************************************************/
1737 static DWORD DirectXFindColorkey( vout_thread_t *p_vout, uint32_t i_color )
1738 {
1739     DDSURFACEDESC ddsd;
1740     HRESULT dxresult;
1741     COLORREF i_rgb = 0;
1742     uint32_t i_pixel_backup;
1743     HDC hdc;
1744
1745     ddsd.dwSize = sizeof(ddsd);
1746     dxresult = IDirectDrawSurface2_Lock( p_vout->p_sys->p_display, NULL,
1747                                          &ddsd, DDLOCK_WAIT, NULL );
1748     if( dxresult != DD_OK ) return 0;
1749
1750     i_pixel_backup = *(uint32_t *)ddsd.lpSurface;
1751
1752     switch( ddsd.ddpfPixelFormat.dwRGBBitCount )
1753     {
1754     case 4:
1755         *(uint8_t *)ddsd.lpSurface = 0x11;
1756         break;
1757     case 8:
1758         *(uint8_t *)ddsd.lpSurface = 0x01;
1759         break;
1760     case 16:
1761         *(uint16_t *)ddsd.lpSurface = 0x01;
1762         break;
1763     default:
1764         *(uint32_t *)ddsd.lpSurface = 0x01;
1765         break;
1766     }
1767
1768     IDirectDrawSurface2_Unlock( p_vout->p_sys->p_display, NULL );
1769
1770     if( IDirectDrawSurface2_GetDC( p_vout->p_sys->p_display, &hdc ) == DD_OK )
1771     {
1772         i_rgb = GetPixel( hdc, 0, 0 );
1773         IDirectDrawSurface2_ReleaseDC( p_vout->p_sys->p_display, hdc );
1774     }
1775
1776     ddsd.dwSize = sizeof(ddsd);
1777     dxresult = IDirectDrawSurface2_Lock( p_vout->p_sys->p_display, NULL,
1778                                          &ddsd, DDLOCK_WAIT, NULL );
1779     if( dxresult != DD_OK ) return i_rgb;
1780
1781     *(uint32_t *)ddsd.lpSurface = i_pixel_backup;
1782
1783     IDirectDrawSurface2_Unlock( p_vout->p_sys->p_display, NULL );
1784
1785     return i_rgb;
1786 }
1787
1788 /*****************************************************************************
1789  * object variables callbacks: a bunch of object variables are used by the
1790  * interfaces to interact with the vout.
1791  *****************************************************************************/
1792 static int OnTopCallback( vlc_object_t *p_this, char const *psz_cmd,
1793                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
1794 {
1795     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1796     p_vout->p_sys->b_on_top_change = VLC_TRUE;
1797     return VLC_SUCCESS;
1798 }
1799
1800 /*****************************************************************************
1801  * config variable callback
1802  *****************************************************************************/
1803 BOOL WINAPI DirectXEnumCallback2( GUID* p_guid, LPTSTR psz_desc,
1804                                   LPTSTR psz_drivername, VOID* p_context,
1805                                   HMONITOR hmon )
1806 {
1807     module_config_t *p_item = (module_config_t *)p_context;
1808
1809     p_item->ppsz_list =
1810         (char **)realloc( p_item->ppsz_list,
1811                           (p_item->i_list+2) * sizeof(char *) );
1812     p_item->ppsz_list_text =
1813         (char **)realloc( p_item->ppsz_list_text,
1814                           (p_item->i_list+2) * sizeof(char *) );
1815
1816     p_item->ppsz_list[p_item->i_list] = strdup( psz_drivername );
1817     p_item->ppsz_list_text[p_item->i_list] = NULL;
1818     p_item->i_list++;
1819     p_item->ppsz_list[p_item->i_list] = NULL;
1820     p_item->ppsz_list_text[p_item->i_list] = NULL;
1821
1822     return TRUE; /* Keep enumerating */
1823 }
1824
1825 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1826                                vlc_value_t newval, vlc_value_t oldval, void *d)
1827 {
1828     HRESULT (WINAPI *OurDirectDrawEnumerateEx)( LPDDENUMCALLBACKEXA, LPVOID,
1829                                                 DWORD );
1830     HINSTANCE hddraw_dll;
1831
1832     module_config_t *p_item;
1833     int i;
1834
1835     p_item = config_FindConfig( p_this, psz_name );
1836     if( !p_item ) return VLC_SUCCESS;
1837
1838     /* Clear-up the current list */
1839     if( p_item->i_list )
1840     {
1841         /* Keep the first entry */
1842         for( i = 1; i < p_item->i_list; i++ )
1843         {
1844             free( p_item->ppsz_list[i] );
1845             free( p_item->ppsz_list_text[i] );
1846         }
1847         /* TODO: Remove when no more needed */
1848         p_item->ppsz_list[i] = NULL;
1849         p_item->ppsz_list_text[i] = NULL;
1850     }
1851     p_item->i_list = 1;
1852
1853     /* Load direct draw DLL */
1854     hddraw_dll = LoadLibrary("DDRAW.DLL");
1855     if( hddraw_dll == NULL ) return VLC_SUCCESS;
1856
1857     OurDirectDrawEnumerateEx =
1858       (void *)GetProcAddress( hddraw_dll, "DirectDrawEnumerateExA" );
1859
1860     if( OurDirectDrawEnumerateEx )
1861     {
1862         /* Enumerate displays */
1863         OurDirectDrawEnumerateEx( DirectXEnumCallback2, p_item,
1864                                   DDENUM_ATTACHEDSECONDARYDEVICES );
1865     }
1866
1867     FreeLibrary( hddraw_dll );
1868
1869     return VLC_SUCCESS;
1870 }