1 /*****************************************************************************
2 * wingdi.c : Win32 / WinCE GDI video output plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2002 VideoLAN
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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
28 #include <stdlib.h> /* malloc(), free() */
37 # define SHFS_HIDESIPBUTTON 0x0008
38 # define MENU_HEIGHT 26
40 # define SHFS_HIDESIPBUTTON 0
41 # define MENU_HEIGHT 0
44 #ifdef MODULE_NAME_IS_wingapi
45 typedef struct GXDisplayProperties {
52 } GXDisplayProperties;
54 typedef struct GXScreenRect {
61 # define GX_FULLSCREEN 0x01
62 # define GX_NORMALKEYS 0x02
63 # define GX_LANDSCAPEKEYS 0x03
66 # define kfLandscape 0x8
67 # define kfPalette 0x10
68 # define kfDirect 0x20
69 # define kfDirect555 0x40
70 # define kfDirect565 0x80
71 # define kfDirect888 0x100
72 # define kfDirect444 0x200
73 # define kfDirectInverted 0x400
75 #endif /* MODULE_NAME_IS_wingapi */
77 #define MAX_DIRECTBUFFERS 10
80 #ifndef WS_OVERLAPPEDWINDOW
81 # define WS_OVERLAPPEDWINDOW 0xcf0000
83 #ifndef WS_EX_NOPARENTNOTIFY
84 # define WS_EX_NOPARENTNOTIFY 4
86 #ifndef WS_EX_APPWINDOW
87 #define WS_EX_APPWINDOW 0x40000
89 #define SetWindowLongPtr SetWindowLong
90 #define GetWindowLongPtr GetWindowLong
91 #define GWLP_USERDATA GWL_USERDATA
94 #ifndef WS_NONAVDONEBUTTON
95 #define WS_NONAVDONEBUTTON 0
97 /*****************************************************************************
99 *****************************************************************************/
100 static int OpenVideo ( vlc_object_t * );
101 static void CloseVideo ( vlc_object_t * );
103 static int Init ( vout_thread_t * );
104 static void End ( vout_thread_t * );
105 static int Manage ( vout_thread_t * );
106 static void Render ( vout_thread_t *, picture_t * );
107 #ifdef MODULE_NAME_IS_wingapi
108 static void DisplayGAPI( vout_thread_t *, picture_t * );
109 static int GAPILockSurface( vout_thread_t *, picture_t * );
110 static int GAPIUnlockSurface( vout_thread_t *, picture_t * );
112 static void DisplayGDI( vout_thread_t *, picture_t * );
114 static void SetPalette( vout_thread_t *, uint16_t *, uint16_t *, uint16_t * );
116 static void EventThread ( vlc_object_t * );
117 static long FAR PASCAL WndProc ( HWND, UINT, WPARAM, LPARAM );
118 static void InitBuffers ( vout_thread_t * );
119 static void UpdateRects ( vout_thread_t *, vlc_bool_t );
121 static int Control( vout_thread_t *p_vout, int i_query, va_list args );
123 /*****************************************************************************
125 *****************************************************************************/
128 /* The event thread */
129 vlc_object_t * p_event;
131 /* Our video output window */
136 HWND hparent; /* Handle of the parent window */
137 WNDPROC pf_wndproc; /* Window handling callback */
138 volatile uint16_t i_changes; /* changes made to the video display */
139 RECT window_placement;
141 /* Window position and size */
149 /* Coordinates of src and dest images (used when blitting to display) */
151 RECT rect_src_clipped;
153 RECT rect_dest_clipped;
157 /* Our offscreen bitmap and its framebuffer */
160 uint8_t * p_pic_buffer;
162 int i_pic_pixel_pitch;
164 BITMAPINFO bitmapinfo;
172 vlc_bool_t b_video_display;
174 #ifdef MODULE_NAME_IS_wingapi
175 HINSTANCE gapi_dll; /* handle of the opened gapi dll */
178 int (*GXOpenDisplay)( HWND hWnd, DWORD dwFlags );
179 int (*GXCloseDisplay)();
180 void *(*GXBeginDraw)();
182 GXDisplayProperties (*GXGetDisplayProperties)();
188 #define GXOpenDisplay p_vout->p_sys->GXOpenDisplay
189 #define GXCloseDisplay p_vout->p_sys->GXCloseDisplay
190 #define GXBeginDraw p_vout->p_sys->GXBeginDraw
191 #define GXEndDraw p_vout->p_sys->GXEndDraw
192 #define GXGetDisplayProperties p_vout->p_sys->GXGetDisplayProperties
193 #define GXSuspend p_vout->p_sys->GXSuspend
194 #define GXResume p_vout->p_sys->GXResume
196 #define DX_POSITION_CHANGE 0x1000
198 /*****************************************************************************
200 *****************************************************************************/
202 #ifdef MODULE_NAME_IS_wingapi
203 set_description( _("Windows GAPI video output") );
204 set_capability( "video output", 20 );
206 set_description( _("Windows GDI video output") );
207 set_capability( "video output", 10 );
209 set_callbacks( OpenVideo, CloseVideo );
212 /*****************************************************************************
213 * OpenVideo: activate GDI video thread output method
214 *****************************************************************************/
215 static int OpenVideo ( vlc_object_t *p_this )
217 vout_thread_t * p_vout = (vout_thread_t *)p_this;
220 p_vout->p_sys = (vout_sys_t *)malloc( sizeof(vout_sys_t) );
221 if( !p_vout->p_sys ) return VLC_ENOMEM;
223 #ifdef MODULE_NAME_IS_wingapi
225 p_vout->p_sys->gapi_dll = LoadLibrary( _T("GX.DLL") );
226 if( p_vout->p_sys->gapi_dll == NULL )
228 msg_Warn( p_vout, "failed loading gx.dll" );
229 free( p_vout->p_sys );
233 GXOpenDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
234 _T("?GXOpenDisplay@@YAHPAUHWND__@@K@Z") );
235 GXCloseDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
236 _T("?GXCloseDisplay@@YAHXZ") );
237 GXBeginDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
238 _T("?GXBeginDraw@@YAPAXXZ") );
239 GXEndDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
240 _T("?GXEndDraw@@YAHXZ") );
241 GXGetDisplayProperties = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
242 _T("?GXGetDisplayProperties@@YA?AUGXDisplayProperties@@XZ") );
243 GXSuspend = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
244 _T("?GXSuspend@@YAHXZ") );
245 GXResume = GetProcAddress( p_vout->p_sys->gapi_dll,
246 _T("?GXResume@@YAHXZ") );
248 if( !GXOpenDisplay || !GXCloseDisplay || !GXBeginDraw || !GXEndDraw ||
249 !GXGetDisplayProperties || !GXSuspend || !GXResume )
251 msg_Err( p_vout, "failed GetProcAddress on gapi.dll" );
252 free( p_vout->p_sys );
256 msg_Dbg( p_vout, "GAPI DLL loaded" );
258 p_vout->p_sys->render_width = p_vout->render.i_width;
259 p_vout->p_sys->render_height = p_vout->render.i_height;
262 p_vout->p_sys->p_event = (vlc_object_t *)
263 vlc_object_create( p_vout, VLC_OBJECT_GENERIC );
264 if( !p_vout->p_sys->p_event )
266 free( p_vout->p_sys );
270 var_Create( p_vout->p_sys->p_event, "p_vout", VLC_VAR_ADDRESS );
271 val.p_address = (void *)p_vout;
272 var_Set( p_vout->p_sys->p_event, "p_vout", val );
274 SetRectEmpty( &p_vout->p_sys->rect_display );
275 SetRectEmpty( &p_vout->p_sys->rect_parent );
277 if( vlc_thread_create( p_vout->p_sys->p_event, "GDI Event Thread",
278 EventThread, 0, 1 ) )
280 msg_Err( p_vout, "cannot spawn EventThread" );
284 p_vout->pf_init = Init;
285 p_vout->pf_end = End;
286 p_vout->pf_manage = Manage;
287 p_vout->pf_render = Render;
288 #ifdef MODULE_NAME_IS_wingapi
289 p_vout->pf_display = DisplayGAPI;
291 p_vout->pf_display = DisplayGDI;
293 p_vout->p_sys->i_changes = 0;
298 /*****************************************************************************
299 * CloseVideo: deactivate the GDI video output
300 *****************************************************************************/
301 static void CloseVideo ( vlc_object_t *p_this )
303 vout_thread_t * p_vout = (vout_thread_t *)p_this;
305 p_vout->p_sys->p_event->b_die = VLC_TRUE;
306 PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0 );
307 vlc_thread_join( p_vout->p_sys->p_event );
309 #ifdef MODULE_NAME_IS_wingapi
310 FreeLibrary( p_vout->p_sys->gapi_dll );
313 var_Destroy( p_vout->p_sys->p_event, "p_vout" );
314 vlc_object_destroy( p_vout->p_sys->p_event );
315 free( p_vout->p_sys );
318 /*****************************************************************************
319 * Init: initialize video thread output method
320 *****************************************************************************/
321 static int Init( vout_thread_t *p_vout )
325 p_vout->p_sys->rect_display.left = 0;
326 p_vout->p_sys->rect_display.top = 0;
327 p_vout->p_sys->rect_display.right = GetSystemMetrics(SM_CXSCREEN);
328 p_vout->p_sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
330 p_vout->p_sys->b_video_display = VLC_TRUE;
331 p_vout->p_sys->p_event->b_die = VLC_FALSE;
333 I_OUTPUTPICTURES = 0;
335 /* Initialize the output structure */
336 switch( p_vout->p_sys->i_depth )
339 p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2');
340 p_vout->output.pf_setpalette = SetPalette;
343 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5');
344 p_vout->output.i_rmask = 0x7c00;
345 p_vout->output.i_gmask = 0x03e0;
346 p_vout->output.i_bmask = 0x001f;
349 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
350 p_vout->output.i_rmask = 0xf800;
351 p_vout->output.i_gmask = 0x07e0;
352 p_vout->output.i_bmask = 0x001f;
355 p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
356 p_vout->output.i_rmask = 0x00ff0000;
357 p_vout->output.i_gmask = 0x0000ff00;
358 p_vout->output.i_bmask = 0x000000ff;
361 p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
362 p_vout->output.i_rmask = 0x00ff0000;
363 p_vout->output.i_gmask = 0x0000ff00;
364 p_vout->output.i_bmask = 0x000000ff;
367 msg_Err( p_vout, "screen depth %i not supported",
368 p_vout->p_sys->i_depth );
373 p_pic = &p_vout->p_picture[0];
375 #ifdef MODULE_NAME_IS_wingapi
376 p_vout->output.i_width = 0;
377 p_vout->output.i_height = 0;
378 p_pic->pf_lock = GAPILockSurface;
379 p_pic->pf_unlock = GAPIUnlockSurface;
381 GAPILockSurface( p_vout, p_pic );
382 p_vout->i_changes = 0;
383 p_vout->output.i_width = p_vout->p_sys->render_width;
384 p_vout->output.i_height = p_vout->p_sys->render_height;
387 p_vout->output.i_width = p_vout->render.i_width;
388 p_vout->output.i_height = p_vout->render.i_height;
390 p_vout->output.i_aspect = p_vout->render.i_aspect;
392 p_pic->p->p_pixels = p_vout->p_sys->p_pic_buffer;
393 p_pic->p->i_lines = p_vout->output.i_height;
394 p_pic->p->i_visible_lines = p_vout->output.i_height;
395 p_pic->p->i_pitch = p_vout->p_sys->i_pic_pitch;
396 p_pic->p->i_pixel_pitch = p_vout->p_sys->i_pic_pixel_pitch;
397 p_pic->p->i_visible_pitch = p_vout->output.i_width *
398 p_pic->p->i_pixel_pitch;
400 p_pic->i_status = DESTROYED_PICTURE;
401 p_pic->i_type = DIRECT_PICTURE;
403 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES++ ] = p_pic;
408 /*****************************************************************************
409 * End: terminate video thread output method
410 *****************************************************************************/
411 static void End( vout_thread_t *p_vout )
415 /*****************************************************************************
416 * Manage: handle events
417 *****************************************************************************
418 * This function should be called regularly by video output thread. It manages
419 * console events. It returns a non null value on error.
420 *****************************************************************************/
421 static int Manage( vout_thread_t *p_vout )
424 WINDOWPLACEMENT window_placement;
427 /* If we do not control our window, we check for geometry changes
428 * ourselves because the parent might not send us its events. */
429 if( p_vout->p_sys->hparent && !p_vout->b_fullscreen )
434 GetClientRect( p_vout->p_sys->hparent, &rect_parent );
435 point.x = point.y = 0;
436 ClientToScreen( p_vout->p_sys->hparent, &point );
437 OffsetRect( &rect_parent, point.x, point.y );
439 if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) )
441 int i_x, i_y, i_width, i_height;
442 p_vout->p_sys->rect_parent = rect_parent;
444 /* This one is to force the update even if only
445 * the position has changed */
446 SetWindowPos( p_vout->p_sys->hwnd, 0, 1, 1,
447 rect_parent.right - rect_parent.left,
448 rect_parent.bottom - rect_parent.top, 0 );
450 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
451 rect_parent.right - rect_parent.left,
452 rect_parent.bottom - rect_parent.top, 0 );
454 vout_PlacePicture( p_vout, rect_parent.right - rect_parent.left,
455 rect_parent.bottom - rect_parent.top,
456 &i_x, &i_y, &i_width, &i_height );
458 SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
459 i_x, i_y, i_width, i_height, 0 );
463 /* We used to call the Win32 PeekMessage function here to read the window
464 * messages. But since window can stay blocked into this function for a
465 * long time (for example when you move your window on the screen), I
466 * decided to isolate PeekMessage in another thread. */
471 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
472 || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
477 HWND hwnd = (p_vout->p_sys->hparent && p_vout->p_sys->hfswnd) ?
478 p_vout->p_sys->hfswnd : p_vout->p_sys->hwnd;
480 p_vout->b_fullscreen = ! p_vout->b_fullscreen;
482 /* We need to switch between Maximized and Normal sized window */
484 window_placement.length = sizeof(WINDOWPLACEMENT);
485 GetWindowPlacement( hwnd, &window_placement );
487 if( p_vout->b_fullscreen )
490 /* Change window style, no borders and no title bar */
491 int i_style = WS_CLIPCHILDREN | WS_VISIBLE;
492 SetWindowLong( hwnd, GWL_STYLE, i_style );
494 if( p_vout->p_sys->hparent )
496 /* Retrieve current window position so fullscreen will happen
497 * on the right screen */
500 ClientToScreen( p_vout->p_sys->hwnd, &point );
501 GetClientRect( p_vout->p_sys->hwnd, &rect );
502 SetWindowPos( hwnd, 0, point.x, point.y,
503 rect.right, rect.bottom,
504 SWP_NOZORDER|SWP_FRAMECHANGED );
505 GetWindowPlacement( hwnd, &window_placement );
508 /* Maximize window */
509 window_placement.showCmd = SW_SHOWMAXIMIZED;
510 SetWindowPlacement( hwnd, &window_placement );
511 SetWindowPos( hwnd, 0, 0, 0, 0, 0,
512 SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
515 if( p_vout->p_sys->hparent )
518 GetClientRect( hwnd, &rect );
519 SetParent( p_vout->p_sys->hwnd, hwnd );
520 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
521 rect.right, rect.bottom,
522 SWP_NOZORDER|SWP_FRAMECHANGED );
525 ShowWindow( hwnd, SW_SHOW );
526 SetForegroundWindow( hwnd );
530 /* Change window style, no borders and no title bar */
531 //SetWindowLong( hwnd, GWL_STYLE, p_vout->p_sys->i_window_style );
535 window_placement.showCmd = SW_SHOWNORMAL;
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( p_vout->p_sys->hparent, &rect );
545 SetParent( p_vout->p_sys->hwnd, p_vout->p_sys->hparent );
546 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
547 rect.right, rect.bottom,
548 SWP_NOZORDER|SWP_FRAMECHANGED );
550 ShowWindow( hwnd, SW_HIDE );
551 SetForegroundWindow( p_vout->p_sys->hparent );
554 /* Make sure the mouse cursor is displayed */
555 //PostMessage( p_vout->p_sys->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 );
558 /* Change window style, borders and title bar */
559 ShowWindow( p_vout->p_sys->hwnd, SW_SHOW );
560 UpdateWindow( p_vout->p_sys->hwnd );
562 /* Update the object variable and trigger callback */
563 val.b_bool = p_vout->b_fullscreen;
564 var_Set( p_vout, "fullscreen", val );
566 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
567 p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
573 /*****************************************************************************
574 * Render: render previously calculated output
575 *****************************************************************************/
576 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
578 /* No need to do anything, the fake direct buffers stay as they are */
581 /*****************************************************************************
582 * Display: displays previously rendered output
583 *****************************************************************************/
584 #define rect_src p_vout->p_sys->rect_src
585 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
586 #define rect_dest p_vout->p_sys->rect_dest
587 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
589 #ifndef MODULE_NAME_IS_wingapi
590 static void DisplayGDI( vout_thread_t *p_vout, picture_t *p_pic )
592 vout_sys_t *p_sys = p_vout->p_sys;
593 RECT rect_dst = rect_dest_clipped;
594 HDC hdc = GetDC( p_sys->hvideownd );
596 OffsetRect( &rect_dst, -rect_dest.left, -rect_dest.top );
597 SelectObject( p_sys->off_dc, p_sys->off_bitmap );
599 if( rect_dest_clipped.right - rect_dest_clipped.left !=
600 rect_src_clipped.right - rect_src_clipped.left ||
601 rect_dest_clipped.bottom - rect_dest_clipped.top !=
602 rect_src_clipped.bottom - rect_src_clipped.top )
604 StretchBlt( hdc, rect_dst.left, rect_dst.top,
605 rect_dst.right, rect_dst.bottom,
606 p_sys->off_dc, rect_src_clipped.left, rect_src_clipped.top,
607 rect_src_clipped.right, rect_src_clipped.bottom, SRCCOPY );
611 BitBlt( hdc, rect_dst.left, rect_dst.top,
612 rect_dst.right, rect_dst.bottom,
613 p_sys->off_dc, rect_src_clipped.left,
614 rect_src_clipped.top, SRCCOPY );
617 ReleaseDC( p_sys->hwnd, hdc );
621 static int GAPILockSurface( vout_thread_t *p_vout, picture_t *p_pic )
623 vout_sys_t *p_sys = p_vout->p_sys;
624 int i_x, i_y, i_width, i_height;
628 /* Undo the display */
629 if( ( GetForegroundWindow() != GetParent(p_sys->hwnd) ) ||
630 ( p_sys->b_video_display == VLC_FALSE ) )
632 //return VLC_EGENERIC;
635 GetClientRect( p_sys->hwnd, &video_rect);
636 vout_PlacePicture( p_vout, video_rect.right - video_rect.left,
637 video_rect.bottom - video_rect.top,
638 &i_x, &i_y, &i_width, &i_height );
639 point.x = point.y = 0;
640 ClientToScreen( p_sys->hwnd, &point );
641 i_x += point.x + video_rect.left;
642 i_y += point.y + video_rect.top;
644 if( i_width != p_vout->output.i_width ||
645 i_height != p_vout->output.i_height )
647 GXDisplayProperties gxdisplayprop = GXGetDisplayProperties();
649 p_sys->render_width = i_width;
650 p_sys->render_height = i_height;
651 p_vout->i_changes |= VOUT_SIZE_CHANGE;
653 msg_Dbg( p_vout, "vout size change (%ix%i -> %ix%i)",
654 i_width, i_height, p_vout->output.i_width,
655 p_vout->output.i_height );
657 p_vout->p_sys->i_pic_pixel_pitch = gxdisplayprop.cbxPitch;
658 p_vout->p_sys->i_pic_pitch = gxdisplayprop.cbyPitch;
663 GXDisplayProperties gxdisplayprop;
664 RECT display_rect, dest_rect;
665 uint8_t *p_dest, *p_src = p_pic->p->p_pixels;
667 video_rect.left = i_x; video_rect.top = i_y;
668 video_rect.right = i_x + i_width;
669 video_rect.bottom = i_y + i_height;
671 gxdisplayprop = GXGetDisplayProperties();
672 display_rect.left = 0; display_rect.top = 0;
673 display_rect.right = gxdisplayprop.cxWidth;
674 display_rect.bottom = gxdisplayprop.cyHeight;
676 if( !IntersectRect( &dest_rect, &video_rect, &display_rect ) )
682 msg_Err( p_vout, "video (%d,%d,%d,%d) display (%d,%d,%d,%d) "
683 "dest (%d,%d,%d,%d)",
684 video_rect.left, video_rect.right,
685 video_rect.top, video_rect.bottom,
686 display_rect.left, display_rect.right,
687 display_rect.top, display_rect.bottom,
688 dest_rect.left, dest_rect.right,
689 dest_rect.top, dest_rect.bottom );
692 if( !(p_dest = GXBeginDraw()) )
695 msg_Err( p_vout, "GXBeginDraw error %d ", GetLastError() );
700 p_src += (dest_rect.left - video_rect.left) * gxdisplayprop.cbxPitch +
701 (dest_rect.top - video_rect.top) * p_pic->p->i_pitch;
702 p_dest += dest_rect.left * gxdisplayprop.cbxPitch +
703 dest_rect.top * gxdisplayprop.cbyPitch;
704 i_width = dest_rect.right - dest_rect.left;
705 i_height = dest_rect.bottom - dest_rect.top;
707 p_pic->p->p_pixels = p_dest;
713 static int GAPIUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
719 static void DisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic )
725 #undef rect_src_clipped
727 #undef rect_dest_clipped
728 /*****************************************************************************
729 * SetPalette: sets an 8 bpp palette
730 *****************************************************************************/
731 static void SetPalette( vout_thread_t *p_vout,
732 uint16_t *red, uint16_t *green, uint16_t *blue )
734 msg_Err( p_vout, "FIXME: SetPalette unimplemented" );
737 /*****************************************************************************
738 * EventThread: Event handling thread
739 *****************************************************************************/
740 static void EventThread ( vlc_object_t *p_event )
742 vout_thread_t *p_vout;
749 /* Initialisations */
750 var_Get( p_event, "p_vout", &val );
751 p_vout = (vout_thread_t *)val.p_address;
753 /* Register window class */
754 memset( &wc, 0, sizeof(wc) );
755 wc.style = CS_HREDRAW | CS_VREDRAW;
756 wc.lpfnWndProc = (WNDPROC)WndProc;
759 wc.hInstance = GetModuleHandle(NULL);
761 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
762 wc.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
764 wc.lpszClassName = _T("VLC WinGDI");
765 RegisterClass( &wc );
767 /* Register the video sub-window class */
768 wc.lpszClassName = _T("VLC WinGDI video"); wc.hIcon = 0;
771 /* Create output window */
772 p_vout->p_sys->hparent = (HWND)
773 vout_RequestWindow( p_vout, &p_vout->p_sys->i_window_x,
774 &p_vout->p_sys->i_window_y,
775 (unsigned int *)&p_vout->p_sys->i_window_width,
776 (unsigned int *)&p_vout->p_sys->i_window_height );
778 ShowWindow( p_vout->p_sys->hparent, SW_SHOW );
780 if( p_vout->p_sys->hparent )
781 i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD;
783 i_style = WS_OVERLAPPEDWINDOW|WS_SIZEBOX|WS_VISIBLE|WS_CLIPCHILDREN;
785 p_vout->p_sys->hwnd =
786 CreateWindow( _T("VLC WinGDI"), _T(VOUT_TITLE), i_style,
787 (p_vout->p_sys->i_window_x < 0) ? CW_USEDEFAULT :
788 p_vout->p_sys->i_window_x, /* default X coordinate */
789 (p_vout->p_sys->i_window_y < 0) ? CW_USEDEFAULT :
790 p_vout->p_sys->i_window_y, /* default Y coordinate */
791 p_vout->p_sys->i_window_width,
792 p_vout->p_sys->i_window_height + 10,
793 p_vout->p_sys->hparent, NULL,
794 GetModuleHandle(NULL), (LPVOID)p_vout );
796 if( !p_vout->p_sys->hwnd )
798 msg_Warn( p_vout, "couldn't create window" );
801 msg_Warn( p_vout, "Created WinGDI window" );
803 if( p_vout->p_sys->hparent )
807 /* We don't want the window owner to overwrite our client area */
808 i_style = GetWindowLong( p_vout->p_sys->hparent, GWL_STYLE );
810 if( !(i_style & WS_CLIPCHILDREN) )
811 /* Hmmm, apparently this is a blocking call... */
812 SetWindowLong( p_vout->p_sys->hparent, GWL_STYLE,
813 i_style | WS_CLIPCHILDREN );
815 /* Create our fullscreen window */
816 p_vout->p_sys->hfswnd =
817 CreateWindowEx( WS_EX_APPWINDOW, _T("VLC WinGDI"),
819 WS_NONAVDONEBUTTON|WS_CLIPCHILDREN,
820 CW_USEDEFAULT, CW_USEDEFAULT,
821 CW_USEDEFAULT, CW_USEDEFAULT,
822 NULL, NULL, GetModuleHandle(NULL), NULL );
825 /* Display our window */
826 ShowWindow( p_vout->p_sys->hwnd, SW_SHOW );
827 UpdateWindow( p_vout->p_sys->hwnd );
829 /* Create video sub-window */
830 p_vout->p_sys->hvideownd =
831 CreateWindow( _T("VLC WinGDI video"), _T(""), /* window class */
832 WS_CHILD | WS_VISIBLE, /* window style */
833 CW_USEDEFAULT, CW_USEDEFAULT, /* default coordinates */
834 CW_USEDEFAULT, CW_USEDEFAULT,
835 p_vout->p_sys->hwnd, /* parent window */
836 NULL, GetModuleHandle(NULL),
837 (LPVOID)p_vout ); /* send p_vout to WM_CREATE */
839 /* Initialize offscreen buffer */
840 InitBuffers( p_vout );
842 p_vout->pf_control = Control;
844 /* Tell the video output we're ready to receive data */
845 vlc_thread_ready( p_event );
847 while( !p_event->b_die && GetMessage( &msg, 0, 0, 0 ) )
849 /* Check if we are asked to exit */
850 if( p_event->b_die ) break;
852 switch( msg.message )
858 p_event->p_vlc->b_die = VLC_TRUE;
861 TranslateMessage( &msg );
869 p_event->p_vlc->b_die = VLC_TRUE;
875 TranslateMessage( &msg );
876 DispatchMessage( &msg );
881 msg_Dbg( p_vout, "CloseWindow" );
883 #ifdef MODULE_NAME_IS_wingapi
886 DeleteDC( p_vout->p_sys->off_dc );
887 DeleteObject( p_vout->p_sys->off_bitmap );
890 DestroyWindow( p_vout->p_sys->hwnd );
891 if( p_vout->p_sys->hfswnd ) DestroyWindow( p_vout->p_sys->hfswnd );
893 if( p_vout->p_sys->hparent )
894 vout_ReleaseWindow( p_vout, (void *)p_vout->p_sys->hparent );
897 /*****************************************************************************
898 * UpdateRects: update clipping rectangles
899 *****************************************************************************
900 * This function is called when the window position or size are changed, and
901 * its job is to update the source and destination RECTs used to display the
903 *****************************************************************************/
904 static void UpdateRects( vout_thread_t *p_vout, vlc_bool_t b_force )
906 #define rect_src p_vout->p_sys->rect_src
907 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
908 #define rect_dest p_vout->p_sys->rect_dest
909 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
911 int i_width, i_height, i_x, i_y;
916 /* Retrieve the window size */
917 GetClientRect( p_vout->p_sys->hwnd, &rect );
919 /* Retrieve the window position */
920 point.x = point.y = 0;
921 ClientToScreen( p_vout->p_sys->hwnd, &point );
923 /* If nothing changed, we can return */
925 && p_vout->p_sys->i_window_width == rect.right
926 && p_vout->p_sys->i_window_height == rect.bottom
927 && p_vout->p_sys->i_window_x == point.x
928 && p_vout->p_sys->i_window_y == point.y )
933 /* Update the window position and size */
934 p_vout->p_sys->i_window_x = point.x;
935 p_vout->p_sys->i_window_y = point.y;
936 p_vout->p_sys->i_window_width = rect.right;
937 p_vout->p_sys->i_window_height = rect.bottom;
939 vout_PlacePicture( p_vout, rect.right, rect.bottom,
940 &i_x, &i_y, &i_width, &i_height );
942 if( p_vout->p_sys->hvideownd )
943 SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
944 i_x, i_y, i_width, i_height, 0 );
946 /* Destination image position and dimensions */
947 rect_dest.left = point.x + i_x;
948 rect_dest.right = rect_dest.left + i_width;
949 rect_dest.top = point.y + i_y;
950 rect_dest.bottom = rect_dest.top + i_height;
952 /* Clip the destination window */
953 if( !IntersectRect( &rect_dest_clipped, &rect_dest,
954 &p_vout->p_sys->rect_display ) )
956 SetRectEmpty( &rect_src_clipped );
961 msg_Dbg( p_vout, "image_dst_clipped coords: %i,%i,%i,%i",
962 rect_dest_clipped.left, rect_dest_clipped.top,
963 rect_dest_clipped.right, rect_dest_clipped.bottom );
966 /* the 2 following lines are to fix a bug when clicking on the desktop */
967 if( (rect_dest_clipped.right - rect_dest_clipped.left)==0 ||
968 (rect_dest_clipped.bottom - rect_dest_clipped.top)==0 )
970 SetRectEmpty( &rect_src_clipped );
974 /* src image dimensions */
977 rect_src.right = p_vout->output.i_width;
978 rect_src.bottom = p_vout->output.i_height;
980 /* Clip the source image */
981 rect_src_clipped.left = (rect_dest_clipped.left - rect_dest.left) *
982 p_vout->output.i_width / (rect_dest.right - rect_dest.left);
983 rect_src_clipped.right = p_vout->output.i_width -
984 (rect_dest.right - rect_dest_clipped.right) * p_vout->output.i_width /
985 (rect_dest.right - rect_dest.left);
986 rect_src_clipped.top = (rect_dest_clipped.top - rect_dest.top) *
987 p_vout->output.i_height / (rect_dest.bottom - rect_dest.top);
988 rect_src_clipped.bottom = p_vout->output.i_height -
989 (rect_dest.bottom - rect_dest_clipped.bottom) * p_vout->output.i_height /
990 (rect_dest.bottom - rect_dest.top);
993 msg_Dbg( p_vout, "image_src_clipped coords: %i,%i,%i,%i",
994 rect_src_clipped.left, rect_src_clipped.top,
995 rect_src_clipped.right, rect_src_clipped.bottom );
998 /* The destination coordinates need to be relative to the current
999 * directdraw primary surface (display) */
1000 rect_dest_clipped.left -= p_vout->p_sys->rect_display.left;
1001 rect_dest_clipped.right -= p_vout->p_sys->rect_display.left;
1002 rect_dest_clipped.top -= p_vout->p_sys->rect_display.top;
1003 rect_dest_clipped.bottom -= p_vout->p_sys->rect_display.top;
1005 /* Signal the change in size/position */
1006 p_vout->p_sys->i_changes |= DX_POSITION_CHANGE;
1009 #undef rect_src_clipped
1011 #undef rect_dest_clipped
1014 /*****************************************************************************
1015 * Message handler for the main window
1016 *****************************************************************************/
1017 static long FAR PASCAL WndProc( HWND hWnd, UINT message,
1018 WPARAM wParam, LPARAM lParam )
1020 vout_thread_t *p_vout;
1022 if( message == WM_CREATE )
1024 /* Store p_vout for future use */
1025 p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
1026 SetWindowLongPtr( hWnd, GWLP_USERDATA, (LONG_PTR)p_vout );
1027 if( p_vout ) msg_Dbg( p_vout, "create: %p", hWnd );
1031 p_vout = (vout_thread_t *)GetWindowLongPtr( hWnd, GWLP_USERDATA );
1035 /* Catch the screensaver and the monitor turn-off */
1036 if( message == WM_SYSCOMMAND &&
1037 ( wParam == SC_SCREENSAVE || wParam == SC_MONITORPOWER ) )
1039 //if( p_vout ) msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND screensaver" );
1040 return 0; /* this stops them from happening */
1046 /* Hmmm mozilla does manage somehow to save the pointer to our
1047 * windowproc and still calls it after the vout has been closed. */
1048 return DefWindowProc(hWnd, message, wParam, lParam);
1051 if( hWnd != p_vout->p_sys->hwnd &&
1052 hWnd != p_vout->p_sys->hvideownd )
1053 return DefWindowProc(hWnd, message, wParam, lParam);
1057 case WM_WINDOWPOSCHANGED:
1058 if( hWnd == p_vout->p_sys->hwnd )
1059 UpdateRects( p_vout, VLC_TRUE );
1063 msg_Err( p_vout, "WM_ACTIVATE: %i", wParam );
1064 #ifdef MODULE_NAME_IS_wingapi
1065 if( wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE )
1067 else if( wParam == WA_INACTIVE )
1072 #ifdef MODULE_NAME_IS_wingapi
1074 msg_Err( p_vout, "WM_KILLFOCUS" );
1079 msg_Err( p_vout, "WM_SETFOCUS" );
1084 case WM_LBUTTONDOWN:
1085 p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE;
1092 case WM_INITMENUPOPUP:
1093 p_vout->p_sys->b_video_display = VLC_FALSE;
1097 // Redo the video display because menu can be closed
1098 // FIXME verify if p_child_window exits
1099 if( (((NMHDR *)lParam)->code) == NM_CUSTOMDRAW )
1100 p_vout->p_sys->b_video_display = VLC_TRUE;
1104 msg_Dbg( p_vout, "WinProc WM_DESTROY" );
1105 PostQuitMessage( 0 );
1109 return DefWindowProc( hWnd, message, wParam, lParam );
1115 /*****************************************************************************
1116 * InitBuffers: initialize an offscreen bitmap for direct buffer operations.
1117 *****************************************************************************/
1118 static void InitBuffers( vout_thread_t *p_vout )
1120 BITMAPINFOHEADER *p_header = &p_vout->p_sys->bitmapinfo.bmiHeader;
1121 BITMAPINFO *p_info = &p_vout->p_sys->bitmapinfo;
1122 int i_pixels = p_vout->render.i_height * p_vout->render.i_width;
1123 HDC window_dc = GetDC( p_vout->p_sys->hvideownd );
1125 /* Get screen properties */
1126 #ifdef MODULE_NAME_IS_wingapi
1127 GXDisplayProperties gx_displayprop = GXGetDisplayProperties();
1128 p_vout->p_sys->i_depth = gx_displayprop.cBPP;
1130 p_vout->p_sys->i_depth = GetDeviceCaps( window_dc, PLANES ) *
1131 GetDeviceCaps( window_dc, BITSPIXEL );
1133 msg_Dbg( p_vout, "GDI depth is %i", p_vout->p_sys->i_depth );
1135 #ifdef MODULE_NAME_IS_wingapi
1136 if( p_vout->b_fullscreen )
1138 /* We need to restore Maximized sized window */
1139 GetWindowRect( p_vout->p_sys->hwnd,
1140 &p_vout->p_sys->window_placement );
1142 /* Maximized window */
1143 if( p_vout->p_sys->hparent )
1145 SetWindowPos( GetParent( p_vout->p_sys->hwnd ), HWND_TOP,
1146 0, 0, GetSystemMetrics(SM_CXSCREEN),
1147 GetSystemMetrics(SM_CYSCREEN), SWP_SHOWWINDOW );
1148 SetWindowPos( p_vout->p_sys->hwnd, HWND_TOP,
1149 0, 0, GetSystemMetrics(SM_CXSCREEN),
1150 GetSystemMetrics(SM_CYSCREEN), SWP_SHOWWINDOW );
1153 /* Hide SIP button, taskbar and menubar */
1154 SHFullScreen( GetParent(p_vout->p_sys->hwnd),
1155 SHFS_HIDESIPBUTTON );
1156 p_vout->p_sys->hTaskBar = FindWindow(_T("HHTaskbar"),NULL);
1157 ShowWindow( p_vout->p_sys->hTaskBar, SW_HIDE);
1158 p_vout->p_sys->hMenu =
1159 SHFindMenuBar( GetParent( p_vout->p_sys->hwnd ) );
1160 ShowWindow( p_vout->p_sys->hMenu,SW_HIDE );
1165 SetWindowPos( p_vout->p_sys->hwnd, HWND_TOP,
1166 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, SWP_SHOWWINDOW );
1171 GXOpenDisplay( p_vout->p_sys->hvideownd, GX_FULLSCREEN );
1174 /* Initialize offscreen bitmap */
1175 memset( p_info, 0, sizeof( BITMAPINFO ) + 3 * sizeof( RGBQUAD ) );
1177 p_header->biSize = sizeof( BITMAPINFOHEADER );
1178 p_header->biSizeImage = 0;
1179 p_header->biPlanes = 1;
1180 switch( p_vout->p_sys->i_depth )
1183 p_header->biBitCount = 8;
1184 p_header->biCompression = BI_RGB;
1185 /* FIXME: we need a palette here */
1188 p_header->biBitCount = 15;
1189 p_header->biCompression = BI_BITFIELDS;//BI_RGB;
1190 ((DWORD*)p_info->bmiColors)[0] = 0x00007c00;
1191 ((DWORD*)p_info->bmiColors)[1] = 0x000003e0;
1192 ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
1195 p_header->biBitCount = 16;
1196 p_header->biCompression = BI_BITFIELDS;//BI_RGB;
1197 ((DWORD*)p_info->bmiColors)[0] = 0x0000f800;
1198 ((DWORD*)p_info->bmiColors)[1] = 0x000007e0;
1199 ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
1202 p_header->biBitCount = 24;
1203 p_header->biCompression = BI_RGB;
1204 ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
1205 ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
1206 ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
1209 p_header->biBitCount = 32;
1210 p_header->biCompression = BI_RGB;
1211 ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
1212 ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
1213 ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
1216 msg_Err( p_vout, "screen depth %i not supported",
1217 p_vout->p_sys->i_depth );
1221 p_header->biWidth = p_vout->render.i_width;
1222 p_header->biHeight = -p_vout->render.i_height;
1223 p_header->biClrImportant = 0;
1224 p_header->biClrUsed = 0;
1225 p_header->biXPelsPerMeter = 0;
1226 p_header->biYPelsPerMeter = 0;
1228 p_vout->p_sys->i_pic_pixel_pitch = p_header->biBitCount / 8;
1229 p_vout->p_sys->i_pic_pitch = p_header->biBitCount * p_header->biWidth / 8;
1231 p_vout->p_sys->off_bitmap =
1232 CreateDIBSection( window_dc, (BITMAPINFO *)p_header, DIB_RGB_COLORS,
1233 (void**)&p_vout->p_sys->p_pic_buffer, NULL, 0 );
1235 p_vout->p_sys->off_dc = CreateCompatibleDC( window_dc );
1237 SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap );
1238 ReleaseDC( 0, window_dc );
1242 /*****************************************************************************
1243 * Control: control facility for the vout
1244 *****************************************************************************/
1245 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
1251 case VOUT_SET_FOCUS:
1252 b_bool = va_arg( args, vlc_bool_t );
1253 #ifdef MODULE_NAME_IS_wingapi
1254 if( b_bool ) GXResume();
1260 return vout_vaControlDefault( p_vout, i_query, args );