1 /*****************************************************************************
2 * wingdi.c : Win32 / WinCE GDI video output plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2002 VideoLAN
7 * Authors: Samuel Hocevar <sam@zoy.org>
8 * Gildas Bazin <gbazin@videolan.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() */
35 #define WIN32_LEAN_AND_MEAN
38 # include <aygshell.h>
39 # define MENU_HEIGHT 26
41 # define SHFS_HIDESIPBUTTON 0
42 # define MENU_HEIGHT 0
43 # define NM_CUSTOMDRAW 0
46 #undef MODULE_NAME_IS_gapi
47 #ifdef MODULE_NAME_IS_gapi
48 typedef struct GXDisplayProperties {
55 } GXDisplayProperties;
57 typedef struct GXScreenRect {
64 # define GX_FULLSCREEN 0x01
65 # define GX_NORMALKEYS 0x02
66 # define GX_LANDSCAPEKEYS 0x03
69 # define kfLandscape 0x8
70 # define kfPalette 0x10
71 # define kfDirect 0x20
72 # define kfDirect555 0x40
73 # define kfDirect565 0x80
74 # define kfDirect888 0x100
75 # define kfDirect444 0x200
76 # define kfDirectInverted 0x400
78 #endif /* MODULE_NAME_IS_gapi */
80 #define MAX_DIRECTBUFFERS 10
83 #ifndef WS_OVERLAPPEDWINDOW
84 # define WS_OVERLAPPEDWINDOW 0xcf0000
86 #ifndef WS_EX_NOPARENTNOTIFY
87 # define WS_EX_NOPARENTNOTIFY 4
89 #ifndef WS_EX_APPWINDOW
90 #define WS_EX_APPWINDOW 0x40000
92 #define SetWindowLongPtr SetWindowLong
93 #define GetWindowLongPtr GetWindowLong
94 #define GWLP_USERDATA GWL_USERDATA
97 #ifndef WS_NONAVDONEBUTTON
98 #define WS_NONAVDONEBUTTON 0
100 /*****************************************************************************
102 *****************************************************************************/
103 static int OpenVideo ( vlc_object_t * );
104 static void CloseVideo ( vlc_object_t * );
106 static int Init ( vout_thread_t * );
107 static void End ( vout_thread_t * );
108 static int Manage ( vout_thread_t * );
109 static void Render ( vout_thread_t *, picture_t * );
110 #ifdef MODULE_NAME_IS_gapi
111 static void DisplayGAPI( vout_thread_t *, picture_t * );
113 static void DisplayGDI( vout_thread_t *, picture_t * );
115 static void SetPalette( vout_thread_t *, uint16_t *, uint16_t *, uint16_t * );
117 static void EventThread ( vlc_object_t * );
118 static long FAR PASCAL WndProc ( HWND, UINT, WPARAM, LPARAM );
119 static void InitBuffers ( vout_thread_t * );
120 static void UpdateRects ( vout_thread_t *, vlc_bool_t );
122 /*****************************************************************************
124 *****************************************************************************/
127 /* The event thread */
128 vlc_object_t * p_event;
130 /* Our video output window */
135 HWND hparent; /* Handle of the parent window */
136 WNDPROC pf_wndproc; /* Window handling callback */
137 volatile uint16_t i_changes; /* changes made to the video display */
138 RECT window_placement;
140 /* Window position and size */
148 /* Coordinates of src and dest images (used when blitting to display) */
150 RECT rect_src_clipped;
152 RECT rect_dest_clipped;
156 /* Our offscreen bitmap and its framebuffer */
161 BITMAPINFO bitmapinfo;
169 vlc_bool_t b_video_display;
171 #ifdef MODULE_NAME_IS_gapi
172 HINSTANCE gapi_dll; /* handle of the opened gapi dll */
175 int (*GXOpenDisplay)( HWND hWnd, DWORD dwFlags );
176 int (*GXCloseDisplay)();
177 void *(*GXBeginDraw)();
179 GXDisplayProperties (*GXGetDisplayProperties)();
185 #define GXOpenDisplay p_vout->p_sys->GXOpenDisplay
186 #define GXCloseDisplay p_vout->p_sys->GXCloseDisplay
187 #define GXBeginDraw p_vout->p_sys->GXBeginDraw
188 #define GXEndDraw p_vout->p_sys->GXEndDraw
189 #define GXGetDisplayProperties p_vout->p_sys->GXGetDisplayProperties
190 #define GXSuspend p_vout->p_sys->GXSuspend
191 #define GXResume p_vout->p_sys->GXResume
193 #define DX_POSITION_CHANGE 0x1000
195 /*****************************************************************************
197 *****************************************************************************/
199 #ifdef MODULE_NAME_IS_gapi
200 set_description( _("Windows GAPI video output") );
201 set_capability( "video output", 20 );
203 set_description( _("Windows GDI video output") );
204 set_capability( "video output", 10 );
206 set_callbacks( OpenVideo, CloseVideo );
209 /*****************************************************************************
210 * OpenVideo: activate GDI video thread output method
211 *****************************************************************************/
212 static int OpenVideo ( vlc_object_t *p_this )
214 vout_thread_t * p_vout = (vout_thread_t *)p_this;
217 p_vout->p_sys = (vout_sys_t *)malloc( sizeof(vout_sys_t) );
218 if( !p_vout->p_sys ) return VLC_ENOMEM;
220 #ifdef MODULE_NAME_IS_gapi
222 p_vout->p_sys->gapi_dll = LoadLibrary( _T("GX.DLL") );
223 if( p_vout->p_sys->gapi_dll == NULL )
225 msg_Warn( p_vout, "failed loading gx.dll" );
226 free( p_vout->p_sys );
230 GXOpenDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
231 _T("?GXOpenDisplay@@YAHPAUHWND__@@K@Z") );
232 GXCloseDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
233 _T("?GXCloseDisplay@@YAHXZ") );
234 GXBeginDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
235 _T("?GXBeginDraw@@YAPAXXZ") );
236 GXEndDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
237 _T("?GXEndDraw@@YAHXZ") );
238 GXGetDisplayProperties = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
239 _T("?GXGetDisplayProperties@@YA?AUGXDisplayProperties@@XZ") );
240 GXSuspend = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
241 _T("?GXSuspend@@YAHXZ") );
242 GXResume = GetProcAddress( p_vout->p_sys->gapi_dll,
243 _T("?GXResume@@YAHXZ") );
245 if( !GXOpenDisplay || !GXCloseDisplay || !GXBeginDraw || !GXEndDraw ||
246 !GXGetDisplayProperties || !GXSuspend || !GXResume )
248 msg_Err( p_vout, "failed GetProcAddress on gapi.dll" );
249 free( p_vout->p_sys );
253 msg_Dbg( p_vout, "GAPI DLL loaded" );
255 p_vout->p_sys->render_width = p_vout->render.i_width;
256 p_vout->p_sys->render_height = p_vout->render.i_height;
259 p_vout->p_sys->p_event = (vlc_object_t *)
260 vlc_object_create( p_vout, VLC_OBJECT_GENERIC );
261 if( !p_vout->p_sys->p_event )
263 free( p_vout->p_sys );
267 var_Create( p_vout->p_sys->p_event, "p_vout", VLC_VAR_ADDRESS );
268 val.p_address = (void *)p_vout;
269 var_Set( p_vout->p_sys->p_event, "p_vout", val );
271 SetRectEmpty( &p_vout->p_sys->rect_display );
272 SetRectEmpty( &p_vout->p_sys->rect_parent );
274 if( vlc_thread_create( p_vout->p_sys->p_event, "GDI Event Thread",
275 EventThread, 0, 1 ) )
277 msg_Err( p_vout, "cannot spawn EventThread" );
281 p_vout->pf_init = Init;
282 p_vout->pf_end = End;
283 p_vout->pf_manage = Manage;
284 p_vout->pf_render = Render;
285 #ifdef MODULE_NAME_IS_gapi
286 p_vout->pf_display = DisplayGAPI;
288 p_vout->pf_display = DisplayGDI;
290 p_vout->p_sys->i_changes = 0;
295 /*****************************************************************************
296 * CloseVideo: deactivate the GDI video output
297 *****************************************************************************/
298 static void CloseVideo ( vlc_object_t *p_this )
300 vout_thread_t * p_vout = (vout_thread_t *)p_this;
302 #ifdef MODULE_NAME_IS_gapi
304 FreeLibrary( p_vout->p_sys->gapi_dll );
307 p_vout->p_sys->p_event->b_die = VLC_TRUE;
308 PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0 );
309 vlc_thread_join( p_vout->p_sys->p_event );
311 var_Destroy( p_vout->p_sys->p_event, "p_vout" );
312 vlc_object_destroy( p_vout->p_sys->p_event );
313 free( p_vout->p_sys );
316 /*****************************************************************************
317 * Init: initialize video thread output method
318 *****************************************************************************/
319 static int Init( vout_thread_t *p_vout )
324 p_vout->p_sys->rect_display.left = 0;
325 p_vout->p_sys->rect_display.top = 0;
326 p_vout->p_sys->rect_display.right = GetSystemMetrics(SM_CXSCREEN);
327 p_vout->p_sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
329 p_vout->p_sys->b_video_display = VLC_TRUE;
330 p_vout->p_sys->p_event->b_die = VLC_FALSE;
332 I_OUTPUTPICTURES = 0;
334 /* Initialize the output structure */
335 switch( p_vout->p_sys->i_depth )
338 p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2');
339 p_vout->output.pf_setpalette = SetPalette;
342 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5');
343 p_vout->output.i_rmask = 0x7c00;
344 p_vout->output.i_gmask = 0x03e0;
345 p_vout->output.i_bmask = 0x001f;
348 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
349 p_vout->output.i_rmask = 0xf800;
350 p_vout->output.i_gmask = 0x07e0;
351 p_vout->output.i_bmask = 0x001f;
354 p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
355 p_vout->output.i_rmask = 0x00ff0000;
356 p_vout->output.i_gmask = 0x0000ff00;
357 p_vout->output.i_bmask = 0x000000ff;
360 p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
361 p_vout->output.i_rmask = 0x00ff0000;
362 p_vout->output.i_gmask = 0x0000ff00;
363 p_vout->output.i_bmask = 0x000000ff;
366 msg_Err( p_vout, "screen depth %i not supported",
367 p_vout->p_sys->i_depth );
372 #ifdef MODULE_NAME_IS_gapi
373 p_vout->output.i_width = p_vout->p_sys->render_width;
374 p_vout->output.i_height = p_vout->p_sys->render_height;
376 p_vout->output.i_width = p_vout->render.i_width;
377 p_vout->output.i_height = p_vout->render.i_height;
379 p_vout->output.i_aspect = p_vout->render.i_aspect;
381 /* Try to initialize MAX_DIRECTBUFFERS direct buffers */
382 while( I_OUTPUTPICTURES < MAX_DIRECTBUFFERS )
386 /* Find an empty picture slot */
387 for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
389 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
391 p_pic = p_vout->p_picture + i_index;
396 /* Allocate the picture */
397 if( p_pic == NULL ) break;
399 vout_AllocatePicture( VLC_OBJECT(p_vout), p_pic,
400 p_vout->output.i_chroma,
401 p_vout->output.i_width, p_vout->output.i_height,
402 p_vout->output.i_aspect );
404 if( p_pic->i_planes == 0 ) break;
406 p_pic->i_status = DESTROYED_PICTURE;
407 p_pic->i_type = DIRECT_PICTURE;
409 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
417 /*****************************************************************************
418 * End: terminate video thread output method
419 *****************************************************************************/
420 static void End( vout_thread_t *p_vout )
424 /* Free the fake output buffers we allocated */
425 for( i_index = I_OUTPUTPICTURES ; i_index ; )
428 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
432 /*****************************************************************************
433 * Manage: handle events
434 *****************************************************************************
435 * This function should be called regularly by video output thread. It manages
436 * console events. It returns a non null value on error.
437 *****************************************************************************/
438 static int Manage( vout_thread_t *p_vout )
441 WINDOWPLACEMENT window_placement;
444 /* If we do not control our window, we check for geometry changes
445 * ourselves because the parent might not send us its events. */
446 if( p_vout->p_sys->hparent && !p_vout->b_fullscreen )
451 GetClientRect( p_vout->p_sys->hparent, &rect_parent );
452 point.x = point.y = 0;
453 ClientToScreen( p_vout->p_sys->hparent, &point );
454 OffsetRect( &rect_parent, point.x, point.y );
456 if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) )
458 int i_x, i_y, i_width, i_height;
459 p_vout->p_sys->rect_parent = rect_parent;
461 /* This one is to force the update even if only
462 * the position has changed */
463 SetWindowPos( p_vout->p_sys->hwnd, 0, 1, 1,
464 rect_parent.right - rect_parent.left,
465 rect_parent.bottom - rect_parent.top, 0 );
467 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
468 rect_parent.right - rect_parent.left,
469 rect_parent.bottom - rect_parent.top, 0 );
471 vout_PlacePicture( p_vout, rect_parent.right - rect_parent.left,
472 rect_parent.bottom - rect_parent.top,
473 &i_x, &i_y, &i_width, &i_height );
475 SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
476 i_x, i_y, i_width, i_height, 0 );
480 /* We used to call the Win32 PeekMessage function here to read the window
481 * messages. But since window can stay blocked into this function for a
482 * long time (for example when you move your window on the screen), I
483 * decided to isolate PeekMessage in another thread. */
488 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
489 || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
494 HWND hwnd = (p_vout->p_sys->hparent && p_vout->p_sys->hfswnd) ?
495 p_vout->p_sys->hfswnd : p_vout->p_sys->hwnd;
497 p_vout->b_fullscreen = ! p_vout->b_fullscreen;
499 /* We need to switch between Maximized and Normal sized window */
501 window_placement.length = sizeof(WINDOWPLACEMENT);
502 GetWindowPlacement( hwnd, &window_placement );
504 if( p_vout->b_fullscreen )
507 /* Change window style, no borders and no title bar */
508 int i_style = WS_CLIPCHILDREN | WS_VISIBLE;
509 SetWindowLong( hwnd, GWL_STYLE, i_style );
511 if( p_vout->p_sys->hparent )
513 /* Retrieve current window position so fullscreen will happen
514 * on the right screen */
517 ClientToScreen( p_vout->p_sys->hwnd, &point );
518 GetClientRect( p_vout->p_sys->hwnd, &rect );
519 SetWindowPos( hwnd, 0, point.x, point.y,
520 rect.right, rect.bottom,
521 SWP_NOZORDER|SWP_FRAMECHANGED );
522 GetWindowPlacement( hwnd, &window_placement );
525 /* Maximize window */
526 window_placement.showCmd = SW_SHOWMAXIMIZED;
527 SetWindowPlacement( hwnd, &window_placement );
528 SetWindowPos( hwnd, 0, 0, 0, 0, 0,
529 SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
532 if( p_vout->p_sys->hparent )
535 GetClientRect( hwnd, &rect );
536 SetParent( p_vout->p_sys->hwnd, hwnd );
537 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
538 rect.right, rect.bottom,
539 SWP_NOZORDER|SWP_FRAMECHANGED );
542 ShowWindow( hwnd, SW_SHOW );
543 SetForegroundWindow( hwnd );
547 /* Change window style, no borders and no title bar */
548 //SetWindowLong( hwnd, GWL_STYLE, p_vout->p_sys->i_window_style );
552 window_placement.showCmd = SW_SHOWNORMAL;
553 SetWindowPlacement( hwnd, &window_placement );
554 SetWindowPos( hwnd, 0, 0, 0, 0, 0,
555 SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
558 if( p_vout->p_sys->hparent )
561 GetClientRect( p_vout->p_sys->hparent, &rect );
562 SetParent( p_vout->p_sys->hwnd, p_vout->p_sys->hparent );
563 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
564 rect.right, rect.bottom,
565 SWP_NOZORDER|SWP_FRAMECHANGED );
567 ShowWindow( hwnd, SW_HIDE );
568 SetForegroundWindow( p_vout->p_sys->hparent );
571 /* Make sure the mouse cursor is displayed */
572 //PostMessage( p_vout->p_sys->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 );
575 #ifdef MODULE_NAME_IS_gapi
579 /* Change window style, borders and title bar */
580 ShowWindow( p_vout->p_sys->hwnd, SW_SHOW );
581 UpdateWindow( p_vout->p_sys->hwnd );
583 #ifdef MODULE_NAME_IS_gapi
584 GXOpenDisplay( p_vout->p_sys->hvideownd, GX_FULLSCREEN );
587 /* Update the object variable and trigger callback */
588 val.b_bool = p_vout->b_fullscreen;
589 var_Set( p_vout, "fullscreen", val );
591 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
592 p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
598 /*****************************************************************************
599 * Render: render previously calculated output
600 *****************************************************************************/
601 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
603 /* No need to do anything, the fake direct buffers stay as they are */
606 /*****************************************************************************
607 * Display: displays previously rendered output
608 *****************************************************************************/
609 #define rect_src p_vout->p_sys->rect_src
610 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
611 #define rect_dest p_vout->p_sys->rect_dest
612 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
614 #ifndef MODULE_NAME_IS_gapi
615 static void DisplayGDI( vout_thread_t *p_vout, picture_t *p_pic )
617 vout_sys_t *p_sys = p_vout->p_sys;
618 int i_src_bytes, i_dest_bytes;
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 );
626 /* Stupid GDI is upside-down */
627 i_src_bytes = p_pic->p->i_lines * p_pic->p->i_pitch;
632 i_src_bytes -= p_pic->p->i_pitch;
634 p_vout->p_vlc->pf_memcpy( p_sys->p_buffer + i_dest_bytes,
635 p_pic->p->p_pixels + i_src_bytes,
636 p_pic->p->i_visible_pitch );
638 i_dest_bytes += p_pic->p->i_pitch;
642 if( rect_dest_clipped.right - rect_dest_clipped.left !=
643 rect_src_clipped.right - rect_src_clipped.left ||
644 rect_dest_clipped.bottom - rect_dest_clipped.top !=
645 rect_src_clipped.bottom - rect_src_clipped.top )
647 StretchBlt( hdc, rect_dst.left, rect_dst.top,
648 rect_dst.right, rect_dst.bottom,
649 p_sys->off_dc, rect_src_clipped.left, rect_src_clipped.top,
650 rect_src_clipped.right, rect_src_clipped.bottom, SRCCOPY );
654 BitBlt( hdc, rect_dst.left, rect_dst.top,
655 rect_dst.right, rect_dst.bottom,
656 p_sys->off_dc, rect_src_clipped.left,
657 rect_src_clipped.top, SRCCOPY );
660 ReleaseDC( p_sys->hwnd, hdc );
664 static void DisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic )
666 vout_sys_t *p_sys = p_vout->p_sys;
667 int i_x, i_y, i_width, i_height;
671 /* Undo the display */
672 if( ( GetForegroundWindow() != GetParent(p_sys->hwnd) ) ||
673 ( p_sys->b_video_display == VLC_FALSE ) )
678 GetClientRect( p_sys->hwnd, &video_rect);
679 vout_PlacePicture( p_vout, video_rect.right - video_rect.left,
680 video_rect.bottom - video_rect.top,
681 &i_x, &i_y, &i_width, &i_height );
682 point.x = point.y = 0;
683 ClientToScreen( p_sys->hwnd, &point );
684 i_x += point.x + video_rect.left;
685 i_y += point.y + video_rect.top;
687 if( i_width != p_vout->output.i_width ||
688 i_height != p_vout->output.i_height )
690 p_sys->render_width = i_width;
691 p_sys->render_height = i_height;
692 p_vout->i_changes |= VOUT_SIZE_CHANGE;
696 GXDisplayProperties gxdisplayprop;
697 RECT display_rect, dest_rect;
698 uint8_t *p_dest, *p_src = p_pic->p->p_pixels;
700 video_rect.left = i_x; video_rect.top = i_y;
701 video_rect.right = i_x + i_width;
702 video_rect.bottom = i_y + i_height;
704 gxdisplayprop = GXGetDisplayProperties();
705 display_rect.left = 0; display_rect.top = 0;
706 display_rect.right = gxdisplayprop.cxWidth;
707 display_rect.bottom = gxdisplayprop.cyHeight;
709 if( !IntersectRect( &dest_rect, &video_rect, &display_rect ) )
715 msg_Err( p_vout, "video (%d,%d,%d,%d) display (%d,%d,%d,%d) "
716 "dest (%d,%d,%d,%d)",
717 video_rect.left, video_rect.right,
718 video_rect.top, video_rect.bottom,
719 display_rect.left, display_rect.right,
720 display_rect.top, display_rect.bottom,
721 dest_rect.left, dest_rect.right,
722 dest_rect.top, dest_rect.bottom );
725 if( !(p_dest = GXBeginDraw()) )
727 msg_Err( p_vout, "GXBeginDraw error %d ", GetLastError() );
731 p_src += (dest_rect.left - video_rect.left) * gxdisplayprop.cbxPitch +
732 (dest_rect.top - video_rect.top) * p_pic->p->i_pitch;
733 p_dest += dest_rect.left * gxdisplayprop.cbxPitch +
734 dest_rect.top * gxdisplayprop.cbyPitch;
735 i_width = dest_rect.right - dest_rect.left;
736 i_height = dest_rect.bottom - dest_rect.top;
740 p_vout->p_vlc->pf_memcpy( p_dest, p_src,
741 i_width * gxdisplayprop.cbxPitch );
742 p_src += p_pic->p->i_pitch;
743 p_dest += gxdisplayprop.cbyPitch;
752 #undef rect_src_clipped
754 #undef rect_dest_clipped
755 /*****************************************************************************
756 * SetPalette: sets an 8 bpp palette
757 *****************************************************************************/
758 static void SetPalette( vout_thread_t *p_vout,
759 uint16_t *red, uint16_t *green, uint16_t *blue )
761 msg_Err( p_vout, "FIXME: SetPalette unimplemented" );
764 /*****************************************************************************
765 * EventThread: Event handling thread
766 *****************************************************************************/
767 static void EventThread ( vlc_object_t *p_event )
769 vout_thread_t *p_vout;
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 = p_vout->p_sys->hwnd = (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 ShowWindow( p_vout->p_sys->hparent, SW_SHOW );
806 if( p_vout->p_sys->hparent )
807 i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD;
809 i_style = WS_OVERLAPPEDWINDOW|WS_SIZEBOX|WS_VISIBLE|WS_CLIPCHILDREN;
811 p_vout->p_sys->hwnd =
812 CreateWindow( _T("VLC WinGDI"), _T(VOUT_TITLE), i_style,
813 (p_vout->p_sys->i_window_x < 0) ? CW_USEDEFAULT :
814 p_vout->p_sys->i_window_x, /* default X coordinate */
815 (p_vout->p_sys->i_window_y < 0) ? CW_USEDEFAULT :
816 p_vout->p_sys->i_window_y, /* default Y coordinate */
817 p_vout->p_sys->i_window_width,
818 p_vout->p_sys->i_window_height + 10,
819 p_vout->p_sys->hparent, NULL,
820 GetModuleHandle(NULL), (LPVOID)p_vout );
822 if( !p_vout->p_sys->hwnd )
824 msg_Warn( p_vout, "couldn't create window" );
827 msg_Warn( p_vout, "Created WinGDI window" );
829 if( p_vout->p_sys->hparent )
833 /* We don't want the window owner to overwrite our client area */
834 i_style = GetWindowLong( p_vout->p_sys->hparent, GWL_STYLE );
836 if( !(i_style & WS_CLIPCHILDREN) )
837 /* Hmmm, apparently this is a blocking call... */
838 SetWindowLong( p_vout->p_sys->hparent, GWL_STYLE,
839 i_style | WS_CLIPCHILDREN );
841 /* Create our fullscreen window */
842 p_vout->p_sys->hfswnd =
843 CreateWindowEx( WS_EX_APPWINDOW, _T("VLC WinGDI"),
845 WS_NONAVDONEBUTTON|WS_CLIPCHILDREN,
846 CW_USEDEFAULT, CW_USEDEFAULT,
847 CW_USEDEFAULT, CW_USEDEFAULT,
848 NULL, NULL, GetModuleHandle(NULL), NULL );
851 /* Display our window */
852 ShowWindow( p_vout->p_sys->hwnd, SW_SHOW );
853 UpdateWindow( p_vout->p_sys->hwnd );
855 /* Create video sub-window */
856 p_vout->p_sys->hvideownd =
857 CreateWindow( _T("VLC WinGDI video"), _T(""), /* window class */
858 WS_CHILD | WS_VISIBLE, /* window style */
859 CW_USEDEFAULT, CW_USEDEFAULT, /* default coordinates */
860 CW_USEDEFAULT, CW_USEDEFAULT,
861 p_vout->p_sys->hwnd, /* parent window */
862 NULL, GetModuleHandle(NULL),
863 (LPVOID)p_vout ); /* send p_vout to WM_CREATE */
865 ShowWindow( p_vout->p_sys->hvideownd, SW_SHOW );
867 /* Initialize offscreen buffer */
868 InitBuffers( p_vout );
870 /* Tell the video output we're ready to receive data */
871 vlc_thread_ready( p_event );
873 while( !p_event->b_die && GetMessage( &msg, 0, 0, 0 ) )
875 /* Check if we are asked to exit */
876 if( p_event->b_die ) break;
878 switch( msg.message )
884 p_event->p_vlc->b_die = VLC_TRUE;
887 TranslateMessage( &msg );
895 p_event->p_vlc->b_die = VLC_TRUE;
900 #ifdef MODULE_NAME_IS_gapi
911 TranslateMessage( &msg );
912 DispatchMessage( &msg );
917 msg_Dbg( p_vout, "CloseWindow" );
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 );
925 DeleteDC( p_vout->p_sys->off_dc );
926 DeleteObject( p_vout->p_sys->off_bitmap );
929 /*****************************************************************************
930 * UpdateRects: update clipping rectangles
931 *****************************************************************************
932 * This function is called when the window position or size are changed, and
933 * its job is to update the source and destination RECTs used to display the
935 *****************************************************************************/
936 static void UpdateRects( vout_thread_t *p_vout, vlc_bool_t b_force )
938 #define rect_src p_vout->p_sys->rect_src
939 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
940 #define rect_dest p_vout->p_sys->rect_dest
941 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
943 int i_width, i_height, i_x, i_y;
948 /* Retrieve the window size */
949 GetClientRect( p_vout->p_sys->hwnd, &rect );
951 /* Retrieve the window position */
952 point.x = point.y = 0;
953 ClientToScreen( p_vout->p_sys->hwnd, &point );
955 /* If nothing changed, we can return */
957 && p_vout->p_sys->i_window_width == rect.right
958 && p_vout->p_sys->i_window_height == rect.bottom
959 && p_vout->p_sys->i_window_x == point.x
960 && p_vout->p_sys->i_window_y == point.y )
965 /* Update the window position and size */
966 p_vout->p_sys->i_window_x = point.x;
967 p_vout->p_sys->i_window_y = point.y;
968 p_vout->p_sys->i_window_width = rect.right;
969 p_vout->p_sys->i_window_height = rect.bottom;
971 vout_PlacePicture( p_vout, rect.right, rect.bottom,
972 &i_x, &i_y, &i_width, &i_height );
974 if( p_vout->p_sys->hvideownd )
975 SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
976 i_x, i_y, i_width, i_height, 0 );
978 /* Destination image position and dimensions */
979 rect_dest.left = point.x + i_x;
980 rect_dest.right = rect_dest.left + i_width;
981 rect_dest.top = point.y + i_y;
982 rect_dest.bottom = rect_dest.top + i_height;
984 /* Clip the destination window */
985 if( !IntersectRect( &rect_dest_clipped, &rect_dest,
986 &p_vout->p_sys->rect_display ) )
988 SetRectEmpty( &rect_src_clipped );
993 msg_Dbg( p_vout, "image_dst_clipped coords: %i,%i,%i,%i",
994 rect_dest_clipped.left, rect_dest_clipped.top,
995 rect_dest_clipped.right, rect_dest_clipped.bottom );
998 /* the 2 following lines are to fix a bug when clicking on the desktop */
999 if( (rect_dest_clipped.right - rect_dest_clipped.left)==0 ||
1000 (rect_dest_clipped.bottom - rect_dest_clipped.top)==0 )
1002 SetRectEmpty( &rect_src_clipped );
1006 /* src image dimensions */
1009 rect_src.right = p_vout->output.i_width;
1010 rect_src.bottom = p_vout->output.i_height;
1012 /* Clip the source image */
1013 rect_src_clipped.left = (rect_dest_clipped.left - rect_dest.left) *
1014 p_vout->output.i_width / (rect_dest.right - rect_dest.left);
1015 rect_src_clipped.right = p_vout->output.i_width -
1016 (rect_dest.right - rect_dest_clipped.right) * p_vout->output.i_width /
1017 (rect_dest.right - rect_dest.left);
1018 rect_src_clipped.top = (rect_dest_clipped.top - rect_dest.top) *
1019 p_vout->output.i_height / (rect_dest.bottom - rect_dest.top);
1020 rect_src_clipped.bottom = p_vout->output.i_height -
1021 (rect_dest.bottom - rect_dest_clipped.bottom) * p_vout->output.i_height /
1022 (rect_dest.bottom - rect_dest.top);
1025 msg_Dbg( p_vout, "image_src_clipped coords: %i,%i,%i,%i",
1026 rect_src_clipped.left, rect_src_clipped.top,
1027 rect_src_clipped.right, rect_src_clipped.bottom );
1030 /* The destination coordinates need to be relative to the current
1031 * directdraw primary surface (display) */
1032 rect_dest_clipped.left -= p_vout->p_sys->rect_display.left;
1033 rect_dest_clipped.right -= p_vout->p_sys->rect_display.left;
1034 rect_dest_clipped.top -= p_vout->p_sys->rect_display.top;
1035 rect_dest_clipped.bottom -= p_vout->p_sys->rect_display.top;
1037 /* Signal the change in size/position */
1038 p_vout->p_sys->i_changes |= DX_POSITION_CHANGE;
1041 #undef rect_src_clipped
1043 #undef rect_dest_clipped
1046 /*****************************************************************************
1047 * Message handler for the main window
1048 *****************************************************************************/
1049 static long FAR PASCAL WndProc ( HWND hWnd, UINT message,
1050 WPARAM wParam, LPARAM lParam )
1052 vout_thread_t *p_vout;
1054 if( message == WM_CREATE )
1056 /* Store p_vout for future use */
1057 p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
1058 SetWindowLongPtr( hWnd, GWLP_USERDATA, (LONG_PTR)p_vout );
1059 if( p_vout ) msg_Dbg( p_vout, "create: %p", hWnd );
1063 p_vout = (vout_thread_t *)GetWindowLongPtr( hWnd, GWLP_USERDATA );
1067 /* Catch the screensaver and the monitor turn-off */
1068 if( message == WM_SYSCOMMAND &&
1069 ( wParam == SC_SCREENSAVE || wParam == SC_MONITORPOWER ) )
1071 //if( p_vout ) msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND screensaver" );
1072 return 0; /* this stops them from happening */
1078 /* Hmmm mozilla does manage somehow to save the pointer to our
1079 * windowproc and still calls it after the vout has been closed. */
1080 return DefWindowProc(hWnd, message, wParam, lParam);
1083 if( hWnd != p_vout->p_sys->hwnd )
1084 return DefWindowProc(hWnd, message, wParam, lParam);
1088 case WM_WINDOWPOSCHANGED:
1089 UpdateRects( p_vout, VLC_TRUE );
1091 case WM_LBUTTONDOWN:
1092 p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE;
1102 case WM_INITMENUPOPUP:
1103 p_vout->p_sys->b_video_display = VLC_FALSE;
1107 // Redo the video display because menu can be closed
1108 // FIXME verify if p_child_window exits
1109 if( (((NMHDR *)lParam)->code) == NM_CUSTOMDRAW )
1110 p_vout->p_sys->b_video_display = VLC_TRUE;
1114 msg_Dbg( p_vout, "WinProc WM_DESTROY" );
1115 PostQuitMessage( 0 );
1119 return DefWindowProc( hWnd, message, wParam, lParam );
1125 /*****************************************************************************
1126 * InitBuffers: initialize an offscreen bitmap for direct buffer operations.
1127 *****************************************************************************/
1128 static void InitBuffers( vout_thread_t *p_vout )
1130 BITMAPINFOHEADER *p_header = &p_vout->p_sys->bitmapinfo.bmiHeader;
1131 BITMAPINFO *p_info = &p_vout->p_sys->bitmapinfo;
1132 int i_pixels = p_vout->render.i_height * p_vout->render.i_width;
1135 window_dc = GetDC( p_vout->p_sys->hvideownd );
1137 /* Get screen properties */
1138 #ifdef MODULE_NAME_IS_gapi
1140 GXDisplayProperties gx_displayprop = GXGetDisplayProperties();
1141 p_vout->p_sys->i_depth = gx_displayprop.cBPP;
1144 p_vout->p_sys->i_depth = GetDeviceCaps( window_dc, PLANES ) *
1145 GetDeviceCaps( window_dc, BITSPIXEL );
1147 msg_Dbg( p_vout, "GDI depth is %i", p_vout->p_sys->i_depth );
1149 #ifdef MODULE_NAME_IS_gapi
1150 if( p_vout->b_fullscreen )
1152 /* We need to restore Maximized sized window */
1153 GetWindowRect( p_vout->p_sys->hwnd,
1154 &p_vout->p_sys->window_placement );
1156 /* Maximized window */
1157 if( p_vout->p_sys->hparent )
1159 SetWindowPos( GetParent( p_vout->p_sys->hwnd ), HWND_TOP,
1160 0, 0, GetSystemMetrics(SM_CXSCREEN),
1161 GetSystemMetrics(SM_CYSCREEN), SWP_SHOWWINDOW );
1162 SetWindowPos( p_vout->p_sys->hwnd, HWND_TOP,
1163 0, 0, GetSystemMetrics(SM_CXSCREEN),
1164 GetSystemMetrics(SM_CYSCREEN), SWP_SHOWWINDOW );
1167 /* Hide SIP button, taskbar and menubar */
1168 SHFullScreen( GetParent(p_vout->p_sys->hwnd),
1169 SHFS_HIDESIPBUTTON );
1170 p_vout->p_sys->hTaskBar = FindWindow(_T("HHTaskbar"),NULL);
1171 ShowWindow( p_vout->p_sys->hTaskBar, SW_HIDE);
1172 p_vout->p_sys->hMenu =
1173 SHFindMenuBar( GetParent( p_vout->p_sys->hwnd ) );
1174 ShowWindow( p_vout->p_sys->hMenu,SW_HIDE );
1179 SetWindowPos( p_vout->p_sys->hwnd, HWND_TOP,
1180 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, SWP_SHOWWINDOW );
1186 GXOpenDisplay( p_vout->p_sys->hvideownd, GX_FULLSCREEN );
1189 /* Initialize offscreen bitmap */
1190 memset( p_info, 0, sizeof( BITMAPINFO ) + 3 * sizeof( RGBQUAD ) );
1192 p_header->biSize = sizeof( BITMAPINFOHEADER );
1193 p_header->biSizeImage = 0;
1194 p_header->biPlanes = 1;
1195 switch( p_vout->p_sys->i_depth )
1198 p_header->biBitCount = 8;
1199 p_header->biCompression = BI_RGB;
1200 /* FIXME: we need a palette here */
1203 p_header->biBitCount = 15;
1204 p_header->biCompression = BI_BITFIELDS;//BI_RGB;
1205 ((DWORD*)p_info->bmiColors)[0] = 0x00007c00;
1206 ((DWORD*)p_info->bmiColors)[1] = 0x000003e0;
1207 ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
1210 p_header->biBitCount = 16;
1211 p_header->biCompression = BI_BITFIELDS;//BI_RGB;
1212 ((DWORD*)p_info->bmiColors)[0] = 0x0000f800;
1213 ((DWORD*)p_info->bmiColors)[1] = 0x000007e0;
1214 ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
1217 p_header->biBitCount = 24;
1218 p_header->biCompression = BI_RGB;
1219 ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
1220 ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
1221 ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
1224 p_header->biBitCount = 32;
1225 p_header->biCompression = BI_RGB;
1226 ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
1227 ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
1228 ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
1231 msg_Err( p_vout, "screen depth %i not supported",
1232 p_vout->p_sys->i_depth );
1236 p_header->biWidth = p_vout->render.i_width;
1237 p_header->biHeight = p_vout->render.i_height;
1238 p_header->biClrImportant = 0;
1239 p_header->biClrUsed = 0;
1240 p_header->biXPelsPerMeter = 0;
1241 p_header->biYPelsPerMeter = 0;
1243 p_vout->p_sys->off_bitmap =
1244 CreateDIBSection( window_dc, (BITMAPINFO *)p_header, DIB_RGB_COLORS,
1245 (void**)&p_vout->p_sys->p_buffer, NULL, 0 );
1247 p_vout->p_sys->off_dc = CreateCompatibleDC( window_dc );
1249 SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap );
1250 ReleaseDC( 0, window_dc );