]> git.sesse.net Git - vlc/blob - plugins/directx/vout_directx.c
bc6d3b74fba38f2bf93fa9946a50f2724dc1dfbc
[vlc] / plugins / directx / vout_directx.c
1 /*****************************************************************************
2  * vout_directx.c: Windows DirectX video output display method
3  *****************************************************************************
4  * Copyright (C) 1998, 1999, 2000 VideoLAN
5  * $Id: vout_directx.c,v 1.3 2001/06/08 20:03:15 sam Exp $
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 #define MODULE_NAME directx
25 #include "modules_inner.h"
26
27 /* This is a list of what needs to be fixed:
28  *
29  * When the option: "Display full screen when dragging window" is enabled in
30  * Windows display properties, the overlay surface coordinates won't be updated
31  * (but it won't crash anymore ;-) I know where the problem is in the code, but * I just don't know yet of a nice way to fix it.
32  *
33  * When you move part of the video window outside the physical display, the
34  * overlay surface coordinates are not updated anymore. This comes from the
35  * directdraw UpdateOverlay function which doesn't like negative coordinates.
36  *
37  * For now, this plugin only works when YUV overlay is supported (which it
38  * should be nowadays on most of the video cards under Windows)...
39  *
40  * The overlay doesn't use double-buffering.
41  *
42  * Port this plugin to Video Output IV
43  */
44
45 /*****************************************************************************
46  * Preamble
47  *
48  *****************************************************************************/
49 #include "defs.h"
50
51 #include <errno.h>                                                 /* ENOMEM */
52 #include <stdlib.h>                                                /* free() */
53 #include <string.h>                                            /* strerror() */
54
55 #include <windows.h>
56 #include <windowsx.h>
57 #include <directx.h>
58
59 #include "config.h"
60 #include "common.h"
61 #include "threads.h"
62 #include "mtime.h"
63 #include "tests.h"
64 #include "netutils.h"
65
66 #include "video.h"
67 #include "video_output.h"
68
69 #include "intf_msg.h"
70 #include "interface.h"
71 #include "main.h"
72
73 #include "modules.h"
74 #include "modules_export.h"
75
76 /*****************************************************************************
77  * vout_sys_t: video output DirectX method descriptor
78  *****************************************************************************
79  * This structure is part of the video output thread descriptor.
80  * It describes the DirectX specific properties of an output thread.
81  *****************************************************************************/
82 typedef struct vout_sys_s
83 {
84
85     LPDIRECTDRAW         p_ddobject;                    /* DirectDraw object */
86     LPDIRECTDRAWSURFACE  p_display;                        /* display device */
87     LPDIRECTDRAWSURFACE  p_overlay;                        /* overlay device */
88     LPDIRECTDRAWCLIPPER  p_clipper;                               /* clipper */
89     HBRUSH               hbrush;           /* window backgound brush (color) */
90     HWND                 hwnd;                  /* Handle of the main window */
91
92     int         i_image_width;                  /* size of the decoded image */
93     int         i_image_height;
94     int         i_window_width;               /* size of the displayed image */
95     int         i_window_height;
96
97     int         i_colorkey;          /* colorkey used to display the overlay */
98  
99     boolean_t   b_display_enabled;
100     boolean_t   b_overlay;
101     boolean_t   b_cursor;
102
103     boolean_t   b_cursor_autohidden;
104     mtime_t     i_lastmoved;
105
106     char       *p_windx_buf[2];                        /* Buffer information */
107
108 } vout_sys_t;
109
110 /*****************************************************************************
111  * Local prototypes.
112  *****************************************************************************/
113 static int  vout_Probe     ( probedata_t *p_data );
114 static int  vout_Create    ( struct vout_thread_s * );
115 static int  vout_Init      ( struct vout_thread_s * );
116 static void vout_End       ( struct vout_thread_s * );
117 static void vout_Destroy   ( struct vout_thread_s * );
118 static int  vout_Manage    ( struct vout_thread_s * );
119 static void vout_Display   ( struct vout_thread_s * );
120 static void vout_SetPalette( p_vout_thread_t p_vout, u16 *red, u16 *green,
121                              u16 *blue, u16 *transp );
122
123 static int  WinDXCreateWindow     ( vout_thread_t *p_vout );
124 static int  WinDXInitDDraw        ( vout_thread_t *p_vout );
125 static int  WinDXCreateDisplay    ( vout_thread_t *p_vout );
126 static int  WinDXCreateYUVOverlay ( vout_thread_t *p_vout );
127 static int  WinDXUpdateOverlay    ( vout_thread_t *p_vout );
128 static void WinDXCloseDDraw       ( vout_thread_t *p_vout );
129 static void WinDXCloseWindow      ( vout_thread_t *p_vout );
130 static void WinDXCloseDisplay     ( vout_thread_t *p_vout );
131 static void WinDXCloseYUVOverlay  ( vout_thread_t *p_vout );
132
133 /*****************************************************************************
134  * Functions exported as capabilities. They are declared as static so that
135  * we don't pollute the namespace too much.
136  *****************************************************************************/
137 void _M( vout_getfunctions )( function_list_t * p_function_list )
138 {
139     p_function_list->pf_probe = vout_Probe;
140     p_function_list->functions.vout.pf_create     = vout_Create;
141     p_function_list->functions.vout.pf_init       = vout_Init;
142     p_function_list->functions.vout.pf_end        = vout_End;
143     p_function_list->functions.vout.pf_destroy    = vout_Destroy;
144     p_function_list->functions.vout.pf_manage     = vout_Manage;
145     p_function_list->functions.vout.pf_display    = vout_Display;
146     p_function_list->functions.vout.pf_setpalette = vout_SetPalette;
147 }
148
149 /*****************************************************************************
150  * vout_Probe: probe the video driver and return a score
151  *****************************************************************************
152  * This function tries to initialize Windows DirectX and returns a score to
153  * the plugin manager so that it can select the best plugin.
154  *****************************************************************************/
155 static int vout_Probe( probedata_t *p_data )
156 {
157     if( TestMethod( VOUT_METHOD_VAR, "directx" ) )
158     {
159         return( 999 );
160     }
161
162     return( 400 );
163 }
164
165 /*****************************************************************************
166  * vout_Create: allocate DirectX video thread output method
167  *****************************************************************************
168  * This function allocates and initialize the DirectX vout method.
169  *****************************************************************************/
170 static int vout_Create( vout_thread_t *p_vout )
171 {
172     /* Allocate structure */
173     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
174     if( p_vout->p_sys == NULL )
175     {
176         intf_ErrMsg( "vout error: can't create p_sys (%s)", strerror(ENOMEM) );
177         return( 1 );
178     }
179
180     /* Initialisations */
181     p_vout->p_sys->p_ddobject = NULL;
182     p_vout->p_sys->p_display = NULL;
183     p_vout->p_sys->p_overlay = NULL;
184     p_vout->p_sys->p_clipper = NULL;
185     p_vout->p_sys->hbrush = INVALID_HANDLE_VALUE;
186     p_vout->p_sys->hwnd = INVALID_HANDLE_VALUE;
187
188     p_vout->p_sys->b_cursor = 1; /* TODO should be done with a main_GetInt.. */
189
190     p_vout->p_sys->b_cursor_autohidden = 0;
191     p_vout->p_sys->b_display_enabled = 0;
192     p_vout->p_sys->i_lastmoved = mdate();
193
194     p_vout->b_fullscreen = main_GetIntVariable( VOUT_FULLSCREEN_VAR,
195                                                 VOUT_FULLSCREEN_DEFAULT );
196     p_vout->p_sys->b_overlay = main_GetIntVariable( VOUT_OVERLAY_VAR,
197                                                     VOUT_OVERLAY_DEFAULT );
198     p_vout->p_sys->i_window_width = main_GetIntVariable( VOUT_WIDTH_VAR,
199                                                          VOUT_WIDTH_DEFAULT );
200     p_vout->p_sys->i_window_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
201                                                          VOUT_HEIGHT_DEFAULT );
202     /* We don't know yet the dimensions of the video so the best guess is to
203      * pick the same as the window */
204     p_vout->p_sys->i_image_width = p_vout->p_sys->i_window_width;
205     p_vout->p_sys->i_image_height = p_vout->p_sys->i_window_height;
206
207     /* Create a window for the video */
208     /* Creating a window under Windows also initializes the thread's event
209      * message qeue */
210     if( WinDXCreateWindow( p_vout ) )
211     {
212         intf_ErrMsg( "vout error: can't create window" );
213         free( p_vout->p_sys );
214         return ( 1 );
215     }
216
217     /* Initialise DirectDraw */
218     if( WinDXInitDDraw( p_vout ) )
219     {
220         intf_ErrMsg( "vout error: can't initialise DirectDraw" );
221         WinDXCloseWindow( p_vout );
222         free( p_vout->p_sys );
223         return ( 1 );
224     }
225
226     /* create the directx display */
227     if( WinDXCreateDisplay( p_vout ) )
228     {
229         intf_ErrMsg( "vout error: can't initialise DirectDraw" );
230         WinDXCloseDDraw( p_vout );
231         WinDXCloseWindow( p_vout );
232         free( p_vout->p_sys );
233         return ( 1 );
234     }
235
236     return( 0 );
237 }
238
239 /*****************************************************************************
240  * vout_Init: initialize DirectX video thread output method
241  *****************************************************************************
242  *
243  *****************************************************************************/
244 static int vout_Init( vout_thread_t *p_vout )
245 {
246     return( 0 );
247 }
248
249 /*****************************************************************************
250  * vout_End: terminate Sys video thread output method
251  *****************************************************************************
252  * Terminate an output method created by vout_Create.
253  * It is called at the end of the thread.
254  *****************************************************************************/
255 static void vout_End( vout_thread_t *p_vout )
256 {
257     return;
258 }
259
260 /*****************************************************************************
261  * vout_Destroy: destroy Sys video thread output method
262  *****************************************************************************
263  * Terminate an output method created by vout_Create
264  *****************************************************************************/
265 static void vout_Destroy( vout_thread_t *p_vout )
266 {
267     intf_WarnMsg( 3, "vout: vout_Destroy" );
268     WinDXCloseDisplay( p_vout );
269     WinDXCloseDDraw( p_vout );
270     WinDXCloseWindow( p_vout );
271
272     if( p_vout->p_sys != NULL )
273     {
274         free( p_vout->p_sys );
275         p_vout->p_sys = NULL;
276     }
277
278 }
279
280 /*****************************************************************************
281  * vout_Manage: handle Sys events
282  *****************************************************************************
283  * This function should be called regularly by video output thread. It returns
284  * a non null value if an error occured.
285  *****************************************************************************/
286 static int vout_Manage( vout_thread_t *p_vout )
287 {
288     MSG             msg;
289     WINDOWPLACEMENT window_placement;
290     boolean_t       b_dispatch_msg = TRUE;
291
292     while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
293     {
294         if( GetMessage(&msg, NULL, 0, 0) >= 0 )
295         {
296             switch( msg.message )
297             {
298
299                 case WM_CLOSE:
300                     intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_CLOSE" );
301                     p_vout->b_die = 1;
302                     break;
303
304                 case WM_QUIT:
305                     intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_QUIT" );
306                     p_main->p_intf->b_die = 1;
307                     break;
308
309                 case WM_MOVE:
310                     intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_MOVE" );
311                     if( !p_vout->b_need_render )
312                     {
313                         p_vout->i_changes |= VOUT_SIZE_CHANGE;
314                     }
315                     /* don't create a never ending loop */
316                     b_dispatch_msg = FALSE;
317                     break;
318
319                 case WM_APP:
320                     intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_APP" );
321                     if( !p_vout->b_need_render )
322                     {
323                         p_vout->i_changes |= VOUT_SIZE_CHANGE;
324                     }
325                     /* don't create a never ending loop */
326                     b_dispatch_msg = FALSE;
327                     break;
328
329                 case WM_PAINT:
330                     intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_PAINT" );
331                     break;
332
333                 case WM_ERASEBKGND:
334                     intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_ERASEBKGND" );
335                     break;
336
337                 case WM_MOUSEMOVE:
338                     intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_MOUSEMOVE" );
339                     if( p_vout->p_sys->b_cursor )
340                     {
341                     if( p_vout->p_sys->b_cursor_autohidden )
342                         {
343                             p_vout->p_sys->b_cursor_autohidden = 0;
344                             p_vout->p_sys->i_lastmoved = mdate();
345                             ShowCursor( TRUE );
346                         }
347                         else
348                         {
349                             p_vout->p_sys->i_lastmoved = mdate();
350                         }
351                     }               
352                     break;
353
354                 case WM_KEYDOWN:
355                     /* the key events are first processed here. The next
356                      * message processed by this main message loop will be the
357                      * char translation of the key event */
358                     intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_KEYDOWN" );
359                     switch( msg.wParam )
360                     {
361                         case VK_ESCAPE:
362                         case VK_F12:
363                             p_main->p_intf->b_die = 1;
364                             break;
365                     }
366                     TranslateMessage(&msg);
367                     b_dispatch_msg = FALSE;
368                     break;
369
370                 case WM_CHAR:
371                     intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_CHAR" );
372                     switch( msg.wParam )
373                     {
374                         case 'q':
375                         case 'Q':
376                             p_main->p_intf->b_die = 1;
377                             break;
378
379                         case 'f':
380                         case 'F':
381                             p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
382                             break;
383
384                         case '0': network_ChannelJoin( 0 ); break;
385                         case '1': network_ChannelJoin( 1 ); break;
386                         case '2': network_ChannelJoin( 2 ); break;
387                         case '3': network_ChannelJoin( 3 ); break;
388                         case '4': network_ChannelJoin( 4 ); break;
389                         case '5': network_ChannelJoin( 5 ); break;
390                         case '6': network_ChannelJoin( 6 ); break;
391                         case '7': network_ChannelJoin( 7 ); break;
392                         case '8': network_ChannelJoin( 8 ); break;
393                         case '9': network_ChannelJoin( 9 ); break;
394
395                         default:
396                             if( intf_ProcessKey( p_main->p_intf,
397                                                  (char )msg.wParam ) )
398                             {
399                                intf_DbgMsg( "unhandled key '%c' (%i)",
400                                             (char)msg.wParam, msg.wParam );
401                             }
402                             break;
403                     }
404
405                 default:
406                     intf_WarnMsg( 3, "vout: WinDX vout_Manage WM Default %i",
407                                   msg.message );
408                 break;
409             }
410
411             /* don't create a never ending loop */
412             if( b_dispatch_msg )
413             {
414                 TranslateMessage(&msg);
415                 DispatchMessage(&msg);
416             }
417             b_dispatch_msg = TRUE;
418
419         }
420         else
421         {
422             return( 1 );
423         }
424
425     }
426
427
428     /*
429      * Size Change 
430      */
431     if( p_vout->i_changes & VOUT_SIZE_CHANGE )
432     {
433         intf_WarnMsg( 3, "vout: WinDX vout_Manage Size Change" );
434         WinDXUpdateOverlay( p_vout );
435         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
436     }
437
438     /*
439      * Fullscreen change
440      */
441     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
442     {
443         p_vout->b_fullscreen = ! p_vout->b_fullscreen;
444
445         /* We need to switch between Maximized and Normal sized window */
446         window_placement.length = sizeof(WINDOWPLACEMENT);
447         GetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
448         if( p_vout->b_fullscreen )
449         {
450             /* Maximized window */
451             window_placement.showCmd = SW_SHOWMAXIMIZED;
452             /* Change window style, no borders and no title bar */
453             SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, 0 );
454
455         }
456         else
457         {
458             /* Normal window */
459             window_placement.showCmd = SW_SHOWNORMAL;
460             /* Change window style, borders and title bar */
461             SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE,
462                            WS_OVERLAPPEDWINDOW | WS_SIZEBOX | WS_VISIBLE );
463         }
464
465         SetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
466         /*WinDXUpdateOverlay( p_vout );*/
467
468         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
469     }
470
471     /*
472      * Pointer change
473      */
474     if( ! p_vout->p_sys->b_cursor_autohidden &&
475         ( mdate() - p_vout->p_sys->i_lastmoved > 2000000 ) )
476     {
477         /* Hide the mouse automatically */
478         p_vout->p_sys->b_cursor_autohidden = 1;
479         ShowCursor( FALSE );
480     }
481
482     if( p_vout->i_changes & VOUT_CURSOR_CHANGE )
483     {
484         p_vout->p_sys->b_cursor = ! p_vout->p_sys->b_cursor;
485
486         ShowCursor( p_vout->p_sys->b_cursor &&
487                      ! p_vout->p_sys->b_cursor_autohidden );
488
489         p_vout->i_changes &= ~VOUT_CURSOR_CHANGE;
490     }
491
492     return( 0 );
493 }
494
495 /*****************************************************************************
496  * vout_SetPalette: sets an 8 bpp palette
497  *****************************************************************************
498  * This function sets the palette given as an argument. It does not return
499  * anything, but could later send information on which colors it was unable
500  * to set.
501  *****************************************************************************/
502 static void vout_SetPalette( p_vout_thread_t p_vout, u16 *red, u16 *green,
503                          u16 *blue, u16 *transp)
504 {
505     /* Nothing yet */
506     return;
507 }
508
509 /*****************************************************************************
510  * vout_Display: displays previously rendered output
511  *****************************************************************************
512  * This function send the currently rendered image to the display, wait until
513  * it is displayed and switch the two rendering buffer, preparing next frame.
514  *****************************************************************************/
515 static void vout_Display( vout_thread_t *p_vout )
516 {
517     DDSURFACEDESC ddsd;
518     HRESULT       dxresult;
519     int           i;
520     int           i_image_width  = p_vout->p_rendered_pic->i_width;
521     int           i_image_height = p_vout->p_rendered_pic->i_height;
522
523
524     if( (p_vout->p_sys->p_display == NULL) )
525     {
526         intf_WarnMsg( 3, "vout error: WinDX no display!!" );
527         return;
528     }
529
530     /* The first time this function is called it enables the display */
531     p_vout->p_sys->b_display_enabled = 1;
532
533     if( p_vout->b_need_render )
534     {
535         /* Nothing yet */
536     }
537     else
538     {
539         /*
540          * p_vout->p_rendered_pic->p_y/u/v contains the YUV buffers to
541          * render
542          */
543         /* TODO: support for streams other than 4:2:0 */
544
545         /* if the size of the decoded pictures has changed then we close the
546          * YUVOverlay (which doesn't have the right size anymore). */
547         if( p_vout->p_sys->i_image_width != i_image_width
548             || p_vout->p_sys->i_image_height != i_image_height )
549         {
550             intf_WarnMsg( 3, "vout: WinDX overlay size changed" );
551             p_vout->p_sys->i_image_width = i_image_width;
552             p_vout->p_sys->i_image_height = i_image_height;
553             WinDXCloseYUVOverlay( p_vout );
554         }
555
556         if( p_vout->p_sys->p_overlay == NULL )
557         {
558             intf_WarnMsg( 3, "vout: WinDX no overlay, open one..." );
559             if( WinDXCreateYUVOverlay( p_vout ) )
560             {
561                 intf_WarnMsg( 3, "vout: WinDX cannot open a new overlay !!" );
562                 return;
563             }
564             /* Display the Overlay */
565             p_vout->p_sys->b_display_enabled = 1;
566             WinDXUpdateOverlay( p_vout );
567         }
568
569         /* Lock the overlay surface */
570         memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
571         ddsd.dwSize = sizeof(DDSURFACEDESC);
572         dxresult = IDirectDrawSurface_Lock(p_vout->p_sys->p_overlay, NULL,
573                                            &ddsd, DDLOCK_NOSYSLOCK, NULL);
574         if ( dxresult == DDERR_SURFACELOST )
575         {
576             /* Your surface can be lost (thanks to windows) so be sure
577              * to check this and restore it if needed */
578             dxresult = IDirectDrawSurface_Restore( p_vout->p_sys->p_overlay );
579             dxresult = IDirectDrawSurface_Lock( p_vout->p_sys->p_overlay,
580                                                 NULL, &ddsd, DDLOCK_NOSYSLOCK
581                                                 | DDLOCK_WAIT, NULL);
582         }
583         if( dxresult != DD_OK )
584         {
585             intf_WarnMsg( 3, "vout: WinDX could not lock the surface" );
586             return;
587         }
588
589         /* Now we can do the actual image copy.
590          * The copy has to be done line by line because of the special case
591          * when the Pitch does not equal the width of the picture */
592         for( i=0; i < ddsd.dwHeight/2; i++)
593         {
594 #ifdef NONAMELESSUNION
595             /* copy Y, we copy two lines at once */
596             memcpy(ddsd.lpSurface + i*2*ddsd.u1.lPitch,
597                    p_vout->p_rendered_pic->p_y + i*2*i_image_width,
598                    i_image_width);
599             memcpy(ddsd.lpSurface + (i*2+1)*ddsd.u1.lPitch,
600                    p_vout->p_rendered_pic->p_y + (i*2+1)*i_image_width,
601                    i_image_width);
602             /* then V */
603             memcpy((ddsd.lpSurface + ddsd.dwHeight * ddsd.u1.lPitch)
604                       + i * ddsd.u1.lPitch/2,
605                    p_vout->p_rendered_pic->p_v + i*i_image_width/2,
606                    i_image_width/2);
607             /* and U */
608             memcpy((ddsd.lpSurface + ddsd.dwHeight * ddsd.u1.lPitch)
609                       + (ddsd.dwHeight * ddsd.u1.lPitch/4)
610                       + i * ddsd.u1.lPitch/2,
611                    p_vout->p_rendered_pic->p_u + i*i_image_width/2,
612                    i_image_width/2);
613 #else
614             /* copy Y, we copy two lines at once */
615             memcpy(ddsd.lpSurface + i*2*ddsd.lPitch,
616                    p_vout->p_rendered_pic->p_y + i*2*i_image_width,
617                    i_image_width);
618             memcpy(ddsd.lpSurface + (i*2+1)*ddsd.lPitch,
619                    p_vout->p_rendered_pic->p_y + (i*2+1)*i_image_width,
620                    i_image_width);
621             /* then V */
622             memcpy((ddsd.lpSurface + ddsd.dwHeight * ddsd.lPitch)
623                       + i * ddsd.lPitch/2,
624                    p_vout->p_rendered_pic->p_v + i*i_image_width/2,
625                    i_image_width/2);
626             /* and U */
627             memcpy((ddsd.lpSurface + ddsd.dwHeight * ddsd.lPitch)
628                       + (ddsd.dwHeight * ddsd.lPitch/4)
629                       + i * ddsd.lPitch/2,
630                    p_vout->p_rendered_pic->p_u + i*i_image_width/2,
631                    i_image_width/2);
632 #endif /* NONAMELESSUNION */
633
634         }
635
636         /* Unlock the Surface */
637         dxresult = IDirectDrawSurface_Unlock(p_vout->p_sys->p_overlay,
638                                              ddsd.lpSurface );
639
640     }
641
642 }
643
644
645 /* following functions are local */
646
647
648 /*****************************************************************************
649  * WinDXEventProc: This is the window event processing function.
650  *****************************************************************************
651  * On Windows, when you create a window you have to attach an event processing
652  * function to it. The aim of this function is to manage "Queued Messages" and
653  * "Nonqueued Messages".
654  * Queued Messages are those picked up and retransmitted by vout_Manage
655  * (using the GetMessage function).
656  * Nonqueued Messages are those that Windows will send directly to this
657  * function (like WM_DESTROY, WM_WINDOWPOSCHANGED...)
658  *****************************************************************************/
659 long FAR PASCAL WinDXEventProc( HWND hwnd, UINT message,
660                                 WPARAM wParam, LPARAM lParam )
661 {
662     switch( message )
663     {
664
665     case WM_ACTIVATE:
666         intf_WarnMsg( 3, "vout: WinDX WinProc WM_ACTIVED" );
667         break;
668
669     case WM_CREATE:
670         intf_WarnMsg( 3, "vout: WinDX WinProc WM_CREATE" );
671         break;
672
673     /* the user wants to close the window */
674     case WM_CLOSE:
675         intf_WarnMsg( 3, "vout: WinDX WinProc WM_CLOSE" );
676         break;
677
678     /* the window has been closed so shut down everything now */
679     case WM_DESTROY:
680         intf_WarnMsg( 3, "vout: WinDX WinProc WM_DESTROY" );
681         PostQuitMessage( 0 );
682         break;
683
684     case WM_SYSCOMMAND:
685         switch (wParam)
686         {
687             case SC_SCREENSAVE:                     /* catch the screensaver */
688             case SC_MONITORPOWER:              /* catch the monitor turn-off */
689             intf_WarnMsg( 3, "vout: WinDX WinProc WM_SYSCOMMAND" );
690             return 0;                      /* this stops them from happening */
691         }
692         break;
693
694     case WM_MOVE:
695         intf_WarnMsg( 3, "vout: WinDX WinProc WM_MOVE" );
696         break;
697
698     case WM_SIZE:
699         intf_WarnMsg( 3, "vout: WinDX WinProc WM_SIZE" );
700         break;
701
702     case WM_MOVING:
703         intf_WarnMsg( 3, "vout: WinDX WinProc WM_MOVING" );
704         break;
705
706     case WM_SIZING:
707         intf_WarnMsg( 3, "vout: WinDX WinProc WM_SIZING" );
708         break;
709
710     case WM_WINDOWPOSCHANGED:
711         intf_WarnMsg( 3, "vout: WinDX WinProc WM_WINDOWPOSCHANGED" );
712         PostMessage( NULL, WM_APP, 0, 0);
713         break;
714
715     case WM_WINDOWPOSCHANGING:
716         intf_WarnMsg( 3, "vout: WinDX WinProc WM_WINDOWPOSCHANGING" );
717         break;
718
719     case WM_PAINT:
720         intf_WarnMsg( 3, "vout: WinDX WinProc WM_PAINT" );
721         break;
722
723     case WM_ERASEBKGND:
724         intf_WarnMsg( 3, "vout: WinDX WinProc WM_ERASEBKGND" );
725         break;
726
727     default:
728         intf_WarnMsg( 3, "vout: WinDX WinProc WM Default %i", message );
729         break;
730     }
731
732     return DefWindowProc(hwnd, message, wParam, lParam);
733 }
734
735 /*****************************************************************************
736  * WinDXCreateWindow: create a windows window where the video will play.
737  *****************************************************************************
738  * Before creating a direct draw surface, we need to create a window in which
739  * the video will be displayed. This window will also allow us to capture the
740  * events.
741  *****************************************************************************/
742 static int WinDXCreateWindow( vout_thread_t *p_vout )
743 {
744     HINSTANCE hInstance;
745     WNDCLASS  wc;                                 /* window class components */
746     RECT      rect_window;
747     COLORREF  colorkey; 
748     HDC       hdc;
749
750     intf_WarnMsg( 3, "vout: WinDX WinDXCreateWindow" );
751
752     /* get this module's instance */
753     hInstance = GetModuleHandle(NULL);
754
755     /* Create a BRUSH that will be used by Windows to paint the window
756      * background.
757      * This window background is important for us as it will be used by the
758      * graphics card to display the overlay.
759      * This is why we carefully choose the color for this background, the goal
760      * being to choose a color which isn't complete black but nearly. We
761      * obviously don't want to use black as a colorkey for the overlay because
762      * black is one of the most used color and thus would give us undesirable
763      * effects */
764     /* the first step is to find the colorkey we want to use. The difficulty
765      * comes from the potential dithering (depends on the display depth)
766      * because we need to know the real RGB value of the chosen colorkey */
767     hdc = GetDC( GetDesktopWindow() );
768     for( colorkey = 1; colorkey < 0xFF /*all shades of red*/; colorkey++ )
769     {
770         if( colorkey == GetNearestColor( hdc, colorkey ) )
771           break;
772     }
773     intf_WarnMsg( 3, "vout: WinDXCreateWindow background color:%i", colorkey );
774     ReleaseDC( p_vout->p_sys->hwnd, hdc );
775
776     /* create the actual brush */  
777     p_vout->p_sys->hbrush = CreateSolidBrush(colorkey);
778     p_vout->p_sys->i_colorkey = (int)colorkey;
779
780     /* fill in the window class structure */
781     wc.style         = 0;                               /* no special styles */
782     wc.lpfnWndProc   = (WNDPROC)WinDXEventProc;             /* event handler */
783     wc.cbClsExtra    = 0;                             /* no extra class data */
784     wc.cbWndExtra    = 0;                            /* no extra window data */
785     wc.hInstance     = hInstance;                                /* instance */
786     wc.hIcon         = LoadIcon(NULL, IDI_WINLOGO);   /* load a default icon */
787     wc.hCursor       = LoadCursor(NULL, IDC_ARROW); /* load a default cursor */
788     wc.hbrBackground = p_vout->p_sys->hbrush;            /* background color */
789     wc.lpszMenuName  = NULL;                                      /* no menu */
790     wc.lpszClassName = "VLC DirectX";                 /* use a special class */
791
792     /* register the window class */
793     if (!RegisterClass(&wc)) {
794         intf_WarnMsg( 3, "vout: WinDX register window FAILED" );
795         return (1);
796     }
797
798     /* when you create a window you give the dimensions you wish it to have.
799      * Unfortunatly these dimensions will include the borders and title bar.
800      * We use the following function to find out the size of the window
801      * corresponding to the useable surface we want */
802     rect_window.top    = 10;
803     rect_window.left   = 10;
804     rect_window.right  = rect_window.left + p_vout->p_sys->i_window_width;
805     rect_window.bottom = rect_window.top + p_vout->p_sys->i_window_height;
806     AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 );
807
808     /* create the window */
809     p_vout->p_sys->hwnd = CreateWindow("VLC DirectX",/* name of window class */
810                     "VLC DirectX",                  /* window title bar text */
811                     WS_OVERLAPPEDWINDOW
812                     | WS_SIZEBOX | WS_VISIBLE,               /* window style */
813                     10,                              /* default X coordinate */
814                     10,                              /* default Y coordinate */
815                     rect_window.right - rect_window.left,    /* window width */
816                     rect_window.bottom - rect_window.top,   /* window height */
817                     NULL,                                /* no parent window */
818                     NULL,                          /* no menu in this window */
819                     hInstance,            /* handle of this program instance */
820                     NULL);                        /* no additional arguments */
821
822     if (p_vout->p_sys->hwnd == NULL) {
823         intf_WarnMsg( 3, "vout: WinDX create window FAILED" );
824         return (1);
825     }
826
827     /* now display the window */
828     ShowWindow(p_vout->p_sys->hwnd, SW_SHOW);
829
830     return ( 0 );
831 }
832
833 /*****************************************************************************
834  * WinDXInitDDraw: Takes care of all the DirectDraw initialisations
835  *****************************************************************************
836  * This function initialise and allocate resources for DirectDraw.
837  *****************************************************************************/
838 static int WinDXInitDDraw( vout_thread_t *p_vout )
839 {
840     HRESULT     dxresult;
841     DWORD       flags;
842
843     intf_WarnMsg( 3, "vout: WinDX WinDXInitDDraw" );
844
845     /* Initialize DirectDraw */
846     dxresult = DirectDrawCreate( NULL, &p_vout->p_sys->p_ddobject, NULL );
847     if( dxresult != DD_OK )
848     {
849         intf_ErrMsg( "vout error: can't initialize Direct Draw" );
850         return( 1 );
851     }
852
853     /* Set DirectDraw Cooperative level, ie what control we want over Windows
854        display */
855     if( p_vout->b_fullscreen )
856     {
857         flags = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;
858     }
859     else
860     {
861         flags = DDSCL_NORMAL;
862     }
863
864     dxresult = IDirectDraw_SetCooperativeLevel( p_vout->p_sys->p_ddobject,
865                                                 p_vout->p_sys->hwnd, flags );
866     if( dxresult != DD_OK )
867     {
868         intf_ErrMsg( "vout error: can't set direct draw cooperative level." );
869         IDirectDraw_Release(p_vout->p_sys->p_ddobject);
870         p_vout->p_sys->p_ddobject = NULL;
871         return( 1 );
872     }
873
874     return( 0 );
875 }
876
877 /*****************************************************************************
878  * WinDXCreateDisplay: create the DirectDraw display.
879  *****************************************************************************
880  * Create and initialize display according to preferences specified in the vout
881  * thread fields.
882  *****************************************************************************/
883 static int WinDXCreateDisplay( vout_thread_t *p_vout )
884 {
885     DDCAPS        ddcaps;
886     HRESULT       dxresult;
887     DDSURFACEDESC ddsd;
888     BOOL          bHasOverlay, bHasColorKey, bCanStretch;
889
890     /* Now create the primary surface. This surface is the displayed surface */
891     /* The following two steps are important! */
892     memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
893     ddsd.dwSize = sizeof(DDSURFACEDESC);
894     ddsd.dwFlags = DDSD_CAPS;
895     ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
896
897     dxresult = IDirectDraw_CreateSurface( p_vout->p_sys->p_ddobject,
898                                           &ddsd,
899                                           &p_vout->p_sys->p_display, NULL );
900     if( dxresult != DD_OK )
901     {
902         intf_ErrMsg( "vout error: can't create direct draw primary surface." );
903         p_vout->p_sys->p_display = NULL;
904         return( 1 );
905     }
906
907 #if 0
908     /* Now create a clipper for our window.
909      * This clipper prevents us to modify by mistake anything on the screen
910      * (primary surface) which doesn't belong to our window */
911     dxresult = IDirectDraw_CreateClipper(p_vout->p_sys->p_ddobject, 0,
912                                          &p_vout->p_sys->p_clipper, NULL);
913     if( dxresult != DD_OK )
914     {
915         intf_ErrMsg( "vout error: can't create clipper." );
916         IDirectDrawSurface_Release( p_vout->p_sys->p_display );
917         p_vout->p_sys->p_display = NULL;
918         return( 1 );
919     }
920
921     dxresult = IDirectDrawClipper_SetHWnd(p_vout->p_sys->p_clipper, 0,
922                                               p_vout->p_sys->hwnd);
923     if( dxresult != DD_OK )
924     {
925         intf_ErrMsg( "vout error: can't attach clipper to window." );
926         IDirectDrawSurface_Release( p_vout->p_sys->p_display );
927         p_vout->p_sys->p_display = NULL;
928         return( 1 );
929     }
930
931     dxresult = IDirectDrawSurface_SetClipper(p_vout->p_sys->p_display,
932                                               p_vout->p_sys->p_clipper);
933     if( dxresult != DD_OK )
934     {
935         intf_ErrMsg( "vout error: can't attach clipper to surface." );
936         IDirectDrawSurface_Release( p_vout->p_sys->p_display );
937         p_vout->p_sys->p_display = NULL;
938         return( 1 );
939     }
940 #endif
941
942     /* Probe the capabilities of the hardware */
943     /* This is just an indication of whever or not we'll support overlay,
944      * but with this test we don't know if we support YUV overlay */
945     memset( &ddcaps, 0, sizeof( DDCAPS ));
946     ddcaps.dwSize = sizeof(DDCAPS);
947     dxresult = IDirectDraw_GetCaps( p_vout->p_sys->p_ddobject,
948                                     &ddcaps, NULL );
949     if(dxresult != DD_OK )
950     {
951         intf_ErrMsg( "vout error: can't get caps." );
952         bHasOverlay  = FALSE;
953         bHasColorKey = FALSE;
954         bCanStretch  = FALSE;
955     }
956     else
957     {
958         /* Determine if the hardware supports overlay surfaces */
959         bHasOverlay = ((ddcaps.dwCaps & DDCAPS_OVERLAY) ==
960                        DDCAPS_OVERLAY) ? TRUE : FALSE;
961         /* Determine if the hardware supports colorkeying */
962         bHasColorKey = ((ddcaps.dwCaps & DDCAPS_COLORKEY) ==
963                         DDCAPS_COLORKEY) ? TRUE : FALSE;
964         /* Determine if the hardware supports scaling of the overlay surface */
965         bCanStretch = ((ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH) ==
966                        DDCAPS_OVERLAYSTRETCH) ? TRUE : FALSE;
967         intf_WarnMsg( 3, "vout: WinDX Caps: overlay=%i colorkey=%i stretch=%i",
968                          bHasOverlay, bHasColorKey, bCanStretch );
969     }
970
971     p_vout->p_sys->p_overlay = NULL;
972     if( bHasOverlay && bHasColorKey && bCanStretch )
973     {
974         if( !WinDXCreateYUVOverlay( p_vout ) )
975         {
976            /* Overlay created successfully */
977            p_vout->b_need_render = 0;
978         }
979     }
980
981
982     /* Now do some initialisation for video_output */
983     if( p_vout->b_need_render )
984     {
985         /* if we want a valid pointer to the surface memory, we must lock
986          * the surface */
987         memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
988         ddsd.dwSize = sizeof(DDSURFACEDESC);
989         dxresult = IDirectDrawSurface_Lock(p_vout->p_sys->p_display,
990                                            NULL, &ddsd,
991                                            DDLOCK_NOSYSLOCK, NULL);
992         if ( dxresult == DDERR_SURFACELOST )
993         {
994             /* Your surface can be lost so be sure
995              * to check this and restore it if needed */
996             dxresult = IDirectDrawSurface_Restore( p_vout->p_sys->p_display );
997             dxresult = IDirectDrawSurface_Lock( p_vout->p_sys->p_display,
998                                                 NULL, &ddsd, DDLOCK_NOSYSLOCK
999                                                 | DDLOCK_WAIT, NULL);
1000         }
1001         if( dxresult != DD_OK )
1002         {
1003             intf_WarnMsg( 3, "vout: WinDX could not lock the surface" );
1004             return( 1 );
1005         }
1006
1007         /* Set the pointer to the surface memory */
1008         p_vout->p_sys->p_windx_buf[ 0 ] = ddsd.lpSurface;
1009         /* back buffer, none for now */
1010         p_vout->p_sys->p_windx_buf[ 1 ] = ddsd.lpSurface;
1011
1012
1013         /* Set thread information */
1014         p_vout->i_width =           ddsd.dwWidth;
1015         p_vout->i_height =          ddsd.dwHeight;
1016
1017 #ifdef NONAMELESSUNION
1018         p_vout->i_bytes_per_line =  ddsd.u1.lPitch;
1019
1020         p_vout->i_screen_depth =    ddsd.ddpfPixelFormat.u1.dwRGBBitCount;
1021         p_vout->i_bytes_per_pixel = ddsd.ddpfPixelFormat.u1.dwRGBBitCount/8;
1022
1023         p_vout->i_red_mask =        ddsd.ddpfPixelFormat.u2.dwRBitMask;
1024         p_vout->i_green_mask =      ddsd.ddpfPixelFormat.u3.dwGBitMask;
1025         p_vout->i_blue_mask =       ddsd.ddpfPixelFormat.u4.dwBBitMask;
1026 #else
1027         p_vout->i_bytes_per_line =  ddsd.lPitch;
1028
1029         p_vout->i_screen_depth =    ddsd.ddpfPixelFormat.dwRGBBitCount;
1030         p_vout->i_bytes_per_pixel = ddsd.ddpfPixelFormat.dwRGBBitCount/8;
1031
1032         p_vout->i_red_mask =        ddsd.ddpfPixelFormat.dwRBitMask;
1033         p_vout->i_green_mask =      ddsd.ddpfPixelFormat.dwGBitMask;
1034         p_vout->i_blue_mask =       ddsd.ddpfPixelFormat.dwBBitMask;
1035
1036 #endif /* NONAMELESSUNION */
1037
1038         /* Unlock the Surface */
1039         dxresult = IDirectDrawSurface_Unlock(p_vout->p_sys->p_display,
1040                                              ddsd.lpSurface );
1041         /* FIXME: palette in 8bpp ?? */
1042         /* Set and initialize buffers */
1043         p_vout->pf_setbuffers( p_vout, p_vout->p_sys->p_windx_buf[ 0 ],
1044                                  p_vout->p_sys->p_windx_buf[ 1 ] );
1045     }
1046     else
1047     {
1048         /* Lock the surface */
1049         memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
1050         ddsd.dwSize = sizeof(DDSURFACEDESC);
1051         dxresult = IDirectDrawSurface_Lock(p_vout->p_sys->p_overlay,
1052                                           NULL, &ddsd, DDLOCK_NOSYSLOCK, NULL);
1053         if ( dxresult == DDERR_SURFACELOST )
1054         {
1055             /* Your surface can be lost (thanks to windows) so be sure
1056              * to check this every time you want to do something with
1057              * it */
1058             dxresult = IDirectDrawSurface_Restore(
1059                                            p_vout->p_sys->p_overlay );
1060             dxresult = IDirectDrawSurface_Lock( p_vout->p_sys->p_overlay
1061                         , NULL, &ddsd,DDLOCK_NOSYSLOCK| DDLOCK_WAIT, NULL);
1062         }
1063         if( dxresult != DD_OK )
1064         {
1065             intf_WarnMsg( 3, "vout: WinDX could not lock the surface" );
1066             return( 1 );
1067         }
1068
1069         p_vout->p_sys->p_windx_buf[ 0 ] = ddsd.lpSurface;
1070         p_vout->p_sys->p_windx_buf[ 1 ] = ddsd.lpSurface;
1071
1072         /* Set thread information */
1073         p_vout->i_width =           ddsd.dwWidth;
1074         p_vout->i_height =          ddsd.dwHeight;
1075 #ifdef NONAMELESSUNION
1076         p_vout->i_bytes_per_line =  ddsd.u1.lPitch;
1077 #else
1078         p_vout->i_bytes_per_line =  ddsd.lPitch;
1079 #endif /* NONAMELESSUNION */
1080
1081         /* Unlock the Surface */
1082         dxresult = IDirectDrawSurface_Unlock(p_vout->p_sys->p_overlay,
1083                                              ddsd.lpSurface );
1084
1085         p_vout->pf_setbuffers( p_vout, p_vout->p_sys->p_windx_buf[ 0 ],
1086                                  p_vout->p_sys->p_windx_buf[ 1 ] );
1087     }
1088
1089     return( 0 );
1090 }
1091
1092 /*****************************************************************************
1093  * WinDXCreateYUVOveraly: create an YUV overlay surface for the video.
1094  *****************************************************************************
1095  * The best method of display is with an YUV overlay because the YUV->RGB
1096  * conversion is done in hardware.
1097  * This function will try to create an YUV overlay.
1098  *****************************************************************************/
1099 static int WinDXCreateYUVOverlay( vout_thread_t *p_vout )
1100 {
1101     HRESULT dxresult;
1102     DDSURFACEDESC ddsd;
1103
1104     /* Now create the overlay surface. This overlay will be displayed on
1105      * top of the primary surface.
1106      * A color key is used to determine whether or not the overlay will be
1107      * displayed, ie the overlay will be displayed in place of the primary
1108      * surface wherever the primary surface will have this color.
1109      * The video window has been created with a background of this color so
1110      * the overlay will be only displayed on top of this window */
1111
1112     memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
1113     ddsd.dwSize = sizeof(DDSURFACEDESC);
1114     ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
1115     ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
1116     ddsd.ddpfPixelFormat.dwFourCC = mmioFOURCC('Y','V','1','2');
1117 #ifdef NONAMELESSUNION
1118     ddsd.ddpfPixelFormat.u1.dwYUVBitCount = 16;
1119 #else
1120     ddsd.ddpfPixelFormat.dwYUVBitCount = 16;
1121 #endif
1122
1123     ddsd.dwSize = sizeof(DDSURFACEDESC);
1124     ddsd.dwFlags = DDSD_CAPS |
1125                    DDSD_HEIGHT |
1126                    DDSD_WIDTH |
1127                    DDSD_PIXELFORMAT;
1128     ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
1129     ddsd.dwHeight =  p_vout->p_sys->i_image_height;
1130     ddsd.dwWidth =  p_vout->p_sys->i_image_width;
1131
1132     dxresult = IDirectDraw_CreateSurface( p_vout->p_sys->p_ddobject,
1133                                           &ddsd,
1134                                           &p_vout->p_sys->p_overlay, NULL );
1135     if( dxresult != DD_OK )
1136     {
1137         intf_ErrMsg( "vout error: can't create overlay surface." );
1138         p_vout->p_sys->p_overlay = NULL;
1139     }
1140     else
1141     {
1142         intf_WarnMsg( 3, "vout: WinDX YUV overlay created successfully" );
1143     }
1144     /* Hide the overlay for now */
1145     IDirectDrawSurface_UpdateOverlay(p_vout->p_sys->p_overlay,
1146                                      NULL,
1147                                      p_vout->p_sys->p_display,
1148                                      NULL,
1149                                      DDOVER_HIDE,
1150                                      NULL);
1151
1152     return ( 0 );
1153 }
1154
1155 /*****************************************************************************
1156  * WinDXUpdateOverlay: Move or resize overlay surface on video display.
1157  *****************************************************************************
1158  * This function is used to move or resize an overlay surface on the screen.
1159  * Ususally the overlay is moved by the user and thus, by a move or resize
1160  * event (in vout_Manage).
1161  *****************************************************************************/
1162 static int WinDXUpdateOverlay( vout_thread_t *p_vout )
1163 {
1164     DDOVERLAYFX     ddofx;
1165     RECT            rect_window, rect_image;
1166     POINT           point_window;
1167     DWORD           dwFlags;
1168     HRESULT         dxresult;
1169     DWORD           dw_colorkey;
1170     DDPIXELFORMAT   pixel_format;
1171
1172     if( p_vout->p_sys->p_overlay == NULL || p_vout->b_need_render)
1173     {
1174         intf_WarnMsg( 3, "vout: WinDX no overlay !!" );
1175         return( 0 );
1176     }
1177
1178     if( !p_vout->p_sys->b_display_enabled )
1179     {
1180         return( 0 );
1181     }
1182
1183
1184     /* Now get the coordinates of the window. We don't actually want the
1185      * window coordinates but these of the usable surface inside the window.
1186      * By specification GetClientRect will always set rect_window.left and
1187      * rect_window.top to 0 because the Client area is always relative to the
1188      * container window */
1189     GetClientRect(p_vout->p_sys->hwnd, &rect_window);
1190
1191     point_window.x = 0;
1192     point_window.y = 0;
1193     ClientToScreen(p_vout->p_sys->hwnd, &point_window);
1194     rect_window.left = point_window.x;
1195     rect_window.top = point_window.y;
1196
1197     point_window.x = rect_window.right;
1198     point_window.y = rect_window.bottom;
1199     ClientToScreen(p_vout->p_sys->hwnd, &point_window);
1200     rect_window.right = point_window.x;
1201     rect_window.bottom = point_window.y;
1202
1203
1204     /* We want to keep the aspect ratio of the video */
1205     if( p_vout->b_scale )
1206     {
1207         switch( p_vout->p_rendered_pic->i_aspect_ratio )
1208         {
1209             case AR_16_9_PICTURE:
1210             if( ((rect_window.right-rect_window.left)*9)
1211                 > ((rect_window.bottom-rect_window.top)*16) )
1212             {
1213                 int temp;
1214                 temp = (rect_window.bottom-rect_window.top)*16/9;
1215                 temp = (rect_window.right-rect_window.left) - temp;
1216                 rect_window.left += (temp/2);
1217                 rect_window.right -= (temp/2);
1218             }
1219             else
1220             {
1221                 int temp;
1222                 temp = (rect_window.right-rect_window.left)*9/16;
1223                 temp = (rect_window.bottom-rect_window.top) - temp;
1224                 rect_window.top += (temp/2);
1225                 rect_window.bottom -= (temp/2);
1226             }
1227             break;
1228
1229             case AR_221_1_PICTURE:
1230             if( ((rect_window.right-rect_window.left)*100)
1231                 > ((rect_window.bottom-rect_window.top)*221) )
1232             {
1233                 int temp;
1234                 temp = (rect_window.bottom-rect_window.top)*221/100;
1235                 temp = (rect_window.right-rect_window.left) - temp;
1236                 rect_window.left += (temp/2);
1237                 rect_window.right -= (temp/2);
1238             }
1239             else
1240             {
1241                 int temp;
1242                 temp = (rect_window.right-rect_window.left)*100/221;
1243                 temp = (rect_window.bottom-rect_window.top) - temp;
1244                 rect_window.top += (temp/2);
1245                 rect_window.bottom -= (temp/2);
1246             }
1247             break;
1248
1249             case AR_SQUARE_PICTURE:
1250             if( (rect_window.right-rect_window.left)
1251                 > (rect_window.bottom-rect_window.top) )
1252             {
1253                 int temp;
1254                 temp = (rect_window.bottom-rect_window.top);
1255                 temp = (rect_window.right-rect_window.left) - temp;
1256                 rect_window.left += (temp/2);
1257                 rect_window.right -= (temp/2);
1258             }
1259             else
1260             {
1261                 int temp;
1262                 temp = (rect_window.right-rect_window.left);
1263                 temp = (rect_window.bottom-rect_window.top) - temp;
1264                 rect_window.top += (temp/2);
1265                 rect_window.bottom -= (temp/2);
1266             }
1267             break;
1268
1269             case AR_3_4_PICTURE:
1270             default:
1271             if( ((rect_window.right-rect_window.left)*3)
1272                 > ((rect_window.bottom-rect_window.top)*4) )
1273             {
1274                 int temp;
1275                 temp = (rect_window.bottom-rect_window.top)*4/3;
1276                 temp = (rect_window.right-rect_window.left) - temp;
1277                 rect_window.left += (temp/2);
1278                 rect_window.right -= (temp/2);
1279             }
1280             else
1281             {
1282                 int temp;
1283                 temp = (rect_window.right-rect_window.left)*3/4;
1284                 temp = (rect_window.bottom-rect_window.top) - temp;
1285                 rect_window.top += (temp/2);
1286                 rect_window.bottom -= (temp/2);
1287             }
1288             break;
1289         }
1290     }
1291
1292
1293     /* It seems we can't feed the UpdateOverlay directdraw function with
1294      * negative values so we have to clip the computed rectangles */
1295     /* FIXME */
1296
1297
1298     /* compute the colorkey pixel value from the RGB value we've got */
1299     memset( &pixel_format, 0, sizeof( DDPIXELFORMAT ));
1300     pixel_format.dwSize = sizeof( DDPIXELFORMAT );
1301     dxresult = IDirectDrawSurface_GetPixelFormat( p_vout->p_sys->p_display,
1302                                                   &pixel_format );
1303     if( dxresult != DD_OK )
1304         intf_WarnMsg( 3, "vout: WinDX GetPixelFormat failed !!" );
1305     dw_colorkey = (DWORD)p_vout->p_sys->i_colorkey;
1306 #ifdef NONAMELESSUNION
1307     dw_colorkey = (DWORD)((( dw_colorkey * pixel_format.u2.dwRBitMask) / 255)
1308                           & pixel_format.u2.dwRBitMask);
1309 #else
1310     dw_colorkey = (DWORD)((( dw_colorkey * pixel_format.dwRBitMask) / 255)
1311                           & pixel_format.dwRBitMask);
1312 #endif
1313
1314     /* Position and show the overlay */
1315     memset(&ddofx, 0, sizeof(DDOVERLAYFX));
1316     ddofx.dwSize = sizeof(DDOVERLAYFX);
1317     ddofx.dckDestColorkey.dwColorSpaceLowValue = dw_colorkey;
1318     ddofx.dckDestColorkey.dwColorSpaceHighValue = dw_colorkey;
1319
1320     dwFlags = DDOVER_KEYDESTOVERRIDE | DDOVER_SHOW;
1321
1322     dxresult = IDirectDrawSurface_UpdateOverlay(p_vout->p_sys->p_overlay,
1323                                                 NULL,    /*&rect_image,*/
1324                                                 p_vout->p_sys->p_display,
1325                                                 &rect_window,
1326                                                 dwFlags,
1327                                                 &ddofx);
1328     if(dxresult != DD_OK)
1329     {
1330         intf_WarnMsg( 3, "vout: WinDX can't move or resize overlay" );
1331     }
1332
1333     return ( 0 );
1334 }
1335
1336 /*****************************************************************************
1337  * WinDXCloseWindow: close the window created by WinDXCreateWindow
1338  *****************************************************************************
1339  * This function returns all resources allocated by WinDXCreateWindow.
1340  *****************************************************************************/
1341 static void WinDXCloseWindow( vout_thread_t *p_vout )
1342 {
1343     HINSTANCE hInstance;
1344
1345     intf_WarnMsg( 3, "vout: WinDXCloseWindow" );
1346     if( p_vout->p_sys->hwnd != INVALID_HANDLE_VALUE )
1347     {
1348         DestroyWindow( p_vout->p_sys->hwnd);
1349         p_vout->p_sys->hwnd = INVALID_HANDLE_VALUE;
1350     }
1351
1352     hInstance = GetModuleHandle(NULL);
1353     UnregisterClass( "VLC DirectX",                            /* class name */
1354                      hInstance );          /* handle to application instance */
1355
1356     /* free window background brush */
1357     if( p_vout->p_sys->hwnd != INVALID_HANDLE_VALUE )
1358     {
1359         DeleteObject( p_vout->p_sys->hbrush );
1360         p_vout->p_sys->hbrush = INVALID_HANDLE_VALUE;
1361     }
1362 }
1363
1364 /*****************************************************************************
1365  * WinDXCloseDDraw: Release the DDraw object allocated by WinDXInitDDraw
1366  *****************************************************************************
1367  * This function returns all resources allocated by WinDXInitDDraw.
1368  *****************************************************************************/
1369 static void WinDXCloseDDraw( vout_thread_t *p_vout )
1370 {
1371     intf_WarnMsg(3, "vout: WinDXCloseDDraw" );
1372     if( p_vout->p_sys->p_ddobject != NULL )
1373     {
1374         IDirectDraw_Release(p_vout->p_sys->p_ddobject);
1375         p_vout->p_sys->p_ddobject = NULL;
1376     }
1377 }
1378
1379 /*****************************************************************************
1380  * WinDXCloseDisplay: close and reset DirectX device
1381  *****************************************************************************
1382  * This function returns all resources allocated by WinDXCreateDisplay and
1383  * restore the original state of the device.
1384  *****************************************************************************/
1385 static void WinDXCloseDisplay( vout_thread_t *p_vout )
1386 {
1387     intf_WarnMsg( 3, "vout: WinDXCloseDisplay" );
1388     if( p_vout->p_sys->p_display != NULL )
1389     {
1390         if( p_vout->p_sys->p_overlay != NULL )
1391         {
1392             intf_WarnMsg( 3, "vout: WinDXCloseDisplay overlay" );
1393             IDirectDraw_Release( p_vout->p_sys->p_overlay );
1394             p_vout->p_sys->p_overlay = NULL;
1395         }
1396
1397         if( p_vout->p_sys->p_clipper != NULL )
1398         {
1399             intf_WarnMsg( 3, "vout: WinDXCloseDisplay clipper" );
1400             IDirectDraw_Release( p_vout->p_sys->p_clipper );
1401             p_vout->p_sys->p_clipper = NULL;
1402         }
1403
1404         intf_WarnMsg( 3, "vout: WinDXCloseDisplay display" );
1405         IDirectDraw_Release( p_vout->p_sys->p_display );
1406         p_vout->p_sys->p_display = NULL;
1407     }
1408 }
1409
1410 /*****************************************************************************
1411  * WinDXCloseYUVOverlay: close the overlay surface
1412  *****************************************************************************
1413  * This function returns all resources allocated by the overlay surface.
1414  * We also call this function when the decoded picture change its dimensions
1415  * (in that case we close the overlay surface and reopen another with the
1416  * right dimensions).
1417  *****************************************************************************/
1418 static void WinDXCloseYUVOverlay( vout_thread_t *p_vout )
1419 {
1420     intf_WarnMsg( 3, "vout: WinDXCloseYUVOverlay" );
1421     if( p_vout->p_sys->p_overlay != NULL )
1422     {
1423         IDirectDraw_Release( p_vout->p_sys->p_overlay );
1424         p_vout->p_sys->p_overlay = NULL;
1425     }
1426     p_vout->p_sys->b_display_enabled = 0;
1427 }