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