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