]> git.sesse.net Git - vlc/blob - modules/video_output/msw/wingdi.c
Split out non events code into common.c
[vlc] / modules / video_output / msw / wingdi.c
1 /*****************************************************************************
2  * wingdi.c : Win32 / WinCE GDI video output plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2002-2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *          Samuel Hocevar <sam@zoy.org>
9  *
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.
14  *
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.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_interface.h>
36 #include <vlc_playlist.h>
37 #include <vlc_vout.h>
38
39 #include <windows.h>
40 #include <tchar.h>
41 #include <commctrl.h>
42
43 #include "vout.h"
44
45 #define MAX_DIRECTBUFFERS 10
46
47 #ifndef WS_NONAVDONEBUTTON
48 #define WS_NONAVDONEBUTTON 0
49 #endif
50 /*****************************************************************************
51  * Local prototypes
52  *****************************************************************************/
53 static int  OpenVideo  ( vlc_object_t * );
54 static void CloseVideo ( vlc_object_t * );
55
56 static int  Init      ( vout_thread_t * );
57 static void End       ( vout_thread_t * );
58 static int  Manage    ( vout_thread_t * );
59 static void Render    ( vout_thread_t *, picture_t * );
60 #ifdef MODULE_NAME_IS_wingapi
61 static void FirstDisplayGAPI( vout_thread_t *, picture_t * );
62 static void DisplayGAPI( vout_thread_t *, picture_t * );
63 static int GAPILockSurface( vout_thread_t *, picture_t * );
64 static int GAPIUnlockSurface( vout_thread_t *, picture_t * );
65 #else
66 static void FirstDisplayGDI( vout_thread_t *, picture_t * );
67 static void DisplayGDI( vout_thread_t *, picture_t * );
68 #endif
69 static void SetPalette( vout_thread_t *, uint16_t *, uint16_t *, uint16_t * );
70
71 static void InitBuffers        ( vout_thread_t * );
72
73
74
75 #define DX_POSITION_CHANGE 0x1000
76
77 /*****************************************************************************
78  * Module descriptor
79  *****************************************************************************/
80 vlc_module_begin ()
81     set_category( CAT_VIDEO )
82     set_subcategory( SUBCAT_VIDEO_VOUT )
83 #ifdef MODULE_NAME_IS_wingapi
84     set_shortname( "Windows GAPI" )
85     set_description( N_("Windows GAPI video output") )
86     set_capability( "video output", 20 )
87 #else
88     set_shortname( "Windows GDI" )
89     set_description( N_("Windows GDI video output") )
90     set_capability( "video output", 10 )
91 #endif
92     set_callbacks( OpenVideo, CloseVideo )
93 vlc_module_end ()
94
95 /*****************************************************************************
96  * OpenVideo: activate GDI video thread output method
97  *****************************************************************************/
98 static int OpenVideo ( vlc_object_t *p_this )
99 {
100     vout_thread_t * p_vout = (vout_thread_t *)p_this;
101
102     p_vout->p_sys = (vout_sys_t *)calloc( 1, sizeof(vout_sys_t) );
103     if( !p_vout->p_sys ) return VLC_ENOMEM;
104
105 #ifdef MODULE_NAME_IS_wingapi
106     /* Load GAPI */
107     p_vout->p_sys->gapi_dll = LoadLibrary( _T("GX.DLL") );
108     if( p_vout->p_sys->gapi_dll == NULL )
109     {
110         msg_Warn( p_vout, "failed loading gx.dll" );
111         free( p_vout->p_sys );
112         return VLC_EGENERIC;
113     }
114
115     GXOpenDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
116         _T("?GXOpenDisplay@@YAHPAUHWND__@@K@Z") );
117     GXCloseDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
118         _T("?GXCloseDisplay@@YAHXZ") );
119     GXBeginDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
120         _T("?GXBeginDraw@@YAPAXXZ") );
121     GXEndDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
122         _T("?GXEndDraw@@YAHXZ") );
123     GXGetDisplayProperties = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
124         _T("?GXGetDisplayProperties@@YA?AUGXDisplayProperties@@XZ") );
125     GXSuspend = (void *)GetProcAddress( p_vout->p_sys->gapi_dll,
126         _T("?GXSuspend@@YAHXZ") );
127     GXResume = GetProcAddress( p_vout->p_sys->gapi_dll,
128         _T("?GXResume@@YAHXZ") );
129
130     if( !GXOpenDisplay || !GXCloseDisplay || !GXBeginDraw || !GXEndDraw ||
131         !GXGetDisplayProperties || !GXSuspend || !GXResume )
132     {
133         msg_Err( p_vout, "failed GetProcAddress on gapi.dll" );
134         free( p_vout->p_sys );
135         return VLC_EGENERIC;
136     }
137
138     msg_Dbg( p_vout, "GAPI DLL loaded" );
139
140     p_vout->p_sys->render_width = p_vout->render.i_width;
141     p_vout->p_sys->render_height = p_vout->render.i_height;
142 #endif
143
144     p_vout->p_sys->p_event = vlc_object_create( p_vout, sizeof( event_thread_t ) );
145     if( !p_vout->p_sys->p_event )
146     {
147         free( p_vout->p_sys );
148         return VLC_ENOMEM;
149     }
150
151     p_vout->pf_init = Init;
152     p_vout->pf_end = End;
153     p_vout->pf_manage = Manage;
154     p_vout->pf_render = Render;
155     p_vout->pf_control = Control;
156 #ifdef MODULE_NAME_IS_wingapi
157     p_vout->pf_display = FirstDisplayGAPI;
158
159     p_vout->p_sys->b_focus = 0;
160     p_vout->p_sys->b_parent_focus = 0;
161
162 #else
163     p_vout->pf_display = FirstDisplayGDI;
164 #endif
165
166     p_vout->p_sys->hwnd = p_vout->p_sys->hvideownd = NULL;
167     p_vout->p_sys->hparent = p_vout->p_sys->hfswnd = NULL;
168     p_vout->p_sys->i_changes = 0;
169     SetRectEmpty( &p_vout->p_sys->rect_display );
170     SetRectEmpty( &p_vout->p_sys->rect_parent );
171
172     var_Create( p_vout, "video-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
173     var_Create( p_vout, "disable-screensaver", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
174
175     p_vout->p_sys->b_cursor_hidden = 0;
176     p_vout->p_sys->i_lastmoved = mdate();
177     p_vout->p_sys->i_mouse_hide_timeout =
178         var_GetInteger(p_vout, "mouse-hide-timeout") * 1000;
179
180     /* Set main window's size */
181     p_vout->p_sys->i_window_width = p_vout->i_window_width;
182     p_vout->p_sys->i_window_height = p_vout->i_window_height;
183
184     if ( CreateEventThread( p_vout ) )
185     {
186
187 #ifndef UNDER_CE
188         /* Variable to indicate if the window should be on top of others */
189         /* Trigger a callback right now */
190         var_TriggerCallback( p_vout, "video-on-top" );
191
192         DisableScreensaver ( p_vout );
193 #endif
194         return VLC_SUCCESS;
195     }
196     else
197     {
198         CloseVideo( VLC_OBJECT(p_vout) );
199         return VLC_EGENERIC;
200     }
201 }
202
203 /*****************************************************************************
204  * CloseVideo: deactivate the GDI video output
205  *****************************************************************************/
206 static void CloseVideo ( vlc_object_t *p_this )
207 {
208     vout_thread_t * p_vout = (vout_thread_t *)p_this;
209
210     StopEventThread( p_vout );
211
212 #ifndef UNDER_CE
213     RestoreScreensaver( p_vout );
214 #endif
215
216 #ifdef MODULE_NAME_IS_wingapi
217     FreeLibrary( p_vout->p_sys->gapi_dll );
218 #endif
219
220     free( p_vout->p_sys );
221     p_vout->p_sys = NULL;
222 }
223
224 /*****************************************************************************
225  * Init: initialize video thread output method
226  *****************************************************************************/
227 static int Init( vout_thread_t *p_vout )
228 {
229     picture_t *p_pic;
230
231     /* Initialize offscreen buffer */
232     InitBuffers( p_vout );
233
234     p_vout->p_sys->rect_display.left = 0;
235     p_vout->p_sys->rect_display.top = 0;
236     p_vout->p_sys->rect_display.right  = GetSystemMetrics(SM_CXSCREEN);
237     p_vout->p_sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
238
239     I_OUTPUTPICTURES = 0;
240
241     /* Initialize the output structure */
242     switch( p_vout->p_sys->i_depth )
243     {
244     case 8:
245         p_vout->output.i_chroma = VLC_CODEC_RGB8;
246         p_vout->output.pf_setpalette = SetPalette;
247         break;
248     case 15:
249         p_vout->output.i_chroma = VLC_CODEC_RGB15;
250         p_vout->output.i_rmask  = 0x7c00;
251         p_vout->output.i_gmask  = 0x03e0;
252         p_vout->output.i_bmask  = 0x001f;
253         break;
254     case 16:
255         p_vout->output.i_chroma = VLC_CODEC_RGB16;
256         p_vout->output.i_rmask  = 0xf800;
257         p_vout->output.i_gmask  = 0x07e0;
258         p_vout->output.i_bmask  = 0x001f;
259         break;
260     case 24:
261         p_vout->output.i_chroma = VLC_CODEC_RGB24;
262         p_vout->output.i_rmask  = 0x00ff0000;
263         p_vout->output.i_gmask  = 0x0000ff00;
264         p_vout->output.i_bmask  = 0x000000ff;
265         break;
266     case 32:
267         p_vout->output.i_chroma = VLC_CODEC_RGB32;
268         p_vout->output.i_rmask  = 0x00ff0000;
269         p_vout->output.i_gmask  = 0x0000ff00;
270         p_vout->output.i_bmask  = 0x000000ff;
271         break;
272     default:
273         msg_Err( p_vout, "screen depth %i not supported",
274                  p_vout->p_sys->i_depth );
275         return VLC_EGENERIC;
276         break;
277     }
278
279     p_pic = &p_vout->p_picture[0];
280
281 #ifdef MODULE_NAME_IS_wingapi
282     p_vout->output.i_width  = 0;
283     p_vout->output.i_height = 0;
284     p_pic->pf_lock  = GAPILockSurface;
285     p_pic->pf_unlock = GAPIUnlockSurface;
286     Manage( p_vout );
287     GAPILockSurface( p_vout, p_pic );
288     p_vout->i_changes = 0;
289     p_vout->output.i_width  = p_vout->p_sys->render_width;
290     p_vout->output.i_height = p_vout->p_sys->render_height;
291
292 #else
293     p_vout->output.i_width  = p_vout->render.i_width;
294     p_vout->output.i_height = p_vout->render.i_height;
295
296     p_vout->fmt_out = p_vout->fmt_in;
297     p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
298 #endif
299
300     p_vout->output.i_aspect = p_vout->render.i_aspect;
301
302     p_pic->p->p_pixels = p_vout->p_sys->p_pic_buffer;
303     p_pic->p->i_lines = p_vout->output.i_height;
304     p_pic->p->i_visible_lines = p_vout->output.i_height;
305     p_pic->p->i_pitch = p_vout->p_sys->i_pic_pitch;
306     p_pic->p->i_pixel_pitch = p_vout->p_sys->i_pic_pixel_pitch;
307     p_pic->p->i_visible_pitch = p_vout->output.i_width *
308         p_pic->p->i_pixel_pitch;
309     p_pic->i_planes = 1;
310     p_pic->i_status = DESTROYED_PICTURE;
311     p_pic->i_type   = DIRECT_PICTURE;
312
313     PP_OUTPUTPICTURE[ I_OUTPUTPICTURES++ ] = p_pic;
314
315     /* Change the window title bar text */
316     PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
317     UpdateRects( p_vout, true );
318
319     return VLC_SUCCESS;
320 }
321
322 /*****************************************************************************
323  * End: terminate video thread output method
324  *****************************************************************************/
325 static void End( vout_thread_t *p_vout )
326 {
327 #ifdef MODULE_NAME_IS_wingapi
328     GXCloseDisplay();
329 #else
330     DeleteDC( p_vout->p_sys->off_dc );
331     DeleteObject( p_vout->p_sys->off_bitmap );
332 #endif
333 }
334
335 /*****************************************************************************
336  * Manage: handle events
337  *****************************************************************************
338  * This function should be called regularly by video output thread. It manages
339  * console events. It returns a non null value on error.
340  *****************************************************************************/
341 static int Manage( vout_thread_t *p_vout )
342 {
343     /* If we do not control our window, we check for geometry changes
344      * ourselves because the parent might not send us its events. */
345     vlc_mutex_lock( &p_vout->p_sys->lock );
346     if( p_vout->p_sys->hparent && !p_vout->b_fullscreen )
347     {
348         RECT rect_parent;
349         POINT point;
350
351         vlc_mutex_unlock( &p_vout->p_sys->lock );
352
353         GetClientRect( p_vout->p_sys->hparent, &rect_parent );
354         point.x = point.y = 0;
355         ClientToScreen( p_vout->p_sys->hparent, &point );
356         OffsetRect( &rect_parent, point.x, point.y );
357
358         if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) )
359         {
360             unsigned int i_x, i_y, i_width, i_height;
361             p_vout->p_sys->rect_parent = rect_parent;
362
363             /* This one is to force the update even if only
364              * the position has changed */
365             SetWindowPos( p_vout->p_sys->hwnd, 0, 1, 1,
366                           rect_parent.right - rect_parent.left,
367                           rect_parent.bottom - rect_parent.top, 0 );
368
369             SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
370                           rect_parent.right - rect_parent.left,
371                           rect_parent.bottom - rect_parent.top, 0 );
372
373             vout_PlacePicture( p_vout, rect_parent.right - rect_parent.left,
374                                rect_parent.bottom - rect_parent.top,
375                                &i_x, &i_y, &i_width, &i_height );
376
377             SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP,
378                           i_x, i_y, i_width, i_height, 0 );
379         }
380     }
381     else
382     {
383         vlc_mutex_unlock( &p_vout->p_sys->lock );
384     }
385
386     /* autoscale toggle */
387     if( p_vout->i_changes & VOUT_SCALE_CHANGE )
388     {
389         p_vout->i_changes &= ~VOUT_SCALE_CHANGE;
390
391         p_vout->b_autoscale = var_GetBool( p_vout, "autoscale" );
392         p_vout->i_zoom = (int) ZOOM_FP_FACTOR;
393
394         UpdateRects( p_vout, true );
395     }
396
397     /* scaling factor */
398     if( p_vout->i_changes & VOUT_ZOOM_CHANGE )
399     {
400         p_vout->i_changes &= ~VOUT_ZOOM_CHANGE;
401
402         p_vout->b_autoscale = false;
403         p_vout->i_zoom =
404             (int)( ZOOM_FP_FACTOR * var_GetFloat( p_vout, "scale" ) );
405         UpdateRects( p_vout, true );
406     }
407
408     /* Check for cropping / aspect changes */
409     if( p_vout->i_changes & VOUT_CROP_CHANGE ||
410         p_vout->i_changes & VOUT_ASPECT_CHANGE )
411     {
412         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
413         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
414
415         p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset;
416         p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset;
417         p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width;
418         p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height;
419         p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect;
420         p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num;
421         p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den;
422         p_vout->output.i_aspect = p_vout->fmt_in.i_aspect;
423         UpdateRects( p_vout, true );
424     }
425
426     /*
427      * Position Change
428      */
429     if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE )
430     {
431         p_vout->p_sys->i_changes &= ~DX_POSITION_CHANGE;
432     }
433
434     /* We used to call the Win32 PeekMessage function here to read the window
435      * messages. But since window can stay blocked into this function for a
436      * long time (for example when you move your window on the screen), I
437      * decided to isolate PeekMessage in another thread. */
438
439     /*
440      * Fullscreen change
441      */
442     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
443         || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
444     {
445         Win32ToggleFullscreen( p_vout );
446
447         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
448         p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
449     }
450
451     /*
452      * Pointer change
453      */
454     if( p_vout->b_fullscreen && !p_vout->p_sys->b_cursor_hidden &&
455         (mdate() - p_vout->p_sys->i_lastmoved) >
456             p_vout->p_sys->i_mouse_hide_timeout )
457     {
458         POINT point;
459         HWND hwnd;
460
461         /* Hide the cursor only if it is inside our window */
462         GetCursorPos( &point );
463         hwnd = WindowFromPoint(point);
464         if( hwnd == p_vout->p_sys->hwnd || hwnd == p_vout->p_sys->hvideownd )
465         {
466             PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
467         }
468         else
469         {
470             p_vout->p_sys->i_lastmoved = mdate();
471         }
472     }
473
474     /*
475      * "Always on top" status change
476      */
477     if( p_vout->p_sys->b_on_top_change )
478     {
479         vlc_value_t val;
480         HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
481
482         var_Get( p_vout, "video-on-top", &val );
483
484         /* Set the window on top if necessary */
485         if( val.b_bool && !( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
486                            & WS_EX_TOPMOST ) )
487         {
488             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
489                            MF_BYCOMMAND | MFS_CHECKED );
490             SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0,
491                           SWP_NOSIZE | SWP_NOMOVE );
492         }
493         else
494         /* The window shouldn't be on top */
495         if( !val.b_bool && ( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
496                            & WS_EX_TOPMOST ) )
497         {
498             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
499                            MF_BYCOMMAND | MFS_UNCHECKED );
500             SetWindowPos( p_vout->p_sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
501                           SWP_NOSIZE | SWP_NOMOVE );
502         }
503
504         p_vout->p_sys->b_on_top_change = false;
505     }
506
507     /* Check if the event thread is still running */
508     if( !vlc_object_alive (p_vout->p_sys->p_event) )
509     {
510         return VLC_EGENERIC; /* exit */
511     }
512
513     return VLC_SUCCESS;
514 }
515
516 /*****************************************************************************
517  * Render: render previously calculated output
518  *****************************************************************************/
519 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
520 {
521     /* No need to do anything, the fake direct buffers stay as they are */
522     (void)p_vout;
523     (void)p_pic;
524 }
525
526 /*****************************************************************************
527  * Display: displays previously rendered output
528  *****************************************************************************/
529 #define rect_src p_vout->p_sys->rect_src
530 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
531 #define rect_dest p_vout->p_sys->rect_dest
532 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
533
534 #ifndef MODULE_NAME_IS_wingapi
535 static void DisplayGDI( vout_thread_t *p_vout, picture_t *p_pic )
536 {
537     VLC_UNUSED( p_pic );
538
539     vout_sys_t *p_sys = p_vout->p_sys;
540     RECT rect_dst = rect_dest_clipped;
541     HDC hdc = GetDC( p_sys->hvideownd );
542
543     OffsetRect( &rect_dst, -rect_dest.left, -rect_dest.top );
544     SelectObject( p_sys->off_dc, p_sys->off_bitmap );
545
546     if( rect_dest_clipped.right - rect_dest_clipped.left !=
547         rect_src_clipped.right - rect_src_clipped.left ||
548         rect_dest_clipped.bottom - rect_dest_clipped.top !=
549         rect_src_clipped.bottom - rect_src_clipped.top )
550     {
551         StretchBlt( hdc, rect_dst.left, rect_dst.top,
552                     rect_dst.right, rect_dst.bottom,
553                     p_sys->off_dc, rect_src_clipped.left, rect_src_clipped.top,
554                     rect_src_clipped.right, rect_src_clipped.bottom, SRCCOPY );
555     }
556     else
557     {
558         BitBlt( hdc, rect_dst.left, rect_dst.top,
559                 rect_dst.right, rect_dst.bottom,
560                 p_sys->off_dc, rect_src_clipped.left,
561                 rect_src_clipped.top, SRCCOPY );
562     }
563
564     ReleaseDC( p_sys->hvideownd, hdc );
565 }
566
567 static void FirstDisplayGDI( vout_thread_t *p_vout, picture_t *p_pic )
568 {
569     /*
570     ** Video window is initially hidden, show it now since we got a
571     ** picture to show.
572     */
573     SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0,
574         SWP_ASYNCWINDOWPOS|
575         SWP_FRAMECHANGED|
576         SWP_SHOWWINDOW|
577         SWP_NOMOVE|
578         SWP_NOSIZE|
579         SWP_NOZORDER );
580
581     /* get initial picture presented */
582     DisplayGDI(p_vout, p_pic);
583
584     /* use and restores proper display function for further pictures */
585     p_vout->pf_display = DisplayGDI;
586 }
587
588 #else
589
590 static int GAPILockSurface( vout_thread_t *p_vout, picture_t *p_pic )
591 {
592     vout_sys_t *p_sys = p_vout->p_sys;
593     int i_x, i_y, i_width, i_height;
594     RECT video_rect;
595     POINT point;
596
597     GetClientRect( p_sys->hwnd, &video_rect);
598     vout_PlacePicture( p_vout, video_rect.right - video_rect.left,
599                        video_rect.bottom - video_rect.top,
600                        &i_x, &i_y, &i_width, &i_height );
601     point.x = point.y = 0;
602     ClientToScreen( p_sys->hwnd, &point );
603     i_x += point.x + video_rect.left;
604     i_y += point.y + video_rect.top;
605
606     if( i_width != p_vout->output.i_width ||
607         i_height != p_vout->output.i_height )
608     {
609         GXDisplayProperties gxdisplayprop = GXGetDisplayProperties();
610
611         p_sys->render_width = i_width;
612         p_sys->render_height = i_height;
613         p_vout->i_changes |= VOUT_SIZE_CHANGE;
614
615         msg_Dbg( p_vout, "vout size change (%ix%i -> %ix%i)",
616                  i_width, i_height, p_vout->output.i_width,
617                  p_vout->output.i_height );
618
619         p_vout->p_sys->i_pic_pixel_pitch = gxdisplayprop.cbxPitch;
620         p_vout->p_sys->i_pic_pitch = gxdisplayprop.cbyPitch;
621         return VLC_EGENERIC;
622     }
623     else
624     {
625         GXDisplayProperties gxdisplayprop;
626         RECT display_rect, dest_rect;
627         uint8_t *p_dest, *p_src = p_pic->p->p_pixels;
628
629         video_rect.left = i_x; video_rect.top = i_y;
630         video_rect.right = i_x + i_width;
631         video_rect.bottom = i_y + i_height;
632
633         gxdisplayprop = GXGetDisplayProperties();
634         display_rect.left = 0; display_rect.top = 0;
635         display_rect.right = gxdisplayprop.cxWidth;
636         display_rect.bottom = gxdisplayprop.cyHeight;
637
638         if( !IntersectRect( &dest_rect, &video_rect, &display_rect ) )
639         {
640             return VLC_EGENERIC;
641         }
642
643 #ifndef NDEBUG
644         msg_Dbg( p_vout, "video (%d,%d,%d,%d) display (%d,%d,%d,%d) "
645                  "dest (%d,%d,%d,%d)",
646                  video_rect.left, video_rect.right,
647                  video_rect.top, video_rect.bottom,
648                  display_rect.left, display_rect.right,
649                  display_rect.top, display_rect.bottom,
650                  dest_rect.left, dest_rect.right,
651                  dest_rect.top, dest_rect.bottom );
652 #endif
653
654         if( !(p_dest = GXBeginDraw()) )
655         {
656 #ifndef NDEBUG
657             msg_Err( p_vout, "GXBeginDraw error %d ", GetLastError() );
658 #endif
659             return VLC_EGENERIC;
660         }
661
662         p_src += (dest_rect.left - video_rect.left) * gxdisplayprop.cbxPitch +
663             (dest_rect.top - video_rect.top) * p_pic->p->i_pitch;
664         p_dest += dest_rect.left * gxdisplayprop.cbxPitch +
665             dest_rect.top * gxdisplayprop.cbyPitch;
666         i_width = dest_rect.right - dest_rect.left;
667         i_height = dest_rect.bottom - dest_rect.top;
668
669         p_pic->p->p_pixels = p_dest;
670     }
671
672     return VLC_SUCCESS;
673 }
674
675 static int GAPIUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
676 {
677     GXEndDraw();
678     return VLC_SUCCESS;
679 }
680
681 static void DisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic )
682 {
683 }
684
685 static void FirstDisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic )
686 {
687     /* get initial picture presented through D3D */
688     DisplayGAPI(p_vout, p_pic);
689
690     /*
691     ** Video window is initially hidden, show it now since we got a
692     ** picture to show.
693     */
694     SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0,
695         SWP_ASYNCWINDOWPOS|
696         SWP_FRAMECHANGED|
697         SWP_SHOWWINDOW|
698         SWP_NOMOVE|
699         SWP_NOSIZE|
700         SWP_NOZORDER );
701
702     /* use and restores proper display function for further pictures */
703     p_vout->pf_display = DisplayGAPI;
704 }
705
706 #endif
707
708 #undef rect_src
709 #undef rect_src_clipped
710 #undef rect_dest
711 #undef rect_dest_clipped
712 /*****************************************************************************
713  * SetPalette: sets an 8 bpp palette
714  *****************************************************************************/
715 static void SetPalette( vout_thread_t *p_vout,
716                         uint16_t *red, uint16_t *green, uint16_t *blue )
717 {
718     VLC_UNUSED( red ); VLC_UNUSED( green );VLC_UNUSED( blue );
719     msg_Err( p_vout, "FIXME: SetPalette unimplemented" );
720 }
721
722 /*****************************************************************************
723  * InitBuffers: initialize an offscreen bitmap for direct buffer operations.
724  *****************************************************************************/
725 static void InitBuffers( vout_thread_t *p_vout )
726 {
727     BITMAPINFOHEADER *p_header = &p_vout->p_sys->bitmapinfo.bmiHeader;
728     BITMAPINFO *p_info = &p_vout->p_sys->bitmapinfo;
729     HDC window_dc = GetDC( p_vout->p_sys->hvideownd );
730
731     /* Get screen properties */
732 #ifdef MODULE_NAME_IS_wingapi
733     GXDisplayProperties gx_displayprop = GXGetDisplayProperties();
734     p_vout->p_sys->i_depth = gx_displayprop.cBPP;
735 #else
736     p_vout->p_sys->i_depth = GetDeviceCaps( window_dc, PLANES ) *
737         GetDeviceCaps( window_dc, BITSPIXEL );
738 #endif
739     msg_Dbg( p_vout, "GDI depth is %i", p_vout->p_sys->i_depth );
740
741 #ifdef MODULE_NAME_IS_wingapi
742     GXOpenDisplay( p_vout->p_sys->hvideownd, GX_FULLSCREEN );
743
744 #else
745
746     /* Initialize offscreen bitmap */
747     memset( p_info, 0, sizeof( BITMAPINFO ) + 3 * sizeof( RGBQUAD ) );
748
749     p_header->biSize = sizeof( BITMAPINFOHEADER );
750     p_header->biSizeImage = 0;
751     p_header->biPlanes = 1;
752     switch( p_vout->p_sys->i_depth )
753     {
754     case 8:
755         p_header->biBitCount = 8;
756         p_header->biCompression = BI_RGB;
757         /* FIXME: we need a palette here */
758         break;
759     case 15:
760         p_header->biBitCount = 15;
761         p_header->biCompression = BI_BITFIELDS;//BI_RGB;
762         ((DWORD*)p_info->bmiColors)[0] = 0x00007c00;
763         ((DWORD*)p_info->bmiColors)[1] = 0x000003e0;
764         ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
765         break;
766     case 16:
767         p_header->biBitCount = 16;
768         p_header->biCompression = BI_BITFIELDS;//BI_RGB;
769         ((DWORD*)p_info->bmiColors)[0] = 0x0000f800;
770         ((DWORD*)p_info->bmiColors)[1] = 0x000007e0;
771         ((DWORD*)p_info->bmiColors)[2] = 0x0000001f;
772         break;
773     case 24:
774         p_header->biBitCount = 24;
775         p_header->biCompression = BI_RGB;
776         ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
777         ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
778         ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
779         break;
780     case 32:
781         p_header->biBitCount = 32;
782         p_header->biCompression = BI_RGB;
783         ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000;
784         ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00;
785         ((DWORD*)p_info->bmiColors)[2] = 0x000000ff;
786         break;
787     default:
788         msg_Err( p_vout, "screen depth %i not supported",
789                  p_vout->p_sys->i_depth );
790         return;
791         break;
792     }
793     p_header->biWidth = p_vout->render.i_width;
794     p_header->biHeight = -p_vout->render.i_height;
795     p_header->biClrImportant = 0;
796     p_header->biClrUsed = 0;
797     p_header->biXPelsPerMeter = 0;
798     p_header->biYPelsPerMeter = 0;
799
800     p_vout->p_sys->i_pic_pixel_pitch = p_header->biBitCount / 8;
801     p_vout->p_sys->i_pic_pitch = p_header->biBitCount * p_header->biWidth / 8;
802
803     p_vout->p_sys->off_bitmap =
804         CreateDIBSection( window_dc, (BITMAPINFO *)p_header, DIB_RGB_COLORS,
805                           &p_vout->p_sys->p_pic_buffer, NULL, 0 );
806
807     p_vout->p_sys->off_dc = CreateCompatibleDC( window_dc );
808
809     SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap );
810     ReleaseDC( p_vout->p_sys->hvideownd, window_dc );
811 #endif
812 }
813