1 /*****************************************************************************
2 * wingdi.c : Win32 / WinCE GDI video output plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2002 the VideoLAN team
7 * Authors: Gildas Bazin <gbazin@videolan.org>
8 * Samuel Hocevar <sam@zoy.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
28 #include <stdlib.h> /* malloc(), free() */
32 #include <vlc_interface.h>
33 #include <vlc_playlist.h>
38 #define SHFS_SHOWSIPBUTTON 0x0004
39 #define SHFS_HIDESIPBUTTON 0x0008
41 #if defined(UNDER_CE) && !defined(__PLUGIN__) /*FIXME*/
42 # define MENU_HEIGHT 26
43 BOOL SHFullScreen(HWND hwndRequester, DWORD dwState);
45 # define MENU_HEIGHT 0
46 # define SHFullScreen(a,b)
49 #ifdef MODULE_NAME_IS_wingapi
50 typedef struct GXDisplayProperties {
57 } GXDisplayProperties;
59 typedef struct GXScreenRect {
66 # define GX_FULLSCREEN 0x01
67 # define GX_NORMALKEYS 0x02
68 # define GX_LANDSCAPEKEYS 0x03
71 # define kfLandscape 0x8
72 # define kfPalette 0x10
73 # define kfDirect 0x20
74 # define kfDirect555 0x40
75 # define kfDirect565 0x80
76 # define kfDirect888 0x100
77 # define kfDirect444 0x200
78 # define kfDirectInverted 0x400
80 #endif /* MODULE_NAME_IS_wingapi */
82 #define MAX_DIRECTBUFFERS 10
85 #ifndef WS_OVERLAPPEDWINDOW
86 # define WS_OVERLAPPEDWINDOW 0xcf0000
88 #ifndef WS_EX_NOPARENTNOTIFY
89 # define WS_EX_NOPARENTNOTIFY 4
91 #ifndef WS_EX_APPWINDOW
92 #define WS_EX_APPWINDOW 0x40000
94 #define SetWindowLongPtr SetWindowLong
95 #define GetWindowLongPtr GetWindowLong
96 #define GWLP_USERDATA GWL_USERDATA
97 #define AdjustWindowRect(a,b,c)
100 #ifndef WS_NONAVDONEBUTTON
101 #define WS_NONAVDONEBUTTON 0
103 /*****************************************************************************
105 *****************************************************************************/
106 static int OpenVideo ( vlc_object_t * );
107 static void CloseVideo ( vlc_object_t * );
109 static int Init ( vout_thread_t * );
110 static void End ( vout_thread_t * );
111 static int Manage ( vout_thread_t * );
112 static void Render ( vout_thread_t *, picture_t * );
113 #ifdef MODULE_NAME_IS_wingapi
114 static void DisplayGAPI( vout_thread_t *, picture_t * );
115 static int GAPILockSurface( vout_thread_t *, picture_t * );
116 static int GAPIUnlockSurface( vout_thread_t *, picture_t * );
118 static void DisplayGDI( vout_thread_t *, picture_t * );
120 static void SetPalette( vout_thread_t *, uint16_t *, uint16_t *, uint16_t * );
122 static void EventThread ( vlc_object_t * );
123 static long FAR PASCAL WndProc ( HWND, UINT, WPARAM, LPARAM );
124 static void InitBuffers ( vout_thread_t * );
125 static void UpdateRects ( vout_thread_t *, vlc_bool_t );
127 static int Control( vout_thread_t *p_vout, int i_query, va_list args );
129 /*****************************************************************************
131 *****************************************************************************/
134 /* The event thread */
135 vlc_object_t * p_event;
137 /* Our video output window */
142 HWND hparent; /* Handle of the parent window */
143 WNDPROC pf_wndproc; /* Window handling callback */
144 volatile uint16_t i_changes; /* changes made to the video display */
145 RECT window_placement;
147 /* Window position and size */
156 /* Coordinates of src and dest images (used when blitting to display) */
158 RECT rect_src_clipped;
160 RECT rect_dest_clipped;
164 /* Our offscreen bitmap and its framebuffer */
167 uint8_t * p_pic_buffer;
169 int i_pic_pixel_pitch;
171 BITMAPINFO bitmapinfo;
177 vlc_bool_t b_video_display;
179 /* Window focus states */
181 vlc_bool_t b_parent_focus;
183 #ifdef MODULE_NAME_IS_wingapi
184 HINSTANCE gapi_dll; /* handle of the opened gapi dll */
187 int (*GXOpenDisplay)( HWND hWnd, DWORD dwFlags );
188 int (*GXCloseDisplay)();
189 void *(*GXBeginDraw)();
191 GXDisplayProperties (*GXGetDisplayProperties)();
197 #define GXOpenDisplay p_vout->p_sys->GXOpenDisplay
198 #define GXCloseDisplay p_vout->p_sys->GXCloseDisplay
199 #define GXBeginDraw p_vout->p_sys->GXBeginDraw
200 #define GXEndDraw p_vout->p_sys->GXEndDraw
201 #define GXGetDisplayProperties p_vout->p_sys->GXGetDisplayProperties
203 #ifdef MODULE_NAME_IS_wingapi
204 # define GXSuspend p_vout->p_sys->GXSuspend
205 # define GXResume p_vout->p_sys->GXResume
211 #define DX_POSITION_CHANGE 0x1000
213 /*****************************************************************************
215 *****************************************************************************/
217 set_category( CAT_VIDEO );
218 set_subcategory( SUBCAT_VIDEO_VOUT );
219 #ifdef MODULE_NAME_IS_wingapi
220 set_shortname( "Windows GAPI" );
221 set_description( _("Windows GAPI video output") );
222 set_capability( "video output", 20 );
224 set_shortname( "Windows GDI" );
225 set_description( _("Windows GDI video output") );
226 set_capability( "video output", 10 );
228 set_callbacks( OpenVideo, CloseVideo );
231 /*****************************************************************************
232 * OpenVideo: activate GDI video thread output method
233 *****************************************************************************/
234 static int OpenVideo ( vlc_object_t *p_this )
236 vout_thread_t * p_vout = (vout_thread_t *)p_this;
239 p_vout->p_sys = (vout_sys_t *)malloc( sizeof(vout_sys_t) );
240 if( !p_vout->p_sys ) return VLC_ENOMEM;
242 #ifdef MODULE_NAME_IS_wingapi
244 p_vout->p_sys->gapi_dll = LoadLibrary( _T("GX.DLL") );
245 if( p_vout->p_sys->gapi_dll == NULL )
247 msg_Warn( p_vout, "failed loading gx.dll" );
248 free( p_vout->p_sys );
252 GXOpenDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
253 _T("?GXOpenDisplay@@YAHPAUHWND__@@K@Z") );
254 GXCloseDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
255 _T("?GXCloseDisplay@@YAHXZ") );
256 GXBeginDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
257 _T("?GXBeginDraw@@YAPAXXZ") );
258 GXEndDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
259 _T("?GXEndDraw@@YAHXZ") );
260 GXGetDisplayProperties = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
261 _T("?GXGetDisplayProperties@@YA?AUGXDisplayProperties@@XZ") );
262 GXSuspend = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
263 _T("?GXSuspend@@YAHXZ") );
264 GXResume = GetProcAddress( p_vout->p_sys->gapi_dll,
265 _T("?GXResume@@YAHXZ") );
267 if( !GXOpenDisplay || !GXCloseDisplay || !GXBeginDraw || !GXEndDraw ||
268 !GXGetDisplayProperties || !GXSuspend || !GXResume )
270 msg_Err( p_vout, "failed GetProcAddress on gapi.dll" );
271 free( p_vout->p_sys );
275 msg_Dbg( p_vout, "GAPI DLL loaded" );
277 p_vout->p_sys->render_width = p_vout->render.i_width;
278 p_vout->p_sys->render_height = p_vout->render.i_height;
281 p_vout->p_sys->p_event = (vlc_object_t *)
282 vlc_object_create( p_vout, VLC_OBJECT_GENERIC );
283 if( !p_vout->p_sys->p_event )
285 free( p_vout->p_sys );
289 var_Create( p_vout->p_sys->p_event, "p_vout", VLC_VAR_ADDRESS );
290 val.p_address = (void *)p_vout;
291 var_Set( p_vout->p_sys->p_event, "p_vout", val );
293 SetRectEmpty( &p_vout->p_sys->rect_display );
294 SetRectEmpty( &p_vout->p_sys->rect_parent );
296 if( vlc_thread_create( p_vout->p_sys->p_event, "GDI Event Thread",
297 EventThread, 0, 1 ) )
299 msg_Err( p_vout, "cannot spawn EventThread" );
303 p_vout->pf_init = Init;
304 p_vout->pf_end = End;
305 p_vout->pf_manage = Manage;
306 p_vout->pf_render = Render;
307 #ifdef MODULE_NAME_IS_wingapi
308 p_vout->pf_display = DisplayGAPI;
310 p_vout->pf_display = DisplayGDI;
312 p_vout->p_sys->i_changes = 0;
314 p_vout->p_sys->b_focus = 0;
315 p_vout->p_sys->b_parent_focus = 0;
320 /*****************************************************************************
321 * CloseVideo: deactivate the GDI video output
322 *****************************************************************************/
323 static void CloseVideo ( vlc_object_t *p_this )
325 vout_thread_t * p_vout = (vout_thread_t *)p_this;
327 p_vout->p_sys->p_event->b_die = VLC_TRUE;
328 PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0 );
329 vlc_thread_join( p_vout->p_sys->p_event );
331 #ifdef MODULE_NAME_IS_wingapi
332 FreeLibrary( p_vout->p_sys->gapi_dll );
335 var_Destroy( p_vout->p_sys->p_event, "p_vout" );
336 vlc_object_destroy( p_vout->p_sys->p_event );
337 free( p_vout->p_sys );
340 /*****************************************************************************
341 * Init: initialize video thread output method
342 *****************************************************************************/
343 static int Init( vout_thread_t *p_vout )
347 p_vout->p_sys->rect_display.left = 0;
348 p_vout->p_sys->rect_display.top = 0;
349 p_vout->p_sys->rect_display.right = GetSystemMetrics(SM_CXSCREEN);
350 p_vout->p_sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
352 p_vout->p_sys->b_video_display = VLC_TRUE;
353 p_vout->p_sys->p_event->b_die = VLC_FALSE;
355 I_OUTPUTPICTURES = 0;
357 /* Initialize the output structure */
358 switch( p_vout->p_sys->i_depth )
361 p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2');
362 p_vout->output.pf_setpalette = SetPalette;
365 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5');
366 p_vout->output.i_rmask = 0x7c00;
367 p_vout->output.i_gmask = 0x03e0;
368 p_vout->output.i_bmask = 0x001f;
371 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
372 p_vout->output.i_rmask = 0xf800;
373 p_vout->output.i_gmask = 0x07e0;
374 p_vout->output.i_bmask = 0x001f;
377 p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
378 p_vout->output.i_rmask = 0x00ff0000;
379 p_vout->output.i_gmask = 0x0000ff00;
380 p_vout->output.i_bmask = 0x000000ff;
383 p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
384 p_vout->output.i_rmask = 0x00ff0000;
385 p_vout->output.i_gmask = 0x0000ff00;
386 p_vout->output.i_bmask = 0x000000ff;
389 msg_Err( p_vout, "screen depth %i not supported",
390 p_vout->p_sys->i_depth );
395 p_pic = &p_vout->p_picture[0];
397 #ifdef MODULE_NAME_IS_wingapi
398 p_vout->output.i_width = 0;
399 p_vout->output.i_height = 0;
400 p_pic->pf_lock = GAPILockSurface;
401 p_pic->pf_unlock = GAPIUnlockSurface;
403 GAPILockSurface( p_vout, p_pic );
404 p_vout->i_changes = 0;
405 p_vout->output.i_width = p_vout->p_sys->render_width;
406 p_vout->output.i_height = p_vout->p_sys->render_height;
409 p_vout->output.i_width = p_vout->render.i_width;
410 p_vout->output.i_height = p_vout->render.i_height;
412 p_vout->fmt_out = p_vout->fmt_in;
413 p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
416 p_vout->output.i_aspect = p_vout->render.i_aspect;
418 p_pic->p->p_pixels = p_vout->p_sys->p_pic_buffer;
419 p_pic->p->i_lines = p_vout->output.i_height;
420 p_pic->p->i_visible_lines = p_vout->output.i_height;
421 p_pic->p->i_pitch = p_vout->p_sys->i_pic_pitch;
422 p_pic->p->i_pixel_pitch = p_vout->p_sys->i_pic_pixel_pitch;
423 p_pic->p->i_visible_pitch = p_vout->output.i_width *
424 p_pic->p->i_pixel_pitch;
426 p_pic->i_status = DESTROYED_PICTURE;
427 p_pic->i_type = DIRECT_PICTURE;
429 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES++ ] = p_pic;
434 /*****************************************************************************
435 * End: terminate video thread output method
436 *****************************************************************************/
437 static void End( vout_thread_t *p_vout )
441 /*****************************************************************************
442 * Manage: handle events
443 *****************************************************************************
444 * This function should be called regularly by video output thread. It manages
445 * console events. It returns a non null value on error.
446 *****************************************************************************/
447 static int Manage( vout_thread_t *p_vout )
450 WINDOWPLACEMENT window_placement;
453 /* If we do not control our window, we check for geometry changes
454 * ourselves because the parent might not send us its events. */
455 if( p_vout->p_sys->hparent && !p_vout->b_fullscreen )
460 GetClientRect( p_vout->p_sys->hparent, &rect_parent );
461 point.x = point.y = 0;
462 ClientToScreen( p_vout->p_sys->hparent, &point );
463 OffsetRect( &rect_parent, point.x, point.y );
465 if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) )
467 int i_x, i_y, i_width, i_height;
468 p_vout->p_sys->rect_parent = rect_parent;
470 /* This one is to force the update even if only
471 * the position has changed */
472 SetWindowPos( p_vout->p_sys->hwnd, 0, 1, 1,
473 rect_parent.right - rect_parent.left,
474 rect_parent.bottom - rect_parent.top, 0 );
476 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
477 rect_parent.right - rect_parent.left,
478 rect_parent.bottom - rect_parent.top, 0 );
480 vout_PlacePicture( p_vout, rect_parent.right - rect_parent.left,
481 rect_parent.bottom - rect_parent.top,
482 &i_x, &i_y, &i_width, &i_height );
484 SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
485 i_x, i_y, i_width, i_height, 0 );
489 /* We used to call the Win32 PeekMessage function here to read the window
490 * messages. But since window can stay blocked into this function for a
491 * long time (for example when you move your window on the screen), I
492 * decided to isolate PeekMessage in another thread. */
497 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
498 || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
503 HWND hwnd = (p_vout->p_sys->hparent && p_vout->p_sys->hfswnd) ?
504 p_vout->p_sys->hfswnd : p_vout->p_sys->hwnd;
506 p_vout->b_fullscreen = ! p_vout->b_fullscreen;
508 /* We need to switch between Maximized and Normal sized window */
510 window_placement.length = sizeof(WINDOWPLACEMENT);
511 GetWindowPlacement( hwnd, &window_placement );
513 if( p_vout->b_fullscreen )
516 /* Change window style, no borders and no title bar */
517 int i_style = WS_CLIPCHILDREN | WS_VISIBLE;
518 SetWindowLong( hwnd, GWL_STYLE, i_style );
520 if( p_vout->p_sys->hparent )
522 /* Retrieve current window position so fullscreen will happen
523 * on the right screen */
526 ClientToScreen( p_vout->p_sys->hwnd, &point );
527 GetClientRect( p_vout->p_sys->hwnd, &rect );
528 SetWindowPos( hwnd, 0, point.x, point.y,
529 rect.right, rect.bottom,
530 SWP_NOZORDER|SWP_FRAMECHANGED );
531 GetWindowPlacement( hwnd, &window_placement );
534 /* Maximize window */
535 window_placement.showCmd = SW_SHOWMAXIMIZED;
536 SetWindowPlacement( hwnd, &window_placement );
537 SetWindowPos( hwnd, 0, 0, 0, 0, 0,
538 SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
541 if( p_vout->p_sys->hparent )
544 GetClientRect( hwnd, &rect );
545 SetParent( p_vout->p_sys->hwnd, hwnd );
546 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
547 rect.right, rect.bottom,
548 SWP_NOZORDER|SWP_FRAMECHANGED );
551 ShowWindow( hwnd, SW_SHOW );
552 SetForegroundWindow( hwnd );
556 /* Change window style, no borders and no title bar */
557 //SetWindowLong( hwnd, GWL_STYLE, p_vout->p_sys->i_window_style );
561 window_placement.showCmd = SW_SHOWNORMAL;
562 SetWindowPlacement( hwnd, &window_placement );
563 SetWindowPos( hwnd, 0, 0, 0, 0, 0,
564 SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
567 if( p_vout->p_sys->hparent )
570 GetClientRect( p_vout->p_sys->hparent, &rect );
571 SetParent( p_vout->p_sys->hwnd, p_vout->p_sys->hparent );
572 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
573 rect.right, rect.bottom,
574 SWP_NOZORDER|SWP_FRAMECHANGED );
576 ShowWindow( hwnd, SW_HIDE );
577 SetForegroundWindow( p_vout->p_sys->hparent );
580 /* Make sure the mouse cursor is displayed */
581 //PostMessage( p_vout->p_sys->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 );
584 /* Change window style, borders and title bar */
585 ShowWindow( p_vout->p_sys->hwnd, SW_SHOW );
586 UpdateWindow( p_vout->p_sys->hwnd );
588 /* Update the object variable and trigger callback */
589 val.b_bool = p_vout->b_fullscreen;
590 var_Set( p_vout, "fullscreen", val );
592 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
593 p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
599 /*****************************************************************************
600 * Render: render previously calculated output
601 *****************************************************************************/
602 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
604 /* No need to do anything, the fake direct buffers stay as they are */
607 /*****************************************************************************
608 * Display: displays previously rendered output
609 *****************************************************************************/
610 #define rect_src p_vout->p_sys->rect_src
611 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
612 #define rect_dest p_vout->p_sys->rect_dest
613 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
615 #ifndef MODULE_NAME_IS_wingapi
616 static void DisplayGDI( vout_thread_t *p_vout, picture_t *p_pic )
618 vout_sys_t *p_sys = p_vout->p_sys;
619 RECT rect_dst = rect_dest_clipped;
620 HDC hdc = GetDC( p_sys->hvideownd );
622 OffsetRect( &rect_dst, -rect_dest.left, -rect_dest.top );
623 SelectObject( p_sys->off_dc, p_sys->off_bitmap );
625 if( rect_dest_clipped.right - rect_dest_clipped.left !=
626 rect_src_clipped.right - rect_src_clipped.left ||
627 rect_dest_clipped.bottom - rect_dest_clipped.top !=
628 rect_src_clipped.bottom - rect_src_clipped.top )
630 StretchBlt( hdc, rect_dst.left, rect_dst.top,
631 rect_dst.right, rect_dst.bottom,
632 p_sys->off_dc, rect_src_clipped.left, rect_src_clipped.top,
633 rect_src_clipped.right, rect_src_clipped.bottom, SRCCOPY );
637 BitBlt( hdc, rect_dst.left, rect_dst.top,
638 rect_dst.right, rect_dst.bottom,
639 p_sys->off_dc, rect_src_clipped.left,
640 rect_src_clipped.top, SRCCOPY );
643 ReleaseDC( p_sys->hwnd, hdc );
647 static int GAPILockSurface( vout_thread_t *p_vout, picture_t *p_pic )
649 vout_sys_t *p_sys = p_vout->p_sys;
650 int i_x, i_y, i_width, i_height;
654 /* Undo the display */
655 if( ( GetForegroundWindow() != GetParent(p_sys->hwnd) ) ||
656 ( p_sys->b_video_display == VLC_FALSE ) )
658 //return VLC_EGENERIC;
661 GetClientRect( p_sys->hwnd, &video_rect);
662 vout_PlacePicture( p_vout, video_rect.right - video_rect.left,
663 video_rect.bottom - video_rect.top,
664 &i_x, &i_y, &i_width, &i_height );
665 point.x = point.y = 0;
666 ClientToScreen( p_sys->hwnd, &point );
667 i_x += point.x + video_rect.left;
668 i_y += point.y + video_rect.top;
670 if( i_width != p_vout->output.i_width ||
671 i_height != p_vout->output.i_height )
673 GXDisplayProperties gxdisplayprop = GXGetDisplayProperties();
675 p_sys->render_width = i_width;
676 p_sys->render_height = i_height;
677 p_vout->i_changes |= VOUT_SIZE_CHANGE;
679 msg_Dbg( p_vout, "vout size change (%ix%i -> %ix%i)",
680 i_width, i_height, p_vout->output.i_width,
681 p_vout->output.i_height );
683 p_vout->p_sys->i_pic_pixel_pitch = gxdisplayprop.cbxPitch;
684 p_vout->p_sys->i_pic_pitch = gxdisplayprop.cbyPitch;
689 GXDisplayProperties gxdisplayprop;
690 RECT display_rect, dest_rect;
691 uint8_t *p_dest, *p_src = p_pic->p->p_pixels;
693 video_rect.left = i_x; video_rect.top = i_y;
694 video_rect.right = i_x + i_width;
695 video_rect.bottom = i_y + i_height;
697 gxdisplayprop = GXGetDisplayProperties();
698 display_rect.left = 0; display_rect.top = 0;
699 display_rect.right = gxdisplayprop.cxWidth;
700 display_rect.bottom = gxdisplayprop.cyHeight;
702 if( !IntersectRect( &dest_rect, &video_rect, &display_rect ) )
708 msg_Err( p_vout, "video (%d,%d,%d,%d) display (%d,%d,%d,%d) "
709 "dest (%d,%d,%d,%d)",
710 video_rect.left, video_rect.right,
711 video_rect.top, video_rect.bottom,
712 display_rect.left, display_rect.right,
713 display_rect.top, display_rect.bottom,
714 dest_rect.left, dest_rect.right,
715 dest_rect.top, dest_rect.bottom );
718 if( !(p_dest = GXBeginDraw()) )
721 msg_Err( p_vout, "GXBeginDraw error %d ", GetLastError() );
726 p_src += (dest_rect.left - video_rect.left) * gxdisplayprop.cbxPitch +
727 (dest_rect.top - video_rect.top) * p_pic->p->i_pitch;
728 p_dest += dest_rect.left * gxdisplayprop.cbxPitch +
729 dest_rect.top * gxdisplayprop.cbyPitch;
730 i_width = dest_rect.right - dest_rect.left;
731 i_height = dest_rect.bottom - dest_rect.top;
733 p_pic->p->p_pixels = p_dest;
739 static int GAPIUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
745 static void DisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic )
751 #undef rect_src_clipped
753 #undef rect_dest_clipped
754 /*****************************************************************************
755 * SetPalette: sets an 8 bpp palette
756 *****************************************************************************/
757 static void SetPalette( vout_thread_t *p_vout,
758 uint16_t *red, uint16_t *green, uint16_t *blue )
760 msg_Err( p_vout, "FIXME: SetPalette unimplemented" );
763 /*****************************************************************************
764 * EventThread: Event handling thread
765 *****************************************************************************/
766 static void EventThread ( vlc_object_t *p_event )
768 vout_thread_t *p_vout;
775 /* Initialisations */
776 var_Get( p_event, "p_vout", &val );
777 p_vout = (vout_thread_t *)val.p_address;
779 /* Register window class */
780 memset( &wc, 0, sizeof(wc) );
781 wc.style = CS_HREDRAW | CS_VREDRAW;
782 wc.lpfnWndProc = (WNDPROC)WndProc;
785 wc.hInstance = GetModuleHandle(NULL);
787 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
788 wc.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
790 wc.lpszClassName = _T("VLC WinGDI");
791 RegisterClass( &wc );
793 /* Register the video sub-window class */
794 wc.lpszClassName = _T("VLC WinGDI video"); wc.hIcon = 0;
797 /* Create output window */
798 p_vout->p_sys->hparent = (HWND)
799 vout_RequestWindow( p_vout, &p_vout->p_sys->i_window_x,
800 &p_vout->p_sys->i_window_y,
801 (unsigned int *)&p_vout->p_sys->i_window_width,
802 (unsigned int *)&p_vout->p_sys->i_window_height );
804 if( p_vout->p_sys->hparent )
805 ShowWindow( p_vout->p_sys->hparent, SW_SHOW );
807 if( p_vout->p_sys->hparent )
808 i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD;
810 i_style = WS_OVERLAPPEDWINDOW|WS_SIZEBOX|WS_VISIBLE|WS_CLIPCHILDREN;
812 p_vout->p_sys->i_window_style = i_style;
814 p_vout->p_sys->hwnd =
815 CreateWindow( _T("VLC WinGDI"), _T(VOUT_TITLE), i_style,
816 (p_vout->p_sys->i_window_x < 0) ? CW_USEDEFAULT :
817 p_vout->p_sys->i_window_x, /* default X coordinate */
818 (p_vout->p_sys->i_window_y < 0) ? CW_USEDEFAULT :
819 p_vout->p_sys->i_window_y, /* default Y coordinate */
820 p_vout->p_sys->i_window_width,
821 p_vout->p_sys->i_window_height + 10,
822 p_vout->p_sys->hparent, NULL,
823 GetModuleHandle(NULL), (LPVOID)p_vout );
825 if( !p_vout->p_sys->hwnd )
827 msg_Warn( p_vout, "couldn't create window" );
830 msg_Warn( p_vout, "Created WinGDI window" );
832 if( p_vout->p_sys->hparent )
836 /* We don't want the window owner to overwrite our client area */
837 i_style = GetWindowLong( p_vout->p_sys->hparent, GWL_STYLE );
839 if( !(i_style & WS_CLIPCHILDREN) )
840 /* Hmmm, apparently this is a blocking call... */
841 SetWindowLong( p_vout->p_sys->hparent, GWL_STYLE,
842 i_style | WS_CLIPCHILDREN );
844 /* Create our fullscreen window */
845 p_vout->p_sys->hfswnd =
846 CreateWindowEx( WS_EX_APPWINDOW, _T("VLC WinGDI"),
848 WS_NONAVDONEBUTTON|WS_CLIPCHILDREN,
849 CW_USEDEFAULT, CW_USEDEFAULT,
850 CW_USEDEFAULT, CW_USEDEFAULT,
851 NULL, NULL, GetModuleHandle(NULL), (LPVOID)p_vout);
854 /* Display our window */
855 ShowWindow( p_vout->p_sys->hwnd, SW_SHOW );
856 UpdateWindow( p_vout->p_sys->hwnd );
858 /* Create video sub-window */
859 p_vout->p_sys->hvideownd =
860 CreateWindow( _T("VLC WinGDI video"), _T(""), /* window class */
861 WS_CHILD | WS_VISIBLE, /* window style */
862 CW_USEDEFAULT, CW_USEDEFAULT, /* default coordinates */
863 CW_USEDEFAULT, CW_USEDEFAULT,
864 p_vout->p_sys->hwnd, /* parent window */
865 NULL, GetModuleHandle(NULL),
866 (LPVOID)p_vout ); /* send p_vout to WM_CREATE */
868 /* Initialize offscreen buffer */
869 InitBuffers( p_vout );
871 p_vout->pf_control = Control;
873 /* Tell the video output we're ready to receive data */
874 vlc_thread_ready( p_event );
876 while( !p_event->b_die && GetMessage( &msg, 0, 0, 0 ) )
878 /* Check if we are asked to exit */
879 if( p_event->b_die ) break;
881 switch( msg.message )
887 p_event->p_libvlc->b_die = VLC_TRUE;
890 TranslateMessage( &msg );
898 p_event->p_libvlc->b_die = VLC_TRUE;
904 TranslateMessage( &msg );
905 DispatchMessage( &msg );
910 msg_Dbg( p_vout, "CloseWindow" );
912 #ifdef MODULE_NAME_IS_wingapi
915 DeleteDC( p_vout->p_sys->off_dc );
916 DeleteObject( p_vout->p_sys->off_bitmap );
919 DestroyWindow( p_vout->p_sys->hwnd );
920 if( p_vout->p_sys->hfswnd ) DestroyWindow( p_vout->p_sys->hfswnd );
922 if( p_vout->p_sys->hparent )
923 vout_ReleaseWindow( p_vout, (void *)p_vout->p_sys->hparent );
926 /*****************************************************************************
927 * UpdateRects: update clipping rectangles
928 *****************************************************************************
929 * This function is called when the window position or size are changed, and
930 * its job is to update the source and destination RECTs used to display the
932 *****************************************************************************/
933 static void UpdateRects( vout_thread_t *p_vout, vlc_bool_t b_force )
935 #define rect_src p_vout->p_sys->rect_src
936 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
937 #define rect_dest p_vout->p_sys->rect_dest
938 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
940 int i_width, i_height, i_x, i_y;
945 /* Retrieve the window size */
946 GetClientRect( p_vout->p_sys->hwnd, &rect );
948 /* Retrieve the window position */
949 point.x = point.y = 0;
950 ClientToScreen( p_vout->p_sys->hwnd, &point );
952 /* If nothing changed, we can return */
954 && p_vout->p_sys->i_window_width == rect.right
955 && p_vout->p_sys->i_window_height == rect.bottom
956 && p_vout->p_sys->i_window_x == point.x
957 && p_vout->p_sys->i_window_y == point.y )
962 /* Update the window position and size */
963 p_vout->p_sys->i_window_x = point.x;
964 p_vout->p_sys->i_window_y = point.y;
965 p_vout->p_sys->i_window_width = rect.right;
966 p_vout->p_sys->i_window_height = rect.bottom;
968 vout_PlacePicture( p_vout, rect.right, rect.bottom,
969 &i_x, &i_y, &i_width, &i_height );
971 if( p_vout->p_sys->hvideownd )
972 SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
973 i_x, i_y, i_width, i_height, 0 );
975 /* Destination image position and dimensions */
976 rect_dest.left = point.x + i_x;
977 rect_dest.right = rect_dest.left + i_width;
978 rect_dest.top = point.y + i_y;
979 rect_dest.bottom = rect_dest.top + i_height;
981 /* Clip the destination window */
982 if( !IntersectRect( &rect_dest_clipped, &rect_dest,
983 &p_vout->p_sys->rect_display ) )
985 SetRectEmpty( &rect_src_clipped );
990 msg_Dbg( p_vout, "image_dst_clipped coords: %i,%i,%i,%i",
991 rect_dest_clipped.left, rect_dest_clipped.top,
992 rect_dest_clipped.right, rect_dest_clipped.bottom );
995 /* the 2 following lines are to fix a bug when clicking on the desktop */
996 if( (rect_dest_clipped.right - rect_dest_clipped.left)==0 ||
997 (rect_dest_clipped.bottom - rect_dest_clipped.top)==0 )
999 SetRectEmpty( &rect_src_clipped );
1003 /* src image dimensions */
1006 rect_src.right = p_vout->output.i_width;
1007 rect_src.bottom = p_vout->output.i_height;
1009 /* Clip the source image */
1010 rect_src_clipped.left = (rect_dest_clipped.left - rect_dest.left) *
1011 p_vout->output.i_width / (rect_dest.right - rect_dest.left);
1012 rect_src_clipped.right = p_vout->output.i_width -
1013 (rect_dest.right - rect_dest_clipped.right) * p_vout->output.i_width /
1014 (rect_dest.right - rect_dest.left);
1015 rect_src_clipped.top = (rect_dest_clipped.top - rect_dest.top) *
1016 p_vout->output.i_height / (rect_dest.bottom - rect_dest.top);
1017 rect_src_clipped.bottom = p_vout->output.i_height -
1018 (rect_dest.bottom - rect_dest_clipped.bottom) * p_vout->output.i_height /
1019 (rect_dest.bottom - rect_dest.top);
1022 msg_Dbg( p_vout, "image_src_clipped coords: %i,%i,%i,%i",
1023 rect_src_clipped.left, rect_src_clipped.top,
1024 rect_src_clipped.right, rect_src_clipped.bottom );
1027 /* The destination coordinates need to be relative to the current
1028 * directdraw primary surface (display) */
1029 rect_dest_clipped.left -= p_vout->p_sys->rect_display.left;
1030 rect_dest_clipped.right -= p_vout->p_sys->rect_display.left;
1031 rect_dest_clipped.top -= p_vout->p_sys->rect_display.top;
1032 rect_dest_clipped.bottom -= p_vout->p_sys->rect_display.top;
1034 /* Signal the change in size/position */
1035 p_vout->p_sys->i_changes |= DX_POSITION_CHANGE;
1038 #undef rect_src_clipped
1040 #undef rect_dest_clipped
1043 /*****************************************************************************
1044 * Message handler for the main window
1045 *****************************************************************************/
1046 static long FAR PASCAL WndProc( HWND hWnd, UINT message,
1047 WPARAM wParam, LPARAM lParam )
1049 vout_thread_t *p_vout;
1051 if( message == WM_CREATE )
1053 /* Store p_vout for future use */
1054 p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
1055 SetWindowLongPtr( hWnd, GWLP_USERDATA, (LONG_PTR)p_vout );
1056 if( p_vout ) msg_Dbg( p_vout, "create: %p", hWnd );
1060 p_vout = (vout_thread_t *)GetWindowLongPtr( hWnd, GWLP_USERDATA );
1064 /* Catch the screensaver and the monitor turn-off */
1065 if( message == WM_SYSCOMMAND &&
1066 ( (wParam & 0xFFF0) == SC_SCREENSAVE || (wParam & 0xFFF0) == SC_MONITORPOWER ) )
1068 //if( p_vout ) msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND screensaver" );
1069 return 0; /* this stops them from happening */
1075 /* Hmmm mozilla does manage somehow to save the pointer to our
1076 * windowproc and still calls it after the vout has been closed. */
1077 return DefWindowProc(hWnd, message, wParam, lParam);
1080 if( hWnd != p_vout->p_sys->hwnd &&
1081 hWnd != p_vout->p_sys->hfswnd &&
1082 hWnd != p_vout->p_sys->hvideownd )
1083 return DefWindowProc(hWnd, message, wParam, lParam);
1087 case WM_WINDOWPOSCHANGED:
1088 if( hWnd == p_vout->p_sys->hwnd )
1089 UpdateRects( p_vout, VLC_TRUE );
1094 msg_Err( p_vout, "WM_ACTIVATE: %i", wParam );
1095 if( wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE )
1097 else if( wParam == WA_INACTIVE )
1103 p_vout->p_sys->b_focus = VLC_FALSE;
1104 if( !p_vout->p_sys->b_parent_focus ) GXSuspend();
1106 if( hWnd == p_vout->p_sys->hfswnd )
1109 HWND htbar = FindWindow( _T("HHTaskbar"), NULL );
1110 ShowWindow( htbar, SW_SHOW );
1114 if( !p_vout->p_sys->hparent ||
1115 hWnd == p_vout->p_sys->hfswnd )
1117 SHFullScreen( hWnd, SHFS_SHOWSIPBUTTON );
1122 p_vout->p_sys->b_focus = VLC_TRUE;
1125 if( p_vout->p_sys->hparent &&
1126 hWnd != p_vout->p_sys->hfswnd && p_vout->b_fullscreen )
1127 p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE;
1129 if( hWnd == p_vout->p_sys->hfswnd )
1132 HWND htbar = FindWindow( _T("HHTaskbar"), NULL );
1133 ShowWindow( htbar, SW_HIDE );
1137 if( !p_vout->p_sys->hparent ||
1138 hWnd == p_vout->p_sys->hfswnd )
1140 SHFullScreen( hWnd, SHFS_HIDESIPBUTTON );
1144 case WM_LBUTTONDOWN:
1145 p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE;
1152 case WM_INITMENUPOPUP:
1153 p_vout->p_sys->b_video_display = VLC_FALSE;
1157 // Redo the video display because menu can be closed
1158 // FIXME verify if p_child_window exits
1159 if( (((NMHDR *)lParam)->code) == NM_CUSTOMDRAW )
1160 p_vout->p_sys->b_video_display = VLC_TRUE;
1163 /* the user wants to close the window */
1166 playlist_t * p_playlist =
1167 (playlist_t *)vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1169 if( p_playlist == NULL ) return 0;
1171 playlist_Stop( p_playlist );
1172 vlc_object_release( p_playlist );
1177 msg_Dbg( p_vout, "WinProc WM_DESTROY" );
1178 PostQuitMessage( 0 );
1182 return DefWindowProc( hWnd, message, wParam, lParam );
1188 /*****************************************************************************
1189 * InitBuffers: initialize an offscreen bitmap for direct buffer operations.
1190 *****************************************************************************/
1191 static void InitBuffers( vout_thread_t *p_vout )
1193 BITMAPINFOHEADER *p_header = &p_vout->p_sys->bitmapinfo.bmiHeader;
1194 BITMAPINFO *p_info = &p_vout->p_sys->bitmapinfo;
1195 int i_pixels = p_vout->render.i_height * p_vout->render.i_width;
1196 HDC window_dc = GetDC( p_vout->p_sys->hvideownd );
1198 /* Get screen properties */
1199 #ifdef MODULE_NAME_IS_wingapi
1200 GXDisplayProperties gx_displayprop = GXGetDisplayProperties();
1201 p_vout->p_sys->i_depth = gx_displayprop.cBPP;
1203 p_vout->p_sys->i_depth = GetDeviceCaps( window_dc, PLANES ) *
1204 GetDeviceCaps( window_dc, BITSPIXEL );
1206 msg_Dbg( p_vout, "GDI depth is %i", p_vout->p_sys->i_depth );
1208 #ifdef MODULE_NAME_IS_wingapi
1209 GXOpenDisplay( p_vout->p_sys->hvideownd, GX_FULLSCREEN );
1213 /* Initialize offscreen bitmap */
1214 memset( p_info, 0, sizeof( BITMAPINFO ) + 3 * sizeof( RGBQUAD ) );
1216 p_header->biSize = sizeof( BITMAPINFOHEADER );
1217 p_header->biSizeImage = 0;
1218 p_header->biPlanes = 1;
1219 switch( p_vout->p_sys->i_depth )
1222 p_header->biBitCount = 8;
1223 p_header->biCompression = BI_RGB;
1224 /* FIXME: we need a palette here */
1227 p_header->biBitCount = 15;
1228 p_header->biCompression = BI_BITFIELDS;//BI_RGB;
1229 ((DWORD*)p_info->bmiColors)[0] = 0x00007c00;
1230 ((DWORD*)p_info->bmiColors)[1] = 0x000003e0;
1231 ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
1234 p_header->biBitCount = 16;
1235 p_header->biCompression = BI_BITFIELDS;//BI_RGB;
1236 ((DWORD*)p_info->bmiColors)[0] = 0x0000f800;
1237 ((DWORD*)p_info->bmiColors)[1] = 0x000007e0;
1238 ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
1241 p_header->biBitCount = 24;
1242 p_header->biCompression = BI_RGB;
1243 ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
1244 ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
1245 ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
1248 p_header->biBitCount = 32;
1249 p_header->biCompression = BI_RGB;
1250 ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
1251 ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
1252 ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
1255 msg_Err( p_vout, "screen depth %i not supported",
1256 p_vout->p_sys->i_depth );
1260 p_header->biWidth = p_vout->render.i_width;
1261 p_header->biHeight = -p_vout->render.i_height;
1262 p_header->biClrImportant = 0;
1263 p_header->biClrUsed = 0;
1264 p_header->biXPelsPerMeter = 0;
1265 p_header->biYPelsPerMeter = 0;
1267 p_vout->p_sys->i_pic_pixel_pitch = p_header->biBitCount / 8;
1268 p_vout->p_sys->i_pic_pitch = p_header->biBitCount * p_header->biWidth / 8;
1270 p_vout->p_sys->off_bitmap =
1271 CreateDIBSection( window_dc, (BITMAPINFO *)p_header, DIB_RGB_COLORS,
1272 (void**)&p_vout->p_sys->p_pic_buffer, NULL, 0 );
1274 p_vout->p_sys->off_dc = CreateCompatibleDC( window_dc );
1276 SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap );
1277 ReleaseDC( 0, window_dc );
1281 /*****************************************************************************
1282 * Control: control facility for the vout
1283 *****************************************************************************/
1284 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
1286 unsigned int *pi_width, *pi_height;
1294 if( p_vout->p_sys->hparent )
1295 return vout_ControlWindow( p_vout,
1296 (void *)p_vout->p_sys->hparent, i_query, args );
1298 pi_width = va_arg( args, unsigned int * );
1299 pi_height = va_arg( args, unsigned int * );
1301 GetClientRect( p_vout->p_sys->hwnd, &rect_window );
1303 *pi_width = rect_window.right - rect_window.left;
1304 *pi_height = rect_window.bottom - rect_window.top;
1308 if( p_vout->p_sys->hparent )
1309 return vout_ControlWindow( p_vout,
1310 (void *)p_vout->p_sys->hparent, i_query, args );
1312 /* Update dimensions */
1313 rect_window.top = rect_window.left = 0;
1314 rect_window.right = va_arg( args, unsigned int );
1315 rect_window.bottom = va_arg( args, unsigned int );
1316 if( !rect_window.right ) rect_window.right = p_vout->i_window_width;
1317 if( !rect_window.bottom ) rect_window.bottom = p_vout->i_window_height;
1318 AdjustWindowRect( &rect_window, p_vout->p_sys->i_window_style, 0 );
1320 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
1321 rect_window.right - rect_window.left,
1322 rect_window.bottom - rect_window.top, SWP_NOMOVE );
1327 ShowWindow( p_vout->p_sys->hwnd, SW_HIDE );
1329 /* Change window style, borders and title bar */
1330 //vlc_mutex_lock( &p_vout->p_sys->lock );
1331 p_vout->p_sys->hparent = 0;
1332 //vlc_mutex_unlock( &p_vout->p_sys->lock );
1334 /* Retrieve the window position */
1335 point.x = point.y = 0;
1336 ClientToScreen( p_vout->p_sys->hwnd, &point );
1338 SetParent( p_vout->p_sys->hwnd, 0 );
1339 p_vout->p_sys->i_window_style =
1340 WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW | WS_SIZEBOX;
1341 SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE,
1342 p_vout->p_sys->i_window_style |
1343 (i_query == VOUT_CLOSE ? 0 : WS_VISIBLE) );
1344 SetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW );
1345 SetWindowPos( p_vout->p_sys->hwnd, 0, point.x, point.y, 0, 0,
1346 SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED );
1348 return vout_vaControlDefault( p_vout, i_query, args );
1350 case VOUT_SET_FOCUS:
1351 b_bool = va_arg( args, vlc_bool_t );
1353 p_vout->p_sys->b_parent_focus = b_bool;
1354 if( b_bool ) GXResume();
1355 else if( !p_vout->p_sys->b_focus ) GXSuspend();
1360 return vout_vaControlDefault( p_vout, i_query, args );