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