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 $
7 * Authors: Gildas Bazin <gbazin@netcourrier.com>
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.
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.
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 *****************************************************************************/
24 #define MODULE_NAME directx
25 #include "modules_inner.h"
27 /* This is a list of what needs to be fixed:
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.
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.
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)...
40 * The overlay doesn't use double-buffering.
42 * Port this plugin to Video Output IV
45 /*****************************************************************************
48 *****************************************************************************/
51 #include <errno.h> /* ENOMEM */
52 #include <stdlib.h> /* free() */
53 #include <string.h> /* strerror() */
67 #include "video_output.h"
70 #include "interface.h"
74 #include "modules_export.h"
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
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 */
92 int i_image_width; /* size of the decoded image */
94 int i_window_width; /* size of the displayed image */
97 int i_colorkey; /* colorkey used to display the overlay */
99 boolean_t b_display_enabled;
103 boolean_t b_cursor_autohidden;
106 char *p_windx_buf[2]; /* Buffer information */
110 /*****************************************************************************
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 );
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 );
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 )
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;
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 )
157 if( TestMethod( VOUT_METHOD_VAR, "directx" ) )
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 )
172 /* Allocate structure */
173 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
174 if( p_vout->p_sys == NULL )
176 intf_ErrMsg( "vout error: can't create p_sys (%s)", strerror(ENOMEM) );
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;
188 p_vout->p_sys->b_cursor = 1; /* TODO should be done with a main_GetInt.. */
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();
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;
207 /* Create a window for the video */
208 /* Creating a window under Windows also initializes the thread's event
210 if( WinDXCreateWindow( p_vout ) )
212 intf_ErrMsg( "vout error: can't create window" );
213 free( p_vout->p_sys );
217 /* Initialise DirectDraw */
218 if( WinDXInitDDraw( p_vout ) )
220 intf_ErrMsg( "vout error: can't initialise DirectDraw" );
221 WinDXCloseWindow( p_vout );
222 free( p_vout->p_sys );
226 /* create the directx display */
227 if( WinDXCreateDisplay( p_vout ) )
229 intf_ErrMsg( "vout error: can't initialise DirectDraw" );
230 WinDXCloseDDraw( p_vout );
231 WinDXCloseWindow( p_vout );
232 free( p_vout->p_sys );
239 /*****************************************************************************
240 * vout_Init: initialize DirectX video thread output method
241 *****************************************************************************
243 *****************************************************************************/
244 static int vout_Init( vout_thread_t *p_vout )
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 )
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 )
267 intf_WarnMsg( 3, "vout: vout_Destroy" );
268 WinDXCloseDisplay( p_vout );
269 WinDXCloseDDraw( p_vout );
270 WinDXCloseWindow( p_vout );
272 if( p_vout->p_sys != NULL )
274 free( p_vout->p_sys );
275 p_vout->p_sys = NULL;
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 )
289 WINDOWPLACEMENT window_placement;
290 boolean_t b_dispatch_msg = TRUE;
292 while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
294 if( GetMessage(&msg, NULL, 0, 0) >= 0 )
296 switch( msg.message )
300 intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_CLOSE" );
305 intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_QUIT" );
306 p_main->p_intf->b_die = 1;
310 intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_MOVE" );
311 if( !p_vout->b_need_render )
313 p_vout->i_changes |= VOUT_SIZE_CHANGE;
315 /* don't create a never ending loop */
316 b_dispatch_msg = FALSE;
320 intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_APP" );
321 if( !p_vout->b_need_render )
323 p_vout->i_changes |= VOUT_SIZE_CHANGE;
325 /* don't create a never ending loop */
326 b_dispatch_msg = FALSE;
330 intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_PAINT" );
334 intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_ERASEBKGND" );
338 intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_MOUSEMOVE" );
339 if( p_vout->p_sys->b_cursor )
341 if( p_vout->p_sys->b_cursor_autohidden )
343 p_vout->p_sys->b_cursor_autohidden = 0;
344 p_vout->p_sys->i_lastmoved = mdate();
349 p_vout->p_sys->i_lastmoved = mdate();
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" );
363 p_main->p_intf->b_die = 1;
366 TranslateMessage(&msg);
367 b_dispatch_msg = FALSE;
371 intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_CHAR" );
376 p_main->p_intf->b_die = 1;
381 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
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;
396 if( intf_ProcessKey( p_main->p_intf,
397 (char )msg.wParam ) )
399 intf_DbgMsg( "unhandled key '%c' (%i)",
400 (char)msg.wParam, msg.wParam );
406 intf_WarnMsg( 3, "vout: WinDX vout_Manage WM Default %i",
411 /* don't create a never ending loop */
414 TranslateMessage(&msg);
415 DispatchMessage(&msg);
417 b_dispatch_msg = TRUE;
431 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
433 intf_WarnMsg( 3, "vout: WinDX vout_Manage Size Change" );
434 WinDXUpdateOverlay( p_vout );
435 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
441 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
443 p_vout->b_fullscreen = ! p_vout->b_fullscreen;
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 )
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 );
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 );
465 SetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
466 /*WinDXUpdateOverlay( p_vout );*/
468 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
474 if( ! p_vout->p_sys->b_cursor_autohidden &&
475 ( mdate() - p_vout->p_sys->i_lastmoved > 2000000 ) )
477 /* Hide the mouse automatically */
478 p_vout->p_sys->b_cursor_autohidden = 1;
482 if( p_vout->i_changes & VOUT_CURSOR_CHANGE )
484 p_vout->p_sys->b_cursor = ! p_vout->p_sys->b_cursor;
486 ShowCursor( p_vout->p_sys->b_cursor &&
487 ! p_vout->p_sys->b_cursor_autohidden );
489 p_vout->i_changes &= ~VOUT_CURSOR_CHANGE;
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
501 *****************************************************************************/
502 static void vout_SetPalette( p_vout_thread_t p_vout, u16 *red, u16 *green,
503 u16 *blue, u16 *transp)
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 )
520 int i_image_width = p_vout->p_rendered_pic->i_width;
521 int i_image_height = p_vout->p_rendered_pic->i_height;
524 if( (p_vout->p_sys->p_display == NULL) )
526 intf_WarnMsg( 3, "vout error: WinDX no display!!" );
530 /* The first time this function is called it enables the display */
531 p_vout->p_sys->b_display_enabled = 1;
533 if( p_vout->b_need_render )
540 * p_vout->p_rendered_pic->p_y/u/v contains the YUV buffers to
543 /* TODO: support for streams other than 4:2:0 */
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 )
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 );
556 if( p_vout->p_sys->p_overlay == NULL )
558 intf_WarnMsg( 3, "vout: WinDX no overlay, open one..." );
559 if( WinDXCreateYUVOverlay( p_vout ) )
561 intf_WarnMsg( 3, "vout: WinDX cannot open a new overlay !!" );
564 /* Display the Overlay */
565 p_vout->p_sys->b_display_enabled = 1;
566 WinDXUpdateOverlay( p_vout );
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 )
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);
583 if( dxresult != DD_OK )
585 intf_WarnMsg( 3, "vout: WinDX could not lock the surface" );
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++)
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,
599 memcpy(ddsd.lpSurface + (i*2+1)*ddsd.u1.lPitch,
600 p_vout->p_rendered_pic->p_y + (i*2+1)*i_image_width,
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,
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,
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,
618 memcpy(ddsd.lpSurface + (i*2+1)*ddsd.lPitch,
619 p_vout->p_rendered_pic->p_y + (i*2+1)*i_image_width,
622 memcpy((ddsd.lpSurface + ddsd.dwHeight * ddsd.lPitch)
624 p_vout->p_rendered_pic->p_v + i*i_image_width/2,
627 memcpy((ddsd.lpSurface + ddsd.dwHeight * ddsd.lPitch)
628 + (ddsd.dwHeight * ddsd.lPitch/4)
630 p_vout->p_rendered_pic->p_u + i*i_image_width/2,
632 #endif /* NONAMELESSUNION */
636 /* Unlock the Surface */
637 dxresult = IDirectDrawSurface_Unlock(p_vout->p_sys->p_overlay,
645 /* following functions are local */
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 )
666 intf_WarnMsg( 3, "vout: WinDX WinProc WM_ACTIVED" );
670 intf_WarnMsg( 3, "vout: WinDX WinProc WM_CREATE" );
673 /* the user wants to close the window */
675 intf_WarnMsg( 3, "vout: WinDX WinProc WM_CLOSE" );
678 /* the window has been closed so shut down everything now */
680 intf_WarnMsg( 3, "vout: WinDX WinProc WM_DESTROY" );
681 PostQuitMessage( 0 );
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 */
695 intf_WarnMsg( 3, "vout: WinDX WinProc WM_MOVE" );
699 intf_WarnMsg( 3, "vout: WinDX WinProc WM_SIZE" );
703 intf_WarnMsg( 3, "vout: WinDX WinProc WM_MOVING" );
707 intf_WarnMsg( 3, "vout: WinDX WinProc WM_SIZING" );
710 case WM_WINDOWPOSCHANGED:
711 intf_WarnMsg( 3, "vout: WinDX WinProc WM_WINDOWPOSCHANGED" );
712 PostMessage( NULL, WM_APP, 0, 0);
715 case WM_WINDOWPOSCHANGING:
716 intf_WarnMsg( 3, "vout: WinDX WinProc WM_WINDOWPOSCHANGING" );
720 intf_WarnMsg( 3, "vout: WinDX WinProc WM_PAINT" );
724 intf_WarnMsg( 3, "vout: WinDX WinProc WM_ERASEBKGND" );
728 intf_WarnMsg( 3, "vout: WinDX WinProc WM Default %i", message );
732 return DefWindowProc(hwnd, message, wParam, lParam);
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
741 *****************************************************************************/
742 static int WinDXCreateWindow( vout_thread_t *p_vout )
745 WNDCLASS wc; /* window class components */
750 intf_WarnMsg( 3, "vout: WinDX WinDXCreateWindow" );
752 /* get this module's instance */
753 hInstance = GetModuleHandle(NULL);
755 /* Create a BRUSH that will be used by Windows to paint the window
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
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++ )
770 if( colorkey == GetNearestColor( hdc, colorkey ) )
773 intf_WarnMsg( 3, "vout: WinDXCreateWindow background color:%i", colorkey );
774 ReleaseDC( p_vout->p_sys->hwnd, hdc );
776 /* create the actual brush */
777 p_vout->p_sys->hbrush = CreateSolidBrush(colorkey);
778 p_vout->p_sys->i_colorkey = (int)colorkey;
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 */
792 /* register the window class */
793 if (!RegisterClass(&wc)) {
794 intf_WarnMsg( 3, "vout: WinDX register window FAILED" );
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 );
808 /* create the window */
809 p_vout->p_sys->hwnd = CreateWindow("VLC DirectX",/* name of window class */
810 "VLC DirectX", /* window title bar text */
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 */
822 if (p_vout->p_sys->hwnd == NULL) {
823 intf_WarnMsg( 3, "vout: WinDX create window FAILED" );
827 /* now display the window */
828 ShowWindow(p_vout->p_sys->hwnd, SW_SHOW);
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 )
843 intf_WarnMsg( 3, "vout: WinDX WinDXInitDDraw" );
845 /* Initialize DirectDraw */
846 dxresult = DirectDrawCreate( NULL, &p_vout->p_sys->p_ddobject, NULL );
847 if( dxresult != DD_OK )
849 intf_ErrMsg( "vout error: can't initialize Direct Draw" );
853 /* Set DirectDraw Cooperative level, ie what control we want over Windows
855 if( p_vout->b_fullscreen )
857 flags = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;
861 flags = DDSCL_NORMAL;
864 dxresult = IDirectDraw_SetCooperativeLevel( p_vout->p_sys->p_ddobject,
865 p_vout->p_sys->hwnd, flags );
866 if( dxresult != DD_OK )
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;
877 /*****************************************************************************
878 * WinDXCreateDisplay: create the DirectDraw display.
879 *****************************************************************************
880 * Create and initialize display according to preferences specified in the vout
882 *****************************************************************************/
883 static int WinDXCreateDisplay( vout_thread_t *p_vout )
888 BOOL bHasOverlay, bHasColorKey, bCanStretch;
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;
897 dxresult = IDirectDraw_CreateSurface( p_vout->p_sys->p_ddobject,
899 &p_vout->p_sys->p_display, NULL );
900 if( dxresult != DD_OK )
902 intf_ErrMsg( "vout error: can't create direct draw primary surface." );
903 p_vout->p_sys->p_display = NULL;
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 )
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;
921 dxresult = IDirectDrawClipper_SetHWnd(p_vout->p_sys->p_clipper, 0,
922 p_vout->p_sys->hwnd);
923 if( dxresult != DD_OK )
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;
931 dxresult = IDirectDrawSurface_SetClipper(p_vout->p_sys->p_display,
932 p_vout->p_sys->p_clipper);
933 if( dxresult != DD_OK )
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;
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,
949 if(dxresult != DD_OK )
951 intf_ErrMsg( "vout error: can't get caps." );
953 bHasColorKey = FALSE;
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 );
971 p_vout->p_sys->p_overlay = NULL;
972 if( bHasOverlay && bHasColorKey && bCanStretch )
974 if( !WinDXCreateYUVOverlay( p_vout ) )
976 /* Overlay created successfully */
977 p_vout->b_need_render = 0;
982 /* Now do some initialisation for video_output */
983 if( p_vout->b_need_render )
985 /* if we want a valid pointer to the surface memory, we must lock
987 memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
988 ddsd.dwSize = sizeof(DDSURFACEDESC);
989 dxresult = IDirectDrawSurface_Lock(p_vout->p_sys->p_display,
991 DDLOCK_NOSYSLOCK, NULL);
992 if ( dxresult == DDERR_SURFACELOST )
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);
1001 if( dxresult != DD_OK )
1003 intf_WarnMsg( 3, "vout: WinDX could not lock the surface" );
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;
1013 /* Set thread information */
1014 p_vout->i_width = ddsd.dwWidth;
1015 p_vout->i_height = ddsd.dwHeight;
1017 #ifdef NONAMELESSUNION
1018 p_vout->i_bytes_per_line = ddsd.u1.lPitch;
1020 p_vout->i_screen_depth = ddsd.ddpfPixelFormat.u1.dwRGBBitCount;
1021 p_vout->i_bytes_per_pixel = ddsd.ddpfPixelFormat.u1.dwRGBBitCount/8;
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;
1027 p_vout->i_bytes_per_line = ddsd.lPitch;
1029 p_vout->i_screen_depth = ddsd.ddpfPixelFormat.dwRGBBitCount;
1030 p_vout->i_bytes_per_pixel = ddsd.ddpfPixelFormat.dwRGBBitCount/8;
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;
1036 #endif /* NONAMELESSUNION */
1038 /* Unlock the Surface */
1039 dxresult = IDirectDrawSurface_Unlock(p_vout->p_sys->p_display,
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 ] );
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 )
1055 /* Your surface can be lost (thanks to windows) so be sure
1056 * to check this every time you want to do something with
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);
1063 if( dxresult != DD_OK )
1065 intf_WarnMsg( 3, "vout: WinDX could not lock the surface" );
1069 p_vout->p_sys->p_windx_buf[ 0 ] = ddsd.lpSurface;
1070 p_vout->p_sys->p_windx_buf[ 1 ] = ddsd.lpSurface;
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;
1078 p_vout->i_bytes_per_line = ddsd.lPitch;
1079 #endif /* NONAMELESSUNION */
1081 /* Unlock the Surface */
1082 dxresult = IDirectDrawSurface_Unlock(p_vout->p_sys->p_overlay,
1085 p_vout->pf_setbuffers( p_vout, p_vout->p_sys->p_windx_buf[ 0 ],
1086 p_vout->p_sys->p_windx_buf[ 1 ] );
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 )
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 */
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;
1120 ddsd.ddpfPixelFormat.dwYUVBitCount = 16;
1123 ddsd.dwSize = sizeof(DDSURFACEDESC);
1124 ddsd.dwFlags = DDSD_CAPS |
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;
1132 dxresult = IDirectDraw_CreateSurface( p_vout->p_sys->p_ddobject,
1134 &p_vout->p_sys->p_overlay, NULL );
1135 if( dxresult != DD_OK )
1137 intf_ErrMsg( "vout error: can't create overlay surface." );
1138 p_vout->p_sys->p_overlay = NULL;
1142 intf_WarnMsg( 3, "vout: WinDX YUV overlay created successfully" );
1144 /* Hide the overlay for now */
1145 IDirectDrawSurface_UpdateOverlay(p_vout->p_sys->p_overlay,
1147 p_vout->p_sys->p_display,
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 )
1165 RECT rect_window, rect_image;
1170 DDPIXELFORMAT pixel_format;
1172 if( p_vout->p_sys->p_overlay == NULL || p_vout->b_need_render)
1174 intf_WarnMsg( 3, "vout: WinDX no overlay !!" );
1178 if( !p_vout->p_sys->b_display_enabled )
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);
1193 ClientToScreen(p_vout->p_sys->hwnd, &point_window);
1194 rect_window.left = point_window.x;
1195 rect_window.top = point_window.y;
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;
1204 /* We want to keep the aspect ratio of the video */
1205 if( p_vout->b_scale )
1207 switch( p_vout->p_rendered_pic->i_aspect_ratio )
1209 case AR_16_9_PICTURE:
1210 if( ((rect_window.right-rect_window.left)*9)
1211 > ((rect_window.bottom-rect_window.top)*16) )
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);
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);
1229 case AR_221_1_PICTURE:
1230 if( ((rect_window.right-rect_window.left)*100)
1231 > ((rect_window.bottom-rect_window.top)*221) )
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);
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);
1249 case AR_SQUARE_PICTURE:
1250 if( (rect_window.right-rect_window.left)
1251 > (rect_window.bottom-rect_window.top) )
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);
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);
1269 case AR_3_4_PICTURE:
1271 if( ((rect_window.right-rect_window.left)*3)
1272 > ((rect_window.bottom-rect_window.top)*4) )
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);
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);
1293 /* It seems we can't feed the UpdateOverlay directdraw function with
1294 * negative values so we have to clip the computed rectangles */
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,
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);
1310 dw_colorkey = (DWORD)((( dw_colorkey * pixel_format.dwRBitMask) / 255)
1311 & pixel_format.dwRBitMask);
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;
1320 dwFlags = DDOVER_KEYDESTOVERRIDE | DDOVER_SHOW;
1322 dxresult = IDirectDrawSurface_UpdateOverlay(p_vout->p_sys->p_overlay,
1323 NULL, /*&rect_image,*/
1324 p_vout->p_sys->p_display,
1328 if(dxresult != DD_OK)
1330 intf_WarnMsg( 3, "vout: WinDX can't move or resize overlay" );
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 )
1343 HINSTANCE hInstance;
1345 intf_WarnMsg( 3, "vout: WinDXCloseWindow" );
1346 if( p_vout->p_sys->hwnd != INVALID_HANDLE_VALUE )
1348 DestroyWindow( p_vout->p_sys->hwnd);
1349 p_vout->p_sys->hwnd = INVALID_HANDLE_VALUE;
1352 hInstance = GetModuleHandle(NULL);
1353 UnregisterClass( "VLC DirectX", /* class name */
1354 hInstance ); /* handle to application instance */
1356 /* free window background brush */
1357 if( p_vout->p_sys->hwnd != INVALID_HANDLE_VALUE )
1359 DeleteObject( p_vout->p_sys->hbrush );
1360 p_vout->p_sys->hbrush = INVALID_HANDLE_VALUE;
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 )
1371 intf_WarnMsg(3, "vout: WinDXCloseDDraw" );
1372 if( p_vout->p_sys->p_ddobject != NULL )
1374 IDirectDraw_Release(p_vout->p_sys->p_ddobject);
1375 p_vout->p_sys->p_ddobject = NULL;
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 )
1387 intf_WarnMsg( 3, "vout: WinDXCloseDisplay" );
1388 if( p_vout->p_sys->p_display != NULL )
1390 if( p_vout->p_sys->p_overlay != NULL )
1392 intf_WarnMsg( 3, "vout: WinDXCloseDisplay overlay" );
1393 IDirectDraw_Release( p_vout->p_sys->p_overlay );
1394 p_vout->p_sys->p_overlay = NULL;
1397 if( p_vout->p_sys->p_clipper != NULL )
1399 intf_WarnMsg( 3, "vout: WinDXCloseDisplay clipper" );
1400 IDirectDraw_Release( p_vout->p_sys->p_clipper );
1401 p_vout->p_sys->p_clipper = NULL;
1404 intf_WarnMsg( 3, "vout: WinDXCloseDisplay display" );
1405 IDirectDraw_Release( p_vout->p_sys->p_display );
1406 p_vout->p_sys->p_display = NULL;
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 )
1420 intf_WarnMsg( 3, "vout: WinDXCloseYUVOverlay" );
1421 if( p_vout->p_sys->p_overlay != NULL )
1423 IDirectDraw_Release( p_vout->p_sys->p_overlay );
1424 p_vout->p_sys->p_overlay = NULL;
1426 p_vout->p_sys->b_display_enabled = 0;