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() */
36 #define SHFS_SHOWSIPBUTTON 0x0004
37 #define SHFS_HIDESIPBUTTON 0x0008
40 # define MENU_HEIGHT 26
41 BOOL SHFullScreen(HWND hwndRequester, DWORD dwState);
43 # define MENU_HEIGHT 0
44 # define SHFullScreen(a,b)
47 #ifdef MODULE_NAME_IS_wingapi
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_wingapi */
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_wingapi
111 static void DisplayGAPI( vout_thread_t *, picture_t * );
112 static int GAPILockSurface( vout_thread_t *, picture_t * );
113 static int GAPIUnlockSurface( vout_thread_t *, picture_t * );
115 static void DisplayGDI( vout_thread_t *, picture_t * );
117 static void SetPalette( vout_thread_t *, uint16_t *, uint16_t *, uint16_t * );
119 static void EventThread ( vlc_object_t * );
120 static long FAR PASCAL WndProc ( HWND, UINT, WPARAM, LPARAM );
121 static void InitBuffers ( vout_thread_t * );
122 static void UpdateRects ( vout_thread_t *, vlc_bool_t );
124 static int Control( vout_thread_t *p_vout, int i_query, va_list args );
126 /*****************************************************************************
128 *****************************************************************************/
131 /* The event thread */
132 vlc_object_t * p_event;
134 /* Our video output window */
139 HWND hparent; /* Handle of the parent window */
140 WNDPROC pf_wndproc; /* Window handling callback */
141 volatile uint16_t i_changes; /* changes made to the video display */
142 RECT window_placement;
144 /* Window position and size */
152 /* Coordinates of src and dest images (used when blitting to display) */
154 RECT rect_src_clipped;
156 RECT rect_dest_clipped;
160 /* Our offscreen bitmap and its framebuffer */
163 uint8_t * p_pic_buffer;
165 int i_pic_pixel_pitch;
167 BITMAPINFO bitmapinfo;
173 vlc_bool_t b_video_display;
175 /* Window focus states */
177 vlc_bool_t b_parent_focus;
179 #ifdef MODULE_NAME_IS_wingapi
180 HINSTANCE gapi_dll; /* handle of the opened gapi dll */
183 int (*GXOpenDisplay)( HWND hWnd, DWORD dwFlags );
184 int (*GXCloseDisplay)();
185 void *(*GXBeginDraw)();
187 GXDisplayProperties (*GXGetDisplayProperties)();
193 #define GXOpenDisplay p_vout->p_sys->GXOpenDisplay
194 #define GXCloseDisplay p_vout->p_sys->GXCloseDisplay
195 #define GXBeginDraw p_vout->p_sys->GXBeginDraw
196 #define GXEndDraw p_vout->p_sys->GXEndDraw
197 #define GXGetDisplayProperties p_vout->p_sys->GXGetDisplayProperties
199 #ifdef MODULE_NAME_IS_wingapi
200 # define GXSuspend p_vout->p_sys->GXSuspend
201 # define GXResume p_vout->p_sys->GXResume
207 #define DX_POSITION_CHANGE 0x1000
209 /*****************************************************************************
211 *****************************************************************************/
213 set_category( CAT_VIDEO );
214 set_subcategory( SUBCAT_VIDEO_VOUT );
215 #ifdef MODULE_NAME_IS_wingapi
216 set_shortname( _("Windows GAPI") );
217 set_description( _("Windows GAPI video output") );
218 set_capability( "video output", 20 );
220 set_shortname( _("Windows GDI") );
221 set_description( _("Windows GDI video output") );
222 set_capability( "video output", 10 );
224 set_callbacks( OpenVideo, CloseVideo );
227 /*****************************************************************************
228 * OpenVideo: activate GDI video thread output method
229 *****************************************************************************/
230 static int OpenVideo ( vlc_object_t *p_this )
232 vout_thread_t * p_vout = (vout_thread_t *)p_this;
235 p_vout->p_sys = (vout_sys_t *)malloc( sizeof(vout_sys_t) );
236 if( !p_vout->p_sys ) return VLC_ENOMEM;
238 #ifdef MODULE_NAME_IS_wingapi
240 p_vout->p_sys->gapi_dll = LoadLibrary( _T("GX.DLL") );
241 if( p_vout->p_sys->gapi_dll == NULL )
243 msg_Warn( p_vout, "failed loading gx.dll" );
244 free( p_vout->p_sys );
248 GXOpenDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
249 _T("?GXOpenDisplay@@YAHPAUHWND__@@K@Z") );
250 GXCloseDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
251 _T("?GXCloseDisplay@@YAHXZ") );
252 GXBeginDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
253 _T("?GXBeginDraw@@YAPAXXZ") );
254 GXEndDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
255 _T("?GXEndDraw@@YAHXZ") );
256 GXGetDisplayProperties = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
257 _T("?GXGetDisplayProperties@@YA?AUGXDisplayProperties@@XZ") );
258 GXSuspend = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
259 _T("?GXSuspend@@YAHXZ") );
260 GXResume = GetProcAddress( p_vout->p_sys->gapi_dll,
261 _T("?GXResume@@YAHXZ") );
263 if( !GXOpenDisplay || !GXCloseDisplay || !GXBeginDraw || !GXEndDraw ||
264 !GXGetDisplayProperties || !GXSuspend || !GXResume )
266 msg_Err( p_vout, "failed GetProcAddress on gapi.dll" );
267 free( p_vout->p_sys );
271 msg_Dbg( p_vout, "GAPI DLL loaded" );
273 p_vout->p_sys->render_width = p_vout->render.i_width;
274 p_vout->p_sys->render_height = p_vout->render.i_height;
277 p_vout->p_sys->p_event = (vlc_object_t *)
278 vlc_object_create( p_vout, VLC_OBJECT_GENERIC );
279 if( !p_vout->p_sys->p_event )
281 free( p_vout->p_sys );
285 var_Create( p_vout->p_sys->p_event, "p_vout", VLC_VAR_ADDRESS );
286 val.p_address = (void *)p_vout;
287 var_Set( p_vout->p_sys->p_event, "p_vout", val );
289 SetRectEmpty( &p_vout->p_sys->rect_display );
290 SetRectEmpty( &p_vout->p_sys->rect_parent );
292 if( vlc_thread_create( p_vout->p_sys->p_event, "GDI Event Thread",
293 EventThread, 0, 1 ) )
295 msg_Err( p_vout, "cannot spawn EventThread" );
299 p_vout->pf_init = Init;
300 p_vout->pf_end = End;
301 p_vout->pf_manage = Manage;
302 p_vout->pf_render = Render;
303 #ifdef MODULE_NAME_IS_wingapi
304 p_vout->pf_display = DisplayGAPI;
306 p_vout->pf_display = DisplayGDI;
308 p_vout->p_sys->i_changes = 0;
310 p_vout->p_sys->b_focus = 0;
311 p_vout->p_sys->b_parent_focus = 0;
316 /*****************************************************************************
317 * CloseVideo: deactivate the GDI video output
318 *****************************************************************************/
319 static void CloseVideo ( vlc_object_t *p_this )
321 vout_thread_t * p_vout = (vout_thread_t *)p_this;
323 p_vout->p_sys->p_event->b_die = VLC_TRUE;
324 PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0 );
325 vlc_thread_join( p_vout->p_sys->p_event );
327 #ifdef MODULE_NAME_IS_wingapi
328 FreeLibrary( p_vout->p_sys->gapi_dll );
331 var_Destroy( p_vout->p_sys->p_event, "p_vout" );
332 vlc_object_destroy( p_vout->p_sys->p_event );
333 free( p_vout->p_sys );
336 /*****************************************************************************
337 * Init: initialize video thread output method
338 *****************************************************************************/
339 static int Init( vout_thread_t *p_vout )
343 p_vout->p_sys->rect_display.left = 0;
344 p_vout->p_sys->rect_display.top = 0;
345 p_vout->p_sys->rect_display.right = GetSystemMetrics(SM_CXSCREEN);
346 p_vout->p_sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
348 p_vout->p_sys->b_video_display = VLC_TRUE;
349 p_vout->p_sys->p_event->b_die = VLC_FALSE;
351 I_OUTPUTPICTURES = 0;
353 /* Initialize the output structure */
354 switch( p_vout->p_sys->i_depth )
357 p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2');
358 p_vout->output.pf_setpalette = SetPalette;
361 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5');
362 p_vout->output.i_rmask = 0x7c00;
363 p_vout->output.i_gmask = 0x03e0;
364 p_vout->output.i_bmask = 0x001f;
367 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
368 p_vout->output.i_rmask = 0xf800;
369 p_vout->output.i_gmask = 0x07e0;
370 p_vout->output.i_bmask = 0x001f;
373 p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
374 p_vout->output.i_rmask = 0x00ff0000;
375 p_vout->output.i_gmask = 0x0000ff00;
376 p_vout->output.i_bmask = 0x000000ff;
379 p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
380 p_vout->output.i_rmask = 0x00ff0000;
381 p_vout->output.i_gmask = 0x0000ff00;
382 p_vout->output.i_bmask = 0x000000ff;
385 msg_Err( p_vout, "screen depth %i not supported",
386 p_vout->p_sys->i_depth );
391 p_pic = &p_vout->p_picture[0];
393 #ifdef MODULE_NAME_IS_wingapi
394 p_vout->output.i_width = 0;
395 p_vout->output.i_height = 0;
396 p_pic->pf_lock = GAPILockSurface;
397 p_pic->pf_unlock = GAPIUnlockSurface;
399 GAPILockSurface( p_vout, p_pic );
400 p_vout->i_changes = 0;
401 p_vout->output.i_width = p_vout->p_sys->render_width;
402 p_vout->output.i_height = p_vout->p_sys->render_height;
405 p_vout->output.i_width = p_vout->render.i_width;
406 p_vout->output.i_height = p_vout->render.i_height;
408 p_vout->output.i_aspect = p_vout->render.i_aspect;
410 p_pic->p->p_pixels = p_vout->p_sys->p_pic_buffer;
411 p_pic->p->i_lines = p_vout->output.i_height;
412 p_pic->p->i_visible_lines = p_vout->output.i_height;
413 p_pic->p->i_pitch = p_vout->p_sys->i_pic_pitch;
414 p_pic->p->i_pixel_pitch = p_vout->p_sys->i_pic_pixel_pitch;
415 p_pic->p->i_visible_pitch = p_vout->output.i_width *
416 p_pic->p->i_pixel_pitch;
418 p_pic->i_status = DESTROYED_PICTURE;
419 p_pic->i_type = DIRECT_PICTURE;
421 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES++ ] = p_pic;
426 /*****************************************************************************
427 * End: terminate video thread output method
428 *****************************************************************************/
429 static void End( vout_thread_t *p_vout )
433 /*****************************************************************************
434 * Manage: handle events
435 *****************************************************************************
436 * This function should be called regularly by video output thread. It manages
437 * console events. It returns a non null value on error.
438 *****************************************************************************/
439 static int Manage( vout_thread_t *p_vout )
442 WINDOWPLACEMENT window_placement;
445 /* If we do not control our window, we check for geometry changes
446 * ourselves because the parent might not send us its events. */
447 if( p_vout->p_sys->hparent && !p_vout->b_fullscreen )
452 GetClientRect( p_vout->p_sys->hparent, &rect_parent );
453 point.x = point.y = 0;
454 ClientToScreen( p_vout->p_sys->hparent, &point );
455 OffsetRect( &rect_parent, point.x, point.y );
457 if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) )
459 int i_x, i_y, i_width, i_height;
460 p_vout->p_sys->rect_parent = rect_parent;
462 /* This one is to force the update even if only
463 * the position has changed */
464 SetWindowPos( p_vout->p_sys->hwnd, 0, 1, 1,
465 rect_parent.right - rect_parent.left,
466 rect_parent.bottom - rect_parent.top, 0 );
468 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
469 rect_parent.right - rect_parent.left,
470 rect_parent.bottom - rect_parent.top, 0 );
472 vout_PlacePicture( p_vout, rect_parent.right - rect_parent.left,
473 rect_parent.bottom - rect_parent.top,
474 &i_x, &i_y, &i_width, &i_height );
476 SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
477 i_x, i_y, i_width, i_height, 0 );
481 /* We used to call the Win32 PeekMessage function here to read the window
482 * messages. But since window can stay blocked into this function for a
483 * long time (for example when you move your window on the screen), I
484 * decided to isolate PeekMessage in another thread. */
489 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
490 || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
495 HWND hwnd = (p_vout->p_sys->hparent && p_vout->p_sys->hfswnd) ?
496 p_vout->p_sys->hfswnd : p_vout->p_sys->hwnd;
498 p_vout->b_fullscreen = ! p_vout->b_fullscreen;
500 /* We need to switch between Maximized and Normal sized window */
502 window_placement.length = sizeof(WINDOWPLACEMENT);
503 GetWindowPlacement( hwnd, &window_placement );
505 if( p_vout->b_fullscreen )
508 /* Change window style, no borders and no title bar */
509 int i_style = WS_CLIPCHILDREN | WS_VISIBLE;
510 SetWindowLong( hwnd, GWL_STYLE, i_style );
512 if( p_vout->p_sys->hparent )
514 /* Retrieve current window position so fullscreen will happen
515 * on the right screen */
518 ClientToScreen( p_vout->p_sys->hwnd, &point );
519 GetClientRect( p_vout->p_sys->hwnd, &rect );
520 SetWindowPos( hwnd, 0, point.x, point.y,
521 rect.right, rect.bottom,
522 SWP_NOZORDER|SWP_FRAMECHANGED );
523 GetWindowPlacement( hwnd, &window_placement );
526 /* Maximize window */
527 window_placement.showCmd = SW_SHOWMAXIMIZED;
528 SetWindowPlacement( hwnd, &window_placement );
529 SetWindowPos( hwnd, 0, 0, 0, 0, 0,
530 SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
533 if( p_vout->p_sys->hparent )
536 GetClientRect( hwnd, &rect );
537 SetParent( p_vout->p_sys->hwnd, hwnd );
538 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
539 rect.right, rect.bottom,
540 SWP_NOZORDER|SWP_FRAMECHANGED );
543 ShowWindow( hwnd, SW_SHOW );
544 SetForegroundWindow( hwnd );
548 /* Change window style, no borders and no title bar */
549 //SetWindowLong( hwnd, GWL_STYLE, p_vout->p_sys->i_window_style );
553 window_placement.showCmd = SW_SHOWNORMAL;
554 SetWindowPlacement( hwnd, &window_placement );
555 SetWindowPos( hwnd, 0, 0, 0, 0, 0,
556 SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
559 if( p_vout->p_sys->hparent )
562 GetClientRect( p_vout->p_sys->hparent, &rect );
563 SetParent( p_vout->p_sys->hwnd, p_vout->p_sys->hparent );
564 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
565 rect.right, rect.bottom,
566 SWP_NOZORDER|SWP_FRAMECHANGED );
568 ShowWindow( hwnd, SW_HIDE );
569 SetForegroundWindow( p_vout->p_sys->hparent );
572 /* Make sure the mouse cursor is displayed */
573 //PostMessage( p_vout->p_sys->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 );
576 /* Change window style, borders and title bar */
577 ShowWindow( p_vout->p_sys->hwnd, SW_SHOW );
578 UpdateWindow( p_vout->p_sys->hwnd );
580 /* Update the object variable and trigger callback */
581 val.b_bool = p_vout->b_fullscreen;
582 var_Set( p_vout, "fullscreen", val );
584 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
585 p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
591 /*****************************************************************************
592 * Render: render previously calculated output
593 *****************************************************************************/
594 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
596 /* No need to do anything, the fake direct buffers stay as they are */
599 /*****************************************************************************
600 * Display: displays previously rendered output
601 *****************************************************************************/
602 #define rect_src p_vout->p_sys->rect_src
603 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
604 #define rect_dest p_vout->p_sys->rect_dest
605 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
607 #ifndef MODULE_NAME_IS_wingapi
608 static void DisplayGDI( vout_thread_t *p_vout, picture_t *p_pic )
610 vout_sys_t *p_sys = p_vout->p_sys;
611 RECT rect_dst = rect_dest_clipped;
612 HDC hdc = GetDC( p_sys->hvideownd );
614 OffsetRect( &rect_dst, -rect_dest.left, -rect_dest.top );
615 SelectObject( p_sys->off_dc, p_sys->off_bitmap );
617 if( rect_dest_clipped.right - rect_dest_clipped.left !=
618 rect_src_clipped.right - rect_src_clipped.left ||
619 rect_dest_clipped.bottom - rect_dest_clipped.top !=
620 rect_src_clipped.bottom - rect_src_clipped.top )
622 StretchBlt( hdc, rect_dst.left, rect_dst.top,
623 rect_dst.right, rect_dst.bottom,
624 p_sys->off_dc, rect_src_clipped.left, rect_src_clipped.top,
625 rect_src_clipped.right, rect_src_clipped.bottom, SRCCOPY );
629 BitBlt( hdc, rect_dst.left, rect_dst.top,
630 rect_dst.right, rect_dst.bottom,
631 p_sys->off_dc, rect_src_clipped.left,
632 rect_src_clipped.top, SRCCOPY );
635 ReleaseDC( p_sys->hwnd, hdc );
639 static int GAPILockSurface( vout_thread_t *p_vout, picture_t *p_pic )
641 vout_sys_t *p_sys = p_vout->p_sys;
642 int i_x, i_y, i_width, i_height;
646 /* Undo the display */
647 if( ( GetForegroundWindow() != GetParent(p_sys->hwnd) ) ||
648 ( p_sys->b_video_display == VLC_FALSE ) )
650 //return VLC_EGENERIC;
653 GetClientRect( p_sys->hwnd, &video_rect);
654 vout_PlacePicture( p_vout, video_rect.right - video_rect.left,
655 video_rect.bottom - video_rect.top,
656 &i_x, &i_y, &i_width, &i_height );
657 point.x = point.y = 0;
658 ClientToScreen( p_sys->hwnd, &point );
659 i_x += point.x + video_rect.left;
660 i_y += point.y + video_rect.top;
662 if( i_width != p_vout->output.i_width ||
663 i_height != p_vout->output.i_height )
665 GXDisplayProperties gxdisplayprop = GXGetDisplayProperties();
667 p_sys->render_width = i_width;
668 p_sys->render_height = i_height;
669 p_vout->i_changes |= VOUT_SIZE_CHANGE;
671 msg_Dbg( p_vout, "vout size change (%ix%i -> %ix%i)",
672 i_width, i_height, p_vout->output.i_width,
673 p_vout->output.i_height );
675 p_vout->p_sys->i_pic_pixel_pitch = gxdisplayprop.cbxPitch;
676 p_vout->p_sys->i_pic_pitch = gxdisplayprop.cbyPitch;
681 GXDisplayProperties gxdisplayprop;
682 RECT display_rect, dest_rect;
683 uint8_t *p_dest, *p_src = p_pic->p->p_pixels;
685 video_rect.left = i_x; video_rect.top = i_y;
686 video_rect.right = i_x + i_width;
687 video_rect.bottom = i_y + i_height;
689 gxdisplayprop = GXGetDisplayProperties();
690 display_rect.left = 0; display_rect.top = 0;
691 display_rect.right = gxdisplayprop.cxWidth;
692 display_rect.bottom = gxdisplayprop.cyHeight;
694 if( !IntersectRect( &dest_rect, &video_rect, &display_rect ) )
700 msg_Err( p_vout, "video (%d,%d,%d,%d) display (%d,%d,%d,%d) "
701 "dest (%d,%d,%d,%d)",
702 video_rect.left, video_rect.right,
703 video_rect.top, video_rect.bottom,
704 display_rect.left, display_rect.right,
705 display_rect.top, display_rect.bottom,
706 dest_rect.left, dest_rect.right,
707 dest_rect.top, dest_rect.bottom );
710 if( !(p_dest = GXBeginDraw()) )
713 msg_Err( p_vout, "GXBeginDraw error %d ", GetLastError() );
718 p_src += (dest_rect.left - video_rect.left) * gxdisplayprop.cbxPitch +
719 (dest_rect.top - video_rect.top) * p_pic->p->i_pitch;
720 p_dest += dest_rect.left * gxdisplayprop.cbxPitch +
721 dest_rect.top * gxdisplayprop.cbyPitch;
722 i_width = dest_rect.right - dest_rect.left;
723 i_height = dest_rect.bottom - dest_rect.top;
725 p_pic->p->p_pixels = p_dest;
731 static int GAPIUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
737 static void DisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic )
743 #undef rect_src_clipped
745 #undef rect_dest_clipped
746 /*****************************************************************************
747 * SetPalette: sets an 8 bpp palette
748 *****************************************************************************/
749 static void SetPalette( vout_thread_t *p_vout,
750 uint16_t *red, uint16_t *green, uint16_t *blue )
752 msg_Err( p_vout, "FIXME: SetPalette unimplemented" );
755 /*****************************************************************************
756 * EventThread: Event handling thread
757 *****************************************************************************/
758 static void EventThread ( vlc_object_t *p_event )
760 vout_thread_t *p_vout;
767 /* Initialisations */
768 var_Get( p_event, "p_vout", &val );
769 p_vout = (vout_thread_t *)val.p_address;
771 /* Register window class */
772 memset( &wc, 0, sizeof(wc) );
773 wc.style = CS_HREDRAW | CS_VREDRAW;
774 wc.lpfnWndProc = (WNDPROC)WndProc;
777 wc.hInstance = GetModuleHandle(NULL);
779 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
780 wc.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
782 wc.lpszClassName = _T("VLC WinGDI");
783 RegisterClass( &wc );
785 /* Register the video sub-window class */
786 wc.lpszClassName = _T("VLC WinGDI video"); wc.hIcon = 0;
789 /* Create output window */
790 p_vout->p_sys->hparent = (HWND)
791 vout_RequestWindow( p_vout, &p_vout->p_sys->i_window_x,
792 &p_vout->p_sys->i_window_y,
793 (unsigned int *)&p_vout->p_sys->i_window_width,
794 (unsigned int *)&p_vout->p_sys->i_window_height );
796 if( p_vout->p_sys->hparent )
797 ShowWindow( p_vout->p_sys->hparent, SW_SHOW );
799 if( p_vout->p_sys->hparent )
800 i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD;
802 i_style = WS_OVERLAPPEDWINDOW|WS_SIZEBOX|WS_VISIBLE|WS_CLIPCHILDREN;
804 p_vout->p_sys->hwnd =
805 CreateWindow( _T("VLC WinGDI"), _T(VOUT_TITLE), i_style,
806 (p_vout->p_sys->i_window_x < 0) ? CW_USEDEFAULT :
807 p_vout->p_sys->i_window_x, /* default X coordinate */
808 (p_vout->p_sys->i_window_y < 0) ? CW_USEDEFAULT :
809 p_vout->p_sys->i_window_y, /* default Y coordinate */
810 p_vout->p_sys->i_window_width,
811 p_vout->p_sys->i_window_height + 10,
812 p_vout->p_sys->hparent, NULL,
813 GetModuleHandle(NULL), (LPVOID)p_vout );
815 if( !p_vout->p_sys->hwnd )
817 msg_Warn( p_vout, "couldn't create window" );
820 msg_Warn( p_vout, "Created WinGDI window" );
822 if( p_vout->p_sys->hparent )
826 /* We don't want the window owner to overwrite our client area */
827 i_style = GetWindowLong( p_vout->p_sys->hparent, GWL_STYLE );
829 if( !(i_style & WS_CLIPCHILDREN) )
830 /* Hmmm, apparently this is a blocking call... */
831 SetWindowLong( p_vout->p_sys->hparent, GWL_STYLE,
832 i_style | WS_CLIPCHILDREN );
834 /* Create our fullscreen window */
835 p_vout->p_sys->hfswnd =
836 CreateWindowEx( WS_EX_APPWINDOW, _T("VLC WinGDI"),
838 WS_NONAVDONEBUTTON|WS_CLIPCHILDREN,
839 CW_USEDEFAULT, CW_USEDEFAULT,
840 CW_USEDEFAULT, CW_USEDEFAULT,
841 NULL, NULL, GetModuleHandle(NULL), (LPVOID)p_vout);
844 /* Display our window */
845 ShowWindow( p_vout->p_sys->hwnd, SW_SHOW );
846 UpdateWindow( p_vout->p_sys->hwnd );
848 /* Create video sub-window */
849 p_vout->p_sys->hvideownd =
850 CreateWindow( _T("VLC WinGDI video"), _T(""), /* window class */
851 WS_CHILD | WS_VISIBLE, /* window style */
852 CW_USEDEFAULT, CW_USEDEFAULT, /* default coordinates */
853 CW_USEDEFAULT, CW_USEDEFAULT,
854 p_vout->p_sys->hwnd, /* parent window */
855 NULL, GetModuleHandle(NULL),
856 (LPVOID)p_vout ); /* send p_vout to WM_CREATE */
858 /* Initialize offscreen buffer */
859 InitBuffers( p_vout );
861 p_vout->pf_control = Control;
863 /* Tell the video output we're ready to receive data */
864 vlc_thread_ready( p_event );
866 while( !p_event->b_die && GetMessage( &msg, 0, 0, 0 ) )
868 /* Check if we are asked to exit */
869 if( p_event->b_die ) break;
871 switch( msg.message )
877 p_event->p_vlc->b_die = VLC_TRUE;
880 TranslateMessage( &msg );
888 p_event->p_vlc->b_die = VLC_TRUE;
894 TranslateMessage( &msg );
895 DispatchMessage( &msg );
900 msg_Dbg( p_vout, "CloseWindow" );
902 #ifdef MODULE_NAME_IS_wingapi
905 DeleteDC( p_vout->p_sys->off_dc );
906 DeleteObject( p_vout->p_sys->off_bitmap );
909 DestroyWindow( p_vout->p_sys->hwnd );
910 if( p_vout->p_sys->hfswnd ) DestroyWindow( p_vout->p_sys->hfswnd );
912 if( p_vout->p_sys->hparent )
913 vout_ReleaseWindow( p_vout, (void *)p_vout->p_sys->hparent );
916 /*****************************************************************************
917 * UpdateRects: update clipping rectangles
918 *****************************************************************************
919 * This function is called when the window position or size are changed, and
920 * its job is to update the source and destination RECTs used to display the
922 *****************************************************************************/
923 static void UpdateRects( vout_thread_t *p_vout, vlc_bool_t b_force )
925 #define rect_src p_vout->p_sys->rect_src
926 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
927 #define rect_dest p_vout->p_sys->rect_dest
928 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
930 int i_width, i_height, i_x, i_y;
935 /* Retrieve the window size */
936 GetClientRect( p_vout->p_sys->hwnd, &rect );
938 /* Retrieve the window position */
939 point.x = point.y = 0;
940 ClientToScreen( p_vout->p_sys->hwnd, &point );
942 /* If nothing changed, we can return */
944 && p_vout->p_sys->i_window_width == rect.right
945 && p_vout->p_sys->i_window_height == rect.bottom
946 && p_vout->p_sys->i_window_x == point.x
947 && p_vout->p_sys->i_window_y == point.y )
952 /* Update the window position and size */
953 p_vout->p_sys->i_window_x = point.x;
954 p_vout->p_sys->i_window_y = point.y;
955 p_vout->p_sys->i_window_width = rect.right;
956 p_vout->p_sys->i_window_height = rect.bottom;
958 vout_PlacePicture( p_vout, rect.right, rect.bottom,
959 &i_x, &i_y, &i_width, &i_height );
961 if( p_vout->p_sys->hvideownd )
962 SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
963 i_x, i_y, i_width, i_height, 0 );
965 /* Destination image position and dimensions */
966 rect_dest.left = point.x + i_x;
967 rect_dest.right = rect_dest.left + i_width;
968 rect_dest.top = point.y + i_y;
969 rect_dest.bottom = rect_dest.top + i_height;
971 /* Clip the destination window */
972 if( !IntersectRect( &rect_dest_clipped, &rect_dest,
973 &p_vout->p_sys->rect_display ) )
975 SetRectEmpty( &rect_src_clipped );
980 msg_Dbg( p_vout, "image_dst_clipped coords: %i,%i,%i,%i",
981 rect_dest_clipped.left, rect_dest_clipped.top,
982 rect_dest_clipped.right, rect_dest_clipped.bottom );
985 /* the 2 following lines are to fix a bug when clicking on the desktop */
986 if( (rect_dest_clipped.right - rect_dest_clipped.left)==0 ||
987 (rect_dest_clipped.bottom - rect_dest_clipped.top)==0 )
989 SetRectEmpty( &rect_src_clipped );
993 /* src image dimensions */
996 rect_src.right = p_vout->output.i_width;
997 rect_src.bottom = p_vout->output.i_height;
999 /* Clip the source image */
1000 rect_src_clipped.left = (rect_dest_clipped.left - rect_dest.left) *
1001 p_vout->output.i_width / (rect_dest.right - rect_dest.left);
1002 rect_src_clipped.right = p_vout->output.i_width -
1003 (rect_dest.right - rect_dest_clipped.right) * p_vout->output.i_width /
1004 (rect_dest.right - rect_dest.left);
1005 rect_src_clipped.top = (rect_dest_clipped.top - rect_dest.top) *
1006 p_vout->output.i_height / (rect_dest.bottom - rect_dest.top);
1007 rect_src_clipped.bottom = p_vout->output.i_height -
1008 (rect_dest.bottom - rect_dest_clipped.bottom) * p_vout->output.i_height /
1009 (rect_dest.bottom - rect_dest.top);
1012 msg_Dbg( p_vout, "image_src_clipped coords: %i,%i,%i,%i",
1013 rect_src_clipped.left, rect_src_clipped.top,
1014 rect_src_clipped.right, rect_src_clipped.bottom );
1017 /* The destination coordinates need to be relative to the current
1018 * directdraw primary surface (display) */
1019 rect_dest_clipped.left -= p_vout->p_sys->rect_display.left;
1020 rect_dest_clipped.right -= p_vout->p_sys->rect_display.left;
1021 rect_dest_clipped.top -= p_vout->p_sys->rect_display.top;
1022 rect_dest_clipped.bottom -= p_vout->p_sys->rect_display.top;
1024 /* Signal the change in size/position */
1025 p_vout->p_sys->i_changes |= DX_POSITION_CHANGE;
1028 #undef rect_src_clipped
1030 #undef rect_dest_clipped
1033 /*****************************************************************************
1034 * Message handler for the main window
1035 *****************************************************************************/
1036 static long FAR PASCAL WndProc( HWND hWnd, UINT message,
1037 WPARAM wParam, LPARAM lParam )
1039 vout_thread_t *p_vout;
1041 if( message == WM_CREATE )
1043 /* Store p_vout for future use */
1044 p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
1045 SetWindowLongPtr( hWnd, GWLP_USERDATA, (LONG_PTR)p_vout );
1046 if( p_vout ) msg_Dbg( p_vout, "create: %p", hWnd );
1050 p_vout = (vout_thread_t *)GetWindowLongPtr( hWnd, GWLP_USERDATA );
1054 /* Catch the screensaver and the monitor turn-off */
1055 if( message == WM_SYSCOMMAND &&
1056 ( wParam == SC_SCREENSAVE || wParam == SC_MONITORPOWER ) )
1058 //if( p_vout ) msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND screensaver" );
1059 return 0; /* this stops them from happening */
1065 /* Hmmm mozilla does manage somehow to save the pointer to our
1066 * windowproc and still calls it after the vout has been closed. */
1067 return DefWindowProc(hWnd, message, wParam, lParam);
1070 if( hWnd != p_vout->p_sys->hwnd &&
1071 hWnd != p_vout->p_sys->hfswnd &&
1072 hWnd != p_vout->p_sys->hvideownd )
1073 return DefWindowProc(hWnd, message, wParam, lParam);
1077 case WM_WINDOWPOSCHANGED:
1078 if( hWnd == p_vout->p_sys->hwnd )
1079 UpdateRects( p_vout, VLC_TRUE );
1084 msg_Err( p_vout, "WM_ACTIVATE: %i", wParam );
1085 if( wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE )
1087 else if( wParam == WA_INACTIVE )
1093 p_vout->p_sys->b_focus = VLC_FALSE;
1094 if( !p_vout->p_sys->b_parent_focus ) GXSuspend();
1096 if( hWnd == p_vout->p_sys->hfswnd )
1099 HWND htbar = FindWindow( _T("HHTaskbar"), NULL );
1100 ShowWindow( htbar, SW_SHOW );
1104 if( !p_vout->p_sys->hparent ||
1105 hWnd == p_vout->p_sys->hfswnd )
1107 SHFullScreen( hWnd, SHFS_SHOWSIPBUTTON );
1112 p_vout->p_sys->b_focus = VLC_TRUE;
1115 if( p_vout->p_sys->hparent &&
1116 hWnd != p_vout->p_sys->hfswnd && p_vout->b_fullscreen )
1117 p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE;
1119 if( hWnd == p_vout->p_sys->hfswnd )
1122 HWND htbar = FindWindow( _T("HHTaskbar"), NULL );
1123 ShowWindow( htbar, SW_HIDE );
1127 if( !p_vout->p_sys->hparent ||
1128 hWnd == p_vout->p_sys->hfswnd )
1130 SHFullScreen( hWnd, SHFS_HIDESIPBUTTON );
1134 case WM_LBUTTONDOWN:
1135 p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE;
1142 case WM_INITMENUPOPUP:
1143 p_vout->p_sys->b_video_display = VLC_FALSE;
1147 // Redo the video display because menu can be closed
1148 // FIXME verify if p_child_window exits
1149 if( (((NMHDR *)lParam)->code) == NM_CUSTOMDRAW )
1150 p_vout->p_sys->b_video_display = VLC_TRUE;
1154 msg_Dbg( p_vout, "WinProc WM_DESTROY" );
1155 PostQuitMessage( 0 );
1159 return DefWindowProc( hWnd, message, wParam, lParam );
1165 /*****************************************************************************
1166 * InitBuffers: initialize an offscreen bitmap for direct buffer operations.
1167 *****************************************************************************/
1168 static void InitBuffers( vout_thread_t *p_vout )
1170 BITMAPINFOHEADER *p_header = &p_vout->p_sys->bitmapinfo.bmiHeader;
1171 BITMAPINFO *p_info = &p_vout->p_sys->bitmapinfo;
1172 int i_pixels = p_vout->render.i_height * p_vout->render.i_width;
1173 HDC window_dc = GetDC( p_vout->p_sys->hvideownd );
1175 /* Get screen properties */
1176 #ifdef MODULE_NAME_IS_wingapi
1177 GXDisplayProperties gx_displayprop = GXGetDisplayProperties();
1178 p_vout->p_sys->i_depth = gx_displayprop.cBPP;
1180 p_vout->p_sys->i_depth = GetDeviceCaps( window_dc, PLANES ) *
1181 GetDeviceCaps( window_dc, BITSPIXEL );
1183 msg_Dbg( p_vout, "GDI depth is %i", p_vout->p_sys->i_depth );
1185 #ifdef MODULE_NAME_IS_wingapi
1186 GXOpenDisplay( p_vout->p_sys->hvideownd, GX_FULLSCREEN );
1190 /* Initialize offscreen bitmap */
1191 memset( p_info, 0, sizeof( BITMAPINFO ) + 3 * sizeof( RGBQUAD ) );
1193 p_header->biSize = sizeof( BITMAPINFOHEADER );
1194 p_header->biSizeImage = 0;
1195 p_header->biPlanes = 1;
1196 switch( p_vout->p_sys->i_depth )
1199 p_header->biBitCount = 8;
1200 p_header->biCompression = BI_RGB;
1201 /* FIXME: we need a palette here */
1204 p_header->biBitCount = 15;
1205 p_header->biCompression = BI_BITFIELDS;//BI_RGB;
1206 ((DWORD*)p_info->bmiColors)[0] = 0x00007c00;
1207 ((DWORD*)p_info->bmiColors)[1] = 0x000003e0;
1208 ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
1211 p_header->biBitCount = 16;
1212 p_header->biCompression = BI_BITFIELDS;//BI_RGB;
1213 ((DWORD*)p_info->bmiColors)[0] = 0x0000f800;
1214 ((DWORD*)p_info->bmiColors)[1] = 0x000007e0;
1215 ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
1218 p_header->biBitCount = 24;
1219 p_header->biCompression = BI_RGB;
1220 ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
1221 ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
1222 ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
1225 p_header->biBitCount = 32;
1226 p_header->biCompression = BI_RGB;
1227 ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
1228 ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
1229 ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
1232 msg_Err( p_vout, "screen depth %i not supported",
1233 p_vout->p_sys->i_depth );
1237 p_header->biWidth = p_vout->render.i_width;
1238 p_header->biHeight = -p_vout->render.i_height;
1239 p_header->biClrImportant = 0;
1240 p_header->biClrUsed = 0;
1241 p_header->biXPelsPerMeter = 0;
1242 p_header->biYPelsPerMeter = 0;
1244 p_vout->p_sys->i_pic_pixel_pitch = p_header->biBitCount / 8;
1245 p_vout->p_sys->i_pic_pitch = p_header->biBitCount * p_header->biWidth / 8;
1247 p_vout->p_sys->off_bitmap =
1248 CreateDIBSection( window_dc, (BITMAPINFO *)p_header, DIB_RGB_COLORS,
1249 (void**)&p_vout->p_sys->p_pic_buffer, NULL, 0 );
1251 p_vout->p_sys->off_dc = CreateCompatibleDC( window_dc );
1253 SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap );
1254 ReleaseDC( 0, window_dc );
1258 /*****************************************************************************
1259 * Control: control facility for the vout
1260 *****************************************************************************/
1261 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
1267 case VOUT_SET_FOCUS:
1268 b_bool = va_arg( args, vlc_bool_t );
1270 p_vout->p_sys->b_parent_focus = b_bool;
1271 if( b_bool ) GXResume();
1272 else if( !p_vout->p_sys->b_focus ) GXSuspend();
1277 return vout_vaControlDefault( p_vout, i_query, args );