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