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