Rename directx-wallpaper to video-wallpaper
[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 #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 static const char *const ppsz_dev[] = { "" };
159 static const char *const ppsz_dev_text[] = { N_("Default") };
160
161 vlc_module_begin ()
162     set_shortname( "DirectX" )
163     set_category( CAT_VIDEO )
164     set_subcategory( SUBCAT_VIDEO_VOUT )
165     add_bool( "directx-hw-yuv", true, NULL, HW_YUV_TEXT, HW_YUV_LONGTEXT,
166               true )
167     add_bool( "directx-use-sysmem", false, NULL, SYSMEM_TEXT, SYSMEM_LONGTEXT,
168               true )
169     add_bool( "directx-3buffering", true, NULL, TRIPLEBUF_TEXT,
170               TRIPLEBUF_LONGTEXT, true )
171
172     add_string( "directx-device", "", NULL, DEVICE_TEXT, DEVICE_LONGTEXT,
173                 true )
174         change_string_list( ppsz_dev, ppsz_dev_text, FindDevicesCallback )
175         change_action_add( FindDevicesCallback, N_("Refresh list") )
176
177     set_description( N_("DirectX (DirectDraw) video output") )
178     set_capability( "video output", 100 )
179     add_shortcut( "directx" )
180     set_callbacks( OpenVideo, CloseVideo )
181
182     /* FIXME: Hack to avoid unregistering our window class */
183     linked_with_a_crap_library_which_uses_atexit ()
184 vlc_module_end ()
185
186 #if 0 /* FIXME */
187     /* check if we registered a window class because we need to
188      * unregister it */
189     WNDCLASS wndclass;
190     if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) )
191         UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) );
192 #endif
193
194 /*****************************************************************************
195  * OpenVideo: allocate DirectX video thread output method
196  *****************************************************************************
197  * This function allocates and initialize the DirectX vout method.
198  *****************************************************************************/
199 static int OpenVideo( vlc_object_t *p_this )
200 {
201     vout_thread_t * p_vout = (vout_thread_t *)p_this;
202     vlc_value_t val;
203     HMODULE huser32;
204
205     /* Allocate structure */
206     p_vout->p_sys = calloc( 1, sizeof( vout_sys_t ) );
207     if( p_vout->p_sys == NULL )
208         return VLC_ENOMEM;
209     vlc_mutex_init( &p_vout->p_sys->lock );
210
211     /* Initialisations */
212     p_vout->pf_init = Init;
213     p_vout->pf_end = End;
214     p_vout->pf_manage = Manage;
215     p_vout->pf_render = NULL;
216     p_vout->pf_display = FirstDisplay;
217     p_vout->pf_control = Control;
218
219     if( CommonInit( p_vout ) )
220         goto error;
221
222     /* */
223     p_vout->p_sys->p_ddobject = NULL;
224     p_vout->p_sys->p_display = NULL;
225     p_vout->p_sys->p_current_surface = NULL;
226     p_vout->p_sys->p_clipper = NULL;
227     p_vout->p_sys->b_wallpaper = 0;
228
229     /* Multimonitor stuff */
230     p_vout->p_sys->hmonitor = NULL;
231     p_vout->p_sys->p_display_driver = NULL;
232     p_vout->p_sys->MonitorFromWindow = NULL;
233     p_vout->p_sys->GetMonitorInfo = NULL;
234     if( (huser32 = GetModuleHandle( _T("USER32") ) ) )
235     {
236         p_vout->p_sys->MonitorFromWindow = (HMONITOR (WINAPI *)( HWND, DWORD ))
237             GetProcAddress( huser32, _T("MonitorFromWindow") );
238         p_vout->p_sys->GetMonitorInfo =
239             GetProcAddress( huser32, _T("GetMonitorInfoW") );
240     }
241
242     var_Create( p_vout, "overlay", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
243     var_Create( p_vout, "directx-use-sysmem", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
244     var_Create( p_vout, "directx-hw-yuv", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
245     var_Create( p_vout, "directx-3buffering", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
246     var_Create( p_vout, "directx-device", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
247
248     /* Initialise DirectDraw */
249     if( DirectXInitDDraw( p_vout ) )
250     {
251         msg_Err( p_vout, "cannot initialize DirectX DirectDraw" );
252         goto error;
253     }
254
255     /* Create the directx display */
256     if( DirectXCreateDisplay( p_vout ) )
257     {
258         msg_Err( p_vout, "cannot initialize DirectX DirectDraw" );
259         goto error;
260     }
261
262     /* Variable to indicate if the window should be on top of others */
263     /* Trigger a callback right now */
264     var_Create( p_vout, "video-wallpaper", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
265     val.psz_string = _("Wallpaper");
266     var_Change( p_vout, "video-wallpaper", VLC_VAR_SETTEXT, &val, NULL );
267     var_AddCallback( p_vout, "video-wallpaper", WallpaperCallback, NULL );
268     var_TriggerCallback( p_vout, "video-wallpaper" );
269
270     return VLC_SUCCESS;
271
272  error:
273     CloseVideo( VLC_OBJECT(p_vout) );
274     return VLC_EGENERIC;
275 }
276
277 /*****************************************************************************
278  * Init: initialize DirectX video thread output method
279  *****************************************************************************
280  * This function create the directx surfaces needed by the output thread.
281  * It is called at the beginning of the thread.
282  *****************************************************************************/
283 static int Init( vout_thread_t *p_vout )
284 {
285     int i_chroma_backup;
286
287     /* Get a few default parameters */
288     p_vout->p_sys->b_using_overlay = var_GetBool( p_vout, "overlay" );
289     p_vout->p_sys->b_use_sysmem = var_GetBool( p_vout, "directx-use-sysmem" );
290     p_vout->p_sys->b_hw_yuv = var_GetBool( p_vout, "directx-hw-yuv" );
291     p_vout->p_sys->b_3buf_overlay = var_GetBool( p_vout, "directx-3buffering" );
292
293     /* Initialise DirectDraw if not already done.
294      * We do this here because on multi-monitor systems we may have to
295      * re-create the directdraw surfaces. */
296     if( !p_vout->p_sys->p_ddobject &&
297         DirectXInitDDraw( p_vout ) != VLC_SUCCESS )
298     {
299         msg_Err( p_vout, "cannot initialize DirectDraw" );
300         return VLC_EGENERIC;
301     }
302
303     /* Create the directx display */
304     if( !p_vout->p_sys->p_display &&
305         DirectXCreateDisplay( p_vout ) != VLC_SUCCESS )
306     {
307         msg_Err( p_vout, "cannot initialize DirectDraw" );
308         return VLC_EGENERIC;
309     }
310
311     /* Initialize the output structure.
312      * Since DirectDraw can do rescaling for us, stick to the default
313      * coordinates and aspect. */
314     p_vout->output.i_width  = p_vout->render.i_width;
315     p_vout->output.i_height = p_vout->render.i_height;
316     p_vout->output.i_aspect = p_vout->render.i_aspect;
317     p_vout->fmt_out = p_vout->fmt_in;
318     UpdateRects( p_vout, true );
319
320 #define MAX_DIRECTBUFFERS 1
321     /* Right now we use only 1 directbuffer because we don't want the
322      * video decoder to decode directly into direct buffers as they are
323      * created into video memory and video memory is _really_ slow */
324
325     /* Choose the chroma we will try first. */
326     switch( p_vout->render.i_chroma )
327     {
328         case VLC_CODEC_YUYV:
329             p_vout->output.i_chroma = VLC_CODEC_YUYV;
330             break;
331         case VLC_CODEC_UYVY:
332             p_vout->output.i_chroma = VLC_CODEC_UYVY;
333             break;
334         case VLC_CODEC_YVYU:
335             p_vout->output.i_chroma = VLC_CODEC_YVYU;
336             break;
337         case VLC_CODEC_I420:
338             p_vout->output.i_chroma = VLC_CODEC_I420;
339             break;
340         default:
341             msg_Dbg( p_vout, "use default chroma YV12 for render " \
342                              "chroma (%4.4s)",
343                              (char *)&p_vout->render.i_chroma);
344             p_vout->output.i_chroma = VLC_CODEC_YV12;
345             break;
346     }
347
348     NewPictureVec( p_vout, p_vout->p_picture );
349
350     i_chroma_backup = p_vout->output.i_chroma;
351
352     if( !I_OUTPUTPICTURES )
353     {
354         /* hmmm, it didn't work! Let's try commonly supported chromas */
355         if( p_vout->output.i_chroma != VLC_CODEC_I420 )
356         {
357             p_vout->output.i_chroma = VLC_CODEC_YV12;
358             NewPictureVec( p_vout, p_vout->p_picture );
359         }
360         if( !I_OUTPUTPICTURES )
361         {
362             /* hmmm, it still didn't work! Let's try another one */
363             p_vout->output.i_chroma = VLC_CODEC_YUYV;
364             NewPictureVec( p_vout, p_vout->p_picture );
365         }
366     }
367
368     if( !I_OUTPUTPICTURES )
369     {
370         /* If it still didn't work then don't try to use an overlay */
371         p_vout->output.i_chroma = i_chroma_backup;
372         p_vout->p_sys->b_using_overlay = false;
373         msg_Warn( p_vout, "Could not initialize directx overlay" ) ;
374         NewPictureVec( p_vout, p_vout->p_picture );
375     }
376
377     p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
378
379     /* Change the window title bar text */
380     const char *psz_fallback;
381     if( p_vout->p_sys->b_using_overlay )
382         psz_fallback = VOUT_TITLE " (hardware YUV overlay DirectX output)";
383     else if( p_vout->p_sys->b_hw_yuv )
384         psz_fallback = VOUT_TITLE " (hardware YUV DirectX output)";
385     else
386         psz_fallback = VOUT_TITLE " (software RGB DirectX output)";
387     EventThreadUpdateTitle( p_vout->p_sys->p_event, psz_fallback );
388     EventThreadUseOverlay( p_vout->p_sys->p_event, p_vout->p_sys->b_using_overlay );
389
390     return VLC_SUCCESS;
391 }
392
393 /*****************************************************************************
394  * End: terminate Sys video thread output method
395  *****************************************************************************
396  * Terminate an output method created by Create.
397  * It is called at the end of the thread.
398  *****************************************************************************/
399 static void End( vout_thread_t *p_vout )
400 {
401     FreePictureVec( p_vout, p_vout->p_picture, I_OUTPUTPICTURES );
402
403     DirectXCloseDisplay( p_vout );
404     DirectXCloseDDraw( p_vout );
405
406     return;
407 }
408
409 /*****************************************************************************
410  * CloseVideo: destroy Sys video thread output method
411  *****************************************************************************
412  * Terminate an output method created by Create
413  *****************************************************************************/
414 static void CloseVideo( vlc_object_t *p_this )
415 {
416     vout_thread_t * p_vout = (vout_thread_t *)p_this;
417
418     /* Make sure the wallpaper is restored */
419     var_DelCallback( p_vout, "video-wallpaper", WallpaperCallback, NULL );
420     SwitchWallpaperMode( p_vout, false );
421
422     CommonClean( p_vout );
423
424     vlc_mutex_destroy( &p_vout->p_sys->lock );
425     free( p_vout->p_sys );
426 }
427
428 /*****************************************************************************
429  * Manage: handle Sys events
430  *****************************************************************************
431  * This function should be called regularly by the video output thread.
432  * It returns a non null value if an error occurred.
433  *****************************************************************************/
434 static int Manage( vout_thread_t *p_vout )
435 {
436     CommonManage( p_vout );
437
438     /*
439      * Position Change
440      */
441     if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE )
442     {
443         p_vout->p_sys->i_changes &= ~DX_POSITION_CHANGE;
444
445         /* Check if we are still on the same monitor */
446         if( p_vout->p_sys->MonitorFromWindow &&
447             p_vout->p_sys->hmonitor !=
448                 p_vout->p_sys->MonitorFromWindow( p_vout->p_sys->hwnd,
449                                                   MONITOR_DEFAULTTONEAREST ) )
450         {
451             /* This will force the vout core to recreate the picture buffers */
452             p_vout->i_changes |= VOUT_PICTURE_BUFFERS_CHANGE;
453         }
454     }
455
456     if( p_vout->p_sys->i_changes & DX_WALLPAPER_CHANGE )
457     {
458         SwitchWallpaperMode( p_vout, !p_vout->p_sys->b_wallpaper );
459         p_vout->p_sys->i_changes &= ~DX_WALLPAPER_CHANGE;
460         DirectDrawUpdateOverlay( p_vout );
461     }
462
463     return VLC_SUCCESS;
464 }
465
466 /*****************************************************************************
467  * Display: displays previously rendered output
468  *****************************************************************************
469  * This function sends the currently rendered image to the display, wait until
470  * it is displayed and switch the two rendering buffers, preparing next frame.
471  *****************************************************************************/
472 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
473 {
474     HRESULT dxresult;
475
476     if( (p_vout->p_sys->p_display == NULL) )
477     {
478         msg_Warn( p_vout, "no display!" );
479         return;
480     }
481
482     /* Our surface can be lost so be sure to check this
483      * and restore it if need be */
484     if( IDirectDrawSurface2_IsLost( p_vout->p_sys->p_display )
485         == DDERR_SURFACELOST )
486     {
487         if( IDirectDrawSurface2_Restore( p_vout->p_sys->p_display ) == DD_OK &&
488             p_vout->p_sys->b_using_overlay )
489             DirectDrawUpdateOverlay( p_vout );
490     }
491
492     if( !p_vout->p_sys->b_using_overlay )
493     {
494         DDBLTFX  ddbltfx;
495
496         /* We ask for the "NOTEARING" option */
497         memset( &ddbltfx, 0, sizeof(DDBLTFX) );
498         ddbltfx.dwSize = sizeof(DDBLTFX);
499         ddbltfx.dwDDFX = DDBLTFX_NOTEARING;
500
501         /* Blit video surface to display */
502         dxresult = IDirectDrawSurface2_Blt( p_vout->p_sys->p_display,
503                                             &p_vout->p_sys->rect_dest_clipped,
504                                             p_pic->p_sys->p_surface,
505                                             &p_vout->p_sys->rect_src_clipped,
506                                             DDBLT_ASYNC, &ddbltfx );
507         if( dxresult != DD_OK )
508         {
509             msg_Warn( p_vout, "could not blit surface (error %li)", dxresult );
510             return;
511         }
512     }
513     else /* using overlay */
514     {
515         /* Flip the overlay buffers if we are using back buffers */
516         if( p_pic->p_sys->p_front_surface == p_pic->p_sys->p_surface )
517         {
518             return;
519         }
520
521         dxresult = IDirectDrawSurface2_Flip( p_pic->p_sys->p_front_surface,
522                                              NULL, DDFLIP_WAIT );
523         if( dxresult != DD_OK )
524         {
525             msg_Warn( p_vout, "could not flip overlay (error %li)", dxresult );
526         }
527
528         /* set currently displayed pic */
529         p_vout->p_sys->p_current_surface = p_pic->p_sys->p_front_surface;
530
531         /* Lock surface to get all the required info */
532         if( DirectXLockSurface( p_vout, p_pic ) )
533         {
534             /* AAARRGG */
535             msg_Warn( p_vout, "cannot lock surface" );
536             return;
537         }
538         DirectXUnlockSurface( p_vout, p_pic );
539     }
540 }
541
542 /*
543 ** this function is only used once when the first picture is received
544 ** this function will show the video window once video is ready for
545 ** display.
546 */
547
548 static void FirstDisplay( vout_thread_t *p_vout, picture_t *p_pic )
549 {
550     /* get initial picture rendered on overlay surface */
551     Display(p_vout, p_pic);
552
553     IDirectDraw_WaitForVerticalBlank(p_vout->p_sys->p_ddobject,
554             DDWAITVB_BLOCKBEGIN, NULL);
555
556     if( p_vout->p_sys->b_using_overlay )
557     {
558         HBRUSH brush = CreateSolidBrush( p_vout->p_sys->i_rgb_colorkey );
559         /* set the colorkey as the backgound brush for the video window */
560         SetClassLongPtr( p_vout->p_sys->hvideownd, GCLP_HBRBACKGROUND, (LONG_PTR)brush );
561     }
562     /*
563     ** Video window is initially hidden, show it now since we got a
564     ** picture to show.
565     */
566     SetWindowPos( p_vout->p_sys->hvideownd, NULL, 0, 0, 0, 0,
567         SWP_ASYNCWINDOWPOS|
568         SWP_FRAMECHANGED|
569         SWP_SHOWWINDOW|
570         SWP_NOMOVE|
571         SWP_NOSIZE|
572         SWP_NOZORDER );
573
574     /* use and restores proper display function for further pictures */
575     p_vout->pf_display = Display;
576 }
577
578 /* following functions are local */
579
580 /*****************************************************************************
581  * DirectXEnumCallback: Device enumeration
582  *****************************************************************************
583  * This callback function is called by DirectDraw once for each
584  * available DirectDraw device.
585  *****************************************************************************/
586 BOOL WINAPI DirectXEnumCallback( GUID* p_guid, LPTSTR psz_desc,
587                                  LPTSTR psz_drivername, VOID* p_context,
588                                  HMONITOR hmon )
589 {
590     vout_thread_t *p_vout = (vout_thread_t *)p_context;
591     char *psz_device;
592
593     msg_Dbg( p_vout, "DirectXEnumCallback: %s, %s", psz_desc, psz_drivername );
594
595     if( hmon )
596     {
597         psz_device = var_GetString( p_vout, "directx-device" );
598
599         if( ( !psz_device || !*psz_device ) &&
600             hmon == p_vout->p_sys->hmonitor )
601         {
602             free( psz_device );
603         }
604         else if( strcmp( psz_drivername, psz_device ) == 0 )
605         {
606             MONITORINFO monitor_info;
607             monitor_info.cbSize = sizeof( MONITORINFO );
608
609             if( p_vout->p_sys->GetMonitorInfo( hmon, &monitor_info ) )
610             {
611                 RECT rect;
612
613                 /* Move window to the right screen */
614                 GetWindowRect( p_vout->p_sys->hwnd, &rect );
615                 if( !IntersectRect( &rect, &rect, &monitor_info.rcWork ) )
616                 {
617                     rect.left = monitor_info.rcWork.left;
618                     rect.top = monitor_info.rcWork.top;
619                     msg_Dbg( p_vout, "DirectXEnumCallback: setting window "
620                              "position to %ld,%ld", rect.left, rect.top );
621                     SetWindowPos( p_vout->p_sys->hwnd, NULL,
622                                   rect.left, rect.top, 0, 0,
623                                   SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
624                 }
625             }
626
627             p_vout->p_sys->hmonitor = hmon;
628             free( psz_device );
629         }
630         else
631         {
632             free( psz_device );
633             return TRUE; /* Keep enumerating */
634         }
635
636         msg_Dbg( p_vout, "selecting %s, %s", psz_desc, psz_drivername );
637         p_vout->p_sys->p_display_driver = malloc( sizeof(GUID) );
638         if( p_vout->p_sys->p_display_driver )
639             memcpy( p_vout->p_sys->p_display_driver, p_guid, sizeof(GUID) );
640     }
641
642     return TRUE; /* Keep enumerating */
643 }
644
645 /*****************************************************************************
646  * DirectXInitDDraw: Takes care of all the DirectDraw initialisations
647  *****************************************************************************
648  * This function initialise and allocate resources for DirectDraw.
649  *****************************************************************************/
650 static int DirectXInitDDraw( vout_thread_t *p_vout )
651 {
652     HRESULT dxresult;
653     HRESULT (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *);
654     HRESULT (WINAPI *OurDirectDrawEnumerateEx)( LPDDENUMCALLBACKEXA, LPVOID,
655                                                 DWORD );
656     LPDIRECTDRAW p_ddobject;
657
658     msg_Dbg( p_vout, "DirectXInitDDraw" );
659
660     /* Load direct draw DLL */
661     p_vout->p_sys->hddraw_dll = LoadLibrary(_T("DDRAW.DLL"));
662     if( p_vout->p_sys->hddraw_dll == NULL )
663     {
664         msg_Warn( p_vout, "DirectXInitDDraw failed loading ddraw.dll" );
665         goto error;
666     }
667
668     OurDirectDrawCreate =
669       (void *)GetProcAddress( p_vout->p_sys->hddraw_dll,
670                               _T("DirectDrawCreate") );
671     if( OurDirectDrawCreate == NULL )
672     {
673         msg_Err( p_vout, "DirectXInitDDraw failed GetProcAddress" );
674         goto error;
675     }
676
677     OurDirectDrawEnumerateEx =
678       (void *)GetProcAddress( p_vout->p_sys->hddraw_dll,
679                               _T("DirectDrawEnumerateExW") );
680
681     if( OurDirectDrawEnumerateEx && p_vout->p_sys->MonitorFromWindow )
682     {
683         char *psz_device;
684
685         psz_device = var_GetString( p_vout, "directx-device" );
686         if( psz_device )
687         {
688             msg_Dbg( p_vout, "directx-device: %s", psz_device );
689             free( psz_device );
690         }
691
692         p_vout->p_sys->hmonitor =
693             p_vout->p_sys->MonitorFromWindow( p_vout->p_sys->hwnd,
694                                               MONITOR_DEFAULTTONEAREST );
695
696         /* Enumerate displays */
697         OurDirectDrawEnumerateEx( DirectXEnumCallback, p_vout,
698                                   DDENUM_ATTACHEDSECONDARYDEVICES );
699     }
700
701     /* Initialize DirectDraw now */
702     dxresult = OurDirectDrawCreate( p_vout->p_sys->p_display_driver,
703                                     &p_ddobject, NULL );
704     if( dxresult != DD_OK )
705     {
706         msg_Err( p_vout, "DirectXInitDDraw cannot initialize DDraw" );
707         goto error;
708     }
709
710     /* Get the IDirectDraw2 interface */
711     dxresult = IDirectDraw_QueryInterface( p_ddobject, &IID_IDirectDraw2,
712                                         &p_vout->p_sys->p_ddobject );
713     /* Release the unused interface */
714     IDirectDraw_Release( p_ddobject );
715     if( dxresult != DD_OK )
716     {
717         msg_Err( p_vout, "cannot get IDirectDraw2 interface" );
718         goto error;
719     }
720
721     /* Set DirectDraw Cooperative level, ie what control we want over Windows
722      * display */
723     dxresult = IDirectDraw2_SetCooperativeLevel( p_vout->p_sys->p_ddobject,
724                                                  NULL, DDSCL_NORMAL );
725     if( dxresult != DD_OK )
726     {
727         msg_Err( p_vout, "cannot set direct draw cooperative level" );
728         goto error;
729     }
730
731     /* Get the size of the current display device */
732     if( p_vout->p_sys->hmonitor && p_vout->p_sys->GetMonitorInfo )
733     {
734         MONITORINFO monitor_info;
735         monitor_info.cbSize = sizeof( MONITORINFO );
736         p_vout->p_sys->GetMonitorInfo( p_vout->p_sys->hmonitor,
737                                        &monitor_info );
738         p_vout->p_sys->rect_display = monitor_info.rcMonitor;
739     }
740     else
741     {
742         p_vout->p_sys->rect_display.left = 0;
743         p_vout->p_sys->rect_display.top = 0;
744         p_vout->p_sys->rect_display.right  = GetSystemMetrics(SM_CXSCREEN);
745         p_vout->p_sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
746     }
747
748     msg_Dbg( p_vout, "screen dimensions (%lix%li,%lix%li)",
749              p_vout->p_sys->rect_display.left,
750              p_vout->p_sys->rect_display.top,
751              p_vout->p_sys->rect_display.right,
752              p_vout->p_sys->rect_display.bottom );
753
754     /* Probe the capabilities of the hardware */
755     DirectXGetDDrawCaps( p_vout );
756
757     msg_Dbg( p_vout, "End DirectXInitDDraw" );
758     return VLC_SUCCESS;
759
760  error:
761     if( p_vout->p_sys->p_ddobject )
762         IDirectDraw2_Release( p_vout->p_sys->p_ddobject );
763     if( p_vout->p_sys->hddraw_dll )
764         FreeLibrary( p_vout->p_sys->hddraw_dll );
765     p_vout->p_sys->hddraw_dll = NULL;
766     p_vout->p_sys->p_ddobject = NULL;
767     return VLC_EGENERIC;
768 }
769
770 /*****************************************************************************
771  * DirectXCreateDisplay: create the DirectDraw display.
772  *****************************************************************************
773  * Create and initialize display according to preferences specified in the vout
774  * thread fields.
775  *****************************************************************************/
776 static int DirectXCreateDisplay( vout_thread_t *p_vout )
777 {
778     HRESULT              dxresult;
779     DDSURFACEDESC        ddsd;
780     LPDIRECTDRAWSURFACE  p_display;
781
782     msg_Dbg( p_vout, "DirectXCreateDisplay" );
783
784     /* Now get the primary surface. This surface is what you actually see
785      * on your screen */
786     memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
787     ddsd.dwSize = sizeof(DDSURFACEDESC);
788     ddsd.dwFlags = DDSD_CAPS;
789     ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
790
791     dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
792                                            &ddsd, &p_display, NULL );
793     if( dxresult != DD_OK )
794     {
795         msg_Err( p_vout, "cannot get primary surface (error %li)", dxresult );
796         return VLC_EGENERIC;
797     }
798
799     dxresult = IDirectDrawSurface_QueryInterface( p_display,
800                                          &IID_IDirectDrawSurface2,
801                                          &p_vout->p_sys->p_display );
802     /* Release the old interface */
803     IDirectDrawSurface_Release( p_display );
804     if ( dxresult != DD_OK )
805     {
806         msg_Err( p_vout, "cannot query IDirectDrawSurface2 interface "
807                          "(error %li)", dxresult );
808         return VLC_EGENERIC;
809     }
810
811     /* The clipper will be used only in non-overlay mode */
812     DirectXCreateClipper( p_vout );
813
814     /* Make sure the colorkey will be painted */
815     p_vout->p_sys->i_colorkey = 1;
816     p_vout->p_sys->i_rgb_colorkey =
817         DirectXFindColorkey( p_vout, &p_vout->p_sys->i_colorkey );
818
819     UpdateRects( p_vout, true );
820
821     return VLC_SUCCESS;
822 }
823
824 /*****************************************************************************
825  * DirectXCreateClipper: Create a clipper that will be used when blitting the
826  *                       RGB surface to the main display.
827  *****************************************************************************
828  * This clipper prevents us to modify by mistake anything on the screen
829  * which doesn't belong to our window. For example when a part of our video
830  * window is hidden by another window.
831  *****************************************************************************/
832 static int DirectXCreateClipper( vout_thread_t *p_vout )
833 {
834     HRESULT dxresult;
835
836     msg_Dbg( p_vout, "DirectXCreateClipper" );
837
838     /* Create the clipper */
839     dxresult = IDirectDraw2_CreateClipper( p_vout->p_sys->p_ddobject, 0,
840                                            &p_vout->p_sys->p_clipper, NULL );
841     if( dxresult != DD_OK )
842     {
843         msg_Warn( p_vout, "cannot create clipper (error %li)", dxresult );
844         goto error;
845     }
846
847     /* Associate the clipper to the window */
848     dxresult = IDirectDrawClipper_SetHWnd( p_vout->p_sys->p_clipper, 0,
849                                            p_vout->p_sys->hvideownd );
850     if( dxresult != DD_OK )
851     {
852         msg_Warn( p_vout, "cannot attach clipper to window (error %li)",
853                           dxresult );
854         goto error;
855     }
856
857     /* associate the clipper with the surface */
858     dxresult = IDirectDrawSurface_SetClipper(p_vout->p_sys->p_display,
859                                              p_vout->p_sys->p_clipper);
860     if( dxresult != DD_OK )
861     {
862         msg_Warn( p_vout, "cannot attach clipper to surface (error %li)",
863                           dxresult );
864         goto error;
865     }
866
867     return VLC_SUCCESS;
868
869  error:
870     if( p_vout->p_sys->p_clipper )
871     {
872         IDirectDrawClipper_Release( p_vout->p_sys->p_clipper );
873     }
874     p_vout->p_sys->p_clipper = NULL;
875     return VLC_EGENERIC;
876 }
877
878 /*****************************************************************************
879  * DirectXCreateSurface: create an YUV overlay or RGB surface for the video.
880  *****************************************************************************
881  * The best method of display is with an YUV overlay because the YUV->RGB
882  * conversion is done in hardware.
883  * You can also create a plain RGB surface.
884  * ( Maybe we could also try an RGB overlay surface, which could have hardware
885  * scaling and which would also be faster in window mode because you don't
886  * need to do any blitting to the main display...)
887  *****************************************************************************/
888 static int DirectXCreateSurface( vout_thread_t *p_vout,
889                                  LPDIRECTDRAWSURFACE2 *pp_surface_final,
890                                  int i_chroma, int b_overlay,
891                                  int i_backbuffers )
892 {
893     HRESULT dxresult;
894     LPDIRECTDRAWSURFACE p_surface;
895     DDSURFACEDESC ddsd;
896
897     /* Create the video surface */
898     if( b_overlay )
899     {
900         /* Now try to create the YUV overlay surface.
901          * This overlay will be displayed on top of the primary surface.
902          * A color key is used to determine whether or not the overlay will be
903          * displayed, ie the overlay will be displayed in place of the primary
904          * surface wherever the primary surface will have this color.
905          * The video window has been created with a background of this color so
906          * the overlay will be only displayed on top of this window */
907
908         memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
909         ddsd.dwSize = sizeof(DDSURFACEDESC);
910         ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
911         ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
912         ddsd.ddpfPixelFormat.dwFourCC = i_chroma;
913         ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
914         ddsd.dwFlags |= (i_backbuffers ? DDSD_BACKBUFFERCOUNT : 0);
915         ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
916         ddsd.ddsCaps.dwCaps |= (i_backbuffers ? DDSCAPS_COMPLEX | DDSCAPS_FLIP
917                                 : 0 );
918         ddsd.dwHeight = p_vout->render.i_height;
919         ddsd.dwWidth = p_vout->render.i_width;
920         ddsd.dwBackBufferCount = i_backbuffers;
921
922         dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
923                                                &ddsd, &p_surface, NULL );
924         if( dxresult != DD_OK )
925         {
926             *pp_surface_final = NULL;
927             return VLC_EGENERIC;
928         }
929     }
930
931     if( !b_overlay )
932     {
933         bool b_rgb_surface =
934             ( i_chroma == VLC_CODEC_RGB8 )
935           || ( i_chroma == VLC_CODEC_RGB15 )
936            || ( i_chroma == VLC_CODEC_RGB16 )
937             || ( i_chroma == VLC_CODEC_RGB24 )
938              || ( i_chroma == VLC_CODEC_RGB32 );
939
940         memset( &ddsd, 0, sizeof( DDSURFACEDESC ) );
941         ddsd.dwSize = sizeof(DDSURFACEDESC);
942         ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
943         ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH | DDSD_CAPS;
944         ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
945         ddsd.dwHeight = p_vout->render.i_height;
946         ddsd.dwWidth = p_vout->render.i_width;
947
948         if( p_vout->p_sys->b_use_sysmem )
949             ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
950         else
951             ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
952
953         if( !b_rgb_surface )
954         {
955             ddsd.dwFlags |= DDSD_PIXELFORMAT;
956             ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
957             ddsd.ddpfPixelFormat.dwFourCC = i_chroma;
958         }
959
960         dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
961                                                &ddsd, &p_surface, NULL );
962         if( dxresult != DD_OK )
963         {
964             *pp_surface_final = NULL;
965             return VLC_EGENERIC;
966         }
967     }
968
969     /* Now that the surface is created, try to get a newer DirectX interface */
970     dxresult = IDirectDrawSurface_QueryInterface( p_surface,
971                                      &IID_IDirectDrawSurface2,
972                                      (LPVOID *)pp_surface_final );
973     IDirectDrawSurface_Release( p_surface );    /* Release the old interface */
974     if ( dxresult != DD_OK )
975     {
976         msg_Err( p_vout, "cannot query IDirectDrawSurface2 interface "
977                          "(error %li)", dxresult );
978         *pp_surface_final = NULL;
979         return VLC_EGENERIC;
980     }
981
982     if( b_overlay )
983     {
984         /* Check the overlay is useable as some graphics cards allow creating
985          * several overlays but only one can be used at one time. */
986         p_vout->p_sys->p_current_surface = *pp_surface_final;
987         if( DirectDrawUpdateOverlay( p_vout ) != VLC_SUCCESS )
988         {
989             IDirectDrawSurface2_Release( *pp_surface_final );
990             *pp_surface_final = NULL;
991             msg_Err( p_vout, "overlay unuseable (might already be in use)" );
992             return VLC_EGENERIC;
993         }
994     }
995
996     return VLC_SUCCESS;
997 }
998
999 /*****************************************************************************
1000  * DirectDrawUpdateOverlay: Move or resize overlay surface on video display.
1001  *****************************************************************************
1002  * This function is used to move or resize an overlay surface on the screen.
1003  * Ususally the overlay is moved by the user and thus, by a move or resize
1004  * event (in Manage).
1005  *****************************************************************************/
1006 int DirectDrawUpdateOverlay( vout_thread_t *p_vout )
1007 {
1008     DDOVERLAYFX     ddofx;
1009     DWORD           dwFlags;
1010     HRESULT         dxresult;
1011     RECT            rect_src = p_vout->p_sys->rect_src_clipped;
1012     RECT            rect_dest = p_vout->p_sys->rect_dest_clipped;
1013
1014     if( !p_vout->p_sys->b_using_overlay ) return VLC_EGENERIC;
1015
1016     if( p_vout->p_sys->b_wallpaper )
1017     {
1018         unsigned i_x, i_y, i_width, i_height;
1019
1020         rect_src.left = p_vout->fmt_out.i_x_offset;
1021         rect_src.top = p_vout->fmt_out.i_y_offset;
1022         rect_src.right = rect_src.left + p_vout->fmt_out.i_visible_width;
1023         rect_src.bottom = rect_src.top + p_vout->fmt_out.i_visible_height;
1024
1025         rect_dest = p_vout->p_sys->rect_display;
1026         vout_PlacePicture( p_vout, rect_dest.right, rect_dest.bottom,
1027                            &i_x, &i_y, &i_width, &i_height );
1028
1029         rect_dest.left += i_x;
1030         rect_dest.right = rect_dest.left + i_width;
1031         rect_dest.top += i_y;
1032         rect_dest.bottom = rect_dest.top + i_height;
1033     }
1034
1035     vlc_mutex_lock( &p_vout->p_sys->lock );
1036     if( p_vout->p_sys->p_current_surface == NULL )
1037     {
1038         vlc_mutex_unlock( &p_vout->p_sys->lock );
1039         return VLC_EGENERIC;
1040     }
1041
1042     /* The new window dimensions should already have been computed by the
1043      * caller of this function */
1044
1045     /* Position and show the overlay */
1046     memset(&ddofx, 0, sizeof(DDOVERLAYFX));
1047     ddofx.dwSize = sizeof(DDOVERLAYFX);
1048     ddofx.dckDestColorkey.dwColorSpaceLowValue = p_vout->p_sys->i_colorkey;
1049     ddofx.dckDestColorkey.dwColorSpaceHighValue = p_vout->p_sys->i_colorkey;
1050
1051     dwFlags = DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE;
1052
1053     dxresult = IDirectDrawSurface2_UpdateOverlay(
1054                    p_vout->p_sys->p_current_surface,
1055                    &rect_src, p_vout->p_sys->p_display, &rect_dest,
1056                    dwFlags, &ddofx );
1057
1058     vlc_mutex_unlock( &p_vout->p_sys->lock );
1059
1060     if(dxresult != DD_OK)
1061     {
1062         msg_Warn( p_vout, "DirectDrawUpdateOverlay cannot move/resize overlay" );
1063         return VLC_EGENERIC;
1064     }
1065
1066     return VLC_SUCCESS;
1067 }
1068
1069 /*****************************************************************************
1070  * DirectXCloseDDraw: Release the DDraw object allocated by DirectXInitDDraw
1071  *****************************************************************************
1072  * This function returns all resources allocated by DirectXInitDDraw.
1073  *****************************************************************************/
1074 static void DirectXCloseDDraw( vout_thread_t *p_vout )
1075 {
1076     msg_Dbg( p_vout, "DirectXCloseDDraw" );
1077     if( p_vout->p_sys->p_ddobject != NULL )
1078     {
1079         IDirectDraw2_Release(p_vout->p_sys->p_ddobject);
1080         p_vout->p_sys->p_ddobject = NULL;
1081     }
1082
1083     if( p_vout->p_sys->hddraw_dll != NULL )
1084     {
1085         FreeLibrary( p_vout->p_sys->hddraw_dll );
1086         p_vout->p_sys->hddraw_dll = NULL;
1087     }
1088
1089     free( p_vout->p_sys->p_display_driver );
1090     p_vout->p_sys->p_display_driver = NULL;
1091
1092     p_vout->p_sys->hmonitor = NULL;
1093 }
1094
1095 /*****************************************************************************
1096  * DirectXCloseDisplay: close and reset the DirectX display device
1097  *****************************************************************************
1098  * This function returns all resources allocated by DirectXCreateDisplay.
1099  *****************************************************************************/
1100 static void DirectXCloseDisplay( vout_thread_t *p_vout )
1101 {
1102     msg_Dbg( p_vout, "DirectXCloseDisplay" );
1103
1104     if( p_vout->p_sys->p_clipper != NULL )
1105     {
1106         msg_Dbg( p_vout, "DirectXCloseDisplay clipper" );
1107         IDirectDrawClipper_Release( p_vout->p_sys->p_clipper );
1108         p_vout->p_sys->p_clipper = NULL;
1109     }
1110
1111     if( p_vout->p_sys->p_display != NULL )
1112     {
1113         msg_Dbg( p_vout, "DirectXCloseDisplay display" );
1114         IDirectDrawSurface2_Release( p_vout->p_sys->p_display );
1115         p_vout->p_sys->p_display = NULL;
1116     }
1117 }
1118
1119 /*****************************************************************************
1120  * DirectXCloseSurface: close the YUV overlay or RGB surface.
1121  *****************************************************************************
1122  * This function returns all resources allocated for the surface.
1123  *****************************************************************************/
1124 static void DirectXCloseSurface( vout_thread_t *p_vout,
1125                                  LPDIRECTDRAWSURFACE2 p_surface )
1126 {
1127     msg_Dbg( p_vout, "DirectXCloseSurface" );
1128     if( p_surface != NULL )
1129     {
1130         IDirectDrawSurface2_Release( p_surface );
1131     }
1132 }
1133
1134 /*****************************************************************************
1135  * NewPictureVec: allocate a picture
1136  * FIXME? make it work for i_num_pic pictures...
1137  *****************************************************************************
1138  * Returns 0 on success, -1 otherwise
1139  *****************************************************************************/
1140 static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic )
1141 {
1142     int i;
1143     int i_ret = VLC_SUCCESS;
1144     LPDIRECTDRAWSURFACE2 p_surface;
1145
1146     msg_Dbg( p_vout, "NewPictureVec overlay:%s chroma:%.4s",
1147              p_vout->p_sys->b_using_overlay ? "yes" : "no",
1148              (char *)&p_vout->output.i_chroma );
1149
1150     I_OUTPUTPICTURES = 0;
1151
1152     /* First we try to use an YUV overlay surface.
1153      * The overlay surface that we create won't be used to decode directly
1154      * into it because accessing video memory directly is way to slow (remember
1155      * that pictures are decoded macroblock per macroblock). Instead the video
1156      * will be decoded in picture buffers in system memory which will then be
1157      * memcpy() to the overlay surface. */
1158     if( p_vout->p_sys->b_using_overlay )
1159     {
1160         /* Triple buffering rocks! it doesn't have any processing overhead
1161          * (you don't have to wait for the vsync) and provides for a very nice
1162          * video quality (no tearing). */
1163         if( p_vout->p_sys->b_3buf_overlay )
1164             i_ret = DirectXCreateSurface( p_vout, &p_surface,
1165                                           p_vout->output.i_chroma,
1166                                           p_vout->p_sys->b_using_overlay,
1167                                           2 /* number of backbuffers */ );
1168
1169         if( !p_vout->p_sys->b_3buf_overlay || i_ret != VLC_SUCCESS )
1170         {
1171             /* Try to reduce the number of backbuffers */
1172             i_ret = DirectXCreateSurface( p_vout, &p_surface,
1173                                           p_vout->output.i_chroma,
1174                                           p_vout->p_sys->b_using_overlay,
1175                                           0 /* number of backbuffers */ );
1176         }
1177
1178         if( i_ret == VLC_SUCCESS )
1179         {
1180             DDSCAPS dds_caps;
1181             picture_t front_pic;
1182             picture_sys_t front_pic_sys;
1183             front_pic.p_sys = &front_pic_sys;
1184
1185             /* Allocate internal structure */
1186             p_pic[0].p_sys = malloc( sizeof( picture_sys_t ) );
1187             if( p_pic[0].p_sys == NULL )
1188             {
1189                 DirectXCloseSurface( p_vout, p_surface );
1190                 return VLC_ENOMEM;
1191             }
1192
1193             /* set front buffer */
1194             p_pic[0].p_sys->p_front_surface = p_surface;
1195
1196             /* Get the back buffer */
1197             memset( &dds_caps, 0, sizeof( DDSCAPS ) );
1198             dds_caps.dwCaps = DDSCAPS_BACKBUFFER;
1199             if( DD_OK != IDirectDrawSurface2_GetAttachedSurface(
1200                                                 p_surface, &dds_caps,
1201                                                 &p_pic[0].p_sys->p_surface ) )
1202             {
1203                 msg_Warn( p_vout, "NewPictureVec could not get back buffer" );
1204                 /* front buffer is the same as back buffer */
1205                 p_pic[0].p_sys->p_surface = p_surface;
1206             }
1207
1208
1209             p_vout->p_sys->p_current_surface = front_pic.p_sys->p_surface =
1210                 p_pic[0].p_sys->p_front_surface;
1211
1212             /* Reset the front buffer memory */
1213             if( DirectXLockSurface( p_vout, &front_pic ) == VLC_SUCCESS )
1214             {
1215                 int i,j;
1216                 for( i = 0; i < front_pic.i_planes; i++ )
1217                     for( j = 0; j < front_pic.p[i].i_visible_lines; j++)
1218                         memset( front_pic.p[i].p_pixels + j *
1219                                 front_pic.p[i].i_pitch, 127,
1220                                 front_pic.p[i].i_visible_pitch );
1221
1222                 DirectXUnlockSurface( p_vout, &front_pic );
1223             }
1224
1225             DirectDrawUpdateOverlay( p_vout );
1226             I_OUTPUTPICTURES = 1;
1227             msg_Dbg( p_vout, "YUV overlay created successfully" );
1228         }
1229     }
1230
1231     /* As we can't have an overlay, we'll try to create a plain offscreen
1232      * surface. This surface will reside in video memory because there's a
1233      * better chance then that we'll be able to use some kind of hardware
1234      * acceleration like rescaling, blitting or YUV->RGB conversions.
1235      * We then only need to blit this surface onto the main display when we
1236      * want to display it */
1237     if( !p_vout->p_sys->b_using_overlay )
1238     {
1239         if( p_vout->p_sys->b_hw_yuv )
1240         {
1241             DWORD i_codes;
1242             DWORD *pi_codes;
1243             bool b_result = false;
1244
1245             /* Check if the chroma is supported first. This is required
1246              * because a few buggy drivers don't mind creating the surface
1247              * even if they don't know about the chroma. */
1248             if( IDirectDraw2_GetFourCCCodes( p_vout->p_sys->p_ddobject,
1249                                              &i_codes, NULL ) == DD_OK )
1250             {
1251                 pi_codes = malloc( i_codes * sizeof(DWORD) );
1252                 if( pi_codes && IDirectDraw2_GetFourCCCodes(
1253                     p_vout->p_sys->p_ddobject, &i_codes, pi_codes ) == DD_OK )
1254                 {
1255                     for( i = 0; i < (int)i_codes; i++ )
1256                     {
1257                         if( p_vout->output.i_chroma == pi_codes[i] )
1258                         {
1259                             b_result = true;
1260                             break;
1261                         }
1262                     }
1263                 }
1264                 free( pi_codes );
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 = xrealloc( p_item->ppsz_list,
1812                           (p_item->i_list+2) * sizeof(char *) );
1813     p_item->ppsz_list_text = xrealloc( p_item->ppsz_list_text,
1814                           (p_item->i_list+2) * sizeof(char *) );
1815
1816     p_item->ppsz_list[p_item->i_list] = strdup( psz_drivername );
1817     p_item->ppsz_list_text[p_item->i_list] = NULL;
1818     p_item->i_list++;
1819     p_item->ppsz_list[p_item->i_list] = NULL;
1820     p_item->ppsz_list_text[p_item->i_list] = NULL;
1821
1822     return TRUE; /* Keep enumerating */
1823 }
1824
1825 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
1826                                vlc_value_t newval, vlc_value_t oldval, void *d)
1827 {
1828     VLC_UNUSED( newval ); VLC_UNUSED( oldval ); VLC_UNUSED( d );
1829
1830     HRESULT (WINAPI *OurDirectDrawEnumerateEx)( LPDDENUMCALLBACKEXA, LPVOID,
1831                                                 DWORD );
1832     HINSTANCE hddraw_dll;
1833
1834     module_config_t *p_item;
1835     int i;
1836
1837     p_item = config_FindConfig( p_this, psz_name );
1838     if( !p_item ) return VLC_SUCCESS;
1839
1840     /* Clear-up the current list */
1841     if( p_item->i_list )
1842     {
1843         /* Keep the first entry */
1844         for( i = 1; i < p_item->i_list; i++ )
1845         {
1846             free( p_item->ppsz_list[i] );
1847             free( p_item->ppsz_list_text[i] );
1848         }
1849         /* TODO: Remove when no more needed */
1850         p_item->ppsz_list[i] = NULL;
1851         p_item->ppsz_list_text[i] = NULL;
1852     }
1853     p_item->i_list = 1;
1854
1855     /* Load direct draw DLL */
1856     hddraw_dll = LoadLibrary(_T("DDRAW.DLL"));
1857     if( hddraw_dll == NULL ) return VLC_SUCCESS;
1858
1859     OurDirectDrawEnumerateEx =
1860       (void *)GetProcAddress( hddraw_dll, _T("DirectDrawEnumerateExW") );
1861
1862     if( OurDirectDrawEnumerateEx )
1863     {
1864         /* Enumerate displays */
1865         OurDirectDrawEnumerateEx( DirectXEnumCallback2, p_item,
1866                                   DDENUM_ATTACHEDSECONDARYDEVICES );
1867     }
1868
1869     FreeLibrary( hddraw_dll );
1870
1871     /* Signal change to the interface */
1872     p_item->b_dirty = true;
1873
1874     return VLC_SUCCESS;
1875 }
1876
1877 static int WallpaperCallback( vlc_object_t *p_this, char const *psz_cmd,
1878                               vlc_value_t oldval, vlc_value_t newval,
1879                               void *p_data )
1880 {
1881     VLC_UNUSED( psz_cmd ); VLC_UNUSED( oldval ); VLC_UNUSED( p_data );
1882     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1883
1884     if( (newval.b_bool && !p_vout->p_sys->b_wallpaper) ||
1885         (!newval.b_bool && p_vout->p_sys->b_wallpaper) )
1886     {
1887         playlist_t *p_playlist = pl_Hold( p_vout );
1888
1889         if( p_playlist )
1890         {
1891             /* Modify playlist as well because the vout might have to be
1892              * restarted */
1893             var_Create( p_playlist, "video-wallpaper", VLC_VAR_BOOL );
1894             var_Set( p_playlist, "video-wallpaper", newval );
1895             pl_Release( p_vout );
1896         }
1897
1898         p_vout->p_sys->i_changes |= DX_WALLPAPER_CHANGE;
1899     }
1900
1901     return VLC_SUCCESS;
1902 }
1903
1904 /*****************************************************************************
1905  * SetPalette: sets an 8 bpp palette
1906  *****************************************************************************/
1907 static void SetPalette( vout_thread_t *p_vout,
1908                         uint16_t *red, uint16_t *green, uint16_t *blue )
1909 {
1910     VLC_UNUSED( red ); VLC_UNUSED( green );VLC_UNUSED( blue );
1911     msg_Err( p_vout, "FIXME: SetPalette unimplemented" );
1912 }